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