v3.3.5: implemented base investigating state for all mobs

This commit is contained in:
Evan Debenham
2026-01-29 14:33:57 -05:00
parent 6d640324c1
commit 5a6083a70b

View File

@@ -111,6 +111,7 @@ public abstract class Mob extends Char {
public AiState SLEEPING = new Sleeping();
public AiState HUNTING = new Hunting();
public AiState INVESTIGATING= new Investigating();
public AiState WANDERING = new Wandering();
public AiState FLEEING = new Fleeing();
public AiState PASSIVE = new Passive();
@@ -159,6 +160,8 @@ public abstract class Mob extends Char {
bundle.put( STATE, Sleeping.TAG );
} else if (state == WANDERING) {
bundle.put( STATE, Wandering.TAG );
} else if (state == INVESTIGATING) {
bundle.put( STATE, Investigating.TAG );
} else if (state == HUNTING) {
bundle.put( STATE, Hunting.TAG );
} else if (state == FLEEING) {
@@ -185,6 +188,8 @@ public abstract class Mob extends Char {
this.state = SLEEPING;
} else if (state.equals( Wandering.TAG )) {
this.state = WANDERING;
} else if (state.equals( Investigating.TAG )) {
this.state = INVESTIGATING;
} else if (state.equals( Hunting.TAG )) {
this.state = HUNTING;
} else if (state.equals( Fleeing.TAG )) {
@@ -229,6 +234,7 @@ public abstract class Mob extends Char {
} else {
sprite.hideAlert();
sprite.hideLost();
sprite.hideInvestigate();
}
if (paralysed > 0) {
@@ -1085,29 +1091,31 @@ public abstract class Mob extends Char {
//can be awoken by the least stealthy hostile present, not necessarily just our target
if (enemyInFOV || (enemy != null && enemy.invisible > 0)) {
float closestHostileDist = Float.POSITIVE_INFINITY;
float highestChance = Float.POSITIVE_INFINITY;
Char closestHostile = null;
for (Char ch : Actor.chars()){
if (fieldOfView[ch.pos] && ch.invisible == 0 && ch.alignment != alignment && ch.alignment != Alignment.NEUTRAL){
float chDist = ch.stealth() + distance(ch);
float bestChance = detectionChance(ch);
//silent steps rogue talent, which also applies to rogue's shadow clone
if ((ch instanceof Hero || ch instanceof ShadowClone.ShadowAlly)
&& Dungeon.hero.hasTalent(Talent.SILENT_STEPS)){
if (distance(ch) >= 4 - Dungeon.hero.pointsInTalent(Talent.SILENT_STEPS)) {
chDist = Float.POSITIVE_INFINITY;
bestChance = Float.POSITIVE_INFINITY;
}
}
//flying characters are naturally stealthy
if (ch.flying && distance(ch) >= 2){
chDist = Float.POSITIVE_INFINITY;
bestChance = Float.POSITIVE_INFINITY;
}
if (chDist < closestHostileDist){
closestHostileDist = chDist;
if (bestChance < highestChance){
highestChance = bestChance;
closestHostile = ch;
}
}
}
if (Random.Float( closestHostileDist ) < 1) {
if (closestHostile != null && Random.Float() < detectionChance(closestHostile)) {
awaken(enemyInFOV);
if (state == SLEEPING){
spend(TICK); //wait if we can't wake up for some reason
@@ -1123,6 +1131,11 @@ public abstract class Mob extends Char {
return true;
}
//chance is 1 in (distance + stealth)
protected float detectionChance( Char enemy ){
return 1 / (distance( enemy ) + enemy.stealth());
}
protected void awaken( boolean enemyInFOV ){
if (enemyInFOV) {
enemySeen = true;
@@ -1154,7 +1167,7 @@ public abstract class Mob extends Char {
@Override
public boolean act( boolean enemyInFOV, boolean justAlerted ) {
if (enemyInFOV && (justAlerted || Random.Float( distance( enemy ) / 2f + enemy.stealth() ) < 1)) {
if (enemyInFOV && (justAlerted || Random.Float() < detectionChance(enemy))) {
return noticeEnemy();
@@ -1164,7 +1177,12 @@ public abstract class Mob extends Char {
}
}
//chance is 1 in (distance/2 + stealth)
protected float detectionChance( Char enemy ){
return 1 / (distance( enemy ) / 2f + enemy.stealth());
}
protected boolean noticeEnemy(){
enemySeen = true;
@@ -1299,6 +1317,33 @@ public abstract class Mob extends Char {
}
}
//essentially a more aggressive version of wandering, where target pos is updated like hunting
//not currently used directly by mobs outside of the vault, which also add more behaviour here
protected class Investigating extends Wandering {
public static final String TAG = "INVESTIGATING";
@Override
public boolean act(boolean enemyInFOV, boolean justAlerted) {
if (enemyInFOV){
target = enemy.pos;
} else {
//we lose our target BEFORE reaching their last known position
if (Dungeon.level.distance(pos, target) <= 1){
sprite.showLost();
state = WANDERING;
target = ((Mob.Wandering)WANDERING).randomDestination();
spend( TICK );
return true;
}
}
return super.act(enemyInFOV, justAlerted);
}
//same detection chance as wandering
}
protected class Fleeing implements AiState {
public static final String TAG = "FLEEING";