From 4acd7a288158fd09cd8087d42d08ad21c3366430 Mon Sep 17 00:00:00 2001 From: Thayol Date: Thu, 23 Jan 2025 10:36:28 +0900 Subject: [PATCH] fix projectiles colliding with dead entities --- src/ability.js | 59 +++++++++++++++++++++++++++++------------------ src/entity.js | 11 ++++++--- src/level.js | 4 ++-- src/projectile.js | 7 ++++-- 4 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/ability.js b/src/ability.js index 588f4f6..4687ce5 100644 --- a/src/ability.js +++ b/src/ability.js @@ -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)) diff --git a/src/entity.js b/src/entity.js index 4624b9b..54384a5 100644 --- a/src/entity.js +++ b/src/entity.js @@ -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) diff --git a/src/level.js b/src/level.js index ffd259e..fc65fad 100644 --- a/src/level.js +++ b/src/level.js @@ -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 }))) diff --git a/src/projectile.js b/src/projectile.js index 6b65762..fe3b434 100644 --- a/src/projectile.js +++ b/src/projectile.js @@ -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)))