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 SLEEPING = new Sleeping();
public AiState HUNTING = new Hunting(); public AiState HUNTING = new Hunting();
public AiState INVESTIGATING= new Investigating();
public AiState WANDERING = new Wandering(); public AiState WANDERING = new Wandering();
public AiState FLEEING = new Fleeing(); public AiState FLEEING = new Fleeing();
public AiState PASSIVE = new Passive(); public AiState PASSIVE = new Passive();
@@ -159,6 +160,8 @@ public abstract class Mob extends Char {
bundle.put( STATE, Sleeping.TAG ); bundle.put( STATE, Sleeping.TAG );
} else if (state == WANDERING) { } else if (state == WANDERING) {
bundle.put( STATE, Wandering.TAG ); bundle.put( STATE, Wandering.TAG );
} else if (state == INVESTIGATING) {
bundle.put( STATE, Investigating.TAG );
} else if (state == HUNTING) { } else if (state == HUNTING) {
bundle.put( STATE, Hunting.TAG ); bundle.put( STATE, Hunting.TAG );
} else if (state == FLEEING) { } else if (state == FLEEING) {
@@ -185,6 +188,8 @@ public abstract class Mob extends Char {
this.state = SLEEPING; this.state = SLEEPING;
} else if (state.equals( Wandering.TAG )) { } else if (state.equals( Wandering.TAG )) {
this.state = WANDERING; this.state = WANDERING;
} else if (state.equals( Investigating.TAG )) {
this.state = INVESTIGATING;
} else if (state.equals( Hunting.TAG )) { } else if (state.equals( Hunting.TAG )) {
this.state = HUNTING; this.state = HUNTING;
} else if (state.equals( Fleeing.TAG )) { } else if (state.equals( Fleeing.TAG )) {
@@ -229,6 +234,7 @@ public abstract class Mob extends Char {
} else { } else {
sprite.hideAlert(); sprite.hideAlert();
sprite.hideLost(); sprite.hideLost();
sprite.hideInvestigate();
} }
if (paralysed > 0) { 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 //can be awoken by the least stealthy hostile present, not necessarily just our target
if (enemyInFOV || (enemy != null && enemy.invisible > 0)) { 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()){ for (Char ch : Actor.chars()){
if (fieldOfView[ch.pos] && ch.invisible == 0 && ch.alignment != alignment && ch.alignment != Alignment.NEUTRAL){ 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 //silent steps rogue talent, which also applies to rogue's shadow clone
if ((ch instanceof Hero || ch instanceof ShadowClone.ShadowAlly) if ((ch instanceof Hero || ch instanceof ShadowClone.ShadowAlly)
&& Dungeon.hero.hasTalent(Talent.SILENT_STEPS)){ && Dungeon.hero.hasTalent(Talent.SILENT_STEPS)){
if (distance(ch) >= 4 - Dungeon.hero.pointsInTalent(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 //flying characters are naturally stealthy
if (ch.flying && distance(ch) >= 2){ if (ch.flying && distance(ch) >= 2){
chDist = Float.POSITIVE_INFINITY; bestChance = Float.POSITIVE_INFINITY;
} }
if (chDist < closestHostileDist){ if (bestChance < highestChance){
closestHostileDist = chDist; highestChance = bestChance;
closestHostile = ch;
} }
} }
} }
if (Random.Float( closestHostileDist ) < 1) { if (closestHostile != null && Random.Float() < detectionChance(closestHostile)) {
awaken(enemyInFOV); awaken(enemyInFOV);
if (state == SLEEPING){ if (state == SLEEPING){
spend(TICK); //wait if we can't wake up for some reason spend(TICK); //wait if we can't wake up for some reason
@@ -1123,6 +1131,11 @@ public abstract class Mob extends Char {
return true; return true;
} }
//chance is 1 in (distance + stealth)
protected float detectionChance( Char enemy ){
return 1 / (distance( enemy ) + enemy.stealth());
}
protected void awaken( boolean enemyInFOV ){ protected void awaken( boolean enemyInFOV ){
if (enemyInFOV) { if (enemyInFOV) {
enemySeen = true; enemySeen = true;
@@ -1154,7 +1167,7 @@ public abstract class Mob extends Char {
@Override @Override
public boolean act( boolean enemyInFOV, boolean justAlerted ) { 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(); 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(){ protected boolean noticeEnemy(){
enemySeen = true; 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 { protected class Fleeing implements AiState {
public static final String TAG = "FLEEING"; public static final String TAG = "FLEEING";