TileMap.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import { AsyncDataReader } from "../AsyncDataReader.js";
  2. import { SpritesheetAtlas } from "../SpritesheetAtlas.js";
  3. import { Point } from "../spatial/Point.js";
  4. export class TileMap {
  5. constructor() {
  6. this.mapData = null
  7. this.loaded = false
  8. this.translated = false;
  9. this.atlases = {}
  10. this.roomDataFile = "";
  11. }
  12. translateV1V2(datain){
  13. let dataout = datain
  14. if(datain.version == 1) {
  15. let tilesArray = []
  16. datain.spritemap.forEach((sprite, index) => {
  17. tilesArray[index] = [sprite, datain.tiles[index], []]
  18. })
  19. dataout.tiles = tilesArray
  20. delete dataout.spritemap
  21. dataout.version = 2
  22. this.translated = true
  23. }
  24. return dataout
  25. }
  26. translateV2V3(datain){
  27. let dataout = datain
  28. if(datain.version == 2) {
  29. dataout.spritesheets = [datain.spritesheet]
  30. let tilesArray = []
  31. datain.tiles.forEach((tile, index) => {
  32. let newTile = []
  33. newTile[0] = [tile[0][0], tile[0][1], 0]
  34. newTile[1] = tile[1]
  35. let newDecals = []
  36. tile[2].forEach((decal) => {
  37. newDecals.push([decal[0], decal[1], 0])
  38. })
  39. newTile[2] = newDecals
  40. tilesArray[index] = newTile
  41. })
  42. dataout.tiles = tilesArray
  43. delete dataout.spritesheet
  44. dataout.version = 3
  45. this.translated = true
  46. }
  47. return dataout
  48. }
  49. translateV3V4(datain) {
  50. let dataout = datain
  51. if(datain.version == 3) {
  52. let tilesArray = []
  53. datain.tiles.forEach((tile, index) => {
  54. tile[3] = []
  55. tilesArray[index] = tile
  56. })
  57. dataout.tiles = tilesArray
  58. dataout.version = 4
  59. this.translated = true
  60. }
  61. return dataout
  62. }
  63. translateV4V5(datain) {
  64. let dataout = datain
  65. if(datain.version == 4) {
  66. let tilesArray = []
  67. let mapMin = new Point(datain.bounds[0], datain.bounds[1])
  68. let mapMax = new Point(datain.bounds[2] - mapMin.x, datain.bounds[3] - mapMin.y)
  69. let xIndex = mapMin.x
  70. let yIndex = mapMin.y
  71. datain.tiles.forEach((tile, index) => {
  72. let newTile = [[xIndex, yIndex], tile[0], tile[1], tile[2], tile[3]]
  73. tilesArray[index] = newTile
  74. xIndex++
  75. if(xIndex >= mapMax.x) {
  76. yIndex++
  77. xIndex = mapMin.x
  78. }
  79. })
  80. dataout.tiles = tilesArray
  81. dataout.version = 5
  82. this.translated = true
  83. }
  84. return dataout
  85. }
  86. translateV5V6(datain) {
  87. let dataout = datain
  88. if (datain.version == 5) {
  89. dataout.exits = [datain.start]
  90. delete dataout.start
  91. let tilesArray = []
  92. datain.tiles.forEach((tile, index) => {
  93. tilesArray[index] = tile
  94. if (tile[2][1]) {
  95. tile[2][1][1] = 0
  96. }
  97. })
  98. dataout.version = 6
  99. this.translated = true
  100. }
  101. return dataout
  102. }
  103. translateV6V7(datain) {
  104. let dataout = datain
  105. if (datain.version == 6) {
  106. dataout.owner = "1"
  107. dataout.writers = []
  108. dataout.version = 7
  109. this.translated = true
  110. }
  111. return dataout
  112. }
  113. async load(filepath) {
  114. this.roomDataFile = filepath
  115. this.loaded = false
  116. try {
  117. let versioned = await new AsyncDataReader().readJson(filepath)
  118. if (versioned.responseType && versioned.responseType == 400) {
  119. //no room found, creating default empty room
  120. versioned = {
  121. "version": 7,
  122. "bounds": [0, 0, 4, 4],
  123. "tiles": [],
  124. "spritesheets": ["./assets/roguelikeSheet_transparent.png"],
  125. "exits": [[2, 2]],
  126. "owner": "",
  127. "writers": []
  128. }
  129. }
  130. versioned = this.translateV1V2(versioned)
  131. versioned = this.translateV2V3(versioned)
  132. versioned = this.translateV3V4(versioned)
  133. versioned = this.translateV4V5(versioned)
  134. versioned = this.translateV5V6(versioned)
  135. versioned = this.translateV6V7(versioned)
  136. this.mapData = versioned
  137. this.sortTiles(this.mapData)
  138. //TODO: if logged in, and owner not set, set ownership to current user
  139. if (this.translated) {
  140. console.log("converted", JSON.stringify(this.mapData))
  141. }
  142. this.atlases = {}
  143. let atlasLoadPromises = []
  144. this.mapData.spritesheets.forEach((sheet, index) => {
  145. let atlas = new SpritesheetAtlas()
  146. atlasLoadPromises.push(atlas.load(sheet, { width: 16, height: 16, margin: 1 }))
  147. this.atlases[index] = atlas
  148. })
  149. await Promise.all(atlasLoadPromises)
  150. this.loaded = true
  151. } catch (e) {
  152. alert("Something went wrong when trying to load this map");
  153. }
  154. }
  155. async loadFromApi(roomName) {
  156. let filepath = `./api/room/${roomName}`
  157. this.roomName = roomName
  158. return await this.load(filepath)
  159. }
  160. reloadAtlas(index) {
  161. let atlas = new SpritesheetAtlas()
  162. atlas.load(this.mapData.spritesheets[index], { width: 16, height: 16, margin: 1 })
  163. this.atlases[index] = atlas
  164. }
  165. sortTiles(mapData) {
  166. mapData.tiles.sort((a, b) => {
  167. if (a[0][0] == b[0][0]) return a[0][1] - b[0][1]
  168. return a[0][0] - b[0][0]
  169. })
  170. }
  171. draw(ctx, scaledCanvas) {
  172. if(!this.loaded) {
  173. return
  174. }
  175. ctx.save();
  176. ctx.translate(16 * this.mapData.bounds[0], 16 * this.mapData.bounds[1])
  177. this.mapData.tiles.forEach((tile) => {
  178. let sprite = tile[1]
  179. let atlas = this.getAtlas(sprite[2])
  180. ctx.save();
  181. ctx.translate(tile[0][0] * 16, tile[0][1] * 16)
  182. atlas.getDrawableSprite(sprite[0], sprite[1]).draw(ctx, scaledCanvas)
  183. ctx.restore();
  184. })
  185. ctx.restore();
  186. }
  187. drawDecals(ctx, scaledCanvas) {
  188. if(!this.loaded) {
  189. return
  190. }
  191. ctx.save();
  192. ctx.translate(16 * this.mapData.bounds[0], 16 * this.mapData.bounds[1])
  193. this.mapData.tiles.forEach((tile, index) => {
  194. ctx.save();
  195. ctx.translate(tile[0][0] * 16, tile[0][1] * 16)
  196. tile[3].forEach((decal) => {
  197. let decalAtlas = this.getAtlas(decal[2])
  198. decalAtlas.getDrawableSprite(decal[0], decal[1]).draw(ctx, scaledCanvas)
  199. })
  200. ctx.restore();
  201. })
  202. ctx.restore();
  203. }
  204. drawOverDecals(ctx, scaledCanvas) {
  205. if(!this.loaded) {
  206. return
  207. }
  208. ctx.save();
  209. ctx.translate(16 * this.mapData.bounds[0], 16 * this.mapData.bounds[1])
  210. this.mapData.tiles.forEach((tile, index) => {
  211. ctx.save();
  212. ctx.translate(tile[0][0] * 16, tile[0][1] * 16)
  213. tile[4].forEach((decal) => {
  214. let decalAtlas = this.getAtlas(decal[2])
  215. decalAtlas.getDrawableSprite(decal[0], decal[1]).draw(ctx, scaledCanvas)
  216. })
  217. ctx.restore();
  218. })
  219. ctx.restore();
  220. }
  221. drawCollision(ctx, scaledCanvas) {
  222. if(!this.loaded) {
  223. return
  224. }
  225. ctx.save();
  226. ctx.translate(16 * this.mapData.bounds[0], 16 * this.mapData.bounds[1])
  227. this.mapData.tiles.forEach((tile, index) => {
  228. let collidable = tile[2][0]
  229. if(collidable == 0) {
  230. return
  231. }
  232. ctx.save();
  233. ctx.translate(tile[0][0] * 16, tile[0][1] * 16)
  234. ctx.fillStyle = "rgba(240, 40, 40, 0.5)"
  235. ctx.beginPath()
  236. ctx.rect(-8,-8, 16, 16)
  237. ctx.fill()
  238. ctx.restore();
  239. })
  240. ctx.restore();
  241. }
  242. drawDoors(ctx, scaledCanvas) {
  243. if (!this.loaded) {
  244. return
  245. }
  246. ctx.save();
  247. ctx.translate(16 * this.mapData.bounds[0], 16 * this.mapData.bounds[1])
  248. this.mapData.tiles.filter((tile, index) => {
  249. return tile[2][1]
  250. }).forEach((tile) => {
  251. ctx.save();
  252. ctx.translate(tile[0][0] * 16, tile[0][1] * 16)
  253. ctx.strokeStyle = "rgba(40, 240, 40, 1.0)"
  254. ctx.beginPath()
  255. ctx.rect(-8, -8, 16, 16)
  256. ctx.stroke()
  257. ctx.restore();
  258. })
  259. this.mapData.exits.forEach((exit, index) => {
  260. ctx.save();
  261. ctx.translate(exit[0] * 16, exit[1] * 16)
  262. ctx.fillStyle = "rgba(240, 240, 40, 1.0)"
  263. ctx.beginPath()
  264. ctx.rect(-8, -8, 16, 16)
  265. ctx.fill()
  266. ctx.fillStyle = "#000000"
  267. ctx.font = "14px Arial"
  268. ctx.textAlign = "center"
  269. ctx.fillText(`${index}`, 0, 5)
  270. ctx.restore();
  271. })
  272. ctx.restore();
  273. }
  274. drawCoords(ctx, scaledCanvas) {
  275. if(!this.loaded) {
  276. return
  277. }
  278. ctx.save();
  279. ctx.translate(16 * this.mapData.bounds[0], 16 * this.mapData.bounds[1])
  280. this.mapData.tiles.forEach((tile, index) => {
  281. ctx.save();
  282. ctx.translate(tile[0][0] * 16, tile[0][1] * 16)
  283. ctx.textAlign = "right"
  284. ctx.font = "6px Arial"
  285. ctx.fillStyle = "rgba(255,255,255,0.4)"
  286. ctx.fillText(`${tile[0][0]},${tile[0][1]}`, 8, 8)
  287. ctx.restore();
  288. })
  289. ctx.restore();
  290. }
  291. drawDecalCoords(ctx, scaledCanvas) {
  292. if(!this.loaded) {
  293. return
  294. }
  295. ctx.save();
  296. ctx.translate(16 * this.mapData.bounds[0], 16 * this.mapData.bounds[1])
  297. this.mapData.tiles.forEach((tile, index) => {
  298. if(tile[3].length > 0 || tile[4].length > 0) {
  299. ctx.save();
  300. ctx.translate(tile[0][0] * 16, tile[0][1] * 16)
  301. ctx.textAlign = "center"
  302. ctx.font = "6px Arial"
  303. ctx.fillStyle = "rgba(0,0,0,0.5)"
  304. ctx.fillText(`${tile[0][0]},${tile[0][1]}`, 0, 0)
  305. ctx.restore();
  306. }
  307. })
  308. ctx.restore();
  309. }
  310. getTile(x, y) {
  311. if(x < this.mapData.bounds[0] ||
  312. x > this.mapData.bounds[0]+this.mapData.bounds[2] - 1 ||
  313. y < this.mapData.bounds[1] ||
  314. y > this.mapData.bounds[1]+this.mapData.bounds[3] - 1) {
  315. throw Error("Not on map")
  316. }
  317. return this.mapData.tiles.find(tile => x == tile[0][0] && y == tile[0][1])
  318. }
  319. updateMovement(gridPosition, successFunction) {
  320. if(!this.loaded) {
  321. return
  322. }
  323. let currentTile = this.getTile(gridPosition.x, gridPosition.y)
  324. if(!currentTile) {
  325. return
  326. }
  327. let tileData = currentTile[2]
  328. if(tileData[0] == 1) {
  329. return
  330. }
  331. if(gridPosition.x < this.mapData.bounds[0] ||
  332. gridPosition.x > this.mapData.bounds[0] + this.mapData.bounds[2] - 1 ||
  333. gridPosition.y < this.mapData.bounds[1] ||
  334. gridPosition.y > this.mapData.bounds[1] + this.mapData.bounds[3] - 1 ) {
  335. return
  336. }
  337. //this.map.loadFromApi("room1")
  338. if (tileData[1]) {
  339. let newRoomData = tileData[1][0]
  340. let newRoomEntrance = tileData[1][1]
  341. successFunction(gridPosition, this.roomName)
  342. this.loadFromApi(newRoomData).then(() => {
  343. let doorData = this.mapData.exits[newRoomEntrance]
  344. let doorLocation = new Point(doorData[0] + this.mapData.bounds[0], doorData[1] + this.mapData.bounds[1])
  345. successFunction(doorLocation, this.roomName)
  346. })
  347. return
  348. }
  349. successFunction(gridPosition, this.roomName)
  350. }
  351. getAtlas(index) {
  352. let atlas = this.atlases[index]
  353. return atlas
  354. }
  355. }