dino: each caveman has a random speed
[nit.git] / examples / mnit_dino / src / game_logic.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012-2013 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 # Entire game logic for the Dino game
18 # Depends only on Nit standard library
19 module game_logic
20
21 interface Turnable
22 fun do_turn( turn : Turn ) is abstract
23 end
24
25 class Game
26 var nbr_wanted_cavemen : Int
27
28 var dino = new Dino
29 var cavemen = new Array[Caveman]
30 var javelins_in_air = new Array[Javelin]
31 var javelins_on_ground = new Array[Javelin]
32 var entities = new Array[Entity].with_items(dino)
33 var turn_nbr = 0
34
35 var over_since = 0
36
37 var random_radius_min = 200
38 var random_radius_max = 400
39 protected var random_radius_diff : Int =
40 random_radius_max - random_radius_min
41
42 var entities_sorter = new EntitiesSorter
43
44 init( cavemen_nbr : Int )
45 do
46 srand
47
48 nbr_wanted_cavemen = cavemen_nbr
49
50 for n in [0..nbr_wanted_cavemen[ do
51 var man = new Caveman
52 cavemen.add( man )
53 entities.add( man )
54
55 var radius = (random_radius_min + random_radius_diff.rand).to_f
56 var angle = (2.0*pi).rand
57 man.pos.x = ( angle.cos * radius ).to_i
58 man.pos.y = ( angle.sin * radius ).to_i
59 end
60 end
61
62 fun do_turn : Turn
63 do
64 turn_nbr += 1
65 var turn = new Turn( self )
66
67 dino.do_turn( turn )
68 for man in cavemen do
69 man.do_turn( turn )
70 if not man.is_alive then
71 cavemen.remove( man )
72 end
73 end
74
75 for j in javelins_in_air do
76 j.do_turn( turn )
77 if j.hit_dino then
78 javelins_in_air.remove( j )
79 entities.remove( j )
80 else if j.hit_ground then
81 javelins_in_air.remove( j )
82 javelins_on_ground.add( j )
83 end
84 end
85
86 if over and over_since == 0 then
87 over_since = turn.nbr
88 end
89
90 # sort for blitting, firsts and in the back
91 entities_sorter.sort entities
92
93 return turn
94 end
95
96 fun add_javelin( j : Javelin )
97 do
98 javelins_in_air.add( j )
99 entities.add( j )
100 end
101
102 fun over : Bool do return dino.life <= 0 or cavemen.is_empty
103 fun won : Bool do return cavemen.is_empty and dino.is_alive
104 fun lost : Bool do return not won
105
106 fun ready_to_start_over : Bool do return over_since + 80 < turn_nbr
107 end
108
109 class Turn
110 var game : Game
111 var nbr : Int
112
113 init ( g : Game )
114 do
115 game = g
116 nbr = game.turn_nbr
117 end
118 end
119
120 class GamePos
121 var x : Int
122 var y : Int
123
124 init ( x, y : Int )
125 do
126 self.x = x
127 self.y = y
128 end
129
130 init copy( src : GamePos )
131 do
132 x = src.x
133 y = src.y
134 end
135
136 fun squared_dist_with( other : GamePos ) : Int
137 do
138 var dx = other.x - x
139 var dy = other.y - y
140
141 return dx*dx + dy*dy
142 end
143
144 redef fun to_s do return "<{x}|{y}>"
145 end
146
147 class Entity
148 super Turnable
149
150 var pos = new GamePos( 0, 0 )
151
152 fun squared_dist_with_dino( game : Game ) : Int
153 do
154 return pos.squared_dist_with( game.dino.pos )
155 end
156 end
157
158 class MovingEntity
159 super Entity
160
161 var going_to : nullable GamePos writable = null
162
163 fun speed : Int is abstract
164
165 var going_left = false
166 var going_right = false
167
168 redef fun do_turn( t )
169 do
170 if going_to != null then
171 var ds = pos.squared_dist_with( going_to.as(not null) )
172 if ds < speed*speed then
173 going_to = null # is there
174 else
175 var dx = going_to.x - pos.x
176 var dy = going_to.y - pos.y
177 var a = atan2( dy.to_f, dx.to_f )
178 var mx = a.cos*speed.to_f
179 var my = a.sin*speed.to_f
180
181 pos.x += mx.to_i
182 pos.y += my.to_i
183
184 going_left = mx < 0.0
185 going_right = mx > 0.0
186 end
187 end
188 end
189 end
190
191 class MortalEntity
192 super Entity
193
194 fun is_alive : Bool is abstract
195 end
196
197 class Dino
198 super MovingEntity
199 super MortalEntity
200
201 #var running_until = 0
202 var total_life = 20
203 var life : Int = total_life
204
205 redef fun speed do return 8
206
207 redef fun is_alive do return life > 0
208
209 fun hit( hitter : Entity, damage : Int )
210 do
211 if is_alive then
212 life -= damage
213 end
214 end
215
216 redef fun do_turn( t )
217 do
218 if is_alive then
219 super
220 end
221 end
222 end
223
224 class Caveman
225 super MovingEntity
226 super MortalEntity
227
228 var afraid_until = 0
229 var cannot_throw_until = 0
230
231 var throw_distance : Int = 400*40+10.rand
232 var fear_distance : Int = 300*20+8.rand
233 var flee_distance : Int = 600*60+16.rand
234 var run_over_distance = 500
235
236 var fear_duration : Int = 80+40.rand
237 var throw_period : Int = 40+8.rand
238
239 var variance_angle: Float = 2.0*pi.rand
240 var variance_dist: Int = throw_distance.to_f.sqrt.to_i-4
241 var variance_x: Int = (variance_angle.cos*variance_dist.to_f).to_i
242 var variance_y: Int = (variance_angle.sin*variance_dist.to_f).to_i
243
244 redef var is_alive : Bool = true
245
246 redef var speed: Int = 3+3.rand
247
248 fun is_afraid( turn : Turn ) : Bool do return turn.nbr < afraid_until
249 fun can_throw( turn : Turn ) : Bool do return cannot_throw_until < turn.nbr
250
251 redef fun do_turn( t )
252 do
253 if is_alive then
254 var dwd = squared_dist_with_dino( t.game )
255
256 if dwd < run_over_distance then
257 if t.game.dino.is_alive then is_alive = false
258 return
259 else if is_afraid( t ) then
260 # going to destination
261 else if t.game.dino.life <= 0 then
262 # dino is dead, chill
263 else
264 if dwd < fear_distance then
265 afraid_until = t.nbr + fear_duration
266
267 var dino_pos = t.game.dino.pos
268 var dx = dino_pos.x - pos.x
269 var dy = dino_pos.y - pos.y
270 var a = atan2( dy.to_f, dx.to_f )
271 a += pi # get opposite
272 a += [-100..100[.rand.to_f*pi/3.0/100.0
273 var x = a.cos*flee_distance.to_f
274 var y = a.sin*flee_distance.to_f
275 going_to = new GamePos( x.to_i, y.to_i )
276 else if dwd < throw_distance then
277 if can_throw( t ) then
278 cannot_throw_until = t.nbr + throw_period
279 var javelin = new Javelin( pos, t.game.dino.pos )
280 t.game.add_javelin( javelin )
281 going_to = null
282 end
283 else
284 # get closer to dino
285 var dino_pos = t.game.dino.pos
286 going_to = new GamePos(dino_pos.x+variance_x, dino_pos.y+variance_y)
287 end
288 end
289
290 super
291 end
292 end
293 end
294
295 class Javelin
296 super Entity
297
298 var z = 400
299 var angle : Float = pi/2.0
300
301 var thrown_angle_xy : Float
302 #var thrown_angle_z
303 var speed_z = 60
304
305 var hit_ground = false
306 var hit_dino = false
307
308 var speed : Int = 10+2.rand
309 var hit_dino_distance = 128
310 var hit_damage = 1
311 var gravity : Int = -3
312
313 init ( from : GamePos, to : GamePos )
314 do
315 var dx = to.x-from.x
316 var dy = to.y-from.y
317 thrown_angle_xy = atan2( dy.to_f, dx.to_f )
318 pos = new GamePos.copy( from )
319 end
320
321 redef fun do_turn( t )
322 do
323 var dwd = squared_dist_with_dino( t.game )
324 if dwd < hit_dino_distance and t.game.dino.is_alive then
325 t.game.dino.hit( self, hit_damage )
326 hit_dino = true
327 else
328 if z <= 0 then
329 hit_ground = true
330 else
331 # in the air
332 speed_z += gravity
333 z += speed_z
334
335 var mx = (thrown_angle_xy.cos * speed.to_f).to_i
336 pos.x += mx
337 pos.y += (thrown_angle_xy.sin * speed.to_f).to_i
338
339 if mx > 0 then
340 angle = atan2( (speed_z/10).to_f, -1.0*speed.to_f )-pi/2.0
341 else
342 angle = atan2( (speed_z/10).to_f, speed.to_f )-pi/2.0
343 end
344 end
345 end
346 end
347 end
348
349 # Sort entities on screen in order of Y, entities in the back are drawn first
350 class EntitiesSorter
351 super AbstractSorter[Entity]
352
353 redef fun compare(a, b) do return b.pos.y <=> a.pos.y
354 end