e4366e62351e6c9f6d21962c19ed9ca022a45fbf
1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
19 import gamnit
::limit_fps
29 var world
: World = new World is lazy
34 # Textures of the biplane, jet, helicopter, parachute and powerups
35 private var planes_sheet
= new PlanesImages
37 # Animation for the player movement
38 private var player_textures
: Array[Texture] =
39 [for f
in [1..12] do new Texture("textures/player/frame_{f.pad(2)}.png")]
42 private var iss_model
= new Model("models/iss.obj")
47 private var ground_texture
= new Texture("textures/fastgras01.jpg")
48 private var tree_texture
= new Texture("textures/Tree03.png")
53 private var splatter_texture
= new Texture("textures/blood_splatter.png")
54 private var splatter_material
: TexturedMaterial do
55 var mat
= new TexturedMaterial([1.0]*4, [0.0]*4, [0.0]*4)
56 mat
.ambient_texture
= splatter_texture
59 private var splatter_model
= new LeafModel(new Plane, splatter_material
)
64 private var city_texture
= new TextureAsset("textures/city_background_clean.png")
66 private var stars_texture
= new Texture("textures/stars.jpg")
67 private var stars
= new Sprite(stars_texture
, new Point3d[Float](0.0, 1100.0, -600.0)) is lazy
73 var explosions
= new ParticleSystem(20, explosion_program
,
74 new Texture("particles/explosion00.png"))
76 # Blood explosion particles
77 var blood
= new ParticleSystem(20, explosion_program
,
78 new Texture("particles/blood07.png"))
80 # Smoke for the background
81 var smoke
= new ParticleSystem(500, smoke_program
,
82 new Texture("particles/blackSmoke12.png"))
84 # Static clouds particles
85 var clouds
= new ParticleSystem(1600, static_program
,
86 new Texture("particles/whitePuff12.png"))
92 #private var fx_fire = new Sound("sounds/fire.mp3")
96 private var texts_sheet
= new TextsImages
98 private var tutorial_wasd
= new Sprite(app
.texts_sheet
.tutorial_wasd
,
99 app
.ui_camera
.center
.offset
(0.0, -250.0, 0.0)) is lazy
101 private var tutorial_arrows
= new Sprite(app
.texts_sheet
.tutorial_arrows
,
102 app
.ui_camera
.center
.offset
(0.0, -350.0, 0.0)) is lazy
104 private var tutorial_chute
= new Sprite(app
.texts_sheet
.tutorial_chute
,
105 app
.ui_camera
.center
.offset
(0.0, -450.0, 0.0)) is lazy
107 private var tutorial_goal
= new Sprite(app
.texts_sheet
.goal
,
108 app
.ui_camera
.center
.offset
(0.0, 0.0, 0.0)) is lazy
110 private var outro_directed
= new Sprite(app
.texts_sheet
.directed
,
111 app
.ui_camera
.center
.offset
(0.0, 400.0, 0.0)) is lazy
113 private var outro_created
= new Sprite(app
.texts_sheet
.created
,
114 app
.ui_camera
.center
.offset
(0.0, -200.0, 0.0)) is lazy
117 # Counters for the UI
119 private var score_counter
= new CounterSprites(texts_sheet
.n
,
120 new Point3d[Float](32.0, -64.0, 0.0))
122 private var altitude_counter
= new CounterSprites(texts_sheet
.n
,
123 new Point3d[Float](1400.0, -64.0, 0.0))
125 # Did the player asked to skip the intro animation?
126 private var skip_intro
= false
132 show_splash_screen
new Texture("textures/splash.jpg")
138 world_camera
.reset_height
60.0
139 ui_camera
.reset_height
1080.0
141 # Register particle systems
142 particle_systems
.add explosions
143 particle_systems
.add blood
144 particle_systems
.add smoke
145 particle_systems
.add clouds
152 city_texture
.pixelated
= true
153 var city_sprite
= new Sprite(city_texture
, new Point3d[Float](0.0, 370.0, -600.0))
154 city_sprite
.scale
= 0.8
155 sprites
.add city_sprite
158 var ground_mesh
= new Plane
159 ground_mesh
.repeat_x
= 100.0
160 ground_mesh
.repeat_y
= 100.0
162 var ground_material
= new TexturedMaterial(
163 [0.0, 0.1, 0.0, 1.0], [0.4, 0.4, 0.4, 1.0], [0.0]*4)
164 ground_material
.diffuse_texture
= ground_texture
166 var ground_model
= new LeafModel(ground_mesh
, ground_material
)
167 var ground
= new Actor(ground_model
, new Point3d[Float](0.0, 0.0, 0.0))
168 ground
.scale
= 5000.0
172 for i
in 2000.times
do
173 var s
= 0.1 + 0.1.rand
174 var h
= tree_texture
.height
* s
175 var sprite
= new Sprite(tree_texture
,
176 new Point3d[Float](0.0 & 1500.0, h
/2.0 - 10.0*s
, 10.0 - 609.0.rand
))
183 sprite
.tint
= [c
, 1.0, c
, 1.0]
187 var no_clouds_layer
= 200.0
188 for i
in [0 .. 32[ do
190 var x
= 0.0 & 1000.0 * zp
191 var y
= no_clouds_layer
+ (world
.boss_altitude
- no_clouds_layer
*2.0).rand
192 var z
= -500.0*zp
- 10.0
198 clouds
.add
(new Point3d[Float](x
+2.0*a
.cos
*rj
, y
+a
.sin
*rj
, z
& 1.0),
199 48000.0 & 16000.0, inf
)
203 # Move the sun to best light the ISS
204 light
.position
.x
= 2000.0
205 light
.position
.z
= 4000.0
207 # Prepare for intro animation
208 ui_sprites
.add tutorial_goal
209 world_camera
.far
= 1024.0
217 # Update background color
218 var player
= world
.player
219 var player_pos
= if player
!= null then player
.center
else new Point3d[Float](0.0, 200.0, 0.0)
220 var altitude
= player_pos
.y
221 var p
= altitude
/ world
.boss_altitude
223 glClearColor
(0.3*ip
, 0.3*ip
, ip
, 1.0)
224 stars
.alpha
= (1.4*p-0
.4
).clamp
(0.0, 1.0)
228 new Point3d[Float](291.0, 338.0, -601.0),
229 new Point3d[Float](-356.0, 422.0, -601.0)]
235 new Point3d[Float](pos
.x
& r
, pos
.y
& r
, pos
.z
& r
),
236 96000.0 & 16000.0, 10.0)
240 world_camera
.position
.x
= player_pos
.x
241 world_camera
.position
.y
= player_pos
.y
+ 5.0
245 var intro_duration
= 8.0
246 if t
< intro_duration
and not skip_intro
then
247 var pitch
= t
/intro_duration
248 pitch
= (pitch
*pi
).sin
249 world_camera
.pitch
= pitch
253 if world
.player
== null then
254 world_camera
.pitch
= 0.0
255 world_camera
.far
= 700.0
261 score_counter
.value
= world
.score
263 if world
.player
!= null then alt
= world
.player
.altitude
.to_i
264 altitude_counter
.value
= alt
266 # General movement on the X axis
267 if player
!= null then
269 if pressed_keys
.has
("left") then player
.moving
-= 1.0
270 if pressed_keys
.has
("right") then player
.moving
+= 1.0
271 player
.sprite
.as(PlayerSprite).update
274 # Try to fire as long as a key is pressed
275 if pressed_keys
.not_empty
then
277 if pressed_keys
.has
("a") then
278 if pressed_keys
.has
("w") then
280 else if pressed_keys
.has
("s") then
285 else if pressed_keys
.has
("d") then
286 if pressed_keys
.has
("w") then
288 else if pressed_keys
.has
("s") then
293 else if pressed_keys
.has
("w") then
295 else if pressed_keys
.has
("s") then
299 if a
!= inf
and player
!= null then
300 player
.shoot
(a
, world
)
305 # Low-gravity controls
306 if player
!= null and player
.is_alive
and player
.altitude
>= world
.boss_altitude
then
308 for key
in pressed_keys
do
310 player
.inertia
.y
+= d
311 else if key
== "down" then
312 player
.inertia
.y
-= d
313 else if key
== "left" then
314 player
.inertia
.x
-= d
315 else if key
== "right" then
316 player
.inertia
.x
+= d
323 if won_at
== null then
324 var boss
= world
.boss
325 if boss
!= null and not boss
.is_alive
then
326 self.won_at
= world
.t
330 var t_since_won
= world
.t
- won_at
331 if t_since_won
> 1.0 and not ui_sprites
.has
(outro_directed
) then ui_sprites
.add outro_directed
332 if t_since_won
> 2.0 and not ui_sprites
.has
(outro_created
) then ui_sprites
.add outro_created
336 # Begin playing, after intro if `initial`, otherwise after death
337 fun begin_play
(initial
: Bool)
342 world
.planes
.add
new Airplane(new Point3d[Float](0.0, world
.player
.center
.y
- 10.0, 0.0), 16.0, 4.0)
346 ui_sprites
.add_all
([tutorial_wasd
, tutorial_arrows
, tutorial_chute
])
350 # Seconds at which the game was won, using `world.t` as reference
351 private var won_at
: nullable Float = null
353 # Remove the tutorial sprite about WASD from `ui_sprites`
354 private fun hide_tutorial_wasd
do if ui_sprites
.has
(tutorial_wasd
) then ui_sprites
.remove
(tutorial_wasd
)
356 # Remove the tutorial sprite about arrows from `ui_sprites`
357 private fun hide_tutorial_arrows
do if ui_sprites
.has
(tutorial_arrows
) then ui_sprites
.remove
(tutorial_arrows
)
359 # Remove the tutorial sprite about the parachute from `ui_sprites`
360 private fun hide_tutorial_chute
do if ui_sprites
.has
(tutorial_chute
) then ui_sprites
.remove
(tutorial_chute
)
362 redef fun accept_event
(event
)
364 if super then return true
366 if event
isa QuitEvent then
369 else if event
isa KeyEvent then
370 if event
.name
== "escape" and event
.is_down
then
375 var player
= world
.player
376 if player
!= null and player
.is_alive
then
378 # Hide tutorial about arrows once they are used
379 var arrows
= once
["left", "right"]
380 if arrows
.has
(event
.name
) then hide_tutorial_arrows
382 if player
.altitude
< world
.boss_altitude
then
383 if event
.name
== "space" and event
.is_down
and not player
.parachute_deployed
and player
.plane
== null then
385 if player
.parachute_deployed
then
386 var pc
= player
.center
387 world
.parachute
= new Parachute(new Point3d[Float](pc
.x
, pc
.y
+ 5.0, pc
.z-0
.1
), 8.0, 5.0)
392 if (event
.name
== "space" or event
.name
== "up") and event
.is_down
then
396 if event
.name
== "left" then
397 var mod
= if event
.is_down
then -1.0 else 1.0
401 if event
.name
== "right" then
402 var mod
= if event
.is_down
then 1.0 else -1.0
406 if player
.moving
== 0.0 then
407 player
.sprite
.as(PlayerSprite).stop_running
408 else player
.sprite
.as(PlayerSprite).start_running
413 # When player is dead, respawn on spacebar or pointer depressed
414 if (event
isa KeyEvent and event
.name
== "space") or
415 (event
isa PointerEvent and not event
.is_move
and event
.depressed
) then
416 var player
= world
.player
417 if player
== null then
419 else if not player
.is_alive
then
429 # Sprite representing this entity if there is no `actor`
430 fun sprite
: Sprite is abstract
433 fun actor
: nullable Actor do return null
438 if actor
!= null then
440 else app
.sprites
.add sprite
443 redef fun destroy
(world
)
448 if actor
!= null then
449 app
.actors
.remove actor
450 else app
.sprites
.remove sprite
462 # Show death animation (explosion)
469 new Point3d[Float](center
.x
& force
, center
.y
& force
, center
.z
& force
),
470 (2048.0 & 4096.0) * force
, 0.3 & 0.1)
476 init do sprite
.scale
= width
/sprite
.texture
.width
478 redef fun update
(dt
, world
)
482 if inertia
.x
< 0.0 then
483 sprite
.invert_x
= false
484 else if inertia
.x
> 0.0 then
485 sprite
.invert_x
= true
491 private fun texture
: Texture do return if center
.y
< 600.0 then app
.planes_sheet
.biplane
else app
.planes_sheet
.jet
493 redef var sprite
= new Sprite(texture
, center
) is lazy
496 redef class Helicopter
497 redef var sprite
= new Sprite(app
.planes_sheet
.helicopter
, center
) is lazy
501 redef var actor
is lazy
do
502 var actor
= new Actor(app
.iss_model
, center
)
503 actor
.rotation
= pi
/2.0
507 redef fun death_animation
510 app
.explosions
.add
(center
, 4096.0 * force
, 0.3)
511 for i
in (8.0*force
).to_i
.times
do
513 new Point3d[Float](center
.x
& force
, center
.y
& force
/8.0, center
.z
& force
),
514 (2048.0 & 1024.0) * force
, 0.3 + 5.0.rand
, 5.0.rand
)
520 redef var sprite
= new Sprite(app
.player_textures
.rand
, center
) is lazy
521 init do sprite
.scale
= width
/sprite
.texture
.width
* 2.0
524 redef class Parachute
525 redef var sprite
= new Sprite(app
.planes_sheet
.parachute
, center
) is lazy
526 init do sprite
.scale
= width
/ sprite
.texture
.width
530 redef var sprite
= new PlayerSprite(app
.player_textures
[1], center
, app
.player_textures
, 0.08) is lazy
531 init do sprite
.scale
= width
/sprite
.texture
.width
* 2.0
533 redef fun update
(dt
, world
)
537 sprite
.invert_x
= false
538 else if moving
< 0.0 then
539 sprite
.invert_x
= true
547 if center
.y
< 10.0 then
548 # Blood splatter on the ground
549 var splatter
= new Actor(app
.splatter_model
,
550 new Point3d[Float](center
.x
, 0.05 & 0.04, center
.y
))
551 splatter
.scale
= 32.0
552 splatter
.rotation
= 2.0 * pi
.rand
553 app
.actors
.add splatter
556 # Display respawn instructions
557 app
.ui_sprites
.add
new Sprite(app
.texts_sheet
.respawn
, app
.ui_camera
.center
)
562 redef var sprite
= new Sprite(weapon
.bullet_texture
, center
) is lazy
566 sprite
.rotation
= angle
571 fun bullet_texture
: Texture do return app
.planes_sheet
.bullet_ak
575 redef fun bullet_texture
do return app
.planes_sheet
.bullet_pistol
578 redef class RocketLauncher
579 redef fun bullet_texture
do return app
.planes_sheet
.bullet_rocket
583 # Scale so it looks like 5 world units wide, not matter the size of the texture
584 init do sprite
.scale
= 5.0/sprite
.texture
.width
588 redef var sprite
= new Sprite(app
.planes_sheet
.ak
, center
) is lazy
591 redef class RocketLauncherPU
592 redef var sprite
= new Sprite(app
.planes_sheet
.rocket
, center
) is lazy
596 redef var sprite
= new Sprite(app
.planes_sheet
.health
, center
) is lazy
597 init do sprite
.scale
= 3.0/sprite
.texture
.height
602 redef fun explode
(center
, force
)
607 app
.explosions
.add
(center
, 8192.0 * force
, 0.3)
608 for i
in (4.0*force
).to_i
.times
do
610 new Point3d[Float](center
.x
& force
, center
.y
& force
/2.0, center
.z
& force
),
611 (4096.0 & 2048.0) * force
, 0.3 & 0.3, 0.5.rand
)
617 # Pad a number with `0`s on the left side to reach `size` digits
618 private fun pad
(size
: Int): String
621 var d
= size
- s
.length
622 if d
> 0 then s
= "0"*d
+ s
627 # Special `Sprite` for the player character which is animated
631 # Animation of the running character
632 var running_animation
: Array[Texture]
634 # Seconds per frame of the animations
635 var time_per_frame
: Float
637 # Currently playing animation
638 private var current_animation
: nullable Array[Texture] = null
640 # Second at witch `current_animation` started
641 private var anim_ot
= 0.0
643 # Start the running animation
646 anim_ot
= app
.world
.t
647 current_animation
= running_animation
650 # Stop the running animation
651 fun stop_running
do current_animation
= null
653 # Update `texture` from `current_animation`
656 var anim
= current_animation
658 var dt
= app
.world
.t
- anim_ot
659 var i
= (dt
/ time_per_frame
).to_i
+2
660 texture
= anim
.modulo
(i
)
665 # Manager to display numbers in sprite
668 # TODO clean up and move up to lib
670 # Number textures, from 0 to 9
672 # Require: `textures.length == 10`
673 var textures
: Array[Texture]
675 # Center of the first digit in UI coordinates
676 var anchor
: Point3d[Float]
678 # Last set of sprites generated to display the `value=`
679 private var sprites
= new Array[Sprite]
681 # Update the value displayed by the counter by inserting new sprites into `app.ui_sprites`
682 fun value
=(value
: Int)
684 # Clean up last used sprites
685 for s
in sprites
do if app
.ui_sprites
.has
(s
) then app
.ui_sprites
.remove s
689 var s
= value
.to_s
# TODO manipulate ints directly
693 var tex
= textures
[i
]
696 sprites
.add
new Sprite(tex
, new Point3d[Float](anchor
.x
+ x
, anchor
.y
, anchor
.z
))
700 # Register sprites to be drawn by `app.ui_camera`
701 app
.ui_sprites
.add_all sprites
705 redef class SmokeProgram
707 # Redef source to get particles that move up faster
708 redef fun vertex_shader_core
do return """
711 c.x += dt * dt * 2.0;
713 gl_Position = c * mvp;
714 gl_PointSize = scale / gl_Position.z * (pt+0.1);
717 v_color.a = pt / 0.1;
719 v_color.a = 1.0 - pt*0.9;