1600 lines
42 KiB
C
1600 lines
42 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.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;
|
|
|
|
int wantdb = B_TRUE;
|
|
|
|
void addignorecell(lifeform_t *lf, cell_t *c) {
|
|
if (c) {
|
|
addflag(lf->flags, F_IGNORECELL, c->x, c->y, NA, NULL);
|
|
}
|
|
}
|
|
|
|
void aiattack(lifeform_t *lf, lifeform_t *victim, int timelimit) {
|
|
int db = B_FALSE;
|
|
flag_t *f;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
}
|
|
|
|
// already targetting this lf?
|
|
f = lfhasflagval(lf, F_TARGET, victim->id, NA, NA, NULL);
|
|
if (f) {
|
|
if ((f->lifetime > 0) && (f->lifetime < timelimit)) {
|
|
f->lifetime = timelimit;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (db) {
|
|
char lfname[BUFLEN],vicname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getlfname(victim, vicname);
|
|
dblog(".oO { %s settings new target: %s }", lfname, vicname);
|
|
}
|
|
|
|
killflagsofid(lf->flags, F_TARGET);
|
|
killflagsofid(lf->flags, F_TARGETCELL);
|
|
|
|
|
|
if ((timelimit == PERMENANT) || (timelimit == UNLIMITED)) {
|
|
addflag(lf->flags, F_TARGET, victim->id, victim->cell->x, victim->cell->y, NULL);
|
|
} else {
|
|
addtempflag(lf->flags, F_TARGET, victim->id , victim->cell->x , victim->cell->y, NULL,timelimit);
|
|
}
|
|
// tell the player
|
|
if (cansee(player, lf)) {
|
|
makenoise(lf, N_GETANGRY);
|
|
}
|
|
|
|
// change allegience ?
|
|
if (!areenemies(lf, victim)) {
|
|
if (getallegiance(victim) == AL_FRIENDLY) {
|
|
if (!hasflag(lf->flags, F_HOSTILE)) {
|
|
addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
killflagsofid(lf->flags, F_FRIENDLY);
|
|
}
|
|
}
|
|
|
|
// no longer a pet
|
|
f = lfhasflagval(lf, F_PETOF, victim->id, NA, NA, NULL);
|
|
if (f) killflag(f);
|
|
}
|
|
|
|
enum OBTYPE aigetattackspell(lifeform_t *lf, lifeform_t *victim) {
|
|
flag_t *f;
|
|
enum OBTYPE poss[MAXPILEOBS];
|
|
int nposs = 0;
|
|
int db = B_FALSE;
|
|
int castok = B_TRUE;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
}
|
|
|
|
f = lfhasflag(lf, F_NEEDOBFORSPELLS);
|
|
if (f && !hasob(lf->pack, f->val[0])) {
|
|
castok = B_FALSE;
|
|
}
|
|
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
if ( (castok && (f->id == F_CANCAST)) ||
|
|
(f->id == F_CANWILL)) {
|
|
if (aispellok(lf, f->val[0], victim, F_AICASTTOATTACK)) {
|
|
poss[nposs] = f->val[0];
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// select a random one
|
|
if (nposs > 0) {
|
|
int sel;
|
|
if (db) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf,lfname);
|
|
dblog(".oO { %s i have %d valid spells/abils. using one. }", lfname, nposs);
|
|
}
|
|
sel = rnd(0,nposs-1);
|
|
return poss[sel];
|
|
}
|
|
|
|
return OT_NONE;
|
|
}
|
|
|
|
enum OBTYPE aigetfleespell(lifeform_t *lf) {
|
|
flag_t *f;
|
|
enum OBTYPE poss[MAXPILEOBS];
|
|
int nposs = 0;
|
|
int db = B_FALSE;
|
|
lifeform_t *fleefrom;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
}
|
|
|
|
f = lfhasflag(lf, F_FLEEFROM);
|
|
if (f) {
|
|
fleefrom = findlf(lf->cell->map, f->val[0]);
|
|
}
|
|
|
|
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
if ((f->id == F_CANCAST) || (f->id == F_CANWILL)) {
|
|
if (aispellok(lf, f->val[0], fleefrom, F_AICASTTOFLEE)) {
|
|
poss[nposs] = f->val[0];
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// select a random one
|
|
if (nposs > 0) {
|
|
int sel;
|
|
sel = rnd(0,nposs-1);
|
|
return poss[sel];
|
|
}
|
|
|
|
return OT_NONE;
|
|
}
|
|
|
|
void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose) {
|
|
int specialcase = B_FALSE;
|
|
flag_t *f;
|
|
|
|
// default - at victim.
|
|
if (spelllf) *spelllf = victim;
|
|
if (spellcell) *spellcell = victim->cell;
|
|
if (spellob) *spellob = NULL;
|
|
|
|
f = hasflag(spelltype->flags, purpose);
|
|
if (f) {
|
|
switch (f->val[0]) {
|
|
case ST_VICTIM:
|
|
// at victim.
|
|
if (spelllf) *spelllf = victim;
|
|
if (spellcell) *spellcell = victim->cell;
|
|
if (spellob) *spellob = NULL;
|
|
break;
|
|
case ST_ADJSELF: // cast at myself when next to victim
|
|
if (getcelldist(lf->cell, victim->cell) <= 1) {
|
|
if (spelllf) *spelllf = lf;
|
|
if (spellcell) *spellcell = lf->cell;
|
|
if (spellob) *spellob = NULL;
|
|
}
|
|
break;
|
|
case ST_ADJVICTIM: // cast at victim when next to victim
|
|
if (getcelldist(lf->cell, victim->cell) <= 1) {
|
|
if (spelllf) *spelllf = victim;
|
|
if (spellcell) *spellcell = victim->cell;
|
|
if (spellob) *spellob = NULL;
|
|
}
|
|
break;
|
|
case ST_SELF:
|
|
if (spelllf) *spelllf = lf;
|
|
if (spellcell) *spellcell = lf->cell;
|
|
if (spellob) *spellob = NULL;
|
|
break;
|
|
case ST_ANYWHERE:
|
|
if (spelllf) *spelllf = NULL;
|
|
if (spellcell) *spellcell = NULL;
|
|
if (spellob) *spellob = NULL;
|
|
break;
|
|
case ST_SPECIAL:
|
|
specialcase = B_TRUE;
|
|
break;
|
|
}
|
|
|
|
if (specialcase) {
|
|
if (spelltype->id == 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;
|
|
}
|
|
// should always be true since we check this in aispellok
|
|
if (nposs > 0) {
|
|
if (spellob) *spellob = poss[rnd(0,nposs-1)];
|
|
}
|
|
|
|
// cast spell at the victim
|
|
if (spelllf) *spelllf = victim;
|
|
if (spellcell) *spellcell = victim->cell;
|
|
} else if (spelltype->id == OT_S_CHARM) {
|
|
lifeform_t *l;
|
|
l = getnearbypeaceful(lf);
|
|
if (l) {
|
|
if (spelllf) *spelllf = l;
|
|
if (spellcell) *spellcell = l->cell;
|
|
if (spellob) *spellob = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
object_t *aigetwand(lifeform_t *lf, enum FLAG purpose) {
|
|
object_t *o;
|
|
object_t *poss[MAXPILEOBS];
|
|
int nposs = 0;
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
// wand with charges left?
|
|
if ((o->type->obclass->id == OC_WAND) && (getcharges(o) > 0)) {
|
|
// do we know how to use it?
|
|
if (hasflag(o->flags, purpose)) {
|
|
// TODO: if castatself, check whether we actually need to (ie. healing, invis, etc)
|
|
poss[nposs] = o;
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
if (nposs > 0) {
|
|
return poss[rnd(0,nposs-1)];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
flag_t *aigoto(lifeform_t *lf, cell_t *c, enum MOVEREASON why, void *data, int timelimit) {
|
|
int db = B_FALSE;
|
|
char whybuf[BUFLEN];
|
|
flag_t *f = NULL;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
}
|
|
|
|
|
|
if (lfhasflagval(lf, F_IGNORECELL, c->x, c->y, NA, NULL)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
dblog(".oO { %s cannot go to targecell %d,%d due to f_ignorecell flag }", lfname, c->x, c->y);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
if (db) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
dblog(".oO { %s going to targecell: %d, %d }", lfname, c->x, c->y);
|
|
}
|
|
|
|
killflagsofid(lf->flags, F_TARGET);
|
|
killflagsofid(lf->flags, F_TARGETCELL);
|
|
|
|
if (why == MR_LF) {
|
|
sprintf(whybuf, "%d", ((lifeform_t *)data)->id);
|
|
} else if (why == MR_OB) {
|
|
sprintf(whybuf, "%ld", ((object_t *)data)->id);
|
|
} else {
|
|
strcpy(whybuf, "");
|
|
}
|
|
|
|
if ((timelimit == PERMENANT) || (timelimit == UNLIMITED)) {
|
|
f = addflag(lf->flags, F_TARGETCELL, c->x, c->y, why, whybuf);
|
|
} else {
|
|
f = addtempflag(lf->flags, F_TARGETCELL, c->x, c->y, why, whybuf,timelimit);
|
|
}
|
|
return f;
|
|
}
|
|
|
|
void aimovetotargetcell(lifeform_t *lf, flag_t *f) {
|
|
int x,y;
|
|
cell_t *c;
|
|
int db = B_FALSE;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
}
|
|
|
|
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) {
|
|
// target cell adjacent and something in the way?
|
|
if (movetowards(lf, c, DT_ORTH)) {
|
|
// 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.
|
|
addignorecell(lf, c);
|
|
} else {
|
|
if (db) dblog(".oO { successfully walked towards f_targetcell. arrived at %d,%d }",lf->cell->x, lf->cell->y);
|
|
// 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.
|
|
addignorecell(lf, c);
|
|
}
|
|
}
|
|
|
|
int aipickup(lifeform_t *lf, object_t *o) {
|
|
if (isedible(o)) {
|
|
return eat(lf, o);
|
|
} else {
|
|
return pickup(lf, o, o->amt, B_TRUE);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int aiobok(lifeform_t *lf, object_t *o, lifeform_t *target) {
|
|
switch (o->type->id) {
|
|
case OT_POT_INVIS:
|
|
case OT_WAND_INVIS:
|
|
if (lfhasflag(target, F_INVISIBLE)) {
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
case OT_POT_INVULN:
|
|
if (lfhasflag(target, F_INVULNERABLE)) {
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
void aiturn(lifeform_t *lf) {
|
|
int db = B_FALSE;
|
|
object_t *curwep,*bestwep, *o;
|
|
int icanattack = B_FALSE;
|
|
object_t *curgun,*bestgun;
|
|
flag_t *f;
|
|
flag_t *mf;
|
|
//flag_t *nextf;
|
|
// lifeform_t *fleefrom = NULL;
|
|
lifeform_t *target,*newtarget;
|
|
enum BODYPART bp;
|
|
//cell_t *c;
|
|
lifeform_t *master = NULL;
|
|
enum IQBRACKET iqb;
|
|
int n;
|
|
|
|
|
|
/*
|
|
if (wantdb && haslos(player, lf->cell)) {
|
|
db = B_TRUE;
|
|
} else {
|
|
db = B_FALSE;
|
|
}
|
|
*/
|
|
|
|
if (wantdb && lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
} else {
|
|
db = B_FALSE;
|
|
}
|
|
|
|
if (db) {
|
|
char lfname[BUFLEN];
|
|
real_getlfname(lf, lfname, B_FALSE);
|
|
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;
|
|
}
|
|
*/
|
|
|
|
///////////////////////////////////////////
|
|
// info gathering
|
|
///////////////////////////////////////////
|
|
// remember our intelligence
|
|
iqb = getiqname(getattr(lf, A_IQ), NULL);
|
|
|
|
|
|
// are we a pet?
|
|
mf = lfhasflagval(lf, F_PETOF, NA, NA, NA, NULL);
|
|
if (mf && (getallegiance(lf) == AL_FRIENDLY)) {
|
|
master = findlf(lf->cell->map, mf->val[0]);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// housekeeping - weapon changes, drop/pickup,
|
|
// use items, etc
|
|
///////////////////////////////////////////////
|
|
|
|
///////////////////////////////////////////////
|
|
// healing
|
|
///////////////////////////////////////////////
|
|
|
|
// special cases
|
|
if ((lf->race->id == R_STIRGE) || (lf->race->id == R_LEECH)) {
|
|
if (ispeaceful(lf)) {
|
|
int sleepval = 18;
|
|
|
|
if (modcounter(lf->flags, 1) >= sleepval) {
|
|
taketime(lf, getactspeed(lf));
|
|
addflag(lf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// need to heal?
|
|
if (lf->hp < (lf->maxhp/2)) {
|
|
if (!useitemwithflag(lf, F_AIHEALITEM)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// burdened?
|
|
if (isburdened(lf)) {
|
|
object_t *o,*heaviest = NULL;
|
|
float hevweight = 0;
|
|
|
|
if (db) dblog(".oO { i am burdened }");
|
|
|
|
// drop our heaviest non-equipped object
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (!isequipped(o)) {
|
|
float thisweight;
|
|
thisweight = getobweight(o);
|
|
if (thisweight > hevweight) {
|
|
hevweight = thisweight;
|
|
heaviest = o;
|
|
}
|
|
}
|
|
}
|
|
if (heaviest) {
|
|
if (db) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, ALL);
|
|
dblog(".oO { i will drop %s to lower my burden }", obname);
|
|
}
|
|
if (!drop(heaviest, ALL)) {
|
|
return;
|
|
}
|
|
if (db) dblog(".oO { drop failed! }");
|
|
}
|
|
if (db) dblog(".oO { couldn't drop anything }");
|
|
}
|
|
|
|
// 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 ammo?
|
|
if (curgun) {
|
|
object_t *curammo;
|
|
curammo = getammo(lf);
|
|
if (!curammo) {
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
testammo(lf, o); // doesn't take any time.
|
|
if (getammo(lf)) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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 (canwear(lf, o, BP_NONE) && isbetterarmourthan(o, curarm)) {
|
|
// wear this armour instead
|
|
if (!wear(lf, o)) return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// now check whetehr we have ANY weapon
|
|
if (curwep || lfhasflag(lf, F_HASATTACK)) {
|
|
icanattack = B_TRUE;
|
|
}
|
|
|
|
// 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 (!lfhasflag(lf, F_HIDING)) {
|
|
if (db) dblog(".oO { looking for covetted objects... }");
|
|
if (lookforobs(lf, B_COVETS)) {
|
|
if (db) dblog(".oO { found covetted object. returning. }");
|
|
return;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// attacks
|
|
///////////////////////////////////////////////
|
|
|
|
// 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 (cansee(lf, target)) {
|
|
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). might 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;
|
|
|
|
// drink boost potions
|
|
if (!useitemwithflag(lf, F_AIBOOSTITEM)) {
|
|
return;
|
|
}
|
|
|
|
if (!lfhasflag(lf, F_HIDING)) {
|
|
objecttype_t *st;
|
|
// can we attack with spells (ie. ones which target the victim)?
|
|
// if target is adjacent, we will normally just attack rather than try a spell.
|
|
spell = aigetattackspell(lf, target);
|
|
st = findot(spell);
|
|
if ( (spell != OT_NONE) && // found a valid spell/ability to use
|
|
((getcelldist(lf->cell, target->cell) != 1) || // there is distance between us and target
|
|
(st->obclass->id == OC_ABILITY) || // OR this works from adjacent
|
|
(rnd(1,3) == 1)) // OR random chance of using anyway...
|
|
) {
|
|
int spellfailed = B_FALSE;
|
|
lifeform_t *spelllf = NULL;
|
|
cell_t *spellcell = NULL;
|
|
object_t *spellob = NULL;
|
|
if (db) {
|
|
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 {
|
|
// pick targets based on spell flags
|
|
aigetspelltarget(lf, st, target, &spelllf, &spellcell, &spellob, F_AICASTTOATTACK);
|
|
}
|
|
|
|
if (spellfailed) {
|
|
if (db) dblog(".oO { cast spell/ability failed (1)! }");
|
|
} else {
|
|
if (getschool(spell) == SS_ABILITY) {
|
|
spellfailed = useability(lf, spell, spelllf, spellcell);
|
|
} else {
|
|
spellfailed = castspell(lf, spell, spelllf, spellob, spellcell);
|
|
}
|
|
}
|
|
if (spellfailed) {
|
|
if (db) dblog(".oO { cast spell/ability tried but failed (2)! reason = %d }", reason);
|
|
} else {
|
|
// spell succesful
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if not adjacent, check for guns, wands, throwing
|
|
if (goingtomove && // if we are still planning on moving
|
|
(getcelldist(lf->cell, target->cell) > 1) && // and we're not adjacent to target
|
|
haslof(lf->cell, target->cell, LOF_NEED, NULL) && // and we have line of fire to them
|
|
(iqb > IQ_ANIMAL) ) { // and we are smarter than an animal
|
|
// can we attack by firing a weapon?
|
|
gun = getfirearm(lf);
|
|
if (goingtomove && gun && getammo(lf)) {
|
|
if (db) {
|
|
char gunname[BUFLEN];
|
|
getobname(gun, gunname, gun->amt);
|
|
dblog(".oO { will fire my gun (%s) at target. }",gunname);
|
|
}
|
|
setguntarget(lf, target);
|
|
if (!shoot(lf)) {
|
|
// succesful
|
|
return;
|
|
} else {
|
|
if (db) dblog(".oO { shoot gun failed! reason = %d }", reason);
|
|
}
|
|
} else {
|
|
if (db) dblog(".oO { not firing out gun }");
|
|
}
|
|
|
|
// can we attack by throwing something?
|
|
if (goingtomove && hasbp(lf, BP_HANDS)) {
|
|
o = getbestthrowmissile(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
|
|
return;
|
|
} else {
|
|
if (db) dblog(".oO { throw failed! }");
|
|
}
|
|
}
|
|
}
|
|
|
|
// do we have a wand we can zap?
|
|
if (lfhasflag(lf, F_HUMANOID) || hasbp(lf, BP_HANDS)) {
|
|
if (lfhasflag(lf, F_FLEEFROM)) {
|
|
o = aigetwand(lf, F_AICASTTOFLEE);
|
|
} else {
|
|
o = aigetwand(lf, F_AICASTTOATTACK);
|
|
}
|
|
if (o) {
|
|
objecttype_t *st;
|
|
cell_t *zapcell = NULL;
|
|
|
|
st = getlinkspell(o);
|
|
if (st) {
|
|
enum FLAG purpose;
|
|
if (lfhasflag(lf, F_FLEEFROM)) {
|
|
purpose = F_AICASTTOFLEE;
|
|
} else {
|
|
purpose = F_AICASTTOATTACK;
|
|
}
|
|
aigetspelltarget(lf, st, target, NULL, &zapcell, NULL, purpose);
|
|
} else {
|
|
// no linkspell - just zap it.
|
|
zapcell = NULL;
|
|
}
|
|
|
|
// zap it
|
|
if (db) dblog(".oO { will zap %s instead of moving }", o->type->name);
|
|
|
|
if (!operate(lf, o, zapcell)) {
|
|
// succesful
|
|
return;
|
|
} else {
|
|
if (db) dblog(".oO { zap failed! }");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end if !hiding
|
|
|
|
// do we have a valid melee attack?
|
|
if (!icanattack) {
|
|
if (db) dblog(".oO { won't move towards target - i have no weapon. }");
|
|
goingtomove = B_FALSE;
|
|
}
|
|
|
|
// if we are hiding, only attack when adjacent to maximise damage
|
|
if (lfhasflag(lf, F_HIDING) &&
|
|
(getcelldist(lf->cell, target->cell) != 1)) {
|
|
goingtomove = B_FALSE;
|
|
}
|
|
|
|
if (goingtomove) {
|
|
if (!movetowards(lf, target->cell, DT_ORTH)) {
|
|
// success
|
|
return;
|
|
} else {
|
|
if (db) dblog(".oO { move towards failed! - reason = %d }",reason);
|
|
}
|
|
}
|
|
} else {
|
|
cell_t *targcell;
|
|
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
|
|
targcell = getcellat(lf->cell->map, lastx, lasty);
|
|
if (targcell) {
|
|
if (!aigoto(lf, targcell, MR_LF, target, PERMENANT)) {
|
|
|
|
if (db) dblog(".oO { aigoto target's last known loc failed! }");
|
|
}
|
|
} else {
|
|
if (db) dblog(".oO { go to target's last known loc failed! }");
|
|
}
|
|
|
|
/*
|
|
// 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;
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// movement
|
|
///////////////////////////////////////////////
|
|
|
|
// do we have a target cell?
|
|
f = hasflag(lf->flags, F_TARGETCELL);
|
|
if (f) {
|
|
int valid = B_TRUE;
|
|
cell_t *c;
|
|
// is it still valid?
|
|
c = getcellat(lf->cell->map, f->val[0], f->val[1]);
|
|
if (!c) {
|
|
valid = B_FALSE;
|
|
} else if (f->val[2] == MR_LF) {
|
|
lifeform_t *who;
|
|
who = findlf(lf->cell->map, atoi(f->text));
|
|
// lf doesn't exist?
|
|
if (!who) {
|
|
valid = B_FALSE;
|
|
} else if (cansee(lf, who) && (lf->cell != c)) {
|
|
// can see them and they're not where we are going?
|
|
valid = B_FALSE;
|
|
}
|
|
} else if (f->val[2] == MR_OB) {
|
|
object_t *what;
|
|
what = findobidinmap(lf->cell->map, atol(f->text));
|
|
if (!what) {
|
|
valid = B_FALSE;
|
|
} else if (haslos(lf, c) && (what->pile->where != c)) {
|
|
// if you can see the cell and object isn't there anymore
|
|
valid = B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (valid) {
|
|
aimovetotargetcell(lf, f);
|
|
} else {
|
|
killflag(f);
|
|
}
|
|
return;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// look for something to do (objects, things
|
|
// to attack, etc)
|
|
///////////////////////////////////////////////
|
|
|
|
// 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. looking for one. }");
|
|
|
|
// look for any race which we hate
|
|
newtarget = NULL;
|
|
for (n = 0; n < lf->nlos; n++) {
|
|
lifeform_t *who;
|
|
who = lf->los[n]->lf;
|
|
if (who && cansee(lf, who)) {
|
|
if (lfhasflagval(lf, F_HATESRACE, who->race->id, NA, NA, NULL) ||
|
|
lfhasflagval(lf, F_HATESRACE, who->race->baseid, NA, NA, NULL) ) {
|
|
if (db) dblog(".oO { found a hated target - lfid %d (%s) ! }",who->id, who->race->name);
|
|
newtarget = who;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!newtarget) {
|
|
// now look for enemies
|
|
for (n = 0; n < lf->nlos; n++) {
|
|
lifeform_t *who;
|
|
who = lf->los[n]->lf;
|
|
if (who && cansee(lf, who)) {
|
|
if (areenemies(lf, who)) {
|
|
if (db) dblog(".oO { found an enemy target - lfid %d (%s) ! }",who->id, who->race->name);
|
|
newtarget = who;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newtarget) {
|
|
aiattack(lf, newtarget, AI_FOLLOWTIME);
|
|
// then move towards them...
|
|
if (db) dblog(".oO { moving towards my new target }");
|
|
|
|
if (icanattack) {
|
|
if (!movetowards(lf, newtarget->cell, DT_ORTH)) return;
|
|
} else {
|
|
if (db) dblog(".oO { won't move towards target - i have no weapon. }");
|
|
}
|
|
}
|
|
|
|
/*
|
|
// look for a target to attack based on our allegiance
|
|
switch (getallegiance(lf)) {
|
|
int i;
|
|
int x,y;
|
|
case AL_HOSTILE:
|
|
if (db) dblog(".oO { i am hostile. looking for a target. }");
|
|
|
|
// look around for a target
|
|
for (i = 0; i < lf->nlos; i++) {
|
|
cell_t *c;
|
|
c = lf->los[i];
|
|
|
|
if (c->lf && cansee(lf, c->lf)) {
|
|
if (isplayer(c->lf)) { // TODO: change to if isenemy ?
|
|
if (db) dblog(".oO { found a target - lfid %d (%s) ! }",c->lf->id, c->lf->race->name);
|
|
|
|
aiattack(lf, c->lf, AI_FOLLOWTIME);
|
|
// then move towards them...
|
|
if (db) dblog(".oO { moving towards my new target }");
|
|
|
|
if (icanattack) {
|
|
if (!movetowards(lf, c, DT_ORTH)) return;
|
|
} else {
|
|
if (db) dblog(".oO { won't move towards target - i have no weapon. }");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case AL_FRIENDLY: // are we friendly? if so, look for a target
|
|
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 && c->lf && (c->lf != lf) && cansee(lf, c->lf)) {
|
|
// player there?
|
|
//if (!isplayer(c->lf) && !ispeaceful(c->lf)) {
|
|
if (areenemies(lf, 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, c->x, c->y, NULL, AI_FOLLOWTIME);
|
|
// then move towards them...
|
|
if (db) dblog(".oO { moving towards my new target }");
|
|
if (!movetowards(lf, c, DT_ORTH)) return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
*/
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// training
|
|
///////////////////////////////////////////////
|
|
|
|
// need to train skills?
|
|
if (readytotrain(lf)) {
|
|
if (safetorest(lf)) {
|
|
// special case - monsters don't need to actually rest to gain
|
|
// skills, although they DO still need to wait until the player
|
|
// is out of sight.
|
|
enhanceskills(lf);
|
|
}
|
|
}
|
|
|
|
// pet movement - note that pets will only rest if their
|
|
// master is resting. the normal rest code underneath this section
|
|
// will never be called.
|
|
if (master) {
|
|
lifeform_t *master;
|
|
master = findlf(lf->cell->map, mf->val[0]);
|
|
if (master && cansee(lf, master)) {
|
|
// can see my master
|
|
if (isresting(master)) {
|
|
// rest as well.
|
|
rest(lf, B_TRUE);
|
|
return;
|
|
}
|
|
// - either move towards them or randomly
|
|
if (isadjacent(lf->cell, master->cell)) {
|
|
if (db) dblog(".oO { i can see my master - moving randomly }");
|
|
dorandommove(lf, B_NOBADMOVES);
|
|
} else {
|
|
// move towards master if not adjacent
|
|
if (db) dblog(".oO { i can see my master - moving towards them }");
|
|
if (movetowards(lf, master->cell, DT_ORTH)) {
|
|
// failed
|
|
if (db) dblog(".oO { failed. moving randomly }");
|
|
dorandommove(lf, B_NOBADMOVES);
|
|
} else {
|
|
// success
|
|
if (db) dblog(".oO { success. }");
|
|
}
|
|
}
|
|
return;
|
|
} else {
|
|
// try to move towards master's last known loc
|
|
if (mf->val[1] != NA) {
|
|
flag_t *movetoflag = NULL;
|
|
cell_t *newcell;
|
|
newcell = getcellat(lf->cell->map, mf->val[1], mf->val[2]);
|
|
|
|
if (newcell == lf->cell) {
|
|
int lastdir;
|
|
|
|
lastdir = getownerlastdir(lf);
|
|
if (lastdir != D_NONE) {
|
|
// try going in last known dir
|
|
if (db) dblog(".oO { cannot see my master and am at last known loc. trying last known dir (%s) }",getdirname(lastdir));
|
|
if (!trymove(lf, lastdir, B_TRUE)) {
|
|
if (db) dblog(".oO { ...successfully }");
|
|
killflagsofid(lf->flags, F_OWNERLASTDIR);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
if (db) dblog(".oO { cannot see my master - adding F_TARGETCELL for last known loc }");
|
|
movetoflag = aigoto(lf, newcell, MR_LF, master, PERMENANT);
|
|
}
|
|
|
|
|
|
if (movetoflag) {
|
|
aimovetotargetcell(lf, movetoflag);
|
|
} else {
|
|
if (db) dblog(".oO { cannot see my master and aigoto last known loc/dir failed. randommove. }");
|
|
dorandommove(lf, B_NOBADMOVES);
|
|
}
|
|
} else {
|
|
if (db) dblog(".oO { cannot see my master and dont have a last known location. randommove. }");
|
|
dorandommove(lf, B_NOBADMOVES);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// resting / healing
|
|
///////////////////////////////////////////////
|
|
|
|
// need to heal?
|
|
if (needstorest(lf, NULL) && safetorest(lf)) {
|
|
if (db) dblog(".oO { resting to heal }");
|
|
rest(lf, B_TRUE);
|
|
return;
|
|
}
|
|
|
|
// DEFAULT - try to move in a random direction
|
|
if (db) dblog(".oO { default - moving randomly }");
|
|
dorandommove(lf, B_NOBADMOVES); // this function will call rest() if we cant move
|
|
}
|
|
|
|
|
|
|
|
// is the spell 'spellid' okay for AI lifeform 'lf' to cast at 'victim', for given purpose.
|
|
// purpose could be F_AICASTTOFLEE or F_ATCASTTOATTACK
|
|
int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG purpose) {
|
|
objecttype_t *ot;
|
|
flag_t *f;
|
|
int db = B_FALSE;
|
|
int ok = B_FALSE;
|
|
int specialcase = B_FALSE;
|
|
int specificcheckok = B_FALSE;
|
|
int needlos = B_TRUE;
|
|
enum LOFTYPE needlof = LOF_NEED;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
}
|
|
ot = findot(spellid);
|
|
|
|
if (ot) {
|
|
flag_t *f;
|
|
f = hasflag(ot->flags, F_LOSLOF);
|
|
if (f) {
|
|
needlos = f->val[0];
|
|
needlof = f->val[1];
|
|
}
|
|
}
|
|
|
|
// enough mp etc?
|
|
if (!cancast(lf, spellid, NULL)) {
|
|
if (db) {
|
|
char why[BUFLEN];
|
|
if (reason == E_NOMP) {
|
|
strcpy(why, "not enough mp");
|
|
} else if (reason == E_TOOPOWERFUL) {
|
|
strcpy(why, "spell too powerful");
|
|
} else if (reason == E_NOTREADY) {
|
|
strcpy(why, "abil not ready");
|
|
} else if (reason == E_NEEDGRAB) {
|
|
strcpy(why, "needs grab");
|
|
} else {
|
|
strcpy(why, "unknown reason");
|
|
}
|
|
if (db) {
|
|
if (ot) {
|
|
dblog(".oO { can't cast %s right now (%s) (mpcost=%d, i have %d) }",
|
|
ot ? ot->name : "?unkownspell?", why,
|
|
getmpcost(lf, ot->id), lf->mp);
|
|
} else {
|
|
dblog(".oO { can't cast ?unknownspell? right now }");
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// boost spell already active?
|
|
if (hasflag(ot->flags, F_ONGOING)) {
|
|
if (lfhasflagval(lf, F_BOOSTSPELL, ot->id, NA, NA, NULL)) {
|
|
if (db) {
|
|
dblog(".oO { can't cast %s - it is already active.", ot ? ot->name : "?unkownspell?");
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
f = hasflag(ot->flags, purpose);
|
|
if (f) {
|
|
int range;
|
|
switch (f->val[0]) {
|
|
case ST_VICTIM:
|
|
range = getspellrange(spellid, getspellpower(lf, spellid));
|
|
if ((range == UNLIMITED) || (getcelldist(lf->cell, victim->cell) <= range)) {
|
|
if (db) {
|
|
dblog(".oO { spell possibility: %s }", ot ? ot->name : "?unkownspell?");
|
|
}
|
|
ok = B_TRUE;
|
|
}
|
|
break;
|
|
case ST_SELF:
|
|
case ST_ANYWHERE:
|
|
if (db) {
|
|
dblog(".oO { spell possibility: %s }", ot ? ot->name : "?unkownspell?");
|
|
}
|
|
ok = B_TRUE;
|
|
break;
|
|
case ST_ADJVICTIM:
|
|
if (getcelldist(lf->cell,victim->cell) == 1) {
|
|
if (ot->id == OT_A_GRAB) {
|
|
if (lfhasflag(lf, F_GRABBING) || lfhasflag(lf, F_GRABBEDBY) ||
|
|
lfhasflag(victim, F_GRABBING) || lfhasflag(victim, F_GRABBEDBY)) {
|
|
} else {
|
|
ok = B_TRUE;
|
|
}
|
|
} else if (ot->id == OT_A_CRUSH) {
|
|
// can only crush if you first grab something
|
|
if (lfhasflag(lf, F_GRABBING)) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else if (ot->id == OT_A_SUCKBLOOD) {
|
|
// must attach first
|
|
if (lfhasflag(lf, F_ATTACHEDTO)) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case ST_ADJSELF:
|
|
if (getcelldist(lf->cell,victim->cell) == 1) {
|
|
ok = B_TRUE;
|
|
}
|
|
break;
|
|
case ST_SPECIAL:
|
|
specialcase = B_TRUE;
|
|
break;
|
|
}
|
|
|
|
// now check for line of sight / fire
|
|
switch (f->val[0]) {
|
|
case ST_VICTIM:
|
|
case ST_ADJVICTIM:
|
|
if (needlos && (!victim || !cansee(lf, victim)) ) {
|
|
if (db) dblog(".oO { cant cast %s - no LOS to victim }", ot ? ot->name : "?unkownspell?");
|
|
return B_FALSE;
|
|
}
|
|
if (needlof && !haslof(lf->cell, victim->cell, needlof, NULL) ) {
|
|
if (db) dblog(".oO { cant cast %s - no LOF to victim }", ot ? ot->name : "?unkownspell?");
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
// invalid spell for this purpose
|
|
if (db) dblog(".oO { cant cast %s - not valid for given purpose }", ot ? ot->name : "?unkownspell?");
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (specialcase) {
|
|
if (ot->id == OT_S_TELEKINESIS) {
|
|
int i,nposs;
|
|
float maxweight;
|
|
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) {
|
|
ok = B_TRUE;
|
|
break;
|
|
}
|
|
if (ok) break;
|
|
}
|
|
}
|
|
} else if (ot->id == OT_S_CHARM) {
|
|
if (getnearbypeaceful(lf)) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else {
|
|
dblog(".oO { cant cast %s - specialcase conditions not yet coded }", ot ? ot->name : "?unkownspell?");
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (!ok) {
|
|
dblog(".oO { cant cast %s - targetting conditions cannot be met }", ot ? ot->name : "?unkownspell?");
|
|
return B_FALSE;
|
|
}
|
|
|
|
// now check whether it meets specific spell conditions
|
|
specificcheckok = B_TRUE;
|
|
if (ot->id == OT_S_AIRBLAST) {
|
|
// target must be in a straight compass dir from us
|
|
if ((victim->x != lf->x) ||
|
|
(victim->y != lf->y) ||
|
|
(abs(victim->x - lf->x) != abs(victim->y - lf->y)) ) {
|
|
} else {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
}
|
|
if (ot->id == OT_S_ANIMATEMETAL) {
|
|
object_t *wep;
|
|
wep = getweapon(lf);
|
|
if (!wep || !ismetal(wep->material->id)) {
|
|
specificcheckok = B_TRUE;
|
|
}
|
|
}
|
|
if ((ot->id == OT_S_BLINDNESS) && isblind(victim)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_S_DRAINLIFE) && isimmuneto(victim->flags, DT_NECROTIC)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if (ot->id == OT_A_FLURRY) {
|
|
if (!isdualweilding(lf)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
}
|
|
if ((ot->id == OT_S_HASTE) && (lfhasflag(victim, F_FASTACT) || lfhasflag(victim, F_FASTACTMOVE)) ) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if (ot->id == OT_A_HEAVYBLOW) {
|
|
if (!getweapon(lf)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
}
|
|
if (ot->id == OT_A_HIDE) {
|
|
if (lfhasflag(victim, F_HIDING)) {
|
|
specificcheckok = B_FALSE;
|
|
} else if (!safetorest(victim)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
}
|
|
if ((ot->id == OT_S_INVISIBILITY) && lfhasflag(victim, F_INVISIBLE)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_S_PAIN) && lfhasflag(victim, F_PAIN)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_S_HEALING) && (lf->hp >= lf->maxhp)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_S_HEALINGMIN) && (lf->hp >= lf->maxhp)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_S_PARALYZE) && lfhasflag(victim, F_PARALYZED)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_S_SLEEP) && lfhasflag(victim, F_ASLEEP)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_S_SLOW) && (lfhasflag(victim, F_SLOWACT) || lfhasflag(victim, F_SLOWACTMOVE)) ) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_A_SPRINT) && lfhasflag(lf, F_SPRINTING)) {
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
if ((ot->id == OT_A_SWOOP) || (ot->id == OT_A_CHARGE)) {
|
|
flag_t *willflag;
|
|
flag_t *srflag;
|
|
int srange = 5;
|
|
srflag = lfhasflag(lf, F_SWOOPRANGE);
|
|
if (srflag) {
|
|
srange = srflag->val[0];
|
|
}
|
|
|
|
willflag = lfhasflagval(lf, F_CANWILL, ot->id, NA, NA, NULL);
|
|
if (willflag) {
|
|
texttospellopts(f->text, NULL, NULL, NULL, &srange);
|
|
if (!srange) srange = 5;
|
|
}
|
|
|
|
|
|
if (!haslof(lf->cell, victim->cell, LOF_NEED,NULL)) {
|
|
specificcheckok = B_FALSE;
|
|
} else if (isimmobile(lf)) {
|
|
specificcheckok = B_FALSE;
|
|
} else if ((ot->id == OT_A_SWOOP) && !lfhasflag(lf, F_FLYING)) {
|
|
specificcheckok = B_FALSE;
|
|
} else if (getcelldist(lf->cell, victim->cell) > srange) {
|
|
specificcheckok = B_FALSE;
|
|
} else if (getcelldist(lf->cell, victim->cell) == 1) { // ie already adjacent
|
|
specificcheckok = B_FALSE;
|
|
}
|
|
}
|
|
if ((ot->id == OT_S_WARPWOOD)) {
|
|
specificcheckok = B_FALSE;
|
|
if (victim) {
|
|
object_t *oo;
|
|
for (oo = victim->pack->first ; oo ; oo = oo->next) {
|
|
if ((oo->type->material->id == MT_WOOD) && isequipped(oo)) {
|
|
specificcheckok = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ((ot->id == OT_S_WEAKEN)) {
|
|
flag_t *lff;
|
|
for (lff = lf->flags->first; lff ; lff = lff->next) {
|
|
if ((lff->id == F_ATTRMOD) && (lff->val[0] == A_STR) && (lff->obfrom == OT_S_WEAKEN)) {
|
|
specificcheckok = B_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!specificcheckok) {
|
|
dblog(".oO { cant cast %s - specific spell check failed }", ot ? ot->name : "?unkownspell?");
|
|
return B_FALSE;
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
int getownerlastdir(lifeform_t *lf) {
|
|
flag_t *odflag;
|
|
odflag = lfhasflag(lf, F_OWNERLASTDIR);
|
|
if (odflag) {
|
|
return odflag->val[0];
|
|
}
|
|
return D_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;
|
|
flag_t *f;
|
|
cell_t *c;
|
|
int n;
|
|
int i;
|
|
int db = B_FALSE;
|
|
|
|
if (wantdb && lfhasflag(lf, F_DEBUG)) {
|
|
db = B_TRUE;
|
|
} else {
|
|
db = B_FALSE;
|
|
}
|
|
|
|
// 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 && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || 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 && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || 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?
|
|
if (lfhasflag(lf, F_HUMANOID) && hasbp(lf, BP_WEAPON)) {
|
|
f = hasflag(lf->flags, F_WANTSBETTERWEP);
|
|
if (f) {
|
|
if (!covetsonly || (f->val[1] == B_COVETS)) {
|
|
o = hasbetterweapon(lf, lf->cell->obpile);
|
|
if (o && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lfhasflag(lf, F_HUMANOID)) {
|
|
// 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 && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
|
|
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 && !lfhasflagval(lf, F_IGNORECELL, c->x, c->y, NA, NULL)) {
|
|
o = hasobmulti(c->obpile, oid, noids);
|
|
if (o && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || 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 && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || caneat(lf, o)) ) {
|
|
if (db) dblog(".oO { remote cell has ob with flag i want (%s) }", o->type->name);
|
|
gothere = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (!gothere) {
|
|
if (lfhasflag(lf, F_HUMANOID) && hasbp(lf, BP_WEAPON)) {
|
|
// 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 && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
|
|
if (db) dblog(".oO { remote cell has better weapon (%s). setting f_targetcell }",o->type->name);
|
|
gothere = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!gothere) {
|
|
if (lfhasflag(lf, F_HUMANOID)) {
|
|
// 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 && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
|
|
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
|
|
if (aigoto(lf, c, MR_OB, o, AI_FOLLOWTIME)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (db) dblog(".oO { didn't find any obs i want }");
|
|
return B_FALSE;
|
|
}
|
|
|
|
// try to use an item with the given flag on ourself.
|
|
// returns B_FALSE if successful
|
|
int useitemwithflag(lifeform_t *lf, enum FLAG whichflag) {
|
|
object_t *o;
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (hasflag(o->flags, whichflag)) {
|
|
if (aiobok(lf, o, lf)) {
|
|
if (o->type->obclass->id == OC_POTION) {
|
|
if (canquaff(lf, o)) {
|
|
quaff(lf, o);
|
|
return B_FALSE;
|
|
}
|
|
} else if (o->type->obclass->id == OC_SCROLL) {
|
|
if (!readsomething(lf, o)) {
|
|
return B_FALSE;
|
|
}
|
|
} else if ((o->type->obclass->id == OC_WAND) && getcharges(o)) {
|
|
// if wand, use it on ourself
|
|
if (!operate(lf, o, lf->cell)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// failed to use an item
|
|
return B_TRUE;
|
|
}
|
|
|
|
|