From b5989ee4f66cc0ee2fd0baad186b12faaae90d83 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Fri, 27 Mar 2020 16:11:01 -0400 Subject: [PATCH] v0.8.0: substantially improved logic for damage immunity: - now referred to as invulnerability, with standardized logic for all chars - chars now ignore enemies who are invulnerable - combo no longer builds on invulnerable targets - allies can now target Yog's eye when it is vulnerable --- .../shatteredpixeldungeon/actors/Char.java | 11 ++++++ .../actors/buffs/Combo.java | 5 ++- .../actors/mobs/DwarfKing.java | 11 ++++-- .../actors/mobs/Mob.java | 23 +++++++++--- .../actors/mobs/NewDM300.java | 13 ++++--- .../actors/mobs/YogDzewa.java | 23 +++++++----- .../actors/mobs/YogFist.java | 37 ++++++------------- .../messages/actors/actors.properties | 7 +--- 8 files changed, 75 insertions(+), 55 deletions(-) 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 6d616cafc..d6185a14b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java @@ -416,6 +416,11 @@ public abstract class Char extends Actor { return; } + if(isInvulnerable(src.getClass())){ + sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "invulnerable")); + return; + } + if (!(src instanceof LifeLink) && buff(LifeLink.class) != null){ HashSet links = buffs(LifeLink.class); for (LifeLink link : links.toArray(new LifeLink[0])){ @@ -697,6 +702,12 @@ public abstract class Char extends Actor { return false; } + //similar to isImmune, but only factors in damage. + //Is used in AI decision-making + public boolean isInvulnerable( Class effect ){ + return false; + } + protected HashSet properties = new HashSet<>(); public HashSet properties() { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Combo.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Combo.java index 9060a664b..00f358bf3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Combo.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/buffs/Combo.java @@ -73,8 +73,9 @@ public class Combo extends Buff implements ActionIndicator.Action { } public void hit( Char enemy ) { - - count++; + + //doesn't increment combo count if enemy invulnerable + if (!enemy.isInvulnerable(target.getClass())) count++; comboTime = 4f; misses = 0; BuffIndicator.refreshHero(); 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 22b5b6eea..e5c4a45fc 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 @@ -354,10 +354,15 @@ public class DwarfKing extends Mob { } } + @Override + public boolean isInvulnerable(Class effect) { + return phase == 2 && effect != KingDamager.class; + } + @Override public void damage(int dmg, Object src) { - if (phase == 2 && !(src instanceof KingDamager)){ - sprite.showStatus( CharSprite.POSITIVE, Messages.get(this, "immune") ); + if (isInvulnerable(src.getClass())){ + super.damage(dmg, src); return; } else if (phase == 3 && !(src instanceof Viscosity.DeferedDamage)){ Viscosity.DeferedDamage deferred = Buff.affect( this, Viscosity.DeferedDamage.class ); @@ -378,7 +383,7 @@ public class DwarfKing extends Mob { summonCooldown -= dmgTaken/8f; if (HP <= 50) { HP = 50; - sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); + sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "invulnerable")); ScrollOfTeleportation.appear(this, NewCityBossLevel.throne); properties.add(Property.IMMOVABLE); phase = 2; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java index 38988e19a..1135d067a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Mob.java @@ -215,17 +215,21 @@ public abstract class Mob extends Char { //find a new enemy if.. boolean newEnemy = false; //we have no enemy, or the current one is dead/missing - if ( enemy == null || !enemy.isAlive() || !Actor.chars().contains(enemy) || state == WANDERING) + if ( enemy == null || !enemy.isAlive() || !Actor.chars().contains(enemy) || state == WANDERING) { newEnemy = true; //We are an ally, and current enemy is another ally. - else if (alignment == Alignment.ALLY && enemy.alignment == Alignment.ALLY) + } else if (alignment == Alignment.ALLY && enemy.alignment == Alignment.ALLY) { newEnemy = true; //We are amoked and current enemy is the hero - else if (buff( Amok.class ) != null && enemy == Dungeon.hero) + } else if (buff( Amok.class ) != null && enemy == Dungeon.hero) { newEnemy = true; //We are charmed and current enemy is what charmed us - else if (buff(Charm.class) != null && buff(Charm.class).object == enemy.id()) + } else if (buff(Charm.class) != null && buff(Charm.class).object == enemy.id()) { newEnemy = true; + //we aren't amoked and current enemy is invulnerable to us + } else if (buff( Amok.class ) == null && enemy.isInvulnerable(getClass())) { + newEnemy = true; + } if ( newEnemy ) { @@ -284,7 +288,16 @@ public abstract class Mob extends Char { enemies.remove(source); } } - + + //if we are not amoked, remove any enemies which are invulnerable to us. + if (buff(Amok.class) == null) { + for (Char enemy : enemies.toArray(new Char[0])) { + if (enemy.isInvulnerable(getClass())){ + enemies.remove(enemy); + } + } + } + //neutral characters in particular do not choose enemies. if (enemies.isEmpty()){ return null; 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 b4328663a..0ced1d209 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 @@ -407,13 +407,11 @@ public class NewDM300 extends Mob { @Override public void damage(int dmg, Object src) { - if (supercharged){ - sprite.showStatus( CharSprite.POSITIVE, Messages.get(this, "immune") ); + super.damage(dmg, src); + if (isInvulnerable(src.getClass())){ return; } - super.damage(dmg, src); - LockedFloor lock = Dungeon.hero.buff(LockedFloor.class); if (lock != null && !isImmune(src.getClass())) lock.addTime(dmg); @@ -426,6 +424,11 @@ public class NewDM300 extends Mob { } + @Override + public boolean isInvulnerable(Class effect) { + return supercharged; + } + public void supercharge(){ supercharged = true; ((NewCavesBossLevel)Dungeon.level).activatePylon(); @@ -433,7 +436,7 @@ public class NewDM300 extends Mob { spend(3f); yell(Messages.get(this, "charging")); - sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); + sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "invulnerable")); sprite.resetColor(); chargeAnnounced = false; } 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 e17acfbca..1d0e907fe 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 @@ -69,7 +69,8 @@ public class YogDzewa extends Mob { EXP = 50; - state = PASSIVE; + //so that allies can attack it. States are never actually used. + state = HUNTING; properties.add(Property.BOSS); properties.add(Property.IMMOVABLE); @@ -113,8 +114,10 @@ public class YogDzewa extends Mob { @Override protected boolean act() { - if (phase == 0 && Dungeon.hero.viewDistance >= Dungeon.level.distance(pos, Dungeon.hero.pos)){ - Dungeon.observe(); + if (phase == 0){ + if (Dungeon.hero.viewDistance >= Dungeon.level.distance(pos, Dungeon.hero.pos)) { + Dungeon.observe(); + } if (Dungeon.level.heroFOV[pos]) { notice(); } @@ -269,12 +272,12 @@ public class YogDzewa extends Mob { } @Override - public void damage( int dmg, Object src ) { + public boolean isInvulnerable(Class effect) { + return phase == 0 || findFist() != null; + } - if (phase == 0 || findFist() != null){ - sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); - return; - } + @Override + public void damage( int dmg, Object src ) { int preHP = HP; super.damage( dmg, src ); @@ -292,7 +295,7 @@ public class YogDzewa extends Mob { } Dungeon.observe(); GLog.n(Messages.get(this, "darkness")); - sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); + sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "invulnerable")); YogFist fist = (YogFist) Reflection.newInstance(fistSummons.remove(0)); fist.pos = Dungeon.level.exit; @@ -319,7 +322,7 @@ public class YogDzewa extends Mob { } LockedFloor lock = Dungeon.hero.buff(LockedFloor.class); - if (lock != null) lock.addTime(dmg); + if (lock != null) lock.addTime(dmgTaken); } 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 9bf400c1b..04aced32f 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 @@ -91,22 +91,25 @@ public abstract class YogFist extends Mob { } } - boolean immuneWarned = false; + boolean invulnWarned = false; protected boolean isNearYog(){ int yogPos = Dungeon.level.exit + 3*Dungeon.level.width(); return Dungeon.level.distance(pos, yogPos) <= 4; } + @Override + public boolean isInvulnerable(Class effect) { + return isNearYog(); + } + @Override public void damage(int dmg, Object src) { - if (isNearYog()){ - sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); - if (!immuneWarned){ - immuneWarned = true; - GLog.w(Messages.get(this, "immune_hint")); + if (isInvulnerable(src.getClass())){ + if (!invulnWarned){ + invulnWarned = true; + GLog.w(Messages.get(this, "invuln_warn")); } - return; } super.damage(dmg, src); } @@ -344,15 +347,7 @@ public abstract class YogFist extends Mob { @Override public void damage(int dmg, Object src) { - if (isNearYog()){ - sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); - if (!immuneWarned){ - immuneWarned = true; - GLog.w(Messages.get(this, "immune_hint")); - } - return; - } - if (!(src instanceof Bleeding)){ + if (!isInvulnerable(src.getClass()) && !(src instanceof Bleeding)){ Bleeding b = buff(Bleeding.class); if (b == null){ b = new Bleeding(); @@ -406,15 +401,7 @@ public abstract class YogFist extends Mob { @Override public void damage(int dmg, Object src) { - if (isNearYog()){ - sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); - if (!immuneWarned){ - immuneWarned = true; - GLog.w(Messages.get(this, "immune_hint")); - } - return; - } - if (!(src instanceof Viscosity.DeferedDamage)){ + if (!isInvulnerable(src.getClass()) && !(src instanceof Viscosity.DeferedDamage)){ Buff.affect(this, Viscosity.DeferedDamage.class).prolong(dmg); sprite.showStatus( CharSprite.WARNING, Messages.get(Viscosity.class, "deferred", dmg) ); } else{ diff --git a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties index 659fb33ca..cb3d9ae4b 100644 --- a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties +++ b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties @@ -511,7 +511,6 @@ actors.mobs.newdm300.supercharged=SUPERCHARGE COMPLETE, OPERATING AT 200% POWER! actors.mobs.newdm300.charge_lost=POWER GRID DAMAGED, REVERTING TO LOCAL POWER! actors.mobs.newdm300.pylons_destroyed=ALERT, INSTABILITY DETECTED IN POWER GRID! actors.mobs.newdm300.rankings_desc=Crushed by the DM-300 -actors.mobs.newdm300.immune=IMMUNE actors.mobs.newdm300.def_verb=blocked actors.mobs.newdm300.defeated=CRITICAL DAMAGE! ATTEMPTING SHUTDO- actors.mobs.newdm300.desc=The DM-300 is the largest and most powerful 'defense machine' that the dwarves ever built. Such an awesome machine is difficult to manufacture, so the dwarves only ever made a few to guard the entrances to their underground metropolis.\n\nIt is equipped with vents to jet its toxic exhaust fumes and a high power drill that it can use both to attack and disrupt the earth. DM-300 can also connect to an energy grid, further enhancing its power. @@ -524,7 +523,6 @@ actors.mobs.dwarfking.lifelink_1=I need of your essence, slave! actors.mobs.dwarfking.lifelink_2=Bleed for me, slave! actors.mobs.dwarfking.teleport_1=Deal with them, slave! actors.mobs.dwarfking.teleport_2=Keep them busy, slave! -actors.mobs.dwarfking.immune=IMMUNE actors.mobs.dwarfking.wave_1=Enough! Arise my slaves! actors.mobs.dwarfking.wave_2=More! Bleed for your king! actors.mobs.dwarfking.wave_3=Useless! KILL THEM NOW! @@ -747,7 +745,6 @@ actors.mobs.yog$larva.desc=Yog-Dzewa is an Old God, a powerful entity from the r actors.mobs.yogdzewa.name=Yog-Dzewa actors.mobs.yogdzewa.notice=I. SEE. YOU. -actors.mobs.yogdzewa.immune=IMMUNE actors.mobs.yogdzewa.darkness=The darkness pulls in closer... actors.mobs.yogdzewa.hope=YOUR. HOPE. IS. AN. ILLUSION! actors.mobs.yogdzewa.defeated=... @@ -759,8 +756,7 @@ actors.mobs.yogdzewa$larva.rankings_desc=Devoured by Yog-Dzewa actors.mobs.yogdzewa$larva.desc=These tiny spawn of Yog-Dzewa are simple minions that are easy to produce. While individually weak, they are summoned quickly and can become overwhelming in large numbers. actors.mobs.yogdzewa$yogripper.rankings_desc=Devoured by Yog-Dzewa -actors.mobs.yogfist.immune=IMMUNE -actors.mobs.yogfist.immune_hint=The fist is immune while near to the eye! +actors.mobs.yogfist.invuln_warn=The fist is invulnerable while near to the eye! actors.mobs.yogfist.rankings_desc=Devoured by Yog-Dzewa actors.mobs.yogfist.desc=This fist is an aspect of Yog-Dzewa's power. Fists are linked to the power of Yog-Dzewa, and will be protected from all damage when they are close to the eye. actors.mobs.yogfist$burning.name=burning fist @@ -782,3 +778,4 @@ actors.mobs.yogfist$dark.desc=This fist is formed out of pure dark energy. It is actors.char.kill=%s killed you... actors.char.defeat=You defeated %s. actors.char.def_verb=dodged +actors.char.invulnerable=invulnerable