diff --git a/package-lock.json b/package-lock.json index 6f7e3ef..7041b7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "skyjo-fe", "version": "0.0.0", "dependencies": { + "@react-three/drei": "^9.84.1", "@react-three/fiber": "^8.14.1", "@types/three": "^0.156.0", "react": "^18.2.0", @@ -911,6 +912,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mediapipe/tasks-vision": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@mediapipe/tasks-vision/-/tasks-vision-0.10.2.tgz", + "integrity": "sha512-d8Q9uRK89ZRWmED2JLI9/blpJcfdbh0iEUuMo8TgkMzNfQBY1/GC0FEJWrairTwHkxIf6Oud1vFBP+aHicWqJA==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -946,6 +952,115 @@ "node": ">= 8" } }, + "node_modules/@react-spring/animated": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.6.1.tgz", + "integrity": "sha512-ls/rJBrAqiAYozjLo5EPPLLOb1LM0lNVQcXODTC1SMtS6DbuBCPaKco5svFUQFMP2dso3O+qcC4k9FsKc0KxMQ==", + "dependencies": { + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.6.1.tgz", + "integrity": "sha512-3HAAinAyCPessyQNNXe5W0OHzRfa8Yo5P748paPcmMowZ/4sMfaZ2ZB6e5x5khQI8NusOHj8nquoutd6FRY5WQ==", + "dependencies": { + "@react-spring/animated": "~9.6.1", + "@react-spring/rafz": "~9.6.1", + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.6.1.tgz", + "integrity": "sha512-v6qbgNRpztJFFfSE3e2W1Uz+g8KnIBs6SmzCzcVVF61GdGfGOuBrbjIcp+nUz301awVmREKi4eMQb2Ab2gGgyQ==" + }, + "node_modules/@react-spring/shared": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.6.1.tgz", + "integrity": "sha512-PBFBXabxFEuF8enNLkVqMC9h5uLRBo6GQhRMQT/nRTnemVENimgRd+0ZT4yFnAQ0AxWNiJfX3qux+bW2LbG6Bw==", + "dependencies": { + "@react-spring/rafz": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/three": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/three/-/three-9.6.1.tgz", + "integrity": "sha512-Tyw2YhZPKJAX3t2FcqvpLRb71CyTe1GvT3V+i+xJzfALgpk10uPGdGaQQ5Xrzmok1340DAeg2pR/MCfaW7b8AA==", + "dependencies": { + "@react-spring/animated": "~9.6.1", + "@react-spring/core": "~9.6.1", + "@react-spring/shared": "~9.6.1", + "@react-spring/types": "~9.6.1" + }, + "peerDependencies": { + "@react-three/fiber": ">=6.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "three": ">=0.126" + } + }, + "node_modules/@react-spring/types": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.6.1.tgz", + "integrity": "sha512-POu8Mk0hIU3lRXB3bGIGe4VHIwwDsQyoD1F394OK7STTiX9w4dG3cTLljjYswkQN+hDSHRrj4O36kuVa7KPU8Q==" + }, + "node_modules/@react-three/drei": { + "version": "9.84.1", + "resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-9.84.1.tgz", + "integrity": "sha512-0RbpRPW87OYAZcFSqnlhHitG2aXoT/52zAAxiPqoWeVXBSHogLqvTkiKPWi2iaKKBOezeGkzXsCDs3sLJ9ED/Q==", + "dependencies": { + "@babel/runtime": "^7.11.2", + "@mediapipe/tasks-vision": "0.10.2", + "@react-spring/three": "~9.6.1", + "@use-gesture/react": "^10.2.24", + "camera-controls": "^2.4.2", + "cross-env": "^7.0.3", + "detect-gpu": "^5.0.28", + "glsl-noise": "^0.0.0", + "lodash.clamp": "^4.0.3", + "lodash.omit": "^4.5.0", + "lodash.pick": "^4.4.0", + "maath": "^0.9.0", + "meshline": "^3.1.6", + "react-composer": "^5.0.3", + "react-merge-refs": "^1.1.0", + "stats-gl": "^1.0.4", + "stats.js": "^0.17.0", + "suspend-react": "^0.1.3", + "three-mesh-bvh": "^0.6.0", + "three-stdlib": "^2.25.1", + "troika-three-text": "^0.47.2", + "utility-types": "^3.10.0", + "zustand": "^3.5.13" + }, + "peerDependencies": { + "@react-three/fiber": ">=8.0", + "react": ">=18.0", + "react-dom": ">=18.0", + "three": ">=0.137" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/@react-three/fiber": { "version": "8.14.1", "resolved": "https://registry.npmjs.org/@react-three/fiber/-/fiber-8.14.1.tgz", @@ -997,6 +1112,11 @@ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" }, + "node_modules/@types/draco3d": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.4.tgz", + "integrity": "sha512-qmFieVcgNRvXphmSjnQnJGXbKq2ymE38L/22GlDD/IIzyA0dHQNI0za+RLSfyW6aG/lOoYWkGudFZnrs9PmqCw==" + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -1009,6 +1129,11 @@ "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==", "dev": true }, + "node_modules/@types/offscreencanvas": { + "version": "2019.7.1", + "resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.1.tgz", + "integrity": "sha512-+HSrJgjBW77ALieQdMJvXhRZUIRN1597L+BKvsyeiIlHHERnqjcuOLyodK3auJ3Y3zRezNKtKAhuQWYJfEgFHQ==" + }, "node_modules/@types/prop-types": { "version": "15.7.5", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", @@ -1262,6 +1387,22 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@use-gesture/core": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.0.tgz", + "integrity": "sha512-rh+6MND31zfHcy9VU3dOZCqGY511lvGcfyJenN4cWZe0u1BH6brBpBddLVXhF2r4BMqWbvxfsbL7D287thJU2A==" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.0.tgz", + "integrity": "sha512-3zc+Ve99z4usVP6l9knYVbVnZgfqhKah7sIG+PS2w+vpig2v2OLct05vs+ZXMzwxdNCMka8B+8WlOo0z6Pn6DA==", + "dependencies": { + "@use-gesture/core": "10.3.0" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.0.4.tgz", @@ -1378,6 +1519,14 @@ } ] }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1441,6 +1590,14 @@ "node": ">=6" } }, + "node_modules/camera-controls": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-2.7.2.tgz", + "integrity": "sha512-6+gaZFK3LYbWaXC94EN0BYLlvpo9xfUqwp59vsU3nV7WXIU05q4wyP5TOgyG1tqTHReuBofb20vKfZNBNjMtzw==", + "peerDependencies": { + "three": ">=0.126.1" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001533", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001533.tgz", @@ -1502,11 +1659,27 @@ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1548,6 +1721,14 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/detect-gpu": { + "version": "5.0.37", + "resolved": "https://registry.npmjs.org/detect-gpu/-/detect-gpu-5.0.37.tgz", + "integrity": "sha512-EraWs84faI4iskB4qvE39bevMIazEvd1RpoyGLOBesRLbiz6eMeJqqRPHjEFClfRByYZzi9IzU35rBXIO76oDw==", + "dependencies": { + "webgl-constants": "^1.1.1" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1572,6 +1753,11 @@ "node": ">=6.0.0" } }, + "node_modules/draco3d": { + "version": "1.5.6", + "resolved": "https://registry.npmjs.org/draco3d/-/draco3d-1.5.6.tgz", + "integrity": "sha512-+3NaRjWktb5r61ZFoDejlykPEFKT5N/LkbXsaddlw6xNSXBanUYpFc2AXXpbJDilPHazcSreU/DpQIaxfX0NfQ==" + }, "node_modules/electron-to-chromium": { "version": "1.4.515", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.515.tgz", @@ -2122,6 +2308,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/glsl-noise": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/glsl-noise/-/glsl-noise-0.0.0.tgz", + "integrity": "sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2229,8 +2420,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/its-fine": { "version": "1.1.1", @@ -2319,6 +2509,11 @@ "json-buffer": "3.0.1" } }, + "node_modules/ktx-parse": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/ktx-parse/-/ktx-parse-0.4.5.tgz", + "integrity": "sha512-MK3FOody4TXbFf8Yqv7EBbySw7aPvEcPX++Ipt6Sox+/YMFvR5xaTyhfNSk1AEmMy+RYIw81ctN4IMxCB8OAlg==" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -2347,12 +2542,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.clamp": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash.clamp/-/lodash.clamp-4.0.3.tgz", + "integrity": "sha512-HvzRFWjtcguTW7yd8NJBshuNaCa8aqNFtnswdT7f/cMd/1YKy5Zzoq4W/Oxvnx9l7aeY258uSdDfM793+eLsVg==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2373,6 +2583,15 @@ "yallist": "^3.0.2" } }, + "node_modules/maath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/maath/-/maath-0.9.0.tgz", + "integrity": "sha512-aAR8hoUqPxlsU8VOxkS9y37jhUzdUxM017NpCuxFU1Gk+nMaZASZxymZrV8LRSHzRk/watlbfyNKu6XPUhCFrQ==", + "peerDependencies": { + "@types/three": ">=0.144.0", + "three": ">=0.144.0" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2382,6 +2601,14 @@ "node": ">= 8" } }, + "node_modules/meshline": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/meshline/-/meshline-3.1.6.tgz", + "integrity": "sha512-8JZJOdaL5oz3PI/upG8JvP/5FfzYUOhrkJ8np/WKvXzl0/PZ2V9pqTvCIjSKv+w9ccg2xb+yyBhXAwt6ier3ug==", + "peerDependencies": { + "three": ">=0.137" + } + }, "node_modules/meshoptimizer": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", @@ -2412,6 +2639,11 @@ "node": "*" } }, + "node_modules/mmd-parser": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mmd-parser/-/mmd-parser-1.0.4.tgz", + "integrity": "sha512-Qi0VCU46t2IwfGv5KF0+D/t9cizcDug7qnNoy9Ggk7aucp0tssV8IwTMkBlDbm+VqAf3cdQHTCARKSsuS2MYFg==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -2447,6 +2679,14 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2456,6 +2696,21 @@ "wrappy": "1" } }, + "node_modules/opentype.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/opentype.js/-/opentype.js-1.3.4.tgz", + "integrity": "sha512-d2JE9RP/6uagpQAVtJoF0pJJA/fgai89Cc50Yp0EJHk+eLp6QQ7gBoblsnubRULNY132I0J1QKMJ+JTbMqz4sw==", + "dependencies": { + "string.prototype.codepointat": "^0.2.1", + "tiny-inflate": "^1.0.3" + }, + "bin": { + "ot": "bin/ot" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -2537,7 +2792,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -2597,6 +2851,11 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -2606,6 +2865,16 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -2646,6 +2915,17 @@ "node": ">=0.10.0" } }, + "node_modules/react-composer": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/react-composer/-/react-composer-5.0.3.tgz", + "integrity": "sha512-1uWd07EME6XZvMfapwZmc7NgCZqDemcvicRi3wMJzXsQLvZ3L7fTHVyPy1bZdnWXM4iPjYuNE+uJ41MLKeTtnA==", + "dependencies": { + "prop-types": "^15.6.0" + }, + "peerDependencies": { + "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -2666,6 +2946,20 @@ "loose-envify": "^1.1.0" } }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-merge-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/react-merge-refs/-/react-merge-refs-1.1.0.tgz", + "integrity": "sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/gregberge" + } + }, "node_modules/react-reconciler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.27.0.tgz", @@ -2707,6 +3001,14 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2825,7 +3127,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2837,7 +3138,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -2886,6 +3186,21 @@ "node": ">=0.10.0" } }, + "node_modules/stats-gl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stats-gl/-/stats-gl-1.0.5.tgz", + "integrity": "sha512-XimMxvwnf1Qf5KwebhcoA34kcX+fWEkIl0QjNkCbu4IpoyDMMsOajExn7FIq5w569k45+LhmsuRlGSrsvmGdNw==" + }, + "node_modules/stats.js": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz", + "integrity": "sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==" + }, + "node_modules/string.prototype.codepointat": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz", + "integrity": "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==" + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -2941,6 +3256,39 @@ "resolved": "https://registry.npmjs.org/three/-/three-0.156.1.tgz", "integrity": "sha512-kP7H0FK9d/k6t/XvQ9FO6i+QrePoDcNhwl0I02+wmUJRNSLCUIDMcfObnzQvxb37/0Uc9TDT0T1HgsRRrO6SYQ==" }, + "node_modules/three-mesh-bvh": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.6.7.tgz", + "integrity": "sha512-RYdjMsH+vZvjLwA+ehI4+ZqTaTehAz4iho2yfL5PdGsIHyxpB78g0iy4Emj8079m/9KBX02TzddkvPSKSruQjg==", + "peerDependencies": { + "three": ">= 0.151.0" + } + }, + "node_modules/three-stdlib": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/three-stdlib/-/three-stdlib-2.26.0.tgz", + "integrity": "sha512-zfae1OrUx7cLnH9GGW9PyIKwu7qCfEbWUk/GIT6JmEn7JZOu153mIPQxVXaJCAD6rDxb0Sr14Ab/vOIcJ7RpsA==", + "dependencies": { + "@types/draco3d": "^1.4.0", + "@types/offscreencanvas": "^2019.6.4", + "@types/webxr": "^0.5.2", + "draco3d": "^1.4.1", + "fflate": "^0.6.9", + "ktx-parse": "^0.4.5", + "mmd-parser": "^1.0.4", + "opentype.js": "^1.3.3", + "potpack": "^1.0.1", + "zstddec": "^0.0.2" + }, + "peerDependencies": { + "three": ">=0.128.0" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -2962,6 +3310,33 @@ "node": ">=8.0" } }, + "node_modules/troika-three-text": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-three-text/-/troika-three-text-0.47.2.tgz", + "integrity": "sha512-qylT0F+U7xGs+/PEf3ujBdJMYWbn0Qci0kLqI5BJG2kW1wdg4T1XSxneypnF05DxFqJhEzuaOR9S2SjiyknMng==", + "dependencies": { + "bidi-js": "^1.0.2", + "troika-three-utils": "^0.47.2", + "troika-worker-utils": "^0.47.2", + "webgl-sdf-generator": "1.1.1" + }, + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-three-utils": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-three-utils/-/troika-three-utils-0.47.2.tgz", + "integrity": "sha512-/28plhCxfKtH7MSxEGx8e3b/OXU5A0xlwl+Sbdp0H8FXUHKZDoksduEKmjQayXYtxAyuUiCRunYIv/8Vi7aiyg==", + "peerDependencies": { + "three": ">=0.125.0" + } + }, + "node_modules/troika-worker-utils": { + "version": "0.47.2", + "resolved": "https://registry.npmjs.org/troika-worker-utils/-/troika-worker-utils-0.47.2.tgz", + "integrity": "sha512-mzss4MeyzUkYBppn4x5cdAqrhBHFEuVmMMgLMTyFV23x6GvQMyo+/R5E5Lsbrt7WSt5RfvewjcwD1DChRTA9lA==" + }, "node_modules/ts-api-utils": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", @@ -3050,6 +3425,14 @@ "punycode": "^2.1.0" } }, + "node_modules/utility-types": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", + "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/vite": { "version": "4.4.9", "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz", @@ -3105,11 +3488,20 @@ } } }, + "node_modules/webgl-constants": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-constants/-/webgl-constants-1.1.1.tgz", + "integrity": "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==" + }, + "node_modules/webgl-sdf-generator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/webgl-sdf-generator/-/webgl-sdf-generator-1.1.1.tgz", + "integrity": "sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3172,6 +3564,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zstddec": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz", + "integrity": "sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==" + }, "node_modules/zustand": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", diff --git a/package.json b/package.json index 41d862c..c7d2399 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@react-three/drei": "^9.84.1", "@react-three/fiber": "^8.14.1", "@types/three": "^0.156.0", "react": "^18.2.0", diff --git a/public/models/table.glb b/public/models/table.glb new file mode 100644 index 0000000..ca4834c Binary files /dev/null and b/public/models/table.glb differ diff --git a/public/textures/card-0.png b/public/textures/card-0.png new file mode 100644 index 0000000..9999f43 Binary files /dev/null and b/public/textures/card-0.png differ diff --git a/public/textures/card-1.png b/public/textures/card-1.png new file mode 100644 index 0000000..81a7d2f Binary files /dev/null and b/public/textures/card-1.png differ diff --git a/public/textures/card-10.png b/public/textures/card-10.png new file mode 100644 index 0000000..8f76c2d Binary files /dev/null and b/public/textures/card-10.png differ diff --git a/public/textures/card-11.png b/public/textures/card-11.png new file mode 100644 index 0000000..65b6282 Binary files /dev/null and b/public/textures/card-11.png differ diff --git a/public/textures/card-12.png b/public/textures/card-12.png new file mode 100644 index 0000000..df789bf Binary files /dev/null and b/public/textures/card-12.png differ diff --git a/public/textures/card-2.png b/public/textures/card-2.png new file mode 100644 index 0000000..0e6e97b Binary files /dev/null and b/public/textures/card-2.png differ diff --git a/public/textures/card-3.png b/public/textures/card-3.png new file mode 100644 index 0000000..a293c70 Binary files /dev/null and b/public/textures/card-3.png differ diff --git a/public/textures/card-4.png b/public/textures/card-4.png new file mode 100644 index 0000000..de8a19c Binary files /dev/null and b/public/textures/card-4.png differ diff --git a/public/textures/card-5.png b/public/textures/card-5.png new file mode 100644 index 0000000..292ebdd Binary files /dev/null and b/public/textures/card-5.png differ diff --git a/public/textures/card-6.png b/public/textures/card-6.png new file mode 100644 index 0000000..302049c Binary files /dev/null and b/public/textures/card-6.png differ diff --git a/public/textures/card-7.png b/public/textures/card-7.png new file mode 100644 index 0000000..41c74ff Binary files /dev/null and b/public/textures/card-7.png differ diff --git a/public/textures/card-8.png b/public/textures/card-8.png new file mode 100644 index 0000000..9a174f0 Binary files /dev/null and b/public/textures/card-8.png differ diff --git a/public/textures/card-9.png b/public/textures/card-9.png new file mode 100644 index 0000000..76a9cf6 Binary files /dev/null and b/public/textures/card-9.png differ diff --git a/public/textures/card-back.png b/public/textures/card-back.png new file mode 100644 index 0000000..e030d8a Binary files /dev/null and b/public/textures/card-back.png differ diff --git a/public/textures/card-minus1.png b/public/textures/card-minus1.png new file mode 100644 index 0000000..7d32635 Binary files /dev/null and b/public/textures/card-minus1.png differ diff --git a/public/textures/card-minus2.png b/public/textures/card-minus2.png new file mode 100644 index 0000000..49f2a44 Binary files /dev/null and b/public/textures/card-minus2.png differ diff --git a/src/App.tsx b/src/App.tsx index abc153e..3cb566e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,11 +1,15 @@ import { useState, useEffect } from "react"; import { socket } from "./socket"; +import ThreeScene from "./components/ThreeScene"; import { ConnectionState } from "./components/ConnectionState"; import { ConnectionManager } from "./components/ConnectionManager"; import { JoinSession } from "./components/JoinSession"; import { Events } from "./components/Events"; import { Game } from "./types/gameTypes"; import Action from "./components/Action"; +import CardStack from "./components/CardStack"; +import DepositCards from "./components/DepositCards"; +import CardCache from "./components/CardCache"; export default function App() { const [isConnected, setIsConnected] = useState(socket.connected); @@ -16,11 +20,7 @@ export default function App() { const [messageDispaly, setMessageDisplay] = useState(""); const showStartGameButton = session !== "" && clientsInRoom >= 2; - - const remainingInCardStack = gameData?.cardStack?.cards?.length || 0; - const remainingInDiscardPile = gameData?.discardPile?.length || 0; - const topCardInDiscardPile = - gameData?.discardPile?.[remainingInDiscardPile - 1]; + const showNextGameButton = gameData?.phase === "new round"; const playersData = extractMyData(gameData); @@ -28,21 +28,15 @@ export default function App() { socket.emit("new-game", { sessionId: session }); } + function nextGame() { + socket.emit("next-round", { sessionId: session }); + } + function clickCard(cardPosition: number) { console.log("Clicked card", cardPosition); socket.emit("click-card", cardPosition); } - function drawFromCardStack() { - console.log("Draw card"); - socket.emit("draw-from-card-stack", { sessionId: session }); - } - - function clickDiscardPile() { - console.log("Clicked discard pile"); - socket.emit("click-discard-pile", { sessionId: session }); - } - function extractMyData(gameData: Game | null) { if (!gameData) return undefined; return gameData.players.find((player) => player.socketId === socket.id); @@ -78,8 +72,9 @@ export default function App() { setClientsInRoom(clients); } - function onMessageEvent(value: string) { - setMessageEvents((previous) => [...previous, value]); + function onMessageEvent(message: string) { + setTempMessage(message); + setMessageEvents((previous) => [...previous, message]); } function onGameUpdate(gameData: Game) { @@ -103,20 +98,23 @@ export default function App() { return (
+ - {showStartGameButton && } + {showNextGameButton && } {gameData && gameData.players.map((playerData, index) => (

Player ID: {playerData.id}

Player Name: {playerData.name}

+

Player Round Points: {playerData.roundPoints}

+

Player Total Points: {playerData.totalPoints}

@@ -155,35 +153,18 @@ export default function App() { ))}
+
+ + + +
))}
- {gameData?.cardStack && remainingInCardStack > 0 && ( -
- - Draw - - - {remainingInCardStack} in Card Set - -
- )} - {gameData?.discardPile && remainingInDiscardPile > 0 && ( -
- - {topCardInDiscardPile?.value} - - - {remainingInDiscardPile} Discard Pile - -
- )} - {playersData?.cardCache && ( -
-

Card Cache: {playersData.cardCache.value}

-
- )} +
+ {messageDispaly && messageDispaly !== "" &&

{messageDispaly}

} +
); } diff --git a/src/components/CardCache.tsx b/src/components/CardCache.tsx new file mode 100644 index 0000000..f1f068d --- /dev/null +++ b/src/components/CardCache.tsx @@ -0,0 +1,21 @@ +import { FC } from "react"; + +import { Player } from "../types/gameTypes"; + +type CardCacheProps = { + playersData: Player | undefined; +}; + +const CardCache: FC = ({ playersData }) => { + if (!playersData?.cardCache) { + return null; + } + + return ( +
+

Card Cache: {playersData.cardCache.value}

+
+ ); +}; + +export default CardCache; diff --git a/src/components/CardStack.tsx b/src/components/CardStack.tsx new file mode 100644 index 0000000..e30849c --- /dev/null +++ b/src/components/CardStack.tsx @@ -0,0 +1,37 @@ +import { FC } from "react"; +import { socket } from "../socket"; + +import Action from "./Action"; +import Display from "./Display"; +import { Game, Player } from "../types/gameTypes"; + +type CardStackProps = { + gameData: Game | null; + playerData: Player; +}; + +const CardStack: FC = ({ gameData, playerData }) => { + const remainingInCardStack = gameData?.cardStack?.cards?.length || 0; + + function drawFromCardStack() { + console.log("Draw card"); + socket.emit("draw-from-card-stack", { sessionId: gameData?.sessionId }); + } + + if (!gameData?.cardStack || remainingInCardStack <= 0) { + return null; + } + + return ( + + + Draw + + + {remainingInCardStack} in Card Set + + + ); +}; + +export default CardStack; diff --git a/src/components/DepositCards.tsx b/src/components/DepositCards.tsx new file mode 100644 index 0000000..348ff1c --- /dev/null +++ b/src/components/DepositCards.tsx @@ -0,0 +1,40 @@ +import { FC } from "react"; +import { socket } from "../socket"; + +import Action from "./Action"; +import Display from "./Display"; +import { Game, Player } from "../types/gameTypes"; + +type DepositCardsProps = { + gameData: Game | null; + playerData: Player; +}; + +const DepositCards: FC = ({ gameData, playerData }) => { + const remainingInCardStack = gameData?.cardStack?.cards?.length || 0; + const remainingInDiscardPile = gameData?.discardPile?.length || 0; + const topCardInDiscardPile = + gameData?.discardPile?.[remainingInDiscardPile - 1]; + + function clickDiscardPile() { + console.log("Clicked discard pile"); + socket.emit("click-discard-pile", { sessionId: gameData?.sessionId }); + } + + if (!gameData?.discardPile || remainingInCardStack <= 0) { + return null; + } + + return ( + + + {topCardInDiscardPile?.value} + + + {remainingInDiscardPile} Discard Pile + + + ); +}; + +export default DepositCards; diff --git a/src/components/Display.tsx b/src/components/Display.tsx new file mode 100644 index 0000000..a87019c --- /dev/null +++ b/src/components/Display.tsx @@ -0,0 +1,26 @@ +import { FC, ReactNode } from "react"; +import { socket } from "../socket"; +import { Player } from "../types/gameTypes"; + +type DisplayProps = { + data: Player | undefined; + children: ReactNode; +}; + +/** + * This component will display its children only if they are the owner. + * @param param0 + * @returns + */ + +const Display: FC = ({ data, children }) => { + const userSocketId = socket.id; + + if (!data || userSocketId !== data.socketId) { + return null; + } + + return
{children}
; +}; + +export default Display; diff --git a/src/components/ThreeScene.tsx b/src/components/ThreeScene.tsx new file mode 100644 index 0000000..e33974b --- /dev/null +++ b/src/components/ThreeScene.tsx @@ -0,0 +1,198 @@ +import { useRef, useEffect, FC } from "react"; +import { + Scene, + PerspectiveCamera, + WebGLRenderer, + Vector3, + DirectionalLight, + DirectionalLightHelper, + AmbientLight, + Object3D, + Mesh, + GridHelper, + AxesHelper, + Material, +} from "three"; +import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; +import { useGLTF } from "@react-three/drei"; +import { createCube } from "../objects/cube"; +import { createPlayerCards, createCard } from "../objects/cards"; +import { Game, Player } from "../types/gameTypes"; +import { socket } from "../socket"; + +type ThreeSceneProps = { + gameData: Game | null; +}; + +const ThreeScene: FC = ({ gameData }) => { + const containerRef = useRef(null); + const cardsRef = useRef([]); + const tableModel = useGLTF("/models/table.glb"); + const heightProportion = 1.25; + + function extractCurrentPlayer(gameData: Game | null): Player | undefined { + if (!gameData) return undefined; + return gameData.players.find((player) => player.socketId === socket.id); + } + + function disposeMesh(mesh: THREE.Mesh) { + if (mesh.material instanceof Material) { + mesh.material.dispose(); + } else if (Array.isArray(mesh.material)) { + for (const material of mesh.material) { + material.dispose(); + } + } + + mesh.geometry.dispose(); + } + + useEffect(() => { + const container = containerRef.current; + const scene = new Scene(); + const camera = new PerspectiveCamera( + 75, + window.innerWidth / (window.innerHeight / heightProportion), + 0.1, + 1000 + ); + + const renderer = new WebGLRenderer(); + + renderer.setSize(window.innerWidth, window.innerHeight / heightProportion); + container && container.appendChild(renderer.domElement); + + // Objects + const cubeCenter = createCube(new Vector3(1, 1, 1), 0x00ff00); + cubeCenter.position.set(0, 20, 0); + + const cubeTL = createCube(new Vector3(1, 1, 1), 0x00ff00); + cubeTL.position.set(-10, 20, -15); + + const cubeTR = createCube(new Vector3(1, 1, 1), 0x00ff00); + cubeTR.position.set(10, 20, -15); + + const cubeBL = createCube(new Vector3(1, 1, 1), 0x00ff00); + cubeBL.position.set(-10, 20, 15); + + const cubeBR = createCube(new Vector3(1, 1, 1), 0x00ff00); + cubeBR.position.set(10, 20, 15); + + scene.add(cubeCenter, cubeBL, cubeBR, cubeTL, cubeTR); + + // Cards + /*const initialCards: Card[] = []; + for (let i = 1; i < 13; i++) { + initialCards.push({ id: i, name: `${i} Card`, value: i as CardValue }); + } + + const playerCards = createPlayerCards(initialCards, new Vector3(0, 20, 0)); + + scene.add(...playerCards);*/ + + const currentPlayer = extractCurrentPlayer(gameData); + if (currentPlayer && !cardsRef.current.length) { + cardsRef.current = createPlayerCards( + currentPlayer.cards, + new Vector3(0, 20, 0) + ); + scene.add(...cardsRef.current); + } + + // Camera + camera.position.set(0, 45, 10); + camera.lookAt(0, 0, 0); + + // Orbit Controls + const orbit = new OrbitControls(camera, renderer.domElement); + orbit.update(); + + // Lights + const directionalLight = new DirectionalLight(0xffffff, 0.8); + directionalLight.position.set(0, 50, 20); + scene.add(directionalLight); + + directionalLight.castShadow = true; + directionalLight.shadow.mapSize.width = 1024; + directionalLight.shadow.mapSize.height = 1024; + + const ambientLight = new AmbientLight(0xa3a3a3, 0.3); + scene.add(ambientLight); + + // Import models + const table = tableModel.scene; + scene.add(table); + // table.rotateY(Math.PI / 2); + table.scale.set(2, 2, 2); + table.position.set(0, 1.8, 0); + + table.traverse(function (node: Object3D) { + if (node instanceof Mesh) { + // node.castShadow = true; + node.receiveShadow = true; + } + }); + + // Helpers + const gridHelper = new GridHelper(100, 100); + scene.add(gridHelper); + + const axesHelper = new AxesHelper(5); + scene.add(axesHelper); + + const directionalLightHelper = new DirectionalLightHelper( + directionalLight, + 5 + ); + scene.add(directionalLightHelper); + + // Animation + const animate = () => { + requestAnimationFrame(animate); + // cube.rotation.x += 0.01; + // cube.rotation.y += 0.01; + const currentPlayer = extractCurrentPlayer(gameData); + if (currentPlayer) { + if (cardsRef.current.length === 0) { + cardsRef.current = createPlayerCards( + currentPlayer.cards, + new Vector3(0, 20, 0) + ); + scene.add(...cardsRef.current); + } else { + cardsRef.current.forEach((card, index) => { + if (card.name !== currentPlayer.cards[index]?.name) { + disposeMesh(card); + scene.remove(card); + cardsRef.current[index] = createCard( + currentPlayer.cards[index], + card.position + ); + scene.add(cardsRef.current[index]); + } + }); + } + //const cards = createPlayerCards(currentPlayer.cards); + //scene.add(...cards); + } + + renderer.render(scene, camera); + }; + + console.log("Animate"); + animate(); + + return () => { + // Clean up on unmount + renderer.dispose(); + // scene.dispose(); + // material.dispose(); + // geometry.dispose(); + container && container.removeChild(renderer.domElement); + }; + }, [gameData]); + + return
; +}; + +export default ThreeScene; diff --git a/src/index.css b/src/index.css index e69de29..293d3b1 100644 --- a/src/index.css +++ b/src/index.css @@ -0,0 +1,3 @@ +body { + margin: 0; +} diff --git a/src/objects/cards.ts b/src/objects/cards.ts new file mode 100644 index 0000000..517e0b5 --- /dev/null +++ b/src/objects/cards.ts @@ -0,0 +1,119 @@ +import { + BoxGeometry, + TextureLoader, + MeshBasicMaterial, + SRGBColorSpace, + Vector3, + Mesh, + Object3DEventMap, +} from "three"; + +import { Card } from "../types/gameTypes"; + +const textureLoader = new TextureLoader(); +const cardSize = 5; +const cardGeometry = new BoxGeometry( + cardSize * 0.4, + cardSize * 0.001, + cardSize * 0.6 +); + +const getCardTexture = (value: number | string) => { + let cardTexture; + switch (value) { + case -2: + cardTexture = textureLoader.load("/textures/card-minus2.png"); + break; + case -1: + cardTexture = textureLoader.load("/textures/card-minus1.png"); + break; + case 0: + cardTexture = textureLoader.load("/textures/card-0.png"); + break; + case 1: + cardTexture = textureLoader.load("/textures/card-1.png"); + break; + case 2: + cardTexture = textureLoader.load("/textures/card-2.png"); + break; + case 3: + cardTexture = textureLoader.load("/textures/card-3.png"); + break; + case 4: + cardTexture = textureLoader.load("/textures/card-4.png"); + break; + case 5: + cardTexture = textureLoader.load("/textures/card-5.png"); + break; + case 6: + cardTexture = textureLoader.load("/textures/card-6.png"); + break; + case 7: + cardTexture = textureLoader.load("/textures/card-7.png"); + break; + case 8: + cardTexture = textureLoader.load("/textures/card-8.png"); + break; + case 9: + cardTexture = textureLoader.load("/textures/card-9.png"); + break; + case 10: + cardTexture = textureLoader.load("/textures/card-10.png"); + break; + case 11: + cardTexture = textureLoader.load("/textures/card-11.png"); + break; + case 12: + cardTexture = textureLoader.load("/textures/card-12.png"); + break; + case "X": + cardTexture = textureLoader.load("/textures/card-back.png"); + break; + default: + cardTexture = textureLoader.load("/textures/card-back.png"); + break; + } + cardTexture.colorSpace = SRGBColorSpace; + return cardTexture; +}; + +export const createCard = (cardData: Card, position: Vector3) => { + const cardMaterial = [ + new MeshBasicMaterial(), + new MeshBasicMaterial(), + new MeshBasicMaterial({ map: getCardTexture("X") }), // X = backside + new MeshBasicMaterial({ map: getCardTexture(cardData.value) }), + new MeshBasicMaterial(), + new MeshBasicMaterial(), + ]; + const card = new Mesh(cardGeometry, cardMaterial); + card.name = cardData.name; + card.position.copy(position); + return card; +}; + +export const createPlayerCards = ( + cards: Card[], + positionReference: Vector3 +) => { + const playerCards: Mesh< + BoxGeometry, + MeshBasicMaterial[], + Object3DEventMap + >[] = []; + cards.forEach((card, index) => { + const cardPositionX = positionReference.x + (index % 4) * 4 - 6; + const cardPositionY = positionReference.y; + const cardPositionZ = + positionReference.z + (Math.ceil((index + 1) / 4) - 1) * 4 - 8; + + const cardPosition = new Vector3( + cardPositionX, + cardPositionY, + cardPositionZ + ); + const playerCard = createCard(card, cardPosition); + playerCards.push(playerCard); + }); + return playerCards; +}; diff --git a/src/objects/cube.ts b/src/objects/cube.ts new file mode 100644 index 0000000..93cd2d8 --- /dev/null +++ b/src/objects/cube.ts @@ -0,0 +1,14 @@ +import { + Vector3, + BoxGeometry, + Mesh, + MeshBasicMaterial, + ColorRepresentation, +} from "three"; + +export const createCube = (size: Vector3, color: ColorRepresentation) => { + const geometry = new BoxGeometry(size.x, size.y, size.z); + const material = new MeshBasicMaterial({ color }); + const cube = new Mesh(geometry, material); + return cube; +}; diff --git a/src/objects/gameObjects.ts b/src/objects/gameObjects.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/types/gameTypes.ts b/src/types/gameTypes.ts index 8c0495d..93811b0 100644 --- a/src/types/gameTypes.ts +++ b/src/types/gameTypes.ts @@ -6,9 +6,13 @@ export type Player = { knownCardPositions: boolean[]; playersTurn: boolean; cardCache: Card | null; + tookDispiledCard: boolean; + roundPoints: number; + totalPoints: number; + closedRound: boolean; }; -type CardValue = +export type CardValue = | -2 | -1 | 0 @@ -25,19 +29,11 @@ type CardValue = | 11 | 12 | "X"; -type CardColor = - | "darkblue" - | "lightblue" - | "green" - | "yellow" - | "red" - | "black"; export type Card = { id: number; name: string; value: CardValue; - color: CardColor; }; export type CardStack = {