arcane-agents 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1204) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +528 -0
  3. package/assets/characters/README.md +114 -0
  4. package/assets/characters/assassin-shadow/animations/walk/east/0.png +0 -0
  5. package/assets/characters/assassin-shadow/animations/walk/east/1.png +0 -0
  6. package/assets/characters/assassin-shadow/animations/walk/east/2.png +0 -0
  7. package/assets/characters/assassin-shadow/animations/walk/east/3.png +0 -0
  8. package/assets/characters/assassin-shadow/animations/walk/east/4.png +0 -0
  9. package/assets/characters/assassin-shadow/animations/walk/east/5.png +0 -0
  10. package/assets/characters/assassin-shadow/animations/walk/east/6.png +0 -0
  11. package/assets/characters/assassin-shadow/animations/walk/east/7.png +0 -0
  12. package/assets/characters/assassin-shadow/animations/walk/north/0.png +0 -0
  13. package/assets/characters/assassin-shadow/animations/walk/north/1.png +0 -0
  14. package/assets/characters/assassin-shadow/animations/walk/north/2.png +0 -0
  15. package/assets/characters/assassin-shadow/animations/walk/north/3.png +0 -0
  16. package/assets/characters/assassin-shadow/animations/walk/north/4.png +0 -0
  17. package/assets/characters/assassin-shadow/animations/walk/north/5.png +0 -0
  18. package/assets/characters/assassin-shadow/animations/walk/north/6.png +0 -0
  19. package/assets/characters/assassin-shadow/animations/walk/north/7.png +0 -0
  20. package/assets/characters/assassin-shadow/animations/walk/south/0.png +0 -0
  21. package/assets/characters/assassin-shadow/animations/walk/south/1.png +0 -0
  22. package/assets/characters/assassin-shadow/animations/walk/south/2.png +0 -0
  23. package/assets/characters/assassin-shadow/animations/walk/south/3.png +0 -0
  24. package/assets/characters/assassin-shadow/animations/walk/south/4.png +0 -0
  25. package/assets/characters/assassin-shadow/animations/walk/south/5.png +0 -0
  26. package/assets/characters/assassin-shadow/animations/walk/south/6.png +0 -0
  27. package/assets/characters/assassin-shadow/animations/walk/south/7.png +0 -0
  28. package/assets/characters/assassin-shadow/animations/walk/west/0.png +0 -0
  29. package/assets/characters/assassin-shadow/animations/walk/west/1.png +0 -0
  30. package/assets/characters/assassin-shadow/animations/walk/west/2.png +0 -0
  31. package/assets/characters/assassin-shadow/animations/walk/west/3.png +0 -0
  32. package/assets/characters/assassin-shadow/animations/walk/west/4.png +0 -0
  33. package/assets/characters/assassin-shadow/animations/walk/west/5.png +0 -0
  34. package/assets/characters/assassin-shadow/animations/walk/west/6.png +0 -0
  35. package/assets/characters/assassin-shadow/animations/walk/west/7.png +0 -0
  36. package/assets/characters/assassin-shadow/animations/working/0.png +0 -0
  37. package/assets/characters/assassin-shadow/animations/working/1.png +0 -0
  38. package/assets/characters/assassin-shadow/animations/working/10.png +0 -0
  39. package/assets/characters/assassin-shadow/animations/working/11.png +0 -0
  40. package/assets/characters/assassin-shadow/animations/working/12.png +0 -0
  41. package/assets/characters/assassin-shadow/animations/working/13.png +0 -0
  42. package/assets/characters/assassin-shadow/animations/working/14.png +0 -0
  43. package/assets/characters/assassin-shadow/animations/working/15.png +0 -0
  44. package/assets/characters/assassin-shadow/animations/working/2.png +0 -0
  45. package/assets/characters/assassin-shadow/animations/working/3.png +0 -0
  46. package/assets/characters/assassin-shadow/animations/working/4.png +0 -0
  47. package/assets/characters/assassin-shadow/animations/working/5.png +0 -0
  48. package/assets/characters/assassin-shadow/animations/working/6.png +0 -0
  49. package/assets/characters/assassin-shadow/animations/working/7.png +0 -0
  50. package/assets/characters/assassin-shadow/animations/working/8.png +0 -0
  51. package/assets/characters/assassin-shadow/animations/working/9.png +0 -0
  52. package/assets/characters/assassin-shadow/rotations/east.png +0 -0
  53. package/assets/characters/assassin-shadow/rotations/north.png +0 -0
  54. package/assets/characters/assassin-shadow/rotations/south.png +0 -0
  55. package/assets/characters/assassin-shadow/rotations/west.png +0 -0
  56. package/assets/characters/assassin-shadow/voice-lines/arrive.mp3 +0 -0
  57. package/assets/characters/assassin-shadow/voice-lines/attention.mp3 +0 -0
  58. package/assets/characters/assassin-shadow/voice-lines/complete.mp3 +0 -0
  59. package/assets/characters/assassin-shadow/voice-lines/death.mp3 +0 -0
  60. package/assets/characters/assassin-shadow/voice-lines/move_variant_1.mp3 +0 -0
  61. package/assets/characters/assassin-shadow/voice-lines/move_variant_2.mp3 +0 -0
  62. package/assets/characters/assassin-shadow/voice-lines/move_variant_3.mp3 +0 -0
  63. package/assets/characters/assassin-shadow/voice-lines/selected_variant_1.mp3 +0 -0
  64. package/assets/characters/assassin-shadow/voice-lines/selected_variant_2.mp3 +0 -0
  65. package/assets/characters/assassin-shadow/voice-lines/selected_variant_3.mp3 +0 -0
  66. package/assets/characters/barbarian-wild/animations/walk/east/0.png +0 -0
  67. package/assets/characters/barbarian-wild/animations/walk/east/1.png +0 -0
  68. package/assets/characters/barbarian-wild/animations/walk/east/2.png +0 -0
  69. package/assets/characters/barbarian-wild/animations/walk/east/3.png +0 -0
  70. package/assets/characters/barbarian-wild/animations/walk/east/4.png +0 -0
  71. package/assets/characters/barbarian-wild/animations/walk/east/5.png +0 -0
  72. package/assets/characters/barbarian-wild/animations/walk/east/6.png +0 -0
  73. package/assets/characters/barbarian-wild/animations/walk/east/7.png +0 -0
  74. package/assets/characters/barbarian-wild/animations/walk/north/0.png +0 -0
  75. package/assets/characters/barbarian-wild/animations/walk/north/1.png +0 -0
  76. package/assets/characters/barbarian-wild/animations/walk/north/2.png +0 -0
  77. package/assets/characters/barbarian-wild/animations/walk/north/3.png +0 -0
  78. package/assets/characters/barbarian-wild/animations/walk/north/4.png +0 -0
  79. package/assets/characters/barbarian-wild/animations/walk/north/5.png +0 -0
  80. package/assets/characters/barbarian-wild/animations/walk/north/6.png +0 -0
  81. package/assets/characters/barbarian-wild/animations/walk/north/7.png +0 -0
  82. package/assets/characters/barbarian-wild/animations/walk/south/0.png +0 -0
  83. package/assets/characters/barbarian-wild/animations/walk/south/1.png +0 -0
  84. package/assets/characters/barbarian-wild/animations/walk/south/2.png +0 -0
  85. package/assets/characters/barbarian-wild/animations/walk/south/3.png +0 -0
  86. package/assets/characters/barbarian-wild/animations/walk/south/4.png +0 -0
  87. package/assets/characters/barbarian-wild/animations/walk/south/5.png +0 -0
  88. package/assets/characters/barbarian-wild/animations/walk/south/6.png +0 -0
  89. package/assets/characters/barbarian-wild/animations/walk/south/7.png +0 -0
  90. package/assets/characters/barbarian-wild/animations/walk/west/0.png +0 -0
  91. package/assets/characters/barbarian-wild/animations/walk/west/1.png +0 -0
  92. package/assets/characters/barbarian-wild/animations/walk/west/2.png +0 -0
  93. package/assets/characters/barbarian-wild/animations/walk/west/3.png +0 -0
  94. package/assets/characters/barbarian-wild/animations/walk/west/4.png +0 -0
  95. package/assets/characters/barbarian-wild/animations/walk/west/5.png +0 -0
  96. package/assets/characters/barbarian-wild/animations/walk/west/6.png +0 -0
  97. package/assets/characters/barbarian-wild/animations/walk/west/7.png +0 -0
  98. package/assets/characters/barbarian-wild/animations/working/0.png +0 -0
  99. package/assets/characters/barbarian-wild/animations/working/1.png +0 -0
  100. package/assets/characters/barbarian-wild/animations/working/10.png +0 -0
  101. package/assets/characters/barbarian-wild/animations/working/11.png +0 -0
  102. package/assets/characters/barbarian-wild/animations/working/12.png +0 -0
  103. package/assets/characters/barbarian-wild/animations/working/13.png +0 -0
  104. package/assets/characters/barbarian-wild/animations/working/14.png +0 -0
  105. package/assets/characters/barbarian-wild/animations/working/15.png +0 -0
  106. package/assets/characters/barbarian-wild/animations/working/2.png +0 -0
  107. package/assets/characters/barbarian-wild/animations/working/3.png +0 -0
  108. package/assets/characters/barbarian-wild/animations/working/4.png +0 -0
  109. package/assets/characters/barbarian-wild/animations/working/5.png +0 -0
  110. package/assets/characters/barbarian-wild/animations/working/6.png +0 -0
  111. package/assets/characters/barbarian-wild/animations/working/7.png +0 -0
  112. package/assets/characters/barbarian-wild/animations/working/8.png +0 -0
  113. package/assets/characters/barbarian-wild/animations/working/9.png +0 -0
  114. package/assets/characters/barbarian-wild/rotations/east.png +0 -0
  115. package/assets/characters/barbarian-wild/rotations/north.png +0 -0
  116. package/assets/characters/barbarian-wild/rotations/south.png +0 -0
  117. package/assets/characters/barbarian-wild/rotations/west.png +0 -0
  118. package/assets/characters/barbarian-wild/voice-lines/arrive.mp3 +0 -0
  119. package/assets/characters/barbarian-wild/voice-lines/attention.mp3 +0 -0
  120. package/assets/characters/barbarian-wild/voice-lines/complete.mp3 +0 -0
  121. package/assets/characters/barbarian-wild/voice-lines/death.mp3 +0 -0
  122. package/assets/characters/barbarian-wild/voice-lines/move_variant_1.mp3 +0 -0
  123. package/assets/characters/barbarian-wild/voice-lines/move_variant_2.mp3 +0 -0
  124. package/assets/characters/barbarian-wild/voice-lines/move_variant_3.mp3 +0 -0
  125. package/assets/characters/barbarian-wild/voice-lines/selected_variant_1.mp3 +0 -0
  126. package/assets/characters/barbarian-wild/voice-lines/selected_variant_2.mp3 +0 -0
  127. package/assets/characters/barbarian-wild/voice-lines/selected_variant_3.mp3 +0 -0
  128. package/assets/characters/blood-elf-spellarcher/animations/walk/east/0.png +0 -0
  129. package/assets/characters/blood-elf-spellarcher/animations/walk/east/1.png +0 -0
  130. package/assets/characters/blood-elf-spellarcher/animations/walk/east/2.png +0 -0
  131. package/assets/characters/blood-elf-spellarcher/animations/walk/east/3.png +0 -0
  132. package/assets/characters/blood-elf-spellarcher/animations/walk/east/4.png +0 -0
  133. package/assets/characters/blood-elf-spellarcher/animations/walk/east/5.png +0 -0
  134. package/assets/characters/blood-elf-spellarcher/animations/walk/east/6.png +0 -0
  135. package/assets/characters/blood-elf-spellarcher/animations/walk/east/7.png +0 -0
  136. package/assets/characters/blood-elf-spellarcher/animations/walk/north/0.png +0 -0
  137. package/assets/characters/blood-elf-spellarcher/animations/walk/north/1.png +0 -0
  138. package/assets/characters/blood-elf-spellarcher/animations/walk/north/2.png +0 -0
  139. package/assets/characters/blood-elf-spellarcher/animations/walk/north/3.png +0 -0
  140. package/assets/characters/blood-elf-spellarcher/animations/walk/north/4.png +0 -0
  141. package/assets/characters/blood-elf-spellarcher/animations/walk/north/5.png +0 -0
  142. package/assets/characters/blood-elf-spellarcher/animations/walk/north/6.png +0 -0
  143. package/assets/characters/blood-elf-spellarcher/animations/walk/north/7.png +0 -0
  144. package/assets/characters/blood-elf-spellarcher/animations/walk/south/0.png +0 -0
  145. package/assets/characters/blood-elf-spellarcher/animations/walk/south/1.png +0 -0
  146. package/assets/characters/blood-elf-spellarcher/animations/walk/south/2.png +0 -0
  147. package/assets/characters/blood-elf-spellarcher/animations/walk/south/3.png +0 -0
  148. package/assets/characters/blood-elf-spellarcher/animations/walk/south/4.png +0 -0
  149. package/assets/characters/blood-elf-spellarcher/animations/walk/south/5.png +0 -0
  150. package/assets/characters/blood-elf-spellarcher/animations/walk/south/6.png +0 -0
  151. package/assets/characters/blood-elf-spellarcher/animations/walk/south/7.png +0 -0
  152. package/assets/characters/blood-elf-spellarcher/animations/walk/west/0.png +0 -0
  153. package/assets/characters/blood-elf-spellarcher/animations/walk/west/1.png +0 -0
  154. package/assets/characters/blood-elf-spellarcher/animations/walk/west/2.png +0 -0
  155. package/assets/characters/blood-elf-spellarcher/animations/walk/west/3.png +0 -0
  156. package/assets/characters/blood-elf-spellarcher/animations/walk/west/4.png +0 -0
  157. package/assets/characters/blood-elf-spellarcher/animations/walk/west/5.png +0 -0
  158. package/assets/characters/blood-elf-spellarcher/animations/walk/west/6.png +0 -0
  159. package/assets/characters/blood-elf-spellarcher/animations/walk/west/7.png +0 -0
  160. package/assets/characters/blood-elf-spellarcher/animations/working/0.png +0 -0
  161. package/assets/characters/blood-elf-spellarcher/animations/working/1.png +0 -0
  162. package/assets/characters/blood-elf-spellarcher/animations/working/10.png +0 -0
  163. package/assets/characters/blood-elf-spellarcher/animations/working/11.png +0 -0
  164. package/assets/characters/blood-elf-spellarcher/animations/working/12.png +0 -0
  165. package/assets/characters/blood-elf-spellarcher/animations/working/13.png +0 -0
  166. package/assets/characters/blood-elf-spellarcher/animations/working/14.png +0 -0
  167. package/assets/characters/blood-elf-spellarcher/animations/working/15.png +0 -0
  168. package/assets/characters/blood-elf-spellarcher/animations/working/2.png +0 -0
  169. package/assets/characters/blood-elf-spellarcher/animations/working/3.png +0 -0
  170. package/assets/characters/blood-elf-spellarcher/animations/working/4.png +0 -0
  171. package/assets/characters/blood-elf-spellarcher/animations/working/5.png +0 -0
  172. package/assets/characters/blood-elf-spellarcher/animations/working/6.png +0 -0
  173. package/assets/characters/blood-elf-spellarcher/animations/working/7.png +0 -0
  174. package/assets/characters/blood-elf-spellarcher/animations/working/8.png +0 -0
  175. package/assets/characters/blood-elf-spellarcher/animations/working/9.png +0 -0
  176. package/assets/characters/blood-elf-spellarcher/rotations/east.png +0 -0
  177. package/assets/characters/blood-elf-spellarcher/rotations/north.png +0 -0
  178. package/assets/characters/blood-elf-spellarcher/rotations/south.png +0 -0
  179. package/assets/characters/blood-elf-spellarcher/rotations/west.png +0 -0
  180. package/assets/characters/blood-elf-spellarcher/voice-lines/arrive.mp3 +0 -0
  181. package/assets/characters/blood-elf-spellarcher/voice-lines/attention.mp3 +0 -0
  182. package/assets/characters/blood-elf-spellarcher/voice-lines/complete.mp3 +0 -0
  183. package/assets/characters/blood-elf-spellarcher/voice-lines/death.mp3 +0 -0
  184. package/assets/characters/blood-elf-spellarcher/voice-lines/move_variant_1.mp3 +0 -0
  185. package/assets/characters/blood-elf-spellarcher/voice-lines/move_variant_2.mp3 +0 -0
  186. package/assets/characters/blood-elf-spellarcher/voice-lines/move_variant_3.mp3 +0 -0
  187. package/assets/characters/blood-elf-spellarcher/voice-lines/selected_variant_1.mp3 +0 -0
  188. package/assets/characters/blood-elf-spellarcher/voice-lines/selected_variant_2.mp3 +0 -0
  189. package/assets/characters/blood-elf-spellarcher/voice-lines/selected_variant_3.mp3 +0 -0
  190. package/assets/characters/chronomancer/animations/walk/east/0.png +0 -0
  191. package/assets/characters/chronomancer/animations/walk/east/1.png +0 -0
  192. package/assets/characters/chronomancer/animations/walk/east/2.png +0 -0
  193. package/assets/characters/chronomancer/animations/walk/east/3.png +0 -0
  194. package/assets/characters/chronomancer/animations/walk/east/4.png +0 -0
  195. package/assets/characters/chronomancer/animations/walk/east/5.png +0 -0
  196. package/assets/characters/chronomancer/animations/walk/east/6.png +0 -0
  197. package/assets/characters/chronomancer/animations/walk/east/7.png +0 -0
  198. package/assets/characters/chronomancer/animations/walk/north/0.png +0 -0
  199. package/assets/characters/chronomancer/animations/walk/north/1.png +0 -0
  200. package/assets/characters/chronomancer/animations/walk/north/2.png +0 -0
  201. package/assets/characters/chronomancer/animations/walk/north/3.png +0 -0
  202. package/assets/characters/chronomancer/animations/walk/north/4.png +0 -0
  203. package/assets/characters/chronomancer/animations/walk/north/5.png +0 -0
  204. package/assets/characters/chronomancer/animations/walk/north/6.png +0 -0
  205. package/assets/characters/chronomancer/animations/walk/north/7.png +0 -0
  206. package/assets/characters/chronomancer/animations/walk/south/0.png +0 -0
  207. package/assets/characters/chronomancer/animations/walk/south/1.png +0 -0
  208. package/assets/characters/chronomancer/animations/walk/south/2.png +0 -0
  209. package/assets/characters/chronomancer/animations/walk/south/3.png +0 -0
  210. package/assets/characters/chronomancer/animations/walk/south/4.png +0 -0
  211. package/assets/characters/chronomancer/animations/walk/south/5.png +0 -0
  212. package/assets/characters/chronomancer/animations/walk/south/6.png +0 -0
  213. package/assets/characters/chronomancer/animations/walk/south/7.png +0 -0
  214. package/assets/characters/chronomancer/animations/walk/west/0.png +0 -0
  215. package/assets/characters/chronomancer/animations/walk/west/1.png +0 -0
  216. package/assets/characters/chronomancer/animations/walk/west/2.png +0 -0
  217. package/assets/characters/chronomancer/animations/walk/west/3.png +0 -0
  218. package/assets/characters/chronomancer/animations/walk/west/4.png +0 -0
  219. package/assets/characters/chronomancer/animations/walk/west/5.png +0 -0
  220. package/assets/characters/chronomancer/animations/walk/west/6.png +0 -0
  221. package/assets/characters/chronomancer/animations/walk/west/7.png +0 -0
  222. package/assets/characters/chronomancer/animations/working/0.png +0 -0
  223. package/assets/characters/chronomancer/animations/working/1.png +0 -0
  224. package/assets/characters/chronomancer/animations/working/10.png +0 -0
  225. package/assets/characters/chronomancer/animations/working/11.png +0 -0
  226. package/assets/characters/chronomancer/animations/working/12.png +0 -0
  227. package/assets/characters/chronomancer/animations/working/13.png +0 -0
  228. package/assets/characters/chronomancer/animations/working/14.png +0 -0
  229. package/assets/characters/chronomancer/animations/working/15.png +0 -0
  230. package/assets/characters/chronomancer/animations/working/2.png +0 -0
  231. package/assets/characters/chronomancer/animations/working/3.png +0 -0
  232. package/assets/characters/chronomancer/animations/working/4.png +0 -0
  233. package/assets/characters/chronomancer/animations/working/5.png +0 -0
  234. package/assets/characters/chronomancer/animations/working/6.png +0 -0
  235. package/assets/characters/chronomancer/animations/working/7.png +0 -0
  236. package/assets/characters/chronomancer/animations/working/8.png +0 -0
  237. package/assets/characters/chronomancer/animations/working/9.png +0 -0
  238. package/assets/characters/chronomancer/rotations/east.png +0 -0
  239. package/assets/characters/chronomancer/rotations/north.png +0 -0
  240. package/assets/characters/chronomancer/rotations/south.png +0 -0
  241. package/assets/characters/chronomancer/rotations/west.png +0 -0
  242. package/assets/characters/chronomancer/voice-lines/arrive.mp3 +0 -0
  243. package/assets/characters/chronomancer/voice-lines/attention.mp3 +0 -0
  244. package/assets/characters/chronomancer/voice-lines/complete.mp3 +0 -0
  245. package/assets/characters/chronomancer/voice-lines/death.mp3 +0 -0
  246. package/assets/characters/chronomancer/voice-lines/move_variant_1.mp3 +0 -0
  247. package/assets/characters/chronomancer/voice-lines/move_variant_2.mp3 +0 -0
  248. package/assets/characters/chronomancer/voice-lines/move_variant_3.mp3 +0 -0
  249. package/assets/characters/chronomancer/voice-lines/selected_variant_1.mp3 +0 -0
  250. package/assets/characters/chronomancer/voice-lines/selected_variant_2.mp3 +0 -0
  251. package/assets/characters/chronomancer/voice-lines/selected_variant_3.mp3 +0 -0
  252. package/assets/characters/crystal-mage/animations/walk/east/0.png +0 -0
  253. package/assets/characters/crystal-mage/animations/walk/east/1.png +0 -0
  254. package/assets/characters/crystal-mage/animations/walk/east/2.png +0 -0
  255. package/assets/characters/crystal-mage/animations/walk/east/3.png +0 -0
  256. package/assets/characters/crystal-mage/animations/walk/east/4.png +0 -0
  257. package/assets/characters/crystal-mage/animations/walk/east/5.png +0 -0
  258. package/assets/characters/crystal-mage/animations/walk/east/6.png +0 -0
  259. package/assets/characters/crystal-mage/animations/walk/east/7.png +0 -0
  260. package/assets/characters/crystal-mage/animations/walk/north/0.png +0 -0
  261. package/assets/characters/crystal-mage/animations/walk/north/1.png +0 -0
  262. package/assets/characters/crystal-mage/animations/walk/north/2.png +0 -0
  263. package/assets/characters/crystal-mage/animations/walk/north/3.png +0 -0
  264. package/assets/characters/crystal-mage/animations/walk/north/4.png +0 -0
  265. package/assets/characters/crystal-mage/animations/walk/north/5.png +0 -0
  266. package/assets/characters/crystal-mage/animations/walk/north/6.png +0 -0
  267. package/assets/characters/crystal-mage/animations/walk/north/7.png +0 -0
  268. package/assets/characters/crystal-mage/animations/walk/south/0.png +0 -0
  269. package/assets/characters/crystal-mage/animations/walk/south/1.png +0 -0
  270. package/assets/characters/crystal-mage/animations/walk/south/2.png +0 -0
  271. package/assets/characters/crystal-mage/animations/walk/south/3.png +0 -0
  272. package/assets/characters/crystal-mage/animations/walk/south/4.png +0 -0
  273. package/assets/characters/crystal-mage/animations/walk/south/5.png +0 -0
  274. package/assets/characters/crystal-mage/animations/walk/south/6.png +0 -0
  275. package/assets/characters/crystal-mage/animations/walk/south/7.png +0 -0
  276. package/assets/characters/crystal-mage/animations/walk/west/0.png +0 -0
  277. package/assets/characters/crystal-mage/animations/walk/west/1.png +0 -0
  278. package/assets/characters/crystal-mage/animations/walk/west/2.png +0 -0
  279. package/assets/characters/crystal-mage/animations/walk/west/3.png +0 -0
  280. package/assets/characters/crystal-mage/animations/walk/west/4.png +0 -0
  281. package/assets/characters/crystal-mage/animations/walk/west/5.png +0 -0
  282. package/assets/characters/crystal-mage/animations/walk/west/6.png +0 -0
  283. package/assets/characters/crystal-mage/animations/walk/west/7.png +0 -0
  284. package/assets/characters/crystal-mage/animations/working/0.png +0 -0
  285. package/assets/characters/crystal-mage/animations/working/1.png +0 -0
  286. package/assets/characters/crystal-mage/animations/working/10.png +0 -0
  287. package/assets/characters/crystal-mage/animations/working/11.png +0 -0
  288. package/assets/characters/crystal-mage/animations/working/12.png +0 -0
  289. package/assets/characters/crystal-mage/animations/working/13.png +0 -0
  290. package/assets/characters/crystal-mage/animations/working/14.png +0 -0
  291. package/assets/characters/crystal-mage/animations/working/15.png +0 -0
  292. package/assets/characters/crystal-mage/animations/working/2.png +0 -0
  293. package/assets/characters/crystal-mage/animations/working/3.png +0 -0
  294. package/assets/characters/crystal-mage/animations/working/4.png +0 -0
  295. package/assets/characters/crystal-mage/animations/working/5.png +0 -0
  296. package/assets/characters/crystal-mage/animations/working/6.png +0 -0
  297. package/assets/characters/crystal-mage/animations/working/7.png +0 -0
  298. package/assets/characters/crystal-mage/animations/working/8.png +0 -0
  299. package/assets/characters/crystal-mage/animations/working/9.png +0 -0
  300. package/assets/characters/crystal-mage/rotations/east.png +0 -0
  301. package/assets/characters/crystal-mage/rotations/north.png +0 -0
  302. package/assets/characters/crystal-mage/rotations/south.png +0 -0
  303. package/assets/characters/crystal-mage/rotations/west.png +0 -0
  304. package/assets/characters/crystal-mage/voice-lines/arrive.mp3 +0 -0
  305. package/assets/characters/crystal-mage/voice-lines/attention.mp3 +0 -0
  306. package/assets/characters/crystal-mage/voice-lines/complete.mp3 +0 -0
  307. package/assets/characters/crystal-mage/voice-lines/death.mp3 +0 -0
  308. package/assets/characters/crystal-mage/voice-lines/move_variant_1.mp3 +0 -0
  309. package/assets/characters/crystal-mage/voice-lines/move_variant_2.mp3 +0 -0
  310. package/assets/characters/crystal-mage/voice-lines/move_variant_3.mp3 +0 -0
  311. package/assets/characters/crystal-mage/voice-lines/selected_variant_1.mp3 +0 -0
  312. package/assets/characters/crystal-mage/voice-lines/selected_variant_2.mp3 +0 -0
  313. package/assets/characters/crystal-mage/voice-lines/selected_variant_3.mp3 +0 -0
  314. package/assets/characters/dwarf-warrior-short/animations/walk/east/0.png +0 -0
  315. package/assets/characters/dwarf-warrior-short/animations/walk/east/1.png +0 -0
  316. package/assets/characters/dwarf-warrior-short/animations/walk/east/2.png +0 -0
  317. package/assets/characters/dwarf-warrior-short/animations/walk/east/3.png +0 -0
  318. package/assets/characters/dwarf-warrior-short/animations/walk/east/4.png +0 -0
  319. package/assets/characters/dwarf-warrior-short/animations/walk/east/5.png +0 -0
  320. package/assets/characters/dwarf-warrior-short/animations/walk/east/6.png +0 -0
  321. package/assets/characters/dwarf-warrior-short/animations/walk/east/7.png +0 -0
  322. package/assets/characters/dwarf-warrior-short/animations/walk/north/0.png +0 -0
  323. package/assets/characters/dwarf-warrior-short/animations/walk/north/1.png +0 -0
  324. package/assets/characters/dwarf-warrior-short/animations/walk/north/2.png +0 -0
  325. package/assets/characters/dwarf-warrior-short/animations/walk/north/3.png +0 -0
  326. package/assets/characters/dwarf-warrior-short/animations/walk/north/4.png +0 -0
  327. package/assets/characters/dwarf-warrior-short/animations/walk/north/5.png +0 -0
  328. package/assets/characters/dwarf-warrior-short/animations/walk/north/6.png +0 -0
  329. package/assets/characters/dwarf-warrior-short/animations/walk/north/7.png +0 -0
  330. package/assets/characters/dwarf-warrior-short/animations/walk/south/0.png +0 -0
  331. package/assets/characters/dwarf-warrior-short/animations/walk/south/1.png +0 -0
  332. package/assets/characters/dwarf-warrior-short/animations/walk/south/2.png +0 -0
  333. package/assets/characters/dwarf-warrior-short/animations/walk/south/3.png +0 -0
  334. package/assets/characters/dwarf-warrior-short/animations/walk/south/4.png +0 -0
  335. package/assets/characters/dwarf-warrior-short/animations/walk/south/5.png +0 -0
  336. package/assets/characters/dwarf-warrior-short/animations/walk/south/6.png +0 -0
  337. package/assets/characters/dwarf-warrior-short/animations/walk/south/7.png +0 -0
  338. package/assets/characters/dwarf-warrior-short/animations/walk/west/0.png +0 -0
  339. package/assets/characters/dwarf-warrior-short/animations/walk/west/1.png +0 -0
  340. package/assets/characters/dwarf-warrior-short/animations/walk/west/2.png +0 -0
  341. package/assets/characters/dwarf-warrior-short/animations/walk/west/3.png +0 -0
  342. package/assets/characters/dwarf-warrior-short/animations/walk/west/4.png +0 -0
  343. package/assets/characters/dwarf-warrior-short/animations/walk/west/5.png +0 -0
  344. package/assets/characters/dwarf-warrior-short/animations/walk/west/6.png +0 -0
  345. package/assets/characters/dwarf-warrior-short/animations/walk/west/7.png +0 -0
  346. package/assets/characters/dwarf-warrior-short/animations/working/0.png +0 -0
  347. package/assets/characters/dwarf-warrior-short/animations/working/1.png +0 -0
  348. package/assets/characters/dwarf-warrior-short/animations/working/10.png +0 -0
  349. package/assets/characters/dwarf-warrior-short/animations/working/11.png +0 -0
  350. package/assets/characters/dwarf-warrior-short/animations/working/12.png +0 -0
  351. package/assets/characters/dwarf-warrior-short/animations/working/13.png +0 -0
  352. package/assets/characters/dwarf-warrior-short/animations/working/14.png +0 -0
  353. package/assets/characters/dwarf-warrior-short/animations/working/15.png +0 -0
  354. package/assets/characters/dwarf-warrior-short/animations/working/2.png +0 -0
  355. package/assets/characters/dwarf-warrior-short/animations/working/3.png +0 -0
  356. package/assets/characters/dwarf-warrior-short/animations/working/4.png +0 -0
  357. package/assets/characters/dwarf-warrior-short/animations/working/5.png +0 -0
  358. package/assets/characters/dwarf-warrior-short/animations/working/6.png +0 -0
  359. package/assets/characters/dwarf-warrior-short/animations/working/7.png +0 -0
  360. package/assets/characters/dwarf-warrior-short/animations/working/8.png +0 -0
  361. package/assets/characters/dwarf-warrior-short/animations/working/9.png +0 -0
  362. package/assets/characters/dwarf-warrior-short/rotations/east.png +0 -0
  363. package/assets/characters/dwarf-warrior-short/rotations/north.png +0 -0
  364. package/assets/characters/dwarf-warrior-short/rotations/south.png +0 -0
  365. package/assets/characters/dwarf-warrior-short/rotations/west.png +0 -0
  366. package/assets/characters/dwarf-warrior-short/voice-lines/arrive.mp3 +0 -0
  367. package/assets/characters/dwarf-warrior-short/voice-lines/attention.mp3 +0 -0
  368. package/assets/characters/dwarf-warrior-short/voice-lines/complete.mp3 +0 -0
  369. package/assets/characters/dwarf-warrior-short/voice-lines/death.mp3 +0 -0
  370. package/assets/characters/dwarf-warrior-short/voice-lines/move_variant_1.mp3 +0 -0
  371. package/assets/characters/dwarf-warrior-short/voice-lines/move_variant_2.mp3 +0 -0
  372. package/assets/characters/dwarf-warrior-short/voice-lines/move_variant_3.mp3 +0 -0
  373. package/assets/characters/dwarf-warrior-short/voice-lines/selected_variant_1.mp3 +0 -0
  374. package/assets/characters/dwarf-warrior-short/voice-lines/selected_variant_2.mp3 +0 -0
  375. package/assets/characters/dwarf-warrior-short/voice-lines/selected_variant_3.mp3 +0 -0
  376. package/assets/characters/elder-wizard/animations/walk/east/0.png +0 -0
  377. package/assets/characters/elder-wizard/animations/walk/east/1.png +0 -0
  378. package/assets/characters/elder-wizard/animations/walk/east/2.png +0 -0
  379. package/assets/characters/elder-wizard/animations/walk/east/3.png +0 -0
  380. package/assets/characters/elder-wizard/animations/walk/east/4.png +0 -0
  381. package/assets/characters/elder-wizard/animations/walk/east/5.png +0 -0
  382. package/assets/characters/elder-wizard/animations/walk/east/6.png +0 -0
  383. package/assets/characters/elder-wizard/animations/walk/east/7.png +0 -0
  384. package/assets/characters/elder-wizard/animations/walk/north/0.png +0 -0
  385. package/assets/characters/elder-wizard/animations/walk/north/1.png +0 -0
  386. package/assets/characters/elder-wizard/animations/walk/north/2.png +0 -0
  387. package/assets/characters/elder-wizard/animations/walk/north/3.png +0 -0
  388. package/assets/characters/elder-wizard/animations/walk/north/4.png +0 -0
  389. package/assets/characters/elder-wizard/animations/walk/north/5.png +0 -0
  390. package/assets/characters/elder-wizard/animations/walk/north/6.png +0 -0
  391. package/assets/characters/elder-wizard/animations/walk/north/7.png +0 -0
  392. package/assets/characters/elder-wizard/animations/walk/south/0.png +0 -0
  393. package/assets/characters/elder-wizard/animations/walk/south/1.png +0 -0
  394. package/assets/characters/elder-wizard/animations/walk/south/2.png +0 -0
  395. package/assets/characters/elder-wizard/animations/walk/south/3.png +0 -0
  396. package/assets/characters/elder-wizard/animations/walk/south/4.png +0 -0
  397. package/assets/characters/elder-wizard/animations/walk/south/5.png +0 -0
  398. package/assets/characters/elder-wizard/animations/walk/south/6.png +0 -0
  399. package/assets/characters/elder-wizard/animations/walk/south/7.png +0 -0
  400. package/assets/characters/elder-wizard/animations/walk/west/0.png +0 -0
  401. package/assets/characters/elder-wizard/animations/walk/west/1.png +0 -0
  402. package/assets/characters/elder-wizard/animations/walk/west/2.png +0 -0
  403. package/assets/characters/elder-wizard/animations/walk/west/3.png +0 -0
  404. package/assets/characters/elder-wizard/animations/walk/west/4.png +0 -0
  405. package/assets/characters/elder-wizard/animations/walk/west/5.png +0 -0
  406. package/assets/characters/elder-wizard/animations/walk/west/6.png +0 -0
  407. package/assets/characters/elder-wizard/animations/walk/west/7.png +0 -0
  408. package/assets/characters/elder-wizard/animations/working/0.png +0 -0
  409. package/assets/characters/elder-wizard/animations/working/1.png +0 -0
  410. package/assets/characters/elder-wizard/animations/working/10.png +0 -0
  411. package/assets/characters/elder-wizard/animations/working/11.png +0 -0
  412. package/assets/characters/elder-wizard/animations/working/12.png +0 -0
  413. package/assets/characters/elder-wizard/animations/working/13.png +0 -0
  414. package/assets/characters/elder-wizard/animations/working/14.png +0 -0
  415. package/assets/characters/elder-wizard/animations/working/15.png +0 -0
  416. package/assets/characters/elder-wizard/animations/working/2.png +0 -0
  417. package/assets/characters/elder-wizard/animations/working/3.png +0 -0
  418. package/assets/characters/elder-wizard/animations/working/4.png +0 -0
  419. package/assets/characters/elder-wizard/animations/working/5.png +0 -0
  420. package/assets/characters/elder-wizard/animations/working/6.png +0 -0
  421. package/assets/characters/elder-wizard/animations/working/7.png +0 -0
  422. package/assets/characters/elder-wizard/animations/working/8.png +0 -0
  423. package/assets/characters/elder-wizard/animations/working/9.png +0 -0
  424. package/assets/characters/elder-wizard/rotations/east.png +0 -0
  425. package/assets/characters/elder-wizard/rotations/north.png +0 -0
  426. package/assets/characters/elder-wizard/rotations/south.png +0 -0
  427. package/assets/characters/elder-wizard/rotations/west.png +0 -0
  428. package/assets/characters/elder-wizard/voice-lines/arrive.mp3 +0 -0
  429. package/assets/characters/elder-wizard/voice-lines/attention.mp3 +0 -0
  430. package/assets/characters/elder-wizard/voice-lines/complete.mp3 +0 -0
  431. package/assets/characters/elder-wizard/voice-lines/death.mp3 +0 -0
  432. package/assets/characters/elder-wizard/voice-lines/move_variant_1.mp3 +0 -0
  433. package/assets/characters/elder-wizard/voice-lines/move_variant_2.mp3 +0 -0
  434. package/assets/characters/elder-wizard/voice-lines/move_variant_3.mp3 +0 -0
  435. package/assets/characters/elder-wizard/voice-lines/selected_variant_1.mp3 +0 -0
  436. package/assets/characters/elder-wizard/voice-lines/selected_variant_2.mp3 +0 -0
  437. package/assets/characters/elder-wizard/voice-lines/selected_variant_3.mp3 +0 -0
  438. package/assets/characters/gothic-witch/animations/walk/east/0.png +0 -0
  439. package/assets/characters/gothic-witch/animations/walk/east/1.png +0 -0
  440. package/assets/characters/gothic-witch/animations/walk/east/2.png +0 -0
  441. package/assets/characters/gothic-witch/animations/walk/east/3.png +0 -0
  442. package/assets/characters/gothic-witch/animations/walk/east/4.png +0 -0
  443. package/assets/characters/gothic-witch/animations/walk/east/5.png +0 -0
  444. package/assets/characters/gothic-witch/animations/walk/east/6.png +0 -0
  445. package/assets/characters/gothic-witch/animations/walk/east/7.png +0 -0
  446. package/assets/characters/gothic-witch/animations/walk/north/0.png +0 -0
  447. package/assets/characters/gothic-witch/animations/walk/north/1.png +0 -0
  448. package/assets/characters/gothic-witch/animations/walk/north/2.png +0 -0
  449. package/assets/characters/gothic-witch/animations/walk/north/3.png +0 -0
  450. package/assets/characters/gothic-witch/animations/walk/north/4.png +0 -0
  451. package/assets/characters/gothic-witch/animations/walk/north/5.png +0 -0
  452. package/assets/characters/gothic-witch/animations/walk/north/6.png +0 -0
  453. package/assets/characters/gothic-witch/animations/walk/north/7.png +0 -0
  454. package/assets/characters/gothic-witch/animations/walk/south/0.png +0 -0
  455. package/assets/characters/gothic-witch/animations/walk/south/1.png +0 -0
  456. package/assets/characters/gothic-witch/animations/walk/south/2.png +0 -0
  457. package/assets/characters/gothic-witch/animations/walk/south/3.png +0 -0
  458. package/assets/characters/gothic-witch/animations/walk/south/4.png +0 -0
  459. package/assets/characters/gothic-witch/animations/walk/south/5.png +0 -0
  460. package/assets/characters/gothic-witch/animations/walk/south/6.png +0 -0
  461. package/assets/characters/gothic-witch/animations/walk/south/7.png +0 -0
  462. package/assets/characters/gothic-witch/animations/walk/west/0.png +0 -0
  463. package/assets/characters/gothic-witch/animations/walk/west/1.png +0 -0
  464. package/assets/characters/gothic-witch/animations/walk/west/2.png +0 -0
  465. package/assets/characters/gothic-witch/animations/walk/west/3.png +0 -0
  466. package/assets/characters/gothic-witch/animations/walk/west/4.png +0 -0
  467. package/assets/characters/gothic-witch/animations/walk/west/5.png +0 -0
  468. package/assets/characters/gothic-witch/animations/walk/west/6.png +0 -0
  469. package/assets/characters/gothic-witch/animations/walk/west/7.png +0 -0
  470. package/assets/characters/gothic-witch/animations/working/0.png +0 -0
  471. package/assets/characters/gothic-witch/animations/working/1.png +0 -0
  472. package/assets/characters/gothic-witch/animations/working/10.png +0 -0
  473. package/assets/characters/gothic-witch/animations/working/11.png +0 -0
  474. package/assets/characters/gothic-witch/animations/working/12.png +0 -0
  475. package/assets/characters/gothic-witch/animations/working/13.png +0 -0
  476. package/assets/characters/gothic-witch/animations/working/14.png +0 -0
  477. package/assets/characters/gothic-witch/animations/working/15.png +0 -0
  478. package/assets/characters/gothic-witch/animations/working/2.png +0 -0
  479. package/assets/characters/gothic-witch/animations/working/3.png +0 -0
  480. package/assets/characters/gothic-witch/animations/working/4.png +0 -0
  481. package/assets/characters/gothic-witch/animations/working/5.png +0 -0
  482. package/assets/characters/gothic-witch/animations/working/6.png +0 -0
  483. package/assets/characters/gothic-witch/animations/working/7.png +0 -0
  484. package/assets/characters/gothic-witch/animations/working/8.png +0 -0
  485. package/assets/characters/gothic-witch/animations/working/9.png +0 -0
  486. package/assets/characters/gothic-witch/rotations/east.png +0 -0
  487. package/assets/characters/gothic-witch/rotations/north.png +0 -0
  488. package/assets/characters/gothic-witch/rotations/south.png +0 -0
  489. package/assets/characters/gothic-witch/rotations/west.png +0 -0
  490. package/assets/characters/gothic-witch/voice-lines/arrive.mp3 +0 -0
  491. package/assets/characters/gothic-witch/voice-lines/attention.mp3 +0 -0
  492. package/assets/characters/gothic-witch/voice-lines/complete.mp3 +0 -0
  493. package/assets/characters/gothic-witch/voice-lines/death.mp3 +0 -0
  494. package/assets/characters/gothic-witch/voice-lines/move_variant_1.mp3 +0 -0
  495. package/assets/characters/gothic-witch/voice-lines/move_variant_2.mp3 +0 -0
  496. package/assets/characters/gothic-witch/voice-lines/move_variant_3.mp3 +0 -0
  497. package/assets/characters/gothic-witch/voice-lines/selected_variant_1.mp3 +0 -0
  498. package/assets/characters/gothic-witch/voice-lines/selected_variant_2.mp3 +0 -0
  499. package/assets/characters/gothic-witch/voice-lines/selected_variant_3.mp3 +0 -0
  500. package/assets/characters/horned-helm-fighter/animations/walk/east/0.png +0 -0
  501. package/assets/characters/horned-helm-fighter/animations/walk/east/1.png +0 -0
  502. package/assets/characters/horned-helm-fighter/animations/walk/east/2.png +0 -0
  503. package/assets/characters/horned-helm-fighter/animations/walk/east/3.png +0 -0
  504. package/assets/characters/horned-helm-fighter/animations/walk/east/4.png +0 -0
  505. package/assets/characters/horned-helm-fighter/animations/walk/east/5.png +0 -0
  506. package/assets/characters/horned-helm-fighter/animations/walk/east/6.png +0 -0
  507. package/assets/characters/horned-helm-fighter/animations/walk/east/7.png +0 -0
  508. package/assets/characters/horned-helm-fighter/animations/walk/north/0.png +0 -0
  509. package/assets/characters/horned-helm-fighter/animations/walk/north/1.png +0 -0
  510. package/assets/characters/horned-helm-fighter/animations/walk/north/2.png +0 -0
  511. package/assets/characters/horned-helm-fighter/animations/walk/north/3.png +0 -0
  512. package/assets/characters/horned-helm-fighter/animations/walk/north/4.png +0 -0
  513. package/assets/characters/horned-helm-fighter/animations/walk/north/5.png +0 -0
  514. package/assets/characters/horned-helm-fighter/animations/walk/north/6.png +0 -0
  515. package/assets/characters/horned-helm-fighter/animations/walk/north/7.png +0 -0
  516. package/assets/characters/horned-helm-fighter/animations/walk/south/0.png +0 -0
  517. package/assets/characters/horned-helm-fighter/animations/walk/south/1.png +0 -0
  518. package/assets/characters/horned-helm-fighter/animations/walk/south/2.png +0 -0
  519. package/assets/characters/horned-helm-fighter/animations/walk/south/3.png +0 -0
  520. package/assets/characters/horned-helm-fighter/animations/walk/south/4.png +0 -0
  521. package/assets/characters/horned-helm-fighter/animations/walk/south/5.png +0 -0
  522. package/assets/characters/horned-helm-fighter/animations/walk/south/6.png +0 -0
  523. package/assets/characters/horned-helm-fighter/animations/walk/south/7.png +0 -0
  524. package/assets/characters/horned-helm-fighter/animations/walk/west/0.png +0 -0
  525. package/assets/characters/horned-helm-fighter/animations/walk/west/1.png +0 -0
  526. package/assets/characters/horned-helm-fighter/animations/walk/west/2.png +0 -0
  527. package/assets/characters/horned-helm-fighter/animations/walk/west/3.png +0 -0
  528. package/assets/characters/horned-helm-fighter/animations/walk/west/4.png +0 -0
  529. package/assets/characters/horned-helm-fighter/animations/walk/west/5.png +0 -0
  530. package/assets/characters/horned-helm-fighter/animations/walk/west/6.png +0 -0
  531. package/assets/characters/horned-helm-fighter/animations/walk/west/7.png +0 -0
  532. package/assets/characters/horned-helm-fighter/animations/working/0.png +0 -0
  533. package/assets/characters/horned-helm-fighter/animations/working/1.png +0 -0
  534. package/assets/characters/horned-helm-fighter/animations/working/10.png +0 -0
  535. package/assets/characters/horned-helm-fighter/animations/working/11.png +0 -0
  536. package/assets/characters/horned-helm-fighter/animations/working/12.png +0 -0
  537. package/assets/characters/horned-helm-fighter/animations/working/13.png +0 -0
  538. package/assets/characters/horned-helm-fighter/animations/working/14.png +0 -0
  539. package/assets/characters/horned-helm-fighter/animations/working/15.png +0 -0
  540. package/assets/characters/horned-helm-fighter/animations/working/2.png +0 -0
  541. package/assets/characters/horned-helm-fighter/animations/working/3.png +0 -0
  542. package/assets/characters/horned-helm-fighter/animations/working/4.png +0 -0
  543. package/assets/characters/horned-helm-fighter/animations/working/5.png +0 -0
  544. package/assets/characters/horned-helm-fighter/animations/working/6.png +0 -0
  545. package/assets/characters/horned-helm-fighter/animations/working/7.png +0 -0
  546. package/assets/characters/horned-helm-fighter/animations/working/8.png +0 -0
  547. package/assets/characters/horned-helm-fighter/animations/working/9.png +0 -0
  548. package/assets/characters/horned-helm-fighter/rotations/east.png +0 -0
  549. package/assets/characters/horned-helm-fighter/rotations/north.png +0 -0
  550. package/assets/characters/horned-helm-fighter/rotations/south.png +0 -0
  551. package/assets/characters/horned-helm-fighter/rotations/west.png +0 -0
  552. package/assets/characters/horned-helm-fighter/voice-lines/arrive.mp3 +0 -0
  553. package/assets/characters/horned-helm-fighter/voice-lines/attention.mp3 +0 -0
  554. package/assets/characters/horned-helm-fighter/voice-lines/complete.mp3 +0 -0
  555. package/assets/characters/horned-helm-fighter/voice-lines/death.mp3 +0 -0
  556. package/assets/characters/horned-helm-fighter/voice-lines/move_variant_1.mp3 +0 -0
  557. package/assets/characters/horned-helm-fighter/voice-lines/move_variant_2.mp3 +0 -0
  558. package/assets/characters/horned-helm-fighter/voice-lines/move_variant_3.mp3 +0 -0
  559. package/assets/characters/horned-helm-fighter/voice-lines/selected_variant_1.mp3 +0 -0
  560. package/assets/characters/horned-helm-fighter/voice-lines/selected_variant_2.mp3 +0 -0
  561. package/assets/characters/horned-helm-fighter/voice-lines/selected_variant_3.mp3 +0 -0
  562. package/assets/characters/knight-blue-guardian/animations/walk/east/0.png +0 -0
  563. package/assets/characters/knight-blue-guardian/animations/walk/east/1.png +0 -0
  564. package/assets/characters/knight-blue-guardian/animations/walk/east/2.png +0 -0
  565. package/assets/characters/knight-blue-guardian/animations/walk/east/3.png +0 -0
  566. package/assets/characters/knight-blue-guardian/animations/walk/east/4.png +0 -0
  567. package/assets/characters/knight-blue-guardian/animations/walk/east/5.png +0 -0
  568. package/assets/characters/knight-blue-guardian/animations/walk/east/6.png +0 -0
  569. package/assets/characters/knight-blue-guardian/animations/walk/east/7.png +0 -0
  570. package/assets/characters/knight-blue-guardian/animations/walk/north/0.png +0 -0
  571. package/assets/characters/knight-blue-guardian/animations/walk/north/1.png +0 -0
  572. package/assets/characters/knight-blue-guardian/animations/walk/north/2.png +0 -0
  573. package/assets/characters/knight-blue-guardian/animations/walk/north/3.png +0 -0
  574. package/assets/characters/knight-blue-guardian/animations/walk/north/4.png +0 -0
  575. package/assets/characters/knight-blue-guardian/animations/walk/north/5.png +0 -0
  576. package/assets/characters/knight-blue-guardian/animations/walk/north/6.png +0 -0
  577. package/assets/characters/knight-blue-guardian/animations/walk/north/7.png +0 -0
  578. package/assets/characters/knight-blue-guardian/animations/walk/south/0.png +0 -0
  579. package/assets/characters/knight-blue-guardian/animations/walk/south/1.png +0 -0
  580. package/assets/characters/knight-blue-guardian/animations/walk/south/2.png +0 -0
  581. package/assets/characters/knight-blue-guardian/animations/walk/south/3.png +0 -0
  582. package/assets/characters/knight-blue-guardian/animations/walk/south/4.png +0 -0
  583. package/assets/characters/knight-blue-guardian/animations/walk/south/5.png +0 -0
  584. package/assets/characters/knight-blue-guardian/animations/walk/south/6.png +0 -0
  585. package/assets/characters/knight-blue-guardian/animations/walk/south/7.png +0 -0
  586. package/assets/characters/knight-blue-guardian/animations/walk/west/0.png +0 -0
  587. package/assets/characters/knight-blue-guardian/animations/walk/west/1.png +0 -0
  588. package/assets/characters/knight-blue-guardian/animations/walk/west/2.png +0 -0
  589. package/assets/characters/knight-blue-guardian/animations/walk/west/3.png +0 -0
  590. package/assets/characters/knight-blue-guardian/animations/walk/west/4.png +0 -0
  591. package/assets/characters/knight-blue-guardian/animations/walk/west/5.png +0 -0
  592. package/assets/characters/knight-blue-guardian/animations/walk/west/6.png +0 -0
  593. package/assets/characters/knight-blue-guardian/animations/walk/west/7.png +0 -0
  594. package/assets/characters/knight-blue-guardian/animations/working/0.png +0 -0
  595. package/assets/characters/knight-blue-guardian/animations/working/1.png +0 -0
  596. package/assets/characters/knight-blue-guardian/animations/working/10.png +0 -0
  597. package/assets/characters/knight-blue-guardian/animations/working/11.png +0 -0
  598. package/assets/characters/knight-blue-guardian/animations/working/12.png +0 -0
  599. package/assets/characters/knight-blue-guardian/animations/working/13.png +0 -0
  600. package/assets/characters/knight-blue-guardian/animations/working/14.png +0 -0
  601. package/assets/characters/knight-blue-guardian/animations/working/15.png +0 -0
  602. package/assets/characters/knight-blue-guardian/animations/working/2.png +0 -0
  603. package/assets/characters/knight-blue-guardian/animations/working/3.png +0 -0
  604. package/assets/characters/knight-blue-guardian/animations/working/4.png +0 -0
  605. package/assets/characters/knight-blue-guardian/animations/working/5.png +0 -0
  606. package/assets/characters/knight-blue-guardian/animations/working/6.png +0 -0
  607. package/assets/characters/knight-blue-guardian/animations/working/7.png +0 -0
  608. package/assets/characters/knight-blue-guardian/animations/working/8.png +0 -0
  609. package/assets/characters/knight-blue-guardian/animations/working/9.png +0 -0
  610. package/assets/characters/knight-blue-guardian/rotations/east.png +0 -0
  611. package/assets/characters/knight-blue-guardian/rotations/north.png +0 -0
  612. package/assets/characters/knight-blue-guardian/rotations/south.png +0 -0
  613. package/assets/characters/knight-blue-guardian/rotations/west.png +0 -0
  614. package/assets/characters/knight-blue-guardian/voice-lines/arrive.mp3 +0 -0
  615. package/assets/characters/knight-blue-guardian/voice-lines/attention.mp3 +0 -0
  616. package/assets/characters/knight-blue-guardian/voice-lines/complete.mp3 +0 -0
  617. package/assets/characters/knight-blue-guardian/voice-lines/death.mp3 +0 -0
  618. package/assets/characters/knight-blue-guardian/voice-lines/move_variant_1.mp3 +0 -0
  619. package/assets/characters/knight-blue-guardian/voice-lines/move_variant_2.mp3 +0 -0
  620. package/assets/characters/knight-blue-guardian/voice-lines/move_variant_3.mp3 +0 -0
  621. package/assets/characters/knight-blue-guardian/voice-lines/selected_variant_1.mp3 +0 -0
  622. package/assets/characters/knight-blue-guardian/voice-lines/selected_variant_2.mp3 +0 -0
  623. package/assets/characters/knight-blue-guardian/voice-lines/selected_variant_3.mp3 +0 -0
  624. package/assets/characters/launch-preview.html +298 -0
  625. package/assets/characters/minotaur-strategist/animations/walk/east/0.png +0 -0
  626. package/assets/characters/minotaur-strategist/animations/walk/east/1.png +0 -0
  627. package/assets/characters/minotaur-strategist/animations/walk/east/2.png +0 -0
  628. package/assets/characters/minotaur-strategist/animations/walk/east/3.png +0 -0
  629. package/assets/characters/minotaur-strategist/animations/walk/east/4.png +0 -0
  630. package/assets/characters/minotaur-strategist/animations/walk/east/5.png +0 -0
  631. package/assets/characters/minotaur-strategist/animations/walk/east/6.png +0 -0
  632. package/assets/characters/minotaur-strategist/animations/walk/east/7.png +0 -0
  633. package/assets/characters/minotaur-strategist/animations/walk/north/0.png +0 -0
  634. package/assets/characters/minotaur-strategist/animations/walk/north/1.png +0 -0
  635. package/assets/characters/minotaur-strategist/animations/walk/north/2.png +0 -0
  636. package/assets/characters/minotaur-strategist/animations/walk/north/3.png +0 -0
  637. package/assets/characters/minotaur-strategist/animations/walk/north/4.png +0 -0
  638. package/assets/characters/minotaur-strategist/animations/walk/north/5.png +0 -0
  639. package/assets/characters/minotaur-strategist/animations/walk/north/6.png +0 -0
  640. package/assets/characters/minotaur-strategist/animations/walk/north/7.png +0 -0
  641. package/assets/characters/minotaur-strategist/animations/walk/south/0.png +0 -0
  642. package/assets/characters/minotaur-strategist/animations/walk/south/1.png +0 -0
  643. package/assets/characters/minotaur-strategist/animations/walk/south/2.png +0 -0
  644. package/assets/characters/minotaur-strategist/animations/walk/south/3.png +0 -0
  645. package/assets/characters/minotaur-strategist/animations/walk/south/4.png +0 -0
  646. package/assets/characters/minotaur-strategist/animations/walk/south/5.png +0 -0
  647. package/assets/characters/minotaur-strategist/animations/walk/south/6.png +0 -0
  648. package/assets/characters/minotaur-strategist/animations/walk/south/7.png +0 -0
  649. package/assets/characters/minotaur-strategist/animations/walk/west/0.png +0 -0
  650. package/assets/characters/minotaur-strategist/animations/walk/west/1.png +0 -0
  651. package/assets/characters/minotaur-strategist/animations/walk/west/2.png +0 -0
  652. package/assets/characters/minotaur-strategist/animations/walk/west/3.png +0 -0
  653. package/assets/characters/minotaur-strategist/animations/walk/west/4.png +0 -0
  654. package/assets/characters/minotaur-strategist/animations/walk/west/5.png +0 -0
  655. package/assets/characters/minotaur-strategist/animations/walk/west/6.png +0 -0
  656. package/assets/characters/minotaur-strategist/animations/walk/west/7.png +0 -0
  657. package/assets/characters/minotaur-strategist/animations/working/0.png +0 -0
  658. package/assets/characters/minotaur-strategist/animations/working/1.png +0 -0
  659. package/assets/characters/minotaur-strategist/animations/working/10.png +0 -0
  660. package/assets/characters/minotaur-strategist/animations/working/11.png +0 -0
  661. package/assets/characters/minotaur-strategist/animations/working/12.png +0 -0
  662. package/assets/characters/minotaur-strategist/animations/working/13.png +0 -0
  663. package/assets/characters/minotaur-strategist/animations/working/14.png +0 -0
  664. package/assets/characters/minotaur-strategist/animations/working/15.png +0 -0
  665. package/assets/characters/minotaur-strategist/animations/working/2.png +0 -0
  666. package/assets/characters/minotaur-strategist/animations/working/3.png +0 -0
  667. package/assets/characters/minotaur-strategist/animations/working/4.png +0 -0
  668. package/assets/characters/minotaur-strategist/animations/working/5.png +0 -0
  669. package/assets/characters/minotaur-strategist/animations/working/6.png +0 -0
  670. package/assets/characters/minotaur-strategist/animations/working/7.png +0 -0
  671. package/assets/characters/minotaur-strategist/animations/working/8.png +0 -0
  672. package/assets/characters/minotaur-strategist/animations/working/9.png +0 -0
  673. package/assets/characters/minotaur-strategist/rotations/east.png +0 -0
  674. package/assets/characters/minotaur-strategist/rotations/north.png +0 -0
  675. package/assets/characters/minotaur-strategist/rotations/south.png +0 -0
  676. package/assets/characters/minotaur-strategist/rotations/west.png +0 -0
  677. package/assets/characters/minotaur-strategist/voice-lines/arrive.mp3 +0 -0
  678. package/assets/characters/minotaur-strategist/voice-lines/attention.mp3 +0 -0
  679. package/assets/characters/minotaur-strategist/voice-lines/complete.mp3 +0 -0
  680. package/assets/characters/minotaur-strategist/voice-lines/death.mp3 +0 -0
  681. package/assets/characters/minotaur-strategist/voice-lines/move_variant_1.mp3 +0 -0
  682. package/assets/characters/minotaur-strategist/voice-lines/move_variant_2.mp3 +0 -0
  683. package/assets/characters/minotaur-strategist/voice-lines/move_variant_3.mp3 +0 -0
  684. package/assets/characters/minotaur-strategist/voice-lines/selected_variant_1.mp3 +0 -0
  685. package/assets/characters/minotaur-strategist/voice-lines/selected_variant_2.mp3 +0 -0
  686. package/assets/characters/minotaur-strategist/voice-lines/selected_variant_3.mp3 +0 -0
  687. package/assets/characters/moon-grove-druid/animations/walk/east/0.png +0 -0
  688. package/assets/characters/moon-grove-druid/animations/walk/east/1.png +0 -0
  689. package/assets/characters/moon-grove-druid/animations/walk/east/2.png +0 -0
  690. package/assets/characters/moon-grove-druid/animations/walk/east/3.png +0 -0
  691. package/assets/characters/moon-grove-druid/animations/walk/east/4.png +0 -0
  692. package/assets/characters/moon-grove-druid/animations/walk/east/5.png +0 -0
  693. package/assets/characters/moon-grove-druid/animations/walk/east/6.png +0 -0
  694. package/assets/characters/moon-grove-druid/animations/walk/east/7.png +0 -0
  695. package/assets/characters/moon-grove-druid/animations/walk/north/0.png +0 -0
  696. package/assets/characters/moon-grove-druid/animations/walk/north/1.png +0 -0
  697. package/assets/characters/moon-grove-druid/animations/walk/north/2.png +0 -0
  698. package/assets/characters/moon-grove-druid/animations/walk/north/3.png +0 -0
  699. package/assets/characters/moon-grove-druid/animations/walk/north/4.png +0 -0
  700. package/assets/characters/moon-grove-druid/animations/walk/north/5.png +0 -0
  701. package/assets/characters/moon-grove-druid/animations/walk/north/6.png +0 -0
  702. package/assets/characters/moon-grove-druid/animations/walk/north/7.png +0 -0
  703. package/assets/characters/moon-grove-druid/animations/walk/south/0.png +0 -0
  704. package/assets/characters/moon-grove-druid/animations/walk/south/1.png +0 -0
  705. package/assets/characters/moon-grove-druid/animations/walk/south/2.png +0 -0
  706. package/assets/characters/moon-grove-druid/animations/walk/south/3.png +0 -0
  707. package/assets/characters/moon-grove-druid/animations/walk/south/4.png +0 -0
  708. package/assets/characters/moon-grove-druid/animations/walk/south/5.png +0 -0
  709. package/assets/characters/moon-grove-druid/animations/walk/south/6.png +0 -0
  710. package/assets/characters/moon-grove-druid/animations/walk/south/7.png +0 -0
  711. package/assets/characters/moon-grove-druid/animations/walk/west/0.png +0 -0
  712. package/assets/characters/moon-grove-druid/animations/walk/west/1.png +0 -0
  713. package/assets/characters/moon-grove-druid/animations/walk/west/2.png +0 -0
  714. package/assets/characters/moon-grove-druid/animations/walk/west/3.png +0 -0
  715. package/assets/characters/moon-grove-druid/animations/walk/west/4.png +0 -0
  716. package/assets/characters/moon-grove-druid/animations/walk/west/5.png +0 -0
  717. package/assets/characters/moon-grove-druid/animations/walk/west/6.png +0 -0
  718. package/assets/characters/moon-grove-druid/animations/walk/west/7.png +0 -0
  719. package/assets/characters/moon-grove-druid/animations/working/0.png +0 -0
  720. package/assets/characters/moon-grove-druid/animations/working/1.png +0 -0
  721. package/assets/characters/moon-grove-druid/animations/working/10.png +0 -0
  722. package/assets/characters/moon-grove-druid/animations/working/11.png +0 -0
  723. package/assets/characters/moon-grove-druid/animations/working/12.png +0 -0
  724. package/assets/characters/moon-grove-druid/animations/working/13.png +0 -0
  725. package/assets/characters/moon-grove-druid/animations/working/14.png +0 -0
  726. package/assets/characters/moon-grove-druid/animations/working/15.png +0 -0
  727. package/assets/characters/moon-grove-druid/animations/working/2.png +0 -0
  728. package/assets/characters/moon-grove-druid/animations/working/3.png +0 -0
  729. package/assets/characters/moon-grove-druid/animations/working/4.png +0 -0
  730. package/assets/characters/moon-grove-druid/animations/working/5.png +0 -0
  731. package/assets/characters/moon-grove-druid/animations/working/6.png +0 -0
  732. package/assets/characters/moon-grove-druid/animations/working/7.png +0 -0
  733. package/assets/characters/moon-grove-druid/animations/working/8.png +0 -0
  734. package/assets/characters/moon-grove-druid/animations/working/9.png +0 -0
  735. package/assets/characters/moon-grove-druid/rotations/east.png +0 -0
  736. package/assets/characters/moon-grove-druid/rotations/north.png +0 -0
  737. package/assets/characters/moon-grove-druid/rotations/south.png +0 -0
  738. package/assets/characters/moon-grove-druid/rotations/west.png +0 -0
  739. package/assets/characters/moon-grove-druid/voice-lines/arrive.mp3 +0 -0
  740. package/assets/characters/moon-grove-druid/voice-lines/attention.mp3 +0 -0
  741. package/assets/characters/moon-grove-druid/voice-lines/complete.mp3 +0 -0
  742. package/assets/characters/moon-grove-druid/voice-lines/death.mp3 +0 -0
  743. package/assets/characters/moon-grove-druid/voice-lines/move_variant_1.mp3 +0 -0
  744. package/assets/characters/moon-grove-druid/voice-lines/move_variant_2.mp3 +0 -0
  745. package/assets/characters/moon-grove-druid/voice-lines/move_variant_3.mp3 +0 -0
  746. package/assets/characters/moon-grove-druid/voice-lines/selected_variant_1.mp3 +0 -0
  747. package/assets/characters/moon-grove-druid/voice-lines/selected_variant_2.mp3 +0 -0
  748. package/assets/characters/moon-grove-druid/voice-lines/selected_variant_3.mp3 +0 -0
  749. package/assets/characters/necromancer-male/animations/walk/east/0.png +0 -0
  750. package/assets/characters/necromancer-male/animations/walk/east/1.png +0 -0
  751. package/assets/characters/necromancer-male/animations/walk/east/2.png +0 -0
  752. package/assets/characters/necromancer-male/animations/walk/east/3.png +0 -0
  753. package/assets/characters/necromancer-male/animations/walk/east/4.png +0 -0
  754. package/assets/characters/necromancer-male/animations/walk/east/5.png +0 -0
  755. package/assets/characters/necromancer-male/animations/walk/east/6.png +0 -0
  756. package/assets/characters/necromancer-male/animations/walk/east/7.png +0 -0
  757. package/assets/characters/necromancer-male/animations/walk/north/0.png +0 -0
  758. package/assets/characters/necromancer-male/animations/walk/north/1.png +0 -0
  759. package/assets/characters/necromancer-male/animations/walk/north/2.png +0 -0
  760. package/assets/characters/necromancer-male/animations/walk/north/3.png +0 -0
  761. package/assets/characters/necromancer-male/animations/walk/north/4.png +0 -0
  762. package/assets/characters/necromancer-male/animations/walk/north/5.png +0 -0
  763. package/assets/characters/necromancer-male/animations/walk/north/6.png +0 -0
  764. package/assets/characters/necromancer-male/animations/walk/north/7.png +0 -0
  765. package/assets/characters/necromancer-male/animations/walk/south/0.png +0 -0
  766. package/assets/characters/necromancer-male/animations/walk/south/1.png +0 -0
  767. package/assets/characters/necromancer-male/animations/walk/south/2.png +0 -0
  768. package/assets/characters/necromancer-male/animations/walk/south/3.png +0 -0
  769. package/assets/characters/necromancer-male/animations/walk/south/4.png +0 -0
  770. package/assets/characters/necromancer-male/animations/walk/south/5.png +0 -0
  771. package/assets/characters/necromancer-male/animations/walk/south/6.png +0 -0
  772. package/assets/characters/necromancer-male/animations/walk/south/7.png +0 -0
  773. package/assets/characters/necromancer-male/animations/walk/west/0.png +0 -0
  774. package/assets/characters/necromancer-male/animations/walk/west/1.png +0 -0
  775. package/assets/characters/necromancer-male/animations/walk/west/2.png +0 -0
  776. package/assets/characters/necromancer-male/animations/walk/west/3.png +0 -0
  777. package/assets/characters/necromancer-male/animations/walk/west/4.png +0 -0
  778. package/assets/characters/necromancer-male/animations/walk/west/5.png +0 -0
  779. package/assets/characters/necromancer-male/animations/walk/west/6.png +0 -0
  780. package/assets/characters/necromancer-male/animations/walk/west/7.png +0 -0
  781. package/assets/characters/necromancer-male/animations/working/0.png +0 -0
  782. package/assets/characters/necromancer-male/animations/working/1.png +0 -0
  783. package/assets/characters/necromancer-male/animations/working/10.png +0 -0
  784. package/assets/characters/necromancer-male/animations/working/11.png +0 -0
  785. package/assets/characters/necromancer-male/animations/working/12.png +0 -0
  786. package/assets/characters/necromancer-male/animations/working/13.png +0 -0
  787. package/assets/characters/necromancer-male/animations/working/14.png +0 -0
  788. package/assets/characters/necromancer-male/animations/working/15.png +0 -0
  789. package/assets/characters/necromancer-male/animations/working/2.png +0 -0
  790. package/assets/characters/necromancer-male/animations/working/3.png +0 -0
  791. package/assets/characters/necromancer-male/animations/working/4.png +0 -0
  792. package/assets/characters/necromancer-male/animations/working/5.png +0 -0
  793. package/assets/characters/necromancer-male/animations/working/6.png +0 -0
  794. package/assets/characters/necromancer-male/animations/working/7.png +0 -0
  795. package/assets/characters/necromancer-male/animations/working/8.png +0 -0
  796. package/assets/characters/necromancer-male/animations/working/9.png +0 -0
  797. package/assets/characters/necromancer-male/rotations/east.png +0 -0
  798. package/assets/characters/necromancer-male/rotations/north.png +0 -0
  799. package/assets/characters/necromancer-male/rotations/south.png +0 -0
  800. package/assets/characters/necromancer-male/rotations/west.png +0 -0
  801. package/assets/characters/necromancer-male/voice-lines/arrive.mp3 +0 -0
  802. package/assets/characters/necromancer-male/voice-lines/attention.mp3 +0 -0
  803. package/assets/characters/necromancer-male/voice-lines/complete.mp3 +0 -0
  804. package/assets/characters/necromancer-male/voice-lines/death.mp3 +0 -0
  805. package/assets/characters/necromancer-male/voice-lines/move_variant_1.mp3 +0 -0
  806. package/assets/characters/necromancer-male/voice-lines/move_variant_2.mp3 +0 -0
  807. package/assets/characters/necromancer-male/voice-lines/move_variant_3.mp3 +0 -0
  808. package/assets/characters/necromancer-male/voice-lines/selected_variant_1.mp3 +0 -0
  809. package/assets/characters/necromancer-male/voice-lines/selected_variant_2.mp3 +0 -0
  810. package/assets/characters/necromancer-male/voice-lines/selected_variant_3.mp3 +0 -0
  811. package/assets/characters/oracle-seer/animations/walk/east/0.png +0 -0
  812. package/assets/characters/oracle-seer/animations/walk/east/1.png +0 -0
  813. package/assets/characters/oracle-seer/animations/walk/east/2.png +0 -0
  814. package/assets/characters/oracle-seer/animations/walk/east/3.png +0 -0
  815. package/assets/characters/oracle-seer/animations/walk/east/4.png +0 -0
  816. package/assets/characters/oracle-seer/animations/walk/east/5.png +0 -0
  817. package/assets/characters/oracle-seer/animations/walk/east/6.png +0 -0
  818. package/assets/characters/oracle-seer/animations/walk/east/7.png +0 -0
  819. package/assets/characters/oracle-seer/animations/walk/north/0.png +0 -0
  820. package/assets/characters/oracle-seer/animations/walk/north/1.png +0 -0
  821. package/assets/characters/oracle-seer/animations/walk/north/2.png +0 -0
  822. package/assets/characters/oracle-seer/animations/walk/north/3.png +0 -0
  823. package/assets/characters/oracle-seer/animations/walk/north/4.png +0 -0
  824. package/assets/characters/oracle-seer/animations/walk/north/5.png +0 -0
  825. package/assets/characters/oracle-seer/animations/walk/north/6.png +0 -0
  826. package/assets/characters/oracle-seer/animations/walk/north/7.png +0 -0
  827. package/assets/characters/oracle-seer/animations/walk/south/0.png +0 -0
  828. package/assets/characters/oracle-seer/animations/walk/south/1.png +0 -0
  829. package/assets/characters/oracle-seer/animations/walk/south/2.png +0 -0
  830. package/assets/characters/oracle-seer/animations/walk/south/3.png +0 -0
  831. package/assets/characters/oracle-seer/animations/walk/south/4.png +0 -0
  832. package/assets/characters/oracle-seer/animations/walk/south/5.png +0 -0
  833. package/assets/characters/oracle-seer/animations/walk/south/6.png +0 -0
  834. package/assets/characters/oracle-seer/animations/walk/south/7.png +0 -0
  835. package/assets/characters/oracle-seer/animations/walk/west/0.png +0 -0
  836. package/assets/characters/oracle-seer/animations/walk/west/1.png +0 -0
  837. package/assets/characters/oracle-seer/animations/walk/west/2.png +0 -0
  838. package/assets/characters/oracle-seer/animations/walk/west/3.png +0 -0
  839. package/assets/characters/oracle-seer/animations/walk/west/4.png +0 -0
  840. package/assets/characters/oracle-seer/animations/walk/west/5.png +0 -0
  841. package/assets/characters/oracle-seer/animations/walk/west/6.png +0 -0
  842. package/assets/characters/oracle-seer/animations/walk/west/7.png +0 -0
  843. package/assets/characters/oracle-seer/animations/working/0.png +0 -0
  844. package/assets/characters/oracle-seer/animations/working/1.png +0 -0
  845. package/assets/characters/oracle-seer/animations/working/10.png +0 -0
  846. package/assets/characters/oracle-seer/animations/working/11.png +0 -0
  847. package/assets/characters/oracle-seer/animations/working/12.png +0 -0
  848. package/assets/characters/oracle-seer/animations/working/13.png +0 -0
  849. package/assets/characters/oracle-seer/animations/working/14.png +0 -0
  850. package/assets/characters/oracle-seer/animations/working/15.png +0 -0
  851. package/assets/characters/oracle-seer/animations/working/2.png +0 -0
  852. package/assets/characters/oracle-seer/animations/working/3.png +0 -0
  853. package/assets/characters/oracle-seer/animations/working/4.png +0 -0
  854. package/assets/characters/oracle-seer/animations/working/5.png +0 -0
  855. package/assets/characters/oracle-seer/animations/working/6.png +0 -0
  856. package/assets/characters/oracle-seer/animations/working/7.png +0 -0
  857. package/assets/characters/oracle-seer/animations/working/8.png +0 -0
  858. package/assets/characters/oracle-seer/animations/working/9.png +0 -0
  859. package/assets/characters/oracle-seer/rotations/east.png +0 -0
  860. package/assets/characters/oracle-seer/rotations/north.png +0 -0
  861. package/assets/characters/oracle-seer/rotations/south.png +0 -0
  862. package/assets/characters/oracle-seer/rotations/west.png +0 -0
  863. package/assets/characters/oracle-seer/voice-lines/arrive.mp3 +0 -0
  864. package/assets/characters/oracle-seer/voice-lines/attention.mp3 +0 -0
  865. package/assets/characters/oracle-seer/voice-lines/complete.mp3 +0 -0
  866. package/assets/characters/oracle-seer/voice-lines/death.mp3 +0 -0
  867. package/assets/characters/oracle-seer/voice-lines/move_variant_1.mp3 +0 -0
  868. package/assets/characters/oracle-seer/voice-lines/move_variant_2.mp3 +0 -0
  869. package/assets/characters/oracle-seer/voice-lines/move_variant_3.mp3 +0 -0
  870. package/assets/characters/oracle-seer/voice-lines/selected_variant_1.mp3 +0 -0
  871. package/assets/characters/oracle-seer/voice-lines/selected_variant_2.mp3 +0 -0
  872. package/assets/characters/oracle-seer/voice-lines/selected_variant_3.mp3 +0 -0
  873. package/assets/characters/pyromancer-wizard/animations/walk/east/0.png +0 -0
  874. package/assets/characters/pyromancer-wizard/animations/walk/east/1.png +0 -0
  875. package/assets/characters/pyromancer-wizard/animations/walk/east/2.png +0 -0
  876. package/assets/characters/pyromancer-wizard/animations/walk/east/3.png +0 -0
  877. package/assets/characters/pyromancer-wizard/animations/walk/east/4.png +0 -0
  878. package/assets/characters/pyromancer-wizard/animations/walk/east/5.png +0 -0
  879. package/assets/characters/pyromancer-wizard/animations/walk/east/6.png +0 -0
  880. package/assets/characters/pyromancer-wizard/animations/walk/east/7.png +0 -0
  881. package/assets/characters/pyromancer-wizard/animations/walk/north/0.png +0 -0
  882. package/assets/characters/pyromancer-wizard/animations/walk/north/1.png +0 -0
  883. package/assets/characters/pyromancer-wizard/animations/walk/north/2.png +0 -0
  884. package/assets/characters/pyromancer-wizard/animations/walk/north/3.png +0 -0
  885. package/assets/characters/pyromancer-wizard/animations/walk/north/4.png +0 -0
  886. package/assets/characters/pyromancer-wizard/animations/walk/north/5.png +0 -0
  887. package/assets/characters/pyromancer-wizard/animations/walk/north/6.png +0 -0
  888. package/assets/characters/pyromancer-wizard/animations/walk/north/7.png +0 -0
  889. package/assets/characters/pyromancer-wizard/animations/walk/south/0.png +0 -0
  890. package/assets/characters/pyromancer-wizard/animations/walk/south/1.png +0 -0
  891. package/assets/characters/pyromancer-wizard/animations/walk/south/2.png +0 -0
  892. package/assets/characters/pyromancer-wizard/animations/walk/south/3.png +0 -0
  893. package/assets/characters/pyromancer-wizard/animations/walk/south/4.png +0 -0
  894. package/assets/characters/pyromancer-wizard/animations/walk/south/5.png +0 -0
  895. package/assets/characters/pyromancer-wizard/animations/walk/south/6.png +0 -0
  896. package/assets/characters/pyromancer-wizard/animations/walk/south/7.png +0 -0
  897. package/assets/characters/pyromancer-wizard/animations/walk/west/0.png +0 -0
  898. package/assets/characters/pyromancer-wizard/animations/walk/west/1.png +0 -0
  899. package/assets/characters/pyromancer-wizard/animations/walk/west/2.png +0 -0
  900. package/assets/characters/pyromancer-wizard/animations/walk/west/3.png +0 -0
  901. package/assets/characters/pyromancer-wizard/animations/walk/west/4.png +0 -0
  902. package/assets/characters/pyromancer-wizard/animations/walk/west/5.png +0 -0
  903. package/assets/characters/pyromancer-wizard/animations/walk/west/6.png +0 -0
  904. package/assets/characters/pyromancer-wizard/animations/walk/west/7.png +0 -0
  905. package/assets/characters/pyromancer-wizard/animations/working/0.png +0 -0
  906. package/assets/characters/pyromancer-wizard/animations/working/1.png +0 -0
  907. package/assets/characters/pyromancer-wizard/animations/working/10.png +0 -0
  908. package/assets/characters/pyromancer-wizard/animations/working/11.png +0 -0
  909. package/assets/characters/pyromancer-wizard/animations/working/12.png +0 -0
  910. package/assets/characters/pyromancer-wizard/animations/working/13.png +0 -0
  911. package/assets/characters/pyromancer-wizard/animations/working/14.png +0 -0
  912. package/assets/characters/pyromancer-wizard/animations/working/15.png +0 -0
  913. package/assets/characters/pyromancer-wizard/animations/working/2.png +0 -0
  914. package/assets/characters/pyromancer-wizard/animations/working/3.png +0 -0
  915. package/assets/characters/pyromancer-wizard/animations/working/4.png +0 -0
  916. package/assets/characters/pyromancer-wizard/animations/working/5.png +0 -0
  917. package/assets/characters/pyromancer-wizard/animations/working/6.png +0 -0
  918. package/assets/characters/pyromancer-wizard/animations/working/7.png +0 -0
  919. package/assets/characters/pyromancer-wizard/animations/working/8.png +0 -0
  920. package/assets/characters/pyromancer-wizard/animations/working/9.png +0 -0
  921. package/assets/characters/pyromancer-wizard/rotations/east.png +0 -0
  922. package/assets/characters/pyromancer-wizard/rotations/north.png +0 -0
  923. package/assets/characters/pyromancer-wizard/rotations/south.png +0 -0
  924. package/assets/characters/pyromancer-wizard/rotations/west.png +0 -0
  925. package/assets/characters/pyromancer-wizard/voice-lines/arrive.mp3 +0 -0
  926. package/assets/characters/pyromancer-wizard/voice-lines/attention.mp3 +0 -0
  927. package/assets/characters/pyromancer-wizard/voice-lines/complete.mp3 +0 -0
  928. package/assets/characters/pyromancer-wizard/voice-lines/death.mp3 +0 -0
  929. package/assets/characters/pyromancer-wizard/voice-lines/move_variant_1.mp3 +0 -0
  930. package/assets/characters/pyromancer-wizard/voice-lines/move_variant_2.mp3 +0 -0
  931. package/assets/characters/pyromancer-wizard/voice-lines/move_variant_3.mp3 +0 -0
  932. package/assets/characters/pyromancer-wizard/voice-lines/selected_variant_1.mp3 +0 -0
  933. package/assets/characters/pyromancer-wizard/voice-lines/selected_variant_2.mp3 +0 -0
  934. package/assets/characters/pyromancer-wizard/voice-lines/selected_variant_3.mp3 +0 -0
  935. package/assets/characters/sun-priestess-healer/animations/walk/east/0.png +0 -0
  936. package/assets/characters/sun-priestess-healer/animations/walk/east/1.png +0 -0
  937. package/assets/characters/sun-priestess-healer/animations/walk/east/2.png +0 -0
  938. package/assets/characters/sun-priestess-healer/animations/walk/east/3.png +0 -0
  939. package/assets/characters/sun-priestess-healer/animations/walk/east/4.png +0 -0
  940. package/assets/characters/sun-priestess-healer/animations/walk/east/5.png +0 -0
  941. package/assets/characters/sun-priestess-healer/animations/walk/east/6.png +0 -0
  942. package/assets/characters/sun-priestess-healer/animations/walk/east/7.png +0 -0
  943. package/assets/characters/sun-priestess-healer/animations/walk/north/0.png +0 -0
  944. package/assets/characters/sun-priestess-healer/animations/walk/north/1.png +0 -0
  945. package/assets/characters/sun-priestess-healer/animations/walk/north/2.png +0 -0
  946. package/assets/characters/sun-priestess-healer/animations/walk/north/3.png +0 -0
  947. package/assets/characters/sun-priestess-healer/animations/walk/north/4.png +0 -0
  948. package/assets/characters/sun-priestess-healer/animations/walk/north/5.png +0 -0
  949. package/assets/characters/sun-priestess-healer/animations/walk/north/6.png +0 -0
  950. package/assets/characters/sun-priestess-healer/animations/walk/north/7.png +0 -0
  951. package/assets/characters/sun-priestess-healer/animations/walk/south/0.png +0 -0
  952. package/assets/characters/sun-priestess-healer/animations/walk/south/1.png +0 -0
  953. package/assets/characters/sun-priestess-healer/animations/walk/south/2.png +0 -0
  954. package/assets/characters/sun-priestess-healer/animations/walk/south/3.png +0 -0
  955. package/assets/characters/sun-priestess-healer/animations/walk/south/4.png +0 -0
  956. package/assets/characters/sun-priestess-healer/animations/walk/south/5.png +0 -0
  957. package/assets/characters/sun-priestess-healer/animations/walk/south/6.png +0 -0
  958. package/assets/characters/sun-priestess-healer/animations/walk/south/7.png +0 -0
  959. package/assets/characters/sun-priestess-healer/animations/walk/west/0.png +0 -0
  960. package/assets/characters/sun-priestess-healer/animations/walk/west/1.png +0 -0
  961. package/assets/characters/sun-priestess-healer/animations/walk/west/2.png +0 -0
  962. package/assets/characters/sun-priestess-healer/animations/walk/west/3.png +0 -0
  963. package/assets/characters/sun-priestess-healer/animations/walk/west/4.png +0 -0
  964. package/assets/characters/sun-priestess-healer/animations/walk/west/5.png +0 -0
  965. package/assets/characters/sun-priestess-healer/animations/walk/west/6.png +0 -0
  966. package/assets/characters/sun-priestess-healer/animations/walk/west/7.png +0 -0
  967. package/assets/characters/sun-priestess-healer/animations/working/0.png +0 -0
  968. package/assets/characters/sun-priestess-healer/animations/working/1.png +0 -0
  969. package/assets/characters/sun-priestess-healer/animations/working/10.png +0 -0
  970. package/assets/characters/sun-priestess-healer/animations/working/11.png +0 -0
  971. package/assets/characters/sun-priestess-healer/animations/working/12.png +0 -0
  972. package/assets/characters/sun-priestess-healer/animations/working/13.png +0 -0
  973. package/assets/characters/sun-priestess-healer/animations/working/14.png +0 -0
  974. package/assets/characters/sun-priestess-healer/animations/working/15.png +0 -0
  975. package/assets/characters/sun-priestess-healer/animations/working/2.png +0 -0
  976. package/assets/characters/sun-priestess-healer/animations/working/3.png +0 -0
  977. package/assets/characters/sun-priestess-healer/animations/working/4.png +0 -0
  978. package/assets/characters/sun-priestess-healer/animations/working/5.png +0 -0
  979. package/assets/characters/sun-priestess-healer/animations/working/6.png +0 -0
  980. package/assets/characters/sun-priestess-healer/animations/working/7.png +0 -0
  981. package/assets/characters/sun-priestess-healer/animations/working/8.png +0 -0
  982. package/assets/characters/sun-priestess-healer/animations/working/9.png +0 -0
  983. package/assets/characters/sun-priestess-healer/rotations/east.png +0 -0
  984. package/assets/characters/sun-priestess-healer/rotations/north.png +0 -0
  985. package/assets/characters/sun-priestess-healer/rotations/south.png +0 -0
  986. package/assets/characters/sun-priestess-healer/rotations/west.png +0 -0
  987. package/assets/characters/sun-priestess-healer/voice-lines/arrive.mp3 +0 -0
  988. package/assets/characters/sun-priestess-healer/voice-lines/attention.mp3 +0 -0
  989. package/assets/characters/sun-priestess-healer/voice-lines/complete.mp3 +0 -0
  990. package/assets/characters/sun-priestess-healer/voice-lines/death.mp3 +0 -0
  991. package/assets/characters/sun-priestess-healer/voice-lines/move_variant_1.mp3 +0 -0
  992. package/assets/characters/sun-priestess-healer/voice-lines/move_variant_2.mp3 +0 -0
  993. package/assets/characters/sun-priestess-healer/voice-lines/move_variant_3.mp3 +0 -0
  994. package/assets/characters/sun-priestess-healer/voice-lines/selected_variant_1.mp3 +0 -0
  995. package/assets/characters/sun-priestess-healer/voice-lines/selected_variant_2.mp3 +0 -0
  996. package/assets/characters/sun-priestess-healer/voice-lines/selected_variant_3.mp3 +0 -0
  997. package/assets/characters/valkyrie-spearmaid/animations/walk/east/0.png +0 -0
  998. package/assets/characters/valkyrie-spearmaid/animations/walk/east/1.png +0 -0
  999. package/assets/characters/valkyrie-spearmaid/animations/walk/east/2.png +0 -0
  1000. package/assets/characters/valkyrie-spearmaid/animations/walk/east/3.png +0 -0
  1001. package/assets/characters/valkyrie-spearmaid/animations/walk/east/4.png +0 -0
  1002. package/assets/characters/valkyrie-spearmaid/animations/walk/east/5.png +0 -0
  1003. package/assets/characters/valkyrie-spearmaid/animations/walk/east/6.png +0 -0
  1004. package/assets/characters/valkyrie-spearmaid/animations/walk/east/7.png +0 -0
  1005. package/assets/characters/valkyrie-spearmaid/animations/walk/north/0.png +0 -0
  1006. package/assets/characters/valkyrie-spearmaid/animations/walk/north/1.png +0 -0
  1007. package/assets/characters/valkyrie-spearmaid/animations/walk/north/2.png +0 -0
  1008. package/assets/characters/valkyrie-spearmaid/animations/walk/north/3.png +0 -0
  1009. package/assets/characters/valkyrie-spearmaid/animations/walk/north/4.png +0 -0
  1010. package/assets/characters/valkyrie-spearmaid/animations/walk/north/5.png +0 -0
  1011. package/assets/characters/valkyrie-spearmaid/animations/walk/north/6.png +0 -0
  1012. package/assets/characters/valkyrie-spearmaid/animations/walk/north/7.png +0 -0
  1013. package/assets/characters/valkyrie-spearmaid/animations/walk/south/0.png +0 -0
  1014. package/assets/characters/valkyrie-spearmaid/animations/walk/south/1.png +0 -0
  1015. package/assets/characters/valkyrie-spearmaid/animations/walk/south/2.png +0 -0
  1016. package/assets/characters/valkyrie-spearmaid/animations/walk/south/3.png +0 -0
  1017. package/assets/characters/valkyrie-spearmaid/animations/walk/south/4.png +0 -0
  1018. package/assets/characters/valkyrie-spearmaid/animations/walk/south/5.png +0 -0
  1019. package/assets/characters/valkyrie-spearmaid/animations/walk/south/6.png +0 -0
  1020. package/assets/characters/valkyrie-spearmaid/animations/walk/south/7.png +0 -0
  1021. package/assets/characters/valkyrie-spearmaid/animations/walk/west/0.png +0 -0
  1022. package/assets/characters/valkyrie-spearmaid/animations/walk/west/1.png +0 -0
  1023. package/assets/characters/valkyrie-spearmaid/animations/walk/west/2.png +0 -0
  1024. package/assets/characters/valkyrie-spearmaid/animations/walk/west/3.png +0 -0
  1025. package/assets/characters/valkyrie-spearmaid/animations/walk/west/4.png +0 -0
  1026. package/assets/characters/valkyrie-spearmaid/animations/walk/west/5.png +0 -0
  1027. package/assets/characters/valkyrie-spearmaid/animations/walk/west/6.png +0 -0
  1028. package/assets/characters/valkyrie-spearmaid/animations/walk/west/7.png +0 -0
  1029. package/assets/characters/valkyrie-spearmaid/animations/working/0.png +0 -0
  1030. package/assets/characters/valkyrie-spearmaid/animations/working/1.png +0 -0
  1031. package/assets/characters/valkyrie-spearmaid/animations/working/10.png +0 -0
  1032. package/assets/characters/valkyrie-spearmaid/animations/working/11.png +0 -0
  1033. package/assets/characters/valkyrie-spearmaid/animations/working/12.png +0 -0
  1034. package/assets/characters/valkyrie-spearmaid/animations/working/13.png +0 -0
  1035. package/assets/characters/valkyrie-spearmaid/animations/working/14.png +0 -0
  1036. package/assets/characters/valkyrie-spearmaid/animations/working/15.png +0 -0
  1037. package/assets/characters/valkyrie-spearmaid/animations/working/2.png +0 -0
  1038. package/assets/characters/valkyrie-spearmaid/animations/working/3.png +0 -0
  1039. package/assets/characters/valkyrie-spearmaid/animations/working/4.png +0 -0
  1040. package/assets/characters/valkyrie-spearmaid/animations/working/5.png +0 -0
  1041. package/assets/characters/valkyrie-spearmaid/animations/working/6.png +0 -0
  1042. package/assets/characters/valkyrie-spearmaid/animations/working/7.png +0 -0
  1043. package/assets/characters/valkyrie-spearmaid/animations/working/8.png +0 -0
  1044. package/assets/characters/valkyrie-spearmaid/animations/working/9.png +0 -0
  1045. package/assets/characters/valkyrie-spearmaid/rotations/east.png +0 -0
  1046. package/assets/characters/valkyrie-spearmaid/rotations/north.png +0 -0
  1047. package/assets/characters/valkyrie-spearmaid/rotations/south.png +0 -0
  1048. package/assets/characters/valkyrie-spearmaid/rotations/west.png +0 -0
  1049. package/assets/characters/valkyrie-spearmaid/voice-lines/arrive.mp3 +0 -0
  1050. package/assets/characters/valkyrie-spearmaid/voice-lines/attention.mp3 +0 -0
  1051. package/assets/characters/valkyrie-spearmaid/voice-lines/complete.mp3 +0 -0
  1052. package/assets/characters/valkyrie-spearmaid/voice-lines/death.mp3 +0 -0
  1053. package/assets/characters/valkyrie-spearmaid/voice-lines/move_variant_1.mp3 +0 -0
  1054. package/assets/characters/valkyrie-spearmaid/voice-lines/move_variant_2.mp3 +0 -0
  1055. package/assets/characters/valkyrie-spearmaid/voice-lines/move_variant_3.mp3 +0 -0
  1056. package/assets/characters/valkyrie-spearmaid/voice-lines/selected_variant_1.mp3 +0 -0
  1057. package/assets/characters/valkyrie-spearmaid/voice-lines/selected_variant_2.mp3 +0 -0
  1058. package/assets/characters/valkyrie-spearmaid/voice-lines/selected_variant_3.mp3 +0 -0
  1059. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/0.png +0 -0
  1060. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/1.png +0 -0
  1061. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/2.png +0 -0
  1062. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/3.png +0 -0
  1063. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/4.png +0 -0
  1064. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/5.png +0 -0
  1065. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/6.png +0 -0
  1066. package/assets/characters/witch-hunter-gunslinger/animations/walk/east/7.png +0 -0
  1067. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/0.png +0 -0
  1068. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/1.png +0 -0
  1069. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/2.png +0 -0
  1070. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/3.png +0 -0
  1071. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/4.png +0 -0
  1072. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/5.png +0 -0
  1073. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/6.png +0 -0
  1074. package/assets/characters/witch-hunter-gunslinger/animations/walk/north/7.png +0 -0
  1075. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/0.png +0 -0
  1076. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/1.png +0 -0
  1077. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/2.png +0 -0
  1078. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/3.png +0 -0
  1079. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/4.png +0 -0
  1080. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/5.png +0 -0
  1081. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/6.png +0 -0
  1082. package/assets/characters/witch-hunter-gunslinger/animations/walk/south/7.png +0 -0
  1083. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/0.png +0 -0
  1084. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/1.png +0 -0
  1085. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/2.png +0 -0
  1086. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/3.png +0 -0
  1087. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/4.png +0 -0
  1088. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/5.png +0 -0
  1089. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/6.png +0 -0
  1090. package/assets/characters/witch-hunter-gunslinger/animations/walk/west/7.png +0 -0
  1091. package/assets/characters/witch-hunter-gunslinger/animations/working/0.png +0 -0
  1092. package/assets/characters/witch-hunter-gunslinger/animations/working/1.png +0 -0
  1093. package/assets/characters/witch-hunter-gunslinger/animations/working/10.png +0 -0
  1094. package/assets/characters/witch-hunter-gunslinger/animations/working/11.png +0 -0
  1095. package/assets/characters/witch-hunter-gunslinger/animations/working/12.png +0 -0
  1096. package/assets/characters/witch-hunter-gunslinger/animations/working/13.png +0 -0
  1097. package/assets/characters/witch-hunter-gunslinger/animations/working/14.png +0 -0
  1098. package/assets/characters/witch-hunter-gunslinger/animations/working/15.png +0 -0
  1099. package/assets/characters/witch-hunter-gunslinger/animations/working/2.png +0 -0
  1100. package/assets/characters/witch-hunter-gunslinger/animations/working/3.png +0 -0
  1101. package/assets/characters/witch-hunter-gunslinger/animations/working/4.png +0 -0
  1102. package/assets/characters/witch-hunter-gunslinger/animations/working/5.png +0 -0
  1103. package/assets/characters/witch-hunter-gunslinger/animations/working/6.png +0 -0
  1104. package/assets/characters/witch-hunter-gunslinger/animations/working/7.png +0 -0
  1105. package/assets/characters/witch-hunter-gunslinger/animations/working/8.png +0 -0
  1106. package/assets/characters/witch-hunter-gunslinger/animations/working/9.png +0 -0
  1107. package/assets/characters/witch-hunter-gunslinger/rotations/east.png +0 -0
  1108. package/assets/characters/witch-hunter-gunslinger/rotations/north.png +0 -0
  1109. package/assets/characters/witch-hunter-gunslinger/rotations/south.png +0 -0
  1110. package/assets/characters/witch-hunter-gunslinger/rotations/west.png +0 -0
  1111. package/assets/characters/witch-hunter-gunslinger/voice-lines/arrive.mp3 +0 -0
  1112. package/assets/characters/witch-hunter-gunslinger/voice-lines/attention.mp3 +0 -0
  1113. package/assets/characters/witch-hunter-gunslinger/voice-lines/complete.mp3 +0 -0
  1114. package/assets/characters/witch-hunter-gunslinger/voice-lines/death.mp3 +0 -0
  1115. package/assets/characters/witch-hunter-gunslinger/voice-lines/move_variant_1.mp3 +0 -0
  1116. package/assets/characters/witch-hunter-gunslinger/voice-lines/move_variant_2.mp3 +0 -0
  1117. package/assets/characters/witch-hunter-gunslinger/voice-lines/move_variant_3.mp3 +0 -0
  1118. package/assets/characters/witch-hunter-gunslinger/voice-lines/selected_variant_1.mp3 +0 -0
  1119. package/assets/characters/witch-hunter-gunslinger/voice-lines/selected_variant_2.mp3 +0 -0
  1120. package/assets/characters/witch-hunter-gunslinger/voice-lines/selected_variant_3.mp3 +0 -0
  1121. package/assets/maps/backgrounds/outpost-v1-2x.png +0 -0
  1122. package/assets/maps/backgrounds/outpost-v1-original.png +0 -0
  1123. package/assets/maps/backgrounds/outpost-v1-upscaled-2x-photo.png +0 -0
  1124. package/assets/maps/backgrounds/outpost-v1.png +0 -0
  1125. package/assets/maps/outpost.json +130 -0
  1126. package/assets/maps/outpost.logic.json +26197 -0
  1127. package/config.example.yaml +91 -0
  1128. package/dist/client/assets/index-B0hs4377.js +80 -0
  1129. package/dist/client/assets/index-TxsheMVB.css +32 -0
  1130. package/dist/client/index.html +13 -0
  1131. package/dist/client/map-mask-editor.html +1853 -0
  1132. package/dist/server/server/assets/avatarCatalog.js +37 -0
  1133. package/dist/server/server/assets/voiceLineCatalog.js +42 -0
  1134. package/dist/server/server/bootstrap/httpApp.js +39 -0
  1135. package/dist/server/server/bootstrap/serverContext.js +60 -0
  1136. package/dist/server/server/bootstrap/shutdown.js +152 -0
  1137. package/dist/server/server/bootstrap/shutdown.test.js +142 -0
  1138. package/dist/server/server/bootstrap/websocketUpgrade.js +111 -0
  1139. package/dist/server/server/bootstrap/websocketUpgrade.test.js +83 -0
  1140. package/dist/server/server/bootstrapApp.js +89 -0
  1141. package/dist/server/server/cli.js +452 -0
  1142. package/dist/server/server/config/discovery.js +159 -0
  1143. package/dist/server/server/config/loadConfig.js +121 -0
  1144. package/dist/server/server/config/schema.js +121 -0
  1145. package/dist/server/server/http/appError.js +30 -0
  1146. package/dist/server/server/http/errorResponse.js +31 -0
  1147. package/dist/server/server/http/errorResponse.test.js +46 -0
  1148. package/dist/server/server/http/requestParsers.js +133 -0
  1149. package/dist/server/server/http/requestParsers.test.js +57 -0
  1150. package/dist/server/server/http/routes/registerApiRoutes.js +146 -0
  1151. package/dist/server/server/index.js +9 -0
  1152. package/dist/server/server/orchestrator/orchestratorService.js +378 -0
  1153. package/dist/server/server/orchestrator/orchestratorService.test.js +111 -0
  1154. package/dist/server/server/orchestrator/reconcile/isSameWorkerRecord.js +17 -0
  1155. package/dist/server/server/orchestrator/spawn/avatarAllocator.js +20 -0
  1156. package/dist/server/server/orchestrator/spawn/command.js +37 -0
  1157. package/dist/server/server/orchestrator/spawn/command.test.js +39 -0
  1158. package/dist/server/server/orchestrator/spawn/resolveSpawnPlan.js +44 -0
  1159. package/dist/server/server/orchestrator/spawn/resolveSpawnPlan.test.js +79 -0
  1160. package/dist/server/server/orchestrator/spawn/spawnPosition.js +147 -0
  1161. package/dist/server/server/orchestrator/spawn/types.js +2 -0
  1162. package/dist/server/server/orchestrator/spawn/windowName.js +12 -0
  1163. package/dist/server/server/persistence/workerRepository.js +211 -0
  1164. package/dist/server/server/status/activityParser.js +172 -0
  1165. package/dist/server/server/status/claudeTranscript/accumulator.js +372 -0
  1166. package/dist/server/server/status/claudeTranscript/constants.js +18 -0
  1167. package/dist/server/server/status/claudeTranscript/io.js +160 -0
  1168. package/dist/server/server/status/claudeTranscript/parser.js +50 -0
  1169. package/dist/server/server/status/claudeTranscript/snapshot.js +82 -0
  1170. package/dist/server/server/status/claudeTranscript/types.js +2 -0
  1171. package/dist/server/server/status/claudeTranscriptTracker.js +59 -0
  1172. package/dist/server/server/status/engine/signalContext.js +41 -0
  1173. package/dist/server/server/status/engine/stateMachine/activity.js +18 -0
  1174. package/dist/server/server/status/engine/stateMachine/constants.js +45 -0
  1175. package/dist/server/server/status/engine/stateMachine/decision.js +213 -0
  1176. package/dist/server/server/status/engine/stateMachine/decision.test.js +139 -0
  1177. package/dist/server/server/status/engine/stateMachine/helpers.js +103 -0
  1178. package/dist/server/server/status/engine/stateMachine/helpers.test.js +113 -0
  1179. package/dist/server/server/status/engine/stateMachine/idleBlockers.js +55 -0
  1180. package/dist/server/server/status/engine/stateMachine/parserErrorRules.js +42 -0
  1181. package/dist/server/server/status/engine/stateMachine/types.js +2 -0
  1182. package/dist/server/server/status/engine/stateMachine/workingEvidence.js +97 -0
  1183. package/dist/server/server/status/engine/stateMachine.js +5 -0
  1184. package/dist/server/server/status/engine/types.js +4 -0
  1185. package/dist/server/server/status/paneObservation.js +35 -0
  1186. package/dist/server/server/status/runtime/activityTextExtractors.js +189 -0
  1187. package/dist/server/server/status/runtime/claudeSignals.js +56 -0
  1188. package/dist/server/server/status/runtime/openCodeSignals.js +63 -0
  1189. package/dist/server/server/status/runtime/sessionDetection.js +37 -0
  1190. package/dist/server/server/status/runtime/textSignals.js +21 -0
  1191. package/dist/server/server/status/runtimeSignals.js +21 -0
  1192. package/dist/server/server/status/statusEvaluator.js +16 -0
  1193. package/dist/server/server/status/statusHeuristics.js +146 -0
  1194. package/dist/server/server/status/statusMonitor.js +390 -0
  1195. package/dist/server/server/status/statusMonitor.test.js +191 -0
  1196. package/dist/server/server/status/statusPipeline.js +45 -0
  1197. package/dist/server/server/tmux/tmuxAdapter.js +338 -0
  1198. package/dist/server/server/utils/appRoot.js +40 -0
  1199. package/dist/server/server/utils/path.js +20 -0
  1200. package/dist/server/server/ws/realtimeHub.js +44 -0
  1201. package/dist/server/server/ws/realtimeHub.test.js +43 -0
  1202. package/dist/server/server/ws/terminalBridge.js +153 -0
  1203. package/dist/server/shared/types.js +2 -0
  1204. package/package.json +68 -0
