Skip to content

Commit eca25f1

Browse files
committed
Add custom EntityBounds implementation for GameObjects
1 parent d578e8d commit eca25f1

File tree

4 files changed

+205
-1
lines changed

4 files changed

+205
-1
lines changed

game/src/main/java/org/apollo/game/model/entity/EntityBounds.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public class EntityBounds {
1919
*
2020
* @param entity The entity.
2121
*/
22-
EntityBounds(Entity entity) {
22+
protected EntityBounds(Entity entity) {
2323
this.entity = entity;
2424
}
2525

game/src/main/java/org/apollo/game/model/entity/obj/GameObject.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public abstract class GameObject extends Entity implements GroupableEntity {
2626
*/
2727
private final int packed;
2828

29+
/**
30+
* The bounds of this GameObject.
31+
*/
32+
private GameObjectBounds bounds;
33+
2934
/**
3035
* Creates the GameObject.
3136
*
@@ -103,6 +108,15 @@ public int getWidth() {
103108
getDefinition().getLength() : getDefinition().getWidth();
104109
}
105110

111+
@Override
112+
public GameObjectBounds getBounds() {
113+
if(bounds == null) {
114+
bounds = new GameObjectBounds(this);
115+
}
116+
117+
return bounds;
118+
}
119+
106120
@Override
107121
public int hashCode() {
108122
return packed;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package org.apollo.game.model.entity.obj;
2+
3+
import com.google.common.collect.ImmutableMap;
4+
import org.apollo.game.model.Direction;
5+
import org.apollo.game.model.Position;
6+
import org.apollo.game.model.entity.EntityBounds;
7+
8+
import java.util.Collections;
9+
import java.util.HashSet;
10+
import java.util.Map;
11+
import java.util.Set;
12+
13+
/**
14+
* The bounds of a {@link GameObject}.
15+
*
16+
* @author Steve Soltys
17+
*/
18+
public class GameObjectBounds extends EntityBounds {
19+
20+
/**
21+
* An {@link ImmutableMap} of Directions mapped to their mask bits for a {@link GameObject} interaction mask.
22+
*/
23+
private static final Map<Direction, Integer> INTERACTION_MASK_BITS = ImmutableMap.<Direction, Integer>builder()
24+
.put(Direction.NORTH, 0x1)
25+
.put(Direction.EAST, 0x8)
26+
.put(Direction.SOUTH, 0x4)
27+
.put(Direction.WEST, 0x2)
28+
.build();
29+
30+
/**
31+
* The GameObject.
32+
*/
33+
private final GameObject gameObject;
34+
35+
/**
36+
* The set of positions where the GameObject can be interacted with.
37+
*/
38+
private Set<Position> interactionPositions;
39+
40+
/**
41+
* Creates an EntityBounds.
42+
*
43+
* @param gameObject The GameObject.
44+
*/
45+
GameObjectBounds(GameObject gameObject) {
46+
super(gameObject);
47+
48+
this.gameObject = gameObject;
49+
}
50+
51+
/**
52+
* Gets the set of positions where the GameObject can be interacted with.
53+
*
54+
* @return The set of positions.
55+
*/
56+
public Set<Position> getInteractionPositions() {
57+
if (interactionPositions == null) {
58+
interactionPositions = computeInteractionPositions();
59+
}
60+
61+
return interactionPositions;
62+
}
63+
64+
/**
65+
* Computes the set of positions where the GameObject can be interacted with.
66+
*
67+
* @return The set of positions.
68+
*/
69+
private Set<Position> computeInteractionPositions() {
70+
71+
switch (ObjectType.valueOf(gameObject.getType())) {
72+
case DIAGONAL_WALL:
73+
return getDiagonalWallInteractionPositions();
74+
75+
case LENGTHWISE_WALL:
76+
return getWallInteractionPositions();
77+
78+
case INTERACTABLE:
79+
return getInteractablePositions();
80+
81+
// TODO: Figure out the rest.
82+
}
83+
84+
return Collections.emptySet();
85+
}
86+
87+
/**
88+
* Gets the set of positions for a diagonal wall where the GameObject can be interacted with.
89+
*
90+
* @return The set of interaction positions.
91+
*/
92+
private Set<Position> getDiagonalWallInteractionPositions() {
93+
Set<Position> positions = new HashSet<>();
94+
Direction direction = Direction.WNES[gameObject.getOrientation()];
95+
Position position = gameObject.getPosition();
96+
97+
// TODO: I think this either is missing one, or has one too many. Need to confirm these directions.
98+
positions.add(position.step(1, direction));
99+
positions.add(position.step(1, direction.counterClockwise()));
100+
positions.add(position.step(1, direction.counterClockwise(2)));
101+
positions.add(position.step(1, direction.clockwise()));
102+
positions.add(position.step(1, direction.clockwise(2)));
103+
positions.add(position.step(1, direction.opposite()));
104+
positions.add(position.step(1, direction.opposite().counterClockwise()));
105+
return positions;
106+
}
107+
108+
/**
109+
* Gets the set of Directions from which the GameObject can be interacted with.
110+
*
111+
* @return The interactable directions.
112+
*/
113+
private Set<Direction> getInteractableDirections() {
114+
Set<Direction> interactableDirections = new HashSet<>();
115+
116+
int interactionMask = gameObject.getDefinition().getInteractionMask();
117+
int rotatedInteractionMask = (interactionMask << gameObject.getOrientation() & 0xf) +
118+
(interactionMask >> 4 - gameObject.getOrientation());
119+
120+
for (Direction direction : Direction.NESW) {
121+
boolean interactive = (rotatedInteractionMask & INTERACTION_MASK_BITS.get(direction)) == 0;
122+
123+
if (interactive) {
124+
interactableDirections.add(direction);
125+
}
126+
}
127+
128+
return interactableDirections;
129+
}
130+
131+
/**
132+
* Gets the set of positions where the GameObject can be interacted with.
133+
*
134+
* @return The set of interaction positions.
135+
*/
136+
private Set<Position> getInteractablePositions() {
137+
Set<Direction> interactableDirections = getInteractableDirections();
138+
Set<Position> interactablePositions = new HashSet<>();
139+
140+
Position position = gameObject.getPosition();
141+
int width = gameObject.getWidth();
142+
int length = gameObject.getLength();
143+
144+
for (int deltaX = position.getX(); deltaX < position.getX() + width; deltaX++) {
145+
for (int deltaY = position.getY(); deltaY < position.getY() + length; deltaY++) {
146+
147+
for(Direction direction : interactableDirections) {
148+
Position deltaPosition = new Position(deltaX, deltaY, position.getHeight()).step(1, direction);
149+
150+
if(!contains(deltaPosition)) {
151+
interactablePositions.add(deltaPosition);
152+
}
153+
}
154+
}
155+
}
156+
157+
return interactablePositions;
158+
}
159+
160+
/**
161+
* Gets the set of positions for a wall where the GameObject can be interacted with.
162+
*
163+
* @return The set of interaction positions.
164+
*/
165+
private Set<Position> getWallInteractionPositions() {
166+
Set<Position> positions = new HashSet<>();
167+
Direction objectDirection = Direction.WNES[gameObject.getOrientation()];
168+
Position position = gameObject.getPosition();
169+
170+
positions.add(position);
171+
positions.add(position.step(1, objectDirection));
172+
positions.add(position.step(1, objectDirection.clockwise(2)));
173+
positions.add(position.step(1, objectDirection.counterClockwise(2)));
174+
return positions;
175+
}
176+
}

game/src/main/java/org/apollo/game/model/entity/obj/ObjectType.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.apollo.game.model.entity.obj;
22

3+
import java.util.Arrays;
4+
35
/**
46
* The type of an object, which affects specified behaviour (such as whether it displaces existing objects). TODO
57
* complete this...
@@ -70,6 +72,18 @@ public enum ObjectType {
7072
this.group = group;
7173
}
7274

75+
/**
76+
* Gets the type with the specified value.
77+
*
78+
* @param value The value.
79+
* @return The type.
80+
*/
81+
public static ObjectType valueOf(int value) {
82+
return Arrays.stream(values())
83+
.filter(type -> type.value == value)
84+
.findAny().orElse(null);
85+
}
86+
7387
/**
7488
* Gets the {@link ObjectGroup} of this ObjectType.
7589
*

0 commit comments

Comments
 (0)