From 8f5f410e9e350739c5e242f0bf304450aa78bdcb Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Tue, 18 Feb 2025 13:52:28 -0500 Subject: [PATCH] v3.0.0: stasis work: - stasis now preserves buffs, but this is buggy - stasis now properly works with all allies - added logic to beaming ray and stasis for immobile allies --- .../assets/messages/actors/actors.properties | 9 ++-- .../hero/abilities/cleric/PowerOfMany.java | 13 ++++-- .../actors/hero/spells/BeamingRay.java | 6 ++- .../actors/hero/spells/Stasis.java | 31 ++++++++++++-- .../actors/mobs/Mob.java | 31 ++++++++++++-- .../items/artifacts/DriedRose.java | 41 ++++++++++--------- .../exotic/ScrollOfPrismaticImage.java | 8 ++++ .../items/wands/WandOfLivingEarth.java | 11 ++++- .../items/wands/WandOfWarding.java | 5 +++ .../shatteredpixeldungeon/levels/Level.java | 7 ++++ 10 files changed, 124 insertions(+), 38 deletions(-) diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index fe09a44ec..eefbe5f59 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -614,7 +614,7 @@ actors.hero.spells.beamingray.name=beaming ray actors.hero.spells.beamingray.no_space=There's no space for your ally to appear there. actors.hero.spells.beamingray.out_of_range=That location is out of range. actors.hero.spells.beamingray.short_desc=Teleports your ally and grants bonus damage. -actors.hero.spells.beamingray.desc=The Cleric channels the power on their ally into a ray of light that beams them to a location. If the ray collides with an enemy it will place the ally nearby and direct them towards the enemy.\n\nThe ray has a maximum range of %1$d tiles, and will increase Power of Many's bonus damage to +%2$d%% for 10 turns against the closest enemy within 4 tiles. +actors.hero.spells.beamingray.desc=The Cleric channels the power on their ally into a ray of light that beams them to a location. If the ray collides with an enemy it will place the ally nearby and direct them towards the enemy.\n\nThe ray has a maximum range of %1$d tiles, and will increase Power of Many's bonus damage to +%2$d%% for 10 turns against the closest enemy within 4 tiles. Beaming ray can even teleport normally immobile allies, but its ranged is halved in these cases. actors.hero.spells.beamingray$beamingrayboost.name=beaming ray actors.hero.spells.beamingray$beamingrayboost.desc=This ally was recently teleported by Beaming Ray, giving them bonus damage against the nearest enemy after the teleport. Power of Many also won't expire when this buff is active.\n\nTurns Remaining: %s. @@ -752,8 +752,9 @@ actors.hero.spells.smite.desc=The Paladin infuses a melee strike with righteous actors.hero.spells.stasis.name=stasis actors.hero.spells.stasis.short_desc=Removes ally from dungeon and preserves them. -actors.hero.spells.stasis.desc=The Cleric focuses their connection with their ally toward themselves, causing the ally to be temporarily held in a sort of energy stasis within the Cleric. The ally will be preserved for up to %d turns, with all positive effects on them being unaffected by their time in stasis.\n\nOnce the spell ends naturally, or once the Cleric re-casts it, the ally will appear next to the Cleric. +actors.hero.spells.stasis.desc=The Cleric focuses their connection with their ally toward themselves, causing the ally to be temporarily held in a sort of energy stasis within the Cleric. The ally will be preserved for up to %d turns, with all positive effects on them being unaffected by their time in stasis.\n\nOnce the spell ends naturally, or once the Cleric re-casts it, the ally will appear next to the Cleric. Stasis can even move immobile allies, but cannot transfer them between floors. actors.hero.spells.stasis$stasisbuff.name=ally in stasis +actors.hero.spells.stasis$stasisbuff.left_behind=Your immobile ally is left behind. actors.hero.spells.stasis$stasisbuff.desc=The Cleric currently has an ally stored in stasis. All positive effects on this ally are preserved, including Power of Many, and they will reappear next to the Cleric when the effect ends. The effect can also be ended early by re-casting Stasis.\n\nAlly in stasis: %1$s.\nTurns Remaining: %2$s. actors.hero.spells.sunray.name=sunray @@ -1257,11 +1258,11 @@ actors.hero.talent.spirit_form.title=spirit form actors.hero.talent.spirit_form.desc=The Cleric can cast _Spirit Form_, which imbues Trinity with the power of a ring or artifact that the Cleric has identified this run (except the holy tome), at the cost of 4 charges.\n\n_+1:_ When Trinity is used, the Cleric will gain a ring's effect at _+1_ for 20 turns, or will trigger an artifact's effect at _+4._\n\n_+2:_ When Trinity is used, the Cleric will gain a ring's effect at _+2_ for 20 turns, or will trigger an artifact's effect at _+6._\n\n_+3:_ When Trinity is used, the Cleric will gain a ring's effect at _+3_ for 20 turns, or will trigger an artifact's effect at _+8._\n\n_+4:_ When Trinity is used, the Cleric will gain a ring's effect at _+4_ for 20 turns, or will trigger an artifact's effect at _+10._\n\nTrinity can only be imbued with one spirit effect at a time. Artifacts have varied effects and armor charge costs when triggered by Trinity. actors.hero.talent.beaming_ray.title=beaming ray -actors.hero.talent.beaming_ray.desc=The Cleric can cast _Beaming Ray_ from an empowered ally at the cost of 1 charge. This ray can go through walls and teleports the ally to a targeted location. If an enemy is at that location, the ally appears next to the enemy and will target them.\n\n_+1:_ Beaming Ray has a max range of 4 tiles, and increases Power of Many's bonus to _+35% damage_ against the closest enemy for 10 turns.\n\n_+2:_ Beaming Ray has a max range of 8 tiles, and increases Power of Many's bonus to _+40% damage_ against the closest enemy for 10 turns.\n\n_+3:_ Beaming Ray has a max range of 12 tiles, and increases Power of Many's bonus to _+45% damage_ against the closest enemy for 10 turns.\n\n_+4:_ Beaming Ray has a max range of 16 tiles, and increases Power of Many's bonus to _+50% damage_ against the closest enemy for 10 turns. +actors.hero.talent.beaming_ray.desc=The Cleric can cast _Beaming Ray_ from an empowered ally at the cost of 1 charge. This ray can go through walls and teleports the ally to a targeted location. If an enemy is at that location, the ally appears next to the enemy and will target them. Beaming ray can even teleport normally immobile allies, but has half range when doing so.\n\n_+1:_ Beaming Ray has a max range of 4 tiles, and increases Power of Many's bonus to _+35% damage_ against the closest enemy for 10 turns.\n\n_+2:_ Beaming Ray has a max range of 8 tiles, and increases Power of Many's bonus to _+40% damage_ against the closest enemy for 10 turns.\n\n_+3:_ Beaming Ray has a max range of 12 tiles, and increases Power of Many's bonus to _+45% damage_ against the closest enemy for 10 turns.\n\n_+4:_ Beaming Ray has a max range of 16 tiles, and increases Power of Many's bonus to _+50% damage_ against the closest enemy for 10 turns. actors.hero.talent.life_link.title=life link actors.hero.talent.life_link.desc=The Cleric can cast _Life Link_ between themselves and an empowered ally at the cost of 2 charges. This spell causes damage to be shared between the Cleric and their ally, and causes beneficial cleric spells of tier 3 or lower to apply to both if either is being affected.\n\n_+1:_ Life Link lasts for _6 turns_ and increases Power of Many's bonus to _-35% damage taken._\n\n_+2:_ Life Link lasts for _8 turns_ and increases Power of Many's bonus to _-40% damage taken._\n\n_+3:_ Life Link lasts for _10 turns_ and increases Power of Many's bonus to _-45% damage taken._\n\n_+4:_ Life Link lasts for _12 turns_ and increases Power of Many's bonus to _-50% damage taken._ actors.hero.talent.stasis.title=Stasis -actors.hero.talent.stasis.desc=The Cleric can cast _Stasis_ on an empowered ally at the cost of 1 charge. Stasis temporarily removes the ally from the dungeon and preserves the remaining time on all buffs, including Power of Many. The ally will reappear next to you when the effect ends. Stasis can be re-cast instantly and at no cost to end the effect early.\n\n_+1:_ Stasis lasts for a max of _40 turns._\n\n_+2:_ Stasis lasts for a max of _60 turns._\n\n_+3:_ Stasis lasts for a max of _80 turns._\n\n_+4:_ Stasis lasts for a max of _100 turns._\n\nThe Cleric can also cast Beaming Ray when an ally is in stasis to resummon them, or cast Life Link to pre-emptively apply its effect. An ally in stasis still benefits from life link if relevant. +actors.hero.talent.stasis.desc=The Cleric can cast _Stasis_ on an empowered ally at the cost of 1 charge. Stasis temporarily removes the ally from the dungeon and preserves the remaining time on all buffs, including Power of Many. The ally will reappear next to you when the effect ends. Stasis can be re-cast instantly and at no cost to end the effect early. Stasis works on immobile allies, but cannot move them between dungeon floors.\n\n_+1:_ Stasis lasts for a max of _40 turns._\n\n_+2:_ Stasis lasts for a max of _60 turns._\n\n_+3:_ Stasis lasts for a max of _80 turns._\n\n_+4:_ Stasis lasts for a max of _100 turns._\n\nThe Cleric can also cast Beaming Ray when an ally is in stasis to resummon them, or cast Life Link to pre-emptively apply its effect. An ally in stasis still benefits from life link if relevant. #universal diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/cleric/PowerOfMany.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/cleric/PowerOfMany.java index 2467c5cfa..42a007bb8 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/cleric/PowerOfMany.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/cleric/PowerOfMany.java @@ -84,6 +84,10 @@ public class PowerOfMany extends ArmorAbility { allyExists = true; } + if (Stasis.getStasisAlly() != null){ + allyExists = true; + } + if (ally instanceof LightAlly){ return Messages.get(this, "prompt_ally"); } else if (!allyExists){ @@ -114,7 +118,7 @@ public class PowerOfMany extends ArmorAbility { allyExists = true; } - if (hero.buff(Stasis.StasisBuff.class) != null){ + if (Stasis.getStasisAlly() != null){ allyExists = true; } @@ -225,9 +229,6 @@ public class PowerOfMany extends ArmorAbility { @Override public void detach() { super.detach(); - if (target instanceof LightAlly){ - target.die(this); - } Dungeon.observe(); GameScene.updateFog(); } @@ -259,6 +260,10 @@ public class PowerOfMany extends ArmorAbility { @Override protected boolean act() { + if (buff(PowerOfMany.PowerBuff.class) == null){ + die(null); + return true; + } int oldPos = pos; boolean result = super.act(); //partially simulates how the hero switches to idle animation diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BeamingRay.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BeamingRay.java index 3da1a1fe1..3063905ff 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BeamingRay.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BeamingRay.java @@ -95,7 +95,11 @@ public class BeamingRay extends TargetedClericSpell { return; } - if (Dungeon.level.distance(ally.pos, telePos) > 4*hero.pointsInTalent(Talent.BEAMING_RAY)){ + int range = 4*hero.pointsInTalent(Talent.BEAMING_RAY); + if (Char.hasProp(ally, Char.Property.IMMOVABLE)){ + range /= 2; + } + if (Dungeon.level.distance(ally.pos, telePos) > range){ GLog.w(Messages.get(this, "out_of_range")); return; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Stasis.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Stasis.java index c0bf67455..5fb3b777f 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Stasis.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Stasis.java @@ -32,6 +32,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.cleric.PowerOfMany; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.DirectableAlly; import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation; @@ -45,6 +46,7 @@ import com.watabou.utils.PathFinder; import com.watabou.utils.Random; import java.util.ArrayList; +import java.util.LinkedHashSet; public class Stasis extends ClericSpell { @@ -91,8 +93,18 @@ public class Stasis extends ClericSpell { hero.sprite.zap(ally.pos); MagicMissile.boltFromChar(hero.sprite.parent, MagicMissile.LIGHT_MISSILE, ally.sprite, hero.pos, null); - //TODO preserve positive effects on ally, don't care about negative ones? + //TODO need to preserve buffs properly, this half works, makes durations wacky (based on current Actor.now values) + LinkedHashSet buffs = ally.buffs(); Actor.remove(ally); + ally.sprite.killAndErase(); + ally.sprite = null; + Dungeon.level.mobs.remove(ally); + for (Buff b : buffs){ + if (b.type == Buff.buffType.POSITIVE) { + ally.add(b); + } + } + Buff.prolong(hero, StasisBuff.class, 20 + 20*hero.pointsInTalent(Talent.STASIS)).stasisAlly = (Mob)ally; Sample.INSTANCE.play(Assets.Sounds.TELEPORT); @@ -100,14 +112,21 @@ public class Stasis extends ClericSpell { hero.buff(LifeLink.class).detach(); } - //TODO will need to make affordances for lots of edge cases like earth golem, wards limits, ghost hero, PoE ally, others? - - //TODO need code in beamign ray and life link to work here, also life link cleric spells? + //TODO need code in beaming ray and life link to work here, also life link cleric spells? hero.spendAndNext(Actor.TICK); + Dungeon.observe(); + GameScene.updateFog(); } + public static Char getStasisAlly(){ + if (Dungeon.hero != null && Dungeon.hero.buff(StasisBuff.class) != null){ + return Dungeon.hero.buff(StasisBuff.class).stasisAlly; + } + return null; + } + public static class StasisBuff extends FlavourBuff { { @@ -146,6 +165,10 @@ public class Stasis extends ClericSpell { stasisAlly.clearTime(); GameScene.add(stasisAlly); + if (stasisAlly instanceof DirectableAlly){ + ((DirectableAlly) stasisAlly).clearDefensingPos(); + } + ScrollOfTeleportation.appear(stasisAlly, stasisAlly.pos); Sample.INSTANCE.play(Assets.Sounds.TELEPORT); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java index cc106cc24..c3dc4c2d1 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java @@ -56,6 +56,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.Fe import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.rogue.ShadowClone; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.ClericSpell; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.GuidingLight; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.Stasis; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.DirectableAlly; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText; @@ -1359,13 +1360,16 @@ public abstract class Mob extends Char { public static void holdAllies( Level level, int holdFromPos ){ heldAllies.clear(); for (Mob mob : level.mobs.toArray( new Mob[0] )) { - //preserve directable allies no matter where they are - if (mob instanceof DirectableAlly) { - ((DirectableAlly) mob).clearDefensingPos(); + //preserve directable allies or empowered intelligent allies no matter where they are + if (mob instanceof DirectableAlly + || (mob.intelligentAlly && PowerOfMany.getPoweredAlly() == mob)) { + if (mob instanceof DirectableAlly) { + ((DirectableAlly) mob).clearDefensingPos(); + } level.mobs.remove( mob ); heldAllies.add(mob); - //preserve intelligent allies if they are near the hero + //preserve other intelligent allies if they are near the hero } else if (mob.alignment == Alignment.ALLY && mob.intelligentAlly && Dungeon.level.distance(holdFromPos, mob.pos) <= 5){ @@ -1401,8 +1405,27 @@ public abstract class Mob extends Char { } }); } + + //can only have one empowered ally at once, prioritize incoming ally + if (Stasis.getStasisAlly() != null){ + for (Mob mob : level.mobs.toArray( new Mob[0] )) { + if (mob.buff(PowerOfMany.PowerBuff.class) != null){ + mob.buff(PowerOfMany.PowerBuff.class).detach(); + } + } + } for (Mob ally : heldAllies) { + + //can only have one empowered ally at once, prioritize incoming ally + if (ally.buff(PowerOfMany.PowerBuff.class) != null){ + for (Mob mob : level.mobs.toArray( new Mob[0] )) { + if (mob.buff(PowerOfMany.PowerBuff.class) != null){ + mob.buff(PowerOfMany.PowerBuff.class).detach(); + } + } + } + level.mobs.add(ally); ally.state = ally.WANDERING; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java index afbaf8e31..c6aab9a4c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/DriedRose.java @@ -37,6 +37,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Regeneration; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Belongings; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.Stasis; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Wraith; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.DirectableAlly; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Ghost; @@ -204,19 +205,30 @@ public class DriedRose extends Artifact { } else if (action.equals(AC_DIRECT)){ if (ghost == null && ghostID != 0){ - Actor a = Actor.findById(ghostID); - if (a != null){ - ghost = (GhostHero)a; - } else { - ghostID = 0; - } + findGhost(); + } + if (ghost != null && ghost != Stasis.getStasisAlly()){ + GameScene.selectCell(ghostDirector); } - if (ghost != null) GameScene.selectCell(ghostDirector); } else if (action.equals(AC_OUTFIT)){ GameScene.show( new WndGhostHero(this) ); } } + + private void findGhost(){ + Actor a = Actor.findById(ghostID); + if (a != null){ + ghost = (GhostHero)a; + } else { + if (Stasis.getStasisAlly() instanceof GhostHero){ + ghost = (GhostHero) Stasis.getStasisAlly(); + ghostID = ghost.id(); + } else { + ghostID = 0; + } + } + } public int ghostStrength(){ return 13 + level()/2; @@ -275,12 +287,7 @@ public class DriedRose extends Artifact { public String status() { if (ghost == null && ghostID != 0){ try { - Actor a = Actor.findById(ghostID); - if (a != null) { - ghost = (GhostHero) a; - } else { - ghostID = 0; - } + findGhost(); } catch ( ClassCastException e ){ ShatteredPixelDungeon.reportException(e); ghostID = 0; @@ -391,12 +398,7 @@ public class DriedRose extends Artifact { spend( TICK ); if (ghost == null && ghostID != 0){ - Actor a = Actor.findById(ghostID); - if (a != null){ - ghost = (GhostHero)a; - } else { - ghostID = 0; - } + findGhost(); } if (ghost != null && !ghost.isAlive()){ @@ -745,6 +747,7 @@ public class DriedRose extends Artifact { @Override public void destroy() { updateRose(); + //TODO stasis? if (rose != null) { rose.ghost = null; rose.charge = 0; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfPrismaticImage.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfPrismaticImage.java index b1d79b3f4..af465d24d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfPrismaticImage.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/scrolls/exotic/ScrollOfPrismaticImage.java @@ -25,6 +25,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.PrismaticGuard; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.Stasis; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.PrismaticImage; import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText; @@ -50,6 +51,13 @@ public class ScrollOfPrismaticImage extends ExoticScroll { m.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(m.HT), FloatingText.HEALING ); } } + + if (!found){ + if (Stasis.getStasisAlly() instanceof PrismaticImage){ + found = true; + Stasis.getStasisAlly().HP = Stasis.getStasisAlly().HT; + } + } if (!found) { Buff.affect(curUser, PrismaticGuard.class).set( PrismaticGuard.maxHP( curUser ) ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfLivingEarth.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfLivingEarth.java index 4c52ad61c..833e910a6 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfLivingEarth.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfLivingEarth.java @@ -31,6 +31,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.cleric.PowerOfMany; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.Stasis; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC; @@ -82,6 +83,10 @@ public class WandOfLivingEarth extends DamageWand { } } + if (Stasis.getStasisAlly() instanceof EarthGuardian){ + guardian = (EarthGuardian)Stasis.getStasisAlly(); + } + RockArmor buff = curUser.buff(RockArmor.class); //only grant armor if we are shooting at an enemy, a hiding mimic, or the guardian if ((guardian == null || ch != guardian) && (ch == null @@ -176,7 +181,9 @@ public class WandOfLivingEarth extends DamageWand { curUser.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 8 + buffedLvl() / 2); } } else { - guardian.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 8 + buffedLvl() / 2); + if (guardian.sprite != null) { //may be in stasis + guardian.sprite.centerEmitter().burst(MagicMissile.EarthParticle.ATTRACT, 8 + buffedLvl() / 2); + } guardian.setInfo(curUser, buffedLvl(), armorToAdd); if (ch.alignment == Char.Alignment.ENEMY || ch.buff(Amok.class) != null) { guardian.aggro(ch); @@ -381,7 +388,7 @@ public class WandOfLivingEarth extends DamageWand { this.wandLevel = wandLevel; HT = 16 + 8 * wandLevel; } - if (HP != 0){ + if (HP != 0 && sprite != null){ sprite.showStatusWithIcon(CharSprite.POSITIVE, Integer.toString(healthToAdd), FloatingText.HEALING); } HP = Math.min(HT, HP + healthToAdd); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfWarding.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfWarding.java index 6bf9f8f85..5d05d9c0e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfWarding.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfWarding.java @@ -33,6 +33,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Sleep; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Terror; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.Stasis; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC; import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText; import com.shatteredpixel.shatteredpixeldungeon.effects.MagicMissile; @@ -78,6 +79,10 @@ public class WandOfWarding extends Wand { currentWardEnergy += ((Ward) ch).tier; } } + + if (Stasis.getStasisAlly() instanceof Ward){ + currentWardEnergy += ((Ward) Stasis.getStasisAlly()).tier; + } int maxWardEnergy = 0; for (Buff buff : curUser.buffs()){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java index f4092a1da..1476ab539 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java @@ -51,6 +51,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.cleric.PowerOfMany; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.SpiritHawk; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.DivineSense; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.Stasis; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.GnollGeomancer; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; @@ -590,6 +591,12 @@ public abstract class Level implements Bundlable { ScrollOfChallenge.ChallengeArena arena = Dungeon.hero.buff(ScrollOfChallenge.ChallengeArena.class); if (arena != null) arena.detach(); + Char ally = Stasis.getStasisAlly(); + if (Char.hasProp(ally, Char.Property.IMMOVABLE)){ + Dungeon.hero.buff(Stasis.StasisBuff.class).act(); + GLog.w(Messages.get(Stasis.StasisBuff.class, "left_behind")); + } + //spend the hero's partial turns, so the hero cannot take partial turns between floors Dungeon.hero.spendToWhole(); for (Actor a : Actor.all()){