Entity.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. package me.hammerle.supersnuvi.entity;
  2. import java.util.LinkedList;
  3. import javafx.scene.image.Image;
  4. import javafx.scene.paint.Color;
  5. import me.hammerle.supersnuvi.gamelogic.Level;
  6. import me.hammerle.supersnuvi.input.IKeyHandler;
  7. import me.hammerle.supersnuvi.rendering.GameRenderer;
  8. import me.hammerle.supersnuvi.tiles.Location;
  9. import me.hammerle.supersnuvi.util.CollisionBox;
  10. import me.hammerle.supersnuvi.util.Face;
  11. import me.hammerle.supersnuvi.util.Utils;
  12. public class Entity
  13. {
  14. // 1 Tile = 1 m
  15. // Pixel m s Pixel
  16. // ----- * --- * ------ = -------
  17. // m s Tick Tick
  18. // added correction factor, real life gravity seems to strong
  19. public static double GRAVITY = GameRenderer.TILE_SIZE * 9.81 / 60 * 0.1;
  20. private double prevPosX;
  21. private double prevPrevY;
  22. private double posX;
  23. private double posY;
  24. private double prevMotionX;
  25. private double prevMotionY;
  26. private double motionX;
  27. private double motionY;
  28. private boolean onGround;
  29. private boolean inWater;
  30. private final CollisionBox collisionBox;
  31. private final double width;
  32. private final double height;
  33. private Image image;
  34. private final Level level;
  35. public Entity(Level level, double x, double y, double width, double height)
  36. {
  37. this.level = level;
  38. this.posX = x;
  39. this.posY = y;
  40. this.prevPosX = x;
  41. this.prevPrevY = y;
  42. this.motionX = 0;
  43. this.motionY = 0;
  44. this.onGround = false;
  45. this.inWater = false;
  46. this.collisionBox = new CollisionBox(0, 0, width, height);
  47. this.width = width;
  48. this.height = height;
  49. image = Utils.getColoredImage(Color.BLACK, (int) width, (int) height);
  50. }
  51. public Level getLevel()
  52. {
  53. return level;
  54. }
  55. //--------------------------------------------------------------------------
  56. // ticking
  57. //--------------------------------------------------------------------------
  58. public void controlTick(IKeyHandler keyHandler)
  59. {
  60. }
  61. public final void tick()
  62. {
  63. if(isAffectedByGravity())
  64. {
  65. motionY -= GRAVITY;
  66. }
  67. prevMotionX = motionX;
  68. prevMotionY = motionY;
  69. prevPosX = posX;
  70. prevPrevY = posY;
  71. setAllowedMotion();
  72. posX += motionX;
  73. posY += motionY;
  74. motionX = Utils.round(motionX);
  75. motionY = Utils.round(motionY);
  76. if(motionY != 0)
  77. {
  78. onGround = false;
  79. }
  80. posX = Utils.round(posX);
  81. posY = Utils.round(posY);
  82. if(!onGround)
  83. {
  84. motionX *= 0.7;
  85. }
  86. LinkedList<CollisionBox> list = level.getMovementBoxesAt(this, getBox());
  87. if(!list.isEmpty())
  88. {
  89. System.out.println(getBox());
  90. list.forEach(s -> System.out.println(s));
  91. System.out.println("WELL");
  92. posX = prevPosX;
  93. posY = prevPrevY;
  94. }
  95. doCollision();
  96. }
  97. //--------------------------------------------------------------------------
  98. // gravity, friction
  99. //--------------------------------------------------------------------------
  100. public final boolean jump()
  101. {
  102. if(onGround)
  103. {
  104. onGround = false;
  105. motionY += getJumpPower();
  106. return true;
  107. }
  108. return false;
  109. }
  110. public boolean isAffectedByGravity()
  111. {
  112. return true;
  113. }
  114. public double getJumpPower()
  115. {
  116. return 0;
  117. }
  118. public boolean isOnGround()
  119. {
  120. return onGround;
  121. }
  122. public final double getMaxSpeedModifier()
  123. {
  124. double max = 1;
  125. if(inWater)
  126. {
  127. max *= 0.65;
  128. }
  129. return max;
  130. }
  131. public final void setInWater(boolean b)
  132. {
  133. inWater = b;
  134. }
  135. public final boolean isInWater()
  136. {
  137. return inWater;
  138. }
  139. public final void resetMovementData()
  140. {
  141. inWater = false;
  142. }
  143. //--------------------------------------------------------------------------
  144. // collision stuff
  145. //--------------------------------------------------------------------------
  146. public final CollisionBox getBox()
  147. {
  148. return collisionBox.reset().offset(posX, posY);
  149. }
  150. private void setAllowedMotion()
  151. {
  152. if(motionX == 0 && motionY == 0)
  153. {
  154. return;
  155. }
  156. double dirX = motionX;
  157. double dirY = motionY;
  158. double startX = posX;
  159. double startY = posY;
  160. double endX = startX + dirX;
  161. double endY = startY + dirY;
  162. CollisionBox eBox = getBox();
  163. double halfWidth = eBox.getWidth() / 2;
  164. double halfHeight = eBox.getHeight() / 2;
  165. eBox = eBox.copy();
  166. startX += halfWidth;
  167. startY += halfHeight;
  168. endX += halfWidth;
  169. endY += halfHeight;
  170. // expanding area by the size of the entity
  171. CollisionBox expandedBox = eBox.copy().expand(motionX, motionY);
  172. LinkedList<CollisionBox> allBoxes = level.getMovementBoxesAt(this, expandedBox);
  173. if(allBoxes.isEmpty())
  174. {
  175. return;
  176. }
  177. double x;
  178. double y;
  179. char lastWall = 0;
  180. if(dirX >= 0)
  181. {
  182. if(dirY <= 0) // Right - Down
  183. {
  184. for(CollisionBox box : allBoxes)
  185. {
  186. box.grow(halfWidth, halfHeight);
  187. if(endX <= box.getMinX() || endY >= box.getMaxY() ||
  188. startX >= box.getMaxX() || startY <= box.getMinY())
  189. {
  190. continue;
  191. }
  192. x = Utils.interpolateX(startX, startY, endX, endY, box.getMaxY());
  193. y = Utils.interpolateY(startX, startY, endX, endY, box.getMinX());
  194. if(y > box.getMinY() && y < box.getMaxY())
  195. {
  196. endX = box.getMinX();
  197. endY = y;
  198. lastWall = 'x';
  199. }
  200. else if(x > box.getMinX() && x < box.getMaxX())
  201. {
  202. endY = box.getMaxY();
  203. endX = x;
  204. lastWall = 'y';
  205. }
  206. else if(x >= box.getMinX() && x <= box.getMaxX())
  207. {
  208. lastWall = 'y';
  209. }
  210. }
  211. if(lastWall == 'x')
  212. {
  213. double lineEndY = startY + dirY;
  214. for(CollisionBox box : allBoxes)
  215. {
  216. if(endX <= box.getMinX() || lineEndY >= box.getMaxY() ||
  217. endX >= box.getMaxX() || endY <= box.getMinY())
  218. {
  219. continue;
  220. }
  221. lineEndY = box.getMaxY();
  222. }
  223. endY = lineEndY;
  224. }
  225. else if(lastWall == 'y')
  226. {
  227. double lineEndX = startX + dirX;
  228. for(CollisionBox box : allBoxes)
  229. {
  230. if(lineEndX <= box.getMinX() || endY >= box.getMaxY() ||
  231. endX >= box.getMaxX() || endY <= box.getMinY())
  232. {
  233. continue;
  234. }
  235. lineEndX = box.getMinX();
  236. }
  237. endX = lineEndX;
  238. }
  239. }
  240. else // Right - Up
  241. {
  242. for(CollisionBox box : allBoxes)
  243. {
  244. box.grow(halfWidth, halfHeight);
  245. if(endX <= box.getMinX() || endY <= box.getMinY() ||
  246. startX >= box.getMaxX() || startY >= box.getMaxY())
  247. {
  248. continue;
  249. }
  250. x = Utils.interpolateX(startX, startY, endX, endY, box.getMinY());
  251. y = Utils.interpolateY(startX, startY, endX, endY, box.getMinX());
  252. if(y > box.getMinY() && y < box.getMaxY())
  253. {
  254. endX = box.getMinX();
  255. endY = y;
  256. lastWall = 'x';
  257. }
  258. else if(x > box.getMinX() && x < box.getMaxX())
  259. {
  260. endY = box.getMinY();
  261. endX = x;
  262. lastWall = 'y';
  263. }
  264. else if(x >= box.getMinX() && x <= box.getMaxX())
  265. {
  266. lastWall = 'y';
  267. }
  268. }
  269. if(lastWall == 'x')
  270. {
  271. double lineEndY = startY + dirY;
  272. for(CollisionBox box : allBoxes)
  273. {
  274. if(endX <= box.getMinX() || lineEndY <= box.getMinY() ||
  275. endX >= box.getMaxX() || endY >= box.getMaxY())
  276. {
  277. continue;
  278. }
  279. lineEndY = box.getMinY();
  280. }
  281. endY = lineEndY;
  282. }
  283. else if(lastWall == 'y')
  284. {
  285. double lineEndX = startX + dirX;
  286. for(CollisionBox box : allBoxes)
  287. {
  288. if(lineEndX <= box.getMinX() || endY >= box.getMaxY() ||
  289. endX >= box.getMaxX() || endY <= box.getMinY())
  290. {
  291. continue;
  292. }
  293. lineEndX = box.getMinX();
  294. }
  295. endX = lineEndX;
  296. }
  297. }
  298. }
  299. else
  300. {
  301. if(dirY <= 0) // Left - Down
  302. {
  303. for(CollisionBox box : allBoxes)
  304. {
  305. box.grow(halfWidth, halfHeight);
  306. if(endX >= box.getMaxX() || endY >= box.getMaxY() ||
  307. startX <= box.getMinX() || startY <= box.getMinY())
  308. {
  309. continue;
  310. }
  311. x = Utils.interpolateX(startX, startY, endX, endY, box.getMaxY());
  312. y = Utils.interpolateY(startX, startY, endX, endY, box.getMaxX());
  313. if(y > box.getMinY() && y < box.getMaxY())
  314. {
  315. endX = box.getMaxX();
  316. endY = y;
  317. lastWall = 'x';
  318. }
  319. else if(x > box.getMinX() && x < box.getMaxX())
  320. {
  321. endY = box.getMaxY();
  322. endX = x;
  323. lastWall = 'y';
  324. }
  325. else if(x >= box.getMinX() && x <= box.getMaxX())
  326. {
  327. lastWall = 'y';
  328. }
  329. }
  330. if(lastWall == 'x')
  331. {
  332. double lineEndY = startY + dirY;
  333. for(CollisionBox box : allBoxes)
  334. {
  335. if(endX <= box.getMinX() || lineEndY >= box.getMaxY() ||
  336. endX >= box.getMaxX() || endY <= box.getMinY())
  337. {
  338. continue;
  339. }
  340. lineEndY = box.getMaxY();
  341. }
  342. endY = lineEndY;
  343. }
  344. else if(lastWall == 'y')
  345. {
  346. double lineEndX = startX + dirX;
  347. for(CollisionBox box : allBoxes)
  348. {
  349. if(lineEndX >= box.getMaxX() || endY >= box.getMaxY() ||
  350. endX <= box.getMinX() || endY <= box.getMinY())
  351. {
  352. continue;
  353. }
  354. lineEndX = box.getMaxX();
  355. }
  356. endX = lineEndX;
  357. }
  358. }
  359. else // Left - Up
  360. {
  361. for(CollisionBox box : allBoxes)
  362. {
  363. box.grow(halfWidth, halfHeight);
  364. if(endX >= box.getMaxX() || endY <= box.getMinY() ||
  365. startX <= box.getMinX() || startY >= box.getMaxY())
  366. {
  367. continue;
  368. }
  369. x = Utils.interpolateX(startX, startY, endX, endY, box.getMinY());
  370. y = Utils.interpolateY(startX, startY, endX, endY, box.getMaxX());
  371. if(y > box.getMinY() && y < box.getMaxY())
  372. {
  373. endX = box.getMaxX();
  374. endY = y;
  375. lastWall = 'x';
  376. }
  377. else if(x > box.getMinX() && x < box.getMaxX())
  378. {
  379. endY = box.getMinY();
  380. endX = x;
  381. lastWall = 'y';
  382. }
  383. else if(x >= box.getMinX() && x <= box.getMaxX())
  384. {
  385. lastWall = 'y';
  386. }
  387. }
  388. if(lastWall == 'x')
  389. {
  390. double lineEndY = startY + dirY;
  391. for(CollisionBox box : allBoxes)
  392. {
  393. if(endX <= box.getMinX() || lineEndY <= box.getMinY() ||
  394. endX >= box.getMaxX() || endY >= box.getMaxY())
  395. {
  396. continue;
  397. }
  398. lineEndY = box.getMinY();
  399. }
  400. endY = lineEndY;
  401. }
  402. else if(lastWall == 'y')
  403. {
  404. double lineEndX = startX + dirX;
  405. for(CollisionBox box : allBoxes)
  406. {
  407. if(lineEndX >= box.getMaxX() || endY >= box.getMaxY() ||
  408. endX <= box.getMinX() || endY <= box.getMinY())
  409. {
  410. continue;
  411. }
  412. lineEndX = box.getMaxX();
  413. }
  414. endX = lineEndX;
  415. }
  416. }
  417. }
  418. motionX = endX - startX;
  419. motionY = endY - startY;
  420. if(dirY < 0 && motionY == 0)
  421. {
  422. onGround = true;
  423. }
  424. if(Math.abs(motionY) >= 1000 || Math.abs(motionX) >= 1000)
  425. {
  426. System.err.println("illegal movement " + motionX + " " + motionY);
  427. System.exit(0);
  428. }
  429. /*if(!onGround && Math.abs(dirY) - Math.abs(motionY) > 1)
  430. {
  431. System.out.println("\nVORHER " + dirX + " " + dirY);
  432. System.out.println("NACHHER " + motionX + " " + motionY);
  433. System.out.println("\nPOS " + posX + " " + posY);
  434. System.exit(0);
  435. }*/
  436. //System.exit(0);
  437. }
  438. private void doCollision()
  439. {
  440. CollisionBox box = getBox().copy();
  441. for(Face f : Face.values())
  442. {
  443. box.reset();
  444. box.expand(f.getCollisionOffsetX(), f.getCollisionOffsetY());
  445. level.getEntitiesCollidingWith(this, box).forEach(ent -> this.onCollideWithEntity(ent, f));
  446. level.getTilesCollidingWith(box).forEach(loc ->
  447. {
  448. this.onCollideWithTile(loc, f);
  449. loc.getTile().onEntityCollide(this, loc.getX(), loc.getY(), f);
  450. });
  451. }
  452. }
  453. public void onCollideWithTile(Location loc, Face face)
  454. {
  455. }
  456. public void onCollideWithEntity(Entity ent, Face face)
  457. {
  458. }
  459. //--------------------------------------------------------------------------
  460. // basic coord and motion stuff
  461. //--------------------------------------------------------------------------
  462. public final double getX()
  463. {
  464. return posX;
  465. }
  466. public final double getY()
  467. {
  468. return posY;
  469. }
  470. public final double getPreviousX()
  471. {
  472. return prevPosX;
  473. }
  474. public final double getPreviousY()
  475. {
  476. return prevPrevY;
  477. }
  478. public boolean isMoving()
  479. {
  480. return motionX != 0.0 || motionY != 0.0;
  481. }
  482. public final double getMotionX()
  483. {
  484. return motionX;
  485. }
  486. public final void setMotionX(double motionX)
  487. {
  488. prevMotionX = this.motionX;
  489. this.motionX = motionX;
  490. }
  491. public final double getMotionY()
  492. {
  493. return motionY;
  494. }
  495. public final void setMotionY(double motionY)
  496. {
  497. prevMotionY = this.motionY;
  498. this.motionY = motionY;
  499. }
  500. public final double getPreviousMotionX()
  501. {
  502. return prevMotionX;
  503. }
  504. public final double getPreviousMotionY()
  505. {
  506. return prevMotionY;
  507. }
  508. //--------------------------------------------------------------------------
  509. // rendering stuff
  510. //--------------------------------------------------------------------------
  511. public final double getWidth()
  512. {
  513. return width;
  514. }
  515. public final double getHeight()
  516. {
  517. return height;
  518. }
  519. public final double getRenderX()
  520. {
  521. return posX;
  522. }
  523. public final double getRenderY()
  524. {
  525. return posY + height;
  526. }
  527. public void setImage(Image image)
  528. {
  529. this.image = image;
  530. }
  531. public boolean drawImageFlipped()
  532. {
  533. return false;
  534. }
  535. public Image getImage()
  536. {
  537. return image;
  538. }
  539. public double getRenderOffsetX()
  540. {
  541. return 0;
  542. }
  543. public double getRenderOffsetY()
  544. {
  545. return 0;
  546. }
  547. }