1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
module Simulation ( simulationStep
) where
import Collision
import Game
import Level
import MainLoop
import Player
import Tank
import Control.Monad.State
import Data.Fixed
import Data.List
import Data.Maybe
import Data.Ratio
updateAngle :: Micro -> State Tank ()
updateAngle angle = do
oldangle <- gets tankDir
tspeed <- liftM (/100) $ gets tankTurnspeed
let diff = angle - oldangle
let diff360 = if (diff > 180)
then (diff-360)
else if (diff <= -180)
then (diff+360)
else diff
let (diff180, angle180) = if (diff360 > 90)
then (diff360-180, oldangle+180)
else if (diff360 <= -90)
then (diff360+180, oldangle-180)
else (diff360, oldangle)
let turn = if (diff180 > tspeed)
then tspeed
else if (diff180 < -tspeed)
then (-tspeed)
else diff180
let newangle = angle180 + turn
let newangle180 = if (newangle > 180)
then (newangle-360)
else if (newangle <= -180)
then (newangle+360)
else newangle
modify $ \tank -> tank {tankDir = newangle180}
updateTank :: GameState -> Maybe Micro -> Bool -> Maybe Micro -> State Tank ()
updateTank game angle move aangle = do
when (isJust angle) $
updateAngle $ fromJust angle
when (isJust aangle) $
modify $ \tank -> tank {tankAim = fromJust aangle}
when move $ do
tank <- get
let tdir = tankDir tank
tspeed = tankSpeed tank
moved = tankMoving tank
when (isNothing angle || (isJust angle && (tdir == fromJust angle)) || moved) $ do
let anglej = (fromRational . toRational $ tdir)*pi/180
dx = tspeed * fromRational (round ((cos anglej)*1000)%100000)
dy = tspeed * fromRational (round ((sin anglej)*1000)%100000)
put tank {tankX = dx + tankX tank, tankY = dy + tankY tank, tankMoving = True}
when (not move) $ do
modify $ \tank -> tank {tankMoving = False}
let lw = fromIntegral . levelWidth . level $ game
lh = fromIntegral . levelHeight . level $ game
modify $ collisionTankBorder lw lh
updateBullet :: GameState -> State Bullet Bool
updateBullet game = do
bullet <- get
let angle = (fromRational . toRational . bulletDir $ bullet)*pi/180
speed = bulletSpeed bullet
dx = speed * fromRational (round ((cos angle)*1000)%100000)
dy = speed * fromRational (round ((sin angle)*1000)%100000)
x = dx + bulletX bullet
y = dy + bulletY bullet
lw = fromIntegral . levelWidth . level $ game
lh = fromIntegral . levelHeight . level $ game
dir = bulletDir bullet
bounces = bulletBouncesLeft bullet
sg = if dir < 0 then -1 else 1
(newx, dir2, bounces2) = if x < 0 then (-x, sg*180 - dir, bounces-1) else if x > lw then (2*lw-x, sg*180 - dir, bounces-1) else (x, dir, bounces)
(newy, dir3, bounces3) = if y < 0 then (-y, -dir2, bounces2-1) else if y > lh then (2*lh-y, -dir2, bounces2-1) else (y, dir2, bounces2)
put bullet {bulletX = newx, bulletY = newy, bulletDir = dir3, bulletBouncesLeft = bounces3}
return (bounces3 >= 0)
simulationStep :: Main ()
simulationStep = do
oldplayers <- gets players
game <- lift get
let oldtanks = tanks game
(p, t, s) <- liftIO $ liftM unzip3 $ mapM (updateTank' game) $ zip oldplayers oldtanks
let ts = zip3 t s [0..]
shootingtanks = map (\(tank, _, n) -> (tank, n)) $ filter (\(tank, shoot, _) -> shoot && (tankBulletsLeft tank) > 0) $ ts
newtanks = map (\(tank, shoot, _) -> if (shoot && (tankBulletsLeft tank) > 0) then tank {tankBulletsLeft = (tankBulletsLeft tank) - 1} else tank) $ ts
newbullets = map (\(tank, n) -> Bullet
{ bulletX = tankX tank
, bulletY = tankY tank
, bulletDir = tankAim tank
, bulletSpeed = tankBulletSpeed tank
, bulletBouncesLeft = tankBulletBounces tank
, bulletTank = n
}) shootingtanks
modify $ \state -> state {players = p}
lift $ modify $ \state ->
let thebullets = map (runState $ updateBullet state) $ bullets state
leftbullets = collideBullets $ zipWith (\(left, bullet') bullet -> (left, bullet, bullet')) thebullets $ bullets state
bt = hitBullets $ liftM2 (\(b, (_, b')) (t, t') -> (b, b', t, t')) (zip (bullets state) leftbullets) (zip (tanks state) newtanks)
leftbullets2 = map (\(left, bullet) -> (left && (all (\(c, b, _) -> (b /= bullet) || (not c)) bt), bullet)) leftbullets
newtanks2 = map (\tank -> tank {tankLife = (tankLife tank) - (sum . map (\(c, _, t) -> if (t == tank && c) then 1 else 0) $ bt)}) newtanks
thetanks = map (\(tank, n) -> tank {tankBulletsLeft = (tankBulletsLeft tank) + (countLostTankBullets n leftbullets2)}) $ zip newtanks2 [0..]
in state {tanks = thetanks, bullets = newbullets ++ (map snd . filter fst $ leftbullets2)}
where
collideBullets [] = []
collideBullets ((left, bullet, bullet'):bs) = let (c, ls) = collideBullet bullet bullet' bs
in (left && not c, bullet'):(collideBullets ls)
collideBullet bullet bullet' bs = let cs = map (\(left, b, b') -> (left, collisionBulletBullet (bullet, bullet') (b, b'), b, b')) bs
collided = any (\(_,c,_,_) -> c) cs
left = map (\(left, c, b, b') -> (left && not c, b, b')) $ cs
in (collided, left)
hitBullets :: [(Bullet, Bullet, Tank, Tank)] -> [(Bool, Bullet, Tank)]
hitBullets [] = []
hitBullets ((b, b', t, t'):xs) = (collisionBulletTank (b, b') (t, t'), b', t'):(hitBullets xs)
updateTank' game (player, tank) = do
(p, angle, move, aangle, shoot) <- playerUpdate player tank
let t = execState (updateTank game angle move aangle) tank
return $ if (tankLife tank > 0) then (p, t, shoot) else (player, tank, False)
countLostTankBullets n (x:xs) = (if ((not . fst $ x) && (n == (bulletTank . snd $ x))) then 1 else 0) + (countLostTankBullets n xs)
countLostTankBullets n [] = 0
|