plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
9cfafea
— Peter John
2026-11-02T20:47:29+05:30
game_scene: keep animating at game over
plum-examples/game_scene.plum
CHANGED
|
@@ -90,7 +90,7 @@ fun mineCount(score: Int) -> Int =
|
|
|
90
90
|
# (origin at the middle of the box, so `rotation` turns it about its centre).
|
|
91
91
|
|
|
92
92
|
# The player. `ax` and `ay` are the input axes (-1..1) for the next `act`.
|
|
93
|
-
record Ship(Actor, vx: Float, vy: Float, ax: Float, ay: Float, hurt: Float, clock: Float, tex: Int) =
|
|
93
|
+
record Ship(Actor, vx: Float, vy: Float, ax: Float, ay: Float, hurt: Float, clock: Float, tex: Int, live: Bool) =
|
|
94
94
|
|
|
95
95
|
fun create(sheet: Int, x: Float, y: Float) -> Ship =
|
|
96
96
|
size := Rules.Cell * Rules.Zoom
|
|
@@ -99,12 +99,14 @@ record Ship(Actor, vx: Float, vy: Float, ax: Float, ay: Float, hurt: Float, cloc
|
|
|
99
99
|
scaleX: 1.0, scaleY: 1.0, rotation: 0.0, color: Color.White, visible: true,
|
|
100
100
|
children: List[Actor](), parent: null, scratch: List[Actor](),
|
|
101
101
|
cull: false, cullX: 0.0, cullY: 0.0, cullW: 0.0, cullH: 0.0,
|
|
102
|
-
vx: 0.0, vy: 0.0, ax: 0.0, ay: 0.0, hurt: 0.0, clock: 0.0, tex: sheet)
|
|
102
|
+
vx: 0.0, vy: 0.0, ax: 0.0, ay: 0.0, hurt: 0.0, clock: 0.0, tex: sheet, live: true)
|
|
103
103
|
|
|
104
104
|
# Accelerates by the input axes, applies drag, keeps the ship in the arena and
|
|
105
105
|
# blinks it while it is hurt (the blink is just `visible`).
|
|
106
106
|
fun act(self, dt: Float) =
|
|
107
107
|
self.clock = self.clock + dt
|
|
108
|
+
if !self.live
|
|
109
|
+
return
|
|
108
110
|
self.hurt = {self.hurt - dt}.max(0.0)
|
|
109
111
|
self.vx = self.vx + self.ax * Rules.Thrust * dt
|
|
110
112
|
self.vy = self.vy + self.ay * Rules.Thrust * dt
|
|
@@ -137,7 +139,7 @@ record Ship(Actor, vx: Float, vy: Float, ax: Float, ay: Float, hurt: Float, cloc
|
|
|
137
139
|
drawSpriteFrame(m, alpha, self.width, self.height, self.color, self.tex, frame * Rules.Cell, 0.0, Rules.Cell, Rules.Cell)
|
|
138
140
|
|
|
139
141
|
# A glowing orb drifting about. `phase` offsets its animation from the other orbs.
|
|
140
|
-
record Orb(Actor, vx: Float, vy: Float, phase: Float, clock: Float, tex: Int) =
|
|
142
|
+
record Orb(Actor, vx: Float, vy: Float, phase: Float, clock: Float, tex: Int, live: Bool) =
|
|
141
143
|
|
|
142
144
|
fun create(sheet: Int, phase: Float, at: Motion) -> Orb =
|
|
143
145
|
size := Rules.Cell * Rules.Zoom
|
|
@@ -146,7 +148,7 @@ record Orb(Actor, vx: Float, vy: Float, phase: Float, clock: Float, tex: Int) =
|
|
|
146
148
|
scaleX: 1.0, scaleY: 1.0, rotation: 0.0, color: Color.White, visible: true,
|
|
147
149
|
children: List[Actor](), parent: null, scratch: List[Actor](),
|
|
148
150
|
cull: false, cullX: 0.0, cullY: 0.0, cullW: 0.0, cullH: 0.0,
|
|
149
|
-
vx: at.vx, vy: at.vy, phase: phase, clock: 0.0, tex: sheet)
|
|
151
|
+
vx: at.vx, vy: at.vy, phase: phase, clock: 0.0, tex: sheet, live: true)
|
|
150
152
|
|
|
151
153
|
fun motion(self) -> Motion =
|
|
152
154
|
Motion(x: self.x, y: self.y, vx: self.vx, vy: self.vy)
|
|
@@ -159,6 +161,8 @@ record Orb(Actor, vx: Float, vy: Float, phase: Float, clock: Float, tex: Int) =
|
|
|
159
161
|
|
|
160
162
|
fun act(self, dt: Float) =
|
|
161
163
|
self.clock = self.clock + dt
|
|
164
|
+
if !self.live
|
|
165
|
+
return
|
|
162
166
|
m := self.motion()
|
|
163
167
|
drift(m, Rules.OrbRadius, dt)
|
|
164
168
|
self.place(m)
|
|
@@ -174,7 +178,7 @@ record Orb(Actor, vx: Float, vy: Float, phase: Float, clock: Float, tex: Int) =
|
|
|
174
178
|
drawSpriteFrame(m, alpha, self.width, self.height, self.color, self.tex, frame * Rules.Cell, Rules.Cell, Rules.Cell, Rules.Cell)
|
|
175
179
|
|
|
176
180
|
# A spinning mine drifting about.
|
|
177
|
-
record Mine(Actor, vx: Float, vy: Float, clock: Float, tex: Int) =
|
|
181
|
+
record Mine(Actor, vx: Float, vy: Float, clock: Float, tex: Int, live: Bool) =
|
|
178
182
|
|
|
179
183
|
fun create(sheet: Int, at: Motion) -> Mine =
|
|
180
184
|
size := Rules.Cell * Rules.Zoom
|
|
@@ -183,7 +187,7 @@ record Mine(Actor, vx: Float, vy: Float, clock: Float, tex: Int) =
|
|
|
183
187
|
scaleX: 1.0, scaleY: 1.0, rotation: 0.0, color: Color.White, visible: true,
|
|
184
188
|
children: List[Actor](), parent: null, scratch: List[Actor](),
|
|
185
189
|
cull: false, cullX: 0.0, cullY: 0.0, cullW: 0.0, cullH: 0.0,
|
|
186
|
-
vx: at.vx, vy: at.vy, clock: 0.0, tex: sheet)
|
|
190
|
+
vx: at.vx, vy: at.vy, clock: 0.0, tex: sheet, live: true)
|
|
187
191
|
|
|
188
192
|
fun motion(self) -> Motion =
|
|
189
193
|
Motion(x: self.x, y: self.y, vx: self.vx, vy: self.vy)
|
|
@@ -196,6 +200,8 @@ record Mine(Actor, vx: Float, vy: Float, clock: Float, tex: Int) =
|
|
|
196
200
|
|
|
197
201
|
fun act(self, dt: Float) =
|
|
198
202
|
self.clock = self.clock + dt
|
|
203
|
+
if !self.live
|
|
204
|
+
return
|
|
199
205
|
m := self.motion()
|
|
200
206
|
drift(m, Rules.MineRadius, dt)
|
|
201
207
|
self.place(m)
|
|
@@ -336,18 +342,27 @@ fun sync(w: World) =
|
|
|
336
342
|
w.lifeIcons.at(i).visible = i < w.lives
|
|
337
343
|
w.overlay.visible = w.over
|
|
338
344
|
|
|
345
|
+
# Lets the actors move (while the game runs) or only animate (once it is over).
|
|
346
|
+
fun setLive(w: World, live: Bool) =
|
|
347
|
+
w.ship.live = live
|
|
348
|
+
for i := range w.orbList.length()
|
|
349
|
+
w.orbList.at(i).live = live
|
|
350
|
+
for i := range w.mineList.length()
|
|
351
|
+
w.mineList.at(i).live = live
|
|
352
|
+
|
|
339
353
|
fun overlaps(ax: Float, ay: Float, ra: Float, bx: Float, by: Float, rb: Float) -> Bool =
|
|
340
354
|
Math.hypot(ax - bx, ay - by) < ra + rb
|
|
341
355
|
|
|
342
356
|
# One simulation step: every actor acts through the stage, then the rules look at
|
|
343
|
-
# the result.
|
|
357
|
+
# the result. Once the game is over the actors only animate; nothing moves or scores.
|
|
344
358
|
fun step(w: World, ax: Float, ay: Float, dt: Float) =
|
|
345
359
|
w.clock = w.clock + dt
|
|
346
|
-
|
|
360
|
+
setLive(w, !w.over)
|
|
347
|
-
return
|
|
348
361
|
w.ship.ax = ax
|
|
349
362
|
w.ship.ay = ay
|
|
350
363
|
w.stage.act(dt)
|
|
364
|
+
if w.over
|
|
365
|
+
return
|
|
351
366
|
ship := w.ship
|
|
352
367
|
for i := range w.orbList.length()
|
|
353
368
|
orb := w.orbList.at(i)
|
|
@@ -544,6 +559,30 @@ test "the game ends at zero lives and then ignores steps"
|
|
|
544
559
|
step(w, 1.0, 0.0, 0.5)
|
|
545
560
|
assert w.ship.x == x
|
|
546
561
|
|
|
562
|
+
test "after game over nothing moves but the animations keep running"
|
|
563
|
+
w := emptyWorld()
|
|
564
|
+
w.lives = 1
|
|
565
|
+
mine := w.mineList.at(0)
|
|
566
|
+
mine.place(Motion(x: w.ship.x, y: w.ship.y, vx: 10.0, vy: 0.0))
|
|
567
|
+
step(w, 0.0, 0.0, 0.016)
|
|
568
|
+
assert w.over
|
|
569
|
+
x := mine.x
|
|
570
|
+
rot := mine.rotation
|
|
571
|
+
clock := mine.clock
|
|
572
|
+
orbClock := w.orbList.at(0).clock
|
|
573
|
+
star := Star.create(0)
|
|
574
|
+
w.stage.root.children.at(1).add(star)
|
|
575
|
+
for i := range 10
|
|
576
|
+
step(w, 1.0, 0.0, 0.1)
|
|
577
|
+
assert mine.x == x
|
|
578
|
+
assert mine.rotation == rot
|
|
579
|
+
assert w.ship.x == Rules.ArenaW / 2.0
|
|
580
|
+
assert !w.ship.visible
|
|
581
|
+
assert mine.clock > clock + 0.9
|
|
582
|
+
assert w.orbList.at(0).clock > orbClock + 0.9
|
|
583
|
+
assert star.clock > 0.9
|
|
584
|
+
assert w.score == 0
|
|
585
|
+
|
|
547
586
|
test "restart resets the run and keeps the best score"
|
|
548
587
|
w := emptyWorld()
|
|
549
588
|
w.score = 90
|