Entity.java 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. package me.hammerle.supersnuvi.entity;
  2. import me.hammerle.supersnuvi.entity.components.ai.Controller;
  3. import me.hammerle.supersnuvi.entity.components.Energy;
  4. import me.hammerle.supersnuvi.entity.components.Health;
  5. import me.hammerle.supersnuvi.entity.components.ItemCollector;
  6. import me.hammerle.supersnuvi.entity.components.Movement;
  7. import me.hammerle.supersnuvi.tiles.Tile;
  8. import me.hammerle.supersnuvi.util.Face;
  9. import me.hammerle.supersnuvi.util.Utils;
  10. import me.hammerle.supersnuvi.gamelogic.Level;
  11. import me.hammerle.supersnuvi.math.Vector;
  12. import me.hammerle.supersnuvi.entity.components.IDespawn;
  13. public final class Entity
  14. {
  15. public static final float GRAVITY = 8.0f * Tile.SIZE_SCALE;
  16. public static final float STEP = 0.0625f;
  17. // this one is a little bit bigger to prevent wrong calculation
  18. // while going upwards
  19. public static final float UP_STEP = STEP + 0.00390625f;
  20. // the last position is used for interpolation during rendering
  21. private final Vector lastPos = new Vector();
  22. // the current position of the entity
  23. private final Vector pos = new Vector();
  24. // the width of the entity
  25. private final float width;
  26. // the height of the entity
  27. private final float height;
  28. // own force used by the controller component
  29. private final Vector ownForce = new Vector();
  30. // the motion, reduced by the movement collision check
  31. private final Vector motion = new Vector();
  32. // the friction reducing motion each tick
  33. private final Vector friction = new Vector(0.0f, 1.0f);
  34. // a flag indicating that the entity is on the ground
  35. private boolean onGround = true;
  36. // entity components
  37. private final Controller controller;
  38. private final Health health;
  39. private final Energy energy;
  40. private final Movement move;
  41. private final ItemCollector itemCollector;
  42. private final IDespawn onDespawn;
  43. // the type of the entity, used by snuvi script
  44. private final String type;
  45. protected Entity(float width, float height, Controller c, Health h, Energy e, Movement m, ItemCollector ic, IDespawn d, String type)
  46. {
  47. this.width = width;
  48. this.height = height;
  49. this.controller = c;
  50. this.health = h;
  51. this.energy = e;
  52. this.move = m;
  53. this.itemCollector = ic;
  54. this.onDespawn = d;
  55. this.type = type;
  56. }
  57. public String getType()
  58. {
  59. return type;
  60. }
  61. //--------------------------------------------------------------------------
  62. // components
  63. //--------------------------------------------------------------------------
  64. public boolean isAnimated()
  65. {
  66. return controller.isAnimated();
  67. }
  68. public Controller getController()
  69. {
  70. return controller;
  71. }
  72. public Health getHealth()
  73. {
  74. return health;
  75. }
  76. public Energy getEnergy()
  77. {
  78. return energy;
  79. }
  80. public Movement getMovement()
  81. {
  82. return move;
  83. }
  84. public ItemCollector getItemCollector()
  85. {
  86. return itemCollector;
  87. }
  88. public void onDespawn()
  89. {
  90. onDespawn.onDespawn(this);
  91. }
  92. //--------------------------------------------------------------------------
  93. // basic stuff
  94. //--------------------------------------------------------------------------
  95. public float getSquaredDistance(Entity e)
  96. {
  97. return Utils.getSquaredDistance(getCenterX(), getCenterY(), e.getCenterX(), e.getCenterY());
  98. }
  99. public float getX()
  100. {
  101. return pos.getX();
  102. }
  103. public float getY()
  104. {
  105. return pos.getY();
  106. }
  107. public void setPosition(float x, float y)
  108. {
  109. lastPos.set(x, y);
  110. pos.set(x, y);
  111. }
  112. public float getLastX()
  113. {
  114. return lastPos.getX();
  115. }
  116. public float getLastY()
  117. {
  118. return lastPos.getY();
  119. }
  120. public float getCenterX()
  121. {
  122. return pos.getX() + width * 0.5f;
  123. }
  124. public float getCenterY()
  125. {
  126. return pos.getY() + height * 0.5f;
  127. }
  128. public float getWidth()
  129. {
  130. return width;
  131. }
  132. public float getHeight()
  133. {
  134. return height;
  135. }
  136. public float getMotionX()
  137. {
  138. return motion.getX();
  139. }
  140. public float getMotionY()
  141. {
  142. return motion.getY();
  143. }
  144. public float getOwnForceX()
  145. {
  146. return ownForce.getX();
  147. }
  148. public float getOwnForceY()
  149. {
  150. return ownForce.getY();
  151. }
  152. public void applyOwnForce(float x, float y)
  153. {
  154. ownForce.add(x, y);
  155. }
  156. public void applyForce(float x, float y)
  157. {
  158. motion.add(x, y);
  159. }
  160. public void setFriction(float fx, float fy)
  161. {
  162. friction.set(fx, fy);
  163. }
  164. public boolean isAt(float x, float y)
  165. {
  166. return Math.abs(x - pos.getX()) < STEP && Math.abs(y - pos.getY()) < STEP;
  167. }
  168. public boolean isColliding(float minX, float minY, float maxX, float maxY)
  169. {
  170. return maxX > getX() && getX() + width > minX && maxY > getY() && getY() + height > minY;
  171. }
  172. private Face getCollidingFace(Entity o)
  173. {
  174. return Utils.getCollidingFace(
  175. o.getX(), o.getY(), o.getX() + o.getWidth(), o.getY() + o.getHeight(),
  176. getX(), getY(), getX() + getWidth(), getY() + getHeight());
  177. }
  178. public boolean jump()
  179. {
  180. if(onGround)
  181. {
  182. ownForce.addY(-move.getJumpPower());
  183. return true;
  184. }
  185. return false;
  186. }
  187. //--------------------------------------------------------------------------
  188. // ticking
  189. //--------------------------------------------------------------------------
  190. public void tick(Level level)
  191. {
  192. // reset own force
  193. ownForce.set(0.0f, 0.0f);
  194. // remeber last position for rendering
  195. lastPos.set(pos);
  196. // update onGround before controller tick
  197. // ToDo: does not work for ramps
  198. onGround = isOnTileGround(level);
  199. // tick components
  200. controller.tick(this, level);
  201. energy.tick(this);
  202. health.tick();
  203. // apply gravity if needed
  204. if(move.hasGravity(this))
  205. {
  206. motion.addY(GRAVITY * move.getGravityFactor());
  207. }
  208. // apply friction
  209. motion.mul(friction);
  210. // modify current motion by own force
  211. motion.add(ownForce);
  212. // (entity <-> tile) collision
  213. doTileCollision(level);
  214. // (entity <-> entity) collision
  215. level.forEachCollidingEntity(this, (ent) ->
  216. {
  217. controller.onCollideWithEntity(this, ent, getCollidingFace(ent));
  218. });
  219. }
  220. public void move(Level level)
  221. {
  222. reduceMotionByTiles(level);
  223. reduceMotionByEntities(level);
  224. pos.add(motion);
  225. }
  226. private boolean isOnTileGround(Level level)
  227. {
  228. float minX = pos.getX();
  229. float minY = pos.getY() + height;
  230. float maxX = pos.getX() + width;
  231. float maxY = pos.getY() + height + STEP * 2;
  232. int startX = Utils.toBlock(minX);
  233. int startY = Utils.toBlock(minY);
  234. int endX = Utils.toBlock(maxX);
  235. int endY = Utils.toBlock(maxY);
  236. for(int x = startX; x <= endX; x++)
  237. {
  238. for(int y = startY; y <= endY; y++)
  239. {
  240. Tile t = level.getInteractionTile(x, y);
  241. if(t.isMoveColliding(minX, minY, maxX, maxY, x, y, level))
  242. {
  243. return true;
  244. }
  245. }
  246. }
  247. return false;
  248. }
  249. private void doTileCollision(Level level)
  250. {
  251. float minX = pos.getX() - STEP;
  252. float minY = pos.getY() - STEP;
  253. float maxX = pos.getX() + width + STEP;
  254. float maxY = pos.getY() + height + STEP;
  255. int startX = Utils.toBlock(minX);
  256. int startY = Utils.toBlock(minY);
  257. int endX = Utils.toBlock(maxX);
  258. int endY = Utils.toBlock(maxY);
  259. for(int x = startX; x <= endX; x++)
  260. {
  261. for(int y = startY; y <= endY; y++)
  262. {
  263. Tile t = level.getInteractionTile(x, y);
  264. if(t.isColliding(minX, minY, maxX, maxY, x, y, level))
  265. {
  266. Face f = t.getCollidingFace(minX, minY, maxX, maxY, x, y, level);
  267. controller.onCollideWithTile(this, x, y, level, t, f);
  268. t.onEntityCollide(this, x, y, f.getOpposite(), level);
  269. }
  270. }
  271. }
  272. }
  273. private void reduceMotionByEntities(Level level)
  274. {
  275. }
  276. private boolean isCollidingWithTiles(int startX, int startY, int endX, int endY, Level level)
  277. {
  278. float minX = getX();
  279. float minY = getY();
  280. float maxX = minX + width;
  281. float maxY = minY + height;
  282. for(int x = startX; x <= endX; x++)
  283. {
  284. for(int y = startY; y <= endY; y++)
  285. {
  286. if(level.getInteractionTile(x, y).isMoveColliding(minX, minY, maxX, maxY, x, y, level))
  287. {
  288. return true;
  289. }
  290. }
  291. }
  292. return false;
  293. }
  294. public void reduceMotionByTiles(Level level)
  295. {
  296. if(move.canMoveEverywhere())
  297. {
  298. return;
  299. }
  300. float mx = motion.getX();
  301. float my = motion.getY();
  302. if(mx == 0.0f && my == 0.0f)
  303. {
  304. return;
  305. }
  306. int startX;
  307. int endX;
  308. if(mx < 0.0f)
  309. {
  310. startX = Utils.toBlock(pos.getX() + mx);
  311. endX = Utils.toBlock(pos.getX() + width);
  312. }
  313. else
  314. {
  315. startX = Utils.toBlock(pos.getX());
  316. endX = Utils.toBlock(pos.getX() + width + mx);
  317. }
  318. int startY;
  319. int endY;
  320. if(my < 0.0f)
  321. {
  322. startY = Utils.toBlock(pos.getY() + my);
  323. endY = Utils.toBlock(pos.getY() + height);
  324. }
  325. else
  326. {
  327. startY = Utils.toBlock(pos.getY());
  328. endY = Utils.toBlock(pos.getY() + height + my);
  329. }
  330. float oldPosX = pos.getX();
  331. float oldPosY = pos.getY();
  332. while(mx != 0.0 || my != 0.0)
  333. {
  334. if(mx != 0.0f)
  335. {
  336. float oldX = pos.getX();
  337. if(mx < 0.0)
  338. {
  339. if(mx > -STEP)
  340. {
  341. pos.addX(mx);
  342. mx = 0.0f;
  343. }
  344. else
  345. {
  346. pos.addX(-STEP);
  347. mx += STEP;
  348. }
  349. }
  350. else if(mx > 0.0)
  351. {
  352. if(mx < STEP)
  353. {
  354. pos.addX(mx);
  355. mx = 0.0f;
  356. }
  357. else
  358. {
  359. pos.addX(STEP);
  360. mx -= STEP;
  361. }
  362. }
  363. if(isCollidingWithTiles(startX, startY, endX, endY, level))
  364. {
  365. pos.addY(-UP_STEP);
  366. if(isCollidingWithTiles(startX, startY, endX, endY, level))
  367. {
  368. pos.addY(UP_STEP);
  369. pos.setX(oldX);
  370. mx = 0.0f;
  371. }
  372. }
  373. }
  374. if(my != 0.0f)
  375. {
  376. float oldY = pos.getY();
  377. if(my < 0.0)
  378. {
  379. if(my > -STEP)
  380. {
  381. pos.addY(my);
  382. my = 0.0f;
  383. }
  384. else
  385. {
  386. pos.addY(-STEP);
  387. my += STEP;
  388. }
  389. }
  390. else if(my > 0.0)
  391. {
  392. if(my < STEP)
  393. {
  394. pos.addY(my);
  395. my = 0.0f;
  396. }
  397. else
  398. {
  399. pos.addY(STEP);
  400. my -= STEP;
  401. }
  402. }
  403. if(isCollidingWithTiles(startX, startY, endX, endY, level))
  404. {
  405. my = 0.0f;
  406. pos.setY(oldY);
  407. }
  408. }
  409. }
  410. motion.set(pos.getX() - oldPosX, pos.getY() - oldPosY);
  411. pos.set(oldPosX, oldPosY);
  412. }
  413. public void renderTick(float lag)
  414. {
  415. controller.renderTick(this, lag);
  416. }
  417. //--------------------------------------------------------------------------
  418. // gravity, friction
  419. //--------------------------------------------------------------------------
  420. public boolean isOnGround()
  421. {
  422. return onGround;
  423. }
  424. }