begin parameterising level so that it fits into larger type (with different modes like TITLE, LEVEL, SETTINGS, etc.)
This commit is contained in:
553
fcore/level/enemy/enemy-behaviour.sml
Normal file
553
fcore/level/enemy/enemy-behaviour.sml
Normal file
@@ -0,0 +1,553 @@
|
||||
structure EnemyBehaviour =
|
||||
struct
|
||||
open EnemyType
|
||||
open EntityType
|
||||
|
||||
fun canWalkAhead (x, y, wallTree, platformTree) =
|
||||
let
|
||||
val y = y + Constants.enemySize - 5
|
||||
val searchHeight = 10
|
||||
val searchWidth = Constants.moveEnemyBy
|
||||
in
|
||||
QuadTree.hasCollisionAt (x, y, searchWidth, searchHeight, ~1, wallTree)
|
||||
orelse
|
||||
QuadTree.hasCollisionAt
|
||||
(x, y, searchWidth, searchHeight, ~1, platformTree)
|
||||
end
|
||||
|
||||
(* 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).
|
||||
* *)
|
||||
fun standingOnArea (enemy: enemy, tree) =
|
||||
let
|
||||
val {x = ex, y = ey, ...} = enemy
|
||||
|
||||
val ey = ey + Constants.enemySize - 1
|
||||
|
||||
val width = Constants.enemySize
|
||||
val height = Platform.platHeight
|
||||
in
|
||||
QuadTree.hasCollisionAt (ex, ey, width, height, ~1, tree)
|
||||
end
|
||||
|
||||
fun getPatrolPatches (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
|
||||
val searchWidth = Constants.moveEnemyBy
|
||||
val searchHeight = Constants.enemySize - 5
|
||||
|
||||
val hasWallAhead = QuadTree.hasCollisionAt
|
||||
(searchStartX, y, searchWidth, searchHeight, ~1, wallTree)
|
||||
in
|
||||
if hasWallAhead then
|
||||
EnemyPatch.W_FACING FACING_RIGHT :: EnemyPatch.W_X_AXIS MOVE_RIGHT
|
||||
:: acc
|
||||
else if canWalkAhead (searchStartX, y, wallTree, platformTree) then
|
||||
(* invert direction if moving further left
|
||||
* will result in falling down *)
|
||||
acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_RIGHT :: EnemyPatch.W_X_AXIS MOVE_RIGHT
|
||||
:: acc
|
||||
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
|
||||
val searchWidth = Constants.moveEnemyBy
|
||||
val searchHeight = Constants.enemySize - 5
|
||||
|
||||
val hasWallAhead = QuadTree.hasCollisionAt
|
||||
(searchStartX, y, searchWidth, searchHeight, ~1, wallTree)
|
||||
in
|
||||
if hasWallAhead then
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_X_AXIS MOVE_LEFT
|
||||
:: acc
|
||||
else if canWalkAhead (searchStartX, y, wallTree, platformTree) then
|
||||
(* invert direction if moving further right
|
||||
* will result in falling down *)
|
||||
acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_X_AXIS MOVE_LEFT
|
||||
:: acc
|
||||
end
|
||||
| STAY_STILL => acc
|
||||
end
|
||||
|
||||
(* pathfinding *)
|
||||
fun isBetween (p1, check, p2) = check >= p1 andalso check <= p2
|
||||
|
||||
fun getHighestPlatform (collisions, platforms, highestY, highestID, checkY) =
|
||||
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 *)
|
||||
if platY < highestY andalso checkY <= platY then
|
||||
getHighestPlatform (tl, platforms, platY, id, checkY)
|
||||
else
|
||||
getHighestPlatform (tl, platforms, highestY, highestID, checkY)
|
||||
end
|
||||
| [] => highestID
|
||||
|
||||
fun getPlatformBelowEnemy (enemy: enemy, platformTree, platforms) =
|
||||
let
|
||||
val {x, y, ...} = enemy
|
||||
|
||||
val searchWidth = Constants.enemySize
|
||||
val searchHeight = Constants.worldHeight - y
|
||||
|
||||
val y = y + Constants.enemySize
|
||||
|
||||
val collisions = QuadTree.getCollisions
|
||||
(x, y, searchWidth, searchHeight, ~1, platformTree)
|
||||
val wh = Constants.worldHeight
|
||||
in
|
||||
getHighestPlatform (collisions, platforms, wh, ~1, y)
|
||||
end
|
||||
|
||||
fun canJump (prevPlatform, nextPlatform) =
|
||||
let
|
||||
val {x = pPlatX, y = pPlatY, width = pPlatW, ...} = prevPlatform
|
||||
val pPlatFinishX = pPlatX + pPlatW
|
||||
|
||||
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 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)
|
||||
val ey = ey + Constants.enemySize
|
||||
|
||||
val standingOnPlat = standingOnArea (enemy, platformTree)
|
||||
in
|
||||
if ey >= platY andalso standingOnPlat then
|
||||
if
|
||||
isBetween (platX, ecx, platFinishX)
|
||||
then
|
||||
(* can jump from same position enemy is at *)
|
||||
let
|
||||
(* since we want to jump vertically and not risk falling off by
|
||||
* jumping + moving either left or right, make enemy stay still *)
|
||||
in
|
||||
case eyAxis of
|
||||
ON_GROUND => EnemyPatch.W_Y_AXIS (JUMPING 0) :: acc
|
||||
| FALLING => EnemyPatch.W_Y_AXIS (JUMPING 0) :: acc
|
||||
| _ => acc
|
||||
end
|
||||
else (* have to travel either left or right before jumping *) if
|
||||
ecx < platX
|
||||
then
|
||||
EnemyPatch.W_FACING FACING_RIGHT :: EnemyPatch.W_X_AXIS MOVE_RIGHT
|
||||
:: acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_X_AXIS MOVE_LEFT
|
||||
:: acc
|
||||
else
|
||||
acc
|
||||
end
|
||||
|
||||
fun canDrop (prevPlatform, nextPlatform) =
|
||||
let
|
||||
val {x = pPlatX, y = pPlatY, width = pPlatW, ...} = prevPlatform
|
||||
val pPlatFinishX = pPlatX + pPlatW
|
||||
|
||||
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
|
||||
|
||||
val standingOnPlat = standingOnArea (enemy, platformTree)
|
||||
in
|
||||
if ey <= platY andalso standingOnPlat then
|
||||
if
|
||||
isBetween (platX, ecx, platFinishX)
|
||||
then
|
||||
(* can jump from same position enemy is at *)
|
||||
let 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
|
||||
else (* have to travel either left or right before jumping *) if
|
||||
ecx < platX
|
||||
then
|
||||
EnemyPatch.W_FACING FACING_RIGHT :: EnemyPatch.W_X_AXIS MOVE_RIGHT
|
||||
:: acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_X_AXIS MOVE_LEFT
|
||||
:: acc
|
||||
else
|
||||
acc
|
||||
end
|
||||
|
||||
fun getMoveRightPatches (nextPlatform, enemy, platformTree, acc) =
|
||||
(* important to check for drop first because path of traceRightJump includes
|
||||
* descent of jump/drop.
|
||||
* So, if we check for jump first, we would always jump before dropping
|
||||
* even if jumping is not necessary. *)
|
||||
if TraceJump.traceRightDrop (enemy, #id nextPlatform, platformTree) then
|
||||
EnemyPatch.W_FACING FACING_RIGHT
|
||||
:: EnemyPatch.W_Y_AXIS DROP_BELOW_PLATFORM
|
||||
:: EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
||||
else if TraceJump.traceRightJump (enemy, #id nextPlatform, platformTree) then
|
||||
if standingOnArea (enemy, platformTree) then
|
||||
EnemyPatch.W_FACING FACING_RIGHT :: EnemyPatch.W_Y_AXIS (JUMPING 0)
|
||||
:: EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_RIGHT :: EnemyPatch.W_X_AXIS MOVE_RIGHT
|
||||
:: acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_RIGHT :: EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
||||
|
||||
fun getMoveLeftPatches (nextPlatform, enemy, platformTree, acc) =
|
||||
if TraceJump.traceLeftDrop (enemy, #id nextPlatform, platformTree) then
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_Y_AXIS DROP_BELOW_PLATFORM
|
||||
:: EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
||||
else if TraceJump.traceLeftJump (enemy, #id nextPlatform, platformTree) then
|
||||
if standingOnArea (enemy, platformTree) then
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_Y_AXIS (JUMPING 0)
|
||||
:: EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
||||
else
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
||||
|
||||
(* get patches to help enemy move to nextPlatformID *)
|
||||
fun getPathToNextPlatform
|
||||
(nextPlatformID, platforms, platformTree, enemy, eID, pID, acc) =
|
||||
let
|
||||
val currentPlatform = Platform.find (eID, platforms)
|
||||
val nextPlatform = Platform.find (nextPlatformID, platforms)
|
||||
|
||||
val canJump = canJump (currentPlatform, nextPlatform)
|
||||
val canDrop = canDrop (currentPlatform, nextPlatform)
|
||||
in
|
||||
if canJump then
|
||||
getJumpPatches (nextPlatform, platformTree, enemy, acc)
|
||||
else if canDrop then
|
||||
getDropPatches (nextPlatform, platformTree, enemy, acc)
|
||||
else
|
||||
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. *)
|
||||
val {x = eX, ...} = enemy
|
||||
val {x = nPlatX, width = nPlatW, ...} = nextPlatform
|
||||
in
|
||||
if eX < nPlatX then
|
||||
getMoveRightPatches (nextPlatform, enemy, platformTree, acc)
|
||||
else
|
||||
getMoveLeftPatches (nextPlatform, enemy, platformTree, acc)
|
||||
end
|
||||
end
|
||||
|
||||
(* 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) = acc
|
||||
|
||||
fun getFallingPatches (enemy, newPlatformID, platforms, acc) =
|
||||
EnemyPatch.W_NEXT_PLAT_ID ~1 :: acc
|
||||
|
||||
fun getJumpLandingPatches (enemy, nextPlatformID, platforms, acc) = acc
|
||||
|
||||
fun getLandingPatches (newPlatformID, platforms, enemy, acc) =
|
||||
case #yAxis enemy of
|
||||
JUMPING _ => getJumpLandingPatches (enemy, newPlatformID, platforms, acc)
|
||||
| _ => getFallingPatches (enemy, newPlatformID, platforms, acc)
|
||||
|
||||
(* 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 =>
|
||||
(case #facing enemy of
|
||||
FACING_RIGHT => EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
||||
| FACING_LEFT => EnemyPatch.W_X_AXIS MOVE_LEFT :: acc)
|
||||
| _ => getPatrolPatches (enemy, wallTree, platformTree, acc)
|
||||
|
||||
fun isInFollowRange (player, enemy) =
|
||||
let
|
||||
val {x = px, y = py, ...} = player
|
||||
val pfx = px + Constants.playerWidth
|
||||
val pfy = py + Constants.playerHeight
|
||||
|
||||
val range = 199
|
||||
|
||||
val {x = ex, y = ey, ...} = enemy
|
||||
val eStartX = ex - range
|
||||
val eStartY = ey - range
|
||||
val efx = ex + Constants.enemySize + range
|
||||
val efy = ey + Constants.enemySize + range
|
||||
in
|
||||
Collision.isColliding (px, py, pfx, pfy, eStartX, eStartY, efx, efy)
|
||||
end
|
||||
|
||||
fun getFollowPatches
|
||||
( player: PlayerType.player
|
||||
, enemy
|
||||
, wallTree
|
||||
, platformTree
|
||||
, platforms
|
||||
, graph
|
||||
, acc
|
||||
) =
|
||||
let
|
||||
val pID = #platID player
|
||||
val eID = #platID enemy
|
||||
in
|
||||
if eID = ~1 orelse pID = ~1 then
|
||||
(* 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. *)
|
||||
startPatrolPatches (player, enemy, wallTree, platformTree, acc)
|
||||
else if eID = #nextPlatID enemy then
|
||||
getLandingPatches (eID, platforms, enemy, acc)
|
||||
else if eID = pID then
|
||||
startPatrolPatches (player, enemy, wallTree, platformTree, acc)
|
||||
else if isInFollowRange (player, enemy) then
|
||||
(* line of sight: only follow player if player is in some range *)
|
||||
let
|
||||
val bestPath = PathFinding.start
|
||||
(pID, eID, platforms, platformTree, graph)
|
||||
in
|
||||
case bestPath of
|
||||
nextPlatformID :: _ =>
|
||||
let
|
||||
val acc = EnemyPatch.W_NEXT_PLAT_ID nextPlatformID :: acc
|
||||
val acc = getPathToNextPlatform
|
||||
( nextPlatformID
|
||||
, platforms
|
||||
, platformTree
|
||||
, enemy
|
||||
, eID
|
||||
, pID
|
||||
, acc
|
||||
)
|
||||
in
|
||||
EnemyPatch.W_X_AXIS STAY_STILL :: acc
|
||||
end
|
||||
| [] =>
|
||||
startPatrolPatches (player, enemy, wallTree, platformTree, acc)
|
||||
end
|
||||
else
|
||||
startPatrolPatches (player, enemy, wallTree, platformTree, acc)
|
||||
end
|
||||
|
||||
fun withDefaultYAxis (enemy: enemy) =
|
||||
case #yAxis enemy of
|
||||
ON_GROUND => EnemyPatch.withPatch (enemy, EnemyPatch.W_Y_AXIS FALLING)
|
||||
| _ => enemy
|
||||
|
||||
fun updatePatrolState
|
||||
(player, enemy, walls, wallTree, platforms, platformTree) =
|
||||
let
|
||||
val {x, y, ...} = enemy
|
||||
val size = Constants.enemySize
|
||||
val enemy = withDefaultYAxis enemy
|
||||
|
||||
val patches = getPatrolPatches (enemy, wallTree, platformTree, [])
|
||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||
|
||||
val patches = EnemyPhysics.getPhysicsPatches enemy
|
||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||
|
||||
val patches = EnemyPhysics.getEnvironmentPatches
|
||||
(enemy, walls, wallTree, platforms, platformTree)
|
||||
in
|
||||
EnemyPatch.withPatches (enemy, patches)
|
||||
end
|
||||
|
||||
fun updateFollowState
|
||||
(player, enemy, walls, wallTree, platforms, platformTree, graph) =
|
||||
let
|
||||
val {x, y, ...} = enemy
|
||||
val size = Constants.enemySize
|
||||
|
||||
val enemy = withDefaultYAxis enemy
|
||||
|
||||
val patches = getFollowPatches
|
||||
(player, enemy, wallTree, platformTree, platforms, graph, [])
|
||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||
|
||||
val patches = EnemyPhysics.getPhysicsPatches enemy
|
||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||
|
||||
val patches = EnemyPhysics.getEnvironmentPatches
|
||||
(enemy, walls, wallTree, platforms, platformTree)
|
||||
in
|
||||
EnemyPatch.withPatches (enemy, patches)
|
||||
end
|
||||
|
||||
fun updateStraightBat (player, enemy, walls, wallTree) =
|
||||
let
|
||||
val {x, y, batRest, batDirY, batMinY, batMaxY, xAxis, ...} = enemy
|
||||
|
||||
val size = Constants.enemySize
|
||||
val moveByY = Constants.moveBatY
|
||||
val moveByX = Constants.moveBatX
|
||||
|
||||
val patches =
|
||||
(* get apatches for up/down movement *)
|
||||
case batDirY of
|
||||
UP =>
|
||||
if y - moveByY <= batMaxY then
|
||||
[EnemyPatch.W_BAT_DIR_Y DOWN, EnemyPatch.W_Y (y + moveByY)]
|
||||
else
|
||||
[EnemyPatch.W_Y (y - moveByY)]
|
||||
| DOWN =>
|
||||
if y + moveByY >= batMinY then
|
||||
[EnemyPatch.W_BAT_DIR_Y UP, EnemyPatch.W_Y (y - moveByY)]
|
||||
else
|
||||
[EnemyPatch.W_Y (y + moveByY)]
|
||||
|
||||
val patches =
|
||||
(* get patches for horizontal movement *)
|
||||
if QuadTree.hasCollisionAt (x, y, size, size, ~1, wallTree) then
|
||||
(* has collision with wall *)
|
||||
if batRest >= Constants.batRestLimit then
|
||||
(* make enemy move in opposite direction *)
|
||||
case xAxis of
|
||||
MOVE_RIGHT =>
|
||||
EnemyPatch.W_FACING FACING_LEFT :: EnemyPatch.W_X_AXIS MOVE_LEFT
|
||||
:: EnemyPatch.W_X (x - 1) :: patches
|
||||
| MOVE_LEFT =>
|
||||
EnemyPatch.W_FACING FACING_RIGHT
|
||||
:: EnemyPatch.W_X_AXIS MOVE_RIGHT :: EnemyPatch.W_X (x + 1)
|
||||
:: patches
|
||||
| _ => patches
|
||||
else
|
||||
(* keep resting until we hit rest limit *)
|
||||
EnemyPatch.W_BAT_REST (batRest + 1) :: patches
|
||||
else
|
||||
(* no collision, so continue moving in direction *)
|
||||
let
|
||||
val patches =
|
||||
case xAxis of
|
||||
MOVE_RIGHT => EnemyPatch.W_X (x + moveByX) :: patches
|
||||
| MOVE_LEFT => EnemyPatch.W_X (x - moveByX) :: patches
|
||||
| STAY_STILL => patches
|
||||
in
|
||||
EnemyPatch.W_BAT_REST 0 :: patches
|
||||
end
|
||||
in
|
||||
EnemyPatch.withPatches (enemy, patches)
|
||||
end
|
||||
|
||||
fun getShieldOnPatches (player, enemy) =
|
||||
if #platID player = #platID enemy then
|
||||
[]
|
||||
else
|
||||
(* turn off shield if player moved to a different platform *)
|
||||
[EnemyPatch.W_SHIELD_ON false]
|
||||
|
||||
fun getShieldOffPatches
|
||||
(player, enemy, walls, wallTree, platforms, platformTree) =
|
||||
let
|
||||
val {x = ex, y = ey, facing = eFacing, platID = eID, ...} = enemy
|
||||
val {x = px, y = py, platID = pID, ...} = player
|
||||
|
||||
val shouldTurnShieldOn =
|
||||
eID = pID
|
||||
andalso
|
||||
case eFacing of
|
||||
FACING_RIGHT => px > ex
|
||||
| FACING_LEFT => px < ex
|
||||
in
|
||||
if shouldTurnShieldOn then
|
||||
[EnemyPatch.W_SHIELD_ON true, EnemyPatch.W_X_AXIS STAY_STILL]
|
||||
else
|
||||
startPatrolPatches (player, enemy, wallTree, platformTree, [])
|
||||
end
|
||||
|
||||
fun updateShieldSlime
|
||||
(player, enemy, walls, wallTree, platforms, platformTree) =
|
||||
let
|
||||
val size = Constants.enemySize
|
||||
val enemy = withDefaultYAxis enemy
|
||||
|
||||
val patches =
|
||||
if #shieldOn enemy then
|
||||
getShieldOnPatches (player, enemy)
|
||||
else
|
||||
getShieldOffPatches
|
||||
(player, enemy, walls, wallTree, platforms, platformTree)
|
||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||
|
||||
val patches = EnemyPhysics.getPhysicsPatches enemy
|
||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||
|
||||
val patches = EnemyPhysics.getEnvironmentPatches
|
||||
(enemy, walls, wallTree, platforms, platformTree)
|
||||
in
|
||||
EnemyPatch.withPatches (enemy, patches)
|
||||
end
|
||||
|
||||
fun updateEnemyState
|
||||
(enemy, walls, wallTree, platforms, platformTree, player, graph) =
|
||||
case #variant enemy of
|
||||
PATROL_SLIME =>
|
||||
updatePatrolState
|
||||
(player, enemy, walls, wallTree, platforms, platformTree)
|
||||
| FOLLOW_SLIME =>
|
||||
updateFollowState
|
||||
(player, enemy, walls, wallTree, platforms, platformTree, graph)
|
||||
| STRAIGHT_BAT => updateStraightBat (player, enemy, walls, wallTree)
|
||||
| SHIELD_SLIME =>
|
||||
updateShieldSlime
|
||||
(player, enemy, walls, wallTree, platforms, platformTree)
|
||||
end
|
||||
1
fcore/level/enemy/enemy-map.sml
Normal file
1
fcore/level/enemy/enemy-map.sml
Normal file
@@ -0,0 +1 @@
|
||||
structure EnemyMap = MakeGapMap(EnemyPair)
|
||||
11
fcore/level/enemy/enemy-pair.sml
Normal file
11
fcore/level/enemy/enemy-pair.sml
Normal file
@@ -0,0 +1,11 @@
|
||||
structure EnemyPair =
|
||||
struct
|
||||
type key = int
|
||||
type value = EnemyType.enemy
|
||||
|
||||
fun l (a: int, b: int) = a < b
|
||||
fun eq (a: int, b: int) = a = b
|
||||
fun g (a: int, b: int) = a > b
|
||||
|
||||
val maxNodeSize = 8
|
||||
end
|
||||
338
fcore/level/enemy/enemy-patch.sml
Normal file
338
fcore/level/enemy/enemy-patch.sml
Normal file
@@ -0,0 +1,338 @@
|
||||
signature ENEMY_PATCH =
|
||||
sig
|
||||
datatype enemy_patch =
|
||||
W_HEALTH of int
|
||||
| W_X of int
|
||||
| W_Y of int
|
||||
| W_X_AXIS of EntityType.x_axis
|
||||
| W_Y_AXIS of EntityType.y_axis
|
||||
| W_PLAT_ID of int
|
||||
| W_NEXT_PLAT_ID of int
|
||||
| W_BAT_REST of int
|
||||
| W_BAT_MAX_Y of int
|
||||
| W_BAT_MIN_Y of int
|
||||
| W_BAT_DIR_Y of EnemyType.bat_dir_y
|
||||
| W_FACING of EntityType.facing
|
||||
| W_SHIELD_ON of bool
|
||||
|
||||
val withPatch: EnemyType.enemy * enemy_patch -> EnemyType.enemy
|
||||
|
||||
val withPatches: EnemyType.enemy * enemy_patch list -> EnemyType.enemy
|
||||
end
|
||||
|
||||
structure EnemyPatch: ENEMY_PATCH =
|
||||
struct
|
||||
datatype enemy_patch =
|
||||
W_HEALTH of int
|
||||
| W_X of int
|
||||
| W_Y of int
|
||||
| W_X_AXIS of EntityType.x_axis
|
||||
| W_Y_AXIS of EntityType.y_axis
|
||||
| W_PLAT_ID of int
|
||||
| W_NEXT_PLAT_ID of int
|
||||
| W_BAT_REST of int
|
||||
| W_BAT_MAX_Y of int
|
||||
| W_BAT_MIN_Y of int
|
||||
| W_BAT_DIR_Y of EnemyType.bat_dir_y
|
||||
| W_FACING of EntityType.facing
|
||||
| W_SHIELD_ON of bool
|
||||
|
||||
fun mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
) =
|
||||
{ id = id
|
||||
, health = health
|
||||
, x = x
|
||||
, y = y
|
||||
, xAxis = xAxis
|
||||
, yAxis = yAxis
|
||||
, variant = variant
|
||||
, platID = platID
|
||||
, nextPlatID = nextPlatID
|
||||
, batRest = batRest
|
||||
, batDirY = batDirY
|
||||
, batMaxY = batMaxY
|
||||
, batMinY = batMinY
|
||||
, facing = facing
|
||||
, shieldOn = shieldOn
|
||||
}
|
||||
|
||||
fun withPatch (enemy, patch) =
|
||||
let
|
||||
val
|
||||
{ id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
} = enemy
|
||||
in
|
||||
case patch of
|
||||
W_HEALTH health =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_X x =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_X_AXIS xAxis =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_Y y =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_Y_AXIS yAxis =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_PLAT_ID platID =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_NEXT_PLAT_ID nextPlatID =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_BAT_REST batRest =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_BAT_MAX_Y batMaxY =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_BAT_MIN_Y batMinY =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_BAT_DIR_Y batDirY =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_FACING facing =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
| W_SHIELD_ON shieldOn =>
|
||||
mkEnemy
|
||||
( id
|
||||
, health
|
||||
, x
|
||||
, y
|
||||
, xAxis
|
||||
, yAxis
|
||||
, variant
|
||||
, platID
|
||||
, nextPlatID
|
||||
, batRest
|
||||
, batDirY
|
||||
, batMaxY
|
||||
, batMinY
|
||||
, facing
|
||||
, shieldOn
|
||||
)
|
||||
end
|
||||
|
||||
fun withPatches (enemy, lst) =
|
||||
case lst of
|
||||
hd :: tl =>
|
||||
let val enemy = withPatch (enemy, hd)
|
||||
in withPatches (enemy, tl)
|
||||
end
|
||||
| [] => enemy
|
||||
end
|
||||
59
fcore/level/enemy/enemy-type.sml
Normal file
59
fcore/level/enemy/enemy-type.sml
Normal file
@@ -0,0 +1,59 @@
|
||||
signature ENEMY_TYPE =
|
||||
sig
|
||||
datatype variant = PATROL_SLIME | FOLLOW_SLIME | STRAIGHT_BAT | SHIELD_SLIME
|
||||
|
||||
datatype bat_dir_y = UP | DOWN
|
||||
|
||||
type enemy =
|
||||
{ id: int
|
||||
, health: int
|
||||
, x: int
|
||||
, y: int
|
||||
, xAxis: EntityType.x_axis
|
||||
, yAxis: EntityType.y_axis
|
||||
, variant: variant
|
||||
, platID: int
|
||||
, nextPlatID: int
|
||||
, batRest: int
|
||||
, batDirY: bat_dir_y
|
||||
, batMaxY: int
|
||||
, batMinY: int
|
||||
, facing: EntityType.facing
|
||||
, shieldOn: bool
|
||||
}
|
||||
|
||||
type falling_enemy = {x: int, y: int, variant: variant}
|
||||
|
||||
datatype shoot_x_axis = SHOOT_LEFT | SHOOT_RIGHT | NO_SHOOT_X
|
||||
datatype shoot_y_axis = SHOOT_UP | SHOOT_DOWN | NO_SHOOT_Y
|
||||
end
|
||||
|
||||
structure EnemyType: ENEMY_TYPE =
|
||||
struct
|
||||
datatype variant = PATROL_SLIME | FOLLOW_SLIME | STRAIGHT_BAT | SHIELD_SLIME
|
||||
|
||||
datatype bat_dir_y = UP | DOWN
|
||||
|
||||
type enemy =
|
||||
{ id: int
|
||||
, health: int
|
||||
, x: int
|
||||
, y: int
|
||||
, xAxis: EntityType.x_axis
|
||||
, yAxis: EntityType.y_axis
|
||||
, variant: variant
|
||||
, platID: int
|
||||
, nextPlatID: int
|
||||
, batRest: int
|
||||
, batDirY: bat_dir_y
|
||||
, batMaxY: int
|
||||
, batMinY: int
|
||||
, facing: EntityType.facing
|
||||
, shieldOn: bool
|
||||
}
|
||||
|
||||
type falling_enemy = {x: int, y: int, variant: variant}
|
||||
|
||||
datatype shoot_x_axis = SHOOT_LEFT | SHOOT_RIGHT | NO_SHOOT_X
|
||||
datatype shoot_y_axis = SHOOT_UP | SHOOT_DOWN | NO_SHOOT_Y
|
||||
end
|
||||
133
fcore/level/enemy/enemy.sml
Normal file
133
fcore/level/enemy/enemy.sml
Normal file
@@ -0,0 +1,133 @@
|
||||
structure Enemy =
|
||||
struct
|
||||
(* - Updating state of enemies per loop - *)
|
||||
structure UpdateEnemies =
|
||||
MakeGapMapMapper
|
||||
(struct
|
||||
structure Pair = EnemyPair
|
||||
|
||||
type env =
|
||||
{ walls: Wall.t vector
|
||||
, wallTree: QuadTree.t
|
||||
, platforms: Platform.t vector
|
||||
, platformTree: QuadTree.t
|
||||
, player: PlayerType.player
|
||||
, graph: PlatSet.elem vector vector
|
||||
}
|
||||
|
||||
type state = EnemyMap.t
|
||||
|
||||
fun map (enemy, env) =
|
||||
let
|
||||
val {walls, wallTree, platforms, platformTree, player, graph} = env
|
||||
in
|
||||
EnemyBehaviour.updateEnemyState
|
||||
(enemy, walls, wallTree, platforms, platformTree, player, graph)
|
||||
end
|
||||
end)
|
||||
|
||||
fun update (enemies, walls, wallTree, platforms, platformTree, player, graph) =
|
||||
let
|
||||
val env =
|
||||
{ walls = walls
|
||||
, wallTree = wallTree
|
||||
, platforms = platforms
|
||||
, platformTree = platformTree
|
||||
, player = player
|
||||
, graph = graph
|
||||
}
|
||||
in
|
||||
UpdateEnemies.map (enemies, env)
|
||||
end
|
||||
|
||||
(* - Generating enemy tree - *)
|
||||
structure EnemyTree =
|
||||
MakeGapMapFolder
|
||||
(struct
|
||||
structure Pair = EnemyPair
|
||||
|
||||
type env = unit
|
||||
type state = QuadTree.t
|
||||
|
||||
fun fold (enemyID, enemy: EnemyType.enemy, (), quadTree) =
|
||||
let
|
||||
val {id, x, y, ...} = enemy
|
||||
val size = Constants.enemySize
|
||||
in
|
||||
QuadTree.insert (x, y, size, size, id, quadTree)
|
||||
end
|
||||
end)
|
||||
|
||||
fun generateTree enemies =
|
||||
EnemyTree.foldUnordered
|
||||
( enemies
|
||||
, ()
|
||||
, QuadTree.create (Constants.worldWidth, Constants.worldHeight)
|
||||
)
|
||||
|
||||
(* - Drawing enemies - *)
|
||||
structure EnemyDrawVec =
|
||||
MakeGapMapFolder
|
||||
(struct
|
||||
structure Pair = EnemyPair
|
||||
|
||||
type env = Real32.real * Real32.real
|
||||
type state = Real32.real vector list
|
||||
|
||||
fun helpGetDrawVec (enemy: EnemyType.enemy, width, height) =
|
||||
let
|
||||
val {x, y, variant, ...} = enemy
|
||||
val wratio = width / Constants.worldWidthReal
|
||||
val hratio = height / Constants.worldHeightReal
|
||||
|
||||
open EnemyType
|
||||
val (r, g, b) =
|
||||
case variant of
|
||||
PATROL_SLIME => (0.5, 0.5, 1.0)
|
||||
| FOLLOW_SLIME => (1.0, 0.5, 0.5)
|
||||
| STRAIGHT_BAT => (0.55, 0.55, 0.55)
|
||||
| SHIELD_SLIME =>
|
||||
if #shieldOn enemy then (0.33, 0.33, 0.11)
|
||||
else (0.5, 0.5, 1.0)
|
||||
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 realSize = Constants.enemySizeReal * wratio
|
||||
in
|
||||
Block.lerp (x, y, realSize, realSize, width, height, r, g, b)
|
||||
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 realSize = Constants.enemySizeReal * hratio
|
||||
in
|
||||
Block.lerp (x, y, realSize, realSize, width, height, r, g, b)
|
||||
end
|
||||
end
|
||||
|
||||
fun fold (_, enemy: EnemyType.enemy, (width, height), acc) =
|
||||
helpGetDrawVec (enemy, width, height) :: acc
|
||||
end)
|
||||
|
||||
fun getDrawVec (enemies, width, height) =
|
||||
let val vec = EnemyDrawVec.foldUnordered (enemies, (width, height), [])
|
||||
in Vector.concat vec
|
||||
end
|
||||
end
|
||||
147
fcore/level/enemy/falling-enemies.sml
Normal file
147
fcore/level/enemy/falling-enemies.sml
Normal file
@@ -0,0 +1,147 @@
|
||||
structure FallingEnemies =
|
||||
struct
|
||||
open EnemyType
|
||||
open EntityType
|
||||
|
||||
(* - Generating tree of falling enemies - *)
|
||||
structure FallingTree =
|
||||
MakeGapMapFolder
|
||||
(struct
|
||||
structure Pair = FallingEnemyPair
|
||||
|
||||
type env = unit
|
||||
type state = QuadTree.t
|
||||
|
||||
fun fold (fallingID, falling: EnemyType.falling_enemy, (), quadTree) =
|
||||
let
|
||||
val {x, y, ...} = falling
|
||||
val size = Constants.enemySize
|
||||
in
|
||||
QuadTree.insert (x, y, size, size, fallingID, quadTree)
|
||||
end
|
||||
end)
|
||||
|
||||
fun generateTree falling =
|
||||
FallingTree.foldUnordered
|
||||
( falling
|
||||
, ()
|
||||
, QuadTree.create (Constants.worldWidth, Constants.worldHeight)
|
||||
)
|
||||
|
||||
(* - Updating position of fallingEnemies
|
||||
* - and filtering out enemies which are no longer in world bounds - *)
|
||||
structure UpdateFalling =
|
||||
MakeGapMapFolder
|
||||
(struct
|
||||
structure Pair = FallingEnemyPair
|
||||
|
||||
type env = unit
|
||||
|
||||
type state = FallingEnemyMap.t
|
||||
|
||||
fun fold (fallingID, fallingEnemy, (), fallingMap) =
|
||||
let
|
||||
val {x, y, variant} = fallingEnemy
|
||||
val size = Constants.enemySize
|
||||
val ww = Constants.worldWidth
|
||||
val wh = Constants.worldHeight
|
||||
in
|
||||
if Collision.isCollidingPlus (x, y, size, size, 0, 0, ww, wh) then
|
||||
let
|
||||
val newFalling =
|
||||
{x = x, y = y - Constants.moveEnemyBy, variant = variant}
|
||||
in
|
||||
FallingEnemyMap.add (fallingID, newFalling, fallingMap)
|
||||
end
|
||||
else
|
||||
(* filter out since not in world bounds *)
|
||||
fallingMap
|
||||
end
|
||||
end)
|
||||
|
||||
fun update fallingEnemies =
|
||||
UpdateFalling.foldUnordered (fallingEnemies, (), FallingEnemyMap.empty)
|
||||
|
||||
(* - Drawing falling enemies - *)
|
||||
structure FallingDrawVec =
|
||||
MakeGapMapFolder
|
||||
(struct
|
||||
structure Pair = FallingEnemyPair
|
||||
|
||||
type env =
|
||||
{ width: Real32.real
|
||||
, height: Real32.real
|
||||
, ratio: Real32.real
|
||||
, xOffset: Real32.real
|
||||
, yOffset: Real32.real
|
||||
}
|
||||
|
||||
type state = Real32.real vector list
|
||||
|
||||
fun helpGetDrawVec
|
||||
(fallingEnemy, width, height, ratio, xOffset, yOffset, acc) =
|
||||
let
|
||||
val {x, y, variant = _} = fallingEnemy
|
||||
|
||||
val x = Real32.fromInt x * ratio + xOffset
|
||||
val y = Real32.fromInt y * ratio + yOffset
|
||||
val size = Real32.fromInt Constants.enemySize * ratio
|
||||
|
||||
val vec = Block.lerp
|
||||
(x, y, size, size, width, height, 0.3, 0.3, 0.3)
|
||||
in
|
||||
vec :: acc
|
||||
end
|
||||
|
||||
fun fold (_, fallingEnemy, env, acc) =
|
||||
let
|
||||
val {width, height, ratio, xOffset, yOffset} = env
|
||||
in
|
||||
helpGetDrawVec
|
||||
(fallingEnemy, width, height, ratio, xOffset, yOffset, acc)
|
||||
end
|
||||
end)
|
||||
|
||||
fun getDrawVec (game: LevelType.level_type, width, height) =
|
||||
let
|
||||
val fallingEnemies = #fallingEnemies game
|
||||
val wratio = width / Constants.worldWidthReal
|
||||
val hratio = height / Constants.worldHeightReal
|
||||
|
||||
val env =
|
||||
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
|
||||
{ width = width
|
||||
, height = height
|
||||
, ratio = wratio
|
||||
, xOffset = 0.0
|
||||
, yOffset = 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
|
||||
{ width = width
|
||||
, height = height
|
||||
, ratio = hratio
|
||||
, xOffset = xOffset
|
||||
, yOffset = 0.0
|
||||
}
|
||||
end
|
||||
|
||||
val lst = FallingDrawVec.foldUnordered (fallingEnemies, env, [])
|
||||
in
|
||||
Vector.concat lst
|
||||
end
|
||||
end
|
||||
1
fcore/level/enemy/falling-enemy-map.sml
Normal file
1
fcore/level/enemy/falling-enemy-map.sml
Normal file
@@ -0,0 +1 @@
|
||||
structure FallingEnemyMap = MakeGapMap(FallingEnemyPair)
|
||||
11
fcore/level/enemy/falling-enemy-pair.sml
Normal file
11
fcore/level/enemy/falling-enemy-pair.sml
Normal file
@@ -0,0 +1,11 @@
|
||||
structure FallingEnemyPair =
|
||||
struct
|
||||
type key = int
|
||||
type value = EnemyType.falling_enemy
|
||||
|
||||
fun l (a: int, b: int) = a < b
|
||||
fun eq (a: int, b: int) = a = b
|
||||
fun g (a: int, b: int) = a > b
|
||||
|
||||
val maxNodeSize = 8
|
||||
end
|
||||
Reference in New Issue
Block a user