StateRenderer.java 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. package me.hammerle.supersnuvi.gamelogic;
  2. import java.io.File;
  3. import java.util.Arrays;
  4. import java.util.HashMap;
  5. import me.hammerle.supersnuvi.javafx.IKeyBinding;
  6. import me.hammerle.supersnuvi.javafx.KeyHandler;
  7. import me.hammerle.supersnuvi.savegame.SimpleConfig;
  8. import me.hammerle.supersnuvi.tiles.*;
  9. import me.hammerle.supersnuvi.util.SoundUtils;
  10. import me.hammerle.supersnuvi.rendering.IRenderer;
  11. public class StateRenderer
  12. {
  13. // constants
  14. public static final ColoredBaseTile FALLBACK_TILE = new ColoredBaseTile(0, 0, 0, 0);
  15. // rendering
  16. private final IRenderer renderer;
  17. // tiles
  18. private final HashMap<Integer, Tile> registeredTiles;
  19. // levels
  20. private Level currentLevel;
  21. private final Level[] levels;
  22. private int levelIndex;
  23. public static final SimpleConfig CONFIG = new SimpleConfig("options.txt", true);
  24. private static final SimpleConfig[] SAVE_SLOTS = new SimpleConfig[]
  25. {
  26. new SimpleConfig("slot1.txt", true), new SimpleConfig("slot2.txt", true), new SimpleConfig("slot3.txt", true)
  27. };
  28. private int screen = 0;
  29. private int startScreenIndex = 0;
  30. private int optionScreenIndex = 0;
  31. private int slotScreenIndex = 0;
  32. private boolean optionsDirty = false;
  33. // sound
  34. private static boolean sound = CONFIG.getBoolean("sound", false);
  35. public static boolean isSoundEnabled()
  36. {
  37. return sound;
  38. }
  39. public StateRenderer(IRenderer renderer)
  40. {
  41. this.renderer = renderer;
  42. this.registeredTiles = new HashMap<>();
  43. registerTiles();
  44. File[] files = new File("./levels").listFiles();
  45. Arrays.sort(files, (o1, o2) -> o1.compareTo(o2));
  46. levels = new Level[files.length];
  47. for(int i = 0; i < levels.length; i++)
  48. {
  49. levels[i] = new Level(this, files[i]);
  50. }
  51. currentLevel = null;
  52. levelIndex = 0;
  53. }
  54. // -------------------------------------------------------------------------
  55. // tile stuff
  56. // -------------------------------------------------------------------------
  57. private void registerTiles()
  58. {
  59. // dirt
  60. for(int i = 0; i < 16; i++)
  61. {
  62. registeredTiles.put(i, new BaseBoxTile("dirt/dirt" + i));
  63. }
  64. // grass
  65. registeredTiles.put(20, new BaseBoxTile("grass/grass4"));
  66. registeredTiles.put(21, new BaseBoxTile("grass/grass9"));
  67. registeredTiles.put(22, new BaseBoxTile("grass/grass8"));
  68. registeredTiles.put(23, new BaseBoxTile("grass/grass10"));
  69. registeredTiles.put(28, new BaseBoxTile("grass/grass7"));
  70. registeredTiles.put(29, new BaseBoxTile("grass/grass15"));
  71. registeredTiles.put(30, new BaseBoxTile("grass/grass16"));
  72. registeredTiles.put(31, new BaseBoxTile("grass/grass14"));
  73. // bottled soul
  74. registeredTiles.put(32, new BottledSoulTile(1));
  75. registeredTiles.put(33, new BottledSoulTile(2));
  76. registeredTiles.put(34, new BottledSoulTile(3));
  77. // bounce shroom
  78. registeredTiles.put(48, new TrampolinTile());
  79. // crumbling stones
  80. registeredTiles.put(64, new CrumblingStoneTile());
  81. // spike trap
  82. registeredTiles.put(80, new SpikeTile());
  83. // water
  84. registeredTiles.put(96, new WaterTile(true));
  85. registeredTiles.put(97, new WaterTile(false));
  86. // snuvi start block
  87. registeredTiles.put(StartTile.ID, new StartTile());
  88. // sky
  89. registeredTiles.put(128, new SkyTile());
  90. // ice
  91. registeredTiles.put(144, new SlipperyTile("ice/ice"));
  92. // slippery slime
  93. registeredTiles.put(148, new SlipperyTile("slippery_slime/slippery_slime148"));
  94. registeredTiles.put(149, new SlipperyTile("slippery_slime/slippery_slime149"));
  95. registeredTiles.put(150, new SlipperyTile("slippery_slime/slippery_slime150"));
  96. registeredTiles.put(151, new SlipperyTile("slippery_slime/slippery_slime151"));
  97. registeredTiles.put(156, new SlipperyTile("slippery_slime/slippery_slime156"));
  98. registeredTiles.put(157, new SlipperyTile("slippery_slime/slippery_slime157"));
  99. registeredTiles.put(158, new SlipperyTile("slippery_slime/slippery_slime158"));
  100. registeredTiles.put(159, new SlipperyTile("slippery_slime/slippery_slime159"));
  101. // end level
  102. registeredTiles.put(160, new GoalTile("end/end"));
  103. registeredTiles.put(161, new GoalTile("end/end2"));
  104. // thorns
  105. registeredTiles.put(176, new KillTile("thorns/thorns_bottom", 2));
  106. registeredTiles.put(177, new KillTile("thorns/thorns_mid", 2));
  107. registeredTiles.put(178, new KillTile("thorns/thorns_top", 2));
  108. registeredTiles.put(179, new KillTile("thorns/thorns_bottom", -3));
  109. registeredTiles.put(180, new KillTile("thorns/thorns_mid", -3));
  110. registeredTiles.put(181, new KillTile("thorns/thorns_top", -3));
  111. // decoration shrooms
  112. registeredTiles.put(208, new DecoShroomTile("shrooms/shroom"));
  113. registeredTiles.put(209, new DecoShroomTile("shrooms/shroom2"));
  114. registeredTiles.put(210, new DecoShroomTile("shrooms/shroom3"));
  115. registeredTiles.put(211, new DecoShroomTile("shrooms/shroom4"));
  116. // fog, starting late to make length changes possible
  117. for(int i = 0; i < 16; i++)
  118. {
  119. registeredTiles.put(16000 + i, new FogTile((i + 1) / 16.0));
  120. }
  121. // london stuff
  122. registeredTiles.put(224, new BaseBoxTile("london_background/london_background"));
  123. // london streets
  124. for(int i = 0; i < 16; i++)
  125. {
  126. registeredTiles.put(240 + i, new BaseBoxTile("london_streets/london_streets" + i));
  127. }
  128. // london street light
  129. for(int i = 0; i < 25; i++)
  130. {
  131. registeredTiles.put(256 + i, new BaseBoxTile("street_light/street_light" + (i % 5) + "_" + (i / 5)));
  132. }
  133. }
  134. public Tile getTile(int id)
  135. {
  136. Tile tile = registeredTiles.get(id);
  137. if(tile == null)
  138. {
  139. return FALLBACK_TILE;
  140. }
  141. return tile;
  142. }
  143. public Tile getInteractionTile(int id)
  144. {
  145. return registeredTiles.getOrDefault(id, FALLBACK_TILE);
  146. }
  147. public void resetTiles()
  148. {
  149. registeredTiles.values().forEach(v -> v.reset());
  150. }
  151. // -------------------------------------------------------------------------
  152. // tick, rendering
  153. // -------------------------------------------------------------------------
  154. public IRenderer getRenderer()
  155. {
  156. return renderer;
  157. }
  158. private static char[] getCharLine(char start, char mid, char end, char spacer)
  159. {
  160. char[] c = new char[MENU_WIDTH];
  161. Arrays.fill(c, 1, c.length - 1, mid);
  162. c[0] = start;
  163. c[c.length - 1] = end;
  164. c[c.length - 3] = spacer;
  165. return c;
  166. }
  167. private static char[] getCharLine(char start, String s, char end)
  168. {
  169. char[] chars = new char[MENU_WIDTH];
  170. chars[0] = start;
  171. chars[chars.length - 1] = end;
  172. int border = Math.min(MENU_WIDTH - 2, s.length());
  173. for(int i = 0; i < border; i++)
  174. {
  175. chars[i + 1] = s.charAt(i);
  176. }
  177. return chars;
  178. }
  179. private static char[] getLevelCharLine(char start, char mid, char end, char spacer)
  180. {
  181. char[] c = new char[MENU_WIDTH];
  182. Arrays.fill(c, 1, c.length - 1, mid);
  183. c[0] = start;
  184. c[c.length - 1] = end;
  185. c[c.length - 7] = spacer;
  186. c[c.length - 13] = spacer;
  187. return c;
  188. }
  189. private static char[] getMoreLevelCharLine(char start, char mid, char end, char spacer)
  190. {
  191. char[] c = new char[MENU_WIDTH];
  192. Arrays.fill(c, 1, c.length - 1, mid);
  193. c[0] = start;
  194. c[1] = '.';
  195. c[2] = '.';
  196. c[3] = '.';
  197. c[c.length - 1] = end;
  198. c[c.length - 7] = spacer;
  199. c[c.length - 13] = spacer;
  200. return c;
  201. }
  202. private static final int MENU_WIDTH = 36;
  203. private static final int MENU_MAX = 5;
  204. private static final char[] TABLE_TOP = getCharLine((char) 131, (char) 136, (char) 133, (char) 136);
  205. private static final char[] TABLE_HEADING = getCharLine((char) 134, "Choose a Level ...", (char) 134);
  206. private static final char[] TABLE_MID = getLevelCharLine((char) 130, (char) 136, (char) 132, (char) 129);
  207. private static final char[] TABLE_BOTTOM = getLevelCharLine((char) 137, (char) 136, (char) 138, (char) 135);
  208. private static final char[] TABLE_MORE = getMoreLevelCharLine((char) 134, ' ', (char) 134, (char) 134);
  209. private static final char[][] START_UP = new char[][]
  210. {
  211. getCharLine((char) 131, (char) 136, (char) 133, (char) 136),
  212. getCharLine((char) 134, "Super Snuvi", (char) 134),
  213. getCharLine((char) 130, (char) 136, (char) 132, (char) 136),
  214. getCharLine((char) 134, "Start Game", (char) 134),
  215. getCharLine((char) 134, "Options", (char) 134),
  216. getCharLine((char) 134, "Exit Game", (char) 134),
  217. getCharLine((char) 137, (char) 136, (char) 138, (char) 136)
  218. };
  219. private static final char[][] SLOTS = new char[][]
  220. {
  221. getCharLine((char) 131, (char) 136, (char) 133, (char) 136),
  222. getCharLine((char) 134, "Choose a Savegame ...", (char) 134),
  223. getCharLine((char) 130, (char) 136, (char) 132, (char) 136),
  224. getCharLine((char) 134, "Slot 1", (char) 134),
  225. getCharLine((char) 134, "Slot 2", (char) 134),
  226. getCharLine((char) 134, "Slot 3", (char) 134),
  227. getCharLine((char) 134, "Back", (char) 134),
  228. getCharLine((char) 137, (char) 136, (char) 138, (char) 136)
  229. };
  230. private static final char[][] OPTIONS = new char[][]
  231. {
  232. getCharLine((char) 131, (char) 136, (char) 133, (char) 136),
  233. getCharLine((char) 134, "Options", (char) 134),
  234. getCharLine((char) 130, (char) 136, (char) 132, (char) 136),
  235. getCharLine((char) 134, "Sound", (char) 134),
  236. getCharLine((char) 134, "K: Up", (char) 134),
  237. getCharLine((char) 134, "K: Down", (char) 134),
  238. getCharLine((char) 134, "K: Left", (char) 134),
  239. getCharLine((char) 134, "K: Rright", (char) 134),
  240. getCharLine((char) 134, "K: Jump", (char) 134),
  241. getCharLine((char) 134, "K: Run", (char) 134),
  242. getCharLine((char) 134, "K: Back", (char) 134),
  243. getCharLine((char) 134, "K: Enter", (char) 134),
  244. getCharLine((char) 134, "K: Combat", (char) 134),
  245. getCharLine((char) 134, "K: Switch Face", (char) 134),
  246. getCharLine((char) 134, "K: Dash/Dodge", (char) 134),
  247. getCharLine((char) 134, "K: Dash/Dodge", (char) 134),
  248. getCharLine((char) 134, "K: Block", (char) 134),
  249. getCharLine((char) 134, "K: Attack", (char) 134),
  250. getCharLine((char) 134, "", (char) 134), // save
  251. getCharLine((char) 134, "Back", (char) 134),
  252. getCharLine((char) 137, (char) 136, (char) 138, (char) 136)
  253. };
  254. private static final char[] SAVE_OVERLAY = getCharLine(' ', "Save", ' ');
  255. private static final int OPTION_OFFSET = 10;
  256. static
  257. {
  258. int pos = OPTIONS[0].length - OPTION_OFFSET;
  259. OPTIONS[2][pos] = (char) 129;
  260. for(int i = 0; i < 17; i++)
  261. {
  262. OPTIONS[3 + i][pos] = (char) 134;
  263. }
  264. OPTIONS[20][pos] = (char) 135;
  265. }
  266. private void menuMove(Runnable esc, Runnable enter, Runnable down, Runnable up, Runnable end)
  267. {
  268. if(KeyHandler.ESCAPE.wasJustReleased())
  269. {
  270. esc.run();
  271. return;
  272. }
  273. if(KeyHandler.ENTER.wasJustReleased())
  274. {
  275. enter.run();
  276. return;
  277. }
  278. if(KeyHandler.DOWN.getDownTime() > 16)
  279. {
  280. KeyHandler.DOWN.resetTime();
  281. down.run();
  282. }
  283. else if(KeyHandler.UP.getDownTime() > 16)
  284. {
  285. KeyHandler.UP.resetTime();
  286. up.run();
  287. }
  288. else if(KeyHandler.DOWN.wasJustReleased())
  289. {
  290. down.run();
  291. }
  292. else if(KeyHandler.UP.wasJustReleased())
  293. {
  294. up.run();
  295. }
  296. end.run();
  297. }
  298. public void update()
  299. {
  300. if(currentLevel != null)
  301. {
  302. SoundUtils.playSound(SoundUtils.Sound.SONG_1);
  303. SoundUtils.stopSound(SoundUtils.Sound.MENU_MUSIC);
  304. currentLevel.update();
  305. // doing that here to prevent concurent modification
  306. if(currentLevel.shouldFinish())
  307. {
  308. String base = "level." + currentLevel.getName();
  309. SimpleConfig sp = SAVE_SLOTS[slotScreenIndex];
  310. // save success
  311. sp.set(base, true);
  312. // update time, if a new highscore was scored
  313. double time = sp.getDouble(base + ".time", Integer.MAX_VALUE);
  314. if(currentLevel.getTime() < time)
  315. {
  316. sp.set(base + ".time", currentLevel.getTime());
  317. }
  318. // update bottles, if a new highscore was scored
  319. int bottles = sp.getInt(base + ".bottles", 0);
  320. if(currentLevel.getCurrentBottles() > bottles)
  321. {
  322. sp.set(base + ".bottles", currentLevel.getCurrentBottles());
  323. }
  324. // final save
  325. sp.save();
  326. currentLevel.resetLevel();
  327. currentLevel = null;
  328. return;
  329. }
  330. if(currentLevel.shouldReset())
  331. {
  332. if(currentLevel.resetLevel())
  333. {
  334. currentLevel = null;
  335. }
  336. }
  337. if(KeyHandler.ESCAPE.wasJustReleased())
  338. {
  339. currentLevel = null;
  340. }
  341. }
  342. else
  343. {
  344. SoundUtils.playSound(SoundUtils.Sound.MENU_MUSIC);
  345. SoundUtils.stopSound(SoundUtils.Sound.SONG_1);
  346. switch(screen)
  347. {
  348. case 0: // start screen
  349. {
  350. menuMove(() ->
  351. {
  352. // do nothing on escape in start screen
  353. }, () ->
  354. {
  355. switch(startScreenIndex)
  356. {
  357. case 0:
  358. screen = 1;
  359. break;
  360. case 1:
  361. screen = 2;
  362. break;
  363. case 2:
  364. System.exit(0);
  365. break;
  366. }
  367. }, () -> startScreenIndex++, () -> startScreenIndex--, () ->
  368. {
  369. if(startScreenIndex < 0)
  370. {
  371. startScreenIndex = 0;
  372. }
  373. else if(startScreenIndex >= 3)
  374. {
  375. startScreenIndex = 2;
  376. }
  377. });
  378. break;
  379. }
  380. case 1: // slot screen
  381. {
  382. menuMove(() ->
  383. {
  384. screen = 0;
  385. }, () ->
  386. {
  387. if(slotScreenIndex == 3)
  388. {
  389. screen = 0;
  390. return;
  391. }
  392. screen = 3;
  393. }, () -> slotScreenIndex++, () -> slotScreenIndex--, () ->
  394. {
  395. if(slotScreenIndex < 0)
  396. {
  397. slotScreenIndex = 0;
  398. }
  399. else if(slotScreenIndex >= 4)
  400. {
  401. slotScreenIndex = 3;
  402. }
  403. });
  404. break;
  405. }
  406. case 2: // option screen
  407. {
  408. menuMove(() ->
  409. {
  410. screen = 0;
  411. }, () ->
  412. {
  413. switch(optionScreenIndex)
  414. {
  415. case 0: // toggle sound
  416. sound = !sound;
  417. if(!sound)
  418. {
  419. SoundUtils.turnSoundOff();
  420. }
  421. optionsDirty = true;
  422. break;
  423. case 15: // save options
  424. for(IKeyBinding binding : KeyHandler.ARRAY)
  425. {
  426. CONFIG.set(binding.getName(), binding.getKeyName());
  427. }
  428. CONFIG.set("sound", sound);
  429. CONFIG.save();
  430. optionsDirty = false;
  431. break;
  432. case 16: // go back
  433. screen = 0;
  434. break;
  435. default: // rebind keys
  436. KeyHandler.rebindKey(KeyHandler.ARRAY[optionScreenIndex - 1]);
  437. optionsDirty = true;
  438. break;
  439. }
  440. }, () -> optionScreenIndex++, () -> optionScreenIndex--, () ->
  441. {
  442. if(optionScreenIndex < 0)
  443. {
  444. optionScreenIndex = 0;
  445. }
  446. else if(optionScreenIndex >= OPTIONS.length - 4)
  447. {
  448. optionScreenIndex = OPTIONS.length - 5;
  449. }
  450. });
  451. break;
  452. }
  453. case 3: // level choose screen
  454. {
  455. menuMove(() ->
  456. {
  457. screen = 1;
  458. }, () -> currentLevel = levels[levelIndex], () -> levelIndex++, () -> levelIndex--, () ->
  459. {
  460. if(levelIndex < 0)
  461. {
  462. levelIndex = 0;
  463. }
  464. else if(levelIndex >= levels.length)
  465. {
  466. levelIndex = levels.length - 1;
  467. }
  468. });
  469. break;
  470. }
  471. }
  472. }
  473. }
  474. public void render()
  475. {
  476. if(currentLevel != null)
  477. {
  478. currentLevel.render();
  479. return;
  480. }
  481. switch(screen)
  482. {
  483. case 0:
  484. {
  485. renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
  486. double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
  487. double y = (renderer.getHeight() - renderer.getTextHeight(7)) * 0.5;
  488. double line = renderer.getTextHeight(1);
  489. renderer.save();
  490. renderer.setFillColor(128, 128, 128, 1.0);
  491. renderer.fillRectangle(
  492. x + renderer.getTextWidth(1),
  493. y + renderer.getTextHeight(startScreenIndex + 3) - 1,
  494. renderer.getTextWidth(MENU_WIDTH - 2),
  495. line);
  496. renderer.restore();
  497. for(char[] c : START_UP)
  498. {
  499. renderer.drawText(x, y, c);
  500. y += line;
  501. }
  502. break;
  503. }
  504. case 1:
  505. {
  506. renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
  507. double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
  508. double y = (renderer.getHeight() - renderer.getTextHeight(7)) * 0.5;
  509. double line = renderer.getTextHeight(1);
  510. renderer.save();
  511. renderer.setFillColor(128, 128, 128, 1.0);
  512. renderer.fillRectangle(
  513. x + renderer.getTextWidth(1),
  514. y + renderer.getTextHeight(slotScreenIndex + 3) - 1,
  515. renderer.getTextWidth(MENU_WIDTH - 2),
  516. line);
  517. renderer.restore();
  518. for(char[] c : SLOTS)
  519. {
  520. renderer.drawText(x, y, c);
  521. y += line;
  522. }
  523. break;
  524. }
  525. case 2:
  526. {
  527. renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
  528. double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
  529. double y = (renderer.getHeight() - renderer.getTextHeight(OPTIONS.length)) * 0.5;
  530. double line = renderer.getTextHeight(1);
  531. renderer.save();
  532. renderer.setFillColor(128, 128, 128, 1.0);
  533. renderer.fillRectangle(
  534. x + renderer.getTextWidth(1),
  535. y + renderer.getTextHeight(optionScreenIndex + 3) - 1,
  536. renderer.getTextWidth(MENU_WIDTH - (OPTION_OFFSET + 1)),
  537. line);
  538. renderer.fillRectangle(
  539. x + renderer.getTextWidth(MENU_WIDTH - (OPTION_OFFSET - 1)),
  540. y + renderer.getTextHeight(optionScreenIndex + 3) - 1,
  541. renderer.getTextWidth(OPTION_OFFSET - 2),
  542. line);
  543. renderer.restore();
  544. // option top
  545. for(int i = 0; i < 3; i++)
  546. {
  547. renderer.drawText(x, y, OPTIONS[i]);
  548. y += line;
  549. }
  550. // sound option
  551. if(sound)
  552. {
  553. int pos = OPTIONS[3].length - OPTION_OFFSET + 1;
  554. OPTIONS[3][pos] = 'y';
  555. OPTIONS[3][pos + 1] = 'e';
  556. OPTIONS[3][pos + 2] = 's';
  557. }
  558. else
  559. {
  560. int pos = OPTIONS[3].length - OPTION_OFFSET + 1;
  561. OPTIONS[3][pos] = 'n';
  562. OPTIONS[3][pos + 1] = 'o';
  563. OPTIONS[3][pos + 2] = ' ';
  564. }
  565. renderer.drawText(x, y, OPTIONS[3]);
  566. y += line;
  567. // key binding options
  568. int end = 4 + KeyHandler.ARRAY.length;
  569. for(int i = 4; i < end; i++)
  570. {
  571. String name = KeyHandler.ARRAY[i - 4].toString();
  572. System.arraycopy(name.toCharArray(), 0, OPTIONS[i],
  573. OPTIONS[i].length - OPTION_OFFSET + 1, Math.min(OPTION_OFFSET - 2, name.length()));
  574. renderer.drawText(x, y, OPTIONS[i]);
  575. y += line;
  576. }
  577. // save
  578. renderer.drawText(x, y, OPTIONS[end]);
  579. if(optionsDirty)
  580. {
  581. renderer.save();
  582. renderer.setFillColor(180, 0, 0, 1);
  583. renderer.setStrokeColor(180, 0, 0, 1);
  584. renderer.drawText(x, y, SAVE_OVERLAY);
  585. renderer.restore();
  586. }
  587. else
  588. {
  589. renderer.drawText(x, y, SAVE_OVERLAY);
  590. }
  591. y += line;
  592. // left over option table
  593. for(int i = end + 1; i < OPTIONS.length; i++)
  594. {
  595. renderer.drawText(x, y, OPTIONS[i]);
  596. y += line;
  597. }
  598. break;
  599. }
  600. case 3:
  601. {
  602. // level screen rendering
  603. renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
  604. int listLength = Math.min(levels.length, MENU_MAX);
  605. double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
  606. double y = (renderer.getHeight() - renderer.getTextHeight(listLength + 4)) * 0.5;
  607. double line = renderer.getTextHeight(1);
  608. renderer.drawText(x, y, TABLE_TOP);
  609. y += line;
  610. renderer.drawText(x, y, TABLE_HEADING);
  611. y += line;
  612. renderer.drawText(x, y, TABLE_MID);
  613. y += line;
  614. if(levels.length > MENU_MAX)
  615. {
  616. int upperHalf = MENU_MAX / 2;
  617. int downHalf = levels.length - upperHalf;
  618. if(levelIndex < upperHalf)
  619. {
  620. paintStringMarking(x, y, line, levelIndex);
  621. y = paintLevelName(x, y, line, 0, MENU_MAX - 1);
  622. renderer.drawText(x, y, TABLE_MORE);
  623. y += line;
  624. }
  625. else if(levelIndex >= downHalf)
  626. {
  627. paintStringMarking(x, y, line, levelIndex - downHalf + upperHalf + ((MENU_MAX & 1) != 0 ? 1 : 0));
  628. renderer.drawText(x, y, TABLE_MORE);
  629. y += line;
  630. y = paintLevelName(x, y, line, levels.length - MENU_MAX + 1, MENU_MAX - 1);
  631. }
  632. else
  633. {
  634. paintStringMarking(x, y, line, upperHalf);
  635. renderer.drawText(x, y, TABLE_MORE);
  636. y += line;
  637. y = paintLevelName(x, y, line, levelIndex - upperHalf + 1, MENU_MAX - 2);
  638. renderer.drawText(x, y, TABLE_MORE);
  639. y += line;
  640. }
  641. }
  642. else
  643. {
  644. paintStringMarking(x, y, line, levelIndex);
  645. y = paintLevelName(x, y, line, 0, levels.length);
  646. }
  647. renderer.drawText(x, y, TABLE_BOTTOM);
  648. renderer.stopTextDrawing();
  649. break;
  650. }
  651. }
  652. }
  653. private double paintLevelName(double x, double y, double line, int from, int length)
  654. {
  655. SimpleConfig sp = SAVE_SLOTS[slotScreenIndex];
  656. char[] chars = new char[MENU_WIDTH];
  657. chars[0] = 134;
  658. chars[MENU_WIDTH - 1] = 134;
  659. length += from;
  660. for(int j = from; j < length; j++)
  661. {
  662. String s = levels[j].getName();
  663. int border = Math.min(MENU_WIDTH - 14, s.length());
  664. for(int i = 0; i < border; i++)
  665. {
  666. chars[i + 1] = s.charAt(i);
  667. }
  668. Arrays.fill(chars, border + 1, MENU_WIDTH - 1, (char) 0);
  669. chars[chars.length - 13] = 134;
  670. chars[chars.length - 7] = 134;
  671. // bottles
  672. char[] tmp = levels[j].formatBottles(sp.getInt("level." + s + ".bottles", 0));
  673. int l = Math.min(5, tmp.length);
  674. System.arraycopy(tmp, 0, chars, chars.length - 7 - l, l);
  675. // time
  676. tmp = levels[j].formatTime(sp.getDouble("level." + s + ".time", -1.0));
  677. l = Math.min(5, tmp.length);
  678. System.arraycopy(tmp, 0, chars, chars.length - 1 - l, l);
  679. renderer.drawText(x, y, chars);
  680. y += line;
  681. }
  682. return y;
  683. }
  684. private void paintStringMarking(double x, double y, double line, int pos)
  685. {
  686. renderer.save();
  687. renderer.setFillColor(128, 128, 128, 1.0);
  688. renderer.fillRectangle(
  689. x + renderer.getTextWidth(1),
  690. y + renderer.getTextHeight(pos) - 1,
  691. renderer.getTextWidth(MENU_WIDTH - 14),
  692. line);
  693. renderer.fillRectangle(
  694. x + renderer.getTextWidth(MENU_WIDTH - 12),
  695. y + renderer.getTextHeight(pos) - 1,
  696. renderer.getTextWidth(5),
  697. line);
  698. renderer.fillRectangle(
  699. x + renderer.getTextWidth(MENU_WIDTH - 6),
  700. y + renderer.getTextHeight(pos) - 1,
  701. renderer.getTextWidth(5),
  702. line);
  703. renderer.restore();
  704. }
  705. public void tickTiles()
  706. {
  707. registeredTiles.values().forEach(tile -> tile.tick());
  708. }
  709. }