@scratch/scratch-vm 11.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/.jsdoc.json +20 -0
  2. package/.nvmrc +1 -0
  3. package/CHANGELOG.md +7004 -0
  4. package/LICENSE +12 -0
  5. package/README.md +120 -0
  6. package/TRADEMARK +1 -0
  7. package/commitlint.config.js +4 -0
  8. package/docs/extensions.md +527 -0
  9. package/package.json +103 -0
  10. package/release.config.js +13 -0
  11. package/src/blocks/scratch3_control.js +206 -0
  12. package/src/blocks/scratch3_core_example.js +69 -0
  13. package/src/blocks/scratch3_data.js +257 -0
  14. package/src/blocks/scratch3_event.js +137 -0
  15. package/src/blocks/scratch3_looks.js +612 -0
  16. package/src/blocks/scratch3_motion.js +288 -0
  17. package/src/blocks/scratch3_operators.js +154 -0
  18. package/src/blocks/scratch3_procedures.js +79 -0
  19. package/src/blocks/scratch3_sensing.js +336 -0
  20. package/src/blocks/scratch3_sound.js +350 -0
  21. package/src/dispatch/central-dispatch.js +143 -0
  22. package/src/dispatch/shared-dispatch.js +235 -0
  23. package/src/dispatch/worker-dispatch.js +110 -0
  24. package/src/engine/adapter.js +176 -0
  25. package/src/engine/block-utility.js +242 -0
  26. package/src/engine/blocks-execute-cache.js +19 -0
  27. package/src/engine/blocks-runtime-cache.js +78 -0
  28. package/src/engine/blocks.js +1299 -0
  29. package/src/engine/comment.js +56 -0
  30. package/src/engine/execute.js +580 -0
  31. package/src/engine/monitor-record.js +23 -0
  32. package/src/engine/mutation-adapter.js +48 -0
  33. package/src/engine/profiler.js +390 -0
  34. package/src/engine/runtime.js +2686 -0
  35. package/src/engine/scratch-blocks-constants.js +27 -0
  36. package/src/engine/sequencer.js +361 -0
  37. package/src/engine/stage-layering.js +29 -0
  38. package/src/engine/target.js +799 -0
  39. package/src/engine/thread.js +404 -0
  40. package/src/engine/variable.js +70 -0
  41. package/src/extension-support/argument-type.js +47 -0
  42. package/src/extension-support/block-type.js +50 -0
  43. package/src/extension-support/define-messages.js +18 -0
  44. package/src/extension-support/extension-manager.js +440 -0
  45. package/src/extension-support/extension-metadata.js +64 -0
  46. package/src/extension-support/extension-worker.js +59 -0
  47. package/src/extension-support/reporter-scope.js +18 -0
  48. package/src/extension-support/target-type.js +17 -0
  49. package/src/extensions/scratch3_boost/index.js +2113 -0
  50. package/src/extensions/scratch3_ev3/index.js +1355 -0
  51. package/src/extensions/scratch3_gdx_for/index.js +981 -0
  52. package/src/extensions/scratch3_gdx_for/scratch-link-device-adapter.js +44 -0
  53. package/src/extensions/scratch3_makeymakey/index.js +396 -0
  54. package/src/extensions/scratch3_microbit/index.js +984 -0
  55. package/src/extensions/scratch3_music/assets/drums/1-snare.mp3 +0 -0
  56. package/src/extensions/scratch3_music/assets/drums/10-wood-block.mp3 +0 -0
  57. package/src/extensions/scratch3_music/assets/drums/11-cowbell.mp3 +0 -0
  58. package/src/extensions/scratch3_music/assets/drums/12-triangle.mp3 +0 -0
  59. package/src/extensions/scratch3_music/assets/drums/13-bongo.mp3 +0 -0
  60. package/src/extensions/scratch3_music/assets/drums/14-conga.mp3 +0 -0
  61. package/src/extensions/scratch3_music/assets/drums/15-cabasa.mp3 +0 -0
  62. package/src/extensions/scratch3_music/assets/drums/16-guiro.mp3 +0 -0
  63. package/src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3 +0 -0
  64. package/src/extensions/scratch3_music/assets/drums/18-cuica.mp3 +0 -0
  65. package/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 +0 -0
  66. package/src/extensions/scratch3_music/assets/drums/3-side-stick.mp3 +0 -0
  67. package/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 +0 -0
  68. package/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 +0 -0
  69. package/src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3 +0 -0
  70. package/src/extensions/scratch3_music/assets/drums/7-tambourine.mp3 +0 -0
  71. package/src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3 +0 -0
  72. package/src/extensions/scratch3_music/assets/drums/9-claves.mp3 +0 -0
  73. package/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3 +0 -0
  74. package/src/extensions/scratch3_music/assets/instruments/1-piano/24.mp3 +0 -0
  75. package/src/extensions/scratch3_music/assets/instruments/1-piano/36.mp3 +0 -0
  76. package/src/extensions/scratch3_music/assets/instruments/1-piano/48.mp3 +0 -0
  77. package/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3 +0 -0
  78. package/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3 +0 -0
  79. package/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3 +0 -0
  80. package/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3 +0 -0
  81. package/src/extensions/scratch3_music/assets/instruments/10-clarinet/48.mp3 +0 -0
  82. package/src/extensions/scratch3_music/assets/instruments/10-clarinet/60.mp3 +0 -0
  83. package/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3 +0 -0
  84. package/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3 +0 -0
  85. package/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3 +0 -0
  86. package/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3 +0 -0
  87. package/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3 +0 -0
  88. package/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3 +0 -0
  89. package/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3 +0 -0
  90. package/src/extensions/scratch3_music/assets/instruments/14-bassoon/36.mp3 +0 -0
  91. package/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3 +0 -0
  92. package/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3 +0 -0
  93. package/src/extensions/scratch3_music/assets/instruments/15-choir/48.mp3 +0 -0
  94. package/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3 +0 -0
  95. package/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3 +0 -0
  96. package/src/extensions/scratch3_music/assets/instruments/16-vibraphone/60.mp3 +0 -0
  97. package/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3 +0 -0
  98. package/src/extensions/scratch3_music/assets/instruments/17-music-box/60.mp3 +0 -0
  99. package/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3 +0 -0
  100. package/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3 +0 -0
  101. package/src/extensions/scratch3_music/assets/instruments/2-electric-piano/60.mp3 +0 -0
  102. package/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3 +0 -0
  103. package/src/extensions/scratch3_music/assets/instruments/21-synth-pad/60.mp3 +0 -0
  104. package/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3 +0 -0
  105. package/src/extensions/scratch3_music/assets/instruments/4-guitar/60.mp3 +0 -0
  106. package/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3 +0 -0
  107. package/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3 +0 -0
  108. package/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3 +0 -0
  109. package/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3 +0 -0
  110. package/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3 +0 -0
  111. package/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3 +0 -0
  112. package/src/extensions/scratch3_music/assets/instruments/8-cello/60.mp3 +0 -0
  113. package/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3 +0 -0
  114. package/src/extensions/scratch3_music/assets/instruments/9-trombone/48.mp3 +0 -0
  115. package/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3 +0 -0
  116. package/src/extensions/scratch3_music/index.js +1333 -0
  117. package/src/extensions/scratch3_music/manifest.js +63 -0
  118. package/src/extensions/scratch3_pen/index.js +770 -0
  119. package/src/extensions/scratch3_speech2text/index.js +700 -0
  120. package/src/extensions/scratch3_text2speech/index.js +766 -0
  121. package/src/extensions/scratch3_translate/index.js +286 -0
  122. package/src/extensions/scratch3_video_sensing/debug.js +13 -0
  123. package/src/extensions/scratch3_video_sensing/index.js +594 -0
  124. package/src/extensions/scratch3_video_sensing/library.js +384 -0
  125. package/src/extensions/scratch3_video_sensing/math.js +76 -0
  126. package/src/extensions/scratch3_video_sensing/view.js +509 -0
  127. package/src/extensions/scratch3_wedo2/index.js +1616 -0
  128. package/src/import/load-costume.js +416 -0
  129. package/src/import/load-sound.js +116 -0
  130. package/src/index.js +10 -0
  131. package/src/io/ble.js +256 -0
  132. package/src/io/bt.js +202 -0
  133. package/src/io/clock.js +39 -0
  134. package/src/io/cloud.js +169 -0
  135. package/src/io/keyboard.js +146 -0
  136. package/src/io/mouse.js +145 -0
  137. package/src/io/mouseWheel.js +28 -0
  138. package/src/io/userData.js +24 -0
  139. package/src/io/video.js +210 -0
  140. package/src/playground/benchmark.css +83 -0
  141. package/src/playground/benchmark.js +753 -0
  142. package/src/playground/index.html +111 -0
  143. package/src/playground/suite.css +78 -0
  144. package/src/playground/suite.html +25 -0
  145. package/src/playground/suite.js +544 -0
  146. package/src/playground/video-sensing.html +18 -0
  147. package/src/playground/video-sensing.js +133 -0
  148. package/src/serialization/deserialize-assets.js +178 -0
  149. package/src/serialization/sb2.js +1295 -0
  150. package/src/serialization/sb2_specmap.js +1818 -0
  151. package/src/serialization/sb3.js +1320 -0
  152. package/src/serialization/serialize-assets.js +60 -0
  153. package/src/sprites/rendered-target.js +1115 -0
  154. package/src/sprites/sprite.js +177 -0
  155. package/src/util/base64-util.js +48 -0
  156. package/src/util/cast.js +218 -0
  157. package/src/util/clone.js +17 -0
  158. package/src/util/color.js +204 -0
  159. package/src/util/fetch-with-timeout.js +53 -0
  160. package/src/util/get-monitor-id.js +33 -0
  161. package/src/util/jsonrpc.js +114 -0
  162. package/src/util/log.js +4 -0
  163. package/src/util/math-util.js +122 -0
  164. package/src/util/maybe-format-message.js +18 -0
  165. package/src/util/new-block-ids.js +33 -0
  166. package/src/util/rateLimiter.js +73 -0
  167. package/src/util/scratch-link-websocket.js +133 -0
  168. package/src/util/string-util.js +94 -0
  169. package/src/util/task-queue.js +203 -0
  170. package/src/util/timer.js +103 -0
  171. package/src/util/uid.js +29 -0
  172. package/src/util/variable-util.js +48 -0
  173. package/src/util/xml-escape.js +33 -0
  174. package/src/virtual-machine.js +1586 -0
  175. package/webpack.config.js +134 -0
