243 lines
7.0 KiB
Standard ML
243 lines
7.0 KiB
Standard ML
structure Player =
|
|
struct
|
|
open GameType
|
|
|
|
(* width/height *)
|
|
val size = 35
|
|
val realSize = 35.0
|
|
|
|
val moveBy = 5
|
|
val jumpLimit = 150
|
|
val floatLimit = 3
|
|
|
|
fun mkPlayer (health, xAxis, yAxis, x, y, jumpPressed) =
|
|
{ yAxis = yAxis
|
|
, xAxis = xAxis
|
|
, health = health
|
|
, x = x
|
|
, y = y
|
|
, jumpPressed = jumpPressed
|
|
}
|
|
|
|
fun checkWalls (yAxis, xAxis, x, y, health, jumpPressed, lst, game: game_type) =
|
|
let
|
|
open QuadTree
|
|
in
|
|
case lst of
|
|
(QUERY_ON_LEFT_SIDE, wallID) :: tl =>
|
|
let
|
|
val {walls, ...} = game
|
|
val {x = wallX, width = wallWidth, ...} =
|
|
Vector.sub (walls, wallID - 1)
|
|
|
|
val newX = wallX + wallWidth
|
|
in
|
|
checkWalls (yAxis, xAxis, newX, y, health, jumpPressed, tl, game)
|
|
end
|
|
| (QUERY_ON_RIGHT_SIDE, wallID) :: tl =>
|
|
let
|
|
val {walls, ...} = game
|
|
val {x = wallX, width = wallWidth, ...} =
|
|
Vector.sub (walls, wallID - 1)
|
|
|
|
val newX = wallX - size
|
|
in
|
|
checkWalls (yAxis, xAxis, newX, y, health, jumpPressed, tl, game)
|
|
end
|
|
| (QUERY_ON_BOTTOM_SIDE, wallID) :: tl =>
|
|
let
|
|
val {walls, ...} = game
|
|
val {y = wallY, ...} = Vector.sub (walls, wallID - 1)
|
|
|
|
val newY = wallY - size
|
|
in
|
|
checkWalls
|
|
(ON_GROUND, xAxis, x, newY, health, jumpPressed, tl, game)
|
|
end
|
|
| (QUERY_ON_TOP_SIDE, wallID) :: tl =>
|
|
checkWalls (yAxis, xAxis, x, y, health, jumpPressed, tl, game)
|
|
| [] => mkPlayer (health, xAxis, yAxis, x, y, jumpPressed)
|
|
end
|
|
|
|
fun helpMove (x, y, xAxis, yAxis, health, jumpPressed, game: game_type) =
|
|
let
|
|
(* check against wall quad tree *)
|
|
val desiredX =
|
|
case xAxis of
|
|
STAY_STILL => x
|
|
| MOVE_LEFT => x - moveBy
|
|
| MOVE_RIGHT => x + moveBy
|
|
in
|
|
case yAxis of
|
|
ON_GROUND =>
|
|
let
|
|
val collisions = QuadTree.getCollisionSides
|
|
(desiredX, y, size, size, 0, 0, 1920, 1080, 0, #wallTree game)
|
|
in
|
|
checkWalls
|
|
(yAxis, xAxis, desiredX, y, health, jumpPressed, collisions, game)
|
|
end
|
|
| FLOATING floated =>
|
|
let
|
|
val collisions = QuadTree.getCollisionSides
|
|
(desiredX, y, size, size, 0, 0, 1920, 1080, 0, #wallTree game)
|
|
|
|
val yAxis =
|
|
if floated = floatLimit then FALLING else FLOATING (floated + 1)
|
|
in
|
|
checkWalls
|
|
(yAxis, xAxis, desiredX, y, health, jumpPressed, collisions, game)
|
|
end
|
|
| FALLING =>
|
|
let
|
|
val desiredY = y + moveBy
|
|
val collisions = QuadTree.getCollisionSides
|
|
( desiredX
|
|
, desiredY
|
|
, size
|
|
, size
|
|
, 0
|
|
, 0
|
|
, 1920
|
|
, 1080
|
|
, 0
|
|
, #wallTree game
|
|
)
|
|
in
|
|
checkWalls
|
|
( yAxis
|
|
, xAxis
|
|
, desiredX
|
|
, desiredY
|
|
, health
|
|
, jumpPressed
|
|
, collisions
|
|
, game
|
|
)
|
|
end
|
|
| JUMPING jumped =>
|
|
if jumped + moveBy > jumpLimit then
|
|
(* if we are above the jump limit, trigger a fall *)
|
|
let
|
|
val collisions = QuadTree.getCollisionSides
|
|
(desiredX, y, size, size, 0, 0, 1920, 1080, 0, #wallTree game)
|
|
in
|
|
checkWalls
|
|
( FLOATING 0
|
|
, xAxis
|
|
, desiredX
|
|
, y
|
|
, health
|
|
, jumpPressed
|
|
, collisions
|
|
, game
|
|
)
|
|
end
|
|
else
|
|
(* jump *)
|
|
let
|
|
val newJumped = jumped + moveBy
|
|
val yAxis = JUMPING newJumped
|
|
val desiredY = y - moveBy
|
|
|
|
val collisions = QuadTree.getCollisionSides
|
|
( desiredX
|
|
, desiredY
|
|
, size
|
|
, size
|
|
, 0
|
|
, 0
|
|
, 1920
|
|
, 1080
|
|
, 0
|
|
, #wallTree game
|
|
)
|
|
in
|
|
checkWalls
|
|
( yAxis
|
|
, xAxis
|
|
, desiredX
|
|
, desiredY
|
|
, health
|
|
, jumpPressed
|
|
, collisions
|
|
, game
|
|
)
|
|
end
|
|
end
|
|
|
|
fun getXAxis (lh, rh) =
|
|
case (lh, rh) of
|
|
(false, false) => STAY_STILL
|
|
| (false, true) => MOVE_RIGHT
|
|
| (true, false) => MOVE_LEFT
|
|
| (true, true) => STAY_STILL
|
|
|
|
(* function returns default yAxis when neither up/down are pressed
|
|
* or both are pressed.
|
|
*
|
|
* In the case where the user was previously jumping,
|
|
* we enter the floating stage, because it's normal for games
|
|
* to have a very brief floating/gliding period before applying gravity.
|
|
*
|
|
* In the case where the user was previously floating, we want the player to
|
|
* keep floating at this point (another function will apply gravity if we
|
|
* floated enough).
|
|
*
|
|
* In every other case, we return the FALLING variant,
|
|
* which has the same effect as returning the ON_GROUND variant,
|
|
* except that it means gravity is applied if we walk off a platform.
|
|
* *)
|
|
fun defaultYAxis prevAxis =
|
|
case prevAxis of
|
|
JUMPING _ => FLOATING 0
|
|
| FLOATING _ => prevAxis
|
|
| _ => FALLING
|
|
|
|
(* We want to prevent a double jump
|
|
* or jumping while the player is falling
|
|
* so we only switch to the JUMPING case if the player
|
|
* is on the ground. *)
|
|
fun onJumpPressed (prevAxis, jumpPressed) =
|
|
case prevAxis of
|
|
ON_GROUND => if jumpPressed then prevAxis else JUMPING 0
|
|
| _ => prevAxis
|
|
|
|
fun move (game: game_type, input) =
|
|
let
|
|
val {x, y, yAxis, health, jumpPressed, ...} = #player game
|
|
val {leftHeld, rightHeld, upHeld, downHeld} = input
|
|
|
|
val xAxis = getXAxis (leftHeld, rightHeld)
|
|
in
|
|
case (upHeld, downHeld) of
|
|
(false, false) =>
|
|
let
|
|
val yAxis = defaultYAxis yAxis
|
|
val jumpPressed = false
|
|
in
|
|
helpMove (x, y, xAxis, yAxis, health, jumpPressed, game)
|
|
end
|
|
| (true, true) =>
|
|
let val yAxis = defaultYAxis yAxis
|
|
in helpMove (x, y, xAxis, yAxis, health, jumpPressed, game)
|
|
end
|
|
| (true, false) =>
|
|
let
|
|
val yAxis = onJumpPressed (yAxis, jumpPressed)
|
|
val jumpPressed = true
|
|
in
|
|
helpMove (x, y, xAxis, yAxis, health, jumpPressed, game)
|
|
end
|
|
| (false, true) =>
|
|
(* todo: should move down if on platform *)
|
|
let val jumpPressed = false
|
|
in helpMove (x, y, xAxis, yAxis, health, jumpPressed, game)
|
|
end
|
|
end
|
|
|
|
(* placeholder *)
|
|
fun getDrawVec ({x, y, ...}: player) =
|
|
Block.lerp (x, y, realSize, realSize, 1920.0, 1080.0, 0.5, 0.5, 0.5)
|
|
end
|