v3.1.0: reworked Warrior's shielding ability, no longer purely passive

This commit is contained in:
Evan Debenham
2025-04-03 12:52:04 -04:00
parent 3b7356a4d3
commit e1d677845f
6 changed files with 132 additions and 13 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -92,7 +92,7 @@ items.armor.armor.cursed_worn=Because this armor is cursed, you are powerless to
items.armor.armor.cursed=You can feel a malevolent magic lurking within this armor.
items.armor.armor.weak_cursed=Despite the curse, you are able to unequip this armor.
items.armor.armor.not_cursed=This armor is free of malevolent magic.
items.armor.armor.seal_attached=The Warrior's broken seal is attached to this armor, it is providing him up to _%d shielding_.
items.armor.armor.seal_attached=The Warrior's broken seal is attached to this armor. When he about to be injured below half health, it will grant him _%d shielding_.
items.armor.armor$glyph.glyph=glyph
items.armor.armor$glyph.killed=%s killed you...
items.armor.armor$glyph.rankings_desc=Killed by a glyph
@@ -2260,11 +2260,14 @@ items.brokenseal.prompt=Select an armor
items.brokenseal.unknown_armor=You must identify that armor first.
items.brokenseal.cursed_armor=The seal won't apply to cursed armor.
items.brokenseal.affix=You affix the seal to your armor!
items.brokenseal.desc=A wax seal, affixed to armor as a symbol of valor. All the markings on the seal have worn off with age and it is broken in half down the middle.\n\nA memento from his home, the seal helps the warrior persevere. While wearing the seal the warrior will slowly generate shielding on top of his health based on the quality of his armor.\n\nThe seal can be _affixed to armor,_ and moved between armors. It can carry a single upgrade with it, so long as that upgrade was applied to the armor while the seal was attached to it.
items.brokenseal.desc=A wax seal, affixed to armor as a symbol of valor. All the markings on the seal have worn off with age and it is broken in half down the middle.\n\nA memento from his home, the seal helps the warrior persevere. While wearing the seal the warrior will instantly gain shielding when he is about to be damaged to half health or lower.\n\nThe seal can be _affixed to armor,_ and moved between armors. It can carry a single upgrade with it, so long as that upgrade was applied to the armor while the seal was attached to it.
items.brokenseal.inscribed=The seal is inscribed with a _%s._
items.brokenseal.choose_title=Choose a Glyph
items.brokenseal.choose_desc=Both this armor and the broken seal are carrying a glyph. Pick which glyph should be kept.\n\nNote that if you pick the glyph that is currently on the armor, the seal will not be able to transfer it later.
items.brokenseal.discover_hint=One of the heroes starts with this item.
items.brokenseal$warriorshield.name=Warrior Shield
items.brokenseal$warriorshield.desc_active=The Warrior's broken seal is currently helping him persevere, granting him shielding on top of his health. There is a 100 turn cooldown after the shielding initially triggers before it can be used again.\n\nThis shield does not decay over time, but will end if no enemies are nearby for a few turns. When it ends, any unused shielding will reduce the cooldown, up to a max of 50%%.\n\nShield remaining: %1$d.\n\nCurrent Cooldown: %2$d.
items.brokenseal$warriorshield.desc_cooldown=The Warrior has recently gained shielding from his broken seal, and must wait until he can benefit from its shielding effect again.\n\nTurns Remaining: %d.
items.dewdrop.name=dewdrop
items.dewdrop.already_full=You already have full health.

View File

@@ -99,6 +99,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.MirrorImage;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.PrismaticImage;
import com.shatteredpixel.shatteredpixeldungeon.effects.FloatingText;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.BrokenSeal;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.Armor;
import com.shatteredpixel.shatteredpixeldungeon.items.armor.curses.Bulk;
@@ -903,6 +904,17 @@ public abstract class Char extends Actor {
buff( Paralysis.class ).processDamage(dmg);
}
BrokenSeal.WarriorShield shield = buff(BrokenSeal.WarriorShield.class);
if (!(src instanceof Hunger)
&& dmg > 0
//either HP is already half or below (ignoring shield)
// or the hit will reduce it to half or below
&& (HP <= HT/2 || HP + shielding() - dmg <= HT/2)
&& shield != null && !shield.coolingDown()){
sprite.showStatusWithIcon(CharSprite.POSITIVE, Integer.toString(buff(BrokenSeal.WarriorShield.class).maxShield()), FloatingText.SHIELDING);
shield.activate();
}
int shielded = dmg;
dmg = ShieldBuff.processDamage(this, dmg, src);
shielded -= dmg;

View File

