v0.6.1: several improvements to mob and ally AI
This commit is contained in:
parent
7ebf612917
commit
cbc21ef9fb
|
@ -240,8 +240,16 @@ public abstract class Mob extends Char {
|
|||
//and add the hero to the list of targets.
|
||||
enemies.add(Dungeon.hero);
|
||||
|
||||
//target one at random.
|
||||
return Random.element(enemies);
|
||||
//go after the closest enemy, preferring the hero if two are equidistant
|
||||
Char closest = null;
|
||||
for (Char curr : enemies){
|
||||
if (closest == null
|
||||
|| Dungeon.level.distance(pos, curr.pos) < Dungeon.level.distance(pos, closest.pos)
|
||||
|| Dungeon.level.distance(pos, curr.pos) == Dungeon.level.distance(pos, closest.pos) && curr == Dungeon.hero){
|
||||
closest = curr;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
|
||||
}
|
||||
|
||||
|
@ -313,7 +321,7 @@ public abstract class Mob extends Char {
|
|||
//or if it's extremely inefficient and checking again may result in a much better path
|
||||
if (path == null || path.isEmpty()
|
||||
|| !Dungeon.level.adjacent(pos, path.getFirst())
|
||||
|| path.size() >= 2*Dungeon.level.distance(pos, target))
|
||||
|| path.size() > 2*Dungeon.level.distance(pos, target))
|
||||
newPath = true;
|
||||
else if (path.getLast() != target) {
|
||||
//if the new target is adjacent to the end of the path, adjust for that
|
||||
|
@ -372,8 +380,14 @@ public abstract class Mob extends Char {
|
|||
Level.fieldOfView);
|
||||
}
|
||||
|
||||
if (path == null)
|
||||
//if hunting something, don't follow a path that is extremely inefficient
|
||||
//FIXME this is fairly brittle, primarily it assumes that hunting mobs can't see through
|
||||
// permanent terrain, such that if their path is inefficient it's always because
|
||||
// of a temporary blockage, and therefore waiting for it to clear is the best option.
|
||||
if (path == null ||
|
||||
(state == HUNTING && path.size() > Math.max(9, 2*Dungeon.level.distance(pos, target)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
step = path.removeFirst();
|
||||
}
|
||||
|
@ -461,12 +475,11 @@ public abstract class Mob extends Char {
|
|||
}
|
||||
}
|
||||
|
||||
//become aggro'd by a corrupted enemy
|
||||
if (enemy.buff(Corruption.class) != null) {
|
||||
//if attacked by something else than current target, and that thing is closer, switch targets
|
||||
if (this.enemy == null
|
||||
|| (enemy != this.enemy && (Dungeon.level.distance(pos, enemy.pos) < Dungeon.level.distance(pos, this.enemy.pos)))) {
|
||||
aggro(enemy);
|
||||
target = enemy.pos;
|
||||
if (state == SLEEPING || state == WANDERING)
|
||||
state = HUNTING;
|
||||
}
|
||||
|
||||
if (buff(SoulMark.class) != null) {
|
||||
|
|
|
@ -32,7 +32,6 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
|
|||
import com.shatteredpixel.shatteredpixeldungeon.sprites.CharSprite;
|
||||
import com.shatteredpixel.shatteredpixeldungeon.sprites.MirrorSprite;
|
||||
import com.watabou.utils.Bundle;
|
||||
import com.watabou.utils.Random;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
|
@ -100,12 +99,22 @@ public class MirrorImage extends NPC {
|
|||
if (enemy == null || !enemy.isAlive()) {
|
||||
HashSet<Mob> enemies = new HashSet<>();
|
||||
for (Mob mob : Dungeon.level.mobs) {
|
||||
if (mob.hostile && Level.fieldOfView[mob.pos]) {
|
||||
if (mob.hostile
|
||||
&& Level.fieldOfView[mob.pos]
|
||||
&& mob.state != mob.PASSIVE) {
|
||||
enemies.add(mob);
|
||||
}
|
||||
}
|
||||
|
||||
enemy = enemies.size() > 0 ? Random.element( enemies ) : null;
|
||||
//go for closest enemy
|
||||
Char closest = null;
|
||||
for (Char curr : enemies){
|
||||
if (closest == null
|
||||
|| Dungeon.level.distance(pos, curr.pos) < Dungeon.level.distance(pos, closest.pos)){
|
||||
closest = curr;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
return enemy;
|
||||
|
|
|
@ -363,10 +363,16 @@ public class DriedRose extends Artifact {
|
|||
|
||||
flying = true;
|
||||
|
||||
ally = true;
|
||||
|
||||
WANDERING = new Wandering();
|
||||
HUNTING = new Hunting();
|
||||
|
||||
state = WANDERING;
|
||||
enemy = null;
|
||||
|
||||
ally = true;
|
||||
//after hero, but before mobs
|
||||
actPriority = 1;
|
||||
}
|
||||
|
||||
private DriedRose rose = null;
|
||||
|
@ -442,24 +448,34 @@ public class DriedRose extends Artifact {
|
|||
return super.act();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean getCloser( int target ) {
|
||||
if (state == WANDERING || Dungeon.level.distance(target, Dungeon.hero.pos) > 6)
|
||||
this.target = target = Dungeon.hero.pos;
|
||||
return super.getCloser( target );
|
||||
}
|
||||
|
||||
//prioritizes closest enemy, and will never attack something far from the player
|
||||
@Override
|
||||
protected Char chooseEnemy() {
|
||||
if (enemy == null || !enemy.isAlive() || !Dungeon.level.mobs.contains(enemy) || state == WANDERING) {
|
||||
if (enemy == null
|
||||
|| !enemy.isAlive()
|
||||
|| !Dungeon.level.mobs.contains(enemy)
|
||||
|| Dungeon.level.distance(enemy.pos, Dungeon.hero.pos) > 8
|
||||
|| state == WANDERING) {
|
||||
|
||||
HashSet<Mob> enemies = new HashSet<Mob>();
|
||||
HashSet<Mob> enemies = new HashSet<>();
|
||||
for (Mob mob : Dungeon.level.mobs) {
|
||||
if (mob.hostile && Level.fieldOfView[mob.pos] && mob.state != mob.PASSIVE) {
|
||||
if (mob.hostile
|
||||
&& Level.fieldOfView[mob.pos]
|
||||
&& Dungeon.level.distance(mob.pos, Dungeon.hero.pos) <= 8
|
||||
&& mob.state != mob.PASSIVE) {
|
||||
enemies.add(mob);
|
||||
}
|
||||
}
|
||||
enemy = enemies.size() > 0 ? Random.element( enemies ) : null;
|
||||
|
||||
//go for closest enemy
|
||||
Char closest = null;
|
||||
for (Char curr : enemies){
|
||||
if (closest == null
|
||||
|| Dungeon.level.distance(pos, curr.pos) < Dungeon.level.distance(pos, closest.pos)){
|
||||
closest = curr;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
return enemy;
|
||||
}
|
||||
|
@ -640,6 +656,75 @@ public class DriedRose extends Artifact {
|
|||
return IMMUNITIES;
|
||||
}
|
||||
|
||||
private class Wandering extends Mob.Wandering {
|
||||
|
||||
@Override
|
||||
public boolean act( boolean enemyInFOV, boolean justAlerted ) {
|
||||
if ( enemyInFOV ) {
|
||||
|
||||
enemySeen = true;
|
||||
|
||||
notice();
|
||||
state = HUNTING;
|
||||
target = enemy.pos;
|
||||
|
||||
} else {
|
||||
|
||||
enemySeen = false;
|
||||
|
||||
int oldPos = pos;
|
||||
//always move towards the hero when wandering
|
||||
if (getCloser( target = Dungeon.hero.pos )) {
|
||||
//moves 2 tiles at a time when returning to the hero from a distance
|
||||
if (!Dungeon.level.adjacent(Dungeon.hero.pos, pos)){
|
||||
getCloser( target = Dungeon.hero.pos );
|
||||
}
|
||||
spend( 1 / speed() );
|
||||
return moveSprite( oldPos, pos );
|
||||
} else {
|
||||
spend( TICK );
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Hunting extends Mob.Hunting {
|
||||
|
||||
@Override
|
||||
public boolean act( boolean enemyInFOV, boolean justAlerted ) {
|
||||
|
||||
enemySeen = enemyInFOV;
|
||||
if (enemyInFOV && !isCharmedBy( enemy ) && canAttack( enemy )) {
|
||||
|
||||
return doAttack( enemy );
|
||||
|
||||
} else {
|
||||
|
||||
if (enemyInFOV) {
|
||||
target = enemy.pos;
|
||||
}
|
||||
|
||||
int oldPos = pos;
|
||||
if (enemyInFOV && getCloser( target )) {
|
||||
|
||||
spend( 1 / speed() );
|
||||
return moveSprite( oldPos, pos );
|
||||
|
||||
} else {
|
||||
|
||||
//don't lose a turn due to the transition, immediately act instead
|
||||
state = WANDERING;
|
||||
return state.act( false, justAlerted );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//************************************************************************************
|
||||
//This is a bunch strings & string arrays, used in all of the sad ghost's voice lines.
|
||||
//************************************************************************************
|
||||
|
|
Loading…
Reference in New Issue
Block a user