nexus/lf.c

15779 lines
357 KiB
C

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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 "spell.h"
#include "text.h"
extern double presin[];
extern double precos[];
extern FILE *logfile;
extern void (*precalclos)(lifeform_t *);
extern int noredraw;
extern map_t *firstmap;
extern race_t *firstrace, *lastrace;
extern raceclass_t *firstraceclass, *lastraceclass;
extern job_t *firstjob, *lastjob;
extern skill_t *firstskill, *lastskill;
extern objecttype_t *objecttype;
extern lifeform_t *player;
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;
// for xplist
race_t **raceposs;
int *xpposs;
int xplistlen;
int notime = B_FALSE; // prevent taketime from doing anything
void autoweild(lifeform_t *lf) {
object_t *bestwep,*bestfirearm;
object_t *o,*firearm;
int pretimespent;
pretimespent = lf->timespent;
// weild weapons if required
bestwep = getbestweapon(lf);
if (bestwep) {
weild(lf, bestwep);
}
bestfirearm = getbestfirearm(lf);
if (bestfirearm) {
weild(lf, bestfirearm);
}
// weild armour if required
for (o = lf->pack->first ; o ; o = o->next) {
if (canwear(lf, o, BP_NONE)) {
wear(lf, o);
} else {
if (isplayer(lf) && (o->type->id == OT_RING_PROTFIRE)) {
dblog("zz");
}
}
}
// 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);
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) {
flag_t *f;
char obname[BUFLEN];
if (lf->cell->type->solid) return;
if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) return;
f = lfhasflag(lf, F_BLOODOB);
if (f) {
if (f->text) {
strcpy(obname, f->text);
} else {
strcpy(obname, "");
}
} else {
strcpy(obname, "splash of blood");
}
if (strlen(obname) > 0) {
addob(lf->cell->obpile, obname);
if (splatter) addobsinradius(lf->cell, 1, DT_COMPASS, obname, B_TRUE);
}
}
int bleedfrom(lifeform_t *lf, enum BODYPART bp, int splatter) {
if (willbleedfrom(lf, bp)) {
bleed(lf, splatter);
return B_FALSE;
}
return B_TRUE;
}
void breakgrabs(lifeform_t *lf, int fromme, int tome) {
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);
killflagsofid(grabee->flags, F_GRABBEDBY);
killflagsofid(lf->flags, F_GRABBING);
}
f = lfhasflag(lf, F_ATTACHEDTO);
if (f) {
killflag(f);
}
}
if (tome) {
f = lfhasflag(lf, F_GRABBEDBY);
if (f) {
lifeform_t *graber;
graber = findlf(NULL, f->val[0]);
assert(graber);
killflagsofid(graber->flags, F_GRABBING);
killflagsofid(lf->flags, F_GRABBEDBY);
}
for (alf = lf->cell->map->lf ; alf ; alf = alf->next) {
f = lfhasflagval(alf, F_ATTACHEDTO, lf->id, NA, NA, NULL);
if (f) {
killflag(f);
}
}
}
}
long calcscore(lifeform_t *lf) {
long points = 0;
object_t *o;
if (lfhasflag(lf, F_NOSCORE)) {
return 0;
}
// objects
for (o = lf->pack->first ; o ; o = o->next) {
points += getobpoints(o);
}
// points for xp
points += (lf->xp / 10);
return points;
}
// figure out how much xp a race is worth
int calcxp(lifeform_t *lf) {
float multiplier = 1;
float xpval = 0;
float offense = 0;
float defence = 0;
float spells = 0;
flag_t *f;
float avgdam = 0;
int db = B_FALSE;
int maxhdroll;
int i;
float xpconstant = 1;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
if (lfhasflag(lf, F_DEBUG)) {
db = B_TRUE;
}
if (db) dblog("calcxp: calculating xpval for %s",lf->race->name);
f = lfhasflag(lf, F_XPVAL);
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;
}
// 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) {
int ndice,nsides,bonus;
texttodice(f->text, &ndice,&nsides,&bonus);
maxhdroll = ndice * nsides + bonus;
} 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 += 10;
}
// 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;
// set up fake map
m.lf = NULL;
m.lastlf = NULL;
// make a fake cell
setcelltype(&c, CT_CORRIDOR);
c.lf = NULL;
c.map = &m;
// make a fake lf
lf = addlf(&c, rid, 1);
xpval = calcxp(lf);
killlf(lf);
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 (!lf->stamina) {
reason = E_NOSTAM;
return B_FALSE;
} else if (lfhasflag(lf, F_STUNNED)) {
reason = E_STUNNED;
return B_FALSE;
}
return B_TRUE;
}
int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
int castable = B_FALSE;
flag_t *f;
objecttype_t *ot;
// TODO: check for mute?
if (lfhasflag(lf, F_STUNNED)) {
reason = E_STUNNED;
return B_FALSE;
}
if (isprone(lf)) {
reason = E_PRONE;
return B_FALSE;
}
if (isswimming(lf) && (getskill(lf, SK_SWIMMING) < PR_EXPERT)) {
reason = E_SWIMMING;
return B_FALSE;
}
reason = E_OK;
ot = findot(oid);
f = lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL);
if (f) {
int needgrab = 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 (lfhasflagval(lf, F_CANCAST, oid, NA, NA, NULL)) {
int cost,power;
// need >animal intelligence to cast spells
if (ot->obclass->id == OC_SPELL) {
if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= IQ_ANIMAL) {
reason = E_LOWIQ;
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;
}
}
f = hasflag(ot->flags, F_STAMCOST);
if (f && (lf->stamina < f->val[0])) {
reason = E_NOSTAM;
return B_FALSE;
}
return castable;
}
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;
}
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 won't eat
if (getraceclass(lf) == RC_UNDEAD) {
reason = E_UNDEAD;
return B_FALSE;
}
if (!isedible(o)) {
reason = E_WRONGOBTYPE;
return B_FALSE;
}
// 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?
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) && (o->material->id == MT_FLESH)) {
reason = E_VEGETARIAN;
return B_FALSE;
}
if (lfhasflag(lf, F_CARNIVORE) && (o->material->id != MT_FLESH)) {
reason = E_CARNIVORE;
return B_FALSE;
}
if (lfhasflag(lf, F_PARTVEGETARIAN) && (o->material->id == MT_FLESH)) {
if (gethungerlevel(gethungerval(player)) < H_PECKISH) {
reason = E_PARTVEGETARIAN;
return B_FALSE;
}
}
// ai lifeforms won't eat tainted food
if (!isplayer(lf)) {
if (hasflag(o->flags, F_TAINTED)) {
reason = E_WONT;
return B_FALSE;
}
}
return B_TRUE;
}
int canhear(lifeform_t *lf, cell_t *dest, int volume) {
int numpixels;
int i;
int x1,y1;
int sounddist;
int x2,y2;
map_t *map;
cell_t *retcell[MAXRETCELLS];
int celldist;
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
if (lfhasflag(lf, F_TRAINING)) return B_FALSE;
// can't hear noises from other maps
if (lf->cell->map != dest->map) return B_FALSE;
// 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)
/*
celldist = getcelldist(lf->cell, dest);
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) {
sounddist--;
}
// 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_CANLEARN, skid, NA, NA, NULL)) {
return B_TRUE;
}
return B_FALSE;
}
int canopendoors(lifeform_t *lf) {
if (lf) {
if (!lfhasflag(lf, F_HUMANOID)) {
return B_FALSE;
}
if (lfhasflagval(lf, F_NOBODYPART, BP_HANDS, NA, NA, NULL)) {
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)) {
reason = E_TOOBIG;
return B_FALSE;
}
if (lf) {
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 (getobunitweight(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;
reason = E_OK;
if ((getlfmaterial(lf) == MT_GAS) || lfhasflag(lf, F_NONCORPOREAL)) {
reason = E_INSUBSTANTIAL;
return B_FALSE;
}
// check lf weight
if (getobweight(o) > 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;
}
return B_TRUE;
}
int canquaff(lifeform_t *lf, object_t *o) {
if (!isdrinkable(o)) {
reason = E_WRONGOBTYPE;
return B_FALSE;
}
if (getlfmaterial(lf) == MT_GAS) {
reason = E_INSUBSTANTIAL;
return B_FALSE;
}
return B_TRUE;
}
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;
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) {
if (f->val[0] > 0) {
tremordist = f->val[0];
} else {
tremordist = dist;
}
} else {
tremordist = -1;
}
// viewer asleep?
if (lfhasflag(viewer, F_ASLEEP)) {
// can only 'see' yourself
if (viewee != viewer) {
return B_FALSE;
}
}
if (uselos) {
// no line of sight?
if (!haslos(viewer, viewee->cell)) {
return B_FALSE;
}
}
// viewee is invisible?
if (lfhasflag(viewee, F_INVISIBLE)) {
if (!lfhasflag(viewer, F_SEEINVIS) && (tremordist < dist)) {
return B_FALSE;
}
}
// 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)) {
if (!lfhasflag(viewer, F_SEEINVIS) && (tremordist < dist)) {
return B_FALSE;
}
}
}
// viewee hiding?
if (lfhasflag(viewee, F_HIDING) && (viewee != viewer)) {
if (!lfhasflagval(viewer, F_SPOTTED, viewee->id, NA, NA, NULL)) {
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)) {
return B_FALSE;
}
}
return B_TRUE;
}
int cansleep(lifeform_t *lf) {
enum RACECLASS rc;
rc = getraceclass(lf);
if (rc == RC_PLANT) {
return B_FALSE;
} else if (rc == RC_UNDEAD) {
return B_FALSE;
}
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;
object_t *oo;
flag_t *f;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
reason = E_OK;
if (where != BP_NONE) {
if (!hasbp(lf, where)) {
reason = E_NOBP;
return B_FALSE;
} else if (lfhasflagval(lf, F_NOARMOURON, where, NA, NA, NULL)) {
reason = E_DOESNTFIT;
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;
}
// too heavy?
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;
}
// other flags to check
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)) {
return B_FALSE;
}
}
}
if (where == BP_NONE) {
// can we wear it ANYWHERE?
enum BODYPART possbp[MAXBODYPARTS];
int nparts = 0;
int i;
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;
}
// are any of these body parts free?
for (i = 0; i < nparts; i++) {
if (isfreebp(lf, possbp[i])) {
return B_TRUE;
}
}
// no parts free
reason = E_WEARINGSOMETHINGELSE;
return B_FALSE;
} else {
// does this object go there?
f = hasflagval(o->flags, F_GOESON, where, NA, NA, NULL);
if (!f) {
// can't wear there!
reason = E_IMPOSSIBLE;
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, &otherloc, NULL);
// already weilding it?
if (o && isequipped(o)) {
reason = E_ALREADYUSING;
return B_FALSE;
}
// too heavy?
if (o && lfhasflagval(lf, F_INJURY, IJ_SHOULDERDISLOCATED, NA, NA, NULL)) {
if (isheavyweapon(o)) {
reason = E_INJURED;
return B_FALSE;
}
}
if (o && 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->id == R_DANCINGWEAPON) {
return B_TRUE;
}
if (!lfhasflag(lf, F_HUMANOID)) {
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_TWOHANDED, 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)) {
return B_FALSE;
}
} else if (f->id == F_TWOHANDED) {
// need both hands free...
if (lfhasflagval(lf, F_NOBODYPART, otherloc, NA, NA, NULL)) {
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) {
flag_t *f;
reason = E_OK;
f = hasflag(o->flags, F_EQUIPPED);
if (!f) {
reason = E_NOTEQUIPPED;
return B_FALSE;
}
// cursed?
if (o->blessed == B_CURSED) {
reason = E_CURSED;
return B_FALSE;
}
return B_TRUE;
}
int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *targob, cell_t *targcell) {
int rv;
int cost;
flag_t *f,*willflag;
int power;
objecttype_t *sp;
// 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_LOWIQ:
msg("You are not smart enough to cast spells.");
break;
case E_PRONE:
msg("You can't cast spells while prone.");
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;
default:
msg("For some reason, you can't cast that.");
break;
}
}
return B_TRUE;
}
willflag = lfhasflagval(lf, F_CANWILL, sid, NA, NA, NULL);
// special case
if (!willflag) {
f = lfhasflag(lf, F_NEEDOBFORSPELLS);
if (f && !hasob(lf->pack, f->val[0])) {
objecttype_t *ot;
ot = findot(f->val[0]);
if (isplayer(lf)) {
msg("You can't cast spells without %s %s.",needan(ot->name) ? "an" : "a", ot->name);
}
return B_TRUE;
}
}
power = getspellpower(lf, sid);
sp = findot(sid);
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++) {
snprintf(buf, BUFLEN, "Power %s (%d MP)", roman(i), getmpcost(lf, sid) * i);
getvarpowerspelldesc(sp->id, i, desc);
if (strlen(desc)) {
strcat(buf, "\t");
strcat(buf, desc);
}
addchoice(&prompt, '0' + i, buf, buf, NULL, NULL);
}
ch = getchoice(&prompt);
power = ch - '0';
// modify cost
cost *= i;
}
}
// take time
taketime(lf, getspellspeed(lf));
// lose mp
losemp(lf, cost);
// miscast chance?
if (isplayer(lf)) {
if (pctchance(getmiscastchance(lf))) {
msg("^WYour cumbersome armour makes you miscast your spell!");
return B_FALSE;
}
}
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 20 + power, 0)) {
if (power > 1) {
// half strength
power /= 2;
if (power <= 1) power = 1;
if (isplayer(lf)) {
msg("^wYour magic resistance lowers your spell's power.");
}
}
}
// announce
if (!isplayer(lf)) {
if (cansee(player, lf)) {
char lfname[BUFLEN];
char whattosay[BUFLEN];
getlfname(lf, lfname);
// special case
f = lfhasflag(lf, F_SPELLCASTTEXT);
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, "itself");
} else {
getlfname(targlf, targname);
}
strcat(whattosay, " at ");
strcat(whattosay, targname);
}
strcat(whattosay, ".");
msg("%s", whattosay);
}
} else {
if (hasflag(sp->flags, F_CASTINGTIME)) {
} else {
msg("%s starts casting a spell.", lfname);
}
}
} else { // player can't see them
if ((targlf == player) || (targcell = player->cell)) {
msg("Something casts a spell at you.");
}
}
}
// boost power?
if (hasjob(lf, J_DRUID)) {
power += countplantsinsight(lf);
limit(&power, NA, 10);
}
// stop hiding
killflagsofid(lf->flags, F_HIDING);
// willing this spell? reset counter!
// do this _before_ casting the spell,
// in case the spell causes us to lose
// the f_canwill flag (eg. polymorph)
if (willflag) {
if (willflag->val[2] != NA) {
willflag->val[1] = -1;
}
}
// cast the spell
f = hasflag(sp->flags, F_CASTINGTIME);
if (f) {
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
addflag(lf->flags, F_CASTINGSPELL, sid, NA, NA, NULL);
rv = dospelleffects(lf, sid, power, targlf, targob, targcell, B_UNCURSED, NULL, B_FALSE);
f = lfhasflag(lf, F_CASTINGSPELL);
if (f) {
killflag(f);
}
}
// successful cast?
if (!rv) {
practice(lf, SK_SPELLCASTING, 1);
if (isplayer(lf)) {
switch (getschool(sid)) {
case SS_DEATH:
pleasegodmaybe(R_GODDEATH, getspelllevel(sid));
angergodmaybe(R_GODPURITY, getspelllevel(sid)*5);
break;
case SS_LIFE:
pleasegodmaybe(R_GODPURITY, getspelllevel(sid));
angergodmaybe(R_GODDEATH, getspelllevel(sid)*5);
break;
default:
break;
}
}
}
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 <= getvisrange(lf, B_FALSE))) 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
if (!c->type->transparent) {
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)) {
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);
} 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 (!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;
}
// 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)) {
return B_FALSE;
}
i = getskill(lf, SK_SWIMMING) - isburdened(lf);
limit(&i, 0, NA);
slev = i;
depth = getobdepth(o, lf);
// 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);
} else if (i == DP_WAIST) {
armour = getouterequippedob(lf, BP_LEGS); if (armour) takedamage(armour, 4, DT_WATER);
armour = getouterequippedob(lf, BP_WAIST); if (armour) takedamage(armour, 4, DT_WATER);
} else if (i == DP_SHOULDERS) {
armour = getouterequippedob(lf, BP_BODY); if (armour) takedamage(armour, 4, DT_WATER);
armour = getouterequippedob(lf, BP_SHOULDERS); if (armour) takedamage(armour, 4, DT_WATER);
} else if (i == DP_HEAD) {
armour = getouterequippedob(lf, BP_HEAD); if (armour) takedamage(armour, 4, DT_WATER);
}
}
// 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 ((!slev || !lf->stamina) && !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) / 3);
limit(&damamt, 1, NA);
if (damamt >= lf->hp) {
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;
setlastdam(lf, "drowning");
} else {
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;
}
losehp(lf, damamt, DT_DIRECT, NULL, "drowning");
}
}
}
if (!isdead(lf)) {
f = isvulnto(lf->flags, DT_WATER);
if (f) {
int dam;
if (strlen(f->text)) {
dam = roll(f->text);
} else {
dam = roll("1d6");
}
applywalkdam(lf, dam, DT_WATER, o);
}
}
return didsomething;
}
int check_rest_ok(lifeform_t *lf) {
if (!safetorest(lf)) {
if (isplayer(lf)) {
switch (reason) {
case E_LEVITATING:
msg("You cannot rest while levitating in mid-air!");
break;
case E_MONSTERNEARBY:
msg("You cannot rest - there are monsters nearby!");
break;
default:
msg("You cannot rest for some reason.");
break;
}
}
return B_TRUE;
}
return B_FALSE;
}
// how dangerous is lf2 to lf1?
// < 0 = harder
// > 0 = easier
float comparelfs(lifeform_t *lf1, lifeform_t *lf2) {
float avgdam[2];
lifeform_t *lf[2];
float turnstokill[2];
int i;
float ratio;
lf[0] = lf1;
lf[1] = lf2;
for (i = 0; i < 2; i++) {
float otherevasion;
// get avg damage
avgdam[i] = getavgdam(lf[i], B_FALSE);
// mod this by other lf's evasion
otherevasion = ((float)getevasion(lf[1-i]));
avgdam[i] -= pctof(otherevasion, avgdam[i]);
// divide other lf's hit points by this lf's avg dam
if (avgdam[i] == 0) {
turnstokill[1-i] = 9999;
} else {
turnstokill[1-i] = (float)lf[1-i]->hp / avgdam[i];
}
}
// compare turnstokill values
ratio = (turnstokill[0] / turnstokill[1]);
return ratio;
}
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;
}
int countinnateattacks(lifeform_t *lf) {
int count = 0,i;
flag_t *f;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (f->id == F_HASATTACK) count++;
}
return count;
}
int countmoney(lifeform_t *lf) {
object_t *o;
int amt = 0;
for (o = lf->pack->first ; o ; o = o->next) {
if (o->type->id == OT_GOLD) {
amt += o->amt;
}
}
return amt;
}
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) {
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;
hd = gethitdice(lf);
gold = hasob(player->pack, OT_GOLD);
if (gold) {
totmoney = countmoney(player);
} else {
totmoney = 0;
}
mongold = hasob(lf->pack, OT_GOLD);
amtwanted = rnd(hd*25, hd*100);
getlfname(lf, lfname);
if (say(lf, "Hand over all your gold!", 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;
}
} 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);
// also make any of its minions peaceful
getminions(lf, minion, &nminions);
for (i = 0; i < nminions; i++) {
makepeaceful(minion[i]);
}
}
// either way, kill bribe flag
killflagsofid(lf->flags, F_DEMANDSBRIBE);
return satisfied;
}
void die(lifeform_t *lf) {
char buf[BUFLEN];
flag_t *f;
cell_t *where;
//int dropobs = B_TRUE;
int vaporised = B_FALSE, i;
int willbecomeghost = B_FALSE;
object_t *corpse = NULL;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
if (cansee(player, lf)) {
needredraw = B_TRUE;
}
if (lfhasflag(lf, F_RISEASGHOST)) {
willbecomeghost = 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);
}
// put out fires
extinguishlf(lf);
if (isplayer(lf)) {
statdirty = B_TRUE;
}
return;
}
if (!willbecomeghost) {
if (isplayer(lf) && hasjob(lf, J_GOD)) {
char ch;
msg("^BYou die..."); more();
ch = askchar("Die", "yn", "n", B_TRUE);
if (ch == 'n') {
lf->hp = lf->maxhp;
msg("Not dying.");
return;
}
}
}
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, 4, "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);
// ai will now look for our coffin
if (isplayer(lf)) {
msg("^GYou feel the presence of a nearby coffin...");
} else {
addflag(lf->flags, F_WANTS, OT_COFFIN, B_COVETS, NA, NULL);
addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
}
return;
}
}
if (lf->race->id == R_GLOWBUG) {
// final spell...
castspell(lf, OT_S_FLASH, NULL, NULL, lf->cell);
}
// revert to your original form first.
if (lfhasflag(lf, F_POLYMORPHED)) {
if (lfhasflag(lf, F_ORIGRACE)) {
abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL);
// ... but you're still dead
lf->hp = 0;
}
}
if (!willbecomeghost) {
lf->alive = B_FALSE;
}
if (isplayer(lf)) {
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();
// god effects...
if (!vaporised) {
god = getrandomprayedgod();
if (god && !godisangry(god->race->id)) {
switch (god->race->id) {
case R_GODDEATH:
godsay(god->race->id, "Come to me, my servant..."); more();
msg("Bony claws rise up and drag your corpse underground."); more(); break;
case R_GODPURITY:
msg("Your spirit ascends to the heavens."); more(); break;
case R_GODTHIEVES:
msg("All your possessions suddenly vanish!"); more();
godsay(god->race->id, "Yoink!"); more();
break;
case R_GODMERCY:
if (onein(10)) {
godsay(god->race->id, "I will grant you a second chance, mortal... use it wisely."); 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);
return;
}
break;
default:
break;
}
}
}
} else {
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->id == R_DANCINGWEAPON) {
if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s drops to the ground.", buf);
}
}
// award xp (but not if another monster killed it)
if (hasflag(lf->flags, F_KILLEDBYPLAYER)) {
awardxpfor(lf,100);
if ((getalignment(lf) == AL_EVIL) || isundead(lf)) {
pleasegodmaybe(R_GODPURITY, 3);
} else if (getalignment(lf) == AL_GOOD) {
pleasegodmaybe(R_GODDEATH, 3);
} else { // ie. neutral
pleasegodmaybe(R_GODDEATH, 1);
}
// mercy god doesn't like killing
angergodmaybe(R_GODMERCY, 1);
}
}
// drop/kill all objects
if (willbecomeghost || !isplayer(lf)) {
while (lf->pack->first) {
if (vaporised) {
killob(lf->pack->first);
} else {
if (!moveob(lf->pack->first, lf->cell->obpile, ALL)) {
killob(lf->pack->first);
}
}
}
}
// drop corpse/splatter blood
if (vaporised) {
switch (rnd(1,2)) {
case 1:
fragments(lf->cell, "chunk of flesh", 2, UNLIMITED);
break;
case 2:
fragments(lf->cell, "pool of blood", 2, UNLIMITED);
break;
}
} else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) {
// shattered
fragments(lf->cell, "chunk of ice", 2, UNLIMITED);
} else {
if (lfhasflag(lf, F_NOCORPSE)) {
if (isundead(lf) && cansee(player, lf)) {
getlfname(lf, buf);
msg("%s crumbles to dust.", buf);
}
} else if (lf->lastdamtype == DT_MELT) {
// drop a pool of water
addob(lf->cell->obpile, "large puddle of water");
} else if (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(lf->cell->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)) {
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);
}
corpse = addob(lf->cell->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_POISONOUS) || 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);
}
// 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);
}
}
// 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(lf->cell->obpile, headname);
colourmatchob(headob, lf);
}
}
}
// splatter
if (!vaporised) {
flag_t *f;
f = lfhasflag(lf, F_DIESPLATTER);
if (f) {
fragments(lf->cell, f->text, 1, f->val[0]);
}
}
if (willbecomeghost) {
flag_t *f, *nextf;
// remove all job flags
lf->born = B_FALSE;
for (f = lf->flags->first ; 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_MYCORPSE, NA, NA, NA, cid);
}
} else {
where = lf->cell;
if (lf->controller != C_PLAYER) {
// kill lifeform
killlf(lf);
assert(where->lf == NULL);
}
}
if (needredraw) {
drawscreen();
}
}
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) {
dblog(" timespent=%3d id %d race %s",lf->timespent, lf->id, lf->race->name);
count++;
}
dblog("END LIFEFORM DUMP (%d found)",count);
}
void dumpmonsters(void) {
race_t *r;
flag_t *f;
int count = 0,wanthd;
dblog("START MONSTER DUMP:");
for (wanthd = 0; wanthd < maxmonhitdice ; wanthd++) {
dblog("%d HIT DICE MONSTERS:",wanthd);
for (r = firstrace ; r ; r = r->next) {
int thishd;
thishd = gethitdicerace(r);
if (thishd == wanthd) {
int ndice,nsides,bonus,min,max;
f = hasflag(r->flags, F_HITDICE);
texttodice(f->text ? f->text : DEF_HITDICE, &ndice,&nsides,&bonus);
min = ndice + bonus;
max = (ndice*nsides) + bonus;
dblog("\t%s (%d avg hp)",r->name, (min+max)/2);
count++;
}
}
}
dblog("END MONSTER DUMP (%d found)",count);
}
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(NULL, raceposs[i]->id));
}
// 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));
}
}
int digcell(lifeform_t *lf, cell_t *c, object_t *o) {
char obname[BUFLEN];
getobname(o, obname, 1);
if (!c) {
return B_TRUE;
}
if (c->type->solid) {
if (isdiggable(c)) {
// replace wall
setcelltype(c, c->map->habitat->emptycelltype);
if (isplayer(lf)) {
msg("You dig through the wall.");
needredraw = B_TRUE;
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s digs through a wall.",lfname);
needredraw = B_TRUE;
}
//drawscreen();
// takes extra time
taketime(lf, getactspeed(lf)*9);
} else {
// fail
if (isplayer(lf)) {
msg("This wall is too hard to dig.");
}
}
} 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) {
// TODO: metal doors are immune to CHOP damage
if (!isimmuneto(door->flags, DT_CHOP)) {
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);
} 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];
getlfname(lf, lfname);
// TODO: check if the floor is solid?
if (o) {
if (lfhasflag(lf, F_LEVITATING)) {
if (isplayer(lf)) {
msg("You can't reach the ground from up here!");
}
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.");
}
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 == RG_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)) {
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 ((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;
gethitdicerange(i-1, &prevmin, &prevmax, RARITYVARIANCELF, B_FALSE);
gethitdicerange(i, &min, &max, RARITYVARIANCELF, B_FALSE);
fprintf(logfile, "Dlev %d (hd >= %d): ",i,min);
for (r = firstrace ; r; r = r->next) {
int hd = 0;
hd = gethitdicerace(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];
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;
if (hasflag(o->flags, F_DRINKABLE)) {
drinking = B_TRUE;
}
if (drinking) {
if (!candrink(lf, o)) {
if (isplayer(lf)) {
switch (reason) {
case E_UNDEAD:
msg("You are undead and don't need to drink.");
break;
default:
case E_WRONGOBTYPE:
msg("You can't drink that!");
break;
}
}
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_WRONGOBTYPE:
default:
msg("You can't eat that!");
break;
}
}
return B_TRUE;
}
}
getobname(o, obname, 1);
getlfname(lf, lfname);
// get total nutrition
nutrition = getnutrition(o);
if (nutrition == 0) {
// this might happen if you purposely try to eat a potion.
// technically a potion will pass above checks because you can
// drink it.
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;
}
}
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 = getobunitweight(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) {
if (alreadyeating) {
if (isplayer(lf)) {
msg("You finish %s.%s", drinking ? "drinking" : "eating",
(f->val[1] >= 20) ? " Yum!" : "");
} else if (cansee(player, lf)) {
msg("%s finishes %s.", lfname, drinking ? "drinking" : "eating");
}
} else {
if (isplayer(lf)) {
msg("You %s %s.%s", drinking ? "drink" : "eat", obname,
(f->val[1] >= 20) ? " Yum!" : "");
} else if (cansee(player, lf)) {
msg("%s %s %s.", lfname, drinking ? "drinks" : "eats", obname);
}
}
} 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 {
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);
}
}
}
hlev = gethungerlevel(gethungerval(player));
if (alreadyeating) {
if (turnstoeat <= 1) {
killflag(alreadyeating);
}
} else if (!alreadyeating && (turnstoeat > 1)) {
addflag(lf->flags, F_EATING, NA, NA, NA, buf);
}
if (isrotting(o)) {
if (!isimmuneto(lf->flags, DT_POISON)) {
char dambuf[BUFLEN];
// lose hp
if (isplayer(lf)) {
msg("^BThat %s was bad!", drinking ? "liquid" : "food");
}
// food poisoning for 20 turns
if (drinking) {
snprintf(dambuf, BUFLEN, "%s",obname);
} else {
snprintf(dambuf, BUFLEN, "a bad %s",noprefix(obname));
}
poison(lf, 20, P_FOOD, 1, dambuf);
}
}
// get less hungry
modhunger(lf, -eateachturn);
posthlev = gethungerlevel(gethungerval(player));
if (fullyeaten) {
// special cases
if (hasflagval(o->flags, F_CORPSEOF, R_GLOWBUG, NA, NA, NULL)) {
addtempflag(lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, 30);
}
if (hasflagval(o->flags, F_CORPSEOF, R_NEWT, NA, NA, NULL)) {
// think "eye of newt"
gainmp(lf, 2);
}
// special case for bananas
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));
}
} // end if fullyeaten
// take time
amt = getactspeed(lf);
if (o->pile->owner != lf) {
amt += SPEED_PICKUP;
}
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);
stopeating = B_TRUE;
}
// eating your pet is very bad!
if (isplayer(lf) && hasflagval(o->flags, F_PETOF, player->id, NA, NA, NULL)) {
angergodmaybe(R_GODPURITY, 150);
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);
if (ch == 'y') {
stopeating = B_TRUE;
}
} else {
stopeating = B_TRUE;
}
}
if (stopeating) {
killflagsofid(lf->flags, F_EATING);
}
if (fullyeaten) {
// 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 (isbleeding(lf) && onein(3)) {
// TODO: replace 4
if (ispetof(lf, player) && !canhear(player, lf->cell, 4)) {
char realname[BUFLEN];
real_getlfname(lf, realname, B_FALSE);
warn("You feel worried about your %s.", noprefix(realname));
} 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;
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) {
poss[nposs] = f->val[0];
nposs++;
}
}
if (nposs > 0) {
sel = rnd(0,nposs-1);
giveskill(lf, poss[sel]);
}
}
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;
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)) {
addflag(lf->flags, F_MPDICE, 1, 0, NA, NULL);
}
// update hp
hpratio = ((float)lf->hp / (float)lf->maxhp);
lf->maxhp += rollhitdice(lf);
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);
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;
if (isplayer(lf)) {
char ch;
more();
ch = askchar("Increase your Strength, Dexterity, Fitness, IQ or Wisdom?", "sdfiw",NULL, B_TRUE);
switch (ch) {
case 's': att = A_STR; break;
case 'd': att = A_DEX; break;
case 'f': att = A_CON; break;
case 'i': att = A_IQ; break;
case 'w': att = A_WIS; break;
}
} else {
// pick randomly
att = rnd(0,MAXATTS-1);
}
modattr(lf, att, 1);
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 done = B_FALSE;
skillstoenhance = 0;
for (f = lf->flags->first ; 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++;
}
}
}
while (!done && lf->skillpoints && (skillstolearn || skillstoenhance)) {
if (skillstolearn) {
char buf[BUFLEN];
if (skillstoenhance) {
snprintf(buf, BUFLEN, "(E)nhance skills, (L)earn skills, or (N)either (%d points left)?",lf->skillpoints);
eorl = askchar(buf,"eln","e", B_TRUE);
} else {
snprintf(buf, BUFLEN,"Learn a new skill (%d points left)?",lf->skillpoints);
ch = askchar(buf,"yn","y", B_TRUE);
if (ch == 'y') eorl = 'l';
else eorl = 'n';
}
} else if (skillstoenhance) {
char buf[BUFLEN];
snprintf(buf, BUFLEN,"Enhance your current skills (%d points left)?",lf->skillpoints);
ch = askchar(buf,"yn","y", B_TRUE);
if (ch == 'y') eorl = 'e';
else eorl = 'n';
} else {
eorl = 'n';
}
if (eorl == 'e') {
// enhance an existing skill
// any skills to get?
if (skillstoenhance) {
char ques[BUFLEN],ques2[BUFLEN];
int done = B_FALSE;
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';
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) {
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);
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);
}
} 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);
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);
}
} else {
done = B_TRUE;
}
}
} else {
msg("There is nothing more that you can learn.");
}
}
} else if (eorl == 'n') {
done = B_TRUE;
} // end enhance/learnnew
} // whiel skillstolearn || skillstoenhance
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) {
// give job-based level rewards
f = levelabilityready(lf);
while (f) {
if (f->id == F_LEVABIL) {
addflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text);
} else if (f->id == F_LEVFLAG) {
addflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text);
} else if (f->id == F_LEVSKILL) {
giveskill(lf, f->val[1]);
} else if (f->id == F_LEVSPELL) {
addflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL);
} else if (f->id == F_LEVSPELLSCHOOL) { // select a spell from school
if (isplayer(lf)) {
int done = B_FALSE;
while (!done) {
makespellchoicelist(&prompt, player, "Learn which new spell:","Describe which spell:", f->val[1], B_TRUE, B_FALSE, B_FALSE, player->maxmp);
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
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
done = B_TRUE;
} else {
describespell(ot);
}
}
} else {
msg("There are no new spells for you to learn at this time.");
done = B_TRUE;
}
}
} 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)].data;
if (ot) {
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
}
}
}
}
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
f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL);
if (f) {
char newtext[BUFLEN];
strcpy(newtext, f->text);
switch(lf->level) {
case 2:
strcpy(newtext,"1d6");
break;
case 3:
case 4:
strcpy(newtext,"1d6+1");
break;
case 5:
strcpy(newtext,"2d4");
break;
case 6:
case 7:
strcpy(newtext,"3d3");
break;
case 8:
strcpy(newtext,"2d6");
break;
case 9:
case 10:
strcpy(newtext,"3d4");
break;
case 11:
strcpy(newtext,"3d4+1");
break;
case 12:
case 13:
strcpy(newtext,"4d4");
break;
case 14:
strcpy(newtext,"4d4+1");
break;
case 15:
strcpy(newtext,"5d4");
break;
case 16:
case 17:
strcpy(newtext,"4d6");
break;
case 18:
strcpy(newtext,"6d4");
break;
case 19:
strcpy(newtext,"5d6");
break;
case 20:
strcpy(newtext,"8d4");
break;
default:
strcpy(newtext,"6d6");
break;
}
if (!streq(newtext, f->text)) {
changeflagtext(f, newtext);
if (isplayer(lf)) msg("^gYour unarmed attack damage has increased!");
}
}
// enhance # attacks
f = lfhasflag(lf, F_MAXATTACKS);
if (f) {
int min,max;
min = f->val[0];
max = f->val[1];
if ((lf->level >= 2) && (lf->level <= 3)) {
min = 1; max = 1;
} else if ((lf->level >= 4) && (lf->level <= 6)) {
min = 1; max = 2;
} else if ((lf->level >= 7) && (lf->level <= 9)) {
min = 2; max = 2;
} else if ((lf->level >= 10) && (lf->level <= 12)) {
min = 2; max = 3;
} else if ((lf->level >= 13) && (lf->level <= 16)) {
min = 3; max = 3;
} else if (lf->level >= 17) {
min = 3; max = 4;
}
if ((min != f->val[0]) || (max != f->val[1])) {
f->val[0] = min;
f->val[1] = max;
if (isplayer(lf)) msg("^gYour number of unarmed attacks has increased!");
}
}
}
// psionics sometimes lets you learn spells
slev = getskill(lf, SK_SS_MENTAL);
if (pctchance(slev*20)) {
// construct list of castable mental spells
makespellchoicelist(&prompt, lf, "Learn which new psionic power:","Describe which psionic power:", SS_MENTAL, B_TRUE, B_FALSE, B_FALSE, player->maxmp);
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
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
} else {
describespell(ot);
}
}
}
}
killflagsofid(lf->flags, F_HASNEWLEVEL);
// ready for another level?
if (lf->xp >= getxpforlev(lf->level + 1)) {
gainlevel(lf); // this will increment 'newlevel'
}
} // end if gainedxplev
}
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;
}
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 (lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET)) {
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*2);
addflag(lf->flags, F_PRONE, B_TRUE, NA, NA, NULL);
loseconcentration(lf);
return B_FALSE;
}
// if you are going to sleep on purpose, use 'gotosleep'.
// this function is for when it is forced upon you by a spell, etc.
int fallasleep(lifeform_t *lf, enum SLEEPTYPE how, int howlong) {
if (lfhasflag(lf, F_ASLEEP)) {
return B_TRUE;
}
if ((how == ST_ASLEEP) && lfhasflag(lf, F_CAFFEINATED)) {
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);
killflagsofid(lf->flags, F_RAGE);
killflagsofid(lf->flags, F_TRAINING);
addtempflag(lf->flags, F_ASLEEP, B_TRUE, how, NA, NULL, howlong);
return B_FALSE;
}
// make 'lf' respond to damage
void fightback(lifeform_t *lf, lifeform_t *attacker) {
interrupt(lf);
if (lfhasflag(lf, F_FEIGNINGDEATH) || lfhasflagval(lf, F_ASLEEP, NA, ST_KO, NA, NULL)) {
// don't respond.
return;
}
// 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);
}
// 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);
}
if (willflee(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)) {
if (isadjacent(lf->cell, attacker->cell)) {
turntoface(lf, attacker->cell);
}
aiattack(lf, attacker, DEF_AIFOLLOWTIME);
}
// 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, DEF_AIFOLLOWTIME);
}
}
}
}
// 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);
}
}
}
}
}
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;
}
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) {
race_t *r;
raceclass_t *rc;
// first check for exact matches
for (r = firstrace; r ; r = r->next) {
if (!strcmp(r->name, name)) {
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);
}
}
// ...then partial matches in names
for (r = firstrace; r ; r = r->next) {
if (strstr(r->name, name)) {
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;
}
// 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, 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)) {
pleasegodmaybe(R_GODMERCY, 5);
angergodmaybe(R_GODDEATH, 25);
}
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);
breakgrabs(lf, B_TRUE, B_FALSE); // 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)) {
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) {
if (!usestairs(lf, stairs, B_TRUE)) {
if (db) dblog("%s - fleeing via %s", stairs->type->name);
return B_TRUE;
}
}
// move away from them
if (!moveawayfrom(lf, fleefrom->cell, DT_ORTH, B_FALSE, B_FALSE)) {
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 (!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);
}
int freezelf(lifeform_t *freezee, lifeform_t *freezer, int howlong) {
if (isimmuneto(freezee->flags, DT_COLD)) {
if (isplayer(freezee)) {
msg("You feel a slight chill.");
}
return B_TRUE;
} else if (isresistantto(freezee->flags, DT_COLD)) {
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 (isplayer(lf)) {
statdirty = B_TRUE;
}
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 (maxed) {
msg("^gYou are now fully healed.");
}
// update screen
drawstatus();
updatestatus();
}
}
void gainlevel(lifeform_t *lf) {
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 3 levels
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);
}
}
// auto skill gain for monsters
if (!isplayer(lf)) {
lf->skillpoints++;
}
if (isplayer(lf)) {
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();
}
}
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, 20, 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) {
statdirty = B_TRUE;
drawstatus();
updatestatus();
}
}
}
void gainxp(lifeform_t *lf, long amt) {
int newskillpoints = 0;
int doxp = B_TRUE;
assert(amt >= 0);
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)) {
int amtneeded;
amtneeded = getspforpoint(lf);
lf->skillxp += amt;
assert(lf->skillxp >= 0);
while (lf->skillxp >= amtneeded) {
newskillpoints++;
lf->skillxp -= amtneeded;
if (isplayer(lf)) statdirty = B_TRUE;
}
}
if (doxp) {
// ready for next level? can only go up ONE level.
if (lf->xp >= getxpforlev(lf->level + 1)) {
gainlevel(lf); // this will increment 'newlevel'
}
}
if (newskillpoints) {
lf->skillpoints += 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;
}
// 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) && cansee(c->lf, lf)) {
int ok = B_FALSE;
if (areallies(lf, c->lf) && haslof(c->lf->cell, lf->cell, LOF_WALLSTOP, NULL)) {
// ally with a clear path to you
ok = B_TRUE;
} else if (areenemies(lf, c->lf) && (getcelldist(c, lf->cell) == 1)) {
// adjacent enemy
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) 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];
}
enum ALLEGIENCE getallegiance(lifeform_t *lf) {
flag_t *f;
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;
*nobs = 0;
for (bp = BP_WEAPON; bp < MAXBODYPARTS; bp++) {
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 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) && (getobweight(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, 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_ARMOURRATING, F_MAGICARMOUR, F_PHALANX, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (f->id == F_ARMOURRATING) {
ar += f->val[0];
if (hitob) {
hitob[*narms] = NULL;
hitchance[*narms] = getbodyparthitchance(BP_BODY);
(*narms)++;
}
}
if (f->id == F_MAGICARMOUR) {
ar += f->val[0];
if (hitob) {
hitob[*narms] = NULL;
hitchance[*narms] = getbodyparthitchance(BP_BODY);
(*narms)++;
}
}
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];
if (hitob) {
hitob[*narms] = NULL;
hitchance[*narms] = getbodyparthitchance(BP_BODY);
(*narms)++;
}
}
}
}
for (o = lf->pack->first ; o ; o = o->next) {
flag_t *eqflag;
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);
if (hitob) {
hitob[*narms] = o;
hitchance[*narms] = getbodyparthitchance(isshield ? BP_BODY : eqflag->val[0]);
(*narms)++;
}
}
}
}
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);
}
return (int)speed;
}
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_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) {
if (attr == A_DEX) {
if (hasjob(lf, J_PIRATE)) {
val += f->val[0];
} else {
val -= f->val[0];
}
} else if (attr == A_WIS) {
val -= f->val[0];
}
}
if (f->id == F_INJURY) {
if ((f->val[0] == IJ_NOSEBROKEN) && (attr == A_CHA)) {
val -= 4;
} else if ((f->val[0] == IJ_WINDPIPECRUSHED) && (attr == A_CON)) {
val -= 6;
}
}
}
if (val < 0) val = 0;
return val;
}
// 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;
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(f, &min,&max);
thisavg = ((float)min + (float)max) / 2.0;
// confers anything?
for (of = o->flags->first ; 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);
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(hasflag(o->flags, F_DAM), &min,&max);
thisavg = ((float)min + (float)max) / 2.0;
// confers anything?
for (of = o->flags->first ; 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;
}
f = hasflag(w->flags, F_DAM);
if (f) {
getdamrange(f, &mindam, &maxdam);
} else {
mindam = 0;
maxdam = 0;
}
mindam += bonus;
maxdam += bonus;
dammod = getstrdammod(lf);
// apply damage mod for strength
if (!hasflag(w->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) {
mindam = (int)((float)mindam * dammod);
maxdam = (int)((float)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;
}
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 += getobweight(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;
// no evasion if you can't move!
if (isimmobile(lf)) {
return 0;
}
// get natural evasion, adjustments for bulky armour/shield
getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_EVASION, F_SHIELDPENALTY, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (f->id == F_ARMOURPENALTY) {
ev -= adjustarmourpenalty(lf, f->val[1]);
} else if (f->id == F_EVASION) {
ev += (f->val[0]);
} else if (f->id == F_SHIELDPENALTY) {
ev -= adjustshieldpenalty(lf, f->val[1]);
}
}
// level based evasion (modified by evasion skill)
level_ev = gethitdice(lf);
skillpctmod = (getskill(lf, SK_EVASION) + 1) * 40;
level_ev = pctof(skillpctmod, level_ev);
ev += level_ev;
// dexterity mod
ev += getattr(lf, A_DEX);
// you are easier to hit if you're glowing
if (hasflag(lf->flags, F_PRODUCESLIGHT)) {
ev -= 5;
}
// 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) {
object_t *bestwep = NULL;
int bestdam = -1;
object_t *o;
for (o = lf->pack->first ; o ; o = o->next) {
if (!isequipped(o) && isthrowmissile(o) ) {
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;
obpile_t *op = NULL;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
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;
if (!op) {
op = addobpile(NULL, NULL, NULL);
}
ot = findot(retflag[i]->val[0]);
if (ot) {
o = addobfast(op, ot->id);
if (isweapon(o) && !isfirearm(o) && canweild(lf, o) && isbetterwepthan(o, bestwep)) {
bestwep = o;
// inherit damage from hasattack flag
if (strlen(retflag[i]->text)) {
flag_t *damflag;
damflag = hasflag(bestwep->flags, F_DAM);
if (damflag) {
free(damflag->text);
damflag->text = strdup(retflag[i]->text);
}
}
} 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)) {
bestwep = o;
}
}
}
if (bestwep && (bestwep->pile->owner == NULL)) {
// ie. best weapon is an innate attack
bestwep = NULL;
}
if (op) {
killobpile(op);
}
return bestwep;
}
int getbodyparthitchance(enum BODYPART bp) {
switch (bp) {
case BP_EYES: return 1;
case BP_HEAD: return 2;
case BP_WAIST: return 3;
case BP_HANDS: return 3;
case BP_FEET: return 3;
case BP_LEGS: return 4;
case BP_SHOULDERS: return 4;
case BP_BODY: return 6;
default: break;
}
return 0; // ie rings, ears, weapon
}
char *getbodypartname(lifeform_t *lf, enum BODYPART bp) {
flag_t *f;
if (lf) {
f = lfhasflagval(lf, F_BODYPARTNAME, bp, NA, NA, NULL);
if (f) {
return f->text;
}
}
switch (bp) {
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_BODY:
return "body";
case BP_SHOULDERS:
return "shoulders";
case BP_WAIST:
return "waist";
case BP_LEGS:
return "legs";
case BP_FEET:
return "feet";
}
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_BODY:
case BP_LEGS:
case BP_FEET:
return "on";
case BP_EYES:
case BP_SHOULDERS:
return "over";
case BP_WAIST:
return "around";
}
return "unknown";
}
object_t *getequippedob(obpile_t *op, enum BODYPART bp) {
object_t *o;
for (o = op->first; o ; o = o->next) {
if (hasflagval(o->flags, F_EQUIPPED, bp, NA, NA, NULL)) {
return o;
}
}
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;
}
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;
}
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;
}
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 = 8; // deafult
range = 2 + (getskill(lf, SK_LISTEN)*2);
return range;
}
int gethidemodifier(lifeform_t *lf) {
int modifier = 0;
reason = E_OK;
if (!safetorest(lf)) {
if (getskill(lf, SK_STEALTH) == PR_EXPERT) {
// can still hide, but with a modifier
modifier = -4;
} else if (getskill(lf, SK_STEALTH) == PR_MASTER) {
// can still hide, but with a modifier
modifier = -2;
} else {
reason = E_IMPOSSIBLE;
modifier = -100; // special case
}
}
// no stealth skill?
if (!getskill(lf, SK_STEALTH)) {
modifier = -5;
}
return modifier;
}
int gethitdice(lifeform_t *lf) {
if (isplayer(lf) || lfhasflag(lf, F_VARLEVEL)) {
return lf->level;
}
return (lf->maxhp / 4);
}
int gethitdicerace(race_t *r) {
flag_t *f;
f = hasflag(r->flags, F_HITDICE);
if (f) {
int ndice,nsides,bonus;
texttodice(f->text ? f->text : DEF_HITDICE, &ndice,&nsides,&bonus);
return ((ndice*nsides)+bonus) / 4;
}
return 1;
}
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_BOLDBLUE; break;
case H_FULL: col = C_BOLDGREEN; break;
case H_NONE: col = C_GREEN; break;
case H_PECKISH: col = C_GREY; break;
case H_HUNGRY: col = C_BROWN; 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;
}
int getlastdir(lifeform_t *lf) {
flag_t *f;
f = lfhasflag(lf, F_LASTDIR);
if (f) {
return f->val[0];
}
return D_NONE;
}
int getlfaccuracy(lifeform_t *lf, object_t *wep) {
flag_t *f;
object_t *o;
int acc = 0,i;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
// get weapon
if (wep) {
acc = getobaccuracy(wep, lf);
} else {
acc = 100; // innate attack
}
// 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];
}
}
// adjust for bulky armour/shield, or injuries
getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_INJURY, F_SHIELDPENALTY, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (f->id == F_SHIELDPENALTY) {
acc -= adjustshieldpenalty(lf, f->val[0]);
} else if (f->id == F_ARMOURPENALTY) {
acc -= adjustarmourpenalty(lf, f->val[0]);
} else if (f->id == F_INJURY) {
if (f->val[0] == IJ_EYELIDSCRAPED) {
acc -= 20;
} else if (f->val[0] == IJ_FINGERBROKEN) {
acc -= 10;
} else if (f->val[0] == IJ_SHOULDERDISLOCATED) {
acc -= 20;
}
}
}
// modify for weilder's level
acc += (lf->level * 2);
if (lfhasflag(lf, F_RAGE)) {
// assume maximum dex
acc += 50;
} else {
int dexmod;
// modify with dexterity
dexmod = getstatmod(lf, A_DEX);
// double dex penalties when dual weilding
if (isdualweilding(lf) && (dexmod < 0)) {
dexmod *= 2;
}
acc += dexmod;
}
// modify for blindness
if (isblind(lf)) {
acc -= 50;
}
// modify for being on the ground
if (isprone(lf)) {
acc -= 40;
}
// 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;
amt = (f->lifetime/TM_DRUNKTIME)+1;
if (hasjob(lf, J_PIRATE)) {
acc += (10*amt);
} else {
acc -= (10*amt);
}
}
// modify for nausea
if (lfhasflag(lf, F_NAUSEATED)) {
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 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 && (f->val[1] != NA)) {
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;
getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_SHIELDPENALTY, F_NONE);
for (i = 0; i < nretflags; i++) {
if (retflag[i]->val[0] != NA) chance += retflag[i]->val[0];
if (retflag[i]->val[1] != NA) chance += retflag[i]->val[1];
}
return chance;
}
int getnightvisrange(lifeform_t *lf) {
int range = 0; // default
flag_t *f;
flag_t *retflag[MAXCANDIDATES];
int nretflags,i;
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];
}
}
limit(&range, 0, MAXVISRANGE);
return range;
}
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;
switch (bp) {
case BP_RIGHTFINGER:
case BP_LEFTFINGER:
o = getequippedob(lf->pack, BP_HANDS);
if (o) return o;
else return getequippedob(lf->pack, bp);
break;
case BP_BODY:
o = getequippedob(lf->pack, BP_SHOULDERS);
if (o) return o;
else return getequippedob(lf->pack, bp);
break;
default:
break;
}
return getequippedob(lf->pack, bp);
}
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);
}
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;
return &tempglyph;
}
f = lfhasflag(lf, F_GLYPH);
if (f) {
tempglyph.ch = f->text[0];
return &tempglyph;
}
return &lf->race->glyph;
}
enum MATERIAL getlfmaterial(lifeform_t *lf) {
if (lf->race->id == 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;
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,maxattacks;
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,
// we get one more attack
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 (min) *min = minattacks;
if (max) *max = maxattacks;
nattacks = rnd(minattacks,maxattacks);
return nattacks;
}
float getmaxcarryweight(lifeform_t *lf) {
float max;
float mod;
enum ATTRBRACKET sbrack;
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;
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;
max = getlfweight(lf, B_NOOBS) * 2; // twice your body weight
return max;
}
float getmaxstamina(lifeform_t *lf) {
int stam = 0;
if (lfhasflagval(lf, F_INJURY, IJ_LUNGCOLLAPSED, NA, NA, NULL)) {
return 0;
}
stam = (getattr(lf, A_CON) / 2);
stam += (getskill(lf, SK_ATHLETICS)*2);
return stam;
}
int getmr(lifeform_t *lf) {
int amt = 0;
sumflags(lf->flags, F_RESISTMAG, &amt, NULL, NULL);
return amt;
}
// 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];
if (f->id == F_VISRANGEMOD) {
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;
}
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_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_INJURY) {
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) {
speed -= 10;
}
}
// flying in low gravity?
if (lfhasflag(lf, F_GRAVLESSENED) && lfhasflag(lf, F_FLYING)) {
speed -= 5;
}
switch (isburdened(lf)) {
case BR_NONE:
break;
case BR_BURDENED:
speed += 5;
break;
case BR_STRAINED:
case BR_OVERLOADED:
speed += 10;
break;
}
if (lfhasflag(lf, F_SNEAK)) {
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) {
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";
}
return "walk";
}
char *getmoveverbother(lifeform_t *lf) {
if (lfhasflag(lf, F_FLYING)) {
return "flies";
} else if (lfhasflag(lf, F_LEVITATING)) {
return "floats";
}
if (lfhasflag(lf, F_FLEEFROM)) {
return "flees";
}
return "walks";
}
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) {
if (lfhasflag(lf, F_FLYING)) {
if (isplayer(lf)) return "fly";
else return "flies";
} else if (onpurpose) { // TODO: check if we are using a rope
if (dir == D_DOWN) {
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, B_TRUE);
}
char *real_getlfname(lifeform_t *lf, char *buf, int usevis) {
char descstring[BUFLEN];
char jobstring[BUFLEN];
char the[6];
char lname[BUFLEN];
job_t *j;
flag_t *f;
// '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, lf->race->name);
} else {
if (ispetof(lf, player)) {
strcpy(the, "your ");
} else {
strcpy(the, "the ");
}
strcpy(lname, lf->race->name);
}
// construct description string
strcpy(descstring, "");
if (lfhasflag(lf, F_FROZEN)) {
strcat(descstring, "frozen ");
}
if (lfhasflag(lf, F_HEADLESS)) {
strcat(descstring, "headless ");
}
// construct job string
strcpy(jobstring, "");
if (!lfhasflag(lf, F_NOJOBTEXT) && !lfhasflag(lf, F_NAME) && !lfhasflag(lf, F_UNIQUE)) {
j = getjob(lf);
if (j) {
snprintf(jobstring, BUFLEN, " %s", j->name);
jobstring[1] = tolower(jobstring[1]);
}
}
if (isplayer(lf)) {
snprintf(buf, BUFLEN, "you");
} else {
//if (isblind(player)) {
if (usevis && !cansee(player, lf)) {
snprintf(buf, BUFLEN, "something");
} else {
if (lf->race->id == R_DANCINGWEAPON) {
object_t *wep;
wep = getweapon(lf);
if (wep) {
char obname[BUFLEN];
real_getobname(wep, obname, 1, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_FALSE);
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, B_TRUE);
}
char *real_getlfnamea(lifeform_t *lf, char *buf, int usevis) {
if (isplayer(lf)) {
snprintf(buf, BUFLEN, "you");
} else {
char buf2[BUFLEN];
char the[6];
real_getlfname(lf, buf2, usevis);
if (lfhasflag(lf, F_NAME) || lfhasflag(lf, F_UNIQUE)) {
strcpy(the, "");
} else {
if (ispetof(lf, player)) {
strcpy(the, "your ");
} else {
if (isvowel(lf->race->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;
f = hasflag(lf->flags, F_SIZE);
assert(f);
return f->val[0];
}
float getlfweight(lifeform_t *lf, int withobs) {
float weight = 0;
weight = lf->race->weight;
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;
}
object_t *getshield(lifeform_t *lf) {
object_t *o;
o = getequippedob(lf->pack, BP_SECWEAPON);
if (o) {
if (isshield(o)) return o;
// master two-weaponers can use their second weapon as a shield.
if ((getskill(lf, SK_TWOWEAPON) >= PR_MASTER) && isweapon(o) && getweaponskill(lf, o)) {
return o;
}
}
return NULL;
}
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;
}
char *getplayername(char *buf) {
flag_t *f;
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];
job_t *j;
getplayername(pname);
j = getjob(player);
if (j) {
snprintf(buf, BUFLEN, "%s the %s", pname, j->name);
} else {
snprintf(buf, BUFLEN, "%s the %s", pname, player->race->name);
}
return buf;
}
int getpoisondamchance(enum POISONTYPE ptype) {
int chance = 0;
switch (ptype) {
case P_FOOD:
case P_GAS:
case P_VENOM:
case P_COLD:
chance = 33;
break;
default:
chance = 0;
break;
}
return chance;
}
char *getpoisondamverb(enum POISONTYPE ptype) {
switch (ptype) {
case P_FOOD:
case P_VENOM:
return "vomit";
case P_GAS:
case P_COLD:
return "cough";
default:
break;
}
return "";
}
char *getpoisondesc(enum POISONTYPE ptype) {
switch (ptype) {
case P_FOOD:
case P_VENOM:
case P_GAS:
case P_WEAKNESS:
return "Poisoned";
case P_COLD:
return "Sick";
default:
break;
}
return "Poisoned";
}
char *getpoisonname(enum POISONTYPE ptype) {
switch (ptype) {
case P_COLD:
return "hypothermia";
case P_FOOD:
return "food poisoning";
case P_GAS:
return "gas inhalation";
case P_VENOM:
return "venom poisoning";
case P_WEAKNESS:
return "weakening poison";
default:
break;
}
return "";
}
enum POISONSEVERITY getpoisonseverity(enum POISONTYPE ptype) {
switch (ptype) {
case P_COLD:
return PS_DISEASE;
default:
break;
}
// default is not too bad.
return PS_POISON;
}
int getraceclass(lifeform_t *lf) {
return lf->race->raceclass->id;
}
int getracerarity(map_t *map, enum RACE rid) {
race_t *r;
int rarity = -1;
r = findrace(rid);
if (r) {
flag_t *f;
f = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL);
if (!f) {
if (map) {
f = hasflagval(r->flags, F_RARITY, map->habitat->id, NA, NA, NULL);
} else {
f = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL);
}
}
if (f) {
// ignore habitat for now!
rarity = f->val[1];
}
}
return rarity;
}
object_t *getrandomarmour(lifeform_t *lf) {
object_t *o;
object_t *poss[MAXBODYPARTS];
object_t **hitposition;
int hitchance[MAXBODYPARTS];
int nposs = 0;
int maxroll = 0;
int i,n,idx;
int sel;
// make a list of all valid armour
getarmourrating(lf, poss, hitchance, &nposs);
if (!nposs) return NULL;
maxroll = 0;
for (i = 0; i < nposs; i++) {
maxroll += hitchance[i];
}
// now figure out chances of each one getting hit
hitposition = malloc(maxroll * sizeof(object_t *));
idx = 0;
for (i = 0; i < nposs; 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;
}
// pick a random major body part
// ie. head, body, arms/hands, legs
enum BODYPART getrandomcorebp(lifeform_t *lf) {
int cutoff[4],nparts = 0,i,max = 0,num;
enum BODYPART bp[4],selbp = BP_NONE;
if (hasbp(lf, BP_BODY)) bp[nparts++] = BP_BODY;
if (hasbp(lf, BP_HANDS)) bp[nparts++] = BP_HANDS;
if (hasbp(lf, BP_LEGS)) bp[nparts++] = BP_LEGS;
if (hasbp(lf, BP_HEAD)) bp[nparts++] = BP_HEAD;
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;
}
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 *getrandomrace(cell_t *c, int forcedepth) {
//int rarity;
race_t *r;
race_t *poss[MAXRANDOMLFCANDIDATES];
int nposs = 0;
int selidx;
int db = B_FALSE;
int depth;
int hdmin,hdmax;
enum RARITY wantrr = RR_COMMON;
// determine rarity of lf to generate
if (forcedepth != NA) {
depth = forcedepth;
} else {
depth = getmapdifficulty((c && c->map) ? c->map : NULL);
}
gethitdicerange(depth, &hdmin, &hdmax, RARITYVARIANCELF, B_TRUE);
// pick rr...
wantrr = RR_COMMON;
while ((wantrr < RR_VERYRARE) && onein(3)) {
wantrr++;
}
if (db) dblog("finding random lf with hitdice %d-%d and rr <= %d\n",hdmin,hdmax, wantrr);
// 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 = gethitdicerace(r);
if ((thishd < hdmin) || (thishd > hdmax)) {
valid = B_FALSE;
} else {
// correct rarity?
rarflag = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL);
if (!rarflag) {
if (c) {
rarflag = hasflagval(r->flags, F_RARITY, c->habitat->id, NA, NA, NULL);
} else {
rarflag = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL);
}
}
if (rarflag) {
if ((rarflag->val[2] == NA) || (rarflag->val[2] <= wantrr)) {
valid = B_TRUE;
thisrr = rarflag->val[2];
}
} else {
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) {
// already at lowest hitdice?
if ((hdmax >= maxmonhitdice) && (hdmin <= 0)) {
// give up
if (db) dblog("no possible lf at all! giving up.");
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];
return r;
}
race_t *getreallyrandomrace(enum RACECLASS wantrc) {
race_t **poss;
race_t *r;
int nposs = 0;
int sel;
int count = 0;
// count races
for (r = firstrace ; r ; r = r->next) {
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 ((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 = 0;
skill_t *sk;
sel = rnd(0,MAXSKILLS-1);
for (sk = firstskill ; sk ; sk = sk->next) {
if (count == sel) return sk->id;
count++;
}
return SK_NONE;
}
object_t *getrestob(lifeform_t *lf) {
object_t *o,*bestob = NULL;
int bestval = -1;
flag_t *f;
for (o = lf->pack->first; o ; o = o->next) {
f = hasflag(o->flags, F_HELPSREST);
if (f) {
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(int volume) {
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;
long amtneeded;
mod = getstatmod(lf, A_IQ);
amtneeded = pctof(100 - mod, SKILLXPPERPOINT);
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;
// <9 = penalty
// 9 = average
// >9 = bonus up to 50%
base = getattr(lf, att) - 9;
mod = (int)((base / 9.0) * 50.0);
return mod;
}
enum ATTRBRACKET getattrbracket(int attrval, enum ATTRIB whichatt, char *buf) {
if (attrval <= 2) {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "hideous"); break;
case A_CON:
strcpy(buf, "frail"); break;
case A_DEX:
strcpy(buf, "uncoordinated"); break;
case A_IQ:
strcpy(buf, "vegetable"); break;
case A_STR:
strcpy(buf, "helpless"); break;
case A_WIS:
strcpy(buf, "witless"); break;
default:
strcpy(buf, "?extralow?"); break;
}
}
return AT_EXLOW;
} else if (attrval <= 6) {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "repulsive"); break;
case A_CON:
strcpy(buf, "sickly"); break;
case A_DEX:
strcpy(buf, "oafish"); break;
case A_IQ:
strcpy(buf, "animal"); break;
case A_STR:
strcpy(buf, "feeble"); break;
case A_WIS:
strcpy(buf, "reckless"); break;
default:
strcpy(buf, "?verylow?"); break;
}
}
return AT_VLOW;
} else if (attrval <= 8) {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "ugly"); break;
case A_CON:
strcpy(buf, "unhealthy"); break;
case A_DEX:
strcpy(buf, "clumsy"); break;
case A_IQ:
strcpy(buf, "dim-witted"); break;
case A_STR:
strcpy(buf, "very weak"); break;
case A_WIS:
strcpy(buf, "foolish"); break;
default:
strcpy(buf, "?low?"); break;
}
}
return AT_LOW;
} else if (attrval <= 10) {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "unattractive"); break;
case A_CON:
strcpy(buf, "unfit"); break;
case A_DEX:
strcpy(buf, "awkward"); break;
case A_IQ:
strcpy(buf, "dopey"); break;
case A_STR:
strcpy(buf, "weak"); break;
case A_WIS:
strcpy(buf, "naive"); break;
default:
strcpy(buf, "?lt_average?"); break;
}
}
return AT_LTAVERAGE;
} else if (attrval <= 12) {
if (buf) {
switch (whichatt) {
case A_CHA:
case A_CON:
case A_DEX:
case A_STR:
case A_IQ:
case A_WIS:
strcpy(buf, "average"); break;
default:
strcpy(buf, "?average?"); break;
}
}
return AT_AVERAGE;
} else if (attrval <= 14) {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "attractive"); break;
case A_CON:
strcpy(buf, "healthy"); break;
case A_DEX:
strcpy(buf, "dextrous"); break;
case A_IQ:
strcpy(buf, "smart"); break;
case A_STR:
strcpy(buf, "strong"); break;
case A_WIS:
strcpy(buf, "prudent"); break;
default:
strcpy(buf, "?gt_average?"); break;
}
}
return AT_GTAVERAGE;
} else if (attrval <= 16) {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "alluring"); break;
case A_CON:
strcpy(buf, "very fit"); break;
case A_DEX:
strcpy(buf, "nimble"); break;
case A_IQ:
strcpy(buf, "enlightened"); break;
case A_STR:
strcpy(buf, "mighty"); break;
case A_WIS:
strcpy(buf, "astute"); break;
default:
strcpy(buf, "?high?"); break;
}
}
return AT_HIGH;
} else if (attrval <= 18) {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "beautiful"); break;
case A_CON:
strcpy(buf, "hardy"); break;
case A_DEX:
strcpy(buf, "agile"); break;
case A_IQ:
strcpy(buf, "genius"); break;
case A_STR:
strcpy(buf, "powerful"); break;
case A_WIS:
strcpy(buf, "wise"); break;
default:
strcpy(buf, "?veryhigh?"); break;
}
}
return AT_VHIGH;
} else {
if (buf) {
switch (whichatt) {
case A_CHA:
strcpy(buf, "stunning"); break;
case A_CON:
strcpy(buf, "very hardy"); break;
case A_DEX:
strcpy(buf, "very agile"); break;
case A_IQ:
strcpy(buf, "supergenius"); break;
case A_STR:
strcpy(buf, "titanic"); break;
case A_WIS:
strcpy(buf, "sagely"); break;
default:
strcpy(buf, "?exhigh?"); break;
}
}
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 *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 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(int str) {
enum ATTRBRACKET sb;
int speed = 0;
sb = getattrbracket(str, A_STR, NULL);
switch (sb) {
case AT_EXLOW:
case AT_VLOW:
speed = 1;
break;
case AT_LOW:
case AT_LTAVERAGE:
speed = 2;
break;
case AT_AVERAGE:
speed = 3;
break;
case AT_GTAVERAGE:
case AT_HIGH:
speed = 4;
break;
case AT_VHIGH:
speed = 5;
break;
default:
case AT_EXHIGH:
speed = 6;
break;
// gun is 10
}
return speed;
}
int getturnspeed(lifeform_t *lf) {
int speed;
speed = getmovespeed(lf) / 2;
limit(&speed, 1, NA);
return speed;
}
void getwantdistance(lifeform_t *lf, int *min, int *max, int attacking) {
flag_t *f;
// 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];
}
} else {
// default - stay with 1-3 cells
*min = 1;
*max = 3;
f = lfhasflag(lf, F_FOLLOWRANGE);
if (f) {
*min = f->val[0];
*max = f->val[1];
}
}
}
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;
}
enum SKILLLEVEL getweaponskill(lifeform_t *lf, object_t *o) {
skill_t *sk;
sk = getobskill(o);
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;
// 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 givejob(lifeform_t *lf, enum JOB jobid) {
job_t *j;
flag_t *f;
int i;
//int rollhp = B_FALSE;
int rollmp = B_FALSE;
int rollatt[MAXATTS];
int db = B_FALSE;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
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);
}
}
// 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
for (f = j->flags->first ; f ; f = f->next) {
int ignorethis = B_FALSE;
int val[3];
int id;
char *text;
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_MAXHPMOD:
case F_JOBATTRMOD:
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 it
givestartskills(lf, lf->flags);
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 = 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);
}
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;
f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL);
if (f) {
// monk fists do more damage
changeflagtext(f, "1d4");
}
} else if (j->id == J_PIRATE) {
flag_t *f;
f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL);
if (f) {
f->val[0] = OT_HOOKHAND;
changeflagtext(f, "1d4");
}
} 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);
}
}
}
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 coin");
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;
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 && hasflagval(o->flags, F_GOESON, f->val[0], NA, NA, NULL)) {
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);
}
for (f = o->flags->first ; 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) {
newflag = addflag_real(lf->flags, f->val[0], f->val[1], f->val[2], NA, f->text, lifetimeval, known, 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)) {
makeknown(o->type->id);
// in some cases, identify the object fully
// (ie. make +xxx bonuses known too)
if (isplayer(lf)) {
if (hasflag(o->flags, F_IDWHENUSED)) {
identify(o);
}
}
}
}
int giveskill(lifeform_t *lf, enum SKILL id) {
flag_t *f, *newf;
skill_t *sk;
sk = findskill(id);
if (!sk) {
return B_TRUE;
}
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]++;
}
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, NA, 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.
// remember that these from from a SKILL, so that
// they are invalidated if we get polymorphed.
if (id == SK_ATHLETICS) {
newf = addflag(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL);
newf->lifetime = FROMSKILL;
} else if (id == SK_COOKING) {
makeknown(OT_POT_WATER);
} else if (id == SK_LORE_ARCANA) {
newf = addflag(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL);
newf->lifetime = FROMSKILL;
} else if (id == SK_METALWORK) {
newf = hasflagval(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL);
if (!newf) {
newf = addflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL);
newf->lifetime = FROMSKILL;
}
} else if (id == SK_SEWING) {
newf = hasflagval(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL);
if (!newf) {
newf = addflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL);
newf->lifetime = FROMSKILL;
}
} else if (id == SK_THIEVERY) {
newf = addflag(lf->flags, F_CANWILL, OT_A_STEAL, NA, NA, NULL);
newf->lifetime = FROMSKILL;
} else if (id == SK_TRAPS) {
newf = addflag(lf->flags, F_CANWILL, OT_A_DISARM, NA, NA, NULL);
newf->lifetime = FROMSKILL;
}
statdirty = B_TRUE; // in case skill changes your stats
}
// special effects based on skill level
if (id == SK_ATHLETICS) {
if (f->val[1] == PR_ADEPT) {
newf = addflag(lf->flags, F_CANWILL, OT_A_TUMBLE, 2, 2, NULL);
newf->lifetime = FROMSKILL;
} else if (f->val[1] == PR_EXPERT) {
newf = addflag(lf->flags, F_CANWILL, OT_A_JUMP, 3, 3, NULL);
newf->lifetime = FROMSKILL;
}
} else if (id == SK_CARTOGRAPHY) {
if (f->val[1] == PR_SKILLED) {
addflag(lf->flags, F_PHOTOMEM, B_TRUE, NA, NA, NULL);
}
if (f->val[1] == PR_MASTER) {
newf = addflag(lf->flags, F_CANWILL, OT_S_MAPPING, 50, 50, "pw:1;");
newf->lifetime = FROMSKILL;
}
} else if (id == SK_COOKING) {
if (f->val[1] == PR_ADEPT) {
newf = addflag(lf->flags, F_CANWILL, OT_A_COOK, NA, NA, NULL);
newf->lifetime = FROMSKILL;
}
} 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_SPELLCASTING) {
if (f->val[1] == PR_SKILLED) {
newf = hasflagval(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL);
if (!newf) {
newf = addflag(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL);
newf->lifetime = FROMSKILL;
}
}
} else if (id == SK_STEALTH) {
if (f->val[1] == PR_BEGINNER) {
newf = addflag(lf->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL);
newf->lifetime = FROMSKILL;
}
} else if (id == SK_TECHUSAGE) {
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])) {
// then make it known!
makeknown(ot->id);
if (isplayer(lf)) {
object_t *o;
o = hasob(lf->pack, ot->id);
if (o) {
char buf[BUFLEN];
getobname(o, buf, o->amt);
msgnocap("%c - %s", o->letter, buf);
}
}
}
}
}
} else if (id == SK_TWOWEAPON) {
if (f->val[1] == PR_EXPERT) {
addflag(lf->flags, F_CANWILL, OT_A_FLURRY, 3, 3, "pw:1;");
}
} else if (id == SK_SS_ALLOMANCY) {
// give all allomantic spells
mayusespellschool(lf->flags, SS_ALLOMANCY, F_CANCAST);
} 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);
}
*/
}
if (isweaponskill(id)) {
if (f->val[1] == PR_MASTER) {
if (!hasflagval(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL)) {
addflag(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, 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 B_FALSE;
}
int giveskilllev(lifeform_t *lf, enum SKILL id, enum SKILLLEVEL slev) {
flag_t *f;
f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL);
if (!f) {
// give one rank of the skill
giveskill(lf, id);
f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL);
}
if (f) {
f->val[1] = slev;
} else {
assert(1 == 0);
}
return B_FALSE;
}
// 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;
flag_t *f;
char buf[BUFLEN],buf2[BUFLEN];
int db = B_FALSE;
obpile_t *op;
map_t *targmap;
enum LFSIZE maxobsize = SZ_MAX;
if (targob) {
cell_t *c;
op = targob->contents;
c = getoblocation(targob);
targmap = c->map;
maxobsize = getobsize(targob);
} else {
op = lf->pack;
targmap = lf->cell->map;
}
if (db) {
snprintf(buf2, BUFLEN, "calling givestartobs for %s",lf->race->name);
}
// handle autoweapon
if (lf && hasflag(fp, F_SELECTWEAPON)) {
skill_t *sk;
objecttype_t *poss[MAXSKILLS];
int nposs = 0, i;
// 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;
if (isplayer(lf)) {
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.
getplayernamefull(buf2);
snprintf(buf, BUFLEN, "%s, select your starting weapon:", buf2);
initprompt(&prompt, buf);
for (i = 0; i < nposs; i++) {
addchoice(&prompt, ch++, poss[i]->name, NULL, poss[i], NULL);
}
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;
}
}
} else {
// not player - pick randomly.
i = rnd(0,nposs-1);
ot = poss[i];
}
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);
// give one extra rank of skill in this weapon
sk = getobskill(o);
giveskill(lf, sk->id);
}
}
} // end if lf && selectweapon
// give start objects and id them
for (f = fp->first ; 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) {
dblog("couldnt give startob: %s", text);
}
}
}
} else if (id == F_STARTOBRND) {
if (rnd(1,100) <= val[0]) {
int depthmod;
depthmod = val[1];
switch (depthmod) {
case NA: depthmod = 0; break;
case RANDOM: depthmod = rnd(0,MAXDEPTH); break;
default: break;
}
// if (getrandomobofsize(targmap, buf, maxobsize)) {
if (real_getrandomob(targmap, buf, RO_HOLDABLE, NA, targmap->depth + depthmod, NA, maxobsize)) {
o = addob(op, buf);
}
}
} else if (id == F_STARTOBDT) {
if (rnd(1,100) <= val[0]) {
if (db) {
snprintf(buf2, BUFLEN, "calling startobdt");
}
if ( real_getrandomob(targmap, buf, RO_DAMTYPE, val[1], NA, NA, maxobsize)) {
if (db) snprintf(buf2, BUFLEN, "finished startobdt successfuly.");
o = addob(op, buf);
} else {
if (db) snprintf(buf2, BUFLEN, "finished startobdt, failed.");
}
//assert(strlen(buf) > 0);
}
} else if (id == F_STARTOBCLASS) {
if (rnd(1,100) <= val[0]) {
if (db) {
snprintf(buf2, BUFLEN, "calling startobclass");
}
//obdb = B_TRUE;
//if (getrandomobwithclass(targmap, val[1], buf, val[2])) {
if (real_getrandomob(targmap, buf, RO_OBCLASS, val[1], getmapdifficulty(targmap) + val[2], NA, maxobsize)) {
if (db) snprintf(buf2, BUFLEN, "finished startobclass, success.");
o = addob(op, buf);
} else {
//obdb = B_FALSE;
if (db) snprintf(buf2, BUFLEN, "finished startobclass. couldnt find an object.");
}
//if (strlen(buf) <= 0);
}
}
// added an object?
if (o) {
if (lf) {
// undead can't have cursed 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);
}
}
}
}
// now remove startob flags so we don't get them again!
killflagsofid(fp, F_STARTOB);
killflagsofid(fp, F_STARTOBDT);
killflagsofid(fp, F_STARTOBCLASS);
// SPECIAL CASES
if (lf) {
if (lf->race->id == R_JAILER) {
regionoutline_t *ro;
region_t *r;
regionthing_t *rt = NULL;
int i;
o = addobfast(lf->pack, OT_MAP);
assert(o);
// find first worldmap village
r = findregion(RG_WORLDMAP);
ro = r->outline;
for (i = 0; i < ro->nthings; i++) {
regionthing_t *thisthing;
thisthing = &ro->thing[i];
if ((thisthing->whatkind == RT_HABITAT) && (thisthing->value == H_VILLAGE)) {
rt = thisthing;
break;
}
}
addflag(o->flags, F_MAPTO, rt->x, rt->y, OT_GATEWOOD, "a village");
}
// 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;
wantval = f->val[1];
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);
}
int gotosleep(lifeform_t *lf, int onpurpose) {
if (lfhasflag(lf, F_CAFFEINATED)) {
if (isplayer(lf)) {
msg("Your caffeine high prevents you from sleeping.");
}
return B_TRUE;
}
if (onpurpose) taketime(lf, getactspeed(lf));
addflag(lf->flags, F_ASLEEP, B_TRUE, lfhasflag(lf, F_MEDITATES) ? ST_MEDITATING : ST_ASLEEP, onpurpose ? B_TRUE : NA, NULL);
return B_FALSE;
}
int hasfreeaction(lifeform_t *lf) {
if (isdead(lf)) return B_FALSE;
if (isimmobile(lf)) return B_FALSE;
if (lfhasflag(lf, F_CASTINGSPELL)) return B_FALSE;
if (lfhasflag(lf, F_EATING)) return B_FALSE;
return B_TRUE;
}
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;
}
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
int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype) {
char lfname[BUFLEN];
char *desc = NULL;
enum INJURY inj = IJ_NONE;
enum BODYPART bp2;
object_t *wep = NULL;
int howlong;
if (where == BP_NONE) return B_TRUE;
if (!hasbp(lf, where)) 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 (damtype == DT_BASH) {
switch (where) {
case BP_BODY: inj = IJ_RIBCRACKED; desc = strdup("ribs are cracked^carrying capacity halved"); break;
case BP_HANDS:
switch (rnd(1,2)) {
case 1:
inj = IJ_FINGERBROKEN; desc = strdup("finger is broken^-10% accuracy"); break;
case 2:
inj = IJ_SHOULDERDISLOCATED; desc = strdup("shoulder is dislocated^-20% accuracy, cannot weild heavy weapons"); break;
}
break;
case BP_HEAD:
switch (rnd(1,4)) {
case 1:
if (!eyesshaded(lf)) {
inj = IJ_BLACKEYE;
desc = strdup("eye is bruised^vision range halved");
}
break;
case 2:
inj = IJ_CONCUSSION;
desc = strdup("brain is concussed^random movement");
break;
case 3:
inj = IJ_WINDPIPECRUSHED;
desc = strdup("windpipe is crushed^fitness penalty");
break;
case 4:
inj = IJ_NOSEBROKEN;
desc = strdup("nose is broken^charisma penalty");
break;
}
break;
case BP_LEGS:
if (onein(3)) {
inj = IJ_LEGBROKEN;
desc = strdup("leg is broken^movement speed greatly lowered"); break;
} else {
switch (rnd(1,2)) {
case 1:
inj = IJ_LEGBRUISE;
desc = strdup("leg is bruised^movement speed lowered"); break;
case 2:
inj = IJ_ANKLESWOLLEN;
desc = strdup("ankle is swollen^cannot wear/remove boots"); break;
}
}
default: break;
}
} else if (damtype == DT_SLASH) {
switch (where) {
case BP_BODY:
if (pctchance(10)) {
if (isplayer(lf)) {
msg("^BYour heart is pierced!");
} else if (cansee(player, lf)) {
getlfname(lf, lfname);
msg("^%c%s%s heart is pierced!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname));
}
if (lf->hp > 0) lf->hp = 0;
} else {
inj = IJ_CHESTBLEED; desc = strdup("chest is bleeding^damage from enemies is increased"); break;
}
break;
case BP_HANDS:
switch (rnd(1,4)) {
case 1:
inj = IJ_HANDBLEED; desc = strdup("hand is bleeding^attacking will cause damage"); break;
case 2:
inj = IJ_TENDONCUT; desc = strdup("right flexor tendon is cut^cannot weild weapons"); break;
case 3:
inj = IJ_ARTERYPIERCE; desc = strdup("radial artery is pierced^1d8 damage per turn"); break;
case 4: // severed finger
if (onein(2)) bp2 = BP_RIGHTFINGER;
else bp2 = BP_LEFTFINGER;
if (hasbp(lf, bp2)) {
object_t *o;
char buf[BUFLEN];
addob(lf->cell->obpile, "severed finger");
o = getequippedob(lf->pack, bp2);
addflag(lf->flags, F_NOBODYPART, bp2, B_FROMINJURY, NA, NULL);
inj = IJ_FINGERMISSING;
sprintf(buf, "%s is severed^cannot wear rings on this hand", getbodypartname(lf, bp2));
desc = strdup(buf);
howlong = PERMENANT;
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)) {
char lfname[BUFLEN];
getobname(o,obname,o->amt);
getlfname(lf,lfname);
msg("%s%s %s drops to the ground.",lfname,getpossessive(lfname),noprefix(obname));
}
moveob(o, lf->cell->obpile, o->amt);
}
break;
}
break;
}
break;
case BP_HEAD:
if (pctchance(10)) {
if (isplayer(lf)) {
msg("^BYour brain is ruptured!");
} else if (cansee(player, lf)) {
getlfname(lf, lfname);
msg("^%c%s%s brain ruptures!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname));
}
if (lf->hp > 0) lf->hp = 0;
} else {
switch (rnd(1,2)) {
case 1:
inj = IJ_EYELIDSCRAPED; desc = strdup("eyelid is scraped^accuracy penalty"); break;
case 2:
inj = IJ_EYEDESTROYED; desc = strdup("right eye is destroyed^field of view halved");
howlong = PERMENANT;
break;
}
}
break;
case BP_LEGS:
switch (rnd(1,2)) {
case 1:
inj = IJ_LEGBLEED; desc = strdup("leg is bleeding^movement will cause damage"); break;
case 2:
inj = IJ_HAMSTRUNG; desc = strdup("left hamstring is torn^lower move speed, chance of falling");
break;
}
default: break;
}
} else if (damtype == DT_EXPLOSIVE) {
switch (where) {
case BP_BODY:
switch (rnd(1,2)) {
case 1: // collapsed lung
inj = IJ_LUNGCOLLAPSED;
desc = strdup("lungs have collapsed^lose all stamina points");
case 2:
inj = IJ_RIBCRACKED; desc = strdup("ribs are cracked^carrying capacity halved"); break;
break;
}
break;
case BP_HANDS:
// lose limb
if (onein(2)) bp2 = BP_WEAPON;
else bp2 = BP_SECWEAPON;
if (hasbp(lf, bp2)) {
object_t *o[2];
char buf[BUFLEN];
int i;
// drop anyting in that hand
o[0] = getequippedob(lf->pack, bp2);
if (bp2 == BP_WEAPON) o[1] = getequippedob(lf->pack, BP_RIGHTFINGER);
else o[1] = getequippedob(lf->pack, BP_LEFTFINGER);
addflag(lf->flags, F_NOBODYPART, bp2, B_FROMINJURY, NA, NULL);
inj = IJ_HANDMISSING;
sprintf(buf, "%s is destroyed^cannot use this hand", getbodypartname(lf, bp2));
desc = strdup(buf);
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);
getlfname(lf,lfname);
msg("%s%s %s drops to the ground.",lfname,getpossessive(lfname),noprefix(obname));
}
moveob(o[i], lf->cell->obpile, o[i]->amt);
}
}
}
break;
case BP_HEAD:
// burnt eyes
switch (rnd(1,2)) {
case 1: // ringing ears
inj = IJ_EARSRINGING;
desc = strdup("ears are ringing^cannot hear sounds");
case 2: // blinded
addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(50,100));
break;
}
break;
case BP_LEGS:
// lose limb
break;
default:
break;
}
}
if (inj == IJ_NONE) {
if (desc) free(desc);
return B_TRUE;
} else {
addtempflag(lf->flags, F_INJURY, inj, where, damtype, desc, howlong);
}
if (desc) free(desc);
// special effects from taking injuries
if (damtype == DT_SLASH) bleed(lf, B_SPLATTER);
switch (inj) {
case IJ_HAMSTRUNG:
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;
default:
break;
}
return B_FALSE;
}
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)) {
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) {
int temp = 0;
int radius = 0;
object_t *o;
// lf producing light itself?
sumflags(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;
}
}
}
return radius;
}
// return true on failure
int lockpick(lifeform_t *lf, 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;
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 (!makeduller(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);
}
}
return B_TRUE;
}
return B_FALSE;
}
void loseobflags(lifeform_t *lf, object_t *o, int kind) {
flag_t *f,*ff,*nextff;
for (f = o->flags->first ; 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;
// 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
for (ff = lf->flags->first ; 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 hasbp(lifeform_t *lf, enum BODYPART bp) {
if (lfhasflagval(lf, F_NOBODYPART, bp, NA, NA, NULL)) {
return B_FALSE;
}
return B_TRUE;
}
flag_t *hasactivespell(lifeform_t *lf, enum OBTYPE sid) {
return lfhasflagval(lf, F_BOOSTSPELL, sid, NA, NA, NULL);
}
// got line of fire to dest? if lof is blocked, return last cell in 'newdest'
//int haslof(lifeform_t *viewer, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) {
int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) {
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
if (loftype & LOF_WALLSTOP) {
if (cell->type->solid) {
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 our destination
if (cell->lf && (!isprone(cell->lf)) && (loftype & LOF_LFSSTOP)) {
// if not in first cell...
if (i != 0) {
// if not in last cell...
if (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) {
int numpixels;
int i;
int x1,y1;
int maxvisrange;
int xray = 0;
//int wentuphill = B_FALSE;
//int origheight;
//int shopwall;
flag_t *f;
int x2,y2;
map_t *map;
int currange;
int nvrange;
cell_t *retcell[MAXRETCELLS];
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;
// 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 (viewer->losdirty) {
precalclos(viewer);
viewer->losdirty = B_FALSE;
}
// can we use pre-calced los?
//
//if ((viewer->cell->map->lf == viewer) && viewer->los) {
if (viewer->los) {
return haslos_fast(viewer, dest);
}
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 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;
}
int isairborne(lifeform_t *lf) {
if (!lf) return B_FALSE;
if (lfhasflag(lf, F_FLYING)) {
return B_TRUE;
} else if (lfhasflag(lf, F_LEVITATING)) {
return B_TRUE;
} else if (lfhasflag(lf, F_ICESLIDE)) {
return B_TRUE;
}
return B_FALSE;
}
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;
}
// is lf behind otherlf?
int isbehind(lifeform_t *lf, lifeform_t *otherlf) {
int dir;
dir = getdirtowards(otherlf->cell, lf->cell, NULL, B_FALSE, DT_ORTH);
if (getrelativedir(otherlf, dir) == RD_BACKWARDS) {
return B_TRUE;
}
return B_FALSE;
}
// returns B_FALSE, B_TRUE, or B_FROMINJURY
int isbleeding(lifeform_t *lf) {
float hppct;
hppct = ((float)lf->hp / (float) lf->maxhp) * 100;
if (hppct <= 40) return B_TRUE;
if (lfhasflagval(lf, F_INJURY, NA, NA, DT_SLASH, NULL)) {
return B_FROMINJURY;
}
return B_FALSE;
}
int isblind(lifeform_t *lf) {
flag_t *f;
if (!lf) return B_FALSE;
f = lfhasflag(lf, F_ASLEEP);
if (f && (f->val[1] != ST_MEDITATING)) {
return B_TRUE;
}
if (lfhasflag(lf, F_BLIND)) {
return B_TRUE;
}
if (lfhasflagval(lf, F_NOBODYPART, BP_EYES, NA, NA, NULL)) {
if (!lfhasflag(lf, F_TREMORSENSE)) {
return B_TRUE;
}
}
return B_FALSE;
}
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 (hasflag(lf->flags, F_DRUNK)) {
reason = E_DRUNK;
return B_FALSE;
}
if (hasflag(lf->flags, F_UNIQUE)) {
reason = E_NOEFFECT; // generic error
return B_FALSE;
}
return B_TRUE;
}
int isdead(lifeform_t *lf) {
if (!lf->alive) return B_TRUE;
if (lf->hp <= 0) 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;
// shouldn't need this...
lf->cell = NULL;
// remove impossible flags...
// 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);
// kill any remaining obs
while (lf->pack->first) {
killob(lf->pack->first);
}
// kill object pile
free(lf->pack);
// kill flags
killflagpile(lf->flags);
// 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_DEAF)) return B_TRUE;
if (lfhasflagval(lf, F_INJURY, IJ_EARSRINGING, NA, NA, NULL)) 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;
}
int isfleeing(lifeform_t *lf) {
if (lfhasflag(lf, F_FLEEFROM)) {
return B_TRUE;
}
return B_FALSE;
}
int isfreebp(lifeform_t *lf, enum BODYPART bp) {
if (hasobwithflagval(lf->pack, F_EQUIPPED, bp, NA, NA, NULL)) 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 && hasflag(o->flags, F_TWOHANDED)) {
return B_FALSE;
}
}
return B_TRUE;
}
int isfriendly(lifeform_t *lf) {
if (lfhasflag(lf, F_FRIENDLY)) {
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_VHIGH) {
return B_TRUE;
}
return B_FALSE;
}
flag_t *isimmuneto(flagpile_t *fp, enum DAMTYPE dt) {
flag_t *f;
f = hasflagval(fp, F_DTIMMUNE, dt, NA, NA, NULL);
if (f) return f;
f = hasflagval(fp, F_DTIMMUNE, DT_ALL, NA, NA, NULL);
if (f) return f;
return NULL;
}
int isinbattle(lifeform_t *lf) {
int dir;
if (isblind(lf)) {
return B_FALSE;
}
for (dir = DC_N; dir <= DC_NW; dir++) {
cell_t *c;
c = getcellindir(lf->cell, dir);
if (c && c->lf && areenemies(lf, c->lf)) {
return B_TRUE;
}
}
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;
}
// 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;
}
return B_FALSE;
}
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 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 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) {
flag_t *f;
f = hasflagval(fp, F_DTRESIST, dt, NA, NA, NULL);
if (f) return f;
f = hasflagval(fp, F_DTRESIST, DT_ALL, NA, NA, NULL);
if (f) 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;
}
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;
}
job_t *addjob(enum JOB id, char *name) {
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->flags = addflagpile(NULL, NULL);
return a;
}
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;
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->skillxp = 0;
a->skillpoints = 0;
a->cell = cell;
a->alive = B_TRUE;
a->lastdam = strdup("nothing");
a->lastdamtype = DT_NONE;
if ((gamemode == GM_GAMESTARTED) && a->prev) {
a->timespent = a->prev->timespent + 1;
} else {
a->timespent = 0;
}
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->losdirty = B_TRUE;
a->nlos = 0;
//a->los = malloc(sizeof(cell_t *) * MAXVISRANGE);
a->los = malloc( sizeof(cell_t *) * ((MAXVISRANGE*2+1)*(MAXVISRANGE*2+1)));
//a->viscell = NULL;
a->viscell = malloc( sizeof(int) * ((MAXVISRANGE*2+1)*(MAXVISRANGE*2+1)));
a->eyeadjustment = 0;
// for ai
// avoid messages when equipping initial obs
a->created = B_FALSE;
a->pack = addobpile(a, 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);
// set race - this will inherit race flags and material
a->race = NULL;
setrace(a, rid, B_FALSE);
// set stamina AFTER setrace as it depends on your attribs
a->stamina = getmaxstamina(a);
// update other things
cell->lf = a;
// give start objetcs
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) && cansee(player, a)) {
needredraw = B_TRUE;
}
return a;
}
race_t *addrace(enum RACE id, char *name, float weight, char glyph, int glyphcolour, enum MATERIAL mat, enum RACECLASS raceclass) {
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->weight = weight;
a->glyph.ch = glyph;
a->glyph.colour = glyphcolour;
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;
}
skill_t *addskill(enum SKILL id, char *name, char *desc, int traintime) {
skill_t *a;
assert(!findskill(id));
// 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->desc = strdup(desc);
a->traintime = traintime;
a->nskilldesc = 0;
return a;
}
void addskilldesc(enum SKILL id, enum SKILLLEVEL lev, char *text, int wantmsg) {
skill_t *sk;
sk = findskill(id);
assert(sk);
sk->skilldesclev[sk->nskilldesc] = lev;
sk->skilldesctext[sk->nskilldesc] = strdup(text);
sk->skilldescmsg[sk->nskilldesc] = wantmsg;
sk->nskilldesc++;
}
void addtrail(lifeform_t *lf, int dir) {
object_t *footprint, *scent;
flag_t *fpflag;
// no tracks at all?
if (lf->cell->type->solid) {
return;
} else if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) {
return;
}
// footprints first
if (!isairborne(lf) && !lfhasflag(lf, F_NONCORPOREAL)) {
int fpdir;
if (getskill(lf, SK_PERCEPTION) >= PR_EXPERT) {
// no footprints!
return;
} else {
fpdir = dir;
}
footprint = hastrailof(lf->cell->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);
footprint = addobfast(lf->cell->obpile, OT_FOOTPRINT);
addtempflag(footprint->flags, F_TRAIL, lf->race->id, fpdir, S_SIGHT, buf, getfootprinttime(lf));
}
}
// now smell
scent = hastrailof(lf->cell->obpile, lf, OT_SCENT, &fpflag, NULL);
if (scent) {
assert(fpflag);
fpflag->lifetime = TM_SCENT;
} else {
char buf[BUFLENTINY];
snprintf(buf, BUFLENTINY, "%d", lf->id);
scent = addobfast(lf->cell->obpile, OT_SCENT);
addtempflag(scent->flags, F_TRAIL, lf->race->id, dir, S_SMELL, buf, TM_SCENT);
}
}
void adjustspeedforwater(lifeform_t *lf, int *speed) {
object_t *o;
flag_t *f;
if (!isairborne(lf)) {
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)) {
*amt = 0;
return;
}
if ((damtype == DT_MAGIC) && getmr(lf) && skillcheck(lf, SC_RESISTMAG, 10 + (*amt * 2), 0)) {
*amt = 0;
return;
}
// water normally doesn't hurt.
if ((damtype == DT_WATER) && !isvulnto(lf->flags, damtype)) {
*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) && lfhasflag(lf, F_FROZEN)) {
(*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 (isresistantto(lf->flags, damtype)) {
(*amt) /= 2;
}
f = isvulnto(lf->flags, damtype);
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((unsigned int *)amt, damtype, getlfmaterial(lf));
if (lfhasflag(lf, F_DRUNK)) {
*amt -= rnd(0,3);
}
if (lf->race->raceclass->id == RC_GOD) {
// immortal
limit(amt, 0, lf->hp-1);
} else {
limit(amt, 0, NA);
}
}
void makepeaceful(lifeform_t *who) {
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);
}
}
addflag(who->flags, F_XPVAL, 0, NA, NA, NULL);
killflagsofid(who->flags, F_HOSTILE);
loseaitargets(who);
}
lifeform_t *makezombie(object_t *o) {
flag_t *f;
race_t *r;
lifeform_t *lf;
cell_t *where;
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;
where = getoblocation(o);
getobname(o, obname, 1);
if (!cellwalkable(NULL, where, NULL)) {
where = getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND);
}
lf = addlf(where, r->id, 1);
addflag(lf->flags, F_LFSUFFIX, B_TRUE, NA, NA, "zombie");
addflag(lf->flags, F_GLYPH, C_GREY, NA, NA, "Z");
addflag(lf->flags, F_UNDEAD, B_TRUE, NA, NA, NULL);
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lf->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL);
addflag(lf->flags, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL);
addflag(lf->flags, F_DTIMMUNE, DT_DECAY, NA, NA, NULL);
addflag(lf->flags, F_DTIMMUNE, DT_NECROTIC, NA, NA, NULL);
addflag(lf->flags, F_DTVULN, DT_HOLY, NA, NA, NULL);
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_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_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];
// 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);
// remove the object
removeob(o,o->amt);
// redraw & announce
if (haslos(player, where)) {
needredraw = B_TRUE;
drawscreen();
msg("%s rises from the dead!", obname);
}
return lf;
}
int areenemies(lifeform_t *lf1, lifeform_t *lf2) {
reason = E_OK;
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 applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o) {
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(lf, dam, damtype, fromlf, damstring);
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);
}
}
}
}
int areallies(lifeform_t *lf1, lifeform_t *lf2) {
if (getallegiance(lf1) == getallegiance(lf2)) { // same allegience?
if (isplayer(lf1) || isplayer(lf2)) {
// if one of them is the player
return B_TRUE;
} else {
if (lf1->race->baseid == lf2->race->baseid) {
return B_TRUE;
}
}
} else {
if (lf1->race->baseid == lf2->race->baseid) {
if (!isplayer(lf1) && !isplayer(lf2)) {
return B_TRUE;
}
}
}
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(lifeform_t *lf) {
npcname_t *poss,*sel;
int nposs = 0,i;
poss = malloc(numnpcnames * sizeof(npcname_t *));
// already got one?
if (lfhasflag(lf, F_NAME)) {
return NULL;
}
// count possibilities
for (i = 0;i < numnpcnames; i++) {
if (npcname[i].valid) {
poss[nposs++] = npcname[i];
}
}
// get random name
i = rnd(0,nposs);
sel = &npcname[i];
// none else can use this name now
sel->valid = B_FALSE;
addflag(lf->flags, F_NAME, NA, NA, NA, sel->name);
return sel->name;
}
// 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;
if (hasjob(lf, J_COMMANDO)) {
return;
}
if (isplayer(lf)) {
slev = PR_NOVICE;
} else {
slev = PR_ADEPT;
}
if (!hasjob(lf, J_WIZARD)) {
for (o = lf->pack->first ; o ; o = o->next) {
if (isweapon(o) && canweild(lf, o)) {
sk = getobskill(o);
if (sk && !getskill(lf, sk->id)) {
giveskilllev(lf, sk->id, slev);
}
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);
}
}
void autotarget(lifeform_t *lf) {
object_t *gun;
lifeform_t *targ,*newtarg;
int closest;
int i;
int gunrange;
int targid;
gun = getfirearm(lf);
if (!gun) return;
if (!getammo(gun)) return;
gunrange = getfirearmrange(gun);
// already got a target?
targid = getguntargetid(lf);
if (targid == -1) {
targ = NULL;
} else {
targ = findlf(NULL, targid);
if (targ) {
// dead? remove target and keep going.
if (isdead(targ)) {
// clear target ?
targ = NULL;
} else if (!haslof(lf->cell, targ->cell, B_FALSE, NULL)) {
// clear target ?
targ = NULL;
} else {
// already got a valid target. return.
return;
}
} else {
// target no longer exists?
// clear target ?
}
}
// 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)) {
int valid = B_TRUE;
if (!areenemies(lf, c->lf)) {
valid = B_FALSE;
}
if (valid) {
int thisdist;
thisdist = getcelldist(lf->cell, c);
if (thisdist < closest) {
newtarg = c->lf;
closest = thisdist;
}
}
}
}
if (newtarg && (newtarg != targ)) {
setguntarget(lf, newtarg);
}
}
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) && (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) {
flag_t *f;
f = hasflagval(fp, F_DTVULN, dt, NA, NA, NULL);
if (f) return f;
f = hasflagval(fp, F_DTVULN, DT_ALL, NA, NA, NULL);
if (f) return f;
return NULL;
}
int isweaponskill(enum SKILL skid) {
switch (skid) {
case SK_AXES:
case SK_CLUBS:
case SK_LONGBLADES:
case SK_POLEARMS:
case SK_SHORTBLADES:
case SK_STAVES:
//case SK_UNARMED:
return B_TRUE;
default:
break;
}
return B_FALSE;
}
enum FLAG iswoozy(lifeform_t *lf) {
if (lfhasflag(lf, F_DRUNK)) return B_TRUE;
if (lfhasflag(lf, F_CONFUSED)) return B_TRUE;
if (lfhasflagval(lf, F_INJURY, IJ_CONCUSSION, NA, NA, NULL)) return B_TRUE;
return F_NONE;
}
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 killrace(race_t *race) {
race_t *nextone, *lastone;
// free mem
free(race->name);
killflagpile(race->flags);
// remove from list
nextone = race->next;
if (nextone != NULL) {
nextone->prev = race->prev;
} else { /* last */
lastrace = race->prev;
}
if (race->prev == NULL) {
/* first */
nextone = race->next;
free(race);
race = nextone;
} else {
lastone = race->prev;
free (lastone->next );
lastone->next = nextone;
}
}
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_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) {
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:
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.", curammo, 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) {
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));
} 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) {
object_t *gun,*newammo,*curammo;
gun = getfirearm(lf);
if (!gun) {
if (isplayer(lf)) {
msg("You have no firearm equipped.");
}
return B_TRUE;
}
curammo = getammo(gun);
if (curammo) {
if (isplayer(lf)) {
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)) {
char buf[BUFLEN];
getobname(gun, buf, 1);
msg("You have no ammo for your %s.", noprefix(buf));
}
return B_TRUE;
}
return B_FALSE;
}
void loseconcentration(lifeform_t *lf) {
// 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);
}
// 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_TRUE, NULL, B_TRUE);
}
int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam, object_t *fromob, int retaliate) {
char buf[BUFLEN];
char buf2[BUFLEN];
char lfname[BUFLEN];
int prebleed = B_FALSE,postbleed = B_FALSE,ko = B_FALSE;
flag_t *f;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
getlfname(lf, lfname);
if (isplayer(lf)) {
statdirty = B_TRUE;
}
if (isbleeding(lf)) {
prebleed = B_TRUE;
}
// check for psychic armour etc
if (amt > 0) {
if (isphysicaldam(damtype)) {
int i;
getflags(lf->flags, retflag, &nretflags, F_MAGICARMOUR, F_NONE);
for (i = 0; i < nretflags; i++) {
int damtaken;
f = retflag[i];
damtaken = amt;
if (damtaken > f->val[0]) {
damtaken = f->val[0];
}
f->val[0] -= damtaken;
if (f->val[0] <= 0) {
// from a spell?
if (f->lifetime == FROMSPELL) {
flag_t *spellflag;
spellflag = hasactivespell(lf, f->obfrom);
if (spellflag) {
killflag(spellflag);
}
}
killflag(f);
} else {
if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s pulses!", lfname, getpossessive(lfname), f->text);
}
}
// reduce damage
amt -= damtaken;
}
}
}
// 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);
// merciful weapons
if (fromob) {
f = hasflag(fromob->flags, F_MERCIFUL);
if (f && (amt >= lf->hp)) {
amt = lf->hp - 1; // ie end up at 1hp
ko = B_TRUE;
if (fromob->pile->owner && cansee(player, fromob->pile->owner)) {
f->known = B_TRUE;
}
}
}
// bashing damage sometimes ko's
if (!ko) {
if (damtype == DT_BASH) {
int hpleft;
hpleft = lf->hp - amt;
if ((hpleft >= -5) && (hpleft <= 0)) {
if (onein(2)) {
ko = B_TRUE;
amt = lf->hp - 1; // ie end up at 1hp
}
}
}
}
// large damage?
if ((amt >= (lf->maxhp / 2)) && (amt >= 20)) {
if (useringofmiracles(lf, 1)) {
return 0;
}
}
// occasionally drop blood
if (damtypecausesbleed(damtype) && onein(3)) {
bleed(lf, B_FALSE);
}
if (hasflag(lf->flags, F_DEBUG)) {
msg("[%s takes %d dam]",lfname, amt);
}
// take damage
lf->hp -= amt;
// fill in lastdam...
lf->lastdamtype = damtype;
// if they died
if (lf->hp <= 0) {
//if (!fromlf || (fromlf == player)) {
if (fromlf && (getallegiance(fromlf) == AL_FRIENDLY)) {
addflag(lf->flags, F_KILLEDBYPLAYER, B_TRUE, NA, NA, NULL);
}
} else {
// effects based on damage type
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, (amt/2) + getexposedlimbs(lf), 0)) {
poison(lf, 20+(amt*3), P_COLD, 0, "the cold");
}
// 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);
}
}
}
} else if (damtype == DT_FIRE) {
int i;
// fire will cauterise slash wounds
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if ((f->val[1] == DT_SLASH) && (f->lifetime > 0)) {
f->lifetime -= 20;
limit(&f->lifetime, 1, NA);
}
}
} else if (damtype == DT_POISONGAS) {
if (amt > 0) {
if (!skillcheck(lf, SC_POISON, 25, 0)) {
poison(lf, rnd(5,10), P_GAS, 2, "poison gas");
}
}
}
}
if (isbleeding(lf)) {
postbleed = B_TRUE;
}
// replace 'the' at start of damsrc with 'a'
if (strstr(damsrc, "the ") == damsrc) {
snprintf(buf, BUFLEN, "a %s", (damsrc+4));
} else {
strcpy(buf, damsrc);
}
// fill in damage amount
snprintf(buf2, BUFLEN, "^%d damage",amt);
strcat(buf, buf2);
setlastdam(lf, buf);
// special case
if (lf->race->id == R_DREAMFUNGUS) {
if (cansee(player, lf)) {
animradialorth(lf->cell, 4, '}', C_MAGENTA);
msg("^w%s releases a cloud of purple spores!", lfname);
drawscreen();
}
spellcloud(lf->cell, 3, '\0', C_MAGENTA, OT_S_SLEEP, 8, B_TRUE);
}
// further effects if not dead...
if (!isdead(lf)) {
// fight back if required
if (fromlf && retaliate && !ko) {
fightback(lf, fromlf);
}
// low hitpoint warning
if (amt > 0) {
if (isplayer(lf)) {
int warnthresh;
warnthresh = (int)((float)0.25 * (float)lf->maxhp);
if (lf->hp <= warnthresh) {
warn("*** LOW HITPOINT WARNING ***");
more();
}
}
}
if (ko) {
// you are knocked unconscious for a _long_ time
fallasleep(lf, ST_KO, rnd(50,100));
if (fromlf && isplayer(fromlf)) {
pleasegodmaybe(R_GODMERCY, 5);
}
} 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 (postbleed && !prebleed) {
f = lfhasflag(lf, F_BLEEDABIL);
if (f) {
flag_t *notime;
notime = addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL);
abilityeffects(lf, f->val[0], lf->cell, lf, NULL);
killflag(notime);
}
}
// elemental effects on held objects
if ((damtype == DT_FIRE) && !fromob) {
object_t *o, *nexto;
// 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) && pctchance(amt*10)) {
int newdam;
// object takes 1/4 of damage
newdam = pctof(25, amt);
limit(&newdam, 1, NA);
takedamage(o, newdam, DT_FIRE);
}
}
} else if ((damtype == DT_COLD) && !fromob) {
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) && pctchance(amt*10)) {
if (isvulnto(o->flags, DT_COLD)) {
int newdam;
// object takes 1/4 of damage
newdam = pctof(25, amt);
limit(&newdam, 1, NA);
takedamage(o, newdam, DT_COLD);
}
}
} else if (damtype == DT_WATER) {
// put out fires
extinguishlf(lf);
}
}
// update screen
drawstatus();
updatestatus();
return amt;
}
void losemp(lifeform_t *lf, int amt) {
lf->mp -= amt;
if (lf->mp < 0) {
lf->mp = 0;
}
if (isplayer(lf)) {
statdirty = B_TRUE;
drawstatus();
updatestatus();
}
}
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);
}
int makenauseated(lifeform_t *lf, int amt, int howlong) {
flag_t *f;
if (isundead(lf)) return B_TRUE;
if (!hasbp(lf, BP_HEAD)) return B_TRUE; // can't smell with no head
if (!lfhasflag(lf, F_HUMANOID)) return B_TRUE;
f = lfhasflag(lf, F_NAUSEATED);
if (f) {
if ((f->lifetime >= 0) && (f->lifetime < howlong)) {
f->lifetime = howlong;
f->val[0] = MAXOF(f->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) {
flag_t *f;
char *verb = NULL, *noun = NULL;
int volume = 1;
if (lfhasflag(lf, F_FROZEN)) {
// can't make noise if frozen!
return;
}
f = hasflagval(lf->flags, F_NOISETEXT, nid, NA, NA, NULL);
if (f) {
char *dummy;
char noisetext[BUFLEN];
volume = f->val[1];
if (f->text[0] == '^') {
verb = NULL;
noun = strtok_r(f->text, "^", &dummy);
} else {
verb = strtok_r(f->text, "^", &dummy);
noun = strtok_r(NULL, "^", &dummy);
}
snprintf(noisetext, BUFLEN, "%s.",noun);
if (nid == N_WALK) {
volume += getarmournoise(lf);
}
noise(lf->cell, lf, noisetypetoclass(nid), volume, noisetext, verb);
} else {
// 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:
volume = 0;
break;
case SZ_SMALL:
volume = 1;
strcpy(movetext, "light footsteps.");
break;
case SZ_MEDIUM:
case SZ_HUMAN:
volume = 2;
strcpy(movetext, "footsteps.");
break;
case SZ_LARGE:
volume = 3;
strcpy(movetext, "heavy footsteps.");
break;
case SZ_HUGE:
volume = 4;
strcpy(movetext, "heavy footsteps.");
break;
case SZ_ENORMOUS:
volume = 5;
strcpy(movetext, "very heavy footsteps.");
break;
case SZ_MAX:
volume = 6;
strcpy(movetext, "extremely loud thumping.");
break;
default:
break;
}
volume += getarmournoise(lf);
if (strlen(movetext)) {
noise(lf->cell, lf, noisetypetoclass(nid), volume, movetext, NULL);
}
} else if (nid == N_SONICBOLT) {
noise(lf->cell, lf, NC_OTHER, 5, "a ear-splitting burst of sound!", "emits an ear-splitting burst of sound!");
} else if (nid == N_WARCRY) {
noise(lf->cell, lf, NC_OTHER, 4, "a blood-curdling war cry!", "shouts a blood-curdling war-cry!");
}
}
}
void mayusespellschool(flagpile_t *fp, enum SPELLSCHOOL ss, enum FLAG how) {
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)) {
addflag(fp, how, ot->id, NA, NA, NULL);
}
}
//}
}
}
// if you don't meet it, return why not in 'reason'
int meetsattreq(lifeform_t *lf, flag_t *f, object_t *o) {
enum ATTRIB att;
int valneeded;
int myval;
att = f->val[0];
valneeded = f->val[1];
// modify for masterwork
if (o) {
if (hasflag(o->flags, F_MASTERWORK)) {
valneeded -= 3;
if (valneeded < 0) valneeded = 0;
}
}
myval = getattr(lf, att);
if (myval < valneeded) {
switch (att) {
case A_DEX:
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;
}
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] >= 18)) {
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, 18);
if ((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_DEX:
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_DEX:
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 (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.");
}
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 modstamina(lifeform_t *lf, float howmuch) {
float orig;
orig = lf->stamina;
lf->stamina += howmuch;
limitf(&(lf->stamina), 0, getmaxstamina(lf));
if (lf->stamina != orig) {
if (isplayer(lf)) {
statdirty = B_TRUE;
drawstatus();
updatestatus();
if (lf->stamina == 0) msg("You are exhausted.");
} else if (cansee(player, lf)) {
if (lf->stamina == 0) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s looks exhausted.", lfname);
}
}
}
if (lf->stamina == 0) {
stopsprinting(lf);
}
}
// 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;
}
if ((getmaxmp(lf) > 0) && (lf->mp < getmaxmp(lf))) {
if (validchars) strcat(validchars, "m");
need = B_TRUE;
}
return need;
}
// 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 rv = B_FALSE;
assert(text);
if (gamemode != GM_GAMESTARTED) {
return B_FALSE;
}
// 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 dist;
int difficulty;
int lbonus;
flag_t *f;
if (l == noisemaker) continue;
// ie. if this lf is in the process of swapping places
if (!l->cell) 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 {
//difficulty = (int) ( ((float)getcelldist(l->cell, c) / (float)gethearingrange(l)) * 20);
difficulty = (int) ( ((float)getcelldist(l->cell, c) / ((float)gethearingrange(l) + volume)) * 14);
}
// listen bonus is based on sound volume
lbonus = volume;
f = lfhasflag(l, F_ASLEEP);
if (f) {
// can't hear while unconscious
if (f->val[1] == ST_KO) continue;
lbonus -= 4;
limit(&lbonus, 0, NA);
}
// skillcheck to hear this
if ( (isplayer(l) && haslos(l, c)) || // only player can "hear by seeing"
(canhear(l, c, volume) && 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.
if (noisemaker && cansee(l, noisemaker)) {
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");
}
msg("%s %s.", lfname, realseetext);
rv = B_TRUE;
}
} 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;
//punc = text[strlen(text)-1];
//strncpy(textnopunc, text, strlen(text)-1);
strcpy(textnopunc, text);
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.
// listen skill gives you more info about monsters
if (noisemaker) {
enum SKILLLEVEL slev;
char lfname[BUFLEN];
char distbuf[BUFLEN],distbufbad[BUFLEN];
char dirbuf[BUFLEN];
int dir;
real_getlfnamea(noisemaker, lfname, B_FALSE);
if (dist >= 20) { // 20+
strcpy(distbuf, " very far away");
strcpy(distbufbad, " far away");
} else if (dist >= 10) { // 10 - 19
strcpy(distbuf, " far away");
} else if (dist >= 5) { // 5 - 9
strcpy(distbuf, " nearby");
strcpy(distbufbad, " nearby");
} else if (dist >= 2) { // 2 - 4
strcpy(distbuf, " very nearby");
strcpy(distbufbad, " nearby");
} else { // 1
strcpy(distbuf, " right beside you");
strcpy(distbufbad, " nearby");
}
dir = getdirtowards(l->cell, c, NULL, B_FALSE, DT_COMPASS);
strcpy(dirbuf, getdirname(dir));
dirbuf[0] = tolower(dirbuf[0]);
slev = getskill(l, SK_LISTEN);
//
// 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 (slev >= PR_MASTER) {
flag_t *f;
f = lfhasflagval(l, F_CANHEARLF, noisemaker->id, NA, NA, NULL);
if (f) {
if (f->lifetime > 0) f->lifetime = 2;
} else {
addtempflag(l->flags, F_CANHEARLF, noisemaker->id, NA, NA, NULL, 2);
}
} else if (slev >= PR_EXPERT) {
msg("You hear %s%s to the %s%c", lfname, distbuf, dirbuf, punc);
rv = B_TRUE;
} else if (slev >= PR_ADEPT) {
msg("You hear %s%s to the %s%c", textnopunc, distbuf, dirbuf, punc);
rv = B_TRUE;
} else if (slev >= PR_BEGINNER) {
msg("You hear %s%s%c", textnopunc, distbuf, punc);
rv = B_TRUE;
} else if (slev >= PR_NOVICE) {
msg("You hear %s%s%c", textnopunc, distbufbad, punc);
rv = B_TRUE;
} else {
assert(text);
msg("You hear %s", text);
rv = B_TRUE;
}
} else {
assert(text);
msg("You hear %s", text);
rv = B_TRUE;
}
// can only hear one 'walk' sound per turn.
if (isplayer(l) && (nclass == NC_MOVEMENT)) {
addflag(l->flags, F_DONELISTEN, B_TRUE, NA, NA, NULL);
practice(l, SK_LISTEN, 1);
}
}
} // end if !isplayer and not asleep
// wake up a little
f = lfhasflag(l, F_ASLEEP);
if (f) {
if (f->lifetime > 0) { // ie. temporary
timeeffectsflag(f, volume + 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 awakens you!");
rv = B_TRUE;
}
killflag(f);
} else {
// ie resting on purpose via 'R'
// only wake up if the sound if very close
if (volume >= getcelldist(c, l->cell)) {
// wake up!
if (isplayer(l)) {
char wakenoise[BUFLEN];
assert(text);
strcpy(wakenoise, text);
wakenoise[strlen(wakenoise)-1] = '\0'; // omit punctuation
//msg("A nearby noise awakens you!");
msg("^wThe sound of %s awakens you!", wakenoise);
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_KO) && cansee(player, l)) {
char lfname[BUFLEN];
getlfname(l, lfname);
msg("%s stir%s in %s slumber...", lfname,
isplayer(l) ? "" : "s",
isplayer(l) ? "your" : "its");
}
} else { // not asleep, but can hear it.
// monsters will turn to face the sound, as long as they're
// not attacking something
if (!isplayer(l)) {
flag_t *f;
int willrespond = B_FALSE;
f = aihastarget(l);
if (f) {
// will probably ignore the sound...
if ((volume >= 5) && onein(2)) {
willrespond = B_TRUE;
}
} else willrespond = B_TRUE;
if (willrespond) {
// turn to face the sound
if (isplayer(noisemaker) && cansee(player, l) && cansee(l, player) && !lfhasflag(l, F_AWARENESS)) {
// peaceful things only turn sometimes
if (!ispeaceful(l) || onein(6)) {
char lfname[BUFLEN];
turntoface(l, c);
getlfname(l, lfname);
msg("%s turns to face you.", lfname);
}
}
}
}
}
} else { // can't hear the sound.
}
} // end for each lf on map
return rv;
}
enum NOISECLASS noisetypetoclass(enum NOISETYPE nt) {
switch (nt) {
case N_WALK:
case N_FLY:
return NC_MOVEMENT;
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);
// weild/wear stuff
autoweild(lf);
}
// make 'lf' into a pet/ally of 'owner'
void petify(lifeform_t *lf, lifeform_t *owner) {
makefriendly(lf, PERMENANT);
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 the ground from up here!");
}
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_NOPICKUP:
msg("You can't pick up %s!",obname);
break;
case E_TOOBIG:
msg("%s %s too large for you to lift.",obname, (what->amt == 1) ? "is" : "are");
break;
case E_TOOHEAVY:
msg("%s %s too heavy to lift!",obname, (what->amt == 1) ? "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);
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;
}
}
// special case
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);
}
}
taketime(lf, SPEED_PICKUP);
// TODO: update burdened status
} 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;
}
return B_FALSE;
}
void poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat) {
flag_t *f;
int found = B_FALSE,i;
enum POISONSEVERITY psev;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
// are you immune to disease?
psev = getpoisonseverity(ptype);
if ((psev == PS_DISEASE) && hasflag(lf->flags, F_DISEASEIMMUNE)) {
return;
}
// adjust time based on first aid skill
howlong -= getskill(lf, SK_FIRSTAID);
if (howlong <= 0) {
return;
}
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;
// 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) {
addtempflag(lf->flags, F_POISONED, ptype, power, NA, fromwhat, howlong);
// also apply specific poison effect
switch (ptype) {
case P_FOOD:
case P_GAS:
case P_VENOM:
default:
break;
case P_WEAKNESS:
f = addtempflag(lf->flags, F_ATTRMOD, A_STR, -power, NA, NULL, FROMPOISON);
f->obfrom = ptype; // poison type
break;
}
}
}
int poisoncausesvomit(enum POISONTYPE ptype) {
switch (ptype) {
case P_FOOD:
case P_VENOM:
return B_TRUE;
default:
break;
}
return B_FALSE;
}
int poisonthreatenslife(lifeform_t *lf, flag_t *f) {
float time,dam,totaldam;
if (!f) return B_FALSE;
time = f->lifetime;
dam = f->val[1];
totaldam = time * dam;
totaldam = pctof(getpoisondamchance(f->val[0]), totaldam);
if (totaldam >= lf->hp) {
return 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;
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_new(lifeform_t *lf) {
cell_t *c;
int startxray,rangemod;
int maxvisrange,nightvisrange;
cell_t **los;
int *blocker;
int nlos = 0,i,nn;
flag_t *f;
int endx[MAXVISLIMIT],endy[MAXVISLIMIT];
int nendcells = 0;
cell_t *retcell[MAXRETCELLS];
int numpixels;
//int db = B_FALSE;
enum SKILLLEVEL plev;
flag_t *missingeye;
if (lf->cell->type->id == CT_FAKE) return;
f = hasflag(lf->flags, F_XRAYVIS);
if (f) {
startxray = f->val[0];
} else {
startxray = 0;
}
// right eye missing?
missingeye = lfhasflagval(lf, F_INJURY, IJ_EYEDESTROYED, NA, NA, NULL);
los = malloc( sizeof(cell_t *) * (MAX_MAPW * MAX_MAPH));
blocker = malloc( sizeof(cell_t *) * (MAX_MAPW * MAX_MAPH));
nlos = 0;
if (lf->los) {
free(lf->los); lf->los = NULL;
}
maxvisrange = getvisrange(lf, B_FALSE);
nightvisrange = getnightvisrange(lf);
plev = getskill(lf, SK_PERCEPTION);
// 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_ADEPT) 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);
}
}
} // 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 );
// keep going until we lose los
for (n = 0; keepgoing && (n < numpixels); n++) {
c = retcell[n];
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.
if (litforus) {
los[nlos] = c;
blocker[nlos] = keepgoing ? B_FALSE : B_TRUE;
nlos++;
}
}
} else { // ie. if !c
keepgoing = B_FALSE;
}
} // end foreach cell and while keepgoing
}
assert(nlos < (MAX_MAPW * MAX_MAPH));
// now fill in lifeform structure
lf->los = malloc(sizeof(cell_t *) * nlos);
for (i = 0; i < nlos; i++) {
lf->los[i] = los[i];
}
lf->nlos = nlos;
free(los);
free(blocker);
if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) {
needredraw = B_TRUE;
}
}
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);
return B_FALSE;
}
int readytotrain(lifeform_t *lf) {
if (lf->skillpoints || getattpoints(lf) || levelabilityready(lf)) {
return B_TRUE;
}
return B_FALSE;
}
int recruit(lifeform_t *lf) {
int rv = B_FALSE;
if (lfhasflag(lf, F_NOHIRE)) {
// refusing to join at all.
sayphrase(lf, SP_RECRUIT_DECLINE, SV_TALK, NA, NULL);
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;
difficulty = 20 + ((gethitdice(player) - gethitdice(lf))*2);
if (real_skillcheck(player, A_CHA, difficulty, 0, &result)) {
// passed
askingprice = rnd(gethitdice(lf)*50, gethitdice(lf)*100 );
addflag(lf->flags, F_HIREPRICE, askingprice, NA, NA, NULL);
} else {
if (difficulty - result >= 10) {
// very expensive
askingprice = rnd(gethitdice(lf)*250, gethitdice(lf)*300 );
addflag(lf->flags, F_HIREPRICE, askingprice, NA, NA, NULL);
} else {
// expensive
askingprice = rnd(gethitdice(lf)*100, gethitdice(lf)*200 );
addflag(lf->flags, F_HIREPRICE, askingprice, NA, NA, NULL);
}
}
}
if (askingprice != 0) {
// modify by charisma
askingprice = pctof(100 + getstatmod(player, A_CHA), askingprice);
sayphrase(lf, SP_RECRUIT_ASKPRICE, SV_TALK, askingprice, NULL);
more();
if (askingprice > countmoney(player)) {
} else {
char ch;
snprintf(buf, BUFLEN, "Pay $%d to hire %s", askingprice, lfname);
ch = askchar(buf, "yn","n", B_TRUE);
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);
}
sayphrase(lf, SP_RECRUIT_ACCEPT, SV_TALK, NA, p);
} else {
if (askingprice > countmoney(player)) {
sayphrase(lf, SP_RECRUIT_DECLINE_CANTPAY, SV_TALK, askingprice, NULL);
} else {
sayphrase(lf, SP_RECRUIT_DECLINE_WONTPAY, SV_TALK, askingprice, NULL);
}
}
} // 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_LEVSKILL, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
// we'll set timeleft to -1 while actually assigning these.
switch (f->id) {
case F_LEVFLAG:
case F_LEVABIL:
case F_LEVSKILL:
case F_LEVSPELL:
case F_LEVSPELLSCHOOL:
f->lifetime = FROMJOB;
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);
}
// strat resting...
int startresting(lifeform_t *lf, int willtrain) {
int traincounter;
if (willtrain) {
traincounter = 50;
traincounter = modifybystat(traincounter, player, A_IQ);
} else {
traincounter = NA;
}
// stop hiding
killflagsofid(lf->flags, F_HIDING);
// stop all spells
stopallspells(lf);
killflagsofid(lf->flags, F_INTERRUPTED);
if (willtrain) {
addflag(lf->flags, F_TRAINING, B_TRUE, NA, traincounter, NULL);
} else {
if (gotosleep(lf, B_TRUE)) {
// 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...");
}
drawmsg();
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,2);
break;
case AT_VLOW:
roll = rnd(3, 6);
break;
case AT_LOW:
roll = rnd(7, 8);
break;
case AT_LTAVERAGE:
roll = rnd(9, 10);
break;
case AT_AVERAGE:
roll = rnd(11, 12);
break;
case AT_GTAVERAGE:
roll = rnd(13, 14);
break;
case AT_HIGH:
roll = rnd(15, 16);
break;
case AT_VHIGH:
roll = rnd(17, 18);
break;
case AT_EXHIGH:
roll = rnd(19, 20);
break;
default:
roll = rolldie(3,6);
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) {
lifeform_t *l;
reason = E_OK;
for (l = lf->cell->map->lf ; l ; l = l->next) {
if ((l != lf) && cansee(lf, l) && areenemies(lf, l) ) {
if (!lfhasflag(l, F_HARMLESS)) {
reason = E_MONSTERNEARBY;
return B_FALSE;
}
}
}
return B_TRUE;
}
int say(lifeform_t *lf, char *text, int volume) {
char seebuf[BUFLEN];
char hearbuf[BUFLEN];
char verb[BUFLEN];
char noun[BUFLEN];
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, text);
snprintf(hearbuf, BUFLEN, "%s \"%s\"", noun, text);
return noise(lf->cell, lf, NC_SPEECH, volume, hearbuf, seebuf);
}
// volume = -1 means "auto"
int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *text) {
int i,rv = B_FALSE;
char buf[BUFLEN];
switch (what) {
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_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_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_PAYTHANKS:
switch (rnd(1,3)) {
case 1: snprintf(buf, BUFLEN, "Pleasure doing business with you!"); break;
case 2: snprintf(buf, BUFLEN, "Thank you, come again!"); break;
case 3: snprintf(buf, BUFLEN, "Another satisfied customer!"); break;
}
rv = say(lf, buf, volume);
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:
snprintf(buf, BUFLEN, "My services will cost you $%d.",val0);
rv = say(lf, buf, volume);
break;
case SP_RECRUIT_DECLINE:
rv = say(lf, "No, I regretfully decline your offer.", 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_SORRY:
if (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;
default:
break;
}
return rv;
}
// returns TRUE if something happened
int scare(lifeform_t *lf, lifeform_t *scarer, int howlong, int scarerbonus) {
int nfailures = 0;
int i;
int penalty = 0;
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;
}
// not intelligent enough to be scared?
if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= IQ_MINDLESS) {
return B_FALSE;
}
// determine morale check penalty
if (isbleeding(lf)) {
penalty += 5;
}
if (lfhasflag(lf, F_HUMANOID)) {
object_t *o;
for (o = scarer->pack->first ; o; o = o->next) {
flag_t *f;
f = isequipped(o);
// ie. equipped, but not primary or secondary weapon
if (f && (f->val[0] > BP_SECWEAPON)) {
f = hasflag(o->flags, F_SCARY);
if (f) {
scarerbonus += f->val[0];
}
}
}
}
// modify by charisma: -3 to 3
scarerbonus += (getstatmod(lf, A_CHA) / 15);
// three checks
nfailures = 0;
for (i = 0; i < 3; i++) {
if (!skillcheckvs(lf, SC_MORALE, -penalty, scarer, SC_MORALE, scarerbonus)) {
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 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];
}
}
int setfacing(lifeform_t *lf, int dir) {
if (dir == D_NONE) {
dblog("xxx");
}
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;
f = hasflag(lf->flags, F_GUNTARGET);
if (f) {
killflag(f);
}
if (targ) {
addflag(lf->flags, F_GUNTARGET, targ->id, NA, NA, NULL);
if (isplayer(lf)) {
// announce
char targname[BUFLEN];
getlfname(targ, targname);
msg("Targetted: %s.",noprefix(targname)); // remove 'the '
} else {
if (lfhasflag(lf, F_DEBUG)) {
char lfname[BUFLEN];
char targname[BUFLEN];
getlfname(lf, lfname);
getlfname(targ, targname);
dblog("%s targetted %s.",lfname,targname);
}
}
} else {
if (isplayer(lf)) {
// announce
msg("Targetted: nothing.");
} else {
if (lfhasflag(lf, F_DEBUG)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
dblog("%s target set to nothing.",lfname);
}
}
}
}
void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) {
flag_t *f,*nextf;
int i,retainhp = B_FALSE;
int nkilled = 0;
race_t *newrace;
char buf[BUFLEN];
flag_t *retflag[MAXCANDIDATES];
int nretflags;
newrace = findrace(rid);
if (isplayer(lf)) {
statdirty = B_TRUE;
}
if (lfhasflag(lf, F_RETAINHPONPOLY)) {
retainhp = B_TRUE;
}
if (frompolymorph && (gamemode == GM_GAMESTARTED) && lf->race) {
race_t *origrace = NULL;
// remove 'become a ghost' flag
killflagsofid(lf->flags, F_RISEASGHOST);
// were we already polymorphed?
f = lfhasflag(lf, F_ORIGRACE);
if (f) {
origrace = findrace(f->val[0]);
}
// announce
if (origrace && (newrace == origrace)) {
// 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);
msg("^wThe %s transforms back to its original form!", newrace->name);
}
}
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,
(f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text);
} else {
msg("^w%s transforms into %s %s!", buf, isvowel(newrace->name[0]) ? "an" : "a", newrace->name);
}
}
}
}
// stop stoning.
killflagsofid(lf->flags, F_BEINGSTONED);
// first remove flags from existing race, or temporary ones
lf->born = B_FALSE;
for (f = lf->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->lifetime == FROMRACE) {
killflag(f);
nkilled++;
} else if ((f->lifetime > 0) && (f->id != F_POLYMORPHED)) {
killflag(f);
nkilled++;
}
}
// set race
lf->race = newrace;
// set material
lf->material = lf->race->material;
// inherit most flags from race
for (f = lf->race->flags->first ; f ; f = f->next) {
switch (f->id) {
case F_RARITY:
case F_MPDICE:
break;
default:
addflag_real(lf->flags, f->id, f->val[0], f->val[1], f->val[2], f->text, FROMRACE, f->known, -1);
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
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);
}
}
// generate 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;
for (i = 0; i < lf->level; i++) {
lf->maxhp += rollhitdice(lf);
assert(lf->maxhp > 0);
}
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;
// 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)) {
enum BODYPART bp;
object_t *o,*nexto;
// no pack?
if (lfhasflag(lf, F_NOPACK)) {
// drop everything
if (countobs(lf->pack, B_FALSE)) {
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;
moveob(o, lf->cell->obpile, o->amt);
}
}
for (bp = BP_WEAPON; bp < MAXBODYPARTS; bp++) {
if (lfhasflagval(lf, F_NOBODYPART, bp, NA, NA, NULL)) {
for (o = lf->pack->first; o ; o = nexto ) {
nexto = o->next;
if (hasflagval(o->flags, F_EQUIPPED, bp, NA, NA, NULL)) {
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);
}
}
}
}
for (o = lf->pack->first ; o ; o = nexto) {
nexto = o->next;
if (!canpickup(lf, o, o->amt)) {
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);
}
}
if (isplayer(lf)) {
needredraw = B_TRUE;
statdirty = B_TRUE;
} else if (cansee(player, lf)) {
needredraw = B_TRUE;
}
} // end if gamestarted
}
void setlastdam(lifeform_t *lf, char *buf) {
if (lf->lastdam) {
free(lf->lastdam);
}
lf->lastdam = strdup(buf);
}
void interrupt(lifeform_t *lf) {
stopeating(lf);
stopresting(lf);
stoprunning(lf);
killflagsofid(lf->flags, F_AUTOCMD);
}
int setlfmaterial(lifeform_t *lf, enum MATERIAL id) {
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 (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) {
lf->losdirty = B_TRUE;
if (isplayer(lf)) {
needredraw = B_TRUE;
drawscreen();
}
}
int shoot(lifeform_t *lf) {
object_t *gun,*ammo;
lifeform_t *targ;
flag_t *f;
int firespeed;
reason = E_OK;
gun = getfirearm(lf);
if (!gun) {
reason = E_NOTEQUIPPED;
return B_TRUE;
}
// get target
targ = getguntarget(lf);
if (!targ) {
reason = E_NOTARGET;
return B_TRUE;
}
// get ammo
ammo = getammo(gun);
if (!ammo) {
reason = E_NOAMMO;
return B_TRUE;
}
// get fire speed
firespeed = getfirearmspeed(gun);
f = hasflag(gun->flags, F_FIRETURNS);
taketime(lf, getactspeed(lf) * f->val[0]);
fireat(lf, ammo, 1, targ->cell, firespeed, gun);
return B_FALSE;
}
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
int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *result) {
int attrib;
int levmod;
int othermod = 0;
int db = B_FALSE;
int roll;
int modroll;
int luckmod = 0;
char mbuf[BUFLEN];
flag_t *f;
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_DEX);
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 -= 3;
}
break;
case SC_WIS:
attrib = getattr(lf, A_WIS);
break;
///////////////
case SC_OPENLOCKS:
attrib = getattr(lf, A_DEX);
break;
case SC_WILL:
attrib = getattr(lf, A_WIS);
break;
case SC_TUMBLE:
attrib = getskill(lf, SK_ATHLETICS);
break;
case SC_LEARNMAGIC:
attrib = (getattr(lf, A_IQ) / 2) + (getskill(lf, SK_SPELLCASTING)/2) + lf->level;
break;
case SC_MORALE: // based on level/hitdice and size.
attrib = (lf->hp / 4);
attrib += ((float)getlfsize(lf) * 1.5);
f = lfhasflag(lf, F_MORALE);
if (f) attrib += f->val[0];
break;
case SC_SLIP:
case SC_FALL:
attrib = getattr(lf, A_DEX);
break;
case SC_SHIELDBLOCK:
attrib = (getattr(lf, A_DEX) / 4);
break;
case SC_POISON:
attrib = getattr(lf, A_CON);
break;
case SC_DISARM:
attrib = (getskill(lf, SK_TRAPS)*3);
if (attrib) {
attrib += (getattr(lf, A_DEX)/4);
}
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)/6) + (getattr(lf, A_WIS)/6) + getmr(lf);
break;
case SC_SEARCH:
attrib = (getskill(lf, SK_PERCEPTION)*2);
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 = (gethitdice(lf) / 3);
// 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];
}
}
} 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_SHIELDBLOCK) {
object_t *shield;
enum SKILLLEVEL slev;
shield = getshield(lf);
if (shield) {
if (isshield(shield)) {
slev = getskill(lf, SK_SHIELDS);
} else {
// twoweaponer using their weapon
slev = PR_NOVICE;
}
} else {
// should never happen!!!
slev = PR_INEPT;
}
switch (slev) {
case PR_NOVICE: othermod = 4; break;
case PR_BEGINNER: othermod = 7; break;
case PR_ADEPT: othermod = 10; break;
case PR_SKILLED: othermod = 13; break;
case PR_EXPERT: othermod = 16; break;
case PR_MASTER: othermod = 20; break;
default:
othermod = 0;
break;
}
} else if (ct == SC_MORALE) {
othermod += (getstatmod(lf, A_WIS) / 10); // ie. -5 to 5
} else if (ct == SC_OPENLOCKS) {
enum SKILLLEVEL slev;
slev = getskill(lf, SK_LOCKPICKING);
if (slev == PR_INEPT) {
othermod -= 10;
} else {
othermod += (5 * slev);
}
} else if (ct == SC_POISON) {
// auto pass if we are immune
if (isimmuneto(lf->flags, DT_POISON)) {
othermod += (diff*2);
} else if (isresistantto(lf->flags, DT_POISON)) {
othermod += 5;
} else if (isvulnto(lf->flags, DT_POISON)) {
othermod -= 10;
}
} else if (ct == SC_SEARCH) {
int bonus = 0;
sumflags(lf->flags, F_ENHANCESEARCH, &bonus, NULL, NULL);
othermod += bonus;
} else if (ct == SC_STEAL) {
if (attrib > 0) {
// ie. -3 to 3
othermod += (getstatmod(lf, A_DEX) / 15);
}
} else if (ct == SC_STEALTH) {
if (attrib > 0) {
if (lfhasflag(lf, F_SNEAK)) {
othermod += 3;
}
}
if (isairborne(lf)) {
othermod += 5;
}
} else if (ct == SC_WILL) {
// level counts for more
levmod *= 2;
} else if (ct == SC_TUMBLE) {
// ie. -3 to 3
othermod += (getstatmod(lf, A_DEX) / 15);
}
// luck
sumflags(lf->flags, F_EXTRALUCK, &luckmod, NULL, NULL);
othermod += luckmod;
roll = rolldie(1, 20);
if (db) {
snprintf(mbuf, BUFLEN, "%s skillcheck (%d) - need %d, got %d(rll)+%d(attr)+%d(lvm)+%d(othmod)+%d(mod)=",lf->race->name,
ct, diff, roll, attrib,levmod, othermod,mod);
}
modroll = roll;
modroll += attrib;
modroll += mod;
modroll += levmod;
modroll += othermod;
if (db) {
msg("%s%d.",mbuf,modroll);
}
// natural 20 will pass some checks
if (roll == 20) {
switch (ct) {
case SC_DODGE:
case SC_SEARCH:
case SC_STEALTH:
if (db) {
msg("%s skillcheck passed with natural 20.", lf->race->name);
}
modroll = diff;
break;
default:
break;
}
}
if (result) {
*result = modroll;
}
if (modroll >= 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;
}
// 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;
}
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)) {
return B_TRUE;
}
getlfname(lf,lfname);
getobname(o, obname, 1);
// slip!
if (isplayer(lf) || cansee(player, lf)) {
char damstring[BUFLEN];
msg("%s slip%s on %s and fall%s to the ground.",lfname, isplayer(lf) ? "" : "s", obname,
isplayer(lf) ? "" : "s");
snprintf(damstring, BUFLEN, "slipping on %s",obname);
losehp(lf, 1, DT_FALL, NULL, damstring);
}
fall(lf, NULL, B_FALSE);
// object gets damaged?
if (hasflag(o->flags, F_DAMAGABLE)) {
takedamage(o, 1, DT_DIRECT);
}
// object moves?
if (hasflag(o->flags, F_SLIPMOVE)) {
cell_t *cur, *new;
cur = getoblocation(o);
new = getrandomadjcell(cur, WE_WALKABLE, 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;
}
}
}
//////////////////////////////////
// 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;
enum ERROR error;
object_t *o;
flag_t *f;
flag_t *asp;
char buf[BUFLEN];
lifeform_t *l;
int i;
int willvanish = B_FALSE;
int donefeetwet = B_FALSE;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
map = lf->cell->map;
if (db) dblog("startlfturn for lf id %d %s", lf->id, lf->race->name);
if (isplayer(lf) && lfhasflag(lf, F_DRUNK)) statdirty = B_TRUE;
// clear one-turn-only flags
killflagsofid(lf->flags, F_DONELISTEN);
killflagsofid(lf->flags, F_NOSWAP);
// update where player knows
// (but without a map you will then slowly forget it)
if (isplayer(lf)) {
updateknowncells();
} else {
// ai start of turn code
killflagsofid(lf->flags, F_IGNORECELL);
}
// stuck inside solid cells?
if (!cellwalkable(lf, lf->cell, &error)) {
if (error == E_WALLINWAY) {
if (isplayer(lf)) {
msg("You reintegrate inside a solid object!");
}
losehp(lf, 9999, DT_DIRECT, NULL, "re-integration inside a solid object");
//}
}
}
// drown?
o = hasobwithflag(lf->cell->obpile, F_DEEPWATER);
if (o) {
if (checkfordrowning(lf, o)) {
if (isdead(lf)) return;
}
}
// suffocate?
if (lfhasflag(lf, F_NEEDSWATER) && !hasobwithflag(lf->cell->obpile, F_DEEPWATER)) {
if (isplayer(lf)) {
msg("^BYou are suffocating without water to breath!");
} else if (cansee(player, lf)) {
int dam;
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 == RG_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);
killflag(f);
}
}
if (lfhasflagval(lf, F_INJURY, IJ_ARTERYPIERCE, NA, NA, NULL)) {
if (!bleedfrom(lf, BP_HANDS, B_SPLATTER)) {
losehp(lf, rnd(1,8), DT_DIRECT, NULL, "blood loss");
}
}
// get more hungry
modhunger(lf, 1);
// regeneration
sumflags(lf->flags, F_REGENERATES, &i, NULL, NULL);
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);
} else if (isswimming(lf)) {
int lossamt;
// lose stamina based on swimming skill
switch (getskill(lf, SK_SWIMMING)) {
case PR_INEPT: lossamt = 3; break;
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;
}
if (lossamt) modstamina(lf, -lossamt);
} else {
if (lf->stamina < getmaxstamina(lf)) {
float regenrate = 0.5;
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);
}
}
limitf(&regenrate, 0, NA);
modstamina(lf, regenrate);
}
}
// god piety gets restored over time
if (isplayer(lf)) {
for (i = 0; i < ngodlfs; i++) {
if (getpietylev(godlf[i]->race->id, NULL, NULL) == PL_TOLERATED) {
// slowly tick upwards
if (onein(2)) modpiety(godlf[i]->race->id, 1);
}
}
}
// sixth sense spell warnings
f = hasactivespell(lf, OT_S_SIXTHSENSE);
if (f) {
cell_t *retcell[MAXCANDIDATES];
int nretcells;
getradiuscells(lf->cell, 2, DT_COMPASS, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0);
for (i = 0; i < nretcells; i++) {
if (retcell[i]->lf && areenemies(lf, retcell[i]->lf)) {
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[2] >= 5) {
char buf[BUFLEN];
real_getlfnamea(retcell[i]->lf, buf, B_FALSE);
warn("^WYour sixth sense warns you of %s %s you!", buf,
(reldir == RD_BACKWARDS) ? "behind" : "beside" );
} else if (f->val[2] >= 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[2] >= 7) {
char ch;
ch = askchar("Turn to face the threat", "yn","y", B_TRUE);
if (ch == 'y') {
turntoface(lf, retcell[i]);
}
} else {
more();
}
}
stopspell(lf, OT_S_SIXTHSENSE);
break;
}
}
}
}
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 (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)) {
f = isequipped(o);
if (f && ((f->val[0] == BP_WEAPON) || (f->val[0] == BP_SECWEAPON))) {
cell_t *c;
// weapon flies away
c = getrandomadjcell(lf->cell, WE_WALKABLE, B_NOEXPAND);
if (!c) c = lf->cell;
moveob(o, c->obpile, o->amt);
if (isplayer(lf)) {
getobname(o, buf, o->amt);
msg("^wYour %s flies out of your hands!",noprefix(buf), (o->amt == 1) ? "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), (o->amt == 1) ? "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, WE_WALKABLE, 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);
}
}
}
}
// special effects
f = lfhasflag(lf, F_AUTOCREATEOB);
if (f) {
int radius;
radius = f->val[0];
addobsinradius(lf->cell, f->val[0], DT_COMPASS, f->text, B_FALSE);
}
// handle ghosts
if (lf->race->id == R_GHOST) {
f = lfhasflag(lf, F_MYCORPSE);
if (f) {
long corpseid;
cell_t *corpseloc = NULL;
object_t *corpse;
corpseid = atol(f->text);
corpse = findobidinmap(lf->cell->map, corpseid);
if (corpse) {
corpseloc = corpse->pile->where;
}
// can we see our corpse?
if (corpseloc && haslos(lf, corpseloc)) {
// give possession ability
if (!lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL)) {
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);
f = addflag(lf->flags, F_CANWILL, OT_S_POSSESSION, NA, NA, pwbuf);
f->lifetime = FROMRACE;
}
} else {
// can't see corpse
f = lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL);
if (f) killflag(f);
// drain life
if (isplayer(lf)) msg("^BWithout your corpse, you feel yourself fading into nothingness.");
losehp(lf, 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 stench?
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);
}
}
}
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 (gethitdice(lf) <= maxhitdice) {
makepeaceful(lf);
}
}
}
// they are hiding, and you haven't spotted them yet
f = lfhasflag(l, F_HIDING);
if (f && !lfhasflagval(lf, F_SPOTTED, l->id, NA, NA, NULL) && !isinbattle(lf)) {
// can you see their cell?
if (!lfhasflag(lf, F_TRAINING) && haslos(lf, l->cell)) {
int bonus = 0;
int dist;
if (!lfhasflag(l, F_SILENTMOVE) && !isdeaf(lf)) {
bonus += getskill(lf, SK_LISTEN);
}
dist = getcelldist(lf->cell, l->cell);
if (dist > 1) {
bonus -= (dist*2);
}
// did you spot them?
if (skillcheckvs(lf, SC_SEARCH, bonus, l, SC_STEALTH, f->val[0])) {
addflag(lf->flags, F_SPOTTED, l->id, NA, NA, NULL);
// announce
if (isplayer(lf)) {
char lname[BUFLEN];
getlfname(l, lname);
msg("^wYou spot %s!", lname);
} else if (isplayer(l) && cansee(l, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("You think %s has spotted you!", lfname);
}
practice(lf, SK_PERCEPTION, 1);
}
}
}
}
}
// secret doors, traps, etc?
if (isplayer(lf) && !isinbattle(lf) && !isblind(lf) && !lfhasflag(lf, F_TRAINING)) {
for (i = 0; i < lf->nlos; i++) {
if (!lf->los[i]->lf) {
object_t *o;
for (o = lf->los[i]->obpile->first; o ; o = o->next) {
flag_t *f;
int mod = 0;
f = hasflag(o->flags, F_SECRET);
if (f && (f->val[0] != NA)) {
if (hasflag(o->flags, F_TRAP)) {
mod += (getskill(lf, SK_TRAPS)*2);
}
if (skillcheck(lf, SC_SEARCH, f->val[0], mod)) {
char obname[BUFLEN];
// reveal it
getobname(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_TRAPS, 1);
}
}
}
f = hasflag(o->flags, F_TRAPPED);
if (f && (f->val[2] != B_TRUE) && !hasflag(o->flags, F_SECRET)) {
objecttype_t *ot;
flag_t *trapflag;
int diff;
// find trap type
ot = findot(f->val[0]);
trapflag = hasflag(ot->flags, F_TRAP);
assert(trapflag);
diff = trapflag->val[0];
mod += getskill(lf, SK_TRAPS);
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_TRAPS, 1);
}
}
}
}
}
}
}
// 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;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// IMPORTANT: any potentially damaging effects have to go after here...
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
f = hasflag(lf->flags, F_POISONED);
if (f) {
// chance of fighting it off - gets easier over time.
//
if (skillcheck(lf, SC_POISON, (f->lifetime * 9), 0 )) {
killflag(f);
} else {
// chance of losing hp
if (rnd(1,100) <= getpoisondamchance(f->val[0])) {
char buf[BUFLEN];
flag_t *asleep;
// being asleep helps.
asleep = hasflag(lf->flags, F_ASLEEP);
if (!asleep) {
if (isplayer(lf)) {
msg("^bYou %s violently.", getpoisondamverb(f->val[0]));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("^%c%s %ss violently.",getlfcol(lf, CC_BAD), lfname, getpoisondamverb(f->val[0]));
}
taketime(lf, getactspeed(lf));
}
snprintf(buf, BUFLEN, "poisoning^from %s",f->text);
losehp(lf, f->val[1], DT_DIRECT, NULL, buf);
if (!asleep) {
if (poisoncausesvomit(f->val[0])) {
addobfast(lf->cell->obpile, OT_VOMITPOOL);
}
loseconcentration(lf);
}
}
// extra effects
if (f->val[0] == P_COLD) {
if (rnd(1,100) <= 10) {
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);
}
}
}
}
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);
}
}
if (lfhasflag(lf, F_FROZEN)) {
// melt...
if (rnd(1,2)) {
losehp(lf, 1, 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;
// damage from cell objects?
for (o = lf->cell->obpile->first ; o ; o = o->next) {
f = hasflag(o->flags, F_WALKDAM);
if (f) {
applywalkdam(lf, roll(f->text), f->val[0], o);
}
f = hasflag(o->flags, F_CAUSESCOUGH);
if (f && !isimmuneto(lf->flags, DT_POISONGAS)) {
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);
}
}
// for flags which can occur multiple times
getflags(o->flags, retflag, &nretflags, F_DEEPWATER, F_WALKDAMBP, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if ((f->id == F_DEEPWATER) && !isairborne(lf)) {
checkfordrowning(lf, o);
if (isdead(lf)) return;
} else if (f->id == F_WALKDAMBP) {
if (!isairborne(lf)) {
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);
} 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) && !donefeetwet) {
msg("Your %s get wet.", getbodypartname(lf, bp));
donefeetwet = B_TRUE; // don't keep repeating this
}
break;
default:
break;
}
// apply damage to lf instead.
applywalkdam(lf, roll(f->text), f->val[1], o);
}
}
}
}
} // end foreach object flag
}
if (isdead(lf)) return;
getflags(lf->flags, retflag, &nretflags, F_ATTACHEDTO, F_CANWILL, F_CHARMEDBY, F_FLEEFROM,
F_GRABBEDBY, F_GRABBING, F_BOOSTSPELL, F_FEIGNINGDEATH, F_INJURY,
F_NOFLEEFROM, F_PETOF, F_SPOTTED, F_STABBEDBY, F_TARGETCELL, F_TARGETLF, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
// remove impossible flags
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_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;
}
}
// bleeding injuries can stain armour
if ((f->id == F_INJURY) && (f->val[2] == DT_SLASH)) {
object_t *arm;
arm = getequippedob(lf->pack, f->val[1]);
if (arm && !hasobmod(arm, OM_BLOODSTAINED) && pctchance(5)) applyobmod(arm, OM_BLOODSTAINED);
}
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_SPOTTED) {
lifeform_t *lf2;
lf2 = findlf(NULL, f->val[0]);
if (!lf || !cansee(lf, lf2) || !lfhasflag(lf2, F_HIDING)) {
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;
}
}
// recharge abilities
if (f->id == F_CANWILL) {
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 && areallies(lf, targ)) {
if (lfhasflag(lf, F_DEBUG)) {
char lfname[BUFLEN];
char targname[BUFLEN];
getlfname(lf, lfname);
getlfname(targ, targname);
//msg("db: %s no longer targetting %s.",lfname,targname);
}
killflag(f);
continue;
}
} // end if f_target or f_targetcell
} // end loop through lf flags
}
// 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) && (getobunitweight(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 ((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 (isplayer(lf) && (slev >= PR_ADEPT)) {
addchoice(&prompt, '-', "Nothing", NULL, NULL, NULL);
// pick what you want
getchoice(&prompt);
o = (object_t *)prompt.result;
} else {
// random
o = (object_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data;
}
if (o) {
killflagsofid(o->flags, F_SHOPITEM);
o = moveob(o, lf->pack, 1);
if (o) {
char obname[BUFLEN];
char lfname[BUFLEN];
char targname[BUFLEN];
getlfname(lf, lfname);
getobname(o, obname, 1);
if (op->owner) {
getlfname(op->owner, targname);
if (isplayer(lf)) {
msg("You 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;
}
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);
snprintf(statname, BUFLEN, "statue of a %s", lf->race->name);
addob(lf->cell->obpile, statname);
// kill lifeform
addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
if (cansee(player, lf)) {
msg("^%c%s %s to stone!", getlfcol(lf, CC_VBAD), lfname, isplayer(lf) ? "turn" : "turns");
}
setlastdam(lf, "petrification");
die(lf);
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);
}
}
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;
}
}
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);
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_FALSE, 1, B_FALSE, 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) {
flag_t *f;
char obname[BUFLEN];
char buf[BUFLEN];
if (!isarmour(o)) {
return unweild(lf, o);
}
getobname(o, obname, 1);
if (!cantakeoff(lf, o)) {
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;
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
f = hasflag(o->flags, F_EQUIPPED);
killflag(f);
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);
}
}
// lose flags
loseobflags(lf, o, F_EQUIPCONFER);
f = hasflag(o->flags, F_CREATEDBYSPELL);
if (f) stopspell(lf, f->val[0]);
if (obproduceslight(o)) {
calclight((getoblocation(o))->map);
setlosdirty(lf);
//precalclos(lf);
drawscreen();
}
return B_FALSE;
}
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;
assert(howlong > 0);
if (db && (gamemode == GM_GAMESTARTED) && cansee(player, lf)) {
dblog("lfid %d (%s) spending %d time\n",lf->id,lf->race->name, howlong);
}
// 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;
}
taketime(thrower, getactspeed(thrower));
return fireat(thrower, o, 1, where, getthrowspeed(getattr(thrower, A_STR)), 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;
// make SURE we don't take any time!
notime = B_TRUE;
// decrement flags
timeeffectsflags(lf->flags);
// remove effects from expired poison
for (f = lf->flags->first ; 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);
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)) {
willfall = B_TRUE;
} else if ((dir == D_UP) && lfhasflag(lf, F_LEVITATING)) {
willfall = B_TRUE;
}
if (willfall) {
usestairs(lf, o, B_FALSE);
donesomething = B_TRUE;
o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL);
}
}
}
notime = B_FALSE;
}
// return B_TRUE on failure.
int tryclimb(lifeform_t *lf, cell_t *where, char *towhat) {
// if you have a rope or there's an adjacent wall, you can try
// to climb up
int adjwalls;
char lfname[BUFLEN];
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, 20, (countadjwalls(where)+1)/2)) {
// you made it!
if (isplayer(lf)) {
msg("You reach %s.", towhat);
} else if (cansee(player, lf)) {
msg("%s reaches %s.", towhat);
}
// train climbing
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)) {
msg("You can't reach the roof!");
}
return B_TRUE;
}
// success
return B_FALSE;
}
// returns B_TRUE if the action which involved touching this should fail
int touch(lifeform_t *lf, object_t *o) {
flag_t *f;
char buf[BUFLEN];
char obname[BUFLEN];
char lfname[BUFLEN];
if ((gamemode != GM_GAMESTARTED)) return B_FALSE;
getlfname(lf, lfname);
getobname(o, obname, o->amt);
// freezing touch?
f = hasflag(lf->flags, F_FREEZINGTOUCH);
if (f) {
// not wearing gloves?
if (!getequippedob(lf->pack, BP_HANDS)) {
// default power of 4
dospelleffects(lf, OT_S_FREEZEOB, 4, NULL, o, NULL, B_UNCURSED, NULL, B_FALSE);
// 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);
}
}
}
// undead and blesed objects?
if (isundead(lf) && isblessed(o)) {
object_t *gloves;
// not wearing gloves?
gloves = getequippedob(lf->pack, BP_HANDS);
if (!gloves) {
if (isplayer(lf)) {
msg("^bThe %s burn%s you as you touch %s!",noprefix(obname),
(o->amt == 1) ? "s" : "",
(o->amt == 1) ? "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_TRUE, B_FALSE, B_FALSE, B_TRUE, B_FALSE);
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) {
drop(o, ALL);
}
return B_TRUE;
}
}
f = hasflag(o->flags, F_SHARP);
if (f) {
object_t *gloves;
gloves = getequippedob(lf->pack, BP_HANDS);
if (!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);
}
}
// flaming objects?
if (hasflag(o->flags, F_ONFIRE)) {
// flaming weapons are ok
if (!isweapon(o)) {
object_t *gloves;
// wearing gloves? they get damaged.
gloves = getequippedob(lf->pack, BP_HANDS);
if (gloves) {
takedamage(gloves, 2, DT_FIRE);
if (!hasflag(gloves->flags, F_DEAD)) {
// if your gloves weren't destroyed the fire
// will go out.
killflagsofid(o->flags, F_ONFIRE);
}
} else {
// otherwise YOU get damaged.
if (isplayer(lf)) {
msg("^bOw! You burn your hands on %s.",obname);
} else if (cansee(player, lf)) {
msg("%s burns itself on %s.",lfname, obname);
}
snprintf(buf, BUFLEN, "touching %s",obname);
losehp(lf, 2, DT_FIRE, NULL, buf);
return B_TRUE;
}
}
}
return B_FALSE;
}
void turntoface(lifeform_t *lf, cell_t *dstcell) {
// 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 unsummon(lifeform_t *lf, int vanishobs) {
lifeform_t *creator = NULL;
flag_t *f;
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_NOCORPSE, B_TRUE, NA, NA, NULL);
addob(lf->cell->obpile, "puff of smoke");
}
int unweild(lifeform_t *lf, object_t *o) {
flag_t *f;
char obname[BUFLEN];
char buf[BUFLEN];
getobname(o, obname, 1);
if (!cantakeoff(lf, o)) {
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;
default:
if (isplayer(lf)) {
msg("For some reason, you cannot stop weilding your %s!", noprefix(obname));
}
break;
}
return B_TRUE;
}
// remove the equipped flag
f = hasflag(o->flags, F_EQUIPPED);
killflagsofid(o->flags, F_EQUIPPED);
// 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);
}
}
// lose flags
loseobflags(lf, o, F_EQUIPCONFER);
if (obproduceslight(o)) {
calclight((getoblocation(o))->map);
setlosdirty(lf);
//precalclos(lf);
drawscreen();
}
if (o->type->id == OT_ENERGYBLADE) {
stopspell(lf, OT_S_SUMMONWEAPON);
}
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_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;
}
// taketime() will happen during abiltiyeffects()
// use the ability
cwflag = lfhasflagval(lf, F_CANWILL, aid, NA, NA, NULL);
rv = abilityeffects(lf, aid, where, who, cwflag);
return rv;
}
int usestairs(lifeform_t *lf, object_t *o, int onpurpose) {
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, NULL)) {
// failed?
return B_FALSE;
}
curmap = obcell->map;
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 (hasflagval(o->flags, F_PIT, D_UP, NA, NA, NULL)) {
// can only go up if you have a rope or are flying/levitating
if (lfhasflag(lf, F_LEVITATING) || lfhasflag(lf, F_FLYING)) {
// ok.
} else {
char buf[BUFLEN];
snprintf(buf, BUFLEN, "the %s", noprefix(obname));
if (tryclimb(lf, obcell, buf)) {
// failed
return B_TRUE;
}
}
}
}
// announce
curs_set(1);
if (isportal) {
if (isplayer(lf)) {
msg("You enter %s...", obname);
// move cursor to msgwindow while we create the new level...
wrefresh(msgwin);
} else if (cansee(player, lf)) {
msg("%s enters %s...", lfname, obname);
}
} else if (hasflag(o->flags, F_PIT)) {
f = hasflag(o->flags, F_PIT);
if (isplayer(lf) || cansee(player, lf)) {
msg("%s %s %s the %s...", lfname, getpitverb(lf, dir,onpurpose), 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 staircase...", getmoveverb(lf), getdirname(dir));
// move cursor to msgwindow while we create the new level...
wrefresh(msgwin);
} else if (cansee(player, lf)) {
msg("%s %s %s the staircase.", lfname, getmoveverbother(lf), getdirname(dir));
}
}
// 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, B_FALSE);
msg("%s follows you.", lname);
}
}
}
// do stairs go somewhere?
newcell = getstairdestination(o);
if (!newcell) {
if (isportal) {
// unconnected portal
if (isplayer(lf)) msg("This portal doesn't seem to go anywhere.");
} else {
// is there already a level of the correct depth?
newmap = findregionmap(newregion->id, newdepth);
if (newmap) {
dblog("ERROR - unlinked stairs!\n");
msg("ERROR - unlinked stairs!\n");
} else {
// generate a new map! this will fill in the destination of our stairs
newmap = addmap();
// first map of a newly created region?
if (newregion->id != curmap->region->id) {
newdepth = 1;
}
createmap(newmap, newdepth, newregion, curmap, dir, o);
// at this point, stairs should have a destination (map creation will
// fill it in)
newcell = getstairdestination(o);
madenewmap = B_TRUE;
}
}
}
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)) {
cell_t *noholecell;
noholecell = real_getrandomadjcell(newcell, WE_WALKABLE, B_ALLOWEXPAND, LOF_NEED, 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 (newcell->lf) {
cell_t *c;
// if they are, find somewhere to move them.
c = getrandomadjcell(newcell, WE_WALKABLE, B_ALLOWEXPAND);
if (c) {
// move them there
movelf(newcell->lf, c);
} else {
// TODO: handle this differently - ie always allow the player
// go there?
if (isplayer(lf)) msg("The stairs seem to be blocked.");
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)) {
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, WE_WALKABLE, B_ALLOWEXPAND);
if (c) {
if (!initiatemove(adjally[n], NULL, NULL)) {
int climbtime;
stopsprinting(adjally[n]);
movelf(adjally[n], c);
climbtime = getmovespeed(adjally[n]);
if ((dir == D_UP) && !isairborne(adjally[n])) {
climbtime *= 2;
}
if (onpurpose) taketime(adjally[n], climbtime);
}
}
}
} 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 == RG_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)) {
msg("^bYou slam into the ground!");
} else if (cansee(player, lf)){
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);
// injure legs
injure(lf, BP_LEGS, DT_BASH);
// 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 != RG_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 if we have one, and drain 'charges'
// charges from it. if expired, it 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) {
if ( (o->type->id == OT_RING_MIRACLES) &&
isequipped(o) &&
getcharges(o) ) {
char obname[BUFLEN];
getobname(o, obname, 1);
if (isplayer(lf) || cansee(player, lf)) {
msg("%s%s %s flashes!", lfname, getpossessive(lfname), noprefix(obname));
}
// use a charge
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);
}
makeknown(OT_RING_MIRACLES);
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;
cell_t fakecell;
map_t fakemap;
// generate xp list
genxplist();
createfakes(&fakemap, &fakecell);
for (r = firstrace ; r ; r = r->next) {
lifeform_t *lf;
int thishd;
// add a fake lf
lf = addlf(&fakecell, r->id, 1);
givestartskills(lf, lf->flags);
// remember max. hitdice for use in dumpmonsters()
thishd = gethitdice(lf);
if (thishd > maxmonhitdice) {
maxmonhitdice = thishd;
}
if (!hasflag(r->flags, F_SIZE)) {
printf("ERROR in race '%s' - missing F_SIZE.\n", r->name);
goterror = B_TRUE;
}
for (f = r->flags->first ; f ; f = f->next) {
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 (strlen(f->text)) {
if (!strchr(f->text, 'd')) {
printf("ERROR in race '%s' - F_HASATTACK text does not seem to be in 'xdy' format.\n", r->name);
goterror = B_TRUE;
}
} else {
printf("ERROR in race '%s' - F_HASATTACK with no damage value.\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_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_PLAYABLE) {
// playable races must have fully defined stats
enum ATTRIB a;
for (a = 0 ; a < MAXATTS; a++) {
if (!hasflagval(r->flags, F_STARTATT, a, NA, NA, NULL)) {
printf("ERROR in race '%s' - race is f_playable but has no f_startatt %s\n", r->name, getattrname(a));
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.", r->name);
goterror = B_TRUE;
}
} else if (f->id == F_NOISETEXT) {
if (f->val[0] == N_FLY) {
if (!hasflag(r->flags, F_FLYING) && !hasflag(r->flags, F_LEVITATING)) {
printf("ERROR in race '%s' - has NOISETEXT N_FLY but isn't flying.", r->name);
goterror = B_TRUE;
}
}
if (f->val[1] == NA) {
printf("ERROR in race '%s' - has NOISETEXT but no volume.", 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);
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);
noredraw = B_FALSE;
return goterror;
}
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) {
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) {
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 (f->val[0] >= healtime) {
int difficulty;
if (isplayer(lf)) {
// ie. for hp, at con <= 4 and no first aid skill, you NEVER Heal!
difficulty = 25;
} else {
// ai passes more easily
difficulty = 10;
}
// 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 (isplayer(lf) && (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)) {
pleasegodmaybe(R_GODMERCY, 1);
}
}
}
if (lf->mp < getmaxmp(lf)) {
// pass a skill check to regain mp
if (skillcheck(lf, SC_IQ, difficulty, getskill(lf, SK_SPELLCASTING))) {
gainmp(lf, mpheal);
}
}
killflag(f);
}
}
}
if (training) {
wantclearmsg = B_FALSE;
rf->val[2]--;
if (rf->val[2] == 0) {
// 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 (lf->hp >= lf->maxhp) {
hf = lfhasflag(lf, F_RESTUNTILHP);
if (hf) killflag(hf);
}
if (lf->mp >= getmaxmp(lf)) {
hf = lfhasflag(lf, F_RESTUNTILMP);
if (hf) killflag(hf);
}
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 (l->hp < l->maxhp) {
moretogo = B_TRUE;
break;
}
}
}
if (!moretogo) {
killflag(hf);
}
}
if (!lfhasflag(lf, F_RESTUNTILHP) &&
!lfhasflag(lf, F_RESTUNTILMP) &&
!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) killflag(rf);
wantclearmsg = B_FALSE;
}
}
} // end if !poisoned
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];
flag_t *f;
enum BODYPART possbp[MAXBODYPARTS];
int nparts = 0;
enum BODYPART bp;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
int showpos = B_FALSE;
// this might impact your AR
if (isplayer(lf)) {
statdirty = B_TRUE;
}
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) {
bp = possbp[0];
} else { // multiple possible locations
int i;
showpos = B_TRUE;
// rings go in first possible place
if (o->type->obclass->id == OC_RING) {
bp = BP_NONE;
for (i = 0; i < nparts; i++) {
if (!hasobwithflagval(lf->pack, F_EQUIPPED, possbp[i], NA, NA, NULL)) {
bp = possbp[i];
break;
}
}
if ((bp == BP_NONE) && isplayer(lf)) msg("You have no room to wear that.");
} 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 = getequippedob(lf->pack, possbp[i]);
if (inway) {
char inwayname[BUFLEN];
getobname(inway, inwayname, inway->amt);
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);
ch = getchoice(&prompt);
if (ch == '-') {
bp = BP_NONE;
if (isplayer(lf)) msg("Cancelled.");
} else bp = *((enum BODYPART *)prompt.result);
}
if (bp == BP_NONE) {
if (isplayer(lf)) {
msg("You have no room to wear that.");
}
return B_TRUE;
}
//}
}
while (!canwear(lf, o, bp)) {
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_INJURED:
if (isplayer(lf)) msg("Your injuries prevent you from wearing this.");
break;
case E_WEARINGSOMETHINGELSE:
f = hasflag(o->flags, F_GOESON);
if (f) {
object_t *inway;
// find what else is there
inway = getequippedob(lf->pack, f->val[0]);
getobname(inway,buf, 1);
if (isplayer(lf)) {
int ch;
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));
ch = askchar(buf2, "yn","y", B_TRUE);
if (ch == 'y') {
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;
}
}
}
}
}
} else {
// should never happen
if (isplayer(lf)) msg("You can't wear that!");
}
break;
case E_NOTKNOWN:
if (isplayer(lf)) msg("You can't wear that!");
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;
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
// 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;
}
// wear it
//f = hasflag(o->flags, F_GOESON);
//bp = f->val[0];
addflag(o->flags, F_EQUIPPED, bp, -1, -1, NULL);
taketime(lf, getactspeed(lf));
maketried(o->type->id);
// 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)) {
if (showpos) {
msg("You are now wearing %s (%s your %s).", obname, getbodypartequipname(bp), getbodypartname(lf, bp));
} else {
msg("You are now wearing %s.", obname);
}
} else if (cansee(player, lf)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s puts on %s.", buf, 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);
}
}
}
// give flags
giveobflags(lf, o, F_EQUIPCONFER);
return rv;
}
int weild(lifeform_t *lf, object_t *o) {
char buf[BUFLEN];
flag_t *f;
object_t *oo;
int twohanded = B_FALSE;
enum BODYPART weildloc,otherloc;
// this might impact your AR
if (isplayer(lf)) {
statdirty = B_TRUE;
}
if (o) {
getobname(o, buf, o->amt);
} 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_NOHANDS:
msg("You do not have enough free hands to weild this weapon.");
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, &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 && !hasflag(oo->flags, F_TWOHANDED)) {
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);
} else {
if (getweaponskill(lf, o)) ch = 'y';
else ch = 'n';
}
if (ch == 'y') {
// make sure we are skiled in 2nd weapon
if (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;
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));
ch = askchar(buf2, "yn","y", B_TRUE);
}
} else {
ch = 'y';
}
if (ch == 'y') {
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;
}
}
} else {
return B_TRUE;
}
} else {
// just unweild it
if (unweild(lf, oo)) {
// if we can't unweild old weapon, stop
return B_TRUE;
}
}
}
// 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)) {
// just unweild it
if (unweild(lf, oo)) {
// if we can't unweild old weapon, stop
return B_TRUE;
}
} 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);
}
} else {
ch = 'y';
}
if (ch == 'y') {
if (isarmour(oo)) {
if (takeoff(lf, oo)) {
// if we can't remove it, stop.
return B_TRUE;
}
} else {
if (unweild(lf, oo)) {
// if we can't remove it, stop.
return B_TRUE;
}
}
} else {
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->id != 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;
}
// now weild this
addflag(o->flags, F_EQUIPPED, weildloc, -1, -1, NULL);
if (hasflag(o->flags, F_TWOHANDED)) {
addflag(o->flags, F_EQUIPPED, otherloc, -1, -1, NULL);
}
taketime(lf, getactspeed(lf));
maketried(o->type->id);
if ((gamemode == GM_GAMESTARTED) && lf->created && (lf->race->id != 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);
if (sk && !getskill(lf, sk->id)) {
msg("^wYou feel rather inept with this weapon.");
}
}
} else if (cansee(player, lf)) {
if (lf->race->id != R_DANCINGWEAPON) {
char buf2[BUFLEN];
getlfname(lf, buf2);
msg("%s weilds %s.", buf2, buf);
}
}
touch(lf, o);
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_ARMOURPIERCE);
if (f) {
msg("^gYour %s seems unnaturally sharp!",noprefix(buf));
f->known = B_TRUE;
}
}
return B_FALSE;
}
int willbleedfrom(lifeform_t *lf, enum BODYPART bp) {
object_t *o;
o = getequippedob(lf->pack, bp);
if (o && (o->type->id == OT_BANDAGE)) {
// don't bleed.
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) + (getobunitweight(o)*howmany) > getmaxcarryweight(lf) ) {
return B_TRUE;
}
return B_FALSE;
}
// will the lf flee after taking damage?
int willflee(lifeform_t *lf) {
enum ATTRBRACKET iqb;
flag_t *f;
if (hasflag(lf->flags, F_NOFLEE)) {
return B_FALSE;
}
if (hasflag(lf->flags, F_FLEEONDAM)) {
return B_TRUE;
}
iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL);
if ((iqb >= AT_GTAVERAGE) && isbleeding(lf)) {
return B_TRUE;
}
f = hasflag(lf->flags, F_FLEEONHPPCT);
if (f) {
if (gethppct(lf) <= f->val[0]) {
return B_TRUE;
}
}
return B_FALSE;
}