niti: fix type in tool description
[nit.git] / examples / shoot / src / shoot_logic.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Space shooter.
16 # This program is a fun game but also a good example of the scene2d module
17 module shoot_logic
18
19 import scene2d
20
21 # The ship of the player
22 class Player
23 super Sprite
24
25 # Where the player is going
26 var going_target = new GoingTarget
27
28 # Activate the `going_target`
29 fun goes_to(x,y: Int, speed: Int)
30 do
31 going_target.x = x
32 going_target.y = y
33 going_target.active = true
34 var angle = angle_to(going_target)
35 set_velocity(angle, speed)
36 end
37
38 # Current forture of the player
39 var money: Int writable = 0
40
41 # Number of basic bullets fired together
42 var nbshoots: Int writable = 1
43
44 # Time bebore the player shoot again a basic bullet (cooldown)
45 # Shoot if 0
46 var shoot_ttl = 0
47
48 # Number of missiles
49 var nbmissiles: Int writable = 0
50
51 # Time bebore the player shoot again a missile (cooldown)
52 # Shoot if 0
53 var missile_ttl = 0
54
55 # Remainind time when the player is protected from impacts
56 var protected_ttl = 0
57
58 # The associated play scene
59 # (mainly used to registed shoots)
60 var scene: PlayScene
61
62 init(scene: PlayScene) do
63 self.scene = scene
64 self.width = 2400
65 self.height = 2400
66 end
67
68 redef fun update
69 do
70 super
71
72 # Out of screen?
73 if self.y < 0 then
74 self.y = 0
75 self.vy = 0
76 else if self.y > scene.height then
77 self.y = scene.height
78 self.vy = 0
79 end
80 if self.x < 0 then
81 self.x = 0
82 self.vx = 0
83 else if self.x > scene.width then
84 self.x = scene.width
85 self.vx = 0
86 end
87
88 # Update of the player protection if any
89 if protected_ttl > 0 then protected_ttl -= 1
90
91 # Need to shoot basic bullets?
92 if shoot_ttl > 0 then
93 shoot_ttl -= 1
94 else
95 shoot_ttl = 30
96 for i in [0..nbshoots[ do
97 var shoot = new Shoot(scene)
98 shoot.x = x
99 shoot.y = top
100 shoot.vy = -500
101 shoot.vx = (i - nbshoots / 2) * 100
102 scene.player_shoots.add(shoot)
103 end
104 end
105
106 # Need to shoot missiles?
107 if missile_ttl > 0 then
108 missile_ttl -= 1
109 else if nbmissiles > 0 then
110 missile_ttl = 500 / nbmissiles
111 var shoot = new Missile(scene)
112 shoot.x = x
113 shoot.y = top
114 shoot.vy = -300
115 shoot.vx = 0
116 scene.player_shoots.add(shoot)
117 end
118
119 end
120
121 # Time before the player is respawned by the scene
122 var respawn_ttl: Int = 0
123
124 fun hit
125 do
126 if self.protected_ttl > 0 then return
127 self.scene.explosion(self.x, self.y, 10)
128 self.exists = false
129
130 # Reset the position for respawn
131 self.x = scene.width / 2
132 self.y = scene.height - 10000
133 self.vx = 0
134 self.vy = 0
135 self.respawn_ttl = 50
136 end
137
138 end
139
140 # Sprites that may be hit by the player.
141 # Eq. enemies, bullets, loots, etc.
142 class Hitable
143 super Sprite
144
145 # What do do when self is hit by the player.
146 # By defaut, do nothing
147 fun hit_by_player(player: Player) do end
148 end
149
150 # Destination for the player (pointer position)
151 class GoingTarget
152 super Hitable
153
154 # true in on move, false if player is at rest
155 var active writable = false
156
157 init do
158 self.width = 500
159 self.height = 500
160 end
161
162 redef fun hit_by_player(player)
163 do
164 if not active then return
165 active = false
166 player.vx = 0
167 player.vy = 0
168 end
169 end
170
171 # A bullet shooted by a ship
172 class Shoot
173 super Hitable
174
175 # Was the shoot fired by an enemy.
176 # Since there is no frendly fire, it is important to distinguish ownership
177 var enemy: Bool = false
178
179 # The scene of the sprite
180 # Is used with bound limits
181 var scene: PlayScene
182
183 init(scene: PlayScene)
184 do
185 self.scene = scene
186 self.width = 800
187 self.height = 800
188 end
189
190 redef fun update
191 do
192 super
193
194 # Out of screen ?
195 if self.y < -100 * 100 or self.y > scene.height + 10000 or self.x < -100 * 100 or self.x > scene.width + 10000 then
196 self.exists = false
197 end
198 end
199
200 redef fun hit_by_player(player)
201 do
202 player.hit
203 self.exists = false
204 end
205 end
206
207 # A advanced bullet that aims a target (player or enemy)
208 class Missile
209 super Shoot
210
211 # The target aquired by the missile
212 var target: nullable Sprite = null
213
214 # When ttl is 0 then the angle stay fixed
215 # The angle is updated toward the target if ttl>0
216 var ttl: Int = 200
217
218 redef fun update
219 do
220 super
221
222 # Do we still update the angle ?
223 if ttl <= 0 then return
224 ttl -= 1
225
226 # Do we have a target?
227 var target = self.target
228 if target == null or not target.exists then return
229
230 # Just update the angle
231 var angle = self.angle_to(target)
232 self.set_velocity(angle, 300)
233 end
234 end
235
236 # A enemy ship
237 # Various enemies exists, each kind has its own subclass
238 abstract class Enemy
239 super Hitable
240
241 # The scene of the ship
242 # Is used to store created bullets or to get info about the player
243 var scene: PlayScene
244
245 # Time bebore the enemy shoot again (cooldown)
246 # Shoot if 0
247 # The default value is used as a grace period to avoid a first shoot on
248 # the first update
249 var shoot_ttl = 50
250
251 init(scene: PlayScene)
252 do
253 self.width = 2400
254 self.height = 2400
255 self.scene = scene
256 scene.enemies.add(self)
257 end
258
259 redef fun update
260 do
261 super
262
263 # Out of screen ?
264 if self.y > scene.height + 10000 or self.x < -100 * 100 or self.x > scene.width + 10000 then
265 # Note: no control on the top to let ennemies appear
266 self.exists = false
267 end
268
269 # Need to shoot?
270 if shoot_ttl > 0 then
271 shoot_ttl -= 1
272 else
273 shoot
274 end
275 end
276
277 # Each enemy has its own kind of shoot strategy
278 # Note: is automatically called by update when shoot_ttl is expired
279 fun shoot do end
280
281 # Money given when the enemy is destroyed
282 fun loot: Int is abstract
283
284 # What to do when the enemy is hit by a player shoot (or by the player himself)?
285 # By default it kill the enemy in an explosion and generate a loot
286 fun hit
287 do
288 self.exists = false
289 scene.explosion(self.x, self.y, 5)
290 if 100.rand < 3 then
291 var upmissile = new UpMissile(scene)
292 upmissile.x = self.x
293 upmissile.y = self.y
294 upmissile.vx = 0
295 upmissile.vy = 0
296 scene.loots.add(upmissile)
297 scene.hitables.add(new LootArea(upmissile, 2000))
298 else
299 for i in [0..self.loot[ do
300 var money = new Money(scene)
301 money.x = self.x
302 money.y = self.y
303 money.set_velocity(100.rand.to_f*pi/50.0, (500+self.loot).rand)
304 scene.loots.add(money)
305 scene.hitables.add(new LootArea(money, 2000))
306 end
307 end
308 end
309
310 redef fun hit_by_player(player)
311 do
312 player.hit
313 hit
314 end
315 end
316
317 # Basic enemy, do not shoot
318 class Enemy0
319 super Enemy
320
321 redef fun loot do return 3
322 end
323
324 # Simple shooter of paris of basic bullets
325 class Enemy1
326 super Enemy
327
328 redef fun shoot
329 do
330 # Next shoot
331 shoot_ttl = 50
332
333 # two bullets shoot each time
334 for dx in [-11, 11] do
335 var shoot = new Shoot(scene)
336 shoot.enemy = true
337 shoot.x = self.x + dx * 100
338 shoot.y = self.bottom
339 shoot.vy = 500
340 scene.enemy_shoots.add(shoot)
341 end
342 end
343
344 redef fun loot do return 5
345 end
346
347 # Enemy that shoot missiles
348 class Enemy2
349 super Enemy
350
351 redef fun shoot
352 do
353 # Next shoot
354 shoot_ttl = 200
355
356 # The missile targets the player
357 var shoot = new Missile(scene)
358 shoot.enemy = true
359 shoot.x = self.x
360 shoot.y = self.bottom
361 shoot.vy = 500
362 shoot.target = scene.player
363 scene.enemy_shoots.add(shoot)
364 end
365
366 redef fun loot do return 10
367 end
368
369 # Enem that shoot rings of basic bullets
370 class Enemy3
371 super Enemy
372
373 redef fun shoot
374 do
375 # Next shoot
376 shoot_ttl = 50
377
378 for i in [0..10[ do
379 var shoot = new Shoot(scene)
380 shoot.enemy = true
381 shoot.x = self.x
382 shoot.y = self.bottom
383 shoot.set_velocity(pi/5.0*i.to_f, 500)
384 scene.enemy_shoots.add(shoot)
385 end
386 end
387
388 redef fun loot do return 20
389 end
390
391 # Enemy with a turret that shoot burst of bullets toward the player
392 class Enemy4
393 super Enemy
394
395 # The angle of the turret
396 var angle: Float = 0.0
397
398 redef fun update
399 do
400 super
401
402 # Rotate the turret toward the player
403 var target = scene.player
404 if target.exists then
405 angle = self.angle_to(target)
406 end
407 end
408
409 # Shoots come in burst
410 var nbshoots: Int = 0
411
412 redef fun shoot
413 do
414 # Next shoot: is there still bullets in the burst?
415 if self.nbshoots < 10 then
416 # Is ther
417 self.nbshoots += 1
418 shoot_ttl = 5
419 else
420 self.nbshoots = 0
421 shoot_ttl = 80
422 end
423
424 # Shoot with the turret angle
425 var shoot = new Shoot(scene)
426 shoot.enemy = true
427 shoot.x = self.x
428 shoot.y = self.y
429 shoot.set_velocity(angle, 500)
430 scene.enemy_shoots.add(shoot)
431 end
432
433 redef fun loot do return 20
434 end
435
436 # Enemy that rush directly on the player
437 class EnemyKamikaze
438 super Enemy
439
440 redef fun update
441 do
442 super
443
444 # Try to target the player
445 var target = scene.player
446 if not target.exists then return
447
448 var angle = self.angle_to(target)
449 self.set_velocity(angle, 600)
450 end
451
452 redef fun loot do return 5
453 end
454
455 # The boss has two semi-independent arms
456 class Boss
457 super Enemy
458
459 # Left arm
460 var left_part: BossPart
461
462 # Right arm
463 var right_part: BossPart
464
465 init(scene)
466 do
467 super
468 self.width = 128 * 100
469 self.height = 100 * 100
470 self.x = scene.width / 2
471 self.y = -100 * 100
472 self.left_part = new BossPart(self, -48*100)
473 self.right_part = new BossPart(self, 48*100)
474 end
475
476 var flick_ttl: Int = 0
477
478 redef fun update
479 do
480 if flick_ttl > 0 then flick_ttl -= 1
481
482 # Path of the boss (down then left<->right)
483 if self.y < 20000 then
484 self.vx = 0
485 self.vy = 100
486 else if self.vx == 0 then
487 self.vx = 100
488 self.vy = 0
489 else if self.x > scene.width - 10000 and self.vx > 0 then
490 self.vx = -self.vx
491 else if self.x < 10000 and self.vx < 0 then
492 self.vx = -self.vx
493 end
494
495 super
496 end
497
498 redef fun shoot
499 do
500 # Do not shoot if not ready
501 if self.vy != 0 then return
502
503 # Try to target the player
504 var target = scene.player
505 if not target.exists then return
506
507 # Next shoot: burst if no arms remains
508 if left_part.exists or right_part.exists then
509 shoot_ttl = 60
510 else
511 shoot_ttl = 20
512 end
513
514 # Shoot the player with a basic bullet
515 var shoot = new Shoot(scene)
516 shoot.enemy = true
517 shoot.x = self.x
518 shoot.y = self.bottom
519 var angle = shoot.angle_to(target)
520 shoot.set_velocity(angle, 500)
521 scene.enemy_shoots.add(shoot)
522 end
523
524 redef fun loot do return 100
525
526 var live: Int = 20
527
528 redef fun hit
529 do
530 # Protected while an arm remains
531 if left_part.exists or right_part.exists then return
532
533 if live > 0 then
534 live -= 1
535 flick_ttl = 2
536 else
537 super
538 scene.explosion(self.x, self.y, 30)
539 end
540 end
541 end
542
543 # An arm of a boss
544 class BossPart
545 super Enemy
546
547 # The associated boss
548 var boss: Boss
549
550 # Relative x coordonate (center to center) of the arm
551 var relx: Int
552
553 # Relative y coordonate (center to center) of the arm
554 var rely: Int = 36 * 100
555
556 var live: Int = 10
557
558 init(boss: Boss, relx: Int)
559 do
560 self.boss = boss
561 self.relx = relx
562 super(boss.scene)
563 self.width = 32 * 100
564 self.height = 60 * 100
565
566 # Alternate the shoots of the arms
567 if relx > 0 then
568 shoot_ttl += 300
569 end
570 self.x = boss.x + relx
571 self.y = boss.y + rely
572 end
573
574 redef fun update
575 do
576 self.x = boss.x + relx
577 self.y = boss.y + rely
578
579 super
580
581 if flick_ttl > 0 then flick_ttl -= 1
582 end
583
584 redef fun shoot
585 do
586 # Do not shoot if not ready
587 if self.boss.vy != 0 then return
588
589 # Next shoot
590 shoot_ttl = 600
591
592 # Shoot a missile that targets the player
593 var shoot = new Missile(scene)
594 shoot.enemy = true
595 shoot.x = self.x
596 shoot.y = self.bottom
597 shoot.vy = 500
598 shoot.target = scene.player
599 scene.enemy_shoots.add(shoot)
600 end
601
602 var flick_ttl: Int = 0
603
604 redef fun hit
605 do
606 if live > 0 then
607 live -= 1
608 flick_ttl = 2
609 else
610 super
611 end
612 end
613
614 redef fun loot do return 10
615 end
616
617 # Whatever reward or bonus that can be picked by the player
618 abstract class Loot
619 super Hitable
620
621 var scene: PlayScene
622
623 init(scene: PlayScene)
624 do
625 self.scene = scene
626 self.width = 400
627 self.height = 400
628 end
629
630 # Magnet effect: The loot will move to the target if set
631 # See LootArea for details
632 var target: nullable Sprite = null
633
634 redef fun update
635 do
636 super
637
638 # Out of screen ?
639 if self.y > scene.height + 10000 then
640 self.exists = false
641 end
642
643 var target = self.target
644 if target == null then
645 # Not magneted: deploy
646
647 # Heavy fuild friction to stops the explosion
648 # Loots are placed with a explosion, see `Enemy::hit'
649 self.vx = self.vx*7/8
650 self.vy = self.vy*7/8
651
652 # Background scroling
653 self.y += 50
654
655 else if target.exists then
656 # Magneted: rush toward the target
657 var angle = self.angle_to(target)
658 self.set_velocity(angle, 800)
659
660 else
661 # Magneted but dead target: reset the loot
662 self.vx = 0
663 self.vy = 0
664 self.target = null
665 end
666 end
667 end
668
669 # Basic money loot
670 class Money
671 super Loot
672
673 redef fun hit_by_player(player)
674 do
675 self.exists = false
676 player.money += 1
677 if player.money > 100 then
678 player.money -= 100
679 player.nbshoots += 1
680 end
681 end
682 end
683
684 # Increase the number of missiles
685 class UpMissile
686 super Loot
687
688 redef fun hit_by_player(player)
689 do
690 self.exists = false
691 player.nbmissiles += 1
692 end
693 end
694
695 # A loot area is an invisible field used to implement the magnet effets of loots
696 # The principle is:
697 # * the loot is an invisible sprite with a hitbox larger than the loot hitbox
698 # * the lootbox remains centered on the loot
699 # * when the player hit the lootarea, then the loot is set to target the player
700 # * when the player hit the loot, then the player gains effectively the loot
701 class LootArea
702 super Hitable
703
704 # The associated loot
705 var loot: Loot
706
707 init(loot: Loot, radius: Int)
708 do
709 self.loot = loot
710 self.width = radius * 2 + loot.width
711 self.height = radius * 2 + loot.height
712 end
713
714 redef fun update
715 do
716 # position remains centered on the loot
717 self.x = loot.x
718 self.y = loot.y
719
720 # No area if no loot
721 if not loot.exists then self.exists = false
722
723 # the super is useless but it is a good practice to call it
724 super
725 end
726
727 redef fun hit_by_player(player)
728 do
729 # Kill the area
730 self.exists = false
731
732 # The loot now targets the player
733 loot.target = player
734 end
735 end
736
737 # A non interactive element of an explosion
738 # A real explosion is made of many Explosion object
739 # Use the `PlayScene::explosion` method to generate a full explosion
740 class Explosion
741 super Sprite
742
743 # Time before the sprite vanishes
744 var ttl: Int = 10
745
746 redef fun update
747 do
748 # Heavy fuild friction to stops the explosion
749 self.vx = self.vx*7/8
750 self.vy = self.vy*7/8
751
752 # Background scrolling
753 self.y += 50
754
755 super
756
757 # Vanishes?
758 if ttl > 0 then
759 ttl -= 1
760 else
761 exists = false
762 end
763 end
764 end
765
766 # A star is a non-interactive background element
767 # Stars are used to simulate a continuous global scroling
768 class Star
769 super Sprite
770
771 # The scene of the sprite
772 # Is used with bound limits
773 var scene: ShotScene
774
775 init(scene: ShotScene)
776 do
777 self.scene = scene
778 # Randomely places stars on the plane
779 self.x = scene.width.rand
780 self.y = scene.height.rand
781 self.vy = 40.rand + 11
782 end
783
784 redef fun update
785 do
786 super
787
788 # Replace the star on the top
789 if self.y > scene.height then
790 self.y = 200.rand * -100
791 self.x = scene.width.rand
792 self.vy = 40.rand + 11
793 end
794 end
795 end
796
797 class ShotScene
798 super Scene
799
800 # When a scene need to be replaced, just assign the next_scene to a non null value
801 var next_scene: nullable ShotScene writable = null
802
803 # The width of the whole scene
804 var width: Int writable
805
806 # The height of the whole scene
807 var height: Int writable
808
809 init(w,h: Int)
810 do
811 width = w
812 height = h
813 end
814 end
815
816 # The main play state
817 class PlayScene
818 super ShotScene
819
820 # The player ship
821 var player: Player
822
823 # Shoots of the player
824 var player_shoots = new LiveGroup[Shoot]
825
826 # Enemy ships
827 var enemies = new LiveGroup[Enemy]
828
829 # Soots of the enemy
830 var enemy_shoots = new LiveGroup[Shoot]
831
832 # Collectible loots
833 var loots = new LiveGroup[Loot]
834
835 # Non active stuff like explosions
836 var pasive_stuff = new LiveGroup[LiveObject]
837
838 # Background stuff like stars
839 var background = new LiveGroup[LiveObject]
840
841 # All other hitable sprites
842 var hitables = new LiveGroup[Hitable]
843
844 # All sprites
845 var sprites = new LiveGroup[LiveObject]
846
847 init(w,h)
848 do
849 super
850 self.player = new Player(self)
851 player.x = self.width / 2
852 player.y = self.height - 10000
853 self.sprites.add(background)
854 self.sprites.add(pasive_stuff)
855 self.sprites.add(loots)
856 self.sprites.add(player_shoots)
857 self.sprites.add(enemy_shoots)
858 self.sprites.add(enemies)
859 self.sprites.add(self.player)
860 self.sprites.add(hitables)
861
862 for i in [0..100[ do
863 background.add(new Star(self))
864 end
865
866 hitables.add(player.going_target)
867 end
868
869 # Generate an explosion
870 fun explosion(x, y: Int, radius: Int)
871 do
872 # Project explosion parts from the given position
873 # The strong friction and the short ttl of each part will achieve the effect
874 for i in [0..radius[ do
875 var ex = new Explosion
876 ex.x = x
877 ex.y = y
878 ex.set_velocity(100.rand.to_f*pi/50.0, (50*radius).rand)
879 ex.ttl += radius.rand
880 pasive_stuff.add(ex)
881 end
882 end
883
884 var enemy_remains: Int = 15
885 var boss_wait_ttl: Int = 0
886 var boss: nullable Boss
887
888 redef fun update
889 do
890 sprites.gc
891 sprites.update
892
893 if enemy_remains == 0 then
894 if boss_wait_ttl > 0 then
895 boss_wait_ttl -= 1
896 else if boss == null then
897 boss = new Boss(self)
898 enemy_remains = 15
899 else if not boss.exists then
900 boss = null
901 end
902 else if 100.rand < 1 then
903 enemy_remains -= 1
904 if enemy_remains == 0 then
905 boss_wait_ttl = 500
906 end
907 var rnd = 100.rand
908 var enemy: Enemy
909 if rnd < 40 then
910 enemy = new Enemy0(self)
911 else if rnd < 60 then
912 enemy = new Enemy1(self)
913 else if rnd < 70 then
914 enemy = new EnemyKamikaze(self)
915 else if rnd < 90 then
916 enemy = new Enemy2(self)
917 else if rnd < 95 then
918 enemy = new Enemy3(self)
919 else
920 enemy = new Enemy4(self)
921 end
922 enemy.x = (self.width - 20000).rand + 10000
923 enemy.vy = 200.rand + 100
924 if 10.rand < 3 then
925 enemy.vx = 200.rand - 100
926 end
927 end
928
929 for ps in player_shoots do
930 if not ps.exists then continue
931 var target: nullable Enemy = null
932 var td = 100000 # big int
933 for e in enemies do
934 if not e.exists then continue
935 if ps.overlaps(e) then
936 ps.exists = false
937 e.hit
938 end
939 var d = (e.x - ps.x).abs + (e.y - ps.y).abs
940 if td > d then
941 target = e
942 td = d
943 end
944 end
945 if ps isa Missile and (ps.target == null or not ps.target.exists) then
946 ps.target = target
947 end
948 end
949
950 for e in enemies do
951 if not e.exists then continue
952 if player.exists and player.overlaps(e) then
953 e.hit_by_player(player)
954 end
955 end
956 for s in enemy_shoots do
957 if not s.exists then continue
958 if player.exists and player.overlaps(s) then
959 s.hit_by_player(player)
960 end
961 end
962 for l in loots do
963 if not l.exists then continue
964 if player.exists and player.overlaps(l) then
965 l.hit_by_player(player)
966 end
967 end
968 for l in hitables do
969 if not l.exists then continue
970 if player.exists and player.overlaps(l) then
971 l.hit_by_player(player)
972 end
973 end
974 if not player.exists then
975 if player.respawn_ttl > 0 then
976 player.respawn_ttl -= 1
977 else
978 player.exists = true
979 player.protected_ttl = 100
980 self.sprites.add(self.player)
981 end
982 end
983 end
984 end
985
986 ###
987
988 class MenuScene
989 super ShotScene
990
991 var sprites = new LiveGroup[LiveObject]
992
993 init(w,h)
994 do
995 super
996 for i in [0..100[ do
997 sprites.add(new Star(self))
998 end
999 end
1000
1001 var play: Bool writable = false
1002 var ttl: Int = 50
1003
1004 redef fun update
1005 do
1006 sprites.update
1007
1008 if not play then return
1009 if ttl > 0 then
1010 ttl -= 1
1011 return
1012 end
1013 next_scene = new PlayScene(width,height)
1014 end
1015 end
1016
1017 fun headless_run
1018 do
1019 print "Headless run"
1020 # Only run the playscene
1021 var scene = new PlayScene(80000,60000)
1022 # beefup the player
1023 scene.player.nbshoots = 5
1024 scene.player.nbmissiles = 5
1025 # play
1026 print "Play"
1027 var turns = 10
1028 if args.length > 0 then
1029 turns = args.first.to_i
1030 end
1031 for i in [0..turns[ do
1032 for j in [0..10000[ do
1033 scene.update
1034 end
1035 print "{i}: money={scene.player.money} enemies={scene.enemies.length} shoots={scene.player_shoots.length}"
1036 end
1037 print "Game Over"
1038 end
1039
1040 headless_run