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..2912e6804 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 @@ -26,6 +26,11 @@ public abstract class GameObject extends Entity implements GroupableEntity { */ private final int packed; + /** + * The bounds of this GameObject. + */ + private GameObjectBounds bounds; + /** * Creates the GameObject. * @@ -103,6 +108,15 @@ public int getWidth() { getDefinition().getLength() : getDefinition().getWidth(); } + @Override + public GameObjectBounds getBounds() { + if(bounds == null) { + bounds = new GameObjectBounds(this); + } + + return 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. *