b780a5d3440a491ecf8146e84b15b87d647a9f02
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))
129 show_splash_screen
new Texture("textures/splash.jpg")
135 world_camera
.reset_height
60.0
136 ui_camera
.reset_height
1080.0
138 # Register particle systems
139 particle_systems
.add explosions
140 particle_systems
.add blood
141 particle_systems
.add smoke
142 particle_systems
.add clouds
149 city_texture
.pixelated
= true
150 var city_sprite
= new Sprite(city_texture
, new Point3d[Float](0.0, 370.0, -600.0))
151 city_sprite
.scale
= 0.8
152 sprites
.add city_sprite
155 var ground_mesh
= new Plane
156 ground_mesh
.repeat_x
= 100.0
157 ground_mesh
.repeat_y
= 100.0
159 var ground_material
= new TexturedMaterial(
160 [0.0, 0.1, 0.0, 1.0], [0.4, 0.4, 0.4, 1.0], [0.0]*4)
161 ground_material
.diffuse_texture
= ground_texture
163 var ground_model
= new LeafModel(ground_mesh
, ground_material
)
164 var ground
= new Actor(ground_model
, new Point3d[Float](0.0, 0.0, 0.0))
165 ground
.scale
= 5000.0
169 for i
in 2000.times
do
170 var s
= 0.1 + 0.1.rand
171 var h
= tree_texture
.height
* s
172 var sprite
= new Sprite(tree_texture
,
173 new Point3d[Float](0.0 & 1500.0, h
/2.0 - 10.0*s
, 10.0 - 609.0.rand
))
180 sprite
.tint
= [c
, 1.0, c
, 1.0]
184 var no_clouds_layer
= 200.0
185 for i
in [0 .. 32[ do
187 var x
= 0.0 & 1000.0 * zp
188 var y
= no_clouds_layer
+ (world
.boss_altitude
- no_clouds_layer
*2.0).rand
189 var z
= -500.0*zp
- 10.0
195 clouds
.add
(new Point3d[Float](x
+2.0*a
.cos
*rj
, y
+a
.sin
*rj
, z
& 1.0),
196 48000.0 & 16000.0, inf
)
200 # Move the sun to best light the ISS
201 light
.position
.x
= 2000.0
202 light
.position
.z
= 4000.0
204 # Prepare for intro animation
205 ui_sprites
.add tutorial_goal
206 world_camera
.far
= 1024.0
214 # Update background color
215 var player
= world
.player
216 var player_pos
= if player
!= null then player
.center
else new Point3d[Float](0.0, 200.0, 0.0)
217 var altitude
= player_pos
.y
218 var p
= altitude
/ world
.boss_altitude
220 glClearColor
(0.3*ip
, 0.3*ip
, ip
, 1.0)
221 stars
.alpha
= (1.4*p-0
.4
).clamp
(0.0, 1.0)
225 new Point3d[Float](291.0, 338.0, -601.0),
226 new Point3d[Float](-356.0, 422.0, -601.0)]
232 new Point3d[Float](pos
.x
& r
, pos
.y
& r
, pos
.z
& r
),
233 96000.0 & 16000.0, 10.0)
237 world_camera
.position
.x
= player_pos
.x
238 world_camera
.position
.y
= player_pos
.y
+ 5.0
242 var intro_duration
= 8.0
243 if t
< intro_duration
then
244 var pitch
= t
/intro_duration
245 pitch
= (pitch
*pi
).sin
246 world_camera
.pitch
= pitch
250 if world
.player
== null then
253 world
.planes
.add
new Airplane(new Point3d[Float](0.0, world
.player
.center
.y
- 10.0, 0.0), 16.0, 4.0)
257 ui_sprites
.add_all
([tutorial_wasd
, tutorial_arrows
, tutorial_chute
])
259 world_camera
.pitch
= 0.0
260 world_camera
.far
= 700.0
264 score_counter
.value
= world
.score
266 if world
.player
!= null then alt
= world
.player
.altitude
.to_i
267 altitude_counter
.value
= alt
269 # General movement on the X axis
270 if player
!= null then
272 if pressed_keys
.has
("left") then player
.moving
-= 1.0
273 if pressed_keys
.has
("right") then player
.moving
+= 1.0
274 player
.sprite
.as(PlayerSprite).update
277 # Try to fire as long as a key is pressed
278 if pressed_keys
.not_empty
then
280 if pressed_keys
.has
("a") then
281 if pressed_keys
.has
("w") then
283 else if pressed_keys
.has
("s") then
288 else if pressed_keys
.has
("d") then
289 if pressed_keys
.has
("w") then
291 else if pressed_keys
.has
("s") then
296 else if pressed_keys
.has
("w") then
298 else if pressed_keys
.has
("s") then
302 if a
!= inf
and player
!= null then
303 player
.shoot
(a
, world
)
308 # Low-gravity controls
309 if player
!= null and player
.is_alive
and player
.altitude
>= world
.boss_altitude
then
311 for key
in pressed_keys
do
313 player
.inertia
.y
+= d
314 else if key
== "down" then
315 player
.inertia
.y
-= d
316 else if key
== "left" then
317 player
.inertia
.x
-= d
318 else if key
== "right" then
319 player
.inertia
.x
+= d
326 if won_at
== null then
327 var boss
= world
.boss
328 if boss
!= null and not boss
.is_alive
then
329 self.won_at
= world
.t
333 var t_since_won
= world
.t
- won_at
334 if t_since_won
> 1.0 and not ui_sprites
.has
(outro_directed
) then ui_sprites
.add outro_directed
335 if t_since_won
> 2.0 and not ui_sprites
.has
(outro_created
) then ui_sprites
.add outro_created
339 # Seconds at which the game was won, using `world.t` as reference
340 private var won_at
: nullable Float = null
342 # Remove the tutorial sprite about WASD from `ui_sprites`
343 private fun hide_tutorial_wasd
do if ui_sprites
.has
(tutorial_wasd
) then ui_sprites
.remove
(tutorial_wasd
)
345 # Remove the tutorial sprite about arrows from `ui_sprites`
346 private fun hide_tutorial_arrows
do if ui_sprites
.has
(tutorial_arrows
) then ui_sprites
.remove
(tutorial_arrows
)
348 # Remove the tutorial sprite about the parachute from `ui_sprites`
349 private fun hide_tutorial_chute
do if ui_sprites
.has
(tutorial_chute
) then ui_sprites
.remove
(tutorial_chute
)
351 redef fun accept_event
(event
)
355 if event
isa QuitEvent then
358 else if event
isa KeyEvent then
359 if event
.name
== "escape" and event
.is_down
then
364 var player
= world
.player
365 if player
!= null and player
.is_alive
then
367 # Hide tutorial about arrows once they are used
368 var arrows
= once
["left", "right"]
369 if arrows
.has
(event
.name
) then hide_tutorial_arrows
371 if player
.altitude
< world
.boss_altitude
then
372 if event
.name
== "space" and event
.is_down
and not player
.parachute_deployed
and player
.plane
== null then
374 if player
.parachute_deployed
then
375 var pc
= player
.center
376 world
.parachute
= new Parachute(new Point3d[Float](pc
.x
, pc
.y
+ 5.0, pc
.z-0
.1
), 8.0, 5.0)
381 if (event
.name
== "space" or event
.name
== "up") and event
.is_down
then
385 if event
.name
== "left" then
386 var mod
= if event
.is_down
then -1.0 else 1.0
390 if event
.name
== "right" then
391 var mod
= if event
.is_down
then 1.0 else -1.0
395 if player
.moving
== 0.0 then
396 player
.sprite
.as(PlayerSprite).stop_running
397 else player
.sprite
.as(PlayerSprite).start_running
401 # When player is dead, respawn on spacebar
402 if player
!= null and not player
.is_alive
then
403 if event
.name
== "space" then
415 # Sprite representing this entity if there is no `actor`
416 fun sprite
: Sprite is abstract
419 fun actor
: nullable Actor do return null
424 if actor
!= null then
426 else app
.sprites
.add sprite
429 redef fun destroy
(world
)
434 if actor
!= null then
435 app
.actors
.remove actor
436 else app
.sprites
.remove sprite
448 # Show death animation (explosion)
455 new Point3d[Float](center
.x
& force
, center
.y
& force
, center
.z
& force
),
456 (2048.0 & 4096.0) * force
, 0.3 & 0.1)
462 init do sprite
.scale
= width
/sprite
.texture
.width
464 redef fun update
(dt
, world
)
468 if inertia
.x
< 0.0 then
469 sprite
.invert_x
= false
470 else if inertia
.x
> 0.0 then
471 sprite
.invert_x
= true
477 private fun texture
: Texture do return if center
.y
< 600.0 then app
.planes_sheet
.biplane
else app
.planes_sheet
.jet
479 redef var sprite
= new Sprite(texture
, center
) is lazy
482 redef class Helicopter
483 redef var sprite
= new Sprite(app
.planes_sheet
.helicopter
, center
) is lazy
487 redef var actor
is lazy
do
488 var actor
= new Actor(app
.iss_model
, center
)
489 actor
.rotation
= pi
/2.0
493 redef fun death_animation
496 app
.explosions
.add
(center
, 4096.0 * force
, 0.3)
497 for i
in (8.0*force
).to_i
.times
do
499 new Point3d[Float](center
.x
& force
, center
.y
& force
/8.0, center
.z
& force
),
500 (2048.0 & 1024.0) * force
, 0.3 + 5.0.rand
, 5.0.rand
)
506 redef var sprite
= new Sprite(app
.player_textures
.rand
, center
) is lazy
507 init do sprite
.scale
= width
/sprite
.texture
.width
* 2.0
510 redef class Parachute
511 redef var sprite
= new Sprite(app
.planes_sheet
.parachute
, center
) is lazy
512 init do sprite
.scale
= width
/ sprite
.texture
.width
516 redef var sprite
= new PlayerSprite(app
.player_textures
[1], center
, app
.player_textures
, 0.08) is lazy
517 init do sprite
.scale
= width
/sprite
.texture
.width
* 2.0
519 redef fun update
(dt
, world
)
523 sprite
.invert_x
= false
524 else if moving
< 0.0 then
525 sprite
.invert_x
= true
533 if center
.y
< 10.0 then
534 # Blood splatter on the ground
535 var splatter
= new Actor(app
.splatter_model
,
536 new Point3d[Float](center
.x
, 0.05 & 0.04, center
.y
))
537 splatter
.scale
= 32.0
538 splatter
.rotation
= 2.0 * pi
.rand
539 app
.actors
.add splatter
542 # Display respawn instructions
543 app
.ui_sprites
.add
new Sprite(app
.texts_sheet
.respawn
, app
.ui_camera
.center
)
548 redef var sprite
= new Sprite(weapon
.bullet_texture
, center
) is lazy
552 sprite
.rotation
= angle
557 fun bullet_texture
: Texture do return app
.planes_sheet
.bullet_ak
561 redef fun bullet_texture
do return app
.planes_sheet
.bullet_pistol
564 redef class RocketLauncher
565 redef fun bullet_texture
do return app
.planes_sheet
.bullet_rocket
569 # Scale so it looks like 5 world units wide, not matter the size of the texture
570 init do sprite
.scale
= 5.0/sprite
.texture
.width
574 redef var sprite
= new Sprite(app
.planes_sheet
.ak
, center
) is lazy
577 redef class RocketLauncherPU
578 redef var sprite
= new Sprite(app
.planes_sheet
.rocket
, center
) is lazy
582 redef var sprite
= new Sprite(app
.planes_sheet
.health
, center
) is lazy
583 init do sprite
.scale
= 3.0/sprite
.texture
.height
588 redef fun explode
(center
, force
)
593 app
.explosions
.add
(center
, 8192.0 * force
, 0.3)
594 for i
in (4.0*force
).to_i
.times
do
596 new Point3d[Float](center
.x
& force
, center
.y
& force
/2.0, center
.z
& force
),
597 (4096.0 & 2048.0) * force
, 0.3 & 0.3, 0.5.rand
)
603 # Pad a number with `0`s on the left side to reach `size` digits
604 private fun pad
(size
: Int): String
607 var d
= size
- s
.length
608 if d
> 0 then s
= "0"*d
+ s
613 # Special `Sprite` for the player character which is animated
617 # Animation of the running character
618 var running_animation
: Array[Texture]
620 # Seconds per frame of the animations
621 var time_per_frame
: Float
623 # Currently playing animation
624 private var current_animation
: nullable Array[Texture] = null
626 # Second at witch `current_animation` started
627 private var anim_ot
= 0.0
629 # Start the running animation
632 anim_ot
= app
.world
.t
633 current_animation
= running_animation
636 # Stop the running animation
637 fun stop_running
do current_animation
= null
639 # Update `texture` from `current_animation`
642 var anim
= current_animation
644 var dt
= app
.world
.t
- anim_ot
645 var i
= (dt
/ time_per_frame
).to_i
+2
646 texture
= anim
.modulo
(i
)
651 # Manager to display numbers in sprite
654 # TODO clean up and move up to lib
656 # Number textures, from 0 to 9
658 # Require: `textures.length == 10`
659 var textures
: Array[Texture]
661 # Center of the first digit in UI coordinates
662 var anchor
: Point3d[Float]
664 # Last set of sprites generated to display the `value=`
665 private var sprites
= new Array[Sprite]
667 # Update the value displayed by the counter by inserting new sprites into `app.ui_sprites`
668 fun value
=(value
: Int)
670 # Clean up last used sprites
671 for s
in sprites
do if app
.ui_sprites
.has
(s
) then app
.ui_sprites
.remove s
675 var s
= value
.to_s
# TODO manipulate ints directly
679 var tex
= textures
[i
]
682 sprites
.add
new Sprite(tex
, new Point3d[Float](anchor
.x
+ x
, anchor
.y
, anchor
.z
))
686 # Register sprites to be drawn by `app.ui_camera`
687 app
.ui_sprites
.add_all sprites
691 redef class SmokeProgram
693 # Redef source to get particles that move up faster
694 redef fun vertex_shader_core
do return """
697 c.x += dt * dt * 2.0;
699 gl_Position = c * mvp;
700 gl_PointSize = scale / gl_Position.z * (pt+0.1);
703 v_color.a = pt / 0.1;
705 v_color.a = 1.0 - pt*0.9;