Scaffold tactical RPG web prototype
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.dev-server.log
|
||||
.dev-server.err
|
||||
*.local
|
||||
33
docs/roadmap.md
Normal file
33
docs/roadmap.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Heros Web Roadmap
|
||||
|
||||
## Initial Goal
|
||||
|
||||
Build a small complete tactical RPG loop:
|
||||
|
||||
1. Title screen
|
||||
2. Prologue story
|
||||
3. First battle briefing
|
||||
4. First battle
|
||||
5. Battle result and save point
|
||||
|
||||
## Current Slice
|
||||
|
||||
- Vite, TypeScript, and Phaser project foundation
|
||||
- Title scene with a new game entry point
|
||||
- Prologue dialogue scene backed by scenario data
|
||||
- First battle scene with a 12x12 map, terrain colors, unit placement, and movement range preview
|
||||
- Flow verification script from title to first battle
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Add selectable commands: move, attack, wait
|
||||
2. Implement grid movement and occupied-tile blocking
|
||||
3. Implement attack range, damage, defeat, and victory checks
|
||||
4. Add a simple enemy AI turn
|
||||
5. Add battle result scene and local save/load
|
||||
|
||||
## Content Direction
|
||||
|
||||
- Use original names, portraits, maps, and dialogue inspired by historical tactical RPGs.
|
||||
- Keep copyrighted scripts, sprites, portraits, music, and maps out of the repository.
|
||||
- Prefer data-driven scenarios so new chapters and battlefields can be added without rewriting scene code.
|
||||
12
index.html
Normal file
12
index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Heros Web</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="game"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
21
package.json
Normal file
21
package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "heros-web",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host 0.0.0.0",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview --host 0.0.0.0",
|
||||
"verify:flow": "node scripts/verify-flow.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"phaser": "^3.90.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.4",
|
||||
"playwright": "^1.61.0",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.3.5"
|
||||
}
|
||||
}
|
||||
725
pnpm-lock.yaml
generated
Normal file
725
pnpm-lock.yaml
generated
Normal file
@@ -0,0 +1,725 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
phaser:
|
||||
specifier: ^3.90.0
|
||||
version: 3.90.0
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^24.0.4
|
||||
version: 24.13.2
|
||||
playwright:
|
||||
specifier: ^1.61.0
|
||||
version: 1.61.0
|
||||
typescript:
|
||||
specifier: ^5.8.3
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: ^6.3.5
|
||||
version: 6.4.3(@types/node@24.13.2)
|
||||
|
||||
packages:
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.12':
|
||||
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.25.12':
|
||||
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.25.12':
|
||||
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.25.12':
|
||||
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.12':
|
||||
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.25.12':
|
||||
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.25.12':
|
||||
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.25.12':
|
||||
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.12':
|
||||
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.12':
|
||||
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.12':
|
||||
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.25.12':
|
||||
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.25.12':
|
||||
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.12':
|
||||
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.12':
|
||||
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.25.12':
|
||||
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.25.12':
|
||||
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.25.12':
|
||||
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.62.2':
|
||||
resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-android-arm64@4.62.2':
|
||||
resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.62.2':
|
||||
resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.62.2':
|
||||
resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.62.2':
|
||||
resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.62.2':
|
||||
resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.62.2':
|
||||
resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.62.2':
|
||||
resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.62.2':
|
||||
resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.62.2':
|
||||
resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.62.2':
|
||||
resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.62.2':
|
||||
resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.62.2':
|
||||
resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.62.2':
|
||||
resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.62.2':
|
||||
resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.62.2':
|
||||
resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.62.2':
|
||||
resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.62.2':
|
||||
resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.62.2':
|
||||
resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.62.2':
|
||||
resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.62.2':
|
||||
resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.62.2':
|
||||
resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.62.2':
|
||||
resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.62.2':
|
||||
resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.62.2':
|
||||
resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/estree@1.0.9':
|
||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||
|
||||
'@types/node@24.13.2':
|
||||
resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==}
|
||||
|
||||
esbuild@0.25.12:
|
||||
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
eventemitter3@5.0.4:
|
||||
resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
nanoid@3.3.13:
|
||||
resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
phaser@3.90.0:
|
||||
resolution: {integrity: sha512-/cziz/5ZIn02uDkC9RzN8VF9x3Gs3XdFFf9nkiMEQT3p7hQlWuyjy4QWosU802qqno2YSLn2BfqwOKLv/sSVfQ==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
picomatch@4.0.4:
|
||||
resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
playwright-core@1.61.0:
|
||||
resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
playwright@1.61.0:
|
||||
resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
postcss@8.5.15:
|
||||
resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
rollup@4.62.2:
|
||||
resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
undici-types@7.18.2:
|
||||
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
|
||||
|
||||
vite@6.4.3:
|
||||
resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==}
|
||||
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||
jiti: '>=1.21.0'
|
||||
less: '*'
|
||||
lightningcss: ^1.21.0
|
||||
sass: '*'
|
||||
sass-embedded: '*'
|
||||
stylus: '*'
|
||||
sugarss: '*'
|
||||
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
|
||||
|
||||
snapshots:
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm64@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-openharmony-arm64@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-gnu@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@types/estree@1.0.9': {}
|
||||
|
||||
'@types/node@24.13.2':
|
||||
dependencies:
|
||||
undici-types: 7.18.2
|
||||
|
||||
esbuild@0.25.12:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.25.12
|
||||
'@esbuild/android-arm': 0.25.12
|
||||
'@esbuild/android-arm64': 0.25.12
|
||||
'@esbuild/android-x64': 0.25.12
|
||||
'@esbuild/darwin-arm64': 0.25.12
|
||||
'@esbuild/darwin-x64': 0.25.12
|
||||
'@esbuild/freebsd-arm64': 0.25.12
|
||||
'@esbuild/freebsd-x64': 0.25.12
|
||||
'@esbuild/linux-arm': 0.25.12
|
||||
'@esbuild/linux-arm64': 0.25.12
|
||||
'@esbuild/linux-ia32': 0.25.12
|
||||
'@esbuild/linux-loong64': 0.25.12
|
||||
'@esbuild/linux-mips64el': 0.25.12
|
||||
'@esbuild/linux-ppc64': 0.25.12
|
||||
'@esbuild/linux-riscv64': 0.25.12
|
||||
'@esbuild/linux-s390x': 0.25.12
|
||||
'@esbuild/linux-x64': 0.25.12
|
||||
'@esbuild/netbsd-arm64': 0.25.12
|
||||
'@esbuild/netbsd-x64': 0.25.12
|
||||
'@esbuild/openbsd-arm64': 0.25.12
|
||||
'@esbuild/openbsd-x64': 0.25.12
|
||||
'@esbuild/openharmony-arm64': 0.25.12
|
||||
'@esbuild/sunos-x64': 0.25.12
|
||||
'@esbuild/win32-arm64': 0.25.12
|
||||
'@esbuild/win32-ia32': 0.25.12
|
||||
'@esbuild/win32-x64': 0.25.12
|
||||
|
||||
eventemitter3@5.0.4: {}
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.4):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.4
|
||||
|
||||
fsevents@2.3.2:
|
||||
optional: true
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
nanoid@3.3.13: {}
|
||||
|
||||
phaser@3.90.0:
|
||||
dependencies:
|
||||
eventemitter3: 5.0.4
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@4.0.4: {}
|
||||
|
||||
playwright-core@1.61.0: {}
|
||||
|
||||
playwright@1.61.0:
|
||||
dependencies:
|
||||
playwright-core: 1.61.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
|
||||
postcss@8.5.15:
|
||||
dependencies:
|
||||
nanoid: 3.3.13
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
rollup@4.62.2:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.9
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.62.2
|
||||
'@rollup/rollup-android-arm64': 4.62.2
|
||||
'@rollup/rollup-darwin-arm64': 4.62.2
|
||||
'@rollup/rollup-darwin-x64': 4.62.2
|
||||
'@rollup/rollup-freebsd-arm64': 4.62.2
|
||||
'@rollup/rollup-freebsd-x64': 4.62.2
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.62.2
|
||||
'@rollup/rollup-linux-arm-musleabihf': 4.62.2
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.62.2
|
||||
'@rollup/rollup-linux-arm64-musl': 4.62.2
|
||||
'@rollup/rollup-linux-loong64-gnu': 4.62.2
|
||||
'@rollup/rollup-linux-loong64-musl': 4.62.2
|
||||
'@rollup/rollup-linux-ppc64-gnu': 4.62.2
|
||||
'@rollup/rollup-linux-ppc64-musl': 4.62.2
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.62.2
|
||||
'@rollup/rollup-linux-riscv64-musl': 4.62.2
|
||||
'@rollup/rollup-linux-s390x-gnu': 4.62.2
|
||||
'@rollup/rollup-linux-x64-gnu': 4.62.2
|
||||
'@rollup/rollup-linux-x64-musl': 4.62.2
|
||||
'@rollup/rollup-openbsd-x64': 4.62.2
|
||||
'@rollup/rollup-openharmony-arm64': 4.62.2
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.62.2
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.62.2
|
||||
'@rollup/rollup-win32-x64-gnu': 4.62.2
|
||||
'@rollup/rollup-win32-x64-msvc': 4.62.2
|
||||
fsevents: 2.3.3
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
tinyglobby@0.2.17:
|
||||
dependencies:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@7.18.2: {}
|
||||
|
||||
vite@6.4.3(@types/node@24.13.2):
|
||||
dependencies:
|
||||
esbuild: 0.25.12
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
postcss: 8.5.15
|
||||
rollup: 4.62.2
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 24.13.2
|
||||
fsevents: 2.3.3
|
||||
2
pnpm-workspace.yaml
Normal file
2
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
48
scripts/verify-flow.mjs
Normal file
48
scripts/verify-flow.mjs
Normal file
@@ -0,0 +1,48 @@
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
const targetUrl = process.env.VERIFY_URL ?? 'http://localhost:5173/';
|
||||
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
|
||||
|
||||
await page.goto(targetUrl, { waitUntil: 'networkidle' });
|
||||
await page.waitForSelector('canvas');
|
||||
await page.waitForFunction(() => window.__HEROS_GAME__ !== undefined);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
await page.mouse.click(170, 320);
|
||||
await page.waitForTimeout(250);
|
||||
|
||||
for (let i = 0; i < 4; i += 1) {
|
||||
await page.keyboard.press('Space');
|
||||
}
|
||||
|
||||
await page.waitForFunction(() => {
|
||||
const activeScenes = window.__HEROS_GAME__?.scene.getScenes(true) ?? [];
|
||||
return activeScenes.some((scene) => scene.scene.key === 'BattleScene');
|
||||
});
|
||||
|
||||
await page.screenshot({ path: 'dist/verification-battle.png', fullPage: true });
|
||||
|
||||
const result = await page.evaluate(() => {
|
||||
const canvas = document.querySelector('canvas');
|
||||
const activeScenes = window.__HEROS_GAME__?.scene.getScenes(true).map((scene) => scene.scene.key) ?? [];
|
||||
|
||||
return {
|
||||
activeScenes,
|
||||
canvasWidth: canvas?.width ?? 0,
|
||||
canvasHeight: canvas?.height ?? 0
|
||||
};
|
||||
});
|
||||
|
||||
await browser.close();
|
||||
|
||||
if (result.canvasWidth !== 1280 || result.canvasHeight !== 720) {
|
||||
throw new Error(`Unexpected canvas size: ${result.canvasWidth}x${result.canvasHeight}`);
|
||||
}
|
||||
|
||||
if (!result.activeScenes.includes('BattleScene')) {
|
||||
throw new Error(`BattleScene was not active. Active scenes: ${result.activeScenes.join(', ')}`);
|
||||
}
|
||||
|
||||
console.log(`Verified title-to-battle flow at ${targetUrl}`);
|
||||
78
src/game/data/scenario.ts
Normal file
78
src/game/data/scenario.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
export type StoryLine = {
|
||||
speaker: string;
|
||||
portrait: 'lord' | 'strategist' | 'captain';
|
||||
text: string;
|
||||
};
|
||||
|
||||
export type UnitData = {
|
||||
id: string;
|
||||
name: string;
|
||||
faction: 'ally' | 'enemy';
|
||||
className: string;
|
||||
hp: number;
|
||||
maxHp: number;
|
||||
attack: number;
|
||||
move: number;
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
export type TerrainType = 'plain' | 'forest' | 'hill' | 'fort';
|
||||
|
||||
export type BattleMap = {
|
||||
width: number;
|
||||
height: number;
|
||||
terrain: TerrainType[][];
|
||||
};
|
||||
|
||||
export const prologueLines: StoryLine[] = [
|
||||
{
|
||||
speaker: '유현',
|
||||
portrait: 'lord',
|
||||
text: '황건의 깃발이 마을 어귀까지 내려왔다. 오늘 물러서면 내일은 지킬 집도 없다.'
|
||||
},
|
||||
{
|
||||
speaker: '서윤',
|
||||
portrait: 'strategist',
|
||||
text: '적은 많지만 대열은 흐트러져 있습니다. 숲길을 잡으면 첫 싸움은 해볼 만합니다.'
|
||||
},
|
||||
{
|
||||
speaker: '장무',
|
||||
portrait: 'captain',
|
||||
text: '명만 내려주십시오. 창병 둘과 기병 하나라면 길목은 막아낼 수 있습니다.'
|
||||
},
|
||||
{
|
||||
speaker: '유현',
|
||||
portrait: 'lord',
|
||||
text: '좋다. 백성을 뒤로 물리고, 우리는 앞에서 시간을 벌겠다.'
|
||||
}
|
||||
];
|
||||
|
||||
export const firstBattleMap: BattleMap = {
|
||||
width: 12,
|
||||
height: 12,
|
||||
terrain: [
|
||||
['plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
|
||||
['plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain'],
|
||||
['plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'fort', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain'],
|
||||
['forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
|
||||
['forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain'],
|
||||
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain']
|
||||
]
|
||||
};
|
||||
|
||||
export const firstBattleUnits: UnitData[] = [
|
||||
{ id: 'you-hyun', name: '유현', faction: 'ally', className: '의병장', hp: 32, maxHp: 32, attack: 9, move: 4, x: 1, y: 9 },
|
||||
{ id: 'jang-mu', name: '장무', faction: 'ally', className: '창병', hp: 28, maxHp: 28, attack: 8, move: 3, x: 2, y: 10 },
|
||||
{ id: 'seo-yun', name: '서윤', faction: 'ally', className: '책사', hp: 24, maxHp: 24, attack: 6, move: 3, x: 1, y: 10 },
|
||||
{ id: 'rebel-a', name: '황건병', faction: 'enemy', className: '도적', hp: 20, maxHp: 20, attack: 6, move: 3, x: 8, y: 2 },
|
||||
{ id: 'rebel-b', name: '황건병', faction: 'enemy', className: '도적', hp: 20, maxHp: 20, attack: 6, move: 3, x: 9, y: 3 },
|
||||
{ id: 'rebel-c', name: '황건궁병', faction: 'enemy', className: '궁병', hp: 18, maxHp: 18, attack: 7, move: 3, x: 10, y: 2 },
|
||||
{ id: 'rebel-leader', name: '두령 한석', faction: 'enemy', className: '두령', hp: 30, maxHp: 30, attack: 9, move: 3, x: 10, y: 4 }
|
||||
];
|
||||
139
src/game/scenes/BattleScene.ts
Normal file
139
src/game/scenes/BattleScene.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
import Phaser from 'phaser';
|
||||
import { firstBattleMap, firstBattleUnits, type TerrainType, type UnitData } from '../data/scenario';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
const tileSize = 48;
|
||||
const mapOffset = { x: 64, y: 72 };
|
||||
|
||||
const terrainColor: Record<TerrainType, number> = {
|
||||
plain: 0x6d8056,
|
||||
forest: 0x355f42,
|
||||
hill: 0x82724e,
|
||||
fort: 0x8b6f48
|
||||
};
|
||||
|
||||
export class BattleScene extends Phaser.Scene {
|
||||
private selectedUnit?: UnitData;
|
||||
private infoText?: Phaser.GameObjects.Text;
|
||||
private turnText?: Phaser.GameObjects.Text;
|
||||
private markers: Phaser.GameObjects.Rectangle[] = [];
|
||||
|
||||
constructor() {
|
||||
super('BattleScene');
|
||||
}
|
||||
|
||||
create() {
|
||||
const { width, height } = this.scale;
|
||||
this.add.rectangle(0, 0, width, height, 0x0f1418).setOrigin(0);
|
||||
this.drawMap();
|
||||
this.drawUnits();
|
||||
this.drawSidePanel(width, height);
|
||||
this.setInfo('아군 차례입니다. 유현을 선택해 이동 범위를 확인하세요.');
|
||||
}
|
||||
|
||||
private drawMap() {
|
||||
firstBattleMap.terrain.forEach((row, y) => {
|
||||
row.forEach((terrain, x) => {
|
||||
const tileX = mapOffset.x + x * tileSize;
|
||||
const tileY = mapOffset.y + y * tileSize;
|
||||
const tile = this.add.rectangle(tileX, tileY, tileSize, tileSize, terrainColor[terrain]).setOrigin(0);
|
||||
tile.setStrokeStyle(1, 0x182028, 0.8);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private drawUnits() {
|
||||
firstBattleUnits.forEach((unit) => {
|
||||
const sprite = this.add.image(
|
||||
mapOffset.x + unit.x * tileSize + tileSize / 2,
|
||||
mapOffset.y + unit.y * tileSize + tileSize / 2,
|
||||
unit.faction === 'ally' ? 'unit-ally' : 'unit-enemy'
|
||||
);
|
||||
sprite.setInteractive({ useHandCursor: unit.faction === 'ally' });
|
||||
sprite.on('pointerdown', () => this.selectUnit(unit));
|
||||
|
||||
this.add.text(sprite.x - 22, sprite.y + 22, unit.name, {
|
||||
fontSize: '13px',
|
||||
color: unit.faction === 'ally' ? '#e7edf7' : '#ffebe7',
|
||||
backgroundColor: '#10131a',
|
||||
padding: { left: 4, right: 4, top: 2, bottom: 2 }
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private drawSidePanel(width: number, height: number) {
|
||||
const panelX = mapOffset.x + firstBattleMap.width * tileSize + 36;
|
||||
this.add.rectangle(panelX, 72, width - panelX - 64, height - 128, palette.panel, 0.96).setOrigin(0);
|
||||
this.add.text(panelX + 30, 108, '첫 전투', {
|
||||
fontSize: '32px',
|
||||
color: '#e8dfca',
|
||||
fontStyle: '700'
|
||||
});
|
||||
this.turnText = this.add.text(panelX + 30, 164, '1턴 / 아군', {
|
||||
fontSize: '22px',
|
||||
color: '#d8b15f'
|
||||
});
|
||||
this.infoText = this.add.text(panelX + 30, 224, '', {
|
||||
fontSize: '20px',
|
||||
color: '#e8dfca',
|
||||
wordWrap: { width: width - panelX - 124, useAdvancedWrap: true },
|
||||
lineSpacing: 8
|
||||
});
|
||||
|
||||
this.add.text(panelX + 30, height - 186, '승리 조건: 적 전멸', {
|
||||
fontSize: '18px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
this.add.text(panelX + 30, height - 148, '패배 조건: 유현 퇴각', {
|
||||
fontSize: '18px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
this.add.text(panelX + 30, height - 110, '다음 단계: 이동/공격/AI 구현', {
|
||||
fontSize: '18px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
this.turnText.setText('1턴 / 아군');
|
||||
}
|
||||
|
||||
private selectUnit(unit: UnitData) {
|
||||
if (unit.faction !== 'ally') {
|
||||
this.setInfo(`${unit.name}: ${unit.className}, HP ${unit.hp}/${unit.maxHp}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedUnit = unit;
|
||||
this.clearMarkers();
|
||||
this.showMoveRange(unit);
|
||||
this.setInfo(`${unit.name} (${unit.className})\nHP ${unit.hp}/${unit.maxHp}, 공격 ${unit.attack}, 이동 ${unit.move}`);
|
||||
}
|
||||
|
||||
private showMoveRange(unit: UnitData) {
|
||||
for (let y = 0; y < firstBattleMap.height; y += 1) {
|
||||
for (let x = 0; x < firstBattleMap.width; x += 1) {
|
||||
const distance = Math.abs(unit.x - x) + Math.abs(unit.y - y);
|
||||
if (distance > 0 && distance <= unit.move) {
|
||||
const marker = this.add.rectangle(
|
||||
mapOffset.x + x * tileSize,
|
||||
mapOffset.y + y * tileSize,
|
||||
tileSize,
|
||||
tileSize,
|
||||
palette.blue,
|
||||
0.24
|
||||
);
|
||||
marker.setOrigin(0);
|
||||
marker.setStrokeStyle(1, palette.blue, 0.6);
|
||||
this.markers.push(marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private clearMarkers() {
|
||||
this.markers.forEach((marker) => marker.destroy());
|
||||
this.markers = [];
|
||||
}
|
||||
|
||||
private setInfo(text: string) {
|
||||
this.infoText?.setText(text);
|
||||
}
|
||||
}
|
||||
67
src/game/scenes/BootScene.ts
Normal file
67
src/game/scenes/BootScene.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import Phaser from 'phaser';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
export class BootScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super('BootScene');
|
||||
}
|
||||
|
||||
create() {
|
||||
this.createGeneratedTextures();
|
||||
this.scene.start('TitleScene');
|
||||
}
|
||||
|
||||
private createGeneratedTextures() {
|
||||
this.createPortrait('portrait-lord', 0x445f92, 0xd8b15f);
|
||||
this.createPortrait('portrait-strategist', 0x5b6e55, 0xe8dfca);
|
||||
this.createPortrait('portrait-captain', 0x8a4f3d, 0xd8b15f);
|
||||
this.createBannerTexture();
|
||||
this.createUnitTexture('unit-ally', palette.blue, 0xe7edf7);
|
||||
this.createUnitTexture('unit-enemy', palette.red, 0xffebe7);
|
||||
}
|
||||
|
||||
private createPortrait(key: string, primary: number, accent: number) {
|
||||
const graphics = this.make.graphics({ x: 0, y: 0 });
|
||||
graphics.fillStyle(0x141820, 1);
|
||||
graphics.fillRoundedRect(0, 0, 128, 128, 8);
|
||||
graphics.fillStyle(primary, 1);
|
||||
graphics.fillCircle(64, 46, 26);
|
||||
graphics.fillRoundedRect(31, 71, 66, 42, 12);
|
||||
graphics.fillStyle(accent, 1);
|
||||
graphics.fillTriangle(38, 29, 64, 8, 90, 29);
|
||||
graphics.lineStyle(4, 0x0b0d12, 0.45);
|
||||
graphics.strokeRoundedRect(2, 2, 124, 124, 8);
|
||||
graphics.generateTexture(key, 128, 128);
|
||||
graphics.destroy();
|
||||
}
|
||||
|
||||
private createBannerTexture() {
|
||||
const graphics = this.make.graphics({ x: 0, y: 0 });
|
||||
graphics.fillStyle(0x1b2630, 1);
|
||||
graphics.fillRect(0, 0, 360, 180);
|
||||
graphics.fillStyle(0xb64a45, 1);
|
||||
graphics.fillRect(44, 28, 104, 132);
|
||||
graphics.fillStyle(0xd8b15f, 1);
|
||||
graphics.fillTriangle(148, 28, 310, 72, 148, 116);
|
||||
graphics.fillStyle(0xe8dfca, 1);
|
||||
graphics.fillCircle(92, 82, 25);
|
||||
graphics.lineStyle(6, 0x0b0d12, 0.5);
|
||||
graphics.strokeRect(0, 0, 360, 180);
|
||||
graphics.generateTexture('title-banner', 360, 180);
|
||||
graphics.destroy();
|
||||
}
|
||||
|
||||
private createUnitTexture(key: string, primary: number, accent: number) {
|
||||
const graphics = this.make.graphics({ x: 0, y: 0 });
|
||||
graphics.fillStyle(0x0c0f14, 0.5);
|
||||
graphics.fillEllipse(32, 49, 44, 17);
|
||||
graphics.fillStyle(primary, 1);
|
||||
graphics.fillRoundedRect(14, 12, 36, 38, 8);
|
||||
graphics.fillStyle(accent, 1);
|
||||
graphics.fillCircle(32, 17, 9);
|
||||
graphics.lineStyle(3, 0x0b0d12, 0.55);
|
||||
graphics.strokeRoundedRect(14, 12, 36, 38, 8);
|
||||
graphics.generateTexture(key, 64, 64);
|
||||
graphics.destroy();
|
||||
}
|
||||
}
|
||||
85
src/game/scenes/StoryScene.ts
Normal file
85
src/game/scenes/StoryScene.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import Phaser from 'phaser';
|
||||
import { prologueLines, type StoryLine } from '../data/scenario';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
const portraitKeys: Record<StoryLine['portrait'], string> = {
|
||||
lord: 'portrait-lord',
|
||||
strategist: 'portrait-strategist',
|
||||
captain: 'portrait-captain'
|
||||
};
|
||||
|
||||
export class StoryScene extends Phaser.Scene {
|
||||
private lineIndex = 0;
|
||||
private portrait?: Phaser.GameObjects.Image;
|
||||
private nameText?: Phaser.GameObjects.Text;
|
||||
private bodyText?: Phaser.GameObjects.Text;
|
||||
|
||||
constructor() {
|
||||
super('StoryScene');
|
||||
}
|
||||
|
||||
create() {
|
||||
const { width, height } = this.scale;
|
||||
this.add.rectangle(0, 0, width, height, 0x151922).setOrigin(0);
|
||||
this.drawCampBackground(width, height);
|
||||
this.drawDialogPanel(width, height);
|
||||
this.showLine(0);
|
||||
|
||||
this.input.on('pointerdown', () => this.advance());
|
||||
this.input.keyboard?.on('keydown-SPACE', () => this.advance());
|
||||
this.input.keyboard?.on('keydown-ENTER', () => this.advance());
|
||||
}
|
||||
|
||||
private drawCampBackground(width: number, height: number) {
|
||||
const graphics = this.add.graphics();
|
||||
graphics.fillStyle(0x26303a, 1);
|
||||
graphics.fillRect(0, height * 0.54, width, height * 0.46);
|
||||
graphics.fillStyle(0x5f6d5a, 1);
|
||||
graphics.fillTriangle(0, height * 0.54, 240, 240, 470, height * 0.54);
|
||||
graphics.fillStyle(0x384858, 1);
|
||||
graphics.fillTriangle(300, height * 0.54, 570, 160, 870, height * 0.54);
|
||||
graphics.fillStyle(0x8a4f3d, 1);
|
||||
graphics.fillRect(935, 264, 98, 200);
|
||||
graphics.fillStyle(0xd8b15f, 1);
|
||||
graphics.fillTriangle(1033, 264, 1180, 304, 1033, 344);
|
||||
}
|
||||
|
||||
private drawDialogPanel(width: number, height: number) {
|
||||
const panelY = height - 214;
|
||||
this.add.rectangle(64, panelY, width - 128, 158, palette.panel, 0.95).setOrigin(0);
|
||||
this.add.rectangle(64, panelY, width - 128, 4, palette.gold, 0.9).setOrigin(0);
|
||||
this.portrait = this.add.image(150, panelY + 79, 'portrait-lord');
|
||||
this.nameText = this.add.text(238, panelY + 28, '', {
|
||||
fontSize: '24px',
|
||||
color: '#d8b15f',
|
||||
fontStyle: '700'
|
||||
});
|
||||
this.bodyText = this.add.text(238, panelY + 68, '', {
|
||||
fontSize: '24px',
|
||||
color: '#e8dfca',
|
||||
wordWrap: { width: width - 370, useAdvancedWrap: true },
|
||||
lineSpacing: 8
|
||||
});
|
||||
this.add.text(width - 190, panelY + 122, 'SPACE / ENTER', {
|
||||
fontSize: '16px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
}
|
||||
|
||||
private showLine(index: number) {
|
||||
const line = prologueLines[index];
|
||||
this.portrait?.setTexture(portraitKeys[line.portrait]);
|
||||
this.nameText?.setText(line.speaker);
|
||||
this.bodyText?.setText(line.text);
|
||||
}
|
||||
|
||||
private advance() {
|
||||
if (this.lineIndex >= prologueLines.length - 1) {
|
||||
this.scene.start('BattleScene');
|
||||
return;
|
||||
}
|
||||
|
||||
this.lineIndex += 1;
|
||||
this.showLine(this.lineIndex);
|
||||
}
|
||||
}
|
||||
57
src/game/scenes/TitleScene.ts
Normal file
57
src/game/scenes/TitleScene.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import Phaser from 'phaser';
|
||||
import { palette } from '../ui/palette';
|
||||
|
||||
export class TitleScene extends Phaser.Scene {
|
||||
constructor() {
|
||||
super('TitleScene');
|
||||
}
|
||||
|
||||
create() {
|
||||
const { width, height } = this.scale;
|
||||
this.add.rectangle(0, 0, width, height, 0x10131a).setOrigin(0);
|
||||
this.add.rectangle(0, height * 0.58, width, height * 0.42, 0x20252c, 0.96).setOrigin(0);
|
||||
this.add.image(width * 0.68, height * 0.33, 'title-banner').setScale(1.1);
|
||||
|
||||
this.add.text(96, 108, 'Heros Web', {
|
||||
fontSize: '62px',
|
||||
color: '#e8dfca',
|
||||
fontStyle: '700'
|
||||
});
|
||||
this.add.text(100, 182, '삼국지풍 웹 전술 RPG', {
|
||||
fontSize: '24px',
|
||||
color: '#d8b15f'
|
||||
});
|
||||
|
||||
const start = this.add.text(104, 296, '새 게임', {
|
||||
fontSize: '28px',
|
||||
color: '#10131a',
|
||||
backgroundColor: '#d8b15f',
|
||||
padding: { left: 28, right: 28, top: 14, bottom: 14 }
|
||||
});
|
||||
start.setInteractive({ useHandCursor: true });
|
||||
start.on('pointerdown', () => this.scene.start('StoryScene'));
|
||||
|
||||
this.add.text(104, 382, '이어하기', {
|
||||
fontSize: '24px',
|
||||
color: '#6f7782'
|
||||
});
|
||||
this.add.text(104, 430, '설정', {
|
||||
fontSize: '24px',
|
||||
color: '#6f7782'
|
||||
});
|
||||
|
||||
this.add.text(96, height - 112, '첫 목표: 프롤로그를 읽고 첫 전투에 진입합니다.', {
|
||||
fontSize: '20px',
|
||||
color: '#9aa3ad'
|
||||
});
|
||||
|
||||
this.input.keyboard?.once('keydown-ENTER', () => this.scene.start('StoryScene'));
|
||||
this.addLine(0, height * 0.58, width);
|
||||
}
|
||||
|
||||
private addLine(x: number, y: number, width: number) {
|
||||
const line = this.add.graphics();
|
||||
line.lineStyle(2, palette.gold, 0.35);
|
||||
line.lineBetween(x, y, x + width, y);
|
||||
}
|
||||
}
|
||||
12
src/game/ui/palette.ts
Normal file
12
src/game/ui/palette.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export const palette = {
|
||||
ink: 0x10131a,
|
||||
panel: 0x1e2430,
|
||||
panelDark: 0x151922,
|
||||
line: 0x3b4658,
|
||||
gold: 0xd8b15f,
|
||||
red: 0xb64a45,
|
||||
blue: 0x4f7ec7,
|
||||
green: 0x5f8f63,
|
||||
cream: 0xe8dfca,
|
||||
muted: 0x9aa3ad
|
||||
} as const;
|
||||
31
src/main.ts
Normal file
31
src/main.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import Phaser from 'phaser';
|
||||
import { BattleScene } from './game/scenes/BattleScene';
|
||||
import { BootScene } from './game/scenes/BootScene';
|
||||
import { StoryScene } from './game/scenes/StoryScene';
|
||||
import { TitleScene } from './game/scenes/TitleScene';
|
||||
import './styles/global.css';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__HEROS_GAME__?: Phaser.Game;
|
||||
}
|
||||
}
|
||||
|
||||
const config: Phaser.Types.Core.GameConfig = {
|
||||
type: Phaser.AUTO,
|
||||
parent: 'game',
|
||||
width: 1280,
|
||||
height: 720,
|
||||
backgroundColor: '#10131a',
|
||||
scale: {
|
||||
mode: Phaser.Scale.FIT,
|
||||
autoCenter: Phaser.Scale.CENTER_BOTH
|
||||
},
|
||||
scene: [BootScene, TitleScene, StoryScene, BattleScene]
|
||||
};
|
||||
|
||||
const game = new Phaser.Game(config);
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
window.__HEROS_GAME__ = game;
|
||||
}
|
||||
29
src/styles/global.css
Normal file
29
src/styles/global.css
Normal file
@@ -0,0 +1,29 @@
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
background: #0b0d12;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-family:
|
||||
Inter,
|
||||
'Segoe UI',
|
||||
system-ui,
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
#game {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
canvas {
|
||||
display: block;
|
||||
}
|
||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"allowJs": false,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"moduleResolution": "Bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user