9d8296503cf3fabb6edcd83a9b137640907a360d
1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # See the License for the specific language governing permissions and
11 # limitations under the License.
13 # Tank and tank turret related logic
14 module tanks
is serialize
19 # All of the tanks in game
20 var tanks
= new TankSet
25 for tank
in tanks
do tank
.do_turn turn
30 # Stats of a tank kind, its config should be move to the `Story` if we want more than 1
35 var width
: Float = 64.0/32.0
38 var length
: Float = 100.0/32.0
40 # Maximum `health` this tank can normally have
43 # Maximum speed of this tank (in world coordinate units per seconds)
46 # Turret turning speed (in radians per second)
47 var turret_turn_speed
= 1.6
49 # Waiting time between shots can be fired
50 var turret_cooldown_time
= 2.0
52 # Maximum `direction_heading` heading, may be double
53 var max_direction
= 1.0
57 # The main (and only) tank configuration in this game
58 var tanks
: Array[TankRule] = [new TankRule(self)]
65 redef type R
: TankRule
67 # In world `Pos` of this entity
70 # Orientation of this entity
73 # The turret mounted on this tank
74 var turret
= new Turret(self)
77 var direction_heading
= 0.0
80 var direction_forwards
= 0.0
82 # Health of this tank, out of `rule.max_health`
83 var health
: Int = rule
.max_health
is lazy
85 redef fun do_turn
(turn
)
87 var collisions
= next_move_collisions
(turn
)
89 if collisions
.is_empty
then
90 var next
= normal_next_pos
(turn
)
92 self.heading
= next
.second
97 turn
.add
new TankMoveEvent(self, pos
, direction_heading
, direction_forwards
, heading
, turret
.relative_heading
)
100 # What would be the next position if not blocked by terrain features?
102 # Returns a couple of the new position and heading.
103 fun normal_next_pos
(turn
: TTurn): Couple[Pos, Float]
105 var heading
= (heading
+ direction_heading
* turn
.dts
).angle_normalize
107 var speed
= direction_forwards
* rule
.max_speed
* turn
.dts
110 pos
+= heading
.to_vector
(speed
)
113 return new Couple[Pos, Float](pos
, heading
)
116 # Damage this tank, server-side
120 var health
= health
- damage
125 turn
.add
new TankHealthChange(self, health
)
129 # Destroy this tank, server-side
130 fun destroy
(turn
: TTurn)
132 turn
.add
new TankDeathEvent(self)
133 turn
.game
.world
.explode
(turn
, pos
, 3)
136 # Collisions on the next move
137 fun next_move_collisions
(turn
: TTurn): HashSet[Feature]
139 var next
= normal_next_pos
(turn
)
140 var features
= new HashSet[Feature]
142 # Use the lines between the corners to detect collisions
143 var corners
= corners_at
(next
)
144 var prev_corner
= corners
.last
145 for corner
in corners
do
146 var feature
= turn
.game
.world
.first_collision
(prev_corner
, corner
)
147 if feature
!= null then features
.add feature
154 # Get the 4 corners at a `next` position
155 fun corners_at
(next
: Couple[Pos, Float]): Array[Pos]
157 var next_pos
= next
.first
158 var heading
= next
.second
160 var corners
= new Array[Pos]
162 var hwy
= rule
.width
/2.0 * (heading
+pi
/2.0).sin
163 var hwx
= rule
.width
/2.0 * (heading
+pi
/2.0).cos
164 var hly
= rule
.length
/2.0 * heading
.sin
165 var hlx
= rule
.length
/2.0 * heading
.cos
166 corners
.add
new Pos(next_pos
.x
+ hlx
+ hwx
, next_pos
.y
+ hly
+ hwy
)
167 corners
.add
new Pos(next_pos
.x
+ hlx
- hwx
, next_pos
.y
+ hly
- hwy
)
168 corners
.add
new Pos(next_pos
.x
- hlx
- hwx
, next_pos
.y
- hly
- hwy
)
169 corners
.add
new Pos(next_pos
.x
- hlx
+ hwx
, next_pos
.y
- hly
+ hwy
)
179 # The `Tank` on which is mounted this turret
182 # Orientation of this turret relative to the tank
183 var relative_heading
= 0.0
185 # Absolute orientation of this turret
186 fun heading
: Float do return (tank
.heading
+relative_heading
).angle_normalize
188 # Current target to aim for and fire upon
189 var target
: nullable Pos = null
191 # Seconds left before the turret can open fire again
194 redef fun do_turn
(turn
)
196 if cooldown
> 0.0 then
197 cooldown
= cooldown
- turn
.dts
199 if cooldown
<= 0.0 then
203 turn
.add
new TurretReadyEvent(tank
)
208 if target
!= null then
210 var angle_to_target
= tank
.pos
.atan2
(target
)
211 var d
= (heading
- angle_to_target
).angle_normalize
213 var max_angle
= tank
.rule
.turret_turn_speed
* turn
.dts
214 if d
.abs
< max_angle
then
215 self.relative_heading
= angle_to_target
- tank
.heading
217 if cooldown
== 0.0 then
223 # Turn towards target
225 self.relative_heading
+= max_angle
226 else self.relative_heading
-= max_angle
232 fun fire
(turn
: TTurn)
237 # Is there something between the tank and the target?
238 var hit
= turn
.game
.world
.first_collision
(tank
.pos
, dst
)
239 if hit
!= null then dst
= hit
.pos
242 turn
.add
new OpenFireEvent(tank
)
243 turn
.game
.world
.explode
(turn
, dst
, 2)
245 # The turret need time to reload, cooldown!
246 cooldown
= tank
.rule
.turret_cooldown_time
251 redef fun explode
(turn
, center
, force
)
255 for tank
in game
.tanks
do
256 if tank
.health
== 0 then continue
257 if center
.dist
(tank
.pos
) <= force
.to_f
+ 1.0 then
264 # A collection of `Tank` that could be optimized
269 # A `tank` centric order
270 abstract class TankOrder
273 # The `Tank` at the center of this order
277 # A command to change the behavior of `tank`
278 class TankDirectionOrder
281 # Desired direction, in [-1.0..1.0]
282 var direction_heading
: Float
284 # Desired speed, in [-1.0..1.0]
285 var direction_forwards
: Float
287 redef fun apply
(game
)
290 var direction_heading
= direction_heading
291 direction_heading
= direction_heading
.min
(1.0).max
(-1.0)
292 tank
.direction_heading
= direction_heading
*tank
.rule
.max_direction
294 var direction_forwards
= direction_forwards
295 direction_forwards
= direction_forwards
.min
(1.0).max
(-1.0)
296 tank
.direction_forwards
= direction_forwards
*tank
.rule
.max_speed
300 # Order to aim and fire at `target`
301 class AimAndFireOrder
304 # Target for the turret
307 redef fun apply
(game
)
309 tank
.turret
.target
= target
313 # A `tank` centric event
314 abstract class TankEvent
317 # The `Tank` at the center of this event
326 # The turret of `tank` is ready to open fire
327 class TurretReadyEvent
331 # `tank` has been destroyed
335 redef fun apply
(game
)
338 game
.tanks
.remove tank
342 # The health of `tank` changes to `new_health`
343 class TankHealthChange
346 # The new health for `tank`
349 redef fun apply
(game
)
351 tank
.health
= new_health
357 # TODO this event is too big, divide in 2 or more and move more logic client-side
364 # The direction of the "wheels"
365 var direction_heading
: Float
368 var direction_forwards
: Float
370 # Orientation of the tank
371 var tank_heading
: Float
373 # Orientation of the turret
374 var turret_heading
: Float
376 redef fun apply
(game
)
379 tank
.direction_heading
= direction_heading
380 tank
.direction_forwards
= direction_forwards
381 tank
.heading
= tank_heading
382 tank
.turret
.relative_heading
= turret_heading