First NIT release and new clean mercurial repository
[nit.git] / tests / sol.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2006-2008 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17
18 import game
19
20 class Playing
21 special SDL_EventListener
22 redef meth on_mouse_button(evt: SDL_MouseButtonEvent)
23 do
24 if evt.is_pressed then
25 if evt.button == 1 then
26 print(_fps)
27 else if evt.button == 4 then
28 load_level(_level + 1)
29 else if evt.button == 5 then
30 load_level(_level - 1)
31 else if evt.button == 3 then
32 _quit_requested = true
33 end
34 end
35 end
36
37 redef meth on_mouse_motion(evt: SDL_MouseMotionEvent)
38 do
39 _mouse_x = evt.x
40 _mouse_y = evt.y
41 end
42
43 redef meth on_quit
44 do
45 _quit_requested = true
46 end
47
48 readable attr _quit_requested: Bool
49
50 attr _last_t: Int
51 meth update
52 do
53 var t = sdl_get_ticks # Current time (ms)
54 var quantum = 10 # Quantum of time (ms) for one step
55
56 var frames = 0
57 while _last_t < t do
58 update_one_step
59 _last_t = _last_t + quantum
60 frames = frames + 1
61 end
62
63 _fps.update_with(t)
64
65 #if _fps.update(t) then
66 # printn("FPS: ")
67 # _fps.count.output
68 #end
69
70 update_draw(frames)
71
72 draw
73 end
74
75 private
76 meth update_one_step
77 do
78 var mx = _mouse_x
79 var my = _mouse_y
80 _player.update_one_step(mx, my)
81
82 var i = 0
83 while i < _boxes.length do
84 var b = _boxes[i]
85 if b.gettable and b.collide(_player) then
86 _boxes[i] = _boxes.last
87 _boxes.pop
88
89 var bx = b.x
90 var by = b.y
91 var boxnumber = 50
92 if _boxes.is_empty then
93 boxnumber = 500
94 load_level(_level + 1)
95 end
96 for j in [0..boxnumber[ do
97 var angle = (2.0*pi).rand
98 var force = 2.0.rand
99 _others.add(new Particle(bx, by, angle.cos * force, angle.sin * force, 10 + 100.rand))
100 end
101 else
102 b.update_one_step
103 i = i + 1
104 end
105 end
106
107 i = 0
108 while i < _others.length do
109 var s = _others[i]
110 if s.update_one_step then
111 _others[i] = _others.last
112 _others.pop
113 else
114 i = i + 1
115 end
116 end
117 end
118
119 meth update_draw(frames: Int)
120 do
121 _player.update_draw(frames)
122 end
123
124 meth draw
125 do
126 _screen.clear
127 for b in _boxes do
128 b.blit_on(_screen)
129 end
130 _player.blit_on(_screen)
131 for b in _others do
132 b.blit_on(_screen)
133 end
134 _screen.flip
135 end
136
137 private meth load_level(l: Int)
138 do
139 _level = l
140 _boxes.clear
141
142 var levels = once load_levels("data/levels.txt")
143 if _level > levels.length then
144 print("You win")
145 _level = 1
146 end
147
148 var lev = levels[_level - 1]
149 print("Level {lev.number}: {lev.name}")
150 init_level(lev.data)
151 end
152
153 private meth init_level(s: String)
154 do
155 var height = 1
156 var width = 0
157 var w = 0
158 for i in [0..s.length[ do
159 var c = s[i]
160 if c == '\n' then
161 w = 0
162 height = height + 1
163 else if c == ' ' then
164 w = w + 1
165 else
166 w = w + 1
167 if w > width then
168 width = w
169 end
170 end
171 end
172 var dx = _screen.width / width
173 var dy = _screen.height / height
174
175 w = 0
176 var h = 0
177 for i in [0..s.length[ do
178 var c = s[i]
179 if c == '\n' then
180 h = h + 1
181 w = -1
182 else if c == '#' then
183 _boxes.add(new Box(dx/2 + dx * w, dy/2 + dy * h))
184 else if c == '*' then
185 _boxes.add(new Box(dx/2 + dx * w, dy/2 + dy * h))
186 end
187 w = w + 1
188 end
189 end
190
191 private
192 attr _screen: SDL_Screen
193
194 attr _mouse_x: Int
195 attr _mouse_y: Int
196
197 attr _fps: TickCounter
198 attr _player: Player
199 attr _boxes: Array[Box]
200 attr _others: Array[Particle]
201
202 attr _level: Int
203
204
205
206 init(s: SDL_Screen)
207 do
208 _screen = s
209
210 _player = new Player(320, 240)
211
212 _boxes = new Array[Box]
213 _others = new Array[Particle]
214 _fps = new TickCounter
215
216 _last_t = sdl_get_ticks
217
218 load_level(1)
219
220 end
221 end
222
223 redef class Sprite
224 private meth load_images(name: String, number: Int): Array[SDL_Surface]
225 do
226 print("load images '{name}' ({number})")
227 var res = new Array[SDL_Surface].with_capacity(number)
228 var ten = 10
229 if number < 10 then
230 ten = number
231 end
232 for i in [0..ten[ do
233 res.add(sdl_load_bmp("data/{name}-0{i}.png"))
234 end
235 for i in [10..number[ do
236 res.add(sdl_load_bmp("data/{name}-{i}.png"))
237 end
238 return res
239 end
240 end
241
242 class MovingSprite
243 special Sprite
244 attr _rx: Float # Real X axis (sub-pixel)
245 attr _ry: Float # Real Y axix (sub-pixel)
246 readable attr _vx: Float # X velocity (speed)
247 readable attr _vy: Float # Y velovity (speed)
248
249 redef meth x=(x: Int)
250 do
251 _x = x
252 _rx = x.to_f
253 end
254 redef meth y=(y: Int)
255 do
256 _y = y
257 _ry = y.to_f
258 end
259 end
260
261 class Player
262 special MovingSprite
263
264 meth update_one_step(mx: Int, my: Int)
265 do
266 var vx = _vx
267 var vy = _vy
268
269 # Calculate objective distance
270 var dbmx = mx - _x
271 var dbmy = my - _y
272 var dbm2 = dbmx * dbmx + dbmy * dbmy
273
274 # Calculate acceleration and update speed
275 if dbm2 > 10 then
276 var dbm = dbm2.to_f.sqrt
277
278 var force = 0.200 # maximal force
279 if dbm < 50.0 then
280 # Less force if near the objective
281 _boost = false
282 force = force * dbm / 100.0
283 else if not _boost and dbm > 60.0 then
284 _boost = true
285 end
286
287 var ax = dbmx.to_f / dbm * force
288 var ay = dbmy.to_f / dbm * force
289
290 vx = vx + ax
291 vy = vy + ay
292 end
293
294 # Speed modifier (min/max)
295 var dv2 = vx*vx + vy*vy # Absolute speed
296 if dv2 > 5.0 then
297 # Hard brake
298 vx = vx / dv2 * 5.0
299 vy = vy / dv2 * 5.0
300 else if dv2 < 1.0 then
301 # Accel
302 vx = vx / dv2
303 vy = vy / dv2
304 end
305
306 # Update position
307 var rx = _rx + vx
308 var ry = _ry + vy
309
310 _vx = vx
311 _vy = vy
312 _rx = rx
313 _ry = ry
314 _x = rx.to_i
315 _y = ry.to_i
316 end
317
318 meth update_draw(frames: Int)
319 do
320 var a = - atan2(_vx, _vy)
321 var x = ((a / 2.0 / pi + 0.5) * 16.0 + 0.5).to_i % 16
322 if _boost then
323 x = x * 2 + 1
324 else
325 x = x * 2
326 end
327 var imgs = _images[x]
328
329 var inum = _image_number + frames
330 if inum >= imgs.length * 8 then
331 inum = inum % (imgs.length * 8)
332 end
333 _image = imgs[inum / 8]
334 _image_number = inum
335 end
336
337 attr _boost: Bool
338 attr _images: Array[Array[SDL_Surface]]
339 attr _image_number: Int
340
341
342 init(x: Int, y: Int)
343 do
344 set_xy(x, y)
345 _vx = 0.0
346 _vy = 0.0
347 _images = once load_ship_images
348 _image_number = 0
349 set_centered_image(_images.first.first)
350 #set_centered_image(once sdl_load_bmp("data/ship-up.png"))
351 end
352 meth load_ship_images: Array[Array[SDL_Surface]]
353 do
354 var res = new Array[Array[SDL_Surface]]
355 for i in ["up", "22", "45", "68", "90", "112", "135", "158", "180", "202", "225", "248", "270", "292", "315", "338"] do
356 res.add(load_images("ship-{i}", 4))
357 res.add(load_images("ship2-{i}", 4))
358 end
359 return res
360 end
361 end
362
363 class Box
364 special Sprite
365 attr _images: Array[SDL_Surface]
366 attr _image_number: Int
367 attr _image_delay: Int
368 attr _back_rotate: Bool
369
370 meth update_one_step
371 do
372 _image_delay = _image_delay + 1
373 if _image_delay > 0 and _image_delay % 8 == 0 then
374 var n = _image_number + 1
375 if n >= _images.length then
376 n = 0
377 end
378 if _back_rotate then
379 _image = _images[n]
380 else
381 _image = _images[_images.length - n - 1]
382 end
383 _image_number = n
384 end
385 end
386
387 redef meth blit_on(s: SDL_Surface)
388 do
389 if _image_delay > 0 then
390 _image.blit_on_xy(s, _x - _x_image, _y - _y_image)
391 end
392 end
393
394 meth gettable: Bool
395 do
396 return _image_delay > 0
397 end
398
399
400 init(x: Int, y: Int)
401 do
402
403 set_xy(x, y)
404 _images = once load_images("box", 15)
405 _image_delay = - (30 + 20.rand)
406 _image_number = _images.length.rand
407 _back_rotate = 2.rand == 1
408 set_centered_image(_images.first)
409 end
410
411 end
412
413 class Particle
414 special MovingSprite
415 meth update_one_step: Bool
416 do
417 _ttl = _ttl - 1
418 if _ttl < 0 then
419 return true
420 end
421
422 _rx = _rx + _vx
423 _ry = _ry + _vy
424 _x = _rx.to_i
425 _y = _ry.to_i
426
427 if _ttl % 4 == 0 then
428 var n = (_ttl / 4) % _images.length
429 _image = _images[n]
430 end
431
432 return false
433 end
434
435 attr _ttl: Int
436 attr _images: Array[SDL_Surface]
437
438
439 init(x: Int, y: Int, vx: Float, vy: Float, ttl: Int)
440 do
441 set_xy(x, y)
442 _vx = vx
443 _vy = vy
444 _ttl = ttl
445 _images = once load_images("debris4", 8)
446 _image = _images.first
447 end
448 end
449
450 class Level
451 readable attr _number: Int
452 readable attr _name: String
453 readable attr _data: String
454
455 redef meth to_s: String
456 do
457 return "level {_number}"
458 end
459
460
461 init(n: Int, name: String, d: String)
462 do
463 _number = n
464 _name = name
465 _data = d
466 end
467 end
468
469 meth load_levels(filename: String): Array[Level]
470 do
471 var levels = new Array[Level]
472 var f = new IFStream.open(filename)
473 var name = ""
474 var data = ""
475 while not f.eof do
476 var l = f.read_line
477 if l[0] == ';' then
478 # comment
479 else if l[0] == '>' then
480 # Level name
481 name = l.substring_from(1)
482 else if l[0] == '<' then
483 # Second name
484 else if l[0] == '!' then
485 # End of level
486 levels.add(new Level(levels.length + 1, name, data))
487 name = ""
488 data = ""
489 else
490 # Level data
491 if data.length > 0 then
492 data.add('\n')
493 end
494 data.append(l)
495 end
496 end
497 f.close
498 return levels
499 end
500
501 srand
502 sdl_init
503
504 sdl_show_cursor = true
505
506 #var screen = sdl_set_video_mode(800, 600, 32)
507 var screen = sdl_set_fullscreen_video_mode(800, 600, 32)
508
509 var evt_proc = new SDL_EventProcessor
510 var evt_list = new Playing(screen)
511
512 evt_proc.add_listener(evt_list)
513
514 while not evt_list.quit_requested do
515 evt_proc.process_all_events
516 evt_list.update
517 sdl_delay(1)
518 end
519