diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 9a1c92542..629190915 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -866,9 +866,9 @@ actors.hero.talent.combined_lethality.desc=_+1:_ If the Champion uses two differ actors.hero.talent.close_the_gap.title=close the gap actors.hero.talent.close_the_gap.desc=_+1:_ The Duelist blinks _up to two tiles_ toward her target when starting a duel.\n\n_+2:_ The Duelist blinks _up to three tiles_ toward her target when starting a duel.\n\n_+3:_ The Duelist blinks _up to four tiles_ toward her target when starting a duel.\n\n_+4:_ The Duelist blinks _up to five tiles_ toward her target when starting a duel.\n\nThis blink can go through enemies and hazards, and is taken into account when determining if an enemy is in range to be challenged. actors.hero.talent.invigorating_victory.title=invigorating victory -actors.hero.talent.invigorating_victory.desc=_+1:_ If the Duelist defeats her target before the duel ends, she heals for _26% of the damage_ she took during the duel.\n\n_+2:_ If the Duelist defeats her target before the duel ends, she heals for _45% of the damage_ she took during the duel.\n\n_+3:_ If the Duelist defeats her target before the duel ends, she heals for _60% of the damage_ she took during the duel.\n\n_+4:_ If the Duelist defeats her target before the duel ends, she heals for _70% of the damage_ she took during the duel. +actors.hero.talent.invigorating_victory.desc=_+1:_ If the Duelist defeats her target before the duel ends, she heals for _30% of the damage_ she took during the duel.\n\n_+2:_ If the Duelist defeats her target before the duel ends, she heals for _50% of the damage_ she took during the duel.\n\n_+3:_ If the Duelist defeats her target before the duel ends, she heals for _65% of the damage_ she took during the duel.\n\n_+4:_ If the Duelist defeats her target before the duel ends, she heals for _75% of the damage_ she took during the duel. actors.hero.talent.elimination_match.title=elimination match -actors.hero.talent.elimination_match.desc=_+1:_ The Duelist can immediately challenge another enemy after a duel ends at a _20% reduced_ charge cost.\n\n_+2:_ The Duelist can immediately challenge another enemy after a duel ends at a _36% reduced_ charge cost.\n\n_+3:_ The Duelist can immediately challenge another enemy after a duel ends at a _50% reduced_ charge cost.\n\n_+4:_ The Duelist can immediately challenge another enemy after a duel ends at a _60% reduced_ charge cost. +actors.hero.talent.elimination_match.desc=_+1:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _20% reduced_ charge cost.\n\n_+2:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _36% reduced_ charge cost.\n\n_+3:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _50% reduced_ charge cost.\n\n_+4:_ If the Duelist challenges again within 3 turns of a duel ending, that challenge has a _60% reduced_ charge cost. #second armor ability diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java index aef987ce0..fbcf74eb2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java @@ -60,6 +60,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Regeneration; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.SnipersMark; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Vertigo; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.ArmorAbility; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.Challenge; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.NaturesPower; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.warrior.Endure; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mimic; @@ -1361,6 +1362,10 @@ public class Hero extends Char { if (effectiveDamage <= 0) return; + if (buff(Challenge.DuelParticipant.class) != null){ + buff(Challenge.DuelParticipant.class).addDamage(effectiveDamage); + } + //flash red when hit for serious damage. float percentDMG = effectiveDamage / (float)preHP; //percent of current HP that was taken float percentHP = 1 - ((HT - postHP) / (float)HT); //percent health after damage was taken diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/Challenge.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/Challenge.java index 0d6c02899..3bfa7714e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/Challenge.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/Challenge.java @@ -33,12 +33,16 @@ 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.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter; +import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; +import com.shatteredpixel.shatteredpixeldungeon.items.Dewdrop; import com.shatteredpixel.shatteredpixeldungeon.items.armor.ClassArmor; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite; import com.shatteredpixel.shatteredpixeldungeon.ui.BuffIndicator; import com.shatteredpixel.shatteredpixeldungeon.ui.HeroIcon; +import com.shatteredpixel.shatteredpixeldungeon.utils.BArray; import com.shatteredpixel.shatteredpixeldungeon.utils.GLog; import com.watabou.noosa.audio.Sample; import com.watabou.utils.Bundle; @@ -65,6 +69,16 @@ public class Challenge extends ArmorAbility { return dst; } + @Override + public float chargeUse( Hero hero ) { + float chargeUse = super.chargeUse(hero); + if (hero.buff(EliminationMatchTracker.class) != null){ + //reduced charge use by 20%/36%/50%/60% + chargeUse *= Math.pow(0.795, hero.pointsInTalent(Talent.ELIMINATION_MATCH)); + } + return chargeUse; + } + @Override protected void activate(ClassArmor armor, Hero hero, Integer target) { if (target == null || !Dungeon.level.heroFOV[target]){ @@ -89,18 +103,55 @@ public class Challenge extends ArmorAbility { for (Char c : Actor.chars()) { if (c != hero) passable[c.pos] = false; } - PathFinder.buildDistanceMap(targetCh.pos, passable); - if (PathFinder.distance[hero.pos] == Integer.MAX_VALUE){ + int[] reachable = PathFinder.distance.clone(); + + int blinkpos = hero.pos; + if (hero.hasTalent(Talent.CLOSE_THE_GAP)){ + + int blinkrange = 1 + hero.pointsInTalent(Talent.CLOSE_THE_GAP); + PathFinder.buildDistanceMap(hero.pos, BArray.not(Dungeon.level.solid,null), blinkrange); + + for (int i = 0; i < PathFinder.distance.length; i++){ + if (PathFinder.distance[i] == Integer.MAX_VALUE + || reachable[i] == Integer.MAX_VALUE + || i == targetCh.pos){ + continue; + } + + if (Dungeon.level.distance(i, targetCh.pos) < Dungeon.level.distance(blinkpos, targetCh.pos)){ + blinkpos = i; + } else if (Dungeon.level.distance(i, targetCh.pos) == Dungeon.level.distance(blinkpos, targetCh.pos)){ + if (Dungeon.level.trueDistance(i, hero.pos) < Dungeon.level.trueDistance(blinkpos, hero.pos)){ + blinkpos = i; + } + } + } + } + + if (PathFinder.distance[blinkpos] == Integer.MAX_VALUE){ GLog.w(Messages.get(this, "unreachable_target")); return; } - if (Dungeon.level.distance(hero.pos, targetCh.pos) >= 5){ + if (Dungeon.level.distance(blinkpos, targetCh.pos) >= 5){ GLog.w(Messages.get(this, "distant_target")); return; } + if (blinkpos != hero.pos){ + Dungeon.hero.pos = blinkpos; + Dungeon.level.occupyCell(Dungeon.hero); + //prevents the hero from being interrupted by seeing new enemies + Dungeon.observe(); + GameScene.updateFog(); + Dungeon.hero.checkVisibleMobs(); + + Dungeon.hero.sprite.place( Dungeon.hero.pos ); + CellEmitter.get( Dungeon.hero.pos ).burst( Speck.factory( Speck.WOOL ), 6 ); + Sample.INSTANCE.play( Assets.Sounds.PUFF ); + } + boolean bossTarget = Char.hasProp(targetCh, Char.Property.BOSS); for (Char toFreeze : Actor.chars()){ if (toFreeze != targetCh && toFreeze.alignment != hero.alignment @@ -124,6 +175,10 @@ public class Challenge extends ArmorAbility { hero.sprite.zap(target); hero.next(); + + if (hero.buff(EliminationMatchTracker.class) != null){ + hero.buff(EliminationMatchTracker.class).detach(); + } } } @@ -132,11 +187,14 @@ public class Challenge extends ArmorAbility { return new Talent[]{Talent.CLOSE_THE_GAP, Talent.INVIGORATING_VICTORY, Talent.ELIMINATION_MATCH, Talent.HEROIC_ENERGY}; } + public static class EliminationMatchTracker extends FlavourBuff{}; + public static class DuelParticipant extends Buff { public static float DURATION = 10f; private int left = (int)DURATION; + private int takenDmg = 0; @Override public int icon() { @@ -153,6 +211,10 @@ public class Challenge extends ArmorAbility { return Integer.toString(left); } + public void addDamage(int dmg){ + takenDmg += dmg; + } + @Override public boolean act() { @@ -185,6 +247,24 @@ public class Challenge extends ArmorAbility { if (target.alignment != Dungeon.hero.alignment){ Sample.INSTANCE.play(Assets.Sounds.BOSS); GameScene.flash(0x80FFFFFF); + + if (Dungeon.hero.hasTalent(Talent.INVIGORATING_VICTORY)){ + DuelParticipant heroBuff = Dungeon.hero.buff(DuelParticipant.class); + + int hpToHeal = 0; + if (heroBuff != null){ + hpToHeal = heroBuff.takenDmg; + } + + //heals for 30%/50%/65%/75% of taken damage, based on talent points + hpToHeal = (int)Math.round(hpToHeal * (1f - Math.pow(0.707f, Dungeon.hero.pointsInTalent(Talent.INVIGORATING_VICTORY)))); + hpToHeal = Math.min(hpToHeal, Dungeon.hero.HT - Dungeon.hero.HP); + if (hpToHeal > 0){ + Dungeon.hero.HP += hpToHeal; + Dungeon.hero.sprite.emitter().start( Speck.factory( Speck.HEALING ), 0.33f, 6 ); + Dungeon.hero.sprite.showStatus( CharSprite.POSITIVE, Messages.get(Dewdrop.class, "heal", hpToHeal) ); + } + } } for (Char ch : Actor.chars()) { @@ -195,9 +275,13 @@ public class Challenge extends ArmorAbility { ch.buff(DuelParticipant.class).detach(); } } - } else if (target == Dungeon.hero){ + } else if (target != Dungeon.hero){ GameScene.flash(0x80FFFFFF); } + + if (target == Dungeon.hero && Dungeon.hero.hasTalent(Talent.ELIMINATION_MATCH) && target.isAlive()){ + Buff.affect(target, EliminationMatchTracker.class, 3); + } } @Override @@ -206,17 +290,20 @@ public class Challenge extends ArmorAbility { } private static final String LEFT = "left"; + private static final String TAKEN_DMG = "taken_dmg"; @Override public void storeInBundle(Bundle bundle) { super.storeInBundle(bundle); bundle.put(LEFT, left); + bundle.put(TAKEN_DMG, takenDmg); } @Override public void restoreFromBundle(Bundle bundle) { super.restoreFromBundle(bundle); left = bundle.getInt(LEFT); + takenDmg = bundle.getInt(TAKEN_DMG); } }