CharacterCreationState.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. import { Camera } from "../../libraries/Camera.js";
  2. import { Point } from "../../libraries/spatial/Point.js";
  3. import { SpritesheetAtlas } from "../../libraries/SpritesheetAtlas.js";
  4. import { TweenManager, Tween, Easing } from "../../libraries/Tween.js";
  5. import { Button } from "../../libraries/components/Button.js"
  6. import { AsyncDataWriter } from "../../libraries/AsyncDataWriter.js";
  7. import { AsyncDataReader } from "../../libraries/AsyncDataReader.js";
  8. export class CharacterCreationState {
  9. constructor(view) {
  10. this.stateMachine = view.stateMachine
  11. this.canvasBounds = null
  12. this.camera = new Camera()
  13. this.camera.scale = new Point(2, 2)
  14. this.tweenManager = new TweenManager()
  15. this.characterAtlas = view.characterAtlas
  16. this.registeredEvents = {}
  17. this.bodyType = 0;
  18. this.allBodies = [
  19. [0,0], [1,0],
  20. [0,1], [1,1],
  21. [0,2], [1,2],
  22. [0,3], [1,3],
  23. ]
  24. this.top = 0;
  25. this.allTops = [
  26. [6,0], [7,0], [8,0], [9,0], [10,0], [11,0], [12,0], [13,0], [14,0], [15,0], [16,0], [17,0],
  27. [6,1], [7,1], [8,1], [9,1], [10,1], [11,1], [12,1], [13,1], [14,1], [15,0], [16,0], [17,0],
  28. [6,2], [7,2], [8,2], [9,2], [10,2], [11,2], [12,2], [13,2], [14,2], [15,2], [16,2], [17,2],
  29. [6,3], [7,3], [8,3], [9,3], [10,3], [11,3], [12,3], [13,3], [14,3], [15,3], [16,3], [17,3],
  30. [6,4], [7,4], [8,4], [9,4], [10,4], [11,4], [12,4], [13,4], [14,4], [15,4], [16,4], [17,4],
  31. [6,5], [7,5], [8,5], [9,5], [10,5], [11,5], [12,5], [13,5], [14,5], [15,5], [16,5], [17,5],
  32. [6,6], [7,6], [8,6], [9,6], [10,6], [11,6], [12,6], [13,6], [14,6], [15,6], [16,6], [17,6],
  33. [6,7], [7,7], [8,7], [9,7], [10,7], [11,7], [12,7], [13,7], [14,7], [15,7], [16,7], [17,7],
  34. [6,8], [7,8], [8,8], [9,8], [10,8], [11,8], [12,8], [13,8], [14,8], [15,8], [16,8], [17,8],
  35. [6,9], [7,9], [8,9], [9,9], [10,9], [11,9], [12,9], [13,9], [14,9], [15,9], [16,9], [17,9],
  36. ]
  37. this.bottom = 0;
  38. this.allBottoms = [
  39. [3,0], [4,0],
  40. [3,1], [4,1],
  41. [3,2], [4,2],
  42. [3,3], [4,3],
  43. [3,4], [4,4],
  44. [3,5], [4,5],
  45. [3,6], [4,6],
  46. [3,7], [4,7],
  47. [3,8], [4,8],
  48. [3,9], [4,9],
  49. ]
  50. this.hair = 0;
  51. this.allHairs = [
  52. [19,0], [20,0], [21,0], [22,0], [23,0], [24,0], [25,0], [26,0],
  53. [19,1], [20,1], [21,1], [22,1], [23,1], [24,1], [25,1], [26,1],
  54. [19,2], [20,2], [21,2], [22,2], [23,2], [24,2], [25,2], [26,2],
  55. [19,3], [20,3], [21,3], [22,3], [23,3], [24,3], [25,3], [26,3],
  56. [19,4], [20,4], [21,4], [22,4], [23,4], [24,4], [25,4], [26,4],
  57. [19,5], [20,5], [21,5], [22,5], [23,5], [24,5], [25,5], [26,5],
  58. [19,6], [20,6], [21,6], [22,6], [23,6], [24,6], [25,6], [26,6],
  59. [19,7], [20,7], [21,7], [22,7], [23,7], [24,7], [25,7], [26,7],
  60. [19,8], [20,8], [21,8], [22,8], [23,8], [24,8], [25,8], [26,8],
  61. [19,9], [20,9], [21,9], [22,9], [23,9], [24,9], [25,9], [26,9],
  62. ]
  63. this.playerPosition = new Point(0,0);
  64. this.button = new Button(200, 600, 140, 60, "Start")
  65. this.button.clickFunction(this.goToGame.bind(this))
  66. this.optionButtons = [];
  67. this.optionButtons.push(new Button(30, 30, 60, 60, "1"))
  68. this.optionButtons.push(new Button(30, 100, 60, 60, "2"))
  69. this.optionButtons.push(new Button(30, 170, 60, 60, "3"))
  70. this.optionButtons.push(new Button(30, 240, 60, 60, "4"))
  71. this.optionButtons[0].clickFunction(() => { this.changeBody(1); })
  72. this.optionButtons[1].clickFunction(() => { this.changeHair(1); })
  73. this.optionButtons[2].clickFunction(() => { this.changeTop(1); })
  74. this.optionButtons[3].clickFunction(() => { this.changeBottom(1); })
  75. this.wasChanged = false
  76. }
  77. init(self) {
  78. let characterData = JSON.parse(localStorage.getItem('teetopia-player'))
  79. let body = this.allBodies[0]
  80. let wearing = this.allTops[0]
  81. let wearingBottom = this.allBottoms[0]
  82. let hr = this.allHairs[0]
  83. if(characterData) {
  84. body = characterData.bodySprite ?? this.allBodies[0]
  85. this.bodyType = this.allBodies.findIndex(b => b.toString() == body.toString()) ?? 0
  86. wearing = characterData.topSprite ?? this.allTops[0]
  87. this.top = this.allTops.findIndex(t => t.toString() == wearing.toString()) ?? 0
  88. wearingBottom = characterData.bottomSprite ?? this.allBottoms[0]
  89. this.bottom = this.allBottoms.findIndex(b => b.toString() == wearingBottom.toString()) ?? 0
  90. hr = characterData.hairSprite ?? this.allHairs[0]
  91. this.hair = this.allHairs.findIndex(h => h.toString() == hr.toString()) ?? 0
  92. }
  93. this.character = this.characterAtlas.getDrawableSprite(body[0], body[1])
  94. this.clothingTop = this.characterAtlas.getDrawableSprite(wearing[0], wearing[1])
  95. this.clothingBottom = this.characterAtlas.getDrawableSprite(wearingBottom[0], wearingBottom[1])
  96. this.characterHair = this.characterAtlas.getDrawableSprite(hr[0], hr[1])
  97. }
  98. draw(ctx, scaledCanvas) {
  99. this.canvasBounds = scaledCanvas.bounds;
  100. this.camera.draw(ctx, scaledCanvas, () => {
  101. ctx.save()
  102. ctx.translate(this.playerPosition.x, this.playerPosition.y)
  103. this.character.draw(ctx, scaledCanvas)
  104. this.clothingBottom.draw(ctx, scaledCanvas)
  105. this.clothingTop.draw(ctx, scaledCanvas)
  106. this.characterHair.draw(ctx, scaledCanvas)
  107. ctx.restore()
  108. })
  109. this.button.setPosition(this.canvasBounds.width /2, this.canvasBounds.height *(7/8))
  110. this.button.draw(ctx, scaledCanvas)
  111. this.optionButtons.forEach((button => {
  112. button.draw(ctx, scaledCanvas)
  113. }))
  114. }
  115. update(delta) {
  116. this.camera.update(delta);
  117. this.tweenManager.update()
  118. }
  119. enter() {
  120. this.init(this.stateMachine.getState("view"))
  121. this.registeredEvents["click"] = this.onClick
  122. this.registeredEvents["resize"] = this.onResize
  123. this.registeredEvents["keyup"] = this.onKeyUp
  124. this.registeredEvents["mousemove"] = this.onMouseMove
  125. this.registeredEvents["mousedown"] = this.onMouseDown
  126. for(let index in this.registeredEvents) {
  127. window.addEventListener(index, this.registeredEvents[index].bind(this))
  128. }
  129. this.openLoginWindow()
  130. }
  131. leave() {
  132. for(let index in this.registeredEvents) {
  133. window.removeEventListener(index, this.registeredEvents[index])
  134. }
  135. this.registeredEvents = {}
  136. this.tweenManager.clear()
  137. }
  138. onClick() {
  139. }
  140. onMouseMove(event) {
  141. document.body.style.cursor = "default"
  142. this.button.mouseMove(event)
  143. this.optionButtons.forEach((button) => {
  144. button.mouseMove(event)
  145. })
  146. }
  147. onMouseDown(event) {
  148. this.button.mouseDown(event)
  149. this.optionButtons.forEach((button) => {
  150. button.mouseDown(event)
  151. })
  152. }
  153. onFinish() {
  154. }
  155. onResize() {
  156. }
  157. onKeyUp(event) {
  158. switch(event.key) {
  159. case "!":
  160. this.changeBody(-1)
  161. break;
  162. case "1":
  163. this.changeBody(1)
  164. break;
  165. case "@":
  166. this.changeHair(-1)
  167. break;
  168. case "2":
  169. this.changeHair(1)
  170. break;
  171. case "#":
  172. this.changeTop(-1)
  173. break;
  174. case "3":
  175. this.changeTop(1)
  176. break;
  177. case "$":
  178. this.changeBottom(-1)
  179. break;
  180. case "4":
  181. this.changeBottom(1)
  182. break;
  183. case "E":
  184. let characterData = JSON.parse(localStorage.getItem('teetopia-player'))
  185. let roomFile = "room5"
  186. if (characterData) {
  187. roomFile = characterData.room ?? "room5"
  188. }
  189. window.location = `./editor.html?room=${roomFile}`
  190. break;
  191. }
  192. }
  193. changeBody(direction) {
  194. [this.bodyType, this.character] = this.spriteCycle(this.bodyType, direction, this.allBodies, this.characterAtlas)
  195. }
  196. changeHair(direction) {
  197. [this.hair, this.characterHair] = this.spriteCycle(this.hair, direction, this.allHairs, this.characterAtlas)
  198. }
  199. changeTop(direction) {
  200. [this.top, this.clothingTop] = this.spriteCycle(this.top, direction, this.allTops, this.characterAtlas)
  201. }
  202. changeBottom(direction) {
  203. [this.bottom, this.clothingBottom] = this.spriteCycle(this.bottom, direction, this.allBottoms, this.characterAtlas)
  204. }
  205. spriteCycle(tracker, direction, collection, atlas) {
  206. this.wasChanged = true
  207. tracker += direction
  208. if(tracker < 0) { tracker += collection.length}
  209. tracker %= collection.length
  210. let spritePosition = collection[tracker]
  211. return [tracker, atlas.getDrawableSprite(spritePosition[0], spritePosition[1])]
  212. }
  213. goToGame() {
  214. let characterData = JSON.parse(localStorage.getItem('teetopia-player'))
  215. if(!characterData || this.wasChanged) {
  216. characterData = {}
  217. characterData["bodySprite"] = this.allBodies[this.bodyType]
  218. characterData["topSprite"] = this.allTops[this.top]
  219. characterData["bottomSprite"] = this.allBottoms[this.bottom]
  220. characterData["hairSprite"] = this.allHairs[this.hair]
  221. }
  222. localStorage['teetopia-player'] = JSON.stringify(characterData)
  223. this.stateMachine.transitionTo("play")
  224. }
  225. async register(username, password, displayname) {
  226. let registerRequest = {
  227. email: username,
  228. password: password,
  229. displayname: displayname
  230. }
  231. let registerResponse = await new AsyncDataWriter().post("./api/login/register", registerRequest);
  232. if (registerResponse.responseType != 200) {
  233. throw new Error(registerResponse.responseMessage)
  234. }
  235. }
  236. async login(username, password) {
  237. let loginRequest = {
  238. username: username,
  239. password: password,
  240. }
  241. let postResponse = await new AsyncDataWriter().post("./api/login", loginRequest);
  242. if (postResponse.responseType != 200) {
  243. throw new Error(postResponse.responseMessage)
  244. }
  245. localStorage.setItem('teetopia-session', JSON.stringify(postResponse));
  246. }
  247. async openLoginWindow() {
  248. let sessionString = localStorage.getItem('teetopia-session');
  249. if(sessionString) {
  250. let sessionData = JSON.parse(sessionString)
  251. let response = await new AsyncDataWriter().post('./api/login/session', {token: sessionData.token})
  252. if(response.responseType == 200) {
  253. //TODO: load user data
  254. return
  255. }
  256. }
  257. let loginWindow = await new AsyncDataReader().readDOM('./html/login.html')
  258. window.document.body.appendChild(loginWindow)
  259. window.document.getElementById("login").addEventListener("click", async () => {
  260. try {
  261. await this.login(
  262. window.document.getElementById("email").value,
  263. window.document.getElementById("password").value,
  264. )
  265. window.document.body.removeChild(loginWindow)
  266. } catch (e) {
  267. window.document.getElementById("login-error").innerHTML = e.message
  268. }
  269. })
  270. window.document.getElementById("register").addEventListener("click", async () => {
  271. try {
  272. await this.register(
  273. window.document.getElementById("email").value,
  274. window.document.getElementById("password").value,
  275. window.document.getElementById("email").value,
  276. )
  277. await this.login(
  278. window.document.getElementById("email").value,
  279. window.document.getElementById("password").value,
  280. )
  281. window.document.body.removeChild(loginWindow)
  282. } catch (e) {
  283. window.document.getElementById("login-error").innerHTML = e.message
  284. }
  285. })
  286. window.document.getElementById("forgot").addEventListener("click", event => {
  287. window.document.body.removeChild(loginWindow)
  288. })
  289. Array.from(window.document.getElementsByClassName("login-close")).forEach(element => {
  290. element.addEventListener("click", event => {
  291. window.document.body.removeChild(loginWindow)
  292. })
  293. })
  294. }
  295. }