diff --git a/core/src/main/assets/interfaces/hero_icons.png b/core/src/main/assets/interfaces/hero_icons.png index 52a2d638a..cb185a633 100644 Binary files a/core/src/main/assets/interfaces/hero_icons.png and b/core/src/main/assets/interfaces/hero_icons.png differ diff --git a/core/src/main/assets/interfaces/talent_icons.png b/core/src/main/assets/interfaces/talent_icons.png index 3878a31be..515f2eaf4 100644 Binary files a/core/src/main/assets/interfaces/talent_icons.png and b/core/src/main/assets/interfaces/talent_icons.png differ diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index b487f5ac3..f444431d2 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -544,6 +544,10 @@ actors.hero.abilities.ratmogrify$transmograt.desc=This enemy has been transforme actors.hero.abilities.ratmogrify$transmograt.rankings_desc=Slain by: ratmogrified enemy ##Cleric Spells +actors.hero.spells.blessspell.name=bless +actors.hero.spells.blessspell.short_desc=Blesses & shields Cleric, blesses & heals others. +actors.hero.spells.blessspell.desc=The Cleric places a holy blessing on themselves or another character they can see.\n\nWhen cast on themselves the effect is weaker, granting %1$d turns of bless and %2$d barrier.\n\nWhen cast on other characters the spell is more powerful, granting %3$d turns of bless and %4$d healing. Excess healing is converted into barrier. + actors.hero.spells.cleanse.name=cleanse actors.hero.spells.cleanse.short_desc=Clears debuffs and grants shielding. actors.hero.spells.cleanse.desc=The Cleric instantly clears all harmful effects on themselves and visible allies. After the spell ends all characters affected also gain %1$d turns of debuff immunity, and %2$d barrier. @@ -1071,8 +1075,8 @@ actors.hero.talent.sunray.title=Sunray actors.hero.talent.sunray.desc=_+1:_ The Cleric can cast _Sunray,_ A spell that deals _2-8 damage_ and blinds the target for _4 turns,_ at the cost of 1 charge.\n\n_+2:_ The Cleric can cast _Sunray,_ A spell that deals _3-12 damage_ and blinds the target for _6 turns,_ at the cost of 1 charge.\n\nSunray can only blind each target once, but if the target is already blinded by Sunray then it paralyses instead. Sunray always deals maximum damage to demonic and undead foes. actors.hero.talent.divine_sense.title=Divine Sense actors.hero.talent.divine_sense.desc=_+1:_ The Cleric can cast _Divine Sense,_ a spell that grants them _8 tiles_ of Mind Vision for 30 turns, at the cost of 2 charges.\n\n_+2:_ The Cleric can cast _Divine Sense,_ a spell that grants them _12 tiles_ of Mind Vision for 30 turns, at the cost of 2 charges. -actors.hero.talent.clerict2e.title=Unknown -actors.hero.talent.clerict2e.desc=This talent hasn't been implemented yet, it currently does nothing. +actors.hero.talent.bless.title=Bless +actors.hero.talent.bless.desc=_+1:_ The Cleric can cast _Bless,_ a spell that grants _4 turns of bless and 10 shielding_ when cast on themselves or _10 turns of bless and 15 healing_ when cast on another character, at the cost of 1 charge.\n\n_+2:_ The Cleric can cast _Bless,_ a spell that grants _6 turns of bless and 15 shielding_ when cast on themselves or _15 turns of bless and 25 healing_ when cast on another character, at the cost of 1 charge.\n\nExcess healing from this spell is converted into shielding. actors.hero.talent.cleanse.title=Cleanse actors.hero.talent.cleanse.desc=_+1:_ The Cleric can cast _Cleanse,_ a spell which is cast instantly, _removes negative status effects_ from the Cleric and any nearby allies, and grants them _10 shielding,_ at the cost of 2 charges.\n\n_+2:_ The Cleric can cast _Cleanse,_ a spell which is cast instantly, _grants 3 turns of negative status immunity_ to the Cleric and any nearby allies, and grants them _20 shielding,_ at the cost of 2 charges.\n\n_+3:_ The Cleric can cast _Cleanse,_ a spell which is cast instantly, _grants 5 turns of negative status immunity_ to the Cleric and any nearby allies, and grants them _30 shielding,_ at the cost of 2 charges. 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 1167e4d1f..d74a66d20 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 @@ -178,7 +178,7 @@ public enum Talent { //Cleric T1 SATIATED_SPELLS(160), DETECT_CURSE(161), SEARING_LIGHT(162), SHIELD_OF_LIGHT(163), //Cleric T2 - ENLIGHTENING_MEAL(164), RECALL_GLYPH(165), SUNRAY(166), DIVINE_SENSE(167), CLERICT2E(168), + ENLIGHTENING_MEAL(164), RECALL_GLYPH(165), SUNRAY(166), DIVINE_SENSE(167), BLESS(168), //Cleric T3 CLEANSE(169, 3), LIGHT_READING(170, 3), //Priest T3 @@ -937,7 +937,7 @@ public enum Talent { Collections.addAll(tierTalents, FOCUSED_MEAL, LIQUID_AGILITY, WEAPON_RECHARGING, LETHAL_HASTE, SWIFT_EQUIP); break; case CLERIC: - Collections.addAll(tierTalents, ENLIGHTENING_MEAL, RECALL_GLYPH, SUNRAY, DIVINE_SENSE, CLERICT2E); + Collections.addAll(tierTalents, ENLIGHTENING_MEAL, RECALL_GLYPH, SUNRAY, DIVINE_SENSE, BLESS); break; } for (Talent talent : tierTalents){ 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 new file mode 100644 index 000000000..fac1fe130 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/spells/BlessSpell.java @@ -0,0 +1,102 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2024 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.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.effects.Flare; +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 BlessSpell extends TargetedClericSpell { + + public static final BlessSpell INSTANCE = new BlessSpell(); + + @Override + public int icon() { + return HeroIcon.BLESS; + } + + @Override + protected void onTargetSelected(HolyTome tome, Hero hero, Integer target) { + if (target == null){ + return; + } + + Char ch = Actor.findChar(target); + if (ch == null || !Dungeon.level.heroFOV[target]){ + GLog.w(Messages.get(this, "no_target")); + return; + } + + Sample.INSTANCE.play(Assets.Sounds.TELEPORT); + + new Flare(6, 32).color(0xFFFF00, true).show(ch.sprite, 2f); + + if (ch == hero){ + Buff.prolong(ch, Bless.class, 2f + 2*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.sprite.operate(ch.pos); + } else { + Buff.prolong(ch, Bless.class, 5f + 5*hero.pointsInTalent(Talent.BLESS)); + int totalHeal = 5 + 10*hero.pointsInTalent(Talent.BLESS); + if (ch.HT - ch.HP < totalHeal){ + int barrier = totalHeal - (ch.HT - ch.HP); + if (ch.HP != ch.HT) { + ch.HP = ch.HT; + ch.sprite.showStatusWithIcon(CharSprite.POSITIVE, Integer.toString(totalHeal - barrier), FloatingText.HEALING); + } + Buff.affect(ch, Barrier.class).setShield(barrier); + ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(barrier), FloatingText.SHIELDING ); + } else { + ch.HP = ch.HP + totalHeal; + ch.sprite.showStatusWithIcon( CharSprite.POSITIVE, Integer.toString(totalHeal), FloatingText.HEALING ); + } + + hero.sprite.zap(ch.pos); + } + + hero.busy(); + hero.sprite.operate(hero.pos); + + onSpellCast(tome, hero); + } + + public String desc(){ + int talentLvl = Dungeon.hero.pointsInTalent(Talent.BLESS); + return Messages.get(this, "desc", 2+2*talentLvl, 5+5*talentLvl, 5+5*talentLvl, 5+10*talentLvl) + "\n\n" + Messages.get(this, "charge_cost", (int)chargeUse(Dungeon.hero)); + } + +} 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 ca801f2f8..b10d3643a 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 @@ -80,44 +80,48 @@ public abstract class ClericSpell { spells.add(HolyWeapon.INSTANCE); spells.add(HolyWard.INSTANCE); - if (cleric.hasTalent(Talent.SHIELD_OF_LIGHT)) { - spells.add(ShieldOfLight.INSTANCE); - } - if (cleric.hasTalent(Talent.DETECT_CURSE)) { spells.add(DetectCurse.INSTANCE); } - } else if (tier == 2) { - - if (cleric.hasTalent(Talent.SUNRAY)){ - spells.add(Sunray.INSTANCE); + if (cleric.hasTalent(Talent.SHIELD_OF_LIGHT)) { + spells.add(ShieldOfLight.INSTANCE); } + } else if (tier == 2) { + if (cleric.hasTalent(Talent.RECALL_GLYPH)){ spells.add(RecallGlyph.INSTANCE); } + if (cleric.hasTalent(Talent.SUNRAY)){ + spells.add(Sunray.INSTANCE); + } + if (cleric.hasTalent(Talent.DIVINE_SENSE)) { spells.add(DivineSense.INSTANCE); } + if (cleric.hasTalent(Talent.BLESS)){ + spells.add(BlessSpell.INSTANCE); + } + } else if (tier == 3){ + if (cleric.subClass == HeroSubClass.PRIEST) { + spells.add(Radiance.INSTANCE); + + } else if (cleric.subClass == HeroSubClass.PALADIN){ + //TODO innate smite spell + + } + if (cleric.hasTalent(Talent.CLEANSE)){ spells.add(Cleanse.INSTANCE); } - if (cleric.subClass == HeroSubClass.PRIEST) { - spells.add(Radiance.INSTANCE); - - if (cleric.hasTalent(Talent.HOLY_LANCE)){ - spells.add(HolyLance.INSTANCE); - } - - } else if (cleric.subClass == HeroSubClass.PALADIN){ - //TODO innate smite spell - + if (cleric.hasTalent(Talent.HOLY_LANCE)){ + spells.add(HolyLance.INSTANCE); } } @@ -130,10 +134,10 @@ public abstract class ClericSpell { spells.add(GuidingLight.INSTANCE); spells.add(HolyWeapon.INSTANCE); spells.add(HolyWard.INSTANCE); - spells.add(ShieldOfLight.INSTANCE); spells.add(DetectCurse.INSTANCE); - spells.add(Sunray.INSTANCE); + spells.add(ShieldOfLight.INSTANCE); spells.add(RecallGlyph.INSTANCE); + spells.add(Sunray.INSTANCE); spells.add(DivineSense.INSTANCE); spells.add(Cleanse.INSTANCE); spells.add(Radiance.INSTANCE); 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 478906bee..c9c25a50e 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 @@ -55,7 +55,7 @@ public class ShieldOfLight extends TargetedClericSpell { } Char ch = Actor.findChar(target); - if (ch == null || ch.alignment == Char.Alignment.ALLY){ + if (ch == null || ch.alignment == Char.Alignment.ALLY || !Dungeon.level.heroFOV[target]){ GLog.w(Messages.get(this, "no_target")); return; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java index acc2c4392..55fc14534 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java @@ -76,14 +76,15 @@ public class HeroIcon extends Image { public static final int GUIDING_LIGHT = 40; public static final int HOLY_WEAPON = 41; public static final int HOLY_WARD = 42; - public static final int SHIELD_OF_LIGHT = 43; - public static final int DETECT_CURSE = 44; - public static final int SUNRAY = 45; - public static final int DIVINE_SENSE = 46; - public static final int RECALL_GLYPH = 47; - public static final int HOLY_LANCE = 48; - public static final int RADIANCE = 49; - public static final int CLEANSE = 50; + public static final int DETECT_CURSE = 43; + public static final int SHIELD_OF_LIGHT = 44; + public static final int RECALL_GLYPH = 45; + public static final int SUNRAY = 46; + public static final int DIVINE_SENSE = 47; + public static final int BLESS = 48; + public static final int CLEANSE = 49; + public static final int RADIANCE = 50; + public static final int HOLY_LANCE = 51; //all cleric spells have a separate icon with no background for the action indicator public static final int SPELL_ACTION_OFFSET = 32;