rip out player-enemy.sml and let player react to enemy collisions independently of enemy reaction to player

This commit is contained in:
2025-02-06 17:37:48 +00:00
parent 886e3a674d
commit e1e20230d6
4 changed files with 66 additions and 246 deletions

View File

@@ -5,22 +5,9 @@ struct
val {player, walls, wallTree, platforms, platformTree, enemies, graph} =
game
val player = Player.runPhysicsAndInput (game, input)
val enemyTree = Enemy.generateTree enemies
val player = Player.runPhysicsAndInput (game, input, enemyTree)
(* check player-enemy collisions and react *)
val (player, enemies) = PlayerEnemy.checkCollisions
( player
, enemies
, enemyTree
, #projectiles player
, walls
, wallTree
, platforms
, platformTree
, graph
)
in
{ player = player
, walls = walls

View File

@@ -1,122 +0,0 @@
structure PlayerEnemy =
struct
open GameType
open PlayerPatch
fun checkCollisions
( player
, enemies
, enemyTree
, projectiles
, walls
, wallTree
, platforms
, platformTree
, graph
) =
let
val {x, y, mainAttack, attacked, ...} = player
val size = Constants.playerSize
val projectileTree = Projectile.generateTree projectiles
in
case mainAttack of
MAIN_ATTACKING =>
let
(* filter enemies based on collisions *)
val enemyCollisions =
Player.getEnemyCollisionsWhenAttacking (x, y, enemyTree)
val enemies = Enemy.filterWhenAttacked
( Vector.length enemies - 1
, enemyCollisions
, enemies
, projectileTree
, []
, walls
, wallTree
, platforms
, platformTree
, player
, graph
)
(* add collided enemies to player record,
* concatenating with the previous enemies defeated *)
val player = Player.concatAttackedEnemies (player, enemyCollisions)
in
(player, enemies)
end
| _ =>
(case attacked of
NOT_ATTACKED =>
let
val enemyCollisions = QuadTree.getCollisions
(x, y, size, size, ~1, enemyTree)
val player =
Player.enemyCollisionReaction
(player, enemies, enemyCollisions, [])
val enemies = Enemy.filterProjectileCollisions
( Vector.length enemies - 1
, enemies
, projectileTree
, []
, walls
, wallTree
, platforms
, platformTree
, player
, graph
)
in
(player, enemies)
end
| ATTACKED amt =>
if amt = Constants.attackedLimit then
(* if reached limit, detect enemies again *)
let
val enemyCollisions = QuadTree.getCollisions
(x, y, size, size, ~1, enemyTree)
val player =
Player.exitAttackedAndCheckEnemies
(player, enemies, enemyCollisions)
val enemies = Enemy.filterProjectileCollisions
( Vector.length enemies - 1
, enemies
, projectileTree
, []
, walls
, wallTree
, platforms
, platformTree
, player
, graph
)
in
(player, enemies)
end
else
(* if attacked, don't detect collisions,
* allowing a brief invincibility period as is common in many games
* *)
let
val player = Player.incrementAttacked (player, amt)
val enemies = Enemy.filterProjectileCollisions
( Vector.length enemies - 1
, enemies
, projectileTree
, []
, walls
, wallTree
, platforms
, platformTree
, player
, graph
)
in
(player, enemies)
end)
end
end

View File

@@ -228,7 +228,7 @@ struct
acc
end
fun getRecoilPatches (player, patches) =
fun getRecoilPatches (player: player, patches) =
case #recoil player of
NO_RECOIL => patches
| RECOIL_LEFT recoiled =>
@@ -300,7 +300,7 @@ struct
end
end
fun getProjectilePatches ({projectiles, ...}) =
fun getProjectilePatches ({projectiles, ...}: player) =
let
val newProjectiles = helpMoveProjectiles
(Vector.length projectiles - 1, projectiles, [])
@@ -308,44 +308,6 @@ struct
[W_PROJECTILES newProjectiles]
end
fun runPhysicsAndInput (game: game_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)
| _ => []
(* 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
structure FoldEnemies =
MakeQuadTreeFold
(struct
@@ -390,12 +352,74 @@ struct
in
eCentreX < pCentreX
end
val patches = getEnemyRecoilPatches (player, playerOnRight, patches)
val patches =
getEnemyRecoilPatches (player, playerOnRight, patches)
in
W_ATTACKED (ATTACKED 0) :: patches
end
end)
fun runPhysicsAndInput (game: game_type, input, enemyTree) =
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)
val player = PlayerPatch.withPatches (player, patches)
(* player reaction to collisions with enemies *)
val patches =
let
val {x, y, ...} = player
val size = Constants.playerSize
val env = (#enemies game, player)
val state = []
in
FoldEnemies.foldRegion (x, y, size, size, env, state, enemyTree)
end
in
PlayerPatch.withPatches (player, patches)
end
(* todo: add attacked enemies to player's 'enemies' field *)
fun concatAttackedEnemies (player: player, enemyCollisions) =
let
@@ -406,74 +430,6 @@ struct
PlayerPatch.withPatch (player, W_ENEMIES allDefeated)
end
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 enemyCollisionReaction (player: player, enemies: enemy vector, lst, acc) =
case lst of
id :: tl =>
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.playerSize
val pHalfW = Constants.playerSize div 2
val pCentreX = x + pHalfW
val {x = ex, y = ey, ...} = Enemy.find (id, enemies)
val eFinishX = ex + Constants.enemySize
val eHalfW = Constants.enemySize div 2
val eCentreX = ex + eHalfW
in
eCentreX < pCentreX
end
val acc = getEnemyRecoilPatches (player, playerOnRight, acc)
in
enemyCollisionReaction (player, enemies, tl, acc)
end
| [] => PlayerPatch.withPatches (player, acc)
fun incrementAttacked (player, amt) =
let val patch = ATTACKED (amt + 1)
in PlayerPatch.withPatch (player, W_ATTACKED patch)
end
fun exitAttackedAndCheckEnemies (player, enemies, enemyCollisions) =
enemyCollisionReaction
(player, enemies, enemyCollisions, [W_ATTACKED NOT_ATTACKED])
fun getEnemyCollisionsWhenAttacking (x, y, enemyTree) =
let
val x = x - Constants.halfPlayerSize
val y = y - Constants.halfPlayerSize
val size = Constants.playerSize * 2
val ww = Constants.worldWidth
val wh = Constants.worldHeight
val enemyCollisions = QuadTree.getCollisions
(x, y, size, size, ~1, enemyTree)
in
Vector.fromList enemyCollisions
end
(*** DRAWING FUNCTIONS ***)
(* block is placeholder asset *)