From d6dba23dd46523bdd7a75e118b7b65a5f7c50209 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Tue, 25 Apr 2023 13:39:13 -0400 Subject: [PATCH] v2.1.0: fixed projectile traps picking targets before actually firing --- .../levels/traps/GrimTrap.java | 129 ++++++++--------- .../levels/traps/PoisonDartTrap.java | 130 +++++++++--------- .../levels/traps/WornDartTrap.java | 101 +++++++------- 3 files changed, 186 insertions(+), 174 deletions(-) diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/GrimTrap.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/GrimTrap.java index 273d7844b..20a6fec6c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/GrimTrap.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/GrimTrap.java @@ -48,76 +48,79 @@ public class GrimTrap extends Trap { @Override public void activate() { - Char target = Actor.findChar(pos); - //find the closest char that can be aimed at - if (target == null){ - float closestDist = Float.MAX_VALUE; - for (Char ch : Actor.chars()){ - float curDist = Dungeon.level.trueDistance(pos, ch.pos); - if (ch.invisible > 0) curDist += 1000; - Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); - if (bolt.collisionPos == ch.pos && curDist < closestDist){ - target = ch; - closestDist = curDist; - } + //we handle this inside of a separate actor as the trap may produce a visual effect we need to pause for + Actor.add(new Actor() { + + { + actPriority = VFX_PRIO; } - } - if (target != null){ - final Char finalTarget = target; - final GrimTrap trap = this; - int damage; - - //instant kill, use a mix of current HP and max HP, just like psi blast (for resistances) - damage = Math.round(finalTarget.HT/2f + finalTarget.HP/2f); + @Override + protected boolean act() { + Actor.remove(this); + Char target = Actor.findChar(pos); - //can't do more than 90% HT for the hero specifically - if (finalTarget == Dungeon.hero){ - damage = (int)Math.min(damage, finalTarget.HT*0.9f); - } - - final int finalDmg = damage; - - Actor.add(new Actor() { - - { - //it's a visual effect, gets priority no matter what - actPriority = VFX_PRIO; + //find the closest char that can be aimed at + if (target == null){ + float closestDist = Float.MAX_VALUE; + for (Char ch : Actor.chars()){ + float curDist = Dungeon.level.trueDistance(pos, ch.pos); + if (ch.invisible > 0) curDist += 1000; + Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); + if (bolt.collisionPos == ch.pos && curDist < closestDist){ + target = ch; + closestDist = curDist; + } + } } - - @Override - protected boolean act() { - final Actor toRemove = this; - ((MagicMissile)finalTarget.sprite.parent.recycle(MagicMissile.class)).reset( - MagicMissile.SHADOW, - DungeonTilemap.tileCenterToWorld(pos), - finalTarget.sprite.center(), - new Callback() { - @Override - public void call() { - finalTarget.damage(finalDmg, trap); - if (finalTarget == Dungeon.hero) { - Sample.INSTANCE.play(Assets.Sounds.CURSED); - if (!finalTarget.isAlive()) { - Badges.validateDeathFromGrimOrDisintTrap(); - Dungeon.fail( GrimTrap.class ); - GLog.n( Messages.get(GrimTrap.class, "ondeath") ); + + if (target != null) { + final Char finalTarget = target; + //instant kill, use a mix of current HP and max HP, just like psi blast (for resistances) + int damage = Math.round(finalTarget.HT/2f + finalTarget.HP/2f); + + //can't do more than 90% HT for the hero specifically + if (finalTarget == Dungeon.hero){ + damage = (int)Math.min(damage, finalTarget.HT*0.9f); + } + + final int finalDmg = damage; + if (Dungeon.level.heroFOV[pos] || Dungeon.level.heroFOV[target.pos]) { + ((MagicMissile)finalTarget.sprite.parent.recycle(MagicMissile.class)).reset( + MagicMissile.SHADOW, + DungeonTilemap.tileCenterToWorld(pos), + finalTarget.sprite.center(), + new Callback() { + @Override + public void call() { + finalTarget.damage(finalDmg, GrimTrap.this); + if (finalTarget == Dungeon.hero) { + Sample.INSTANCE.play(Assets.Sounds.CURSED); + if (!finalTarget.isAlive()) { + Badges.validateDeathFromGrimOrDisintTrap(); + Dungeon.fail( GrimTrap.this.getClass() ); + GLog.n( Messages.get(GrimTrap.class, "ondeath") ); + } + } else { + Sample.INSTANCE.play(Assets.Sounds.BURNING); } - } else { - Sample.INSTANCE.play(Assets.Sounds.BURNING); + finalTarget.sprite.emitter().burst(ShadowParticle.UP, 10); + next(); } - finalTarget.sprite.emitter().burst(ShadowParticle.UP, 10); - Actor.remove(toRemove); - next(); - } - }); - return false; + }); + return false; + } else { + finalTarget.damage(finalDmg, GrimTrap.this); + return true; + } + } else { + CellEmitter.get(pos).burst(ShadowParticle.UP, 10); + Sample.INSTANCE.play(Assets.Sounds.BURNING); + return true; } - }); - } else { - CellEmitter.get(pos).burst(ShadowParticle.UP, 10); - Sample.INSTANCE.play(Assets.Sounds.BURNING); - } + } + + }); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/PoisonDartTrap.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/PoisonDartTrap.java index 3b1780904..d17e12398 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/PoisonDartTrap.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/PoisonDartTrap.java @@ -56,70 +56,74 @@ public class PoisonDartTrap extends Trap { @Override public void activate() { - Char target = Actor.findChar(pos); - - if (target != null && !canTarget(target)){ - target = null; - } - - //find the closest char that can be aimed at - if (target == null){ - float closestDist = Float.MAX_VALUE; - for (Char ch : Actor.chars()){ - float curDist = Dungeon.level.trueDistance(pos, ch.pos); - if (ch.invisible > 0) curDist += 1000; - Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); - if (canTarget(ch) && bolt.collisionPos == ch.pos && curDist < closestDist){ - target = ch; - closestDist = curDist; + + //we handle this inside of a separate actor as the trap may produce a visual effect we need to pause for + Actor.add(new Actor() { + + { + actPriority = VFX_PRIO; + } + + @Override + protected boolean act() { + Actor.remove(this); + Char target = Actor.findChar(pos); + + if (target != null && !canTarget(target)){ + target = null; + } + + //find the closest char that can be aimed at + if (target == null){ + float closestDist = Float.MAX_VALUE; + for (Char ch : Actor.chars()){ + float curDist = Dungeon.level.trueDistance(pos, ch.pos); + if (ch.invisible > 0) curDist += 1000; + Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); + if (canTarget(ch) && bolt.collisionPos == ch.pos && curDist < closestDist){ + target = ch; + closestDist = curDist; + } + } + } + + if (target != null) { + final Char finalTarget = target; + if (Dungeon.level.heroFOV[pos] || Dungeon.level.heroFOV[target.pos]) { + ((MissileSprite) ShatteredPixelDungeon.scene().recycle(MissileSprite.class)). + reset(pos, finalTarget.sprite, new PoisonDart(), new Callback() { + @Override + public void call() { + int dmg = Random.NormalIntRange(4, 8) - finalTarget.drRoll(); + finalTarget.damage(dmg, PoisonDartTrap.this); + if (finalTarget == Dungeon.hero){ + //for the poison dart traps in the Tengu fight + if (Dungeon.depth == 10) { + Statistics.qualifiedForBossChallengeBadge = false; + Statistics.bossScores[1] -= 100; + } + if (!finalTarget.isAlive()) { + Dungeon.fail(PoisonDartTrap.this.getClass()); + } + } + Buff.affect( finalTarget, Poison.class ).set( poisonAmount() ); + Sample.INSTANCE.play(Assets.Sounds.HIT, 1, 1, Random.Float(0.8f, 1.25f)); + finalTarget.sprite.bloodBurstA(finalTarget.sprite.center(), dmg); + finalTarget.sprite.flash(); + next(); + } + }); + return false; + } else { + finalTarget.damage(Random.NormalIntRange(4, 8) - finalTarget.drRoll(), PoisonDartTrap.this); + Buff.affect( finalTarget, Poison.class ).set( poisonAmount() ); + return true; + } + } else { + return true; } } - } - if (target != null) { - final Char finalTarget = target; - final PoisonDartTrap trap = this; - if (Dungeon.level.heroFOV[pos] || Dungeon.level.heroFOV[target.pos]) { - Actor.add(new Actor() { - - { - //it's a visual effect, gets priority no matter what - actPriority = VFX_PRIO; - } - - @Override - protected boolean act() { - final Actor toRemove = this; - ((MissileSprite) ShatteredPixelDungeon.scene().recycle(MissileSprite.class)). - reset(pos, finalTarget.sprite, new PoisonDart(), new Callback() { - @Override - public void call() { - int dmg = Random.NormalIntRange(4, 8) - finalTarget.drRoll(); - finalTarget.damage(dmg, trap); - if (finalTarget == Dungeon.hero){ - //for the poison dart traps in the Tengu fight - if (Dungeon.depth == 10) { - Statistics.qualifiedForBossChallengeBadge = false; - Statistics.bossScores[1] -= 100; - } - if (!finalTarget.isAlive()) { - Dungeon.fail(trap.getClass()); - } - } - Buff.affect( finalTarget, Poison.class ).set( poisonAmount() ); - Sample.INSTANCE.play(Assets.Sounds.HIT, 1, 1, Random.Float(0.8f, 1.25f)); - finalTarget.sprite.bloodBurstA(finalTarget.sprite.center(), dmg); - finalTarget.sprite.flash(); - Actor.remove(toRemove); - next(); - } - }); - return false; - } - }); - } else { - finalTarget.damage(Random.NormalIntRange(4, 8) - finalTarget.drRoll(), trap); - Buff.affect( finalTarget, Poison.class ).set( poisonAmount() ); - } - } + + }); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/WornDartTrap.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/WornDartTrap.java index 39bd28b17..eaa8e4f2e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/WornDartTrap.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/traps/WornDartTrap.java @@ -45,56 +45,61 @@ public class WornDartTrap extends Trap { @Override public void activate() { - Char target = Actor.findChar(pos); - if (target == null){ - float closestDist = Float.MAX_VALUE; - for (Char ch : Actor.chars()){ - float curDist = Dungeon.level.trueDistance(pos, ch.pos); - if (ch.invisible > 0) curDist += 1000; - Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); - if (bolt.collisionPos == ch.pos && curDist < closestDist){ - target = ch; - closestDist = curDist; + //we handle this inside of a separate actor as the trap may produce a visual effect we need to pause for + Actor.add(new Actor() { + + { + actPriority = VFX_PRIO; + } + + @Override + protected boolean act() { + Actor.remove(this); + Char target = Actor.findChar(pos); + + //find the closest char that can be aimed at + if (target == null){ + float closestDist = Float.MAX_VALUE; + for (Char ch : Actor.chars()){ + float curDist = Dungeon.level.trueDistance(pos, ch.pos); + if (ch.invisible > 0) curDist += 1000; + Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); + if (bolt.collisionPos == ch.pos && curDist < closestDist){ + target = ch; + closestDist = curDist; + } + } + } + + if (target != null) { + final Char finalTarget = target; + if (Dungeon.level.heroFOV[pos] || Dungeon.level.heroFOV[target.pos]) { + ((MissileSprite) ShatteredPixelDungeon.scene().recycle(MissileSprite.class)). + reset(pos, finalTarget.sprite, new Dart(), new Callback() { + @Override + public void call() { + int dmg = Random.NormalIntRange(4, 8) - finalTarget.drRoll(); + finalTarget.damage(dmg, WornDartTrap.this); + if (finalTarget == Dungeon.hero && !finalTarget.isAlive()){ + Dungeon.fail( WornDartTrap.this.getClass() ); + } + Sample.INSTANCE.play(Assets.Sounds.HIT, 1, 1, Random.Float(0.8f, 1.25f)); + finalTarget.sprite.bloodBurstA(finalTarget.sprite.center(), dmg); + finalTarget.sprite.flash(); + next(); + } + }); + return false; + } else { + finalTarget.damage(Random.NormalIntRange(4, 8) - finalTarget.drRoll(), WornDartTrap.this); + return true; + } + } else { + return true; } } - } - if (target != null) { - final Char finalTarget = target; - final WornDartTrap trap = this; - if (Dungeon.level.heroFOV[pos] || Dungeon.level.heroFOV[target.pos]) { - Actor.add(new Actor() { - - { - //it's a visual effect, gets priority no matter what - actPriority = VFX_PRIO; - } - - @Override - protected boolean act() { - final Actor toRemove = this; - ((MissileSprite) ShatteredPixelDungeon.scene().recycle(MissileSprite.class)). - reset(pos, finalTarget.sprite, new Dart(), new Callback() { - @Override - public void call() { - int dmg = Random.NormalIntRange(4, 8) - finalTarget.drRoll(); - finalTarget.damage(dmg, trap); - if (finalTarget == Dungeon.hero && !finalTarget.isAlive()){ - Dungeon.fail( trap.getClass() ); - } - Sample.INSTANCE.play(Assets.Sounds.HIT, 1, 1, Random.Float(0.8f, 1.25f)); - finalTarget.sprite.bloodBurstA(finalTarget.sprite.center(), dmg); - finalTarget.sprite.flash(); - Actor.remove(toRemove); - next(); - } - }); - return false; - } - }); - } else { - finalTarget.damage(Random.NormalIntRange(4, 8) - finalTarget.drRoll(), trap); - } - } + + }); } }