diff --git a/data/plugins/navigation/door/constants.rb b/data/plugins/navigation/door/constants.rb new file mode 100644 index 00000000..ec923218 --- /dev/null +++ b/data/plugins/navigation/door/constants.rb @@ -0,0 +1,38 @@ +java_import 'org.apollo.game.model.Direction' + +module DoorConstants + # TODO: GameObjectOrientation enumeration in Apollo's core? + module Orientation + WEST = 0 + NORTH = 1 + EAST = 2 + SOUTH = 3 + end + + # The object size of a door. + DOOR_SIZE = 1 + + # Door object ids that have a hinge on the left side. + LEFT_SIDE_HINGE_DOOR_IDS = [1516, 1536, 1533] + + # Door object ids that have a hinge on the right side. + RIGHT_SIDE_HINGE_DOOR_IDS = [1519, 1530, 4465, 4467, 3014, 3017, 3018, 3019] + + # A map of orientations that a door will translate to when opened. + ORIENTATIONS = { + # Orientations for doors that have a hinge on the left side. + :left_side_hinge => { + Orientation::NORTH => Orientation::WEST, + Orientation::SOUTH => Orientation::EAST, + Orientation::WEST => Orientation::SOUTH, + Orientation::EAST => Orientation::NORTH + }, + # Orientations for doors that have a hinge on the right side. + :right_side_hinge => { + Orientation::NORTH => Orientation::EAST, + Orientation::SOUTH => Orientation::WEST, + Orientation::WEST => Orientation::NORTH, + Orientation::EAST => Orientation::SOUTH + } + } +end \ No newline at end of file diff --git a/data/plugins/navigation/door/door.rb b/data/plugins/navigation/door/door.rb new file mode 100644 index 00000000..7ee1af49 --- /dev/null +++ b/data/plugins/navigation/door/door.rb @@ -0,0 +1,31 @@ +java_import 'org.apollo.game.action.DistancedAction' + +# A distanced action which opens a door. +class OpenDoorAction < DistancedAction + include DoorConstants + + attr_reader :door_object + + def initialize(mob, door_object) + super(0, true, mob, door_object.position, DOOR_SIZE) + @door_object = door_object + end + + def executeAction + mob.turn_to @door_object.position + DoorUtil::toggle(@door_object, mob) + stop + end + + def equals(other) + return (get_class == other.get_class && @position == other.position) + end +end + +# Message handler for opening and closing doors. +on :message, :first_object_action do |ctx, player, message| + if DoorUtil::is_door?(message.id) + door_object = DoorUtil::get_door_object(message.position, message.id) + player.start_action(OpenDoorAction.new(player, door_object)) unless door_object.nil? + end +end \ No newline at end of file diff --git a/data/plugins/navigation/door/plugin.xml b/data/plugins/navigation/door/plugin.xml new file mode 100644 index 00000000..4b5f435f --- /dev/null +++ b/data/plugins/navigation/door/plugin.xml @@ -0,0 +1,16 @@ + + + door + 1 + Doors + Adds support for doors throughout the game. + + Shiver + + + + + + + + \ No newline at end of file diff --git a/data/plugins/navigation/door/util.rb b/data/plugins/navigation/door/util.rb new file mode 100644 index 00000000..33d35725 --- /dev/null +++ b/data/plugins/navigation/door/util.rb @@ -0,0 +1,99 @@ +java_import 'org.apollo.game.model.entity.GameObject' +java_import 'org.apollo.game.model.entity.Entity' +java_import 'org.apollo.game.model.Position' +java_import 'org.apollo.game.message.impl.PositionMessage' +java_import 'org.apollo.game.message.impl.RemoveObjectMessage' +java_import 'org.apollo.game.message.impl.SendObjectMessage' + +module DoorUtil + include DoorConstants + + # A hash containing currently toggled door objects mapped to the original door objects. + TOGGLED_DOOR_REPOSITORY = {} + + # Translates a door's position in the direction of its orientation. + def self.translate_door_position(door) + position = door.position + orientation = door.orientation + case orientation + when Orientation::WEST + return Position.new(position.x - 1, position.y, position.height) + when Orientation::EAST + return Position.new(position.x + 1, position.y, position.height) + when Orientation::NORTH + return Position.new(position.x, position.y + 1, position.height) + when Orientation::SOUTH + return Position.new(position.x, position.y - 1, position.height) + end + raise 'Invalid orientation for door!' + end + + # Translates the orientation of a door to a toggled position. + def self.translate_door_orientation(door) + object_id = door.id + orientation = door.orientation + if RIGHT_SIDE_HINGE_DOOR_IDS.include?(object_id) + return ORIENTATIONS[:right_side_hinge][orientation] + elsif LEFT_SIDE_HINGE_DOOR_IDS.include?(object_id) + return ORIENTATIONS[:left_side_hinge][orientation] + end + raise 'Given object was not registered as a door!' + end + + # Replaces a door object for a given player. + # TODO: This is temporary. + def self.replace_door(player, original, new) + player.send PositionMessage.new(player.last_known_sector, original.position) + player.send RemoveObjectMessage.new(original) + player.send PositionMessage.new(player.last_known_sector, new.position) + player.send SendObjectMessage.new(new) + end + + # Toggles the given door. + def self.toggle(door, player) + # First, we remove the door we're toggling (or un-toggling) from the game world. + position = door.position + sector = $world.sector_repository.from_position(position) + sector.remove_entity door + + # Have we toggled this door already? + if TOGGLED_DOOR_REPOSITORY.include?(door) + # If we have, we get our original door. This also deletes the entry from our repository. + original_door = TOGGLED_DOOR_REPOSITORY.delete(door) + + # Now we add our new door to the game world. + original_sector = $world.sector_repository.from_position(original_door.position) + original_sector.add_entity original_door + + # TODO: This and the 'player' parameter are temporary. We still need to synchronize objects for local players. + replace_door player, door, original_door + else + # If not, we get the translated orientation and position for this door, and create a new game object. + toggled_position = translate_door_position(door) + toggled_orientation = translate_door_orientation(door) + toggled_door = GameObject.new(door.id, toggled_position, door.type, toggled_orientation) + + # Now we add our new door to the game world. + toggled_sector = $world.sector_repository.from_position(toggled_position) + toggled_sector.add_entity toggled_door + + # Insert our toggled door in the repository. + TOGGLED_DOOR_REPOSITORY[toggled_door] = door + + # TODO: This and the 'player' parameter are temporary. We still need to synchronize objects for local players. + replace_door player, door, toggled_door + end + end + + # Gets the door object at the given position, if it exists. + def self.get_door_object(position, object_id) + game_objects = $world.sector_repository.from_position(position).get_entities(position, EntityType::GAME_OBJECT) + game_objects.each { |game_object| return game_object if game_object.get_id == object_id } + return nil + end + + # Checks if the given game object id is a door. + def self.is_door?(object_id) + RIGHT_SIDE_HINGE_DOOR_IDS.include?(object_id) || LEFT_SIDE_HINGE_DOOR_IDS.include?(object_id) + end +end \ No newline at end of file