Level.java 14 KB

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