v2.1.0: fixed projectile traps picking targets before actually firing
This commit is contained in:
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user