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