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
This commit is contained in:
Evan Debenham 2020-03-27 16:11:01 -04:00
parent 97236333d8
commit b5989ee4f6
8 changed files with 75 additions and 55 deletions

View File

@ -416,6 +416,11 @@ public abstract class Char extends Actor {
return; return;
} }
if(isInvulnerable(src.getClass())){
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "invulnerable"));
return;
}
if (!(src instanceof LifeLink) && buff(LifeLink.class) != null){ if (!(src instanceof LifeLink) && buff(LifeLink.class) != null){
HashSet<LifeLink> links = buffs(LifeLink.class); HashSet<LifeLink> links = buffs(LifeLink.class);
for (LifeLink link : links.toArray(new LifeLink[0])){ for (LifeLink link : links.toArray(new LifeLink[0])){
@ -697,6 +702,12 @@ public abstract class Char extends Actor {
return false; 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<Property> properties = new HashSet<>(); protected HashSet<Property> properties = new HashSet<>();
public HashSet<Property> properties() { public HashSet<Property> properties() {

View File

@ -73,8 +73,9 @@ public class Combo extends Buff implements ActionIndicator.Action {
} }
public void hit( Char enemy ) { public void hit( Char enemy ) {
count++; //doesn't increment combo count if enemy invulnerable
if (!enemy.isInvulnerable(target.getClass())) count++;
comboTime = 4f; comboTime = 4f;
misses = 0; misses = 0;
BuffIndicator.refreshHero(); BuffIndicator.refreshHero();

View File

@ -354,10 +354,15 @@ public class DwarfKing extends Mob {
} }
} }
@Override
public boolean isInvulnerable(Class effect) {
return phase == 2 && effect != KingDamager.class;
}
@Override @Override
public void damage(int dmg, Object src) { public void damage(int dmg, Object src) {
if (phase == 2 && !(src instanceof KingDamager)){ if (isInvulnerable(src.getClass())){
sprite.showStatus( CharSprite.POSITIVE, Messages.get(this, "immune") ); super.damage(dmg, src);
return; return;
} else if (phase == 3 && !(src instanceof Viscosity.DeferedDamage)){ } else if (phase == 3 && !(src instanceof Viscosity.DeferedDamage)){
Viscosity.DeferedDamage deferred = Buff.affect( this, Viscosity.DeferedDamage.class ); Viscosity.DeferedDamage deferred = Buff.affect( this, Viscosity.DeferedDamage.class );
@ -378,7 +383,7 @@ public class DwarfKing extends Mob {
summonCooldown -= dmgTaken/8f; summonCooldown -= dmgTaken/8f;
if (HP <= 50) { if (HP <= 50) {
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); ScrollOfTeleportation.appear(this, NewCityBossLevel.throne);
properties.add(Property.IMMOVABLE); properties.add(Property.IMMOVABLE);
phase = 2; phase = 2;

View File

@ -215,17 +215,21 @@ public abstract class Mob extends Char {
//find a new enemy if.. //find a new enemy if..
boolean newEnemy = false; boolean newEnemy = false;
//we have no enemy, or the current one is dead/missing //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; newEnemy = true;
//We are an ally, and current enemy is another ally. //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; newEnemy = true;
//We are amoked and current enemy is the hero //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; newEnemy = true;
//We are charmed and current enemy is what charmed us //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; 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 ) { if ( newEnemy ) {
@ -284,7 +288,16 @@ public abstract class Mob extends Char {
enemies.remove(source); 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. //neutral characters in particular do not choose enemies.
if (enemies.isEmpty()){ if (enemies.isEmpty()){
return null; return null;

View File

@ -407,13 +407,11 @@ public class NewDM300 extends Mob {
@Override @Override
public void damage(int dmg, Object src) { public void damage(int dmg, Object src) {
if (supercharged){ super.damage(dmg, src);
sprite.showStatus( CharSprite.POSITIVE, Messages.get(this, "immune") ); if (isInvulnerable(src.getClass())){
return; return;
} }
super.damage(dmg, src);
LockedFloor lock = Dungeon.hero.buff(LockedFloor.class); LockedFloor lock = Dungeon.hero.buff(LockedFloor.class);
if (lock != null && !isImmune(src.getClass())) lock.addTime(dmg); 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(){ public void supercharge(){
supercharged = true; supercharged = true;
((NewCavesBossLevel)Dungeon.level).activatePylon(); ((NewCavesBossLevel)Dungeon.level).activatePylon();
@ -433,7 +436,7 @@ public class NewDM300 extends Mob {
spend(3f); spend(3f);
yell(Messages.get(this, "charging")); yell(Messages.get(this, "charging"));
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "invulnerable"));
sprite.resetColor(); sprite.resetColor();
chargeAnnounced = false; chargeAnnounced = false;
} }

View File

@ -69,7 +69,8 @@ public class YogDzewa extends Mob {
EXP = 50; EXP = 50;
state = PASSIVE; //so that allies can attack it. States are never actually used.
state = HUNTING;
properties.add(Property.BOSS); properties.add(Property.BOSS);
properties.add(Property.IMMOVABLE); properties.add(Property.IMMOVABLE);
@ -113,8 +114,10 @@ public class YogDzewa extends Mob {
@Override @Override
protected boolean act() { protected boolean act() {
if (phase == 0 && Dungeon.hero.viewDistance >= Dungeon.level.distance(pos, Dungeon.hero.pos)){ if (phase == 0){
Dungeon.observe(); if (Dungeon.hero.viewDistance >= Dungeon.level.distance(pos, Dungeon.hero.pos)) {
Dungeon.observe();
}
if (Dungeon.level.heroFOV[pos]) { if (Dungeon.level.heroFOV[pos]) {
notice(); notice();
} }
@ -269,12 +272,12 @@ public class YogDzewa extends Mob {
} }
@Override @Override
public void damage( int dmg, Object src ) { public boolean isInvulnerable(Class effect) {
return phase == 0 || findFist() != null;
}
if (phase == 0 || findFist() != null){ @Override
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); public void damage( int dmg, Object src ) {
return;
}
int preHP = HP; int preHP = HP;
super.damage( dmg, src ); super.damage( dmg, src );
@ -292,7 +295,7 @@ public class YogDzewa extends Mob {
} }
Dungeon.observe(); Dungeon.observe();
GLog.n(Messages.get(this, "darkness")); 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)); YogFist fist = (YogFist) Reflection.newInstance(fistSummons.remove(0));
fist.pos = Dungeon.level.exit; fist.pos = Dungeon.level.exit;
@ -319,7 +322,7 @@ public class YogDzewa extends Mob {
} }
LockedFloor lock = Dungeon.hero.buff(LockedFloor.class); LockedFloor lock = Dungeon.hero.buff(LockedFloor.class);
if (lock != null) lock.addTime(dmg); if (lock != null) lock.addTime(dmgTaken);
} }

View File

@ -91,22 +91,25 @@ public abstract class YogFist extends Mob {
} }
} }
boolean immuneWarned = false; boolean invulnWarned = false;
protected boolean isNearYog(){ protected boolean isNearYog(){
int yogPos = Dungeon.level.exit + 3*Dungeon.level.width(); int yogPos = Dungeon.level.exit + 3*Dungeon.level.width();
return Dungeon.level.distance(pos, yogPos) <= 4; return Dungeon.level.distance(pos, yogPos) <= 4;
} }
@Override
public boolean isInvulnerable(Class effect) {
return isNearYog();
}
@Override @Override
public void damage(int dmg, Object src) { public void damage(int dmg, Object src) {
if (isNearYog()){ if (isInvulnerable(src.getClass())){
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune")); if (!invulnWarned){
if (!immuneWarned){ invulnWarned = true;
immuneWarned = true; GLog.w(Messages.get(this, "invuln_warn"));
GLog.w(Messages.get(this, "immune_hint"));
} }
return;
} }
super.damage(dmg, src); super.damage(dmg, src);
} }
@ -344,15 +347,7 @@ public abstract class YogFist extends Mob {
@Override @Override
public void damage(int dmg, Object src) { public void damage(int dmg, Object src) {
if (isNearYog()){ if (!isInvulnerable(src.getClass()) && !(src instanceof Bleeding)){
sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune"));
if (!immuneWarned){
immuneWarned = true;
GLog.w(Messages.get(this, "immune_hint"));
}
return;
}
if (!(src instanceof Bleeding)){
Bleeding b = buff(Bleeding.class); Bleeding b = buff(Bleeding.class);
if (b == null){ if (b == null){
b = new Bleeding(); b = new Bleeding();
@ -406,15 +401,7 @@ public abstract class YogFist extends Mob {
@Override @Override
public void damage(int dmg, Object src) { public void damage(int dmg, Object src) {
if (isNearYog()){ if (!isInvulnerable(src.getClass()) && !(src instanceof Viscosity.DeferedDamage)){
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)){
Buff.affect(this, Viscosity.DeferedDamage.class).prolong(dmg); Buff.affect(this, Viscosity.DeferedDamage.class).prolong(dmg);
sprite.showStatus( CharSprite.WARNING, Messages.get(Viscosity.class, "deferred", dmg) ); sprite.showStatus( CharSprite.WARNING, Messages.get(Viscosity.class, "deferred", dmg) );
} else{ } else{

View File

@ -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.charge_lost=POWER GRID DAMAGED, REVERTING TO LOCAL POWER!
actors.mobs.newdm300.pylons_destroyed=ALERT, INSTABILITY DETECTED IN POWER GRID! actors.mobs.newdm300.pylons_destroyed=ALERT, INSTABILITY DETECTED IN POWER GRID!
actors.mobs.newdm300.rankings_desc=Crushed by the DM-300 actors.mobs.newdm300.rankings_desc=Crushed by the DM-300
actors.mobs.newdm300.immune=IMMUNE
actors.mobs.newdm300.def_verb=blocked actors.mobs.newdm300.def_verb=blocked
actors.mobs.newdm300.defeated=CRITICAL DAMAGE! ATTEMPTING SHUTDO- 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. 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.lifelink_2=Bleed for me, slave!
actors.mobs.dwarfking.teleport_1=Deal with them, slave! actors.mobs.dwarfking.teleport_1=Deal with them, slave!
actors.mobs.dwarfking.teleport_2=Keep them busy, 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_1=Enough! Arise my slaves!
actors.mobs.dwarfking.wave_2=More! Bleed for your king! actors.mobs.dwarfking.wave_2=More! Bleed for your king!
actors.mobs.dwarfking.wave_3=Useless! KILL THEM NOW! 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.name=Yog-Dzewa
actors.mobs.yogdzewa.notice=I. SEE. YOU. actors.mobs.yogdzewa.notice=I. SEE. YOU.
actors.mobs.yogdzewa.immune=IMMUNE
actors.mobs.yogdzewa.darkness=The darkness pulls in closer... actors.mobs.yogdzewa.darkness=The darkness pulls in closer...
actors.mobs.yogdzewa.hope=YOUR. HOPE. IS. AN. ILLUSION! actors.mobs.yogdzewa.hope=YOUR. HOPE. IS. AN. ILLUSION!
actors.mobs.yogdzewa.defeated=... 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$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.yogdzewa$yogripper.rankings_desc=Devoured by Yog-Dzewa
actors.mobs.yogfist.immune=IMMUNE actors.mobs.yogfist.invuln_warn=The fist is invulnerable while near to the eye!
actors.mobs.yogfist.immune_hint=The fist is immune while near to the eye!
actors.mobs.yogfist.rankings_desc=Devoured by Yog-Dzewa 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.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 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.kill=%s killed you...
actors.char.defeat=You defeated %s. actors.char.defeat=You defeated %s.
actors.char.def_verb=dodged actors.char.def_verb=dodged
actors.char.invulnerable=invulnerable