/** The movements of the Enemy are very predictable. There is no randomness. The get "stuck" in a horizontal tunnel if Player is directly in line with the cul-de-sac. They do NOT get stuck vertically. Enemies like to change direction. So if there is a long straight path, with a T junction, they will always make the turn. */ class Enemy : Item { var direction = -1 var allowReverse = true override fun isSolid() = false override fun createAction() : Action { // The speed of the Enemy is ever so slightly slower than Player (0.11 vs 0.1) // TODO We could make this a setting, and have an "easy" and "hard" version. return (this:>chooseMovement.once() then Delay( 0.11 )).forever() and flash() } fun chooseMovement() { val player = Play.instance.player var newDir = direction // If we are heading directly towards or away from the player, then do not try left or right if (player.actor.y == actor.y && direction % 2 == 0) { // When heading into an East/West cul-de-sac, then get stuck. This makes it easy to trap // the nasties, but can also make life tricky for the player in some circumstances. allowReverse = false newDir = if (player.actor.x > actor.x) 0 else 2 } else if (player.actor.x == actor.x && direction % 2 == 1) { newDir = if (player.actor.y > actor.y) 1 else 3 } else { // Try not to move forwards again, so look LEFT first newDir = (direction + 1) % 4 // If we are heading away, then try RIGHT first if (headingAway( newDir )) { newDir = (newDir + 2) % 4 } if (! canMove( newDir ) ) { // Couldn't turn one way, so lets try turning the other way. // This will be away from the player, but not in a straight line. newDir = (newDir + 2) % 4 if (! canMove( newDir ) ) { // We couldn't turn left, or right, so lets try straight ahead (keep direction unchanged). if ( canMove( direction ) ) { newDir = direction } else { if (allowReverse) { newDir = (direction + 2) % 4 } } } } if (canMove( newDir )) { allowReverse = true } } if (canMove( newDir )) { direction = newDir move( direction ) } } override fun move( direction : int ) { val there = look( direction ) if (there is Player) { (there as Player).kill() } if (Play.instance.mainView.visible( actor ) ) { actor.event( "move" + direction ) } super.move(direction) } fun headingAway( direction : int ) : bool { val player = Play.instance.player val dx = getDeltaX( direction ) val dy = getDeltaY( direction ) val tx = player.actor.x - actor.x val ty = player.actor.y - actor.y if (dx * tx > 0) return false if (dy * ty > 0) return false return true } fun canMove( direction : int ) = look( direction ).isEmpty() }