2025-01-18 22:18:05 +00:00
|
|
|
structure EnemyBehaviour =
|
2025-01-14 11:50:27 +00:00
|
|
|
struct
|
|
|
|
|
open GameType
|
|
|
|
|
|
|
|
|
|
fun canWalkAhead (x, y, wallTree, platformTree) =
|
|
|
|
|
let
|
|
|
|
|
val y = y + Constants.enemySize - 5
|
|
|
|
|
val searchHeight = 10
|
2025-01-26 23:32:20 +00:00
|
|
|
val searchWidth = Constants.moveEnemyBy
|
2025-01-14 11:50:27 +00:00
|
|
|
in
|
2025-01-27 23:41:59 +00:00
|
|
|
QuadTree.hasCollisionAt (x, y, searchWidth, searchHeight, ~1, wallTree)
|
2025-01-14 11:50:27 +00:00
|
|
|
orelse
|
2025-01-27 23:41:59 +00:00
|
|
|
QuadTree.hasCollisionAt
|
|
|
|
|
(x, y, searchWidth, searchHeight, ~1, platformTree)
|
2025-01-14 11:50:27 +00:00
|
|
|
end
|
|
|
|
|
|
2025-01-17 11:49:34 +00:00
|
|
|
(* same function takes either wallTree or platformTree and returns true
|
|
|
|
|
* if standing on tree.
|
|
|
|
|
* Function is monomorphic in the sense that wallTree and platformTree
|
|
|
|
|
* are both same type (no generics/parametric polymorphism).
|
|
|
|
|
* *)
|
2025-01-21 23:54:53 +00:00
|
|
|
fun standingOnArea (enemy: enemy, tree) =
|
2025-01-17 11:43:49 +00:00
|
|
|
let
|
|
|
|
|
val {x = ex, y = ey, ...} = enemy
|
|
|
|
|
|
|
|
|
|
val ey = ey + Constants.enemySize - 1
|
|
|
|
|
|
|
|
|
|
val width = Constants.enemySize
|
2025-01-20 02:38:22 +00:00
|
|
|
val height = Platform.platHeight
|
2025-01-17 11:43:49 +00:00
|
|
|
in
|
2025-01-27 23:41:59 +00:00
|
|
|
QuadTree.hasCollisionAt (ex, ey, width, height, ~1, tree)
|
2025-01-17 11:43:49 +00:00
|
|
|
end
|
|
|
|
|
|
2025-01-14 11:50:27 +00:00
|
|
|
fun getPatrollPatches (enemy: enemy, wallTree, platformTree, acc) =
|
|
|
|
|
let
|
|
|
|
|
(* This function is meant to check
|
|
|
|
|
* if enemy should switch the horizontal direction
|
|
|
|
|
* if the enemy is patrolling.
|
|
|
|
|
*
|
|
|
|
|
* Algorithm:
|
|
|
|
|
* 1. Check if enemy there is a wall ahead of the enemy
|
|
|
|
|
* in the direction the enemy is walking.
|
|
|
|
|
* 1.1. If there is a wall, then invert the direction.
|
|
|
|
|
*
|
|
|
|
|
* 2. If there is no wall, check if there is space to
|
|
|
|
|
* walk ahead on, such that enemy will not fall
|
|
|
|
|
* if enemy continues to walk.
|
|
|
|
|
* 2.1. If continuing to walk will cause the enemy to fall,
|
|
|
|
|
* then invert the direction.
|
|
|
|
|
*
|
|
|
|
|
* 3. Else, do not invert direction and simply return given list.
|
|
|
|
|
* *)
|
|
|
|
|
|
|
|
|
|
val {x, y, xAxis, ...} = enemy
|
|
|
|
|
in
|
|
|
|
|
case xAxis of
|
|
|
|
|
MOVE_LEFT =>
|
|
|
|
|
let
|
|
|
|
|
(* search to see if there is wall on left side *)
|
|
|
|
|
val searchStartX = x - Constants.moveEnemyBy
|
2025-01-26 23:32:20 +00:00
|
|
|
val searchWidth = Constants.moveEnemyBy
|
2025-01-14 11:50:27 +00:00
|
|
|
val searchHeight = Constants.enemySize - 5
|
|
|
|
|
|
2025-01-27 23:41:59 +00:00
|
|
|
val hasWallAhead = QuadTree.hasCollisionAt
|
|
|
|
|
(searchStartX, y, searchWidth, searchHeight, ~1, wallTree)
|
2025-01-14 11:50:27 +00:00
|
|
|
in
|
2025-01-27 23:41:59 +00:00
|
|
|
if hasWallAhead then
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
|
|
|
|
else if canWalkAhead (searchStartX, y, wallTree, platformTree) then
|
2025-01-24 22:51:20 +00:00
|
|
|
(* invert direction if moving further left
|
2025-01-27 23:41:59 +00:00
|
|
|
* will result in falling down *)
|
2025-01-24 22:51:20 +00:00
|
|
|
acc
|
2025-01-27 23:41:59 +00:00
|
|
|
else
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
2025-01-14 11:50:27 +00:00
|
|
|
end
|
|
|
|
|
| MOVE_RIGHT =>
|
|
|
|
|
let
|
|
|
|
|
(* enemy's x field is top left coordinate
|
|
|
|
|
* but we want to check top * right coordinate,
|
|
|
|
|
* so add enemySize *)
|
|
|
|
|
val searchStartX = x + Constants.enemySize + Constants.moveEnemyBy
|
2025-01-26 23:32:20 +00:00
|
|
|
val searchWidth = Constants.moveEnemyBy
|
2025-01-14 11:50:27 +00:00
|
|
|
val searchHeight = Constants.enemySize - 5
|
|
|
|
|
|
2025-01-27 23:41:59 +00:00
|
|
|
val hasWallAhead = QuadTree.hasCollisionAt
|
|
|
|
|
(searchStartX, y, searchWidth, searchHeight, ~1, wallTree)
|
2025-01-14 11:50:27 +00:00
|
|
|
in
|
2025-01-27 23:41:59 +00:00
|
|
|
if hasWallAhead then
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
|
|
|
else if canWalkAhead (searchStartX, y, wallTree, platformTree) then
|
2025-01-24 22:51:20 +00:00
|
|
|
(* invert direction if moving further right
|
2025-01-27 23:41:59 +00:00
|
|
|
* will result in falling down *)
|
2025-01-24 22:51:20 +00:00
|
|
|
acc
|
2025-01-27 23:41:59 +00:00
|
|
|
else
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
2025-01-14 11:50:27 +00:00
|
|
|
end
|
|
|
|
|
| STAY_STILL => acc
|
|
|
|
|
end
|
|
|
|
|
|
2025-01-21 23:54:53 +00:00
|
|
|
(* pathfinding *)
|
2025-01-18 22:13:16 +00:00
|
|
|
fun isBetween (p1, check, p2) = check >= p1 andalso check <= p2
|
|
|
|
|
|
2025-01-25 00:23:30 +00:00
|
|
|
fun getHighestPlatform (collisions, platforms, highestY, highestID, checkY) =
|
2025-01-17 13:18:02 +00:00
|
|
|
case collisions of
|
|
|
|
|
id :: tl =>
|
|
|
|
|
let
|
|
|
|
|
val {y = platY, ...} = Platform.find (id, platforms)
|
|
|
|
|
in
|
|
|
|
|
(* platY < highestY is correct because lowest number = highest
|
|
|
|
|
* in * this case *)
|
2025-01-27 23:41:59 +00:00
|
|
|
if platY < highestY andalso checkY <= platY then
|
2025-01-25 00:23:30 +00:00
|
|
|
getHighestPlatform (tl, platforms, platY, id, checkY)
|
2025-01-27 23:41:59 +00:00
|
|
|
else
|
2025-01-25 00:23:30 +00:00
|
|
|
getHighestPlatform (tl, platforms, highestY, highestID, checkY)
|
2025-01-17 13:18:02 +00:00
|
|
|
end
|
|
|
|
|
| [] => highestID
|
|
|
|
|
|
2025-01-18 00:25:27 +00:00
|
|
|
fun getPlatformBelowPlayer (player, platformTree, platforms) =
|
2025-01-17 12:29:07 +00:00
|
|
|
let
|
2025-01-17 13:18:02 +00:00
|
|
|
val {x, y, ...} = player
|
|
|
|
|
|
|
|
|
|
val searchWidth = Constants.playerSize
|
|
|
|
|
val searchHeight = Constants.worldHeight - y
|
|
|
|
|
|
2025-01-27 23:41:59 +00:00
|
|
|
val collisions = QuadTree.getCollisions
|
|
|
|
|
(x, y, searchWidth, searchHeight, ~1, platformTree)
|
2025-01-25 00:23:30 +00:00
|
|
|
val checkY = y + Constants.playerSize
|
2025-01-27 23:27:52 +00:00
|
|
|
|
|
|
|
|
val wh = Constants.worldHeight
|
2025-01-17 13:18:02 +00:00
|
|
|
in
|
2025-01-25 00:23:30 +00:00
|
|
|
getHighestPlatform (collisions, platforms, wh, ~1, checkY)
|
2025-01-17 13:18:02 +00:00
|
|
|
end
|
|
|
|
|
|
2025-01-18 00:25:27 +00:00
|
|
|
fun getPlatformBelowEnemy (enemy: enemy, platformTree, platforms) =
|
|
|
|
|
let
|
|
|
|
|
val {x, y, ...} = enemy
|
|
|
|
|
|
|
|
|
|
val searchWidth = Constants.enemySize
|
|
|
|
|
val searchHeight = Constants.worldHeight - y
|
|
|
|
|
|
2025-01-21 23:54:53 +00:00
|
|
|
val y = y + Constants.enemySize
|
|
|
|
|
|
2025-01-27 23:41:59 +00:00
|
|
|
val collisions = QuadTree.getCollisions
|
|
|
|
|
(x, y, searchWidth, searchHeight, ~1, platformTree)
|
2025-01-18 00:25:27 +00:00
|
|
|
val wh = Constants.worldHeight
|
|
|
|
|
in
|
2025-01-25 00:23:30 +00:00
|
|
|
getHighestPlatform (collisions, platforms, wh, ~1, y)
|
2025-01-18 00:25:27 +00:00
|
|
|
end
|
|
|
|
|
|
2025-01-24 02:09:20 +00:00
|
|
|
fun canJump (prevPlatform, nextPlatform) =
|
2025-01-17 13:18:02 +00:00
|
|
|
let
|
2025-01-24 02:09:20 +00:00
|
|
|
val {x = pPlatX, y = pPlatY, width = pPlatW, ...} = prevPlatform
|
|
|
|
|
val pPlatFinishX = pPlatX + pPlatW
|
2025-01-18 00:25:27 +00:00
|
|
|
|
2025-01-24 02:09:20 +00:00
|
|
|
val {x = nPlatX, y = nPlatY, width = nPlatW, ...} = nextPlatform
|
|
|
|
|
val nPlatFinishX = nPlatX + nPlatW
|
|
|
|
|
in
|
2025-01-24 02:12:04 +00:00
|
|
|
(isBetween (nPlatX, pPlatX, nPlatFinishX)
|
2025-01-24 22:51:20 +00:00
|
|
|
orelse isBetween (nPlatX, pPlatFinishX, nPlatFinishX))
|
2025-01-24 02:12:04 +00:00
|
|
|
andalso pPlatY > nPlatY
|
2025-01-24 02:09:20 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun getJumpPatches (nextPlatform, platformTree, enemy, acc) =
|
|
|
|
|
let
|
|
|
|
|
val {x = platX, y = platY, width = platWidth, ...} = nextPlatform
|
|
|
|
|
val platFinishX = platX + platWidth
|
|
|
|
|
|
|
|
|
|
val {x = eX, y = ey, yAxis = eyAxis, xAxis = exAxis, ...} = enemy
|
|
|
|
|
val ecx = eX + (Constants.enemySize div 2)
|
2025-01-23 01:06:16 +00:00
|
|
|
val ey = ey + Constants.enemySize
|
2025-01-18 00:25:27 +00:00
|
|
|
|
2025-01-21 23:54:53 +00:00
|
|
|
val standingOnPlat = standingOnArea (enemy, platformTree)
|
|
|
|
|
in
|
2025-01-24 09:48:57 +00:00
|
|
|
if ey >= platY andalso standingOnPlat then
|
2025-01-24 22:51:20 +00:00
|
|
|
if
|
|
|
|
|
isBetween (platX, ecx, platFinishX)
|
|
|
|
|
then
|
2025-01-24 02:09:20 +00:00
|
|
|
(* can jump from same position enemy is at *)
|
2025-01-29 02:23:44 +00:00
|
|
|
let
|
|
|
|
|
(* since we want to jump vertically and not risk falling off by
|
|
|
|
|
* jumping + moving either left or right, make enemy stay still *)
|
|
|
|
|
val acc = EnemyPatch.W_X_AXIS STAY_STILL :: acc
|
|
|
|
|
in
|
|
|
|
|
case eyAxis of
|
|
|
|
|
ON_GROUND => EnemyPatch.W_Y_AXIS (JUMPING 0) :: acc
|
|
|
|
|
| FALLING => EnemyPatch.W_Y_AXIS (JUMPING 0) :: acc
|
|
|
|
|
| _ => acc
|
|
|
|
|
end
|
2025-01-27 23:41:59 +00:00
|
|
|
else (* have to travel either left or right before jumping *) if
|
|
|
|
|
ecx < platX
|
|
|
|
|
then
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
|
|
|
|
else
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
2025-01-24 22:51:20 +00:00
|
|
|
else
|
|
|
|
|
acc
|
2025-01-21 23:54:53 +00:00
|
|
|
end
|
2025-01-17 12:29:07 +00:00
|
|
|
|
2025-01-24 22:51:20 +00:00
|
|
|
fun canDrop (prevPlatform, nextPlatform) =
|
2025-01-23 00:01:15 +00:00
|
|
|
let
|
2025-01-24 22:51:20 +00:00
|
|
|
val {x = pPlatX, y = pPlatY, width = pPlatW, ...} = prevPlatform
|
|
|
|
|
val pPlatFinishX = pPlatX + pPlatW
|
2025-01-23 00:01:15 +00:00
|
|
|
|
2025-01-24 22:51:20 +00:00
|
|
|
val {x = nPlatX, y = nPlatY, width = nPlatW, ...} = nextPlatform
|
|
|
|
|
val nPlatFinishX = nPlatX + nPlatW
|
|
|
|
|
in
|
|
|
|
|
(isBetween (nPlatX, pPlatX, nPlatFinishX)
|
|
|
|
|
orelse isBetween (nPlatX, pPlatFinishX, nPlatFinishX))
|
|
|
|
|
andalso pPlatY < nPlatY
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun getDropPatches (nextPlatform, platformTree, enemy, acc) =
|
|
|
|
|
let
|
|
|
|
|
val {x = platX, y = platY, width = platWidth, ...} = nextPlatform
|
|
|
|
|
val platFinishX = platX + platWidth
|
|
|
|
|
|
|
|
|
|
val {x = eX, y = ey, yAxis = eyAxis, xAxis = exAxis, ...} = enemy
|
|
|
|
|
val ecx = eX + (Constants.enemySize div 2)
|
|
|
|
|
val ey = ey + Constants.enemySize
|
2025-01-23 00:01:15 +00:00
|
|
|
|
|
|
|
|
val standingOnPlat = standingOnArea (enemy, platformTree)
|
|
|
|
|
in
|
2025-01-24 22:51:20 +00:00
|
|
|
if ey <= platY andalso standingOnPlat then
|
|
|
|
|
if
|
|
|
|
|
isBetween (platX, ecx, platFinishX)
|
|
|
|
|
then
|
|
|
|
|
(* can jump from same position enemy is at *)
|
2025-01-29 02:23:44 +00:00
|
|
|
let
|
|
|
|
|
val acc = EnemyPatch.W_X_AXIS STAY_STILL :: acc
|
|
|
|
|
in
|
|
|
|
|
case eyAxis of
|
|
|
|
|
ON_GROUND => EnemyPatch.W_Y_AXIS DROP_BELOW_PLATFORM :: acc
|
|
|
|
|
| FALLING => EnemyPatch.W_Y_AXIS DROP_BELOW_PLATFORM :: acc
|
|
|
|
|
| _ => acc
|
|
|
|
|
end
|
2025-01-27 23:41:59 +00:00
|
|
|
else (* have to travel either left or right before jumping *) if
|
|
|
|
|
ecx < platX
|
|
|
|
|
then
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
|
|
|
|
else
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
2025-01-24 22:51:20 +00:00
|
|
|
else
|
|
|
|
|
acc
|
2025-01-23 00:01:15 +00:00
|
|
|
end
|
|
|
|
|
|
2025-01-25 00:23:30 +00:00
|
|
|
fun getMoveRightPatches (nextPlatform, enemy, platformTree, acc) =
|
2025-01-29 19:12:24 +00:00
|
|
|
if TraceJump.traceRightJump (enemy, #id nextPlatform, platformTree, nextPlatform) then
|
|
|
|
|
if standingOnArea (enemy, platformTree) then
|
|
|
|
|
EnemyPatch.W_Y_AXIS (JUMPING 0) :: EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
2025-01-25 00:23:30 +00:00
|
|
|
else
|
2025-01-29 19:12:24 +00:00
|
|
|
EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
|
|
|
|
else
|
|
|
|
|
(* placeholder: should check if we can move to the next platform while
|
|
|
|
|
* dropping *)
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
2025-01-25 00:23:30 +00:00
|
|
|
|
2025-01-25 19:43:56 +00:00
|
|
|
fun getMoveLeftPatches (nextPlatform, enemy, platformTree, acc) =
|
|
|
|
|
let
|
|
|
|
|
val {x = platX, y = platY, width = platWidth, ...} = nextPlatform
|
|
|
|
|
val platFinishX = platX + platWidth
|
|
|
|
|
|
|
|
|
|
val {x = ex, y = ey, yAxis = eyAxis, ...} = enemy
|
|
|
|
|
|
|
|
|
|
val xDiff = ex - platX
|
|
|
|
|
in
|
|
|
|
|
if ey > platY then
|
|
|
|
|
(* enemy is lower than next platform so needs to jump *)
|
|
|
|
|
let
|
|
|
|
|
val jumpAmt =
|
|
|
|
|
case eyAxis of
|
|
|
|
|
JUMPING amt => amt
|
|
|
|
|
| _ => 0
|
|
|
|
|
val apexY = ey - (Constants.jumpLimit - jumpAmt)
|
|
|
|
|
|
|
|
|
|
val yDiff = platY - apexY
|
|
|
|
|
in
|
2025-01-29 02:56:21 +00:00
|
|
|
if yDiff <= xDiff then
|
|
|
|
|
(* enemy can reach platform by jumping *)
|
2025-01-25 19:43:56 +00:00
|
|
|
let
|
|
|
|
|
val acc =
|
|
|
|
|
if standingOnArea (enemy, platformTree) then
|
|
|
|
|
EnemyPatch.W_Y_AXIS (JUMPING 0) :: acc
|
|
|
|
|
else
|
|
|
|
|
acc
|
|
|
|
|
in
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
|
|
|
end
|
|
|
|
|
else
|
2025-01-27 23:41:59 +00:00
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
2025-01-25 19:43:56 +00:00
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
(* platform is below or at same y coordinat as enemy
|
|
|
|
|
* so might possibly require dropping below rather than jumping. *)
|
|
|
|
|
let
|
|
|
|
|
(* check if we can get to next platform without jumping.
|
|
|
|
|
* If we can, then simply move rightwards
|
|
|
|
|
* and possibly drop below platform.
|
|
|
|
|
* Else, jump and move rightwards *)
|
|
|
|
|
val yDiff = platY - ey
|
|
|
|
|
in
|
|
|
|
|
if yDiff >= xDiff then
|
2025-01-29 02:56:21 +00:00
|
|
|
(* can reach next platform by simply dropping and moving left *)
|
2025-01-27 23:41:59 +00:00
|
|
|
EnemyPatch.W_Y_AXIS DROP_BELOW_PLATFORM
|
|
|
|
|
:: EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
2025-01-25 19:43:56 +00:00
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val jumpAmt =
|
|
|
|
|
case eyAxis of
|
|
|
|
|
JUMPING amt => amt
|
|
|
|
|
| _ => 0
|
|
|
|
|
val apexY = ey - (Constants.jumpLimit - jumpAmt)
|
|
|
|
|
val yDiff = platY - apexY
|
|
|
|
|
in
|
2025-01-29 02:56:21 +00:00
|
|
|
if yDiff <= xDiff then
|
2025-01-25 19:43:56 +00:00
|
|
|
(* can reach if we jump and move left *)
|
|
|
|
|
let
|
|
|
|
|
val acc =
|
|
|
|
|
if standingOnArea (enemy, platformTree) then
|
|
|
|
|
EnemyPatch.W_Y_AXIS (JUMPING 0) :: acc
|
|
|
|
|
else
|
|
|
|
|
acc
|
|
|
|
|
in
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
|
|
|
end
|
|
|
|
|
else
|
|
|
|
|
(* cannot reach yet so move left until we can *)
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2025-01-21 23:54:53 +00:00
|
|
|
(* get patches to help enemy move to nextPlatformID *)
|
|
|
|
|
fun getPathToNextPlatform
|
|
|
|
|
(nextPlatformID, platforms, platformTree, enemy, eID, pID, acc) =
|
|
|
|
|
let
|
2025-01-24 02:09:20 +00:00
|
|
|
val currentPlatform = Platform.find (eID, platforms)
|
2025-01-21 23:54:53 +00:00
|
|
|
val nextPlatform = Platform.find (nextPlatformID, platforms)
|
2025-01-17 12:29:07 +00:00
|
|
|
|
2025-01-24 02:09:20 +00:00
|
|
|
val canJump = canJump (currentPlatform, nextPlatform)
|
2025-01-24 22:51:20 +00:00
|
|
|
val canDrop = canDrop (currentPlatform, nextPlatform)
|
2025-01-21 23:54:53 +00:00
|
|
|
in
|
2025-01-24 22:51:20 +00:00
|
|
|
if canJump then
|
|
|
|
|
getJumpPatches (nextPlatform, platformTree, enemy, acc)
|
|
|
|
|
else if canDrop then
|
|
|
|
|
getDropPatches (nextPlatform, platformTree, enemy, acc)
|
2025-01-27 23:41:59 +00:00
|
|
|
else
|
2025-01-25 00:23:30 +00:00
|
|
|
let
|
|
|
|
|
(* if can neither jump or drop to next platform vertically
|
|
|
|
|
* then remaining options are either jumping to the right or left.
|
|
|
|
|
* Figure out which the enemy needs to do and progress to it. *)
|
2025-01-26 13:23:20 +00:00
|
|
|
val {x = eX, ...} = enemy
|
2025-01-25 00:23:30 +00:00
|
|
|
val {x = nPlatX, width = nPlatW, ...} = nextPlatform
|
|
|
|
|
in
|
|
|
|
|
if eX < nPlatX then
|
|
|
|
|
getMoveRightPatches (nextPlatform, enemy, platformTree, acc)
|
|
|
|
|
else
|
2025-01-25 19:43:56 +00:00
|
|
|
getMoveLeftPatches (nextPlatform, enemy, platformTree, acc)
|
2025-01-25 00:23:30 +00:00
|
|
|
end
|
2025-01-21 23:54:53 +00:00
|
|
|
end
|
2025-01-17 13:18:02 +00:00
|
|
|
|
2025-01-26 13:23:20 +00:00
|
|
|
(* if only one side in x direction overlaps with platform,
|
|
|
|
|
* then move enemy left/right to make them fully overlap with platform *)
|
|
|
|
|
fun getHorizontalLandingPatches (enemy, nextPlatform, acc) =
|
2025-01-29 02:23:44 +00:00
|
|
|
case #xAxis enemy of
|
|
|
|
|
STAY_STILL =>
|
2025-01-27 23:41:59 +00:00
|
|
|
acc
|
2025-01-29 02:23:44 +00:00
|
|
|
| _ =>
|
|
|
|
|
let
|
|
|
|
|
val {x = px, width = pw, ...} = nextPlatform
|
|
|
|
|
val pfx = px + pw
|
|
|
|
|
|
|
|
|
|
val {x = ex, ...} = enemy
|
|
|
|
|
val efx = ex + Constants.enemySize
|
|
|
|
|
in
|
|
|
|
|
if isBetween (px, ex, pfx) andalso isBetween (px, efx, pfx) then
|
|
|
|
|
acc
|
|
|
|
|
else
|
|
|
|
|
let
|
|
|
|
|
val startDiff = abs (px - ex)
|
|
|
|
|
val endDiff = abs (pfx - efx)
|
|
|
|
|
in
|
|
|
|
|
if startDiff > endDiff then EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
|
|
|
else EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
|
|
|
|
end
|
|
|
|
|
end
|
2025-01-26 13:23:20 +00:00
|
|
|
|
|
|
|
|
fun getFallingPatches (enemy, newPlatformID, platforms, acc) =
|
|
|
|
|
let
|
|
|
|
|
val nextPlatform = Platform.find (newPlatformID, platforms)
|
|
|
|
|
val acc = getHorizontalLandingPatches (enemy, nextPlatform, acc)
|
|
|
|
|
in
|
|
|
|
|
EnemyPatch.W_NEXT_PLAT_ID ~1 :: acc
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun getJumpLandingPatches (enemy, nextPlatformID, platforms, acc) =
|
|
|
|
|
let
|
|
|
|
|
val nextPlatform = Platform.find (nextPlatformID, platforms)
|
|
|
|
|
val {y = py, ...} = nextPlatform
|
|
|
|
|
|
|
|
|
|
val {y = ey, ...} = enemy
|
|
|
|
|
|
|
|
|
|
val acc = getHorizontalLandingPatches (enemy, nextPlatform, acc)
|
|
|
|
|
in
|
|
|
|
|
if ey < py - 65 then
|
|
|
|
|
(* set to falling *)
|
2025-01-27 23:41:59 +00:00
|
|
|
EnemyPatch.W_NEXT_PLAT_ID ~1 :: EnemyPatch.W_Y_AXIS FALLING :: acc
|
2025-01-26 13:23:20 +00:00
|
|
|
else
|
|
|
|
|
acc
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fun getLandingPatches (newPlatformID, platforms, enemy, acc) =
|
|
|
|
|
case #yAxis enemy of
|
2025-01-27 23:41:59 +00:00
|
|
|
JUMPING _ => getJumpLandingPatches (enemy, newPlatformID, platforms, acc)
|
|
|
|
|
| _ => getFallingPatches (enemy, newPlatformID, platforms, acc)
|
2025-01-26 13:23:20 +00:00
|
|
|
|
2025-01-29 00:22:48 +00:00
|
|
|
(* to be called by FOLLOW_SIME. The FOLLOW_SIME sometimes changes its x axis
|
|
|
|
|
* to STAY_STILL, so if this happens and we want to patrol,
|
|
|
|
|
* then start patrolling in the direction the player is in *)
|
|
|
|
|
fun startPatrolPatches (player, enemy, wallTree, platformTree, acc) =
|
|
|
|
|
case #xAxis enemy of
|
|
|
|
|
STAY_STILL =>
|
|
|
|
|
let
|
|
|
|
|
val acc =
|
|
|
|
|
if #x player <= #x enemy then
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
|
|
|
|
else
|
|
|
|
|
EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
|
|
|
in
|
|
|
|
|
acc
|
|
|
|
|
end
|
|
|
|
|
| _ =>
|
|
|
|
|
getPatrollPatches (enemy, wallTree, platformTree, acc)
|
|
|
|
|
|
2025-01-27 23:41:59 +00:00
|
|
|
fun getFollowPatches
|
2025-01-26 14:22:27 +00:00
|
|
|
(player: player, enemy, wallTree, platformTree, platforms, acc) =
|
2025-01-21 23:54:53 +00:00
|
|
|
let
|
|
|
|
|
(* todo: possibly get pID and eID of player/enemy in a different way *)
|
|
|
|
|
val pID = getPlatformBelowPlayer (player, platformTree, platforms)
|
2025-01-27 23:41:59 +00:00
|
|
|
val pID = if pID = ~1 then #platID player else pID
|
2025-01-25 09:17:58 +00:00
|
|
|
|
2025-01-21 23:54:53 +00:00
|
|
|
val eID = getPlatformBelowEnemy (enemy, platformTree, platforms)
|
2025-01-27 23:41:59 +00:00
|
|
|
val eID = if eID = ~1 then #platID enemy else eID
|
2025-01-29 00:22:48 +00:00
|
|
|
|
2025-01-17 13:18:02 +00:00
|
|
|
in
|
2025-01-27 04:52:49 +00:00
|
|
|
if eID = ~1 orelse pID = ~1 then
|
2025-01-25 00:48:56 +00:00
|
|
|
(* without checking that neither of these are ~1
|
|
|
|
|
* (which means there is no platform below the enemy/player)
|
|
|
|
|
* there is a subscript error because the PathFinding.start
|
|
|
|
|
* function expects neither of these values to be ~1. *)
|
2025-01-29 00:22:48 +00:00
|
|
|
startPatrolPatches (player, enemy, wallTree, platformTree, acc)
|
2025-01-27 04:52:49 +00:00
|
|
|
else if eID = #nextPlatID enemy then
|
|
|
|
|
getLandingPatches (eID, platforms, enemy, acc)
|
2025-01-26 14:22:27 +00:00
|
|
|
else if eID = pID then
|
2025-01-29 00:22:48 +00:00
|
|
|
startPatrolPatches (player, enemy, wallTree, platformTree, acc)
|
2025-01-21 23:54:53 +00:00
|
|
|
else
|
2025-01-25 00:48:56 +00:00
|
|
|
let
|
|
|
|
|
val bestPath = PathFinding.start (pID, eID, platforms, platformTree)
|
|
|
|
|
in
|
|
|
|
|
case bestPath of
|
|
|
|
|
nextPlatformID :: _ =>
|
2025-01-26 13:23:20 +00:00
|
|
|
let
|
|
|
|
|
val acc = EnemyPatch.W_NEXT_PLAT_ID nextPlatformID :: acc
|
2025-01-29 00:22:48 +00:00
|
|
|
val acc =
|
2025-01-26 13:23:20 +00:00
|
|
|
getPathToNextPlatform
|
2025-01-27 23:41:59 +00:00
|
|
|
( nextPlatformID
|
|
|
|
|
, platforms
|
|
|
|
|
, platformTree
|
|
|
|
|
, enemy
|
|
|
|
|
, eID
|
|
|
|
|
, pID
|
|
|
|
|
, acc
|
|
|
|
|
)
|
2025-01-29 00:22:48 +00:00
|
|
|
in
|
|
|
|
|
EnemyPatch.W_X_AXIS STAY_STILL :: acc
|
2025-01-26 13:23:20 +00:00
|
|
|
end
|
2025-01-29 00:45:49 +00:00
|
|
|
| [] => startPatrolPatches (player, enemy, wallTree, platformTree, acc)
|
2025-01-25 00:48:56 +00:00
|
|
|
end
|
2025-01-17 12:29:07 +00:00
|
|
|
end
|
|
|
|
|
|
2025-01-16 08:15:29 +00:00
|
|
|
fun getVariantPatches
|
|
|
|
|
(enemy, walls, wallTree, platforms, platformTree, player, acc) =
|
2025-01-14 11:50:27 +00:00
|
|
|
let
|
|
|
|
|
open EnemyVariants
|
|
|
|
|
in
|
|
|
|
|
case #variant enemy of
|
|
|
|
|
PATROL_SLIME => getPatrollPatches (enemy, wallTree, platformTree, acc)
|
2025-01-17 11:43:49 +00:00
|
|
|
| FOLLOW_SIME =>
|
2025-01-17 13:18:02 +00:00
|
|
|
getFollowPatches
|
|
|
|
|
(player, enemy, wallTree, platformTree, platforms, acc)
|
2025-01-14 11:50:27 +00:00
|
|
|
end
|
|
|
|
|
end
|