examples: update users of `mnit::mnit_fps`
[nit.git] / contrib / crazy_moles / src / moles.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2012-2014 Alexis Laferrière <alexis.laf@xymus.net>
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 # Classic moles game
18 #
19 # This is a minimal practical example of the mnit framework.
20 module moles is
21 app_version(1, 0, git_revision)
22 app_name("Crazy Groundhogs")
23 end
24
25 import mnit
26 import realtime
27
28 import drawing
29
30 # A hole with a possible mole, or a trap in it
31 class Hole
32 var game: Game
33
34 # Horizontal center of the hole
35 var x: Int
36
37 # Vertical center of the hole
38 var y: Int
39
40 # Half width of the hit box
41 var dx = 200.0
42
43 # Height of the hit box
44 var dy = 800.0
45
46 # Content (and state) of this hole
47 var content: nullable HoleContent = null
48
49 # Time at witch this hole can act again
50 var timeout_until: Float = app.clock.total.to_f is lazy
51
52 # Timeout for 1/2 sec or less with a higher score
53 private fun reset_timeout do
54 timeout_until = app.clock.total.to_f +
55 0.5 - 0.4 * (game.points.to_f / 1000.0).min(1.0)
56 end
57
58 fun do_turn
59 do
60 if timeout_until > app.clock.total.to_f then return
61
62 var content = content
63 if content != null then
64 if content == game.down then
65 if (20.0*game.speed_modifier).to_i.rand == 0 then
66 # dead / hide
67 self.content = null
68 reset_timeout
69 end
70 else if (80.0*game.speed_modifier).to_i.rand == 0 then
71 # hide
72 self.content = null
73 reset_timeout
74 end
75 else if (100.0*game.speed_modifier).to_i.rand == 0 then
76 self.content = to_pop
77 reset_timeout
78 end
79 end
80
81 # Get next `HoleContent` to pop
82 fun to_pop: HoleContent
83 do
84 # show traps only at 10 points and up
85 if game.points > 10 then
86
87 # After 50 points we have more and more traps until point 1000
88 var d = 1250-(game.points - 50)
89 if d < 200 then d = 200
90
91 if d.rand < 100 then return game.trap
92 end
93
94 return game.up
95 end
96
97 # Does this hole intercepts `event`?
98 fun intercepts(event: PointerEvent): Bool
99 do
100 if content == null then return false
101
102 var dx = (dx*display_scale).to_i
103 var dy = (dy*display_scale).to_i
104 var ex = event.x.to_i - display_offset_x
105 var ey = event.y.to_i - display_offset_y
106 return ex > x - dx and ex < x + dx and
107 ey > y - dy and ey < y
108 end
109
110 # Draw this hole and content to `display`
111 fun draw(display: Display)
112 do
113 # The hole itself
114 var img = app.assets.empty
115 var dx = 300.0*display_scale
116 var dy = 256.0*display_scale
117 img.scale = display_scale
118 display.blit(img, x-dx.to_i+display_offset_x, y-dy.to_i+display_offset_y)
119
120 # The mole in the hole, or other content
121 var content = self.content
122 if content != null then
123 content.draw(display, x, y)
124 end
125 end
126 end
127
128 # Content of a `Hole`
129 class HoleContent
130 # Image
131 var img: Image
132
133 # Offset of the horizontal center of the hole
134 var dx: Float
135
136 # Offset of the vertical center of the hole
137 var dy: Float
138
139 # Hit this hole content
140 fun hit(game: Game, hole: Hole, event: PointerEvent) do end
141
142 # Draw this content to `display`
143 fun draw(display: Display, x, y: Int)
144 do
145 img.scale = display_scale
146 display.blit(img,
147 x-dx.to_i+display_offset_x,
148 y-dy.to_i+display_offset_y)
149 end
150 end
151
152 # A mole in a hole
153 class Mole
154 super HoleContent
155
156 # Points value when hit
157 var value: Int
158
159 redef fun hit(game, hole, event)
160 do
161 game.points += value
162 hole.content = game.down
163 end
164 end
165
166 # A trap held out of a hole
167 class Trap
168 super HoleContent
169
170 # Points penalty when hit
171 var penalty: Int
172
173 redef fun hit(game, hole, event)
174 do
175 game.points -= penalty
176 if game.points < 0 then game.points = 0
177 hole.content = null
178 end
179 end
180
181 class Game
182 # All holes, filled or not
183 var holes = new Array[Hole]
184
185 # rule / const
186 var modifier_half_life = 1000.0
187
188 # Row count
189 fun rows: Int do return 4
190
191 # Columns count
192 fun columns: Int do return 5
193
194 # Score
195 var points = 0
196
197 # Acceleration
198 var speed_modifier = 1.0
199
200 # Vertical offset between rows
201 var dist_between_rows = 448
202
203 # Horizontal offset between columns
204 var dist_between_columns = 600
205
206 # Global accumulation control, applied to `speed_modifier`
207 fun global_speed_modifier: Float do return 2.0
208
209 # A mole, in a hole
210 var up = new Mole(app.assets.up, 212.0*display_scale, 820.0*display_scale, 1) is lazy
211
212 # A mole that was hit
213 var down = new HoleContent(app.assets.hit, 250.0*display_scale, 512.0*display_scale) is lazy
214
215 # A trap out of the hole
216 var trap = new Trap(app.assets.trap, 212.0*display_scale, 830.0*display_scale, 10) is lazy
217
218 init
219 do
220 var dx = (dist_between_columns.to_f*display_scale).to_i
221 var dy = (dist_between_rows.to_f*display_scale).to_i
222 for x in [0 .. columns[ do
223 for y in [0 .. rows[ do
224 holes.add(new Hole(self, x*dx, y*dy))
225 end
226 end
227 end
228
229 fun do_turn
230 do
231 for hole in holes do hole.do_turn
232
233 speed_modifier = modifier_half_life / (modifier_half_life+points.to_f) * global_speed_modifier
234 end
235 end
236
237 # Where all the UI stuff is done
238 class Screen
239 # The running game
240 var game = new Game is lazy
241
242 fun do_frame(display: Display)
243 do
244 display.clear(0.0, 0.45, 0.0)
245
246 app.assets.sign_warning.scale = display_scale
247 display.blit(app.assets.sign_warning, (380.0*display_scale).to_i, (256.0*display_scale).to_i)
248
249 app.assets.sign_cute.scale = display_scale
250 display.blit(app.assets.sign_cute, (1024.0*display_scale).to_i, (64.0*display_scale).to_i)
251
252 for hole in game.holes do
253 hole.draw display
254 end
255
256 draw_hud display
257 end
258
259 fun input(event: InputEvent): Bool
260 do
261 if event isa PointerEvent then
262 for hole in game.holes.reverse_iterator do
263 if hole.intercepts(event) then
264 var hole_content = hole.content
265 if hole_content != null then hole_content.hit(game, hole, event)
266 return true
267 end
268 end
269 end
270
271 return false
272 end
273
274 # Draw the HUD as the topmost layer of the screen
275 fun draw_hud(display: Display)
276 do
277 var board = app.assets.points_board
278 board.scale = display_scale
279 display.blit(board, (32.0*display_scale).to_i, -10)
280
281 draw_score(display)
282 end
283
284 # Draw the score
285 fun draw_score(display: Display, score: nullable Int)
286 do
287 if score == null then score = game.points
288
289 # Draw the score itself
290 for img in app.numbers.imgs do img.scale = display_scale
291 display.blit_number(app.numbers, score,
292 (92.0*display_scale).to_i,
293 (172.0*display_scale).to_i)
294 end
295 end
296
297 redef class App
298
299 # Main play screen
300 var screen = new Screen
301
302 # Image set generate by inkscape_tools
303 var assets = new DrawingImages
304
305 # Numbers to display the score
306 var numbers = new NumberImages(assets.n)
307
308 # Elapsed time since program launch
309 var clock = new Clock
310
311 redef fun on_start
312 do
313 super
314 assets.load_all self
315 end
316
317 redef fun on_create
318 do
319 super
320
321 maximum_fps = 50.0
322 end
323
324 redef fun frame_core(display)
325 do
326 screen.game.do_turn
327 screen.do_frame(display)
328 end
329
330 redef fun input(ie)
331 do
332 if ie isa QuitEvent or
333 (ie isa KeyEvent and ie.to_c == 'q') then
334 quit = true
335 return true
336 end
337
338 return screen.input(ie)
339 end
340 end
341
342 # Main zoom
343 fun display_scale: Float do return 1.0
344
345 # Depends on the hole center in the uo image
346 fun display_offset_x: Int do return (512.0*display_scale).to_i
347
348 # Depends on the width of the holes
349 fun display_offset_y: Int do return (1000.0*display_scale).to_i