Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • 9562850/fp-asteroids
1 result
Show changes
Commits on Source (8)
......@@ -14,7 +14,7 @@ import Data.Ord(comparing)
step :: Float -> GameState -> IO GameState
step secs gstate
= -- Normal tick
return $ checkDeath $ checkPowerupCollision $ applyKeys $ tickNotification $ tickPowerup $ tick secs gstate
return $ checkDeath $ checkPowerupCollision $ applyKeys $ tickNotification $ tickPowerup $ tickMeteors secs $ tick secs gstate
-- Handle all the logic not directly influenced by the user
tick :: Float -> GameState -> GameState
......@@ -25,6 +25,22 @@ tick secs gstate@(GameState {screen = Game, elapsedTime, powerupTimer}) = gstate
}
tick _ gstate = gstate -- Don't do anything while the game is not in progress
{-
| elapsedTime gstate + secs > nO_SECS_BETWEEN_CYCLES
= -- add a new meteor
do randomNumber <- randomIO
let location = locationOutsideScreen (randomLocation randomNumber) (randomOrientation randomNumber)
orientation = randomOrientation randomNumber + pi
size = randomSize randomNumber
newMeteor = Meteor location orientation Forward size
newMeteors = newMeteor : meteors gstate
return $ GameState { meteors = map (translateMeteor 10) newMeteors, infoToShow = ShowMeteors newMeteors, elapsedTime = 0, spaceShip = spaceShip gstate }
| otherwise
= -- Just translate the existing meteors and update the elapsed time
return $ gstate {meteors = map (translateMeteor 10) (meteors gstate), infoToShow = ShowMeteors (meteors gstate), elapsedTime = elapsedTime gstate + secs }
>>>>>>> origin/meteor
-}
-- | Handle user input
input :: Event -> GameState -> IO GameState
input e gstate@(GameState {screen = Game}) = return (inputKey e gstate)
......@@ -77,6 +93,44 @@ inputDS _ gstate = gstate
-- GAME SECTION :
meteorSpeed :: Velocity
meteorSpeed = 100
spawnMeteor :: StdGen -> (Meteor, StdGen)
spawnMeteor gen = do
let orientation = randomOrientation gen
location = locationOutsideScreen (randomLocation gen) (orientation + pi)
size = randomSize gen
((Meteor location orientation meteorSpeed size), newGen gen)
-- | Generate a random location on the y-axis
randomLocation :: StdGen -> Location
randomLocation gen =
let (y, _) = randomR (-50, 50) gen
in (0, y)
-- | Generate a random orientation for the meteor
randomOrientation :: StdGen -> Orientation
randomOrientation gen =
let (angle, _) = randomR (0, 2*pi) gen
in angle
-- | Generate a random starting location for the meteor
locationOutsideScreen :: Location -> Orientation -> Location
locationOutsideScreen (x, y) angle = do
let (rx, ry) = (cos angle, sin angle)
let (x', y') = (x + 800 * rx, y + 500 * ry)
(x', y')
-- | Generate a random size for the meteor
randomSize :: StdGen -> Int
randomSize gen =
let (size, _) = randomR (1, 3) gen
in size
newGen :: StdGen -> StdGen
newGen gen = snd $ split gen
inputKey :: Event -> GameState -> GameState
inputKey (EventKey (SpecialKey KeyEsc) _ _ _) gstate = (initialState (seed gstate)) { player = (player gstate) { lives = 3, score = 0}, highscores = highscores gstate }
inputKey (EventKey key Down _ _) gstate = gstate { keys = insert key $ keys gstate}
......@@ -114,6 +168,16 @@ tickNotification gstate@(GameState {powerupTimer, elapsedTime})
| powerupTimer > 0 && elapsedTime > 3 = gstate { notification = "You have just claimed a powerup!" }
| otherwise = gstate { notification = "", tooltip = "" }
meteorSpawnDelay :: Float
meteorSpawnDelay = 2
tickMeteors :: Float -> GameState -> GameState
tickMeteors secs gstate@(GameState{meteorTimer, seed})
| meteorTimer > meteorSpawnDelay = do
let (newMeteor, newGen) = spawnMeteor seed
gstate { meteors = map (translateMeteor secs) $ newMeteor : (meteors gstate), seed = newGen, meteorTimer = 0}
| otherwise = gstate { meteors = map (translateMeteor secs) $ meteors gstate, meteorTimer = meteorTimer + secs}
checkDeath :: GameState -> GameState
checkDeath gstate@(GameState {screen = Game, player = (Player 0 score name)}) =
gstate { screen = Death, highscores = sortHighscores (insert (name, score) (highscores gstate)) }
......@@ -199,4 +263,10 @@ updateBullet :: Float -> Bullet -> Bullet
updateBullet secs (Bullet (x,y) angle speed) = do
let (rx, ry) = (cos angle, sin angle)
let (x', y') = (x + secs * speed * rx, y + secs * speed * ry)
Bullet (x', y') angle speed
\ No newline at end of file
Bullet (x', y') angle speed
translateMeteor :: Float -> Meteor -> Meteor
translateMeteor secs (Meteor (x,y) angle speed lives) = do
let (rx, ry) = (cos angle, sin angle)
let (x', y') = (x + speed * secs * rx, y + speed * secs * ry)
Meteor (x', y') angle speed lives
......@@ -10,7 +10,6 @@ type Location = (Float, Float)
type Orientation = Float
data Bullet = Bullet Location Orientation Velocity
type Lives = Int
data Spaceship = Spaceship {
location :: Location
, orientation :: Orientation
......@@ -27,9 +26,9 @@ data Specifications = Specs {
, fireRate :: Float -- bullets per second
}
data Meteor = Meteor Location Velocity Lives
type Size = Int
data Meteor = Meteor Location Orientation Velocity Size
data UFO = UFO Location Orientation Velocity Bullet
type Score = Int
type Name = String
type Highscore = (Name, Score)
......@@ -49,7 +48,9 @@ data GameState = GameState {
, tooltip :: String -- Small addition to notification
, elapsedTime :: Float
, powerupTimer :: Float
, meteorTimer :: Float
, spaceShip :: Spaceship
, meteors :: [Meteor]
, powerup :: Maybe Powerup
, seed :: StdGen -- Each game has its own 'seed' which allows for pure randomness
}
......@@ -71,11 +72,13 @@ initialState seed = GameState {
, powerupTimer = 0
, spaceShip = Spaceship (0,0) 0 0 [] seed 0 defaultSpecs
, powerup = Nothing
, meteors = []
, notification = ""
, tooltip = ""
, seed = seed
, meteorTimer = 0
, highscores = [
("Player1", 2),
("Player2", 1)
]
}
\ No newline at end of file
}
......@@ -6,12 +6,13 @@ import Model
import Graphics.Gloss
import Text.Printf
import System.Random
view :: GameState -> IO Picture
view = return . viewPure
viewPure :: GameState -> Picture
viewPure gstate@(GameState {screen, spaceShip, player, powerupTimer, powerup, elapsedTime, notification, tooltip, highscores}) = case screen of
viewPure gstate@(GameState {screen, meteorTimer, spaceShip, player, powerupTimer, powerup, elapsedTime, notification, tooltip, highscores}) = case screen of
MainMenu -> do
let title = color white (textPlus (-200, 250) 0.5 "ASTEROIDS")
let playerName = color yellow (textPlus (-50, 150) 0.3 $ show (name player))
......@@ -57,10 +58,13 @@ viewPure gstate@(GameState {screen, spaceShip, player, powerupTimer, powerup, el
let score' = color yellow (textPlus (textX, textY - 30) textSize $ "score: " ++ show (score player))
let velocity' = color green (textPlus (textX, textY - 60) textSize $ "speed: " ++ show (round $ velocity spaceShip) ++ " m/s")
let time = color green (textPlus (textX, textY - 90) textSize $ "time: " ++ printf "%.3f" elapsedTime ++ " s")
let debug = color red (textPlus (textX, textY - 120) textSize $ "meteortimer: " ++ printf "%.3f" meteorTimer)
let notif = color white (textPlus (-100, textY) textSize $ notification)
let tooltip' = color (greyN 0.5) (textPlus (-50, textY - 30) (textSize-0.06) $ tooltip)
pictures (player':score':tooltip':notif:powerup':time:velocity':ship:bullets')
let meteors' = pictures (map drawMeteor (meteors gstate))
pictures (debug:player':meteors':score':tooltip':notif:powerup':time:velocity':ship:bullets')
where
(a,b,c) = makeSpaceship spaceShip 30
bs = map (polygon . makeBullet 5) (bullets spaceShip)
......@@ -98,6 +102,16 @@ makeBullet o (Bullet loc angle _) = do
let (a,b,c) = makeTriangle o loc angle
[a,b,c]
makeMeteor :: Meteor -> (Point, Point, Point, Point, Point, Point)
makeMeteor (Meteor loc angle _ 1) = makeHexagon 20 loc angle
makeMeteor (Meteor loc angle _ 2) = makeHexagon 40 loc angle
makeMeteor (Meteor loc angle _ 3) = makeHexagon 60 loc angle
drawMeteor :: Meteor -> Picture
drawMeteor meteor = color white (polygon [a,b,c,d,e,f])
where
(a,b,c,d,e,f) = makeMeteor meteor
-- Creates the 3 points of a triangle which are centered on a Location,
-- The triangle is oriented in a certain way and always has the same size (o)
makeTriangle :: Float -> Location -> Orientation -> (Point, Point, Point)
......@@ -114,4 +128,28 @@ makeTriangle o (locx, locy) angle = do
let (x2', y2') = translate $ rotate (x2, y2)
let (x3', y3') = translate $ rotate (x3, y3)
((x1', y1'), (x2', y2'), (x3', y3'))
\ No newline at end of file
((x1', y1'), (x2', y2'), (x3', y3'))
makeHexagon :: Float -> Location -> Orientation -> (Point, Point, Point, Point, Point, Point)
makeHexagon o (locx, locy) angle = do
let gen = mkStdGen 2024
let angley = fst (randomR (negate locy - 200, negate locy + 200) gen) / 100
let anglex = negate locx / 100
let translate (x, y) = (x + anglex, y + angley)
let (x1, y1) = ( locx - o, locy) --left corner
let (x2, y2) = ( locx - (o/2), locy + o) --left top corner
let (x3, y3) = ( locx + (o/2), locy + o) --right top corner
let (x4, y4) = ( locx + o, locy) --right corner
let (x5, y5) = ( locx + (o/2), locy - o) --right bottom corner
let (x6, y6) = ( locx - (o/2), locy - o) --left bottom corner
let (x1', y1') = translate (x1, y1)
let (x2', y2') = translate (x2, y2)
let (x3', y3') = translate (x3, y3)
let (x4', y4') = translate (x4, y4)
let (x5', y5') = translate (x5, y5)
let (x6', y6') = translate (x6, y6)
((x1', y1'), (x2', y2'), (x3', y3'), (x4', y4'), (x5', y5'), (x6', y6'))
\ No newline at end of file