fix pathfinding issues
This commit is contained in:
+74
-67
@@ -3,6 +3,7 @@ import SAT from 'sat'
|
||||
import SATX from './satx.js'
|
||||
import Pathfind from './pathfind.js'
|
||||
import Ability from './ability.js'
|
||||
import Team from './team.js'
|
||||
|
||||
export default class Entity {
|
||||
id = crypto.randomUUID()
|
||||
@@ -11,21 +12,23 @@ export default class Entity {
|
||||
health = 1
|
||||
maxHealth = 1
|
||||
abilities = [
|
||||
Ability.basicAttack,
|
||||
Ability.rangedAttack,
|
||||
Ability.straightShot,
|
||||
Ability.shieldThrow,
|
||||
Ability.blink,
|
||||
]
|
||||
casting = null
|
||||
// TODO: teams
|
||||
team = Team.neutral
|
||||
|
||||
cooldowns = {}
|
||||
|
||||
#attack = false
|
||||
#position = new Vector2()
|
||||
#dest = null
|
||||
#game = null
|
||||
#logic = null
|
||||
#path = []
|
||||
#position = new Vector2()
|
||||
#scheduledPathfinding = null
|
||||
|
||||
static collider(x, y, radius) {
|
||||
return new SAT.Circle(new SAT.Vector(x, y), radius)
|
||||
@@ -35,11 +38,19 @@ export default class Entity {
|
||||
Object.entries(options).forEach(([key, value]) => this[key] = value)
|
||||
}
|
||||
|
||||
get destination() { return this.#dest }
|
||||
get logic() { return this.#logic }
|
||||
get game() { return this.#game }
|
||||
get position() { return this.#position }
|
||||
get scheduledPathfinding() { return this.#scheduledPathfinding }
|
||||
get x() { return this.position.x }
|
||||
get y() { return this.position.y }
|
||||
|
||||
set destination(value) { this.#dest = value }
|
||||
set logic(value) { this.#logic = value }
|
||||
set game(value) { this.#game = value }
|
||||
set position(value) { this.#position = value }
|
||||
set scheduledPathfinding(value) { this.#scheduledPathfinding = value }
|
||||
set x(value) { this.position.x = value }
|
||||
set y(value) { this.position.y = value }
|
||||
|
||||
@@ -123,20 +134,6 @@ export default class Entity {
|
||||
this.#attack = false
|
||||
}
|
||||
|
||||
autoAttack() {
|
||||
if (!this.#attack) { return false }
|
||||
|
||||
if (this.game?.entities.some((e) => e.id != this.id && e.position.clone().sub(this.position).length() < this.abilities[0].range)) {
|
||||
const cooldown = this.game?.secToTick(this.abilities[0].cooldown) ?? 0
|
||||
const lastCast = this.cooldowns[this.abilities[0].id]
|
||||
const timestamp = this.game?.currentTick ?? 0
|
||||
if (lastCast != null && lastCast + cooldown > timestamp) { return false }
|
||||
|
||||
const target = this.#dest ?? this.position
|
||||
this.castAction(0, target.x, target.y, false)
|
||||
}
|
||||
}
|
||||
|
||||
cast() {
|
||||
if (this.casting == null) {
|
||||
return false
|
||||
@@ -209,50 +206,71 @@ export default class Entity {
|
||||
this.fixPosition()
|
||||
}
|
||||
|
||||
// TODO: unset destination on teleports, etc.
|
||||
// TODO: recalculate path on obstructions (currently next waypoint is used)
|
||||
takeStep(distanceTraveled = 0) {
|
||||
if (this.casting != null) { return false }
|
||||
|
||||
const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled
|
||||
const collidables = this.collidables()
|
||||
if (this.#dest != null) {
|
||||
const fixedDest = SATX.clamp(SATX.fixCollisions(this.#dest, collidables, this.radius), this.game?.width, this.game?.height, this.radius)
|
||||
if (this.#attack && this.game?.entities.some((e) => e.team != this.team && e.position.clone().sub(this.position).length() < this.abilities[0].range)) {
|
||||
const cooldown = this.game?.secToTick(this.abilities[0].cooldown) ?? 0
|
||||
const lastCast = this.cooldowns[this.abilities[0].id]
|
||||
const timestamp = this.game?.currentTick ?? 0
|
||||
if (lastCast != null && lastCast + cooldown > timestamp) { return false }
|
||||
|
||||
if (this.#path.length < 1 || !this.#path.at(-1).equals(fixedDest)) {
|
||||
// console.time('pathfinding')
|
||||
const start = SATX.vectorToFloat32Array(this.position)
|
||||
const goal = SATX.vectorToFloat32Array(fixedDest)
|
||||
const nonUniqueWaypoints = this.waypoints().map((w) => SATX.vectorToFloat32Array(w)).concat([start, goal])
|
||||
const waypoints = Pathfind.uniqueWaypoints(nonUniqueWaypoints)
|
||||
const graph = Pathfind.buildGraph(waypoints, collidables, this.radius)
|
||||
this.#path = Pathfind.shortestPath(graph, start, goal).map((waypoint) => new Vector2(waypoint[0], waypoint[1]))
|
||||
// console.timeEnd('pathfinding')
|
||||
const target = this.#dest ?? this.position
|
||||
this.castAction(0, target.x, target.y, false)
|
||||
return true
|
||||
}
|
||||
|
||||
if (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)
|
||||
const destinationInLineOfSight = !SATX.collideObjects(tunnel, collidables)
|
||||
|
||||
if (this.#path.length > 0) {
|
||||
if (!destinationInLineOfSight) {
|
||||
this.#path = []
|
||||
}
|
||||
}
|
||||
|
||||
if (this.#path.length < 1 || !this.#path.at(-1).equals(fixedDest)) {
|
||||
if (destinationInLineOfSight) {
|
||||
this.#path = [fixedDest]
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.#path.length < 1 || !this.#path.at(-1).equals(fixedDest)) && (!this.#scheduledPathfinding || this.game?.currentTick % this.game?.tickRate == this.#scheduledPathfinding)) {
|
||||
const start = SATX.vectorToFloat32Array(this.position)
|
||||
const goal = SATX.vectorToFloat32Array(fixedDest)
|
||||
const nonUniqueWaypoints = this.waypoints().map((w) => SATX.vectorToFloat32Array(w)).concat([start, goal])
|
||||
const waypoints = Pathfind.uniqueWaypoints(nonUniqueWaypoints)
|
||||
const graph = Pathfind.buildGraph(waypoints, collidables, this.radius)
|
||||
this.#path = Pathfind.shortestPath(graph, start, goal).map((waypoint) => new Vector2(waypoint[0], waypoint[1]))
|
||||
}
|
||||
|
||||
if (this.#path.length > 0) {
|
||||
const speed = (this.speed / (this.game?.tickBudget ?? 1000)) - distanceTraveled
|
||||
const destination = this.#path.at(0)
|
||||
const difference = destination.clone().sub(this.position)
|
||||
const distance = difference.length()
|
||||
const direction = difference.clone().normalize()
|
||||
const stepTaken = this.position.clone().add(direction.multiplyScalar(speed))
|
||||
const position = distance <= speed ? destination : stepTaken
|
||||
|
||||
const collider = Entity.collider(position.x, position.y, this.radius)
|
||||
const isColliding = SATX.collideObjects(collider, this.collidables())
|
||||
|
||||
if (!isColliding) {
|
||||
this.position.copy(position)
|
||||
}
|
||||
|
||||
if (this.#path.length > 0) {
|
||||
const destination = this.#path.at(0)
|
||||
const difference = destination.clone().sub(this.position)
|
||||
const distance = difference.length()
|
||||
const direction = difference.clone().normalize()
|
||||
const stepTaken = this.position.clone().add(direction.multiplyScalar(speed))
|
||||
const position = distance <= speed ? destination : stepTaken
|
||||
|
||||
const collider = Entity.collider(position.x, position.y, this.radius)
|
||||
const isColliding = SATX.collideObjects(collider, this.collidables())
|
||||
|
||||
if (!isColliding) {
|
||||
this.position.copy(position)
|
||||
if (this.position.equals(destination)) {
|
||||
this.#path = this.#path.slice(1)
|
||||
if (this.#path.length > 0) {
|
||||
this.takeStep(distance)
|
||||
}
|
||||
|
||||
if (this.position.equals(destination)) {
|
||||
this.#path = this.#path.slice(1)
|
||||
if (this.#path.length > 0) {
|
||||
this.takeStep(distance)
|
||||
}
|
||||
else {
|
||||
this.#dest = null
|
||||
}
|
||||
else {
|
||||
this.#dest = null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,19 +280,8 @@ export default class Entity {
|
||||
this.cast()
|
||||
this.takeStep()
|
||||
this.fixPosition()
|
||||
this.autoAttack()
|
||||
|
||||
// TODO: proper death and respawn
|
||||
if (this.health <= 0) {
|
||||
if (this.id == '1' || this.id == '2') {
|
||||
this.health = this.maxHealth
|
||||
if (this.id == '1') {
|
||||
this.teleport(new Vector2(200, 200))
|
||||
}
|
||||
if (this.id == '2') {
|
||||
this.teleport(new Vector2(1800, 1800))
|
||||
}
|
||||
}
|
||||
if (this.#logic != null) {
|
||||
this.#logic()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user