diff --git a/fcore/enemy-behaviour.sml b/fcore/enemy-behaviour.sml new file mode 100644 index 0000000..7eb4412 --- /dev/null +++ b/fcore/enemy-behaviour.sml @@ -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 diff --git a/fcore/enemy-patch.sml b/fcore/enemy-patch.sml index 5d59e84..6f96f6b 100644 --- a/fcore/enemy-patch.sml +++ b/fcore/enemy-patch.sml @@ -21,19 +21,26 @@ struct | W_X_AXIS of GameType.x_axis | W_Y_AXIS of GameType.y_axis - fun mkEnemy (id, health, x, y, xAxis, yAxis) = - {id = id, health = health, x = x, y = y, xAxis = xAxis, yAxis = yAxis} + fun mkEnemy (id, health, x, y, xAxis, yAxis, variant) = + { id = id + , health = health + , x = x + , y = y + , xAxis = xAxis + , yAxis = yAxis + , variant = variant + } fun withPatch (enemy, patch) = let - val {id, health, x, y, xAxis, yAxis} = enemy + val {id, health, x, y, xAxis, yAxis, variant} = enemy in case patch of - W_HEALTH health => mkEnemy (id, health, x, y, xAxis, yAxis) - | W_X x => mkEnemy (id, health, x, y, xAxis, yAxis) - | W_X_AXIS xAxis => mkEnemy (id, health, x, y, xAxis, yAxis) - | W_Y y => mkEnemy (id, health, x, y, xAxis, yAxis) - | W_Y_AXIS yAxis => 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, variant) + | W_X_AXIS xAxis => mkEnemy (id, health, x, y, xAxis, yAxis, variant) + | W_Y y => mkEnemy (id, health, x, y, xAxis, yAxis, variant) + | W_Y_AXIS yAxis => mkEnemy (id, health, x, y, xAxis, yAxis, variant) end fun withPatches (enemy, lst) = diff --git a/fcore/enemy-variants.sml b/fcore/enemy-variants.sml new file mode 100644 index 0000000..7c60368 --- /dev/null +++ b/fcore/enemy-variants.sml @@ -0,0 +1,6 @@ +signature ENEMY_VARIANTS = +sig + datatype t = PATROL_SLIME +end + +structure EnemyVariants: ENEMY_VARIANTS = struct datatype t = PATROL_SLIME end diff --git a/fcore/enemy.sml b/fcore/enemy.sml index 7f9ea59..048baad 100644 --- a/fcore/enemy.sml +++ b/fcore/enemy.sml @@ -12,122 +12,19 @@ struct 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, * to adjust enemy data on collision with projectile *) fun onCollisionWithProjectile - (enemy, projectileTree, acc, walls, wallTree, platforms, platformTree) = + ( enemy: enemy + , projectileTree + , acc + , walls + , wallTree + , platforms + , platformTree + ) = let - val {x, y, health, id, xAxis, yAxis} = enemy + val {x, y, health, ...} = enemy val size = Constants.enemySize val ww = Constants.worldWidth @@ -151,8 +48,10 @@ struct val patches = EnemyPhysics.getEnvironmentPatches (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) in @@ -167,8 +66,10 @@ struct val patches = EnemyPhysics.getEnvironmentPatches (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) in @@ -253,13 +154,12 @@ struct ) end - fun helpGenerateTree (pos, enemyVec, acc) = + fun helpGenerateTree (pos, enemyVec: enemy vector, acc) = if pos = Vector.length enemyVec then acc else let - val {id, x, y, health = _, xAxis = _, yAxis = _} = - Vector.sub (enemyVec, pos) + val {id, x, y, ...} = Vector.sub (enemyVec, pos) val size = Constants.enemySize val ww = Constants.worldWidth @@ -272,12 +172,12 @@ struct 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 *) let val mid = low + ((high - low) div 2) val enemy = Vector.sub (vec, mid) - val {id = curNum, x = _, y = _, health = _, xAxis = _, yAxis = _} = enemy + val {id = curNum, ...} = enemy in if curNum = findNum then enemy else if curNum < findNum then helpFind (findNum, vec, mid + 1, high) @@ -287,9 +187,9 @@ struct fun find (findNum, vec) = helpFind (findNum, vec, 0, Vector.length vec - 1) - fun helpGetDrawVec (enemy, width, height) = + fun helpGetDrawVec (enemy: enemy, width, height) = let - val {x, y, id = _, health = _, xAxis = _, yAxis = _} = enemy + val {x, y, ...} = enemy val wratio = width / Constants.worldWidthReal val hratio = height / Constants.worldHeightReal in diff --git a/fcore/game-type.sml b/fcore/game-type.sml index 8a24d4a..4e1ccff 100644 --- a/fcore/game-type.sml +++ b/fcore/game-type.sml @@ -47,7 +47,14 @@ sig } 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 = { player: player @@ -111,7 +118,14 @@ struct } 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 = { player: player @@ -158,6 +172,7 @@ struct , health = 1 , xAxis = MOVE_LEFT , yAxis = FALLING + , variant = EnemyVariants.PATROL_SLIME } val enemy2 = { id = 2 @@ -166,6 +181,7 @@ struct , health = 1 , xAxis = MOVE_LEFT , yAxis = FALLING + , variant = EnemyVariants.PATROL_SLIME } val enemy3 = { id = 3 @@ -174,6 +190,7 @@ struct , health = 1 , xAxis = MOVE_RIGHT , yAxis = FALLING + , variant = EnemyVariants.PATROL_SLIME } val enemies = Vector.fromList [enemy1, enemy2, enemy3] in diff --git a/oms.mlb b/oms.mlb index b86b57a..1d36e39 100644 --- a/oms.mlb +++ b/oms.mlb @@ -14,12 +14,14 @@ end fcore/wall.sml fcore/platform.sml +fcore/enemy-variants.sml fcore/game-type.sml fcore/player-patch.sml fcore/enemy-patch.sml fcore/physics.sml +fcore/enemy-behaviour.sml fcore/enemy.sml fcore/player.sml fcore/projectile.sml