v0.9.3: implemented the death mark ability

This commit is contained in:
Evan Debenham 2021-05-19 21:30:26 -04:00
parent ff80adeb30
commit 886bd3d0a8
7 changed files with 169 additions and 15 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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),

View File

@ -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);
}
}
}

View File

@ -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){

View File

@ -511,7 +511,7 @@ public class NewDM300 extends Mob {
@Override
public boolean isAlive() {
return HP > 0 || pylonsActivated < totalPylonsToActivate();
return super.isAlive() || pylonsActivated < totalPylonsToActivate();
}
@Override

View File

@ -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