fd9444760de472c557944a792964fd05c644de3f
[nit.git] / examples / shoot / src / shoot.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 is
18 app_name("Space Shooter")
19 app_version(0, 1, git_revision)
20 end
21
22 import mnit
23 import shoot_logic
24
25 redef class Sprite
26 # mnit specific method to draw a sprite
27 # app is used to optain the assets and the display
28 # Each sprite should implements this method
29 fun draw_on_display(app: App) do end
30
31 # Helper function to draw an image centered on the current sprite position
32 fun draw_image(app: App, img: Image)
33 do
34 app.display.blit_centered(img, (self.x.to_f/app.scale).to_i, (self.y.to_f/app.scale).to_i)
35 end
36
37 # Helper function to draw an image translated and rotated on the current sprite position
38 fun draw_rotated_image(app: App, img: Image, dx, dy: Int, angle: Float)
39 do
40 app.display.blit_rotated(img, self.x.to_f/app.scale, self.y.to_f/app.scale, angle)
41 end
42 end
43
44 redef class Player
45 redef fun draw_on_display(app)
46 do
47 if protected_ttl > 30 and protected_ttl%10 > 5 then return # blink on protected
48 draw_image(app, app.img_player)
49 end
50 end
51
52 redef class Shoot
53 redef fun draw_on_display(app)
54 do
55 var img = if enemy then app.img_enemy_shoot else app.img_player_shoot
56 draw_image(app, img)
57 end
58 end
59
60 redef class Missile
61 redef fun draw_on_display(app)
62 do
63 var angle = velocity_angle
64 var img = if enemy then app.img_enemy_missile else app.img_player_missile
65 draw_rotated_image(app, img, 0, 0, angle)
66 end
67 end
68
69 redef class Enemy0
70 redef fun draw_on_display(app)
71 do
72 draw_image(app, app.img_enemy0)
73 end
74 end
75
76 redef class Enemy1
77 redef fun draw_on_display(app)
78 do
79 draw_image(app, app.img_enemy1)
80 end
81 end
82
83 redef class Enemy2
84 redef fun draw_on_display(app)
85 do
86 draw_image(app, app.img_enemy2)
87 end
88 end
89
90 redef class Enemy3
91 redef fun draw_on_display(app)
92 do
93 draw_image(app, app.img_enemy3)
94 end
95 end
96
97 redef class Enemy4
98 redef fun draw_on_display(app)
99 do
100 draw_image(app, app.img_enemy4)
101 draw_rotated_image(app, app.img_enemy4_turret, 0, 0, self.angle)
102 end
103 end
104
105 redef class EnemyKamikaze
106 redef fun draw_on_display(app)
107 do
108 var angle = self.velocity_angle
109 draw_rotated_image(app, app.img_enemy_kamikaze, 0, 0, angle)
110 end
111 end
112
113 redef class Boss
114 redef fun draw_on_display(app)
115 do
116 if flick_ttl > 0 then return
117 draw_image(app, app.img_boss)
118 end
119 end
120
121 redef class BossPart
122 redef fun draw_on_display(app)
123 do
124 if flick_ttl > 0 then return
125 if relx > 0 then
126 self.boss.draw_image(app, app.img_boss_right)
127 else
128 self.boss.draw_image(app, app.img_boss_left)
129 end
130 end
131 end
132
133 redef class Money
134 redef fun draw_on_display(app)
135 do
136 draw_image(app, app.img_money)
137 end
138 end
139
140 redef class UpMissile
141 redef fun draw_on_display(app)
142 do
143 draw_image(app, app.img_upmissile)
144 end
145 end
146
147 redef class Explosion
148 redef fun draw_on_display(app)
149 do
150 draw_image(app, app.img_explosion)
151 end
152 end
153
154 redef class Star
155 redef fun draw_on_display(app)
156 do
157 # Simulate depth:
158 # More a star is fast, more it requires a bright image
159 var img: Image
160 if self.vy < 20 then
161 img = app.img_star0
162 else if self.vy < 40 then
163 img = app.img_star1
164 else
165 img = app.img_star2
166 end
167 draw_image(app, img)
168 end
169 end
170
171 redef class Scene
172 fun draw_on_display(app: App) do end
173 fun input(app: App, input_event: InputEvent): Bool do return false
174 end
175
176 redef class PlayScene
177 redef fun draw_on_display(app)
178 do
179 app.display.clear( 0.0, 0.0, 0.0 )
180 self.sprites.draw(app)
181 for i in [0..player.money[
182 do
183 app.display.blit(app.img_money, 10, app.display.height-10-i)
184 end
185 for i in [1..player.nbshoots]
186 do
187 app.display.blit(app.img_player_shoot, 30, app.display.height-10 - i*10)
188 end
189 for i in [1..player.nbmissiles]
190 do
191 app.display.blit(app.img_player_missile, 40, app.display.height-10 - i*20)
192 end
193 end
194
195 redef fun input(app, input_event)
196 do
197 var speed = 400
198 if input_event isa KeyEvent then
199 if input_event.is_arrow_down then
200 if input_event.is_down then
201 player.vy = speed
202 else
203 player.vy = 0
204 end
205 else if input_event.is_arrow_up then
206 if input_event.is_down then
207 player.vy = -speed
208 else
209 player.vy = 0
210 end
211 else if input_event.is_arrow_left then
212 if input_event.is_down then
213 player.vx = -speed
214 else
215 player.vx = 0
216 end
217 else if input_event.is_arrow_right then
218 if input_event.is_down then
219 player.vx = speed
220 else
221 player.vx = 0
222 end
223 end
224 return true
225 else if input_event isa PointerEvent and input_event.pressed then
226 var x = (input_event.x * app.scale).to_i
227 var y = (input_event.y * app.scale).to_i
228 player.goes_to(x, y, speed)
229 return true
230 end
231 return false # unknown event, can be handled by something else
232 end
233 end
234
235 ###
236
237 redef class MenuScene
238 redef fun draw_on_display(app)
239 do
240 var display = app.display
241 assert display != null
242 blit_fs(display, app.img_splash)
243 sprites.draw(app)
244 if not play or ttl%10 > 5 then
245 blit_fs(display, app.img_splash_play)
246 end
247 end
248
249 fun blit_fs(d: Display, img: Image)
250 do
251 var w = d.width.to_f
252 var h = d.height.to_f
253 d.blit_stretched(img, 0.0,0.0, 0.0,h, w,h, w,0.0)
254 end
255
256 redef fun input(app, input_event)
257 do
258 if input_event isa KeyEvent then
259 play = true
260 return true
261 else if input_event isa PointerEvent then
262 play = true
263 return true
264 end
265 return false
266 end
267 end
268
269 ###
270
271 redef class App
272 super View
273
274 var debug: Bool = false
275
276 redef fun draw_sprite(s: Sprite)
277 do
278 s.draw_on_display(self)
279 if debug and s.width != 0 and s.height != 0 then
280 var left = s.left.to_f/scale
281 var right = s.right.to_f/scale
282 var top = s.top.to_f/scale
283 var bot = s.bottom.to_f/scale
284 display.blit_stretched(img_hitbox, right, top, right, bot, left, bot, left, top)
285 end
286 end
287
288 var scene: ShotScene
289
290 var img_hitbox: Image
291
292 var img_splash: Image
293 var img_splash_play: Image
294
295 var img_player: Image
296 var img_player_shoot: Image
297 var img_player_missile: Image
298 var img_enemy0: Image
299 var img_enemy1: Image
300 var img_enemy2: Image
301 var img_enemy3: Image
302 var img_enemy4: Image
303 var img_enemy4_turret: Image
304 var img_enemy_kamikaze: Image
305 var img_enemy_shoot: Image
306 var img_enemy_missile: Image
307 var img_money: Image
308 var img_upmissile: Image
309 var img_explosion: Image
310 var img_star0: Image
311 var img_star1: Image
312 var img_star2: Image
313
314 var img_boss: Image
315 var img_boss_left: Image
316 var img_boss_right: Image
317
318 redef fun on_create
319 do
320 super
321
322 scale = (800.0 * 600.0 / display.width.to_f / display.height.to_f).sqrt * 100.0
323
324 debug = args.length > 0 and args.first == "--debug"
325
326 # TODO load assets here
327 # ex: img = load_image( "img.png" )
328 # to get file located at assets/img.png before deployement
329 self.img_hitbox = load_image("hitbox.png")
330
331 self.img_splash = load_image("splash.png")
332 self.img_splash_play = load_image("splash_play.png")
333
334 self.img_player = load_image("player.png")
335 self.img_player_shoot = load_image("player_shoot.png")
336 self.img_player_missile = load_image("player_missile.png")
337 self.img_enemy0 = load_image("enemy0.png")
338 self.img_enemy1 = load_image("enemy1.png")
339 self.img_enemy2 = load_image("enemy2.png")
340 self.img_enemy3 = load_image("enemy3.png")
341 self.img_enemy4 = load_image("enemy4.png")
342 self.img_enemy4_turret = load_image("enemy4_turret.png")
343 self.img_enemy_kamikaze = load_image("enemy_kamikaze.png")
344 self.img_enemy_shoot = load_image("enemy_shoot.png")
345 self.img_enemy_missile = load_image("enemy_missile.png")
346 self.img_money = load_image("money.png")
347 self.img_upmissile = load_image("up_missile.png")
348 self.img_explosion = load_image("explosion.png")
349 self.img_star0 = load_image("star0.png")
350 self.img_star1 = load_image("star1.png")
351 self.img_star2 = load_image("star2.png")
352 self.img_boss = load_image("boss.png")
353 self.img_boss_left = load_image("boss_left.png")
354 self.img_boss_right = load_image("boss_right.png")
355
356 var w = (display.width.to_f * scale).to_i
357 var h = (display.height.to_f * scale).to_i
358 self.scene = new MenuScene(w, h)
359 end
360
361 # Whole scaling to convert display pixels to game pixels
362 var scale: Float = 200.0
363
364 redef fun load_image(filename)
365 do
366 var res = super
367 res.scale = 100.0 / self.scale
368 return res
369 end
370
371 redef fun frame_core( display )
372 # the arg display is not null but otherwise the same than self.display
373 do
374 if not paused then
375 self.scene.update
376 var next = self.scene.next_scene
377 if next != null then
378 self.scene = next
379 end
380 if not self.scene.exists then quit = true
381 end
382 self.scene.draw_on_display(self)
383 end
384
385 var paused: Bool = false
386
387 redef fun input( input_event )
388 do
389 if input_event isa QuitEvent then # close window button
390 quit = true # orders system to quit
391 return true # this event has been handled
392 else if input_event isa KeyEvent then
393 if input_event.to_c == 'p' then
394 paused = input_event.is_down
395 return true # this event has been handled
396 else if input_event.to_c == 'q' then
397 quit = true # orders system to quit
398 return true # this event has been handled
399 end
400 end
401
402 # Maybe the event is specific to the scene
403 return self.scene.input(self, input_event)
404 end
405 end
406
407 if args.length > 0 and args.first == "--headless" then
408 headless_run
409 return
410 end
411
412 app.setup
413 app.run