experimenting with first three scenes

This commit is contained in:
pb-coding 2023-09-21 15:12:19 +02:00
parent f525004512
commit f5d58c1541
30 changed files with 892 additions and 59 deletions

411
package-lock.json generated
View file

@ -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",

View file

@ -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",

BIN
public/models/table.glb Normal file

Binary file not shown.

BIN
public/textures/card-0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
public/textures/card-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
public/textures/card-10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
public/textures/card-11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
public/textures/card-12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
public/textures/card-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
public/textures/card-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
public/textures/card-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
public/textures/card-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
public/textures/card-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
public/textures/card-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
public/textures/card-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
public/textures/card-9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -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<string>("");
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 (
<div className="App">
<ThreeScene gameData={gameData} />
<ConnectionState
isConnected={isConnected}
session={session}
clientsInRoom={clientsInRoom}
/>
<Events events={messageEvents} />
<ConnectionManager />
<JoinSession session={session} setSession={setSession} />
{showStartGameButton && <button onClick={startGame}>Start Game</button>}
{showNextGameButton && <button onClick={nextGame}>Next Game</button>}
{gameData &&
gameData.players.map((playerData, index) => (
<div key={index}>
<p>Player ID: {playerData.id}</p>
<p>Player Name: {playerData.name}</p>
<p>Player Round Points: {playerData.roundPoints}</p>
<p>Player Total Points: {playerData.totalPoints}</p>
<table>
<thead>
<tr>
@ -155,35 +153,18 @@ export default function App() {
))}
</tbody>
</table>
<br />
<CardStack gameData={gameData} playerData={playerData} />
<DepositCards gameData={gameData} playerData={playerData} />
<CardCache playersData={playersData} />
<br />
</div>
))}
<br />
{gameData?.cardStack && remainingInCardStack > 0 && (
<div>
<Action data={playersData} action={drawFromCardStack}>
Draw
</Action>
<span style={{ color: "grey", marginLeft: "5px" }}>
{remainingInCardStack} in Card Set
</span>
</div>
)}
{gameData?.discardPile && remainingInDiscardPile > 0 && (
<div>
<Action data={playersData} action={clickDiscardPile}>
{topCardInDiscardPile?.value}
</Action>
<span style={{ color: "grey", marginLeft: "5px" }}>
{remainingInDiscardPile} Discard Pile
</span>
</div>
)}
{playersData?.cardCache && (
<div>
<p>Card Cache: {playersData.cardCache.value}</p>
</div>
)}
<br />
{messageDispaly && messageDispaly !== "" && <p>{messageDispaly}</p>}
<Events events={messageEvents} />
</div>
);
}

View file

@ -0,0 +1,21 @@
import { FC } from "react";
import { Player } from "../types/gameTypes";
type CardCacheProps = {
playersData: Player | undefined;
};
const CardCache: FC<CardCacheProps> = ({ playersData }) => {
if (!playersData?.cardCache) {
return null;
}
return (
<div>
<p>Card Cache: {playersData.cardCache.value}</p>
</div>
);
};
export default CardCache;

View file

@ -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<CardStackProps> = ({ 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 (
<Display data={playerData}>
<Action data={playerData} action={drawFromCardStack}>
Draw
</Action>
<span style={{ color: "grey", marginLeft: "5px" }}>
{remainingInCardStack} in Card Set
</span>
</Display>
);
};
export default CardStack;

View file

@ -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<DepositCardsProps> = ({ 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 (
<Display data={playerData}>
<Action data={playerData} action={clickDiscardPile}>
{topCardInDiscardPile?.value}
</Action>
<span style={{ color: "grey", marginLeft: "5px" }}>
{remainingInDiscardPile} Discard Pile
</span>
</Display>
);
};
export default DepositCards;

View file

@ -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<DisplayProps> = ({ data, children }) => {
const userSocketId = socket.id;
if (!data || userSocketId !== data.socketId) {
return null;
}
return <div>{children}</div>;
};
export default Display;

View file

@ -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<ThreeSceneProps> = ({ gameData }) => {
const containerRef = useRef<HTMLDivElement | null>(null);
const cardsRef = useRef<Mesh[]>([]);
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 <div ref={containerRef}></div>;
};
export default ThreeScene;

View file

@ -0,0 +1,3 @@
body {
margin: 0;
}

119
src/objects/cards.ts Normal file
View file

@ -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;
};

14
src/objects/cube.ts Normal file
View file

@ -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;
};

View file

View file

@ -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 = {