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
98 turn
.add
new TankMoveEvent(self, pos
, direction_heading
, direction_forwards
, heading
, turret
.relative_heading
)
102 # What would be the next position if not blocked by terrain features?
104 # Returns a couple of the new position and heading.
105 fun normal_next_pos
(turn
: TTurn): Couple[Pos, Float]
107 var heading
= (heading
+ direction_heading
* turn
.dts
).angle_normalize
109 var speed
= direction_forwards
* rule
.max_speed
* turn
.dts
112 pos
+= heading
.to_vector
(speed
)
115 return new Couple[Pos, Float](pos
, heading
)
118 # Damage this tank, server-side
122 var health
= health
- damage
127 turn
.add
new TankHealthChange(self, health
)
131 # Destroy this tank, server-side
132 fun destroy
(turn
: TTurn)
134 turn
.add
new TankDeathEvent(self)
135 turn
.game
.world
.explode
(turn
, pos
, 3)
138 # Collisions on the next move
139 fun next_move_collisions
(turn
: TTurn): HashSet[Feature]
141 var next
= normal_next_pos
(turn
)
142 var features
= new HashSet[Feature]
144 # Use the lines between the corners to detect collisions
145 var corners
= corners_at
(next
)
146 var prev_corner
= corners
.last
147 for corner
in corners
do
148 var feature
= turn
.game
.world
.first_collision
(prev_corner
, corner
)
149 if feature
!= null then features
.add feature
156 # Get the 4 corners at a `next` position
157 fun corners_at
(next
: Couple[Pos, Float]): Array[Pos]
159 var next_pos
= next
.first
160 var heading
= next
.second
162 var corners
= new Array[Pos]
164 var hwy
= rule
.width
/2.0 * (heading
+pi
/2.0).sin
165 var hwx
= rule
.width
/2.0 * (heading
+pi
/2.0).cos
166 var hly
= rule
.length
/2.0 * heading
.sin
167 var hlx
= rule
.length
/2.0 * heading
.cos
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
)
170 corners
.add
new Pos(next_pos
.x
- hlx
- hwx
, next_pos
.y
- hly
- hwy
)
171 corners
.add
new Pos(next_pos
.x
- hlx
+ hwx
, next_pos
.y
- hly
+ hwy
)
181 # The `Tank` on which is mounted this turret
184 # Orientation of this turret relative to the tank
185 var relative_heading
= 0.0
187 # Absolute orientation of this turret
188 fun heading
: Float do return (tank
.heading
+relative_heading
).angle_normalize
190 # Current target to aim for and fire upon
191 var target
: nullable Pos = null
193 # Seconds left before the turret can open fire again
196 redef fun do_turn
(turn
)
198 if cooldown
> 0.0 then
199 cooldown
= cooldown
- turn
.dts
201 if cooldown
<= 0.0 then
205 turn
.add
new TurretReadyEvent(tank
)
210 if target
!= null then
212 var angle_to_target
= tank
.pos
.atan2
(target
)
213 var d
= (heading
- angle_to_target
).angle_normalize
215 var max_angle
= tank
.rule
.turret_turn_speed
* turn
.dts
216 if d
.abs
< max_angle
then
217 self.relative_heading
= angle_to_target
- tank
.heading
219 if cooldown
== 0.0 then
225 # Turn towards target
227 self.relative_heading
+= max_angle
228 else self.relative_heading
-= max_angle
234 fun fire
(turn
: TTurn)
239 # Is there something between the tank and the target?
240 var hit
= turn
.game
.world
.first_collision
(tank
.pos
, dst
)
241 if hit
!= null then dst
= hit
.pos
244 turn
.add
new OpenFireEvent(tank
)
245 turn
.game
.world
.explode
(turn
, dst
, 2)
247 # The turret need time to reload, cooldown!
248 cooldown
= tank
.rule
.turret_cooldown_time
253 redef fun explode
(turn
, center
, force
)
257 for tank
in game
.tanks
do
258 if tank
.health
== 0 then continue
259 if center
.dist
(tank
.pos
) <= force
.to_f
+ 1.0 then
266 # A collection of `Tank` that could be optimized
271 # A `tank` centric order
272 abstract class TankOrder
275 # The `Tank` at the center of this order
279 # A command to change the behavior of `tank`
280 class TankDirectionOrder
283 # Desired direction, in [-1.0..1.0]
284 var direction_heading
: Float
286 # Desired speed, in [-1.0..1.0]
287 var direction_forwards
: Float
289 redef fun apply
(game
)
292 var direction_heading
= direction_heading
293 direction_heading
= direction_heading
.min
(1.0).max
(-1.0)
294 tank
.direction_heading
= direction_heading
*tank
.rule
.max_direction
296 var direction_forwards
= direction_forwards
297 direction_forwards
= direction_forwards
.min
(1.0).max
(-1.0)
298 tank
.direction_forwards
= direction_forwards
*tank
.rule
.max_speed
302 # Order to aim and fire at `target`
303 class AimAndFireOrder
306 # Target for the turret
309 redef fun apply
(game
)
311 tank
.turret
.target
= target
315 # A `tank` centric event
316 abstract class TankEvent
319 # The `Tank` at the center of this event
328 # The turret of `tank` is ready to open fire
329 class TurretReadyEvent
333 # `tank` has been destroyed
337 redef fun apply
(game
)
340 game
.tanks
.remove tank
344 # The health of `tank` changes to `new_health`
345 class TankHealthChange
348 # The new health for `tank`
351 redef fun apply
(game
)
353 tank
.health
= new_health
359 # TODO this event is too big, divide in 2 or more and move more logic client-side
366 # The direction of the "wheels"
367 var direction_heading
: Float
370 var direction_forwards
: Float
372 # Orientation of the tank
373 var tank_heading
: Float
375 # Orientation of the turret
376 var turret_heading
: Float
378 redef fun apply
(game
)
381 tank
.direction_heading
= direction_heading
382 tank
.direction_forwards
= direction_forwards
383 tank
.heading
= tank_heading
384 tank
.turret
.relative_heading
= turret_heading