Level.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. package me.hammerle.supersnuvi.gamelogic;
  2. import java.io.File;
  3. import java.util.HashMap;
  4. import java.util.LinkedList;
  5. import java.util.List;
  6. import java.util.TreeSet;
  7. import java.util.stream.Collectors;
  8. import me.hammerle.supersnuvi.entity.Entity;
  9. import me.hammerle.supersnuvi.entity.EntityBuilder;
  10. import me.hammerle.supersnuvi.entity.components.animator.EntityAnimator;
  11. import me.hammerle.supersnuvi.tiles.BottledSoulTile;
  12. import me.hammerle.supersnuvi.tiles.Location;
  13. import me.hammerle.supersnuvi.tiles.StartTile;
  14. import me.hammerle.supersnuvi.tiles.Tile;
  15. import me.hammerle.supersnuvi.util.CollisionBox;
  16. import me.hammerle.supersnuvi.rendering.IRenderer;
  17. public final class Level
  18. {
  19. private final StateRenderer state;
  20. private final IRenderer renderer;
  21. private final boolean worldLoaded;
  22. private final LevelData data;
  23. private final String name;
  24. private final HashMap<Integer, Entity> entities;
  25. private Entity hero;
  26. private int entityCounter;
  27. private boolean shouldReset;
  28. private boolean done;
  29. private int souls;
  30. private int maxSouls;
  31. private double time;
  32. private TreeSet<Point> spawns;
  33. public Level(StateRenderer state, File f)
  34. {
  35. this.state = state;
  36. this.renderer = state.getRenderer();
  37. this.data = new LevelData(f);
  38. this.worldLoaded = data.load();
  39. this.entities = new HashMap<>();
  40. this.entityCounter = 0;
  41. this.shouldReset = false;
  42. this.done = false;
  43. this.time = 0.0;
  44. this.spawns = new TreeSet<>();
  45. if(worldLoaded)
  46. {
  47. this.name = data.getString("name", "error");
  48. maxSouls = 0;
  49. data.forEachInteractTile((x, y, tile) ->
  50. {
  51. if(tile == BottledSoulTile.ID)
  52. {
  53. maxSouls++;
  54. }
  55. else if(tile == StartTile.ID)
  56. {
  57. spawns.add(new Point(x, y));
  58. }
  59. });
  60. }
  61. else
  62. {
  63. this.name = "error";
  64. maxSouls = 0;
  65. }
  66. // there must be at least one spawn
  67. if(spawns.isEmpty())
  68. {
  69. spawns.add(new Point(5, 5));
  70. }
  71. resetLevel();
  72. Entity test = EntityBuilder.buildTest(this, 100, 100);
  73. entities.put(entityCounter++, test);
  74. }
  75. // -------------------------------------------------------------------------
  76. // basic stuff
  77. // -------------------------------------------------------------------------
  78. public String getName()
  79. {
  80. return name;
  81. }
  82. public void finishLevel()
  83. {
  84. shouldReset = true;
  85. done = true;
  86. }
  87. public boolean shouldFinish()
  88. {
  89. return done;
  90. }
  91. public boolean shouldReset()
  92. {
  93. return shouldReset;
  94. }
  95. public void scheduleReset()
  96. {
  97. shouldReset = true;
  98. }
  99. public void resetLevel()
  100. {
  101. state.resetTiles();
  102. souls = 0;
  103. shouldReset = false;
  104. done = false;
  105. Entity h = spawnHero();
  106. hero = h;
  107. entities.clear();
  108. entities.put(entityCounter++, h);
  109. }
  110. public Entity spawnHero()
  111. {
  112. Entity h = hero;
  113. Point p;
  114. if(h == null || hero.getX() < 0)
  115. {
  116. // first spawn or out of map, use first spawn
  117. p = spawns.first();
  118. }
  119. else
  120. {
  121. // hero is somewhere in the map, getting last spawn
  122. p = spawns.floor(new Point(renderer.toBlock(hero.getX()), renderer.toBlock(hero.getY())));
  123. if(p == null)
  124. {
  125. p = spawns.first();
  126. }
  127. }
  128. return EntityBuilder.buildHero(this, renderer.toCoord(p.getX()), renderer.toCoord(p.getY()));
  129. }
  130. public void increaseSouls()
  131. {
  132. souls++;
  133. }
  134. // -------------------------------------------------------------------------
  135. // tick
  136. // -------------------------------------------------------------------------
  137. public void update()
  138. {
  139. if(worldLoaded)
  140. {
  141. if(hero.getY() < 0)
  142. {
  143. resetLevel();
  144. return;
  145. }
  146. time += 0.0125;
  147. state.tickTiles();
  148. // doing entity logic first
  149. entities.values().forEach(entity ->
  150. {
  151. entity.tick();
  152. });
  153. }
  154. }
  155. public void render()
  156. {
  157. if(worldLoaded)
  158. {
  159. // setting the right view center
  160. double rWidth = renderer.getWidth();
  161. double rHeight = renderer.getHeight();
  162. double centerX = Math.min(Math.max(0, hero.getX() - rWidth / 2), data.getWidth() * 32 - rWidth);
  163. double centerY = Math.min(Math.max(0, hero.getY() - rHeight / 2), data.getHeight() * 32 - rHeight);
  164. renderer.setViewCenter(centerX, centerY);
  165. int startX = renderer.getFirstVisibleBlockX();
  166. int startY = renderer.getFirstVisibleBlockY();
  167. int endX = Math.min(renderer.getLastVisibleBlockX() + 1, data.getWidth());
  168. int endY = Math.min(renderer.getLastVisibleBlockY() + 1, data.getHeight());
  169. data.forEachBackground((x, y, tile) ->
  170. {
  171. if(tile != -1)
  172. {
  173. Tile t = state.getTile(tile);
  174. renderer.drawBlockImage(t.getImage(x, y), x, y, t.getRenderOffsetX(), t.getRenderOffsetY());
  175. }
  176. }, startX, endX, startY, endY);
  177. entities.values().forEach(en ->
  178. {
  179. EntityAnimator a = en.getAnimator();
  180. a.bindSlot();
  181. if(a.drawImageFlipped())
  182. {
  183. renderer.drawFlippedImage(a.getImage(), a.getRenderX(), a.getRenderY());
  184. }
  185. else
  186. {
  187. renderer.drawImage(a.getImage(), a.getRenderX(), a.getRenderY());
  188. }
  189. });
  190. data.forEachForeground((x, y, tile) ->
  191. {
  192. if(tile != -1)
  193. {
  194. Tile t = state.getTile(tile);
  195. renderer.drawBlockImage(t.getImage(x, y), x, y, t.getRenderOffsetX(), t.getRenderOffsetY());
  196. }
  197. }, startX, endX, startY, endY);
  198. // menu rendering
  199. // |-------------------------------------|
  200. // | B00/00 | ENERGYENERGY | HPHP | TIME |
  201. // |-------------------------------------|
  202. renderer.prepareTextDrawing(255, 255, 255, 1.0, 40);
  203. {
  204. double baseLine = renderer.getTextHeight(1);
  205. double line = baseLine * 1.5;
  206. double y = baseLine * 0.25;
  207. renderer.save();
  208. {
  209. renderer.setFillColor(0, 0, 0, 0.5);
  210. renderer.fillRectangle(0, 0, renderer.getWidth(), line);
  211. }
  212. renderer.restore();
  213. // soul rendering
  214. char[] c = new char[5];
  215. if(souls <= 9)
  216. {
  217. c[0] = '0';
  218. c[1] = (char) (souls + '0');
  219. }
  220. else if(souls > 99)
  221. {
  222. c[0] = 'X';
  223. c[1] = 'X';
  224. }
  225. else
  226. {
  227. c[0] = (char) ((souls / 10) + '0');
  228. c[1] = (char) ((souls % 10) + '0');
  229. }
  230. c[2] = '/';
  231. if(maxSouls <= 9)
  232. {
  233. c[3] = '0';
  234. c[4] = (char) (maxSouls + '0');
  235. }
  236. else if(maxSouls > 99)
  237. {
  238. c[3] = 'X';
  239. c[4] = 'X';
  240. }
  241. else
  242. {
  243. c[3] = (char) ((maxSouls / 10) + '0');
  244. c[4] = (char) ((maxSouls % 10) + '0');
  245. }
  246. double x = 5;
  247. renderer.drawFixedImage(BottledSoulTile.IMAGE, x, 0, line, line);
  248. x += line;
  249. renderer.drawText(x, y, c);
  250. x += renderer.getTextWidth(5) + 10;
  251. // energy rendering
  252. double leftX = (renderer.getWidth() - 40.0 - renderer.getTextWidth(10) - line) * 0.5;
  253. //double energyWidth = renderer.getWidth() * 0.2;
  254. //double ex = (renderer.getWidth() / 2) - energyWidth;
  255. //energyWidth *= 2;
  256. renderer.save();
  257. {
  258. renderer.setFillColor(0, 0, 0, 1.0);
  259. renderer.fillRectangle(x, y, leftX, baseLine);
  260. renderer.setFillColor(0, 0, 150, 1.0);
  261. renderer.fillRectangle(x + 3, y + 3, (leftX * hero.getEnergy().getEnergyPercent()) - 6, baseLine - 6);
  262. }
  263. renderer.restore();
  264. x += leftX + 10;
  265. // hp rendering
  266. renderer.save();
  267. {
  268. renderer.setFillColor(0, 0, 0, 1.0);
  269. renderer.fillRectangle(x, y, leftX, baseLine);
  270. renderer.setFillColor(150, 0, 0, 1.0);
  271. renderer.fillRectangle(x + 3, y + 3, (leftX * hero.getHealth().getHealthPercent()) - 6, baseLine - 6);
  272. }
  273. renderer.restore();
  274. x += leftX + 10;
  275. // time rendering
  276. if(time >= 999.9)
  277. {
  278. c[0] = '9';
  279. c[1] = '9';
  280. c[2] = '9';
  281. c[3] = '.';
  282. c[4] = '9';
  283. renderer.drawText(x, y, c);
  284. }
  285. else
  286. {
  287. renderer.drawText(x, y, String.format("%05.1f", time).toCharArray());
  288. }
  289. }
  290. renderer.stopTextDrawing();
  291. }
  292. }
  293. // -------------------------------------------------------------------------
  294. // collision box, interaction layer
  295. // -------------------------------------------------------------------------
  296. public Tile getInteractionTile(int x, int y)
  297. {
  298. int i = data.getInteractionTile(x, y);
  299. if(i == -1)
  300. {
  301. return StateRenderer.FALLBACK_TILE;
  302. }
  303. return state.getInteractionTile(i);
  304. }
  305. public CollisionBox getTileMovementBox(int x, int y)
  306. {
  307. int i = data.getInteractionTile(x, y);
  308. if(i == -1)
  309. {
  310. return CollisionBox.NULL_BOX;
  311. }
  312. Tile tile = state.getInteractionTile(i);
  313. return tile.getMovementBox(x, y).reset().offset(renderer.toCoord(x), renderer.toCoord(y));
  314. }
  315. public LinkedList<Location> getTilesInMovementOf(CollisionBox cb)
  316. {
  317. LinkedList<Location> boxes = new LinkedList<>();
  318. int startX = renderer.toBlock(cb.getMinX());
  319. int endX = renderer.toBlock(cb.getMaxX());
  320. int startY = renderer.toBlock(cb.getMinY());
  321. int endY = renderer.toBlock(cb.getMaxY());
  322. for(int x = startX; x <= endX; x++)
  323. {
  324. for(int y = startY; y <= endY; y++)
  325. {
  326. if(getTileMovementBox(x, y).intersects(cb))
  327. {
  328. boxes.add(new Location(getInteractionTile(x, y), x, y));
  329. }
  330. }
  331. }
  332. return boxes;
  333. }
  334. public CollisionBox getTileCollisionBox(int x, int y)
  335. {
  336. int i = data.getInteractionTile(x, y);
  337. if(i == -1)
  338. {
  339. return CollisionBox.NULL_BOX;
  340. }
  341. Tile tile = state.getInteractionTile(i);
  342. return tile.getCollisionBox(x, y).reset().offset(renderer.toCoord(x), renderer.toCoord(y));
  343. }
  344. public LinkedList<Location> getTilesCollidingWith(CollisionBox cb)
  345. {
  346. LinkedList<Location> boxes = new LinkedList<>();
  347. int startX = renderer.toBlock(cb.getMinX());
  348. int endX = renderer.toBlock(cb.getMaxX());
  349. int startY = renderer.toBlock(cb.getMinY());
  350. int endY = renderer.toBlock(cb.getMaxY());
  351. for(int x = startX; x <= endX; x++)
  352. {
  353. for(int y = startY; y <= endY; y++)
  354. {
  355. if(getTileCollisionBox(x, y).intersects(cb))
  356. {
  357. boxes.add(new Location(getInteractionTile(x, y), x, y));
  358. }
  359. }
  360. }
  361. return boxes;
  362. }
  363. public List<Entity> getEntitiesCollidingWith(Entity not, CollisionBox cb)
  364. {
  365. return entities.values().stream().filter(ent -> ent != not && ent.getBox().intersects(cb)).collect(Collectors.toList());
  366. }
  367. public LinkedList<CollisionBox> getMovementBoxesAt(Entity not, CollisionBox cb)
  368. {
  369. LinkedList<CollisionBox> boxes = new LinkedList<>(/*entities.values().stream()
  370. .filter(ent -> ent != not && ent.getBox().intersects(cb))
  371. .map(ent -> ent.getBox())
  372. .collect(Collectors.toList())*/);
  373. int startX = renderer.toBlock(cb.getMinX());
  374. int endX = renderer.toBlock(cb.getMaxX());
  375. int startY = renderer.toBlock(cb.getMinY());
  376. int endY = renderer.toBlock(cb.getMaxY());
  377. CollisionBox box;
  378. for(int x = startX; x <= endX; x++)
  379. {
  380. for(int y = startY; y <= endY; y++)
  381. {
  382. box = getTileMovementBox(x, y);
  383. if(box.intersects(cb))
  384. {
  385. boxes.add(box.copy());
  386. }
  387. }
  388. }
  389. return boxes;
  390. }
  391. public Entity getHero()
  392. {
  393. return hero;
  394. }
  395. }