LevelData.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. package me.hammerle.supersnuvi.gamelogic;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.util.HashMap;
  7. import java.util.Map.Entry;
  8. public class LevelData
  9. {
  10. private final static int STRING_ID = 1;
  11. private final static int INTEGER_ID = 2;
  12. // container for all custom data
  13. private final HashMap<String, Object> data;
  14. // layers up to bIndex (including) are considered background
  15. // the next layer is the entity spawn layer
  16. // every layer after that is foreground
  17. private int bIndex;
  18. private int width;
  19. private int height;
  20. private int layers;
  21. // storage for tiles
  22. // [layer][width][height]
  23. private int[][][] tiles;
  24. // the file of the level for saving and loading
  25. private final File file;
  26. public LevelData(File file)
  27. {
  28. this.file = file;
  29. this.data = new HashMap<>();
  30. this.bIndex = 0;
  31. this.width = 0;
  32. this.height = 0;
  33. this.layers = 0;
  34. this.tiles = null;
  35. }
  36. public LevelData(File file, int bIndex, int layer, int width, int height)
  37. {
  38. this.file = file;
  39. this.data = new HashMap<>();
  40. this.bIndex = bIndex;
  41. this.width = width;
  42. this.height = height;
  43. this.layers = layer;
  44. this.tiles = new int[layer][width][height];
  45. for(int l = 0; l < layer; l++)
  46. {
  47. for(int x = 0; x < width; x++)
  48. {
  49. for(int y = 0; y < height; y++)
  50. {
  51. tiles[l][x][y] = -1;
  52. }
  53. }
  54. }
  55. }
  56. // -------------------------------------------------------------------------
  57. // file writing / reading
  58. // -------------------------------------------------------------------------
  59. private void writeString(FileOutputStream out, String s, boolean identifier) throws IOException
  60. {
  61. // writing type identifier
  62. if(identifier)
  63. {
  64. out.write(STRING_ID);
  65. }
  66. // writing length of string
  67. byte[] b = s.getBytes();
  68. writeInt(out, b.length, false);
  69. // writing the bytes of the string
  70. out.write(b);
  71. }
  72. private String readString(FileInputStream in) throws IOException
  73. {
  74. int length = readInt(in);
  75. byte[] b = new byte[length];
  76. in.read(b);
  77. if(length > 1024 * 1024)
  78. {
  79. throw new IOException("invalid level file");
  80. }
  81. return new String(b);
  82. }
  83. private void writeInt(FileOutputStream out, int i, boolean identifier) throws IOException
  84. {
  85. // writing type identifier
  86. if(identifier)
  87. {
  88. out.write(INTEGER_ID);
  89. }
  90. // writing the int per byte
  91. out.write(i & 0xFF);
  92. out.write((i >> 8) & 0xFF);
  93. out.write((i >> 16) & 0xFF);
  94. out.write((i >> 24) & 0xFF);
  95. }
  96. private int readInt(FileInputStream in) throws IOException
  97. {
  98. int i = 0;
  99. i |= in.read();
  100. i |= in.read() << 8;
  101. i |= in.read() << 16;
  102. i |= in.read() << 24;
  103. return i;
  104. }
  105. private void writeRepeated(int count, int currentTile, FileOutputStream out) throws IOException
  106. {
  107. while(true)
  108. {
  109. if(count <= 0)
  110. {
  111. break;
  112. }
  113. else if(count >= 255)
  114. {
  115. count -= 255;
  116. out.write(255);
  117. writeInt(out, currentTile, false);
  118. }
  119. else
  120. {
  121. out.write(count);
  122. writeInt(out, currentTile, false);
  123. break;
  124. }
  125. }
  126. }
  127. public boolean save()
  128. {
  129. if(file == null)
  130. {
  131. return false;
  132. }
  133. try
  134. {
  135. try(FileOutputStream out = new FileOutputStream(file))
  136. {
  137. // writing number of data fields
  138. writeInt(out, data.size(), false);
  139. // writing keys and data, data varies, key are always strings
  140. for(Entry<String, Object> e : data.entrySet())
  141. {
  142. writeString(out, e.getKey(), false);
  143. if(e.getValue() instanceof Integer)
  144. {
  145. writeInt(out, (Integer) e.getValue(), true);
  146. }
  147. else
  148. {
  149. writeString(out, e.getValue().toString(), true);
  150. }
  151. }
  152. // write background index
  153. writeInt(out, bIndex, false);
  154. // write number of layers
  155. writeInt(out, layers, false);
  156. // write layer width
  157. writeInt(out, width, false);
  158. // write layer height
  159. writeInt(out, height, false);
  160. // write tiles
  161. if(layers > 0 && width > 0 && height > 0)
  162. {
  163. int count = 0;
  164. int currentTile = tiles[0][0][0];
  165. for(int x = 0; x < layers; x++)
  166. {
  167. for(int y = 0; y < width; y++)
  168. {
  169. for(int z = 0; z < height; z++)
  170. {
  171. if(currentTile == tiles[x][y][z])
  172. {
  173. count++;
  174. }
  175. else
  176. {
  177. writeRepeated(count, currentTile, out);
  178. count = 1;
  179. currentTile = tiles[x][y][z];
  180. }
  181. }
  182. }
  183. }
  184. writeRepeated(count, currentTile, out);
  185. }
  186. out.flush();
  187. out.close();
  188. }
  189. return true;
  190. }
  191. catch(IOException ex)
  192. {
  193. ex.printStackTrace();
  194. return false;
  195. }
  196. }
  197. public boolean load()
  198. {
  199. if(file == null)
  200. {
  201. return false;
  202. }
  203. try
  204. {
  205. try (FileInputStream in = new FileInputStream(file))
  206. {
  207. // getting the number of data fields
  208. int fields = readInt(in);
  209. // getting keys and data, data varies, key are always strings
  210. for(; fields > 0; fields--)
  211. {
  212. String key = readString(in);
  213. int id = in.read();
  214. switch(id)
  215. {
  216. case STRING_ID:
  217. data.put(key, readString(in));
  218. break;
  219. case INTEGER_ID:
  220. data.put(key, readInt(in));
  221. break;
  222. default:
  223. System.err.println("fucked up level data");
  224. return false;
  225. }
  226. }
  227. //data.forEach((k, v) -> System.out.println(k + " " + v + " " + v.getClass()));
  228. // getting background index
  229. bIndex = readInt(in);
  230. // getting number of layers
  231. layers = readInt(in);
  232. // getting layer width
  233. width = readInt(in);
  234. // getting layer height
  235. height = readInt(in);
  236. //System.out.println(layers + " " + width + " " + height);
  237. // init tiles
  238. tiles = new int[layers][width][height];
  239. // getting tiles
  240. if(layers > 0 && width > 0 && height > 0)
  241. {
  242. int count = 0;
  243. int currentTile = -1;
  244. for(int x = 0; x < layers; x++)
  245. {
  246. for(int y = 0; y < width; y++)
  247. {
  248. for(int z = 0; z < height; z++)
  249. {
  250. if(count == 0)
  251. {
  252. count = in.read();
  253. currentTile = readInt(in);
  254. //System.out.println(count + " " + currentTile);
  255. }
  256. tiles[x][y][z] = currentTile;
  257. count--;
  258. }
  259. }
  260. }
  261. }
  262. in.close();
  263. //save();
  264. }
  265. int x = bIndex + 1;
  266. for(int y = 0; y < width; y++)
  267. {
  268. for(int z = 0; z < height; z++)
  269. {
  270. if(tiles[x][y][z] == -1)
  271. {
  272. tiles[x][y][z] = 0;
  273. }
  274. }
  275. }
  276. return true;
  277. }
  278. catch(IOException ex)
  279. {
  280. ex.printStackTrace();
  281. return false;
  282. }
  283. }
  284. // -------------------------------------------------------------------------
  285. // variable level data
  286. // -------------------------------------------------------------------------
  287. public void setData(String name, int value)
  288. {
  289. data.put(name, value);
  290. }
  291. public void setData(String name, String value)
  292. {
  293. data.put(name, value);
  294. }
  295. public int getInt(String name, int error)
  296. {
  297. Object o = data.get(name);
  298. if(o == null || !(o instanceof Integer))
  299. {
  300. return error;
  301. }
  302. return (Integer) o;
  303. }
  304. public String getString(String name, String error)
  305. {
  306. Object o = data.get(name);
  307. if(o == null || !(o instanceof String))
  308. {
  309. return error;
  310. }
  311. return (String) o;
  312. }
  313. // -------------------------------------------------------------------------
  314. // level data getter / setter
  315. // -------------------------------------------------------------------------
  316. public int getBackgroundIndex()
  317. {
  318. return bIndex;
  319. }
  320. public void setBackgroundIndex(int bIndex)
  321. {
  322. this.bIndex = bIndex;
  323. }
  324. public int getLayers()
  325. {
  326. return layers;
  327. }
  328. public int getWidth()
  329. {
  330. return width;
  331. }
  332. public int getHeight()
  333. {
  334. return height;
  335. }
  336. // -------------------------------------------------------------------------
  337. // layer stuff
  338. // -------------------------------------------------------------------------
  339. public void clearLayer(int index)
  340. {
  341. if(index >= 0 && index < layers)
  342. {
  343. for(int x = 0; x < width; x++)
  344. {
  345. for(int y = 0; y < height; y++)
  346. {
  347. tiles[index][x][y] = -1;
  348. }
  349. }
  350. }
  351. }
  352. public void copyLayer(int fromIndex, int toIndex)
  353. {
  354. if(fromIndex >= 0 && fromIndex < layers && toIndex >= 0 && toIndex < layers)
  355. {
  356. for(int x = 0; x < width; x++)
  357. {
  358. System.arraycopy(tiles[fromIndex][x], 0, tiles[toIndex][x], 0, height);
  359. }
  360. }
  361. }
  362. public void addLayer()
  363. {
  364. int[][][] newLayers = new int[layers + 1][][];
  365. System.arraycopy(tiles, 0, newLayers, 0, layers);
  366. newLayers[layers] = new int[width][height];
  367. for(int x = 0; x < width; x++)
  368. {
  369. for(int y = 0; y < height; y++)
  370. {
  371. newLayers[layers][x][y] = -1;
  372. }
  373. }
  374. layers++;
  375. tiles = newLayers;
  376. }
  377. public void addLayer(int index)
  378. {
  379. if(index >= layers)
  380. {
  381. addLayer();
  382. return;
  383. }
  384. int[][][] newLayers = new int[layers + 1][][];
  385. System.arraycopy(tiles, 0, newLayers, 0, index);
  386. newLayers[index] = new int[width][height];
  387. for(int x = 0; x < width; x++)
  388. {
  389. for(int y = 0; y < height; y++)
  390. {
  391. newLayers[index][x][y] = -1;
  392. }
  393. }
  394. System.arraycopy(tiles, index, newLayers, index + 1, layers - index);
  395. layers++;
  396. tiles = newLayers;
  397. }
  398. public void removeLayer(int index)
  399. {
  400. if(layers <= 1)
  401. {
  402. layers = 0;
  403. tiles = new int[0][0][0];
  404. return;
  405. }
  406. int[][][] newLayers = new int[layers - 1][][];
  407. if(index > 0)
  408. {
  409. System.arraycopy(tiles, 0, newLayers, 0, index);
  410. }
  411. int left = layers - index - 1;
  412. if(left > 0)
  413. {
  414. System.arraycopy(tiles, index + 1, newLayers, index, left);
  415. }
  416. layers--;
  417. tiles = newLayers;
  418. }
  419. public void setLayerSize(int w, int h)
  420. {
  421. int[][][] newLayers = new int[layers][w][h];
  422. int cw = Math.min(w, width);
  423. int ch = Math.min(h, height);
  424. for(int l = 0; l < layers; l++)
  425. {
  426. for(int x = 0; x < cw; x++)
  427. {
  428. System.arraycopy(tiles[l][x], 0, newLayers[l][x], 0, ch);
  429. }
  430. }
  431. width = w;
  432. height = h;
  433. tiles = newLayers;
  434. }
  435. // -------------------------------------------------------------------------
  436. // tile getter / setter
  437. // -------------------------------------------------------------------------
  438. public int getInteractionTile(int x, int y)
  439. {
  440. if(x < 0 || y < 0 || x >= width || y >= height || bIndex >= layers)
  441. {
  442. return -1;
  443. }
  444. return tiles[bIndex][x][y];
  445. }
  446. public int getTile(int layer, int x, int y)
  447. {
  448. return tiles[layer][x][y];
  449. }
  450. public void setTile(int layer, int x, int y, int value)
  451. {
  452. tiles[layer][x][y] = value;
  453. }
  454. // -------------------------------------------------------------------------
  455. // entity layer
  456. // -------------------------------------------------------------------------
  457. public void deactivateEntity(int x, int y)
  458. {
  459. tiles[bIndex + 1][x][y] = -Math.abs(tiles[bIndex + 1][x][y]);
  460. }
  461. public void activateEntities()
  462. {
  463. int[][] ent = tiles[bIndex + 1];
  464. forEachEntity((x, y, tile) ->
  465. {
  466. if(tile < 0)
  467. {
  468. ent[x][y] = Math.abs(ent[x][y]);
  469. }
  470. }, 0, width, 0, height);
  471. }
  472. // -------------------------------------------------------------------------
  473. // tile iterators
  474. // -------------------------------------------------------------------------
  475. public void forEachBackground(TileConsumer c, int sx, int ex, int sy, int ey)
  476. {
  477. int max = Math.min(layers - 1, bIndex);
  478. sx = Math.max(0, sx);
  479. sy = Math.max(0, sy);
  480. ex = Math.min(width, ex);
  481. ey = Math.min(height, ey);
  482. for(int l = 0; l <= max; l++)
  483. {
  484. for(int x = sx; x < ex; x++)
  485. {
  486. for(int y = sy; y < ey; y++)
  487. {
  488. c.consume(x, y, tiles[l][x][y]);
  489. }
  490. }
  491. }
  492. }
  493. public void forEachInteractTile(TileConsumer c)
  494. {
  495. if(bIndex < layers)
  496. {
  497. for(int y = 0; y < width; y++)
  498. {
  499. for(int z = 0; z < height; z++)
  500. {
  501. c.consume(y, z, tiles[bIndex][y][z]);
  502. }
  503. }
  504. }
  505. }
  506. public void forEachEntity(TileConsumer c, int sx, int ex, int sy, int ey)
  507. {
  508. int l = bIndex + 1;
  509. if(l < layers)
  510. {
  511. sx = Math.max(0, sx);
  512. sy = Math.max(0, sy);
  513. ex = Math.min(width, ex);
  514. ey = Math.min(height, ey);
  515. int[][] ent = tiles[l];
  516. for(int x = sx; x < ex; x++)
  517. {
  518. for(int y = sy; y < ey; y++)
  519. {
  520. c.consume(x, y, ent[x][y]);
  521. }
  522. }
  523. }
  524. }
  525. public void forEachForeground(TileConsumer c, int sx, int ex, int sy, int ey)
  526. {
  527. sx = Math.max(0, sx);
  528. sy = Math.max(0, sy);
  529. ex = Math.min(width, ex);
  530. ey = Math.min(height, ey);
  531. for(int l = bIndex + 2; l < layers; l++)
  532. {
  533. for(int x = sx; x < ex; x++)
  534. {
  535. for(int y = sy; y < ey; y++)
  536. {
  537. c.consume(x, y, tiles[l][x][y]);
  538. }
  539. }
  540. }
  541. }
  542. }