#define _GNU_SOURCE #include #include #include #include #include #include #include #include "ai.h" #include "attack.h" #include "defs.h" #include "flag.h" #include "god.h" #include "io.h" #include "lf.h" #include "map.h" #include "move.h" #include "nexus.h" #include "objects.h" #include "save.h" #include "shops.h" #include "spell.h" #include "text.h" extern double presin[]; extern double precos[]; extern FILE *logfile; extern int enteringmap; extern int playerorigalignment; extern map_t *firstmap; extern race_t *firstrace, *lastrace; extern raceclass_t *firstraceclass, *lastraceclass; extern behaviour_t *firstbehaviour, *lastbehaviour; extern job_t *firstjob, *lastjob; extern skill_t *firstskill, *lastskill; extern poisontype_t *firstpoisontype,*lastpoisontype; extern objecttype_t *objecttype; extern lifeform_t *player; extern int totalraces; extern glyph_t playerglyph; extern glyph_t tempglyph; extern int maxmonhitdice; extern npcname_t *npcname; extern int numnpcnames; extern int needredraw; extern prompt_t prompt; extern enum GAMEMODE gamemode; extern long nextlfid; extern WINDOW *msgwin; extern WINDOW *statwin; extern int statdirty; extern int needredraw; extern int loading; extern int obdb; extern lifeform_t *godlf[]; extern int ngodlfs; extern enum ERROR reason; extern void *rdata; extern object_t *retobs[MAXPILEOBS+1]; extern int retobscount[MAXPILEOBS+1]; extern int nretobs; extern condset_t ccwalkable; // for xplist race_t **raceposs; int *xpposs; int xplistlen; // notime has three options: // B_FALSE = no special effects // B_MAYBE = prevent taketime from doing anything // B_TRUE = prevent taketime from doing anything // don't announce flag gain/loss int notime = B_FALSE; /* void add_subjob_alignment_restrictions(flagpile_t *fp, enum SUBJOB sj) { if (sj == SJ_PALADIN) { addflag(fp, F_ALIGNMENT, AL_NONE, NA, NA, "g"); } else if (sj == SJ_NECROMANCER) { addflag(fp, F_ALIGNMENT, AL_NONE, NA, NA, "e"); } } */ void autoweild(lifeform_t *lf) { object_t *bestwep,*bestfirearm; object_t *o,*firearm; int donesecondary = B_FALSE; int pretimespent; int pass; pretimespent = lf->timespent; if (isplayer(lf)) { dblog("db: calling autoweild for player."); } // weild weapons if required bestwep = getbestweapon(lf); if (bestwep) { weild(lf, bestwep); } bestfirearm = getbestfirearm(lf); if (bestfirearm) { weild(lf, bestfirearm); } // weild armour/2nd weapons if required, // and mark other weapons as secondary for (pass = 0; pass < 2; pass++) { for (o = lf->pack->first ; o ; o = o->next) { if (isweapon(o) && !isequipped(o) && getskill(lf, SK_TWOWEAPON) && !getequippedob(lf->pack, BP_SECWEAPON) && canweild(lf, o)) { weild(lf, o); } else if (!donesecondary && isweapon(o) && !isequipped(o)) { addflag(o->flags, F_SECONDARY, B_TRUE, NA, NA, NULL); donesecondary = B_TRUE; } else { if (canwear(lf, o, BP_NONE)) { if ((pass == 0) && !hasflag(o->flags, F_UNDERCLOTHING)) { // only equip underclothing on the first pass. } else { wear(lf, o); } } } } } // start using ammo if required //if (isplayer(lf)) { firearm = getfirearm(lf); if (firearm) { o = getrandomammo(lf); if (o) { loadfirearm(NULL, firearm, o); } } //} // make sure monsters have ammo for their weapons if (!isplayer(lf) && firearm && !getammo(firearm)) { objecttype_t *ot; // make some ammo ot = getrandomammofor(firearm, B_FALSE); if (ot) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "1-5 %s",ot->name); o = addob(lf->pack, buf); loadfirearm(NULL, firearm, o); } } // make sure it doesn't take any time lf->timespent = pretimespent; } int appearsrandomly(enum RACE rid) { race_t *r; r = findrace(rid); if (!r) return B_FALSE; if (!hasflag(r->flags, F_RARITY)) { return B_FALSE; } else if (hasflagval(r->flags, F_RARITY, NA, F_UNIQUE, NA, NULL)) { return B_FALSE; } return B_TRUE; } void awardxpfor(lifeform_t *killed, float pct) { int xpval,xpeach,i,nposs; int initialxp; lifeform_t *poss[MAXCANDIDATES], *l; int n; initialxp = calcxp(killed); assert(initialxp < 10000); assert(initialxp >= 0); xpval = (int) ((float)initialxp * (pct/100)); assert(xpval < 10000); assert(xpval >= 0); // find all allies on the map nposs = 0; n = 0; for (l = killed->cell->map->lf ; l ; l = l->next) { if ((l != killed) && (getallegiance(l) == AL_FRIENDLY)) { poss[nposs] = l; nposs++; } } if (nposs == 0) return; xpeach = xpval / nposs; for (i = 0; i < nposs; i++) { gainxp(poss[i], xpeach); } } void bleed(lifeform_t *lf, int splatter) { char obname[BUFLEN]; if (lf->cell->type->solid) return; if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) return; if (hasequippedobid(lf->pack, OT_AMU_BLOOD)) return; getbloodobname(lf->flags, obname, B_ANYONE); if (strlen(obname) > 0) { object_t *o; o = addob(lf->cell->obpile, obname); setbloodfrom(o, lf); if (splatter) { object_t *bloodob[MAXCANDIDATES]; int i,nbloodobs = 0; // this call will populate retobs[]... addobsinradius(lf->cell, 1, DT_COMPASS, obname, B_TRUE, B_NOCENTRE, NULL, bloodob, NULL, &nbloodobs); for (i = 0; i < nbloodobs; i++) { setbloodfrom(bloodob[i], lf); } } } } int bleedfrom(lifeform_t *lf, enum BODYPART bp, int splatter) { if (willbleedfrom(lf, bp)) { bleed(lf, splatter); return B_FALSE; } return B_TRUE; } int bpcantakearmour(lifeform_t *lf, enum BODYPART bp) { int i; // unnaturally shaped limbs? for (i = 0; i < lf->race->nbodyparts; i++) { if ((lf->race->bodypart[i].id == bp) && !lf->race->bodypart[i].armourok) { return B_FALSE; } } return B_TRUE; } void breakgrabs(lifeform_t *lf, int fromme, int tome, int wantannounce) { flag_t *f; lifeform_t *alf; if (fromme) { f = lfhasflag(lf, F_GRABBING); if (f) { lifeform_t *grabee; grabee = findlf(NULL, f->val[0]); assert(grabee); real_killflagsofid(grabee->flags, F_GRABBEDBY, wantannounce); real_killflagsofid(lf->flags, F_GRABBING, wantannounce); } real_killflagsofid(lf->flags, F_ATTACHEDTO, wantannounce); } if (tome) { f = lfhasflag(lf, F_GRABBEDBY); if (f) { lifeform_t *graber; graber = findlf(NULL, f->val[0]); assert(graber); real_killflagsofid(graber->flags, F_GRABBING, wantannounce); real_killflagsofid(lf->flags, F_GRABBEDBY, wantannounce); } for (alf = lf->cell->map->lf ; alf ; alf = alf->next) { f = lfhasflagval(alf, F_ATTACHEDTO, lf->id, NA, NA, NULL); if (f) { real_killflag(f, wantannounce); } } } } void breakaitargets(lifeform_t *lf, int onlylowerlev) { lifeform_t *l; for (l = lf->cell->map->lf ; l ; l = l->next) { flag_t *f; if (l == lf) continue; if (!onlylowerlev || (gettr(l) <= gettr(lf)) ) { f = lfhasflagval(l, F_TARGETLF, lf->id, NA, NA, NULL); if (f) killflag(f); f = lfhasflagval(l, F_TARGETCELL, lf->cell->x, lf->cell->y, NA, NULL); if (f) killflag(f); } } } long calcscore(lifeform_t *lf) { flag_t *winner; long points = 0; object_t *o; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; if (lfhasflag(lf, F_NOSCORE)) { return 0; } winner = hasflag(player->flags, F_WINNER); // objects for (o = lf->pack->first ; o ; o = o->next) { // only partial points if you didn't win // the game. if (winner) { points += getobpoints(o); } else if (isknown(o)) { points += (getobpoints(o)/4); } else { points += (getobpoints(o)/6); } } // donated items etc getflags(lf->flags, retflag, &nretflags, F_SCOREBONUS, F_NONE); for (i = 0; i < nretflags; i++) { long thisamt; thisamt = (retflag[i]->val[1] * 65535) + retflag[i]->val[0]; points += thisamt; } // points for xp points += (lf->xp / 10); winner = hasflag(player->flags, F_WINNER); if (winner) { if (winner->val[0] == WT_DEMIGOD) { points *= 13; } else if (winner->val[0] == WT_GOD) { points *= 23; } } return points; } // figure out how much xp a race is worth int calcxp(lifeform_t *lf) { float multiplier = 1; float xpval = 0; flag_t *f; int db = B_FALSE; int i; float xpconstant = 1; enum RARITY rr; int mylev; if (lfhasflag(lf, F_DEBUG)) { db = B_TRUE; } if (db) dblog("calcxp: calculating xpval for %s",lf->race->name); f = lfhasflag(lf, F_XPMULTIPLY); if (f) { multiplier = f->val[0]; } f = lfhasflag(lf, F_XPVAL); if (f) { if (db) dblog("calcxp: got F_XPVAL, forcing result to %d\n",f->val[0]); return f->val[0] * multiplier; } mylev = gettr(lf); xpval = mylev * 3; if (db) dblog("calcxp: base xpval from threatlev %f is %d\n",gettr(lf), (int)xpval); // rarity? getracerarity(H_ALL, lf->race->id, &rr); for (i = 0;i < rr; i++) { xpval += (mylev * 3); } /* // attack // - get average attack damage avgdam = getavgdam(lf, B_TRUE); if (db) dblog("calcxp: avg damage dealt is %0.1f",avgdam); // defense // -- hitdice f = lfhasflag(lf, F_HITDICE); if (f) { maxhdroll = flagtomaxhp(f); } else { maxhdroll = 4; } defence += (maxhdroll * lf->level); //defence /= 2; // -- evasion ? getflags(lf->race->flags, retflag, &nretflags, F_EVASION, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->val[0] > 0) { defence += f->val[0]; } } // -- armour f = hasflag(lf->race->flags, F_ARMOURRATING); if (f && (f->val[0] > 0)) { defence += pctof(150, f->val[0]); } if (db) dblog("calcxp: DEFENCE IS %0.1f",defence); // spells getflags(lf->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_CANCAST) { objecttype_t *ot; int power,level; ot = findot(f->val[0]); power = getspellpower(lf, f->val[0]); level = getspelllevel(f->val[0]); spells += (power * level); if (db) dblog("cancast %s: == power %d * spelllevel %d = %d",ot->name, power, level, power*level); } else if (f->id == F_CANWILL) { objecttype_t *ot; // is this a spell? ot = findot(f->val[0]); if (ot && ot->obclass->id == OC_SPELL) { int power,level; power = getspellpower(lf, ot->id); level = getspelllevel(ot->id); spells += (power * level); if (db) dblog("canwill spell %s: == power %d * spelllevel %d = %d",ot->name, power, level, power*level); } else { char damstr[BUFLEN]; int needgrab = B_FALSE; objecttype_t *ot; flag_t *af; ot = findot(f->val[0]); af = hasflag(ot->flags, F_XPVAL); if (af) { float thisamt = af->val[0]; if (f->val[2] == NA) { if (db) dblog("canwill ability %s == %0.1f",ot->name, thisamt); } else { thisamt /= f->val[2]; if (db) dblog("canwill ability %s every %d turns == %0.1f",ot->name, f->val[2], thisamt); } spells += thisamt; } // other special cases switch (f->val[0]) { case OT_A_SWOOP: if (db) dblog("canwill SWOOP - increasing avg dam from %0.1f to %0.1f.", avgdam, avgdam*1.1); avgdam *= 1.1; // attack bonus! break; default: break; } // get dam from text texttospellopts(f->text, "dam:", damstr, "needgrab:", &needgrab, NULL ); if (strlen(damstr)) { int dice,sides,mod,min,max; float thisavg; texttodice(damstr, &dice,&sides,&mod); dicetotext(dice,sides,mod, &min, &max, damstr, NULL); thisavg = ((min+max)/2); if (db) dblog("canwill ability %s causes dam %0.1f",ot->name, thisavg); // adjust if you have to grab first if (needgrab) { thisavg /= 2; if (db) dblog(" (adjusted to %0.1f due to needing to grab)", thisavg); } // adjust based on how often we can do it if (f->val[2] != NA) { thisavg /= f->val[2]; if (db) dblog(" (can do every %d turns. --> %0.1f)", f->val[2], thisavg); } if (ot->id == OT_A_GRAB) { // assume can only grab once in the 10 turns. thisavg /= 10; } avgdam += thisavg; } } } } // -- avg damage in 10 turns if (db) dblog("calcxp: avg damage dealt (with abilities) is %0.1f",avgdam); avgdam *= 10; offense = avgdam; if (db) dblog("calcxp: ATTACKVAL IS %0.1f",offense); // extra 'spells' if (hasflag(lf->race->flags, F_INDUCEFEAR)) { spells += 5; } if (hasflag(lf->race->flags, F_DRAINONHIT)) { spells += 15; } // TOTAL: xpval = offense + defence + spells; f = lfhasflag(lf, F_XPMOD); if (f) { xpval += f->val[0]; if (db) dblog("calcxp: F_XPMOD is %d",f->val[0]); } */ if (multiplier > 1) { xpval *= multiplier; if (db) dblog("calcxp: mulitplier takes val to %0.1f",xpval); } if (db) dblog("calcxp: xpval: %0.1f --> %0.1f",xpval, xpval * xpconstant); xpval *= xpconstant; if (db) dblog("calcxp: ------ FINAL XPVAL: %d ------",(int)xpval); if (db) dblog(""); return (int) xpval; } int calcxprace(enum RACE rid) { cell_t c; int xpval; map_t m; lifeform_t *lf; createfakes(&m, &c); // make a fake lf redrawpause(); lf = addlf(&c, rid, 1); xpval = calcxp(lf); killlf(lf); redrawresume(); killfakes(&m, &c); return xpval; } void callguards(lifeform_t *caller, lifeform_t *victim) { lifeform_t *l; for (l = caller->cell->map->lf ; l ; l = l->next) { if (!isplayer(l) && (l != caller) && (l != victim) && lfhasflag(l, F_GUARD)) { aiattack(l, victim, PERMENANT); if (isplayer(victim)) { addflag(l->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); } } } } int canattack(lifeform_t *lf) { /* if (!getstamina(lf)) { reason = E_NOSTAM; return B_FALSE; } else */ if (lfhasflag(lf, F_STUNNED)) { reason = E_STUNNED; return B_FALSE; } else if (lfhasflag(lf, F_NOATTACK)) { reason = E_IMPOSSIBLE; return B_FALSE; } return B_TRUE; } // need ob is populated with with the obejcttype needed. // returns TRUE if we can build it. int canbuild(lifeform_t *lf, objecttype_t *ot, char *needtext, enum OBTYPE *needob, int *numneed) { char requiretext[BUFLEN]; objecttype_t *tempot; int i; char *template = " (required %d x %s)"; enum OBTYPE localneed = OT_NONE; enum OBTYPE altob[MAXCANDIDATES]; int localnum = 0,nalt = 0; object_t *o; for (i = 0; i < MAXCANDIDATES; i++) { altob[i] = OT_NONE; } if (needtext) { sprintf(needtext, "%s", ot->name); } switch (ot->id) { case OT_ARROW: localneed = OT_WOODSHARD; localnum = 1; altob[0] = OT_STICK; nalt = 1; break; case OT_BOLT: localneed = OT_METALCHUNK; localnum = 1; break; case OT_BARRICADE: localneed = OT_IRONSTAFF; localnum = 2; break; case OT_RUBBLE: localneed = OT_STONE; localnum = 20; break; case OT_FENCEWOOD: localneed = OT_WOODPLANK; localnum = 1; break; case OT_TRAPDOORFALL: localneed = OT_NONE; localnum = 0; break; case OT_TRAPNEEDLEP: localneed = OT_NEEDLE; localnum = 1; break; case OT_TRAPFIRE: localneed = OT_POT_RUM; localnum = 1; break; case OT_TRAPMINE: localneed = OT_GRENADE; localnum = 1; break; case OT_TRAPARROW: localneed = OT_ARROW; localnum = 1; break; case OT_TRAPROCK: localneed = OT_STONE; localnum = 1; break; case OT_TRAPTRIP: localneed = OT_ROPE; localnum = 1; break; default: break; } if (needob) *needob = localneed; if (numneed) *numneed = localnum; if (localneed == OT_NONE) { return B_TRUE; } o = hasob(lf->pack, localneed); if (!o && nalt) { int i; for (i = 0; i < nalt; i++ ){ o = hasob(lf->pack, altob[i]); if (o) { localneed = altob[i]; if (needob) *needob = localneed; break; } } } if (o && (o->amt >= localnum)) { tempot = findot(localneed); sprintf(requiretext, template, localnum, tempot->name); if (needtext) strcat(needtext, requiretext); return B_TRUE; } return B_FALSE; } // can lf cast spell/use ability 'oid' int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) { int castable = B_FALSE; flag_t *f; objecttype_t *ot; int stamcost = 0; if (mpcost) { // default to base cost *mpcost = getmpcost(lf, oid); } if (lfhasflag(lf, F_SILENCED)) { reason = E_SILENCED; return B_FALSE; } if (lfhasflag(lf, F_STUNNED)) { reason = E_STUNNED; return B_FALSE; } if (isprone(lf) && (oid != OT_A_FEIGNDEATH)) { reason = E_PRONE; return B_FALSE; } if (isswimming(lf) && (getskill(lf, SK_SWIMMING) < PR_EXPERT)) { if (!isaquatic(lf)) { reason = E_SWIMMING; return B_FALSE; } } if (isclimbing(lf)) { reason = E_CLIMBING; return B_FALSE; } if (hasjob(lf, J_PALADIN)) { object_t *o; // using cursed weapons/armour? no spells. for (o = lf->pack->first ; o ; o = o->next) { if (isequipped(o) && iscursed(o)) { if (isweapon(o)) { reason = E_PALADIN; return B_FALSE; } else if (isarmour(o)) { reason = E_PALADIN2; return B_FALSE; } } } } reason = E_OK; ot = findot(oid); f = lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL); if (f) { int needgrab = B_FALSE; // flying with broken/destroyed wings? if ((oid == OT_S_FLIGHT) && (f->lifetime == FROMRACE)) { if (lfhasflagval(lf, F_INJURY, IJ_WINGTORN, NA, NA, NULL)) { reason = E_INJURED; return B_FALSE; } else if (lfhasflagval(lf, F_NOBODYPART, BP_WINGS, NA, NA, NULL)) { reason = E_INJURED; return B_FALSE; } } // get canwill opts texttospellopts(f->text, "needgrab:", &needgrab, NULL); // no mp cost. if (mpcost) *mpcost = 0; // ability ready? if (f->val[1] == f->val[2]) { castable = B_TRUE; } else { reason = E_NOTREADY; return B_FALSE; } if (needgrab || hasflag(ot->flags, F_NEEDSGRAB)) { if (!lfhasflag(lf, F_GRABBING)) { reason = E_NEEDGRAB; return B_FALSE; } } } else if ((f = lfhasflagval(lf, F_CANCAST, oid, NA, NA, NULL)) != NULL) { int cost,power; // can only cast innate racial spelsl? if ((ot->obclass->id == OC_SPELL) && lfhasflag(lf, F_NOSPELLS)) { if (f->lifetime != FROMRACE) { reason = E_NOSPELLS; return B_FALSE; } } // need >animal intelligence to cast spells if (ot->obclass->id == OC_SPELL) { if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= IQ_ANIMAL) { if (!lfhasflag(lf, F_CASTWITHOUTIQ)) { reason = E_LOWIQ; return B_FALSE; } } } // for mosnters: spell ready? if (f->val[1] == f->val[2]) { castable = B_TRUE; } else { reason = E_NOTREADY; return B_FALSE; } // how powerful is this spell? power = getspellpower(lf, oid); if (power <= 0) { reason = E_TOOPOWERFUL; return B_FALSE; } // how much mp does it take to cast this? if (isplayer(lf)) { cost = getmpcost(lf, oid); } else { cost = 0; } if (mpcost) *mpcost = cost; if (lf->mp >= cost) { castable = B_TRUE; } else { reason = E_NOMP; return B_FALSE; } } // do we have enough stamina to do this? stamcost = getstamcost(lf, oid); if (stamcost && (getstamina(lf) < stamcost) && !lfhasflag(lf, F_NOSTAM)) { // exception: you can always stop flying if ((oid == OT_A_FLY) && isflyingwithwings(lf)) { // ok } else { reason = E_NOSTAM; return B_FALSE; } } return castable; } int cancook(lifeform_t *lf, recipe_t *rec, enum ERROR *reason) { // skill high enough? if (!canmakerecipe(lf, rec)) { if (reason) *reason = E_TOOHARD; return B_FALSE; } // do we have the ingredients? if (getingredients(lf->pack, rec, NULL, NULL, NULL, NULL, B_FALSE)) { if (reason) *reason = E_NOOB; return B_FALSE; } return B_TRUE; } int canclimb(lifeform_t *lf, enum ERROR *reason) { cell_t *cc; if (!isorthogonal(lf->facing)) { if (reason) *reason = E_BADCLIMBDIR2; return B_FALSE; } cc = getcellindir(lf->cell, lf->facing); if (isclimbing(lf)) { if (reason) *reason = E_CLIMBING; return B_FALSE; } else if (!cc) { if (reason) *reason = E_BADCLIMBDIR; return B_FALSE; } else if (!cc->type->solid) { if (hasobwithflagval(cc->obpile, F_PIT, D_DOWN, NA, NA, NULL)) { // ok } else if (hasobwithflag(cc->obpile, F_CLIMBOBSTACLE)) { } else { if (reason) *reason = E_BADCLIMBDIR; return B_FALSE; } } else if (cc->lf) { if (reason) *reason = E_LFINWAY; return B_FALSE; } else if (isswimming(lf)) { if (reason) *reason = E_SWIMMING; return B_FALSE; } else if (isimmobile(lf)) { if (reason) *reason = E_CANTMOVE; return B_FALSE; } else if (isburdened(lf) || lfhasflag(lf, F_GRAVBOOSTED)) { if (reason) *reason = E_TOOHEAVY; return B_FALSE; } else if ((getraceclass(lf) != RC_HUMANOID) && !getskill(lf, SK_CLIMBING)) { if (reason) *reason = E_NOABIL; return B_FALSE; } return B_TRUE; } int candrink(lifeform_t *lf, object_t *o) { // undead won't drink if (getraceclass(lf) == RC_UNDEAD) { reason = E_UNDEAD; return B_FALSE; } if (!isdrinkable(o)) { reason = E_WRONGOBTYPE; return B_FALSE; } if (getlfmaterial(lf) == MT_GAS) { reason = E_INSUBSTANTIAL; return B_FALSE; } if (facecovered(lf)) { reason = E_FACECOVERED; return B_FALSE; } if (lfhasflag(lf, F_VEGETARIAN) && hasflag(o->flags, F_ISMEAT)) { reason = E_VEGETARIAN; return B_FALSE; } reason = E_OK; return B_TRUE; } // if false, returns why you can't eat it in 'reason' int caneat(lifeform_t *lf, object_t *o) { // undead will only eat fleshy corpses if (getraceclass(lf) == RC_UNDEAD) { if (hasflag(o->flags, F_CORPSEOF) && (o->material->id == MT_FLESH)) { return B_TRUE; } reason = E_UNDEAD; return B_FALSE; } // note: must do this check _before_ call to isedible()! if (lfhasflagval(lf, F_CANEATMATERIAL, o->material->id, NA, NA, NULL)) { return B_TRUE; } if (!isedible(o)) { reason = E_WRONGOBTYPE; return B_FALSE; } if (!isplayer(lf) && lfhasflag(lf, F_RAGE)) { reason = E_WONT; return B_FALSE; } // ring of hunger overrides most eating checks if (!hasequippedobid(lf->pack, OT_RING_HUNGER)) { // ai won't eat bad food if (!isplayer(lf) && isrotting(o)) { reason = E_WONT; return B_FALSE; } if ((o->type->id == OT_CORPSE) || (o->type->id == OT_HEAD)) { flag_t *f; f = hasflag(o->flags, F_CORPSEOF); if (f) { race_t *r; r = findrace(f->val[0]); // same race? let the player do so. if (!isplayer(lf)) { if ((r->id == lf->race->id) || (r->baseid == lf->race->baseid)) { enum RACECLASS rc; rc = getraceclass(lf); // race which doens't eat its own? if ((rc == RC_ANIMAL) || (rc == RC_HUMANOID)) { // no cannibulism! reason = E_NOCANNIBUL; return B_FALSE; } } } } } if (lfhasflag(lf, F_VEGETARIAN) && hasflag(o->flags, F_ISMEAT)) { reason = E_VEGETARIAN; return B_FALSE; } if (lfhasflag(lf, F_CARNIVORE) && !hasflag(o->flags, F_ISMEAT)) { reason = E_CARNIVORE; return B_FALSE; } if (lfhasflag(lf, F_NAUSEATED)) { reason = E_NAUSEATED; return B_FALSE; } } if (lfhasflag(lf, F_PARTVEGETARIAN) && hasflag(o->flags, F_ISMEAT)) { if (gethungerlevel(gethungerval(player)) < H_PECKISH) { reason = E_PARTVEGETARIAN; return B_FALSE; } } // ai lifeforms won't eat tainted food unless they can handle it if (!isplayer(lf)) { if (hasflag(o->flags, F_TAINTED) && !lfhasflag(lf, F_CANEATRAW)) { reason = E_WONT; return B_FALSE; } } if (facecovered(lf)) { reason = E_FACECOVERED; return B_FALSE; } return B_TRUE; } // can lf try to evade attacker (or anything, if attacker isn't given) int canevade(lifeform_t *lf, lifeform_t *attacker) { if (isprone(lf)) return B_FALSE; if (isexhausted(lf)) return B_FALSE; if (attacker && !cansee(lf, attacker)) { return B_FALSE; } return B_TRUE; } int canexorcise(lifeform_t *lf, lifeform_t *target, int modifier) { int maxlevel; if (lfhasflag(target, F_UNIQUE)) return B_FALSE; maxlevel = gettr(lf) + getskill(lf, SK_LORE_DEMONS) + modifier; if (gettr(target) <= maxlevel) { return B_TRUE; } return B_FALSE; } int canhaverandombehaviour(lifeform_t *lf) { if (lfhasflag(lf, F_BEHAVIOUR)) return B_FALSE; if (isundead(lf)) return B_FALSE; if (isgod(lf)) return B_FALSE; if (getraceclass(lf) == RC_HUMANOID) { return B_TRUE; } return B_FALSE; } // ie. will sound from 'dest' reach the ears of 'lf' int canhear(lifeform_t *lf, cell_t *dest, int volume, int *numwalls) { int numpixels; int i; int x1,y1; int sounddist; int x2,y2; map_t *map; cell_t *retcell[MAXRETCELLS]; int celldist; if (numwalls) *numwalls = 0; if (!lf) return B_FALSE; if (!dest) return B_FALSE; if (!lf->cell) return B_FALSE; // can't hear when dead. if (isdead(lf)) return B_FALSE; // can't hear if you are deaf or have no ears if (!hasbp(lf, BP_EARS)) return B_FALSE; if (isdeaf(lf)) return B_FALSE; // can't hear when training or enraged if (lfhasflag(lf, F_RAGE)) return B_FALSE; if (lfhasflag(lf, F_TRAINING)) return B_FALSE; // can't hear noises from other maps if (lf->cell->map != dest->map) return B_FALSE; celldist = getcelldist(lf->cell, dest); // for player only: // can't hear if you have a hostile mosnter next to you // and you're not blind. // (you're too engrossed in the battle) /* if (isplayer(lf) && isinbattle(lf)) { if (celldist != 1) return B_FALSE; } */ map = dest->map; x1 = lf->cell->x; y1 = lf->cell->y; x2 = dest->x; y2 = dest->y; calcbresnham(map, x1, y1, x2, y2, retcell, &numpixels ); // get sound celldistance //sounddist = gethearingrange(lf); sounddist = getsounddist(volume); if (sounddist < celldist) { // sound won't travel far enough return B_FALSE; } for (i = 0; i < numpixels ; i++) { cell_t *cell; cell = retcell[i]; // don't need to move out of the last one if ((cell->x == x2) && (cell->y == y2)) { break; } // you can always hear your own cell if (i != 0) { // solid cells decrease hearing range if (cell->type->solid) { if (numwalls) (*numwalls)++; sounddist--; } // magic barriers stop all sound if (hasob(cell->obpile, OT_MAGICBARRIER)) { return B_FALSE; } // hearing range decreases by one sounddist--; } if (sounddist <= 0) { return B_FALSE; } } return B_TRUE; } // int cannotmove(lifeform_t *lf) { if (lfhasflag(lf, F_PARALYZED) || lfhasflag(lf, F_FROZEN) || lfhasflag(lf, F_ASLEEP)) { return B_TRUE; } return B_FALSE; } int canlearn(lifeform_t *lf, enum SKILL skid) { if (ismaxedskill(lf, skid)) return B_FALSE; if (lfhasflagval(lf, F_NOSKILL, skid, NA, NA, NULL)) { return B_FALSE; } if (lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL)) { return B_TRUE; } return B_FALSE; } int canmakerecipe(lifeform_t *lf, recipe_t *rec) { if (getskill(lf, SK_COOKING) >= rec->ningredients) { return B_TRUE; } return B_FALSE; } int canopendoors(lifeform_t *lf) { if (lf) { if (!lfhasflag(lf, F_HUMANOID)) return B_FALSE; if (!hasbp(lf, BP_HANDS)) return B_FALSE; } return B_TRUE; } int canoperate(lifeform_t *lf, object_t *o, enum ERROR *why) { flag_t *f; if (why) *why = E_OK; if (lfhasflag(lf, F_RAGE)) { if (why) *why = E_RAGE; return B_FALSE; } if (lfhasflag(lf, F_STUNNED)) { if (why) *why = E_STUNNED; return B_FALSE; } if (!lfhasflag(lf, F_HUMANOID) || !hasbp(lf, BP_HANDS)) { if (!hasflag(o->flags, F_OPERWITHOUTHANDS)) { if (why) *why = E_NOHANDS; return B_FALSE; } } f = hasflag(o->flags, F_OPERNEEDSKILL); if (f) { if (getskill(lf, f->val[0]) < f->val[1]) { if (why) *why = E_NOSKILL; return B_FALSE; } } return B_TRUE; } int canpickup(lifeform_t *lf, object_t *o, int amt) { reason = E_OK; if (amt == ALL) { amt = o->amt; } if (hasflag(o->flags, F_NOPICKUP)) { reason = E_NOPICKUP; return B_FALSE; } if (isimpassableob(o, lf, getlfsize(lf))) { reason = E_TOOBIG; return B_FALSE; } if (lf) { if (isstuck(lf) == o) { reason = E_STUCK; return B_FALSE; } if (getobsize(o) > getlfsize(lf)) { reason = E_TOOBIG; return B_FALSE; } if (lfhasflag(lf, F_NOPACK)) { reason = E_NOPACK; return B_FALSE; } if (lfhasflag(lf, F_GRAVBOOSTED)) { reason = E_GRAVBOOSTED; return B_FALSE; } if ((getlfmaterial(lf) == MT_GAS) || lfhasflag(lf, F_NONCORPOREAL)) { reason = E_INSUBSTANTIAL; return B_FALSE; } // too heavy to lift? //max = getlfweight(lf, B_NOOBS) * 2; // twice your body weight if (getobweight(o) + getobpileweight(lf->pack) > (getmaxcarryweight(lf)*2)) { reason = E_TOOHEAVY; return B_FALSE; } // space in pack? if (!obfits(o, lf->pack)) { reason = E_NOSPACE; return B_FALSE; } } return B_TRUE; } // - can't turn into monsters which aren't randomly generated. // - can't turn into unique monsters // - can't turn into undead monsters int canpolymorphto(enum RACE rid) { race_t *r; r = findrace(rid); if (!r) return B_FALSE; if (!appearsrandomly(rid)) return B_FALSE; if (hasflag(r->flags, F_UNDEAD) || (r->raceclass->id == RC_UNDEAD)) return B_FALSE; return B_TRUE; } int canpush(lifeform_t *lf, object_t *o, int dir) { cell_t *obcell, *dstcell; float adjweight; reason = E_OK; if ((getlfmaterial(lf) == MT_GAS) || lfhasflag(lf, F_NONCORPOREAL)) { reason = E_INSUBSTANTIAL; return B_FALSE; } // check object weight vs. lf weight // adjust object weight for cell sliperriness. adjweight = getobmass(o); if (o->pile->where) { adjweight -= getslipperyness(o->pile->where, NULL); limitf(&adjweight, 0, NA); } if (adjweight > getmaxpushweight(lf)) { reason = E_TOOHEAVY; return B_FALSE; } // check cell behind object obcell = o->pile->where; if (!obcell) { reason = E_FAILED; return B_FALSE; } dstcell = getcellindir(obcell, dir); if (!cellwalkable(NULL, dstcell, NULL)) { reason = E_FAILED; return B_FALSE; } if (isswimming(lf) || isclimbing(lf)) { reason = E_FAILED; return B_FALSE; } return B_TRUE; } // can lf reach victim to attack them? int canreach(lifeform_t *lf, lifeform_t *victim, int *reachpenalty) { int diff,lffootheight,victimfootheight; // default if (reachpenalty) *reachpenalty = 0; // harder to hit flying/levitating enemies, or ones climbing // (ie they are higher than you) lffootheight = getfeetheight(lf); victimfootheight = getfeetheight(victim); diff = victimfootheight - (getlfsize(lf) + lffootheight); if (diff > 0) { if (reachpenalty) *reachpenalty = diff; return B_FALSE; } return B_TRUE; } // can lf reach victim's bodypart bp? int canreachbp(lifeform_t *lf, lifeform_t *victim, enum BODYPART bp) { int lffeet, vicfeet; int bpheight,topthresh,bottomthresh; // can't reach them at all! if (!canreach(lf, victim, NULL)) { return B_FALSE; } // get height of your feet lffeet = getfeetheight(lf); vicfeet = getfeetheight(victim); switch (bp) { // upper case BP_EYES: case BP_HEAD: case BP_HEAD2: case BP_HEAD3: case BP_EARS: case BP_NECK: case BP_SHOULDERS: bpheight = vicfeet + getlfsize(victim); break; case BP_WEAPON: case BP_SECWEAPON: case BP_HANDS: case BP_RIGHTFINGER: case BP_LEFTFINGER: bpheight = vicfeet + pctof(75, getlfsize(victim)); break; case BP_NONE: // this counts as body case BP_BODY: case BP_WAIST: case BP_WINGS: case BP_TAIL: bpheight = vicfeet + pctof(50, getlfsize(victim)); break; case BP_LEGS: case BP_BACKLEGS: case BP_FRONTLEGS: bpheight = vicfeet + pctof(25, getlfsize(victim)); break; case BP_FEET: bpheight = vicfeet; break; } // your reach is defined as anywhere between your feet and just over your // head. flying creatures have more leeway. if (isairborne(lf, NULL)) { topthresh = 3; bottomthresh = 2; } else { topthresh = 0; bottomthresh = 0; } if ((bpheight < lffeet - bottomthresh) || (bpheight > (lffeet + getlfsize(lf)) + topthresh)) { return B_FALSE; } return B_TRUE; } int canreachroof(lifeform_t *lf) { if (getlfsize(lf) >= SZ_HUGE) { return B_TRUE; } if (lfhasflag(lf, F_LEVITATING) || lfhasflag(lf, F_FLYING) || lfhasflag(lf, F_CLIMBING)) { return B_TRUE; } return B_FALSE; } int cansee(lifeform_t *viewer, lifeform_t *viewee) { return cansee_real(viewer, viewee, B_TRUE); } // if uselos is false, then this function means "COULD viewer see // viewee assuming they have los?" int cansee_real(lifeform_t *viewer, lifeform_t *viewee, int uselos) { object_t *o; flag_t *f; int xray = 0; int dist; int tremordist,smelldist,darkvisdist; int invisible = B_FALSE; if (gamemode < GM_GAMESTARTED) { return B_TRUE; } if (!viewee->cell || !viewer->cell) { return B_FALSE; } f = hasflag(viewer->flags, F_XRAYVIS); if (f) { xray = f->val[0]; } else { xray = 0; } dist = getcelldist(viewer->cell, viewee->cell); f = lfhasflag(viewer, F_TREMORSENSE); if (f && !isairborne(viewee, NULL) && !isairborne(viewer, NULL)) { if (f->val[0] > 0) { tremordist = f->val[0]; } else { tremordist = dist; } } else { tremordist = -1; } f = lfhasflag(viewer, F_ENHANCESMELL); if (f) { if (f->val[0] > 0) { smelldist = f->val[0]; } else { smelldist = dist; } } else { smelldist = -1; } darkvisdist = getnightvisrange(viewer); // viewer asleep? f = lfhasflag(viewer, F_ASLEEP); if (f) { if (f->val[1] != ST_MEDITATING) { // can only 'see' yourself if (viewee != viewer) { return B_FALSE; } } } /* if (areallies(player, viewee) && !isplayer(viewee) && canhear(player, viewee->cell, 3)) { return B_TRUE; } */ if (uselos) { // no line of sight? if (!haslos(viewer, viewee->cell)) { if ((dist <= tremordist)) { } else if ((dist <= smelldist)) { } else { return B_FALSE; } } } // viewee is invisible? if (lfhasflag(viewee, F_INVISIBLE)) { invisible = B_TRUE; } // cloak of shadows? /* o = getequippedob(viewee->pack, BP_SHOULDERS); if (o && hasflagval(o->flags, F_HASBRAND, BR_SHADOWS, NA, NA, NULL)) { if (!islit(viewee->cell)) { invisible = B_TRUE; } } */ f = lfhasflag(viewee, F_SHADOWED); if (f && (dist > f->val[0])) { if (dist <= darkvisdist) { } else if ((dist <= tremordist)) { } else if ((dist <= smelldist)) { } else { return B_FALSE; } } // viewee hiding? if (ishidingfrom(viewee, viewer)) { invisible = B_TRUE; } if (invisible) { if (lfhasflag(viewer, F_SEEINVIS)) { } else if ((dist <= tremordist)) { } else if ((dist <= smelldist)) { } else { return B_FALSE; } } // something obscuring them? for (o = viewee->cell->obpile->first ; o ; o = o->next) { f = hasflag(o->flags, F_BLOCKSVIEW); if (f) { if (!lfhasflagval(viewer, F_CANSEETHROUGHMAT, o->material->id, NA, NA, NULL)) { if (!xray || (xray < dist)) { return B_FALSE; } } } // viewee underwater and more than 1 cell away? if ((getobdepth(o, viewee) >= DP_HEAD) && (dist > 1)) { if (!isairborne(viewee, NULL)) { return B_FALSE; } } } return B_TRUE; } int cansleep(lifeform_t *lf) { if (lfhasflag(lf, F_NOSLEEP)) { return B_FALSE; } return B_TRUE; } int canstudyspell(lifeform_t *lf, enum OBTYPE spellid) { //object_t *o; //int hasbook = B_FALSE; enum SPELLSCHOOL school; school = getspellschool(spellid); switch (school) { case SS_NATURE: case SS_ALLOMANCY: case SS_LIFE: case SS_MENTAL: return B_FALSE; default: break; } if (!lfhasflagval(lf, F_CANSTUDY, school, NA, NA, NULL)) return B_FALSE; /* // must have a spellbook to record the spell in. for (o = lf->pack->first ; o ; o = o->next) { if (o->type->id == OT_SPELLBOOK) { if (hasflagval(o->flags, F_LINKSCHOOL, school, NA, NA, NULL)) { hasbook = B_TRUE; } } else if (o->type->id == OT_GRIMOIRE) { hasbook = B_TRUE; } } if (!hasbook) return B_FALSE; */ if (lf->skillpoints < getspelllevel(spellid)) return B_FALSE; // too high a level if (!spelllearnable(lf, spellid)) return B_FALSE; // too high powered ? return B_TRUE; } int canthrow(lifeform_t *lf, object_t *o, enum ERROR *why) { flag_t *f; if (why) *why = E_OK; if (lfhasflag(lf, F_RAGE)) { if (why) *why = E_RAGE; return B_FALSE; } if (lfhasflag(lf, F_STUNNED)) { if (why) *why = E_STUNNED; return B_FALSE; } f = hasflag(o->flags, F_EQUIPPED); if (f && (f->val[0] != BP_WEAPON)) { if (why) *why = E_EQUIPPED; return B_FALSE; } if (getmaxthrowrange(lf, o) < 1) { if (why) *why = E_LOWSTR; return B_FALSE; } return B_TRUE; } int canuseweapons(lifeform_t *lf) { if (lfhasflag(lf, F_HUMANOID)) { return B_TRUE; } return B_TRUE; } // where == BP_NONE means "can i wear it anywhere?' int canwear(lifeform_t *lf, object_t *o, enum BODYPART where) { int i,nretflags,nparts = 0; //object_t *oo; flag_t *f, *retflag[MAXCANDIDATES]; enum BODYPART possbp[MAXBODYPARTS]; reason = E_OK; if (where != BP_NONE) { if (!hasbp(lf, where)) { reason = E_NOBP; return B_FALSE; } if (!bpcantakearmour(lf, where)) { reason = E_DOESNTFIT; return B_FALSE; } } if (!armourfits(lf, o, &reason)) { return B_FALSE; } // already equipped? if (hasflag(o->flags, F_EQUIPPED)) { reason = E_ALREADYUSING; return B_FALSE; } // wearable at all? if (!iswearable(o) || !lfhasflag(lf, F_HUMANOID)) { reason = E_IMPOSSIBLE; return B_FALSE; } // paladin? if (hasjob(lf, J_PALADIN)) { if (!isblessed(o) || !o->blessknown) { if (isarmour(o)) { reason = E_PALADIN; return B_FALSE; } } } // injuries? if ((where == BP_HANDS) && lfhasflagval(lf, F_INJURY, IJ_HANDSWOLLEN, NA, NA, NULL)) { reason = E_INJURED; return B_FALSE; } if ((where == BP_FEET) && lfhasflagval(lf, F_INJURY, IJ_ANKLESWOLLEN, NA, NA, NULL)) { reason = E_INJURED; return B_FALSE; } if (gettechlevel(o->type->id) > getskill(lf, SK_TECHUSAGE)) { reason = E_NOTKNOWN; return B_FALSE; } if (!meetsallattreqs(lf, o)) { // meetsattreq will set 'reason' for us. return B_FALSE; } // can we wear it ANYWHERE? getflags(o->flags, retflag, &nretflags, F_GOESON, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_GOESON) { possbp[nparts] = f->val[0]; nparts++; } } if (nparts == 0) { // can't wear anywhere! reason = E_IMPOSSIBLE; return B_FALSE; } if (where != BP_NONE) { int found = B_FALSE; // is one of the possible locations the one we are checking? for (i = 0; i < nparts; i++) { if (possbp[i] == where) { found = B_TRUE; } } if (!found) { // can't wear there! reason = E_IMPOSSIBLE; return B_FALSE; } // is the GIVEN part free? if (!isfreebp(lf, where, o)) { reason = E_WEARINGSOMETHINGELSE; return B_FALSE; } } else { if (hasflag(o->flags, F_GOESONMULTI)) { // are ALL of these body parts free? for (i = 0; i < nparts; i++) { if (!isfreebp(lf, possbp[i], o)) { reason = E_WEARINGSOMETHINGELSE; return B_FALSE; } } return B_TRUE; } else { int ok = B_FALSE; // are ANY of these body parts free? for (i = 0; i < nparts; i++) { if (isfreebp(lf, possbp[i], o)) { ok = B_TRUE; break; } } // no parts free if (!ok) { reason = E_WEARINGSOMETHINGELSE; return B_FALSE; } } } /* // anything else worn there? for (oo = lf->pack->first ; oo ; oo = oo->next) { f = hasflagval(oo->flags, F_EQUIPPED, where, -1, -1, NULL); if (f) { reason = E_WEARINGSOMETHINGELSE; return B_FALSE; } } */ return B_TRUE; } int canweild(lifeform_t *lf, object_t *o) { flag_t *f; enum BODYPART weildloc,otherloc; flag_t *retflag[MAXCANDIDATES]; int nretflags; weildloc = getweildloc(o, lf, &otherloc, NULL); // already weilding it? if (o) { if (isequipped(o)) { reason = E_ALREADYUSING; return B_FALSE; } // paladin? if (hasjob(lf, J_PALADIN)) { if (!isblessed(o) || !o->blessknown) { reason = E_PALADIN; return B_FALSE; } } // too heavy? if (lfhasflagval(lf, F_INJURY, IJ_SHOULDERDISLOCATED, NA, NA, NULL)) { if (isheavyweapon(o)) { reason = E_INJURED; return B_FALSE; } } if ((gettechlevel(o->type->id) > getskill(lf, SK_TECHUSAGE))) { reason = E_NOTKNOWN; return B_FALSE; } if (lfhasflagval(lf, F_INJURY, IJ_TENDONCUT, NA, NA, NULL)) { if ((weildloc == BP_WEAPON) || (otherloc == BP_WEAPON)) { reason = E_INJURED; return B_FALSE; } } } // special case... if (lf->race->baseid == R_DANCINGWEAPON) { return B_TRUE; } if (!canuseweapons(lf)) { reason = E_IMPOSSIBLE; return B_FALSE; } if (!hasbp(lf, weildloc)) { if (o && isfirearm(o) && !getequippedob(lf->pack, otherloc)) { int temp; // firearm can go in other hand. // swap locations. temp = weildloc; weildloc = otherloc; otherloc = temp; } else { reason = E_NOHANDS; return B_FALSE; } } if (o) { int i; getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_ATTREQ) { // meetsattreq will set 'reason' for us. if (!meetsattreq(lf, f, o, NULL)) { return B_FALSE; } } } if (istwohandedfor(o, lf)) { // need both hands free... if (!hasbp(lf, otherloc)) { reason = E_NOHANDS; return B_FALSE; } } } reason = E_OK; // trying to fight unarmed, but no unarmed attack? if (o == NULL) { if (!hasflag(lf->flags, F_HASATTACK)) { reason = E_NOUNARMEDATTACK; return B_FALSE; } } return B_TRUE; } int cantakeoff(lifeform_t *lf, object_t *o, object_t **errob) { flag_t *f; object_t *oo; enum BODYPART bp = BP_NONE; if (errob) *errob = NULL; reason = E_OK; f = hasflag(o->flags, F_EQUIPPED); if (!f) { reason = E_NOTEQUIPPED; return B_FALSE; } bp = f->val[0]; // something else equipped here? oo = getouterequippedob(lf, bp); if (oo && (oo != o)) { if (errob) *errob = oo; reason = E_UNDERNEATH; return B_FALSE; } // cursed? if (o->blessed == B_CURSED) { reason = E_CURSED; return B_FALSE; } // injuries? if (isequippedon(o, BP_HANDS) && lfhasflagval(lf, F_INJURY, IJ_HANDSWOLLEN, NA, NA, NULL)) { reason = E_INJURED; return B_FALSE; } if (isequippedon(o, BP_FEET) && lfhasflagval(lf, F_INJURY, IJ_ANKLESWOLLEN, NA, NA, NULL)) { reason = E_INJURED; return B_FALSE; } return B_TRUE; } int cantalk(lifeform_t *lf) { if (lfhasflag(lf, F_SILENCED)) { return B_FALSE; } // phantasms dont talk if (lfhasflag(lf, F_PHANTASM)) { return B_FALSE; } if (lfhasflag(lf, F_NOTALK)) { return B_FALSE; } // if the lf can't explicitly talk, check its race if (!lfhasflag(lf, F_CANTALK)) { if (!racecantalk(lf->race->id)) { return B_FALSE; } } // too dumb? if (getattr(lf, A_IQ) <= AT_VLOW) { return B_FALSE; } // beheaded? if (lfhasflag(lf, F_BEHEADED)) { return B_FALSE; } return B_TRUE; } int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *targob, cell_t *targcell, object_t *fromob, int *seen) { int rv; int needtovalidate = B_FALSE; int targettype; int cost; flag_t *f,*willflag = NULL,*castflag = NULL; int power; int spellblessed = B_UNCURSED; objecttype_t *sp; enum CASTTYPE casttype = CT_NORMAL; if (fromob) { spellblessed = fromob->blessed; power = getobspellpower(fromob, lf); } else { spellblessed = B_UNCURSED; power = getspellpower(lf, sid); // check whether we _can_ cast it. // do we have this spell/ability? // enough mp? etc if (!cancast(lf, sid, &cost)) { if (isplayer(lf)) { // announce switch (reason) { case E_TOOPOWERFUL: msg("That spell is too powerful for you to cast."); break; case E_NOMP: msg("You don't have enough mana to cast that."); break; case E_INJURED: msg("Your injury prevents you from doing that."); break; case E_LOWIQ: msg("You are not smart enough to cast spells."); break; case E_NOTREADY: msg("This ability is not recharged yet."); break; case E_NOSPELLS: msg("You can't cast spells."); break; case E_PALADIN: msg("Your cursed weapon is nullifying your holy magic."); break; case E_PALADIN2: msg("Your cursed armour is nullifying your holy magic."); break; case E_PRONE: msg("You can't cast spells while prone."); break; case E_CLIMBING: msg("You can't cast spells while climbing."); break; case E_SWIMMING: msg("You can't cast spells while swimming."); break; case E_STUNNED: msg("You can't cast spells while stunned."); break; case E_SILENCED: msg("You are unable to utter the words to your spell!"); break; default: msg("For some reason, you can't cast that."); break; } } return B_TRUE; } willflag = lfhasflagval(lf, F_CANWILL, sid, NA, NA, NULL); castflag = lfhasflagval(lf, F_CANCAST, sid, NA, NA, NULL); // special case if (!willflag) { flag_t *mf; mf = missingspellcastob(lf); if (mf) { if (strlen(mf->text)) { if (isplayer(lf)) { msg("You can't cast spells without a %s.", mf->text); } } else if (mf->val[0] != NA) { objecttype_t *ot; ot = findot(mf->val[0]); if (isplayer(lf)) { msg("You can't cast spells without %s %s.",needan(ot->name) ? "an" : "a", ot->name); } } else { if (isplayer(lf)) { msg("You can't cast spells without a spell focus."); } } return B_TRUE; } } casttype = getcasttype(lf, sid); } // override 'fromob' based on conferred flags if (willflag && (willflag->obfrom != NA)) { fromob = findobbyid(lf->pack, willflag->obfrom); } else if (castflag && (castflag->obfrom != NA)) { fromob = findobbyid(lf->pack, castflag->obfrom); } sp = findot(sid); if (!fromob) { if (isplayer(lf) && (power > 1) && hasflag(sp->flags, F_VARPOWER)) { if (!hasactivespell(lf, sp->id)) { int max; char buf[BUFLEN],desc[BUFLEN]; int i; char ch; // ask what power max = power; snprintf(buf, BUFLEN, "Cast %s at what power level?", sp->name); initprompt(&prompt, buf); for (i = 1; i <= max; i++) { int thiscost; thiscost = getmpcost(lf, sid) * i; if (lf->mp >= thiscost) { snprintf(buf, BUFLEN, "Power %s (%d MP)", roman(i), thiscost); getspelldesc(sp->id, i, desc); if (strlen(desc)) { strcat(buf, "\t"); strcat(buf, desc); } addchoice(&prompt, '0' + i, buf, buf, NULL, NULL); } } addchoice(&prompt, '0', "Cancel", "Cancel", NULL, NULL); ch = getchoice(&prompt); power = ch - '0'; if (power == 0) { msg("Cancelled."); return B_TRUE; } // modify cost cost *= i; } } } // ask for target cell if ((f = hasflag(sp->flags, F_TARGETTEDSPELL)) != NULL) { if ((f->val[1] == NA) || (f->val[2] == NA)) { targettype = f->val[0]; needtovalidate = B_TRUE; } else if ((power >= f->val[1]) && (power <= f->val[2])) { targettype = f->val[0]; needtovalidate = B_TRUE; } else { targettype = TT_NONE; needtovalidate = B_FALSE; } } else if (targcell) { targettype = TT_NONE; needtovalidate = B_FALSE; } if (needtovalidate) { if (!validatespellcell(lf, &targcell,targettype, sid, power, fromob ? B_TRUE : B_FALSE)) { if (isplayer(lf)) msg("Cancelled."); return B_TRUE; } if (fromob && !targcell) { return B_TRUE; } } if (isplayer(lf)) { if ((sid == OT_S_GATHERFLAME) && (lf->material->id == MT_FIRE)) { if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { char ch; ch = askchar("Casting this while made of flame will harm you, continue?","yn", "n", B_TRUE, B_FALSE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } } } } // stop hiding killflagsofid(lf->flags, F_HIDING); // take time taketime(lf, getspellspeed(lf)); killflagsofid(lf->flags, F_FULLSHIELD); if (!fromob) { flag_t *retflag[MAXCANDIDATES]; int nretflags,tempboost = 0,i; // lose mp losemp(lf, cost); // remember spell for rePeat command if (isplayer(lf)) { modflag(lf->flags, F_LASTSPELL, sid, NA, NA, NULL); } // spell fails? // miscast chance? if (isplayer(lf) && !hasjob(lf, J_GOD)) { if (pctchance(getmiscastchance(lf))) { msg("^WYour cumbersome armour makes you miscast your spell!"); return B_FALSE; } } // casting a god-related spell while that god is angry at you? if (isplayer(lf) && !willflag) { if (godprayedto(R_GODNATURE) && godisangry(R_GODNATURE)) { if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL)) { msg("You seem to be cut off from the powers of nature magic."); return B_FALSE; } } if (godprayedto(R_GODLIFE) && godisangry(R_GODLIFE)) { if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL)) { msg("You seem to be cut off from the powers of life magic."); return B_FALSE; } } } // adjust power? if (hasjob(lf, J_DRUID)) { power += countplantsinsight(lf); limit(&power, NA, 10); } tempboost = 0; getflags(lf->flags, retflag, &nretflags, F_TEMPMAGICBOOST, F_NONE); for (i = 0; i < nretflags; i++) { tempboost += retflag[i]->val[0]; killflag(retflag[i]); } // boost based on time? switch (gettimephase()) { case TP_MIDNIGHT: if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL)) { tempboost += 3; } break; case TP_TWILIGHTMORN: case TP_SUNRISE: if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL)) { tempboost += 3; } break; default: break; } if (tempboost) { power += tempboost; if (isplayer(lf)) { msg("^gYour spell's power is boosted!^n"); } } if (!willflag && real_getmr(lf, B_ONLYEXTERNAL) && skillcheck(lf, SC_RESISTMAG, 90 + (power*10), 0) && !isgod(lf)) { if (power > 1) { // half strength power /= 2; if (power <= 1) power = 1; if (isplayer(lf)) { msg("^wYour magic resistance lowers your spell's power."); } } } } // noise.... makenoise(lf, N_SPELLCAST); // announce if (!isplayer(lf) && !fromob) { int doannounce = B_FALSE; if (gamemode == GM_GAMESTARTED) { if (cansee(player, lf)) { doannounce = B_TRUE; } else if ((casttype == CT_SOUNDBASED) && canhear(player, lf->cell, SV_TALK, NULL)) { doannounce = B_TRUE; } } if (doannounce) { char lfname[BUFLEN]; char whattosay[BUFLEN]; getlfname(lf, lfname); // special case f = getspellcasttextflag(lf, sid); if (f) { if (strlen(f->text)) { snprintf(whattosay, BUFLEN, "%s %s", lfname, f->text); if (targlf && (f->val[2] == B_APPENDYOU)) { char targname[BUFLEN]; if (targlf == lf) { strcpy(targname, it(lf)); strcat(targname, "self"); } else { getlfname(targlf, targname); } strcat(whattosay, " at "); strcat(whattosay, targname); } strcat(whattosay, "."); msg("%s", whattosay); } } else { if (hasflag(sp->flags, F_CASTINGTIME)) { msg("%s starts casting a spell.", lfname); } else { msg("%s casts a spell.", lfname); } } } else { // player can't see them if ((targlf == player) || (targcell == player->cell)) { if (!lfhasflag(player, F_ASLEEP) && !lfhasflag(lf, F_SPELLCASTTEXT)) { msg("Something casts a spell at you."); } } } } // eye protection will stop some spells! if (targcell && targcell->lf) { lifeform_t *victim; victim = targcell->lf; int protected = B_FALSE; object_t *protob = NULL; switch (casttype) { case CT_EYESPIT: protob = getarmour(victim, BP_EYES); if (protob) protected = B_TRUE; break; case CT_GAZE: protob = eyesshaded(victim); if (protob) protected = B_TRUE; break; case CT_SOUNDBASED: if (isdeaf(victim)) protected = B_TRUE; break; default: protob = NULL; protected = B_FALSE; break; } if (protected) { if (protob) { if (isplayer(victim)) { char gbuf[BUFLEN]; getobname(protob, gbuf, protob->amt); msg("Your %s protects you.", noprefix(gbuf)); } else if (cansee(player, victim)) { char lfname[BUFLEN],gbuf[BUFLEN]; getobname(protob, gbuf, protob->amt); getlfname(victim, lfname); msg("%s%s %s protects %s.", lfname, getpossessive(lfname), noprefix(gbuf), it(victim) ); } } else { if (isplayer(victim)) { msg("You are unaffected."); } else if (cansee(player, victim)) { char lfname[BUFLEN]; getlfname(victim, lfname); msg("%s is unaffected.", lfname); } } return B_FALSE; } } if (!fromob) { flag_t *ff; // is this a spell which we can only cast every x turns? // if so, rest counter. // do this _before_ casting the spell, // in case the spell causes us to lose // the f_canwill/f_cancast flag (eg. polymorph) if (willflag) ff = willflag; else ff = castflag; if (ff) { if (ff->val[2] != NA) { ff->val[1] = -1; } } } // cast the spell. does it take time? f = hasflag(sp->flags, F_CASTINGTIME) ; if (!f) { f = lfhasflagval(lf, F_SPELLCASTTIME, NA, sp->id, NA, NULL); } if (f && !fromob) { int castingtime; char tempbuf[BUFLEN]; char castingbuf[BUFLEN]; flag_t *castingflag; castingtime = f->val[0]; strcpy(castingbuf, ""); if (targlf) { snprintf(tempbuf, BUFLEN, "%d;",targlf->id); } else { strcpy(tempbuf, "-1;"); } strcat(castingbuf, tempbuf); if (targob) { snprintf(tempbuf, BUFLEN, "%ld;",targob->id); } else { strcpy(tempbuf, "-1;"); } strcat(castingbuf, tempbuf); if (targcell) { snprintf(tempbuf, BUFLEN, "%d;%d;%d;",targcell->map->id,targcell->x, targcell->y); } else { strcpy(tempbuf, "-1;-1;-1;"); } strcat(castingbuf, tempbuf); castingflag = addflag(lf->flags, F_CASTINGSPELL, sid, power, castingtime, castingbuf); rv = B_FALSE; if (isplayer(lf)) { // announce msg("You start casting %s.", sp->name); } } else { // instant cast int obfromid = -1; addflag(lf->flags, F_CASTINGSPELL, sid, NA, NA, NULL); // override obfrom if required if (willflag && (willflag->obfrom != -1)) { obfromid = willflag->obfrom; } else if (castflag && (castflag->obfrom != -1)) { obfromid = castflag->obfrom; } if (obfromid != -1) { fromob = findobbyid(lf->pack, obfromid); } rv = dospelleffects(lf, sid, power, targlf, targob, targcell, spellblessed, seen, B_FALSE, fromob); f = lfhasflag(lf, F_CASTINGSPELL); if (f) { killflag(f); } } // successful cast? if (!rv) { enum SPELLSCHOOL school; school = getspellschoolknown(lf, sid); if (!fromob) { if (school != SS_NONE) { enum SKILL skid; skid = getschoolskill(school); if (skid != SK_NONE) { practice(lf, skid, 1); } } } if (isplayer(lf)) { objecttype_t *ot; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; // god of magic likes all spells except psionics if (school != SS_MENTAL) { pleasegodmaybe(R_GODMAGIC, getspelllevel(sid)); } // god of battle hates all spells except nullify and psionics if (!fromob && (sid != OT_S_NULLIFY)) { if (school != SS_MENTAL) { angergodmaybe(R_GODBATTLE, 25, GA_SPELL); } } ot = findot(sid); getflags(ot->flags, retflag, &nretflags, F_PLEASESGOD, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_PLEASESGOD) { pleasegodmaybe(f->val[0], f->val[1]); } } // anger gods? switch (school) { case SS_COLD: angergodmaybe(R_GODFIRE, getspelllevel(sid)*5, GA_SPELL); break; case SS_DEATH: angergodmaybe(R_GODLIFE, getspelllevel(sid)*5, GA_SPELL); break; case SS_LIFE: angergodmaybe(R_GODDEATH, getspelllevel(sid)*5, GA_SPELL); break; default: break; } } f = lfhasflagval(lf, F_DIEAFTERUSING, sid, NA, NA, NULL); if (f) { lf->hp = 0; setlastdam(lf, f->text); } } return rv; } /* int celllitfor(lifeform_t *lf, cell_t *c, int maxvisrange, int nightvisrange) { int dist; //int ambientvis; dist = getcelldist(lf->cell, c); // too far away if (maxvisrange != UNLIMITED) { if (dist > maxvisrange) { // if it's lit and within our non-ambient-adjusted visrange, it's still ok if (islit(c) && (dist <= maxvisrange)) return B_TRUE; else return B_FALSE; } } // outside ambient light range and beyond nightvis range //ambientvis = getmapmaxvisrange(c->map); //if ((ambientvis == 0) || (dist > ambientvis)) { // if (dist >= nightvisrange) { // return B_FALSE; // } // } // outside the range of our light, and not lit if ((nightvisrange != UNLIMITED) && !islit(c)) { if (dist >= nightvisrange) { return B_FALSE; } else { // inside our nightvis range and magically dark if (c->lit == L_PERMDARK) { return B_FALSE; } } } return B_TRUE; } */ int celltransparentfor(lifeform_t *lf, cell_t *c, int *xray, int *rangemod) { object_t *o; flag_t *f; if (rangemod) *rangemod = 0; // solid cells stop los (unless it's your own cell) if (!c->type->transparent && (c != lf->cell)) { int ok = B_FALSE; // high engineering lets you detect hollow walls. ie. // if a wall has another wall behind it, you 'see' it. //if (c->type->solid && nextcell && nextcell->type->solid) { // if (isadjacent(c, lf->cell) && !isadjacent(nextcell, lf->cell) && // (getskill(lf, SK_ENGINEERING) >= PR_BEGINNER)) { // ok = B_TRUE; // } //} if (!ok) { if (xray && *xray) { (*xray)--; } else return B_FALSE; } } // check for lfs which block view if (c->lf && (c->lf != lf) && cansee_real(lf, c->lf, B_FALSE)) { if (!lfhasflag(lf, F_CANSEETHROUGHLF)) { int sizediff; // high sizediff means that the lf in the cell is bigger than the viewer sizediff = getlfsize(c->lf) - getlfsize(lf); // lf greater than 2 sizes bigger than us? if (sizediff >= 2) { if (xray && *xray) { (*xray) -= (sizediff-1); if (*xray < 0) *xray = 0; } else return B_FALSE; } } } // check for objects which block view for (o = c->obpile->first ; o ; o = o->next) { f = hasflag(o->flags, F_BLOCKSVIEW); if (f) { if ((lf->cell == c) && (f->val[1] == B_TRUE)) { } else if (!lfhasflagval(lf, F_CANSEETHROUGHMAT, o->material->id, NA, NA, NULL)) { if (xray && *xray) { (*xray)--; } else { if (f->val[0] == B_TRUE) { return B_FALSE; } else { if (rangemod) *rangemod += f->val[0]; } } } } } return B_TRUE; } int charmedaction(lifeform_t *lf, flag_t *charmflag) { lifeform_t *charmer; char charmername[BUFLEN]; charmer = findlf(lf->cell->map, charmflag->val[0]); if (!charmer) { killflag(charmflag); return B_TRUE; } getlfname(charmer, charmername); if (isadjacent(lf->cell, charmer->cell)) { object_t *o; enum OBTYPE oid[MAXPILEOBS]; int oidcovet[MAXPILEOBS]; int noids = 0; enum FLAG wantflag[MAXPILEOBS]; int wantflagcovet[MAXPILEOBS]; int nwantflags = 0; int ndone = 0; // hands over wantob/weapon/armour makewantedoblist(charmer, &noids, oid, oidcovet, &nwantflags, wantflag, wantflagcovet); for (o = lf->pack->first ; o ; o = o->next) { if (iscursed(o)) continue; if (isweapon(o) || aiwants_real(charmer, o, NULL, &noids, oid, oidcovet, &nwantflags, wantflag, wantflagcovet)) { if (isequipped(o)) { takeoff(lf, o); } else { if (isplayer(lf)) { char obname[BUFLEN]; char mastername[BUFLEN]; sprintf(mastername, "your new %s", (getgender(charmer) == G_FEMALE) ? "mistress" : "master"); getobname(o, obname, o->amt); msg("^wYou hand over your %s to %s.", noprefix(obname), cansee(lf, charmer) ? charmername : mastername); } else if (cansee(player, lf)) { char lfname[BUFLEN]; char obname[BUFLEN]; getlfname(lf, lfname); getobname(o, obname, o->amt); msg("^w%s hands over %s to %s.", lfname, obname, cansee(player, charmer) ? charmername : "someone"); } moveob(o, charmer->pack, o->amt); } ndone++; break; } } // nothing they want? just remove armour/weapons. if (!ndone) { for (o = lf->pack->first ; o ; o = o->next) { if (isequipped(o)) { takeoff(lf, o); ndone++; break; } } } if (!ndone) { if (isplayer(lf)) { msg("^wYou bask in the glory of %s!", cansee(lf, charmer) ? charmername : "your new master"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^w%s stares in awe at %s.", lfname, cansee(lf, charmer) ? charmername : "someone"); } taketime(lf, getactspeed(lf)); } } else { int dir; // walks towards charmer, regardless of anything in the way turntoface(lf, charmer->cell); dir = getdirtowards(lf->cell, charmer->cell, lf, B_FALSE, DT_ORTH); if (dir == D_NONE) { if (isplayer(lf)) { msg("^wYou try to %s towards %s, but fail.", getmoveverb(lf), cansee(lf, charmer) ? charmername : "your new master"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^w%s tries to %s towards %s, but fail.", lfname, getmoveverb(lf), cansee(lf, charmer) ? charmername : "something"); } } else { if (isplayer(lf)) { msg("^wYou mindlessly %s towards %s.", getmoveverb(lf), cansee(lf, charmer) ? charmername : "your new master"); } trymove(lf, dir, B_FALSE, B_TRUE); } taketime(lf, getactspeed(lf)); } return B_FALSE; } int checkburdened(lifeform_t *lf, int preburdened) { int postburdened; postburdened = isburdened(lf); if (postburdened != preburdened) { if (postburdened) { if (preburdened == BR_NONE) { msg("^wThe weight of your possessions is burdening you!"); statdirty = B_TRUE; } else if (postburdened > preburdened) { msg("^wThe weight of your possessions is burdening you even more!"); } else { // ie. postburdened < preburdened msg("^wThe weight of your possessions is burdening you a little less now."); } } else { // not burdened msg("^wYour possessions are no longer weighing you down."); statdirty = B_TRUE; } return B_TRUE; } return B_FALSE; } // returns TRUE if something happened. int checkfordrowning(lifeform_t *lf, object_t *o) { int depth,i; int didsomething = B_FALSE; flag_t *f; enum SKILLLEVEL slev; if (isairborne(lf, NULL)) { return B_FALSE; } depth = getobdepth(o, lf); // activate amulet of swimming if (depth >= DP_HEAD) { object_t *o; o = hasequippedobid(lf->pack, OT_AMU_SWIMMING); if (o && !ispolymorphed(lf)) { // transform into a swan! if (!polymorphto(lf, R_SWAN, PERMENANT)) makeknown(o->type->id); } o = hasequippedobid(lf->pack, OT_AMU_EVOLUTION); if (o) { if (!polymorphto(lf, R_FISHFOLK, 5)) makeknown(o->type->id); } } // recalculate depth now depth = getobdepth(o, lf); if (lfhasflag(lf, F_AQUATIC)) { slev = PR_MASTER; } else { i = getskill(lf, SK_SWIMMING) - isburdened(lf); limit(&i, 0, NA); slev = i; } // apply water damage (ie rust etc) to armour. for (i = 0; i <= depth; i++) { object_t *armour = NULL; if (i == DP_FEET) { armour = getouterequippedob(lf, BP_FEET); if (armour) takedamage(armour, 4, DT_WATER, NULL); } else if (i == DP_WAIST) { armour = getouterequippedob(lf, BP_LEGS); if (armour) takedamage(armour, 4, DT_WATER, NULL); armour = getouterequippedob(lf, BP_WAIST); if (armour) takedamage(armour, 4, DT_WATER, NULL); } else if (i == DP_SHOULDERS) { armour = getouterequippedob(lf, BP_BODY); if (armour) takedamage(armour, 4, DT_WATER, NULL); armour = getouterequippedob(lf, BP_SHOULDERS); if (armour) takedamage(armour, 4, DT_WATER, NULL); } else if (i == DP_HEAD) { armour = getouterequippedob(lf, BP_HEAD); if (armour) takedamage(armour, 4, DT_WATER, NULL); } } // will you drown? if (depth >= DP_HEAD) { if (lf->race->id == R_VAMPIRE) { if (isplayer(lf)) { msg("^BThe running water burns you!"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%cThe running water burns %s!", getlfcol(lf, CC_BAD), lfname); } losehp(lf, roll("6d6"), DT_DIRECT, NULL, "running water"); } if (needstobreath(lf)) { if ((!slev || !getstamina(lf)) && !lfhasflag(lf, F_BREATHWATER) ) { int damamt; // take drowning damage. generally you'll die // in around 3-4 turns. damamt = lf->maxhp / (getattr(lf, A_CON) / 15); limit(&damamt, 1, NA); if (damamt >= lf->hp) { char obname[BUFLEN]; if (isplayer(lf)) { msg("^BYou drown."); didsomething = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s drowns.",getlfcol(lf, CC_BAD) , lfname); didsomething = B_TRUE; } addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); lf->hp = 0; getobnametruebase(o, obname, o->amt); lf->lastdamtype = DT_DIRECT; setlastdam(lf, obname); setkillverb(lf, "Drowned"); } else { char obname[BUFLEN]; if (isplayer(lf)) { msg("^BYou are drowning!"); didsomething = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s is drowning!", getlfcol(lf, CC_VBAD), lfname); didsomething = B_TRUE; } getobnametruebase(o, obname, o->amt); losehp(lf, damamt, DT_DIRECT, NULL, obname); setlastdam(lf, obname); setkillverb(lf, "Drowned"); } } } //end if needs to breath } // end if depth > head if (!isdead(lf)) { f = isvulnto(lf->flags, DT_WATER, B_FALSE); if (f) { int dam; if (strlen(f->text)) { dam = roll(f->text); } else { dam = roll("1d6"); } applywalkdam(lf, dam, DT_WATER, o, BP_NONE); } } return didsomething; } int check_rest_ok(lifeform_t *lf) { enum ERROR why; if (!safetorest(lf, &why)) { if (isplayer(lf)) { switch (why) { case E_LEVITATING: msg("You cannot rest while levitating in mid-air!"); break; case E_MONSTERNEARBY: msg("You cannot rest - there are monsters in view!"); break; case E_STASIS: msg("You cannot rest while your body is in stasis."); break; case E_TOOCOLD: msg("The extreme cold prevents you from sleeping."); break; default: msg("You cannot rest for some reason."); break; } } return B_TRUE; } return B_FALSE; } lifeform_t *clonelf(lifeform_t *src, cell_t *where) { lifeform_t *lf; lf = addmonster(where, src->race->id, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (lf) { killflagsofid(lf->flags, F_XPVAL); addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL); } return lf; } // how dangerous is lf2 to lf1? // < 0 = harder // > 0 = easier float comparelfs(lifeform_t *lf1, lifeform_t *lf2) { float turnstokill1, turnstokill2; float ratio; turnstokill1 = gethitstokill(lf1, lf2, B_TRUE, B_TRUE); // #turns for lf1 to kill lf2 turnstokill2 = gethitstokill(lf2, lf1, B_TRUE, B_TRUE); // #turns for lf2 to kill lf1 if (turnstokill1 == 0) { // lf1 can never kill lf2 ratio = -1; } else if (turnstokill2 == 0) { // lf2 can never kill lf1 ratio = 5; } else { // compare avgturnstokill values // if turns to kill lf2 is lower, ratio will be positive. ratio = (turnstokill2 / turnstokill1); } return ratio; } // note: this will kill incubateflag! void completeincubation(lifeform_t *lf, flag_t *incubateflag) { // parse flag text to get power & whatfrom char *p; char buf[BUFLEN]; int power; enum POISONTYPE ptype; ptype = incubateflag->val[0]; p = readuntil(buf, incubateflag->text, '^'); power = atoi(buf); readuntil(buf, p, '^'); addtempflag(lf->flags, F_POISONED, ptype, power, incubateflag->obfrom, buf, incubateflag->val[2]); poisoneffects(lf, ptype, power); killflag(incubateflag); } int confuse(lifeform_t *lf, int howlong) { flag_t *f; f = lfhasflag(lf, F_CONFUSED); if (f) { if (f->lifetime > 0) f->lifetime += howlong; } else { addtempflag(lf->flags, F_CONFUSED, B_TRUE, NA, NA, NULL, howlong); } return B_FALSE; } void copycorpseflags(flagpile_t *dst, flagpile_t *src) { // inherit physical properties... copyflag(dst, src, F_DTVULN); copyflag(dst, src, F_DTRESIST); copyflag(dst, src, F_DTIMMUNE); // inherit alighnment copyflag(dst, src, F_ALIGNMENT); // inherit eat conferred flags copyflag(dst, src, F_EATCONFER); } int continuedigging(lifeform_t *lf) { cell_t *c; flag_t *f = NULL; object_t *digob = NULL; int digpower; int stopnow = B_FALSE; if (!real_hasfreeaction(lf, F_DIGGING)) { stopnow = B_TRUE; } f = hasflag(lf->flags, F_DIGGING); if (!f) { stopnow = B_TRUE; } if (!stopnow && strlen(f->text)) { long obid; // do we still ahve the object we were using to dig? obid = atol(f->text); digob = hasobid(lf->pack, obid); if (!digob) { stopnow = B_TRUE; } } if (!stopnow) { c = getcellat(lf->cell->map, f->val[0], f->val[1]); if (!c) { stopnow = B_TRUE; } } if (stopnow) { if (f) killflag(f); return B_TRUE; } if (digob) makeopersound(lf->cell,digob); digpower = f->val[2]; if (getskill(lf, SK_ENGINEERING) >= PR_SKILLED) { digpower *= 2; } c->hp -= digpower; if (c->hp <= 0) { if (isplayer(lf)) { msg("You finish %s %s %s.",digob ? "digging through" : "dismantling", needan(c->type->name) ? "an" : "a", c->type->name); needredraw = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s finishes %s %s %s.",lfname, digob ? "digging through" : "dismantling", needan(c->type->name) ? "an" : "a", c->type->name); needredraw = B_TRUE; } // replace wall //setcelltype(c, c->map->habitat->emptycelltype); breakwall(c, NULL); // stop digging killflag(f); } else { if (isplayer(lf)) { msg("You %s into %s %s.", digob ? "dig into" : "continue dismantling", needan(c->type->name) ? "an" : "a", c->type->name); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s %s %s %s.",lfname, digob ? "digs into" : "continues dismantling", needan(c->type->name) ? "an" : "a", c->type->name); } } return B_FALSE; } int continuerepairing(lifeform_t *lf, flag_t *repairflag) { object_t *helpob,*o; char helpobname[BUFLEN]; flag_t *f; int repbonus = 0,repamt = 1; enum SKILL whichskill; o = findobbyid(lf->pack, atol(repairflag->text)); if (!o) { killflag(repairflag); return B_TRUE; } // in case it's on fire, etc if (touch(lf, o)) { taketime(lf, getactspeed(lf)); return B_FALSE; } f = hasflag(o->flags, F_IMMUTABLE); if (f) { if (isplayer(lf)) { char obname[BUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); msg("Your %s somehow resists your attempts to repair it!", noprefix(obname)); } killflag(repairflag); f->known = B_TRUE; return B_TRUE; } // get helper ob helpob = getworkhelpob(lf->pack, o->material->id, &repbonus, NULL); if (helpob) { real_getobname(helpob, helpobname, helpob->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); } else { strcpy(helpobname, ""); } // figure out which skill we're using. whichskill = getskilltorepair(o); // repair it a bit more. repamt = 1 + getskill(lf, whichskill) + repbonus; // fully repair it. f = hasflag(o->flags, F_OBHP); f->val[0] += repamt; limit(&(f->val[0]), NA, f->val[1]); if (f->val[0] == f->val[1]) { if (isplayer(lf)) { char obname[BUFLEN],withbuf[BIGBUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); if (helpob) snprintf(withbuf, BIGBUFLEN, " (with %s)", helpobname); else strcpy(withbuf, ""); msg("You finish repairing your %s%s.", noprefix(obname), withbuf); } else if (cansee(player, lf)) { char obname[BUFLEN],withbuf[BIGBUFLEN]; char lfname[BUFLEN]; getlfname(lf, lfname); real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); if (helpob) snprintf(withbuf, BIGBUFLEN, " with %s", helpobname); else strcpy(withbuf, ""); msg("%s finishes repairing %s%s.", lfname, obname, withbuf); } practice(lf, SK_METALWORK, 1); practice(lf, SK_SEWING, 1); // finished repairing this object now. killflag(repairflag); } else { if (isplayer(lf)) { char obname[BUFLEN],withbuf[BIGBUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); if (helpob) snprintf(withbuf, BIGBUFLEN, " (with %s)", helpobname); else strcpy(withbuf, ""); msg("You continue repairing your %s%s.", noprefix(obname), withbuf); } else if (cansee(player, lf)) { char obname[BUFLEN],withbuf[BUFLEN]; char lfname[BUFLEN]; getlfname(lf, lfname); real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); if (helpob) snprintf(withbuf, BUFLEN, " with %s", helpobname); else strcpy(withbuf, ""); msg("%s continue repairing %s%s.", lfname, obname, withbuf); } } if (helpob) makeopersound(lf->cell,helpob); // take some time. taketime(lf, getactspeed(lf)); return B_FALSE; } int countinnateattacks(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE); return nretflags; } int countlegs(lifeform_t *lf) { enum BODYPART bp; int legs = 0; for (bp = 0; bp <= MAXBODYPARTS; bp++) { if (hasbp(lf, bp)) { switch (bp) { case BP_LEGS: case BP_FRONTLEGS: case BP_BACKLEGS: legs += 2; break; default: break; } } } if (lfhasflag(lf, F_LOTSOFLEGS)) legs += 20; return legs; } int countmonsters(void) { int count = 0; race_t *r; for (r = firstrace ; r ; r = r->next) { count++; } return count; } int countnearbyallies(lifeform_t *lf) { lifeform_t *l; int count = 0; for (l = lf->cell->map->lf ; l ; l = l->next) { if ((l != lf) && areallies(l, lf)) { count++; } } return count; } int countnearbyhurtallies(lifeform_t *lf) { lifeform_t *l; int count = 0; for (l = lf->cell->map->lf ; l ; l = l->next) { if ((l != lf) && areallies(l, lf)) { if (l->hp != l->maxhp) { count++; } } } return count; } int countplantsinsight(lifeform_t *lf) { int n, boost = 0; for (n = 0; n < lf->nlos; n++) { if (lf->los[n]->lf && getraceclass(lf->los[n]->lf) == RC_PLANT) { boost++; } else if (hasobofclass(lf->los[n]->obpile, OC_FLORA)) { boost++; } } return boost; } // toggle debugging void debug(lifeform_t *lf) { char lfname[BUFLEN]; flag_t *f; getlfname(lf, lfname); f = hasflag(lf->flags, F_DEBUG); if (f) { killflag(f); msg("%s - debugging is DISABLED.", lfname); } else { addflag(lf->flags, F_DEBUG, B_TRUE, NA, NA, NULL); msg("%s - debugging is ON.", lfname); } } // returns true if the player pays. int demandbribe(lifeform_t *lf) { lifeform_t *l; char lfname[BUFLEN]; int amtwanted,amtgiven,totmoney; int hd; char buf[BUFLEN], answer[BUFLEN]; object_t *gold, *mongold; int satisfied = B_FALSE; int i,heard; char saybuf[BUFLEN]; flag_t *demflag; demflag = lfhasflag(lf, F_DEMANDSBRIBE); hd = gettr(lf); gold = hasob(player->pack, OT_GOLD); if (gold) { totmoney = countmoney(player->pack); } else { totmoney = 0; } mongold = hasob(lf->pack, OT_GOLD); amtwanted = rnd(hd*25, hd*100); getlfname(lf, lfname); if (demflag && strlen(demflag->text)) { strcpy(saybuf, demflag->text); } else { strcpy(saybuf, "Hand over all your gold!"); } if (say(lf, saybuf, SV_TALK)) { heard = B_TRUE; } else { heard = B_FALSE; } if (heard) { int doagain = B_TRUE; more(); while (doagain) { snprintf(buf, BUFLEN, "How much gold will you give %s (you have $%d)", lfname, totmoney); askstring(buf, '?', answer, BUFLEN, NULL); amtgiven = atoi(answer); if (amtgiven > totmoney) { msg("You don't have that much gold!"); more(); doagain = B_TRUE; } else if (amtgiven < 0) { msg("Please enter a valid number."); more(); doagain = B_TRUE; } else { doagain = B_FALSE; } } limit(&amtgiven, 0, totmoney); } else { amtgiven = 0; } if (gold && (amtgiven > 0)) { gold->amt -= amtgiven; if (mongold) { mongold->amt += amtgiven; } else { char gbuf[BUFLEN]; snprintf(gbuf, BUFLEN, "%d gold", amtgiven); mongold = addob(lf->pack, gbuf); } if ((amtgiven == totmoney) || (amtgiven >= amtwanted)) { // always succeed say(lf, "Pleasure doing business with you!", SV_TALK); satisfied = B_TRUE; } else { say(lf, "Then die!", SV_SHOUT); satisfied = B_FALSE; } if (gold->amt <= 0) { removeob(gold, ALL); } } else { // TODO: luck check to receive money ? say(lf, "Then die!", SV_SHOUT); satisfied = B_FALSE; } // if you gave the gold, mosnter becomes peaceful if (satisfied) { int nminions; lifeform_t *minion[MAXCANDIDATES]; makepeaceful(lf, player); // also make any of its minions peaceful getminions(lf, minion, &nminions); for (i = 0; i < nminions; i++) { makepeaceful(minion[i], player); } // special cases if (lf->race->id == R_BOGGART) { // help the player out. say(lf, "I have a present for you too!", SV_TALK); dospelleffects(NULL, OT_S_BARKSKIN, 10, player, NULL, player->cell, B_TRUE, NULL, B_FALSE, NULL); } } // either way, kill bribe flag for all bandits on the level for (l = lf->cell->map->lf ; l ; l = l->next) { if (!isplayer(l) && (l->race->baseid == lf->race->baseid)) { killflagsofid(l->flags, F_DEMANDSBRIBE); } } return satisfied; } // return B_TRUE if the lf is still alive (saved by ring of miracles, etc) int die(lifeform_t *lf) { char buf[BUFLEN]; flag_t *f; int killedgod = B_FALSE; //int dropobs = B_TRUE; int vaporised = B_FALSE, i; int dropobs = B_TRUE; int willbecomeghost = B_FALSE; object_t *corpse = NULL; flag_t *retflag[MAXCANDIDATES]; int nretflags,b; cell_t *corpsecell; lifeform_t *killer = NULL; int dobonesfile = B_FALSE; char reanimateas[BUFLEN]; cell_t *where; int thisisplayer = B_FALSE; // for earthwyrms etc dividing enum RACE dividerace = R_NONE; enum RACE createrace = R_NONE; cell_t *dividecell[2] = {NULL, NULL}; char dividename[BUFLEN]; int dividetr, dividehp; int dividelos = B_FALSE; strcpy(reanimateas, ""); where = lf->cell; thisisplayer = isplayer(lf); if (lf->lastdamlf != -1) { killer = findlf(lf->cell->map, lf->lastdamlf); } if (cansee(player, lf)) { needredraw = B_TRUE; } // prevent rising from dead? switch (lf->race->id) { case R_REVENANT: if ((lf->lastdamtype == DT_HOLY) || lfhasflag(lf, F_MUTILATED)) { killflagsofid(lf->flags, F_REVIVETIMER); } break; case R_TROLL: if (basedamagetype(lf->lastdamtype) == DT_FIRE) { killflagsofid(lf->flags, F_REVIVETIMER); } break; default: break; } if (lf->lastdamtype == DT_HOLY) { killflagsofid(lf->flags, F_RISEASGHOST); } if (lfhasflag(lf, F_RISEASGHOST)) { willbecomeghost = B_TRUE; } // remove poison when you die killflagsofid(lf->flags, F_INCUBATING); killflagsofid(lf->flags, F_POISONED); // died after entering a new level without a chance to move? // note that this won't save you frmo bring killed by a monster which // chases you up/down the stairs. if (thisisplayer && lfhasflag(lf, F_JUSTENTERED) && godprayedto(R_GODMERCY)) { int dosave = B_FALSE; if (!killer) { dosave = B_TRUE; } else if (killer && !lfhasflag(killer, F_JUSTENTERED)) { dosave = B_TRUE; } if (dosave) { godsay(R_GODMERCY, B_TRUE, "Well, that seems unfair..."); more(); // return to full health lf->hp = lf->maxhp; statdirty = B_TRUE; // teleport away! dospelleffects(NULL, OT_S_DISPERSAL, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE, NULL); } return B_TRUE; } if (useringofmiracles(lf, 3)) { int hunger; // return to full health lf->hp = lf->maxhp; // cure poison killflagsofid(lf->flags, F_POISONED); // cure starvation hunger = gethungerval(lf); if (hunger > 0) { modhunger(lf, -hunger); } // fix drained attribs restoredrainedstats(lf); // put out fires extinguishlf(lf); if (thisisplayer) { statdirty = B_TRUE; } return B_TRUE; } // a god died? if ((getraceclass(lf) == RC_GOD) && !isplayer(lf)) { flag_t *f; // update godlf pointer for (i = 0; i < ngodlfs; i++) { if (godlf[i] == lf) { godlf[i] = NULL; break; } } f = hasflag(lf->flags, F_GODOF); addflag(player->flags, F_WINNER, WT_GOD, lf->race->id, NA, f->text); killedgod = B_TRUE; } if (!willbecomeghost) { if (thisisplayer && hasjob(lf, J_GOD)) { if (!lfhasflag(lf, F_WINNER)) { char ch; msg("^BYou are about to die..."); more(); ch = askchar("Die", "yn", "n", B_TRUE, B_FALSE); if (ch == 'n') { lf->hp = lf->maxhp; msg("Not dying."); return B_TRUE; } } } } // special cases based on race id if (lf->race->id == R_BABAYAGAHUT) { if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s slumps to the ground, exhausted.",lfname); } } if (lf->race->id == R_BABAYAGA) { object_t *exitob; cell_t *exitcell; // unlock the vault exit! exitcell = findobinmap(lf->cell->map, OT_BYHUTDOOR); exitob = hasob(exitcell->obpile, OT_BYHUTDOOR); assert(exitob); killflagsofid(exitob->flags, F_LOCKED); if (haslos(player, exitcell)) { char obname[BUFLEN]; getobname(exitob, obname, 1); msg("%s unlocks with a loud 'click'.", obname); } noise(exitcell, NULL, NC_OTHER, SV_TALK, "a loud 'click'.", NULL); } if ((lf->race->id == R_EARTHWYRM) && (lf->lastdamtype == DT_SLASH)) { // remember stats dividehp = lf->maxhp / 2; dividetr = gettr(lf) / 2; if ((dividehp >= 1) && (dividetr >= 1)) { dividerace = lf->race->id; getlfname(lf, dividename); if (cansee(player, lf)) dividelos = B_TRUE; // are there 2 random cells free? dividecell[0] = real_getrandomadjcell(lf->cell, &ccwalkable, B_NOEXPAND, LOF_WALLSTOP, NULL, NULL ); if (dividecell[0]) { dividecell[1] = real_getrandomadjcell(lf->cell, &ccwalkable, B_NOEXPAND, LOF_WALLSTOP, dividecell[0], NULL ); } if (!dividecell[1]) { dividecell[1] = lf->cell; } if (dividecell[0] && dividecell[1]) { // don't leave a corpse killflagsofid(lf->flags, F_CORPSETYPE); killflagsofid(lf->flags, F_EXTRACORPSE); killflagsofid(lf->flags, F_NOCORPSE); addflag(lf->flags, F_CORPSETYPE, NA, NA, NA, NULL); } } } if ((lf->race->id == R_VAMPIRE) && !hasflag(lf->flags, F_ORIGRACE)) { // if are asleep or killed by running water/sunlight, we will die normally if (lfhasflag(lf, F_ASLEEP) || (lf->lastdamtype == DT_DIRECT)) { noise(lf->cell, lf, NC_OTHER, SV_CAR, "a horrified scream!", "screams in horror!"); } else if (findobinmap(lf->cell->map, OT_COFFIN)) { // coffin around? // restore 1 hp lf->hp = 1; killflagsofid(lf->flags, F_DEAD); // convert into a gas cloud! dospelleffects(NULL, OT_S_GASEOUSFORM, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, NULL); // ai will now look for our coffin if (thisisplayer) { msg("^GYou feel the presence of a nearby coffin..."); } else { addflag(lf->flags, F_WANTS, OT_COFFIN, B_COVETS, NA, NULL); //killflagsofid(lf->flags, F_HOSTILE); addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); } return B_TRUE; } } if (lf->race->id == R_LICH) { f = lfhasflag(lf, F_LIFEOB); if (f) { cell_t *loc; loc = findnearbylifeob(lf->cell, UNLIMITED, f, NULL); if (loc) { // announce if (thisisplayer) { msg("^GYou feel your soul being pulled to safety!^n"); } // teleport back to life ob, and revive. teleportto(lf, loc, B_FALSE); // restore all hp lf->hp = lf->maxhp; killflagsofid(lf->flags, F_DEAD); return B_TRUE; } } else { noise(lf->cell, lf, NC_OTHER, SV_CAR, "a horrified scream!", "screams in horror!"); } } if (lf->race->id == R_GLOWBUG) { // final spell... castspell(lf, OT_S_FLASH, NULL, NULL, lf->cell, NULL, NULL); } loseconcentration(lf); // revert to your original form first. if (lfhasflag(lf, F_POLYMORPHED)) { if (lfhasflag(lf, F_ORIGRACE)) { int premaxhp; premaxhp = lf->maxhp; abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL); // but you stay at lower hp. if (premaxhp < lf->maxhp) { float ratio; ratio = (float)premaxhp / (float) lf->maxhp; lf->hp = ratio * lf->maxhp; limit(&(lf->hp), 1, lf->maxhp); } if (thisisplayer) statdirty = B_TRUE; /* // ... but you're still dead lf->hp = 0; */ return B_TRUE; } } if (!willbecomeghost) { lf->alive = B_FALSE; } if (thisisplayer) { lifeform_t *god; // force screen redraw so you see your hp = 0 drawscreen(); if (lf->lastdamtype == DT_EXPLOSIVE) { msg("^BYou are vaporised!"); vaporised = B_TRUE; } else { msg("^BYou die."); } more(); drawmsg(); if (!vaporised) { flag_t *retflag[MAXCANDIDATES], *f; int nretflags,i; f = hasflag(lf->flags, F_REVIVETIMER); if (f) { race_t *r; r = findrace(f->val[2]); if (f) { strcpy(reanimateas, r->name); } } if (!strlen(reanimateas)) { // killed by a vampire = vampire. if (killer) { switch (killer->race->id) { case R_VAMPIRE: snprintf(reanimateas, BUFLEN, "vampire"); break; default: break; } } } // died by eating your own race = ghoul. // died by eating a vampire corpse = vampire. if (!strlen(reanimateas)) { getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[2] == R_VAMPIRE) { snprintf(reanimateas, BUFLEN, "vampire"); } else if (retflag[i]->val[2] == lf->race->baseid) { snprintf(reanimateas, BUFLEN, "ghoul"); } } } // hecta-worshippers often get reanimated. if (!strlen(reanimateas) && thisisplayer && godprayedto(R_GODDEATH)) { if (onein(3)) { switch (rnd(1,2)) { case 1: snprintf(reanimateas, BUFLEN, "zombie"); break; case 2: snprintf(reanimateas, BUFLEN, "skeleton"); break; } } } } // god effects... if (!vaporised) { god = getrandomprayedgod(); if (god && !godisangry(god->race->id)) { switch (god->race->id) { case R_GODBATTLE: godsay(god->race->id, B_TRUE, "Rest in peace, brave warrior."); more(); break; case R_GODDEATH: if (strlen(reanimateas)) { godsay(god->race->id, B_TRUE, "Arise, my servant..."); more(); break; // you will rise as a monster. } else { godsay(god->race->id, B_TRUE, "Come to me, my servant..."); more(); msg("Bony claws rise up and drag your body underground."); break; } case R_GODLIFE: msg("Your spirit ascends to the heavens."); more(); break; case R_GODTHIEVES: // lose all gold / gems if (countmoney(player->pack)) { int taken = B_FALSE; if (killobsofid(player->pack, OT_GOLD, B_TRUE)) { taken = B_TRUE; msg("All your gold suddenly vanishes!"); more(); } if (killobswithflag(player->pack, F_GEM, B_TRUE)) { taken = B_TRUE; msg("All your gems suddenly vanish!"); more(); } if (taken) { godsay(god->race->id, B_TRUE, "Yoink!"); more(); } } break; case R_GODMERCY: switch (getpietylev(R_GODMERCY, NULL, NULL)) { case PL_ECSTATIC: i = 90; break; case PL_DELIGHTED: i = 30; break; case PL_PLEASED: i = 20; break; case PL_INDIFFERENT: i = 10; break; case PL_TOLERATED: i = 5; break; default: i = 0; break; } if (pctchance(i)) { char *nth; f = incflag(lf->flags, F_NUMDEATHS, 1, NA, NA); nth = getnthtext(f->val[0]); snprintf(buf, BUFLEN, "I will grant you %s %s chance, mortal... use it wisely.", needan(nth) ? "an" : "a", nth); more(); godsay(god->race->id, B_TRUE, buf); more(); lf->hp = lf->maxhp; lf->alive = B_TRUE; killflagsofid(lf->flags, F_DEAD); statdirty = B_TRUE; // teleport somewhere different. dospelleffects(god, OT_S_TELEPORT, 3, lf, NULL, NULL, B_UNCURSED, NULL, B_TRUE, NULL); // drop piety level. setpiety(R_GODMERCY, getpietycutoff(PL_INDIFFERENT)); return B_TRUE; } break; default: break; } } } } else { lifeform_t *minion[MAXCANDIDATES]; int nminions = 0; // intelligent monsters will say something if (!hasflag(lf->flags, F_NODEATHSPEECH) && !lfhasflag(lf, F_SUMMONEDBY)) { if (ispetof(lf, player)) { if (cantalk(lf) && canhear(player, lf->cell, 4, NULL)) { sayphrase(lf, SP_DIE, SV_SHOUT, NA, NULL, player); } else if (!cansee(player, lf)) { // redraw since you can "see" the pet even if it's out of sight needredraw = B_TRUE; if (!isundead(lf)) { warn("You feel a profound sense of loss."); more(); } //} else { // makenoise(lf, N_DIE); } } else if (cantalk(lf)) { if (pctchance(33)) { sayphrase(lf, SP_DIE, SV_SHOUT, NA, NULL, killer); } } } if (!hasflag(lf->flags, F_NODEATHANNOUNCE)) { if (cansee(player, lf)) { getlfname(lf, buf); if (lf->lastdamtype == DT_EXPLOSIVE) { msg("^%c%s is vaporised!",getlfcol(lf, CC_BAD), buf); vaporised = B_TRUE; } else if (lf->lastdamtype == DT_MELT) { msg("^%c%s completely melts.",getlfcol(lf, CC_BAD), buf); } else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) { msg("^%c%s shatters!",getlfcol(lf, CC_BAD), buf); } else { msg("^%c%s dies.",getlfcol(lf, CC_BAD), buf); } } } if (lf->race->baseid == R_DANCINGWEAPON) { if (cansee(player, lf)) { getlfname(lf, buf); msg("%s drops to the ground.", buf); } } if (hasflag(lf->flags, F_KILLEDBYPLAYER)) { // award xp if the player killed it. awardxpfor(lf,100); if ((getalignment(lf) == AL_EVIL) || isundead(lf) || (getraceclass(lf) == RC_DEMON)) { pleasegodmaybe(R_GODPURITY, 5); if (isundead(lf)) pleasegodmaybe(R_GODLIFE, 10); } else if (getalignment(lf) == AL_GOOD) { pleasegodmaybe(R_GODDEATH, 5); } else { // ie. neutral pleasegodmaybe(R_GODDEATH, 1); } // killing someone with an injury is considered merciful. // (but only for injuries which you didn't just inflict!) getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->obfrom != B_NEWINJURY) { pleasegodmaybe(R_GODMERCY, 5); // only do this once. break; } } switch (lf->race->raceclass->id) { case RC_DRAGON: pleasegodmaybe(R_GODNATURE, 50); break; case RC_PLANT: pleasegodmaybe(R_GODFIRE, 10); break; case RC_ROBOT: pleasegodmaybe(R_GODLIFE, rnd(10,20)); pleasegodmaybe(R_GODMAGIC, rnd(2,3)); break; default: break; } if (lf->lastdamtype == DT_FIRE) { pleasegodmaybe(R_GODFIRE, 5); } // mercy god doesn't like killing //angergodmaybe(R_GODMERCY, 1, GA_MURDER); } // minions who see this one die drop morale, and might flee getminions(lf, minion, &nminions); for (i = 0; i < nminions; i++) { if (cansee(minion[i], lf)) { f = lfhasflag(minion[i], F_MORALE); if (f) { f->val[0] -= 2; // might flee? if (killer && (f->val[0] <= 0)) { scare(minion[i], killer, PERMENANT, 10); } } } } // player might get stamina back... if (isplayer(killer)) { if (getskill(player, SK_COMBAT) >= PR_EXPERT) { modstamina(lf, rnd(1,getmaxstamina(lf))); } } } // determine where to drop objects corpsecell = lf->cell; if (corpsecell && corpsecell->type->solid) { // try the cell in front (in case they were climbing) corpsecell = getcellindir(corpsecell, lf->facing); // still solid? give up. if (corpsecell && corpsecell->type->solid) { corpsecell = NULL; } } if (lfhasflag(lf, F_PHANTASM)) { dropobs = B_FALSE; } // drop/kill all objects if (corpsecell && (willbecomeghost || !thisisplayer)) { while (lf->pack->first) { if (vaporised || !dropobs) { killob(lf->pack->first); } else { object_t *droppedob; droppedob = moveob(lf->pack->first, corpsecell->obpile, ALL); if (droppedob) { if (killer && isplayer(killer)) { addflag(droppedob->flags, F_BATTLESPOILS, B_TRUE, NA, NA, NULL); } } else { killob(lf->pack->first); } } } } // drop corpse/splatter blood if (corpsecell) { lifeform_t *souleater = NULL; flag_t *soulflag = NULL; // soul consumed? if (corpsecell && hassoul(lf)) { // can you consume them? lifeform_t *l; for (l = lf->cell->map->lf ; l ; l = l->next) { if (haslos(l, lf->cell) && (l->hp < l->maxhp)) { soulflag = lfhasflag(l, F_CONSUMESOULS); if (soulflag && (getcelldist(l->cell, lf->cell) <= soulflag->val[1])) { souleater = l; break; } } } } if (vaporised) { if (lf->material->id == MT_FLESH) { switch (rnd(1,2)) { case 1: fragments(corpsecell, "chunk of flesh", 0, UNLIMITED); break; case 2: fragments(corpsecell, "pool of blood", 0, UNLIMITED); break; } } } else if (souleater && soulflag) { int amt; if (isplayer(souleater)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%cYou consume %s%s soul!", getlfcol(souleater, CC_VGOOD), lfname, getpossessive(lfname)); soulflag->known = B_TRUE; revealflagob(souleater, soulflag); } else if (cansee(player, souleater)) { char lfname[BUFLEN]; getlfname(lf, lfname); getlfname(souleater, buf); msg("^%c%s consumes %s%s soul!", getlfcol(souleater, CC_VGOOD), buf, lfname, getpossessive(lfname)); soulflag->known = B_TRUE; } amt = pctof( rnd(1,soulflag->val[0]), lf->maxhp); limit(&amt, 1, NA); gainhp(souleater, amt); // copy some flags copyflag(souleater->flags, lf->flags, F_BLOODBOIL); // drop bones addob(corpsecell->obpile, "pile of ash"); } else if (lfhasflag(lf, F_BLOODBOIL)) { if (haslos(player, corpsecell)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s explodes into flames!", lfname); } // explode into flames addobsinradius(corpsecell, 1, DT_COMPASS, "medium fire", B_TRUE, B_INCLUDECENTRE, NULL, NULL, NULL, NULL); } else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) { // shattered fragments(corpsecell, "chunk of ice", 2, UNLIMITED); } else { int unique = B_FALSE; if (lfhasflag(lf, F_UNIQUE)) unique = B_TRUE; if (lfhasflag(lf, F_NOCORPSE)) { if (isundead(lf) && cansee(player, lf)) { getlfname(lf, buf); msg("%s crumbles to dust.", buf); } } else if (!unique && (lf->lastdamtype == DT_MELT)) { // drop a pool of water addob(corpsecell->obpile, "large puddle of water"); } else if (!unique && (lf->lastdamtype == DT_NECROTIC)) { int n; int numbones = 0; char bonestring[BUFLEN]; for (n = 0; n < getlfsize(lf); n++) { numbones += rnd(1,10); } // drop bones snprintf(bonestring, BUFLEN, "%d bones",numbones); addob(corpsecell->obpile, bonestring); } else { char corpseprefix[BUFLEN]; char corpsename[BUFLEN]; strcpy(corpseprefix, ""); switch (lf->lastdamtype) { case DT_COLD: strcat(corpseprefix, "frozen "); break; default: if (lfhasflag(lf, F_FROZEN)) { strcat(corpseprefix, "frozen "); } break; } if (lfhasflag(lf, F_BEHEADED) && !lfhasflag(lf, F_CORPSETYPE)) { strcat(corpseprefix, "headless "); } f = lfhasflag(lf, F_CORPSETYPE); if (f) { snprintf(corpsename, BUFLEN, "%s%s", corpseprefix, f->text); } else { snprintf(corpsename, BUFLEN, "%s%s corpse", corpseprefix, lf->race->name); } // special cases corpse = addob(corpsecell->obpile, corpsename); if (corpse) { if ((lf->lastdamtype == DT_FIRE) && isflammable(corpse)) { addflag(corpse->flags, F_ONFIRE, B_TRUE, NA, NA, NULL); } // tainted? if ((lf->lastdamtype == DT_POISONGAS) || lfhasflag(lf, F_POISONCORPSE) || lfhasflag(lf, F_POISONED)) { addflag(corpse->flags, F_TAINTED, B_TRUE, NA, NA, NULL); } // set colour based on monster if (corpse->type->id == OT_CORPSE) { colourmatchob(corpse, lf); } // inherit hp f = hasflag(corpse->flags, F_OBHP); if (f) { f->val[0] = lf->maxhp; f->val[1] = lf->maxhp; } else { addflag(corpse->flags, F_OBHP, lf->maxhp, lf->maxhp, NA, NULL); } // inherit lifeform knowledge and abilities in case we raise it copyflag(corpse->flags, lf->flags, F_KNOWSABOUT); copyflag(corpse->flags, lf->flags, F_HOMEMAP); copyflag(corpse->flags, lf->flags, F_CANCAST); copyflag(corpse->flags, lf->flags, F_CANWILL); copyflag(corpse->flags, lf->flags, F_JOB); // race subspecies flags, so that we can have mosnters which eat // only certain kinds of corpses copyflag(corpse->flags, lf->flags, F_AVIAN); copyflag(corpse->flags, lf->flags, F_CANINE); copyflag(corpse->flags, lf->flags, F_EQUINE); copyflag(corpse->flags, lf->flags, F_FELINE); f = hasflag(corpse->flags, F_CORPSEOF); if (f) { f->val[1] = gettr(lf); } // some corpses will regenerate... if (copyflag(corpse->flags, lf->flags, F_REVIVETIMER)) { killflagsofid(corpse->flags, F_OBHPDRAIN); } else { // non-regenerating corpses might bleed... if (damtypecausesbleed(lf->lastdamtype, NULL)) { char bname[BUFLEN]; if (getbloodobname(lf->flags, bname, B_FIRSTONE)) { int howlong; char *locbname; locbname = strdup(bname); // insert race name. for example, // replace 'splash of blood' with 'splash of xat blood' if (strends(locbname, " of blood")) { char replacewith[BUFLEN]; snprintf(replacewith, BUFLEN, " of %s blood",lf->race->name); strrep(&locbname, " of blood", replacewith, NULL); } // amount of time blood will appear for depends // on size. howlong = getlfsize(lf)*4; if (howlong > 0) { flag_t *bf; bf = addtempflag(corpse->flags, F_GENERATES, 80, 0, NA, locbname, howlong); dblog("xx"); } free(locbname); } } } // special case if (lf->race->id == R_BLASTBUG) { flag_t *hpflag; hpflag = hasflag(corpse->flags, F_OBHP); if (hpflag) hpflag->val[0] = rnd(5,6); } // corpse of a player pet? if (ispetof(lf, player)) { addflag(corpse->flags, F_PETOF, player->id, NA, NA, NULL); } // add extra flags ? getflags(lf->flags, retflag, &nretflags, F_CORPSEFLAG, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_CORPSEFLAG) { addflag(corpse->flags, f->val[0], f->val[1], f->val[2], NA, f->text); } } // inherit size from lf f = hasflag(corpse->flags, F_SIZE); if (f) { f->val[0] = getlfsize(lf); } else { addflag(corpse->flags, F_SIZE, getlfsize(lf), NA, NA, NULL); } // remember what killed us. f = hasflag(corpse->flags, F_CORPSEOF); if (f) { changeflagtext(f, lf->lastdam); } if (hasflag(corpse->flags, F_HEADLESS)) { object_t *headob; char headname[BUFLEN]; // drop head too snprintf(headname, BUFLEN, "%s head",lf->race->name); headob = addob(corpsecell->obpile, headname); if (headob) { flag_t *hpflag; colourmatchob(headob, lf); hpflag = hasflag(headob->flags, F_OBHP); if (hpflag) { hpflag->val[0] = pctof(10, lf->maxhp); limit(&(hpflag->val[0]), 1, NA); hpflag->val[1] = hpflag->val[0]; } } } if (corpse->type->id == OT_BABAYAGAHUT) { // link the hut to the new region. createbranchlink(corpsecell->map, corpsecell, corpse, NULL, BH_BABAYAGAHUT, corpsecell->map->region); } } // For bones files: if (thisisplayer) { char pname[BUFLEN]; getplayername(pname); addflag(corpse->flags, F_NAMED, NA, NA, NA, pname); if (streq(lf->killverb, "Eaten")) { // this will cause the player's corpse description // to show up as "partially eaten". used for // bones files. f = hasflag(corpse->flags, F_EDIBLE); if (f) { f->val[2] = f->val[1] / 2; } } } if (lf->race->id == R_SPRITEFIRE) { addobfast(corpsecell->obpile, OT_FIRESMALL); } } // some lfs have extra corpse objects getflags(lf->flags, retflag, &nretflags, F_EXTRACORPSE, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->val[0] == NA) || pctchance(f->val[0])) { addob(corpsecell->obpile, f->text); } } // ...or even create new lfs when they die! getflags(lf->flags, retflag, &nretflags, F_CORPSELF, F_NONE); if (nretflags) { createrace = retflag[rnd(0,nretflags-1)]->val[0]; } } // splatter if (!vaporised) { flag_t *f; f = lfhasflag(lf, F_DIESPLATTER); if (f) { int speed,howfar; howfar = f->val[0]; speed = f->val[1]; if (speed < 0) speed = 0; if (howfar < 0) howfar = UNLIMITED; fragments(corpsecell, f->text, f->val[1], f->val[0]); } } } // end if corpsecell // player killed last monster? if (killer && isplayer(killer) && !thisisplayer) { lifeform_t *l; int battledone = B_TRUE; for (l = lf->cell->map->lf ; l ; l = l->next) { if (!isplayer(l) && areenemies(l, player) && (l != lf)) { if ((getcelldist(l->cell, player->cell) <= 3) || haslof(l->cell, player->cell, LOF_WALLSTOP, NULL)) { battledone = B_FALSE; break; } } } if (battledone) { pleasegodmaybe(R_GODBATTLE, 1); } } // normally the god of nature purposely doesn't mind you killing // plants/animals ("survival of the fittest"), but if // you are in the sylvan forest... if (killer) { if ((lf->race->raceclass->id == RC_PLANT) && (lf->cell->map->region->rtype->id == BH_WOODS)) { magicwoods_angry(killer); } } if (willbecomeghost) { flag_t *f, *nextf; // remove all job flags lf->born = B_FALSE; foreach_bucket(b) { for (f = lf->flags->first[b] ; f ; f = nextf) { nextf = f->next; if (f->lifetime == FROMJOB) { killflag(f); } } } killflagsofid(lf->flags, F_JOB); lf->born = B_TRUE; // turn into a ghost setrace(lf, R_GHOST, B_TRUE); lf->hp = lf->maxhp; if (corpse) { char cid[BUFLEN]; snprintf(cid, BUFLEN, "%ld",corpse->id); addflag(lf->flags, F_LIFEOB, NA, 5, 2, cid); } } else { // Will the dead player get reanimated up as a monster in bones files? // if (!vaporised) { // announce if (strlen(reanimateas)) { if (thisisplayer || cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s%s corpse rises up as %s %s!^n", getlfcol(player, CC_VBAD), lfname, getpossessive(lfname), needan(reanimateas) ? "an" : "a", reanimateas); } } } // now kill the lifeform. //if (lf->controller != C_PLAYER) { if (!thisisplayer) { // actually kill the lifeform killlf(lf); assert(where->lf == NULL); } } // IMPORTANT: DO NOT REFERENCE lf->xxxx AFTER THIS POINT // UNLESS WE ARE _SURE_ IT IS THE PLAYER (ie. thisisplayer = true) if (createrace != R_NONE) { lifeform_t *newlf; newlf = addmonster(where, createrace, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (newlf) { // no xp for killing killflagsofid(newlf->flags, F_XPVAL); addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL); } } else if (dividerace != R_NONE) { if (dividelos) { msg("^w%s divides!", dividename); } // add two new worms nearby, with less hp. for (i = 0;i < 2; i++) { lifeform_t *newlf; newlf = addmonster(dividecell[i], dividerace, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (newlf) { // half hp f = hasflag(newlf->flags, F_HITDICE); f->val[0] = 0; f->val[1] = 1; f->val[2] = dividehp; newlf->maxhp = dividehp; newlf->hp = dividehp; // half TR f = hasflag(newlf->flags, F_TR); f->val[0] = dividetr; // no xp for killing killflagsofid(newlf->flags, F_XPVAL); addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL); } } } // Note that if we reanimate, the new monster will appear in an // ADJACENT cell. this solves the problem that we can't actually // kill() the player's lf because we need to see their objects to // show their final possessions. if (strlen(reanimateas)) { cell_t *c = NULL; // announce, then add on " named playername", if required. if (lf && isplayer(lf)) { char pname[BUFLEN]; getplayername(pname); strcat(reanimateas, " named "); strcat(reanimateas, pname); } if (where->lf) { c = getrandomadjcell(where, &ccwalkable, B_ALLOWEXPAND); } else { c = where; } if (c) { // remove the corpse... if (corpse) killob(corpse); // add the reanimated monster addmonster(c, R_SPECIFIED, reanimateas, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); } } // Write out a bones file? A bones file might be written if: // - the player died in a ROOM. // AND // - the player died in a major dungeon branch, which wasn't the realm of gods if (thisisplayer && (where->room) && (where->map->region->rtype->majorbranch) && (where->map->region->rtype->id != BH_HEAVEN)) { // If all of the above are true, the below will trigger a bones file: // Player reanimating as some kind of monster // Being killed by stoning // Random chance // if (strlen(reanimateas)) { dobonesfile = B_TRUE; } else if (corpse && (corpse->type->id == OT_STATUE)) { dobonesfile = B_TRUE; } else if (onein(4)) { dobonesfile = B_TRUE; } if (hasjob(lf, J_GOD)) { char ch; char ques[BUFLEN]; snprintf(ques, BUFLEN, "Bones file dump = %s. Override?", dobonesfile ? "true" : "false"); ch = askchar(ques, "yn", "n", B_TRUE, B_FALSE); if (ch == 'y') { ch = askchar("Dump bones file?", "yn", "n", B_TRUE, B_FALSE); if (ch == 'y') { dobonesfile = B_TRUE; } else { dobonesfile = B_FALSE; } } } } if (dobonesfile) { savebones(where->map, where->room); } if (needredraw) { drawscreen(); } if (killedgod) { wingame(); } return B_FALSE; } void dumplf(void) { lifeform_t *lf; map_t *startmap; int count = 0; dblog("START LIFEFORM DUMP:"); if (player) { startmap = player->cell->map; } else { startmap = firstmap; } for (lf = startmap->lf ; lf ; lf = lf->next) { char buf[BUFLEN]; snprintf(buf, BUFLEN," timespent=%3d id %d race %s, redraws last turn=%d",lf->timespent, lf->id, lf->race->name, lf->redraws); if (cansee(player, lf)) { strcat(buf, "(seen by player)"); } dblog("%s", buf); count++; } dblog("END LIFEFORM DUMP (%d found)",count); } void dumpmonsters(enum HABITAT hab) { race_t *r; flag_t *f; habitat_t *h; int wanthd,i; int totcount = 0; FILE *out; //f = hasflagval(r->flags, F_RARITY, map->habitat->id, NA, NA, NULL); out = fopen("monsters.html", "wt"); assert(out); h = findhabitat(hab); fprintf(out, "\n"); fprintf(out, "\n"); fprintf(out, "

Monsters for habitat '%s'

\n",h ? h->name : ""); fprintf(out, "\n"); fprintf(out, ""); for (i = RR_FREQUENT; i <= RR_UNIQUE; i++) { fprintf(out, "",getrarityname(i)); } fprintf(out, "\n"); dblog("START MONSTER DUMP:"); for (wanthd = 0; wanthd <= maxmonhitdice ; wanthd++) { int count = 0; // count them for (r = firstrace ; r ; r = r->next) { if (gettrrace(r) == wanthd) { count++; totcount++; } } dblog("MONSTERS WITH THREAT RATING %d (%d found):",wanthd, count); fprintf(out, "\n"); fprintf(out, "\t\n", wanthd); for (i = RR_FREQUENT; i <= RR_UNIQUE; i++) { int thiscount = 0; fprintf(out, "\t\n"); } fprintf(out, "\n"); } dblog("END MONSTER DUMP (%d found)",totcount); fprintf(out, "
 %s
Threat Rating %d\n"); for (r = firstrace ; r ; r = r->next) { int thishd; enum RARITY rr; thishd = gettrrace(r); getracerarity(hab, r->id, &rr); if ((thishd == wanthd) && (rr == i)) { int max; int thisxp; f = hasflag(r->flags, F_HITDICE); max = flagtomaxhp(f); thisxp = calcxprace(r->id); dblog("\t%s (%d hp, %d xp)",r->name, max, thisxp); fprintf(out, "\t\t%s (%d hp, %d xp)
\n",r->name, max, thisxp); thiscount++; } } if (!thiscount) { fprintf(out, " \n"); } fprintf(out, "\t
\n"); fprintf(out, "\n"); fprintf(out, "\n"); fclose(out); } void genareaknowledge(flagpile_t *fp, int chancemod) { char knownstuff[BUFLENSMALL]; if (hasflag(fp, F_KNOWSABOUT)) return; strcpy(knownstuff, ""); // determine what someone knows about // stairs? (high chance) if (pctchance(75 + chancemod)) { strcat(knownstuff, "e"); } // rare monsters/objects? (highish chance) if (pctchance(60 + chancemod)) { strcat(knownstuff, "o"); } if (pctchance(60 + chancemod)) { strcat(knownstuff, "m"); } // shops? (med chance) if (pctchance(50 + chancemod)) { strcat(knownstuff, "s"); } // traps? (low chance) if (pctchance(25 + chancemod)) { strcat(knownstuff, "t"); } addflag(fp, F_KNOWSABOUT, NA, NA, NA, knownstuff); } // populates 'buf' with a string containing // G, N or E based on possible alignments from // the given flagpile's F_ALIGNMENT flags. // // returns # of possibilities int genalignmentlist(flagpile_t *fp, char *buf) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i,n,nposs = 0; char allaligns[] = { 'g','n','e' }; char localposs[BUFLEN]; // start with no possibilities strcpy(localposs, ""); // get a list of any conditions... getflags(fp, retflag, &nretflags, F_ALIGNMENT, F_NONE); // for each G, N, E for (n = 0; n < 3; n++ ) { char candidate = '\0'; candidate = allaligns[n]; // does it match all flags? for (i = 0; i < nretflags; i++) { char this[BUFLEN]; strcpy(this, ""); switch (retflag[i]->val[0]) { case AL_GOOD: strcpy(this, "g"); break; case AL_NEUTRAL: strcpy(this, "n"); break; case AL_EVIL: strcpy(this, "e"); break; default: case AL_NONE: strncpy(this, retflag[i]->text, BUFLEN); break; } if (!strchr(this, candidate)) { // not possible candidate = '\0'; break; } } // still possible? if (candidate != '\0') { if (!strchr(localposs, candidate)) { // not already in list? char letter[2]; // this one is okay. snprintf(letter, 2, "%c", candidate); strcat(localposs, letter); nposs++; } } } if (buf) { strcpy(buf, localposs); } return nposs; } // if alignment needs to be generated randomly (or selected in the case of the player), do so. void generatealignment(lifeform_t *lf) { int nposs,i; char possbuf[BUFLEN],buf[BUFLEN],buf2[BUFLEN],ch; enum ALIGNMENT wantalignment = AL_NONE; // get a list of all possible alignments nposs = genalignmentlist(lf->flags, possbuf); getplayername(buf2); snprintf(buf, BUFLEN, "%s, select your alignment:", buf2); initprompt(&prompt, buf); if (nposs == 0) { if (isplayer(lf)) { assert("Error - no possible alignment for player." == 0); } else { // monster has no alignment addchoice(&prompt, '-', "None", NULL, NULL, NULL); } } else { for (i = 0; i < strlen(possbuf); i++) { if (possbuf[i] == 'g') addchoice(&prompt, 'g', "Good", NULL, NULL, NULL); if (possbuf[i] == 'n') addchoice(&prompt, 'n', "Neutral", NULL, NULL, NULL); if (possbuf[i] == 'e') addchoice(&prompt, 'e', "Evil", NULL, NULL, NULL); } } assert(prompt.nchoices > 0); if (prompt.nchoices == 1) { ch = prompt.choice[0].ch; } else if (isplayer(lf)) { ch = getchoice(&prompt); } else { ch = prompt.choice[rnd(0,prompt.nchoices-1)].ch; } // remove other flags killflagsofid(lf->flags, F_ALIGNMENT); // now set alignment switch (ch) { case 'g': wantalignment = AL_GOOD; break; case 'n': wantalignment = AL_NEUTRAL; break; case 'e': wantalignment = AL_EVIL; break; default: case '-': wantalignment = AL_NONE; break; } addflag(lf->flags, F_ALIGNMENT, wantalignment, NA, NA, NULL); } void genxplist(void) { race_t *r; race_t *racetemp; int xptemp; int count = 0; int i; int donesomething; // count races for (r = firstrace ; r; r = r->next) { count++; } // allocate space raceposs = malloc(count * sizeof(race_t *)); xpposs = malloc(count * sizeof(int)); // get xpval for all races xplistlen = 0; for (r = firstrace ; r; r = r->next) { raceposs[xplistlen] = r; xpposs[xplistlen] = calcxprace(r->id); xplistlen++; } // bubblesort donesomething = B_TRUE; while (donesomething) { donesomething = B_FALSE; for (i = 0; i < (xplistlen-1); i++) { if (xpposs[i] > xpposs[i+1]) { // swap with next xptemp = xpposs[i]; racetemp = raceposs[i]; xpposs[i] = xpposs[i+1]; raceposs[i] = raceposs[i+1]; xpposs[i+1] = xptemp; raceposs[i+1] = racetemp; donesomething = B_TRUE; break; } } } } void dumpxp(void) { int i; // dump dblog("%-10s%-30s%s","XP", "Race", "Rarity"); for (i = 0; i < xplistlen; i++) { dblog("%-10d%-30s%d",xpposs[i], raceposs[i]->name,getracerarity(H_ALL, raceposs[i]->id, NULL)); } // free mem free(xpposs); free(raceposs); // dump xp for levels dblog(""); dblog(""); for (i = 2; i < 30; i++) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "Lev %d",i); dblog("%-10s%ld",buf, getxpforlev(i)); } } // pass o OR ot, not both. int digcell(lifeform_t *lf, cell_t *c, object_t *o, int dismantle) { char obname[BUFLEN]; flag_t *f; int digpower = 1; if (!c) { return B_TRUE; } if (o) { f = hasflag(o->flags, F_HELPSDIG); if (f) { digpower = f->val[0]; } getobname(o, obname, 1); } if (c->type->solid) { turntoface(lf, c); if (isdiggable(c, dismantle ? OT_A_DISMANTLE : o->type->id)) { char digoid[BUFLEN]; // start digging! if (o && !dismantle) { snprintf(digoid, BUFLEN, "%ld",o->id); } else { strcpy(digoid,""); } addflag(lf->flags, F_DIGGING, c->x, c->y, digpower, digoid); if (isplayer(lf)) { if (dismantle) { msg("You start dismantling %s %s.", needan(c->type->name) ? "an" : "a", c->type->name); } else { msg("You start digging into %s %s.", needan(c->type->name) ? "an" : "a", c->type->name); } needredraw = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s starts %s %s %s.", lfname, dismantle ? "dismantling" : "digging into", needan(c->type->name) ? "an" : "a", c->type->name); needredraw = B_TRUE; } taketime(lf, getactspeed(lf)); } else { // fail if (isplayer(lf)) { if (dismantle) { msg("You can only dismantle man-made walls."); } else { msg("This wall is too hard to dig."); } } return B_TRUE; } } else { // not solid int failed = B_FALSE; object_t *door; door = hasobwithflag(c->obpile, F_DOOR); if (door) { int dooropen; // only closed doors! isdoor(door, &dooropen); if (dooropen) { door = NULL; } } if (door) { if (!isimmuneto(door->flags, DT_CHOP, B_FALSE)) { taketime(lf, getactspeed(lf)); removeob(door, door->amt); if (isplayer(lf)) { msg("You smash open a door!"); needredraw = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s smashes open a door.",lfname); needredraw = B_TRUE; } drawscreen(); failed = B_FALSE; } } else if (hasob(c->obpile, OT_STATUE)) { int dam; object_t *so; char statname[BUFLEN]; flag_t *f; so = hasob(c->obpile, OT_STATUE); getobname(so, statname, so->amt); taketime(lf, getactspeed(lf)); // statue takes 1/2 damage f = hasflag(so->flags, F_OBHP); if (f) { dam = (f->val[1] / 2); // ie. half max hp } else { dam = 1; } // statue ? if (isplayer(lf)) { msg("You hit %s with your %s.", statname, noprefix(obname)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s hits %s with %s.", lfname, statname, obname); } takedamage(so, dam, DT_CHOP, lf); } else { if (isplayer(lf)) { msg("You swing your %s through the air.",noprefix(obname)); } taketime(lf, getactspeed(lf)); } } return B_FALSE; } int digdown(lifeform_t *lf, object_t *o) { char lfname[BUFLEN]; enum OBTYPE digoid = OT_NONE; getlfname(lf, lfname); if (o) { if (lfhasflag(lf, F_LEVITATING)) { if (isplayer(lf)) { msg("You can't reach the ground from up here!"); } return B_TRUE; } digoid = o->type->id; } // is cell diggable at all? if (!isdiggable(lf->cell, digoid)) { if (isplayer(lf)) { msg("The floor here is too hard to dig through."); } else if (cansee(player, lf)) { msg("%s tries to dig a hole in the floor, but fails.", lfname); } return B_TRUE; } // TODO: check if the floor is solid? if (lf->cell->map->depth >= lf->cell->map->region->rtype->maxdepth) { if (isplayer(lf)) { msg("The floor here is too solid to dig in."); } else if (cansee(player, lf)) { msg("%s tries to dig a hole in the floor, but fails.", lfname); } return B_TRUE; } if (isplayer(lf)) { msg("You dig a hole in the floor."); } else if (cansee(player, lf)) { msg("%s digs a hole in the floor.", lfname); } addobfast(lf->cell->obpile, OT_HOLEINGROUND); // takes a lot of time if (o) { taketime(lf, getactspeed(lf) * 9); } return B_FALSE; } int digup(lifeform_t *lf, object_t *o) { char lfname[BUFLEN]; getlfname(lf, lfname); // no roof? if (lf->cell->map->region->rtype->id == BH_WORLDMAP) { if (isplayer(lf)) { msg("There is no roof above you to dig into!"); } return B_TRUE; } // if digging with an object, you must be able to reach the roof if (o) { if (!isairborne(lf, NULL)) { if (isplayer(lf)) { msg("You can't reach the roof!"); } return B_TRUE; } } if (isplayer(lf)) { msg("You dig a hole in the roof."); } else if (cansee(player, lf)) { msg("%s digs a hole in the roof."); } // add some stones here addob(lf->cell->obpile, "20-50 stones"); addobfast(lf->cell->obpile, OT_HOLEINROOF); // takes a LOT of time since gravity is against us if (o) { taketime(lf, getactspeed(lf) * 18); } return B_FALSE; } /* void do_eyesight_adjust(lifeform_t *lf) { int i,nlitcells = 0; int preea; if (isblind(lf)) return; // any lit cells within los? for (i = 0; i < lf->nlos; i++) { if (lf->los[i]->lit) { nlitcells++; } } preea = lf->eyeadjustment; if (nlitcells) { // if you could see any lit cells, you lose your eyeadjustment for nightvision if (lf->eyeadjustment/10) { lf->eyeadjustment = 0; if (isplayer(lf)) msg("The light causes you to lose your natural night sight."); setlosdirty(lf); //precalclos(lf); } } else if (!lfhasflag(lf, F_SEEINDARK)) { if ((lf->cell->lit == L_NOTLIT) && (lf->eyeadjustment < MAX_EYEADJ)) { // your eyes start to adjust... lf->eyeadjustment++; if (isplayer(lf) && (lf->eyeadjustment/10) > (preea/10)) { if (preea/10) { msg("Your eyes have fully adjusted to the darkness."); } else { msg("Your eyes are starting to adjust to the darkness."); } } setlosdirty(lf); //precalclos(lf); } } } */ // dump which level random things will appear at void dumplev(void) { int i; race_t *r; //objecttype_t *ot; //flag_t *f; dblog("Start lev monster dump"); dblog("------------------\n"); // NOTE: this code copied from getrandomrace(), which is used by addmonster(). for (i = 1; i <= 25; i++) { int min,max,prevmin,prevmax; gettrrange(i-1, &prevmin, &prevmax, RARITYVARIANCELF, B_FALSE); gettrrange(i, &min, &max, RARITYVARIANCELF, B_FALSE); fprintf(logfile, "Dlev %d (hd %d-%d): ",i,min,max); for (r = firstrace ; r; r = r->next) { int hd = 0; hd = gettrrace(r); // ok this lev? if ((hd >= min) && (hd <= max)) { char buf[BUFLEN]; strcpy(buf, ""); // ok on previous lev too? if ((hd >= prevmin) && (hd <= prevmax)) { // only print if dlev is 1 if (i == 1) { snprintf(buf, BUFLEN, "%s, ", r->name); } } else { // ie. new mosnter for this lev //snprintf(buf, BUFLEN, "*%s*, ", r->name); //makeuppercase(buf); snprintf(buf, BUFLEN, "%s, ", r->name); } fprintf(logfile, "%s", buf); } } fprintf(logfile, "\n"); } /* dblog("Start object dump"); dblog("------------------\n"); for (i = 1; i <= 25; i++) { int min,max,prevmin,prevmax; getrarityrange(i-1, &prevmin, &prevmax, RARITYVARIANCEOB, B_FALSE); getrarityrange(i, &min, &max, RARITYVARIANCEOB, B_FALSE); fprintf(logfile, "Dlev %d (rar >= %d): ",i,min); for (ot = objecttype ; ot; ot = ot->next) { int rarity = 0; f = hasflagval(ot->flags, F_RARITY, H_DUNGEON, NA, NA, NULL); if (f) { rarity = f->val[1]; // ok this lev? if ((rarity >= min) && (rarity <= max)) { char buf[BUFLEN]; strcpy(buf, ""); // ok on previous lev too? if ((rarity >= prevmin) && (rarity <= prevmax)) { // only print if dlev is 1 if (i == 1) { snprintf(buf, BUFLEN, "%s, ", ot->name); } } else { // ie. new object for this lev //snprintf(buf, BUFLEN, "*%s*, ", r->name); //makeuppercase(buf); snprintf(buf, BUFLEN, "%s, ", ot->name); } fprintf(logfile, "%s", buf); } } } fprintf(logfile, "\n"); } */ fflush(logfile); } int eat(lifeform_t *lf, object_t *o) { char lfname[BUFLEN]; char obname[BUFLEN]; object_t *oo; char buf[BUFLEN]; flag_t *f; double nutrition; double turnstoeat; double eateachturn; double startpcteaten = 0; double pcteaten; int drinking = B_FALSE; int amt; int fullyeaten = B_FALSE; flag_t *alreadyeating; enum HUNGER hlev,posthlev; int stopeating = B_FALSE; int rawmeat = B_FALSE; race_t *corpserace = NULL; flag_t *cf; if (lfhasflag(lf, F_RAGE)) { if (isplayer(lf)) msg("You are too enraged to eat!"); return B_TRUE; } if (hasflag(o->flags, F_DRINKABLE)) { drinking = B_TRUE; } if (drinking) { if (!candrink(lf, o)) { if (isplayer(lf)) { cantdrink(reason); } return B_TRUE; } } else { if (!caneat(lf, o)) { if (isplayer(lf)) { switch (reason) { case E_NOCANNIBUL: msg("The idea of eating your own race is abhorrent to you."); break; case E_UNDEAD: msg("You are undead and don't need to eat."); break; case E_CARNIVORE: msg("The thought of eating plant matter disgusts you."); break; case E_VEGETARIAN: msg("The thought of eating flesh disgusts you."); break; case E_PARTVEGETARIAN: msg("You aren't hungry enough to eat meat yet."); break; case E_FACECOVERED: oo = facecovered(lf); assert(oo); getobname(oo,buf, 1); msg("You can't eat through your %s!", noprefix(buf)); break; case E_NAUSEATED: msg("You are too nauseated to eat."); break; case E_WRONGOBTYPE: default: msg("You can't eat that!"); break; } } return B_TRUE; } } getobname(o, obname, 1); getlfname(lf, lfname); // is this a corpse? cf = hasflag(o->flags, F_CORPSEOF); if (cf) { corpserace = findrace(cf->val[0]); // special case if (lf->race->id == R_LINGPARASITE) { lifeform_t *newlf; if (isplayer(lf)) { msg("You crawl inside %s.", obname); } else if (cansee(player, lf)) { msg("%s crawls inside %s.", lfname, obname); } // die, but animate the zombie! addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); setlastdam(lf, "merging with a corpse"); lf->lastdamtype = DT_DIRECT; lf->hp = 0; newlf = makezombie(o, 0, "rises from the dead", NULL); if (newlf) { addflag(newlf->flags, F_CORPSELF, R_LINGPARASITE, NA, NA, NULL); } return B_FALSE; } } // get total nutrition nutrition = getnutrition(o); if (nutrition == 0) { // this might happen if you purposely try to eat something non-edible but drinkable // (eg. a potion). also when you have f_caneatmaterial and you something which // isn't normally edible (ie. a goat eating wood). if (lfhasflagval(lf, F_CANEATMATERIAL, o->material->id, NA, NA, NULL)) { // nutrition based on object weight nutrition = getobmass(o)*5; } else { if (isplayer(lf)) { msg("That doesn't seem very nutritious..."); } return B_TRUE; } } // only do this check for the player - basically it should // handle the case where we have poluymorphed into something // which doesn't eat. if (isplayer(lf)) { f = hasflag(lf->flags, F_HUNGER); if (!f) { msg("You don't need to %s!", drinking ? "drink" : "eat"); return B_TRUE; } } // after this point, you ARE going to eat it. if (o->amt > 1) { o = splitob(o); } // uncooked meat? if (iscorpse(o) && isplayer(lf)) { int ch; char ques[BUFLEN]; if (!hasflag(o->flags, F_PREPARED) && !lfhasflag(lf, F_CANEATRAW)) { if (!lfhasflag(lf, F_EATING) && getskill(lf, SK_COOKING)) { more(); snprintf(ques, BUFLEN,"Really eat %s raw?",obname); ch = askchar(ques,"yn","y", B_TRUE, B_FALSE); if (ch != 'y') { return B_TRUE; } } // limit nutrition //if (nutrition > (HUNGERCONST/2)) nutrition = (HUNGERCONST/2); nutrition /= 2; rawmeat = B_TRUE; } // cannibulism ? if (corpserace->id == lf->race->baseid) { if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { more(); snprintf(ques, BUFLEN,"Really eat your own race?"); ch = askchar(ques,"yn","y", B_TRUE, B_FALSE); if (ch != 'y') { return B_TRUE; } } } } if (touch(lf, o)) { taketime(lf, getactspeed(lf)); return B_TRUE; } // how many turns to eat whole thing? f = hasflag(o->flags, F_EDIBLE); if (f && (f->val[2] != NA)) { startpcteaten = f->val[2]; } else { startpcteaten = 0; } if (drinking) { // when drinking you can drink all of it. turnstoeat = 1; } else { // time to eat entire food: turnstoeat = getobweight(o) / (getlfweight(lf, B_NOOBS) / 10); } if (startpcteaten > 0) { turnstoeat -= ((startpcteaten/100) * turnstoeat); } // now find out how much we'll eat in one turn. if (turnstoeat <= 1) { eateachturn = ((100 - startpcteaten)/100) * nutrition; fullyeaten = B_TRUE; } else { eateachturn = nutrition / turnstoeat; } pcteaten = (eateachturn / nutrition) * 100; // announce snprintf(buf, BUFLEN, "%ld",o->id); alreadyeating = lfhasflagval(lf, F_EATING, NA, NA, NA, buf); // announce if (turnstoeat <= 1) { char taste[BUFLEN]; if (rawmeat) { snprintf(taste, BUFLEN, "The raw meat tastes disgusting!"); } else if (hasflagval(o->flags, F_CORPSEOF, R_CHICKEN, NA, NA, NULL)) { snprintf(taste, BUFLEN, "Tastes like chicken!"); } else if (f && (f->val[1] >= 20)) { snprintf(taste, BUFLEN, "Yum!"); } else { strcpy(taste, ""); } if (alreadyeating) { if (isplayer(lf)) { msg("You finish %s.", drinking ? "drinking" : "eating"); if (strlen(taste)) msg("%s", taste); } else if (cansee(player, lf)) { msg("%s finishes %s.", lfname, drinking ? "drinking" : "eating"); } else { noise(lf->cell, lf, NC_OTHER, SV_WHISPER, drinking ? "something being quaffed." : "something being eaten.", NULL); } } else { if (isplayer(lf)) { msg("You %s %s.", drinking ? "drink" : "eat", obname); if (strlen(taste)) msg("%s", taste); } else if (cansee(player, lf)) { msg("%s %s %s.", lfname, drinking ? "drinks" : "eats", obname); } else { noise(lf->cell, lf, NC_OTHER, SV_WHISPER, drinking ? "something being quaffed." : "something being eaten.", NULL); } } } else { if (alreadyeating) { if (isplayer(lf)) { msg("You continue %s.", drinking ? "drinking" : "eating"); } else if (cansee(player, lf)) { msg("%s continues %s.", lfname, drinking ? "drinking" : "eating"); } else { noise(lf->cell, lf, NC_OTHER, SV_TALK, drinking ? "something being quaffed." : "something being eaten.", NULL); } } else { if (isplayer(lf)) { msg("You start %s %s.", drinking ? "drinking" : "eating", obname); } else if (cansee(player, lf)) { msg("%s starts %s %s.", lfname, drinking ? "drinking" : "eating", obname); } else { noise(lf->cell, lf, NC_OTHER, SV_TALK, drinking ? "something being quaffed." : "something being eaten.", NULL); } } } // ai stops chasing things loseaitargets(lf); if (alreadyeating) { if (turnstoeat <= 1) { killflag(alreadyeating); } } else if (!alreadyeating && (turnstoeat > 1)) { addflag(lf->flags, F_EATING, NA, NA, NA, buf); } if (!isimmuneto(lf->flags, DT_POISON, B_FALSE)) { if (isrotting(o)) { char dambuf[BUFLEN]; char plainobname[BUFLEN]; enum POISONTYPE ptid; int ppower = 1; getobnametruebase(o, plainobname, 1); // lose hp if (isplayer(lf)) { msg("^BThat %s was bad!", drinking ? "liquid" : "food"); } // food poisoning for 20 turns if (drinking) { snprintf(dambuf, BUFLEN, "%s",plainobname); } else { snprintf(dambuf, BUFLEN, "a bad %s",noprefix(plainobname)); } if (onein(3)) { ptid = P_FOODBAD; } else { ptid = P_FOOD; } // cannibulism ? if (corpserace && (corpserace->id == lf->race->baseid)) { ppower = 3; } poison(lf, rnd(20,40), ptid, ppower, dambuf, cf ? cf->val[0] : R_NONE, B_FALSE); } else if (!drinking) { // raw meat? if (corpserace && israwmeat(o) && !lfhasflag(lf, F_CANEATRAW)) { int checkdiff; enum POISONTYPE ptid; int timemin,timemax; int ppower = 1; char dambuf[BUFLEN]; snprintf(dambuf, BUFLEN, "a bad %s",noprefix(obname)); if (hasflag(corpserace->flags, F_AVIAN)) { checkdiff = 120; ptid = P_FOODBAD; timemin = 30; timemax = 50; } else { checkdiff = 85; if (onein(3)) { ptid = P_FOOD; } else { ptid = P_MIGRAINE; } timemin = 20; timemax = 40; } // fresh food makes the check easier if (getobhppct(o) >= 80) { checkdiff = pctof(70, checkdiff); } // cannibulism ? if (corpserace->id == lf->race->baseid) { ppower = 3; } if (!skillcheck(lf, SC_POISON, checkdiff, 0)) { poison(lf, rnd(timemin,timemax), ptid, ppower, dambuf, corpserace ? corpserace->id : R_NONE, B_FALSE); } } } } // get less hungry hlev = gethungerlevel(gethungerval(lf)); modhunger(lf, -eateachturn); posthlev = gethungerlevel(gethungerval(lf)); if (fullyeaten) { // special cases if (!hasflag(o->flags, F_TAINTED)) { flag_t *mutable = NULL; flag_t *retflag[MAXCANDIDATES]; int nretflags,i,curechance = 0; if (hasflagval(o->flags, F_CORPSEOF, R_NEWT, NA, NA, NULL)) { // think "eye of newt" gainmp(lf, 2); } if (hasflagval(o->flags, F_CORPSEOF, R_EYEBAT, NA, NA, NULL)) { lf->maxmp++; if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); msg("You feel slightly more magically attuned."); } } else if (hasflagval(o->flags, F_CORPSEOF, R_BEHOLDER, NA, NA, NULL)) { lf->maxmp += 10; if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); msg("You feel much more magically attuned!"); } } // most eat effects only happen if you have f_MUTABLE. mutable = lfhasflag(lf, F_MUTABLE); // healthy food has a chance to cure poison getflags(o->flags, retflag, &nretflags, F_HEALTHY, F_NONE); for (i = 0; i < nretflags; i++) { if (f->val[0] >= 0) { curechance += f->val[0]; } } if (curechance > 0) { int luckmod = 0; sumflags(lf->flags, F_EXTRALUCK, &luckmod, NULL, NULL); curechance += (luckmod*5); limit(&curechance, 0, 100); if (pctchance(curechance)) { killflagsofid(lf->flags, F_POISONED); // cure poison } } getflags(o->flags, retflag, &nretflags, F_EATCONFER, F_EATMUTATE, F_NONE); for (i = 0; i < nretflags; i++) { int chance; int luckmod = 0; f = retflag[i]; if ((f->id == F_EATMUTATE) && !mutable) continue; chance = atoi(f->text); sumflags(lf->flags, F_EXTRALUCK, &luckmod, NULL, NULL); chance += (luckmod*5); limit(&chance, 0, 100); if (pctchance(chance)) { enum FLAG fid; int val[2]; fid = f->val[0]; val[0] = f->val[1]; val[1] = f->val[2]; // already got this? if (lfhasflag(lf, fid)) { int changed = B_FALSE; // sometimes improve it switch (fid) { case F_DTRESIST: fid = F_DTIMMUNE; changed = B_TRUE; break; case F_SEEINDARK: case F_PRODUCESLIGHT: break; // can have multiple. default: fid = F_NONE; break; } // still got the new one? if (changed && (fid != F_NONE)) { if (lfhasflag(lf, fid)) { fid = F_NONE; } } } if (fid != F_NONE) { if (f->id == F_EATMUTATE) { // lose half your max hp! losehp_real(lf, (lf->maxhp/2), DT_DIRECT, NULL, "the shock of mutation", B_NODAMADJUST, o, B_NORETALIATE, NULL, B_DAMEFFECTS, BP_NONE, B_NOCRIT); if (isplayer(lf)) { msg("^%cYou convulse in agony as your body mutates!", getlfcol(lf, CC_BAD)); } else if (cansee(player, lf)) { msg("^%c%s convulses in agony as its body mutates!", getlfcol(lf, CC_BAD), lfname); } } // still alive? you gain the ability! if (!isdead(lf)) { addflag(lf->flags, fid, val[0], val[1], NA, NULL); } } } // end if pctchance } // end foreach f_eatconfer flag } // end if !tainted // special cases for object types if (o->type->id == OT_BANANA) { object_t *skin; skin = addobfast(lf->pack, OT_BANANASKIN); if (skin) { if (isplayer(lf)) { char skinname[BUFLEN]; getobname(skin, skinname, 1); msgnocap("%c - %s", skin->letter, skinname); } } else { skin = addobfast(lf->cell->obpile, OT_BANANASKIN); if (skin && cansee(player, lf)) { char skinname[BUFLEN]; getobname(skin, skinname, 1); msg("%s drop%s %s on the ground.",lfname, isplayer(lf) ? "" : "s", skinname); } } } else if (o->type->id == OT_CARROT) { killtransitoryflags(lf->flags, F_BLIND); addtempflag(lf->flags, F_SEEINDARK, 3, NA, NA, NULL, rnd(20,40)); } else if (o->type->id == OT_MUSHROOMGREY) { addtempflag(lf->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, rnd(20,40)); } else if (o->type->id == OT_POISONSAC) { // very bad! poison(lf, rnd(10,20), P_VENOM, 4, "eating a venom sac", R_NONE, B_TRUE); } if (isplayer(lf)) makeknown(o->type->id); } // end if fullyeaten // take time amt = getactspeed(lf); if (o->pile->owner != lf) { amt += SPEED_PICKUP; } // humanoids can use knives and forks to eat faster. if (getraceclass(lf) == RC_HUMANOID) { int pct = 100; if (hasob(lf->pack, OT_FORK)) { pct -= 25; } if (hasob(lf->pack, OT_KNIFE) || hasob(lf->pack, OT_STEAKKNIFE)) { pct -= 25; } amt = pctof(pct, amt); } limit(&amt, SP_ULTRAFAST, NA); taketime(lf, amt); // special cases even if not fully eaten if (hasflagval(o->flags, F_CORPSEOF, R_DOGBLINK, NA, NA, NULL)) { // blink! dospelleffects(lf, OT_S_BLINK, 1, lf, NULL, NULL, B_UNCURSED, NULL, B_TRUE, NULL); stopeating = B_TRUE; } else if (hasflagval(o->flags, F_CORPSEOF, R_SPIDERPHASE, NA, NA, NULL)) { addtempflag(lf->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL, 15); stopeating = B_TRUE; } else if (hasflagval(o->flags, F_CORPSEOF, R_BLASTBUG, NA, NA, NULL)) { dospelleffects(lf, OT_S_DETONATE, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, NULL); stopeating = B_TRUE; } // you can gain F_MUTABLE even if you don't fully eat the corpse. f = hasflagval(o->flags, F_EATCONFER, F_MUTABLE, NA, NA, NULL); if (f) { if (!lfhasflag(lf, F_MUTABLE)) addflag(lf->flags, F_MUTABLE, B_TRUE, NA, NA, NULL); } // god effects if (isplayer(lf)) { // eating your pet is very bad! if (hasflagval(o->flags, F_PETOF, player->id, NA, NA, NULL)) { angergodmaybe(R_GODPURITY, 150, GA_EAT); stopeating = B_TRUE; } // cannibulism ? if (corpserace && (corpserace->id == lf->race->baseid)) { angergodmaybe(R_GODPURITY, 150, GA_EAT); angergodmaybe(R_GODNATURE, 100, GA_EAT); stopeating = B_TRUE; } } if (lfhasflagval(lf, F_FATALFOOD, o->type->id, NA, NA, NULL)) { char buf[BUFLEN]; // die. lf->hp = 0; snprintf(buf, BUFLEN, "eating %s", obname); setlastdam(lf, buf); stopeating = B_TRUE; } // stop eating if we are full if (!stopeating && !fullyeaten && (posthlev != hlev) && (posthlev <= H_FULL)) { if (isplayer(lf) && (posthlev != H_STUFFED)) { int ch; more(); ch = askchar("Stop eating?","yn","y", B_TRUE, B_FALSE); if (ch == 'y') { stopeating = B_TRUE; } } else { stopeating = B_TRUE; } } if (stopeating) { killflagsofid(lf->flags, F_EATING); } if (fullyeaten) { // eating animals pleases ekrub if (corpserace && (corpserace->raceclass->id == RC_ANIMAL)) { //if (gethungerlevel(gethungerval(lf)) > H_NONE) { pleasegodmaybe(R_GODNATURE, 2); addflag(o->flags, F_NOSACRIFICE, B_TRUE, NA, NA, NULL); //} } // special cases only when eaten switch (o->type->id) { case OT_BREADGARLIC: addtempflag(lf->flags, F_STENCH, 2, NA, NA, NULL, rnd(30,50)); gainhp(lf,rnd(6,12)); break; case OT_CAKEFRUIT: setstamina(lf, getmaxstamina(lf)); gainhp(lf, lf->maxhp); gainmp(lf, getmaxmp(lf)); break; case OT_CHOCOLATE: setstamina(lf, getmaxstamina(lf)); break; case OT_CURADOUGH: addtempflag(lf->flags, F_SLOWMETAB, 3, NA, NA, NULL, rnd(100,200)); gainhp(lf, lf->maxhp); break; case OT_PSITRUFFLE: addtempflag(lf->flags, F_LEARNBOOST, 100, NA, NA, NULL, 100); break; case OT_HOTDOG: addtempflag(lf->flags, F_ATTRMOD, A_STR, 3, NA, NULL, rnd(30,50)); gainhp(lf,rnd(6,12)); break; case OT_MUSHROOMSTUFFED: lf->maxhp++; break; case OT_RUMBALL: killflagsofid(lf->flags, F_PAIN); gainhp(lf, rnd(5,10)); break; case OT_SANDWICHCHEESE: setstamina(lf, getmaxstamina(lf)); gainhp(lf,rnd(1,5)); break; case OT_SANDWICHPB: setstamina(lf, getmaxstamina(lf)); addtempflag(lf->flags, F_ATTRMOD, A_CON, 3, NA, NULL, rnd(30,50)); gainhp(lf,rnd(13,18)); break; default: break; } // remove object removeob(o, 1); } else { // mark how much we ate f = hasflag(o->flags, F_EDIBLE); if (f) { f->val[2] = (int)(startpcteaten + pcteaten); } } if (isplayer(lf)) { drawstatus(); wrefresh(statwin); } return B_FALSE; } // end of turn effects void endlfturn(lifeform_t *lf) { // lf will complain if in pain if (islowhp(lf) && onein(3) && !hasflag(lf->flags, F_ASLEEP)) { // TODO: replace 4 if (ispetof(lf, player) && !isundead(lf)) { if (!canhear(player, lf->cell, 4, NULL)) { char realname[BUFLEN]; real_getlfname(lf, realname, NULL, B_NOSHOWALL, B_REALRACE); warn("You feel worried about %s%s.", lfhasflag(lf, F_NAME) ? "" : "your ", noprefix(realname)); } else if (cantalk(lf)) { sayphrase(lf, SP_ALLY_INPAIN, SV_SHOUT, NA, NULL, player); } else { makenoise(lf, N_LOWHP); } } if (ispetof(lf, player)) { more(); } } } void enhancerandomskill(lifeform_t *lf) { flag_t *f; enum SKILL poss[MAXSKILLS]; int nposs = 0; int sel,b; enum SKILLLEVEL wantlev; // enhance lower level skills first, and only up to PR_ADEPT. for (wantlev = PR_NOVICE; wantlev <= PR_BEGINNER; wantlev++) { foreach_bucket(b) { for (f = lf->flags->first[b] ; f ; f = f->next) { if ((f->id == F_HASSKILL) && !ismaxedskill(lf, f->val[0]) && (f->val[1] == wantlev)) { if (isplayer(lf) && (f->val[2] != B_TRUE)) { // for player - select only from skills which we have used since last levelup. } else { poss[nposs] = f->val[0]; nposs++; } } } if (nposs > 0) { sel = rnd(0,nposs-1); giveskill(lf, poss[sel]); break; } } } } void enhanceskills(lifeform_t *lf) { enum SKILL whichsk; flag_t *f; skill_t *sk; char ch = 'a'; int newskillcost = 1; float hpratio,mpratio; enum SKILLLEVEL slev; int gainedxplev = B_FALSE; flag_t *retflag[MAXCANDIDATES]; int nretflags,b; if (lf->newlevel != lf->level) { lf->level = lf->newlevel; gainedxplev = B_TRUE; } if (gainedxplev) { // special cases which happen before doing hp/mp if (hasjob(lf, J_MONK) && (lf->level == 2) && !lfhasflag(lf, F_NOSPELLS)) { if (!lfhasflag(lf, F_MPDICE)) { addflag(lf->flags, F_MPDICE, 1, 0, NA, NULL); } } // update hp hpratio = ((float)lf->hp / (float)lf->maxhp); lf->maxhp += rollhitdice(lf, isplayer(lf) ? B_FALSE : B_TRUE); lf->maxhp += (getskill(lf, SK_FIRSTAID)*2); lf->hp = hpratio * (float)lf->maxhp; // update mp if (lfhasflag(lf, F_MPDICE)) { if (lf->maxmp == 0) { mpratio = 1; } else { mpratio = ((float)lf->mp / (float)lf->maxmp); } lf->maxmp += rollmpdice(lf, B_FALSE); lf->mp = mpratio * (float)lf->maxmp; } if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); wrefresh(statwin); msg("^GWelcome to level %d!",lf->level); } // enhance a random skill every 2 levels (ie. 3/5/7/etc) if (((lf->level + 1) % 2) == 0) { enhancerandomskill(lf); } } // increase str/int etc if we can f = lfhasflag(lf, F_STATGAINREADY); while (f && (f->val[2] > 0)) { enum ATTRIB att = A_NONE; enum ATTRIB a,poss[MAXATTS]; int nposs = 0; for (a = 0; a < MAXATTS; a++) { if (!ismaxedattr(lf, a) && attrincreasable(a)) { poss[nposs++] = a; } } if (nposs == 1) { att = poss[0]; } else if (nposs) { if (isplayer(lf)) { char ch,ques[BUFLEN],answers[BUFLEN]; int i; more(); strcpy(answers, ""); for (i = 0; i < nposs; i++) { char this[2]; snprintf(this, 2, "%c", getattrletter(poss[i])); if (isalpha(this[0])) { if (i == 0) { snprintf(ques, BUFLEN, "Increase your %s", getattrname(poss[i])); } else if (i == (nposs-1)) { strcat(ques, " or "); strcat(ques, getattrname(poss[i])); strcat(ques, "?"); } else { strcat(ques, ", "); strcat(ques, getattrname(poss[i])); } strcat(answers, this); } } ch = askchar(ques, answers,NULL, B_TRUE, B_FALSE); switch (ch) { case 's': att = A_STR; break; case 'a': att = A_AGI; break; case 'f': att = A_CON; break; case 'i': att = A_IQ; break; case 'w': att = A_WIS; break; } } else { // pick randomly att = poss[rnd(0,nposs-1)]; } } else { // none possible if (isplayer(lf)) { more(); msg("All your attributes are already maxed."); } } if (att != A_NONE) { addflag(lf->flags, F_STATGAINED, lf->level, att, NA, NULL); modattr(lf, att, STATAMTPERLEVEL); } f->val[2]--; if (f->val[2] <= 0) { killflag(f); } if (isplayer(lf)) { // update status bar with new stats drawstatus(); wrefresh(statwin); // wait for player to acknowledge 'you feel stronger' etc drawmsg(); // TODO: this use to be more() } f = lfhasflag(lf, F_STATGAINREADY); } // now ask about learning/enhancing skills if (isplayer(lf)) { char eorl = 'e'; int skillstoenhance = 0; int skillstolearn = 0; int magictolearn = 0; int done = B_FALSE; obpile_t *spells; objecttype_t *ot; skillstoenhance = 0; foreach_bucket(b) { for (f = lf->flags->first[b] ; f ; f = f->next) { if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) { if (lf->skillpoints >= getskilllevcost(f->val[1] + 1)) { skillstoenhance++; } } } } if (lf->skillpoints >= newskillcost) { skillstolearn = 0; for (sk = firstskill ; sk ; sk = sk->next) { if (!getskill(player, sk->id) && canlearn(player, sk->id)) { skillstolearn++; } } } // construct list of spells you can study // // can study a spell if: // - it's not a nature spell. // - player can't already cast it // - player is skilled in at least one of the spell's schools. // - player is a high enough level to cast the spell. // - player has sufficient training points magictolearn = 0; spells = addobpile(NOOWNER, NOLOC, NULL); for (ot = objecttype ; ot ; ot = ot->next) { if (ot->obclass->id != OC_SPELL) continue; if (!canstudyspell(lf, ot->id)) continue; // spell is learnable - add to the list addobfast(spells, ot->id); magictolearn++; } while (!done && lf->skillpoints && (skillstolearn || skillstoenhance || magictolearn)) { char q[BUFLEN],buf2[BUFLEN],validchars[BUFLEN]; char defchar[BUFLEN]; strcpy(q, ""); strcpy(validchars, "n"); strcpy(defchar, "n"); if (skillstolearn) { strcat(q, "(E)nhance"); strcat(validchars, "e"); strcpy(defchar, "e"); } if (skillstoenhance) { if (strlen(q)) { strcat(q, "/"); } strcat(q, "(L)earn"); strcat(validchars, "l"); } strcat(q, " skills"); if (magictolearn && !lfhasflag(lf, F_NOSPELLS)) { strcat(q, ", study (M)agic"); strcat(validchars, "m"); } strcat(q, " or (N)one"); snprintf(buf2, BUFLEN, " [%d trn left]",lf->skillpoints); // ooo add color? strcat(q, buf2); eorl = askchar(q,validchars,defchar, B_TRUE, B_FALSE); if (eorl == 'e') { // enhance an existing skill // any skills to get? if (skillstoenhance) { char ques[BUFLEN],ques2[BUFLEN]; int done = B_FALSE,i; snprintf(ques, BUFLEN, "Enhance which skill (%d points left)?", lf->skillpoints); snprintf(ques2, BUFLEN, "Describe which skill?"); initprompt(&prompt, ques); addpromptq(&prompt, ques2); ch = 'a'; getflags(lf->flags, retflag, &nretflags, F_HASSKILL, F_NONE); for (i = 0;i < nretflags; i++) { f = retflag[i]; if (!ismaxedskill(lf, f->val[0])) { int cost; cost = getskilllevcost(f->val[1] + 1); if (lf->skillpoints >= cost) { char buf[BUFLEN]; char buf2[HUGEBUFLEN]; snprintf(buf, BUFLEN, "%s -> %s (cost:%d points)", getskillname(f->val[0]), getskilllevelname(f->val[1] + 1), cost); makedesc_skill(f->val[0], buf2, f->val[1]+1); addchoice(&prompt, ch++, getskillname(f->val[0]), buf, f, buf2); } } } addchoice(&prompt, '-', "None", "None", NULL, NULL); while (!done) { getchoicestr(&prompt, B_FALSE, B_TRUE); f = (flag_t *)prompt.result; if (f) { whichsk = f->val[0]; if (prompt.whichq == 0) { lf->skillpoints -= getskilllevcost(f->val[1]+1); giveskill(lf, whichsk); done = B_TRUE; } else { // ie. describing a skill describeskill(whichsk, f->val[1]+1); } } else { done = B_TRUE; } } } else { msg("You have already mastered all your current skills."); } } else if (eorl == 'l') { // learn a new skill // enough points? if (player->skillpoints < newskillcost) { msg("You need at least %d skill points to learn a new skill.", newskillcost); } else { if (skillstolearn) { int done = B_FALSE; char ques[BUFLEN],ques2[BUFLEN]; snprintf(ques, BUFLEN, "Learn which new skill (%d points left)?", player->skillpoints); snprintf(ques2, BUFLEN, "Describe which skill?"); initprompt(&prompt, ques); addpromptq(&prompt, ques2); ch = 'a'; for (sk = firstskill ; sk ; sk = sk->next) { if (!getskill(player, sk->id) && canlearn(player, sk->id)) { char buf[BUFLEN]; char buf2[HUGEBUFLEN]; snprintf(buf, BUFLEN, "%-18s(%s)", getskillname(sk->id), getskilldesc(sk->id)); makedesc_skill(sk->id, buf2, PR_NOVICE); addchoice(&prompt, ch++, getskillname(sk->id), buf, sk, buf2); } } addchoice(&prompt, '-', "None", "None", NULL, NULL); while (!done) { getchoicestr(&prompt, B_FALSE, B_TRUE); sk = (skill_t *)prompt.result; if (sk) { if (prompt.whichq == 0) { giveskill(player, sk->id); player->skillpoints -= newskillcost; done = B_TRUE; } else { describeskill(sk->id, PR_NOVICE); } } else { done = B_TRUE; } } } else { msg("There is nothing more that you can learn."); } } } else if (eorl == 'm') { object_t *o = NULL; int schoolok[SS_LAST],i; // select a magic spell // get a list of possible schools for (i = 0; i < SS_LAST; i++) { schoolok[i] = B_FALSE; } for (o = spells->first ; o ; o = o->next) { enum SPELLSCHOOL school; school = getspellschoolknown(lf, o->type->id); schoolok[school] = B_TRUE; } // ask which school initprompt(&prompt, "Learn a spell from which school?"); ch = 'a'; for (i = 0; i < SS_LAST; i++) { if (schoolok[i]) { addchoice(&prompt, i, getschoolname(i), NULL, NULL, NULL); } } addchoice(&prompt, '\0', "(none)", NULL, NULL, NULL); ch = getchoicestr(&prompt, B_FALSE, B_TRUE); if (ch != '\0') { char ques[BUFLEN]; snprintf(ques, BUFLEN, "Learn which spell (%d point%s left)?", lf->skillpoints, (lf->skillpoints == 1) ? "" : "s"); initprompt(&prompt, ques); // ask which spell from that school for (o = spells->first ; o ; o = o->next) { enum SPELLSCHOOL school; school = getspellschoolknown(lf, o->type->id); if (school == ch) { char buf[BUFLEN],descbuf[HUGEBUFLEN]; snprintf(buf, BUFLEN, "%s (%s) [%d points]", o->type->name, getschoolname(school), getspelllevel(o->type->id) ); makedesc_spell(o->type, descbuf); addchoice(&prompt, o->type->id, o->type->name, buf, o, descbuf); } } addchoice(&prompt, '-', "(none)", NULL, NULL, NULL); getchoicestr(&prompt, B_FALSE, B_TRUE); o = prompt.result; if (o) { if (prompt.whichq == 0) { learnspell(lf, o->type->id, B_TRUE); player->skillpoints -= getspelllevel(o->type->id); done = B_TRUE; /* char bookname[BUFLEN]; enum SPELLSCHOOL school; school = getspellschoolknown(lf, o->type->id); object_t *book; // add it to a spellbook. snprintf(ques, BUFLEN, "Which spellbook will you record '%s' in?", o->type->name); initprompt(&prompt, ques); for (book = lf->pack->first ; book ; book = book->next) { int ok = B_FALSE; if (book->type->id == OT_SPELLBOOK) { if (hasflagval(book->flags, F_LINKSCHOOL, school, NA, NA, NULL)) { ok = B_TRUE; } } else if (book->type->id == OT_GRIMOIRE) { ok = B_TRUE; } if (ok) { addchoice(&prompt, book->letter, book->type->name, NULL, book, NULL); } } // prompt for which spellbook. if (prompt.nchoices == 1) { book = prompt.choice[0].data; } else { getchoice(&prompt); book = (object_t *)prompt.result; } assert(book != NULL); addobfast(book->contents, o->type->id); getobname(book, bookname, 1); msg("'%s' added to %s.", o->type->name, bookname); */ } else { describespell(o->type); } } } } else if (eorl == 'n') { done = B_TRUE; } // end enhance/learnnew } // whiel skillstolearn || skillstoenhance // free list of possible spells killobpile(spells); statdirty = B_TRUE; } else if (lf->skillpoints) { // monsters will just enhance a random skill, they never learn new ones. enhancerandomskill(lf); // only costs 1 point for monsters to get any skill lf->skillpoints--; } // end if isplayer if (gainedxplev) { flag_t *ff; // give job-based level rewards f = levelabilityready(lf); while (f) { if (f->id == F_LEVABIL) { flag_t *abilflag[MAXCANDIDATES]; int nabilflags = 0; int origborn,i; origborn = lf->born; // already had this power with different options? remove it. getflags(lf->flags, abilflag, &nabilflags, F_CANWILL, F_NONE); for (i = 0;i < nabilflags; i++) { if ((abilflag[i]->val[0] == f->val[1]) && (abilflag[i]->lifetime == FROMJOB)) { lf->born = B_FALSE; // stop flag loss from being announced killflag(abilflag[i]); lf->born = origborn; } } // now add the new one ff = addtempflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text, FROMJOB); ff->fromlev = lf->level; } else if (f->id == F_LEVFLAG) { ff = addtempflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text, FROMJOB); ff->fromlev = lf->level; } else if (f->id == F_LEVSKILL) { ff = giveskill(lf, f->val[1]); ff->fromlev = lf->level; } else if ((f->id == F_LEVSPELL) && !lfhasflag(lf, F_NOSPELLS)) { learnspell(lf, f->val[1], B_TRUE); } else if ((f->id == F_LEVSPELLSCHOOL) && !lfhasflag(lf, F_NOSPELLS)) { // select a spell from school if (isplayer(lf)) { select_new_spell(f->val[1], lf->level); } else { // monster gets random spell makespellchoicelist(&prompt, lf, "xx","xx:", f->val[1], B_TRUE, B_FALSE, B_FALSE, lf->maxmp); if (prompt.nchoices > 0) { objecttype_t *ot; // pick one randomly ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; if (ot) { learnspell(lf, ot->id, B_TRUE); } } } } else if ((f->id == F_LEVSPELLSCHOOLFROMX) && !lfhasflag(lf, F_NOSPELLS)) { // select from X spells from given school int nleft,highestlev = -1,n,i; enum SPELLSCHOOL wantschool; int possidx[MAXCANDIDATES],nposs; char qbuf[BUFLEN]; objecttype_t *ot; wantschool = f->val[1]; nleft = f->val[2]; // adjust by intelligence nleft += getextraspellchoices(lf); limit(&nleft, 1, NA); // get all possible spells to learn, from the given school snprintf(qbuf, BUFLEN, "Learn which new spell (maxmp=%d):", getmaxmp(player)); makespellchoicelist(&prompt, player, qbuf, "Describe which spell:", f->val[1], B_TRUE, B_FALSE, B_FALSE, player->maxmp); // find highest possible level, and mark all choices as invalid. for (i = 0; i < prompt.nchoices; i++) { int thislev; if (prompt.choice[i].heading) continue; ot = (objecttype_t *)(prompt.choice[i].data); thislev = getspelllevel(ot->id); if (thislev > highestlev) highestlev = thislev; prompt.choice[i].valid = B_FALSE; } // select 1 from the highest possible level, and mark it ok nposs = 0; for (i = 0; i < prompt.nchoices; i++) { int thislev; if (prompt.choice[i].heading) continue; ot = (objecttype_t *)(prompt.choice[i].data); thislev = getspelllevel(ot->id); if (thislev == highestlev) { possidx[nposs++] = i; } } i = possidx[rnd(0,nposs-1)]; prompt.choice[i].valid = B_TRUE; nleft--; // TODO: select nleft-1 random other ones. for (n = 0; n < nleft; n++) { nposs = 0; for (i = 0; i < prompt.nchoices; i++) { if (!prompt.choice[i].valid && !prompt.choice[i].heading) { possidx[nposs++] = i; } } if (!nposs) { break; } i = possidx[rnd(0,nposs-1)]; prompt.choice[i].valid = B_TRUE; } // now let player select one, or pick randomly if a mosnter if (isplayer(lf)) { int done = B_FALSE; while (!done) { if (prompt.nchoices > 0) { objecttype_t *ot; getchoicestr(&prompt, B_TRUE, B_TRUE); ot = prompt.result; if (ot) { if (prompt.whichq == 0) { // learn the spell learnspell(lf, ot->id, B_TRUE); done = B_TRUE; } else { describespell(ot); } } } else { msg("There are no new spells for you to learn at this time."); done = B_TRUE; } } } else { if (prompt.nchoices > 0) { // pick randomly ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; if (ot) { learnspell(lf, ot->id, B_TRUE); } } } } f->lifetime = LEVABILITYDONE; // mark as done. // get next one f = levelabilityready(lf); } // now refresh them all for next level. refreshlevelabilities(lf); // special case level-based job effects if (hasjob(lf, J_MONK)) { // enhance fist strength and change type adjustmonk(lf, B_FALSE); } // allomancy sometimes lets you learn spells if (!lfhasflag(lf, F_NOSPELLS)) { slev = getskill(lf, SK_SS_ALLOMANCY); if (pctchance(slev*20)) { char qbuf[BUFLEN]; snprintf(qbuf, BUFLEN, "Learn which allomantic ability (maxmp=%d):", getmaxmp(player)); // construct list of castable mental spells makespellchoicelist(&prompt, lf, qbuf, "Describe which allomantic ability:", SS_ALLOMANCY, B_TRUE, B_FALSE, B_FALSE, player->maxmp); if (prompt.nchoices > 0) { objecttype_t *ot; msg("Your body has attuned itself to a new allomantic ability!"); more(); getchoicestr(&prompt, B_TRUE, B_TRUE); ot = prompt.result; if (ot) { if (prompt.whichq == 0) { // learn the spell ff = addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); ff->fromlev = lf->level; } else { describespell(ot); } } } } // psionics sometimes lets you learn spells slev = getskill(lf, SK_SS_MENTAL); if (pctchance(slev*20)) { char qbuf[BUFLEN]; int pickfrom; snprintf(qbuf, BUFLEN, "Learn which psionic power (maxmp=%d):", getmaxmp(player)); // construct list of castable mental spells makespellchoicelist(&prompt, lf, qbuf, "Describe which psionic power:", SS_MENTAL, B_TRUE, B_FALSE, B_FALSE, player->maxmp); // randomly remove choices down to (IQ/10) pickfrom = getattr(lf, A_IQ)/10; if (pickfrom > 0) { while (prompt.nchoices > pickfrom) { int sel; sel = rnd(0,prompt.nchoices-1); killchoice(&prompt, sel); } if (prompt.nchoices > 0) { objecttype_t *ot; msg("Your brain has unlocked a new psionic power!"); more(); getchoicestr(&prompt, B_TRUE, B_TRUE); ot = prompt.result; if (ot) { if (prompt.whichq == 0) { // learn the spell ff = addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); ff->fromlev = lf->level; } else { describespell(ot); } } } } } } // end if !hasflag nospells killflagsofid(lf->flags, F_HASNEWLEVEL); // ready for another level? if (lf->xp >= getxpforlev(lf->level + 1)) { gainlevel(lf, B_FALSE); // this will increment 'newlevel' } killwarningtext(TEXT_WARN_ATTACK_NOXP); } // end if gainedxplev } // returns B_TRUE if we were already enraged. int enrage(lifeform_t *lf, int howlong) { flag_t *retflag[MAXCANDIDATES],*f; int nretflags = 0,i; int alreadyraging = B_FALSE; // already enraged? getflags(lf->flags, retflag, &nretflags, F_RAGE, F_AICONTROLLED, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->lifetime > 0) { if (f->id == F_RAGE) { f->lifetime = howlong; alreadyraging = B_TRUE; } else if ((f->id == F_AICONTROLLED) && (f->val[2] == F_RAGE)) { if (isplayer(lf)) { f->lifetime = howlong; alreadyraging = B_TRUE; } } } } if (alreadyraging) return B_TRUE; addtempflag(lf->flags, F_RAGE, NA, NA, NA, NULL, howlong); if (isplayer(lf)) { addtempflag(lf->flags, F_AICONTROLLED, B_TRUE, NA, F_RAGE, NULL, howlong); } else { loseaitargets(lf); } return B_FALSE; } int exchangeweapon(lifeform_t *lf) { object_t *wep,*sec = NULL,*newsec = NULL; // get secondary (if available) sec = hasobwithflag(lf->pack, F_SECONDARY); // get current weapon wep = getweapon(lf); // try to unweild current weapon if (wep) { if (unweild(lf, wep)) { // error return B_TRUE; } else { // success addflag(wep->flags, F_SECONDARY, B_TRUE, NA, NA, NULL); newsec = wep; } } if (sec) { // try to weild secondary if (weild(lf, sec)) { // error return B_TRUE; } } // success! if (newsec) { char obname[BUFLEN]; getobname(newsec, obname, 1); msg("Spare weapon: %c - %s", newsec->letter, obname); } else { msg("Spare weapon: (none)"); } return B_FALSE; } void extinguishlf(lifeform_t *lf) { object_t *o,*nexto; for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; extinguish(o); } killflagsofid(lf->flags, F_ONFIRE); } // if the lf wearing something which shades their eyes? object_t *eyesshaded(lifeform_t *lf) { object_t *glasses; glasses = getarmour(lf, BP_EYES); if (glasses && hasflag(glasses->flags, F_TINTED)) { return glasses; } return NULL; } object_t *facecovered(lifeform_t *lf) { object_t *mask; mask = getarmour(lf, BP_HEAD); if (mask && hasflag(mask->flags, F_COVERSFACE)) { return mask; } return NULL; } int fall(lifeform_t *lf, lifeform_t *fromlf, int announce) { char lfname[BUFLEN]; if (isdead(lf)) return B_TRUE; if (isprone(lf)) return B_TRUE; if (!isairborne(lf, NULL)) { if ((lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET))) { return B_TRUE; } } if (lfhasflag(lf, F_GRAVLESSENED)) return B_TRUE; getlfname(lf,lfname); if (announce) { if (isplayer(lf) || cansee(player, lf)) { if (fromlf) { char fromlfname[BUFLEN]; getlfname(fromlf, fromlfname); msg("^w%s knock%s %s to the ground!",fromlfname, isplayer(fromlf) ? "" : "s", lfname); } else { msg("^w%s fall%s to the ground.",lfname, isplayer(lf) ? "" : "s"); } } } taketime(lf, SP_NORMAL); addflag(lf->flags, F_PRONE, B_TRUE, NA, NA, NULL); loseconcentration(lf); interrupt(lf); breakgrabs(lf, B_TRUE, B_TRUE, B_TRUE); if (isvulnto(lf->flags, DT_FALL, B_FALSE)) { // 0 will be repplaced with the dtvuln flag losehp(lf, 0, DT_FALL, fromlf, "a bad fall"); } return B_FALSE; } // if you are going to sleep on purpose, use 'gotosleep'. // // this function is for when sleep/unconsciousness is forced upon you by a spell, etc. int fallasleep(lifeform_t *lf, enum SLEEPTYPE how, int howlong) { flag_t *f; f = lfhasflag(lf, F_ASLEEP); if (f) { if (f->val[1] == how) { return B_TRUE; } else { killflag(f); } } if (how == ST_ASLEEP) { if (lfhasflag(lf, F_CAFFEINATED) || isundead(lf)) { if (isplayer(lf)) { msg("You feel momentarily tired."); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s looks momentarily tired.", lfname); } return B_TRUE; } } loseconcentration(lf); interrupt(lf); breakgrabs(lf, B_TRUE, B_FALSE, B_TRUE); // falling asleep while flying = fall! fall_from_air(lf); killflagsofid(lf->flags, F_RAGE); killflagsofid(lf->flags, F_TRAINING); addtempflag(lf->flags, F_ASLEEP, B_TRUE, how, NA, NULL, howlong); return B_FALSE; } // returns true if you fell int fall_from_air(lifeform_t *lf) { return real_fall_from_air(lf, SZ_MAX); } // returns true if you fell int real_fall_from_air(lifeform_t *lf, int leqsize) { int willfall = B_FALSE,willinjure = B_FALSE, n; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; if (getlfsize(lf) > leqsize) return B_FALSE; getflags(lf->flags, retflag, &nretflags, F_FLYING, F_LEVITATING, F_NONE); for (n = 0; n < nretflags; n++) { if (istransitoryflag(retflag[n]) || (retflag[n]->lifetime == FROMRACE)) { if (retflag[n]->id == F_FLYING) willinjure = B_TRUE; willfall = B_TRUE; killflag(retflag[n]); } } if (willfall) fall(lf, NULL, B_TRUE); if (willinjure) injure(lf, getrandomcorebp(lf, NULL), DT_BASH, IJ_NONE); if (willfall || willinjure) { return B_TRUE; } return B_FALSE; } // make 'lf' respond to damage void fightback(lifeform_t *lf, lifeform_t *attacker) { if (isdead(lf)) return; if (lfhasflag(lf, F_FEIGNINGDEATH) || lfhasflagval(lf, F_ASLEEP, NA, ST_KO, NA, NULL)) { // don't respond. return; } interrupt(lf); // special cases if ((lf->race->id == R_STIRGE) || (lf->race->id == R_LEECH)) { if (ispeaceful(lf)) { // rest our sated counter killflagsofid(lf->flags, F_COUNTER); } } if (attacker) { flag_t *f; f = lfhasflag(lf, F_ASLEEP); if (f && (f->val[1] != ST_KO)) { // wake up killflagsofid(lf->flags, F_ASLEEP); } f = lfhasflagval(lf, F_FEIGNFOOLEDBY, attacker->id, NA, NA, NULL); if (f) { killflag(f); } // monsters might flee, fight back, etc if (!isplayer(lf)) { if (isplayer(attacker) && ishirable(lf)) { // can never recruit this person now! addflag(lf->flags, F_NOHIRE, B_TRUE, NA, NA, NULL); } // induction of fear? if (lfhasflag(attacker, F_INDUCEFEAR) && cansee(lf, attacker)) { scare(lf, attacker, rnd(2,3), 0); } else if (mightflee(lf)) { scare(lf, attacker, PERMENANT, 0); } else { lifeform_t *l; // special case for player's pets/allies... if (areallies(lf, attacker) && (getallegiance(lf) == AL_FRIENDLY)) { // only fight back if it was the PLAYER who attacked us if (!isplayer(attacker)) return; } // turn to face our attacker if (!lfhasflag(lf, F_STUNNED) && !isdead(lf)) { if (!isfleeing(lf)) { //if (isadjacent(lf->cell, attacker->cell)) { turntoface(lf, attacker->cell); //} aiattack(lf, attacker, aigetchasetime(lf)); } } // any nearby monsters which will help out? if (getallegiance(lf) != AL_FRIENDLY) { for (l = lf->cell->map->lf ; l ; l = l->next) { if (!isdead(l) && areallies(l,lf)) { if (cansee(l, attacker)) { aiattack(l, attacker, aigetchasetime(l)); } } } } // special cases (which will only happen if not retreating) } // special cases (which will happen whether retreating or not) if (lf->race->id == R_DARKMANTLE) { cell_t *poss[MAXCANDIDATES]; int i, nposs = 0; // darkjump for (i = 0; i < lf->nlos; i++) { if (lf->los[i] != lf->cell) { if (cellwalkable(lf, lf->los[i], NULL) && isdark(lf->los[i])) { poss[nposs] = lf->los[i]; nposs++; } } } if (nposs) { //teleportto(lf, poss[rnd(0,nposs-1)], B_FALSE); // no smoke abilityeffects(lf, OT_A_DARKWALK, poss[rnd(0,nposs-1)], NULL, NULL); } } } } } behaviour_t *findbehaviour(enum BEHAVIOUR bid) { behaviour_t *b; for (b = firstbehaviour ; b ; b = b->next) { if (b->id == bid) return b; } return NULL; } job_t *findjob(enum JOB jobid) { job_t *j; for (j = firstjob ; j ; j = j->next) { if (j->id == jobid) return j; } return NULL; } job_t *findjobbyname(char *name) { job_t *j; for (j = firstjob ; j ; j = j->next) { if (!strcasecmp(j->name, name)) return j; } return NULL; } lifeform_t *findlf(map_t *m, int lfid) { lifeform_t *lf; map_t *thismap; if (m) { for (lf = m->lf ; lf ; lf = lf->next) { if (lf->id == lfid) return lf; } } else { for (thismap = firstmap ; thismap ; thismap = thismap->next) { for (lf = thismap->lf ; lf ; lf = lf->next) { if (lf->id == lfid) return lf; } } } return NULL; } lifeform_t *findlfunique(enum RACE rid) { lifeform_t *lf; map_t *thismap; for (thismap = firstmap ; thismap ; thismap = thismap->next) { for (lf = thismap->lf ; lf ; lf = lf->next) { if (lf->race->id == rid) return lf; } } return NULL; } // if maxdist is NA, use the distance from the flag // if maxdist is UNLIMITED, search the entire map cell_t *findnearbylifeob(cell_t *src, int maxdist, flag_t *lifeobflag, object_t **retlifeob) { object_t *corpse = NULL; if (maxdist == NA) { maxdist = lifeobflag->val[1]; } else if (maxdist == UNLIMITED) { maxdist = MAXOF(src->map->w, src->map->h); } if (retlifeob) { *retlifeob = NULL; } if (strlen(lifeobflag->text)) { long corpseid; // find the corpse corpseid = atol(lifeobflag->text); corpse = findobidinmap(src->map, corpseid); if (getcelldist(src, corpse->pile->where) > maxdist) { corpse = NULL; } } else { // find closest location with corpse... cell_t *retcell[MAX_MAPW*MAX_MAPH]; int nretcells,i; enum OBTYPE oid; int mindist = 9999; oid = lifeobflag->val[0]; getradiuscells(src, maxdist, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_TRUE, retcell, &nretcells, 0); for (i = 0; i < nretcells; i++) { object_t *o; cell_t *c; int thisdist; c = retcell[i]; o = hasob(c->obpile, oid); if (o) { thisdist = getcelldist(src, c); if (thisdist < mindist) { mindist = thisdist; corpse = o; } } } } if (corpse) { if (retlifeob) { *retlifeob = corpse; } return corpse->pile->where; } return NULL; } poisontype_t *findpoisontype(enum POISONTYPE id) { poisontype_t *pt; for (pt = firstpoisontype; pt ; pt = pt->next) { if (pt->id == id) { return pt; } } return NULL; } race_t *findrace(enum RACE id) { race_t *r; for (r = firstrace; r ; r = r->next) { if (r->id == id) { return r; } } return NULL; } race_t *findracebyname(char *name, condset_t *cs) { race_t *r; raceclass_t *rc; char searchfor[BUFLEN]; // first check for exact matches for (r = firstrace; r ; r = r->next) { if (!strcmp(r->name, name)) { if (racemeets(r->id, cs)) { return r; } } } // now check raceclasses for (rc = firstraceclass; rc ; rc = rc->next) { // using strstarts rather than streq in case there is a job suffix if (strstarts(name, rc->name)) { // return a random race from this class return getreallyrandomrace(rc->id, cs); } } // ...then partial matches start of words in name // ie. "ant" should match "soldier ant" before matching "giant" snprintf(searchfor, BUFLEN, " %s",name); for (r = firstrace; r ; r = r->next) { if (strstr(r->name, searchfor) && racemeets(r->id, cs)) { return r; } } // ...then partial matches in names for (r = firstrace; r ; r = r->next) { if (strstr(r->name, name) && racemeets(r->id, cs)) { return r; } } return NULL; } raceclass_t *findraceclass(enum RACECLASS id) { raceclass_t *r; for (r = firstraceclass; r ; r = r->next) { if (r->id == id) { return r; } } return NULL; } /* lifeform_t *findshopkeeper(map_t *m, int roomid) { lifeform_t *lf; for (lf = m->lf ; lf ; lf = lf->next) { if (lfhasflagval(lf, F_OWNSSHOP, roomid, NA, NA, NULL)) { return lf; } } return NULL; } */ skill_t *findskill(enum SKILL id) { skill_t *r; for (r = firstskill; r ; r = r->next) { if (r->id == id) { return r; } } return NULL; } skill_t *findskillbyname(char *name) { skill_t *s; for (s = firstskill ; s ; s = s->next) { if (!strcasecmp(s->name, name)) return s; } for (s = firstskill ; s ; s = s->next) { if (!strcasestr(s->name, name)) return s; } return NULL; } enum SKILLLEVEL findskilllevbyname(char *name) { enum SKILLLEVEL slev; for (slev = PR_INEPT; slev <= PR_MASTER; slev++) { if (!strcasecmp(getskilllevelname(slev), name)) { return slev; } } return PR_INEPT; } /* subjob_t *findsubjob(enum SUBJOB sjid) { subjob_t *j; for (j = firstsubjob ; j ; j = j->next) { if (j->id == sjid) return j; } return NULL; } subjob_t *findsubjobbyletter(char letter) { subjob_t *j; for (j = firstsubjob ; j ; j = j->next) { if (j->letter == letter) return j; } return NULL; } */ // returns true if we did somethign int fixcurses(lifeform_t *lf) { int donesomething = B_FALSE,b; flag_t *f,*nextf; foreach_bucket(b) { for (f = lf->flags->first[b] ; f ; f = nextf) { nextf = f->next; if (f->id == F_LYCANTHROPE) { donesomething = B_TRUE; killflag(f); continue; } else if ((f->id == F_INCUBATING) || (f->id == F_POISONED)) { poisontype_t *pt; pt = findpoisontype(f->val[0]); if (pt->severity == PS_CURSE) { donesomething = B_TRUE; killflag(f); continue; } } else if (f->lifetime == FROMLYCANTHROPY) { donesomething = B_TRUE; killflag(f); continue; } } } if (donesomething) { if (isplayer(lf)) { msg("^%cYour curses are lifted!", getlfcol(lf, CC_VGOOD)); } } return donesomething; } // try to actually do the 'run away' action for // anyone we are fleeing from. // returns TRUE if we ran away from something int flee(lifeform_t *lf) { flag_t *f; lifeform_t *fleefrom = NULL; int i; int db = B_FALSE; char lfname[BUFLEN]; flag_t *retflag[MAXCANDIDATES]; int nretflags; if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; real_getlfname(lf, lfname, NULL, B_NOSHOWALL, B_CURRACE); if (isdead(lf)) return B_FALSE; // are we fleeing? getflags(lf->flags, retflag, &nretflags, F_FLEEFROM, F_NONE); // mindless? if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) == IQ_MINDLESS) { if (!nretflags) return B_FALSE; } // now determine who to flee from. for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_FLEEFROM) { lifeform_t *thisone; thisone = findlf(lf->cell->map, f->val[0]); if (thisone) { // TODO: this really means that the monster is cheating if (haslof(lf->cell, thisone->cell, LOF_WALLSTOP, NULL)) { // if not fleeing from anyone, or this one is closer... if (!fleefrom || getcelldist(lf->cell, thisone->cell) < getcelldist(lf->cell, fleefrom->cell)) { fleefrom = thisone; } } else { // if we don't have LOF to the person we're running from, and it's not enforced for // a certain time period (ie. f->lifetime == PERMENANT), we can now stop fleeing. if (f->lifetime == PERMENANT) { // player let something flee? if (isplayer(thisone) && !haslos(thisone, lf->cell)) { // purposely not using cansee if (!lfhasflag(lf, F_ALREADYFLED)) { pleasegodmaybe(R_GODMERCY, 5); addflag(lf->flags, F_ALREADYFLED, B_TRUE, NA, NA, NULL); //if ((lf->lastdamlf == player->id) || cansee(player, lf)) { if (lf->lastdamlf == player->id) { // ie. only if the player saw them run away, or has already // attacked them. if (!lfhasflag(lf, F_HECTAESCAPEE)) { // 5 turns until hecta gets angry addflag(lf->flags, F_HECTAESCAPEE, 5, 10, NA, NULL); } //angergodmaybe(R_GODDEATH, 10, GA_MERCY); } } } killflag(f); } else { // if the flag is temporary, keep fleeing and wait for it to time out normally fleefrom = thisone; } } } else { killflag(f); } } } // found someone who we are fleeing from? if (fleefrom) { object_t *stairs; if (db) dblog("%s - fleeing from %s", lfname, fleefrom->race->name); // lower our shield killflagsofid(lf->flags, F_FULLSHIELD); breakgrabs(lf, B_TRUE, B_FALSE, B_TRUE); // stop grabbing anyone // ways of fleeing other than movement? if (!isplayer(lf)) { enum OBTYPE spell; // if AI, try to use specific spells like teleport self spell = aigetfleespell(lf); if (spell != OT_NONE) { lifeform_t *targlf; cell_t *targcell; object_t *targob; objecttype_t *sp; sp = findot(spell); aigetspelltarget(lf, findot(spell), fleefrom, &targlf, &targcell, &targob, F_AICASTTOFLEE); if (getschool(spell) == SS_ABILITY) { if (db) dblog("%s - using ability %s to flee", sp->name); if (!useability(lf, spell, targlf, targcell)) { if (db) dblog("%s - success.", lfname); return B_TRUE; } } else { if (db) dblog("%s - casting %s to flee", sp->name); if (!castspell(lf, spell, targlf, targob, targcell, NULL, NULL)) { if (db) dblog("%s - success.", lfname); return B_TRUE; } } } // if AI, use helpful fleeing items if (!useitemwithflag(lf, F_AIFLEEITEM)) { if (db) dblog("%s - used an item to flee", lfname); return B_TRUE; } } // announce if (isplayer(lf)) { char buf[BUFLEN]; drawscreen(); getlfname(fleefrom, buf); msg("^wYou flee from %s!",buf); } // can we flee via stairs? stairs = hasobwithflag(lf->cell->obpile, F_CLIMBABLE); if (stairs && !lfhasflag(lf, F_NOSTAIRS)) { if (db) dblog("%s - trying to flee via %s", lfname, stairs->type->name); if (!usestairs(lf, stairs, B_TRUE, B_TRUE)) { // success if (isplayer(fleefrom)) { pleasegodmaybe(R_GODMERCY, 5); if ((lf->lastdamlf == player->id) || cansee(player, lf)) { // ie. only if the player saw them run away, or has already // attacked them. if (!lfhasflag(lf, F_HECTAESCAPEE)) { // 5 turns until hecta gets angry addflag(lf->flags, F_HECTAESCAPEE, 5, 10, NA, NULL); } //angergodmaybe(R_GODDEATH, 10, GA_MERCY); } } return B_TRUE; } if (db) dblog("%s - failed to flee via %s", lfname, stairs->type->name); } // move away from them if (!moveawayfrom(lf, fleefrom->cell, DT_ORTH, B_FALSE, B_FALSE, isplayer(lf) ? B_NOTONPURPOSE : B_ONPURPOSE)) { if (db) dblog("%s - fleeing by moving away", lfname); return B_TRUE; } if (db) dblog("%s - failed to flee!", lfname); } // if we get here, it means we didn't need to or couldn't flee return B_FALSE; } // start fleeing from 'enemy' void fleefrom(lifeform_t *lf, lifeform_t *enemy, int howlong, int onpurpose) { flag_t *f; if (lf == enemy) return; if (!onpurpose) { // in recovery from fleeing? // this is to prevent constant usage of war cry! f = hasflagval(lf->flags, F_NOFLEEFROM, enemy->id, NA, NA, NULL); if (f) { if (f->lifetime > 0) { f->lifetime -= 5; if (f->lifetime <= 0) { killflag(f); } } if (isplayer(lf)) { msg("You flinch."); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s flinches.",lfname); } return; } } // already fleeing? f = hasflagval(lf->flags, F_FLEEFROM, enemy->id, NA, NA, NULL); if (f) { // just update time if (f->lifetime != PERMENANT) { if (f->lifetime < howlong) { f->lifetime = howlong; } } } else { addtempflag(lf->flags, F_FLEEFROM, enemy->id, NA, NA, NULL, howlong); } // stop targetting anyone or going anywhere loseaitargets(lf); // if no morale left, become timid for twice the flee time if (!getmorale(lf)) { addtempflag(lf->flags, F_TIMID, NA, NA, NA, NULL, (howlong == PERMENANT) ? howlong : (howlong*2)); } } int freezelf(lifeform_t *freezee, lifeform_t *freezer, int howlong) { if (isimmuneto(freezee->flags, DT_COLD, B_FALSE)) { if (isplayer(freezee)) { msg("You feel a slight chill."); } return B_TRUE; } else if (isresistantto(freezee->flags, DT_COLD, B_FALSE)) { char buf[BUFLEN]; if (isplayer(freezee)) { msg("^bYou feel freezing cold!"); } if (freezer) { char lfname[BUFLEN]; getlfname(freezer, lfname); snprintf(buf, BUFLEN, "being frozen by %s",lfname); } else { strcpy(buf, "being frozen"); } // note: damage value here will be halved due to resistance losehp(freezee, rnd(howlong,howlong*2), DT_COLD, freezer, buf); return B_TRUE; } if (!lfhasflag(freezee, F_FROZEN)) { // turn to ice addtempflag(freezee->flags, F_FROZEN, B_TRUE, NA, NA, NULL, howlong); } return B_FALSE; } void gainhp(lifeform_t *lf, int amt) { int gained = B_FALSE; int maxed = B_FALSE; if (lf->hp < lf->maxhp) { lf->hp += amt; gained = B_TRUE; } if (lf->hp >= lf->maxhp) { lf->hp = lf->maxhp; if (gained) maxed = B_TRUE; } if (isplayer(lf)) { if (gained) { player->damlastturn = 0; statdirty = B_TRUE; } if (maxed) { msg("^gYou are now fully healed."); } // update screen drawstatus(); updatestatus(); } } void gainlevel(lifeform_t *lf, int autotrain) { flag_t *f; race_t *mybaserace; //int skillready = B_FALSE; //int preready,postready; if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); wrefresh(statwin); } //preready = readytotrain(lf); addflag(lf->flags, F_HASNEWLEVEL, B_TRUE, NA, NA, NULL); lf->newlevel++; // stat gain (str etc) every level /* if ((lf->newlevel % 3) == 0) { flag_t *f; f = lfhasflag(lf, F_STATGAINREADY); if (f) { // TODO: should never happen now. f->val[2]++; } else { f = addflag(lf->flags, F_STATGAINREADY, NA, NA, 1, NULL); } } */ f = lfhasflag(lf, F_STATGAINREADY); if (f) { // TODO: should never happen now. f->val[2]++; } else { f = addflag(lf->flags, F_STATGAINREADY, NA, NA, 1, NULL); } // auto skill gain for monsters if (!isplayer(lf)) { lf->skillpoints++; } if (isplayer(lf)) { if (!autotrain) { msg("^GYou are ready to train a new experience level!"); more(); } } else if (cansee(player, lf)) { //getlfname(lf, buf); //msg("%s looks more confident!",buf); } // you can now re-attempt identification of objects killflagsofid(lf->flags, F_FAILEDINSPECT); // monster races can be promoted... if (lf->race->baseid) { mybaserace = findrace(lf->race->baseid); } else { mybaserace = lf->race; } f = hasflagval(mybaserace->flags, F_LEVRACE, lf->newlevel, NA, NA, NULL); if (f && (lf->race->id != f->val[1])) { // promotion! setrace(lf, f->val[1], B_FALSE); } if (isplayer(lf)) { needredraw = B_TRUE; statdirty = B_TRUE; drawscreen(); } if (autotrain) { enhanceskills(lf); } } void gainmp(lifeform_t *lf, int amt) { int gained = B_FALSE; int maxed = B_FALSE; int max; max = getmaxmp(lf); // magic resistance means you can't regenerate mana! if (skillcheck(lf, SC_RESISTMAG, 100, 0)) { return; } if (lf->mp < max) { lf->mp += amt; gained = B_TRUE; } if (lf->mp >= max) { lf->mp = max; if (gained) maxed = B_TRUE; } if (isplayer(lf)) { if (maxed) { msg("^GYour mana is now fully restored."); } if (gained) { player->mplastturn = 0; statdirty = B_TRUE; drawstatus(); updatestatus(); } } } void gainxp(lifeform_t *lf, long amt) { int newskillpoints = 0; int doxp = B_TRUE; int boostamt = 0; assert(amt >= 0); // adjust for xp boosts... sumflags(lf->flags, F_LEARNBOOST, &boostamt, NULL, NULL); amt = pctof(100+boostamt, amt); if (lfhasflag(lf, F_HASNEWLEVEL) || (lf->level == 0)) { doxp = B_FALSE; } if (doxp) { lf->xp += amt; if (isplayer(lf)) statdirty = B_TRUE; assert(lf->xp >= 0); // skill xp if (isplayer(lf)) { long amtneeded; amtneeded = getspforpoint(lf); assert(amtneeded > 0); /* // would you gain more than 5 levels? probably a bug! if (((lf->skillxp + amt) / amtneeded) >= 3) { raise(SIGINT); } */ lf->skillxp += amt; assert(lf->skillxp >= 0); while (lf->skillxp >= amtneeded) { newskillpoints++; lf->skillpoints++; lf->totskillpoints++; lf->skillxp -= amtneeded; // recalculate amtneeded = getspforpoint(lf); if (isplayer(lf)) statdirty = B_TRUE; } } // ready for next level? can only go up ONE level. if (lf->xp >= getxpforlev(lf->level + 1)) { gainlevel(lf, B_FALSE); // this will increment 'newlevel' } if (newskillpoints) { msg("^GYou feel ready to learn a new skill!"); } } } int fovlist_contains(int *endx, int *endy, int nendcells, int x, int y) { int i; for (i = 0;i < nendcells; i++) { if ((endx[i] == x) && (endy[i] == y)) { return B_TRUE; } } return B_FALSE; } int get_fov_quad_endpoints(lifeform_t *lf, enum QUADRANT quad, int maxvisrange, int *endx, int *endy, int *nendcells) { int ix,iy,start,end; switch (quad) { case Q_NNE: // n to ne iy = lf->cell->y - maxvisrange; start = lf->cell->x; end = lf->cell->x + maxvisrange; for (ix = start; ix <= end; ix++) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_ENE: // ne to e ix = lf->cell->x + maxvisrange; start = lf->cell->y - maxvisrange; end = lf->cell->y; for (iy = start; iy <= end; iy++) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_ESE: // e to se ix = lf->cell->x + maxvisrange; start = lf->cell->y; end = lf->cell->y + maxvisrange; for (iy = start; iy <= end; iy++) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_SSE: // se to s iy = lf->cell->y + maxvisrange; start = lf->cell->x + maxvisrange; end = lf->cell->x; for (ix = start; ix >= end; ix--) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_SSW: // s to sw iy = lf->cell->y + maxvisrange; start = lf->cell->x; end = lf->cell->x - maxvisrange; for (ix = start; ix >= end; ix--) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_WSW: // sw to w ix = lf->cell->x - maxvisrange; start = lf->cell->y + maxvisrange; end = lf->cell->y; for (iy = start; iy >= end; iy--) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_WNW: // w to nw ix = lf->cell->x - maxvisrange; start = lf->cell->y; end = lf->cell->y - maxvisrange; for (iy = start; iy >= end; iy--) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_NNW: // nw to n iy = lf->cell->y - maxvisrange; start = lf->cell->x - maxvisrange; end = lf->cell->x; for (ix = start; ix <= end; ix++) { if (!fovlist_contains(endx,endy,*nendcells,ix,iy)) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } } break; case Q_NONE: break; } return *nendcells; } int get_adjacent_quadrants(int dir, enum QUADRANT *start, enum QUADRANT *end) { switch (dir) { case DC_N: // nw to ne if (start) *start = Q_NNW; if (end) *end = Q_NNE; break; case DC_NE: // n to ne, ne to e if (start) *start = Q_NNE; if (end) *end = Q_ENE; break; case DC_E: // ne to se if (start) *start = Q_ENE; if (end) *end = Q_ESE; break; case DC_SE: // e to se, se to s if (start) *start = Q_ESE; if (end) *end = Q_SSE; break; case DC_S: // se to sw if (start) *start = Q_SSE; if (end) *end = Q_SSW; break; case DC_SW: // s to sw, sw to w if (start) *start = Q_SSW; if (end) *end = Q_WSW; break; case DC_W: //sw to nw if (start) *start = Q_WSW; if (end) *end = Q_WNW; break; case DC_NW: // w to nw, nw to n if (start) *start = Q_WNW; if (end) *end = Q_NNW; break; default: if (start) *start = Q_NONE; if (end) *end = Q_NONE; return B_TRUE; } return B_FALSE; } int get_circular_fov_endpoints(lifeform_t *lf, int maxvisrange, int *endx, int *endy, int *nendcells) { int ix,iy,start,end,db = B_FALSE; *nendcells = 0; // n iy = lf->cell->y - maxvisrange; start = lf->cell->x - maxvisrange; end = lf->cell->x + maxvisrange; if (db) dblog("North::%d,%d - %d,%d",start,iy,end,iy); for (ix = start; ix < end; ix++) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } // e ix = lf->cell->x + maxvisrange; start = lf->cell->y - maxvisrange; end = lf->cell->y + maxvisrange; if (db) dblog("East::%d,%d - %d,%d",ix,start,ix,end); for (iy = start; iy < end; iy++) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } // s iy = lf->cell->y + maxvisrange; start = lf->cell->x + maxvisrange; end = lf->cell->x - maxvisrange; if (db) dblog("South::%d,%d - %d,%d",start,iy,end,iy); for (ix = start; ix > end; ix--) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } // w ix = lf->cell->x - maxvisrange; start = lf->cell->y + maxvisrange; end = lf->cell->y - maxvisrange; if (db) dblog("West::%d,%d - %d,%d",ix,start,ix,end); for (iy = start; iy > end; iy--) { endx[*nendcells] = ix; endy[*nendcells] = iy; (*nendcells)++; assert(*nendcells < MAXVISLIMIT); } return *nendcells; } int getactspeed(lifeform_t *lf) { int speed = 0; flag_t *f; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags; f = lfhasflag(lf, F_ACTIONSPEED); if (f) { speed = f->val[0]; } else { speed = SPEED_ACTION; // default } // modifier? getflags(lf->flags, retflag, &nretflags, F_SLOWACT, F_SLOWACTMOVE, F_FASTACT, F_FASTACTMOVE, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->id == F_SLOWACT) || (f->id == F_SLOWACTMOVE)) { speed += f->val[0]; } else if ((f->id == F_FASTACT) || (f->id == F_FASTACTMOVE)) { speed -= f->val[0]; } } switch (isburdened(lf)) { case BR_NONE: break; case BR_BURDENED: speed += 5; break; case BR_STRAINED: case BR_OVERLOADED: speed += 10; break; } adjustspeedforwater(lf, &speed); if (speed < 1) speed = 1; return speed; } int getadjenemies(lifeform_t *who, lifeform_t **adjlf, int *nadjlfs) { int d,nadj = 0; for (d = DC_N; d <= DC_NW; d++) { cell_t *c; c = getcellindir(who->cell, d); if (c && c->lf && !isdead(c->lf) && areenemies(who, c->lf)) { if (cansee(who, c->lf) && haslof(c->lf->cell, who->cell, LOF_WALLSTOP, NULL)) { if (adjlf) { adjlf[*nadjlfs] = c->lf; (*nadjlfs)++; } nadj++; } } } return nadj; } // populates (and returns) buf with object name for this flagpile's blood. // if no bloodob, buf will be set to "" and NULL returned. // // 'userandom' determines the behaviour if the flagpile has multiple F_BLOODOB flags. // B_FIRSTONE means just return the first flag. // B_ANYONE means return a random flag char *getbloodobname(flagpile_t *fp, char *buf, int userandom) { flag_t *retflag[MAXCANDIDATES],*f = NULL; int nretflags; getflags(fp, retflag, &nretflags, F_BLOODOB, F_NONE); if (nretflags) { if (nretflags == 1) { f = retflag[0]; } else { if (userandom) { // pick a random one f = retflag[rnd(0,nretflags-1)]; } else { // pick first one f = retflag[0]; } } if (f->text) { strcpy(buf, f->text); } else { strcpy(buf, ""); return NULL; } } else { // default strcpy(buf, "splash of blood"); } return buf; } // include allies or enemies which will follow you up/down stairs etc // ie. allies within LOS // ie. adjacent enemies void getwhowillfollow(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *seen, int *nadjallies) { int x,y; for (y = 0; y < lf->cell->map->h; y++) { for (x = 0; x < lf->cell->map->w; x++) { cell_t *c; c = getcellat(lf->cell->map, x, y); if (c && c->lf && (c->lf != lf)) { if (!isimmobile(c->lf) && !lfhasflag(c->lf, F_DOESNTMOVE) && !lfhasflag(c->lf, F_NOSTAIRS)) { int ok = B_FALSE; if (areallies(lf, c->lf) && haslof(c->lf->cell, lf->cell, LOF_NEED, NULL)) { // ally with a clear path to you (even if they can't see you, we assume // that you would tell them where you are going) ok = B_TRUE; } else if (areenemies(lf, c->lf) && (getcelldist(c, lf->cell) == 1) && cansee(c->lf, lf)) { // adjacent enemy who can see you ok = B_TRUE; } if (ok) { // if this was a pit, only flying things will follow if (stairob && hasflag(stairob->flags, F_PIT)) { if (!lfhasflag(c->lf, F_FLYING)) { ok = B_FALSE; } } if (ok) { // still ok? adjally[*nadjallies] = c->lf; if (seen) { if (areallies(lf, c->lf)) { seen[*nadjallies] = B_TRUE; } else { seen[*nadjallies] = cansee(lf, c->lf); } } (*nadjallies)++; if (*nadjallies >= MAXFOLLOWLFS) return; } } } } } } } enum ALIGNMENT getalignment(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_ALIGNMENT); if (!f) { return AL_NONE; } return f->val[0]; } // return skillcheck modifier for speech checks player vs lf int getalignmod(lifeform_t *lf) { enum ALIGNMENT al = AL_NONE; int alignmod = 0; al = getalignment(lf); if ((al == AL_GOOD) || (al == AL_EVIL)) { if (al == getalignment(player)) { alignmod = 3; } } return alignmod; } enum ALLEGIENCE getallegiance(lifeform_t *lf) { flag_t *f; if (lfhasflag(player, F_PLANTFRIEND) && (getraceclass(lf) == RC_PLANT)) { return AL_FRIENDLY; } f = lfhasflag(lf, F_CHARMEDBY); if (f) { lifeform_t *cb; cb = findlf(NULL, f->val[0]); if (cb) { return getallegiance(cb); } else { killflag(f); } } if (isplayer(lf) || isfriendly(lf)) { return AL_FRIENDLY; } else if (ispeaceful(lf)) { return AL_PEACEFUL; } return AL_HOSTILE; } int getallouterarmour(lifeform_t *lf, object_t **ob, int *nobs) { object_t *o; enum BODYPART bp; int i; *nobs = 0; for (i = 0; i < lf->race->nbodyparts; i++) { bp = lf->race->bodypart[i].id; o = getouterequippedob(lf, bp); if (o) { int n,found = B_FALSE; // already got htis one? for (n = 0; n < *nobs; n++) { if (ob[n]->id == o->id) { found = B_TRUE; } } if (!found) { ob[*nobs] = o; (*nobs)++; } } } return *nobs; } object_t *getarmour(lifeform_t *lf, enum BODYPART bp) { object_t *o; for (o = lf->pack->first ; o ; o = o->next) { if (isarmour(o) && isequippedon(o, bp)) { return o; } } return NULL; } int getarmouraccpenalty(lifeform_t *lf) { int pen = 0; flag_t *retflag[MAXCANDIDATES],*f; int nretflags,i; // get adjustments for bulky armour/shield getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_SHIELDPENALTY, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_ARMOURPENALTY) { pen += adjustarmourpenalty(lf, f->val[0]); } else if (f->id == F_SHIELDPENALTY) { pen += adjustshieldpenalty(lf, f->val[0]); } } return pen; } int getarmourevpenalty(lifeform_t *lf) { int pen = 0; flag_t *retflag[MAXCANDIDATES],*f; int nretflags,i; // get adjustments for bulky armour/shield getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_SHIELDPENALTY, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_ARMOURPENALTY) { pen += adjustarmourpenalty(lf, f->val[1]); } else if (f->id == F_SHIELDPENALTY) { pen += adjustshieldpenalty(lf, f->val[1]); } } return pen; } int getarmournoise(lifeform_t *lf) { object_t *o; int volmod = 0; for (o = lf->pack->first ; o ; o = o->next) { if (isarmour(o) && isequipped(o)) { // heavy metal armour makes noise if (ismetal(o->material->id) && (getobmass(o) >= 4)) { volmod++; } } } volmod -= getskill(lf, SK_ARMOUR); limit(&volmod, 0, NA); return volmod; } // hitob, hitchnace and narms are optional int getarmourrating(lifeform_t *lf, object_t **hitob, int *hitchance, enum BODYPART *hitbp, int *narms) { object_t *o; flag_t *f; int ar = 0, i; flag_t *retflag[MAXCANDIDATES]; int nretflags; if (narms) { (*narms) = 0; } getflags(lf->flags, retflag, &nretflags, F_ARBOOST, F_ARMOURRATING, F_MAGICARMOUR, F_PHALANX, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_ARBOOST) { ar += f->val[0]; } if (f->id == F_ARMOURRATING) { ar += f->val[0]; /* if (hitob) { hitob[*narms] = NULL; hitchance[*narms] = getbodyparthitchance(BP_BODY); if (hitbp) hitbp[*narms] = BP_BODY; (*narms)++; } */ } if (f->id == F_MAGICARMOUR) { ar += f->val[0]; } if (f->id == F_PHALANX) { int dir; cell_t *c; int nmatched = 0; // count adjacent allies of name xx for (dir = DC_N; dir <= DC_NW; dir++) { c = getcellindir(lf->cell, dir); if (c && c->lf) { if (strcasestr(c->lf->race->name, f->text)) { nmatched++; } } } if (nmatched >= f->val[2]) { ar += f->val[0]; } } } for (o = lf->pack->first ; o ; o = o->next) { flag_t *eqflag; // note: for obs equipped in multiples places, only the first is used. eqflag = hasflag(o->flags, F_EQUIPPED); if (eqflag) { f = hasflag(o->flags, F_ARMOURRATING); if (f) { float thisar; int isshield = B_FALSE; thisar = f->val[0]; if (hasflag(o->flags, F_SHIELD)) { isshield = B_TRUE; } // adjust for skill if (isshield) { switch (getskill(lf, SK_SHIELDS)) { case PR_INEPT: thisar *= 0.5; break; case PR_NOVICE: break; case PR_BEGINNER: thisar *= 1.2; break; case PR_ADEPT: thisar *= 1.4; break; case PR_SKILLED: thisar *= 1.6; break; case PR_EXPERT: thisar *= 1.8; break; case PR_MASTER: thisar *= 2; break; } } // adjust for condition //thisar = pctof(getobhppct(o), thisar); ar += thisar; ar += getobbonus(o, B_FALSE); if (hitob) { hitob[*narms] = o; hitchance[*narms] = getbodyparthitchance(isshield ? BP_BODY : eqflag->val[0]); if (hitbp) hitbp[*narms] = isshield ? BP_BODY : eqflag->val[0]; (*narms)++; } } } } limit(&ar, 0, NA); return ar; } // how far away should we be before attacking? int getattackspeed(lifeform_t *lf) { object_t *w; float speed; speed = getactspeed(lf); w = getweapon(lf); if (w) { int del; del = getobattackdelay(w); speed = pctof(del, speed); } // 50% longer to attack if exhausted if (isexhausted(lf)) { speed = pctof(150, speed); } return (int)speed; } float getattackstamlosslf(lifeform_t *lf, object_t *wep) { float loss; loss = getattackstamloss(wep); // base loss if (wep && loss > 0) { flag_t *f; // modify based on proficiency f = hasflag(wep->flags, F_USESSKILL); if (f && (f->val[0] != SK_NONE)) { enum SKILLLEVEL slev; slev = getskill(lf, f->val[0]); if (slev >= PR_NOVICE) { loss *= ((float)slev/10); limitf(&loss, 0, NA); } } } return loss; } float getattackstamloss(object_t *wep) { float loss; loss = STAMTOATTACK; if (wep) { int pctmod; pctmod = getobattackdelay(wep); // ie. 50 is fast, 100 is normal, 150 is slow, etc. pctmod -= 100; // ie. -50 is fast, 0 is normal, 50 is slow // ie. normal attack stamina cost, plus twice the weapon's "attack delay" loss = loss + (pctof(pctmod, loss)*3); } return loss; } int getattpoints(lifeform_t *lf) { flag_t *f; int attpoints = 0; f = lfhasflag(lf, F_STATGAINREADY); if (f) { attpoints = f->val[2]; } return attpoints; } int getattr(lifeform_t *lf, enum ATTRIB attr) { return real_getattr(lf, attr, B_FALSE); } int real_getattr(lifeform_t *lf, enum ATTRIB attr, int ignoreattrset) { int val = 0, i; flag_t *f; flag_t *retflag[MAXCANDIDATES]; int nretflags; // override? if (!ignoreattrset) { f = lfhasflagval(lf, F_ATTRSET, attr, NA, NA, NULL); if (f) { val = f->val[1]; } else { val = lf->att[attr]; } } else { // base attribute val = lf->att[attr]; } // modified? getflags(lf->flags, retflag, &nretflags, F_ATTRMOD, F_DRUNK, F_INJURY, F_RAGE, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->id == F_ATTRMOD) && (f->val[0] == attr)) { val += f->val[1]; } if (f->id == F_DRUNK) { val += getdrunkattrmod(lf, attr, f->val[0]); } if (f->id == F_INJURY) { if ((f->val[0] == IJ_NOSEBROKEN) && (attr == A_CHA)) { val -= 20; } else if ((f->val[0] == IJ_WINDPIPECRUSHED) && (attr == A_CON)) { val -= 30; } } if ((f->id == F_RAGE) && (attr == A_STR)) { val += 50; } } if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { val += getattrgodmod(attr, NULL); } if (val < 0) val = 0; return val; } // returns how much the given attrib is being modified by god piety. // if provided, populates 'retgod' with the id of the god modifying it (or R_NONE). int getattrgodmod(enum ATTRIB attr, enum RACE *retgodid) { int mod = 0; enum RACE whichgod = R_NONE; if ((attr == A_WIS) && godprayedto(R_GODMERCY)) { // worshipping yumi increases wisdom based on piety enum PIETYLEV plev; plev = getpietylev(R_GODMERCY, NULL, NULL); switch (plev) { case PL_ENRAGED: mod -= 25; break; case PL_FURIOUS: mod -= 10; break; case PL_ANGRY: mod -= 5; break; case PL_TOLERATED: break; case PL_INDIFFERENT: mod += 10; break; case PL_PLEASED: mod += 20 ; break; case PL_DELIGHTED: mod += 35 ; break; case PL_ECSTATIC: mod += 50 ; break; default: break; } if (mod != 0) { whichgod = R_GODMERCY; } } else if ((attr == A_CON) && godprayedto(R_GODLIFE)) { enum PIETYLEV plev; plev = getpietylev(R_GODLIFE, NULL, NULL); switch (plev) { case PL_INDIFFERENT: mod += 5; break; case PL_PLEASED: mod += 10 ; break; case PL_DELIGHTED: mod += 15 ; break; case PL_ECSTATIC: mod += 20 ; break; default: break; } if (mod != 0) { whichgod = R_GODLIFE; } } else if ((attr == A_IQ) && godprayedto(R_GODMAGIC)) { enum PIETYLEV plev; plev = getpietylev(R_GODMAGIC, NULL, NULL); switch (plev) { case PL_ENRAGED: mod -= 20; break; case PL_FURIOUS: mod -= 10; break; case PL_ANGRY: mod -= 5; break; case PL_TOLERATED: break; case PL_INDIFFERENT: mod += 10; break; case PL_PLEASED: mod += 20 ; break; case PL_DELIGHTED: mod += 30 ; break; case PL_ECSTATIC: mod += 50 ; break; default: break; } if (mod != 0) { whichgod = R_GODMAGIC; } } if (retgodid) { *retgodid = whichgod; } return mod; } // returns average damage per turn, modified by accuracy int getavgdam(lifeform_t *lf, int forxp) { obpile_t *op; int avgdam = 0,i; int db = B_FALSE; flag_t *f; flag_t *retflag[MAXCANDIDATES]; int nretflags,b; if (lfhasflag(lf, F_DEBUG)) { db = B_TRUE; } op = addobpile(NULL, NULL, NULL); getflags(lf->race->flags, retflag, &nretflags, F_HASATTACK, F_STARTOB, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_HASATTACK) { int min,max; float thisavg; object_t *o; float acc; flag_t *of; objecttype_t *ot; ot = findot(f->val[0]); o = addobfast(op, ot->id); getdamrange(o, f, &min,&max); thisavg = ((float)min + (float)max) / 2.0; // confers anything? foreach_bucket(b) { for (of = o->flags->first[b] ; of ; of = of->next) { if (of->id == F_HITCONFER) { flag_t *valflag; int maxlifetime; // get max lifetime gethitconferlifetime(f->text, NULL, &maxlifetime); valflag = hasflag(o->flags, F_HITCONFERVALS); assert(valflag); // assign xp based on what is conferred switch (of->val[0]) { case F_POISONED: // sum up poison power thisavg += (maxlifetime * valflag->val[1]); break; default: thisavg += 10; break; } } } } // modify for accuracy acc = getlfaccuracy(lf, o); limitf(&acc, 0, 100); thisavg = pctof(acc, thisavg); avgdam += thisavg; if (db) { char obname[BUFLEN]; getobname(o,obname,1); if (db) dblog("getavgdam: %s: == %d-%d dam, avg is %0.1f",obname, min, max, thisavg); } } else if (forxp && (f->id == F_STARTOB)) { // starting weapons... objecttype_t *ot; ot = findotn(f->text); if (ot && (ot->obclass->id == OC_WEAPON)) { obpile_t *op2; object_t *o; op2 = addobpile(NULL,NULL, NULL); o = addob(op2, f->text); if (o) { int min,max; float thisavg; float acc; flag_t *of; getdamrange(o, NULL, &min,&max); thisavg = ((float)min + (float)max) / 2.0; // confers anything? foreach_bucket(b) { for (of = o->flags->first[b] ; of ; of = of->next) { if (of->id == F_HITCONFER) { thisavg += 10; } } } // modify for accuracy acc = getlfaccuracy(lf, o); thisavg = pctof(acc, thisavg); avgdam += thisavg; if (db) { char obname[BUFLEN]; getobname(o,obname,1); if (db) dblog("getavgdam: %s: == %d-%d dam, avg is %0.1f",obname, min, max, thisavg); } } killobpile(op2); } } } if (!forxp) { object_t *w; // current weapon... w = getweapon(lf); if (w) { float thisavg,acc; float dammod; int bonus = 0,mindam,maxdam; // damage f = hasflag(w->flags, F_BONUS); if (f) { // only tell player about bonuses if they are known.! bonus = f->val[0]; } else { bonus = 0; } getdamrange(w, NULL, &mindam, &maxdam); mindam += bonus; maxdam += bonus; dammod = getstrdammod(lf); // apply damage mod for strength if (!hasflag(w->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) { mindam += dammod; maxdam += dammod; } if (mindam < 0) mindam = 0; if (maxdam < 0) maxdam = 0; thisavg = (((float)(mindam + maxdam)) / 2); acc = getlfaccuracy(lf, w); thisavg = pctof(acc, thisavg); avgdam += thisavg; } } if (op) killobpile(op); return (int)avgdam; } enum CASTTYPE getcasttype(lifeform_t *lf, enum OBTYPE sid) { flag_t *ctf; ctf = lfhasflagval(lf, F_CASTTYPE, sid, NA, NA, NULL); if (!ctf) { ctf = lfhasflagval(lf, F_CASTTYPE, OT_NONE, NA, NA, NULL); } if (ctf) { return ctf->val[1]; } return CT_NORMAL; } int getdistspotmod(lifeform_t *lf, cell_t *c) { int distmod,distance; distance = getcelldist(lf->cell, c); // can't spot things which are further away than your // perception skill. if (distance > getskill(lf, SK_PERCEPTION)) { distmod = 400; // ie. almost impossible } else { distmod = distance * 20; } limit(&distmod, 1, NA); return distmod; } int getdrunkattrmod(lifeform_t *lf, enum ATTRIB att, int drunkamt) { int val = 0; if (att == A_AGI) { if (hasjob(lf, J_PIRATE)) { val += (drunkamt*5); } else { val -= (drunkamt*5); } } else if (att == A_WIS) { val -= (drunkamt*5); } return val; } int getengineeringwallmod(lifeform_t *lf) { enum SKILLLEVEL slev; slev = getskill(lf, SK_ENGINEERING); return slev * 2; } float getequippedweight(lifeform_t *lf) { object_t *o; float total = 0; for (o = lf->pack->first ; o ; o = o->next) { if (isequipped(o) && isarmour(o)) { total += getobmass(o); } } return total; } int getevasion(lifeform_t *lf) { flag_t *f; int ev = 0,i; flag_t *retflag[MAXCANDIDATES]; int nretflags; double level_ev = 0, skillpctmod; enum SKILLLEVEL slev; slev = getskill(lf, SK_EVASION); // no evasion if you can't move or are exhausted! if (isimmobile(lf)) { return 0; } if (isexhausted(lf)) { return 0; } // no evasion if you're holding someone, or someone is holding you if (lfhasflag(lf, F_GRABBEDBY) || lfhasflag(lf, F_GRABBING)) { return 0; } ////////////////////////////////////////////////// // positive modifiers first ////////////////////////////////////////////////// // get natural evasion, adjustments for bulky armour/shield getflags(lf->flags, retflag, &nretflags, F_EVASION, F_NONE); for (i = 0; i < nretflags; i++) { ev += (retflag[i]->val[0]); } // level based evasion level_ev = gettr(lf); ev += level_ev; // dexterity mod if (slev) { ev += (getattr(lf, A_AGI)/5); } else { ev += (getattr(lf, A_AGI)/10); } // apply skill based evasion modifier skillpctmod = 100 + (slev * 12); ev = pctof(skillpctmod, ev); // swimevasion creatures get extra evasion while swimming f = lfhasflag(lf, F_SWIMEVASION); if (f && isswimming(lf)) { ev += f->val[0]; } // flightevasion creatures get extra evasion while flying //f = lfhasflag(lf, F_FLIGHTEVASION); //if (f && (isairborne(lf) == F_FLYING)) { // ev += f->val[0]; // } if (lfhasflag(lf, F_SWARM)) { int pct; pct = gethppct(lf); ev = pctof(100-pct,ev); limit(&ev, 0, NA); } ////////////////////////////////////////////////// // now negative modifiers ////////////////////////////////////////////////// ev -= getarmourevpenalty(lf); // you are easier to hit if you're glowing if (lfproduceslight(lf, NULL)) { ev -= 5; } // swimevasion creatures get extra evasion while swimming if (isswimming(lf) && lfhasflag(lf, F_SWIMEVASION) && !isaquatic(lf)) { // evasion penalty based on swimming skill switch (getskill(lf, SK_SWIMMING)) { case PR_INEPT: ev -= 30; break; case PR_NOVICE: ev -= 20; break; case PR_BEGINNER: ev -= 10; break; case PR_ADEPT: ev -= 5; break; case PR_SKILLED: ev -= 0; break; case PR_EXPERT: case PR_MASTER: break; } } if (lfhasflag(lf, F_FULLSHIELD)) { ev -= 50; } // modify for stickiness if (hasobwithflag(lf->cell->obpile, F_RESTRICTMOVEMENT)) { ev -= 50; } // modify for blindness // PLUS if you're blind, your evasion is 0 anyway for anyone // attacking you. if (isblind(lf)) { ev -= 15; } limit(&ev, 0, NA); return ev; } object_t *getbestthrowmissile(lifeform_t *lf, lifeform_t *target) { object_t *bestwep = NULL; int bestdam = -1; object_t *o; for (o = lf->pack->first ; o ; o = o->next) { int ismissileob = B_FALSE; if (!isequipped(o) && isthrowmissile(o) ) { ismissileob = B_TRUE; } else if (lfhasflagval(lf, F_WILLTHROW, o->type->id, NA, NA, NULL)) { ismissileob = B_TRUE; } if (ismissileob && canthrow(lf, o, NULL)) { int valid = B_TRUE; // powder is only a valid missile if we're adjacent to our target if (target && hasflag(o->flags, F_POWDER)) { if (getcelldist(lf->cell,target->cell) > 1) valid = B_FALSE; } if (valid) { int thisdam; // better than last one? thisdam = getthrowdam(o) + getshatterdam(o); if ((bestwep == NULL) || (thisdam > bestdam)) { bestwep = o; bestdam = thisdam; } } } } return bestwep; } object_t *getbestfirearm(lifeform_t *lf) { object_t *bestgun = NULL; object_t *o; int bestfirespeed = -1; bestgun = NULL; for (o = lf->pack->first ; o ; o = o->next) { // if it is a gun and we can weild it... if (isfirearm(o) && (isequipped(o) || canweild(lf, o))) { int thisfirespeed; thisfirespeed = getfirearmspeed(o); if (thisfirespeed > bestfirespeed) { bestgun = o; bestfirespeed = thisfirespeed; } } } return bestgun; } object_t *getbestweapon(lifeform_t *lf) { //obpile_t *op = NULL; object_t *bestwep = NULL; //int bestmaxdam = -999; object_t *o; flag_t *retflag[MAXCANDIDATES]; int nretflags; obpile_t *op = NULL; op = addobpile(NULL, NULL, NULL); bestwep = getweapon(lf); if (!bestwep) { int i; // get best innate attack getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE); for (i = 0; i < nretflags; i++) { objecttype_t *ot; ot = findot(retflag[i]->val[0]); if (ot) { o = addobfast(op, ot->id); if (isweapon(o) && !isfirearm(o) && canweild(lf, o) && isbetterwepthan(o, bestwep, lf)) { flag_t *damflag; bestwep = o; // inherit damage from hasattack flag damflag = hasflag(bestwep->flags, F_DAM); if (damflag) { damflag->val[1] = retflag[i]->val[1]; } } else { killob(o); } } } } for (o = lf->pack->first ; o ; o = o->next) { // if it does damage and we can weild it... if (isweapon(o) && !isfirearm(o) && canweild(lf, o)) { if (isbetterwepthan(o, bestwep, lf)) { bestwep = o; } } } if (bestwep && (bestwep->pile->owner == NULL)) { // ie. best weapon is an innate attack bestwep = NULL; } killobpile(op); return bestwep; } int getbodyparthitchance(enum BODYPART bp) { switch (bp) { case BP_NONE: return 0; case BP_WEAPON: return 0; case BP_SECWEAPON: return 0; case BP_EYES: return 1; case BP_HEAD: return 2; case BP_HEAD2: return 2; case BP_HEAD3: return 2; case BP_WAIST: return 3; case BP_HANDS: return 3; case BP_FEET: return 3; case BP_TAIL: return 3; case BP_FRONTLEGS: return 4; case BP_BACKLEGS: return 4; case BP_LEGS: return 4; case BP_SHOULDERS: return 4; case BP_WINGS: return 4; case BP_BODY: return 6; case BP_EARS: case BP_RIGHTFINGER: case BP_LEFTFINGER: case BP_NECK: break; } return 0; // ie rings, ears, weapon } char *getbodypartname(lifeform_t *lf, enum BODYPART bp) { if (lf) { int i; // does this bodypart have a special name? for (i = 0; i < lf->race->nbodyparts; i++) { if (lf->race->bodypart[i].id == bp) { if (strlen(lf->race->bodypart[i].name)) { return lf->race->bodypart[i].name; } } } } switch (bp) { case BP_NONE: return "__bp_none__"; case BP_WEAPON: return "right hand"; case BP_SECWEAPON: return "left hand"; case BP_RIGHTFINGER: return "right finger"; case BP_LEFTFINGER: return "left finger"; case BP_HANDS: return "hands"; case BP_EARS: return "ears"; case BP_EYES: return "eyes"; case BP_HEAD: return "head"; case BP_HEAD2: return "second head"; case BP_HEAD3: return "third head"; case BP_NECK: return "neck"; case BP_BODY: return "body"; case BP_SHOULDERS: return "shoulders"; case BP_WAIST: return "waist"; case BP_FRONTLEGS: return "front legs"; case BP_BACKLEGS: return "back legs"; case BP_LEGS: return "legs"; case BP_FEET: return "feet"; case BP_TAIL: return "tail"; case BP_WINGS: return "wings"; } return "unknown"; } char *getbodypartequipname(enum BODYPART bp) { switch (bp) { case BP_WEAPON: case BP_SECWEAPON: case BP_EARS: return "in"; // ie. 'in right hand' case BP_RIGHTFINGER: case BP_LEFTFINGER: case BP_HANDS: case BP_HEAD: case BP_HEAD2: case BP_HEAD3: case BP_BODY: case BP_FRONTLEGS: case BP_BACKLEGS: case BP_LEGS: case BP_FEET: case BP_WINGS: case BP_TAIL: case BP_NONE: return "on"; case BP_EYES: case BP_SHOULDERS: return "over"; case BP_WAIST: case BP_NECK: return "around"; } return "unknown"; } object_t *getequippedob(obpile_t *op, enum BODYPART bp) { object_t *o,*poss[MAXCANDIDATES]; int nposs = 0,i; // first get a list of all objects equipped there. // normally there will only be one, unless we have // underclothing (ie. shirt + body armour). for (o = op->first; o ; o = o->next) { if (hasflagval(o->flags, F_EQUIPPED, bp, NA, NA, NULL)) { poss[nposs++] = o; } } if (!nposs) { return NULL; } else if (nposs == 1) { return poss[0]; } // we have more than one item equipped. get the inner one with F_UNDERCLOTHING. for(i = 0; i < nposs; i++) { if (hasflag(poss[i]->flags, F_UNDERCLOTHING)) return poss[i]; } // should never get here... assert("bug in getequippedob()." == 0); return NULL; } int getexposedlimbs(lifeform_t *lf) { int exposedlimbs = 0; if (!getouterequippedob(lf, BP_HEAD)) exposedlimbs += 1; if (!getouterequippedob(lf, BP_BODY)) exposedlimbs += 2; if (!getouterequippedob(lf, BP_HANDS)) exposedlimbs += 1; if (!getouterequippedob(lf, BP_LEGS)) exposedlimbs += 2; if (!getouterequippedob(lf, BP_FEET)) exposedlimbs += 1; return exposedlimbs; } // how many extra spells does this lf get to choose from? // based on iq. int getextraspellchoices(lifeform_t *lf) { int mod = 0; enum ATTRBRACKET iqb; iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); switch (iqb) { case AT_EXLOW: mod -= 3; break; case AT_VLOW: mod -= 2; break; case AT_LOW: mod -= 1; break; case AT_LTAVERAGE: mod -= 1; break; case AT_GTAVERAGE: mod += 1; break; case AT_HIGH: mod += 1; break; case AT_VHIGH: mod += 2; break; case AT_EXHIGH: mod += 3; break; default: break; } return mod; } object_t *getfirearm(lifeform_t *lf) { object_t *o; o = getequippedob(lf->pack, BP_SECWEAPON); if (o && isfirearm(o)) { return o; } o = getequippedob(lf->pack, BP_WEAPON); if (o && hasflag(o->flags, F_FIREARM)) { return o; } return NULL; } enum LOFTYPE getfirearmloftype(lifeform_t *lf) { if (getskill(lf, SK_RANGED) >= PR_EXPERT) { return LOF_WALLSTOP; } return LOF_NEED; } int getfootprinttime(lifeform_t *lf) { int time; time = TM_FOOTPRINT; switch (getlfsize(lf)) { case SZ_MINI: time = 1; break; case SZ_TINY: time -= 15; break; case SZ_SMALL: time -= 10; break; default: break; case SZ_LARGE: time += 10; break; case SZ_HUGE: time += 20; break; case SZ_ENORMOUS: time += 30; break; } return time; } enum GENDER getgender(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_GENDER); if (f) { return f->val[0]; } return G_NONE; } lifeform_t *getguntarget(lifeform_t *lf) { flag_t *f; f = hasflag(lf->flags, F_GUNTARGET); if (!f) { return NULL; } return findlf(NULL, f->val[0]); } int getguntargetid(lifeform_t *lf) { flag_t *f; f = hasflag(lf->flags, F_GUNTARGET); if (!f) { return -1; } return f->val[0]; } int gethearingrange(lifeform_t *lf) { int range = 2; // default if (!isasleep(lf)) { // if awake, your listen skills helps range += (getskill(lf, SK_LISTEN)*2); } return range; } int gettrrace(race_t *r) { flag_t *f; f = hasflag(r->flags, F_TR); return f->val[0]; } int gethitstokill(lifeform_t *lf, lifeform_t *victim, int useevasion, int usearmour) { object_t *wep[MAXCANDIDATES]; flag_t *damflag[MAXCANDIDATES]; obpile_t *op = NULL; int nweps = 0,hitstokill = 0; getweapons(lf, B_MELEEONLY, wep, damflag, NULL, &op, &nweps); if (nweps) { int maxdam; getdamrange(wep[0], damflag[0], NULL, &maxdam); // modify by victim's evasion? if (useevasion) { float ev; ev = ((float)getevasion(victim)); maxdam -= pctof(ev, maxdam); } // modify by victim's armour? if (usearmour) { int ar,aravg,amin,amax; ar = getarmourrating(victim, NULL, NULL, NULL, NULL); getarrange(ar, &amin, &amax); aravg = (int)(((float)amin + (float)amax) / 2.0); maxdam -= aravg; } if (maxdam >= 1) { int nattacks; hitstokill = victim->hp / maxdam; limit(&hitstokill, 1, NA); // modify by maxattacks getattacks(lf, NULL, &nattacks); hitstokill /= nattacks; limit(&hitstokill, 1, NA); } else { hitstokill = 0; // ie you'll never kill it. } } if (op) killobpile(op); return hitstokill; } int gethppct(lifeform_t *lf) { float pct; pct = (int)(((float)lf->hp / (float)lf->maxhp) * 100); return pct; } enum COLOUR gethungercol(enum HUNGER hlev) { enum COLOUR col = C_GREY; switch (hlev) { case H_STUFFED: col = C_LIGHTBLUE; break; case H_FULL: col = C_LIGHTGREEN; break; case H_NONE: col = C_GREEN; break; case H_PECKISH: col = C_GREY; break; case H_HUNGRY: col = C_DARKYELLOW; break; case H_VHUNGRY: col = C_YELLOW; break; default: case H_STARVING: col = C_RED; break; } return col; } enum HUNGER gethungerlevel(int hunger) { int thresh = HUNGERCONST; if (hunger < -thresh) { return H_STUFFED; } else if (hunger < 0) { return H_FULL; } else if (hunger <= thresh) { return H_NONE; } else if (hunger <= (thresh*2)) { return H_PECKISH; } else if (hunger <= (thresh*3)) { return H_HUNGRY; } else if (hunger <= (thresh*4)) { return H_VHUNGRY; } else if (hunger <= (thresh*5)) { return H_STARVING; } return H_STARVED; } char *gethungername(lifeform_t *lf, enum HUNGER hunger, char *buf) { switch (hunger) { case H_STUFFED: strcpy(buf, "stuffed"); break; case H_FULL: strcpy(buf, "full"); break; case H_NONE: if (lf && (lf->race->id == R_VAMPIRE)) { strcpy(buf, "not thirsty"); } else { strcpy(buf, "not hungry"); } break; case H_PECKISH: if (lf && (lf->race->id == R_VAMPIRE)) { strcpy(buf, "parched"); } else { strcpy(buf, "peckish"); } break; case H_HUNGRY: if (lf && (lf->race->id == R_VAMPIRE)) { strcpy(buf, "thirsty"); } else { strcpy(buf, "hungry"); } break; case H_VHUNGRY: if (lf && (lf->race->id == R_VAMPIRE)) { strcpy(buf, "very thirsty"); } else { strcpy(buf, "ravenous"); } break; case H_STARVING: if (lf && (lf->race->id == R_VAMPIRE)) { strcpy(buf, "dehydrating"); } else { strcpy(buf, "starving"); } break; case H_STARVED: strcpy(buf, "starved"); break; } return buf; } int gethungerval(lifeform_t *lf) { flag_t *f; f = hasflag(lf->flags, F_HUNGER); if (f) { return f->val[0]; } return 0; } job_t *getjob(lifeform_t *lf) { flag_t *f; // no job if polymorphed if (lfhasflag(lf, F_POLYMORPHED)) { return NULL; } f = hasflag(lf->flags, F_JOB); if (f) { return findjob(f->val[0]); } return NULL; } enum JOBCATEGORY getjobcat(lifeform_t *lf) { flag_t *f; // no job if polymorphed if (lfhasflag(lf, F_POLYMORPHED)) { return JC_NONE; } f = hasflag(lf->flags, F_JOB); if (f) { job_t *j; j = findjob(f->val[0]); if (j) return j->category; } return JC_NONE; } /* enum SUBJOB getsubjob(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_JOB); if (f && (f->val[1] != NA)) { return f->val[1]; } return SJ_NONE; } */ char *getjobname(lifeform_t *lf) { job_t *j; j = getjob(lf); if (j) { return j->name; } return ""; } int getjobrecommendation(race_t *r, job_t *j) { int rec = 0; flag_t *retflag[MAXCANDIDATES],*jobflag,*f; int nretflags,i,b; getflags(r->flags, retflag, &nretflags, F_STARTATT, F_STARTSKILL, F_NOSPELLS, F_NOSKILL, F_MPMOD, F_DTIMMUNE, F_DTRESIST, F_RESISTMAG, F_MPDICE, F_ARMOURRATING, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->id == F_STARTATT) && (f->val[1] != AT_RANDOM)) { flag_t *jobflag; jobflag = hasflagval(j->flags, F_JOBATTRMOD, f->val[0], NA, NA, NULL); if (jobflag && (jobflag->val[1] != AT_RANDOM)) { if ((f->val[1] >= AT_HIGH) && (jobflag->val[1] <= -10)) { // race high, job low = -- rec -= 1; } else if ((f->val[1] >= AT_GTAVERAGE) && (jobflag->val[1] > 0)) { // race high, job high = ++ rec += 2; } else if ((f->val[1] <= AT_LTAVERAGE) && (jobflag->val[1] > 0)) { // race low, job high = -- rec -= 2; //} else if ((f->val[1] < 0) && (jobflag->val[1] < 0)) { // race low, job low = ---- // rec -= 2; } } } else if (f->id == F_STARTSKILL) { if (!isloreskill(f->val[0])) { jobflag = hasflagval(j->flags, f->id, f->val[0], NA, NA, NULL); if (jobflag) { // both race and job start with the same skill rec++; } jobflag = hasflagval(j->flags, F_NOSKILL, f->val[0], NA, NA, NULL); if (jobflag) { // race has a skill, job can't have that skill rec -= 4; } // magic skill, and job has nomagic? if (isspellskill(f->val[0]) && hasflag(j->flags, F_NOSPELLS)) { rec -= 6; } } } else if (f->id == F_ARMOURRATING) { foreach_bucket(b) { for (jobflag = j->flags->first[b] ; jobflag ; jobflag = jobflag->next) { if ((jobflag->id == F_STARTSKILL) && isweaponskill(jobflag->val[0])) { rec += 1; } } } } else if ((f->id == F_DTIMMUNE) || (f->id == F_DTRESIST)) { if (hasflag(j->flags, f->id)) { rec += 2; } if ((f->val[0] == DT_FIRE) || (f->val[0] == DT_HEAT)) { if (hasflagval(j->flags, F_STARTSKILL, SK_SS_FIRE, NA, NA, NULL)) { rec += 4; } } else if (f->val[0] == DT_COLD) { if (hasflagval(j->flags, F_STARTSKILL, SK_SS_COLD, NA, NA, NULL)) { rec += 4; } } else if (f->val[0] == DT_NECROTIC) { if (hasflagval(j->flags, F_STARTSKILL, SK_SS_DEATH, NA, NA, NULL)) { rec += 4; } } } else if (f->id == F_NOSPELLS) { // both race and job can't us spells if (hasflag(j->flags, f->id)) { rec += 4; } // race can't have spells, and job has a magic skill? // (SLOW search) foreach_bucket(b) { for (jobflag = j->flags->first[b] ; jobflag ; jobflag = jobflag->next) { if ((jobflag->id == F_STARTSKILL) && isspellskill(jobflag->val[0])) { rec -= 2; } } } // job uses mp? if (hasflag(j->flags, F_MPDICE)) { rec -= 4; } } else if (f->id == F_MPMOD) { jobflag = hasflag(j->flags, f->id); if (f->val[0] < 0) { // mp penalty if (jobflag && (jobflag->val[0] < 0)) { rec += 4; } if (hasflag(j->flags, F_NOSPELLS)) { rec += 6; } else if (hasflag(j->flags, F_MPDICE)) { rec -= 2; } } else if (f->val[0] > 0) { // mp bonus if (jobflag && (jobflag->val[0] > 0)) { rec += 4; } else if (jobflag && (jobflag->val[0] < 0)) { rec -= 4; } if (hasflag(j->flags, F_NOSPELLS)) { rec -= 6; } else if (hasflag(j->flags, F_MPDICE)) { rec += 4; } } } else if (f->id == F_RESISTMAG) { if (hasflag(j->flags, f->id)) { rec += 6; } } else if (f->id == F_MPDICE) { if (hasflag(j->flags, F_NOSPELLS)) { rec -= 4; } } else if (f->id == F_NOSKILL) { jobflag = hasflagval(j->flags, F_STARTSKILL, f->val[0], NA, NA, NULL); if (jobflag) { // race can't have a skill, job starts with it rec -= 4; } jobflag = hasflagval(j->flags, F_CANLEARN, f->val[0], NA, NA, NULL); if (jobflag) { // race can't have a skill, job can learn it rec--; } jobflag = hasflagval(j->flags, F_NOSKILL, f->val[0], NA, NA, NULL); if (jobflag) { // race can't have a skill, job can't have that skill rec += 2; } } } return rec; } int getkotime(lifeform_t *lf) { int kotime; kotime = rnd(50,100); if (lf != player) { // monsters stay knocked out for a long time kotime *= 10; } return kotime; } int getlastdir(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_LASTDIR); if (f) { return f->val[0]; } return D_NONE; } int getleftrightwalls(lifeform_t *lf) { cell_t *rightcell,*leftcell; int rightdir,leftdir; int walls = 0; // remember walls to left and right leftdir = lf->facing - 1; if (leftdir < DC_N) leftdir = DC_NW; rightdir = lf->facing + 1; if (rightdir > DC_NW) rightdir = DC_N; leftcell = getcellindir(lf->cell, leftdir); rightcell = getcellindir(lf->cell, rightdir); if (!leftcell || (leftcell && leftcell->type->solid)) { walls += 1; } if (!rightcell || (rightcell && rightcell->type->solid)) { walls += 2; } return walls; } // returns lf's accuracy, as a percentage. int getlfaccuracy(lifeform_t *lf, object_t *wep) { flag_t *f; object_t *o; int acc = 0,i; flag_t *retflag[MAXCANDIDATES]; int nretflags; int unarmed = B_FALSE; enum TEMPERATURE temp; // get weapon if (wep) { acc = getobaccuracy(wep, lf, B_FALSE); if (hasflag(wep->flags, F_UNARMEDWEP)) { unarmed = B_TRUE; } else { unarmed = B_FALSE; } } else { unarmed = B_TRUE; // for unarmed attacks, accuracy is based on agility acc = getattr(lf, A_AGI) + 20; limit(&acc, 20, 100); } // dual weilding? if (isdualweilding(lf)) { switch (getskill(lf, SK_TWOWEAPON)) { case PR_INEPT: acc -= 100; break; case PR_NOVICE: acc -= 20; break; case PR_BEGINNER: acc -= 10; break; default: break; } } for (o = lf->pack->first ;o ; o = o->next) { if (isequipped(o)) { f = hasflag(o->flags, F_ACCURACYMOD); if (f) acc += f->val[0]; } } acc -= getarmouraccpenalty(lf); // adjust for bulky armour/shield, or injuries getflags(lf->flags, retflag, &nretflags, F_ACCURACYMOD, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_ACCURACYMOD) { acc += f->val[0]; } else if (f->id == F_INJURY) { switch (f->val[0]) { case IJ_FINGERBROKEN: case IJ_TORSOBRUISED: acc -= 10; break; case IJ_EYELIDSCRAPED: case IJ_TORSOBRUISEDBAD: case IJ_SHOULDERDISLOCATED: acc -= 20; break; case IJ_RIBBROKEN: case IJ_TAILBROKEN: acc -= 30; break; } } } // modify for weilder's level acc += (lf->level * 2); if (lfhasflag(lf, F_FULLSHIELD)) { acc -= 50; } if (lfhasflag(lf, F_RAGE)) { // huge bonus acc += 50; } /* else { int dexmod; // modify with dexterity dexmod = getstatmod(lf, A_AGI) / 2; // -25 - +25 // double dex penalties when dual weilding if (isdualweilding(lf) && (dexmod < 0)) { dexmod *= 2; } acc += dexmod; } */ // modify for blindness if (isblind(lf)) { if (getskill(lf, SK_COMBAT) < PR_ADEPT) { acc -= 50; } } // modify for being on the ground if (isprone(lf)) { if (getskill(lf, SK_COMBAT) < PR_BEGINNER) { acc -= 40; } } // cold = less accuracy temp = getlftemp(lf); acc -= gettempaccpenalty(lf, temp); // day/night boosts if (isnighttime()) { f = lfhasflag(lf, F_NIGHTBOOST); if (f) { if (f->val[0] != NA) acc += f->val[0]; } } else if (isdaytime()) { // ie. daytime f = lfhasflag(lf, F_DAYBOOST); if (f) { if (f->val[0] != NA) acc += f->val[0]; } } // modify for swimming if (isswimming(lf) && !isaquatic(lf)) { switch (getskill(lf, SK_SWIMMING)) { // you can't attack until you are at // expert or better. default: acc = 0; break; case PR_EXPERT: acc -= 20; break; case PR_MASTER: break; // no penalty } } // modify for drunkenness f = lfhasflag(lf, F_DRUNK); if (f) { int amt; int time; time = f->lifetime; if (time < 0) time = 70; // ie permenant limit(&time, NA, 70); amt = (time/TM_DRUNKTIME)+1; if (hasjob(lf, J_PIRATE)) { acc += (10*amt); } else { acc -= (10*amt); } } // agi scaling on weapon if (wep) { getflags(wep->flags, retflag, &nretflags, F_ATTREQ, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[0] == A_AGI) { int pctmod; meetsattreq(lf, retflag[i], wep, &pctmod); acc += pctmod; } } } // modify for nausea if (lfhasflag(lf, F_NAUSEATED)) { if (getskill(lf, SK_COMBAT) < PR_BEGINNER) { acc -= 25; } } // modify for stickiness if (gamemode == GM_GAMESTARTED) { if (hasobwithflag(lf->cell->obpile, F_RESTRICTMOVEMENT)) { acc -= 25; } } //if (acc < 0) acc = 0; return acc; } char getlfcol(lifeform_t *lf, enum MSGCHARCOL cc) { switch (cc) { case CC_VBAD: if (areallies(player, lf)) { return 'B'; } else if (areenemies(player, lf)) { return 'G'; } else { return 'n'; } break; case CC_BAD: if (areallies(player, lf)) { return 'b'; } else if (areenemies(player, lf)) { return 'g'; } else { return 'n'; } break; case CC_NORMAL: return 'n'; case CC_GOOD: if (areallies(player, lf)) { return 'g'; } else { return 'n'; } break; case CC_VGOOD: if (areallies(player, lf)) { return 'G'; } else { return 'n'; } break; } return 'n'; } enum LFCONDITION getlfcondition(lifeform_t *lf) { float hp,maxhp; int pct; hp = lf->hp; maxhp = lf->maxhp; pct = (int)((hp / maxhp) * 100.0); if (pct == 100) { return C_HEALTHY; } else if (pct >= 80) { return C_HURT; } else if (pct >= 50) { return C_WOUNDED; } else if (pct >= 25) { return C_SERIOUS; } else if (pct > 0) { return C_CRITICAL; } // ie. <= 0 return C_DEAD; } enum TEMPERATURE getlftemp(lifeform_t *lf) { int temp; enum TEMPERATURE brack; // start with cell temperature brack = getcelltemp(lf->cell, &temp); // cold? if (brack < T_NORMAL) { // adjust for warm/cold blood if (lfhasflag(lf, F_COLDBLOOD)) { brack = T_NORMAL; } else if (isimmuneto(lf->flags, DT_COLD, B_FALSE)) { brack = T_NORMAL; } else if (isresistantto(lf->flags, DT_COLD, B_FALSE)) { brack++; // one bracket higher } else if (isvulnto(lf->flags, DT_COLD, B_FALSE) && (brack > T_VCOLD)) { brack--; // one bracket lower } } else if (brack > T_NORMAL) { // adjust for warm/cold blood if (lfhasflag(lf, F_COLDBLOOD) && (brack < T_VHOT)) { brack++; } else if (isimmuneto(lf->flags, DT_FIRE, B_FALSE)) { brack = T_NORMAL; } else if (isresistantto(lf->flags, DT_FIRE, B_FALSE)) { brack--; // one bracket lower } else if (isvulnto(lf->flags, DT_FIRE, B_FALSE) && (brack < T_VHOT)) { brack++; // one bracket higher } } return brack; } // returns a value representing 'lf's height off the ground. // higher value means higher int getfeetheight(lifeform_t *lf) { int height = 0; int temph; if (isairborne(lf, &temph)) { height += temph; } else { // climbing a wall? if (isclimbing(lf)) { if (height < SZ_LARGE) height = SZ_LARGE; } else { object_t *o; // is lf climbing on top of something, or on a wall? o = hasobwithflag(lf->cell->obpile, F_CLIMBOBSTACLE); if (o) { flag_t *f; f = hasflag(o->flags, F_IMPASSABLE); if (f && (f->val[0] > 0)) { height += f->val[0]; } } } } return height; } int getlistendetectrange(lifeform_t *lf) { switch (getskill(lf, SK_LISTEN)) { default: case PR_INEPT: return 0; case PR_NOVICE: return 0; case PR_BEGINNER: return 1; case PR_ADEPT: return 2; case PR_SKILLED: return 3; case PR_EXPERT: return 4; case PR_MASTER: return 6; } return 0; } int getmasterid(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_PETOF); if (f) { return f->val[0]; } return -1; } enum SKILLLEVEL getmaxskilllevel(lifeform_t *lf, enum SKILL skid) { flag_t *f; enum SKILLLEVEL maxlev = PR_MASTER; f = lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL); if (f) { if (f->val[1] == NA) { maxlev = PR_MASTER; } else { maxlev = f->val[1]; } } return maxlev; } int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions) { flag_t *f; lifeform_t *min; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags; *nminions = 0; getflags(lf->flags, retflag, &nretflags, F_MINION, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_MINION) { min = findlf(lf->cell->map, f->val[0]); if (min) { (minion[*nminions]) = min; (*nminions)++; } } } return *nminions; } int getmiscastchance(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; int chance = 0; sumflags(lf->flags, F_MISCASTCHANCE, &chance, NULL, NULL); getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_SHIELDPENALTY, F_NONE); for (i = 0; i < nretflags; i++) { int (*adjustfunc)(lifeform_t *, float) = NULL; if (retflag[i]->id == F_ARMOURPENALTY) { adjustfunc = adjustarmourpenalty; } else { adjustfunc = adjustshieldpenalty; } if (retflag[i]->val[0] != NA) chance += adjustfunc(lf, retflag[i]->val[0]); if (retflag[i]->val[1] != NA) chance += adjustfunc(lf, retflag[i]->val[1]); } return chance; } void getmonkattacks(int level, int *rmin, int *rmax) { int min = 1,max = 1; if ((level >= 2) && (level <= 3)) { min = 1; max = 1; } else if ((level >= 4) && (level <= 6)) { min = 1; max = 2; } else if ((level >= 7) && (level <= 9)) { min = 2; max = 2; } else if ((level >= 10) && (level <= 12)) { min = 2; max = 3; } else if ((level >= 13) && (level <= 16)) { min = 3; max = 3; } else if (level >= 17) { min = 3; max = 4; } if (rmin) *rmin = min; if (rmax) *rmax = max; } int getmonkdr(int level) { int dr = 5; if (level == 1) { dr = 5; } else { dr = 5+(level/2); } limit(&dr, NA, 8); return dr; } int getmorale(lifeform_t *lf) { flag_t *f; f = hasflag(lf->flags, F_MORALE); if (f) return f->val[0]; // defaults to threat level return gettr(lf); } int getnaturalflightheight(lifeform_t *lf) { switch (getskill(lf, SK_FLIGHT)) { case PR_NOVICE: return SZ_SMALL; case PR_BEGINNER: return SZ_MEDIUM; case PR_ADEPT: return SZ_HUMAN; case PR_SKILLED: return SZ_LARGE; case PR_EXPERT: return SZ_HUGE; case PR_MASTER: return SZ_ENORMOUS; default: break; } return 0; } // returns a number from 1 - LASTSHORTCUT containing the first avilaable shortcut slot. // // if none available, return NA. // int getnextshortcut(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i,slotnum = 0; // get all currently assigned shortcuts getflags(lf->flags, retflag, &nretflags, F_SHORTCUT, F_NONE); // use 1=10 instead of 0=9, so that '0' is last. for (slotnum = 1; slotnum <= (LASTSHORTCUT+1); slotnum++) { int found = B_FALSE; int slottouse; if (slotnum == 10) slottouse = 0; else slottouse = slotnum; // technically this is faster than using hasflagval() for (i = 0; i < nretflags; i++) { if (retflag[i]->val[0] == slottouse) { found = B_TRUE; break; } } if (!found) { // this slot is available return slottouse; } } return NA; } int getnightvisrange(lifeform_t *lf) { int range = 0; // default flag_t *f; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; int lightamt = 0; f = lfhasflag(lf, F_SEEINDARK); if (f && !isblind(lf)) { if (f->val[0] == UNLIMITED) { range = MAXVISRANGE; } else { range = f->val[0]; } } else { f = lfhasflag(lf, F_TREMORSENSE); if (f) { return f->val[0]; } /* else { // depends how much your eyes have adjusted range = (lf->eyeadjustment / 10); } */ } // modifications? getflags(lf->flags, retflag, &nretflags, F_NIGHTVISRANGEMOD, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_NIGHTVISRANGEMOD) { range += f->val[0]; } } //getmaxflags(lf->flags, F_PRODUCESLIGHT, &lightamt, NULL, NULL); lightamt = lfproduceslight(lf, NULL); range += lightamt; limit(&range, 0, MAXVISRANGE); return range; } // populates heartext, seetext and volume // "lf" is optional. if not given, "noiseflag" should be provided. // returns TRUE on failure int getnoisedetails(lifeform_t *lf, enum NOISETYPE nid, flag_t *noiseflag, char *heartext,char *seetext, int *volume, flag_t **returnedflag) { flag_t *retflag[MAXCANDIDATES],*nflag[MAXCANDIDATES]; flag_t *nf = NULL; int nretflags, i,nnflags = 0; int ok = B_FALSE; // deafults if (volume) *volume = 0; if (heartext) strcpy(heartext, ""); if (seetext) strcpy(seetext, ""); if (returnedflag) *returnedflag = NULL; if (noiseflag) { nf = noiseflag; } else if (lf) { if (lfhasflag(lf, F_FROZEN)) { // can't make noise if frozen! return B_TRUE; } if ((nid == N_WALK) || nid == (N_FLY)) { if (!movecausesnoise(lf)) { return B_TRUE; } } getflags(lf->flags, retflag, &nretflags, F_NOISETEXT, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[0] == nid) { nflag[nnflags++] = retflag[i]; } } if (nnflags) { nf = nflag[rnd(0,nnflags-1)]; } } if (nf) { char verb[BUFLEN], noun[BUFLEN]; if (volume) *volume = nf->val[1]; if (returnedflag) { *returnedflag = nf; } if (nf->text[0] == '^') { strcpy(verb, ""); //noun = strtok_r(nf->text, "^", &dummy); strcpy(noun, nf->text + 1); } else { char *p; p = readuntil(verb, nf->text, '^'); readuntil(noun, p, '^'); // ie eol //verb = strtok_r(nf->text, "^", &dummy); //noun = strtok_r(NULL, "^", &dummy); } if (heartext) { if (strlen(noun)) { snprintf(heartext, BUFLEN, "%s.",noun); } } if (seetext) { if (strlen(verb)) { strcpy(seetext, verb); } } if (nid == N_WALK) { if (lf) *volume += getarmournoise(lf); } ok = B_TRUE; } else if (lf) { // some defaults if (nid == N_WALK) { enum LFSIZE sz; char movetext[BUFLEN]; strcpy(movetext, ""); sz = getlfsize(lf); switch (sz) { case SZ_MINI: case SZ_TINY: if (volume) *volume = 0; break; case SZ_SMALL: if (volume) *volume = 1; strcpy(movetext, "light footsteps."); break; case SZ_MEDIUM: case SZ_HUMAN: if (volume) *volume = 2; strcpy(movetext, "footsteps."); break; case SZ_LARGE: if (volume) *volume = 3; strcpy(movetext, "heavy footsteps."); break; case SZ_HUGE: if (volume) *volume = 4; strcpy(movetext, "heavy footsteps."); break; case SZ_ENORMOUS: if (volume) *volume = 5; strcpy(movetext, "very heavy footsteps."); break; case SZ_MAX: if (volume) *volume = 6; strcpy(movetext, "extremely loud thumping."); break; default: break; } if (strlen(movetext)) { if (volume) *volume += getarmournoise(lf); if (heartext) strcpy(heartext, movetext); if (lfhasflag(lf, F_CAREFULMOVE)) { (*volume)--; limit(volume, 1, NA); } ok = B_TRUE; } } else if (nid == N_DEAFENSCREAM) { if (volume) *volume = SV_PLANE; if (heartext) strcpy(heartext, "a deafening scream"); if (seetext) strcpy(seetext, "screams loudly!"); ok = B_TRUE; } else if (nid == N_DEATHKEEN) { if (volume) *volume = SV_TALK; if (heartext) strcpy(heartext, "the dread wailing of death!"); if (seetext) strcpy(seetext, "wails with the power of death!"); ok = B_TRUE; } else if (nid == N_SONICBOLT) { if (volume) *volume = 5; if (heartext) strcpy(heartext, "a ear-splitting burst of sound!"); if (seetext) strcpy(seetext, "emits an ear-splitting burst of sound!"); ok = B_TRUE; } else if (nid == N_WARCRY) { if (volume) *volume = 4; if (heartext) strcpy(heartext, "a blood-curdling war cry!"); if (seetext) strcpy(seetext, "shouts a blood-curdling war-cry!"); ok = B_TRUE; } } if (!ok) { // failed! return B_TRUE; } // when hiding, volume is decreased by one if (lfhasflag(lf, F_HIDING)) { if (volume) { *volume -= 1; if (*volume <= 0) return B_TRUE; } } // adjust footstep volume for floor type if ((nid == N_WALK) && lf->cell && volume) { *volume += lf->cell->type->volumemod; if (*volume <= 0) return B_TRUE; } return B_FALSE; } char *getlfconditionname(enum LFCONDITION cond) { switch (cond) { case C_CRITICAL: return "critically wounded"; case C_SERIOUS: return "seriously wounded"; case C_WOUNDED: return "wounded"; case C_HURT: return "hurt"; case C_HEALTHY: return "healthy"; case C_DEAD: return "dead"; } return "?unknown condition?"; } object_t *getouterequippedob(lifeform_t *lf, enum BODYPART bp) { object_t *o,*poss[MAXCANDIDATES]; enum BODYPART where = bp; int i,nposs = 0; switch (bp) { case BP_RIGHTFINGER: case BP_LEFTFINGER: if (getequippedob(lf->pack, BP_HANDS)) { where = BP_HANDS; } break; case BP_BODY: if (getequippedob(lf->pack, BP_SHOULDERS)) { where = BP_SHOULDERS; } break; default: break; } // get all obs equipped here. for (o = lf->pack->first ; o ; o = o->next) { if (hasflagval(o->flags, F_EQUIPPED, where, NA, NA, NULL)) { poss[nposs++] = o; } } if (!nposs) { return NULL; } else if (nposs == 1) { return poss[0]; } // return the OUTER one. for (i = 0;i < nposs; i++) { if (!hasflag(poss[i]->flags, F_UNDERCLOTHING)) { return poss[i]; } } // should never get here. assert("bug in getouterequippedob()." == 0); return NULL; } /* int getowing(lifeform_t *buyer, int shopid, int *retnitems) { object_t *o; flag_t *f; int totcost = 0; int nitems = 0; for (o = buyer->pack->first ; o ; o = o->next) { f = hasflagval(o->flags, F_SHOPITEM, NA, shopid, NA, NULL); if (f) { totcost += f->val[0]; nitems++; } } if (retnitems) { *retnitems = nitems; } return totcost; } */ // return the healthiest possible hurt condition that 'lf' will // recognise when looking at someone else. // // this is based on intelligence and firstaid skill enum LFCONDITION getseenlfconditioncutoff(lifeform_t *lf) { enum ATTRBRACKET iqb; enum SKILLLEVEL slev; enum LFCONDITION cutoff; // intelligence higher than 'average' doesn't count. iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); if (iqb > AT_AVERAGE) iqb = AT_AVERAGE; // adjust for firstaid skill slev = getskill(lf, SK_FIRSTAID); iqb += slev; // figure out health cutoff - condition > cutoff gets no description if (iqb >= AT_VHIGH) { cutoff = C_HEALTHY; // } else if (iqb >= AT_AVERAGE) { cutoff = C_HURT; // ie. no real cutoff } else if (iqb >= AT_LTAVERAGE) { cutoff = C_WOUNDED; } else if (iqb >= AT_VLOW) { cutoff = C_SERIOUS; } else { cutoff = C_DEAD; } return cutoff; } char *getseenlfconditionname(lifeform_t *lf, lifeform_t *viewer) { enum LFCONDITION cond,cutoff; cutoff = getseenlfconditioncutoff(viewer); cond = getlfcondition(lf); if (cond > cutoff) { return ""; } return getlfconditionname(cond); } int getsmellrange(lifeform_t *lf) { flag_t *f; int range = 0; f = lfhasflag(lf, F_ENHANCESMELL); if (f) { range = f->val[0]; // adjust for injuries if (lfhasflagval(lf, F_INJURY, IJ_NOSEBROKEN, NA, NA, NULL)) { range /= 2; } } return range; } flag_t *getspellcasttextflag(lifeform_t *lf, enum OBTYPE sid) { flag_t *f; flag_t *retflag[MAXCANDIDATES],*genposs[MAXCANDIDATES],*specposs[MAXCANDIDATES]; int nretflags,i,ngenposs = 0,nspecposs = 0; getflags(lf->flags, retflag, &nretflags, F_SPELLCASTTEXT, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[0] == sid ) { specposs[nspecposs++] = retflag[i]; } else if (retflag[i]->val[0] == OT_NONE) { genposs[ngenposs++] = retflag[i]; } } if (nspecposs) { f = specposs[rnd(0,nspecposs-1)]; } else if (ngenposs) { f = genposs[rnd(0,ngenposs-1)]; } else { f = NULL; } return f; } glyph_t *getlfglyph(lifeform_t *lf) { flag_t *f; if (lfhasflag(lf, F_FEIGNINGDEATH)) { // look like a corpse tempglyph.ch = '%'; tempglyph.colour = lf->race->glyph.colour; } else if ((f = lfhasflag(lf, F_GLYPH)) != NULL) { tempglyph.ch = f->val[1]; tempglyph.colour = f->val[0]; } else if ((f = lfhasflag(lf, F_SIZETIMER)) != NULL) { int newchar; newchar = lf->race->glyph.ch; // made larger? if (getlfsize(lf) > f->val[0]) { switch (lf->race->glyph.ch) { case 'n': case 'h': case '@': newchar = 'H'; break; case 'g': newchar = 'G'; break; case 'i': newchar = 'I'; break; case 'q': newchar = 'Q'; break; case 'w': newchar = 'W'; break; default: break; } } tempglyph.ch = newchar; tempglyph.colour = lf->race->glyph.colour; } else { tempglyph = lf->race->glyph; } if (lf->cell && (getcellwaterdepth(lf->cell, player) >= DP_WAIST)) { object_t *o; int bgcol = 0; o = hasobwithflag(lf->cell->obpile, F_DEEPWATER); switch (o->type->material->id) { case MT_WATER: bgcol = BLUEBG; break; case MT_SLIME: bgcol = GREENBG; break; default: break; } if (bgcol) { // avoid having the same foreground and background colour int fixfg = B_FALSE; if ((bgcol == BLUEBG) && (tempglyph.colour == C_BLUE)) { fixfg = B_TRUE; } else if ((bgcol == GREENBG) && (tempglyph.colour == C_GREEN)) { fixfg = B_TRUE; } if (fixfg) tempglyph.colour = C_BLACK; tempglyph.colour += bgcol; } } //return &lf->race->glyph; return &tempglyph; } enum MATERIAL getlfmaterial(lifeform_t *lf) { if (lf->race->baseid == R_DANCINGWEAPON) { object_t *wep; wep = getweapon(lf); if (wep) { return wep->material->id; } } return lf->material->id; } enum SKILLLEVEL getlorelevel(lifeform_t *lf, enum RACECLASS rcid) { enum SKILLLEVEL slev = PR_INEPT; raceclass_t *rc; if (gamemode <= GM_LOADING) { return PR_INEPT; } rc = findraceclass(rcid); if (rc) { slev = getskill(lf, rc->skill); } return slev; } int getattacks(lifeform_t *lf, int *min, int *max) { flag_t *f; int minattacks = 1,maxattacks = 1; int nattacks; f = lfhasflag(lf, F_MAXATTACKS); if (f) { minattacks = f->val[0]; maxattacks = f->val[1]; } else { minattacks = countinnateattacks(lf); maxattacks = countinnateattacks(lf); } // if we have high unarmed skill and our second hand is free, // AND we only had 1 attack before, we get one more attack if (maxattacks < 2) { if (lfhasflag(lf, F_HUMANOID)) { if (getskill(lf, SK_UNARMED) >= PR_SKILLED) { if (hasbp(lf, BP_SECWEAPON) && !getequippedob(lf->pack, BP_SECWEAPON)) { maxattacks++; } } } } if (getskill(lf, SK_TWOWEAPON) && isdualweilding(lf)) { minattacks++; maxattacks++; } if (lfhasflag(lf, F_SWARM)) { int pct,origmin; pct = gethppct(lf); origmin = minattacks; minattacks = pctof(pct,minattacks); maxattacks = pctof(pct,maxattacks); if (origmin >= 1) { limit(&minattacks, 1, NA); } limit(&maxattacks, minattacks, NA); } if (min) *min = minattacks; if (max) *max = maxattacks; // if we moved last turn, get get the minimum attack amount. // otherwise we get a randmo number between 2 and the max. if (lfhasflag(lf, F_MOVED)) { nattacks = minattacks; } else { nattacks = maxattacks; } //nattacks = rnd(minattacks,maxattacks); return nattacks; } float getmaxcarryweight(lifeform_t *lf) { float max; /* sbrack = getattrbracket(getattr(lf, A_STR), A_STR, NULL); switch (sbrack) { case AT_EXLOW: mod = 0.1; break; case AT_VLOW: mod = 0.25; break; case AT_LOW: mod = 0.5; break; case AT_LTAVERAGE: mod = 0.75; break; case AT_AVERAGE: mod = 1; break; // your body weight case AT_GTAVERAGE: mod = 1.25; break; case AT_HIGH: mod = 1.5; break; case AT_VHIGH: mod = 2; break; // twice your own body weight case AT_EXHIGH: mod = 2.25; break; // over twice default: mod = 1; break; // your body weight } max = getlfweight(lf, B_NOOBS) * mod; */ // half your weight + your strength max = (getlfweight(lf, B_NOOBS)/2) + getattr(lf, A_STR); if (lfhasflagval(lf, F_INJURY, IJ_RIBBROKEN, NA, NA, NULL)) { max /= 2; } else if (lfhasflagval(lf, F_INJURY, IJ_RIBCRACKED, NA, NA, NULL)) { max /= 2; } limitf(&max, 0, NA); return max; } int getmaxmp(lifeform_t *lf) { flag_t *f; int activemp = 0; int maxmp,i; int extrapct = 0; flag_t *retflag[MAXCANDIDATES]; int nretflags; // base maxmp = lf->maxmp; // extras sumflags(lf->flags, F_EXTRAMP, &extrapct, NULL, NULL); maxmp += pctof(maxmp, extrapct); // losses activemp = 0; getflags(lf->flags, retflag, &nretflags, F_BOOSTSPELL, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_BOOSTSPELL) { activemp += f->val[1]; } } maxmp -= activemp; //if (maxmp < 0) maxmp = 0; // ???? return maxmp; } float getmaxpushweight(lifeform_t *lf) { float max; int pct; pct = 100 + getstatmod(lf, A_STR); max = getlfweight(lf, B_NOOBS); // your body weight max = pctof(pct, max); return max; } float getmaxstamina(lifeform_t *lf) { int stam = 0; int slev; int boostamt = 0; slev = getskill(lf, SK_ATHLETICS); limit(&slev, 1, NA); stam = (getattr(lf, A_CON) / 15) * (slev+1); sumflags(lf->flags, F_STAMBOOST, &boostamt, NULL, NULL); stam += boostamt; if (lfhasflagval(lf, F_INJURY, IJ_LUNGCOLLAPSED, NA, NA, NULL)) { limit(&stam, NA, 2); } return stam; } object_t *getmeleeweapon(lifeform_t *lf) { object_t *o; o = getweapon(lf); if (o && ismeleeweapon(o)) { return o; } return NULL; } int getmr(lifeform_t *lf) { return real_getmr(lf, B_FALSE); } int real_getmr(lifeform_t *lf, int onlyexternal) { int amt = 0; if (onlyexternal) { flag_t *f; // sum all MR flags which DONT come from race/job for (f = hasflag(lf->flags, F_RESISTMAG) ; f && (f->id == F_RESISTMAG) ; f = f->next) { if ((f->lifetime != FROMRACE) && (f->lifetime != FROMJOB)) { amt += f->val[0]; } } } else { // sum all MR flags sumflags(lf->flags, F_RESISTMAG, &amt, NULL, NULL); // scourge gets job-based bonus if (hasjob(lf, J_SCOURGE)) { amt += (gettr(lf) * 3); } } return amt; } int gettempaccpenalty(lifeform_t *lf, int temperature) { switch (temperature) { case T_CHILLY: return (getexposedlimbs(lf)*2); break; case T_COLD: return (getexposedlimbs(lf)*3); break; case T_VCOLD: return (getexposedlimbs(lf)*4); break; default: break; } return 0; } int gettempstammod(lifeform_t *lf, int temperature) { switch (temperature) { case T_WARM: return 150; case T_HOT: return 200; case T_VHOT: return 250; default: break; } return 100; } // get maximum vision range for a lf int getvisrange(lifeform_t *lf, int useambient) { int range,i; flag_t *f; flag_t *retflag[MAXCANDIDATES]; int nretflags; if (isblind(lf)) { return 0; } f = lfhasflag(lf, F_VISRANGE); if (f) { range = f->val[0]; } else { range = DEF_VISRANGE; } if (useambient) { if ((gamemode == GM_GAMESTARTED) && (lf->cell)) { int maxrange; maxrange = getmapmaxvisrange(lf->cell->map); limit(&range, NA, MAXOF(maxrange, getnightvisrange(lf))); } } // positive modifiers getflags(lf->flags, retflag, &nretflags, F_VISRANGEMOD, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; range += f->val[0]; } //negative modifiers getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { switch (retflag[i]->val[0]) { case IJ_BLACKEYE: range /= 2; break; } } if (isprone(lf) && (range >= 2)) { // can't see as far if you're on the ground range /= 2; } if (lfhasflag(lf, F_FULLSHIELD)) { if (range > 1) range = 1; } limit(&range, 0, MAXVISRANGE); return range; } int getmovespeed(lifeform_t *lf) { int speed = 0,i; flag_t *f; object_t *o; flag_t *retflag[MAXCANDIDATES]; int nretflags; f = lfhasflag(lf, F_MOVESPEED); if (f) { speed = f->val[0]; } else { speed = SPEED_MOVE; // default } // modifier? getflags(lf->flags, retflag, &nretflags, F_FASTMOVE, F_FASTACTMOVE, F_HIDING, F_INJURY, F_SLOWMOVE, F_SLOWACTMOVE, F_SPRINTING, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->id == F_SLOWMOVE) || (f->id == F_SLOWACTMOVE)) { speed += f->val[0]; } else if ((f->id == F_FASTMOVE) || (f->id == F_FASTACTMOVE)) { speed -= f->val[0]; } else if (f->id == F_HIDING) { speed += 10; } else if (f->id == F_INJURY) { if (!isairborne(lf, NULL)) { switch (f->val[0]) { case IJ_LEGBROKEN: case IJ_HAMSTRUNG: speed += 10; break; case IJ_LEGBRUISE: speed += 5; break; } } } else if (f->id == F_SPRINTING) { if (!isairborne(lf, NULL)) { speed -= 10; } } } // flying in low gravity? f = lfhasflag(lf, F_FLYING); if (f) { if (lfhasflag(lf, F_GRAVLESSENED)) { speed -= 5; } if ((f->lifetime == FROMRACE) && lfhasflagval(lf, F_INJURY, IJ_WINGBRUISED, NA, NA, NULL)) { speed += 10; } } switch (isburdened(lf)) { case BR_NONE: break; case BR_BURDENED: speed += 5; break; case BR_STRAINED: case BR_OVERLOADED: speed += 10; break; } // caseful walking if (lfhasflag(lf, F_CAREFULMOVE)) { speed *= 2; } if (speed < 1) speed = 1; // reducemovement flags? o = hasobwithflag(lf->cell->obpile, F_REDUCEMOVEMENT); if (o) { f = hasflag(o->flags, F_REDUCEMOVEMENT); speed += (f->val[0] * SPEEDUNIT); } // water adjustspeedforwater(lf, &speed); return speed; } char *getmoveverb(lifeform_t *lf) { flag_t *f; if (lfhasflag(lf, F_CLIMBING)) { return "climb"; } if (lfhasflag(lf, F_FLYING)) { return "fly"; } else if (lfhasflag(lf, F_LEVITATING)) { return "float"; } if (lfhasflag(lf, F_FLEEFROM)) { return "flee"; } if (isswimming(lf)) { return "swim"; } f = lfhasflag(lf, F_WALKVERB); if (f) { return f->text; } return "walk"; } char *getmoveverbother(lifeform_t *lf, char *buf) { flag_t *f; if (lfhasflag(lf, F_CLIMBING)) { strcpy(buf, "climbs"); return buf; } if (lfhasflag(lf, F_FLYING)) { strcpy(buf, "flies"); return buf; } else if (lfhasflag(lf, F_LEVITATING)) { strcpy(buf, "floats"); return buf; } if (lfhasflag(lf, F_FLEEFROM)) { strcpy(buf, "flees"); return buf; } if (isswimming(lf)) { strcpy(buf, "swims"); return buf; } f = lfhasflag(lf, F_WALKVERB); if (f) { snprintf(buf, BUFLEN, "%ss", f->text); return buf; } strcpy(buf, "walks"); return buf; } lifeform_t *getnearbypeaceful(lifeform_t *lf) { int i; lifeform_t *poss[MAXCANDIDATES]; lifeform_t *l; int nposs = 0; // peaceful enemy in los ? for (i = 0; i < lf->nlos; i++) { if (lf->los[i] != lf->cell) { l = lf->los[i]->lf; if (l && (getallegiance(l) == AL_PEACEFUL)) { poss[nposs] = l; } } } if (nposs) { return poss[rnd(0,nposs-1)]; } return NULL; } char *getpitverb(lifeform_t *lf, int dir, int onpurpose, int climb) { if (lfhasflag(lf, F_FLYING)) { if (isplayer(lf)) return "fly"; else return "flies"; } else if (onpurpose) { if (dir == D_DOWN) { if (climb) { if (isplayer(lf)) return "climb"; else return "climbs"; } else { if (isplayer(lf)) return "jump"; else return "jumps"; } } else { if (isplayer(lf)) return "climb"; else return "climbs"; } } else { if (dir == D_DOWN) { if (isplayer(lf)) return "fall"; else return "falls"; } else { if (isplayer(lf)) return "rise"; else return "rises"; } } return "?unkonwnmoveverb?"; } char *getlfname(lifeform_t *lf, char *buf) { return real_getlfname(lf, buf, player, B_NOSHOWALL, B_CURRACE); } char *real_getlfname(lifeform_t *lf, char *buf, lifeform_t *usevis, int showall, int useorigrace) { char descstring[BUFLEN]; char jobstring[BUFLEN]; char the[6]; char lname[BUFLEN]; race_t *lfrace = NULL; flag_t *f; enum LFSIZE size,racesize; int dobehaviour = B_TRUE; enum SKILLLEVEL lorelev; if (usevis && (gamemode != GM_GAMESTARTED)) { usevis = NULL; } if (ispolymorphed(lf) && useorigrace) { f = lfhasflag(lf, F_ORIGRACE); if (f) { lfrace = findrace(f->val[0]); } } if (!lfrace) { if (lfhasflag(lf, F_LYCANTHROPE) && !ispolymorphed(lf) && !lfhasflag(lf, F_NAME)) { // lycanthropes in human form appear as human unless you know better if ((getlorelevel(player, RC_HUMANOID) >= PR_ADEPT) || (getlorelevel(player, RC_MAGIC) >= PR_BEGINNER)) { lfrace = lf->race; } else { lfrace = findrace(R_HUMAN); } } else if (lf->race->id == R_ANDROID) { if ((getlorelevel(player, RC_HUMANOID) >= PR_BEGINNER) || (getlorelevel(player, RC_ROBOT) >= PR_BEGINNER)) { lfrace = lf->race; } else { lfrace = findrace(R_HUMAN); } } else { lfrace = lf->race; } } if (gamemode == GM_GAMESTARTED) { lorelev = getlorelevel(player, lfrace->raceclass->id); } else { lorelev = PR_MASTER; } f = hasname(lf); if (f) { strcpy(the, ""); strcpy(lname, f->text); } else { // 'the' or 'your' ? f = lfhasflag(lf, F_NAME); if (f) { strcpy(the, ""); strcpy(lname, f->text); } else if (lfhasflag(lf, F_UNIQUE)) { strcpy(the, ""); strcpy(lname, lfrace->name); } else { if (ispetof(lf, player)) { strcpy(the, "your "); } else { strcpy(the, "the "); } f = lfhasflag(lf, F_NAMED); if (f) { // ie. "the xat named blah" strcpy(lname, lfrace->name); strcat(lname, " named "); strcat(lname, f->text); } else { // ie. "the xat" strcpy(lname, lfrace->name); } } } // construct description string strcpy(descstring, ""); // are they larger or smaller than they should be? f = hasflag(lfrace->flags, F_SIZE); if (f) { racesize = f->val[0]; } else { racesize = SZ_HUMAN; // default } size = getlfsize(lf); if (!useorigrace && (size != racesize)) { strcat(descstring, getsizetext(size)); strcat(descstring, " "); } if (lf->race->id == R_HYDRA) { int nheads; char numbuf[BUFLEN]; nheads = countflagsofid(lf->flags, F_HASATTACK); numtotext(nheads, numbuf); strcat(descstring, numbuf); strcat(descstring, "-headed "); } // need a certain amount of race knowledge to recognise ai traits if (lorelev < PR_SKILLED) { dobehaviour = B_FALSE; } // frozen/headless trump behavioural descriptions like "insane" if (lfhasflag(lf, F_FROZEN)) { strcat(descstring, "frozen "); dobehaviour = B_FALSE; } if (lfhasflag(lf, F_HEADLESS)) { strcat(descstring, "headless "); dobehaviour = B_FALSE; } if (!isplayer(lf) && isexhausted(lf) && cansee(player, lf)) { if (lorelev >= PR_NOVICE) { strcat(descstring, "exhausted "); dobehaviour = B_FALSE; } } if (dobehaviour) { f = lfhasflag(lf, F_BEHAVIOUR); if (f) { behaviour_t *b; b = findbehaviour(f->val[0]); if (b) { strcat(descstring, b->name); strcat(descstring, " "); } } } // construct job string strcpy(jobstring, ""); if (showall || isplayer(lf) || (lorelev >= PR_BEGINNER)) { if (!lfhasflag(lf, F_NOJOBTEXT) && !lfhasflag(lf, F_NAME) && !lfhasflag(lf, F_UNIQUE)) { if (getjob(lf)) { snprintf(jobstring, BUFLEN, " %s", getjobname(lf)); jobstring[1] = tolower(jobstring[1]); } } } if (isplayer(lf)) { snprintf(buf, BUFLEN, "you"); } else { //if (isblind(player)) { if (usevis && !cansee(usevis, lf) && !showall) { snprintf(buf, BUFLEN, "something"); } else { if (lf->race->baseid == R_DANCINGWEAPON) { object_t *wep; wep = getweapon(lf); if (wep) { char obname[BUFLEN]; real_getobname(wep, obname, 1, B_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); snprintf(buf, BUFLEN, "%s%s%s",the,descstring,noprefix(obname)); } else { snprintf(buf, BUFLEN, "%s%s%s%s",the,descstring,lname,jobstring); } } else if (lfhasflag(lf, F_FEIGNINGDEATH)) { if (lfhasflag(lf, F_NAME)) { // ie. "jimbo's corpse" snprintf(buf, BUFLEN, "%s%s corpse", lname, getpossessive(lname)); } else { // ie. "a wolf corpse" snprintf(buf, BUFLEN, "%s %s corpse", needan(lname) ? "an" : "a", lname); } } else if (lfhasflag(lf, F_NAME)) { snprintf(buf, BUFLEN, "%s%s", descstring, lname); } else { char zombiestring[BUFLEN]; f = hasflag(lf->flags, F_LFSUFFIX); strcpy(zombiestring, ""); if (f) { snprintf(zombiestring, BUFLEN, " %s", f->text); } snprintf(buf, BUFLEN, "%s%s%s%s%s",the,descstring,lname,jobstring,zombiestring); } } } return buf; } char *getlfnamea(lifeform_t *lf, char *buf) { return real_getlfnamea(lf, buf, player, B_NOSHOWALL, B_CURRACE); } char *real_getlfnamea(lifeform_t *lf, char *buf, lifeform_t * usevis, int showall, int useorigrace) { race_t *lfrace = NULL; enum SKILLLEVEL lorelev; flag_t *f; if (usevis && (gamemode != GM_GAMESTARTED)) { usevis = NULL; } if (ispolymorphed(lf) && useorigrace) { f = lfhasflag(lf, F_ORIGRACE); if (f) { lfrace = findrace(f->val[0]); } } if (!lfrace) { if (lfhasflag(lf, F_LYCANTHROPE) && !ispolymorphed(lf)) { // lycanthropes in human form appear as human unless you know better if (getlorelevel(player, RC_HUMANOID) >= PR_ADEPT) { lfrace = lf->race; } else { lfrace = findrace(R_HUMAN); } } else { lfrace = lf->race; } } if (gamemode == GM_GAMESTARTED) { lorelev = getlorelevel(player, lfrace->raceclass->id); } else { lorelev = PR_MASTER; } if (isplayer(lf)) { snprintf(buf, BUFLEN, "you"); } else { char buf2[BUFLEN]; char the[6]; real_getlfname(lf, buf2, usevis, showall, useorigrace); if (hasname(lf) || lfhasflag(lf, F_UNIQUE)) { strcpy(the, ""); } else { if (ispetof(lf, player)) { strcpy(the, "your "); } else { if (isvowel(lfrace->name[0])) { strcpy(the, "an "); } else { strcpy(the, "a "); } } } snprintf(buf, BUFLEN, "%s%s", the, noprefix(buf2)); } return buf; } enum LFSIZE getlfsize(lifeform_t *lf) { flag_t *f = NULL; enum LFSIZE size = SZ_HUMAN; f = hasflag(lf->flags, F_SIZE); if (f) { size = f->val[0]; } else { size = getracesize(lf->race->id); } return size; } float getlfweight(lifeform_t *lf, int withobs) { float weight = 0; weight = lf->race->mass; if (lfhasflag(lf, F_OBESE)) { weight *= 2; } if (withobs) { weight += getobpileweight(lf->pack); } return weight; } object_t *getsecmeleeweapon(lifeform_t *lf) { object_t *o; o = getequippedob(lf->pack, BP_SECWEAPON); if (o && ismeleeweapon(o)) { return o; } return NULL; } // returns the best shield for the given damtype object_t *getshield(lifeform_t *lf, enum DAMTYPE dt) { object_t *shield[MAXPILEOBS]; int checkmod[MAXPILEOBS]; int nshields,i; object_t *bestshield = NULL; int bestcheckmod = -99; getallshields(lf, dt, shield, checkmod, &nshields); for (i = 0; i < nshields; i++) { if (checkmod[i] > bestcheckmod) { bestcheckmod = checkmod[i]; bestshield = shield[i]; } } return bestshield; } int getallshields(lifeform_t *lf, enum DAMTYPE damtype, object_t **retob, int *checkmod, int *nretobs) { object_t *o; *nretobs = 0; if (!hasfreeaction(lf)) { return 0; } for (o = lf->pack->first ; o ; o = o->next) { flag_t *f; f = isequipped(o); if (f && meetsallattreqs(lf, o)) { int isblockob = B_FALSE; if (isshield(o) && (f->val[0] == BP_SECWEAPON)) { // shield? isblockob = B_TRUE; } else if (isweapon(o) && (getweaponskill(lf, o) >= PR_SKILLED) && (damtype != DT_PROJECTILE)) { if (f->val[0] == BP_WEAPON) { // primary weapon which we are skilled in? isblockob = B_TRUE; } else if ((f->val[0] == BP_SECWEAPON) && (getskill(lf, SK_TWOWEAPON) >= PR_MASTER)) { // secondary weapon which we are skilled in, // and we are a master two-weaponer ? isblockob = B_TRUE; } } if (isblockob) { // can it block this damage type? if ((damtype == DT_ALL) || hasflagval(o->flags, F_CANBLOCK, damtype, NA, NA, NULL) || hasflagval(o->flags, F_CANBLOCK, DT_ALL, NA, NA, NULL)) { retob[*nretobs] = o; if (checkmod) checkmod[*nretobs] = getshieldblockmod(lf, o); (*nretobs)++; } } } } return *nretobs; } int getshieldblockmod(lifeform_t *lf, object_t *o) { flag_t *f; enum SKILLLEVEL slev = PR_INEPT; int othermod = 0; if (isshield(o)) { slev = getskill(lf, SK_SHIELDS); } else { // using a weapon // skilled weapon == beginner block // expert weapon == adept block // master weapon == skilled block slev = getweaponskill(lf, o) - 2; } switch (slev) { /* case PR_NOVICE: othermod = 0; break; case PR_BEGINNER: othermod = 4; break; case PR_ADEPT: othermod = 7; break; case PR_SKILLED: othermod = 10; break; case PR_EXPERT: othermod = 13; break; case PR_MASTER: othermod = 16; break; */ case PR_NOVICE: othermod = 0; break; case PR_BEGINNER: othermod = 5; break; case PR_ADEPT: othermod = 10; break; case PR_SKILLED: othermod = 15; break; case PR_EXPERT: othermod = 20; break; case PR_MASTER: othermod = 25; break; default: // should never happen othermod = -15; break; } // now modify for shield type f = hasflag(o->flags, F_SHIELD); if (f) { othermod += f->val[0]; } return othermod; } int getspellspeed(lifeform_t *lf) { int speed = 0; flag_t *f; f = lfhasflag(lf, F_SPELLSPEED); if (f) { speed = f->val[0]; } else { f = lfhasflag(lf, F_MOVESPEED); if (f) { speed = f->val[0]; } else { speed = SPEED_MOVE; // default } } // don't use movement speed modifier! return speed; } // note: stamina is stored as a float, but we treat it as an int. int getstamina(lifeform_t *lf) { return (int)floor(lf->stamina); } int getstaminapct(lifeform_t *lf) { float max; if (lfhasflag(lf, F_NOSTAM)) return 100; max = getmaxstamina(lf); if ((max == 0) || lfhasflag(lf, F_NOSTAM)) return 100; return ((float)lf->stamina / max) * 100.0; } float getstamregen(lifeform_t *lf) { float regenrate = STAMREGEN; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; // flying with wings? if (isflyingwithwings(lf)) return 0; if (lfhasflag(lf, F_TRAINING)) { // don't regain stamina while training! return 0; } else if (lfhasflagval(lf, F_INJURY, IJ_WINDPIPECRUSHED, NA, NA, NULL)) { regenrate = 0.2; // override everything else } else { if (lfhasflag(lf, F_ASLEEP)) { regenrate = pctof(200, regenrate); } if (ispoisoned(lf)) { regenrate = pctof(50, regenrate); } } getflags(lf->flags, retflag, &nretflags, F_STAMREGEN, F_FLYING, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->id == F_STAMREGEN) { regenrate += atof(retflag[i]->text); } } limitf(®enrate, 0, NA); return regenrate; } char *getplayername(char *buf) { flag_t *f; if (!player) { strcpy(buf, ""); } else { f = hasflag(player->flags, F_NAME); if (f) { strcpy(buf, f->text); } else{ // should never happen! strcpy(buf, ""); } } return buf; } char *getplayernamefull(char *buf) { char pname[BUFLEN]; getplayername(pname); if (strlen(pname)) { if (getjob(player)) { snprintf(buf, BUFLEN, "%s the %s %s", pname, player->race->name, getjobname(player)); } else { snprintf(buf, BUFLEN, "%s the %s", pname, player->race->name); } } return buf; } enum RACECLASS getraceclass(lifeform_t *lf) { return lf->race->raceclass->id; } // returns rarity number. if optional rr is passed, this is will be returned too. // if 'hab' isn't H_ALL, get rarity for this habitat. int getracerarity(enum HABITAT hab, enum RACE rid, enum RARITY *rr) { race_t *r; int rarity = -1; // default if (rr) *rr = RR_NEVER; r = findrace(rid); if (r) { flag_t *f = NULL; if (hasflag(r->flags, F_UNIQUE)) { if (rr) *rr = RR_UNIQUE; } if (hab != H_ALL) { f = hasflagval(r->flags, F_RARITY, hab, NA, NA, NULL); if (!f) { f = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL); } } else { // just take the FIRST rarity flag. f = hasflag(r->flags, F_RARITY); } /*if (!f) { f = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL); }*/ if (f) { // ignore habitat for now! rarity = f->val[1]; if (rr) *rr = f->val[2]; } } return rarity; } enum LFSIZE getracesize(enum RACE rid) { race_t *r = NULL; flag_t *f = NULL; enum LFSIZE size = SZ_HUMAN; r = findrace(rid); if (r) { f = hasflag(r->flags, F_SIZE); if (f) { size = f->val[0]; } } return size; } // if optional 'attacker' is provided, only select form armours which they // can reach. object_t *getrandomarmour(lifeform_t *lf, lifeform_t *attacker) { object_t *o; object_t *poss[MAXBODYPARTS]; object_t **hitposition; int hitchance[MAXBODYPARTS]; enum BODYPART hitbp[MAXBODYPARTS]; int nposs = 0; int maxroll = 0; int i,n,idx; int sel; // make a list of all valid armour getarmourrating(lf, poss, hitchance, hitbp, &nposs); if (!nposs) return NULL; maxroll = 0; for (i = 0; i < nposs; i++) { if (!attacker || canreachbp(attacker, lf, hitbp[i])) { maxroll += hitchance[i]; } } if (maxroll == 0) { return NULL; } // now figure out chances of each one getting hit hitposition = malloc(maxroll * sizeof(object_t *)); idx = 0; for (i = 0; i < nposs; i++) { if (!attacker || canreachbp(attacker, lf, hitbp[i])) { for (n = 0; n < hitchance[i]; n++) { hitposition[idx] = poss[i]; idx++; } } } sel = rnd(0, maxroll-1); o = hitposition[sel]; free(hitposition); return o; } enum BEHAVIOUR getrandombehaviour(void) { behaviour_t *b; int numb = 0,sel; enum BEHAVIOUR selb = BH_NONE; for (b = firstbehaviour ; b ; b = b->next) { numb++; } sel = rnd(0,numb-1); numb = 0; for (b = firstbehaviour ; b ; b = b->next) { if (numb == sel) { selb = b->id; break; } numb++; } if (selb == BH_NONE) { // should never happen! assert("getrandombehaviour() failed!" == 0); } return selb; } // pick a random major body part // ie. head, body, arms/hands, legs, wings, tail enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker) { int cutoff[MAXBODYPARTS],nparts = 0,i,max = 0,num; enum BODYPART bp[MAXBODYPARTS],selbp = BP_NONE; if (hasbp(lf, BP_BODY)) { if (attacker && !canreachbp(attacker, lf, BP_BODY)) { } else { bp[nparts++] = BP_BODY; } } if (hasbp(lf, BP_HANDS)) { if (attacker && !canreachbp(attacker, lf, BP_HANDS)) { } else { bp[nparts++] = BP_HANDS; } } if (hasbp(lf, BP_HEAD)) { if (attacker && !canreachbp(attacker, lf, BP_HEAD)) { } else { bp[nparts++] = BP_HEAD; } } if (hasbp(lf, BP_LEGS) || hasbp(lf, BP_FRONTLEGS) || hasbp(lf, BP_BACKLEGS)) { if (attacker && !canreachbp(attacker, lf, BP_LEGS)) { } else { bp[nparts++] = BP_LEGS; } } if (hasbp(lf, BP_WINGS)) { if (attacker && !canreachbp(attacker, lf, BP_WINGS)) { } else { bp[nparts++] = BP_WINGS; } } if (hasbp(lf, BP_TAIL)) { if (attacker && !canreachbp(attacker, lf, BP_TAIL)) { } else { bp[nparts++] = BP_TAIL; } } if (!nparts) { return BP_NONE; } for (i = 0;i < nparts; i++) { if (i == 0) { cutoff[i] = getbodyparthitchance(bp[i]); } else { cutoff[i] = cutoff[i-1] + getbodyparthitchance(bp[i]); } } max = cutoff[nparts-1]; num = rnd(1,max); for (i = 0;i < nparts; i++) { if (num <= cutoff[i]) { selbp = bp[i]; break; } } if (selbp == BP_NONE) { dblog("error in getrandomcorebodypart!"); msg("error in getrandomcorebodypart!"); } return selbp; } race_t *getrandomcorpserace(cell_t *c, enum LFSIZE wantsize) { condset_t cs; initcondv(&cs, CC_HASCORPSE, B_TRUE, NA, CC_HASFLAG, B_FALSE, F_UNIQUE, CC_NONE); if (wantsize != SZ_ANY) { addcond(&cs, CC_HASSIZE, B_TRUE, wantsize); } return getrandomrace(c, NA, &cs); } job_t *getrandomjob(int onlyplayerjobs) { job_t *j; int njobs = 0; int sel,n; // count them for (j = firstjob ; j ; j = j->next) { if (onlyplayerjobs && hasflag(j->flags, F_NOPLAYER)) { } else if (j->id == J_GOD) { } else { njobs++; } } if (njobs) { sel = rnd(0,njobs-1); n = 0; for (j = firstjob ; j ; j = j->next) { if (onlyplayerjobs && hasflag(j->flags, F_NOPLAYER)) { } else if (j->id == J_GOD) { } else { if (n == sel) return j; n++; } } } return NULL; } int getrandommonlevel(race_t *r, map_t *m) { flag_t *f; int wantlev = 1; f = hasflag(r->flags, F_VARLEVEL); if (f) { wantlev = rnd(1, getmapdifficulty(m)); if (f->val[0] > 0) { limit(&wantlev, 1, f->val[0]); } } return wantlev; } race_t *getrandomraceofsize(enum LFSIZE wantsize) { condset_t cs; initcondv(&cs, CC_HASSIZE, B_TRUE, wantsize, CC_NONE); return getrandomrace(NULL, NA, &cs); } race_t *getrandomrace(cell_t *c, int forcedepth, condset_t *cs) { //int rarity; race_t *r; race_t **poss; int nposs = 0; int selidx; int db = B_FALSE; int depth; int hdmin,hdmax; enum RARITY wantrr = RR_FREQUENT; poss = calloc(totalraces, sizeof(race_t *)); // determine rarity of lf to generate if (forcedepth != NA) { depth = forcedepth; } else if (c) { depth = getmapdifficulty((c && c->map) ? c->map : NULL); } else { //depth = rnd(1,MAXDEPTH); depth = MAXDEPTH; } gettrrange(depth, &hdmin, &hdmax, RARITYVARIANCELF, B_TRUE); // pick rr... wantrr = pickrr(TT_MONSTER, NA); if (db) dblog("finding random lf with hitdice %d-%d and rr <= %d (for depth %d)\n",hdmin,hdmax, wantrr, depth); // try to find a lf of this type which will // fit in the map's habitat nposs = 0; while (nposs == 0) { for (r = firstrace ; r ; r = r->next) { int valid = B_FALSE; int thishd; enum RARITY thisrr = RR_NEVER; flag_t *rarflag = NULL; thishd = gettrrace(r); if ((thishd < hdmin) || (thishd > hdmax)) { valid = B_FALSE; } else { if (db) dblog("%s hitdice %d ok (between %d - %d)",r->name,thishd,hdmin,hdmax); // correct rarity? if (c) { rarflag = hasflagval(r->flags, F_RARITY, c->habitat->id, NA, NA, NULL); if (!rarflag) { rarflag = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL); } } else { rarflag = hasflag(r->flags, F_RARITY); } /*if (!rarflag) { rarflag = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL); }*/ if (rarflag) { if ((rarflag->val[2] == NA) || (rarflag->val[2] <= wantrr)) { if (db) dblog("%s has correct rarity rr (%d <= wantrr(%d)).",r->name, rarflag->val[2], wantrr); valid = B_TRUE; thisrr = rarflag->val[2]; } else { if (db) dblog("%s has wrong rr (%d, wantrr=%d).",r->name, rarflag->val[2], wantrr); } } else { if (db) dblog("%s has no rarity flag.",r->name); valid = B_FALSE; } } if (valid && !appearsrandomly(r->id)) { valid = B_FALSE; } if (valid && cs) { if (!racemeets(r->id, cs)) { if (db) dblog("%s does not match given conditions",r->name); valid = B_FALSE; } } if (valid && c) { // can it go into the cell? if (hasobwithflag(c->obpile, F_DEEPWATER)) { // water if (!hasflag(r->flags, F_AQUATIC) && (r->raceclass->id != RC_AQUATIC)) { // not aquatic valid = B_FALSE; } } else { // no water if (hasflag(r->flags, F_NEEDSWATER)) { valid = B_FALSE; } } } if (valid) { if (db) dblog("-> possibility: %s, hitdice=%d, rarity=%s",r->name, thishd, getrarityname(thisrr)); poss[nposs] = r; nposs++; if (nposs >= MAXRANDOMLFCANDIDATES) break; } } // nothing found? if (nposs == 0) { // at frequency "FREQUENT"? Try COMMON. if (wantrr < RR_VERYRARE) { wantrr++; if (db) dblog("no possible lfs like this. trying again with wantrr = %d.\n", wantrr); } else { if ((hdmax >= maxmonhitdice) && (hdmin <= 0)) { // give up if (db) dblog("no possible lf at all! giving up."); free(poss); return NULL; } // expand range and try again hdmax++; if (hdmax > maxmonhitdice) hdmax = maxmonhitdice; hdmin--; if (hdmin < 0) hdmin = 0; if (db) dblog("no possible lfs like this. trying again with hitdice %d-%d\n",hdmin,hdmax); } } } if (db) dblog("got %d possibilities.",nposs); // pick a random lf from our possiblities selidx = rnd(0,nposs-1); r = poss[selidx]; free(poss); return r; } race_t *getrandomracewithflag(enum FLAG fid) { condset_t cs; initcondv(&cs, CC_HASFLAG, B_TRUE, fid, CC_NONE); //return getrandomrace(NULL, NA, &cs); return getreallyrandomrace(RC_ANY, &cs); } race_t *getreallyrandomrace(enum RACECLASS wantrc, condset_t *cs) { race_t **poss; race_t *r; int nposs = 0; int sel; int count = 0; // count races for (r = firstrace ; r ; r = r->next) { if (!racemeets(r->id, cs)) continue; if ((wantrc == RC_ANY) || (r->raceclass->id == wantrc)) { if (appearsrandomly(r->id)) { count++; } } } poss = malloc(count * sizeof(race_t *)); for (r = firstrace ; r ; r = r->next) { if (!racemeets(r->id, cs)) continue; if ((wantrc == RC_ANY) || (r->raceclass->id == wantrc)) { if (appearsrandomly(r->id)) { poss[nposs] = r; nposs++; } } } sel = rnd(0,nposs-1); r = poss[sel]; free(poss); return r; } enum SKILL getrandomskill(void) { int sel,count; int nskills; skill_t *sk; enum SKILL retskill = SK_NONE; // count skills nskills = 0; for (sk = firstskill ; sk ; sk = sk->next) { nskills++; } sel = rnd(0,nskills-1); count = 0; for (sk = firstskill ; sk ; sk = sk->next) { if (count == sel) { retskill = sk->id; break; } count++; } assert(findskill(retskill)); return retskill; } object_t *getrestob(lifeform_t *lf) { object_t *o,*bestob = NULL; int bestval = -1; flag_t *f; f = lfhasflag(lf, F_RESTINGINMOTEL); if (f) { o = findobidinmap(lf->cell->map, atol(f->text)); if (o) return o; } for (o = lf->cell->obpile->first ; o ; o = o->next) { if (isknown(o)) { f = hasflag(o->flags, F_HELPSREST); if (f && !isarmour(o)) { if (!bestob || (f->val[0] > bestval)) { bestval = f->val[0]; bestob = o; } } } } for (o = lf->pack->first; o ; o = o->next) { if (isknown(o)) { f = hasflag(o->flags, F_HELPSREST); if (f) { int valid = B_TRUE; // can't rest in armour which doesn't fit if (isarmour(o) && !armourfits(lf, o, NULL)) { valid = B_FALSE; } if (valid) { if (!bestob || (f->val[0] > bestval)) { bestval = f->val[0]; bestob = o; } } } } } return bestob; } // returns proficiency level in a given skill enum SKILLLEVEL getskill(lifeform_t *lf, enum SKILL id) { flag_t *f; // special case: if ((id == SK_CARTOGRAPHY) && lfhasflag(lf, F_RAGE)) { return PR_INEPT; } f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL); if (f) { return f->val[1]; } return PR_INEPT; } int getskilllevcost(enum SKILLLEVEL slev) { int cost; cost = slev - 1; limit(&cost, 1, NA); return cost; } int getsounddist(enum SPEECHVOL volume) { switch (volume) { case SV_SILENT: return 0; case SV_WHISPER: return 1; case SV_TALK: return 5; case SV_SHOUT: return 9; case SV_CAR: return 15; case SV_TRUCK: return 18; case SV_PLANE: return 30; default: break; } return 5; //return (3 * volume); } char *getspeedname(int speed, char *buf) { snprintf(buf, BUFLEN, "unknownspeed"); if (speed <= SP_GODLIKE) { snprintf(buf, BUFLEN, "insanely fast"); } else if (speed <= SP_ULTRAFAST) { snprintf(buf, BUFLEN, "extremely fast"); } else if (speed <= SP_VERYFAST) { snprintf(buf, BUFLEN, "very fast"); } else if (speed <= SP_FAST) { snprintf(buf, BUFLEN, "fast"); } else if (speed <= SP_NORMAL) { snprintf(buf, BUFLEN, "normal"); } else if (speed <= SP_SLOW) { snprintf(buf, BUFLEN, "slow"); } else if (speed <= SP_VERYSLOW) { snprintf(buf, BUFLEN, "very slow"); } else if (speed <= SP_ULTRASLOW) { snprintf(buf, BUFLEN, "extremely slow"); } else { snprintf(buf, BUFLEN, "insanely slow"); } return buf; } char *getspeednameshort(int speed, char *buf) { snprintf(buf, BUFLEN, "unknownspeed"); if (speed <= SP_GODLIKE) { snprintf(buf, BUFLEN, "fast+++"); } else if (speed <= SP_ULTRAFAST) { snprintf(buf, BUFLEN, "fast++"); } else if (speed <= SP_VERYFAST) { snprintf(buf, BUFLEN, "fast+"); } else if (speed <= SP_FAST) { snprintf(buf, BUFLEN, "fast"); } else if (speed <= SP_NORMAL) { snprintf(buf, BUFLEN, "normal"); } else if (speed <= SP_SLOW) { snprintf(buf, BUFLEN, "slow"); } else if (speed <= SP_VERYSLOW) { snprintf(buf, BUFLEN, "slow+"); } else if (speed <= SP_ULTRASLOW) { snprintf(buf, BUFLEN, "slow++"); } else { snprintf(buf, BUFLEN, "slow+++"); } return buf; } long getspforpoint(lifeform_t *lf) { float mod; float amtneeded; amtneeded = SKILLXPPERPOINT; amtneeded += (15 * lf->totskillpoints); //mod = MAXOF(getstatmod(lf, A_IQ), getstatmod(lf, A_WIS)); mod = getstatmod(lf, A_IQ) + getstatmod(lf, A_WIS); amtneeded = pctof(100 - mod, amtneeded); return amtneeded; } // returns a pct addition for things like: // accuracy, evasion, lockpicking // result will be between -50 and 50 float getstatmod(lifeform_t *lf, enum ATTRIB att) { float mod = 0; //float base; // <10 = penalty // 10 = average // >10 = bonus up to 50% mod = getattr(lf, att) - 50; return mod; } enum ATTRBRACKET getattrbracket(int attrval, enum ATTRIB whichatt, char *buf) { if (attrval <= 12) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_EXLOW)); return AT_EXLOW; } else if (attrval <= 23) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_VLOW)); return AT_VLOW; } else if (attrval <= 34) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_LOW)); return AT_LOW; } else if (attrval <= 45) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_LTAVERAGE)); return AT_LTAVERAGE; } else if (attrval <= 56) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_AVERAGE)); return AT_AVERAGE; } else if (attrval <= 67) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_GTAVERAGE)); return AT_GTAVERAGE; } else if (attrval <= 78) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_HIGH)); return AT_HIGH; } else if (attrval <= 89) { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_VHIGH)); return AT_VHIGH; } else { if (buf) strcpy(buf, getattrbracketname(whichatt, AT_EXHIGH)); return AT_EXHIGH; } if (buf) strcpy(buf, "??noattrbracketname??"); return AT_AVERAGE; } char *getskilldesc(enum SKILL id) { skill_t *s; s = findskill(id); if (s) { return s->desc; } return "?unknownskilldesc?"; } char *getskillname(enum SKILL id) { skill_t *s; s = findskill(id); if (s) { return s->name; } return "?unknownskill?"; } char *getskilllevelabbr(enum SKILLLEVEL sl) { switch (sl) { case PR_INEPT: return "---"; case PR_NOVICE: return "Nov"; case PR_BEGINNER: return "Beg"; case PR_ADEPT: return "Adp"; case PR_SKILLED: return "Skl"; case PR_EXPERT: return "Exp"; case PR_MASTER: return "Mst"; } return "???"; } char *getskilllevelname(enum SKILLLEVEL sl) { switch (sl) { case PR_INEPT: return "Inept"; case PR_NOVICE: return "Novice"; case PR_BEGINNER: return "Beginner"; case PR_ADEPT: return "Adept"; case PR_SKILLED: return "Skilled"; case PR_EXPERT: return "Expert"; case PR_MASTER: return "Master"; } return "?Unknownskilllevel?"; } // get a list of of skills which teacher could teach student. // returns # of potential skills found. int getteachableskills(lifeform_t *teacher, lifeform_t *student, int *info, enum TRADEINFOTYPE *tradetype, int *ninfo ) { flag_t *retflag[MAXCANDIDATES], *f; int nretflags, i; *ninfo = 0; // 'teacher' can teach a skill to 'student' if: // - teacher and student both know the skill, and teacher's skill level is higher. // OR // - only teacher has the skill, and teacher's skill level is >= adept. getflags(teacher->flags, retflag, &nretflags, F_HASSKILL, F_NONE); for (i = 0; i < nretflags; i++) { enum SKILLLEVEL studentlev,teacherlev; enum SKILL sid; int teachable = B_FALSE; f = retflag[i]; sid = f->val[0]; teacherlev = f->val[1]; studentlev = getskill(student, sid); if (studentlev && (teacherlev > studentlev) && !ismaxedskill(student, sid)) { teachable = B_TRUE; } else if (!studentlev && (teacherlev >= PR_ADEPT)) { teachable = B_TRUE; } if (teachable) { info[*ninfo] = sid; tradetype[*ninfo] = TI_SKILL; (*ninfo)++; } } // 'teacher' can teach a magic spell (oc_spell) to 'student' if: // - teacher can will/cast the spell. // AND // - student can't already will/cast the spell // AND // - student is skilled in at least one of the spell's schools. // AND // - student is a high enough level to cast the spell. // (use existing code from spellbooks!) getflags(teacher->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE); for (i = 0; i < nretflags; i++) { objecttype_t *spell; enum OBTYPE spellid; int teachable = B_FALSE; f = retflag[i]; spellid = f->val[0]; spell = findot(spellid); if (spell->obclass->id != OC_SPELL) continue; teachable = spelllearnable(student, spellid); if (teachable) { info[*ninfo] = spellid; tradetype[*ninfo] = TI_SPELL; (*ninfo)++; } } return *ninfo; } // get threat rating int gettr(lifeform_t *lf) { flag_t *f; if (isplayer(lf) || lfhasflag(lf, F_VARLEVEL)) { return lf->level; } f = hasflag(lf->flags, F_TR); assert(f); return f->val[0]; //return (lf->maxhp / 4); } char *gettradeinfoname(int what, enum TRADEINFOTYPE tradetype, char *buf) { if (tradetype == TI_SKILL) { skill_t *sk; sk = findskill(what); strcpy(buf, sk->name); } else { // ie. spell objecttype_t *ot; ot = findot(what); sprintf(buf, "the spell '%s'", ot->name); } return buf; } // get throw speed (ie. damage multiplier) // based on strength. // // cube the number to get km/h. // ie. 1 = 1km/h // ie. 2 = 16km/h // ie. 3 = 27km/h // ie. 4 = 64km/h // ie. 5 = 125km/h // ie. 10 = 1000km/h int getthrowspeed(lifeform_t *lf) { enum ATTRBRACKET sb; int speed = 0; // ie. 1 - 4 speed = (getskill(lf, SK_THROWING) / 3) + 1; sb = getattrbracket(getattr(lf, A_STR), A_STR, NULL); switch (sb) { default: break; case AT_VHIGH: speed++; break; case AT_EXHIGH: speed += 2; break; // gun is 15 } return speed; } int getturnspeed(lifeform_t *lf) { int speed; speed = getmovespeed(lf) / 2; limit(&speed, 1, NA); return speed; } void getwantdistance(lifeform_t *lf, lifeform_t *victim, int *min, int *max, int attacking) { flag_t *f; enum ATTRBRACKET iqb; iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); // default - run into them *min = 0; *max = 0; if (attacking) { f = lfhasflag(lf, F_ATTACKRANGE); if (f) { *min = f->val[0]; *max = f->val[1]; } // if you can thrust, keep your distance if (cancast(lf, OT_A_THRUST, NULL)) { if (*min < 2) *min = 2; if (*max < *min) *max = *min; } // if you're timid, it means you will only go close if you are // behind them. if (victim && lfhasflag(lf, F_TIMID)) { // if not already adjacent... if (getcelldist(lf->cell,victim->cell) >= 2) { if ((*min < 2) && !isbehind(lf, victim)) { *min = 2; if (*max < *min) *max = *min; } } } if (iqb >= AT_GTAVERAGE) { // if we're exhausted but aster than our opponent, retreat to // maintain space. if (isexhausted(lf) && (getmovespeed(lf) < getmovespeed(victim))) { if (*min < 2) *min = 2; if (*max < *min) *max = *min; } } } else { // default - stay with 1-2 cells *min = 1; *max = 2; f = lfhasflag(lf, F_FOLLOWRANGE); if (f) { *min = f->val[0]; *max = f->val[1]; } } if (*max < *min) *max = *min; } object_t *getweapon(lifeform_t *lf) { object_t *o; o = getequippedob(lf->pack, BP_WEAPON); if (o) { return o; } // no primary weapon. do we have a secondary one? o = getequippedob(lf->pack, BP_SECWEAPON); if (o && ismeleeweapon(o)) { return o; } return NULL; } // this function MIGHT allocate op! int getweapons(lifeform_t *lf, int meleeonly, object_t **wep, flag_t **damflag, int *lastweaponidx, obpile_t **op, int *nweps) { int gotweapon = B_FALSE; flag_t *retflag[MAXCANDIDATES],*f,*forcewep; int nretflags,i; // first use our weapon... *nweps = 0; forcewep = lfhasflag(lf, F_FORCEATTACK); if (!forcewep) { wep[*nweps] = meleeonly ? getmeleeweapon(lf) : getweapon(lf); if (wep[*nweps]) { if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM); if (lastweaponidx) *lastweaponidx = 0; (*nweps)++; gotweapon = B_TRUE; } // if we are skilled at twoweaponing, we can attack with our second weapon // as well, with a possible accuracy penalty depending on our skill level. if (getskill(lf, SK_TWOWEAPON)) { wep[*nweps] = getsecmeleeweapon(lf); if (wep[*nweps]) { if ((*nweps >= 1) && (wep[*nweps] == wep[(*nweps)-1])) { // can't be the same as first one } else { if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM); if (lastweaponidx) *lastweaponidx = *nweps; (*nweps)++; gotweapon = B_TRUE; } } } } // end if !forcewep // then use all our innate attacks.. getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (!forcewep || (f->val[0] == forcewep->val[0])) { objecttype_t *ot; if (!(*op)) { *op = addobpile(NULL, NULL, NULL); } ot = findot(f->val[0]); if (ot) { wep[*nweps] = addobfast(*op, ot->id); if (damflag) damflag[*nweps] = f; (*nweps)++; } } } return gotweapon; } enum SKILLLEVEL getweaponskill(lifeform_t *lf, object_t *o) { skill_t *sk; if (!o) { return getskill(lf, SK_UNARMED); } sk = getobskill(o->flags); if (sk) { enum SKILLLEVEL weplev; weplev = getskill(lf, sk->id); return weplev; } return PR_INEPT; } long getxpforlev(int level) { long needxp = 0; // 2.8 float multiplier = 13; float constant = 2.8; if (level <= 0) return 0; // no xp needed for level 1 /* for (i = 0; i < level - 1; i++) { //needxp += (20 * pow(2,i)); needxp += (20 * (pow(i,2.8))); } */ needxp = (multiplier * (pow(level,constant) - 1)); return needxp; } void givebehaviour(lifeform_t *lf, enum BEHAVIOUR bid) { behaviour_t *b; flag_t *f; b = findbehaviour(bid); if (!b) return; addflag(lf->flags, F_BEHAVIOUR, bid, NA, NA, NULL); copyflags(lf->flags, b->flags, FROMRACE); switch (bid) { case BH_TIMID: f = lfhasflag(lf, F_MORALE); if (f) { f->val[0] /= 2; } break; case BH_DRUGGED: killflagsofid(lf->flags, F_FLEEONDAM); killflagsofid(lf->flags, F_FLEEONHPPCT); killflagsofid(lf->flags, F_FLEEFROM); break; case BH_DRUNK: addflag(lf->flags, F_DRUNK, rnd(2,5), NA, NA, NULL); case BH_MUSCLED: lf->maxhp = pctof(rnd(125,200), lf->maxhp); // 25-100% more hp lf->hp = lf->maxhp; break; case BH_SCRAWNY: lf->maxhp = pctof(rnd(50,75), lf->maxhp); // 25-50% less hp limit(&(lf->maxhp), 1, NA); lf->hp = lf->maxhp; break; default: break; } } void givejob(lifeform_t *lf, enum JOB jobid) { job_t *j; flag_t *f; int i,b; //int rollhp = B_FALSE; int rollmp = B_FALSE; int rollatt[MAXATTS]; int db = B_FALSE; flag_t *retflag[MAXCANDIDATES]; int nretflags; object_t *sb1 = NULL, *sb2 = NULL,*o = NULL; skill_t *sk; if (db) dblog("givejob() starting.\n"); for (i = 0; i < MAXATTS; i++) { rollatt[i] = B_FALSE; } // give the job addflag(lf->flags, F_JOB, jobid, NA, NA, NULL); j = findjob(jobid); // apply job's maxhp mod f = hasflag(j->flags, F_MAXHPMOD); if (f) { lf->maxhp = pctof(f->val[0], lf->maxhp); lf->hp = lf->maxhp; } // override mpdice from race if (hasflag(j->flags, F_MPDICE)) { rollmp = B_TRUE; f = lfhasflag(lf, F_MPDICE); if (f) { killflag(f); } } /* // for monsters, DONT override alignment from race // // for players, keep it, we'll use it to figure out our // choices when generating our alignment. if (!isplayer(lf)) { if (hasflag(j->flags, F_ALIGNMENT)) { killflagsofid(lf->flags, F_ALIGNMENT); } } */ // apply attrib mods from this job getflags(j->flags, retflag, &nretflags, F_JOBATTRMOD, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; modattr(lf, f->val[0], f->val[1]); // these aren't temporary. lf->baseatt[f->val[0]] = lf->att[f->val[0]]; } // inherit all flags except: // - hpmod ones // - skills which we already have f_noskill for. // - alignment (in the case of monsters) foreach_bucket(b) { for (f = j->flags->first[b] ; f ; f = f->next) { int val[3],id,ignorethis = B_FALSE; char *text; flag_t *f2; id = f->id; val[0] = f->val[0]; val[1] = f->val[1]; val[2] = f->val[2]; text = f->text; if (f->chance != 100) { if (rnd(0,100) > f->chance) { // failed! got an altval? if (f->altval) { // use it instead id = f->altval->id; val[0] = f->altval->val[0]; val[1] = f->altval->val[1]; val[2] = f->altval->val[2]; text = f->altval->text; } else { // ignore this one. ignorethis = B_TRUE; } } } if ((f->condition == FC_IFMONSTER) && isplayer(lf)) { ignorethis = B_TRUE; } else if ((f->condition == FC_IFPLAYER) && !isplayer(lf)) { ignorethis = B_TRUE; } switch (f->id) { case F_ALIGNMENT: if (!isplayer(lf)) ignorethis = B_TRUE; break; case F_MAXHPMOD: case F_JOBATTRMOD: //case F_CANHAVESUBJOB: ignorethis = B_TRUE; break; case F_CANLEARN: if (lfhasflagval(lf, F_NOSKILL, f->val[0], NA, NA, NULL)) ignorethis = B_TRUE; if (!ignorethis && (f->val[1] != NA)) { // already have a better limit? f2 = lfhasflagval(lf, F_NOSKILL, f->val[0], NA, NA, NULL); if (f2 && (f2->val[1] != NA) && (f2->val[1] > f->val[1])) ignorethis = B_TRUE; } break; default: break; } if (!ignorethis) { if (db) dblog("inheriting flagid %d.", f->id); addflag_real(lf->flags, id, val[0], val[1], val[2], text, FROMJOB,B_TRUE, -1); if (id == F_STARTATT) { // need to reroll attribs if we override them. rollatt[val[0]] = B_TRUE; } } } } // now give start obs/skills from the job givestartskills(lf, lf->flags); if (!lfhasflag(lf, F_PHANTASM)) { givestartobs(lf, NULL, lf->flags); autoskill(lf); } // override hp/mp from race /* if (rollhp) { lf->maxhp = 0; for (i = 0; i < lf->level; i++) { lf->maxhp += rollhitdice(lf); assert(lf->maxhp > 0); } lf->hp = lf->maxhp; } */ if (rollmp) { f = hasflag(lf->flags, F_MPDICE); if (f) { lf->maxmp = rollmpdice(lf, B_TRUE); for (i = 0; i < lf->level-1; i++) { lf->maxmp += rollmpdice(lf, B_FALSE); } lf->mp = lf->maxmp; } } // re-roll attributes if required for (i = 0; i < MAXATTS; i++) { if (rollatt[i]) { rollstat(lf, i); lf->baseatt[i] = lf->att[i]; } } // reset max stamina if required. lf->stamina = getmaxstamina(lf); if ((gamemode != GM_GAMESTARTED)) { autoweild(lf); } // special cases if (j->id == J_MONK) { flag_t *f; // monk fists do more damage // also, they always use fists, even if the race has claws etc. f = lfhasflag(lf, F_HASATTACK); if (f) { f->val[0] = OT_FISTS; f->val[1] = 5; } } else if (j->id == J_PIRATE) { flag_t *f; // pirate has a hook instead of a hand f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL); if (f) { f->val[0] = OT_HOOKHAND; f->val[1] = 4; } /* } else if (j->id == J_SHOPKEEPER) { // shopkeepers are not hostile. killflagsofid(lf->flags, F_HOSTILE); killflagsofid(lf->flags, F_HATESRACE); // mark its home shop if (isroom(lf->cell)) { addflag(lf->flags, F_OWNSSHOP, getroomid(lf->cell), NA, NA, NULL); addflag(lf->flags, F_STAYINROOM, getroomid(lf->cell), NA, NA, NULL); } */ } // extra tasks for some jobs if (j->id == J_DRUID) { enum OBTYPE spell; int i; for (i = 0;i < 3; i++) { // pick a spell which you don't already have... spell = getrandomspellfromschool(SS_NATURE, 1); while (cancast(lf, spell, NULL)) { spell = getrandomspellfromschool(SS_NATURE, 1); } // you can now cast it. learnspell(lf, spell, B_FALSE); } // druids always worship ekrub if (isplayer(lf)) { lifeform_t *god; god = findgod(R_GODNATURE); addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL); } } // magic-user spellbooks switch (j->id) { case J_AIRMAGE: sb1 = addob(lf->pack, "spellbook of air magic"); if (!isplayer(lf)) { addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL); addflag(lf->flags, F_RNDSPELLSCHOOL, SS_AIR, 1, lf->level, NULL); } break; case J_ICEMAGE: sb1 = addob(lf->pack, "spellbook of cold magic"); if (!isplayer(lf)) { addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL); addflag(lf->flags, F_RNDSPELLSCHOOL, SS_COLD, 1, lf->level, NULL); } break; case J_FIREMAGE: sb1 = addob(lf->pack, "spellbook of fire magic"); if (!isplayer(lf)) { addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL); addflag(lf->flags, F_RNDSPELLSCHOOL, SS_FIRE, 1, lf->level, NULL); } break; case J_NECROMANCER: sb1 = addob(lf->pack, "spellbook of necromancy"); if (!isplayer(lf)) { addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL); addflag(lf->flags, F_RNDSPELLSCHOOL, SS_DEATH, 1, lf->level, NULL); } break; case J_WILDMAGE: sb1 = addob(lf->pack, "spellbook of wild magic"); if (!isplayer(lf)) { addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL); addflag(lf->flags, F_RNDSPELLSCHOOL, SS_WILD, 1, lf->level, NULL); } break; case J_BATTLEMAGE: // starts off with a grimoire (special code in objects.c will restrict this // to only have 3 spells) sb1 = addob(lf->pack, "grimoire"); identify(sb1); // make sure we have the right skills for (o = sb1->contents->first ; o ; o = o->next) { giveskill(lf, getschoolskill(getspellschool(o->type->id))); } break; case J_PALADIN: sb1 = addob(lf->pack, "spellbook of life magic"); // must worship glorana if (isplayer(lf)) { lifeform_t *god; god = findgod(R_GODLIFE); addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL); } //if (isplayer(lf)) { // autoshortcut(lf, OT_S_TURNUNDEAD); //} // all starting gear is blessed for (o = lf->pack->first ; o ; o = o->next) { if (isweapon(o) || isarmour(o) || isshield(o)) { blessob(o); } } // monster spells if (!isplayer(lf)) { addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL); addflag(lf->flags, F_RNDSPELLSCHOOL, SS_LIFE, 1, 6, NULL); } break; default: break; } // player-controlled specialised wizards now get a secondary school if (isplayer(lf) && (j->category == JC_MAGE)) { initprompt(&prompt, "Select your secondary spell school:"); addchoice(&prompt, 'd', getskillname(SK_SS_DIVINATION), NULL, findskill(SK_SS_DIVINATION), NULL); addchoice(&prompt, 's', getskillname(SK_SS_SUMMONING), NULL, findskill(SK_SS_SUMMONING), NULL); addchoice(&prompt, 't', getskillname(SK_SS_TRANSLOCATION), NULL, findskill(SK_SS_TRANSLOCATION), NULL); getchoice(&prompt); sk = (skill_t *) prompt.result; if (!getskill(lf, sk->id)) { giveskill(lf, sk->id); } switch (sk->id) { case SK_SS_DIVINATION: sb2 = addob(lf->pack, "spellbook of divination magic"); break; case SK_SS_SUMMONING: sb2 = addob(lf->pack, "spellbook of summoning magic"); break; case SK_SS_TRANSLOCATION: sb2 = addob(lf->pack, "spellbook of translocation magic"); break; default: sb2 = NULL; break; } if (sb2) { autolearnspellsfrombook(lf, sb2); addflag(sb2->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); } identify(sb2); } // end if not plain wizard // special spellbook code. if (sb1) { if (isplayer(lf)) { // auto learn and shortcut all spells form it. autolearnspellsfrombook(lf, sb1); addflag(sb1->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); identify(sb1); } else { object_t *o; // monster generally know all spells from the book for (o = sb1->contents->first ; o ; o = o->next) { int lev; lev = getspelllevel(o->type->id); if (!cancast(lf, o->type->id, NULL) && (gettr(lf) >= lev)) { int pow; char pwbuf[BUFLEN]; pow = MINOF((gettr(lf)/2), getspellmaxpower(o->type->id)); snprintf(pwbuf, BUFLEN, "pw:%d;", pow); addtempflag(lf->flags, F_CANCAST, o->type->id, NA, NA, pwbuf, FROMJOB); } } // chance of spellbook vanishing if (pctchance(66)) { killob(sb1); sb1 = NULL; } } } // select subjobs /* if ((gamemode == GM_CHARGEN) && isplayer(lf)) { subjob_t *sub; else { initprompt(&prompt, "Select your job specialty:"); for (sub = firstsubjob ; sub ; sub = sub->next) { if (hasflagval(j->flags, F_CANHAVESUBJOB, sub->id, NA, NA, NULL)) { if (jobpossible(lf->flags, J_NONE, sub->id)) { addchoice(&prompt, sub->letter, sub->name, NULL, sub, sub->desc); } } } addchoice(&prompt, '-', "(none)", NULL, NULL, NULL); if (prompt.nchoices > 1) { getchoicestr(&prompt, B_FALSE, B_TRUE); sub = (subjob_t *)prompt.result; if (sub) { sj = sub->id; } else { sj = SJ_NONE; } } } } */ // call this again, for cases like the paladin. if ((gamemode != GM_GAMESTARTED)) { autoweild(lf); } if (isplayer(lf)) { generatealignment(lf); } if (hasflag(j->flags, F_STAYINROOM)) sethomeroom(lf); } int givemoney(lifeform_t *from, lifeform_t *to, int amt) { object_t *gold; gold = hasob(from->pack, OT_GOLD); if (!gold) { return B_TRUE; } if (gold->amt < amt) { return B_TRUE; } // lose it removeob(gold, amt); // give it to other person if (to) { object_t *togold; togold = hasob(to->pack, OT_GOLD); if (!togold) { togold = addob(to->pack, "gold dollar"); amt--; } togold += amt; } if (isplayer(from)) { flag_t *f; f = lfhasflag(from, F_GAVEMONEY); if (f) { f->val[0] += amt; } else { addflag(from->flags, F_GAVEMONEY, amt, NA, NA, NULL); } } return B_FALSE; } void giveobflags(lifeform_t *lf, object_t *o, enum FLAG whattype) { int flagsknown = 0, flagsfound = 0; flag_t *f,*newflag; int held = B_FALSE, equipped = B_FALSE,activated = B_FALSE; int lifetimeval,b; if (gettechlevel(o->type->id) > getskill(lf, SK_TECHUSAGE)) { return; } if (o->pile->owner == lf) held = B_TRUE; if (held) { f = hasflag(o->flags, F_EQUIPPED); // make sure it's equipped in the right place - ie. weilded rings don't // do anything. if (f) { if (hasflagval(o->flags, F_GOESON, f->val[0], NA, NA, NULL)) { equipped = B_TRUE; } else if ((f->val[0] == BP_WEAPON) || (f->val[0] == BP_SECWEAPON)) { if (isweapon(o)) { equipped = B_TRUE; } } } } if (held && hasflag(o->flags, F_ACTIVATED)) { activated = B_TRUE; } if (whattype == F_EQUIPCONFER) { if (!equipped) { return; } else { lifetimeval = FROMOBEQUIP; } } else if (whattype == F_HOLDCONFER) { if (!held) { return; } else { lifetimeval = FROMOBHOLD; } } else if (whattype == F_ACTIVATECONFER) { if (!activated) { return; } else { lifetimeval = FROMOBACTIVATE; } } else { assert(1 == 0); } foreach_bucket(b) { for (f = o->flags->first[b] ; f ; f = f->next) { if (f->id == whattype) { int known = B_FALSE; int addit = B_FALSE; if (isknown(o) || !isplayer(lf)) { known = B_TRUE; } if (f->val[2] == IFKNOWN) { // only confer if known if (known) { addit = B_TRUE; } } else { addit = B_TRUE; } if (addit) { int newid; int newval[2]; newid = f->val[0]; newval[0] = f->val[1]; newval[1] = f->val[2]; // cursed objects might confer different flags... // TODO: if you uncurse the object, you don't lose the penalty until // you take it off. if (iscursed(o)) { if (newid == F_VISRANGEMOD) { if (newval[0] != NA) { newval[0]--; limit(&(newval[0]), NA, -1); // make it at least -1. } } } newflag = addflag_real(lf->flags, newid, newval[0], newval[1], NA, f->text, lifetimeval, B_FALSE, o->id); if (newflag->known) { // ie. if we found out about it through it being announced flagsknown++; } flagsfound++; } } } } // if all conferred flags now known, object is known if (flagsfound && (flagsknown == flagsfound) && !isknown(o)) { int willmakeknown = B_FALSE; if (isplayer(lf) || cansee(player, lf)) { willmakeknown = B_TRUE; } if (willmakeknown) { makeknown(o->type->id); // in some cases, identify the object fully // (ie. make +xxx bonuses known too) if (hasflag(o->flags, F_IDWHENUSED)) { identify(o); } if (isplayer(lf)) { char buf[BUFLEN]; getobname(o, buf, o->amt); msgnocap("%s %s!", (o->amt == 1) ? "This is" : "These are", buf); } } } } int giverandomobs(lifeform_t *lf, int amt) { char buf[BUFLEN]; int i,ngiven = 0; if (!lfhasflag(lf, F_HUMANOID) || lfhasflag(lf, F_NOPACK)) { return 0; } for (i = 0; i < amt; i++) { if (getrandomob(lf->cell, buf)) { object_t *o; o = addob(lf->pack, buf); if (o) { if (!canpickup(lf, o, o->amt)) { killob(o); } else { ngiven++; } } } } return ngiven; } flag_t *giveskill(lifeform_t *lf, enum SKILL id) { flag_t *f = NULL, *newf; skill_t *sk; int markasused = B_FALSE,i; switch (id) { case SK_CARTOGRAPHY: case SK_PERCEPTION: markasused = B_TRUE; break; default: break; } if (lfhasflagval(lf, F_NOSKILL, id, NA, NA, NULL)) { return NULL; } sk = findskill(id); if (!sk) { return NULL; } f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL); if (f) { // already have the skill - make it better if (f->val[1] < PR_MASTER) { f->val[1]++; f->val[2] = markasused; } if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) { msg("^gYou have learned the %s %s skill!", getskilllevelname(f->val[1]), getskillname(sk->id)); more(); } statdirty = B_TRUE; // in case skill changes your stats } else { // gaining a new skill f = addflag(lf->flags, F_HASSKILL, id, PR_NOVICE, markasused, NULL); if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) { msg("^gYou have learned the %s %s skill!", getskilllevelname(PR_NOVICE), getskillname(sk->id)); more(); } // special effects for gaining a skill (other than conferred abilities). if (id == SK_COOKING) { if (isplayer(lf)) { makeknown(OT_POT_WATER); makeknown(OT_POT_JUICE); makeknown(OT_POT_RUM); } } // learning a new spell school skill after the game has started will grant // you a random first level spell from that school. (player and allies only) if (isspellskill(id) && (gamemode == GM_GAMESTARTED)) { if (isplayer(lf) || areallies(player, lf)) { enum OBTYPE oid; int tries = 0; oid = getrandomspellfromschool(getskillschool(id), 1); while (cancast(lf, oid, NULL) && (tries < 3)) { oid = getrandomspellfromschool(getskillschool(id), 1); tries++; } if (oid != OT_NONE) { addflag(lf->flags, F_CANCAST, oid, NA, NA, NULL); } } } statdirty = B_TRUE; // in case skill changes your stats } // special effects based on skill level if (isplayer(lf) && isloreskill(id) && (f->val[1] == PR_ADEPT)) { race_t *r; // at adept lore skill, you can look up this kind of race in '?r' for (r = firstrace ; r ; r = r->next) { if (r->raceclass->skill == id) r->known = B_TRUE; } } // grant abilities based on skills... for (i = 0; i < sk->nskillwills; i++) { if (sk->skillwill[i].lev == f->val[1]) { newf = hasflagval(lf->flags, F_CANWILL, sk->skillwill[i].abilid, NA, NA, NULL); if (!newf || (newf->lifetime > 0)) { newf = addtempflag(lf->flags, F_CANWILL, sk->skillwill[i].abilid, sk->skillwill[i].timeout, sk->skillwill[i].timeout, sk->skillwill[i].text, FROMSKILL); newf->skillfrom = sk; } } } if (isspellskill(id)) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODMAGIC, 10); } } if (isweaponskill(id)) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODBATTLE, 10); } } if (id == SK_FLIGHT) { flag_t *f; int h; // adjust flight height if we're already flying. h = getnaturalflightheight(lf); f = lfhasflag(lf, F_FLYING); if (f && (f->val[0] < h)) { f->val[0] = h; } } if (id == SK_ARMOUR) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODBATTLE, 5); } } else if (id == SK_COMBAT) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODBATTLE, 5); } } else if (id == SK_COOKING) { if (f->val[1] == PR_ADEPT) { if (isplayer(lf)) { makeknown(OT_MUSHROOMSHI); makeknown(OT_MUSHROOMTOAD); } } } else if (id == SK_FIRSTAID) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODLIFE, 20); } } else if (id == SK_LOCKPICKING) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODTHIEVES, 5); } } else if (id == SK_LORE_ARCANA) { if (isplayer(lf)) { switch (f->val[1]) { case PR_BEGINNER: makeknownobclass(OC_WAND, RR_FREQUENT); break; case PR_ADEPT: makeknownobclass(OC_WAND, RR_COMMON); break; case PR_SKILLED: makeknownobclass(OC_WAND, RR_UNCOMMON); break; case PR_EXPERT: makeknownobclass(OC_WAND, RR_RARE); break; case PR_MASTER: makeknownobclass(OC_WAND, RR_VERYRARE); break; } } if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODMAGIC, 10); } } else if (id == SK_LORE_UNDEAD) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODDEATH, 10); } } else if (id == SK_LORE_CHEMISTRY) { if (isplayer(lf)) { switch (f->val[1]) { case PR_BEGINNER: makeknownobclass(OC_POTION, RR_FREQUENT); break; case PR_ADEPT: makeknownobclass(OC_POTION, RR_COMMON); break; case PR_SKILLED: makeknownobclass(OC_POTION, RR_UNCOMMON); break; case PR_EXPERT: makeknownobclass(OC_POTION, RR_RARE); break; case PR_MASTER: makeknownobclass(OC_POTION, RR_VERYRARE); break; } } } else if (id == SK_LORE_LANGUAGE) { if (isplayer(lf)) { switch (f->val[1]) { case PR_BEGINNER: makeknownobclass(OC_SCROLL, RR_FREQUENT); makeknownobclass(OC_BOOK, RR_FREQUENT); break; case PR_ADEPT: makeknownobclass(OC_SCROLL, RR_COMMON); makeknownobclass(OC_BOOK, RR_COMMON); break; case PR_SKILLED: makeknownobclass(OC_SCROLL, RR_UNCOMMON); makeknownobclass(OC_BOOK, RR_UNCOMMON); break; case PR_EXPERT: makeknownobclass(OC_SCROLL, RR_RARE); makeknownobclass(OC_BOOK, RR_RARE); break; case PR_MASTER: makeknownobclass(OC_SCROLL, RR_VERYRARE); makeknownobclass(OC_BOOK, RR_VERYRARE); break; } } } else if (id == SK_LORE_NATURE) { if (f->val[1] == PR_BEGINNER) { if (isplayer(lf)) { makeknown(OT_MUSHROOMSHI); makeknown(OT_MUSHROOMTOAD); } } if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODNATURE, 5); } } else if (id == SK_LORE_RELICS) { if (isplayer(lf)) { switch (f->val[1]) { case PR_BEGINNER: makeknownobclass(OC_RING, RR_FREQUENT); makeknownobclass(OC_AMULET, RR_FREQUENT); break; case PR_ADEPT: makeknownobclass(OC_RING, RR_COMMON); makeknownobclass(OC_AMULET, RR_COMMON); break; case PR_SKILLED: makeknownobclass(OC_RING, RR_UNCOMMON); makeknownobclass(OC_AMULET, RR_UNCOMMON); break; case PR_EXPERT: makeknownobclass(OC_RING, RR_RARE); makeknownobclass(OC_AMULET, RR_RARE); break; case PR_MASTER: makeknownobclass(OC_RING, RR_VERYRARE); makeknownobclass(OC_AMULET, RR_VERYRARE); break; } } } else if (id == SK_PERCEPTION) { if ((f->val[1] == PR_ADEPT) || (f->val[1] == PR_MASTER)) { // our FOV gets wider lf->losdirty = B_TRUE; if (isplayer(lf)) needredraw = B_TRUE; } } else if (id == SK_STEALTH) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODTHIEVES, 5); } } else if (id == SK_TECHUSAGE) { if (isplayer(lf)) { objecttype_t *ot; // automatically make known all tech <= our skill level for (ot = objecttype ; ot ; ot = ot->next) { // if objecttype is not known... if (!isknownot(ot)) { flag_t *tf; tf = hasflag(ot->flags, F_TECHLEVEL); // if objecttype has a tech level , and it is // lower (or equal to) our tech knowledge... if (tf && !isknownot(ot) && (tf->val[0] <= f->val[1])) { //object_t *o; // then make it known! makeknown(ot->id); /* for (o = lf->pack->first ; o ; o = o->next) { if (o->type->id == ot->id) { if (isplayer(lf)) { char buf[BUFLEN]; getobname(o, buf, o->amt); msgnocap("%c - %s", o->letter, buf); } // now confer effects... giveobflags(lf, o, F_HOLDCONFER); if (isactivated(o)) { giveobflags(lf, o, F_ACTIVATECONFER); } if (isequipped(o)) { giveobflags(lf, o, F_EQUIPCONFER); } } } */ } } } } } else if (id == SK_THIEVERY) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODTHIEVES, 5); } } else if (id == SK_SS_ALLOMANCY) { // give all allomantic spells //mayusespellschool(lf->flags, SS_ALLOMANCY, F_CANCAST , B_FALSE); } else if (id == SK_SS_FIRE) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODFIRE, 5); } } else if (id == SK_SS_LIFE) { if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { if (isplayer(lf)) pleasegodmaybe(R_GODLIFE, 20); } } else if (id == SK_SS_MENTAL) { // give a spell /* if (!lfhasflagval(lf, F_CANCAST, OT_S_MINDSCAN, NA, NA, NULL)) { addflag(lf->flags, F_CANCAST, OT_S_MINDSCAN, NA, NA, NULL); } */ } // announecments based on skill level if ( (gamemode == GM_GAMESTARTED) && isplayer(lf)) { int i; for (i = 0; i < sk->nskilldesc; i++) { if (sk->skilldesclev[i] == f->val[1]) { // does our proficiency need a description? if (sk->skilldescmsg[i]) { msg(sk->skilldesctext[i]); } } } } if ((gamemode == GM_GAMESTARTED) && statdirty) { // redraw it right away. drawstatus(); } return f; } flag_t *giveskilllev(lifeform_t *lf, enum SKILL id, enum SKILLLEVEL slev) { flag_t *f = NULL; f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL); while (!f || (f->val[1] < slev)) { // give another rank f = giveskill(lf, id); if (!f) break; } return f; } // give start objects from a particular flagpile // only give EITHER lf OR targob void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { object_t *o = NULL; flag_t *f; char buf[BUFLEN],buf2[BUFLEN]; int db = B_FALSE; obpile_t *op; map_t *targmap; cell_t *mapcell; enum LFSIZE maxobsize = SZ_MAX; int isshop = B_FALSE; int depthmod2 = 0,b; cell_t fakecell; map_t fakemap; createfakes(&fakemap, &fakecell); if (targob) { cell_t *c; op = targob->contents; c = getoblocation(targob); targmap = c->map; maxobsize = getobsize(targob); if (hasflag(targob->flags, F_SHOP)) isshop = B_TRUE; depthmod2 += rnd(2,8); mapcell = c; } else if (lf) { op = lf->pack; targmap = lf->cell->map; mapcell = lf->cell; } else { assert("error - givestartobs() called without lf or targob" == 0); } if (db && lf) { if (isplayer(lf)) { snprintf(buf2, BUFLEN, "calling givestartobs for %s (PLAYER)",lf->race->name); } else { snprintf(buf2, BUFLEN, "calling givestartobs for %s",lf->race->name); } dblog("%s", buf2); } // handle autoweapon if (lf && hasflag(fp, F_SELECTWEAPON)) { //skill_t *sk; flag_t *f2; objecttype_t *poss[MAXSKILLS]; int nposs = 0, i; flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(fp, retflag, &nretflags, F_SELECTWEAPON, F_NONE); for (i = 0; i < nretflags; i++) { poss[nposs++] = findot(retflag[i]->val[0]); } // find all the weapon skills this lf can learn // and get basic objects of this type /* for (sk = firstskill ; sk ; sk = sk->next) { if (isweaponskill(sk->id)) { if (canlearn(lf, sk->id) || getskill(lf, sk->id)) { objecttype_t *ot; ot = getbasicweaponforskill(sk->id); if (ot) { poss[nposs++] = ot; } } } } */ if (nposs) { objecttype_t *ot = NULL; char ch = 'a'; // note: we use getplayername here even if this isn't a player, // since in that case the prompt will never be displayed. getplayername(buf2); snprintf(buf, BUFLEN, "%s, select your starting weapon:", buf2); initprompt(&prompt, buf); for (i = 0; i < nposs; i++) { char thisdesc[BUFLEN]; int dam,acc; int ok = B_TRUE; enum DAMTYPE dt; object_t *o = NULL; flag_t *vf; f2 = hasflag(poss[i]->flags, F_ACCURACY); acc = f2->val[0]; f2 = hasflag(poss[i]->flags, F_DAM); dt = f2->val[0]; dam = f2->val[1]; snprintf(thisdesc, BUFLEN, "%s (Damage: %d %s, Accuracy: %s)", poss[i]->name, dam, getdamname(dt), getaccuracyname(acc)); // don't allow this if the player is vulnerable to it! vf = lfhasflagval(lf, F_MATVULN, poss[i]->material->id, NA, NA, NULL); if (vf && (vf->val[2] > 0)) { ok = B_FALSE; } if (ok) { o = addobfast(fakecell.obpile, poss[i]->id); if (!o) { ok = B_FALSE; } } if (ok) { if (!canpickup(lf, o, 1)) { ok = B_FALSE; } } if (ok) { addchoice(&prompt, ch++, thisdesc, NULL, poss[i], NULL); } if (o) killob(o); } if (prompt.nchoices == 1) { ot = (objecttype_t *)prompt.choice[0].data; } else if (prompt.nchoices) { if (isplayer(lf)) { getchoice(&prompt); ot = (objecttype_t *)prompt.result; } else { ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; } } if (ot) { skill_t *sk; object_t *o; // give that weapon o = addobfast(lf->pack, ot->id); if (isplayer(lf)) { addflag(o->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); identify(o); } // give one extra rank of skill in this weapon sk = getobskill(o->flags); giveskill(lf, sk->id); // warriors get another rank if (hasjob(lf, J_WARRIOR)) { giveskill(lf, sk->id); } } } } // end if lf && selectweapon killfakes(&fakemap, &fakecell); // give start objects and id them foreach_bucket(b) { for (f = fp->first[b] ; f ; f = f->next) { int val[3]; int id; char *text; o = NULL; id = f->id; val[0] = f->val[0]; val[1] = f->val[1]; val[2] = f->val[2]; text = f->text; if (lf) { if (isplayer(lf)) { // if this is the player, DONT inherit any STARTOB* flags from race if (f->lifetime == FROMRACE) { continue; } } else { // if this is a jobless monster, DONT inherit specific STARTOB flags from race if ((f->lifetime == FROMJOB) && (f->id == F_STARTOB)) { if (!lfhasflag(lf, F_JOB)) { continue; } } } } // now handle other flag conditions... if (f->chance != 100) { if (rnd(0,100) > f->chance) { // failed! got an altval? if (f->altval) { // use it instead id = f->altval->id; val[0] = f->altval->val[0]; val[1] = f->altval->val[1]; val[2] = f->altval->val[2]; text = f->altval->text; } else { // ignore this one. continue; } } } if (id == F_STARTOB) { assert(strlen(text) > 0); if (rnd(1,100) <= val[0]) { o = addob(op, text); if (o) { if (db && lf) { dblog("successfully gave startob: %s (asked for '%s')", o->type->name, text); } } else { if (db) { dblog("couldnt give startob: %s", text); } } } } else if (id == F_STARTOBRND) { if (rnd(1,100) <= val[0]) { int depthmod; enum RARITY wantrr = RR_NONE; condset_t cs; depthmod = val[1]; switch (depthmod) { case NA: depthmod = 0; break; case RANDOM: depthmod = rnd(0,MAXDEPTH); break; default: break; } depthmod += depthmod2; // select the rr first so that we can enforce a minimum if (val[2] != NA) { wantrr = pickrr(TT_OBJECT, val[2]); } initcondv(&cs, CC_MAXSIZE, B_TRUE, maxobsize, CC_NONE); if (real_getrandomob(mapcell, buf, targmap->depth + depthmod, NA, wantrr, B_TRUE, &cs)) { if (isshop) apply_shopob_restrictions(buf); o = addob(op, buf); } } } else if (id == F_STARTOBDT) { if (rnd(1,100) <= val[0]) { int depthmod; condset_t cs; if (db) { snprintf(buf2, BUFLEN, "calling startobdt"); } depthmod = val[2]; switch (depthmod) { case NA: depthmod = 0; break; case RANDOM: depthmod = rnd(0,MAXDEPTH); break; default: break; } depthmod += depthmod2; initcondv(&cs, CC_MAXSIZE, B_TRUE, maxobsize, CC_DAMTYPE, B_TRUE, val[1], CC_OBCLASS, B_TRUE, OC_WEAPON, CC_NONE); if (lf) { apply_wep_tr_limit(lf, &cs); } if (real_getrandomob(mapcell, buf, targmap->depth + depthmod, NA, RR_NONE, B_TRUE, &cs )) { if (db) snprintf(buf2, BUFLEN, "finished startobdt successfuly."); if (isshop) apply_shopob_restrictions(buf); o = addob(op, buf); } else { if (db) snprintf(buf2, BUFLEN, "finished startobdt, failed."); } //assert(strlen(buf) > 0); } } else if (id == F_STARTOBWEPSK) { if (rnd(1,100) <= val[0]) { int depthmod; condset_t cs; if (db) { snprintf(buf2, BUFLEN, "calling startobwepsk"); } depthmod = val[2]; switch (depthmod) { case NA: depthmod = 0; break; case RANDOM: depthmod = rnd(0,MAXDEPTH); break; default: break; } depthmod += depthmod2; initcondv(&cs, CC_MAXSIZE, B_TRUE, maxobsize, CC_WEPSK, B_TRUE, val[1], CC_OBCLASS, B_TRUE, OC_WEAPON, CC_NONE); if (lf) { apply_wep_tr_limit(lf, &cs); } if (real_getrandomob(mapcell, buf, targmap->depth + depthmod, NA, getrarityval(text), B_TRUE, &cs)) { char buf3[BUFLEN]; if (db) snprintf(buf2, BUFLEN, "finished startobwepsk successfuly."); snprintf(buf3, BUFLEN, "%s%s%s",text,strlen(text) ? " " : "",buf); o = addob(op, buf3); } else { if (db) snprintf(buf2, BUFLEN, "finished startobwepsk, failed."); } } } else if (id == F_STARTOBCLASS) { if (rnd(1,100) <= val[0]) { int depthmod; condset_t cs; if (db) { snprintf(buf2, BUFLEN, "calling startobclass"); } depthmod = val[2]; switch (depthmod) { case NA: depthmod = 0; break; case RANDOM: depthmod = rnd(0,MAXDEPTH); break; default: break; } depthmod += depthmod2; initcondv(&cs, CC_MAXSIZE, B_TRUE, maxobsize, CC_OBCLASS, B_TRUE, val[1], CC_NONE); if (lf && (val[1] == OC_WEAPON)) { apply_wep_tr_limit(lf, &cs); } // special case if (targob && (targob->type->id == OT_FRIDGE)) { addcond(&cs, CC_HASFLAG, B_TRUE, F_ISMEAT); } //obdb = B_TRUE; if (real_getrandomob(mapcell, buf, getmapdifficulty(targmap) + depthmod, NA, RR_NONE, B_TRUE, &cs)) { if (db) snprintf(buf2, BUFLEN, "finished startobclass, success."); if (isshop) apply_shopob_restrictions(buf); o = addob(op, buf); } else { //obdb = B_FALSE; if (db) snprintf(buf2, BUFLEN, "finished startobclass. couldnt find an object."); } } } // end what is fid? // TODO: maybe add object to temp cell, then MOVE it to the destination pile. // this will make sure the right checks happen. // some things aren't possible... if (o) { int obok = B_TRUE; if (!obfits(o, op)) { // don't add objects which won't fit obok = B_FALSE; if (db) dblog("problem: ob doesn't fit in its pile (%s)",o->type->name); } else if (op->parentob && hasflag(o->flags, F_CONTAINER)) { // don't put containers in other containers obok = B_FALSE; if (db) dblog("problem: cant put container in another container (%s)",o->type->name); } else if (lf && lfhasflagval(lf, F_MATVULN, o->material->id, NA, NA, NULL)) { int ii; flag_t *retflag2[MAXCANDIDATES],*f2,*vf; int nretflags2; int resolved = B_FALSE; vf = lfhasflagval(lf, F_MATVULN, o->material->id, NA, NA, NULL); assert(vf); if (vf->val[2] > 0) { // don't start with objects which are dangerous to the lf // ...but try to change their material into something else. getflags(o->type->flags, retflag2, &nretflags2, F_CANBEDIFFMAT, F_NONE); for (ii = 0; ii < nretflags2; ii++) { f2 = retflag2[ii]; if (!lfhasflagval(lf, F_MATVULN, f2->val[0], NA, NA, NULL)) { if (!changemat(o, f2->val[0])) { resolved = B_TRUE; break; } } } if (!resolved) { obok = B_FALSE; if (db) dblog("problem: lf is vuln to object's material (%s)",o->type->name); } } } if (!obok) { killob(o); o = NULL; } } // added an object? if (o) { if (lf) { // undead can't have blessed objecst if (isundead(lf)) { if (o->blessed == B_BLESSED) setblessed(o, B_CURSED); } // player knows the objects they start with if (isplayer(lf) || (o->pile->parentob && isplayer(o->pile->parentob->pile->owner))) { identify(o); } if (isplayer(lf)) { // not worth any points addflag(o->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); } } else if (isshop) { flag_t *f2; int b2; // all flags are known foreach_bucket(b2) { for (f2 = o->flags->first[b2] ; f2; f2 = f2->next) { f2->known = B_TRUE; } } } } // end if o } // end foreach container flag } // end foreach bucket // now remove startob flags so we don't get them again! killflagsofid(fp, F_STARTOB); killflagsofid(fp, F_STARTOBDT); killflagsofid(fp, F_STARTOBCLASS); killflagsofid(fp, F_STARTOBWEPSK); if (lf) { // SPECIAL CASES GO HERE. if (lf->race->id == R_JAILER) { o = addob(lf->pack, "map to the goblin caves"); assert(o); } // special case! if (lf->race->baseid == R_DANCINGWEAPON) { // link up the weapon object o = hasobofclass(lf->pack, OC_WEAPON); assert(o); obtodancing(lf, o); } // make sure lf doesn't start off burdened! while (isburdened(lf)) { modattr(lf, A_STR, 1); // get stronger } } } void givestartskills(lifeform_t *lf, flagpile_t *fp) { flag_t *f; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(fp, retflag, &nretflags, F_STARTSKILL, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_STARTSKILL) { int wantval,n; wantval = f->val[1]; for (n = 0; n < wantval; n++) { //while (getskill(lf, f->val[0]) < wantval) { giveskill(lf, f->val[0]); } } } // now remove startskill flags so we don't get them again! killflagsofid(fp, F_STARTSKILL); // all races know about their own race if (!isplayer(lf)) { if (getlorelevel(lf, lf->race->raceclass->id) < PR_NOVICE) { giveskilllev(lf, lf->race->raceclass->skill, PR_NOVICE); } } } // go to sleep/rest/start meditating on purpose. If from a spell effect etc, use fallasleep(). int gotosleep(lifeform_t *lf, int wakewhenhealed) { char lightid[BUFLEN]; strcpy(lightid, ""); if (!lfhasflag(lf, F_MEDITATES)) { if (isundead(lf)) { if (isplayer(lf)) { msg("The undead do not require sleep!"); } return B_TRUE; } if (lfhasflag(lf, F_CAFFEINATED)) { if (isplayer(lf)) { msg("Your caffeine high prevents you from sleeping."); } return B_TRUE; } } taketime(lf, getactspeed(lf)); if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { object_t *o; // turn off light sources first for (o = lf->pack->first ; o ; o = o->next) { if (hasflag(o->flags, F_LIGHTSOURCE) && hasflag(o->flags, F_ACTIVATED)) { if (!strlen(lightid)) { // first one. if (isplayer(lf)) { char ch; ch = askchar("Turn off your light sources before resting?", "yn","y", B_TRUE, B_FALSE); if (ch != 'y') break; } sprintf(lightid, "%ld", o->id); } turnoff(lf, o); } } } addflag(lf->flags, F_ASLEEP, B_TRUE, lfhasflag(lf, F_MEDITATES) ? ST_MEDITATING : ST_ASLEEP, wakewhenhealed ? B_TRUE : NA, lightid); return B_FALSE; } void growhydrahead(lifeform_t *lf, int announce) { flag_t *f; char vname[BUFLEN]; int dr = 0; f = lfhasflagval(lf, F_HASATTACK, OT_TEETH, NA, NA, NULL); dr = f->val[1]; // remember dr if (announce) { // remove one hasattack flag so that the name string // is correct. killflag(f); getlfname(lf, vname); // regrow if (cansee(player, lf)) { msg("^%c%s grow%s two more heads!", getlfcol(lf, CC_GOOD), vname, isplayer(lf) ? "" : "s"); } } if (announce) { // add two more attack flags, since we removed one before. addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE); addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE); } else { // just add one addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE); } // also add extra hp lf->maxhp += HITDIESIDES; gainhp(lf, HITDIESIDES); if (isplayer(lf)) statdirty = B_TRUE; // adjust TR f = hasflag(lf->flags, F_TR); f->val[0]++; } void losehydrahead(lifeform_t *lf) { flag_t *f; int dr = 0; f = lfhasflagval(lf, F_HASATTACK, OT_TEETH, NA, NA, NULL); dr = f->val[1]; // remember dr // remove one hasattack flag killflag(f); // still more heads left? if (countflagsofid(lf->flags, F_HASATTACK)) { // adjust maxhp lf->maxhp -= HITDIESIDES; if (lf->hp > lf->maxhp) lf->hp = lf->maxhp; if (isplayer(lf)) statdirty = B_TRUE; // adjust TR f = hasflag(lf->flags, F_TR); f->val[0]--; } else { // die! lf->lastdamtype = DT_DIRECT; setlastdam(lf, "decapitation"); lf->hp = 0; } } flag_t *hasbleedinginjury(lifeform_t *lf, enum BODYPART bp) { flag_t *f, *retflag[MAXCANDIDATES]; int nretflags,i; getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0;i < nretflags; i++) { f = retflag[i]; if (isplayer(lf) && (f->lifetime < 0)) { // for the player, only temporary flags count - not permenant ones // this is so that losing a whole finger etc doesn't completely // cripple you by making you always bleed. } else if ((f->val[1] == bp) && (f->val[2] == DT_SLASH)) { return f; } } return NULL; } int hasfreeaction(lifeform_t *lf) { return real_hasfreeaction(lf, F_NONE); } // special case: exception = F_DEAD means don't check whether lf is dead, even // though they won't really have the F_DEAD flag. int real_hasfreeaction(lifeform_t *lf, enum FLAG exception) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; getflags(lf->flags, retflag, &nretflags, F_ASLEEP, F_CASTINGSPELL, F_DIGGING, F_EATING, F_FROZEN, F_PARALYZED, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->id != exception) { return B_FALSE; } } if ((exception != F_DEAD) && isdead(lf)) return B_FALSE; return B_TRUE; } int hashealableinjuries(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; int injurycount = 0; // has injuries which can heal? getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->lifetime > 0) injurycount++; } return injurycount; } flag_t *hasinjuredbp(lifeform_t *lf, enum BODYPART bp) { return lfhasflagval(lf, F_INJURY, NA, bp, NA, NULL); } job_t *hasjob(lifeform_t *lf, enum JOB job) { job_t *j = NULL; if (lfhasflagval(lf, F_JOB, job, NA, NA, NULL)) { j = findjob(job); } return j; } int hasjobcat(lifeform_t *lf, enum JOBCATEGORY jcid) { job_t *j = NULL; j = getjob(lf); if (j && (j->category == jcid)) { return B_TRUE; } return B_FALSE; } int hastempinjuries(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i,count = 0; getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->lifetime > 0) count++; } return count; } flag_t *hasname(lifeform_t *lf) { flag_t *nameflag = NULL,*f; // check for polymorphed named creatures f = lfhasflag(lf, F_ORIGRACE); if (f) { race_t *origrace; origrace = findrace(f->val[0]); if (origrace) { nameflag = hasflag(origrace->flags, F_NAME); } } if (!nameflag) { nameflag = lfhasflag(lf, F_NAME); } return nameflag; } /* int hassubjob(lifeform_t *lf, enum SUBJOB id) { flag_t *f; f = lfhasflag(lf, F_JOB); if (f && (f->val[1] == id)) { return B_TRUE; } return B_FALSE; } */ int hassoul(lifeform_t *lf) { switch (getraceclass(lf)) { case RC_HUMANOID: case RC_ANIMAL: case RC_DRAGON: break; default: break; } return B_FALSE; } void inc_quad_range(enum QUADRANT *start, enum QUADRANT *end, int howmuch) { int i; for (i = 0; i < abs(howmuch); i++) { if (start) { if (*start == Q_NNE) *start = Q_NNW; else (*start)--; } if (end) { if (*end == Q_NNW) *end = Q_NNE; else (*end)++; } } } // where shoudl always be body, hands, legs or head // damtype should be bash or slash // // if forcetype is not supplied, injury type will be selected randomly. int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype, enum INJURY forcetype) { char lfname[BUFLEN],buf[BUFLEN]; char *desc = NULL; enum INJURY inj = IJ_NONE; enum BODYPART bp2 = BP_NONE; object_t *wep = NULL,*o; int howlong; getlfname(lf, lfname); o = hasequippedobid(lf->pack, OT_AMU_NOINJURY); if (o) { char obname[BUFLEN]; int seen = B_FALSE; getobname(o, obname, 1); if (isplayer(lf)) { msg("Your %s throbs.", noprefix(obname)); seen = B_TRUE; } else if (cansee(player, lf)) { msg("%s%s %s throbs.", lfname, getpossessive(lfname), obname); seen = B_TRUE; } if (seen && !isknown(o)) { makeknown(o->type->id); if (isplayer(lf)) { msg("Amazingly, your %s is unharmed!", getbodypartname(lf, where)); } } return B_TRUE; } if (where == BP_NONE) return B_TRUE; if (!hasbp(lf, where)) { if (where == BP_LEGS) { // adjust bp_legs into bp_frontlegs/bp_backelgs if (hasbp(lf, BP_FRONTLEGS) || hasbp(lf, BP_BACKLEGS)) { // ok. } else { return B_TRUE; } } else { return B_TRUE; } } if (lfhasflag(lf, F_NOINJURIES)) return B_TRUE; if (lfhasflagval(lf, F_INJURY, NA, where, NA, NULL)) return B_TRUE; howlong = rnd(30,80); // might be overridden depending on injury if (forcetype) { inj = forcetype; } else { if (damtype == DT_BASH) { switch (where) { case BP_BODY: switch (rnd(1,5)) { case 1: inj = IJ_RIBCRACKED; break; case 2: inj = IJ_RIBBROKEN; break; case 3: inj = IJ_TORSOBRUISED; break; case 4: inj = IJ_TORSOBRUISEDBAD; break; case 5: inj = IJ_WINDED; break; } break; case BP_HANDS: switch (rnd(1,3)) { case 1: inj = IJ_FINGERBROKEN; break; case 2: inj = IJ_SHOULDERDISLOCATED; break; case 3: inj = IJ_HANDSWOLLEN; break; } break; case BP_HEAD: switch (rnd(1,4)) { case 1: inj = IJ_BLACKEYE; break; case 2: inj = IJ_CONCUSSION; break; case 3: inj = IJ_WINDPIPECRUSHED; break; case 4: inj = IJ_NOSEBROKEN; break; } break; case BP_LEGS: if (onein(3)) { inj = IJ_LEGBROKEN; break; } else { switch (rnd(1,2)) { case 1: inj = IJ_LEGBRUISE; break; case 2: inj = IJ_ANKLESWOLLEN; break; } } break; case BP_TAIL: switch (rnd(1,2)) { case 1: inj = IJ_TAILBRUISED; break; case 2: inj = IJ_TAILBROKEN; break; } break; case BP_WINGS: inj = IJ_WINGBRUISED; break; default: break; } } else if (damtype == DT_SLASH) { switch (where) { case BP_BODY: if (pctchance(10)) { inj = IJ_HEARTPIERCED; } else { inj = IJ_CHESTBLEED; break; } break; case BP_HANDS: switch (rnd(1,4)) { case 1: inj = IJ_HANDBLEED; break; case 2: inj = IJ_TENDONCUT; break; case 3: inj = IJ_ARTERYPIERCE; break; case 4: // severed finger inj = IJ_FINGERMISSING; break; } break; case BP_HEAD: if (pctchance(10)) { inj = IJ_BRAINRUPTURED; } else { switch (rnd(1,2)) { case 1: inj = IJ_EYELIDSCRAPED; break; case 2: inj = IJ_EYEDESTROYED; break; } } break; case BP_LEGS: switch (rnd(1,2)) { case 1: inj = IJ_LEGBLEED; break; case 2: inj = IJ_HAMSTRUNG; break; } break; case BP_TAIL: inj = IJ_TAILBLEED; break; case BP_WINGS: switch (rnd(1,2)) { case 1: inj = IJ_WINGTORN; break; case 2: inj = IJ_WINGBLEED; break; } default: break; } } else if (damtype == DT_EXPLOSIVE) { switch (where) { case BP_BODY: switch (rnd(1,3)) { case 1: // collapsed lung inj = IJ_LUNGCOLLAPSED; break; case 2: inj = IJ_RIBCRACKED; break; case 3: inj = IJ_RIBBROKEN; break; } break; case BP_HANDS: inj = IJ_HANDMISSING; break; case BP_HEAD: switch (rnd(1,2)) { case 1: // ringing ears inj = IJ_EARSRINGING; break; case 2: // blinded inj = IJ_BLINDED; break; } break; case BP_LEGS: // lose limb break; case BP_TAIL: inj = IJ_TAILLACERATED; break; case BP_WINGS: inj = IJ_WINGDESTROYED; break; default: break; } } } // some injuries have extra effects before announcing the injury... switch (inj) { case IJ_BLACKEYE: if (eyesshaded(lf)) { inj = IJ_NONE; } break; case IJ_BLINDED: addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(50,100)); inj = IJ_NONE; break; case IJ_BRAINRUPTURED: if (isplayer(lf)) { msg("^BYour brain is ruptured!"); } else if (cansee(player, lf)) { msg("^%c%s%s brain ruptures!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname)); } if (lf->hp > 0) { setlastdam(lf, "a ruptured brain"); lf->lastdamtype = DT_DIRECT; lf->hp = 0; } inj = IJ_NONE; break; case IJ_HANDMISSING: // lose limb if (onein(2)) { if (hasbp(lf, BP_WEAPON)) bp2 = BP_WEAPON; else bp2 = BP_SECWEAPON; } else { if (hasbp(lf, BP_SECWEAPON)) bp2 = BP_SECWEAPON; else bp2 = BP_WEAPON; } if (hasbp(lf, bp2)) { object_t *o[2]; enum BODYPART fingerbp; int i; // drop anyting in that hand o[0] = getequippedob(lf->pack, bp2); if (bp2 == BP_WEAPON) { o[1] = getequippedob(lf->pack, BP_RIGHTFINGER); fingerbp = BP_RIGHTFINGER; } else { o[1] = getequippedob(lf->pack, BP_LEFTFINGER); fingerbp = BP_LEFTFINGER; } addtempflag(lf->flags, F_NOBODYPART, bp2, B_FROMINJURY, NA, NULL, FROMINJURY); addtempflag(lf->flags, F_NOBODYPART, fingerbp, B_FROMINJURY, NA, NULL, FROMINJURY); howlong = PERMENANT; for (i = 0; i < 2; i++) { if (o[i]) { char obname[BUFLEN]; if (isplayer(lf)) { getobname(o[i],obname,o[i]->amt); msg("Your %s drops to the ground.",noprefix(obname)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getobname(o[i],obname,o[i]->amt); msg("%s%s %s drops to the ground.",lfname,getpossessive(lfname),noprefix(obname)); } moveob(o[i], lf->cell->obpile, o[i]->amt); } } } else { inj = IJ_NONE; } break; case IJ_HEARTPIERCED: if (isplayer(lf)) { msg("^BYour heart is pierced!"); } else if (cansee(player, lf)) { msg("^%c%s%s heart is pierced!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname)); } if (lf->hp > 0) { setlastdam(lf, "a pierced heart"); lf->lastdamtype = DT_DIRECT; lf->hp = 0; } inj = IJ_NONE; break; case IJ_FINGERMISSING: if (onein(2)) { if (hasbp(lf, BP_RIGHTFINGER)) bp2 = BP_RIGHTFINGER; else bp2 = BP_LEFTFINGER; } else { if (hasbp(lf, BP_LEFTFINGER)) bp2 = BP_LEFTFINGER; else bp2 = BP_RIGHTFINGER; } if (hasbp(lf, bp2)) { object_t *o; addob(lf->cell->obpile, "severed finger"); o = getequippedob(lf->pack, bp2); addtempflag(lf->flags, F_NOBODYPART, bp2, B_FROMINJURY, NA, NULL,FROMINJURY); if (o) { char obname[BUFLEN]; if (isplayer(lf)) { getobname(o,obname,o->amt); msg("Your %s drops to the ground.",noprefix(obname)); } else if (cansee(player, lf)) { getobname(o,obname,o->amt); msg("%s%s %s drops to the ground.",lfname,getpossessive(lfname),noprefix(obname)); } moveob(o, lf->cell->obpile, o->amt); } break; } else { inj = IJ_NONE; } break; case IJ_EYEDESTROYED: case IJ_TAILLACERATED: case IJ_WINGDESTROYED: howlong = PERMENANT; break; case IJ_WINDED: howlong = rnd(3,5); break; default: break; } // set description based on injury switch (inj) { case IJ_RIBCRACKED: desc = strdup("ribs are cracked^carrying capacity halved"); break; case IJ_RIBBROKEN: desc = strdup("ribs are broken^carrying capacity halved, -6 accuracy"); break; case IJ_TORSOBRUISED: desc = strdup("torso is bruised^-2 accuracy"); break; case IJ_TORSOBRUISEDBAD: desc = strdup("torso is badly bruised^-4 accuracy, -10% dam"); break; case IJ_WINDED: desc = strdup("stomach is winded^-1 Fitness"); break; case IJ_BLACKEYE: desc = strdup("eye is bruised^vision range halved"); break; case IJ_CONCUSSION: desc = strdup("brain is concussed^random movement"); break; case IJ_WINDPIPECRUSHED: desc = strdup("windpipe is crushed^fitness penalty"); break; case IJ_NOSEBROKEN: desc = strdup("nose is broken^charisma penalty,reduced smell sense"); case IJ_LEGBROKEN: desc = strdup("leg is broken^movement speed greatly lowered"); break; case IJ_LEGBRUISE: desc = strdup("leg is bruised^movement speed lowered"); break; case IJ_ANKLESWOLLEN: desc = strdup("ankle is swollen^cannot wear/remove boots"); break; case IJ_TAILBRUISED: desc = strdup("tail is bruised^accuracy penalty"); break; case IJ_TAILBROKEN: desc = strdup("tail is fractured^occasional random movement"); break; case IJ_WINGBRUISED: desc = strdup("wings are bruised^flight speed lowered"); break; case IJ_CHESTBLEED: desc = strdup("chest is bleeding^damage from enemies is increased"); break; case IJ_HANDBLEED: desc = strdup("hand is bleeding^attacking will cause damage"); break; case IJ_TENDONCUT: desc = strdup("right flexor tendon is cut^cannot weild weapons"); break; case IJ_FINGERMISSING: snprintf(buf, BUFLEN, "%s is severed^cannot wear rings on this hand", getbodypartname(lf, bp2)); desc = strdup(buf); break; case IJ_EYELIDSCRAPED: desc = strdup("eyelid is scraped^accuracy penalty"); break; case IJ_EYEDESTROYED: desc = strdup("right eye is destroyed^field of view halved"); break; case IJ_LEGBLEED: desc = strdup("leg is bleeding^movement will cause damage"); break; case IJ_HAMSTRUNG: desc = strdup("left hamstring is torn^lower move speed, chance of falling"); break; case IJ_TAILBLEED: desc = strdup("tail is bleeding^no additional effects"); break; case IJ_WINGTORN: desc = strdup("wings are torn^cannot fly"); break; case IJ_WINGBLEED: desc = strdup("wings are bleeding^flying causes damage"); break; case IJ_LUNGCOLLAPSED: desc = strdup("lungs have collapsed^lose all stamina points"); break; case IJ_HANDMISSING: snprintf(buf, BUFLEN, "%s is destroyed^cannot use this hand", getbodypartname(lf, bp2)); desc = strdup(buf); break; case IJ_EARSRINGING: desc = strdup("ears are ringing^cannot hear sounds"); break; case IJ_TAILLACERATED: desc = strdup("tail is lacerated^chance of falling during movement"); break; case IJ_WINGDESTROYED: desc = strdup("wings are destroyed^cannot fly"); break; case IJ_HANDSWOLLEN: desc = strdup("hand is swollen^rings cannot be put on/removed"); break; case IJ_FINGERBROKEN: desc = strdup("finger is broken^acc penalty"); break; case IJ_SHOULDERDISLOCATED: desc = strdup("shoulder is dislocated^acc penalty, cannot use heavy weapons"); break; case IJ_ARTERYPIERCE: desc = strdup("radial artery is pierced^constant bleeding"); break; // fatal - no description case IJ_BRAINRUPTURED: break; // fatal - no description case IJ_HEARTPIERCED: break; // fatal - no description case IJ_BLINDED: break; // no injury will be added. case IJ_NONE: break; } if (inj == IJ_NONE) { if (desc) free(desc); return B_TRUE; } else { flag_t *injflag; enum BODYPART newwhere; switch (inj) { case IJ_BLACKEYE: case IJ_EYELIDSCRAPED: case IJ_EYEDESTROYED: newwhere = BP_EYES; break; case IJ_SHOULDERDISLOCATED: newwhere = BP_SHOULDERS; break; default: newwhere = where; break; } injflag = addtempflag(lf->flags, F_INJURY, inj, where, damtype, desc, howlong); injflag->obfrom = B_NEWINJURY; } if (desc) free(desc); // special effects from taking injuries if (damtype == DT_SLASH) bleed(lf, B_SPLATTER); // extra effects after getting the injury. switch (inj) { case IJ_BLACKEYE: case IJ_EYELIDSCRAPED: case IJ_EYEDESTROYED: if (isplayer(lf)) setlosdirty(lf); break; case IJ_HAMSTRUNG: if (!isairborne(lf, NULL)) fall(lf, NULL, B_TRUE); break; case IJ_SHOULDERDISLOCATED: wep = getweapon(lf); if (wep && isheavyweapon(wep)) drop(wep, wep->amt); break; case IJ_TENDONCUT: wep = getweapon(lf); if (wep) drop(wep, wep->amt); break; case IJ_WINGTORN: killflagsofid(lf->flags, F_FLYING); break; case IJ_WINGDESTROYED: addtempflag(lf->flags, F_NOBODYPART, BP_WINGS, B_FROMINJURY, NA, NULL,FROMINJURY); break; case IJ_WINDED: lf->stamina = 0; if (isplayer(lf)) statdirty = B_TRUE; break; default: break; } if (where == BP_TAIL) { if (lf->race->id == R_MANTICORE) { flag_t *f; // can't use spike volley f = hasflagval(lf->flags, F_CANWILL, OT_S_SPIKEVOLLEY, NA, NA, NULL); if (f) killflag(f); } } return B_FALSE; } // return TRUE on failure int leveldrain(lifeform_t *lf, int amt, enum CHECKTYPE sctype, int scdiff, lifeform_t *fromlf) { int resisted = B_FALSE; if (isimmuneto(lf->flags, DT_NECROTIC, B_FALSE)) resisted = B_TRUE; if (!resisted && isresistantto(lf->flags, DT_NECROTIC, B_FALSE) && onein(2)) resisted = B_TRUE; // fit check if (!resisted && skillcheck(lf, sctype, scdiff, 0 )) { resisted = B_TRUE; } if (resisted) { if (isplayer(lf)) { msg("You struggle to retain your life force!"); } return B_TRUE; } // announce if (isplayer(lf)) { msg("^%cYou feel your life force draining away!",getlfcol(lf, CC_VBAD)); } if (fromlf) { char lfname[BUFLEN]; setkillverb(lf, "Life-drained"); real_getlfnamea(fromlf, lfname, NULL, B_TRUE, B_TRUE); setlastdam(lf, lfname); } else { setkillverb(lf, "Killed"); setlastdam(lf, "life force drain"); } if (fromlf) { lf->lastdamlf = fromlf->id; } loselevel(lf, amt, fromlf); return B_FALSE; } int lfcanbekod(lifeform_t *lf) { if (isundead(lf)) return B_FALSE; switch (getraceclass(lf)) { case RC_GOD: case RC_SLIME: case RC_PLANT: case RC_OTHER: case RC_UNDEAD: return B_FALSE; default: break; } if (lfhasflag(lf, F_NONCORPOREAL)) { return B_FALSE; } if (isdead(lf)) { return B_FALSE; } if (lfhasflag(lf, F_NOKO)) { return B_FALSE; } // note: not checking whether they are already unconscious // because merciful weapons CAN still KO them in this case. return B_TRUE; } int lfcanbestoned(lifeform_t *lf) { switch (getlfmaterial(lf)) { case MT_GAS: case MT_STONE: return B_FALSE; default: break; } if (lfhasflag(lf, F_NONCORPOREAL)) { return B_FALSE; } if (isimmuneto(lf->flags, DT_PETRIFY, B_FALSE)) { return B_FALSE; } if (isdead(lf)) { return B_FALSE; } return B_TRUE; } // does the lf has this flag, either internally or // conferred by a held/equipped object? flag_t *lfhasflag(lifeform_t *lf, enum FLAG fid) { flag_t *f; f = hasflag(lf->flags, fid); if (f) return f; return NULL; } flag_t *lfhasflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, char *text) { flag_t *f; f = hasflagval(lf->flags, fid, val0, val1, val2, text); if (f) return f; return NULL; } flag_t *lfhasknownflag(lifeform_t *lf, enum FLAG fid) { flag_t *f; // do we have this flag directly? f = hasflagknown(lf->flags, fid); if (f && f->known) return f; return NULL; } flag_t *lfhasknownflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, char *text) { flag_t *f; // got the flag directly? f = hasflagvalknown(lf->flags, fid, val0, val1, val2, text); if (f && f->known) return f; return NULL; } // returns radius of light produces int lfproduceslight(lifeform_t *lf, object_t **fromwhat) { int temp = 0; int radius = 0; object_t *o; if (fromwhat) *fromwhat = NULL; // lf producing light itself? getmaxflags(lf->flags, F_PRODUCESLIGHT, &temp, NULL, NULL); if (temp) radius = temp; // objects in hands or on body... for (o = lf->pack->first ; o ; o = o->next) { if (isequipped(o)) { temp = obproduceslight(o); if (temp > radius) { radius = temp; if (fromwhat) *fromwhat = o; } } } return radius; } int lighthurtseyes(lifeform_t *lf) { flag_t *f; if (eyesshaded(lf)) return B_FALSE; if (isblind(lf)) return B_FALSE; // if you don't have eyes, your'e safe! if (lfhasflagval(lf, F_NOBODYPART, BP_EYES, NA, NA, NULL)) { return B_FALSE; } f = lfhasflag(lf, F_SEEINDARK); if (f && (f->val[1] == B_BLINDABLE)) { return B_TRUE; } return B_FALSE; } // if you pass in 'target', then you don't need 'targcell' // return true on failure int lockpick(lifeform_t *lf, cell_t *targcell, object_t *target, object_t *device) { flag_t *f,*lockflag; char lfname[BUFLEN]; char obname[BUFLEN]; int faileffect; int difficulty = 20; // default, never used though int bonus = 0; char ch; // TODO: for now, only players can lockpick if (!isplayer(lf)) { return B_TRUE; } // what will we use? if (!device) { condset_t cs; if (!hasobwithflag(lf->pack, F_PICKLOCKS)) { msg("You have nothing to use for lockpicking!"); return B_TRUE; } // ask which object to use initcondv(&cs, CC_HASFLAG, B_TRUE, F_PICKLOCKS, CC_NONE); device = askobject(lf->pack, "Lockpick using what", NULL, NULL, 'p', &cs, B_FALSE); if (!device) { msg("Cancelled."); return B_TRUE; } else if (!hasflag(device->flags, F_PICKLOCKS)) { msg("That can't be used to pick locks!"); return B_TRUE; } } if (!target) { object_t *poss[MAXCANDIDATES],*o; int nposs = 0,i; if (!targcell) { int dir; dir = askdir("Lockpick in which direction (- to cancel)", B_TRUE, B_FALSE); if (dir == D_NONE) { targcell = lf->cell; } else { targcell = getcellindir(lf->cell, dir); } if (!targcell) { return B_TRUE; } } // get a list of lockpickable obejcts there for (o = targcell->obpile->first ; o ; o = o->next) { if (hasflag(o->flags, F_LOCKED)) { poss[nposs++] = o; } } if (!nposs) { msg("There is nothing here to lockpick!"); return B_TRUE; } else if (nposs == 1) { target = poss[0]; } else { // ask which one initprompt(&prompt, "What will you try to unlock?"); ch = 'a'; for (i = 0 ; i < nposs; i++) { char obname[BUFLEN]; getobname(poss[i], obname, 1); addchoice(&prompt, ch++, obname, NULL, poss[i], NULL); } prompt.maycancel = B_TRUE; ch = getchoice(&prompt); if (ch != '\0') target = (object_t *)prompt.result; if (!target) { msg("Cancelled."); return B_TRUE; } } } // we should now have both 'device' and 'target' filled in. lockflag = hasflag(target->flags, F_LOCKED); if (lockflag) { difficulty = lockflag->val[1]; } else { // should never happen if (isplayer(lf)) { msg("That isn't locked!"); } return B_TRUE; } getlfname(lf,lfname); getobname(target,obname, 1); f = hasflag(device->flags, F_PICKLOCKS); assert(f); bonus = f->val[0]; if (isblessed(device)) bonus += 5; faileffect = f->val[1]; // take time taketime(lf, getactspeed(lf) ); if (skillcheck(lf, SC_OPENLOCKS, difficulty, bonus )) { // success! // announce if (isplayer(lf) || cansee(player, lf)) { msg("%s unlock%s %s.",lfname, isplayer(lf) ? "" : "s", obname); } // unlock it killflagsofid(target->flags, F_LOCKED); // xp if (isplayer(lf)) { if (hasjob(lf, J_ROGUE)) { gainxp(lf, difficulty); } else { gainxp(lf, difficulty/3); } } // training practice(lf, SK_LOCKPICKING, 1); // gods if (isplayer(lf)) pleasegodmaybe(R_GODTHIEVES, 5); } else { // failed! if (faileffect == B_DIEONFAIL) { char devname[BUFLEN]; getobname(device,devname, 1); // kill object if (isplayer(lf)) { msg("^wYour %s breaks!",noprefix(devname)); } else if (cansee(player, lf)) { msg("%s%s %s breaks!",lfname, getpossessive(lfname), noprefix(devname)); } removeob(device, 1); } else if (faileffect == B_BLUNTONFAIL) { if (!makedullermaybe(device, 1)) { if (isplayer(lf) || cansee(player, lf)) { msg("%s fail%s to unlock %s.",lfname, isplayer(lf) ? "" : "s", obname); } } } else { if (isplayer(lf) || cansee(player, lf)) { msg("%s fail%s to unlock %s.",lfname, isplayer(lf) ? "" : "s", obname); } } practice(lf, SK_LOCKPICKING, 1); return B_TRUE; } practice(lf, SK_LOCKPICKING, 1); return B_FALSE; } void loseobflags(lifeform_t *lf, object_t *o, int kind) { flag_t *f,*ff,*nextff; int b; foreach_bucket(b) { for (f = o->flags->first[b] ; f ; f = f->next) { int checkthis = B_FALSE; if (kind == ALLCONFERRED) { if ((f->id == F_EQUIPCONFER) || (f->id == F_HOLDCONFER) || (f->id == F_ACTIVATECONFER)) { checkthis = B_TRUE; } } else { if (f->id == kind) { checkthis = B_TRUE; } } if (checkthis) { int lifetimecheck,b2; // probably don't need this now that we are using // flag->obfrom... if (f->id == F_EQUIPCONFER) { lifetimecheck = FROMOBEQUIP; } else if (f->id == F_ACTIVATECONFER) { lifetimecheck = FROMOBACTIVATE; } else { // ie. F_HOLDCONFER lifetimecheck = FROMOBHOLD; } // not using killflagsofid() because we only want // to kill flags which came from this object foreach_bucket(b2) { for (ff = lf->flags->first[b2] ; ff ; ff = nextff) { nextff = ff->next; if ( (ff->id == f->val[0]) && (ff->val[0] == f->val[1]) && (ff->val[1] == f->val[2]) && (ff->lifetime == lifetimecheck) ) { if (ff->obfrom == o->id) { killflag(ff); } } } } } } } } int handlearmour(lifeform_t *lf, object_t *fromob, int *dam, enum DAMTYPE dt) { int damreducedbyarmour = 0; if (!armourcanstopdam(dt)) return 0; // modify for defender's armour // first figure out how much to reduce the damage by. damreducedbyarmour = getarmourdamreduction(lf, fromob, *dam, dt); // now actually reduce the damage amount applyarmourdamreduction(lf, fromob, damreducedbyarmour, dam, dt); return damreducedbyarmour; } int hasbp(lifeform_t *lf, enum BODYPART bp) { int i; if (lfhasflagval(lf, F_NOBODYPART, bp, NA, NA, NULL)) { return B_FALSE; } for (i = 0; i < lf->race->nbodyparts; i++) { if (lf->race->bodypart[i].id == bp) return B_TRUE; } return B_FALSE; } flag_t *hasactivespell(lifeform_t *lf, enum OBTYPE sid) { return lfhasflagval(lf, F_BOOSTSPELL, sid, NA, NA, NULL); } int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) { return haslof_real(src, dest, loftype, newdest, NULL, B_TRUE); } int haslofknown(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) { return haslof_real(src, dest, loftype, newdest, src->lf, B_TRUE); } // got line of fire to dest? if lof is blocked, return last cell in 'newdest' // if 'srclf' is set, we are checking whether 'srclf' THINKS they have line of fire. ie invisible/unseed // lifeforms don't block lof. also srclf won't be able to block its own lof. // // if 'walllfsok' is set, then we ARE allowed to have lineoffire to walls (ie. solid cells) if there is a lifeform // there. int haslof_real(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest, lifeform_t *srclf, int walllfsok) { int numpixels; int i; int x1,y1,x2,y2; map_t *map; cell_t *retcell[MAXRETCELLS]; reason = E_OK; if (loftype == LOF_DONTNEED) { return B_TRUE; } if (newdest) *newdest = src; //if (!viewer) return B_FALSE; if (!src) return B_FALSE; if (src->map != dest->map) { reason = E_NOLOS; return B_FALSE; } map = dest->map; x1 = src->x; y1 = src->y; x2 = dest->x; y2 = dest->y; // can always fire at your own cell if ((x1 == x2) && (y1 == y2)) { return B_TRUE; } calcbresnham(map, x1, y1, x2, y2, retcell, &numpixels); for (i = 0; i < numpixels ; i++) { cell_t *cell; object_t *blockob; cell = retcell[i]; // we _DO_ need to move out of last cell for line of fire. // if walls don't stop lof, this is now a valid cell. if (!(loftype & LOF_WALLSTOP)) { if (newdest) *newdest = cell; } // solid cells stop lof UNLESS // - this is our source cell // - there is a lf there and this is our target cell if (loftype & LOF_WALLSTOP) { if (cell->type->solid) { if (i == 0) { // ok } else if (walllfsok && cell->lf && (i == (numpixels-1)) ) { // ok } else { reason = E_NOLOF; return B_FALSE; } } // certain objects block lof blockob = hasobwithflag(cell->obpile, F_BLOCKSLOF); if (blockob) { int blocked = B_TRUE; // default int isopen; if (isdoor(blockob, &isopen)) { if (isopen) { // open doors don't block line of fire blocked = B_FALSE; } } if (blocked) { reason = E_NOLOF; return B_FALSE; } } } // if lifeforms don't stop lof, this is now a valid cell. // if (!(loftype & LOF_LFSSTOP)) { if (newdest) *newdest = cell; // } // lifeforms block lof unless: // - they're in the first cell (ie the one doing the throwing/firing/shooting/etc) // - they're in our destination if (cell->lf && (!isprone(cell->lf)) && (loftype & LOF_LFSSTOP) && (cell->lf != srclf)) { int lfcanblocklof = B_TRUE; if (srclf && !cansee(srclf, cell->lf)) { lfcanblocklof = B_FALSE; } if (lfcanblocklof) { // if not in first or last cell... if ((i != 0) && (i != (numpixels-1)) ) { reason = E_NOLOF; return B_FALSE; } } } // cell is now valid if (newdest) *newdest = cell; } // made it to the target cell! return B_TRUE; } void idxtoxy(lifeform_t *lf, int idx, int *x, int *y) { int xoffset,yoffset; // get the offset form the lf's position. xoffset = (idx % lf->visw) - lf->visrange; yoffset = (idx / lf->visw) - lf->visrange; // return the actual cell there *x = lf->cell->x + xoffset; *y = lf->cell->y + yoffset; } // confirmed good! int xytoidx(lifeform_t *lf, int x, int y) { int idx; int xd,yd; if (abs(x - lf->cell->x) > lf->visrange) return -1; if (abs(y - lf->cell->y) > lf->visrange) return -1; xd = lf->cell->x - lf->visrange; yd = lf->cell->y - lf->visrange; idx = (y - yd) * lf->visw + (x - xd); return idx; } /* void setviscell(lifeform_t *lf, cell_t *cell, int how) { int idx; int x,y; cell_t *c; idx = xytoidx(lf, cell->x, cell->y); assert(idx >= 0); assert(idx < (lf->visw * lf->visw)); lf->viscell[idx] = how; // checks if (how == B_VIS) { assert (abs(lf->cell->x - cell->x) <= 10); assert (abs(lf->cell->y - cell->y) <= 10); } idxtoxy(lf, idx, &x, &y); c = getcellat(lf->cell->map, x, y); assert(c); } int getviscell(lifeform_t *lf, cell_t *cell) { int idx; idx = xytoidx(lf, cell->x, cell->y); if ((idx < 0) || (idx >= (lf->visw * lf->visw)) ) { return B_NOVIS; } return lf->viscell[idx]; } */ int haslos(lifeform_t *viewer, cell_t *dest) { if (gamemode == GM_CLEANUP) return B_FALSE; if (!viewer) return B_FALSE; if (!dest) return B_FALSE; if (!viewer->cell) return B_FALSE; if (viewer->cell->map != dest->map) return B_FALSE; if (gamemode < GM_GAMESTARTED) return B_FALSE; // can't see when you're dead UNLESS you are the player. this is // to prevent the screen from going black when "You die" appears. //if (isdead(viewer) && !isplayer(viewer)) return B_FALSE; if (lfhasflag(viewer, F_DEAD) && !isplayer(viewer)) return B_FALSE; if (viewer->losdirty) { precalclos(viewer); viewer->losdirty = B_FALSE; } // can we use pre-calced los? // /* if (viewer->los) { return haslos_fast(viewer, dest); } */ assert(viewer->los || (viewer->nlos == 0)); /* if (haslos_fast(viewer, dest)) { return B_TRUE; } return haslosdark(viewer, dest); */ return haslos_fast(viewer, dest); /* // THIS CODE IS NO LONGER EVER USED map = dest->map; x1 = viewer->cell->x; y1 = viewer->cell->y; x2 = dest->x; y2 = dest->y; f = hasflag(viewer->flags, F_XRAYVIS); if (f) { xray = f->val[0]; } else { xray = 0; } // can't see if you're blind if (isblind(viewer)) { return B_FALSE; } maxvisrange = getvisrange(viewer, B_TRUE); nvrange = getnightvisrange(viewer); limit(&nvrange, NA, maxvisrange); if (!celllitfor(viewer, dest, maxvisrange, nvrange)) { return B_FALSE; } calcbresnham(map, x1, y1, x2, y2, retcell, &numpixels); currange = 0; for (i = 0; i < numpixels ; i++) { cell_t *cell; cell = retcell[i]; // don't need to move out of the last one if ((cell->x == x2) && (cell->y == y2)) { break; } if (i != 0) { // ie. if not blind, you can always see your own cell int rangemod; currange++; if (currange > maxvisrange) { return B_FALSE; } // if we went uphill, stop here //if (wentuphill) { // return B_FALSE; //} if (!celltransparentfor(viewer, cell, &xray, &rangemod)) { return B_FALSE; } currange += rangemod; // xray vision decreases by one if (xray) xray--; // if you went uphill, can't see any further //if (getheight(x,y,z) > origheight) { // wentuphill = B_TRUE; //} } } return B_TRUE; */ } /* int haslosdark(lifeform_t *viewer, cell_t *dest) { int i; for (i = 0; i < viewer->nlosdark; i++) { if (viewer->losdark[i] == dest) { return B_TRUE; } } return B_FALSE; } */ int haslos_fast(lifeform_t *viewer, cell_t *dest) { int i; for (i = 0; i < viewer->nlos; i++) { if (viewer->los[i] == dest) { return B_TRUE; } } return B_FALSE; } enum FLAG isairborne(lifeform_t *lf, int *height) { flag_t *f; if (height) *height = 0; if (!lf) return B_FALSE; f = lfhasflag(lf, F_FLYING); if (f) { if (height) *height = f->val[0]; return F_FLYING; } if (lfhasflag(lf, F_LEVITATING)) { if (height) *height = SZ_MEDIUM; return F_LEVITATING; } if (lfhasflag(lf, F_ICESLIDE)) { if (height) *height = SZ_MEDIUM; return F_LEVITATING; } return F_NONE; } int isaquatic(lifeform_t *lf) { if (lf->race->raceclass->id == RC_AQUATIC) { return B_TRUE; } else if (lfhasflag(lf, F_AQUATIC)) { return B_TRUE; } return B_FALSE; } flag_t *isasleep(lifeform_t *lf) { return lfhasflagval(lf, F_ASLEEP, NA, ST_ASLEEP, NA, NULL); } // is lf behind otherlf? // you can never be "behind" something with f_awareness int isbehind(lifeform_t *lf, lifeform_t *otherlf) { int dir; if (lfhasflag(otherlf, F_AWARENESS)) return B_FALSE; dir = getdirtowards(otherlf->cell, lf->cell, NULL, B_FALSE, DT_ORTH); if (getrelativedir(otherlf, dir) == RD_BACKWARDS) { return B_TRUE; } return B_FALSE; } int isbleeding(lifeform_t *lf) { if (lfhasflagval(lf, F_INJURY, NA, NA, DT_SLASH, NULL)) { //return B_FROMINJURY; return B_TRUE; } return B_FALSE; } flag_t *isblind(lifeform_t *lf) { flag_t *f; if (!lf) return NULL; f = lfhasflag(lf, F_ASLEEP); if (f && (f->val[1] != ST_MEDITATING)) { return f; } f = lfhasflag(lf, F_BLIND); if (f) { return f; } f = lfhasflagval(lf, F_NOBODYPART, BP_EYES, NA, NA, NULL); if (f) { if (!lfhasflag(lf, F_TREMORSENSE) && !lfhasflag(lf, F_SEEWITHOUTEYES)) { return f; } } return NULL; } enum BURDENED isburdened(lifeform_t *lf) { float cur,max; float ratio; // monsters can't be burdened! if (!isplayer(lf)) { return BR_NONE; } max = getmaxcarryweight(lf); cur = getobpileweight(lf->pack); ratio = cur / max; if (ratio >= 2) { return BR_OVERLOADED; } else if (ratio > 1.5) { return BR_STRAINED; } else if (ratio > 1) { return BR_BURDENED; } return BR_NONE; } int ischarmable(lifeform_t *lf) { if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= AT_EXLOW) { reason = E_LOWIQ; return B_FALSE; } if (isundead(lf)) { reason = E_UNDEAD; return B_FALSE; } if (getraceclass(lf) == RC_ROBOT) { reason = E_ROBOT; return B_FALSE; } if (hasflag(lf->flags, F_DRUNK)) { reason = E_DRUNK; return B_FALSE; } if (lfhasflag(lf, F_CHARMEDBY)) { reason = E_ALREADYUSING; return B_FALSE; } if (hasflag(lf->flags, F_UNIQUE) || hasflag(lf->flags, F_NOCHARM)) { reason = E_NOEFFECT; // generic error return B_FALSE; } return B_TRUE; } int isclimbing(lifeform_t *lf) { if (hasflag(lf->flags, F_CLIMBING)) { return B_TRUE; } return B_FALSE; } int isdead(lifeform_t *lf) { if (!lf->alive) return B_TRUE; if (lf->hp <= 0) { if (hasactivespell(lf, OT_S_DELAYDEATH) && (lf->hp > -15)) { } else { return B_TRUE; } } return B_FALSE; } void killlf(lifeform_t *lf) { lifeform_t *nextone, *lastone, *l; map_t *m; flag_t *f; if (gamemode == GM_GAMESTARTED) { if (cansee(player, lf)) { needredraw = B_TRUE; } } m = lf->cell->map; // remove references lf->cell->lf = NULL; // stomach? f = lfhasflag(lf, F_MAPLINK); if (f) { map_t *m; m = findmap(f->val[0]); killmap(m); // kill the stomach killflag(f); // kill the link flag } // shouldn't need this... lf->cell = NULL; // remove line of sight data if (lf->los) { free(lf->los); lf->los = NULL; } // remove impossible stuff if (getstamina(lf) > getmaxstamina(lf)) { setstamina(lf, getmaxstamina(lf)); } // check if anyone is targetting us. // if so, stop targetting us now that // we are dead. // also: does anyone have us as a master? // TODO: check on all maps? if (gamemode == GM_GAMESTARTED) { for (l = m->lf ; l ; l = l->next) { f = lfhasflagval(l, F_TARGETLF, lf->id, NA, NA, NULL); if (f) killflag(f); f = lfhasflagval(l, F_ATTACHEDTO, lf->id, NA, NA, NULL); if (f) killflag(f); f = lfhasflagval(l, F_GRABBEDBY, lf->id, NA, NA, NULL); if (f) killflag(f); f = lfhasflagval(l, F_GRABBING, lf->id, NA, NA, NULL); if (f) killflag(f); f = lfhasflagval(l, F_PETOF, lf->id, NA, NA, NULL); if (f) killflag(f); } } // free mem if (lf->lastdam) free(lf->lastdam); if (lf->killverb) free(lf->killverb); // kill any remaining obs while (lf->pack->first) { killob(lf->pack->first); } free(lf->pack); lf->pack = NULL; // kill any remaining obs while (lf->polypack->first) { killob(lf->polypack->first); } free(lf->polypack); lf->polypack = NULL; // kill flags killflagpile(lf->flags); lf->flags = NULL; // remove from list nextone = lf->next; if (nextone != NULL) { nextone->prev = lf->prev; } else { /* last */ m->lastlf = lf->prev; } if (lf->prev == NULL) { /* first */ nextone = lf->next; free(m->lf); m->lf = nextone; } else { lastone = lf->prev; free (lastone->next ); lastone->next = nextone; } } int isdeaf(lifeform_t *lf) { if (lfhasflag(lf, F_TREMORSENSE)) return B_FALSE; if (lfhasflag(lf, F_DEAF)) return B_TRUE; if (isresting(lf) && lfhasflag(lf, F_RESTINGINMOTEL)) return B_TRUE; if (lfhasflagval(lf, F_INJURY, IJ_EARSRINGING, NA, NA, NULL)) return B_TRUE; if (hasequippedobid(lf->pack, OT_AMU_SLEEP) && isasleep(lf)) return B_TRUE; return B_FALSE; } // returns second weapon if you are dual weilding object_t *isdualweilding(lifeform_t *lf) { object_t *priwep,*secwep; // dual weilding? priwep = getweapon(lf); secwep = getsecmeleeweapon(lf); if (priwep && secwep) { if (priwep != secwep) { // twohanded weapons dont count return secwep; } } return B_FALSE; } flag_t *isfleeing(lifeform_t *lf) { return lfhasflag(lf, F_FLEEFROM); } flag_t *isfleeingfrom(lifeform_t *lf, lifeform_t *runfrom) { return lfhasflagval(lf, F_FLEEFROM, runfrom->id, NA, NA, NULL); } flag_t *isflyingwithwings(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; getflags(lf->flags, retflag, &nretflags, F_FLYING, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->lifetime == FROMABIL) { return retflag[i]; } } return NULL; } int isfreebp(lifeform_t *lf, enum BODYPART bp, object_t *whatfor) { object_t *o; o = hasobwithflagval(lf->pack, F_EQUIPPED, bp, NA, NA, NULL); if (o) { if (whatfor && !hasflag(whatfor->flags, F_UNDERCLOTHING) && hasflag(o->flags, F_UNDERCLOTHING)) { // ok. } else { return B_FALSE; } } if (!hasbp(lf, bp)) return B_FALSE; if ((bp == BP_WEAPON) || (bp == BP_SECWEAPON)) { object_t *o; enum BODYPART otherbp; // check for 2handed weapons in other hand too if (bp == BP_WEAPON) otherbp = BP_SECWEAPON; else otherbp = BP_WEAPON; o = hasobwithflagval(lf->pack, F_EQUIPPED, otherbp, NA, NA, NULL); if (o) { if (istwohandedfor(o, lf)) { return B_FALSE; } } } return B_TRUE; } int isfriendly(lifeform_t *lf) { if (lfhasflag(lf, F_FRIENDLY)) { return B_TRUE; } if (ispetof(lf, player)) { return B_TRUE; } return B_FALSE; } int isfullyhealed(lifeform_t *lf) { int healed = B_TRUE; if ((lf->hp < lf->maxhp) || hastempinjuries(lf)) { healed = B_FALSE; } else if (lf->mp < getmaxmp(lf)) { healed = B_FALSE; } else if (getstamina(lf) < getmaxstamina(lf)) { healed = B_FALSE; } else if (hashealableinjuries(lf)) { healed = B_FALSE; } return healed; } int isexhausted(lifeform_t *lf) { if (lfhasflag(lf, F_NOSTAM)) return B_FALSE; if (!getstamina(lf)) return B_TRUE; return B_FALSE; } int isgenius(lifeform_t *lf) { enum ATTRBRACKET iqb; iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); if (lfhasflag(lf, F_OMNIPOTENT) || lfhasflag(lf, F_EXTRAINFO) || iqb >= AT_EXHIGH) { return B_TRUE; } return B_FALSE; } flag_t *isimmuneto(flagpile_t *fp, enum DAMTYPE dt, int onlytemp) { flag_t *f; dt = basedamagetype(dt); f = hasflagval(fp, F_DTIMMUNE, dt, NA, NA, NULL); if (f) { if (!onlytemp || (f->lifetime != FROMRACE)) { return f; } } f = hasflagval(fp, F_DTIMMUNE, DT_ALL, NA, NA, NULL); if (f) { if (!onlytemp || (f->lifetime != FROMRACE)) { return f; } } return NULL; } // returns distance to closest enemy. 0 = not in battle. int isinbattle(lifeform_t *lf, int includedistant, int onlyarmed) { if (includedistant) { lifeform_t *l; for (l = lf->cell->map->lf ; l ;l = l->next) { if ((l != lf) && areenemies(l, lf) && cansee(lf, l)) { if (!lfhasflag(l, F_DOESNTMOVE)) { if (!onlyarmed || getweapon(l)) { return getcelldist(lf->cell, l->cell); } } } } } else { int dir; for (dir = DC_N; dir <= DC_NW; dir++) { cell_t *c; c = getcellindir(lf->cell, dir); if (c && c->lf && areenemies(lf, c->lf) && cansee(lf, c->lf)) { if (!lfhasflag(c->lf, F_DOESNTMOVE)) { if (!onlyarmed || getweapon(c->lf)) { return 1; } } } } } return B_FALSE; } int isingunrange(lifeform_t *lf, cell_t *where) { object_t *gun; int range; gun = getfirearm(lf); if (!gun) return B_FALSE; range = getfirearmrange(gun); if (getcelldist(lf->cell, where) <= range) { return B_TRUE; } return B_FALSE; } int isgod(lifeform_t *lf) { if (lf->race->raceclass->id == RC_GOD) return B_TRUE; //if (hasjob(lf, J_GOD)) return B_TRUE; return B_FALSE; } int ishelplessvictim(lifeform_t *victim, lifeform_t *attacker, enum HELPLESSTYPE *how) { if (isundead(victim)) return B_FALSE; if (!cansee(attacker, victim)) return B_FALSE; if (isfleeing(victim)) { if (how) *how = HL_FLEEING; return B_TRUE; } else if (!cansee(victim, attacker) && (getraceclass(victim) != RC_PLANT)) { if (gettargetlf(victim) != attacker) { if (how) *how = HL_CANTSEE; return B_TRUE; } } if (how) *how = HL_NONE; return B_FALSE; } flag_t *ishidingfrom(lifeform_t *hider, lifeform_t *seeker) { if (hider != seeker) { flag_t *f; f = lfhasflag(hider, F_HIDING); if (f && !lfhasflagval(seeker, F_SPOTTED, hider->id, NA, NA, NULL)) { return f; } } return NULL; } // can you try to recruit this lf? int ishirable(lifeform_t *lf) { if (!isplayer(lf) && ispeaceful(lf)) { if (lfhasflag(lf, F_HIRABLE)) { return B_TRUE; } if (lfhasflag(lf, F_ISPRISONER)) { return B_TRUE; } } return B_FALSE; } // cannot voluntarily move _at all_. int isimmobile(lifeform_t *lf) { if (lfhasflag(lf, F_ASLEEP)) { return B_TRUE; } if (lfhasflag(lf, F_FROZEN)) { return B_TRUE; } if (lfhasflag(lf, F_PARALYZED)) { return B_TRUE; } if (isdead(lf)) { return B_TRUE; } return B_FALSE; } int isliving(lifeform_t *lf) { switch (getraceclass(lf)) { case RC_UNDEAD: case RC_OTHER: case RC_MAGIC: case RC_ROBOT: case RC_SLIME: return B_FALSE; default: break; } return B_TRUE; } int isloreskill(enum SKILL skid) { switch (skid) { case SK_LORE_ARCANA: case SK_LORE_DEMONS: case SK_LORE_HUMANOID: case SK_LORE_NATURE: case SK_LORE_UNDEAD: return B_TRUE; default: break; } return B_FALSE; } int islowhp(lifeform_t *lf) { float hppct; hppct = ((float)lf->hp / (float) lf->maxhp) * 100; if (hppct <= 40) return B_TRUE; return B_FALSE; } int isspellskill(enum SKILL skid) { switch (skid) { case SK_SS_ALLOMANCY: case SK_SS_MENTAL: case SK_SS_NATURE: case SK_SS_AIR: case SK_SS_DEATH: case SK_SS_DIVINATION: case SK_SS_FIRE: case SK_SS_COLD: case SK_SS_LIFE: case SK_SS_SUMMONING: case SK_SS_TRANSLOCATION: case SK_SS_WILD: return B_TRUE; default: break; } return B_FALSE; } int ismadeofice(lifeform_t *lf) { if (lf->material->id == MT_ICE) return B_TRUE; if (lfhasflag(lf, F_FROZEN)) return B_TRUE; return B_FALSE; } // is this lf's BASE attrib maxed? int ismaxedattr(lifeform_t *lf, enum ATTRIB a) { if (lf->att[a] >= MAX_ATTRIBVAL) { return B_TRUE; } return B_FALSE; } int ismaxedskill(lifeform_t *lf, enum SKILL skid) { if (getskill(lf, skid) >= getmaxskilllevel(lf, skid)) { return B_TRUE; } return B_FALSE; } int ispeaceful(lifeform_t *lf) { if (isplayer(lf)) return B_FALSE; if (lfhasflag(lf, F_HOSTILE)) { return B_FALSE; } if (lfhasflagval(lf, F_TARGETLF, player->id, NA, NA, NULL)) { return B_FALSE; } return B_TRUE; } int isknownpeaceful(lifeform_t *lf) { if (ispetof(lf, player)) return B_TRUE; if ((getlorelevel(player, getraceclass(lf)) >= PR_NOVICE) || (getskill(player, SK_SPEECH)) ) { return B_TRUE; } return ispeaceful(lf); } int ispetof(lifeform_t *lf, lifeform_t *owner) { if (!lf || !owner) return B_FALSE; if (lfhasflagval(lf, F_PETOF, owner->id, NA, NA, NULL)) { return B_TRUE; } return B_FALSE; } flag_t *ispetortarget(lifeform_t *lf, lifeform_t *ownertarget) { flag_t *f; if (!lf || !ownertarget) return B_FALSE; f = lfhasflagval(lf, F_PETOF, ownertarget->id, NA, NA, NULL); if (f) return f; f = lfhasflagval(lf, F_TARGETLF, ownertarget->id, NA, NA, NULL); if (f) return f; return NULL; } int isplayer(lifeform_t *lf) { if (lf && (lf->controller == C_PLAYER)) { return B_TRUE; } return B_FALSE; } // returns poison flag with longest remaining lifetime flag_t *ispoisoned(lifeform_t *lf) { flag_t *f,*max = NULL; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->lifetime == PERMENANT) { return f; } else if ((max == NULL) || (f->lifetime > max->lifetime)) { max = f; } } return max; } flag_t *ispoisonedwith(lifeform_t *lf, enum POISONTYPE pt) { return lfhasflagval(lf, F_POISONED, pt, NA, NA, NULL); } int ispolymorphed(lifeform_t *lf) { if (lfhasflag(lf, F_POLYMORPHED)) { return B_TRUE; } else { return B_FALSE; } } int isprone(lifeform_t *lf) { flag_t *f; if (lfhasflag(lf, F_PRONE)) { return B_TRUE; } f = lfhasflag(lf, F_ASLEEP); if (f && (f->val[1] != ST_MEDITATING)) { return B_TRUE; } return B_FALSE; } flag_t *isresistantto(flagpile_t *fp, enum DAMTYPE dt, int onlytemp) { flag_t *f; dt = basedamagetype(dt); f = hasflagval(fp, F_DTRESIST, dt, NA, NA, NULL); if (f) { if (!onlytemp || (f->lifetime != FROMRACE)) { return f; } } f = hasflagval(fp, F_DTRESIST, DT_ALL, NA, NA, NULL); if (f) { if (!onlytemp || (f->lifetime != FROMRACE)) { return f; } } if (dt == DT_FIRE) { f = hasflag(fp, F_WET); if (f) return f; f = hasflag(fp, F_DRUNK); if (f) return f; } if (dt == DT_COLD) { f = hasflag(fp, F_DRUNK); if (f) return f; } return NULL; } flag_t *isresting(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_ASLEEP); if (f && (f->val[2] != NA)) { return f; } return NULL; } int issleepingtimefor(lifeform_t *lf) { if (lfhasflag(lf, F_NOCTURNAL) && !isnighttime()) { return B_TRUE; } else if (lfhasflag(lf, F_DIURNAL) && isnighttime()) { return B_TRUE; } return B_FALSE; } object_t *isstuck(lifeform_t *lf) { object_t *o; for (o = lf->cell->obpile->first ; o ; o = o->next) { if ((o->type->id == OT_WEB) && (lf->race->baseid == R_SPIDER)) { continue; } if (hasflag(o->flags, F_RESTRICTMOVEMENT)) { return o; } } return NULL; } int isstuckinwall(lifeform_t *lf) { enum ERROR error; if (!cellwalkable(lf, lf->cell, &error)) { if ((error == E_WALLINWAY) && !isclimbing(lf)) { return B_TRUE; } } return B_FALSE; } poisontype_t *addpoisontype(enum POISONTYPE id, char *name, char *desc, char *contracttext, char *damverb, enum OBTYPE vomitob, int dam, int dampct, enum POISONSEVERITY severity, int incubationtime) { poisontype_t *a; // add to the end of the list if (firstpoisontype == NULL) { firstpoisontype = malloc(sizeof(poisontype_t)); a = firstpoisontype; a->prev = NULL; } else { // go to end of list a = lastpoisontype; a->next = malloc(sizeof(poisontype_t)); a->next->prev = a; a = a->next; } lastpoisontype = a; a->next = NULL; // props a->id = id; a->name = strdup(name); a->desc = strdup(desc); a->contracttext = strdup(contracttext); a->damverb = strdup(damverb); a->vomitob = vomitob; a->dam = dam; a->dampct = dampct; a->severity = severity; a->incubationtime = incubationtime; return a; } job_t *addjob(enum JOB id, char *name, char *desc, enum JOBCATEGORY category) { job_t *a; // add to the end of the list if (firstjob == NULL) { firstjob = malloc(sizeof(job_t)); a = firstjob; a->prev = NULL; } else { // go to end of list a = lastjob; a->next = malloc(sizeof(job_t)); a->next->prev = a; a = a->next; } lastjob = a; a->next = NULL; // props a->id = id; a->name = strdup(name); a->desc = strdup(desc); a->category = category; a->flags = addflagpile(NULL, NULL); return a; } void addbodypart(race_t *r, enum BODYPART bp, char *name) { r->bodypart[r->nbodyparts].id = bp; if (name) { r->bodypart[r->nbodyparts].name = strdup(name); } else { r->bodypart[r->nbodyparts].name = strdup(""); } r->bodypart[r->nbodyparts].armourok = B_TRUE; r->nbodyparts++; } lifeform_t *addlf(cell_t *cell, enum RACE rid, int level) { return real_addlf(cell, rid, level, C_AI); } lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { map_t *m; lifeform_t *a; int i; //int db = B_FALSE; redrawpause(); assert(cell); if (cell->type != (celltype_t *) DUMMYCELLTYPE) { assert(!cell->type->solid); } m = cell->map; // add to the end of the list if (m->lf == NULL) { m->lf = malloc(sizeof(lifeform_t)); a = m->lf; a->prev = NULL; } else { // go to end of list a = m->lastlf; a->next = malloc(sizeof(lifeform_t)); a->next->prev = a; a = a->next; } if (controller == C_PLAYER) { player = a; } m->lastlf = a; a->next = NULL; a->born = B_FALSE; // will set this back to true later // props a->id = nextlfid; nextlfid++; a->controller = controller; a->level = level; a->newlevel = level; a->xp = getxpforlev(a->level); a->totskillpoints = 0; a->skillxp = 0; a->skillpoints = 0; a->cell = cell; a->alive = B_TRUE; a->lastdam = strdup("nothing"); a->lastdamtype = DT_NONE; a->lastdamlf = -1; a->damlastturn = 0; a->mplastturn = 0; a->stamlastturn = 0; a->bartimer = 0; a->killverb = strdup("Killed"); if ((gamemode == GM_GAMESTARTED) && a->prev) { a->timespent = a->prev->timespent + 1; } else { a->timespent = 0; } a->rotated = B_FALSE; a->sorted = B_FALSE; a->forgettimer = 0; a->prevcell[0] = NULL; a->prevcell[1] = NULL; a->polyrevert = B_FALSE; // for precalcing line of sight a->facing = rnd(DC_N, DC_NW); a->turncounter = 0; a->losdirty = B_TRUE; //a->los = malloc( sizeof(cell_t *) * ((MAXVISRANGE*2+1)*(MAXVISRANGE*2+1))); a->los = NULL; // will be alloced in precalclos //a->losdark = NULL; // will be alloced in precalclos a->nlos = 0; a->loslock = B_FALSE; //a->nlosdark = 0; //a->eyeadjustment = 0; a->changinglev = B_FALSE; // debug a->redraws = 0; // for ai // avoid messages when equipping initial obs a->created = B_FALSE; a->pack = addobpile(a, NOLOC, NOOB); a->polypack = addobpile(NOOWNER, NOLOC, NOOB); a->turnsskipped = 0; // clear laoding variables for (i = 0; i < MAXPILEOBS; i++) { a->oblist[i] = -1; } a->x = -1; a->y = -1; // defaults a->hp = 1; a->maxhp = a->hp; a->mp = 0; a->maxmp = a->mp; a->material = findmaterial(MT_FLESH); // might be overridden in setrace // init flags a->flags = addflagpile(a, NULL); // link cell back to lf cell->lf = a; // set race - this will inherit race flags and material a->race = NULL; setrace(a, rid, B_FALSE); // now do everything which must occur AFTER setting the race: // remember original attribs so that if we are polymorphed, we're // able to revert back. for (i = 0; i < MAXATTS; i++) { a->origatt[i] = a->baseatt[i]; } // set stamina AFTER setrace as it depends on your attribs a->stamina = getmaxstamina(a); // give start objets AFTER setrace as some races start with objects if ((gamemode != GM_LOADING) && (gamemode != GM_VALIDATION)) { outfitlf(a); } a->created = B_TRUE; a->born = B_TRUE; // now finished creating it. if (gamemode == GM_GAMESTARTED) { if (cansee(player, a)) { needredraw = B_TRUE; } } // a->losdirty = B_TRUE; addflag(a->flags, F_HOMEMAP, cell->map->id, NA, NA, NULL); // set home room. sethomeroom(a); redrawresume(); return a; } race_t *addrace(enum RACE id, char *name, float weight, int glyph, int glyphcolour, enum MATERIAL mat, enum RACECLASS raceclass, char *desc) { race_t *a; assert(!findrace(id)); // add to the end of the list if (firstrace == NULL) { firstrace = malloc(sizeof(race_t)); a = firstrace; a->prev = NULL; } else { // go to end of list a = lastrace; a->next = malloc(sizeof(race_t)); a->next->prev = a; a = a->next; } lastrace = a; a->next = NULL; // props a->id = id; a->baseid = id; // default a->raceclass = findraceclass(raceclass); a->material = findmaterial(mat); assert(a->material); a->name = strdup(name); a->desc = strdup(desc); a->mass = weight; a->glyph.ch = glyph; a->glyph.colour = glyphcolour; a->known = B_FALSE; a->nbodyparts = 0; a->flags = addflagpile(NULL, NULL); return a; } raceclass_t *addraceclass(enum RACECLASS id, char *name, char *pluralname, enum SKILL skill) { raceclass_t *a; assert(!findraceclass(id)); // add to the end of the list if (firstraceclass == NULL) { firstraceclass = malloc(sizeof(raceclass_t)); a = firstraceclass; a->prev = NULL; } else { // go to end of list a = lastraceclass; a->next = malloc(sizeof(raceclass_t)); a->next->prev = a; a = a->next; } lastraceclass = a; a->next = NULL; // props a->id = id; a->name = strdup(name); a->pluralname = strdup(pluralname); a->skill = skill; return a; } void addskillabil(enum SKILL id, enum SKILLLEVEL lev, enum OBTYPE abilid, int timeout, char *text, int announce) { skill_t *sk; sk = findskill(id); assert(sk); assert(sk->nskillwills < MAXSKILLWILLS); if (announce) { objecttype_t *ot; char buf[BUFLEN]; ot = findot(abilid); assert(ot); snprintf(buf, BUFLEN, "^gYou gain the '%s' ability.^n", ot->name); addskilldesc(sk->id, lev, buf, B_FALSE); } sk->skillwill[sk->nskillwills].lev = lev; sk->skillwill[sk->nskillwills].abilid = abilid; sk->skillwill[sk->nskillwills].timeout = timeout; if (text) { sk->skillwill[sk->nskillwills].text = strdup(text); } else { sk->skillwill[sk->nskillwills].text = NULL; } sk->nskillwills++; } // rid is optional, can be R_NONE void addraceclassflags(flagpile_t *fp, enum RACECLASS rcid, enum RACE rid) { flag_t *f; if (rcid == RC_AQUATIC) { addflag(fp, F_HASSKILL, SK_SWIMMING, PR_MASTER, NA, NULL); addflag(fp, F_AQUATIC, B_TRUE, NA, NA, NULL); addflag(fp, F_BREATHWATER, B_TRUE, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_WATER, NA, NA, NULL); addflag(fp, F_STABILITY, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); } else if (rcid == RC_DEMON) { addflag(fp, F_NOBREATH, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_MATVULN, MT_SILVER, 200, 6, NULL); addflag(fp, F_NOFLEE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(fp, F_SEEINVIS, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); } else if (rcid == RC_INSECT) { addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_STABILITY, B_TRUE, NA, NA, NULL); addflag(fp, F_DTRESIST, DT_COLD, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); } else if (rcid == RC_DRAGON) { // wyrms hate hydras if (rid != R_HYDRA) { addflag(fp, F_HATESRACE, R_HYDRA, NA, NA, NULL); } addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); } else if (rcid == RC_GOD) { addflag(fp, F_NOBREATH, B_TRUE, NA, NA, NULL); addflag(fp, F_PIETY, 100, NA, NA, NULL); addflag(fp, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(fp, F_MORALE, 30, NA, NA, NULL); addflag(fp, F_BREATHWATER, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_POISON, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL); addflag(fp, F_FLEEONHPPCT, 20, NA, NA, NULL); addflag(fp, F_RESISTMAG, 15, NA, NA, NULL); addflag(fp, F_MEDITATES, B_TRUE, NA, NA, NULL); addflag(fp, F_SEEINDARK, 10, NA, NA, NULL); addflag(fp, F_SEEINVIS, B_TRUE, NA, NA, NULL); addflag(fp, F_CANWILL, OT_S_PLANESHIFT, NA, NA, "pw:1;"); addflag(fp, F_FILLPOT, OT_POT_AMBROSIA, NA, NA, NULL); addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_GIFTTIMER, 0, 50, NA, NULL); addflag(fp, F_NONAUSEA, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); } else if (rcid == RC_MAGIC) { addflag(fp, F_NOBREATH, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_NONAUSEA, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); } else if (rcid == RC_PLANT) { addflag(fp, F_NOKO, B_TRUE, NA, NA, NULL); addflag(fp, F_GETKILLEDVERB, NA, NA, NA, "destroy"); addflag(fp, F_NOBREATH, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_DTRESIST, DT_BASH, NA, NA, NULL); addflag(fp, F_DTVULN, DT_FIRE, NA, NA, NULL); addflag(fp, F_DTVULN, DT_COLD, NA, NA, NULL); addflag(fp, F_DTVULN, DT_DECAY, NA, NA, NULL); addflag(fp, F_FLAMMABLE, PERMENANT, NA, NA, NULL); addflag(fp, F_NOFLEE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_NOINJURIES, B_TRUE, NA, NA, NULL); addflag(fp, F_DAYBOOST, 10, NA, NA, NULL); addflag(fp, F_NONAUSEA, B_TRUE, NA, NA, NULL); addflag(fp, F_AWARENESS, B_TRUE, NA, NA, NULL); } else if (rcid == RC_SLIME) { addflag(fp, F_NOBREATH, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOFLEE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOINJURIES, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_NONAUSEA, B_TRUE, NA, NA, NULL); addflag(fp, F_NOTAKECRITS, B_TRUE, NA, NA, NULL); addflag(fp, F_TREMORSENSE, 5, NA, NA, NULL); } else if (rcid == RC_ROBOT) { addflag(fp, F_NOKO, B_TRUE, NA, NA, NULL); addflag(fp, F_GETKILLEDVERB, NA, NA, NA, "destroy"); addflag(fp, F_NOFLEE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOBREATH, B_TRUE, NA, NA, NULL); addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(fp, F_NOINJURIES, B_TRUE, NA, NA, NULL); addflag(fp, F_BREATHWATER, B_TRUE, NA, NA, NULL); addflag(fp, F_DTVULN, DT_ELECTRIC, NA, NA, NULL); addflag(fp, F_DTVULN, DT_WATER, NA, NA, NULL); addflag(fp, F_CORPSETYPE, NA, NA, NA, "unstable power core"); addflag(fp, F_MORALE, 30, NA, NA, NULL); addflag(fp, F_SEEINDARK, B_TRUE, NA, NA, NULL); addflag(fp, F_BLOODOB, NA, NA, NA, "puddle of oil"); addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSMELL, B_TRUE, NA, NA, NULL); if (!hasflagval(fp, F_NOISETEXT, N_WALK, NA, NA, NULL)) { addflag(fp, F_NOISETEXT, N_WALK, 2, NA, "^whirring"); } if (!hasflagval(fp, F_NOISETEXT, N_GETANGRY, NA, NA, NULL)) { addflag(fp, F_NOISETEXT, N_GETANGRY, 3, NA, "beeps^a beep"); } // will probably also be immune to fire/cold because they are metal } else if (rcid == RC_UNDEAD) { addflag(fp, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_COLD, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_POISON, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_DECAY, NA, NA, NULL); addflag(fp, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL); addflag(fp, F_NOTAKECRITS, B_TRUE, NA, NA, NULL); addflag(fp, F_NOBREATH, B_TRUE, NA, NA, NULL); addflag(fp, F_NORESTHEAL, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSTAM, B_TRUE, NA, NA, NULL); addflag(fp, F_NONAUSEA, B_TRUE, NA, NA, NULL); // +/- 15 accuracy during day/night addflag(fp, F_NIGHTBOOST, 15, NA, NA, NULL); addflag(fp, F_DAYBOOST, -15, NA, NA, NULL); switch (rid) { case R_SKELETONFIRE: f = hasflagval(fp, F_DTIMMUNE, DT_COLD, NA, NA, NULL); if (f) killflag(f); break; case R_MUMMYG: break; default: addflag(fp, F_DTVULN, DT_HOLY, NA, NA, NULL); break; } addflag(fp, F_SEEINDARK, B_TRUE, NA, NA, NULL); addflag(fp, F_NOFLEE, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSLEEP, B_TRUE, NA, NA, NULL); addflag(fp, F_NOTALK, B_TRUE, NA, NA, NULL); addflag(fp, F_NOSTAIRS, B_TRUE, NA, NA, NULL); } } skill_t *addskill(enum SKILL id, char *name, char *desc, int traintime) { skill_t *a,*temp; int count = 0; assert(!findskill(id)); for (temp = firstskill ; temp ; temp = temp->next) { count++; } assert(count < MAXSKILLS); // add to the end of the list if (firstskill == NULL) { firstskill = malloc(sizeof(skill_t)); a = firstskill; a->prev = NULL; } else { // go to end of list a = lastskill; a->next = malloc(sizeof(skill_t)); a->next->prev = a; a = a->next; } lastskill = a; a->next = NULL; // props a->id = id; a->name = strdup(name); a->shortname = strdup(name); // if (strstr(a->shortname, "Sorcery:")) { strrep(&(a->shortname), "Sorcery:", "", NULL); } a->desc = strdup(desc); a->traintime = traintime; a->nskilldesc = 0; a->nskillwills = 0; return a; } void addskilldesc(enum SKILL id, enum SKILLLEVEL lev, char *text, int wantmsg) { skill_t *sk; sk = findskill(id); assert(sk); assert(sk->nskilldesc < MAXSKILLDESC); sk->skilldesclev[sk->nskilldesc] = lev; sk->skilldesctext[sk->nskilldesc] = strdup(text); sk->skilldescmsg[sk->nskilldesc] = wantmsg; sk->nskilldesc++; } /* subjob_t *addsubjob(enum SUBJOB id, char *name, char *desc, char letter) { subjob_t *a; // add to the end of the list if (firstsubjob == NULL) { firstsubjob = malloc(sizeof(subjob_t)); a = firstsubjob; a->prev = NULL; } else { // go to end of list a = lastsubjob; a->next = malloc(sizeof(subjob_t)); a->next->prev = a; a = a->next; } lastsubjob = a; a->next = NULL; // props a->id = id; a->name = strdup(name); a->desc = strdup(desc); a->letter = letter; return a; } */ object_t *addtrail(lifeform_t *lf, cell_t *where, int dir, int doprints, int doscents) { object_t *footprint, *scent,*retob = NULL; flag_t *fpflag = NULL; //checkflagpile_maplfs(lf->cell->map); // no tracks at all? if (where->type->solid) { return NULL; } else if (hasobwithflag(where->obpile, F_DEEPWATER)) { return NULL; } if (lfhasflag(lf, F_NOPRINTS)) { doprints = B_FALSE; } if (getraceclass(lf) == RC_ROBOT) { doscents = B_FALSE; } // footprints first if (doprints) { if (!isairborne(lf, NULL) && !lfhasflag(lf, F_NONCORPOREAL)) { int fpdir; if (getskill(lf, SK_PERCEPTION) >= PR_EXPERT) { // no footprints! return NULL; } else { fpdir = dir; } //checkflagpile_maplfs(lf->cell->map); footprint = hastrailof(where->obpile, lf, OT_FOOTPRINT, &fpflag, NULL); if (footprint) { assert(fpflag); fpflag->lifetime = getfootprinttime(lf); fpflag->val[1] = fpdir; } else { char buf[BUFLENTINY]; snprintf(buf, BUFLENTINY, "%d", lf->id); //checkflagpile_maplfs(lf->cell->map); footprint = addobfast(where->obpile, OT_FOOTPRINT); //checkflagpile_maplfs(lf->cell->map); addtempflag(footprint->flags, F_TRAIL, lf->race->id, fpdir, S_SIGHT, buf, getfootprinttime(lf)); //checkflagpile_maplfs(lf->cell->map); } retob = footprint; } } // now smell if (doscents) { scent = hastrailof(where->obpile, lf, OT_SCENT, &fpflag, NULL); if (scent) { assert(fpflag); fpflag->lifetime = TM_SCENT; } else { char buf[BUFLENTINY]; snprintf(buf, BUFLENTINY, "%d", lf->id); //checkflagpile_maplfs(lf->cell->map); scent = addobfast(where->obpile, OT_SCENT); assert(scent); //checkflagpile_maplfs(lf->cell->map); addtempflag(scent->flags, F_TRAIL, lf->race->id, dir, S_SMELL, buf, TM_SCENT); //checkflagpile_maplfs(lf->cell->map); } retob = scent; } //checkflagpile_maplfs(lf->cell->map); return retob; } // will never make stats worse unelss allowloss is true. void adjustmonk(lifeform_t *lf, int allowloss) { flag_t *f; f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL); //f = lfhasflag(lf, F_HASATTACK); if (f) { int newdr; newdr = getmonkdr(lf->level); if (newdr < f->val[1]) { if (allowloss) { f->val[1] = newdr; if (isplayer(lf)) msg("^%cYour unarmed attack damage has decreased!", getlfcol(lf, CC_VBAD)); } else { // do nothing - your unarmed attack was already stronger or // equivilant. } } else if (newdr > f->val[1]) { f->val[1] = newdr; if (isplayer(lf)) msg("^%cYour unarmed attack damage has increased!", getlfcol(lf, CC_GOOD)); } } // enhance # attacks f = lfhasflag(lf, F_MAXATTACKS); if (f) { int min,max; getmonkattacks(lf->level, &min, &max); if ((min < f->val[0]) || (max < f->val[1])) { if (allowloss) { f->val[0] = min; f->val[1] = max; if (isplayer(lf)) msg("^%cYour number of unarmed attacks has decreased!", getlfcol(lf, CC_VBAD)); } } else if ((min > f->val[0]) || (max > f->val[1])) { f->val[0] = min; f->val[1] = max; if (isplayer(lf)) msg("^%cYour number of unarmed attacks has increased!", getlfcol(lf, CC_GOOD)); } } } void adjustspeedforwater(lifeform_t *lf, int *speed) { object_t *o; flag_t *f; if (!isairborne(lf, NULL)) { for (o = lf->cell->obpile->first ; o ; o = o->next) { f = hasflag(o->flags, F_DEEPWATER); if (f) { int modamt; modamt = (f->val[0] / 5); // ie. 0 - 4 if (modamt > 4) modamt = 4; // water if (isaquatic(lf)) { modamt = 0; } else { switch (getskill(lf, SK_SWIMMING)) { default: case PR_NOVICE: case PR_INEPT: break; // normal penalty case PR_BEGINNER: modamt -= 2; if (modamt < 0) modamt = 0; break; // bit less case PR_ADEPT: modamt = 0; break; // nothing case PR_SKILLED: modamt = -1; break; // faster case PR_EXPERT: modamt = -2; break; // faster case PR_MASTER: modamt = -2; break; // faster } } limit(&modamt, 0, 5); *speed += (modamt * SPEEDUNIT); } } } } void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype) { flag_t *f; if (isimmuneto(lf->flags, damtype, B_FALSE)) { *amt = 0; return; } if ((damtype == DT_MAGIC) && getmr(lf) && skillcheck(lf, SC_RESISTMAG, 100 + (*amt * 2), 0)) { *amt = 0; return; } // water normally doesn't hurt. if ((damtype == DT_WATER) && !isvulnto(lf->flags, damtype, B_FALSE)) { *amt = 0; return; } if ((damtype == DT_SONIC) && isdeaf(lf)) { *amt = 0; return; } if (lfhasflag(lf, F_INVULNERABLE)) { switch (damtype) { case DT_DIRECT: case DT_NONE: break; default: *amt = 0; break; } return; } if ((damtype == DT_BASH) && ismadeofice(lf)) { (*amt) *= 2; } if ((damtype == DT_ELECTRIC) && hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { (*amt) *= 2; } if ((damtype == DT_COLD) && hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { (*amt) = pctof(150, *amt); } if ((damtype == DT_FALL) && lfhasflag(lf, F_GRAVLESSENED)) { *amt = 0; } if (lfhasflag(lf, F_FROZEN)) { if (basedamagetype(damtype) == DT_FIRE) { // extra damage (*amt) = pctof(150,*amt); } else if (basedamagetype(damtype) == DT_COLD) { *amt = 0; } } if (isresistantto(lf->flags, damtype, B_FALSE)) { (*amt) /= 2; } f = isvulnto(lf->flags, damtype, B_FALSE); if (f) { if ((*amt == 0) && strlen(f->text)) { int ndice,nsides,bonus; texttodice(f->text, &ndice,&nsides,&bonus); *amt = rolldie(ndice,nsides) + bonus; } else { (*amt) *= 2; } } // don't adjust for lifeform material - we inherit all the material's flags. //adjustdammaterial((unsigned int *)amt, damtype, getlfmaterial(lf)); adjustdamhardness(amt, damtype, getlfmaterial(lf)); f = lfhasflag(lf, F_DRUNK); if (f) { *amt -= rnd(1,f->val[0]); } // gods can't die unless they are in the realm of gods if (lf->race->raceclass->id == RC_GOD) { int damok = B_FALSE; if (lf->cell->map->habitat->id == H_HEAVEN) { if (hasob(player->pack, getrelatedgodstone(lf->race->id)) ) { damok = B_TRUE; } } if (!damok) { // immortal limit(amt, 0, lf->hp-1); } } else { limit(amt, 0, NA); } } void makepeaceful(lifeform_t *who, lifeform_t *causedby) { char lfname[BUFLEN]; getlfname(who, lfname); if (lfhasflag(who, F_DEBUG)) { msg("Making %s friendly.",lfname); } if (lfhasflag(who, F_HOSTILE)) { if (cansee(player, who)) { char lfname[BUFLEN]; getlfname(who, lfname); msg("%s calms down.", lfname); } } if (causedby && isplayer(causedby)) { angergodmaybe(R_GODBATTLE, 5, GA_COWARD); pleasegodmaybe(R_GODMERCY, 10); } addflag(who->flags, F_XPVAL, 0, NA, NA, NULL); killflagsofid(who->flags, F_HOSTILE); killflagsofid(who->flags, F_HATESALL); killflagsofid(who->flags, F_HATESRACE); killflagsofid(who->flags, F_TERRITORIAL); killflagsofid(who->flags, F_HATESRACEWITHFLAG); loseaitargets(who); } // 'power' is 0-10, determines how poewrful the zombie is. // if 0, don't do any special changes. lifeform_t *makezombie(object_t *o, int power, char *description, lifeform_t *master) { flag_t *f; race_t *r; lifeform_t *lf; cell_t *where,*origwhere; char obname[BUFLEN]; if (o->type->id != OT_CORPSE) return NULL; f = hasflag(o->flags, F_CORPSEOF); if (!f) return NULL; r = findrace(f->val[0]); if (!r) return NULL; origwhere = getoblocation(o); where = origwhere; getobname(o, obname, 1); if (!cellwalkable(NULL, where, NULL)) { where = getrandomadjcell(where, &ccwalkable, B_ALLOWEXPAND); } if (!where) return NULL; lf = addlf(where, r->id, 1); addflag(lf->flags, F_LFSUFFIX, B_TRUE, NA, NA, "zombie"); killflagsofid(lf->flags, F_GLYPH); addflag(lf->flags, F_GLYPH, C_BLUE, 'Z', NA, NULL); addflag(lf->flags, F_UNDEAD, B_TRUE, NA, NA, NULL); killflagsofid(lf->flags, F_CORPSETYPE); killflagsofid(lf->flags, F_EXTRACORPSE); addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); killflagsofid(lf->flags, F_DTIMMUNE); killflagsofid(lf->flags, F_DTVULN); killflagsofid(lf->flags, F_DTRESIST); addraceclassflags(lf->flags, RC_UNDEAD, R_NONE); // less night/day penalty/bonus for raised undead. killflagsofid(lf->flags, F_DAYBOOST); killflagsofid(lf->flags, F_NIGHTBOOST); if (hasflag(o->flags, F_HEADLESS)) { // remove the head addflag(lf->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL); // remove the eyes (this will make the creature blind) addflag(lf->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL); // need HEADLESS too to show that this monster normally // _does_ have a head. this will cause getlfname // to add "headless " to the description. addflag(lf->flags, F_HEADLESS, B_TRUE, NA, NA, NULL); } killflagsofid(lf->flags, F_XPVAL); addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL); killflagsofid(lf->flags, F_WANTSBETTERWEP); killflagsofid(lf->flags, F_WANTSBETTERARM); killflagsofid(lf->flags, F_WANTSOBFLAG); killflagsofid(lf->flags, F_WANTS); killflagsofid(lf->flags, F_NOISETEXT); killflagsofid(lf->flags, F_SEEINDARK); killflagsofid(lf->flags, F_TREMORSENSE); killflagsofid(lf->flags, F_TERRITORIAL); killflagsofid(lf->flags, F_MOVESPEED); addflag(lf->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL); killflagsofid(lf->flags, F_ACTIONSPEED); addflag(lf->flags, F_ACTIONSPEED, SP_SLOW, NA, NA, NULL); lf->baseatt[A_IQ] = rollattr(IQ_MINDLESS); lf->att[A_IQ] = lf->baseatt[A_IQ]; killflagsofid(lf->flags, F_REVIVETIMER); // no magic lf->maxmp = 0; lf->mp = 0; // no objects while (lf->pack->first) { killob(lf->pack->first); } // no abilities killflagsofid(lf->flags, F_CANCAST); killflagsofid(lf->flags, F_CANWILL); // modify max hp based on power // need at least 2-4. if (power > 0) { lf->maxhp = pctof(100 + (10*power), lf->maxhp); //limit(&(lf->maxhp), rnd(2,4), NA); lf->hp = lf->maxhp; } // remove the object removeob(o,o->amt); // redraw & announce if (haslos(player, where) || haslos(player, origwhere)) { needredraw = B_TRUE; drawscreen(); msg("^W%s %s!", obname, description); } if (master) { petify(lf, master); } return lf; } int areenemies(lifeform_t *lf1, lifeform_t *lf2) { reason = E_OK; if (ispetof(lf1, lf2) || ispetof(lf2, lf1)) return B_FALSE; if (!isplayer(lf1) && lfhasflagval(lf1, F_TARGETLF, lf2->id, NA, NA, NULL)) return B_TRUE; if (!isplayer(lf2) && lfhasflagval(lf2, F_TARGETLF, lf1->id, NA, NA, NULL)) return B_TRUE; if (hasjob(lf1, J_DRUID) && (getraceclass(lf2) == RC_PLANT)) { return B_FALSE; } else if (hasjob(lf2, J_DRUID) && (getraceclass(lf1) == RC_PLANT)) { return B_FALSE; } switch (getallegiance(lf1)) { case AL_HOSTILE: switch (getallegiance(lf2)) { case AL_HOSTILE: return B_FALSE; case AL_PEACEFUL: return B_FALSE; case AL_FRIENDLY: return B_TRUE; } break; case AL_PEACEFUL: return B_FALSE; case AL_FRIENDLY: switch (getallegiance(lf2)) { case AL_HOSTILE: return B_TRUE; case AL_PEACEFUL: return B_FALSE; case AL_FRIENDLY: return B_FALSE; } break; } return B_TRUE; } void age(lifeform_t *lf, int pct) { if (hasjob(lf, J_GOD)) return; lf->maxhp -= pctof(pct,lf->maxhp); limit(&lf->maxhp, 0, NA); limit(&lf->hp, NA, lf->maxhp); if (isplayer(lf)) { msg("^BYour body ages unnaturally!"); statdirty = B_TRUE; } } void adjustdamforblessings(lifeform_t *attacker, int *dam, lifeform_t *victim, int blessed) { if ((blessed == B_BLESSED) && lfhasflagval(victim, F_DTVULN, DT_HOLY, NA, NA, NULL)) { // a little extra damage *dam = pctof(125, *dam); } } void applylfdammod(int *dam, lifeform_t *lf, object_t *wep) { flag_t *retflag[MAXCANDIDATES]; int nretflags = 0,i; enum SKILLLEVEL slev; // modify for strength if (!hasflag(wep->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) { *dam = *dam + getstrdammod(lf); } // modify for generic f_extradam getflags(lf->flags, retflag, &nretflags, F_WOUNDING, F_NONE); for (i = 0; i < nretflags; i++) { *dam += retflag[i]->val[0]; } // strength scaling on weapon getflags(wep->flags, retflag, &nretflags, F_ATTREQ, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[0] == A_STR) { int pctmod; meetsattreq(lf, retflag[i], wep, &pctmod); if (pctmod != 0) { *dam = pctof(100 + pctmod, *dam); } } } // modify for injuries getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { switch (retflag[i]->val[0]) { case IJ_TORSOBRUISEDBAD: *dam = pctof(90, *dam); break; } } // extra damage for being skilled? slev = getweaponskill(lf, wep); if (slev >= 3) { float pctextra; pctextra = ((slev - 2) * 10); *dam += pctof(pctextra, *dam); } } void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o, int bodypart) { flag_t *fromlfflag; lifeform_t *fromlf = NULL; char damstring[BUFLEN],buf[BUFLEN]; getobname(o, buf, o->amt); fromlfflag = hasflag(o->flags, F_CREATEDBY); if (fromlfflag) { snprintf(damstring, BUFLEN, "%s^created by %s",buf, fromlfflag->text); } else { strcpy(damstring, buf); } dam = losehp_bp(lf, dam, damtype, fromlf, damstring, bodypart); if (dam > 0) { if (damtype == DT_POISONGAS) { if (isplayer(lf) || cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s choke%s on %s!", getlfcol(lf, CC_BAD), lfname, isplayer(lf) ? "" : "s", buf); } } else { if (isplayer(lf)) { msg("^b%s %ss you!", buf, getattackverb(NULL, NULL, damtype, dam,lf->maxhp)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; char buf2[BUFLEN]; getlfname(lf, lfname); snprintf(buf2, BUFLEN, "^%c%s %ss %s!", getlfcol(lf, CC_BAD), buf, getattackverb(NULL, NULL, damtype, dam,lf->maxhp), lfname); msg("%s", buf2); } } } } void apply_wep_tr_limit(lifeform_t *lf, condset_t *cs) { if (!isplayer(lf)) { object_t *wep[MAXCANDIDATES]; flag_t *dflag[MAXCANDIDATES]; int nweps = 0,i; int tr,maxdr = NA,thresh = 0; obpile_t *op = NULL; // generate a set of conditions for this lf's weapon. // this is basically to prevent lowlevel monsters from // starting with super strong weapon! getweapons(lf, B_MELEEONLY, wep, dflag, NULL, &op,&nweps); // get highest damage innate attack for (i = 0; i < nweps; i++) { if (dflag[i]->val[1] > maxdr) maxdr = dflag[i]->val[1]; } if (op) { killobpile(op); } tr = gettr(lf); if (ISINRANGE(tr,0,4)) { //getgoodnessdr(G_AVERAGE, NULL, &maxdr); thresh = 2; } else if (ISINRANGE(tr,5,7)) { //getgoodnessdr(G_GOOD, NULL, &maxdr); thresh = 4; } else if (ISINRANGE(tr,8,10)) { //getgoodnessdr(G_EXCELLENT, NULL, &maxdr); thresh = 6; } else { thresh = 20; // basically unlimited } if (maxdr != NA) { // can go a little higher, but not too much. addcond(cs, CC_MAXDR, B_TRUE, maxdr+2); } } } int areallies(lifeform_t *lf1, lifeform_t *lf2) { int master1,master2; master1 = getmasterid(lf1); master2 = getmasterid(lf2); if ((master1 == master2) && (master1 != -1)) { // both pets of the same lf return B_TRUE; } else if (getallegiance(lf1) == getallegiance(lf2)) { // same allegience? if (isplayer(lf1) || isplayer(lf2)) { // if one of them is the player return B_TRUE; } else if (ispetof(lf1, player)) { // both pets of the player return B_TRUE; } else { if (lf1->race->baseid == lf2->race->baseid) { return B_TRUE; } } } else { if ((lf1->race->baseid == lf2->race->baseid) && // same base race !isplayer(lf1) && !isplayer(lf2) && // not the player (master1 == -1) && (master2 == -1)) { // not charmed/zombies/etc return B_TRUE; } } return B_FALSE; } int armourfits(lifeform_t *lf, object_t *o, enum ERROR *reason) { enum LFSIZE lfsize,armsize; lfsize = getlfsize(lf); armsize = getarmoursize(o); if (armsize != SZ_ANY) { if (armsize > lfsize) { if (reason) *reason = E_TOOBIG; return B_FALSE; } else if (armsize < lfsize) { if (reason) *reason = E_TOOSMALL; return B_FALSE; } } return B_TRUE; } // returns TRUE if 'lf' agrees to share knowledge with the player int askforinfo(lifeform_t *lf, int diffmod) { int alignmod = 0; alignmod = getalignmod(lf); if (lfhasflag(lf, F_NOINFO)) { // refusing to give info sayphrase(lf, SP_INFO_REFUSE_AGAIN, SV_TALK, NA, NULL, player); return B_FALSE; } else { int doinfo = B_FALSE; int askingprice = -1; char lfname[BUFLEN]; char buf[BUFLEN]; flag_t *f; getlfname(lf, lfname); if (areallies(player, lf)) { askingprice = 0; } else { // they will consider it - now negotiate a price f = lfhasflag(lf, F_INFOPRICE); if (f) { // already got a price in mind? askingprice = f->val[0]; } else { int result; int difficulty; int greedy = B_FALSE; // delvers are greedy, so they'll nearly always help, but // nearly always want money too! if ((lf->race->id == R_DWARF) && (player->race->id != R_DWARF)) { greedy = B_TRUE; diffmod += 15; } difficulty = 70 + diffmod + ((gettr(player) - gettr(lf))*2); if (real_skillcheck(player, SC_SPEECH, difficulty, alignmod, &result)) { askingprice = 0; // passed - free! } else { if (!greedy && !real_skillcheck(player, SC_SPEECH, difficulty/2, alignmod, &result)) { // will not help! askingprice = -1; } else { // will help for gold askingprice = rnd(gettr(lf)*5, gettr(lf)*15 ); limit(&askingprice, 1, NA); // just in case } } if (askingprice == -1) { addflag(lf->flags, F_NOINFO, B_TRUE, NA, NA, NULL); sayphrase(lf, SP_INFO_REFUSE, SV_TALK, NA, NULL, player); return B_FALSE; } else { addflag(lf->flags, F_INFOPRICE, askingprice, NA, NA, NULL); } } } if (askingprice != 0) { // modify for same job if (getjob(player) == getjob(lf)) { askingprice = pctof(50, askingprice); } else if (player->race->baseid == lf->race->baseid) { // modify for same race askingprice = pctof(80, askingprice); } // modify by charisma askingprice = pctof(100 - getstatmod(player, A_CHA), askingprice); limit(&askingprice, 0, NA); } if (askingprice > 0) { char ch; sayphrase(lf, SP_INFO_ASKPRICE, SV_TALK, askingprice, NULL, player); more(); if (askingprice > countmoney(player->pack)) { msg("You can't afford to pay $%d.", askingprice); return B_FALSE; } else { snprintf(buf, BUFLEN, "Pay $%d for information", askingprice); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); if (ch == 'y') { doinfo = B_TRUE; } } } else { doinfo = B_TRUE; } if (doinfo) { sayphrase(lf, SP_INFO_ACCEPT, SV_TALK, NA, NULL, player); return B_TRUE; } else { sayphrase(lf, SP_INFO_DECLINE_WONTPAY, SV_TALK, askingprice, NULL, player); } } // end if !nohire return B_FALSE; } /* int askforpayment(lifeform_t *shk, lifeform_t *lf) { char saybuf[BUFLEN]; int totcost = 0; int nitems,shopid; flag_t *f; f = lfhasflag(shk, F_OWNSSHOP); if (f) { shopid = f->val[0]; } else { return B_TRUE; } totcost = getowing(lf, shopid, &nitems); if (nitems == 1) { snprintf(saybuf, BUFLEN, "That will cost you $%d.", totcost); } else { snprintf(saybuf, BUFLEN, "That brings your bill to $%d.", totcost); } say(shk, saybuf, SV_TALK); return B_FALSE; } */ char *assignnpcname(flagpile_t *fp) { npcname_t *sel; int *poss; int nposs = 0,i; poss = malloc(numnpcnames * sizeof(int)); // already got one? if (hasflag(fp, F_NAME)) { return NULL; } // count possibilities for (i = 0;i < numnpcnames; i++) { if (npcname[i].valid) { poss[nposs++] = i; } } // get random name i = poss[rnd(0,nposs-1)]; sel = &npcname[i]; // none else can use this name now sel->valid = B_FALSE; addflag(fp, F_NAME, NA, NA, NA, sel->name); free(poss); return sel->name; } // is this attrib increasable on levelup? int attrincreasable(enum ATTRIB a) { switch (a) { case A_AGI: case A_CON: case A_IQ: case A_STR: case A_WIS: return B_TRUE; default: break; } return B_FALSE; } void autolearnspellsfrombook(lifeform_t *lf, object_t *book) { object_t *o; for (o = book->contents->first ; o ; o = o->next) { if ((getspellschoolknown(lf, o->type->id) != SS_NONE) && !hasflagval(lf->flags, F_CANCAST, o->type->id, NA, NA, NULL) && (getspellpower(lf, o->type->id) > 0)) { //addtempflag(lf->flags, F_CANCAST, o->type->id, NA, NA, NULL, FROMJOB); learnspell(lf, o->type->id, B_FALSE); //if (isplayer(lf)) { // autoshortcut(lf, o->type->id); //} } } } // If spellid = OT_NONE, automatically assign slots for all job/race/skill abilities, // starting at 'startslot' // // If spellid = , auto assign a shortcut just for that one. Use 'startslot' // if given, otherwise pick the next available one. // void autoshortcut(lifeform_t *lf, enum OBTYPE spellid, int startslot) { flag_t *retflag[MAXCANDIDATES],*f; int nretflags,i; int min = 0; objecttype_t *ot = NULL; if (startslot == NA) { min = getnextshortcut(lf); if (min == NA) return; } else { min = startslot; } if (min == 10) min = 0; limit(&min, 0, LASTSHORTCUT); getflags(lf->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE); for (i = 0; (i < nretflags) && (min <= LASTSHORTCUT); i++) { f = retflag[i]; ot = findot(f->val[0]); if (!ot) continue; // already have a shortcut for this? if (lfhasflagval(lf, F_SHORTCUT, NA, NA, NA, ot->name)) continue; if (spellid == OT_NONE) { // autoshortcut everything from your job/race if ((f->lifetime == FROMJOB) || (f->lifetime == FROMRACE) || (f->lifetime == FROMSKILL)) { // if we don't already have a shortcut for this... if (!lfhasflagval(lf, F_SHORTCUT, NA, NA, NA, ot->name)) { addflag(lf->flags, F_SHORTCUT, min, NA, NA, ot->name); dblog("added shortcut #%d = %s", min, ot->name); min = getnextshortcut(lf); dblog(" next shortcut is %d", min); if (min == NA) return; } } } else { // autoshortcut the given one only. if (f->val[0] == spellid) { // set to lowest possible shortcut addflag(lf->flags, F_SHORTCUT, min, NA, NA, ot->name); dblog("added shortcut #%d = %s", min, ot->name); dblog(" returning"); return; } } } } // make sure player has at least novice skill in all their start weapons/armour void autoskill(lifeform_t *lf) { skill_t *sk; object_t *o; enum SKILLLEVEL slev; int nweps = 0; flag_t *retflag[MAXCANDIDATES],*f; int nretflags,i; if (hasjob(lf, J_COMMANDO)) { return; } if (isplayer(lf)) { slev = PR_NOVICE; } else { int max; enum ATTRBRACKET iqb; iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); switch (iqb) { case AT_EXLOW: max = PR_NOVICE; break; case AT_VLOW: max = PR_NOVICE; break; case AT_LOW: max = PR_NOVICE; break; case AT_LTAVERAGE: max = PR_NOVICE; break; case AT_AVERAGE: max = PR_BEGINNER; break; case AT_GTAVERAGE: max = PR_BEGINNER; break; case AT_HIGH: max = PR_ADEPT; break; case AT_VHIGH: max = PR_ADEPT; break; case AT_EXHIGH: max = PR_SKILLED; break; default: max = PR_ADEPT; break; } limit(&max, PR_NOVICE, PR_MASTER); slev = rnd(PR_NOVICE, max); } for (o = lf->pack->first ; o ; o = o->next) { //if (isweapon(o) && canweild(lf, o)) { if (isweapon(o)) { sk = getobskill(o->flags); if (sk && !getskill(lf, sk->id)) { if ((sk->id == SK_SHORTBLADES) && hasjob(lf, J_DRUID)) { // special case. } else { giveskilllev(lf, sk->id, slev); } } // monsters:increase stats to match attribn requirements for starting // weapon. //if (!isplayer(lf)) { getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (getattr(lf, f->val[0]) < f->val[1]) { setattr(lf, f->val[0], f->val[1]); } } //} nweps++; } if (isfirearm(o) && canweild(lf, o)) { giveskilllev(lf, SK_RANGED, slev); } if (isarmour(o) && canwear(lf, o, BP_NONE)) { flag_t *f; // evasion penalty? f = hasflag(o->flags, F_EVASION); if (f && (f->val[0] < 0)) { giveskilllev(lf, SK_ARMOUR, slev); } } if (isshield(o) && canwear(lf, o, BP_NONE)) { giveskilllev(lf, SK_SHIELDS, slev); } } // monsters must get unarmed skill! if (!nweps && !isplayer(lf)) { giveskilllev(lf, SK_UNARMED, slev); } } // hand out random spells to monsters void autospells(lifeform_t *lf, int howmany) { int nretflags,i,nposs = 0; flag_t *retflag[MAXCANDIDATES]; enum OBTYPE poss[MAXCANDIDATES]; int powerposs[MAXCANDIDATES]; int mapdiff; mapdiff = getmapdifficulty(lf->cell->map); // get list of possible random spells for this lf. // set random spells? getflags(lf->flags, retflag, &nretflags, F_RNDSPELLPOSS, F_NONE); for (i = 0; i < nretflags; i++) { poss[nposs] = retflag[i]->val[0]; powerposs[nposs] = 1; // will be overridden texttospellopts(retflag[i]->text, "pw:", &(powerposs[nposs]), NULL); nposs++; } // random spells from schools? getflags(lf->flags, retflag, &nretflags, F_RNDSPELLSCHOOL, F_NONE); for (i = 0; i < nretflags; i++) { int min,max; enum SPELLSCHOOL ss; int thispower; objecttype_t *sp; ss = retflag[i]->val[0]; if (ss == SS_NONE) { ss = getrandomspellschool(NULL, B_FALSE); } min = retflag[i]->val[1]; max = retflag[i]->val[2]; texttospellopts(retflag[i]->text, "pw:", &thispower, NULL); for (sp = objecttype; sp ; sp = sp->next) { if ((sp->obclass->id == OC_SPELL) && spellokformonsters(sp->id)) { int lev; lev = getspelllevel(sp->id); if (spellisfromschool(sp->id, ss) && (lev >= min) && (lev <= max)) { poss[nposs] = sp->id; powerposs[nposs] = thispower; nposs++; } } } } // power = 0 means select it based on depth for (i = 0 ; i < nposs; i++) { if (powerposs[nposs] == 0) { // ooo TODO: use of unit value here ?? powerposs[nposs] = mapdiff; limit(&(powerposs[nposs]), 1, getspellmaxpower(poss[nposs])); } } // now we have a list of all possible spells. select from these. for (i = 0; (i < howmany) && nposs; i++) { int sel,n; enum OBTYPE spellid; char pwbuf[BUFLEN]; sel = rnd(0,nposs-1); spellid = poss[sel]; snprintf(pwbuf, BUFLEN, "pw:%d;",powerposs[sel]); //addflag(lf->flags, F_CANCAST, sel, NA, NA, pwbuf); addflag(lf->flags, F_CANCAST, spellid, NA, NA, pwbuf); // remove this one... for (n = sel; n < nposs-1; n++) { poss[n] = poss[n+1]; powerposs[n] = powerposs[n+1]; } nposs--; } } void autotarget(lifeform_t *lf) { object_t *gun; lifeform_t *targ = NULL,*newtarg = NULL; int closest; int i; int gunrange; int targid; gun = getfirearm(lf); if (gun && getammo(gun)) { gunrange = getfirearmrange(gun); // already got a target? targid = getguntargetid(lf); } else { gunrange = -1; targid = -1; } if (targid == -1) { // no existing target targ = NULL; } else { targ = findlf(NULL, targid); if (targ) { // dead? remove target and keep going. if (isdead(targ)) { // clear target ? targ = NULL; } else if (!cansee(lf, targ) || !haslof(lf->cell, targ->cell, getfirearmloftype(lf), NULL)) { // clear target ? targ = NULL; } else { flag_t *f; // already got a valid target. just update targetting text. f = hasflag(lf->flags, F_GUNTARGET); if (f) { char targbuf[BUFLEN]; makegunaimstring(lf, f->val[0], targbuf); if (!streq(targbuf, f->text)) { free(f->text); f->text = strdup(targbuf); if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); updatestatus(); } } } return; } } else { // target no longer exists // clear target targ = NULL; } } if (gun && (gunrange != -1)) { // find new target newtarg = NULL; closest = 9999; for (i = 0; i < lf->nlos; i++) { cell_t *c; c = lf->los[i]; if (c->lf && (c->lf != lf) && cansee(lf, c->lf) && isingunrange(lf, c) && areenemies(lf, c->lf)) { if (haslof(lf->cell, c, getfirearmloftype(lf), NULL)) { int thisdist; thisdist = getcelldist(lf->cell, c); if (thisdist < closest) { newtarg = c->lf; closest = thisdist; } } } } } if (newtarg != targ) { setguntarget(lf, newtarg); } if (!newtarg) { killflagsofid(lf->flags, F_GUNTARGET); } } int issmellablelf(lifeform_t *lf) { if (lf->race->raceclass->id != RC_UNDEAD) { return B_TRUE; } return B_FALSE; } int isswimming(lifeform_t *lf) { if (!lf) return B_FALSE; if (gamemode != GM_GAMESTARTED) { return B_FALSE; } if (!isairborne(lf, NULL) && (getcellwaterdepth(lf->cell, lf) >= DP_WAIST) && getskill(lf, SK_SWIMMING)) { return B_TRUE; } return B_FALSE; } int isunconscious(lifeform_t *lf) { if (lfhasflagval(lf, F_ASLEEP, NA, ST_KO, NA, NULL)) return B_TRUE; return B_FALSE; } int isundead(lifeform_t *lf) { if (lf->race->raceclass->id == RC_UNDEAD) { return B_TRUE; } if (lfhasflag(lf, F_UNDEAD)) { return B_TRUE; } return B_FALSE; } flag_t *isvulnto(flagpile_t *fp, enum DAMTYPE dt, int onlytemp) { flag_t *f; dt = basedamagetype(dt); f = hasflagval(fp, F_DTVULN, dt, NA, NA, NULL); if (f) { if (!onlytemp || (f->lifetime != FROMRACE)) { return f; } } f = hasflagval(fp, F_DTVULN, DT_ALL, NA, NA, NULL); if (f) { if (!onlytemp || (f->lifetime != FROMRACE)) { return f; } } return NULL; } int isweaponbp(enum BODYPART bp) { if ((bp == BP_WEAPON) || (bp == BP_SECWEAPON)) { return B_TRUE; } return B_FALSE; } int isweaponskill(enum SKILL skid) { switch (skid) { case SK_AXES: case SK_CLUBS: case SK_EXOTICWEPS: case SK_LONGBLADES: case SK_POLEARMS: case SK_SHORTBLADES: case SK_STAVES: case SK_WHIPS: //case SK_UNARMED: return B_TRUE; default: break; } return B_FALSE; } enum FLAG iswoozy(lifeform_t *lf) { if (lfhasflag(lf, F_DRUNK)) return F_DRUNK; if (lfhasflag(lf, F_CONFUSED)) return F_CONFUSED; if (lfhasflagval(lf, F_INJURY, IJ_CONCUSSION, NA, NA, NULL)) return F_INJURY; return F_NONE; } // check if this job is possible int jobpossible(flagpile_t *basefp, enum JOB jid) { int ok = B_FALSE; job_t *j; flagpile_t *fp; fp = addflagpile(NULL, NULL); // take lifeform/race's alignment restrictions copyflag(fp, basefp, F_ALIGNMENT); // take job's alignment restrictions j = findjob(jid); if (j) { copyflag(fp, j->flags, F_ALIGNMENT); } /* // take subjob's alignment restrictions add_subjob_alignment_restrictions(fp, sjid); */ if (genalignmentlist(fp, NULL)) { ok = B_TRUE; } killflagpile(fp); return ok; } void killjob(job_t *job) { job_t *nextone, *lastone; // free mem free(job->name); killflagpile(job->flags); // remove from list nextone = job->next; if (nextone != NULL) { nextone->prev = job->prev; } else { /* last */ lastjob = job->prev; } if (job->prev == NULL) { /* first */ nextone = job->next; free(job); job = nextone; } else { lastone = job->prev; free (lastone->next ); lastone->next = nextone; } } void killpoisontype(poisontype_t *pt) { poisontype_t *nextone, *lastone; // free mem free(pt->name); free(pt->desc); free(pt->contracttext); free(pt->damverb); // remove from list nextone = pt->next; if (nextone != NULL) { nextone->prev = pt->prev; } else { /* last */ lastpoisontype = pt->prev; } if (pt->prev == NULL) { /* first */ nextone = pt->next; free(firstpoisontype); firstpoisontype = nextone; } else { lastone = pt->prev; free (lastone->next ); lastone->next = nextone; } } void killrace(race_t *r) { race_t *nextone, *lastone; int i; // free mem free(r->name); free(r->desc); killflagpile(r->flags); for (i = 0; i < r->nbodyparts; i++) { free(r->bodypart[i].name); } // remove from list nextone = r->next; if (nextone != NULL) { nextone->prev = r->prev; } else { /* last */ lastrace = r->prev; } if (r->prev == NULL) { /* first */ nextone = r->next; free(firstrace); firstrace = nextone; } else { lastone = r->prev; free (lastone->next ); lastone->next = nextone; } } /* void killsubjob(subjob_t *sj) { subjob_t *nextone, *lastone; // free mem free(sj->name); free(sj->desc); // remove from list nextone = sj->next; if (nextone != NULL) { nextone->prev = sj->prev; } else { // last lastsubjob = sj->prev; } if (sj->prev == NULL) { // first nextone = sj->next; free(firstsubjob); firstsubjob = nextone; } else { lastone = sj->prev; free (lastone->next ); lastone->next = nextone; } } */ void learnspell(lifeform_t *lf, enum OBTYPE sid, int fromlevelup) { flag_t *ff; // learn the spell ff = addtempflag(lf->flags, F_CANCAST, sid, NA, NA, NULL, FROMJOB); assert(ff); if (fromlevelup) { ff->fromlev = lf->level; } } flag_t *levelabilityready(lifeform_t *lf) { flag_t *f; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags; if (!lfhasflag(lf, F_HASNEWLEVEL)) return NULL; getflags(lf->flags, retflag, &nretflags, F_LEVABIL, F_LEVFLAG, F_LEVSPELL, F_LEVSPELLSCHOOL, F_LEVSPELLSCHOOLFROMX, F_LEVSKILL, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; // we'll set lifetime to -1 while actually assigning these. if ((f->lifetime == FROMJOB) || (f->lifetime == FROMGODPIETY)) { switch (f->id) { case F_LEVFLAG: if (lf->newlevel == f->val[0]) { return f; } break; case F_LEVABIL: case F_LEVSKILL: case F_LEVSPELL: case F_LEVSPELLSCHOOL: case F_LEVSPELLSCHOOLFROMX: if ((f->val[0] < 100) && (lf->newlevel == f->val[0])) { return f; } else if ((f->val[0] >= 100) && (lf->newlevel % (f->val[0] - 100) == 0) ) { return f; } break; default: break; } } } return NULL; } int loadfirearm(lifeform_t *lf, object_t *gun, object_t *ammo) { int amttoload; int guncapacityleft,guncapacityused; char gunname[BUFLEN]; object_t *curammo; flag_t *f,*ammoflag; curammo = gun->contents->first; getobname(gun, gunname, 1); if (curammo) { guncapacityused = curammo->amt; } else { guncapacityused = 0; } ammoflag = hasflag(gun->flags, F_AMMOCAPACITY); guncapacityleft = ammoflag->val[0] - guncapacityused; // is ammo ok? if (curammo && (ammo->type->id != curammo->type->id)) { if (lf) { // unload current ammo first moveob(curammo, lf->pack, curammo->amt); if (lf && isplayer(lf)) { char buf[BUFLEN]; getobname(curammo, buf, curammo->amt); msg("You unload %s from your %s.", buf, noprefix(gunname)); } } else { return B_TRUE; } } else { if (guncapacityleft <= 0) { if (isplayer(lf)) { msg("Your %s is already fully loaded.", noprefix(gunname)); } return B_TRUE; } } if (ammo->amt >= guncapacityleft) { amttoload = guncapacityleft; } else { amttoload = ammo->amt; } // load ammo into gun moveob(ammo, gun->contents, amttoload); // take time if (lf) { if (getskill(lf, SK_RANGED) < PR_ADEPT) { f = hasflag(gun->flags, F_RELOADTURNS); taketime(lf, getactspeed(lf)*f->val[0]); } if (isplayer(lf)) { //char buf[BUFLEN]; //getobname(gun->contents->first, buf, gun->contents->first->amt); //msg("You load %s into your %s.", buf, noprefix(gunname)); msg("You reload your %s.", noprefix(gunname)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s reloads %s.", lfname, gunname); } // update target autotarget(lf); } return B_FALSE; } int loadfirearmfast(lifeform_t *lf, int onpurpose) { object_t *gun,*newammo,*curammo; gun = getfirearm(lf); if (!gun) { if (isplayer(lf) && onpurpose) { msg("You have no firearm equipped."); } return B_TRUE; } curammo = getammo(gun); if (curammo) { if (isplayer(lf) && onpurpose) { char buf[BUFLEN]; getobname(gun, buf, 1); msg("Your %s is already loaded!", noprefix(buf)); } return B_TRUE; } newammo = getrandomammo(lf); if (newammo) { loadfirearm(lf, gun, newammo); } else { if (isplayer(lf) && onpurpose) { char buf[BUFLEN]; getobname(gun, buf, 1); msg("You have no ammo for your %s.", noprefix(buf)); } return B_TRUE; } if ((gamemode == GM_GAMESTARTED) && onpurpose) taketime(lf, getactspeed(lf)); return B_FALSE; } void loseconcentration(lifeform_t *lf) { killflagsofid(lf->flags, F_FULLSHIELD); // stop sprinting stopsprinting(lf); // stop casting spells killflagsofid(lf->flags, F_CASTINGSPELL); // boost spells end stopallspells(lf); // stop hiding killflagsofid(lf->flags, F_HIDING); interrupt(lf); } void loseconsciousness(lifeform_t *lf, int howlong, lifeform_t *fromlf) { if (!isunconscious(lf)) { fallasleep(lf, ST_KO, howlong); if (fromlf && isplayer(fromlf)) { pleasegodmaybe(R_GODMERCY, 5); } } } // lose hp, and adjust damage based on resistances int losehp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc) { return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_DAMEFFECTS, BP_NONE, B_NOCRIT); } int losehp_noarm(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc) { return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_NODAMEFFECTS, BP_NONE, B_NOCRIT); } int losehp_bp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int bodypart) { return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_DAMEFFECTS, bodypart, B_NOCRIT); } // returns the amt of damage taken int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam, object_t *fromob, int retaliate, int *waskod, int doeffects, int bodypart, int fromcrit) { char buf[BUFLEN]; char buf2[BUFLEN]; char lfname[BUFLEN]; int prelowhp = B_FALSE,ko = B_FALSE; int murder = B_FALSE; flag_t *f; int predead = B_FALSE; int damreducedbyarm = 0; int hpleftafterdam; if (gamemode < GM_GAMESTARTED) return 0; getlfname(lf, lfname); if (isdead(lf)) { predead = B_TRUE; } if (isplayer(lf)) { statdirty = B_TRUE; } if (isbleeding(lf)) { prelowhp = B_TRUE; } if (fromlf && areallies(lf, fromlf)) { murder = B_TRUE; } if (doeffects) { // handle armour damreducedbyarm = handlearmour(lf, fromob, &amt, damtype); } // adjust for source object's material if (fromob) { if (lfhasflagval(lf, F_MATIMMUNE, fromob->material->id, NA, NA, NULL)) { amt = 0; } else { f = lfhasflagval(lf, F_MATVULN, fromob->material->id, NA, NA, NULL); if (f) { // will always do some damage if (!amt) amt = 1; amt = pctof(f->val[1], amt); // announce if (isplayer(lf)) { msg("The touch of %s sears you!", fromob->material->name); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("The touch of %s sears %s!", fromob->material->name, lfname); } } } } // adjust damage! if (reducedam) { adjustdamlf(lf, &amt, damtype); } // stop resting/running! interrupt(lf); // stop hiding killflagsofid(lf->flags, F_HIDING); // methods of knocking unconscious // would this damage reduce the lf to < 0 hp ??? hpleftafterdam = lf->hp - amt; if (lfcanbekod(lf) && (lf->hp > 1) && (hpleftafterdam <= 0)) { int threshold = -10,kochance = 0; // merciful weapons - these will ALWAYS ko, even if // they are already unconscious. if (!ko && fromob) { f = hasflag(fromob->flags, F_MERCIFUL); if (f && (amt >= lf->hp)) { kochance = 100; if (fromob->pile->owner && cansee(player, fromob->pile->owner)) { f->known = B_TRUE; } } } // bashing damage sometimes ko's // damage when eating corpses also does this. if (!ko && !isunconscious(lf)) { int damtypeok = B_FALSE; int playerinvolved = B_FALSE; if (isplayer(lf)) { playerinvolved = B_TRUE; } else if (fromlf && isplayer(fromlf)) { playerinvolved = B_TRUE; } if (damtype == DT_BASH) { damtypeok = B_TRUE; } else if ((damtype == DT_DIRECT) && fromob && hasflag(fromob->flags, F_EATCONFER)) { damtypeok = B_TRUE; } if (damtypeok) { if (playerinvolved && godprayedto(R_GODMERCY)) { threshold -= 10; if (isplayer(lf)) { // player being hit? kochance += 75; } else { // player hitting something else? kochance += 30; } } else { kochance += 15; // base chance to KO with bashing. } // TRYING to ko rather than kill? if (fromlf && lfhasflag(fromlf, F_STRIKETOKO)) { if (cansee(fromlf, lf)) { kochance += 50; threshold -= 15; } } } if (kochance && (hpleftafterdam >= threshold)) { if (pctchance(kochance)) { ko = B_TRUE; } } } /* if (!ko && !isunconscious(lf)) { if (fromlf && lfhasflag(fromlf, F_STRIKETOKO)) { if (cansee(fromlf, lf)) { ko = B_TRUE; } } } */ } // just knock them out, don't kill. if (ko) { amt = lf->hp - 1; // ie end up at 1hp } /* // large damage will be stopped by a ring of miracles if ((amt >= (lf->maxhp / 2)) && (amt >= 20)) { if (useringofmiracles(lf, 1)) { return 0; } } */ // drop blood if (damtypecausesbleed(damtype, fromob)) { bleed(lf, B_NOSPLATTER); } if (hasflag(lf->flags, F_DEBUG)) { msg("[%s takes %d dam]",lfname, amt); } // remember the amt of damage if (isplayer(lf)) { if (lf->bartimer == 2) { lf->damlastturn += amt; } else { lf->damlastturn = amt; lf->bartimer = 2; } } // phantasms have a fake hp counter f = lfhasflag(lf, F_PHANTASM); if (f) { f->val[0] -= amt; // they die when their FAKE hp drops below 0 if (f->val[0] <= 0) { setlastdam(lf, "disappation"); lf->lastdamtype = DT_DIRECT; lf->hp = 0; } } else { // take damage lf->hp -= amt; } if (!predead) { // fill in lastdam... lf->lastdamtype = damtype; if (fromlf) { lf->lastdamlf = fromlf->id; } else { lf->lastdamlf = -1; } } // if they died if (lf->hp <= 0) { // bleed some more. if (damtypecausesbleed(damtype, fromob)) { bleed(lf, B_NOSPLATTER); } if (fromlf && (getallegiance(fromlf) == AL_FRIENDLY)) { addflag(lf->flags, F_KILLEDBYPLAYER, B_TRUE, NA, NA, NULL); } } if (doeffects || ko) { losehpeffects(lf, amt, damtype, fromlf, fromob, retaliate, ko, waskod, prelowhp, bodypart, damreducedbyarm, fromcrit); } if ((lf->hp > 0) && !ko && fromlf && (getcelldist(lf->cell, fromlf->cell) > 1)) { int lifetimeinc; lifetimeinc = rnd(2,6); f = lfhasflag(lf, F_AIHITBYRANGED); if (f) { f->lifetime += lifetimeinc; } else { addtempflag(lf->flags, F_AIHITBYRANGED, B_TRUE, NA, NA, NULL, lifetimeinc); } } if (!predead) { ////////////////////////////////////////// // Figure out death text for tombstone. // eg. Killed by something // Impaled by something. // etc ////////////////////////////////////////// // replace 'the' at start of damsrc with 'a' if (strstr(damsrc, "the ") == damsrc) { int an; an = needan(damsrc+4); snprintf(buf, BUFLEN, "%s %s",an ? "an" : "a", (damsrc+4)); } else { strcpy(buf, damsrc); } // fill in damage amount snprintf(buf2, BUFLEN, "^%d damage",amt); strcat(buf, buf2); // unseen? if (fromlf && !cansee(lf, fromlf)) { strcat(buf, "^unseen"); } // "while xxx" strcpy(buf2, ""); if ((countcellexits(lf->cell, DT_COMPASS) == 1) && fromlf && (getcelldist(fromlf->cell, lf->cell) == 1)) { strcat(buf2, "^while cornered"); } if (!real_hasfreeaction(lf, F_DEAD)) { if (strlen(buf2)) strcat(buf2, " and helpless"); else strcat(buf2, "^while helpless"); } if (strlen(buf2)) strcat(buf, buf2); setlastdam(lf, buf); if (fromlf && willeatlf(fromlf, lf) && (!fromob || hasflag(fromob->flags, F_UNARMEDWEP)) ) { // this string is special - die() checks for this to see whether // to add "partially eaten" to the corpse. setkillverb(lf, "Eaten"); } else { switch (damtype) { case DT_COLD: setkillverb(lf, "Frozen"); break; case DT_CRUSH: setkillverb(lf, "Crushed"); break; case DT_ELECTRIC: setkillverb(lf, "Electrocuted"); break; case DT_FIRE: setkillverb(lf, "Incinerated"); break; case DT_HEAT: setkillverb(lf, "Scalded"); break; case DT_MELT: setkillverb(lf, "Melted"); break; case DT_PIERCE: setkillverb(lf, "Impaled"); break; case DT_EXPLOSIVE: setkillverb(lf, "Vaporised"); break; default: if (fromlf) { if (murder) { setkillverb(lf, "Murdered"); } else if ((amt >= lf->maxhp) && !fromob) { setkillverb(lf, "Slaughtered"); } else { setkillverb(lf, "Slain"); } } else { setkillverb(lf, "Killed"); } break; } } } // update screen drawstatus(); updatestatus(); f = lfhasflag(lf, F_SOULLINK); if (f && (damtype != DT_DIRECT)) { lifeform_t *otherlf; otherlf = findlf(lf->cell->map, f->val[0]); if (otherlf) { losehp_real(otherlf, amt, DT_DIRECT, lf, f->text, B_FALSE, NULL, B_FALSE, NULL, B_NODAMEFFECTS, BP_NONE, B_NOCRIT); } } return amt; } void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fromlf, object_t *fromob, int retaliate, int ko, int *waskod, int prelowhp, int bodypart, int damreducedbyarm, int crit) { int postlowhp = B_FALSE; flag_t *retflag[MAXCANDIDATES],*f,*magicarm = NULL; int nretflags; char buf[BUFLEN],lfname[BUFLEN]; getlfname(lf, lfname); // armour damage if ((dam > 0) && isphysicaldam(damtype)) { getflags(lf->flags, retflag, &nretflags, F_HEAVENARM, F_MAGICARMOUR, F_NONE); if (nretflags) { magicarm = retflag[0]; } } // now magic armour will pulse and maybe vanish. if (magicarm) { int damprevented; // prevent all damage if possible damprevented = dam; magicarm->val[0] -= damprevented; if (magicarm->val[0] <= 0) { // did magic armour come from a spell? if (magicarm->lifetime == FROMSPELL) { flag_t *spellflag; spellflag = hasactivespell(lf, magicarm->obfrom); if (spellflag) { killflag(spellflag); } } // magic armour vanishes now. killflag(magicarm); } else { if (cansee(player, lf)) { msg("^%d%s%s %s pulses!^n", CC_GOOD, lfname, getpossessive(lfname), magicarm->text); } } } else { // victim's armour loses hp. note that it loses the full // amount of damage dealt, not just what it reduced. if (damreducedbyarm && !crit) { applyarmourdamage(lf, fromob, dam + damreducedbyarm, damtype, fromlf); // train armour practice(lf, SK_ARMOUR, 1); } } if (lf->hp > 0) { // effects based on damage type, if lf is still alive if (damtype == DT_COLD) { int i; if (lfhasflag(lf, F_COLDBLOOD)) { // slow them addtempflag(lf->flags, F_SLOWMOVE, 5, NA, NA, NULL, 10); } // catch a cold? if (!skillcheck(lf, SC_CON, (dam/2) + (getexposedlimbs(lf)*20), 0)) { poison(lf, 20+(dam*3), P_COLD, 0, "the cold", fromlf ? fromlf->race->id : R_NONE, B_FALSE); } // cold will heal bruised limbs getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->lifetime > 0) { if ((f->val[0] == IJ_LEGBRUISE) || (f->val[0] == IJ_BLACKEYE)) { f->lifetime -= 5; limit(&f->lifetime, 1, NA); } } } if (fromlf && isplayer(fromlf)) { angergodmaybe(R_GODFIRE, 25, GA_HERESY); } } else if (damtype == DT_FIRE) { int i; // fire will ignire flammable lifeforms, cauterise slash wounds, etc getflags(lf->flags, retflag, &nretflags, F_FLAMMABLELF, F_INJURY, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_FLAMMABLELF) { if (isplayer(lf)) { msg("You are engulfed in flames!"); } else if (cansee(player, lf)) { msg("%s is engulfed in flames!", lfname); } addobfast(lf->cell->obpile, OT_FIRESMALL); } else if (f->id == F_INJURY) { if ((f->val[1] == DT_SLASH) && (f->lifetime > 0)) { f->lifetime -= 20; limit(&f->lifetime, 1, NA); } } } } else if (damtype == DT_ELECTRIC) { if (getraceclass(lf) == RC_ROBOT) { f = hasflag(lf->flags, F_CONFUSED); if (f) { if (f->lifetime > 0) f->lifetime += rnd(dam, dam*2); } else { addtempflag(lf->flags, F_CONFUSED, B_TRUE, NA, NA, NULL, rnd(dam,dam*2)); } } } else if (damtype == DT_POISONGAS) { if (dam > 0) { if (!skillcheck(lf, SC_POISON, 170, 0)) { // HARD. poison(lf, rnd(20,40), P_GAS, 2, "poison gas", fromlf ? fromlf->race->id : R_NONE, B_TRUE); } } } } // end if hp > 0 // effects based on damage type, even if lf is dead if (damtype == DT_ELECTRIC) { if (hasobofmaterial(lf->cell->obpile, MT_WATER)) { cell_t *retcell[MAX_MAPW*MAX_MAPH]; int nretcells = 0; // anyone else in the water takes damage too if (getconnectedwatercells(lf->cell, retcell, &nretcells)) { int i; animflashcells(retcell, nretcells, '/', C_WHITE, "Electricity arcs through the water!"); noise(lf->cell, NULL, NC_OTHER, SV_CAR, "arcing electricity", NULL); for (i = 0 ; i < nretcells; i++) { if (retcell[i]->lf && (retcell[i]->lf != lf)) { if (!isairborne(retcell[i]->lf, NULL)) { losehp_real(retcell[i]->lf, dam, DT_ELECTRIC, fromlf, "an electric shock", B_TRUE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE, B_NOCRIT); } } } } } } // special cases if (lf->race->id == R_FUNGUSDREAM) { char buf2[BUFLEN]; snprintf(buf, BUFLEN, "^w%s releases a cloud of purple spores!", lfname); snprintf(buf2, BUFLEN, "^wSomething releases a cloud of purple spores!"); spellcloud(lf->cell, 1, DT_COMPASS, UNI_SHADELIGHT, C_MAGENTA, OT_S_SLEEP, 8, B_TRUE, buf, buf2, B_FALSE, NULL, B_NOCENTRE); } else if (lf->race->id == R_FUNGUSPETRIFY) { char buf2[BUFLEN]; snprintf(buf, BUFLEN, "^w%s releases a cloud of grey spores!", lfname); snprintf(buf2, BUFLEN, "^wSomething releases a cloud of grey spores!"); spellcloud(lf->cell, 1, DT_COMPASS, UNI_SHADELIGHT, C_GREY, OT_S_PETRIFY, 5, B_TRUE, buf, buf2, B_FALSE, NULL, B_NOCENTRE); } else if (lf->race->id == R_FUNGUSRAGE) { char buf2[BUFLEN]; snprintf(buf, BUFLEN, "^w%s releases a cloud of red spores!", lfname); snprintf(buf2, BUFLEN, "^wSomething releases a cloud of red spores!"); spellcloud(lf->cell, 1, DT_COMPASS, UNI_SHADELIGHT, C_RED, OT_A_RAGE, 8, B_TRUE, buf, buf2, B_FALSE, NULL, B_NOCENTRE); } else if ((lf->race->id == R_UNYON) && ((damtype == DT_SLASH) || (damtype == DT_CHOP))) { char buf2[BUFLEN]; snprintf(buf, BUFLEN, "^w%s releases a cloud of fumes!", lfname); snprintf(buf2, BUFLEN, "^wSomething releases a cloud of fumes!"); spellcloud(lf->cell, 2, DT_ORTH, UNI_SHADELIGHT, C_GREY, OT_S_BLINDNESS, 8, B_TRUE, buf, buf2, B_TRUE, NULL, B_NOCENTRE); } if (isbleeding(lf)) { postlowhp = B_TRUE; } // further effects if not dead... if (!isdead(lf)) { object_t *o; // fight back if required if (fromlf && retaliate && !ko) { fightback(lf, fromlf); } if (ko) { int kotime; // is waskod was passed, allow the calling function to actually // knock us unconscious. // // this is mainly used in attack.c, so that we can announce // "you hit xxx. xxx loses consciousness." as opposed to // "xxx loses consciousness. you hit xxx." which makes no sense! kotime = getkotime(lf); if (waskod) { *waskod = kotime; } else { loseconsciousness(lf, kotime, fromlf); } } else { // you wake up if you were hit, unless you were unconscious! f = lfhasflag(lf, F_ASLEEP); if (f && (f->val[1] != ST_KO)) { killflagsofid(lf->flags, F_ASLEEP); } } // automatic onbleed abilities? if (postlowhp && !prelowhp) { f = lfhasflag(lf, F_LOWHPABIL); if (f) { flag_t *ntm; ntm = addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL); abilityeffects(lf, f->val[0], lf->cell, lf, NULL); killflag(ntm); } } // elemental effects on held objects if ((damtype == DT_FIRE) && !fromob && ((bodypart == BP_NONE) || (bodypart <= BP_HANDS))) { object_t *o, *nexto; int nburnt = 0; // up to dam/5 objects might be burnt // fire: dam*10 chance of burning each object which is vulnerable to fire for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; if (isvulnto(o->flags, DT_FIRE, B_FALSE) && pctchance(dam*10)) { int newdam; nburnt++; if (nburnt >= (dam/5)) break; // object takes 1/4 of damage newdam = pctof(25, dam); limit(&newdam, 1, NA); takedamage(o, newdam, DT_FIRE, fromlf); } } } else if ((damtype == DT_COLD) && !fromob && ((bodypart == BP_NONE) || (bodypart <= BP_HANDS))) { object_t *o, *nexto; // cold: dam*10 chance of shattering potions, or damaging other things. for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; if (isvulnto(o->flags, DT_COLD, B_FALSE) && pctchance(dam*10)) { int newdam; // object takes 1/4 of damage newdam = pctof(25, dam); limit(&newdam, 1, NA); takedamage(o, newdam, DT_COLD, fromlf); } } } else if ((damtype == DT_WATER) && ((bodypart == BP_NONE)||(bodypart <= BP_WAIST))) { int dosteam = B_FALSE; if (lfhasflag(lf, F_ONFIRE) || (getlfmaterial(lf) == MT_FIRE)) { dosteam = B_TRUE; } // put out fires extinguishlf(lf); if (dosteam) { addob(lf->cell->obpile, "cloud of steam"); } } // low hitpoint warning for player if (isplayer(lf) && (dam > 0)) { int warnthresh; warnthresh = (int)((float)0.25 * (float)lf->maxhp); if ((lf->hp <= warnthresh) && (lf->hp > 0)) { warn("*** LOW HITPOINT WARNING ***"); more(); } } o = hasequippedobid(lf->pack, OT_AMU_EVOLUTION); if (o) { enum DAMTYPE basedt; basedt = basedamagetype(damtype); if (basedt == DT_FIRE) { if (!polymorphto(lf, R_LAVAX, 5)) makeknown(o->type->id); } else if (basedt == DT_COLD) { if (!polymorphto(lf, R_SASQUATCH, 5)) makeknown(o->type->id); } } } // end if !isdead } void loselevel(lifeform_t *lf, int amt, lifeform_t *fromlf) { flag_t *f,*nextf; float hpratio,mpratio; int newlev,b; int ndice,nsides,plus; int nretflags,i; flag_t *retflag[MAXCANDIDATES]; if (isplayer(lf)) { lf->level -= amt; lf->newlevel = lf->level; lf->xp = getxpforlev(lf->level); } else { f = lfhasflag(lf, F_TR); if (f) { f->val[0] -= amt; } } newlev = gettr(lf); if (newlev <= 0) { // dead. lf->hp = 0; lf->maxhp = 0; return; } // adjust hp hpratio = ((float)lf->hp / (float)lf->maxhp); gethitdicestats(lf, &ndice,&nsides,&plus); lf->maxhp -= ((ndice*nsides)+plus); lf->hp = hpratio * (float)lf->maxhp; // adjust mp mpratio = ((float)lf->mp / (float)getmaxmp(lf)); getmpdicestats(lf, &ndice,&plus); lf->maxmp -= ((ndice*4)+plus); lf->mp = mpratio * (float)getmaxmp(lf); // lose stats (do this after hitdice in case we // lower our fitness) getflags(lf->flags, retflag, &nretflags, F_STATGAINED, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->val[0] > newlev) { // reduce BASE attrib too. not using modattr because this // is a permenant change. modattr(lf, f->val[1], -STATAMTPERLEVEL); lf->baseatt[f->val[1]] -= STATAMTPERLEVEL; } killflag(f); } // stop spells interrupt(lf); loseconcentration(lf); // handle monks if (hasjob(lf, J_MONK)) { adjustmonk(lf, B_TRUE); } // lose flags. foreach_bucket(b) { for (f = lf->flags->first[b] ; f; f = nextf) { nextf = f->next; if (f->fromlev > newlev) { killflag(f); } } } if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); wrefresh(statwin); msg("^%cWelcome back to level %d.",getlfcol(lf, CC_VBAD), lf->level); } } void losemp(lifeform_t *lf, int amt) { if (isplayer(lf)) { if (lf->bartimer == 2) { lf->mplastturn += amt; } else { lf->mplastturn = amt; lf->bartimer = 2; } } lf->mp -= amt; if (lf->mp < 0) { lf->mp = 0; } if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); updatestatus(); } } void loseskill(lifeform_t *lf, enum SKILL skid) { flag_t *f; skill_t *sk; int i; sk = findskill(skid); assert(sk); f = lfhasflagval(lf, F_HASSKILL, skid, NA, NA, NULL); if (f) { killflag(f); } // now lose any skills which came from this. for (i = 0; i < sk->nskillwills; i++) { int nretflags,n; flag_t *retflag[MAXCANDIDATES]; getflags(lf->flags, retflag, &nretflags, F_CANWILL, F_NONE); for (n = 0; n < nretflags; n++) { f = retflag[n]; if ((f->val[0] == sk->skillwill[i].abilid) && (f->lifetime == FROMSKILL) && (f->skillfrom == sk) ) { killflag(f); continue; } } } } void magicwoods_warn(lifeform_t *who) { if (isplayer(who)) { msg("^wYou sense anger from the woods around you..."); } if (!lfhasflag(who, F_SYLVANWARN)) { addflag(who->flags, F_SYLVANWARN, B_TRUE, NA, NA, NULL); } } void magicwoods_angry(lifeform_t *who) { cell_t *retcell[MAXCANDIDATES]; int nretcells,i,ntrees; char lfname[BUFLEN]; if (isplayer(who)) { msg("^bYou feel an intense rage from the woods around you!"); } if (!lfhasflag(who, F_SYLVANWARN)) { addflag(who->flags, F_SYLVANWARN, B_TRUE, NA, NA, NULL); } getlfname(who, lfname); // nearby trees attack you getradiuscells(who->cell, 1, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_FALSE, retcell, &nretcells, 0); ntrees = 0; for (i = 0; i < nretcells; i++) { if (retcell[i]->type->id == CT_WALLTREE) { ntrees++; } } if (ntrees) { enum DAMTYPE dt; if (isplayer(who)){ msg("^%cThe trees around you thrash you with their branches!", getlfcol(who, CC_BAD)); } else if (cansee(player, who)) { msg("^%cThe trees around %s thrash it with their branches!", lfname, getlfcol(who, CC_BAD)); } dt = DT_SLASH; if (isimmuneto(who->flags, dt, B_FALSE)) { dt = DT_BASH; } if (isimmuneto(who->flags, dt, B_FALSE)) { dt = DT_PIERCE; } losehp(who, rolldie(ntrees,4), dt, NULL, "the wrath of the Sylvan Woods"); setkillverb(who, "Killed"); } //if (pctchance(33)) { if (pctchance(100)) { cell_t *where; lifeform_t *newlf; enum RACE rid; switch (rnd(1,2)) { case 1: rid = R_TREANTYOUNG; break; case 2: rid = R_TREANT; break; case 3: rid = R_TREANTOLD; break; } where = getrandomadjcell(who->cell, &ccwalkable, B_ALLOWEXPAND); newlf = summonmonster(NULL, where, rid, NULL, PERMENANT, B_FALSE); if (newlf) { if (!isplayer(who)) { killflagsofid(newlf->flags, F_HOSTILE); } aiattack(newlf, who, PERMENANT); } } } // make a list of choices for player to talk to a lf // // 'multiple' means 'we're talking to multiple people,not just one' void makecommslist(prompt_t *p, lifeform_t *lf, int multiple) { flag_t *f; object_t *godstone; enum ATTRBRACKET iqb; iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); // are they friendly? if (ispetof(lf, player)) { if ((iqb >= IQ_ANIMAL) || isundead(lf)) { if (!haschoice(p, 'a')) addchoice(p, 'a', "Attack something", NULL, NULL, NULL); } if (!isadjacent(lf->cell, player->cell)) { if (!haschoice(p, 'c')) addchoice(p, 'c', "Come here", NULL, NULL, NULL); } if (!haschoice(p, 'g')) addchoice(p, 'g', "Go somewhere", NULL, NULL, NULL); if (isadjacent(lf->cell, player->cell) && !lfhasflag(lf, F_NOPACK) && !multiple) { if (!haschoice(p, 't')) addchoice(p, 't', "Trade items with me", NULL, NULL, NULL); } if (iqb >= IQ_ANIMAL) { f = isresting(lf); if (f) { if (!haschoice(p, 'r')) addchoice(p, 'r', "Stop resting.", NULL, NULL, NULL); } else { if (!haschoice(p, 'r')) addchoice(p, 'r', "Rest until you are healed.", NULL, NULL, NULL); } if (!haschoice(p, '"')) addchoice(p, '<', "Stay close.", NULL, NULL, NULL); if (!haschoice(p, '"')) addchoice(p, '>', "Keep your distance.", NULL, NULL, NULL); } } else if (ishirable(lf) && !multiple) { if (lfhasflag(lf, F_ISPRISONER)) { if (!haschoice(p, 'j')) addchoice(p, 'j', "Join me, and I will help you escape.", NULL, NULL, NULL); } else if (getskill(player, SK_SPEECH) >= PR_EXPERT) { if (!haschoice(p, 'j')) addchoice(p, 'j', "Join me on my quest!", NULL, NULL, NULL); } } if (!isgod(lf) && ispeaceful(lf) && cantalk(lf) && !multiple) { enum SKILLLEVEL slev; job_t *j; slev = getskill(player, SK_SPEECH); // same race or job? j = getjob(player); if ((slev != PR_MASTER) && j && (j == getjob(lf))) slev++; if ((slev != PR_MASTER) && (player->race->id == lf->race->id)) slev++; if (slev >= PR_NOVICE) { if (!haschoice(p, 'x')) addchoice(p, 'x', "Any dangers nearby that I should look out for?", NULL, NULL, NULL); } if (slev >= PR_BEGINNER) { if (!haschoice(p, 'i')) addchoice(p, 'i', "What can you tell me about this area?", NULL, NULL, NULL); } if (!areallies(player, lf)) { if (slev >= PR_SKILLED) { if (!haschoice(p, 'k')) addchoice(p, 'k', "Care to trade knowledge?", NULL, NULL, NULL); } } } if (isadjacent(lf->cell, player->cell) && !multiple) { if (areenemies(player, lf)) { if (!haschoice(p, 'm')) addchoice(p, 'm', "Have mercy!", NULL, NULL, NULL); } // if you are allies, use 'trade items' instead if (!areallies(player, lf)) { if (isgod(lf)) { // may only donate the godstone godstone = hasob(player->pack, getopposinggodstone(lf->race->id)); if (godstone) { char buf[BUFLEN],obname[BUFLEN]; getobname(godstone, obname, 1); snprintf(buf, BUFLEN, "(offer %s)", obname); if (!haschoice(p, 'o')) addchoice(p, 'o', buf, NULL, NULL, NULL); } } else { if (!haschoice(p, 'o')) addchoice(p, 'o', "(offer a bribe)", NULL, NULL, NULL); } } } /* f = lfhasflag(lf, F_OWNSSHOP); if (f) { int shopid; shopid = f->val[0]; moneyowing = getowing(player, shopid, NULL); if (moneyowing > 0) { snprintf(buf, BUFLEN, "(pay $%d to the shopkeeper)",moneyowing); addchoice(p, 'p', buf, NULL, NULL, NULL); } } */ if (!multiple && !haschoice(p, 'y')) addchoice(p, 'y', "Yeeeeeaaaargh!", NULL, NULL, NULL); if (!haschoice(p, 'n')) addchoice(p, 'n', "(nothing)", NULL, NULL, NULL); } void makefriendly(lifeform_t *who, int howlong) { char lfname[BUFLEN]; getlfname(who, lfname); if (lfhasflag(who, F_DEBUG)) { msg("Making %s friendly.",lfname); } if (howlong == PERMENANT) { killflagsofid(who->flags, F_HOSTILE); } // if not unlimited, they'll revert back. if (!hasflag(who->flags, F_FRIENDLY)) { addtempflag(who->flags, F_FRIENDLY, B_TRUE, NA, NA, NULL, howlong); } loseaitargets(who); } void makeheard(lifeform_t *listener, lifeform_t *noisemaker, int showglyph, char *noisetext, int howlong) { flag_t *f; f = lfhasflagval(listener, F_CANHEARLF, noisemaker->id, NA, NA, NULL); if (f) { if (f->lifetime > 0) f->lifetime = howlong; if (showglyph) { f->val[1] = B_TRUE; } else { f->val[1] = B_FALSE; } if (noisetext) { free(f->text); f->text = strdup(noisetext); } } else { f = addtempflag(listener->flags, F_CANHEARLF, noisemaker->id, showglyph, NA, noisetext, howlong); } if (isplayer(listener)) needredraw = B_TRUE; } // make lf be able to learn the given skill // returns TRUE if we did something. int makelearnable(lifeform_t *lf, enum SKILL skid) { flag_t *f,*learnable; int changed = B_FALSE; f = hasflagval(lf->flags, F_NOSKILL, skid, NA, NA, NULL); if (f) { killflag(f); } learnable = lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL); if (learnable) { if (learnable->val[1] != NA) { // able to learn thievery, but limitted learnable->val[1] = NA; changed = B_TRUE; } } else if (!getskill(lf, SK_THIEVERY)) { // don't have the skill, not learnable addflag(lf->flags, F_CANLEARN, skid, NA, NA, NULL); changed = B_TRUE; } if (changed) { if (isplayer(lf)) { skill_t *sk; sk = findskill(skid); msg("^GYou are now capable of learning the %s skill.^n",sk->name); } return B_TRUE; } return B_FALSE; } // returns TRUE on failure. int makenauseated(lifeform_t *lf, int amt, int howlong, enum ERROR *why) { flag_t *nflag = NULL; if (why) *why = E_OK; switch (lf->race->raceclass->id) { case RC_HUMANOID: case RC_ANIMAL: break; default: // only affects humanoids & animals, unless you have enhanced smell. if (!lfhasflag(lf, F_ENHANCESMELL)) { if (why) *why = E_NOEFFECT; return B_TRUE; } break; } if (isasleep(lf)) { if (why) *why = E_NOEFFECT; return B_TRUE; } if (lfhasflag(lf, F_STENCH)) { if (why) *why = E_NOEFFECT; return B_TRUE; // your own smell makes you used to it } if (lfhasflag(lf, F_NOSMELL) || lfhasflag(lf, F_NONAUSEA)) { if (why) *why = E_NOEFFECT; return B_TRUE; // can't smell it. } if (!hasbp(lf, BP_HEAD)) { if (why) *why = E_NOEFFECT; return B_TRUE; // can't smell with no head } if (lfhasflag(lf, F_ENHANCESMELL)) amt += 2; //if (!lfhasflag(lf, F_HUMANOID)) return B_TRUE; nflag = lfhasflag(lf, F_NAUSEATED); // skillcheck to avoid this. if (skillcheck(lf, SC_CON, 80 + (amt*10), gettr(lf)*2)) { // passed skillcheck if (why) *why = E_RESISTED; // announce if (isplayer(lf)) { msg("You feel momentarily %sunwell.", nflag ? "more " : ""); } else if (cansee(player, lf)) { char buf[BUFLEN]; getlfname(lf, buf); msg("%s looks momentarily %sunwell.", buf, nflag ? "more " : ""); } return B_TRUE; } // already nauseated? if (nflag) { if ((nflag->lifetime >= 0) && (nflag->lifetime < howlong)) { nflag->lifetime = howlong; nflag->val[0] = MAXOF(nflag->val[0], amt); } } else { addtempflag(lf->flags, F_NAUSEATED, amt, NA, NA, NULL, howlong); } return B_FALSE; } void makenoise(lifeform_t *lf, enum NOISETYPE nid) { int volume = 1; char hear[BUFLEN], see[BUFLEN]; flag_t *nflag = NULL; if (!getnoisedetails(lf, nid, NULL, hear, see, &volume, &nflag)) { // success if (nflag && (nflag->val[2] != NA) && cantalk(lf)) { sayphrase(lf, nflag->val[2], volume, NA, NULL, NULL); } else { noise(lf->cell, lf, noisetypetoclass(nid), volume, strlen(hear) ? hear : NULL, strlen(see) ? see : NULL); } } } void mayusespellschool(flagpile_t *fp, enum SPELLSCHOOL ss, enum FLAG how, int overridepower) { objecttype_t *ot; skill_t *sk; sk = findskill(getschoolskill(ss)); if (sk) { if (!hasflagval(fp, F_STARTSKILL, sk->id, NA, NA, NULL)) { addflag(fp, F_STARTSKILL, sk->id, PR_NOVICE, NA, NULL); } } for (ot = objecttype ; ot ; ot = ot->next) { //if (ot->obclass->id == OC_SPELL) { if (hasflagval(ot->flags, F_SPELLSCHOOL, ss, NA, NA, NULL)) { if (!hasflagval(fp, how, ot->id, NA, NA, NULL)) { char text[BUFLEN]; if (overridepower) { snprintf(text, BUFLEN, "pw:%d;",overridepower); } else { strcpy(text, ""); } addflag(fp, how, ot->id, NA, NA, text); } } //} } } int meetsallattreqs(lifeform_t *lf, object_t *o) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; // other flags to check getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE); for (i = 0; i < nretflags; i++) { if (!meetsattreq(lf, retflag[i], o, NULL)) { return B_FALSE; } } return B_TRUE; } // if you don't meet it, return why not in 'reason' int meetsattreq(lifeform_t *lf, flag_t *f, object_t *o, int *pctmod) { enum ATTRIB att; int valneeded,valbonus; int myval; int neededdiff,bonusdiff; int dopenaltycheck = B_TRUE, dobonuscheck = B_FALSE; int scale_bonus = 0, scale_penalty = 15; enum { NONE=0, PENALTY=1, BONUS = 2, } bonorpen = NONE; if (pctmod) { *pctmod = 0; } att = f->val[0]; valneeded = f->val[1]; if (valneeded == NA) { dopenaltycheck = B_FALSE; } else { dopenaltycheck = B_TRUE; } valbonus = f->val[2]; if (valbonus == NA) { dobonuscheck = B_FALSE; } else { dobonuscheck = B_TRUE; } if (strlen(f->text)) { scale_bonus = atoi(f->text); } else { scale_bonus = 0; // default } // modify for masterwork if (o) { if (hasflag(o->flags, F_MASTERWORK)) { if (dopenaltycheck) { valneeded -= 10; if (valneeded < 0) valneeded = 0; } if (dobonuscheck) { valbonus -= 10; if (valbonus < 0) valbonus = 0; } } } myval = getattr(lf, att); if (dopenaltycheck) { neededdiff = myval - valneeded; limit(&neededdiff,-20, 20); } if (dobonuscheck) { bonusdiff = myval - valbonus; // for this one, just meeting it gives you once "scale_bonus" worth of bonus if (bonusdiff >= 0) bonusdiff += 10; limit(&bonusdiff,-20, 30); } if (dopenaltycheck && (neededdiff < 0)) { // penalty? // for firearms, armour or scale_bonus == 0, you MUST meet the requirement. if (scale_bonus == 0) { neededdiff = -20; } else if (o && isarmour(o)) { neededdiff = -20; } else if (o && isfirearm(o)) { neededdiff = -20; } bonorpen = PENALTY; } else if (dobonuscheck && (bonusdiff > 0)) { // maybe a bonus? // no bonusses if you're unskilled if (o && !getweaponskill(lf, o)) { bonusdiff = 0; } else { bonorpen = BONUS; } } if (scale_bonus && pctmod) { // for each 10 points you are over/under the requirement, adjust "scale_bonus" percent. if (bonorpen == PENALTY) { *pctmod = (neededdiff/10) * scale_penalty; } else if (bonorpen == BONUS) { *pctmod = (bonusdiff/10) * scale_bonus; } } // too low? if ((bonorpen == PENALTY) && (neededdiff <= -20)) { switch (att) { case A_AGI: reason = E_LOWDEX; break; case A_CHA: reason = E_LOWCHA; break; case A_CON: reason = E_LOWCON; break; case A_IQ: reason = E_LOWIQ; break; case A_STR: reason = E_LOWSTR; break; case A_WIS: reason = E_LOWWIS; break; case A_NONE: break; } return B_FALSE; } reason = E_OK; return B_TRUE; } // will the lf flee after taking damage? int mightflee(lifeform_t *lf) { flag_t *f; enum ATTRBRACKET iqb; iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); if (iqb < IQ_ANIMAL) { // too stupid to know to flee return B_FALSE; } if (hasflag(lf->flags, F_NOFLEE)) { return B_FALSE; } if (lfhasflag(lf, F_RAGE)) { return B_FALSE; } if (hasflag(lf->flags, F_FLEEONDAM)) { return B_TRUE; } f = hasflag(lf->flags, F_FLEEONHPPCT); if (f) { if (gethppct(lf) <= f->val[0]) { return B_TRUE; } } iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL); if (iqb >= AT_AVERAGE) { if (!skillcheck(lf, SC_MORALE, (160 - gethppct(lf))/2, 0)) { return B_TRUE; } } return B_FALSE; } // returns F_NEEDOBFORSPELLS flag if the lifeform is missing an object which they // need for spellcasting. If the lf has everything needed to cast spells // OR simply can't cast spells, it returns NULL. // flag_t *missingspellcastob(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_NEEDOBFORSPELLS); if (f) { if ((f->val[0] != NA) && !hasob(lf->pack, f->val[0])) { return f; } if ((f->val[1] != NA) && !hasobwithflag(lf->pack, f->val[1])) { return f; } } return B_FALSE; } int modattr(lifeform_t *lf, enum ATTRIB attr, int amt) { if (isplayer(lf)) { statdirty = B_TRUE; } // already at max/min? if ((amt > 0) && (lf->att[attr] >= MAX_ATTRIBVAL)) { return B_TRUE; } if ((amt < 0) && (lf->att[attr] <= 0)) { return B_TRUE; } lf->att[attr] += amt; // increase base if required if (lf->att[attr] > lf->baseatt[attr]) { lf->baseatt[attr] = lf->att[attr]; } // enforce limits limit(&lf->att[attr], 0, MAX_ATTRIBVAL); if (lf->born && (gamemode == GM_GAMESTARTED) && (amt != 0) && (isplayer(lf) || cansee(player, lf))) { char lfname[BUFLEN], verb[BUFLEN], adverb[BUFLEN]; getlfname(lf, lfname); if (isplayer(lf)) strcpy(verb, "feel"); else strcpy(verb, "looks"); if (amt > 0) { switch (attr) { case A_STR: strcpy(adverb, "stronger"); break; case A_CHA: strcpy(adverb, "more attractive"); break; case A_CON: strcpy(adverb, "healthier"); break; case A_AGI: strcpy(adverb, "more agile"); break; case A_IQ: strcpy(adverb, "smarter"); break; case A_WIS: strcpy(adverb, "wiser"); break; default: break; } } else { // ie. amt < 0 switch (attr) { case A_STR: strcpy(adverb, "weaker"); break; case A_CHA: strcpy(adverb, "less attractive"); break; case A_CON: strcpy(adverb, "frail"); break; case A_AGI: strcpy(adverb, "sluggish"); break; case A_IQ: strcpy(adverb, "stupid"); break; case A_WIS: strcpy(adverb, "foolish"); break; default: break; } } msg("^%c%s %s %s!", getlfcol(lf, (amt > 0) ? CC_GOOD : CC_BAD), lfname, verb, adverb); more(); } return B_FALSE; } void modhunger(lifeform_t *lf, int amt) { flag_t *f; int prehlev, posthlev; f = hasflag(lf->flags, F_HUNGER); if (!f) { return; } if (isundead(lf) && (lf->race->id != R_VAMPIRE)) { return; } // modify for effects if (amt > 0) { int multiplier = 0; int tempmult; sumflags(lf->flags, F_FASTMETAB, &tempmult, NULL, NULL); multiplier += tempmult; sumflags(lf->flags, F_SLOWMETAB, &tempmult, NULL, NULL); multiplier -= tempmult; if (lfhasflagval(lf, F_ASLEEP, ST_MEDITATING, NA, NA, NULL)) { multiplier -= 2; } if (multiplier > 0) { amt *= multiplier; } else if (multiplier < 0) { amt /= abs(multiplier); } } prehlev = gethungerlevel(f->val[0]); f->val[0] += amt; posthlev = gethungerlevel(f->val[0]); if (posthlev == H_STARVED) { if (isplayer(lf)) { msg("^BYou collapse from starvation."); } lf->lastdamtype = DT_DIRECT; setlastdam(lf, "starvation"); lf->hp = 0; } else if (prehlev != posthlev) { if (posthlev != H_NONE) { char buf[BUFLEN]; int needfeeling = B_FALSE; int needexclam = B_FALSE; switch (posthlev) { case H_PECKISH: case H_HUNGRY: case H_VHUNGRY: needfeeling = B_TRUE; break; default: needfeeling = B_FALSE; break; } switch (posthlev) { case H_STUFFED: case H_STARVING: needexclam = B_TRUE; break; default: needexclam = B_FALSE; break; } if (isplayer(lf)) { gethungername(lf, posthlev, buf); msg("^wYou are %s%s%s%c", ((amt < 0) && (posthlev > H_NONE)) ? "still " : "", needfeeling ? "feeling " : "", buf, (needexclam) ? '!' : '.'); statdirty = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); gethungername(lf, posthlev, buf); msg("^%c%s looks %s%c", getlfcol(lf, CC_BAD), lfname, buf, (needexclam) ? '!' : '.'); } if ((posthlev >= H_VHUNGRY) && (amt > 0)) { stopresting(lf); } if (posthlev > H_STARVING) { useringofmiracles(lf, 2); // reset hunger f->val[0] = 0; } // ai doesn't get hungry anymore after they have // satisfied their hunger. if (!isplayer(lf) && (posthlev <= H_NONE)) { killflag(f); return; } } } } float modifybystat(float num, lifeform_t *lf, enum ATTRIB att) { int newnum; newnum = num + ( num * (getstatmod(lf, att) / 100.0) ); return newnum; } void modmorale(lifeform_t *lf, int howmuch) { flag_t *mf; mf = hasflag(lf->flags, F_MORALE); if (mf) { mf->val[0] += howmuch; if (mf->val[0] <= 0) { killflag(mf); } } else { if (howmuch > 0) { addflag(lf->flags, F_MORALE, howmuch, NA, NA, NULL); } } } void modstamina(lifeform_t *lf, float howmuch) { float orig; enum TEMPERATURE temp; if (howmuch == 0) return; if (lfhasflag(lf, F_NOSTAM)) { return; } if (isundead(lf)) return; // you don't lose stamina while enraged or caffeinated if (lfhasflag(lf, F_RAGE) && (howmuch < 0)) return; if (lfhasflag(lf, F_CAFFEINATED) && (howmuch < 0)) return; // hot = use stamina more quickly temp = getlftemp(lf); howmuch = pctof(gettempstammod(lf,temp),howmuch); if (isplayer(lf)) { if (howmuch < 0) { if (lf->bartimer == 2) { lf->stamlastturn += fabs(howmuch); } else { lf->stamlastturn = fabs(howmuch); lf->bartimer = 2; } } else if (howmuch > 0) { lf->stamlastturn = 0; } } orig = getstamina(lf); lf->stamina += howmuch; limitf(&(lf->stamina), 0, getmaxstamina(lf)); if (gamemode == GM_GAMESTARTED) { if (getstamina(lf) != orig) { if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); updatestatus(); if (isexhausted(lf)) { msg("^BYou are exhausted."); } else if (orig == 0) { msg("You feel less exhausted now."); } } else if (cansee(player, lf)) { if (isexhausted(lf)) { char lfname[BUFLEN]; char *p; getlfname(lf, lfname); p = strdup(lfname); strrep(&p, "exhausted ","", NULL); msg("%s looks exhausted.", p); free(p); } } } } if (getstamina(lf) == 0) { flag_t *f; stopsprinting(lf); f = isflyingwithwings(lf); if (f) { killflag(f); } } } int movecausesnoise(lifeform_t *lf) { if (lfhasflag(lf, F_SILENTMOVE)) { return B_FALSE; } return B_TRUE; } // chance of this lf moving randomly due to drunkenness etc... int willmoverandomly(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_DRUNK); if (f) { if (!hasjob(lf, J_PIRATE)) { if (rnd(1,6) <= ((f->lifetime/TM_DRUNKTIME)+1)) { // randomize move return B_TRUE; } } } if (iswoozy(lf) && onein(3)) { return B_TRUE; } if (lfhasflagval(lf, F_INJURY, IJ_TAILBRUISED, NA, NA, NULL) && onein(5)) { return B_TRUE; } return B_FALSE; } int needstobreath(lifeform_t *lf) { if (lfhasflag(lf, F_NOBREATH)) { return B_FALSE; } return B_TRUE; } // if validchars is set, we will populate it with a list of valid // choice letters for asking the player how to rest. int needstorest(lifeform_t *lf, char *validchars) { int need = B_FALSE; if (validchars) strcpy(validchars, ""); if (lf->hp < lf->maxhp) { if (validchars) strcat(validchars, "h"); need = B_TRUE; } else if (hastempinjuries(lf)) { if (validchars) strcat(validchars, "h"); need = B_TRUE; } if ((getmaxmp(lf) > 0) && (lf->mp < getmaxmp(lf))) { if (validchars) strcat(validchars, "m"); need = B_TRUE; } if (getstamina(lf) < getmaxstamina(lf)) { if (validchars) strcat(validchars, "s"); need = B_TRUE; } return need; } void noarmouron(race_t *r, enum BODYPART bp) { int i; for (i = 0; i < r->nbodyparts; i++) { if (r->bodypart[i].id == bp) { r->bodypart[i].armourok = B_FALSE; break; } } } // returns TRUE if the player heard it. int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, char *text, char *seetext) { lifeform_t *l; int sounddist; int alwayshear = B_FALSE; int rv = B_FALSE; assert(text); if (gamemode != GM_GAMESTARTED) { return B_FALSE; } if (nclass == NC_SPELLEFFECT) { alwayshear = B_TRUE; } // sound will travel 3*volume cells sounddist = getsounddist(volume); // if anything nearby hears it, it might respond for (l = c->map->lf; l ; l = l->next) { int migraine = B_FALSE; int dist; int difficulty; int lbonus; int myvol; int nwalls = 0; int hearable = B_FALSE; flag_t *sleepflag; if (l == noisemaker) continue; // ie. if this lf is in the process of swapping places if (!l->cell) continue; sleepflag = lfhasflag(l, F_ASLEEP); // can't hear while unconscious if (sleepflag && (sleepflag->val[1] == ST_KO)) continue; dist = getcelldist(l->cell, c); // listen check difficulty is based on sound distance vs max hearing distance if ((nclass == NC_SPEECH) && isplayer(l)) { // you always hear it, as long as you're in range difficulty = 0; } else if ((nclass == NC_FIGHTING) && isplayer(l) && (dist <= 1)) { // players never hear fighting unless it's a small distance away // this prevents "you hear fighting" when you are attacked from behind. difficulty = D_ALWAYSFAIL; } else { //difficulty = (int) ( ((float)getcelldist(l->cell, c) / (float)gethearingrange(l)) * 20); difficulty = (int) ( ((float) dist / ((float)gethearingrange(l) + volume)) * 75); } /* if (sleepflag) { myvol = volume - 1; } else { myvol = volume; } limit(&myvol, 0, NA); */ myvol = volume; hearable = canhear(l,c,myvol,&nwalls); // adjust volume for number of walls myvol -= nwalls; // listen bonus is based on sound volume lbonus = (myvol*5); if (sleepflag) { lbonus -= 25; limit(&lbonus, 0, NA); } if ((myvol >= 3) && lfhasflagval(l, F_POISONED, P_MIGRAINE, NA, NA, NULL)) { migraine = B_TRUE; } // skillcheck to hear this if ( (isplayer(l) && haslos(l, c)) || // only player can "hear by seeing" (hearable && (alwayshear || skillcheck(l, SC_LISTEN, difficulty, lbonus)) ) ) { flag_t *f; // announce? if (isplayer(l) && !lfhasflag(l, F_ASLEEP)) { // never say "you hear xxx" if you can see the lf that caused the noise. // or the cell which made the noise - UNLESS you have a migraine. if (!migraine && noisemaker && cansee(l, noisemaker) ) { // you can see the lf which made the noise if (seetext) { char lfname[BUFLEN]; char realseetext[BUFLEN]; char *quotepos; getlfname(noisemaker, lfname); // adjust it if you're deaf strcpy(realseetext, seetext); quotepos = strchr(realseetext, '\"'); if (isdeaf(l) && quotepos) { *quotepos = '\0'; strcat(realseetext, "something"); } if (isdead(noisemaker)) { msg("The dying %s %s.", noprefix(lfname), realseetext); } else { msg("%s %s%s", lfname, realseetext, (realseetext[strlen(realseetext)-1] == '!') ? "" : "."); } rv = B_TRUE; } } else if (!migraine && !noisemaker && haslos(player, c)) { // you can see the cell which made the noise if (seetext) { msg("%s", seetext); rv = B_TRUE; } } else if (!migraine && noisemaker && ispetof(noisemaker, player)) { // your pet made the noise rv = B_FALSE; } else if (text && !isdeaf(l) && ((nclass != NC_MOVEMENT) || !lfhasflag(l, F_DONELISTEN))) { // this means you can only hear one 'walk' sound per turn char textnopunc[BUFLEN]; char punc; int dist; int muffled = B_FALSE; char prefix[BUFLEN]; enum SKILLLEVEL slev; char distbuf[BUFLEN],distbufbad[BUFLEN]; char dirbuf[BUFLEN]; char localtext[BUFLEN]; if (nwalls >= 1) muffled = B_TRUE; if (muffled) { char *p; // "you hear [a xxx]" // becomes: // "you hear [a muffled xxx]" p = strstartswitha(text, prefix); if (p) { snprintf(localtext, BUFLEN, "%smuffled %s", prefix, p); } else { // "you hear [xxx]" // becomes: // "you hear muffled [xxx]" // snprintf(localtext, BUFLEN, "muffled %s", text); } } else { strcpy(localtext, text); } //punc = text[strlen(text)-1]; //strncpy(textnopunc, text, strlen(text)-1); strcpy(textnopunc, localtext); punc = textnopunc[strlen(textnopunc)-1]; if (punc == '\"') { // ie. someone saying something punc = '\0'; } else { textnopunc[strlen(textnopunc)-1] = '\0'; } dist = getcelldist(l->cell, c); // adjust text if you are deaf. getdisttext(l->cell, c, distbuf, distbufbad, dirbuf); slev = getskill(l, SK_LISTEN); // listen skill gives you more info about monsters if (noisemaker) { int detectdist = 0; char lfname[BUFLEN]; real_getlfnamea(noisemaker, lfname, NULL, B_NOSHOWALL, B_CURRACE); detectdist = getlistendetectrange(l); // // high listen skill lets you know more info. // // beginner = distance // adept = distance and direction // expert = monstername and distance and direction // master = temporary scan of where they are! if (dist <= detectdist) { if (slev >= PR_EXPERT) { // fully id makeheard(l, noisemaker, B_TRUE, NULL, 2); } else { // show that you heard a sound makeheard(l, noisemaker, B_FALSE, textnopunc, 2); } } // now announce it. if (slev >= PR_EXPERT) { if (muffled) { msg("You hear a muffled %s%s to the %s%c", noprefix(lfname), distbuf, dirbuf, punc); } else { msg("You hear %s%s to the %s%c", lfname, distbuf, dirbuf, punc); } rv = B_TRUE; } else if (slev >= PR_BEGINNER) { msg("You hear %s%s to the %s%c", textnopunc, distbuf, dirbuf, punc); rv = B_TRUE; } else if (slev >= PR_NOVICE) { msg("You hear %s%s%c", textnopunc, distbufbad, punc); rv = B_TRUE; } else { msg("You hear %s", localtext); rv = B_TRUE; } } else { assert(text); if (slev >= PR_BEGINNER) { msg("You hear %s%s to the %s%c", textnopunc, distbuf, dirbuf, punc); } else if (slev >= PR_NOVICE) { msg("You hear %s%s%c", textnopunc, distbufbad, punc); } else { msg("You hear %s", localtext); } rv = B_TRUE; } // can only hear one 'walk' sound per turn. if (nclass == NC_MOVEMENT) { addflag(l->flags, F_DONELISTEN, B_TRUE, NA, NA, NULL); practice(l, SK_LISTEN, 1); } } } // end if isplayer and not asleep f = lfhasflag(l, F_ASLEEP); if (f && (f->val[1] != ST_KO)) { stir(l,myvol,getcelldist(c, l->cell), text); } else { // not asleep, but can hear it. // migraine? if ((myvol >= 3) && migraine) { losehp(l, (myvol-1), DT_SONIC, NULL, "a migraine"); if (isplayer(l)) { msg("Your head explodes in pain at the sound!"); } } // monsters will go to investigate the sound, as long as they're // not otherwise occupied if (!isdead(l) && !isplayer(l)) { int willrespond = B_FALSE; flag_t *tf; tf = aihastarget(l); if (lfhasflag(l, F_AUTOROTATE)) { } else if (isfleeing(l)) { } else if (noisemaker && !isplayer(noisemaker) && (nclass == NC_MOVEMENT)) { // monsters won't turn to face other monsters' footsteps } else if (tf) { lifeform_t *targlf = NULL; if (tf->id == F_TARGETLF) { // will probably ignore the sound unless // it is closer. targlf = findlf(l->cell->map, tf->val[0]); } if (targlf && (targlf->cell)) { // ie. not swapping places if (getcelldist(l->cell, c) < getcelldist(l->cell, targlf->cell)) { if ((myvol >= SV_SHOUT)) { if ((nclass != NC_ENVIRONMENTAL) && (nclass != NC_MOVEMENT)) { int chance; chance = 40; chance += (20*(myvol - SV_SHOUT)); if (pctchance(chance)) { willrespond = B_TRUE; } } } } } else { // will respond if the sound is closer than our target cell. cell_t *tc; tc = getcellat(l->cell->map, tf->val[1], tf->val[2]); if (tc && (getcelldist(l->cell, c) < getcelldist(l->cell, tc))) { willrespond = B_TRUE; } } } else willrespond = B_TRUE; if (willrespond) { // abandon non-lf targets. if (!gettargetlf(l)) { loseaitargets(l); } // if within lof, turn to face it. if (haslof(l->cell, c, LOF_WALLSTOP, NULL)) { // turn to face the player //if (isplayer(noisemaker) && cansee(l, player) && // !lfhasflag(l, F_AWARENESS) && !isdead(l) && // !isfriendly(l)) { if (!haslos(l, c) && !isfriendly(l) ) { char lfname[BUFLEN]; int prefacing; prefacing = l->facing; turntoface(l, c); // announce if (!lfhasflag(l, F_FEIGNINGDEATH) && cansee(player, l) && (prefacing != l->facing)) { char dname[BUFLEN]; strcpy(dname, getdirname(l->facing)); dname[0] = tolower(dname[0]); getlfname(l, lfname); msg("%s turns to face %s.", lfname, cansee(l, player) ? "you" : dname); } } } else { int chasetime; // if NOT within lof, go and investigate chasetime = myvol * 10; aigoto(l, c, MR_SOUND, NULL, chasetime); } } } } } else { // can't hear the sound. } } // end for each lf on map if (rv == B_TRUE) { if (getoption(OPT_STOPRUNONNOISE)) { stoprunning(player); } } return rv; } enum NOISECLASS noisetypetoclass(enum NOISETYPE nt) { switch (nt) { case N_WALK: case N_FLY: return NC_MOVEMENT; case N_SONICBOLT: case N_DEAFENSCREAM: case N_DEATHKEEN: case N_WARCRY: return NC_SPELLEFFECT; default: break; } return NC_OTHER; } // give initial equiment / skills to a lifeform void outfitlf(lifeform_t *lf) { //int db = B_FALSE; givestartskills(lf, lf->flags); givestartobs(lf, NULL, lf->flags); autoskill(lf); // weild/wear stuff autoweild(lf); } // make 'lf' into a pet/ally of 'owner' void petify(lifeform_t *lf, lifeform_t *owner) { if (isplayer(owner)) { makefriendly(lf, PERMENANT); killflagsofid(lf->flags, F_NOINFO); killflagsofid(lf->flags, F_INFOPRICE); } addflag(lf->flags, F_PETOF, owner->id, owner->cell->x, owner->cell->y, NULL); killflagsofid(lf->flags, F_STAYINROOM); } int pickup(lifeform_t *lf, object_t *what, int howmany, int fromground, int wantannounce) { char obname[BUFLEN]; object_t *o; //flag_t *f; int failed = B_FALSE; if (!what) { return B_TRUE; } if (howmany == 0) { return B_TRUE; } // in case we get burduned if (isplayer(lf)) statdirty = B_TRUE; getobname(what, obname, howmany); if (howmany == ALL) howmany = what->amt; if (fromground) { if (lfhasflag(lf, F_LEVITATING)) { if (isplayer(lf)) { msg("You can't reach %s from up here!", obname); } return B_TRUE; } } if (!canpickup(lf, what,howmany)) { // tell the player why! if (isplayer(lf)) { switch (reason) { case E_INSUBSTANTIAL: msg("Your hand passes straight through %s.",obname); break; case E_NOSPACE: msg("Your pack is too full to fit any more objects."); break; case E_NOPACK: msg("You lack the ability to carry things!"); break; case E_STUCK: msg("You can't pick up %s while caught in it!",obname); break; case E_NOPICKUP: msg("You can't pick up %s!",obname); break; case E_TOOBIG: msg("%s %s too large for you to lift.",obname, OB1(what,"is","are")); break; case E_TOOHEAVY: msg("%s %s too heavy to lift!",obname, OB1(what,"is","are")); break; case E_GRAVBOOSTED: msg("The %s feels too heavy to lift!",noprefix(obname)); break; default: msg("For some reason, you cannot pick up %s!",obname); break; } } failed = B_TRUE; } if (!failed) { // warn if it is too heavy if (isplayer(lf) && !isburdened(lf) && willburden(lf, what, howmany)) { char ch,buf[BUFLEN]; snprintf(buf, BUFLEN, "Picking up %s will burden you. Continue", obname); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } } if (touch(lf, what)) { taketime(lf, SPEED_PICKUP); return B_TRUE; } if (hasflag(what->flags, F_DEAD)) { taketime(lf, SPEED_PICKUP); return B_TRUE; } } if (!failed) { // try to move whatever was selected o = moveob(what, lf->pack, howmany); if (o) { // if pickup was successful... if (wantannounce) { if (isplayer(lf)) { // refresh obname to copd with stacking getobname(o, obname, o->amt); msgnocap("%c - %s",o->letter, obname); } else if (cansee(player, lf)) { char buf[BUFLEN]; getlfname(lf, buf); msg("%s picks up %s.",buf, obname); } } // you get one free pickup/drop per turn if (lfhasflag(lf, F_DONEPICKUP)) { taketime(lf, SPEED_PICKUP); } else { addflag(lf->flags, F_DONEPICKUP, B_TRUE, NA, NA, NULL); } } else { // tell the player why! if (isplayer(lf)) { switch (reason) { case E_NOSPACE: msg("Your pack is too full to fit any more objects."); break; default: msg("For some reason, you cannot pick up %s!",obname); break; } } failed = B_TRUE; } } if (failed) { // if object isn't where player is, move it to player's pile // for example, if obejct was being picked up via telekenesis if (what->pile->where != lf->cell) { moveob(what, lf->cell->obpile, howmany); } return B_TRUE; } // picked up something our god doesn't like? if (isplayer(lf)) { if ( hasflagknown(o->flags, F_POISONED) || hasflag(o->flags, F_VENOMSAC) || hasflagvalknown(o->flags, F_HITCONFER, F_POISONED, NA, NA, NULL)) { int i,nretgods; lifeform_t *retgod[MAXGODS]; getprayedgods(retgod, &nretgods); for (i = 0 ; i < nretgods; i++) { if (lfhasflagval(retgod[i], F_GODPOISON, B_FALSE, NA, NA, NULL)) { char warnbuf[BUFLEN]; snprintf(warnbuf, BUFLEN, "I hope you're not planning on using %s...", (o->amt == 1) ? "that" : "those"); godsay(retgod[i]->race->id, B_TRUE, warnbuf); break; } } } } return B_FALSE; } // returns true on failure int poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat, enum RACE srcraceid, int immediate) { flag_t *f; int found = B_FALSE,i; enum POISONSEVERITY psev; flag_t *retflag[MAXCANDIDATES]; int nretflags; race_t *srcrace; poisontype_t *pt; pt = findpoisontype(ptype); srcrace = findrace(srcraceid); // special case - lycanthropy only affects players if ((pt->id == P_LYCANTHROPY) && !isplayer(lf)) { return B_TRUE; } // plants can't be diseased if (getraceclass(lf) == RC_PLANT) { return B_TRUE; } // are you immune to disease/poison? psev = pt->severity; switch (psev) { case PS_POISON: if (isimmuneto(lf->flags, DT_POISON, B_FALSE)) return B_TRUE; break; case PS_DISEASE: if (isimmuneto(lf->flags, DT_POISON, B_FALSE)) return B_TRUE; if (hasflag(lf->flags, F_DISEASEIMMUNE)) return B_TRUE; break; case PS_CURSE: default: break; } if ((psev != PS_CURSE) && (howlong != PERMENANT)) { // adjust time based on first aid skill howlong -= getskill(lf, SK_FIRSTAID); if (howlong <= 0) { return B_TRUE; } } getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ( (f->id == F_POISONED) && (f->val[0] == ptype) && (f->lifetime > 0) ) { // extend duration f->lifetime += howlong; // if applicable, remember what race you got it from if (srcrace) { f->val[2] = srcrace->id; } // announce - announceflaggain won't be called // since this isn't a new flag. if (isplayer(lf)) { msg("^%cYou feel more sick.", getlfcol(lf, CC_VBAD)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s looks even more sick.", getlfcol(lf, CC_VBAD), lfname); } found = B_TRUE; } } if (!found) { // incubation period? if (!immediate && pt->incubationtime && isplayer(lf)) { flag_t *ii; int multiplier = 0; int tempmult; if (pt->incubationtime != -1) { // modify incubation time based on metabolism sumflags(lf->flags, F_FASTMETAB, &tempmult, NULL, NULL); multiplier += tempmult; sumflags(lf->flags, F_SLOWMETAB, &tempmult, NULL, NULL); multiplier -= tempmult; if (multiplier > 0) { howlong /= multiplier; } else if (multiplier < 0) { howlong *= abs(multiplier); } } ii = lfhasflagval(lf, F_INCUBATING, ptype, NA, NA, NULL); if (ii) { if (ptype != P_LYCANTHROPY) { // will happen faster ii->val[2] /= 2; if (ii->val[2] < 1) ii->val[2] = 1; if (getskill(lf, SK_FIRSTAID) >= PR_BEGINNER) { ii->known = B_TRUE; msg("^BYou recognise the increased onset of %s.", pt->name); } } } else { char ftext[BUFLEN]; snprintf(ftext, BUFLEN, "%d^%s", power, fromwhat); ii = addflag(lf->flags, F_INCUBATING, ptype, pt->incubationtime, howlong, ftext); ii->obfrom = srcrace ? srcrace->id : NA; if (getskill(lf, SK_FIRSTAID) >= PR_BEGINNER) { ii->known = B_TRUE; msg("^BYou recognise the onset of %s.", pt->name); } else { ii->known = B_FALSE; } } } else { addtempflag(lf->flags, F_POISONED, ptype, power, srcrace ? srcrace->id : NA, fromwhat, howlong); poisoneffects(lf, ptype, power); } } return B_FALSE; } int poisoneffects(lifeform_t *lf, enum POISONTYPE ptid, int power) { flag_t *f; switch (ptid) { case P_FOOD: case P_GAS: case P_VENOM: default: return B_TRUE; case P_MIGRAINE: f = addtempflag(lf->flags, F_DTVULN, DT_SONIC, NA, NA, NULL, FROMPOISON); f->obfrom = ptid; f = addtempflag(lf->flags, F_DTVULN, DT_LIGHT, NA, NA, NULL, FROMPOISON); f->obfrom = ptid; break; case P_WEAKNESS: f = addtempflag(lf->flags, F_ATTRMOD, A_STR, -(power*10), NA, NULL, FROMPOISON); f->obfrom = ptid; // poison type break; case P_ROT: f = addtempflag(lf->flags, F_ATTRMOD, A_CHA, -(power*10), NA, NULL, FROMPOISON); f->obfrom = ptid; f = addtempflag(lf->flags, F_ATTRMOD, A_STR, -(power*5), NA, NULL, FROMPOISON); f->obfrom = ptid; f = addtempflag(lf->flags, F_ATTRMOD, A_CON, -(power*10), NA, NULL, FROMPOISON); f->obfrom = ptid; f = addtempflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL, FROMPOISON); f->obfrom = ptid; break; } return B_FALSE; } int poisonthreatenslife(lifeform_t *lf, flag_t *f) { float time,dam,totaldam; poisontype_t *pt; if (!f) return B_FALSE; pt = findpoisontype(f->val[0]); time = f->lifetime; dam = pt->dam * f->val[1]; totaldam = time * dam; totaldam = pctof(pt->dampct, totaldam); if (totaldam >= lf->hp) { return B_TRUE; } return B_FALSE; } int polymorphto(lifeform_t *lf, enum RACE rid, int howlong) { // alreay that race? just update polymorph timer. if (lf->race->id == rid) { flag_t *f; f = hasflag(lf->flags, F_POLYMORPHED); if (f) { f->lifetime = howlong; } return B_TRUE; } if (!hasflag(lf->flags, F_ORIGRACE)) { // remember the original race addflag(lf->flags, F_ORIGRACE, lf->race->id, NA, NA, NULL); } killflagsofid(lf->flags, F_POLYMORPHED); addtempflag(lf->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, howlong); setrace(lf, rid, B_TRUE); return B_FALSE; } // maybe practice a skill void practice(lifeform_t *lf, enum SKILL skid, int amt) { flag_t *f; skill_t *sk; enum SKILLLEVEL slev; int timeneeded; sk = findskill(skid); if (!sk) return; // do this even if the skill is not 'trainable' setskillused(lf, skid); slev = getskill(lf, skid); timeneeded = sk->traintime * (getskill(lf, skid)+1); if (!timeneeded) return; // can only practice skills which you're capable of learning if (!canlearn(lf, skid)) return; if (!slev || onein(slev)) { // practice a little bit... f = lfhasflagval(lf, F_PRACTICINGSKILL, skid, NA, NA, NULL); if (f) { f->val[1] += amt; } else { f = addflag(lf->flags, F_PRACTICINGSKILL, skid, amt, NA, NULL); } if (f->val[1] >= timeneeded) { // learnt the next rank giveskill(lf, skid); killflag(f); } } } void precalclos(lifeform_t *lf) { cell_t *c; int startxray,rangemod; int maxvisrange,nightvisrange; cell_t **los; //cell_t **losdark; int *blocker; int nlos = 0,i,nn; //int nlosdark = 0; flag_t *f; int endx[MAXVISLIMIT],endy[MAXVISLIMIT]; int nendcells = 0; cell_t *retcell[MAXRETCELLS]; int numpixels; //int db = B_FALSE; enum SKILLLEVEL plev = PR_INEPT; enum SKILLLEVEL elev = PR_INEPT; flag_t *missingeye; //long visdiameter; long allocamt; if (gamemode == GM_CLEANUP) return; if (lf->loslock) return; if (lf->cell->type->id == CT_FAKE) return; if (lf->los) { free(lf->los); lf->los = NULL; } /* if (lf->losdark) { free(lf->losdark); lf->losdark = NULL; } */ if (!lf->born) { lf->nlos = 0; //lf->nlosdark = 0; return; } // right eye missing? missingeye = lfhasflagval(lf, F_INJURY, IJ_EYEDESTROYED, NA, NA, NULL); f = hasflag(lf->flags, F_XRAYVIS); if (f) { startxray = f->val[0]; } else { startxray = 0; } // ie. you can see both ways, plus your own cell //visdiameter = (MAXVISRANGE+1)*2; //allocamt = visdiameter * visdiameter; allocamt = MAX_MAPW * MAX_MAPH; los = malloc( sizeof(cell_t *) * allocamt); //losdark = malloc( sizeof(cell_t *) * allocamt); blocker = malloc( sizeof(cell_t *) * allocamt); nlos = 0; //nlosdark = 0; //maxvisrange = getvisrange(lf, B_FALSE); maxvisrange = getvisrange(lf, B_TRUE); nightvisrange = getnightvisrange(lf); plev = getskill(lf, SK_PERCEPTION); elev = getskill(lf, SK_ENGINEERING); // find all cells at max fov nendcells = 0; if (MAXOF(maxvisrange, nightvisrange) == 0) { } else if (lfhasflag(lf, F_AWARENESS) || (lf->facing == D_ALL)) { get_circular_fov_endpoints(lf, maxvisrange, endx, endy, &nendcells); } else { enum QUADRANT startq, endq,curq; if (!get_adjacent_quadrants(lf->facing, &startq, &endq)) { if (missingeye) endq = startq; // ie. lose the right hand one if (plev >= PR_MASTER) { inc_quad_range(&startq, missingeye ? NULL : &endq, 2); } else if (plev >= PR_BEGINNER) { inc_quad_range(&startq, missingeye ? NULL : &endq, 1); } curq = startq; get_fov_quad_endpoints(lf, curq, maxvisrange, endx, endy, &nendcells); while (curq != endq) { if (curq == Q_NNW) curq = Q_NNE; else curq++; get_fov_quad_endpoints(lf, curq, maxvisrange, endx, endy, &nendcells); } if (plev >= PR_NOVICE) { enum TURNDIR turndir; cell_t *adjcell; // include cells immediately to the lifeform's left and right. for (turndir = missingeye ? TD_LEFT : TD_RIGHT; turndir <= TD_LEFT; turndir++) { int dir; dir = rotatedir(lf->facing, turndir, 2); adjcell = getcellindir(lf->cell, dir); if (adjcell) { endx[nendcells] = adjcell->x; endy[nendcells] = adjcell->y; nendcells++; } } } } } // end if facing == all assert(nendcells < MAXVISLIMIT); // look in the lf's field of vision arc //for (ang = 0; ang < 360; ang += 30) { for (nn = 0; nn < nendcells; nn++) { int keepgoing = B_TRUE; int currange,xray; int n; // start at lf's cell //c = lf->cell; //x = c->x; //y = c->y; xray = startxray; currange = 0; // calc path to end cell calcbresnham(lf->cell->map, lf->cell->x, lf->cell->y, endx[nn], endy[nn], retcell, &numpixels ); assert(numpixels < MAXRETCELLS); // keep going until we lose los for (n = 0; keepgoing && (n < numpixels); n++) { cell_t *nextc = NULL; c = retcell[n]; if (n < numpixels-1) { nextc = retcell[n+1]; } if (n != 0) currange++; if (currange > maxvisrange) c = NULL; if (c) { int found = B_FALSE; // have we already marked it as seen? for (i = 0; i < nlos; i++) { if (los[i] == c) { if (blocker[i]) { keepgoing = B_FALSE; } found = B_TRUE; break; } } if (!found) { /* int litforus; // is the cell lit? if it isn't, then still keep going, // as there might be a light-producing object further on. litforus = celllitfor(lf, c, maxvisrange, nightvisrange); */ if (!celltransparentfor(lf, c, &xray, &rangemod)) { keepgoing = B_FALSE; } currange += rangemod; if (currange > maxvisrange) keepgoing = B_FALSE; // if keepgoing was false, still count it // BUT then stop looking. los[nlos] = c; blocker[nlos] = keepgoing ? B_FALSE : B_TRUE; nlos++; assert (nlos < allocamt); } // high engineering lets you detect hollow walls. ie. // if a wall has another wall behind it, you 'see' it. if (!keepgoing) { if (nextc && (elev >= PR_BEGINNER) && nextc->type->solid && (c != lf->cell) && (!c->type->transparent) && isadjacent(c, lf->cell) && !isadjacent(nextc, lf->cell)) { // we can see through. keepgoing = B_TRUE; } } } else { // ie. if !c keepgoing = B_FALSE; } } // end foreach cell and while keepgoing } assert(nlos < allocamt); //assert(nlosdark < allocamt); // now fill in lifeform structure if (nlos) { lf->los = malloc(sizeof(cell_t *) * nlos); for (i = 0; i < nlos; i++) { lf->los[i] = los[i]; } } else { lf->los = NULL; } lf->nlos = nlos; /* if (nlosdark) { lf->losdark = malloc(sizeof(cell_t *) * nlosdark); for (i = 0; i < nlosdark; i++) { lf->losdark[i] = losdark[i]; } } else { lf->losdark = NULL; } lf->nlosdark = nlosdark; */ free(los); //free(losdark); free(blocker); if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) { needredraw = B_TRUE; } } void preparecorpse(lifeform_t *lf, object_t *corpse) { char obname[BUFLEN]; if (corpse->amt > 1) { corpse = splitob(corpse); } if (isplayer(lf) && (corpse->pile->owner == lf)) { // add flag _before_ getting the name addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); killflagsofid(corpse->flags, F_GENERATES); // stop making blood if (!hasflag(corpse->flags, F_TAINTED)) { flag_t *f; // it lasts a lot longer now. f = hasflag(corpse->flags, F_OBHP); if (f) { f->val[1] *= 3; f->val[0] = f->val[1]; } } getobname(corpse, obname, corpse->amt); msgnocap("%c - %s.", corpse->letter, obname); } else if (isplayer(lf)) { // add flag after getting the name getobname(corpse, obname, corpse->amt); msg("You cook %s.", obname); addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); killflagsofid(corpse->flags, F_GENERATES); // stop making blood } else if (cansee(player, lf)) { char lfname[BUFLEN]; // add flag after getting the name getlfname(lf, lfname); getobname(corpse, obname, corpse->amt); msg("%s cooks %s.", obname); addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); killflagsofid(corpse->flags, F_GENERATES); // stop making blood } else { addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL); killflagsofid(corpse->flags, F_FROZEN); killflagsofid(corpse->flags, F_GENERATES); // stop making blood } } int push(lifeform_t *lf, object_t *o, int dir) { cell_t *obcell, *dstcell; char obname[BUFLEN]; getobname(o, obname, o->amt); // remember cells obcell = o->pile->where; dstcell = getcellindir(obcell, dir); // take time (even if it will fail) - twice normal taketime(lf, getactspeed(lf) * 2); if (!obcell || !dstcell) { return B_TRUE; } if (touch(lf, o)) { return B_TRUE; } // move object o = moveob(o, dstcell->obpile, o->amt); if (!o) { return B_TRUE; } // move player moveto(lf, obcell, B_TRUE, B_FALSE); // announce if (isplayer(lf)) { msg("You push %s.", obname); } else if (haslos(player, dstcell)) { char buf[BUFLEN]; getlfname(lf, buf); capitalise(buf); msg("%s pushes %s.", cansee(player, lf) ? buf : "Something", obname); } touch(lf, o); addflagifneeded(lf->flags, F_MOVED, B_TRUE, NA, NA, NULL); addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL); return B_FALSE; } int racecantalk(enum RACE rid) { race_t *r; r = findrace(rid); if (r) { switch (r->raceclass->id) { case RC_DEMON: case RC_DRAGON: case RC_GOD: case RC_HUMANOID: // these ones can talk return B_TRUE; break; default: break; } if (hasflag(r->flags, F_CANTALK)) return B_TRUE; } return B_FALSE; } // returns wheter, in general, creatures of this raceclass will bleed. // (individual creatures may override this though) int raceclassbleeds(enum RACECLASS id) { switch (id) { case RC_ANIMAL: case RC_AQUATIC: case RC_DRAGON: case RC_HUMANOID: case RC_INSECT: return B_TRUE; default: break; } return B_FALSE; } int racemeetscondition(race_t *r, enum CELLCONDITION cond, int arg, int val) { int ok = B_FALSE; flag_t *f; switch (cond) { case CC_HASCORPSE: if (val == B_TRUE) { if (!hasflag(r->flags, F_NOCORPSE) && !hasflag(r->flags, F_CORPSETYPE) && !hasflag(r->flags, F_EXTRACORPSE)) { ok = B_TRUE; } } else { if (hasflag(r->flags, F_NOCORPSE) || hasflag(r->flags, F_CORPSETYPE) || hasflag(r->flags, F_EXTRACORPSE)) { ok = B_TRUE; } } break; case CC_HASFLAG: if (val == B_TRUE) { if (hasflag(r->flags, arg)) ok = B_TRUE; } else { if (!hasflag(r->flags, arg)) ok = B_TRUE; } break; case CC_HASSIZE: f = hasflag(r->flags, F_SIZE); if (val == B_TRUE) { if (f && (f->val[0] == arg)) { ok = B_TRUE; } } else { if (f && (f->val[0] != arg)) { ok = B_TRUE; } } break; case CC_NONE: ok = B_TRUE; break; default: break; } return ok; } int racemeets(enum RACE rid, condset_t *cs) { race_t *r; int i; r = findrace(rid); assert(r); if (!cs) return B_TRUE; for (i = 0; i < cs->nconds; i++) { if (!racemeetscondition(r, cs->cond[i], cs->arg[i], cs->val[i])) { return B_FALSE; } } return B_TRUE; } int readytotrain(lifeform_t *lf) { if (lf->skillpoints || getattpoints(lf) || levelabilityready(lf)) { return B_TRUE; } return B_FALSE; } int recruit(lifeform_t *lf) { enum ALIGNMENT pa,lfa; // alignments must match, otherwire VERY hard to make them join! pa = getalignment(player); lfa = getalignment(lf); int rv = B_FALSE; if (lfhasflag(lf, F_NOHIRE)) { // refusing to join at all. sayphrase(lf, SP_RECRUIT_DECLINE, SV_TALK, NA, NULL, player); rv = B_TRUE; } else if ( ((pa == AL_GOOD) && (lfa == AL_EVIL)) || ((pa == AL_EVIL) && (lfa == AL_GOOD)) ) { sayphrase(lf, SP_RECRUIT_DECLINE, SV_TALK, NA, NULL, player); rv = B_TRUE; } else { int dohire = B_FALSE; int askingprice = -1; char lfname[BUFLEN]; char buf[BUFLEN]; flag_t *f; getlfname(lf, lfname); // they will consider it - now negotiate a price f = lfhasflag(lf, F_HIREPRICE); if (f) { askingprice = f->val[0]; } else { int result; int difficulty; int minmult,maxmult; // since you have to be at least speech=5(expert) to ask someone to // join, add +20 to difficulty (pr_skilled * 5) difficulty = 60 + 20 + ((gettr(player) - gettr(lf))*5); if (real_skillcheck(player, SC_SPEECH, difficulty, 0, &result)) { minmult = 10; maxmult = 20; // passed } else { if (result <= 30) { // very expensive minmult = 20; maxmult = 30; } else { // expensive minmult = 15; maxmult = 25; } } askingprice = rnd(gettr(lf)*minmult, gettr(lf)*maxmult ); addflag(lf->flags, F_HIREPRICE, askingprice, NA, NA, NULL); } if (askingprice != 0) { // modify for same job if (getjob(player) == getjob(lf)) { askingprice = pctof(50, askingprice); } else if (player->race->baseid == lf->race->baseid) { // modify for same race askingprice = pctof(80, askingprice); } // modify by charisma askingprice = pctof(100 - getstatmod(player, A_CHA), askingprice); limit(&askingprice, 0, NA); } if (askingprice > 0) { sayphrase(lf, SP_RECRUIT_ASKPRICE, SV_TALK, askingprice, NULL, player); more(); if (askingprice > countmoney(player->pack)) { } else { char ch; snprintf(buf, BUFLEN, "Pay $%d to hire %s", askingprice, lfname); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); if (ch == 'y') { dohire = B_TRUE; } } } else { dohire = B_TRUE; } if (dohire) { char *p = NULL; petify(lf, player); // give them a name //if (getjob(lf)) { if (lf->race->raceclass->id == RC_HUMANOID) { p = assignnpcname(lf->flags); } sayphrase(lf, SP_RECRUIT_ACCEPT, SV_TALK, NA, p, player); } else { if (askingprice > countmoney(player->pack)) { sayphrase(lf, SP_RECRUIT_DECLINE_CANTPAY, SV_TALK, askingprice, NULL, player); } else { sayphrase(lf, SP_RECRUIT_DECLINE_WONTPAY, SV_TALK, askingprice, NULL, player); } } } // end if !nohire return rv; } void refreshlevelabilities(lifeform_t *lf) { flag_t *f; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(lf->flags, retflag, &nretflags, F_LEVABIL, F_LEVFLAG, F_LEVSPELL, F_LEVSPELLSCHOOL, F_LEVSPELLSCHOOLFROMX, F_LEVSKILL, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; // we previously set timeleft to -1 while actually assigning these. // now we need to restore the original setting. switch (f->id) { case F_LEVFLAG: case F_LEVABIL: case F_LEVSKILL: case F_LEVSPELL: case F_LEVSPELLSCHOOL: case F_LEVSPELLSCHOOLFROMX: //f->lifetime = FROMJOB; f->lifetime = f->origlifetime; break; default: break; } } } // move lf to the _START_ of the destination map list void relinklf(lifeform_t *src, map_t *dst) { map_t *srcmap; srcmap = src->cell->map; // unlink this player from the current list if (src->prev == NULL) { // first srcmap->lf = src->next; } else { // not first src->prev->next = src->next; } if (src->next == NULL) { // last srcmap->lastlf = src->prev; } else { // not last src->next->prev = src->prev; } // add this lf to the START of the list on the new map if (dst->lf == NULL) { // first (and only) element in new list dst->lf = src; src->prev = NULL; src->next = NULL; dst->lastlf = src; } else { lifeform_t *aa; // go to start of list aa = dst->lf; dst->lf = src; src->prev = NULL; src->next = aa; aa->prev = src; } // note this function will NOT set the lifeform's cell to one on the new map. // now rise up sortlf(dst, src); } void setskillused(lifeform_t *lf, enum SKILL skid) { flag_t *f; f = lfhasflagval(lf, F_HASSKILL, skid, NA, NA, NULL); if (f) { f->val[2] = B_TRUE; } } void spot_hiding_lf(lifeform_t *lf, lifeform_t *hider) { addflag(lf->flags, F_SPOTTED, hider->id, NA, NA, NULL); setlosdirty(lf); // announce if (isplayer(lf)) { char hidername[BUFLEN]; getlfnamea(hider, hidername); needredraw = B_TRUE; msg("^wYou spot %s!", hidername); } else if (isplayer(hider) && cansee(hider, lf)) { if (getlorelevel(hider, lf->race->raceclass->id)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("You think %s has spotted you!", lfname); } } practice(lf, SK_PERCEPTION, 1); } int startclimbing(lifeform_t *lf) { cell_t *where; char lfname[BUFLEN]; object_t *pit = NULL,*obstacle = NULL; flag_t *f; char pitname[BUFLEN]; char obname[BUFLEN]; getlfname(lf, lfname); where = getcellindir(lf->cell, lf->facing); pit = hasobwithflagval(where->obpile, F_PIT, D_DOWN, NA, NA, NULL); if (pit) { getobname(pit, pitname, 1); } else { strcpy(pitname, ""); } // technically you need the climbing skill to do this, // since the only way to direclty trigger 'startclimbing' // is either by prompting when moving onto a CLIMBOBSTACLE // (which a pit isn't), or by using the 'climb' // ability (which you need the skill to get). if (pit && isplayer(lf)) { char ques[BUFLEN], ch; snprintf(ques, BUFLEN, "Climb down %s?",pitname); ch = askchar(ques, "yn","n", B_TRUE, B_FALSE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } } obstacle = hasobwithflag(where->obpile, F_CLIMBOBSTACLE); if (obstacle) { getobname(obstacle, obname, 1); } else { strcpy(obname, ""); } taketime(lf, getmovespeed(lf)); // climbing down a pit? if (pit) { int diff; // move there. movelf(lf, where, B_TRUE); // make a skill check. //mod = (countadjwalls(where)+1)/2; diff = getcellclimbdifficultyavg(where); if (skillcheck(lf, SC_CLIMB, diff, 0)) { // if you pass, safely move down the pit. // usestairs() will announce this. usestairs(lf, pit, B_TRUE, B_TRUE); } else { // you will fall... if (isplayer(lf)) { msg("^bYou lose your footing!"); } } } else if (obstacle) { f = hasflag(obstacle->flags, F_CLIMBOBSTACLE); if (skillcheck(lf, SC_CLIMB, f->val[0], 0)) { // announce if (isplayer(lf)) { msg("You climb onto %s.", obname); } else if (cansee(player, lf)) { if (haslos(player, where)) { msg("%s climbs onto %s.", lfname, obname); } else { msg("%s climbs out of view.", lfname); } } movelf(lf, where, B_TRUE); practice(lf, SK_CLIMBING, 1); } else { cell_t *c2; if (isplayer(lf)) { msg("^wYou try to climb onto %s, but lose your footing!", obname); } else if (cansee(player, lf)) { msg("%s tries to start climbing, but slips.", lfname); } c2 = getrandomadjcell(where, &ccwalkable, B_NOEXPAND); if (c2) { movelf(lf, c2, B_TRUE); } fall(lf, NULL, B_TRUE); // fall return B_TRUE; } } else { // you need to climbing skill to climb walls if (!getskill(lf, SK_CLIMBING) && !lfhasflag(lf, F_SPIDERCLIMB)) { if (isplayer(lf)) { msg("You are not sufficiently skilled to climb walls."); } return B_TRUE; } else if (skillcheck(lf, SC_CLIMB, getcellclimbdifficulty(where), 0)) { // announce if (isplayer(lf)) { msg("You climb onto %s %s.", needan(where->type->name) ? "an" : "a", where->type->name); } else if (cansee(player, lf)) { if (haslos(player, where)) { msg("%s climbs onto %s %s.", lfname, needan(where->type->name) ? "an" : "a", where->type->name); } else { msg("%s climbs out of view.", lfname); } } // change facing BEFORE moving, so that we don't reveal cells on the other // side of the wall setfacing(lf, diropposite(lf->facing)); movelf(lf, where, B_TRUE); addflag(lf->flags, F_CLIMBING, B_TRUE, NA, NA, NULL); } else { if (isplayer(lf)) { msg("You try to climb onto %s, but fail.", where->type->name); } else if (cansee(player, lf)) { msg("%s tries to start climbing, but fails.", lfname); } practice(lf, SK_CLIMBING, 1); return B_TRUE; } } practice(lf, SK_CLIMBING, 1); return B_FALSE; } int startresting(lifeform_t *lf, int willtrain) { flag_t *f; // player can't rest while in the air, unless you're in a motel room if (isplayer(lf)) { if (isairborne(lf, NULL) && !lfhasflag(lf, F_RESTINGINMOTEL)) { msg("You can't %s while airborne!", willtrain ? "train" : "rest"); return B_TRUE; } } if (lfhasflag(lf, F_RAGE)) { if (isplayer(lf)) msg("You are too enraged to %s!", willtrain ? "train" : "rest"); return B_TRUE; } // stop hiding killflagsofid(lf->flags, F_HIDING); // stop all spells stopallspells(lf); stopsprinting(lf); killflagsofid(lf->flags, F_INTERRUPTED); if (willtrain) { f = lfhasflag(lf, F_TRAINING); if (f) { int trainloss; trainloss = 25; trainloss = modifybystat(trainloss, player, A_IQ); f->val[0] -= trainloss; limit(&(f->val[0]), 0, NA); } else { int traincounter; traincounter = 50; traincounter = modifybystat(traincounter, player, A_IQ); addflag(lf->flags, F_TRAINING, 0, traincounter, NA, NULL); } } else { if (gotosleep(lf, B_WAKEWHENHEALED)) { // failed return B_TRUE; } } if (isplayer(lf)) { statdirty = B_TRUE; needredraw = B_TRUE; lf->turnsskipped = 0; } // stop movement for all allies if (isplayer(lf)) { lifeform_t *l; for (l = lf->cell->map->lf ; l ; l = l->next) { if ((l != lf) && areallies(l, lf)) { killflagsofid(l->flags, F_TARGETCELL); } } } if (isplayer(lf)) { if (willtrain) { msg("You start training..."); } else { if (getlftemp(lf) >= T_HOT) { if (!isimmuneto(lf->flags, DT_FIRE, B_FALSE) && !isresistantto(lf->flags, DT_FIRE, B_FALSE)) { msg("You sleep fitfully due to the heat (reduced healing rate)."); } } } drawmsg(); updatescreen(); drawcursor(); } // do the first one right away rest(lf, B_TRUE); return B_FALSE; } int rollattr(enum ATTRBRACKET bracket) { int roll = 0; switch (bracket) { case AT_EXLOW: roll = rnd(0,12); break; case AT_VLOW: roll = rnd(13, 23); break; case AT_LOW: roll = rnd(24, 34); break; case AT_LTAVERAGE: roll = rnd(35, 45); break; case AT_AVERAGE: roll = rnd(46, 56); break; case AT_GTAVERAGE: roll = rnd(57, 67); break; case AT_HIGH: roll = rnd(68, 78); break; case AT_VHIGH: roll = rnd(79, 89); break; case AT_EXHIGH: roll = rnd(90, 100); break; default: roll = rolldie(3,6)*5; break; } return roll; } int rollstat(lifeform_t *lf, enum ATTRIB attr) { flag_t *f; enum ATTRBRACKET bracket; f = hasflagval(lf->flags, F_STARTATT, attr, NA, NA, NULL); if (f) { if (strlen(f->text)) { int val; if (strchr(f->text, '-')) { int min,max; char *p; char buf[BUFLENSMALL]; // text is a range p = readuntil(buf, f->text, '-'); min = atoi(buf); p = readuntil(buf, p, '-'); max = atoi(buf); val = rnd(min,max); } else { val = atoi(f->text); } lf->att[attr] = val; return B_FALSE; } else { bracket = f->val[1]; } } else { bracket = AT_RANDOM; } lf->att[attr] = rollattr(bracket); return B_FALSE; } // safe to rest? int safetorest(lifeform_t *lf, enum ERROR *why) { lifeform_t *l; if (why) *why = E_OK; if (lfhasflag(lf, F_STASIS)) { if (why) *why = E_STASIS; return B_FALSE; } for (l = lf->cell->map->lf ; l ; l = l->next) { if ((l != lf) && (areenemies(lf, l) || !isknownpeaceful(l) ) && !lfhasflag(l, F_HARMLESS) && !lfhasflag(l, F_FEIGNINGDEATH)) { int monsternearby = B_FALSE; if (isplayer(lf)) { if (cansee(lf, l)) { monsternearby = B_TRUE; } } else { if (haslof(lf->cell, l->cell, LOF_WALLSTOP, NULL)) { monsternearby = B_TRUE; } } if (monsternearby) { if (why) *why = E_MONSTERNEARBY; return B_FALSE; } } } if (getlftemp(lf) <= T_VCOLD) { if (!isimmuneto(lf->flags, DT_COLD, B_FALSE) && !isresistantto(lf->flags, DT_COLD, B_FALSE)) { if (why) *why = E_TOOCOLD; return B_FALSE; } } // extra checks for monsters if (!isplayer(lf)) { int timeneeded; flag_t *f; // must have gone at lesat 10 turns without being in battle timeneeded = 16 - (getmorale(lf) / 5); limit(&timeneeded, 5, NA); f = lfhasflag(lf, F_TURNSINPEACE); if (!f || (f->val[0] < timeneeded)) { if (why) *why = E_TOOSOON; return B_FALSE; } } return B_TRUE; } // if lf is null, the player will just get a message \"blah blah blah\" // this is used (for example) when shopkeepers are talking from the // in-shop menu. int say(lifeform_t *lf, char *text, int volume) { char seebuf[BUFLEN]; char hearbuf[BUFLEN]; char verb[BUFLEN]; char noun[BUFLEN]; char *localtext; int rv = B_FALSE; if (lf && lfhasflag(lf, F_SILENCED)) { return B_FALSE; } localtext = strdup(text); // adjust text and volume for gods if (lf) { switch (lf->race->id) { case R_GODMAGIC: volume = SV_TALK; strrep(&localtext, "You have", "One has", NULL); strrep(&localtext, "You", "One", NULL); strrep(&localtext, "you", "one", NULL); break; case R_GODTHIEVES: case R_GODDEATH: volume = SV_WHISPER; break; case R_GODFIRE: volume = SV_TRUCK; makeuppercase(localtext); break; case R_GODPURITY: case R_GODLIFE: case R_GODMERCY: case R_GODNATURE: volume = SV_TALK; break; case R_GODBATTLE: if (localtext[strlen(localtext)-1] == '.') { localtext[strlen(localtext)-1] = '!'; } volume = SV_SHOUT; break; default: break; } } if (volume < 2) { strcpy(verb, "whispers"); strcpy(noun, "a whisper:"); } else if (volume == 2) { strcpy(verb, "says"); strcpy(noun, "a voice:"); } else if (volume == 3) { strcpy(verb, "shouts"); strcpy(noun, "a shout:"); } else if (volume == 4) { strcpy(verb, "roars"); strcpy(noun, "a roar:"); } else { // ie > 4 strcpy(verb, "bellows"); strcpy(noun, "a bellow:"); } snprintf(seebuf, BUFLEN, "%s \"%s\"", verb, localtext); snprintf(hearbuf, BUFLEN, "%s \"%s\"", noun, localtext); if (lf) { rv = noise(lf->cell, lf, NC_SPEECH, volume, hearbuf, seebuf); } else { msg("\"%s\"", localtext); } free(localtext); return rv; } // volume = -1 means "auto" int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *text, lifeform_t *talkingto) { int i,rv = B_FALSE; char buf[BUFLEN]; char buf2[BUFLEN]; char buf3[BUFLEN]; char *p,*p2; race_t *r; if (lf && lfhasflag(lf, F_SILENCED)) { return B_FALSE; } switch (what) { case SP_ALLY_ATTACK: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "I'm attacking %s!", text); break; case 2: snprintf(buf, BUFLEN, "%s is mine!", text); buf[0] = toupper(buf[0]); break; case 3: snprintf(buf, BUFLEN, "I'll take care of %s!", text); break; } rv = say(lf, buf, volume); break; case SP_ALLY_ATTACKUNSEEN: p = strdup(text); strrep(&p, "the ","a ", NULL); switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "There's %s over here!", p); break; case 2: snprintf(buf, BUFLEN, "I'm attacking %s!", p); break; case 3: snprintf(buf, BUFLEN, "Beware %s!", p); break; } free(p); rv = say(lf, buf, volume); break; case SP_ALLY_INPAIN: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "I'm hurting here!"); break; case 2: snprintf(buf, BUFLEN, "I need to rest soon!"); break; case 3: snprintf(buf, BUFLEN, "Help me!"); break; } rv = say(lf, buf, volume); break; case SP_ALLY_TARGETKILL: switch (rnd(1,4)) { case 1: snprintf(buf, BUFLEN, "Got it!"); break; case 2: snprintf(buf, BUFLEN, "%s one, %s zero!", lf ? lf->race->name : "Me", noprefix(text)); buf[0] = toupper(buf[0]); break; case 3: snprintf(buf, BUFLEN, "Pow!"); break; case 4: snprintf(buf, BUFLEN, "Take that!"); break; } rv = say(lf, buf, volume); break; case SP_BEG: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "Spare a coin, mister?"); break; case 2: snprintf(buf, BUFLEN, "Alms for the poor!"); break; case 3: snprintf(buf, BUFLEN, "Alms!"); break; } rv = say(lf, buf, volume); break; case SP_BEGATTACK: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "Now give me the everything else!"); break; case 2: snprintf(buf, BUFLEN, "Rich fool!"); break; case 3: snprintf(buf, BUFLEN, "Is that all?"); break; } rv = say(lf, buf, volume); break; case SP_BEGTHANKS: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "A thousand thanks, good sir!"); break; case 2: snprintf(buf, BUFLEN, "Oh thank you!"); break; case 3: snprintf(buf, BUFLEN, "My family shall eat tonight!"); break; } rv = say(lf, buf, volume); break; case SP_CLOSEDTILMORN: switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "We're closed, come back in the morning!"); break; case 2: snprintf(buf, BUFLEN, "Sorry, we're only open during daylight hours."); break; } rv = say(lf, buf, volume); break; case SP_CLOSEDTILNIGHT: switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "We're closed, come back in the evening!"); break; case 2: snprintf(buf, BUFLEN, "Sorry, we're only open during nighttime hours."); break; } rv = say(lf, buf, volume); break; case SP_CLOSEDTILTIME: switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "We're closed, come back at %d o'clock.", val0); break; case 2: snprintf(buf, BUFLEN, "Sorry, we're closed until %d o'clock.", val0); break; } rv = say(lf, buf, volume); break; case SP_DIE: switch (rnd(1,4)) { case 1: if (lf && ispetof(lf, player)) { getplayername(buf2); snprintf(buf, BUFLEN, "Avenge me, %s!", buf2); } else { snprintf(buf, BUFLEN, "My death will be avenged!"); } break; case 2: snprintf(buf, BUFLEN, "Argh!"); break; case 3: snprintf(buf, BUFLEN, "Nooooo!"); break; case 4: snprintf(buf, BUFLEN, "This isn't over!"); break; } rv = say(lf, buf, volume); break; case SP_DRUNK: // random blurred speech strcpy(buf, ""); for (i = 0; i < rnd(15,30); i++) { if ((i != 0) && onein(4)) { strcat(buf, " "); } else { char let[2]; let[0] = rnd('a', 'z'); let[1] = '\0'; strcat(buf, let); } } if (volume >= SV_SHOUT) { strcat(buf, "!"); } else { strcat(buf, "."); } say(lf, buf, volume); break; case SP_LIFEOB_DESTROYED: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "NOOOOOOOOO!"); break; case 2: snprintf(buf, BUFLEN, "NO! What have you done!?"); break; case 3: snprintf(buf, BUFLEN, "It cannot be!"); break; } rv = say(lf, buf, volume); break; case SP_MERCYACCEPT: switch (rnd(1,2)) { case 1: r = findrace(val0); snprintf(buf, BUFLEN, "Cowardly %s...", text); break; case 2: snprintf(buf, BUFLEN, "Guess I don't need to actually kill you..."); break; } rv = say(lf, buf, volume); break; case SP_INFO_ACCEPT: switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "Okay, here's what I know..."); break; case 2: if (lf && talkingto && (talkingto->race->id != lf->race->id)) { snprintf(buf, BUFLEN, "Listen carefully, %s...", talkingto->race->name); break; } else { snprintf(buf, BUFLEN, "Listen carefully..."); break; } } rv = say(lf, buf, volume); break; case SP_INFO_ASKPRICE: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "I'll tell you for $%d...",val0); break; case 2: snprintf(buf, BUFLEN, "Is the info worth $%d to you?",val0); break; case 3: snprintf(buf, BUFLEN, "$%d and I'll tell you...",val0); break; } rv = say(lf, buf, volume); break; case SP_INFO_REFUSE: switch (rnd(1,4)) { case 1: snprintf(buf, BUFLEN, "What do you think I am, a library?"); break; case 2: snprintf(buf, BUFLEN, "Can't help, sorry."); break; case 3: if (lf && talkingto && (talkingto->race->id != lf->race->id)) { snprintf(buf, BUFLEN, "Get lost, %s!", talkingto->race->name); } else { snprintf(buf, BUFLEN, "Get lost!"); } break; case 4: if (lf && talkingto && (talkingto->race->id != lf->race->id)) { snprintf(buf, BUFLEN, "No time to talk, %s!", talkingto->race->name); } else { snprintf(buf, BUFLEN, "No time to talk!"); } break; } rv = say(lf, buf, volume); break; case SP_INFO_REFUSE_AGAIN: switch (rnd(1,4)) { case 1: snprintf(buf, BUFLEN, "Asking twice isn't going to change the answer!"); break; case 2: snprintf(buf, BUFLEN, "Still can't help, sorry."); break; case 3: snprintf(buf, BUFLEN, "I already told you to go away!"); break; case 4: snprintf(buf, BUFLEN, "Stop pestering me!"); break; } rv = say(lf, buf, volume); break; case SP_INFO_DECLINE_WONTPAY: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "Cheapskate."); break; case 2: snprintf(buf, BUFLEN, "Well, I'll be here if you change your mind."); break; case 3: snprintf(buf, BUFLEN, "Your loss."); break; } rv = say(lf, buf, volume); break; case SP_PAYWARN: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "Hey! Where do you think you're going?"); break; case 2: snprintf(buf, BUFLEN, "AHEM!"); break; case 3: snprintf(buf, BUFLEN, "I hope you are going to pay for %s!", text); break; } rv = say(lf, buf, volume); break; case SP_PAYTHREAT: switch (rnd(1,3)) { case 1: rv = say(lf, "Stop thief!", volume); break; case 2: rv = say(lf, "GUARDS!", volume); break; case 3: rv = say(lf, "I've been robbed!", volume); break; } break; case SP_RECRUIT_ACCEPT: if (lf->race->id == R_PRISONER) { if (text) { snprintf(buf, BUFLEN, "Thank you! My name is %s.", text); } else { snprintf(buf, BUFLEN, "Thank you!"); } } else { if (text) { snprintf(buf, BUFLEN, "I will join you - my name is %s.", text); } else { snprintf(buf, BUFLEN, "I will join you."); } } rv = say(lf, buf, volume); break; case SP_RECRUIT_ASKPRICE: switch (rnd(1,8)) { case 1: snprintf(buf, BUFLEN, "My services will cost you $%d.", val0); break; case 2: snprintf(buf, BUFLEN, "$%d and you have yourself a deal.", val0); break; case 3: snprintf(buf, BUFLEN, "Okay. How does $%d sound?", val0); break; case 4: snprintf(buf, BUFLEN, "I'll do it for $%d.", val0); break; case 5: snprintf(buf, BUFLEN, "Let's see... $%d should do it.", val0); break; case 6: snprintf(buf, BUFLEN, "My fee is $%d. Still interested?", val0); break; case 7: snprintf(buf, BUFLEN, "I don't work for free. $%d.", val0); break; case 8: snprintf(buf, BUFLEN, "$%d and your cause is mine.", val0); break; } rv = say(lf, buf, volume); break; case SP_RECRUIT_DECLINE: switch (rnd(1,8)) { case 1: snprintf(buf, BUFLEN, "No, I regretfully decline your offer."); break; case 2: snprintf(buf, BUFLEN, "You dare ask me for help?"); break; case 3: snprintf(buf, BUFLEN, "I will never help you."); break; case 4: snprintf(buf, BUFLEN, "No."); break; case 5: snprintf(buf, BUFLEN, "Me? Help you?."); break; case 6: snprintf(buf, BUFLEN, "Ahem. I think not."); break; case 7: snprintf(buf, BUFLEN, "I don't think that would be a good idea."); break; case 8: snprintf(buf, BUFLEN, "Sorry, but no."); break; } rv = say(lf, buf, volume); break; case SP_RECRUIT_DECLINE_CANTPAY: rv = say(lf, "...which I see you cannot afford.", volume); break; case SP_RECRUIT_DECLINE_WONTPAY: rv = say(lf, "Perhaps another time, then.", volume); break; case SP_ROBBED: switch (rnd(0,1)) { case 0: rv = say(lf, "Hey! Where are my things?", volume); break; case 1: rv = say(lf, "I've been robbed!", volume); break; } break; case SP_SORRY: if (lf && getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) >= AT_HIGH) { switch (rnd(0,1)) { case 0: rv = say(lf, "My sincerest condolences!", volume); break; case 1: rv = say(lf, "My mistake, I apologise.", volume); break; } } else { switch (rnd(0,1)) { case 0: rv = say(lf, "Whoops, sorry!", volume); break; case 1: rv = say(lf, "Sorry 'bout that!", volume); break; } } break; case SP_PAYTHANKS: switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "Pleasure doing business with you!"); break; case 2: snprintf(buf, BUFLEN, "Thank you, come again!"); break; } rv = say(lf, buf, volume); break; case SP_THANKS: switch (rnd(1,5)) { case 1: snprintf(buf, BUFLEN, "Why, thank you!"); break; case 2: snprintf(buf, BUFLEN, "You have my gratitude!"); break; case 3: snprintf(buf, BUFLEN, "Thanks!"); break; case 4: snprintf(buf, BUFLEN, "Thank you, that is very generous of you!"); break; case 5: snprintf(buf, BUFLEN, "How kind of you!"); break; } rv = say(lf, buf, volume); break; case SP_TOOCLOSE: switch (rnd(1,7)) { case 1: snprintf(buf, BUFLEN, "Stay away!"); break; case 2: snprintf(buf, BUFLEN, "Keep back!"); break; case 3: snprintf(buf, BUFLEN, "Keep your distance!"); break; case 4: snprintf(buf, BUFLEN, "Get away from me!"); break; case 5: snprintf(buf, BUFLEN, "That's close enough."); break; case 6: snprintf(buf, BUFLEN, "Out of my way!"); break; case 7: snprintf(buf, BUFLEN, "Don't come any closer!"); break; } rv = say(lf, buf, volume); break; case SP_TRADEINFO_ACCEPT: p = text; p2 = buf2; while (*p != '^') { *p2 = *p; p++; p2++; } *p2 = '\0'; p++; // next one... p2 = buf3; while (*p != '\0') { *p2 = *p; p++; p2++; } *p2 = '\0'; switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "I'll train you in %s...", buf3); break; case 2: snprintf(buf, BUFLEN, "I can teach you %s...", buf3); break; } strcat(buf, "if you teach me "); strcat(buf, buf2); rv = say(lf, buf, volume); break; case SP_TRADEINFO_CANCEL: switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "Maybe another time, then."); break; case 2: snprintf(buf, BUFLEN, "Fair enough."); break; } rv = say(lf, buf, volume); break; case SP_TRADEINFO_DECLINE_ALREADYDONE: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "Thank you, but I have enough knowledge for now."); break; case 2: snprintf(buf, BUFLEN, "I'm done with teaching for the moment."); break; case 3: snprintf(buf, BUFLEN, "I'm sick of learning at the moment."); break; } rv = say(lf, buf, volume); break; case SP_TRADEINFO_DECLINE_OTHERBAD: switch (rnd(1,2)) { case 1: snprintf(buf, BUFLEN, "Thank you, but there is nothing I could teach you."); break; case 2: snprintf(buf, BUFLEN, "I fear there is nothing you could learn from me."); break; } rv = say(lf, buf, volume); break; case SP_TRADEINFO_DECLINE_PLAYERBAD: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "You teach ME something? I think not."); break; case 2: snprintf(buf, BUFLEN, "Sorry, I don't believe there is anything you could teach me."); break; case 3: snprintf(buf, BUFLEN, "No thank you, there's nothing I need from you."); break; } rv = say(lf, buf, volume); break; default: break; } return rv; } // returns TRUE if something happened int scare(lifeform_t *lf, lifeform_t *scarer, int howlong, int scarerbonus) { int nfailures = 0,nsuccesses = 0,scareebonus = 0,i; int nchecks,ninjuries; if (!scarer) return B_FALSE; // immune to fear? if (lfhasflag(lf, F_UNDEAD)) return B_FALSE; if (isgod(lf)) return B_FALSE; if (lfhasflag(lf, F_ASLEEP)) return B_FALSE; if (lfhasflag(lf, F_RAGE)) return B_FALSE; if (lfhasflag(lf, F_FEARLESS)) return B_FALSE; // not intelligent enough to be scared? if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= IQ_MINDLESS) { return B_FALSE; } ninjuries = countflagsofid(lf->flags, F_INJURY); scareebonus -= (ninjuries*10); // determine morale check penalty if (isbleeding(lf) || islowhp(lf)) { scareebonus -= 5; } if (lfhasflag(lf, F_HUMANOID)) { object_t *o; for (o = scarer->pack->first ; o; o = o->next) { flag_t *scareflag; scareflag = hasflag(o->flags, F_SCARY); if (scareflag) { flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(o->flags, retflag, &nretflags, F_EQUIPPED, F_NONE); for (i = 0; i < nretflags; i++) { // ie. equipped, but not primary or secondary weapon if (retflag[i] && (retflag[i]->val[0] > BP_SECWEAPON)) { scarerbonus += scareflag->val[0]; } } } } } // if you have morale left, you make 3 checks. chance of not fleeing at all. // if you DONT have morale left, the first check always fails. if (isplayer(lf) || lfhasflag(lf, F_MORALE)) { nfailures = 0; nchecks = 3; } else { nfailures = 1; nchecks = 2; // two checks - first one always fails. } // the person being scared gets a wisdom bonus scareebonus += (getstatmod(lf, A_WIS)); for (i = 0; i < nchecks; i++) { if (!skillcheckvs(lf, SC_MORALE, scareebonus, scarer, SC_MORALE, scarerbonus)) { nfailures++; } } nsuccesses = 3 - nfailures; // modify morale modmorale(lf, nsuccesses - nfailures); if (nfailures == 1) { // cower fleefrom(lf, scarer, rnd(1,3), B_FALSE); return B_TRUE; } else if (nfailures == 2) { // flee for given time fleefrom(lf, scarer, howlong, B_FALSE); return B_TRUE; } else if (nfailures == 3) { object_t *wep; // drop weapon and flee for given time fleefrom(lf, scarer, howlong, B_FALSE); wep = getweapon(lf); if (wep) { drop(wep, ALL); } return B_TRUE; } return B_FALSE; } void setalignment(lifeform_t *lf, enum ALIGNMENT al) { flag_t *f; f = lfhasflag(lf, F_ALIGNMENT); if (f) { f->val[0] = al; } else { addflag(lf->flags, F_ALIGNMENT, al, NA, NA, NULL); } } void setattr(lifeform_t *lf, enum ATTRIB attr, int val) { lf->att[attr] = val; if (lf->att[attr] > lf->baseatt[attr]) { lf->baseatt[attr] = lf->att[attr]; } if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) { statdirty = B_TRUE; } } // if object o is a single object, mark that it came from lf. // // if it is more than one object, then maybe turn it into 'mixed blood' // if there was already blood from a different kind of lf here. // this will also make it unusable for filling flasks (ie. removes // the F_FILLPOT flag) // // returns TRUE if blood is pure, FALSE if we now have mixed blood int setbloodfrom(object_t *o, lifeform_t *lf) { int pure = B_TRUE; if (o->amt == 1) { // ie. not joining a stack addflag(o->flags, F_LINKRACE, lf->race->id, NA, NA, NULL); if (hasflag(lf->flags, F_FILLPOT)) { // override object flags killflagsofid(o->flags, F_FILLPOT); copyflag(o->flags, lf->flags, F_FILLPOT); } } else { // if object isn't already linked to this lf, do so. if (!hasflagval(o->flags, F_LINKRACE, lf->race->id, NA, NA, NULL)) { addflag(o->flags, F_LINKRACE, lf->race->id, NA, NA, NULL); } // if there are now more than a single F_LINKRACE, it means that // this is mixed blood. if (countflagsofid(o->flags, F_LINKRACE) > 1) { // mixed blood - can't use it for potions anymore killflagsofid(o->flags, F_FILLPOT); pure = B_FALSE; } } return pure; } void setbodypartname(race_t *r, enum BODYPART bp, char *name) { int i; for (i = 0; i < r->nbodyparts; i++) { if (r->bodypart[i].id == bp) { free(r->bodypart[i].name); r->bodypart[i].name = strdup(name); break; } } } void setbodytype(race_t *r, enum BODYTYPE bt) { int i; switch (bt) { case BT_BIRD: addbodypart(r, BP_EYES, NULL); addbodypart(r, BP_EARS, NULL); addbodypart(r, BP_HEAD, NULL); addbodypart(r, BP_NECK, NULL); addbodypart(r, BP_BODY, NULL); addbodypart(r, BP_LEGS, NULL); addbodypart(r, BP_FEET, "talons"); addbodypart(r, BP_WINGS, NULL); if (getracesize(r->id) <= SZ_SMALL) { addflagifneeded(r->flags, F_STABILITY, B_TRUE, NA, NA, NULL); } addflagifneeded(r->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); break; case BT_HUMANOID: for (i = BP_WEAPON; i <= BP_LEFTFINGER; i++) { addbodypart(r, i, NULL); } break; case BT_FLYINGINSECT: addbodypart(r, BP_EYES, NULL); addbodypart(r, BP_HEAD, NULL); addbodypart(r, BP_BODY, NULL); addbodypart(r, BP_LEGS, NULL); addbodypart(r, BP_WINGS, NULL); addflag(r->flags, F_SEEWITHOUTEYES, B_TRUE, NA, NA, NULL ); addflagifneeded(r->flags, F_STABILITY, B_TRUE, NA, NA, NULL); addflagifneeded(r->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); break; case BT_QUADRAPED: addbodypart(r, BP_EYES, NULL); addbodypart(r, BP_EARS, NULL); addbodypart(r, BP_HEAD, NULL); addbodypart(r, BP_NECK, NULL); addbodypart(r, BP_BODY, NULL); addbodypart(r, BP_BACKLEGS, "back legs"); addbodypart(r, BP_FRONTLEGS, "front legs"); addbodypart(r, BP_FEET, "paws"); if (getracesize(r->id) <= SZ_SMALL) { addflagifneeded(r->flags, F_STABILITY, B_TRUE, NA, NA, NULL); } break; case BT_FISH: addbodypart(r, BP_EYES, NULL); addbodypart(r, BP_HEAD, NULL); addbodypart(r, BP_BODY, NULL); addbodypart(r, BP_TAIL, NULL); addflagifneeded(r->flags, F_STABILITY, B_TRUE, NA, NA, NULL); addflagifneeded(r->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); break; case BT_SNAKE: addbodypart(r, BP_EYES, NULL); addbodypart(r, BP_HEAD, NULL); addbodypart(r, BP_TAIL, NULL); addflagifneeded(r->flags, F_STABILITY, B_TRUE, NA, NA, NULL); addflagifneeded(r->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); break; case BT_SPIDER: addbodypart(r, BP_EYES, NULL); addbodypart(r, BP_HEAD, "cephalothorax"); addbodypart(r, BP_BODY, "abdomen"); addbodypart(r, BP_LEGS, NULL); addflagifneeded(r->flags, F_STABILITY, B_TRUE, NA, NA, NULL); addflagifneeded(r->flags, F_DISEASEIMMUNE, B_TRUE, NA, NA, NULL); break; } } int setfacing(lifeform_t *lf, int dir) { if (dir < 0) { raise(SIGINT); } if (isclimbing(lf)) { // can't change dir while climbing return B_TRUE; } if (lf->facing == dir) { // already facing that way return B_TRUE; } lf->facing = dir; lf->losdirty = B_TRUE; if (isplayer(lf)) needredraw = B_TRUE; return B_FALSE; } void setfollowdistance(lifeform_t *lf, int min, int max) { flag_t *f; f = lfhasflag(lf, F_FOLLOWRANGE); if (f) { f->val[0] = min; f->val[1] = max; } else { addflag(lf->flags, F_FOLLOWRANGE, min, max, NA, NULL); } } void setguntarget(lifeform_t *lf, lifeform_t *targ) { flag_t *f,*targflag = NULL; char oldtargbuf[BUFLEN]; char targbuf[BUFLEN]; // have an existing target? f = hasflag(lf->flags, F_GUNTARGET); if (f) { strcpy(oldtargbuf, f->text); killflag(f); } else { strcpy(oldtargbuf, ""); } if (targ) { makegunaimstring(lf, targ->id, targbuf); // fill in the text f = addflag(lf->flags, F_GUNTARGET, targ->id, NA, NA, targbuf); } else { targflag = NULL; strcpy(targbuf, "nothing"); } // announce if required if (isplayer(lf)) { if (!streq(targbuf, oldtargbuf)) { warn("Targetted: %s", targbuf ); statdirty = B_TRUE; } } else { if (lfhasflag(lf, F_DEBUG)) { char lfname[BUFLEN]; char targname[BUFLEN]; getlfname(lf, lfname); getlfname(targ, targname); dblog("%s targetted %s.",lfname,targname); } } } void sethomeroom(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags = 0,i; if (gamemode == GM_LOADING) return; getflags(lf->flags, retflag, &nretflags, F_STAYINROOM, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[1] == NA) { if (lf->cell->room) { retflag[i]->val[1] = getroomid(lf->cell); } } } } void setkillverb(lifeform_t *lf, char *buf) { if (lf->killverb) { free(lf->killverb); } lf->killverb = strdup(buf); } void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) { flag_t *f,*nextf; int i,b,retainhp = B_FALSE; int nkilled = 0; race_t *newrace; char buf[BUFLEN]; flag_t *retflag[MAXCANDIDATES]; race_t *origrace = NULL; int nretflags; int reverting = B_FALSE; int lycanthrope = B_FALSE; lifeform_t *l; //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging newrace = findrace(rid); if (isplayer(lf)) { statdirty = B_TRUE; } if (lfhasflag(lf, F_RETAINHPONPOLY)) { retainhp = B_TRUE; } loseconcentration(lf); loseaitargets(lf); // were we already polymorphed? f = lfhasflag(lf, F_ORIGRACE); if (f) { origrace = findrace(f->val[0]); } if (origrace && (newrace == origrace)) { reverting = B_TRUE; } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (frompolymorph && (gamemode == GM_GAMESTARTED) && lf->race) { // remove 'become a ghost' flag killflagsofid(lf->flags, F_RISEASGHOST); // announce if (reverting) { // reverting to original form.... //if (!isdead(lf)) { if (isplayer(lf)) { msg("^wYou revert to your original form!"); } else if (cansee(player, lf)) { getlfname(lf, buf); if (hasname(lf)) { msg("^w%s transforms back to %s original form!", newrace->name, (getgender(lf) == G_FEMALE) ? "her" : "his"); } else { msg("^wThe %s transforms back to its original form!", newrace->name); } } //} // reverting from initial lycanthrope change? if (lfhasflagval(lf, F_POISONED, P_LYCANTHROPY, NA, NA, NULL) && lfhasflag(lf, F_AICONTROLLED) && !lfhasflag(lf, F_LYCANTHROPE)) { // add lycanthropy flag addflag(lf->flags, F_LYCANTHROPE, 4, NA, NA, lf->race->name); killflagsofid(lf->flags, F_AICONTROLLED); killflagsofid(lf->flags, F_RAGE); if (isplayer(lf)) killflagsofid(lf->flags, F_HATESALL); } else { flag_t *lyflag; lyflag = lfhasflag(lf, F_LYCANTHROPE); if (lyflag && (lyflag->val[0] > 0)) { // reduce # of auto changes lyflag->val[0]--; if (lyflag->val[0] <= 0) { char cwtext[BUFLEN]; if (isplayer(lf)) { msg("^%cYou have gained control of your lycanthropy.", getlfcol(lf, CC_GOOD)); } snprintf(cwtext, BUFLEN, "pw:1;race:%s;", lf->race->name); addtempflag(lf->flags, F_CANWILL, OT_S_SHAPESHIFT, NA, NA, cwtext, FROMLYCANTHROPY); } } } if ((lf->race->id == R_GASCLOUD) && (origrace->id == R_VAMPIRE)) { if (!isplayer(lf)) { f = lfhasflagval(lf, F_WANTS, OT_COFFIN, NA, NA, NULL); if (f) killflag(f); } } f = lfhasflagval(lf, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL); if (f) { killflag(f); } killflagsofid(lf->flags, F_ORIGRACE); killflagsofid(lf->flags, F_POLYMORPHED); } else { if (isplayer(lf)) { msg("^wYou transform into %s %s!", isvowel(newrace->name[0]) ? "an" : "a", newrace->name); } else if (cansee(player, lf)) { getlfname(lf, buf); f = lfhasflag(lf, F_GODOF); if (f) { msg("^w%s transforms into %s, the %s of %s!", buf, newrace->name, (getgender(lf) == G_FEMALE) ? "Goddess" : "God", f->text); } else { msg("^w%s transforms into %s %s!", buf, isvowel(newrace->name[0]) ? "an" : "a", newrace->name); } } } } // fix injuries killflagsofid(lf->flags, F_INJURY); // stop stoning. killflagsofid(lf->flags, F_BEINGSTONED); if (lfhasflag(lf, F_LYCANTHROPE)) { lycanthrope = B_TRUE; } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging // first remove flags from existing race, or temporary ones lf->born = B_FALSE; foreach_bucket(b) { for (f = lf->flags->first[b] ; f ; f = nextf) { nextf = f->next; if (frompolymorph && flagretainedduringpoly(f->id)) continue; if (lycanthrope) { switch (f->id) { case F_DTVULN: case F_MATVULN: case F_HITCONFER: case F_HITCONFERVALS: case F_CANEATRAW: case F_CARNIVORE: case F_VEGETARIAN: case F_FILLPOT: continue; break; default: break; } } if (f->lifetime == FROMRACE) { killflag(f); nkilled++; } else if ((f->lifetime > 0) && (f->id != F_POLYMORPHED)) { // kill most temporary flags, with exceptions switch (f->id) { default: killflag(f); nkilled++; break; } } } } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging // set race lf->race = newrace; // set material lf->material = lf->race->material; // inherit most flags from new race foreach_bucket(b) { for (f = lf->race->flags->first[b] ; f ; f = f->next) { int ignorethis = B_FALSE; switch (f->id) { case F_RARITY: case F_MPDICE: break; default: if ((f->condition == FC_IFMONSTER) && isplayer(lf)) { ignorethis = B_TRUE; } else if ((f->condition == FC_IFPLAYER) && !isplayer(lf)) { ignorethis = B_TRUE; } // don't change hostility when polymorphing if (frompolymorph) { if (flagretainedduringpoly(f->id)) ignorethis = B_TRUE; //if (f->id == F_HOSTILE) ignorethis = B_TRUE; } if (!ignorethis) { //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging addflag_real(lf->flags, f->id, f->val[0], f->val[1], f->val[2], f->text, FROMRACE, f->known, -1); //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } break; } } } /* copyflags(lf->flags, lf->race->flags, FROMRACE); // don't want certain race only flags... killflagsofid(lf->flags, F_RARITY); */ // certain other flags are rnadom //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (!frompolymorph) { getflags(lf->flags, retflag, &nretflags, F_RNDHOSTILE, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_RNDHOSTILE) { if (pctchance(f->val[0])) { addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); } killflag(f); } } } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (reverting) { // restore stats - note that your "base" attrib score // is set to your original one here. for (i = 0; i < MAXATTS; i++) { lf->att[i] = lf->origatt[i]; lf->baseatt[i] = lf->origatt[i]; } if (isplayer(lf)) statdirty = B_TRUE; } else { // remember original atts for (i = 0; i < MAXATTS; i++) { lf->origatt[i] = lf->att[i]; } // generate new stats for (i = 0; i < MAXATTS; i++) { rollstat(lf, i); lf->baseatt[i] = lf->att[i]; } } if (!retainhp) { // generate hp/maxhp from hit dice lf->maxhp = 0; if (isplayer(lf)) { for (i = 0; i < lf->level; i++) { int wantmax = B_FALSE; if (i == 0) { wantmax = B_TRUE; } lf->maxhp += rollhitdice(lf, wantmax); if (i == 0) { lf->maxhp += 4; } assert(lf->maxhp > 0); } } else { lf->maxhp += rollhitdice(lf, B_TRUE); // one more hitdie for every level after the first for (i = 1; i < lf->level; i++) { lf->maxhp += rolldie(1, HITDIESIDES); } } lf->hp = lf->maxhp; // don't regenerate mp /* // generate mp, if you have it. f = hasflag(lf->flags, F_MPDICE); if (f) { lf->maxmp = f->val[0] * 4; if (f->val[1] != NA) lf->maxmp += f->val[1]; for (i = 0; i < lf->level-1; i++) { lf->maxmp += rollmpdice(lf); } } else { lf->maxmp = 0; } lf->mp = lf->maxmp; */ } lf->born = B_TRUE; setlosdirty(lf); // check whether: // new race can equip things (F_NOBODYPART xx) // new race can hold objects (F_NOPACK xx) // TODO: new race can use magic (F_NOSPELLS) // new race can still hold all the items which you have if (gamemode == GM_GAMESTARTED) { object_t *o,*nexto; int donemeldmsg = B_FALSE; //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging // no pack? //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; f = isequipped(o); if (f) { int stillok = B_TRUE; if (!hasbp(lf, f->val[0])) { stillok = B_FALSE; } else if (isarmour(o) && !armourfits(lf, o, NULL)) { stillok = B_FALSE; } if (!stillok) { if (reverting) { char obname[BUFLEN]; getobname(o, obname, o->amt); // drop it! if (isplayer(lf)) { msg("Your %s drops to the ground!",noprefix(obname)); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s%s %s drop to the ground!",buf, getpossessive(buf), noprefix(obname)); } moveob(o, lf->cell->obpile, o->amt); } else { char obname[BUFLEN]; getobname(o, obname, o->amt); if (!donemeldmsg) { if (isplayer(lf)) { msg("Your equipment melds into your new form!"); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s%s equipment melds into its new form!",buf, getpossessive(buf)); } donemeldmsg = B_TRUE; } // drop it! moveob(o, lf->polypack, o->amt); } } } } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; if (!canpickup(lf, o, o->amt) && !isequipped(o)) { if (reverting) { char obname[BUFLEN]; getobname(o, obname, o->amt); // drop it! if (isplayer(lf)) { msg("Your %s drops to the ground!",noprefix(obname)); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s%s %s drop to the ground!",buf, getpossessive(buf), noprefix(obname)); } moveob(o, lf->cell->obpile, o->amt); } else { char obname[BUFLEN]; getobname(o, obname, o->amt); if (!donemeldmsg) { if (isplayer(lf)) { msg("Your equipment melds into your new form!"); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s%s equipment melds into its new form!",buf, getpossessive(buf)); } donemeldmsg = B_TRUE; } moveob(o, lf->polypack, o->amt); } } } // drop/meld everything which isn't equipped. // equipped objects are okay because if our new form lacked the required // body part, they would have already been melded above. if (lfhasflag(lf, F_NOPACK)) { if (reverting) { // drop everything which isn't equipped if (countobs(lf->pack, B_FALSE) - countobswithflag(lf->pack, F_EQUIPPED)) { if (isplayer(lf)) { msg("Your possessions drop to the ground!"); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s%s possessions drop to the ground!",buf, getpossessive(buf)); } } for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; if (!isequipped(o)) moveob(o, lf->cell->obpile, o->amt); } } else { // meld if (countobs(lf->pack, B_FALSE) - countobswithflag(lf->pack, F_EQUIPPED)) { if (isplayer(lf)) { msg("Your possessions meld into your new form!"); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s%s possessions meld into its new form!",buf, getpossessive(buf)); } donemeldmsg = B_TRUE; } for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; if (!isequipped(o)) moveob(o, lf->polypack, o->amt); } } } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging if (lfhasflag(lf, F_NATURALFLIGHT) && !isairborne(lf, NULL)) { if (cancast(lf, OT_S_FLIGHT, NULL)) { notime = B_TRUE; castspell(lf, OT_S_FLIGHT, lf, NULL, lf->cell, NULL, NULL); notime = B_FALSE; } } if (isplayer(lf)) { needredraw = B_TRUE; statdirty = B_TRUE; } else if (cansee(player, lf)) { needredraw = B_TRUE; } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } // end if gamestarted if (reverting) { // restore objects while (lf->polypack->first) { moveob(lf->polypack->first, lf->pack, lf->polypack->first->amt); } } // losdirty for anyone who sees it (including us again, // in case something which affects our vision came out of // or went in to ->polypack ) for (l = lf->cell->map->lf ; l ; l = l->next) { if (cansee(l, lf)) setlosdirty(l); } //if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging } void setlastdam(lifeform_t *lf, char *buf) { if (lf->lastdam) { free(lf->lastdam); } lf->lastdam = strdup(buf); setkillverb(lf, "Killed"); // default } void interrupt(lifeform_t *lf) { stopeating(lf); stopresting(lf); stoprunning(lf); stoppathfinding(lf); killflagsofid(lf->flags, F_AUTOCMD); killflagsofid(lf->flags, F_DIGGING); } int setlfmaterial(lifeform_t *lf, enum MATERIAL id, int wantannounce) { if (getlfmaterial(lf) == id) { return B_TRUE; } if (hasactivespell(lf, OT_S_BARKSKIN) && (id != MT_WOOD)) { stopspell(lf, OT_S_BARKSKIN); } lf->material = findmaterial(id); // announce if (wantannounce && (gamemode == GM_GAMESTARTED)) { if (isplayer(lf)) { msg("^wYour body %s to %s%c", (id == lf->race->material->id) ? "reverts" : "turns", lf->material->name, (id == lf->race->material->id) ? '.' : '!' ); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s%s body %s to %s%c", lfname, getpossessive(lfname), (id == lf->race->material->id) ? "reverts" : "turns", lf->material->name, (id == lf->race->material->id) ? '.' : '!' ); } } return B_FALSE; } void setlosdirty(lifeform_t *lf) { // already dirty? if (lf->losdirty) return; lf->losdirty = B_TRUE; if (lf->losdirty && isplayer(lf)) { if (!lf->changinglev) { needredraw = B_TRUE; drawscreen(); } } } void setstamina(lifeform_t *lf, float howmuch) { float orig; orig = getstamina(lf); lf->stamina = howmuch; limitf(&(lf->stamina), 0, getmaxstamina(lf)); if (getstamina(lf) != orig) { if (isplayer(lf)) { statdirty = B_TRUE; drawstatus(); updatestatus(); if (getstamina(lf) == 0) { msg("^BYou are exhausted."); } else if (orig == 0) { msg("You feel less exhausted now."); } } /*else if (cansee(player, lf)) { if (getstamina(lf) == 0) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s looks exhausted.", lfname); } }*/ } if (getstamina(lf) == 0) { stopsprinting(lf); } } int canshoot(lifeform_t *lf, enum ERROR *why) { object_t *gun,*ammo; lifeform_t *targ; reason = E_OK; if (lfhasflag(lf, F_RAGE)) { if (why) *why = E_RAGE; return B_FALSE; } if (lfhasflag(lf, F_STUNNED)) { if (why) *why = E_STUNNED; return B_FALSE; } gun = getfirearm(lf); if (!gun) { if (why) *why = E_NOTEQUIPPED; return B_FALSE; } // get target targ = getguntarget(lf); if (!targ) { if (why) *why = E_NOTARGET; return B_FALSE; } // get ammo ammo = getammo(gun); if (!ammo) { if (why) *why = E_NOAMMO; return B_FALSE; } return B_TRUE; } void shiver(lifeform_t *lf) { object_t *wep; if (isplayer(lf)) { msg("^bYou shiver uncontrollably."); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s shivers.", getlfcol(lf, CC_BAD), lfname); } wep = getweapon(lf); if (wep) { char wname[BUFLEN]; getobname(wep, wname, 1); drop(wep, wep->amt); } loseconcentration(lf); } int shoot(lifeform_t *lf) { object_t *gun,*ammo; lifeform_t *targ; int firespeed; if (!canshoot(lf, NULL)) { return B_TRUE; } // get details... we know this will work because // canshoot returned true. gun = getfirearm(lf); targ = getguntarget(lf); ammo = getammo(gun); // get fire speed firespeed = getfirearmspeed(gun); taketime(lf, getactspeed(lf)); fireat(lf, ammo, 1, targ->cell, firespeed, gun); if (!getammo(gun)) { if (!isplayer(lf) || getoption(OPT_AUTORELOAD)) { // automatically try to reload our empty firearm/ranged weapon. if (loadfirearmfast(lf, B_FALSE)) { if (isplayer(lf)) { char obname[BUFLEN]; getobname(gun, obname, 1); msg("^bYour %s is now out of ammo!", noprefix(obname)); } } } } return B_FALSE; } /* int getskillcheckchance(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod) { int attrib; int levmod; int othermod = 0; int db = B_FALSE; int luckmod = 0; char mbuf[BUFLEN]; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; flag_t *f; int pct; if (lfhasflag(lf, F_DEBUG)) { if (ct != SC_STEALTH) { // dont show debug info for stealth checks db = B_TRUE; } } switch (ct) { case SC_STR: attrib = getattr(lf, A_STR)/5; break; case SC_CON: attrib = getattr(lf, A_CON)/5; break; case SC_DEX: attrib = getattr(lf, A_AGI)/5; break; case SC_IQ: attrib = getattr(lf, A_IQ)/5; break; case SC_CHA: // if you're bleeding you're less attractive! attrib = (pctof(gethppct(lf), getattr(lf, A_CHA)) / 5); if (lfhasflagval(lf, F_INJURY, IJ_BLACKEYE, NA, NA, NULL)) { attrib -= 3; } break; case SC_WIS: attrib = getattr(lf, A_WIS)/5; break; /////////////// case SC_OPENLOCKS: attrib = (getattr(lf, A_AGI)/10); break; case SC_WILL: attrib = getattr(lf, A_WIS)/5; break; case SC_TUMBLE: attrib = getskill(lf, SK_ATHLETICS); break; case SC_LEARNMAGIC: attrib = (getattr(lf, A_IQ) / 10) + lf->level; break; case SC_MORALE: // based on morale, level/hitdice and size. attrib = getmorale(lf); attrib += getlfsize(lf); break; case SC_SLIP: case SC_FALL: attrib = getattr(lf, A_AGI)/5; break; case SC_SHIELDBLOCK: attrib = (getattr(lf, A_AGI) / 20); break; case SC_POISON: attrib = getattr(lf, A_CON)/5; break; case SC_DISARM: attrib = (getskill(lf, SK_ENGINEERING)*3); if (attrib) { attrib += (getattr(lf, A_AGI)/20); } break; case SC_CLIMB: attrib = (getskill(lf, SK_CLIMBING)*2); break; case SC_DODGE: // getevasion returns 0-100 (ie. pct chance) // convert this to 0-20 attrib = getevasion(lf) / 5; break; case SC_LISTEN: // if you are asleep, listen check doesn't help. if (lfhasflag(lf, F_ASLEEP) || isdeaf(lf)) { attrib = 0; } else { attrib = getskill(lf, SK_LISTEN); } break; case SC_RESISTMAG: attrib = (getattr(lf, A_CON)/30) + (getattr(lf, A_WIS)/30) + (getmr(lf)/5); break; case SC_SEARCH: attrib = (getskill(lf, SK_PERCEPTION)*2); break; case SC_SPEECH: attrib = (getattr(lf, A_CHA)/10) + (getattr(lf, A_IQ)/10); break; case SC_STEAL: attrib = (getskill(lf, SK_THIEVERY)); break; case SC_STEALTH: attrib = (getskill(lf, SK_STEALTH)*3); break; default: // invalid checktype dblog("warning: invalid checktype %d", (int)ct); msg("warning: invalid checktype %d", (int)ct); attrib = 0; break; } // level modifier levmod = (gettr(lf) / 2); // other modifiers if (ct == SC_CLIMB) { object_t *o; for (o = lf->pack->first ; o ; o = o->next) { f = hasflag(o->flags, F_HELPSCLIMB); if (f && isknown(o)) { othermod += f->val[0]; } } // autopass if we have spiderclimb skill. if (lfhasflag(lf, F_SPIDERCLIMB)) { othermod = diff; } } else if (ct == SC_DODGE) { if (attrib) { // ie. -25 to 25 othermod += (getstatmod(lf, A_AGI) / 2); } } else if (ct == SC_SLIP) { if (lfhasflagval(lf, F_INJURY, IJ_LEGBROKEN, NA, NA, NULL)) { othermod -= 25; } else { if (getequippedob(lf->pack, BP_FEET)) { othermod += 5; } if (lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET)) { othermod += 25; } } } else if (ct == SC_FALL) { if (lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET)) { othermod += 10; } } else if (ct == SC_LISTEN) { if (hasequippedobid(lf->pack, OT_AMU_LISTEN)) { othermod += 20; } // nocturnal monsters asleep at night, or diurnal during the day if (isasleep(lf) && issleepingtimefor(lf)) { othermod -= 10; } } else if (ct == SC_MORALE) { if (isbleeding(lf) || islowhp(lf)) { othermod -= 5; } } else if (ct == SC_OPENLOCKS) { enum SKILLLEVEL slev; slev = getskill(lf, SK_LOCKPICKING); if (slev == PR_INEPT) { othermod -= 10; } else { othermod += (5 * slev); } if (hasequippedobid(lf->pack, OT_AMU_THIEF)) { othermod += 25; } } else if (ct == SC_POISON) { // auto pass if we are immune if (isimmuneto(lf->flags, DT_POISON, B_FALSE)) { othermod += (diff*2); } else if (isresistantto(lf->flags, DT_POISON, B_FALSE)) { othermod += 5; } else if (isvulnto(lf->flags, DT_POISON, B_FALSE)) { othermod -= 10; } } else if (ct == SC_SEARCH) { int bonus = 0; sumflags(lf->flags, F_ENHANCESEARCH, &bonus, NULL, NULL); othermod += bonus; } else if (ct == SC_SPEECH) { othermod += (getskill(lf, SK_SPEECH)*2); } else if (ct == SC_STEAL) { if (attrib > 0) { // ie. -25 to 25 othermod += (getstatmod(lf, A_AGI) / 2); } } else if (ct == SC_STEALTH) { //if (attrib > 0) { // if (lfhasflag(lf, F_CAREFULMOVE)) { // othermod += 3; // } // } if (!islit(lf->cell)) { othermod += 5; } } else if (ct == SC_WILL) { // level counts for more levmod *= 2; } else if (ct == SC_TUMBLE) { // ie. -25 to 25 othermod += (getstatmod(lf, A_AGI) / 2); } getflags(lf->flags, retflag, &nretflags, F_SKILLCHECKMOD, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[0] == ct) mod += retflag[i]->val[1]; } // luck sumflags(lf->flags, F_EXTRALUCK, &luckmod, NULL, NULL); othermod += luckmod; pct = 100 - (diff*5); // ie. diff 20 = 100%, 10 = 50% pct += (attrib*5); pct += (mod*5); pct += (levmod*5); pct += (othermod*5); if (db) { snprintf(mbuf, BUFLEN, "%s skcheck (%d): diff=%d, %d(attr)+%d(lvm)+%d(othmod)+%d(mod),pct=%d%%", lf->race->name, ct, diff, attrib,levmod, othermod,mod,pct); msg(mbuf); more(); } limit(&pct, 0, 100); return pct; } */ int modskillcheckroll(lifeform_t *lf, enum CHECKTYPE ct, int *roll) { int attrib; int levmod; int othermod = 0; int db = B_FALSE; int luckmod = 0; char mbuf[BUFLEN]; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; flag_t *f; int origroll; origroll = *roll; if (lfhasflag(lf, F_DEBUG)) { if (ct != SC_STEALTH) { // dont show debug info for stealth checks db = B_TRUE; } } switch (ct) { case SC_STR: attrib = getattr(lf, A_STR); break; case SC_CON: attrib = getattr(lf, A_CON); break; case SC_DEX: attrib = getattr(lf, A_AGI); break; case SC_IQ: attrib = getattr(lf, A_IQ); break; case SC_CHA: // if you're bleeding you're less attractive! attrib = (pctof(gethppct(lf), getattr(lf, A_CHA))); if (lfhasflagval(lf, F_INJURY, IJ_BLACKEYE, NA, NA, NULL)) { attrib -= 15; } break; case SC_WIS: attrib = getattr(lf, A_WIS); break; /////////////// case SC_OPENLOCKS: attrib = (getattr(lf, A_AGI)/2); break; case SC_WILL: attrib = getattr(lf, A_WIS); break; case SC_TUMBLE: attrib = (getskill(lf, SK_ATHLETICS)*20); break; case SC_LEARNMAGIC: attrib = (getattr(lf, A_IQ) / 2) + lf->level; break; case SC_MORALE: // based on morale, level/hitdice and size. attrib = (getmorale(lf)*3); attrib += (getlfsize(lf)*2); break; case SC_SLIP: case SC_FALL: attrib = getattr(lf, A_AGI); break; case SC_SHIELDBLOCK: attrib = (getattr(lf, A_AGI) / 3); break; case SC_POISON: attrib = getattr(lf, A_CON); break; case SC_DISARM: attrib = (getskill(lf, SK_ENGINEERING)*15); if (attrib) { attrib += (getattr(lf, A_AGI)/3); } break; case SC_CLIMB: attrib = (getskill(lf, SK_CLIMBING)*10); break; case SC_DODGE: attrib = getevasion(lf); break; case SC_LISTEN: // if you are asleep, listen check doesn't help. if (lfhasflag(lf, F_ASLEEP) || isdeaf(lf)) { attrib = 0; } else { attrib = getskill(lf, SK_LISTEN)*5; } break; case SC_RESISTMAG: attrib = (getattr(lf, A_CON)/5) + (getattr(lf, A_WIS)/5) + (getmr(lf)); break; case SC_SEARCH: attrib = (getskill(lf, SK_PERCEPTION)*10); break; case SC_SPEECH: attrib = (getattr(lf, A_CHA)/2) + (getattr(lf, A_IQ)/2); break; case SC_STEAL: attrib = (getskill(lf, SK_THIEVERY)*5); break; case SC_STEALTH: attrib = (getskill(lf, SK_STEALTH)*10); break; default: // invalid checktype dblog("warning: invalid checktype %d", (int)ct); msg("warning: invalid checktype %d", (int)ct); attrib = 0; break; } // level modifier levmod = gettr(lf); // other modifiers if (ct == SC_CLIMB) { object_t *o; for (o = lf->pack->first ; o ; o = o->next) { f = hasflag(o->flags, F_HELPSCLIMB); if (f && isknown(o)) { othermod += f->val[0]; } } // autopass if we have spiderclimb skill. if (lfhasflag(lf, F_SPIDERCLIMB)) { othermod = 300; } } else if (ct == SC_DODGE) { if (attrib) { // ie. -2 to 2 othermod += (getstatmod(lf, A_AGI) / 2); } } else if (ct == SC_SLIP) { if (lfhasflagval(lf, F_INJURY, IJ_LEGBROKEN, NA, NA, NULL)) { othermod -= 25; } else { if (getequippedob(lf->pack, BP_FEET)) { othermod += 20; } if (lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET)) { othermod += 50; } } if (lfhasflag(lf, F_SPRINTING)) { othermod -= 40; } } else if (ct == SC_FALL) { if (lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET)) { othermod += 30; } } else if (ct == SC_LISTEN) { if (hasequippedobid(lf->pack, OT_AMU_LISTEN)) { othermod += 50; } // nocturnal monsters asleep at night, or diurnal during the day // count as being in a deep sleep if (isasleep(lf) && issleepingtimefor(lf)) { othermod -= 20; } } else if (ct == SC_MORALE) { if (isbleeding(lf) || islowhp(lf)) { othermod -= 25; } if (hasjob(lf, J_MONK)) { othermod += 50; } } else if (ct == SC_OPENLOCKS) { enum SKILLLEVEL slev; slev = getskill(lf, SK_LOCKPICKING); if (slev == PR_INEPT) { othermod -= 50; } else { othermod += (5 * slev); } if (hasequippedobid(lf->pack, OT_AMU_THIEF)) { othermod += 50; } } else if (ct == SC_POISON) { // auto pass if we are immune if (isimmuneto(lf->flags, DT_POISON, B_FALSE)) { othermod += 300; } else if (isresistantto(lf->flags, DT_POISON, B_FALSE)) { othermod += 25; } else if (isvulnto(lf->flags, DT_POISON, B_FALSE)) { othermod -= 50; } } else if (ct == SC_SEARCH) { int bonus = 0; sumflags(lf->flags, F_ENHANCESEARCH, &bonus, NULL, NULL); othermod += bonus; // hard to search while fighting! if (isinbattle(lf, B_NODISTANT, B_FALSE)) { othermod -= 50; } } else if (ct == SC_SPEECH) { othermod += (getskill(lf, SK_SPEECH)*10); } else if (ct == SC_STEAL) { if (attrib > 0) { // ie. -25 to 25 othermod += (getstatmod(lf, A_AGI) / 2); } } else if (ct == SC_STEALTH) { /* if (attrib > 0) { if (lfhasflag(lf, F_CAREFULMOVE)) { othermod += 3; } } */ /* if (!islit(lf->cell)) { othermod += 5; } */ if (!islit(lf->cell)) { othermod += 50; } else { switch (lf->cell->map->illumination) { case IL_FULLLIT: break; case IL_WELLLIT: othermod += 10; break; case IL_DIM: othermod += 20; break; case IL_SHADOWY: othermod += 30; break; case IL_FULLDARK: othermod += 40; break; } } } else if (ct == SC_WILL) { // level counts for more levmod *= 2; } else if (ct == SC_TUMBLE) { // ie. -25 to 25 othermod += (getstatmod(lf, A_AGI) / 2); } getflags(lf->flags, retflag, &nretflags, F_SKILLCHECKMOD, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->val[0] == ct) othermod += retflag[i]->val[1]; } // luck sumflags(lf->flags, F_EXTRALUCK, &luckmod, NULL, NULL); othermod += luckmod; *roll += (attrib); *roll += (levmod); *roll += (othermod); // auto fail... if (isimmobile(lf) || lfhasflag(lf, F_DOESNTMOVE) || !getstamina(lf)) { switch (ct) { case SC_CLIMB: case SC_DODGE: case SC_SHIELDBLOCK: *roll = 0; break; default: break; } } if (lfhasflag(lf, F_RAGE) && (ct == SC_STEALTH)) { *roll = 0; } // auto pass... if (lfhasflag(lf, F_RAGE) && (ct == SC_MORALE)) { *roll = 300; } if (hasequippedobid(lf->pack, OT_AMU_ACROBAT) && (ct == SC_TUMBLE)) { *roll = 300; } // smell chance to autopass/autofail some checks if (pctchance(5)) { switch (ct) { case SC_DODGE: case SC_SEARCH: // so that you never get stuck with secret doors case SC_STEALTH: if (db) { msg("%s skillcheck autopassed with 'natural 20'.", lf->race->name); } *roll = 300; break; case SC_POISON: if (db) { msg("%s skillcheck autofailed with 'natural 0'.", lf->race->name); } *roll = 0; break; default: break; } } if (db) { snprintf(mbuf, BUFLEN, "%s checkmod (type %s): %d(roll)+%d(attr)+%d(lvm)+%d(othmod),totroll=%d", lf->race->name, getskillcheckname(ct), origroll, attrib,levmod, othermod,*roll); msg(mbuf); more(); } //limit(&pct, 0, 100); //return pct; return *roll; } int skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod) { return real_skillcheck(lf, ct, diff, mod, NULL); } // positive mod makes it easier, negative makes it harder // "result" will be filled with difference between our pct chance and what you rolled. // positive "result" means you rolled better than what you need. // negative "result" means you rolled worse. int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *result) { int roll,db = B_FALSE; char dbtag[BUFLEN]; if (ct == SC_NONE) return B_FALSE; if (lfhasflag(lf, F_DEBUG)) { if (ct != SC_STEALTH) { // dont show debug info for stealth checks db = B_TRUE; } } snprintf(dbtag, BUFLEN, "[lfid %d (%s) %s check] ",lf->id, lf->race->name, getskillcheckname(ct)); ////////////////////////////////////// // debugging for new skillcheck code if (lfhasflag(lf, F_DEBUG)) dblog("%s difficulty %d, mod %d",dbtag, diff, mod); ////////////////////////////////////// if (diff == D_ALWAYSFAIL) { // fail. if (result) *result = -1; if (lfhasflag(lf, F_DEBUG)) dblog("%s autofail as difficulty is D_ALWAYSFAIL.",dbtag); return B_FALSE; } //pct = getskillcheckchance(lf, ct, diff, mod); // higher roll is better //roll = rnd(1,100); roll = rnd(1,50); // debugging for new skillcheck code if (lfhasflag(lf, F_DEBUG)) dblog("%s initial roll=%d",dbtag,roll); ////////////////////////////////////// roll += mod; if (lfhasflag(lf, F_DEBUG)) dblog("%s +mod(%d) = %d",dbtag,mod,roll); modskillcheckroll(lf, ct, &roll); if (lfhasflag(lf, F_DEBUG)) dblog("%s +other_factors = %d",dbtag,roll); if (db) { msg("%s: %s check, rolled %d, need >= %d. (%s)",lf->race->name, getskillcheckname(ct), roll,diff, (roll >= diff) ? "PASS" : "fail"); } if (lfhasflag(lf, F_DEBUG)) dblog("%s === rolled %d, need >= %d. (%s)",dbtag,roll,diff, (roll >= diff) ? "PASS" : "fail"); if (result) { *result = roll; } if (roll >= diff) { // passed! // some checks will train skills when passed. switch (ct) { case SC_DODGE: practice(lf, SK_EVASION, 1); break; default: break; } return B_TRUE; } return B_FALSE; } int restoredrainedstats(lifeform_t *lf) { enum ATTRIB a; int donesomething = B_FALSE; for (a = 0; a < MAXATTS; a++) { if ((lf->baseatt[a] > 0) && (getattr(lf, a) <= 0)) { setattr(lf, a, lf->baseatt[a]); donesomething = B_TRUE; } } return donesomething; } // returns TRUE if lf1 succeeded, FALSE if lf1 failed. int skillcheckvs(lifeform_t *lf1, enum CHECKTYPE ct1, int mod1, lifeform_t *lf2, enum CHECKTYPE ct2, int mod2) { int roll1,roll2; int db = B_FALSE; if (lfhasflag(lf1, F_DEBUG)) { db = B_TRUE; } // bonus for knowledge about the other lf's race if (getlorelevel(lf1, lf2->race->raceclass->id) >= PR_SKILLED) { mod1 += 5; } if (getlorelevel(lf2, lf1->race->raceclass->id) >= PR_SKILLED) { mod2 += 5; } // ignore the difficulty, we just want the modified roll. real_skillcheck(lf1, ct1, 0, mod1, &roll1); real_skillcheck(lf2, ct2, 0, mod2, &roll2); if (db) { char lfname1[BUFLEN]; char lfname2[BUFLEN]; getlfname(lf1, lfname1); getlfname(lf2, lfname2); msg("%s:%d %s %s:%d, %s.", lfname1,roll1, (roll1 > roll2) ? ">" : "<=", lfname2,roll2, (roll1 > roll2) ? "pass" : "fail" ); } if (roll1 > roll2) { return B_TRUE; } return B_FALSE; } int slipon(lifeform_t *lf, object_t *o) { char obname[BUFLEN]; char lfname[BUFLEN]; if (lfhasflag(lf, F_NONCORPOREAL) || isairborne(lf, NULL)) { return B_TRUE; } getlfname(lf,lfname); if (o) getobname(o, obname, 1); // slip! if (isplayer(lf) || cansee(player, lf)) { char onwhat[BUFLEN]; char damstring[BUFLEN]; if (o) { snprintf(onwhat, BUFLEN, "%s", obname); } else { snprintf(onwhat, BUFLEN, "the %s", lf->cell->type->name); } msg("%s slip%s on %s and fall%s to the ground.",lfname, isplayer(lf) ? "" : "s", onwhat, isplayer(lf) ? "" : "s"); snprintf(damstring, BUFLEN, "slipping on %s",onwhat); losehp(lf, 1, DT_FALL, NULL, damstring); } noise(lf->cell, lf, NC_OTHER, SV_TALK, "a thump.", NULL); fall(lf, NULL, B_FALSE); if (isplayer(lf)) { real_warnabout("(use 'm-tiptoe' to walk carefully)", PERMENANT, B_FALSE); } else { // most monsters who slipped will tiptoe next turn, if it is still slippery here. if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) >= IQ_ANIMAL) { if (getslipperyness(lf->cell, NULL)) { addflag(lf->flags, F_SLIPPEDLASTTURN, B_TRUE, NA, NA, NULL); } } } if (o) { // object gets damaged? if (hasflag(o->flags, F_DAMAGABLE)) { int dam; dam = getlfsize(lf); limit(&dam, 1, NA); takedamage(o, dam, DT_DIRECT, NULL); } // object moves? if (hasflag(o->flags, F_SLIPMOVE)) { cell_t *cur, *new; cur = getoblocation(o); new = getrandomadjcell(cur, &ccwalkable, B_NOEXPAND); if (new) { if (haslos(player, cur) || haslos(player, new)) { msg("%s slips across the floor.", obname); } moveob(o, new->obpile, 1); } } } loseconcentration(lf); return B_FALSE; } void sortlf(map_t *map, lifeform_t *lf) { if ((lf->next) && lf->timespent >= lf->next->timespent) { lifeform_t *temp; // remember next element temp = lf->next; // remove this element from list // don't bother checking if (l->next == NULL) as we know // this won't be true due to the for loop condition if (lf->prev == NULL) { // first map->lf = lf->next; lf->next->prev = NULL; } else { // not first lf->prev->next = lf->next; lf->next->prev = lf->prev; } // TESTING: re-add at correct position. while (temp->next && (temp->next->timespent <= lf->timespent)) { //dblog("moving past %d %s (timespend=%d)...",temp->next->id, temp->next->race->name, temp->next->timespent); temp = temp->next; } // re-add element afterwards lf->next = temp->next; lf->prev = temp; temp->next = lf; if (lf->next == NULL) { map->lastlf = lf; } else { lf->next->prev = lf; } } else if ((lf->prev) && (lf->timespent < lf->prev->timespent)) { lifeform_t *temp; // remember previous element temp = lf->prev; // remove this element from list // don't bother checking if (l->prev == NULL) as we know // this won't be true due to the for loop condition if (lf->next == NULL) { // last map->lastlf = lf->prev; lf->prev->next = NULL; } else { // not last lf->prev->next = lf->next; lf->next->prev = lf->prev; } // re-add at correct position. while (temp->prev && (temp->prev->timespent > lf->timespent)) { //dblog("moving past %d %s (timespend=%d)...",temp->next->id, temp->next->race->name, temp->next->timespent); temp = temp->prev; } // re-add element before lf->next = temp; lf->prev = temp->prev; temp->prev = lf; if (lf->prev == NULL) { map->lf = lf; } else { lf->prev->next = lf; } } } // return TRUE on failure int statdrain(lifeform_t *lf, enum ATTRIB attr, int amt, enum CHECKTYPE sctype, int scdiff, lifeform_t *fromlf) { int resisted = B_FALSE; if (isimmuneto(lf->flags, DT_NECROTIC, B_FALSE)) resisted = B_TRUE; if (!resisted && isresistantto(lf->flags, DT_NECROTIC, B_FALSE) && onein(2)) resisted = B_TRUE; // skill check if (!resisted && skillcheck(lf, sctype, scdiff, 0 )) { resisted = B_TRUE; } if (resisted) { if (isplayer(lf)) { msg("You struggle to retain your %s!", getattrname(attr)); } return B_TRUE; } // announce if (fromlf) { char lfname[BUFLEN]; char buf[BUFLEN]; snprintf(buf, BUFLEN, "%s-drained",getattrname(attr)); setkillverb(lf, buf); real_getlfnamea(fromlf, lfname, NULL, B_TRUE, B_TRUE); setlastdam(lf, lfname); } else { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%s drain",getattrname(attr)); setkillverb(lf, "Killed"); setlastdam(lf, buf); } if (fromlf) { lf->lastdamlf = fromlf->id; } modattr(lf, attr, -amt); return B_FALSE; } ////////////////////////////////// // effects which happen before every TURN // (ie. the faster the player is, the faster they happen) // eg. damage from walking on things ////////////////////////////////// void startlfturn(lifeform_t *lf) { int db = B_FALSE; map_t *map; object_t *o,*nexto; flag_t *f; flag_t *asp, *sf,*stasis = NULL; char buf[BUFLEN]; lifeform_t *l; int i; int willvanish = B_FALSE; flag_t *retflag[MAXCANDIDATES]; int nretflags; int movedlastturn = B_FALSE; object_t *bloodamu = NULL; enum ATTRIB a; enum TIMEPHASE tp; enum FLAG inair = F_NONE; enum TEMPERATURE temp; int meltchance = 25,meltdam = 1; tp = gettimephase(); map = lf->cell->map; if (db) dblog("startlfturn for lf id %d %s", lf->id, lf->race->name); // if (lf->hp < 0) lf->hp = 0; // debugging lf->redraws = 0; stasis = lfhasflag(lf, F_STASIS); // more debug if (isplayer(lf)) { if ((gamemode == GM_GAMESTARTED) && (getalignment(lf) != playerorigalignment)) { //dblog("WARNING: player alignment has changed!!!"); //msg("WARNING: player alignment has changed!!!"); //more(); } } // MOVE OUT OF STOMACHS WHICH DONT EXIST ANYMORE sf = hasflag(map->flags, F_STOMACHOF); if (sf) { lifeform_t *stomachlf; stomachlf = findlf(NULL, sf->val[0]); if (!stomachlf || isdead(stomachlf)) { object_t *exit; cell_t *exitcell,*c; map_t *exitmap; exitcell = findobinmap(map, OT_STOMACHEXIT); exit = hasob(exitcell->obpile, OT_STOMACHEXIT); f = hasflag(exit->flags, F_MAPLINK); exitmap = findmap(f->val[0]); c = getcellat(exitmap, f->val[1], f->val[2]); while (!cellwalkable(lf, c, NULL)) { c = getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND); } // move lf out! movelf(lf, c, B_FALSE); if (isplayer(lf)) { msg("You get expelled from %s!", sf->text); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s gets expelled from %s!", lfname, sf->text); } } } if (isasleep(lf) && ((f = lfhasflag(lf, F_WOKENBY)) != NULL)) { lifeform_t *thief; thief = findlf(lf->cell->map, f->val[0]); if (isplayer(lf)) { msg("%s", f->text); } if (thief) { turntoface(lf, thief->cell); } killflagsofid(lf->flags, F_ASLEEP); killflagsofid(lf->flags, F_WOKENBY); } if (!isdead(lf) && !isunconscious(lf) && !isasleep(lf) && hasflag(lf->flags, F_WASROBBED)) { if (isplayer(lf)) { if (countobs(lf->pack, B_FALSE)) { msg("^wSome of your items are missing!^n"); more(); } else { msg("^wAll of your items are missing!^n"); more(); } } else if (cantalk(lf)) { sayphrase(lf, SP_ROBBED, SV_SHOUT, NA, NULL, NULL); // suspect anyone in sight until we calm down! if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) < AT_AVERAGE) { int howlong; howlong = (100 - getattr(lf, A_WIS)) / 2; limit(&howlong, 10, 50); addtempflag(lf->flags, F_HATESALL, B_TRUE, NA, NA, NULL, howlong); } } killflagsofid(lf->flags, F_WASROBBED); } if (isplayer(lf) && lfhasflag(lf, F_DRUNK)) statdirty = B_TRUE; // clear one-turn-only flags lf->rotated = B_FALSE; //killflagsofid(lf->flags, F_JUSTENTERED); killflagsofid(lf->flags, F_UNSEENATTACKER); killflagsofid(lf->flags, F_DONELISTEN); killflagsofid(lf->flags, F_DONEBURNMSG); killflagsofid(lf->flags, F_NOSWAP); killflagsofid(lf->flags, F_LASTATTACKHIT); movedlastturn = 0; movedlastturn += killflagsofid(lf->flags, F_HASBEENMOVED); movedlastturn += killflagsofid(lf->flags, F_MOVED); // if we didn't turn lsat move, kill our turncounter if (!killflagsofid(lf->flags, F_TURNED)) { lf->turncounter = 0; } // update where player knows // (but without a map you will then slowly forget it) if (isplayer(lf)) { // update line of sight if required if (lf->losdirty) precalclos(lf); updateknowncells(); } else { // ai start of turn code killflagsofid(lf->flags, F_IGNORECELL); // track how long we have gone without fighitng. // this is used to determine when it is safe to // rest. if (isinbattle(lf, B_INCLUDEDISTANT, B_FALSE)) { killflagsofid(lf->flags, F_TURNSINPEACE); } else { f = lfhasflag(lf, F_TURNSINPEACE); if (f) { f->val[0]++; } else { addflag(lf->flags, F_TURNSINPEACE, 1, NA, NA, NULL); } } // cope with hecta's "sacrifices" escaping from the player f = lfhasflag(lf, F_HECTAESCAPEE); if (f) { if (cansee(player, lf)) { killflag(f); } else { f->val[0]--; if (f->val[0] <= 0) { killflag(f); angergodmaybe(R_GODDEATH, f->val[1], GA_MERCY); } } } // if player can se } // stuck inside solid cells? if (isstuckinwall(lf)) { if (isplayer(lf)) { msg("^%cYou reintegrate inside a solid object!",getlfcol(lf, CC_VBAD)); } losehp(lf, 9999, DT_DIRECT, NULL, "re-integration inside a solid object"); } if (!stasis) { // drown? o = hasobwithflag(lf->cell->obpile, F_DEEPWATER); if (o) { if (checkfordrowning(lf, o)) { if (isdead(lf)) return; } // fixes stench killtransitoryflags(lf->flags, F_STENCH); } else { // amulet of swimming? if (hasequippedobid(lf->pack, OT_AMU_SWIMMING) && (lf->race->id == R_SWAN) && ispolymorphed(lf)) { // revert to normal form abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL); } } // suffocate? if (needstobreath(lf)) { if (lfhasflag(lf, F_NEEDSWATER) && !hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { int dam; if (isplayer(lf)) { msg("^BYou are suffocating without water to breath!"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s is suffocating!", getlfcol(lf, CC_VBAD), lfname); } dam = lf->maxhp / 3; limit(&dam, 1, NA); losehp(lf, dam, DT_DIRECT, NULL, "suffocation"); if (isdead(lf)) return; } } } // vampire in sunlight? if ((lf->race->id == R_VAMPIRE) && isoutdoors(lf->cell->map) && !isnighttime()) { if (isplayer(lf)) { msg("^bThe sunlight burns you!"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%cThe sunlight burns %s!", getlfcol(lf, CC_BAD), lfname); } losehp(lf, roll("6d6"), DT_DIRECT, NULL, "sunlight"); if (isdead(lf)) return; } // float up into space? if (lf->cell->map->region->rtype->id == BH_WORLDMAP) { if (lfhasflag(lf, F_LEVITATING)) { if (isplayer(lf)) { msg("You float up into the sky!"); msg("You float up through the atmosphere."); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s floats up into the sky!", lfname); } if (isplayer(lf)) { lf->hp = 0; lf->lastdamtype = DT_DIRECT; setlastdam(lf, "high altitude suffocation"); } else { killlf(lf); } } } // gods get angry if (isplayer(lf)) { f = lfhasflag(lf, F_GAVEMONEY); if (f) { int angeramt; angeramt = f->val[0]; limit(&angeramt, 25, NA); angergodmaybe(R_GODTHIEVES, angeramt, GA_MONEY); killflag(f); } } // oooooo replace with f_bleeding ? if (!stasis && lfhasflagval(lf, F_INJURY, IJ_ARTERYPIERCE, NA, NA, NULL)) { if (!bleedfrom(lf, BP_HANDS, B_SPLATTER)) { if (isplayer(lf)) msg("^BYou bleed!"); losehp(lf, rnd(2,8), DT_DIRECT, NULL, "blood loss"); } } // get more hungry if (!stasis) { modhunger(lf, 1); // regeneration sumflags(lf->flags, F_REGENERATES, &i, NULL, NULL); if ((tp == TP_TWILIGHTMORN) || (tp == TP_SUNRISE)) { if (isplayer(lf) && godprayedto(R_GODLIFE)) { i += 1; } } if (i > 0) { // heal hp gainhp(lf, i); } // MP regeneration getflags(lf->flags, retflag, &nretflags, F_MPREGEN, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_MPREGEN) { gainmp(lf, f->val[0]); } } // druid gains mp from plants if (hasjob(lf, J_DRUID)) { int chance = 0; // 5% per plant in sight of gaining mp chance = countplantsinsight(lf) * 5; if (pctchance(chance)) { gainmp(lf, 1); } } } // either use up stamina, or gain it if (lfhasflag(lf, F_SPRINTING)) { modstamina(lf, -1.5); } else if (isswimming(lf) && isplayer(lf)) { double lossamt; // players lose stamina based on swimming skill // monsters don't. switch (getskill(lf, SK_SWIMMING)) { case PR_NOVICE: lossamt = 1; break; case PR_BEGINNER: lossamt = 1; break; case PR_ADEPT: lossamt = 0.5; break; case PR_SKILLED: lossamt = 0; break; case PR_EXPERT: lossamt = 0; break; case PR_MASTER: lossamt = 0; break; default: case PR_INEPT: lossamt = 3; break; } if (lossamt) modstamina(lf, -lossamt); } else if (isclimbing(lf) && !lfhasflag(lf, F_SPIDERCLIMB)) { double lossamt; // players lose stamina based on climbing skill // monsters don't lose stamina to climb. switch (getskill(lf, SK_CLIMBING)) { case PR_INEPT: lossamt = 2; break; case PR_NOVICE: lossamt = 1; break; case PR_BEGINNER: lossamt = 1; break; case PR_MASTER: lossamt = 0; break; default: lossamt = 0.5; break; } if (lossamt) modstamina(lf, -lossamt); } else { if (!stasis) { // if we didn't take action last turn, regenerate stamina. if (!killflagsofid(lf->flags, F_TOOKACTION) && !lfhasflag(lf, F_TRAINING)) { if (getstamina(lf) < getmaxstamina(lf)) { modstamina(lf, getstamregen(lf)); } } } } // god piety gets restored over time if (isplayer(lf)) { for (i = 0; i < ngodlfs; i++) { int piety; if (!godlf[i]) continue; if (!godprayedto(godlf[i]->race->id)) continue; piety = getpiety(godlf[i]->race->id); if ((piety < 100) || (piety > 199)) { int dir,chance; if (lfhasflag(player, F_TRAINING)) { chance = 0; } else if (piety < 100) { dir = 1; chance = piety; limit(&chance, 10, 100); } else { if (lfhasflagval(player, F_NOPIETYLOSS, godlf[i]->race->id, NA, NA, NULL)) { dir = 0; chance = 0; } else { dir = -1; chance = 100 - (piety-200); // ie. 100 max limit(&chance, 1, 50); } } // the further away from neutral you are, the less chance // piety/anger has of 'expiring' if (pctchance(chance)) { enum PIETYLEV newplev,oldplev; // slowly move towards normal oldplev = getpietylev(godlf[i]->race->id, NULL, NULL); modpiety(godlf[i]->race->id, dir); newplev = getpietylev(godlf[i]->race->id, NULL, NULL); checkgodbonus(godlf[i]->race->id,newplev, oldplev); } } } } if (pctchance(25) && lfhasflag(lf, F_PARANOIA) && !lfhasflag(lf, F_AWARENESS)) { if (isplayer(lf)) { cell_t *c; c = getcellindir(lf->cell, diropposite(lf->facing)); if (c && cellwalkable(NULL, c, NULL)) { int monisreal = B_FALSE; race_t *r; lifeform_t *mon; if (pctchance(15)) monisreal = B_TRUE; // either make a random noise from right behind the player, // or ACTUALL create a monster behind them! // get random mosnter who makes walk noise f = NULL; while (!f) { r = getreallyrandomrace(RC_ANY, NULL); f = hasflagval(r->flags, F_NOISETEXT, N_WALK, NA, NA, NULL); // don't want mosnters which will create objects, since we are going // to palce a temporary one! if (hasflag(r->flags, F_AUTOCREATEOB)) f = NULL; } // create the monster directly behind you if (monisreal) { // set autogen to false, we don't want minions appearing too. mon = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); } else { mon = addlf(c, r->id, 1); } if (mon) { if (monisreal) { killflagsofid(mon->flags, F_XPVAL); addflag(mon->flags, F_XPVAL, 0, NA, NA, NULL); turntoface(mon, lf->cell); } // it makes noise makenoise(mon, N_WALK); if (!monisreal) { // now kill the monster killlf(mon); } } } } else { // monster just turns around to look behind. loseaitargets(lf); setfacing(lf, diropposite(lf->facing)); } } // sixth sense spell warnings f = lfhasflag(lf, F_SIXTHSENSE); if (f) { cell_t *retcell[MAXCANDIDATES]; int nretcells; getradiuscells(lf->cell, 2, DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0); for (i = 0; i < nretcells; i++) { if (retcell[i]->lf && areenemies(lf, retcell[i]->lf) && !lfhasflag(retcell[i]->lf, F_6SENSEWARNED)) { int dir,reldir; dir = getdirtowards(lf->cell, retcell[i], NULL, B_FALSE, DT_ORTH); reldir = getrelativedir(lf, dir); if (reldir != RD_FORWARDS) { if (isplayer(lf)) { if (f->val[0] >= 5) { char buf[BUFLEN]; real_getlfnamea(retcell[i]->lf, buf, NULL, B_NOSHOWALL, B_CURRACE); warn("^WYour sixth sense warns you of %s %s you!", buf, (reldir == RD_BACKWARDS) ? "behind" : "beside" ); } else if (f->val[0] >= 3) { warn("^WYour sixth sense warns you of %s %s %s you!", needan(retcell[i]->lf->race->raceclass->name) ? "an" : "a", retcell[i]->lf->race->raceclass->name, (reldir == RD_BACKWARDS) ? "behind" : "beside" ); } else { warn("^WYour sixth sense warns you of something %s you!", (reldir == RD_BACKWARDS) ? "behind" : "beside" ); } if (f->val[0] >= 7) { char ch; ch = askchar("Turn to face the threat", "yn","y", B_TRUE, B_FALSE); if (ch == 'y') { turntoface(lf, retcell[i]); } } else { more(); } addtempflag(retcell[i]->lf->flags, F_6SENSEWARNED, B_TRUE, NA, NA, NULL, 10); } else { // monsters just turn to face you turntoface(lf, retcell[i]); } //killflag(f); break; } } } } if (onein(3) && hasbp(lf, BP_TAIL)) { cell_t *c; c = getcellindir(lf->cell, diropposite(lf->facing)); if (c && c->lf) { if (isplayer(lf) && !lfhasflag(lf, F_RAGE)) { warn("^WYour tail brushes up against something behind you!"); } else { turntoface(lf, c); } } } if (hasactivespell(lf, OT_S_SUMMONWEAPON) && !hasobwithflagval(lf->pack, F_CREATEDBYSPELL, OT_S_SUMMONWEAPON, NA, NA, NULL)) { stopspell(lf, OT_S_SUMMONWEAPON); } if (hasactivespell(lf, OT_S_CRYSTALARM) && !hasobwithflagval(lf->pack, F_CREATEDBYSPELL, OT_S_CRYSTALARM, NA, NA, NULL)) { stopspell(lf, OT_S_CRYSTALARM); } if (hasactivespell(lf, OT_S_CRYSTALSHIELD) && !hasobwithflagval(lf->pack, F_CREATEDBYSPELL, OT_S_CRYSTALSHIELD, NA, NA, NULL)) { stopspell(lf, OT_S_CRYSTALSHIELD); } if (hasactivespell(lf, OT_S_SLIDE)) { f = lfhasflag(lf, F_ICESLIDE); if (f) { f->val[0]--; if (f->val[0] <= 0) { stopspell(lf, OT_S_SLIDE); } } else { stopspell(lf, OT_S_SLIDE); } } if (hasactivespell(lf, OT_S_SIXTHSENSE) && !hasflag(lf->flags, F_SIXTHSENSE)) { stopspell(lf, OT_S_SIXTHSENSE); } if (lfhasflag(lf, F_MAGSHIELD)) { object_t *nexto; // metal weapons? for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; if (ismetal(o->material->id)) { if (isequippedon(o, BP_WEAPON) || isequippedon(o, BP_SECWEAPON)) { cell_t *c; // weapon flies away c = getrandomadjcell(lf->cell, &ccwalkable, B_NOEXPAND); if (!c) c = lf->cell; moveob(o, c->obpile, o->amt); if (isplayer(lf)) { getobname(o, buf, o->amt); msg("^wYour %s %s out of your hands!",noprefix(buf), OB1(o,"flies","fly")); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getobname(o, buf, o->amt); getlfname(lf, lfname); msg("%s%s %s flies out of its hands!",lfname, getpossessive(lfname), noprefix(buf), OB1(o,"flies","fly")); } } } } // objects on the ground? for (o = lf->cell->obpile->first ; o ; o = nexto) { nexto = o->next; if (ismetal(o->material->id)) { cell_t *c; // object flies away c = getrandomadjcell(lf->cell, &ccwalkable, B_NOEXPAND); if (!c) c = lf->cell; moveob(o, c->obpile, o->amt); if (isplayer(lf)) { getobname(o, buf, o->amt); msg("%s is repelled away from you!",buf); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getobname(o, buf, o->amt); getlfname(lf, lfname); msg("%s is repelled away from %s!",buf, lfname); } } else if (o->material->id == MT_WATER) { killtransitoryflags(lf->flags, F_HOTFEET); } } } // special effects if ((lf->race->id == R_ASHKARI) && !lfhasflag(lf, F_CONTROL)) { lifeform_t *otherlf; for (i = 1; i < lf->nlos; i++) { otherlf = lf->los[i]->lf; if (otherlf) { if (lfhasflag(otherlf, F_CANINE) || lfhasflag(otherlf, F_AVIAN)) { f = lfhasflag(lf, F_RAGE); if (f && (f->lifetime > 0)) { // already enraged? just extend time. enrage(lf, DEF_RAGETIME/2); } else { // announce, them make enraged. if (isplayer(lf)) { char lfname[BUFLEN]; getlfname(otherlf, lfname); msg("The sight of %s makes your blood boil!", lfname); } enrage(lf, DEF_RAGETIME/2); if (getstamina(lf) < 1) setstamina(lf, 1); forceredraw(); if (isplayer(lf)) more(); } } } } } f = lfhasflag(lf, F_TERRITORIAL); if (f && !lfhasflag(lf, F_HOSTILE) && !isinbattle(lf, B_INCLUDEDISTANT, B_FALSE) && !isfleeing(lf) && !isplayer(lf)) { lf->loslock = B_TRUE; for (i = 1 ; i < lf->nlos; i++) { lifeform_t *otherlf; otherlf = lf->los[i]->lf; if (otherlf && !areallies(lf, otherlf) && cansee(lf, otherlf) && !isasleep(otherlf)) { int dist; dist = getcelldist(lf->cell, lf->los[i]); if (dist == (f->val[0] + 1) || (dist == (f->val[0] + 2))) { // nearly in range makenoise(lf, N_TOOCLOSE); } else if (dist <= f->val[0]) { aiattack(lf, otherlf, aigetchasetime(lf)); if (cansee(player, lf) && !lfhasflagval(lf, F_NOISETEXT, N_GETANGRY, NA, NA, NULL)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^w%s becomes aggressive!", lfname); } } } } lf->loslock = B_FALSE; } f = lfhasflag(lf, F_AUTOROTATE); if (f && (f->val[0] != 0)) { if (!gettargetlf(lf)) { int n,whichway; if (f->val[0] > 0) whichway = 1; else whichway = -1; for (n = 0; n < abs(f->val[0]); n++) { int newdir; newdir = lf->facing + whichway; if (newdir < DC_N) newdir = DC_NW; if (newdir > DC_NW) newdir = DC_N; setfacing(lf, newdir); } // announce if (isplayer(lf)) { msg("You turn to face %s.", getdirname(lf->facing)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s turns to face %s.", lfname, getdirname(lf->facing)); } } } f = lfhasflag(lf, F_AUTOCREATEOB); if (f) { if (lf->cell->habitat->id != H_HEAVEN) { int radius; radius = f->val[0]; if (radius == -1) { cell_t *c; // add object in front. c = getcellindir(lf->cell, lf->facing); if (c && !c->type->solid) { addob(c->obpile, f->text); } } else { addobsinradius(lf->cell, f->val[0], DT_COMPASS, f->text, B_FALSE, B_INCLUDECENTRE, lf, NULL, NULL, NULL); } } } if (!stasis) { // handle life objects f = lfhasflag(lf, F_LIFEOB); if (f) { cell_t *corpseloc = NULL; object_t *corpse; char corpsename[BUFLEN]; corpseloc = findnearbylifeob(lf->cell, NA, f, &corpse); if (corpse) { // did we find a corpse in range ? if (corpse->type->id == OT_CORPSE) { strcpy(corpsename, "corpse"); } else { strcpy(corpsename, corpse->type->name); } if (lf->race->id == R_GHOST) { // give possession ability if (isplayer(lf) && !lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL)) { flag_t *f2; char pwbuf[BUFLEN]; int power; power = lf->level / 3; if (power < 1) power = 1; if (power > 10) power = 10; snprintf(pwbuf, BUFLEN, "pw:%d;",power); f2 = addflag(lf->flags, F_CANWILL, OT_S_POSSESSION, NA, NA, pwbuf); f2->lifetime = FROMRACE; } } } else { // can't see corpse if (lf->race->id == R_GHOST) { flag_t *f2; f2 = lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL); if (f2) killflag(f2); } // drain life if (isplayer(lf)) { msg("^BWithout your %s, you feel yourself fading into nothingness.", corpsename); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s seems to be fading from view!", lfname); } losehp(lf, f->val[2], DT_DIRECT, NULL, "fading into nothingness"); } } } //effects from other lifeforms for (l = lf->cell->map->lf ; l ; l = l->next) { if (!isdead(l) && (l != lf)) { if (l->race->baseid != lf->race->baseid) { // can't smell your own race. // gains/loses nauseation? f = lfhasflag(l, F_STENCH); if (f) { int range,power; range = f->val[0]; power = f->val[1]; if (getcelldist(l->cell, lf->cell) <= range) { // you get nauseated makenauseated(lf, power, 2, NULL); } } } asp = hasactivespell(l, OT_S_CALMINGSCENT); if (asp && (getcelldist(l->cell,lf->cell) == 1)) { if ((getraceclass(lf) == RC_ANIMAL) && lfhasflag(lf, F_HOSTILE)) { int maxhitdice; // calming strong enough? maxhitdice = asp->val[2] + 1; if (gettr(lf) <= maxhitdice) { makepeaceful(lf, l); } } } // they are hiding, and you haven't spotted them yet f = ishidingfrom(l, lf); if (f && !isinbattle(lf, B_NODISTANT, B_FALSE)) { // can you see their cell? if (!lfhasflag(lf, F_TRAINING) && haslos(lf, l->cell)) { int spotmod = 0; int dist; int trmod; /* if (!lfhasflag(l, F_SILENTMOVE) && !isdeaf(lf)) { bonus += (getskill(lf, SK_LISTEN)/2); } */ // harder to spot hiding lfs which are further away dist = getcelldist(lf->cell, l->cell); if (dist == 1) { spotmod += 15; } else if (dist > 1) { spotmod -= ((dist-1)*5); } // spotters gets +5 to the check for every threat rating // they are higher than the hiding lf. trmod = gettr(lf) - gettr(l); if (trmod > 0) { spotmod += (trmod*5); } // did you spot them? if (skillcheckvs(lf, SC_SEARCH, spotmod, l, SC_STEALTH, f->val[0])) { spot_hiding_lf(lf, l); } } } } } // secret doors, traps, etc? if (isplayer(lf) && !isinbattle(lf, B_INCLUDEDISTANT, B_FALSE) && !isblind(lf) && !lfhasflag(lf, F_TRAINING)) { for (i = 0; i < lf->nlos; i++) { if (!lf->los[i]->lf || (lf->los[i]->lf == lf)) { int distmod; object_t *o; distmod = getdistspotmod(lf, lf->los[i]); for (o = lf->los[i]->obpile->first; o ; o = o->next) { flag_t *f; int mod = 0; // object which IS secret (ie a trap or secret door) f = hasflag(o->flags, F_SECRET); if (f && (f->val[0] != NA)) { int diff; if (hasflag(o->flags, F_TRAP)) { enum SKILLLEVEL slev; slev = getskill(lf, SK_ENGINEERING); if (slev == PR_MASTER) { mod += 500; // ie. autopass } else { mod += (slev*2); } } diff = f->val[0] + distmod; if (skillcheck(lf, SC_SEARCH, diff, mod)) { char obname[BUFLEN]; // reveal it getobnametruebase(o, obname, o->amt); msg("^wYou notice %s!",obname); interrupt(lf); killflag(f); needredraw = B_TRUE; drawscreen(); // train skills practice(lf, SK_PERCEPTION, 1); if (hasflag(o->flags, F_TRAP)) { practice(lf, SK_ENGINEERING, 1); } } } // object which CONTAINS something secret (ie. trapped door/chest) f = hasflag(o->flags, F_TRAPPED); if (f && (f->val[2] != B_TRUE) && !hasflag(o->flags, F_SECRET)) { objecttype_t *ot; flag_t *trapflag; enum SKILLLEVEL slev; int diff; // find trap type ot = findot(f->val[0]); trapflag = hasflag(ot->flags, F_TRAP); assert(trapflag); diff = trapflag->val[0] + distmod; slev = getskill(lf, SK_ENGINEERING); if (slev == PR_MASTER) { mod += 500; // ie. autopass } else { mod += (slev*2); } diff = trapflag->val[0] + distmod; if (skillcheck(lf, SC_SEARCH, diff, mod)) { char obname[BUFLEN]; // reveal it getobname(o, obname, o->amt); msg("^wYou notice a trap on %s!",obname); interrupt(lf); f->val[2] = B_TRUE; needredraw = B_TRUE; drawscreen(); // train skills practice(lf, SK_PERCEPTION, 1); if (hasflag(o->flags, F_TRAP)) { practice(lf, SK_ENGINEERING, 1); } } } // object which is really a monster f = hasflag(o->flags, F_ISMONSTER); if (f && (f->val[2] != NA)) { int diff; diff = f->val[2] + distmod; if (skillcheck(lf, SC_SEARCH, diff, mod)) { lifeform_t *newlf; // reveal it newlf = reveal_pretendob(o); if (newlf && cansee(player, newlf)) { char obname[BUFLEN]; char lfname[BUFLEN]; getobname(o, obname, o->amt); capitalise(obname); getlfnamea(newlf, lfname); msg("^wHey! %s %s really %s!",obname, (o->amt == 1) ? "is" : "are",lfname); more(); interrupt(lf); needredraw = B_TRUE; drawscreen(); // train skills practice(lf, SK_PERCEPTION, 1); } continue; } } } } } } // summoned creatures f = hasflag(lf->flags, F_SUMMONEDBY); if (f) { lifeform_t *creator; creator = findlf(NULL, f->val[0]); if (!creator) { willvanish = B_TRUE; } else { if (f->val[1] > 0) { f->val[1]--; if (f->val[1] <= 0) { willvanish = B_TRUE; } } } if (willvanish) { // lf dies. unsummon(lf, B_TRUE); } } if (isdead(lf)) return; if (lfhasflag(lf, F_DODGES)) { enum ERROR e; if (celldangerous(lf, lf->cell, B_TRUE, &e)) { object_t *avoidob = NULL; cell_t *adj; if ((e == E_AVOIDOB) && rdata) { // remember this sine getdodgecell() will call // calldangerous() again which will overwrite rdata. avoidob = (object_t *)rdata; } // autododge! adj = getdodgecell(lf); if (adj) { char fromx[BUFLEN]; if ((e == E_AVOIDOB) && rdata) { getobname(avoidob, fromx, avoidob->amt); } else { snprintf(fromx, BUFLEN, "danger"); } if (isplayer(lf)) { msg("You reflexively dodge away from %s!", fromx); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s reflexively dodges away from %s!", lfname, fromx); } f = addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL); moveto(lf, adj, B_FALSE, B_FALSE); killflag(f); } } } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // IMPORTANT: any potentially damaging effects have to go after here... ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// for (a = 0; a < MAXATTS; a++) { if ((lf->baseatt[a] > 0) && (getattr(lf, a) <= 0)) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%s drain",getattrname(a)); setkillverb(lf, "Killed"); setlastdam(lf, buf); if (isplayer(lf)) { msg("^%cThe last of your %s drains away...", getlfcol(lf, CC_VBAD), getattrname(a)); more(); } lf->hp = 0; return; } } o = hasequippedobid(lf->pack, OT_AMU_CHOKING); if (o && needstobreath(lf)) { int seen = B_FALSE; if (isplayer(lf)) { msg("Your amulet is choking you!"); seen = B_TRUE; } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s%s amulet is choking %s!", lfname, getpossessive(lfname), it(lf)); seen = B_TRUE; } if (seen && !isknown(o)) { makeknown(o->type->id); } losehp(lf, rolldie(2,6), DT_DIRECT, NULL, "an amulet of choking"); setkillverb(lf, "Strangled"); } if (lfhasflag(lf, F_RAGE)) { killflagsofid(lf->flags, F_HIDING); } // if you run out of stamina while climbing, you fall if (isclimbing(lf)) { if ((getstamina(lf) <= 0) || isburdened(lf) || lfhasflag(lf, F_GRAVBOOSTED)) { stopclimbing(lf, B_FALSE); if (isdead(lf)) return; } } // if you are stunned and flying, you fall inair = isairborne(lf, NULL); if (inair) { if (lfhasflag(lf, F_STUNNED)) { fall_from_air(lf); } else if (isburdened(lf)) { fall_from_air(lf); } } // poison effects if (!stasis) { getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE); for (i = 0; i < nretflags; i++) { poisontype_t *pt; f = retflag[i]; pt = findpoisontype(f->val[0]); // chance of fighting it off - gets easier over time. // if ((f->lifetime > 0) && skillcheck(lf, SC_POISON, (f->lifetime * 20), -(f->val[1]*10) )) { killflag(f); } else { flag_t *asleep; int chance; int ko; // being asleep helps. asleep = hasflag(lf->flags, F_ASLEEP); ko = isunconscious(lf); // chance of losing hp chance = pt->dampct; if (f->val[1] > 1) { //+10% chance per power > 1 chance = pctof(100 + ((f->val[1] - 1)*10), chance); } if (!ko && pctchance(chance)) { char buf[BUFLEN]; if (isplayer(lf) || cansee(player, lf)) { char *p; char lfname[BUFLEN],lfnameposs[BUFLEN]; getlfname(lf, lfname); snprintf(lfnameposs, BUFLEN, "%s%s",lfname, getpossessive(lfname)); p = strdup(pt->damverb); if (isplayer(lf)) { strrep(&p, "YOUR", "Your", NULL); if (asleep) { strrep(&p, "YOU", "You wake up and", NULL); } else { strrep(&p, "YOU", "You", NULL); } strrep(&p, "#S", "", NULL); } else { strrep(&p, "YOUR", lfnameposs, NULL); if (asleep) { char replacetext[BUFLEN]; snprintf(replacetext, BUFLEN, "%s wakes and", lfname); strrep(&p, "YOU", replacetext, NULL); } else { strrep(&p, "YOU", lfname, NULL); } strrep(&p, "#S", "s", NULL); } msg("%s", p); free(p); if (asleep) { int origborn; origborn = lf->born; lf->born = B_FALSE; // supress a second announcement of waking up killflagsofid(lf->flags, F_ASLEEP); lf->born = origborn; // force LOS recalc since we are now awake. setlosdirty(lf); } else { taketime(lf, getactspeed(lf)); } } // special case if (pt->id == P_TETANUS) { enum BODYPART bp,poss[MAXBODYPARTS]; int nposs = 0; for (bp = 0; bp < MAXBODYPARTS; bp++) { int ok; switch (bp) { case BP_LEGS: case BP_HEAD: case BP_HANDS: case BP_BODY: case BP_TAIL: case BP_WINGS: ok = B_TRUE; break; default: ok = B_FALSE; break; } if (ok && hasbp(lf, bp)) { poss[nposs++] = bp; } } if (nposs) { object_t *oo; bp = poss[rnd(0,nposs-1)]; switch (bp) { case BP_LEGS: injure(lf, bp, DT_BASH, IJ_LEGBROKEN); break; case BP_HEAD: break; // no bad effects case BP_HANDS: // drop your weapon oo = getequippedob(lf->pack, bp); if (oo) drop(oo, oo->amt); break; case BP_BODY: if (onein(2)) { injure(lf, bp, DT_BASH, IJ_RIBCRACKED); } else { if (isplayer(lf)) { msg("^BYour spine snaps!"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s%s spine snaps!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname)); } losehp_real(lf, lf->maxhp, DT_DIRECT, NULL, "tetanus", B_FALSE, NULL, B_FALSE, NULL, B_FALSE, BP_NONE, B_FROMCRIT); } break; case BP_TAIL: injure(lf, bp, DT_BASH, IJ_TAILBROKEN); break; case BP_WINGS: injure(lf, bp, DT_SLASH, IJ_WINGTORN); break; default: break; } } } else { snprintf(buf, BUFLEN, "%s^from %s",pt->name, f->text); losehp(lf, pt->dam * f->val[1], DT_DIRECT, NULL, buf); } if (pt->vomitob != OT_NONE) { addobfast(lf->cell->obpile, pt->vomitob); } loseconcentration(lf); } // extra effects if (!ko && (f->val[0] == P_COLD) && !isimmuneto(lf->flags, DT_COLD, B_FALSE)) { if (rnd(1,100) <= 10) { shiver(lf); } } else if (f->val[0] == P_MIGRAINE) { // sleeping will avoid all migraine effects if (!asleep && !ko && !isblind(lf)) { object_t *lamp = NULL; int amt; amt = lfproduceslight(lf, &lamp); if (amt) { int dam; // note: amt will be doubled due to light vulnerability, // so half it here. dam = amt/2; limit(&dam, 1, NA); losehp(lf, dam, DT_LIGHT, NULL, "a migraine"); if (isplayer(lf)) { char obname[BUFLEN]; if (lamp) { getobname(lamp, obname, lamp->amt); } else { strcpy(obname, "a body"); // noprefix will strip the 'a ' } msg("Your head explodes in pain at the light from your %s!", noprefix(obname)); } } } } } } if (!isunconscious(lf) && !isasleep(lf)) { f = hasflag(lf->flags, F_NAUSEATED); if (f) { // chance of being delayed if (onein(4)) { if (isplayer(lf)) { msg("^bYou %s!", rnd(0,1) ? "retch" : "gag"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s %s.", getlfcol(lf, CC_BAD), lfname, rnd(0,1) ? "retches" : "gags"); } taketime(lf,getactspeed(lf)); loseconcentration(lf); } } } } // end if !statis if (isdead(lf)) return; // temperature effects temp = getlftemp(lf); if (temp != T_NORMAL) { int shivermult = 0, coldmult = 0; int exlimbs,coldtime = 0; exlimbs = getexposedlimbs(lf); switch (temp) { case T_VCOLD: shivermult = 30; coldmult = 20; coldtime = 30 + rnd(10,30); break; case T_COLD: shivermult = 20; coldmult = 15; coldtime = 20 + rnd(10,20); break; case T_CHILLY: shivermult = 10; coldmult = 0; break; case T_NORMAL: break; case T_WARM: meltchance += 25; meltdam = roll("1d6"); break; case T_HOT: meltchance += 50; meltdam = roll("2d6"); break; case T_VHOT: meltchance += 100; meltdam = roll("2d6"); break; } if ((temp < T_NORMAL) && !isimmuneto(lf->flags, DT_COLD, B_FALSE) && !isresistantto(lf->flags, DT_COLD, B_FALSE)) { if (!skillcheck(lf, SC_CON, (exlimbs*coldmult), 0)) { poison(lf, coldtime, P_COLD, 0, "the cold", R_NONE, B_FALSE); } else if (!skillcheck(lf, SC_CON, (exlimbs*shivermult), 0)) { shiver(lf); } } // chance to melt if you are frozen or made of ice if (lfhasflag(lf, F_FROZEN) || (lf->material->id == MT_ICE)) { if (pctchance(meltchance)) { losehp(lf, meltdam, DT_MELT, NULL, "melting"); addob(lf->cell->obpile, "small puddle of water"); if (isplayer(lf)) { msg("^BYou are melting!"); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s melts a little.",getlfcol(lf, CC_BAD), lfname); } } } } if (isdead(lf)) return; if (!movedlastturn) { getflags(lf->flags, retflag, &nretflags, F_HOTFEET, F_NONE); if (nretflags && !hasobofmaterial(lf->cell->obpile, MT_WATER) && !isimmuneto(lf->flags, DT_FIRE, B_FALSE)) { for (i = 0; i < nretflags; i++) { int dam; enum DAMTYPE dt; f = retflag[i]; dam = f->val[0]; dt = f->val[1]; o = getarmour(lf, BP_FEET); if (o && ismetal(o->material->id)) { dam *= 2; } if (losehp(lf, dam, dt, NULL, f->text)) { if (isplayer(lf)) { msg("^bYour %s burn!^n", getbodypartname(lf, BP_FEET)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^b%s%s %s burn!^n", lfname, getpossessive(lfname), getbodypartname(lf, BP_FEET)); } } } } } if (isdead(lf)) return; bloodamu = hasequippedobid(lf->pack, OT_AMU_BLOOD); // effects from cell objects? for (o = lf->cell->obpile->first ; o ; o = nexto) { int amt; nexto = o->next; if (bloodamu && (o->material->id == MT_BLOOD)) { int amt; amt = getobhp(o, NULL) * o->amt; limit(&amt, 1, 10); if (!isknown(bloodamu)) { if (isplayer(lf) || cansee(player, lf)) { char obname[BUFLEN],bname[BUFLEN]; char lfname[BUFLEN]; getlfname(lf, lfname); getobname(bloodamu, obname, 1); getobname(o, bname, 1); msg("^%c%s%s %s sucks up %s!", getlfcol(lf, CC_GOOD), lfname, getpossessive(lfname), noprefix(obname), bname); makeknown(bloodamu->type->id); } } gainhp(lf, amt); killob(o); continue; } if (o->type->id == OT_HOLYCIRCLE) { if (isundead(lf) || lfhasflag(lf, F_LYCANTHROPE)) { char obname[BUFLEN]; getobname(o, obname, 1); if (isplayer(lf)) { msg("^%c%s burns you!^n", getlfcol(lf, CC_BAD), obname); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s burns %s!^n", getlfcol(lf, CC_BAD), obname, lfname); } losehp(lf, rnd(10,20), DT_HOLY, NULL, "a holy circle"); } } if (o->type->id == OT_PENTAGRAM) { if (isundead(lf) && (lf->hp < lf->maxhp)) { char obname[BUFLEN]; getobname(o, obname, 1); if (isplayer(lf)) { msg("^%c%s heals you!^n", getlfcol(lf, CC_GOOD), obname); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("^%c%s heals %s!^n", getlfcol(lf, CC_GOOD), obname, lfname); } gainhp(lf, rnd(1,6)); } } /* f = hasflag(o->flags, F_GODSTONE); if (f && (f->val[2] == B_TRUE)) { flag_t *f2; lifeform_t *god; // warn! f2 = hasflag(o->flags, F_LINKGOD); god = findgod(f2->val[0]); godsay(god->id, B_TRUE, "Mortal! Do not touch that!"); more(); f->val[2] = B_FALSE; } */ f = hasflag(o->flags, F_WALKDAM); if (f) { applywalkdam(lf, roll(f->text), f->val[0], o, BP_NONE); } amt = obproduceslight(o); if (amt && !isblind(lf)) { if (lighthurtseyes(lf)) { int min=2,max; if (isplayer(lf)) { msg("The bright light burns your vision-enhanced eyes!"); } // blind for 2+ turns max = (amt/2); limit(&max, min+1,NA); addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(2,max)); } if (isvulnto(lf->flags, DT_LIGHT, B_FALSE)) { int dam; char obname[BUFLEN],damstring[BUFLEN]; // note: amt will be doubled due to light vulnerability, // so half it here. dam = amt/2; limit(&dam, 1, NA); getobnametruebase(o, obname, o->amt); snprintf(damstring, BUFLEN, "light from %s", obname); if (isplayer(lf)) { msg("Your head explodes in pain at the light from %s!", obname); } losehp(lf, dam, DT_LIGHT, NULL, damstring); } } if (!stasis) { f = hasflag(o->flags, F_CAUSESCOUGH); if (f && !isimmuneto(lf->flags, DT_POISONGAS, B_FALSE)) { char obname[BUFLEN]; getobname(o, obname, o->amt); if (!skillcheck(lf, SC_CON, f->val[0] * o->amt, 0)) { if (isplayer(lf)) { msg("^wYou cough on %s.", obname); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s coughs on %s.",lfname, obname); } taketime(lf, 5); loseconcentration(lf); } } f = hasflag(o->flags, F_STENCH); if (f) { makenauseated(lf, f->val[1], 2, NULL); } } // for flags which can occur multiple times getflags(o->flags, retflag, &nretflags, F_WALKDAMBP, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_WALKDAMBP) { if (!isairborne(lf, NULL)) { object_t *armour; int dam; enum BODYPART bp; enum DAMTYPE damtype; bp = f->val[0]; damtype = f->val[1]; dam = roll(f->text); getobname(o, buf, o->amt); // some things get your armour too! armour = getouterequippedob(lf, bp); if (armour) { takedamage(armour, dam, damtype, NULL); } else { if (f->val[2] == FALLTHRU) { // certain combinations might give announcements... switch (damtype) { case DT_WATER: if ((bp == BP_FEET) && isplayer(lf) && hasbp(lf, bp)) { char warntext[BUFLEN]; snprintf(warntext, BUFLEN,"Your %s get wet.", getbodypartname(lf, bp)); // only announce this if you've had a turn // without your feet wet. real_warnabout(warntext, 2, B_FALSE); } break; default: break; } // apply damage to lf instead. applywalkdam(lf, roll(f->text), f->val[1], o, f->val[0]); } } } } } // end foreach object flag } if (isdead(lf)) return; // effects from pack objects for (o = lf->pack->first ; o ; o = o->next) { // touch your weapons/armour in case it became hot, it was blessed and you // becaome undead, etc. if (isequipped(o)) { real_touch(lf, o, B_NOTONPURPOSE); } } if (isdead(lf)) return; // effects for/on your own flags getflags(lf->flags, retflag, &nretflags, F_ANTICIPATE, F_ATTACHEDTO, F_CANCAST, F_CANWILL, F_CHARMEDBY, F_CLIMBING, F_FEIGNFOOLEDBY,F_FLEEFROM, F_GRABBEDBY, F_GRABBING, F_HIDING, F_BOOSTSPELL, F_FEIGNINGDEATH, F_FULLSHIELD,F_HPDRAIN, F_INCUBATING, F_INJURY, F_NOFLEEFROM, F_PETOF, F_SIZETIMER, F_SOULLINK, F_SPOTTED, F_STRIKETOKO, F_TARGETCELL, F_TARGETLF, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; // remove impossible/expired flags if (f->id == F_ANTICIPATE) { if (f->val[1] <= 0) { killflag(f); continue; } else if (!findlf(lf->cell->map, f->val[0])) { killflag(f); continue; } } if ((f->id == F_BOOSTSPELL) && (f->val[0] == OT_S_PASSWALL)) { if (!lfhasflag(lf, F_NONCORPOREAL)) { killflag(f); continue; } } if ((f->id == F_FEIGNINGDEATH) && !isprone(lf)) { killflag(f); continue; } if (f->id == F_ATTACHEDTO) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); if (!lf2) { killflag(f); continue; } else if (getcelldist(lf2->cell, lf->cell) != 1) { killflag(f); continue; } } if (f->id == F_HIDING) { if (lfhasflag(lf, F_SPRINTING) || lfproduceslight(lf, NULL)) { killflag(f); continue; } } if (f->id == F_FULLSHIELD) { object_t *sh; sh = hasobid(lf->pack, atol(f->text)); if (!sh || !isequipped(sh)) { killflag(f); continue; } } if (f->id == F_GRABBEDBY) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); if (!lf2) { killflag(f); continue; } else if (getcelldist(lf2->cell, lf->cell) != 1) { killflag(f); continue; } } if (f->id == F_GRABBING) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); if (!lf2) { killflag(f); continue; } else if (getcelldist(lf2->cell, lf->cell) != 1) { killflag(f); continue; } } if (!stasis) { if ((f->id == F_INCUBATING) && (f->val[1] > 0)) { f->val[1]--; if (f->val[1] <= 0) { // note: this functino will kill f completeincubation(lf, f); continue; } } // bleeding injuries can stain armour. also mark injuries as no longer new. if ((f->id == F_INJURY) && (f->val[2] == DT_SLASH)) { object_t *arm; if (f->obfrom == B_NEWINJURY) f->obfrom = B_FALSE; arm = getouterequippedob(lf, f->val[1]); if (arm && !hasobmod(arm, findobmod(OM_BLOODSTAINED)) && pctchance(5)) applyobmod(arm, findobmod(OM_BLOODSTAINED)); } if (f->id == F_SIZETIMER) { f->val[1]--; if (f->val[1] <= 0) { resizelf(lf, f->val[0], f->val[2]); // resizelf will have now killed 'f'. continue; } } } /* if (f->id == F_STABBEDBY) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); // stabber can't see you anymore if (!lf2 || !cansee(lf2, lf)) { killflag(f); continue; } } */ if (f->id == F_SOULLINK) { lifeform_t *lf2; lf2 = findlf(lf->cell->map, f->val[0]); if (!lf2) { killflag(f); continue; } } if (f->id == F_SPOTTED) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); if (!lf || !lf2 || !cansee(lf, lf2) || !lfhasflag(lf2, F_HIDING)) { killflag(f); continue; } } if (f->id == F_CLIMBING) { if (!lf->cell->type->solid) { killflag(f); continue; } } if (f->id == F_FEIGNFOOLEDBY) { if (!findlf(lf->cell->map, f->val[0])) { killflag(f); continue; } } if ((f->id == F_CHARMEDBY) || (f->id == F_PETOF) || (f->id == F_FLEEFROM) || (f->id == F_NOFLEEFROM)) { lifeform_t *lf2; lf2 = findlf(NULL, f->val[0]); if (!lf2) { killflag(f); continue; } } if (f->id == F_STRIKETOKO) { object_t *weapon; weapon = getweapon(lf); if (weapon) { // (unarmed is ok) skill_t *sk; sk = getobskill(weapon->flags); if (!sk || (sk->id != SK_CLUBS)) { killflag(f); continue; } } } // recharge abilities if (!stasis) { if ((f->id == F_CANWILL) || (f->id == F_CANCAST)) { if (f->val[2] != NA) { if (f->val[1] < f->val[2]) { f->val[1]++; } } } } // remove invalid targets if ((f->id == F_TARGETLF) || (f->id == F_TARGETCELL)) { lifeform_t *targ = NULL; if (f->id == F_TARGETLF) { targ = findlf(lf->cell->map, f->val[0]); } else if (f->id == F_TARGETCELL) { cell_t *c; c = getcellat(lf->cell->map, f->val[0], f->val[1]); if (c && c->lf) { targ = c->lf; } } if (targ) { if (areallies(lf, targ) || lfhasflagval(lf, F_FEIGNFOOLEDBY, targ->id, NA, NA, NULL)) { if (lfhasflag(lf, F_DEBUG)) { char lfname[BUFLEN]; char targname[BUFLEN]; getlfname(lf, lfname); getlfname(targ, targname); dblog("db: %s no longer targetting %s.",lfname,targname); } killflag(f); continue; } } } // end if f_target or f_targetcell // hp drain if (!stasis) { if (f->id == F_HPDRAIN) { losehp(lf, f->val[0], f->val[1], NULL, f->text); if (isdead(lf)) { break; } } } } // end loop through lf flags // picked up your first godstone ? if (isplayer(lf)) { o = hasobofclass(lf->pack, OC_GODSTONE); if (o && !lfhasflag(lf, F_FOUNDGODSTONE)) { lifeform_t *god,*opposegod; // get linked gods f = hasflag(o->flags, F_LINKGOD); god = findgod(f->val[0]); opposegod = findgod(getopposinggod(god->race->id)); godstone_pickup_effects(god, opposegod, o); addflag(lf->flags, F_FOUNDGODSTONE, B_TRUE, NA, NA, NULL); } } } // returns TRUE on failure (ie. nothing to steal) int steal(lifeform_t *lf, obpile_t *op, enum FLAG wantflag) { enum SKILLLEVEL slev; object_t *o; int i,nsteals; int numgot = 0; int fromground; char letter = 'a'; slev = getskill(lf, SK_THIEVERY); if (op->owner) { fromground = B_FALSE; } else { fromground = B_TRUE; } // if (slev >= PR_EXPERT) { nsteals = 2; } else { nsteals = 1; } // what do we steal? for (i = 0; i < nsteals; i++) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "Steal what (%d of %d)?", i+1, nsteals); initprompt(&prompt, buf); for (o = op->first ; o ; o = o->next) { int ok = B_TRUE; if ((slev < PR_SKILLED) && (getobweight(o) >= 3)) { // too heavy to steal ok = B_FALSE; } else if ((slev < PR_MASTER) && isequipped(o)) { // equipped ok = B_FALSE; } else if (!canpickup(lf, o, 1)) { // can't pick it up ok = B_FALSE; } else if (hasflag(o->flags, F_NOSTEAL)) { ok = B_FALSE; } else if ((wantflag != F_NONE) && !hasflag(o->flags, wantflag)) { // don't have the right flag ok = B_FALSE; } if (ok) { getobname(o, buf, 1); addchoice(&prompt, fromground ? letter++ : o->letter, buf, NULL, o, NULL); } } if (prompt.nchoices > 1) { if (slev >= PR_ADEPT) { if (isplayer(lf)) { addchoice(&prompt, '-', "Nothing", NULL, NULL, NULL); // pick what you want getchoice(&prompt); o = (object_t *)prompt.result; } else { int nn,nposs = 0; object_t *poss[MAXCHOICES*2]; // get something we want for (nn = 0; nn < prompt.nchoices; nn++) { int cov; if (aiwants(lf, (object_t *)prompt.choice[nn].data, &cov)) { if (cov) { // add twice poss[nposs++] = o; poss[nposs++] = o; } else { poss[nposs++] = o; } } } if (nposs) { o = poss[rnd(0,nposs-1)]; } else { o = (object_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; } } } else { // random o = (object_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; } if (o) { int amt = 1; //killflagsofid(o->flags, F_SHOPITEM); if (o->type->id == OT_GOLD) { amt = rnd(1,slev*33); } o = moveob(o, lf->pack, amt); if (o) { char obname[BUFLEN]; char lfname[BUFLEN]; char targname[BUFLEN]; getlfname(lf, lfname); getobname(o, obname, amt); if (op->owner) { getlfname(op->owner, targname); if (isplayer(lf)) { msg("^%cYou steal %s from %s!", getlfcol(op->owner, CC_BAD), obname, targname); } else if (cansee(player, lf)) { msg("^%c%s steals %s from %s!", getlfcol(op->owner, CC_BAD), lfname, obname, targname); } } else { if (isplayer(lf)) { msg("You steal %s!", obname); } else if (cansee(player, lf)) { msg("%s steals %s!", lfname, obname); } } numgot++; } } } else { // nothing left to steal if (numgot == 0) { return B_TRUE; } break; } } // end foreach steal if (isplayer(lf)) pleasegodmaybe(R_GODTHIEVES, 5+numgot); return B_FALSE; } // returns TRUE if l woke up. int stir(lifeform_t *l, int vol, int dist, char *noisetext) { flag_t *f; int rv = B_FALSE; // wake up a little f = lfhasflag(l, F_ASLEEP); if (f && (f->val[1] != ST_KO)) { if (f->lifetime > 0) { // ie. temporary timeeffectsflag(f, vol + rnd(1,3)); } else if (f->lifetime == PERMENANT) { if (f->val[2] == NA) { // ie asleep rather than 'resting' // wake up! if (isplayer(l)) { msg("^wA nearby noise %s you!", (f->val[1] == ST_MEDITATING) ? "startles" : "awakens" ); rv = B_TRUE; } killflag(f); } else { // ie resting on purpose via 'R' // only wake up if the sound if very close if (vol >= dist) { // wake up! if (isplayer(l)) { if (noisetext) { char wakenoise[BUFLEN]; char *punc; strcpy(wakenoise, noisetext); // omit punctuation punc = &(wakenoise[strlen(wakenoise)-1]); switch (*punc) { case '"': break; default: *punc = '\0'; break; } msg("^wThe sound of %s awakens you!", wakenoise); } else { msg("Something awakens you!"); } rv = B_TRUE; } killflag(f); } } // make it temporary //f->lifetime = rnd(1,10); } // still asleep? f = lfhasflag(l, F_ASLEEP); if (f && (f->val[1] == ST_ASLEEP) && cansee(player, l)) { char lfname[BUFLEN]; getlfname(l, lfname); msg("%s stir%s in %s slumber...", lfname, isplayer(l) ? "" : "s", isplayer(l) ? "your" : "its"); } } return rv; } int stone(lifeform_t *lf) { char lfname[BUFLEN]; char statname[BUFLEN]; int failed = B_FALSE; if (!lfcanbestoned(lf)) { failed = B_TRUE; } if (failed) { return B_TRUE; } getlfname(lf, lfname); // kill lifeform addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); snprintf(statname, BUFLEN, "statue of a %s", lf->race->name); killflagsofid(lf->flags, F_CORPSETYPE); addflag(lf->flags, F_CORPSETYPE, B_TRUE, NA, NA, statname); if (cansee(player, lf)) { msg("^%c%s %s to stone!", getlfcol(lf, CC_VBAD), lfname, isplayer(lf) ? "turn" : "turns"); } setlastdam(lf, "petrification"); lf->hp = 0; return B_FALSE; } void stopeating(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_EATING); if (f) { if (isplayer(lf)){ msg("You stop eating."); } killflagsofid(lf->flags, F_EATING); } } int stopclimbing(lifeform_t *lf, int onpurpose) { cell_t *c; char lfname[BUFLEN]; getlfname(lf, lfname); if (!onpurpose) { if (isplayer(lf)) { msg("You fall off %s!", lf->cell->type->name); } else if (cansee(player, lf)) { msg("%s falls off %s!", lfname, lf->cell->type->name); } } c = getcellindir(lf->cell, lf->facing); if (!cellwalkable(lf, c, NULL)) { if (onpurpose) { if (isplayer(lf)) { msg("There is no room for you to stop climbing!"); } return B_TRUE; } // if not on purpose, try to find another cell c = getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND); if (!c) { if (isplayer(lf)) { msg("Luckily, there is no room for you to fall."); } return B_TRUE; } } movelf(lf, c, onpurpose); killflagsofid(lf->flags, F_CLIMBING); if (onpurpose) { if (isplayer(lf)) msg("You drop down to the ground."); taketime(lf, getmovespeed(lf)); } else { fall(lf, NULL, B_TRUE); } return B_FALSE; } void stoppathfinding(lifeform_t *lf) { killflagsofid(lf->flags, F_PATHFINDING); } void stopresting(lifeform_t *lf) { flag_t *f; // stop training f = hasflag(lf->flags, F_TRAINING); if (f) { killflag(f); if (isplayer(lf)) { msg("Your training is interrupted!"); } else if (cansee(player, lf)) { char buf[BUFLEN]; getlfname(lf, buf); capitalise(buf); msg("%s stops training.",buf); } statdirty = B_TRUE; } // stop resting! f = isresting(lf); if (f) { if (isplayer(lf)) { msg("Your %s is interrupted!", (f->val[1] == ST_MEDITATING) ? "meditation" : "rest"); } else if (cansee(player, lf)) { char buf[BUFLEN]; getlfname(lf, buf); msg("%s stops %s.",buf, (f->val[1] == ST_MEDITATING) ? "meditating" : "resting"); } killflag(f); statdirty = B_TRUE; } killflagsofid(lf->flags, F_RESTUNTILALLIES); killflagsofid(lf->flags, F_RESTUNTILBETTER); /* if (isplayer(lf)) { f = lfhasflag(lf, F_RESTINGINMOTEL); if (f) { msg("You stayed for %d turns out of %d.", f->val[1], f->val[0]); } } */ killflagsofid(lf->flags, F_RESTINGINMOTEL); } void stoprunning(lifeform_t *lf) { killflagsofid(lf->flags, F_RUNNING); } void stopsprinting(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_SPRINTING); if (f && f->val[0]) { killflag(f); } } int stun(lifeform_t *lf, int nturns) { if (lfhasflag(lf, F_ASLEEP) || lfhasflag(lf, F_STUNNED)) { return B_TRUE; } addtempflag(lf->flags, F_STUNNED, B_TRUE, NA, NA, NULL, nturns); loseaitargets(lf); loseconcentration(lf); return B_FALSE; } // wrapper for addmonster(), but announce that it appears // and make it worth zero xp. // // for unique monsters, they move from their current position. lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, char *racename, int lifetime, int wantfriendly) { lifeform_t *newlf = NULL; char buf[BUFLEN]; newlf = addmonster(c, rid, racename, B_TRUE, 1, B_FALSE, B_NOEXTRA, NULL); if (newlf) { if (haslos(player, c)) { //char *newbuf; getlfnamea(newlf, buf); capitalise(buf); msg("%s appears!", buf); } if (!hasflag(newlf->flags, F_UNIQUE)) { // summoned if (caster) { addflag(newlf->flags, F_SUMMONEDBY, caster->id, lifetime, NA, NULL); if (wantfriendly) { addflag(newlf->flags, F_PETOF, caster->id, NA, NA, NULL); if (areallies(player, caster)) { makefriendly(newlf, PERMENANT); } } } // not worth any xp killflagsofid(newlf->flags, F_XPVAL); addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL); } } return newlf; } int takeoff(lifeform_t *lf, object_t *o) { char obname[BUFLEN]; char buf[BUFLEN]; object_t *errob = NULL; if (!isarmour(o)) { return unweild(lf, o); } if (lfhasflag(lf, F_RAGE)) { if (isplayer(lf)) msg("You are too enraged!"); return B_TRUE; } getobname(o, obname, 1); if (!cantakeoff(lf, o, &errob)) { switch (reason) { case E_CURSED: if (isplayer(lf)) { msg("Your %s appears to be stuck to you!", noprefix(obname)); o->blessknown = B_TRUE; } // still take time taketime(lf, getactspeed(lf)); break; case E_NOTEQUIPPED: if (isplayer(lf)) { msg("You are not wearing that!"); } break; case E_INJURED: if (isplayer(lf)) { msg("Your injury prevents you from removing your %s.", noprefix(obname)); } break; case E_UNDERNEATH: if (isplayer(lf)) { if (errob) { getobname(errob, buf, 1); msg("Your %s is in the way!", noprefix(buf)); } else { msg("You will need to remove your outer armour first."); } } break; default: if (isplayer(lf)) { msg("For some reason, you cannot remove your %s!", noprefix(obname)); } // still take time taketime(lf, getactspeed(lf)); break; } return B_TRUE; } // remove the equipped flag taketime(lf, getactspeed(lf)); if (gamemode == GM_GAMESTARTED) { if (isplayer(lf)) { msg("You take off %s.", obname); statdirty = B_TRUE; } else if (cansee(player, lf)) { getlfname(lf, buf); capitalise(buf); msg("%s takes off %s.", buf, obname); } } makeunequipped(lf, o); return B_FALSE; } void takerotationtime(lifeform_t *lf) { // first rotation in a turn is free if (lf->rotated) { taketime(lf, getturnspeed(lf)); } else { lf->rotated = B_TRUE; } } void taketime(lifeform_t *lf, long howlong) { int db = B_FALSE; map_t *map; if (notime || lfhasflag(lf, F_NOTIME)) { return; } map = lf->cell->map; if (gamemode == GM_GAMESTARTED) { if (!enteringmap && (map != player->cell->map)) { // lfs not on the player's map don't take time. // this avoids the assertion below failing when // (for example) a monster falls through a pit. // // the exception is when we're simulating turns for // monsters on the destination level when a player // walks up/down stairs. in this case, "enteringmap" // will be set. return; } if (db && cansee(player, lf)) { dblog("lfid %d (%s) spending %d time\n",lf->id,lf->race->name, howlong); } } assert(howlong > 0); // inc timespent lf->timespent += howlong; assert(lf->timespent >= 0); // time-based effects on lifeforms (eg hp regeneration) go here... // TODO: decrement lifeform's (or their object's) temporary flags // if you don't have a map, start forgetting the dungeon if (isplayer(lf)) { if (!lfhasflag(lf, F_PHOTOMEM) && (getskill(lf, SK_CARTOGRAPHY) < PR_SKILLED)) { lf->forgettimer += ((float)howlong / 500.0); if (lf->forgettimer > 1) { int amt; // TODO: modify using race memory amt = (int)floor(lf->forgettimer); forgetcells(lf->cell->map, amt); lf->forgettimer -= amt; } } } // now move player up in linked list... sortlf(map, lf); } int throwat(lifeform_t *thrower, object_t *o, cell_t *where) { if (!hasbp(thrower, BP_HANDS)) { if (isplayer(thrower)) msg("You have no hands to throw with!"); return B_TRUE; } if (lfhasflag(thrower, F_RAGE)) { if (isplayer(thrower)) { msg("You are too enraged to throw anything!"); } return B_TRUE; } taketime(thrower, getactspeed(thrower)); return fireat(thrower, o, 1, where, getthrowspeed(thrower), NULL); } // lf effects which happen every xx ticks // IMPORTANT - don't call taketime() during this function. void timeeffectslf(lifeform_t *lf) { object_t *o, *nexto; flag_t *f,*nextf; int dir,b; // make SURE we don't take any time! notime = B_MAYBE; // decrement flags timeeffectsflags(lf->flags); // remove effects from expired poison foreach_bucket(b) { for (f = lf->flags->first[b] ; f; f = nextf) { nextf = f->next; if (f->lifetime == FROMPOISON) { if (!lfhasflagval(lf, F_POISONED, f->obfrom, NA, NA, NULL)) { killflag(f); } } } } if (lfhasflag(lf, F_INTERRUPTED)) { interrupt(lf); killflagsofid(lf->flags, F_INTERRUPTED); } if (isdead(lf)) { killflagsofid(lf->flags, F_NOTIME); notime = B_FALSE; return; } // revert to original form if a polymorph just expired if (lf->polyrevert) { enum RACE rid = R_NONE; race_t *r; flag_t *ff; // change back ff = lfhasflag(lf, F_ORIGRACE); if (ff) { rid = ff->val[0]; } else { rid = R_NONE; // should never happen! } r = findrace(rid); if (r) { setrace(lf, r->id, B_TRUE); } else { if (isplayer(lf)) { msg("For some reason, you are unable to revert to your original form!"); } } lf->polyrevert = B_FALSE; } // time effects on lifeform's objects for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; f = hasflag(o->flags, F_CREATEDBYSPELL); if (f && !hasactivespell(lf, f->val[0])) { killob(o); continue; } timeeffectsob(o); } // holes in the floor/roof for (dir = D_UP; dir <= D_DOWN; dir++) { int donesomething = B_TRUE; o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL); while (o && donesomething) { int willfall = B_FALSE; donesomething = B_FALSE; if ((dir == D_DOWN) && !isairborne(lf, NULL)) { object_t *amu; willfall = B_TRUE; amu = hasequippedobid(lf->pack, OT_AMU_EVOLUTION); if (amu) { if (!polymorphto(lf, R_AVIAD, 5)) { makeknown(amu->type->id); dospelleffects(lf, OT_S_FLIGHT, 1, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE, NULL); willfall = B_FALSE; } } } else if ((dir == D_UP) && lfhasflag(lf, F_LEVITATING)) { willfall = B_TRUE; } if (willfall) { usestairs(lf, o, B_FALSE, B_FALSE); taketime(lf, getactspeed(lf)); // to avoid infinte loops donesomething = B_TRUE; o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL); } } } notime = B_FALSE; } // returns TRUE if lf declines to teach. int tradeknowledge(lifeform_t *lf) { int poss[MAXCANDIDATES], fromplayer, toplayer; enum TRADEINFOTYPE tradetype[MAXCANDIDATES],fromplayertype, toplayertype; int nposs = 0,sel; char buf[BUFLEN],tradetext[BUFLEN],lfname[BUFLEN],ch; char fromplayertext[BUFLEN],toplayertext[BUFLEN]; getlfname(lf, lfname); // already traded? if (lfhasflag(lf, F_DONEKNOWLEDGETRADE)) { sayphrase(lf, SP_TRADEINFO_DECLINE_ALREADYDONE, SV_TALK, NA, NULL, player); return B_TRUE; } // does the player have a skill which lf needs? getteachableskills(player, lf, poss, tradetype, &nposs); if (nposs) { sel = rnd(0,nposs-1); fromplayer = poss[sel]; fromplayertype = tradetype[sel]; } else { sayphrase(lf, SP_TRADEINFO_DECLINE_PLAYERBAD, SV_TALK, NA, NULL, player); return B_TRUE; } // does lf have a skill which the player needs? getteachableskills(lf, player, poss, tradetype, &nposs); if (nposs) { if (getskill(player, SK_SPEECH) >= PR_MASTER) { int i; // you can pick which one to learn. snprintf(buf, BUFLEN, "What would you like to learn from %s?",lfname); initprompt(&prompt, buf); ch = 'a'; for (i = 0; i < nposs; i++) { gettradeinfoname(poss[i], tradetype[i], buf); addchoice(&prompt, ch++, buf, NULL, NULL, NULL); } addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); ch = getchoice(&prompt); if (ch == '-') { sayphrase(lf, SP_TRADEINFO_CANCEL, SV_TALK, NA, NULL, player); return B_TRUE; } else { sel = ch - 'a'; } } else { sel = rnd(0,nposs-1); } toplayer = poss[sel]; toplayertype = tradetype[sel]; } else { sayphrase(lf, SP_TRADEINFO_DECLINE_OTHERBAD, SV_TALK, NA, NULL, player); return B_TRUE; } // at this point we can do a valid trade. make the offer. // encode skill/spell names into a single string of the form: // fromplayer^toplayer gettradeinfoname(fromplayer, fromplayertype, fromplayertext); gettradeinfoname(toplayer, toplayertype, toplayertext); snprintf(tradetext, BUFLEN, "%s^%s",fromplayertext,toplayertext); sayphrase(lf, SP_TRADEINFO_ACCEPT, SV_TALK, NA, tradetext, player); more(); // confirm. snprintf(buf, BUFLEN, "Learn %s from %s", toplayertext, lfname); ch = askchar(buf, "yn","y", B_TRUE, B_FALSE); if (ch == 'y') { // lf learns skill if (fromplayertype == TI_SKILL) { giveskill(lf, fromplayer); } else { // ie. spell snprintf(buf, BUFLEN, "pw:%d;", getspellpower(lf, fromplayer)); addflag(lf->flags, F_CANCAST, fromplayer, NA, NA, buf); } // player learns skill if (toplayertype == TI_SKILL) { giveskill(player, toplayer); } else { // ie. spell addflag(player->flags, F_CANCAST, toplayer, NA, NA, NULL); } // can't trade knowledge anymore addflag(lf->flags, F_DONEKNOWLEDGETRADE, B_TRUE, NA, NA, NULL); } return B_FALSE; } // return B_TRUE on failure. int tryclimb(lifeform_t *lf, cell_t *where, char *towhat, int onpurpose) { // if you have a rope or there's an adjacent wall, you can try // to climb up int adjwalls; char lfname[BUFLEN]; // climbing without climb skill? if (isplayer(lf) && onpurpose && !getskill(lf, SK_CLIMBING) && !lfhasflag(lf, F_SPIDERCLIMB) && !hasobwithflag(lf->pack, F_HELPSCLIMB)) { // you are about to do something foolish! if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { if (!warnabout(TEXT_WARN_CLIMB)) { return B_TRUE; } } } getlfname(lf, lfname); adjwalls = countadjwalls(where); if (adjwalls || hasobwithflag(lf->pack, F_HELPSCLIMB)) { if (isplayer(lf)) { msg("You start climbing..."); } else if (cansee(player, lf)) { msg("%s starts climbing...", lfname); } taketime(lf, getactspeed(lf)); // base difficulty of 20 if (skillcheck(lf, SC_CLIMB, getcellclimbdifficultyavg(where), 0)) { // you made it! if (isplayer(lf)) { msg("You reach %s.", towhat); } else if (cansee(player, lf)) { msg("%s reaches %s.", towhat); } // train climbing if (!lfhasflag(lf, F_SPIDERCLIMB)) { practice(lf, SK_CLIMBING, 1); } // continue... } else { // you fall. if (isplayer(lf)) { msg("You fall to the ground!"); } else if (cansee(player, lf)) { msg("%s falls to the ground!", lfname); } fall(lf, NULL, B_FALSE); // this will take some time. losehp(lf, roll("1d6"), DT_FALL, NULL, "a fall while climbing"); return B_TRUE; } } else { // no rope or adjacent walls if (isplayer(lf)) { if (onpurpose) { msg("You can't reach the roof!"); } else { // ie. you were fleeing. msg("You try to climb upwards, but can't reach the roof!"); } } return B_TRUE; } // success return B_FALSE; } int touch(lifeform_t *lf, object_t *o) { return real_touch(lf, o, B_ONPURPOSE); } // returns B_TRUE if the action which involved touching this should fail // // onpurpose = true means that we are actively touching the object with our hands (ie // picking it up). // // onpurpose = false means that we have inadvertantly touched the object, maybe not // with our hands (ie. we're constantl 'touching' armour while // wearing it.) int real_touch(lifeform_t *lf, object_t *o, int onpurpose) { flag_t *f; flag_t *retflag[MAXCANDIDATES]; int nretflags,i; char buf[BUFLEN]; char obname[BUFLEN]; char lfname[BUFLEN]; object_t *gloves; if ((gamemode != GM_GAMESTARTED)) return B_FALSE; touch_battle_spoils(o); getlfname(lf, lfname); getobname(o, obname, o->amt); // freezing touch? f = hasflag(lf->flags, F_FREEZINGTOUCH); if (f) { // not wearing gloves? if (!getouterequippedob(lf, BP_HANDS)) { // default power of 4 dospelleffects(lf, OT_S_FREEZEOB, 4, NULL, o, NULL, B_UNCURSED, NULL, B_FALSE, NULL); // we use val[0] here rather than timeleft, because we don't // want to decrement it each turn. f->val[0]--; if (f->val[0] <= 0) { if (isplayer(lf)) { msg("Your hands stop glowing blue."); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s's hands stop glowing blue."); } killflag(f); } } } gloves = getouterequippedob(lf, BP_HANDS); // undead and blessed objects? if (isundead(lf) && isblessed(o)) { int safe = B_FALSE; if (gloves) { if (onpurpose) { // okay. safe = B_TRUE; } else { if (isweaponbp(getequiploc(o))) { safe = B_TRUE; } } } if (!safe) { if (isplayer(lf)) { msg("^b%s %s burn%s you as you touch %s!",isequipped(o) ? "Your" : "The", noprefix(obname), OBS1(o), OB1(o, "it", "them") ) ; o->blessknown = B_TRUE; } else if (cansee(player, lf)) { msg("%s touches %s then recoils in pain!",lfname, obname); o->blessknown = B_TRUE; } // use real name here... real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); snprintf(buf, BUFLEN, "touching %s",obname); losehp(lf, 2, DT_HOLY, NULL, buf); // drop the object if we're holding it if ((o->pile->owner == lf) && !isequipped(o)) { drop(o, ALL); } return B_TRUE; } } if (isedible(o)) { int willrot = B_FALSE; if (isplayer(lf) && godprayedto(R_GODNATURE) && godisangry(R_GODNATURE) ) { willrot = B_TRUE; } else if (lf->race->id == R_WRAITHBOG) { willrot = B_TRUE; } if (willrot) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("A green miasma surrounds %s as %s touch%s %s.", obname, lfname, isplayer(lf) ? "" : "es", (o->amt == 1) ? "it" : "them"); if (!hasflag(o->flags, F_TAINTED)) { addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL); } return B_TRUE; } } f = hasflag(o->flags, F_SHARP); if (f && !gloves) { if (isplayer(lf)) { msg("^bOw! You cut your finger on %s.", obname); } snprintf(buf, BUFLEN, "touching %s", obname); losehp(lf, rnd(1,2), DT_SLASH, NULL, buf); bleed(lf, B_FALSE); // drop the object if we're holding it if ((o->pile->owner == lf) && !isequipped(o)) { drop(o, ALL); } return B_TRUE; } f = (lfhasflagval(lf, F_MATVULN, o->material->id, NA, NA, NULL)); if (f && (f->val[2] > 0) && !gloves) { if (isplayer(lf)) { msg("^%cThe %s%s %s burns you!", getlfcol(lf, CC_BAD), obname, getpossessive(obname), o->material->name); } else if (cansee(player, lf)) { msg("^%cThe %s%s %s burns %s!", getlfcol(lf, CC_BAD), obname, getpossessive(obname), o->material->name); } snprintf(buf, BUFLEN, "the touch of %s", o->material->name); losehp(lf, f->val[2], DT_DIRECT, NULL, buf); return B_TRUE; } /* if (isequipped(o) && !isweapon(o) && !isimmuneto(lf->flags, DT_FIRE, B_FALSE)) { f = hasflag(o->flags, F_HOT); if (f) { f->known = B_TRUE; if (isplayer(lf)) { getobname(o, buf, 1); msg("^BYour %s burns you!^n", noprefix(buf)); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); getobname(o, buf, 1); msg("^B%s%s %s burns it!^n", lfname, getpossessive(lfname), noprefix(buf)); } losehp_real(lf, f->val[0], DT_HEAT, NULL, buf, B_TRUE, o, B_FALSE); } } */ // flaming or red-hot objects? if (!isimmuneto(lf->flags, DT_FIRE, B_FALSE)) { // only check for DT_FIRE, no such thing as 'heat resistance' int resistant = B_FALSE,equipped = B_FALSE; if (isresistantto(lf->flags, DT_FIRE, B_FALSE)) { resistant = B_TRUE; } // if 'onpurpose = false' and it's equipped, then really the object it touching us. if (isequipped(o) && (o->pile->owner == lf) && !onpurpose) { equipped = B_TRUE; } getflags(o->flags, retflag, &nretflags, F_ONFIRE, F_HOT, F_NONE); // IMPORTANT - check ONFIRE first! for (i = 0; i < nretflags; i++) { f = retflag[i]; // flaming weapons are ok - only the blade is burning if ((f->id == F_ONFIRE) && isweapon(o)) { } else if ((f->id == F_HOT) && resistant) { // fire resistance will stop F_HOT, but not F_ONFIRE. } else { // touching something onfire while wearing gloves? they get damaged. if (gloves && onpurpose) { if (f->id == F_ONFIRE) { // fire will damage your gloves, just being hot will not. takedamage(gloves, 2, DT_FIRE, NULL); if (hasflag(gloves->flags, F_DEAD)) { gloves = NULL; } else { // if your gloves weren't destroyed the fire // will go out. killflagsofid(o->flags, F_ONFIRE); } } } else { enum DAMTYPE dt = DT_HEAT; int dam = 3; // otherwise YOU get damaged. f->known = B_TRUE; getobname(o, obname, o->amt); // get name again after making flag known if (isplayer(lf)) { if (equipped) { char burnbuf[BUFLEN]; snprintf(burnbuf, BUFLEN, "%s burns you!", obname); capitalise(burnbuf); msg("^b%s",burnbuf); } else { msg("^bOw! You burn your hands on %s.",obname); } } else if (cansee(player, lf)) { if (equipped) { msg("%s%s %s burns %s!", lfname, getpossessive(lfname), noprefix(obname), it(lf)); } else { msg("%s burns %sself on %s.", lfname, it(lf), obname); } } snprintf(buf, BUFLEN, "touching %s",obname); if (f->id == F_ONFIRE) { dt = DT_FIRE; dam = rnd(2,4); } else if (f->id == F_HOT) { dt = DT_HEAT; dam = f->val[0]; } losehp(lf, dam, dt, NULL, buf); // drop the object if it's an equipped weapon/shield if ((o->pile->owner == lf) && isweaponbp(getequiploc(o))) { drop(o, ALL); } return B_TRUE; } } } } // end if !immune to fire return B_FALSE; } lifeform_t *reveal_pretendob(object_t *o) { enum RACE rid; flag_t *f; cell_t *c; lifeform_t *lf; c = getoblocation(o); f = hasflag(o->flags, F_ISMONSTER); assert(f); rid = f->val[0]; addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); lf = addmonster(c, rid, NULL, B_TRUE, 1, B_FALSE, B_FALSE, NULL); if (lf) { f = lfhasflag(lf, F_PRETENDSTOBE); if (f) { char obname[BUFLEN]; // replace text getobnametruebase(o, obname, 1); free(f->text); f->text = strdup(obname); } } return lf; } void turntoface(lifeform_t *lf, cell_t *dstcell) { if (isdead(lf)) return; if (lfhasflag(lf, F_AUTOROTATE)) return; if (dstcell == lf->cell) return; // not providing srclf, since this will make getdirtowards() not include // directions in which the next cell is unwalkable. in this case we're // not actually walking there, so we don't care. setfacing(lf, getdirtowards(lf->cell, dstcell, NULL, B_FALSE, DT_ORTH) ); } void unequipeffects(lifeform_t *lf, object_t *o) { flag_t *f; // lose flags loseobflags(lf, o, F_EQUIPCONFER); if (obproduceslight(o)) { //calclight((getoblocation(o))->map); setlosdirty(lf); //precalclos(lf); } if (o->type->id == OT_ENERGYBLADE) { stopspell(lf, OT_S_SUMMONWEAPON); // object might be dead now, so stop. return; } f = hasflag(o->flags, F_CREATEDBYSPELL); if (f) { stopspell(lf, f->val[0]); } if ((o->type->id == OT_AMU_SWIMMING) && (lf->race->id == R_SWAN) && ispolymorphed(lf)) { // revert to normal form abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL); } if (o->type->id == OT_AMU_TRAVEL) { map_t *newmap = NULL; cell_t *newcell = NULL; f = hasflag(o->flags, F_ORIGMAP); if (f) { // should always be true... newmap = findmap(f->val[0]); newcell = getcellat(newmap, f->val[1], f->val[2]); } if (newcell) { // go there! teleportto(lf, newcell, B_FALSE); } makeknown(o->type->id); } } void unsummon(lifeform_t *lf, int vanishobs) { lifeform_t *creator = NULL; flag_t *f; char unsummonob[BUFLEN]; f = hasflag(lf->flags, F_SUMMONEDBY); if (f) { creator = findlf(NULL, f->val[0]); } if (cansee(player, lf)) { char lfname[BUFLEN]; int doyour = B_FALSE; if (creator && (creator == player)) { if (!hasflag(lf->flags, F_UNIQUE)) { doyour = B_TRUE; } } getlfname(lf, lfname); msg("%s%s vanishes.", doyour ? "Your " : "", doyour ? noprefix(lfname) : lfname); } if (vanishobs) { // all objects vanish while (lf->pack->first) { killob(lf->pack->first); } } lf->hp = 0; addflag(lf->flags, F_DEAD, B_TRUE, NA, NA, NULL); addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); addflag(lf->flags, F_NODEATHSPEECH, B_TRUE, NA, NA, NULL); addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); f = lfhasflag(lf, F_UNSUMMONOB); if (f && strlen(f->text)) { strcpy(unsummonob, f->text); } else { strcpy(unsummonob, "puff of smoke"); } if (strlen(unsummonob)) { addob(lf->cell->obpile, unsummonob); } } int unweild(lifeform_t *lf, object_t *o) { char obname[BUFLEN]; char buf[BUFLEN]; object_t *errob; getobname(o, obname, 1); if (lfhasflag(lf, F_RAGE)) { if (isplayer(lf)) msg("You are too enraged to unweild a weapon!"); return B_TRUE; } if (!cantakeoff(lf, o, &errob)) { switch (reason) { case E_CURSED: if (isplayer(lf)) { msg("Your %s appears to be stuck to your hand!", noprefix(obname)); if (!o->blessknown) { o->blessknown = B_TRUE; } } break; case E_NOTEQUIPPED: if (isplayer(lf)) { msg("You are not weilding that!"); } break; case E_UNDERNEATH: if (isplayer(lf)) { if (errob) { getobname(errob, buf, 1); msg("Your %s is in the way!", noprefix(buf)); } else { msg("You will need to remove your outer armour first."); } } break; default: if (isplayer(lf)) { msg("For some reason, you cannot take off your %s!", noprefix(obname)); } break; } return B_TRUE; } // unweilding doesn't take any time if (gamemode == GM_GAMESTARTED) { if (isplayer(lf)) { msg("You are no longer weilding %s.", obname); } else if (cansee(player, lf)) { getlfname(lf, buf); capitalise(buf); msg("%s stops weilding %s.", buf, obname); } } makeunequipped(lf, o); return B_FALSE; } int useability(lifeform_t *lf, enum OBTYPE aid, lifeform_t *who, cell_t *where) { int rv; flag_t *cwflag; if (!cancast(lf, aid, NULL)) { if (isplayer(lf)) { // announce switch (reason) { case E_NEEDGRAB: msg("You need to hold someone before using this ability."); break; case E_NOTREADY: msg("This ability is not recharged yet."); break; case E_NOSTAM: msg("You are too tired to do that."); break; case E_PRONE: msg("You can't use abilities while prone."); break; case E_STUNNED: msg("You can't use abilities while stunned."); break; default: msg("For some reason, you can't use this ability."); break; } } return B_TRUE; } /* Duplicate check. stamcost = getstamcost(lf, aid); if (stamcost) { if (getstamina(lf) < stamcost) { if (isplayer(lf)) { msg("You are too tired to do that right now."); } return B_TRUE; } } */ if (aid != OT_A_FULLSHIELD) { killflagsofid(lf->flags, F_FULLSHIELD); } // taketime() will happen during abiltiyeffects() // use the ability cwflag = lfhasflagval(lf, F_CANWILL, aid, NA, NA, NULL); rv = abilityeffects(lf, aid, where, who, cwflag); // remember ability for rePeat command if (isplayer(lf)) { modflag(lf->flags, F_LASTSPELL, aid, NA, NA, NULL); } if (!rv) { flag_t *f; objecttype_t *ot; int stamcost = 0; // success - charge stamina ot = findot(aid); stamcost = getstamcost(lf, aid); if (stamcost) { modstamina(lf, -stamcost); } f = lfhasflagval(lf, F_DIEAFTERUSING, aid, NA, NA, NULL); if (f) { lf->hp = 0; setlastdam(lf, f->text); } } return rv; } int usestairs(lifeform_t *lf, object_t *o, int onpurpose, int climb) { flag_t *f; map_t *curmap; map_t *newmap; cell_t *obcell; cell_t *newcell; int dir; int newdepth = 0; // should always be replaced char lfname[BUFLEN]; char obname[BUFLEN]; int isportal = B_FALSE; lifeform_t *adjally[MAXFOLLOWLFS]; int seen[MAXFOLLOWLFS]; int nadjallies = 0; int falling = B_FALSE; int madenewmap = B_FALSE; region_t *newregion = NULL; // need up update 'dlev:' if (isplayer(lf)) { statdirty = B_TRUE; } getlfname(lf, lfname); getobname(o, obname, 1); obcell = getoblocation(o); if (initiatemove(lf, NULL, onpurpose, NULL)) { // failed? return B_FALSE; } // locked? if (hasflag(o->flags, F_LOCKED)) { if (isplayer(lf)) { msg("The %s seems to be locked.", noprefix(obname)); if (o->type->id == OT_VSTAIRSDOWN) { object_t *key; key = hasobwithflag(lf->pack, F_VAULTKEY); if (key) { char ques[BUFLEN],obname[BUFLEN],ch; getobname(key, obname, 1); snprintf(ques, BUFLEN,"Use your %s to open it?", noprefix(obname)); ch = askchar(ques,"yn","n", B_TRUE, B_FALSE); if (ch == 'y') { operate(lf, key, lf->cell); } } } } if (onpurpose) taketime(lf, getmovespeed(lf)); return B_TRUE; } if (hasflag(o->flags, F_PORTAL)) { f = hasflag(o->flags, F_MAPLINK); if (!f) { int wantdepth; object_t *dstportal; wantdepth = rnd(1,lf->cell->map->region->rtype->maxdepth); // generate random destination portal dstportal = linkportaltodepth(o, wantdepth); if (dstportal) { flag_t *newf; // if source portal is temporary, make the // destination portal temporary as well. if (hasflagval(o->flags, F_OBHPDRAIN, NA, DT_DIRECT, NA, NULL)) { newf = hasflag(o->flags, F_OBHP); if (newf) { maketemporary(dstportal, newf->val[0], "vanishes"); } } // now update 'f' since 'o' should now have F_MAPLINK. f = hasflag(o->flags, F_MAPLINK); } else { // failed to create destinaiton. if (isplayer(lf)) msg("The portal doesn't seem to take you anywhere."); if (onpurpose) taketime(lf, getmovespeed(lf)); return B_TRUE; } } assert(f); if (f->val[0] != NA) { map_t *m; m = findmap(f->val[0]); if (m && (m->habitat->id == H_HEAVEN)) { if (!hasobofclass(lf->pack, OC_GODSTONE)) { if (isplayer(lf)) msg("The portal doesn't seem to take you anywhere."); if (onpurpose) taketime(lf, getmovespeed(lf)); return B_TRUE; } } } } curmap = obcell->map; if ((o->type->id == OT_GRATINGFLOOR) && !hasflag(o->flags, F_MAPLINK)) { createbranchlink(curmap, obcell, o, NULL, BH_SEWER, curmap->region); } f = hasflag(o->flags, F_CLIMBABLE); assert(f); dir = f->val[0]; if (f->val[1] == NA) { // use same region newregion = obcell->map->region; } else { newregion = findregion(f->val[1]); } // depth of new level? if (dir == D_UP) { newdepth = curmap->depth - 1; isportal = B_FALSE; } else if (dir == D_DOWN) { newdepth = curmap->depth + 1; isportal = B_FALSE; } else { // portal! isportal = B_TRUE; } if (!onpurpose) { falling = B_TRUE; } // check... if (dir == D_DOWN) { if (lfhasflag(lf, F_LEVITATING)) { if (isplayer(lf)) { msg("You can't reach the ground from up here!"); } return B_TRUE; } } else if (dir == D_UP) { if (isinroof(o)) { if (canreachroof(lf)) { // ok. } else { if (asktoclimb(o, "Climb up %s")) { // failed return B_TRUE; } else { // success climb = B_TRUE; } } } } // announce curs_set(1); if (isportal) { if (isplayer(lf)) { msg("You enter the %s...", f->text); // move cursor to msgwindow while we create the new level... wrefresh(msgwin); } else if (cansee(player, lf)) { msg("%s enters the %s...", lfname, f->text); } } else if (hasflag(o->flags, F_PIT) || (o->type->id == OT_GRATINGFLOOR) || (o->type->id == OT_GRATINGROOF)) { //f = hasflag(o->flags, F_PIT); if (isplayer(lf) || cansee(player, lf)) { msg("%s %s %s the %s...", lfname, getpitverb(lf, dir,onpurpose, climb), getdirname(dir), noprefix(obname)); // move cursor to msgwindow while we create the new level... if (isplayer(lf)) wrefresh(msgwin); } } else { if (isplayer(lf)) { msg("You %s %s the %s...", getmoveverb(lf), getdirname(dir), f->text); // move cursor to msgwindow while we create the new level... wrefresh(msgwin); } else if (cansee(player, lf)) { char buf[BUFLEN]; getmoveverbother(lf, buf); msg("%s %s %s the %s.", lfname, buf, getdirname(dir), f->text); } } // find adjacent allies or enemies which will follow you // (getwhowillfollow will handle following through pits) if (isplayer(lf)) { int n; getwhowillfollow(lf, o, adjally, seen, &nadjallies); for (n = 0; n < nadjallies; n++) { if (seen[n]) { char lname[BUFLEN]; real_getlfname(adjally[n], lname, NULL, B_NOSHOWALL, B_CURRACE); msg("%s follows you.", lname); } } } // do stairs go somewhere? generate new maps if required. newcell = getstairdestination(o, &madenewmap); if (newcell) { newmap = newcell->map; } else { if (isportal) { // unconnected portal - make a destination. if (curmap->depth < MAXDEPTH) { newdepth = rnd(curmap->depth, curmap->depth + 5); limit(&newdepth, curmap->depth, MAXDEPTH); linkportaltodepth(o, newdepth); newcell = getstairdestination(o, &madenewmap); } if (!newcell) { if (isplayer(lf)) msg("This portal doesn't seem to go anywhere."); } } } curs_set(0); if (newcell) { int n; // if we just climbed up through a hole, and are not flying, we want to // end up adjacent to the hole in the ground. otherwise we'll just fall // straight back down! if (hasflag(o->flags, F_PIT) && (dir == D_UP) && !isairborne(lf, NULL)) { cell_t *noholecell; noholecell = real_getrandomadjcell(newcell, &ccwalkable, B_ALLOWEXPAND, LOF_NEED, NULL, NULL ); if (noholecell) { // go here instead newcell = noholecell; } else { // alert if (isplayer(lf)) { msg("You can't find anywhere safe to get out."); } } } // check noone is in the way if (movelfsoutofway(newcell) || !cellwalkable(lf, newcell, NULL)) { // TODO: handle this differently - ie always allow the player // go there? if (isplayer(lf)) msg("The %s seems to be blocked.",f->text); return B_TRUE; } // announce if (isplayer(lf)) { announcearrival(lf, newcell->map); f = hasflag(o->flags, F_MAPLINK); if (f) f->known = B_KNOWN; } // move player to new map moveto(lf, newcell, onpurpose, B_TRUE); // take time if ((dir == D_UP) && !isairborne(lf, NULL)) { stopsprinting(lf); // you can sprint down stairs, but not up if (onpurpose) taketime(lf, getmovespeed(lf)*2); // takes longer to climb } else { if (onpurpose) taketime(lf, getmovespeed(lf)); } // move adjacent allies/monsters too for (n = 0; n < nadjallies; n++) { cell_t *c; c = getrandomadjcell(newcell, &ccwalkable, B_ALLOWEXPAND); if (c) { if (!initiatemove(adjally[n], NULL, B_TRUE, NULL)) { int climbtime; stopsprinting(adjally[n]); movelf(adjally[n], c, B_TRUE); climbtime = getmovespeed(adjally[n]); if ((dir == D_UP) && !isairborne(adjally[n], NULL)) { climbtime *= 2; } if (onpurpose) taketime(adjally[n], climbtime); turntoface(adjally[n], newcell); } } } } else { dblog("ERROR - can't find opposite end of stairs/portal!"); msg("ERROR - can't find opposite end of stairs/portal!"); return B_TRUE; } if (falling && (dir == D_DOWN) && madenewmap && (newcell->map->region->rtype->id == BH_PIT)) { // you just dug downwards and made a big hole, so you // didn't actually fall. falling = B_FALSE; } if (falling) { if (dir == D_DOWN) { if (hasobwithflagval(lf->cell->obpile, F_PIT, D_DOWN, NA, NA, NULL)) { flag_t *ff; // inc fall distance ff = lfhasflag(lf, F_FALLDISTANCE); if (ff) { ff->val[0]++; } else { addflag(lf->flags, F_FALLDISTANCE, 1, NA, NA, NULL); } } else { int howfar; if (isplayer(lf)) { if (isimmuneto(lf->flags, DT_FALL, B_FALSE)) { msg("You land gently on the ground."); } else { msg("^bYou slam into the ground!"); } } else if (cansee(player, lf)){ if (isimmuneto(lf->flags, DT_FALL, B_FALSE)) { msg("%s lands gently on the ground.", lfname); } else { msg("^%c%s slams into the ground!", getlfcol(lf, CC_BAD), lfname); } } // how far did you fall? sumflags(lf->flags, F_FALLDISTANCE, &howfar, NULL, NULL); howfar++; // take fall damage. 2d6 per level. losehp(lf, rolldie(howfar*2, 6), DT_FALL, NULL, "falling"); killflagsofid(lf->flags, F_FALLDISTANCE); if (!isimmuneto(lf->flags, DT_FALL, B_FALSE)) { // injure legs injure(lf, BP_LEGS, DT_BASH, IJ_NONE); // fall over fall(lf, NULL, B_FALSE); } } } else if (dir == D_UP) { if (hasobwithflagval(lf->cell->obpile, F_PIT, D_UP, NA, NA, NULL)) { flag_t *ff; // inc fall distance ff = lfhasflag(lf, F_FALLDISTANCE); if (ff) { ff->val[0]++; } else { addflag(lf->flags, F_FALLDISTANCE, 1, NA, NA, NULL); } } else if (newcell->map->region->rtype->id != BH_WORLDMAP) { int howfar; if (isplayer(lf)) { msg("^bYou slam into the roof!"); } else if (cansee(player, lf)){ msg("^%c%s slams into the roof!",getlfcol(lf, CC_BAD), lfname); } // how far did you fall? sumflags(lf->flags, F_FALLDISTANCE, &howfar, NULL, NULL); howfar++; // take hitting roof damage (less than floor). 1d4 per level. losehp(lf, rolldie(howfar, 4), DT_FALL, NULL, "slamming into the roof"); killflagsofid(lf->flags, F_FALLDISTANCE); } } } /* if (isplayer(lf)) { statdirty = B_TRUE; needredraw = B_TRUE; calclight(lf->cell->map); setlosdirty(lf); //precalclos(lf); drawscreen(); } */ return B_FALSE; } // use a ring of miracles (or godstone of mercy) if we have one, and drain 'charges' // charges from it. if expired, ring of miracles will vanish. // returns B_TRUE if we found one. int useringofmiracles(lifeform_t *lf, int charges) { object_t *o; char lfname[BUFLEN]; if (lf->lastdamtype == DT_DIRECT) { return B_FALSE; } getlfname(lf, lfname); for (o = lf->pack->first ; o ; o = o->next) { int doit = B_FALSE; // use ring of miracles first if ( (o->type->id == OT_RING_MIRACLES) && isequipped(o) && getcharges(o) ) { doit = B_TRUE; } else if ((o->type->id == OT_GODSTONE_MERCY) && isfullycharged(o)) { doit = B_TRUE; } if (doit) { char obname[BUFLEN]; getobname(o, obname, 1); if (isplayer(lf) || cansee(player, lf)) { if (o->type->id == OT_RING_MIRACLES) { msg("%s%s %s flashes!", lfname, getpossessive(lfname), noprefix(obname)); } else { msg("%s flashes!", obname); } } // you know know the obejct makeknown(o->type->id); getobname(o, obname, 1); // refresh its name in case you didnt know what it was. // use a charge if (o->type->id == OT_RING_MIRACLES) { if (usecharge(o) <= 0) { if (isplayer(lf) || cansee(player, lf)) { msg("%s%s %s crumbles to dust.", lfname, getpossessive(lfname), noprefix(obname)); } removeob(o, ALL); } } else { // ie. godstone usecharges(o, getcharges(o)); } return B_TRUE; } } return B_FALSE; } int validateraces(void) { int goterror = B_FALSE; race_t *r; flag_t *f; skill_t *sk; job_t *j; int i,b; cell_t fakecell; map_t fakemap; flag_t *retflag[MAXCANDIDATES]; int nretflags; redrawpause(); // generate xp list genxplist(); createfakes(&fakemap, &fakecell); for (r = firstrace ; r ; r = r->next) { lifeform_t *lf; int thishd; enum ATTRIB a; // add a fake lf lf = addlf(&fakecell, r->id, 1); givestartskills(lf, lf->flags); // remember max. hitdice for use in dumpmonsters() thishd = gettr(lf); if (thishd > maxmonhitdice) { maxmonhitdice = thishd; } // all races must have fully defined stats for (a = 0 ; a < MAXATTS; a++) { if (!hasflagval(r->flags, F_STARTATT, a, NA, NA, NULL)) { printf("ERROR in race '%s' - race has no f_startatt %s\n", r->name, getattrname(a)); goterror = B_TRUE; } } // duplicate startatt flags? if (countflagsofid(r->flags, F_STARTATT) != MAXATTS) { printf("ERROR in race '%s' - duplicate f_startatt flags detected.\n", r->name); goterror = B_TRUE; } if (strstr(r->desc, "wings") && !hasbp(lf, BP_WINGS)) { printf("ERROR in race '%s' - description refers to wings but race has no bp_wings.\n", r->name); goterror = B_TRUE; } if (!hasflag(r->flags, F_SIZE)) { printf("ERROR in race '%s' - missing F_SIZE.\n", r->name); goterror = B_TRUE; } if (!hasflag(r->flags, F_TR)) { printf("ERROR in race '%s' - missing F_TR (threat level).\n", r->name); goterror = B_TRUE; } if (hasflagval(r->flags, F_CANWILL, OT_A_FLY, NA, NA, NULL)) { if (!getskill(lf,SK_FLIGHT)) { printf("ERROR in race '%s' - has flight but no flight skill.\n", r->name); goterror = B_TRUE; } } if (hasflag(r->flags, F_CANCAST) && !hasflag(r->flags, F_CASTWITHOUTIQ)) { if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= IQ_ANIMAL) { printf("ERROR in race '%s' - has F_CANCAST but iq is too low. might need f_castwithoutiq?\n", r->name); goterror = B_TRUE; } } if (hasflag(r->flags, F_NOCORPSE) && hasflag(r->flags, F_CORPSETYPE)) { printf("ERROR in race '%s' - has both F_NOCORPSE and F_CORPSETYPE.\n", r->name); goterror = B_TRUE; } foreach_bucket(b) { for (f = r->flags->first[b] ; f ; f = f->next) { if (f->id == F_RNDSPELLCOUNT) { if (!hasflag(r->flags, F_RNDSPELLSCHOOL) && !hasflag(r->flags, F_RNDSPELLPOSS)) { printf("ERROR in race '%s' - F_RNDSPELLCOUNT but no spell candidates.\n", r->name); goterror = B_TRUE; } } if (f->id == F_FILLPOT) { flag_t *f2; if (!findot(f->val[0])) { printf("ERROR in race '%s' - F_FILLPOT with bad object.\n", r->name); goterror = B_TRUE; } f2 = hasflag(r->flags, F_BLOODOB); if (f2 && !strlen(f2->text)) { printf("ERROR in race '%s' - has F_FILLPOT but no bleed object.\n", r->name); goterror = B_TRUE; } } if (f->id == F_HASATTACK) { if (!findot(f->val[0])) { printf("ERROR in race '%s' - F_HASATTACK with bad object.\n", r->name); goterror = B_TRUE; } if (f->val[1] == NA) { printf("ERROR in race '%s' - F_HASATTACK with no DR\n", r->name); goterror = B_TRUE; } } else if (f->id == F_FLYING) { if (!hasflagval(r->flags, F_CANWILL, OT_S_FLIGHT, NA, NA, NULL)) { printf("ERROR in race '%s' - has F_FLYING but can't use FLIGHT ability.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_NOFLEE) { if (lfhasflag(lf, F_FLEEONHPPCT)) { printf("ERROR in race '%s' - has both F_NOFLEE and F_FLEEONHPPCT.\n", r->name); goterror = B_TRUE; } if (lfhasflag(lf, F_FLEEONDAM)) { printf("ERROR in race '%s' - has both F_NOFLEE and F_FLEEONDAM.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_STARTOB) { if (!f->text || (strlen(f->text) == 0)) { printf("ERROR in race '%s' - F_STARTOB with zero length text.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_MAXATTACKS) { for (i = 0; i <= 1; i++) { if (f->val[i] < 0) { printf("ERROR in race '%s' - F_MAXATTACKS with v%d < 0.\n", r->name, i); goterror = B_TRUE; } } } else if (f->id == F_HITCONFER) { if (!lfhasflag(lf, F_HITCONFERVALS)) { printf("ERROR in race '%s' - F_HITCONFER, but no HITCONFERVALS defined.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_STARTATT) { if (strlen(f->text) && (f->val[1] != NA)) { printf("ERROR in race '%s' - F_STARTATT has both text range and val1.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_NOISETEXT) { if (f->val[0] == N_FLY) { if (!hasflag(r->flags, F_NATURALFLIGHT) && !hasflag(r->flags, F_LEVITATING)) { printf("ERROR in race '%s' - has NOISETEXT N_FLY but isn't flying.\n", r->name); goterror = B_TRUE; } } if (f->val[1] == NA) { printf("ERROR in race '%s' - has NOISETEXT but no volume.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_NOSLEEP) { if (hasflag(r->flags, F_NOCTURNAL) || hasflag(r->flags, F_DIURNAL) || hasflag(r->flags, F_STARTASLEEPPCT)) { printf("ERROR in race '%s' - has both NOSSLEEP and nocturnal/diurnal/startasleeppct.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_NOSMELL) { if (hasflag(r->flags, F_ENHANCESMELL)) { printf("ERROR in race '%s' - has both NOSMELL and ENHANCESMELL.\n", r->name); goterror = B_TRUE; } } else if (f->id == F_STARTOBWEPSK) { if (!findskill(f->val[1])) { printf("ERROR in race '%s' - STARTOBWEPSK with invalid weapon skill.\n", r->name); goterror = B_TRUE; } } } } // end foreach flag getflags(lf->flags, retflag, &nretflags, F_MORALE, F_NONE); if (nretflags > 1) { printf("ERROR in race '%s' - has multiple F_MORALE flags.\n", r->name); goterror = B_TRUE; } // xp check... calcxp(lf); // remove a fake lf killlf(lf); } i = 0; for (sk = firstskill ; sk ; sk = sk->next) { i++; } if (i >= MAXSKILLS) { printf("ERROR - MAXSKILLS is %d but found %d skills.\n",MAXSKILLS,i); goterror = B_TRUE; } for (j = firstjob ; j ; j = j->next) { f = hasflag(j->flags, F_HASPET); if (f) { race_t *r; r = findracebyname(f->text, NULL); if (!r) { printf("ERROR - job %s has unknown pet '%s'\n",j->name,f->text); goterror = B_TRUE; } } } for (i = 0; i < MAXSKILLS; i++) { if (i != SK_NONE) { if (!findskill(i)) { printf("ERROR - undefined skill %d\n",i); goterror = B_TRUE; } } } killfakes(&fakemap, &fakecell); redrawresume(); return goterror; } // returns TRUE on error int resizelf(lifeform_t *lf, enum LFSIZE newsize, int doobs) { flag_t *f; enum LFSIZE origsize,racesize = SZ_ANY; int changedir; char lfname[BUFLEN]; flag_t *reverting = NULL; int origstr,origmaxhp; object_t *o,*nexto; getlfname(lf, lfname); // already resized? reverting = lfhasflag(lf, F_SIZETIMER); if (reverting) { char buf[BUFLEN], *p; // override size. newsize = reverting->val[0]; doobs = reverting->val[2]; // will revert str/hp p = readuntil(buf, reverting->text, ','); origstr = atoi(buf); readuntil(buf, p, ','); origmaxhp = atoi(buf); } f = hasflag(lf->race->flags, F_SIZE); if (f) { racesize = f->val[0]; } origsize = getlfsize(lf); if (origsize == newsize) { return B_TRUE; } else if (newsize > origsize) { changedir = 1; } else { changedir = -1; } // actually do the resize f = lfhasflag(lf, F_SIZE); if (f) { f->val[0] = newsize; } else { addflag(lf->flags, F_SIZE, newsize, NA, NA, NULL); } // announce if (racesize == newsize) { if (isplayer(lf)) { msg("Your body returns to its regular size."); more(); } else if (cansee(player, lf)) { msg("%s returns to its regular size.", lfname); } } else { if (isplayer(lf)) { msg("Your body %s unnaturally!", (changedir == 1) ? "grows" : "shrinks"); more(); } else if (cansee(player, lf)) { msg("%s %s unnaturally!", lfname, (changedir == 1) ? "grows" : "shrinks"); } } // effects on objects, armour etc for (o = lf->pack->first ; o ; o = nexto) { nexto = o->next; if (isequipped(o)) { // object is now the wrong size? if (isarmour(o) && !armourfits(lf, o, NULL)) { if (doobs) { resizeobject(o, newsize); } else { char obname[BUFLEN]; getobname(o, obname, o->amt); if (isplayer(lf)) { msg("Your %s no longer fits!", noprefix(obname)); } else if (cansee(player, lf)) { msg("%s%s %s no longer fits!", lfname, getpossessive(lfname), noprefix(obname)); } killflagsofid(o->flags, F_EQUIPPED); unequipeffects(lf, o); if (isplayer(lf)) statdirty = B_TRUE; // might have impacted AR } } if (isweapon(o)) { flag_t *f; // wasn't twohanded before, but is now? if (istwohandedfor(o, lf)) { f = hasflag(o->flags, F_TWOHANDED); if ((f->val[0] > 0) && (origsize > f->val[0])) { if (isplayer(lf)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("Your %s %s now too heavy to weild in one hand!", noprefix(obname), (o->amt == 1) ? "is" : "are"); } // just drop it - this avoids complication if you have something in your // other hand. drop(o, o->amt); continue; } } else { // was twohanded before, but isn't now? f = hasflag(o->flags, F_TWOHANDED); if (f && ((f->val[0] < 0) || (origsize <= f->val[0]))) { flag_t *f2; if (isplayer(lf)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("You can now weild your %s in one hand!", noprefix(obname)); } f2 = hasflagval(o->flags, F_EQUIPPED, BP_SECWEAPON, NA, NA, NULL); if (f2) killflag(f2); } } } } // object is now too big to hold? if (!canpickup(lf, o, o->amt)) { if (reason == E_TOOBIG) { drop(o, o->amt); continue; } } } if (reverting) { // restore str etc. setattr(lf, A_STR, origstr); lf->maxhp = origmaxhp; limit(&(lf->hp), 0, lf->maxhp); if (isplayer(lf)) statdirty = B_TRUE; killflag(reverting); } // resizing an lf might change its glyph... if (cansee(player, lf) || isplayer(lf)) { needredraw = B_TRUE; } return B_FALSE; } lifeform_t *ressurect(object_t *o) { flag_t *f; race_t *r; lifeform_t *lf; cell_t *where; int level = 1; char obname[BUFLEN]; if (o->type->id != OT_CORPSE) return NULL; if (hasflag(o->flags, F_HEADLESS)) return NULL; f = hasflag(o->flags, F_CORPSEOF); if (f) { level = f->val[1]; } else { return NULL; } r = findrace(f->val[0]); if (!r) return NULL; where = getoblocation(o); getobname(o, obname, 1); if (!cellwalkable(NULL, where, NULL)) { where = getrandomadjcell(where, &ccwalkable, B_ALLOWEXPAND); } if (!where) return NULL; lf = addlf(where, r->id, level); // remove the corpse object removeob(o,o->amt); // redraw & announce if (haslos(player, where)) { needredraw = B_TRUE; drawscreen(); msg("%s is restored to life!", obname); } return lf; } int rest(lifeform_t *lf, int onpurpose) { flag_t *f; flag_t *ff; int healtime = 3; int wantclearmsg = B_TRUE; int mpheal = 1; int hpheal = 1; flag_t *rf; int training = B_FALSE; int resting = B_FALSE; object_t *restob = NULL; // special case if ((lf->race->id == R_GASCLOUD) && lfhasflagval(lf, F_ORIGRACE, R_VAMPIRE, NA, NA, NULL)) { if (hasob(lf->cell->obpile, OT_COFFIN)) { // restore original form. abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL); // restore full hp lf->hp = lf->maxhp; // fall asleep for a while fallasleep(lf, ST_ASLEEP, 50); // mark screen as dirty needredraw = B_TRUE; if (isplayer(lf)) { statdirty = B_TRUE; } return B_FALSE; } } rf = lfhasflag(lf, F_TRAINING); if (rf) { training = B_TRUE; } else { rf = isresting(lf); if (rf) { resting = B_TRUE; // ie. resting via 'R' restob = getrestob(lf); } } taketime(lf, getactspeed(lf)); if (!lfhasflag(lf, F_POISONED)) { // slowly heal hp/mp if (!training && !lfhasflag(lf, F_NORESTHEAL)) { ff = lfhasflag(lf, F_RESTHEALAMT); if (ff) { hpheal = ff->val[0]; } else { hpheal = 1; } ff = lfhasflag(lf, F_RESTHEALMPAMT); if (ff) { mpheal = ff->val[0]; } else { mpheal = 1; } if (onpurpose || !isplayer(lf)) { f = lfhasflag(lf, F_RESTCOUNT); if (!f) { f = addflag(lf->flags, F_RESTCOUNT, 0, NA, NA, NULL); } f->val[0]++; f->lifetime = 2; ff = lfhasflag(lf, F_RESTHEALTIME); if (ff) { healtime = ff->val[0]; } else { // 3 turns = heal 1 hp healtime = DEF_RESTHEALTIME; } // modify via restob if resting using 'R' if (restob) { if (rf->val[1] != NA) { healtime -= rf->val[1]; limit(&healtime, 1, NA); } } if (isplayer(lf) && godprayedto(R_GODLIFE) && godisangry(R_GODLIFE)) { enum PIETYLEV plev; plev = getpietylev(R_GODLIFE, NULL, NULL); if (plev <= PL_FURIOUS) { healtime = 0; // ie. never heal! } else { healtime *= 2; } } // hot? if (!isimmuneto(lf->flags, DT_FIRE, B_FALSE) && !isresistantto(lf->flags, DT_FIRE, B_FALSE)) { switch (getlftemp(lf)) { case T_HOT: healtime *= 2; break; case T_VHOT: healtime *= 3; break; default: break; } } if (f->val[0] >= healtime) { int difficulty; if (isplayer(lf)) { difficulty = 50; } else { // ai passes very easily difficulty = 25; } // modify difficulty if you're resting properly via 'R' if (resting && restob) { flag_t *f; f = hasflag(restob->flags, F_HELPSREST); if (f) { difficulty -= f->val[0]; limit(&difficulty, 0, NA); } } //if (isplayer(lf)) msg("hp given."); if (lf->hp < lf->maxhp) { // pass a skill check to regain hp if (skillcheck(lf, SC_CON, difficulty, getskill(lf, SK_FIRSTAID))) { gainhp(lf, hpheal); if (isplayer(lf) && godprayedto(R_GODLIFE)) { pleasegodmaybe(R_GODLIFE, 1); } practice(lf, SK_FIRSTAID, 1); } } if (lf->mp < getmaxmp(lf)) { // pass a skill check to regain mp if (skillcheck(lf, SC_IQ, difficulty, lf->level)) { gainmp(lf, mpheal); } } killflag(f); } } } if (training) { wantclearmsg = B_FALSE; rf->val[0]++; if (rf->val[0] >= rf->val[1]) { // ask about gaining skills if (isplayer(lf)) { msg("You finish training."); more(); } killflag(rf); // kill resting/training flag. enhanceskills(lf); } } else if (resting) { // just asleep/resting flag_t *hf; int fullpartyrest = B_FALSE; wantclearmsg = B_FALSE; // resting if (isfullyhealed(lf)) { killflagsofid(lf->flags, F_RESTUNTILBETTER); } hf = lfhasflag(lf, F_RESTUNTILALLIES); if (hf) { int moretogo = B_FALSE; lifeform_t *l; fullpartyrest = B_TRUE; for (l = lf->cell->map->lf ; l ; l = l->next) { if ((l != lf) && areallies(l, lf)) { if (!isfullyhealed(l)) { moretogo = B_TRUE; break; } } } if (!moretogo) { killflag(hf); } } if (!lfhasflag(lf, F_RESTUNTILBETTER) && !lfhasflag(lf, F_RESTUNTILALLIES)) { if (isplayer(lf)) { if (fullpartyrest) { msg("Your party has finished resting."); } else { msg("You finish resting."); } } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s finishes resting.",lfname); } if (isplayer(lf)) statdirty = B_TRUE; killflagsofid(lf->flags, F_TRAINING); rf = isresting(lf); if (rf) { // since you've woken up normally, turn your light source back on. if (strlen(rf->text)) { object_t *light; light = hasobid(lf->pack, atol(rf->text)); if (light) { turnon(lf, light); } } // kill sleeping flag killflag(rf); } wantclearmsg = B_FALSE; } } // allies should wake up once healed if (areallies(player, lf) && !isplayer(lf)) { if ((lf->hp >= lf->maxhp) && (lf->mp >= getmaxmp(lf)) && (getstamina(lf) >= getmaxstamina(lf))) { if (!hashealableinjuries(lf)) { killflagsofid(lf->flags, F_ASLEEP); } } } } // end if !poisoned if (isresting(lf)) { f = hasflag(lf->flags, F_RESTINGINMOTEL); if (f) { f->val[1]++; if (f->val[1] >= f->val[0]) { killflag(f); stopresting(lf); msg("\"Your check-out time has arrived!\""); wantclearmsg = B_FALSE; } } } if (statdirty || needredraw) { drawscreen(); } if (onpurpose && wantclearmsg) { if (isplayer(lf)) { // clear msg bar clearmsg(); } } return B_FALSE; } int wear(lifeform_t *lf, object_t *o) { int rv = B_FALSE,i; char buf[BUFLEN],obname[BUFLEN]; int preknown; flag_t *f; enum BODYPART possbp[MAXBODYPARTS]; int nparts = 0,ncheckparts = 0; enum BODYPART bp; flag_t *retflag[MAXCANDIDATES]; int nretflags; int showpos = B_FALSE; if (lfhasflag(lf, F_RAGE)) { if (isplayer(lf)) msg("You are too enraged!"); return B_TRUE; } if (o->amt > 1) { // eg. for melted wax in your ears o = splitob(o); } getobname(o, obname, 1); // check if it is already equipped first! f = hasflag(o->flags, F_EQUIPPED); if (f) { if (isplayer(lf)) { if (f->val[0] == BP_WEAPON) { msg("You're weilding that!"); } else { msg("You're already wearing that!"); } } return B_TRUE; } // metal objects and magshield? if (lfhasflag(lf, F_MAGSHIELD)) { if (isplayer(lf)) { msg("^wYour %s evades your grasp!", noprefix(obname)); } return B_TRUE; } nparts = 0; getflags(o->flags, retflag, &nretflags, F_GOESON, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_GOESON) { possbp[nparts] = f->val[0]; nparts++; } } if (nparts == 0) { if (isplayer(lf)) { msg("You can't wear that!"); } return B_TRUE; } else if (nparts == 1) { // go on the only needed body part ncheckparts = nparts; } else { // multiple possible locations int i; showpos = B_TRUE; if (hasflag(o->flags, F_GOESONMULTI)) { // goes on ALL of the parts ncheckparts = nparts; } else { // rings go in first possible place if (o->type->obclass->id == OC_RING) { bp = BP_NONE; for (i = 0; i < nparts; i++) { if (isfreebp(lf, possbp[i], o)) { bp = possbp[i]; break; } } if (bp == BP_NONE) { if (isplayer(lf)) msg("You have no room to wear that."); return B_TRUE; } possbp[0] = bp; nparts = 1; ncheckparts = nparts; } else { char ch = 'a'; // ask where to put it snprintf(buf, BUFLEN, "Where will you wear %s?",obname); initprompt(&prompt, buf); for (i = 0; i < nparts; i++) { object_t *inway; inway = getouterequippedob(lf, possbp[i]); if (inway) { char inwayname[BUFLEN]; getobname(inway, inwayname, inway->amt); if (hasflag(inway->flags, F_UNDERCLOTHING)) { snprintf(buf, BUFLEN, "%s (over %s)", getbodypartname(lf, possbp[i]), inwayname); } else { snprintf(buf, BUFLEN, "%s (replace %s)", getbodypartname(lf, possbp[i]), inwayname); } } else { snprintf(buf, BUFLEN, "%s", getbodypartname(lf, possbp[i])); } addchoice(&prompt, ch++, buf, NULL, &possbp[i], NULL); } addchoice(&prompt, '-', "(cancel)", NULL, NULL, NULL); if (prompt.nchoices == 1) { if (isplayer(lf)) msg("You have nowhere to wear that!"); return B_TRUE; } if (isplayer(lf)) { ch = getchoice(&prompt); if (ch == '-') { bp = BP_NONE; if (isplayer(lf)) msg("Cancelled."); return B_TRUE; } else bp = *((enum BODYPART *)prompt.result); } else { bp = *((enum BODYPART *)prompt.choice[rnd(0,prompt.nchoices - 1)].data); } possbp[0] = bp; nparts = 1; ncheckparts = nparts; } } //} } // make sure all required parts are free... for (i = 0; i < ncheckparts; i++) { bp = possbp[i]; while (!canwear(lf, o, bp)) { object_t *inway = NULL; int errresolved = B_FALSE; if ((gamemode == GM_GAMESTARTED) && lf->created) { switch (reason) { case E_ALREADYUSING: if (isplayer(lf)) msg("You're already wearing that!"); break; case E_DOESNTFIT: if (isplayer(lf)) msg("The unnatural shape of your %s prevents this from fitting.", getbodypartname(lf, bp)); break; case E_PALADIN: if (isplayer(lf)) msg("Your vows prevent you from wearing non-blessed armour."); break; case E_INJURED: if (isplayer(lf)) msg("Your injuries prevent you from wearing this."); break; case E_WEARINGSOMETHINGELSE: // find what else is there inway = getequippedob(lf->pack, bp); getobname(inway,buf, 1); if (isplayer(lf)) { int ch = '\0'; char buf2[BUFLEN]; if (inway->blessknown && iscursed(inway)) { msg("You cannot remove your %s.", noprefix(buf)); } else { // take offending item off first - this takes extra time. snprintf(buf2, BUFLEN, "Remove your %s",noprefix(buf)); while (!ch) { ch = askchar(buf2, "ynd?","y", B_TRUE, B_FALSE); if (ch == '?') { describeob(o); ch = '\0'; } } if ((ch == 'y') || (ch == 'd')) { if (isarmour(inway)) { if (!takeoff(player, inway)) { // took it off! errresolved = B_TRUE; } } else { // weapon if (!unweild(player, inway)) { // took it off! errresolved = B_TRUE; } } if (errresolved && (ch == 'd')) { drop(inway, inway->amt); } } } } break; case E_NOTKNOWN: if (isplayer(lf)) msg("You can't wear that!"); // same message as wearing non-armour break; case E_NOBP: if (isplayer(lf)) msg("You have no %s on which to wear that!", getbodypartname(lf, bp)); break; case E_LOWCHA: msg("You are not attractive enough to wear this."); break; case E_LOWCON: msg("You are not healthy enough to wear this."); break; case E_LOWDEX: msg("You are not dextrous enough to wear this."); break; case E_LOWIQ: msg("You are not smart enough to wear this."); break; case E_LOWSTR: msg("You are not strong enough to wear this."); break; case E_LOWWIS: msg("You are not wise enough to wear this."); break; case E_TOOSMALL: if (isplayer(lf)) msg("This armour is too small for you."); break; case E_TOOBIG: if (isplayer(lf)) msg("This armour is too big for you."); break; default: if (isplayer(lf)) { msg("You can't wear that!"); } break; } } // end if gamestarted if (!errresolved) { return B_TRUE; } // if we DID resolve the error, loop around and check if we can wear again... } // end while !canwear } // end for each required bodypart // at this point, we're going to try to wear it... killflagsofid(lf->flags, F_HIDING); // was the object already known? preknown = isknown(o); if (isplayer(lf)) { if (o->type->id == OT_RING_UNHOLINESS) { int protect = B_FALSE; if (godprayedto(R_GODPURITY)) { godsay(R_GODPURITY, B_TRUE, "Beware mortal! That ring holds the taint of impurity!"); protect = B_TRUE; } else if (godprayedto(R_GODLIFE)) { godsay(R_GODLIFE, B_TRUE, "Beware child! That ring is not for the living!"); protect = B_TRUE; } if (protect) { char ques[BUFLEN],ch; snprintf(ques, BUFLEN,"Still put on %s?",obname); ch = askchar(ques,"yn","n", B_TRUE, B_FALSE); if (ch != 'y') { msg("Cancelled."); return B_TRUE; } // otherwise, you were warned, so count it as known... preknown = B_TRUE; } } } // some checks first... if (touch(lf, o)) { taketime(lf, getactspeed(lf)); return B_TRUE; } // probably don't need this now... if (hasflag(o->flags, F_DEAD)) { taketime(lf, getactspeed(lf)); return B_TRUE; } // actually wear it for (i = 0; i < ncheckparts; i++) { addflag(o->flags, F_EQUIPPED, possbp[i], -1, -1, NULL); } taketime(lf, getactspeed(lf)); if (isplayer(lf)) maketried(o->type->id, NULL); // when you wear armour, you find out // its bonuses. if (isplayer(lf) && (o->type->obclass->id == OC_ARMOUR)) { f = hasflag(o->flags, F_BONUS); if (f && !f->known) { f->known = B_TRUE; getobname(o, obname, 1); } } if ((gamemode == GM_GAMESTARTED) && lf->created) { if (isplayer(lf)) { // announce if (showpos) { char posbuf[BUFLEN]; makewearstring(lf, o, B_TRUE, posbuf ); msg("You are now wearing %s (%s).", obname, posbuf); } else { msg("You are now wearing %s.", obname); } } else if (cansee(player, lf)) { char verb[BUFLEN]; getlfname(lf, buf); capitalise(buf); switch (rnd(1,2)) { case 1: strcpy(verb, "puts on"); break; case 2: strcpy(verb, "dons"); break; } msg("%s %s %s.", buf, verb, obname); } if (o->blessed == B_CURSED) { if (isplayer(lf)) { msg("^bOh no! The %s releases a pulse of evil!", noprefix(obname)); o->blessknown = B_TRUE; } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s%s %s releases a pulse of evil!", buf, getpossessive(buf), obname); o->blessknown = B_TRUE; } } } // warn if it will be cumbersome if (isplayer(lf)) { char howmuch[BUFLEN]; f = hasflagval(o->flags, F_EQUIPCONFER, F_SHIELDPENALTY, NA, NA, NULL); if (f) { int penalty; penalty = adjustshieldpenalty(lf, f->val[1]); if (penalty) { if (penalty >= 40) { strcpy(howmuch, "incredibly"); } else if (penalty >= 30) { strcpy(howmuch, "extremely"); } else if (penalty >= 20) { strcpy(howmuch, "very"); } else if (penalty >= 10) { strcpy(howmuch, "quite"); } else { strcpy(howmuch, "slightly"); } msg("^wYou find this shield %s cumbersome to use.", howmuch); } } } // special cases: if (isplayer(lf)) { switch (o->type->id) { case OT_RING_INVIS: // make ring of invis fully known - the HPDRAIN flag // won't be announced, so since we don't know all the flags we would // otherwise get "you turn invisible!" but still have the ring known // as "a blue ring" (or whatever) case OT_RING_UNHOLINESS: makeknown(o->type->id); break; default: break; } } if (o->type->id == OT_AMU_TRAVEL) { if (isplayer(lf) || cansee(player, lf)) { makeknown(o->type->id); } } else if (o->type->id == OT_AMU_CHOKING) { if (isplayer(lf)) { msg("^%c%s starts to constrict around your neck!", getlfcol(lf, CC_VBAD), obname); makeknown(o->type->id); if (!needstobreath(lf)) { msg("Luckily, you don't need to breath."); } } } // give flags giveobflags(lf, o, F_EQUIPCONFER); // special case... if (o->type->id == OT_AMU_TRAVEL) { map_t *newmap = NULL; cell_t *newcell = NULL; f = hasflag(o->flags, F_NEWMAP); if (f) { newmap = findmap(f->val[0]); newcell = getcellat(newmap, f->val[1], f->val[2]); } else { int mindepth,maxdepth,newdepth; region_t *newregion; // TODO: add other planes here. they must be from branchs which ALWAYS // exist (ie. which are created at the start of the game). switch (rnd(0,1)) { case 0: case 1: newregion = findregionbytype(BH_MAINDUNGEON); break; } // pick a new map at least 10 levels below maxdepth = newregion->rtype->maxdepth; mindepth = lf->cell->map->depth + 10; if (mindepth >= maxdepth) { newdepth = maxdepth; } else { newdepth = rnd(mindepth, maxdepth); } newmap = findregionmap(newregion->id, newdepth); if (!newmap) { // create new map newmap = addmap(); createmap(newmap, newdepth, newregion, NULL, D_NONE, NULL); } // find random cell newcell = getrandomcell(newmap); while (!cellwalkable(NULL, newcell, NULL) || celldangerous(lf, newcell, B_FALSE, NULL)) { newcell = getrandomcell(newmap); } } // remember orig cell killflagsofid(o->flags, F_ORIGMAP); addflag(o->flags, F_ORIGMAP, lf->cell->map->id, lf->cell->x, lf->cell->y, NULL); // go there! teleportto(lf, newcell, B_FALSE); makeknown(o->type->id); } if (isplayer(lf) && (o->type->id == OT_RING_UNHOLINESS) && preknown) { enum RACE whichgod = R_NONE; if (godprayedto(R_GODPURITY)) { whichgod = R_GODPURITY; } else if (godprayedto(R_GODLIFE)) { whichgod = R_GODLIFE; } if (whichgod != R_NONE) { angergod(whichgod, 50, GA_HERESY); } } if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) { // set statdirty since this might have impacted your AR / EV statdirty = B_TRUE; } return rv; } int weild(lifeform_t *lf, object_t *o) { char buf[BUFLEN]; flag_t *f; object_t *oo,*unweildob[MAXBODYPARTS],*takeoffob[MAXBODYPARTS]; int twohanded = B_FALSE,i,nunweild = 0,ntakeoff = 0; enum BODYPART weildloc,otherloc; for (i = 0; i < MAXBODYPARTS; i++) { unweildob[i] = NULL; takeoffob[i] = NULL; } // this might impact your AR if (isplayer(lf)) { statdirty = B_TRUE; } if (lfhasflag(lf, F_RAGE)) { if (isplayer(lf)) msg("You are too enraged to weild a weapon!"); return B_TRUE; } if (o) { getobname(o, buf, o->amt); killflagsofid(o->flags, F_SECONDARY); } else { snprintf(buf, BUFLEN, "nothing"); } if (!canweild(lf, o)) { if ((gamemode == GM_GAMESTARTED) && lf->created) { if (isplayer(lf)) { switch (reason) { case E_ALREADYUSING: msg("You are already weilding that!"); break; case E_NOUNARMEDATTACK: msg("You cannot fight without a weapon!"); break; case E_IMPOSSIBLE: msg("You cannot weild weapons!"); break; case E_PALADIN: if (isplayer(lf)) msg("Your vows prevent you from weilding non-blessed weapons."); break; case E_NOHANDS: msg("You do not have enough free hands to weild this weapon."); break; case E_NOTKNOWN: if (isplayer(lf)) msg("You can't weild that!"); // same message as wearing non-armour break; case E_LOWCHA: msg("You are not attractive enough to use this weapon."); break; case E_LOWCON: msg("You are not healthy enough to use this weapon."); break; case E_LOWDEX: msg("You are not dextrous enough to use this weapon."); break; case E_LOWIQ: msg("You are not smart enough to use this weapon."); break; case E_LOWSTR: msg("You are not strong enough to use this weapon."); break; case E_LOWWIS: msg("You are not wise enough to use this weapon."); break; case E_INJURED: msg("Your injuries prevent you from using this weapon."); break; default: msg("For some reason, you weild this!"); break; } } } return B_TRUE; } // anything else weilded? weildloc = getweildloc(o, lf, &otherloc, &twohanded); // firearm in regular hand? // note: this is the same logic as in canweild() if (o && !hasbp(lf, weildloc) && isfirearm(o) && !getequippedob(lf->pack, otherloc)) { int temp; // firearm can go in other hand. // swap locations. temp = weildloc; weildloc = otherloc; otherloc = temp; } // metal objects and magshield? if (lfhasflag(lf, F_MAGSHIELD)) { if (isplayer(lf)) { msg("^wYour %s evades your grasp!", noprefix(buf)); } return B_TRUE; } // if we are skilled at twoweaponing, and somehting is in the way, // ask if we want to use our other hand. if (!twohanded && hasbp(lf, otherloc)) { oo = getequippedob(lf->pack, weildloc); if (getskill(lf, SK_TWOWEAPON) && oo && !istwohandedfor(oo, lf)) { char ch; if (isplayer(lf)) { char buf2[BUFLEN]; snprintf(buf2, BUFLEN, "Weild %s in your left hand?",buf); ch = askchar(buf2, "yn","y", B_TRUE, B_FALSE); } else { if (getweaponskill(lf, o)) ch = 'y'; else ch = 'n'; } if (ch == 'y') { // make sure we are skilled in 2nd weapon // (weilding null (nothing) there is always okay too. if (!o || getweaponskill(lf, o)) { enum BODYPART temp; // swap locations. temp = weildloc; weildloc = otherloc; otherloc = temp; } else { msg("You are not skilled enough to use this weapon in your left hand."); return B_TRUE; } } } } // any other objects in the way? for (oo = lf->pack->first ; oo ; oo = oo->next) { f = hasflagval(oo->flags, F_EQUIPPED, weildloc, -1, -1, NULL); if (f) { if (isarmour(oo)) { char inwayname[BUFLEN]; char buf2[BUFLEN]; char ch = '\0'; if (isplayer(lf)) { if (oo->blessknown && iscursed(oo)) { msg("You cannot remove your %s.", noprefix(inwayname)); ch = 'n'; } else { getobname(oo, inwayname, oo->amt); // prompt before taking it off. snprintf(buf2, BUFLEN, "Remove your %s",noprefix(inwayname)); while (!ch) { ch = askchar(buf2, "ynd?","y", B_TRUE, B_FALSE); if (ch == '?') { describeob(o); ch = '\0'; } } } } else { ch = 'y'; } if ((ch == 'y') || (ch == 'd')) { if (isarmour(oo)) { if (takeoff(lf, oo)) { // if we can't remove it, stop. return B_TRUE; } } else { // weapon if (unweild(lf, oo)) { // if we can't remove it, stop. return B_TRUE; } } if (ch == 'd') { drop(oo, oo->amt); } } else { return B_TRUE; } } else { // don't unweild it yet, because we might still need // to ask if the player wants to unweild something in their // other hand. If they say NO, then we shouldn't unweild the // primary weapon either. unweildob[nunweild++] = oo; } } // new weapon is two handed? check other hand too. if (twohanded) { f = hasflagval(oo->flags, F_EQUIPPED, otherloc, -1, -1, NULL); if (f) { if (isweapon(oo)) { unweildob[nunweild++] = oo; } else { // armour char buf2[BUFLEN]; char inwayname[BUFLEN]; char ch; if (isplayer(lf)) { if (oo->blessknown && iscursed(oo)) { msg("You cannot remove your %s.", noprefix(inwayname)); ch = 'n'; } else { // prompt before taking it off. getobname(oo, inwayname, oo->amt); snprintf(buf2, BUFLEN, "Remove your %s",noprefix(inwayname)); ch = askchar(buf2, "yn","y", B_TRUE, B_FALSE); } } else { ch = 'y'; } if (ch == 'y') { if (isarmour(oo)) { takeoffob[ntakeoff++] = oo; } else { unweildob[nunweild++] = oo; } } else { return B_TRUE; } } } } } // now unweild stuff if required. if (nunweild) { for (i = 0; i < nunweild; i++) { if (unweild(lf, unweildob[i])) { // if we can't unweild old weapon, stop return B_TRUE; } } } if (ntakeoff) { for (i = 0; i < ntakeoff; i++) { if (takeoff(lf, takeoffob[i])) { // if we can't remove it, stop. return B_TRUE; } } } // if we asked to just unweild our weapon, exit now // with no error. if (!o) { if ((gamemode == GM_GAMESTARTED) && lf->created && (lf->race->baseid != R_DANCINGWEAPON)) { if (isplayer(lf)) { msg("You are now fighting unarmed."); } else if (cansee(player, lf)) { getlfname(lf, buf); msg("%s is now fighting unarmed.",buf); } } return B_FALSE; } // touching the new weapon caused us to drop it or similar. if (touch(lf, o)) { taketime(lf, getactspeed(lf)); return B_TRUE; } // now weild the new weapon. addflag(o->flags, F_EQUIPPED, weildloc, -1, -1, NULL); if (istwohandedfor(o, lf)) { addflag(o->flags, F_EQUIPPED, otherloc, -1, -1, NULL); } taketime(lf, getactspeed(lf)); if (isplayer(lf) && isweapon(o)) maketried(o->type->id, NULL); if ((gamemode == GM_GAMESTARTED) && lf->created && (lf->race->baseid != R_DANCINGWEAPON)) { if (isplayer(lf)) { char buf2[BUFLEN]; snprintf(buf2, BUFLEN, "You are now weilding %c - %s", o->letter, buf); if (twohanded) { strcat(buf2, " (both hands)"); } else if (weildloc == BP_SECWEAPON) { strcat(buf2, " (in left hand)"); } strcat(buf2, "."); msg(buf2); // warn if it won't do any damage if (!ismeleeweapon(o) && !isfirearm(o)) { msg("^wYou have a feeling that this weapon will not be very effective..."); } else { // warn if unskilled in this weapon skill_t *sk; sk = getobskill(o->flags); if (sk && !getskill(lf, sk->id)) { msg("^wYou feel rather inept with this weapon."); } else { flag_t *retflag[MAXCANDIDATES]; int nretflags,i; // warn about not meeting attrib reqs getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE); for (i = 0; i < nretflags; i++) { int pctmod; meetsattreq(lf, retflag[i], o, &pctmod); if (pctmod < 0) { msg("^wYou low %s will lower your %s with this weapon.", getattrname(retflag[i]->val[0]), (retflag[i]->val[0] == A_AGI) ? "accuracy" : "damage"); } } } } } else if (cansee(player, lf)) { if (lf->race->baseid != R_DANCINGWEAPON) { char buf2[BUFLEN]; getlfname(lf, buf2); msg("%s weilds %s.", buf2, buf); } } if (o->blessed == B_CURSED) { if (isplayer(lf)) { msg("^bOh no! The %s releases a pulse of evil!", strchr(buf, ' ')+1); o->blessknown = B_TRUE; } else if (cansee(player, lf)) { char buf2[BUFLEN]; getlfname(lf, buf2); msg("%s%s %s releases a pulse of evil!", buf2, getpossessive(buf2), buf); o->blessknown = B_TRUE; } } } // give flags giveobflags(lf, o, F_EQUIPCONFER); // make certain flags known if (isplayer(lf)) { f = hasflag(o->flags, F_ARMOURIGNORE); if (f) { msg("^gYour %s seems unnaturally sharp!",noprefix(buf)); f->known = B_TRUE; } if ((o->material->id == MT_BONE) && godprayedto(R_GODDEATH)) { lifeform_t *g; g = findgod(R_GODDEATH); if (g) msg("You sense %s's approval.",g->race->name); } else if ((o->material->id == MT_WOOD) && godprayedto(R_GODNATURE)) { lifeform_t *g; g = findgod(R_GODNATURE); if (g) msg("You sense %s's approval.",g->race->name); } } return B_FALSE; } enum SKILLLEVEL whichlevforabil(enum SKILL skid, enum OBTYPE oid) { int i; skill_t *sk; sk = findskill(skid); assert(sk); for (i = 0; i < sk->nskillwills; i++) { if (sk->skillwill[i].abilid == oid) { return sk->skillwill[i].lev; } } return PR_INEPT; } int willattackdoors(lifeform_t *lf) { lifeform_t *targ; targ = gettargetlf(lf); // ie. if we can see/hear our target behind the door if (targ) { int vol; getnoisedetails(targ, N_WALK, NULL, NULL, NULL, &vol, NULL); if (canhear(lf, targ->cell, vol, NULL) || cansee(lf, targ)) { return B_TRUE; } } return B_FALSE; } int willbackstab(lifeform_t *lf, lifeform_t *victim, object_t *wep) { if (getraceclass(victim) == RC_PLANT) return B_FALSE; if (wep && (getdamtype(wep) == DT_PIERCE) && // stabbing weapon getskill(lf, SK_BACKSTAB) && // able to backstab // !lfhasflagval(victim, F_STABBEDBY, lf->id, NA, NA, NULL) && // haven't stabbed them already ishelplessvictim(victim, lf, NULL)) { // victim can't see us or is fleeing return B_TRUE; } return B_FALSE; } int willbleedfrom(lifeform_t *lf, enum BODYPART bp) { object_t *o; o = hasequippedobidon(lf->pack, OT_BANDAGE, bp); if (o) { // don't bleed. return B_FALSE; } if (hasequippedobid(lf->pack, OT_AMU_BLOOD)) return B_FALSE; return B_TRUE; } // if lf picks up 'o', will they be burdened afterwards? // (if they are _already_ burdened, then stil return true) int willburden(lifeform_t *lf, object_t *o, int howmany) { if (getobpileweight(lf->pack) + (getobweight(o)*howmany) > getmaxcarryweight(lf) ) { return B_TRUE; } return B_FALSE; } int willeatlf(lifeform_t *eater, lifeform_t *eatee) { if (isplayer(eater)) return B_FALSE; if (eater == eatee) return B_FALSE; // no self-cannibulism! // doesn't want to eat if (!lfhasflagval(eater, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL)) { return B_FALSE; } // does eater eat eatee's material? if ((eatee->material->id == MT_FLESH) && lfhasflag(eater, F_CARNIVORE)) { return B_TRUE; } if ((eatee->material->id == MT_PLANT) && lfhasflag(eater, F_VEGETARIAN)) { return B_TRUE; } if (eater->race->id == R_BINGEBARK) { return B_TRUE; } return B_FALSE; }