v3.0.0: implemented the recall glyph talent/spell for scrolls only

This commit is contained in:
Evan Debenham
2024-11-04 11:46:18 -05:00
parent 6b4a03e36f
commit d5bb26b840
19 changed files with 194 additions and 15 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -570,6 +570,12 @@ actors.hero.spells.holyweapon.desc=The Cleric enchants their worn weapon with ho
actors.hero.spells.holyweapon$holywepbuff.name=holy weapon
actors.hero.spells.holyweapon$holywepbuff.desc=The Cleric has imbued their worn weapon with holy energy, temporarily overriding any existing enchantment and causing the weapon to deal an extra 2 magical damage on each attack.\n\nTurns renaming: %s.
actors.hero.spells.recallglyph.name=recall glyph
actors.hero.spells.recallglyph.short_desc=Repeats a recently used stone or scroll.
actors.hero.spells.recallglyph.desc=The Cleric uses holy magic to replicate and re-cast the effect of a magical glyph found on a runestone or scroll they used in the last %s turns.\n\nRecall Glyph cannot be used to replicate scrolls of upgrade. This spell's charge cost varies based on which item was used recently: 1 for a runestone, 2 for a scroll, 3 for an exotic scroll. This charge cost is also doubled when replicating a scroll of transmutation, or alchemy items that must be crafted using transmutation or upgrade.
actors.hero.spells.recallglyph$usedglyphtracker.name=recently used glyph
actors.hero.spells.recallglyph$usedglyphtracker.desc=The Cleric has recently used an item that works with the recall glyph spell. The Cleric can cast the spell to repeat the item's effect.\n\nItem Used: %1$s.\n\nTurns Remaining: %2$s.
actors.hero.spells.shieldoflight.name=shield of light
actors.hero.spells.shieldoflight.prompt=Choose a target
actors.hero.spells.shieldoflight.short_desc=Grants temporary armor against a target.
@@ -1020,12 +1026,12 @@ actors.hero.talent.detect_curse.desc=_+1:_ The Cleric can cast _Detect Curse,_ a
actors.hero.talent.searing_light.title=searing light
actors.hero.talent.searing_light.desc=_+1:_ Physical attacks on enemies illuminated by _Guiding Light_ deal _+3 damage._\n\n_+2:_ Physical attacks on enemies illuminated by _Guiding Light_ deal _+5 damage._
actors.hero.talent.shield_of_light.title=shield of light
actors.hero.talent.shield_of_light.desc=_+1:_ The Cleric can cast _Shield of Light,_ a spell that is cast instantly and grants them 2-4 armor against a target for _3 turns_ at the cost of 1 charge.\n\n_+2:_ The Cleric can cast _Shield of Light,_ a spell that is cast instantly and them 2-4 armor against a target for _5 turns_ at the cost of 1 charge.
actors.hero.talent.shield_of_light.desc=_+1:_ The Cleric can cast _Shield of Light,_ a spell that is cast instantly and grants them 2-4 armor against a target for _3 turns_ at the cost of 1 charge.\n\n_+2:_ The Cleric can cast _Shield of Light,_ a spell that is cast instantly and grants them 2-4 armor against a target for _5 turns_ at the cost of 1 charge.
actors.hero.talent.enlightening_meal.title=Enlightening Meal
actors.hero.talent.enlightening_meal.desc=_+1:_ Eating food takes the Cleric 1 turn and grants them _1 charge_ on their holy tome.\n\n_+2:_ Eating food takes the Cleric 1 turn and grants them _1.5 charges_ on their holy tome.
actors.hero.talent.clerict2b.title=TODO
actors.hero.talent.clerict2b.desc=TODO
actors.hero.talent.recall_glyph.title=Recall Glyph
actors.hero.talent.recall_glyph.desc=_+1:_ The Cleric can cast _Recall Glyph,_ a spell that lets the Cleric repeat the effect of the last runestone or scroll they used within the last _10 turns._\n\n_+2:_ The Cleric can cast _Recall Glyph,_ a spell that lets the Cleric repeat the effect of the last runestone or scroll they used within the last _300 turns._\n\nRecall Glyph cannot be used with scrolls of upgrade. This spell's charge cost varies based on which item was used recently: 1 for a runestone, 2 for a scroll, 3 for an exotic scroll. This charge cost is also doubled when used with a scroll of transmutation, or alchemy items that must be crafted using transmutation or upgrade.
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
@@ -44,6 +44,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.WandEmpower;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.ArmorAbility;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.Ratmogrify;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.GuidingLight;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells.RecallGlyph;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText;
@@ -57,6 +58,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.CloakOfShadows;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HornOfPlenty;
import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfRecharging;
import com.shatteredpixel.shatteredpixeldungeon.items.trinkets.ShardOfOblivion;
import com.shatteredpixel.shatteredpixeldungeon.items.wands.Wand;
@@ -175,7 +177,7 @@ public enum Talent {
//Cleric T1
SATIATED_SPELLS(160), DETECT_CURSE(161), SEARING_LIGHT(162), SHIELD_OF_LIGHT(163),
//Cleric T2
ENLIGHTENING_MEAL(164), CLERICT2B(165), SUNRAY(166), DIVINE_SENSE(167), CLERICT2E(168),
ENLIGHTENING_MEAL(164), RECALL_GLYPH(165), SUNRAY(166), DIVINE_SENSE(167), CLERICT2E(168),
//Cleric T3
CLERICT3A(169, 3), CLERICT3B(170, 3),
@@ -688,7 +690,7 @@ public enum Talent {
}
}
public static void onScrollUsed( Hero hero, int pos, float factor ){
public static void onScrollUsed( Hero hero, int pos, float factor, Class<?extends Item> cls ){
if (hero.hasTalent(INSCRIBED_POWER)){
// 2/3 empowered wand zaps
Buff.affect(hero, ScrollEmpower.class).reset((int) (factor * (1 + hero.pointsInTalent(INSCRIBED_POWER))));
@@ -698,6 +700,11 @@ public enum Talent {
Buff.affect(hero, Invisibility.class, factor * (1 + 2*hero.pointsInTalent(INSCRIBED_STEALTH)));
Sample.INSTANCE.play( Assets.Sounds.MELD );
}
if (hero.heroClass == HeroClass.CLERIC
&& hero.hasTalent(RECALL_GLYPH)
&& Scroll.class.isAssignableFrom(cls)){
Buff.affect(hero, RecallGlyph.UsedGlyphTracker.class, hero.pointsInTalent(RECALL_GLYPH) == 2 ? 300 : 10).item = cls;
}
}
public static void onUpgradeScrollUsed( Hero hero ){
@@ -915,7 +922,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, CLERICT2B, SUNRAY, DIVINE_SENSE, CLERICT2E);
Collections.addAll(tierTalents, ENLIGHTENING_MEAL, RECALL_GLYPH, SUNRAY, DIVINE_SENSE, CLERICT2E);
break;
}
for (Talent talent : tierTalents){
@@ -41,6 +41,10 @@ public abstract class ClericSpell {
return 1;
}
public boolean canCast( Hero hero ){
return true;
}
public String name(){
return Messages.get(this, "name");
}
@@ -89,6 +93,10 @@ public abstract class ClericSpell {
spells.add(Sunray.INSTANCE);
}
if (cleric.hasTalent(Talent.RECALL_GLYPH)){
spells.add(RecallGlyph.INSTANCE);
}
if (cleric.hasTalent(Talent.DIVINE_SENSE)) {
spells.add(DivineSense.INSTANCE);
}
@@ -0,0 +1,151 @@
/*
* 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 <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.hero.spells;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
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.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.HolyTome;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.Scroll;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTransmutation;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ExoticScroll;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfEnchantment;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.exotic.ScrollOfMetamorphosis;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.Runestone;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfAugmentation;
import com.shatteredpixel.shatteredpixeldungeon.items.stones.StoneOfEnchantment;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.ui.HeroIcon;
import com.watabou.utils.Bundle;
import com.watabou.utils.Reflection;
public class RecallGlyph extends ClericSpell {
public static RecallGlyph INSTANCE = new RecallGlyph();
@Override
public int icon() {
return HeroIcon.RECALL_GLYPH;
}
@Override
public String desc() {
return Messages.get(this, "desc", Dungeon.hero.pointsInTalent(Talent.RECALL_GLYPH) == 2 ? 300 : 10) + "\n\n" + Messages.get(this, "charge_cost", (int)chargeUse(Dungeon.hero));
}
@Override
public void onCast(HolyTome tome, Hero hero) {
if (hero.buff(UsedGlyphTracker.class) == null){
return;
}
Item item = Reflection.newInstance(hero.buff(UsedGlyphTracker.class).item);
item.setCurrent(hero);
//TODO runestones
if (item instanceof Scroll){
((Scroll) item).anonymize();
((Scroll) item).doRead();
}
onSpellCast(tome, hero);
hero.buff(UsedGlyphTracker.class).detach();
}
@Override
public float chargeUse(Hero hero) {
if (hero.buff(UsedGlyphTracker.class) != null){
Class<? extends Item> item = hero.buff(UsedGlyphTracker.class).item;
if (ExoticScroll.class.isAssignableFrom(item)){
if (item == ScrollOfMetamorphosis.class || item == ScrollOfEnchantment.class){
return 6;
} else {
return 3;
}
} else if (Scroll.class.isAssignableFrom(item)){
if (item == ScrollOfTransmutation.class){
return 4;
} else {
return 2;
}
} else if (Runestone.class.isAssignableFrom(item)){
if (item == StoneOfAugmentation.class || item == StoneOfEnchantment.class){
return 2;
} else {
return 1;
}
}
}
return 0;
}
@Override
public boolean canCast(Hero hero) {
return hero.buff(UsedGlyphTracker.class) != null;
}
public static class UsedGlyphTracker extends FlavourBuff {
{
type = buffType.POSITIVE;
}
public Class<?extends Item> item;
@Override
public int icon() {
return BuffIndicator.GLYPH_RECALL;
}
@Override
public float iconFadePercent() {
float duration = Dungeon.hero.pointsInTalent(Talent.RECALL_GLYPH) == 2 ? 300 : 10;
return Math.max(0, (duration - visualcooldown()) / duration);
}
@Override
public String desc() {
return Messages.get(this, "desc", Messages.titleCase(Reflection.newInstance(item).name()), dispTurns());
}
private static String ITEM = "item";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(ITEM, item);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
item = bundle.getClass(ITEM);
}
}
}
@@ -687,6 +687,11 @@ public class Item implements Bundlable {
protected static Hero curUser = null;
protected static Item curItem = null;
public void setCurrent( Hero hero ){
curUser = hero;
curItem = this;
}
protected static CellSelector.Listener thrower = new CellSelector.Listener() {
@Override
public void onSelect( Integer target ) {
@@ -87,7 +87,7 @@ public class HolyTome extends Artifact {
}
public boolean canCast( Hero hero, ClericSpell spell ){
return (charge >= spell.chargeUse(hero));
return (charge >= spell.chargeUse(hero) && spell.canCast(hero));
}
public void spendCharge( float chargesSpent ){
@@ -201,7 +201,7 @@ public abstract class Scroll extends Item {
if (!anonymous) {
Catalog.countUse(getClass());
if (Random.Float() < talentChance) {
Talent.onScrollUsed(curUser, curUser.pos, talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, talentFactor, getClass());
}
}
@@ -261,7 +261,7 @@ public class Alchemize extends Spell {
}
Catalog.countUse(getClass());
if (curItem instanceof Alchemize && Random.Float() < ((Alchemize)curItem).talentChance){
Talent.onScrollUsed(curUser, curUser.pos, ((Alchemize) curItem).talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, ((Alchemize) curItem).talentFactor, curItem.getClass());
}
}
@@ -176,7 +176,7 @@ public class BeaconOfReturning extends Spell {
detach(hero.belongings.backpack);
Catalog.countUse(getClass());
if (Random.Float() < talentChance){
Talent.onScrollUsed(curUser, curUser.pos, talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, talentFactor, getClass());
}
}
@@ -98,7 +98,7 @@ public abstract class InventorySpell extends Spell {
Catalog.countUse(curItem.getClass());
if (Random.Float() < ((Spell) curItem).talentChance) {
Talent.onScrollUsed(curUser, curUser.pos, ((Spell) curItem).talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, ((Spell) curItem).talentFactor, curItem.getClass());
}
}
@@ -78,7 +78,7 @@ public class MagicalInfusion extends InventorySpell {
Catalog.countUse(curItem.getClass());
if (Random.Float() < ((Spell) curItem).talentChance) {
Talent.onScrollUsed(curUser, curUser.pos, ((Spell) curItem).talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, ((Spell) curItem).talentFactor, getClass());
}
}
@@ -123,7 +123,7 @@ public class SummonElemental extends Spell {
detach(Dungeon.hero.belongings.backpack);
Catalog.countUse(getClass());
if (Random.Float() < talentChance){
Talent.onScrollUsed(curUser, curUser.pos, talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, talentFactor, getClass());
}
} else {
@@ -95,7 +95,7 @@ public abstract class TargetedSpell extends Spell {
curUser.spendAndNext( 1f );
Catalog.countUse(curSpell.getClass());
if (Random.Float() < curSpell.talentChance){
Talent.onScrollUsed(curUser, curUser.pos, curSpell.talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, curSpell.talentFactor, curSpell.getClass());
}
}
});
@@ -115,7 +115,7 @@ public class UnstableSpell extends Spell {
Catalog.countUse(getClass());
if (Random.Float() < talentChance){
Talent.onScrollUsed(curUser, curUser.pos, talentFactor);
Talent.onScrollUsed(curUser, curUser.pos, talentFactor, getClass());
}
}
@@ -127,6 +127,7 @@ public class BuffIndicator extends Component {
public static final int SPELL_FOOD = 75;
public static final int LIGHT_SHIELD= 76;
public static final int HOLY_SIGHT = 77;
public static final int GLYPH_RECALL= 78;
public static final int SIZE_SMALL = 7;
public static final int SIZE_LARGE = 16;
@@ -75,6 +75,7 @@ public class HeroIcon extends Image {
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;
//action indicator visuals
public static final int BERSERK = 80;