@@ -0,0 +1,1853 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Arcane Agents Map Mask Editor</title>
7
+ <style>
8
+ :root {
9
+ --bg: #0f1613;
10
+ --panel: #16231e;
11
+ --panel-border: #2b3b34;
12
+ --text: #e9f0e8;
13
+ --muted: #a3b7ad;
14
+ --accent: #8dd5ad;
15
+ --danger: #f08e8e;
16
+ --collision: #ff6d6d;
17
+ --occlusion: #56b6ff;
18
+ --occlusion-hard: #8b6bff;
19
+ --flame: #ffb15f;
20
+ }
21
+
22
+ * {
23
+ box-sizing: border-box;
24
+ }
25
+
26
+ html,
27
+ body {
28
+ margin: 0;
29
+ width: 100%;
30
+ height: 100%;
31
+ color: var(--text);
32
+ background: radial-gradient(circle at 12% 4%, #1f3128 0%, #101916 48%, #0a0f0d 100%);
33
+ font-family: "Trebuchet MS", "Segoe UI", sans-serif;
34
+ }
35
+
36
+ .shell {
37
+ width: 100%;
38
+ height: 100%;
39
+ display: grid;
40
+ grid-template-columns: minmax(320px, 390px) 1fr;
41
+ gap: 10px;
42
+ padding: 10px;
43
+ }
44
+
45
+ .controls {
46
+ min-height: 0;
47
+ overflow: auto;
48
+ border: 1px solid var(--panel-border);
49
+ border-radius: 12px;
50
+ background: linear-gradient(180deg, rgba(20, 33, 27, 0.95), rgba(12, 20, 17, 0.98));
51
+ padding: 12px;
52
+ display: flex;
53
+ flex-direction: column;
54
+ gap: 12px;
55
+ }
56
+
57
+ h1 {
58
+ margin: 0;
59
+ font-size: 20px;
60
+ letter-spacing: 0.02em;
61
+ }
62
+
63
+ .subtitle {
64
+ margin: 0;
65
+ color: var(--muted);
66
+ line-height: 1.35;
67
+ font-size: 13px;
68
+ }
69
+
70
+ .group {
71
+ border: 1px solid rgba(206, 225, 191, 0.18);
72
+ border-radius: 10px;
73
+ background: rgba(17, 27, 23, 0.84);
74
+ padding: 10px;
75
+ display: flex;
76
+ flex-direction: column;
77
+ gap: 8px;
78
+ }
79
+
80
+ .group h2 {
81
+ margin: 0;
82
+ font-size: 14px;
83
+ color: #e4f3de;
84
+ letter-spacing: 0.04em;
85
+ text-transform: uppercase;
86
+ }
87
+
88
+ label {
89
+ font-size: 12px;
90
+ color: var(--muted);
91
+ display: flex;
92
+ flex-direction: column;
93
+ gap: 4px;
94
+ }
95
+
96
+ .row {
97
+ display: flex;
98
+ gap: 8px;
99
+ flex-wrap: wrap;
100
+ align-items: center;
101
+ }
102
+
103
+ .triple {
104
+ display: grid;
105
+ grid-template-columns: repeat(3, minmax(0, 1fr));
106
+ gap: 8px;
107
+ }
108
+
109
+ input,
110
+ select,
111
+ textarea,
112
+ button {
113
+ border-radius: 8px;
114
+ border: 1px solid rgba(216, 232, 196, 0.28);
115
+ background: rgba(8, 13, 11, 0.92);
116
+ color: var(--text);
117
+ font-family: inherit;
118
+ }
119
+
120
+ input,
121
+ select,
122
+ textarea {
123
+ padding: 8px 9px;
124
+ font-size: 13px;
125
+ }
126
+
127
+ input[type="range"] {
128
+ padding: 0;
129
+ }
130
+
131
+ button,
132
+ .file-label {
133
+ padding: 8px 10px;
134
+ font-size: 12px;
135
+ font-weight: 700;
136
+ cursor: pointer;
137
+ background: rgba(34, 58, 46, 0.95);
138
+ border: 1px solid rgba(164, 223, 183, 0.32);
139
+ color: #e7f7df;
140
+ text-decoration: none;
141
+ }
142
+
143
+ button:hover,
144
+ .file-label:hover {
145
+ background: rgba(47, 78, 62, 0.95);
146
+ }
147
+
148
+ button.secondary {
149
+ background: rgba(28, 40, 34, 0.95);
150
+ border-color: rgba(208, 224, 183, 0.24);
151
+ }
152
+
153
+ button.danger {
154
+ background: rgba(98, 37, 37, 0.95);
155
+ border-color: rgba(242, 160, 160, 0.35);
156
+ }
157
+
158
+ .file-label {
159
+ position: relative;
160
+ overflow: hidden;
161
+ display: inline-flex;
162
+ align-items: center;
163
+ justify-content: center;
164
+ min-height: 34px;
165
+ }
166
+
167
+ .file-label input[type="file"] {
168
+ position: absolute;
169
+ inset: 0;
170
+ opacity: 0;
171
+ cursor: pointer;
172
+ }
173
+
174
+ .layer-toggle {
175
+ display: grid;
176
+ grid-template-columns: repeat(4, minmax(0, 1fr));
177
+ gap: 8px;
178
+ }
179
+
180
+ .layer-option {
181
+ display: flex;
182
+ align-items: center;
183
+ gap: 6px;
184
+ border: 1px solid rgba(212, 227, 188, 0.2);
185
+ border-radius: 8px;
186
+ background: rgba(14, 21, 18, 0.85);
187
+ padding: 8px;
188
+ cursor: pointer;
189
+ user-select: none;
190
+ color: #e5f1e2;
191
+ }
192
+
193
+ .layer-chip {
194
+ width: 10px;
195
+ height: 10px;
196
+ border-radius: 999px;
197
+ flex: 0 0 auto;
198
+ }
199
+
200
+ .layer-chip.collision {
201
+ background: var(--collision);
202
+ box-shadow: 0 0 0 1px rgba(255, 109, 109, 0.35);
203
+ }
204
+
205
+ .layer-chip.occlusion {
206
+ background: var(--occlusion);
207
+ box-shadow: 0 0 0 1px rgba(86, 182, 255, 0.35);
208
+ }
209
+
210
+ .layer-chip.occlusion-hard {
211
+ background: var(--occlusion-hard);
212
+ box-shadow: 0 0 0 1px rgba(139, 107, 255, 0.35);
213
+ }
214
+
215
+ .layer-chip.flame {
216
+ background: var(--flame);
217
+ box-shadow: 0 0 0 1px rgba(255, 177, 95, 0.38);
218
+ }
219
+
220
+ .status {
221
+ min-height: 18px;
222
+ font-size: 12px;
223
+ color: var(--muted);
224
+ }
225
+
226
+ .status.error {
227
+ color: var(--danger);
228
+ }
229
+
230
+ .status.ok {
231
+ color: var(--accent);
232
+ }
233
+
234
+ textarea {
235
+ width: 100%;
236
+ min-height: 170px;
237
+ resize: vertical;
238
+ line-height: 1.35;
239
+ font-family: "Consolas", "Monaco", monospace;
240
+ }
241
+
242
+ .stage {
243
+ min-height: 0;
244
+ border: 1px solid var(--panel-border);
245
+ border-radius: 12px;
246
+ overflow: hidden;
247
+ background: rgba(9, 14, 12, 0.85);
248
+ display: grid;
249
+ grid-template-rows: 1fr auto;
250
+ }
251
+
252
+ .canvas-wrap {
253
+ min-height: 0;
254
+ overflow: auto;
255
+ padding: 10px;
256
+ display: grid;
257
+ place-items: start center;
258
+ background: linear-gradient(180deg, rgba(11, 19, 16, 0.78), rgba(8, 12, 10, 0.88));
259
+ }
260
+
261
+ #editor-canvas {
262
+ width: min(100%, 1400px);
263
+ height: auto;
264
+ border: 1px solid rgba(213, 229, 193, 0.22);
265
+ border-radius: 6px;
266
+ background: #1a231e;
267
+ touch-action: none;
268
+ box-shadow: 0 12px 28px rgba(0, 0, 0, 0.35);
269
+ }
270
+
271
+ .hud {
272
+ border-top: 1px solid rgba(206, 225, 191, 0.18);
273
+ background: rgba(14, 21, 18, 0.95);
274
+ display: flex;
275
+ align-items: center;
276
+ justify-content: space-between;
277
+ gap: 8px;
278
+ padding: 8px 12px;
279
+ font-size: 12px;
280
+ color: #d4e5d8;
281
+ flex-wrap: wrap;
282
+ }
283
+
284
+ .legend {
285
+ display: flex;
286
+ align-items: center;
287
+ gap: 10px;
288
+ }
289
+
290
+ .legend-item {
291
+ display: inline-flex;
292
+ align-items: center;
293
+ gap: 6px;
294
+ }
295
+
296
+ .dot {
297
+ width: 9px;
298
+ height: 9px;
299
+ border-radius: 999px;
300
+ display: inline-block;
301
+ }
302
+
303
+ .dot.collision {
304
+ background: var(--collision);
305
+ }
306
+
307
+ .dot.occlusion {
308
+ background: var(--occlusion);
309
+ }
310
+
311
+ .dot.occlusion-hard {
312
+ background: var(--occlusion-hard);
313
+ }
314
+
315
+ .dot.flame {
316
+ background: var(--flame);
317
+ }
318
+
319
+ @media (max-width: 1100px) {
320
+ .shell {
321
+ grid-template-columns: 1fr;
322
+ grid-template-rows: auto 1fr;
323
+ }
324
+
325
+ .controls {
326
+ max-height: 46vh;
327
+ }
328
+ }
329
+ </style>
330
+ </head>
331
+ <body>
332
+ <div class="shell">
333
+ <aside class="controls">
334
+ <h1>Map Mask Editor</h1>
335
+ <p class="subtitle">
336
+ Paint collision, soft/hard occlusion, and flame squares over a background map. Left click paints, right click erases.
337
+ </p>
338
+
339
+ <section class="group">
340
+ <h2>Background</h2>
341
+ <label>
342
+ Preset
343
+ <select id="variant-preset">
344
+ <option value="/api/assets/maps/backgrounds/outpost-v1-2x.png" selected>Outpost V1 (2x current)</option>
345
+ <option value="/api/assets/maps/backgrounds/outpost-v1.png">Outpost V1 (1x previous)</option>
346
+ <option value="/api/assets/maps/backgrounds/outpost-v1-original.png">Outpost V1 (original)</option>
347
+ <option value="/api/assets/maps/backgrounds/outpost-v1-upscaled-2x-photo.png">Outpost V1 (upscaled photo)</option>
348
+ <option value="custom">Custom URL</option>
349
+ <option value="/api/dev-map09/variant-01.png">Map09 Variant 01 (legacy)</option>
350
+ <option value="/api/dev-map09/variant-02.png">Map09 Variant 02</option>
351
+ <option value="/api/dev-map09/variant-03.png">Map09 Variant 03</option>
352
+ <option value="/api/dev-map09/variant-04.png">Map09 Variant 04</option>
353
+ <option value="/api/dev-map09/variant-05.png">Map09 Variant 05</option>
354
+ <option value="/api/dev-map09/variant-06.png">Map09 Variant 06</option>
355
+ <option value="/api/dev-map09/variant-07.png">Map09 Variant 07</option>
356
+ <option value="/api/dev-map09/variant-08.png">Map09 Variant 08</option>
357
+ <option value="/api/dev-map09/variant-09.png">Map09 Variant 09</option>
358
+ <option value="/api/dev-map09/variant-10.png">Map09 Variant 10</option>
359
+ <option value="/api/dev-map09/variant-11.png">Map09 Variant 11</option>
360
+ <option value="/api/dev-map09/variant-12.png">Map09 Variant 12</option>
361
+ </select>
362
+ </label>
363
+ <label>
364
+ Image URL
365
+ <input id="image-url" type="text" spellcheck="false" value="/api/assets/maps/backgrounds/outpost-v1-2x.png">
366
+ </label>
367
+ <div class="row">
368
+ <button id="load-image" type="button">Load URL</button>
369
+ <label class="file-label">Load local image<input id="load-file" type="file" accept="image/*"></label>
370
+ </div>
371
+ <div id="image-status" class="status"></div>
372
+ </section>
373
+
374
+ <section class="group">
375
+ <h2>Map Grid</h2>
376
+ <div class="triple">
377
+ <label>Width (tiles)<input id="map-width" type="number" min="8" max="200" value="40"></label>
378
+ <label>Height (tiles)<input id="map-height" type="number" min="8" max="200" value="30"></label>
379
+ <label>Tile px<input id="tile-size" type="number" min="8" max="128" value="32"></label>
380
+ </div>
381
+ <label>
382
+ Occlusion cell px
383
+ <select id="occlusion-cell-size">
384
+ <option value="32">32 (tile)</option>
385
+ <option value="16">16</option>
386
+ <option value="8" selected>8 (recommended)</option>
387
+ <option value="4">4</option>
388
+ <option value="2">2</option>
389
+ </select>
390
+ </label>
391
+ <label>
392
+ Flame cell px
393
+ <select id="flame-cell-size">
394
+ <option value="32">32 (tile)</option>
395
+ <option value="16" selected>16 (recommended)</option>
396
+ <option value="8">8</option>
397
+ <option value="4">4</option>
398
+ <option value="2">2</option>
399
+ </select>
400
+ </label>
401
+ <div class="row">
402
+ <button id="apply-grid" type="button">Apply grid size</button>
403
+ <label style="flex-direction: row; align-items: center; gap: 6px;">
404
+ <input id="show-grid" type="checkbox" checked>
405
+ Show grid
406
+ </label>
407
+ </div>
408
+ </section>
409
+
410
+ <section class="group">
411
+ <h2>Paint</h2>
412
+ <div class="layer-toggle">
413
+ <label class="layer-option"><input type="radio" name="layer" value="collision" checked><span class="layer-chip collision"></span>Collision</label>
414
+ <label class="layer-option"><input type="radio" name="layer" value="occlusion"><span class="layer-chip occlusion"></span>Occlusion</label>
415
+ <label class="layer-option"><input type="radio" name="layer" value="occlusion-hard"><span class="layer-chip occlusion-hard"></span>Hard Occ.</label>
416
+ <label class="layer-option"><input type="radio" name="layer" value="flame"><span class="layer-chip flame"></span>Flame</label>
417
+ </div>
418
+ <label>
419
+ Brush size (cells)
420
+ <input id="brush-size" type="number" min="1" max="32" step="1" value="1">
421
+ </label>
422
+ <div id="brush-readout" class="status">Brush: 1 cell</div>
423
+ <label>
424
+ Collision opacity
425
+ <input id="collision-opacity" type="range" min="0" max="1" step="0.05" value="0.42">
426
+ </label>
427
+ <label>
428
+ Occlusion opacity
429
+ <input id="occlusion-opacity" type="range" min="0" max="1" step="0.05" value="0.36">
430
+ </label>
431
+ <label>
432
+ Hard occlusion opacity
433
+ <input id="occlusion-hard-opacity" type="range" min="0" max="1" step="0.05" value="0.4">
434
+ </label>
435
+ <label>
436
+ Flame opacity
437
+ <input id="flame-opacity" type="range" min="0" max="1" step="0.05" value="0.45">
438
+ </label>
439
+ <div class="row">
440
+ <button id="clear-layer" class="secondary" type="button">Clear active layer</button>
441
+ <button id="clear-all" class="danger" type="button">Clear all masks</button>
442
+ </div>
443
+ </section>
444
+
445
+ <section class="group">
446
+ <h2>Export / Import</h2>
447
+ <div class="row">
448
+ <button id="refresh-json" type="button">Refresh JSON</button>
449
+ <button id="copy-json" class="secondary" type="button">Copy JSON</button>
450
+ <button id="download-json" class="secondary" type="button">Download JSON</button>
451
+ </div>
452
+ <div class="row">
453
+ <button id="load-json" class="secondary" type="button">Load JSON from box</button>
454
+ <button id="download-masks" class="secondary" type="button">Download mask PNGs</button>
455
+ </div>
456
+ <textarea id="json-output" spellcheck="false"></textarea>
457
+ </section>
458
+ </aside>
459
+
460
+ <main class="stage">
461
+ <div class="canvas-wrap" id="canvas-wrap">
462
+ <canvas id="editor-canvas" width="1280" height="960"></canvas>
463
+ </div>
464
+ <div class="hud">
465
+ <div id="tile-readout">Tile: -</div>
466
+ <div class="legend">
467
+ <span class="legend-item"><span class="dot collision"></span>Collision</span>
468
+ <span class="legend-item"><span class="dot occlusion"></span>Occlusion</span>
469
+ <span class="legend-item"><span class="dot occlusion-hard"></span>Hard Occlusion</span>
470
+ <span class="legend-item"><span class="dot flame"></span>Flame</span>
471
+ </div>
472
+ <div id="zoom-readout">Zoom: 100%</div>
473
+ <div id="count-readout">Collision: 0 | Occlusion: 0 | Flames: 0</div>
474
+ </div>
475
+ </main>
476
+ </div>
477
+
478
+ <script>
479
+ const canvas = document.getElementById("editor-canvas");
480
+ const context = canvas.getContext("2d");
481
+ const canvasWrap = document.getElementById("canvas-wrap");
482
+
483
+ const minZoom = 0.5;
484
+ const maxZoom = 6;
485
+ const zoomFactorStep = 1.12;
486
+
487
+ const imageUrlInput = document.getElementById("image-url");
488
+ const variantPreset = document.getElementById("variant-preset");
489
+ const loadImageButton = document.getElementById("load-image");
490
+ const loadFileInput = document.getElementById("load-file");
491
+ const imageStatus = document.getElementById("image-status");
492
+
493
+ const mapWidthInput = document.getElementById("map-width");
494
+ const mapHeightInput = document.getElementById("map-height");
495
+ const tileSizeInput = document.getElementById("tile-size");
496
+ const occlusionCellSizeInput = document.getElementById("occlusion-cell-size");
497
+ const flameCellSizeInput = document.getElementById("flame-cell-size");
498
+ const applyGridButton = document.getElementById("apply-grid");
499
+ const showGridInput = document.getElementById("show-grid");
500
+
501
+ const collisionOpacityInput = document.getElementById("collision-opacity");
502
+ const occlusionOpacityInput = document.getElementById("occlusion-opacity");
503
+ const occlusionHardOpacityInput = document.getElementById("occlusion-hard-opacity");
504
+ const flameOpacityInput = document.getElementById("flame-opacity");
505
+ const brushSizeInput = document.getElementById("brush-size");
506
+ const brushReadout = document.getElementById("brush-readout");
507
+ const clearLayerButton = document.getElementById("clear-layer");
508
+ const clearAllButton = document.getElementById("clear-all");
509
+
510
+ const refreshJsonButton = document.getElementById("refresh-json");
511
+ const copyJsonButton = document.getElementById("copy-json");
512
+ const downloadJsonButton = document.getElementById("download-json");
513
+ const loadJsonButton = document.getElementById("load-json");
514
+ const downloadMasksButton = document.getElementById("download-masks");
515
+ const jsonOutput = document.getElementById("json-output");
516
+
517
+ const tileReadout = document.getElementById("tile-readout");
518
+ const zoomReadout = document.getElementById("zoom-readout");
519
+ const countReadout = document.getElementById("count-readout");
520
+
521
+ const urlParams = new URLSearchParams(window.location.search);
522
+ const initialImageUrl = urlParams.get("image") || imageUrlInput.value;
523
+ const initialLogicUrl = urlParams.get("logic") || "/api/assets/maps/outpost.logic.json";
524
+
525
+ const state = {
526
+ mapWidth: 40,
527
+ mapHeight: 30,
528
+ tileSize: 32,
529
+ occlusionCellSize: 8,
530
+ flameCellSize: 16,
531
+ zoom: 1,
532
+ showGrid: true,
533
+ activeLayer: "collision",
534
+ brushSizeCells: 1,
535
+ collisionOpacity: 0.42,
536
+ occlusionOpacity: 0.36,
537
+ occlusionHardOpacity: 0.4,
538
+ flameOpacity: 0.45,
539
+ collisionMask: createGrid(40, 30),
540
+ occlusionMask: createOcclusionGrid(40, 30, 32, 8),
541
+ occlusionHardMask: createOcclusionGrid(40, 30, 32, 8),
542
+ flameMask: createOcclusionGrid(40, 30, 32, 16),
543
+ backgroundImage: null,
544
+ backgroundImageUrl: initialImageUrl,
545
+ hoverInfo: null,
546
+ pointerDown: false,
547
+ pointerPaintValue: true
548
+ };
549
+
550
+ imageUrlInput.value = initialImageUrl;
551
+
552
+ for (const input of document.querySelectorAll("input[name='layer']")) {
553
+ input.addEventListener("change", () => {
554
+ if (input.checked) {
555
+ state.activeLayer = input.value;
556
+ draw();
557
+ }
558
+ });
559
+ }
560
+
561
+ variantPreset.addEventListener("change", () => {
562
+ if (variantPreset.value === "custom") {
563
+ return;
564
+ }
565
+
566
+ imageUrlInput.value = variantPreset.value;
567
+ void loadImageFromUrl(variantPreset.value);
568
+ });
569
+
570
+ loadImageButton.addEventListener("click", () => {
571
+ void loadImageFromUrl(imageUrlInput.value);
572
+ });
573
+
574
+ loadFileInput.addEventListener("change", () => {
575
+ const file = loadFileInput.files?.[0];
576
+ if (!file) {
577
+ return;
578
+ }
579
+
580
+ const objectUrl = URL.createObjectURL(file);
581
+ state.backgroundImageUrl = file.name;
582
+ imageUrlInput.value = file.name;
583
+ variantPreset.value = "custom";
584
+ void loadImageFromUrl(objectUrl, true);
585
+ });
586
+
587
+ applyGridButton.addEventListener("click", () => {
588
+ const previousState = {
589
+ mapWidth: state.mapWidth,
590
+ mapHeight: state.mapHeight,
591
+ tileSize: state.tileSize,
592
+ occlusionCellSize: state.occlusionCellSize,
593
+ occlusionMask: state.occlusionMask,
594
+ occlusionHardMask: state.occlusionHardMask,
595
+ flameCellSize: state.flameCellSize,
596
+ flameMask: state.flameMask
597
+ };
598
+
599
+ const nextWidth = clampInt(mapWidthInput.value, 8, 200, state.mapWidth);
600
+ const nextHeight = clampInt(mapHeightInput.value, 8, 200, state.mapHeight);
601
+ const nextTileSize = clampInt(tileSizeInput.value, 8, 128, state.tileSize);
602
+ const nextOcclusionCellSize = clampOcclusionCellSize(occlusionCellSizeInput.value, nextTileSize, state.occlusionCellSize);
603
+ const nextFlameCellSize = clampOcclusionCellSize(flameCellSizeInput.value, nextTileSize, state.flameCellSize);
604
+
605
+ state.collisionMask = resizeGrid(state.collisionMask, nextWidth, nextHeight);
606
+ state.mapWidth = nextWidth;
607
+ state.mapHeight = nextHeight;
608
+ state.tileSize = nextTileSize;
609
+ state.occlusionCellSize = nextOcclusionCellSize;
610
+ state.flameCellSize = nextFlameCellSize;
611
+ state.occlusionMask = resampleOcclusionGrid(
612
+ previousState.occlusionMask,
613
+ previousState.mapWidth,
614
+ previousState.mapHeight,
615
+ previousState.tileSize,
616
+ previousState.occlusionCellSize,
617
+ nextWidth,
618
+ nextHeight,
619
+ nextTileSize,
620
+ nextOcclusionCellSize
621
+ );
622
+ state.occlusionHardMask = resampleOcclusionGrid(
623
+ previousState.occlusionHardMask,
624
+ previousState.mapWidth,
625
+ previousState.mapHeight,
626
+ previousState.tileSize,
627
+ previousState.occlusionCellSize,
628
+ nextWidth,
629
+ nextHeight,
630
+ nextTileSize,
631
+ nextOcclusionCellSize
632
+ );
633
+ state.flameMask = resampleOcclusionGrid(
634
+ previousState.flameMask,
635
+ previousState.mapWidth,
636
+ previousState.mapHeight,
637
+ previousState.tileSize,
638
+ previousState.flameCellSize,
639
+ nextWidth,
640
+ nextHeight,
641
+ nextTileSize,
642
+ nextFlameCellSize
643
+ );
644
+
645
+ mapWidthInput.value = String(nextWidth);
646
+ mapHeightInput.value = String(nextHeight);
647
+ tileSizeInput.value = String(nextTileSize);
648
+ occlusionCellSizeInput.value = String(nextOcclusionCellSize);
649
+ flameCellSizeInput.value = String(nextFlameCellSize);
650
+
651
+ resizeCanvas();
652
+ updateBrushReadout();
653
+ draw();
654
+ refreshJsonOutput();
655
+ setStatus(
656
+ `Grid set to ${nextWidth}x${nextHeight} tiles, tile ${nextTileSize}px, occlusion ${nextOcclusionCellSize}px, flame ${nextFlameCellSize}px.`,
657
+ "ok"
658
+ );
659
+ });
660
+
661
+ occlusionCellSizeInput.addEventListener("change", () => {
662
+ const nextOcclusionCellSize = clampOcclusionCellSize(occlusionCellSizeInput.value, state.tileSize, state.occlusionCellSize);
663
+ if (nextOcclusionCellSize === state.occlusionCellSize) {
664
+ occlusionCellSizeInput.value = String(state.occlusionCellSize);
665
+ return;
666
+ }
667
+
668
+ state.occlusionMask = resampleOcclusionGrid(
669
+ state.occlusionMask,
670
+ state.mapWidth,
671
+ state.mapHeight,
672
+ state.tileSize,
673
+ state.occlusionCellSize,
674
+ state.mapWidth,
675
+ state.mapHeight,
676
+ state.tileSize,
677
+ nextOcclusionCellSize
678
+ );
679
+ state.occlusionHardMask = resampleOcclusionGrid(
680
+ state.occlusionHardMask,
681
+ state.mapWidth,
682
+ state.mapHeight,
683
+ state.tileSize,
684
+ state.occlusionCellSize,
685
+ state.mapWidth,
686
+ state.mapHeight,
687
+ state.tileSize,
688
+ nextOcclusionCellSize
689
+ );
690
+ state.occlusionCellSize = nextOcclusionCellSize;
691
+ occlusionCellSizeInput.value = String(nextOcclusionCellSize);
692
+
693
+ updateBrushReadout();
694
+ draw();
695
+ refreshJsonOutput();
696
+ setStatus(`Occlusion cell size set to ${nextOcclusionCellSize}px.`, "ok");
697
+ });
698
+
699
+ flameCellSizeInput.addEventListener("change", () => {
700
+ const nextFlameCellSize = clampOcclusionCellSize(flameCellSizeInput.value, state.tileSize, state.flameCellSize);
701
+ if (nextFlameCellSize === state.flameCellSize) {
702
+ flameCellSizeInput.value = String(state.flameCellSize);
703
+ return;
704
+ }
705
+
706
+ state.flameMask = resampleOcclusionGrid(
707
+ state.flameMask,
708
+ state.mapWidth,
709
+ state.mapHeight,
710
+ state.tileSize,
711
+ state.flameCellSize,
712
+ state.mapWidth,
713
+ state.mapHeight,
714
+ state.tileSize,
715
+ nextFlameCellSize
716
+ );
717
+ state.flameCellSize = nextFlameCellSize;
718
+ flameCellSizeInput.value = String(nextFlameCellSize);
719
+
720
+ updateBrushReadout();
721
+ draw();
722
+ refreshJsonOutput();
723
+ setStatus(`Flame cell size set to ${nextFlameCellSize}px.`, "ok");
724
+ });
725
+
726
+ showGridInput.addEventListener("change", () => {
727
+ state.showGrid = showGridInput.checked;
728
+ draw();
729
+ });
730
+
731
+ collisionOpacityInput.addEventListener("input", () => {
732
+ state.collisionOpacity = clampNumber(Number(collisionOpacityInput.value), 0, 1);
733
+ draw();
734
+ });
735
+
736
+ occlusionOpacityInput.addEventListener("input", () => {
737
+ state.occlusionOpacity = clampNumber(Number(occlusionOpacityInput.value), 0, 1);
738
+ draw();
739
+ });
740
+
741
+ occlusionHardOpacityInput.addEventListener("input", () => {
742
+ state.occlusionHardOpacity = clampNumber(Number(occlusionHardOpacityInput.value), 0, 1);
743
+ draw();
744
+ });
745
+
746
+ flameOpacityInput.addEventListener("input", () => {
747
+ state.flameOpacity = clampNumber(Number(flameOpacityInput.value), 0, 1);
748
+ draw();
749
+ });
750
+
751
+ brushSizeInput.addEventListener("input", () => {
752
+ const nextBrushSize = clampInt(brushSizeInput.value, 1, 32, state.brushSizeCells);
753
+ state.brushSizeCells = nextBrushSize;
754
+ brushSizeInput.value = String(nextBrushSize);
755
+ updateBrushReadout();
756
+ draw();
757
+ });
758
+
759
+ clearLayerButton.addEventListener("click", () => {
760
+ const label = state.activeLayer;
761
+ if (!window.confirm(`Clear all ${label} tiles?`)) {
762
+ return;
763
+ }
764
+
765
+ clearLayer(state.activeLayer);
766
+ draw();
767
+ refreshJsonOutput();
768
+ setStatus(`Cleared ${label} layer.`, "ok");
769
+ });
770
+
771
+ clearAllButton.addEventListener("click", () => {
772
+ if (!window.confirm("Clear collision, soft/hard occlusion, and flame layers?")) {
773
+ return;
774
+ }
775
+
776
+ clearLayer("collision");
777
+ clearLayer("occlusion");
778
+ clearLayer("occlusion-hard");
779
+ clearLayer("flame");
780
+ draw();
781
+ refreshJsonOutput();
782
+ setStatus("Cleared all masks.", "ok");
783
+ });
784
+
785
+ refreshJsonButton.addEventListener("click", () => {
786
+ refreshJsonOutput();
787
+ setStatus("JSON refreshed.", "ok");
788
+ });
789
+
790
+ copyJsonButton.addEventListener("click", async () => {
791
+ refreshJsonOutput();
792
+ try {
793
+ await navigator.clipboard.writeText(jsonOutput.value);
794
+ setStatus("Copied JSON to clipboard.", "ok");
795
+ } catch {
796
+ setStatus("Clipboard copy failed. You can still copy from the text area.", "error");
797
+ }
798
+ });
799
+
800
+ downloadJsonButton.addEventListener("click", () => {
801
+ refreshJsonOutput();
802
+ downloadTextFile("map-logic.json", jsonOutput.value, "application/json");
803
+ setStatus("Downloaded map-logic.json.", "ok");
804
+ });
805
+
806
+ loadJsonButton.addEventListener("click", () => {
807
+ const raw = jsonOutput.value.trim();
808
+ if (!raw) {
809
+ setStatus("Paste JSON into the text area first.", "error");
810
+ return;
811
+ }
812
+
813
+ try {
814
+ const parsed = JSON.parse(raw);
815
+ applySerializedLogic(parsed);
816
+ draw();
817
+ refreshJsonOutput();
818
+ setStatus("Loaded JSON into editor.", "ok");
819
+ } catch (error) {
820
+ const message = error instanceof Error ? error.message : "Invalid JSON";
821
+ setStatus(`Load failed: ${message}`, "error");
822
+ }
823
+ });
824
+
825
+ downloadMasksButton.addEventListener("click", async () => {
826
+ await downloadMaskPng("collision-mask.png", state.collisionMask, state.tileSize, "#000000");
827
+ await downloadMaskPng("occlusion-mask.png", state.occlusionMask, state.occlusionCellSize, "#000000");
828
+ await downloadMaskPng("occlusion-hard-mask.png", state.occlusionHardMask, state.occlusionCellSize, "#000000");
829
+ await downloadMaskPng("flame-mask.png", state.flameMask, state.flameCellSize, "#000000");
830
+ setStatus("Downloaded collision-mask.png, occlusion-mask.png, occlusion-hard-mask.png, and flame-mask.png.", "ok");
831
+ });
832
+
833
+ canvas.addEventListener("pointerdown", (event) => {
834
+ if (event.button !== 0 && event.button !== 2) {
835
+ return;
836
+ }
837
+
838
+ event.preventDefault();
839
+ state.pointerDown = true;
840
+ state.pointerPaintValue = event.button === 2 ? false : !event.altKey;
841
+ canvas.setPointerCapture(event.pointerId);
842
+ paintFromPointer(event);
843
+ });
844
+
845
+ canvas.addEventListener("pointermove", (event) => {
846
+ updateHoverFromPointer(event);
847
+
848
+ if (!state.pointerDown) {
849
+ draw();
850
+ return;
851
+ }
852
+
853
+ paintFromPointer(event);
854
+ });
855
+
856
+ canvas.addEventListener("pointerup", (event) => {
857
+ state.pointerDown = false;
858
+ if (canvas.hasPointerCapture(event.pointerId)) {
859
+ canvas.releasePointerCapture(event.pointerId);
860
+ }
861
+ refreshJsonOutput();
862
+ });
863
+
864
+ canvas.addEventListener("pointercancel", (event) => {
865
+ state.pointerDown = false;
866
+ if (canvas.hasPointerCapture(event.pointerId)) {
867
+ canvas.releasePointerCapture(event.pointerId);
868
+ }
869
+ });
870
+
871
+ canvas.addEventListener("pointerleave", () => {
872
+ state.hoverInfo = null;
873
+ draw();
874
+ });
875
+
876
+ canvas.addEventListener("contextmenu", (event) => {
877
+ event.preventDefault();
878
+ });
879
+
880
+ canvas.addEventListener(
881
+ "wheel",
882
+ (event) => {
883
+ event.preventDefault();
884
+
885
+ const direction = event.deltaY < 0 ? 1 : -1;
886
+ const magnitude = Math.max(1, Math.min(3, Math.abs(event.deltaY) / 120));
887
+ const factor = direction > 0 ? Math.pow(zoomFactorStep, magnitude) : Math.pow(1 / zoomFactorStep, magnitude);
888
+ setZoom(state.zoom * factor, event.clientX, event.clientY);
889
+ },
890
+ {
891
+ passive: false
892
+ }
893
+ );
894
+
895
+ window.addEventListener("keydown", (event) => {
896
+ if (isTypingTarget(event.target)) {
897
+ return;
898
+ }
899
+
900
+ const key = event.key.toLowerCase();
901
+
902
+ if (key === "c") {
903
+ setActiveLayer("collision");
904
+ event.preventDefault();
905
+ } else if (key === "o") {
906
+ setActiveLayer("occlusion");
907
+ event.preventDefault();
908
+ } else if (key === "h") {
909
+ setActiveLayer("occlusion-hard");
910
+ event.preventDefault();
911
+ } else if (key === "f") {
912
+ setActiveLayer("flame");
913
+ event.preventDefault();
914
+ } else if (key === "g") {
915
+ state.showGrid = !state.showGrid;
916
+ showGridInput.checked = state.showGrid;
917
+ draw();
918
+ event.preventDefault();
919
+ } else if (key === "x") {
920
+ clearLayer(state.activeLayer);
921
+ draw();
922
+ refreshJsonOutput();
923
+ setStatus(`Cleared ${state.activeLayer} layer.`, "ok");
924
+ event.preventDefault();
925
+ } else if (key === "]") {
926
+ state.brushSizeCells = clampInt(state.brushSizeCells + 1, 1, 32, state.brushSizeCells);
927
+ brushSizeInput.value = String(state.brushSizeCells);
928
+ updateBrushReadout();
929
+ draw();
930
+ event.preventDefault();
931
+ } else if (key === "[") {
932
+ state.brushSizeCells = clampInt(state.brushSizeCells - 1, 1, 32, state.brushSizeCells);
933
+ brushSizeInput.value = String(state.brushSizeCells);
934
+ updateBrushReadout();
935
+ draw();
936
+ event.preventDefault();
937
+ } else if (key === "+" || key === "=") {
938
+ setZoom(state.zoom * zoomFactorStep);
939
+ event.preventDefault();
940
+ } else if (key === "-") {
941
+ setZoom(state.zoom / zoomFactorStep);
942
+ event.preventDefault();
943
+ } else if (key === "0") {
944
+ setZoom(1);
945
+ event.preventDefault();
946
+ } else if (key === "s" && (event.ctrlKey || event.metaKey)) {
947
+ event.preventDefault();
948
+ refreshJsonOutput();
949
+ downloadTextFile("map-logic.json", jsonOutput.value, "application/json");
950
+ setStatus("Downloaded map-logic.json.", "ok");
951
+ }
952
+ });
953
+
954
+ function setActiveLayer(layer) {
955
+ state.activeLayer = layer;
956
+ for (const input of document.querySelectorAll("input[name='layer']")) {
957
+ input.checked = input.value === layer;
958
+ }
959
+ updateBrushReadout();
960
+ draw();
961
+ }
962
+
963
+ function getLayerConfig(layer) {
964
+ if (layer === "collision") {
965
+ return {
966
+ grid: state.collisionMask,
967
+ cellSize: state.tileSize
968
+ };
969
+ }
970
+
971
+ if (layer === "flame") {
972
+ return {
973
+ grid: state.flameMask,
974
+ cellSize: state.flameCellSize
975
+ };
976
+ }
977
+
978
+ if (layer === "occlusion-hard") {
979
+ return {
980
+ grid: state.occlusionHardMask,
981
+ cellSize: state.occlusionCellSize
982
+ };
983
+ }
984
+
985
+ return {
986
+ grid: state.occlusionMask,
987
+ cellSize: state.occlusionCellSize
988
+ };
989
+ }
990
+
991
+ function updateBrushReadout() {
992
+ const activeLayerConfig = getLayerConfig(state.activeLayer);
993
+ const activePixels = state.brushSizeCells * activeLayerConfig.cellSize;
994
+ const collisionPixels = state.brushSizeCells * state.tileSize;
995
+ const occlusionPixels = state.brushSizeCells * state.occlusionCellSize;
996
+ const flamePixels = state.brushSizeCells * state.flameCellSize;
997
+ brushReadout.textContent = `Active ${state.activeLayer}: ${state.brushSizeCells} cells (${activePixels}px). Collision ${collisionPixels}px | Occlusion ${occlusionPixels}px | Flame ${flamePixels}px.`;
998
+ }
999
+
1000
+ function createGrid(width, height) {
1001
+ return Array.from({ length: height }, () => Array.from({ length: width }, () => false));
1002
+ }
1003
+
1004
+ function resizeGrid(existing, nextWidth, nextHeight) {
1005
+ const next = createGrid(nextWidth, nextHeight);
1006
+ for (let y = 0; y < nextHeight; y += 1) {
1007
+ for (let x = 0; x < nextWidth; x += 1) {
1008
+ next[y][x] = Boolean(existing[y]?.[x]);
1009
+ }
1010
+ }
1011
+ return next;
1012
+ }
1013
+
1014
+ function createOcclusionGrid(mapWidth, mapHeight, tileSize, occlusionCellSize) {
1015
+ const worldWidth = mapWidth * tileSize;
1016
+ const worldHeight = mapHeight * tileSize;
1017
+ const columns = Math.max(1, Math.ceil(worldWidth / occlusionCellSize));
1018
+ const rows = Math.max(1, Math.ceil(worldHeight / occlusionCellSize));
1019
+ return createGrid(columns, rows);
1020
+ }
1021
+
1022
+ function clampOcclusionCellSize(rawValue, tileSize, fallback) {
1023
+ const candidate = clampInt(rawValue, 2, tileSize, fallback);
1024
+ if (candidate > 0 && tileSize % candidate === 0) {
1025
+ return candidate;
1026
+ }
1027
+
1028
+ for (let divisor = candidate; divisor >= 2; divisor -= 1) {
1029
+ if (tileSize % divisor === 0) {
1030
+ return divisor;
1031
+ }
1032
+ }
1033
+
1034
+ return Math.max(2, Math.min(tileSize, fallback));
1035
+ }
1036
+
1037
+ function clearGrid(grid) {
1038
+ for (let y = 0; y < grid.length; y += 1) {
1039
+ for (let x = 0; x < (grid[y]?.length ?? 0); x += 1) {
1040
+ grid[y][x] = false;
1041
+ }
1042
+ }
1043
+ }
1044
+
1045
+ function countGridActive(grid) {
1046
+ let count = 0;
1047
+ for (let y = 0; y < grid.length; y += 1) {
1048
+ for (let x = 0; x < (grid[y]?.length ?? 0); x += 1) {
1049
+ if (grid[y][x]) {
1050
+ count += 1;
1051
+ }
1052
+ }
1053
+ }
1054
+ return count;
1055
+ }
1056
+
1057
+ function collectGridActive(grid) {
1058
+ const result = [];
1059
+ for (let y = 0; y < grid.length; y += 1) {
1060
+ for (let x = 0; x < (grid[y]?.length ?? 0); x += 1) {
1061
+ if (grid[y][x]) {
1062
+ result.push([x, y]);
1063
+ }
1064
+ }
1065
+ }
1066
+ return result;
1067
+ }
1068
+
1069
+ function resampleOcclusionGrid(
1070
+ previousMask,
1071
+ previousMapWidth,
1072
+ previousMapHeight,
1073
+ previousTileSize,
1074
+ previousCellSize,
1075
+ nextMapWidth,
1076
+ nextMapHeight,
1077
+ nextTileSize,
1078
+ nextCellSize
1079
+ ) {
1080
+ const nextMask = createOcclusionGrid(nextMapWidth, nextMapHeight, nextTileSize, nextCellSize);
1081
+ const previousWorldWidth = previousMapWidth * previousTileSize;
1082
+ const previousWorldHeight = previousMapHeight * previousTileSize;
1083
+ const nextWorldWidth = nextMapWidth * nextTileSize;
1084
+ const nextWorldHeight = nextMapHeight * nextTileSize;
1085
+
1086
+ for (let previousY = 0; previousY < previousMask.length; previousY += 1) {
1087
+ const row = previousMask[previousY];
1088
+ if (!Array.isArray(row)) {
1089
+ continue;
1090
+ }
1091
+
1092
+ for (let previousX = 0; previousX < row.length; previousX += 1) {
1093
+ if (!row[previousX]) {
1094
+ continue;
1095
+ }
1096
+
1097
+ const left = previousX * previousCellSize;
1098
+ const top = previousY * previousCellSize;
1099
+ const right = Math.min(previousWorldWidth, left + previousCellSize);
1100
+ const bottom = Math.min(previousWorldHeight, top + previousCellSize);
1101
+
1102
+ if (right <= 0 || bottom <= 0 || left >= nextWorldWidth || top >= nextWorldHeight) {
1103
+ continue;
1104
+ }
1105
+
1106
+ const clampedLeft = Math.max(0, left);
1107
+ const clampedTop = Math.max(0, top);
1108
+ const clampedRight = Math.min(nextWorldWidth, right);
1109
+ const clampedBottom = Math.min(nextWorldHeight, bottom);
1110
+ if (clampedRight <= clampedLeft || clampedBottom <= clampedTop) {
1111
+ continue;
1112
+ }
1113
+
1114
+ const minCellX = Math.floor(clampedLeft / nextCellSize);
1115
+ const maxCellX = Math.floor((clampedRight - 0.001) / nextCellSize);
1116
+ const minCellY = Math.floor(clampedTop / nextCellSize);
1117
+ const maxCellY = Math.floor((clampedBottom - 0.001) / nextCellSize);
1118
+
1119
+ for (let nextY = minCellY; nextY <= maxCellY; nextY += 1) {
1120
+ if (nextY < 0 || nextY >= nextMask.length) {
1121
+ continue;
1122
+ }
1123
+ for (let nextX = minCellX; nextX <= maxCellX; nextX += 1) {
1124
+ if (nextX < 0 || nextX >= nextMask[nextY].length) {
1125
+ continue;
1126
+ }
1127
+ nextMask[nextY][nextX] = true;
1128
+ }
1129
+ }
1130
+ }
1131
+ }
1132
+
1133
+ return nextMask;
1134
+ }
1135
+
1136
+ function clampInt(rawValue, min, max, fallback) {
1137
+ const value = Number(rawValue);
1138
+ if (!Number.isFinite(value)) {
1139
+ return fallback;
1140
+ }
1141
+ return Math.max(min, Math.min(max, Math.round(value)));
1142
+ }
1143
+
1144
+ function clampNumber(value, min, max) {
1145
+ if (!Number.isFinite(value)) {
1146
+ return min;
1147
+ }
1148
+ return Math.max(min, Math.min(max, value));
1149
+ }
1150
+
1151
+ function applyCanvasZoom() {
1152
+ const displayWidth = Math.max(1, Math.round(canvas.width * state.zoom));
1153
+ const displayHeight = Math.max(1, Math.round(canvas.height * state.zoom));
1154
+ canvas.style.width = `${displayWidth}px`;
1155
+ canvas.style.height = `${displayHeight}px`;
1156
+
1157
+ if (zoomReadout) {
1158
+ zoomReadout.textContent = `Zoom: ${Math.round(state.zoom * 100)}%`;
1159
+ }
1160
+ }
1161
+
1162
+ function setZoom(nextZoom, anchorClientX, anchorClientY) {
1163
+ const clampedZoom = clampNumber(nextZoom, minZoom, maxZoom);
1164
+ if (Math.abs(clampedZoom - state.zoom) < 0.001) {
1165
+ return;
1166
+ }
1167
+
1168
+ const wrapRect = canvasWrap.getBoundingClientRect();
1169
+ const canvasRect = canvas.getBoundingClientRect();
1170
+ const pointerClientX = Number.isFinite(anchorClientX) ? anchorClientX : wrapRect.left + wrapRect.width / 2;
1171
+ const pointerClientY = Number.isFinite(anchorClientY) ? anchorClientY : wrapRect.top + wrapRect.height / 2;
1172
+
1173
+ const anchorCanvasX = clampNumber(
1174
+ (pointerClientX - canvasRect.left) * (canvas.width / Math.max(1, canvasRect.width)),
1175
+ 0,
1176
+ canvas.width
1177
+ );
1178
+ const anchorCanvasY = clampNumber(
1179
+ (pointerClientY - canvasRect.top) * (canvas.height / Math.max(1, canvasRect.height)),
1180
+ 0,
1181
+ canvas.height
1182
+ );
1183
+ const pointerOffsetX = pointerClientX - wrapRect.left;
1184
+ const pointerOffsetY = pointerClientY - wrapRect.top;
1185
+
1186
+ state.zoom = clampedZoom;
1187
+ applyCanvasZoom();
1188
+
1189
+ const nextScrollLeft = anchorCanvasX * state.zoom - pointerOffsetX;
1190
+ const nextScrollTop = anchorCanvasY * state.zoom - pointerOffsetY;
1191
+ const maxScrollLeft = Math.max(0, canvasWrap.scrollWidth - canvasWrap.clientWidth);
1192
+ const maxScrollTop = Math.max(0, canvasWrap.scrollHeight - canvasWrap.clientHeight);
1193
+
1194
+ canvasWrap.scrollLeft = clampNumber(nextScrollLeft, 0, maxScrollLeft);
1195
+ canvasWrap.scrollTop = clampNumber(nextScrollTop, 0, maxScrollTop);
1196
+
1197
+ draw();
1198
+ }
1199
+
1200
+ function resizeCanvas() {
1201
+ canvas.width = state.mapWidth * state.tileSize;
1202
+ canvas.height = state.mapHeight * state.tileSize;
1203
+ applyCanvasZoom();
1204
+ }
1205
+
1206
+ async function loadImageFromUrl(url, fromObjectUrl = false) {
1207
+ const trimmed = String(url || "").trim();
1208
+ if (!trimmed) {
1209
+ setStatus("Enter an image URL first.", "error");
1210
+ return;
1211
+ }
1212
+
1213
+ setStatus("Loading image...", "");
1214
+
1215
+ try {
1216
+ const image = await loadImage(trimmed);
1217
+ state.backgroundImage = image;
1218
+ if (!fromObjectUrl) {
1219
+ state.backgroundImageUrl = trimmed;
1220
+ }
1221
+ draw();
1222
+ setStatus(`Loaded ${image.naturalWidth}x${image.naturalHeight} image.`, "ok");
1223
+ } catch {
1224
+ setStatus("Image load failed. Check URL/path and server route.", "error");
1225
+ }
1226
+ }
1227
+
1228
+ function loadImage(url) {
1229
+ return new Promise((resolve, reject) => {
1230
+ const image = new Image();
1231
+ image.onload = () => resolve(image);
1232
+ image.onerror = () => reject(new Error("Image load error"));
1233
+ image.src = url;
1234
+ });
1235
+ }
1236
+
1237
+ function updateHoverFromPointer(event) {
1238
+ const pointer = pointerToCanvasPoint(event);
1239
+ if (!pointer) {
1240
+ state.hoverInfo = null;
1241
+ return;
1242
+ }
1243
+
1244
+ if (!isCanvasPointInBounds(pointer.canvasX, pointer.canvasY)) {
1245
+ state.hoverInfo = null;
1246
+ return;
1247
+ }
1248
+
1249
+ state.hoverInfo = {
1250
+ canvasX: pointer.canvasX,
1251
+ canvasY: pointer.canvasY,
1252
+ tileX: Math.floor(pointer.canvasX / state.tileSize),
1253
+ tileY: Math.floor(pointer.canvasY / state.tileSize),
1254
+ occlusionX: Math.floor(pointer.canvasX / state.occlusionCellSize),
1255
+ occlusionY: Math.floor(pointer.canvasY / state.occlusionCellSize),
1256
+ flameX: Math.floor(pointer.canvasX / state.flameCellSize),
1257
+ flameY: Math.floor(pointer.canvasY / state.flameCellSize)
1258
+ };
1259
+ }
1260
+
1261
+ function paintFromPointer(event) {
1262
+ const pointer = pointerToCanvasPoint(event);
1263
+ if (!pointer || !isCanvasPointInBounds(pointer.canvasX, pointer.canvasY)) {
1264
+ return;
1265
+ }
1266
+
1267
+ const changed = applyBrushToLayer(state.activeLayer, pointer.canvasX, pointer.canvasY, state.pointerPaintValue);
1268
+
1269
+ if (changed) {
1270
+ draw();
1271
+ }
1272
+ }
1273
+
1274
+ function pointerToCanvasPoint(event) {
1275
+ const rect = canvas.getBoundingClientRect();
1276
+ if (rect.width <= 0 || rect.height <= 0) {
1277
+ return null;
1278
+ }
1279
+
1280
+ const canvasX = Math.floor((event.clientX - rect.left) * (canvas.width / rect.width));
1281
+ const canvasY = Math.floor((event.clientY - rect.top) * (canvas.height / rect.height));
1282
+ return {
1283
+ canvasX,
1284
+ canvasY
1285
+ };
1286
+ }
1287
+
1288
+ function isCanvasPointInBounds(canvasX, canvasY) {
1289
+ return canvasX >= 0 && canvasY >= 0 && canvasX < canvas.width && canvasY < canvas.height;
1290
+ }
1291
+
1292
+ function isGridCoordinateInBounds(grid, coordinateX, coordinateY) {
1293
+ return coordinateY >= 0 && coordinateY < grid.length && coordinateX >= 0 && coordinateX < (grid[coordinateY]?.length ?? 0);
1294
+ }
1295
+
1296
+ function computeBrushCellRange(layer, canvasX, canvasY) {
1297
+ const { grid, cellSize } = getLayerConfig(layer);
1298
+ const brushCells = clampInt(state.brushSizeCells, 1, 32, 1);
1299
+ const centerCellX = Math.floor(canvasX / cellSize);
1300
+ const centerCellY = Math.floor(canvasY / cellSize);
1301
+
1302
+ const startCellX = centerCellX - Math.floor(brushCells / 2);
1303
+ const startCellY = centerCellY - Math.floor(brushCells / 2);
1304
+ const minCellX = startCellX;
1305
+ const maxCellX = startCellX + brushCells - 1;
1306
+ const minCellY = startCellY;
1307
+ const maxCellY = startCellY + brushCells - 1;
1308
+
1309
+ const maxGridY = grid.length - 1;
1310
+ const maxGridX = Math.max(0, (grid[0]?.length ?? 1) - 1);
1311
+
1312
+ if (maxGridX < 0 || maxGridY < 0) {
1313
+ return null;
1314
+ }
1315
+
1316
+ const startX = Math.max(0, minCellX);
1317
+ const endX = Math.min(maxGridX, maxCellX);
1318
+ const startY = Math.max(0, minCellY);
1319
+ const endY = Math.min(maxGridY, maxCellY);
1320
+ if (endX < startX || endY < startY) {
1321
+ return null;
1322
+ }
1323
+
1324
+ return {
1325
+ cellSize,
1326
+ startX,
1327
+ endX,
1328
+ startY,
1329
+ endY
1330
+ };
1331
+ }
1332
+
1333
+ function applyBrushToLayer(layer, canvasX, canvasY, value) {
1334
+ const { grid } = getLayerConfig(layer);
1335
+ const range = computeBrushCellRange(layer, canvasX, canvasY);
1336
+ if (!range) {
1337
+ return false;
1338
+ }
1339
+
1340
+ let changed = false;
1341
+ for (let y = range.startY; y <= range.endY; y += 1) {
1342
+ for (let x = range.startX; x <= range.endX; x += 1) {
1343
+ if (!isGridCoordinateInBounds(grid, x, y)) {
1344
+ continue;
1345
+ }
1346
+ if (Boolean(grid[y][x]) === value) {
1347
+ continue;
1348
+ }
1349
+ grid[y][x] = value;
1350
+ changed = true;
1351
+ }
1352
+ }
1353
+
1354
+ return changed;
1355
+ }
1356
+
1357
+ function setMaskTile(layer, coordinateX, coordinateY, value) {
1358
+ const { grid: target } = getLayerConfig(layer);
1359
+ if (!isGridCoordinateInBounds(target, coordinateX, coordinateY)) {
1360
+ return false;
1361
+ }
1362
+
1363
+ const current = Boolean(target[coordinateY]?.[coordinateX]);
1364
+ if (current === value) {
1365
+ return false;
1366
+ }
1367
+
1368
+ target[coordinateY][coordinateX] = value;
1369
+ return true;
1370
+ }
1371
+
1372
+ function clearLayer(layer) {
1373
+ const { grid: target } = getLayerConfig(layer);
1374
+ clearGrid(target);
1375
+ }
1376
+
1377
+ function draw() {
1378
+ context.clearRect(0, 0, canvas.width, canvas.height);
1379
+
1380
+ if (state.backgroundImage) {
1381
+ context.imageSmoothingEnabled = true;
1382
+ context.drawImage(state.backgroundImage, 0, 0, canvas.width, canvas.height);
1383
+ } else {
1384
+ drawPlaceholderBackground();
1385
+ }
1386
+
1387
+ drawMaskLayer(state.collisionMask, state.tileSize, `rgba(255, 109, 109, ${state.collisionOpacity})`);
1388
+ drawMaskLayer(state.occlusionMask, state.occlusionCellSize, `rgba(86, 182, 255, ${state.occlusionOpacity})`);
1389
+ drawMaskLayer(state.occlusionHardMask, state.occlusionCellSize, `rgba(139, 107, 255, ${state.occlusionHardOpacity})`);
1390
+ drawMaskLayer(state.flameMask, state.flameCellSize, `rgba(255, 177, 95, ${state.flameOpacity})`);
1391
+
1392
+ if (state.showGrid) {
1393
+ drawGrid(state.tileSize, "rgba(233, 241, 224, 0.2)");
1394
+ if ((state.activeLayer === "occlusion" || state.activeLayer === "occlusion-hard") && state.occlusionCellSize < state.tileSize) {
1395
+ drawGrid(state.occlusionCellSize, "rgba(129, 207, 255, 0.18)");
1396
+ }
1397
+ if (state.activeLayer === "flame" && state.flameCellSize < state.tileSize) {
1398
+ drawGrid(state.flameCellSize, "rgba(255, 185, 118, 0.22)");
1399
+ }
1400
+ }
1401
+
1402
+ if (state.hoverInfo) {
1403
+ context.save();
1404
+ context.strokeStyle = "rgba(255, 236, 130, 0.95)";
1405
+ context.lineWidth = 2;
1406
+ context.strokeRect(
1407
+ state.hoverInfo.tileX * state.tileSize + 1,
1408
+ state.hoverInfo.tileY * state.tileSize + 1,
1409
+ state.tileSize - 2,
1410
+ state.tileSize - 2
1411
+ );
1412
+
1413
+ context.strokeStyle = "rgba(120, 210, 255, 0.95)";
1414
+ context.lineWidth = 1.5;
1415
+ context.strokeRect(
1416
+ state.hoverInfo.occlusionX * state.occlusionCellSize + 0.75,
1417
+ state.hoverInfo.occlusionY * state.occlusionCellSize + 0.75,
1418
+ state.occlusionCellSize - 1.5,
1419
+ state.occlusionCellSize - 1.5
1420
+ );
1421
+
1422
+ context.strokeStyle = "rgba(255, 177, 95, 0.95)";
1423
+ context.lineWidth = 1.5;
1424
+ context.strokeRect(
1425
+ state.hoverInfo.flameX * state.flameCellSize + 0.75,
1426
+ state.hoverInfo.flameY * state.flameCellSize + 0.75,
1427
+ state.flameCellSize - 1.5,
1428
+ state.flameCellSize - 1.5
1429
+ );
1430
+
1431
+ const brushRange = computeBrushCellRange(state.activeLayer, state.hoverInfo.canvasX, state.hoverInfo.canvasY);
1432
+ if (brushRange) {
1433
+ const brushWidth = (brushRange.endX - brushRange.startX + 1) * brushRange.cellSize;
1434
+ const brushHeight = (brushRange.endY - brushRange.startY + 1) * brushRange.cellSize;
1435
+ context.setLineDash([4, 3]);
1436
+ context.strokeStyle = "rgba(246, 249, 225, 0.95)";
1437
+ context.lineWidth = 1.6;
1438
+ context.strokeRect(
1439
+ brushRange.startX * brushRange.cellSize + 0.8,
1440
+ brushRange.startY * brushRange.cellSize + 0.8,
1441
+ Math.max(1, brushWidth - 1.6),
1442
+ Math.max(1, brushHeight - 1.6)
1443
+ );
1444
+ context.setLineDash([]);
1445
+ }
1446
+
1447
+ context.restore();
1448
+ }
1449
+
1450
+ updateHud();
1451
+ }
1452
+
1453
+ function drawPlaceholderBackground() {
1454
+ const tile = 32;
1455
+ for (let y = 0; y < canvas.height; y += tile) {
1456
+ for (let x = 0; x < canvas.width; x += tile) {
1457
+ const odd = ((x / tile) + (y / tile)) % 2 === 1;
1458
+ context.fillStyle = odd ? "#16231d" : "#1b2b24";
1459
+ context.fillRect(x, y, tile, tile);
1460
+ }
1461
+ }
1462
+ }
1463
+
1464
+ function drawMaskLayer(grid, cellSize, color) {
1465
+ context.save();
1466
+ context.fillStyle = color;
1467
+ for (let y = 0; y < grid.length; y += 1) {
1468
+ for (let x = 0; x < (grid[y]?.length ?? 0); x += 1) {
1469
+ if (!grid[y][x]) {
1470
+ continue;
1471
+ }
1472
+
1473
+ const drawX = x * cellSize;
1474
+ const drawY = y * cellSize;
1475
+ const drawWidth = Math.min(cellSize, canvas.width - drawX);
1476
+ const drawHeight = Math.min(cellSize, canvas.height - drawY);
1477
+ if (drawWidth <= 0 || drawHeight <= 0) {
1478
+ continue;
1479
+ }
1480
+
1481
+ context.fillRect(drawX, drawY, drawWidth, drawHeight);
1482
+ }
1483
+ }
1484
+ context.restore();
1485
+ }
1486
+
1487
+ function drawGrid(stepSize, strokeStyle) {
1488
+ context.save();
1489
+ context.strokeStyle = strokeStyle;
1490
+ context.lineWidth = 1;
1491
+ for (let x = 0; x <= canvas.width; x += stepSize) {
1492
+ const px = x + 0.5;
1493
+ context.beginPath();
1494
+ context.moveTo(px, 0);
1495
+ context.lineTo(px, canvas.height);
1496
+ context.stroke();
1497
+ }
1498
+
1499
+ for (let y = 0; y <= canvas.height; y += stepSize) {
1500
+ const py = y + 0.5;
1501
+ context.beginPath();
1502
+ context.moveTo(0, py);
1503
+ context.lineTo(canvas.width, py);
1504
+ context.stroke();
1505
+ }
1506
+ context.restore();
1507
+ }
1508
+
1509
+ function collectOcclusionTilesFromCells(maskGrid = state.occlusionMask) {
1510
+ const tiles = [];
1511
+ const seen = new Set();
1512
+
1513
+ for (let cellY = 0; cellY < maskGrid.length; cellY += 1) {
1514
+ for (let cellX = 0; cellX < (maskGrid[cellY]?.length ?? 0); cellX += 1) {
1515
+ if (!maskGrid[cellY][cellX]) {
1516
+ continue;
1517
+ }
1518
+
1519
+ const worldX = cellX * state.occlusionCellSize;
1520
+ const worldY = cellY * state.occlusionCellSize;
1521
+ const tileX = Math.floor(worldX / state.tileSize);
1522
+ const tileY = Math.floor(worldY / state.tileSize);
1523
+ if (tileX < 0 || tileY < 0 || tileX >= state.mapWidth || tileY >= state.mapHeight) {
1524
+ continue;
1525
+ }
1526
+
1527
+ const key = `${tileX},${tileY}`;
1528
+ if (seen.has(key)) {
1529
+ continue;
1530
+ }
1531
+
1532
+ seen.add(key);
1533
+ tiles.push([tileX, tileY]);
1534
+ }
1535
+ }
1536
+
1537
+ return tiles;
1538
+ }
1539
+
1540
+ function updateHud() {
1541
+ if (state.hoverInfo) {
1542
+ tileReadout.textContent = `Tile: ${state.hoverInfo.tileX},${state.hoverInfo.tileY} | O-cell: ${state.hoverInfo.occlusionX},${state.hoverInfo.occlusionY} | F-cell: ${state.hoverInfo.flameX},${state.hoverInfo.flameY}`;
1543
+ } else {
1544
+ tileReadout.textContent = "Tile: -";
1545
+ }
1546
+
1547
+ const collisionCount = countGridActive(state.collisionMask);
1548
+ const occlusionCount = countGridActive(state.occlusionMask);
1549
+ const occlusionHardCount = countGridActive(state.occlusionHardMask);
1550
+ const flameCount = countGridActive(state.flameMask);
1551
+ countReadout.textContent = `Collision: ${collisionCount} | Occlusion: ${occlusionCount} | Hard Occ: ${occlusionHardCount} | Flames: ${flameCount}`;
1552
+ }
1553
+
1554
+ function serializeLogic() {
1555
+ const occlusionCells = collectGridActive(state.occlusionMask);
1556
+ const occlusionHardCells = collectGridActive(state.occlusionHardMask);
1557
+ const ambientFlameCells = collectGridActive(state.flameMask);
1558
+ return {
1559
+ version: 1,
1560
+ width: state.mapWidth,
1561
+ height: state.mapHeight,
1562
+ tileSize: state.tileSize,
1563
+ backgroundImage: state.backgroundImageUrl,
1564
+ collisionTiles: collectGridActive(state.collisionMask),
1565
+ occlusionCellSize: state.occlusionCellSize,
1566
+ occlusionCells,
1567
+ occlusionTiles: collectOcclusionTilesFromCells(),
1568
+ occlusionHardCells,
1569
+ occlusionHardTiles: collectOcclusionTilesFromCells(state.occlusionHardMask),
1570
+ ambientFlameCellSize: state.flameCellSize,
1571
+ ambientFlameCells
1572
+ };
1573
+ }
1574
+
1575
+ function refreshJsonOutput() {
1576
+ jsonOutput.value = JSON.stringify(serializeLogic(), null, 2);
1577
+ }
1578
+
1579
+ function applySerializedLogic(serialized) {
1580
+ if (!serialized || typeof serialized !== "object") {
1581
+ throw new Error("Expected object at top level");
1582
+ }
1583
+
1584
+ const width = clampInt(serialized.width, 8, 200, state.mapWidth);
1585
+ const height = clampInt(serialized.height, 8, 200, state.mapHeight);
1586
+ const tileSize = clampInt(serialized.tileSize, 8, 128, state.tileSize);
1587
+ const hasOcclusionCells = Array.isArray(serialized.occlusionCells);
1588
+ const hasOcclusionHardCells = Array.isArray(serialized.occlusionHardCells);
1589
+ const defaultOcclusionCellSize = hasOcclusionCells || hasOcclusionHardCells ? state.occlusionCellSize : tileSize;
1590
+ const occlusionCellSize = clampOcclusionCellSize(serialized.occlusionCellSize, tileSize, defaultOcclusionCellSize);
1591
+ const hasFlameCells = Array.isArray(serialized.ambientFlameCells);
1592
+ const defaultFlameCellSize = hasFlameCells ? state.flameCellSize : Math.max(2, Math.min(tileSize, 16));
1593
+ const flameCellSize = clampOcclusionCellSize(serialized.ambientFlameCellSize, tileSize, defaultFlameCellSize);
1594
+
1595
+ state.mapWidth = width;
1596
+ state.mapHeight = height;
1597
+ state.tileSize = tileSize;
1598
+ state.occlusionCellSize = occlusionCellSize;
1599
+ state.flameCellSize = flameCellSize;
1600
+ mapWidthInput.value = String(width);
1601
+ mapHeightInput.value = String(height);
1602
+ tileSizeInput.value = String(tileSize);
1603
+ occlusionCellSizeInput.value = String(occlusionCellSize);
1604
+ flameCellSizeInput.value = String(flameCellSize);
1605
+
1606
+ state.collisionMask = createGrid(width, height);
1607
+ state.occlusionMask = createOcclusionGrid(width, height, tileSize, occlusionCellSize);
1608
+ state.occlusionHardMask = createOcclusionGrid(width, height, tileSize, occlusionCellSize);
1609
+ state.flameMask = createOcclusionGrid(width, height, tileSize, flameCellSize);
1610
+
1611
+ if (Array.isArray(serialized.collisionTiles)) {
1612
+ for (const entry of serialized.collisionTiles) {
1613
+ if (!Array.isArray(entry) || entry.length < 2) {
1614
+ continue;
1615
+ }
1616
+
1617
+ const x = clampInt(entry[0], 0, width - 1, -1);
1618
+ const y = clampInt(entry[1], 0, height - 1, -1);
1619
+ if (x >= 0 && y >= 0) {
1620
+ state.collisionMask[y][x] = true;
1621
+ }
1622
+ }
1623
+ }
1624
+
1625
+ if (hasOcclusionCells) {
1626
+ for (const entry of serialized.occlusionCells) {
1627
+ if (!Array.isArray(entry) || entry.length < 2) {
1628
+ continue;
1629
+ }
1630
+
1631
+ const x = clampInt(entry[0], 0, (state.occlusionMask[0]?.length ?? 1) - 1, -1);
1632
+ const y = clampInt(entry[1], 0, state.occlusionMask.length - 1, -1);
1633
+ if (x >= 0 && y >= 0) {
1634
+ state.occlusionMask[y][x] = true;
1635
+ }
1636
+ }
1637
+ } else if (Array.isArray(serialized.occlusionTiles)) {
1638
+ for (const entry of serialized.occlusionTiles) {
1639
+ if (!Array.isArray(entry) || entry.length < 2) {
1640
+ continue;
1641
+ }
1642
+
1643
+ const tileX = clampInt(entry[0], 0, width - 1, -1);
1644
+ const tileY = clampInt(entry[1], 0, height - 1, -1);
1645
+ if (tileX < 0 || tileY < 0) {
1646
+ continue;
1647
+ }
1648
+
1649
+ const left = tileX * tileSize;
1650
+ const top = tileY * tileSize;
1651
+ const right = left + tileSize;
1652
+ const bottom = top + tileSize;
1653
+ const minCellX = Math.floor(left / occlusionCellSize);
1654
+ const maxCellX = Math.floor((right - 0.001) / occlusionCellSize);
1655
+ const minCellY = Math.floor(top / occlusionCellSize);
1656
+ const maxCellY = Math.floor((bottom - 0.001) / occlusionCellSize);
1657
+
1658
+ for (let cellY = minCellY; cellY <= maxCellY; cellY += 1) {
1659
+ if (cellY < 0 || cellY >= state.occlusionMask.length) {
1660
+ continue;
1661
+ }
1662
+ for (let cellX = minCellX; cellX <= maxCellX; cellX += 1) {
1663
+ if (cellX < 0 || cellX >= state.occlusionMask[cellY].length) {
1664
+ continue;
1665
+ }
1666
+ state.occlusionMask[cellY][cellX] = true;
1667
+ }
1668
+ }
1669
+ }
1670
+ }
1671
+
1672
+ if (hasOcclusionHardCells) {
1673
+ for (const entry of serialized.occlusionHardCells) {
1674
+ if (!Array.isArray(entry) || entry.length < 2) {
1675
+ continue;
1676
+ }
1677
+
1678
+ const x = clampInt(entry[0], 0, (state.occlusionHardMask[0]?.length ?? 1) - 1, -1);
1679
+ const y = clampInt(entry[1], 0, state.occlusionHardMask.length - 1, -1);
1680
+ if (x >= 0 && y >= 0) {
1681
+ state.occlusionHardMask[y][x] = true;
1682
+ }
1683
+ }
1684
+ } else if (Array.isArray(serialized.occlusionHardTiles)) {
1685
+ for (const entry of serialized.occlusionHardTiles) {
1686
+ if (!Array.isArray(entry) || entry.length < 2) {
1687
+ continue;
1688
+ }
1689
+
1690
+ const tileX = clampInt(entry[0], 0, width - 1, -1);
1691
+ const tileY = clampInt(entry[1], 0, height - 1, -1);
1692
+ if (tileX < 0 || tileY < 0) {
1693
+ continue;
1694
+ }
1695
+
1696
+ const left = tileX * tileSize;
1697
+ const top = tileY * tileSize;
1698
+ const right = left + tileSize;
1699
+ const bottom = top + tileSize;
1700
+ const minCellX = Math.floor(left / occlusionCellSize);
1701
+ const maxCellX = Math.floor((right - 0.001) / occlusionCellSize);
1702
+ const minCellY = Math.floor(top / occlusionCellSize);
1703
+ const maxCellY = Math.floor((bottom - 0.001) / occlusionCellSize);
1704
+
1705
+ for (let cellY = minCellY; cellY <= maxCellY; cellY += 1) {
1706
+ if (cellY < 0 || cellY >= state.occlusionHardMask.length) {
1707
+ continue;
1708
+ }
1709
+ for (let cellX = minCellX; cellX <= maxCellX; cellX += 1) {
1710
+ if (cellX < 0 || cellX >= state.occlusionHardMask[cellY].length) {
1711
+ continue;
1712
+ }
1713
+ state.occlusionHardMask[cellY][cellX] = true;
1714
+ }
1715
+ }
1716
+ }
1717
+ }
1718
+
1719
+ if (hasFlameCells) {
1720
+ for (const entry of serialized.ambientFlameCells) {
1721
+ if (!Array.isArray(entry) || entry.length < 2) {
1722
+ continue;
1723
+ }
1724
+
1725
+ const x = clampInt(entry[0], 0, (state.flameMask[0]?.length ?? 1) - 1, -1);
1726
+ const y = clampInt(entry[1], 0, state.flameMask.length - 1, -1);
1727
+ if (x >= 0 && y >= 0) {
1728
+ state.flameMask[y][x] = true;
1729
+ }
1730
+ }
1731
+ }
1732
+
1733
+ if (typeof serialized.backgroundImage === "string" && serialized.backgroundImage.trim()) {
1734
+ const backgroundImage = serialized.backgroundImage.trim();
1735
+ state.backgroundImageUrl = backgroundImage;
1736
+ imageUrlInput.value = backgroundImage;
1737
+ }
1738
+
1739
+ updateBrushReadout();
1740
+ resizeCanvas();
1741
+ if (state.backgroundImageUrl) {
1742
+ void loadImageFromUrl(state.backgroundImageUrl);
1743
+ }
1744
+ }
1745
+
1746
+ function downloadTextFile(fileName, contents, mimeType) {
1747
+ const blob = new Blob([contents], { type: mimeType });
1748
+ const link = document.createElement("a");
1749
+ link.href = URL.createObjectURL(blob);
1750
+ link.download = fileName;
1751
+ document.body.appendChild(link);
1752
+ link.click();
1753
+ link.remove();
1754
+ URL.revokeObjectURL(link.href);
1755
+ }
1756
+
1757
+ async function downloadMaskPng(fileName, grid, cellSize, fillColor) {
1758
+ const offscreen = document.createElement("canvas");
1759
+ offscreen.width = state.mapWidth * state.tileSize;
1760
+ offscreen.height = state.mapHeight * state.tileSize;
1761
+
1762
+ const offscreenContext = offscreen.getContext("2d");
1763
+ offscreenContext.clearRect(0, 0, offscreen.width, offscreen.height);
1764
+ offscreenContext.fillStyle = fillColor;
1765
+
1766
+ for (let y = 0; y < grid.length; y += 1) {
1767
+ for (let x = 0; x < (grid[y]?.length ?? 0); x += 1) {
1768
+ if (!grid[y][x]) {
1769
+ continue;
1770
+ }
1771
+
1772
+ const drawX = x * cellSize;
1773
+ const drawY = y * cellSize;
1774
+ const drawWidth = Math.min(cellSize, offscreen.width - drawX);
1775
+ const drawHeight = Math.min(cellSize, offscreen.height - drawY);
1776
+ if (drawWidth <= 0 || drawHeight <= 0) {
1777
+ continue;
1778
+ }
1779
+
1780
+ offscreenContext.fillRect(drawX, drawY, drawWidth, drawHeight);
1781
+ }
1782
+ }
1783
+
1784
+ const blob = await new Promise((resolve) => offscreen.toBlob(resolve, "image/png"));
1785
+ if (!blob) {
1786
+ throw new Error("Mask export failed");
1787
+ }
1788
+
1789
+ const link = document.createElement("a");
1790
+ link.href = URL.createObjectURL(blob);
1791
+ link.download = fileName;
1792
+ document.body.appendChild(link);
1793
+ link.click();
1794
+ link.remove();
1795
+ URL.revokeObjectURL(link.href);
1796
+ }
1797
+
1798
+ function setStatus(message, kind) {
1799
+ imageStatus.textContent = message;
1800
+ imageStatus.classList.remove("ok", "error");
1801
+ if (kind) {
1802
+ imageStatus.classList.add(kind);
1803
+ }
1804
+ }
1805
+
1806
+ function isTypingTarget(target) {
1807
+ if (!(target instanceof HTMLElement)) {
1808
+ return false;
1809
+ }
1810
+
1811
+ const tagName = target.tagName.toLowerCase();
1812
+ return target.isContentEditable || tagName === "input" || tagName === "textarea" || tagName === "select";
1813
+ }
1814
+
1815
+ async function fetchJsonResource(url) {
1816
+ const response = await fetch(url, {
1817
+ cache: "no-store"
1818
+ });
1819
+
1820
+ if (!response.ok) {
1821
+ throw new Error(`${response.status} ${response.statusText}`.trim());
1822
+ }
1823
+
1824
+ return response.json();
1825
+ }
1826
+
1827
+ async function bootstrapEditor() {
1828
+ resizeCanvas();
1829
+ updateBrushReadout();
1830
+ draw();
1831
+
1832
+ const disableAutoLoad = urlParams.get("autoload") === "0";
1833
+ if (!disableAutoLoad) {
1834
+ try {
1835
+ const serialized = await fetchJsonResource(initialLogicUrl);
1836
+ applySerializedLogic(serialized);
1837
+ refreshJsonOutput();
1838
+ setStatus(`Loaded logic from ${initialLogicUrl}.`, "ok");
1839
+ return;
1840
+ } catch (error) {
1841
+ const message = error instanceof Error ? error.message : "Unknown error";
1842
+ setStatus(`Could not load ${initialLogicUrl}: ${message}. Using blank masks.`, "error");
1843
+ }
1844
+ }
1845
+
1846
+ refreshJsonOutput();
1847
+ await loadImageFromUrl(initialImageUrl);
1848
+ }
1849
+
1850
+ void bootstrapEditor();
1851
+ </script>
1852
+ </body>
1853
+ </html>