Rogue: Savage Rats, a retro-themed dungeon crawler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rogue-savage-rats/src/mightypork/rogue/world/entity/impl/MonsterAi.java

350 lines
6.0 KiB

package mightypork.rogue.world.entity.impl;
import java.util.List;
import mightypork.rogue.world.entity.AiTimer;
import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityModule;
import mightypork.rogue.world.entity.EntityType;
import mightypork.rogue.world.entity.modules.EntityMoveListener;
import mightypork.rogue.world.tile.Tile;
10 years ago
import mightypork.utils.annotations.Stub;
import mightypork.utils.ion.IonDataBundle;
import mightypork.utils.math.Calc;
import mightypork.utils.math.algo.Coord;
import mightypork.utils.math.algo.Move;
import mightypork.utils.math.algo.Moves;
import mightypork.utils.math.algo.pathfinding.PathFinder;
import mightypork.utils.math.algo.pathfinding.PathFinderProxy;
public class MonsterAi extends EntityModule implements EntityMoveListener {
10 years ago
private boolean chasing = false;
10 years ago
10 years ago
private final AiTimer timerFindPrey = new AiTimer(3) {
10 years ago
@Override
public void run()
{
if (!isIdle()) return;
lookForTarget();
}
};
10 years ago
private final AiTimer timerAttack = new AiTimer(1) {
10 years ago
@Override
public void run()
{
if (!isChasing()) return;
10 years ago
final Entity prey = getPreyEntity();
10 years ago
if (prey == null || prey.isDead()) return;
10 years ago
attackPrey(prey);
}
};
10 years ago
private final AiTimer timerRandomWalk = new AiTimer(0.2) {
10 years ago
@Override
public void run()
{
if (!isIdle()) return;
10 years ago
// annoyed by attacking.
10 years ago
if (entity.getLastAttackTime() < 0.5) {
lookForTarget();
return;
}
10 years ago
10 years ago
if (entity.pos.isMoving()) return;
10 years ago
10 years ago
if (Calc.rand.nextInt(10) == 0) {
entity.pos.addStep(Moves.randomCardinal());
}
}
};
10 years ago
private PathFinder noDoorPf;
10 years ago
/** Prey id */
private int preyId = -1;
10 years ago
public MonsterAi(final Entity entity)
{
super(entity);
10 years ago
noDoorPf = new PathFinderProxy(entity.getPathFinder()) {
10 years ago
@Override
public boolean isAccessible(Coord pos)
{
10 years ago
final Tile t = entity.getLevel().getTile(pos);
if (t.isDoor()) return false;
10 years ago
return super.isAccessible(pos);
}
10 years ago
};
10 years ago
noDoorPf.setIgnoreEnd(true);
10 years ago
timerAttack.start();
timerFindPrey.start();
}
10 years ago
@Override
public void onStepFinished()
{
if (entity.isDead()) return;
10 years ago
if (isChasing()) {
final Entity prey = getPreyEntity();
if (!isPreyValid(prey)) {
stopChasing();
return;
}
10 years ago
if (!isPreyInAttackRange(prey)) {
stepTowardsPrey(prey);
}
}
}
10 years ago
@Override
public void onPathFinished()
{
}
10 years ago
@Override
public void onPathInterrupted()
{
}
10 years ago
@Override
public void save(IonDataBundle bundle)
{
bundle.putBundled("tscan", timerFindPrey);
bundle.putBundled("tattack", timerAttack);
10 years ago
bundle.put("chasing", chasing);
10 years ago
bundle.put("prey", preyId);
}
10 years ago
@Override
public void load(IonDataBundle bundle)
{
bundle.loadBundled("tscan", timerFindPrey);
bundle.loadBundled("tattack", timerAttack);
10 years ago
chasing = bundle.get("chasing", chasing);
10 years ago
preyId = bundle.get("prey", preyId);
}
10 years ago
@Override
public boolean isModuleSaved()
{
return true;
}
10 years ago
@Override
public void update(double delta)
{
if (entity.isDead()) return;
10 years ago
timerFindPrey.update(delta);
timerAttack.update(delta);
timerRandomWalk.update(delta);
10 years ago
// go after the prey
if (isChasing() && !entity.pos.isMoving()) {
final Entity prey = getPreyEntity();
if (prey == null) {
// prey killed and cleaned from level
stopChasing();
}
10 years ago
if (!isPreyInAttackRange(prey)) {
stepTowardsPrey(prey);
}
}
}
10 years ago
public boolean isIdle()
{
return !chasing;
}
10 years ago
public boolean isChasing()
{
return chasing;
}
10 years ago
private void lookForTarget()
{
if (entity.isDead()) return;
10 years ago
final Entity prey = entity.getLevel().getClosestEntity(entity.pos.getVisualPos(), EntityType.PLAYER, getScanRadius());
if (prey != null) {
10 years ago
// check if reachable without leaving room
final List<Coord> noDoorPath = noDoorPf.findPath(entity.getCoord(), prey.getCoord());
10 years ago
if (noDoorPath == null) return; // cant reach, give up
10 years ago
startChasing(prey);
}
}
10 years ago
private Entity getPreyEntity()
{
return entity.getLevel().getEntity(preyId);
}
10 years ago
private boolean isPreyInAttackRange(Entity prey)
{
return prey.getCoord().dist(entity.getCoord()) <= getAttackDistance();
}
10 years ago
private boolean isPreyValid(Entity prey)
{
return prey != null && !prey.isDead();
}
10 years ago
private void startChasing(Entity prey)
{
if (entity.isDead()) return;
10 years ago
preyId = prey.getEntityId();
chasing = true;
10 years ago
entity.pos.setStepTime(getStepTime());
10 years ago
// follow this one prey
timerFindPrey.pause();
10 years ago
onStepFinished(); // go towards prey
}
10 years ago
private void stopChasing()
{
chasing = false;
10 years ago
entity.pos.setStepTime(getStepTime());
10 years ago
preyId = -1;
timerFindPrey.restart();
}
10 years ago
private List<Move> getPathToPrey(Entity prey)
{
if (!isPreyValid(prey)) return null;
10 years ago
return entity.getPathFinder().findPathRelative(entity.getCoord(), prey.getCoord(), false, true);
}
10 years ago
private void stepTowardsPrey(Entity prey)
{
if (entity.isDead()) return;
10 years ago
if (!isPreyValid(prey)) return;
10 years ago
// if close enough
if (isPreyInAttackRange(prey)) {
// attack using the timed loop
return;
}
10 years ago
final List<Move> preyPath = getPathToPrey(prey);
10 years ago
if (preyPath == null || preyPath.size() > getPreyAbandonDistance()) {
stopChasing();
return;
}
10 years ago
entity.pos.cancelPath();
entity.pos.addSteps(preyPath);
}
10 years ago
private void attackPrey(Entity prey)
{
if (entity.isDead()) return;
10 years ago
if (!isPreyInAttackRange(prey)) return;
10 years ago
prey.receiveAttack(entity, getAttackStrength());
}
10 years ago
protected void setAttackTime(double secs)
{
timerAttack.setDuration(secs);
}
10 years ago
protected void setScanTime(double secs)
{
timerFindPrey.setDuration(secs);
}
10 years ago
10 years ago
@Stub
protected double getScanRadius()
{
return isIdle() ? Calc.randInt(1, 3) : Calc.randInt(4, 8); // For override
}
10 years ago
10 years ago
@Stub
protected int getPreyAbandonDistance()
{
return Calc.randInt(5, 8); // For override
}
10 years ago
10 years ago
@Stub
protected double getAttackDistance()
{
return 1;
}
10 years ago
10 years ago
@Stub
protected int getAttackStrength()
{
return 1; // For override
}
10 years ago
10 years ago
@Stub
protected double getStepTime()
{
return isIdle() ? 0.7 : 0.4;
}
}