#include #include #include #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 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); } } } } 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) { 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; } 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; } void aigoto(lifeform_t *lf, cell_t *c, int timelimit) { int db = B_FALSE; if (lfhasflag(lf, F_DEBUG)) { db = B_TRUE; } 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 ((timelimit == PERMENANT) || (timelimit == UNLIMITED)) { addflag(lf->flags, F_TARGETCELL, c->x, c->y, NA, NULL); } else { addtempflag(lf->flags, F_TARGETCELL, c->x, c->y, NA, NULL,timelimit); } } void aimove(lifeform_t *lf) { 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 (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; } */ // 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 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 (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 (goingtomove && (getcelldist(lf->cell, target->cell) == 1)) { } // 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); if ( (spell != OT_NONE) && // found a valid spell/ability to use ((getcelldist(lf->cell, target->cell) != 1) || (rnd(1,3) == 1)) ) { int spellfailed = B_FALSE; lifeform_t *spelllf = NULL; cell_t *spellcell = NULL; object_t *spellob = NULL; objecttype_t *st; st = findot(spell); 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 && (getcelldist(lf->cell, target->cell) > 1) && haslof(lf, target->cell, B_FALSE, NULL)) { // 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 goingtomove = B_FALSE; } 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 goingtomove = B_FALSE; } else { if (db) dblog(".oO { zap 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, 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) { aigoto(lf, targcell, PERMENANT); } 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; */ } } } // 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, 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. lf->ignorecell = 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. 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 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 (curwep) { 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)) { 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; } // need to heal? if ((lf->hp < lf->maxhp) || ((lf->mp < getmaxmp(lf)) && lfhasflag(lf, F_RESTHEALMPAMT)) ) { if (lf->hp < (lf->maxhp/2)) { if (!useitemwithflag(lf, F_AIHEALITEM)) { return; } } if (db) dblog(".oO { resting }"); rest(lf, B_TRUE); } // 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); } } // 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, 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; } // 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; 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 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_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_SWOOP) { flag_t *srflag; int srange = 5; srflag = lfhasflag(lf, F_SWOOPRANGE); if (srflag) { srange = srflag->val[0]; } if (!haslof(lf, victim->cell, LOF_NEED,NULL)) { specificcheckok = B_FALSE; } else if (isimmobile(lf) || !lfhasflag(lf, F_FLYING)) { specificcheckok = B_FALSE; } else if (getcelldist(lf->cell, victim->cell) > srange) { specificcheckok = B_FALSE; } } if ((ot->id == OT_S_HASTE) && lfhasflag(lf, F_FASTACT)) { 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)) { specificcheckok = B_FALSE; } if ((ot->id == OT_A_SPRINT) && lfhasflag(lf, F_SPRINTING)) { specificcheckok = 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)) { 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; } 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 && (c != lf->ignorecell)) { 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 aigoto(lf, c, AI_FOLLOWTIME); return B_TRUE; } } } } 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; }