slight refactoring to help with extensibility of adding enemy variants later
This commit is contained in:
122
fcore/enemy-behaviour.sml
Normal file
122
fcore/enemy-behaviour.sml
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
structure EnemyBehaviour =
|
||||||
|
struct
|
||||||
|
open GameType
|
||||||
|
|
||||||
|
fun canWalkAhead (x, y, wallTree, platformTree) =
|
||||||
|
let
|
||||||
|
val ww = Constants.worldWidth
|
||||||
|
val wh = Constants.worldHeight
|
||||||
|
|
||||||
|
val searchWidth = Constants.enemySize
|
||||||
|
|
||||||
|
val y = y + Constants.enemySize - 5
|
||||||
|
val searchHeight = 10
|
||||||
|
in
|
||||||
|
QuadTree.hasCollisionAt
|
||||||
|
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, wallTree)
|
||||||
|
orelse
|
||||||
|
QuadTree.hasCollisionAt
|
||||||
|
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, platformTree)
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
val searchWidth = Constants.enemySize
|
||||||
|
val searchHeight = Constants.enemySize - 5
|
||||||
|
|
||||||
|
val ww = Constants.worldWidth
|
||||||
|
val wh = Constants.worldHeight
|
||||||
|
|
||||||
|
val hasWallAhead = QuadTree.hasCollisionAt
|
||||||
|
( searchStartX
|
||||||
|
, y
|
||||||
|
, searchWidth
|
||||||
|
, searchHeight
|
||||||
|
, 0
|
||||||
|
, 0
|
||||||
|
, ww
|
||||||
|
, wh
|
||||||
|
, ~1
|
||||||
|
, wallTree
|
||||||
|
)
|
||||||
|
in
|
||||||
|
if
|
||||||
|
hasWallAhead
|
||||||
|
then EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
||||||
|
else (* invert direction if moving further left
|
||||||
|
* will result in falling down *) if
|
||||||
|
canWalkAhead (searchStartX, y, wallTree, platformTree)
|
||||||
|
then acc
|
||||||
|
else 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.enemySize
|
||||||
|
val searchHeight = Constants.enemySize - 5
|
||||||
|
|
||||||
|
val ww = Constants.worldWidth
|
||||||
|
val wh = Constants.worldHeight
|
||||||
|
|
||||||
|
val hasWallAhead = QuadTree.hasCollisionAt
|
||||||
|
( searchStartX
|
||||||
|
, y
|
||||||
|
, searchWidth
|
||||||
|
, searchHeight
|
||||||
|
, 0
|
||||||
|
, 0
|
||||||
|
, ww
|
||||||
|
, wh
|
||||||
|
, ~1
|
||||||
|
, wallTree
|
||||||
|
)
|
||||||
|
in
|
||||||
|
if
|
||||||
|
hasWallAhead
|
||||||
|
then EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
||||||
|
else (* invert direction if moving further right
|
||||||
|
* will result in falling down *) if
|
||||||
|
canWalkAhead (searchStartX, y, wallTree, platformTree)
|
||||||
|
then acc
|
||||||
|
else EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
||||||
|
end
|
||||||
|
| STAY_STILL => acc
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fun getVariantPatches (enemy, walls, wallTree, platforms, platformTree, acc) =
|
||||||
|
let
|
||||||
|
open EnemyVariants
|
||||||
|
in
|
||||||
|
case #variant enemy of
|
||||||
|
PATROL_SLIME => getPatrollPatches (enemy, wallTree, platformTree, acc)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -21,19 +21,26 @@ struct
|
|||||||
| W_X_AXIS of GameType.x_axis
|
| W_X_AXIS of GameType.x_axis
|
||||||
| W_Y_AXIS of GameType.y_axis
|
| W_Y_AXIS of GameType.y_axis
|
||||||
|
|
||||||
fun mkEnemy (id, health, x, y, xAxis, yAxis) =
|
fun mkEnemy (id, health, x, y, xAxis, yAxis, variant) =
|
||||||
{id = id, health = health, x = x, y = y, xAxis = xAxis, yAxis = yAxis}
|
{ id = id
|
||||||
|
, health = health
|
||||||
|
, x = x
|
||||||
|
, y = y
|
||||||
|
, xAxis = xAxis
|
||||||
|
, yAxis = yAxis
|
||||||
|
, variant = variant
|
||||||
|
}
|
||||||
|
|
||||||
fun withPatch (enemy, patch) =
|
fun withPatch (enemy, patch) =
|
||||||
let
|
let
|
||||||
val {id, health, x, y, xAxis, yAxis} = enemy
|
val {id, health, x, y, xAxis, yAxis, variant} = enemy
|
||||||
in
|
in
|
||||||
case patch of
|
case patch of
|
||||||
W_HEALTH health => mkEnemy (id, health, x, y, xAxis, yAxis)
|
W_HEALTH health => mkEnemy (id, health, x, y, xAxis, yAxis, variant)
|
||||||
| W_X x => mkEnemy (id, health, x, y, xAxis, yAxis)
|
| W_X x => mkEnemy (id, health, x, y, xAxis, yAxis, variant)
|
||||||
| W_X_AXIS xAxis => mkEnemy (id, health, x, y, xAxis, yAxis)
|
| W_X_AXIS xAxis => mkEnemy (id, health, x, y, xAxis, yAxis, variant)
|
||||||
| W_Y y => mkEnemy (id, health, x, y, xAxis, yAxis)
|
| W_Y y => mkEnemy (id, health, x, y, xAxis, yAxis, variant)
|
||||||
| W_Y_AXIS yAxis => mkEnemy (id, health, x, y, xAxis, yAxis)
|
| W_Y_AXIS yAxis => mkEnemy (id, health, x, y, xAxis, yAxis, variant)
|
||||||
end
|
end
|
||||||
|
|
||||||
fun withPatches (enemy, lst) =
|
fun withPatches (enemy, lst) =
|
||||||
|
|||||||
6
fcore/enemy-variants.sml
Normal file
6
fcore/enemy-variants.sml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
signature ENEMY_VARIANTS =
|
||||||
|
sig
|
||||||
|
datatype t = PATROL_SLIME
|
||||||
|
end
|
||||||
|
|
||||||
|
structure EnemyVariants: ENEMY_VARIANTS = struct datatype t = PATROL_SLIME end
|
||||||
146
fcore/enemy.sml
146
fcore/enemy.sml
@@ -12,122 +12,19 @@ struct
|
|||||||
|
|
||||||
fun exists (id, collisions) = helpExists (0, id, collisions)
|
fun exists (id, collisions) = helpExists (0, id, collisions)
|
||||||
|
|
||||||
fun canWalkAhead (x, y, wallTree, platformTree) =
|
|
||||||
let
|
|
||||||
val ww = Constants.worldWidth
|
|
||||||
val wh = Constants.worldHeight
|
|
||||||
|
|
||||||
val searchWidth = Constants.enemySize
|
|
||||||
|
|
||||||
val y = y + Constants.enemySize - 5
|
|
||||||
val searchHeight = 10
|
|
||||||
in
|
|
||||||
QuadTree.hasCollisionAt
|
|
||||||
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, wallTree)
|
|
||||||
orelse
|
|
||||||
QuadTree.hasCollisionAt
|
|
||||||
(x, y, searchWidth, searchHeight, 0, 0, ww, wh, ~1, platformTree)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
val searchWidth = Constants.enemySize
|
|
||||||
val searchHeight = Constants.enemySize - 5
|
|
||||||
|
|
||||||
val ww = Constants.worldWidth
|
|
||||||
val wh = Constants.worldHeight
|
|
||||||
|
|
||||||
val hasWallAhead = QuadTree.hasCollisionAt
|
|
||||||
( searchStartX
|
|
||||||
, y
|
|
||||||
, searchWidth
|
|
||||||
, searchHeight
|
|
||||||
, 0
|
|
||||||
, 0
|
|
||||||
, ww
|
|
||||||
, wh
|
|
||||||
, ~1
|
|
||||||
, wallTree
|
|
||||||
)
|
|
||||||
in
|
|
||||||
if
|
|
||||||
hasWallAhead
|
|
||||||
then EnemyPatch.W_X_AXIS MOVE_RIGHT :: acc
|
|
||||||
else (* invert direction if moving further left
|
|
||||||
* will result in falling down *) if
|
|
||||||
canWalkAhead (searchStartX, y, wallTree, platformTree)
|
|
||||||
then acc
|
|
||||||
else 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.enemySize
|
|
||||||
val searchHeight = Constants.enemySize - 5
|
|
||||||
|
|
||||||
val ww = Constants.worldWidth
|
|
||||||
val wh = Constants.worldHeight
|
|
||||||
|
|
||||||
val hasWallAhead = QuadTree.hasCollisionAt
|
|
||||||
( searchStartX
|
|
||||||
, y
|
|
||||||
, searchWidth
|
|
||||||
, searchHeight
|
|
||||||
, 0
|
|
||||||
, 0
|
|
||||||
, ww
|
|
||||||
, wh
|
|
||||||
, ~1
|
|
||||||
, wallTree
|
|
||||||
)
|
|
||||||
in
|
|
||||||
if
|
|
||||||
hasWallAhead
|
|
||||||
then EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
||||||
else (* invert direction if moving further right
|
|
||||||
* will result in falling down *) if
|
|
||||||
canWalkAhead (searchStartX, y, wallTree, platformTree)
|
|
||||||
then acc
|
|
||||||
else EnemyPatch.W_X_AXIS MOVE_LEFT :: acc
|
|
||||||
end
|
|
||||||
| STAY_STILL => acc
|
|
||||||
end
|
|
||||||
|
|
||||||
(* called when filtering enemies,
|
(* called when filtering enemies,
|
||||||
* to adjust enemy data on collision with projectile *)
|
* to adjust enemy data on collision with projectile *)
|
||||||
fun onCollisionWithProjectile
|
fun onCollisionWithProjectile
|
||||||
(enemy, projectileTree, acc, walls, wallTree, platforms, platformTree) =
|
( enemy: enemy
|
||||||
|
, projectileTree
|
||||||
|
, acc
|
||||||
|
, walls
|
||||||
|
, wallTree
|
||||||
|
, platforms
|
||||||
|
, platformTree
|
||||||
|
) =
|
||||||
let
|
let
|
||||||
val {x, y, health, id, xAxis, yAxis} = enemy
|
val {x, y, health, ...} = enemy
|
||||||
|
|
||||||
val size = Constants.enemySize
|
val size = Constants.enemySize
|
||||||
val ww = Constants.worldWidth
|
val ww = Constants.worldWidth
|
||||||
@@ -151,8 +48,10 @@ struct
|
|||||||
|
|
||||||
val patches = EnemyPhysics.getEnvironmentPatches
|
val patches = EnemyPhysics.getEnvironmentPatches
|
||||||
(enemy, walls, wallTree, platforms, platformTree)
|
(enemy, walls, wallTree, platforms, platformTree)
|
||||||
val patches =
|
|
||||||
getPatrollPatches (enemy, wallTree, platformTree, patches)
|
(* get patches specific to this type of enemy *)
|
||||||
|
val patches = EnemyBehaviour.getVariantPatches
|
||||||
|
(enemy, walls, wallTree, platforms, platformTree, patches)
|
||||||
|
|
||||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||||
in
|
in
|
||||||
@@ -167,8 +66,10 @@ struct
|
|||||||
|
|
||||||
val patches = EnemyPhysics.getEnvironmentPatches
|
val patches = EnemyPhysics.getEnvironmentPatches
|
||||||
(enemy, walls, wallTree, platforms, platformTree)
|
(enemy, walls, wallTree, platforms, platformTree)
|
||||||
val patches =
|
|
||||||
getPatrollPatches (enemy, wallTree, platformTree, patches)
|
(* get patches specific to this type of enemy *)
|
||||||
|
val patches = EnemyBehaviour.getVariantPatches
|
||||||
|
(enemy, walls, wallTree, platforms, platformTree, patches)
|
||||||
|
|
||||||
val enemy = EnemyPatch.withPatches (enemy, patches)
|
val enemy = EnemyPatch.withPatches (enemy, patches)
|
||||||
in
|
in
|
||||||
@@ -253,13 +154,12 @@ struct
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
fun helpGenerateTree (pos, enemyVec, acc) =
|
fun helpGenerateTree (pos, enemyVec: enemy vector, acc) =
|
||||||
if pos = Vector.length enemyVec then
|
if pos = Vector.length enemyVec then
|
||||||
acc
|
acc
|
||||||
else
|
else
|
||||||
let
|
let
|
||||||
val {id, x, y, health = _, xAxis = _, yAxis = _} =
|
val {id, x, y, ...} = Vector.sub (enemyVec, pos)
|
||||||
Vector.sub (enemyVec, pos)
|
|
||||||
|
|
||||||
val size = Constants.enemySize
|
val size = Constants.enemySize
|
||||||
val ww = Constants.worldWidth
|
val ww = Constants.worldWidth
|
||||||
@@ -272,12 +172,12 @@ struct
|
|||||||
|
|
||||||
fun generateTree enemyVec = helpGenerateTree (0, enemyVec, QuadTree.empty)
|
fun generateTree enemyVec = helpGenerateTree (0, enemyVec, QuadTree.empty)
|
||||||
|
|
||||||
fun helpFind (findNum, vec, low, high) =
|
fun helpFind (findNum, vec: enemy vector, low, high) =
|
||||||
(* should only be called when we know enemy already exists in vec *)
|
(* should only be called when we know enemy already exists in vec *)
|
||||||
let
|
let
|
||||||
val mid = low + ((high - low) div 2)
|
val mid = low + ((high - low) div 2)
|
||||||
val enemy = Vector.sub (vec, mid)
|
val enemy = Vector.sub (vec, mid)
|
||||||
val {id = curNum, x = _, y = _, health = _, xAxis = _, yAxis = _} = enemy
|
val {id = curNum, ...} = enemy
|
||||||
in
|
in
|
||||||
if curNum = findNum then enemy
|
if curNum = findNum then enemy
|
||||||
else if curNum < findNum then helpFind (findNum, vec, mid + 1, high)
|
else if curNum < findNum then helpFind (findNum, vec, mid + 1, high)
|
||||||
@@ -287,9 +187,9 @@ struct
|
|||||||
fun find (findNum, vec) =
|
fun find (findNum, vec) =
|
||||||
helpFind (findNum, vec, 0, Vector.length vec - 1)
|
helpFind (findNum, vec, 0, Vector.length vec - 1)
|
||||||
|
|
||||||
fun helpGetDrawVec (enemy, width, height) =
|
fun helpGetDrawVec (enemy: enemy, width, height) =
|
||||||
let
|
let
|
||||||
val {x, y, id = _, health = _, xAxis = _, yAxis = _} = enemy
|
val {x, y, ...} = enemy
|
||||||
val wratio = width / Constants.worldWidthReal
|
val wratio = width / Constants.worldWidthReal
|
||||||
val hratio = height / Constants.worldHeightReal
|
val hratio = height / Constants.worldHeightReal
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -47,7 +47,14 @@ sig
|
|||||||
}
|
}
|
||||||
|
|
||||||
type enemy =
|
type enemy =
|
||||||
{id: int, health: int, x: int, y: int, xAxis: x_axis, yAxis: y_axis}
|
{ id: int
|
||||||
|
, health: int
|
||||||
|
, x: int
|
||||||
|
, y: int
|
||||||
|
, xAxis: x_axis
|
||||||
|
, yAxis: y_axis
|
||||||
|
, variant: EnemyVariants.t
|
||||||
|
}
|
||||||
|
|
||||||
type game_type =
|
type game_type =
|
||||||
{ player: player
|
{ player: player
|
||||||
@@ -111,7 +118,14 @@ struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
type enemy =
|
type enemy =
|
||||||
{id: int, health: int, x: int, y: int, xAxis: x_axis, yAxis: y_axis}
|
{ id: int
|
||||||
|
, health: int
|
||||||
|
, x: int
|
||||||
|
, y: int
|
||||||
|
, xAxis: x_axis
|
||||||
|
, yAxis: y_axis
|
||||||
|
, variant: EnemyVariants.t
|
||||||
|
}
|
||||||
|
|
||||||
type game_type =
|
type game_type =
|
||||||
{ player: player
|
{ player: player
|
||||||
@@ -158,6 +172,7 @@ struct
|
|||||||
, health = 1
|
, health = 1
|
||||||
, xAxis = MOVE_LEFT
|
, xAxis = MOVE_LEFT
|
||||||
, yAxis = FALLING
|
, yAxis = FALLING
|
||||||
|
, variant = EnemyVariants.PATROL_SLIME
|
||||||
}
|
}
|
||||||
val enemy2 =
|
val enemy2 =
|
||||||
{ id = 2
|
{ id = 2
|
||||||
@@ -166,6 +181,7 @@ struct
|
|||||||
, health = 1
|
, health = 1
|
||||||
, xAxis = MOVE_LEFT
|
, xAxis = MOVE_LEFT
|
||||||
, yAxis = FALLING
|
, yAxis = FALLING
|
||||||
|
, variant = EnemyVariants.PATROL_SLIME
|
||||||
}
|
}
|
||||||
val enemy3 =
|
val enemy3 =
|
||||||
{ id = 3
|
{ id = 3
|
||||||
@@ -174,6 +190,7 @@ struct
|
|||||||
, health = 1
|
, health = 1
|
||||||
, xAxis = MOVE_RIGHT
|
, xAxis = MOVE_RIGHT
|
||||||
, yAxis = FALLING
|
, yAxis = FALLING
|
||||||
|
, variant = EnemyVariants.PATROL_SLIME
|
||||||
}
|
}
|
||||||
val enemies = Vector.fromList [enemy1, enemy2, enemy3]
|
val enemies = Vector.fromList [enemy1, enemy2, enemy3]
|
||||||
in
|
in
|
||||||
|
|||||||
2
oms.mlb
2
oms.mlb
@@ -14,12 +14,14 @@ end
|
|||||||
|
|
||||||
fcore/wall.sml
|
fcore/wall.sml
|
||||||
fcore/platform.sml
|
fcore/platform.sml
|
||||||
|
fcore/enemy-variants.sml
|
||||||
fcore/game-type.sml
|
fcore/game-type.sml
|
||||||
|
|
||||||
fcore/player-patch.sml
|
fcore/player-patch.sml
|
||||||
fcore/enemy-patch.sml
|
fcore/enemy-patch.sml
|
||||||
fcore/physics.sml
|
fcore/physics.sml
|
||||||
|
|
||||||
|
fcore/enemy-behaviour.sml
|
||||||
fcore/enemy.sml
|
fcore/enemy.sml
|
||||||
fcore/player.sml
|
fcore/player.sml
|
||||||
fcore/projectile.sml
|
fcore/projectile.sml
|
||||||
|
|||||||
Reference in New Issue
Block a user