pxt-common-packages 9.4.1 → 9.4.5

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 (65) hide show
  1. package/built/common-sim.d.ts +8 -0
  2. package/built/common-sim.js +74 -0
  3. package/libs/azureiot/built/debug/binary.js +461 -461
  4. package/libs/color/built/debug/binary.js +8 -8
  5. package/libs/color-sensor/built/debug/binary.js +8 -8
  6. package/libs/controller/built/debug/binary.js +7126 -6975
  7. package/libs/controller---none/built/debug/binary.js +7105 -6954
  8. package/libs/core---esp32/pins.cpp +30 -4
  9. package/libs/core---esp32/shims.d.ts +15 -0
  10. package/libs/core---esp32s2/shims.d.ts +15 -0
  11. package/libs/datalogger/built/debug/binary.js +63 -63
  12. package/libs/edge-connector/built/debug/binary.js +8 -8
  13. package/libs/esp32/built/debug/binary.js +462 -462
  14. package/libs/game/_locales/game-jsdoc-strings.json +10 -1
  15. package/libs/game/_locales/game-strings.json +18 -2
  16. package/libs/game/assetTemplates.ts +2 -0
  17. package/libs/game/built/debug/binary.js +7018 -6867
  18. package/libs/game/docs/reference/scene/get-tile-location.md +96 -0
  19. package/libs/game/docs/reference/scene/get-tiles-by-type.md +64 -43
  20. package/libs/game/docs/reference/scene/is-hitting-tile.md +87 -0
  21. package/libs/game/docs/reference/scene/on-hit-wall.md +88 -0
  22. package/libs/game/docs/reference/scene/on-overlap-tile.md +88 -0
  23. package/libs/game/docs/reference/scene/place-on-random-tile.md +95 -41
  24. package/libs/game/docs/reference/scene/place-on-tile.md +88 -0
  25. package/libs/game/docs/reference/scene/set-tile-at.md +45 -41
  26. package/libs/game/docs/reference/scene/set-tilemap.md +89 -0
  27. package/libs/game/docs/reference/scene/set-wall-at.md +86 -0
  28. package/libs/game/docs/reference/scene/tile-at-location-equals.md +97 -0
  29. package/libs/game/docs/reference/scene/tile-kind-at.md +118 -0
  30. package/libs/game/docs/reference/scene.md +20 -16
  31. package/libs/game/docs/reference/sprites/sprite/ax.md +34 -3
  32. package/libs/game/docs/reference/sprites/sprite/ay.md +34 -3
  33. package/libs/game/docs/reference/sprites/sprite/fx.md +123 -0
  34. package/libs/game/docs/reference/sprites/sprite/fy.md +123 -0
  35. package/libs/game/docs/reference/sprites.md +2 -0
  36. package/libs/game/physics.ts +1 -1
  37. package/libs/game/renderText.ts +74 -11
  38. package/libs/game/scenes.ts +5 -1
  39. package/libs/game/sprite.ts +23 -6
  40. package/libs/game/spriteevents.ts +7 -7
  41. package/libs/game/sprites.ts +16 -0
  42. package/libs/game/spritesay.ts +204 -51
  43. package/libs/game/tilemap.ts +173 -27
  44. package/libs/lcd/built/debug/binary.js +8 -8
  45. package/libs/light-spectrum-sensor/built/debug/binary.js +8 -8
  46. package/libs/lora/built/debug/binary.js +8 -8
  47. package/libs/matrix-keypad/built/debug/binary.js +8 -8
  48. package/libs/mqtt/built/debug/binary.js +176 -176
  49. package/libs/net/built/debug/binary.js +176 -176
  50. package/libs/net-game/built/debug/binary.js +8802 -8651
  51. package/libs/palette/built/debug/binary.js +7017 -6866
  52. package/libs/pixel/built/debug/binary.js +8 -8
  53. package/libs/power/built/debug/binary.js +8 -8
  54. package/libs/proximity/built/debug/binary.js +8 -8
  55. package/libs/radio/built/debug/binary.js +8 -8
  56. package/libs/radio-broadcast/built/debug/binary.js +8 -8
  57. package/libs/rotary-encoder/built/debug/binary.js +8 -8
  58. package/libs/screen/built/debug/binary.js +50 -50
  59. package/libs/servo/built/debug/binary.js +8 -8
  60. package/libs/storyboard/built/debug/binary.js +7017 -6866
  61. package/libs/text-to-speech/pxt.json +14 -0
  62. package/libs/text-to-speech/shims.d.ts +47 -0
  63. package/libs/text-to-speech/sim/tts.ts +72 -0
  64. package/libs/text-to-speech/tts.ts +70 -0
  65. package/package.json +1 -1
