From 344b313b599734103b7bd38340b2470aa2cce7cb Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Mon, 17 Feb 2025 15:55:22 -0500 Subject: [PATCH] v3.0.0: implemented life link's spell sharing effect also fixed mnemonic prayer extending buffs from trinity and PoM --- .../assets/messages/actors/actors.properties | 4 +- .../shatteredpixeldungeon/actors/Char.java | 16 +-- .../actors/hero/spells/BlessSpell.java | 34 ++++-- .../actors/hero/spells/Cleanse.java | 8 ++ .../actors/hero/spells/ClericSpell.java | 9 +- .../actors/hero/spells/DivineSense.java | 12 ++- .../actors/hero/spells/HallowedGround.java | 101 +++++++++++++----- .../actors/hero/spells/LayOnHands.java | 35 ++++-- .../actors/hero/spells/MnemonicPrayer.java | 46 +++++--- .../actors/hero/spells/ShieldOfLight.java | 7 ++ .../items/armor/Armor.java | 7 +- .../shatteredpixeldungeon/levels/Level.java | 10 +- 12 files changed, 210 insertions(+), 79 deletions(-) diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index a98e4ad38..b9a26ff33 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -642,7 +642,7 @@ actors.hero.spells.divinesense.name=divine sense actors.hero.spells.divinesense.short_desc=Gain temporary mind vision in a wide range. actors.hero.spells.divinesense.desc=The Cleric focuses their senses on their surroundings, gaining mind vision with a %d tile range for 30 turns. actors.hero.spells.divinesense$divinesensetracker.name=divine sense -actors.hero.spells.divinesense$divinesensetracker.desc=The Cleric is temporarily able to see other nearby creatures with their mind!\n\nTurns remaining: %s. +actors.hero.spells.divinesense$divinesensetracker.desc=This character is temporarily able to see other nearby creatures with their mind!\n\nTurns remaining: %s. actors.hero.spells.divineintervention.name=divine intervention actors.hero.spells.divineintervention.short_desc=Massive shield boost to Cleric and allies. @@ -738,7 +738,7 @@ actors.hero.spells.shieldoflight.name=shield of light actors.hero.spells.shieldoflight.short_desc=Grants temporary armor against a target. actors.hero.spells.shieldoflight.desc=The Cleric creates a thin barrier of light between themselves and an enemy, increasing their armor's block power against that enemy by %1$d-%2$d for 4 turns.\n\nThis spell takes no time to cast, but cannot be used against multiple targets simultaneously. actors.hero.spells.shieldoflight$shieldoflighttracker.name=shield of light -actors.hero.spells.shieldoflight$shieldoflighttracker.desc=A thin shield of light is standing between the Cleric and an enemy. It's not strong enough to outright block attacks, but will soften them.\n\nTurns Remaining: %s +actors.hero.spells.shieldoflight$shieldoflighttracker.desc=A thin shield of light is standing between this character and an enemy. It's not strong enough to outright block attacks, but will soften them.\n\nTurns Remaining: %s actors.hero.spells.spiritform.name=spirit form actors.hero.spells.spiritform.short_desc=Assigns Trinity to a ring or artifact. diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java index deaf5f32e..fc0f83a95 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -458,8 +458,8 @@ public abstract class Char extends Actor { } if (Dungeon.hero.alignment == enemy.alignment - && Dungeon.level.distance(enemy.pos, Dungeon.hero.pos) <= 2 - && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null){ + && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null + && (Dungeon.level.distance(enemy.pos, Dungeon.hero.pos) <= 2 || enemy.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null)){ dmg *= 0.925f - 0.075f*Dungeon.hero.pointsInTalent(Talent.AURA_OF_PROTECTION); } @@ -699,8 +699,8 @@ public abstract class Char extends Actor { // hero and pris images skip this as they already benefit from hero's armor glyph proc if (!(this instanceof Hero || this instanceof PrismaticImage)) { if (Dungeon.hero.alignment == alignment && Dungeon.hero.belongings.armor() != null - && Dungeon.level.distance(pos, Dungeon.hero.pos) <= 2 - && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null) { + && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null + && (Dungeon.level.distance(pos, Dungeon.hero.pos) <= 2 || buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null)) { damage = Dungeon.hero.belongings.armor().proc( enemy, this, damage ); } } @@ -713,8 +713,8 @@ public abstract class Char extends Actor { public int glyphLevel(Class cls){ if (Dungeon.hero != null && Dungeon.level != null && this != Dungeon.hero && Dungeon.hero.alignment == alignment - && Dungeon.level.distance(pos, Dungeon.hero.pos) <= 2 - && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null) { + && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null + && (Dungeon.level.distance(pos, Dungeon.hero.pos) <= 2 || buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null)) { return Dungeon.hero.glyphLevel(cls); } else { return -1; @@ -795,8 +795,8 @@ public abstract class Char extends Actor { //if dmg is from a character we already reduced it in defenseProc if (!(src instanceof Char)) { if (Dungeon.hero.alignment == alignment - && Dungeon.level.distance(pos, Dungeon.hero.pos) <= 2 - && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null) { + && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null + && (Dungeon.level.distance(pos, Dungeon.hero.pos) <= 2 || buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null)) { damage *= 0.925f - 0.075f*Dungeon.hero.pointsInTalent(Talent.AURA_OF_PROTECTION); } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BlessSpell.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BlessSpell.java index 6816ddcba..8e76baf48 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BlessSpell.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BlessSpell.java @@ -30,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Bless; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; 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.effects.Flare; import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; @@ -72,15 +73,35 @@ public class BlessSpell extends TargetedClericSpell { Sample.INSTANCE.play(Assets.Sounds.TELEPORT); - new Flare(6, 32).color(0xFFFF00, true).show(ch.sprite, 2f); + affectChar(hero, ch); + if (ch == hero){ + hero.busy(); + hero.sprite.operate(ch.pos); + hero.spend( 1f ); + } else { + hero.sprite.zap(ch.pos); + hero.spendAndNext( 1f ); + } + + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + if (ch == hero){ + affectChar(hero, ally); //if cast on hero, duplicate to ally + } else if (ally == ch) { + affectChar(hero, hero); //if cast on ally, duplicate to hero + } + } + + onSpellCast(tome, hero); + } + + private void affectChar(Hero hero, Char ch){ + new Flare(6, 32).color(0xFFFF00, true).show(ch.sprite, 2f); if (ch == hero){ Buff.prolong(ch, Bless.class, 2f + 4*hero.pointsInTalent(Talent.BLESS)); Buff.affect(ch, Barrier.class).setShield(5 + 5*hero.pointsInTalent(Talent.BLESS)); ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(5 + 5*hero.pointsInTalent(Talent.BLESS)), FloatingText.SHIELDING ); - hero.busy(); - hero.sprite.operate(ch.pos); - hero.spend( 1f ); } else { Buff.prolong(ch, Bless.class, 5f + 5*hero.pointsInTalent(Talent.BLESS)); int totalHeal = 5 + 5*hero.pointsInTalent(Talent.BLESS); @@ -99,12 +120,7 @@ public class BlessSpell extends TargetedClericSpell { ch.HP = ch.HP + totalHeal; ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(totalHeal), FloatingText.HEALING ); } - - hero.sprite.zap(ch.pos); - hero.spendAndNext( 1f ); } - - onSpellCast(tome, hero); } public String desc(){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Cleanse.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Cleanse.java index e414e2c49..6db00d88b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Cleanse.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/Cleanse.java @@ -30,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LostInventory; 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.effects.Flare; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; @@ -78,6 +79,13 @@ public class Cleanse extends ClericSpell { } } + Char ally = PowerOfMany.getPoweredAlly(); + //hero is always affected, to just check for life linked ally + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null + && !affected.contains(ally)){ + affected.add(ally); + } + for (Char ch : affected) { for (Buff b : ch.buffs()) { if (b.type == Buff.buffType.NEGATIVE diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ClericSpell.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ClericSpell.java index 874d0f93d..cbbad1029 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ClericSpell.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ClericSpell.java @@ -22,6 +22,7 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility; @@ -29,6 +30,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.cleric.AscendedForm; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.cleric.PowerOfMany; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.ui.HeroIcon; @@ -74,7 +76,12 @@ public abstract class ClericSpell { public void onSpellCast(HolyTome tome, Hero hero){ Invisibility.dispel(); if (hero.hasTalent(Talent.SATIATED_SPELLS) && hero.buff(Talent.SatiatedSpellsTracker.class) != null){ - Buff.affect(hero, Barrier.class).setShield(1 + 2*hero.pointsInTalent(Talent.SATIATED_SPELLS)); + int amount = 1 + 2*hero.pointsInTalent(Talent.SATIATED_SPELLS); + Buff.affect(hero, Barrier.class).setShield(amount); + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + Buff.affect(ally, Barrier.class).setShield(amount); + } hero.buff(Talent.SatiatedSpellsTracker.class).detach(); } tome.spendCharge(chargeUse(hero)); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/DivineSense.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/DivineSense.java index 3efd54398..66c4475f6 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/DivineSense.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/DivineSense.java @@ -23,10 +23,13 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells; import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff; 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.effects.SpellSprite; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; @@ -55,15 +58,22 @@ public class DivineSense extends ClericSpell { @Override public void onCast(HolyTome tome, Hero hero) { - Buff.affect(hero, DivineSenseTracker.class, 30f); + Buff.prolong(hero, DivineSenseTracker.class, 30f); Dungeon.observe(); Sample.INSTANCE.play(Assets.Sounds.READ); hero.spend( 1f ); hero.busy(); + SpellSprite.show(hero, SpellSprite.VISION); hero.sprite.operate(hero.pos); + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + Buff.prolong(ally, DivineSenseTracker.class, 30f); + SpellSprite.show(ally, SpellSprite.VISION); + } + onSpellCast(tome, hero); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/HallowedGround.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/HallowedGround.java index ac2de31ff..04b223f24 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/HallowedGround.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/HallowedGround.java @@ -35,6 +35,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Regeneration; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Roots; 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.effects.BlobEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText; @@ -54,6 +55,8 @@ import com.watabou.utils.BArray; import com.watabou.utils.PathFinder; import com.watabou.utils.Random; +import java.util.ArrayList; + public class HallowedGround extends TargetedClericSpell { public static final HallowedGround INSTANCE = new HallowedGround(); @@ -90,6 +93,8 @@ public class HallowedGround extends TargetedClericSpell { return; } + ArrayList affected = new ArrayList<>(); + PathFinder.buildDistanceMap(target, BArray.not(Dungeon.level.solid, null), hero.pointsInTalent(Talent.HALLOWED_GROUND)); for (int i = 0; i < Dungeon.level.length(); i++){ if (PathFinder.distance[i] != Integer.MAX_VALUE){ @@ -104,27 +109,24 @@ public class HallowedGround extends TargetedClericSpell { Char ch = Actor.findChar(i); if (ch != null){ - if (ch.alignment == Char.Alignment.ALLY){ - - if (ch == Dungeon.hero || ch.HP == ch.HT){ - Buff.affect(ch, Barrier.class).incShield(10); - ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, "10", FloatingText.SHIELDING ); - } else { - int barrier = 10 - (ch.HT - ch.HP); - barrier = Math.max(barrier, 0); - ch.HP += 10 - barrier; - ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(10-barrier), FloatingText.HEALING ); - if (barrier > 0){ - Buff.affect(ch, Barrier.class).incShield(barrier); - ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(barrier), FloatingText.SHIELDING ); - } - } - } else if (!ch.flying) { - Buff.affect(ch, Roots.class, 1f); - } + affected.add(ch); } } } + + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + if (affected.contains(hero) && !affected.contains(ally)){ + affected.add(ally); + } else if (!affected.contains(hero) && affected.contains(ally)){ + affected.add(hero); + } + } + + for (Char ch : affected){ + affectChar(ch); + } + //5 casts per hero level before furrowing Buff.affect(hero, HallowedFurrowTracker.class).countUp(1); @@ -136,6 +138,27 @@ public class HallowedGround extends TargetedClericSpell { } + private void affectChar( Char ch ){ + if (ch.alignment == Char.Alignment.ALLY){ + + if (ch == Dungeon.hero || ch.HP == ch.HT){ + Buff.affect(ch, Barrier.class).incShield(10); + ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, "10", FloatingText.SHIELDING ); + } else { + int barrier = 10 - (ch.HT - ch.HP); + barrier = Math.max(barrier, 0); + ch.HP += 10 - barrier; + ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(10-barrier), FloatingText.HEALING ); + if (barrier > 0){ + Buff.affect(ch, Barrier.class).incShield(barrier); + ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(barrier), FloatingText.SHIELDING ); + } + } + } else if (!ch.flying) { + Buff.affect(ch, Roots.class, 1f); + } + } + public String desc(){ int area = 1 + 2*Dungeon.hero.pointsInTalent(Talent.HALLOWED_GROUND); return Messages.get(this, "desc", area) + "\n\n" + Messages.get(this, "charge_cost", (int)chargeUse(Dungeon.hero)); @@ -150,6 +173,8 @@ public class HallowedGround extends TargetedClericSpell { Fire fire = (Fire)Dungeon.level.blobs.get( Fire.class ); + ArrayList affected = new ArrayList<>(); + // on avg, hallowed ground produces 9/17/25 tiles of grass, 100/67/50% of total tiles int chance = 10 + 10*Dungeon.hero.pointsInTalent(Talent.HALLOWED_GROUND); @@ -184,17 +209,7 @@ public class HallowedGround extends TargetedClericSpell { Char ch = Actor.findChar(cell); if (ch != null){ - if (ch.alignment == Char.Alignment.ALLY){ - if (ch == Dungeon.hero || ch.HP == ch.HT){ - Buff.affect(ch, Barrier.class).incShield(1); - ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, "1", FloatingText.SHIELDING ); - } else { - ch.HP++; - ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, "1", FloatingText.HEALING ); - } - } else if (!ch.flying && ch.buff(Roots.class) == null){ - Buff.prolong(ch, Cripple.class, 1f); - } + affected.add(ch); } off[cell] = cur[cell] - 1; @@ -204,6 +219,34 @@ public class HallowedGround extends TargetedClericSpell { } } } + + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + if (affected.contains(Dungeon.hero) && !affected.contains(ally)){ + affected.add(ally); + } else if (!affected.contains(Dungeon.hero) && affected.contains(ally)){ + affected.add(Dungeon.hero); + } + } + + for (Char ch :affected){ + affectChar(ch); + } + + } + + private void affectChar( Char ch ){ + if (ch.alignment == Char.Alignment.ALLY){ + if (ch == Dungeon.hero || ch.HP == ch.HT){ + Buff.affect(ch, Barrier.class).incShield(1); + ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, "1", FloatingText.SHIELDING ); + } else { + ch.HP++; + ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, "1", FloatingText.HEALING ); + } + } else if (!ch.flying && ch.buff(Roots.class) == null){ + Buff.prolong(ch, Cripple.class, 1f); + } } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/LayOnHands.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/LayOnHands.java index cea3322cd..eec6206ad 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/LayOnHands.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/LayOnHands.java @@ -29,6 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Barrier; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; 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.effects.FloatingText; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; @@ -80,6 +81,30 @@ public class LayOnHands extends TargetedClericSpell { Sample.INSTANCE.play(Assets.Sounds.TELEPORT); + affectChar(hero, ch); + + if (ch == hero){ + hero.sprite.operate(ch.pos); + hero.next(); + } else { + hero.sprite.zap(ch.pos); + hero.next(); + } + + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + if (ch == hero){ + affectChar(hero, ally); //if cast on hero, duplicate to ally + } else if (ally == ch) { + affectChar(hero, hero); //if cast on ally, duplicate to hero + } + } + + onSpellCast(tome, hero); + + } + + private void affectChar(Hero hero, Char ch){ Barrier barrier = Buff.affect(ch, Barrier.class); int totalHeal = 5 + 5*hero.pointsInTalent(Talent.LAY_ON_HANDS); int totalBarrier = 0; @@ -89,8 +114,6 @@ public class LayOnHands extends TargetedClericSpell { totalBarrier = Math.max(0, totalBarrier); Buff.affect(ch, Barrier.class).incShield(totalBarrier); ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(totalBarrier), FloatingText.SHIELDING ); - hero.sprite.operate(ch.pos); - hero.next(); } else { if (ch.HT - ch.HP < totalHeal){ totalBarrier = totalHeal - (ch.HT - ch.HP); @@ -100,7 +123,7 @@ public class LayOnHands extends TargetedClericSpell { ch.HP = ch.HT; ch.sprite.showStatusWithIcon(CharSprite.POSITIVE, Integer.toString(totalHeal - totalBarrier), FloatingText.HEALING); } - if (totalBarrier >= 0) { + if (totalBarrier > 0) { barrier.incShield(totalBarrier); ch.sprite.showStatusWithIcon(CharSprite.POSITIVE, Integer.toString(totalBarrier), FloatingText.SHIELDING); } @@ -108,12 +131,6 @@ public class LayOnHands extends TargetedClericSpell { ch.HP = ch.HP + totalHeal; ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(totalHeal), FloatingText.HEALING ); } - - hero.sprite.zap(ch.pos); - hero.next(); } - - onSpellCast(tome, hero); - } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/MnemonicPrayer.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/MnemonicPrayer.java index 064e07261..9ef78bc02 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/MnemonicPrayer.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/MnemonicPrayer.java @@ -38,6 +38,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FireImbue; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.GreaterHaste; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Healing; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LifeLink; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Ooze; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Poison; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ShieldBuff; @@ -46,6 +47,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.WellFed; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.cleric.AscendedForm; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.cleric.PowerOfMany; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.Viscosity; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; @@ -97,7 +99,32 @@ public class MnemonicPrayer extends TargetedClericSpell { QuickSlotButton.target(ch); float extension = 2 + hero.pointsInTalent(Talent.MNEMONIC_PRAYER); + affectChar(ch, extension); + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + if (ch == hero){ + affectChar(ally, extension); //if cast on hero, duplicate to ally + } else if (ch == ally){ + affectChar(hero, extension); //if cast on ally, duplicate to hero + } + } + + if (ch == hero){ + hero.busy(); + hero.sprite.operate(ch.pos); + hero.spend( 1f ); + BuffIndicator.refreshHero(); + } else { + hero.sprite.zap(ch.pos); + hero.spendAndNext( 1f ); + } + + onSpellCast(tome, hero); + + } + + private void affectChar( Char ch, float extension ){ if (ch.alignment == Char.Alignment.ALLY){ Sample.INSTANCE.play(Assets.Sounds.CHARGEUP); @@ -108,8 +135,10 @@ public class MnemonicPrayer extends TargetedClericSpell { continue; } - //no it does not boost ascended form lmao - if (b instanceof AscendedForm.AscendBuff){ + //does not boost buffs from armor abilities or T4 spells + if (b instanceof AscendedForm.AscendBuff + || b instanceof BodyForm.BodyFormBuff || b instanceof SpiritForm.SpiritFormBuff + || b instanceof PowerOfMany.PowerBuff || b instanceof BeamingRay.BeamingRayBoost || b instanceof LifeLink || b instanceof LifeLinkSpell.LifeLinkSpellBuff){ continue; } @@ -164,19 +193,6 @@ public class MnemonicPrayer extends TargetedClericSpell { } } - - if (ch == hero){ - hero.busy(); - hero.sprite.operate(ch.pos); - hero.spend( 1f ); - BuffIndicator.refreshHero(); - } else { - hero.sprite.zap(ch.pos); - hero.spendAndNext( 1f ); - } - - onSpellCast(tome, hero); - } public String desc(){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ShieldOfLight.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ShieldOfLight.java index 10317a3c6..f2ea0f76e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ShieldOfLight.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/ShieldOfLight.java @@ -29,6 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.FlavourBuff; 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.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; @@ -84,6 +85,12 @@ public class ShieldOfLight extends TargetedClericSpell { hero.sprite.operate(hero.pos); hero.sprite.emitter().start(Speck.factory(Speck.LIGHT), 0.15f, 6); + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null){ + Buff.prolong( ally, ShieldOfLightTracker.class, 3f).object = ch.id(); + ally.sprite.emitter().start(Speck.factory(Speck.LIGHT), 0.15f, 6); + } + onSpellCast(tome, hero); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java index 621b45204..2c0f3bdd7 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/armor/Armor.java @@ -36,6 +36,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.AuraOfProtection; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.BodyForm; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.HolyWard; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.LifeLinkSpell; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.shatteredpixel.shatteredpixeldungeon.items.BrokenSeal; import com.shatteredpixel.shatteredpixeldungeon.items.EquipableItem; @@ -464,8 +465,8 @@ public class Armor extends EquipableItem { } //so that this effect procs for allies using this armor via aura of protection if (defender.alignment == Dungeon.hero.alignment - && Dungeon.level.distance(defender.pos, Dungeon.hero.pos) <= 2 && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null + && (Dungeon.level.distance(defender.pos, Dungeon.hero.pos) <= 2 || defender.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null) && Dungeon.hero.buff(HolyWard.HolyArmBuff.class) != null) { int blocking = Dungeon.hero.subClass == HeroSubClass.PALADIN ? 3 : 1; damage -= Math.round(blocking * Glyph.genericProcChanceMultiplier(defender)); @@ -756,8 +757,8 @@ public class Armor extends EquipableItem { float multi = RingOfArcana.enchantPowerMultiplier(defender); if (Dungeon.hero.alignment == defender.alignment - && Dungeon.level.distance(defender.pos, Dungeon.hero.pos) <= 2 - && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null){ + && Dungeon.hero.buff(AuraOfProtection.AuraBuff.class) != null + && (Dungeon.level.distance(defender.pos, Dungeon.hero.pos) <= 2 || defender.buff(LifeLinkSpell.LifeLinkSpellBuff.class) != null)){ multi += 0.25f + 0.25f*Dungeon.hero.pointsInTalent(Talent.AURA_OF_PROTECTION); } 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 e910b3f31..f4092a1da 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java @@ -1377,13 +1377,19 @@ public abstract class Level implements Bundlable { } mindVisRange = Math.max(mindVisRange, EyeOfNewt.mindVisionRange()); + //power of many's life link spell allows allies to get divine sense + Char ally = PowerOfMany.getPoweredAlly(); + if (ally != null && ally.buff(DivineSense.DivineSenseTracker.class) == null){ + ally = null; + } + if (mindVisRange >= 1) { for (Mob mob : mobs) { - if (mob instanceof Mimic && mob.alignment == Char.Alignment.NEUTRAL&& ((Mimic) mob).stealthy()){ + if (mob instanceof Mimic && mob.alignment == Char.Alignment.NEUTRAL && ((Mimic) mob).stealthy()){ continue; } int p = mob.pos; - if (!fieldOfView[p] && distance(c.pos, p) <= mindVisRange) { + if (!fieldOfView[p] && (distance(c.pos, p) <= mindVisRange || (ally != null && distance(ally.pos, p) <= mindVisRange))) { for (int i : PathFinder.NEIGHBOURS9) { heroMindFov[mob.pos + i] = true; }