Initial commit
24
.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
11
index.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Prototype 2</title>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="game" style="cursor: none"></canvas>
|
||||
<script type="module" src="src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
906
package-lock.json
generated
Normal file
@ -0,0 +1,906 @@
|
||||
{
|
||||
"name": "prototype1",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "prototype1",
|
||||
"version": "0.0.0",
|
||||
"devDependencies": {
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
|
||||
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
|
||||
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
|
||||
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
|
||||
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
|
||||
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
|
||||
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
|
||||
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
|
||||
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
|
||||
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
|
||||
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.31.0.tgz",
|
||||
"integrity": "sha512-9NrR4033uCbUBRgvLcBrJofa2KY9DzxL2UKZ1/4xA/mnTNyhZCWBuD8X3tPm1n4KxcgaraOYgrFKSgwjASfmlA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.31.0.tgz",
|
||||
"integrity": "sha512-iBbODqT86YBFHajxxF8ebj2hwKm1k8PTBQSojSt3d1FFt1gN+xf4CowE47iN0vOSdnd+5ierMHBbu/rHc7nq5g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.31.0.tgz",
|
||||
"integrity": "sha512-WHIZfXgVBX30SWuTMhlHPXTyN20AXrLH4TEeH/D0Bolvx9PjgZnn4H677PlSGvU6MKNsjCQJYczkpvBbrBnG6g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.31.0.tgz",
|
||||
"integrity": "sha512-hrWL7uQacTEF8gdrQAqcDy9xllQ0w0zuL1wk1HV8wKGSGbKPVjVUv/DEwT2+Asabf8Dh/As+IvfdU+H8hhzrQQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.31.0.tgz",
|
||||
"integrity": "sha512-S2oCsZ4hJviG1QjPY1h6sVJLBI6ekBeAEssYKad1soRFv3SocsQCzX6cwnk6fID6UQQACTjeIMB+hyYrFacRew==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.31.0.tgz",
|
||||
"integrity": "sha512-pCANqpynRS4Jirn4IKZH4tnm2+2CqCNLKD7gAdEjzdLGbH1iO0zouHz4mxqg0uEMpO030ejJ0aA6e1PJo2xrPA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.31.0.tgz",
|
||||
"integrity": "sha512-0O8ViX+QcBd3ZmGlcFTnYXZKGbFu09EhgD27tgTdGnkcYXLat4KIsBBQeKLR2xZDCXdIBAlWLkiXE1+rJpCxFw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.31.0.tgz",
|
||||
"integrity": "sha512-w5IzG0wTVv7B0/SwDnMYmbr2uERQp999q8FMkKG1I+j8hpPX2BYFjWe69xbhbP6J9h2gId/7ogesl9hwblFwwg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.31.0.tgz",
|
||||
"integrity": "sha512-JyFFshbN5xwy6fulZ8B/8qOqENRmDdEkcIMF0Zz+RsfamEW+Zabl5jAb0IozP/8UKnJ7g2FtZZPEUIAlUSX8cA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.31.0.tgz",
|
||||
"integrity": "sha512-kpQXQ0UPFeMPmPYksiBL9WS/BDiQEjRGMfklVIsA0Sng347H8W2iexch+IEwaR7OVSKtr2ZFxggt11zVIlZ25g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.31.0.tgz",
|
||||
"integrity": "sha512-pMlxLjt60iQTzt9iBb3jZphFIl55a70wexvo8p+vVFK+7ifTRookdoXX3bOsRdmfD+OKnMozKO6XM4zR0sHRrQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.31.0.tgz",
|
||||
"integrity": "sha512-D7TXT7I/uKEuWiRkEFbed1UUYZwcJDU4vZQdPTcepK7ecPhzKOYk4Er2YR4uHKme4qDeIh6N3XrLfpuM7vzRWQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.31.0.tgz",
|
||||
"integrity": "sha512-wal2Tc8O5lMBtoePLBYRKj2CImUCJ4UNGJlLwspx7QApYny7K1cUYlzQ/4IGQBLmm+y0RS7dwc3TDO/pmcneTw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.31.0.tgz",
|
||||
"integrity": "sha512-O1o5EUI0+RRMkK9wiTVpk2tyzXdXefHtRTIjBbmFREmNMy7pFeYXCFGbhKFwISA3UOExlo5GGUuuj3oMKdK6JQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.31.0.tgz",
|
||||
"integrity": "sha512-zSoHl356vKnNxwOWnLd60ixHNPRBglxpv2g7q0Cd3Pmr561gf0HiAcUBRL3S1vPqRC17Zo2CX/9cPkqTIiai1g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.31.0.tgz",
|
||||
"integrity": "sha512-ypB/HMtcSGhKUQNiFwqgdclWNRrAYDH8iMYH4etw/ZlGwiTVxBz2tDrGRrPlfZu6QjXwtd+C3Zib5pFqID97ZA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.31.0.tgz",
|
||||
"integrity": "sha512-JuhN2xdI/m8Hr+aVO3vspO7OQfUFO6bKLIRTAy0U15vmWjnZDLrEgCZ2s6+scAYaQVpYSh9tZtRijApw9IXyMw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.31.0.tgz",
|
||||
"integrity": "sha512-U1xZZXYkvdf5MIWmftU8wrM5PPXzyaY1nGCI4KI4BFfoZxHamsIe+BtnPLIvvPykvQWlVbqUXdLa4aJUuilwLQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.31.0.tgz",
|
||||
"integrity": "sha512-ul8rnCsUumNln5YWwz0ted2ZHFhzhRRnkpBZ+YRuHoRAlUji9KChpOUOndY7uykrPEPXVbHLlsdo6v5yXo/TXw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
|
||||
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.24.2",
|
||||
"@esbuild/android-arm": "0.24.2",
|
||||
"@esbuild/android-arm64": "0.24.2",
|
||||
"@esbuild/android-x64": "0.24.2",
|
||||
"@esbuild/darwin-arm64": "0.24.2",
|
||||
"@esbuild/darwin-x64": "0.24.2",
|
||||
"@esbuild/freebsd-arm64": "0.24.2",
|
||||
"@esbuild/freebsd-x64": "0.24.2",
|
||||
"@esbuild/linux-arm": "0.24.2",
|
||||
"@esbuild/linux-arm64": "0.24.2",
|
||||
"@esbuild/linux-ia32": "0.24.2",
|
||||
"@esbuild/linux-loong64": "0.24.2",
|
||||
"@esbuild/linux-mips64el": "0.24.2",
|
||||
"@esbuild/linux-ppc64": "0.24.2",
|
||||
"@esbuild/linux-riscv64": "0.24.2",
|
||||
"@esbuild/linux-s390x": "0.24.2",
|
||||
"@esbuild/linux-x64": "0.24.2",
|
||||
"@esbuild/netbsd-arm64": "0.24.2",
|
||||
"@esbuild/netbsd-x64": "0.24.2",
|
||||
"@esbuild/openbsd-arm64": "0.24.2",
|
||||
"@esbuild/openbsd-x64": "0.24.2",
|
||||
"@esbuild/sunos-x64": "0.24.2",
|
||||
"@esbuild/win32-arm64": "0.24.2",
|
||||
"@esbuild/win32-ia32": "0.24.2",
|
||||
"@esbuild/win32-x64": "0.24.2"
|
||||
}
|
||||
},
|
||||
"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,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.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
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
|
||||
"integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.8",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.31.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.31.0.tgz",
|
||||
"integrity": "sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.6"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0",
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.31.0",
|
||||
"@rollup/rollup-android-arm64": "4.31.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.31.0",
|
||||
"@rollup/rollup-darwin-x64": "4.31.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.31.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.31.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.31.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.31.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.31.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.31.0",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.31.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.31.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.31.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.31.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.31.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.31.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.31.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.31.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.31.0",
|
||||
"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,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
|
||||
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "6.0.9",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.9.tgz",
|
||||
"integrity": "sha512-MSgUxHcaXLtnBPktkbUSoQUANApKYuxZ6DrbVENlIorbhL2dZydTLaZ01tjUoE3szeFzlFk9ANOKk0xurh4MKA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.24.2",
|
||||
"postcss": "^8.4.49",
|
||||
"rollup": "^4.23.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "prototype1",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.0.5"
|
||||
}
|
||||
}
|
BIN
src/art/bgs/sampleroom1.png
Normal file
After Width: | Height: | Size: 874 B |
BIN
src/art/characters/bat.png
Normal file
After Width: | Height: | Size: 950 B |
BIN
src/art/characters/kobold.png
Normal file
After Width: | Height: | Size: 835 B |
BIN
src/art/characters/raccoon walking 2.aseprite
Normal file
BIN
src/art/characters/raccoon.png
Normal file
After Width: | Height: | Size: 840 B |
BIN
src/art/characters/raccoon_walking.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/art/characters/robot.png
Normal file
After Width: | Height: | Size: 772 B |
BIN
src/art/characters/snake.png
Normal file
After Width: | Height: | Size: 885 B |
BIN
src/art/fonts/vga_8x16.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
71
src/art/mapdata/sampleroom1.ldtk
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"__header__": {
|
||||
"fileType": "LDtk Project JSON",
|
||||
"app": "LDtk",
|
||||
"doc": "https://ldtk.io/json",
|
||||
"schema": "https://ldtk.io/files/JSON_SCHEMA.json",
|
||||
"appAuthor": "Sebastien 'deepnight' Benard",
|
||||
"appVersion": "1.5.3",
|
||||
"url": "https://ldtk.io"
|
||||
},
|
||||
"iid": "018dc430-c210-11ef-bffb-99d47c7356b6",
|
||||
"jsonVersion": "1.5.3",
|
||||
"appBuildId": 473703,
|
||||
"nextUid": 1,
|
||||
"identifierStyle": "Capitalize",
|
||||
"toc": [],
|
||||
"worldLayout": "Free",
|
||||
"worldGridWidth": 256,
|
||||
"worldGridHeight": 256,
|
||||
"defaultLevelWidth": 256,
|
||||
"defaultLevelHeight": 256,
|
||||
"defaultPivotX": 0,
|
||||
"defaultPivotY": 0,
|
||||
"defaultGridSize": 16,
|
||||
"defaultEntityWidth": 16,
|
||||
"defaultEntityHeight": 16,
|
||||
"bgColor": "#40465B",
|
||||
"defaultLevelBgColor": "#696A79",
|
||||
"minifyJson": false,
|
||||
"externalLevels": false,
|
||||
"exportTiled": false,
|
||||
"simplifiedExport": false,
|
||||
"imageExportMode": "None",
|
||||
"exportLevelBg": true,
|
||||
"pngFilePattern": null,
|
||||
"backupOnSave": false,
|
||||
"backupLimit": 10,
|
||||
"backupRelPath": null,
|
||||
"levelNamePattern": "Level_%idx",
|
||||
"tutorialDesc": null,
|
||||
"customCommands": [],
|
||||
"flags": [],
|
||||
"defs": { "layers": [], "entities": [], "tilesets": [], "enums": [], "externalEnums": [], "levelFields": [] },
|
||||
"levels": [
|
||||
{
|
||||
"identifier": "Level_0",
|
||||
"iid": "018deb40-c210-11ef-bffb-151cf6f68a2d",
|
||||
"uid": 0,
|
||||
"worldX": 0,
|
||||
"worldY": 0,
|
||||
"worldDepth": 0,
|
||||
"pxWid": 256,
|
||||
"pxHei": 256,
|
||||
"__bgColor": "#696A79",
|
||||
"bgColor": null,
|
||||
"useAutoIdentifier": true,
|
||||
"bgRelPath": null,
|
||||
"bgPos": null,
|
||||
"bgPivotX": 0.5,
|
||||
"bgPivotY": 0.5,
|
||||
"__smartColor": "#ADADB5",
|
||||
"__bgPos": null,
|
||||
"externalRelPath": null,
|
||||
"fieldInstances": [],
|
||||
"layerInstances": [],
|
||||
"__neighbours": []
|
||||
}
|
||||
],
|
||||
"worlds": [],
|
||||
"dummyWorldIid": "018dc431-c210-11ef-bffb-cb15d2e5f164"
|
||||
}
|
41
src/assets.ts
Normal file
@ -0,0 +1,41 @@
|
||||
class Assets {
|
||||
#images: Record<string, HTMLImageElement>;
|
||||
|
||||
constructor() {
|
||||
this.#images = {};
|
||||
}
|
||||
|
||||
isLoaded(): boolean {
|
||||
// you could use this, if so inclined, to check if a certain
|
||||
// list of assets had been loaded prior to game start
|
||||
//
|
||||
// (to do so, you would call getImage() for each desired asset
|
||||
// and then wait for isLoaded to return true)
|
||||
for (let filename in this.#images) {
|
||||
if (!this.#images[filename].complete) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getImage(filename: string): HTMLImageElement {
|
||||
let element: HTMLImageElement;
|
||||
if (this.#images[filename]) {
|
||||
element = this.#images[filename];
|
||||
} else {
|
||||
element = document.createElement("img");
|
||||
element.src = filename;
|
||||
this.#images[filename] = element;
|
||||
}
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
let active: Assets = new Assets();
|
||||
|
||||
export function getAssets(): Assets {
|
||||
return active;
|
||||
}
|
||||
|
44
src/clock.ts
Normal file
@ -0,0 +1,44 @@
|
||||
const MAX_UPDATES_BANKED: number = 20.0;
|
||||
|
||||
// always run physics at 240 hz
|
||||
const UPDATES_PER_MS: number = 1/(1000.0/240.0);
|
||||
|
||||
class Clock {
|
||||
#lastTimestamp: number | undefined;
|
||||
#updatesBanked: number
|
||||
|
||||
constructor() {
|
||||
this.#lastTimestamp = undefined;
|
||||
this.#updatesBanked = 0.0
|
||||
}
|
||||
|
||||
|
||||
recordTimestamp(timestamp: number) {
|
||||
if (this.#lastTimestamp) {
|
||||
let delta = timestamp - this.#lastTimestamp;
|
||||
this.#bankDelta(delta);
|
||||
}
|
||||
this.#lastTimestamp = timestamp;
|
||||
}
|
||||
|
||||
popUpdate() {
|
||||
// return true if we should do an update right now
|
||||
// and remove one draw from the bank
|
||||
if (this.#updatesBanked > 1) {
|
||||
this.#updatesBanked -= 1;
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#bankDelta(delta: number) {
|
||||
this.#updatesBanked = Math.min(delta * UPDATES_PER_MS, MAX_UPDATES_BANKED);
|
||||
}
|
||||
}
|
||||
|
||||
let active: Clock = new Clock();
|
||||
|
||||
export function getClock(): Clock {
|
||||
return active;
|
||||
}
|
||||
|
||||
|
1
src/colors.ts
Normal file
@ -0,0 +1 @@
|
||||
export const BG_OUTER = "#000";
|
9
src/counter.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export function setupCounter(element: HTMLButtonElement) {
|
||||
let counter = 0
|
||||
const setCounter = (count: number) => {
|
||||
counter = count
|
||||
element.innerHTML = `count is ${counter}`
|
||||
}
|
||||
element.addEventListener('click', () => setCounter(counter + 1))
|
||||
setCounter(0)
|
||||
}
|
146
src/font.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import {getAssets} from "./assets.ts";
|
||||
import fontSheet from './art/fonts/vga_8x16.png';
|
||||
|
||||
export enum AlignX {
|
||||
Left = 0,
|
||||
Center = 1,
|
||||
Right = 2
|
||||
}
|
||||
|
||||
export enum AlignY {
|
||||
Top = 0,
|
||||
Middle = 1,
|
||||
Right = 2,
|
||||
}
|
||||
|
||||
class Font {
|
||||
#filename: string;
|
||||
#cx: number;
|
||||
#cy: number;
|
||||
#px: number;
|
||||
#py: number;
|
||||
#tintingCanvas: HTMLCanvasElement;
|
||||
#tintedVersions: Record<string, HTMLImageElement>;
|
||||
|
||||
constructor(filename: string, cx: number, cy: number, px: number, py: number) {
|
||||
this.#filename = filename;
|
||||
this.#cx = cx;
|
||||
this.#cy = cy;
|
||||
this.#px = px;
|
||||
this.#py = py;
|
||||
this.#tintingCanvas = document.createElement("canvas");
|
||||
this.#tintedVersions = {}
|
||||
}
|
||||
|
||||
#getTintedImage(color: string): HTMLImageElement | null {
|
||||
let image = getAssets().getImage(this.#filename);
|
||||
|
||||
if (!image.complete) { return null; }
|
||||
|
||||
let tintedVersion = this.#tintedVersions[color];
|
||||
if (tintedVersion != undefined) {
|
||||
return tintedVersion;
|
||||
}
|
||||
|
||||
let w = image.width;
|
||||
let h = image.height;
|
||||
|
||||
if (!(w == this.#cx * this.#px && h == this.#cy * this.#py)) {
|
||||
throw `unexpected image dimensions for font ${this.#filename}: ${w} x ${h}`
|
||||
}
|
||||
|
||||
this.#tintingCanvas.width = w;
|
||||
this.#tintingCanvas.height = h;
|
||||
let ctx = this.#tintingCanvas.getContext("2d")!;
|
||||
ctx.clearRect(0, 0, w, h);
|
||||
ctx.drawImage(image, 0, 0);
|
||||
ctx.globalCompositeOperation = "source-in";
|
||||
ctx.fillStyle = color;
|
||||
ctx.rect(0, 0, w, h);
|
||||
ctx.fill();
|
||||
|
||||
let result = new Image();
|
||||
result.src = this.#tintingCanvas.toDataURL();
|
||||
this.#tintedVersions[color] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
drawText({ctx, text, x, y, alignX, alignY, forceWidth, color}: {
|
||||
ctx: CanvasRenderingContext2D,
|
||||
text: string,
|
||||
x: number, y: number, alignX?: AlignX, alignY?: AlignY,
|
||||
forceWidth?: number, color?: string
|
||||
}) {
|
||||
alignX = alignX == undefined ? AlignX.Left : alignX;
|
||||
alignY = alignY == undefined ? AlignY.Top : alignY;
|
||||
forceWidth = forceWidth == undefined ? 65535 : forceWidth;
|
||||
color = color == undefined ? "#ffffff" : color;
|
||||
|
||||
let image = this.#getTintedImage(color)
|
||||
if (image == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let wcx = Math.floor(forceWidth / this.#px);
|
||||
|
||||
let sz = this.#glyphwise(text, wcx, () => {});
|
||||
let offsetX = x;
|
||||
let offsetY = y;
|
||||
offsetX += (alignX == AlignX.Left ? 0 : alignX == AlignX.Center ? -sz.w / 2 : - sz.w)
|
||||
offsetY += (alignY == AlignY.Top ? 0 : alignY == AlignY.Middle ? -sz.h / 2 : - sz.h)
|
||||
|
||||
this.#glyphwise(text, wcx, (cx, cy, char) => {
|
||||
let srcIx = char.charCodeAt(0);
|
||||
this.#drawGlyph({ctx: ctx, image: image, ix: srcIx, x: offsetX + cx * this.#px, y: offsetY + cy * this.#py});
|
||||
})
|
||||
}
|
||||
|
||||
#drawGlyph({ctx, image, ix, x, y}: {ctx: CanvasRenderingContext2D, image: HTMLImageElement, ix: number, x: number, y: number}) {
|
||||
let srcCx = ix % this.#cx;
|
||||
let srcCy = Math.floor(ix / this.#cx);
|
||||
let srcPx = srcCx * this.#px;
|
||||
let srcPy = srcCy * this.#py;
|
||||
ctx.drawImage(
|
||||
image,
|
||||
srcPx, srcPy, this.#px, this.#py,
|
||||
Math.floor(x), Math.floor(y), this.#px, this.#py
|
||||
);
|
||||
}
|
||||
|
||||
measureText({text, forceWidth}: {text: string, forceWidth?: number}): {w: number, h: number} {
|
||||
return this.#glyphwise(text, forceWidth, () => {});
|
||||
}
|
||||
|
||||
#glyphwise(text: string, forceWidth: number | undefined, callback: (x: number, y: number, char: string) => void): {w: number, h: number} {
|
||||
let cx = 0;
|
||||
let cy = 0;
|
||||
let cw = 0;
|
||||
let ch = 0;
|
||||
let wcx = forceWidth == undefined ? undefined : Math.floor(forceWidth / this.#px);
|
||||
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
let char = text[i]
|
||||
if (char == '\n') {
|
||||
cx = 0;
|
||||
cy += 1;
|
||||
ch = cy + 1;
|
||||
} else {
|
||||
// console.log("cx, cy, char", [cx, cy, char])
|
||||
callback(cx, cy, char)
|
||||
cx += 1;
|
||||
cw = Math.max(cw, cx);
|
||||
ch = cy + 1;
|
||||
|
||||
if (wcx != undefined && cx > wcx) {
|
||||
cx = 0;
|
||||
cy += 1;
|
||||
ch = cy + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { w: cw * this.#px, h: ch * this.#py };
|
||||
}
|
||||
}
|
||||
|
||||
export let mainFont = new Font(fontSheet, 32, 8, 8, 16);
|
172
src/game.ts
Normal file
@ -0,0 +1,172 @@
|
||||
import {desiredHeight, desiredWidth, getScreen} from "./screen.ts";
|
||||
import {BG_OUTER} from "./colors.ts";
|
||||
import {mainFont} from "./font.ts";
|
||||
import {getInput} from "./input.ts";
|
||||
|
||||
type Point = {x: number, y: number}
|
||||
|
||||
class MenuCamera {
|
||||
// measured in whole screens
|
||||
position: Point;
|
||||
target: Point
|
||||
|
||||
constructor({position, target}: {position: Point, target: Point}) {
|
||||
this.position = position;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
update() {
|
||||
let adjust = (x0: number, x1: number) => {
|
||||
if (Math.abs(x1 - x0) < 0.01) { return x1; }
|
||||
return (x0 * 8 + x1 * 2) / 10;
|
||||
}
|
||||
this.position.x = adjust(this.position.x, this.target.x);
|
||||
this.position.y = adjust(this.position.y, this.target.y);
|
||||
}
|
||||
}
|
||||
|
||||
type GameState = "Overview" | "Gameplay" | "Thralls";
|
||||
|
||||
function getScreenLocation(state: GameState): {x: number, y: number} {
|
||||
if (state === "Overview") {
|
||||
return {x: 0.0, y: 0.0}
|
||||
}
|
||||
if (state === "Gameplay") {
|
||||
return {x: 1.0, y: 0.0}
|
||||
}
|
||||
if (state === "Thralls") {
|
||||
return {x: 0.0, y: 1.0}
|
||||
}
|
||||
|
||||
throw `invalid state: ${state}`
|
||||
}
|
||||
|
||||
export class Game {
|
||||
msg: string;
|
||||
camera: MenuCamera;
|
||||
state: GameState;
|
||||
|
||||
constructor() {
|
||||
this.msg = "You have been given a gift.";
|
||||
this.camera = new MenuCamera({
|
||||
position: {x: 0.0, y: 0.0},
|
||||
target: {x: 0.0, y: 0.0}
|
||||
});
|
||||
this.state = "Overview";
|
||||
}
|
||||
|
||||
update() {
|
||||
if (getInput().isPressed("a") || getInput().isPressed("w")) {
|
||||
this.state = "Overview"
|
||||
}
|
||||
if (getInput().isPressed("d")) {
|
||||
this.state = "Gameplay"
|
||||
}
|
||||
if (getInput().isPressed("s")) {
|
||||
this.state = "Thralls"
|
||||
}
|
||||
|
||||
if (getInput().isPressed("leftMouse")) {
|
||||
this.msg = "Left click, asshole!"
|
||||
}
|
||||
if (getInput().isReleased("leftMouse")) {
|
||||
this.msg = "Un-left click, asshole!"
|
||||
}
|
||||
|
||||
this.camera.target = getScreenLocation(this.state);
|
||||
this.camera.update();
|
||||
}
|
||||
|
||||
draw() {
|
||||
let screen = getScreen();
|
||||
let ctx = screen.makeContext();
|
||||
|
||||
// draw screen background
|
||||
ctx.fillStyle = BG_OUTER;
|
||||
ctx.fillRect(0, 0, screen.w, screen.h);
|
||||
|
||||
this.drawOverview();
|
||||
// this.drawGameplay();
|
||||
|
||||
// we draw all states at once and pan between them
|
||||
// mainFont.drawText({ctx: ctx, text: "You have been given a gift.", x: 0, y: 0})
|
||||
let xy = this.getCameraMouseXy();
|
||||
if (xy != null) {
|
||||
ctx = this.makeCameraContext();
|
||||
ctx.fillStyle = "#fff";
|
||||
ctx.strokeStyle = "#fff";
|
||||
ctx.fillRect(xy.x - 1, xy.y - 1, 3, 3);
|
||||
}
|
||||
}
|
||||
|
||||
getCameraOffset(): Point {
|
||||
let screen = getScreen();
|
||||
let {w, h} = screen;
|
||||
return {
|
||||
x: Math.floor(w * this.camera.position.x),
|
||||
y: Math.floor(h * this.camera.position.y)
|
||||
};
|
||||
}
|
||||
|
||||
getCameraMouseXy(): Point | null {
|
||||
let xy = getInput().mouseXy();
|
||||
if (xy == null) {
|
||||
return null;
|
||||
}
|
||||
let {x: dx, y: dy} = this.getCameraOffset();
|
||||
return {
|
||||
x: xy.x + dx,
|
||||
y: xy.y + dy,
|
||||
};
|
||||
}
|
||||
|
||||
makeCameraContext() {
|
||||
let screen = getScreen();
|
||||
let ctx = screen.makeContext();
|
||||
let {x, y} = this.getCameraOffset();
|
||||
|
||||
ctx.translate(-x, -y);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
getPaneRegionForGameState(gameState: GameState) {
|
||||
let screen = getScreen();
|
||||
let {w, h} = screen;
|
||||
let overallScreenLocation = getScreenLocation(gameState);
|
||||
|
||||
let bigPaneX = overallScreenLocation.x * w;
|
||||
let bigPaneY = overallScreenLocation.y * h;
|
||||
let bigPaneW = w;
|
||||
let bigPaneH = h;
|
||||
|
||||
let smallPaneW = desiredWidth;
|
||||
let smallPaneH = desiredHeight;
|
||||
let smallPaneX = Math.floor(bigPaneX + (bigPaneW - smallPaneW) / 2)
|
||||
let smallPaneY = Math.floor(bigPaneY + (bigPaneH - smallPaneH) / 2)
|
||||
|
||||
return {
|
||||
big: {
|
||||
position: {x: bigPaneX, y: bigPaneY},
|
||||
size: {x: bigPaneW, y: bigPaneH}
|
||||
},
|
||||
small: {
|
||||
position: {x: smallPaneX, y: smallPaneY},
|
||||
size: {x: smallPaneW, y: smallPaneH}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
drawOverview() {
|
||||
let region = this.getPaneRegionForGameState("Overview")
|
||||
let ctx = this.makeCameraContext()
|
||||
ctx.translate(region.small.position.x, region.small.position.y)
|
||||
|
||||
// ctx.strokeStyle = "#ffffff";
|
||||
// ctx.strokeRect(0.5, 0.5, region.small.size.x - 1, region.small.size.y - 1);
|
||||
|
||||
mainFont.drawText({ctx: ctx, text: this.msg, x: 0, y: 0})
|
||||
}
|
||||
}
|
||||
|
||||
export let game = new Game();
|
108
src/input.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import {getScreen} from "./screen.ts";
|
||||
|
||||
function handleKey(e: KeyboardEvent, down: boolean) {
|
||||
active.handleDown(e.key, down);
|
||||
}
|
||||
function handleMouseOut() {
|
||||
active.handleMouseMove(-1, -1);
|
||||
}
|
||||
|
||||
function handleMouseMove(canvas: HTMLCanvasElement, m: MouseEvent) {
|
||||
if (canvas.offsetWidth == 0 || canvas.offsetHeight == 0) {
|
||||
return;
|
||||
}
|
||||
active.handleMouseMove(m.offsetX / canvas.offsetWidth, m.offsetY / canvas.offsetHeight);
|
||||
}
|
||||
|
||||
function handleMouseButton(canvas: HTMLCanvasElement, m: MouseEvent, down: boolean) {
|
||||
if (canvas.offsetWidth == 0 || canvas.offsetHeight == 0) {
|
||||
return;
|
||||
}
|
||||
active.handleMouseMove(m.offsetX / canvas.offsetWidth, m.offsetY / canvas.offsetHeight);
|
||||
let button: KnownButton | null = (
|
||||
m.button == 0 ? "leftMouse" :
|
||||
m.button == 1 ? "rightMouse" :
|
||||
null
|
||||
)
|
||||
if (button != null) {
|
||||
active.handleDown(button, down);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function setupInput(canvas: HTMLCanvasElement) {
|
||||
canvas.addEventListener("keyup", (k) => handleKey(k, false));
|
||||
document.addEventListener("keyup", (k) => handleKey(k, false));
|
||||
canvas.addEventListener("keydown", (k) => handleKey(k, true));
|
||||
document.addEventListener("keydown", (k) => handleKey(k, true));
|
||||
canvas.addEventListener("mouseout", (_) => handleMouseOut());
|
||||
canvas.addEventListener("mousemove", (m) => handleMouseMove(canvas, m));
|
||||
canvas.addEventListener("mousedown", (m) => handleMouseButton(canvas, m, true));
|
||||
canvas.addEventListener("mouseup", (m) => handleMouseButton(canvas, m, false));
|
||||
}
|
||||
|
||||
type KnownKey = "w" | "a" | "s" | "d";
|
||||
type KnownButton = "leftMouse" | "rightMouse";
|
||||
|
||||
class Input {
|
||||
#down: Record<string, boolean>;
|
||||
#previousDown: Record<string, boolean>;
|
||||
#mouseXy: {x: number, y: number} | null;
|
||||
|
||||
constructor() {
|
||||
this.#down = {};
|
||||
this.#previousDown = {};
|
||||
this.#mouseXy = null;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.#previousDown = {...this.#down};
|
||||
}
|
||||
|
||||
handleDown(name: string, down: boolean) {
|
||||
if (down) {
|
||||
this.#down[name] = down;
|
||||
}
|
||||
else {
|
||||
delete this.#down[name];
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseMove(x: number, y: number) {
|
||||
let screen = getScreen();
|
||||
if (x < 0.0 || x >= 1.0) { this.#mouseXy = null; }
|
||||
if (y < 0.0 || y >= 1.0) { this.#mouseXy = null; }
|
||||
|
||||
let w = screen.w;
|
||||
let h = screen.h;
|
||||
this.#mouseXy = {
|
||||
x: Math.floor(x * w),
|
||||
y: Math.floor(y * h),
|
||||
}
|
||||
}
|
||||
|
||||
isDown(key: KnownKey | KnownButton) : boolean {
|
||||
return this.#down[key];
|
||||
}
|
||||
|
||||
isPressed(key: KnownKey | KnownButton) : boolean {
|
||||
return this.#down[key] && !this.#previousDown[key];
|
||||
}
|
||||
|
||||
isReleased(key: KnownKey | KnownButton) : boolean {
|
||||
return !this.#down[key] && this.#previousDown[key];
|
||||
}
|
||||
|
||||
mouseXy(): {x: number, y: number} | null {
|
||||
if (this.#mouseXy == null) {
|
||||
return null;
|
||||
}
|
||||
return {...this.#mouseXy}
|
||||
}
|
||||
}
|
||||
|
||||
let active = new Input();
|
||||
|
||||
export function getInput(): Input {
|
||||
return active;
|
||||
}
|
87
src/main.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import './style.css'
|
||||
import {pollAndTouch} from "./screen.ts";
|
||||
import {getClock} from "./clock.ts";
|
||||
import {game} from "./game.ts";
|
||||
import {getInput, setupInput} from "./input.ts";
|
||||
// import typescriptLogo from './typescript.svg'
|
||||
// import viteLogo from '/vite.svg'
|
||||
// import { setupCounter } from './counter.ts'
|
||||
|
||||
// import {AlignX, mainFont} from "./font.ts";
|
||||
|
||||
|
||||
function setupGame() {
|
||||
let gameCanvas = document.getElementById("game") as HTMLCanvasElement;
|
||||
setupInput(gameCanvas);
|
||||
onFrame(undefined); // start on-frame draw loop, set up screen
|
||||
}
|
||||
|
||||
function onFrame(timestamp: number | undefined) {
|
||||
let gameCanvas = document.getElementById("game") as HTMLCanvasElement;
|
||||
requestAnimationFrame(onFrame);
|
||||
|
||||
if (timestamp) {
|
||||
getClock().recordTimestamp(timestamp);
|
||||
}
|
||||
onFrameFixScreen(gameCanvas);
|
||||
|
||||
while (getClock().popUpdate()) {
|
||||
game.update();
|
||||
getInput().update();
|
||||
}
|
||||
|
||||
game.draw();
|
||||
|
||||
/*
|
||||
let ctx = getScreen().canvas.getContext("2d")!;
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.fillRect(0, 0, gameCanvas.width, gameCanvas.height);
|
||||
|
||||
// ctx.drawImage(getAssets().getImage(font), 0, frame % (getScreen().h + 256) - 128);
|
||||
// console.log(mainFont.measureText({text: "a!\nb\n"}));
|
||||
mainFont.drawText({
|
||||
ctx: ctx,
|
||||
text: "Hello, world!\nI wish you luck!",
|
||||
x: gameCanvas.width,
|
||||
y: 0,
|
||||
color: "#f00",
|
||||
alignX: AlignX.Right,
|
||||
});
|
||||
mainFont.drawText({
|
||||
ctx: ctx,
|
||||
text: "^._.^",
|
||||
x: gameCanvas.width/2,
|
||||
y: 32,
|
||||
color: "#0ff",
|
||||
alignX: AlignX.Center,
|
||||
});
|
||||
*/
|
||||
}
|
||||
|
||||
function onFrameFixScreen(canvas: HTMLCanvasElement) {
|
||||
pollAndTouch(canvas);
|
||||
}
|
||||
|
||||
setupGame();
|
||||
|
||||
/*
|
||||
document.querySelector<HTMLDivElement>('#app')!.innerHTML = `
|
||||
<div>
|
||||
<a href="https://vite.dev" target="_blank">
|
||||
<img src="${viteLogo}" class="logo" alt="Vite logo" />
|
||||
</a>
|
||||
<a href="https://www.typescriptlang.org/" target="_blank">
|
||||
<img src="${typescriptLogo}" class="logo vanilla" alt="TypeScript logo" />
|
||||
</a>
|
||||
<h1>Vite + TypeScript</h1>
|
||||
<div class="card">
|
||||
<button id="counter" type="button"></button>
|
||||
</div>
|
||||
<p class="read-the-docs">
|
||||
Click on the Vite and TypeScript logos to learn more
|
||||
</p>
|
||||
</div>
|
||||
`
|
||||
|
||||
setupCounter(document.querySelector<HTMLButtonElement>('#counter')!)
|
||||
*/
|
62
src/screen.ts
Normal file
@ -0,0 +1,62 @@
|
||||
class Screen {
|
||||
#canvas: HTMLCanvasElement
|
||||
w: number
|
||||
h: number
|
||||
|
||||
constructor(canvas: HTMLCanvasElement, w: number, h: number) {
|
||||
this.#canvas = canvas;
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
}
|
||||
|
||||
makeContext(): CanvasRenderingContext2D {
|
||||
let ctx = this.#canvas.getContext("2d")!;
|
||||
|
||||
// TODO: Other stuff to do here?
|
||||
ctx.strokeStyle = "#000";
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.resetTransform();
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let active: Screen | undefined = undefined
|
||||
export let desiredWidth = 384;
|
||||
export let desiredHeight = 384;
|
||||
|
||||
export function pollAndTouch(canvas: HTMLCanvasElement) {
|
||||
let realWidth = canvas.offsetWidth;
|
||||
let realHeight = canvas.offsetHeight;
|
||||
|
||||
let divisors = [0.25, 0.5, 1, 2, 3, 4, 5, 6];
|
||||
for (let i = 0; i < divisors.length; i++) {
|
||||
// TODO: Is this necessary?
|
||||
divisors[i] /= window.devicePixelRatio;
|
||||
}
|
||||
|
||||
let div = 0;
|
||||
while (
|
||||
(div < divisors.length - 1) &&
|
||||
(realWidth / divisors[div + 1] >= desiredWidth) &&
|
||||
(realHeight / divisors[div + 1] >= desiredHeight)
|
||||
) {
|
||||
div += 1;
|
||||
}
|
||||
realWidth = Math.floor(canvas.offsetWidth / divisors[div]);
|
||||
realHeight = Math.floor(canvas.offsetHeight / divisors[div]);
|
||||
canvas.width = realWidth;
|
||||
canvas.height = realHeight;
|
||||
active = new Screen(canvas, realWidth, realHeight);
|
||||
}
|
||||
|
||||
export function getScreen(): Screen {
|
||||
if (active === undefined) {
|
||||
throw `screen should have been defined: ${active}`
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
|
48
src/sprite.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import {getAssets} from "./assets.ts";
|
||||
|
||||
|
||||
export class Sprite {
|
||||
readonly imageSet: string;
|
||||
// spritesheet params
|
||||
// image size (px, py)
|
||||
readonly px: number; readonly py: number;
|
||||
// origin (ox, oy)
|
||||
readonly ox: number; readonly oy: number;
|
||||
// dimension in cells (cx, cy)
|
||||
readonly cx: number; readonly cy: number;
|
||||
// number of frames
|
||||
readonly nFrames: number;
|
||||
|
||||
constructor(imageSet: string, px: number, py: number, ox: number, oy: number, cx: number, cy: number, nFrames: number) {
|
||||
this.imageSet = imageSet;
|
||||
this.px = px; this.py = py;
|
||||
this.ox = ox; this.oy = oy;
|
||||
this.cx = cx; this.cy = cy;
|
||||
this.nFrames = nFrames;
|
||||
|
||||
if (this.nFrames < this.cx * this.cy) {
|
||||
throw `can't have ${this.nFrames} with a spritesheet dimension of ${this.cx * this.cy}`;
|
||||
}
|
||||
}
|
||||
|
||||
draw(ctx: CanvasRenderingContext2D, {x, y, ix, xScale, yScale, angle}: {x: number, y: number, ix?: number, xScale?: number, yScale?: number, angle?: number}) {
|
||||
ix = ix == undefined ? 0 : ix;
|
||||
xScale = xScale == undefined ? 1.0 : xScale;
|
||||
yScale = yScale == undefined ? 1.0 : yScale;
|
||||
angle = angle == undefined ? 0.0 : angle;
|
||||
|
||||
// ctx.translate(Math.floor(x), Math.floor(y));
|
||||
ctx.translate(x, y);
|
||||
ctx.rotate(angle * Math.PI / 180);
|
||||
ctx.scale(xScale, yScale);
|
||||
ctx.translate(-this.ox, -this.oy);
|
||||
|
||||
let me = getAssets().getImage(this.imageSet);
|
||||
let srcCx = ix % this.cx;
|
||||
let srcCy = Math.floor(ix / this.cx);
|
||||
let srcPx = srcCx * this.px;
|
||||
let srcPy = srcCy * this.py;
|
||||
console.log(`src px and py ${srcPx} ${srcPy}`)
|
||||
ctx.drawImage(me, srcPx, srcPy, this.px, this.py, 0, 0, this.px, this.py);
|
||||
}
|
||||
}
|
14
src/sprites.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {Sprite} from "./sprite.ts";
|
||||
import imgBat from "./art/characters/bat.png";
|
||||
import imgKobold from "./art/characters/kobold.png";
|
||||
import imgRaccoon from "./art/characters/raccoon.png";
|
||||
import imgRaccoonWalking from "./art/characters/raccoon_walking.png";
|
||||
import imgRobot from "./art/characters/robot.png";
|
||||
import imgSnake from "./art/characters/snake.png";
|
||||
|
||||
export let sprBat = new Sprite(imgBat, 64, 64, 32, 32, 1, 1, 1);
|
||||
export let sprKobold = new Sprite(imgKobold, 64, 64, 32, 32, 1, 1, 1);
|
||||
export let sprRaccoon = new Sprite(imgRaccoon, 64, 64, 32, 32, 1, 1, 1);
|
||||
export let sprRaccoonWalking = new Sprite(imgRaccoonWalking, 64, 64, 32, 32, 8, 1, 8);
|
||||
export let sprRobot = new Sprite(imgRobot, 64, 64, 32, 32, 1, 1, 1);
|
||||
export let sprSnake = new Sprite(imgSnake, 64, 64, 32, 32, 1, 1, 1);
|
12
src/style.css
Normal file
@ -0,0 +1,12 @@
|
||||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#game {
|
||||
image-rendering: pixelated;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
24
tsconfig.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|