v2.1.0: fixed projectile traps picking targets before actually firing

This commit is contained in:
Evan Debenham
2023-04-25 13:39:13 -04:00
parent c622a1658f
commit d6dba23dd4
3 changed files with 186 additions and 174 deletions

View File

@@ -48,76 +48,79 @@ public class GrimTrap extends Trap {
@Override @Override
public void activate() { public void activate() {
Char target = Actor.findChar(pos);
//find the closest char that can be aimed at //we handle this inside of a separate actor as the trap may produce a visual effect we need to pause for
if (target == null){ Actor.add(new Actor() {
float closestDist = Float.MAX_VALUE;
for (Char ch : Actor.chars()){ {
float curDist = Dungeon.level.trueDistance(pos, ch.pos); actPriority = VFX_PRIO;
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){ @Override
final Char finalTarget = target; protected boolean act() {
final GrimTrap trap = this; Actor.remove(this);
int damage; Char target = Actor.findChar(pos);
//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);
//can't do more than 90% HT for the hero specifically //find the closest char that can be aimed at
if (finalTarget == Dungeon.hero){ if (target == null){
damage = (int)Math.min(damage, finalTarget.HT*0.9f); float closestDist = Float.MAX_VALUE;
} for (Char ch : Actor.chars()){
float curDist = Dungeon.level.trueDistance(pos, ch.pos);
final int finalDmg = damage; if (ch.invisible > 0) curDist += 1000;
Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE);
Actor.add(new Actor() { if (bolt.collisionPos == ch.pos && curDist < closestDist){
target = ch;
{ closestDist = curDist;
//it's a visual effect, gets priority no matter what }
actPriority = VFX_PRIO; }
} }
@Override if (target != null) {
protected boolean act() { final Char finalTarget = target;
final Actor toRemove = this; //instant kill, use a mix of current HP and max HP, just like psi blast (for resistances)
((MagicMissile)finalTarget.sprite.parent.recycle(MagicMissile.class)).reset( int damage = Math.round(finalTarget.HT/2f + finalTarget.HP/2f);
MagicMissile.SHADOW,
DungeonTilemap.tileCenterToWorld(pos), //can't do more than 90% HT for the hero specifically
finalTarget.sprite.center(), if (finalTarget == Dungeon.hero){
new Callback() { damage = (int)Math.min(damage, finalTarget.HT*0.9f);
@Override }
public void call() {
finalTarget.damage(finalDmg, trap); final int finalDmg = damage;
if (finalTarget == Dungeon.hero) { if (Dungeon.level.heroFOV[pos] || Dungeon.level.heroFOV[target.pos]) {
Sample.INSTANCE.play(Assets.Sounds.CURSED); ((MagicMissile)finalTarget.sprite.parent.recycle(MagicMissile.class)).reset(
if (!finalTarget.isAlive()) { MagicMissile.SHADOW,
Badges.validateDeathFromGrimOrDisintTrap(); DungeonTilemap.tileCenterToWorld(pos),
Dungeon.fail( GrimTrap.class ); finalTarget.sprite.center(),
GLog.n( Messages.get(GrimTrap.class, "ondeath") ); 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 { finalTarget.sprite.emitter().burst(ShadowParticle.UP, 10);
Sample.INSTANCE.play(Assets.Sounds.BURNING); next();
} }
finalTarget.sprite.emitter().burst(ShadowParticle.UP, 10); });
Actor.remove(toRemove); return false;
next(); } else {
} finalTarget.damage(finalDmg, GrimTrap.this);
}); return true;
return false; }
} 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);
}
} }
} }

View File

@@ -56,70 +56,74 @@ public class PoisonDartTrap extends Trap {
@Override @Override
public void activate() { public void activate() {
Char target = Actor.findChar(pos);
//we handle this inside of a separate actor as the trap may produce a visual effect we need to pause for
if (target != null && !canTarget(target)){ Actor.add(new Actor() {
target = null;
} {
actPriority = VFX_PRIO;
//find the closest char that can be aimed at }
if (target == null){
float closestDist = Float.MAX_VALUE; @Override
for (Char ch : Actor.chars()){ protected boolean act() {
float curDist = Dungeon.level.trueDistance(pos, ch.pos); Actor.remove(this);
if (ch.invisible > 0) curDist += 1000; Char target = Actor.findChar(pos);
Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE);
if (canTarget(ch) && bolt.collisionPos == ch.pos && curDist < closestDist){ if (target != null && !canTarget(target)){
target = ch; target = null;
closestDist = curDist; }
//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() );
}
}
} }
} }

View File

@@ -45,56 +45,61 @@ public class WornDartTrap extends Trap {
@Override @Override
public void activate() { public void activate() {
Char target = Actor.findChar(pos);
if (target == null){ //we handle this inside of a separate actor as the trap may produce a visual effect we need to pause for
float closestDist = Float.MAX_VALUE; Actor.add(new Actor() {
for (Char ch : Actor.chars()){
float curDist = Dungeon.level.trueDistance(pos, ch.pos); {
if (ch.invisible > 0) curDist += 1000; actPriority = VFX_PRIO;
Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); }
if (bolt.collisionPos == ch.pos && curDist < closestDist){
target = ch; @Override
closestDist = curDist; 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);
}
}
} }
} }