@@ -0,0 +1,288 @@
1
+ const Cast = require('../util/cast');
2
+ const MathUtil = require('../util/math-util');
3
+ const Timer = require('../util/timer');
4
+
5
+ class Scratch3MotionBlocks {
6
+ constructor (runtime) {
7
+ /**
8
+ * The runtime instantiating this block package.
9
+ * @type {Runtime}
10
+ */
11
+ this.runtime = runtime;
12
+ }
13
+
14
+ /**
15
+ * Retrieve the block primitives implemented by this package.
16
+ * @return {object.<string, Function>} Mapping of opcode to Function.
17
+ */
18
+ getPrimitives () {
19
+ return {
20
+ motion_movesteps: this.moveSteps,
21
+ motion_gotoxy: this.goToXY,
22
+ motion_goto: this.goTo,
23
+ motion_turnright: this.turnRight,
24
+ motion_turnleft: this.turnLeft,
25
+ motion_pointindirection: this.pointInDirection,
26
+ motion_pointtowards: this.pointTowards,
27
+ motion_glidesecstoxy: this.glide,
28
+ motion_glideto: this.glideTo,
29
+ motion_ifonedgebounce: this.ifOnEdgeBounce,
30
+ motion_setrotationstyle: this.setRotationStyle,
31
+ motion_changexby: this.changeX,
32
+ motion_setx: this.setX,
33
+ motion_changeyby: this.changeY,
34
+ motion_sety: this.setY,
35
+ motion_xposition: this.getX,
36
+ motion_yposition: this.getY,
37
+ motion_direction: this.getDirection,
38
+ // Legacy no-op blocks:
39
+ motion_scroll_right: () => {},
40
+ motion_scroll_up: () => {},
41
+ motion_align_scene: () => {},
42
+ motion_xscroll: () => {},
43
+ motion_yscroll: () => {}
44
+ };
45
+ }
46
+
47
+ getMonitored () {
48
+ return {
49
+ motion_xposition: {
50
+ isSpriteSpecific: true,
51
+ getId: targetId => `${targetId}_xposition`
52
+ },
53
+ motion_yposition: {
54
+ isSpriteSpecific: true,
55
+ getId: targetId => `${targetId}_yposition`
56
+ },
57
+ motion_direction: {
58
+ isSpriteSpecific: true,
59
+ getId: targetId => `${targetId}_direction`
60
+ }
61
+ };
62
+ }
63
+
64
+ moveSteps (args, util) {
65
+ const steps = Cast.toNumber(args.STEPS);
66
+ const radians = MathUtil.degToRad(90 - util.target.direction);
67
+ const dx = steps * Math.cos(radians);
68
+ const dy = steps * Math.sin(radians);
69
+ util.target.setXY(util.target.x + dx, util.target.y + dy);
70
+ }
71
+
72
+ goToXY (args, util) {
73
+ const x = Cast.toNumber(args.X);
74
+ const y = Cast.toNumber(args.Y);
75
+ util.target.setXY(x, y);
76
+ }
77
+
78
+ getTargetXY (targetName, util) {
79
+ let targetX = 0;
80
+ let targetY = 0;
81
+ if (targetName === '_mouse_') {
82
+ targetX = util.ioQuery('mouse', 'getScratchX');
83
+ targetY = util.ioQuery('mouse', 'getScratchY');
84
+ } else if (targetName === '_random_') {
85
+ const stageWidth = this.runtime.constructor.STAGE_WIDTH;
86
+ const stageHeight = this.runtime.constructor.STAGE_HEIGHT;
87
+ targetX = Math.round(stageWidth * (Math.random() - 0.5));
88
+ targetY = Math.round(stageHeight * (Math.random() - 0.5));
89
+ } else {
90
+ targetName = Cast.toString(targetName);
91
+ const goToTarget = this.runtime.getSpriteTargetByName(targetName);
92
+ if (!goToTarget) return;
93
+ targetX = goToTarget.x;
94
+ targetY = goToTarget.y;
95
+ }
96
+ return [targetX, targetY];
97
+ }
98
+
99
+ goTo (args, util) {
100
+ const targetXY = this.getTargetXY(args.TO, util);
101
+ if (targetXY) {
102
+ util.target.setXY(targetXY[0], targetXY[1]);
103
+ }
104
+ }
105
+
106
+ turnRight (args, util) {
107
+ const degrees = Cast.toNumber(args.DEGREES);
108
+ util.target.setDirection(util.target.direction + degrees);
109
+ }
110
+
111
+ turnLeft (args, util) {
112
+ const degrees = Cast.toNumber(args.DEGREES);
113
+ util.target.setDirection(util.target.direction - degrees);
114
+ }
115
+
116
+ pointInDirection (args, util) {
117
+ const direction = Cast.toNumber(args.DIRECTION);
118
+ util.target.setDirection(direction);
119
+ }
120
+
121
+ pointTowards (args, util) {
122
+ let targetX = 0;
123
+ let targetY = 0;
124
+ if (args.TOWARDS === '_mouse_') {
125
+ targetX = util.ioQuery('mouse', 'getScratchX');
126
+ targetY = util.ioQuery('mouse', 'getScratchY');
127
+ } else if (args.TOWARDS === '_random_') {
128
+ util.target.setDirection(Math.round(Math.random() * 360) - 180);
129
+ return;
130
+ } else {
131
+ args.TOWARDS = Cast.toString(args.TOWARDS);
132
+ const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS);
133
+ if (!pointTarget) return;
134
+ targetX = pointTarget.x;
135
+ targetY = pointTarget.y;
136
+ }
137
+
138
+ const dx = targetX - util.target.x;
139
+ const dy = targetY - util.target.y;
140
+ const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx));
141
+ util.target.setDirection(direction);
142
+ }
143
+
144
+ glide (args, util) {
145
+ if (util.stackFrame.timer) {
146
+ const timeElapsed = util.stackFrame.timer.timeElapsed();
147
+ if (timeElapsed < util.stackFrame.duration * 1000) {
148
+ // In progress: move to intermediate position.
149
+ const frac = timeElapsed / (util.stackFrame.duration * 1000);
150
+ const dx = frac * (util.stackFrame.endX - util.stackFrame.startX);
151
+ const dy = frac * (util.stackFrame.endY - util.stackFrame.startY);
152
+ util.target.setXY(
153
+ util.stackFrame.startX + dx,
154
+ util.stackFrame.startY + dy
155
+ );
156
+ util.yield();
157
+ } else {
158
+ // Finished: move to final position.
159
+ util.target.setXY(util.stackFrame.endX, util.stackFrame.endY);
160
+ }
161
+ } else {
162
+ // First time: save data for future use.
163
+ util.stackFrame.timer = new Timer();
164
+ util.stackFrame.timer.start();
165
+ util.stackFrame.duration = Cast.toNumber(args.SECS);
166
+ util.stackFrame.startX = util.target.x;
167
+ util.stackFrame.startY = util.target.y;
168
+ util.stackFrame.endX = Cast.toNumber(args.X);
169
+ util.stackFrame.endY = Cast.toNumber(args.Y);
170
+ if (util.stackFrame.duration <= 0) {
171
+ // Duration too short to glide.
172
+ util.target.setXY(util.stackFrame.endX, util.stackFrame.endY);
173
+ return;
174
+ }
175
+ util.yield();
176
+ }
177
+ }
178
+
179
+ glideTo (args, util) {
180
+ const targetXY = this.getTargetXY(args.TO, util);
181
+ if (targetXY) {
182
+ this.glide({SECS: args.SECS, X: targetXY[0], Y: targetXY[1]}, util);
183
+ }
184
+ }
185
+
186
+ ifOnEdgeBounce (args, util) {
187
+ const bounds = util.target.getBounds();
188
+ if (!bounds) {
189
+ return;
190
+ }
191
+ // Measure distance to edges.
192
+ // Values are positive when the sprite is far away,
193
+ // and clamped to zero when the sprite is beyond.
194
+ const stageWidth = this.runtime.constructor.STAGE_WIDTH;
195
+ const stageHeight = this.runtime.constructor.STAGE_HEIGHT;
196
+ const distLeft = Math.max(0, (stageWidth / 2) + bounds.left);
197
+ const distTop = Math.max(0, (stageHeight / 2) - bounds.top);
198
+ const distRight = Math.max(0, (stageWidth / 2) - bounds.right);
199
+ const distBottom = Math.max(0, (stageHeight / 2) + bounds.bottom);
200
+ // Find the nearest edge.
201
+ let nearestEdge = '';
202
+ let minDist = Infinity;
203
+ if (distLeft < minDist) {
204
+ minDist = distLeft;
205
+ nearestEdge = 'left';
206
+ }
207
+ if (distTop < minDist) {
208
+ minDist = distTop;
209
+ nearestEdge = 'top';
210
+ }
211
+ if (distRight < minDist) {
212
+ minDist = distRight;
213
+ nearestEdge = 'right';
214
+ }
215
+ if (distBottom < minDist) {
216
+ minDist = distBottom;
217
+ nearestEdge = 'bottom';
218
+ }
219
+ if (minDist > 0) {
220
+ return; // Not touching any edge.
221
+ }
222
+ // Point away from the nearest edge.
223
+ const radians = MathUtil.degToRad(90 - util.target.direction);
224
+ let dx = Math.cos(radians);
225
+ let dy = -Math.sin(radians);
226
+ if (nearestEdge === 'left') {
227
+ dx = Math.max(0.2, Math.abs(dx));
228
+ } else if (nearestEdge === 'top') {
229
+ dy = Math.max(0.2, Math.abs(dy));
230
+ } else if (nearestEdge === 'right') {
231
+ dx = 0 - Math.max(0.2, Math.abs(dx));
232
+ } else if (nearestEdge === 'bottom') {
233
+ dy = 0 - Math.max(0.2, Math.abs(dy));
234
+ }
235
+ const newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90;
236
+ util.target.setDirection(newDirection);
237
+ // Keep within the stage.
238
+ const fencedPosition = util.target.keepInFence(util.target.x, util.target.y);
239
+ util.target.setXY(fencedPosition[0], fencedPosition[1]);
240
+ }
241
+
242
+ setRotationStyle (args, util) {
243
+ util.target.setRotationStyle(args.STYLE);
244
+ }
245
+
246
+ changeX (args, util) {
247
+ const dx = Cast.toNumber(args.DX);
248
+ util.target.setXY(util.target.x + dx, util.target.y);
249
+ }
250
+
251
+ setX (args, util) {
252
+ const x = Cast.toNumber(args.X);
253
+ util.target.setXY(x, util.target.y);
254
+ }
255
+
256
+ changeY (args, util) {
257
+ const dy = Cast.toNumber(args.DY);
258
+ util.target.setXY(util.target.x, util.target.y + dy);
259
+ }
260
+
261
+ setY (args, util) {
262
+ const y = Cast.toNumber(args.Y);
263
+ util.target.setXY(util.target.x, y);
264
+ }
265
+
266
+ getX (args, util) {
267
+ return this.limitPrecision(util.target.x);
268
+ }
269
+
270
+ getY (args, util) {
271
+ return this.limitPrecision(util.target.y);
272
+ }
273
+
274
+ getDirection (args, util) {
275
+ return util.target.direction;
276
+ }
277
+
278
+ // This corresponds to snapToInteger in Scratch 2
279
+ limitPrecision (coordinate) {
280
+ const rounded = Math.round(coordinate);
281
+ const delta = coordinate - rounded;
282
+ const limitedCoord = (Math.abs(delta) < 1e-9) ? rounded : coordinate;
283
+
284
+ return limitedCoord;
285
+ }
286
+ }
287
+
288
+ module.exports = Scratch3MotionBlocks;
@@ -0,0 +1,154 @@
1
+ const Cast = require('../util/cast.js');
2
+ const MathUtil = require('../util/math-util.js');
3
+
4
+ class Scratch3OperatorsBlocks {
5
+ constructor (runtime) {
6
+ /**
7
+ * The runtime instantiating this block package.
8
+ * @type {Runtime}
9
+ */
10
+ this.runtime = runtime;
11
+ }
12
+
13
+ /**
14
+ * Retrieve the block primitives implemented by this package.
15
+ * @return {object.<string, Function>} Mapping of opcode to Function.
16
+ */
17
+ getPrimitives () {
18
+ return {
19
+ operator_add: this.add,
20
+ operator_subtract: this.subtract,
21
+ operator_multiply: this.multiply,
22
+ operator_divide: this.divide,
23
+ operator_lt: this.lt,
24
+ operator_equals: this.equals,
25
+ operator_gt: this.gt,
26
+ operator_and: this.and,
27
+ operator_or: this.or,
28
+ operator_not: this.not,
29
+ operator_random: this.random,
30
+ operator_join: this.join,
31
+ operator_letter_of: this.letterOf,
32
+ operator_length: this.length,
33
+ operator_contains: this.contains,
34
+ operator_mod: this.mod,
35
+ operator_round: this.round,
36
+ operator_mathop: this.mathop
37
+ };
38
+ }
39
+
40
+ add (args) {
41
+ return Cast.toNumber(args.NUM1) + Cast.toNumber(args.NUM2);
42
+ }
43
+
44
+ subtract (args) {
45
+ return Cast.toNumber(args.NUM1) - Cast.toNumber(args.NUM2);
46
+ }
47
+
48
+ multiply (args) {
49
+ return Cast.toNumber(args.NUM1) * Cast.toNumber(args.NUM2);
50
+ }
51
+
52
+ divide (args) {
53
+ return Cast.toNumber(args.NUM1) / Cast.toNumber(args.NUM2);
54
+ }
55
+
56
+ lt (args) {
57
+ return Cast.compare(args.OPERAND1, args.OPERAND2) < 0;
58
+ }
59
+
60
+ equals (args) {
61
+ return Cast.compare(args.OPERAND1, args.OPERAND2) === 0;
62
+ }
63
+
64
+ gt (args) {
65
+ return Cast.compare(args.OPERAND1, args.OPERAND2) > 0;
66
+ }
67
+
68
+ and (args) {
69
+ return Cast.toBoolean(args.OPERAND1) && Cast.toBoolean(args.OPERAND2);
70
+ }
71
+
72
+ or (args) {
73
+ return Cast.toBoolean(args.OPERAND1) || Cast.toBoolean(args.OPERAND2);
74
+ }
75
+
76
+ not (args) {
77
+ return !Cast.toBoolean(args.OPERAND);
78
+ }
79
+
80
+ random (args) {
81
+ const nFrom = Cast.toNumber(args.FROM);
82
+ const nTo = Cast.toNumber(args.TO);
83
+ const low = nFrom <= nTo ? nFrom : nTo;
84
+ const high = nFrom <= nTo ? nTo : nFrom;
85
+ if (low === high) return low;
86
+ // If both arguments are ints, truncate the result to an int.
87
+ if (Cast.isInt(args.FROM) && Cast.isInt(args.TO)) {
88
+ return low + Math.floor(Math.random() * ((high + 1) - low));
89
+ }
90
+ return (Math.random() * (high - low)) + low;
91
+ }
92
+
93
+ join (args) {
94
+ return Cast.toString(args.STRING1) + Cast.toString(args.STRING2);
95
+ }
96
+
97
+ letterOf (args) {
98
+ const index = Cast.toNumber(args.LETTER) - 1;
99
+ const str = Cast.toString(args.STRING);
100
+ // Out of bounds?
101
+ if (index < 0 || index >= str.length) {
102
+ return '';
103
+ }
104
+ return str.charAt(index);
105
+ }
106
+
107
+ length (args) {
108
+ return Cast.toString(args.STRING).length;
109
+ }
110
+
111
+ contains (args) {
112
+ const format = function (string) {
113
+ return Cast.toString(string).toLowerCase();
114
+ };
115
+ return format(args.STRING1).includes(format(args.STRING2));
116
+ }
117
+
118
+ mod (args) {
119
+ const n = Cast.toNumber(args.NUM1);
120
+ const modulus = Cast.toNumber(args.NUM2);
121
+ let result = n % modulus;
122
+ // Scratch mod uses floored division instead of truncated division.
123
+ if (result / modulus < 0) result += modulus;
124
+ return result;
125
+ }
126
+
127
+ round (args) {
128
+ return Math.round(Cast.toNumber(args.NUM));
129
+ }
130
+
131
+ mathop (args) {
132
+ const operator = Cast.toString(args.OPERATOR).toLowerCase();
133
+ const n = Cast.toNumber(args.NUM);
134
+ switch (operator) {
135
+ case 'abs': return Math.abs(n);
136
+ case 'floor': return Math.floor(n);
137
+ case 'ceiling': return Math.ceil(n);
138
+ case 'sqrt': return Math.sqrt(n);
139
+ case 'sin': return parseFloat(Math.sin((Math.PI * n) / 180).toFixed(10));
140
+ case 'cos': return parseFloat(Math.cos((Math.PI * n) / 180).toFixed(10));
141
+ case 'tan': return MathUtil.tan(n);
142
+ case 'asin': return (Math.asin(n) * 180) / Math.PI;
143
+ case 'acos': return (Math.acos(n) * 180) / Math.PI;
144
+ case 'atan': return (Math.atan(n) * 180) / Math.PI;
145
+ case 'ln': return Math.log(n);
146
+ case 'log': return Math.log(n) / Math.LN10;
147
+ case 'e ^': return Math.exp(n);
148
+ case '10 ^': return Math.pow(10, n);
149
+ }
150
+ return 0;
151
+ }
152
+ }
153
+
154
+ module.exports = Scratch3OperatorsBlocks;
@@ -0,0 +1,79 @@
1
+ class Scratch3ProcedureBlocks {
2
+ constructor (runtime) {
3
+ /**
4
+ * The runtime instantiating this block package.
5
+ * @type {Runtime}
6
+ */
7
+ this.runtime = runtime;
8
+ }
9
+
10
+ /**
11
+ * Retrieve the block primitives implemented by this package.
12
+ * @return {object.<string, Function>} Mapping of opcode to Function.
13
+ */
14
+ getPrimitives () {
15
+ return {
16
+ procedures_definition: this.definition,
17
+ procedures_call: this.call,
18
+ argument_reporter_string_number: this.argumentReporterStringNumber,
19
+ argument_reporter_boolean: this.argumentReporterBoolean
20
+ };
21
+ }
22
+
23
+ definition () {
24
+ // No-op: execute the blocks.
25
+ }
26
+
27
+ call (args, util) {
28
+ if (!util.stackFrame.executed) {
29
+ const procedureCode = args.mutation.proccode;
30
+ const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode);
31
+
32
+ // If null, procedure could not be found, which can happen if custom
33
+ // block is dragged between sprites without the definition.
34
+ // Match Scratch 2.0 behavior and noop.
35
+ if (paramNamesIdsAndDefaults === null) {
36
+ return;
37
+ }
38
+
39
+ const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
40
+
41
+ // Initialize params for the current stackFrame to {}, even if the procedure does
42
+ // not take any arguments. This is so that `getParam` down the line does not look
43
+ // at earlier stack frames for the values of a given parameter (#1729)
44
+ util.initParams();
45
+ for (let i = 0; i < paramIds.length; i++) {
46
+ if (Object.prototype.hasOwnProperty.call(args, paramIds[i])) {
47
+ util.pushParam(paramNames[i], args[paramIds[i]]);
48
+ } else {
49
+ util.pushParam(paramNames[i], paramDefaults[i]);
50
+ }
51
+ }
52
+
53
+ util.stackFrame.executed = true;
54
+ util.startProcedure(procedureCode);
55
+ }
56
+ }
57
+
58
+ argumentReporterStringNumber (args, util) {
59
+ const value = util.getParam(args.VALUE);
60
+ if (value === null) {
61
+ // When the parameter is not found in the most recent procedure
62
+ // call, the default is always 0.
63
+ return 0;
64
+ }
65
+ return value;
66
+ }
67
+
68
+ argumentReporterBoolean (args, util) {
69
+ const value = util.getParam(args.VALUE);
70
+ if (value === null) {
71
+ // When the parameter is not found in the most recent procedure
72
+ // call, the default is always 0.
73
+ return 0;
74
+ }
75
+ return value;
76
+ }
77
+ }
78
+
79
+ module.exports = Scratch3ProcedureBlocks;