From 886bd3d0a89f7eb295c568b9192656ae52fe7443 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Wed, 19 May 2021 21:30:26 -0400 Subject: [PATCH] v0.9.3: implemented the death mark ability --- .../assets/messages/actors/actors.properties | 15 +- .../shatteredpixeldungeon/actors/Char.java | 30 ++++ .../actors/hero/Talent.java | 4 +- .../hero/abilities/rogue/DeathMark.java | 129 +++++++++++++++++- .../actors/mobs/Brute.java | 2 +- .../actors/mobs/NewDM300.java | 2 +- .../actors/mobs/Tengu.java | 2 +- 7 files changed, 169 insertions(+), 15 deletions(-) diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 1f90489c4..2e7263849 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -363,8 +363,11 @@ actors.hero.abilities.rogue.smokebomb.prompt=Choose a location to jump to actors.hero.abilities.rogue.smokebomb.short_desc=The Rogue throws down a _Smoke Bomb_ while blinking away. He becomes temporarily invisible and blinds enemies near his old location. actors.hero.abilities.rogue.smokebomb.desc=TODO actors.hero.abilities.rogue.deathmark.name=death mark +actors.hero.abilities.rogue.deathmark.ally_target=You can only mark enemies actors.hero.abilities.rogue.deathmark.short_desc=The Rogue places a _Death Mark_ on a chosen enemy. Marked enemies take bonus damage, but cannot die until the mark ends. actors.hero.abilities.rogue.deathmark.desc=TODO +actors.hero.abilities.rogue.deathmark$deathmarktracker.name=Marked for Death +actors.hero.abilities.rogue.deathmark$deathmarktracker.desc=TODO\n\nTurns left: %s. actors.hero.abilities.rogue.shadowclone.name=shadow clone actors.hero.abilities.rogue.shadowclone.short_desc=The Rogue summons a _Shadow Clone_, which is frail, but can be directed and deals damage based on his weapon. actors.hero.abilities.rogue.shadowclone.desc=TODO @@ -621,12 +624,12 @@ actors.hero.talent.body_replacement.desc=_+1:_ After blinking, the Rogue leaves actors.hero.talent.shadow_step.title=shadow step actors.hero.talent.shadow_step.desc=_+1:_ If the rogue uses smoke bomb while invisible, it occurs instantly and has a _24% reduced_ charge cost, but it also doesn't blind enemies or trigger other talents.\n\n_+2:_ If the rogue uses smoke bomb while invisible, it occurs instantly and has a _42% reduced_ charge cost, but it also doesn't blind enemies or trigger other talents.\n\n_+3:_ If the rogue uses smoke bomb while invisible, it occurs instantly and has a _56% reduced_ charge cost, but it also doesn't blind enemies or trigger other talents.\n\n_+4:_ If the rogue uses smoke bomb while invisible, it occurs instantly and has a _67% reduced_ charge cost, but it also doesn't blind enemies or trigger other talents. -actors.hero.talent.rogue_2_1.title=TODO NAME -actors.hero.talent.rogue_2_1.desc=TODO DESC -actors.hero.talent.rogue_2_2.title=TODO NAME -actors.hero.talent.rogue_2_2.desc=TODO DESC -actors.hero.talent.rogue_2_3.title=TODO NAME -actors.hero.talent.rogue_2_3.desc=TODO DESC +actors.hero.talent.fear_the_reaper.title=fear the reaper +actors.hero.talent.fear_the_reaper.desc=_+1:_ When a death marked enemy reaches 0 hp, they become _crippled_.\n\n_+2:_ When a death marked enemy reaches 0 hp, they become _feared and crippled_.\n\n_+3:_ When a death marked enemy reaches 0 hp, they become _feared and crippled_ and enemies within 3 tiles become _crippled_.\n\n_+4:_ When a death marked enemy reaches 0 hp, they become _feared and crippled_ and enemies within 3 tiles become _feared and crippled_. +actors.hero.talent.deathly_durability.title=deathly durability +actors.hero.talent.deathly_durability.desc=_+1:_ Enemies killed by death mark give the rogue shielding equal to _10%_ of their hp when they were marked.\n\n_+2:_ Enemies killed by death mark give the rogue shielding equal to _20%_ of their hp when they were marked.\n\n_+3:_ Enemies killed by death mark give the rogue shielding equal to _30%_ of their hp when they were marked.\n\n_+4:_ Enemies killed by death mark give the rogue shielding equal to _40%_ of their hp when they were marked. +actors.hero.talent.double_mark.title=double mark +actors.hero.talent.double_mark.desc=_+1:_ Marking a second target at the same time as the first one has a _24% reduced_ charge cost.\n\n_+2:_ Marking a second target at the same time as the first one has a _42% reduced_ charge cost.\n\n_+3:_ Marking a second target at the same time as the first one has a _56% reduced_ charge cost.\n\n_+4:_ Marking a second target at the same time as the first one has a _67% reduced_ charge cost. actors.hero.talent.rogue_3_1.title=TODO NAME actors.hero.talent.rogue_3_1.desc=TODO DESC 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 e9636fac1..362673185 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -63,6 +63,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Weakness; 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.rogue.DeathMark; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Elemental; import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.items.armor.glyphs.AntiMagic; @@ -96,6 +97,7 @@ import com.watabou.utils.Bundle; import com.watabou.utils.PathFinder; import com.watabou.utils.Random; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -546,6 +548,9 @@ public abstract class Char extends Actor { if (this.buff(Doom.class) != null && !isImmune(Doom.class)){ dmg *= 2; } + if (alignment != Alignment.ALLY && this.buff(DeathMark.DeathMarkTracker.class) != null){ + dmg *= 1.25f; + } Class srcClass = src.getClass(); if (isImmune( srcClass )) { @@ -586,6 +591,28 @@ public abstract class Char extends Actor { if (!isAlive()) { die( src ); + } else if (HP == 0 && buff(DeathMark.DeathMarkTracker.class) != null){ + if (Dungeon.hero.hasTalent(Talent.FEAR_THE_REAPER)) { + if (Dungeon.hero.pointsInTalent(Talent.FEAR_THE_REAPER) >= 2) { + Buff.prolong(this, Terror.class, 5f).target = Dungeon.hero; + } + Buff.prolong(this, Cripple.class, 5f); + + if (Dungeon.hero.pointsInTalent(Talent.FEAR_THE_REAPER) >= 3) { + boolean[] passable = BArray.not(Dungeon.level.solid, null); + PathFinder.buildDistanceMap(pos, passable, 3); + + for (Char ch : Actor.chars()) { + if (ch != this && ch.alignment == Alignment.ENEMY + && PathFinder.distance[ch.pos] != Integer.MAX_VALUE) { + if (Dungeon.hero.pointsInTalent(Talent.FEAR_THE_REAPER) == 4) { + Buff.prolong(ch, Terror.class, 5f).target = Dungeon.hero; + } + Buff.prolong(ch, Cripple.class, 5f); + } + } + } + } } } @@ -612,6 +639,9 @@ public abstract class Char extends Actor { } public boolean isAlive() { + if (buff(DeathMark.DeathMarkTracker.class) != null){ + return true; + } return HP > 0; } 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 439142f7f..52510df8f 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 @@ -117,8 +117,8 @@ public enum Talent { EVASIVE_ARMOR(78, 3), PROJECTILE_MOMENTUM(79, 3), SPEEDY_STEALTH(80, 3), //Smoke Bomb T4 HASTY_RETREAT(81, 4), BODY_REPLACEMENT(82, 4), SHADOW_STEP(83, 4), - //??? T4 - ROGUE_2_1(84, 4), ROGUE_2_2(85, 4), ROGUE_2_3(86, 4), + //Death Mark T4 + FEAR_THE_REAPER(84, 4), DEATHLY_DURABILITY(85, 4), DOUBLE_MARK(86, 4), //??? T4 ROGUE_3_1(87, 4), ROGUE_3_2(88, 4), ROGUE_3_3(89, 4), diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/rogue/DeathMark.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/rogue/DeathMark.java index d883cb3cb..4b3b73eac 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/rogue/DeathMark.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/rogue/DeathMark.java @@ -21,20 +21,141 @@ package com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.rogue; +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.buffs.FlavourBuff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Invisibility; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Talent; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.ArmorAbility; import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClassArmor; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; +import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Bundle; public class DeathMark extends ArmorAbility { @Override - protected void activate(ClassArmor armor, Hero hero, Integer target) { - //TODO + protected String targetingPrompt() { + return Messages.get(this, "prompt"); } @Override - public Talent[] talents() { - return new Talent[]{Talent.ROGUE_2_1, Talent.ROGUE_2_2, Talent.ROGUE_2_3, Talent.HEROIC_ENERGY}; + public float chargeUse( Hero hero ) { + float chargeUse = super.chargeUse(hero); + if (hero.buff(DoubleMarkTracker.class) != null){ + //reduced charge use by 24%/42%/56%/67% + chargeUse *= Math.pow(0.76, hero.pointsInTalent(Talent.DOUBLE_MARK)); + } + return chargeUse; } + + @Override + protected void activate(ClassArmor armor, Hero hero, Integer target) { + if (target == null){ + return; + } + + Char ch = Actor.findChar(target); + + if (ch == null){ + GLog.w(Messages.get(this, "no_target")); + } else if (ch.alignment != Char.Alignment.ENEMY){ + GLog.w(Messages.get(this, "ally_target")); + } + + if (ch != null){ + Buff.affect(ch, DeathMarkTracker.class, 5f); + ch.buff(DeathMarkTracker.class).setInitialHP(ch.HP); + } + + armor.charge -= chargeUse( hero ); + armor.updateQuickslot(); + hero.sprite.zap(target); + + Invisibility.dispel(); + hero.next(); + + if (hero.buff(DoubleMarkTracker.class) != null){ + hero.buff(DoubleMarkTracker.class).detach(); + } else if (hero.hasTalent(Talent.DOUBLE_MARK)) { + Buff.affect(hero, DoubleMarkTracker.class, 0.01f); + } + + } + + public static class DoubleMarkTracker extends FlavourBuff{}; + + @Override + public Talent[] talents() { + return new Talent[]{Talent.FEAR_THE_REAPER, Talent.DEATHLY_DURABILITY, Talent.DOUBLE_MARK, Talent.HEROIC_ENERGY}; + } + + public static class DeathMarkTracker extends FlavourBuff { + + int initialHP = 0; + + { + type = buffType.NEGATIVE; + announced = true; + } + + @Override + public int icon() { + return BuffIndicator.MARK; + } + + @Override + public String toString() { + return Messages.get(this, "name"); + } + + @Override + public String desc() { + //TODO show initial HP here? + return Messages.get(this, "desc", dispTurns(visualcooldown())); + } + + private void setInitialHP( int hp ){ + if (initialHP < hp){ + initialHP = hp; + } + } + + @Override + public void detach() { + super.detach(); + if (!target.isAlive()){ + target.sprite.flash(); + target.sprite.bloodBurstA(target.sprite.center(), target.HT*2); + Sample.INSTANCE.play(Assets.Sounds.HIT_STRONG); + target.die(this); + int shld = Math.round(initialHP * (0.1f*Dungeon.hero.pointsInTalent(Talent.DEATHLY_DURABILITY))); + if (shld > 0 && target.alignment != Char.Alignment.ALLY){ + Buff.affect(Dungeon.hero, Barrier.class).setShield(shld); + } + } + } + + private static String INITIAL_HP = "initial_hp"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(INITIAL_HP, initialHP); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + initialHP = bundle.getInt(INITIAL_HP); + } + } + } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Brute.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Brute.java index 038454915..5fe7f479d 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Brute.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Brute.java @@ -80,7 +80,7 @@ public class Brute extends Mob { @Override public synchronized boolean isAlive() { - if (HP > 0){ + if (super.isAlive()){ return true; } else { if (!hasRaged){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java index 532fca944..410ac87f4 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java @@ -511,7 +511,7 @@ public class NewDM300 extends Mob { @Override public boolean isAlive() { - return HP > 0 || pylonsActivated < totalPylonsToActivate(); + return super.isAlive() || pylonsActivated < totalPylonsToActivate(); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Tengu.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Tengu.java index 4b7d90231..6f5bdf5fa 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Tengu.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Tengu.java @@ -194,7 +194,7 @@ public class Tengu extends Mob { @Override public boolean isAlive() { - return HP > 0 || Dungeon.level.mobs.contains(this); //Tengu has special death rules, see prisonbosslevel.progress() + return super.isAlive() || Dungeon.level.mobs.contains(this); //Tengu has special death rules, see prisonbosslevel.progress() } @Override