From f89946958c7f1ec76852ba3180f19efe8d07f93b Mon Sep 17 00:00:00 2001 From: Steve Soltys Date: Mon, 23 Jul 2018 21:30:21 -0400 Subject: [PATCH 1/3] Add rotation functions to Direction --- .../java/org/apollo/game/model/Direction.java | 90 ++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/game/src/main/java/org/apollo/game/model/Direction.java b/game/src/main/java/org/apollo/game/model/Direction.java index d1a13ffbd..56d98f3a6 100644 --- a/game/src/main/java/org/apollo/game/model/Direction.java +++ b/game/src/main/java/org/apollo/game/model/Direction.java @@ -161,7 +161,95 @@ public static Direction[] diagonalComponents(Direction direction) { } /** - * Gets the opposite direction of the this direction. + * Gets the direction that is clockwise of this direction. + * + * @return The clockwise direction. + */ + public Direction clockwise() { + switch (this) { + case NORTH: + return NORTH_EAST; + case SOUTH: + return SOUTH_WEST; + case EAST: + return SOUTH_EAST; + case WEST: + return NORTH_WEST; + case NORTH_WEST: + return NORTH; + case NORTH_EAST: + return EAST; + case SOUTH_EAST: + return SOUTH; + case SOUTH_WEST: + return WEST; + } + + return NONE; + } + + /** + * Gets a direction that is clockwise of this direction, given the number of times to rotate. + * + * @param count The number of times to apply the rotation. + * @return The rotated direction. + */ + public Direction clockwise(int count) { + Direction direction = this; + + for (int index = 0; index < count; index++) { + direction = direction.clockwise(); + } + + return direction; + } + + /** + * Gets the direction that is counter-clockwise of this direction. + * + * @return The counter-clockwise direction. + */ + public Direction counterClockwise() { + switch (this) { + case NORTH: + return NORTH_WEST; + case SOUTH: + return SOUTH_EAST; + case EAST: + return NORTH_EAST; + case WEST: + return SOUTH_WEST; + case NORTH_WEST: + return WEST; + case NORTH_EAST: + return NORTH; + case SOUTH_EAST: + return EAST; + case SOUTH_WEST: + return SOUTH; + } + + return NONE; + } + + /** + * Gets a direction that is counter-clockwise of this direction, given the number of times to rotate. + * + * @param count The number of times to apply the rotation. + * @return The rotated direction. + */ + public Direction counterClockwise(int count) { + Direction direction = this; + + for (int index = 0; index < count; index++) { + direction = direction.counterClockwise(); + } + + return direction; + } + + /** + * Gets the opposite direction of this direction. * * @return The opposite direction. */ From d578e8d153956fdae3e92fda7d96c6765b7d4e1c Mon Sep 17 00:00:00 2001 From: Steve Soltys Date: Mon, 30 Jul 2018 22:26:30 -0400 Subject: [PATCH 2/3] Add interaction mask decoding for GameObject definitions --- .../decoder/ObjectDefinitionDecoder.java | 2 +- .../apollo/cache/def/ObjectDefinition.java | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/cache/src/main/java/org/apollo/cache/decoder/ObjectDefinitionDecoder.java b/cache/src/main/java/org/apollo/cache/decoder/ObjectDefinitionDecoder.java index 3ffc797a9..8c9917db5 100644 --- a/cache/src/main/java/org/apollo/cache/decoder/ObjectDefinitionDecoder.java +++ b/cache/src/main/java/org/apollo/cache/decoder/ObjectDefinitionDecoder.java @@ -119,7 +119,7 @@ private ObjectDefinition decode(int id, ByteBuffer data) { } else if (opcode == 60 || opcode >= 65 && opcode <= 68) { data.getShort(); } else if (opcode == 69) { - data.get(); + definition.setInteractionMask(data.get()); } else if (opcode >= 70 && opcode <= 72) { data.getShort(); } else if (opcode == 73) { diff --git a/cache/src/main/java/org/apollo/cache/def/ObjectDefinition.java b/cache/src/main/java/org/apollo/cache/def/ObjectDefinition.java index 8f0543076..70d34bee8 100644 --- a/cache/src/main/java/org/apollo/cache/def/ObjectDefinition.java +++ b/cache/src/main/java/org/apollo/cache/def/ObjectDefinition.java @@ -75,6 +75,11 @@ public static ObjectDefinition lookup(int id) { */ private boolean impenetrable = true; + /** + * A mask containing the set of directions from which this object can be interacted with. + */ + private int interactionMask; + /** * Denotes whether this object has actions associated with it or not. */ @@ -137,6 +142,15 @@ public int getId() { return id; } + /** + * Gets the interaction mask for this object. + * + * @return The interaction mask. + */ + public int getInteractionMask() { + return interactionMask; + } + /** * Gets the length of this object. * @@ -236,6 +250,15 @@ public void setInteractive(boolean interactive) { this.interactive = interactive; } + /** + * Sets the interaction mask for this object. + * + * @param interactionMask The interaction mask. + */ + public void setInteractionMask(int interactionMask) { + this.interactionMask = interactionMask; + } + /** * Sets the length of this object. * From 48f0d69fdba4a76e51d532b114c6fd4ca9ec201f Mon Sep 17 00:00:00 2001 From: Steve Soltys Date: Mon, 30 Jul 2018 23:09:01 -0400 Subject: [PATCH 3/3] Add custom EntityBounds implementation for GameObjects --- .../org/apollo/game/model/entity/Entity.java | 2 +- .../game/model/entity/EntityBounds.java | 2 +- .../game/model/entity/obj/GameObject.java | 9 + .../model/entity/obj/GameObjectBounds.java | 176 ++++++++++++++++++ .../game/model/entity/obj/ObjectType.java | 14 ++ 5 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 game/src/main/java/org/apollo/game/model/entity/obj/GameObjectBounds.java diff --git a/game/src/main/java/org/apollo/game/model/entity/Entity.java b/game/src/main/java/org/apollo/game/model/entity/Entity.java index 3f5a955a9..74bcba9cd 100644 --- a/game/src/main/java/org/apollo/game/model/entity/Entity.java +++ b/game/src/main/java/org/apollo/game/model/entity/Entity.java @@ -23,7 +23,7 @@ public abstract class Entity { /** * The EntityBounds for this Entity. */ - private EntityBounds bounds; + protected EntityBounds bounds; /** * Creates the Entity. diff --git a/game/src/main/java/org/apollo/game/model/entity/EntityBounds.java b/game/src/main/java/org/apollo/game/model/entity/EntityBounds.java index 3d4c3a9ae..126a0d79c 100644 --- a/game/src/main/java/org/apollo/game/model/entity/EntityBounds.java +++ b/game/src/main/java/org/apollo/game/model/entity/EntityBounds.java @@ -19,7 +19,7 @@ public class EntityBounds { * * @param entity The entity. */ - EntityBounds(Entity entity) { + protected EntityBounds(Entity entity) { this.entity = entity; } diff --git a/game/src/main/java/org/apollo/game/model/entity/obj/GameObject.java b/game/src/main/java/org/apollo/game/model/entity/obj/GameObject.java index 0d8391bfc..64fbe95ef 100644 --- a/game/src/main/java/org/apollo/game/model/entity/obj/GameObject.java +++ b/game/src/main/java/org/apollo/game/model/entity/obj/GameObject.java @@ -103,6 +103,15 @@ public int getWidth() { getDefinition().getLength() : getDefinition().getWidth(); } + @Override + public GameObjectBounds getBounds() { + if(bounds == null) { + bounds = new GameObjectBounds(this); + } + + return (GameObjectBounds) bounds; + } + @Override public int hashCode() { return packed; diff --git a/game/src/main/java/org/apollo/game/model/entity/obj/GameObjectBounds.java b/game/src/main/java/org/apollo/game/model/entity/obj/GameObjectBounds.java new file mode 100644 index 000000000..40820dab3 --- /dev/null +++ b/game/src/main/java/org/apollo/game/model/entity/obj/GameObjectBounds.java @@ -0,0 +1,176 @@ +package org.apollo.game.model.entity.obj; + +import com.google.common.collect.ImmutableMap; +import org.apollo.game.model.Direction; +import org.apollo.game.model.Position; +import org.apollo.game.model.entity.EntityBounds; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * The bounds of a {@link GameObject}. + * + * @author Steve Soltys + */ +public class GameObjectBounds extends EntityBounds { + + /** + * An {@link ImmutableMap} of Directions mapped to their mask bits for a {@link GameObject} interaction mask. + */ + private static final Map INTERACTION_MASK_BITS = ImmutableMap.builder() + .put(Direction.NORTH, 0x1) + .put(Direction.EAST, 0x8) + .put(Direction.SOUTH, 0x4) + .put(Direction.WEST, 0x2) + .build(); + + /** + * The GameObject. + */ + private final GameObject gameObject; + + /** + * The set of positions where the GameObject can be interacted with. + */ + private Set interactionPositions; + + /** + * Creates an EntityBounds. + * + * @param gameObject The GameObject. + */ + GameObjectBounds(GameObject gameObject) { + super(gameObject); + + this.gameObject = gameObject; + } + + /** + * Gets the set of positions where the GameObject can be interacted with. + * + * @return The set of positions. + */ + public Set getInteractionPositions() { + if (interactionPositions == null) { + interactionPositions = computeInteractionPositions(); + } + + return interactionPositions; + } + + /** + * Computes the set of positions where the GameObject can be interacted with. + * + * @return The set of positions. + */ + private Set computeInteractionPositions() { + + switch (ObjectType.valueOf(gameObject.getType())) { + case DIAGONAL_WALL: + return getDiagonalWallInteractionPositions(); + + case LENGTHWISE_WALL: + return getWallInteractionPositions(); + + case INTERACTABLE: + return getInteractablePositions(); + + // TODO: Figure out the rest. + } + + return Collections.emptySet(); + } + + /** + * Gets the set of positions for a diagonal wall where the GameObject can be interacted with. + * + * @return The set of interaction positions. + */ + private Set getDiagonalWallInteractionPositions() { + Set positions = new HashSet<>(); + Direction direction = Direction.WNES[gameObject.getOrientation()]; + Position position = gameObject.getPosition(); + + // TODO: I think this either is missing one, or has one too many. Need to confirm these directions. + positions.add(position.step(1, direction)); + positions.add(position.step(1, direction.counterClockwise())); + positions.add(position.step(1, direction.counterClockwise(2))); + positions.add(position.step(1, direction.clockwise())); + positions.add(position.step(1, direction.clockwise(2))); + positions.add(position.step(1, direction.opposite())); + positions.add(position.step(1, direction.opposite().counterClockwise())); + return positions; + } + + /** + * Gets the set of Directions from which the GameObject can be interacted with. + * + * @return The interactable directions. + */ + private Set getInteractableDirections() { + Set interactableDirections = new HashSet<>(); + + int interactionMask = gameObject.getDefinition().getInteractionMask(); + int rotatedInteractionMask = (interactionMask << gameObject.getOrientation() & 0xf) + + (interactionMask >> 4 - gameObject.getOrientation()); + + for (Direction direction : Direction.NESW) { + boolean interactive = (rotatedInteractionMask & INTERACTION_MASK_BITS.get(direction)) == 0; + + if (interactive) { + interactableDirections.add(direction); + } + } + + return interactableDirections; + } + + /** + * Gets the set of positions where the GameObject can be interacted with. + * + * @return The set of interaction positions. + */ + private Set getInteractablePositions() { + Set interactableDirections = getInteractableDirections(); + Set interactablePositions = new HashSet<>(); + + Position position = gameObject.getPosition(); + int width = gameObject.getWidth(); + int length = gameObject.getLength(); + + for (int deltaX = position.getX(); deltaX < position.getX() + width; deltaX++) { + for (int deltaY = position.getY(); deltaY < position.getY() + length; deltaY++) { + + for(Direction direction : interactableDirections) { + Position deltaPosition = new Position(deltaX, deltaY, position.getHeight()).step(1, direction); + + if(!contains(deltaPosition)) { + interactablePositions.add(deltaPosition); + } + } + } + } + + return interactablePositions; + } + + /** + * Gets the set of positions for a wall where the GameObject can be interacted with. + * + * @return The set of interaction positions. + */ + private Set getWallInteractionPositions() { + Set positions = new HashSet<>(); + Direction objectDirection = Direction.WNES[gameObject.getOrientation()]; + Position position = gameObject.getPosition(); + + positions.add(position); + positions.add(position.step(1, objectDirection)); + positions.add(position.step(1, objectDirection.clockwise(2))); + positions.add(position.step(1, objectDirection.counterClockwise(2))); + return positions; + } +} diff --git a/game/src/main/java/org/apollo/game/model/entity/obj/ObjectType.java b/game/src/main/java/org/apollo/game/model/entity/obj/ObjectType.java index 9f62b3336..fccf8c52d 100644 --- a/game/src/main/java/org/apollo/game/model/entity/obj/ObjectType.java +++ b/game/src/main/java/org/apollo/game/model/entity/obj/ObjectType.java @@ -1,5 +1,7 @@ package org.apollo.game.model.entity.obj; +import java.util.Arrays; + /** * The type of an object, which affects specified behaviour (such as whether it displaces existing objects). TODO * complete this... @@ -70,6 +72,18 @@ public enum ObjectType { this.group = group; } + /** + * Gets the type with the specified value. + * + * @param value The value. + * @return The type. + */ + public static ObjectType valueOf(int value) { + return Arrays.stream(values()) + .filter(type -> type.value == value) + .findAny().orElse(null); + } + /** * Gets the {@link ObjectGroup} of this ObjectType. *