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