diff --git a/.gitignore b/.gitignore index 8de8fb06..c852a9f9 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,5 @@ /lib/ */target/ */build/ -**/build/ \ No newline at end of file +**/build/ +**/out/ \ No newline at end of file diff --git a/game/plugin/navigation/door/build.gradle b/game/plugin/navigation/door/build.gradle new file mode 100644 index 00000000..2d62d81f --- /dev/null +++ b/game/plugin/navigation/door/build.gradle @@ -0,0 +1,9 @@ +plugin { + name = "door" + packageName = "org.apollo.game.plugin.navigation.door" + dependencies = ["api"] + authors = [ + "Shiver", + "Arin" + ] +} \ No newline at end of file diff --git a/game/plugin/navigation/door/src/door.kt b/game/plugin/navigation/door/src/door.kt new file mode 100644 index 00000000..b3369cad --- /dev/null +++ b/game/plugin/navigation/door/src/door.kt @@ -0,0 +1,165 @@ +package org.apollo.plugin.navigation.door + +import org.apollo.game.action.DistancedAction +import org.apollo.game.model.Direction +import org.apollo.game.model.Position +import org.apollo.game.model.World +import org.apollo.game.model.entity.Player +import org.apollo.game.model.entity.obj.DynamicGameObject +import org.apollo.game.model.entity.obj.GameObject +import org.apollo.game.model.event.PlayerEvent +import org.apollo.net.message.Message +import java.util.Objects +import findObject + +enum class DoorType { + LEFT, RIGHT, NOT_SUPPORTED +} + +class Door(private val gameObject: GameObject) { + + companion object { + + val LEFT_HINGE_ORIENTATION: HashMap = hashMapOf( + Direction.NORTH to Direction.WEST, + Direction.SOUTH to Direction.EAST, + Direction.WEST to Direction.SOUTH, + Direction.EAST to Direction.NORTH + ) + + val RIGHT_HINGE_ORIENTATION: HashMap = hashMapOf( + Direction.NORTH to Direction.EAST, + Direction.SOUTH to Direction.WEST, + Direction.WEST to Direction.NORTH, + Direction.EAST to Direction.SOUTH + ) + + val toggledDoors: HashMap = hashMapOf() + + val LEFT_HINGED = setOf(1516, 1536, 1533) + + val RIGHT_HINGED = setOf(1519, 1530, 4465, 4467, 3014, 3017, 3018, 3019) + + /** + * Find a given door in the world + * @param world The [World] the door lives in + * @param position The [Position] of the door + * @param objectId The [GameObject] id of the door + */ + fun find(world: World, position: Position, objectId: Int): Door? { + val region = world.regionRepository.fromPosition(position) + val gameObject = region.findObject(position, objectId).orElseGet(null) + return if (gameObject == null) { + null + } else { + Door(gameObject) + } + } + } + + /** + * Returns the supported doors by the system + * See [DoorType] + */ + fun supported(): Boolean { + return type() !== DoorType.NOT_SUPPORTED + } + + + /** + * Computes the given door type by which id exists in + * the supported left and right hinged doors + */ + fun type(): DoorType { + return when { + gameObject.id in LEFT_HINGED -> DoorType.LEFT + gameObject.id in RIGHT_HINGED -> DoorType.RIGHT + else -> DoorType.NOT_SUPPORTED + } + } + + /** + * Toggles a given [GameObject] orientation and position + * Stores the door state in toggleDoors class variable + */ + fun toggle() { + val world = gameObject.world + val regionRepository = world.regionRepository + + regionRepository.fromPosition(gameObject.position).removeEntity(gameObject) + + val originalDoor: GameObject? = toggledDoors[gameObject] + + if (originalDoor == null) { + val position = movePosition() + val orientation: Int = translateDirection()?.toOrientationInteger() ?: gameObject.orientation + + val toggledDoor = DynamicGameObject.createPublic(world, gameObject.id, position, gameObject.type, orientation) + + regionRepository.fromPosition(position).addEntity(toggledDoor) + toggledDoors.put(toggledDoor, gameObject) + } else { + toggledDoors.remove(gameObject) + regionRepository.fromPosition(originalDoor.position).addEntity(originalDoor) + } + + } + + /** + * Calculates the position to move the door based on orientation + */ + private fun movePosition(): Position { + return gameObject.position.step(1, Direction.WNES[gameObject.orientation]) + } + + /** + * Calculates the orientation of the door based on + * if it is right or left hinged door + */ + private fun translateDirection(): Direction? { + val direction = Direction.WNES[gameObject.orientation] + return when(type()) { + DoorType.LEFT -> LEFT_HINGE_ORIENTATION[direction] + DoorType.RIGHT -> RIGHT_HINGE_ORIENTATION[direction] + DoorType.NOT_SUPPORTED -> null + } + } + +} + +class OpenDoorAction(private val player: Player, private val door: Door, position: Position) : DistancedAction(0, true, player, position, DISTANCE) { + + companion object { + + /** + * The distance threshold that must be reached before the door is opened. + */ + const val DISTANCE = 1 + + /** + * Starts a [OpenDoorAction] for the specified [Player], terminating the [Message] that triggered. + */ + fun start(message: Message, player: Player, door: Door, position: Position) { + player.startAction(OpenDoorAction(player, door, position)) + message.terminate() + } + + } + + override fun executeAction() { + if (player.world.submit(OpenDoorEvent(player))) { + player.turnTo(position) + door.toggle() + } + stop() + } + + override fun equals(other: Any?): Boolean { + return other is OpenDoorAction && position == other.position && player == other.player + } + + override fun hashCode(): Int = Objects.hash(position, player) + +} + +class OpenDoorEvent(player: Player) : PlayerEvent(player) diff --git a/game/plugin/navigation/door/src/door.plugin.kts b/game/plugin/navigation/door/src/door.plugin.kts new file mode 100644 index 00000000..d91ceee9 --- /dev/null +++ b/game/plugin/navigation/door/src/door.plugin.kts @@ -0,0 +1,16 @@ + +import org.apollo.game.message.impl.ObjectActionMessage +import org.apollo.plugin.navigation.door.Door +import org.apollo.plugin.navigation.door.OpenDoorAction + +/** + * Hook into the [ObjectActionMessage] and listens for a supported door [GameObject] + */ +on { ObjectActionMessage::class } + .where { option == 1 } + .then { + val door = Door.find(it.world, position, id) ?: return@then + if (door.supported()) { + OpenDoorAction.start(this, it, door, position) + } + } \ No newline at end of file