4625ac7ca5cc2ac7a4e95fa91ac5911b3d917293
[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
215 do
216 for hole in holes do hole.do_turn
217
218 speed_modifier = modifier_half_life / (modifier_half_life+points.to_f) * global_speed_modifier
219 end
220 end
221
222 # Where all the UI stuff is done
223 class Screen
224 # The running game
225 var game = new Game
226
227 fun do_frame(display: Display)
228 do
229 display.clear(0.0, 0.45, 0.0)
230
231 app.assets.sign_warning.scale = display_scale
232 display.blit(app.assets.sign_warning, (380.0*display_scale).to_i, (256.0*display_scale).to_i)
233
234 app.assets.sign_cute.scale = display_scale
235 display.blit(app.assets.sign_cute, (1024.0*display_scale).to_i, (64.0*display_scale).to_i)
236
237 for hole in game.holes do
238 hole.draw display
239 end
240
241 draw_hud display
242 end
243
244 fun input(event: InputEvent): Bool
245 do
246 if event isa PointerEvent then
247 for hole in game.holes.reverse_iterator do
248 if hole.intercepts(event) then
249 var hole_content = hole.content
250 if hole_content != null then hole_content.hit(game, hole, event)
251 return true
252 end
253 end
254 end
255
256 return false
257 end
258
259 # Draw the HUD as the topmost layer of the screen
260 fun draw_hud(display: Display)
261 do
262 var board = app.assets.points_board
263 board.scale = display_scale
264 display.blit(board, (32.0*display_scale).to_i, -10)
265
266 draw_score(display)
267 end
268
269 # Draw the score
270 fun draw_score(display: Display, score: nullable Int)
271 do
272 if score == null then score = game.points
273
274 # Draw the score itself
275 for img in app.numbers.imgs do img.scale = display_scale
276 display.blit_number(app.numbers, score,
277 (92.0*display_scale).to_i,
278 (172.0*display_scale).to_i)
279 end
280 end
281
282 redef class App
283
284 # Main play screen
285 var screen = new Screen
286
287 # Image set generate by inkscape_tools
288 var assets = new DrawingImages
289
290 # Numbers to display the score
291 var numbers = new NumberImages(assets.n)
292
293 redef fun on_start
294 do
295 super
296 assets.load_all self
297 end
298
299 redef fun on_create
300 do
301 super
302
303 maximum_fps = 50
304 end
305
306 redef fun frame_core(display)
307 do
308 screen.game.do_turn
309 screen.do_frame(display)
310 end
311
312 redef fun input(ie)
313 do
314 if ie isa QuitEvent or
315 (ie isa KeyEvent and ie.to_c == 'q') then
316 quit = true
317 return true
318 end
319
320 return screen.input(ie)
321 end
322 end
323
324 # Main zoom
325 fun display_scale: Float do return 1.0
326
327 # Depends on the hole center in the uo image
328 fun display_offset_x: Int do return (512.0*display_scale).to_i
329
330 # Depends on the width of the holes
331 fun display_offset_y: Int do return (800.0*display_scale).to_i