1 package nl.tudelft.jpacman.level;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.List;
6 import java.util.Map;
7
8 import nl.tudelft.jpacman.board.Unit;
9
10 /**
11 * A map of possible collisions and their handlers.
12 *
13 * @author Michael de Jong
14 * @author Jeroen Roosen
15 */
16 public class CollisionInteractionMap implements CollisionMap {
17
18 /**
19 * The collection of collision handlers.
20 */
21 private final Map<Class<? extends Unit>,
22 Map<Class<? extends Unit>, CollisionHandler<?, ?>>> handlers;
23
24 /**
25 * Creates a new, empty collision map.
26 */
27 public CollisionInteractionMap() {
28 this.handlers = new HashMap<>();
29 }
30
31 /**
32 * Adds a two-way collision interaction to this collection, i.e. the
33 * collision handler will be used for both C1 versus C2 and C2 versus C1.
34 *
35 * @param <C1>
36 * The collider type.
37 * @param <C2>
38 * The collidee (unit that was moved into) type.
39 *
40 * @param collider
41 * The collider type.
42 * @param collidee
43 * The collidee type.
44 * @param handler
45 * The handler that handles the collision.
46 */
47 public <C1 extends Unit, C2 extends Unit> void onCollision(
48 Class<C1> collider, Class<C2> collidee, CollisionHandler<C1, C2> handler) {
49 onCollision(collider, collidee, true, handler);
50 }
51
52 /**
53 * Adds a collision interaction to this collection.
54 *
55 * @param <C1>
56 * The collider type.
57 * @param <C2>
58 * The collidee (unit that was moved into) type.
59 *
60 * @param collider
61 * The collider type.
62 * @param collidee
63 * The collidee type.
64 * @param symetric
65 * <code>true</code> if this collision is used for both
66 * C1 against C2 and vice versa;
67 * <code>false</code> if only for C1 against C2.
68 * @param handler
69 * The handler that handles the collision.
70 */
71 public <C1 extends Unit, C2 extends Unit> void onCollision(
72 Class<C1> collider, Class<C2> collidee, boolean symetric,
73 CollisionHandler<C1, C2> handler) {
74 addHandler(collider, collidee, handler);
75 if (symetric) {
76 addHandler(collidee, collider, new InverseCollisionHandler<>(handler));
77 }
78 }
79
80 /**
81 * Adds the collision interaction..
82 *
83 * @param collider
84 * The collider type.
85 * @param collidee
86 * The collidee type.
87 * @param handler
88 * The handler that handles the collision.
89 */
90 private void addHandler(Class<? extends Unit> collider,
91 Class<? extends Unit> collidee, CollisionHandler<?, ?> handler) {
92 if (!handlers.containsKey(collider)) {
93 handlers.put(collider, new HashMap<>());
94 }
95
96 Map<Class<? extends Unit>, CollisionHandler<?, ?>> map = handlers.get(collider);
97 map.put(collidee, handler);
98 }
99
100 /**
101 * Handles the collision between two colliding parties, if a suitable
102 * collision handler is listed.
103 *
104 * @param <C1>
105 * The collider type.
106 * @param <C2>
107 * The collidee (unit that was moved into) type.
108 *
109 * @param collider
110 * The collider.
111 * @param collidee
112 * The collidee.
113 */
114 @SuppressWarnings("unchecked")
115 @Override
116 public <C1 extends Unit, C2 extends Unit> void collide(C1 collider,
117 C2 collidee) {
118 Class<? extends Unit> colliderKey = getMostSpecificClass(handlers, collider.getClass());
119 if (colliderKey == null) {
120 return;
121 }
122
123 Map<Class<? extends Unit>, CollisionHandler<?, ?>> map = handlers.get(colliderKey);
124 Class<? extends Unit> collideeKey = getMostSpecificClass(map, collidee.getClass());
125 if (collideeKey == null) {
126 return;
127 }
128
129 CollisionHandler<C1, C2> collisionHandler = (CollisionHandler<C1, C2>) map.get(collideeKey);
130 if (collisionHandler == null) {
131 return;
132 }
133
134 collisionHandler.handleCollision(collider, collidee);
135 }
136
137 /**
138 * Figures out the most specific class that is listed in the map. I.e. if A
139 * extends B and B is listed while requesting A, then B will be returned.
140 *
141 * @param map
142 * The map with the key collection to find a matching class in.
143 * @param key
144 * The class to search the most suitable key for.
145 * @return The most specific class from the key collection.
146 */
147 private Class<? extends Unit> getMostSpecificClass(
148 Map<Class<? extends Unit>, ?> map, Class<? extends Unit> key) {
149 List<Class<? extends Unit>> collideeInheritance = getInheritance(key);
150 for (Class<? extends Unit> pointer : collideeInheritance) {
151 if (map.containsKey(pointer)) {
152 return pointer;
153 }
154 }
155 return null;
156 }
157
158 /**
159 * Returns a list of all classes and interfaces the class inherits.
160 *
161 * @param clazz
162 * The class to create a list of super classes and interfaces
163 * for.
164 * @return A list of all classes and interfaces the class inherits.
165 */
166 @SuppressWarnings("unchecked")
167 private List<Class<? extends Unit>> getInheritance(
168 Class<? extends Unit> clazz) {
169 List<Class<? extends Unit>> found = new ArrayList<>();
170 found.add(clazz);
171
172 int index = 0;
173 while (found.size() > index) {
174 Class<?> current = found.get(index);
175 Class<?> superClass = current.getSuperclass();
176 if (superClass != null && Unit.class.isAssignableFrom(superClass)) {
177 found.add((Class<? extends Unit>) superClass);
178 }
179 for (Class<?> classInterface : current.getInterfaces()) {
180 if (Unit.class.isAssignableFrom(classInterface)) {
181 found.add((Class<? extends Unit>) classInterface);
182 }
183 }
184 index++;
185 }
186
187 return found;
188 }
189
190 /**
191 * Handles the collision between two colliding parties.
192 *
193 * @author Michael de Jong
194 *
195 * @param <C1>
196 * The collider type.
197 * @param <C2>
198 * The collidee type.
199 */
200 public interface CollisionHandler<C1 extends Unit, C2 extends Unit> {
201
202 /**
203 * Handles the collision between two colliding parties.
204 *
205 * @param collider
206 * The collider.
207 * @param collidee
208 * The collidee.
209 */
210 void handleCollision(C1 collider, C2 collidee);
211 }
212
213 /**
214 * An symmetrical copy of a collision hander.
215 *
216 * @author Michael de Jong
217 *
218 * @param <C1>
219 * The collider type.
220 * @param <C2>
221 * The collidee type.
222 */
223 private static class InverseCollisionHandler<C1 extends Unit, C2 extends Unit>
224 implements CollisionHandler<C1, C2> {
225
226 /**
227 * The handler of this collision.
228 */
229 private final CollisionHandler<C2, C1> handler;
230
231 /**
232 * Creates a new collision handler.
233 *
234 * @param handler
235 * The symmetric handler for this collision.
236 */
237 InverseCollisionHandler(CollisionHandler<C2, C1> handler) {
238 this.handler = handler;
239 }
240
241 /**
242 * Handles this collision by flipping the collider and collidee, making
243 * it compatible with the initial collision.
244 */
245 @Override
246 public void handleCollision(C1 collider, C2 collidee) {
247 handler.handleCollision(collidee, collider);
248 }
249 }
250
251 }