initial commit
This commit is contained in:
		@@ -4,10 +4,6 @@
 | 
				
			|||||||
    <meta charset="UTF-8" />
 | 
					    <meta charset="UTF-8" />
 | 
				
			||||||
    <link rel="icon" href="/favicon.ico" />
 | 
					    <link rel="icon" href="/favicon.ico" />
 | 
				
			||||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
					    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 | 
				
			||||||
    <link
 | 
					 | 
				
			||||||
      rel="stylesheet"
 | 
					 | 
				
			||||||
      href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.grey.min.css"
 | 
					 | 
				
			||||||
    />
 | 
					 | 
				
			||||||
    <title>XXX App</title>
 | 
					    <title>XXX App</title>
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										802
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										802
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -8,12 +8,14 @@
 | 
				
			|||||||
      "name": "domino-client",
 | 
					      "name": "domino-client",
 | 
				
			||||||
      "version": "0.0.0",
 | 
					      "version": "0.0.0",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "bulma": "^1.0.1",
 | 
				
			||||||
        "pinia": "^2.1.7",
 | 
					        "pinia": "^2.1.7",
 | 
				
			||||||
 | 
					        "pixi-filters": "^6.0.4",
 | 
				
			||||||
        "pixi.js": "^8.2.1",
 | 
					        "pixi.js": "^8.2.1",
 | 
				
			||||||
        "socket.io-client": "^4.7.5",
 | 
					        "socket.io-client": "^4.7.5",
 | 
				
			||||||
        "vue": "^3.4.29",
 | 
					        "vue": "^3.4.29",
 | 
				
			||||||
        "vue-router": "^4.3.3",
 | 
					        "vue-clipboard3": "^2.0.0",
 | 
				
			||||||
        "vue3-pixi": "^0.9.3"
 | 
					        "vue-router": "^4.3.3"
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "devDependencies": {
 | 
					      "devDependencies": {
 | 
				
			||||||
        "@rushstack/eslint-patch": "^1.8.0",
 | 
					        "@rushstack/eslint-patch": "^1.8.0",
 | 
				
			||||||
@@ -37,14 +39,6 @@
 | 
				
			|||||||
        "vue-tsc": "^2.0.21"
 | 
					        "vue-tsc": "^2.0.21"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@antfu/utils": {
 | 
					 | 
				
			||||||
      "version": "0.7.10",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==",
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@babel/parser": {
 | 
					    "node_modules/@babel/parser": {
 | 
				
			||||||
      "version": "7.24.7",
 | 
					      "version": "7.24.7",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.7.tgz",
 | 
				
			||||||
@@ -661,354 +655,11 @@
 | 
				
			|||||||
      "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
 | 
					      "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@pixi/accessibility": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/accessibility/-/accessibility-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-R6VEolm8uyy1FB1F2qaLKxVbzXAFTZCF2ka8fl9lsz7We6ZfO4QpXv9ur7DvzratjCQUQVCKo0/V7xL5q1EV/g==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/events": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/app": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/app/-/app-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-ugkH3kOgjT8P1mTMY29yCOgEh+KuVMAn8uBxeY0aMqaUgIMysfpnFv+Aepp2CtvI9ygr22NC+OiKl+u+eEaQHw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/assets": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/assets/-/assets-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-anxho59H9egZwoaEdM5aLvYyxoz6NCy3CaQIvNHD1bbGg8L16Ih0e26QSBR5fu53jl8OjT6M7s+p6n7uu4+fGA==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@types/css-font-loading-module": "^0.0.12"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/color": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/color/-/color-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-av1LOvhHsiaW8+T4n/FgnOKHby55/w7VcA1HzPIHRBtEcsmxvSCDanT1HU2LslNhrxLPzyVx18nlmalOyt5OBg==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@pixi/colord": "^2.9.6"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/colord": {
 | 
					    "node_modules/@pixi/colord": {
 | 
				
			||||||
      "version": "2.9.6",
 | 
					      "version": "2.9.6",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@pixi/colord/-/colord-2.9.6.tgz",
 | 
				
			||||||
      "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA=="
 | 
					      "integrity": "sha512-nezytU2pw587fQstUu1AsJZDVEynjskwOL+kibwcdxsMBFqPsFFNA7xl0ii/gXuDi6M0xj3mfRJj8pBSc2jCfA=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@pixi/compressed-textures": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/compressed-textures/-/compressed-textures-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-VJrt7el6O4ZJSWkeOGXwrhJaiLg1UBhHB3fj42VR4YloYkAxpfd9K6s6IcbcVz7n9L48APKBMgHyaB2pX2Ck/A==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/assets": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/constants": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/constants/-/constants-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-N9vn6Wpz5WIQg7ugUg2+SdqD2u2+NM0QthE8YzLJ4tLH2Iz+/TrnPKUJzeyIqbg3sxJG5ZpGGPiacqIBpy1KyA=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/core": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/core/-/core-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-UbMtgSEnyCOFPzbE6ThB9qopXxbZ5GCof2ArB4FXOC5Xi/83MOIIYg5kf5M8689C5HJMhg2SrJu3xLKppF+CMg==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@pixi/color": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/constants": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/extensions": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/math": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/runner": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/settings": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/ticker": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/utils": "7.4.2"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "type": "opencollective",
 | 
					 | 
				
			||||||
        "url": "https://opencollective.com/pixijs"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/display": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/display/-/display-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-DaD0J7gIlNlzO0Fdlby/0OH+tB5LtCY6rgFeCBKVDnzmn8wKW3zYZRenWBSFJ0Psx6vLqXYkSIM/rcokaKviIw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/events": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/events/-/events-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Jw/w57heZjzZShIXL0bxOvKB+XgGIevyezhGtfF2ZSzQoSBWo+Fj1uE0QwKd0RIaXegZw/DhSmiMJSbNmcjifA==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/extensions": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/extensions/-/extensions-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Hmx2+O0yZ8XIvgomHM9GZEGcy9S9Dd8flmtOK5Aa3fXs/8v7xD08+ANQpN9ZqWU2Xs+C6UBlpqlt2BWALvKKKA=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/extract": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/extract/-/extract-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-JOX27TRWjVEjauGBbF8PU7/g6LYXnivehdgqS5QlVDv1CNHTOrz/j3MdKcVWOhyZPbH5c9sh7lxyRxvd9AIuTQ==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/filter-alpha": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/filter-alpha/-/filter-alpha-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-9OsKJ+yvY2wIcQXwswj5HQBiwNGymwmqdxfp7mo+nZSBoDmxUqvMZzE9UNJ3eUlswuNvNRO8zNOsQvwdz7WFww==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/filter-blur": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/filter-blur/-/filter-blur-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-gOXBbIUx6CRZP1fmsis2wLzzSsofrqmIHhbf1gIkZMIQaLsc9T7brj+PaLTTiOiyJgnvGN5j20RZnkERWWKV0Q==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/filter-color-matrix": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/filter-color-matrix/-/filter-color-matrix-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-ykZiR59Gvj80UKs9qm7jeUTKvn+wWk6HBVJOmJbK9jFK5juakDWp7BbH26U78Q61EWj97kI1FdfcbMkuQ7rqkA==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/filter-displacement": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/filter-displacement/-/filter-displacement-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-QS/eWp/ivsxef3xapNeGwpPX7vrqQQeo99Fux4k5zsvplnNEsf91t6QYJLG776AbZEu/qh8VYRBA5raIVY/REw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/filter-fxaa": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/filter-fxaa/-/filter-fxaa-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-U/ptJgDsfs/r8y2a6gCaiPfDu2IFAxpQ4wtfmBpz6vRhqeE4kI8yNIUx5dZbui57zlsJaW0BNacOQxHU0vLkyQ==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/filter-noise": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/filter-noise/-/filter-noise-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Vy9ViBFhZEGh6xKkd3kFWErolZTwv1Y5Qb1bV7qPIYbvBECYsqzlR4uCrrjBV6KKm0PufpG/+NKC5vICZaqKzg==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/graphics": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/graphics/-/graphics-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-jH4/Tum2RqWzHGzvlwEr7HIVduoLO57Ze705N2zQPkUD57TInn5911aGUeoua7f/wK8cTLGzgB9BzSo2kTdcHw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/math": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/math/-/math-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-7jHmCQoYk6e0rfSKjdNFOPl0wCcdgoraxgteXJTTHv3r0bMNx2pHD9FJ0VvocEUG7XHfj55O3+u7yItOAx0JaQ=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/mesh": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/mesh/-/mesh-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-mEkKyQvvMrYXC3pahvH5WBIKtrtB63WixRr91ANFI7zXD+ESG6Ap6XtxMCJmXDQPwBDNk7SWVMiCflYuchG7kA==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/mesh-extras": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/mesh-extras/-/mesh-extras-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-vNR/7wjxjs7sv9fGoKkHyU91ZAD+7EnMHBS5F3CVISlOIFxLi96NNZCB81oUIdky/90pHw40johd/4izR5zTyw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/mesh": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/mixin-cache-as-bitmap": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/mixin-cache-as-bitmap/-/mixin-cache-as-bitmap-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-6dgthi2ruUT/lervSrFDQ7vXkEsHo6CxdgV7W/wNdW1dqgQlKfDvO6FhjXzyIMRLSooUf5FoeluVtfsjkUIYrw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/mixin-get-child-by-name": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/mixin-get-child-by-name/-/mixin-get-child-by-name-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-0Cfw8JpQhsixprxiYph4Lj+B5n83Kk4ftNMXgM5xtZz+tVLz5s91qR0MqcdzwTGTJ7utVygiGmS4/3EfR/duRQ==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/mixin-get-global-position": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/mixin-get-global-position/-/mixin-get-global-position-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-LcsahbVdX4DFS2IcGfNp4KaXuu7SjAwUp/flZSGIfstyKOKb5FWFgihtqcc9ZT4coyri3gs2JbILZub/zPZj1w==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/particle-container": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/particle-container/-/particle-container-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-B78Qq86kt0lEa5WtB2YFIm3+PjhKfw9La9R++GBSgABl+g13s2UaZ6BIPxvY3JxWMdxPm4iPrQPFX1QWRN68mw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/prepare": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/prepare/-/prepare-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-PugyMzReCHXUzc3so9PPJj2OdHwibpUNWyqG4mWY2UUkb6c8NAGK1AnAPiscOvLilJcv/XQSFoNhX+N1jrvJEg==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/graphics": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/text": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/runner": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/runner/-/runner-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-LPBpwym4vdyyDY5ucF4INQccaGyxztERyLTY1YN6aqJyyMmnc7iqXlIKt+a0euMBtNoLoxy6MWMvIuZj0JfFPA=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/settings": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/settings/-/settings-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-pMN+L6aWgvUbwhFIL/BTHKe2ShYGPZ8h9wlVBnFHMtUcJcFLMF1B3lzuvCayZRepOphs6RY0TqvnDvVb585JhQ==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@pixi/constants": "7.4.2",
 | 
					 | 
				
			||||||
        "@types/css-font-loading-module": "^0.0.12",
 | 
					 | 
				
			||||||
        "ismobilejs": "^1.1.0"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/sprite": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/sprite/-/sprite-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Ccf/OVQsB+HQV0Fyf5lwD+jk1jeU7uSIqEjbxenNNssmEdB7S5qlkTBV2EJTHT83+T6Z9OMOHsreJZerydpjeg==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/sprite-animated": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/sprite-animated/-/sprite-animated-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-QPT6yxCUGOBN+98H3pyIZ1ZO6Y7BN1o0Q2IMZEsD1rNfZJrTYS3Q8VlCG5t2YlFlcB8j5iBo24bZb6FUxLOmsQ==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/sprite-tiling": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/sprite-tiling/-/sprite-tiling-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Z8PP6ewy3nuDYL+NeEdltHAhuucVgia33uzAitvH3OqqRSx6a6YRBFbNLUM9Sx+fBO2Lk3PpV1g6QZX+NE5LOg==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/spritesheet": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/spritesheet/-/spritesheet-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-YIvHdpXW+AYp8vD0NkjJmrdnVHTZKidCnx6k8ATSuuvCT6O5Tuh2N/Ul2oDj4/QaePy0lVhyhAbZpJW00Jr7mQ==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/assets": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/text": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/text/-/text-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-rZZWpJNsIQ8WoCWrcVg8Gi6L/PDakB941clo6dO3XjoII2ucoOUcnpe5HIkudxi2xPvS/8Bfq990gFEx50TP5A==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/text-bitmap": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/text-bitmap/-/text-bitmap-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-lPBMJ83JnpFVL+6ckQ8KO8QmwdPm0z9Zs/M0NgFKH2F+BcjelRNnk80NI3O0qBDYSEDQIE+cFbKoZ213kf7zwA==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/assets": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/mesh": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/text": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/text-html": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/text-html/-/text-html-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-duOu8oDYeDNuyPozj2DAsQ5VZBbRiwIXy78Gn7H2pCiEAefw/Uv5jJYwdgneKME0e1tOxz1eOUGKPcI6IJnZjw==",
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/text": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/ticker": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/ticker/-/ticker-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-cAvxCh/KI6IW4m3tp2b+GQIf+DoSj9NNmPJmsOeEJ7LzvruG8Ps7SKI6CdjQob5WbceL1apBTDbqZ/f77hFDiQ==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@pixi/extensions": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/settings": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/utils": "7.4.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/utils": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pixi/utils/-/utils-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-aU/itcyMC4TxFbmdngmak6ey4kC5c16Y5ntIYob9QnjNAfD/7GTsYIBnP6FqEAyO1eq0MjkAALxdONuay1BG3g==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@pixi/color": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/constants": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/settings": "7.4.2",
 | 
					 | 
				
			||||||
        "@types/earcut": "^2.1.0",
 | 
					 | 
				
			||||||
        "earcut": "^2.2.4",
 | 
					 | 
				
			||||||
        "eventemitter3": "^4.0.0",
 | 
					 | 
				
			||||||
        "url": "^0.11.0"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pixi/utils/node_modules/eventemitter3": {
 | 
					 | 
				
			||||||
      "version": "4.0.7",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@pkgjs/parseargs": {
 | 
					    "node_modules/@pkgjs/parseargs": {
 | 
				
			||||||
      "version": "0.11.0",
 | 
					      "version": "0.11.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
 | 
				
			||||||
@@ -1278,6 +929,11 @@
 | 
				
			|||||||
      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
 | 
					      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@types/gradient-parser": {
 | 
				
			||||||
 | 
					      "version": "0.1.5",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@types/gradient-parser/-/gradient-parser-0.1.5.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-r7K3NkJz3A95WkVVmjs0NcchhHstC2C/VIYNX4JC6tieviUNo774FFeOHjThr3Vw/WCeMP9kAT77MKbIRlO/4w=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/@types/jsdom": {
 | 
					    "node_modules/@types/jsdom": {
 | 
				
			||||||
      "version": "21.1.7",
 | 
					      "version": "21.1.7",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.7.tgz",
 | 
				
			||||||
@@ -1304,11 +960,6 @@
 | 
				
			|||||||
      "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
 | 
					      "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@types/web-bluetooth": {
 | 
					 | 
				
			||||||
      "version": "0.0.20",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@typescript-eslint/eslint-plugin": {
 | 
					    "node_modules/@typescript-eslint/eslint-plugin": {
 | 
				
			||||||
      "version": "7.14.1",
 | 
					      "version": "7.14.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.14.1.tgz",
 | 
				
			||||||
@@ -1819,89 +1470,6 @@
 | 
				
			|||||||
      "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
 | 
					      "integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/@vueuse/core": {
 | 
					 | 
				
			||||||
      "version": "10.11.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@types/web-bluetooth": "^0.0.20",
 | 
					 | 
				
			||||||
        "@vueuse/metadata": "10.11.0",
 | 
					 | 
				
			||||||
        "@vueuse/shared": "10.11.0",
 | 
					 | 
				
			||||||
        "vue-demi": ">=0.14.8"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@vueuse/core/node_modules/vue-demi": {
 | 
					 | 
				
			||||||
      "version": "0.14.8",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
 | 
					 | 
				
			||||||
      "hasInstallScript": true,
 | 
					 | 
				
			||||||
      "bin": {
 | 
					 | 
				
			||||||
        "vue-demi-fix": "bin/vue-demi-fix.js",
 | 
					 | 
				
			||||||
        "vue-demi-switch": "bin/vue-demi-switch.js"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=12"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@vue/composition-api": "^1.0.0-rc.1",
 | 
					 | 
				
			||||||
        "vue": "^3.0.0-0 || ^2.6.0"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "peerDependenciesMeta": {
 | 
					 | 
				
			||||||
        "@vue/composition-api": {
 | 
					 | 
				
			||||||
          "optional": true
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@vueuse/metadata": {
 | 
					 | 
				
			||||||
      "version": "10.11.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==",
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@vueuse/shared": {
 | 
					 | 
				
			||||||
      "version": "10.11.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "vue-demi": ">=0.14.8"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@vueuse/shared/node_modules/vue-demi": {
 | 
					 | 
				
			||||||
      "version": "0.14.8",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
 | 
					 | 
				
			||||||
      "hasInstallScript": true,
 | 
					 | 
				
			||||||
      "bin": {
 | 
					 | 
				
			||||||
        "vue-demi-fix": "bin/vue-demi-fix.js",
 | 
					 | 
				
			||||||
        "vue-demi-switch": "bin/vue-demi-switch.js"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=12"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@vue/composition-api": "^1.0.0-rc.1",
 | 
					 | 
				
			||||||
        "vue": "^3.0.0-0 || ^2.6.0"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "peerDependenciesMeta": {
 | 
					 | 
				
			||||||
        "@vue/composition-api": {
 | 
					 | 
				
			||||||
          "optional": true
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/@webgpu/types": {
 | 
					    "node_modules/@webgpu/types": {
 | 
				
			||||||
      "version": "0.1.43",
 | 
					      "version": "0.1.43",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.43.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.43.tgz",
 | 
				
			||||||
@@ -2097,6 +1665,11 @@
 | 
				
			|||||||
        "node": ">=8"
 | 
					        "node": ">=8"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/bulma": {
 | 
				
			||||||
 | 
					      "version": "1.0.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/bulma/-/bulma-1.0.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-+xv/BIAEQakHkR0QVz+s+RjNqfC53Mx9ZYexyaFNFo9wx5i76HXArNdwW7bccyJxa5mgV/T5DcVGqsAB19nBJQ=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/cac": {
 | 
					    "node_modules/cac": {
 | 
				
			||||||
      "version": "6.7.14",
 | 
					      "version": "6.7.14",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
 | 
				
			||||||
@@ -2106,24 +1679,6 @@
 | 
				
			|||||||
        "node": ">=8"
 | 
					        "node": ">=8"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/call-bind": {
 | 
					 | 
				
			||||||
      "version": "1.0.7",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "es-define-property": "^1.0.0",
 | 
					 | 
				
			||||||
        "es-errors": "^1.3.0",
 | 
					 | 
				
			||||||
        "function-bind": "^1.1.2",
 | 
					 | 
				
			||||||
        "get-intrinsic": "^1.2.4",
 | 
					 | 
				
			||||||
        "set-function-length": "^1.2.1"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/callsites": {
 | 
					    "node_modules/callsites": {
 | 
				
			||||||
      "version": "3.1.0",
 | 
					      "version": "3.1.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
 | 
				
			||||||
@@ -2215,6 +1770,16 @@
 | 
				
			|||||||
        "node": ">= 6"
 | 
					        "node": ">= 6"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/clipboard": {
 | 
				
			||||||
 | 
					      "version": "2.0.11",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "good-listener": "^1.2.2",
 | 
				
			||||||
 | 
					        "select": "^1.1.2",
 | 
				
			||||||
 | 
					        "tiny-emitter": "^2.0.0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/color-convert": {
 | 
					    "node_modules/color-convert": {
 | 
				
			||||||
      "version": "2.0.1",
 | 
					      "version": "2.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 | 
				
			||||||
@@ -2390,22 +1955,6 @@
 | 
				
			|||||||
      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
 | 
					      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/define-data-property": {
 | 
					 | 
				
			||||||
      "version": "1.1.4",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "es-define-property": "^1.0.0",
 | 
					 | 
				
			||||||
        "es-errors": "^1.3.0",
 | 
					 | 
				
			||||||
        "gopd": "^1.0.1"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/delayed-stream": {
 | 
					    "node_modules/delayed-stream": {
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
 | 
				
			||||||
@@ -2415,6 +1964,11 @@
 | 
				
			|||||||
        "node": ">=0.4.0"
 | 
					        "node": ">=0.4.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/delegate": {
 | 
				
			||||||
 | 
					      "version": "3.2.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/diff-sequences": {
 | 
					    "node_modules/diff-sequences": {
 | 
				
			||||||
      "version": "29.6.3",
 | 
					      "version": "29.6.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
 | 
				
			||||||
@@ -2529,25 +2083,6 @@
 | 
				
			|||||||
        "url": "https://github.com/fb55/entities?sponsor=1"
 | 
					        "url": "https://github.com/fb55/entities?sponsor=1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/es-define-property": {
 | 
					 | 
				
			||||||
      "version": "1.0.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "get-intrinsic": "^1.2.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/es-errors": {
 | 
					 | 
				
			||||||
      "version": "1.3.0",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/esbuild": {
 | 
					    "node_modules/esbuild": {
 | 
				
			||||||
      "version": "0.21.5",
 | 
					      "version": "0.21.5",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
 | 
				
			||||||
@@ -3034,14 +2569,6 @@
 | 
				
			|||||||
        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
 | 
					        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/function-bind": {
 | 
					 | 
				
			||||||
      "version": "1.1.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/get-func-name": {
 | 
					    "node_modules/get-func-name": {
 | 
				
			||||||
      "version": "2.0.2",
 | 
					      "version": "2.0.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
 | 
				
			||||||
@@ -3051,24 +2578,6 @@
 | 
				
			|||||||
        "node": "*"
 | 
					        "node": "*"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/get-intrinsic": {
 | 
					 | 
				
			||||||
      "version": "1.2.4",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "es-errors": "^1.3.0",
 | 
					 | 
				
			||||||
        "function-bind": "^1.1.2",
 | 
					 | 
				
			||||||
        "has-proto": "^1.0.1",
 | 
					 | 
				
			||||||
        "has-symbols": "^1.0.3",
 | 
					 | 
				
			||||||
        "hasown": "^2.0.0"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/get-stream": {
 | 
					    "node_modules/get-stream": {
 | 
				
			||||||
      "version": "8.0.1",
 | 
					      "version": "8.0.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
 | 
				
			||||||
@@ -3151,15 +2660,12 @@
 | 
				
			|||||||
        "url": "https://github.com/sponsors/sindresorhus"
 | 
					        "url": "https://github.com/sponsors/sindresorhus"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/gopd": {
 | 
					    "node_modules/good-listener": {
 | 
				
			||||||
      "version": "1.0.1",
 | 
					      "version": "1.2.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
 | 
				
			||||||
      "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
 | 
					      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
        "get-intrinsic": "^1.1.3"
 | 
					        "delegate": "^3.1.2"
 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/graphemer": {
 | 
					    "node_modules/graphemer": {
 | 
				
			||||||
@@ -3177,50 +2683,6 @@
 | 
				
			|||||||
        "node": ">=8"
 | 
					        "node": ">=8"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/has-property-descriptors": {
 | 
					 | 
				
			||||||
      "version": "1.0.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "es-define-property": "^1.0.0"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/has-proto": {
 | 
					 | 
				
			||||||
      "version": "1.0.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/has-symbols": {
 | 
					 | 
				
			||||||
      "version": "1.0.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/hasown": {
 | 
					 | 
				
			||||||
      "version": "2.0.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "function-bind": "^1.1.2"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/he": {
 | 
					    "node_modules/he": {
 | 
				
			||||||
      "version": "1.2.0",
 | 
					      "version": "1.2.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
 | 
				
			||||||
@@ -3929,17 +3391,6 @@
 | 
				
			|||||||
      "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
 | 
					      "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/object-inspect": {
 | 
					 | 
				
			||||||
      "version": "1.13.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==",
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/once": {
 | 
					    "node_modules/once": {
 | 
				
			||||||
      "version": "1.4.0",
 | 
					      "version": "1.4.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
 | 
				
			||||||
@@ -4198,6 +3649,17 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/pixi-filters": {
 | 
				
			||||||
 | 
					      "version": "6.0.4",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/pixi-filters/-/pixi-filters-6.0.4.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-W9SjOTF4yo+v9t5YOBtsWhJoFLLRM6DsIk1C2YBNiQhdyX7J/5UIJtPlTMhZ7wQKoFUiyeUAaCzTEdmw/TVD6w==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@types/gradient-parser": "^0.1.2"
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "pixi.js": ">=8.0.0-0"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/pixi.js": {
 | 
					    "node_modules/pixi.js": {
 | 
				
			||||||
      "version": "8.2.1",
 | 
					      "version": "8.2.1",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.2.1.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-8.2.1.tgz",
 | 
				
			||||||
@@ -4348,20 +3810,6 @@
 | 
				
			|||||||
        "node": ">=6"
 | 
					        "node": ">=6"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/qs": {
 | 
					 | 
				
			||||||
      "version": "6.12.1",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "side-channel": "^1.0.6"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=0.6"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/querystringify": {
 | 
					    "node_modules/querystringify": {
 | 
				
			||||||
      "version": "2.2.0",
 | 
					      "version": "2.2.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
 | 
				
			||||||
@@ -4602,6 +4050,11 @@
 | 
				
			|||||||
        "node": ">=v12.22.7"
 | 
					        "node": ">=v12.22.7"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/select": {
 | 
				
			||||||
 | 
					      "version": "1.1.2",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/semver": {
 | 
					    "node_modules/semver": {
 | 
				
			||||||
      "version": "7.6.2",
 | 
					      "version": "7.6.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
 | 
				
			||||||
@@ -4614,22 +4067,6 @@
 | 
				
			|||||||
        "node": ">=10"
 | 
					        "node": ">=10"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/set-function-length": {
 | 
					 | 
				
			||||||
      "version": "1.2.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "define-data-property": "^1.1.4",
 | 
					 | 
				
			||||||
        "es-errors": "^1.3.0",
 | 
					 | 
				
			||||||
        "function-bind": "^1.1.2",
 | 
					 | 
				
			||||||
        "get-intrinsic": "^1.2.4",
 | 
					 | 
				
			||||||
        "gopd": "^1.0.1",
 | 
					 | 
				
			||||||
        "has-property-descriptors": "^1.0.2"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/shebang-command": {
 | 
					    "node_modules/shebang-command": {
 | 
				
			||||||
      "version": "2.0.0",
 | 
					      "version": "2.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 | 
				
			||||||
@@ -4660,23 +4097,6 @@
 | 
				
			|||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					        "url": "https://github.com/sponsors/ljharb"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/side-channel": {
 | 
					 | 
				
			||||||
      "version": "1.0.6",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "call-bind": "^1.0.7",
 | 
					 | 
				
			||||||
        "es-errors": "^1.3.0",
 | 
					 | 
				
			||||||
        "get-intrinsic": "^1.2.4",
 | 
					 | 
				
			||||||
        "object-inspect": "^1.13.1"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">= 0.4"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/ljharb"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/siginfo": {
 | 
					    "node_modules/siginfo": {
 | 
				
			||||||
      "version": "2.0.0",
 | 
					      "version": "2.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
 | 
				
			||||||
@@ -4916,6 +4336,11 @@
 | 
				
			|||||||
      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
 | 
					      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
 | 
				
			||||||
      "dev": true
 | 
					      "dev": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/tiny-emitter": {
 | 
				
			||||||
 | 
					      "version": "2.1.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/tinybench": {
 | 
					    "node_modules/tinybench": {
 | 
				
			||||||
      "version": "2.8.0",
 | 
					      "version": "2.8.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.8.0.tgz",
 | 
				
			||||||
@@ -5073,15 +4498,6 @@
 | 
				
			|||||||
        "punycode": "^2.1.0"
 | 
					        "punycode": "^2.1.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/url": {
 | 
					 | 
				
			||||||
      "version": "0.11.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "punycode": "^1.4.1",
 | 
					 | 
				
			||||||
        "qs": "^6.11.2"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/url-parse": {
 | 
					    "node_modules/url-parse": {
 | 
				
			||||||
      "version": "1.5.10",
 | 
					      "version": "1.5.10",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
 | 
				
			||||||
@@ -5092,11 +4508,6 @@
 | 
				
			|||||||
        "requires-port": "^1.0.0"
 | 
					        "requires-port": "^1.0.0"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/url/node_modules/punycode": {
 | 
					 | 
				
			||||||
      "version": "1.4.1",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/util-deprecate": {
 | 
					    "node_modules/util-deprecate": {
 | 
				
			||||||
      "version": "1.0.2",
 | 
					      "version": "1.0.2",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
 | 
				
			||||||
@@ -5271,6 +4682,14 @@
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/vue-clipboard3": {
 | 
				
			||||||
 | 
					      "version": "2.0.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==",
 | 
				
			||||||
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "clipboard": "^2.0.6"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/vue-component-type-helpers": {
 | 
					    "node_modules/vue-component-type-helpers": {
 | 
				
			||||||
      "version": "2.0.22",
 | 
					      "version": "2.0.22",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.0.22.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.0.22.tgz",
 | 
				
			||||||
@@ -5342,101 +4761,6 @@
 | 
				
			|||||||
        "typescript": "*"
 | 
					        "typescript": "*"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/vue3-pixi": {
 | 
					 | 
				
			||||||
      "version": "0.9.3",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/vue3-pixi/-/vue3-pixi-0.9.3.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-BSiCD92diniDP0T1tANynYwcujtKQlhzHFZWtPpOM/sVgPj8uIis/L8vNeMrp6TQjCsND5x1dI4RXBMW9WyLDw==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@antfu/utils": "^0.7.8",
 | 
					 | 
				
			||||||
        "@vueuse/core": "^10.10.0",
 | 
					 | 
				
			||||||
        "nanoid": "^4.0.2",
 | 
					 | 
				
			||||||
        "pixi.js": "^7.4.2",
 | 
					 | 
				
			||||||
        "vue-demi": "^0.14.8"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/vue3-pixi/node_modules/nanoid": {
 | 
					 | 
				
			||||||
      "version": "4.0.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==",
 | 
					 | 
				
			||||||
      "funding": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "type": "github",
 | 
					 | 
				
			||||||
          "url": "https://github.com/sponsors/ai"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "bin": {
 | 
					 | 
				
			||||||
        "nanoid": "bin/nanoid.js"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": "^14 || ^16 || >=18"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/vue3-pixi/node_modules/pixi.js": {
 | 
					 | 
				
			||||||
      "version": "7.4.2",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-7.4.2.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-TifqgHGNofO7UCEbdZJOpUu7dUnpu4YZ0o76kfCqxDa4RS8ITc9zjECCbtalmuNXkVhSEZmBKQvE7qhHMqw/xg==",
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "@pixi/accessibility": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/app": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/assets": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/compressed-textures": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/core": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/display": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/events": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/extensions": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/extract": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/filter-alpha": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/filter-blur": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/filter-color-matrix": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/filter-displacement": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/filter-fxaa": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/filter-noise": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/graphics": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/mesh": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/mesh-extras": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/mixin-cache-as-bitmap": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/mixin-get-child-by-name": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/mixin-get-global-position": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/particle-container": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/prepare": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite-animated": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/sprite-tiling": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/spritesheet": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/text": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/text-bitmap": "7.4.2",
 | 
					 | 
				
			||||||
        "@pixi/text-html": "7.4.2"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "type": "opencollective",
 | 
					 | 
				
			||||||
        "url": "https://opencollective.com/pixijs"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/vue3-pixi/node_modules/vue-demi": {
 | 
					 | 
				
			||||||
      "version": "0.14.8",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
 | 
					 | 
				
			||||||
      "hasInstallScript": true,
 | 
					 | 
				
			||||||
      "bin": {
 | 
					 | 
				
			||||||
        "vue-demi-fix": "bin/vue-demi-fix.js",
 | 
					 | 
				
			||||||
        "vue-demi-switch": "bin/vue-demi-switch.js"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": ">=12"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "funding": {
 | 
					 | 
				
			||||||
        "url": "https://github.com/sponsors/antfu"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "peerDependencies": {
 | 
					 | 
				
			||||||
        "@vue/composition-api": "^1.0.0-rc.1",
 | 
					 | 
				
			||||||
        "vue": "^3.0.0-0 || ^2.6.0"
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "peerDependenciesMeta": {
 | 
					 | 
				
			||||||
        "@vue/composition-api": {
 | 
					 | 
				
			||||||
          "optional": true
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/w3c-xmlserializer": {
 | 
					    "node_modules/w3c-xmlserializer": {
 | 
				
			||||||
      "version": "5.0.0",
 | 
					      "version": "5.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,12 +14,14 @@
 | 
				
			|||||||
    "format": "prettier --write src/"
 | 
					    "format": "prettier --write src/"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "bulma": "^1.0.1",
 | 
				
			||||||
    "pinia": "^2.1.7",
 | 
					    "pinia": "^2.1.7",
 | 
				
			||||||
 | 
					    "pixi-filters": "^6.0.4",
 | 
				
			||||||
    "pixi.js": "^8.2.1",
 | 
					    "pixi.js": "^8.2.1",
 | 
				
			||||||
    "socket.io-client": "^4.7.5",
 | 
					    "socket.io-client": "^4.7.5",
 | 
				
			||||||
    "vue": "^3.4.29",
 | 
					    "vue": "^3.4.29",
 | 
				
			||||||
    "vue-router": "^4.3.3",
 | 
					    "vue-clipboard3": "^2.0.0",
 | 
				
			||||||
    "vue3-pixi": "^0.9.3"
 | 
					    "vue-router": "^4.3.3"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
    "@rushstack/eslint-patch": "^1.8.0",
 | 
					    "@rushstack/eslint-patch": "^1.8.0",
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								public/images/circle.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/images/circle.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.1 KiB  | 
@@ -1,86 +1,9 @@
 | 
				
			|||||||
/* color palette from <https://github.com/vuejs/theme> */
 | 
					 | 
				
			||||||
:root {
 | 
					:root {
 | 
				
			||||||
  --vt-c-white: #ffffff;
 | 
					  /* bulma color variables */
 | 
				
			||||||
  --vt-c-white-soft: #f8f8f8;
 | 
					  --bulma-primary-h: 40deg;
 | 
				
			||||||
  --vt-c-white-mute: #f2f2f2;
 | 
					  --bulma-primary-s: 48%;
 | 
				
			||||||
 | 
					  --bulma-primary-l: 48%;
 | 
				
			||||||
  --vt-c-black: #181818;
 | 
					  --bulma-info-h: 168deg;
 | 
				
			||||||
  --vt-c-black-soft: #222222;
 | 
					  --bulma-info-s: 58%;
 | 
				
			||||||
  --vt-c-black-mute: #282828;
 | 
					  --bulma-info-l: 28%;
 | 
				
			||||||
 | 
					 | 
				
			||||||
  --vt-c-indigo: #2c3e50;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
 | 
					 | 
				
			||||||
  --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
 | 
					 | 
				
			||||||
  --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
 | 
					 | 
				
			||||||
  --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  --vt-c-text-light-1: var(--vt-c-indigo);
 | 
					 | 
				
			||||||
  --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
 | 
					 | 
				
			||||||
  --vt-c-text-dark-1: var(--vt-c-white);
 | 
					 | 
				
			||||||
  --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* semantic color variables for this project */
 | 
					 | 
				
			||||||
:root {
 | 
					 | 
				
			||||||
  --color-background: var(--vt-c-white);
 | 
					 | 
				
			||||||
  --color-background-soft: var(--vt-c-white-soft);
 | 
					 | 
				
			||||||
  --color-background-mute: var(--vt-c-white-mute);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  --color-border: var(--vt-c-divider-light-2);
 | 
					 | 
				
			||||||
  --color-border-hover: var(--vt-c-divider-light-1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  --color-heading: var(--vt-c-text-light-1);
 | 
					 | 
				
			||||||
  --color-text: var(--vt-c-text-light-1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  --section-gap: 160px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@media (prefers-color-scheme: dark) {
 | 
					 | 
				
			||||||
  :root {
 | 
					 | 
				
			||||||
    --color-background: var(--vt-c-black);
 | 
					 | 
				
			||||||
    --color-background-soft: var(--vt-c-black-soft);
 | 
					 | 
				
			||||||
    --color-background-mute: var(--vt-c-black-mute);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --color-border: var(--vt-c-divider-dark-2);
 | 
					 | 
				
			||||||
    --color-border-hover: var(--vt-c-divider-dark-1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    --color-heading: var(--vt-c-text-dark-1);
 | 
					 | 
				
			||||||
    --color-text: var(--vt-c-text-dark-2);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
*,
 | 
					 | 
				
			||||||
*::before,
 | 
					 | 
				
			||||||
*::after {
 | 
					 | 
				
			||||||
  box-sizing: border-box;
 | 
					 | 
				
			||||||
  margin: 0;
 | 
					 | 
				
			||||||
  font-weight: normal;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
body {
 | 
					 | 
				
			||||||
  min-height: 100vh;
 | 
					 | 
				
			||||||
  color: var(--color-text);
 | 
					 | 
				
			||||||
  background: var(--color-background);
 | 
					 | 
				
			||||||
  transition:
 | 
					 | 
				
			||||||
    color 0.5s,
 | 
					 | 
				
			||||||
    background-color 0.5s;
 | 
					 | 
				
			||||||
  line-height: 1.6;
 | 
					 | 
				
			||||||
  font-family:
 | 
					 | 
				
			||||||
    Inter,
 | 
					 | 
				
			||||||
    -apple-system,
 | 
					 | 
				
			||||||
    BlinkMacSystemFont,
 | 
					 | 
				
			||||||
    'Segoe UI',
 | 
					 | 
				
			||||||
    Roboto,
 | 
					 | 
				
			||||||
    Oxygen,
 | 
					 | 
				
			||||||
    Ubuntu,
 | 
					 | 
				
			||||||
    Cantarell,
 | 
					 | 
				
			||||||
    'Fira Sans',
 | 
					 | 
				
			||||||
    'Droid Sans',
 | 
					 | 
				
			||||||
    'Helvetica Neue',
 | 
					 | 
				
			||||||
    sans-serif;
 | 
					 | 
				
			||||||
  font-size: 15px;
 | 
					 | 
				
			||||||
  text-rendering: optimizeLegibility;
 | 
					 | 
				
			||||||
  -webkit-font-smoothing: antialiased;
 | 
					 | 
				
			||||||
  -moz-osx-font-smoothing: grayscale;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								src/assets/images/circle.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/assets/images/circle.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 3.1 KiB  | 
@@ -1,33 +1 @@
 | 
				
			|||||||
/* @import './base.css';
 | 
					@import './base.css';
 | 
				
			||||||
 | 
					 | 
				
			||||||
#app {
 | 
					 | 
				
			||||||
  max-width: 1280px;
 | 
					 | 
				
			||||||
  margin: 0 auto;
 | 
					 | 
				
			||||||
  padding: 2rem;
 | 
					 | 
				
			||||||
  font-weight: normal;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
a,
 | 
					 | 
				
			||||||
.green {
 | 
					 | 
				
			||||||
  text-decoration: none;
 | 
					 | 
				
			||||||
  color: hsla(160, 100%, 37%, 1);
 | 
					 | 
				
			||||||
  transition: 0.4s;
 | 
					 | 
				
			||||||
  padding: 3px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@media (hover: hover) {
 | 
					 | 
				
			||||||
  a:hover {
 | 
					 | 
				
			||||||
    background-color: hsla(160, 100%, 37%, 0.2);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@media (min-width: 1024px) {
 | 
					 | 
				
			||||||
  body {
 | 
					 | 
				
			||||||
    display: flex;
 | 
					 | 
				
			||||||
    place-items: center;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  #app {
 | 
					 | 
				
			||||||
    padding: 0 2rem;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
} */
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,120 +1,92 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import type { GameState, PlayerState } from '@/utilities/interfaces'
 | 
					import type { GameSessionState, GameState, PlayerState } from '@/utilities/interfaces'
 | 
				
			||||||
import { onMounted, ref, type PropType } from 'vue'
 | 
					import { onMounted, onUnmounted, ref, watch } from 'vue'
 | 
				
			||||||
import { assets } from '@/utilities/assets'
 | 
					import { Game } from '@/utilities/Game'
 | 
				
			||||||
 | 
					import { useGameStore } from '@/stores/game'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Assets, Application as PixiApplication, Container, Sprite, Texture } from 'pixi.js'
 | 
					const emit = defineEmits(['move'])
 | 
				
			||||||
import { Scale } from '@/utilities/scale'
 | 
					const props = defineProps({
 | 
				
			||||||
import Hand from '@/utilities/Hand'
 | 
					  playerId: String
 | 
				
			||||||
import { playerState as mockPS } from '@/utilities/mocks'
 | 
					 | 
				
			||||||
import { Board } from '@/utilities/Board'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const emit = defineEmits(['tileClick', 'passClick', 'leftClick', 'rightClick'])
 | 
					 | 
				
			||||||
defineProps({
 | 
					 | 
				
			||||||
  gameState: Object as PropType<GameState>,
 | 
					 | 
				
			||||||
  playerState: Object as PropType<PlayerState>
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
// console.log('d1 :>> ', d1)
 | 
					 | 
				
			||||||
// console.log('t1 :>> ', t1)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// const tile = new Sprite(t1)
 | 
					const gameStore = useGameStore()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let appEl = ref<HTMLElement | null>(null)
 | 
					let appEl = ref<HTMLElement | null>(null)
 | 
				
			||||||
const app: PixiApplication = new PixiApplication()
 | 
					const game = new Game(
 | 
				
			||||||
let hand: Hand
 | 
					  {
 | 
				
			||||||
let board: Board
 | 
					    width: 1200,
 | 
				
			||||||
 | 
					    height: 650,
 | 
				
			||||||
 | 
					    boardScale: 0.8,
 | 
				
			||||||
 | 
					    handScale: 1
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  emit,
 | 
				
			||||||
 | 
					  props
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const scaleX = Scale([-16, 16], [0, 800])
 | 
					watch(
 | 
				
			||||||
const scaleY = Scale([-12, 12], [0, 600])
 | 
					  () => gameStore.canMakeMove,
 | 
				
			||||||
 | 
					  (value: boolean) => {
 | 
				
			||||||
 | 
					    game.setCanMakeMove(value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					  () => gameStore.gameState,
 | 
				
			||||||
 | 
					  (value: GameState | undefined) => {
 | 
				
			||||||
 | 
					    if (value === undefined) return
 | 
				
			||||||
 | 
					    game.board.setState(value, props.playerId ?? '')
 | 
				
			||||||
 | 
					    // console.log('gameState-------------------------------------- :>> ', value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					  () => gameStore.sessionState,
 | 
				
			||||||
 | 
					  (value: GameSessionState | undefined) => {
 | 
				
			||||||
 | 
					    if (value === undefined) return
 | 
				
			||||||
 | 
					    // console.log('gameSessionState-------------------------------------- :>> ', value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(
 | 
				
			||||||
 | 
					  () => gameStore.playerState,
 | 
				
			||||||
 | 
					  (value: PlayerState | undefined) => {
 | 
				
			||||||
 | 
					    if (value === undefined) return
 | 
				
			||||||
 | 
					    game.hand.update(value as PlayerState)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {
 | 
				
			||||||
  await setup()
 | 
					  if (appEl.value === null) return
 | 
				
			||||||
  await preload()
 | 
					  const canvas = await game.setup()
 | 
				
			||||||
 | 
					  appEl.value.appendChild(canvas)
 | 
				
			||||||
 | 
					  await game.preload()
 | 
				
			||||||
 | 
					  await game.start()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  await start()
 | 
					  // mockMove(game, [6, 6], 'left')
 | 
				
			||||||
  // console.log('appEl :>> ', appEl.value)
 | 
					  // mockMove(game, [6, 4], 'left')
 | 
				
			||||||
  // console.log('mounted')
 | 
					  // mockMove(game, [4, 4], 'left')
 | 
				
			||||||
  // if (appEl.value === null) return
 | 
					  // mockMove(game, [4, 0], 'left')
 | 
				
			||||||
 | 
					  // mockMove(game, [0, 0], 'left')
 | 
				
			||||||
  // console.log('App initialized')
 | 
					  // mockMove(game, [2, 0], 'left')
 | 
				
			||||||
  // console.log('loading assets')
 | 
					  // mockMove(game, [2, 2], 'left')
 | 
				
			||||||
  // const t1 = await Assets.load([d1])
 | 
					  // mockMove(game, [6, 5], 'right')
 | 
				
			||||||
  // console.log(`${d1} loaded`)
 | 
					  // mockMove(game, [5, 5], 'right')
 | 
				
			||||||
  // console.log('assets loaded')
 | 
					  // mockMove(game, [5, 1], 'right')
 | 
				
			||||||
  // const tile = new Sprite(t1)
 | 
					  // mockMove(game, [3, 1], 'right')
 | 
				
			||||||
  // tile.anchor.set(0.5)
 | 
					  // mockMove(game, [3, 0], 'right')
 | 
				
			||||||
  // tile.width = 50
 | 
					  // mockMove(game, [5, 0], 'right')
 | 
				
			||||||
  // tile.height = 100
 | 
					 | 
				
			||||||
  // tile.x = app.screen.width / 2
 | 
					 | 
				
			||||||
  // tile.y = app.screen.height / 2
 | 
					 | 
				
			||||||
  // app.stage.addChild(tile)
 | 
					 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function start() {
 | 
					onUnmounted(() => {
 | 
				
			||||||
  console.log('start')
 | 
					  game.destroy()
 | 
				
			||||||
  console.log('scaleX(0) :>> ', scaleX(0))
 | 
					})
 | 
				
			||||||
  console.log('scaleY(0) :>> ', scaleY(0))
 | 
					 | 
				
			||||||
  const boardContainer: Container = new Container()
 | 
					 | 
				
			||||||
  const handContainer: Container = new Container()
 | 
					 | 
				
			||||||
  app.stage.addChild(boardContainer)
 | 
					 | 
				
			||||||
  app.stage.addChild(handContainer)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  hand = new Hand(handContainer, app.canvas)
 | 
					 | 
				
			||||||
  board = new Board(boardContainer, app.canvas)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  hand.setTiles(mockPS as PlayerState)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  hand.on('tileClick', (tile) => {
 | 
					 | 
				
			||||||
    emit('tileClick', tile)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  hand.on('passClick', () => {
 | 
					 | 
				
			||||||
    emit('passClick')
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  hand.on('leftClick', () => {
 | 
					 | 
				
			||||||
    emit('tileClick')
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  hand.on('rightClick', () => {
 | 
					 | 
				
			||||||
    emit('rightClick')
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  app.ticker.add((time) => {
 | 
					 | 
				
			||||||
    // hand.update(time)
 | 
					 | 
				
			||||||
    // board.update(time)
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function setup() {
 | 
					 | 
				
			||||||
  await app.init({ width: 800, height: 600 })
 | 
					 | 
				
			||||||
  if (appEl.value === null) return
 | 
					 | 
				
			||||||
  appEl.value.appendChild(app.canvas)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function preload() {
 | 
					 | 
				
			||||||
  console.log('loading assets')
 | 
					 | 
				
			||||||
  await Assets.load(assets)
 | 
					 | 
				
			||||||
  console.log('assets loaded')
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div ref="appEl" class="game-container"></div>
 | 
					  <div ref="appEl" class="game-app"></div>
 | 
				
			||||||
  <!-- <Application :width="800" :height="600">
 | 
					 | 
				
			||||||
    <Loader :resources="resources">
 | 
					 | 
				
			||||||
      <template #fallback="{ progress }">
 | 
					 | 
				
			||||||
        <text :x="120" :y="120" :anchor="0.5">
 | 
					 | 
				
			||||||
          {{ `Loading... ${progress}` }}
 | 
					 | 
				
			||||||
        </text>
 | 
					 | 
				
			||||||
      </template>
 | 
					 | 
				
			||||||
      <template #default="{}"> </template>
 | 
					 | 
				
			||||||
    </Loader>
 | 
					 | 
				
			||||||
  </Application> -->
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
.game-container {
 | 
					.game-app {
 | 
				
			||||||
  position: relative;
 | 
					  position: relative;
 | 
				
			||||||
  z-index: 10;
 | 
					  z-index: 10;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,6 @@ function getPipClasses(value: number | undefined): string[] {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return positions[value]
 | 
					  return positions[value]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
console.log('props.tile :>> ', props.tile)
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,7 @@ function logout() {
 | 
				
			|||||||
  <div class="authenticated-layout">
 | 
					  <div class="authenticated-layout">
 | 
				
			||||||
    <header>
 | 
					    <header>
 | 
				
			||||||
      <nav>
 | 
					      <nav>
 | 
				
			||||||
        <button @click="logout">Logout</button>
 | 
					        <!-- <button @click="logout">Logout</button> -->
 | 
				
			||||||
      </nav>
 | 
					      </nav>
 | 
				
			||||||
    </header>
 | 
					    </header>
 | 
				
			||||||
    <main>
 | 
					    <main>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,16 +3,18 @@ import './assets/main.css'
 | 
				
			|||||||
import { createApp } from 'vue'
 | 
					import { createApp } from 'vue'
 | 
				
			||||||
import { createPinia } from 'pinia'
 | 
					import { createPinia } from 'pinia'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import '../node_modules/bulma/css/bulma.css'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import App from './App.vue'
 | 
					import App from './App.vue'
 | 
				
			||||||
import router from './router'
 | 
					import router from './router'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import socketService from '@/services/socket'
 | 
					import { SocketIoClientService } from '@/services/SocketIoClientService'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const app = createApp(App)
 | 
					const app = createApp(App)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.use(createPinia())
 | 
					app.use(createPinia())
 | 
				
			||||||
app.use(router)
 | 
					app.use(router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.provide('socket', socketService)
 | 
					app.provide('socket', new SocketIoClientService('http://localhost:3000'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
app.mount('#app')
 | 
					app.mount('#app')
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								src/managers/SocketIoEventManager.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/managers/SocketIoEventManager.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					import { useGameStore } from '@/stores/game'
 | 
				
			||||||
 | 
					import { wait } from '@/utilities/helpers'
 | 
				
			||||||
 | 
					import type { GameSessionState } from '@/utilities/interfaces'
 | 
				
			||||||
 | 
					import { storeToRefs } from 'pinia'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class SocketIoEventManager {
 | 
				
			||||||
 | 
					  gameStore: any = useGameStore()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleSessionStateEvent(data: GameSessionState) {
 | 
				
			||||||
 | 
					    const { updateSessionState } = this.gameStore
 | 
				
			||||||
 | 
					    updateSessionState(data)
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      status: 'ok'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleGameStateEvent(data: any) {
 | 
				
			||||||
 | 
					    const { updateGameState } = this.gameStore
 | 
				
			||||||
 | 
					    updateGameState(data)
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      status: 'ok'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handlePlayerStateEvent(data: any) {
 | 
				
			||||||
 | 
					    const { updatePlayerState } = this.gameStore
 | 
				
			||||||
 | 
					    updatePlayerState(data)
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      status: 'ok'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async handleCanMakeMoveEvent(data: any) {
 | 
				
			||||||
 | 
					    const { canMakeMove, moveToMake } = storeToRefs(this.gameStore)
 | 
				
			||||||
 | 
					    const { updateCanMakeMove, setIncomingFreeEnds } = this.gameStore
 | 
				
			||||||
 | 
					    setIncomingFreeEnds(data.freeHands)
 | 
				
			||||||
 | 
					    updateCanMakeMove(true)
 | 
				
			||||||
 | 
					    while (canMakeMove.value) {
 | 
				
			||||||
 | 
					      await wait(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      status: 'ok',
 | 
				
			||||||
 | 
					      ...moveToMake.value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async handleCanSelectTileEvent() {
 | 
				
			||||||
 | 
					    const { canSelectTile } = storeToRefs(this.gameStore)
 | 
				
			||||||
 | 
					    const { updateCanSelectTile } = this.gameStore
 | 
				
			||||||
 | 
					    updateCanSelectTile(true)
 | 
				
			||||||
 | 
					    while (canSelectTile.value) {
 | 
				
			||||||
 | 
					      await wait(500)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      status: 'ok'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										82
									
								
								src/services/SocketIoClientService.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/services/SocketIoClientService.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					import type { GameSessionState, GameState, PlayerState } from '@/utilities/interfaces'
 | 
				
			||||||
 | 
					import { io, Socket } from 'socket.io-client'
 | 
				
			||||||
 | 
					import { SocketIoEventManager } from '@/managers/SocketIoEventManager'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class SocketIoClientService {
 | 
				
			||||||
 | 
					  public socket: Socket
 | 
				
			||||||
 | 
					  private isConnected = false
 | 
				
			||||||
 | 
					  private gameEventManager = new SocketIoEventManager()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(url: string) {
 | 
				
			||||||
 | 
					    this.socket = io(url)
 | 
				
			||||||
 | 
					    this.addEvents()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addEvents(): void {
 | 
				
			||||||
 | 
					    this.socket.on('connect', () => {
 | 
				
			||||||
 | 
					      this.isConnected = true
 | 
				
			||||||
 | 
					      if (this.socket && this.socket.recovered) {
 | 
				
			||||||
 | 
					        console.log('socket recovered succesfully')
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        console.log('socket connected')
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('disconnect', () => {
 | 
				
			||||||
 | 
					      this.isConnected = false
 | 
				
			||||||
 | 
					      console.log('Disconnected from server')
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('reconnect', () => {
 | 
				
			||||||
 | 
					      this.isConnected = true
 | 
				
			||||||
 | 
					      console.log('Reconnected to server')
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('reconnect_error', () => {
 | 
				
			||||||
 | 
					      this.isConnected = false
 | 
				
			||||||
 | 
					      console.log('Failed to reconnect to server')
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('sessionState', (data: GameSessionState, callback: any) => {
 | 
				
			||||||
 | 
					      callback(this.gameEventManager.handleSessionStateEvent(data))
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('gameState', (data: GameState, callback: any) => {
 | 
				
			||||||
 | 
					      callback(this.gameEventManager.handleGameStateEvent(data))
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('playerState', (data: PlayerState, callback: any) => {
 | 
				
			||||||
 | 
					      callback(this.gameEventManager.handlePlayerStateEvent(data))
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('makeMove', async (data: any, callback: any) => {
 | 
				
			||||||
 | 
					      callback(await this.gameEventManager.handleCanMakeMoveEvent(data))
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.socket.on('chooseTile', async (data: any, callback: any) => {
 | 
				
			||||||
 | 
					      callback(await this.gameEventManager.handleCanSelectTileEvent())
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sendMessage(event: string, data: any): void {
 | 
				
			||||||
 | 
					    if (this.isConnected) {
 | 
				
			||||||
 | 
					      this.socket?.emit(event, data)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.log('Not connected to server')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async sendMessageWithAck(event: string, data: any): Promise<any> {
 | 
				
			||||||
 | 
					    if (this.isConnected) {
 | 
				
			||||||
 | 
					      return await this.socket?.emitWithAck(event, data)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.log('Not connected to server')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  disconnect(): void {
 | 
				
			||||||
 | 
					    this.socket?.disconnect()
 | 
				
			||||||
 | 
					    this.isConnected = false
 | 
				
			||||||
 | 
					    console.log('Disconnected from server')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,17 +3,32 @@ import { io } from 'socket.io-client'
 | 
				
			|||||||
const URL = 'http://localhost:3000'
 | 
					const URL = 'http://localhost:3000'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
process.env.NODE_ENV === 'production' ? undefined : 'http://localhost:3001'
 | 
					process.env.NODE_ENV === 'production' ? undefined : 'http://localhost:3001'
 | 
				
			||||||
console.log('URL :>> ', URL)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const socket = URL === undefined ? io() : io(URL)
 | 
					const ioOpts = {
 | 
				
			||||||
 | 
					  // timeout: 300 * 1000,
 | 
				
			||||||
 | 
					  // reconnectionDelay: 5000 // defaults to 1000
 | 
				
			||||||
 | 
					  // reconnectionDelayMax: 10000 // defaults to 5000reconnection: true, // Enable reconnection
 | 
				
			||||||
 | 
					  reconnectionAttempts: Infinity, // Maximum number of reconnection attempts
 | 
				
			||||||
 | 
					  reconnectionDelay: 1000, // Time between each attempt (in ms)
 | 
				
			||||||
 | 
					  reconnectionDelayMax: 5000, // Maximum time between attempts (in ms)
 | 
				
			||||||
 | 
					  randomizationFactor: 0.5, // Randomization factor for the delay
 | 
				
			||||||
 | 
					  timeout: 20000 // Connection timeout (in ms)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					// const socket = URL === undefined ? io(ioOpts) : io(URL, ioOpts)
 | 
				
			||||||
 | 
					const socket = io('http://localhost:3000', ioOpts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
socket.on('connect', () => {
 | 
					socket.on('connect', () => {
 | 
				
			||||||
  console.log('---> connected')
 | 
					  if (socket.recovered) {
 | 
				
			||||||
 | 
					    console.log('socket recovered succesfully')
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    console.log('socket connected')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // setTimeout(() => {
 | 
				
			||||||
 | 
					  //   socket.io.engine.close()
 | 
				
			||||||
 | 
					  // }, 60 * 1000)
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
socket.onAny((event, msg) => {
 | 
					
 | 
				
			||||||
  console.log('event :>> ', event)
 | 
					 | 
				
			||||||
  console.log('msg :>> ', msg)
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
console.log('help :>> ')
 | 
					 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  socket,
 | 
					  socket,
 | 
				
			||||||
  sendMessage(event: any, data: any) {
 | 
					  sendMessage(event: any, data: any) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										58
									
								
								src/stores/game.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/stores/game.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					import { ref } from 'vue'
 | 
				
			||||||
 | 
					import { defineStore } from 'pinia'
 | 
				
			||||||
 | 
					import type { GameSessionState, GameState, Movement, PlayerState } from '@/utilities/interfaces'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const useGameStore = defineStore('game', () => {
 | 
				
			||||||
 | 
					  const sessionState = ref<GameSessionState | undefined>(undefined)
 | 
				
			||||||
 | 
					  const gameState = ref<GameState | undefined>(undefined)
 | 
				
			||||||
 | 
					  const playerState = ref<PlayerState | undefined>(undefined)
 | 
				
			||||||
 | 
					  const canMakeMove = ref(false)
 | 
				
			||||||
 | 
					  const canSelectTile = ref(false)
 | 
				
			||||||
 | 
					  const moveToMake = ref<Movement | undefined>(undefined)
 | 
				
			||||||
 | 
					  const incomingFreeEnds = ref<[number, number] | undefined>(undefined)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function updateSessionState(newState: GameSessionState) {
 | 
				
			||||||
 | 
					    sessionState.value = newState
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function updateGameState(newState: GameState) {
 | 
				
			||||||
 | 
					    gameState.value = newState
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function updatePlayerState(newState: PlayerState) {
 | 
				
			||||||
 | 
					    playerState.value = newState
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function updateCanMakeMove(value: boolean) {
 | 
				
			||||||
 | 
					    canMakeMove.value = value
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function setMoveToMake(move: Movement) {
 | 
				
			||||||
 | 
					    moveToMake.value = move
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function updateCanSelectTile(value: boolean) {
 | 
				
			||||||
 | 
					    canSelectTile.value = value
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function setIncomingFreeEnds(freeEnds: [number, number]) {
 | 
				
			||||||
 | 
					    incomingFreeEnds.value = freeEnds
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    sessionState,
 | 
				
			||||||
 | 
					    gameState,
 | 
				
			||||||
 | 
					    playerState,
 | 
				
			||||||
 | 
					    canMakeMove,
 | 
				
			||||||
 | 
					    moveToMake,
 | 
				
			||||||
 | 
					    incomingFreeEnds,
 | 
				
			||||||
 | 
					    canSelectTile,
 | 
				
			||||||
 | 
					    updateSessionState,
 | 
				
			||||||
 | 
					    updateGameState,
 | 
				
			||||||
 | 
					    updatePlayerState,
 | 
				
			||||||
 | 
					    updateCanMakeMove,
 | 
				
			||||||
 | 
					    setMoveToMake,
 | 
				
			||||||
 | 
					    setIncomingFreeEnds,
 | 
				
			||||||
 | 
					    updateCanSelectTile
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
@@ -1,143 +1,556 @@
 | 
				
			|||||||
import { Container, EventEmitter, Graphics, Text } from 'pixi.js'
 | 
					import { Application, Container, EventEmitter, Graphics, Text, Ticker } from 'pixi.js'
 | 
				
			||||||
import { Scale } from './scale'
 | 
					import { Scale, type ScaleFunction } from './scale'
 | 
				
			||||||
import type { GameState } from './interfaces'
 | 
					import type { GameState, Movement, TileDto } from './interfaces'
 | 
				
			||||||
import type { Tile } from './Tile'
 | 
					import { Tile } from './Tile'
 | 
				
			||||||
import { getColorBackground } from './backgrounds'
 | 
					import { DIRECTIONS, createContainer, isTilePair, isTileVertical } from './helpers'
 | 
				
			||||||
 | 
					import { createText } from './fonts'
 | 
				
			||||||
 | 
					import { Dot } from './Dot'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Board extends EventEmitter {
 | 
					export class Board extends EventEmitter {
 | 
				
			||||||
  container: Container
 | 
					  private _scale: number = 1
 | 
				
			||||||
 | 
					  private _canMove: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ticker: Ticker
 | 
				
			||||||
  height: number
 | 
					  height: number
 | 
				
			||||||
  width: number
 | 
					  width: number
 | 
				
			||||||
  scaleY: Function
 | 
					  grain: number = 25
 | 
				
			||||||
  scaleX: Function
 | 
					  scaleY: ScaleFunction
 | 
				
			||||||
 | 
					  scaleX: ScaleFunction
 | 
				
			||||||
  state: GameState | undefined
 | 
					  state: GameState | undefined
 | 
				
			||||||
  initialContainer: Container = new Container()
 | 
					  container!: Container
 | 
				
			||||||
  mainContainer: Container = new Container()
 | 
					  initialContainer!: Container
 | 
				
			||||||
  leftContainer: Container = new Container()
 | 
					  tilesContainer!: Container
 | 
				
			||||||
  rightContainer: Container = new Container()
 | 
					  leftContainer!: Container
 | 
				
			||||||
 | 
					  rightContainer!: Container
 | 
				
			||||||
 | 
					  interactionContainer!: Container
 | 
				
			||||||
 | 
					  textContainer!: Container
 | 
				
			||||||
  tiles: Tile[] = []
 | 
					  tiles: Tile[] = []
 | 
				
			||||||
  boneyard: Tile[] = []
 | 
					  boneyard: Tile[] = []
 | 
				
			||||||
 | 
					  movements: any[] = []
 | 
				
			||||||
 | 
					  freeEnds?: [number, number]
 | 
				
			||||||
 | 
					  leftTile?: Tile
 | 
				
			||||||
 | 
					  rightTile?: Tile
 | 
				
			||||||
 | 
					  nextTile?: Tile
 | 
				
			||||||
 | 
					  textWaitForPlayers!: Text
 | 
				
			||||||
 | 
					  textYourTurn!: Text
 | 
				
			||||||
 | 
					  leftDirection: string = 'west'
 | 
				
			||||||
 | 
					  rightDirection: string = 'east'
 | 
				
			||||||
 | 
					  playerHand: Tile[] = []
 | 
				
			||||||
 | 
					  firstTile: Tile | undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(container: Container, canvas: HTMLCanvasElement) {
 | 
					  constructor(app: Application) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.container = container
 | 
					    this.ticker = app.ticker
 | 
				
			||||||
    this.height = canvas.height - 130
 | 
					    this.height = app.canvas.height - 130
 | 
				
			||||||
    this.width = canvas.width
 | 
					    this.width = app.canvas.width
 | 
				
			||||||
    this.container.width = this.width
 | 
					    this.scaleX = Scale([0, this.width], [0, this.width])
 | 
				
			||||||
    this.container.height = this.height
 | 
					    this.scaleY = Scale([0, this.height], [0, this.height])
 | 
				
			||||||
    this.container.y = 0
 | 
					    this.calculateScale()
 | 
				
			||||||
    const scaleXSteps = Math.floor(this.width / 25)
 | 
					
 | 
				
			||||||
    const scaleYSteps = Math.floor(this.height / 25)
 | 
					    this.container = createContainer({
 | 
				
			||||||
 | 
					      width: this.width,
 | 
				
			||||||
 | 
					      height: this.height,
 | 
				
			||||||
 | 
					      parent: app.stage
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.initialContainer = createContainer({
 | 
				
			||||||
 | 
					      width: this.width,
 | 
				
			||||||
 | 
					      height: this.height,
 | 
				
			||||||
 | 
					      color: 0x1e2f23,
 | 
				
			||||||
 | 
					      visible: false,
 | 
				
			||||||
 | 
					      parent: this.container
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.tilesContainer = createContainer({
 | 
				
			||||||
 | 
					      width: this.width,
 | 
				
			||||||
 | 
					      height: this.height,
 | 
				
			||||||
 | 
					      color: 0x1e2f23,
 | 
				
			||||||
 | 
					      parent: this.container
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.interactionContainer = createContainer({
 | 
				
			||||||
 | 
					      width: this.width,
 | 
				
			||||||
 | 
					      height: this.height,
 | 
				
			||||||
 | 
					      parent: this.container,
 | 
				
			||||||
 | 
					      visible: false
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const verticalLine = new Graphics()
 | 
				
			||||||
 | 
					      .moveTo(this.scaleX(0), 0)
 | 
				
			||||||
 | 
					      .lineTo(this.scaleX(0), this.height)
 | 
				
			||||||
 | 
					      .stroke(0xff0000)
 | 
				
			||||||
 | 
					    const horizontalLine = new Graphics()
 | 
				
			||||||
 | 
					      .moveTo(0, this.scaleY(0))
 | 
				
			||||||
 | 
					      .lineTo(this.width, this.scaleY(0))
 | 
				
			||||||
 | 
					      .stroke(0xff0000)
 | 
				
			||||||
 | 
					    verticalLine.alpha = 0.2
 | 
				
			||||||
 | 
					    horizontalLine.alpha = 0.2
 | 
				
			||||||
 | 
					    this.tilesContainer.addChild(verticalLine)
 | 
				
			||||||
 | 
					    this.tilesContainer.addChild(horizontalLine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.createTexts()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private calculateScale() {
 | 
				
			||||||
 | 
					    const scaleXSteps = Math.floor(this.width / (this.grain * this.scale)) / 2
 | 
				
			||||||
 | 
					    const scaleYSteps = Math.floor(this.height / (this.grain * this.scale)) / 2
 | 
				
			||||||
    this.scaleX = Scale([-scaleXSteps, scaleXSteps], [0, this.width])
 | 
					    this.scaleX = Scale([-scaleXSteps, scaleXSteps], [0, this.width])
 | 
				
			||||||
    this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
 | 
					    this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.initialContainer = new Container()
 | 
					 | 
				
			||||||
    this.initialContainer.addChild(
 | 
					 | 
				
			||||||
      new Text({
 | 
					 | 
				
			||||||
        text: 'Initial Board',
 | 
					 | 
				
			||||||
        style: {
 | 
					 | 
				
			||||||
          fontFamily: 'Arial',
 | 
					 | 
				
			||||||
          fontSize: 12,
 | 
					 | 
				
			||||||
          fill: 0xff1010,
 | 
					 | 
				
			||||||
          align: 'center'
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    this.container.addChild(this.initialContainer)
 | 
					 | 
				
			||||||
    this.mainContainer = new Container()
 | 
					 | 
				
			||||||
    this.mainContainer.addChild(
 | 
					 | 
				
			||||||
      new Text({
 | 
					 | 
				
			||||||
        text: 'Main Board',
 | 
					 | 
				
			||||||
        style: {
 | 
					 | 
				
			||||||
          fontFamily: 'Arial',
 | 
					 | 
				
			||||||
          fontSize: 12,
 | 
					 | 
				
			||||||
          fill: 0xff1010,
 | 
					 | 
				
			||||||
          align: 'center'
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    this.container.addChild(this.mainContainer)
 | 
					 | 
				
			||||||
    this.createLeftRightContainers()
 | 
					 | 
				
			||||||
    this.initialContainer.visible = false
 | 
					 | 
				
			||||||
    this.mainContainer.visible = false
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private createLeftRightContainers() {
 | 
					  get count() {
 | 
				
			||||||
    console.log('this.height :>> ', this.height)
 | 
					    return this.tiles.length
 | 
				
			||||||
    this.leftContainer.x = 0
 | 
					 | 
				
			||||||
    this.leftContainer.y = 0
 | 
					 | 
				
			||||||
    this.leftContainer.width = 50
 | 
					 | 
				
			||||||
    this.leftContainer.height = this.height
 | 
					 | 
				
			||||||
    this.leftContainer.eventMode = 'static'
 | 
					 | 
				
			||||||
    this.leftContainer.cursor = 'pointer'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const leftSide = new Graphics()
 | 
					 | 
				
			||||||
      .rect(0, 0, 50, this.height)
 | 
					 | 
				
			||||||
      // Draw the wheel
 | 
					 | 
				
			||||||
      .fill({ color: 0x848484 })
 | 
					 | 
				
			||||||
    leftSide.alpha = 0
 | 
					 | 
				
			||||||
    this.leftContainer.addChild(leftSide)
 | 
					 | 
				
			||||||
    this.leftContainer.on('pointerover', () => {
 | 
					 | 
				
			||||||
      leftSide.alpha = 0.5
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    this.leftContainer.on('pointerout', () => {
 | 
					 | 
				
			||||||
      leftSide.alpha = 0
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    this.leftContainer.on('pointerdown', () => {
 | 
					 | 
				
			||||||
      this.emit('leftClick')
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.leftContainer.addChild(
 | 
					 | 
				
			||||||
      new Text({
 | 
					 | 
				
			||||||
        text: 'Left',
 | 
					 | 
				
			||||||
        style: {
 | 
					 | 
				
			||||||
          fontFamily: 'Arial',
 | 
					 | 
				
			||||||
          fontSize: 12,
 | 
					 | 
				
			||||||
          fill: 0xff1010,
 | 
					 | 
				
			||||||
          align: 'center'
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    this.rightContainer.x = this.width - 50
 | 
					 | 
				
			||||||
    this.rightContainer.y = 0
 | 
					 | 
				
			||||||
    this.rightContainer.width = 50
 | 
					 | 
				
			||||||
    this.rightContainer.height = this.height
 | 
					 | 
				
			||||||
    this.rightContainer.eventMode = 'static'
 | 
					 | 
				
			||||||
    this.rightContainer.cursor = 'pointer'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const rightSide = new Graphics()
 | 
					 | 
				
			||||||
      .rect(0, 0, 50, this.height)
 | 
					 | 
				
			||||||
      // Draw the wheel
 | 
					 | 
				
			||||||
      .fill({ color: 0x848484 })
 | 
					 | 
				
			||||||
    rightSide.alpha = 0
 | 
					 | 
				
			||||||
    this.rightContainer.addChild(rightSide)
 | 
					 | 
				
			||||||
    this.rightContainer.on('pointerover', () => {
 | 
					 | 
				
			||||||
      rightSide.alpha = 0.5
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    this.rightContainer.on('pointerout', () => {
 | 
					 | 
				
			||||||
      rightSide.alpha = 0
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    this.rightContainer.on('pointerdown', () => {
 | 
					 | 
				
			||||||
      this.emit('rightClick')
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    this.rightContainer.addChild(
 | 
					 | 
				
			||||||
      new Text({
 | 
					 | 
				
			||||||
        text: 'Right',
 | 
					 | 
				
			||||||
        style: {
 | 
					 | 
				
			||||||
          fontFamily: 'Arial',
 | 
					 | 
				
			||||||
          fontSize: 12,
 | 
					 | 
				
			||||||
          fill: 0xff1010,
 | 
					 | 
				
			||||||
          align: 'center'
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    this.container.addChild(this.leftContainer)
 | 
					 | 
				
			||||||
    this.container.addChild(this.rightContainer)
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setState(state: GameState) {
 | 
					  get scale() {
 | 
				
			||||||
 | 
					    return this._scale
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set scale(value: number) {
 | 
				
			||||||
 | 
					    this._scale = value
 | 
				
			||||||
 | 
					    this.calculateScale()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get canMove() {
 | 
				
			||||||
 | 
					    return this._canMove
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set canMove(value: boolean) {
 | 
				
			||||||
 | 
					    this._canMove = value
 | 
				
			||||||
 | 
					    this.updateCanMoveText()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setPlayerHand(tiles: Tile[]) {
 | 
				
			||||||
 | 
					    this.playerHand = tiles
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createTexts() {
 | 
				
			||||||
 | 
					    this.textContainer = new Container()
 | 
				
			||||||
 | 
					    this.textWaitForPlayers = createText('Waiting for players', this.scaleX(0), 100)
 | 
				
			||||||
 | 
					    this.textYourTurn = createText('Your turn!', this.scaleX(0), 100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.container.addChild(this.textContainer)
 | 
				
			||||||
 | 
					    this.textContainer.addChild(this.textWaitForPlayers)
 | 
				
			||||||
 | 
					    this.textContainer.addChild(this.textYourTurn)
 | 
				
			||||||
 | 
					    this.textYourTurn.visible = false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private updateCanMoveText() {
 | 
				
			||||||
 | 
					    this.textWaitForPlayers.visible = !this.canMove
 | 
				
			||||||
 | 
					    this.textYourTurn.visible = this.canMove
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setState(state: GameState, playerId: string) {
 | 
				
			||||||
    this.state = state
 | 
					    this.state = state
 | 
				
			||||||
 | 
					    const { lastMove } = state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (lastMove === null) {
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (
 | 
				
			||||||
 | 
					      lastMove !== null &&
 | 
				
			||||||
 | 
					      lastMove.tile !== undefined &&
 | 
				
			||||||
 | 
					      lastMove.tile.pips !== undefined &&
 | 
				
			||||||
 | 
					      lastMove.playerId !== playerId
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					      const tile = new Tile(lastMove.tile.id, this.ticker, lastMove.tile.pips, this.scale)
 | 
				
			||||||
 | 
					      this.nextTile = tile
 | 
				
			||||||
 | 
					      lastMove.tile = tile.toPlain()
 | 
				
			||||||
 | 
					      this.movements.push(lastMove)
 | 
				
			||||||
 | 
					      this.addTile(tile, lastMove)
 | 
				
			||||||
 | 
					      this.setFreeEnd(lastMove)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getOrientationII(tile: TileDto, side: string) {
 | 
				
			||||||
 | 
					    let orientation = ''
 | 
				
			||||||
 | 
					    const isPair = isTilePair(tile)
 | 
				
			||||||
 | 
					    if (side === 'left') {
 | 
				
			||||||
 | 
					      if (this.freeEnds !== undefined && tile.pips !== undefined) {
 | 
				
			||||||
 | 
					        const isInverted = this.freeEnds[0] === tile.pips[1]
 | 
				
			||||||
 | 
					        if (this.leftDirection === 'east') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'east' : 'west'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'north'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (this.leftDirection === 'west') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'west' : 'east'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'north'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (this.leftDirection === 'north') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'north' : 'south'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'west'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (this.leftDirection === 'south') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'south' : 'north'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'west'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (side === 'right') {
 | 
				
			||||||
 | 
					      if (this.freeEnds !== undefined && tile.pips !== undefined) {
 | 
				
			||||||
 | 
					        const isInverted = this.freeEnds[1] === tile.pips[0]
 | 
				
			||||||
 | 
					        if (this.rightDirection === 'east') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'west' : 'east'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'north'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (this.rightDirection === 'west') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'east' : 'west'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'north'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (this.rightDirection === 'north') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'south' : 'north'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'west'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else if (this.rightDirection === 'south') {
 | 
				
			||||||
 | 
					          if (!isPair) {
 | 
				
			||||||
 | 
					            orientation = isInverted ? 'north' : 'south'
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            orientation = 'west'
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return orientation
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addTile(tile: Tile, move: Movement) {
 | 
				
			||||||
 | 
					    let orientation = ''
 | 
				
			||||||
 | 
					    let x: number =
 | 
				
			||||||
 | 
					      move.type === 'left'
 | 
				
			||||||
 | 
					        ? this.scaleX.inverse(this.leftTile?.x ?? 0)
 | 
				
			||||||
 | 
					        : this.scaleX.inverse(this.rightTile?.x ?? 0)
 | 
				
			||||||
 | 
					    let y: number =
 | 
				
			||||||
 | 
					      move.type === 'left'
 | 
				
			||||||
 | 
					        ? this.scaleY.inverse(this.leftTile?.y ?? 0)
 | 
				
			||||||
 | 
					        : this.scaleY.inverse(this.rightTile?.y ?? 0)
 | 
				
			||||||
 | 
					    const isLeft = move.type === 'left'
 | 
				
			||||||
 | 
					    const tileDto = tile.toPlain()
 | 
				
			||||||
 | 
					    let direction = move.type === 'left' ? this.leftDirection : this.rightDirection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.tiles.length === 0) {
 | 
				
			||||||
 | 
					      x = 0
 | 
				
			||||||
 | 
					      y = 0
 | 
				
			||||||
 | 
					      if (tile.isPair()) {
 | 
				
			||||||
 | 
					        orientation = 'north'
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        orientation = 'east'
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.firstTile = tile
 | 
				
			||||||
 | 
					    } else if (move.direction !== undefined) {
 | 
				
			||||||
 | 
					      direction = move.direction
 | 
				
			||||||
 | 
					      const availableMoves = this.nextTileValidMoves(tileDto, move.type)
 | 
				
			||||||
 | 
					      const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
 | 
				
			||||||
 | 
					      const directionIndex = DIRECTIONS.indexOf(direction)
 | 
				
			||||||
 | 
					      isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
 | 
				
			||||||
 | 
					      const availablePosition: [number, number] | undefined = availablePositions[directionIndex]
 | 
				
			||||||
 | 
					      orientation = this.getOrientationII(tileDto, move.type)
 | 
				
			||||||
 | 
					      availablePosition && ([x, y] = availablePosition)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (this.tiles.length === 0) {
 | 
				
			||||||
 | 
					        x = 0
 | 
				
			||||||
 | 
					        y = 0
 | 
				
			||||||
 | 
					        if (tile.isPair()) {
 | 
				
			||||||
 | 
					          tile.setOrientation('north')
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          tile.setOrientation('east')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        const availableMoves = this.nextTileValidMoves(tileDto, move.type)
 | 
				
			||||||
 | 
					        const availablePositions = this.nextTileValidPoints(tileDto, move.type, availableMoves)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let directionIndex = DIRECTIONS.indexOf(direction)
 | 
				
			||||||
 | 
					        let availablePosition: [number, number] | undefined = availablePositions[directionIndex]
 | 
				
			||||||
 | 
					        let endlessLoop: number = 0
 | 
				
			||||||
 | 
					        while (endlessLoop < 4 && availablePosition === undefined) {
 | 
				
			||||||
 | 
					          directionIndex = (directionIndex + 1) % 4
 | 
				
			||||||
 | 
					          availablePosition = availablePositions[directionIndex]
 | 
				
			||||||
 | 
					          endlessLoop++
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (endlessLoop >= 4) {
 | 
				
			||||||
 | 
					          throw new Error('No available position')
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        direction = DIRECTIONS[directionIndex]
 | 
				
			||||||
 | 
					        isLeft ? (this.leftDirection = direction) : (this.rightDirection = direction)
 | 
				
			||||||
 | 
					        orientation = this.getOrientationII(tileDto, move.type)
 | 
				
			||||||
 | 
					        availablePosition && ([x, y] = availablePosition)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const endTile = isLeft ? this.leftTile : this.rightTile
 | 
				
			||||||
 | 
					    const isEndVertical = endTile?.isVertical() ?? false
 | 
				
			||||||
 | 
					    const isNextVertical = orientation === 'north' || orientation === 'south'
 | 
				
			||||||
 | 
					    if (this.tiles.length > 0 && endTile !== undefined) {
 | 
				
			||||||
 | 
					      //!tile.equals(endTile)
 | 
				
			||||||
 | 
					      if (direction === 'east') {
 | 
				
			||||||
 | 
					        x += !isEndVertical && isNextVertical ? 0 : 1
 | 
				
			||||||
 | 
					      } else if (direction === 'west') {
 | 
				
			||||||
 | 
					        x -= !isEndVertical && isNextVertical ? 0 : 1
 | 
				
			||||||
 | 
					      } else if (direction === 'north') {
 | 
				
			||||||
 | 
					        y -= isEndVertical && !isNextVertical ? 0 : 1
 | 
				
			||||||
 | 
					      } else if (direction === 'south') {
 | 
				
			||||||
 | 
					        y += isEndVertical && !isNextVertical ? 0 : 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    console.log('position::>>', tile.pips, x, y)
 | 
				
			||||||
 | 
					    tile.setPosition(this.scaleX(x), this.scaleY(y))
 | 
				
			||||||
 | 
					    tile.setOrientation(orientation)
 | 
				
			||||||
 | 
					    tile.reScale(this.scale)
 | 
				
			||||||
 | 
					    this.tiles.push(tile)
 | 
				
			||||||
 | 
					    tile.addTo(this.tilesContainer)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getPlayedTile(id: string): Tile | undefined {
 | 
				
			||||||
 | 
					    return this.tiles.find((t) => t.id === id)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getTileInHand(id: string): Tile | undefined {
 | 
				
			||||||
 | 
					    return this.playerHand.find((t) => t.id === id)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setFreeEnd(move: Movement) {
 | 
				
			||||||
 | 
					    const { type, tile: tileDto } = move
 | 
				
			||||||
 | 
					    const tile = this.getPlayedTile(tileDto?.id ?? '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (tile === undefined) {
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { pips } = tile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pips === undefined) {
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.freeEnds === undefined) {
 | 
				
			||||||
 | 
					      this.leftTile = this.rightTile = tile
 | 
				
			||||||
 | 
					      if (tile.isPair()) {
 | 
				
			||||||
 | 
					        this.freeEnds = [pips[0], pips[1]]
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.freeEnds = [pips[1], pips[0]]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (type === 'left') {
 | 
				
			||||||
 | 
					      this.leftTile = tile
 | 
				
			||||||
 | 
					      if (tile.isPair()) {
 | 
				
			||||||
 | 
					        this.freeEnds[0] = pips[0]
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.freeEnds[0] = pips[0] === this.freeEnds[0] ? pips[1] : pips[0]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if (type === 'right') {
 | 
				
			||||||
 | 
					      this.rightTile = tile
 | 
				
			||||||
 | 
					      if (tile.isPair()) {
 | 
				
			||||||
 | 
					        this.freeEnds[1] = pips[0]
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.freeEnds[1] = pips[0] === this.freeEnds[1] ? pips[1] : pips[0]
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  updateBoard(move: Movement) {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      const { tile: tileDto } = move
 | 
				
			||||||
 | 
					      const tile = this.getTileInHand(tileDto?.id ?? '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (tile === undefined) {
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.movements.push(move)
 | 
				
			||||||
 | 
					      this.addTile(tile, move)
 | 
				
			||||||
 | 
					      this.setFreeEnd(move)
 | 
				
			||||||
 | 
					    } catch (error) {
 | 
				
			||||||
 | 
					      console.log('error :>> ', error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setMovesForTile(values: [boolean, boolean], tile: TileDto) {
 | 
				
			||||||
 | 
					    this.setValidEnds(values, tile)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setValidEnds(values: boolean[], tile: TileDto) {
 | 
				
			||||||
 | 
					    console.log('validEnds')
 | 
				
			||||||
 | 
					    if (this.count === 0) {
 | 
				
			||||||
 | 
					      this.createInteractionsII('right', [[0, 0], undefined, undefined, undefined])
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (values[0]) {
 | 
				
			||||||
 | 
					      const side = 'left'
 | 
				
			||||||
 | 
					      const validInteractions = this.nextTileValidMoves(tile, side)
 | 
				
			||||||
 | 
					      const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
 | 
				
			||||||
 | 
					      console.log('validInteractions :>> ', validInteractions)
 | 
				
			||||||
 | 
					      // this.createInteractions(side, tile)
 | 
				
			||||||
 | 
					      this.createInteractionsII(side, validPoints)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (values[1]) {
 | 
				
			||||||
 | 
					      const side = 'right'
 | 
				
			||||||
 | 
					      const validInteractions = this.nextTileValidMoves(tile, side)
 | 
				
			||||||
 | 
					      const validPoints = this.nextTileValidPoints(tile, side, validInteractions)
 | 
				
			||||||
 | 
					      console.log('validInteractions :>> ', validInteractions)
 | 
				
			||||||
 | 
					      this.createInteractionsII(side, validPoints)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nextTileValidPoints(
 | 
				
			||||||
 | 
					    tile: TileDto,
 | 
				
			||||||
 | 
					    side: string,
 | 
				
			||||||
 | 
					    validMoves: boolean[]
 | 
				
			||||||
 | 
					  ): ([number, number] | undefined)[] {
 | 
				
			||||||
 | 
					    const isLeft = side === 'left'
 | 
				
			||||||
 | 
					    const end = isLeft ? this.leftTile : this.rightTile
 | 
				
			||||||
 | 
					    const isEndVertical = end?.isVertical() ?? false
 | 
				
			||||||
 | 
					    const direction = isLeft ? this.leftDirection : this.rightDirection
 | 
				
			||||||
 | 
					    const signX = direction === 'west' ? -1 : 1
 | 
				
			||||||
 | 
					    const signY = direction === 'south' ? 1 : -1
 | 
				
			||||||
 | 
					    const isEndPair = end?.isPair() ?? false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const x = this.scaleX.inverse(end?.x ?? 0)
 | 
				
			||||||
 | 
					    const y = this.scaleY.inverse(end?.y ?? 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let pointNorth: [number, number] | undefined = undefined
 | 
				
			||||||
 | 
					    let pointSouth: [number, number] | undefined = undefined
 | 
				
			||||||
 | 
					    let pointEast: [number, number] | undefined = undefined
 | 
				
			||||||
 | 
					    let pointWest: [number, number] | undefined = undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.count !== 0) {
 | 
				
			||||||
 | 
					      if (validMoves[0]) {
 | 
				
			||||||
 | 
					        // north
 | 
				
			||||||
 | 
					        if (isEndVertical) {
 | 
				
			||||||
 | 
					          pointNorth = [x, y - 3]
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          pointNorth = isEndPair ? [x, y - 2] : [x + 1 * signX, y - 2]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (validMoves[2]) {
 | 
				
			||||||
 | 
					        // south
 | 
				
			||||||
 | 
					        if (isEndVertical) {
 | 
				
			||||||
 | 
					          pointSouth = [x, y + 3]
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          pointSouth = isEndPair ? [x, y + 2] : [x + 1 * signX, y + 2]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (validMoves[1]) {
 | 
				
			||||||
 | 
					        // east
 | 
				
			||||||
 | 
					        if (isEndVertical) {
 | 
				
			||||||
 | 
					          pointEast = isEndPair ? [x + 2, y] : [x + 2, y + 1 * signY]
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          pointEast = [x + 3, y]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (validMoves[3]) {
 | 
				
			||||||
 | 
					        // west
 | 
				
			||||||
 | 
					        if (isEndVertical) {
 | 
				
			||||||
 | 
					          pointWest = isEndPair ? [x - 2, y] : [x - 2, y + 1 * signY]
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          pointWest = [x - 3, y]
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return [pointNorth, pointEast, pointSouth, pointWest]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createInteractionsII(side: string, validInteractions: ([number, number] | undefined)[]) {
 | 
				
			||||||
 | 
					    if (validInteractions[0] !== undefined) {
 | 
				
			||||||
 | 
					      this.addInteraction(validInteractions[0][0], validInteractions[0][1], side, 'north')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (validInteractions[1] !== undefined) {
 | 
				
			||||||
 | 
					      this.addInteraction(validInteractions[1][0], validInteractions[1][1], side, 'east')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (validInteractions[2] !== undefined) {
 | 
				
			||||||
 | 
					      this.addInteraction(validInteractions[2][0], validInteractions[2][1], side, 'south')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (validInteractions[3] !== undefined) {
 | 
				
			||||||
 | 
					      this.addInteraction(validInteractions[3][0], validInteractions[3][1], side, 'west')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  cleanInteractions() {
 | 
				
			||||||
 | 
					    this.interactionContainer.removeChildren()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addInteraction(x: number, y: number, side: string, direction?: string) {
 | 
				
			||||||
 | 
					    const dot = new Dot(this.ticker, this.scale)
 | 
				
			||||||
 | 
					    dot.alpha = 0.5
 | 
				
			||||||
 | 
					    dot.interactive = true
 | 
				
			||||||
 | 
					    dot.on('pointerdown', () => {
 | 
				
			||||||
 | 
					      console.log('direction :>> ', direction)
 | 
				
			||||||
 | 
					      this.emit(`${side}Click`, direction && { direction, x, y })
 | 
				
			||||||
 | 
					      this.cleanInteractions()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    dot.on('pointerover', () => {
 | 
				
			||||||
 | 
					      dot.alpha = 1
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    dot.on('pointerout', () => {
 | 
				
			||||||
 | 
					      dot.alpha = 0.5
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    dot.setPosition(this.scaleX(x), this.scaleY(y))
 | 
				
			||||||
 | 
					    dot.addTo(this.interactionContainer)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nextTileValidMoves(tile: TileDto, side: string): boolean[] {
 | 
				
			||||||
 | 
					    if (tile === undefined || this.freeEnds === undefined) return [false, false, false, false]
 | 
				
			||||||
 | 
					    if (this.count === 0) return [false, true, false, true] // depends on game mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const isLeft = side === 'left'
 | 
				
			||||||
 | 
					    const end = isLeft ? this.freeEnds[0] : this.freeEnds[1]
 | 
				
			||||||
 | 
					    const endTile = isLeft ? this.leftTile : this.rightTile
 | 
				
			||||||
 | 
					    const direction = isLeft ? this.leftDirection : this.rightDirection
 | 
				
			||||||
 | 
					    const signX = isLeft ? -1 : 1
 | 
				
			||||||
 | 
					    const signY = direction === 'south' ? 1 : -1
 | 
				
			||||||
 | 
					    const validMove = tile.pips?.includes(end) ?? false
 | 
				
			||||||
 | 
					    const endX = endTile?.x ?? 0
 | 
				
			||||||
 | 
					    const endY = endTile?.y ?? 0
 | 
				
			||||||
 | 
					    const tileHeight = endTile?.height ?? 0
 | 
				
			||||||
 | 
					    const margin = 20
 | 
				
			||||||
 | 
					    const spaceNeeded = tileHeight / 2 + tileHeight + margin
 | 
				
			||||||
 | 
					    const isSecond = this.count === 1
 | 
				
			||||||
 | 
					    const isPair = isTilePair(tile)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let canPlayNorth = false
 | 
				
			||||||
 | 
					    let canPlaySouth = false
 | 
				
			||||||
 | 
					    let canPlayEast = false
 | 
				
			||||||
 | 
					    let canPlayWest = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (validMove) {
 | 
				
			||||||
 | 
					      canPlayEast = direction !== 'west' && endX + signX * spaceNeeded < this.width
 | 
				
			||||||
 | 
					      canPlayWest = direction !== 'east' && endX + signX * spaceNeeded > 0
 | 
				
			||||||
 | 
					      canPlayNorth = direction !== 'south' && endY + signY * spaceNeeded > 0
 | 
				
			||||||
 | 
					      canPlaySouth = direction !== 'north' && endY + signY * spaceNeeded < this.height
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isSecond) {
 | 
				
			||||||
 | 
					      canPlayNorth = canPlaySouth = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isPair && !endTile?.isVertical()) {
 | 
				
			||||||
 | 
					      canPlayNorth = canPlaySouth = false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isPair && endTile?.isVertical()) {
 | 
				
			||||||
 | 
					      if (direction === 'north') {
 | 
				
			||||||
 | 
					        canPlaySouth = false
 | 
				
			||||||
 | 
					      } else if (direction === 'south') {
 | 
				
			||||||
 | 
					        canPlayNorth = false
 | 
				
			||||||
 | 
					      } else if (direction === 'east') {
 | 
				
			||||||
 | 
					        canPlayWest = false
 | 
				
			||||||
 | 
					      } else if (direction === 'west') {
 | 
				
			||||||
 | 
					        canPlayEast = false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return [canPlayNorth, canPlayEast, canPlaySouth, canPlayWest]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										14
									
								
								src/utilities/Dot.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/utilities/Dot.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					import { Graphics, Texture, Ticker } from 'pixi.js'
 | 
				
			||||||
 | 
					import { SpriteBase } from './SpriteBase'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Dot extends SpriteBase {
 | 
				
			||||||
 | 
					  constructor(ticker: Ticker, scale: number = 1) {
 | 
				
			||||||
 | 
					    super(ticker, scale)
 | 
				
			||||||
 | 
					    this.sprite.texture = this.createTexture()
 | 
				
			||||||
 | 
					    this.anchor = 0.5
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createTexture(): Texture {
 | 
				
			||||||
 | 
					    return Texture.from('dot')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										136
									
								
								src/utilities/Game.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/utilities/Game.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,136 @@
 | 
				
			|||||||
 | 
					import { Application, Assets } from 'pixi.js'
 | 
				
			||||||
 | 
					import { Board } from './Board'
 | 
				
			||||||
 | 
					import Hand from './Hand'
 | 
				
			||||||
 | 
					import { assets } from '@/utilities/assets'
 | 
				
			||||||
 | 
					import type { Movement, TileDto } from './interfaces'
 | 
				
			||||||
 | 
					import type { Tile } from './Tile'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export class Game {
 | 
				
			||||||
 | 
					  public board!: Board
 | 
				
			||||||
 | 
					  public hand!: Hand
 | 
				
			||||||
 | 
					  private app: Application = new Application()
 | 
				
			||||||
 | 
					  private selectedTile: TileDto | undefined
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    private options: { boardScale: number; handScale: number; width: number; height: number } = {
 | 
				
			||||||
 | 
					      boardScale: 1,
 | 
				
			||||||
 | 
					      handScale: 1,
 | 
				
			||||||
 | 
					      width: 1200,
 | 
				
			||||||
 | 
					      height: 650
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    private emit: any,
 | 
				
			||||||
 | 
					    private props: any
 | 
				
			||||||
 | 
					  ) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async setup(): Promise<HTMLCanvasElement> {
 | 
				
			||||||
 | 
					    const width = 1200
 | 
				
			||||||
 | 
					    const height = 650
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await this.app.init({ width, height })
 | 
				
			||||||
 | 
					    return this.app.canvas
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  start() {
 | 
				
			||||||
 | 
					    this.board = new Board(this.app)
 | 
				
			||||||
 | 
					    this.hand = new Hand(this.app)
 | 
				
			||||||
 | 
					    this.hand.scale = this.options.handScale
 | 
				
			||||||
 | 
					    this.board.scale = this.options.boardScale
 | 
				
			||||||
 | 
					    this.setBoardEvents()
 | 
				
			||||||
 | 
					    this.setHandEvents()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async preload() {
 | 
				
			||||||
 | 
					    await Assets.load(assets)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  destroy() {
 | 
				
			||||||
 | 
					    this.removeBoardEvents()
 | 
				
			||||||
 | 
					    this.removeHandEvents()
 | 
				
			||||||
 | 
					    this.app.destroy()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private setHandEvents() {
 | 
				
			||||||
 | 
					    this.hand.on('handUpdated', (tiles: Tile[]) => {
 | 
				
			||||||
 | 
					      this.board.setPlayerHand(tiles)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    this.hand.on('tileClick', (tile: TileDto) => {
 | 
				
			||||||
 | 
					      this.selectedTile = tile
 | 
				
			||||||
 | 
					      if (tile !== undefined) {
 | 
				
			||||||
 | 
					        this.board.setMovesForTile(this.getMoves(tile), tile)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.board.cleanInteractions()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.hand.on('passClick', () => {
 | 
				
			||||||
 | 
					      const move: Movement = {
 | 
				
			||||||
 | 
					        id: '',
 | 
				
			||||||
 | 
					        type: 'pass',
 | 
				
			||||||
 | 
					        playerId: this.props.playerId ?? ''
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.emit('move', move)
 | 
				
			||||||
 | 
					      this.board.updateBoard(move)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getMoves(tile: any): [boolean, boolean] {
 | 
				
			||||||
 | 
					    if (tile === undefined) return [false, false]
 | 
				
			||||||
 | 
					    if (this.board.count === 0) return [false, true]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const validEnds: [boolean, boolean] = [false, false]
 | 
				
			||||||
 | 
					    const freeEnds = this.board.freeEnds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (freeEnds !== undefined) {
 | 
				
			||||||
 | 
					      if (tile.pips != undefined) {
 | 
				
			||||||
 | 
					        validEnds[0] = tile.pips.includes(freeEnds[0])
 | 
				
			||||||
 | 
					        validEnds[1] = tile.pips.includes(freeEnds[1])
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return validEnds
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public setCanMakeMove(value: boolean) {
 | 
				
			||||||
 | 
					    this.hand.canMove = value
 | 
				
			||||||
 | 
					    this.board.canMove = value
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private setBoardEvents() {
 | 
				
			||||||
 | 
					    this.board.on('leftClick', (data) => {
 | 
				
			||||||
 | 
					      console.log('left data :>> ', data)
 | 
				
			||||||
 | 
					      if (this.selectedTile === undefined) return
 | 
				
			||||||
 | 
					      const move: Movement = {
 | 
				
			||||||
 | 
					        tile: this.selectedTile,
 | 
				
			||||||
 | 
					        type: 'left',
 | 
				
			||||||
 | 
					        playerId: this.props.playerId ?? '',
 | 
				
			||||||
 | 
					        ...data
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.emit('move', move)
 | 
				
			||||||
 | 
					      this.hand.tileMoved(this.selectedTile)
 | 
				
			||||||
 | 
					      this.board.updateBoard({ ...move, tile: this.selectedTile })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.board.on('rightClick', (data) => {
 | 
				
			||||||
 | 
					      console.log('right data :>> ', data)
 | 
				
			||||||
 | 
					      if (this.selectedTile === undefined) return
 | 
				
			||||||
 | 
					      const move: Movement = {
 | 
				
			||||||
 | 
					        tile: this.selectedTile,
 | 
				
			||||||
 | 
					        type: 'right',
 | 
				
			||||||
 | 
					        playerId: this.props.playerId ?? '',
 | 
				
			||||||
 | 
					        ...data
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      this.emit('move', move)
 | 
				
			||||||
 | 
					      this.hand.tileMoved(this.selectedTile)
 | 
				
			||||||
 | 
					      this.board.updateBoard({ ...move, tile: this.selectedTile })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private removeBoardEvents() {
 | 
				
			||||||
 | 
					    this.board.off('leftClick')
 | 
				
			||||||
 | 
					    this.board.off('rightClick')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private removeHandEvents() {
 | 
				
			||||||
 | 
					    this.hand.off('tileClick')
 | 
				
			||||||
 | 
					    this.hand.off('passClick')
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -3,67 +3,107 @@ import {
 | 
				
			|||||||
  Container,
 | 
					  Container,
 | 
				
			||||||
  EventEmitter,
 | 
					  EventEmitter,
 | 
				
			||||||
  Graphics,
 | 
					  Graphics,
 | 
				
			||||||
  Rectangle,
 | 
					 | 
				
			||||||
  RoundedRectangle,
 | 
					 | 
				
			||||||
  Sprite,
 | 
					  Sprite,
 | 
				
			||||||
  Text,
 | 
					  Text,
 | 
				
			||||||
  Texture
 | 
					  Texture,
 | 
				
			||||||
 | 
					  Ticker
 | 
				
			||||||
} from 'pixi.js'
 | 
					} from 'pixi.js'
 | 
				
			||||||
import { Tile } from './Tile'
 | 
					import { Tile } from './Tile'
 | 
				
			||||||
import type { PlayerState } from './interfaces'
 | 
					import type { PlayerState, TileDto } from './interfaces'
 | 
				
			||||||
 | 
					import { GlowFilter } from 'pixi-filters'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class Hand extends EventEmitter {
 | 
					export default class Hand extends EventEmitter {
 | 
				
			||||||
  tiles: Tile[] = []
 | 
					  tiles: Tile[] = []
 | 
				
			||||||
  container: Container
 | 
					  container: Container = new Container()
 | 
				
			||||||
 | 
					  buttonPassContainer: Container = new Container()
 | 
				
			||||||
  height: number
 | 
					  height: number
 | 
				
			||||||
  width: number
 | 
					  width: number
 | 
				
			||||||
 | 
					  ticker: Ticker
 | 
				
			||||||
 | 
					  lastTimeClicked: number = 0
 | 
				
			||||||
 | 
					  doubleClickThreshold: number = 300
 | 
				
			||||||
 | 
					  initialized: boolean = false
 | 
				
			||||||
 | 
					  _canMove: boolean = false
 | 
				
			||||||
 | 
					  scale: number = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(container: Container, canvas: HTMLCanvasElement) {
 | 
					  constructor(app: Application) {
 | 
				
			||||||
    super()
 | 
					    super()
 | 
				
			||||||
    this.container = container
 | 
					    app.stage.addChild(this.container)
 | 
				
			||||||
 | 
					    this.ticker = app.ticker
 | 
				
			||||||
    this.height = 130
 | 
					    this.height = 130
 | 
				
			||||||
    this.width = canvas.width
 | 
					    this.width = app.canvas.width
 | 
				
			||||||
    this.container.y = canvas.height - this.height
 | 
					    this.container.y = app.canvas.height - this.height
 | 
				
			||||||
    this.container.width = this.width
 | 
					    this.container.width = this.width
 | 
				
			||||||
    this.container.height = this.height
 | 
					    this.container.height = this.height
 | 
				
			||||||
    this.addBg()
 | 
					    this.addBg()
 | 
				
			||||||
    this.createPassButton()
 | 
					    this.createPassButton()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  setTiles(playerState: PlayerState) {
 | 
					  get canMove() {
 | 
				
			||||||
 | 
					    return this._canMove
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set canMove(value: boolean) {
 | 
				
			||||||
 | 
					    this._canMove = value
 | 
				
			||||||
 | 
					    this.buttonPassContainer.eventMode = value ? 'static' : 'none'
 | 
				
			||||||
 | 
					    this.buttonPassContainer.cursor = value ? 'pointer' : 'default'
 | 
				
			||||||
 | 
					    this.tiles.forEach((tile) => {
 | 
				
			||||||
 | 
					      tile.interactive = value
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  initialize(playerState: PlayerState) {
 | 
				
			||||||
    this.tiles = this.createTiles(playerState)
 | 
					    this.tiles = this.createTiles(playerState)
 | 
				
			||||||
 | 
					    this.emit('handUpdated', this.tiles)
 | 
				
			||||||
 | 
					    this.initialized = this.tiles.length > 0
 | 
				
			||||||
 | 
					    this.renderTiles()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private addBg() {
 | 
					  private addBg() {
 | 
				
			||||||
    console.log('addBg')
 | 
					 | 
				
			||||||
    const bg = new Sprite(Texture.WHITE)
 | 
					    const bg = new Sprite(Texture.WHITE)
 | 
				
			||||||
    bg.alpha = 0.05
 | 
					    bg.alpha = 0.08
 | 
				
			||||||
    bg.width = this.width
 | 
					    bg.width = this.width
 | 
				
			||||||
    bg.height = this.height
 | 
					    bg.height = this.height
 | 
				
			||||||
    this.container.addChild(bg)
 | 
					    this.container.addChild(bg)
 | 
				
			||||||
    this.container.addChild(
 | 
					 | 
				
			||||||
      new Text({
 | 
					 | 
				
			||||||
        text: 'Hand',
 | 
					 | 
				
			||||||
        style: {
 | 
					 | 
				
			||||||
          fontFamily: 'Arial',
 | 
					 | 
				
			||||||
          fontSize: 12,
 | 
					 | 
				
			||||||
          fill: 0xff1010,
 | 
					 | 
				
			||||||
          align: 'center'
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private onClick(tile: Tile) {
 | 
					  private onTileClick(tile: Tile) {
 | 
				
			||||||
    const selected = this.tiles.find((t) => t.selectd)
 | 
					    // if (Date.now() - this.lastTimeClicked < this.doubleClickThreshold) {
 | 
				
			||||||
 | 
					    //   this.emit('tileDoubleClick', { id: tile.id })
 | 
				
			||||||
 | 
					    //   return
 | 
				
			||||||
 | 
					    // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const selected = this.tiles.find((t) => t.selected)
 | 
				
			||||||
    if (selected) {
 | 
					    if (selected) {
 | 
				
			||||||
      selected.y = selected.y + 10
 | 
					      this.deselectTile(selected)
 | 
				
			||||||
      selected.selectd = false
 | 
					      if (selected.id === tile.id) {
 | 
				
			||||||
 | 
					        this.emit('tileClick')
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tile.selectd = true
 | 
					    tile.selected = true
 | 
				
			||||||
    tile.y = tile.y - 10
 | 
					    tile.alpha = 1
 | 
				
			||||||
    this.emit('tileClick', { id: tile.id })
 | 
					
 | 
				
			||||||
 | 
					    tile.animateTo(tile.x, tile.y - 10)
 | 
				
			||||||
 | 
					    this.emit('tileClick', tile.toPlain())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private deselectTile(selected: Tile) {
 | 
				
			||||||
 | 
					    selected.animateTo(selected.x, selected.y + 10)
 | 
				
			||||||
 | 
					    selected.selected = false
 | 
				
			||||||
 | 
					    selected.alpha = 0.7
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public tileMoved(tileDto: TileDto) {
 | 
				
			||||||
 | 
					    const tile = this.tiles.find((t) => t.id === tileDto.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!tile) return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tile.interactive = false
 | 
				
			||||||
 | 
					    tile.clearFilters()
 | 
				
			||||||
 | 
					    tile.off('pointerdown')
 | 
				
			||||||
 | 
					    tile.off('pointerover')
 | 
				
			||||||
 | 
					    tile.off('pointerout')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private createPassButton() {
 | 
					  private createPassButton() {
 | 
				
			||||||
@@ -81,51 +121,85 @@ export default class Hand extends EventEmitter {
 | 
				
			|||||||
    })
 | 
					    })
 | 
				
			||||||
    text.anchor = 0.5
 | 
					    text.anchor = 0.5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const buttonPassContainer = new Container()
 | 
					    this.buttonPassContainer = new Container()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buttonPassContainer.addChild(rectangle)
 | 
					    this.buttonPassContainer.addChild(rectangle)
 | 
				
			||||||
    buttonPassContainer.addChild(text)
 | 
					    this.buttonPassContainer.addChild(text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    text.y = buttonPassContainer.height / 2 - 4
 | 
					    text.y = this.buttonPassContainer.height / 2 - 4
 | 
				
			||||||
    text.x = buttonPassContainer.width / 2 - 8
 | 
					    text.x = this.buttonPassContainer.width / 2 - 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buttonPassContainer.eventMode = 'static'
 | 
					    this.buttonPassContainer.eventMode = 'none'
 | 
				
			||||||
    buttonPassContainer.cursor = 'pointer'
 | 
					    this.buttonPassContainer.cursor = 'default'
 | 
				
			||||||
    buttonPassContainer.x = 20
 | 
					    this.buttonPassContainer.x = 20
 | 
				
			||||||
    buttonPassContainer.y = this.height / 2 - 10
 | 
					    this.buttonPassContainer.y = this.height / 2 - 10
 | 
				
			||||||
    rectangle.alpha = 0.7
 | 
					    rectangle.alpha = 0.7
 | 
				
			||||||
    text.alpha = 0.7
 | 
					    text.alpha = 0.7
 | 
				
			||||||
    buttonPassContainer.on('pointerdown', () => {
 | 
					    this.buttonPassContainer.on('pointerdown', () => {
 | 
				
			||||||
      this.emit('passClick')
 | 
					      this.emit('passClick')
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    buttonPassContainer.on('pointerover', () => {
 | 
					    this.buttonPassContainer.on('pointerover', () => {
 | 
				
			||||||
      rectangle.alpha = 1
 | 
					      rectangle.alpha = 1
 | 
				
			||||||
      text.alpha = 1
 | 
					      text.alpha = 1
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buttonPassContainer.on('pointerout', () => {
 | 
					    this.buttonPassContainer.on('pointerout', () => {
 | 
				
			||||||
      rectangle.alpha = 0.7
 | 
					      rectangle.alpha = 0.7
 | 
				
			||||||
      text.alpha = 0.7
 | 
					      text.alpha = 0.7
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.container.addChild(buttonPassContainer)
 | 
					    this.container.addChild(this.buttonPassContainer)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  update(playerState: PlayerState) {
 | 
				
			||||||
 | 
					    if (!this.initialized) {
 | 
				
			||||||
 | 
					      this.initialize(playerState)
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const missing: Tile | undefined = this.tiles.find(
 | 
				
			||||||
 | 
					      (tile: Tile) => !playerState.hand.find((t) => t.id === tile.id)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    if (missing) {
 | 
				
			||||||
 | 
					      this.container.removeChild(missing.getSprite())
 | 
				
			||||||
 | 
					      this.tiles = this.tiles.filter((tile) => tile.id !== missing.id)
 | 
				
			||||||
 | 
					      this.emit('handUpdated', this.tiles)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.renderTiles()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private createTiles(playerState: PlayerState) {
 | 
					  private createTiles(playerState: PlayerState) {
 | 
				
			||||||
    const deltaX = (playerState.hand.length * 50 - 5) / 2
 | 
					 | 
				
			||||||
    return playerState.hand.map((tile, i) => {
 | 
					    return playerState.hand.map((tile, i) => {
 | 
				
			||||||
      const newTile: Tile = new Tile(tile.id, tile.pips)
 | 
					      const newTile: Tile = new Tile(tile.id, this.ticker, tile.pips, this.scale)
 | 
				
			||||||
 | 
					 | 
				
			||||||
      newTile.y = newTile.height / 2 + 20
 | 
					 | 
				
			||||||
      newTile.x = deltaX + newTile.width / 2 + i * (newTile.width + 5)
 | 
					 | 
				
			||||||
      newTile.eventMode = 'static'
 | 
					 | 
				
			||||||
      newTile.cursor = 'pointer'
 | 
					 | 
				
			||||||
      newTile.alpha = 0.7
 | 
					      newTile.alpha = 0.7
 | 
				
			||||||
      this.container.addChild(newTile)
 | 
					      newTile.anchor = 0.5
 | 
				
			||||||
      newTile.on('pointerdown', () => this.onClick(newTile))
 | 
					      newTile.addTo(this.container)
 | 
				
			||||||
      newTile.on('pointerover', () => (newTile.alpha = 1))
 | 
					      newTile.on('pointerdown', () => this.onTileClick(newTile))
 | 
				
			||||||
      newTile.on('pointerout', () => (newTile.alpha = 0.7))
 | 
					      newTile.on('pointerover', () => {
 | 
				
			||||||
 | 
					        newTile.alpha = 1
 | 
				
			||||||
 | 
					        newTile.setFilters([
 | 
				
			||||||
 | 
					          new GlowFilter({
 | 
				
			||||||
 | 
					            distance: 10,
 | 
				
			||||||
 | 
					            outerStrength: 2,
 | 
				
			||||||
 | 
					            innerStrength: 1,
 | 
				
			||||||
 | 
					            color: 0xffffff,
 | 
				
			||||||
 | 
					            quality: 0.5
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        ])
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      newTile.on('pointerout', () => {
 | 
				
			||||||
 | 
					        if (!newTile.selected) {
 | 
				
			||||||
 | 
					          newTile.alpha = 0.7
 | 
				
			||||||
 | 
					          newTile.getSprite().filters = []
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
      return newTile
 | 
					      return newTile
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  renderTiles() {
 | 
				
			||||||
 | 
					    const deltaX = this.width / 2 - (this.tiles.length * 50 - 5) / 2
 | 
				
			||||||
 | 
					    this.tiles.forEach((tile, i) => {
 | 
				
			||||||
 | 
					      tile.setPosition(deltaX + tile.width / 2 + i * (tile.width + 5), tile.height / 2 + 20)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										111
									
								
								src/utilities/SpriteBase.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/utilities/SpriteBase.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					import { Sprite, Texture, Ticker } from 'pixi.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export abstract class SpriteBase {
 | 
				
			||||||
 | 
					  private _interactive: boolean = false
 | 
				
			||||||
 | 
					  protected sprite: Sprite = new Sprite()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  constructor(
 | 
				
			||||||
 | 
					    protected ticker?: Ticker,
 | 
				
			||||||
 | 
					    protected scale: number = 1
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    this.ticker = ticker
 | 
				
			||||||
 | 
					    this.scale = scale
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  abstract createTexture(): Texture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set interactive(value: boolean) {
 | 
				
			||||||
 | 
					    this._interactive = value
 | 
				
			||||||
 | 
					    this.sprite.eventMode = value ? 'static' : 'none'
 | 
				
			||||||
 | 
					    this.sprite.cursor = value ? 'pointer' : 'default'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get interactive(): boolean {
 | 
				
			||||||
 | 
					    return this._interactive
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get x(): number {
 | 
				
			||||||
 | 
					    return this.sprite.x
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get y(): number {
 | 
				
			||||||
 | 
					    return this.sprite.y
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get width(): number {
 | 
				
			||||||
 | 
					    return this.sprite.width
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get height(): number {
 | 
				
			||||||
 | 
					    return this.sprite.height
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set alpha(value: number) {
 | 
				
			||||||
 | 
					    this.sprite.alpha = value
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  get alpha(): number {
 | 
				
			||||||
 | 
					    return this.sprite.alpha
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  set anchor(value: { x: number; y: number } | number) {
 | 
				
			||||||
 | 
					    if (typeof value === 'number') {
 | 
				
			||||||
 | 
					      this.sprite.anchor.set(value)
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.sprite.anchor.set(value.x, value.y)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  on(event: string, fn: any): void {
 | 
				
			||||||
 | 
					    this.sprite.on(event, fn)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  off(event: string): void {
 | 
				
			||||||
 | 
					    this.sprite.off(event)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  getSprite(): Sprite {
 | 
				
			||||||
 | 
					    return this.sprite
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setPosition(x: number, y: number) {
 | 
				
			||||||
 | 
					    this.sprite.x = x
 | 
				
			||||||
 | 
					    this.sprite.y = y
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setFilters(filters: any[]) {
 | 
				
			||||||
 | 
					    this.sprite.filters = filters
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  clearFilters() {
 | 
				
			||||||
 | 
					    this.sprite.filters = []
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  animateTo(x: number, y: number) {
 | 
				
			||||||
 | 
					    const initialX = this.sprite.x
 | 
				
			||||||
 | 
					    const initialY = this.sprite.y
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const deltaX = x - this.sprite.x
 | 
				
			||||||
 | 
					    const deltaY = y - this.sprite.y
 | 
				
			||||||
 | 
					    let elapsed: number = 0
 | 
				
			||||||
 | 
					    const duration: number = 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const tick: any = (delta: any) => {
 | 
				
			||||||
 | 
					      elapsed += delta.deltaTime
 | 
				
			||||||
 | 
					      const progress = Math.min(elapsed / duration, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.sprite.x = initialX + deltaX * progress
 | 
				
			||||||
 | 
					      this.sprite.y = initialY + deltaY * progress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (progress === 1) {
 | 
				
			||||||
 | 
					        this.ticker?.remove(tick)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.ticker?.add(tick)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addTo(container: any) {
 | 
				
			||||||
 | 
					    container.addChild(this.sprite)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,25 +1,86 @@
 | 
				
			|||||||
import { Sprite, Texture } from 'pixi.js'
 | 
					import { Texture, Ticker } from 'pixi.js'
 | 
				
			||||||
 | 
					import { SpriteBase } from './SpriteBase'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Tile extends Sprite {
 | 
					export class Tile extends SpriteBase {
 | 
				
			||||||
  id: string
 | 
					  selected: boolean = false
 | 
				
			||||||
  pips: [number, number] | undefined
 | 
					  orientation: string
 | 
				
			||||||
  selectd: boolean = false
 | 
					  // sprite: Sprite = new Sprite()
 | 
				
			||||||
 | 
					  // private _interactive: boolean = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(id: string, pips?: [number, number]) {
 | 
					  constructor(
 | 
				
			||||||
    super()
 | 
					    public id: string,
 | 
				
			||||||
 | 
					    ticker?: Ticker,
 | 
				
			||||||
 | 
					    public pips?: [number, number],
 | 
				
			||||||
 | 
					    scale: number = 1
 | 
				
			||||||
 | 
					  ) {
 | 
				
			||||||
 | 
					    super(ticker, scale)
 | 
				
			||||||
    this.id = id
 | 
					    this.id = id
 | 
				
			||||||
    this.anchor.set(0.5)
 | 
					    // this.ticker = ticker
 | 
				
			||||||
    this.width = 50
 | 
					    this.sprite.texture = this.createTexture()
 | 
				
			||||||
    this.height = 100
 | 
					    this.sprite.anchor.set(0.5)
 | 
				
			||||||
 | 
					    this.sprite.height = 100 * this.scale
 | 
				
			||||||
 | 
					    this.sprite.width = 50 * this.scale
 | 
				
			||||||
    this.pips = pips
 | 
					    this.pips = pips
 | 
				
			||||||
    this.texture = this.getTexture()
 | 
					    this.orientation = 'north'
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private getTexture(): Texture {
 | 
					  toPlain() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      id: this.id,
 | 
				
			||||||
 | 
					      pips: this.pips,
 | 
				
			||||||
 | 
					      orientation: this.orientation,
 | 
				
			||||||
 | 
					      x: this.sprite.x,
 | 
				
			||||||
 | 
					      y: this.sprite.y,
 | 
				
			||||||
 | 
					      width: this.sprite.width,
 | 
				
			||||||
 | 
					      height: this.sprite.height
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  equals(tile: Tile | undefined): boolean {
 | 
				
			||||||
 | 
					    if (this.pips === undefined || tile === undefined || tile.pips === undefined) return false
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      (this.pips[0] === tile.pips[0] && this.pips[1] === tile.pips[1]) ||
 | 
				
			||||||
 | 
					      (this.pips[0] === tile.pips[1] && this.pips[1] === tile.pips[0])
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  createTexture(): Texture {
 | 
				
			||||||
    if (this.pips !== undefined) {
 | 
					    if (this.pips !== undefined) {
 | 
				
			||||||
      return Texture.from(`tile-${this.pips[1]}_${this.pips[0]}`)
 | 
					      const sortedInversrPips = this.pips.slice().sort((a, b) => b - a)
 | 
				
			||||||
 | 
					      return Texture.from(`tile-${sortedInversrPips[0]}_${sortedInversrPips[1]}`)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      return Texture.from('tile-back')
 | 
					      return Texture.from('tile-back')
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  reScale(scale: number) {
 | 
				
			||||||
 | 
					    this.scale = scale
 | 
				
			||||||
 | 
					    this.sprite.height = 100 * this.scale
 | 
				
			||||||
 | 
					    this.sprite.width = 50 * this.scale
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  isPair(): boolean {
 | 
				
			||||||
 | 
					    return this.pips !== undefined && this.pips[0] === this.pips[1]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  isVertical(): boolean {
 | 
				
			||||||
 | 
					    return this.orientation === 'north' || this.orientation === 'south'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  setOrientation(value: string) {
 | 
				
			||||||
 | 
					    switch (value) {
 | 
				
			||||||
 | 
					      case 'north':
 | 
				
			||||||
 | 
					        this.sprite.rotation = 0
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      case 'east':
 | 
				
			||||||
 | 
					        this.sprite.rotation = Math.PI / 2
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      case 'south':
 | 
				
			||||||
 | 
					        this.sprite.rotation = Math.PI
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      case 'west':
 | 
				
			||||||
 | 
					        this.sprite.rotation = (3 * Math.PI) / 2
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.orientation = value
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ import tile6_3 from '@/assets/images/tiles/6-3.png'
 | 
				
			|||||||
import tile6_4 from '@/assets/images/tiles/6-4.png'
 | 
					import tile6_4 from '@/assets/images/tiles/6-4.png'
 | 
				
			||||||
import tile6_5 from '@/assets/images/tiles/6-5.png'
 | 
					import tile6_5 from '@/assets/images/tiles/6-5.png'
 | 
				
			||||||
import tile6_6 from '@/assets/images/tiles/6-6.png'
 | 
					import tile6_6 from '@/assets/images/tiles/6-6.png'
 | 
				
			||||||
 | 
					import dot from '@/assets/images/circle.png'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const assets = [
 | 
					export const assets = [
 | 
				
			||||||
  { alias: 'tile-back', src: tileBack },
 | 
					  { alias: 'tile-back', src: tileBack },
 | 
				
			||||||
@@ -57,5 +58,6 @@ export const assets = [
 | 
				
			|||||||
  { alias: 'tile-6_3', src: tile6_3 },
 | 
					  { alias: 'tile-6_3', src: tile6_3 },
 | 
				
			||||||
  { alias: 'tile-6_4', src: tile6_4 },
 | 
					  { alias: 'tile-6_4', src: tile6_4 },
 | 
				
			||||||
  { alias: 'tile-6_5', src: tile6_5 },
 | 
					  { alias: 'tile-6_5', src: tile6_5 },
 | 
				
			||||||
  { alias: 'tile-6_6', src: tile6_6 }
 | 
					  { alias: 'tile-6_6', src: tile6_6 },
 | 
				
			||||||
 | 
					  { alias: 'dot', src: dot }
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,14 +0,0 @@
 | 
				
			|||||||
import { Graphics, Container } from 'pixi.js'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export function getColorBackground(container: Container, colorName: string, alpha: number = 0.5) {
 | 
					 | 
				
			||||||
  const graphics = new Graphics()
 | 
					 | 
				
			||||||
  const color = 0xffffff
 | 
					 | 
				
			||||||
  graphics.rect(0, 0, container.width, container.height)
 | 
					 | 
				
			||||||
  graphics.fill(color)
 | 
					 | 
				
			||||||
  graphics.alpha = alpha
 | 
					 | 
				
			||||||
  graphics.x = 0
 | 
					 | 
				
			||||||
  graphics.y = 0
 | 
					 | 
				
			||||||
  console.log('graphics :>> ', graphics)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return graphics
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										25
									
								
								src/utilities/fonts.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/utilities/fonts.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					import { Text, TextStyle } from 'pixi.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const dropShadowStyle = {
 | 
				
			||||||
 | 
					  alpha: 0.5,
 | 
				
			||||||
 | 
					  angle: 0.3,
 | 
				
			||||||
 | 
					  blur: 5,
 | 
				
			||||||
 | 
					  distance: 4
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const mainText = new TextStyle({
 | 
				
			||||||
 | 
					  dropShadow: dropShadowStyle,
 | 
				
			||||||
 | 
					  fill: '#b71a1a',
 | 
				
			||||||
 | 
					  fontFamily: 'Arial, Helvetica, sans-serif',
 | 
				
			||||||
 | 
					  fontWeight: 'bold',
 | 
				
			||||||
 | 
					  letterSpacing: 1,
 | 
				
			||||||
 | 
					  stroke: '#658f56'
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function createText(str: string, x: number, y: number, style: TextStyle = mainText) {
 | 
				
			||||||
 | 
					  const text = new Text({ text: str, style })
 | 
				
			||||||
 | 
					  text.anchor.set(0.5, 0.5)
 | 
				
			||||||
 | 
					  text.x = x
 | 
				
			||||||
 | 
					  text.y = y
 | 
				
			||||||
 | 
					  return text
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										62
									
								
								src/utilities/helpers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/utilities/helpers.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,62 @@
 | 
				
			|||||||
 | 
					import { Graphics, Container } from 'pixi.js'
 | 
				
			||||||
 | 
					import type { TileDto } from './interfaces'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getColorBackground(container: Container, colorName: string, alpha: number = 0.5) {
 | 
				
			||||||
 | 
					  const graphics = new Graphics()
 | 
				
			||||||
 | 
					  const color = 0xffffff
 | 
				
			||||||
 | 
					  graphics.rect(0, 0, container.width, container.height)
 | 
				
			||||||
 | 
					  graphics.fill(color)
 | 
				
			||||||
 | 
					  graphics.alpha = alpha
 | 
				
			||||||
 | 
					  graphics.x = 0
 | 
				
			||||||
 | 
					  graphics.y = 0
 | 
				
			||||||
 | 
					  return graphics
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface ContainerOptions {
 | 
				
			||||||
 | 
					  width?: number
 | 
				
			||||||
 | 
					  height?: number
 | 
				
			||||||
 | 
					  x?: number
 | 
				
			||||||
 | 
					  y?: number
 | 
				
			||||||
 | 
					  color?: number
 | 
				
			||||||
 | 
					  visible?: boolean
 | 
				
			||||||
 | 
					  parent?: Container
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const defaultContainerOptions = {
 | 
				
			||||||
 | 
					  width: 100,
 | 
				
			||||||
 | 
					  height: 100,
 | 
				
			||||||
 | 
					  x: 0,
 | 
				
			||||||
 | 
					  y: 0,
 | 
				
			||||||
 | 
					  visible: true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function createContainer(options: ContainerOptions) {
 | 
				
			||||||
 | 
					  const opts = { ...defaultContainerOptions, ...options }
 | 
				
			||||||
 | 
					  const container = new Container()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const rect = new Graphics().rect(opts.x, opts.y, opts.width, opts.height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (opts.color) {
 | 
				
			||||||
 | 
					    rect.fill(opts.color)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  rect.visible = opts.visible
 | 
				
			||||||
 | 
					  container.addChild(rect)
 | 
				
			||||||
 | 
					  if (opts.parent) {
 | 
				
			||||||
 | 
					    opts.parent.addChild(container)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return container
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function wait(ms: number) {
 | 
				
			||||||
 | 
					  return new Promise((resolve) => setTimeout(resolve, ms))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DIRECTIONS = ['north', 'east', 'south', 'west']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function isTilePair(tile: TileDto): boolean {
 | 
				
			||||||
 | 
					  return !!(tile.pips && tile.pips[0] === tile.pips[1])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function isTileVertical(tile: TileDto): boolean {
 | 
				
			||||||
 | 
					  return tile.orientation === 'north' || tile.orientation === 'south'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,20 +1,65 @@
 | 
				
			|||||||
 | 
					export interface PlayerDto {
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  score?: number
 | 
				
			||||||
 | 
					  hand?: string[]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface TileDto {
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  pips?: [number, number]
 | 
				
			||||||
 | 
					  orientation?: string
 | 
				
			||||||
 | 
					  x?: number
 | 
				
			||||||
 | 
					  y?: number
 | 
				
			||||||
 | 
					  width?: number
 | 
				
			||||||
 | 
					  height?: number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					export interface GameSessionState {
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  name: string
 | 
				
			||||||
 | 
					  creator: string
 | 
				
			||||||
 | 
					  players: PlayerDto[]
 | 
				
			||||||
 | 
					  seed: string
 | 
				
			||||||
 | 
					  waitingForPlayers: boolean
 | 
				
			||||||
 | 
					  mode: string
 | 
				
			||||||
 | 
					  pointsToWin: number
 | 
				
			||||||
 | 
					  sessionInProgress: boolean
 | 
				
			||||||
 | 
					  status: string
 | 
				
			||||||
 | 
					  maxPlayers: number
 | 
				
			||||||
 | 
					  numPlayers: number
 | 
				
			||||||
 | 
					  waitingSeconds: number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface GameState {
 | 
					export interface GameState {
 | 
				
			||||||
  players: any[]
 | 
					  id: string
 | 
				
			||||||
  boneyard: any[]
 | 
					  players: PlayerDto[]
 | 
				
			||||||
  currentPlayer: string
 | 
					  tilesInBoneyard: TileDto[]
 | 
				
			||||||
  board: any[]
 | 
					  currentPlayer: PlayerDto | null
 | 
				
			||||||
 | 
					  tilesInBoard: TileDto[]
 | 
				
			||||||
  gameInProgress: boolean
 | 
					  gameInProgress: boolean
 | 
				
			||||||
  winner?: any
 | 
					  winner?: any
 | 
				
			||||||
  gameBlocked: boolean
 | 
					  gameBlocked: boolean
 | 
				
			||||||
  gameTied: boolean
 | 
					  gameTied: boolean
 | 
				
			||||||
  gameId: string
 | 
					  gameId: string
 | 
				
			||||||
  tileSelectionPhase: boolean
 | 
					  tileSelectionPhase: boolean
 | 
				
			||||||
 | 
					  boardFreeEnds: number[]
 | 
				
			||||||
 | 
					  lastMove: Movement
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface PlayerState {
 | 
					export interface PlayerState {
 | 
				
			||||||
  id: string
 | 
					  id: string
 | 
				
			||||||
  name: string
 | 
					  name: string
 | 
				
			||||||
  score: number
 | 
					  score: number
 | 
				
			||||||
  hand: any[]
 | 
					  hand: TileDto[]
 | 
				
			||||||
  teamedWith: string | undefined
 | 
					  teamedWith: string | undefined
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export interface Movement {
 | 
				
			||||||
 | 
					  id: string
 | 
				
			||||||
 | 
					  type: string
 | 
				
			||||||
 | 
					  tile?: TileDto
 | 
				
			||||||
 | 
					  playerId: string
 | 
				
			||||||
 | 
					  direction?: string
 | 
				
			||||||
 | 
					  x?: number
 | 
				
			||||||
 | 
					  y?: number
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,3 +1,8 @@
 | 
				
			|||||||
 | 
					import { Tile } from './Tile'
 | 
				
			||||||
 | 
					import type { GameState, Movement } from './interfaces'
 | 
				
			||||||
 | 
					import type { Game } from './Game'
 | 
				
			||||||
 | 
					import { wait } from './helpers'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const playerState = {
 | 
					export const playerState = {
 | 
				
			||||||
  id: '6fddcf4f-eaa9-4c87-a599-2af944477091',
 | 
					  id: '6fddcf4f-eaa9-4c87-a599-2af944477091',
 | 
				
			||||||
  name: 'arhuako',
 | 
					  name: 'arhuako',
 | 
				
			||||||
@@ -15,7 +20,7 @@ export const playerState = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
      id: 'f516ef48-99f2-4add-96eb-877f03bf0cb1',
 | 
					      id: 'f516ef48-99f2-4add-96eb-877f03bf0cb1',
 | 
				
			||||||
      pips: [2, 3],
 | 
					      pips: [6, 6],
 | 
				
			||||||
      flipped: false
 | 
					      flipped: false
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -40,3 +45,111 @@ export const playerState = {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const gameState_0: GameState = {
 | 
				
			||||||
 | 
					  id: 'f043051e-6850-444f-857c-b889220fc187',
 | 
				
			||||||
 | 
					  lastMove: {
 | 
				
			||||||
 | 
					    tile: {
 | 
				
			||||||
 | 
					      id: 'c3f6b525-2e6f-455a-b355-9b8c8f5075f0',
 | 
				
			||||||
 | 
					      pips: [6, 6]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    type: 'both',
 | 
				
			||||||
 | 
					    playerId: '137b68a6-f26c-43f5-badf-e5ae406fd059',
 | 
				
			||||||
 | 
					    id: 'c71943a5-1fef-4418-bd66-39767ac36817'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  gameInProgress: true,
 | 
				
			||||||
 | 
					  winner: null,
 | 
				
			||||||
 | 
					  tileSelectionPhase: false,
 | 
				
			||||||
 | 
					  gameBlocked: false,
 | 
				
			||||||
 | 
					  gameTied: false,
 | 
				
			||||||
 | 
					  gameId: 'd76c4f98-1f26-4390-9e8d-2d727876b710',
 | 
				
			||||||
 | 
					  tilesInBoneyard: [],
 | 
				
			||||||
 | 
					  players: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      id: 'f639f263-e415-428c-bdd2-113f00f3c392',
 | 
				
			||||||
 | 
					      name: 'arhuako',
 | 
				
			||||||
 | 
					      score: 0,
 | 
				
			||||||
 | 
					      hand: [
 | 
				
			||||||
 | 
					        '51399f92-7555-4ffc-91ed-f6b5b9041a54',
 | 
				
			||||||
 | 
					        'e9c40c13-c821-4bad-810c-12e7cb79b47d',
 | 
				
			||||||
 | 
					        '95e8412b-1938-48d5-9647-cb8617659db0',
 | 
				
			||||||
 | 
					        '54948d0a-b7a4-416c-a3f7-6602cfff1851',
 | 
				
			||||||
 | 
					        'a40e6a76-c2e1-4692-82b3-603a4412a6a9',
 | 
				
			||||||
 | 
					        'cb7827df-fe19-4c28-a786-a47e07c2d368',
 | 
				
			||||||
 | 
					        '6d5a31d3-b734-4312-b8fd-1b5efa3fc258'
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      id: '77b2c512-d9e5-45f3-850f-99baab1befdf',
 | 
				
			||||||
 | 
					      name: 'Alice (AI)',
 | 
				
			||||||
 | 
					      score: 0,
 | 
				
			||||||
 | 
					      hand: [
 | 
				
			||||||
 | 
					        '066fd011-cabe-483b-a985-c649f4908356',
 | 
				
			||||||
 | 
					        '01434825-eb3c-4baf-8ad1-cc055f982e75',
 | 
				
			||||||
 | 
					        'c085c90f-f69c-4f83-acb6-87c576927366',
 | 
				
			||||||
 | 
					        '1b5d5b46-a41c-4e69-9a02-efff9adbc201',
 | 
				
			||||||
 | 
					        '74833dcc-1526-4c6c-a64c-a7807e647e77',
 | 
				
			||||||
 | 
					        'fb7e6976-67a9-496f-9758-0b172aa069dd',
 | 
				
			||||||
 | 
					        '58b612f0-cf40-4b5f-ba05-8a089f19b43a'
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      id: '137b68a6-f26c-43f5-badf-e5ae406fd059',
 | 
				
			||||||
 | 
					      name: 'Bob (AI)',
 | 
				
			||||||
 | 
					      score: 0,
 | 
				
			||||||
 | 
					      hand: [
 | 
				
			||||||
 | 
					        '9d5dcb97-7478-4030-a0f6-4e7080a5b8e0',
 | 
				
			||||||
 | 
					        'a07e1aaf-e651-4678-a61c-414f4324d8e8',
 | 
				
			||||||
 | 
					        '4db7aebb-ab4b-4709-b208-b39176e8d70a',
 | 
				
			||||||
 | 
					        'd1733f9c-86a6-4597-b1ff-ec44e9a375d1',
 | 
				
			||||||
 | 
					        'cf6e2c1c-57f8-4342-8ff4-8046971f99db',
 | 
				
			||||||
 | 
					        '33cec060-7f40-4af9-9182-f98cbacbae10'
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      id: '188c1d69-8c5c-4fec-a08b-dc5fa09d49fd',
 | 
				
			||||||
 | 
					      name: 'Charlie (AI)',
 | 
				
			||||||
 | 
					      score: 0,
 | 
				
			||||||
 | 
					      hand: [
 | 
				
			||||||
 | 
					        '58433be6-e8d9-4ed2-bbdc-4328425a3687',
 | 
				
			||||||
 | 
					        '3e24a6cd-02ea-436a-a3e4-41ae62c717d0',
 | 
				
			||||||
 | 
					        '2a2723d1-6c46-4718-a1c2-fa8d2c148ac8',
 | 
				
			||||||
 | 
					        'd521de5e-1db1-4a2e-971d-f91cc761342c',
 | 
				
			||||||
 | 
					        '13d943d7-b694-4419-82f5-729117878d01',
 | 
				
			||||||
 | 
					        'afcf2509-acfb-47ed-ade7-d08f1a20afd2',
 | 
				
			||||||
 | 
					        'f50f3589-41ab-4d99-919e-c4c7a71ef5b0'
 | 
				
			||||||
 | 
					      ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  currentPlayer: {
 | 
				
			||||||
 | 
					    id: '188c1d69-8c5c-4fec-a08b-dc5fa09d49fd',
 | 
				
			||||||
 | 
					    name: 'arhuako'
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  tilesInBoard: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      id: 'c3f6b525-2e6f-455a-b355-9b8c8f5075f0',
 | 
				
			||||||
 | 
					      pips: [6, 6]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
 | 
					  boardFreeEnds: [6, 6]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function getMovement(
 | 
				
			||||||
 | 
					  pips: [number, number] = [6, 6],
 | 
				
			||||||
 | 
					  side: string = 'left',
 | 
				
			||||||
 | 
					  scale: number = 1
 | 
				
			||||||
 | 
					): Movement {
 | 
				
			||||||
 | 
					  const tile = new Tile('string', undefined, pips, scale)
 | 
				
			||||||
 | 
					  tile.orientation = 'north'
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    id: 'movemnetid',
 | 
				
			||||||
 | 
					    type: side,
 | 
				
			||||||
 | 
					    tile,
 | 
				
			||||||
 | 
					    playerId: 'string'
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function mockMove(game: Game, pips: [number, number], side: string = 'left') {
 | 
				
			||||||
 | 
					  await wait(1000)
 | 
				
			||||||
 | 
					  game.board.updateBoard(getMovement(pips, side, game.board.scale))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,20 @@
 | 
				
			|||||||
 | 
					export type ScaleFunction = Function & {
 | 
				
			||||||
 | 
					  inverse: Function
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param domain example: [0, 100]
 | 
					 * @param domain example: [0, 100]
 | 
				
			||||||
 * @param range examaple: [0, width]
 | 
					 * @param range examaple: [0, width]
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export function Scale(domain: number[], range: number[]): Function {
 | 
					export function Scale(domain: number[], range: number[]): ScaleFunction {
 | 
				
			||||||
  return function (value: number): number {
 | 
					  function Fn(value: number): number {
 | 
				
			||||||
    return ((value - domain[0]) * (range[1] - range[0])) / (domain[1] - domain[0]) + range[0]
 | 
					    return ((value - domain[0]) * (range[1] - range[0])) / (domain[1] - domain[0]) + range[0]
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Fn.inverse = function (value: number): number {
 | 
				
			||||||
 | 
					    return ((value - range[0]) * (domain[1] - domain[0])) / (range[1] - range[0]) + domain[0]
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return Fn
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,119 +1,43 @@
 | 
				
			|||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import GameComponent from '@/components/GameComponent.vue'
 | 
					import GameComponent from '@/components/GameComponent.vue'
 | 
				
			||||||
import TileComponent from '@/components/TileComponent.vue'
 | 
					import { useGameStore } from '@/stores/game'
 | 
				
			||||||
import type { GameState, PlayerState } from '@/utilities/interfaces'
 | 
					import { storeToRefs } from 'pinia'
 | 
				
			||||||
import { inject, onBeforeUnmount, ref } from 'vue'
 | 
					import { inject, onBeforeUnmount, ref } from 'vue'
 | 
				
			||||||
import { onMounted } from 'vue'
 | 
					import { onMounted } from 'vue'
 | 
				
			||||||
 | 
					import useClipboard from 'vue-clipboard3'
 | 
				
			||||||
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const socketService: any = inject('socket')
 | 
					const socketService: any = inject('socket')
 | 
				
			||||||
const { socket } = socketService
 | 
					 | 
				
			||||||
let gameState = ref<GameState | undefined>(undefined)
 | 
					 | 
				
			||||||
let playerState = ref<PlayerState | undefined>(undefined)
 | 
					 | 
				
			||||||
let data = ref('')
 | 
					let data = ref('')
 | 
				
			||||||
let responseField = ref('')
 | 
					let responseField = ref('')
 | 
				
			||||||
let statusField = ref('')
 | 
					let statusField = ref('')
 | 
				
			||||||
let sessionId = ref('')
 | 
					let sessionId = ref('')
 | 
				
			||||||
 | 
					let seed = ref('')
 | 
				
			||||||
 | 
					let playerId = ref('')
 | 
				
			||||||
let selectdAction: any = undefined
 | 
					let selectdAction: any = undefined
 | 
				
			||||||
let canSelectTile = false
 | 
					 | 
				
			||||||
let canMakeMove = false
 | 
					 | 
				
			||||||
let tileSelected = ''
 | 
					 | 
				
			||||||
let moveType = ''
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { toClipboard } = useClipboard()
 | 
				
			||||||
 | 
					const gameStore = useGameStore()
 | 
				
			||||||
 | 
					const { moveToMake, canMakeMove, sessionState, gameState } = storeToRefs(gameStore)
 | 
				
			||||||
const options = [
 | 
					const options = [
 | 
				
			||||||
  { value: 'createSession', default: '{"user": "arhuako"}' },
 | 
					  { value: 'createSession', default: '{"user": "arhuako"}' },
 | 
				
			||||||
  { value: 'startSession', default: (id: string) => `{"sessionId": "${id}"}` },
 | 
					  { value: 'startSession', default: (id: string) => `{"sessionId": "${id}"}` }
 | 
				
			||||||
  { value: 'joinSession', default: '{"user": "pepe", "sessionId": "arhuako"}' },
 | 
					  // { value: 'joinSession', default: '{"user": "pepe", "sessionId": "arhuako"}' },
 | 
				
			||||||
  { value: 'leaveSession', default: '{"user": "pepe", "sessionId": "arhuako"}' },
 | 
					  // { value: 'leaveSession', default: '{"user": "pepe", "sessionId": "arhuako"}' },
 | 
				
			||||||
  { value: 'chat message', default: 'chat message' }
 | 
					  // { value: 'chat message', default: 'chat message' }
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(async () => {
 | 
					onMounted(async () => {})
 | 
				
			||||||
  socket.on('gameState', (data: GameState, callback: any) => {
 | 
					 | 
				
			||||||
    gameState.value = data
 | 
					 | 
				
			||||||
    statusField.value = statusField.value + JSON.stringify(data, null, 2)
 | 
					 | 
				
			||||||
    callback({
 | 
					 | 
				
			||||||
      status: 'ok'
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  socket.on('playerState', (data: PlayerState, callback: any) => {
 | 
					function makeMove(move: any) {
 | 
				
			||||||
    console.log('playerState :>> ', data)
 | 
					  moveToMake.value = move
 | 
				
			||||||
    playerState.value = data
 | 
					  canMakeMove.value = false
 | 
				
			||||||
    statusField.value = statusField.value + JSON.stringify(data, null, 2)
 | 
					 | 
				
			||||||
    callback({
 | 
					 | 
				
			||||||
      status: 'ok'
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  socket.on('makeMove', async (data: any, callback: any) => {
 | 
					 | 
				
			||||||
    statusField.value = statusField.value + JSON.stringify(data, null, 2)
 | 
					 | 
				
			||||||
    canMakeMove = true
 | 
					 | 
				
			||||||
    while (canMakeMove) {
 | 
					 | 
				
			||||||
      await wait(500)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    callback({
 | 
					 | 
				
			||||||
      status: 'ok',
 | 
					 | 
				
			||||||
      tileId: tileSelected,
 | 
					 | 
				
			||||||
      moveType
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  socket.on('chooseTile', async (data: any, callback: any) => {
 | 
					 | 
				
			||||||
    statusField.value = statusField.value + JSON.stringify(data, null, 2)
 | 
					 | 
				
			||||||
    canSelectTile = true
 | 
					 | 
				
			||||||
    while (canSelectTile) {
 | 
					 | 
				
			||||||
      await wait(500)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    callback({
 | 
					 | 
				
			||||||
      status: 'ok',
 | 
					 | 
				
			||||||
      tileId: tileSelected
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  })
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function selectTile(id: string) {
 | 
					 | 
				
			||||||
  if (canSelectTile) {
 | 
					 | 
				
			||||||
    tileSelected = id
 | 
					 | 
				
			||||||
    canSelectTile = false
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function selectTileToMove(id: string) {
 | 
					 | 
				
			||||||
  if (canMakeMove) {
 | 
					 | 
				
			||||||
    if (tileSelected === id) {
 | 
					 | 
				
			||||||
      tileSelected = ''
 | 
					 | 
				
			||||||
      return
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    tileSelected = id
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function onTileClick(tileId: string) {
 | 
					 | 
				
			||||||
  console.log('tileId :>> ', tileId)
 | 
					 | 
				
			||||||
  selectTile(tileId)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function makeMove(type: string) {
 | 
					 | 
				
			||||||
  if (canMakeMove) {
 | 
					 | 
				
			||||||
    if (type === 'pass') {
 | 
					 | 
				
			||||||
      tileSelected = ''
 | 
					 | 
				
			||||||
      canMakeMove = false
 | 
					 | 
				
			||||||
    } else if (tileSelected) {
 | 
					 | 
				
			||||||
      moveType = type
 | 
					 | 
				
			||||||
      canMakeMove = false
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onBeforeUnmount(() => {
 | 
					onBeforeUnmount(() => {
 | 
				
			||||||
  socketService.disconnect()
 | 
					  // socketService.disconnect()
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function actionSelected() {
 | 
					function actionSelected() {
 | 
				
			||||||
  console.log('event :>> ', selectdAction)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (selectdAction.value === 'createSession') {
 | 
					  if (selectdAction.value === 'createSession') {
 | 
				
			||||||
    responseField.value = ''
 | 
					    responseField.value = ''
 | 
				
			||||||
  } else if (selectdAction.value === 'startSession') {
 | 
					  } else if (selectdAction.value === 'startSession') {
 | 
				
			||||||
@@ -129,15 +53,36 @@ const getMessage = (msg: string) => {
 | 
				
			|||||||
  return msg
 | 
					  return msg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function createSession() {
 | 
				
			||||||
 | 
					  const response = await socketService.sendMessageWithAck('createSession', { user: 'arhuako' })
 | 
				
			||||||
 | 
					  sessionId.value = response.sessionId
 | 
				
			||||||
 | 
					  playerId.value = response.playerId
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function startSession() {
 | 
				
			||||||
 | 
					  if (sessionId.value) {
 | 
				
			||||||
 | 
					    await socketService.sendMessageWithAck('startSession', {
 | 
				
			||||||
 | 
					      sessionId: sessionId.value,
 | 
				
			||||||
 | 
					      seed: seed.value.trim()
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function joinSession() {
 | 
				
			||||||
 | 
					  if (sessionId.value) {
 | 
				
			||||||
 | 
					    await socketService.sendMessageWithAck('joinSession', {
 | 
				
			||||||
 | 
					      user: 'pepe',
 | 
				
			||||||
 | 
					      sessionId: sessionId.value
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function sendMessage() {
 | 
					async function sendMessage() {
 | 
				
			||||||
  if (selectdAction && data.value.trim() !== '') {
 | 
					  if (selectdAction && data.value.trim() !== '') {
 | 
				
			||||||
    console.log('socketService :>> ', socketService)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const response = await socketService.sendMessageWithAck(
 | 
					    const response = await socketService.sendMessageWithAck(
 | 
				
			||||||
      selectdAction.value,
 | 
					      selectdAction.value,
 | 
				
			||||||
      getMessage(data.value.trim())
 | 
					      getMessage(data.value.trim())
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    console.log('response :>> ', response)
 | 
					 | 
				
			||||||
    handleResponse(response)
 | 
					    handleResponse(response)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // socketService.emit('message', data.value)
 | 
					  // socketService.emit('message', data.value)
 | 
				
			||||||
@@ -146,6 +91,7 @@ async function sendMessage() {
 | 
				
			|||||||
function handleResponse(response: any) {
 | 
					function handleResponse(response: any) {
 | 
				
			||||||
  if (selectdAction.value === 'createSession') {
 | 
					  if (selectdAction.value === 'createSession') {
 | 
				
			||||||
    sessionId.value = response.sessionId
 | 
					    sessionId.value = response.sessionId
 | 
				
			||||||
 | 
					    playerId.value = response.playerId
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  data.value = ''
 | 
					  data.value = ''
 | 
				
			||||||
@@ -155,28 +101,92 @@ function handleResponse(response: any) {
 | 
				
			|||||||
    : responseField.value + '\n---\n ' + responseStr
 | 
					    : responseField.value + '\n---\n ' + responseStr
 | 
				
			||||||
  selectdAction = undefined
 | 
					  selectdAction = undefined
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function copySeed() {
 | 
				
			||||||
 | 
					  if (sessionState?.value?.seed) toClipboard(sessionState.value.seed)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="grid">
 | 
					  <div class="block">
 | 
				
			||||||
    <div>
 | 
					    <section class="block">
 | 
				
			||||||
      <GameComponent :gameState="gameState" :playerState="playerState" @tileClick="onTileClick" />
 | 
					 | 
				
			||||||
      <p>SessionID: {{ sessionId }}</p>
 | 
					 | 
				
			||||||
      <ul id="messages"></ul>
 | 
					 | 
				
			||||||
      <form id="form" action="">
 | 
					 | 
				
			||||||
      <p>
 | 
					      <p>
 | 
				
			||||||
          <select v-model="selectdAction" id="event" autocomplete="off" @change="actionSelected">
 | 
					        Running: {{ sessionState?.sessionInProgress }} Seed: {{ sessionState?.seed }}
 | 
				
			||||||
 | 
					        <button @click="copySeed">Copy!</button>
 | 
				
			||||||
 | 
					      </p>
 | 
				
			||||||
 | 
					      <p>FreeEnds: {{ gameState?.boardFreeEnds }} - {{ gameState?.currentPlayer?.name }}</p>
 | 
				
			||||||
 | 
					      <p v-if="sessionId">SessionID: {{ sessionId }} PlayerID: {{ playerId }}</p>
 | 
				
			||||||
 | 
					    </section>
 | 
				
			||||||
 | 
					    <section class="block">
 | 
				
			||||||
 | 
					      <div class="game-container">
 | 
				
			||||||
 | 
					        <GameComponent :playerId="playerId" :canMakeMove="canMakeMove" @move="makeMove" />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </section>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <section class="block">
 | 
				
			||||||
 | 
					      <div class="fixed-grid has-8-cols">
 | 
				
			||||||
 | 
					        <div class="grid" v-if="!sessionId">
 | 
				
			||||||
 | 
					          <div class="cell">
 | 
				
			||||||
 | 
					            <button style="width: 200px" class="button" @click="createSession">
 | 
				
			||||||
 | 
					              Create Session
 | 
				
			||||||
 | 
					            </button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="cell is-col-span7"></div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="grid" v-if="sessionId">
 | 
				
			||||||
 | 
					          <div class="cell">
 | 
				
			||||||
 | 
					            <button class="button" style="width: 200px" @click="startSession">Start Session</button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="cell is-col-span-7">
 | 
				
			||||||
 | 
					            <input class="input" style="margin-bottom: 0" v-model="seed" placeholder="Seed" />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="grid" v-if="!sessionId">
 | 
				
			||||||
 | 
					          <div class="cell">
 | 
				
			||||||
 | 
					            <button class="button" style="width: 200px" @click="joinSession">Join Session</button>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          <div class="cell is-col-span-7">
 | 
				
			||||||
 | 
					            <input
 | 
				
			||||||
 | 
					              class="input"
 | 
				
			||||||
 | 
					              style="margin-bottom: 0"
 | 
				
			||||||
 | 
					              v-model="sessionId"
 | 
				
			||||||
 | 
					              placeholder="Session Id"
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div class="mt-1 action-select"></div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div class="grid" style="margin-top: 16px; display: none">
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
 | 
					          <!-- <ul id="messages"></ul> -->
 | 
				
			||||||
 | 
					          <form id="form" action="">
 | 
				
			||||||
 | 
					            <div class="action-select select">
 | 
				
			||||||
 | 
					              <select
 | 
				
			||||||
 | 
					                v-model="selectdAction"
 | 
				
			||||||
 | 
					                id="event"
 | 
				
			||||||
 | 
					                autocomplete="off"
 | 
				
			||||||
 | 
					                @change="actionSelected"
 | 
				
			||||||
 | 
					              >
 | 
				
			||||||
                <option value="">Select event</option>
 | 
					                <option value="">Select event</option>
 | 
				
			||||||
                <option :key="option.value" v-for="option in options" :value="option">
 | 
					                <option :key="option.value" v-for="option in options" :value="option">
 | 
				
			||||||
                  {{ option.value }}
 | 
					                  {{ option.value }}
 | 
				
			||||||
                </option>
 | 
					                </option>
 | 
				
			||||||
              </select>
 | 
					              </select>
 | 
				
			||||||
        </p>
 | 
					              <button @click.prevent.stop="sendMessage">Send</button>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
            <!-- <p><input id="room" autocomplete="off" /></p> -->
 | 
					            <!-- <p><input id="room" autocomplete="off" /></p> -->
 | 
				
			||||||
            <p>
 | 
					            <p>
 | 
				
			||||||
          <textarea v-model="data" id="message" autocomplete="off" placeholder="Data"></textarea>
 | 
					              <textarea
 | 
				
			||||||
 | 
					                v-model="data"
 | 
				
			||||||
 | 
					                id="message"
 | 
				
			||||||
 | 
					                autocomplete="off"
 | 
				
			||||||
 | 
					                placeholder="Data"
 | 
				
			||||||
 | 
					              ></textarea>
 | 
				
			||||||
            </p>
 | 
					            </p>
 | 
				
			||||||
        <p><button @click.prevent.stop="sendMessage">Send</button></p>
 | 
					          </form>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <div>
 | 
				
			||||||
          <div class="grid">
 | 
					          <div class="grid">
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
              <textarea
 | 
					              <textarea
 | 
				
			||||||
@@ -195,61 +205,9 @@ function handleResponse(response: any) {
 | 
				
			|||||||
              ></textarea>
 | 
					              ></textarea>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
      </form>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
    <div>
 | 
					 | 
				
			||||||
      <!-- <div style="display: flex; gap: 4px">
 | 
					 | 
				
			||||||
        <TileComponent :revealed="false" :top="6" :bottom="0" />
 | 
					 | 
				
			||||||
        <TileComponent :revealed="true" :top="6" :bottom="0" />
 | 
					 | 
				
			||||||
      </div> -->
 | 
					 | 
				
			||||||
      <h4>Board</h4>
 | 
					 | 
				
			||||||
      <!-- Board -->
 | 
					 | 
				
			||||||
      <div class="board-container">
 | 
					 | 
				
			||||||
        <div v-if="gameState?.tileSelectionPhase">
 | 
					 | 
				
			||||||
          <div class="tiles-container">
 | 
					 | 
				
			||||||
            <TileComponent
 | 
					 | 
				
			||||||
              :key="tile.id"
 | 
					 | 
				
			||||||
              v-for="tile in gameState.boneyard"
 | 
					 | 
				
			||||||
              @tile-clicked="selectTile(tile.id)"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
          </div>
 | 
					 | 
				
			||||||
          <div v-if="canSelectTile">Please select a tile.</div>
 | 
					 | 
				
			||||||
          <div v-else>Wait your turn.</div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
        <div v-if="!gameState?.tileSelectionPhase">
 | 
					 | 
				
			||||||
          <div class="tiles-container">
 | 
					 | 
				
			||||||
            <TileComponent
 | 
					 | 
				
			||||||
              :class="{ 'selected-tile': tileSelected === tile.id }"
 | 
					 | 
				
			||||||
              :revealed="true"
 | 
					 | 
				
			||||||
              :key="tile.id"
 | 
					 | 
				
			||||||
              v-for="tile in gameState?.board"
 | 
					 | 
				
			||||||
              :top="tile.pips[0]"
 | 
					 | 
				
			||||||
              :bottom="tile.pips[1]"
 | 
					 | 
				
			||||||
            />
 | 
					 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      </div>
 | 
					    </section>
 | 
				
			||||||
      <!-- End board-->
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <div class="hand-container">
 | 
					 | 
				
			||||||
        <h4>
 | 
					 | 
				
			||||||
          Your Hand <button class="control" @click="makeMove('left')">Left</button
 | 
					 | 
				
			||||||
          ><button class="control" @click="makeMove('right')">Right</button
 | 
					 | 
				
			||||||
          ><button class="control" @click="makeMove('pass')">Pass</button>
 | 
					 | 
				
			||||||
        </h4>
 | 
					 | 
				
			||||||
        <div class="tiles-container">
 | 
					 | 
				
			||||||
          <TileComponent
 | 
					 | 
				
			||||||
            :revealed="true"
 | 
					 | 
				
			||||||
            :key="tile.id"
 | 
					 | 
				
			||||||
            v-for="tile in playerState?.hand ?? []"
 | 
					 | 
				
			||||||
            @tile-clicked="selectTileToMove(tile.id)"
 | 
					 | 
				
			||||||
            :top="tile.pips[0]"
 | 
					 | 
				
			||||||
            :bottom="tile.pips[1]"
 | 
					 | 
				
			||||||
          />
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <!-- End hand-->
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -258,21 +216,32 @@ function handleResponse(response: any) {
 | 
				
			|||||||
  box-sizing: border-box;
 | 
					  box-sizing: border-box;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.action-select {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  gap: 16px;
 | 
				
			||||||
 | 
					  align-items: start;
 | 
				
			||||||
 | 
					  justify-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.game-container {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: start;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.control {
 | 
					.control {
 | 
				
			||||||
  font-size: 60%;
 | 
					 | 
				
			||||||
  padding: 8px;
 | 
					  padding: 8px;
 | 
				
			||||||
  margin-right: 4px;
 | 
					  margin-right: 4px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
textarea {
 | 
					textarea {
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
  height: 150px;
 | 
					  height: 110px;
 | 
				
			||||||
  resize: none;
 | 
					  resize: none;
 | 
				
			||||||
  font-size: 70%;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#response,
 | 
					#response,
 | 
				
			||||||
#status {
 | 
					#status {
 | 
				
			||||||
  height: 400px;
 | 
					  height: 180px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.tiles-container {
 | 
					.tiles-container {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,16 +9,21 @@ function startGame() {
 | 
				
			|||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="home">
 | 
					  <div class="block home">
 | 
				
			||||||
    <h1>Welcome to the Player's Home Page</h1>
 | 
					    <section class="section">
 | 
				
			||||||
 | 
					      <h1 class="title is-2">Welcome to the Player's Home Page</h1>
 | 
				
			||||||
 | 
					      <div class="block">
 | 
				
			||||||
        <p>This is a protected route.</p>
 | 
					        <p>This is a protected route.</p>
 | 
				
			||||||
    <button @click="startGame">Start Game</button>
 | 
					      </div>
 | 
				
			||||||
 | 
					      <button class="button" @click="startGame">Start Game</button>
 | 
				
			||||||
 | 
					    </section>
 | 
				
			||||||
 | 
					    <section class="section available-sessions">
 | 
				
			||||||
 | 
					      <h2 class="title is-4">Available Sessions</h2>
 | 
				
			||||||
 | 
					      <div class="bloc">
 | 
				
			||||||
 | 
					        <p>There are no available sessions at the moment.</p>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </section>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped lang="scss">
 | 
					<style scoped lang="scss"></style>
 | 
				
			||||||
.home {
 | 
					 | 
				
			||||||
  max-width: 600px;
 | 
					 | 
				
			||||||
  margin: auto;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,10 +19,28 @@ function login() {
 | 
				
			|||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="login">
 | 
					  <div class="login">
 | 
				
			||||||
    <h1>Login</h1>
 | 
					    <h1>Login</h1>
 | 
				
			||||||
    <form @submit.prevent="login">
 | 
					    <form class="form" @submit.prevent="login">
 | 
				
			||||||
      <input type="text" v-model="username" placeholder="Username" />
 | 
					      <div class="field">
 | 
				
			||||||
      <input type="password" v-model="password" placeholder="Password" />
 | 
					        <label class="label">Username</label>
 | 
				
			||||||
      <button type="submit">Login</button>
 | 
					        <div class="control">
 | 
				
			||||||
 | 
					          <input class="input" type="text" v-model="username" placeholder="Username" />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					      <div class="field">
 | 
				
			||||||
 | 
					        <label class="label">Username</label>
 | 
				
			||||||
 | 
					        <div class="control">
 | 
				
			||||||
 | 
					          <input class="input" type="password" v-model="password" placeholder="Password" />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <div class="field is-grouped">
 | 
				
			||||||
 | 
					        <div class="control">
 | 
				
			||||||
 | 
					          <button class="button is-primary" type="submit">Login</button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <!-- <div class="control">
 | 
				
			||||||
 | 
					          <button class="button">Cancel</button>
 | 
				
			||||||
 | 
					        </div> -->
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
    </form>
 | 
					    </form>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										5
									
								
								type.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								type.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					declare module 'socket.io' {
 | 
				
			||||||
 | 
					  interface Socket {
 | 
				
			||||||
 | 
					    data: any
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,20 +2,10 @@ import { fileURLToPath, URL } from 'node:url'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { defineConfig } from 'vite'
 | 
					import { defineConfig } from 'vite'
 | 
				
			||||||
import vue from '@vitejs/plugin-vue'
 | 
					import vue from '@vitejs/plugin-vue'
 | 
				
			||||||
import { isCustomElement, transformAssetUrls } from 'vue3-pixi/compiler'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// https://vitejs.dev/config/
 | 
					// https://vitejs.dev/config/
 | 
				
			||||||
export default defineConfig({
 | 
					export default defineConfig({
 | 
				
			||||||
  plugins: [
 | 
					  plugins: [vue()],
 | 
				
			||||||
    vue({
 | 
					 | 
				
			||||||
      template: {
 | 
					 | 
				
			||||||
        // support for custom elements and remove the unknown element warnings
 | 
					 | 
				
			||||||
        compilerOptions: { isCustomElement },
 | 
					 | 
				
			||||||
        // support for asset url conversion
 | 
					 | 
				
			||||||
        transformAssetUrls
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
  ],
 | 
					 | 
				
			||||||
  resolve: {
 | 
					  resolve: {
 | 
				
			||||||
    alias: {
 | 
					    alias: {
 | 
				
			||||||
      '@': fileURLToPath(new URL('./src', import.meta.url))
 | 
					      '@': fileURLToPath(new URL('./src', import.meta.url))
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user