@@ -36,13 +36,15 @@ mySprite.ax = 0
36
36
 
37
37
  * **value**: the new horizontal acceleration for the sprite in pixels per second, per second.
38
38
 
39
- ## Sprite acceleration
39
+ ## Sprite horizontal acceleration
40
40
 
41
- Acceleration of a sprite makes it speed up or slow down. The value for acceleration determines how quickly the speed of the sprite will change. A positive value makes the sprite speed up as it moves. A negative value causes the sprite to slow down while it moves.
41
+ The value for acceleration determines how quickly the speed of the sprite will change. Acceleration of a sprite makes it speed up in a direction toward the right or left. A positive value causes the sprite's speed in the `right` direction to increase. A negative value causes the sprite's speed in the `left` direction to increase.
42
+
43
+ Acceleration can be used as an opposing force too. If a sprite is travelling toward the right at a certain speed and a negative horizontal acceleration is applied, the sprite will keep moving to the right but it will slow down. When the sprite's speed reaches `0`, the sprite will begin to move to the left.
42
44
 
43
45
  ### ~ hint
44
46
 
45
- **How speed changes**
47
+ #### How speed changes
46
48
 
47
49
  Speed, or velocity, is how much distance an object moves during some period of time. Distance in your game is measured in pixels so the speed of a sprite is in _pixels per second_. Speed is changed by _accelerating_ an object. In your game, a sprite is accelerated by some amount of change in speed per second. So, acceleration is measured in _pixels per second per second_.
48
50
 
@@ -131,6 +133,35 @@ game.onUpdateInterval(1000, function () {
131
133
  })
132
134
  ```
133
135
 
136
+ ### Opposite force #ex3
137
+
138
+ Make a sprite move from the left side of the screen to the right side. Apply acceleration in the opposite direction to slow the sprite down and send back to the left.
139
+
140
+ ```blocks
141
+ let mySprite = sprites.create(img`
142
+ . . . . . . . . . . . . . . . .
143
+ . . . . . . . . . . . . . . . .
144
+ . . . . . 5 5 5 5 5 5 . . . . .
145
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
146
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
147
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
148
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
149
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
150
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
151
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
152
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
153
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
154
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
155
+ . . . . . 5 5 5 5 5 5 . . . . .
156
+ . . . . . . . . . . . . . . . .
157
+ . . . . . . . . . . . . . . . .
158
+ `, SpriteKind.Player)
159
+ mySprite.setStayInScreen(true)
160
+ mySprite.left = 0
161
+ mySprite.vx = 100
162
+ mySprite.ax = -35
163
+ ```
164
+
134
165
  ## See also #seealso
135
166
 
136
167
  [ay](/reference/sprites/sprite/ay),
@@ -36,13 +36,16 @@ mySprite.ay = 0
36
36
 
37
37
  * **value**: the new vertical acceleration for the sprite in pixels per second, per second.
38
38
 
39
- ## Sprite acceleration
39
+ ## Sprite vertical acceleration
40
+
41
+ The value for acceleration determines how quickly the speed of the sprite will change. Acceleration of a sprite makes it speed up in a direction toward the bottom or top. A positive value causes the sprite's speed in the `bottom` direction to increase. A negative value causes the sprite's speed in the `top` direction to increase.
42
+
43
+ Acceleration can be used as an opposing force too. If a sprite is travelling toward the bottom at a certain speed and a negative vertical acceleration is applied, the sprite will keep moving to the bottom but it will slow down. When the sprite's speed reaches `0`, the sprite will begin to move to the top.
40
44
 
41
- Acceleration of a sprite makes it speed up or slow down. The value for acceleration determines how quickly the speed of the sprite will change. A positive value makes the sprite speed up as it moves. A negative value causes the sprite to slow down while it moves.
42
45
 
43
46
  ### ~ hint
44
47
 
45
- **How speed changes**
48
+ #### How speed changes
46
49
 
47
50
  Speed, or velocity, is how much distance an object moves during some period of time. Distance in your game is measured in pixels so the speed of a sprite is in _pixels per second_. Speed is changed by _accelerating_ an object. In your game, a sprite is accelerated by some amount of change in speed per second. So, acceleration is measured in _pixels per second per second_.
48
51
 
@@ -129,6 +132,34 @@ game.onUpdateInterval(1000, function () {
129
132
  interval += 1
130
133
  })
131
134
  ```
