diff --git a/core/src/main/assets/interfaces/buffs.png b/core/src/main/assets/interfaces/buffs.png index f8cb82a25..8a283f13b 100644 Binary files a/core/src/main/assets/interfaces/buffs.png and b/core/src/main/assets/interfaces/buffs.png differ diff --git a/core/src/main/assets/interfaces/hero_icons.png b/core/src/main/assets/interfaces/hero_icons.png index f5712a5a3..689683d7a 100644 Binary files a/core/src/main/assets/interfaces/hero_icons.png and b/core/src/main/assets/interfaces/hero_icons.png differ diff --git a/core/src/main/assets/interfaces/large_buffs.png b/core/src/main/assets/interfaces/large_buffs.png index ace48dab3..bdfd5ebf9 100644 Binary files a/core/src/main/assets/interfaces/large_buffs.png and b/core/src/main/assets/interfaces/large_buffs.png differ diff --git a/core/src/main/assets/interfaces/talent_icons.png b/core/src/main/assets/interfaces/talent_icons.png index f5cc67c3d..e6fc86a7b 100644 Binary files a/core/src/main/assets/interfaces/talent_icons.png and b/core/src/main/assets/interfaces/talent_icons.png differ diff --git a/core/src/main/assets/messages/actors/actors.properties b/core/src/main/assets/messages/actors/actors.properties index 891295794..9a1c92542 100644 --- a/core/src/main/assets/messages/actors/actors.properties +++ b/core/src/main/assets/messages/actors/actors.properties @@ -457,6 +457,17 @@ actors.hero.abilities.huntress.spirithawk$hawkally.direct_attack=Your hawk moves actors.hero.abilities.huntress.spirithawk$hawkally.desc=A magical hawk, summoned by the Huntress. It glows a bright ethereal blue, its head constantly shifts around as it surveys the area.\n\nWhile it isn't much of a fighter its speed and vision make it excellent for scouting and distracting enemies.\n\nTurns remaining: %d. actors.hero.abilities.huntress.spirithawk$hawkally.desc_dodges=Guaranteed dodges remaining: %d. +actors.hero.abilities.duelist.challenge.name=challenge +actors.hero.abilities.duelist.challenge.prompt=Choose an enemy to challenge +actors.hero.abilities.duelist.challenge.already_dueling=You can only be in one duel at a time. +actors.hero.abilities.duelist.challenge.ally_target=You can only challenge enemies. +actors.hero.abilities.duelist.challenge.unreachable_target=You must be able to reach your target. +actors.hero.abilities.duelist.challenge.distant_target=That enemy is too far away to challenge. +actors.hero.abilities.duelist.challenge.short_desc=The Duelist _challenges_ a nearby enemy to a duel, temporarily freezing all other enemies. +actors.hero.abilities.duelist.challenge.desc=The Duelist challenges a nearby enemy to a duel. That enemy is compelled to fight her while all other enemies are temporarily frozen in time.\n\nThe target must be reachable and within 5 tiles of the Duelist. The duel lasts until 10 turns pass, the enemy dies, or the Duelist is more than 5 tiles away from the enemy.\n\nFrozen enemies are invulnerable. The Duelist's allies are not frozen by this ability, but if a boss is targeted its minions will not be frozen either. +actors.hero.abilities.duelist.challenge$duelparticipant.name=duel participant +actors.hero.abilities.duelist.challenge$duelparticipant.desc=This character is participating in a duel. They, and any of their allies or minions, are allowed to act normally while others are frozen.\n\nThe duel will last for a set amount of time, until one participant dies, or until the participants move more than 5 tiles away from eachother.\n\nTurns remaining: %d. + actors.hero.abilities.ratmogrify.name=ratmogrify actors.hero.abilities.ratmogrify.cant_transform=You can't ratmogrify that! actors.hero.abilities.ratmogrify.too_strong=That enemy is too strong to ratmogrify! @@ -848,7 +859,20 @@ actors.hero.talent.secondary_charge.desc=_+1:_ The Champion's secondary weapon c actors.hero.talent.twin_upgrades.title=twin upgrades actors.hero.talent.twin_upgrades.desc=_+1:_ If the weaker of the Champion's two equipped weapons is _2 or more tiers lower_ than the stronger one, it is boosted to the stronger weapon's level.\n\n_+2:_ If the weaker of the Champion's two equipped weapons is _1 or more tiers lower_ than the stronger one, it is boosted to the stronger weapon's level.\n\n_+3:_ If the weaker of the Champion's two equipped weapons is _the same tier or lower_ than the stronger one, it is boosted to the stronger weapon's level. actors.hero.talent.combined_lethality.title=combined lethality -actors.hero.talent.combined_lethality.desc=_+1:_ If the Champion uses two different weapon abilities successively, the second ability will execute any non-boss enemy _below 8% health_.\n\n_+2:_ If the Champion uses two different weapon abilities successively, the second ability will execute any non-boss enemy _below 17% health_.\n\n_+3:_ If the Champion uses two different weapon abilities successively, the second ability will execute any non-boss enemy _below 25% health_.\n\nIf the second ability does not contain an attack, this talent will instead trigger on the Champion's next attack within 5 turns. +actors.hero.talent.combined_lethality.desc=_+1:_ If the Champion uses two different weapon abilities successively, the second ability will execute any non-boss enemy _below 8% HP_.\n\n_+2:_ If the Champion uses two different weapon abilities successively, the second ability will execute any non-boss enemy _below 17% HP_.\n\n_+3:_ If the Champion uses two different weapon abilities successively, the second ability will execute any non-boss enemy _below 25% HP_.\n\nIf the second ability does not contain an attack, this talent will instead trigger on the Champion's next attack within 5 turns. + +#second subclass + +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.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. + +#second armor ability + +#third armor ability #universal actors.hero.talent.heroic_energy.title=heroic energy diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Actor.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Actor.java index d39028126..5809827e9 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Actor.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Actor.java @@ -357,6 +357,15 @@ public abstract class Actor implements Bundlable { } } } + + //'freezes' a character in time for a specified amount of time + //USE CAREFULLY! Manipulating time like this is useful for some gameplay effects but is tricky + public static void delayChar( Char ch, float time ){ + ch.spendConstant(time); + for (Buff b : ch.buffs()){ + b.spendConstant(time); + } + } public static synchronized Char findChar( int pos ) { for (Char ch : chars){ 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 5b15cb0f7..f83cb00f9 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -70,6 +70,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.duelist.Challenge; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.rogue.DeathMark; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.warrior.Endure; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Elemental; @@ -842,6 +843,10 @@ public abstract class Char extends Actor { } } + if (sprite != null && buff(Challenge.SpectatorFreeze.class) != null){ + return; //can't add buffs while frozen and game is loaded + } + buffs.add( buff ); if (Actor.chars().contains(this)) Actor.add( buff ); @@ -985,7 +990,7 @@ public abstract class Char extends Actor { //similar to isImmune, but only factors in damage. //Is used in AI decision-making public boolean isInvulnerable( Class effect ){ - return false; + return buff(Challenge.SpectatorFreeze.class) != null; } protected HashSet properties = new HashSet<>(); @@ -1004,6 +1009,7 @@ public abstract class Char extends Actor { new HashSet( Arrays.asList(AllyBuff.class, Dread.class) )), MINIBOSS ( new HashSet(), new HashSet( Arrays.asList(AllyBuff.class, Dread.class) )), + BOSS_MINION, UNDEAD, DEMONIC, INORGANIC ( new HashSet(), 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 42037aeb9..aef987ce0 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 @@ -2052,7 +2052,7 @@ public class Hero extends Char { @Override public boolean isInvulnerable(Class effect) { - return buff(AnkhInvulnerability.class) != null; + return super.isInvulnerable(effect) || buff(AnkhInvulnerability.class) != null; } public boolean search( boolean intentional ) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java index ffcdd4694..66f7e1a8c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroClass.java @@ -29,6 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.QuickSlot; import com.shatteredpixel.shatteredpixeldungeon.SPDSettings; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; 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.huntress.SpiritHawk; import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.huntress.SpectralBlades; @@ -254,7 +255,7 @@ public enum HeroClass { case HUNTRESS: return new ArmorAbility[]{new SpectralBlades(), new NaturesPower(), new SpiritHawk()}; case DUELIST: - return new ArmorAbility[]{}; + return new ArmorAbility[]{new Challenge()}; } } 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 f6c5a8669..27bc7bac3 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 @@ -158,7 +158,7 @@ public enum Talent { //Duelist S2 T3 DUELIST_S2_1(142, 3), DUELIST_S2_2(143, 3), DUELIST_S2_3(144, 3), //Duelist A1 T4 - DUELIST_A1_1(145, 4), DUELIST_A1_2(146, 4), DUELIST_A1_3(147, 4), + CLOSE_THE_GAP(145, 4), INVIGORATING_VICTORY(146, 4), ELIMINATION_MATCH(147, 4), //Duelist A2 T4 DUELIST_A2_1(148, 4), DUELIST_A2_2(149, 4), DUELIST_A2_3(150, 4), //Duelist A3 T4 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 new file mode 100644 index 000000000..0d6c02899 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/abilities/duelist/Challenge.java @@ -0,0 +1,250 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2023 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 + */ + +package com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist; + +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.BlobImmunity; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; +import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Doom; +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.actors.hero.abilities.ArmorAbility; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +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.GLog; +import com.watabou.noosa.audio.Sample; +import com.watabou.utils.Bundle; +import com.watabou.utils.PathFinder; + +public class Challenge extends ArmorAbility { + + { + baseChargeUse = 35; + } + + @Override + public int icon() { + return HeroIcon.CHALLENGE; + } + + @Override + public String targetingPrompt() { + return Messages.get(this, "prompt"); + } + + @Override + public int targetedPos(Char user, int dst) { + return dst; + } + + @Override + protected void activate(ClassArmor armor, Hero hero, Integer target) { + if (target == null || !Dungeon.level.heroFOV[target]){ + return; + } + + Char targetCh = Actor.findChar(target); + + if (hero.buff(DuelParticipant.class) != null){ + GLog.w(Messages.get(this, "already_dueling")); + return; + } + + if (targetCh != null){ + + if (targetCh.alignment == hero.alignment){ + GLog.w(Messages.get(this, "ally_target")); + return; + } + + boolean[] passable = Dungeon.level.passable.clone(); + 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){ + GLog.w(Messages.get(this, "unreachable_target")); + return; + } + + if (Dungeon.level.distance(hero.pos, targetCh.pos) >= 5){ + GLog.w(Messages.get(this, "distant_target")); + return; + } + + boolean bossTarget = Char.hasProp(targetCh, Char.Property.BOSS); + for (Char toFreeze : Actor.chars()){ + if (toFreeze != targetCh && toFreeze.alignment != hero.alignment + && (!bossTarget || !(Char.hasProp(targetCh, Char.Property.BOSS) || Char.hasProp(targetCh, Char.Property.BOSS_MINION)))) { + Actor.delayChar(toFreeze, DuelParticipant.DURATION); + Buff.affect(toFreeze, SpectatorFreeze.class, DuelParticipant.DURATION); + } + } + + Buff.affect(targetCh, DuelParticipant.class); + Buff.affect(hero, DuelParticipant.class); + if (targetCh instanceof Mob){ + ((Mob) targetCh).aggro(hero); + } + + GameScene.flash(0x80FFFFFF); + Sample.INSTANCE.play(Assets.Sounds.DESCEND); + + armor.charge -= chargeUse( hero ); + armor.updateQuickslot(); + hero.sprite.zap(target); + + hero.next(); + } + } + + @Override + public Talent[] talents() { + return new Talent[]{Talent.CLOSE_THE_GAP, Talent.INVIGORATING_VICTORY, Talent.ELIMINATION_MATCH, Talent.HEROIC_ENERGY}; + } + + public static class DuelParticipant extends Buff { + + public static float DURATION = 10f; + + private int left = (int)DURATION; + + @Override + public int icon() { + return BuffIndicator.CHALLENGE; + } + + @Override + public float iconFadePercent() { + return Math.max(0, (DURATION - left) / DURATION); + } + + @Override + public String iconTextDisplay() { + return Integer.toString(left); + } + + @Override + public boolean act() { + + left--; + if (left == 0) { + detach(); + } else { + Char other = null; + for (Char ch : Actor.chars()){ + if (ch != target && ch.buff(DuelParticipant.class) != null){ + other = ch; + } + } + + if (other == null){ + detach(); + } else if (Dungeon.level.distance(target.pos, other.pos) > 5){ + detach(); + } + } + + spend(TICK); + return true; + } + + @Override + public void detach() { + super.detach(); + if (!target.isAlive()) { + if (target.alignment != Dungeon.hero.alignment){ + Sample.INSTANCE.play(Assets.Sounds.BOSS); + GameScene.flash(0x80FFFFFF); + } + + for (Char ch : Actor.chars()) { + if (ch.buff(SpectatorFreeze.class) != null) { + ch.buff(SpectatorFreeze.class).detach(); + } + if (ch.buff(DuelParticipant.class) != null && ch != target) { + ch.buff(DuelParticipant.class).detach(); + } + } + } else if (target == Dungeon.hero){ + GameScene.flash(0x80FFFFFF); + } + } + + @Override + public String desc() { + return Messages.get(this, "desc", left); + } + + private static final String LEFT = "left"; + + @Override + public void storeInBundle(Bundle bundle) { + super.storeInBundle(bundle); + bundle.put(LEFT, left); + } + + @Override + public void restoreFromBundle(Bundle bundle) { + super.restoreFromBundle(bundle); + left = bundle.getInt(LEFT); + } + } + + public static class SpectatorFreeze extends FlavourBuff { + + @Override + public void fx(boolean on) { + if (on) { + target.sprite.add(CharSprite.State.DARKENED); + target.sprite.add(CharSprite.State.PARALYSED); + } else { + //allies can't be spectator frozen, so just check doom + if (target.buff(Doom.class) == null) target.sprite.remove(CharSprite.State.DARKENED); + if (target.paralysed == 0) target.sprite.remove(CharSprite.State.PARALYSED); + } + } + + @Override + public void detach(){ + super.detach(); + if (cooldown() > 0) { + Actor.delayChar(target, -cooldown()); + } + } + + { + immunities.addAll(new BlobImmunity().immunities()); + } + + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM300.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM300.java index 618a0afe6..4daa113a4 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM300.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DM300.java @@ -503,7 +503,7 @@ public class DM300 extends Mob { invulnWarned = true; GLog.w(Messages.get(this, "charging_hint")); } - return supercharged; + return supercharged || super.isInvulnerable(effect); } public void supercharge(){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java index 25473d62a..906d1b9d9 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java @@ -438,7 +438,11 @@ public class DwarfKing extends Mob { @Override public boolean isInvulnerable(Class effect) { - return phase == 2 && effect != KingDamager.class; + if (effect == KingDamager.class){ + return false; + } else { + return phase == 2 || super.isInvulnerable(effect); + } } @Override @@ -562,6 +566,7 @@ public class DwarfKing extends Mob { public static class DKGhoul extends Ghoul { { + properties.add(Property.BOSS_MINION); state = HUNTING; } @@ -574,12 +579,14 @@ public class DwarfKing extends Mob { public static class DKMonk extends Monk { { + properties.add(Property.BOSS_MINION); state = HUNTING; } } public static class DKWarlock extends Warlock { { + properties.add(Property.BOSS_MINION); state = HUNTING; } @@ -594,6 +601,7 @@ public class DwarfKing extends Mob { public static class DKGolem extends Golem { { + properties.add(Property.BOSS_MINION); state = HUNTING; } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java index b510b9e37..8888f5b8b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java @@ -28,6 +28,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.SacrificialFire; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AllyBuff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Buff; import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.ChampionEnemy; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.duelist.Challenge; import com.shatteredpixel.shatteredpixeldungeon.effects.Pushing; import com.shatteredpixel.shatteredpixeldungeon.items.Gold; import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm; @@ -261,7 +262,10 @@ public class Ghoul extends Mob { return true; } - turnsToRevive--; + //have to delay this manually here are a downed ghouls can't be directly frozen otherwise + if (target.buff(Challenge.DuelParticipant.class) == null) { + turnsToRevive--; + } if (turnsToRevive <= 0){ if (Actor.findChar( ghoul.pos ) != null) { ArrayList candidates = new ArrayList<>(); @@ -345,7 +349,10 @@ public class Ghoul extends Mob { public static Ghoul searchForHost(Ghoul dieing){ for (Char ch : Actor.chars()){ - if (ch != dieing && ch instanceof Ghoul && ch.alignment == dieing.alignment){ + //don't count hero ally ghouls or duel frozen ghouls + if (ch != dieing && ch instanceof Ghoul + && ch.alignment == dieing.alignment + && ch.buff(Challenge.SpectatorFreeze.class) == null){ if (ch.fieldOfView == null){ ch.fieldOfView = new boolean[Dungeon.level.length()]; Dungeon.level.updateFieldOfView( ch, ch.fieldOfView ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Pylon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Pylon.java index 3b1600112..4b5f5f75b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Pylon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Pylon.java @@ -63,6 +63,7 @@ public class Pylon extends Mob { maxLvl = -2; properties.add(Property.MINIBOSS); + properties.add(Property.BOSS_MINION); properties.add(Property.INORGANIC); properties.add(Property.ELECTRIC); properties.add(Property.IMMOVABLE); @@ -178,7 +179,7 @@ public class Pylon extends Mob { @Override public boolean isInvulnerable(Class effect) { //immune to damage when inactive - return (alignment == Alignment.NEUTRAL); + return alignment == Alignment.NEUTRAL || super.isInvulnerable(effect); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java index db9fc681e..5272da8dc 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java @@ -368,7 +368,7 @@ public class YogDzewa extends Mob { @Override public boolean isInvulnerable(Class effect) { - return phase == 0 || findFist() != null; + return phase == 0 || findFist() != null || super.isInvulnerable(effect); } @Override @@ -629,6 +629,7 @@ public class YogDzewa extends Mob { maxLvl = -2; properties.add(Property.DEMONIC); + properties.add(Property.BOSS_MINION); } @Override @@ -649,15 +650,22 @@ public class YogDzewa extends Mob { } //used so death to yog's ripper demons have their own rankings description - public static class YogRipper extends RipperDemon {} + public static class YogRipper extends RipperDemon { + { + maxLvl = -2; + properties.add(Property.BOSS_MINION); + } + } public static class YogEye extends Eye { { maxLvl = -2; + properties.add(Property.BOSS_MINION); } } public static class YogScorpio extends Scorpio { { maxLvl = -2; + properties.add(Property.BOSS_MINION); } } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java index 47c4dbe53..569cd20e2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogFist.java @@ -116,7 +116,7 @@ public abstract class YogFist extends Mob { invulnWarned = true; GLog.w(Messages.get(this, "invuln_warn")); } - return isNearYog(); + return isNearYog() || super.isInvulnerable(effect); } @Override diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java index d6f83ceef..2b20c139a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/BuffIndicator.java @@ -118,6 +118,7 @@ public class BuffIndicator extends Component { public static final int DUEL_DANCE = 64; public static final int DUEL_BRAWL = 65; public static final int DUEL_XBOW = 66; + public static final int CHALLENGE = 67; public static final int SIZE_SMALL = 7; public static final int SIZE_LARGE = 16; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java index 0a5677149..70274adcb 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ui/HeroIcon.java @@ -61,7 +61,10 @@ public class HeroIcon extends Image { public static final int SPECTRAL_BLADES = 25; public static final int NATURES_POWER = 26; public static final int SPIRIT_HAWK = 27; - public static final int RATMOGRIFY = 28; + public static final int CHALLENGE = 28; + public static final int DUELIST_2 = 29; + public static final int DUELIST_3 = 30; + public static final int RATMOGRIFY = 33; public HeroIcon(HeroSubClass subCls){ super( Assets.Interfaces.HERO_ICONS );