@@ -23,7 +23,6 @@ package com.shatteredpixel.shatteredpixeldungeon.items;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Regeneration;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ShieldBuff;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Belongings;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero;
@@ -35,12 +34,15 @@ import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSprite;
import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndBag;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions;
import com.shatteredpixel.shatteredpixeldungeon.windows.WndUseItem;
import com.watabou.noosa.Image;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle;
import com.watabou.utils.GameMath;
import java.util.ArrayList;
import java.util.Arrays;
@@ -88,7 +90,8 @@ public class BrokenSeal extends Item {
}
public int maxShield( int armTier, int armLvl ){
return armTier + armLvl + Dungeon.hero.pointsInTalent(Talent.IRON_WILL);
// 5-15, based on equip tier and iron will
return 3 + 2*armTier + Dungeon.hero.pointsInTalent(Talent.IRON_WILL);
}
@Override
@@ -213,35 +216,112 @@ public class BrokenSeal extends Item {
public static class WarriorShield extends ShieldBuff {
{
type = buffType.POSITIVE;
detachesAtZero = false;
shieldUsePriority = 2;
}
private Armor armor;
private float partialShield;
private int cooldown = 0;
private int turnsSinceEnemies = 0;
private static int COOLDOWN_START = 100;
@Override
public int icon() {
if (coolingDown() || shielding() > 0){
return BuffIndicator.SEAL_SHIELD;
} else {
return BuffIndicator.NONE;
}
}
@Override
public void tintIcon(Image icon) {
if (coolingDown() && shielding() == 0){
icon.brightness(0.3f);
} else {
icon.resetColor();
}
}
@Override
public float iconFadePercent() {
if (shielding() > 0){
return GameMath.gate(0, 1f - shielding()/(float)maxShield(), 1);
} else if (coolingDown()){
return GameMath.gate(0, cooldown / (float)COOLDOWN_START, 1);
} else {
return 0;
}
}
@Override
public String iconTextDisplay() {
if (shielding() > 0){
return Integer.toString(shielding());
} else if (coolingDown()){
return Integer.toString(cooldown);
} else {
return "";
}
}
@Override
public String desc() {
if (shielding() > 0){
return Messages.get(this, "desc_active", shielding(), cooldown);
} else {
return Messages.get(this, "desc_cooldown", cooldown);
}
}
@Override
public synchronized boolean act() {
if (Regeneration.regenOn() && shielding() < maxShield()) {
partialShield += 1/30f;
if (cooldown > 0){
cooldown--;
}
if (shielding() > 0){
if (Dungeon.hero.visibleEnemies() == 0){
turnsSinceEnemies++;
//TODO
if (turnsSinceEnemies >= 5){
float percentLeft = shielding() / (float)maxShield();
cooldown -= COOLDOWN_START*(percentLeft/2f); //max of 50% cooldown refund
decShield(shielding());
}
} else {
turnsSinceEnemies = 0;
}
}
while (partialShield >= 1){
incShield();
partialShield--;
}
if (shielding() <= 0 && maxShield() <= 0){
if (shielding() <= 0 && maxShield() <= 0 && cooldown == 0){
detach();
}
spend(TICK);
return true;
}
public synchronized void activate() {
setShield(maxShield());
cooldown = COOLDOWN_START;
turnsSinceEnemies = 0;
}
public boolean coolingDown(){
return cooldown > 0;
}
public synchronized void supercharge(int maxShield){
if (maxShield > shielding()){
setShield(maxShield);
}
cooldown = COOLDOWN_START;
turnsSinceEnemies = 0;
}
public synchronized void setArmor(Armor arm){
@@ -260,5 +340,28 @@ public class BrokenSeal extends Item {
return 0;
}
}
public static final String COOLDOWN = "cooldown";
public static final String TURNS_SINCE_ENEMIES = "turns_since_enemies";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(COOLDOWN, cooldown);
bundle.put(TURNS_SINCE_ENEMIES, turnsSinceEnemies);
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
if (bundle.contains(COOLDOWN)) {
cooldown = bundle.getInt(COOLDOWN);
turnsSinceEnemies = bundle.getInt(TURNS_SINCE_ENEMIES);
} else {
//TODO what about berserker runs in progress?
// we could potentially screw someone who had a big shield prior to v3.1
setShield(0); //clears old pre-v3.1 shield
}
}
}
}

View File

@@ -133,6 +133,7 @@ public class BuffIndicator extends Component {
public static final int ILLUMINATED = 81;
public static final int TRINITY_FORM= 82;
public static final int MANY_POWER = 83;
public static final int SEAL_SHIELD = 84;
public static final int SIZE_SMALL = 7;
public static final int SIZE_LARGE = 16;