1 package com.jed.state;
2
3 import com.jed.actor.AbstractEntity;
4 import com.jed.actor.Player;
5 import com.jed.core.Collision;
6 import com.jed.core.MotherBrainConstants;
7 import com.jed.core.QuadTree;
8 import com.jed.util.Rectangle;
9 import com.jed.util.Util;
10 import com.jed.util.Vector2f;
11 import org.lwjgl.opengl.GL11;
12 import org.newdawn.slick.Color;
13 import org.newdawn.slick.opengl.Texture;
14
15 import javax.annotation.Nonnull;
16 import java.util.*;
17 import java.util.concurrent.CopyOnWriteArrayList;
18
19
20
21
22
23
24
25
26 public final class GameMap extends AbstractDisplayableState {
27
28
29
30
31 private int width;
32
33
34
35
36 private int height;
37
38
39
40
41 private int tileWidth;
42
43
44
45
46 private int tileHeight;
47
48
49
50
51 private String tileSetPath;
52
53
54
55
56
57 private static final Vector2f INITIAL_POSITION = new Vector2f(0, 0);
58
59
60
61
62 private List<MapTile> tiles;
63
64
65
66
67 @Nonnull
68 private Texture texture;
69
70
71
72
73 private final Player player;
74
75
76
77
78 private final Stack<AbstractEntity> scene;
79
80
81
82
83 private QuadTree quadTree;
84
85
86
87
88 private float gravity = 0.21875f;
89
90
91
92
93 private boolean isDebugViewEnabled;
94
95
96
97
98 public GameMap() {
99
100 scene = new GameEntityStack<>();
101 player = new Player(new Vector2f(50, 200), 256, 256, this);
102 scene.push(player);
103 }
104
105 @Override
106 public void entered() {
107 texture = Util.loadTexture(tileSetPath);
108
109 quadTree = new QuadTree(
110 new Vector2f(0, 0), 0,
111 new Rectangle(
112 width * tileWidth,
113 height * tileHeight),
114 this);
115
116
117
118
119
120
121 drawMap();
122 }
123
124 @Override
125 public void update() {
126 tiles.forEach(each -> { each.setColliding(false); each.setEvaluating(false); });
127 scene.forEach(quadTree::insert);
128 detectCollisions();
129 scene.forEach(AbstractEntity::update);
130 scrollMap();
131 }
132
133
134
135
136 private void scrollMap() {
137 final float playerHeightMinusInitialPosition = (player.getHeight() / 2) - INITIAL_POSITION.y;
138 if (player.getMovement().y > 0) {
139 if ((player.getPosition().y + playerHeightMinusInitialPosition) > MotherBrainConstants.HEIGHT / 2) {
140 if (INITIAL_POSITION.y + player.getMovement().y > height * tileHeight - MotherBrainConstants.HEIGHT) {
141 INITIAL_POSITION.y = height * tileHeight - MotherBrainConstants.HEIGHT;
142 } else {
143 INITIAL_POSITION.y += player.getMovement().y;
144 }
145 }
146 } else if (player.getMovement().y < 0) {
147 if ((player.getPosition().y + playerHeightMinusInitialPosition) < MotherBrainConstants.HEIGHT / 2) {
148 if (player.getMovement().y + INITIAL_POSITION.y < 0) {
149 INITIAL_POSITION.y = 0;
150 } else {
151 INITIAL_POSITION.y += player.getMovement().y;
152 }
153 }
154 }
155 final float playerWidthMinusInitialPosition = (player.getWidth() / 2) - INITIAL_POSITION.x;
156 if (player.getMovement().x > 0) {
157 if ((player.getPosition().x + playerWidthMinusInitialPosition) > MotherBrainConstants.WIDTH / 2) {
158 if (INITIAL_POSITION.x + player.getMovement().x > width * tileWidth - MotherBrainConstants.WIDTH) {
159 INITIAL_POSITION.x = width * tileWidth - MotherBrainConstants.WIDTH;
160 } else {
161 INITIAL_POSITION.x += player.getMovement().x;
162 }
163 }
164 } else if (player.getMovement().x < 0) {
165 if ((player.getPosition().x + playerWidthMinusInitialPosition) < MotherBrainConstants.WIDTH / 2) {
166 if (player.getMovement().x + INITIAL_POSITION.x < 0) {
167 INITIAL_POSITION.x = 0;
168 } else {
169 INITIAL_POSITION.x += player.getMovement().x;
170 }
171 }
172 }
173 }
174
175
176
177
178 private void detectCollisions() {
179 final List<AbstractEntity> returnObjects = new ArrayList<>(scene.size());
180 final List<Collision> collisions = new CopyOnWriteArrayList<>();
181 for (final AbstractEntity entity : scene) {
182 quadTree.retrieve(returnObjects, entity);
183
184 returnObjects.stream().filter(returnObject -> !returnObject.equals(entity)).forEach(returnObject -> {
185 final Collision collision = new Collision(entity, returnObject, isDebugViewEnabled());
186
187 if (collision.detectCollision()) {
188 collisions.add(collision);
189 }
190 });
191
192
193
194
195
196 final Iterator<Collision> it = collisions.iterator();
197 while (collisions.size() > 0) {
198 Collections.sort(collisions);
199 collisions.get(0).resolveCollision();
200 collisions.remove(0);
201 while (it.hasNext()) {
202 final Collision each = it.next();
203 if (!each.detectCollision()) {
204 collisions.remove(each);
205 }
206 }
207 }
208 }
209
210 }
211
212
213 @Override
214 public void render() {
215 quadTree.clear();
216 drawMap();
217 if(isDebugViewEnabled()) {
218 quadTree.render();
219 }
220 for (AbstractEntity each : scene) {
221 each.render();
222 }
223 }
224
225
226
227
228 private void drawMap() {
229 Color.white.bind();
230 texture.bind();
231 GL11.glEnable(GL11.GL_TEXTURE_2D);
232
233 final float tileOffsetY = INITIAL_POSITION.y / tileHeight;
234 final double pixelOffsetY = tileHeight * (tileOffsetY % 1);
235
236 final float tileOffsetX = INITIAL_POSITION.x / tileWidth;
237 final double pixelOffsetX = tileWidth * (tileOffsetX % 1);
238
239 int tileIndex = (int) (width * (Math.floor(tileOffsetY)) + tileOffsetX);
240
241 final int rows = (MotherBrainConstants.HEIGHT / tileHeight + (pixelOffsetY == 0 ? 0 : 1));
242 final int columns = (MotherBrainConstants.WIDTH / tileWidth + (pixelOffsetX == 0 ? 0 : 1));
243 final int nextRow = width - columns;
244
245 MapTile mapTile;
246 for (int rowIndex = 0; rowIndex < rows; rowIndex++) {
247 for (int columnIndex = 0; columnIndex < columns; columnIndex++) {
248 mapTile = tiles.get(tileIndex);
249 if (mapTile.getTileId() != 0) {
250 mapTile.render();
251 quadTree.insert(mapTile);
252 }
253 tileIndex++;
254 }
255 tileIndex += nextRow;
256 }
257
258 GL11.glDisable(GL11.GL_TEXTURE_2D);
259 }
260
261
262
263
264
265 public void drawChildVertex2f(float x, float y) {
266 GL11.glVertex2f(x - INITIAL_POSITION.x, y - INITIAL_POSITION.y);
267 }
268
269
270
271
272
273 public void setTileSetPath(String tileSetPath) {
274 this.tileSetPath = tileSetPath;
275 }
276
277
278
279
280
281 public void setWidth(int width) {
282 this.width = width;
283 }
284
285
286
287
288
289 public int getWidth() {
290 return width;
291 }
292
293
294
295
296
297 public void setHeight(int height) {
298 this.height = height;
299 }
300
301
302
303
304
305 public int getHeight() {
306 return height;
307 }
308
309
310
311
312
313 public float getGravity() {
314 return gravity;
315 }
316
317
318
319
320
321 public int getTileWidth() {
322 return tileWidth;
323 }
324
325
326
327
328
329 public void setTileWidth(int tileWidth) {
330 this.tileWidth = tileWidth;
331 }
332
333
334
335
336
337 public int getTileHeight() {
338 return tileHeight;
339 }
340
341
342
343
344
345 public void setTileHeight(int tileHeight) {
346 this.tileHeight = tileHeight;
347 }
348
349
350
351
352
353 public void setGravity(Float gravity) {
354 this.gravity = gravity;
355 }
356
357
358
359
360
361 public void setTiles(final List<MapTile> tiles) {
362 this.tiles = tiles;
363 }
364
365
366
367
368
369 public List<MapTile> getTiles() {
370 return tiles;
371 }
372
373
374
375
376
377 public void setDebugViewEnabled(boolean isDebugViewEnabled) {
378 this.isDebugViewEnabled = isDebugViewEnabled;
379 }
380
381
382
383
384
385 boolean isDebugViewEnabled() {
386 return isDebugViewEnabled;
387 }
388
389
390
391
392
393 public Player getPlayer() {
394 return player;
395 }
396 }