v3.3.2: improved code for managing usage of targeted spells

Most notably this fixes exploits on reclaim trap where it would count as used when not being consumed.
This commit is contained in:
Evan Debenham
2025-12-31 15:10:31 -05:00
parent e227830d3b
commit 4e73f82c46
5 changed files with 25 additions and 9 deletions

View File

@@ -63,6 +63,7 @@ public class PhaseShift extends TargetedSpell {
} else {
GLog.w( Messages.get(this, "no_target") );
}
onSpellused();
}
@Override

View File

@@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.items.spells;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.quest.MetalShard;
@@ -82,7 +83,6 @@ public class ReclaimTrap extends TargetedSpell {
}
}
if (storedTrap == null) {
quantity++; //storing a trap doesn't consume the spell
Trap t = Dungeon.level.traps.get(bolt.collisionPos);
if (t != null && t.active && t.visible) {
t.disarm(); //even disarms traps that normally wouldn't be
@@ -95,6 +95,11 @@ public class ReclaimTrap extends TargetedSpell {
} else {
GLog.w(Messages.get(this, "no_trap"));
}
//spell is not consumed, so doesn't count as a full use
Invisibility.dispel();
curUser.spendAndNext( timeToCast() );
} else {
Trap t = Reflection.newInstance(storedTrap);
@@ -104,6 +109,8 @@ public class ReclaimTrap extends TargetedSpell {
Bestiary.countEncounter(t.getClass());
t.activate();
onSpellused();
}
}

View File

@@ -57,6 +57,17 @@ public abstract class TargetedSpell extends Spell {
Sample.INSTANCE.play( Assets.Sounds.ZAP );
}
protected void onSpellused(){
detach( curUser.belongings.backpack );
Invisibility.dispel();
updateQuickslot();
curUser.spendAndNext( timeToCast() );
Catalog.countUse(getClass());
if (Random.Float() < talentChance){
Talent.onScrollUsed(curUser, curUser.pos, talentFactor, getClass());
}
}
protected float timeToCast(){
return Actor.TICK;
}
@@ -93,14 +104,7 @@ public abstract class TargetedSpell extends Spell {
curSpell.fx(shot, new Callback() {
public void call() {
curSpell.affectTarget(shot, curUser);
curSpell.detach( curUser.belongings.backpack );
Invisibility.dispel();
curSpell.updateQuickslot();
curUser.spendAndNext( curSpell.timeToCast() );
Catalog.countUse(curSpell.getClass());
if (Random.Float() < curSpell.talentChance){
Talent.onScrollUsed(curUser, curUser.pos, curSpell.talentFactor, curSpell.getClass());
}
curSpell.onSpellused();
}
});

View File

@@ -59,6 +59,7 @@ public class TelekineticGrab extends TargetedSpell {
@Override
protected float timeToCast() {
//time is processed in the spell's logic, as it relates to items picked up
return 0;
}
@@ -133,6 +134,7 @@ public class TelekineticGrab extends TargetedSpell {
hero.spend(Actor.TICK);
}
onSpellused();
}
@Override

View File

@@ -67,6 +67,8 @@ public class WildEnergy extends TargetedSpell {
Buff.affect(hero, Recharging.class, 8f);
Buff.affect(hero, ArtifactRecharge.class).extend( 8 ).ignoreHornOfPlenty = false;
onSpellused();
}
@Override