nexus/ai.c

1073 lines
28 KiB
C
Raw Normal View History

2010-12-02 12:17:54 +11:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2010-12-02 12:17:54 +11:00
#include "ai.h"
2011-02-01 06:16:13 +11:00
#include "attack.h"
2010-12-02 12:17:54 +11:00
#include "defs.h"
#include "flag.h"
2011-02-01 06:16:13 +11:00
#include "io.h"
2010-12-02 12:17:54 +11:00
#include "lf.h"
#include "map.h"
2011-02-01 06:16:13 +11:00
#include "move.h"
#include "nexus.h"
2010-12-02 12:17:54 +11:00
#include "objects.h"
2011-02-01 06:16:13 +11:00
#include "spell.h"
#include "text.h"
2010-12-02 12:17:54 +11:00
extern lifeform_t *player;
2011-02-01 06:16:13 +11:00
extern enum ERROR reason;
2010-12-02 12:17:54 +11:00
int wantdb = B_TRUE;
2011-03-04 12:22:36 +11:00
enum OBTYPE aigetattackspell(lifeform_t *lf, lifeform_t *victim) {
flag_t *f;
enum OBTYPE poss[MAXPILEOBS];
int nposs = 0;
int db = B_FALSE;
if (lfhasflag(lf, F_DEBUG)) {
db = B_TRUE;
}
for (f = lf->flags->first ; f ; f = f->next) {
2011-03-04 12:22:36 +11:00
if ((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;
sel = rnd(0,nposs-1);
return poss[sel];
}
return OT_NONE;
}
2011-03-16 15:45:46 +11:00
enum OBTYPE aigetfleespell(lifeform_t *lf) {
flag_t *f;
enum OBTYPE poss[MAXPILEOBS];
int nposs = 0;
int db = B_FALSE;
lifeform_t *fleefrom;
2011-03-16 15:45:46 +11:00
if (lfhasflag(lf, F_DEBUG)) {
db = B_TRUE;
}
f = lfhasflag(lf, F_FLEEFROM);
if (f) {
fleefrom = findlf(lf->cell->map, f->val[0]);
}
2011-03-16 15:45:46 +11:00
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++;
2011-03-16 15:45:46 +11:00
}
}
}
// 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;
2011-03-04 12:22:36 +11:00
}
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;
}
2011-03-04 12:22:36 +11:00
}
}
}
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)) {
2011-03-16 15:45:46 +11:00
// 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;
}
2010-12-02 12:17:54 +11:00
void aimove(lifeform_t *lf) {
2010-12-07 18:34:26 +11:00
int db = B_FALSE;
2011-02-01 06:16:13 +11:00
object_t *curwep,*bestwep, *o;
object_t *curgun,*bestgun;
2010-12-02 12:17:54 +11:00
flag_t *f;
2011-02-01 06:16:13 +11:00
//flag_t *nextf;
// lifeform_t *fleefrom = NULL;
2010-12-02 12:17:54 +11:00
lifeform_t *target;
2011-02-01 06:16:13 +11:00
enum BODYPART bp;
int x,y;
cell_t *c;
obpile_t *unarmedpile = NULL;
flag_t *unarmedflag = NULL;
2010-12-07 18:34:26 +11:00
/*
2010-12-07 18:34:26 +11:00
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;
}
2010-12-07 18:34:26 +11:00
2011-02-01 06:16:13 +11:00
if (db) {
char lfname[BUFLEN];
getlfname(lf, lfname);
dblog("AIMOVE: %s", lfname);
}
2010-12-07 18:34:26 +11:00
2011-02-01 06:16:13 +11:00
/*
2010-12-02 12:17:54 +11:00
// if lifeform isn't alive, skip turn
if (isdead(lf)) {
2010-12-07 18:34:26 +11:00
if (db) dblog(".oO { i am not alive, skipping turn. }");
2010-12-02 12:17:54 +11:00
taketime(lf, SPEED_DEAD);
return;
}
2011-02-01 06:16:13 +11:00
*/
2010-12-02 12:17:54 +11:00
2010-12-07 18:34:26 +11:00
// 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
2011-02-01 06:16:13 +11:00
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;
}
2011-03-04 12:22:36 +11:00
// 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;
}
}
}
2011-02-01 06:16:13 +11:00
// 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. }");
2010-12-07 18:34:26 +11:00
return;
}
// do we already have a target we are attacking?
2010-12-02 12:17:54 +11:00
f = hasflag(lf->flags, F_TARGET);
if (f) {
int targid;
2011-02-01 06:16:13 +11:00
int lastx,lasty;
2010-12-07 18:34:26 +11:00
if (db) dblog(".oO { i have a target... }");
2010-12-02 12:17:54 +11:00
targid = f->val[0];
2011-02-01 06:16:13 +11:00
lastx = f->val[1];
lasty = f->val[2];
2010-12-02 12:17:54 +11:00
target = findlf(lf->cell->map, targid);
if (target) {
2010-12-07 18:34:26 +11:00
if (db) dblog(".oO { my target is lfid %d (%s). }", targid, target->race->name);
2011-03-16 15:45:46 +11:00
if (cansee(lf, target)) {
2011-02-01 06:16:13 +11:00
int goingtomove = B_TRUE;
enum OBTYPE spell;
object_t *gun;
// reset F_TARGET lifetime to full.
f->lifetime = AI_FOLLOWTIME;
2011-03-04 12:22:36 +11:00
if (db) dblog(".oO { i can see my target (at %d,%d). might move towards it. }",target->cell->x,target->cell->y);
2011-02-01 06:16:13 +11:00
// remember last known loc
f->val[1] = target->cell->x;
f->val[2] = target->cell->y;
goingtomove = B_TRUE;
2011-03-04 12:22:36 +11:00
// drink boost potions
if (!useitemwithflag(lf, F_AIBOOSTITEM)) {
return;
}
2011-02-01 06:16:13 +11:00
// can we attack with spells (ie. ones which target the victim)?
2011-03-04 12:22:36 +11:00
spell = aigetattackspell(lf, target);
2011-02-01 06:16:13 +11:00
if (spell != OT_NONE) {
int spellfailed = B_FALSE;
lifeform_t *spelllf = NULL;
cell_t *spellcell = NULL;
object_t *spellob = NULL;
objecttype_t *st;
st = findot(spell);
2011-02-01 06:16:13 +11:00
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);
2011-02-01 06:16:13 +11:00
}
2011-03-04 12:22:36 +11:00
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)! }");
} else {
2011-02-01 06:16:13 +11:00
// spell succesful
return;
}
}
// if not adjacent, check for guns, wands, throwing
2011-03-04 12:22:36 +11:00
if (goingtomove && (getcelldist(lf->cell, target->cell) > 1) && haslof(lf, target->cell)) {
// can we attack by firing something?
gun = getfirearm(lf);
if (goingtomove && gun && getammo(lf)) {
2011-03-04 12:22:36 +11:00
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 {
2011-03-04 12:22:36 +11:00
if (db) dblog(".oO { shoot gun failed! reason = %d }", reason);
}
2011-03-04 12:22:36 +11:00
} else {
if (db) dblog(".oO { not firing out gun }");
}
// can we attack by throwing something?
if (goingtomove) {
// 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! }");
}
}
2011-02-01 06:16:13 +11:00
}
// do we have a wand we can zap?
if (lfhasflag(lf, F_FLEEFROM)) {
o = aigetwand(lf, F_AICASTTOFLEE);
} else {
o = aigetwand(lf, F_AICASTTOATTACK);
}
2011-02-01 06:16:13 +11:00
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)) {
2011-02-01 06:16:13 +11:00
// succesful
goingtomove = B_FALSE;
} else {
if (db) dblog(".oO { zap failed! }");
2011-02-01 06:16:13 +11:00
}
}
2011-02-01 06:16:13 +11:00
}
// 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);
}
}
2010-12-02 12:17:54 +11:00
} else {
2011-02-01 06:16:13 +11:00
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);
2010-12-02 12:17:54 +11:00
2011-02-01 06:16:13 +11:00
/*
2010-12-02 12:17:54 +11:00
// just try to move in a random direction
2010-12-07 18:34:26 +11:00
if (db) dblog(".oO { will move randomly }");
2011-02-01 06:16:13 +11:00
// dorandommove will call taketime() if it fails,
// so it's safe to just return
2010-12-07 18:34:26 +11:00
dorandommove(lf, B_NOBADMOVES);
2010-12-02 12:17:54 +11:00
return;
2011-02-01 06:16:13 +11:00
*/
2010-12-02 12:17:54 +11:00
}
}
2011-02-01 06:16:13 +11:00
}
// 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 i;
2011-02-01 06:16:13 +11:00
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];
2011-03-16 15:45:46 +11:00
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);
// target them!
addtempflag(lf->flags, F_TARGET, c->lf->id, c->x, c->y, NULL, AI_FOLLOWTIME);
// tell the player
2011-03-16 15:45:46 +11:00
if (cansee(player, lf)) {
makenoise(lf, N_GETANGRY);
2011-02-01 06:16:13 +11:00
}
// 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. }");
}
break;
2011-02-01 06:16:13 +11:00
}
}
}
}
// 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?
2011-03-16 15:45:46 +11:00
if (c && c->lf && (c->lf != lf) && cansee(lf, c->lf)) {
2011-02-01 06:16:13 +11:00
// player there?
2011-03-16 15:45:46 +11:00
if (!isplayer(c->lf)) {
2011-02-01 06:16:13 +11:00
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);
2011-02-01 06:16:13 +11:00
// then move towards them...
if (db) dblog(".oO { moving towards my new target }");
if (!movetowards(lf, c)) return;
2010-12-02 12:17:54 +11:00
}
}
}
}
2011-02-01 06:16:13 +11:00
}
2010-12-02 12:17:54 +11:00
2011-02-01 06:16:13 +11:00
// need to heal?
if ((lf->hp < lf->maxhp) ||
((lf->mp < getmaxmp(lf)) && lfhasflag(lf, F_RESTHEALMPAMT)) ) {
2011-03-04 12:22:36 +11:00
if (lf->hp < (lf->maxhp/2)) {
if (!useitemwithflag(lf, F_AIHEALITEM)) {
return;
}
}
if (db) dblog(".oO { resting }");
rest(lf, B_TRUE);
}
2011-03-16 15:45:46 +11:00
// need to train skills?
if (lf->skillpoints || lfhasflag(lf, F_STATGAINREADY)) {
if (canrest(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);
}
}
2011-02-01 06:16:13 +11:00
// just try to move in a random direction
if (db) dblog(".oO { default - moving randomly }");
dorandommove(lf, B_NOBADMOVES);
return;
2010-12-02 12:17:54 +11:00
// if we get this far, just wait
2011-02-01 06:16:13 +11:00
rest(lf, B_TRUE);
2010-12-02 12:17:54 +11:00
}
2011-02-01 06:16:13 +11:00
int aipickup(lifeform_t *lf, object_t *o) {
if (isedible(o)) {
return eat(lf, o);
} else {
return pickup(lf, o, o->amt, B_TRUE);
2011-02-01 06:16:13 +11:00
}
return B_FALSE;
}
2011-03-16 15:45:46 +11:00
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;
}
2011-02-01 06:16:13 +11:00
// 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;
if (lfhasflag(lf, F_DEBUG)) {
db = B_TRUE;
}
ot = findot(spellid);
// 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 {
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;
}
} else {
// invalid spell for this purpose
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 {
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 spellcasting conditions
if ((ot->id == OT_S_BLINDNESS) && isblind(victim)) {
return B_FALSE;
}
if ((ot->id == OT_S_HASTE) && lfhasflag(lf, F_FASTACT)) {
return B_FALSE;
}
if ((ot->id == OT_S_INVISIBILITY) && lfhasflag(victim, F_INVISIBLE)) {
return B_FALSE;
}
if ((ot->id == OT_S_PAIN) && lfhasflag(victim, F_PAIN)) {
return B_FALSE;
}
if ((ot->id == OT_S_HEALING) && (lf->hp >= lf->maxhp)) {
2011-03-04 12:22:36 +11:00
return B_FALSE;
}
if ((ot->id == OT_S_HEALINGMIN) && (lf->hp >= lf->maxhp)) {
2011-03-16 15:45:46 +11:00
return B_FALSE;
}
if ((ot->id == OT_S_PARALYZE) && lfhasflag(victim, F_PARALYZED)) {
return B_FALSE;
}
if ((ot->id == OT_S_SLEEP) && lfhasflag(victim, F_ASLEEP)) {
return B_FALSE;
}
if ((ot->id == OT_S_SLOW) && lfhasflag(victim, F_SLOWACT)) {
2011-03-04 12:22:36 +11:00
return B_FALSE;
}
if ((ot->id == OT_A_SPRINT) && lfhasflag(lf, F_SPRINTING)) {
2011-03-04 12:22:36 +11:00
return B_FALSE;
}
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)) {
return B_FALSE;
}
}
}
2011-03-04 12:22:36 +11:00
return B_TRUE;
}
2011-02-01 06:16:13 +11:00
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;
2010-12-02 12:17:54 +11:00
}
2011-02-01 06:16:13 +11:00
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;
}
2010-12-02 12:17:54 +11:00
}
2011-02-01 06:16:13 +11:00
return NULL;
2010-12-02 12:17:54 +11:00
}
2011-02-01 06:16:13 +11:00
// 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;
}
2011-02-01 06:16:13 +11:00
// 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)) ) {
2011-02-01 06:16:13 +11:00
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)) ) {
2011-02-01 06:16:13 +11:00
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 && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
2011-02-01 06:16:13 +11:00
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 && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
2011-02-01 06:16:13 +11:00
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 && !isdangerousob(o, lf, B_TRUE) && (canpickup(lf, o, 1) || caneat(lf,o)) ) {
2011-02-01 06:16:13 +11:00
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)) ) {
2011-02-01 06:16:13 +11:00
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 && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
2011-02-01 06:16:13 +11:00
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 && !isdangerousob(o, lf, B_TRUE) && canpickup(lf, o, 1)) {
2011-02-01 06:16:13 +11:00
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;
}
}
}
}
if (db) dblog(".oO { didn't find any obs i want }");
2011-02-01 06:16:13 +11:00
return B_FALSE;
}
2011-03-16 15:45:46 +11:00
// 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;
}