fix projectiles colliding with dead entities

This commit is contained in:
2025-01-23 10:36:28 +09:00
parent afa419e77a
commit 4acd7a2881
4 changed files with 52 additions and 29 deletions
+37 -22
View File
@@ -217,47 +217,62 @@ export default class Ability {
name: 'Shield Throw',
castTime: 0.25,
cooldown: 5,
damage: 90,
deceleratePerTick: 90,
radius: 110,
range: 1025,
speed: 2400,
effect: function shieldThrowEffect(caster, cursor) {
const ability = this
const amount = ability.damage
let onTheWayBack = false
let collided = new Set()
const accelerateLogic = function accelerateLogic() {
const projectile = this
projectile.speed += ability.deceleratePerTick
}
const shieldThrowCollision = function shieldThrowCollision(projectile, collidingEntity) {
if (collidingEntity == null) { return }
if (collidingEntity.id == caster.id) { return }
if (caster.team == null || collidingEntity.team == null || collidingEntity.team != caster.team) { return }
const entityId = collidingEntity.id
const decelerateLogic = function decelerateLogic() {
const projectile = this
if (projectile.speed - ability.deceleratePerTick >= ability.deceleratePerTick) {
projectile.speed = projectile.speed - ability.deceleratePerTick
if (!collided.has(entityId)) {
collidingEntity.heal(amount, caster)
collided.add(entityId)
}
}
const shieldThrowReturn = function shieldThrowReturn(projectile, homingTarget) {
const returnProjectile = new Projectile({
homingTarget: caster,
logic: accelerateLogic,
owner: caster.id,
position: projectile.position.clone(),
radius: ability.radius,
speed: ability.deceleratePerTick,
visionRange: ability.radius,
})
const accelerateLogic = function accelerateLogic(projectile) {
if (onTheWayBack) {
projectile.speed += ability.deceleratePerTick
}
else {
if (projectile.speed - ability.deceleratePerTick >= ability.deceleratePerTick) {
projectile.speed = projectile.speed - ability.deceleratePerTick
}
}
}
caster.game?.spawnProjectile(returnProjectile)
const shieldThrowSecondAfter = function shieldThrowSecondAfter(projectile, homingTarget) {
caster.heal(amount, caster)
caster.heal(amount, caster) // NOTE: duplicated on purpose
}
const shieldThrowFirstAfter = function shieldThrowFirstAfter(projectile, homingTarget) {
projectile.destination = null
projectile.homingTarget = caster
onTheWayBack = true
collided.clear()
projectile.after = shieldThrowSecondAfter
}
const projectile = new Projectile({
after: shieldThrowReturn,
logic: decelerateLogic,
after: shieldThrowFirstAfter,
logic: accelerateLogic,
onCollide: shieldThrowCollision,
owner: caster.id,
position: caster.position.clone(),
radius: ability.radius,
speed: ability.speed,
visionRange: ability.radius,
visionRange: ability.radius * 1.5,
})
projectile.destination = caster.position.clone().add(cursor.clone().sub(caster.position).normalize().multiplyScalar(ability.range + caster.radius))
+8 -3
View File
@@ -177,6 +177,7 @@ export default class Entity {
this.moveAction(cursor, true)
}
// TODO: buffer skill inputs
castAction(slot, cursor, halt = false) {
if (this.dead) { return }
@@ -295,10 +296,13 @@ export default class Entity {
this.cooldowns[id] = this.game?.currentTick ?? 0
}
closestTargetTo(cursor, range) {
closestTargetTo(cursor, range, targetAllies = false) {
const entities = this.game?.entities
if (entities == null) { return }
const targetsInRange = entities.filter((it) => this.team != it.team && it.distanceTo(cursor) <= range + this.radius + it.radius)
const targetsInRange = targetAllies
? entities.filter((it) => this.team == it.team && it.distanceTo(cursor) <= range + this.radius + it.radius)
: entities.filter((it) => this.team != it.team && it.distanceTo(cursor) <= range + this.radius + it.radius)
if (targetsInRange.length < 1) { return }
const absoluteClosestTarget = targetsInRange.reduce((e1, e2) => (e1?.distanceTo(cursor) ?? Infinity) < e2.distanceTo(cursor) ? e1 : e2, null)
@@ -321,6 +325,7 @@ export default class Entity {
return entitiesAndTerrains.filter((it) => !it.dead && it.collision && !((this.ghosting && it.ghostable) || (this.ghostable && it.ghosting)) && SATX.bboxCheck(bbox, it.bbox))
}
// TODO: add shielding logic
damage(amount, source = null) {
if (this.dead) { return }
@@ -399,7 +404,7 @@ export default class Entity {
return this.buffs.some((it) => it.id == id) && this.game?.buffs.some((it) => it.id == id)
}
heal(amount) {
heal(amount, _source) {
if (this.dead) { return }
this.health = Math.min(Math.max(0, this.health + amount), this.maxHealth)
+2 -2
View File
@@ -22,8 +22,8 @@ export class Dungeon {
game.addTerrain(new Terrain([new Vector2(2000, 1500), new Vector2(2500, 1000), new Vector2(2500, 1500)], false))
game.spawnEntity(new Entity(Template.player({ id: '6', spawnPosition: new Vector2(2400, 1400), team: enemy, logic: castQ })))
game.spawnEntity(new Entity(Template.player({ id: '1', spawnPosition: new Vector2(1500, 700), team, dead: true })))
game.spawnEntity(new Entity(Template.player({ id: '2', spawnPosition: new Vector2(200, 1300), team, health: 10 })))
game.spawnEntity(new Entity(Template.player({ id: '1', spawnPosition: new Vector2(200, 1300), team, health: 10 })))
game.spawnEntity(new Entity(Template.player({ id: '2', spawnPosition: new Vector2(1500, 700), team, dead: true })))
game.spawnEntity(new Entity(Template.basilisk({ id: 'boss', spawnPosition: new Vector2(2200, 750), team: Team.neutral })))
+5 -2
View File
@@ -123,6 +123,9 @@ export default class Projectile {
this.#after(this, this.#homingTarget)
}
if (this.destination == null) { return }
if (!this.position.equals(this.destination)) { return }
this.despawn()
}
@@ -131,7 +134,7 @@ export default class Projectile {
const bbox = this.bbox
const entitiesAndTerrains = this.game?.entities ?? []
const bboxCheckedObstacles = entitiesAndTerrains.filter((it) => SATX.bboxCheck(bbox, it.bbox))
const bboxCheckedObstacles = entitiesAndTerrains.filter((it) => !it.dead && SATX.bboxCheck(bbox, it.bbox))
if (bboxCheckedObstacles.length > 0) {
const collider = this.collider()
const colliding = bboxCheckedObstacles.filter((it) => it.colliders().some((c) => SATX.collideObject(collider, c)))
@@ -155,7 +158,7 @@ export default class Projectile {
if (this.#onCollide != null) {
const bbox = Entity.tunnelBbox(prevPos.x, prevPos.y, this.position.x, this.position.y, this.radius)
const entitiesAndTerrains = this.game?.entities ?? []
const bboxCheckedObstacles = entitiesAndTerrains.filter((it) => SATX.bboxCheck(bbox, it.bbox))
const bboxCheckedObstacles = entitiesAndTerrains.filter((it) => !it.dead && SATX.bboxCheck(bbox, it.bbox))
if (bboxCheckedObstacles.length > 0) {
const collider = Entity.tunnelCollider(prevPos.x, prevPos.y, this.position.x, this.position.y, this.radius)
const colliding = bboxCheckedObstacles.filter((it) => it.colliders().some((c) => SATX.collideObject(collider, c)))