commit 75f65e7918c72008e7f47116fa86aab87a22d8b1 Author: Horoli Date: Fri May 22 01:14:08 2026 +0900 feat: Initial project setup diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6e3c953 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +.vite/ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..2e12556 --- /dev/null +++ b/index.html @@ -0,0 +1,48 @@ + + + + + + Arena Picker + + +
+
+
+

Arena Picker

+

팀 전투 뽑기

+
+
+
+ Players + + +
+
+ Match +
+ + 5 vs 5 +
+ +
+ +
+
+
+
+
+
+
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7f633be --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1161 @@ +{ + "name": "arena-picker", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "arena-picker", + "version": "0.1.0", + "dependencies": { + "phaser": "^3.90.0" + }, + "devDependencies": { + "vite": "^7.1.12" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/phaser": { + "version": "3.90.0", + "resolved": "https://registry.npmjs.org/phaser/-/phaser-3.90.0.tgz", + "integrity": "sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f6bd740 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "arena-picker", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "phaser": "^3.90.0" + }, + "devDependencies": { + "vite": "^7.1.12" + } +} + diff --git a/public/assets/characters/archer/Archer-Attack01.png b/public/assets/characters/archer/Archer-Attack01.png new file mode 100644 index 0000000..1f3814b Binary files /dev/null and b/public/assets/characters/archer/Archer-Attack01.png differ diff --git a/public/assets/characters/archer/Archer-Attack02.png b/public/assets/characters/archer/Archer-Attack02.png new file mode 100644 index 0000000..da92e03 Binary files /dev/null and b/public/assets/characters/archer/Archer-Attack02.png differ diff --git a/public/assets/characters/archer/Archer-Death.png b/public/assets/characters/archer/Archer-Death.png new file mode 100644 index 0000000..bb7e60c Binary files /dev/null and b/public/assets/characters/archer/Archer-Death.png differ diff --git a/public/assets/characters/archer/Archer-Hurt.png b/public/assets/characters/archer/Archer-Hurt.png new file mode 100644 index 0000000..84c1035 Binary files /dev/null and b/public/assets/characters/archer/Archer-Hurt.png differ diff --git a/public/assets/characters/archer/Archer-Idle.png b/public/assets/characters/archer/Archer-Idle.png new file mode 100644 index 0000000..6a7c861 Binary files /dev/null and b/public/assets/characters/archer/Archer-Idle.png differ diff --git a/public/assets/characters/archer/Archer-Walk.png b/public/assets/characters/archer/Archer-Walk.png new file mode 100644 index 0000000..9c68da4 Binary files /dev/null and b/public/assets/characters/archer/Archer-Walk.png differ diff --git a/public/assets/characters/archer/projectiles/Arrow02(100x100).png b/public/assets/characters/archer/projectiles/Arrow02(100x100).png new file mode 100644 index 0000000..bc89f56 Binary files /dev/null and b/public/assets/characters/archer/projectiles/Arrow02(100x100).png differ diff --git a/public/assets/characters/archer/projectiles/Arrow02(32x32).png b/public/assets/characters/archer/projectiles/Arrow02(32x32).png new file mode 100644 index 0000000..53d24df Binary files /dev/null and b/public/assets/characters/archer/projectiles/Arrow02(32x32).png differ diff --git a/public/assets/characters/armored-axeman/Armored Axeman-Attack01.png b/public/assets/characters/armored-axeman/Armored Axeman-Attack01.png new file mode 100644 index 0000000..8217f0c Binary files /dev/null and b/public/assets/characters/armored-axeman/Armored Axeman-Attack01.png differ diff --git a/public/assets/characters/armored-axeman/Armored Axeman-Attack02.png b/public/assets/characters/armored-axeman/Armored Axeman-Attack02.png new file mode 100644 index 0000000..48661ae Binary files /dev/null and b/public/assets/characters/armored-axeman/Armored Axeman-Attack02.png differ diff --git a/public/assets/characters/armored-axeman/Armored Axeman-Attack03.png b/public/assets/characters/armored-axeman/Armored Axeman-Attack03.png new file mode 100644 index 0000000..37ab425 Binary files /dev/null and b/public/assets/characters/armored-axeman/Armored Axeman-Attack03.png differ diff --git a/public/assets/characters/armored-axeman/Armored Axeman-Death.png b/public/assets/characters/armored-axeman/Armored Axeman-Death.png new file mode 100644 index 0000000..be27851 Binary files /dev/null and b/public/assets/characters/armored-axeman/Armored Axeman-Death.png differ diff --git a/public/assets/characters/armored-axeman/Armored Axeman-Hurt.png b/public/assets/characters/armored-axeman/Armored Axeman-Hurt.png new file mode 100644 index 0000000..ac3d88a Binary files /dev/null and b/public/assets/characters/armored-axeman/Armored Axeman-Hurt.png differ diff --git a/public/assets/characters/armored-axeman/Armored Axeman-Idle.png b/public/assets/characters/armored-axeman/Armored Axeman-Idle.png new file mode 100644 index 0000000..de41edd Binary files /dev/null and b/public/assets/characters/armored-axeman/Armored Axeman-Idle.png differ diff --git a/public/assets/characters/armored-axeman/Armored Axeman-Walk.png b/public/assets/characters/armored-axeman/Armored Axeman-Walk.png new file mode 100644 index 0000000..e25a531 Binary files /dev/null and b/public/assets/characters/armored-axeman/Armored Axeman-Walk.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Attack01.png b/public/assets/characters/armored-orc/Armored Orc-Attack01.png new file mode 100644 index 0000000..c4a214f Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Attack01.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Attack02.png b/public/assets/characters/armored-orc/Armored Orc-Attack02.png new file mode 100644 index 0000000..33bf3d6 Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Attack02.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Attack03.png b/public/assets/characters/armored-orc/Armored Orc-Attack03.png new file mode 100644 index 0000000..fbccf8e Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Attack03.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Block.png b/public/assets/characters/armored-orc/Armored Orc-Block.png new file mode 100644 index 0000000..c880aac Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Block.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Death.png b/public/assets/characters/armored-orc/Armored Orc-Death.png new file mode 100644 index 0000000..1c8bd33 Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Death.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Hurt.png b/public/assets/characters/armored-orc/Armored Orc-Hurt.png new file mode 100644 index 0000000..aa6fe9d Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Hurt.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Idle.png b/public/assets/characters/armored-orc/Armored Orc-Idle.png new file mode 100644 index 0000000..b73696b Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Idle.png differ diff --git a/public/assets/characters/armored-orc/Armored Orc-Walk.png b/public/assets/characters/armored-orc/Armored Orc-Walk.png new file mode 100644 index 0000000..a266733 Binary files /dev/null and b/public/assets/characters/armored-orc/Armored Orc-Walk.png differ diff --git a/public/assets/characters/armored-skeleton/Armored Skeleton-Attack01.png b/public/assets/characters/armored-skeleton/Armored Skeleton-Attack01.png new file mode 100644 index 0000000..9ee1614 Binary files /dev/null and b/public/assets/characters/armored-skeleton/Armored Skeleton-Attack01.png differ diff --git a/public/assets/characters/armored-skeleton/Armored Skeleton-Attack02.png b/public/assets/characters/armored-skeleton/Armored Skeleton-Attack02.png new file mode 100644 index 0000000..9bb7965 Binary files /dev/null and b/public/assets/characters/armored-skeleton/Armored Skeleton-Attack02.png differ diff --git a/public/assets/characters/armored-skeleton/Armored Skeleton-Death.png b/public/assets/characters/armored-skeleton/Armored Skeleton-Death.png new file mode 100644 index 0000000..0354e37 Binary files /dev/null and b/public/assets/characters/armored-skeleton/Armored Skeleton-Death.png differ diff --git a/public/assets/characters/armored-skeleton/Armored Skeleton-Hurt.png b/public/assets/characters/armored-skeleton/Armored Skeleton-Hurt.png new file mode 100644 index 0000000..a2f94ae Binary files /dev/null and b/public/assets/characters/armored-skeleton/Armored Skeleton-Hurt.png differ diff --git a/public/assets/characters/armored-skeleton/Armored Skeleton-Idle.png b/public/assets/characters/armored-skeleton/Armored Skeleton-Idle.png new file mode 100644 index 0000000..e81cfd8 Binary files /dev/null and b/public/assets/characters/armored-skeleton/Armored Skeleton-Idle.png differ diff --git a/public/assets/characters/armored-skeleton/Armored Skeleton-Walk.png b/public/assets/characters/armored-skeleton/Armored Skeleton-Walk.png new file mode 100644 index 0000000..a3d43ea Binary files /dev/null and b/public/assets/characters/armored-skeleton/Armored Skeleton-Walk.png differ diff --git a/public/assets/characters/elite-orc/Elite Orc-Attack01.png b/public/assets/characters/elite-orc/Elite Orc-Attack01.png new file mode 100644 index 0000000..5b356a6 Binary files /dev/null and b/public/assets/characters/elite-orc/Elite Orc-Attack01.png differ diff --git a/public/assets/characters/elite-orc/Elite Orc-Attack02.png b/public/assets/characters/elite-orc/Elite Orc-Attack02.png new file mode 100644 index 0000000..0251cc6 Binary files /dev/null and b/public/assets/characters/elite-orc/Elite Orc-Attack02.png differ diff --git a/public/assets/characters/elite-orc/Elite Orc-Attack03.png b/public/assets/characters/elite-orc/Elite Orc-Attack03.png new file mode 100644 index 0000000..e8ffb03 Binary files /dev/null and b/public/assets/characters/elite-orc/Elite Orc-Attack03.png differ diff --git a/public/assets/characters/elite-orc/Elite Orc-Death.png b/public/assets/characters/elite-orc/Elite Orc-Death.png new file mode 100644 index 0000000..0822fbb Binary files /dev/null and b/public/assets/characters/elite-orc/Elite Orc-Death.png differ diff --git a/public/assets/characters/elite-orc/Elite Orc-Hurt.png b/public/assets/characters/elite-orc/Elite Orc-Hurt.png new file mode 100644 index 0000000..a2e6f95 Binary files /dev/null and b/public/assets/characters/elite-orc/Elite Orc-Hurt.png differ diff --git a/public/assets/characters/elite-orc/Elite Orc-Idle.png b/public/assets/characters/elite-orc/Elite Orc-Idle.png new file mode 100644 index 0000000..fb93fbc Binary files /dev/null and b/public/assets/characters/elite-orc/Elite Orc-Idle.png differ diff --git a/public/assets/characters/elite-orc/Elite Orc-Walk.png b/public/assets/characters/elite-orc/Elite Orc-Walk.png new file mode 100644 index 0000000..cdc9120 Binary files /dev/null and b/public/assets/characters/elite-orc/Elite Orc-Walk.png differ diff --git a/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack01.png b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack01.png new file mode 100644 index 0000000..505e161 Binary files /dev/null and b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack01.png differ diff --git a/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack02.png b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack02.png new file mode 100644 index 0000000..20f53e0 Binary files /dev/null and b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack02.png differ diff --git a/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack03.png b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack03.png new file mode 100644 index 0000000..ff1fff2 Binary files /dev/null and b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Attack03.png differ diff --git a/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Death.png b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Death.png new file mode 100644 index 0000000..a9ab1ba Binary files /dev/null and b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Death.png differ diff --git a/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Hurt.png b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Hurt.png new file mode 100644 index 0000000..7cbbb14 Binary files /dev/null and b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Hurt.png differ diff --git a/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Idle.png b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Idle.png new file mode 100644 index 0000000..43ac207 Binary files /dev/null and b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Idle.png differ diff --git a/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Walk.png b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Walk.png new file mode 100644 index 0000000..4d1fbd8 Binary files /dev/null and b/public/assets/characters/greatsword-skeleton/Greatsword Skeleton-Walk.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Attack01.png b/public/assets/characters/knight-templar/Knight Templar-Attack01.png new file mode 100644 index 0000000..1869378 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Attack01.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Attack02.png b/public/assets/characters/knight-templar/Knight Templar-Attack02.png new file mode 100644 index 0000000..c3cb618 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Attack02.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Attack03.png b/public/assets/characters/knight-templar/Knight Templar-Attack03.png new file mode 100644 index 0000000..d946344 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Attack03.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Block.png b/public/assets/characters/knight-templar/Knight Templar-Block.png new file mode 100644 index 0000000..eab2f7d Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Block.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Death.png b/public/assets/characters/knight-templar/Knight Templar-Death.png new file mode 100644 index 0000000..8e332b1 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Death.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Hurt.png b/public/assets/characters/knight-templar/Knight Templar-Hurt.png new file mode 100644 index 0000000..7d30400 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Hurt.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Idle.png b/public/assets/characters/knight-templar/Knight Templar-Idle.png new file mode 100644 index 0000000..f4e0a29 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Idle.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Walk01.png b/public/assets/characters/knight-templar/Knight Templar-Walk01.png new file mode 100644 index 0000000..6e2dfa1 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Walk01.png differ diff --git a/public/assets/characters/knight-templar/Knight Templar-Walk02.png b/public/assets/characters/knight-templar/Knight Templar-Walk02.png new file mode 100644 index 0000000..0b87167 Binary files /dev/null and b/public/assets/characters/knight-templar/Knight Templar-Walk02.png differ diff --git a/public/assets/characters/knight/Knight-Attack01.png b/public/assets/characters/knight/Knight-Attack01.png new file mode 100644 index 0000000..7e551dc Binary files /dev/null and b/public/assets/characters/knight/Knight-Attack01.png differ diff --git a/public/assets/characters/knight/Knight-Attack02.png b/public/assets/characters/knight/Knight-Attack02.png new file mode 100644 index 0000000..c69544b Binary files /dev/null and b/public/assets/characters/knight/Knight-Attack02.png differ diff --git a/public/assets/characters/knight/Knight-Attack03.png b/public/assets/characters/knight/Knight-Attack03.png new file mode 100644 index 0000000..95c08c5 Binary files /dev/null and b/public/assets/characters/knight/Knight-Attack03.png differ diff --git a/public/assets/characters/knight/Knight-Block.png b/public/assets/characters/knight/Knight-Block.png new file mode 100644 index 0000000..0ff2284 Binary files /dev/null and b/public/assets/characters/knight/Knight-Block.png differ diff --git a/public/assets/characters/knight/Knight-Death.png b/public/assets/characters/knight/Knight-Death.png new file mode 100644 index 0000000..8780388 Binary files /dev/null and b/public/assets/characters/knight/Knight-Death.png differ diff --git a/public/assets/characters/knight/Knight-Hurt.png b/public/assets/characters/knight/Knight-Hurt.png new file mode 100644 index 0000000..b45e146 Binary files /dev/null and b/public/assets/characters/knight/Knight-Hurt.png differ diff --git a/public/assets/characters/knight/Knight-Idle.png b/public/assets/characters/knight/Knight-Idle.png new file mode 100644 index 0000000..223241a Binary files /dev/null and b/public/assets/characters/knight/Knight-Idle.png differ diff --git a/public/assets/characters/knight/Knight-Walk.png b/public/assets/characters/knight/Knight-Walk.png new file mode 100644 index 0000000..a29876d Binary files /dev/null and b/public/assets/characters/knight/Knight-Walk.png differ diff --git a/public/assets/characters/lancer/Lancer-Attack01.png b/public/assets/characters/lancer/Lancer-Attack01.png new file mode 100644 index 0000000..47fecb5 Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Attack01.png differ diff --git a/public/assets/characters/lancer/Lancer-Attack02.png b/public/assets/characters/lancer/Lancer-Attack02.png new file mode 100644 index 0000000..3025a8c Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Attack02.png differ diff --git a/public/assets/characters/lancer/Lancer-Attack03.png b/public/assets/characters/lancer/Lancer-Attack03.png new file mode 100644 index 0000000..e81577c Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Attack03.png differ diff --git a/public/assets/characters/lancer/Lancer-Death.png b/public/assets/characters/lancer/Lancer-Death.png new file mode 100644 index 0000000..d3076ff Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Death.png differ diff --git a/public/assets/characters/lancer/Lancer-Hurt.png b/public/assets/characters/lancer/Lancer-Hurt.png new file mode 100644 index 0000000..ead4811 Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Hurt.png differ diff --git a/public/assets/characters/lancer/Lancer-Idle.png b/public/assets/characters/lancer/Lancer-Idle.png new file mode 100644 index 0000000..14bd0ce Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Idle.png differ diff --git a/public/assets/characters/lancer/Lancer-Walk01.png b/public/assets/characters/lancer/Lancer-Walk01.png new file mode 100644 index 0000000..a9c7065 Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Walk01.png differ diff --git a/public/assets/characters/lancer/Lancer-Walk02.png b/public/assets/characters/lancer/Lancer-Walk02.png new file mode 100644 index 0000000..92fb67c Binary files /dev/null and b/public/assets/characters/lancer/Lancer-Walk02.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Attack01.png b/public/assets/characters/orc-rider/Orc rider-Attack01.png new file mode 100644 index 0000000..81a5fd9 Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Attack01.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Attack02.png b/public/assets/characters/orc-rider/Orc rider-Attack02.png new file mode 100644 index 0000000..9899999 Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Attack02.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Attack03.png b/public/assets/characters/orc-rider/Orc rider-Attack03.png new file mode 100644 index 0000000..93d5b9e Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Attack03.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Block.png b/public/assets/characters/orc-rider/Orc rider-Block.png new file mode 100644 index 0000000..535f252 Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Block.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Death.png b/public/assets/characters/orc-rider/Orc rider-Death.png new file mode 100644 index 0000000..1351ce3 Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Death.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Hurt.png b/public/assets/characters/orc-rider/Orc rider-Hurt.png new file mode 100644 index 0000000..a0541ee Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Hurt.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Idle.png b/public/assets/characters/orc-rider/Orc rider-Idle.png new file mode 100644 index 0000000..4819774 Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Idle.png differ diff --git a/public/assets/characters/orc-rider/Orc rider-Walk.png b/public/assets/characters/orc-rider/Orc rider-Walk.png new file mode 100644 index 0000000..94b77c5 Binary files /dev/null and b/public/assets/characters/orc-rider/Orc rider-Walk.png differ diff --git a/public/assets/characters/orc/Orc-Attack01.png b/public/assets/characters/orc/Orc-Attack01.png new file mode 100644 index 0000000..2b68cdc Binary files /dev/null and b/public/assets/characters/orc/Orc-Attack01.png differ diff --git a/public/assets/characters/orc/Orc-Attack02.png b/public/assets/characters/orc/Orc-Attack02.png new file mode 100644 index 0000000..dbed803 Binary files /dev/null and b/public/assets/characters/orc/Orc-Attack02.png differ diff --git a/public/assets/characters/orc/Orc-Death.png b/public/assets/characters/orc/Orc-Death.png new file mode 100644 index 0000000..04bbf6b Binary files /dev/null and b/public/assets/characters/orc/Orc-Death.png differ diff --git a/public/assets/characters/orc/Orc-Hurt.png b/public/assets/characters/orc/Orc-Hurt.png new file mode 100644 index 0000000..1afe89a Binary files /dev/null and b/public/assets/characters/orc/Orc-Hurt.png differ diff --git a/public/assets/characters/orc/Orc-Idle.png b/public/assets/characters/orc/Orc-Idle.png new file mode 100644 index 0000000..508f335 Binary files /dev/null and b/public/assets/characters/orc/Orc-Idle.png differ diff --git a/public/assets/characters/orc/Orc-Walk.png b/public/assets/characters/orc/Orc-Walk.png new file mode 100644 index 0000000..f3b64ca Binary files /dev/null and b/public/assets/characters/orc/Orc-Walk.png differ diff --git a/public/assets/characters/priest/Priest-Attack.png b/public/assets/characters/priest/Priest-Attack.png new file mode 100644 index 0000000..b5d1dca Binary files /dev/null and b/public/assets/characters/priest/Priest-Attack.png differ diff --git a/public/assets/characters/priest/Priest-Death.png b/public/assets/characters/priest/Priest-Death.png new file mode 100644 index 0000000..cf42a8a Binary files /dev/null and b/public/assets/characters/priest/Priest-Death.png differ diff --git a/public/assets/characters/priest/Priest-Heal.png b/public/assets/characters/priest/Priest-Heal.png new file mode 100644 index 0000000..51260f4 Binary files /dev/null and b/public/assets/characters/priest/Priest-Heal.png differ diff --git a/public/assets/characters/priest/Priest-Hurt.png b/public/assets/characters/priest/Priest-Hurt.png new file mode 100644 index 0000000..dbb8ed9 Binary files /dev/null and b/public/assets/characters/priest/Priest-Hurt.png differ diff --git a/public/assets/characters/priest/Priest-Idle.png b/public/assets/characters/priest/Priest-Idle.png new file mode 100644 index 0000000..ed111c1 Binary files /dev/null and b/public/assets/characters/priest/Priest-Idle.png differ diff --git a/public/assets/characters/priest/Priest-Walk.png b/public/assets/characters/priest/Priest-Walk.png new file mode 100644 index 0000000..ad2f720 Binary files /dev/null and b/public/assets/characters/priest/Priest-Walk.png differ diff --git a/public/assets/characters/priest/effects/Priest-Attack_Effect.png b/public/assets/characters/priest/effects/Priest-Attack_Effect.png new file mode 100644 index 0000000..ce7c11b Binary files /dev/null and b/public/assets/characters/priest/effects/Priest-Attack_Effect.png differ diff --git a/public/assets/characters/priest/effects/Priest-Heal_Effect.png b/public/assets/characters/priest/effects/Priest-Heal_Effect.png new file mode 100644 index 0000000..9a240c5 Binary files /dev/null and b/public/assets/characters/priest/effects/Priest-Heal_Effect.png differ diff --git a/public/assets/characters/skeleton-archer/Skeleton Archer-Attack.png b/public/assets/characters/skeleton-archer/Skeleton Archer-Attack.png new file mode 100644 index 0000000..249717b Binary files /dev/null and b/public/assets/characters/skeleton-archer/Skeleton Archer-Attack.png differ diff --git a/public/assets/characters/skeleton-archer/Skeleton Archer-Death.png b/public/assets/characters/skeleton-archer/Skeleton Archer-Death.png new file mode 100644 index 0000000..6410af0 Binary files /dev/null and b/public/assets/characters/skeleton-archer/Skeleton Archer-Death.png differ diff --git a/public/assets/characters/skeleton-archer/Skeleton Archer-Hurt.png b/public/assets/characters/skeleton-archer/Skeleton Archer-Hurt.png new file mode 100644 index 0000000..7e41937 Binary files /dev/null and b/public/assets/characters/skeleton-archer/Skeleton Archer-Hurt.png differ diff --git a/public/assets/characters/skeleton-archer/Skeleton Archer-Idle.png b/public/assets/characters/skeleton-archer/Skeleton Archer-Idle.png new file mode 100644 index 0000000..bc24176 Binary files /dev/null and b/public/assets/characters/skeleton-archer/Skeleton Archer-Idle.png differ diff --git a/public/assets/characters/skeleton-archer/Skeleton Archer-Walk.png b/public/assets/characters/skeleton-archer/Skeleton Archer-Walk.png new file mode 100644 index 0000000..a8b4400 Binary files /dev/null and b/public/assets/characters/skeleton-archer/Skeleton Archer-Walk.png differ diff --git a/public/assets/characters/skeleton-archer/projectiles/Arrow03(100x100).png b/public/assets/characters/skeleton-archer/projectiles/Arrow03(100x100).png new file mode 100644 index 0000000..993347a Binary files /dev/null and b/public/assets/characters/skeleton-archer/projectiles/Arrow03(100x100).png differ diff --git a/public/assets/characters/skeleton-archer/projectiles/Arrow03(32x32).png b/public/assets/characters/skeleton-archer/projectiles/Arrow03(32x32).png new file mode 100644 index 0000000..415383c Binary files /dev/null and b/public/assets/characters/skeleton-archer/projectiles/Arrow03(32x32).png differ diff --git a/public/assets/characters/skeleton/Skeleton-Attack01.png b/public/assets/characters/skeleton/Skeleton-Attack01.png new file mode 100644 index 0000000..b51ec2a Binary files /dev/null and b/public/assets/characters/skeleton/Skeleton-Attack01.png differ diff --git a/public/assets/characters/skeleton/Skeleton-Attack02.png b/public/assets/characters/skeleton/Skeleton-Attack02.png new file mode 100644 index 0000000..85b32e8 Binary files /dev/null and b/public/assets/characters/skeleton/Skeleton-Attack02.png differ diff --git a/public/assets/characters/skeleton/Skeleton-Block.png b/public/assets/characters/skeleton/Skeleton-Block.png new file mode 100644 index 0000000..9d77b69 Binary files /dev/null and b/public/assets/characters/skeleton/Skeleton-Block.png differ diff --git a/public/assets/characters/skeleton/Skeleton-Death.png b/public/assets/characters/skeleton/Skeleton-Death.png new file mode 100644 index 0000000..6885656 Binary files /dev/null and b/public/assets/characters/skeleton/Skeleton-Death.png differ diff --git a/public/assets/characters/skeleton/Skeleton-Hurt.png b/public/assets/characters/skeleton/Skeleton-Hurt.png new file mode 100644 index 0000000..b20f07c Binary files /dev/null and b/public/assets/characters/skeleton/Skeleton-Hurt.png differ diff --git a/public/assets/characters/skeleton/Skeleton-Idle.png b/public/assets/characters/skeleton/Skeleton-Idle.png new file mode 100644 index 0000000..b185788 Binary files /dev/null and b/public/assets/characters/skeleton/Skeleton-Idle.png differ diff --git a/public/assets/characters/skeleton/Skeleton-Walk.png b/public/assets/characters/skeleton/Skeleton-Walk.png new file mode 100644 index 0000000..bba12b7 Binary files /dev/null and b/public/assets/characters/skeleton/Skeleton-Walk.png differ diff --git a/public/assets/characters/slime/Slime-Attack01.png b/public/assets/characters/slime/Slime-Attack01.png new file mode 100644 index 0000000..98c23d3 Binary files /dev/null and b/public/assets/characters/slime/Slime-Attack01.png differ diff --git a/public/assets/characters/slime/Slime-Attack02.png b/public/assets/characters/slime/Slime-Attack02.png new file mode 100644 index 0000000..d7a1199 Binary files /dev/null and b/public/assets/characters/slime/Slime-Attack02.png differ diff --git a/public/assets/characters/slime/Slime-Death.png b/public/assets/characters/slime/Slime-Death.png new file mode 100644 index 0000000..a8738dc Binary files /dev/null and b/public/assets/characters/slime/Slime-Death.png differ diff --git a/public/assets/characters/slime/Slime-Hurt.png b/public/assets/characters/slime/Slime-Hurt.png new file mode 100644 index 0000000..a5b9238 Binary files /dev/null and b/public/assets/characters/slime/Slime-Hurt.png differ diff --git a/public/assets/characters/slime/Slime-Idle.png b/public/assets/characters/slime/Slime-Idle.png new file mode 100644 index 0000000..2d732b6 Binary files /dev/null and b/public/assets/characters/slime/Slime-Idle.png differ diff --git a/public/assets/characters/slime/Slime-Walk.png b/public/assets/characters/slime/Slime-Walk.png new file mode 100644 index 0000000..6942110 Binary files /dev/null and b/public/assets/characters/slime/Slime-Walk.png differ diff --git a/public/assets/characters/soldier/Soldier-Attack01.png b/public/assets/characters/soldier/Soldier-Attack01.png new file mode 100644 index 0000000..8989ab3 Binary files /dev/null and b/public/assets/characters/soldier/Soldier-Attack01.png differ diff --git a/public/assets/characters/soldier/Soldier-Attack02.png b/public/assets/characters/soldier/Soldier-Attack02.png new file mode 100644 index 0000000..9db9257 Binary files /dev/null and b/public/assets/characters/soldier/Soldier-Attack02.png differ diff --git a/public/assets/characters/soldier/Soldier-Attack03.png b/public/assets/characters/soldier/Soldier-Attack03.png new file mode 100644 index 0000000..dab6949 Binary files /dev/null and b/public/assets/characters/soldier/Soldier-Attack03.png differ diff --git a/public/assets/characters/soldier/Soldier-Death.png b/public/assets/characters/soldier/Soldier-Death.png new file mode 100644 index 0000000..73b558e Binary files /dev/null and b/public/assets/characters/soldier/Soldier-Death.png differ diff --git a/public/assets/characters/soldier/Soldier-Hurt.png b/public/assets/characters/soldier/Soldier-Hurt.png new file mode 100644 index 0000000..27f2c4b Binary files /dev/null and b/public/assets/characters/soldier/Soldier-Hurt.png differ diff --git a/public/assets/characters/soldier/Soldier-Idle.png b/public/assets/characters/soldier/Soldier-Idle.png new file mode 100644 index 0000000..20c2267 Binary files /dev/null and b/public/assets/characters/soldier/Soldier-Idle.png differ diff --git a/public/assets/characters/soldier/Soldier-Walk.png b/public/assets/characters/soldier/Soldier-Walk.png new file mode 100644 index 0000000..230bdc6 Binary files /dev/null and b/public/assets/characters/soldier/Soldier-Walk.png differ diff --git a/public/assets/characters/soldier/projectiles/Arrow01(100x100).png b/public/assets/characters/soldier/projectiles/Arrow01(100x100).png new file mode 100644 index 0000000..010ed8f Binary files /dev/null and b/public/assets/characters/soldier/projectiles/Arrow01(100x100).png differ diff --git a/public/assets/characters/soldier/projectiles/Arrow01(32x32).png b/public/assets/characters/soldier/projectiles/Arrow01(32x32).png new file mode 100644 index 0000000..0421d40 Binary files /dev/null and b/public/assets/characters/soldier/projectiles/Arrow01(32x32).png differ diff --git a/public/assets/characters/swordsman/Swordsman-Attack01.png b/public/assets/characters/swordsman/Swordsman-Attack01.png new file mode 100644 index 0000000..cc50983 Binary files /dev/null and b/public/assets/characters/swordsman/Swordsman-Attack01.png differ diff --git a/public/assets/characters/swordsman/Swordsman-Attack02.png b/public/assets/characters/swordsman/Swordsman-Attack02.png new file mode 100644 index 0000000..03274d1 Binary files /dev/null and b/public/assets/characters/swordsman/Swordsman-Attack02.png differ diff --git a/public/assets/characters/swordsman/Swordsman-Attack3.png b/public/assets/characters/swordsman/Swordsman-Attack3.png new file mode 100644 index 0000000..43a8d25 Binary files /dev/null and b/public/assets/characters/swordsman/Swordsman-Attack3.png differ diff --git a/public/assets/characters/swordsman/Swordsman-Death.png b/public/assets/characters/swordsman/Swordsman-Death.png new file mode 100644 index 0000000..6e389f3 Binary files /dev/null and b/public/assets/characters/swordsman/Swordsman-Death.png differ diff --git a/public/assets/characters/swordsman/Swordsman-Hurt.png b/public/assets/characters/swordsman/Swordsman-Hurt.png new file mode 100644 index 0000000..5319d8e Binary files /dev/null and b/public/assets/characters/swordsman/Swordsman-Hurt.png differ diff --git a/public/assets/characters/swordsman/Swordsman-Idle.png b/public/assets/characters/swordsman/Swordsman-Idle.png new file mode 100644 index 0000000..54c0f46 Binary files /dev/null and b/public/assets/characters/swordsman/Swordsman-Idle.png differ diff --git a/public/assets/characters/swordsman/Swordsman-Walk.png b/public/assets/characters/swordsman/Swordsman-Walk.png new file mode 100644 index 0000000..58b7467 Binary files /dev/null and b/public/assets/characters/swordsman/Swordsman-Walk.png differ diff --git a/public/assets/characters/werebear/Werebear-Attack01.png b/public/assets/characters/werebear/Werebear-Attack01.png new file mode 100644 index 0000000..c84bde6 Binary files /dev/null and b/public/assets/characters/werebear/Werebear-Attack01.png differ diff --git a/public/assets/characters/werebear/Werebear-Attack02.png b/public/assets/characters/werebear/Werebear-Attack02.png new file mode 100644 index 0000000..1b1ee9f Binary files /dev/null and b/public/assets/characters/werebear/Werebear-Attack02.png differ diff --git a/public/assets/characters/werebear/Werebear-Attack03.png b/public/assets/characters/werebear/Werebear-Attack03.png new file mode 100644 index 0000000..4e71ea7 Binary files /dev/null and b/public/assets/characters/werebear/Werebear-Attack03.png differ diff --git a/public/assets/characters/werebear/Werebear-Death.png b/public/assets/characters/werebear/Werebear-Death.png new file mode 100644 index 0000000..b143261 Binary files /dev/null and b/public/assets/characters/werebear/Werebear-Death.png differ diff --git a/public/assets/characters/werebear/Werebear-Hurt.png b/public/assets/characters/werebear/Werebear-Hurt.png new file mode 100644 index 0000000..260940d Binary files /dev/null and b/public/assets/characters/werebear/Werebear-Hurt.png differ diff --git a/public/assets/characters/werebear/Werebear-Idle.png b/public/assets/characters/werebear/Werebear-Idle.png new file mode 100644 index 0000000..4843dde Binary files /dev/null and b/public/assets/characters/werebear/Werebear-Idle.png differ diff --git a/public/assets/characters/werebear/Werebear-Walk.png b/public/assets/characters/werebear/Werebear-Walk.png new file mode 100644 index 0000000..54a0761 Binary files /dev/null and b/public/assets/characters/werebear/Werebear-Walk.png differ diff --git a/public/assets/characters/werewolf/Werewolf-Attack01.png b/public/assets/characters/werewolf/Werewolf-Attack01.png new file mode 100644 index 0000000..9c8e05d Binary files /dev/null and b/public/assets/characters/werewolf/Werewolf-Attack01.png differ diff --git a/public/assets/characters/werewolf/Werewolf-Attack02.png b/public/assets/characters/werewolf/Werewolf-Attack02.png new file mode 100644 index 0000000..2d06583 Binary files /dev/null and b/public/assets/characters/werewolf/Werewolf-Attack02.png differ diff --git a/public/assets/characters/werewolf/Werewolf-Death.png b/public/assets/characters/werewolf/Werewolf-Death.png new file mode 100644 index 0000000..3699fbf Binary files /dev/null and b/public/assets/characters/werewolf/Werewolf-Death.png differ diff --git a/public/assets/characters/werewolf/Werewolf-Hurt.png b/public/assets/characters/werewolf/Werewolf-Hurt.png new file mode 100644 index 0000000..46c35f9 Binary files /dev/null and b/public/assets/characters/werewolf/Werewolf-Hurt.png differ diff --git a/public/assets/characters/werewolf/Werewolf-Idle.png b/public/assets/characters/werewolf/Werewolf-Idle.png new file mode 100644 index 0000000..16dd58a Binary files /dev/null and b/public/assets/characters/werewolf/Werewolf-Idle.png differ diff --git a/public/assets/characters/werewolf/Werewolf-Walk.png b/public/assets/characters/werewolf/Werewolf-Walk.png new file mode 100644 index 0000000..bad9182 Binary files /dev/null and b/public/assets/characters/werewolf/Werewolf-Walk.png differ diff --git a/public/assets/characters/wizard/Wizard-Attack01.png b/public/assets/characters/wizard/Wizard-Attack01.png new file mode 100644 index 0000000..519d2f6 Binary files /dev/null and b/public/assets/characters/wizard/Wizard-Attack01.png differ diff --git a/public/assets/characters/wizard/Wizard-Attack02.png b/public/assets/characters/wizard/Wizard-Attack02.png new file mode 100644 index 0000000..56ce083 Binary files /dev/null and b/public/assets/characters/wizard/Wizard-Attack02.png differ diff --git a/public/assets/characters/wizard/Wizard-DEATH.png b/public/assets/characters/wizard/Wizard-DEATH.png new file mode 100644 index 0000000..832b19d Binary files /dev/null and b/public/assets/characters/wizard/Wizard-DEATH.png differ diff --git a/public/assets/characters/wizard/Wizard-Hurt.png b/public/assets/characters/wizard/Wizard-Hurt.png new file mode 100644 index 0000000..6d4bf85 Binary files /dev/null and b/public/assets/characters/wizard/Wizard-Hurt.png differ diff --git a/public/assets/characters/wizard/Wizard-Idle.png b/public/assets/characters/wizard/Wizard-Idle.png new file mode 100644 index 0000000..0418aa5 Binary files /dev/null and b/public/assets/characters/wizard/Wizard-Idle.png differ diff --git a/public/assets/characters/wizard/Wizard-Walk.png b/public/assets/characters/wizard/Wizard-Walk.png new file mode 100644 index 0000000..bb1b82e Binary files /dev/null and b/public/assets/characters/wizard/Wizard-Walk.png differ diff --git a/public/assets/characters/wizard/effects/Wizard-Attack01_Effect.png b/public/assets/characters/wizard/effects/Wizard-Attack01_Effect.png new file mode 100644 index 0000000..62f291a Binary files /dev/null and b/public/assets/characters/wizard/effects/Wizard-Attack01_Effect.png differ diff --git a/public/assets/characters/wizard/effects/Wizard-Attack02_Effect.png b/public/assets/characters/wizard/effects/Wizard-Attack02_Effect.png new file mode 100644 index 0000000..b7f6d21 Binary files /dev/null and b/public/assets/characters/wizard/effects/Wizard-Attack02_Effect.png differ diff --git a/src/game/ArenaScene.js b/src/game/ArenaScene.js new file mode 100644 index 0000000..42523df --- /dev/null +++ b/src/game/ArenaScene.js @@ -0,0 +1,92 @@ +import Phaser from "phaser"; +import { drawArena } from "./arenaRenderer.js"; +import { clearCombatObjects, updateFighter } from "./combat.js"; +import { ARENA_SIZE } from "./config.js"; +import { createFighterAnimations, preloadFighterSheets } from "./fighterAssets.js"; +import { createFighter, syncFighterHud } from "./fighterFactory.js"; +import { fighterManifest } from "./fighterManifest.js"; +import { pickFighters } from "./fighterSelection.js"; +import { createMatchSetup, matchStatusText } from "./matchSetup.js"; + +export class ArenaScene extends Phaser.Scene { + constructor({ getInitialMatchConfig, setStatus }) { + super("arena"); + this.fighters = []; + this.getInitialMatchConfig = getInitialMatchConfig; + this.matchId = 0; + this.matchOver = false; + this.ready = false; + this.setStatus = setStatus; + this.teams = []; + } + + preload() { + preloadFighterSheets(this, fighterManifest); + } + + create() { + this.physics.world.setBounds(0, 0, ARENA_SIZE, ARENA_SIZE); + this.cameras.main.setBounds(0, 0, ARENA_SIZE, ARENA_SIZE); + this.cameras.main.setBackgroundColor("#282819"); + drawArena(this); + createFighterAnimations(this, fighterManifest); + this.ready = true; + this.startMatch(this.getInitialMatchConfig()); + } + + startMatch({ names = [], teamSize } = {}) { + if (!this.ready) { + return; + } + + if (names.length < 2) { + this.setStatus("참가자 닉네임을 2명 이상 입력하세요."); + return; + } + + const matchSetup = createMatchSetup(names, teamSize); + const matchSkins = pickFighters(fighterManifest, matchSetup.fighters.length); + + this.matchId += 1; + this.matchOver = false; + clearCombatObjects(this); + this.fighters.forEach((fighter) => fighter.destroy()); + this.teams = matchSetup.teams; + this.fighters = matchSetup.fighters.map((fighterSetup, index) => + createFighter(this, { + ...fighterSetup, + skin: matchSkins[index], + }), + ); + + this.setStatus(matchStatusText(this.teams)); + } + + update(time) { + this.fighters.forEach(syncFighterHud); + + if (this.matchOver) { + return; + } + + this.fighters.forEach((fighter) => { + updateFighter(this, fighter, time, () => this.finishMatch()); + }); + } + + finishMatch() { + const livingTeams = new Set( + this.fighters.filter((fighter) => !fighter.isDead).map((fighter) => fighter.team.id), + ); + + if (livingTeams.size > 1) { + return; + } + + const winningTeam = this.teams.find((team) => livingTeams.has(team.id)); + this.matchOver = true; + clearCombatObjects(this); + this.fighters.forEach((fighter) => fighter.body.setVelocity(0, 0)); + this.setStatus(`${winningTeam?.label ?? "Draw"} 승리`); + } +} diff --git a/src/game/arenaRenderer.js b/src/game/arenaRenderer.js new file mode 100644 index 0000000..773be24 --- /dev/null +++ b/src/game/arenaRenderer.js @@ -0,0 +1,30 @@ +import { ARENA_SIZE, GRID_SIZE, TILE_SIZE } from "./config.js"; + +export function drawArena(scene) { + const graphics = scene.add.graphics(); + graphics.fillStyle(0x34351f, 1); + graphics.fillRect(0, 0, ARENA_SIZE, ARENA_SIZE); + graphics.fillStyle(0x556235, 0.12); + + for (let row = 0; row < GRID_SIZE; row += 1) { + for (let column = 0; column < GRID_SIZE; column += 1) { + if ((row + column) % 2 === 0) { + graphics.fillRect(column * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE); + } + } + } + + graphics.lineStyle(1, 0xd3bd72, 0.11); + + for (let index = 0; index <= GRID_SIZE; index += 1) { + const offset = index * TILE_SIZE; + graphics.lineBetween(offset, 0, offset, ARENA_SIZE); + graphics.lineBetween(0, offset, ARENA_SIZE, offset); + } + + graphics.lineStyle(12, 0x17180e, 1); + graphics.strokeRect(0, 0, ARENA_SIZE, ARENA_SIZE); + graphics.lineStyle(2, 0xd3bd72, 0.35); + graphics.strokeRect(12, 12, ARENA_SIZE - 24, ARENA_SIZE - 24); +} + diff --git a/src/game/combat.js b/src/game/combat.js new file mode 100644 index 0000000..ddc2258 --- /dev/null +++ b/src/game/combat.js @@ -0,0 +1,356 @@ +import Phaser from "phaser"; +import { + ATTACK_COOLDOWN, + ATTACK_RANGE, + FIGHTER_SCALE, + MELEE_CRITICAL_CHANCE, + MOVE_SPEED, + PROJECTILE_LIFETIME, + PROJECTILE_SPEED, + RANGED_CRITICAL_CHANCE, + RANGED_ATTACK_RANGE, +} from "./config.js"; +import { + getAttackSpeedMultiplier, + getMovementSpeedMultiplier, +} from "./combatSettings.js"; +import { + fighterAnimationKey, + fighterAttackEffectAnimationKey, + fighterAttackEffectKey, + fighterProjectileKey, +} from "./fighterAssets.js"; + +const MELEE_HIT_DELAY = 260; +const PROJECTILE_FIRE_DELAY = 360; +const PROJECTILE_HIT_RADIUS = 8; +const SPELL_CAST_DELAY = 340; +const SPELL_HIT_DELAY = 160; + +export function updateFighter(scene, fighter, time, onWinner) { + const enemy = findNearestEnemy(scene.fighters, fighter); + + if (!enemy || fighter.isDead || enemy.isDead || fighter.isLocked) { + fighter.body.setVelocity(0, 0); + return; + } + + const distance = Phaser.Math.Distance.Between(fighter.x, fighter.y, enemy.x, enemy.y); + fighter.setFlipX(enemy.x < fighter.x); + + if (distance > getAttackRange(fighter)) { + scene.physics.moveToObject(fighter, enemy, MOVE_SPEED * getMovementSpeedMultiplier()); + playIfNeeded(fighter, "walk"); + return; + } + + fighter.body.setVelocity(0, 0); + + if (time >= fighter.nextAttackAt) { + beginAttack(scene, fighter, enemy, time, onWinner); + return; + } + + playIfNeeded(fighter, "idle"); +} + +export function clearCombatObjects(scene) { + scene.combatObjects?.forEach((object) => { + object.cleanup?.(); + object.destroy(); + }); + scene.combatObjects?.clear(); +} + +function beginAttack(scene, attacker, defender, time, onWinner) { + const attack = createAttackProfile(attacker); + attacker.nextAttackAt = time + scaledAttackDelay(attacker.skin.combat?.cooldown ?? ATTACK_COOLDOWN); + attacker.isLocked = true; + playAnimation(attacker, attack.animation, getAttackSpeedMultiplier()); + + switch (getCombatType(attacker)) { + case "projectile": + queueProjectile(scene, attacker, defender, onWinner); + return; + case "instant-spell": + queueInstantSpell(scene, attacker, defender, onWinner); + return; + default: + queueMeleeHit(scene, attacker, defender, onWinner, attack); + } +} + +function queueMeleeHit(scene, attacker, defender, onWinner, attack) { + const matchId = scene.matchId; + + scene.time.delayedCall(scaledAttackDelay(MELEE_HIT_DELAY), () => { + applyHit(scene, attacker, defender, onWinner, matchId, { + instantKill: attack.isCritical, + }); + }); +} + +function queueProjectile(scene, attacker, defender, onWinner) { + const matchId = scene.matchId; + + scene.time.delayedCall(scaledAttackDelay(PROJECTILE_FIRE_DELAY), () => { + if (!isAttackValid(scene, attacker, defender, matchId)) { + return; + } + + spawnProjectile(scene, attacker, defender, onWinner, matchId); + }); +} + +function queueInstantSpell(scene, attacker, defender, onWinner) { + const matchId = scene.matchId; + + scene.time.delayedCall(scaledAttackDelay(SPELL_CAST_DELAY), () => { + if (!isAttackValid(scene, attacker, defender, matchId)) { + return; + } + + spawnSpellEffect(scene, attacker, defender, onWinner, matchId); + }); +} + +function spawnProjectile(scene, attacker, defender, onWinner, matchId) { + const direction = defender.x < attacker.x ? -1 : 1; + const projectile = scene.physics.add.image( + attacker.x + direction * 42, + attacker.y + 4, + fighterProjectileKey(attacker.skin), + ); + projectile.setDepth(3); + projectile.setScale(2); + projectile.body.setCircle(PROJECTILE_HIT_RADIUS, 8, 8); + projectile.setRotation(Phaser.Math.Angle.Between(projectile.x, projectile.y, defender.x, defender.y)); + scene.physics.moveToObject( + projectile, + defender, + (attacker.skin.combat?.projectile?.speed ?? PROJECTILE_SPEED) * getAttackSpeedMultiplier(), + ); + trackCombatObject(scene, projectile); + + projectile.lastHitCheckX = projectile.x; + projectile.lastHitCheckY = projectile.y; + + const hitDefender = () => { + if (projectile.hasHit) { + return; + } + + if (!isAttackValid(scene, attacker, defender, matchId)) { + disposeCombatObject(scene, projectile); + return; + } + + projectile.hasHit = true; + disposeCombatObject(scene, projectile); + applyHit(scene, attacker, defender, onWinner, matchId); + }; + + const overlap = scene.physics.add.overlap(projectile, defender, hitDefender); + const checkProjectilePath = () => { + if (!projectile.active || projectile.hasHit) { + return; + } + + if (!isAttackValid(scene, attacker, defender, matchId)) { + disposeCombatObject(scene, projectile); + return; + } + + if (projectilePathHitsDefender(projectile, defender)) { + hitDefender(); + return; + } + + projectile.lastHitCheckX = projectile.x; + projectile.lastHitCheckY = projectile.y; + }; + + scene.events.on(Phaser.Scenes.Events.UPDATE, checkProjectilePath); + + projectile.cleanup = () => { + overlap.destroy(); + scene.events.off(Phaser.Scenes.Events.UPDATE, checkProjectilePath); + }; + + scene.time.delayedCall(PROJECTILE_LIFETIME, () => { + disposeCombatObject(scene, projectile); + }); +} + +function spawnSpellEffect(scene, attacker, defender, onWinner, matchId) { + const effect = scene.add.sprite(defender.x, defender.y, fighterAttackEffectKey(attacker.skin)); + effect.setDepth(3); + effect.setScale(FIGHTER_SCALE); + effect.play(fighterAttackEffectAnimationKey(attacker.skin)); + trackCombatObject(scene, effect); + + effect.once(Phaser.Animations.Events.ANIMATION_COMPLETE, () => { + disposeCombatObject(scene, effect); + }); + + scene.time.delayedCall(scaledAttackDelay(attacker.skin.combat?.attackEffect?.hitDelay ?? SPELL_HIT_DELAY), () => { + applyHit(scene, attacker, defender, onWinner, matchId); + }); +} + +function applyHit(scene, attacker, defender, onWinner, matchId, { instantKill = false } = {}) { + if (!isAttackValid(scene, attacker, defender, matchId)) { + return; + } + + defender.hp = instantKill ? 0 : Math.max(0, defender.hp - Phaser.Math.Between(14, 24)); + defender.body.setVelocity(0, 0); + + if (defender.hp === 0) { + killFighter(defender, attacker, onWinner); + return; + } + + defender.isLocked = true; + playAnimation(defender, "hurt"); + scene.cameras.main.shake(90, 0.002); +} + +function getAttackRange(fighter) { + if (getCombatType(fighter) === "melee") { + return ATTACK_RANGE; + } + + return fighter.skin.combat?.range ?? RANGED_ATTACK_RANGE; +} + +function getCombatType(fighter) { + return fighter.skin.combat?.type ?? "melee"; +} + +function createAttackProfile(attacker) { + const isCritical = Math.random() < getCriticalChance(attacker); + + return { + animation: + isCritical && attacker.skin.animations.attack03 ? "attack03" : "attack", + isCritical, + }; +} + +function getCriticalChance(fighter) { + if (getCombatType(fighter) !== "melee") { + return fighter.skin.combat?.criticalChance ?? RANGED_CRITICAL_CHANCE; + } + + return fighter.skin.combat?.criticalChance ?? MELEE_CRITICAL_CHANCE; +} + +function isAttackValid(scene, attacker, defender, matchId) { + return ( + !scene.matchOver && + matchId === scene.matchId && + attacker.active && + defender.active && + !attacker.isDead && + !defender.isDead + ); +} + +function projectilePathHitsDefender(projectile, defender) { + if (!defender.body) { + return false; + } + + const projectilePath = new Phaser.Geom.Line( + projectile.lastHitCheckX, + projectile.lastHitCheckY, + projectile.x, + projectile.y, + ); + const defenderHitArea = new Phaser.Geom.Rectangle( + defender.body.x - PROJECTILE_HIT_RADIUS, + defender.body.y - PROJECTILE_HIT_RADIUS, + defender.body.width + PROJECTILE_HIT_RADIUS * 2, + defender.body.height + PROJECTILE_HIT_RADIUS * 2, + ); + + return ( + Phaser.Geom.Rectangle.Contains(defenderHitArea, projectile.x, projectile.y) || + Phaser.Geom.Intersects.LineToRectangle(projectilePath, defenderHitArea) + ); +} + +function killFighter(defender, winner, onWinner) { + defender.isDead = true; + defender.isLocked = true; + defender.body.setVelocity(0, 0); + defender.body.enable = false; + defender.healthBar.width = 0; + playAnimation(defender, "death"); + winner.isLocked = false; + winner.body.setVelocity(0, 0); + playAnimation(winner, "idle"); + onWinner(winner); +} + +function findNearestEnemy(fighters, fighter) { + let nearestEnemy; + let nearestDistance = Number.POSITIVE_INFINITY; + + fighters.forEach((candidate) => { + if ( + candidate === fighter || + candidate.isDead || + candidate.team.id === fighter.team.id + ) { + return; + } + + const distance = Phaser.Math.Distance.Between( + fighter.x, + fighter.y, + candidate.x, + candidate.y, + ); + + if (distance < nearestDistance) { + nearestDistance = distance; + nearestEnemy = candidate; + } + }); + + return nearestEnemy; +} + +function playIfNeeded(fighter, action) { + const key = fighterAnimationKey(fighter.skin, action); + + if (fighter.anims.currentAnim?.key !== key) { + playAnimation(fighter, action); + } +} + +function playAnimation(fighter, action, timeScale = 1) { + fighter.anims.timeScale = timeScale; + fighter.play(fighterAnimationKey(fighter.skin, action), true); +} + +function scaledAttackDelay(duration) { + return duration / getAttackSpeedMultiplier(); +} + +function trackCombatObject(scene, object) { + scene.combatObjects ??= new Set(); + scene.combatObjects.add(object); +} + +function disposeCombatObject(scene, object) { + if (!object?.active) { + return; + } + + object.cleanup?.(); + scene.combatObjects?.delete(object); + object.destroy(); +} diff --git a/src/game/combatSettings.js b/src/game/combatSettings.js new file mode 100644 index 0000000..6c7e248 --- /dev/null +++ b/src/game/combatSettings.js @@ -0,0 +1,32 @@ +const combatSpeed = { + attack: 1, + movement: 1, +}; + +export function getAttackSpeedMultiplier() { + return combatSpeed.attack; +} + +export function getMovementSpeedMultiplier() { + return combatSpeed.movement; +} + +export function setCombatSpeedMultipliers({ attack, movement }) { + if (attack !== undefined) { + combatSpeed.attack = validMultiplier(attack); + } + + if (movement !== undefined) { + combatSpeed.movement = validMultiplier(movement); + } +} + +function validMultiplier(value) { + const multiplier = Number(value); + + if (!Number.isFinite(multiplier) || multiplier <= 0) { + throw new Error(`Invalid speed multiplier: ${value}`); + } + + return multiplier; +} diff --git a/src/game/config.js b/src/game/config.js new file mode 100644 index 0000000..b1c926d --- /dev/null +++ b/src/game/config.js @@ -0,0 +1,26 @@ +export const GRID_SIZE = 16; +export const TILE_SIZE = 64; +export const ARENA_SIZE = GRID_SIZE * TILE_SIZE; + +export const ATTACK_RANGE = 84; +export const ATTACK_COOLDOWN = 840; +export const DEFAULT_TEAM_SIZE = 5; +export const FIGHTER_SCALE = 3; +export const MAX_TEAM_SIZE = 100; +export const MELEE_CRITICAL_CHANCE = 0.05; +export const MOVE_SPEED = 148; +export const PROJECTILE_LIFETIME = 1800; +export const PROJECTILE_SPEED = 420; +export const RANGED_CRITICAL_CHANCE = 0; +export const RANGED_ATTACK_RANGE = TILE_SIZE * 5; + +export const TEAM_COLORS = [ + "#da6a48", + "#5fb4d9", + "#9bd15a", + "#d6a94a", + "#d477b8", + "#7f90e8", + "#63c5a6", + "#d98755", +]; diff --git a/src/game/fighterAssets.js b/src/game/fighterAssets.js new file mode 100644 index 0000000..a63eb61 --- /dev/null +++ b/src/game/fighterAssets.js @@ -0,0 +1,113 @@ +const animationOptions = { + attack: { frameRate: 15, repeat: 0 }, + attack02: { frameRate: 15, repeat: 0 }, + attack03: { frameRate: 15, repeat: 0 }, + block: { frameRate: 13, repeat: 0 }, + death: { frameRate: 11, repeat: 0 }, + heal: { frameRate: 13, repeat: 0 }, + hurt: { frameRate: 13, repeat: 0 }, + idle: { frameRate: 7, repeat: -1 }, + walk: { frameRate: 10, repeat: -1 }, + walk02: { frameRate: 10, repeat: -1 }, +}; + +export function fighterSheetKey(skin, action) { + return `${skin.key}-${action}`; +} + +export function fighterAnimationKey(skin, action) { + return `${fighterSheetKey(skin, action)}-anim`; +} + +export function fighterAttackEffectKey(skin) { + return `${skin.key}-attack-effect`; +} + +export function fighterAttackEffectAnimationKey(skin) { + return `${fighterAttackEffectKey(skin)}-anim`; +} + +export function fighterProjectileKey(skin) { + return `${skin.key}-projectile`; +} + +export function preloadFighterSheets(scene, skins) { + skins.forEach((skin) => { + Object.entries(skin.animations).forEach(([action, animation]) => { + scene.load.spritesheet( + fighterSheetKey(skin, action), + `${skin.assetRoot}/${animation.file}`, + { frameWidth: 100, frameHeight: 100 }, + ); + }); + + preloadCombatAssets(scene, skin); + }); +} + +export function createFighterAnimations(scene, skins) { + skins.forEach((skin) => { + Object.entries(skin.animations).forEach(([action, animation]) => { + const key = fighterAnimationKey(skin, action); + + if (scene.anims.exists(key)) { + return; + } + + const { frameRate, repeat } = animationOptions[action]; + + scene.anims.create({ + key, + frames: scene.anims.generateFrameNumbers(fighterSheetKey(skin, action), { + start: 0, + end: animation.frames - 1, + }), + frameRate, + repeat, + }); + }); + + createAttackEffectAnimation(scene, skin); + }); +} + +function preloadCombatAssets(scene, skin) { + const projectile = skin.combat?.projectile; + const attackEffect = skin.combat?.attackEffect; + + if (projectile) { + scene.load.image(fighterProjectileKey(skin), `${skin.assetRoot}/${projectile.file}`); + } + + if (attackEffect) { + scene.load.spritesheet( + fighterAttackEffectKey(skin), + `${skin.assetRoot}/${attackEffect.file}`, + { frameWidth: 100, frameHeight: 100 }, + ); + } +} + +function createAttackEffectAnimation(scene, skin) { + const attackEffect = skin.combat?.attackEffect; + + if (!attackEffect) { + return; + } + + const key = fighterAttackEffectAnimationKey(skin); + + if (scene.anims.exists(key)) { + return; + } + + scene.anims.create({ + key, + frames: scene.anims.generateFrameNumbers(fighterAttackEffectKey(skin), { + start: 0, + end: attackEffect.frames - 1, + }), + frameRate: attackEffect.frameRate ?? 14, + repeat: 0, + }); +} diff --git a/src/game/fighterFactory.js b/src/game/fighterFactory.js new file mode 100644 index 0000000..e68fcd7 --- /dev/null +++ b/src/game/fighterFactory.js @@ -0,0 +1,73 @@ +import Phaser from "phaser"; +import { FIGHTER_SCALE } from "./config.js"; +import { fighterAnimationKey, fighterSheetKey } from "./fighterAssets.js"; + +export function createFighter(scene, { faceLeft, name, skin, team, teamIndex, x, y }) { + const fighter = scene.physics.add.sprite(x, y, fighterSheetKey(skin, "idle"), 0); + fighter.setScale(FIGHTER_SCALE); + fighter.setDepth(2); + fighter.setCollideWorldBounds(true); + fighter.setFlipX(faceLeft); + fighter.body.setSize(22, 20); + fighter.body.setOffset(39, 60); + + fighter.nameLabel = scene.add + .text(x, y - 68, name, { + color: "#fff2c2", + fontFamily: "Inter, Pretendard, sans-serif", + fontSize: "18px", + fontStyle: "700", + stroke: team.color, + strokeThickness: 4, + }) + .setOrigin(0.5) + .setDepth(4); + fighter.healthBack = scene.add + .rectangle(x, y - 44, 72, 8, 0x17180e, 0.92) + .setDepth(4); + fighter.healthBar = scene.add + .rectangle(x - 34, y - 44, 68, 4, 0xd95f3f, 1) + .setOrigin(0, 0.5) + .setDepth(5); + + fighter.skin = skin; + fighter.team = team; + fighter.teamIndex = teamIndex; + fighter.hp = 100; + fighter.nextAttackAt = 0; + fighter.isLocked = false; + fighter.isDead = false; + fighter.play(fighterAnimationKey(skin, "walk")); + + fighter.on(Phaser.Animations.Events.ANIMATION_COMPLETE, (animation) => { + if (fighter.isDead) { + return; + } + + if (animation.key.includes("-attack") || animation.key.endsWith("-hurt-anim")) { + fighter.isLocked = false; + } + }); + + attachHudCleanup(fighter); + + return fighter; +} + +export function syncFighterHud(fighter) { + fighter.nameLabel.setPosition(fighter.x, fighter.y - 68); + fighter.healthBack.setPosition(fighter.x, fighter.y - 44); + fighter.healthBar.setPosition(fighter.x - 34, fighter.y - 44); + fighter.healthBar.width = Math.max(0, 68 * (fighter.hp / 100)); +} + +function attachHudCleanup(fighter) { + const originalDestroy = fighter.destroy.bind(fighter); + + fighter.destroy = (...args) => { + fighter.nameLabel.destroy(); + fighter.healthBack.destroy(); + fighter.healthBar.destroy(); + originalDestroy(...args); + }; +} diff --git a/src/game/fighterManifest.js b/src/game/fighterManifest.js new file mode 100644 index 0000000..ad20cd2 --- /dev/null +++ b/src/game/fighterManifest.js @@ -0,0 +1,327 @@ +const animation = (file, frames) => ({ file, frames }); + +export const fighterManifest = [ + { + key: "knight", + label: "Knight", + assetRoot: "assets/characters/knight", + animations: { + idle: animation("Knight-Idle.png", 6), + walk: animation("Knight-Walk.png", 8), + attack: animation("Knight-Attack01.png", 7), + attack02: animation("Knight-Attack02.png", 10), + attack03: animation("Knight-Attack03.png", 11), + block: animation("Knight-Block.png", 4), + hurt: animation("Knight-Hurt.png", 4), + death: animation("Knight-Death.png", 4), + }, + }, + { + key: "orc", + label: "Orc", + assetRoot: "assets/characters/orc", + animations: { + idle: animation("Orc-Idle.png", 6), + walk: animation("Orc-Walk.png", 8), + attack: animation("Orc-Attack01.png", 6), + attack02: animation("Orc-Attack02.png", 6), + hurt: animation("Orc-Hurt.png", 4), + death: animation("Orc-Death.png", 4), + }, + }, + { + key: "archer", + label: "Archer", + assetRoot: "assets/characters/archer", + combat: { + projectile: { + file: "projectiles/Arrow02(32x32).png", + }, + type: "projectile", + }, + animations: { + idle: animation("Archer-Idle.png", 6), + walk: animation("Archer-Walk.png", 8), + attack: animation("Archer-Attack01.png", 9), + attack02: animation("Archer-Attack02.png", 12), + hurt: animation("Archer-Hurt.png", 4), + death: animation("Archer-Death.png", 4), + }, + }, + { + key: "armored-axeman", + label: "Armored Axeman", + assetRoot: "assets/characters/armored-axeman", + animations: { + idle: animation("Armored Axeman-Idle.png", 6), + walk: animation("Armored Axeman-Walk.png", 8), + attack: animation("Armored Axeman-Attack01.png", 9), + attack02: animation("Armored Axeman-Attack02.png", 9), + attack03: animation("Armored Axeman-Attack03.png", 12), + hurt: animation("Armored Axeman-Hurt.png", 4), + death: animation("Armored Axeman-Death.png", 4), + }, + }, + { + key: "armored-orc", + label: "Armored Orc", + assetRoot: "assets/characters/armored-orc", + animations: { + idle: animation("Armored Orc-Idle.png", 6), + walk: animation("Armored Orc-Walk.png", 8), + attack: animation("Armored Orc-Attack01.png", 7), + attack02: animation("Armored Orc-Attack02.png", 8), + attack03: animation("Armored Orc-Attack03.png", 9), + block: animation("Armored Orc-Block.png", 4), + hurt: animation("Armored Orc-Hurt.png", 4), + death: animation("Armored Orc-Death.png", 4), + }, + }, + { + key: "armored-skeleton", + label: "Armored Skeleton", + assetRoot: "assets/characters/armored-skeleton", + animations: { + idle: animation("Armored Skeleton-Idle.png", 6), + walk: animation("Armored Skeleton-Walk.png", 8), + attack: animation("Armored Skeleton-Attack01.png", 8), + attack02: animation("Armored Skeleton-Attack02.png", 9), + hurt: animation("Armored Skeleton-Hurt.png", 4), + death: animation("Armored Skeleton-Death.png", 4), + }, + }, + { + key: "elite-orc", + label: "Elite Orc", + assetRoot: "assets/characters/elite-orc", + animations: { + idle: animation("Elite Orc-Idle.png", 6), + walk: animation("Elite Orc-Walk.png", 8), + attack: animation("Elite Orc-Attack01.png", 7), + attack02: animation("Elite Orc-Attack02.png", 11), + attack03: animation("Elite Orc-Attack03.png", 9), + hurt: animation("Elite Orc-Hurt.png", 4), + death: animation("Elite Orc-Death.png", 4), + }, + }, + { + key: "greatsword-skeleton", + label: "Greatsword Skeleton", + assetRoot: "assets/characters/greatsword-skeleton", + animations: { + idle: animation("Greatsword Skeleton-Idle.png", 6), + walk: animation("Greatsword Skeleton-Walk.png", 9), + attack: animation("Greatsword Skeleton-Attack01.png", 9), + attack02: animation("Greatsword Skeleton-Attack02.png", 12), + attack03: animation("Greatsword Skeleton-Attack03.png", 8), + hurt: animation("Greatsword Skeleton-Hurt.png", 4), + death: animation("Greatsword Skeleton-Death.png", 4), + }, + }, + { + key: "knight-templar", + label: "Knight Templar", + assetRoot: "assets/characters/knight-templar", + animations: { + idle: animation("Knight Templar-Idle.png", 6), + walk: animation("Knight Templar-Walk01.png", 8), + walk02: animation("Knight Templar-Walk02.png", 8), + attack: animation("Knight Templar-Attack01.png", 7), + attack02: animation("Knight Templar-Attack02.png", 8), + attack03: animation("Knight Templar-Attack03.png", 11), + block: animation("Knight Templar-Block.png", 4), + hurt: animation("Knight Templar-Hurt.png", 4), + death: animation("Knight Templar-Death.png", 4), + }, + }, + { + key: "lancer", + label: "Lancer", + assetRoot: "assets/characters/lancer", + animations: { + idle: animation("Lancer-Idle.png", 6), + walk: animation("Lancer-Walk01.png", 8), + walk02: animation("Lancer-Walk02.png", 8), + attack: animation("Lancer-Attack01.png", 6), + attack02: animation("Lancer-Attack02.png", 9), + attack03: animation("Lancer-Attack03.png", 8), + hurt: animation("Lancer-Hurt.png", 4), + death: animation("Lancer-Death.png", 4), + }, + }, + { + key: "orc-rider", + label: "Orc rider", + assetRoot: "assets/characters/orc-rider", + animations: { + idle: animation("Orc rider-Idle.png", 6), + walk: animation("Orc rider-Walk.png", 8), + attack: animation("Orc rider-Attack01.png", 8), + attack02: animation("Orc rider-Attack02.png", 9), + attack03: animation("Orc rider-Attack03.png", 11), + block: animation("Orc rider-Block.png", 4), + hurt: animation("Orc rider-Hurt.png", 4), + death: animation("Orc rider-Death.png", 4), + }, + }, + { + key: "priest", + label: "Priest", + assetRoot: "assets/characters/priest", + combat: { + attackEffect: { + file: "effects/Priest-Attack_Effect.png", + frames: 5, + }, + type: "instant-spell", + }, + animations: { + idle: animation("Priest-Idle.png", 6), + walk: animation("Priest-Walk.png", 8), + attack: animation("Priest-Attack.png", 9), + heal: animation("Priest-Heal.png", 6), + hurt: animation("Priest-Hurt.png", 4), + death: animation("Priest-Death.png", 4), + }, + }, + { + key: "skeleton", + label: "Skeleton", + assetRoot: "assets/characters/skeleton", + animations: { + idle: animation("Skeleton-Idle.png", 6), + walk: animation("Skeleton-Walk.png", 8), + attack: animation("Skeleton-Attack01.png", 6), + attack02: animation("Skeleton-Attack02.png", 7), + block: animation("Skeleton-Block.png", 4), + hurt: animation("Skeleton-Hurt.png", 4), + death: animation("Skeleton-Death.png", 4), + }, + }, + { + key: "skeleton-archer", + label: "Skeleton Archer", + assetRoot: "assets/characters/skeleton-archer", + combat: { + projectile: { + file: "projectiles/Arrow03(32x32).png", + }, + type: "projectile", + }, + animations: { + idle: animation("Skeleton Archer-Idle.png", 6), + walk: animation("Skeleton Archer-Walk.png", 8), + attack: animation("Skeleton Archer-Attack.png", 9), + hurt: animation("Skeleton Archer-Hurt.png", 4), + death: animation("Skeleton Archer-Death.png", 4), + }, + }, + { + key: "slime", + label: "Slime", + assetRoot: "assets/characters/slime", + animations: { + idle: animation("Slime-Idle.png", 6), + walk: animation("Slime-Walk.png", 6), + attack: animation("Slime-Attack01.png", 6), + attack02: animation("Slime-Attack02.png", 12), + hurt: animation("Slime-Hurt.png", 4), + death: animation("Slime-Death.png", 4), + }, + }, + { + key: "soldier-close", + label: "Soldier Close", + assetRoot: "assets/characters/soldier", + combat: { + type: "melee", + }, + animations: { + idle: animation("Soldier-Idle.png", 6), + walk: animation("Soldier-Walk.png", 8), + attack: animation("Soldier-Attack01.png", 6), + attack02: animation("Soldier-Attack02.png", 6), + hurt: animation("Soldier-Hurt.png", 4), + death: animation("Soldier-Death.png", 4), + }, + }, + { + key: "soldier-range", + label: "Soldier Range", + assetRoot: "assets/characters/soldier", + combat: { + projectile: { + file: "projectiles/Arrow01(32x32).png", + }, + type: "projectile", + }, + animations: { + idle: animation("Soldier-Idle.png", 6), + walk: animation("Soldier-Walk.png", 8), + attack: animation("Soldier-Attack03.png", 9), + hurt: animation("Soldier-Hurt.png", 4), + death: animation("Soldier-Death.png", 4), + }, + }, + { + key: "swordsman", + label: "Swordsman", + assetRoot: "assets/characters/swordsman", + animations: { + idle: animation("Swordsman-Idle.png", 6), + walk: animation("Swordsman-Walk.png", 8), + attack: animation("Swordsman-Attack01.png", 7), + attack02: animation("Swordsman-Attack02.png", 15), + attack03: animation("Swordsman-Attack3.png", 12), + hurt: animation("Swordsman-Hurt.png", 5), + death: animation("Swordsman-Death.png", 4), + }, + }, + { + key: "werebear", + label: "Werebear", + assetRoot: "assets/characters/werebear", + animations: { + idle: animation("Werebear-Idle.png", 6), + walk: animation("Werebear-Walk.png", 8), + attack: animation("Werebear-Attack01.png", 9), + attack02: animation("Werebear-Attack02.png", 13), + attack03: animation("Werebear-Attack03.png", 9), + hurt: animation("Werebear-Hurt.png", 4), + death: animation("Werebear-Death.png", 4), + }, + }, + { + key: "werewolf", + label: "Werewolf", + assetRoot: "assets/characters/werewolf", + animations: { + idle: animation("Werewolf-Idle.png", 6), + walk: animation("Werewolf-Walk.png", 8), + attack: animation("Werewolf-Attack01.png", 9), + attack02: animation("Werewolf-Attack02.png", 13), + hurt: animation("Werewolf-Hurt.png", 4), + death: animation("Werewolf-Death.png", 4), + }, + }, + { + key: "wizard", + label: "Wizard", + assetRoot: "assets/characters/wizard", + combat: { + attackEffect: { + file: "effects/Wizard-Attack01_Effect.png", + frames: 10, + }, + type: "instant-spell", + }, + animations: { + idle: animation("Wizard-Idle.png", 6), + walk: animation("Wizard-Walk.png", 8), + attack: animation("Wizard-Attack01.png", 6), + attack02: animation("Wizard-Attack02.png", 6), + hurt: animation("Wizard-Hurt.png", 4), + death: animation("Wizard-DEATH.png", 4), + }, + }, +]; diff --git a/src/game/fighterSelection.js b/src/game/fighterSelection.js new file mode 100644 index 0000000..36a0571 --- /dev/null +++ b/src/game/fighterSelection.js @@ -0,0 +1,32 @@ +export function pickUniqueFighters(fighters, count) { + if (count > fighters.length) { + throw new Error(`Cannot pick ${count} fighters from ${fighters.length} entries.`); + } + + return shuffleFighters(fighters).slice(0, count); +} + +export function pickFighters(fighters, count) { + if (fighters.length === 0) { + return []; + } + + const picks = []; + + while (picks.length < count) { + picks.push(...shuffleFighters(fighters).slice(0, count - picks.length)); + } + + return picks; +} + +function shuffleFighters(fighters) { + const pool = [...fighters]; + + for (let index = pool.length - 1; index > 0; index -= 1) { + const randomIndex = Math.floor(Math.random() * (index + 1)); + [pool[index], pool[randomIndex]] = [pool[randomIndex], pool[index]]; + } + + return pool; +} diff --git a/src/game/matchSetup.js b/src/game/matchSetup.js new file mode 100644 index 0000000..e92c973 --- /dev/null +++ b/src/game/matchSetup.js @@ -0,0 +1,114 @@ +import { + ARENA_SIZE, + DEFAULT_TEAM_SIZE, + GRID_SIZE, + MAX_TEAM_SIZE, + TEAM_COLORS, + TILE_SIZE, +} from "./config.js"; + +export function createMatchSetup(names, requestedTeamSize = DEFAULT_TEAM_SIZE) { + const shuffledNames = shuffle([...names]); + const teamSize = resolveTeamSize(shuffledNames.length, requestedTeamSize); + const teams = createTeams(shuffledNames.length, teamSize); + const spawns = createRandomSpawnPoints(shuffledNames.length); + + return { + fighters: shuffledNames.map((name, index) => { + const teamSlot = Math.floor(index / teamSize); + + return { + ...spawns[index], + name, + team: teams[teamSlot], + teamIndex: index - teamSlot * teamSize, + }; + }), + teams, + }; +} + +export function matchStatusText(teams) { + if (teams.length > 8) { + const playerCount = teams.reduce((count, team) => count + team.size, 0); + + return `${teams.length}팀 전투: 참가자 ${playerCount}명`; + } + + return `${teams.length}팀 전투: ${teams.map((team) => team.size).join(" vs ")}`; +} + +function createTeams(playerCount, teamSize) { + return Array.from({ length: Math.ceil(playerCount / teamSize) }, (_, index) => ({ + color: TEAM_COLORS[index % TEAM_COLORS.length], + id: `team-${index + 1}`, + label: `Team ${index + 1}`, + size: Math.min(teamSize, playerCount - index * teamSize), + })); +} + +function createRandomSpawnPoints(count) { + const spawnSlots = []; + + for (let row = 1; row < GRID_SIZE - 1; row += 1) { + for (let column = 0; column < GRID_SIZE; column += 1) { + spawnSlots.push({ + x: column * TILE_SIZE + TILE_SIZE / 2, + y: row * TILE_SIZE + TILE_SIZE / 2, + }); + } + } + + const points = []; + + while (points.length < count) { + shuffle([...spawnSlots]).forEach((slot) => { + if (points.length >= count) { + return; + } + + points.push({ + faceLeft: Math.random() >= 0.5, + x: clampInsideArena(slot.x + spawnJitter(), TILE_SIZE / 2), + y: clampInsideArena(slot.y + spawnJitter(), TILE_SIZE), + }); + }); + } + + return points; +} + +function resolveTeamSize(playerCount, requestedTeamSize) { + const teamSize = clamp( + Math.round(Number(requestedTeamSize) || DEFAULT_TEAM_SIZE), + 1, + MAX_TEAM_SIZE, + ); + + if (playerCount <= teamSize) { + return Math.max(1, Math.ceil(playerCount / 2)); + } + + return teamSize; +} + +function spawnJitter() { + return (Math.random() - 0.5) * TILE_SIZE * 0.36; +} + +function clampInsideArena(value, margin) { + return clamp(value, margin, ARENA_SIZE - margin); +} + +function clamp(value, minimum, maximum) { + return Math.min(maximum, Math.max(minimum, value)); +} + +function shuffle(items) { + for (let index = items.length - 1; index > 0; index -= 1) { + const randomIndex = Math.floor(Math.random() * (index + 1)); + [items[index], items[randomIndex]] = [items[randomIndex], items[index]]; + } + + return items; +} diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..99b6217 --- /dev/null +++ b/src/main.js @@ -0,0 +1,35 @@ +import Phaser from "phaser"; +import { ArenaScene } from "./game/ArenaScene.js"; +import { ARENA_SIZE } from "./game/config.js"; +import { createMatchForm } from "./ui/matchForm.js"; +import "./styles.css"; + +const matchForm = createMatchForm(); +const arenaScene = new ArenaScene({ + getInitialMatchConfig: matchForm.readMatchConfig, + setStatus: matchForm.setStatus, +}); + +const game = new Phaser.Game({ + type: Phaser.AUTO, + parent: "game", + width: ARENA_SIZE, + height: ARENA_SIZE, + pixelArt: true, + backgroundColor: "#282819", + physics: { + default: "arcade", + arcade: { + debug: false, + }, + }, + scale: { + mode: Phaser.Scale.FIT, + autoCenter: Phaser.Scale.CENTER_BOTH, + }, + scene: arenaScene, +}); + +matchForm.onSubmit((matchConfig) => arenaScene.startMatch(matchConfig)); + +window.arenaGame = game; diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..3deee51 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,226 @@ +:root { + color-scheme: dark; + font-family: + Inter, Pretendard, "Noto Sans KR", system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; + background: #141612; + color: #f6f1dd; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-width: 320px; + min-height: 100vh; +} + +button, +input, +textarea { + font: inherit; +} + +button { + border: 0; + cursor: pointer; +} + +#app { + display: grid; + min-height: 100vh; + grid-template-columns: minmax(280px, 360px) minmax(0, 1fr); + background: + linear-gradient(135deg, rgb(112 53 29 / 0.16), transparent 30%), + linear-gradient(180deg, #171912, #0d0f0c); +} + +.fighter-entry { + display: grid; + align-content: center; + gap: 28px; + padding: clamp(24px, 5vw, 48px); + border-right: 1px solid rgb(230 207 134 / 0.14); +} + +.entry-copy { + display: grid; + gap: 10px; +} + +.eyebrow { + margin: 0; + color: #d6a94a; + font-size: 0.82rem; + font-weight: 800; + text-transform: uppercase; +} + +h1 { + margin: 0; + font-size: clamp(1.8rem, 4vw, 3rem); + line-height: 1.05; + letter-spacing: 0; +} + +form { + display: grid; + gap: 16px; +} + +fieldset { + display: grid; + gap: 10px; + min-width: 0; + margin: 0; + border: 1px solid rgb(230 207 134 / 0.18); + border-radius: 8px; + padding: 12px; +} + +legend { + padding: 0 6px; + color: #d6a94a; + font-size: 0.82rem; + font-weight: 800; + text-transform: uppercase; +} + +.team-size-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +output { + min-width: 88px; + border: 1px solid rgb(230 207 134 / 0.18); + border-radius: 6px; + padding: 8px 10px; + background: #1d2017; + color: #fff7df; + text-align: center; + font-weight: 800; +} + +label { + color: #e6d7ac; + font-size: 0.92rem; +} + +input:not([type="range"]), +textarea { + min-height: 48px; + border: 1px solid rgb(230 207 134 / 0.24); + border-radius: 6px; + padding: 0 14px; + background: #26291d; + color: #fff7df; + outline: none; +} + +textarea { + min-height: 232px; + resize: vertical; + padding-block: 12px; + line-height: 1.45; +} + +input[type="range"] { + width: 100%; + accent-color: #d6a94a; +} + +input:focus, +textarea:focus { + border-color: #d6a94a; + box-shadow: 0 0 0 3px rgb(214 169 74 / 0.18); +} + +button { + min-height: 50px; + border-radius: 6px; + background: #c84f34; + color: #fff1da; + font-weight: 800; +} + +button:hover { + background: #dd6245; +} + +.arena-shell { + position: relative; + display: grid; + place-items: center; + min-height: 100vh; + overflow: hidden; + padding: clamp(16px, 3vw, 36px); +} + +#game { + width: min(100%, calc(100vh - 72px), 1080px); + aspect-ratio: 1; + overflow: hidden; + border: 1px solid rgb(245 219 136 / 0.22); + border-radius: 8px; + background: #242617; + box-shadow: + 0 24px 80px rgb(0 0 0 / 0.45), + inset 0 0 0 1px rgb(255 244 205 / 0.06); +} + +#game canvas { + display: block; + width: 100%; + height: 100%; + image-rendering: pixelated; +} + +.match-status { + position: absolute; + top: clamp(24px, 4vw, 48px); + left: 50%; + min-width: min(78vw, 340px); + transform: translateX(-50%); + border: 1px solid rgb(252 224 147 / 0.22); + border-radius: 8px; + padding: 12px 16px; + background: rgb(18 19 13 / 0.82); + color: #f8e8b5; + text-align: center; + font-weight: 800; + backdrop-filter: blur(8px); + pointer-events: none; +} + +@media (max-width: 820px) { + #app { + grid-template-columns: 1fr; + } + + .fighter-entry { + align-content: start; + gap: 18px; + padding-bottom: 18px; + border-right: 0; + border-bottom: 1px solid rgb(230 207 134 / 0.14); + } + + .arena-shell { + align-content: start; + min-height: auto; + } + + #game { + width: min(100%, calc(100svh - 360px)); + min-width: min(100%, 320px); + } + + .match-status { + top: 28px; + } +} diff --git a/src/ui/matchForm.js b/src/ui/matchForm.js new file mode 100644 index 0000000..f1c7452 --- /dev/null +++ b/src/ui/matchForm.js @@ -0,0 +1,53 @@ +const nicknameLength = 18; + +export function createMatchForm() { + const form = getElement("#fighter-form"); + const namesInput = getElement("#player-names"); + const statusNode = getElement("#match-status"); + const teamSizeInput = getElement("#team-size"); + const teamSizeOutput = getElement("#team-size-value"); + + const readMatchConfig = () => ({ + names: nicknameValues(namesInput.value), + teamSize: Number(teamSizeInput.value), + }); + + syncTeamSizeOutput(teamSizeInput, teamSizeOutput); + teamSizeInput.addEventListener("input", () => { + syncTeamSizeOutput(teamSizeInput, teamSizeOutput); + }); + + return { + onSubmit(handler) { + form.addEventListener("submit", (event) => { + event.preventDefault(); + handler(readMatchConfig()); + }); + }, + readMatchConfig, + setStatus(message) { + statusNode.textContent = message; + }, + }; +} + +function getElement(selector) { + const element = document.querySelector(selector); + + if (!element) { + throw new Error(`Missing required element: ${selector}`); + } + + return element; +} + +function nicknameValues(value) { + return value + .split(/\r?\n|,/) + .map((name) => name.trim().slice(0, nicknameLength)) + .filter(Boolean); +} + +function syncTeamSizeOutput(input, output) { + output.textContent = `${input.value} vs ${input.value}`; +} diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..e69de29