135
+ ### Opposite force #ex3
136
+
137
+ Make a sprite move from the top of the screen to the bottom. Apply acceleration in the opposite direction to slow the sprite down and send back to the top.
138
+
139
+ ```blocks
140
+ let mySprite = sprites.create(img`
141
+ . . . . . . . . . . . . . . . .
142
+ . . . . . . . . . . . . . . . .
143
+ . . . . . 5 5 5 5 5 5 . . . . .
144
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
145
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
146
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
147
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
148
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
149
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
150
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
151
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
152
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
153
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
154
+ . . . . . 5 5 5 5 5 5 . . . . .
155
+ . . . . . . . . . . . . . . . .
156
+ . . . . . . . . . . . . . . . .
157
+ `, SpriteKind.Player)
158
+ mySprite.setStayInScreen(true)
159
+ mySprite.top = 0
160
+ mySprite.vy = 80
161
+ mySprite.ay = -35
162
+ ```
132
163
 
133
164
  ## See also #seealso
134
165
 
@@ -0,0 +1,123 @@
1
+ # fy (property)
2
+
3
+ Get or set the friction opposing a sprite's motion in the vertical direction.
4
+
5
+ ## Get
6
+
7
+ Get the vertical friction on the sprite.
8
+
9
+ ```block
10
+ let mySprite: Sprite = null
11
+
12
+ let horzAccel = mySprite.fy
13
+ ```
14
+
15
+ ```typescript-ignorelet
16
+ horzAccel = mySprite.fy
17
+ ```
18
+
19
+ ### Returns
20
+
21
+ * a [number](/types/number) that is the current vertical friction of the sprite.
22
+
23
+ ## Set
24
+
25
+ Set the vertical friction for the sprite.
26
+
27
+ ```block
28
+ let mySprite: Sprite = null
29
+
30
+ mySprite.fy = 0
31
+ ```
32
+
33
+ ```typescript-ignore
34
+ mySprite.fy = 0
35
+ ```
36
+
37
+ ### Parameter
38
+
39
+ * **value**: the new vertical friction opposing the sprite's motion in pixels per second, per second.
40
+
41
+ ## Sprite vertical friction
42
+
43
+ Friction is an opposing force against the motion of a sprite. If a sprite has a vertical velocity (`vy`), it's friction will slow the sprite down until it's vertical velocity becomes `0`. This is similar to setting an opposite vertical acceleration but the opposite acceleration goes away once the sprite stops.
44
+
45
+ ## Examples #example
46
+
47
+ ### Measure stopping time #ex1
48
+
49
+ Move the sprite from top to bottom across the screen. Use the correct amount of friction (`fy`) to make the sprite stop at `3` seconds.
50
+
51
+ ```blocks
52
+ let stopTime = 0
53
+ let counting = true
54
+ let mySprite = sprites.create(img`
55
+ . . . . . . . . . . . . . . . .
56
+ . . . . . . . . . . . . . . . .
57
+ . . . . . 5 5 5 5 5 5 . . . . .
58
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
59
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
60
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
61
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
62
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
63
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
64
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
65
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
66
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
67
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
68
+ . . . . . 5 5 5 5 5 5 . . . . .
69
+ . . . . . . . . . . . . . . . .
70
+ . . . . . . . . . . . . . . . .
71
+ `, SpriteKind.Player)
72
+ mySprite.setStayInScreen(true)
73
+ mySprite.top = 0
74
+ mySprite.fy = 20
75
+ mySprite.vy = 60
76
+ game.onUpdateInterval(100, function () {
77
+ if (counting) {
78
+ stopTime += 0.1
79
+ if (mySprite.vy == 0) {
80
+ counting = false
81
+ mySprite.sayText("" + stopTime + "sec", 5000, false)
82
+ }
83
+ }
84
+ })
85
+ ```
86
+
87
+ ### Skipppng sprite #ex2
88
+
89
+ Make a sprite move from the top to the bottom. Make it skip to the right once every second.
90
+
91
+ ```blocks
92
+ let mySprite = sprites.create(img`
93
+ . . . . . . . . . . . . . . . .
94
+ . . . . . . . . . . . . . . . .
95
+ . . . . . 5 5 5 5 5 5 . . . . .
96
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
97
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
98
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
99
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
100
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
101
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
102
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
103
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
104
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
105
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
106
+ . . . . . 5 5 5 5 5 5 . . . . .
107
+ . . . . . . . . . . . . . . . .
108
+ . . . . . . . . . . . . . . . .
109
+ `, SpriteKind.Player)
110
+ mySprite.setStayInScreen(true)
111
+ mySprite.top = 0
112
+ mySprite.fy = 60
113
+ mySprite.vy = 60
114
+ for (let index = 0; index < 3; index++) {
115
+ mySprite.vy = 60
116
+ pause(1000)
117
+ }
118
+ ```
119
+
120
+ ## See also #seealso
121
+
122
+ [fx](/reference/sprites/sprite/fx),
123
+ [ay](/reference/sprites/sprite/ay)
@@ -0,0 +1,123 @@
1
+ # fy (property)
2
+
3
+ Get or set the friction opposing a sprite's motion in the horizontal direction.
4
+
5
+ ## Get
6
+
7
+ Get the horizontal friction on the sprite.
8
+
9
+ ```block
10
+ let mySprite: Sprite = null
11
+
12
+ let horzAccel = mySprite.fx
13
+ ```
14
+
15
+ ```typescript-ignorelet
16
+ horzAccel = mySprite.fx
17
+ ```
18
+
19
+ ### Returns
20
+
21
+ * a [number](/types/number) that is the current horizontal friction of the sprite.
22
+
23
+ ## Set
24
+
25
+ Set the horizontal friction for the sprite.
26
+
27
+ ```block
28
+ let mySprite: Sprite = null
29
+
30
+ mySprite.fx = 0
31
+ ```
32
+
33
+ ```typescript-ignore
34
+ mySprite.fx = 0
35
+ ```
36
+
37
+ ### Parameter
38
+
39
+ * **value**: the new horizontal friction opposing the sprite's motion in pixels per second, per second.
40
+
41
+ ## Sprite horizontal friction
42
+
43
+ Friction is an opposing force against the motion of a sprite. If a sprite has a horizontal velocity (`vx`), it's friction will slow the sprite down until it's horizontal velocity becomes `0`. This is similar to setting an opposite horizontal acceleration but the opposite acceleration goes away once the sprite stops.
44
+
45
+ ## Examples #example
46
+
47
+ ### Measure stopping time #ex1
48
+
49
+ Move the sprite from left to right across the screen. Use the correct amount of friction (`fx`) to make the sprite stop at `3` seconds.
50
+
51
+ ```blocks
52
+ let stopTime = 0
53
+ let counting = true
54
+ let mySprite = sprites.create(img`
55
+ . . . . . . . . . . . . . . . .
56
+ . . . . . . . . . . . . . . . .
57
+ . . . . . 5 5 5 5 5 5 . . . . .
58
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
59
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
60
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
61
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
62
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
63
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
64
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
65
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
66
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
67
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
68
+ . . . . . 5 5 5 5 5 5 . . . . .
69
+ . . . . . . . . . . . . . . . .
70
+ . . . . . . . . . . . . . . . .
71
+ `, SpriteKind.Player)
72
+ mySprite.setStayInScreen(true)
73
+ mySprite.left = 0
74
+ mySprite.fx = 30
75
+ mySprite.vx = 90
76
+ game.onUpdateInterval(100, function () {
77
+ if (counting) {
78
+ stopTime += 0.1
79
+ if (mySprite.vx == 0) {
80
+ counting = false
81
+ mySprite.sayText("" + stopTime + "sec", 5000, false)
82
+ }
83
+ }
84
+ })
85
+ ```
86
+
87
+ ### Skipppng sprite #ex2
88
+
89
+ Make a sprite move from the left to the right. Make it skip to the right once every second.
90
+
91
+ ```blocks
92
+ let mySprite = sprites.create(img`
93
+ . . . . . . . . . . . . . . . .
94
+ . . . . . . . . . . . . . . . .
95
+ . . . . . 5 5 5 5 5 5 . . . . .
96
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
97
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
98
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
99
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
100
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
101
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
102
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
103
+ . . 5 5 5 5 5 5 5 5 5 5 5 5 . .
104
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
105
+ . . . 5 5 5 5 5 5 5 5 5 5 . . .
106
+ . . . . . 5 5 5 5 5 5 . . . . .
107
+ . . . . . . . . . . . . . . . .
108
+ . . . . . . . . . . . . . . . .
109
+ `, SpriteKind.Player)
110
+ mySprite.setStayInScreen(true)
111
+ mySprite.left = 0
112
+ mySprite.fx = 80
113
+ mySprite.vx = 80
114
+ for (let index = 0; index < 3; index++) {
115
+ mySprite.vx = 80
116
+ pause(1000)
117
+ }
118
+ ```
119
+
120
+ ## See also #seealso
121
+
122
+ [fy](/reference/sprites/sprite/fy),
123
+ [ax](/reference/sprites/sprite/ax)
@@ -58,6 +58,8 @@ sprites.onOverlap(0, 0, function (sprite, otherSprite) {})
58
58
  * [**vy- velocity y**](/reference/sprites/sprite/vy)
