620 lines
16 KiB
C
620 lines
16 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "ai.h"
|
|
#include "attack.h"
|
|
#include "defs.h"
|
|
#include "flag.h"
|
|
#include "io.h"
|
|
#include "lf.h"
|
|
#include "map.h"
|
|
#include "move.h"
|
|
#include "nexus.h"
|
|
#include "objects.h"
|
|
#include "spell.h"
|
|
#include "text.h"
|
|
|
|
extern lifeform_t *player;
|
|
extern enum ERROR reason;
|
|
|
|
void aimove(lifeform_t *lf) {
|
|
int wantdb = B_TRUE;
|
|
int db = B_FALSE;
|
|
object_t *curwep,*bestwep, *o;
|
|
object_t *curgun,*bestgun;
|
|
flag_t *f;
|
|
//flag_t *nextf;
|
|
// lifeform_t *fleefrom = NULL;
|
|
lifeform_t *target;
|
|
enum BODYPART bp;
|
|
int x,y;
|
|
cell_t *c;
|
|
obpile_t *unarmedpile = NULL;
|
|
flag_t *unarmedflag = NULL;
|
|
|
|
|
|
|
|
if (wantdb && haslos(player, lf->cell)) {
|
|
db = B_TRUE;
|
|
} else {
|
|
db = B_FALSE;
|
|
}
|
|
|
|
if (db) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
dblog("AIMOVE: %s", lfname);
|
|
}
|
|
|
|
/*
|
|
// if lifeform isn't alive, skip turn
|
|
if (isdead(lf)) {
|
|
if (db) dblog(".oO { i am not alive, skipping turn. }");
|
|
taketime(lf, SPEED_DEAD);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
// do we have a better weapon we could use?
|
|
curwep = getweapon(lf);
|
|
bestwep = getbestweapon(lf);
|
|
|
|
if (curwep != bestwep) {
|
|
if (db) dblog(".oO { i have a better weapon than my current one (%s > %s) }",bestwep->type->name, curwep ? curwep->type->name : "nothing");
|
|
// weild better one
|
|
if (!weild(lf, bestwep)) return;
|
|
}
|
|
|
|
// do we have a better firearm ?
|
|
curgun = getfirearm(lf);
|
|
bestgun = getbestfirearm(lf);
|
|
|
|
if (curgun != bestgun) {
|
|
if (db) dblog(".oO { i have a better gun than my current one (%s > %s) }",bestgun->type->name, curgun ? curgun->type->name : "nothing");
|
|
// weild better one
|
|
if (!weild(lf, bestgun)) return;
|
|
}
|
|
|
|
// do we have better armour?
|
|
for (bp = BP_RIGHTHAND ; bp < MAXBODYPARTS; bp++) {
|
|
object_t *curarm;
|
|
curarm = getarmour(lf, bp);
|
|
// do we have a better one?
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (isbetterarmourthan(o, curarm)) {
|
|
// wear this armour instead
|
|
if (!wear(lf, o)) return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// now check whetehr we have ANY weapon
|
|
curwep = getattackwep(lf, &unarmedpile, &unarmedflag);
|
|
if (unarmedpile) killobpile(unarmedpile);
|
|
|
|
// before attacking targets,
|
|
// look for any object which we _covet_.
|
|
// ie. if we covet something, we will pick it up
|
|
// instead of attacking our target.
|
|
if (db) dblog(".oO { looking for covetted objects... }");
|
|
if (lookforobs(lf, B_COVETS)) {
|
|
if (db) dblog(".oO { found covetted object. returning. }");
|
|
return;
|
|
}
|
|
|
|
// do we already have a target we are attacking?
|
|
f = hasflag(lf->flags, F_TARGET);
|
|
if (f) {
|
|
int targid;
|
|
int lastx,lasty;
|
|
if (db) dblog(".oO { i have a target... }");
|
|
targid = f->val[0];
|
|
lastx = f->val[1];
|
|
lasty = f->val[2];
|
|
target = findlf(lf->cell->map, targid);
|
|
if (target) {
|
|
if (db) dblog(".oO { my target is lfid %d (%s). }", targid, target->race->name);
|
|
if (haslos(lf, target->cell)) {
|
|
int goingtomove = B_TRUE;
|
|
enum OBTYPE spell;
|
|
object_t *gun;
|
|
|
|
// reset F_TARGET lifetime to full.
|
|
f->lifetime = AI_FOLLOWTIME;
|
|
if (db) dblog(".oO { i can see my target (at %d,%d). will move towards it. }",target->cell->x,target->cell->y);
|
|
// remember last known loc
|
|
f->val[1] = target->cell->x;
|
|
f->val[2] = target->cell->y;
|
|
|
|
goingtomove = B_TRUE;
|
|
|
|
|
|
// can we attack with spells (ie. ones which target the victim)?
|
|
spell = getattackspell(lf);
|
|
if (spell != OT_NONE) {
|
|
int spellfailed = B_FALSE;
|
|
lifeform_t *spelllf = NULL;
|
|
cell_t *spellcell = NULL;
|
|
object_t *spellob = NULL;
|
|
if (db) {
|
|
objecttype_t *st;
|
|
st = findot(spell);
|
|
dblog(".oO { will cast attack spell: %s }", st->name);
|
|
}
|
|
|
|
// special cases: eg. spells like telekenesis
|
|
if (spell == OT_S_TELEKINESIS) {
|
|
float maxweight;
|
|
object_t *poss[MAXPILEOBS];
|
|
int nposs;
|
|
int i;
|
|
// find nearest object which can be picked up
|
|
|
|
// this is copied out of the telekenesis spell code!
|
|
maxweight = getlfweight(lf, B_NOOBS) +
|
|
(getlfweight(lf, B_NOOBS) * (getstatmod(lf, A_IQ) / 100));
|
|
|
|
nposs = 0;
|
|
for (i = 0; i < lf->nlos; i++) {
|
|
object_t *o;
|
|
for (o = lf->los[i]->obpile->first ; o ; o = o->next) {
|
|
if (!hasflag(o->flags, F_NOPICKUP) &&
|
|
getobweight(o) <= maxweight) {
|
|
poss[nposs] = o;
|
|
nposs++;
|
|
if (nposs >= MAXPILEOBS) break;
|
|
}
|
|
}
|
|
if (nposs >= MAXPILEOBS) break;
|
|
}
|
|
if (nposs > 0) {
|
|
spellob = poss[rnd(0,nposs-1)];
|
|
} else {
|
|
spellfailed = B_TRUE;
|
|
}
|
|
|
|
// cast spell at the player
|
|
spelllf = target;
|
|
spellcell = target->cell;
|
|
} else {
|
|
spelllf = target;
|
|
spellcell = target->cell;
|
|
spellob = NULL;
|
|
}
|
|
|
|
|
|
if (!spellfailed && !castspell(lf, spell, spelllf, spellob, spellcell)) {
|
|
// spell succesful
|
|
return;
|
|
} else {
|
|
if (db) dblog(".oO { cast spell failed! }");
|
|
}
|
|
}
|
|
|
|
// can we attack by firing something?
|
|
gun = getfirearm(lf);
|
|
if (goingtomove && gun && getammo(lf)) {
|
|
setguntarget(lf, target);
|
|
if (!shoot(lf)) {
|
|
// succesful
|
|
return;
|
|
} else {
|
|
if (db) dblog(".oO { shoot gun failed! }");
|
|
}
|
|
}
|
|
|
|
// can we attack by throwing something?
|
|
if (goingtomove && getcelldist(lf->cell, target->cell) > 1) {
|
|
// TODO: or firing! check if we have a firearm first.
|
|
o = getbestmissile(lf);
|
|
if (o) {
|
|
if (db) dblog(".oO { will throw %s at my target instead of moving }", o->type->name);
|
|
// try to throw it!
|
|
if (!throwat(lf, o, target->cell)) {
|
|
// succesful
|
|
goingtomove = B_FALSE;
|
|
} else {
|
|
if (db) dblog(".oO { throw failed! }");
|
|
}
|
|
}
|
|
}
|
|
|
|
// do we have a valid melee attack?
|
|
if (!curwep) {
|
|
if (db) dblog(".oO { won't move towards target - i have no weapon. }");
|
|
goingtomove = B_FALSE;
|
|
}
|
|
|
|
if (goingtomove) {
|
|
if (!movetowards(lf, target->cell)) {
|
|
// success
|
|
return;
|
|
} else {
|
|
if (db) dblog(".oO { move towards failed! - reason = %d }",reason);
|
|
}
|
|
}
|
|
} else {
|
|
if (db) dblog(".oO { i cannot see my target. moving to last known loc %d/%d }",lastx,lasty);
|
|
|
|
// can't see target.
|
|
// move towards their last known location instead
|
|
addflag(lf->flags, F_TARGETCELL, lastx, lasty, NA, NULL);
|
|
// remove f_target
|
|
killflag(f);
|
|
|
|
/*
|
|
// just try to move in a random direction
|
|
if (db) dblog(".oO { will move randomly }");
|
|
// dorandommove will call taketime() if it fails,
|
|
// so it's safe to just return
|
|
dorandommove(lf, B_NOBADMOVES);
|
|
return;
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
if (lookforobs(lf, B_COVETS)) {
|
|
if (db) dblog(".oO { found covetted object. returning. }");
|
|
return;
|
|
}
|
|
*/
|
|
|
|
// do we have a target cell?
|
|
f = hasflag(lf->flags, F_TARGETCELL);
|
|
if (f) {
|
|
// if so, move towards it
|
|
x = f->val[0];
|
|
y = f->val[1];
|
|
if (db) dblog(".oO { walking from %d,%d towards f_targetcell (%d,%d) ... }", lf->cell->x, lf->cell->y, x, y);
|
|
c = getcellat(lf->cell->map, x, y);
|
|
if (c) {
|
|
if (movetowards(lf, c)) {
|
|
// couldn't move towards it for some reason.
|
|
// so stop trying.
|
|
if (db) dblog(".oO { couldn't walk towards f_targetcell. abandoning it. }");
|
|
killflag(f);
|
|
// remember NOT to target this one.
|
|
lf->ignorecell = c;
|
|
} else {
|
|
if (db) dblog(".oO { successfully walked towards f_targetcell. }");
|
|
// moved towards it.
|
|
// reset lifetime
|
|
f->lifetime = AI_FOLLOWTIME;
|
|
|
|
// are we there yet?
|
|
if (lf->cell == c) {
|
|
if (db) dblog(".oO { arrived at f_targetcell. removing. }");
|
|
killflag(f);
|
|
}
|
|
}
|
|
} else {
|
|
if (db) dblog(".oO { f_targetcell doesn't exist. abandoning. }");
|
|
// destination doesn't exist!
|
|
killflag(f);
|
|
// remember NOT to target this one.
|
|
lf->ignorecell = c;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// look for any object which we want
|
|
if (db) dblog(".oO { looking for any ob which i want. }");
|
|
if (lookforobs(lf, B_ANY)) {
|
|
if (db) dblog(".oO { found ob that i want. returning. }");
|
|
return;
|
|
}
|
|
|
|
|
|
// not attacking anyone in particular
|
|
if (db) dblog(".oO { i do not have a target or can't move towards it. }");
|
|
// are we hostile? if so, look for a target
|
|
f = hasflag(lf->flags, F_HOSTILE);
|
|
if (f) {
|
|
int x,y;
|
|
if (db) dblog(".oO { i am hostile. looking for a target. }");
|
|
// look around for a target
|
|
// TODO: use our vis rang einstead of 10!
|
|
for (y = lf->cell->y - 10; y <= lf->cell->y + 10; y++) {
|
|
for (x = lf->cell->x - 10; x <= lf->cell->x + 10; x++) {
|
|
c = getcellat(lf->cell->map, x, y);
|
|
// cell exists and we can see it?
|
|
if (c && haslos(lf, c)) {
|
|
// player there?
|
|
if (c->lf && (c->lf != lf) && isplayer(c->lf)) {
|
|
if (db) dblog(".oO { found a target - lfid %d (%s) ! }",c->lf->id, c->lf->race->name);
|
|
// target them!
|
|
addtempflag(lf->flags, F_TARGET, c->lf->id, NA, NA, NULL, AI_FOLLOWTIME);
|
|
// tell the player
|
|
if (haslos(player, lf->cell)) {
|
|
makenoise(lf, N_GETANGRY);
|
|
}
|
|
// then move towards them...
|
|
if (db) dblog(".oO { moving towards my new target }");
|
|
|
|
if (curwep) {
|
|
if (!movetowards(lf, c)) return;
|
|
} else {
|
|
if (db) dblog(".oO { won't move towards target - i have no weapon. }");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// are we friendly? if so, look for a target
|
|
f = hasflag(lf->flags, F_FRIENDLY);
|
|
if (f) {
|
|
int x,y;
|
|
if (db) dblog(".oO { i am friendly to the player. looking for a target. }");
|
|
// look around for a target
|
|
// TODO: use our vis rang einstead of 10!
|
|
for (y = lf->cell->y - 10; y <= lf->cell->y + 10; y++) {
|
|
for (x = lf->cell->x - 10; x <= lf->cell->x + 10; x++) {
|
|
c = getcellat(lf->cell->map, x, y);
|
|
// cell exists and we can see it?
|
|
if (c && haslos(lf, c)) {
|
|
// player there?
|
|
if (c->lf && (c->lf != lf) && !isplayer(c->lf)) {
|
|
if (db) dblog(".oO { found a target - lfid %d (%s) ! }",c->lf->id, c->lf->race->name);
|
|
// target them!
|
|
addtempflag(lf->flags, F_TARGET, c->lf->id, NA, NA, NULL, AI_FOLLOWTIME);
|
|
// then move towards them...
|
|
if (db) dblog(".oO { moving towards my new target }");
|
|
if (!movetowards(lf, c)) return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// just try to move in a random direction
|
|
if (db) dblog(".oO { default - moving randomly }");
|
|
dorandommove(lf, B_NOBADMOVES);
|
|
return;
|
|
|
|
// if we get this far, just wait
|
|
rest(lf, B_TRUE);
|
|
}
|
|
|
|
int aipickup(lifeform_t *lf, object_t *o) {
|
|
if (isedible(o)) {
|
|
return eat(lf, o);
|
|
} else {
|
|
return pickup(lf, o, o->amt);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
enum OBTYPE getattackspell(lifeform_t *lf) {
|
|
flag_t *f;
|
|
enum OBTYPE poss[MAXPILEOBS];
|
|
int nposs = 0;
|
|
int db = B_TRUE;
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
if (f->id == F_CANWILL) {
|
|
poss[nposs] = f->val[0];
|
|
nposs++;
|
|
} else if (f->id == F_CANCAST) {
|
|
objecttype_t *ot;
|
|
ot = findot(f->val[0]);
|
|
if (cancast(lf, f->val[0], NULL)) {
|
|
if (db) {
|
|
dblog(".oO { spell possibility: %s }", ot ? ot->name : "?unkownspell?");
|
|
|
|
}
|
|
poss[nposs] = f->val[0];
|
|
nposs++;
|
|
} else {
|
|
if (db) {
|
|
if (ot) {
|
|
dblog(".oO { can't cast %s right now (mpcost=%d, i have %d) }",
|
|
ot ? ot->name : "?unkownspell?",
|
|
getmpcost(ot->id), lf->mp);
|
|
} else {
|
|
dblog(".oO { can't cast ?unknownspell? right now }");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// select a random one
|
|
if (nposs > 0) {
|
|
int sel;
|
|
sel = rnd(0,nposs-1);
|
|
return poss[sel];
|
|
}
|
|
|
|
return OT_NONE;
|
|
}
|
|
|
|
|
|
object_t *hasbetterarmour(lifeform_t *lf, obpile_t *op) {
|
|
object_t *o;
|
|
|
|
for (o = op->first ; o ; o = o->next) {
|
|
if (isarmour(o)) {
|
|
object_t *curarm;
|
|
enum BODYPART bp;
|
|
flag_t *f;
|
|
// where does it go?
|
|
f = hasflag(o->flags, F_GOESON);
|
|
bp = f->val[0];
|
|
// is it better than what we have in that position?
|
|
curarm = getarmour(lf, bp);
|
|
if (isbetterwepthan(o, curarm)) {
|
|
return o;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
object_t *hasbetterweapon(lifeform_t *lf, obpile_t *op) {
|
|
object_t *bestwep, *o;
|
|
|
|
bestwep = getbestweapon(lf);
|
|
for (o = op->first ; o ; o = o->next) {
|
|
if (isweapon(o) && isbetterwepthan(o, bestwep) && canweild(lf, o)) {
|
|
return o;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// returns B_TRUE if we did something
|
|
int lookforobs(lifeform_t *lf, int covetsonly) {
|
|
object_t *o;
|
|
enum OBTYPE oid[MAXPILEOBS];
|
|
int noids = 0;
|
|
enum FLAG wantflag[MAXPILEOBS];
|
|
int nwantflags = 0;
|
|
int db = B_TRUE;
|
|
flag_t *f;
|
|
cell_t *c;
|
|
int n;
|
|
int i;
|
|
|
|
// construct a list of objects which we want
|
|
noids = 0;
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
if (f->id == F_WANTS) {
|
|
if (!covetsonly || (f->val[1] == B_COVETS)) {
|
|
oid[noids] = f->val[0];
|
|
noids++;
|
|
}
|
|
} else if (f->id == F_WANTSOBFLAG) {
|
|
if (!covetsonly || (f->val[1] == B_COVETS)) {
|
|
wantflag[nwantflags] = f->val[0];
|
|
nwantflags++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// current cell has an object we want?
|
|
o = hasobmulti(lf->cell->obpile, oid, noids);
|
|
if (o && (canpickup(lf, o) || caneat(lf,o)) ) {
|
|
if (db) dblog(".oO { current cell has ob i want (%s) }",o->type->name);
|
|
// try to pick it up
|
|
if (!aipickup(lf, o)) return B_TRUE;
|
|
if (db) dblog(".oO { pickup of %s failed, trying to eat! }",o->type->name);
|
|
if (!eat(lf, o)) return B_TRUE;
|
|
if (db) dblog(".oO { eating %s failed }",o->type->name);
|
|
}
|
|
// has an object with a flag we want?
|
|
for (n = 0; n < nwantflags; n++) {
|
|
o = hasobwithflag(lf->cell->obpile, wantflag[n]);
|
|
if (o && (canpickup(lf, o) || caneat(lf,o)) ) {
|
|
if (db) dblog(".oO { current cell has ob with flag i want (%s) }",o->type->name);
|
|
// try to pick it up
|
|
if (!aipickup(lf, o)) return B_TRUE;
|
|
if (db) dblog(".oO { pickup of %s with wantflag failed, trying to eat! }",o->type->name);
|
|
if (!eat(lf, o)) return B_TRUE;
|
|
if (db) dblog(".oO { eating %s with wantflag failed }",o->type->name);
|
|
|
|
}
|
|
}
|
|
// current cell has better weapon?
|
|
f = hasflag(lf->flags, F_WANTSBETTERWEP);
|
|
if (f ) {
|
|
if (!covetsonly || (f->val[1] == B_COVETS)) {
|
|
o = hasbetterweapon(lf, lf->cell->obpile);
|
|
if (o && canpickup(lf, o)) {
|
|
if (db) dblog(".oO { current cell has better weapon (%s) }",o->type->name);
|
|
// try to pick it up
|
|
if (!aipickup(lf, o)) return B_TRUE;
|
|
if (db) dblog(".oO { pickup of better wep %s failed! }",o->type->name);
|
|
}
|
|
}
|
|
}
|
|
// current cell has better armour?
|
|
f = hasflag(lf->flags, F_WANTSBETTERARM);
|
|
if (f ) {
|
|
if (!covetsonly || (f->val[1] == B_COVETS)) {
|
|
o = hasbetterarmour(lf, lf->cell->obpile);
|
|
if (o && canpickup(lf, o)) {
|
|
if (db) dblog(".oO { current cell has better armour (%s) }",o->type->name);
|
|
// try to pick it up
|
|
if (!aipickup(lf, o)) return B_TRUE;
|
|
if (db) dblog(".oO { pickup of better armour %s failed! }",o->type->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
// look around for objects which we want, if we don't already have a targetcell.
|
|
if (!hasflag(lf->flags, F_TARGETCELL)) {
|
|
if (db) dblog(".oO { no targetcell, so looking for remote objects }");
|
|
for (i = 0 ; i < lf->nlos; i++) {
|
|
int gothere = B_FALSE;
|
|
|
|
c = lf->los[i];
|
|
if (c != lf->ignorecell) {
|
|
o = hasobmulti(c->obpile, oid, noids);
|
|
if (o && (canpickup(lf, o) || caneat(lf,o)) ) {
|
|
if (db) dblog(".oO { remote cell has ob i want (%s). setting f_targetcell. }",o->type->name);
|
|
gothere = B_TRUE;
|
|
}
|
|
if (!gothere) {
|
|
// has an object with a flag we want?
|
|
for (n = 0; n < nwantflags; n++) {
|
|
o = hasobwithflag(c->obpile, wantflag[n]);
|
|
if (o && (canpickup(lf, o) || caneat(lf, o)) ) {
|
|
if (db) dblog(".oO { remote cell has ob with flag i want (%s) }", o->type->name);
|
|
gothere = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (!gothere) {
|
|
// remote cell has better weapon?
|
|
f = hasflag(lf->flags, F_WANTSBETTERWEP);
|
|
if (f) {
|
|
if (!covetsonly || (f->val[1] == B_COVETS)) {
|
|
|
|
o = hasbetterweapon(lf, c->obpile);
|
|
if (o && canpickup(lf, o)) {
|
|
if (db) dblog(".oO { remote cell has better weapon (%s). setting f_targetcell }",o->type->name);
|
|
gothere = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!gothere) {
|
|
// remote cell has better armour?
|
|
f = hasflag(lf->flags, F_WANTSBETTERARM);
|
|
if (f) {
|
|
if (!covetsonly || (f->val[1] == B_COVETS)) {
|
|
|
|
o = hasbetterarmour(lf, c->obpile);
|
|
if (o && canpickup(lf, o)) {
|
|
if (db) dblog(".oO { remote cell has better armour (%s). setting f_targetcell }",o->type->name);
|
|
gothere = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (gothere) {
|
|
// start walking towards target cell
|
|
addtempflag(lf->flags, F_TARGETCELL, c->x, c->y, NA, NULL, AI_FOLLOWTIME);
|
|
// forget about people we are attacking
|
|
killflagsofid(lf->flags, F_TARGET);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|