1 package com.jed.actor;
2
3 import com.jed.state.AbstractDisplayableState;
4 import com.jed.state.GameMap;
5 import com.jed.state.State;
6 import com.jed.util.Util;
7 import com.jed.util.Vector2f;
8 import org.lwjgl.opengl.GL11;
9 import org.newdawn.slick.Color;
10 import org.newdawn.slick.opengl.Texture;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import javax.annotation.Nonnull;
15
16
17
18
19
20
21
22
23
24
25
26 public class Player extends AbstractEntity {
27
28
29
30
31 private static final Logger LOGGER = LoggerFactory.getLogger(Player.class);
32
33
34
35
36 private static final float X_MOVEMENT_SCALAR = 0.5f;
37
38
39
40 private final int height;
41
42
43
44
45 private final int width;
46
47
48
49
50 private int xDir;
51
52
53
54
55 private static final String TEXTURE_PATH = "MEGA_MAN_SH.png";
56
57
58
59
60 @Nonnull
61 private Texture texture;
62
63
64
65
66 private static final int PLAYER_RIGHT = 1;
67
68
69
70
71 private static final int PLAYER_LEFT = 0;
72
73
74
75
76 @Nonnull
77 private AbstractPlayerState currentState;
78
79
80
81
82 @Nonnull
83 private final AbstractPlayerState fallingState;
84
85
86
87
88 @Nonnull
89 private final AbstractPlayerState idleState;
90
91
92
93
94 @Nonnull
95 private final AbstractPlayerState walkingState;
96
97
98
99
100 @Nonnull
101 private final AbstractPlayerState jumpingState;
102
103
104
105
106 private boolean collideDown = false;
107
108
109
110
111 private static final float FRICTION = .046875f;
112
113
114
115
116 private int jumpCount = 0;
117
118
119
120
121 private final GameMap map;
122
123
124
125
126 private boolean isMovingLeft;
127
128
129
130
131 private boolean isMovingRight;
132
133
134
135
136 private boolean isJumping;
137
138
139
140
141
142
143
144
145
146
147 public Player(Vector2f position, int height, int width, GameMap map) {
148 super(
149 position,
150 new Vector2f(0, 0),
151 new PolygonBoundary(
152 new Vector2f(110, 130),
153 new Vector2f[]{
154 new Vector2f(0, 0),
155 new Vector2f(40, 0),
156 new Vector2f(40, 120),
157 new Vector2f(0, 120)
158 })
159 );
160
161 this.setAcceleration(FRICTION);
162 this.height = height;
163 this.width = width;
164 this.map = map;
165 this.texture = Util.loadTexture(TEXTURE_PATH);
166
167 this.fallingState = new Falling();
168 this.idleState = new Idle();
169 this.walkingState = new Walking();
170 this.jumpingState = new Jumping();
171 }
172
173
174
175
176 public void changeState(State state) {
177 currentState = (AbstractPlayerState) state;
178 currentState.entered();
179 }
180
181 @Override
182 public void entered() {
183 changeState(fallingState);
184 }
185
186 @Override
187 public void update() {
188 currentState.handleInput();
189
190 if (!currentState.falling && !collideDown) {
191 changeState(fallingState);
192 }
193 collideDown = false;
194
195 if (currentState.falling) {
196 getMovement().y += map.getGravity();
197 }
198
199 getPosition().x = this.getPosition().x + getMovement().x;
200 getPosition().y = getPosition().y + getMovement().y;
201
202 currentState.update();
203 }
204
205 @Override
206 public void render() {
207 currentState.render();
208 getBounds().render();
209 }
210
211
212
213
214
215
216
217
218 @Override
219 public void collideDown(AbstractEntity sEntity) {
220 collideDown = true;
221 if (currentState.falling) {
222 changeState(idleState);
223 }
224 }
225
226
227
228
229
230
231 private abstract class AbstractPlayerState extends AbstractDisplayableState {
232
233
234
235
236 boolean falling;
237
238
239
240
241 public AbstractPlayerState() {
242 falling = false;
243 }
244
245
246
247
248 public abstract void handleInput();
249 }
250
251
252
253
254 private abstract class AbstractNonEnterablePlayerState extends AbstractPlayerState {
255
256
257
258
259 final Logger LOGGER = LoggerFactory.getLogger(AbstractNonEnterablePlayerState.class);
260
261 @Override
262 public void entered() {
263 LOGGER.debug("com.jed.actor.Player.AbstractNonEnterablePlayerState#entered");
264 }
265 }
266
267
268
269
270
271
272 private class Falling extends AbstractNonEnterablePlayerState {
273
274
275
276
277 private float bottomLeftX;
278
279
280
281
282 private float bottomRightX;
283
284
285
286
287 private float topRightX;
288
289
290
291
292 private float topLeftX;
293
294
295
296
297 public Falling() {
298 this.falling = true;
299 }
300
301 @Override
302 public void update() {
303
304 if (getMovement().y == 0) {
305 if (getMovement().x != 0) {
306 changeState(walkingState);
307 } else {
308 changeState(idleState);
309 }
310 }
311 }
312
313 @Override
314 public void handleInput() {
315 keyHoldEvent();
316 }
317
318 @Override
319 public void render() {
320 Color.white.bind();
321 texture.bind();
322 GL11.glEnable(GL11.GL_TEXTURE_2D);
323 GL11.glBegin(GL11.GL_QUADS);
324
325 if (xDir == PLAYER_LEFT) {
326 bottomLeftX = getPosition().x + width;
327 bottomRightX = getPosition().x;
328 topLeftX = getPosition().x;
329 topRightX = getPosition().x + width;
330 } else {
331 bottomLeftX = getPosition().x;
332 bottomRightX = getPosition().x + width;
333 topLeftX = getPosition().x + width;
334 topRightX = getPosition().x;
335 }
336
337 GL11.glTexCoord2f(.25f, .5f);
338 map.drawChildVertex2f(bottomLeftX, getPosition().y);
339 GL11.glTexCoord2f(.3125f, .5f);
340 map.drawChildVertex2f(bottomRightX, getPosition().y);
341 GL11.glTexCoord2f(.3125f, 1);
342 map.drawChildVertex2f(topLeftX, getPosition().y + height);
343 GL11.glTexCoord2f(.25f, 1);
344 map.drawChildVertex2f(topRightX, getPosition().y + height);
345 GL11.glEnd();
346 GL11.glDisable(GL11.GL_TEXTURE_2D);
347 }
348
349 }
350
351
352
353
354
355
356 private final class Jumping extends Falling {
357
358
359
360
361 final float[] animation = {.0625f, .125f, .1875f, .25f, .3125f, .375f, .4375f};
362
363
364
365
366 final float frameWidth = .0625f;
367
368
369
370
371 int frame, ticks;
372
373
374
375
376 public Jumping() {
377 this.falling = true;
378 }
379
380 @Override
381 public void entered() {
382 frame = 0;
383 ticks = 0;
384 }
385
386 @Override
387 public void update() {
388 ticks++;
389 if (ticks % 16 == 0) {
390 frame = frame == animation.length - 1 ? frame : frame + 1;
391 }
392
393 super.update();
394 }
395
396 @Override
397 public void render() {
398 Color.white.bind();
399 texture.bind();
400 GL11.glEnable(GL11.GL_TEXTURE_2D);
401 GL11.glBegin(GL11.GL_QUADS);
402 if (xDir != PLAYER_LEFT) {
403 GL11.glTexCoord2f(animation[frame] - frameWidth, .5f);
404 map.drawChildVertex2f(getPosition().x, getPosition().y);
405 GL11.glTexCoord2f(animation[frame], .5f);
406 map.drawChildVertex2f(getPosition().x + width, getPosition().y);
407 GL11.glTexCoord2f(animation[frame], 1);
408 map.drawChildVertex2f(getPosition().x + width, getPosition().y + height);
409 GL11.glTexCoord2f(animation[frame] - frameWidth, 1);
410 map.drawChildVertex2f(getPosition().x, getPosition().y + height);
411 } else {
412 GL11.glTexCoord2f(animation[frame] - frameWidth, .5f);
413 map.drawChildVertex2f(getPosition().x + width, getPosition().y);
414 GL11.glTexCoord2f(animation[frame], .5f);
415 map.drawChildVertex2f(getPosition().x, getPosition().y);
416 GL11.glTexCoord2f(animation[frame], 1);
417 map.drawChildVertex2f(getPosition().x, getPosition().y + height);
418 GL11.glTexCoord2f(animation[frame] - frameWidth, 1);
419 map.drawChildVertex2f(getPosition().x + width, getPosition().y + height);
420 }
421 GL11.glEnd();
422 GL11.glDisable(GL11.GL_TEXTURE_2D);
423
424 }
425
426 }
427
428
429
430
431
432
433 private final class Idle extends AbstractNonEnterablePlayerState {
434
435 @Override
436 public void update() {
437 if (getMovement().y != 0) {
438 changeState(fallingState);
439 } else if (getMovement().x != 0) {
440 changeState(walkingState);
441 }
442 }
443
444 @Override
445 public void handleInput() {
446 keyHoldEvent();
447 }
448
449 @Override
450 public void render() {
451 Color.white.bind();
452 texture.bind();
453 GL11.glEnable(GL11.GL_TEXTURE_2D);
454 GL11.glBegin(GL11.GL_QUADS);
455
456 if (xDir == PLAYER_LEFT) {
457 GL11.glTexCoord2f(0, 0);
458 map.drawChildVertex2f(getPosition().x + width, getPosition().y);
459 GL11.glTexCoord2f(.0625f, 0);
460 map.drawChildVertex2f(getPosition().x, getPosition().y);
461 GL11.glTexCoord2f(.0625f, .5f);
462 map.drawChildVertex2f(getPosition().x, getPosition().y + height);
463 GL11.glTexCoord2f(0, .5f);
464 map.drawChildVertex2f(getPosition().x + width, getPosition().y + height);
465 } else {
466 GL11.glTexCoord2f(0, 0);
467 map.drawChildVertex2f(getPosition().x, getPosition().y);
468 GL11.glTexCoord2f(.0625f, 0);
469 map.drawChildVertex2f(getPosition().x + width, getPosition().y);
470 GL11.glTexCoord2f(.0625f, .5f);
471 map.drawChildVertex2f(getPosition().x + width, getPosition().y + height);
472 GL11.glTexCoord2f(0, .5f);
473 map.drawChildVertex2f(getPosition().x, getPosition().y + height);
474 }
475 GL11.glEnd();
476 GL11.glDisable(GL11.GL_TEXTURE_2D);
477 }
478
479 }
480
481
482
483
484
485
486 private final class Walking extends AbstractPlayerState {
487
488
489
490
491 final float[] animation = {.125f, .1875f, .25f, .3125f, .375f, .4375f, .5f, .5625f, .625f, .6875f, .75f};
492
493
494
495
496 final float frameWidth = .0625f;
497
498
499
500
501 int frame, ticks;
502
503 @Override
504 public void entered() {
505 frame = 0;
506 ticks = 0;
507 }
508
509 @Override
510 public void handleInput() {
511 keyHoldEvent();
512 }
513
514 @Override
515 public void update() {
516 ticks++;
517 if (ticks % 8 == 0) {
518 frame = frame == animation.length - 1 ? 1 : frame + 1;
519 }
520
521 if (getMovement().x == 0) {
522 changeState(idleState);
523 }
524 }
525
526 @Override
527 public void render() {
528 Color.white.bind();
529 texture.bind();
530 GL11.glEnable(GL11.GL_TEXTURE_2D);
531 GL11.glBegin(GL11.GL_QUADS);
532 if (getMovement().x > 0) {
533 GL11.glTexCoord2f(animation[frame] - frameWidth, 0);
534 map.drawChildVertex2f(getPosition().x, getPosition().y);
535 GL11.glTexCoord2f(animation[frame], 0);
536 map.drawChildVertex2f(getPosition().x + width, getPosition().y);
537 GL11.glTexCoord2f(animation[frame], .5f);
538 map.drawChildVertex2f(getPosition().x + width, getPosition().y + height);
539 GL11.glTexCoord2f(animation[frame] - frameWidth, .5f);
540 map.drawChildVertex2f(getPosition().x, getPosition().y + height);
541 } else {
542 GL11.glTexCoord2f(animation[frame] - frameWidth, 0);
543 map.drawChildVertex2f(getPosition().x + width, getPosition().y);
544 GL11.glTexCoord2f(animation[frame], 0);
545 map.drawChildVertex2f(getPosition().x, getPosition().y);
546 GL11.glTexCoord2f(animation[frame], .5f);
547 map.drawChildVertex2f(getPosition().x, getPosition().y + height);
548 GL11.glTexCoord2f(animation[frame] - frameWidth, .5f);
549 map.drawChildVertex2f(getPosition().x + width, getPosition().y + height);
550 }
551 GL11.glEnd();
552 GL11.glDisable(GL11.GL_TEXTURE_2D);
553 }
554 }
555
556 @Override
557 public void drawChildVertex2f(float x, float y) {
558 map.drawChildVertex2f(getPosition().x + x, getPosition().y + y);
559 }
560
561
562
563
564 private void jump() {
565 boolean isJumpCountLessThanTwo = jumpCount < 2;
566 int heightOffsetWithYPosition = Math.round(getPosition().y) + height;
567 if (isJumpCountLessThanTwo || heightOffsetWithYPosition == map.getHeight() * map.getTileHeight()) {
568 getMovement().y = -8;
569 jumpCount++;
570 changeState(jumpingState);
571 }
572 isJumping = false;
573 }
574
575
576
577
578 private void moveRight() {
579 LOGGER.info("moveRight");
580 if (Float.compare(getMovement().x, 0) < 0) {
581 getMovement().x += X_MOVEMENT_SCALAR;
582 } else {
583 getMovement().x += getAcceleration();
584 xDir = PLAYER_RIGHT;
585 }
586 }
587
588
589
590
591 private void moveLeft() {
592 LOGGER.info("moveLeft");
593 if (Float.compare(getMovement().x, 0) > 0) {
594 getMovement().x -= X_MOVEMENT_SCALAR;
595 } else {
596 getMovement().x -= getAcceleration();
597 xDir = PLAYER_LEFT;
598 }
599 }
600
601
602
603
604
605
606 private void keyHoldEvent() {
607 if(isJumping) {
608 jump();
609 }
610 if(isMovingLeft) {
611 moveLeft();
612 } else if(isMovingRight) {
613 moveRight();
614 } else if (Float.compare(getMovement().x, 0) != 0) {
615 getMovement().x = getMovement().x - Math.min(Math.abs(getMovement().x), FRICTION)
616 * Math.signum(getMovement().x);
617 }
618 if (!isJumping && !currentState.falling) {
619 jumpCount = 0;
620 }
621 }
622
623
624
625
626
627 public int getHeight() {
628 return height;
629 }
630
631
632
633
634
635 public int getWidth() {
636 return width;
637 }
638
639
640
641
642
643 public void setMovingLeft(boolean isMovingLeft) {
644 this.isMovingLeft = isMovingLeft;
645 }
646
647
648
649
650
651 public void setMovingRight(boolean isMovingRight) {
652 this.isMovingRight = isMovingRight;
653 }
654
655
656
657
658
659 public void setJumping(boolean isJumping) {
660 this.isJumping = isJumping;
661 }
662 }