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
diff --git a/src/org/apollo/game/message/impl/RemoveObjectMessage.java b/src/org/apollo/game/message/impl/RemoveObjectMessage.java
new file mode 100644
index 00000000..910b96fd
--- /dev/null
+++ b/src/org/apollo/game/message/impl/RemoveObjectMessage.java
@@ -0,0 +1,76 @@
+package org.apollo.game.message.impl;
+
+import org.apollo.game.message.Message;
+import org.apollo.game.model.entity.GameObject;
+
+/**
+ * A {@link Message} sent to the client to remove an object from a tile.
+ *
+ * @author Major
+ */
+public final class RemoveObjectMessage extends Message {
+
+ /**
+ * The orientation of the object.
+ */
+ private final int orientation;
+
+ /**
+ * The position of the object.
+ */
+ private final int positionOffset;
+
+ /**
+ * The type of the object.
+ */
+ private final int type;
+
+ /**
+ * Creates the RemoveObjectMessage.
+ *
+ * @param object The {@link GameObject} to send.
+ */
+ public RemoveObjectMessage(GameObject object) {
+ this(object, 0);
+ }
+
+ /**
+ * Creates the RemoveObjectMessage.
+ *
+ * @param object The {@link GameObject} to send.
+ * @param positionOffset The offset of the object's position from the sector's central position.
+ */
+ public RemoveObjectMessage(GameObject object, int positionOffset) {
+ this.positionOffset = positionOffset;
+ this.type = object.getType();
+ this.orientation = object.getOrientation();
+ }
+
+ /**
+ * Gets the orientation of the object.
+ *
+ * @return The orientation.
+ */
+ public int getOrientation() {
+ return orientation;
+ }
+
+ /**
+ * Gets the position offset of the object.
+ *
+ * @return The position offset.
+ */
+ public int getPositionOffset() {
+ return positionOffset;
+ }
+
+ /**
+ * Gets the orientation of the object.
+ *
+ * @return The type.
+ */
+ public int getType() {
+ return type;
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/game/message/impl/SendObjectMessage.java b/src/org/apollo/game/message/impl/SendObjectMessage.java
index da40741d..02c4b216 100644
--- a/src/org/apollo/game/message/impl/SendObjectMessage.java
+++ b/src/org/apollo/game/message/impl/SendObjectMessage.java
@@ -31,15 +31,25 @@ public final class SendObjectMessage extends Message {
private final int type;
/**
- * Creates the send object message.
+ * Creates the SendObjectMessage.
*
* @param object The {@link GameObject} to send.
*/
public SendObjectMessage(GameObject object) {
+ this(object, 0);
+ }
+
+ /**
+ * Creates the SendObjectMessage.
+ *
+ * @param object The {@link GameObject} to send.
+ * @param positionOffset The offset of the object's position from the sector's central position.
+ */
+ public SendObjectMessage(GameObject object, int positionOffset) {
this.id = object.getId();
- this.positionOffset = 0;
+ this.positionOffset = positionOffset;
this.type = object.getType();
- this.orientation = object.getRotation();
+ this.orientation = object.getOrientation();
}
/**
diff --git a/src/org/apollo/game/model/entity/GameObject.java b/src/org/apollo/game/model/entity/GameObject.java
index 8592c7a1..958383cd 100644
--- a/src/org/apollo/game/model/entity/GameObject.java
+++ b/src/org/apollo/game/model/entity/GameObject.java
@@ -28,7 +28,7 @@ public final class GameObject extends Entity {
*/
public GameObject(int id, Position position, int type, int orientation) {
super(position);
- this.packed = id << 8 | type << 2 | orientation;
+ this.packed = id << 8 | (type & 0x3F) << 2 | orientation & 0x3;
}
@Override
@@ -69,7 +69,7 @@ public final class GameObject extends Entity {
*
* @return The orientation.
*/
- public int getRotation() {
+ public int getOrientation() {
return packed & 0x3;
}
@@ -89,7 +89,8 @@ public final class GameObject extends Entity {
@Override
public String toString() {
- return MoreObjects.toStringHelper(this).add("id", getId()).add("type", getType()).add("rotation", getRotation()).toString();
+ return MoreObjects.toStringHelper(this).add("id", getId()).add("type", getType()).add("orientation", getOrientation())
+ .toString();
}
}
\ No newline at end of file
diff --git a/src/org/apollo/net/release/r317/Release317.java b/src/org/apollo/net/release/r317/Release317.java
index 39c77ab4..54d16065 100644
--- a/src/org/apollo/net/release/r317/Release317.java
+++ b/src/org/apollo/net/release/r317/Release317.java
@@ -24,6 +24,7 @@ import org.apollo.game.message.impl.OpenSidebarMessage;
import org.apollo.game.message.impl.PlayerSynchronizationMessage;
import org.apollo.game.message.impl.PositionMessage;
import org.apollo.game.message.impl.PrivacyOptionMessage;
+import org.apollo.game.message.impl.RemoveObjectMessage;
import org.apollo.game.message.impl.RemoveTileItemMessage;
import org.apollo.game.message.impl.SectorChangeMessage;
import org.apollo.game.message.impl.SendFriendMessage;
@@ -206,6 +207,7 @@ public final class Release317 extends Release {
register(UpdateTileItemMessage.class, new UpdateTileItemMessageEncoder());
register(RemoveTileItemMessage.class, new RemoveTileItemMessageEncoder());
register(SendObjectMessage.class, new SendObjectMessageEncoder());
+ register(RemoveObjectMessage.class, new RemoveObjectMessageEncoder());
register(ForwardPrivateChatMessage.class, new ForwardPrivateChatMessageEncoder());
register(FriendServerStatusMessage.class, new FriendServerStatusMessageEncoder());
diff --git a/src/org/apollo/net/release/r317/RemoveObjectMessageEncoder.java b/src/org/apollo/net/release/r317/RemoveObjectMessageEncoder.java
new file mode 100644
index 00000000..bf86a1a9
--- /dev/null
+++ b/src/org/apollo/net/release/r317/RemoveObjectMessageEncoder.java
@@ -0,0 +1,25 @@
+package org.apollo.net.release.r317;
+
+import org.apollo.game.message.impl.RemoveObjectMessage;
+import org.apollo.net.codec.game.DataTransformation;
+import org.apollo.net.codec.game.DataType;
+import org.apollo.net.codec.game.GamePacket;
+import org.apollo.net.codec.game.GamePacketBuilder;
+import org.apollo.net.release.MessageEncoder;
+
+/**
+ * A {@link MessageEncoder} for the {@link RemoveObjectMessage}.
+ *
+ * @author Major
+ */
+public final class RemoveObjectMessageEncoder extends MessageEncoder {
+
+ @Override
+ public GamePacket encode(RemoveObjectMessage message) {
+ GamePacketBuilder builder = new GamePacketBuilder(101);
+ builder.put(DataType.BYTE, DataTransformation.NEGATE, message.getType() << 2 | message.getOrientation());
+ builder.put(DataType.BYTE, message.getPositionOffset());
+ return builder.toGamePacket();
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/net/release/r317/SendObjectMessageEncoder.java b/src/org/apollo/net/release/r317/SendObjectMessageEncoder.java
index 67cf38c6..4a815aee 100644
--- a/src/org/apollo/net/release/r317/SendObjectMessageEncoder.java
+++ b/src/org/apollo/net/release/r317/SendObjectMessageEncoder.java
@@ -20,7 +20,7 @@ public final class SendObjectMessageEncoder extends MessageEncoder {
+
+ @Override
+ public GamePacket encode(RemoveObjectMessage message) {
+ GamePacketBuilder builder = new GamePacketBuilder(88);
+ builder.put(DataType.BYTE, DataTransformation.SUBTRACT, message.getPositionOffset());
+ builder.put(DataType.BYTE, DataTransformation.SUBTRACT, message.getType() << 2 | message.getOrientation());
+ return builder.toGamePacket();
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/apollo/net/release/r377/SendObjectMessageEncoder.java b/src/org/apollo/net/release/r377/SendObjectMessageEncoder.java
index 533534c3..69d7e1a8 100644
--- a/src/org/apollo/net/release/r377/SendObjectMessageEncoder.java
+++ b/src/org/apollo/net/release/r377/SendObjectMessageEncoder.java
@@ -18,7 +18,7 @@ public final class SendObjectMessageEncoder extends MessageEncoder {
return new WalkMessage(positions, run);
}
-}
+}
\ No newline at end of file