begin parameterising level so that it fits into larger type (with different modes like TITLE, LEVEL, SETTINGS, etc.)
This commit is contained in:
150
fcore/level/player/player-attack.sml
Normal file
150
fcore/level/player/player-attack.sml
Normal file
@@ -0,0 +1,150 @@
|
||||
structure PlayerAttack =
|
||||
struct
|
||||
(* - Handle collisions where player hits enemy directly - *)
|
||||
structure PlayerAttackEnemy =
|
||||
MakeQuadTreeFold
|
||||
(struct
|
||||
type env = unit
|
||||
type state = PlayerType.defeated_enemies list * EnemyMap.t
|
||||
|
||||
open EnemyType
|
||||
|
||||
fun defeatEnemy (enemyID, enemyMap, defeatedList) =
|
||||
let
|
||||
val defeatedList = {angle = 1} :: defeatedList
|
||||
val enemyMap = EnemyMap.remove (enemyID, enemyMap)
|
||||
in
|
||||
(defeatedList, enemyMap)
|
||||
end
|
||||
|
||||
fun shieldSlimeAttacked (enemyID, enemy, enemyMap, defeatedList) =
|
||||
if #shieldOn enemy then (defeatedList, enemyMap)
|
||||
else defeatEnemy (enemyID, enemyMap, defeatedList)
|
||||
|
||||
fun onPlayerAttack (enemyID, enemy, enemyMap, defeatedList) =
|
||||
case #variant enemy of
|
||||
PATROL_SLIME => defeatEnemy (enemyID, enemyMap, defeatedList)
|
||||
| FOLLOW_SLIME => defeatEnemy (enemyID, enemyMap, defeatedList)
|
||||
| STRAIGHT_BAT => defeatEnemy (enemyID, enemyMap, defeatedList)
|
||||
| SHIELD_SLIME =>
|
||||
shieldSlimeAttacked (enemyID, enemy, enemyMap, defeatedList)
|
||||
|
||||
fun fold (enemyID, (), (defeatedList, enemyMap)) =
|
||||
case EnemyMap.get (enemyID, enemyMap) of
|
||||
SOME enemy =>
|
||||
onPlayerAttack (enemyID, enemy, enemyMap, defeatedList)
|
||||
| NONE => (defeatedList, enemyMap)
|
||||
end)
|
||||
|
||||
structure PlayerAttackFalling =
|
||||
MakeQuadTreeFold
|
||||
(struct
|
||||
type env = unit
|
||||
type state = PlayerType.defeated_enemies list * FallingEnemyMap.t
|
||||
|
||||
fun fold (fallingID, (), (defeatedList, fallingMap)) =
|
||||
let
|
||||
val defeatedList = {angle = 1} :: defeatedList
|
||||
val fallingMap = FallingEnemyMap.remove (fallingID, fallingMap)
|
||||
in
|
||||
(defeatedList, fallingMap)
|
||||
end
|
||||
end)
|
||||
|
||||
fun attackEnemies (player: PlayerType.player, enemyMap, enemyTree, fallingMap) =
|
||||
let
|
||||
open PlayerType
|
||||
val {x, y, facing, mainAttack, ...} = player
|
||||
in
|
||||
case mainAttack of
|
||||
MAIN_ATTACKING {length, ...} =>
|
||||
let
|
||||
open EntityType
|
||||
val height = Constants.playerHeight
|
||||
val x =
|
||||
(case facing of
|
||||
FACING_RIGHT => x + Constants.playerWidth
|
||||
| FACING_LEFT => x - length)
|
||||
|
||||
val (defeatedList, enemyMap) = PlayerAttackEnemy.foldRegion
|
||||
(x, y, length, height, (), ([], enemyMap), enemyTree)
|
||||
|
||||
val fallingTree = FallingEnemies.generateTree fallingMap
|
||||
val (defeatedList, fallingMap) = PlayerAttackFalling.foldRegion
|
||||
( x
|
||||
, y
|
||||
, length
|
||||
, height
|
||||
, ()
|
||||
, (defeatedList, fallingMap)
|
||||
, fallingTree
|
||||
)
|
||||
|
||||
val defeatedList = Vector.fromList defeatedList
|
||||
val defeatedList = Vector.concat [defeatedList, #enemies player]
|
||||
|
||||
val player =
|
||||
PlayerPatch.withPatch (player, PlayerPatch.W_ENEMIES defeatedList)
|
||||
in
|
||||
(player, enemyMap, fallingMap)
|
||||
end
|
||||
| _ => (player, enemyMap, fallingMap)
|
||||
end
|
||||
|
||||
(* - Handle collisions when player's projectile hits enemy - *)
|
||||
structure ProjectileHitEnemy =
|
||||
MakeQuadTreeFold
|
||||
(struct
|
||||
type env = unit
|
||||
type state = FallingEnemyMap.t * EnemyMap.t
|
||||
|
||||
open EnemyType
|
||||
|
||||
fun onDefeated (enemyID, enemy, enemyMap, fallingMap) =
|
||||
let
|
||||
val {x, y, variant, ...} = enemy
|
||||
val fallingItem = {x = x, y = y, variant = variant}
|
||||
val fallingMap =
|
||||
FallingEnemyMap.add (enemyID, fallingItem, fallingMap)
|
||||
val enemyMap = EnemyMap.remove (enemyID, enemyMap)
|
||||
in
|
||||
(fallingMap, enemyMap)
|
||||
end
|
||||
|
||||
fun onShieldSlimeAttacked (enemyID, enemy, enemyMap, fallingMap) =
|
||||
if #shieldOn enemy then (fallingMap, enemyMap)
|
||||
else onDefeated (enemyID, enemy, enemyMap, fallingMap)
|
||||
|
||||
fun onProjectileAttack (enemyID, enemy, enemyMap, fallingMap) =
|
||||
case #variant enemy of
|
||||
PATROL_SLIME => onDefeated (enemyID, enemy, enemyMap, fallingMap)
|
||||
| FOLLOW_SLIME => onDefeated (enemyID, enemy, enemyMap, fallingMap)
|
||||
| STRAIGHT_BAT => onDefeated (enemyID, enemy, enemyMap, fallingMap)
|
||||
| SHIELD_SLIME =>
|
||||
onShieldSlimeAttacked (enemyID, enemy, enemyMap, fallingMap)
|
||||
|
||||
fun fold (enemyID, (), (fallingMap, enemyMap)) =
|
||||
case EnemyMap.get (enemyID, enemyMap) of
|
||||
SOME enemy =>
|
||||
onProjectileAttack (enemyID, enemy, enemyMap, fallingMap)
|
||||
| NONE => (fallingMap, enemyMap)
|
||||
end)
|
||||
|
||||
fun helpProjectileHitEnemy (pos, projectiles, enemyTree, enemyMap, fallingMap) =
|
||||
if pos = Vector.length projectiles then
|
||||
(fallingMap, enemyMap)
|
||||
else
|
||||
let
|
||||
val {x, y, ...}: PlayerType.player_projectile =
|
||||
Vector.sub (projectiles, pos)
|
||||
val size = Constants.projectileSizeInt
|
||||
val (fallingMap, enemyMap) = ProjectileHitEnemy.foldRegion
|
||||
(x, y, size, size, (), (fallingMap, enemyMap), enemyTree)
|
||||
in
|
||||
helpProjectileHitEnemy
|
||||
(pos + 1, projectiles, enemyTree, enemyMap, fallingMap)
|
||||
end
|
||||
|
||||
fun projectileHitEnemy (projectiles, enemyMap, enemyTree, fallingMap) =
|
||||
helpProjectileHitEnemy (0, projectiles, enemyTree, enemyMap, fallingMap)
|
||||
end
|
||||
377
fcore/level/player/player-patch.sml
Normal file
377
fcore/level/player/player-patch.sml
Normal file
@@ -0,0 +1,377 @@
|
||||
signature PLAYER_PATCH =
|
||||
sig
|
||||
datatype player_patch =
|
||||
W_X_AXIS of EntityType.x_axis
|
||||
| W_Y_AXIS of EntityType.y_axis
|
||||
| W_FACING of EntityType.facing
|
||||
| W_RECOIL of PlayerType.player_recoil
|
||||
| W_ATTACKED of PlayerType.player_attacked
|
||||
| W_MAIN_ATTACK of PlayerType.main_attack
|
||||
| W_HEALTH of int
|
||||
| W_X of int
|
||||
| W_Y of int
|
||||
| W_JUMP_PRESSED of bool
|
||||
| W_MAIN_ATTACK_PRESSED of bool
|
||||
| W_ENEMIES of PlayerType.defeated_enemies vector
|
||||
| W_CHARGE of int
|
||||
| W_PROJECTILES of PlayerType.player_projectile vector
|
||||
| W_PLAT_ID of int
|
||||
|
||||
val withPatch: PlayerType.player * player_patch -> PlayerType.player
|
||||
val withPatches: PlayerType.player * player_patch list -> PlayerType.player
|
||||
end
|
||||
|
||||
structure PlayerPatch: PLAYER_PATCH =
|
||||
struct
|
||||
datatype player_patch =
|
||||
W_X_AXIS of EntityType.x_axis
|
||||
| W_Y_AXIS of EntityType.y_axis
|
||||
| W_FACING of EntityType.facing
|
||||
| W_RECOIL of PlayerType.player_recoil
|
||||
| W_ATTACKED of PlayerType.player_attacked
|
||||
| W_MAIN_ATTACK of PlayerType.main_attack
|
||||
| W_HEALTH of int
|
||||
| W_X of int
|
||||
| W_Y of int
|
||||
| W_JUMP_PRESSED of bool
|
||||
| W_MAIN_ATTACK_PRESSED of bool
|
||||
| W_ENEMIES of PlayerType.defeated_enemies vector
|
||||
| W_CHARGE of int
|
||||
| W_PROJECTILES of PlayerType.player_projectile vector
|
||||
| W_PLAT_ID of int
|
||||
|
||||
fun mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
) =
|
||||
{ yAxis = yAxis
|
||||
, xAxis = xAxis
|
||||
, recoil = recoil
|
||||
, attacked = attacked
|
||||
, mainAttack = mainAttack
|
||||
, mainAttackPressed = mainAttackPressed
|
||||
, facing = facing
|
||||
, health = health
|
||||
, x = x
|
||||
, y = y
|
||||
, jumpPressed = jumpPressed
|
||||
, enemies = enemies
|
||||
, charge = charge
|
||||
, projectiles = projectiles
|
||||
, platID = platID
|
||||
}
|
||||
|
||||
fun withPatch (player, patch) =
|
||||
let
|
||||
val
|
||||
{ yAxis
|
||||
, xAxis
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, mainAttackPressed
|
||||
, facing
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
} = player
|
||||
in
|
||||
case patch of
|
||||
W_X_AXIS xAxis =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_Y_AXIS yAxis =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_RECOIL recoil =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_ATTACKED attacked =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_MAIN_ATTACK mainAttack =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_FACING facing =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_HEALTH health =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_X x =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_Y y =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_JUMP_PRESSED jumpPressed =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_MAIN_ATTACK_PRESSED mainAttackPressed =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_ENEMIES enemies =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_CHARGE charge =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_PROJECTILES projectiles =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
| W_PLAT_ID platID =>
|
||||
mkPlayer
|
||||
( health
|
||||
, xAxis
|
||||
, yAxis
|
||||
, x
|
||||
, y
|
||||
, jumpPressed
|
||||
, recoil
|
||||
, attacked
|
||||
, mainAttack
|
||||
, facing
|
||||
, mainAttackPressed
|
||||
, enemies
|
||||
, charge
|
||||
, projectiles
|
||||
, platID
|
||||
)
|
||||
end
|
||||
|
||||
fun withPatches (player: PlayerType.player, lst) =
|
||||
case lst of
|
||||
hd :: tl =>
|
||||
let val player = withPatch (player, hd)
|
||||
in withPatches (player, tl)
|
||||
end
|
||||
| [] => player
|
||||
end
|
||||
223
fcore/level/player/player-sprite.sml
Normal file
223
fcore/level/player/player-sprite.sml
Normal file
@@ -0,0 +1,223 @@
|
||||
structure PlayerSprite =
|
||||
struct
|
||||
fun lerp (startX, startY, drawWidth, drawHeight, windowWidth, windowHeight, r, g, b) : Real32.real vector =
|
||||
let
|
||||
val endY = windowHeight - startY
|
||||
val startY = windowHeight - (startY + drawHeight)
|
||||
val endX = startX + drawWidth
|
||||
val windowHeight = windowHeight / 2.0
|
||||
val windowWidth = windowWidth / 2.0
|
||||
in
|
||||
#[ (((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.950000047684)) + (endY * 0.950000047684)) / windowHeight) - 1.0,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.950000047684)) + (endY * 0.950000047684)) / windowHeight) - 1.0,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.950000047684)) + (endY * 0.950000047684)) / windowHeight) - 1.0,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
(((startX * (1.0 - 0.3125)) + (endX * 0.3125)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.0)) + (endY * 0.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.3125)) + (endX * 0.3125)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.25)) + (endX * 0.25)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.25)) + (endX * 0.25)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.25)) + (endX * 0.25)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.0)) + (endY * 0.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.3125)) + (endX * 0.3125)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.0)) + (endY * 0.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.6875)) + (endX * 0.6875)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.0)) + (endY * 0.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.6875)) + (endX * 0.6875)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.75)) + (endX * 0.75)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.75)) + (endX * 0.75)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.75)) + (endX * 0.75)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.0)) + (endY * 0.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.6875)) + (endX * 0.6875)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.0)) + (endY * 0.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.950000047684)) + (endY * 0.950000047684)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.950000047684)) + (endY * 0.950000047684)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.950000047684)) + (endY * 0.950000047684)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 1.0)) + (endX * 1.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 1.0)) + (endX * 1.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.9375)) + (endX * 0.9375)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 1.0)) + (endX * 1.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 1.0)) + (endX * 1.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 1.0)) + (endX * 1.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 1.0)) + (endX * 1.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.25)) + (endY * 0.25)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0)) + (endX * 0.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0)) + (endX * 0.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0625)) + (endX * 0.0625)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 0.200000017881)) + (endY * 0.200000017881)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
(((startX * (1.0 - 0.0)) + (endX * 0.0)) / windowWidth) - 1.0,
|
||||
(((startY * (1.0 - 1.0)) + (endY * 1.0)) / windowHeight) - 1.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
]
|
||||
end
|
||||
end
|
||||
33
fcore/level/player/player-type.sml
Normal file
33
fcore/level/player/player-type.sml
Normal file
@@ -0,0 +1,33 @@
|
||||
structure PlayerType =
|
||||
struct
|
||||
datatype player_recoil = NO_RECOIL | RECOIL_LEFT of int | RECOIL_RIGHT of int
|
||||
|
||||
datatype player_attacked = NOT_ATTACKED | ATTACKED of int
|
||||
|
||||
datatype main_attack =
|
||||
MAIN_NOT_ATTACKING
|
||||
| MAIN_ATTACKING of {length: int, growing: bool}
|
||||
| MAIN_THROWING
|
||||
|
||||
type defeated_enemies = {angle: int}
|
||||
|
||||
type player_projectile = {x: int, y: int, facing: EntityType.facing}
|
||||
|
||||
type player =
|
||||
{ yAxis: EntityType.y_axis
|
||||
, xAxis: EntityType.x_axis
|
||||
, recoil: player_recoil
|
||||
, attacked: player_attacked
|
||||
, mainAttack: main_attack
|
||||
, mainAttackPressed: bool
|
||||
, facing: EntityType.facing
|
||||
, health: int
|
||||
, x: int
|
||||
, y: int
|
||||
, jumpPressed: bool
|
||||
, enemies: defeated_enemies vector
|
||||
, charge: int
|
||||
, projectiles: player_projectile vector
|
||||
, platID: int
|
||||
}
|
||||
end
|
||||
718
fcore/level/player/player.sml
Normal file
718
fcore/level/player/player.sml
Normal file
@@ -0,0 +1,718 @@
|
||||
structure Player =
|
||||
struct
|
||||
open PlayerPatch
|
||||
open EntityType
|
||||
open PlayerType
|
||||
|
||||
(* helper functions checking input *)
|
||||
fun getXAxis (lh, rh) =
|
||||
case (lh, rh) of
|
||||
(false, false) => STAY_STILL
|
||||
| (false, true) => MOVE_RIGHT
|
||||
| (true, false) => MOVE_LEFT
|
||||
| (true, true) => STAY_STILL
|
||||
|
||||
fun getFacing (facing, xAxis) =
|
||||
case xAxis of
|
||||
STAY_STILL => facing
|
||||
| MOVE_LEFT => FACING_LEFT
|
||||
| MOVE_RIGHT => FACING_RIGHT
|
||||
|
||||
(* 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
|
||||
| DROP_BELOW_PLATFORM => 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 (* apply gravity *) FALLING else JUMPING 0
|
||||
| _ => prevAxis
|
||||
|
||||
fun getJumpPatches (player, jumpHeld, downHeld, acc) =
|
||||
let
|
||||
val {yAxis, jumpPressed, ...} = player
|
||||
in
|
||||
case (jumpHeld, downHeld) of
|
||||
(false, false) =>
|
||||
let
|
||||
val yAxis = defaultYAxis yAxis
|
||||
val jumpPressed = false
|
||||
in
|
||||
W_JUMP_PRESSED jumpPressed :: W_Y_AXIS yAxis :: acc
|
||||
end
|
||||
| (true, true) =>
|
||||
let val yAxis = defaultYAxis yAxis
|
||||
in W_Y_AXIS yAxis :: acc
|
||||
end
|
||||
| (true, false) =>
|
||||
let
|
||||
val yAxis = onJumpPressed (yAxis, jumpPressed)
|
||||
val jumpPressed = true
|
||||
in
|
||||
W_Y_AXIS yAxis :: W_JUMP_PRESSED jumpPressed :: acc
|
||||
end
|
||||
| (false, true) =>
|
||||
let
|
||||
val jumpPressed = false
|
||||
val yAxis = DROP_BELOW_PLATFORM
|
||||
in
|
||||
W_Y_AXIS yAxis :: W_JUMP_PRESSED jumpPressed :: acc
|
||||
end
|
||||
end
|
||||
|
||||
(* called only when player has no projectiles or was not previously attacking *)
|
||||
fun helpGetMainAttackPatches (attackHeld, mainAttackPressed, charge, acc) =
|
||||
let
|
||||
val attack =
|
||||
if attackHeld andalso not mainAttackPressed then
|
||||
MAIN_ATTACKING {length = 3, growing = true}
|
||||
else
|
||||
MAIN_NOT_ATTACKING
|
||||
in
|
||||
W_MAIN_ATTACK_PRESSED (attackHeld andalso mainAttackPressed)
|
||||
:: W_MAIN_ATTACK attack :: acc
|
||||
end
|
||||
|
||||
fun degreesToRadians degrees = Real32.fromInt degrees * Constants.projectilePi
|
||||
|
||||
fun defeatedEnemiesToProjectiles
|
||||
(pos, defeteadEnemies, player as {x, y, facing, ...}, acc) =
|
||||
if pos = Vector.length defeteadEnemies then
|
||||
Vector.fromList acc
|
||||
else
|
||||
let
|
||||
val halfProjectileSize = Constants.projectileSize / 2.0
|
||||
val diffX = Constants.halfPlayerWidthReal - halfProjectileSize
|
||||
val diffY = Constants.halfPlayerHeightReal - halfProjectileSize
|
||||
val x = Real32.fromInt x + diffX
|
||||
val y = Real32.fromInt y + diffY
|
||||
|
||||
val {angle} = Vector.sub (defeteadEnemies, pos)
|
||||
val angle = degreesToRadians angle
|
||||
|
||||
val x = ((Real32.Math.cos angle) * Constants.projectileDistance) + x
|
||||
val y = ((Real32.Math.sin angle) * Constants.projectileDistance) + y
|
||||
|
||||
val x = Real32.toInt IEEEReal.TO_NEAREST x
|
||||
val y = Real32.toInt IEEEReal.TO_NEAREST y
|
||||
|
||||
val acc = {x = x, y = y, facing = facing} :: acc
|
||||
in
|
||||
defeatedEnemiesToProjectiles (pos + 1, defeteadEnemies, player, acc)
|
||||
end
|
||||
|
||||
fun getThrowPatches (defeteadEnemies, projectiles, player, acc) =
|
||||
let
|
||||
val newProjectiles =
|
||||
defeatedEnemiesToProjectiles (0, defeteadEnemies, player, [])
|
||||
|
||||
(* concatenate new projectiles with previous projectiles *)
|
||||
val allProjectiles = Vector.concat [newProjectiles, projectiles]
|
||||
|
||||
(* remove defeated enemies from player record *)
|
||||
val enemies = Vector.fromList []
|
||||
in
|
||||
W_MAIN_ATTACK MAIN_THROWING :: W_PROJECTILES allProjectiles
|
||||
:: W_ENEMIES enemies :: acc
|
||||
end
|
||||
|
||||
fun getMainAttackPatches
|
||||
( prevAttack
|
||||
, defeteadEnemies
|
||||
, projectiles
|
||||
, attackHeld
|
||||
, charge
|
||||
, player
|
||||
, acc
|
||||
, mainAttackPressed
|
||||
) =
|
||||
case prevAttack of
|
||||
MAIN_NOT_ATTACKING =>
|
||||
if
|
||||
attackHeld andalso not mainAttackPressed
|
||||
andalso Vector.length defeteadEnemies > 0
|
||||
then
|
||||
(* shoot projectiles if player was not attacking previously,
|
||||
* and there is more than one enemy *)
|
||||
getThrowPatches (defeteadEnemies, projectiles, player, acc)
|
||||
else
|
||||
helpGetMainAttackPatches (attackHeld, mainAttackPressed, charge, acc)
|
||||
| MAIN_ATTACKING {length, growing} =>
|
||||
let
|
||||
val mainAttack =
|
||||
if growing then
|
||||
if length < Constants.attackLengthLimit then
|
||||
let val newLength = length + Constants.moveProjectileBy
|
||||
in MAIN_ATTACKING {length = newLength, growing = true}
|
||||
end
|
||||
else
|
||||
let
|
||||
val newLength = length - Constants.moveProjectileBy
|
||||
in
|
||||
if newLength <= 0 then MAIN_NOT_ATTACKING
|
||||
else MAIN_ATTACKING {length = newLength, growing = false}
|
||||
end
|
||||
else
|
||||
let
|
||||
val newLength = length - Constants.moveProjectileBy
|
||||
in
|
||||
if newLength <= 0 then MAIN_NOT_ATTACKING
|
||||
else MAIN_ATTACKING {length = newLength, growing = false}
|
||||
end
|
||||
in
|
||||
W_MAIN_ATTACK_PRESSED true :: W_MAIN_ATTACK mainAttack :: acc
|
||||
end
|
||||
| MAIN_THROWING =>
|
||||
if attackHeld then
|
||||
acc
|
||||
else
|
||||
helpGetMainAttackPatches (attackHeld, mainAttackPressed, charge, acc)
|
||||
|
||||
fun getInputPatches (player: player, input) =
|
||||
let
|
||||
val
|
||||
{ x
|
||||
, y
|
||||
, yAxis
|
||||
, jumpPressed
|
||||
, facing
|
||||
, mainAttack
|
||||
, mainAttackPressed
|
||||
, charge
|
||||
, enemies
|
||||
, projectiles
|
||||
, ...
|
||||
} = player
|
||||
|
||||
val {leftHeld, rightHeld, upHeld, downHeld, attackHeld, jumpHeld} = input
|
||||
|
||||
val xAxis = getXAxis (leftHeld, rightHeld)
|
||||
val facing = getFacing (facing, xAxis)
|
||||
|
||||
val charge = (* todo: rework charge *) charge
|
||||
|
||||
val acc = [W_X_AXIS xAxis, W_FACING facing, W_CHARGE charge]
|
||||
|
||||
val acc = getMainAttackPatches
|
||||
( mainAttack
|
||||
, enemies
|
||||
, projectiles
|
||||
, attackHeld
|
||||
, charge
|
||||
, player
|
||||
, acc
|
||||
, mainAttackPressed
|
||||
)
|
||||
|
||||
val acc = getJumpPatches (player, jumpHeld, downHeld, acc)
|
||||
in
|
||||
acc
|
||||
end
|
||||
|
||||
fun getRecoilPatches (player: player, patches) =
|
||||
case #recoil player of
|
||||
NO_RECOIL => patches
|
||||
| RECOIL_LEFT recoiled =>
|
||||
(* if player is recoiling, don't accept or adjust any input.
|
||||
* However, if player has reached the recoil limit, exit the recoil
|
||||
* state and accept input.
|
||||
* *)
|
||||
if recoiled = Constants.recoilLimit then
|
||||
W_RECOIL NO_RECOIL :: patches
|
||||
else
|
||||
let
|
||||
val {x, y, health, attacked, facing, xAxis, ...} = player
|
||||
(* difference between RECOIL_LEFT and RECOIL_RIGHT
|
||||
* is the direction player moves back in *)
|
||||
val x = x - 5
|
||||
|
||||
val xAxis = STAY_STILL
|
||||
val yAxis = FALLING
|
||||
val jumpPressed = false
|
||||
val recoiled = recoiled + 1
|
||||
val recoil = RECOIL_LEFT recoiled
|
||||
val facing = getFacing (facing, xAxis)
|
||||
in
|
||||
W_X x :: W_X_AXIS xAxis :: W_Y_AXIS yAxis
|
||||
:: W_JUMP_PRESSED jumpPressed :: W_RECOIL recoil :: W_FACING facing
|
||||
:: patches
|
||||
end
|
||||
| RECOIL_RIGHT recoiled =>
|
||||
if recoiled = Constants.recoilLimit then
|
||||
W_RECOIL NO_RECOIL :: patches
|
||||
else
|
||||
let
|
||||
val {x, y, health, attacked, facing, xAxis, ...} = player
|
||||
val x = x + 5
|
||||
|
||||
val xAxis = STAY_STILL
|
||||
val yAxis = FALLING
|
||||
val jumpPressed = false
|
||||
val recoiled = recoiled + 1
|
||||
val recoil = RECOIL_RIGHT recoiled
|
||||
val facing = getFacing (facing, xAxis)
|
||||
in
|
||||
W_X x :: W_X_AXIS xAxis :: W_Y_AXIS yAxis
|
||||
:: W_JUMP_PRESSED jumpPressed :: W_RECOIL recoil :: W_FACING facing
|
||||
:: patches
|
||||
end
|
||||
|
||||
fun helpMoveProjectiles (pos, projectiles, acc) =
|
||||
if pos < 0 then
|
||||
Vector.fromList acc
|
||||
else
|
||||
let
|
||||
val {x, y, facing} = Vector.sub (projectiles, pos)
|
||||
in
|
||||
if x <= 0 orelse x >= Constants.worldWidth then
|
||||
(* filter out since projectile is not visible *)
|
||||
helpMoveProjectiles (pos - 1, projectiles, acc)
|
||||
else
|
||||
let
|
||||
val x =
|
||||
case facing of
|
||||
FACING_LEFT => x - Constants.moveProjectileBy
|
||||
| FACING_RIGHT => x + Constants.moveProjectileBy
|
||||
|
||||
val newTile = {x = x, y = y, facing = facing}
|
||||
val acc = newTile :: acc
|
||||
in
|
||||
helpMoveProjectiles (pos - 1, projectiles, acc)
|
||||
end
|
||||
end
|
||||
|
||||
fun getProjectilePatches ({projectiles, ...}: player) =
|
||||
let
|
||||
val newProjectiles = helpMoveProjectiles
|
||||
(Vector.length projectiles - 1, projectiles, [])
|
||||
in
|
||||
[W_PROJECTILES newProjectiles]
|
||||
end
|
||||
|
||||
structure FoldEnemies =
|
||||
MakeQuadTreeFold
|
||||
(struct
|
||||
type env = EnemyMap.t * player
|
||||
type state = PlayerPatch.player_patch list
|
||||
|
||||
fun getEnemyRecoilPatches (player, playerOnRight, acc) =
|
||||
if playerOnRight then
|
||||
let
|
||||
val newRecoil = RECOIL_RIGHT 0
|
||||
val newAttacked = ATTACKED 0
|
||||
in
|
||||
W_RECOIL newRecoil :: W_ATTACKED newAttacked
|
||||
:: W_FACING FACING_LEFT :: W_Y_AXIS FALLING
|
||||
:: W_X_AXIS STAY_STILL :: acc
|
||||
end
|
||||
else
|
||||
let
|
||||
val newRecoil = RECOIL_LEFT 0
|
||||
val newAttacked = ATTACKED 0
|
||||
in
|
||||
W_RECOIL newRecoil :: W_ATTACKED newAttacked
|
||||
:: W_FACING FACING_RIGHT :: W_Y_AXIS FALLING
|
||||
:: W_X_AXIS STAY_STILL :: acc
|
||||
end
|
||||
|
||||
fun fold (enemyID, (enemies, player: player), patches) =
|
||||
let
|
||||
val playerOnRight =
|
||||
(* check if collision is closer to left side of enemy or right
|
||||
* and then chose appropriate direction to recoil in *)
|
||||
let
|
||||
val {x, ...} = player
|
||||
val pFinishX = x + Constants.playerWidth
|
||||
val pHalfW = Constants.playerWidth div 2
|
||||
val pCentreX = x + pHalfW
|
||||
in
|
||||
case EnemyMap.get (enemyID, enemies) of
|
||||
SOME {x = ex, y = ey, ...} =>
|
||||
let
|
||||
val eFinishX = ex + Constants.enemySize
|
||||
val eHalfW = Constants.enemySize div 2
|
||||
val eCentreX = ex + eHalfW
|
||||
in
|
||||
eCentreX < pCentreX
|
||||
end
|
||||
| NONE => false
|
||||
end
|
||||
|
||||
val patches =
|
||||
getEnemyRecoilPatches (player, playerOnRight, patches)
|
||||
in
|
||||
W_ATTACKED (ATTACKED 0) :: patches
|
||||
end
|
||||
end)
|
||||
|
||||
structure AttackEnemies =
|
||||
MakeQuadTreeFold
|
||||
(struct
|
||||
type env = unit
|
||||
type state = defeated_enemies list
|
||||
|
||||
fun fold (_, (), defeatedList) = {angle = 1} :: defeatedList
|
||||
end)
|
||||
|
||||
fun runPhysicsAndInput (game: LevelType.level_type, input) =
|
||||
let
|
||||
val player = #player game
|
||||
|
||||
val patches = getProjectilePatches player
|
||||
val patches = getRecoilPatches (player, patches)
|
||||
val player = PlayerPatch.withPatches (player, patches)
|
||||
|
||||
val patches =
|
||||
(* we only accept and handle input if player is not recoiling.
|
||||
* It's important to apply the recoil patches after handling input
|
||||
* because we want to act on the latest recoil state straight away. *)
|
||||
case #recoil player of
|
||||
NO_RECOIL => getInputPatches (player, input)
|
||||
| _ => []
|
||||
|
||||
val patches =
|
||||
(* control timer for how long player should be immune to damage
|
||||
* after being attacked *)
|
||||
case #attacked player of
|
||||
ATTACKED amt =>
|
||||
if amt >= Constants.attackedLimit then
|
||||
W_ATTACKED NOT_ATTACKED :: patches
|
||||
else
|
||||
W_ATTACKED (ATTACKED (amt + 1)) :: patches
|
||||
| _ => patches
|
||||
|
||||
(* animate projectiles *)
|
||||
val player =
|
||||
let
|
||||
val e = #enemies player
|
||||
val e =
|
||||
Vector.map
|
||||
(fn {angle} => {angle = if angle < 360 then angle + 5 else 0}) e
|
||||
val patches = W_ENEMIES e :: patches
|
||||
in
|
||||
PlayerPatch.withPatches (player, patches)
|
||||
end
|
||||
|
||||
val patches = PlayerPhysics.getPhysicsPatches player
|
||||
val player = PlayerPatch.withPatches (player, patches)
|
||||
|
||||
val {walls, wallTree, platforms, platformTree, ...} = game
|
||||
val patches = PlayerPhysics.getEnvironmentPatches
|
||||
(player, walls, wallTree, platforms, platformTree)
|
||||
in
|
||||
PlayerPatch.withPatches (player, patches)
|
||||
end
|
||||
|
||||
(* player reaction to collisions with enemies.
|
||||
* We only detect collisions if player is not in invincibility period
|
||||
* after being previously attacked. *)
|
||||
fun checkEnemyCollisions (player: PlayerType.player, enemies, enemyTree) =
|
||||
case #attacked player of
|
||||
ATTACKED _ => player
|
||||
| _ =>
|
||||
let
|
||||
val {x, y, ...} = player
|
||||
val ew = Constants.playerWidth
|
||||
val eh = Constants.playerHeight
|
||||
val env = (enemies, player)
|
||||
val state = []
|
||||
val patches = FoldEnemies.foldRegion
|
||||
(x, y, ew, eh, env, state, enemyTree)
|
||||
in
|
||||
PlayerPatch.withPatches (player, patches)
|
||||
end
|
||||
|
||||
(* todo: check which enemies are being attacked by player,
|
||||
* updating player's 'defeatedEnemies' field (if enemy's health would reach 0)
|
||||
* and updating enemy (if enemy's health wouldn't reach 0, decrement health)
|
||||
val patches =
|
||||
(* if player is attacking, check if enemies collide with attack *)
|
||||
case #mainAttack player of
|
||||
MAIN_ATTACKING {length, ...} =>
|
||||
let
|
||||
val height = Constants.playerSize
|
||||
val {x, y, facing, enemies, ...} = player
|
||||
val x =
|
||||
(case facing of
|
||||
FACING_RIGHT => x + Constants.playerSize
|
||||
| FACING_LEFT => x - length)
|
||||
|
||||
val state = []
|
||||
(* detect collisions from enemies who are hit by attack *)
|
||||
val newDefeated = AttackEnemies.foldRegion
|
||||
(x, y, length, height, (), state, enemyTree)
|
||||
|
||||
(* detect collisions from falling enemies too *)
|
||||
val fallingTree =
|
||||
FallingEnemies.generateTree (#fallingEnemies game)
|
||||
val newDefeated = AttackEnemies.foldRegion
|
||||
(x, y, length, height, (), newDefeated, fallingTree)
|
||||
|
||||
val allDefeated =
|
||||
Vector.concat [Vector.fromList newDefeated, enemies]
|
||||
in
|
||||
W_ENEMIES allDefeated :: patches
|
||||
end
|
||||
| _ => patches
|
||||
in
|
||||
PlayerPatch.withPatches (player, patches)
|
||||
end
|
||||
*)
|
||||
|
||||
(* todo: add attacked enemies to player's 'enemies' field *)
|
||||
fun concatAttackedEnemies (player: player, enemyCollisions) =
|
||||
let
|
||||
val newDefeated = Vector.map (fn id => {angle = 360}) enemyCollisions
|
||||
val oldDefeated = #enemies player
|
||||
val allDefeated = Vector.concat [oldDefeated, newDefeated]
|
||||
in
|
||||
PlayerPatch.withPatch (player, W_ENEMIES allDefeated)
|
||||
end
|
||||
|
||||
(*** DRAWING FUNCTIONS ***)
|
||||
|
||||
(* block is placeholder asset *)
|
||||
fun helpGetDrawVec
|
||||
(x, y, realWidth, realHeight, width, height, attacked, mainAttack) =
|
||||
let
|
||||
val (r, g, b) =
|
||||
case attacked of
|
||||
NOT_ATTACKED => (1.0, 1.0, 1.0)
|
||||
| ATTACKED amt =>
|
||||
if amt mod 5 = 0 then
|
||||
(1.0, 1.0, 1.0)
|
||||
else
|
||||
(1.0, 0.75, 0.75)
|
||||
in
|
||||
PlayerSprite.lerp (x, y, realWidth, realHeight, width, height, r, g, b)
|
||||
end
|
||||
|
||||
fun getDrawVec (player: player, width, height) =
|
||||
let
|
||||
val {x, y, attacked, mainAttack, ...} = player
|
||||
val wratio = width / Constants.worldWidthReal
|
||||
val hratio = height / Constants.worldHeightReal
|
||||
in
|
||||
if wratio < hratio then
|
||||
let
|
||||
val scale = Constants.worldHeightReal * wratio
|
||||
val yOffset =
|
||||
if height > scale then (height - scale) / 2.0
|
||||
else if height < scale then (scale - height) / 2.0
|
||||
else 0.0
|
||||
|
||||
val x = Real32.fromInt x * wratio
|
||||
val y = Real32.fromInt y * wratio + yOffset
|
||||
|
||||
val realWidth = Constants.playerWidthReal * wratio
|
||||
val realHeight = Constants.playerHeightReal * wratio
|
||||
in
|
||||
helpGetDrawVec
|
||||
(x, y, realWidth, realHeight, width, height, attacked, mainAttack)
|
||||
end
|
||||
else
|
||||
let
|
||||
val scale = Constants.worldWidthReal * hratio
|
||||
val xOffset =
|
||||
if width > scale then (width - scale) / 2.0
|
||||
else if width < scale then (scale - width) / 2.0
|
||||
else 0.0
|
||||
|
||||
val x = Real32.fromInt x * hratio + xOffset
|
||||
val y = Real32.fromInt y * hratio
|
||||
|
||||
val realWidth = Constants.playerWidthReal * hratio
|
||||
val realHeight = Constants.playerHeightReal * hratio
|
||||
in
|
||||
helpGetDrawVec
|
||||
(x, y, realWidth, realHeight, width, height, attacked, mainAttack)
|
||||
end
|
||||
end
|
||||
|
||||
fun getFieldVec (player: player, width, height) =
|
||||
case #mainAttack player of
|
||||
MAIN_ATTACKING {length, ...} =>
|
||||
let
|
||||
val {x, y, facing, ...} = player
|
||||
val wratio = width / Constants.worldWidthReal
|
||||
val hratio = height / Constants.worldHeightReal
|
||||
val x =
|
||||
case #facing player of
|
||||
FACING_RIGHT => x + Constants.playerWidth
|
||||
| FACING_LEFT => x - length
|
||||
in
|
||||
if wratio < hratio then
|
||||
let
|
||||
val scale = Constants.worldHeightReal * wratio
|
||||
val yOffset =
|
||||
if height > scale then (height - scale) / 2.0
|
||||
else if height < scale then (scale - height) / 2.0
|
||||
else 0.0
|
||||
|
||||
val x = Real32.fromInt x * wratio
|
||||
val y = Real32.fromInt y * wratio + yOffset
|
||||
|
||||
val realLength = Real32.fromInt length * wratio
|
||||
val realHeight = Constants.playerHeightReal * wratio
|
||||
|
||||
val {charge, ...} = player
|
||||
val alpha = Real32.fromInt charge / 60.0
|
||||
in
|
||||
case facing of
|
||||
FACING_RIGHT =>
|
||||
ChainEdgeRight.lerp
|
||||
(x, y, realLength, realHeight, width, height, 0.5, 0.5, 0.5)
|
||||
| FACING_LEFT =>
|
||||
ChainEdgeLeft.lerp
|
||||
(x, y, realLength, realHeight, width, height, 0.5, 0.5, 0.5)
|
||||
end
|
||||
else
|
||||
let
|
||||
val scale = Constants.worldWidthReal * hratio
|
||||
val xOffset =
|
||||
if width > scale then (width - scale) / 2.0
|
||||
else if width < scale then (scale - width) / 2.0
|
||||
else 0.0
|
||||
|
||||
val x = Real32.fromInt x * hratio + xOffset
|
||||
val y = Real32.fromInt y * hratio
|
||||
|
||||
val realLength = Real32.fromInt length * hratio
|
||||
val realHeight = Constants.playerHeightReal * hratio
|
||||
|
||||
val {charge, ...} = player
|
||||
val alpha = Real32.fromInt charge / 60.0
|
||||
in
|
||||
case facing of
|
||||
FACING_RIGHT =>
|
||||
ChainEdgeRight.lerp
|
||||
(x, y, realLength, realHeight, width, height, 0.5, 0.5, 0.5)
|
||||
| FACING_LEFT =>
|
||||
ChainEdgeLeft.lerp
|
||||
(x, y, realLength, realHeight, width, height, 0.5, 0.5, 0.5)
|
||||
end
|
||||
end
|
||||
| _ => Vector.fromList []
|
||||
|
||||
fun helpGetPelletVec
|
||||
( playerX
|
||||
, playerY
|
||||
, pos
|
||||
, enemies
|
||||
, width
|
||||
, height
|
||||
, ratio
|
||||
, xOffset
|
||||
, yOffset
|
||||
, acc
|
||||
) =
|
||||
if pos = Vector.length enemies then
|
||||
Vector.concat acc
|
||||
else
|
||||
let
|
||||
val {angle} = Vector.sub (enemies, pos)
|
||||
(* convert degrees to radians *)
|
||||
val angle = degreesToRadians angle
|
||||
|
||||
(* calculate pellet's x and y *)
|
||||
val pelletX =
|
||||
((Real32.Math.cos angle) * Constants.projectileDistance) + playerX
|
||||
val pelletX = pelletX * ratio + xOffset
|
||||
|
||||
val pelletY =
|
||||
((Real32.Math.sin angle) * Constants.projectileDistance) + playerY
|
||||
val pelletY = pelletY * ratio + yOffset
|
||||
|
||||
val defeatedSize = Constants.projectileSize * ratio
|
||||
|
||||
val vec = Field.lerp
|
||||
( pelletX
|
||||
, pelletY
|
||||
, defeatedSize
|
||||
, defeatedSize
|
||||
, width
|
||||
, height
|
||||
, 0.3
|
||||
, 0.9
|
||||
, 0.3
|
||||
, 1.0
|
||||
)
|
||||
val acc = vec :: acc
|
||||
in
|
||||
helpGetPelletVec
|
||||
( playerX
|
||||
, playerY
|
||||
, pos + 1
|
||||
, enemies
|
||||
, width
|
||||
, height
|
||||
, ratio
|
||||
, xOffset
|
||||
, yOffset
|
||||
, acc
|
||||
)
|
||||
end
|
||||
|
||||
fun getPelletVec (player: player, width, height) =
|
||||
if Vector.length (#enemies player) = 0 then
|
||||
Vector.fromList []
|
||||
else
|
||||
let
|
||||
val {x, y, enemies, ...} = player
|
||||
|
||||
(* get centre (x, y) coordinates of player *)
|
||||
val halfProjectileSize = Constants.projectileSize / 2.0
|
||||
val diffX = Constants.halfPlayerWidthReal - halfProjectileSize
|
||||
val diffY = Constants.halfPlayerHeightReal - halfProjectileSize
|
||||
val x = Real32.fromInt x + diffX
|
||||
val y = Real32.fromInt y + diffY
|
||||
|
||||
val wratio = width / Constants.worldWidthReal
|
||||
val hratio = height / Constants.worldHeightReal
|
||||
in
|
||||
if wratio < hratio then
|
||||
let
|
||||
val scale = Constants.worldHeightReal * wratio
|
||||
val yOffset =
|
||||
if height > scale then (height - scale) / 2.0
|
||||
else if height < scale then (scale - height) / 2.0
|
||||
else 0.0
|
||||
in
|
||||
helpGetPelletVec
|
||||
(x, y, 0, enemies, width, height, wratio, 0.0, yOffset, [])
|
||||
end
|
||||
else
|
||||
let
|
||||
val scale = Constants.worldWidthReal * hratio
|
||||
val xOffset =
|
||||
if width > scale then (width - scale) / 2.0
|
||||
else if width < scale then (scale - width) / 2.0
|
||||
else 0.0
|
||||
in
|
||||
helpGetPelletVec
|
||||
(x, y, 0, enemies, width, height, hratio, xOffset, 0.0, [])
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user