nexus/ai.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;
}