From 86cf0ad29bdd444c48dc190e33bcf143fb89c7e8 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Tue, 14 Jan 2025 16:22:47 -0500 Subject: [PATCH] v3.0.0: implemented the Paladin's lay on hands spell --- .../assets/messages/actors/actors.properties | 8 +- .../actors/hero/Talent.java | 4 +- .../actors/hero/spells/ClericSpell.java | 6 +- .../actors/hero/spells/LayOnHands.java | 114 ++++++++++++++++++ 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/LayOnHands.java diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 03447ba1c..1181b0960 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -647,6 +647,10 @@ actors.hero.spells.judgement.name=judgement actors.hero.spells.judgement.short_desc=Damages all visible enemies. actors.hero.spells.judgement.desc=The Cleric slams their fist onto the ground, causing an eruption of light that deals damage to all enemies they can see. This deals %1$d-%2$d damage at base, but also deals an additional 5-10 damage for each spell the Cleric has cast since entering ascended form (or since they last cast Judgement).\n\nCurrently Judgement will deal %3$d-%4$d damage. +actors.hero.spells.layonhands.name=lay on hands +actors.hero.spells.layonhands.short_desc=Instantly heals an adjacent character or shields the Paladin. +actors.hero.spells.layonhands.desc=The Paladin channels holy energy through their hands, healing or protecting whatever they touch.\n\nThe Paladin can cast this spell on any adjacent character to give them %1$d healing, or on themselves to gain %1$d shielding. Excess healing from this spell is converted into shielding.\n\nThis spell is cast instantaneously and can be cast repeatedly, but cannot grant more than three casts worth of shielding at a time. + actors.hero.spells.mindform.name=mind form actors.hero.spells.mindform.short_desc=TODO actors.hero.spells.mindform.desc=TODO @@ -1152,8 +1156,8 @@ actors.hero.talent.hallowed_ground.desc=_+1:_ The Priest can cast _Hallowed Grou actors.hero.talent.mnemonic_prayer.title=Mnemonic Prayer actors.hero.talent.mnemonic_prayer.desc=_+1:_ The Priest can cast _Mnemonic Prayer,_ a spell that extends buffs/debuffs on an ally/enemy for _3 turns_ at 1 charge cost.\n\n_+2:_ The Priest can cast _Mnemonic Prayer,_ a spell that extends buffs/debuffs on an ally/enemy for _4 turns_ at 1 charge cost.\n\n_+3:_ The Priest can cast _Mnemonic Prayer,_ a spell that extends buffs/debuffs on an ally/enemy for _5 turns_ at 1 charge cost.\n\nMnemonic Prayer also re-illuminates the target if they were illuminated previously. Mnemonic Prayer can only extend a particular instance of a buff/debuff once, and cannot extend buffs from armor abilities. -actors.hero.talent.paladint3a.title=Unknown -actors.hero.talent.paladint3a.desc=This talent hasn't been implemented yet, it currently does nothing. +actors.hero.talent.lay_on_hands.title=lay on hands +actors.hero.talent.lay_on_hands.desc=_+1:_ The Paladin can cast _Lay on Hands,_ a spell that instantly heals a nearby character for _10 HP,_ or grants the Paladin _10 shielding,_ at the cost of 1 charge.\n\n_+2:_ The Paladin can cast _Lay on Hands,_ a spell that instantly heals a nearby character for _15 HP,_ or grants the Paladin _15 shielding,_ at the cost of 1 charge.\n\n_+3:_ The Paladin can cast _Lay on Hands,_ a spell that instantly heals a nearby character for _20 HP,_ or grants the Paladin _20 shielding,_ at the cost of 1 charge.\n\nExcess healing from this spell is converted into shielding. Lay on Hands can be cast repeatedly, but cannot apply more than three casts worth of shielding at once. actors.hero.talent.paladint3b.title=Unknown actors.hero.talent.paladint3b.desc=This talent hasn't been implemented yet, it currently does nothing. actors.hero.talent.paladint3c.title=Unknown diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java index 3b4690333..d08024252 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Talent.java @@ -183,7 +183,7 @@ public enum Talent { //Priest T3 HOLY_LANCE(171, 3), HALLOWED_GROUND(172, 3), MNEMONIC_PRAYER(173, 3), //Paladin T3 - PALADINT3A(174, 3), PALADINT3B(175, 3), PALADINT3C(176, 3), + LAY_ON_HANDS(174, 3), PALADINT3B(175, 3), PALADINT3C(176, 3), //Ascended Form T4 DIVINE_INTERVENTION(177, 4), JUDGEMENT(178, 4), FLASH(179, 4), //Trinity T4 @@ -1022,7 +1022,7 @@ public enum Talent { Collections.addAll(tierTalents, HOLY_LANCE, HALLOWED_GROUND, MNEMONIC_PRAYER); break; case PALADIN: - Collections.addAll(tierTalents, PALADINT3A, PALADINT3B, PALADINT3C); + Collections.addAll(tierTalents, LAY_ON_HANDS, PALADINT3B, PALADINT3C); break; } for (Talent talent : tierTalents){ 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 359d40935..f078ee84c 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 @@ -152,7 +152,9 @@ public abstract class ClericSpell { spells.add(MnemonicPrayer.INSTANCE); } - //TODO paladin spells + if (cleric.hasTalent(Talent.LAY_ON_HANDS)){ + spells.add(LayOnHands.INSTANCE); + } } else if (tier == 4){ @@ -194,6 +196,8 @@ public abstract class ClericSpell { spells.add(BlessSpell.INSTANCE); spells.add(Cleanse.INSTANCE); spells.add(Radiance.INSTANCE); + spells.add(Smite.INSTANCE); + spells.add(LayOnHands.INSTANCE); spells.add(HolyLance.INSTANCE); spells.add(HallowedGround.INSTANCE); spells.add(MnemonicPrayer.INSTANCE); 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 new file mode 100644 index 000000000..c397c93d8 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/LayOnHands.java @@ -0,0 +1,114 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2025 Evan Debenham + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + */ + +package com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; +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.hero.Hero; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; +import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText; +import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; +import com.shatteredpixel.shatteredpixeldungeon.ui.HeroIcon; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.watabou.noosa.audio.Sample; + +public class LayOnHands extends TargetedClericSpell { + + public static LayOnHands INSTANCE = new LayOnHands(); + + @Override + public int icon() { + return HeroIcon.LAY_ON_HANDS; + } + + @Override + public String desc() { + return Messages.get(this, "desc", 5 + 5*Dungeon.hero.pointsInTalent(Talent.LAY_ON_HANDS)) + "\n\n" + Messages.get(this, "charge_cost", (int)chargeUse(Dungeon.hero)); + } + + @Override + public int targetingFlags(){ + return -1; //auto-targeting behaviour is often wrong, so we don't use it + } + + @Override + protected void onTargetSelected(HolyTome tome, Hero hero, Integer target) { + if (target == null) { + return; + } + + if (Dungeon.level.distance(hero.pos, target) > 1){ + GLog.w(Messages.get(this, "invalid_target")); + return; + } + + Char ch = Actor.findChar(target); + if (ch == null){ + GLog.w(Messages.get(this, "no_target")); + return; + } + + Sample.INSTANCE.play(Assets.Sounds.TELEPORT); + + Barrier barrier = Buff.affect(ch, Barrier.class); + int totalHeal = 5 + 5*hero.pointsInTalent(Talent.LAY_ON_HANDS); + int totalBarrier = 0; + if (ch == hero){ + totalBarrier = totalHeal; + totalBarrier = Math.min(3*totalHeal - barrier.shielding(), totalBarrier); + 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); + totalBarrier = Math.min(3*totalHeal - barrier.shielding(), totalBarrier); + totalBarrier = Math.max(0, totalBarrier); + if (ch.HP != ch.HT) { + ch.HP = ch.HT; + ch.sprite.showStatusWithIcon(CharSprite.POSITIVE, Integer.toString(totalHeal - totalBarrier), FloatingText.HEALING); + } + if (totalBarrier >= 0) { + barrier.incShield(totalBarrier); + ch.sprite.showStatusWithIcon(CharSprite.POSITIVE, Integer.toString(totalBarrier), FloatingText.SHIELDING); + } + } else { + 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); + + } +}