Level.java 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863
  1. package me.hammerle.supersnuvi.gamelogic;
  2. import java.io.File;
  3. import java.util.ArrayList;
  4. import java.util.HashMap;
  5. import java.util.LinkedList;
  6. import java.util.List;
  7. import java.util.TreeSet;
  8. import java.util.function.Consumer;
  9. import java.util.stream.Collectors;
  10. import me.hammerle.snuviengine.api.Shader;
  11. import me.hammerle.snuviengine.api.Texture;
  12. import me.hammerle.snuviengine.api.TextureRenderer;
  13. import me.hammerle.snuviengine.util.Rectangle;
  14. import me.hammerle.snuviscript.code.Script;
  15. import me.hammerle.supersnuvi.Game;
  16. import me.hammerle.supersnuvi.Keys;
  17. import me.hammerle.supersnuvi.entity.Entity;
  18. import me.hammerle.supersnuvi.entity.EntityBuilder;
  19. import me.hammerle.supersnuvi.tiles.Location;
  20. import me.hammerle.supersnuvi.tiles.Tile;
  21. import me.hammerle.supersnuvi.util.CollisionObject;
  22. import me.hammerle.supersnuvi.util.Utils;
  23. public final class Level implements ILevel
  24. {
  25. public final static float ERROR = 1f / 65536f;
  26. public final static Texture TILES = new Texture("resources/tiles.png");
  27. private final static Texture GUI = new Texture("resources/gui.png");
  28. private final static TextureRenderer GUI_RENDERER = new TextureRenderer(60);
  29. private final boolean worldLoaded;
  30. private final LevelData data;
  31. private final String fileName;
  32. private final String name;
  33. private final HashMap<Integer, Entity> entities = new HashMap<>();
  34. private final LinkedList<Entity> spawnQueue = new LinkedList<>();
  35. private Entity hero;
  36. private int entityCounter = 0;
  37. private boolean shouldReset = false;
  38. private boolean done = true; // this will be reseted in resetLevel()
  39. private int souls;
  40. private int maxSouls;
  41. private float time = 0.0f;
  42. private float cameraX = 0.0f;
  43. private float cameraY = 0.0f;
  44. private float oldCameraX = 0.0f;
  45. private float oldCameraY = 0.0f;
  46. private final int meshSize = 16;
  47. private final int meshLayers;
  48. private final int meshWidth;
  49. private final int meshHeight;
  50. private final TextureRenderer[][][] meshes;
  51. //private int tries = 7;
  52. private TreeSet<Point> spawns = new TreeSet<>();
  53. private Script levelScript = null;
  54. private final LinkedList<String> messages = new LinkedList<>();
  55. public Level(File f)
  56. {
  57. this.data = new LevelData(f);
  58. this.worldLoaded = data.load();
  59. if(worldLoaded)
  60. {
  61. this.name = data.getString("name", "error");
  62. meshLayers = data.getLayers() - 1;
  63. meshWidth = (int) Math.ceil((double) data.getWidth() / meshSize);
  64. meshHeight = (int) Math.ceil((double) data.getHeight() / meshSize);
  65. meshes = new TextureRenderer[meshLayers][meshWidth][meshHeight];
  66. for(int l = 0; l < meshLayers; l++)
  67. {
  68. for(int mx = 0; mx < meshWidth; mx++)
  69. {
  70. for(int my = 0; my < meshHeight; my++)
  71. {
  72. meshes[l][mx][my] = new TextureRenderer(meshSize * meshSize * 2);
  73. }
  74. }
  75. }
  76. // debug stuff
  77. /*if(name.equals("01-Tech Demo"))
  78. {
  79. int l = data.getBackgroundIndex();
  80. }*/
  81. // end debug stuff
  82. maxSouls = 0;
  83. data.forEachInteractTile((x, y, tile) ->
  84. {
  85. Tile t = Game.get().getTile(tile);
  86. if(t != null)
  87. {
  88. maxSouls += t.getBottleScore();
  89. }
  90. if(tile == 24) // start tile
  91. {
  92. spawns.add(new Point(x, y - 1));
  93. }
  94. });
  95. data.forEachEntity((x, y, tile) ->
  96. {
  97. if(tile == 1)
  98. {
  99. maxSouls++;
  100. }
  101. }, 0, data.getWidth(), 0, data.getHeight());
  102. }
  103. else
  104. {
  105. this.name = f.getName();
  106. maxSouls = 0;
  107. meshLayers = 0;
  108. meshWidth = 0;
  109. meshHeight = 0;
  110. meshes = new TextureRenderer[0][0][0];
  111. }
  112. fileName = f.getName();
  113. // there must be at least one spawn
  114. if(spawns.isEmpty())
  115. {
  116. spawns.add(new Point(5, 5));
  117. }
  118. // make sure hero is spawned before any script starts
  119. resetLevel();
  120. String scriptName = f.getPath().replace(".map", "");
  121. // mark current level as active to make currentLevel calls work
  122. Level l = Game.get().getCurrentLevel();
  123. Game.get().setCurrentLevel(this);
  124. levelScript = Game.get().getParser().startScript(false, ".snuvi", scriptName);
  125. // call level reset here, because levelScript was null in resetLevel()
  126. callEvent("level_reset");
  127. // mark previous level as active
  128. Game.get().setCurrentLevel(l);
  129. }
  130. // -------------------------------------------------------------------------
  131. // basic stuff
  132. // -------------------------------------------------------------------------
  133. @Override
  134. public Entity getHero()
  135. {
  136. return hero;
  137. }
  138. public String getFileName()
  139. {
  140. return fileName;
  141. }
  142. public String getName()
  143. {
  144. return name;
  145. }
  146. @Override
  147. public void finishLevel()
  148. {
  149. shouldReset = true;
  150. done = true;
  151. }
  152. public boolean shouldFinish()
  153. {
  154. return done;
  155. }
  156. public boolean shouldReset()
  157. {
  158. return shouldReset;
  159. }
  160. @Override
  161. public void scheduleReset()
  162. {
  163. shouldReset = true;
  164. }
  165. public boolean resetLevel()
  166. {
  167. Game.get().resetTiles(this);
  168. data.activateEntities();
  169. souls = 0;
  170. time = 0.0f;
  171. shouldReset = false;
  172. done = false;
  173. Entity h = spawnHero(true);
  174. hero = h;
  175. entities.clear();
  176. spawnEntity(hero);
  177. for(int l = 0; l < meshLayers; l++)
  178. {
  179. for(int x = 0; x < meshWidth; x++)
  180. {
  181. for(int y = 0; y < meshHeight; y++)
  182. {
  183. meshes[l][x][y].clear();
  184. }
  185. }
  186. }
  187. messages.clear();
  188. callEvent("level_reset");
  189. return false;
  190. }
  191. @Override
  192. public void spawnEntity(Entity ent)
  193. {
  194. spawnQueue.add(ent);
  195. }
  196. public Entity spawnHero(boolean first)
  197. {
  198. Entity h = hero;
  199. Point p;
  200. if(h == null || hero.getX() < 0 || first)
  201. {
  202. // first spawn or out of map, use first spawn
  203. p = spawns.first();
  204. }
  205. else
  206. {
  207. // hero is somewhere in the map, getting last spawn
  208. p = spawns.floor(new Point(Utils.toBlock(hero.getX()), Utils.toBlock(hero.getY())));
  209. if(p == null)
  210. {
  211. p = spawns.first();
  212. }
  213. }
  214. Entity newHero = EntityBuilder.buildHero(this, Utils.toCoord(p.getX()), Utils.toCoord(p.getY()));
  215. // reset the camera
  216. oldCameraX = -getViewX(newHero.getCenterX());
  217. oldCameraY = -getViewY(newHero.getCenterY());
  218. cameraX = oldCameraX;
  219. cameraY = oldCameraY;
  220. return newHero;
  221. }
  222. @Override
  223. public void increaseSouls(int score)
  224. {
  225. souls += score;
  226. }
  227. public int getCurrentBottles()
  228. {
  229. return souls;
  230. }
  231. public LevelData getData()
  232. {
  233. return data;
  234. }
  235. public float getTime()
  236. {
  237. return time;
  238. }
  239. @Override
  240. public int getWidth()
  241. {
  242. return data.getWidth();
  243. }
  244. @Override
  245. public int getHeight()
  246. {
  247. return data.getHeight();
  248. }
  249. // -------------------------------------------------------------------------
  250. // tick
  251. // -------------------------------------------------------------------------
  252. public void tick()
  253. {
  254. if(worldLoaded)
  255. {
  256. if(!messages.isEmpty() && Keys.ENTER.getTime() == 1)
  257. {
  258. messages.removeFirst();
  259. }
  260. time += Game.SECS_PER_TICK;
  261. Game.get().tickTiles();
  262. // doing entity logic first
  263. entities.values().removeIf(entity ->
  264. {
  265. entity.tick();
  266. if(entity.getHealth().shouldDespawn())
  267. {
  268. callEvent("entity_despawn", (sc) ->
  269. {
  270. sc.setVar("entity", entity);
  271. }, null);
  272. return true;
  273. }
  274. return false;
  275. });
  276. if(!spawnQueue.isEmpty())
  277. {
  278. spawnQueue.forEach(ent ->
  279. {
  280. entities.put(entityCounter++, ent);
  281. callEvent("entity_spawn", (sc) ->
  282. {
  283. sc.setVar("entity", ent);
  284. }, null);
  285. });
  286. spawnQueue.clear();
  287. }
  288. // calculate new camera position
  289. oldCameraX = cameraX;
  290. oldCameraY = cameraY;
  291. cameraX = -getViewX(hero.getCenterX());
  292. cameraY = -getViewY(hero.getCenterY());
  293. // entity spawn layer after camera update
  294. int startX = (int) (-cameraX / Tile.SIZE);
  295. int startY = (int) (-cameraY / Tile.SIZE);
  296. int endX = Math.min((int) Math.ceil((-cameraX + Shader.getViewWidth()) / Tile.SIZE), data.getWidth());
  297. int endY = Math.min((int) Math.ceil((-cameraY + Shader.getViewHeight()) / Tile.SIZE), data.getWidth());
  298. data.forEachEntity((x, y, tile) ->
  299. {
  300. if(tile > 0)
  301. {
  302. data.deactivateEntity(x, y);
  303. Entity ent = EntityBuilder.fromId(tile, this, Utils.toCoord(x), Utils.toCoord(y));
  304. if(ent != null)
  305. {
  306. entities.put(entityCounter++, ent);
  307. }
  308. }
  309. }, startX, endX, startY, endY);
  310. }
  311. }
  312. public String formatBottles(int bottles)
  313. {
  314. char[] c = new char[5];
  315. if(bottles <= 9)
  316. {
  317. c[0] = '0';
  318. c[1] = (char) (bottles + '0');
  319. }
  320. else if(bottles > 99)
  321. {
  322. c[0] = 'X';
  323. c[1] = 'X';
  324. }
  325. else
  326. {
  327. c[0] = (char) ((bottles / 10) + '0');
  328. c[1] = (char) ((bottles % 10) + '0');
  329. }
  330. c[2] = '/';
  331. int currentMaxSouls = Math.max(bottles, maxSouls);
  332. if(currentMaxSouls <= 9)
  333. {
  334. c[3] = '0';
  335. c[4] = (char) (currentMaxSouls + '0');
  336. }
  337. else if(currentMaxSouls > 99)
  338. {
  339. c[3] = 'X';
  340. c[4] = 'X';
  341. }
  342. else
  343. {
  344. c[3] = (char) ((currentMaxSouls / 10) + '0');
  345. c[4] = (char) ((currentMaxSouls % 10) + '0');
  346. }
  347. return new String(c);
  348. }
  349. public String formatTime(float time)
  350. {
  351. if(time == -1.0f)
  352. {
  353. return "-----";
  354. }
  355. else if(time >= 999.9f)
  356. {
  357. return "999.9";
  358. }
  359. return String.format("%05.1f", time);
  360. }
  361. private float getViewX(float x)
  362. {
  363. x -= Shader.getViewWidth() >> 1;
  364. if(x < 0)
  365. {
  366. return 0;
  367. }
  368. float max = data.getWidth() * Tile.SIZE - Shader.getViewWidth();
  369. if(x > max)
  370. {
  371. return max;
  372. }
  373. return x;
  374. }
  375. private float getViewY(float y)
  376. {
  377. y -= Shader.getViewHeight() >> 1;
  378. if(y < 0)
  379. {
  380. return 0;
  381. }
  382. float max = data.getHeight() * Tile.SIZE - Shader.getViewHeight();
  383. if(y > max)
  384. {
  385. return max;
  386. }
  387. return y;
  388. }
  389. @Override
  390. public void updateTile(int layer, int x, int y)
  391. {
  392. if(layer == data.getBackgroundIndex() + 1)
  393. {
  394. // do not update changes on entity layer
  395. return;
  396. }
  397. if(layer > data.getBackgroundIndex())
  398. {
  399. layer--;
  400. }
  401. meshes[layer][x / meshSize][y / meshSize].clear();
  402. }
  403. @Override
  404. public void updateTile(int x, int y)
  405. {
  406. updateTile(data.getBackgroundIndex(), x, y);
  407. }
  408. private void drawMesh(int l, int tl, int mx, int my)
  409. {
  410. TextureRenderer tr = meshes[l][mx][my];
  411. if(!tr.isBuilt())
  412. {
  413. int tsx = mx * meshSize;
  414. int tsy = my * meshSize;
  415. int tex = Math.min(tsx + meshSize, data.getWidth());
  416. int tey = Math.min(tsy + meshSize, data.getHeight());
  417. for(int x = tsx; x < tex; x++)
  418. {
  419. for(int y = tsy; y < tey; y++)
  420. {
  421. Tile t = Game.get().getTile(data.getTile(tl, x, y));
  422. if(t.shouldRender(x, y, this))
  423. {
  424. float minX = x * Tile.SIZE + t.getOffsetX();
  425. float minY = y * Tile.SIZE + t.getOffsetY();
  426. tr.addRectangle(minX, minY,
  427. minX + t.getWidth(), minY + t.getHeight(),
  428. t.getTextureMinX(x, y, this) + ERROR, t.getTextureMinY(x, y, this) + ERROR,
  429. t.getTextureMaxX(x, y, this) - ERROR, t.getTextureMaxY(x, y, this) - ERROR);
  430. }
  431. }
  432. }
  433. tr.build();
  434. }
  435. meshes[l][mx][my].draw();
  436. }
  437. private String[] split(String s)
  438. {
  439. ArrayList<String> list = new ArrayList<>();
  440. int old = 0;
  441. int index = 0;
  442. while(index < s.length())
  443. {
  444. switch(s.charAt(index))
  445. {
  446. case '\n':
  447. list.add(s.substring(old, index));
  448. list.add("\n");
  449. old = index + 1;
  450. break;
  451. case ' ':
  452. list.add(s.substring(old, index));
  453. old = index + 1;
  454. break;
  455. }
  456. index++;
  457. }
  458. if(old < s.length())
  459. {
  460. list.add(s.substring(old, index));
  461. }
  462. return list.toArray(new String[list.size()]);
  463. }
  464. public void renderTick(float lag)
  465. {
  466. if(worldLoaded)
  467. {
  468. float camX = Utils.interpolate(oldCameraX, cameraX, lag);
  469. float camY = Utils.interpolate(oldCameraY, cameraY, lag);
  470. Shader.translateTo(camX, camY);
  471. Shader.updateMatrix();
  472. int startX = (int) (-camX / (meshSize * Tile.SIZE));
  473. int startY = (int) (-camY / (meshSize * Tile.SIZE));
  474. int endX = (int) Math.ceil((-camX + Shader.getViewWidth()) / (meshSize * Tile.SIZE));
  475. int endY = (int) Math.ceil((-camY + Shader.getViewHeight()) / (meshSize * Tile.SIZE));
  476. startX = Math.min(Math.max(startX, 0), meshWidth);
  477. startY = Math.min(Math.max(startY, 0), meshHeight);
  478. endX = Math.min(Math.max(endX, 0), meshWidth);
  479. endY = Math.min(Math.max(endY, 0), meshHeight);
  480. // background
  481. Shader.setColorEnabled(false);
  482. Shader.setTextureEnabled(true);
  483. Shader.setBlendingEnabled(true);
  484. TILES.bind();
  485. int fromLayer = 0;
  486. int toLayer = data.getBackgroundIndex() + 1;
  487. for(int l = fromLayer; l < toLayer; l++)
  488. {
  489. for(int mx = startX; mx < endX; mx++)
  490. {
  491. for(int my = startY; my < endY; my++)
  492. {
  493. drawMesh(l, l, mx, my);
  494. }
  495. }
  496. }
  497. // entities
  498. for(Entity entity : entities.values())
  499. {
  500. entity.renderTick(lag);
  501. }
  502. // foreground
  503. Shader.setColorEnabled(false);
  504. Shader.setTextureEnabled(true);
  505. Shader.setBlendingEnabled(true);
  506. TILES.bind();
  507. fromLayer = toLayer + 1;
  508. toLayer = data.getLayers();
  509. for(int l = fromLayer; l < toLayer; l++)
  510. {
  511. for(int mx = startX; mx < endX; mx++)
  512. {
  513. for(int my = startY; my < endY; my++)
  514. {
  515. drawMesh(l - 1, l, mx, my);
  516. }
  517. }
  518. }
  519. // menu rendering
  520. Shader.translateTo(0.0f, 0.0f);
  521. Shader.updateMatrix();
  522. // grey background of clock and bottles
  523. float lineHeight = Shader.getFontRenderer().getHeight();
  524. float lineWidth = Shader.getFontRenderer().getWidth();
  525. Shader.setColorEnabled(true);
  526. Shader.setTextureEnabled(false);
  527. Shader.getColorRenderer().drawRectangle(0.0f, 0.0f, (lineWidth * 6.0f) + 10.0f, (lineHeight * 2.0f + 10.0f), 0x77000000);
  528. Shader.setTextureEnabled(true);
  529. float y = 5.0f;
  530. y = Shader.getFontRenderer().drawString(13.0f, y, formatBottles(souls));
  531. Shader.getFontRenderer().drawString(13.0f, y, formatTime(time));
  532. Shader.setColorEnabled(false);
  533. // draw messages
  534. if(!messages.isEmpty())
  535. {
  536. String[] message = split(messages.getFirst());
  537. int index = 0;
  538. ArrayList<StringBuilder> list = new ArrayList<>();
  539. list.add(new StringBuilder());
  540. float currentWidth = 0;
  541. float w = Shader.getViewWidth() - 26;
  542. for(String s : message)
  543. {
  544. if(s.equals("\n"))
  545. {
  546. currentWidth = w;
  547. continue;
  548. }
  549. Rectangle rec = Shader.getFontRenderer().getSize(s);
  550. // + lineWidth for the space
  551. if(currentWidth + rec.getWidth() + lineWidth < w)
  552. {
  553. currentWidth += rec.getWidth();
  554. StringBuilder sb = list.get(index);
  555. if(sb.length() == 0)
  556. {
  557. sb.append(s);
  558. }
  559. else
  560. {
  561. sb.append(" ");
  562. sb.append(s);
  563. currentWidth += lineWidth;
  564. }
  565. }
  566. else
  567. {
  568. StringBuilder sb = new StringBuilder();
  569. list.add(sb);
  570. index++;
  571. sb.append(s);
  572. currentWidth = rec.getWidth();
  573. }
  574. }
  575. float height = list.size() * lineHeight;
  576. Shader.setColorEnabled(true);
  577. Shader.setTextureEnabled(false);
  578. float messageY = Shader.getViewHeight() - height - 26;
  579. Shader.getColorRenderer().drawRectangle(0.0f, messageY, Shader.getViewWidth(), Shader.getViewHeight(), 0x77000000);
  580. messageY += 13;
  581. Shader.setTextureEnabled(true);
  582. for(StringBuilder sb : list)
  583. {
  584. messageY = Shader.getFontRenderer().drawString(13.0f, messageY, sb.toString());
  585. }
  586. Shader.setColorEnabled(false);
  587. }
  588. /*Shader.setColorEnabled(true);
  589. Shader.setTextureEnabled(false);
  590. Shader.getColorRenderer().drawRectangle(0.0f, 0.0f, (lineWidth * 6.0f) + 10.0f, (lineHeight * 2.0f + 10.0f), 0x77000000);
  591. Shader.setTextureEnabled(true);
  592. y = 5.0f;
  593. y = Shader.getFontRenderer().drawString(13.0f, y, formatBottles(souls));
  594. Shader.getFontRenderer().drawString(13.0f, y, formatTime(time));
  595. Shader.setColorEnabled(false);*/
  596. GUI.bind();
  597. GUI_RENDERER.clear();
  598. int scale = Shader.getViewScale();
  599. // bottles
  600. switch(scale)
  601. {
  602. case 1: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.0f, 0.046875f, 0.01171875f, 0.068359375f); break;
  603. case 2: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.01171875f, 0.046875f, 0.037109375f, 0.0859375f); break;
  604. default: GUI_RENDERER.addRectangle(6.0f, 4.0f, 12.0f, 14.0f, 0.037109375f, 0.046875f, 0.06640625f, 0.10546875f); break;
  605. }
  606. // clock
  607. switch(scale)
  608. {
  609. case 1: GUI_RENDERER.addRectangle(4.0f, y, 13.0f, y + 9.0f, 0.0f, 0.265625f, 0.017578125f, 0.283203125f); break;
  610. case 2: GUI_RENDERER.addRectangle(4.5f, y, 13.0f, y + 8.5f, 0.017578125f, 0.265625f, 0.05078125f, 0.298828125f); break;
  611. default: GUI_RENDERER.addRectangle(4.666666666f, y, 13.0f, y + 8.333333333f, 0.05078125f, 0.265625f, 0.099609375f, 0.314453125f); break;
  612. }
  613. float w = Shader.getViewWidth();
  614. // gui background
  615. GUI_RENDERER.addRectangle(w - 111.0f, 0.0f, w - 44.0f, 24.0f, 0.0f, 0.0f, 0.130859375f, 0.046875f);
  616. GUI_RENDERER.addRectangle(w - 44.0f, 0.0f, w - 18.0f, 16.0f, 0.130859375f, 0.0f, 0.181640625f, 0.03125f);
  617. GUI_RENDERER.addRectangle(w - 76.0f, 24.0f, w - 45.0f, 57.0f, 0.068359375f, 0.046875f, 0.12890625f, 0.111328125f);
  618. // health mirror
  619. int healthFrame = (int) (hero.getHealth().getHealthPercent() * 7);
  620. float leftMirror = (7 - healthFrame) * 0.0625f;
  621. GUI_RENDERER.addRectangle(w - 39.0f, 8.0f, w - 7.0f, 46.0f, leftMirror, 0.15625f, leftMirror + 0.0625f, 0.23046875f);
  622. // energy
  623. float energy = hero.getEnergy().getEnergyPercent();
  624. float fullEnd = w - 109.0f + 64.0f * energy;
  625. GUI_RENDERER.addRectangle(w - 109.0f, 13.0f, fullEnd, 21.0f, 0.0f, 0.140625f, 0.125f * energy, 0.15625f);
  626. GUI_RENDERER.addRectangle(fullEnd, 13.0f, w - 45.0f, 21.0f, 0.125f * energy, 0.125f, 0.125f, 0.140625f);
  627. // gui foreground
  628. GUI_RENDERER.addRectangle(w - 49.0f, 0.0f, w, 64.0f, 0.201171875f, 0.0f, 0.296875f, 0.125f);
  629. GUI_RENDERER.addRectangle(w - 109.0f, 15.0f, w - 106.0f, 18.0f, 0.15625f, 0.03125f, 0.162109375f, 0.037109375f);
  630. GUI_RENDERER.addRectangle(w - 97.0f, 15.0f, w - 92.0f, 20.0f, 0.1796875f, 0.03125f, 0.189453125f, 0.041015625f);
  631. // health number overlay
  632. GUI_RENDERER.addRectangle(w - 30.0f, 53.0f, w - 12.0f, 62.0f, leftMirror, 0.23828125f, leftMirror + 0.03515625f, 0.255859375f);
  633. GUI_RENDERER.build();
  634. GUI_RENDERER.draw();
  635. // dynamic clock hand
  636. Shader.setColorEnabled(true);
  637. Shader.setTextureEnabled(false);
  638. switch(scale)
  639. {
  640. case 1: Shader.translateTo(8.5f, y + 4.5f); break;
  641. case 2: Shader.translateTo(8.75f, y + 4.25f); break;
  642. default: Shader.translateTo(8.8333333333f, y + 4.16666667f); break;
  643. }
  644. Shader.rotate(-time * 72.0f);
  645. Shader.updateMatrix();
  646. Shader.getColorRenderer().drawRectangle(-0.5f / scale, -0.5f / scale, 0.5f / scale, 4.0f - 0.5f * scale, 0xFF000000);
  647. }
  648. }
  649. // -------------------------------------------------------------------------
  650. // collision box, interaction layer
  651. // -------------------------------------------------------------------------
  652. private Tile getInteractionTile(int x, int y)
  653. {
  654. int i = data.getInteractionTile(x, y);
  655. if(i == -1)
  656. {
  657. return Game.FALLBACK_TILE;
  658. }
  659. return Game.get().getTile(i);
  660. }
  661. private CollisionObject getMovementBox(int x, int y)
  662. {
  663. int i = data.getInteractionTile(x, y);
  664. if(i == -1)
  665. {
  666. return CollisionObject.NULL_BOX;
  667. }
  668. return Game.get().getTile(i).getMovementBox(x, y, this).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
  669. }
  670. @Override
  671. public List<CollisionObject> getMovementBoxesAt(CollisionObject box, Entity not)
  672. {
  673. List<CollisionObject> boxes;
  674. if(not != null)
  675. {
  676. boxes = getEntitiesCollidingWith(not, box).stream().map(ent -> ent.getBox()).collect(Collectors.toList());
  677. }
  678. else
  679. {
  680. boxes = new LinkedList<>();
  681. }
  682. int startX = Utils.toBlock(box.getMinX());
  683. int endX = Utils.toBlock(box.getMaxX());
  684. int startY = Utils.toBlock(box.getMinY());
  685. int endY = Utils.toBlock(box.getMaxY());
  686. for(int x = startX; x <= endX; x++)
  687. {
  688. for(int y = startY; y <= endY; y++)
  689. {
  690. CollisionObject cb = getMovementBox(x, y);
  691. if(cb.mayCollide(box) && cb != CollisionObject.NULL_BOX)
  692. {
  693. boxes.add(cb.copy());
  694. }
  695. }
  696. }
  697. return boxes;
  698. }
  699. private CollisionObject getCollisionBox(int x, int y)
  700. {
  701. int i = data.getInteractionTile(x, y);
  702. if(i == -1)
  703. {
  704. return CollisionObject.NULL_BOX;
  705. }
  706. Tile tile = Game.get().getTile(i);
  707. return tile.getCollisionBox(x, y, this).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
  708. }
  709. @Override
  710. public List<Location> getCollisionBoxesAt(CollisionObject cb)
  711. {
  712. LinkedList<Location> boxes = new LinkedList<>();
  713. int startX = Utils.toBlock(cb.getMinX());
  714. int endX = Utils.toBlock(cb.getMaxX());
  715. int startY = Utils.toBlock(cb.getMinY());
  716. int endY = Utils.toBlock(cb.getMaxY());
  717. for(int x = startX; x <= endX; x++)
  718. {
  719. for(int y = startY; y <= endY; y++)
  720. {
  721. if(getCollisionBox(x, y).isColliding(cb))
  722. {
  723. boxes.add(new Location(getInteractionTile(x, y), this, x, y));
  724. }
  725. }
  726. }
  727. return boxes;
  728. }
  729. @Override
  730. public List<Entity> getEntitiesCollidingWith(Entity not, CollisionObject cb)
  731. {
  732. return entities.values().stream().filter(ent -> ent != not && ent.getBox().isColliding(cb)).collect(Collectors.toList());
  733. }
  734. @Override
  735. public void callEvent(String name, Consumer<Script> before, Consumer<Script> after)
  736. {
  737. if(levelScript != null)
  738. {
  739. //Level l = Game.get().getCurrentLevel();
  740. //Game.get().setCurrentLevel(this);
  741. Game.get().getParser().callEvent(name, levelScript, before, after);
  742. //Game.get().setCurrentLevel(l);
  743. }
  744. }
  745. @Override
  746. public void callEvent(String name)
  747. {
  748. callEvent(name, null, null);
  749. }
  750. public void addMessage(String message)
  751. {
  752. messages.add(message);
  753. }
  754. }