tests: update model-related tools because there is less mclassdef
[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
21
22 import mnit
23 import realtime
24
25 class Hole
26 var game: Game
27
28 # Center of the hole
29 var x: Int
30 var y: Int
31
32 # Half width of the hit box
33 var dx = 200.0
34 # Heigth of the hit box
35 var dy = 800.0
36
37 # state
38 var up = false
39 var hitted = false
40 var trap = false
41
42 init (g: Game, x, y: Int)
43 do
44 game = g
45 self.x = x
46 self.y = y
47 end
48
49 fun do_turn
50 do
51 if up then
52 if hitted then
53 if (20.0*game.speed_modifier).to_i.rand == 0 then
54 # dead / hide
55 hitted = false
56 up = false
57 end
58 else if (80.0*game.speed_modifier).to_i.rand == 0 then
59 # hide
60 up = false
61 end
62 else if (100.0*game.speed_modifier).to_i.rand == 0 then
63 # show up
64 up = true
65
66 # shot traps only at 50 points and up
67 trap = false
68 if game.points > 50 then
69
70 # After 50 points we have more and more traps until point 1000
71 var d = 1250-(game.points - 50)
72 if d < 200 then d = 200
73
74 if d.rand < 100 then trap = true
75 end
76 end
77 end
78
79 fun intercepts(event: PointerEvent): Bool
80 do
81 if not up or hitted then return false
82
83 var dx = (dx*display_scale).to_i
84 var dy = (dy*display_scale).to_i
85 var ex = event.x.to_i - display_offset_x
86 var ey = event.y.to_i - display_offset_y
87 return ex > x - dx and ex < x + dx and
88 ey > y - dy and ey < y
89 end
90
91 fun hit
92 do
93 if hitted then return
94
95 if trap then
96 up = false
97 game.points -= 5
98 if game.points < 0 then game.points = 0
99 else
100 hitted = true
101 game.points += 1
102 end
103 end
104 end
105
106 class Game
107 var holes = new Array[Hole].with_capacity(4)
108
109 # rule / const
110 var modifier_half_life = 1000.0
111 fun rows: Int do return 4
112 fun columns: Int do return 5
113
114 # state
115 var points = 0
116 var speed_modifier = 1.0
117
118 # configs
119 var dist_between_rows = 512
120 var dist_between_columns = 600
121 fun global_speed_modifier: Float do return 2.0
122
123 init
124 do
125 var dx = (dist_between_columns.to_f*display_scale).to_i
126 var dy = (dist_between_rows.to_f*display_scale).to_i
127 for x in [0 .. columns[ do
128 for y in [0 .. rows[ do
129 holes.add(new Hole(self, x*dx, y*dy))
130 end
131 end
132 end
133
134 fun do_turn do
135 for hole in holes do hole.do_turn
136
137 speed_modifier = modifier_half_life / (modifier_half_life+points.to_f) * global_speed_modifier
138 end
139 end
140
141 # Where all the UI stuff is done
142 class Screen
143 var empty_img: Image
144 var up_img: Image
145 var hit_img: Image
146 var trap_img: Image
147 var numbers: NumberImages
148
149 var sign_warning: Image
150 var sign_cute: Image
151 var sign_hits: Image
152
153 var game = new Game
154
155 init (app: App)
156 do
157 empty_img = app.load_image("images/empty.png")
158 up_img = app.load_image("images/up.png")
159 hit_img = app.load_image("images/hit.png")
160 trap_img = app.load_image("images/trap.png")
161 numbers = app.load_numbers("images/#.png")
162
163 sign_warning = app.load_image("images/sign-warning.png")
164 sign_cute = app.load_image("images/sign-cute.png")
165 sign_hits = app.load_image("images/sign-hits.png")
166 end
167
168 fun do_frame(display: Display)
169 do
170 display.clear(0.1, 0.65, 0.2)
171
172 sign_warning.scale = display_scale
173 sign_cute.scale = display_scale
174 sign_hits.scale = display_scale
175 for img in numbers.imgs do img.scale = display_scale
176
177 display.blit(sign_warning, (-120.0*display_scale).to_i, (-235.0*display_scale).to_i)
178 display.blit(sign_cute, (540.0*display_scale).to_i, (-180.0*display_scale).to_i)
179 display.blit(sign_hits, (1340.0*display_scale).to_i, (55.0*display_scale).to_i)
180 display.blit_number(numbers, game.points, (1460.0*display_scale).to_i, (270.0*display_scale).to_i)
181
182 for hole in game.holes do
183 # Hole
184 var img = empty_img
185 var dx = 512.0*display_scale
186 var dy = 512.0*display_scale
187 img.scale = display_scale
188 display.blit(img, hole.x-dx.to_i+display_offset_x, hole.y-dy.to_i+display_offset_y)
189
190 # Mole
191 var empty = false
192 if hole.hitted then
193 img = hit_img
194 dx = 256.0*display_scale
195 dy = 417.0*display_scale
196 else if hole.up then
197 if hole.trap then
198 img = trap_img
199 dx = 512.0*display_scale
200 dy = 830.0*display_scale
201 else
202 img = up_img
203 dx = 512.0*display_scale
204 dy = 830.0*display_scale
205 end
206 else empty = true
207
208 if not empty then
209 img.scale = display_scale
210 display.blit(img, hole.x-dx.to_i+display_offset_x, hole.y-dy.to_i+display_offset_y)
211 end
212 end
213 end
214
215 fun input(event: InputEvent): Bool
216 do
217 if event isa PointerEvent then
218 for hole in game.holes do
219 if hole.intercepts(event) then
220 if hole.up then hole.hit
221 return true
222 end
223 end
224 end
225
226 return false
227 end
228 end
229
230 redef class App
231
232 var screen: nullable Screen = null
233
234 var target_dt = 20000000
235
236 redef fun window_created
237 do
238 super
239
240 init_screen_and_game
241 end
242
243 fun init_screen_and_game do screen = new Screen(self)
244
245 redef fun frame_core(display)
246 do
247 var screen = self.screen
248 if screen != null then
249 var clock = new Clock
250
251 screen.game.do_turn
252 screen.do_frame(display)
253
254 var dt = clock.lapse
255 if dt.sec == 0 and dt.nanosec < target_dt then
256 var sleep_t = target_dt - dt.nanosec
257 sys.nanosleep(0, sleep_t)
258 end
259 end
260 end
261
262 redef fun input(ie)
263 do
264 var screen = screen
265 if ie isa QuitEvent or
266 (ie isa KeyEvent and ie.to_c == 'q') then
267 quit = true
268 return true
269 else if screen != null then
270 return screen.input(ie)
271 else
272 print "unknown input: {ie}"
273 return false
274 end
275 end
276 end
277
278 fun display_scale: Float do return 1.0
279
280 # Depends on the hole center in the uo image
281 fun display_offset_x: Int do return (512.0*display_scale).to_i
282
283 # Depends on the width of the holes
284 fun display_offset_y: Int do return (800.0*display_scale).to_i