fix projectiles phasing through stuff

This commit is contained in:
2025-01-17 14:01:18 +09:00
parent 20f8a2f1fe
commit 787b48a4df
4 changed files with 41 additions and 25 deletions
+8 -6
View File
@@ -24,21 +24,23 @@ export default class Ability {
Object.entries(options).forEach(([key, value]) => this[key] = value) Object.entries(options).forEach(([key, value]) => this[key] = value)
} }
// TODO: skill seemingly going right through minions without a registered hit
static straightShot = new Ability({ static straightShot = new Ability({
id: 'straight_shot', id: 'straight_shot',
name: 'Straight Shot', name: 'Straight Shot',
castTime: 0.1, castTime: 0.25,
cooldown: 1, cooldown: 1,
damage: 10, damage: 10,
radius: 7, radius: 60,
range: 800, range: 1200,
speed: 3000, speed: 2000,
effect: function straightShotEffect(caster, cursor) { effect: function straightShotEffect(caster, cursor) {
const ability = this const ability = this
const straightShotCollision = function straightShotCollision(projectile, collidingEntity) { const straightShotCollision = function straightShotCollision(projectile, collidingEntity) {
if (!projectile.memory.hit) {
collidingEntity.damage(ability.damage) collidingEntity.damage(ability.damage)
projectile.despawn() projectile.memory.hit = true
projectile.destination = collidingEntity.position.clone()
}
} }
const projectile = new Projectile({ const projectile = new Projectile({
+3 -2
View File
@@ -88,7 +88,7 @@ export default class Entity {
this.moveAction(cursor, true) this.moveAction(cursor, true)
} }
castAction(slot, cursor, halt = true) { castAction(slot, cursor, halt = false) {
const ability = this.abilities[slot] const ability = this.abilities[slot]
if (this.casting != null) { if (this.casting != null) {
@@ -120,6 +120,7 @@ export default class Entity {
this.#moving = false this.#moving = false
} }
// TODO: cancelable and uncancelable abilities
moveAction(cursor, attack = false) { moveAction(cursor, attack = false) {
if (this.casting != null && (!this.#attacking || this.casting.ability.id != this.abilities[0].id)) { if (this.casting != null && (!this.#attacking || this.casting.ability.id != this.abilities[0].id)) {
this.casting = null this.casting = null
@@ -146,7 +147,7 @@ export default class Entity {
} }
collider() { collider() {
return new SAT.Circle(new SAT.Vector(this.x, this.y), this.radius) return new SAT.Circle(new SAT.Vector(this.position.x, this.position.y), this.radius)
} }
colliders() { colliders() {
+15 -9
View File
@@ -1,6 +1,5 @@
import { Vector2 } from 'three' import { Vector2 } from 'three'
import { WebSocketExpress } from 'websocket-express' import { WebSocketExpress } from 'websocket-express'
import Ability from './ability.js'
import Entity from './entity.js' import Entity from './entity.js'
import express from 'express' import express from 'express'
import Game from './game.js' import Game from './game.js'
@@ -69,13 +68,13 @@ function laneScenario() {
game.spawnEntity(player1) game.spawnEntity(player1)
player1.attackAction(new Vector2(500, 150)) player1.attackAction(new Vector2(500, 150))
const player2 = new Entity(Template.player({ // const player2 = new Entity(Template.player({
id: '2', // id: '2',
spawnPosition: new Vector2(1600, 1800), // spawnPosition: new Vector2(1600, 1800),
team: Team.red, // team: Team.red,
})) // }))
game.spawnEntity(player2) // game.spawnEntity(player2)
player2.attackAction(new Vector2(1600, 1800)) // player2.attackAction(new Vector2(1600, 1800))
const midWallStart = new Vector2(600, 600) const midWallStart = new Vector2(600, 600)
const midWallEnd = new Vector2(1400, 1400) const midWallEnd = new Vector2(1400, 1400)
@@ -117,7 +116,7 @@ function laneScenario() {
game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: true, route: redRoute }))) game.spawnEntity(new Entity(Template.minion(Team.red, { ranged: true, route: redRoute })))
} }
} }
game.logic = gameLogic // game.logic = gameLogic
// const uBottomPoints = [ // const uBottomPoints = [
// midSouthWallPoints.at(0).clone().sub(midWallThickness), // midSouthWallPoints.at(0).clone().sub(midWallThickness),
@@ -128,6 +127,13 @@ function laneScenario() {
// const uBottom = new Terrain(uBottomPoints) // const uBottom = new Terrain(uBottomPoints)
// uBottom.id = 'uBottom' // uBottom.id = 'uBottom'
// game.addTerrain(uBottom) // game.addTerrain(uBottom)
const minion = new Entity({...Template.minion(Team.red, { ranged: true }), logic: null})
minion.teleport(new Vector2(850, 250))
game.spawnEntity(minion)
player1.stopAction()
player1.castAction(1, new Vector2(850, 250))
} }
app.listen(port, () => { app.listen(port, () => {
+13 -6
View File
@@ -6,6 +6,7 @@ export default class Projectile {
id = crypto.randomUUID() id = crypto.randomUUID()
after = null after = null
height = 50 height = 50
memory = {} // TODO: WARNING: currently only used for hit detection (code smell?)
onCollide = null onCollide = null
owner = null owner = null
position = new Vector2() position = new Vector2()
@@ -29,18 +30,18 @@ export default class Projectile {
Object.entries(options).forEach(([key, value]) => this[key] = value) Object.entries(options).forEach(([key, value]) => this[key] = value)
} }
checkCollisions() { checkCollisions(collider) {
(this.game?.entities ?? []).filter((e) => e.id != this.id).forEach((e) => { (this.game?.entities ?? []).filter((e) => e.id != this.id).forEach((e) => {
if (e.id == this.owner?.id) { return } if (e.id == this.owner?.id) { return }
if (SATX.collideObject(this.collider(), e.collider())) { if (SATX.collideObject(collider, e.collider())) {
this.onCollide(this, e) if (this.onCollide != null) { this.onCollide(this, e) }
} }
}) })
} }
collider() { collider() {
return new SAT.Circle(new SAT.Vector(this.x, this.y), this.radius) return new SAT.Circle(new SAT.Vector(this.position.x, this.position.y), this.radius)
} }
despawn() { despawn() {
@@ -55,11 +56,12 @@ export default class Projectile {
update() { update() {
this.#move() this.#move()
if (this.onCollide != null) { this.checkCollisions() } if (this.onCollide != null) { this.checkCollisions(this.collider()) }
this.#checkIfArrived() this.#checkIfArrived()
} }
#checkIfArrived() { #checkIfArrived() {
if (this.destination == null) { return }
if (!this.position.equals(this.destination)) { return } if (!this.position.equals(this.destination)) { return }
if (this.after != null) { if (this.after != null) {
@@ -71,11 +73,16 @@ export default class Projectile {
#move() { #move() {
const speed = (this.speed / (this.game?.tickBudget ?? 1000)) const speed = (this.speed / (this.game?.tickBudget ?? 1000))
const prevPos = this.position.clone()
if (this.position.distanceTo(this.destination) < speed) { if (this.position.distanceTo(this.destination) < speed) {
this.position.copy(this.destination) this.position.copy(this.destination)
} }
else {
const step = this.destination.clone().sub(this.position).normalize().multiplyScalar(speed) const step = this.destination.clone().sub(this.position).normalize().multiplyScalar(speed)
this.position.add(step) this.position.add(step)
} }
const tunnel = SATX.entityTunnel(prevPos.x, prevPos.y, this.position.x, this.position.y, this.radius)
if (this.onCollide != null) { this.checkCollisions(tunnel) }
}
} }