59
59
  * [**ax - acceleration x**](/reference/sprites/sprite/ax)
60
60
  * [**ay - acceleration y**](/reference/sprites/sprite/ay)
61
+ * [**fx - friction x**](/reference/sprites/sprite/fx)
62
+ * [**fy - friction y**](/reference/sprites/sprite/fy)
61
63
 
62
64
  ### Image and Attributes
63
65
 
@@ -597,7 +597,7 @@ class ArcadePhysicsEngine extends PhysicsEngine {
597
597
  const alreadyHandled: tiles.Location[] = [];
598
598
 
599
599
  for (const tile of overlappedTiles) {
600
- if (alreadyHandled.some(l => l.col === tile.col && l.row === tile.row)) {
600
+ if (alreadyHandled.some(l => l.column === tile.column && l.row === tile.row)) {
601
601
  continue;
602
602
  }
603
603
  alreadyHandled.push(tile);
@@ -61,6 +61,8 @@ namespace sprites {
61
61
  }
62
62
 
63
63
  calculatePartialHeight(startLine: number, lengthToDraw: number) {
64
+ if (this.linebreaks.length === 0) return this.font.charHeight;
65
+
64
66
  let current = 0;
65
67
 
66
68
  for (let i = startLine; i < this.linebreaks.length + 1; i++) {
@@ -93,9 +95,9 @@ namespace sprites {
93
95
  return total;
94
96
  }
95
97
 
96
- protected lineEnd(index: number) {
97
- const prevEnd = index > 0 ? this.linebreaks[index - 1] : 0;
98
- let end = index < this.linebreaks.length ? this.linebreaks[index] : this.text.length;
98
+ lineEnd(lineIndex: number) {
99
+ const prevEnd = lineIndex > 0 ? this.linebreaks[lineIndex - 1] : 0;
100
+ let end = lineIndex < this.linebreaks.length ? this.linebreaks[lineIndex] : this.text.length;
99
101
  let didMove = false;
100
102
 
101
103
  // Trim trailing whitespace
@@ -115,8 +117,8 @@ namespace sprites {
115
117
  return didMove ? end + 1 : end;
116
118
  }
117
119
 
118
- protected lineStart(index: number) {
119
- let start = index > 0 ? this.linebreaks[index - 1] : 0;
120
+ lineStart(lineIndex: number) {
121
+ let start = lineIndex > 0 ? this.linebreaks[lineIndex - 1] : 0;
120
122
 
121
123
  // Trim leading whitespace
122
124
  while (start < this.text.length) {
@@ -133,6 +135,29 @@ namespace sprites {
133
135
 
134
136
  return start;
135
137
  }
138
+
139
+ widthOfLine(lineIndex: number, fullTextOffset?: number) {
140
+ if (fullTextOffset != undefined) {
141
+ return (Math.min(this.lineEnd(lineIndex), fullTextOffset + 1) - this.lineStart(lineIndex)) * this.font.charWidth;
142
+ }
143
+ return (this.lineEnd(lineIndex) - this.lineStart(lineIndex)) * this.font.charWidth;
144
+ }
145
+
146
+ widthOfLines(lineStartIndex: number, lineEndIndex: number, offset?: number) {
147
+ if (this.linebreaks.length === 0) return this.widthOfLine(0, offset);
148
+
149
+ let width = 0;
150
+ let fullTextOffset: number;
151
+ for (let i = lineStartIndex; i < Math.min(lineEndIndex, this.linebreaks.length + 1); i++) {
152
+ if (offset != undefined) {
153
+ fullTextOffset = this.lineStart(i) + offset;
154
+ offset -= this.lineEnd(i) - this.lineStart(i);
155
+ }
156
+ if (fullTextOffset !== undefined && this.lineStart(i) > fullTextOffset) break;
157
+ width = Math.max(width, this.widthOfLine(i, fullTextOffset));
158
+ }
159
+ return width;
160
+ }
136
161
  }
137
162
 
138
163
 
@@ -203,6 +228,9 @@ namespace sprites {
203
228
  protected pageLine: number;
204
229
  protected timer: number;
205
230
  protected pauseMillis: number;
231
+ protected onTickCB: () => void;
232
+ protected onEndCB: () => void;
233
+ protected prevOffset: number;
206
234
 
207
235
  constructor(public text: RenderText, public height: number) {
208
236
  this.state = RenderTextAnimationState.Idle;
@@ -232,17 +260,21 @@ namespace sprites {
232
260
  }
233
261
 
234
262
  currentHeight() {
235
- const maxHeight = Math.min(
236
- Math.idiv(this.height, this.text.lineHeight()) + 1,
237
- this.text.linebreaks.length + 1 - this.pageLine
238
- ) * this.text.lineHeight();
263
+ const minHeight = this.text.lineHeight();
264
+ const maxHeight = Math.max(
265
+ Math.min(
266
+ Math.idiv(this.height, this.text.lineHeight()) + 1,
267
+ this.text.linebreaks.length + 1 - this.pageLine
268
+ ) * this.text.lineHeight(),
269
+ minHeight
270
+ );
239
271
 
240
272
 
241
273
  if (this.state === RenderTextAnimationState.Printing) {
242
- return Math.min(
274
+ return Math.max(Math.min(
243
275
  this.text.calculatePartialHeight(this.pageLine, this.currentOffset()),
244
276
  maxHeight
245
- );
277
+ ), minHeight)
246
278
  }
247
279
  else if (this.state === RenderTextAnimationState.Pausing) {
248
280
  return maxHeight
@@ -252,10 +284,34 @@ namespace sprites {
252
284
  }
253
285
  }
254
286
 
287
+ currentWidth() {
288
+ return this.text.widthOfLines(
289
+ this.pageLine,
290
+ this.pageLine + Math.idiv(this.currentHeight(), this.text.lineHeight()) + 1,
291
+ this.state === RenderTextAnimationState.Printing ? this.currentOffset() : undefined
292
+ );
293
+ }
294
+
255
295
  currentOffset() {
256
296
  return Math.idiv(control.millis() - this.timer, this.tickPeriod)
257
297
  }
258
298
 
299
+ isDone() {
300
+ return this.state === RenderTextAnimationState.Idle;
301
+ }
302
+
303
+ cancel() {
304
+ this.state = RenderTextAnimationState.Idle;
305
+ }
306
+
307
+ onCharacterPrinted(cb: () => void) {
308
+ this.onTickCB = cb;
309
+ }
310
+
311
+ onAnimationEnd(cb: () => void) {
312
+ this.onEndCB = cb;
313
+ }
314
+
259
315
  draw(canvas: Image, left: number, top: number, color: number) {
260
316
  if (this.state === RenderTextAnimationState.Idle) return;
261
317
  else if (this.state === RenderTextAnimationState.Printing) {
@@ -269,6 +325,10 @@ namespace sprites {
269
325
  this.pageLine + Math.idiv(this.height, this.text.lineHeight()) + 1
270
326
  );
271
327
 
328
+ if (this.onTickCB && this.prevOffset !== this.currentOffset()) {
329
+ this.onTickCB();
330
+ }
331
+
272
332
  if (pageFinished) {
273
333
  this.state = RenderTextAnimationState.Pausing;
274
334
  this.timer = this.pauseMillis
@@ -290,6 +350,7 @@ namespace sprites {
290
350
  this.pageLine += Math.idiv(this.height, this.text.lineHeight()) + 1;
291
351
  if (this.pageLine > this.text.linebreaks.length) {
292
352
  this.state = RenderTextAnimationState.Idle;
353
+ if (this.onEndCB) this.onEndCB();
293
354
  }
294
355
  else {
295
356
  this.state = RenderTextAnimationState.Printing;
@@ -297,6 +358,8 @@ namespace sprites {
297
358
  }
298
359
  }
299
360
  }
361
+
362
+ this.prevOffset = this.currentOffset();
300
363
  }
301
364
  }
302
365
  }
@@ -18,7 +18,7 @@ enum CameraProperty {
18
18
  }
19
19
 
20
20
  //% weight=88 color="#4b6584" icon="\uf1bb"
21
- //% groups='["Screen", "Effects", "Tiles", "Collisions", "Camera"]'
21
+ //% groups='["Screen", "Camera", "Effects", "Tilemaps", "Tilemap Operations", "Locations"]'
22
22
  //% blockGap=8
23
23
  namespace scene {
24
24
  /**
@@ -128,6 +128,7 @@ namespace scene {
128
128
  //% duration.shadow=timePicker duration.defl=500
129
129
  //% group="Camera"
130
130
  //% help=scene/camera-shake
131
+ //% weight=90
131
132
  export function cameraShake(amplitude: number = 4, duration: number = 500) {
132
133
  const scene = game.currentScene();
133
134
  scene.camera.shake(amplitude, duration);
@@ -140,6 +141,7 @@ namespace scene {
140
141
  //% blockId=camerafollow block="camera follow sprite %sprite=variables_get(mySprite)"
141
142
  //% group="Camera"
142
143
  //% help=scene/camera-follow-sprite
144
+ //% weight=100
143
145
  export function cameraFollowSprite(sprite: Sprite) {
144
146
  const scene = game.currentScene();
145
147
  scene.camera.sprite = sprite;
@@ -152,6 +154,7 @@ namespace scene {
152
154
  //% blockId=camerapos block="center camera at x %x y %y"
153
155
  //% group="Camera"
154
156
  //% help=scene/center-camera-at
157
+ //% weight=80
155
158
  export function centerCameraAt(x: number, y: number) {
156
159
  const scene = game.currentScene();
157
160
  scene.camera.sprite = undefined;
@@ -190,6 +193,7 @@ namespace scene {
190
193
  //% blockId=cameraproperty block="camera $property"
191
194
  //% group="Camera"
192
195
  //% help=scene/camera-property
196
+ //% weight=70
193
197
  export function cameraProperty(property: CameraProperty): number {
194
198
  const scene = game.currentScene();
195
199
  switch (property) {
@@ -705,8 +705,9 @@ class Sprite extends sprites.BaseSprite {
705
705
  * @param direction
706
706
  */
707
707
  //% blockId=spritehasobstacle block="is %sprite(mySprite) hitting wall %direction"
708
- //% blockNamespace="scene" group="Collisions" blockGap=8
709
- //% help=sprites/sprite/is-hitting-tile
708
+ //% blockNamespace="scene" group="Locations" blockGap=24
709
+ //% help=scene/is-hitting-tile
710
+ //% weight=15
710
711
  isHittingTile(direction: CollisionDirection): boolean {
711
712
  return this._obstacles && !!this._obstacles[direction];
712
713
  }
@@ -717,8 +718,9 @@ class Sprite extends sprites.BaseSprite {
717
718
  */
718
719
  //% blockId=spritetileat block="tile to $direction of $this(mySprite) is $tile"
719
720
  //% tile.shadow=tileset_tile_picker
720
- //% blockNamespace="scene" group="Collisions" blockGap=8
721
- //% help=sprites/sprite/tile-kind-at
721
+ //% blockNamespace="scene" group="Locations" blockGap=8
722
+ //% help=scene/tile-kind-at
723
+ //% weight=20
722
724
  tileKindAt(direction: TileDirection, tile: Image): boolean {
723
725
  const tilemap = game.currentScene().tileMap;
724
726
  let x = this.x >> tilemap.scale;
@@ -748,13 +750,28 @@ class Sprite extends sprites.BaseSprite {
748
750
  * @param direction
749
751
  */
750
752
  //% blockId=spriteobstacle block="%sprite(mySprite) wall hit on %direction"
751
- //% blockNamespace="scene" group="Collisions"
753
+ //% blockNamespace="scene" group="Locations"
754
+ //% direction.shadow=tiles_collision_direction_editor
752
755
  //% help=sprites/sprite/tile-hit-from
753
756
  //% deprecated=1
754
- tileHitFrom(direction: CollisionDirection): number {
757
+ tileHitFrom(direction: number): number {
755
758
  return (this._obstacles && this._obstacles[direction]) ? this._obstacles[direction].tileIndex : -1;
756
759
  }
757
760
 
761
+ /**
762
+ * Gets the tilemap location at the center of a sprite
763
+ */
764
+ //% block="tilemap location of $this"
765
+ //% blockId=tiles_location_of_sprite
766
+ //% this.shadow=variables_get
767
+ //% this.defl=mySprite
768
+ //% blockNamespace="scene" group="Locations" weight=90
769
+ tilemapLocation(): tiles.Location {
770
+ const scene = game.currentScene();
771
+ if (!scene.tileMap) return undefined;
772
+ return tiles.getTileLocation(this.x >> scene.tileMap.scale, this.y >> scene.tileMap.scale);
773
+ }
774
+
758
775
  clearObstacles() {
759
776
  this._obstacles = [];
760
777
  }
@@ -46,7 +46,7 @@ namespace sprites {
46
46
  //% group="Overlaps"
47
47
  //% weight=100 draggableParameters="reporter"
48
48
  //% blockId=spritesoverlap block="on $sprite of kind $kind=spritekind overlaps $otherSprite of kind $otherKind=spritekind"
49
- //% help=sprites/on-overlap
49
+ //% help=scene/on-overlap
50
50
  //% blockGap=8
51
51
  export function onOverlap(kind: number, otherKind: number, handler: (sprite: Sprite, otherSprite: Sprite) => void) {
52
52
  if (kind == undefined || otherKind == undefined || !handler) return;
@@ -82,11 +82,11 @@ namespace scene {
82
82
  * @param tile
83
83
  * @param handler
84
84
  */
85
- //% group="Tiles"
86
- //% weight=100 draggableParameters="reporter" blockGap=8
85
+ //% group="Tilemaps"
86
+ //% weight=120 draggableParameters="reporter" blockGap=8
87
87
  //% blockId=spriteshittile block="on $sprite of kind $kind=spritekind overlaps $tile at $location"
88
88
  //% tile.shadow=tileset_tile_picker
89
- //% help=tiles/on-overlap-tile
89
+ //% help=scene/on-overlap-tile
90
90
  export function onOverlapTile(kind: number, tile: Image, handler: (sprite: Sprite, location: tiles.Location) => void) {
91
91
  if (kind == undefined || !tile || !handler) return;
92
92
 
@@ -105,10 +105,10 @@ namespace scene {
105
105
  * @param kind
106
106
  * @param handler
107
107
  */
108
- //% group="Tiles"
109
- //% weight=120 draggableParameters="reporter" blockGap=8
108
+ //% group="Tilemaps"
109
+ //% weight=100 draggableParameters="reporter" blockGap=8
110
110
  //% blockId=spriteshitwall block="on $sprite of kind $kind=spritekind hits wall at $location"
111
- //% help=tiles/on-hit-wall
111
+ //% help=scene/on-hit-wall
112
112
  export function onHitWall(kind: number, handler: (sprite: Sprite, location: tiles.Location) => void) {
113
113
  if (kind == undefined || !handler) return;
114
114