From 9b5ab25c4dae039a18c2c143385149b078ab28d4 Mon Sep 17 00:00:00 2001
From: Sheldan <5037282+Sheldan@users.noreply.github.com>
Date: Thu, 21 Aug 2025 13:16:29 +0200
Subject: [PATCH] survivors: adding first version
---
.github/workflows/build.yml | 24 +-
absurd-survivors/.gitignore | 24 +
absurd-survivors/index.html | 13 +
absurd-survivors/package-lock.json | 1088 ++++++++++++++++++++++++++++
absurd-survivors/package.json | 16 +
absurd-survivors/src/Enemies.ts | 144 ++++
absurd-survivors/src/Player.ts | 116 +++
absurd-survivors/src/World.ts | 70 ++
absurd-survivors/src/base.ts | 59 ++
absurd-survivors/src/instance.ts | 7 +
absurd-survivors/src/interfaces.ts | 31 +
absurd-survivors/src/main.ts | 133 ++++
absurd-survivors/src/projectile.ts | 100 +++
absurd-survivors/src/style.css | 4 +
absurd-survivors/src/ui.ts | 72 ++
absurd-survivors/src/utils.ts | 18 +
absurd-survivors/src/vite-env.d.ts | 1 +
absurd-survivors/src/weapons.ts | 59 ++
absurd-survivors/tsconfig.json | 25 +
absurd-survivors/vite.config.js | 7 +
img/survivors.png | Bin 0 -> 40381 bytes
index.html | 1 +
22 files changed, 2003 insertions(+), 9 deletions(-)
create mode 100644 absurd-survivors/.gitignore
create mode 100644 absurd-survivors/index.html
create mode 100644 absurd-survivors/package-lock.json
create mode 100644 absurd-survivors/package.json
create mode 100644 absurd-survivors/src/Enemies.ts
create mode 100644 absurd-survivors/src/Player.ts
create mode 100644 absurd-survivors/src/World.ts
create mode 100644 absurd-survivors/src/base.ts
create mode 100644 absurd-survivors/src/instance.ts
create mode 100644 absurd-survivors/src/interfaces.ts
create mode 100644 absurd-survivors/src/main.ts
create mode 100644 absurd-survivors/src/projectile.ts
create mode 100644 absurd-survivors/src/style.css
create mode 100644 absurd-survivors/src/ui.ts
create mode 100644 absurd-survivors/src/utils.ts
create mode 100644 absurd-survivors/src/vite-env.d.ts
create mode 100644 absurd-survivors/src/weapons.ts
create mode 100644 absurd-survivors/tsconfig.json
create mode 100644 absurd-survivors/vite.config.js
create mode 100644 img/survivors.png
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 406ced2..fbd031d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -29,59 +29,65 @@ jobs:
with:
node-version: 21
- name: Orbits Install dependencies
- run: npm install
+ run: npm ci
working-directory: orbits
- name: Orbits Build
run: npx vite build
working-directory: orbits
- name: recBubbles Install dependencies
- run: npm install
+ run: npm ci
working-directory: recBubbles
- name: recBubbles Build
run: npx vite build
working-directory: recBubbles
- name: balls Install dependencies
- run: npm install
+ run: npm ci
working-directory: balls
- name: balls Build
run: npx vite build
working-directory: balls
- name: fireWorks Install dependencies
- run: npm install
+ run: npm ci
working-directory: fireWorks
- name: fireWorks Build
run: npx vite build
working-directory: fireWorks
- name: bubbles Install dependencies
- run: npm install
+ run: npm ci
working-directory: bubbles
- name: bubbles Build
run: npx vite build
working-directory: bubbles
- name: circleBs Install dependencies
- run: npm install
+ run: npm ci
working-directory: circleBs
- name: circleBs Build
run: npx vite build
working-directory: circleBs
- name: clusterFilter Install dependencies
- run: npm install
+ run: npm ci
working-directory: clusterFilter
- name: clusterFilter Build
run: npx vite build
working-directory: clusterFilter
- name: collatzConjecture Install dependencies
- run: npm install
+ run: npm ci
working-directory: collatzConjecture
- name: collatzConjecture Build
run: npx vite build
working-directory: collatzConjecture
- name: dotLines Install dependencies
- run: npm install
+ run: npm ci
working-directory: dotLines
- name: dotLines Build
run: npx vite build
working-directory: dotLines
+ - name: survivors install dependencies
+ run: npm ci
+ working-directory: absurd-survivors
+ - name: survivors build
+ run: npx vite build
+ working-directory: absurd-survivors
- name: Move index
run: cp index.html dist/
- name: Move overview images
diff --git a/absurd-survivors/.gitignore b/absurd-survivors/.gitignore
new file mode 100644
index 0000000..a547bf3
--- /dev/null
+++ b/absurd-survivors/.gitignore
@@ -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?
diff --git a/absurd-survivors/index.html b/absurd-survivors/index.html
new file mode 100644
index 0000000..b00cce9
--- /dev/null
+++ b/absurd-survivors/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+ overtuned survivors
+
+
+
+
+
+
+
diff --git a/absurd-survivors/package-lock.json b/absurd-survivors/package-lock.json
new file mode 100644
index 0000000..3a0d662
--- /dev/null
+++ b/absurd-survivors/package-lock.json
@@ -0,0 +1,1088 @@
+{
+ "name": "overtuned-survivors",
+ "version": "0.0.1",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "overtuned-survivors",
+ "version": "0.0.1",
+ "devDependencies": {
+ "canvas-common": "file:../canvas-common",
+ "typescript": "~5.8.3",
+ "vite": "^7.1.2"
+ }
+ },
+ "../canvas-common": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.46.3",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.46.3",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/canvas-common": {
+ "resolved": "../canvas-common",
+ "link": true
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "dev": true,
+ "hasInstallScript": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.9",
+ "@esbuild/android-arm": "0.25.9",
+ "@esbuild/android-arm64": "0.25.9",
+ "@esbuild/android-x64": "0.25.9",
+ "@esbuild/darwin-arm64": "0.25.9",
+ "@esbuild/darwin-x64": "0.25.9",
+ "@esbuild/freebsd-arm64": "0.25.9",
+ "@esbuild/freebsd-x64": "0.25.9",
+ "@esbuild/linux-arm": "0.25.9",
+ "@esbuild/linux-arm64": "0.25.9",
+ "@esbuild/linux-ia32": "0.25.9",
+ "@esbuild/linux-loong64": "0.25.9",
+ "@esbuild/linux-mips64el": "0.25.9",
+ "@esbuild/linux-ppc64": "0.25.9",
+ "@esbuild/linux-riscv64": "0.25.9",
+ "@esbuild/linux-s390x": "0.25.9",
+ "@esbuild/linux-x64": "0.25.9",
+ "@esbuild/netbsd-arm64": "0.25.9",
+ "@esbuild/netbsd-x64": "0.25.9",
+ "@esbuild/openbsd-arm64": "0.25.9",
+ "@esbuild/openbsd-x64": "0.25.9",
+ "@esbuild/openharmony-arm64": "0.25.9",
+ "@esbuild/sunos-x64": "0.25.9",
+ "@esbuild/win32-arm64": "0.25.9",
+ "@esbuild/win32-ia32": "0.25.9",
+ "@esbuild/win32-x64": "0.25.9"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.46.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.46.3",
+ "@rollup/rollup-android-arm64": "4.46.3",
+ "@rollup/rollup-darwin-arm64": "4.46.3",
+ "@rollup/rollup-darwin-x64": "4.46.3",
+ "@rollup/rollup-freebsd-arm64": "4.46.3",
+ "@rollup/rollup-freebsd-x64": "4.46.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.46.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.46.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.46.3",
+ "@rollup/rollup-linux-arm64-musl": "4.46.3",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.46.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.46.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.46.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.46.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.46.3",
+ "@rollup/rollup-linux-x64-gnu": "4.46.3",
+ "@rollup/rollup-linux-x64-musl": "4.46.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.46.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.46.3",
+ "@rollup/rollup-win32-x64-msvc": "4.46.3",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz",
+ "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.14"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ }
+ },
+ "dependencies": {
+ "@esbuild/aix-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/android-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/darwin-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/freebsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-mips64el": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-riscv64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-s390x": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/linux-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/netbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/netbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/openharmony-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/sunos-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "dev": true,
+ "optional": true
+ },
+ "@esbuild/win32-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-gnu": {
+ "version": "4.46.3",
+ "dev": true,
+ "optional": true
+ },
+ "@rollup/rollup-linux-x64-musl": {
+ "version": "4.46.3",
+ "dev": true,
+ "optional": true
+ },
+ "@types/estree": {
+ "version": "1.0.8",
+ "dev": true
+ },
+ "canvas-common": {
+ "version": "file:../canvas-common"
+ },
+ "esbuild": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "dev": true,
+ "requires": {
+ "@esbuild/aix-ppc64": "0.25.9",
+ "@esbuild/android-arm": "0.25.9",
+ "@esbuild/android-arm64": "0.25.9",
+ "@esbuild/android-x64": "0.25.9",
+ "@esbuild/darwin-arm64": "0.25.9",
+ "@esbuild/darwin-x64": "0.25.9",
+ "@esbuild/freebsd-arm64": "0.25.9",
+ "@esbuild/freebsd-x64": "0.25.9",
+ "@esbuild/linux-arm": "0.25.9",
+ "@esbuild/linux-arm64": "0.25.9",
+ "@esbuild/linux-ia32": "0.25.9",
+ "@esbuild/linux-loong64": "0.25.9",
+ "@esbuild/linux-mips64el": "0.25.9",
+ "@esbuild/linux-ppc64": "0.25.9",
+ "@esbuild/linux-riscv64": "0.25.9",
+ "@esbuild/linux-s390x": "0.25.9",
+ "@esbuild/linux-x64": "0.25.9",
+ "@esbuild/netbsd-arm64": "0.25.9",
+ "@esbuild/netbsd-x64": "0.25.9",
+ "@esbuild/openbsd-arm64": "0.25.9",
+ "@esbuild/openbsd-x64": "0.25.9",
+ "@esbuild/openharmony-arm64": "0.25.9",
+ "@esbuild/sunos-x64": "0.25.9",
+ "@esbuild/win32-arm64": "0.25.9",
+ "@esbuild/win32-ia32": "0.25.9",
+ "@esbuild/win32-x64": "0.25.9"
+ }
+ },
+ "fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "requires": {}
+ },
+ "fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "optional": true
+ },
+ "nanoid": {
+ "version": "3.3.11",
+ "dev": true
+ },
+ "picocolors": {
+ "version": "1.1.1",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true
+ },
+ "postcss": {
+ "version": "8.5.6",
+ "dev": true,
+ "requires": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "rollup": {
+ "version": "4.46.3",
+ "dev": true,
+ "requires": {
+ "@rollup/rollup-android-arm-eabi": "4.46.3",
+ "@rollup/rollup-android-arm64": "4.46.3",
+ "@rollup/rollup-darwin-arm64": "4.46.3",
+ "@rollup/rollup-darwin-x64": "4.46.3",
+ "@rollup/rollup-freebsd-arm64": "4.46.3",
+ "@rollup/rollup-freebsd-x64": "4.46.3",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.46.3",
+ "@rollup/rollup-linux-arm-musleabihf": "4.46.3",
+ "@rollup/rollup-linux-arm64-gnu": "4.46.3",
+ "@rollup/rollup-linux-arm64-musl": "4.46.3",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.46.3",
+ "@rollup/rollup-linux-ppc64-gnu": "4.46.3",
+ "@rollup/rollup-linux-riscv64-gnu": "4.46.3",
+ "@rollup/rollup-linux-riscv64-musl": "4.46.3",
+ "@rollup/rollup-linux-s390x-gnu": "4.46.3",
+ "@rollup/rollup-linux-x64-gnu": "4.46.3",
+ "@rollup/rollup-linux-x64-musl": "4.46.3",
+ "@rollup/rollup-win32-arm64-msvc": "4.46.3",
+ "@rollup/rollup-win32-ia32-msvc": "4.46.3",
+ "@rollup/rollup-win32-x64-msvc": "4.46.3",
+ "@types/estree": "1.0.8",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "source-map-js": {
+ "version": "1.2.1",
+ "dev": true
+ },
+ "tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "requires": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ }
+ },
+ "typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true
+ },
+ "vite": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz",
+ "integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.5.0",
+ "fsevents": "~2.3.3",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.14"
+ }
+ }
+ }
+}
diff --git a/absurd-survivors/package.json b/absurd-survivors/package.json
new file mode 100644
index 0000000..f40bd76
--- /dev/null
+++ b/absurd-survivors/package.json
@@ -0,0 +1,16 @@
+{
+ "name": "overtuned-survivors",
+ "private": true,
+ "version": "0.0.1",
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "canvas-common": "file:../canvas-common",
+ "typescript": "~5.8.3",
+ "vite": "^7.1.2"
+ }
+}
diff --git a/absurd-survivors/src/Enemies.ts b/absurd-survivors/src/Enemies.ts
new file mode 100644
index 0000000..6ed1b23
--- /dev/null
+++ b/absurd-survivors/src/Enemies.ts
@@ -0,0 +1,144 @@
+import type {Acting, Drawable, Healthy, Moving, Shooting} from "./interfaces.ts";
+import {drawDot, moveInDirectionOf} from "./utils.ts";
+import {Vector} from "./base.ts";
+import {World} from "./World.ts";
+import type {Projectile} from "./projectile.ts";
+import {HomingProjectile, StraightProjectile} from "./projectile.ts";
+
+export abstract class Enemy implements Moving, Drawable, Acting, Healthy {
+ protected _position: Vector;
+ protected speed: number;
+ protected world: World;
+ protected status: EnemyStatus = new EnemyStatus(10);
+
+ constructor(position: Vector) {
+ this._position = position;
+ }
+
+ draw(ctx: CanvasRenderingContext2D) {
+ }
+
+ act() {
+ this.move()
+ }
+
+ move() {
+ }
+
+
+ getPosition(): Vector {
+ return this._position;
+ }
+
+ takeDamage(damage: number) {
+ this.status.health -= damage;
+ if(this.status.dead) {
+ this.world.removeEnemy(this)
+ }
+ }
+}
+
+export class BasicEnemy extends Enemy {
+
+ constructor(position: Vector) {
+ super(position);
+ }
+
+ protected size: number;
+ protected color: string;
+ protected impactDamage: number;
+ protected impactCooldown: number = 0;
+ protected impactInterval: number = 60;
+
+ draw(ctx: CanvasRenderingContext2D) {
+ drawDot(this._position, this.size, this.color, ctx)
+ }
+
+
+ move() {
+ this._position = moveInDirectionOf(this._position, this.world.player.position, this.speed)
+ }
+
+ act() {
+ super.act();
+ if(this._position.distanceTo(this.world.player.position) < this.size && this.impactCooldown <= 0) {
+ this.world.player.takeDamage(this.impactDamage)
+ this.impactCooldown = this.impactInterval;
+ }
+ this.impactCooldown -= 1;
+ }
+
+ static generateBasicEnemy(world: World, position?: Vector): BasicEnemy {
+ if(position === undefined) {
+ position = new Vector(250, 250)
+ }
+ let basicEnemy = new BasicEnemy(position);
+ basicEnemy.size = 5;
+ basicEnemy.world = world;
+ basicEnemy.speed = 0.5;
+ basicEnemy.color = 'orange'
+ basicEnemy.impactDamage = 2;
+ return basicEnemy;
+ }
+}
+
+export class ShootingEnemy extends BasicEnemy implements Shooting {
+ private shootCooldown: number = 0;
+ private shootInterval: number;
+ private projectiles: Projectile[] = []
+
+ constructor(position: Vector) {
+ super(position);
+ }
+
+ removeProjectile(projectile: Projectile) {
+ this.projectiles = this.projectiles.filter(item => item !== projectile)
+ }
+
+ act() {
+ super.act();
+ if(this.shootCooldown <= 0) {
+ this.createProjectile()
+ this.shootCooldown = this.shootInterval;
+ }
+ this.shootCooldown -= 1;
+ }
+
+ createProjectile() {
+ let projectile = StraightProjectile.createStraightProjectile(this.world, this._position, this.world.player.position, this)
+ this.projectiles.push(projectile)
+ return projectile
+ }
+
+ static generateShootingEnemy(world: World, position?: Vector) {
+ if(position === undefined) {
+ position = new Vector(250, 250)
+ }
+ let basicEnemy = new ShootingEnemy(position);
+ basicEnemy.size = 5;
+ basicEnemy.world = world;
+ basicEnemy.speed = 0.5;
+ basicEnemy.color = 'green'
+ basicEnemy.impactDamage = 2;
+ basicEnemy.shootInterval = 100
+ return basicEnemy;
+ }
+}
+
+export class EnemyStatus {
+ constructor(private _health: number) {
+ }
+
+
+ get health(): number {
+ return this._health;
+ }
+
+ get dead(): boolean {
+ return this._health <= 0;
+ }
+
+ set health(value: number) {
+ this._health = value;
+ }
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/Player.ts b/absurd-survivors/src/Player.ts
new file mode 100644
index 0000000..5976383
--- /dev/null
+++ b/absurd-survivors/src/Player.ts
@@ -0,0 +1,116 @@
+import type {Acting, Drawable, Healthy, Weapon} from "./interfaces.ts";
+import {Vector} from "./base.ts";
+import {drawDot} from "./utils.ts";
+
+export class Player implements Drawable, Acting, Healthy {
+ private _position: Vector;
+ private _stats: Stats;
+ private _color: string;
+ private _status: Status;
+ private _weapons: [Weapon] = []
+
+ // temp
+ private _speed: Vector;
+
+
+ constructor(position: Vector) {
+ this._position = position;
+ }
+
+ draw(ctx: CanvasRenderingContext2D) {
+ drawDot(this.position, this._stats.size, this._color, ctx)
+ this._weapons.forEach(weapon => weapon.draw(ctx))
+ }
+
+ public static generatePlayer(): Player {
+ let player = new Player(new Vector(500, 500));
+ player._color = 'blue';
+ player._stats = Stats.defaultPlayerStats();
+ player._speed = new Vector(0, 0)
+ player._status = new Status(10);
+ return player;
+ }
+
+ addWeapon(weapon: Weapon) {
+ this._weapons.push(weapon)
+ }
+
+ move(direction: Vector) {
+ this._position = this.position.add(direction)
+ }
+
+ takeDamage(damage: number) {
+ this._status.health -= damage;
+ }
+
+ get health(): number {
+ return this._status.health;
+ }
+
+ get position(): Vector {
+ return this._position;
+ }
+
+ get color(): string {
+ return this._color;
+ }
+
+ get stats(): Stats {
+ return this._stats;
+ }
+
+ get speed(): Vector {
+ return this._speed;
+ }
+
+ act() {
+ this._weapons.forEach(weapon => weapon.act())
+ }
+}
+
+export class Status {
+ constructor(private _health: number) {
+ }
+
+
+ get health(): number {
+ return this._health;
+ }
+
+
+ set health(value: number) {
+ this._health = value;
+ }
+}
+
+export class Stats {
+ constructor(private _speed: number,
+ private _size: number,
+ private _health: number) {
+ }
+
+ get speed(): number {
+ return this._speed;
+ }
+
+ set speed(value: number) {
+ this._speed = value;
+ }
+
+ get size(): number {
+ return this._size;
+ }
+
+ set size(value: number) {
+ this._size = value;
+ }
+
+
+ get health(): number {
+ return this._health;
+ }
+
+ public static defaultPlayerStats(): Stats {
+ return new Stats(2, 5, 10);
+ }
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/World.ts b/absurd-survivors/src/World.ts
new file mode 100644
index 0000000..209fb18
--- /dev/null
+++ b/absurd-survivors/src/World.ts
@@ -0,0 +1,70 @@
+import {Enemy} from "./Enemies.ts";
+import type {Player} from "./Player.ts";
+import {Player} from "./Player.ts";
+import {Projectile} from "./projectile.ts";
+import {Vector} from "./base.ts";
+import type {Moving} from "./interfaces.ts";
+
+export class World {
+ private _enemies: [Enemy] = [];
+ private _projectiles: [Projectile] = [];
+ private _player: Player;
+ private _ctx: CanvasRenderingContext2D;
+
+
+ constructor(player: Player, ctx: CanvasRenderingContext2D) {
+ this._player = player;
+ this._ctx = ctx;
+ }
+
+ enemiesAct() {
+ this._enemies.forEach(enemy => enemy.act())
+ this._projectiles.forEach(projectile => projectile.act())
+ }
+
+ draw() {
+ this._enemies.forEach(enemy => enemy.draw(this._ctx))
+ this._projectiles.forEach(projectile => projectile.draw(this._ctx))
+ this._player.draw(this._ctx);
+ }
+
+ addProjectile(projectile: Projectile) {
+ this._projectiles.push(projectile)
+ }
+
+ removeProjectile(projectile: Projectile) {
+ this._projectiles = this._projectiles.filter(item => item !== projectile)
+ }
+
+ removeEnemy(enemy: Enemy) {
+ this._enemies = this._enemies.filter(item => item !== enemy)
+ }
+
+
+ get enemies(): [Enemy] {
+ return this._enemies;
+ }
+
+ addEnemy(enemy: Enemy) {
+ this._enemies.push(enemy)
+ }
+
+ getClosestTargetTo(point: Vector): [number, Moving | undefined] | undefined {
+ let currentTarget;
+ let currentDistance = Number.MAX_SAFE_INTEGER;
+ this._enemies.forEach(enemy => {
+ let distance = point.distanceTo(enemy.getPosition());
+ if(distance < currentDistance) {
+ currentDistance = distance;
+ currentTarget = enemy
+ }
+ })
+ if(currentTarget) {
+ return [currentDistance, currentTarget];
+ }
+ }
+
+ get player(): Player {
+ return this._player;
+ }
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/base.ts b/absurd-survivors/src/base.ts
new file mode 100644
index 0000000..0264ac7
--- /dev/null
+++ b/absurd-survivors/src/base.ts
@@ -0,0 +1,59 @@
+export class Vector {
+
+ constructor(private _x: number, private _y: number) {
+ }
+
+ static createVector(tip: Vector, shaft: Vector): Vector {
+ return new Vector(tip.x - shaft.x, tip.y - shaft.y);
+ }
+
+ normalize(): Vector {
+ let length = this.vecLength();
+ return new Vector(this.x / length, this.y / length)
+ }
+
+ vecLength(): number {
+ return Math.sqrt(this.x * this.x + this.y * this.y);
+ }
+
+ distanceTo(point: Vector): number {
+ return Math.sqrt(Math.pow(this.x - point.x, 2) + Math.pow(this.y - point.y, 2));
+ }
+
+ add(vec: Vector): Vector {
+ return new Vector(this._x + vec._x, this._y + vec._y)
+ }
+
+ minus(vec: Vector): Vector {
+ return new Vector(this.x - vec._x, this.y - vec.y)
+ }
+
+ multiply(number: number): Vector {
+ return new Vector(this.x * number, this.y * number)
+ }
+
+ multiplyVec(vec: Vector): Vector {
+ return new Vector(this.x * vec._x, this.y * vec.y)
+ }
+
+ negate(): Vector {
+ return this.multiply(-1)
+ }
+
+
+ get x(): number {
+ return this._x;
+ }
+
+ set x(value: number) {
+ this._x = value;
+ }
+
+ get y(): number {
+ return this._y;
+ }
+
+ set y(value: number) {
+ this._y = value;
+ }
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/instance.ts b/absurd-survivors/src/instance.ts
new file mode 100644
index 0000000..5c58000
--- /dev/null
+++ b/absurd-survivors/src/instance.ts
@@ -0,0 +1,7 @@
+import type {Healthy} from "./interfaces.ts";
+
+export class InstanceOfUtils {
+ static instanceOfHealthy(object: any): object is Healthy {
+ return 'takeDamage' in object;
+ }
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/interfaces.ts b/absurd-survivors/src/interfaces.ts
new file mode 100644
index 0000000..432f560
--- /dev/null
+++ b/absurd-survivors/src/interfaces.ts
@@ -0,0 +1,31 @@
+import {Vector} from "./base.ts";
+
+export interface Acting {
+ act()
+}
+
+export interface Healthy {
+ takeDamage(damage: number);
+}
+
+export interface Moving {
+ move()
+ getPosition(): Vector;
+}
+
+export interface Projectile extends Drawable {
+
+}
+
+export interface Weapon extends Drawable{
+ act()
+}
+
+export interface Shooting {
+ createProjectile(): Projectile;
+ removeProjectile(projectile: Projectile)
+}
+
+export interface Drawable {
+ draw(ctx: CanvasRenderingContext2D);
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/main.ts b/absurd-survivors/src/main.ts
new file mode 100644
index 0000000..57bd976
--- /dev/null
+++ b/absurd-survivors/src/main.ts
@@ -0,0 +1,133 @@
+import './style.css'
+
+import {docReady} from "canvas-common";
+import {World} from "./World.ts";
+import {Player} from "./Player.ts";
+import {Vector} from "./base.ts";
+import {BasicEnemy, Enemy, ShootingEnemy} from "./Enemies.ts";
+import {HUD} from "./ui.ts";
+import {Pistol} from "./weapons.ts";
+
+
+let hud: HUD;
+let world: World;
+let config: Config;
+let state: WorldState;
+let ctx: CanvasRenderingContext2D;
+let canvas;
+
+export class Config {
+ private _size: Vector = new Vector(window.innerWidth, window.innerHeight)
+ private _fps: number = 60;
+
+
+ get size(): Vector {
+ return this._size;
+ }
+
+ get fps(): number {
+ return this._fps;
+ }
+}
+
+export class WorldState {
+ private _ended: boolean = false;
+
+
+ get ended(): boolean {
+ return this._ended;
+ }
+}
+
+
+function updateCanvas() {
+ ctx.clearRect(0, 0, config.size.x, config.size.y);
+ hud.draw(ctx)
+ if(!state.ended) {
+ world.enemiesAct()
+ world.player.act()
+ world.draw()
+ for(let key in keys) {
+ if(keys[key].state) {
+ keys[key].fun()
+ }
+ }
+ } else {
+ ctx.fillText('End', 15, 15)
+ }
+
+ setTimeout(function () {
+ requestAnimationFrame(updateCanvas);
+ }, 1000 / config.fps)
+
+}
+
+function makeKey(char, fun) {
+ keys[char] = {
+ state: false,
+ fun: fun
+ }
+}
+
+let keys = {};
+makeKey('w', function () {
+ world.player.position.y += -world.player.stats.speed
+})
+makeKey('s', function () {
+ world.player.position.y += world.player.stats.speed
+})
+makeKey('a', function () {
+ world.player.position.x += -world.player.stats.speed
+})
+makeKey('d', function () {
+ world.player.position.x += world.player.stats.speed
+})
+
+
+function keyUp(event) {
+ if(event.key in keys) {
+ keys[event.key].state = false;
+ }
+}
+
+function keyDown(event) {
+ if(event.key in keys) {
+ keys[event.key].state = true;
+ }
+}
+
+document.onkeyup = keyUp;
+document.onkeydown = keyDown;
+docReady(function () {
+ canvas = document.getElementById('canvas');
+
+ config = new Config();
+ canvas.width = config.size.x;
+
+ canvas.height = config.size.y;
+
+
+ ctx = canvas.getContext("2d");
+
+ let player = Player.generatePlayer();
+
+ world = new World(player, ctx);
+ state = new WorldState();
+
+ world.addEnemy(BasicEnemy.generateBasicEnemy(world))
+ world.addEnemy(ShootingEnemy.generateShootingEnemy(world, new Vector(350, 350)))
+ setInterval(() => {
+ world.addEnemy(ShootingEnemy.generateShootingEnemy(world, new Vector(Math.random() * config.size.x, Math.random() * config.size.y)))
+ }, 1000)
+ player.addWeapon(Pistol.spawnPistol(world))
+ let secondPistol = Pistol.spawnPistol(world, new Vector(-5, -5));
+ player.addWeapon(secondPistol)
+ hud = new HUD(world);
+
+
+ requestAnimationFrame(updateCanvas);
+
+
+
+})
+
diff --git a/absurd-survivors/src/projectile.ts b/absurd-survivors/src/projectile.ts
new file mode 100644
index 0000000..30914bb
--- /dev/null
+++ b/absurd-survivors/src/projectile.ts
@@ -0,0 +1,100 @@
+import type {Acting, Moving, Healthy} from "./interfaces.ts";
+import type {Vector} from "./base.ts";
+import {World} from "./World.ts";
+import {Vector} from "./base.ts";
+import {drawDot, moveInDirectionOf, straightMove} from "./utils.ts";
+import {InstanceOfUtils} from "./instance.ts";
+
+export abstract class Projectile implements Acting, Moving {
+
+ protected position: Vector;
+ protected speedVec: Vector;
+ protected impact: number;
+ protected world: World;
+ protected size: number;
+ protected parent: any;
+ protected color: string
+
+
+ constructor(position: Vector, speedVec: Vector, world: World, parent: any) {
+ this.position = position;
+ this.speedVec = speedVec;
+ this.world = world;
+ this.parent = parent;
+ }
+
+ act() {
+ this.move()
+ if(this.parent != this.world.player) {
+ if(this.position.distanceTo(this.world.player.position) < (this.size + this.world.player.stats.size)) {
+ this.impactPlayer()
+ }
+ }
+ if(this.parent == this.world.player) {
+ let closestTargetTo = this.world.getClosestTargetTo(this.position);
+ if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined && closestTargetTo[1]?.getPosition().distanceTo(this.position) < (this.size + this.world.player.stats.size)) {
+ let target: Moving = closestTargetTo[1]!;
+ if(InstanceOfUtils.instanceOfHealthy(target)) {
+ let healthy = target as Healthy;
+ healthy.takeDamage(this.impact)
+ }
+ }
+ }
+
+ }
+
+ impactPlayer() {
+ this.world.player.takeDamage(this.impact)
+ this.world.removeProjectile(this)
+ };
+
+ draw(ctx: CanvasRenderingContext2D) {
+ drawDot(this.position, this.size, this.color, ctx)
+ }
+
+ move() {
+ }
+
+ getPosition(): Vector {
+ return this.position;
+ }
+
+}
+
+export class StraightProjectile extends Projectile {
+
+
+ constructor(position: Vector, dirVector: Vector, world: World, parent: any) {
+ super(position, dirVector, world, parent);
+ }
+
+ move() {
+ this.position = straightMove(this.position, this.speedVec)
+ }
+
+ static createStraightProjectile(world: World, start: Vector, targetPosition: Vector, parent: any) {
+ let projectile = new StraightProjectile(start, Vector.createVector(targetPosition, start).normalize().multiply(5), world, parent)
+ projectile.impact = 1;
+ projectile.size = 1
+ projectile.color = 'red';
+ world.addProjectile(projectile)
+ return projectile;
+ }
+}
+
+export class HomingProjectile extends Projectile {
+
+ move() {
+ this.position = moveInDirectionOf(this.position, this.world.player.position, this.speedVec.vecLength())
+ }
+
+ static createHomingProjectile(world: World, start: Vector, parent: any) {
+ let projectile = new HomingProjectile(start, new Vector(5, 1), world, parent)
+ projectile.impact = 1;
+ projectile.size = 1
+ projectile.color = 'red';
+ world.addProjectile(projectile)
+ return projectile;
+ }
+
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/style.css b/absurd-survivors/src/style.css
new file mode 100644
index 0000000..cfb0669
--- /dev/null
+++ b/absurd-survivors/src/style.css
@@ -0,0 +1,4 @@
+html, body, div, canvas {
+ margin: 0;
+ padding: 0;
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/ui.ts b/absurd-survivors/src/ui.ts
new file mode 100644
index 0000000..510125f
--- /dev/null
+++ b/absurd-survivors/src/ui.ts
@@ -0,0 +1,72 @@
+import type {World} from "./World.ts";
+import type {Drawable} from "./interfaces.ts";
+import {World} from "./World.ts";
+import type {Vector} from "./base.ts";
+import {Vector} from "./base.ts";
+
+export class HUD implements Drawable{
+ private health: HealthInfo;
+ private world: World;
+
+
+ constructor(world: World) {
+ this.world = world;
+ this.health = new HealthInfo(world);
+ }
+
+ draw(ctx: CanvasRenderingContext2D) {
+ this.health.draw(ctx)
+ }
+
+
+}
+
+export class HealthInfo implements Drawable{
+ private bar: InfoBar;
+ private world: World;
+
+ constructor(world: World) {
+ this.world = world;
+ this.bar = new InfoBar(new Vector(0, 50), 50, 150, () => 'Health', () => this.world.player.health, () => this.world.player.stats.health)
+ }
+
+ draw(ctx: CanvasRenderingContext2D) {
+ this.bar.draw(ctx)
+ }
+}
+
+export class InfoBar implements Drawable {
+ private position: Vector;
+ private height: number;
+ private width: number;
+ private fillColor: string = 'green';
+ private borderColor: string = 'black';
+ private textLambda: () => string;
+ private valueLambda: () => number;
+ private totalValueLambda: () => number;
+
+
+ constructor(position: Vector, height: number, width: number, textLambda: () => string, valueLambda: () => number, totalValueLambda: () => number) {
+ this.position = position;
+ this.height = height;
+ this.width = width;
+ this.textLambda = textLambda;
+ this.valueLambda = valueLambda;
+ this.totalValueLambda = totalValueLambda;
+ }
+
+ draw(ctx: CanvasRenderingContext2D) {
+ ctx.beginPath();
+ ctx.strokeStyle = this.borderColor
+ ctx.strokeRect(this.position.x, this.position.y, this.width, this.height)
+ ctx.fillStyle = this.fillColor;
+ let value = this.valueLambda();
+ let totalValue = this.totalValueLambda();
+ let usedWidth = (value / totalValue) * this.width;
+ ctx.fillRect(this.position.x, this.position.y, usedWidth, this.height)
+ ctx.fillStyle = this.borderColor
+ ctx.fillText(`${value}/${totalValue}`, this.position.x + this.width / 2, this.position.y + this.height / 2)
+ ctx.fill()
+ }
+
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/utils.ts b/absurd-survivors/src/utils.ts
new file mode 100644
index 0000000..aae3eb8
--- /dev/null
+++ b/absurd-survivors/src/utils.ts
@@ -0,0 +1,18 @@
+import {Vector} from "./base.ts";
+
+export function drawDot(position: Vector, size: number, color: string, ctx: CanvasRenderingContext2D) {
+ ctx.beginPath();
+ ctx.fillStyle = color;
+ ctx.arc(position.x, position.y, size, 0, 2 * Math.PI);
+ ctx.fill();
+}
+
+export function moveInDirectionOf(position: Vector, target: Vector, speedFactor): Vector {
+ let playerVector = Vector.createVector(target, position);
+ let direction = playerVector.normalize()
+ return position.add(direction.multiply(speedFactor))
+}
+
+export function straightMove(position: Vector, speed: Vector): Vector {
+ return position.add(speed)
+}
\ No newline at end of file
diff --git a/absurd-survivors/src/vite-env.d.ts b/absurd-survivors/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/absurd-survivors/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/absurd-survivors/src/weapons.ts b/absurd-survivors/src/weapons.ts
new file mode 100644
index 0000000..d814751
--- /dev/null
+++ b/absurd-survivors/src/weapons.ts
@@ -0,0 +1,59 @@
+import type {Weapon} from "./interfaces.ts";
+import {drawDot} from "./utils.ts";
+import {Player} from "./Player.ts";
+import {Projectile, StraightProjectile} from "./projectile.ts";
+import {World} from "./World.ts";
+import {Vector} from "./base.ts";
+
+export class Pistol implements Weapon {
+
+ private player: Player
+ private shootInterval: number;
+ private shootCooldown: number = 0;
+ private world: World;
+ private offset: Vector;
+ private projectiles: [Projectile] = []
+ private color: string;
+ private size: number;
+
+ constructor(world: World) {
+ this.player = world.player;
+ this.world = world;
+ }
+
+ draw(ctx: CanvasRenderingContext2D) {
+ drawDot(this.player.position.add(this.offset), this.size, this.color, ctx)
+ }
+
+ act() {
+ if(this.shootCooldown <= 0) {
+ if(this.createProjectile()) {
+ this.shootCooldown = this.shootInterval;
+ }
+ }
+ this.shootCooldown -= 1;
+ }
+
+ private createProjectile(): boolean {
+ let closestTargetTo = this.world.getClosestTargetTo(this.world.player.position);
+ if(closestTargetTo !== undefined && closestTargetTo[1] !== undefined) {
+ let projectile = StraightProjectile.createStraightProjectile(this.world, this.player.position.add(this.offset), closestTargetTo[1]!.getPosition(), this.player)
+ this.projectiles.push(projectile)
+ return true
+ } else {
+ return false;
+ }
+ }
+
+ static spawnPistol(world: World, offset?: Vector) {
+ if(!offset) {
+ offset = new Vector(5, 5)
+ }
+ let pistol = new Pistol(world)
+ pistol.offset = offset;
+ pistol.size = 1;
+ pistol.color = 'yellow';
+ pistol.shootInterval = 10;
+ return pistol;
+ }
+}
\ No newline at end of file
diff --git a/absurd-survivors/tsconfig.json b/absurd-survivors/tsconfig.json
new file mode 100644
index 0000000..4f5edc2
--- /dev/null
+++ b/absurd-survivors/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/absurd-survivors/vite.config.js b/absurd-survivors/vite.config.js
new file mode 100644
index 0000000..991563c
--- /dev/null
+++ b/absurd-survivors/vite.config.js
@@ -0,0 +1,7 @@
+import { defineConfig } from 'vite'
+
+export default defineConfig({
+ build: {
+ outDir: '../../dist/survivors'
+ },
+})
\ No newline at end of file
diff --git a/img/survivors.png b/img/survivors.png
new file mode 100644
index 0000000000000000000000000000000000000000..80d50f10042479f1a399d126324cfd6cffa1fd7a
GIT binary patch
literal 40381
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 E|N6d?0;K_a
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