diff --git a/package-lock.json b/package-lock.json index 85d9b35..6a4eac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,6 @@ "license": "UNLICENSED", "dependencies": { "@tweenjs/tween.js": "^25.0.0", - "quickhull": "^1.0.3", "sat": "^0.9.0", "three": "^0.171.0", "websocket-express": "^3.1.2" @@ -753,12 +752,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/quickhull": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/quickhull/-/quickhull-1.0.3.tgz", - "integrity": "sha512-AQbLaXdzGDJdO9Mu3qY/NY5JWlDqIutCLW8vJbsQTq+/bydIZeltnMVRKCElp81Y5/uRm4Yw/RsMdcltFYsS6w==", - "license": "MIT" - }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", diff --git a/package.json b/package.json index 289fdb5..511b9f6 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "description": "", "dependencies": { "@tweenjs/tween.js": "^25.0.0", - "quickhull": "^1.0.3", "sat": "^0.9.0", "three": "^0.171.0", "websocket-express": "^3.1.2" diff --git a/public/client.js b/public/client.js index 13435bc..98432e7 100644 --- a/public/client.js +++ b/public/client.js @@ -184,8 +184,6 @@ function connectWebSocket() { entity.position.set(e.position.x / 100, e.position.y / 100, e.height / 100) scene.add(entity) - // TODO: player model out of basic geometries - const hpMargin = 0.4 const maxHp = new THREE.Sprite(new THREE.SpriteMaterial({ color: 0xd03333 })) maxHp.position.set(0, (e.height / 100) + hpMargin, 0) @@ -386,13 +384,6 @@ window.addEventListener('load', () => { if (event.code == 'KeyE') { websocket.send(JSON.stringify({ action: 'cast', slot: 3, id: playerId, x, y })) } - - if (event.code == 'KeyD') { - websocket.send(JSON.stringify({ action: 'teleport', id: playerId, x, y })) - } - if (event.code == 'KeyF') { - websocket.send(JSON.stringify({ action: 'teleport', id: playerId, x, y })) - } } }) diff --git a/src/ability.js b/src/ability.js index 55c561f..34b9ff6 100644 --- a/src/ability.js +++ b/src/ability.js @@ -68,7 +68,7 @@ export default class Ability { let closest = null let distance = Infinity caster.game?.entities.filter((e) => e.team != caster.team && e.position.clone().sub(caster.position).length() < ability.range).forEach((e) => { - const newDistance = e.position.clone().sub(cursor).length() < distance + const newDistance = e.position.clone().sub(cursor).length() if (newDistance < distance) { closest = e distance = newDistance diff --git a/src/entity.js b/src/entity.js index c1bd0ad..9b1af83 100644 --- a/src/entity.js +++ b/src/entity.js @@ -27,6 +27,7 @@ export default class Entity { #dest = null #game = null #logic = null + #move = false #path = [] #position = new Vector2() #scheduledPathfinding = null @@ -87,7 +88,7 @@ export default class Entity { this.moveAction(x, y, true) } - castAction(slot, x, y, clearDestination = true) { + castAction(slot, x, y, halt = true) { const ability = this.abilities[slot] if (this.casting != null) { @@ -99,8 +100,8 @@ export default class Entity { return false } - if (clearDestination) { - this.#dest = null + if (halt) { + this.#move = false } const cursor = new Vector2(x, y) @@ -117,21 +118,22 @@ export default class Entity { } haltAction() { - this.#dest = null + this.#move = false } moveAction(x, y, attack = false) { - this.#attack = attack if (this.casting != null && (!this.#attack || this.casting.ability.id != this.abilities[0].id)) { this.casting = null } + this.#attack = attack + this.#move = true this.#dest = SATX.fixCollisions(new Vector2(x, y), this.collidables(), this.radius, this.game?.width, this.game?.height) } stopAction() { this.casting = null - this.#dest = null + this.#move = true this.#attack = false } @@ -160,14 +162,6 @@ export default class Entity { return entityColliders.concat(terrainColliders) } - // DEPRECATED: hulls were a failed concept for position fixing - collidableHulls() { - const entityColliders = (this.game?.entities ?? []).filter((e) => e.id != this.id).map((e) => e.collider) - const terrainColliders = (this.game?.terrains ?? []).map((t) => t.hull) - - return entityColliders.concat(terrainColliders) - } - cooldown(id) { this.cooldowns[id] = this.game?.currentTick ?? 0 } @@ -207,7 +201,7 @@ export default class Entity { this.fixPosition() } - takeStep(distanceTraveled = 0) { + move(distanceTraveled = 0) { if (this.casting != null) { return false } if (this.#attack && this.game?.entities.some((e) => e.team != this.team && e.position.clone().sub(this.position).length() < this.abilities[0].range)) { @@ -221,8 +215,8 @@ export default class Entity { return true } - if (this.#dest == null) { return false } - + if (!this.#move || this.#dest == null) { return false } + const collidables = this.collidables() const fixedDest = SATX.clamp(SATX.fixCollisions(this.#dest, collidables, this.radius), this.game?.width, this.game?.height, this.radius) const tunnel = SATX.entityTunnel(this.#position.x, this.#position.y, fixedDest.x, fixedDest.y, this.radius) @@ -268,10 +262,11 @@ export default class Entity { if (this.position.equals(destination)) { this.#path = this.#path.slice(1) if (this.#path.length > 0) { - this.takeStep(distance) + this.move(distance) } else { this.#dest = null + this.#move = false } } } @@ -279,7 +274,7 @@ export default class Entity { update() { this.cast() - this.takeStep() + this.move() this.fixPosition() if (this.#logic != null) { this.#logic() diff --git a/src/index.js b/src/index.js index 8c53ab6..6a7134e 100644 --- a/src/index.js +++ b/src/index.js @@ -35,11 +35,6 @@ app.ws('/ws', async (req, res) => { } console.log(message) - // DEPRECATED: teleporting is now directly possible via Ability... - if (message.action == 'teleport') { - entity.teleport(new Vector2(message.x, message.y)) - } - if (message.action == 'attack') { entity.attackAction(message.x, message.y) } @@ -62,79 +57,6 @@ app.ws('/ws', async (req, res) => { }) }) -// function testScenario() { -// const entity1 = new Entity() -// entity1.id = '1' -// entity1.teleport(new Vector2(200, 500)) -// entity1.radius = 50 -// entity1.maxHealth = 100 -// entity1.health = 80 -// game.spawnEntity(entity1) - -// const entity2 = new Entity() -// entity2.id = '2' -// entity2.teleport(new Vector2(110, 110)) -// entity2.radius = 50 -// entity2.maxHealth = 50 -// entity2.health = 50 -// game.spawnEntity(entity2) - -// const horseshoe = new Terrain([ -// { x: 400, y: 200 }, -// { x: 600, y: 200 }, -// { x: 700, y: 300 }, -// { x: 650, y: 600 }, -// { x: 400, y: 600 }, -// { x: 400, y: 450 }, -// { x: 600, y: 500 }, -// { x: 600, y: 300 }, -// { x: 400, y: 300 }, -// ]) -// horseshoe.id = 'horseshoe' -// game.addTerrain(horseshoe) - -// const stopsign = new Terrain([ -// { x: 800, y: 800 }, -// { x: 900, y: 900 }, -// { x: 900, y: 1000 }, -// { x: 800, y: 1100 }, -// { x: 800, y: 1100 }, -// { x: 700, y: 1100 }, -// { x: 600, y: 1000 }, -// { x: 600, y: 900 }, -// { x: 700, y: 800 }, -// ]) -// stopsign.id = 'stopsign' -// game.addTerrain(stopsign) - -// const box = new Terrain([ -// { x: 1200, y: 700 }, -// { x: 1200, y: 800 }, -// { x: 1300, y: 800 }, -// { x: 1300, y: 700 }, -// ]) -// box.id = 'box' -// game.addTerrain(box) - -// const diamond = new Terrain([ -// { x: 1000, y: 300 }, -// { x: 1100, y: 400 }, -// { x: 1000, y: 500 }, -// { x: 900, y: 400 }, -// ]) -// diamond.id = 'diamond' -// game.addTerrain(diamond) - -// const pole = new Terrain([ -// { x: 400, y: 1000 }, -// { x: 410, y: 1000 }, -// { x: 410, y: 1010 }, -// { x: 400, y: 1010 }, -// ]) -// pole.id = 'pole' -// game.addTerrain(pole) -// } - function laneScenario() { const entity1 = new Entity({ id: '1', @@ -197,6 +119,9 @@ function laneScenario() { if (entity.id == '2') { entity.teleport(new Vector2(1600, 1800)) } + if (entity.id == '3') { + entity.teleport(new Vector2(1800, 1600)) + } } } } diff --git a/src/pathfind.js b/src/pathfind.js index 3a8a348..6312be1 100644 --- a/src/pathfind.js +++ b/src/pathfind.js @@ -4,19 +4,26 @@ import PriorityQueue from './priority-queue.js' import SATX from './satx.js' export default class Pathfind { - static precision = 0.001 - static multiplier = 1000 // (1 / this.precision) + static precision = 0.01 + static multiplier = 1000000 // (1 / this.precision) * 10^expected_digit_count / 10 static key2(a, b) { return `${a},${b}` } - // TODO: Value exceeds safe integer limit: collisions cause waypointing anomalies + // Fowler-Noll-Vo hash prime and offset basis for small keyspaces static floatKey4(a, b, c, d) { - return Math.floor(a * Pathfind.multiplier) + - Math.floor(b * Pathfind.multiplier) * Pathfind.multiplier + - Math.floor(c * Pathfind.multiplier) * Pathfind.multiplier ** 2 + - Math.floor(d * Pathfind.multiplier) * Pathfind.multiplier ** 3 + const prime = 16777619 + let result = 2166136261 + result ^= Math.floor(a * Pathfind.multiplier) + result *= prime + result ^= Math.floor(b * Pathfind.multiplier) + result *= prime + result ^= Math.floor(c * Pathfind.multiplier) + result *= prime + result ^= Math.floor(d * Pathfind.multiplier) + result *= prime + return result } static uniqueWaypoints(waypoints) { diff --git a/src/terrain.js b/src/terrain.js index d6a8a53..e119aa4 100644 --- a/src/terrain.js +++ b/src/terrain.js @@ -1,4 +1,3 @@ -import QuickHull from 'quickhull' // DEPRECATED: hulls were a failed concept for position fixing import SAT from 'sat' import { Shape, ShapeUtils, Vector2 } from 'three' @@ -26,7 +25,6 @@ export default class Terrain { } get colliders() { return this.#colliders } - get hull() { return this.#hull } // DEPRECATED: hulls were a failed concept for position fixing get unadjustedWaypoints() { return this.#unadjustedWaypoints } get vertices() { return this.#vertices } @@ -76,16 +74,6 @@ export default class Terrain { } this.#colliders = ShapeUtils.triangulateShape(points.shape, points.holes).map(indicesToPolygon) - this.#calculateHull() - } - - // DEPRECATED: hulls were a failed concept for position fixing - #calculateHull() { - const vertices = QuickHull(this.#vertices.map((v) => ({ x: v.x, y: v.y }))).map((v) => new Vector2(v.x, v.y)) - const first = vertices.at(0) - const satPoints = [new SAT.Vector(...first.toArray()), ...vertices.slice(1).map((v) => new SAT.Vector(...v.clone(first).sub(first).toArray()))] - - this.#hull = new SAT.Polygon(satPoints[0], [new SAT.Vector(), ...satPoints.slice(1)]) } #calculatePosition() {