23982 lines
578 KiB
C
23982 lines
578 KiB
C
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <signal.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 "save.h"
|
|
#include "shops.h"
|
|
#include "spell.h"
|
|
#include "text.h"
|
|
|
|
extern double presin[];
|
|
extern double precos[];
|
|
|
|
extern FILE *logfile;
|
|
|
|
extern int noredraw;
|
|
extern int enteringmap;
|
|
|
|
extern map_t *firstmap;
|
|
extern race_t *firstrace, *lastrace;
|
|
extern raceclass_t *firstraceclass, *lastraceclass;
|
|
extern behaviour_t *firstbehaviour, *lastbehaviour;
|
|
extern job_t *firstjob, *lastjob;
|
|
extern skill_t *firstskill, *lastskill;
|
|
extern poisontype_t *firstpoisontype,*lastpoisontype;
|
|
extern subjob_t *firstsubjob,*lastsubjob;
|
|
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;
|
|
extern void *rdata;
|
|
|
|
extern object_t *retobs[MAXPILEOBS+1];
|
|
extern int retobscount[MAXPILEOBS+1];
|
|
extern int nretobs;
|
|
|
|
// 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 donesecondary = B_FALSE;
|
|
int pretimespent;
|
|
|
|
pretimespent = lf->timespent;
|
|
|
|
if (isplayer(lf)) {
|
|
dblog("db: calling autoweild for player.");
|
|
}
|
|
|
|
// weild weapons if required
|
|
bestwep = getbestweapon(lf);
|
|
if (bestwep) {
|
|
weild(lf, bestwep);
|
|
}
|
|
|
|
bestfirearm = getbestfirearm(lf);
|
|
if (bestfirearm) {
|
|
weild(lf, bestfirearm);
|
|
}
|
|
|
|
// weild armour/2nd weapons if required,
|
|
// and mark other weapons as secondary
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (isweapon(o) && !isequipped(o) && getskill(lf, SK_TWOWEAPON) && !getequippedob(lf->pack, BP_SECWEAPON) && canweild(lf, o)) {
|
|
weild(lf, o);
|
|
} else if (!donesecondary && isweapon(o) && !isequipped(o)) {
|
|
addflag(o->flags, F_SECONDARY, B_TRUE, NA, NA, NULL);
|
|
donesecondary = B_TRUE;
|
|
} else {
|
|
if (canwear(lf, o, BP_NONE)) {
|
|
wear(lf, o);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// start using ammo if required
|
|
//if (isplayer(lf)) {
|
|
firearm = getfirearm(lf);
|
|
if (firearm) {
|
|
o = getrandomammo(lf);
|
|
if (o) {
|
|
loadfirearm(NULL, firearm, o);
|
|
}
|
|
}
|
|
//}
|
|
|
|
// make sure monsters have ammo for their weapons
|
|
if (!isplayer(lf) && firearm && !getammo(firearm)) {
|
|
objecttype_t *ot;
|
|
// make some ammo
|
|
ot = getrandomammofor(firearm, B_FALSE);
|
|
if (ot) {
|
|
char buf[BUFLEN];
|
|
snprintf(buf, BUFLEN, "1-5 %s",ot->name);
|
|
o = addob(lf->pack, buf);
|
|
loadfirearm(NULL, firearm, o);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// make sure it doesn't take any time
|
|
lf->timespent = pretimespent;
|
|
}
|
|
|
|
int appearsrandomly(enum RACE rid) {
|
|
race_t *r;
|
|
r = findrace(rid);
|
|
if (!r) return B_FALSE;
|
|
|
|
if (!hasflag(r->flags, F_RARITY)) {
|
|
return B_FALSE;
|
|
} else if (hasflagval(r->flags, F_RARITY, NA, F_UNIQUE, NA, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
void awardxpfor(lifeform_t *killed, float pct) {
|
|
int xpval,xpeach,i,nposs;
|
|
int initialxp;
|
|
lifeform_t *poss[MAXCANDIDATES], *l;
|
|
int n;
|
|
|
|
|
|
initialxp = calcxp(killed);
|
|
assert(initialxp < 10000);
|
|
assert(initialxp >= 0);
|
|
|
|
xpval = (int) ((float)initialxp * (pct/100));
|
|
assert(xpval < 10000);
|
|
assert(xpval >= 0);
|
|
|
|
// find all allies on the map
|
|
nposs = 0;
|
|
n = 0;
|
|
for (l = killed->cell->map->lf ; l ; l = l->next) {
|
|
if ((l != killed) && (getallegiance(l) == AL_FRIENDLY)) {
|
|
poss[nposs] = l;
|
|
nposs++;
|
|
}
|
|
}
|
|
if (nposs == 0) return;
|
|
|
|
xpeach = xpval / nposs;
|
|
|
|
for (i = 0; i < nposs; i++) {
|
|
gainxp(poss[i], xpeach);
|
|
}
|
|
}
|
|
|
|
void bleed(lifeform_t *lf, int splatter) {
|
|
flag_t *f;
|
|
char obname[BUFLEN];
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
if (lf->cell->type->solid) return;
|
|
if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) return;
|
|
|
|
if (hasequippedobid(lf->pack, OT_AMU_BLOOD)) return;
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_BLOODOB, F_NONE);
|
|
if (nretflags) {
|
|
// pick a random one
|
|
f = retflag[rnd(0,nretflags-1)];
|
|
if (f->text) {
|
|
strcpy(obname, f->text);
|
|
} else {
|
|
strcpy(obname, "");
|
|
}
|
|
} else {
|
|
strcpy(obname, "splash of blood");
|
|
}
|
|
|
|
if (strlen(obname) > 0) {
|
|
int nfillpot = 0;
|
|
object_t *o;
|
|
o = addob(lf->cell->obpile, obname);
|
|
|
|
if (hasflag(lf->flags, F_FILLPOT)) {
|
|
// override object flags
|
|
killflagsofid(o->flags, F_FILLPOT);
|
|
}
|
|
nfillpot = copyflag(o->flags, lf->flags, F_FILLPOT);
|
|
|
|
|
|
if (splatter) {
|
|
addobsinradius(lf->cell, 1, DT_COMPASS, obname, B_TRUE, NULL);
|
|
if (nfillpot) {
|
|
int i;
|
|
for (i = 0; i < nretobs; i++) {
|
|
copyflag(retobs[i]->flags, lf->flags, F_FILLPOT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int bleedfrom(lifeform_t *lf, enum BODYPART bp, int splatter) {
|
|
if (willbleedfrom(lf, bp)) {
|
|
bleed(lf, splatter);
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int bpcantakearmour(lifeform_t *lf, enum BODYPART bp) {
|
|
int i;
|
|
// unnaturally shaped limbs?
|
|
for (i = 0; i < lf->race->nbodyparts; i++) {
|
|
if ((lf->race->bodypart[i].id == bp) && !lf->race->bodypart[i].armourok) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
void breakgrabs(lifeform_t *lf, int fromme, int tome) {
|
|
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);
|
|
}
|
|
killflagsofid(lf->flags, F_ATTACHEDTO);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void breakaitargets(lifeform_t *lf, int onlylowerlev) {
|
|
lifeform_t *l;
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
flag_t *f;
|
|
if (l == lf) continue;
|
|
if (!onlylowerlev || (gettr(l) <= gettr(lf)) ) {
|
|
f = lfhasflagval(l, F_TARGETLF, lf->id, NA, NA, NULL);
|
|
if (f) killflag(f);
|
|
f = lfhasflagval(l, F_TARGETCELL, lf->cell->x, lf->cell->y, NA, NULL);
|
|
if (f) killflag(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
long calcscore(lifeform_t *lf) {
|
|
flag_t *f;
|
|
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);
|
|
|
|
f = hasflag(player->flags, F_WINNER);
|
|
if (f) {
|
|
if (f->val[0] == WT_DEMIGOD) {
|
|
points *= 13;
|
|
} else if (f->val[0] == WT_GOD) {
|
|
points *= 23;
|
|
}
|
|
}
|
|
|
|
return points;
|
|
}
|
|
|
|
// figure out how much xp a race is worth
|
|
int calcxp(lifeform_t *lf) {
|
|
float multiplier = 1;
|
|
float xpval = 0;
|
|
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_XPMULTIPLY);
|
|
if (f) {
|
|
multiplier = f->val[0];
|
|
}
|
|
|
|
f = lfhasflag(lf, F_XPVAL);
|
|
if (f) {
|
|
if (db) dblog("calcxp: got F_XPVAL, forcing result to %d\n",f->val[0]);
|
|
return f->val[0] * multiplier;
|
|
}
|
|
|
|
// attack
|
|
|
|
// - get average attack damage
|
|
avgdam = getavgdam(lf, B_TRUE);
|
|
if (db) dblog("calcxp: avg damage dealt is %0.1f",avgdam);
|
|
|
|
|
|
// defense
|
|
// -- hitdice
|
|
f = lfhasflag(lf, F_HITDICE);
|
|
if (f) {
|
|
maxhdroll = flagtomaxhp(f);
|
|
} else {
|
|
maxhdroll = 4;
|
|
}
|
|
|
|
defence += (maxhdroll * lf->level);
|
|
//defence /= 2;
|
|
|
|
// -- evasion ?
|
|
getflags(lf->race->flags, retflag, &nretflags, F_EVASION, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->val[0] > 0) {
|
|
defence += f->val[0];
|
|
}
|
|
}
|
|
|
|
// -- armour
|
|
f = hasflag(lf->race->flags, F_ARMOURRATING);
|
|
if (f && (f->val[0] > 0)) {
|
|
defence += pctof(150, f->val[0]);
|
|
}
|
|
|
|
|
|
if (db) dblog("calcxp: DEFENCE IS %0.1f",defence);
|
|
|
|
|
|
// spells
|
|
getflags(lf->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_CANCAST) {
|
|
objecttype_t *ot;
|
|
int power,level;
|
|
ot = findot(f->val[0]);
|
|
power = getspellpower(lf, f->val[0]);
|
|
level = getspelllevel(f->val[0]);
|
|
spells += (power * level);
|
|
if (db) dblog("cancast %s: == power %d * spelllevel %d = %d",ot->name, power, level, power*level);
|
|
} else if (f->id == F_CANWILL) {
|
|
objecttype_t *ot;
|
|
// is this a spell?
|
|
ot = findot(f->val[0]);
|
|
if (ot && ot->obclass->id == OC_SPELL) {
|
|
int power,level;
|
|
power = getspellpower(lf, ot->id);
|
|
level = getspelllevel(ot->id);
|
|
spells += (power * level);
|
|
if (db) dblog("canwill spell %s: == power %d * spelllevel %d = %d",ot->name, power, level, power*level);
|
|
} else {
|
|
char damstr[BUFLEN];
|
|
int needgrab = B_FALSE;
|
|
objecttype_t *ot;
|
|
|
|
flag_t *af;
|
|
|
|
ot = findot(f->val[0]);
|
|
|
|
af = hasflag(ot->flags, F_XPVAL);
|
|
if (af) {
|
|
float thisamt = af->val[0];
|
|
if (f->val[2] == NA) {
|
|
if (db) dblog("canwill ability %s == %0.1f",ot->name, thisamt);
|
|
} else {
|
|
thisamt /= f->val[2];
|
|
if (db) dblog("canwill ability %s every %d turns == %0.1f",ot->name, f->val[2], thisamt);
|
|
}
|
|
spells += thisamt;
|
|
}
|
|
|
|
// other special cases
|
|
switch (f->val[0]) {
|
|
case OT_A_SWOOP:
|
|
if (db) dblog("canwill SWOOP - increasing avg dam from %0.1f to %0.1f.", avgdam, avgdam*1.1);
|
|
avgdam *= 1.1; // attack bonus!
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// get dam from text
|
|
texttospellopts(f->text, "dam:", damstr, "needgrab:", &needgrab, NULL );
|
|
if (strlen(damstr)) {
|
|
int dice,sides,mod,min,max;
|
|
float thisavg;
|
|
texttodice(damstr, &dice,&sides,&mod);
|
|
dicetotext(dice,sides,mod, &min, &max, damstr, NULL);
|
|
thisavg = ((min+max)/2);
|
|
|
|
if (db) dblog("canwill ability %s causes dam %0.1f",ot->name, thisavg);
|
|
// adjust if you have to grab first
|
|
if (needgrab) {
|
|
thisavg /= 2;
|
|
if (db) dblog(" (adjusted to %0.1f due to needing to grab)", thisavg);
|
|
}
|
|
// adjust based on how often we can do it
|
|
if (f->val[2] != NA) {
|
|
thisavg /= f->val[2];
|
|
if (db) dblog(" (can do every %d turns. --> %0.1f)", f->val[2], thisavg);
|
|
}
|
|
if (ot->id == OT_A_GRAB) {
|
|
// assume can only grab once in the 10 turns.
|
|
thisavg /= 10;
|
|
}
|
|
avgdam += thisavg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// -- avg damage in 10 turns
|
|
if (db) dblog("calcxp: avg damage dealt (with abilities) is %0.1f",avgdam);
|
|
avgdam *= 10;
|
|
offense = avgdam;
|
|
if (db) dblog("calcxp: ATTACKVAL IS %0.1f",offense);
|
|
|
|
|
|
// extra 'spells'
|
|
if (hasflag(lf->race->flags, F_INDUCEFEAR)) {
|
|
spells += 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 (!getstamina(lf)) {
|
|
reason = E_NOSTAM;
|
|
return B_FALSE;
|
|
} else if (lfhasflag(lf, F_STUNNED)) {
|
|
reason = E_STUNNED;
|
|
return B_FALSE;
|
|
} else if (lfhasflag(lf, F_NOATTACK)) {
|
|
reason = E_IMPOSSIBLE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
|
|
int castable = B_FALSE;
|
|
flag_t *f;
|
|
objecttype_t *ot;
|
|
int stamcost = 0;
|
|
// TODO: check for mute?
|
|
|
|
if (lfhasflag(lf, F_STUNNED)) {
|
|
reason = E_STUNNED;
|
|
return B_FALSE;
|
|
}
|
|
if (isprone(lf) && (oid != OT_A_FEIGNDEATH)) {
|
|
reason = E_PRONE;
|
|
return B_FALSE;
|
|
}
|
|
if (isswimming(lf) && (getskill(lf, SK_SWIMMING) < PR_EXPERT)) {
|
|
if (!isaquatic(lf)) {
|
|
reason = E_SWIMMING;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
if (isclimbing(lf)) {
|
|
reason = E_CLIMBING;
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (hassubjob(lf, SJ_PALADIN)) {
|
|
object_t *o;
|
|
// using cursed weapons/armour? no spells.
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (isequipped(o) && iscursed(o)) {
|
|
if (isweapon(o)) {
|
|
reason = E_PALADIN;
|
|
return B_FALSE;
|
|
} else if (isarmour(o)) {
|
|
reason = E_PALADIN2;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
reason = E_OK;
|
|
|
|
ot = findot(oid);
|
|
|
|
f = lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL);
|
|
if (f) {
|
|
int needgrab = B_FALSE;
|
|
|
|
// flying with broken/destroyed wings?
|
|
if ((oid == OT_S_FLIGHT) && (f->lifetime == FROMRACE)) {
|
|
if (lfhasflagval(lf, F_INJURY, IJ_WINGTORN, NA, NA, NULL)) {
|
|
reason = E_INJURED;
|
|
return B_FALSE;
|
|
} else if (lfhasflagval(lf, F_NOBODYPART, BP_WINGS, NA, NA, NULL)) {
|
|
reason = E_INJURED;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// get canwill opts
|
|
texttospellopts(f->text, "needgrab:", &needgrab, NULL);
|
|
|
|
// no mp cost.
|
|
if (mpcost) *mpcost = 0;
|
|
// ability ready?
|
|
if (f->val[1] == f->val[2]) {
|
|
castable = B_TRUE;
|
|
} else {
|
|
reason = E_NOTREADY;
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (needgrab || hasflag(ot->flags, F_NEEDSGRAB)) {
|
|
if (!lfhasflag(lf, F_GRABBING)) {
|
|
reason = E_NEEDGRAB;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
} else if ((f = lfhasflagval(lf, F_CANCAST, oid, NA, NA, NULL)) != NULL) {
|
|
int cost,power;
|
|
|
|
// can only cast innate racial spels?
|
|
if ((ot->obclass->id == OC_SPELL) && lfhasflag(lf, F_NOSPELLS)) {
|
|
if (f->lifetime != FROMRACE) {
|
|
reason = E_NOSPELLS;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// need >animal intelligence to cast spells
|
|
if (ot->obclass->id == OC_SPELL) {
|
|
if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= IQ_ANIMAL) {
|
|
if (!lfhasflag(lf, F_CASTWITHOUTIQ)) {
|
|
reason = E_LOWIQ;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// for mosnters: spell ready?
|
|
if (f->val[1] == f->val[2]) {
|
|
castable = B_TRUE;
|
|
} else {
|
|
reason = E_NOTREADY;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// how powerful is this spell?
|
|
power = getspellpower(lf, oid);
|
|
if (power <= 0) {
|
|
reason = E_TOOPOWERFUL;
|
|
return B_FALSE;
|
|
}
|
|
// how much mp does it take to cast this?
|
|
if (isplayer(lf)) {
|
|
cost = getmpcost(lf, oid);
|
|
} else {
|
|
cost = 0;
|
|
}
|
|
if (mpcost) *mpcost = cost;
|
|
if (lf->mp >= cost) {
|
|
castable = B_TRUE;
|
|
} else {
|
|
reason = E_NOMP;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// do we have enough stamina to do this?
|
|
stamcost = getstamcost(lf, oid);
|
|
if (stamcost && (getstamina(lf) < stamcost)) {
|
|
reason = E_NOSTAM;
|
|
return B_FALSE;
|
|
}
|
|
|
|
return castable;
|
|
}
|
|
|
|
int cancook(lifeform_t *lf, recipe_t *rec, enum ERROR *reason) {
|
|
// skill high enough?
|
|
if (!canmakerecipe(lf, rec)) {
|
|
if (reason) *reason = E_TOOHARD;
|
|
return B_FALSE;
|
|
}
|
|
// do we have the ingredients?
|
|
if (getingredients(lf->pack, rec, NULL, NULL, NULL, NULL, B_FALSE)) {
|
|
if (reason) *reason = E_NOOB;
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int canclimb(lifeform_t *lf, enum ERROR *reason) {
|
|
cell_t *cc;
|
|
cc = getcellindir(lf->cell, lf->facing);
|
|
if (isclimbing(lf)) {
|
|
if (reason) *reason = E_CLIMBING;
|
|
return B_FALSE;
|
|
} else if (!cc) {
|
|
if (reason) *reason = E_BADCLIMBDIR;
|
|
return B_FALSE;
|
|
} else if (!cc->type->solid) {
|
|
if (hasobwithflagval(cc->obpile, F_PIT, D_DOWN, NA, NA, NULL)) {
|
|
// ok
|
|
} else {
|
|
if (reason) *reason = E_BADCLIMBDIR;
|
|
return B_FALSE;
|
|
}
|
|
} else if (cc->lf) {
|
|
if (reason) *reason = E_LFINWAY;
|
|
return B_FALSE;
|
|
} else if (isswimming(lf)) {
|
|
if (reason) *reason = E_SWIMMING;
|
|
return B_FALSE;
|
|
} else if (isimmobile(lf)) {
|
|
if (reason) *reason = E_CANTMOVE;
|
|
return B_FALSE;
|
|
} else if (isburdened(lf) || lfhasflag(lf, F_GRAVBOOSTED)) {
|
|
if (reason) *reason = E_TOOHEAVY;
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int candrink(lifeform_t *lf, object_t *o) {
|
|
// undead won't drink
|
|
if (getraceclass(lf) == RC_UNDEAD) {
|
|
reason = E_UNDEAD;
|
|
return B_FALSE;
|
|
}
|
|
if (!isdrinkable(o)) {
|
|
reason = E_WRONGOBTYPE;
|
|
return B_FALSE;
|
|
}
|
|
reason = E_OK;
|
|
return B_TRUE;
|
|
}
|
|
|
|
// if false, returns why you can't eat it in 'reason'
|
|
int caneat(lifeform_t *lf, object_t *o) {
|
|
// undead will only eat fleshy corpses
|
|
if (getraceclass(lf) == RC_UNDEAD) {
|
|
if (hasflag(o->flags, F_CORPSEOF) && (o->material->id == MT_FLESH)) {
|
|
return B_TRUE;
|
|
}
|
|
reason = E_UNDEAD;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// note: must do this check _before_ call to isedible()!
|
|
if (lfhasflagval(lf, F_CANEATMATERIAL, o->material->id, NA, NA, NULL)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!isedible(o)) {
|
|
reason = E_WRONGOBTYPE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (!isplayer(lf) && lfhasflag(lf, F_RAGE)) {
|
|
reason = E_WONT;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// ring of hunger overrides most eating checks
|
|
if (!hasequippedobid(lf->pack, OT_RING_HUNGER)) {
|
|
// ai won't eat bad food
|
|
if (!isplayer(lf) && isrotting(o)) {
|
|
reason = E_WONT;
|
|
return B_FALSE;
|
|
}
|
|
if ((o->type->id == OT_CORPSE) || (o->type->id == OT_HEAD)) {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_CORPSEOF);
|
|
if (f) {
|
|
race_t *r;
|
|
r = findrace(f->val[0]);
|
|
// same race? let the player do so.
|
|
if (!isplayer(lf)) {
|
|
if ((r->id == lf->race->id) || (r->baseid == lf->race->baseid)) {
|
|
enum RACECLASS rc;
|
|
rc = getraceclass(lf);
|
|
// race which doens't eat its own?
|
|
if ((rc == RC_ANIMAL) || (rc == RC_HUMANOID)) {
|
|
// no cannibulism!
|
|
reason = E_NOCANNIBUL;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lfhasflag(lf, F_VEGETARIAN) && hasflag(o->flags, F_ISMEAT)) {
|
|
reason = E_VEGETARIAN;
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_CARNIVORE) && !hasflag(o->flags, F_ISMEAT)) {
|
|
reason = E_CARNIVORE;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (lfhasflag(lf, F_PARTVEGETARIAN) && hasflag(o->flags, F_ISMEAT)) {
|
|
if (gethungerlevel(gethungerval(player)) < H_PECKISH) {
|
|
reason = E_PARTVEGETARIAN;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// ai lifeforms won't eat tainted food unless they can handle it
|
|
if (!isplayer(lf)) {
|
|
if (hasflag(o->flags, F_TAINTED) && !lfhasflag(lf, F_CANEATRAW)) {
|
|
reason = E_WONT;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
int canhaverandombehaviour(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_BEHAVIOUR)) return B_FALSE;
|
|
if (isundead(lf)) return B_FALSE;
|
|
if (isgod(lf)) return B_FALSE;
|
|
if (getraceclass(lf) == RC_HUMANOID) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// ie. will sound from 'dest' reach the ears of 'lf'
|
|
int canhear(lifeform_t *lf, cell_t *dest, int volume) {
|
|
int 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;
|
|
|
|
celldist = getcelldist(lf->cell, dest);
|
|
|
|
// for player only:
|
|
// can't hear if you have a hostile mosnter next to you
|
|
// and you're not blind.
|
|
// (you're too engrossed in the battle)
|
|
/*
|
|
if (isplayer(lf) && isinbattle(lf)) {
|
|
if (celldist != 1) return B_FALSE;
|
|
}
|
|
*/
|
|
|
|
map = dest->map;
|
|
|
|
x1 = lf->cell->x;
|
|
y1 = lf->cell->y;
|
|
x2 = dest->x;
|
|
y2 = dest->y;
|
|
|
|
calcbresnham(map, x1, y1, x2, y2, retcell, &numpixels );
|
|
|
|
// get sound celldistance
|
|
//sounddist = gethearingrange(lf);
|
|
sounddist = getsounddist(volume);
|
|
if (sounddist < celldist) { // sound won't travel far enough
|
|
return B_FALSE;
|
|
}
|
|
|
|
for (i = 0; i < numpixels ; i++) {
|
|
cell_t *cell;
|
|
|
|
cell = retcell[i];
|
|
|
|
// don't need to move out of the last one
|
|
if ((cell->x == x2) && (cell->y == y2)) {
|
|
break;
|
|
}
|
|
// you can always hear your own cell
|
|
if (i != 0) {
|
|
// solid cells decrease hearing range
|
|
if (cell->type->solid) {
|
|
sounddist--;
|
|
}
|
|
// magic barriers stop all sound
|
|
if (hasob(cell->obpile, OT_MAGICBARRIER)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
// hearing range decreases by one
|
|
sounddist--;
|
|
}
|
|
|
|
if (sounddist <= 0) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
//
|
|
int cannotmove(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_PARALYZED) || lfhasflag(lf, F_FROZEN) || lfhasflag(lf, F_ASLEEP)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int canlearn(lifeform_t *lf, enum SKILL skid) {
|
|
if (ismaxedskill(lf, skid)) return B_FALSE;
|
|
|
|
if (lfhasflagval(lf, F_NOSKILL, skid, NA, NA, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int canmakerecipe(lifeform_t *lf, recipe_t *rec) {
|
|
if (getskill(lf, SK_COOKING) >= rec->ningredients) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int canopendoors(lifeform_t *lf) {
|
|
if (lf) {
|
|
if (!lfhasflag(lf, F_HUMANOID)) return B_FALSE;
|
|
if (!hasbp(lf, BP_HANDS)) return B_FALSE;
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int canpickup(lifeform_t *lf, object_t *o, int amt) {
|
|
reason = E_OK;
|
|
|
|
if (amt == ALL) {
|
|
amt = o->amt;
|
|
}
|
|
|
|
if (hasflag(o->flags, F_NOPICKUP)) {
|
|
reason = E_NOPICKUP;
|
|
return B_FALSE;
|
|
}
|
|
if (isimpassableob(o, lf, getlfsize(lf))) {
|
|
reason = E_TOOBIG;
|
|
return B_FALSE;
|
|
}
|
|
if (lf) {
|
|
if (isstuck(lf) == o) {
|
|
reason = E_STUCK;
|
|
return B_FALSE;
|
|
}
|
|
if (getobsize(o) > getlfsize(lf)) {
|
|
reason = E_TOOBIG;
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_NOPACK)) {
|
|
reason = E_NOPACK;
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_GRAVBOOSTED)) {
|
|
reason = E_GRAVBOOSTED;
|
|
return B_FALSE;
|
|
}
|
|
if ((getlfmaterial(lf) == MT_GAS) || lfhasflag(lf, F_NONCORPOREAL)) {
|
|
reason = E_INSUBSTANTIAL;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// too heavy to lift?
|
|
//max = getlfweight(lf, B_NOOBS) * 2; // twice your body weight
|
|
if (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;
|
|
}
|
|
if (isswimming(lf) || isclimbing(lf)) {
|
|
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;
|
|
}
|
|
|
|
|
|
// can lf reach victim to attack them?
|
|
int canreach(lifeform_t *lf, lifeform_t *victim, int *reachpenalty) {
|
|
enum LFSIZE sizetoreach = SZ_ANY;
|
|
int pen = 0;
|
|
// harder to hit flying/levitating enemies, unless you are
|
|
// large (ie. tall) enough to reach them.
|
|
// if you are smaller than "sizetoreach", you get a penalty
|
|
if (reachpenalty) *reachpenalty = 0;
|
|
if (!isairborne(lf)) {
|
|
switch (isairborne(victim)) {
|
|
case F_FLYING:
|
|
sizetoreach = SZ_HUMAN;
|
|
break;
|
|
case F_LEVITATING:
|
|
sizetoreach = SZ_MEDIUM;
|
|
break;
|
|
default: sizetoreach = SZ_ANY; break;
|
|
}
|
|
}
|
|
|
|
if (sizetoreach == SZ_ANY) {
|
|
// harder to hit something which is climbing above you
|
|
if (isclimbing(victim) && !isclimbing(lf)) {
|
|
if (getskill(lf, SK_CLIMBING) != PR_MASTER) {
|
|
if (!lfhasflag(lf, F_SPIDERCLIMB)) {
|
|
sizetoreach = SZ_LARGE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sizetoreach == SZ_ANY) {
|
|
if (reachpenalty) *reachpenalty = 0;
|
|
return B_TRUE;
|
|
}
|
|
pen = sizetoreach - getlfsize(lf);
|
|
if (pen > 0) {
|
|
if (reachpenalty) *reachpenalty = (sizetoreach - getlfsize(lf));
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// can lf reach victim's bodypart bp?
|
|
int canreachbp(lifeform_t *lf, lifeform_t *victim, enum BODYPART bp) {
|
|
int howmuchsmaller;
|
|
int lfsize, victimsize;
|
|
|
|
// can't reach them at all!
|
|
if (!canreach(lf, victim, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
lfsize = getlfsize(lf);
|
|
lfsize += getflightsizemod(lf);
|
|
victimsize = getlfsize(victim);
|
|
victimsize += getflightsizemod(victim);
|
|
|
|
howmuchsmaller = victimsize - lfsize;
|
|
if (howmuchsmaller <= 0) return B_TRUE;
|
|
|
|
switch (bp) {
|
|
// upper
|
|
case BP_EYES:
|
|
case BP_HEAD:
|
|
case BP_HEAD2:
|
|
case BP_HEAD3:
|
|
case BP_EARS:
|
|
case BP_NECK:
|
|
case BP_SHOULDERS:
|
|
if (howmuchsmaller >= 1) {
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
case BP_WEAPON:
|
|
case BP_SECWEAPON:
|
|
case BP_HANDS:
|
|
case BP_RIGHTFINGER:
|
|
case BP_LEFTFINGER:
|
|
if (howmuchsmaller >= 2) {
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
case BP_BODY:
|
|
case BP_WAIST:
|
|
case BP_WINGS:
|
|
case BP_TAIL:
|
|
if (howmuchsmaller >= 3) {
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
case BP_LEGS:
|
|
case BP_FEET:
|
|
case BP_BACKLEGS:
|
|
case BP_FRONTLEGS:
|
|
if (howmuchsmaller >= 4) {
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
}
|
|
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,smelldist,darkvisdist;
|
|
int invisible = B_FALSE;
|
|
|
|
|
|
if (gamemode < GM_GAMESTARTED) {
|
|
return B_TRUE;
|
|
}
|
|
if (!viewee->cell || !viewer->cell) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
f = hasflag(viewer->flags, F_XRAYVIS);
|
|
if (f) {
|
|
xray = f->val[0];
|
|
} else {
|
|
xray = 0;
|
|
}
|
|
dist = getcelldist(viewer->cell, viewee->cell);
|
|
f = lfhasflag(viewer, F_TREMORSENSE);
|
|
if (f && !isairborne(viewee) && !isairborne(viewer)) {
|
|
if (f->val[0] > 0) {
|
|
tremordist = f->val[0];
|
|
} else {
|
|
tremordist = dist;
|
|
}
|
|
} else {
|
|
tremordist = -1;
|
|
}
|
|
|
|
f = lfhasflag(viewer, F_ENHANCESMELL);
|
|
if (f) {
|
|
if (f->val[0] > 0) {
|
|
smelldist = f->val[0];
|
|
} else {
|
|
smelldist = dist;
|
|
}
|
|
} else {
|
|
smelldist = -1;
|
|
}
|
|
|
|
darkvisdist = getnightvisrange(viewer);
|
|
|
|
// viewer asleep?
|
|
f = lfhasflag(viewer, F_ASLEEP);
|
|
if (f) {
|
|
if (f->val[1] != ST_MEDITATING) {
|
|
// can only 'see' yourself
|
|
if (viewee != viewer) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (areallies(player, viewee) && !isplayer(viewee) && canhear(player, viewee->cell, 3)) {
|
|
return B_TRUE;
|
|
}
|
|
*/
|
|
|
|
if (uselos) {
|
|
// no line of sight?
|
|
if (!haslos(viewer, viewee->cell)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// viewee is invisible?
|
|
if (lfhasflag(viewee, F_INVISIBLE)) {
|
|
invisible = B_TRUE;
|
|
}
|
|
|
|
// cloak of shadows?
|
|
/*
|
|
o = getequippedob(viewee->pack, BP_SHOULDERS);
|
|
if (o && hasflagval(o->flags, F_HASBRAND, BR_SHADOWS, NA, NA, NULL)) {
|
|
if (!islit(viewee->cell)) {
|
|
invisible = B_TRUE;
|
|
}
|
|
}
|
|
*/
|
|
f = lfhasflag(viewee, F_SHADOWED);
|
|
if (f && (dist > f->val[0])) {
|
|
if (dist <= darkvisdist) {
|
|
} else if ((dist <= tremordist)) {
|
|
} else if ((dist <= smelldist)) {
|
|
} else {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// viewee hiding?
|
|
if (ishidingfrom(viewee, viewer)) {
|
|
invisible = B_TRUE;
|
|
}
|
|
|
|
if (invisible) {
|
|
if (lfhasflag(viewer, F_SEEINVIS)) {
|
|
} else if ((dist <= tremordist)) {
|
|
} else if ((dist <= smelldist)) {
|
|
} else {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// something obscuring them?
|
|
for (o = viewee->cell->obpile->first ; o ; o = o->next) {
|
|
f = hasflag(o->flags, F_BLOCKSVIEW);
|
|
if (f) {
|
|
if (!lfhasflagval(viewer, F_CANSEETHROUGHMAT, o->material->id, NA, NA, NULL)) {
|
|
if (!xray || (xray < dist)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
// viewee underwater and more than 1 cell away?
|
|
if ((getobdepth(o, viewee) >= DP_HEAD) && (dist > 1)) {
|
|
if (!isairborne(viewee)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int cansleep(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_NOSLEEP)) {
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int canuseweapons(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_HUMANOID)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// where == BP_NONE means "can i wear it anywhere?'
|
|
int canwear(lifeform_t *lf, object_t *o, enum BODYPART where) {
|
|
int i,nretflags,nparts = 0;
|
|
//object_t *oo;
|
|
flag_t *f, *retflag[MAXCANDIDATES];
|
|
enum BODYPART possbp[MAXBODYPARTS];
|
|
|
|
reason = E_OK;
|
|
if (where != BP_NONE) {
|
|
if (!hasbp(lf, where)) {
|
|
reason = E_NOBP;
|
|
return B_FALSE;
|
|
}
|
|
if (!bpcantakearmour(lf, where)) {
|
|
reason = E_DOESNTFIT;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (!armourfits(lf, o, &reason)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
// already equipped?
|
|
if (hasflag(o->flags, F_EQUIPPED)) {
|
|
reason = E_ALREADYUSING;
|
|
return B_FALSE;
|
|
}
|
|
// wearable at all?
|
|
if (!iswearable(o) || !lfhasflag(lf, F_HUMANOID)) {
|
|
reason = E_IMPOSSIBLE;
|
|
return B_FALSE;
|
|
}
|
|
// paladin?
|
|
if (getsubjob(lf) == SJ_PALADIN) {
|
|
if (!isblessed(o) || !o->blessknown) {
|
|
if (isarmour(o)) {
|
|
reason = E_PALADIN;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
// injuries?
|
|
if ((where == BP_HANDS) && lfhasflagval(lf, F_INJURY, IJ_HANDSWOLLEN, NA, NA, NULL)) {
|
|
reason = E_INJURED;
|
|
return B_FALSE;
|
|
}
|
|
if ((where == BP_FEET) && lfhasflagval(lf, F_INJURY, IJ_ANKLESWOLLEN, NA, NA, NULL)) {
|
|
reason = E_INJURED;
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (gettechlevel(o->type->id) > getskill(lf, SK_TECHUSAGE)) {
|
|
reason = E_NOTKNOWN;
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (!meetsallattreqs(lf, o)) {
|
|
// meetsattreq will set 'reason' for us.
|
|
return B_FALSE;
|
|
}
|
|
|
|
// can we wear it ANYWHERE?
|
|
getflags(o->flags, retflag, &nretflags, F_GOESON, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_GOESON) {
|
|
possbp[nparts] = f->val[0];
|
|
nparts++;
|
|
}
|
|
}
|
|
|
|
if (nparts == 0) {
|
|
// can't wear anywhere!
|
|
reason = E_IMPOSSIBLE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (where != BP_NONE) {
|
|
int found = B_FALSE;
|
|
// is one of the possible locations the one we are checking?
|
|
for (i = 0; i < nparts; i++) {
|
|
if (possbp[i] == where) {
|
|
found = B_TRUE;
|
|
}
|
|
}
|
|
if (!found) {
|
|
// can't wear there!
|
|
reason = E_IMPOSSIBLE;
|
|
return B_FALSE;
|
|
}
|
|
// is the GIVEN part free?
|
|
if (!isfreebp(lf, where)) {
|
|
reason = E_WEARINGSOMETHINGELSE;
|
|
return B_FALSE;
|
|
}
|
|
} else {
|
|
if (hasflag(o->flags, F_GOESONMULTI)) {
|
|
// are ALL of these body parts free?
|
|
for (i = 0; i < nparts; i++) {
|
|
if (!isfreebp(lf, possbp[i])) {
|
|
reason = E_WEARINGSOMETHINGELSE;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
} else {
|
|
int ok = B_FALSE;
|
|
// are ANY of these body parts free?
|
|
for (i = 0; i < nparts; i++) {
|
|
if (isfreebp(lf, possbp[i])) {
|
|
ok = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
// no parts free
|
|
if (!ok) {
|
|
reason = E_WEARINGSOMETHINGELSE;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
// anything else worn there?
|
|
for (oo = lf->pack->first ; oo ; oo = oo->next) {
|
|
f = hasflagval(oo->flags, F_EQUIPPED, where, -1, -1, NULL);
|
|
if (f) {
|
|
reason = E_WEARINGSOMETHINGELSE;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
*/
|
|
return B_TRUE;
|
|
}
|
|
|
|
int canweild(lifeform_t *lf, object_t *o) {
|
|
flag_t *f;
|
|
enum BODYPART weildloc,otherloc;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
weildloc = getweildloc(o, lf, &otherloc, NULL);
|
|
|
|
// already weilding it?
|
|
if (o && isequipped(o)) {
|
|
reason = E_ALREADYUSING;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// paladin?
|
|
if (getsubjob(lf) == SJ_PALADIN) {
|
|
if (!isblessed(o) || !o->blessknown) {
|
|
reason = E_PALADIN;
|
|
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 && (gettechlevel(o->type->id) > getskill(lf, SK_TECHUSAGE))) {
|
|
reason = E_NOTKNOWN;
|
|
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 (!canuseweapons(lf)) {
|
|
reason = E_IMPOSSIBLE;
|
|
return B_FALSE;
|
|
}
|
|
if (!hasbp(lf, weildloc)) {
|
|
if (o && isfirearm(o) && !getequippedob(lf->pack, otherloc)) {
|
|
int temp;
|
|
// firearm can go in other hand.
|
|
// swap locations.
|
|
temp = weildloc;
|
|
weildloc = otherloc;
|
|
otherloc = temp;
|
|
} else {
|
|
reason = E_NOHANDS;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (o) {
|
|
int i;
|
|
getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_ATTREQ) {
|
|
// meetsattreq will set 'reason' for us.
|
|
if (!meetsattreq(lf, f, o, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
}
|
|
if (istwohandedfor(o, lf)) {
|
|
// need both hands free...
|
|
if (!hasbp(lf, otherloc)) {
|
|
reason = E_NOHANDS;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
reason = E_OK;
|
|
|
|
// trying to fight unarmed, but no unarmed attack?
|
|
if (o == NULL) {
|
|
if (!hasflag(lf->flags, F_HASATTACK)) {
|
|
reason = E_NOUNARMEDATTACK;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int cantakeoff(lifeform_t *lf, object_t *o) {
|
|
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;
|
|
}
|
|
// injuries?
|
|
if (isequippedon(o, BP_HANDS) && lfhasflagval(lf, F_INJURY, IJ_HANDSWOLLEN, NA, NA, NULL)) {
|
|
reason = E_INJURED;
|
|
return B_FALSE;
|
|
}
|
|
if (isequippedon(o, BP_FEET) && lfhasflagval(lf, F_INJURY, IJ_ANKLESWOLLEN, NA, NA, NULL)) {
|
|
reason = E_INJURED;
|
|
return B_FALSE;
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int cantalk(lifeform_t *lf) {
|
|
// phantasms dont talk
|
|
if (lfhasflag(lf, F_PHANTASM)) {
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_NOTALK)) {
|
|
return B_FALSE;
|
|
}
|
|
// if the lf can't explicitly talk, check its race
|
|
if (!lfhasflag(lf, F_CANTALK)) {
|
|
if (!racecantalk(lf->race->id)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// too dumb?
|
|
if (getattr(lf, A_IQ) <= AT_VLOW) {
|
|
return B_FALSE;
|
|
}
|
|
// beheaded?
|
|
if (lfhasflag(lf, F_BEHEADED)) {
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *targob, cell_t *targcell, object_t *fromob, int *seen) {
|
|
int rv;
|
|
int needtovalidate = B_FALSE;
|
|
int targettype;
|
|
int cost;
|
|
flag_t *f,*willflag = NULL,*castflag = NULL;
|
|
int power;
|
|
int spellblessed = B_UNCURSED;
|
|
objecttype_t *sp;
|
|
enum CASTTYPE casttype = CT_NORMAL;
|
|
|
|
|
|
if (fromob) {
|
|
spellblessed = fromob->blessed;
|
|
power = getobspellpower(fromob, lf);
|
|
} else {
|
|
spellblessed = B_UNCURSED;
|
|
power = getspellpower(lf, sid);
|
|
// check whether we _can_ cast it.
|
|
// do we have this spell/ability?
|
|
// enough mp? etc
|
|
if (!cancast(lf, sid, &cost)) {
|
|
if (isplayer(lf)) {
|
|
// announce
|
|
switch (reason) {
|
|
case E_TOOPOWERFUL:
|
|
msg("That spell is too powerful for you to cast.");
|
|
break;
|
|
case E_NOMP:
|
|
msg("You don't have enough mana to cast that.");
|
|
break;
|
|
case E_INJURED:
|
|
msg("Your injury prevents you from doing that.");
|
|
break;
|
|
case E_LOWIQ:
|
|
msg("You are not smart enough to cast spells.");
|
|
break;
|
|
case E_NOTREADY:
|
|
msg("This ability is not recharged yet.");
|
|
break;
|
|
case E_NOSPELLS:
|
|
msg("You can't cast spells.");
|
|
break;
|
|
case E_PALADIN:
|
|
msg("Your cursed weapon is nullifying your holy magic.");
|
|
break;
|
|
case E_PALADIN2:
|
|
msg("Your cursed armour is nullifying your holy magic.");
|
|
break;
|
|
case E_PRONE:
|
|
msg("You can't cast spells while prone.");
|
|
break;
|
|
case E_CLIMBING:
|
|
msg("You can't cast spells while climbing.");
|
|
break;
|
|
case E_SWIMMING:
|
|
msg("You can't cast spells while swimming.");
|
|
break;
|
|
case E_STUNNED:
|
|
msg("You can't cast spells while stunned.");
|
|
break;
|
|
default:
|
|
msg("For some reason, you can't cast that.");
|
|
break;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
willflag = lfhasflagval(lf, F_CANWILL, sid, NA, NA, NULL);
|
|
castflag = lfhasflagval(lf, F_CANCAST, sid, NA, NA, NULL);
|
|
|
|
// special case
|
|
if (!willflag) {
|
|
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;
|
|
}
|
|
}
|
|
casttype = getcasttype(lf, sid);
|
|
}
|
|
|
|
sp = findot(sid);
|
|
|
|
if (!fromob) {
|
|
if (isplayer(lf) && (power > 1) && hasflag(sp->flags, F_VARPOWER)) {
|
|
if (!hasactivespell(lf, sp->id)) {
|
|
int max;
|
|
char buf[BUFLEN],desc[BUFLEN];
|
|
int i;
|
|
char ch;
|
|
// ask what power
|
|
max = power;
|
|
snprintf(buf, BUFLEN, "Cast %s at what power level?", sp->name);
|
|
initprompt(&prompt, buf);
|
|
for (i = 1; i <= max; i++) {
|
|
int thiscost;
|
|
thiscost = getmpcost(lf, sid) * i;
|
|
if (lf->mp >= thiscost) {
|
|
snprintf(buf, BUFLEN, "Power %s (%d MP)", roman(i), thiscost);
|
|
getspelldesc(sp->id, i, desc);
|
|
if (strlen(desc)) {
|
|
strcat(buf, "\t");
|
|
strcat(buf, desc);
|
|
}
|
|
addchoice(&prompt, '0' + i, buf, buf, NULL, NULL);
|
|
}
|
|
}
|
|
addchoice(&prompt, '0', "Cancel", "Cancel", NULL, NULL);
|
|
ch = getchoice(&prompt);
|
|
power = ch - '0';
|
|
|
|
if (power == 0) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
// modify cost
|
|
cost *= i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ask for target cell
|
|
if ((f = hasflag(sp->flags, F_TARGETTEDSPELL)) != NULL) {
|
|
if ((f->val[1] == NA) || (f->val[2] == NA)) {
|
|
targettype = f->val[0];
|
|
needtovalidate = B_TRUE;
|
|
} else if ((power >= f->val[1]) && (power <= f->val[2])) {
|
|
targettype = f->val[0];
|
|
needtovalidate = B_TRUE;
|
|
} else {
|
|
targettype = TT_NONE;
|
|
needtovalidate = B_FALSE;
|
|
}
|
|
} else if (targcell) {
|
|
targettype = TT_NONE;
|
|
needtovalidate = B_FALSE;
|
|
}
|
|
|
|
if (needtovalidate) {
|
|
if (!validatespellcell(lf, &targcell,targettype, sid, power, fromob ? B_TRUE : B_FALSE)) {
|
|
if (isplayer(lf)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (fromob && !targcell) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
if ((sid == OT_S_GATHERFLAME) && (lf->material->id == MT_FIRE)) {
|
|
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) {
|
|
char ch;
|
|
ch = askchar("Casting this while made of flame will harm you, continue?","yn", "n", B_TRUE, B_FALSE);
|
|
if (ch != 'y') {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// stop hiding
|
|
killflagsofid(lf->flags, F_HIDING);
|
|
// take time
|
|
taketime(lf, getspellspeed(lf));
|
|
|
|
if (!fromob) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,tempboost = 0,i;
|
|
// lose mp
|
|
losemp(lf, cost);
|
|
|
|
// spell fails?
|
|
// miscast chance?
|
|
if (isplayer(lf) && !hasjob(lf, J_GOD)) {
|
|
if (pctchance(getmiscastchance(lf))) {
|
|
msg("^WYour cumbersome armour makes you miscast your spell!");
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
// casting a god-related spell while that god is angry at you?
|
|
if (isplayer(lf) && !willflag) {
|
|
if (godprayedto(R_GODNATURE) && godisangry(R_GODNATURE)) {
|
|
if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL)) {
|
|
msg("You seem to be cut off from the powers of nature magic.");
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
if (godprayedto(R_GODLIFE) && godisangry(R_GODLIFE)) {
|
|
if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL)) {
|
|
msg("You seem to be cut off from the powers of life magic.");
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// adjust power?
|
|
if (hasjob(lf, J_DRUID)) {
|
|
power += countplantsinsight(lf);
|
|
limit(&power, NA, 10);
|
|
}
|
|
|
|
tempboost = 0;
|
|
getflags(lf->flags, retflag, &nretflags, F_TEMPMAGICBOOST, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
tempboost += retflag[i]->val[0];
|
|
killflag(retflag[i]);
|
|
}
|
|
if (tempboost) {
|
|
power += tempboost;
|
|
if (isplayer(lf)) {
|
|
msg("^gYour spell's power is boosted!^n");
|
|
}
|
|
}
|
|
|
|
if (!willflag && real_getmr(lf, B_ONLYEXTERNAL) && skillcheck(lf, SC_RESISTMAG, 20 + power, 0) && !isgod(lf)) {
|
|
if (power > 1) {
|
|
// half strength
|
|
power /= 2;
|
|
if (power <= 1) power = 1;
|
|
|
|
if (isplayer(lf)) {
|
|
msg("^wYour magic resistance lowers your spell's power.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// noise....
|
|
makenoise(lf, N_SPELLCAST);
|
|
|
|
// announce
|
|
if (!isplayer(lf) && !fromob) {
|
|
int doannounce = B_FALSE;
|
|
|
|
if (cansee(player, lf)) {
|
|
doannounce = B_TRUE;
|
|
} else if ((casttype == CT_SOUNDBASED) && canhear(player, lf->cell, SV_TALK)) {
|
|
doannounce = B_TRUE;
|
|
}
|
|
if (doannounce) {
|
|
char lfname[BUFLEN];
|
|
char whattosay[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
|
|
// special case
|
|
f = getspellcasttextflag(lf, sid);
|
|
if (f) {
|
|
if (strlen(f->text)) {
|
|
snprintf(whattosay, BUFLEN, "%s %s", lfname, f->text);
|
|
if (targlf && (f->val[2] == B_APPENDYOU)) {
|
|
char targname[BUFLEN];
|
|
if (targlf == lf) {
|
|
strcpy(targname, "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)) {
|
|
if (!lfhasflag(player, F_ASLEEP) && !lfhasflag(lf, F_SPELLCASTTEXT)) {
|
|
msg("Something casts a spell at you.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// eye protection will stop some spells!
|
|
if (targcell && targcell->lf) {
|
|
lifeform_t *victim;
|
|
victim = targcell->lf;
|
|
int protected = B_FALSE;
|
|
object_t *protob = NULL;
|
|
switch (casttype) {
|
|
case CT_EYESPIT:
|
|
protob = getarmour(victim, BP_EYES);
|
|
if (protob) protected = B_TRUE;
|
|
break;
|
|
case CT_GAZE:
|
|
protob = eyesshaded(victim);
|
|
if (protob) protected = B_TRUE;
|
|
break;
|
|
case CT_SOUNDBASED:
|
|
if (isdeaf(victim)) protected = B_TRUE;
|
|
break;
|
|
default:
|
|
protob = NULL;
|
|
protected = B_FALSE;
|
|
break;
|
|
}
|
|
if (protected) {
|
|
if (protob) {
|
|
if (isplayer(victim)) {
|
|
char gbuf[BUFLEN];
|
|
getobname(protob, gbuf, protob->amt);
|
|
msg("Your %s protects you.", noprefix(gbuf));
|
|
} else if (cansee(player, victim)) {
|
|
char lfname[BUFLEN],gbuf[BUFLEN];
|
|
getobname(protob, gbuf, protob->amt);
|
|
getlfname(victim, lfname);
|
|
msg("%s%s %s protects %s.", lfname, getpossessive(lfname), noprefix(gbuf), it(victim) );
|
|
}
|
|
} else {
|
|
if (isplayer(victim)) {
|
|
msg("You are unaffected.");
|
|
} else if (cansee(player, victim)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(victim, lfname);
|
|
msg("%s is unaffected.", lfname);
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (!fromob) {
|
|
flag_t *ff;
|
|
// is this a spell which we can only cast every x turns?
|
|
// if so, rest counter.
|
|
// do this _before_ casting the spell,
|
|
// in case the spell causes us to lose
|
|
// the f_canwill/f_cancast flag (eg. polymorph)
|
|
if (willflag) ff = willflag;
|
|
else ff = castflag;
|
|
|
|
if (ff) {
|
|
if (ff->val[2] != NA) {
|
|
ff->val[1] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// cast the spell. does it take time?
|
|
f = hasflag(sp->flags, F_CASTINGTIME) ;
|
|
if (!f) {
|
|
f = lfhasflagval(lf, F_SPELLCASTTIME, NA, sp->id, NA, NULL);
|
|
}
|
|
if (f && !fromob) {
|
|
int castingtime;
|
|
char tempbuf[BUFLEN];
|
|
char castingbuf[BUFLEN];
|
|
flag_t *castingflag;
|
|
castingtime = f->val[0];
|
|
|
|
strcpy(castingbuf, "");
|
|
|
|
if (targlf) {
|
|
snprintf(tempbuf, BUFLEN, "%d;",targlf->id);
|
|
} else {
|
|
strcpy(tempbuf, "-1;");
|
|
}
|
|
strcat(castingbuf, tempbuf);
|
|
|
|
if (targob) {
|
|
snprintf(tempbuf, BUFLEN, "%ld;",targob->id);
|
|
} else {
|
|
strcpy(tempbuf, "-1;");
|
|
}
|
|
strcat(castingbuf, tempbuf);
|
|
|
|
if (targcell) {
|
|
snprintf(tempbuf, BUFLEN, "%d;%d;%d;",targcell->map->id,targcell->x, targcell->y);
|
|
} else {
|
|
strcpy(tempbuf, "-1;-1;-1;");
|
|
}
|
|
strcat(castingbuf, tempbuf);
|
|
|
|
castingflag = addflag(lf->flags, F_CASTINGSPELL, sid, power, castingtime, castingbuf);
|
|
rv = B_FALSE;
|
|
|
|
if (isplayer(lf)) {
|
|
// announce
|
|
msg("You start casting %s.", sp->name);
|
|
}
|
|
} else { // instant cast
|
|
int obfromid = -1;
|
|
addflag(lf->flags, F_CASTINGSPELL, sid, NA, NA, NULL);
|
|
|
|
|
|
// override obfrom if required
|
|
if (willflag && (willflag->obfrom != -1)) {
|
|
obfromid = willflag->obfrom;
|
|
} else if (castflag && (castflag->obfrom != -1)) {
|
|
obfromid = castflag->obfrom;
|
|
}
|
|
if (obfromid != -1) {
|
|
fromob = findobbyid(lf->pack, obfromid);
|
|
}
|
|
|
|
rv = dospelleffects(lf, sid, power, targlf, targob, targcell, spellblessed, seen, B_FALSE, fromob);
|
|
f = lfhasflag(lf, F_CASTINGSPELL);
|
|
if (f) {
|
|
killflag(f);
|
|
}
|
|
}
|
|
|
|
// successful cast?
|
|
if (!rv) {
|
|
enum SPELLSCHOOL school;
|
|
school = getspellschoolknown(lf, sid);
|
|
if (!fromob) {
|
|
if (school != SS_NONE) {
|
|
enum SKILL skid;
|
|
skid = getschoolskill(school);
|
|
if (skid != SK_NONE) {
|
|
practice(lf, skid, 1);
|
|
}
|
|
}
|
|
}
|
|
if (isplayer(lf)) {
|
|
objecttype_t *ot;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
// god of magic likes all spells
|
|
pleasegodmaybe(R_GODMAGIC, getspelllevel(sid));
|
|
// god of battle hates all spells except nullify
|
|
if (!fromob && (sid != OT_S_NULLIFY)) {
|
|
angergodmaybe(R_GODBATTLE, 25, GA_SPELL);
|
|
}
|
|
ot = findot(sid);
|
|
getflags(ot->flags, retflag, &nretflags, F_PLEASESGOD, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_PLEASESGOD) {
|
|
pleasegodmaybe(f->val[0], f->val[1]);
|
|
}
|
|
}
|
|
|
|
// anger gods?
|
|
switch (school) {
|
|
case SS_COLD:
|
|
angergodmaybe(R_GODFIRE, getspelllevel(sid)*5, GA_SPELL);
|
|
break;
|
|
case SS_DEATH:
|
|
angergodmaybe(R_GODLIFE, getspelllevel(sid)*5, GA_SPELL);
|
|
break;
|
|
case SS_LIFE:
|
|
angergodmaybe(R_GODDEATH, getspelllevel(sid)*5, GA_SPELL);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
int celllitfor(lifeform_t *lf, cell_t *c, int maxvisrange, int nightvisrange) {
|
|
int dist;
|
|
//int ambientvis;
|
|
dist = getcelldist(lf->cell, c);
|
|
// too far away
|
|
if (maxvisrange != UNLIMITED) {
|
|
if (dist > maxvisrange) {
|
|
// if it's lit and within our non-ambient-adjusted visrange, it's still ok
|
|
if (islit(c) && (dist <= maxvisrange)) return B_TRUE;
|
|
else return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// outside ambient light range and beyond nightvis range
|
|
/*
|
|
ambientvis = getmapmaxvisrange(c->map);
|
|
if ((ambientvis == 0) || (dist > ambientvis)) {
|
|
if (dist >= nightvisrange) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
*/
|
|
|
|
// outside the range of our light, and not lit
|
|
if ((nightvisrange != UNLIMITED) && !islit(c)) {
|
|
if (dist >= nightvisrange) {
|
|
return B_FALSE;
|
|
} else {
|
|
// inside our nightvis range and magically dark
|
|
if (c->lit == L_PERMDARK) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int celltransparentfor(lifeform_t *lf, cell_t *c, int *xray, int *rangemod) {
|
|
object_t *o;
|
|
flag_t *f;
|
|
|
|
if (rangemod) *rangemod = 0;
|
|
|
|
// solid cells stop los (unless it's your own cell)
|
|
if (!c->type->transparent && (c != lf->cell)) {
|
|
if (xray && *xray) {
|
|
(*xray)--;
|
|
} else return B_FALSE;
|
|
}
|
|
|
|
// check for lfs which block view
|
|
if (c->lf && (c->lf != lf) && cansee_real(lf, c->lf, B_FALSE)) {
|
|
if (!lfhasflag(lf, F_CANSEETHROUGHLF)) {
|
|
int sizediff;
|
|
// high sizediff means that the lf in the cell is bigger than the viewer
|
|
sizediff = getlfsize(c->lf) - getlfsize(lf);
|
|
// lf greater than 2 sizes bigger than us?
|
|
if (sizediff >= 2) {
|
|
if (xray && *xray) {
|
|
(*xray) -= (sizediff-1);
|
|
if (*xray < 0) *xray = 0;
|
|
} else return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for objects which block view
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
f = hasflag(o->flags, F_BLOCKSVIEW);
|
|
if (f) {
|
|
if (!lfhasflagval(lf, F_CANSEETHROUGHMAT, o->material->id, NA, NA, NULL)) {
|
|
if (xray && *xray) {
|
|
(*xray)--;
|
|
} else {
|
|
if (f->val[0] == B_TRUE) {
|
|
return B_FALSE;
|
|
} else {
|
|
if (rangemod) *rangemod += f->val[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int charmedaction(lifeform_t *lf, flag_t *charmflag) {
|
|
lifeform_t *charmer;
|
|
char charmername[BUFLEN];
|
|
charmer = findlf(lf->cell->map, charmflag->val[0]);
|
|
if (!charmer) {
|
|
killflag(charmflag);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(charmer, charmername);
|
|
|
|
if (isadjacent(lf->cell, charmer->cell)) {
|
|
object_t *o;
|
|
enum OBTYPE oid[MAXPILEOBS];
|
|
int oidcovet[MAXPILEOBS];
|
|
int noids = 0;
|
|
enum FLAG wantflag[MAXPILEOBS];
|
|
int wantflagcovet[MAXPILEOBS];
|
|
int nwantflags = 0;
|
|
int ndone = 0;
|
|
// hands over wantob/weapon/armour
|
|
makewantedoblist(charmer, &noids, oid, oidcovet, &nwantflags, wantflag, wantflagcovet);
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (iscursed(o)) continue;
|
|
if (isweapon(o) || aiwants_real(charmer, o, NULL, &noids, oid, oidcovet, &nwantflags, wantflag, wantflagcovet)) {
|
|
if (isequipped(o)) {
|
|
takeoff(lf, o);
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("^wYou hand over your %s to %s.", noprefix(obname),
|
|
cansee(lf, charmer) ? charmername : "your new master");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
char obname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getobname(o, obname, o->amt);
|
|
msg("^w%s hands over %s to %s.", lfname, obname,
|
|
cansee(player, charmer) ? charmername : "someone");
|
|
}
|
|
moveob(o, charmer->pack, o->amt);
|
|
}
|
|
ndone++;
|
|
break;
|
|
}
|
|
}
|
|
// nothing they want? just remove armour/weapons.
|
|
if (!ndone) {
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (isequipped(o)) {
|
|
takeoff(lf, o);
|
|
ndone++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
if (isplayer(lf)) {
|
|
msg("^wYou bask in the glory of %s!", cansee(lf, charmer) ? charmername : "your new master");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^w%s stares in awe at %s.", lfname, cansee(lf, charmer) ? charmername : "someone");
|
|
}
|
|
taketime(lf, getactspeed(lf));
|
|
}
|
|
} else {
|
|
int dir;
|
|
// walks towards charmer, regardless of anything in the way
|
|
turntoface(lf, charmer->cell);
|
|
dir = getdirtowards(lf->cell, charmer->cell, lf, B_FALSE, DT_ORTH);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(lf)) {
|
|
msg("^wYou try to %s towards %s, but fail.", getmoveverb(lf),
|
|
cansee(lf, charmer) ? charmername : "your new master");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^w%s tries to %s towards %s, but fail.", lfname, getmoveverb(lf),
|
|
cansee(lf, charmer) ? charmername : "something");
|
|
}
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("^wYou mindlessly %s towards %s.", getmoveverb(lf),
|
|
cansee(lf, charmer) ? charmername : "your new master");
|
|
}
|
|
trymove(lf, dir, B_FALSE, B_TRUE);
|
|
}
|
|
taketime(lf, getactspeed(lf));
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int checkburdened(lifeform_t *lf, int preburdened) {
|
|
int postburdened;
|
|
|
|
postburdened = isburdened(lf);
|
|
if (postburdened != preburdened) {
|
|
if (postburdened) {
|
|
if (preburdened == BR_NONE) {
|
|
msg("^wThe weight of your possessions is burdening you!");
|
|
statdirty = B_TRUE;
|
|
} else if (postburdened > preburdened) {
|
|
msg("^wThe weight of your possessions is burdening you even more!");
|
|
} else { // ie. postburdened < preburdened
|
|
msg("^wThe weight of your possessions is burdening you a little less now.");
|
|
}
|
|
} else { // not burdened
|
|
msg("^wYour possessions are no longer weighing you down.");
|
|
statdirty = B_TRUE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// returns TRUE if something happened.
|
|
int checkfordrowning(lifeform_t *lf, object_t *o) {
|
|
int depth,i;
|
|
int didsomething = B_FALSE;
|
|
flag_t *f;
|
|
enum SKILLLEVEL slev;
|
|
|
|
if (isairborne(lf)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
depth = getobdepth(o, lf);
|
|
|
|
// activate amulet of swimming
|
|
if (depth >= DP_HEAD) {
|
|
object_t *o;
|
|
o = hasequippedobid(lf->pack, OT_AMU_SWIMMING);
|
|
if (o && !ispolymorphed(lf)) {
|
|
// transform into a swan!
|
|
if (!polymorphto(lf, R_SWAN, PERMENANT)) makeknown(o->type->id);
|
|
}
|
|
|
|
o = hasequippedobid(lf->pack, OT_AMU_EVOLUTION);
|
|
if (o) {
|
|
if (!polymorphto(lf, R_FISHFOLK, 5)) makeknown(o->type->id);
|
|
}
|
|
}
|
|
|
|
// recalculate depth now
|
|
depth = getobdepth(o, lf);
|
|
|
|
if (lfhasflag(lf, F_AQUATIC)) {
|
|
slev = PR_MASTER;
|
|
} else {
|
|
i = getskill(lf, SK_SWIMMING) - isburdened(lf);
|
|
limit(&i, 0, NA);
|
|
slev = i;
|
|
}
|
|
|
|
|
|
// apply water damage (ie rust etc) to armour.
|
|
for (i = 0; i <= depth; i++) {
|
|
object_t *armour = NULL;
|
|
if (i == DP_FEET) {
|
|
armour = getouterequippedob(lf, BP_FEET); if (armour) takedamage(armour, 4, DT_WATER);
|
|
} 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 (needstobreath(lf)) {
|
|
if ((!slev || !getstamina(lf)) && !lfhasflag(lf, F_BREATHWATER) ) {
|
|
int damamt;
|
|
|
|
// take drowning damage. generally you'll die
|
|
// in around 3-4 turns.
|
|
damamt = lf->maxhp / (getattr(lf, A_CON) / 15);
|
|
|
|
limit(&damamt, 1, NA);
|
|
|
|
if (damamt >= lf->hp) {
|
|
char obname[BUFLEN];
|
|
if (isplayer(lf)) {
|
|
msg("^BYou drown.");
|
|
didsomething = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%c%s drowns.",getlfcol(lf, CC_BAD) , lfname);
|
|
didsomething = B_TRUE;
|
|
}
|
|
addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
|
|
lf->hp = 0;
|
|
getobnametruebase(o, obname, o->amt);
|
|
lf->lastdamtype = DT_DIRECT;
|
|
setlastdam(lf, obname);
|
|
setkillverb(lf, "Drowned");
|
|
} else {
|
|
char obname[BUFLEN];
|
|
if (isplayer(lf)) {
|
|
msg("^BYou are drowning!");
|
|
didsomething = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%c%s is drowning!", getlfcol(lf, CC_VBAD), lfname);
|
|
didsomething = B_TRUE;
|
|
}
|
|
getobnametruebase(o, obname, o->amt);
|
|
losehp(lf, damamt, DT_DIRECT, NULL, obname);
|
|
setlastdam(lf, obname);
|
|
setkillverb(lf, "Drowned");
|
|
}
|
|
}
|
|
} //end if needs to breath
|
|
} // end if depth > head
|
|
|
|
if (!isdead(lf)) {
|
|
f = isvulnto(lf->flags, DT_WATER, B_FALSE);
|
|
if (f) {
|
|
int dam;
|
|
if (strlen(f->text)) {
|
|
dam = roll(f->text);
|
|
} else {
|
|
dam = roll("1d6");
|
|
}
|
|
applywalkdam(lf, dam, DT_WATER, o);
|
|
}
|
|
}
|
|
|
|
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 in view!");
|
|
break;
|
|
default:
|
|
msg("You cannot rest for some reason.");
|
|
break;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
lifeform_t *clonelf(lifeform_t *src, cell_t *where) {
|
|
lifeform_t *lf;
|
|
lf = addmonster(where, src->race->id, NULL, B_FALSE, 1, B_FALSE, NULL);
|
|
if (lf) {
|
|
killflagsofid(lf->flags, F_XPVAL);
|
|
addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
}
|
|
return lf;
|
|
}
|
|
|
|
// how dangerous is lf2 to lf1?
|
|
// < 0 = harder
|
|
// > 0 = easier
|
|
float comparelfs(lifeform_t *lf1, lifeform_t *lf2) {
|
|
float turnstokill1, turnstokill2;
|
|
float ratio;
|
|
turnstokill1 = gethitstokill(lf1, lf2, B_TRUE, B_TRUE); // #turns for lf1 to kill lf2
|
|
turnstokill2 = gethitstokill(lf2, lf1, B_TRUE, B_TRUE); // #turns for lf2 to kill lf1
|
|
|
|
if (turnstokill1 == 0) {
|
|
// lf1 can never kill lf2
|
|
ratio = -1;
|
|
} else if (turnstokill2 == 0) {
|
|
// lf2 can never kill lf1
|
|
ratio = 5;
|
|
} else {
|
|
// compare avgturnstokill values
|
|
// if turns to kill lf2 is lower, ratio will be positive.
|
|
ratio = (turnstokill2 / turnstokill1);
|
|
}
|
|
|
|
return ratio;
|
|
}
|
|
|
|
int confuse(lifeform_t *lf, int howlong) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_CONFUSED);
|
|
if (f) {
|
|
if (f->lifetime > 0) f->lifetime += howlong;
|
|
} else {
|
|
addtempflag(lf->flags, F_CONFUSED, B_TRUE, NA, NA, NULL, howlong);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void copycorpseflags(flagpile_t *dst, flagpile_t *src) {
|
|
// inherit physical properties...
|
|
copyflag(dst, src, F_DTVULN);
|
|
copyflag(dst, src, F_DTRESIST);
|
|
copyflag(dst, src, F_DTIMMUNE);
|
|
// inherit alighnment
|
|
copyflag(dst, src, F_ALIGNMENT);
|
|
// inherit eat conferred flags
|
|
copyflag(dst, src, F_EATCONFER);
|
|
}
|
|
|
|
int continuedigging(lifeform_t *lf) {
|
|
cell_t *c;
|
|
flag_t *f = NULL;
|
|
int digpower;
|
|
int stopnow = B_FALSE;
|
|
|
|
if (!real_hasfreeaction(lf, F_DIGGING)) {
|
|
stopnow = B_TRUE;
|
|
}
|
|
|
|
f = hasflag(lf->flags, F_DIGGING);
|
|
if (!f) {
|
|
stopnow = B_TRUE;
|
|
}
|
|
if (!stopnow) {
|
|
c = getcellat(lf->cell->map, f->val[0], f->val[1]);
|
|
if (!c) {
|
|
stopnow = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (stopnow) {
|
|
if (f) killflag(f);
|
|
return B_TRUE;
|
|
}
|
|
|
|
digpower = f->val[2];
|
|
c->hp -= digpower;
|
|
if (c->hp <= 0) {
|
|
if (isplayer(lf)) {
|
|
msg("You finish digging through %s %s.",needan(c->type->name) ? "an" : "a", c->type->name);
|
|
needredraw = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s finishes digging through %s %s.",lfname,needan(c->type->name) ? "an" : "a", c->type->name);
|
|
needredraw = B_TRUE;
|
|
}
|
|
// replace wall
|
|
setcelltype(c, c->map->habitat->emptycelltype);
|
|
// stop digging
|
|
killflag(f);
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("You dig into %s %s.",needan(c->type->name) ? "an" : "a", c->type->name);
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s digs into %s %s.",lfname,needan(c->type->name) ? "an" : "a", c->type->name);
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int continuerepairing(lifeform_t *lf, flag_t *repairflag) {
|
|
object_t *helpob,*o;
|
|
char helpobname[BUFLEN];
|
|
flag_t *f;
|
|
|
|
o = findobbyid(lf->pack, atol(repairflag->text));
|
|
if (!o) {
|
|
killflag(repairflag);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// in case it's on fire, etc
|
|
if (touch(lf, o)) {
|
|
taketime(lf, getactspeed(lf));
|
|
return B_FALSE;
|
|
}
|
|
|
|
f = hasflag(o->flags, F_IMMUTABLE);
|
|
if (f) {
|
|
if (isplayer(lf)) {
|
|
char obname[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
msg("Your %s somehow resists your attempts to repair it!", noprefix(obname));
|
|
}
|
|
killflag(repairflag);
|
|
f->known = B_TRUE;
|
|
return B_TRUE;
|
|
}
|
|
|
|
// get helper ob
|
|
helpob = getworkhelpob(lf->pack, o->material->id);
|
|
if (helpob) {
|
|
real_getobname(helpob, helpobname, helpob->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
} else {
|
|
strcpy(helpobname, "");
|
|
}
|
|
|
|
// fully repair it.
|
|
f = hasflag(o->flags, F_OBHP);
|
|
f->val[0] = f->val[1];
|
|
if (isplayer(lf)) {
|
|
char obname[BUFLEN],withbuf[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
if (helpob) sprintf(withbuf, " (with %s)", helpobname);
|
|
else strcpy(withbuf, "");
|
|
|
|
msg("You repair your %s%s.", noprefix(obname), withbuf);
|
|
} else if (cansee(player, lf)) {
|
|
char obname[BUFLEN],withbuf[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
if (helpob) sprintf(withbuf, " with %s", helpobname);
|
|
else strcpy(withbuf, "");
|
|
msg("%s repairs %s%s.", lfname, obname, withbuf);
|
|
}
|
|
|
|
practice(lf, SK_METALWORK, 1);
|
|
practice(lf, SK_SEWING, 1);
|
|
|
|
// take some time.
|
|
taketime(lf, getactspeed(lf));
|
|
|
|
// finished repairing this object now.
|
|
killflag(repairflag);
|
|
return B_FALSE;
|
|
}
|
|
|
|
int countinnateattacks(lifeform_t *lf) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE);
|
|
return nretflags;
|
|
}
|
|
|
|
int countlegs(lifeform_t *lf) {
|
|
enum BODYPART bp;
|
|
int legs = 0;
|
|
for (bp = 0; bp <= MAXBODYPARTS; bp++) {
|
|
if (hasbp(lf, bp)) {
|
|
switch (bp) {
|
|
case BP_LEGS:
|
|
case BP_FRONTLEGS:
|
|
case BP_BACKLEGS:
|
|
legs += 2;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
return legs;
|
|
}
|
|
|
|
int countnearbyallies(lifeform_t *lf) {
|
|
lifeform_t *l;
|
|
int count = 0;
|
|
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if ((l != lf) && areallies(l, lf)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countnearbyhurtallies(lifeform_t *lf) {
|
|
lifeform_t *l;
|
|
int count = 0;
|
|
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if ((l != lf) && areallies(l, lf)) {
|
|
if (l->hp != l->maxhp) {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countplantsinsight(lifeform_t *lf) {
|
|
int n, boost = 0;
|
|
for (n = 0; n < lf->nlos; n++) {
|
|
if (lf->los[n]->lf && getraceclass(lf->los[n]->lf) == RC_PLANT) {
|
|
boost++;
|
|
} else if (hasobofclass(lf->los[n]->obpile, OC_FLORA)) {
|
|
boost++;
|
|
}
|
|
}
|
|
return boost;
|
|
}
|
|
|
|
// toggle debugging
|
|
void debug(lifeform_t *lf) {
|
|
char lfname[BUFLEN];
|
|
flag_t *f;
|
|
getlfname(lf, lfname);
|
|
|
|
f = hasflag(lf->flags, F_DEBUG);
|
|
if (f) {
|
|
killflag(f);
|
|
msg("%s - debugging is DISABLED.", lfname);
|
|
} else {
|
|
addflag(lf->flags, F_DEBUG, B_TRUE, NA, NA, NULL);
|
|
msg("%s - debugging is ON.", lfname);
|
|
}
|
|
}
|
|
|
|
// returns true if the player pays.
|
|
int demandbribe(lifeform_t *lf) {
|
|
lifeform_t *l;
|
|
char lfname[BUFLEN];
|
|
int amtwanted,amtgiven,totmoney;
|
|
int hd;
|
|
char buf[BUFLEN], answer[BUFLEN];
|
|
object_t *gold, *mongold;
|
|
int satisfied = B_FALSE;
|
|
int i,heard;
|
|
char saybuf[BUFLEN];
|
|
flag_t *demflag;
|
|
|
|
demflag = lfhasflag(lf, F_DEMANDSBRIBE);
|
|
|
|
hd = gettr(lf);
|
|
gold = hasob(player->pack, OT_GOLD);
|
|
if (gold) {
|
|
totmoney = countmoney(player->pack);
|
|
} else {
|
|
totmoney = 0;
|
|
}
|
|
mongold = hasob(lf->pack, OT_GOLD);
|
|
amtwanted = rnd(hd*25, hd*100);
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
if (demflag && strlen(demflag->text)) {
|
|
strcpy(saybuf, demflag->text);
|
|
} else {
|
|
strcpy(saybuf, "Hand over all your gold!");
|
|
}
|
|
|
|
if (say(lf, saybuf, SV_TALK)) {
|
|
heard = B_TRUE;
|
|
} else {
|
|
heard = B_FALSE;
|
|
}
|
|
|
|
if (heard) {
|
|
int doagain = B_TRUE;
|
|
more();
|
|
while (doagain) {
|
|
snprintf(buf, BUFLEN, "How much gold will you give %s (you have $%d)", lfname, totmoney);
|
|
askstring(buf, '?', answer, BUFLEN, NULL);
|
|
amtgiven = atoi(answer);
|
|
if (amtgiven > totmoney) {
|
|
msg("You don't have that much gold!"); more();
|
|
doagain = B_TRUE;
|
|
} else if (amtgiven < 0) {
|
|
msg("Please enter a valid number."); more();
|
|
doagain = B_TRUE;
|
|
} else {
|
|
doagain = B_FALSE;
|
|
}
|
|
}
|
|
limit(&amtgiven, 0, totmoney);
|
|
} else {
|
|
amtgiven = 0;
|
|
}
|
|
|
|
if (gold && (amtgiven > 0)) {
|
|
gold->amt -= amtgiven;
|
|
|
|
if (mongold) {
|
|
mongold->amt += amtgiven;
|
|
} else {
|
|
char gbuf[BUFLEN];
|
|
snprintf(gbuf, BUFLEN, "%d gold", amtgiven);
|
|
mongold = addob(lf->pack, gbuf);
|
|
}
|
|
|
|
if ((amtgiven == totmoney) || (amtgiven >= amtwanted)) {
|
|
// always succeed
|
|
say(lf, "Pleasure doing business with you!", SV_TALK);
|
|
satisfied = B_TRUE;
|
|
} else {
|
|
say(lf, "Then die!", SV_SHOUT);
|
|
satisfied = B_FALSE;
|
|
}
|
|
} else {
|
|
// TODO: luck check to receive money ?
|
|
say(lf, "Then die!", SV_SHOUT);
|
|
satisfied = B_FALSE;
|
|
}
|
|
|
|
|
|
// if you gave the gold, mosnter becomes peaceful
|
|
if (satisfied) {
|
|
int nminions;
|
|
lifeform_t *minion[MAXCANDIDATES];
|
|
makepeaceful(lf, player);
|
|
// also make any of its minions peaceful
|
|
getminions(lf, minion, &nminions);
|
|
for (i = 0; i < nminions; i++) {
|
|
makepeaceful(minion[i], player);
|
|
}
|
|
}
|
|
|
|
// either way, kill bribe flag for all bandits on the level
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if (!isplayer(l) && (l->race->baseid == lf->race->baseid)) {
|
|
killflagsofid(l->flags, F_DEMANDSBRIBE);
|
|
}
|
|
}
|
|
|
|
return satisfied;
|
|
}
|
|
|
|
void die(lifeform_t *lf) {
|
|
char buf[BUFLEN];
|
|
flag_t *f;
|
|
int killedgod = B_FALSE;
|
|
//int dropobs = B_TRUE;
|
|
int vaporised = B_FALSE, i;
|
|
int dropobs = B_TRUE;
|
|
int willbecomeghost = B_FALSE;
|
|
object_t *corpse = NULL;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
cell_t *corpsecell;
|
|
lifeform_t *killer = NULL;
|
|
int dobonesfile = B_FALSE;
|
|
char reanimateas[BUFLEN];
|
|
cell_t *where;
|
|
int thisisplayer = B_FALSE;
|
|
// for earthwyrms etc dividing
|
|
enum RACE dividerace = R_NONE;
|
|
cell_t *dividecell[2] = {NULL, NULL};
|
|
char dividename[BUFLEN];
|
|
int dividetr, dividehp;
|
|
int dividelos = B_FALSE;
|
|
|
|
|
|
strcpy(reanimateas, "");
|
|
|
|
where = lf->cell;
|
|
|
|
thisisplayer = isplayer(lf);
|
|
|
|
|
|
if (lf->lastdamlf != -1) {
|
|
killer = findlf(lf->cell->map, lf->lastdamlf);
|
|
}
|
|
|
|
if (cansee(player, lf)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
|
|
|
|
// prevent rising from dead?
|
|
switch (lf->race->id) {
|
|
case R_REVENANT:
|
|
if ((lf->lastdamtype == DT_HOLY) || lfhasflag(lf, F_MUTILATED)) {
|
|
killflagsofid(lf->flags, F_REVIVETIMER);
|
|
}
|
|
break;
|
|
case R_TROLL:
|
|
if (basedamagetype(lf->lastdamtype) == DT_FIRE) {
|
|
killflagsofid(lf->flags, F_REVIVETIMER);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (lf->lastdamtype == DT_HOLY) {
|
|
killflagsofid(lf->flags, F_RISEASGHOST);
|
|
}
|
|
|
|
if (lfhasflag(lf, F_RISEASGHOST)) {
|
|
willbecomeghost = B_TRUE;
|
|
}
|
|
|
|
// died after entering a new level without a chance to move?
|
|
if (thisisplayer && lfhasflag(lf, F_JUSTENTERED) && godprayedto(R_GODMERCY)) {
|
|
godsay(R_GODMERCY, B_TRUE, "Well, that seems unfair..."); more();
|
|
// return to full health
|
|
lf->hp = lf->maxhp;
|
|
statdirty = B_TRUE;
|
|
// teleport away!
|
|
dospelleffects(NULL, OT_S_DISPERSAL, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE, NULL);
|
|
return;
|
|
}
|
|
|
|
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 (thisisplayer) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// a god died?
|
|
if ((getraceclass(lf) == RC_GOD) && !isplayer(lf)) {
|
|
flag_t *f;
|
|
// update godlf pointer
|
|
for (i = 0; i < ngodlfs; i++) {
|
|
if (godlf[i] == lf) {
|
|
godlf[i] = NULL;
|
|
break;
|
|
}
|
|
}
|
|
f = hasflag(lf->flags, F_GODOF);
|
|
addflag(player->flags, F_WINNER, WT_GOD, lf->race->id, NA, f->text);
|
|
killedgod = B_TRUE;
|
|
}
|
|
|
|
if (!willbecomeghost) {
|
|
if (thisisplayer && hasjob(lf, J_GOD)) {
|
|
if (!lfhasflag(lf, F_WINNER)) {
|
|
char ch;
|
|
msg("^BYou are about to die..."); more();
|
|
ch = askchar("Die", "yn", "n", B_TRUE, B_FALSE);
|
|
if (ch == 'n') {
|
|
lf->hp = lf->maxhp;
|
|
msg("Not dying.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// special cases based on race id
|
|
if (lf->race->id == R_BABAYAGAHUT) {
|
|
if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s slumps to the ground, exhausted.",lfname);
|
|
}
|
|
}
|
|
if (lf->race->id == R_BABAYAGA) {
|
|
object_t *exitob;
|
|
cell_t *exitcell;
|
|
// unlock the vault exit!
|
|
exitcell = findobinmap(lf->cell->map, OT_BYHUTDOOR);
|
|
exitob = hasob(exitcell->obpile, OT_BYHUTDOOR);
|
|
assert(exitob);
|
|
killflagsofid(exitob->flags, F_LOCKED);
|
|
if (haslos(player, exitcell)) {
|
|
char obname[BUFLEN];
|
|
getobname(exitob, obname, 1);
|
|
msg("%s unlocks with a loud 'click'.", obname);
|
|
}
|
|
noise(exitcell, NULL, NC_OTHER, SV_TALK, "a loud 'click'.", NULL);
|
|
}
|
|
|
|
|
|
if (lf->race->id == R_EARTHWYRM) {
|
|
// remember stats
|
|
dividehp = lf->maxhp / 2;
|
|
dividetr = gettr(lf) / 2;
|
|
|
|
if ((dividehp >= 1) && (dividetr >= 1)) {
|
|
dividerace = lf->race->id;
|
|
getlfname(lf, dividename);
|
|
if (cansee(player, lf)) dividelos = B_TRUE;
|
|
|
|
// are there 2 random cells free?
|
|
dividecell[0] = real_getrandomadjcell(lf->cell, WE_WALKABLE, B_NOEXPAND, LOF_WALLSTOP, NULL, NULL, NULL);
|
|
if (dividecell[0]) {
|
|
dividecell[1] = real_getrandomadjcell(lf->cell, WE_WALKABLE, B_NOEXPAND, LOF_WALLSTOP, NULL, dividecell[0], NULL);
|
|
}
|
|
|
|
if (!dividecell[1]) {
|
|
dividecell[1] = lf->cell;
|
|
}
|
|
if (dividecell[0] && dividecell[1]) {
|
|
// don't leave a corpse
|
|
killflagsofid(lf->flags, F_CORPSETYPE);
|
|
killflagsofid(lf->flags, F_EXTRACORPSE);
|
|
killflagsofid(lf->flags, F_NOCORPSE);
|
|
addflag(lf->flags, F_CORPSETYPE, NA, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((lf->race->id == R_VAMPIRE) && !hasflag(lf->flags, F_ORIGRACE)) {
|
|
// if are asleep or killed by running water/sunlight, we will die normally
|
|
if (lfhasflag(lf, F_ASLEEP) || (lf->lastdamtype == DT_DIRECT)) {
|
|
noise(lf->cell, lf, NC_OTHER, SV_CAR, "a horrified scream!", "screams in horror!");
|
|
} else if (findobinmap(lf->cell->map, OT_COFFIN)) { // coffin around?
|
|
// restore 1 hp
|
|
lf->hp = 1;
|
|
killflagsofid(lf->flags, F_DEAD);
|
|
// convert into a gas cloud!
|
|
dospelleffects(NULL, OT_S_GASEOUSFORM, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, NULL);
|
|
// ai will now look for our coffin
|
|
if (thisisplayer) {
|
|
msg("^GYou feel the presence of a nearby coffin...");
|
|
} else {
|
|
addflag(lf->flags, F_WANTS, OT_COFFIN, B_COVETS, NA, NULL);
|
|
//killflagsofid(lf->flags, F_HOSTILE);
|
|
addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (lf->race->id == R_LICH) {
|
|
f = lfhasflag(lf, F_LIFEOB);
|
|
if (f) {
|
|
cell_t *loc;
|
|
loc = findnearbylifeob(lf->cell, UNLIMITED, f, NULL);
|
|
if (loc) {
|
|
// announce
|
|
if (thisisplayer) {
|
|
msg("^GYou feel your soul being pulled to safety!^n");
|
|
}
|
|
// teleport back to life ob, and revive.
|
|
teleportto(lf, loc, B_FALSE);
|
|
// restore all hp
|
|
lf->hp = lf->maxhp;
|
|
killflagsofid(lf->flags, F_DEAD);
|
|
return;
|
|
}
|
|
} else {
|
|
noise(lf->cell, lf, NC_OTHER, SV_CAR, "a horrified scream!", "screams in horror!");
|
|
}
|
|
}
|
|
|
|
if (lf->race->id == R_GLOWBUG) {
|
|
// final spell...
|
|
castspell(lf, OT_S_FLASH, NULL, NULL, lf->cell, NULL, NULL);
|
|
}
|
|
|
|
loseconcentration(lf);
|
|
|
|
// revert to your original form first.
|
|
if (lfhasflag(lf, F_POLYMORPHED)) {
|
|
if (lfhasflag(lf, F_ORIGRACE)) {
|
|
int premaxhp;
|
|
premaxhp = lf->maxhp;
|
|
abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL);
|
|
// but you stay at lower hp.
|
|
if (premaxhp < lf->maxhp) {
|
|
float ratio;
|
|
ratio = (float)premaxhp / (float) lf->maxhp;
|
|
lf->hp = ratio * lf->maxhp;
|
|
limit(&(lf->hp), 1, lf->maxhp);
|
|
}
|
|
if (thisisplayer) statdirty = B_TRUE;
|
|
/*
|
|
// ... but you're still dead
|
|
lf->hp = 0;
|
|
*/
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (!willbecomeghost) {
|
|
lf->alive = B_FALSE;
|
|
}
|
|
|
|
if (thisisplayer) {
|
|
lifeform_t *god;
|
|
// force screen redraw so you see your hp = 0
|
|
drawscreen();
|
|
|
|
if (lf->lastdamtype == DT_EXPLOSIVE) {
|
|
msg("^BYou are vaporised!");
|
|
vaporised = B_TRUE;
|
|
} else {
|
|
msg("^BYou die.");
|
|
}
|
|
more(); drawmsg();
|
|
|
|
// god effects...
|
|
if (!vaporised) {
|
|
god = getrandomprayedgod();
|
|
if (god && !godisangry(god->race->id)) {
|
|
switch (god->race->id) {
|
|
case R_GODBATTLE:
|
|
godsay(god->race->id, B_TRUE, "Rest in peace, brave warrior."); more();
|
|
break;
|
|
case R_GODDEATH:
|
|
if (strlen(reanimateas)) {
|
|
godsay(god->race->id, B_TRUE, "Arise, my servant..."); more(); break;
|
|
// you will rise as a monster.
|
|
} else {
|
|
godsay(god->race->id, B_TRUE, "Come to me, my servant..."); more();
|
|
msg("Bony claws rise up and drag your body underground.");
|
|
break;
|
|
}
|
|
case R_GODLIFE:
|
|
msg("Your spirit ascends to the heavens."); more(); break;
|
|
case R_GODTHIEVES: // lose all gold / gems
|
|
if (countmoney(player->pack)) {
|
|
int taken = B_FALSE;
|
|
if (killobsofid(player->pack, OT_GOLD, B_TRUE)) {
|
|
taken = B_TRUE;
|
|
msg("All your gold suddenly vanishes!"); more();
|
|
}
|
|
if (killobswithflag(player->pack, F_GEM, B_TRUE)) {
|
|
taken = B_TRUE;
|
|
msg("All your gems suddenly vanish!"); more();
|
|
}
|
|
if (taken) {
|
|
godsay(god->race->id, B_TRUE, "Yoink!"); more();
|
|
}
|
|
}
|
|
break;
|
|
case R_GODMERCY:
|
|
if (onein(10)) {
|
|
godsay(god->race->id, B_TRUE, "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, NULL);
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
lifeform_t *minion[MAXCANDIDATES];
|
|
int nminions = 0;
|
|
// intelligent monsters will say something
|
|
if (!hasflag(lf->flags, F_NODEATHSPEECH) && !lfhasflag(lf, F_SUMMONEDBY)) {
|
|
if (ispetof(lf, player)) {
|
|
if (cantalk(lf) && canhear(player, lf->cell, 4)) {
|
|
sayphrase(lf, SP_DIE, SV_SHOUT, NA, NULL);
|
|
} else if (!cansee(player, lf)) {
|
|
warn("You feel a profound sense of loss.");
|
|
// redraw since you can "see" the pet even if it's out of sight
|
|
needredraw = B_TRUE;
|
|
more();
|
|
//} else {
|
|
// makenoise(lf, N_DIE);
|
|
}
|
|
} else if (cantalk(lf)) {
|
|
if (pctchance(33)) {
|
|
sayphrase(lf, SP_DIE, SV_SHOUT, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
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, 5);
|
|
if (isundead(lf)) pleasegodmaybe(R_GODLIFE, 10);
|
|
} else if (getalignment(lf) == AL_GOOD) {
|
|
pleasegodmaybe(R_GODDEATH, 5);
|
|
} else { // ie. neutral
|
|
pleasegodmaybe(R_GODDEATH, 1);
|
|
}
|
|
|
|
// killing someone with an injury is considered merciful.
|
|
// (but only for injuries which you didn't just inflict!)
|
|
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->obfrom != B_NEWINJURY) {
|
|
pleasegodmaybe(R_GODMERCY, 5);
|
|
// only do this once.
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (lf->race->raceclass->id) {
|
|
case RC_DRAGON:
|
|
pleasegodmaybe(R_GODNATURE, 50);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (lf->lastdamtype == DT_FIRE) {
|
|
pleasegodmaybe(R_GODFIRE, 5);
|
|
}
|
|
// mercy god doesn't like killing
|
|
//angergodmaybe(R_GODMERCY, 1, GA_MURDER);
|
|
}
|
|
|
|
// minions who see this one die drop morale, and might flee
|
|
getminions(lf, minion, &nminions);
|
|
for (i = 0; i < nminions; i++) {
|
|
if (cansee(minion[i], lf)) {
|
|
f = lfhasflag(minion[i], F_MORALE);
|
|
if (f) {
|
|
f->val[0] -= 2;
|
|
// might flee?
|
|
if (killer && (f->val[0] <= 0)) {
|
|
scare(minion[i], killer, PERMENANT, 10);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// determine where to drop objects
|
|
corpsecell = lf->cell;
|
|
if (corpsecell && corpsecell->type->solid) {
|
|
// try the cell in front (in case they were climbing)
|
|
corpsecell = getcellindir(corpsecell, lf->facing);
|
|
// still solid? give up.
|
|
if (corpsecell && corpsecell->type->solid) {
|
|
corpsecell = NULL;
|
|
}
|
|
}
|
|
|
|
if (lfhasflag(lf, F_PHANTASM)) {
|
|
dropobs = B_FALSE;
|
|
}
|
|
|
|
|
|
|
|
// drop/kill all objects
|
|
if (corpsecell && (willbecomeghost || !thisisplayer)) {
|
|
while (lf->pack->first) {
|
|
if (vaporised || !dropobs) {
|
|
killob(lf->pack->first);
|
|
} else {
|
|
object_t *droppedob;
|
|
droppedob = moveob(lf->pack->first, corpsecell->obpile, ALL);
|
|
if (droppedob) {
|
|
if (killer && isplayer(killer)) {
|
|
addflag(droppedob->flags, F_BATTLESPOILS, B_TRUE, NA, NA, NULL);
|
|
}
|
|
} else {
|
|
killob(lf->pack->first);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// drop corpse/splatter blood
|
|
if (corpsecell) {
|
|
lifeform_t *souleater = NULL;
|
|
flag_t *soulflag = NULL;
|
|
|
|
// soul consumed?
|
|
if (corpsecell) {
|
|
lifeform_t *l;
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if (haslos(l, lf->cell)) {
|
|
soulflag = lfhasflag(l, F_CONSUMESOULS);
|
|
if (soulflag) {
|
|
souleater = l;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vaporised) {
|
|
if (lf->material->id == MT_FLESH) {
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
fragments(corpsecell, "chunk of flesh", 0, UNLIMITED);
|
|
break;
|
|
case 2:
|
|
fragments(corpsecell, "pool of blood", 0, UNLIMITED);
|
|
break;
|
|
}
|
|
}
|
|
} else if (souleater && soulflag) {
|
|
int amt;
|
|
if (isplayer(souleater)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%cYou consume %s%s soul!", getlfcol(souleater, CC_VGOOD), lfname, getpossessive(lfname));
|
|
soulflag->known = B_TRUE;
|
|
|
|
revealflagob(souleater, soulflag);
|
|
} else if (cansee(player, souleater)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getlfname(souleater, buf);
|
|
msg("^%c%s consumes %s%s soul!", getlfcol(souleater, CC_VGOOD), buf, lfname, getpossessive(lfname));
|
|
soulflag->known = B_TRUE;
|
|
}
|
|
amt = pctof( rnd(1,soulflag->val[0]), lf->maxhp);
|
|
limit(&amt, 1, NA);
|
|
gainhp(souleater, amt);
|
|
// copy some flags
|
|
copyflag(souleater->flags, lf->flags, F_BLOODBOIL);
|
|
// drop bones
|
|
addob(corpsecell->obpile, "pile of ash");
|
|
} else if (lfhasflag(lf, F_BLOODBOIL)) {
|
|
if (haslos(player, corpsecell)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s explodes into flames!", lfname);
|
|
}
|
|
// explode into flames
|
|
addobsinradius(corpsecell, 1, DT_COMPASS, "medium fire", B_TRUE, NULL);
|
|
} else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) {
|
|
// shattered
|
|
fragments(corpsecell, "chunk of ice", 2, UNLIMITED);
|
|
} else {
|
|
int unique = B_FALSE;
|
|
if (lfhasflag(lf, F_UNIQUE)) unique = B_TRUE;
|
|
|
|
if (lfhasflag(lf, F_NOCORPSE)) {
|
|
if (isundead(lf) && cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s crumbles to dust.", buf);
|
|
}
|
|
} else if (!unique && (lf->lastdamtype == DT_MELT)) {
|
|
// drop a pool of water
|
|
addob(corpsecell->obpile, "large puddle of water");
|
|
} else if (!unique && (lf->lastdamtype == DT_NECROTIC)) {
|
|
int n;
|
|
int numbones = 0;
|
|
char bonestring[BUFLEN];
|
|
for (n = 0; n < getlfsize(lf); n++) {
|
|
numbones += rnd(1,10);
|
|
}
|
|
// drop bones
|
|
snprintf(bonestring, BUFLEN, "%d bones",numbones);
|
|
addob(corpsecell->obpile, bonestring);
|
|
} else {
|
|
char corpseprefix[BUFLEN];
|
|
char corpsename[BUFLEN];
|
|
|
|
strcpy(corpseprefix, "");
|
|
switch (lf->lastdamtype) {
|
|
case DT_COLD:
|
|
strcat(corpseprefix, "frozen ");
|
|
break;
|
|
default:
|
|
if (lfhasflag(lf, F_FROZEN)) {
|
|
strcat(corpseprefix, "frozen ");
|
|
}
|
|
break;
|
|
}
|
|
if (lfhasflag(lf, F_BEHEADED) && !lfhasflag(lf, F_CORPSETYPE)) {
|
|
strcat(corpseprefix, "headless ");
|
|
}
|
|
|
|
f = lfhasflag(lf, F_CORPSETYPE);
|
|
if (f) {
|
|
snprintf(corpsename, BUFLEN, "%s%s", corpseprefix, f->text);
|
|
} else {
|
|
snprintf(corpsename, BUFLEN, "%s%s corpse", corpseprefix, lf->race->name);
|
|
}
|
|
|
|
// special cases
|
|
corpse = addob(corpsecell->obpile, corpsename);
|
|
if (corpse) {
|
|
if ((lf->lastdamtype == DT_FIRE) && isflammable(corpse)) {
|
|
addflag(corpse->flags, F_ONFIRE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
|
|
// tainted?
|
|
if ((lf->lastdamtype == DT_POISONGAS) || lfhasflag(lf, F_POISONCORPSE) || lfhasflag(lf, F_POISONED)) {
|
|
addflag(corpse->flags, F_TAINTED, B_TRUE, NA, NA, NULL);
|
|
}
|
|
|
|
// set colour based on monster
|
|
if (corpse->type->id == OT_CORPSE) {
|
|
colourmatchob(corpse, lf);
|
|
}
|
|
|
|
// inherit lifeform knowledge and abilities in case we raise it
|
|
copyflag(corpse->flags, lf->flags, F_KNOWSABOUT);
|
|
copyflag(corpse->flags, lf->flags, F_HOMEMAP);
|
|
copyflag(corpse->flags, lf->flags, F_CANCAST);
|
|
copyflag(corpse->flags, lf->flags, F_CANWILL);
|
|
copyflag(corpse->flags, lf->flags, F_JOB);
|
|
// race subspecies flags, so that we can have mosnters which eat
|
|
// only certain kinds of corpses
|
|
copyflag(corpse->flags, lf->flags, F_AVIAN);
|
|
copyflag(corpse->flags, lf->flags, F_CANINE);
|
|
copyflag(corpse->flags, lf->flags, F_EQUINE);
|
|
copyflag(corpse->flags, lf->flags, F_FELINE);
|
|
|
|
f = hasflag(corpse->flags, F_CORPSEOF);
|
|
if (f) {
|
|
f->val[1] = lf->level;
|
|
}
|
|
|
|
// some corpses will regenerate...
|
|
if (copyflag(corpse->flags, lf->flags, F_REVIVETIMER)) {
|
|
killflagsofid(corpse->flags, F_OBHPDRAIN);
|
|
}
|
|
// special case
|
|
if (lf->race->id == R_BLASTBUG) {
|
|
flag_t *hpflag;
|
|
hpflag = hasflag(corpse->flags, F_OBHP);
|
|
if (hpflag) hpflag->val[0] = rnd(5,6);
|
|
}
|
|
|
|
// corpse of a player pet?
|
|
if (ispetof(lf, player)) {
|
|
addflag(corpse->flags, F_PETOF, player->id, NA, NA, NULL);
|
|
}
|
|
|
|
// add extra flags ?
|
|
getflags(lf->flags, retflag, &nretflags, F_CORPSEFLAG, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_CORPSEFLAG) {
|
|
addflag(corpse->flags, f->val[0], f->val[1], f->val[2], NA, f->text);
|
|
}
|
|
}
|
|
|
|
// inherit size from lf
|
|
f = hasflag(corpse->flags, F_SIZE);
|
|
if (f) {
|
|
f->val[0] = getlfsize(lf);
|
|
} else {
|
|
addflag(corpse->flags, F_SIZE, getlfsize(lf), NA, NA, NULL);
|
|
}
|
|
|
|
// remember what killed us.
|
|
f = hasflag(corpse->flags, F_CORPSEOF);
|
|
if (f) {
|
|
changeflagtext(f, lf->lastdam);
|
|
}
|
|
|
|
if (hasflag(corpse->flags, F_HEADLESS)) {
|
|
object_t *headob;
|
|
char headname[BUFLEN];
|
|
// drop head too
|
|
snprintf(headname, BUFLEN, "%s head",lf->race->name);
|
|
headob = addob(corpsecell->obpile, headname);
|
|
colourmatchob(headob, lf);
|
|
}
|
|
|
|
if (corpse->type->id == OT_BABAYAGAHUT) {
|
|
// link the hut to the new region.
|
|
createbranchlink(corpsecell->map, corpsecell, corpse, NULL, BH_BABAYAGAHUT, corpsecell->map->region);
|
|
}
|
|
}
|
|
|
|
// For bones files:
|
|
if (thisisplayer) {
|
|
char pname[BUFLEN];
|
|
getplayername(pname);
|
|
addflag(corpse->flags, F_NAMED, NA, NA, NA, pname);
|
|
if (streq(lf->killverb, "Eaten")) {
|
|
// this will cause the player's corpse description
|
|
// to show up as "partially eaten". used for
|
|
// bones files.
|
|
f = hasflag(corpse->flags, F_EDIBLE);
|
|
if (f) {
|
|
f->val[2] = f->val[1] / 2;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (lf->race->id == R_SPRITEFIRE) {
|
|
addobfast(corpsecell->obpile, OT_FIRESMALL);
|
|
}
|
|
}
|
|
|
|
// some lfs have extra corpse objects
|
|
getflags(lf->flags, retflag, &nretflags, F_EXTRACORPSE, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if ((f->val[0] == NA) || pctchance(f->val[0])) {
|
|
addob(corpsecell->obpile, f->text);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// splatter
|
|
if (!vaporised) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_DIESPLATTER);
|
|
if (f) {
|
|
int speed,howfar;
|
|
howfar = f->val[0];
|
|
speed = f->val[1];
|
|
|
|
if (speed < 0) speed = 0;
|
|
if (howfar < 0) howfar = UNLIMITED;
|
|
fragments(corpsecell, f->text, f->val[1], f->val[0]);
|
|
}
|
|
}
|
|
} // end if corpsecell
|
|
|
|
// player killed last monster?
|
|
if (killer && isplayer(killer) && !thisisplayer) {
|
|
lifeform_t *l;
|
|
int battledone = B_TRUE;
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if (!isplayer(l) && areenemies(l, player) && haslof(l->cell, player->cell, LOF_WALLSTOP, NULL)) {
|
|
if (l != lf) {
|
|
battledone = B_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (battledone) {
|
|
pleasegodmaybe(R_GODBATTLE, 5);
|
|
}
|
|
}
|
|
|
|
|
|
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_LIFEOB, NA, 5, 2, cid);
|
|
}
|
|
} else {
|
|
// Will the dead lf get reanimated up as a monster ?
|
|
//
|
|
if (!vaporised) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
// killed by a vampire = vampire.
|
|
if (killer) {
|
|
switch (killer->race->id) {
|
|
case R_VAMPIRE: sprintf(reanimateas, "vampire"); break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
// died by eating your own race = ghoul.
|
|
// died by eating a vampire corpse = vampire.
|
|
if (!strlen(reanimateas)) {
|
|
getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[2] == R_VAMPIRE) {
|
|
sprintf(reanimateas, "vampire");
|
|
} else if (retflag[i]->val[2] == lf->race->baseid) {
|
|
sprintf(reanimateas, "ghoul");
|
|
}
|
|
}
|
|
}
|
|
|
|
// hecta-worshippers often get reanimated.
|
|
if (!strlen(reanimateas) && thisisplayer && godprayedto(R_GODDEATH)) {
|
|
if (onein(3)) {
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
sprintf(reanimateas, "zombie");
|
|
break;
|
|
case 2:
|
|
sprintf(reanimateas, "skeleton");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// announce
|
|
if (strlen(reanimateas)) {
|
|
if (thisisplayer || cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%c%s%s corpse rises up as %s %s!^n",
|
|
C_MAGENTA,
|
|
lfname, getpossessive(lfname),
|
|
needan(reanimateas) ? "an" : "a", reanimateas);
|
|
}
|
|
}
|
|
}
|
|
|
|
// now kill the lifeform.
|
|
//if (lf->controller != C_PLAYER) {
|
|
if (!thisisplayer) {
|
|
// actually kill the lifeform
|
|
killlf(lf);
|
|
assert(where->lf == NULL);
|
|
}
|
|
}
|
|
|
|
// IMPORTANT: DO NOT REFERENCE lf->xxxx AFTER THIS POINT
|
|
// UNLESS WE ARE _SURE_ IT IS THE PLAYER (ie. thisisplayer = true)
|
|
|
|
if (dividerace != R_NONE) {
|
|
if (dividelos) {
|
|
msg("^w%s divides!", dividename);
|
|
}
|
|
// add two new worms nearby, with less hp.
|
|
for (i = 0;i < 2; i++) {
|
|
lifeform_t *newlf;
|
|
newlf = addmonster(dividecell[i], dividerace, NULL, B_FALSE, 1, B_FALSE, NULL);
|
|
if (newlf) {
|
|
// half hp
|
|
f = hasflag(newlf->flags, F_HITDICE);
|
|
f->val[0] = 0;
|
|
f->val[1] = 1;
|
|
f->val[2] = dividehp;
|
|
newlf->maxhp = dividehp;
|
|
newlf->hp = dividehp;
|
|
// half TR
|
|
f = hasflag(newlf->flags, F_TR);
|
|
f->val[0] = dividetr;
|
|
// no xp for killing
|
|
killflagsofid(newlf->flags, F_XPVAL);
|
|
addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Note that if we reanimate, the new monster will appear in an
|
|
// ADJACENT cell. this solves the problem that we can't actually
|
|
// kill() the player's lf because we need to see their objects to
|
|
// show their final possessions.
|
|
if (strlen(reanimateas)) {
|
|
cell_t *c = NULL;
|
|
// announce, then add on " named playername", if required.
|
|
if (lf && isplayer(lf)) {
|
|
char pname[BUFLEN];
|
|
getplayername(pname);
|
|
strcat(reanimateas, " named ");
|
|
strcat(reanimateas, pname);
|
|
}
|
|
|
|
if (where->lf) {
|
|
c = getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND);
|
|
} else {
|
|
c = where;
|
|
}
|
|
if (c) {
|
|
// remove the corpse...
|
|
if (corpse) killob(corpse);
|
|
// add the reanimated monster
|
|
addmonster(c, R_SPECIFIED, reanimateas, B_FALSE, 1, B_FALSE, NULL);
|
|
}
|
|
}
|
|
|
|
// Write out a bones file? A bones file might be written if:
|
|
// - the player died in a ROOM.
|
|
// AND
|
|
// - the player died in a major dungeon branch, which wasn't the realm of gods
|
|
if (thisisplayer && (where->room) &&
|
|
(where->map->region->rtype->majorbranch) &&
|
|
(where->map->region->rtype->id != BH_HEAVEN)) {
|
|
// If all of the above are true, the below will trigger a bones file:
|
|
// Player reanimating as some kind of monster
|
|
// Being killed by stoning
|
|
// Random chance
|
|
//
|
|
if (strlen(reanimateas)) {
|
|
dobonesfile = B_TRUE;
|
|
} else if (corpse && (corpse->type->id == OT_STATUE)) {
|
|
dobonesfile = B_TRUE;
|
|
} else if (onein(4)) {
|
|
dobonesfile = B_TRUE;
|
|
}
|
|
|
|
if (hasjob(lf, J_GOD)) {
|
|
char ch;
|
|
char ques[BUFLEN];
|
|
sprintf(ques, "Bones file dump = %s. Override?", dobonesfile ? "true" : "false");
|
|
ch = askchar(ques, "yn", "n", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
ch = askchar("Dump bones file?", "yn", "n", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
dobonesfile = B_TRUE;
|
|
} else {
|
|
dobonesfile = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dobonesfile) {
|
|
savebones(where->map, where->room);
|
|
}
|
|
|
|
if (needredraw) {
|
|
drawscreen();
|
|
}
|
|
|
|
if (killedgod) {
|
|
wingame();
|
|
}
|
|
}
|
|
|
|
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 wanthd;
|
|
int totcount = 0;
|
|
|
|
dblog("START MONSTER DUMP:");
|
|
for (wanthd = 0; wanthd <= maxmonhitdice ; wanthd++) {
|
|
int count = 0;
|
|
// count them
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
if (gettrrace(r) == wanthd) {
|
|
count++;
|
|
totcount++;
|
|
}
|
|
}
|
|
dblog("MONSTERS WITH THREAT RATING %d (%d found):",wanthd, count);
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
int thishd;
|
|
thishd = gettrrace(r);
|
|
if (thishd == wanthd) {
|
|
int max;
|
|
f = hasflag(r->flags, F_HITDICE);
|
|
max = flagtomaxhp(f);
|
|
dblog("\t%s (%d hp)",r->name, max);
|
|
}
|
|
}
|
|
}
|
|
dblog("END MONSTER DUMP (%d found)",totcount);
|
|
}
|
|
|
|
void genareaknowledge(flagpile_t *fp, int chancemod) {
|
|
char knownstuff[BUFLENSMALL];
|
|
|
|
if (hasflag(fp, F_KNOWSABOUT)) return;
|
|
|
|
strcpy(knownstuff, "");
|
|
// determine what someone knows about
|
|
// stairs? (high chance)
|
|
if (pctchance(75 + chancemod)) {
|
|
strcat(knownstuff, "e");
|
|
}
|
|
// rare monsters/objects? (highish chance)
|
|
if (pctchance(60 + chancemod)) {
|
|
strcat(knownstuff, "o");
|
|
}
|
|
if (pctchance(60 + chancemod)) {
|
|
strcat(knownstuff, "m");
|
|
}
|
|
// shops? (med chance)
|
|
if (pctchance(50 + chancemod)) {
|
|
strcat(knownstuff, "s");
|
|
}
|
|
// traps? (low chance)
|
|
if (pctchance(25 + chancemod)) {
|
|
strcat(knownstuff, "t");
|
|
}
|
|
addflag(fp, F_KNOWSABOUT, NA, NA, NA, knownstuff);
|
|
}
|
|
|
|
// if alignment needs to be generated randomly (or selected in the case of the player), do so.
|
|
void generatealignment(lifeform_t *lf) {
|
|
flag_t *f;
|
|
|
|
// sometimes alignment is forced.
|
|
switch (getsubjob(lf)) {
|
|
case SJ_NECROMANCER:
|
|
setalignment(lf, AL_EVIL);
|
|
return;
|
|
case SJ_PALADIN:
|
|
setalignment(lf, AL_GOOD);
|
|
return;
|
|
default: break;
|
|
}
|
|
|
|
f = hasflagval(lf->flags, F_ALIGNMENT, AL_NONE, NA, NA, NULL);
|
|
if (f && strlen(f->text)) {
|
|
char buf[BUFLEN];
|
|
char buf2[BUFLEN],ch;
|
|
getplayername(buf2);
|
|
snprintf(buf, BUFLEN, "%s, select your alignment:", buf2);
|
|
initprompt(&prompt, buf);
|
|
if (strchr(f->text, 'g')) addchoice(&prompt, 'g', "Good", NULL, NULL, NULL);
|
|
if (strchr(f->text, 'n')) addchoice(&prompt, 'n', "Neutral", NULL, NULL, NULL);
|
|
if (strchr(f->text, 'e')) addchoice(&prompt, 'e', "Evil", NULL, NULL, NULL);
|
|
if (isplayer(lf)) {
|
|
ch = getchoice(&prompt);
|
|
} else {
|
|
ch = prompt.choice[rnd(0,prompt.nchoices-1)].ch;
|
|
}
|
|
switch (ch) {
|
|
case 'g': f->val[0] = AL_GOOD; break;
|
|
case 'e': f->val[0] = AL_EVIL; break;
|
|
default:
|
|
case 'n': f->val[0] = AL_NEUTRAL; break;
|
|
}
|
|
free(f->text);
|
|
f->text = strdup("");
|
|
}
|
|
}
|
|
|
|
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, NULL));
|
|
}
|
|
|
|
// free mem
|
|
free(xpposs);
|
|
free(raceposs);
|
|
|
|
// dump xp for levels
|
|
dblog("");
|
|
dblog("");
|
|
for (i = 2; i < 30; i++) {
|
|
char buf[BUFLEN];
|
|
snprintf(buf, BUFLEN, "Lev %d",i);
|
|
dblog("%-10s%ld",buf, getxpforlev(i));
|
|
}
|
|
}
|
|
|
|
int digcell(lifeform_t *lf, cell_t *c, object_t *o) {
|
|
char obname[BUFLEN];
|
|
flag_t *f;
|
|
int digpower = 1;
|
|
getobname(o, obname, 1);
|
|
f = hasflag(o->flags, F_HELPSDIG);
|
|
if (f) {
|
|
digpower = f->val[0];
|
|
}
|
|
|
|
if (!c) {
|
|
return B_TRUE;
|
|
}
|
|
if (c->type->solid) {
|
|
if (isdiggable(c)) {
|
|
// start digging!
|
|
addflag(lf->flags, F_DIGGING, c->x, c->y, digpower, NULL);
|
|
if (isplayer(lf)) {
|
|
msg("You start digging into %s %s.", needan(c->type->name) ? "an" : "a", c->type->name);
|
|
needredraw = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s starts digging into %s %s.", lfname, needan(c->type->name) ? "an" : "a", c->type->name);
|
|
needredraw = B_TRUE;
|
|
}
|
|
taketime(lf, getactspeed(lf));
|
|
} 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, B_FALSE)) {
|
|
taketime(lf, getactspeed(lf));
|
|
removeob(door, door->amt);
|
|
if (isplayer(lf)) {
|
|
msg("You smash open a door!");
|
|
needredraw = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s smashes open a door.",lfname);
|
|
needredraw = B_TRUE;
|
|
}
|
|
drawscreen();
|
|
failed = B_FALSE;
|
|
}
|
|
} else if (hasob(c->obpile, OT_STATUE)) {
|
|
int dam;
|
|
object_t *so;
|
|
char statname[BUFLEN];
|
|
flag_t *f;
|
|
|
|
so = hasob(c->obpile, OT_STATUE);
|
|
getobname(so, statname, so->amt);
|
|
|
|
taketime(lf, getactspeed(lf));
|
|
|
|
// statue takes 1/2 damage
|
|
f = hasflag(so->flags, F_OBHP);
|
|
if (f) {
|
|
dam = (f->val[1] / 2); // ie. half max hp
|
|
} else {
|
|
dam = 1;
|
|
}
|
|
// statue ?
|
|
if (isplayer(lf)) {
|
|
msg("You hit %s with your %s.", statname, noprefix(obname));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s hits %s with %s.", lfname, statname, obname);
|
|
}
|
|
takedamage(so, dam, DT_CHOP);
|
|
} 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);
|
|
|
|
if (o) {
|
|
if (lfhasflag(lf, F_LEVITATING)) {
|
|
if (isplayer(lf)) {
|
|
msg("You can't reach the ground from up here!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// TODO: check if the floor is solid?
|
|
if (lf->cell->map->depth >= lf->cell->map->region->rtype->maxdepth) {
|
|
if (isplayer(lf)) {
|
|
msg("The floor here is too solid to dig in.");
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s tries to dig a hole in the floor, but fails.", lfname);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
if (isplayer(lf)) {
|
|
msg("You dig a hole in the floor.");
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s digs a hole in the floor.", lfname);
|
|
}
|
|
|
|
addobfast(lf->cell->obpile, OT_HOLEINGROUND);
|
|
|
|
// takes a lot of time
|
|
if (o) {
|
|
taketime(lf, getactspeed(lf) * 9);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int digup(lifeform_t *lf, object_t *o) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
|
|
// no roof?
|
|
if (lf->cell->map->region->rtype->id == BH_WORLDMAP) {
|
|
if (isplayer(lf)) {
|
|
msg("There is no roof above you to dig into!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// if digging with an object, you must be able to reach the roof
|
|
if (o) {
|
|
if (!isairborne(lf)) {
|
|
if (isplayer(lf)) {
|
|
msg("You can't reach the roof!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
msg("You dig a hole in the roof.");
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s digs a hole in the roof.");
|
|
}
|
|
|
|
// add some stones here
|
|
addob(lf->cell->obpile, "20-50 stones");
|
|
addobfast(lf->cell->obpile, OT_HOLEINROOF);
|
|
|
|
// takes a LOT of time since gravity is against us
|
|
if (o) {
|
|
taketime(lf, getactspeed(lf) * 18);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void do_eyesight_adjust(lifeform_t *lf) {
|
|
int i,nlitcells = 0;
|
|
int preea;
|
|
|
|
if (isblind(lf)) return;
|
|
|
|
// any lit cells within los?
|
|
for (i = 0; i < lf->nlos; i++) {
|
|
if (lf->los[i]->lit) {
|
|
nlitcells++;
|
|
}
|
|
}
|
|
|
|
preea = lf->eyeadjustment;
|
|
if (nlitcells) {
|
|
// if you could see any lit cells, you lose your eyeadjustment for nightvision
|
|
if (lf->eyeadjustment/10) {
|
|
lf->eyeadjustment = 0;
|
|
if (isplayer(lf)) msg("The light causes you to lose your natural night sight.");
|
|
setlosdirty(lf);
|
|
//precalclos(lf);
|
|
}
|
|
} else if (!lfhasflag(lf, F_SEEINDARK)) {
|
|
if ((lf->cell->lit == L_NOTLIT) && (lf->eyeadjustment < MAX_EYEADJ)) {
|
|
// your eyes start to adjust...
|
|
lf->eyeadjustment++;
|
|
if (isplayer(lf) && (lf->eyeadjustment/10) > (preea/10)) {
|
|
if (preea/10) {
|
|
msg("Your eyes have fully adjusted to the darkness.");
|
|
} else {
|
|
msg("Your eyes are starting to adjust to the darkness.");
|
|
}
|
|
}
|
|
setlosdirty(lf);
|
|
//precalclos(lf);
|
|
}
|
|
}
|
|
}
|
|
|
|
// dump which level random things will appear at
|
|
void dumplev(void) {
|
|
int i;
|
|
race_t *r;
|
|
objecttype_t *ot;
|
|
flag_t *f;
|
|
dblog("Start lev monster dump");
|
|
dblog("------------------\n");
|
|
// NOTE: this code copied from getrandomrace(), which is used by addmonster().
|
|
for (i = 1; i <= 25; i++) {
|
|
int min,max,prevmin,prevmax;
|
|
gettrrange(i-1, &prevmin, &prevmax, RARITYVARIANCELF, B_FALSE);
|
|
gettrrange(i, &min, &max, RARITYVARIANCELF, B_FALSE);
|
|
fprintf(logfile, "Dlev %d (hd %d-%d): ",i,min,max);
|
|
for (r = firstrace ; r; r = r->next) {
|
|
int hd = 0;
|
|
hd = gettrrace(r);
|
|
// ok this lev?
|
|
if ((hd >= min) && (hd <= max)) {
|
|
char buf[BUFLEN];
|
|
strcpy(buf, "");
|
|
// ok on previous lev too?
|
|
if ((hd >= prevmin) && (hd <= prevmax)) {
|
|
// only print if dlev is 1
|
|
if (i == 1) {
|
|
snprintf(buf, BUFLEN, "%s, ", r->name);
|
|
}
|
|
} else {
|
|
// ie. new mosnter for this lev
|
|
//snprintf(buf, BUFLEN, "*%s*, ", r->name);
|
|
//makeuppercase(buf);
|
|
snprintf(buf, BUFLEN, "%s, ", r->name);
|
|
}
|
|
fprintf(logfile, "%s", buf);
|
|
}
|
|
}
|
|
fprintf(logfile, "\n");
|
|
}
|
|
|
|
|
|
dblog("Start object dump");
|
|
dblog("------------------\n");
|
|
for (i = 1; i <= 25; i++) {
|
|
int min,max,prevmin,prevmax;
|
|
getrarityrange(i-1, &prevmin, &prevmax, RARITYVARIANCEOB, B_FALSE);
|
|
getrarityrange(i, &min, &max, RARITYVARIANCEOB, B_FALSE);
|
|
fprintf(logfile, "Dlev %d (rar >= %d): ",i,min);
|
|
for (ot = objecttype ; ot; ot = ot->next) {
|
|
int rarity = 0;
|
|
f = hasflagval(ot->flags, F_RARITY, H_DUNGEON, NA, NA, NULL);
|
|
if (f) {
|
|
rarity = f->val[1];
|
|
// ok this lev?
|
|
if ((rarity >= min) && (rarity <= max)) {
|
|
char buf[BUFLEN];
|
|
strcpy(buf, "");
|
|
// ok on previous lev too?
|
|
if ((rarity >= prevmin) && (rarity <= prevmax)) {
|
|
// only print if dlev is 1
|
|
if (i == 1) {
|
|
snprintf(buf, BUFLEN, "%s, ", ot->name);
|
|
}
|
|
} else {
|
|
// ie. new object for this lev
|
|
//snprintf(buf, BUFLEN, "*%s*, ", r->name);
|
|
//makeuppercase(buf);
|
|
snprintf(buf, BUFLEN, "%s, ", ot->name);
|
|
}
|
|
fprintf(logfile, "%s", buf);
|
|
}
|
|
}
|
|
}
|
|
fprintf(logfile, "\n");
|
|
}
|
|
|
|
fflush(logfile);
|
|
}
|
|
|
|
int eat(lifeform_t *lf, object_t *o) {
|
|
char lfname[BUFLEN];
|
|
char obname[BUFLEN];
|
|
char buf[BUFLEN];
|
|
flag_t *f;
|
|
double nutrition;
|
|
double turnstoeat;
|
|
double eateachturn;
|
|
double startpcteaten = 0;
|
|
double pcteaten;
|
|
int drinking = B_FALSE;
|
|
int amt;
|
|
int fullyeaten = B_FALSE;
|
|
flag_t *alreadyeating;
|
|
enum HUNGER hlev,posthlev;
|
|
int stopeating = B_FALSE;
|
|
int rawmeat = B_FALSE;
|
|
race_t *corpserace = NULL;
|
|
flag_t *cf;
|
|
|
|
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
if (isplayer(lf)) msg("You are too enraged to eat!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (hasflag(o->flags, F_DRINKABLE)) {
|
|
drinking = B_TRUE;
|
|
}
|
|
|
|
if (drinking) {
|
|
if (!candrink(lf, o)) {
|
|
if (isplayer(lf)) {
|
|
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);
|
|
|
|
// is this a corpse?
|
|
cf = hasflag(o->flags, F_CORPSEOF);
|
|
if (cf) {
|
|
corpserace = findrace(cf->val[0]);
|
|
// special case
|
|
if (lf->race->id == R_LINGPARASITE) {
|
|
if (isplayer(lf)) {
|
|
msg("You crawl inside %s.", obname);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s crawls inside %s.", lfname, obname);
|
|
}
|
|
// die, but animate the zombie!
|
|
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
|
|
addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
|
|
setlastdam(lf, "merging with a corpse");
|
|
lf->lastdamtype = DT_DIRECT;
|
|
lf->hp = 0;
|
|
makezombie(o);
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// get total nutrition
|
|
nutrition = getnutrition(o);
|
|
|
|
if (nutrition == 0) {
|
|
// this might happen if you purposely try to eat something non-edible but drinkable
|
|
// (eg. a potion). also when you have f_caneatmaterial and you something which
|
|
// isn't normally edible (ie. a goat eating wood).
|
|
if (lfhasflagval(lf, F_CANEATMATERIAL, o->material->id, NA, NA, NULL)) {
|
|
// nutrition based on object weight
|
|
nutrition = getobweight(o)*5;
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("That doesn't seem very nutritious...");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// only do this check for the player - basically it should
|
|
// handle the case where we have poluymorphed into something
|
|
// which doesn't eat.
|
|
if (isplayer(lf)) {
|
|
f = hasflag(lf->flags, F_HUNGER);
|
|
if (!f) {
|
|
msg("You don't need to %s!", drinking ? "drink" : "eat");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// after this point, you ARE going to eat it.
|
|
if (o->amt > 1) {
|
|
o = splitob(o);
|
|
}
|
|
|
|
// uncooked meat?
|
|
if (iscorpse(o) && isplayer(lf)) {
|
|
int ch;
|
|
char ques[BUFLEN];
|
|
if (!hasflag(o->flags, F_PREPARED) && !lfhasflag(lf, F_CANEATRAW)) {
|
|
if (!lfhasflag(lf, F_EATING) && getskill(lf, SK_COOKING)) {
|
|
more();
|
|
sprintf(ques,"Really eat %s raw?",obname);
|
|
ch = askchar(ques,"yn","y", B_TRUE, B_FALSE);
|
|
if (ch != 'y') {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
// limit nutrition
|
|
//if (nutrition > (HUNGERCONST/2)) nutrition = (HUNGERCONST/2);
|
|
rawmeat = B_TRUE;
|
|
}
|
|
|
|
// cannibulism ?
|
|
if (corpserace->id == lf->race->baseid) {
|
|
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) {
|
|
more();
|
|
sprintf(ques,"Really eat your own race?");
|
|
ch = askchar(ques,"yn","y", B_TRUE, B_FALSE);
|
|
if (ch != 'y') {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (touch(lf, o)) {
|
|
taketime(lf, getactspeed(lf));
|
|
return B_TRUE;
|
|
}
|
|
|
|
// how many turns to eat whole thing?
|
|
f = hasflag(o->flags, F_EDIBLE);
|
|
if (f && (f->val[2] != NA)) {
|
|
startpcteaten = f->val[2];
|
|
} else {
|
|
startpcteaten = 0;
|
|
}
|
|
|
|
if (drinking) {
|
|
// when drinking you can drink all of it.
|
|
turnstoeat = 1;
|
|
} else {
|
|
// time to eat entire food:
|
|
turnstoeat = 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) {
|
|
char taste[BUFLEN];
|
|
if (rawmeat) {
|
|
sprintf(taste, "The raw meat tastes disgusting!");
|
|
} else if (hasflagval(o->flags, F_CORPSEOF, R_CHICKEN, NA, NA, NULL)) {
|
|
sprintf(taste, "Tastes like chicken!");
|
|
} else if (f && (f->val[1] >= 20)) {
|
|
sprintf(taste, "Yum!");
|
|
} else {
|
|
strcpy(taste, "");
|
|
}
|
|
if (alreadyeating) {
|
|
if (isplayer(lf)) {
|
|
msg("You finish %s.", drinking ? "drinking" : "eating");
|
|
if (strlen(taste)) msg("%s", taste);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s finishes %s.", lfname, drinking ? "drinking" : "eating");
|
|
}
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("You %s %s.", drinking ? "drink" : "eat", obname);
|
|
if (strlen(taste)) msg("%s", taste);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s %s %s.", lfname, drinking ? "drinks" : "eats", obname);
|
|
}
|
|
}
|
|
} else {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (alreadyeating) {
|
|
if (turnstoeat <= 1) {
|
|
killflag(alreadyeating);
|
|
}
|
|
} else if (!alreadyeating && (turnstoeat > 1)) {
|
|
addflag(lf->flags, F_EATING, NA, NA, NA, buf);
|
|
}
|
|
|
|
if (!isimmuneto(lf->flags, DT_POISON, B_FALSE)) {
|
|
if (isrotting(o)) {
|
|
char dambuf[BUFLEN];
|
|
enum POISONTYPE ptid;
|
|
int ppower = 1;
|
|
|
|
// lose hp
|
|
if (isplayer(lf)) {
|
|
msg("^BThat %s was bad!", drinking ? "liquid" : "food");
|
|
}
|
|
// food poisoning for 20 turns
|
|
if (drinking) {
|
|
snprintf(dambuf, BUFLEN, "%s",obname);
|
|
} else {
|
|
snprintf(dambuf, BUFLEN, "a bad %s",noprefix(obname));
|
|
}
|
|
if (onein(3)) {
|
|
ptid = P_FOODBAD;
|
|
} else {
|
|
ptid = P_FOOD;
|
|
}
|
|
// cannibulism ?
|
|
if (corpserace && (corpserace->id == lf->race->baseid)) {
|
|
ppower = 3;
|
|
}
|
|
poison(lf, rnd(20,40), ptid, ppower, dambuf, cf ? cf->val[0] : R_NONE);
|
|
} else if (!drinking) {
|
|
// raw meat?
|
|
if (corpserace && israwmeat(o) && !lfhasflag(lf, F_CANEATRAW)) {
|
|
int checkdiff;
|
|
enum POISONTYPE ptid;
|
|
int timemin,timemax;
|
|
int ppower = 1;
|
|
char dambuf[BUFLEN];
|
|
snprintf(dambuf, BUFLEN, "a bad %s",noprefix(obname));
|
|
if (hasflag(corpserace->flags, F_AVIAN)) {
|
|
checkdiff = 20;
|
|
ptid = P_FOODBAD;
|
|
timemin = 30;
|
|
timemax = 50;
|
|
} else {
|
|
checkdiff = 13;
|
|
if (onein(3)) {
|
|
ptid = P_FOOD;
|
|
} else {
|
|
ptid = P_MIGRAINE;
|
|
}
|
|
timemin = 20;
|
|
timemax = 40;
|
|
}
|
|
|
|
// cannibulism ?
|
|
if (corpserace->id == lf->race->baseid) {
|
|
ppower = 3;
|
|
}
|
|
if (!skillcheck(lf, SC_POISON, checkdiff, 0)) {
|
|
poison(lf, rnd(timemin,timemax), ptid, ppower, dambuf, corpserace ? corpserace->id : R_NONE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// get less hungry
|
|
hlev = gethungerlevel(gethungerval(lf));
|
|
modhunger(lf, -eateachturn);
|
|
posthlev = gethungerlevel(gethungerval(lf));
|
|
|
|
if (fullyeaten) {
|
|
// special cases
|
|
if (!hasflag(o->flags, F_TAINTED)) {
|
|
if (hasflagval(o->flags, F_CORPSEOF, R_NEWT, NA, NA, NULL)) {
|
|
// think "eye of newt"
|
|
gainmp(lf, 2);
|
|
}
|
|
if (hasflagval(o->flags, F_CORPSEOF, R_EYEBAT, NA, NA, NULL)) {
|
|
lf->maxmp++;
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
msg("You feel slightly more magically attuned.");
|
|
}
|
|
} else if (hasflagval(o->flags, F_CORPSEOF, R_BEHOLDER, NA, NA, NULL)) {
|
|
lf->maxmp += 10;
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
msg("You feel much more magically attuned!");
|
|
}
|
|
}
|
|
|
|
// most eat effects only happen if you have f_MUTABLE.
|
|
if (lfhasflag(lf, F_MUTABLE)) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
getflags(o->flags, retflag, &nretflags, F_EATCONFER, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->val[0] != F_MUTABLE) {
|
|
int chance;
|
|
int luckmod = 0;
|
|
chance = atoi(f->text);
|
|
sumflags(lf->flags, F_EXTRALUCK, &luckmod, NULL, NULL);
|
|
chance += (luckmod*5);
|
|
limit(&chance, 0, 100);
|
|
if (pctchance(chance)) {
|
|
enum FLAG fid;
|
|
int val[2];
|
|
fid = f->val[0];
|
|
val[0] = f->val[1];
|
|
val[1] = f->val[2];
|
|
// already got this?
|
|
if (lfhasflag(lf, fid)) {
|
|
int changed = B_FALSE;
|
|
// sometimes improve it
|
|
switch (fid) {
|
|
case F_DTRESIST: fid = F_DTIMMUNE; changed = B_TRUE; break;
|
|
case F_SEEINDARK:
|
|
case F_PRODUCESLIGHT:
|
|
break; // can have multiple.
|
|
default: fid = F_NONE; break;
|
|
}
|
|
// still got the new one?
|
|
if (changed && (fid != F_NONE)) {
|
|
if (lfhasflag(lf, fid)) {
|
|
fid = F_NONE;
|
|
}
|
|
}
|
|
}
|
|
if (fid != F_NONE) {
|
|
// lose half your max hp!
|
|
losehp_real(lf, (lf->maxhp/2), DT_DIRECT, NULL,
|
|
"the shock of mutation",
|
|
B_NODAMADJUST, o, B_NORETALIATE, NULL, B_DAMEFFECTS);
|
|
if (isplayer(lf)) {
|
|
msg("^%cYou convulse in agony as your body mutates!",
|
|
getlfcol(lf, CC_BAD));
|
|
} else if (cansee(player, lf)) {
|
|
msg("^%c%s convulses in agony as its body mutates!",
|
|
getlfcol(lf, CC_BAD), lfname);
|
|
}
|
|
// still alive? you gain the ability!
|
|
if (!isdead(lf)) {
|
|
addflag(lf->flags, fid, val[0], val[1], NA, NULL);
|
|
}
|
|
}
|
|
} // end if pctchance
|
|
} // end if v0 not f_mutable
|
|
} // end foreach f_eatconfer flag
|
|
} // end if lf has f_mutable
|
|
} // end if !tainted
|
|
|
|
// special cases for object types
|
|
if (o->type->id == OT_BANANA) {
|
|
object_t *skin;
|
|
skin = addobfast(lf->pack, OT_BANANASKIN);
|
|
if (skin) {
|
|
if (isplayer(lf)) {
|
|
char skinname[BUFLEN];
|
|
getobname(skin, skinname, 1);
|
|
msgnocap("%c - %s", skin->letter, skinname);
|
|
}
|
|
} else {
|
|
skin = addobfast(lf->cell->obpile, OT_BANANASKIN);
|
|
if (skin && cansee(player, lf)) {
|
|
char skinname[BUFLEN];
|
|
getobname(skin, skinname, 1);
|
|
msg("%s drop%s %s on the ground.",lfname, isplayer(lf) ? "" : "s",
|
|
skinname);
|
|
}
|
|
}
|
|
} else if (o->type->id == OT_CARROT) {
|
|
killtransitoryflags(lf->flags, F_BLIND);
|
|
addtempflag(lf->flags, F_SEEINDARK, 3, NA, NA, NULL, rnd(20,40));
|
|
} else if (o->type->id == OT_POISONSAC) {
|
|
// very bad!
|
|
poison(lf, rnd(10,20), P_VENOM, 4, "eating a venom sac", R_NONE);
|
|
}
|
|
|
|
if (isplayer(lf)) makeknown(o->type->id);
|
|
|
|
if (isplayer(lf)) {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_CORPSEOF);
|
|
if (f) {
|
|
race_t *r;
|
|
r = findrace(f->val[0]);
|
|
if (r && (r->raceclass->id == RC_ANIMAL)) {
|
|
if (gethungerlevel(gethungerval(lf)) > H_NONE) {
|
|
pleasegodmaybe(R_GODNATURE, 5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end if fullyeaten
|
|
|
|
// take time
|
|
amt = getactspeed(lf);
|
|
if (o->pile->owner != lf) {
|
|
amt += SPEED_PICKUP;
|
|
}
|
|
// humanoids can use knives and forks to eat faster.
|
|
if (getraceclass(lf) == RC_HUMANOID) {
|
|
int pct = 100;
|
|
if (hasob(lf->pack, OT_FORK)) {
|
|
pct -= 25;
|
|
}
|
|
if (hasob(lf->pack, OT_KNIFE) || hasob(lf->pack, OT_STEAKKNIFE)) {
|
|
pct -= 25;
|
|
}
|
|
amt = pctof(pct, amt);
|
|
}
|
|
limit(&amt, SP_ULTRAFAST, NA);
|
|
taketime(lf, amt);
|
|
|
|
// special cases even if not fully eaten
|
|
if (hasflagval(o->flags, F_CORPSEOF, R_DOGBLINK, NA, NA, NULL)) {
|
|
// blink!
|
|
dospelleffects(lf, OT_S_BLINK, 1, lf, NULL, NULL, B_UNCURSED, NULL, B_TRUE, NULL);
|
|
|
|
stopeating = B_TRUE;
|
|
} else if (hasflagval(o->flags, F_CORPSEOF, R_BLASTBUG, NA, NA, NULL)) {
|
|
dospelleffects(lf, OT_S_DETONATE, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, NULL);
|
|
stopeating = B_TRUE;
|
|
}
|
|
// you can gain F_MUTABLE even if you don't fully eat the corpse.
|
|
f = hasflagval(o->flags, F_EATCONFER, F_MUTABLE, NA, NA, NULL);
|
|
if (f) {
|
|
if (!lfhasflag(lf, F_MUTABLE)) addflag(lf->flags, F_MUTABLE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
|
|
|
|
// god effects
|
|
if (isplayer(lf)) {
|
|
// eating your pet is very bad!
|
|
if (hasflagval(o->flags, F_PETOF, player->id, NA, NA, NULL)) {
|
|
angergodmaybe(R_GODPURITY, 150, GA_EAT);
|
|
stopeating = B_TRUE;
|
|
}
|
|
// cannibulism ?
|
|
if (corpserace && (corpserace->id == lf->race->baseid)) {
|
|
angergodmaybe(R_GODPURITY, 150, GA_EAT);
|
|
angergodmaybe(R_GODNATURE, 100, GA_EAT);
|
|
stopeating = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (lfhasflagval(lf, F_FATALFOOD, o->type->id, NA, NA, NULL)) {
|
|
char buf[BUFLEN];
|
|
// die.
|
|
lf->hp = 0;
|
|
sprintf(buf, "eating %s", obname);
|
|
setlastdam(lf, buf);
|
|
stopeating = B_TRUE;
|
|
}
|
|
|
|
|
|
// stop eating if we are full
|
|
if (!stopeating && !fullyeaten && (posthlev != hlev) && (posthlev <= H_FULL)) {
|
|
if (isplayer(lf) && (posthlev != H_STUFFED)) {
|
|
int ch;
|
|
more();
|
|
ch = askchar("Stop eating?","yn","y", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
stopeating = B_TRUE;
|
|
}
|
|
} else {
|
|
stopeating = B_TRUE;
|
|
}
|
|
}
|
|
if (stopeating) {
|
|
killflagsofid(lf->flags, F_EATING);
|
|
}
|
|
|
|
if (fullyeaten) {
|
|
// special cases only when eaten
|
|
switch (o->type->id) {
|
|
case OT_BREADGARLIC:
|
|
addtempflag(lf->flags, F_STENCH, 2, NA, NA, NULL, rnd(30,50));
|
|
gainhp(lf,rnd(6,12));
|
|
break;
|
|
case OT_CAKEFRUIT:
|
|
setstamina(lf, getmaxstamina(lf));
|
|
gainhp(lf, lf->maxhp);
|
|
gainmp(lf, getmaxmp(lf));
|
|
break;
|
|
case OT_CHOCOLATE:
|
|
setstamina(lf, getmaxstamina(lf));
|
|
break;
|
|
case OT_CURADOUGH:
|
|
addtempflag(lf->flags, F_SLOWMETAB, 3, NA, NA, NULL, rnd(100,200));
|
|
gainhp(lf, lf->maxhp);
|
|
break;
|
|
case OT_PSITRUFFLE:
|
|
addtempflag(lf->flags, F_LEARNBOOST, 100, NA, NA, NULL, 100);
|
|
break;
|
|
case OT_HOTDOG:
|
|
addtempflag(lf->flags, F_ATTRMOD, A_STR, 3, NA, NULL, rnd(30,50));
|
|
gainhp(lf,rnd(6,12));
|
|
break;
|
|
case OT_MUSHROOMSTUFFED:
|
|
lf->maxhp++;
|
|
break;
|
|
case OT_RUMBALL:
|
|
killflagsofid(lf->flags, F_PAIN);
|
|
gainhp(lf, rnd(5,10));
|
|
break;
|
|
case OT_SANDWICHCHEESE:
|
|
setstamina(lf, getmaxstamina(lf));
|
|
gainhp(lf,rnd(1,5));
|
|
break;
|
|
case OT_SANDWICHPB:
|
|
setstamina(lf, getmaxstamina(lf));
|
|
addtempflag(lf->flags, F_ATTRMOD, A_CON, 3, NA, NULL, rnd(30,50));
|
|
gainhp(lf,rnd(13,18));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// remove object
|
|
removeob(o, 1);
|
|
} else {
|
|
// mark how much we ate
|
|
f = hasflag(o->flags, F_EDIBLE);
|
|
if (f) {
|
|
f->val[2] = (int)(startpcteaten + pcteaten);
|
|
}
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
drawstatus();
|
|
wrefresh(statwin);
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
// end of turn effects
|
|
void endlfturn(lifeform_t *lf) {
|
|
// lf will complain if in pain
|
|
if (islowhp(lf) && onein(3) && !hasflag(lf->flags, F_ASLEEP)) {
|
|
// TODO: replace 4
|
|
if (ispetof(lf, player)) {
|
|
if (!canhear(player, lf->cell, 4)) {
|
|
char realname[BUFLEN];
|
|
real_getlfname(lf, realname, NULL, B_NOSHOWALL, B_REALRACE);
|
|
warn("You feel worried about %s%s.", lfhasflag(lf, F_NAME) ? "" : "your ", noprefix(realname));
|
|
} else if (cantalk(lf)) {
|
|
sayphrase(lf, SP_ALLY_INPAIN, SV_SHOUT, NA, NULL);
|
|
} 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;
|
|
enum SKILLLEVEL wantlev;
|
|
|
|
// enhance lower level skills first, and only up to PR_ADEPT.
|
|
for (wantlev = PR_NOVICE; wantlev <= PR_BEGINNER; wantlev++) {
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
if ((f->id == F_HASSKILL) && !ismaxedskill(lf, f->val[0]) && (f->val[1] == wantlev)) {
|
|
if (isplayer(lf) && (f->val[2] != B_TRUE)) {
|
|
// for player - select only from skills which we have used since last levelup.
|
|
} else {
|
|
poss[nposs] = f->val[0];
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
if (nposs > 0) {
|
|
sel = rnd(0,nposs-1);
|
|
giveskill(lf, poss[sel]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void enhanceskills(lifeform_t *lf) {
|
|
enum SKILL whichsk;
|
|
flag_t *f;
|
|
skill_t *sk;
|
|
char ch = 'a';
|
|
int newskillcost = 1;
|
|
float hpratio,mpratio;
|
|
enum SKILLLEVEL slev;
|
|
int gainedxplev = B_FALSE;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
if (lf->newlevel != lf->level) {
|
|
lf->level = lf->newlevel;
|
|
gainedxplev = B_TRUE;
|
|
}
|
|
|
|
if (gainedxplev) {
|
|
// special cases which happen before doing hp/mp
|
|
if (hasjob(lf, J_MONK) && (lf->level == 2) && !lfhasflag(lf, F_NOSPELLS)) {
|
|
if (!lfhasflag(lf, F_MPDICE)) {
|
|
addflag(lf->flags, F_MPDICE, 1, 0, NA, NULL);
|
|
}
|
|
}
|
|
|
|
// update hp
|
|
hpratio = ((float)lf->hp / (float)lf->maxhp);
|
|
lf->maxhp += rollhitdice(lf, isplayer(lf) ? B_FALSE : B_TRUE);
|
|
lf->maxhp += (getskill(lf, SK_FIRSTAID)*2);
|
|
|
|
|
|
lf->hp = hpratio * (float)lf->maxhp;
|
|
|
|
// update mp
|
|
if (lfhasflag(lf, F_MPDICE)) {
|
|
if (lf->maxmp == 0) {
|
|
mpratio = 1;
|
|
} else {
|
|
mpratio = ((float)lf->mp / (float)lf->maxmp);
|
|
}
|
|
lf->maxmp += rollmpdice(lf, B_FALSE);
|
|
lf->mp = mpratio * (float)lf->maxmp;
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
wrefresh(statwin);
|
|
msg("^GWelcome to level %d!",lf->level);
|
|
}
|
|
|
|
// enhance a random skill every 2 levels (ie. 3/5/7/etc)
|
|
if (((lf->level + 1) % 2) == 0) {
|
|
enhancerandomskill(lf);
|
|
}
|
|
}
|
|
|
|
|
|
// increase str/int etc if we can
|
|
f = lfhasflag(lf, F_STATGAINREADY);
|
|
while (f && (f->val[2] > 0)) {
|
|
enum ATTRIB att = A_NONE;
|
|
enum ATTRIB a,poss[MAXATTS];
|
|
int nposs = 0;
|
|
for (a = 0; a < MAXATTS; a++) {
|
|
if (!ismaxedattr(lf, a) && attrincreasable(a)) {
|
|
poss[nposs++] = a;
|
|
}
|
|
}
|
|
|
|
if (nposs == 1) {
|
|
att = poss[0];
|
|
} else if (nposs) {
|
|
if (isplayer(lf)) {
|
|
char ch,ques[BUFLEN],answers[BUFLEN];
|
|
int i;
|
|
more();
|
|
strcpy(answers, "");
|
|
|
|
for (i = 0; i < nposs; i++) {
|
|
char this[2];
|
|
sprintf(this, "%c", getattrletter(poss[i]));
|
|
if (isalpha(this[0])) {
|
|
if (i == 0) {
|
|
sprintf(ques, "Increase your %s", getattrname(poss[i]));
|
|
} else if (i == (nposs-1)) {
|
|
strcat(ques, "or ");
|
|
strcat(ques, getattrname(poss[i]));
|
|
strcat(ques, "?");
|
|
} else {
|
|
strcat(ques, ", ");
|
|
strcat(ques, getattrname(poss[i]));
|
|
}
|
|
|
|
strcat(answers, this);
|
|
}
|
|
}
|
|
|
|
ch = askchar(ques, answers,NULL, B_TRUE, B_FALSE);
|
|
switch (ch) {
|
|
case 's': att = A_STR; break;
|
|
case 'a': att = A_AGI; break;
|
|
case 'f': att = A_CON; break;
|
|
case 'i': att = A_IQ; break;
|
|
case 'w': att = A_WIS; break;
|
|
}
|
|
} else {
|
|
// pick randomly
|
|
att = poss[rnd(0,nposs-1)];
|
|
}
|
|
} else {
|
|
// none possible
|
|
if (isplayer(lf)) {
|
|
more();
|
|
msg("All your attributes are already maxed.");
|
|
}
|
|
}
|
|
|
|
if (att != A_NONE) {
|
|
modattr(lf, att, 5);
|
|
}
|
|
|
|
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, B_FALSE);
|
|
} else {
|
|
snprintf(buf, BUFLEN,"Learn a new skill (%d points left)?",lf->skillpoints);
|
|
ch = askchar(buf,"yn","y", B_TRUE, B_FALSE);
|
|
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, B_FALSE);
|
|
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,i;
|
|
snprintf(ques, BUFLEN, "Enhance which skill (%d points left)?", lf->skillpoints);
|
|
snprintf(ques2, BUFLEN, "Describe which skill?");
|
|
initprompt(&prompt, ques);
|
|
addpromptq(&prompt, ques2);
|
|
|
|
ch = 'a';
|
|
getflags(lf->flags, retflag, &nretflags, F_HASSKILL, F_NONE);
|
|
for (i = 0;i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (!ismaxedskill(lf, f->val[0])) {
|
|
int cost;
|
|
cost = getskilllevcost(f->val[1] + 1);
|
|
if (lf->skillpoints >= cost) {
|
|
char buf[BUFLEN];
|
|
char buf2[HUGEBUFLEN];
|
|
snprintf(buf, BUFLEN, "%s -> %s (cost:%d points)", getskillname(f->val[0]),
|
|
getskilllevelname(f->val[1] + 1), cost);
|
|
makedesc_skill(f->val[0], buf2, f->val[1]+1);
|
|
addchoice(&prompt, ch++, getskillname(f->val[0]), buf, f, buf2);
|
|
}
|
|
}
|
|
}
|
|
addchoice(&prompt, '-', "None", "None", NULL, NULL);
|
|
|
|
while (!done) {
|
|
getchoicestr(&prompt, B_FALSE, B_TRUE);
|
|
f = (flag_t *)prompt.result;
|
|
if (f) {
|
|
whichsk = f->val[0];
|
|
|
|
if (prompt.whichq == 0) {
|
|
lf->skillpoints -= getskilllevcost(f->val[1]+1);
|
|
giveskill(lf, whichsk);
|
|
done = B_TRUE;
|
|
} else { // ie. describing a skill
|
|
describeskill(whichsk, f->val[1]+1);
|
|
}
|
|
} else {
|
|
done = B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
msg("You have already mastered all your current skills.");
|
|
}
|
|
} else if (eorl == 'l') {
|
|
// learn a new skill
|
|
|
|
// enough points?
|
|
if (player->skillpoints < newskillcost) {
|
|
msg("You need at least %d skill points to learn a new skill.", newskillcost);
|
|
} else {
|
|
if (skillstolearn) {
|
|
int done = B_FALSE;
|
|
char ques[BUFLEN],ques2[BUFLEN];
|
|
snprintf(ques, BUFLEN, "Learn which new skill (%d points left)?", player->skillpoints);
|
|
snprintf(ques2, BUFLEN, "Describe which skill?");
|
|
initprompt(&prompt, ques);
|
|
addpromptq(&prompt, ques2);
|
|
|
|
ch = 'a';
|
|
for (sk = firstskill ; sk ; sk = sk->next) {
|
|
if (!getskill(player, sk->id) && canlearn(player, sk->id)) {
|
|
char buf[BUFLEN];
|
|
char buf2[HUGEBUFLEN];
|
|
snprintf(buf, BUFLEN, "%-18s(%s)", getskillname(sk->id), getskilldesc(sk->id));
|
|
makedesc_skill(sk->id, buf2, PR_NOVICE);
|
|
addchoice(&prompt, ch++, getskillname(sk->id), buf, sk, buf2);
|
|
}
|
|
}
|
|
addchoice(&prompt, '-', "None", "None", NULL, NULL);
|
|
while (!done) {
|
|
getchoicestr(&prompt, B_FALSE, B_TRUE);
|
|
sk = (skill_t *)prompt.result;
|
|
if (sk) {
|
|
if (prompt.whichq == 0) {
|
|
giveskill(player, sk->id);
|
|
player->skillpoints -= newskillcost;
|
|
done = B_TRUE;
|
|
} else {
|
|
describeskill(sk->id, PR_NOVICE);
|
|
}
|
|
} else {
|
|
done = B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
msg("There is nothing more that you can learn.");
|
|
}
|
|
}
|
|
} else if (eorl == '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) {
|
|
flag_t *abilflag[MAXCANDIDATES];
|
|
int nabilflags = 0;
|
|
int origborn,i;
|
|
origborn = lf->born;
|
|
// already had this power with different options? remove it.
|
|
getflags(lf->flags, abilflag, &nabilflags, F_CANWILL, F_NONE);
|
|
for (i = 0;i < nabilflags; i++) {
|
|
if ((abilflag[i]->val[0] == f->val[1]) && (abilflag[i]->lifetime == FROMJOB)) {
|
|
lf->born = B_FALSE; // stop flag loss from being announced
|
|
killflag(abilflag[i]);
|
|
lf->born = origborn;
|
|
}
|
|
}
|
|
// now add the new one
|
|
addtempflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text, FROMJOB);
|
|
|
|
} else if (f->id == F_LEVFLAG) {
|
|
addtempflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text, FROMJOB);
|
|
} else if (f->id == F_LEVSKILL) {
|
|
giveskill(lf, f->val[1]);
|
|
} else if ((f->id == F_LEVSPELL) && !lfhasflag(lf, F_NOSPELLS)) {
|
|
addtempflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL, FROMJOB);
|
|
} else if ((f->id == F_LEVSPELLSCHOOL) && !lfhasflag(lf, F_NOSPELLS)) { // select a spell from school
|
|
if (isplayer(lf)) {
|
|
select_new_spell(f->val[1]);
|
|
} else {
|
|
// monster gets random spell
|
|
makespellchoicelist(&prompt, lf, "xx","xx:", f->val[1], B_TRUE, B_FALSE, B_FALSE, lf->maxmp);
|
|
if (prompt.nchoices > 0) {
|
|
objecttype_t *ot;
|
|
// pick one randomly
|
|
ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data;
|
|
if (ot) {
|
|
addtempflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL, FROMJOB);
|
|
}
|
|
}
|
|
}
|
|
} else if ((f->id == F_LEVSPELLSCHOOLFROMX) && !lfhasflag(lf, F_NOSPELLS)) { // select from X spells from given school
|
|
int nleft,highestlev = -1,n,i;
|
|
enum SPELLSCHOOL wantschool;
|
|
int possidx[MAXCANDIDATES],nposs;
|
|
char qbuf[BUFLEN];
|
|
objecttype_t *ot;
|
|
|
|
wantschool = f->val[1];
|
|
nleft = f->val[2];
|
|
// get all possible spells to learn, from the given school
|
|
sprintf(qbuf, "Learn which new spell (maxmp=%d):", getmaxmp(player));
|
|
makespellchoicelist(&prompt, player, qbuf, "Describe which spell:", f->val[1], B_TRUE, B_FALSE, B_FALSE, player->maxmp);
|
|
// find highest possible level, and mark all choices as invalid.
|
|
for (i = 0; i < prompt.nchoices; i++) {
|
|
int thislev;
|
|
if (prompt.choice[i].heading) continue;
|
|
|
|
ot = (objecttype_t *)(prompt.choice[i].data);
|
|
thislev = getspelllevel(ot->id);
|
|
if (thislev > highestlev) highestlev = thislev;
|
|
prompt.choice[i].valid = B_FALSE;
|
|
}
|
|
// select 1 from the highest possible level, and mark it ok
|
|
nposs = 0;
|
|
for (i = 0; i < prompt.nchoices; i++) {
|
|
int thislev;
|
|
if (prompt.choice[i].heading) continue;
|
|
|
|
ot = (objecttype_t *)(prompt.choice[i].data);
|
|
thislev = getspelllevel(ot->id);
|
|
if (thislev == highestlev) {
|
|
possidx[nposs++] = i;
|
|
}
|
|
}
|
|
i = possidx[rnd(0,nposs-1)];
|
|
prompt.choice[i].valid = B_TRUE;
|
|
nleft--;
|
|
|
|
// TODO: select nleft-1 random other ones.
|
|
for (n = 0; n < nleft; n++) {
|
|
nposs = 0;
|
|
for (i = 0; i < prompt.nchoices; i++) {
|
|
if (!prompt.choice[i].valid && !prompt.choice[i].heading) {
|
|
possidx[nposs++] = i;
|
|
}
|
|
}
|
|
if (!nposs) {
|
|
break;
|
|
}
|
|
i = possidx[rnd(0,nposs-1)];
|
|
prompt.choice[i].valid = B_TRUE;
|
|
}
|
|
// now let player select one, or pick randomly if a mosnter
|
|
if (isplayer(lf)) {
|
|
int done = B_FALSE;
|
|
while (!done) {
|
|
if (prompt.nchoices > 0) {
|
|
objecttype_t *ot;
|
|
getchoicestr(&prompt, B_TRUE, B_TRUE);
|
|
ot = prompt.result;
|
|
if (ot) {
|
|
if (prompt.whichq == 0) { // learn the spell
|
|
addtempflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL, FROMJOB);
|
|
done = B_TRUE;
|
|
} else {
|
|
describespell(ot);
|
|
}
|
|
}
|
|
} else {
|
|
msg("There are no new spells for you to learn at this time.");
|
|
done = B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
if (prompt.nchoices > 0) {
|
|
// pick randomly
|
|
ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data;
|
|
if (ot) {
|
|
addtempflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL, FROMJOB);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
f->lifetime = LEVABILITYDONE; // mark as done.
|
|
|
|
// get next one
|
|
f = levelabilityready(lf);
|
|
}
|
|
// now refresh them all for next level.
|
|
refreshlevelabilities(lf);
|
|
|
|
// special case level-based job effects
|
|
if (hasjob(lf, J_MONK)) {
|
|
// enhance fist strength and change type
|
|
//f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL);
|
|
f = lfhasflag(lf, F_HASATTACK);
|
|
if (f) {
|
|
int newdr;
|
|
newdr = lf->level+2;
|
|
limit(&newdr, NA, 12);
|
|
if (newdr <= f->val[1]) {
|
|
// do nothing - your unarmed attack was already stronger or
|
|
// equivilant.
|
|
} else if (newdr > f->val[1]) {
|
|
f->val[1] = newdr;
|
|
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!");
|
|
}
|
|
}
|
|
}
|
|
|
|
// allomancy sometimes lets you learn spells
|
|
if (!lfhasflag(lf, F_NOSPELLS)) {
|
|
slev = getskill(lf, SK_SS_ALLOMANCY);
|
|
if (pctchance(slev*20)) {
|
|
char qbuf[BUFLEN];
|
|
sprintf(qbuf, "Learn which allomantic ability (maxmp=%d):", getmaxmp(player));
|
|
// construct list of castable mental spells
|
|
makespellchoicelist(&prompt, lf, qbuf, "Describe which allomantic ability:", SS_ALLOMANCY, B_TRUE, B_FALSE, B_FALSE, player->maxmp);
|
|
if (prompt.nchoices > 0) {
|
|
objecttype_t *ot;
|
|
msg("Your body has attuned itself to a new allomantic ability!"); more();
|
|
getchoicestr(&prompt, B_TRUE, B_TRUE);
|
|
ot = prompt.result;
|
|
if (ot) {
|
|
if (prompt.whichq == 0) { // learn the spell
|
|
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
|
|
} else {
|
|
describespell(ot);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
// psionics sometimes lets you learn spells
|
|
slev = getskill(lf, SK_SS_MENTAL);
|
|
if (pctchance(slev*20)) {
|
|
char qbuf[BUFLEN];
|
|
sprintf(qbuf, "Learn which psionic power (maxmp=%d):", getmaxmp(player));
|
|
// construct list of castable mental spells
|
|
makespellchoicelist(&prompt, lf, qbuf, "Describe which psionic power:", SS_MENTAL, B_TRUE, B_FALSE, B_FALSE, player->maxmp);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
} // end if !hasflag nospells
|
|
|
|
killflagsofid(lf->flags, F_HASNEWLEVEL);
|
|
// ready for another level?
|
|
if (lf->xp >= getxpforlev(lf->level + 1)) {
|
|
gainlevel(lf, B_FALSE); // this will increment 'newlevel'
|
|
}
|
|
|
|
killwarningtext(TEXT_WARN_ATTACK_NOXP);
|
|
} // end if gainedxplev
|
|
}
|
|
|
|
int exchangeweapon(lifeform_t *lf) {
|
|
object_t *wep,*sec = NULL,*newsec = NULL;
|
|
// get secondary (if available)
|
|
sec = hasobwithflag(lf->pack, F_SECONDARY);
|
|
|
|
// get current weapon
|
|
wep = getweapon(lf);
|
|
// try to unweild current weapon
|
|
if (wep) {
|
|
if (unweild(lf, wep)) {
|
|
// error
|
|
return B_TRUE;
|
|
} else {
|
|
// success
|
|
addflag(wep->flags, F_SECONDARY, B_TRUE, NA, NA, NULL);
|
|
newsec = wep;
|
|
}
|
|
}
|
|
|
|
|
|
if (sec) {
|
|
// try to weild secondary
|
|
if (weild(lf, sec)) {
|
|
// error
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
// success!
|
|
if (newsec) {
|
|
char obname[BUFLEN];
|
|
getobname(newsec, obname, 1);
|
|
msg("Spare weapon: %c - %s", newsec->letter, obname);
|
|
} else {
|
|
msg("Spare weapon: (none)");
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void extinguishlf(lifeform_t *lf) {
|
|
object_t *o,*nexto;
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
extinguish(o);
|
|
}
|
|
killflagsofid(lf->flags, F_ONFIRE);
|
|
}
|
|
|
|
// if the lf wearing something which shades their eyes?
|
|
object_t *eyesshaded(lifeform_t *lf) {
|
|
object_t *glasses;
|
|
glasses = getarmour(lf, BP_EYES);
|
|
if (glasses && hasflag(glasses->flags, F_TINTED)) {
|
|
return glasses;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int fall(lifeform_t *lf, lifeform_t *fromlf, int announce) {
|
|
char lfname[BUFLEN];
|
|
|
|
if (isdead(lf)) return B_TRUE;
|
|
if (isprone(lf)) return B_TRUE;
|
|
if (!isairborne(lf)) {
|
|
if ((lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET))) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
if (lfhasflag(lf, F_GRAVLESSENED)) return B_TRUE;
|
|
|
|
getlfname(lf,lfname);
|
|
|
|
if (announce) {
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
if (fromlf) {
|
|
char fromlfname[BUFLEN];
|
|
getlfname(fromlf, fromlfname);
|
|
msg("^w%s knock%s %s to the ground!",fromlfname, isplayer(fromlf) ? "" : "s", lfname);
|
|
} else {
|
|
msg("^w%s fall%s to the ground.",lfname, isplayer(lf) ? "" : "s");
|
|
}
|
|
}
|
|
}
|
|
|
|
taketime(lf, SP_NORMAL);
|
|
|
|
addflag(lf->flags, F_PRONE, B_TRUE, NA, NA, NULL);
|
|
loseconcentration(lf);
|
|
breakgrabs(lf, B_TRUE, B_TRUE);
|
|
if (isvulnto(lf->flags, DT_FALL, B_FALSE)) {
|
|
// 0 will be repplaced with the dtvuln flag
|
|
losehp(lf, 0, DT_FALL, fromlf, "a bad fall");
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
// if you are going to sleep on purpose, use 'gotosleep'.
|
|
// this function is for when it is forced upon you by a spell, etc.
|
|
int fallasleep(lifeform_t *lf, enum SLEEPTYPE how, int howlong) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_ASLEEP);
|
|
if (f) {
|
|
if (f->val[1] == how) {
|
|
return B_TRUE;
|
|
} else {
|
|
killflag(f);
|
|
}
|
|
}
|
|
|
|
if ((how == ST_ASLEEP) && 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);
|
|
breakgrabs(lf, B_TRUE, B_FALSE);
|
|
|
|
// falling asleep while flying = fall!
|
|
fall_from_air(lf);
|
|
|
|
killflagsofid(lf->flags, F_RAGE);
|
|
killflagsofid(lf->flags, F_TRAINING);
|
|
|
|
addtempflag(lf->flags, F_ASLEEP, B_TRUE, how, NA, NULL, howlong);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// returns true if you fell
|
|
int fall_from_air(lifeform_t *lf) {
|
|
int willfall = B_FALSE,willinjure = B_FALSE, n;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags = 0;
|
|
getflags(lf->flags, retflag, &nretflags, F_FLYING, F_LEVITATING, F_NONE);
|
|
for (n = 0; n < nretflags; n++) {
|
|
if (!istransitoryflag(retflag[n]) || (retflag[n]->lifetime == FROMRACE)) {
|
|
if (retflag[n]->id == F_FLYING) willinjure = B_TRUE;
|
|
willfall = B_TRUE;
|
|
killflag(retflag[n]);
|
|
}
|
|
}
|
|
if (willfall) fall(lf, NULL, B_TRUE);
|
|
if (willinjure) injure(lf, getrandomcorebp(lf, NULL), DT_BASH, IJ_NONE);
|
|
if (willfall || willinjure) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// make 'lf' respond to damage
|
|
void fightback(lifeform_t *lf, lifeform_t *attacker) {
|
|
if (isdead(lf)) return;
|
|
|
|
if (lfhasflag(lf, F_FEIGNINGDEATH) || lfhasflagval(lf, F_ASLEEP, NA, ST_KO, NA, NULL)) {
|
|
// don't respond.
|
|
return;
|
|
}
|
|
interrupt(lf);
|
|
|
|
// special cases
|
|
if ((lf->race->id == R_STIRGE) || (lf->race->id == R_LEECH)) {
|
|
if (ispeaceful(lf)) {
|
|
// rest our sated counter
|
|
killflagsofid(lf->flags, F_COUNTER);
|
|
}
|
|
}
|
|
|
|
if (attacker) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_ASLEEP);
|
|
if (f && (f->val[1] != ST_KO)) {
|
|
// wake up
|
|
killflagsofid(lf->flags, F_ASLEEP);
|
|
}
|
|
|
|
f = lfhasflagval(lf, F_FEIGNFOOLEDBY, attacker->id, NA, NA, NULL);
|
|
if (f) {
|
|
killflag(f);
|
|
}
|
|
|
|
// monsters might flee, fight back, etc
|
|
if (!isplayer(lf)) {
|
|
if (isplayer(attacker) && ishirable(lf)) {
|
|
// can never recruit this person now!
|
|
addflag(lf->flags, F_NOHIRE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
|
|
// induction of fear?
|
|
if (lfhasflag(attacker, F_INDUCEFEAR) && cansee(lf, attacker)) {
|
|
scare(lf, attacker, rnd(2,3), 0);
|
|
} else if (mightflee(lf)) {
|
|
scare(lf, attacker, PERMENANT, 0);
|
|
} else {
|
|
lifeform_t *l;
|
|
|
|
// special case for player's pets/allies...
|
|
if (areallies(lf, attacker) && (getallegiance(lf) == AL_FRIENDLY)) {
|
|
// only fight back if it was the PLAYER who attacked us
|
|
if (!isplayer(attacker)) return;
|
|
}
|
|
|
|
// turn to face our attacker
|
|
if (!lfhasflag(lf, F_STUNNED) && !isdead(lf)) {
|
|
if (!isfleeing(lf)) {
|
|
//if (isadjacent(lf->cell, attacker->cell)) {
|
|
turntoface(lf, attacker->cell);
|
|
//}
|
|
aiattack(lf, attacker, aigetchasetime(lf));
|
|
}
|
|
}
|
|
|
|
// any nearby monsters which will help out?
|
|
if (getallegiance(lf) != AL_FRIENDLY) {
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if (!isdead(l) && areallies(l,lf)) {
|
|
if (cansee(l, attacker)) {
|
|
aiattack(l, attacker, aigetchasetime(l));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// special cases (which will only happen if not retreating)
|
|
}
|
|
// special cases (which will happen whether retreating or not)
|
|
if (lf->race->id == R_DARKMANTLE) {
|
|
cell_t *poss[MAXCANDIDATES];
|
|
int i, nposs = 0;
|
|
// darkjump
|
|
for (i = 0; i < lf->nlos; i++) {
|
|
if (lf->los[i] != lf->cell) {
|
|
if (cellwalkable(lf, lf->los[i], NULL) && isdark(lf->los[i])) {
|
|
poss[nposs] = lf->los[i];
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
if (nposs) {
|
|
//teleportto(lf, poss[rnd(0,nposs-1)], B_FALSE); // no smoke
|
|
abilityeffects(lf, OT_A_DARKWALK, poss[rnd(0,nposs-1)], NULL, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
behaviour_t *findbehaviour(enum BEHAVIOUR bid) {
|
|
behaviour_t *b;
|
|
for (b = firstbehaviour ; b ; b = b->next) {
|
|
if (b->id == bid) return b;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
job_t *findjob(enum JOB jobid) {
|
|
job_t *j;
|
|
for (j = firstjob ; j ; j = j->next) {
|
|
if (j->id == jobid) return j;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
job_t *findjobbyname(char *name) {
|
|
job_t *j;
|
|
for (j = firstjob ; j ; j = j->next) {
|
|
if (!strcasecmp(j->name, name)) return j;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
lifeform_t *findlf(map_t *m, int lfid) {
|
|
lifeform_t *lf;
|
|
map_t *thismap;
|
|
if (m) {
|
|
for (lf = m->lf ; lf ; lf = lf->next) {
|
|
if (lf->id == lfid) return lf;
|
|
}
|
|
} else {
|
|
for (thismap = firstmap ; thismap ; thismap = thismap->next) {
|
|
for (lf = thismap->lf ; lf ; lf = lf->next) {
|
|
if (lf->id == lfid) return lf;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
lifeform_t *findlfunique(enum RACE rid) {
|
|
lifeform_t *lf;
|
|
map_t *thismap;
|
|
for (thismap = firstmap ; thismap ; thismap = thismap->next) {
|
|
for (lf = thismap->lf ; lf ; lf = lf->next) {
|
|
if (lf->race->id == rid) return lf;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// if maxdist is NA, use the distance from the flag
|
|
// if maxdist is UNLIMITED, search the entire map
|
|
cell_t *findnearbylifeob(cell_t *src, int maxdist, flag_t *lifeobflag, object_t **retlifeob) {
|
|
object_t *corpse = NULL;
|
|
|
|
if (maxdist == NA) {
|
|
maxdist = lifeobflag->val[1];
|
|
} else if (maxdist == UNLIMITED) {
|
|
maxdist = MAXOF(src->map->w, src->map->h);
|
|
}
|
|
|
|
if (retlifeob) {
|
|
*retlifeob = NULL;
|
|
}
|
|
|
|
if (strlen(lifeobflag->text)) {
|
|
long corpseid;
|
|
// find the corpse
|
|
corpseid = atol(lifeobflag->text);
|
|
corpse = findobidinmap(src->map, corpseid);
|
|
if (getcelldist(src, corpse->pile->where) > maxdist) {
|
|
corpse = NULL;
|
|
}
|
|
} else {
|
|
// find closest location with corpse...
|
|
cell_t *retcell[MAX_MAPW*MAX_MAPH];
|
|
int nretcells,i;
|
|
enum OBTYPE oid;
|
|
int mindist = 9999;
|
|
|
|
oid = lifeobflag->val[0];
|
|
getradiuscells(src, maxdist, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_TRUE, retcell, &nretcells, 0);
|
|
for (i = 0; i < nretcells; i++) {
|
|
object_t *o;
|
|
cell_t *c;
|
|
int thisdist;
|
|
c = retcell[i];
|
|
o = hasob(c->obpile, oid);
|
|
if (o) {
|
|
thisdist = getcelldist(src, c);
|
|
if (thisdist < mindist) {
|
|
mindist = thisdist;
|
|
corpse = o;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (corpse) {
|
|
if (retlifeob) {
|
|
*retlifeob = corpse;
|
|
}
|
|
return corpse->pile->where;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
poisontype_t *findpoisontype(enum POISONTYPE id) {
|
|
poisontype_t *pt;
|
|
for (pt = firstpoisontype; pt ; pt = pt->next) {
|
|
if (pt->id == id) {
|
|
return pt;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
race_t *findrace(enum RACE id) {
|
|
race_t *r;
|
|
for (r = firstrace; r ; r = r->next) {
|
|
if (r->id == id) {
|
|
return r;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
race_t *findracebyname(char *name) {
|
|
race_t *r;
|
|
raceclass_t *rc;
|
|
char searchfor[BUFLEN];
|
|
// first check for exact matches
|
|
for (r = firstrace; r ; r = r->next) {
|
|
if (!strcmp(r->name, name)) {
|
|
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 start of words in name
|
|
// ie. "ant" should match "soldier ant" before matching "giant"
|
|
sprintf(searchfor, " %s",name);
|
|
for (r = firstrace; r ; r = r->next) {
|
|
if (strstr(r->name, searchfor)) {
|
|
return r;
|
|
}
|
|
}
|
|
|
|
// ...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;
|
|
}
|
|
|
|
subjob_t *findsubjob(enum SUBJOB sjid) {
|
|
subjob_t *j;
|
|
for (j = firstsubjob ; j ; j = j->next) {
|
|
if (j->id == sjid) return j;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// try to actually do the 'run away' action for
|
|
// anyone we are fleeing from.
|
|
// returns TRUE if we ran away from something
|
|
int flee(lifeform_t *lf) {
|
|
flag_t *f;
|
|
lifeform_t *fleefrom = NULL;
|
|
int i;
|
|
int db = B_FALSE;
|
|
char lfname[BUFLEN];
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) db = B_TRUE;
|
|
|
|
real_getlfname(lf, lfname, NULL, B_NOSHOWALL, B_CURRACE);
|
|
|
|
if (isdead(lf)) return B_FALSE;
|
|
|
|
// are we fleeing?
|
|
getflags(lf->flags, retflag, &nretflags, F_FLEEFROM, F_NONE);
|
|
|
|
// mindless?
|
|
if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) == IQ_MINDLESS) {
|
|
if (!nretflags) return B_FALSE;
|
|
}
|
|
|
|
// now determine who to flee from.
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_FLEEFROM) {
|
|
lifeform_t *thisone;
|
|
thisone = findlf(lf->cell->map, f->val[0]);
|
|
if (thisone) {
|
|
// TODO: this really means that the monster is cheating
|
|
if (haslof(lf->cell, thisone->cell, LOF_WALLSTOP, NULL)) {
|
|
// if not fleeing from anyone, or this one is closer...
|
|
if (!fleefrom || getcelldist(lf->cell, thisone->cell) < getcelldist(lf->cell, fleefrom->cell)) {
|
|
fleefrom = thisone;
|
|
}
|
|
} else {
|
|
// if we don't have LOF to the person we're running from, and it's not enforced for
|
|
// a certain time period (ie. f->lifetime == PERMENANT), we can now stop fleeing.
|
|
if (f->lifetime == PERMENANT) {
|
|
// player let something flee?
|
|
if (isplayer(thisone) && !haslos(thisone, lf->cell)) { // purposely not using cansee
|
|
pleasegodmaybe(R_GODMERCY, 5);
|
|
//if ((lf->lastdamlf == player->id) || cansee(player, lf)) {
|
|
if (lf->lastdamlf == player->id) {
|
|
// ie. only if the player saw them run away, or has already
|
|
// attacked them.
|
|
angergodmaybe(R_GODDEATH, 10, GA_MERCY);
|
|
}
|
|
}
|
|
killflag(f);
|
|
} else {
|
|
// if the flag is temporary, keep fleeing and wait for it to time out normally
|
|
fleefrom = thisone;
|
|
}
|
|
}
|
|
} else {
|
|
killflag(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// found someone who we are fleeing from?
|
|
if (fleefrom) {
|
|
object_t *stairs;
|
|
|
|
if (db) dblog("%s - fleeing from %s", lfname, fleefrom->race->name);
|
|
|
|
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, NULL, NULL)) {
|
|
if (db) dblog("%s - success.", lfname);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
// if AI, use helpful fleeing items
|
|
if (!useitemwithflag(lf, F_AIFLEEITEM)) {
|
|
if (db) dblog("%s - used an item to flee", lfname);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// announce
|
|
if (isplayer(lf)) {
|
|
char buf[BUFLEN];
|
|
drawscreen();
|
|
getlfname(fleefrom, buf);
|
|
msg("^wYou flee from %s!",buf);
|
|
}
|
|
|
|
// can we flee via stairs?
|
|
stairs = hasobwithflag(lf->cell->obpile, F_CLIMBABLE);
|
|
if (stairs && !lfhasflag(lf, F_NOSTAIRS)) {
|
|
if (db) dblog("%s - trying to flee via %s", lfname, stairs->type->name);
|
|
if (!usestairs(lf, stairs, B_TRUE, B_TRUE)) {
|
|
// success
|
|
if (isplayer(fleefrom)) {
|
|
pleasegodmaybe(R_GODMERCY, 5);
|
|
if ((lf->lastdamlf == player->id) || cansee(player, lf)) {
|
|
// ie. only if the player saw them run away, or has already
|
|
// attacked them.
|
|
angergodmaybe(R_GODDEATH, 10, GA_MERCY);
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
if (db) dblog("%s - failed to flee via %s", lfname, stairs->type->name);
|
|
}
|
|
// move away from them
|
|
if (!moveawayfrom(lf, fleefrom->cell, DT_ORTH, B_FALSE, B_FALSE, isplayer(lf) ? B_NOTONPURPOSE : B_ONPURPOSE)) {
|
|
if (db) dblog("%s - fleeing by moving away", lfname);
|
|
return B_TRUE;
|
|
}
|
|
if (db) dblog("%s - failed to flee!", lfname);
|
|
}
|
|
|
|
// if we get here, it means we didn't need to or couldn't flee
|
|
return B_FALSE;
|
|
}
|
|
|
|
// start fleeing from 'enemy'
|
|
void fleefrom(lifeform_t *lf, lifeform_t *enemy, int howlong, int onpurpose) {
|
|
flag_t *f;
|
|
|
|
if (lf == enemy) return;
|
|
|
|
if (!onpurpose) {
|
|
// in recovery from fleeing?
|
|
// this is to prevent constant usage of war cry!
|
|
f = hasflagval(lf->flags, F_NOFLEEFROM, enemy->id, NA, NA, NULL);
|
|
if (f) {
|
|
if (f->lifetime > 0) {
|
|
f->lifetime -= 5;
|
|
if (f->lifetime <= 0) {
|
|
killflag(f);
|
|
}
|
|
}
|
|
if (isplayer(lf)) {
|
|
msg("You flinch.");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s flinches.",lfname);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// already fleeing?
|
|
f = hasflagval(lf->flags, F_FLEEFROM, enemy->id, NA, NA, NULL);
|
|
if (f) {
|
|
// just update time
|
|
if (f->lifetime != PERMENANT) {
|
|
if (f->lifetime < howlong) {
|
|
f->lifetime = howlong;
|
|
}
|
|
}
|
|
} else {
|
|
addtempflag(lf->flags, F_FLEEFROM, enemy->id, NA, NA, NULL, howlong);
|
|
}
|
|
|
|
// stop targetting anyone or going anywhere
|
|
loseaitargets(lf);
|
|
|
|
// if no morale left, become timid for twice the flee time
|
|
if (!getmorale(lf)) {
|
|
addtempflag(lf->flags, F_TIMID, NA, NA, NA, NULL, (howlong == PERMENANT) ? howlong : (howlong*2));
|
|
}
|
|
}
|
|
|
|
int freezelf(lifeform_t *freezee, lifeform_t *freezer, int howlong) {
|
|
if (isimmuneto(freezee->flags, DT_COLD, B_FALSE)) {
|
|
if (isplayer(freezee)) {
|
|
msg("You feel a slight chill.");
|
|
}
|
|
return B_TRUE;
|
|
} else if (isresistantto(freezee->flags, DT_COLD, B_FALSE)) {
|
|
char buf[BUFLEN];
|
|
if (isplayer(freezee)) {
|
|
msg("^bYou feel freezing cold!");
|
|
}
|
|
if (freezer) {
|
|
char lfname[BUFLEN];
|
|
getlfname(freezer, lfname);
|
|
snprintf(buf, BUFLEN, "being frozen by %s",lfname);
|
|
} else {
|
|
strcpy(buf, "being frozen");
|
|
}
|
|
// note: damage value here will be halved due to resistance
|
|
losehp(freezee, rnd(howlong,howlong*2), DT_COLD, freezer, buf);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!lfhasflag(freezee, F_FROZEN)) {
|
|
// turn to ice
|
|
addtempflag(freezee->flags, F_FROZEN, B_TRUE, NA, NA, NULL, howlong);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void gainhp(lifeform_t *lf, int amt) {
|
|
int gained = B_FALSE;
|
|
int maxed = B_FALSE;
|
|
|
|
if (lf->hp < lf->maxhp) {
|
|
lf->hp += amt;
|
|
gained = B_TRUE;
|
|
}
|
|
|
|
if (lf->hp >= lf->maxhp) {
|
|
lf->hp = lf->maxhp;
|
|
if (gained) maxed = B_TRUE;
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
if (gained) {
|
|
player->damlastturn = 0;
|
|
statdirty = B_TRUE;
|
|
}
|
|
if (maxed) {
|
|
msg("^gYou are now fully healed.");
|
|
}
|
|
// update screen
|
|
drawstatus();
|
|
updatestatus();
|
|
}
|
|
}
|
|
|
|
void gainlevel(lifeform_t *lf, int autotrain) {
|
|
flag_t *f;
|
|
race_t *mybaserace;
|
|
//int skillready = B_FALSE;
|
|
//int preready,postready;
|
|
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
wrefresh(statwin);
|
|
}
|
|
|
|
//preready = readytotrain(lf);
|
|
|
|
addflag(lf->flags, F_HASNEWLEVEL, B_TRUE, NA, NA, NULL);
|
|
|
|
lf->newlevel++;
|
|
|
|
// stat gain (str etc) every level
|
|
/*
|
|
if ((lf->newlevel % 3) == 0) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_STATGAINREADY);
|
|
if (f) {
|
|
// TODO: should never happen now.
|
|
f->val[2]++;
|
|
} else {
|
|
f = addflag(lf->flags, F_STATGAINREADY, NA, NA, 1, NULL);
|
|
}
|
|
}
|
|
*/
|
|
f = lfhasflag(lf, F_STATGAINREADY);
|
|
if (f) {
|
|
// TODO: should never happen now.
|
|
f->val[2]++;
|
|
} else {
|
|
f = addflag(lf->flags, F_STATGAINREADY, NA, NA, 1, NULL);
|
|
}
|
|
|
|
// auto skill gain for monsters
|
|
if (!isplayer(lf)) {
|
|
lf->skillpoints++;
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
if (!autotrain) {
|
|
msg("^GYou are ready to train a new experience level!");
|
|
more();
|
|
}
|
|
} else if (cansee(player, lf)) {
|
|
//getlfname(lf, buf);
|
|
//msg("%s looks more confident!",buf);
|
|
}
|
|
|
|
|
|
// you can now re-attempt identification of objects
|
|
killflagsofid(lf->flags, F_FAILEDINSPECT);
|
|
|
|
// monster races can be promoted...
|
|
if (lf->race->baseid) {
|
|
mybaserace = findrace(lf->race->baseid);
|
|
} else {
|
|
mybaserace = lf->race;
|
|
}
|
|
f = hasflagval(mybaserace->flags, F_LEVRACE, lf->newlevel, NA, NA, NULL);
|
|
if (f && (lf->race->id != f->val[1])) {
|
|
// promotion!
|
|
setrace(lf, f->val[1], B_FALSE);
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
needredraw = B_TRUE;
|
|
statdirty = B_TRUE;
|
|
drawscreen();
|
|
}
|
|
|
|
if (autotrain) {
|
|
enhanceskills(lf);
|
|
}
|
|
}
|
|
|
|
|
|
void gainmp(lifeform_t *lf, int amt) {
|
|
int gained = B_FALSE;
|
|
int maxed = B_FALSE;
|
|
int max;
|
|
|
|
max = getmaxmp(lf);
|
|
|
|
// magic resistance means you can't regenerate mana!
|
|
if (skillcheck(lf, SC_RESISTMAG, 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) {
|
|
player->mplastturn = 0;
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
updatestatus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void gainxp(lifeform_t *lf, long amt) {
|
|
int newskillpoints = 0;
|
|
int doxp = B_TRUE;
|
|
int boostamt = 0;
|
|
|
|
assert(amt >= 0);
|
|
|
|
// adjust for xp boosts...
|
|
sumflags(lf->flags, F_LEARNBOOST, &boostamt, NULL, NULL);
|
|
amt = pctof(100+boostamt, amt);
|
|
|
|
if (lfhasflag(lf, F_HASNEWLEVEL) || (lf->level == 0)) {
|
|
doxp = B_FALSE;
|
|
}
|
|
|
|
if (doxp) {
|
|
lf->xp += amt;
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
assert(lf->xp >= 0);
|
|
|
|
// skill xp
|
|
if (isplayer(lf)) {
|
|
long amtneeded;
|
|
|
|
amtneeded = getspforpoint(lf);
|
|
assert(amtneeded > 0);
|
|
|
|
/*
|
|
// would you gain more than 5 levels? probably a bug!
|
|
if (((lf->skillxp + amt) / amtneeded) >= 3) {
|
|
raise(SIGINT);
|
|
}
|
|
*/
|
|
lf->skillxp += amt;
|
|
|
|
assert(lf->skillxp >= 0);
|
|
while (lf->skillxp >= amtneeded) {
|
|
newskillpoints++;
|
|
lf->skillxp -= amtneeded;
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
}
|
|
// debug!
|
|
if (newskillpoints >= 3) {
|
|
raise(SIGINT);
|
|
}
|
|
}
|
|
|
|
// ready for next level? can only go up ONE level.
|
|
if (lf->xp >= getxpforlev(lf->level + 1)) {
|
|
gainlevel(lf, B_FALSE); // this will increment 'newlevel'
|
|
}
|
|
if (newskillpoints) {
|
|
lf->skillpoints += newskillpoints;
|
|
msg("^GYou feel ready to learn a new skill!");
|
|
lf->totskillpoints += newskillpoints;
|
|
}
|
|
}
|
|
}
|
|
|
|
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) && !lfhasflag(c->lf, F_DOESNTMOVE) && !lfhasflag(c->lf, F_NOSTAIRS)) {
|
|
int ok = B_FALSE;
|
|
if (areallies(lf, c->lf) && haslof(c->lf->cell, lf->cell, LOF_NEED, NULL)) {
|
|
// ally with a clear path to you (even if they can't see you, we assume
|
|
// that you would tell them where you are going)
|
|
ok = B_TRUE;
|
|
} else if (areenemies(lf, c->lf) && (getcelldist(c, lf->cell) == 1) && cansee(c->lf, lf)) {
|
|
// adjacent enemy who can see you
|
|
ok = B_TRUE;
|
|
}
|
|
|
|
if (ok) {
|
|
// if this was a pit, only flying things will follow
|
|
if (stairob && hasflag(stairob->flags, F_PIT)) {
|
|
if (!lfhasflag(c->lf, F_FLYING)) {
|
|
ok = B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (ok) { // still ok?
|
|
adjally[*nadjallies] = c->lf;
|
|
if (seen) {
|
|
if (areallies(lf, c->lf)) {
|
|
seen[*nadjallies] = B_TRUE;
|
|
} else {
|
|
seen[*nadjallies] = cansee(lf, c->lf);
|
|
}
|
|
}
|
|
(*nadjallies)++;
|
|
if (*nadjallies >= MAXFOLLOWLFS) return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
enum ALIGNMENT getalignment(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_ALIGNMENT);
|
|
if (!f) {
|
|
return AL_NONE;
|
|
}
|
|
return f->val[0];
|
|
}
|
|
|
|
// return skillcheck modifier for speech checks player vs lf
|
|
int getalignmod(lifeform_t *lf) {
|
|
enum ALIGNMENT al = AL_NONE;
|
|
int alignmod = 0;
|
|
al = getalignment(lf);
|
|
if ((al == AL_GOOD) || (al == AL_EVIL)) {
|
|
if (al == getalignment(player)) {
|
|
alignmod = 3;
|
|
}
|
|
}
|
|
return alignmod;
|
|
}
|
|
|
|
enum ALLEGIENCE getallegiance(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_CHARMEDBY);
|
|
if (f) {
|
|
lifeform_t *cb;
|
|
cb = findlf(NULL, f->val[0]);
|
|
if (cb) {
|
|
return getallegiance(cb);
|
|
} else {
|
|
killflag(f);
|
|
}
|
|
}
|
|
if (isplayer(lf) || isfriendly(lf)) {
|
|
return AL_FRIENDLY;
|
|
} else if (ispeaceful(lf)) {
|
|
return AL_PEACEFUL;
|
|
}
|
|
return AL_HOSTILE;
|
|
}
|
|
|
|
int getallouterarmour(lifeform_t *lf, object_t **ob, int *nobs) {
|
|
object_t *o;
|
|
enum BODYPART bp;
|
|
int i;
|
|
*nobs = 0;
|
|
for (i = 0; i < lf->race->nbodyparts; i++) {
|
|
bp = lf->race->bodypart[i].id;
|
|
o = getouterequippedob(lf, bp);
|
|
if (o) {
|
|
int n,found = B_FALSE;
|
|
// already got htis one?
|
|
for (n = 0; n < *nobs; n++) {
|
|
if (ob[n]->id == o->id) {
|
|
found = B_TRUE;
|
|
}
|
|
}
|
|
if (!found) {
|
|
ob[*nobs] = o;
|
|
(*nobs)++;
|
|
}
|
|
}
|
|
}
|
|
return *nobs;
|
|
}
|
|
|
|
object_t *getarmour(lifeform_t *lf, enum BODYPART bp) {
|
|
object_t *o;
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (isarmour(o) && isequippedon(o, bp)) {
|
|
return o;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int 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, enum BODYPART *hitbp, int *narms) {
|
|
object_t *o;
|
|
flag_t *f;
|
|
int ar = 0, i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
if (narms) {
|
|
(*narms) = 0;
|
|
}
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_ARBOOST, F_ARMOURRATING, F_MAGICARMOUR, F_PHALANX, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_ARBOOST) {
|
|
ar += f->val[0];
|
|
}
|
|
if (f->id == F_ARMOURRATING) {
|
|
ar += f->val[0];
|
|
/*
|
|
if (hitob) {
|
|
hitob[*narms] = NULL;
|
|
hitchance[*narms] = getbodyparthitchance(BP_BODY);
|
|
if (hitbp) hitbp[*narms] = BP_BODY;
|
|
(*narms)++;
|
|
}
|
|
*/
|
|
}
|
|
if (f->id == F_MAGICARMOUR) {
|
|
ar += f->val[0];
|
|
}
|
|
if (f->id == F_PHALANX) {
|
|
int dir;
|
|
cell_t *c;
|
|
int nmatched = 0;
|
|
// count adjacent allies of name xx
|
|
for (dir = DC_N; dir <= DC_NW; dir++) {
|
|
c = getcellindir(lf->cell, dir);
|
|
if (c && c->lf) {
|
|
if (strcasestr(c->lf->race->name, f->text)) {
|
|
nmatched++;
|
|
}
|
|
}
|
|
}
|
|
if (nmatched >= f->val[2]) {
|
|
ar += f->val[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
flag_t *eqflag;
|
|
// note: for obs equipped in multiples places, only the first is used.
|
|
eqflag = hasflag(o->flags, F_EQUIPPED);
|
|
if (eqflag) {
|
|
f = hasflag(o->flags, F_ARMOURRATING);
|
|
if (f) {
|
|
float thisar;
|
|
int isshield = B_FALSE;
|
|
|
|
thisar = f->val[0];
|
|
if (hasflag(o->flags, F_SHIELD)) {
|
|
isshield = B_TRUE;
|
|
}
|
|
|
|
// adjust for skill
|
|
if (isshield) {
|
|
switch (getskill(lf, SK_SHIELDS)) {
|
|
case PR_INEPT:
|
|
thisar *= 0.5;
|
|
break;
|
|
case PR_NOVICE:
|
|
break;
|
|
case PR_BEGINNER:
|
|
thisar *= 1.2;
|
|
break;
|
|
case PR_ADEPT:
|
|
thisar *= 1.4;
|
|
break;
|
|
case PR_SKILLED:
|
|
thisar *= 1.6;
|
|
break;
|
|
case PR_EXPERT:
|
|
thisar *= 1.8;
|
|
break;
|
|
case PR_MASTER:
|
|
thisar *= 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// adjust for condition
|
|
//thisar = pctof(getobhppct(o), thisar);
|
|
|
|
ar += thisar;
|
|
|
|
ar += getobbonus(o, B_FALSE);
|
|
if (hitob) {
|
|
hitob[*narms] = o;
|
|
hitchance[*narms] = getbodyparthitchance(isshield ? BP_BODY : eqflag->val[0]);
|
|
if (hitbp) hitbp[*narms] = isshield ? BP_BODY : eqflag->val[0];
|
|
(*narms)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
limit(&ar, 0, NA);
|
|
|
|
return ar;
|
|
}
|
|
|
|
// how far away should we be before attacking?
|
|
int getattackspeed(lifeform_t *lf) {
|
|
object_t *w;
|
|
float speed;
|
|
speed = getactspeed(lf);
|
|
|
|
w = getweapon(lf);
|
|
if (w) {
|
|
int del;
|
|
del = getobattackdelay(w);
|
|
speed = pctof(del, speed);
|
|
}
|
|
|
|
return (int)speed;
|
|
}
|
|
|
|
float getattackstamloss(lifeform_t *lf) {
|
|
object_t *w;
|
|
float loss;
|
|
loss = STAMTOATTACK;
|
|
|
|
w = getweapon(lf);
|
|
if (w) {
|
|
int del;
|
|
del = getobattackdelay(w);
|
|
loss = pctof(del, loss);
|
|
}
|
|
|
|
return loss;
|
|
}
|
|
|
|
|
|
int getattpoints(lifeform_t *lf) {
|
|
flag_t *f;
|
|
int attpoints = 0;
|
|
f = lfhasflag(lf, F_STATGAINREADY);
|
|
if (f) {
|
|
attpoints = f->val[2];
|
|
}
|
|
return attpoints;
|
|
}
|
|
|
|
int getattr(lifeform_t *lf, enum ATTRIB attr) {
|
|
return real_getattr(lf, attr, B_FALSE);
|
|
}
|
|
|
|
int real_getattr(lifeform_t *lf, enum ATTRIB attr, int ignoreattrset) {
|
|
int val = 0, i;
|
|
flag_t *f;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
// override?
|
|
if (!ignoreattrset) {
|
|
f = lfhasflagval(lf, F_ATTRSET, attr, NA, NA, NULL);
|
|
if (f) {
|
|
val = f->val[1];
|
|
} else {
|
|
val = lf->att[attr];
|
|
}
|
|
} else {
|
|
// base attribute
|
|
val = lf->att[attr];
|
|
}
|
|
|
|
// modified?
|
|
getflags(lf->flags, retflag, &nretflags, F_ATTRMOD, F_DRUNK, F_INJURY, F_RAGE, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if ((f->id == F_ATTRMOD) && (f->val[0] == attr)) {
|
|
val += f->val[1];
|
|
}
|
|
if (f->id == F_DRUNK) {
|
|
if (attr == A_AGI) {
|
|
if (hasjob(lf, J_PIRATE)) {
|
|
val += (f->val[0]*5);
|
|
} else {
|
|
val -= (f->val[0]*5);
|
|
}
|
|
} else if (attr == A_WIS) {
|
|
val -= (f->val[0]*5);
|
|
}
|
|
}
|
|
if (f->id == F_INJURY) {
|
|
if ((f->val[0] == IJ_NOSEBROKEN) && (attr == A_CHA)) {
|
|
val -= 20;
|
|
} else if ((f->val[0] == IJ_WINDPIPECRUSHED) && (attr == A_CON)) {
|
|
val -= 30;
|
|
}
|
|
}
|
|
if ((f->id == F_RAGE) && (attr == A_STR)) {
|
|
val += 25;
|
|
}
|
|
}
|
|
|
|
if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) {
|
|
// worshipping yumi increases wisdom based on piety
|
|
if ((attr == A_WIS) && godprayedto(R_GODMERCY)) {
|
|
enum PIETYLEV plev;
|
|
plev = getpietylev(R_GODMERCY, NULL, NULL);
|
|
switch (plev) {
|
|
case PL_ENRAGED: val -= 25; break;
|
|
case PL_FURIOUS: val -= 10; break;
|
|
case PL_ANGRY: val -= 5; break;
|
|
case PL_TOLERATED: break;
|
|
case PL_INDIFFERENT: val += 10; break;
|
|
case PL_PLEASED: val += 20 ; break;
|
|
case PL_DELIGHTED: val += 35 ; break;
|
|
case PL_ECSTATIC: val += 50 ; break;
|
|
default: break;
|
|
}
|
|
} else if ((attr == A_CON) && godprayedto(R_GODLIFE)) {
|
|
enum PIETYLEV plev;
|
|
plev = getpietylev(R_GODLIFE, NULL, NULL);
|
|
switch (plev) {
|
|
case PL_INDIFFERENT: val += 5; break;
|
|
case PL_PLEASED: val += 10 ; break;
|
|
case PL_DELIGHTED: val += 15 ; break;
|
|
case PL_ECSTATIC: val += 20 ; break;
|
|
default: break;
|
|
}
|
|
} else if ((attr == A_IQ) && godprayedto(R_GODMAGIC)) {
|
|
enum PIETYLEV plev;
|
|
plev = getpietylev(R_GODMAGIC, NULL, NULL);
|
|
switch (plev) {
|
|
case PL_ENRAGED: val -= 20; break;
|
|
case PL_FURIOUS: val -= 10; break;
|
|
case PL_ANGRY: val -= 5; break;
|
|
case PL_TOLERATED: break;
|
|
case PL_INDIFFERENT: val += 10; break;
|
|
case PL_PLEASED: val += 20 ; break;
|
|
case PL_DELIGHTED: val += 30 ; break;
|
|
case PL_ECSTATIC: val += 50 ; break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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(o, 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);
|
|
limitf(&acc, 0, 100);
|
|
thisavg = pctof(acc, thisavg);
|
|
|
|
avgdam += thisavg;
|
|
if (db) {
|
|
char obname[BUFLEN];
|
|
getobname(o,obname,1);
|
|
if (db) dblog("getavgdam: %s: == %d-%d dam, avg is %0.1f",obname, min, max, thisavg);
|
|
}
|
|
} else if (forxp && (f->id == F_STARTOB)) { // starting weapons...
|
|
objecttype_t *ot;
|
|
|
|
ot = findotn(f->text);
|
|
if (ot && (ot->obclass->id == OC_WEAPON)) {
|
|
obpile_t *op2;
|
|
object_t *o;
|
|
op2 = addobpile(NULL,NULL, NULL);
|
|
o = addob(op2, f->text);
|
|
if (o) {
|
|
int min,max;
|
|
float thisavg;
|
|
float acc;
|
|
flag_t *of;
|
|
|
|
getdamrange(o, NULL, &min,&max);
|
|
thisavg = ((float)min + (float)max) / 2.0;
|
|
|
|
// confers anything?
|
|
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;
|
|
}
|
|
|
|
getdamrange(w, NULL, &mindam, &maxdam);
|
|
|
|
mindam += bonus;
|
|
maxdam += bonus;
|
|
|
|
dammod = getstrdammod(lf);
|
|
// apply damage mod for strength
|
|
if (!hasflag(w->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) {
|
|
mindam += dammod;
|
|
maxdam += dammod;
|
|
}
|
|
|
|
if (mindam < 0) mindam = 0;
|
|
if (maxdam < 0) maxdam = 0;
|
|
|
|
thisavg = (((float)(mindam + maxdam)) / 2);
|
|
acc = getlfaccuracy(lf, w);
|
|
thisavg = pctof(acc, thisavg);
|
|
|
|
avgdam += thisavg;
|
|
}
|
|
}
|
|
|
|
if (op) killobpile(op);
|
|
|
|
return (int)avgdam;
|
|
}
|
|
|
|
enum CASTTYPE getcasttype(lifeform_t *lf, enum OBTYPE sid) {
|
|
flag_t *ctf;
|
|
ctf = lfhasflagval(lf, F_CASTTYPE, sid, NA, NA, NULL);
|
|
if (!ctf) {
|
|
ctf = lfhasflagval(lf, F_CASTTYPE, OT_NONE, NA, NA, NULL);
|
|
}
|
|
if (ctf) {
|
|
return ctf->val[1];
|
|
}
|
|
return CT_NORMAL;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// no evasion if you're holding someone, or someone is holding you
|
|
if (lfhasflag(lf, F_GRABBEDBY) || lfhasflag(lf, F_GRABBING)) {
|
|
return 0;
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// positive modifiers first
|
|
//////////////////////////////////////////////////
|
|
// get natural evasion, adjustments for bulky armour/shield
|
|
getflags(lf->flags, retflag, &nretflags, F_EVASION, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
ev += (retflag[i]->val[0]);
|
|
}
|
|
|
|
// level based evasion
|
|
level_ev = gettr(lf);
|
|
ev += level_ev;
|
|
|
|
// dexterity mod
|
|
ev += (getattr(lf, A_AGI)/5);
|
|
|
|
// apply skill based evasion modifier
|
|
skillpctmod = 100 + (getskill(lf, SK_EVASION) * 12);
|
|
ev = pctof(skillpctmod, ev);
|
|
|
|
// swimevasion creatures get extra evasion while swimming
|
|
f = lfhasflag(lf, F_SWIMEVASION);
|
|
if (f && isswimming(lf)) {
|
|
ev += f->val[0];
|
|
}
|
|
|
|
// flightevasion creatures get extra evasion while flying
|
|
f = lfhasflag(lf, F_FLIGHTEVASION);
|
|
if (f && (isairborne(lf) == F_FLYING)) {
|
|
ev += f->val[0];
|
|
}
|
|
|
|
//////////////////////////////////////////////////
|
|
// now negative modifiers
|
|
//////////////////////////////////////////////////
|
|
// get adjustments for bulky armour/shield
|
|
getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_SHIELDPENALTY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_ARMOURPENALTY) {
|
|
ev -= adjustarmourpenalty(lf, f->val[1]);
|
|
} else if (f->id == F_SHIELDPENALTY) {
|
|
ev -= adjustshieldpenalty(lf, f->val[1]);
|
|
}
|
|
}
|
|
|
|
// you are easier to hit if you're glowing
|
|
if (hasflag(lf->flags, F_PRODUCESLIGHT)) {
|
|
ev -= 5;
|
|
}
|
|
|
|
// swimevasion creatures get extra evasion while swimming
|
|
if (isswimming(lf) && lfhasflag(lf, F_SWIMEVASION) && !isaquatic(lf)) {
|
|
// evasion penalty based on swimming skill
|
|
switch (getskill(lf, SK_SWIMMING)) {
|
|
case PR_INEPT: ev -= 30; break;
|
|
case PR_NOVICE: ev -= 20; break;
|
|
case PR_BEGINNER: ev -= 10; break;
|
|
case PR_ADEPT: ev -= 5; break;
|
|
case PR_SKILLED: ev -= 0; break;
|
|
case PR_EXPERT:
|
|
case PR_MASTER:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// modify for stickiness
|
|
if (hasobwithflag(lf->cell->obpile, F_RESTRICTMOVEMENT)) {
|
|
ev -= 50;
|
|
}
|
|
|
|
// modify for blindness
|
|
// PLUS if you're blind, your evasion is 0 anyway for anyone
|
|
// attacking you.
|
|
if (isblind(lf)) {
|
|
ev -= 15;
|
|
}
|
|
|
|
limit(&ev, 0, NA);
|
|
|
|
return ev;
|
|
}
|
|
|
|
object_t *getbestthrowmissile(lifeform_t *lf, lifeform_t *target) {
|
|
object_t *bestwep = NULL;
|
|
int bestdam = -1;
|
|
object_t *o;
|
|
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
int ismissileob = B_FALSE;
|
|
if (!isequipped(o) && isthrowmissile(o) ) {
|
|
ismissileob = B_TRUE;
|
|
} else if (lfhasflagval(lf, F_WILLTHROW, o->type->id, NA, NA, NULL)) {
|
|
ismissileob = B_TRUE;
|
|
}
|
|
if (ismissileob) {
|
|
int valid = B_TRUE;
|
|
// powder is only a valid missile if we're adjacent to our target
|
|
if (target && hasflag(o->flags, F_POWDER)) {
|
|
if (getcelldist(lf->cell,target->cell) > 1) valid = B_FALSE;
|
|
}
|
|
|
|
if (valid) {
|
|
int thisdam;
|
|
// better than last one?
|
|
thisdam = getthrowdam(o) + getshatterdam(o);
|
|
if ((bestwep == NULL) || (thisdam > bestdam)) {
|
|
bestwep = o;
|
|
bestdam = thisdam;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return bestwep;
|
|
}
|
|
|
|
object_t *getbestfirearm(lifeform_t *lf) {
|
|
object_t *bestgun = NULL;
|
|
object_t *o;
|
|
int bestfirespeed = -1;
|
|
|
|
|
|
bestgun = NULL;
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
// if it is a gun and we can weild it...
|
|
if (isfirearm(o) && (isequipped(o) || canweild(lf, o))) {
|
|
int thisfirespeed;
|
|
thisfirespeed = getfirearmspeed(o);
|
|
if (thisfirespeed > bestfirespeed) {
|
|
bestgun = o;
|
|
bestfirespeed = thisfirespeed;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestgun;
|
|
}
|
|
|
|
|
|
object_t *getbestweapon(lifeform_t *lf) {
|
|
//obpile_t *op = NULL;
|
|
object_t *bestwep = NULL;
|
|
//int bestmaxdam = -999;
|
|
object_t *o;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
obpile_t *op = NULL;
|
|
|
|
op = addobpile(NULL, NULL, NULL);
|
|
|
|
bestwep = getweapon(lf);
|
|
if (!bestwep) {
|
|
int i;
|
|
// get best innate attack
|
|
getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
objecttype_t *ot;
|
|
|
|
ot = findot(retflag[i]->val[0]);
|
|
if (ot) {
|
|
o = addobfast(op, ot->id);
|
|
|
|
if (isweapon(o) && !isfirearm(o) && canweild(lf, o) && isbetterwepthan(o, bestwep, lf)) {
|
|
flag_t *damflag;
|
|
bestwep = o;
|
|
// inherit damage from hasattack flag
|
|
damflag = hasflag(bestwep->flags, F_DAM);
|
|
if (damflag) {
|
|
damflag->val[1] = retflag[i]->val[1];
|
|
}
|
|
} else {
|
|
killob(o);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
// if it does damage and we can weild it...
|
|
if (isweapon(o) && !isfirearm(o) && canweild(lf, o)) {
|
|
if (isbetterwepthan(o, bestwep, lf)) {
|
|
bestwep = o;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestwep && (bestwep->pile->owner == NULL)) {
|
|
// ie. best weapon is an innate attack
|
|
bestwep = NULL;
|
|
}
|
|
|
|
killobpile(op);
|
|
return bestwep;
|
|
}
|
|
|
|
|
|
int getbodyparthitchance(enum BODYPART bp) {
|
|
switch (bp) {
|
|
case BP_WEAPON: return 0;
|
|
case BP_SECWEAPON: return 0;
|
|
case BP_EYES: return 1;
|
|
case BP_HEAD: return 2;
|
|
case BP_HEAD2: return 2;
|
|
case BP_HEAD3: return 2;
|
|
case BP_WAIST: return 3;
|
|
case BP_HANDS: return 3;
|
|
case BP_FEET: return 3;
|
|
case BP_TAIL: return 3;
|
|
case BP_FRONTLEGS: return 4;
|
|
case BP_BACKLEGS: return 4;
|
|
case BP_LEGS: return 4;
|
|
case BP_SHOULDERS: return 4;
|
|
case BP_WINGS: return 4;
|
|
case BP_BODY: return 6;
|
|
|
|
case BP_EARS:
|
|
case BP_RIGHTFINGER:
|
|
case BP_LEFTFINGER:
|
|
case BP_NECK:
|
|
break;
|
|
}
|
|
return 0; // ie rings, ears, weapon
|
|
}
|
|
|
|
char *getbodypartname(lifeform_t *lf, enum BODYPART bp) {
|
|
if (lf) {
|
|
int i;
|
|
|
|
// does this bodypart have a special name?
|
|
for (i = 0; i < lf->race->nbodyparts; i++) {
|
|
if (lf->race->bodypart[i].id == bp) {
|
|
if (strlen(lf->race->bodypart[i].name)) {
|
|
return lf->race->bodypart[i].name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (bp) {
|
|
case BP_WEAPON:
|
|
return "right hand";
|
|
case BP_SECWEAPON:
|
|
return "left hand";
|
|
case BP_RIGHTFINGER:
|
|
return "right finger";
|
|
case BP_LEFTFINGER:
|
|
return "left finger";
|
|
case BP_HANDS:
|
|
return "hands";
|
|
case BP_EARS:
|
|
return "ears";
|
|
case BP_EYES:
|
|
return "eyes";
|
|
case BP_HEAD:
|
|
return "head";
|
|
case BP_HEAD2:
|
|
return "second head";
|
|
case BP_HEAD3:
|
|
return "third head";
|
|
case BP_NECK:
|
|
return "neck";
|
|
case BP_BODY:
|
|
return "body";
|
|
case BP_SHOULDERS:
|
|
return "shoulders";
|
|
case BP_WAIST:
|
|
return "waist";
|
|
case BP_FRONTLEGS:
|
|
return "front legs";
|
|
case BP_BACKLEGS:
|
|
return "back legs";
|
|
case BP_LEGS:
|
|
return "legs";
|
|
case BP_FEET:
|
|
return "feet";
|
|
case BP_TAIL:
|
|
return "tail";
|
|
case BP_WINGS:
|
|
return "wings";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
char *getbodypartequipname(enum BODYPART bp) {
|
|
switch (bp) {
|
|
case BP_WEAPON:
|
|
case BP_SECWEAPON:
|
|
case BP_EARS:
|
|
return "in"; // ie. 'in right hand'
|
|
case BP_RIGHTFINGER:
|
|
case BP_LEFTFINGER:
|
|
case BP_HANDS:
|
|
case BP_HEAD:
|
|
case BP_HEAD2:
|
|
case BP_HEAD3:
|
|
case BP_BODY:
|
|
case BP_FRONTLEGS:
|
|
case BP_BACKLEGS:
|
|
case BP_LEGS:
|
|
case BP_FEET:
|
|
case BP_WINGS:
|
|
case BP_TAIL:
|
|
return "on";
|
|
case BP_EYES:
|
|
case BP_SHOULDERS:
|
|
return "over";
|
|
case BP_WAIST:
|
|
case BP_NECK:
|
|
return "around";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
object_t *getequippedob(obpile_t *op, enum BODYPART bp) {
|
|
object_t *o;
|
|
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;
|
|
}
|
|
|
|
enum LOFTYPE getfirearmloftype(lifeform_t *lf) {
|
|
if (getskill(lf, SK_RANGED) >= PR_EXPERT) {
|
|
return LOF_WALLSTOP;
|
|
}
|
|
return LOF_NEED;
|
|
}
|
|
|
|
int getfootprinttime(lifeform_t *lf) {
|
|
int time;
|
|
|
|
time = TM_FOOTPRINT;
|
|
switch (getlfsize(lf)) {
|
|
case SZ_MINI: time = 1; break;
|
|
case SZ_TINY: time -= 15; break;
|
|
case SZ_SMALL: time -= 10; break;
|
|
default: break;
|
|
case SZ_LARGE: time += 10; break;
|
|
case SZ_HUGE: time += 20; break;
|
|
case SZ_ENORMOUS: time += 30; break;
|
|
}
|
|
|
|
return time;
|
|
}
|
|
|
|
enum GENDER getgender(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_GENDER);
|
|
if (f) {
|
|
return f->val[0];
|
|
}
|
|
return G_NONE;
|
|
}
|
|
|
|
lifeform_t *getguntarget(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = hasflag(lf->flags, F_GUNTARGET);
|
|
if (!f) {
|
|
return NULL;
|
|
}
|
|
return findlf(NULL, f->val[0]);
|
|
}
|
|
|
|
int getguntargetid(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = hasflag(lf->flags, F_GUNTARGET);
|
|
if (!f) {
|
|
return -1;
|
|
}
|
|
return f->val[0];
|
|
}
|
|
|
|
int gethearingrange(lifeform_t *lf) {
|
|
int range = 2; // default
|
|
|
|
if (!isasleep(lf)) {
|
|
// if awake, your listen skills helps
|
|
range += (getskill(lf, SK_LISTEN)*2);
|
|
}
|
|
|
|
return range;
|
|
}
|
|
|
|
int gettrrace(race_t *r) {
|
|
flag_t *f;
|
|
f = hasflag(r->flags, F_TR);
|
|
return f->val[0];
|
|
}
|
|
|
|
int gethitstokill(lifeform_t *lf, lifeform_t *victim, int useevasion, int usearmour) {
|
|
object_t *wep[MAXCANDIDATES];
|
|
flag_t *damflag[MAXCANDIDATES];
|
|
obpile_t *op = NULL;
|
|
int nweps = 0,hitstokill = 0;
|
|
getweapons(lf, B_MELEEONLY, wep, damflag, NULL, &op, &nweps);
|
|
if (nweps) {
|
|
int maxdam;
|
|
getdamrange(wep[0], damflag[0], NULL, &maxdam);
|
|
|
|
// modify by victim's evasion?
|
|
if (useevasion) {
|
|
float ev;
|
|
ev = ((float)getevasion(victim));
|
|
maxdam -= pctof(ev, maxdam);
|
|
}
|
|
|
|
// modify by victim's armour?
|
|
if (usearmour) {
|
|
int ar,aravg,amin,amax;
|
|
ar = getarmourrating(victim, NULL, NULL, NULL, NULL);
|
|
getarrange(ar, &amin, &amax);
|
|
aravg = (int)(((float)amin + (float)amax) / 2.0);
|
|
maxdam -= aravg;
|
|
}
|
|
|
|
if (maxdam >= 1) {
|
|
int nattacks;
|
|
hitstokill = victim->hp / maxdam;
|
|
limit(&hitstokill, 1, NA);
|
|
// modify by maxattacks
|
|
getattacks(lf, NULL, &nattacks);
|
|
hitstokill /= nattacks;
|
|
limit(&hitstokill, 1, NA);
|
|
} else {
|
|
hitstokill = 0; // ie you'll never kill it.
|
|
}
|
|
}
|
|
if (op) killobpile(op);
|
|
return hitstokill;
|
|
}
|
|
|
|
|
|
int gethppct(lifeform_t *lf) {
|
|
float pct;
|
|
pct = (int)(((float)lf->hp / (float)lf->maxhp) * 100);
|
|
return pct;
|
|
}
|
|
|
|
enum COLOUR gethungercol(enum HUNGER hlev) {
|
|
enum COLOUR col = C_GREY;
|
|
switch (hlev) {
|
|
case H_STUFFED: col = C_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;
|
|
}
|
|
|
|
enum SUBJOB getsubjob(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_JOB);
|
|
if (f && (f->val[1] != NA)) {
|
|
return f->val[1];
|
|
}
|
|
return SJ_NONE;
|
|
}
|
|
|
|
char *getjobname(lifeform_t *lf) {
|
|
enum SUBJOB sjid;
|
|
job_t *j;
|
|
subjob_t *sj;
|
|
sjid = getsubjob(lf);
|
|
sj = findsubjob(sjid);
|
|
if (sj) {
|
|
return sj->name;
|
|
}
|
|
|
|
j = getjob(lf);
|
|
if (j) {
|
|
return j->name;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
int getlastdir(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_LASTDIR);
|
|
if (f) {
|
|
return f->val[0];
|
|
}
|
|
return D_NONE;
|
|
}
|
|
|
|
int getleftrightwalls(lifeform_t *lf) {
|
|
cell_t *rightcell,*leftcell;
|
|
int rightdir,leftdir;
|
|
int walls = 0;
|
|
// remember walls to left and right
|
|
leftdir = lf->facing - 1;
|
|
if (leftdir < DC_N) leftdir = DC_NW;
|
|
rightdir = lf->facing + 1;
|
|
if (rightdir > DC_NW) rightdir = DC_N;
|
|
leftcell = getcellindir(lf->cell, leftdir);
|
|
rightcell = getcellindir(lf->cell, rightdir);
|
|
if (!leftcell || (leftcell && leftcell->type->solid)) {
|
|
walls += 1;
|
|
}
|
|
if (!rightcell || (rightcell && rightcell->type->solid)) {
|
|
walls += 2;
|
|
}
|
|
return walls;
|
|
}
|
|
|
|
int getlfaccuracy(lifeform_t *lf, object_t *wep) {
|
|
flag_t *f;
|
|
object_t *o;
|
|
int acc = 0,i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
int unarmed = B_FALSE;
|
|
|
|
// get weapon
|
|
if (wep) {
|
|
acc = getobaccuracy(wep, lf, B_FALSE);
|
|
if (hasflag(wep->flags, F_UNARMEDWEP)) {
|
|
unarmed = B_TRUE;
|
|
} else {
|
|
unarmed = B_FALSE;
|
|
}
|
|
} else {
|
|
unarmed = B_TRUE;
|
|
// for unarmed attacks, accuracy is based on agility
|
|
acc = getattr(lf, A_AGI) + 20;
|
|
limit(&acc, 20, 100);
|
|
}
|
|
|
|
// dual weilding?
|
|
if (isdualweilding(lf)) {
|
|
switch (getskill(lf, SK_TWOWEAPON)) {
|
|
case PR_INEPT: acc -= 100; break;
|
|
case PR_NOVICE: acc -= 20; break;
|
|
case PR_BEGINNER: acc -= 10; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
for (o = lf->pack->first ;o ; o = o->next) {
|
|
if (isequipped(o)) {
|
|
f = hasflag(o->flags, F_ACCURACYMOD);
|
|
if (f) acc += f->val[0];
|
|
}
|
|
}
|
|
|
|
// adjust for bulky armour/shield, or injuries
|
|
getflags(lf->flags, retflag, &nretflags, F_ACCURACYMOD, F_ARMOURPENALTY, F_INJURY, F_SHIELDPENALTY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_ACCURACYMOD) {
|
|
acc += f->val[0];
|
|
} else if (f->id == F_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) {
|
|
switch (f->val[0]) {
|
|
case IJ_FINGERBROKEN:
|
|
case IJ_TORSOBRUISED:
|
|
acc -= 10; break;
|
|
case IJ_EYELIDSCRAPED:
|
|
case IJ_TORSOBRUISEDBAD:
|
|
case IJ_SHOULDERDISLOCATED:
|
|
acc -= 20; break;
|
|
case IJ_RIBBROKEN:
|
|
case IJ_TAILBROKEN:
|
|
acc -= 30; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// modify for weilder's level
|
|
acc += (lf->level * 2);
|
|
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
// huge bonus
|
|
acc += 50;
|
|
}
|
|
/*
|
|
else {
|
|
int dexmod;
|
|
// modify with dexterity
|
|
dexmod = getstatmod(lf, A_AGI) / 2; // -25 - +25
|
|
// double dex penalties when dual weilding
|
|
if (isdualweilding(lf) && (dexmod < 0)) {
|
|
dexmod *= 2;
|
|
}
|
|
acc += dexmod;
|
|
}
|
|
*/
|
|
|
|
|
|
// modify for blindness
|
|
if (isblind(lf)) {
|
|
acc -= 50;
|
|
}
|
|
// modify for being on the ground
|
|
if (isprone(lf)) {
|
|
acc -= 40;
|
|
}
|
|
|
|
// day/night boosts
|
|
if (isnighttime()) {
|
|
f = lfhasflag(lf, F_NIGHTBOOST);
|
|
if (f) {
|
|
if (f->val[0] != NA) acc += f->val[0];
|
|
}
|
|
} else { // ie. daytime
|
|
f = lfhasflag(lf, F_DAYBOOST);
|
|
if (f) {
|
|
if (f->val[0] != NA) acc += f->val[0];
|
|
}
|
|
}
|
|
|
|
// modify for swimming
|
|
if (isswimming(lf) && !isaquatic(lf)) {
|
|
switch (getskill(lf, SK_SWIMMING)) {
|
|
// you can't attack until you are at
|
|
// expert or better.
|
|
default: acc = 0; break;
|
|
case PR_EXPERT: acc -= 20; break;
|
|
case PR_MASTER: break; // no penalty
|
|
}
|
|
}
|
|
|
|
// modify for drunkenness
|
|
f = lfhasflag(lf, F_DRUNK);
|
|
if (f) {
|
|
int amt;
|
|
int time;
|
|
time = f->lifetime;
|
|
if (time < 0) time = 70; // ie permenant
|
|
limit(&time, NA, 70);
|
|
amt = (time/TM_DRUNKTIME)+1;
|
|
if (hasjob(lf, J_PIRATE)) {
|
|
acc += (10*amt);
|
|
} else {
|
|
acc -= (10*amt);
|
|
}
|
|
}
|
|
|
|
// agi scaling on weapon
|
|
if (wep) {
|
|
getflags(wep->flags, retflag, &nretflags, F_ATTREQ, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[0] == A_AGI) {
|
|
int pctmod;
|
|
meetsattreq(lf, retflag[i], wep, &pctmod);
|
|
acc += pctmod;
|
|
}
|
|
}
|
|
}
|
|
|
|
// modify for nausea
|
|
if (lfhasflag(lf, F_NAUSEATED)) {
|
|
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;
|
|
}
|
|
|
|
int getlistendetectrange(lifeform_t *lf) {
|
|
switch (getskill(lf, SK_LISTEN)) {
|
|
default:
|
|
case PR_INEPT:
|
|
return 0;
|
|
case PR_NOVICE:
|
|
return 0;
|
|
case PR_BEGINNER:
|
|
return 1;
|
|
case PR_ADEPT:
|
|
return 2;
|
|
case PR_SKILLED:
|
|
return 3;
|
|
case PR_EXPERT:
|
|
return 4;
|
|
case PR_MASTER:
|
|
return 6;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// how much taller are you due to your flight?
|
|
int getflightsizemod(lifeform_t *lf) {
|
|
int howmuch = 0;
|
|
switch (isairborne(lf)) {
|
|
case F_FLYING: howmuch += 2; break;
|
|
case F_LEVITATING: howmuch++; break;
|
|
default: break;
|
|
}
|
|
return howmuch;
|
|
}
|
|
|
|
enum SKILLLEVEL getmaxskilllevel(lifeform_t *lf, enum SKILL skid) {
|
|
flag_t *f;
|
|
enum SKILLLEVEL maxlev = PR_MASTER;
|
|
f = lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL);
|
|
if (f) {
|
|
if (f->val[1] == NA) {
|
|
maxlev = PR_MASTER;
|
|
} else {
|
|
maxlev = f->val[1];
|
|
}
|
|
}
|
|
return maxlev;
|
|
}
|
|
|
|
int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions) {
|
|
flag_t *f;
|
|
lifeform_t *min;
|
|
int i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
*nminions = 0;
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_MINION, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_MINION) {
|
|
min = findlf(lf->cell->map, f->val[0]);
|
|
if (min) {
|
|
(minion[*nminions]) = min;
|
|
(*nminions)++;
|
|
}
|
|
}
|
|
}
|
|
return *nminions;
|
|
}
|
|
|
|
int getmiscastchance(lifeform_t *lf) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
int chance = 0;
|
|
|
|
sumflags(lf->flags, F_MISCASTCHANCE, &chance, NULL, NULL);
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_SHIELDPENALTY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
int (*adjustfunc)(lifeform_t *, float) = NULL;
|
|
if (retflag[i]->id == F_ARMOURPENALTY) {
|
|
adjustfunc = adjustarmourpenalty;
|
|
} else {
|
|
adjustfunc = adjustshieldpenalty;
|
|
}
|
|
if (retflag[i]->val[0] != NA) chance += adjustfunc(lf, retflag[i]->val[0]);
|
|
if (retflag[i]->val[1] != NA) chance += adjustfunc(lf, retflag[i]->val[1]);
|
|
}
|
|
return chance;
|
|
}
|
|
|
|
int getmorale(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = hasflag(lf->flags, F_MORALE);
|
|
if (f) return f->val[0];
|
|
// defaults to threat level
|
|
return gettr(lf);
|
|
}
|
|
|
|
int getnextshortcut(lifeform_t *lf) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i,min = 0;
|
|
getflags(lf->flags, retflag, &nretflags, F_SHORTCUT, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[0] > min) {
|
|
min = retflag[i]->val[0];
|
|
}
|
|
}
|
|
return min + 1;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// populates heartext, seetext and volume
|
|
// "lf" is optional. if not given, "noiseflag" should be provided.
|
|
// returns
|
|
int getnoisedetails(lifeform_t *lf, enum NOISETYPE nid, flag_t *noiseflag, char *heartext,char *seetext, int *volume) {
|
|
flag_t *retflag[MAXCANDIDATES],*nflag[MAXCANDIDATES];
|
|
int nretflags, i,nnflags = 0;
|
|
|
|
// deafults
|
|
if (volume) *volume = 0;
|
|
if (heartext) strcpy(heartext, "");
|
|
if (seetext) strcpy(seetext, "");
|
|
|
|
if (lf) {
|
|
if (lfhasflag(lf, F_FROZEN)) {
|
|
// can't make noise if frozen!
|
|
return B_TRUE;
|
|
}
|
|
|
|
if ((nid == N_WALK) || nid == (N_FLY)) {
|
|
if (!movecausesnoise(lf)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_NOISETEXT, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[0] == nid) {
|
|
nflag[nnflags++] = retflag[i];
|
|
}
|
|
}
|
|
|
|
if (nnflags) {
|
|
noiseflag = nflag[rnd(0,nnflags-1)];
|
|
}
|
|
}
|
|
|
|
if (noiseflag) {
|
|
char verb[BUFLEN], noun[BUFLEN];
|
|
if (volume) *volume = noiseflag->val[1];
|
|
|
|
if (noiseflag->text[0] == '^') {
|
|
strcpy(verb, "");
|
|
//noun = strtok_r(noiseflag->text, "^", &dummy);
|
|
strcpy(noun, noiseflag->text + 1);
|
|
} else {
|
|
char *p;
|
|
p = readuntil(verb, noiseflag->text, '^');
|
|
readuntil(noun, p, '^'); // ie eol
|
|
//verb = strtok_r(noiseflag->text, "^", &dummy);
|
|
//noun = strtok_r(NULL, "^", &dummy);
|
|
}
|
|
|
|
if (heartext) {
|
|
if (strlen(noun)) {
|
|
snprintf(heartext, BUFLEN, "%s.",noun);
|
|
}
|
|
}
|
|
if (seetext) {
|
|
if (strlen(verb)) {
|
|
strcpy(seetext, verb);
|
|
}
|
|
}
|
|
if (nid == N_WALK) {
|
|
if (lf) *volume += getarmournoise(lf);
|
|
}
|
|
return B_FALSE;
|
|
} else if (lf) {
|
|
// some defaults
|
|
if (nid == N_WALK) {
|
|
enum LFSIZE sz;
|
|
char movetext[BUFLEN];
|
|
strcpy(movetext, "");
|
|
sz = getlfsize(lf);
|
|
switch (sz) {
|
|
case SZ_MINI:
|
|
case SZ_TINY:
|
|
if (volume) *volume = 0;
|
|
break;
|
|
case SZ_SMALL:
|
|
if (volume) *volume = 1;
|
|
strcpy(movetext, "light footsteps.");
|
|
break;
|
|
case SZ_MEDIUM:
|
|
case SZ_HUMAN:
|
|
if (volume) *volume = 2;
|
|
strcpy(movetext, "footsteps.");
|
|
break;
|
|
case SZ_LARGE:
|
|
if (volume) *volume = 3;
|
|
strcpy(movetext, "heavy footsteps.");
|
|
break;
|
|
case SZ_HUGE:
|
|
if (volume) *volume = 4;
|
|
strcpy(movetext, "heavy footsteps.");
|
|
break;
|
|
case SZ_ENORMOUS:
|
|
if (volume) *volume = 5;
|
|
strcpy(movetext, "very heavy footsteps.");
|
|
break;
|
|
case SZ_MAX:
|
|
if (volume) *volume = 6;
|
|
strcpy(movetext, "extremely loud thumping.");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (strlen(movetext)) {
|
|
if (volume) *volume += getarmournoise(lf);
|
|
if (heartext) strcpy(heartext, movetext);
|
|
if (lfhasflag(lf, F_CAREFULMOVE)) {
|
|
(*volume)--;
|
|
limit(volume, 1, NA);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
} else if (nid == N_DEATHKEEN) {
|
|
if (volume) *volume = SV_TALK;
|
|
if (heartext) strcpy(heartext, "the dread wailing of death!");
|
|
if (seetext) strcpy(seetext, "wails with the power of death!");
|
|
return B_FALSE;
|
|
} else if (nid == N_SONICBOLT) {
|
|
if (volume) *volume = 5;
|
|
if (heartext) strcpy(heartext, "a ear-splitting burst of sound!");
|
|
if (seetext) strcpy(seetext, "emits an ear-splitting burst of sound!");
|
|
return B_FALSE;
|
|
} else if (nid == N_WARCRY) {
|
|
if (volume) *volume = 4;
|
|
if (heartext) strcpy(heartext, "a blood-curdling war cry!");
|
|
if (seetext) strcpy(seetext, "shouts a blood-curdling war-cry!");
|
|
return B_FALSE;
|
|
}
|
|
// failed!
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
char *getlfconditionname(enum LFCONDITION cond) {
|
|
switch (cond) {
|
|
case C_CRITICAL:
|
|
return "critically wounded";
|
|
case C_SERIOUS:
|
|
return "seriously wounded";
|
|
case C_WOUNDED:
|
|
return "wounded";
|
|
case C_HURT:
|
|
return "hurt";
|
|
case C_HEALTHY:
|
|
return "healthy";
|
|
case C_DEAD:
|
|
return "dead";
|
|
}
|
|
return "?unknown condition?";
|
|
}
|
|
|
|
object_t *getouterequippedob(lifeform_t *lf, enum BODYPART bp) {
|
|
object_t *o;
|
|
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);
|
|
}
|
|
|
|
int getsmellrange(lifeform_t *lf) {
|
|
flag_t *f;
|
|
int range = 0;
|
|
f = lfhasflag(lf, F_ENHANCESMELL);
|
|
if (f) {
|
|
range = f->val[0];
|
|
// adjust for injuries
|
|
if (lfhasflagval(lf, F_INJURY, IJ_NOSEBROKEN, NA, NA, NULL)) {
|
|
range /= 2;
|
|
}
|
|
}
|
|
return range;
|
|
}
|
|
|
|
flag_t *getspellcasttextflag(lifeform_t *lf, enum OBTYPE sid) {
|
|
flag_t *f;
|
|
flag_t *retflag[MAXCANDIDATES],*genposs[MAXCANDIDATES],*specposs[MAXCANDIDATES];
|
|
int nretflags,i,ngenposs = 0,nspecposs = 0;
|
|
getflags(lf->flags, retflag, &nretflags, F_SPELLCASTTEXT, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[0] == sid ) {
|
|
specposs[nspecposs++] = retflag[i];
|
|
} else if (retflag[i]->val[0] == OT_NONE) {
|
|
genposs[ngenposs++] = retflag[i];
|
|
}
|
|
}
|
|
if (nspecposs) {
|
|
f = specposs[rnd(0,nspecposs-1)];
|
|
} else if (ngenposs) {
|
|
f = genposs[rnd(0,ngenposs-1)];
|
|
} else {
|
|
f = NULL;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
glyph_t *getlfglyph(lifeform_t *lf) {
|
|
flag_t *f;
|
|
|
|
if (lfhasflag(lf, F_FEIGNINGDEATH)) {
|
|
// look like a corpse
|
|
tempglyph.ch = '%';
|
|
tempglyph.colour = lf->race->glyph.colour;
|
|
} else if ((f = lfhasflag(lf, F_GLYPH)) != NULL) {
|
|
tempglyph.ch = f->val[1];
|
|
tempglyph.colour = f->val[0];
|
|
} else if ((f = lfhasflag(lf, F_SIZETIMER)) != NULL) {
|
|
int newchar;
|
|
newchar = lf->race->glyph.ch;
|
|
// made larger?
|
|
if (getlfsize(lf) > f->val[0]) {
|
|
switch (lf->race->glyph.ch) {
|
|
case 'n':
|
|
case 'h':
|
|
case '@':
|
|
newchar = 'H'; break;
|
|
case 'g':
|
|
newchar = 'G'; break;
|
|
case 'i':
|
|
newchar = 'I'; break;
|
|
case 'q':
|
|
newchar = 'Q'; break;
|
|
case 'w':
|
|
newchar = 'W'; break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
tempglyph.ch = newchar;
|
|
tempglyph.colour = lf->race->glyph.colour;
|
|
} else {
|
|
tempglyph = lf->race->glyph;
|
|
}
|
|
|
|
if (lf->cell && (getcellwaterdepth(lf->cell, player) >= DP_WAIST)) {
|
|
object_t *o;
|
|
int bgcol = 0;
|
|
o = hasobwithflag(lf->cell->obpile, F_DEEPWATER);
|
|
switch (o->type->material->id) {
|
|
case MT_WATER: bgcol = BLUEBG; break;
|
|
case MT_SLIME: bgcol = GREENBG; break;
|
|
default: break;
|
|
}
|
|
if (bgcol) {
|
|
// avoid having the same foreground and background colour
|
|
int fixfg = B_FALSE;
|
|
if ((bgcol == BLUEBG) && (tempglyph.colour == C_BLUE)) {
|
|
fixfg = B_TRUE;
|
|
} else if ((bgcol == GREENBG) && (tempglyph.colour == C_GREEN)) {
|
|
fixfg = B_TRUE;
|
|
}
|
|
if (fixfg) tempglyph.colour = C_BLACK;
|
|
tempglyph.colour += bgcol;
|
|
}
|
|
}
|
|
|
|
//return &lf->race->glyph;
|
|
return &tempglyph;
|
|
}
|
|
|
|
enum MATERIAL getlfmaterial(lifeform_t *lf) {
|
|
if (lf->race->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;
|
|
if (gamemode < GM_GAMESTARTED) {
|
|
return PR_INEPT;
|
|
}
|
|
rc = findraceclass(rcid);
|
|
if (rc) {
|
|
slev = getskill(lf, rc->skill);
|
|
}
|
|
return slev;
|
|
}
|
|
|
|
int getattacks(lifeform_t *lf, int *min, int *max) {
|
|
flag_t *f;
|
|
int minattacks = 1,maxattacks = 1;
|
|
int nattacks;
|
|
f = lfhasflag(lf, F_MAXATTACKS);
|
|
if (f) {
|
|
minattacks = f->val[0];
|
|
maxattacks = f->val[1];
|
|
|
|
} else {
|
|
minattacks = countinnateattacks(lf);
|
|
maxattacks = countinnateattacks(lf);
|
|
}
|
|
|
|
// if we have high unarmed skill and our second hand is free,
|
|
// AND we only had 1 attack before, we get one more attack
|
|
if (maxattacks < 2) {
|
|
if (lfhasflag(lf, F_HUMANOID)) {
|
|
if (getskill(lf, SK_UNARMED) >= PR_SKILLED) {
|
|
if (hasbp(lf, BP_SECWEAPON) && !getequippedob(lf->pack, BP_SECWEAPON)) {
|
|
maxattacks++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (getskill(lf, SK_TWOWEAPON) && isdualweilding(lf)) {
|
|
minattacks++;
|
|
maxattacks++;
|
|
}
|
|
|
|
if (min) *min = minattacks;
|
|
if (max) *max = maxattacks;
|
|
nattacks = rnd(minattacks,maxattacks);
|
|
|
|
return nattacks;
|
|
}
|
|
|
|
float getmaxcarryweight(lifeform_t *lf) {
|
|
float max;
|
|
|
|
/*
|
|
sbrack = getattrbracket(getattr(lf, A_STR), A_STR, NULL);
|
|
switch (sbrack) {
|
|
case AT_EXLOW:
|
|
mod = 0.1; break;
|
|
case AT_VLOW:
|
|
mod = 0.25; break;
|
|
case AT_LOW:
|
|
mod = 0.5; break;
|
|
case AT_LTAVERAGE:
|
|
mod = 0.75; break;
|
|
case AT_AVERAGE:
|
|
mod = 1; break; // your body weight
|
|
case AT_GTAVERAGE:
|
|
mod = 1.25; break;
|
|
case AT_HIGH:
|
|
mod = 1.5; break;
|
|
case AT_VHIGH:
|
|
mod = 2; break; // twice your own body weight
|
|
case AT_EXHIGH:
|
|
mod = 2.25; break; // over twice
|
|
default:
|
|
mod = 1; break; // your body weight
|
|
}
|
|
|
|
max = getlfweight(lf, B_NOOBS) * mod;
|
|
*/
|
|
|
|
// half your weight + your strength
|
|
max = (getlfweight(lf, B_NOOBS)/2) + getattr(lf, A_STR);
|
|
|
|
if (lfhasflagval(lf, F_INJURY, IJ_RIBBROKEN, NA, NA, NULL)) {
|
|
max /= 2;
|
|
} else if (lfhasflagval(lf, F_INJURY, IJ_RIBCRACKED, NA, NA, NULL)) {
|
|
max /= 2;
|
|
}
|
|
limitf(&max, 0, NA);
|
|
|
|
return max;
|
|
}
|
|
|
|
int getmaxmp(lifeform_t *lf) {
|
|
flag_t *f;
|
|
int activemp = 0;
|
|
int maxmp,i;
|
|
int extrapct = 0;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
// base
|
|
maxmp = lf->maxmp;
|
|
|
|
// extras
|
|
sumflags(lf->flags, F_EXTRAMP, &extrapct, NULL, NULL);
|
|
maxmp += pctof(maxmp, extrapct);
|
|
|
|
// losses
|
|
activemp = 0;
|
|
getflags(lf->flags, retflag, &nretflags, F_BOOSTSPELL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_BOOSTSPELL) {
|
|
activemp += f->val[1];
|
|
}
|
|
}
|
|
maxmp -= activemp;
|
|
//if (maxmp < 0) maxmp = 0; // ????
|
|
|
|
return maxmp;
|
|
}
|
|
|
|
float getmaxpushweight(lifeform_t *lf) {
|
|
float max;
|
|
int pct;
|
|
pct = 100 + getstatmod(lf, A_STR);
|
|
max = getlfweight(lf, B_NOOBS); // your body weight
|
|
max = pctof(pct, max);
|
|
return max;
|
|
}
|
|
|
|
float getmaxstamina(lifeform_t *lf) {
|
|
int stam = 0;
|
|
int slev;
|
|
slev = getskill(lf, SK_ATHLETICS);
|
|
limit(&slev, 1, NA);
|
|
stam = (getattr(lf, A_CON) / 15) * (slev+1);
|
|
if (lfhasflagval(lf, F_INJURY, IJ_LUNGCOLLAPSED, NA, NA, NULL)) {
|
|
limit(&stam, NA, 2);
|
|
}
|
|
return stam;
|
|
}
|
|
|
|
object_t *getmeleeweapon(lifeform_t *lf) {
|
|
object_t *o;
|
|
o = getweapon(lf);
|
|
if (o && ismeleeweapon(o)) {
|
|
return o;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
int getmr(lifeform_t *lf) {
|
|
return real_getmr(lf, B_FALSE);
|
|
}
|
|
|
|
int real_getmr(lifeform_t *lf, int onlyexternal) {
|
|
int amt = 0;
|
|
|
|
if (onlyexternal) {
|
|
flag_t *f;
|
|
// sum all MR flags which DONT come from race/job
|
|
for (f = hasflag(lf->flags, F_RESISTMAG) ; f && (f->id == F_RESISTMAG) ; f = f->next) {
|
|
if ((f->lifetime != FROMRACE) && (f->lifetime != FROMJOB)) {
|
|
amt += f->val[0];
|
|
}
|
|
}
|
|
} else {
|
|
// sum all MR flags
|
|
sumflags(lf->flags, F_RESISTMAG, &amt, NULL, NULL);
|
|
// scourge gets job-based bonus
|
|
if (hassubjob(lf, SJ_SCOURGE)) {
|
|
amt += (gettr(lf) * 3);
|
|
}
|
|
}
|
|
|
|
|
|
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_HIDING, F_INJURY, F_SLOWMOVE,
|
|
F_SLOWACTMOVE, F_SPRINTING, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if ((f->id == F_SLOWMOVE) || (f->id == F_SLOWACTMOVE)) {
|
|
speed += f->val[0];
|
|
} else if ((f->id == F_FASTMOVE) || (f->id == F_FASTACTMOVE)) {
|
|
speed -= f->val[0];
|
|
} else if (f->id == F_HIDING) {
|
|
speed += 10;
|
|
} else if (f->id == F_INJURY) {
|
|
if (!isairborne(lf)) {
|
|
switch (f->val[0]) {
|
|
case IJ_LEGBROKEN:
|
|
case IJ_HAMSTRUNG:
|
|
speed += 10; break;
|
|
case IJ_LEGBRUISE:
|
|
speed += 5; break;
|
|
}
|
|
}
|
|
} else if (f->id == F_SPRINTING) {
|
|
if (!isairborne(lf)) {
|
|
speed -= 10;
|
|
}
|
|
}
|
|
}
|
|
|
|
// flying in low gravity?
|
|
f = lfhasflag(lf, F_FLYING);
|
|
if (f) {
|
|
if (lfhasflag(lf, F_GRAVLESSENED)) {
|
|
speed -= 5;
|
|
}
|
|
if ((f->lifetime == FROMRACE) && lfhasflagval(lf, F_INJURY, IJ_WINGBRUISED, NA, NA, NULL)) {
|
|
speed += 10;
|
|
}
|
|
}
|
|
|
|
switch (isburdened(lf)) {
|
|
case BR_NONE:
|
|
break;
|
|
case BR_BURDENED:
|
|
speed += 5;
|
|
break;
|
|
case BR_STRAINED:
|
|
case BR_OVERLOADED:
|
|
speed += 10;
|
|
break;
|
|
}
|
|
|
|
|
|
// caseful walking
|
|
if (lfhasflag(lf, F_CAREFULMOVE)) {
|
|
speed *= 2;
|
|
}
|
|
|
|
if (speed < 1) speed = 1;
|
|
|
|
// reducemovement flags?
|
|
o = hasobwithflag(lf->cell->obpile, F_REDUCEMOVEMENT);
|
|
if (o) {
|
|
f = hasflag(o->flags, F_REDUCEMOVEMENT);
|
|
speed += (f->val[0] * SPEEDUNIT);
|
|
}
|
|
|
|
// water
|
|
adjustspeedforwater(lf, &speed);
|
|
|
|
|
|
return speed;
|
|
}
|
|
|
|
char *getmoveverb(lifeform_t *lf) {
|
|
flag_t *f;
|
|
if (lfhasflag(lf, F_CLIMBING)) {
|
|
return "climb";
|
|
}
|
|
if (lfhasflag(lf, F_FLYING)) {
|
|
return "fly";
|
|
} else if (lfhasflag(lf, F_LEVITATING)) {
|
|
return "float";
|
|
}
|
|
if (lfhasflag(lf, F_FLEEFROM)) {
|
|
return "flee";
|
|
}
|
|
if (isswimming(lf)) {
|
|
return "swim";
|
|
}
|
|
|
|
f = lfhasflag(lf, F_WALKVERB);
|
|
if (f) {
|
|
return f->text;
|
|
}
|
|
return "walk";
|
|
}
|
|
|
|
char *getmoveverbother(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_CLIMBING)) {
|
|
return "climbs";
|
|
}
|
|
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, int climb) {
|
|
if (lfhasflag(lf, F_FLYING)) {
|
|
if (isplayer(lf)) return "fly";
|
|
else return "flies";
|
|
} else if (onpurpose) {
|
|
if (dir == D_DOWN) {
|
|
if (climb) {
|
|
if (isplayer(lf)) return "climb";
|
|
else return "climbs";
|
|
} else {
|
|
if (isplayer(lf)) return "jump";
|
|
else return "jumps";
|
|
}
|
|
} else {
|
|
if (isplayer(lf)) return "climb";
|
|
else return "climbs";
|
|
}
|
|
} else {
|
|
if (dir == D_DOWN) {
|
|
if (isplayer(lf)) return "fall";
|
|
else return "falls";
|
|
} else {
|
|
if (isplayer(lf)) return "rise";
|
|
else return "rises";
|
|
}
|
|
}
|
|
return "?unkonwnmoveverb?";
|
|
}
|
|
|
|
char *getlfname(lifeform_t *lf, char *buf) {
|
|
return real_getlfname(lf, buf, player, B_NOSHOWALL, B_CURRACE);
|
|
}
|
|
|
|
char *real_getlfname(lifeform_t *lf, char *buf, lifeform_t *usevis, int showall, int useorigrace) {
|
|
char descstring[BUFLEN];
|
|
char jobstring[BUFLEN];
|
|
char the[6];
|
|
char lname[BUFLEN];
|
|
race_t *lfrace = NULL;
|
|
flag_t *f;
|
|
enum LFSIZE size,racesize;
|
|
int dobehaviour = B_TRUE;
|
|
enum SKILLLEVEL lorelev;
|
|
|
|
if (usevis && (gamemode != GM_GAMESTARTED)) {
|
|
usevis = NULL;
|
|
}
|
|
|
|
if (ispolymorphed(lf) && useorigrace) {
|
|
f = lfhasflag(lf, F_ORIGRACE);
|
|
if (f) {
|
|
lfrace = findrace(f->val[0]);
|
|
}
|
|
}
|
|
|
|
if (!lfrace) {
|
|
if (lfhasflag(lf, F_LYCANTHROPE)) {
|
|
// lycanthropes appear as human unless you know better
|
|
if ((getlorelevel(player, RC_HUMANOID) >= PR_ADEPT) ||
|
|
(getlorelevel(player, RC_MAGIC) >= PR_BEGINNER)) {
|
|
lfrace = lf->race;
|
|
} else {
|
|
lfrace = findrace(R_HUMAN);
|
|
}
|
|
} else if (lf->race->id == R_ANDROID) {
|
|
if ((getlorelevel(player, RC_HUMANOID) >= PR_BEGINNER) ||
|
|
(getlorelevel(player, RC_ROBOT) >= PR_BEGINNER)) {
|
|
lfrace = lf->race;
|
|
} else {
|
|
lfrace = findrace(R_HUMAN);
|
|
}
|
|
} else {
|
|
lfrace = lf->race;
|
|
}
|
|
}
|
|
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
lorelev = getlorelevel(player, lfrace->raceclass->id);
|
|
} else {
|
|
lorelev = PR_MASTER;
|
|
}
|
|
|
|
// 'the' or 'your' ?
|
|
f = lfhasflag(lf, F_NAME);
|
|
if (f) {
|
|
strcpy(the, "");
|
|
strcpy(lname, f->text);
|
|
} else if (lfhasflag(lf, F_UNIQUE)) {
|
|
strcpy(the, "");
|
|
strcpy(lname, lfrace->name);
|
|
} else {
|
|
if (ispetof(lf, player)) {
|
|
strcpy(the, "your ");
|
|
} else {
|
|
strcpy(the, "the ");
|
|
}
|
|
f = lfhasflag(lf, F_NAMED);
|
|
if (f) { // ie. "the xat named blah"
|
|
strcpy(lname, lfrace->name);
|
|
strcat(lname, " named ");
|
|
strcat(lname, f->text);
|
|
} else { // ie. "the xat"
|
|
strcpy(lname, lfrace->name);
|
|
}
|
|
}
|
|
|
|
// construct description string
|
|
strcpy(descstring, "");
|
|
|
|
// are they larger or smaller than they should be?
|
|
f = hasflag(lfrace->flags, F_SIZE);
|
|
if (f) {
|
|
racesize = f->val[0];
|
|
} else {
|
|
racesize = SZ_HUMAN; // default
|
|
}
|
|
size = getlfsize(lf);
|
|
if (size != racesize) {
|
|
strcat(descstring, getsizetext(size));
|
|
strcat(descstring, " ");
|
|
}
|
|
|
|
if (lf->race->id == R_HYDRA) {
|
|
int nheads;
|
|
char numbuf[BUFLEN];
|
|
nheads = countflagsofid(lf->flags, F_HASATTACK);
|
|
numtotext(nheads, numbuf);
|
|
strcat(descstring, numbuf);
|
|
strcat(descstring, "-headed ");
|
|
}
|
|
|
|
// need a certain amount of race knowledge to recognise ai traits
|
|
if (lorelev < PR_SKILLED) {
|
|
dobehaviour = B_FALSE;
|
|
}
|
|
// frozen/headless trump behavioural descriptions like "insane"
|
|
if (lfhasflag(lf, F_FROZEN)) {
|
|
strcat(descstring, "frozen ");
|
|
dobehaviour = B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_HEADLESS)) {
|
|
strcat(descstring, "headless ");
|
|
dobehaviour = B_FALSE;
|
|
}
|
|
|
|
if (dobehaviour) {
|
|
f = lfhasflag(lf, F_BEHAVIOUR);
|
|
if (f) {
|
|
behaviour_t *b;
|
|
b = findbehaviour(f->val[0]);
|
|
if (b) {
|
|
strcat(descstring, b->name);
|
|
strcat(descstring, " ");
|
|
}
|
|
}
|
|
}
|
|
|
|
// construct job string
|
|
strcpy(jobstring, "");
|
|
if (showall || isplayer(lf) || (lorelev >= PR_BEGINNER)) {
|
|
if (!lfhasflag(lf, F_NOJOBTEXT) && !lfhasflag(lf, F_NAME) && !lfhasflag(lf, F_UNIQUE)) {
|
|
if (getjob(lf)) {
|
|
snprintf(jobstring, BUFLEN, " %s", getjobname(lf));
|
|
jobstring[1] = tolower(jobstring[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
snprintf(buf, BUFLEN, "you");
|
|
} else {
|
|
//if (isblind(player)) {
|
|
if (usevis && !cansee(usevis, lf) && !showall) {
|
|
snprintf(buf, BUFLEN, "something");
|
|
} else {
|
|
if (lf->race->id == R_DANCINGWEAPON) {
|
|
object_t *wep;
|
|
wep = getweapon(lf);
|
|
if (wep) {
|
|
char obname[BUFLEN];
|
|
real_getobname(wep, obname, 1, B_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
snprintf(buf, BUFLEN, "%s%s%s",the,descstring,noprefix(obname));
|
|
} else {
|
|
snprintf(buf, BUFLEN, "%s%s%s%s",the,descstring,lname,jobstring);
|
|
}
|
|
} else if (lfhasflag(lf, F_FEIGNINGDEATH)) {
|
|
if (lfhasflag(lf, F_NAME)) {
|
|
// ie. "jimbo's corpse"
|
|
snprintf(buf, BUFLEN, "%s%s corpse", lname, getpossessive(lname));
|
|
} else {
|
|
// ie. "a wolf corpse"
|
|
snprintf(buf, BUFLEN, "%s %s corpse", needan(lname) ? "an" : "a", lname);
|
|
}
|
|
} else if (lfhasflag(lf, F_NAME)) {
|
|
snprintf(buf, BUFLEN, "%s%s", descstring, lname);
|
|
} else {
|
|
char zombiestring[BUFLEN];
|
|
f = hasflag(lf->flags, F_LFSUFFIX);
|
|
|
|
strcpy(zombiestring, "");
|
|
if (f) {
|
|
snprintf(zombiestring, BUFLEN, " %s", f->text);
|
|
}
|
|
|
|
snprintf(buf, BUFLEN, "%s%s%s%s%s",the,descstring,lname,jobstring,zombiestring);
|
|
}
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
char *getlfnamea(lifeform_t *lf, char *buf) {
|
|
return real_getlfnamea(lf, buf, player, B_NOSHOWALL, B_CURRACE);
|
|
}
|
|
|
|
char *real_getlfnamea(lifeform_t *lf, char *buf, lifeform_t * usevis, int showall, int useorigrace) {
|
|
race_t *lfrace = NULL;
|
|
enum SKILLLEVEL lorelev;
|
|
flag_t *f;
|
|
|
|
if (usevis && (gamemode != GM_GAMESTARTED)) {
|
|
usevis = NULL;
|
|
}
|
|
|
|
if (ispolymorphed(lf) && useorigrace) {
|
|
f = lfhasflag(lf, F_ORIGRACE);
|
|
if (f) {
|
|
lfrace = findrace(f->val[0]);
|
|
}
|
|
}
|
|
|
|
if (!lfrace) {
|
|
if (lfhasflag(lf, F_LYCANTHROPE)) {
|
|
// lycanthropes appear as human unless you know better
|
|
if (getlorelevel(player, RC_HUMANOID) >= PR_ADEPT) {
|
|
lfrace = lf->race;
|
|
} else {
|
|
lfrace = findrace(R_HUMAN);
|
|
}
|
|
} else {
|
|
lfrace = lf->race;
|
|
}
|
|
}
|
|
|
|
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
lorelev = getlorelevel(player, lfrace->raceclass->id);
|
|
} else {
|
|
lorelev = PR_MASTER;
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
snprintf(buf, BUFLEN, "you");
|
|
} else {
|
|
char buf2[BUFLEN];
|
|
char the[6];
|
|
|
|
real_getlfname(lf, buf2, usevis, showall, useorigrace);
|
|
|
|
if (lfhasflag(lf, F_NAME) || lfhasflag(lf, F_UNIQUE)) {
|
|
strcpy(the, "");
|
|
} else {
|
|
if (ispetof(lf, player)) {
|
|
strcpy(the, "your ");
|
|
} else {
|
|
if (isvowel(lfrace->name[0])) {
|
|
strcpy(the, "an ");
|
|
} else {
|
|
strcpy(the, "a ");
|
|
}
|
|
}
|
|
}
|
|
|
|
snprintf(buf, BUFLEN, "%s%s", the, noprefix(buf2));
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
enum LFSIZE getlfsize(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = hasflag(lf->flags, F_SIZE);
|
|
if (!f) f = hasflag(lf->race->flags, F_SIZE);
|
|
if (!f) {
|
|
return SZ_HUMAN;
|
|
}
|
|
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;
|
|
}
|
|
|
|
// returns the best shield for the given damtype
|
|
object_t *getshield(lifeform_t *lf, enum DAMTYPE dt) {
|
|
object_t *shield[MAXPILEOBS];
|
|
int checkmod[MAXPILEOBS];
|
|
int nshields,i;
|
|
object_t *bestshield = NULL;
|
|
int bestcheckmod = -99;
|
|
getallshields(lf, dt, shield, checkmod, &nshields);
|
|
for (i = 0; i < nshields; i++) {
|
|
if (checkmod[i] > bestcheckmod) {
|
|
bestcheckmod = checkmod[i];
|
|
bestshield = shield[i];
|
|
}
|
|
}
|
|
return bestshield;
|
|
}
|
|
|
|
int getallshields(lifeform_t *lf, enum DAMTYPE damtype, object_t **retob, int *checkmod, int *nretobs) {
|
|
object_t *o;
|
|
|
|
*nretobs = 0;
|
|
|
|
if (!hasfreeaction(lf)) {
|
|
return 0;
|
|
}
|
|
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
flag_t *f;
|
|
f = isequipped(o);
|
|
if (f && meetsallattreqs(lf, o)) {
|
|
int isblockob = B_FALSE;
|
|
if (isshield(o) && (f->val[0] == BP_SECWEAPON)) {
|
|
// shield?
|
|
isblockob = B_TRUE;
|
|
} else if (isweapon(o) && (getweaponskill(lf, o) >= PR_SKILLED) && (damtype != DT_PROJECTILE)) {
|
|
if (f->val[0] == BP_WEAPON) {
|
|
// primary weapon which we are skilled in?
|
|
isblockob = B_TRUE;
|
|
} else if ((f->val[0] == BP_SECWEAPON) && (getskill(lf, SK_TWOWEAPON) >= PR_MASTER)) {
|
|
// secondary weapon which we are skilled in,
|
|
// and we are a master two-weaponer ?
|
|
isblockob = B_TRUE;
|
|
}
|
|
}
|
|
if (isblockob) {
|
|
// can it block this damage type?
|
|
if ((damtype == DT_ALL) ||
|
|
hasflagval(o->flags, F_CANBLOCK, damtype, NA, NA, NULL) ||
|
|
hasflagval(o->flags, F_CANBLOCK, DT_ALL, NA, NA, NULL)) {
|
|
retob[*nretobs] = o;
|
|
if (checkmod) checkmod[*nretobs] = getshieldblockmod(lf, o);
|
|
(*nretobs)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return *nretobs;
|
|
}
|
|
|
|
int getshieldblockmod(lifeform_t *lf, object_t *o) {
|
|
enum SKILLLEVEL slev = PR_INEPT;
|
|
int othermod = 0;
|
|
if (isshield(o)) {
|
|
slev = getskill(lf, SK_SHIELDS);
|
|
} else {
|
|
// using a weapon
|
|
// skilled weapon == beginner block
|
|
// expert weapon == adept block
|
|
// master weapon == skilled block
|
|
slev = getweaponskill(lf, o) - 2;
|
|
}
|
|
switch (slev) {
|
|
case PR_NOVICE: othermod = 0; break;
|
|
case PR_BEGINNER: othermod = 4; break;
|
|
case PR_ADEPT: othermod = 7; break;
|
|
case PR_SKILLED: othermod = 10; break;
|
|
case PR_EXPERT: othermod = 13; break;
|
|
case PR_MASTER: othermod = 16; break;
|
|
default:
|
|
// should never happen
|
|
othermod = -4;
|
|
break;
|
|
}
|
|
return othermod;
|
|
}
|
|
|
|
|
|
int getspellspeed(lifeform_t *lf) {
|
|
int speed = 0;
|
|
flag_t *f;
|
|
|
|
f = lfhasflag(lf, F_SPELLSPEED);
|
|
if (f) {
|
|
speed = f->val[0];
|
|
} else {
|
|
f = lfhasflag(lf, F_MOVESPEED);
|
|
if (f) {
|
|
speed = f->val[0];
|
|
} else {
|
|
speed = SPEED_MOVE; // default
|
|
}
|
|
}
|
|
|
|
// don't use movement speed modifier!
|
|
return speed;
|
|
}
|
|
|
|
// note: stamina is stored as a float, but we treat it as an int.
|
|
int getstamina(lifeform_t *lf) {
|
|
return (int)floor(lf->stamina);
|
|
}
|
|
|
|
float getstamregen(lifeform_t *lf) {
|
|
float regenrate = STAMREGEN;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
|
|
if (lfhasflag(lf, F_TRAINING)) {
|
|
// don't regain stamina while training!
|
|
return 0;
|
|
} else if (lfhasflagval(lf, F_INJURY, IJ_WINDPIPECRUSHED, NA, NA, NULL)) {
|
|
regenrate = 0.2; // override everything else
|
|
} else {
|
|
if (lfhasflag(lf, F_ASLEEP)) {
|
|
regenrate = pctof(200, regenrate);
|
|
}
|
|
if (ispoisoned(lf)) {
|
|
regenrate = pctof(50, regenrate);
|
|
}
|
|
}
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_STAMREGEN, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
regenrate += atof(retflag[i]->text);
|
|
}
|
|
limitf(®enrate, 0, NA);
|
|
|
|
return regenrate;
|
|
}
|
|
|
|
|
|
char *getplayername(char *buf) {
|
|
flag_t *f;
|
|
if (!player) {
|
|
strcpy(buf, "");
|
|
} else {
|
|
f = hasflag(player->flags, F_NAME);
|
|
if (f) {
|
|
strcpy(buf, f->text);
|
|
} else{
|
|
// should never happen!
|
|
strcpy(buf, "");
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
char *getplayernamefull(char *buf) {
|
|
char pname[BUFLEN];
|
|
|
|
getplayername(pname);
|
|
|
|
if (strlen(pname)) {
|
|
if (getjob(player)) {
|
|
snprintf(buf, BUFLEN, "%s the %s %s", pname, player->race->name, getjobname(player));
|
|
} else {
|
|
snprintf(buf, BUFLEN, "%s the %s", pname, player->race->name);
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
enum RACECLASS getraceclass(lifeform_t *lf) {
|
|
return lf->race->raceclass->id;
|
|
}
|
|
|
|
// returns rarity number. if optional rr is passed, this is will be returned too.
|
|
int getracerarity(map_t *map, enum RACE rid, enum RARITY *rr) {
|
|
race_t *r;
|
|
int rarity = -1;
|
|
|
|
if (rr) *rr = RR_NONE;
|
|
|
|
r = findrace(rid);
|
|
if (r) {
|
|
flag_t *f = NULL;
|
|
|
|
if (map) {
|
|
f = hasflagval(r->flags, F_RARITY, map->habitat->id, NA, NA, NULL);
|
|
if (!f) {
|
|
f = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL);
|
|
}
|
|
} else {
|
|
f = hasflag(r->flags, F_RARITY);
|
|
}
|
|
/*if (!f) {
|
|
f = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL);
|
|
}*/
|
|
|
|
|
|
if (f) {
|
|
// ignore habitat for now!
|
|
rarity = f->val[1];
|
|
if (rr) *rr = f->val[2];
|
|
}
|
|
}
|
|
|
|
return rarity;
|
|
}
|
|
|
|
// if optional 'attacker' is provided, only select form armours which they
|
|
// can reach.
|
|
object_t *getrandomarmour(lifeform_t *lf, lifeform_t *attacker) {
|
|
object_t *o;
|
|
object_t *poss[MAXBODYPARTS];
|
|
object_t **hitposition;
|
|
int hitchance[MAXBODYPARTS];
|
|
enum BODYPART hitbp[MAXBODYPARTS];
|
|
int nposs = 0;
|
|
int maxroll = 0;
|
|
int i,n,idx;
|
|
int sel;
|
|
|
|
// make a list of all valid armour
|
|
getarmourrating(lf, poss, hitchance, hitbp, &nposs);
|
|
if (!nposs) return NULL;
|
|
|
|
maxroll = 0;
|
|
for (i = 0; i < nposs; i++) {
|
|
if (!attacker || canreachbp(attacker, lf, hitbp[i])) {
|
|
maxroll += hitchance[i];
|
|
}
|
|
}
|
|
if (maxroll == 0) {
|
|
return NULL;
|
|
}
|
|
// now figure out chances of each one getting hit
|
|
hitposition = malloc(maxroll * sizeof(object_t *));
|
|
idx = 0;
|
|
for (i = 0; i < nposs; i++) {
|
|
if (!attacker || canreachbp(attacker, lf, hitbp[i])) {
|
|
for (n = 0; n < hitchance[i]; n++) {
|
|
hitposition[idx] = poss[i];
|
|
idx++;
|
|
}
|
|
}
|
|
}
|
|
|
|
sel = rnd(0, maxroll-1);
|
|
o = hitposition[sel];
|
|
|
|
free(hitposition);
|
|
|
|
return o;
|
|
}
|
|
|
|
enum BEHAVIOUR getrandombehaviour(void) {
|
|
behaviour_t *b;
|
|
int numb = 0,sel;
|
|
enum BEHAVIOUR selb = BH_NONE;
|
|
for (b = firstbehaviour ; b ; b = b->next) {
|
|
numb++;
|
|
}
|
|
sel = rnd(0,numb-1);
|
|
numb = 0;
|
|
for (b = firstbehaviour ; b ; b = b->next) {
|
|
if (numb == sel) {
|
|
selb = b->id;
|
|
break;
|
|
}
|
|
numb++;
|
|
}
|
|
if (selb == BH_NONE) {
|
|
// should never happen!
|
|
assert("getrandombehaviour() failed!" == 0);
|
|
}
|
|
return selb;
|
|
}
|
|
|
|
// pick a random major body part
|
|
// ie. head, body, arms/hands, legs, wings, tail
|
|
enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker) {
|
|
int cutoff[4],nparts = 0,i,max = 0,num;
|
|
enum BODYPART bp[4],selbp = BP_NONE;
|
|
|
|
if (hasbp(lf, BP_BODY)) {
|
|
if (attacker && !canreachbp(attacker, lf, BP_BODY)) {
|
|
} else {
|
|
bp[nparts++] = BP_BODY;
|
|
}
|
|
}
|
|
if (hasbp(lf, BP_HANDS)) {
|
|
if (attacker && !canreachbp(attacker, lf, BP_HANDS)) {
|
|
} else {
|
|
bp[nparts++] = BP_HANDS;
|
|
}
|
|
}
|
|
if (hasbp(lf, BP_HEAD)) {
|
|
if (attacker && !canreachbp(attacker, lf, BP_HEAD)) {
|
|
} else {
|
|
bp[nparts++] = BP_HEAD;
|
|
}
|
|
}
|
|
if (hasbp(lf, BP_LEGS) || hasbp(lf, BP_FRONTLEGS) || hasbp(lf, BP_BACKLEGS)) {
|
|
if (attacker && !canreachbp(attacker, lf, BP_LEGS)) {
|
|
} else {
|
|
bp[nparts++] = BP_LEGS;
|
|
}
|
|
}
|
|
if (hasbp(lf, BP_WINGS)) {
|
|
if (attacker && !canreachbp(attacker, lf, BP_WINGS)) {
|
|
} else {
|
|
bp[nparts++] = BP_WINGS;
|
|
}
|
|
}
|
|
if (hasbp(lf, BP_TAIL)) {
|
|
if (attacker && !canreachbp(attacker, lf, BP_TAIL)) {
|
|
} else {
|
|
bp[nparts++] = BP_TAIL;
|
|
}
|
|
}
|
|
|
|
if (!nparts) {
|
|
return BP_NONE;
|
|
}
|
|
|
|
for (i = 0;i < nparts; i++) {
|
|
if (i == 0) {
|
|
cutoff[i] = getbodyparthitchance(bp[i]);
|
|
} else {
|
|
cutoff[i] = cutoff[i-1] + getbodyparthitchance(bp[i]);
|
|
}
|
|
}
|
|
max = cutoff[nparts-1];
|
|
num = rnd(1,max);
|
|
for (i = 0;i < nparts; i++) {
|
|
if (num <= cutoff[i]) {
|
|
selbp = bp[i];
|
|
break;
|
|
}
|
|
}
|
|
if (selbp == BP_NONE) {
|
|
dblog("error in getrandomcorebodypart!");
|
|
msg("error in getrandomcorebodypart!");
|
|
}
|
|
return selbp;
|
|
}
|
|
|
|
race_t *getrandomcorpserace(cell_t *c) {
|
|
race_t *r = NULL;
|
|
|
|
while (!r || hasflag(r->flags, F_NOCORPSE) || hasflag(r->flags, F_CORPSETYPE) || hasflag(r->flags, F_EXTRACORPSE)) {
|
|
if (c) r = getrandomrace(c, NA);
|
|
else r = getreallyrandomrace(RC_ANY);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
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_FREQUENT;
|
|
|
|
|
|
// determine rarity of lf to generate
|
|
if (forcedepth != NA) {
|
|
depth = forcedepth;
|
|
} else if (c) {
|
|
depth = getmapdifficulty((c && c->map) ? c->map : NULL);
|
|
} else {
|
|
depth = rnd(1,MAXDEPTH);
|
|
}
|
|
|
|
gettrrange(depth, &hdmin, &hdmax, RARITYVARIANCELF, B_TRUE);
|
|
|
|
// pick rr...
|
|
wantrr = pickrr(TT_MONSTER);
|
|
|
|
if (db) dblog("finding random lf with hitdice %d-%d and rr <= %d (for depth %d)\n",hdmin,hdmax, wantrr, depth);
|
|
|
|
// try to find a lf of this type which will
|
|
// fit in the map's habitat
|
|
nposs = 0;
|
|
while (nposs == 0) {
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
int valid = B_FALSE;
|
|
int thishd;
|
|
enum RARITY thisrr = RR_NEVER;
|
|
flag_t *rarflag = NULL;
|
|
|
|
thishd = gettrrace(r);
|
|
if ((thishd < hdmin) || (thishd > hdmax)) {
|
|
valid = B_FALSE;
|
|
} else {
|
|
if (db) dblog("%s hitdice %d ok (between %d - %d)",r->name,thishd,hdmin,hdmax);
|
|
// correct rarity?
|
|
if (c) {
|
|
rarflag = hasflagval(r->flags, F_RARITY, c->habitat->id, NA, NA, NULL);
|
|
if (!rarflag) {
|
|
rarflag = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL);
|
|
}
|
|
} else {
|
|
rarflag = hasflag(r->flags, F_RARITY);
|
|
}
|
|
/*if (!rarflag) {
|
|
rarflag = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL);
|
|
}*/
|
|
|
|
if (rarflag) {
|
|
if ((rarflag->val[2] == NA) || (rarflag->val[2] <= wantrr)) {
|
|
if (db) dblog("%s has correct rarity rr (%d <= wantrr(%d)).",r->name, rarflag->val[2], wantrr);
|
|
valid = B_TRUE;
|
|
thisrr = rarflag->val[2];
|
|
} else {
|
|
if (db) dblog("%s has wrong rr (%d, wantrr=%d).",r->name, rarflag->val[2], wantrr);
|
|
}
|
|
} else {
|
|
if (db) dblog("%s has no rarity flag.",r->name);
|
|
valid = B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (valid && c) {
|
|
// can it go into the cell?
|
|
if (hasobwithflag(c->obpile, F_DEEPWATER)) {
|
|
// water
|
|
if (!hasflag(r->flags, F_AQUATIC) && (r->raceclass->id != RC_AQUATIC)) {
|
|
// not aquatic
|
|
valid = B_FALSE;
|
|
}
|
|
} else {
|
|
// no water
|
|
if (hasflag(r->flags, F_NEEDSWATER)) {
|
|
valid = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (valid) {
|
|
if (db) dblog("-> possibility: %s, hitdice=%d, rarity=%s",r->name, thishd, getrarityname(thisrr));
|
|
poss[nposs] = r;
|
|
nposs++;
|
|
if (nposs >= MAXRANDOMLFCANDIDATES) break;
|
|
}
|
|
}
|
|
|
|
// nothing found?
|
|
if (nposs == 0) {
|
|
// at frequency "FREQUENT"? Try COMMON.
|
|
if (wantrr == RR_FREQUENT) {
|
|
if (db) dblog("no possible lfs like this. trying again with rr_common instead of frequent.\n");
|
|
wantrr = RR_COMMON;
|
|
} else {
|
|
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;
|
|
int nskills;
|
|
skill_t *sk;
|
|
enum SKILL retskill = SK_NONE;
|
|
// count skills
|
|
nskills = 0;
|
|
for (sk = firstskill ; sk ; sk = sk->next) {
|
|
nskills++;
|
|
}
|
|
|
|
sel = rnd(0,nskills-1);
|
|
count = 0;
|
|
for (sk = firstskill ; sk ; sk = sk->next) {
|
|
if (count == sel) {
|
|
retskill = sk->id;
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
assert(findskill(retskill));
|
|
return retskill;
|
|
}
|
|
|
|
object_t *getrestob(lifeform_t *lf) {
|
|
object_t *o,*bestob = NULL;
|
|
int bestval = -1;
|
|
flag_t *f;
|
|
|
|
f = lfhasflag(lf, F_RESTINGINMOTEL);
|
|
if (f) {
|
|
o = findobidinmap(lf->cell->map, atol(f->text));
|
|
if (o) return o;
|
|
}
|
|
|
|
for (o = lf->cell->obpile->first ; o ; o = o->next) {
|
|
if (isknown(o)) {
|
|
f = hasflag(o->flags, F_HELPSREST);
|
|
if (f && !isarmour(o)) {
|
|
if (!bestob || (f->val[0] > bestval)) {
|
|
bestval = f->val[0];
|
|
bestob = o;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for (o = lf->pack->first; o ; o = o->next) {
|
|
if (isknown(o)) {
|
|
f = hasflag(o->flags, F_HELPSREST);
|
|
if (f) {
|
|
int valid = B_TRUE;
|
|
// can't rest in armour which doesn't fit
|
|
if (isarmour(o) && !armourfits(lf, o, NULL)) {
|
|
valid = B_FALSE;
|
|
}
|
|
if (valid) {
|
|
if (!bestob || (f->val[0] > bestval)) {
|
|
bestval = f->val[0];
|
|
bestob = o;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestob;
|
|
}
|
|
|
|
// returns proficiency level in a given skill
|
|
enum SKILLLEVEL getskill(lifeform_t *lf, enum SKILL id) {
|
|
flag_t *f;
|
|
// special case:
|
|
if ((id == SK_CARTOGRAPHY) && lfhasflag(lf, F_RAGE)) {
|
|
return PR_INEPT;
|
|
}
|
|
f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL);
|
|
if (f) {
|
|
return f->val[1];
|
|
}
|
|
return PR_INEPT;
|
|
}
|
|
|
|
int getskilllevcost(enum SKILLLEVEL slev) {
|
|
int cost;
|
|
cost = slev - 1;
|
|
limit(&cost, 1, NA);
|
|
return cost;
|
|
}
|
|
|
|
int getsounddist(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;
|
|
float amtneeded;
|
|
amtneeded = SKILLXPPERPOINT;
|
|
amtneeded += (30 * lf->totskillpoints);
|
|
//mod = MAXOF(getstatmod(lf, A_IQ), getstatmod(lf, A_WIS));
|
|
mod = getstatmod(lf, A_IQ) + getstatmod(lf, A_WIS);
|
|
amtneeded = pctof(100 - mod, amtneeded);
|
|
return amtneeded;
|
|
}
|
|
|
|
// returns a pct addition for things like:
|
|
// accuracy, evasion, lockpicking
|
|
// result will be between -50 and 50
|
|
float getstatmod(lifeform_t *lf, enum ATTRIB att) {
|
|
float mod = 0;
|
|
//float base;
|
|
// <10 = penalty
|
|
// 10 = average
|
|
// >10 = bonus up to 50%
|
|
mod = getattr(lf, att) - 50;
|
|
return mod;
|
|
}
|
|
|
|
|
|
enum ATTRBRACKET getattrbracket(int attrval, enum ATTRIB whichatt, char *buf) {
|
|
if (attrval <= 12) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_EXLOW));
|
|
return AT_EXLOW;
|
|
} else if (attrval <= 23) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_VLOW));
|
|
return AT_VLOW;
|
|
} else if (attrval <= 34) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_LOW));
|
|
return AT_LOW;
|
|
} else if (attrval <= 45) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_LTAVERAGE));
|
|
return AT_LTAVERAGE;
|
|
} else if (attrval <= 56) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_AVERAGE));
|
|
return AT_AVERAGE;
|
|
} else if (attrval <= 67) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_GTAVERAGE));
|
|
return AT_GTAVERAGE;
|
|
} else if (attrval <= 78) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_HIGH));
|
|
return AT_HIGH;
|
|
} else if (attrval <= 89) {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_VHIGH));
|
|
return AT_VHIGH;
|
|
} else {
|
|
if (buf) strcpy(buf, getattrbracketname(whichatt, AT_EXHIGH));
|
|
return AT_EXHIGH;
|
|
}
|
|
|
|
if (buf) strcpy(buf, "??noattrbracketname??");
|
|
return AT_AVERAGE;
|
|
}
|
|
|
|
char *getskilldesc(enum SKILL id) {
|
|
skill_t *s;
|
|
s = findskill(id);
|
|
if (s) {
|
|
return s->desc;
|
|
}
|
|
return "?unknownskilldesc?";
|
|
}
|
|
|
|
char *getskillname(enum SKILL id) {
|
|
skill_t *s;
|
|
s = findskill(id);
|
|
if (s) {
|
|
return s->name;
|
|
}
|
|
return "?unknownskill?";
|
|
}
|
|
|
|
char *getskilllevelname(enum SKILLLEVEL sl) {
|
|
switch (sl) {
|
|
case PR_INEPT:
|
|
return "Inept";
|
|
case PR_NOVICE:
|
|
return "Novice";
|
|
case PR_BEGINNER:
|
|
return "Beginner";
|
|
case PR_ADEPT:
|
|
return "Adept";
|
|
case PR_SKILLED:
|
|
return "Skilled";
|
|
case PR_EXPERT:
|
|
return "Expert";
|
|
case PR_MASTER:
|
|
return "Master";
|
|
}
|
|
return "?Unknownskilllevel?";
|
|
}
|
|
|
|
|
|
// get a list of of skills which teacher could teach student.
|
|
// returns # of potential skills found.
|
|
int getteachableskills(lifeform_t *teacher, lifeform_t *student, int *info, enum TRADEINFOTYPE *tradetype, int *ninfo ) {
|
|
flag_t *retflag[MAXCANDIDATES], *f;
|
|
int nretflags, i;
|
|
|
|
*ninfo = 0;
|
|
// 'teacher' can teach a skill to 'student' if:
|
|
// - teacher and student both know the skill, and teacher's skill level is higher.
|
|
// OR
|
|
// - only teacher has the skill, and teacher's skill level is >= adept.
|
|
getflags(teacher->flags, retflag, &nretflags, F_HASSKILL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
enum SKILLLEVEL studentlev,teacherlev;
|
|
enum SKILL sid;
|
|
int teachable = B_FALSE;
|
|
f = retflag[i];
|
|
sid = f->val[0];
|
|
teacherlev = f->val[1];
|
|
studentlev = getskill(student, sid);
|
|
if (studentlev && (teacherlev > studentlev) && !ismaxedskill(student, sid)) {
|
|
teachable = B_TRUE;
|
|
} else if (!studentlev && (teacherlev >= PR_ADEPT)) {
|
|
teachable = B_TRUE;
|
|
}
|
|
if (teachable) {
|
|
info[*ninfo] = sid;
|
|
tradetype[*ninfo] = TI_SKILL;
|
|
(*ninfo)++;
|
|
}
|
|
}
|
|
|
|
// 'teacher' can teach a magic spell (oc_spell) to 'student' if:
|
|
// - teacher can will/cast the spell.
|
|
// AND
|
|
// - student can't already will/cast the spell
|
|
// AND
|
|
// - student is skilled in at least one of the spell's schools.
|
|
// AND
|
|
// - student is a high enough level to cast the spell.
|
|
// (use existing code from spellbooks!)
|
|
getflags(teacher->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
objecttype_t *spell;
|
|
enum OBTYPE spellid;
|
|
enum SPELLSCHOOL knownschool;
|
|
int teachable = B_FALSE;
|
|
f = retflag[i];
|
|
spellid = f->val[0];
|
|
spell = findot(spellid);
|
|
if (spell->obclass->id != OC_SPELL) continue;
|
|
|
|
// student skilled in one of the spell's schools?
|
|
knownschool = getspellschoolknown(student, spellid);
|
|
if (getskill(student, getschoolskill(knownschool))) {
|
|
int mpneeded;
|
|
if (!cancast(student, spellid, &mpneeded) && (getspellpower(student, spellid) > 0)) {
|
|
if (getmaxmp(student) >= mpneeded) {
|
|
teachable = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (teachable) {
|
|
info[*ninfo] = spellid;
|
|
tradetype[*ninfo] = TI_SPELL;
|
|
(*ninfo)++;
|
|
}
|
|
}
|
|
return *ninfo;
|
|
}
|
|
|
|
// get threat rating
|
|
int gettr(lifeform_t *lf) {
|
|
flag_t *f;
|
|
if (isplayer(lf) || lfhasflag(lf, F_VARLEVEL)) {
|
|
return lf->level;
|
|
}
|
|
f = hasflag(lf->flags, F_TR);
|
|
return f->val[0];
|
|
//return (lf->maxhp / 4);
|
|
}
|
|
|
|
|
|
char *gettradeinfoname(int what, enum TRADEINFOTYPE tradetype, char *buf) {
|
|
if (tradetype == TI_SKILL) {
|
|
skill_t *sk;
|
|
sk = findskill(what);
|
|
strcpy(buf, sk->name);
|
|
} else { // ie. spell
|
|
objecttype_t *ot;
|
|
ot = findot(what);
|
|
sprintf(buf, "the spell '%s'", ot->name);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
|
|
|
|
// get throw speed (ie. damage multiplier)
|
|
// based on strength.
|
|
//
|
|
// cube the number to get km/h.
|
|
// ie. 1 = 1km/h
|
|
// ie. 2 = 16km/h
|
|
// ie. 3 = 27km/h
|
|
// ie. 4 = 64km/h
|
|
// ie. 5 = 125km/h
|
|
// ie. 10 = 1000km/h
|
|
int getthrowspeed(lifeform_t *lf) {
|
|
enum ATTRBRACKET sb;
|
|
int speed = 0;
|
|
|
|
// ie. 1 - 4
|
|
speed = (getskill(lf, SK_THROWING) / 3) + 1;
|
|
|
|
sb = getattrbracket(getattr(lf, A_STR), A_STR, NULL);
|
|
switch (sb) {
|
|
default:
|
|
break;
|
|
case AT_VHIGH:
|
|
speed++;
|
|
break;
|
|
case AT_EXHIGH:
|
|
speed += 2;
|
|
break;
|
|
// gun is 15
|
|
}
|
|
return speed;
|
|
}
|
|
|
|
int getturnspeed(lifeform_t *lf) {
|
|
int speed;
|
|
speed = getmovespeed(lf) / 2;
|
|
limit(&speed, 1, NA);
|
|
return speed;
|
|
}
|
|
|
|
void getwantdistance(lifeform_t *lf, lifeform_t *victim, int *min, int *max, int attacking) {
|
|
flag_t *f;
|
|
// default - run into them
|
|
*min = 0;
|
|
*max = 0;
|
|
if (attacking) {
|
|
f = lfhasflag(lf, F_ATTACKRANGE);
|
|
if (f) {
|
|
*min = f->val[0];
|
|
*max = f->val[1];
|
|
}
|
|
// if you can thrust, keep your distance
|
|
if (cancast(lf, OT_A_THRUST, NULL)) {
|
|
if (*min < 2) *min = 2;
|
|
if (*max < *min) *max = *min;
|
|
}
|
|
// if you're timid, it means you will only go close if you are
|
|
// behind them.
|
|
if (victim && lfhasflag(lf, F_TIMID)) {
|
|
// if not already adjacent...
|
|
if (getcelldist(lf->cell,victim->cell) >= 2) {
|
|
if ((*min < 2) && !isbehind(lf, victim)) {
|
|
*min = 2;
|
|
if (*max < *min) *max = *min;
|
|
}
|
|
}
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
// this function MIGHT allocate op!
|
|
int getweapons(lifeform_t *lf, int meleeonly, object_t **wep, flag_t **damflag, int *lastweaponidx, obpile_t **op, int *nweps) {
|
|
int gotweapon = B_FALSE;
|
|
flag_t *retflag[MAXCANDIDATES],*f,*forcewep;
|
|
int nretflags,i;
|
|
// first use our weapon...
|
|
|
|
*nweps = 0;
|
|
|
|
forcewep = lfhasflag(lf, F_FORCEATTACK);
|
|
if (!forcewep) {
|
|
wep[*nweps] = meleeonly ? getmeleeweapon(lf) : getweapon(lf);
|
|
if (wep[*nweps]) {
|
|
if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM);
|
|
if (lastweaponidx) *lastweaponidx = 0;
|
|
(*nweps)++;
|
|
gotweapon = B_TRUE;
|
|
}
|
|
|
|
// if we are skilled at twoweaponing, we can attack with our second weapon
|
|
// as well, with a possible accuracy penalty depending on our skill level.
|
|
if (getskill(lf, SK_TWOWEAPON)) {
|
|
wep[*nweps] = getsecmeleeweapon(lf);
|
|
if (wep[*nweps]) {
|
|
if ((*nweps >= 1) && (wep[*nweps] == wep[(*nweps)-1])) {
|
|
// can't be the same as first one
|
|
} else {
|
|
if (damflag) damflag[*nweps] = hasflag(wep[*nweps]->flags, F_DAM);
|
|
if (lastweaponidx) *lastweaponidx = *nweps;
|
|
(*nweps)++;
|
|
gotweapon = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
} // end if !forcewep
|
|
|
|
// then use all our innate attacks..
|
|
getflags(lf->flags, retflag, &nretflags, F_HASATTACK, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (!forcewep || (f->val[0] == forcewep->val[0])) {
|
|
objecttype_t *ot;
|
|
|
|
if (!(*op)) {
|
|
*op = addobpile(NULL, NULL, NULL);
|
|
}
|
|
|
|
ot = findot(f->val[0]);
|
|
if (ot) {
|
|
wep[*nweps] = addobfast(*op, ot->id);
|
|
if (damflag) damflag[*nweps] = f;
|
|
(*nweps)++;
|
|
}
|
|
}
|
|
}
|
|
return gotweapon;
|
|
}
|
|
|
|
|
|
enum SKILLLEVEL getweaponskill(lifeform_t *lf, object_t *o) {
|
|
skill_t *sk;
|
|
if (!o) {
|
|
return getskill(lf, SK_UNARMED);
|
|
}
|
|
sk = getobskill(o->flags);
|
|
if (sk) {
|
|
enum SKILLLEVEL weplev;
|
|
weplev = getskill(lf, sk->id);
|
|
return weplev;
|
|
}
|
|
return PR_INEPT;
|
|
}
|
|
|
|
long getxpforlev(int level) {
|
|
long needxp = 0;
|
|
// 2.8
|
|
float multiplier = 13;
|
|
float constant = 2.8;
|
|
|
|
// no xp needed for level 1
|
|
/*
|
|
for (i = 0; i < level - 1; i++) {
|
|
//needxp += (20 * pow(2,i));
|
|
needxp += (20 * (pow(i,2.8)));
|
|
}
|
|
*/
|
|
needxp = (multiplier * (pow(level,constant) - 1));
|
|
return needxp;
|
|
}
|
|
|
|
void givebehaviour(lifeform_t *lf, enum BEHAVIOUR bid) {
|
|
behaviour_t *b;
|
|
flag_t *f;
|
|
b = findbehaviour(bid);
|
|
if (!b) return;
|
|
addflag(lf->flags, F_BEHAVIOUR, bid, NA, NA, NULL);
|
|
copyflags(lf->flags, b->flags, FROMRACE);
|
|
switch (bid) {
|
|
case BH_TIMID:
|
|
f = lfhasflag(lf, F_MORALE);
|
|
if (f) {
|
|
f->val[0] /= 2;
|
|
}
|
|
break;
|
|
case BH_DRUGGED:
|
|
killflagsofid(lf->flags, F_FLEEONDAM);
|
|
killflagsofid(lf->flags, F_FLEEONHPPCT);
|
|
killflagsofid(lf->flags, F_FLEEFROM);
|
|
break;
|
|
case BH_DRUNK:
|
|
addflag(lf->flags, F_DRUNK, rnd(2,5), NA, NA, NULL);
|
|
case BH_MUSCLED:
|
|
lf->maxhp = pctof(rnd(125,200), lf->maxhp); // 25-100% more hp
|
|
lf->hp = lf->maxhp;
|
|
break;
|
|
case BH_SCRAWNY:
|
|
lf->maxhp = pctof(rnd(50,75), lf->maxhp); // 25-50% less hp
|
|
limit(&(lf->maxhp), 1, NA);
|
|
lf->hp = lf->maxhp;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void givejob(lifeform_t *lf, enum JOB jobid) {
|
|
job_t *j;
|
|
flag_t *f;
|
|
int i;
|
|
//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);
|
|
}
|
|
}
|
|
// override alignment from race
|
|
if (hasflag(j->flags, F_ALIGNMENT)) {
|
|
killflagsofid(lf->flags, F_ALIGNMENT);
|
|
}
|
|
|
|
|
|
// apply attrib mods from this job
|
|
getflags(j->flags, retflag, &nretflags, F_JOBATTRMOD, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
modattr(lf, f->val[0], f->val[1]);
|
|
// these aren't temporary.
|
|
lf->baseatt[f->val[0]] = lf->att[f->val[0]];
|
|
}
|
|
|
|
// inherit all flags except:
|
|
// - hpmod ones
|
|
// - skills which we already have f_noskill for.
|
|
for (f = j->flags->first ; f ; f = f->next) {
|
|
int val[3],id,ignorethis = B_FALSE;
|
|
char *text;
|
|
flag_t *f2;
|
|
|
|
id = f->id;
|
|
val[0] = f->val[0];
|
|
val[1] = f->val[1];
|
|
val[2] = f->val[2];
|
|
text = f->text;
|
|
|
|
if (f->chance != 100) {
|
|
if (rnd(0,100) > f->chance) {
|
|
// failed! got an altval?
|
|
if (f->altval) {
|
|
// use it instead
|
|
id = f->altval->id;
|
|
val[0] = f->altval->val[0];
|
|
val[1] = f->altval->val[1];
|
|
val[2] = f->altval->val[2];
|
|
text = f->altval->text;
|
|
} else {
|
|
// ignore this one.
|
|
ignorethis = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((f->condition == FC_IFMONSTER) && isplayer(lf)) {
|
|
ignorethis = B_TRUE;
|
|
} else if ((f->condition == FC_IFPLAYER) && !isplayer(lf)) {
|
|
ignorethis = B_TRUE;
|
|
}
|
|
|
|
switch (f->id) {
|
|
case F_MAXHPMOD:
|
|
case F_JOBATTRMOD:
|
|
case F_CANHAVESUBJOB:
|
|
ignorethis = B_TRUE;
|
|
break;
|
|
case F_CANLEARN:
|
|
if (lfhasflagval(lf, F_NOSKILL, f->val[0], NA, NA, NULL)) ignorethis = B_TRUE;
|
|
if (!ignorethis && (f->val[1] != NA)) {
|
|
// already have a better limit?
|
|
f2 = lfhasflagval(lf, F_NOSKILL, f->val[0], NA, NA, NULL);
|
|
if (f2 && (f2->val[1] != NA) && (f2->val[1] > f->val[1])) ignorethis = B_TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!ignorethis) {
|
|
if (db) dblog("inheriting flagid %d.", f->id);
|
|
addflag_real(lf->flags, id, val[0], val[1], val[2], text, FROMJOB,B_TRUE, -1);
|
|
if (id == F_STARTATT) { // need to reroll attribs if we override them.
|
|
rollatt[val[0]] = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// now give start obs/skills from it
|
|
givestartskills(lf, lf->flags);
|
|
if (!lfhasflag(lf, F_PHANTASM)) {
|
|
givestartobs(lf, NULL, lf->flags);
|
|
autoskill(lf);
|
|
}
|
|
|
|
// override hp/mp from race
|
|
/*
|
|
if (rollhp) {
|
|
lf->maxhp = 0;
|
|
for (i = 0; i < lf->level; i++) {
|
|
lf->maxhp += rollhitdice(lf);
|
|
assert(lf->maxhp > 0);
|
|
}
|
|
lf->hp = lf->maxhp;
|
|
}
|
|
*/
|
|
|
|
if (rollmp) {
|
|
f = hasflag(lf->flags, F_MPDICE);
|
|
if (f) {
|
|
lf->maxmp = rollmpdice(lf, B_TRUE);
|
|
for (i = 0; i < lf->level-1; i++) {
|
|
lf->maxmp += rollmpdice(lf, B_FALSE);
|
|
}
|
|
lf->mp = lf->maxmp;
|
|
}
|
|
}
|
|
|
|
// re-roll attributes if required
|
|
for (i = 0; i < MAXATTS; i++) {
|
|
if (rollatt[i]) {
|
|
rollstat(lf, i);
|
|
lf->baseatt[i] = lf->att[i];
|
|
}
|
|
}
|
|
|
|
// reset max stamina if required.
|
|
lf->stamina = getmaxstamina(lf);
|
|
|
|
if ((gamemode != GM_GAMESTARTED)) {
|
|
autoweild(lf);
|
|
}
|
|
|
|
// special cases
|
|
if (j->id == J_MONK) {
|
|
flag_t *f;
|
|
// monk fists do more damage
|
|
// also, they always use fists, even if the race has claws etc.
|
|
f = lfhasflag(lf, F_HASATTACK);
|
|
if (f) {
|
|
f->val[0] = OT_FISTS;
|
|
f->val[1] = 5;
|
|
}
|
|
} else if (j->id == J_PIRATE) {
|
|
flag_t *f;
|
|
// pirate has a hook instead of a hand
|
|
f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL);
|
|
if (f) {
|
|
f->val[0] = OT_HOOKHAND;
|
|
f->val[1] = 4;
|
|
}
|
|
/*
|
|
} else if (j->id == J_SHOPKEEPER) {
|
|
// shopkeepers are not hostile.
|
|
killflagsofid(lf->flags, F_HOSTILE);
|
|
killflagsofid(lf->flags, F_HATESRACE);
|
|
// mark its home shop
|
|
if (isroom(lf->cell)) {
|
|
addflag(lf->flags, F_OWNSSHOP, getroomid(lf->cell), NA, NA, NULL);
|
|
addflag(lf->flags, F_STAYINROOM, getroomid(lf->cell), NA, NA, NULL);
|
|
}
|
|
*/
|
|
}
|
|
|
|
// extra tasks for some jobs
|
|
if (j->id == J_DRUID) {
|
|
enum OBTYPE spell;
|
|
int i;
|
|
for (i = 0;i < 3; i++) {
|
|
// pick a spell which you don't already have...
|
|
spell = getrandomspellfromschool(SS_NATURE, 1);
|
|
while (cancast(lf, spell, NULL)) {
|
|
spell = getrandomspellfromschool(SS_NATURE, 1);
|
|
}
|
|
// you can now cast it.
|
|
addflag(lf->flags, F_CANCAST, spell, NA, NA, NULL);
|
|
}
|
|
// druids always worship ekrub
|
|
if (isplayer(lf)) {
|
|
lifeform_t *god;
|
|
god = findgod(R_GODNATURE);
|
|
addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
// select subjobs
|
|
if ((gamemode == GM_CHARGEN) && isplayer(lf)) {
|
|
subjob_t *sub;
|
|
enum SUBJOB sj = SJ_NONE;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
getflags(j->flags, retflag, &nretflags, F_CANHAVESUBJOB, F_NONE);
|
|
initprompt(&prompt, "Select your job specialty:");
|
|
for (sub = firstsubjob ; sub ; sub = sub->next) {
|
|
if (hasflagval(j->flags, F_CANHAVESUBJOB, sub->id, NA, NA, NULL)) {
|
|
addchoice(&prompt, sub->letter, sub->name, NULL, sub, sub->desc);
|
|
}
|
|
}
|
|
|
|
addchoice(&prompt, '-', "(none)", NULL, NULL, NULL);
|
|
if (prompt.nchoices > 1) {
|
|
getchoicestr(&prompt, B_FALSE, B_TRUE);
|
|
sub = (subjob_t *)prompt.result;
|
|
if (sub) {
|
|
sj = sub->id;
|
|
} else {
|
|
sj = SJ_NONE;
|
|
}
|
|
}
|
|
givesubjob(lf, sj);
|
|
|
|
|
|
|
|
|
|
if (j->id == J_WIZARD) {
|
|
object_t *sb2;
|
|
skill_t *sk;
|
|
// wizards with no subjob:
|
|
if (sj == SJ_NONE) {
|
|
object_t *sb1;
|
|
// start with a grimoire of spells
|
|
sb1 = addob(lf->pack, "grimoire");
|
|
identify(sb1);
|
|
|
|
// monsters get spells from a random school
|
|
addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL);
|
|
addflag(lf->flags, F_RNDSPELLSCHOOL, SS_NONE, 1, lf->level, NULL);
|
|
|
|
}
|
|
|
|
if (sj != SJ_NONE) {
|
|
// wizards with sub-jobs now get a secondary school
|
|
initprompt(&prompt, "Select your secondary spell school:");
|
|
addchoice(&prompt, 'd', getskillname(SK_SS_DIVINATION), NULL, findskill(SK_SS_DIVINATION), NULL);
|
|
addchoice(&prompt, 'm', getskillname(SK_SS_MODIFICATION), NULL, findskill(SK_SS_MODIFICATION), NULL);
|
|
addchoice(&prompt, 's', getskillname(SK_SS_SUMMONING), NULL, findskill(SK_SS_SUMMONING), NULL);
|
|
addchoice(&prompt, 't', getskillname(SK_SS_TRANSLOCATION), NULL, findskill(SK_SS_TRANSLOCATION), NULL);
|
|
getchoice(&prompt);
|
|
sk = (skill_t *) prompt.result;
|
|
switch (sk->id) {
|
|
case SK_SS_DIVINATION:
|
|
sb2 = addob(lf->pack, "spellbook of divination magic");
|
|
break;
|
|
case SK_SS_MODIFICATION:
|
|
sb2 = addob(lf->pack, "spellbook of modification magic");
|
|
break;
|
|
case SK_SS_SUMMONING:
|
|
sb2 = addob(lf->pack, "spellbook of summoning magic");
|
|
break;
|
|
case SK_SS_TRANSLOCATION:
|
|
sb2 = addob(lf->pack, "spellbook of translocation magic");
|
|
break;
|
|
default:
|
|
sb2 = NULL;
|
|
break;
|
|
}
|
|
if (sb2) {
|
|
addflag(lf->flags, F_CANCAST, sb2->contents->first->type->id, NA, NA, NULL);
|
|
if (isplayer(lf)) {
|
|
addflag(lf->flags, F_SHORTCUT, getnextshortcut(lf), NA, NA, sb2->contents->first->type->name);
|
|
addflag(sb2->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
identify(sb2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
generatealignment(lf);
|
|
}
|
|
|
|
if (hasflag(j->flags, F_STAYINROOM)) sethomeroom(lf);
|
|
}
|
|
|
|
void givesubjob(lifeform_t *lf, enum SUBJOB sj) {
|
|
flag_t *jobflag,*f;
|
|
object_t *sb1 = NULL,*o;
|
|
int i;
|
|
|
|
if (sj == SJ_NONE) return;
|
|
|
|
// remember the subjob
|
|
jobflag = lfhasflag(lf, F_JOB);
|
|
jobflag->val[1] = sj;
|
|
|
|
switch (sj) {
|
|
// mage types
|
|
case SJ_AIRMAGE:
|
|
o = addob(lf->pack, "15 blessed darts");
|
|
identify(o);
|
|
giveskilllev(lf, SK_THROWING, PR_NOVICE);
|
|
addflag(lf->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL);
|
|
sb1 = addob(lf->pack, "spellbook of air magic");
|
|
if (!isplayer(lf)) {
|
|
addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL);
|
|
addflag(lf->flags, F_RNDSPELLSCHOOL, SS_AIR, 1, lf->level, NULL);
|
|
}
|
|
break;
|
|
case SJ_ICEMAGE:
|
|
sb1 = addob(lf->pack, "spellbook of cold magic");
|
|
if (!isplayer(lf)) {
|
|
addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL);
|
|
addflag(lf->flags, F_RNDSPELLSCHOOL, SS_COLD, 1, lf->level, NULL);
|
|
}
|
|
break;
|
|
case SJ_FIREMAGE:
|
|
sb1 = addob(lf->pack, "spellbook of fire magic");
|
|
if (!isplayer(lf)) {
|
|
addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL);
|
|
addflag(lf->flags, F_RNDSPELLSCHOOL, SS_FIRE, 1, lf->level, NULL);
|
|
}
|
|
break;
|
|
case SJ_NECROMANCER:
|
|
sb1 = addob(lf->pack, "spellbook of necromancy");
|
|
if (!isplayer(lf)) {
|
|
addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL);
|
|
addflag(lf->flags, F_RNDSPELLSCHOOL, SS_DEATH, 1, lf->level, NULL);
|
|
}
|
|
break;
|
|
case SJ_WILDMAGE:
|
|
sb1 = addob(lf->pack, "spellbook of wild magic");
|
|
if (!isplayer(lf)) {
|
|
addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL);
|
|
addflag(lf->flags, F_RNDSPELLSCHOOL, SS_WILD, 1, lf->level, NULL);
|
|
}
|
|
break;
|
|
// warrior types
|
|
case SJ_BATTLEMAGE:
|
|
// starts off with a grimoire (special code in objects.c will restrict this
|
|
// to only have 3 spells)
|
|
sb1 = addob(lf->pack, "grimoire");
|
|
identify(sb1);
|
|
// can learn some spell schools, but only up to adept level
|
|
addtempflag(lf->flags, F_CANLEARN, SK_SS_FIRE, PR_ADEPT, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_CANLEARN, SK_SS_COLD, PR_ADEPT, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_CANLEARN, SK_SS_AIR, PR_ADEPT, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_CANLEARN, SK_SS_MODIFICATION, PR_ADEPT, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_CANLEARN, SK_SS_TRANSLOCATION, PR_ADEPT, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_CANLEARN, SK_SS_WILD, PR_ADEPT, NA, NULL, FROMJOB);
|
|
// raise IQ to average so that we aren't too dumb to cast L1 spells
|
|
i = rollattr(AT_AVERAGE);
|
|
limit(&i, 50, NA);
|
|
lf->baseatt[A_IQ] = i;
|
|
lf->att[A_IQ] = i;
|
|
// replace mpdice
|
|
killflagsofid(lf->flags, F_MPDICE);
|
|
addtempflag(lf->flags, F_MPDICE, 1, 0, NA, NULL, FROMJOB);
|
|
// remove warrior's hitdice bonus
|
|
killflagsofid(lf->flags, F_MAXHPMOD);
|
|
// remove warrior's level abilities
|
|
killflagsofid(lf->flags, F_LEVABIL);
|
|
// make sure we have the right skills
|
|
for (o = sb1->contents->first ; o ; o = o->next) {
|
|
giveskill(lf, getschoolskill(getspellschool(o->type->id)));
|
|
}
|
|
// un-set sb1 so we don't automatically learn the first spell
|
|
sb1 = NULL;
|
|
break;
|
|
case SJ_PALADIN:
|
|
// extra skills - healing magic & speech
|
|
giveskilllev(lf, SK_SS_LIFE, PR_NOVICE);
|
|
sb1 = addob(lf->pack, "spellbook of life magic");
|
|
giveskilllev(lf, SK_SPEECH, PR_NOVICE);
|
|
|
|
// must worship glorana
|
|
if (isplayer(lf)) {
|
|
lifeform_t *god;
|
|
god = findgod(R_GODLIFE);
|
|
addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL);
|
|
}
|
|
// raise WIS to gtaverage for life magic
|
|
lf->baseatt[A_WIS] = rollattr(AT_GTAVERAGE);
|
|
lf->att[A_WIS] = lf->baseatt[A_WIS];
|
|
// raise CHA to average for speech
|
|
if (getattrbracket(getattr(lf, A_CHA), A_CHA, NULL) < AT_AVERAGE) {
|
|
lf->baseatt[A_CHA] = rollattr(AT_AVERAGE);
|
|
lf->att[A_CHA] = lf->baseatt[A_CHA];
|
|
}
|
|
// can permenantly turn undead for 0 power.
|
|
addtempflag(lf->flags, F_CANWILL, OT_S_TURNUNDEAD, NA, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 5, OT_S_DISRUPTUNDEAD, NA, NULL, FROMJOB);
|
|
if (isplayer(lf)) {
|
|
addflag(lf->flags, F_SHORTCUT, getnextshortcut(lf), NA, NA, "turn undead");
|
|
}
|
|
// all starting gear is blessed
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (isequipped(o)) {
|
|
blessob(o);
|
|
}
|
|
}
|
|
// remove warrior's level abilities
|
|
killflagsofid(lf->flags, F_LEVABIL);
|
|
// monster spells
|
|
if (!isplayer(lf)) {
|
|
addflag(lf->flags, F_RNDSPELLCOUNT, rnd(2,4), NA, NA, NULL);
|
|
addflag(lf->flags, F_RNDSPELLSCHOOL, SS_LIFE, 1, 6, NULL);
|
|
}
|
|
break;
|
|
case SJ_SCOURGE:
|
|
addtempflag(lf->flags, F_RESISTMAG, 5, NA, NA, NULL, FROMJOB);
|
|
// no mp other other magic.
|
|
killflagsofid(lf->flags, F_MPDICE);
|
|
killflagsofid(lf->flags, F_CANCAST);
|
|
f = lfhasflagval(lf, F_HASSKILL, SK_SS_ALLOMANCY, NA, NA, NULL);
|
|
if (f) killflag(f);
|
|
//
|
|
addtempflag(lf->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 2, OT_S_NULLIFY, NA, "pw:1;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 4, OT_S_NULLIFY, NA, "pw:2;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 6, OT_S_NULLIFY, NA, "pw:3;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 8, OT_S_NULLIFY, NA, "pw:4;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 10, OT_S_NULLIFY, NA, "pw:5;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 12, OT_S_NULLIFY, NA, "pw:6;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 14, OT_S_NULLIFY, NA, "pw:7;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 16, OT_S_NULLIFY, NA, "pw:8;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 18, OT_S_NULLIFY, NA, "pw:9;", FROMJOB);
|
|
addtempflag(lf->flags, F_LEVABIL, 20, OT_S_NULLIFY, NA, "pw:10;", FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_ALLOMANCY, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_MENTAL, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_NATURE, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_AIR, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_DEATH, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_DIVINATION, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_FIRE, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_COLD, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_LIFE, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_MODIFICATION, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_SUMMONING, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_TRANSLOCATION, NA, NA, NULL,FROMJOB);
|
|
addtempflag(lastrace->flags, F_NOSKILL, SK_SS_WILD, NA, NA, NULL,FROMJOB);
|
|
|
|
break;
|
|
//
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (sb1) {
|
|
if (isplayer(lf)) {
|
|
addtempflag(lf->flags, F_CANCAST, sb1->contents->first->type->id, NA, NA, NULL, FROMJOB);
|
|
addtempflag(lf->flags, F_SHORTCUT, getnextshortcut(lf), NA, NA, sb1->contents->first->type->name, FROMJOB);
|
|
addflag(sb1->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL);
|
|
identify(sb1);
|
|
} else {
|
|
object_t *o;
|
|
// monster generally know all spells from the book
|
|
for (o = sb1->contents->first ; o ; o = o->next) {
|
|
int lev;
|
|
lev = getspelllevel(o->type->id);
|
|
if (!cancast(lf, o->type->id, NULL) && (gettr(lf) >= lev)) {
|
|
int pow;
|
|
char pwbuf[BUFLEN];
|
|
pow = MINOF((gettr(lf)/2), getspellmaxpower(o->type->id));
|
|
sprintf(pwbuf, "pw:%d;", pow);
|
|
addflag(lf->flags, F_CANCAST, o->type->id, NA, NA, pwbuf);
|
|
}
|
|
}
|
|
// chance of spellbook vanishing
|
|
if (pctchance(66)) {
|
|
killob(sb1);
|
|
sb1 = 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 (gettechlevel(o->type->id) > getskill(lf, SK_TECHUSAGE)) {
|
|
return;
|
|
}
|
|
|
|
if (o->pile->owner == lf) held = B_TRUE;
|
|
|
|
if (held) {
|
|
f = hasflag(o->flags, F_EQUIPPED);
|
|
// make sure it's equipped in the right place - ie. weilded rings don't
|
|
// do anything.
|
|
if (f) {
|
|
if (hasflagval(o->flags, F_GOESON, f->val[0], NA, NA, NULL)) {
|
|
equipped = B_TRUE;
|
|
} else if ((f->val[0] == BP_WEAPON) || (f->val[0] == BP_SECWEAPON)) {
|
|
if (isweapon(o)) {
|
|
equipped = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (held && hasflag(o->flags, F_ACTIVATED)) {
|
|
activated = B_TRUE;
|
|
}
|
|
|
|
if (whattype == F_EQUIPCONFER) {
|
|
if (!equipped) {
|
|
return;
|
|
} else {
|
|
lifetimeval = FROMOBEQUIP;
|
|
}
|
|
} else if (whattype == F_HOLDCONFER) {
|
|
if (!held) {
|
|
return;
|
|
} else {
|
|
lifetimeval = FROMOBHOLD;
|
|
}
|
|
} else if (whattype == F_ACTIVATECONFER) {
|
|
if (!activated) {
|
|
return;
|
|
} else {
|
|
lifetimeval = FROMOBACTIVATE;
|
|
}
|
|
} else {
|
|
assert(1 == 0);
|
|
}
|
|
|
|
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, B_FALSE, o->id);
|
|
if (newflag->known) { // ie. if we found out about it through it being announced
|
|
flagsknown++;
|
|
}
|
|
flagsfound++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if all conferred flags now known, object is known
|
|
if (flagsfound && (flagsknown == flagsfound) && !isknown(o)) {
|
|
int willmakeknown = B_FALSE;
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
willmakeknown = B_TRUE;
|
|
}
|
|
if (willmakeknown) {
|
|
makeknown(o->type->id);
|
|
// in some cases, identify the object fully
|
|
// (ie. make +xxx bonuses known too)
|
|
if (hasflag(o->flags, F_IDWHENUSED)) {
|
|
identify(o);
|
|
}
|
|
if (isplayer(lf)) {
|
|
char buf[BUFLEN];
|
|
getobname(o, buf, o->amt);
|
|
msgnocap("%s %s!", (o->amt == 1) ? "This is" : "These are", buf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int giverandomobs(lifeform_t *lf, int amt) {
|
|
char buf[BUFLEN];
|
|
int i,ngiven = 0;
|
|
if (!lfhasflag(lf, F_HUMANOID) || lfhasflag(lf, F_NOPACK)) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < amt; i++) {
|
|
if (getrandomob(lf->cell->map, buf)) {
|
|
object_t *o;
|
|
o = addob(lf->pack, buf);
|
|
if (o) {
|
|
if (!canpickup(lf, o, o->amt)) {
|
|
killob(o);
|
|
} else {
|
|
ngiven++;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
return ngiven;
|
|
}
|
|
|
|
flag_t *giveskill(lifeform_t *lf, enum SKILL id) {
|
|
flag_t *f = NULL, *newf;
|
|
skill_t *sk;
|
|
int markasused = B_FALSE;
|
|
|
|
switch (id) {
|
|
case SK_CARTOGRAPHY:
|
|
case SK_PERCEPTION:
|
|
markasused = B_TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
if (lfhasflagval(lf, F_NOSKILL, id, NA, NA, NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
sk = findskill(id);
|
|
if (!sk) {
|
|
return NULL;
|
|
}
|
|
|
|
f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL);
|
|
if (f) {
|
|
// already have the skill - make it better
|
|
if (f->val[1] < PR_MASTER) {
|
|
f->val[1]++;
|
|
f->val[2] = markasused;
|
|
}
|
|
if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) {
|
|
msg("^gYou have learned the %s %s skill!", getskilllevelname(f->val[1]), getskillname(sk->id));
|
|
more();
|
|
}
|
|
statdirty = B_TRUE; // in case skill changes your stats
|
|
} else {
|
|
// gaining a new skill
|
|
f = addflag(lf->flags, F_HASSKILL, id, PR_NOVICE, markasused, NULL);
|
|
if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) {
|
|
msg("^gYou have learned the %s %s skill!", getskilllevelname(PR_NOVICE), getskillname(sk->id));
|
|
more();
|
|
}
|
|
|
|
// special effects for gaining a skill.
|
|
|
|
// remember that these from from a SKILL, so that
|
|
// they are invalidated if we get polymorphed.
|
|
if (id == SK_ATHLETICS) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_CLIMBING) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_CLIMB, NA, NA, NULL);
|
|
if (!newf || (newf->lifetime > 0)) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_CLIMB, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_CLUBS) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_STRIKETOKO, NA, NA, NULL);
|
|
if (!newf || (newf->lifetime > 0)) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_STRIKETOKO, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_COOKING) {
|
|
if (isplayer(lf)) {
|
|
makeknown(OT_POT_WATER);
|
|
makeknown(OT_POT_JUICE);
|
|
makeknown(OT_POT_RUM);
|
|
}
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_COOK, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_COOK, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_LORE_ARCANA) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_LOCKPICKING) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_PICKLOCK, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_PICKLOCK, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_METALWORK) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_SEWING) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_STEALTH) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_THIEVERY) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_STEAL, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_STEAL, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_TRAPS) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_DISARM, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
|
|
// learning a new spell school skill after the game has started will grant
|
|
// you a random first level spell from that school. (player and allies only)
|
|
if (isspellskill(id) && (gamemode == GM_GAMESTARTED)) {
|
|
if (isplayer(lf) || areallies(player, lf)) {
|
|
enum OBTYPE oid;
|
|
int tries = 0;
|
|
oid = getrandomspellfromschool(getskillschool(id), 1);
|
|
while (cancast(lf, oid, NULL) && (tries < 3)) {
|
|
oid = getrandomspellfromschool(getskillschool(id), 1);
|
|
tries++;
|
|
}
|
|
if (oid != OT_NONE) {
|
|
addflag(lf->flags, F_CANCAST, oid, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
statdirty = B_TRUE; // in case skill changes your stats
|
|
|
|
}
|
|
|
|
|
|
// special effects based on skill level
|
|
if (isplayer(lf) && isloreskill(id) && (f->val[1] == PR_ADEPT)) {
|
|
race_t *r;
|
|
// at adept lore skill, you can look up this kind of race in '?r'
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
if (r->raceclass->skill == id) r->known = B_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
if (id == SK_ATHLETICS) {
|
|
if (f->val[1] == PR_ADEPT) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_TUMBLE, NA, NA, NULL, FROMSKILL);
|
|
} else if (f->val[1] == PR_EXPERT) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_JUMP, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_CARTOGRAPHY) {
|
|
if (f->val[1] == PR_SKILLED) {
|
|
addtempflag(lf->flags, F_PHOTOMEM, B_TRUE, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
if (f->val[1] == PR_MASTER) {
|
|
if (!hasflagval(lf->flags, F_CANWILL, OT_S_MAPPING, NA, NA, NULL)) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_S_MAPPING, 50, 50, "pw:1;", FROMSKILL);
|
|
}
|
|
}
|
|
} else if (id == SK_COOKING) {
|
|
if (f->val[1] == PR_ADEPT) {
|
|
if (isplayer(lf)) {
|
|
makeknown(OT_MUSHROOMSHI);
|
|
makeknown(OT_MUSHROOMTOAD);
|
|
}
|
|
}
|
|
} else if (id == SK_EVASION) {
|
|
if (f->val[1] == PR_ADEPT) {
|
|
if (isplayer(lf)) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_SNATCH, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (f->val[1] == PR_SKILLED) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_REFLEXDODGE, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_REFLEXDODGE, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
}
|
|
} else if (id == SK_LORE_ARCANA) {
|
|
if (f->val[1] == PR_ADEPT) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
}
|
|
} else if (id == SK_LORE_NATURE) {
|
|
if (f->val[1] == PR_ADEPT) {
|
|
if (isplayer(lf)) {
|
|
makeknown(OT_MUSHROOMSHI);
|
|
makeknown(OT_MUSHROOMTOAD);
|
|
}
|
|
}
|
|
} else if (id == SK_METALWORK) {
|
|
if (f->val[1] == PR_SKILLED) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (f->val[1] == PR_MASTER) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL, 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_SEWING) {
|
|
if (f->val[1] == PR_SKILLED) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (f->val[1] == PR_MASTER) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
}
|
|
} else if (id == SK_SHIELDS) {
|
|
if (f->val[1] == PR_BEGINNER) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_SHIELDBASH, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (id == SK_TECHUSAGE) {
|
|
if (isplayer(lf)) {
|
|
objecttype_t *ot;
|
|
// automatically make known all tech <= our skill level
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
// if objecttype is not known...
|
|
if (!isknownot(ot)) {
|
|
flag_t *tf;
|
|
tf = hasflag(ot->flags, F_TECHLEVEL);
|
|
// if objecttype has a tech level , and it is
|
|
// lower (or equal to) our tech knowledge...
|
|
if (tf && !isknownot(ot) && (tf->val[0] <= f->val[1])) {
|
|
object_t *o;
|
|
// then make it known!
|
|
makeknown(ot->id);
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (o->type->id == ot->id) {
|
|
if (isplayer(lf)) {
|
|
char buf[BUFLEN];
|
|
getobname(o, buf, o->amt);
|
|
msgnocap("%c - %s", o->letter, buf);
|
|
}
|
|
// now confer effects...
|
|
giveobflags(lf, o, F_HOLDCONFER);
|
|
if (isactivated(o)) {
|
|
giveobflags(lf, o, F_ACTIVATECONFER);
|
|
}
|
|
if (isequipped(o)) {
|
|
giveobflags(lf, o, F_EQUIPCONFER);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (id == SK_TWOWEAPON) {
|
|
if (f->val[1] == PR_EXPERT) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_FLURRY, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_FLURRY, NA, NA, "pw:1;", FROMSKILL);
|
|
}
|
|
}
|
|
} else if (id == SK_UNARMED) {
|
|
if (f->val[1] == PR_EXPERT) {
|
|
newf = hasflagval(lf->flags, F_CANWILL, OT_A_FLIP, NA, NA, NULL);
|
|
if (!newf) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_FLIP, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
}
|
|
} else if (id == SK_SS_ALLOMANCY) {
|
|
// give all allomantic spells
|
|
//mayusespellschool(lf->flags, SS_ALLOMANCY, F_CANCAST , B_FALSE);
|
|
} 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_BEGINNER) {
|
|
if (!hasflagval(lf->flags, F_CANWILL, OT_A_EXPOSEDSTRIKE, NA, NA, NULL)) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_EXPOSEDSTRIKE, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (f->val[1] == PR_ADEPT) {
|
|
if (!hasflagval(lf->flags, F_CANWILL, OT_A_ALTERATTACK, NA, NA, NULL)) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_ALTERATTACK, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
} else if (f->val[1] == PR_MASTER) {
|
|
if (!hasflagval(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL)) {
|
|
newf = addtempflag(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL, FROMSKILL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// announecments based on skill level
|
|
if ( (gamemode == GM_GAMESTARTED) && isplayer(lf)) {
|
|
int i;
|
|
for (i = 0; i < sk->nskilldesc; i++) {
|
|
if (sk->skilldesclev[i] == f->val[1]) { // does our proficiency need a description?
|
|
if (sk->skilldescmsg[i]) {
|
|
msg(sk->skilldesctext[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((gamemode == GM_GAMESTARTED) && statdirty) {
|
|
// redraw it right away.
|
|
drawstatus();
|
|
}
|
|
|
|
return f;
|
|
}
|
|
|
|
flag_t *giveskilllev(lifeform_t *lf, enum SKILL id, enum SKILLLEVEL slev) {
|
|
flag_t *f = NULL;
|
|
f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL);
|
|
while (!f || (f->val[1] < slev)) {
|
|
// give another rank
|
|
f = giveskill(lf, id);
|
|
if (!f) break;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
// give start objects from a particular flagpile
|
|
// only give EITHER lf OR targob
|
|
void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) {
|
|
object_t *o = NULL;
|
|
flag_t *f;
|
|
char buf[BUFLEN],buf2[BUFLEN];
|
|
int db = B_FALSE;
|
|
obpile_t *op;
|
|
map_t *targmap;
|
|
enum LFSIZE maxobsize = SZ_MAX;
|
|
int isshop = B_FALSE;
|
|
|
|
if (targob) {
|
|
cell_t *c;
|
|
op = targob->contents;
|
|
c = getoblocation(targob);
|
|
targmap = c->map;
|
|
maxobsize = getobsize(targob);
|
|
if (hasflag(targob->flags, F_SHOP)) isshop = B_TRUE;
|
|
} 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;
|
|
flag_t *f2;
|
|
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;
|
|
char ch = 'a';
|
|
|
|
// note: we use getplayername here even if this isn't a player,
|
|
// since in that case the prompt will never be displayed.
|
|
getplayername(buf2);
|
|
snprintf(buf, BUFLEN, "%s, select your starting weapon:", buf2);
|
|
initprompt(&prompt, buf);
|
|
|
|
for (i = 0; i < nposs; i++) {
|
|
char thisdesc[BUFLEN];
|
|
int dam,acc;
|
|
enum DAMTYPE dt;
|
|
|
|
f2 = hasflag(poss[i]->flags, F_ACCURACY);
|
|
acc = f2->val[0];
|
|
f2 = hasflag(poss[i]->flags, F_DAM);
|
|
dt = f2->val[0];
|
|
dam = f2->val[1];
|
|
sprintf(thisdesc, "%s (Damage: %d %s, Accuracy: %s)", poss[i]->name,
|
|
dam, getdamname(dt), getaccuracyname(acc));
|
|
addchoice(&prompt, ch++, thisdesc, 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;
|
|
}
|
|
}
|
|
|
|
if (ot) {
|
|
skill_t *sk;
|
|
object_t *o;
|
|
// give that weapon
|
|
o = addobfast(lf->pack, ot->id);
|
|
if (isplayer(lf)) {
|
|
addflag(o->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL);
|
|
identify(o);
|
|
}
|
|
|
|
// give one extra rank of skill in this weapon
|
|
sk = getobskill(o->flags);
|
|
giveskill(lf, sk->id);
|
|
|
|
// warriors get another rank
|
|
if (hasjob(lf, J_WARRIOR)) {
|
|
giveskill(lf, sk->id);
|
|
}
|
|
}
|
|
}
|
|
} // end if lf && selectweapon
|
|
|
|
|
|
// 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 (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, SK_NONE, B_TRUE, OC_NONE, DT_NONE, F_NONE)) {
|
|
if (isshop) apply_shopob_restrictions(buf);
|
|
o = addob(op, buf);
|
|
}
|
|
}
|
|
} else if (id == F_STARTOBDT) {
|
|
if (rnd(1,100) <= val[0]) {
|
|
int depthmod;
|
|
if (db) {
|
|
snprintf(buf2, BUFLEN, "calling startobdt");
|
|
}
|
|
|
|
depthmod = val[2];
|
|
switch (depthmod) {
|
|
case NA: depthmod = 0; break;
|
|
case RANDOM: depthmod = rnd(0,MAXDEPTH); break;
|
|
default: break;
|
|
}
|
|
if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, SK_NONE, B_TRUE, OC_NONE, val[1], DT_NONE, F_NONE)) {
|
|
if (db) snprintf(buf2, BUFLEN, "finished startobdt successfuly.");
|
|
if (isshop) apply_shopob_restrictions(buf);
|
|
o = addob(op, buf);
|
|
} else {
|
|
if (db) snprintf(buf2, BUFLEN, "finished startobdt, failed.");
|
|
}
|
|
//assert(strlen(buf) > 0);
|
|
}
|
|
} else if (id == F_STARTOBWEPSK) {
|
|
if (rnd(1,100) <= val[0]) {
|
|
int depthmod;
|
|
if (db) {
|
|
snprintf(buf2, BUFLEN, "calling startobwepsk");
|
|
}
|
|
|
|
depthmod = val[2];
|
|
switch (depthmod) {
|
|
case NA: depthmod = 0; break;
|
|
case RANDOM: depthmod = rnd(0,MAXDEPTH); break;
|
|
default: break;
|
|
}
|
|
if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, val[1], B_TRUE, OC_NONE, DT_NONE, F_NONE)) {
|
|
char buf3[BUFLEN];
|
|
if (db) snprintf(buf2, BUFLEN, "finished startobwepsk successfuly.");
|
|
sprintf(buf3, "%s%s%s",text,strlen(text) ? " " : "",buf);
|
|
o = addob(op, buf3);
|
|
} else {
|
|
if (db) snprintf(buf2, BUFLEN, "finished startobwepsk, failed.");
|
|
}
|
|
}
|
|
} else if (id == F_STARTOBCLASS) {
|
|
if (rnd(1,100) <= val[0]) {
|
|
int depthmod;
|
|
if (db) {
|
|
snprintf(buf2, BUFLEN, "calling startobclass");
|
|
}
|
|
|
|
depthmod = val[2];
|
|
switch (depthmod) {
|
|
case NA: depthmod = 0; break;
|
|
case RANDOM: depthmod = rnd(0,MAXDEPTH); break;
|
|
default: break;
|
|
}
|
|
|
|
//obdb = B_TRUE;
|
|
if (real_getrandomob(targmap, buf, getmapdifficulty(targmap) + depthmod, NA, maxobsize, SK_NONE, B_TRUE,
|
|
val[1], OC_NONE, DT_NONE, F_NONE)) {
|
|
if (db) snprintf(buf2, BUFLEN, "finished startobclass, success.");
|
|
if (isshop) apply_shopob_restrictions(buf);
|
|
o = addob(op, buf);
|
|
} else {
|
|
//obdb = B_FALSE;
|
|
if (db) snprintf(buf2, BUFLEN, "finished startobclass. couldnt find an object.");
|
|
}
|
|
}
|
|
} // end what is fid?
|
|
|
|
// TODO: maybe add object to temp cell, then MOVE it to the destination pile.
|
|
// this will make sure the right checks happen.
|
|
|
|
// some things aren't possible...
|
|
if (o) {
|
|
if (!obfits(o, op)) {
|
|
killob(o);
|
|
o = NULL;
|
|
} else if (op->parentob && hasflag(o->flags, F_CONTAINER)) {
|
|
// don't put containers in other containers
|
|
killob(o);
|
|
o = NULL;
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
} else if (isshop) {
|
|
flag_t *f2;
|
|
// all flags are known
|
|
for (f2 = o->flags->first ; f2; f2 = f2->next) {
|
|
f2->known = B_TRUE;
|
|
}
|
|
}
|
|
} // end if o
|
|
} // end foreach container flag
|
|
|
|
|
|
// now remove startob flags so we don't get them again!
|
|
killflagsofid(fp, F_STARTOB);
|
|
killflagsofid(fp, F_STARTOBDT);
|
|
killflagsofid(fp, F_STARTOBCLASS);
|
|
killflagsofid(fp, F_STARTOBWEPSK);
|
|
|
|
if (lf) {
|
|
// SPECIAL CASES GO HERE.
|
|
if (lf->race->id == R_JAILER) {
|
|
o = addob(lf->pack, "map to the goblin caves");
|
|
assert(o);
|
|
}
|
|
|
|
// make sure lf doesn't start off burdened!
|
|
while (isburdened(lf)) {
|
|
modattr(lf, A_STR, 1); // get stronger
|
|
}
|
|
}
|
|
}
|
|
|
|
void givestartskills(lifeform_t *lf, flagpile_t *fp) {
|
|
flag_t *f;
|
|
int i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
getflags(fp, retflag, &nretflags, F_STARTSKILL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
|
|
if (f->id == F_STARTSKILL) {
|
|
int wantval,n;
|
|
wantval = f->val[1];
|
|
for (n = 0; n < wantval; n++) {
|
|
//while (getskill(lf, f->val[0]) < wantval) {
|
|
giveskill(lf, f->val[0]);
|
|
}
|
|
}
|
|
}
|
|
// now remove startskill flags so we don't get them again!
|
|
killflagsofid(fp, F_STARTSKILL);
|
|
|
|
// all races know about their own race
|
|
if (getlorelevel(lf, lf->race->raceclass->id) < PR_NOVICE) {
|
|
giveskilllev(lf, lf->race->raceclass->skill, PR_NOVICE);
|
|
}
|
|
}
|
|
|
|
int gotosleep(lifeform_t *lf, int onpurpose) {
|
|
char lightid[BUFLEN];
|
|
strcpy(lightid, "");
|
|
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));
|
|
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) {
|
|
object_t *o;
|
|
// turn off light sources first
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
if (hasflag(o->flags, F_LIGHTSOURCE) && hasflag(o->flags, F_ACTIVATED)) {
|
|
if (!strlen(lightid)) {
|
|
// first one.
|
|
if (isplayer(lf)) {
|
|
char ch;
|
|
ch = askchar("Turn off your light sources before resting?", "yn","y", B_TRUE, B_FALSE);
|
|
if (ch != 'y') break;
|
|
}
|
|
sprintf(lightid, "%ld", o->id);
|
|
}
|
|
turnoff(lf, o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
addflag(lf->flags, F_ASLEEP, B_TRUE, lfhasflag(lf, F_MEDITATES) ? ST_MEDITATING : ST_ASLEEP, onpurpose ? B_TRUE : NA, lightid);
|
|
return B_FALSE;
|
|
}
|
|
|
|
void growhydrahead(lifeform_t *lf, int announce) {
|
|
flag_t *f;
|
|
char vname[BUFLEN];
|
|
int dr = 0;
|
|
f = lfhasflagval(lf, F_HASATTACK, OT_TEETH, NA, NA, NULL);
|
|
dr = f->val[1]; // remember dr
|
|
if (announce) {
|
|
// remove one hasattack flag so that the name string
|
|
// is correct.
|
|
killflag(f);
|
|
getlfname(lf, vname);
|
|
// regrow
|
|
if (cansee(player, lf)) {
|
|
msg("^%c%s grow%s two more heads!", getlfcol(lf, CC_GOOD), vname, isplayer(lf) ? "" : "s");
|
|
}
|
|
}
|
|
|
|
if (announce) {
|
|
// add two more attack flags, since we removed one before.
|
|
addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE);
|
|
addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE);
|
|
} else {
|
|
// just add one
|
|
addtempflag(lf->flags, F_HASATTACK, OT_TEETH, dr, NA, NULL, FROMRACE);
|
|
}
|
|
// also add extra hp
|
|
lf->maxhp += HITDIESIDES;
|
|
gainhp(lf, HITDIESIDES);
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
// adjust TR
|
|
f = hasflag(lf->flags, F_TR);
|
|
f->val[0]++;
|
|
}
|
|
|
|
void losehydrahead(lifeform_t *lf) {
|
|
flag_t *f;
|
|
int dr = 0;
|
|
f = lfhasflagval(lf, F_HASATTACK, OT_TEETH, NA, NA, NULL);
|
|
dr = f->val[1]; // remember dr
|
|
// remove one hasattack flag
|
|
killflag(f);
|
|
|
|
// still more heads left?
|
|
if (countflagsofid(lf->flags, F_HASATTACK)) {
|
|
// adjust maxhp
|
|
lf->maxhp -= HITDIESIDES;
|
|
if (lf->hp > lf->maxhp) lf->hp = lf->maxhp;
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
|
|
// adjust TR
|
|
f = hasflag(lf->flags, F_TR);
|
|
f->val[0]--;
|
|
} else {
|
|
// die!
|
|
lf->lastdamtype = DT_DIRECT;
|
|
setlastdam(lf, "decapitation");
|
|
lf->hp = 0;
|
|
}
|
|
}
|
|
|
|
flag_t *hasbleedinginjury(lifeform_t *lf, enum BODYPART bp) {
|
|
flag_t *f, *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
|
|
for (i = 0;i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (isplayer(lf) && (f->lifetime < 0)) {
|
|
// for the player, only temporary flags count - not permenant ones
|
|
// this is so that losing a whole finger etc doesn't completely
|
|
// cripple you by making you always bleed.
|
|
} else if ((f->val[1] == bp) && (f->val[2] == DT_SLASH)) {
|
|
return f;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int hasfreeaction(lifeform_t *lf) {
|
|
return real_hasfreeaction(lf, F_NONE);
|
|
}
|
|
int real_hasfreeaction(lifeform_t *lf, enum FLAG exception) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
if (isimmobile(lf)) return B_FALSE;
|
|
getflags(lf->flags, retflag, &nretflags, F_ASLEEP, F_CASTINGSPELL, F_DIGGING, F_EATING, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->id != exception) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int hashealableinjuries(lifeform_t *lf) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
int injurycount = 0;
|
|
// has injuries which can heal?
|
|
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->lifetime > 0) injurycount++;
|
|
}
|
|
return injurycount;
|
|
}
|
|
|
|
job_t *hasjob(lifeform_t *lf, enum JOB job) {
|
|
job_t *j = NULL;
|
|
if (lfhasflagval(lf, F_JOB, job, NA, NA, NULL)) {
|
|
j = findjob(job);
|
|
}
|
|
return j;
|
|
}
|
|
|
|
int hastempinjuries(lifeform_t *lf) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i,count = 0;
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->lifetime > 0) count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int hassubjob(lifeform_t *lf, enum SUBJOB id) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_JOB);
|
|
if (f && (f->val[1] == id)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void inc_quad_range(enum QUADRANT *start, enum QUADRANT *end, int howmuch) {
|
|
int i;
|
|
for (i = 0; i < abs(howmuch); i++) {
|
|
if (start) {
|
|
if (*start == Q_NNE) *start = Q_NNW;
|
|
else (*start)--;
|
|
}
|
|
if (end) {
|
|
if (*end == Q_NNW) *end = Q_NNE;
|
|
else (*end)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// where shoudl always be body, hands, legs or head
|
|
// damtype should be bash or slash
|
|
//
|
|
// if forcetype is not supplied, injury type will be selected randomly.
|
|
int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype, enum INJURY forcetype) {
|
|
char lfname[BUFLEN],buf[BUFLEN];
|
|
char *desc = NULL;
|
|
enum INJURY inj = IJ_NONE;
|
|
enum BODYPART bp2 = BP_NONE;
|
|
object_t *wep = NULL,*o;
|
|
int howlong;
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
o = hasequippedobid(lf->pack, OT_RING_NOINJURY);
|
|
if (o) {
|
|
char obname[BUFLEN];
|
|
int seen = B_FALSE;
|
|
getobname(o, obname, 1);
|
|
if (isplayer(lf)) {
|
|
msg("Your %s throbs.", noprefix(obname));
|
|
seen = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s%s %s throbs.", lfname, getpossessive(lfname), obname);
|
|
seen = B_TRUE;
|
|
}
|
|
if (seen && !isknown(o)) {
|
|
makeknown(o->type->id);
|
|
if (isplayer(lf)) {
|
|
msg("Amazingly, your %s is unharmed!", getbodypartname(lf, where));
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (where == BP_NONE) return B_TRUE;
|
|
|
|
if (!hasbp(lf, where)) {
|
|
if (where == BP_LEGS) {
|
|
// adjust bp_legs into bp_frontlegs/bp_backelgs
|
|
if (hasbp(lf, BP_FRONTLEGS) || hasbp(lf, BP_BACKLEGS)) {
|
|
// ok.
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
if (lfhasflag(lf, F_NOINJURIES)) return B_TRUE;
|
|
if (lfhasflagval(lf, F_INJURY, NA, where, NA, NULL)) return B_TRUE;
|
|
|
|
howlong = rnd(30,80); // might be overridden depending on injury
|
|
|
|
if (forcetype) {
|
|
inj = forcetype;
|
|
} else {
|
|
if (damtype == DT_BASH) {
|
|
switch (where) {
|
|
case BP_BODY:
|
|
switch (rnd(1,5)) {
|
|
case 1:
|
|
inj = IJ_RIBCRACKED; break;
|
|
case 2:
|
|
inj = IJ_RIBBROKEN; break;
|
|
case 3:
|
|
inj = IJ_TORSOBRUISED; break;
|
|
case 4:
|
|
inj = IJ_TORSOBRUISEDBAD; break;
|
|
case 5:
|
|
inj = IJ_WINDED; break;
|
|
}
|
|
break;
|
|
case BP_HANDS:
|
|
switch (rnd(1,3)) {
|
|
case 1:
|
|
inj = IJ_FINGERBROKEN; break;
|
|
case 2:
|
|
inj = IJ_SHOULDERDISLOCATED; break;
|
|
case 3:
|
|
inj = IJ_HANDSWOLLEN; break;
|
|
}
|
|
break;
|
|
case BP_HEAD:
|
|
switch (rnd(1,4)) {
|
|
case 1:
|
|
inj = IJ_BLACKEYE;
|
|
break;
|
|
case 2:
|
|
inj = IJ_CONCUSSION;
|
|
break;
|
|
case 3:
|
|
inj = IJ_WINDPIPECRUSHED;
|
|
break;
|
|
case 4:
|
|
inj = IJ_NOSEBROKEN;
|
|
break;
|
|
}
|
|
break;
|
|
case BP_LEGS:
|
|
if (onein(3)) {
|
|
inj = IJ_LEGBROKEN;
|
|
break;
|
|
} else {
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
inj = IJ_LEGBRUISE;
|
|
break;
|
|
case 2:
|
|
inj = IJ_ANKLESWOLLEN;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case BP_TAIL:
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
inj = IJ_TAILBRUISED;
|
|
break;
|
|
case 2:
|
|
inj = IJ_TAILBROKEN;
|
|
break;
|
|
}
|
|
break;
|
|
case BP_WINGS:
|
|
inj = IJ_WINGBRUISED;
|
|
break;
|
|
default: break;
|
|
}
|
|
} else if (damtype == DT_SLASH) {
|
|
switch (where) {
|
|
case BP_BODY:
|
|
if (pctchance(10)) {
|
|
inj = IJ_HEARTPIERCED;
|
|
} else {
|
|
inj = IJ_CHESTBLEED; break;
|
|
}
|
|
break;
|
|
case BP_HANDS:
|
|
switch (rnd(1,4)) {
|
|
case 1:
|
|
inj = IJ_HANDBLEED; break;
|
|
case 2:
|
|
inj = IJ_TENDONCUT; break;
|
|
case 3:
|
|
inj = IJ_ARTERYPIERCE; break;
|
|
case 4: // severed finger
|
|
inj = IJ_FINGERMISSING; break;
|
|
}
|
|
break;
|
|
case BP_HEAD:
|
|
if (pctchance(10)) {
|
|
inj = IJ_BRAINRUPTURED;
|
|
} else {
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
inj = IJ_EYELIDSCRAPED;
|
|
break;
|
|
case 2:
|
|
inj = IJ_EYEDESTROYED;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case BP_LEGS:
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
inj = IJ_LEGBLEED;
|
|
break;
|
|
case 2:
|
|
inj = IJ_HAMSTRUNG;
|
|
break;
|
|
}
|
|
break;
|
|
case BP_TAIL:
|
|
inj = IJ_TAILBLEED;
|
|
break;
|
|
case BP_WINGS:
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
inj = IJ_WINGTORN;
|
|
break;
|
|
case 2:
|
|
inj = IJ_WINGBLEED;
|
|
break;
|
|
}
|
|
default: break;
|
|
}
|
|
} else if (damtype == DT_EXPLOSIVE) {
|
|
switch (where) {
|
|
case BP_BODY:
|
|
switch (rnd(1,3)) {
|
|
case 1: // collapsed lung
|
|
inj = IJ_LUNGCOLLAPSED;
|
|
break;
|
|
case 2:
|
|
inj = IJ_RIBCRACKED; break;
|
|
case 3:
|
|
inj = IJ_RIBBROKEN; break;
|
|
}
|
|
break;
|
|
case BP_HANDS:
|
|
inj = IJ_HANDMISSING;
|
|
break;
|
|
case BP_HEAD:
|
|
switch (rnd(1,2)) {
|
|
case 1: // ringing ears
|
|
inj = IJ_EARSRINGING; break;
|
|
case 2: // blinded
|
|
inj = IJ_BLINDED;
|
|
break;
|
|
}
|
|
break;
|
|
case BP_LEGS:
|
|
// lose limb
|
|
break;
|
|
case BP_TAIL:
|
|
inj = IJ_TAILLACERATED;
|
|
break;
|
|
case BP_WINGS:
|
|
inj = IJ_WINGDESTROYED;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// some injuries have extra effects before announcing the injury...
|
|
switch (inj) {
|
|
case IJ_BLACKEYE:
|
|
if (eyesshaded(lf)) {
|
|
inj = IJ_NONE;
|
|
}
|
|
break;
|
|
case IJ_BLINDED:
|
|
addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(50,100));
|
|
inj = IJ_NONE;
|
|
break;
|
|
case IJ_BRAINRUPTURED:
|
|
if (isplayer(lf)) {
|
|
msg("^BYour brain is ruptured!");
|
|
} else if (cansee(player, lf)) {
|
|
msg("^%c%s%s brain ruptures!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname));
|
|
}
|
|
if (lf->hp > 0) {
|
|
setlastdam(lf, "a ruptured brain");
|
|
lf->lastdamtype = DT_DIRECT;
|
|
lf->hp = 0;
|
|
}
|
|
inj = IJ_NONE;
|
|
break;
|
|
case IJ_HANDMISSING:
|
|
// lose limb
|
|
if (onein(2)) {
|
|
if (hasbp(lf, BP_WEAPON)) bp2 = BP_WEAPON;
|
|
else bp2 = BP_SECWEAPON;
|
|
} else {
|
|
if (hasbp(lf, BP_SECWEAPON)) bp2 = BP_SECWEAPON;
|
|
else bp2 = BP_WEAPON;
|
|
}
|
|
|
|
if (hasbp(lf, bp2)) {
|
|
object_t *o[2];
|
|
enum BODYPART fingerbp;
|
|
int i;
|
|
// drop anyting in that hand
|
|
o[0] = getequippedob(lf->pack, bp2);
|
|
if (bp2 == BP_WEAPON) {
|
|
o[1] = getequippedob(lf->pack, BP_RIGHTFINGER);
|
|
fingerbp = BP_RIGHTFINGER;
|
|
} else {
|
|
o[1] = getequippedob(lf->pack, BP_LEFTFINGER);
|
|
fingerbp = BP_LEFTFINGER;
|
|
}
|
|
addflag(lf->flags, F_NOBODYPART, bp2, B_FROMINJURY, NA, NULL);
|
|
addflag(lf->flags, F_NOBODYPART, fingerbp, B_FROMINJURY, NA, NULL);
|
|
howlong = PERMENANT;
|
|
for (i = 0; i < 2; i++) {
|
|
if (o[i]) {
|
|
char obname[BUFLEN];
|
|
if (isplayer(lf)) {
|
|
getobname(o[i],obname,o[i]->amt);
|
|
msg("Your %s drops to the ground.",noprefix(obname));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getobname(o[i],obname,o[i]->amt);
|
|
msg("%s%s %s drops to the ground.",lfname,getpossessive(lfname),noprefix(obname));
|
|
}
|
|
moveob(o[i], lf->cell->obpile, o[i]->amt);
|
|
}
|
|
}
|
|
} else {
|
|
inj = IJ_NONE;
|
|
}
|
|
break;
|
|
case IJ_HEARTPIERCED:
|
|
if (isplayer(lf)) {
|
|
msg("^BYour heart is pierced!");
|
|
} else if (cansee(player, lf)) {
|
|
msg("^%c%s%s heart is pierced!", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname));
|
|
}
|
|
if (lf->hp > 0) {
|
|
setlastdam(lf, "a pierced heart");
|
|
lf->lastdamtype = DT_DIRECT;
|
|
lf->hp = 0;
|
|
}
|
|
inj = IJ_NONE;
|
|
break;
|
|
case IJ_FINGERMISSING:
|
|
if (onein(2)) {
|
|
if (hasbp(lf, BP_RIGHTFINGER)) bp2 = BP_RIGHTFINGER;
|
|
else bp2 = BP_LEFTFINGER;
|
|
} else {
|
|
if (hasbp(lf, BP_LEFTFINGER)) bp2 = BP_LEFTFINGER;
|
|
else bp2 = BP_RIGHTFINGER;
|
|
}
|
|
|
|
if (hasbp(lf, bp2)) {
|
|
object_t *o;
|
|
addob(lf->cell->obpile, "severed finger");
|
|
o = getequippedob(lf->pack, bp2);
|
|
addflag(lf->flags, F_NOBODYPART, bp2, B_FROMINJURY, NA, NULL);
|
|
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)) {
|
|
getobname(o,obname,o->amt);
|
|
msg("%s%s %s drops to the ground.",lfname,getpossessive(lfname),noprefix(obname));
|
|
}
|
|
moveob(o, lf->cell->obpile, o->amt);
|
|
}
|
|
break;
|
|
} else {
|
|
inj = IJ_NONE;
|
|
}
|
|
break;
|
|
case IJ_EYEDESTROYED:
|
|
case IJ_TAILLACERATED:
|
|
case IJ_WINGDESTROYED:
|
|
howlong = PERMENANT;
|
|
break;
|
|
case IJ_WINDED:
|
|
howlong = rnd(3,5);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// set description based on injury
|
|
switch (inj) {
|
|
case IJ_RIBCRACKED: desc = strdup("ribs are cracked^carrying capacity halved"); break;
|
|
case IJ_RIBBROKEN: desc = strdup("ribs are broken^carrying capacity halved, -6 accuracy"); break;
|
|
case IJ_TORSOBRUISED: desc = strdup("torso is bruised^-2 accuracy"); break;
|
|
case IJ_TORSOBRUISEDBAD: desc = strdup("torso is badly bruised^-4 accuracy, -10% dam"); break;
|
|
case IJ_WINDED: desc = strdup("stomach is winded^-1 Fitness"); break;
|
|
case IJ_BLACKEYE: desc = strdup("eye is bruised^vision range halved"); break;
|
|
case IJ_CONCUSSION: desc = strdup("brain is concussed^random movement"); break;
|
|
case IJ_WINDPIPECRUSHED: desc = strdup("windpipe is crushed^fitness penalty"); break;
|
|
case IJ_NOSEBROKEN: desc = strdup("nose is broken^charisma penalty,reduced smell sense");
|
|
case IJ_LEGBROKEN: desc = strdup("leg is broken^movement speed greatly lowered"); break;
|
|
case IJ_LEGBRUISE: desc = strdup("leg is bruised^movement speed lowered"); break;
|
|
case IJ_ANKLESWOLLEN: desc = strdup("ankle is swollen^cannot wear/remove boots"); break;
|
|
case IJ_TAILBRUISED: desc = strdup("tail is bruised^accuracy penalty"); break;
|
|
case IJ_TAILBROKEN: desc = strdup("tail is fractured^occasional random movement"); break;
|
|
case IJ_WINGBRUISED: desc = strdup("wings are bruised^flight speed lowered"); break;
|
|
case IJ_CHESTBLEED: desc = strdup("chest is bleeding^damage from enemies is increased"); break;
|
|
case IJ_HANDBLEED: desc = strdup("hand is bleeding^attacking will cause damage"); break;
|
|
case IJ_TENDONCUT: desc = strdup("right flexor tendon is cut^cannot weild weapons"); break;
|
|
case IJ_FINGERMISSING:
|
|
sprintf(buf, "%s is severed^cannot wear rings on this hand", getbodypartname(lf, bp2));
|
|
desc = strdup(buf);
|
|
break;
|
|
case IJ_EYELIDSCRAPED: desc = strdup("eyelid is scraped^accuracy penalty"); break;
|
|
case IJ_EYEDESTROYED: desc = strdup("right eye is destroyed^field of view halved"); break;
|
|
case IJ_LEGBLEED: desc = strdup("leg is bleeding^movement will cause damage"); break;
|
|
case IJ_HAMSTRUNG: desc = strdup("left hamstring is torn^lower move speed, chance of falling"); break;
|
|
case IJ_TAILBLEED: desc = strdup("tail is bleeding^no additional effects"); break;
|
|
case IJ_WINGTORN: desc = strdup("wings are torn^cannot fly"); break;
|
|
case IJ_WINGBLEED: desc = strdup("wings are bleeding^flying causes damage"); break;
|
|
case IJ_LUNGCOLLAPSED: desc = strdup("lungs have collapsed^lose all stamina points"); break;
|
|
case IJ_HANDMISSING:
|
|
sprintf(buf, "%s is destroyed^cannot use this hand", getbodypartname(lf, bp2));
|
|
desc = strdup(buf);
|
|
break;
|
|
case IJ_EARSRINGING: desc = strdup("ears are ringing^cannot hear sounds"); break;
|
|
case IJ_TAILLACERATED: desc = strdup("tail is lacerated^chance of falling during movement"); break;
|
|
case IJ_WINGDESTROYED: desc = strdup("wings are destroyed^cannot fly"); break;
|
|
case IJ_HANDSWOLLEN: desc = strdup("hand is swollen^rings cannot be put on/removed"); break;
|
|
case IJ_FINGERBROKEN: desc = strdup("finger is broken^acc penalty"); break;
|
|
case IJ_SHOULDERDISLOCATED: desc = strdup("shoulder is dislocated^acc penalty, cannot use heavy weapons"); break;
|
|
case IJ_ARTERYPIERCE: desc = strdup("radial artery is pierced^constant bleeding"); break; // fatal - no description
|
|
case IJ_BRAINRUPTURED: break; // fatal - no description
|
|
case IJ_HEARTPIERCED: break; // fatal - no description
|
|
case IJ_BLINDED: break; // no injury will be added.
|
|
case IJ_NONE: break;
|
|
}
|
|
|
|
if (inj == IJ_NONE) {
|
|
if (desc) free(desc);
|
|
return B_TRUE;
|
|
} else {
|
|
flag_t *injflag;
|
|
injflag = addtempflag(lf->flags, F_INJURY, inj, where, damtype, desc, howlong);
|
|
injflag->obfrom = B_NEWINJURY;
|
|
}
|
|
if (desc) free(desc);
|
|
|
|
// special effects from taking injuries
|
|
if (damtype == DT_SLASH) bleed(lf, B_SPLATTER);
|
|
|
|
// extra effects after getting the injury.
|
|
switch (inj) {
|
|
case IJ_BLACKEYE:
|
|
case IJ_EYELIDSCRAPED:
|
|
case IJ_EYEDESTROYED:
|
|
if (isplayer(lf)) setlosdirty(lf);
|
|
break;
|
|
case IJ_HAMSTRUNG:
|
|
fall(lf, NULL, B_TRUE);
|
|
break;
|
|
case IJ_SHOULDERDISLOCATED:
|
|
wep = getweapon(lf);
|
|
if (wep && isheavyweapon(wep)) drop(wep, wep->amt);
|
|
break;
|
|
case IJ_TENDONCUT:
|
|
wep = getweapon(lf);
|
|
if (wep) drop(wep, wep->amt);
|
|
break;
|
|
case IJ_WINGTORN:
|
|
killflagsofid(lf->flags, F_FLYING);
|
|
break;
|
|
case IJ_WINGDESTROYED:
|
|
addflag(lf->flags, F_NOBODYPART, BP_WINGS, B_FROMINJURY, NA, NULL);
|
|
break;
|
|
case IJ_WINDED:
|
|
lf->stamina = 0;
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (where == BP_TAIL) {
|
|
if (lf->race->id == R_MANTICORE) {
|
|
flag_t *f;
|
|
// can't use spike volley
|
|
f = hasflagval(lf->flags, F_CANWILL, OT_S_SPIKEVOLLEY, NA, NA, NULL);
|
|
if (f) killflag(f);
|
|
}
|
|
}
|
|
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int lfcanbekod(lifeform_t *lf) {
|
|
if (isundead(lf)) return B_FALSE;
|
|
switch (getraceclass(lf)) {
|
|
case RC_ROBOT:
|
|
case RC_GOD:
|
|
case RC_SLIME:
|
|
case RC_PLANT:
|
|
case RC_OTHER:
|
|
case RC_UNDEAD:
|
|
return B_FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
if (lfhasflag(lf, F_NONCORPOREAL)) {
|
|
return B_FALSE;
|
|
}
|
|
if (isdead(lf)) {
|
|
return B_FALSE;
|
|
}
|
|
// note: not checking whether they are already unconscious
|
|
// because merciful weapons CAN still KO them in this case.
|
|
return B_TRUE;
|
|
}
|
|
|
|
int lfcanbestoned(lifeform_t *lf) {
|
|
switch (getlfmaterial(lf)) {
|
|
case MT_GAS:
|
|
case MT_STONE:
|
|
return B_FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
if (lfhasflag(lf, F_NONCORPOREAL)) {
|
|
return B_FALSE;
|
|
}
|
|
if (isimmuneto(lf->flags, DT_PETRIFY, B_FALSE)) {
|
|
return B_FALSE;
|
|
}
|
|
if (isdead(lf)) {
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// does the lf has this flag, either internally or
|
|
// conferred by a held/equipped object?
|
|
flag_t *lfhasflag(lifeform_t *lf, enum FLAG fid) {
|
|
flag_t *f;
|
|
f = hasflag(lf->flags, fid);
|
|
if (f) return f;
|
|
return NULL;
|
|
}
|
|
|
|
flag_t *lfhasflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, char *text) {
|
|
flag_t *f;
|
|
f = hasflagval(lf->flags, fid, val0, val1, val2, text);
|
|
if (f) return f;
|
|
return NULL;
|
|
}
|
|
|
|
flag_t *lfhasknownflag(lifeform_t *lf, enum FLAG fid) {
|
|
flag_t *f;
|
|
|
|
// do we have this flag directly?
|
|
f = hasflagknown(lf->flags, fid);
|
|
if (f && f->known) return f;
|
|
return NULL;
|
|
}
|
|
|
|
flag_t *lfhasknownflagval(lifeform_t *lf, enum FLAG fid, int val0, int val1, int val2, char *text) {
|
|
flag_t *f;
|
|
// got the flag directly?
|
|
f = hasflagvalknown(lf->flags, fid, val0, val1, val2, text);
|
|
if (f && f->known) return f;
|
|
return NULL;
|
|
}
|
|
|
|
// returns radius of light produces
|
|
int lfproduceslight(lifeform_t *lf, object_t **fromwhat) {
|
|
int temp = 0;
|
|
int radius = 0;
|
|
object_t *o;
|
|
|
|
if (fromwhat) *fromwhat = NULL;
|
|
|
|
// lf producing light itself?
|
|
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;
|
|
if (fromwhat) *fromwhat = o;
|
|
}
|
|
}
|
|
}
|
|
return radius;
|
|
}
|
|
|
|
// if you pass in 'target', then you don't need 'targcell'
|
|
// return true on failure
|
|
int lockpick(lifeform_t *lf, cell_t *targcell, object_t *target, object_t *device) {
|
|
flag_t *f,*lockflag;
|
|
char lfname[BUFLEN];
|
|
char obname[BUFLEN];
|
|
int faileffect;
|
|
int difficulty = 20; // default, never used though
|
|
int bonus = 0;
|
|
char ch;
|
|
|
|
// TODO: for now, only players can lockpick
|
|
if (!isplayer(lf)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
// what will we use?
|
|
if (!device) {
|
|
if (!hasobwithflag(lf->pack, F_PICKLOCKS)) {
|
|
msg("You have nothing to use for lockpicking!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask which object to use
|
|
device = askobjectwithflag(lf->pack, "Lockpick using what", NULL, NULL, 'p', AO_NONE, F_PICKLOCKS);
|
|
if (!device) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else if (!hasflag(device->flags, F_PICKLOCKS)) {
|
|
msg("That can't be used to pick locks!");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!target) {
|
|
object_t *poss[MAXCANDIDATES],*o;
|
|
int nposs = 0,i;
|
|
if (!targcell) {
|
|
int dir;
|
|
ch = askchar("Lockpick in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
|
|
if ((ch == '-') || (ch == '\0')) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
dir = chartodir(ch);
|
|
if (dir == D_NONE) {
|
|
targcell = lf->cell;
|
|
} else {
|
|
targcell = getcellindir(lf->cell, dir);
|
|
}
|
|
if (!targcell) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// get a list of lockpickable obejcts there
|
|
for (o = targcell->obpile->first ; o ; o = o->next) {
|
|
if (hasflag(o->flags, F_LOCKED)) {
|
|
poss[nposs++] = o;
|
|
}
|
|
}
|
|
|
|
if (!nposs) {
|
|
msg("There is nothing here to lockpick!");
|
|
return B_TRUE;
|
|
} else if (nposs == 1) {
|
|
target = poss[0];
|
|
} else {
|
|
// ask which one
|
|
initprompt(&prompt, "What will you try to unlock?");
|
|
ch = 'a';
|
|
for (i = 0 ; i < nposs; i++) {
|
|
char obname[BUFLEN];
|
|
getobname(poss[i], obname, 1);
|
|
addchoice(&prompt, ch++, obname, NULL, poss[i], NULL);
|
|
}
|
|
prompt.maycancel = B_TRUE;
|
|
ch = getchoice(&prompt);
|
|
if (ch != '\0') target = (object_t *)prompt.result;
|
|
if (!target) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// we should now have both 'device' and 'target' filled in.
|
|
lockflag = hasflag(target->flags, F_LOCKED);
|
|
if (lockflag) {
|
|
difficulty = lockflag->val[1];
|
|
} else {
|
|
// should never happen
|
|
if (isplayer(lf)) {
|
|
msg("That isn't locked!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
getlfname(lf,lfname);
|
|
getobname(target,obname, 1);
|
|
|
|
f = hasflag(device->flags, F_PICKLOCKS);
|
|
assert(f);
|
|
bonus = f->val[0];
|
|
if (isblessed(device)) bonus += 5;
|
|
faileffect = f->val[1];
|
|
|
|
// take time
|
|
taketime(lf, getactspeed(lf) );
|
|
|
|
if (skillcheck(lf, SC_OPENLOCKS, difficulty, bonus )) {
|
|
// success!
|
|
// announce
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
msg("%s unlock%s %s.",lfname, isplayer(lf) ? "" : "s", obname);
|
|
}
|
|
// unlock it
|
|
killflagsofid(target->flags, F_LOCKED);
|
|
// xp
|
|
if (isplayer(lf)) {
|
|
if (hasjob(lf, J_ROGUE)) {
|
|
gainxp(lf, difficulty);
|
|
} else {
|
|
gainxp(lf, difficulty/3);
|
|
}
|
|
}
|
|
// training
|
|
practice(lf, SK_LOCKPICKING, 1);
|
|
// gods
|
|
if (isplayer(lf)) pleasegodmaybe(R_GODTHIEVES, 5);
|
|
} else {
|
|
// failed!
|
|
if (faileffect == B_DIEONFAIL) {
|
|
char devname[BUFLEN];
|
|
getobname(device,devname, 1);
|
|
// kill object
|
|
if (isplayer(lf)) {
|
|
msg("^wYour %s breaks!",noprefix(devname));
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s%s %s breaks!",lfname, getpossessive(lfname), noprefix(devname));
|
|
}
|
|
removeob(device, 1);
|
|
} else if (faileffect == B_BLUNTONFAIL) {
|
|
if (!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);
|
|
}
|
|
}
|
|
practice(lf, SK_LOCKPICKING, 1);
|
|
return B_TRUE;
|
|
}
|
|
practice(lf, SK_LOCKPICKING, 1);
|
|
return B_FALSE;
|
|
}
|
|
|
|
void loseobflags(lifeform_t *lf, object_t *o, int kind) {
|
|
flag_t *f,*ff,*nextff;
|
|
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) {
|
|
int i;
|
|
if (lfhasflagval(lf, F_NOBODYPART, bp, NA, NA, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
for (i = 0; i < lf->race->nbodyparts; i++) {
|
|
if (lf->race->bodypart[i].id == bp) return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
flag_t *hasactivespell(lifeform_t *lf, enum OBTYPE sid) {
|
|
return lfhasflagval(lf, F_BOOSTSPELL, sid, NA, NA, NULL);
|
|
}
|
|
|
|
int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) {
|
|
return haslof_real(src, dest, loftype, newdest, NULL, B_TRUE);
|
|
}
|
|
|
|
// got line of fire to dest? if lof is blocked, return last cell in 'newdest'
|
|
// if 'srclf' is set, we are checking whether 'srclf' THINKS they have line of fire. ie invisible/unseed
|
|
// lifeforms don't block lof.
|
|
//
|
|
// if 'walllfsok' is set, then we ARE allowed to have lineoffire to walls (ie. solid cells) if there is a lifeform
|
|
// there.
|
|
int haslof_real(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest, lifeform_t *srclf, int walllfsok) {
|
|
int numpixels;
|
|
int i;
|
|
int x1,y1,x2,y2;
|
|
map_t *map;
|
|
cell_t *retcell[MAXRETCELLS];
|
|
|
|
reason = E_OK;
|
|
|
|
if (loftype == LOF_DONTNEED) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (newdest) *newdest = src;
|
|
|
|
//if (!viewer) return B_FALSE;
|
|
if (!src) return B_FALSE;
|
|
|
|
if (src->map != dest->map) {
|
|
reason = E_NOLOS;
|
|
return B_FALSE;
|
|
}
|
|
map = dest->map;
|
|
|
|
|
|
x1 = src->x;
|
|
y1 = src->y;
|
|
x2 = dest->x;
|
|
y2 = dest->y;
|
|
|
|
// can always fire at your own cell
|
|
if ((x1 == x2) && (y1 == y2)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
calcbresnham(map, x1, y1, x2, y2, retcell, &numpixels);
|
|
|
|
|
|
for (i = 0; i < numpixels ; i++) {
|
|
cell_t *cell;
|
|
object_t *blockob;
|
|
|
|
cell = retcell[i];
|
|
|
|
// we _DO_ need to move out of last cell for line of fire.
|
|
|
|
// if walls don't stop lof, this is now a valid cell.
|
|
if (!(loftype & LOF_WALLSTOP)) {
|
|
if (newdest) *newdest = cell;
|
|
}
|
|
|
|
// solid cells stop lof UNLESS
|
|
// - this is our source cell
|
|
// - there is a lf there and this is our target cell
|
|
|
|
if (loftype & LOF_WALLSTOP) {
|
|
if (cell->type->solid) {
|
|
if (i == 0) {
|
|
// ok
|
|
} else if (walllfsok && cell->lf && (i == (numpixels-1)) ) {
|
|
// ok
|
|
} else {
|
|
reason = E_NOLOF;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
// certain objects block lof
|
|
blockob = hasobwithflag(cell->obpile, F_BLOCKSLOF);
|
|
if (blockob) {
|
|
int blocked = B_TRUE; // default
|
|
int isopen;
|
|
if (isdoor(blockob, &isopen)) {
|
|
if (isopen) {
|
|
// open doors don't block line of fire
|
|
blocked = B_FALSE;
|
|
}
|
|
}
|
|
if (blocked) {
|
|
reason = E_NOLOF;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
// if lifeforms don't stop lof, this is now a valid cell.
|
|
// if (!(loftype & LOF_LFSSTOP)) {
|
|
if (newdest) *newdest = cell;
|
|
// }
|
|
|
|
// lifeforms block lof unless:
|
|
// - they're in the first cell (ie the one doing the throwing/firing/shooting/etc)
|
|
// - they're in our destination
|
|
if (cell->lf && (!isprone(cell->lf)) && (loftype & LOF_LFSSTOP)) {
|
|
int lfcanblocklof = B_TRUE;
|
|
if (srclf && !cansee(srclf, cell->lf)) {
|
|
lfcanblocklof = B_FALSE;
|
|
}
|
|
if (lfcanblocklof) {
|
|
// if not in first or last cell...
|
|
if ((i != 0) && (i != (numpixels-1)) ) {
|
|
reason = E_NOLOF;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
// cell is now valid
|
|
if (newdest) *newdest = cell;
|
|
}
|
|
|
|
// made it to the target cell!
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
void idxtoxy(lifeform_t *lf, int idx, int *x, int *y) {
|
|
int xoffset,yoffset;
|
|
// get the offset form the lf's position.
|
|
xoffset = (idx % lf->visw) - lf->visrange;
|
|
yoffset = (idx / lf->visw) - lf->visrange;
|
|
// return the actual cell there
|
|
*x = lf->cell->x + xoffset;
|
|
*y = lf->cell->y + yoffset;
|
|
}
|
|
|
|
// confirmed good!
|
|
int xytoidx(lifeform_t *lf, int x, int y) {
|
|
int idx;
|
|
int xd,yd;
|
|
|
|
if (abs(x - lf->cell->x) > lf->visrange) return -1;
|
|
if (abs(y - lf->cell->y) > lf->visrange) return -1;
|
|
|
|
xd = lf->cell->x - lf->visrange;
|
|
yd = lf->cell->y - lf->visrange;
|
|
|
|
idx = (y - yd) * lf->visw + (x - xd);
|
|
return idx;
|
|
}
|
|
|
|
/*
|
|
void setviscell(lifeform_t *lf, cell_t *cell, int how) {
|
|
int idx;
|
|
int x,y;
|
|
cell_t *c;
|
|
idx = xytoidx(lf, cell->x, cell->y);
|
|
assert(idx >= 0);
|
|
assert(idx < (lf->visw * lf->visw));
|
|
lf->viscell[idx] = how;
|
|
|
|
// checks
|
|
if (how == B_VIS) {
|
|
assert (abs(lf->cell->x - cell->x) <= 10);
|
|
assert (abs(lf->cell->y - cell->y) <= 10);
|
|
}
|
|
|
|
|
|
idxtoxy(lf, idx, &x, &y);
|
|
c = getcellat(lf->cell->map, x, y);
|
|
assert(c);
|
|
|
|
}
|
|
|
|
int getviscell(lifeform_t *lf, cell_t *cell) {
|
|
int idx;
|
|
idx = xytoidx(lf, cell->x, cell->y);
|
|
if ((idx < 0) || (idx >= (lf->visw * lf->visw)) ) {
|
|
return B_NOVIS;
|
|
}
|
|
|
|
return lf->viscell[idx];
|
|
}
|
|
*/
|
|
|
|
int haslos(lifeform_t *viewer, cell_t *dest) {
|
|
if (gamemode == GM_CLEANUP) return B_FALSE;
|
|
|
|
if (!viewer) return B_FALSE;
|
|
if (!dest) return B_FALSE;
|
|
if (!viewer->cell) return B_FALSE;
|
|
if (viewer->cell->map != dest->map) return B_FALSE;
|
|
|
|
if (gamemode < GM_GAMESTARTED) return B_FALSE;
|
|
|
|
// can't see when you're dead UNLESS you are the player. this is
|
|
// to prevent the screen from going black when "You die" appears.
|
|
//if (isdead(viewer) && !isplayer(viewer)) return B_FALSE;
|
|
if (lfhasflag(viewer, F_DEAD) && !isplayer(viewer)) return B_FALSE;
|
|
|
|
if (viewer->losdirty) {
|
|
precalclos(viewer);
|
|
viewer->losdirty = B_FALSE;
|
|
}
|
|
|
|
// can we use pre-calced los?
|
|
//
|
|
/*
|
|
if (viewer->los) {
|
|
return haslos_fast(viewer, dest);
|
|
}
|
|
*/
|
|
|
|
assert(viewer->los || (viewer->nlos == 0));
|
|
|
|
/*
|
|
if (haslos_fast(viewer, dest)) {
|
|
return B_TRUE;
|
|
}
|
|
return haslosdark(viewer, dest);
|
|
*/
|
|
return haslos_fast(viewer, dest);
|
|
/*
|
|
// THIS CODE IS NO LONGER EVER USED
|
|
map = dest->map;
|
|
|
|
x1 = viewer->cell->x;
|
|
y1 = viewer->cell->y;
|
|
x2 = dest->x;
|
|
y2 = dest->y;
|
|
|
|
f = hasflag(viewer->flags, F_XRAYVIS);
|
|
if (f) {
|
|
xray = f->val[0];
|
|
} else {
|
|
xray = 0;
|
|
}
|
|
|
|
// can't see if you're blind
|
|
if (isblind(viewer)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
maxvisrange = getvisrange(viewer, B_TRUE);
|
|
|
|
nvrange = getnightvisrange(viewer);
|
|
limit(&nvrange, NA, maxvisrange);
|
|
if (!celllitfor(viewer, dest, maxvisrange, nvrange)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
calcbresnham(map, x1, y1, x2, y2, retcell, &numpixels);
|
|
|
|
currange = 0;
|
|
|
|
for (i = 0; i < numpixels ; i++) {
|
|
cell_t *cell;
|
|
|
|
cell = retcell[i];
|
|
// don't need to move out of the last one
|
|
if ((cell->x == x2) && (cell->y == y2)) {
|
|
break;
|
|
}
|
|
|
|
if (i != 0) { // ie. if not blind, you can always see your own cell
|
|
int rangemod;
|
|
|
|
currange++;
|
|
if (currange > maxvisrange) {
|
|
return B_FALSE;
|
|
}
|
|
// if we went uphill, stop here
|
|
//if (wentuphill) {
|
|
// return B_FALSE;
|
|
//}
|
|
|
|
if (!celltransparentfor(viewer, cell, &xray, &rangemod)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
currange += rangemod;
|
|
|
|
// xray vision decreases by one
|
|
if (xray) xray--;
|
|
|
|
// if you went uphill, can't see any further
|
|
//if (getheight(x,y,z) > origheight) {
|
|
// wentuphill = B_TRUE;
|
|
//}
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
*/
|
|
}
|
|
|
|
int haslosdark(lifeform_t *viewer, cell_t *dest) {
|
|
int i;
|
|
for (i = 0; i < viewer->nlosdark; i++) {
|
|
if (viewer->losdark[i] == dest) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int haslos_fast(lifeform_t *viewer, cell_t *dest) {
|
|
int i;
|
|
for (i = 0; i < viewer->nlos; i++) {
|
|
if (viewer->los[i] == dest) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
enum FLAG isairborne(lifeform_t *lf) {
|
|
if (!lf) return B_FALSE;
|
|
if (lfhasflag(lf, F_FLYING)) {
|
|
return F_FLYING;
|
|
} else if (lfhasflag(lf, F_LEVITATING)) {
|
|
return F_LEVITATING;
|
|
} else if (lfhasflag(lf, F_ICESLIDE)) {
|
|
return F_LEVITATING;
|
|
}
|
|
return F_NONE;
|
|
}
|
|
|
|
int isaquatic(lifeform_t *lf) {
|
|
if (lf->race->raceclass->id == RC_AQUATIC) {
|
|
return B_TRUE;
|
|
} else if (lfhasflag(lf, F_AQUATIC)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
flag_t *isasleep(lifeform_t *lf) {
|
|
return lfhasflagval(lf, F_ASLEEP, NA, ST_ASLEEP, NA, NULL);
|
|
}
|
|
|
|
// is lf behind otherlf?
|
|
// you can never be "behind" something with f_awareness
|
|
int isbehind(lifeform_t *lf, lifeform_t *otherlf) {
|
|
int dir;
|
|
if (lfhasflag(otherlf, F_AWARENESS)) return B_FALSE;
|
|
dir = getdirtowards(otherlf->cell, lf->cell, NULL, B_FALSE, DT_ORTH);
|
|
if (getrelativedir(otherlf, dir) == RD_BACKWARDS) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isbleeding(lifeform_t *lf) {
|
|
if (lfhasflagval(lf, F_INJURY, NA, NA, DT_SLASH, NULL)) {
|
|
//return B_FROMINJURY;
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
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 (getraceclass(lf) == RC_ROBOT) {
|
|
reason = E_ROBOT;
|
|
return B_FALSE;
|
|
}
|
|
if (hasflag(lf->flags, F_DRUNK)) {
|
|
reason = E_DRUNK;
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_CHARMEDBY)) {
|
|
reason = E_ALREADYUSING;
|
|
return B_FALSE;
|
|
}
|
|
if (hasflag(lf->flags, F_UNIQUE) || hasflag(lf->flags, F_NOCHARM)) {
|
|
reason = E_NOEFFECT; // generic error
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int isclimbing(lifeform_t *lf) {
|
|
if (hasflag(lf->flags, F_CLIMBING)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isdead(lifeform_t *lf) {
|
|
if (!lf->alive) return B_TRUE;
|
|
if (lf->hp <= 0) return B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
void killlf(lifeform_t *lf) {
|
|
lifeform_t *nextone, *lastone, *l;
|
|
map_t *m;
|
|
flag_t *f;
|
|
|
|
if ((gamemode == GM_GAMESTARTED)) {
|
|
if (cansee(player, lf)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
}
|
|
|
|
m = lf->cell->map;
|
|
|
|
// remove references
|
|
lf->cell->lf = NULL;
|
|
// stomach?
|
|
f = lfhasflag(lf, F_MAPLINK);
|
|
if (f) {
|
|
map_t *m;
|
|
m = findmap(f->val[0]);
|
|
killmap(m); // kill the stomach
|
|
killflag(f); // kill the link flag
|
|
}
|
|
|
|
// shouldn't need this...
|
|
lf->cell = NULL;
|
|
|
|
// remove 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);
|
|
}
|
|
free(lf->pack);
|
|
lf->pack = NULL;
|
|
// kill any remaining obs
|
|
while (lf->polypack->first) {
|
|
killob(lf->polypack->first);
|
|
}
|
|
free(lf->polypack);
|
|
lf->polypack = NULL;
|
|
|
|
// kill flags
|
|
killflagpile(lf->flags);
|
|
lf->flags = NULL;
|
|
|
|
// remove from list
|
|
nextone = lf->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = lf->prev;
|
|
} else { /* last */
|
|
m->lastlf = lf->prev;
|
|
}
|
|
|
|
if (lf->prev == NULL) {
|
|
/* first */
|
|
nextone = lf->next;
|
|
free(m->lf);
|
|
m->lf = nextone;
|
|
} else {
|
|
lastone = lf->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
int isdeaf(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_TREMORSENSE)) return B_FALSE;
|
|
|
|
if (lfhasflag(lf, F_DEAF)) return B_TRUE;
|
|
if (isresting(lf) && lfhasflag(lf, F_RESTINGINMOTEL)) return B_TRUE;
|
|
if (lfhasflagval(lf, F_INJURY, IJ_EARSRINGING, NA, NA, NULL)) return B_TRUE;
|
|
|
|
if (hasequippedobid(lf->pack, OT_AMU_SLEEP) && isasleep(lf)) return B_TRUE;
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
// returns second weapon if you are dual weilding
|
|
object_t *isdualweilding(lifeform_t *lf) {
|
|
object_t *priwep,*secwep;
|
|
// dual weilding?
|
|
priwep = getweapon(lf);
|
|
secwep = getsecmeleeweapon(lf);
|
|
if (priwep && secwep) {
|
|
if (priwep != secwep) { // twohanded weapons dont count
|
|
return secwep;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
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) {
|
|
if (istwohandedfor(o, lf)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int isfriendly(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_FRIENDLY)) {
|
|
return B_TRUE;
|
|
}
|
|
if (ispetof(lf, player)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isfullyhealed(lifeform_t *lf) {
|
|
int healed = B_TRUE;
|
|
if ((lf->hp < lf->maxhp) || hastempinjuries(lf)) {
|
|
healed = B_FALSE;
|
|
} else if (lf->mp < getmaxmp(lf)) {
|
|
healed = B_FALSE;
|
|
} else if (getstamina(lf) < getmaxstamina(lf)) {
|
|
healed = B_FALSE;
|
|
} else if (hashealableinjuries(lf)) {
|
|
healed = B_FALSE;
|
|
}
|
|
return healed;
|
|
}
|
|
|
|
int 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, int onlytemp) {
|
|
flag_t *f;
|
|
dt = basedamagetype(dt);
|
|
f = hasflagval(fp, F_DTIMMUNE, dt, NA, NA, NULL);
|
|
if (f) {
|
|
if (!onlytemp || (f->lifetime != FROMRACE)) {
|
|
return f;
|
|
}
|
|
}
|
|
f = hasflagval(fp, F_DTIMMUNE, DT_ALL, NA, NA, NULL);
|
|
if (f) {
|
|
if (!onlytemp || (f->lifetime != FROMRACE)) {
|
|
return f;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int isinbattle(lifeform_t *lf, int includedistant, int onlyarmed) {
|
|
if (includedistant) {
|
|
lifeform_t *l;
|
|
for (l = lf->cell->map->lf ; l ;l = l->next) {
|
|
if ((l != lf) && areenemies(l, lf) && cansee(lf, l)) {
|
|
if (!lfhasflag(l, F_DOESNTMOVE)) {
|
|
if (!onlyarmed || getweapon(l)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
int dir;
|
|
for (dir = DC_N; dir <= DC_NW; dir++) {
|
|
cell_t *c;
|
|
c = getcellindir(lf->cell, dir);
|
|
if (c && c->lf && areenemies(lf, c->lf) && cansee(lf, c->lf)) {
|
|
if (!lfhasflag(c->lf, F_DOESNTMOVE)) {
|
|
if (!onlyarmed || getweapon(c->lf)) {
|
|
return 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;
|
|
}
|
|
|
|
int ishelplessvictim(lifeform_t *victim, lifeform_t *attacker, enum HELPLESSTYPE *how) {
|
|
if (!cansee(attacker, victim)) return B_FALSE;
|
|
if (isfleeing(victim)) {
|
|
if (how) *how = HL_FLEEING;
|
|
return B_TRUE;
|
|
} else if (!cansee(victim, attacker) && (getraceclass(victim) != RC_PLANT)) {
|
|
if (how) *how = HL_CANTSEE;
|
|
return B_TRUE;
|
|
}
|
|
if (how) *how = HL_NONE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
flag_t *ishidingfrom(lifeform_t *hider, lifeform_t *seeker) {
|
|
if (hider != seeker) {
|
|
flag_t *f;
|
|
f = lfhasflag(hider, F_HIDING);
|
|
if (f && !lfhasflagval(seeker, F_SPOTTED, hider->id, NA, NA, NULL)) {
|
|
return f;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// can you try to recruit this lf?
|
|
int ishirable(lifeform_t *lf) {
|
|
if (!isplayer(lf) && ispeaceful(lf)) {
|
|
if (lfhasflag(lf, F_HIRABLE)) {
|
|
return B_TRUE;
|
|
}
|
|
if (lfhasflag(lf, F_ISPRISONER)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// cannot voluntarily move _at all_.
|
|
int isimmobile(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_ASLEEP)) {
|
|
return B_TRUE;
|
|
}
|
|
if (lfhasflag(lf, F_FROZEN)) {
|
|
return B_TRUE;
|
|
}
|
|
if (lfhasflag(lf, F_PARALYZED)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isliving(lifeform_t *lf) {
|
|
switch (getraceclass(lf)) {
|
|
case RC_UNDEAD:
|
|
case RC_OTHER:
|
|
case RC_MAGIC:
|
|
case RC_ROBOT:
|
|
case RC_SLIME:
|
|
return B_FALSE;
|
|
default: break;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int isloreskill(enum SKILL skid) {
|
|
switch (skid) {
|
|
case SK_LORE_ARCANA:
|
|
case SK_LORE_DEMONS:
|
|
case SK_LORE_HUMANOID:
|
|
case SK_LORE_NATURE:
|
|
case SK_LORE_UNDEAD:
|
|
return B_TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int islowhp(lifeform_t *lf) {
|
|
float hppct;
|
|
hppct = ((float)lf->hp / (float) lf->maxhp) * 100;
|
|
|
|
if (hppct <= 40) return B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isspellskill(enum SKILL skid) {
|
|
switch (skid) {
|
|
case SK_SS_ALLOMANCY:
|
|
case SK_SS_MENTAL:
|
|
case SK_SS_NATURE:
|
|
case SK_SS_AIR:
|
|
case SK_SS_DEATH:
|
|
case SK_SS_DIVINATION:
|
|
case SK_SS_FIRE:
|
|
case SK_SS_COLD:
|
|
case SK_SS_LIFE:
|
|
case SK_SS_MODIFICATION:
|
|
case SK_SS_SUMMONING:
|
|
case SK_SS_TRANSLOCATION:
|
|
case SK_SS_WILD:
|
|
return B_TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int ismadeofice(lifeform_t *lf) {
|
|
if (lf->material->id == MT_ICE) return B_TRUE;
|
|
if (lfhasflag(lf, F_FROZEN)) return B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// is this lf's BASE attrib maxed?
|
|
int ismaxedattr(lifeform_t *lf, enum ATTRIB a) {
|
|
if (lf->att[a] >= MAX_ATTRIBVAL) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int ismaxedskill(lifeform_t *lf, enum SKILL skid) {
|
|
if (getskill(lf, skid) >= getmaxskilllevel(lf, skid)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int ispeaceful(lifeform_t *lf) {
|
|
if (isplayer(lf)) return B_FALSE;
|
|
if (lfhasflag(lf, F_HOSTILE)) {
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflagval(lf, F_TARGETLF, player->id, NA, NA, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
int isknownpeaceful(lifeform_t *lf) {
|
|
if ((getlorelevel(player, getraceclass(lf)) >= PR_NOVICE) ||
|
|
(getskill(player, SK_SPEECH)) ) {
|
|
return B_FALSE;
|
|
}
|
|
return ispeaceful(lf);
|
|
}
|
|
|
|
|
|
int ispetof(lifeform_t *lf, lifeform_t *owner) {
|
|
if (!lf || !owner) return B_FALSE;
|
|
if (lfhasflagval(lf, F_PETOF, owner->id, NA, NA, NULL)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
flag_t *ispetortarget(lifeform_t *lf, lifeform_t *ownertarget) {
|
|
flag_t *f;
|
|
if (!lf || !ownertarget) return B_FALSE;
|
|
f = lfhasflagval(lf, F_PETOF, ownertarget->id, NA, NA, NULL);
|
|
if (f) return f;
|
|
f = lfhasflagval(lf, F_TARGETLF, ownertarget->id, NA, NA, NULL);
|
|
if (f) return f;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int isplayer(lifeform_t *lf) {
|
|
if (lf && (lf->controller == C_PLAYER)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// returns poison flag with longest remaining lifetime
|
|
flag_t *ispoisoned(lifeform_t *lf) {
|
|
flag_t *f,*max = NULL;
|
|
int i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->lifetime == PERMENANT) {
|
|
return f;
|
|
} else if ((max == NULL) || (f->lifetime > max->lifetime)) {
|
|
max = f;
|
|
}
|
|
}
|
|
|
|
return max;
|
|
}
|
|
|
|
flag_t *ispoisonedwith(lifeform_t *lf, enum POISONTYPE pt) {
|
|
return lfhasflagval(lf, F_POISONED, pt, NA, NA, NULL);
|
|
}
|
|
|
|
int ispolymorphed(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_POLYMORPHED)) {
|
|
return B_TRUE;
|
|
} else {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
int isprone(lifeform_t *lf) {
|
|
flag_t *f;
|
|
if (lfhasflag(lf, F_PRONE)) {
|
|
return B_TRUE;
|
|
}
|
|
f = lfhasflag(lf, F_ASLEEP);
|
|
if (f && (f->val[1] != ST_MEDITATING)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
flag_t *isresistantto(flagpile_t *fp, enum DAMTYPE dt, int onlytemp) {
|
|
flag_t *f;
|
|
dt = basedamagetype(dt);
|
|
f = hasflagval(fp, F_DTRESIST, dt, NA, NA, NULL);
|
|
if (f) {
|
|
if (!onlytemp || (f->lifetime != FROMRACE)) {
|
|
return f;
|
|
}
|
|
}
|
|
f = hasflagval(fp, F_DTRESIST, DT_ALL, NA, NA, NULL);
|
|
if (f) {
|
|
if (!onlytemp || (f->lifetime != FROMRACE)) {
|
|
return f;
|
|
}
|
|
}
|
|
if (dt == DT_FIRE) {
|
|
f = hasflag(fp, F_WET);
|
|
if (f) return f;
|
|
f = hasflag(fp, F_DRUNK);
|
|
if (f) return f;
|
|
}
|
|
if (dt == DT_COLD) {
|
|
f = hasflag(fp, F_DRUNK);
|
|
if (f) return f;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
flag_t *isresting(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_ASLEEP);
|
|
if (f && (f->val[2] != NA)) {
|
|
return f;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int issleepingtimefor(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_NOCTURNAL) && !isnighttime()) {
|
|
return B_TRUE;
|
|
} else if (lfhasflag(lf, F_DIURNAL) && isnighttime()) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
object_t *isstuck(lifeform_t *lf) {
|
|
object_t *o;
|
|
|
|
for (o = lf->cell->obpile->first ; o ; o = o->next) {
|
|
if ((o->type->id == OT_WEB) && (lf->race->baseid == R_SPIDER)) {
|
|
continue;
|
|
}
|
|
if (hasflag(o->flags, F_RESTRICTMOVEMENT)) {
|
|
return o;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
poisontype_t *addpoisontype(enum POISONTYPE id, char *name, char *desc, char *damverb, enum OBTYPE vomitob, int dam, int dampct, enum POISONSEVERITY severity, int incubationtime) {
|
|
poisontype_t *a;
|
|
|
|
// add to the end of the list
|
|
if (firstpoisontype == NULL) {
|
|
firstpoisontype = malloc(sizeof(poisontype_t));
|
|
a = firstpoisontype;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastpoisontype;
|
|
a->next = malloc(sizeof(poisontype_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastpoisontype = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->name = strdup(name);
|
|
a->desc = strdup(desc);
|
|
a->damverb = strdup(damverb);
|
|
a->vomitob = vomitob;
|
|
a->dam = dam;
|
|
a->dampct = dampct;
|
|
a->severity = severity;
|
|
a->incubationtime = incubationtime;
|
|
|
|
return a;
|
|
}
|
|
|
|
job_t *addjob(enum JOB id, char *name, char *desc) {
|
|
job_t *a;
|
|
|
|
// add to the end of the list
|
|
if (firstjob == NULL) {
|
|
firstjob = malloc(sizeof(job_t));
|
|
a = firstjob;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastjob;
|
|
a->next = malloc(sizeof(job_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastjob = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->name = strdup(name);
|
|
a->desc = strdup(desc);
|
|
|
|
a->flags = addflagpile(NULL, NULL);
|
|
return a;
|
|
}
|
|
|
|
void addbodypart(race_t *r, enum BODYPART bp, char *name) {
|
|
r->bodypart[r->nbodyparts].id = bp;
|
|
if (name) {
|
|
r->bodypart[r->nbodyparts].name = strdup(name);
|
|
} else {
|
|
r->bodypart[r->nbodyparts].name = strdup("");
|
|
}
|
|
r->bodypart[r->nbodyparts].armourok = B_TRUE;
|
|
r->nbodyparts++;
|
|
}
|
|
|
|
lifeform_t *addlf(cell_t *cell, enum RACE rid, int level) {
|
|
return real_addlf(cell, rid, level, C_AI);
|
|
}
|
|
|
|
lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) {
|
|
map_t *m;
|
|
lifeform_t *a;
|
|
int i;
|
|
//int db = B_FALSE;
|
|
|
|
assert(cell);
|
|
if (cell->type != (celltype_t *) DUMMYCELLTYPE) {
|
|
assert(!cell->type->solid);
|
|
}
|
|
|
|
|
|
m = cell->map;
|
|
|
|
// add to the end of the list
|
|
if (m->lf == NULL) {
|
|
m->lf = malloc(sizeof(lifeform_t));
|
|
a = m->lf;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = m->lastlf;
|
|
a->next = malloc(sizeof(lifeform_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
|
|
if (controller == C_PLAYER) {
|
|
player = a;
|
|
}
|
|
|
|
m->lastlf = a;
|
|
a->next = NULL;
|
|
|
|
a->born = B_FALSE; // will set this back to true later
|
|
|
|
// props
|
|
a->id = nextlfid; nextlfid++;
|
|
a->controller = controller;
|
|
a->level = level;
|
|
a->newlevel = level;
|
|
a->xp = getxpforlev(a->level);
|
|
a->totskillpoints = 0;
|
|
a->skillxp = 0;
|
|
a->skillpoints = 0;
|
|
a->cell = cell;
|
|
a->alive = B_TRUE;
|
|
a->lastdam = strdup("nothing");
|
|
a->lastdamtype = DT_NONE;
|
|
a->lastdamlf = -1;
|
|
a->damlastturn = 0;
|
|
a->mplastturn = 0;
|
|
a->stamlastturn = 0;
|
|
a->bartimer = 0;
|
|
a->killverb = strdup("Killed");
|
|
if ((gamemode == GM_GAMESTARTED) && a->prev) {
|
|
a->timespent = a->prev->timespent + 1;
|
|
} else {
|
|
a->timespent = 0;
|
|
}
|
|
a->rotated = B_FALSE;
|
|
a->sorted = B_FALSE;
|
|
a->forgettimer = 0;
|
|
|
|
a->prevcell[0] = NULL;
|
|
a->prevcell[1] = NULL;
|
|
|
|
a->polyrevert = B_FALSE;
|
|
|
|
// for precalcing line of sight
|
|
a->facing = rnd(DC_N, DC_NW);
|
|
a->turncounter = 0;
|
|
a->losdirty = B_TRUE;
|
|
//a->los = malloc( sizeof(cell_t *) * ((MAXVISRANGE*2+1)*(MAXVISRANGE*2+1)));
|
|
a->los = NULL; // will be alloced in precalclos
|
|
a->losdark = NULL; // will be alloced in precalclos
|
|
a->nlos = 0;
|
|
a->nlosdark = 0;
|
|
|
|
a->eyeadjustment = 0;
|
|
|
|
// for ai
|
|
|
|
// avoid messages when equipping initial obs
|
|
a->created = B_FALSE;
|
|
|
|
a->pack = addobpile(a, NOLOC, NOOB);
|
|
a->polypack = addobpile(NOOWNER, NOLOC, NOOB);
|
|
|
|
a->turnsskipped = 0;
|
|
|
|
// clear laoding variables
|
|
for (i = 0; i < MAXPILEOBS; i++) {
|
|
a->oblist[i] = -1;
|
|
}
|
|
a->x = -1;
|
|
a->y = -1;
|
|
|
|
// defaults
|
|
a->hp = 1;
|
|
a->maxhp = a->hp;
|
|
a->mp = 0;
|
|
a->maxmp = a->mp;
|
|
|
|
a->material = findmaterial(MT_FLESH); // might be overridden in setrace
|
|
|
|
// init flags
|
|
a->flags = addflagpile(a, NULL);
|
|
|
|
// link cell back to lf
|
|
cell->lf = a;
|
|
|
|
// set race - this will inherit race flags and material
|
|
a->race = NULL;
|
|
|
|
setrace(a, rid, B_FALSE);
|
|
|
|
// now do everything which must occur AFTER setting the race:
|
|
|
|
// remember original attribs so that if we are polymorphed, we're
|
|
// able to revert back.
|
|
for (i = 0; i < MAXATTS; i++) {
|
|
a->origatt[i] = a->baseatt[i];
|
|
}
|
|
// set stamina AFTER setrace as it depends on your attribs
|
|
a->stamina = getmaxstamina(a);
|
|
// give start objets AFTER setrace as some races start with objects
|
|
if ((gamemode != GM_LOADING) && (gamemode != GM_VALIDATION)) {
|
|
outfitlf(a);
|
|
}
|
|
a->created = B_TRUE;
|
|
a->born = B_TRUE; // now finished creating it.
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
if (cansee(player, a)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
}
|
|
//
|
|
a->losdirty = B_TRUE;
|
|
addflag(a->flags, F_HOMEMAP, cell->map->id, NA, NA, NULL);
|
|
|
|
// set home room.
|
|
sethomeroom(a);
|
|
|
|
return a;
|
|
}
|
|
|
|
|
|
|
|
race_t *addrace(enum RACE id, char *name, float weight, char glyph, int glyphcolour, enum MATERIAL mat, enum RACECLASS raceclass, char *desc) {
|
|
race_t *a;
|
|
|
|
assert(!findrace(id));
|
|
|
|
// add to the end of the list
|
|
if (firstrace == NULL) {
|
|
firstrace = malloc(sizeof(race_t));
|
|
a = firstrace;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastrace;
|
|
a->next = malloc(sizeof(race_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastrace = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->baseid = id; // default
|
|
a->raceclass = findraceclass(raceclass);
|
|
|
|
a->material = findmaterial(mat);
|
|
assert(a->material);
|
|
a->name = strdup(name);
|
|
a->desc = strdup(desc);
|
|
a->weight = weight;
|
|
a->glyph.ch = glyph;
|
|
a->glyph.colour = glyphcolour;
|
|
a->known = B_FALSE;
|
|
|
|
a->nbodyparts = 0;
|
|
|
|
a->flags = addflagpile(NULL, NULL);
|
|
return a;
|
|
}
|
|
|
|
raceclass_t *addraceclass(enum RACECLASS id, char *name, char *pluralname, enum SKILL skill) {
|
|
raceclass_t *a;
|
|
|
|
assert(!findraceclass(id));
|
|
|
|
// add to the end of the list
|
|
if (firstraceclass == NULL) {
|
|
firstraceclass = malloc(sizeof(raceclass_t));
|
|
a = firstraceclass;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastraceclass;
|
|
a->next = malloc(sizeof(raceclass_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastraceclass = a;
|
|
a->next = NULL;
|
|
|
|
|
|
// props
|
|
a->id = id;
|
|
a->name = strdup(name);
|
|
a->pluralname = strdup(pluralname);
|
|
a->skill = skill;
|
|
|
|
return a;
|
|
}
|
|
|
|
skill_t *addskill(enum SKILL id, char *name, char *desc, int traintime) {
|
|
skill_t *a,*temp;
|
|
int count = 0;
|
|
|
|
assert(!findskill(id));
|
|
|
|
for (temp = firstskill ; temp ; temp = temp->next) {
|
|
count++;
|
|
}
|
|
assert(count < MAXSKILLS);
|
|
|
|
// add to the end of the list
|
|
if (firstskill == NULL) {
|
|
firstskill = malloc(sizeof(skill_t));
|
|
a = firstskill;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastskill;
|
|
a->next = malloc(sizeof(skill_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastskill = a;
|
|
a->next = NULL;
|
|
|
|
|
|
// props
|
|
a->id = id;
|
|
a->name = strdup(name);
|
|
a->shortname = strdup(name);
|
|
//
|
|
strrep(a->shortname, "Sorcery:", "", NULL);
|
|
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++;
|
|
}
|
|
|
|
subjob_t *addsubjob(enum SUBJOB id, char *name, char *desc, char letter) {
|
|
subjob_t *a;
|
|
|
|
// add to the end of the list
|
|
if (firstsubjob == NULL) {
|
|
firstsubjob = malloc(sizeof(subjob_t));
|
|
a = firstsubjob;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastsubjob;
|
|
a->next = malloc(sizeof(subjob_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastsubjob = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->name = strdup(name);
|
|
a->desc = strdup(desc);
|
|
a->letter = letter;
|
|
|
|
return a;
|
|
}
|
|
|
|
object_t *addtrail(lifeform_t *lf, cell_t *where, int dir, int doprints, int doscents) {
|
|
object_t *footprint, *scent,*retob = NULL;
|
|
flag_t *fpflag = NULL;
|
|
|
|
// no tracks at all?
|
|
if (where->type->solid) {
|
|
return NULL;
|
|
} else if (hasobwithflag(where->obpile, F_DEEPWATER)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (lfhasflag(lf, F_NOPRINTS)) {
|
|
doprints = B_FALSE;
|
|
}
|
|
if (getraceclass(lf) == RC_ROBOT) {
|
|
doscents = B_FALSE;
|
|
}
|
|
|
|
// footprints first
|
|
if (doprints) {
|
|
if (!isairborne(lf) && !lfhasflag(lf, F_NONCORPOREAL)) {
|
|
int fpdir;
|
|
if (getskill(lf, SK_PERCEPTION) >= PR_EXPERT) {
|
|
// no footprints!
|
|
return NULL;
|
|
} else {
|
|
fpdir = dir;
|
|
}
|
|
|
|
footprint = hastrailof(where->obpile, lf, OT_FOOTPRINT, &fpflag, NULL);
|
|
if (footprint) {
|
|
assert(fpflag);
|
|
fpflag->lifetime = getfootprinttime(lf);
|
|
fpflag->val[1] = fpdir;
|
|
} else {
|
|
char buf[BUFLENTINY];
|
|
snprintf(buf, BUFLENTINY, "%d", lf->id);
|
|
footprint = addobfast(where->obpile, OT_FOOTPRINT);
|
|
addtempflag(footprint->flags, F_TRAIL, lf->race->id, fpdir, S_SIGHT, buf, getfootprinttime(lf));
|
|
}
|
|
retob = footprint;
|
|
}
|
|
}
|
|
|
|
// now smell
|
|
if (doscents) {
|
|
scent = hastrailof(where->obpile, lf, OT_SCENT, &fpflag, NULL);
|
|
if (scent) {
|
|
assert(fpflag);
|
|
fpflag->lifetime = TM_SCENT;
|
|
} else {
|
|
char buf[BUFLENTINY];
|
|
snprintf(buf, BUFLENTINY, "%d", lf->id);
|
|
scent = addobfast(where->obpile, OT_SCENT);
|
|
addtempflag(scent->flags, F_TRAIL, lf->race->id, dir, S_SMELL, buf, TM_SCENT);
|
|
}
|
|
retob = scent;
|
|
}
|
|
return retob;
|
|
}
|
|
|
|
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, B_FALSE)) {
|
|
*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, B_FALSE)) {
|
|
*amt = 0;
|
|
return;
|
|
}
|
|
|
|
if ((damtype == DT_SONIC) && isdeaf(lf)) {
|
|
*amt = 0;
|
|
return;
|
|
}
|
|
|
|
if (lfhasflag(lf, F_INVULNERABLE)) {
|
|
switch (damtype) {
|
|
case DT_DIRECT:
|
|
case DT_NONE:
|
|
break;
|
|
default:
|
|
*amt = 0;
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((damtype == DT_BASH) && ismadeofice(lf)) {
|
|
(*amt) *= 2;
|
|
}
|
|
|
|
if ((damtype == DT_ELECTRIC) && hasobwithflag(lf->cell->obpile, F_DEEPWATER)) {
|
|
(*amt) *= 2;
|
|
}
|
|
if ((damtype == DT_COLD) && hasobwithflag(lf->cell->obpile, F_DEEPWATER)) {
|
|
(*amt) = pctof(150, *amt);
|
|
}
|
|
|
|
if ((damtype == DT_FALL) && lfhasflag(lf, F_GRAVLESSENED)) {
|
|
*amt = 0;
|
|
}
|
|
|
|
if (isresistantto(lf->flags, damtype, B_FALSE)) {
|
|
(*amt) /= 2;
|
|
}
|
|
f = isvulnto(lf->flags, damtype, B_FALSE);
|
|
if (f) {
|
|
if ((*amt == 0) && strlen(f->text)) {
|
|
int ndice,nsides,bonus;
|
|
texttodice(f->text, &ndice,&nsides,&bonus);
|
|
*amt = rolldie(ndice,nsides) + bonus;
|
|
} else {
|
|
(*amt) *= 2;
|
|
}
|
|
}
|
|
|
|
|
|
// don't adjust for lifeform material - we inherit all the material's flags.
|
|
//adjustdammaterial((unsigned int *)amt, damtype, getlfmaterial(lf));
|
|
adjustdamhardness(amt, damtype, getlfmaterial(lf));
|
|
|
|
f = lfhasflag(lf, F_DRUNK);
|
|
if (f) {
|
|
*amt -= rnd(1,f->val[0]);
|
|
}
|
|
|
|
// gods can't die unless they are in the realm of gods
|
|
if (lf->race->raceclass->id == RC_GOD) {
|
|
int damok = B_FALSE;
|
|
if (lf->cell->map->habitat->id == H_HEAVEN) {
|
|
if (hasob(player->pack, getrelatedgodstone(lf->race->id)) ) {
|
|
damok = B_TRUE;
|
|
}
|
|
}
|
|
if (!damok) {
|
|
// immortal
|
|
limit(amt, 0, lf->hp-1);
|
|
}
|
|
} else {
|
|
limit(amt, 0, NA);
|
|
}
|
|
}
|
|
|
|
void makepeaceful(lifeform_t *who, lifeform_t *causedby) {
|
|
char lfname[BUFLEN];
|
|
|
|
getlfname(who, lfname);
|
|
if (lfhasflag(who, F_DEBUG)) {
|
|
msg("Making %s friendly.",lfname);
|
|
}
|
|
|
|
if (lfhasflag(who, F_HOSTILE)) {
|
|
if (cansee(player, who)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(who, lfname);
|
|
msg("%s calms down.", lfname);
|
|
}
|
|
}
|
|
|
|
if (causedby && isplayer(causedby)) {
|
|
angergodmaybe(R_GODBATTLE, 5, GA_COWARD);
|
|
pleasegodmaybe(R_GODMERCY, 10);
|
|
}
|
|
|
|
addflag(who->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
|
|
killflagsofid(who->flags, F_HOSTILE);
|
|
killflagsofid(who->flags, F_HATESALL);
|
|
killflagsofid(who->flags, F_HATESRACE);
|
|
killflagsofid(who->flags, F_TERRITORIAL);
|
|
killflagsofid(who->flags, F_HATESRACEWITHFLAG);
|
|
loseaitargets(who);
|
|
}
|
|
|
|
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);
|
|
}
|
|
if (!where) return NULL;
|
|
lf = addlf(where, r->id, 1);
|
|
|
|
addflag(lf->flags, F_LFSUFFIX, B_TRUE, NA, NA, "zombie");
|
|
killflagsofid(lf->flags, F_GLYPH);
|
|
addflag(lf->flags, F_GLYPH, C_BLUE, 'Z', NA, NULL);
|
|
addflag(lf->flags, F_UNDEAD, B_TRUE, NA, NA, NULL);
|
|
killflagsofid(lf->flags, F_CORPSETYPE);
|
|
killflagsofid(lf->flags, F_EXTRACORPSE);
|
|
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
|
|
killflagsofid(lf->flags, F_DTIMMUNE);
|
|
killflagsofid(lf->flags, F_DTVULN);
|
|
killflagsofid(lf->flags, F_DTRESIST);
|
|
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_XPVAL);
|
|
addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
|
|
killflagsofid(lf->flags, F_WANTSBETTERWEP);
|
|
killflagsofid(lf->flags, F_WANTSBETTERARM);
|
|
killflagsofid(lf->flags, F_WANTSOBFLAG);
|
|
killflagsofid(lf->flags, F_WANTS);
|
|
killflagsofid(lf->flags, F_NOISETEXT);
|
|
killflagsofid(lf->flags, F_SEEINDARK);
|
|
killflagsofid(lf->flags, F_TREMORSENSE);
|
|
|
|
killflagsofid(lf->flags, F_MOVESPEED);
|
|
addflag(lf->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
|
|
killflagsofid(lf->flags, F_ACTIONSPEED);
|
|
addflag(lf->flags, F_ACTIONSPEED, SP_SLOW, NA, NA, NULL);
|
|
lf->baseatt[A_IQ] = rollattr(IQ_MINDLESS);
|
|
lf->att[A_IQ] = lf->baseatt[A_IQ];
|
|
|
|
|
|
killflagsofid(lf->flags, F_REVIVETIMER);
|
|
|
|
// no magic
|
|
lf->maxmp = 0;
|
|
lf->mp = 0;
|
|
|
|
// no objects
|
|
while (lf->pack->first) {
|
|
killob(lf->pack->first);
|
|
}
|
|
|
|
// no abilities
|
|
killflagsofid(lf->flags, F_CANCAST);
|
|
killflagsofid(lf->flags, F_CANWILL);
|
|
|
|
// 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 (!isplayer(lf1) && lfhasflagval(lf1, F_TARGETLF, lf2->id, NA, NA, NULL)) return B_TRUE;
|
|
if (!isplayer(lf2) && lfhasflagval(lf2, F_TARGETLF, lf1->id, NA, NA, NULL)) return B_TRUE;
|
|
|
|
if (hasjob(lf1, J_DRUID) && (getraceclass(lf2) == RC_PLANT)) {
|
|
return B_FALSE;
|
|
} else if (hasjob(lf2, J_DRUID) && (getraceclass(lf1) == RC_PLANT)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
switch (getallegiance(lf1)) {
|
|
case AL_HOSTILE:
|
|
switch (getallegiance(lf2)) {
|
|
case AL_HOSTILE:
|
|
return B_FALSE;
|
|
case AL_PEACEFUL:
|
|
return B_FALSE;
|
|
case AL_FRIENDLY:
|
|
return B_TRUE;
|
|
}
|
|
break;
|
|
case AL_PEACEFUL:
|
|
return B_FALSE;
|
|
case AL_FRIENDLY:
|
|
switch (getallegiance(lf2)) {
|
|
case AL_HOSTILE:
|
|
return B_TRUE;
|
|
case AL_PEACEFUL:
|
|
return B_FALSE;
|
|
case AL_FRIENDLY:
|
|
return B_FALSE;
|
|
}
|
|
break;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
void age(lifeform_t *lf, int pct) {
|
|
if (hasjob(lf, J_GOD)) return;
|
|
|
|
lf->maxhp -= pctof(pct,lf->maxhp);
|
|
limit(&lf->maxhp, 0, NA);
|
|
limit(&lf->hp, NA, lf->maxhp);
|
|
if (isplayer(lf)) {
|
|
msg("^BYour body ages unnaturally!");
|
|
statdirty = B_TRUE;
|
|
}
|
|
}
|
|
|
|
void adjustdamforblessings(int *dam, lifeform_t *victim, int blessed) {
|
|
if ((blessed == B_BLESSED) && lfhasflagval(victim, F_DTVULN, DT_HOLY, NA, NA, NULL)) {
|
|
// a little extra damage
|
|
*dam = pctof(125, *dam);
|
|
}
|
|
}
|
|
|
|
void applylfdammod(int *dam, lifeform_t *lf, object_t *wep) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags = 0,i;
|
|
enum SKILLLEVEL slev;
|
|
// modify for strength
|
|
if (!hasflag(wep->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) {
|
|
*dam = *dam + getstrdammod(lf);
|
|
}
|
|
|
|
// strength scaling on weapon
|
|
getflags(wep->flags, retflag, &nretflags, F_ATTREQ, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[0] == A_STR) {
|
|
int pctmod;
|
|
meetsattreq(lf, retflag[i], wep, &pctmod);
|
|
if (pctmod != 0) {
|
|
*dam = pctof(100 + pctmod, *dam);
|
|
}
|
|
}
|
|
}
|
|
|
|
// modify for injuries
|
|
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
switch (retflag[i]->val[0]) {
|
|
case IJ_TORSOBRUISEDBAD: *dam = pctof(90, *dam); break;
|
|
}
|
|
}
|
|
|
|
// extra damage for being skilled?
|
|
slev = getweaponskill(lf, wep);
|
|
if (slev >= 3) {
|
|
float pctextra;
|
|
pctextra = ((slev - 2) * 10);
|
|
*dam += pctof(pctextra, *dam);
|
|
}
|
|
}
|
|
|
|
|
|
void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o) {
|
|
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 armourfits(lifeform_t *lf, object_t *o, enum ERROR *reason) {
|
|
enum LFSIZE lfsize,armsize;
|
|
|
|
lfsize = getlfsize(lf);
|
|
armsize = getarmoursize(o);
|
|
|
|
if (armsize != SZ_ANY) {
|
|
if (armsize > lfsize) {
|
|
if (reason) *reason = E_TOOBIG;
|
|
return B_FALSE;
|
|
} else if (armsize < lfsize) {
|
|
if (reason) *reason = E_TOOSMALL;
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
// returns TRUE if 'lf' agrees to share knowledge with the player
|
|
int askforinfo(lifeform_t *lf, int diffmod) {
|
|
int alignmod = 0;
|
|
alignmod = getalignmod(lf);
|
|
|
|
if (lfhasflag(lf, F_NOINFO)) {
|
|
// refusing to give info
|
|
sayphrase(lf, SP_INFO_REFUSE_AGAIN, SV_TALK, NA, NULL);
|
|
return B_FALSE;
|
|
} else {
|
|
int doinfo = B_FALSE;
|
|
int askingprice = -1;
|
|
char lfname[BUFLEN];
|
|
char buf[BUFLEN];
|
|
flag_t *f;
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
if (areallies(player, lf)) {
|
|
askingprice = 0;
|
|
} else {
|
|
// they will consider it - now negotiate a price
|
|
f = lfhasflag(lf, F_INFOPRICE);
|
|
if (f) { // already got a price in mind?
|
|
askingprice = f->val[0];
|
|
} else {
|
|
int result;
|
|
int difficulty;
|
|
int greedy = B_FALSE;
|
|
|
|
// delvers are greedy, so they'll nearly always help, but
|
|
// nearly always want money too!
|
|
if ((lf->race->id == R_DWARF) && (player->race->id != R_DWARF)) {
|
|
greedy = B_TRUE;
|
|
diffmod += 15;
|
|
}
|
|
|
|
difficulty = 20 + diffmod + ((gettr(player) - gettr(lf))*2);
|
|
if (real_skillcheck(player, SC_SPEECH, difficulty, alignmod, &result)) {
|
|
askingprice = 0;
|
|
// passed - free!
|
|
} else {
|
|
if (!greedy && (result <= 40)) {
|
|
// will not help!
|
|
askingprice = -1;
|
|
} else {
|
|
// will help for gold
|
|
askingprice = rnd(gettr(lf)*5, gettr(lf)*15 );
|
|
limit(&askingprice, 1, NA); // just in case
|
|
}
|
|
}
|
|
|
|
if (askingprice == -1) {
|
|
addflag(lf->flags, F_NOINFO, B_TRUE, NA, NA, NULL);
|
|
sayphrase(lf, SP_INFO_REFUSE, SV_TALK, NA, NULL);
|
|
return B_FALSE;
|
|
} else {
|
|
addflag(lf->flags, F_INFOPRICE, askingprice, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (askingprice != 0) {
|
|
// modify for same job
|
|
if (getjob(player) == getjob(lf)) {
|
|
askingprice = pctof(50, askingprice);
|
|
} else if (player->race->baseid == lf->race->baseid) { // modify for same race
|
|
askingprice = pctof(80, askingprice);
|
|
}
|
|
// modify by charisma
|
|
askingprice = pctof(100 - getstatmod(player, A_CHA), askingprice);
|
|
limit(&askingprice, 0, NA);
|
|
}
|
|
|
|
if (askingprice > 0) {
|
|
char ch;
|
|
sayphrase(lf, SP_INFO_ASKPRICE, SV_TALK, askingprice, NULL);
|
|
more();
|
|
|
|
if (askingprice > countmoney(player->pack)) {
|
|
msg("You can't afford to pay $%d.", askingprice);
|
|
return B_FALSE;
|
|
} else {
|
|
snprintf(buf, BUFLEN, "Pay $%d for information", askingprice);
|
|
ch = askchar(buf, "yn","n", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
doinfo = B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
doinfo = B_TRUE;
|
|
}
|
|
|
|
if (doinfo) {
|
|
sayphrase(lf, SP_INFO_ACCEPT, SV_TALK, NA, NULL);
|
|
return B_TRUE;
|
|
} else {
|
|
sayphrase(lf, SP_INFO_DECLINE_WONTPAY, SV_TALK, askingprice, NULL);
|
|
}
|
|
} // end if !nohire
|
|
return B_FALSE;
|
|
}
|
|
|
|
/*
|
|
int askforpayment(lifeform_t *shk, lifeform_t *lf) {
|
|
char saybuf[BUFLEN];
|
|
int totcost = 0;
|
|
int nitems,shopid;
|
|
flag_t *f;
|
|
f = lfhasflag(shk, F_OWNSSHOP);
|
|
if (f) {
|
|
shopid = f->val[0];
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
|
|
totcost = getowing(lf, shopid, &nitems);
|
|
|
|
if (nitems == 1) {
|
|
snprintf(saybuf, BUFLEN, "That will cost you $%d.", totcost);
|
|
} else {
|
|
snprintf(saybuf, BUFLEN, "That brings your bill to $%d.", totcost);
|
|
}
|
|
say(shk, saybuf, SV_TALK);
|
|
return B_FALSE;
|
|
}
|
|
*/
|
|
|
|
char *assignnpcname(flagpile_t *fp) {
|
|
npcname_t *sel;
|
|
int *poss;
|
|
int nposs = 0,i;
|
|
poss = malloc(numnpcnames * sizeof(int));
|
|
|
|
// already got one?
|
|
if (hasflag(fp, F_NAME)) {
|
|
return NULL;
|
|
}
|
|
// count possibilities
|
|
for (i = 0;i < numnpcnames; i++) {
|
|
if (npcname[i].valid) {
|
|
poss[nposs++] = i;
|
|
}
|
|
}
|
|
// get random name
|
|
i = poss[rnd(0,nposs-1)];
|
|
sel = &npcname[i];
|
|
|
|
// none else can use this name now
|
|
sel->valid = B_FALSE;
|
|
addflag(fp, F_NAME, NA, NA, NA, sel->name);
|
|
|
|
free(poss);
|
|
return sel->name;
|
|
}
|
|
|
|
// is this attrib increasable on levelup?
|
|
int attrincreasable(enum ATTRIB a) {
|
|
switch (a) {
|
|
case A_AGI:
|
|
case A_CON:
|
|
case A_IQ:
|
|
case A_STR:
|
|
case A_WIS:
|
|
return B_TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void autoshortcut(lifeform_t *lf, enum OBTYPE spellid) {
|
|
flag_t *retflag[MAXCANDIDATES],*f;
|
|
int nretflags,i,found = B_FALSE;
|
|
int min = 1;
|
|
objecttype_t *ot = NULL;
|
|
getflags(lf->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->val[0] == spellid) {
|
|
found = B_TRUE;
|
|
}
|
|
}
|
|
if (!found) {
|
|
return;
|
|
}
|
|
|
|
ot = findot(spellid);
|
|
if (ot) {
|
|
// set to lowest possible shortcut
|
|
min = getnextshortcut(lf);
|
|
if (min < 10) {
|
|
addflag(lf->flags, F_SHORTCUT, min, NA, NA, ot->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;
|
|
flag_t *retflag[MAXCANDIDATES],*f;
|
|
int nretflags,i;
|
|
|
|
if (hasjob(lf, J_COMMANDO)) {
|
|
return;
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
slev = PR_NOVICE;
|
|
} else {
|
|
int max;
|
|
max = gettr(lf)/3;
|
|
limit(&max, PR_NOVICE, PR_MASTER);
|
|
slev = rnd(PR_NOVICE, max);
|
|
}
|
|
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
//if (isweapon(o) && canweild(lf, o)) {
|
|
if (isweapon(o)) {
|
|
sk = getobskill(o->flags);
|
|
if (sk && !getskill(lf, sk->id)) {
|
|
giveskilllev(lf, sk->id, slev);
|
|
}
|
|
|
|
// monsters:increase stats to match attribn requirements for starting
|
|
// weapon.
|
|
//if (!isplayer(lf)) {
|
|
getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (getattr(lf, f->val[0]) < f->val[1]) {
|
|
setattr(lf, f->val[0], f->val[1]);
|
|
}
|
|
}
|
|
//}
|
|
nweps++;
|
|
}
|
|
if (isfirearm(o) && canweild(lf, o)) {
|
|
giveskilllev(lf, SK_RANGED, slev);
|
|
}
|
|
if (isarmour(o) && canwear(lf, o, BP_NONE)) {
|
|
flag_t *f;
|
|
// evasion penalty?
|
|
f = hasflag(o->flags, F_EVASION);
|
|
if (f && (f->val[0] < 0)) {
|
|
giveskilllev(lf, SK_ARMOUR, slev);
|
|
}
|
|
}
|
|
if (isshield(o) && canwear(lf, o, BP_NONE)) {
|
|
giveskilllev(lf, SK_SHIELDS, slev);
|
|
}
|
|
}
|
|
|
|
// monsters must get unarmed skill!
|
|
if (!nweps && !isplayer(lf)) {
|
|
giveskilllev(lf, SK_UNARMED, slev);
|
|
}
|
|
|
|
}
|
|
|
|
// hand out random spells to monsters
|
|
void autospells(lifeform_t *lf, int howmany) {
|
|
int nretflags,i,nposs = 0;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
enum OBTYPE poss[MAXCANDIDATES];
|
|
int powerposs[MAXCANDIDATES];
|
|
int mapdiff;
|
|
|
|
mapdiff = getmapdifficulty(lf->cell->map);
|
|
|
|
// get list of possible random spells for this lf.
|
|
// set random spells?
|
|
getflags(lf->flags, retflag, &nretflags, F_RNDSPELLPOSS, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
poss[nposs] = retflag[i]->val[0];
|
|
texttospellopts(retflag[i]->text, "pw:", &powerposs[nposs], NULL);
|
|
nposs++;
|
|
}
|
|
// random spells from schools?
|
|
getflags(lf->flags, retflag, &nretflags, F_RNDSPELLSCHOOL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
int min,max;
|
|
enum SPELLSCHOOL ss;
|
|
int thispower;
|
|
objecttype_t *sp;
|
|
ss = retflag[i]->val[0];
|
|
if (ss == SS_NONE) {
|
|
ss = getrandomspellschool(NULL, B_FALSE);
|
|
}
|
|
min = retflag[i]->val[1];
|
|
max = retflag[i]->val[2];
|
|
texttospellopts(retflag[i]->text, "pw:", &thispower, NULL);
|
|
for (sp = objecttype; sp ; sp = sp->next) {
|
|
if ((sp->obclass->id == OC_SPELL) && spellokformonsters(sp->id)) {
|
|
int lev;
|
|
lev = getspelllevel(sp->id);
|
|
if (spellisfromschool(sp->id, ss) &&
|
|
(lev >= min) && (lev <= max)) {
|
|
poss[nposs] = sp->id;
|
|
powerposs[nposs] = thispower;
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// power = 0 means select it based on depth
|
|
for (i = 0 ; i < nposs; i++) {
|
|
if (powerposs[nposs] == 0) {
|
|
powerposs[nposs] = mapdiff;
|
|
limit(&(powerposs[nposs]), 1, getspellmaxpower(poss[nposs]));
|
|
}
|
|
}
|
|
|
|
// now we have a list of all possible spells. select from these.
|
|
for (i = 0; (i < howmany) && nposs; i++) {
|
|
int sel,n;
|
|
enum OBTYPE spellid;
|
|
char pwbuf[BUFLEN];
|
|
sel = rnd(0,nposs-1);
|
|
spellid = poss[sel];
|
|
sprintf(pwbuf, "pw:%d;",powerposs[sel]);
|
|
//addflag(lf->flags, F_CANCAST, sel, NA, NA, pwbuf);
|
|
addflag(lf->flags, F_CANCAST, spellid, NA, NA, pwbuf);
|
|
// remove this one...
|
|
for (n = sel; n < nposs-1; n++) {
|
|
poss[n] = poss[n+1];
|
|
powerposs[n] = powerposs[n+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
}
|
|
|
|
void autotarget(lifeform_t *lf) {
|
|
object_t *gun;
|
|
lifeform_t *targ = NULL,*newtarg = NULL;
|
|
int closest;
|
|
int i;
|
|
int gunrange;
|
|
int targid;
|
|
|
|
gun = getfirearm(lf);
|
|
if (gun && getammo(gun)) {
|
|
gunrange = getfirearmrange(gun);
|
|
// already got a target?
|
|
targid = getguntargetid(lf);
|
|
} else {
|
|
gunrange = -1;
|
|
targid = -1;
|
|
}
|
|
|
|
if (targid == -1) {
|
|
// no existing target
|
|
targ = NULL;
|
|
} else {
|
|
targ = findlf(NULL, targid);
|
|
if (targ) {
|
|
// dead? remove target and keep going.
|
|
if (isdead(targ)) {
|
|
// clear target ?
|
|
targ = NULL;
|
|
} else if (!cansee(lf, targ) || !haslof(lf->cell, targ->cell, getfirearmloftype(lf), NULL)) {
|
|
// clear target ?
|
|
targ = NULL;
|
|
} else {
|
|
flag_t *f;
|
|
|
|
// already got a valid target. just update targetting text.
|
|
f = hasflag(lf->flags, F_GUNTARGET);
|
|
if (f) {
|
|
char targbuf[BUFLEN];
|
|
makegunaimstring(lf, f->val[0], targbuf);
|
|
if (!streq(targbuf, f->text)) {
|
|
free(f->text);
|
|
f->text = strdup(targbuf);
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
updatestatus();
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
// target no longer exists
|
|
// clear target
|
|
targ = NULL;
|
|
}
|
|
}
|
|
|
|
if (gun && (gunrange != -1)) {
|
|
// find new target
|
|
newtarg = NULL;
|
|
closest = 9999;
|
|
for (i = 0; i < lf->nlos; i++) {
|
|
cell_t *c;
|
|
c = lf->los[i];
|
|
if (c->lf && (c->lf != lf) && cansee(lf, c->lf) && isingunrange(lf, c) && areenemies(lf, c->lf)) {
|
|
if (haslof(lf->cell, c, getfirearmloftype(lf), NULL)) {
|
|
int thisdist;
|
|
thisdist = getcelldist(lf->cell, c);
|
|
if (thisdist < closest) {
|
|
newtarg = c->lf;
|
|
closest = thisdist;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newtarg != targ) {
|
|
setguntarget(lf, newtarg);
|
|
}
|
|
|
|
if (!newtarg) {
|
|
killflagsofid(lf->flags, F_GUNTARGET);
|
|
}
|
|
}
|
|
|
|
int issmellablelf(lifeform_t *lf) {
|
|
if (lf->race->raceclass->id != RC_UNDEAD) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
int isswimming(lifeform_t *lf) {
|
|
if (!lf) return B_FALSE;
|
|
if (gamemode != GM_GAMESTARTED) {
|
|
return B_FALSE;
|
|
}
|
|
if (!isairborne(lf) && (getcellwaterdepth(lf->cell, lf) >= DP_WAIST) &&
|
|
getskill(lf, SK_SWIMMING)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isunconscious(lifeform_t *lf) {
|
|
if (lfhasflagval(lf, F_ASLEEP, NA, ST_KO, NA, NULL)) return B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isundead(lifeform_t *lf) {
|
|
if (lf->race->raceclass->id == RC_UNDEAD) {
|
|
return B_TRUE;
|
|
}
|
|
if (lfhasflag(lf, F_UNDEAD)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
flag_t *isvulnto(flagpile_t *fp, enum DAMTYPE dt, int onlytemp) {
|
|
flag_t *f;
|
|
dt = basedamagetype(dt);
|
|
|
|
f = hasflagval(fp, F_DTVULN, dt, NA, NA, NULL);
|
|
if (f) {
|
|
if (!onlytemp || (f->lifetime != FROMRACE)) {
|
|
return f;
|
|
}
|
|
}
|
|
f = hasflagval(fp, F_DTVULN, DT_ALL, NA, NA, NULL);
|
|
if (f) {
|
|
if (!onlytemp || (f->lifetime != FROMRACE)) {
|
|
return f;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int isweaponskill(enum SKILL skid) {
|
|
switch (skid) {
|
|
case SK_AXES:
|
|
case SK_CLUBS:
|
|
case SK_EXOTICWEPS:
|
|
case SK_LONGBLADES:
|
|
case SK_POLEARMS:
|
|
case SK_SHORTBLADES:
|
|
case SK_STAVES:
|
|
case SK_WHIPS:
|
|
//case SK_UNARMED:
|
|
return B_TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
enum FLAG iswoozy(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_DRUNK)) return 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 killpoisontype(poisontype_t *pt) {
|
|
poisontype_t *nextone, *lastone;
|
|
|
|
// free mem
|
|
free(pt->name);
|
|
free(pt->desc);
|
|
free(pt->damverb);
|
|
|
|
// remove from list
|
|
nextone = pt->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = pt->prev;
|
|
} else { /* last */
|
|
lastpoisontype = pt->prev;
|
|
}
|
|
|
|
if (pt->prev == NULL) {
|
|
/* first */
|
|
nextone = pt->next;
|
|
free(firstpoisontype);
|
|
firstpoisontype = nextone;
|
|
} else {
|
|
lastone = pt->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
void killrace(race_t *r) {
|
|
race_t *nextone, *lastone;
|
|
|
|
// free mem
|
|
free(r->name);
|
|
killflagpile(r->flags);
|
|
|
|
// remove from list
|
|
nextone = r->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = r->prev;
|
|
} else { /* last */
|
|
lastrace = r->prev;
|
|
}
|
|
|
|
if (r->prev == NULL) {
|
|
/* first */
|
|
nextone = r->next;
|
|
free(firstrace);
|
|
firstrace = nextone;
|
|
} else {
|
|
lastone = r->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
void killsubjob(subjob_t *sj) {
|
|
subjob_t *nextone, *lastone;
|
|
|
|
// free mem
|
|
free(sj->name);
|
|
free(sj->desc);
|
|
|
|
// remove from list
|
|
nextone = sj->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = sj->prev;
|
|
} else { /* last */
|
|
lastsubjob = sj->prev;
|
|
}
|
|
|
|
if (sj->prev == NULL) {
|
|
/* first */
|
|
nextone = sj->next;
|
|
free(firstsubjob);
|
|
firstsubjob = nextone;
|
|
} else {
|
|
lastone = sj->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
flag_t *levelabilityready(lifeform_t *lf) {
|
|
flag_t *f;
|
|
int i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
if (!lfhasflag(lf, F_HASNEWLEVEL)) return NULL;
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_LEVABIL, F_LEVFLAG, F_LEVSPELL, F_LEVSPELLSCHOOL, F_LEVSPELLSCHOOLFROMX, F_LEVSKILL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
// we'll set lifetime to -1 while actually assigning these.
|
|
if (f->lifetime == FROMJOB) {
|
|
switch (f->id) {
|
|
case F_LEVFLAG:
|
|
if (lf->newlevel == f->val[0]) {
|
|
return f;
|
|
}
|
|
break;
|
|
case F_LEVABIL:
|
|
case F_LEVSKILL:
|
|
case F_LEVSPELL:
|
|
case F_LEVSPELLSCHOOL:
|
|
case F_LEVSPELLSCHOOLFROMX:
|
|
if ((f->val[0] < 100) && (lf->newlevel == f->val[0])) {
|
|
return f;
|
|
} else if ((f->val[0] >= 100) && (lf->newlevel % (f->val[0] - 100) == 0) ) {
|
|
return f;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int loadfirearm(lifeform_t *lf, object_t *gun, object_t *ammo) {
|
|
int amttoload;
|
|
int guncapacityleft,guncapacityused;
|
|
char gunname[BUFLEN];
|
|
object_t *curammo;
|
|
flag_t *f,*ammoflag;
|
|
|
|
curammo = gun->contents->first;
|
|
getobname(gun, gunname, 1);
|
|
|
|
if (curammo) {
|
|
guncapacityused = curammo->amt;
|
|
} else {
|
|
guncapacityused = 0;
|
|
}
|
|
|
|
ammoflag = hasflag(gun->flags, F_AMMOCAPACITY);
|
|
guncapacityleft = ammoflag->val[0] - guncapacityused;
|
|
|
|
// is ammo ok?
|
|
if (curammo && (ammo->type->id != curammo->type->id)) {
|
|
if (lf) {
|
|
// unload current ammo first
|
|
moveob(curammo, lf->pack, curammo->amt);
|
|
if (lf && isplayer(lf)) {
|
|
char buf[BUFLEN];
|
|
getobname(curammo, buf, curammo->amt);
|
|
msg("You unload %s from your %s.", buf, noprefix(gunname));
|
|
}
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
if (guncapacityleft <= 0) {
|
|
if (isplayer(lf)) {
|
|
msg("Your %s is already fully loaded.", noprefix(gunname));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (ammo->amt >= guncapacityleft) {
|
|
amttoload = guncapacityleft;
|
|
} else {
|
|
amttoload = ammo->amt;
|
|
}
|
|
|
|
// load ammo into gun
|
|
moveob(ammo, gun->contents, amttoload);
|
|
|
|
// take time
|
|
if (lf) {
|
|
if (getskill(lf, SK_RANGED) < PR_ADEPT) {
|
|
f = hasflag(gun->flags, F_RELOADTURNS);
|
|
taketime(lf, getactspeed(lf)*f->val[0]);
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
//char buf[BUFLEN];
|
|
//getobname(gun->contents->first, buf, gun->contents->first->amt);
|
|
//msg("You load %s into your %s.", buf, noprefix(gunname));
|
|
msg("You reload your %s.", noprefix(gunname));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s reloads %s.", lfname, gunname);
|
|
}
|
|
|
|
// update target
|
|
autotarget(lf);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int loadfirearmfast(lifeform_t *lf, int onpurpose) {
|
|
object_t *gun,*newammo,*curammo;
|
|
gun = getfirearm(lf);
|
|
if (!gun) {
|
|
if (isplayer(lf) && onpurpose) {
|
|
msg("You have no firearm equipped.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
curammo = getammo(gun);
|
|
if (curammo) {
|
|
if (isplayer(lf) && onpurpose) {
|
|
char buf[BUFLEN];
|
|
getobname(gun, buf, 1);
|
|
msg("Your %s is already loaded!", noprefix(buf));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
newammo = getrandomammo(lf);
|
|
if (newammo) {
|
|
loadfirearm(lf, gun, newammo);
|
|
} else {
|
|
if (isplayer(lf) && onpurpose) {
|
|
char buf[BUFLEN];
|
|
getobname(gun, buf, 1);
|
|
msg("You have no ammo for your %s.", noprefix(buf));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
if ((gamemode == GM_GAMESTARTED) && onpurpose) taketime(lf, getactspeed(lf));
|
|
return B_FALSE;
|
|
}
|
|
|
|
void loseconcentration(lifeform_t *lf) {
|
|
// stop sprinting
|
|
stopsprinting(lf);
|
|
|
|
// stop casting spells
|
|
killflagsofid(lf->flags, F_CASTINGSPELL);
|
|
|
|
// boost spells end
|
|
stopallspells(lf);
|
|
|
|
// stop hiding
|
|
killflagsofid(lf->flags, F_HIDING);
|
|
|
|
interrupt(lf);
|
|
}
|
|
|
|
void loseconsciousness(lifeform_t *lf, int howlong, lifeform_t *fromlf) {
|
|
if (!isunconscious(lf)) {
|
|
fallasleep(lf, ST_KO, rnd(50,100));
|
|
if (fromlf && isplayer(fromlf)) {
|
|
pleasegodmaybe(R_GODMERCY, 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
// lose hp, and adjust damage based on resistances
|
|
int losehp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc) {
|
|
return losehp_real(lf, amt, damtype, fromlf, damsrc, B_DAMADJUST, NULL, B_RETALIATE, NULL, B_DAMEFFECTS);
|
|
}
|
|
|
|
// returns the amt of damage taken
|
|
int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam, object_t *fromob, int retaliate, int *waskod, int doeffects) {
|
|
char buf[BUFLEN];
|
|
char buf2[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
int prelowhp = B_FALSE,ko = B_FALSE;
|
|
int murder = B_FALSE;
|
|
flag_t *f;
|
|
int predead = B_FALSE;
|
|
|
|
if (gamemode < GM_GAMESTARTED) return 0;
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
if (isdead(lf)) {
|
|
predead = B_TRUE;
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
|
|
if (isbleeding(lf)) {
|
|
prelowhp = B_TRUE;
|
|
}
|
|
|
|
if (fromlf && areallies(lf, fromlf)) {
|
|
murder = B_TRUE;
|
|
}
|
|
|
|
|
|
// adjust for source object's material
|
|
if (fromob) {
|
|
if (lfhasflagval(lf, F_MATIMMUNE, fromob->material->id, NA, NA, NULL)) {
|
|
amt = 0;
|
|
} else {
|
|
f = lfhasflagval(lf, F_MATVULN, fromob->material->id, NA, NA, NULL);
|
|
if (f) {
|
|
// will always do some damage
|
|
if (!amt) amt = 1;
|
|
amt = pctof(f->val[1], amt);
|
|
// announce
|
|
if (isplayer(lf)) {
|
|
msg("The touch of %s sears you!", fromob->material->name);
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("The touch of %s sears %s!", fromob->material->name, lfname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// adjust damage!
|
|
if (reducedam) {
|
|
adjustdamlf(lf, &amt, damtype);
|
|
}
|
|
|
|
// stop resting/running!
|
|
interrupt(lf);
|
|
|
|
// stop hiding
|
|
killflagsofid(lf->flags, F_HIDING);
|
|
|
|
// methods of knocking unconscious
|
|
if (!lfcanbekod(lf)) {
|
|
// merciful weapons - these will ALWAYS ko, even if
|
|
// they are already unconscious.
|
|
if (!ko && fromob) {
|
|
f = hasflag(fromob->flags, F_MERCIFUL);
|
|
if (f && (amt >= lf->hp)) {
|
|
ko = B_TRUE;
|
|
if (fromob->pile->owner && cansee(player, fromob->pile->owner)) {
|
|
f->known = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// bashing damage sometimes ko's
|
|
// damage when eating corpses also does this.
|
|
if (!ko && !isunconscious(lf)) {
|
|
int threshold,kochance = 0;
|
|
int damtypeok = B_FALSE;
|
|
int playerinvolved = B_FALSE;
|
|
|
|
if (isplayer(lf)) {
|
|
playerinvolved = B_TRUE;
|
|
} else if (fromlf && isplayer(fromlf)) {
|
|
playerinvolved = B_TRUE;
|
|
}
|
|
|
|
if (damtype == DT_BASH) {
|
|
damtypeok = B_TRUE;
|
|
} else if ((damtype == DT_DIRECT) && fromob && hasflag(fromob->flags, F_EATCONFER)) {
|
|
damtypeok = B_TRUE;
|
|
}
|
|
|
|
if (damtypeok) {
|
|
if (playerinvolved && godprayedto(R_GODMERCY)) {
|
|
threshold = -10;
|
|
if (isplayer(lf)) {
|
|
kochance = 75;
|
|
} else {
|
|
kochance = 50;
|
|
}
|
|
} else {
|
|
threshold = -5;
|
|
kochance = 30;
|
|
}
|
|
}
|
|
|
|
if (kochance) {
|
|
int hpleftafterdam;
|
|
// if this damage would reduce the lf to between -threshold and 0 hp
|
|
hpleftafterdam = lf->hp - amt;
|
|
if ((lf->hp > 1) && (hpleftafterdam >= threshold) && (hpleftafterdam <= 0)) {
|
|
if (pctchance(kochance)) {
|
|
ko = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ko && !isunconscious(lf)) {
|
|
if (fromlf && lfhasflag(fromlf, F_STRIKETOKO)) {
|
|
if (cansee(fromlf, lf)) {
|
|
ko = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// just knock them out, don't kill.
|
|
if (ko) {
|
|
amt = lf->hp - 1; // ie end up at 1hp
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
// large damage will be stopped by a ring of miracles
|
|
if ((amt >= (lf->maxhp / 2)) && (amt >= 20)) {
|
|
if (useringofmiracles(lf, 1)) {
|
|
return 0;
|
|
}
|
|
}
|
|
*/
|
|
|
|
// drop blood
|
|
if (damtypecausesbleed(damtype, fromob)) {
|
|
bleed(lf, B_NOSPLATTER);
|
|
}
|
|
|
|
if (hasflag(lf->flags, F_DEBUG)) {
|
|
msg("[%s takes %d dam]",lfname, amt);
|
|
}
|
|
|
|
// remember the amt of damage
|
|
if (isplayer(lf)) {
|
|
if (lf->bartimer == 2) {
|
|
lf->damlastturn += amt;
|
|
} else {
|
|
lf->damlastturn = amt;
|
|
lf->bartimer = 2;
|
|
}
|
|
}
|
|
|
|
|
|
// phantasms have a fake hp counter
|
|
f = lfhasflag(lf, F_PHANTASM);
|
|
if (f) {
|
|
f->val[0] -= amt;
|
|
// they die when their FAKE hp drops below 0
|
|
if (f->val[0] <= 0) {
|
|
setlastdam(lf, "disappation");
|
|
lf->lastdamtype = DT_DIRECT;
|
|
lf->hp = 0;
|
|
}
|
|
} else {
|
|
// take damage
|
|
lf->hp -= amt;
|
|
}
|
|
|
|
if (!predead) {
|
|
// fill in lastdam...
|
|
lf->lastdamtype = damtype;
|
|
|
|
if (fromlf) {
|
|
lf->lastdamlf = fromlf->id;
|
|
} else {
|
|
lf->lastdamlf = -1;
|
|
}
|
|
}
|
|
|
|
// if they died
|
|
if (lf->hp <= 0) {
|
|
if (fromlf && (getallegiance(fromlf) == AL_FRIENDLY)) {
|
|
addflag(lf->flags, F_KILLEDBYPLAYER, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
|
|
if (doeffects || ko) {
|
|
losehpeffects(lf, amt, damtype, fromlf, fromob, retaliate, ko, waskod, prelowhp);
|
|
}
|
|
|
|
if (!predead) {
|
|
//////////////////////////////////////////
|
|
// Figure out death text for tombstone.
|
|
// eg. Killed by something
|
|
// Impaled by something.
|
|
// etc
|
|
//////////////////////////////////////////
|
|
|
|
// replace 'the' at start of damsrc with 'a'
|
|
if (strstr(damsrc, "the ") == damsrc) {
|
|
snprintf(buf, BUFLEN, "a %s", (damsrc+4));
|
|
} else {
|
|
strcpy(buf, damsrc);
|
|
}
|
|
|
|
// fill in damage amount
|
|
snprintf(buf2, BUFLEN, "^%d damage",amt);
|
|
strcat(buf, buf2);
|
|
|
|
// unseen?
|
|
if (fromlf && !cansee(lf, fromlf)) {
|
|
strcat(buf, "^unseen");
|
|
}
|
|
|
|
// "while xxx"
|
|
strcpy(buf2, "");
|
|
if ((countcellexits(lf->cell, DT_COMPASS) == 1) && fromlf && (getcelldist(fromlf->cell, lf->cell) == 1)) {
|
|
strcat(buf2, "^while cornered");
|
|
}
|
|
if (!hasfreeaction(lf)) {
|
|
if (strlen(buf2)) strcat(buf2, " and helpless");
|
|
else strcat(buf2, "^while helpless");
|
|
}
|
|
if (strlen(buf2)) strcat(buf, buf2);
|
|
|
|
setlastdam(lf, buf);
|
|
|
|
if (fromlf && willeatlf(fromlf, lf)) {
|
|
// this string is special - die() checks for this to see whether
|
|
// to add "partially eaten" to the corpse.
|
|
setkillverb(lf, "Eaten");
|
|
} else {
|
|
switch (damtype) {
|
|
case DT_ACID: setkillverb(lf, "Dissolved"); break;
|
|
case DT_COLD: setkillverb(lf, "Frozen"); break;
|
|
case DT_CRUSH: setkillverb(lf, "Crushed"); break;
|
|
case DT_ELECTRIC: setkillverb(lf, "Electrocuted"); break;
|
|
case DT_FIRE:
|
|
case DT_HEAT:
|
|
setkillverb(lf, "Incinerated"); break;
|
|
case DT_MELT: setkillverb(lf, "Melted"); break;
|
|
case DT_PIERCE: setkillverb(lf, "Impaled"); break;
|
|
case DT_EXPLOSIVE: setkillverb(lf, "Vaporised"); break;
|
|
default:
|
|
if (fromlf) {
|
|
if (murder) {
|
|
setkillverb(lf, "Murdered");
|
|
} else if ((amt >= lf->maxhp) && !fromob) {
|
|
setkillverb(lf, "Slaughtered");
|
|
} else {
|
|
setkillverb(lf, "Slain");
|
|
}
|
|
} else {
|
|
setkillverb(lf, "Killed");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update screen
|
|
drawstatus();
|
|
updatestatus();
|
|
return amt;
|
|
}
|
|
|
|
void losehpeffects(lifeform_t *lf, int dam, enum DAMTYPE damtype, lifeform_t *fromlf, object_t *fromob, int retaliate, int ko, int *waskod, int prelowhp) {
|
|
int postlowhp = B_FALSE;
|
|
flag_t *retflag[MAXCANDIDATES],*f;
|
|
int nretflags;
|
|
char buf[BUFLEN],lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
if (lf->hp > 0) {
|
|
// effects based on damage type, if lf is still alive
|
|
if (damtype == DT_COLD) {
|
|
int i;
|
|
if (lfhasflag(lf, F_COLDBLOOD)) {
|
|
// slow them
|
|
addtempflag(lf->flags, F_SLOWMOVE, 5, NA, NA, NULL, 10);
|
|
}
|
|
// catch a cold?
|
|
if (!skillcheck(lf, SC_CON, (dam/2) + getexposedlimbs(lf), 0)) {
|
|
poison(lf, 20+(dam*3), P_COLD, 0, "the cold", fromlf ? fromlf->race->id : R_NONE);
|
|
}
|
|
// cold will heal bruised limbs
|
|
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->lifetime > 0) {
|
|
if ((f->val[0] == IJ_LEGBRUISE) || (f->val[0] == IJ_BLACKEYE)) {
|
|
f->lifetime -= 5;
|
|
limit(&f->lifetime, 1, NA);
|
|
}
|
|
}
|
|
}
|
|
if (fromlf && isplayer(fromlf)) {
|
|
angergodmaybe(R_GODFIRE, 25, GA_HERESY);
|
|
}
|
|
} else if (damtype == DT_FIRE) {
|
|
int i;
|
|
// fire will ignire flammable lifeforms, cauterise slash wounds, etc
|
|
getflags(lf->flags, retflag, &nretflags, F_FLAMMABLELF, F_INJURY, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_FLAMMABLELF) {
|
|
if (isplayer(lf)) {
|
|
msg("You are engulfed in flames!");
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s is engulfed in flames!", lfname);
|
|
}
|
|
addobfast(lf->cell->obpile, OT_FIRESMALL);
|
|
} else if (f->id == F_INJURY) {
|
|
if ((f->val[1] == DT_SLASH) && (f->lifetime > 0)) {
|
|
f->lifetime -= 20;
|
|
limit(&f->lifetime, 1, NA);
|
|
}
|
|
}
|
|
}
|
|
} else if (damtype == DT_ELECTRIC) {
|
|
if (getraceclass(lf) == RC_ROBOT) {
|
|
f = hasflag(lf->flags, F_CONFUSED);
|
|
if (f) {
|
|
if (f->lifetime > 0) f->lifetime += rnd(dam, dam*2);
|
|
} else {
|
|
addtempflag(lf->flags, F_CONFUSED, B_TRUE, NA, NA, NULL, rnd(dam,dam*2));
|
|
}
|
|
}
|
|
} else if (damtype == DT_POISONGAS) {
|
|
if (dam > 0) {
|
|
if (!skillcheck(lf, SC_POISON, 35, 0)) { // HARD.
|
|
poison(lf, rnd(20,40), P_GAS, 2, "poison gas", fromlf ? fromlf->race->id : R_NONE);
|
|
}
|
|
}
|
|
}
|
|
} // end if hp > 0
|
|
|
|
// effects based on damage type, even if lf is dead
|
|
if (damtype == DT_ELECTRIC) {
|
|
if (hasobofmaterial(lf->cell->obpile, MT_WATER)) {
|
|
cell_t *retcell[MAX_MAPW*MAX_MAPH];
|
|
int nretcells = 0;
|
|
// anyone else in the water takes damage too
|
|
if (getconnectedwatercells(lf->cell, retcell, &nretcells)) {
|
|
int i;
|
|
animflashcells(retcell, nretcells, '/', C_WHITE, "Electricity arcs through the water!");
|
|
noise(lf->cell, NULL, NC_OTHER, SV_CAR, "arcing electricity", NULL);
|
|
for (i = 0 ; i < nretcells; i++) {
|
|
if (retcell[i]->lf && (retcell[i]->lf != lf)) {
|
|
if (!isairborne(retcell[i]->lf)) {
|
|
losehp_real(retcell[i]->lf, dam, DT_ELECTRIC, fromlf, "an electric shock", B_TRUE, NULL, B_FALSE, NULL, B_FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// special cases
|
|
if (lf->race->id == R_FUNGUSDREAM) {
|
|
char buf2[BUFLEN];
|
|
sprintf(buf, "^w%s releases a cloud of purple spores!", lfname);
|
|
sprintf(buf2, "^wSomething releases a cloud of purple spores!");
|
|
spellcloud(lf->cell, 3, UNI_SHADELIGHT, C_MAGENTA, OT_S_SLEEP, 8, B_TRUE, buf, buf2, B_FALSE, NULL);
|
|
} else if (lf->race->id == R_FUNGUSRAGE) {
|
|
char buf2[BUFLEN];
|
|
sprintf(buf, "^w%s releases a cloud of red spores!", lfname);
|
|
sprintf(buf2, "^wSomething releases a cloud of red spores!");
|
|
spellcloud(lf->cell, 3, UNI_SHADELIGHT, C_RED, OT_A_RAGE, 8, B_TRUE, buf, buf2, B_FALSE, NULL);
|
|
} else if ((lf->race->id == R_UNYON) && ((damtype == DT_SLASH) || (damtype == DT_PIERCE))) {
|
|
char buf2[BUFLEN];
|
|
sprintf(buf, "^w%s releases a cloud of fumes!", lfname);
|
|
sprintf(buf2, "^wSomething releases a cloud of fumes!");
|
|
spellcloud(lf->cell, 1, UNI_SHADELIGHT, C_GREY, OT_S_BLINDNESS, 8, B_TRUE, buf, buf2, B_TRUE, NULL);
|
|
}
|
|
|
|
|
|
if (isbleeding(lf)) {
|
|
postlowhp = B_TRUE;
|
|
}
|
|
|
|
// further effects if not dead...
|
|
if (!isdead(lf)) {
|
|
object_t *o;
|
|
|
|
// fight back if required
|
|
if (fromlf && retaliate && !ko) {
|
|
fightback(lf, fromlf);
|
|
}
|
|
|
|
if (ko) {
|
|
int kotime;
|
|
// is waskod was passed, allow the calling function to actually
|
|
// knock us unconscious.
|
|
//
|
|
// this is mainly used in attack.c, so that we can announce
|
|
// "you hit xxx. xxx loses consciousness." as opposed to
|
|
// "xxx loses consciousness. you hit xxx." which makes no sense!
|
|
kotime = rnd(50,100);
|
|
if (waskod) {
|
|
*waskod = kotime;
|
|
} else {
|
|
loseconsciousness(lf, kotime, fromlf);
|
|
}
|
|
} else {
|
|
// you wake up if you were hit, unless you were unconscious!
|
|
f = lfhasflag(lf, F_ASLEEP);
|
|
if (f && (f->val[1] != ST_KO)) {
|
|
killflagsofid(lf->flags, F_ASLEEP);
|
|
}
|
|
}
|
|
|
|
// automatic onbleed abilities?
|
|
if (postlowhp && !prelowhp) {
|
|
f = lfhasflag(lf, F_LOWHPABIL);
|
|
if (f) {
|
|
flag_t *ntm;
|
|
ntm = addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL);
|
|
abilityeffects(lf, f->val[0], lf->cell, lf, NULL);
|
|
killflag(ntm);
|
|
}
|
|
}
|
|
|
|
// elemental effects on held objects
|
|
if ((damtype == DT_FIRE) && !fromob) {
|
|
object_t *o, *nexto;
|
|
int nburnt = 0;
|
|
// up to dam/5 objects might be burnt
|
|
// fire: dam*10 chance of burning each object which is vulnerable to fire
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (isvulnto(o->flags, DT_FIRE, B_FALSE) && pctchance(dam*10)) {
|
|
int newdam;
|
|
nburnt++;
|
|
if (nburnt >= (dam/5)) break;
|
|
// object takes 1/4 of damage
|
|
newdam = pctof(25, dam);
|
|
limit(&newdam, 1, NA);
|
|
takedamage(o, newdam, DT_FIRE);
|
|
}
|
|
}
|
|
} 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, B_FALSE) && pctchance(dam*10)) {
|
|
int newdam;
|
|
// object takes 1/4 of damage
|
|
newdam = pctof(25, dam);
|
|
limit(&newdam, 1, NA);
|
|
takedamage(o, newdam, DT_COLD);
|
|
}
|
|
}
|
|
} else if (damtype == DT_WATER) {
|
|
// put out fires
|
|
extinguishlf(lf);
|
|
}
|
|
|
|
// low hitpoint warning for player
|
|
if (isplayer(lf) && (dam > 0)) {
|
|
int warnthresh;
|
|
warnthresh = (int)((float)0.25 * (float)lf->maxhp);
|
|
if ((lf->hp <= warnthresh) && (lf->hp > 0)) {
|
|
warn("*** LOW HITPOINT WARNING ***");
|
|
more();
|
|
}
|
|
}
|
|
|
|
|
|
o = hasequippedobid(lf->pack, OT_AMU_EVOLUTION);
|
|
if (o) {
|
|
enum DAMTYPE basedt;
|
|
basedt = basedamagetype(damtype);
|
|
if (basedt == DT_FIRE) {
|
|
if (!polymorphto(lf, R_LAVAX, 5)) makeknown(o->type->id);
|
|
} else if (basedt == DT_COLD) {
|
|
if (!polymorphto(lf, R_SASQUATCH, 5)) makeknown(o->type->id);
|
|
}
|
|
}
|
|
|
|
} // end if !isdead
|
|
}
|
|
|
|
void losemp(lifeform_t *lf, int amt) {
|
|
if (isplayer(lf)) {
|
|
if (lf->bartimer == 2) {
|
|
lf->mplastturn += amt;
|
|
} else {
|
|
lf->mplastturn = amt;
|
|
lf->bartimer = 2;
|
|
}
|
|
}
|
|
lf->mp -= amt;
|
|
|
|
if (lf->mp < 0) {
|
|
lf->mp = 0;
|
|
}
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
updatestatus();
|
|
}
|
|
}
|
|
|
|
|
|
void makefriendly(lifeform_t *who, int howlong) {
|
|
char lfname[BUFLEN];
|
|
|
|
getlfname(who, lfname);
|
|
if (lfhasflag(who, F_DEBUG)) {
|
|
msg("Making %s friendly.",lfname);
|
|
}
|
|
|
|
if (howlong == PERMENANT) {
|
|
killflagsofid(who->flags, F_HOSTILE);
|
|
} // if not unlimited, they'll revert back.
|
|
|
|
if (!hasflag(who->flags, F_FRIENDLY)) {
|
|
addtempflag(who->flags, F_FRIENDLY, B_TRUE, NA, NA, NULL, howlong);
|
|
}
|
|
loseaitargets(who);
|
|
|
|
}
|
|
|
|
void makeheard(lifeform_t *listener, lifeform_t *noisemaker, int showglyph, char *noisetext, int howlong) {
|
|
flag_t *f;
|
|
f = lfhasflagval(listener, F_CANHEARLF, noisemaker->id, NA, NA, NULL);
|
|
if (f) {
|
|
if (f->lifetime > 0) f->lifetime = howlong;
|
|
if (showglyph) {
|
|
f->val[1] = B_TRUE;
|
|
} else {
|
|
f->val[1] = B_FALSE;
|
|
}
|
|
if (noisetext) {
|
|
free(f->text);
|
|
f->text = strdup(noisetext);
|
|
}
|
|
} else {
|
|
f = addtempflag(listener->flags, F_CANHEARLF, noisemaker->id, showglyph, NA, noisetext, howlong);
|
|
}
|
|
if (isplayer(listener)) needredraw = B_TRUE;
|
|
}
|
|
|
|
// make lf be able to learn the given skill
|
|
// returns TRUE if we did something.
|
|
int makelearnable(lifeform_t *lf, enum SKILL skid) {
|
|
flag_t *f,*learnable;
|
|
int changed = B_FALSE;
|
|
f = hasflagval(lf->flags, F_NOSKILL, skid, NA, NA, NULL);
|
|
if (f) {
|
|
killflag(f);
|
|
}
|
|
learnable = lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL);
|
|
if (learnable) {
|
|
if (learnable->val[1] != NA) {
|
|
// able to learn thievery, but limitted
|
|
learnable->val[1] = NA;
|
|
changed = B_TRUE;
|
|
}
|
|
} else if (!getskill(lf, SK_THIEVERY)) {
|
|
// don't have the skill, not learnable
|
|
addflag(lf->flags, F_CANLEARN, skid, NA, NA, NULL);
|
|
changed = B_TRUE;
|
|
}
|
|
if (changed) {
|
|
if (isplayer(lf)) {
|
|
skill_t *sk;
|
|
sk = findskill(skid);
|
|
msg("^GYou are now capable of learning the %s skill.^n",sk->name);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int makenauseated(lifeform_t *lf, int amt, int howlong) {
|
|
flag_t *f;
|
|
|
|
switch (lf->race->raceclass->id) {
|
|
case RC_HUMANOID:
|
|
case RC_ANIMAL:
|
|
break;
|
|
default:
|
|
// only affects humanoids & animals, unless you have enhanced smell.
|
|
if (!lfhasflag(lf, F_ENHANCESMELL)) {
|
|
return B_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (lfhasflag(lf, F_STENCH)) return B_TRUE; // your own smell makes you used to it
|
|
if (lfhasflag(lf, F_NOSMELL)) return B_TRUE; // can't smell it.
|
|
if (!hasbp(lf, BP_HEAD)) return B_TRUE; // can't smell with no head
|
|
|
|
if (lfhasflag(lf, F_ENHANCESMELL)) amt += 2;
|
|
|
|
//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) {
|
|
int volume = 1;
|
|
char hear[BUFLEN], see[BUFLEN];
|
|
|
|
if (!getnoisedetails(lf, nid, NULL, hear, see, &volume)) {
|
|
// success
|
|
noise(lf->cell, lf, noisetypetoclass(nid), volume, strlen(hear) ? hear : NULL, strlen(see) ? see : NULL);
|
|
}
|
|
}
|
|
|
|
void mayusespellschool(flagpile_t *fp, enum SPELLSCHOOL ss, enum FLAG how, int overridepower) {
|
|
objecttype_t *ot;
|
|
skill_t *sk;
|
|
|
|
sk = findskill(getschoolskill(ss));
|
|
|
|
if (sk) {
|
|
if (!hasflagval(fp, F_STARTSKILL, sk->id, NA, NA, NULL)) {
|
|
addflag(fp, F_STARTSKILL, sk->id, PR_NOVICE, NA, NULL);
|
|
}
|
|
}
|
|
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
//if (ot->obclass->id == OC_SPELL) {
|
|
if (hasflagval(ot->flags, F_SPELLSCHOOL, ss, NA, NA, NULL)) {
|
|
if (!hasflagval(fp, how, ot->id, NA, NA, NULL)) {
|
|
char text[BUFLEN];
|
|
if (overridepower) {
|
|
sprintf(text, "pw:%d;",overridepower);
|
|
} else {
|
|
strcpy(text, "");
|
|
}
|
|
addflag(fp, how, ot->id, NA, NA, text);
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
}
|
|
|
|
int meetsallattreqs(lifeform_t *lf, object_t *o) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
|
|
// other flags to check
|
|
getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (!meetsattreq(lf, retflag[i], o, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// if you don't meet it, return why not in 'reason'
|
|
int meetsattreq(lifeform_t *lf, flag_t *f, object_t *o, int *pctmod) {
|
|
enum ATTRIB att;
|
|
int valneeded,valbonus;
|
|
int myval;
|
|
int neededdiff,bonusdiff;
|
|
int dopenaltycheck = B_TRUE, dobonuscheck = B_FALSE;
|
|
int scaleamt;
|
|
enum {
|
|
NONE=0,
|
|
PENALTY=1,
|
|
BONUS = 2,
|
|
} bonorpen = NONE;
|
|
|
|
|
|
if (pctmod) {
|
|
*pctmod = 0;
|
|
}
|
|
|
|
att = f->val[0];
|
|
valneeded = f->val[1];
|
|
if (valneeded == NA) {
|
|
dopenaltycheck = B_FALSE;
|
|
} else {
|
|
dopenaltycheck = B_TRUE;
|
|
}
|
|
valbonus = f->val[2];
|
|
if (valbonus == NA) {
|
|
dobonuscheck = B_FALSE;
|
|
} else {
|
|
dobonuscheck = B_TRUE;
|
|
}
|
|
|
|
if (strlen(f->text)) {
|
|
scaleamt = atoi(f->text);
|
|
} else {
|
|
scaleamt = 0;
|
|
}
|
|
|
|
// modify for masterwork
|
|
if (o) {
|
|
if (hasflag(o->flags, F_MASTERWORK)) {
|
|
if (dopenaltycheck) {
|
|
valneeded -= 10;
|
|
if (valneeded < 0) valneeded = 0;
|
|
}
|
|
if (dobonuscheck) {
|
|
valbonus -= 10;
|
|
if (valbonus < 0) valbonus = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
myval = getattr(lf, att);
|
|
|
|
if (dopenaltycheck) {
|
|
neededdiff = myval - valneeded;
|
|
limit(&neededdiff,-20, 20);
|
|
}
|
|
|
|
if (dobonuscheck) {
|
|
bonusdiff = myval - valbonus;
|
|
// for this one, just meeting it gives you once "scaleamt" worth of bonus
|
|
if (bonusdiff >= 0) bonusdiff += 10;
|
|
limit(&bonusdiff,-20, 30);
|
|
}
|
|
|
|
if (dopenaltycheck && (neededdiff < 0)) {
|
|
// penalty?
|
|
// for firearms, armour or scaleamt == 0, you MUST meet the requirement.
|
|
if (scaleamt == 0) {
|
|
neededdiff = -20;
|
|
} else if (o && isarmour(o)) {
|
|
neededdiff = -20;
|
|
} else if (o && isfirearm(o)) {
|
|
neededdiff = -20;
|
|
}
|
|
bonorpen = PENALTY;
|
|
} else if (dobonuscheck && (bonusdiff > 0)) {
|
|
// maybe a bonus?
|
|
// no bonusses if you're unskilled
|
|
if (o && !getweaponskill(lf, o)) {
|
|
bonusdiff = 0;
|
|
} else {
|
|
bonorpen = BONUS;
|
|
}
|
|
}
|
|
|
|
if (scaleamt && pctmod) {
|
|
// for each 10 points you are over/under the requirement, adjust "scaleamt" percent.
|
|
if (bonorpen == PENALTY) {
|
|
*pctmod = (neededdiff/10) * scaleamt;
|
|
} else if (bonorpen == BONUS) {
|
|
*pctmod = (bonusdiff/10) * scaleamt;
|
|
}
|
|
}
|
|
|
|
|
|
// too low?
|
|
if ((bonorpen == PENALTY) && (neededdiff <= -20)) {
|
|
switch (att) {
|
|
case A_AGI:
|
|
reason = E_LOWDEX;
|
|
break;
|
|
case A_CHA:
|
|
reason = E_LOWCHA;
|
|
break;
|
|
case A_CON:
|
|
reason = E_LOWCON;
|
|
break;
|
|
case A_IQ:
|
|
reason = E_LOWIQ;
|
|
break;
|
|
case A_STR:
|
|
reason = E_LOWSTR;
|
|
break;
|
|
case A_WIS:
|
|
reason = E_LOWWIS;
|
|
break;
|
|
case A_NONE:
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
reason = E_OK;
|
|
return B_TRUE;
|
|
}
|
|
|
|
// will the lf flee after taking damage?
|
|
int mightflee(lifeform_t *lf) {
|
|
flag_t *f;
|
|
enum ATTRBRACKET iqb;
|
|
|
|
if (hasflag(lf->flags, F_NOFLEE)) {
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (hasflag(lf->flags, F_FLEEONDAM)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = hasflag(lf->flags, F_FLEEONHPPCT);
|
|
if (f) {
|
|
if (gethppct(lf) <= f->val[0]) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL);
|
|
if (iqb >= AT_AVERAGE) {
|
|
if (!skillcheck(lf, SC_MORALE, (100 - gethppct(lf))/2, 0)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
int modattr(lifeform_t *lf, enum ATTRIB attr, int amt) {
|
|
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
// already at max/min?
|
|
if ((amt > 0) && (lf->att[attr] >= MAX_ATTRIBVAL)) {
|
|
return B_TRUE;
|
|
}
|
|
if ((amt < 0) && (lf->att[attr] <= 0)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
lf->att[attr] += amt;
|
|
|
|
// increase base if required
|
|
if (lf->att[attr] > lf->baseatt[attr]) {
|
|
lf->baseatt[attr] = lf->att[attr];
|
|
}
|
|
|
|
// enforce limits
|
|
limit(&lf->att[attr], 0, MAX_ATTRIBVAL);
|
|
|
|
if (lf->born && (gamemode == GM_GAMESTARTED) && (amt != 0) && (isplayer(lf) || cansee(player, lf))) {
|
|
char lfname[BUFLEN], verb[BUFLEN], adverb[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
if (isplayer(lf)) strcpy(verb, "feel");
|
|
else strcpy(verb, "looks");
|
|
if (amt > 0) {
|
|
switch (attr) {
|
|
case A_STR:
|
|
strcpy(adverb, "stronger");
|
|
break;
|
|
case A_CHA:
|
|
strcpy(adverb, "more attractive");
|
|
break;
|
|
case A_CON:
|
|
strcpy(adverb, "healthier");
|
|
break;
|
|
case A_AGI:
|
|
strcpy(adverb, "more agile");
|
|
break;
|
|
case A_IQ:
|
|
strcpy(adverb, "smarter");
|
|
break;
|
|
case A_WIS:
|
|
strcpy(adverb, "wiser");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else { // ie. amt < 0
|
|
switch (attr) {
|
|
case A_STR:
|
|
strcpy(adverb, "weaker");
|
|
break;
|
|
case A_CHA:
|
|
strcpy(adverb, "less attractive");
|
|
break;
|
|
case A_CON:
|
|
strcpy(adverb, "frail");
|
|
break;
|
|
case A_AGI:
|
|
strcpy(adverb, "sluggish");
|
|
break;
|
|
case A_IQ:
|
|
strcpy(adverb, "stupid");
|
|
break;
|
|
case A_WIS:
|
|
strcpy(adverb, "foolish");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
msg("^%c%s %s %s!", getlfcol(lf, (amt > 0) ? CC_GOOD : CC_BAD), lfname, verb, adverb);
|
|
more();
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void modhunger(lifeform_t *lf, int amt) {
|
|
flag_t *f;
|
|
int prehlev, posthlev;
|
|
f = hasflag(lf->flags, F_HUNGER);
|
|
if (!f) {
|
|
return;
|
|
}
|
|
|
|
if (isundead(lf) && (lf->race->id != R_VAMPIRE)) {
|
|
return;
|
|
}
|
|
|
|
// modify for effects
|
|
if (amt > 0) {
|
|
int multiplier = 0;
|
|
int tempmult;
|
|
sumflags(lf->flags, F_FASTMETAB, &tempmult, NULL, NULL);
|
|
multiplier += tempmult;
|
|
sumflags(lf->flags, F_SLOWMETAB, &tempmult, NULL, NULL);
|
|
multiplier -= tempmult;
|
|
|
|
if (lfhasflagval(lf, F_ASLEEP, ST_MEDITATING, NA, NA, NULL)) {
|
|
multiplier -= 2;
|
|
}
|
|
|
|
if (multiplier > 0) {
|
|
amt *= multiplier;
|
|
} else if (multiplier < 0) {
|
|
amt /= abs(multiplier);
|
|
}
|
|
}
|
|
|
|
prehlev = gethungerlevel(f->val[0]);
|
|
f->val[0] += amt;
|
|
posthlev = gethungerlevel(f->val[0]);
|
|
|
|
if (posthlev == H_STARVED) {
|
|
if (isplayer(lf)) {
|
|
msg("^BYou collapse from starvation.");
|
|
}
|
|
lf->lastdamtype = DT_DIRECT;
|
|
setlastdam(lf, "starvation");
|
|
lf->hp = 0;
|
|
} else if (prehlev != posthlev) {
|
|
if (posthlev != H_NONE) {
|
|
char buf[BUFLEN];
|
|
int needfeeling = B_FALSE;
|
|
int needexclam = B_FALSE;
|
|
switch (posthlev) {
|
|
case H_PECKISH:
|
|
case H_HUNGRY:
|
|
case H_VHUNGRY:
|
|
needfeeling = B_TRUE;
|
|
break;
|
|
default:
|
|
needfeeling = B_FALSE;
|
|
break;
|
|
}
|
|
switch (posthlev) {
|
|
case H_STUFFED:
|
|
case H_STARVING:
|
|
needexclam = B_TRUE;
|
|
break;
|
|
default:
|
|
needexclam = B_FALSE;
|
|
break;
|
|
}
|
|
|
|
|
|
if (isplayer(lf)) {
|
|
gethungername(lf, posthlev, buf);
|
|
msg("^wYou are %s%s%s%c",
|
|
((amt < 0) && (posthlev > H_NONE)) ? "still " : "",
|
|
needfeeling ? "feeling " : "",
|
|
buf,
|
|
(needexclam) ? '!' : '.');
|
|
|
|
statdirty = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
gethungername(lf, posthlev, buf);
|
|
msg("^%c%s looks %s%c", getlfcol(lf, CC_BAD), lfname, buf, (needexclam) ? '!' : '.');
|
|
}
|
|
|
|
if ((posthlev >= H_VHUNGRY) && (amt > 0)) {
|
|
stopresting(lf);
|
|
}
|
|
if (posthlev > H_STARVING) {
|
|
useringofmiracles(lf, 2);
|
|
// reset hunger
|
|
f->val[0] = 0;
|
|
}
|
|
|
|
// ai doesn't get hungry anymore after they have
|
|
// satisfied their hunger.
|
|
if (!isplayer(lf) && (posthlev <= H_NONE)) {
|
|
killflag(f);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
float modifybystat(float num, lifeform_t *lf, enum ATTRIB att) {
|
|
int newnum;
|
|
newnum = num + ( num * (getstatmod(lf, att) / 100.0) );
|
|
return newnum;
|
|
}
|
|
|
|
void modmorale(lifeform_t *lf, int howmuch) {
|
|
flag_t *mf;
|
|
mf = hasflag(lf->flags, F_MORALE);
|
|
if (mf) {
|
|
mf->val[0] += howmuch;
|
|
if (mf->val[0] <= 0) {
|
|
killflag(mf);
|
|
}
|
|
} else {
|
|
if (howmuch > 0) {
|
|
addflag(lf->flags, F_MORALE, howmuch, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void modstamina(lifeform_t *lf, float howmuch) {
|
|
float orig;
|
|
|
|
if (howmuch == 0) return;
|
|
|
|
// you don't lose stamina while enraged or caffeinated
|
|
if (lfhasflag(lf, F_RAGE) && (howmuch < 0)) return;
|
|
if (lfhasflag(lf, F_CAFFEINATED) && (howmuch < 0)) return;
|
|
|
|
if (isplayer(lf)) {
|
|
if (howmuch < 0) {
|
|
if (lf->bartimer == 2) {
|
|
lf->stamlastturn += fabs(howmuch);
|
|
} else {
|
|
lf->stamlastturn = fabs(howmuch);
|
|
lf->bartimer = 2;
|
|
}
|
|
} else if (howmuch > 0) {
|
|
lf->stamlastturn = 0;
|
|
}
|
|
}
|
|
|
|
orig = getstamina(lf);
|
|
lf->stamina += howmuch;
|
|
limitf(&(lf->stamina), 0, getmaxstamina(lf));
|
|
if (getstamina(lf) != orig) {
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
updatestatus();
|
|
if (getstamina(lf) == 0) {
|
|
msg("^BYou are exhausted.");
|
|
} else if (orig == 0) {
|
|
msg("You feel less exhausted now.");
|
|
}
|
|
}
|
|
/*else if (cansee(player, lf)) {
|
|
if (getstamina(lf) == 0) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s looks exhausted.", lfname);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
if (getstamina(lf) == 0) {
|
|
stopsprinting(lf);
|
|
}
|
|
}
|
|
|
|
|
|
int movecausesnoise(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_SILENTMOVE)) {
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
int needstobreath(lifeform_t *lf) {
|
|
if (lfhasflag(lf, F_NOBREATH)) {
|
|
return B_FALSE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// if validchars is set, we will populate it with a list of valid
|
|
// choice letters for asking the player how to rest.
|
|
int needstorest(lifeform_t *lf, char *validchars) {
|
|
int need = B_FALSE;
|
|
if (validchars) strcpy(validchars, "");
|
|
|
|
if (lf->hp < lf->maxhp) {
|
|
if (validchars) strcat(validchars, "h");
|
|
need = B_TRUE;
|
|
} else if (hastempinjuries(lf)) {
|
|
if (validchars) strcat(validchars, "h");
|
|
need = B_TRUE;
|
|
}
|
|
if ((getmaxmp(lf) > 0) && (lf->mp < getmaxmp(lf))) {
|
|
if (validchars) strcat(validchars, "m");
|
|
need = B_TRUE;
|
|
}
|
|
if (getstamina(lf) < getmaxstamina(lf)) {
|
|
if (validchars) strcat(validchars, "s");
|
|
need = B_TRUE;
|
|
}
|
|
return need;
|
|
}
|
|
|
|
void noarmouron(race_t *r, enum BODYPART bp) {
|
|
int i;
|
|
for (i = 0; i < r->nbodyparts; i++) {
|
|
if (r->bodypart[i].id == bp) {
|
|
r->bodypart[i].armourok = B_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns TRUE if the player heard it.
|
|
int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, char *text, char *seetext) {
|
|
lifeform_t *l;
|
|
int sounddist;
|
|
int alwayshear = B_FALSE;
|
|
int rv = B_FALSE;
|
|
|
|
assert(text);
|
|
|
|
if (gamemode != GM_GAMESTARTED) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (nclass == NC_SPELLEFFECT) {
|
|
alwayshear = B_TRUE;
|
|
}
|
|
|
|
// sound will travel 3*volume cells
|
|
sounddist = getsounddist(volume);
|
|
|
|
// if anything nearby hears it, it might respond
|
|
for (l = c->map->lf; l ; l = l->next) {
|
|
int 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 if ((nclass == NC_FIGHTING) && isplayer(l) && (dist <= 1)) {
|
|
// players never hear fighting unless it's a small distance away
|
|
// this prevents "you hear fighting" when you are attacked from behind.
|
|
difficulty = 9999;
|
|
} else {
|
|
//difficulty = (int) ( ((float)getcelldist(l->cell, c) / (float)gethearingrange(l)) * 20);
|
|
difficulty = (int) ( ((float) dist / ((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 -= 6;
|
|
limit(&lbonus, 0, NA);
|
|
}
|
|
|
|
// skillcheck to hear this
|
|
if ( (isplayer(l) && haslos(l, c)) || // only player can "hear by seeing"
|
|
(canhear(l, c, volume) && (alwayshear || skillcheck(l, SC_LISTEN, difficulty, lbonus)) ) ) {
|
|
flag_t *f;
|
|
// announce?
|
|
if (isplayer(l) && !lfhasflag(l, F_ASLEEP)) {
|
|
// never say "you hear xxx" if you can see the lf that caused the noise.
|
|
// or the cell which made the noise
|
|
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");
|
|
}
|
|
if (isdead(noisemaker)) {
|
|
msg("The dying %s %s.", noprefix(lfname), realseetext);
|
|
} else {
|
|
msg("%s %s%s", lfname, realseetext,
|
|
(realseetext[strlen(realseetext)-1] == '!') ? "" : ".");
|
|
}
|
|
rv = B_TRUE;
|
|
}
|
|
} else if (!noisemaker && haslos(player, c)) {
|
|
// you can see the cell which made the noise
|
|
if (seetext) {
|
|
msg("%s", seetext);
|
|
rv = B_TRUE;
|
|
}
|
|
} else if (noisemaker && ispetof(noisemaker, player)) {
|
|
rv = B_FALSE;
|
|
} else if (text && !isdeaf(l) && ((nclass != NC_MOVEMENT) || !lfhasflag(l, F_DONELISTEN))) {
|
|
// this means you can only hear one 'walk' sound per turn
|
|
char textnopunc[BUFLEN];
|
|
char punc;
|
|
int dist;
|
|
|
|
//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 detectdist = 0;
|
|
|
|
real_getlfnamea(noisemaker, lfname, NULL, B_NOSHOWALL, B_CURRACE);
|
|
getdisttext(l->cell, c, distbuf, distbufbad, dirbuf);
|
|
|
|
slev = getskill(l, SK_LISTEN);
|
|
detectdist = getlistendetectrange(l);
|
|
//
|
|
// high listen skill lets you know more info.
|
|
//
|
|
// beginner = distance
|
|
// adept = distance and direction
|
|
// expert = monstername and distance and direction
|
|
// master = temporary scan of where they are!
|
|
if (dist <= detectdist) {
|
|
if (slev >= PR_EXPERT) {
|
|
// fully id
|
|
makeheard(l, noisemaker, B_TRUE, NULL, 2);
|
|
} else {
|
|
// show that you heard a sound
|
|
makeheard(l, noisemaker, B_FALSE, textnopunc, 2);
|
|
}
|
|
}
|
|
// now announce it.
|
|
if (slev >= PR_EXPERT) {
|
|
msg("You hear %s%s to the %s%c", lfname, distbuf, dirbuf, punc);
|
|
rv = B_TRUE;
|
|
} else if (slev >= PR_BEGINNER) {
|
|
msg("You hear %s%s to the %s%c", textnopunc, distbuf, dirbuf, punc);
|
|
rv = B_TRUE;
|
|
} else if (slev >= PR_NOVICE) {
|
|
msg("You hear %s%s%c", textnopunc, distbufbad, punc);
|
|
rv = B_TRUE;
|
|
} else {
|
|
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 (nclass == NC_MOVEMENT) {
|
|
addflag(l->flags, F_DONELISTEN, B_TRUE, NA, NA, NULL);
|
|
practice(l, SK_LISTEN, 1);
|
|
}
|
|
// migraine?
|
|
if ((volume > 1) && lfhasflagval(l, F_POISONED, P_MIGRAINE, NA, NA, NULL)) {
|
|
losehp(l, (volume-1), DT_SONIC, NULL, "a migraine");
|
|
if (isplayer(l)) {
|
|
msg("Your head explodes in pain at the sound!");
|
|
}
|
|
}
|
|
}
|
|
} // 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];
|
|
char *punc;
|
|
assert(text);
|
|
strcpy(wakenoise, text);
|
|
// omit punctuation
|
|
punc = &(wakenoise[strlen(wakenoise)-1]);
|
|
switch (*punc) {
|
|
case '"': break;
|
|
default:
|
|
*punc = '\0';
|
|
break;
|
|
}
|
|
//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_ASLEEP) && cansee(player, l)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(l, lfname);
|
|
msg("%s stir%s in %s slumber...", lfname,
|
|
isplayer(l) ? "" : "s",
|
|
isplayer(l) ? "your" : "its");
|
|
}
|
|
} 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)) {
|
|
int willrespond = B_FALSE;
|
|
|
|
if (lfhasflag(l, F_AUTOROTATE)) {
|
|
} else if (aihastarget(l)) {
|
|
// 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(l, player) && !lfhasflag(l, F_AWARENESS) && !isdead(l)) {
|
|
// peaceful things only turn sometimes
|
|
//if (!ispeaceful(l) || onein(6)) {
|
|
if (!isfriendly(l)) {
|
|
char lfname[BUFLEN];
|
|
int prefacing;
|
|
prefacing = l->facing;
|
|
turntoface(l, c);
|
|
if (!lfhasflag(l, F_FEIGNINGDEATH) && cansee(player, l) &&
|
|
(prefacing != l->facing)) {
|
|
getlfname(l, lfname);
|
|
msg("%s turns to face you.", lfname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else { // can't hear the sound.
|
|
}
|
|
} // end for each lf on map
|
|
|
|
if (rv == B_TRUE) {
|
|
if (getoption(OPT_STOPRUNONNOISE)) {
|
|
stoprunning(player);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
enum NOISECLASS noisetypetoclass(enum NOISETYPE nt) {
|
|
switch (nt) {
|
|
case N_WALK:
|
|
case N_FLY:
|
|
return NC_MOVEMENT;
|
|
case N_SONICBOLT:
|
|
case N_DEATHKEEN:
|
|
case N_WARCRY:
|
|
return NC_SPELLEFFECT;
|
|
default:
|
|
break;
|
|
}
|
|
return NC_OTHER;
|
|
}
|
|
|
|
// give initial equiment / skills to a lifeform
|
|
void outfitlf(lifeform_t *lf) {
|
|
//int db = B_FALSE;
|
|
givestartskills(lf, lf->flags);
|
|
givestartobs(lf, NULL, lf->flags);
|
|
autoskill(lf);
|
|
|
|
// weild/wear stuff
|
|
autoweild(lf);
|
|
}
|
|
|
|
|
|
// make 'lf' into a pet/ally of 'owner'
|
|
void petify(lifeform_t *lf, lifeform_t *owner) {
|
|
if (isplayer(owner)) {
|
|
makefriendly(lf, PERMENANT);
|
|
killflagsofid(lf->flags, F_NOINFO);
|
|
killflagsofid(lf->flags, F_INFOPRICE);
|
|
}
|
|
addflag(lf->flags, F_PETOF, owner->id, owner->cell->x, owner->cell->y, NULL);
|
|
killflagsofid(lf->flags, F_STAYINROOM);
|
|
}
|
|
|
|
|
|
int pickup(lifeform_t *lf, object_t *what, int howmany, int fromground, int wantannounce) {
|
|
char obname[BUFLEN];
|
|
object_t *o;
|
|
//flag_t *f;
|
|
int failed = B_FALSE;
|
|
|
|
if (!what) {
|
|
return B_TRUE;
|
|
}
|
|
if (howmany == 0) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
// in case we get burduned
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
|
|
getobname(what, obname, howmany);
|
|
if (howmany == ALL) howmany = what->amt;
|
|
|
|
if (fromground) {
|
|
if (lfhasflag(lf, F_LEVITATING)) {
|
|
if (isplayer(lf)) {
|
|
msg("You can't reach %s from up here!", obname);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!canpickup(lf, what,howmany)) {
|
|
// tell the player why!
|
|
if (isplayer(lf)) {
|
|
switch (reason) {
|
|
case E_INSUBSTANTIAL:
|
|
msg("Your hand passes straight through %s.",obname);
|
|
break;
|
|
case E_NOSPACE:
|
|
msg("Your pack is too full to fit any more objects.");
|
|
break;
|
|
case E_NOPACK:
|
|
msg("You lack the ability to carry things!");
|
|
break;
|
|
case E_STUCK:
|
|
msg("You can't pick up %s while caught in it!",obname);
|
|
break;
|
|
case E_NOPICKUP:
|
|
msg("You can't pick up %s!",obname);
|
|
break;
|
|
case E_TOOBIG:
|
|
msg("%s %s too large for you to lift.",obname, OB1(what,"is","are"));
|
|
break;
|
|
case E_TOOHEAVY:
|
|
msg("%s %s too heavy to lift!",obname, OB1(what,"is","are"));
|
|
break;
|
|
case E_GRAVBOOSTED:
|
|
msg("The %s feels too heavy to lift!",noprefix(obname));
|
|
break;
|
|
default:
|
|
msg("For some reason, you cannot pick up %s!",obname);
|
|
break;
|
|
}
|
|
}
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (!failed) {
|
|
// warn if it is too heavy
|
|
if (isplayer(lf) && !isburdened(lf) && willburden(lf, what, howmany)) {
|
|
char ch,buf[BUFLEN];
|
|
snprintf(buf, BUFLEN, "Picking up %s will burden you. Continue", obname);
|
|
ch = askchar(buf, "yn","n", B_TRUE, B_FALSE);
|
|
if (ch != 'y') {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (touch(lf, what)) {
|
|
taketime(lf, SPEED_PICKUP);
|
|
return B_TRUE;
|
|
}
|
|
if (hasflag(what->flags, F_DEAD)) {
|
|
taketime(lf, SPEED_PICKUP);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!failed) {
|
|
// try to move whatever was selected
|
|
o = moveob(what, lf->pack, howmany);
|
|
if (o) { // if pickup was successful...
|
|
if (wantannounce) {
|
|
if (isplayer(lf)) {
|
|
// refresh obname to copd with stacking
|
|
getobname(o, obname, o->amt);
|
|
msgnocap("%c - %s",o->letter, obname);
|
|
} else if (cansee(player, lf)) {
|
|
char buf[BUFLEN];
|
|
getlfname(lf, buf);
|
|
msg("%s picks up %s.",buf, obname);
|
|
}
|
|
}
|
|
|
|
// you get one free pickup/drop per turn
|
|
if (lfhasflag(lf, F_DONEPICKUP)) {
|
|
taketime(lf, SPEED_PICKUP);
|
|
} else {
|
|
addflag(lf->flags, F_DONEPICKUP, B_TRUE, NA, NA, NULL);
|
|
}
|
|
} else {
|
|
// tell the player why!
|
|
if (isplayer(lf)) {
|
|
switch (reason) {
|
|
case E_NOSPACE:
|
|
msg("Your pack is too full to fit any more objects.");
|
|
break;
|
|
default:
|
|
msg("For some reason, you cannot pick up %s!",obname);
|
|
break;
|
|
}
|
|
}
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
// if object isn't where player is, move it to player's pile
|
|
// for example, if obejct was being picked up via telekenesis
|
|
if (what->pile->where != lf->cell) {
|
|
moveob(what, lf->cell->obpile, howmany);
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
// picked up something our god doesn't like?
|
|
if (isplayer(lf)) {
|
|
if ( hasflagknown(o->flags, F_POISONED) ||
|
|
hasflagvalknown(o->flags, F_HITCONFER, F_POISONED, NA, NA, NULL)) {
|
|
int i,nretgods;
|
|
lifeform_t *retgod[MAXGODS];
|
|
|
|
getprayedgods(retgod, &nretgods);
|
|
for (i = 0 ; i < nretgods; i++) {
|
|
if (lfhasflagval(retgod[i], F_GODPOISON, B_FALSE, NA, NA, NULL)) {
|
|
char warnbuf[BUFLEN];
|
|
sprintf(warnbuf, "I hope you're not planning on using %s...", (o->amt == 1) ? "that" : "those");
|
|
godsay(retgod[i]->race->id, B_TRUE, warnbuf);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
void poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char *fromwhat, enum RACE srcraceid) {
|
|
flag_t *f;
|
|
int found = B_FALSE,i;
|
|
enum POISONSEVERITY psev;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
race_t *srcrace;
|
|
poisontype_t *pt;
|
|
|
|
pt = findpoisontype(ptype);
|
|
srcrace = findrace(srcraceid);
|
|
|
|
// are you immune to disease/poison?
|
|
psev = pt->severity;
|
|
switch (psev) {
|
|
case PS_POISON:
|
|
if (isimmuneto(lf->flags, DT_POISON, B_FALSE)) return;
|
|
break;
|
|
case PS_DISEASE:
|
|
if (isimmuneto(lf->flags, DT_POISON, B_FALSE)) return;
|
|
if (hasflag(lf->flags, F_DISEASEIMMUNE)) return;
|
|
break;
|
|
case PS_CURSE:
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
if ((psev != PS_CURSE) && (howlong != PERMENANT)) {
|
|
// adjust time based on first aid skill
|
|
howlong -= getskill(lf, SK_FIRSTAID);
|
|
if (howlong <= 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
|
|
if ( (f->id == F_POISONED) && (f->val[0] == ptype) &&
|
|
(f->lifetime > 0) ) {
|
|
// extend duration
|
|
f->lifetime += howlong;
|
|
// if applicable, remember what race you got it from
|
|
if (srcrace) {
|
|
f->val[2] = srcrace->id;
|
|
}
|
|
// announce - announceflaggain won't be called
|
|
// since this isn't a new flag.
|
|
if (isplayer(lf)) {
|
|
msg("^%cYou feel more sick.", getlfcol(lf, CC_VBAD));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%c%s looks even more sick.", getlfcol(lf, CC_VBAD), lfname);
|
|
}
|
|
|
|
found = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
// incubation period?
|
|
if (pt->incubationtime && isplayer(lf)) {
|
|
flag_t *ii;
|
|
int multiplier = 0;
|
|
int tempmult;
|
|
// modify incubation time based on metabolism
|
|
sumflags(lf->flags, F_FASTMETAB, &tempmult, NULL, NULL);
|
|
multiplier += tempmult;
|
|
sumflags(lf->flags, F_SLOWMETAB, &tempmult, NULL, NULL);
|
|
multiplier -= tempmult;
|
|
|
|
if (multiplier > 0) {
|
|
howlong /= multiplier;
|
|
} else if (multiplier < 0) {
|
|
howlong *= abs(multiplier);
|
|
}
|
|
ii = lfhasflagval(lf, F_INCUBATING, ptype, NA, NA, NULL);
|
|
if (ii) {
|
|
// will happen faster
|
|
ii->val[2] /= 2;
|
|
if (ii->val[2] < 1) ii->val[2] = 1;
|
|
if (getskill(lf, SK_FIRSTAID) >= PR_BEGINNER) {
|
|
ii->known = B_TRUE;
|
|
msg("^BYou recognise the increased onset of %s.", pt->name);
|
|
}
|
|
} else {
|
|
char ftext[BUFLEN];
|
|
sprintf(ftext, "%d^%s", power, fromwhat);
|
|
ii = addflag(lf->flags, F_INCUBATING, ptype, pt->incubationtime, howlong, ftext);
|
|
ii->obfrom = srcrace ? srcrace->id : NA;
|
|
if (getskill(lf, SK_FIRSTAID) >= PR_BEGINNER) {
|
|
ii->known = B_TRUE;
|
|
msg("^BYou recognise the onset of %s.", pt->name);
|
|
} else {
|
|
ii->known = B_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
addtempflag(lf->flags, F_POISONED, ptype, power, srcrace ? srcrace->id : NA, fromwhat, howlong);
|
|
poisoneffects(lf, ptype, power);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
int poisoneffects(lifeform_t *lf, enum POISONTYPE ptid, int power) {
|
|
flag_t *f;
|
|
switch (ptid) {
|
|
case P_FOOD:
|
|
case P_GAS:
|
|
case P_VENOM:
|
|
default: return B_TRUE;
|
|
case P_MIGRAINE:
|
|
f = addtempflag(lf->flags, F_DTVULN, DT_SONIC, NA, NA, NULL, FROMPOISON); f->obfrom = ptid;
|
|
f = addtempflag(lf->flags, F_DTVULN, DT_LIGHT, NA, NA, NULL, FROMPOISON); f->obfrom = ptid;
|
|
break;
|
|
case P_WEAKNESS:
|
|
f = addtempflag(lf->flags, F_ATTRMOD, A_STR, -(power*10), NA, NULL, FROMPOISON);
|
|
f->obfrom = ptid; // poison type
|
|
break;
|
|
case P_ROT:
|
|
f = addtempflag(lf->flags, F_ATTRMOD, A_CHA, -(power*10), NA, NULL, FROMPOISON);
|
|
f->obfrom = ptid;
|
|
f = addtempflag(lf->flags, F_ATTRMOD, A_STR, -(power*5), NA, NULL, FROMPOISON);
|
|
f->obfrom = ptid;
|
|
f = addtempflag(lf->flags, F_ATTRMOD, A_CON, -(power*10), NA, NULL, FROMPOISON);
|
|
f->obfrom = ptid;
|
|
f = addtempflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL, FROMPOISON);
|
|
f->obfrom = ptid;
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int poisonthreatenslife(lifeform_t *lf, flag_t *f) {
|
|
float time,dam,totaldam;
|
|
poisontype_t *pt;
|
|
|
|
if (!f) return B_FALSE;
|
|
|
|
pt = findpoisontype(f->val[0]);
|
|
|
|
time = f->lifetime;
|
|
dam = pt->dam * f->val[1];
|
|
|
|
|
|
totaldam = time * dam;
|
|
totaldam = pctof(pt->dampct, totaldam);
|
|
|
|
if (totaldam >= lf->hp) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int polymorphto(lifeform_t *lf, enum RACE rid, int howlong) {
|
|
// alreay that race? just update polymorph timer.
|
|
if (lf->race->id == rid) {
|
|
flag_t *f;
|
|
f = hasflag(lf->flags, F_POLYMORPHED);
|
|
if (f) {
|
|
f->lifetime = howlong;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!hasflag(lf->flags, F_ORIGRACE)) {
|
|
// remember the original race
|
|
addflag(lf->flags, F_ORIGRACE, lf->race->id, NA, NA, NULL);
|
|
}
|
|
killflagsofid(lf->flags, F_POLYMORPHED);
|
|
addtempflag(lf->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, howlong);
|
|
setrace(lf, rid, B_TRUE);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// maybe practice a skill
|
|
void practice(lifeform_t *lf, enum SKILL skid, int amt) {
|
|
flag_t *f;
|
|
skill_t *sk;
|
|
enum SKILLLEVEL slev;
|
|
int timeneeded;
|
|
|
|
sk = findskill(skid);
|
|
if (!sk) return;
|
|
|
|
// do this even if the skill is not 'trainable'
|
|
setskillused(lf, skid);
|
|
|
|
|
|
slev = getskill(lf, skid);
|
|
|
|
timeneeded = sk->traintime * (getskill(lf, skid)+1);
|
|
if (!timeneeded) return;
|
|
|
|
// can only practice skills which you're capable of learning
|
|
if (!canlearn(lf, skid)) return;
|
|
|
|
if (!slev || onein(slev)) {
|
|
// practice a little bit...
|
|
f = lfhasflagval(lf, F_PRACTICINGSKILL, skid, NA, NA, NULL);
|
|
if (f) {
|
|
f->val[1] += amt;
|
|
} else {
|
|
f = addflag(lf->flags, F_PRACTICINGSKILL, skid, amt, NA, NULL);
|
|
}
|
|
|
|
if (f->val[1] >= timeneeded) {
|
|
// learnt the next rank
|
|
giveskill(lf, skid);
|
|
killflag(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
void precalclos(lifeform_t *lf) {
|
|
cell_t *c;
|
|
int startxray,rangemod;
|
|
int maxvisrange,nightvisrange;
|
|
cell_t **los;
|
|
cell_t **losdark;
|
|
int *blocker;
|
|
int nlos = 0,nlosdark = 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 = PR_INEPT;
|
|
flag_t *missingeye;
|
|
//long visdiameter;
|
|
long allocamt;
|
|
|
|
|
|
if (gamemode == GM_CLEANUP) return;
|
|
|
|
if (lf->cell->type->id == CT_FAKE) return;
|
|
|
|
if (lf->los) {
|
|
free(lf->los); lf->los = NULL;
|
|
}
|
|
if (lf->losdark) {
|
|
free(lf->losdark); lf->losdark = NULL;
|
|
}
|
|
|
|
if (!lf->born) {
|
|
lf->nlos = 0;
|
|
lf->nlosdark = 0;
|
|
return;
|
|
}
|
|
|
|
// right eye missing?
|
|
missingeye = lfhasflagval(lf, F_INJURY, IJ_EYEDESTROYED, NA, NA, NULL);
|
|
|
|
f = hasflag(lf->flags, F_XRAYVIS);
|
|
if (f) {
|
|
startxray = f->val[0];
|
|
} else {
|
|
startxray = 0;
|
|
}
|
|
|
|
// ie. you can see both ways, plus your own cell
|
|
//visdiameter = (MAXVISRANGE+1)*2;
|
|
//allocamt = visdiameter * visdiameter;
|
|
allocamt = MAX_MAPW * MAX_MAPH;
|
|
|
|
los = malloc( sizeof(cell_t *) * allocamt);
|
|
losdark = malloc( sizeof(cell_t *) * allocamt);
|
|
blocker = malloc( sizeof(cell_t *) * allocamt);
|
|
nlos = 0;
|
|
nlosdark = 0;
|
|
|
|
maxvisrange = getvisrange(lf, B_FALSE);
|
|
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);
|
|
}
|
|
|
|
if (plev >= PR_BEGINNER) {
|
|
enum TURNDIR turndir;
|
|
cell_t *adjcell;
|
|
// include cells immediately to the lifeform's left and right.
|
|
for (turndir = missingeye ? TD_LEFT : TD_RIGHT; turndir <= TD_LEFT; turndir++) {
|
|
int dir;
|
|
dir = rotatedir(lf->facing, turndir, 2);
|
|
adjcell = getcellindir(lf->cell, dir);
|
|
if (adjcell) {
|
|
endx[nendcells] = adjcell->x;
|
|
endy[nendcells] = adjcell->y;
|
|
nendcells++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end if facing == all
|
|
assert(nendcells < MAXVISLIMIT);
|
|
|
|
// look in the lf's field of vision arc
|
|
|
|
//for (ang = 0; ang < 360; ang += 30) {
|
|
for (nn = 0; nn < nendcells; nn++) {
|
|
int keepgoing = B_TRUE;
|
|
int currange,xray;
|
|
int n;
|
|
// start at lf's cell
|
|
//c = lf->cell;
|
|
//x = c->x;
|
|
//y = c->y;
|
|
xray = startxray;
|
|
currange = 0;
|
|
// calc path to end cell
|
|
calcbresnham(lf->cell->map, lf->cell->x, lf->cell->y, endx[nn], endy[nn], retcell, &numpixels );
|
|
assert(numpixels < MAXRETCELLS);
|
|
// keep going until we lose los
|
|
for (n = 0; keepgoing && (n < numpixels); n++) {
|
|
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++;
|
|
assert (nlos < allocamt);
|
|
} else {
|
|
int idx,found=B_FALSE;
|
|
for (idx=0;idx<nlosdark;idx++) {
|
|
if (losdark[idx] == c) {
|
|
found = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
// if cell WASNT lit, add it to an array of dark "visible" cells
|
|
// (lf->darklos)
|
|
// that way we can check for these to determine whether we need to recalc our los
|
|
// when light changes.
|
|
losdark[nlosdark] = c;
|
|
nlosdark++;
|
|
assert (nlosdark < allocamt);
|
|
}
|
|
}
|
|
}
|
|
} else { // ie. if !c
|
|
keepgoing = B_FALSE;
|
|
}
|
|
} // end foreach cell and while keepgoing
|
|
}
|
|
assert(nlos < allocamt);
|
|
assert(nlosdark < allocamt);
|
|
|
|
// now fill in lifeform structure
|
|
if (nlos) {
|
|
lf->los = malloc(sizeof(cell_t *) * nlos);
|
|
for (i = 0; i < nlos; i++) {
|
|
lf->los[i] = los[i];
|
|
}
|
|
} else {
|
|
lf->los = NULL;
|
|
}
|
|
lf->nlos = nlos;
|
|
|
|
if (nlosdark) {
|
|
lf->losdark = malloc(sizeof(cell_t *) * nlosdark);
|
|
for (i = 0; i < nlosdark; i++) {
|
|
lf->losdark[i] = losdark[i];
|
|
}
|
|
} else {
|
|
lf->losdark = NULL;
|
|
}
|
|
lf->nlosdark = nlosdark;
|
|
|
|
free(los);
|
|
free(losdark);
|
|
free(blocker);
|
|
|
|
if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
}
|
|
|
|
void preparecorpse(lifeform_t *lf, object_t *corpse) {
|
|
char obname[BUFLEN];
|
|
if (corpse->amt > 1) {
|
|
corpse = splitob(corpse);
|
|
}
|
|
|
|
if (isplayer(lf) && (corpse->pile->owner == lf)) {
|
|
// add flag _before_ getting the name
|
|
addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL);
|
|
killflagsofid(corpse->flags, F_FROZEN);
|
|
getobname(corpse, obname, corpse->amt);
|
|
msgnocap("%c - %s.", corpse->letter, obname);
|
|
} else if (isplayer(lf)) {
|
|
// add flag after getting the name
|
|
getobname(corpse, obname, corpse->amt);
|
|
msg("You cook %s.", obname);
|
|
addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL);
|
|
killflagsofid(corpse->flags, F_FROZEN);
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
// add flag after getting the name
|
|
getlfname(lf, lfname);
|
|
getobname(corpse, obname, corpse->amt);
|
|
msg("%s cooks %s.", obname);
|
|
addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL);
|
|
killflagsofid(corpse->flags, F_FROZEN);
|
|
} else {
|
|
addflag(corpse->flags, F_PREPARED, B_TRUE, NA, NA, NULL);
|
|
killflagsofid(corpse->flags, F_FROZEN);
|
|
}
|
|
}
|
|
|
|
int push(lifeform_t *lf, object_t *o, int dir) {
|
|
cell_t *obcell, *dstcell;
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
|
|
// remember cells
|
|
obcell = o->pile->where;
|
|
dstcell = getcellindir(obcell, dir);
|
|
|
|
// take time (even if it will fail) - twice normal
|
|
taketime(lf, getactspeed(lf) * 2);
|
|
|
|
if (!obcell || !dstcell) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
if (touch(lf, o)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
// move object
|
|
o = moveob(o, dstcell->obpile, o->amt);
|
|
if (!o) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
// move player
|
|
moveto(lf, obcell, B_TRUE, B_FALSE);
|
|
|
|
// announce
|
|
if (isplayer(lf)) {
|
|
msg("You push %s.", obname);
|
|
} else if (haslos(player, dstcell)) {
|
|
char buf[BUFLEN];
|
|
getlfname(lf, buf);
|
|
capitalise(buf);
|
|
msg("%s pushes %s.", cansee(player, lf) ? buf : "Something", obname);
|
|
}
|
|
|
|
touch(lf, o);
|
|
addflagifneeded(lf->flags, F_MOVED, B_TRUE, NA, NA, NULL);
|
|
addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL);
|
|
|
|
return B_FALSE;
|
|
|
|
}
|
|
|
|
int racecantalk(enum RACE rid) {
|
|
race_t *r;
|
|
r = findrace(rid);
|
|
if (r) {
|
|
switch (r->raceclass->id) {
|
|
case RC_DEMON:
|
|
case RC_DRAGON:
|
|
case RC_GOD:
|
|
case RC_HUMANOID:
|
|
// these ones can talk
|
|
return B_TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (hasflag(r->flags, F_CANTALK)) return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int readytotrain(lifeform_t *lf) {
|
|
if (lf->skillpoints || getattpoints(lf) || levelabilityready(lf)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int recruit(lifeform_t *lf) {
|
|
enum ALIGNMENT pa,lfa;
|
|
// alignments must match, otherwire VERY hard to make them join!
|
|
pa = getalignment(player);
|
|
lfa = getalignment(lf);
|
|
int rv = B_FALSE;
|
|
|
|
if (lfhasflag(lf, F_NOHIRE)) {
|
|
// refusing to join at all.
|
|
sayphrase(lf, SP_RECRUIT_DECLINE, SV_TALK, NA, NULL);
|
|
rv = B_TRUE;
|
|
} else if ( ((pa == AL_GOOD) && (lfa == AL_EVIL)) ||
|
|
((pa == AL_EVIL) && (lfa == AL_GOOD)) ) {
|
|
sayphrase(lf, SP_RECRUIT_DECLINE, SV_TALK, NA, NULL);
|
|
rv = B_TRUE;
|
|
} else {
|
|
int dohire = B_FALSE;
|
|
int askingprice = -1;
|
|
char lfname[BUFLEN];
|
|
char buf[BUFLEN];
|
|
flag_t *f;
|
|
|
|
getlfname(lf, lfname);
|
|
// they will consider it - now negotiate a price
|
|
f = lfhasflag(lf, F_HIREPRICE);
|
|
if (f) {
|
|
askingprice = f->val[0];
|
|
} else {
|
|
int result;
|
|
int difficulty;
|
|
int minmult,maxmult;
|
|
// since you have to be at least speech=5(expert) to ask someone to
|
|
// join, add +10 to difficulty (pr_skilled * 2)
|
|
difficulty = 25 + 10 + ((gettr(player) - gettr(lf))*2);
|
|
|
|
if (real_skillcheck(player, SC_SPEECH, difficulty, 0, &result)) {
|
|
minmult = 10;
|
|
maxmult = 20;
|
|
// passed
|
|
} else {
|
|
if (result <= 30) {
|
|
// very expensive
|
|
minmult = 20;
|
|
maxmult = 30;
|
|
} else {
|
|
// expensive
|
|
minmult = 15;
|
|
maxmult = 25;
|
|
}
|
|
}
|
|
askingprice = rnd(gettr(lf)*minmult, gettr(lf)*maxmult );
|
|
addflag(lf->flags, F_HIREPRICE, askingprice, NA, NA, NULL);
|
|
}
|
|
|
|
if (askingprice != 0) {
|
|
// modify for same job
|
|
if (getjob(player) == getjob(lf)) {
|
|
askingprice = pctof(50, askingprice);
|
|
} else if (player->race->baseid == lf->race->baseid) { // modify for same race
|
|
askingprice = pctof(80, askingprice);
|
|
}
|
|
// modify by charisma
|
|
askingprice = pctof(100 - getstatmod(player, A_CHA), askingprice);
|
|
limit(&askingprice, 0, NA);
|
|
}
|
|
|
|
if (askingprice > 0) {
|
|
sayphrase(lf, SP_RECRUIT_ASKPRICE, SV_TALK, askingprice, NULL);
|
|
more();
|
|
|
|
if (askingprice > countmoney(player->pack)) {
|
|
} else {
|
|
char ch;
|
|
snprintf(buf, BUFLEN, "Pay $%d to hire %s", askingprice, lfname);
|
|
ch = askchar(buf, "yn","n", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
dohire = B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
dohire = B_TRUE;
|
|
}
|
|
|
|
if (dohire) {
|
|
char *p = NULL;
|
|
petify(lf, player);
|
|
|
|
// give them a name
|
|
//if (getjob(lf)) {
|
|
if (lf->race->raceclass->id == RC_HUMANOID) {
|
|
p = assignnpcname(lf->flags);
|
|
}
|
|
sayphrase(lf, SP_RECRUIT_ACCEPT, SV_TALK, NA, p);
|
|
} else {
|
|
if (askingprice > countmoney(player->pack)) {
|
|
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_LEVSPELLSCHOOLFROMX, 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:
|
|
case F_LEVSPELLSCHOOLFROMX:
|
|
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);
|
|
}
|
|
|
|
void setskillused(lifeform_t *lf, enum SKILL skid) {
|
|
flag_t *f;
|
|
f = lfhasflagval(lf, F_HASSKILL, skid, NA, NA, NULL);
|
|
if (f) {
|
|
f->val[2] = B_TRUE;
|
|
}
|
|
}
|
|
|
|
void spot_hiding_lf(lifeform_t *lf, lifeform_t *hider) {
|
|
addflag(lf->flags, F_SPOTTED, hider->id, NA, NA, NULL);
|
|
// announce
|
|
if (isplayer(lf)) {
|
|
char hidername[BUFLEN];
|
|
getlfname(hider, hidername);
|
|
msg("^wYou spot %s!", hidername);
|
|
} else if (isplayer(hider) && cansee(hider, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("You think %s has spotted you!", lfname);
|
|
}
|
|
practice(lf, SK_PERCEPTION, 1);
|
|
}
|
|
|
|
int startclimbing(lifeform_t *lf) {
|
|
cell_t *where;
|
|
char lfname[BUFLEN];
|
|
object_t *pit = NULL;
|
|
char pitname[BUFLEN];
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
where = getcellindir(lf->cell, lf->facing);
|
|
pit = hasobwithflagval(where->obpile, F_PIT, D_DOWN, NA, NA, NULL);
|
|
if (pit) {
|
|
getobname(pit, pitname, 1);
|
|
} else {
|
|
strcpy(pitname, "");
|
|
}
|
|
|
|
if (pit && isplayer(lf)) {
|
|
char ques[BUFLEN], ch;
|
|
sprintf(ques, "Climb down %s?",pitname);
|
|
|
|
ch = askchar(ques, "yn","n", B_TRUE, B_FALSE);
|
|
if (ch != 'y') {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
taketime(lf, getmovespeed(lf));
|
|
|
|
// climbing down a pit?
|
|
if (pit) {
|
|
int diff;
|
|
// move there.
|
|
movelf(lf, where);
|
|
// make a skill check.
|
|
//mod = (countadjwalls(where)+1)/2;
|
|
diff = getcellclimbdifficultyavg(where);
|
|
if (skillcheck(lf, SC_CLIMB, diff, 0)) {
|
|
// if you pass, safely move down the pit.
|
|
// usestairs() will announce this.
|
|
usestairs(lf, pit, B_TRUE, B_TRUE);
|
|
} else {
|
|
// you will fall...
|
|
if (isplayer(lf)) {
|
|
msg("You lose your footing!");
|
|
}
|
|
}
|
|
} else {
|
|
if (skillcheck(lf, SC_CLIMB, getcellclimbdifficulty(where), 0)) {
|
|
// announce
|
|
if (isplayer(lf)) {
|
|
msg("You climb onto %s %s.", needan(where->type->name) ? "an" : "a", where->type->name);
|
|
} else if (cansee(player, lf)) {
|
|
if (haslos(player, where)) {
|
|
msg("%s climbs onto %s %s.", lfname, needan(where->type->name) ? "an" : "a", where->type->name);
|
|
} else {
|
|
msg("%s climbs out of view.", lfname);
|
|
}
|
|
}
|
|
// change facing BEFORE moving, so that we don't reveal cells on the other
|
|
// side of the wall
|
|
setfacing(lf, diropposite(lf->facing));
|
|
movelf(lf, where);
|
|
addflag(lf->flags, F_CLIMBING, B_TRUE, NA, NA, NULL);
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("You try to climb onto %s, but fail.", where->type->name);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s tries to start climbing, but fails.", lfname);
|
|
}
|
|
practice(lf, SK_CLIMBING, 1);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
practice(lf, SK_CLIMBING, 1);
|
|
return B_FALSE;
|
|
}
|
|
|
|
int startresting(lifeform_t *lf, int willtrain) {
|
|
flag_t *f;
|
|
|
|
// player can't rest while in the air, unless you're in a motel room
|
|
if (isplayer(lf)) {
|
|
if (isairborne(lf) && !lfhasflag(lf, F_RESTINGINMOTEL)) {
|
|
msg("You can't %s while airborne!", willtrain ? "train" : "rest");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
if (isplayer(lf)) msg("You are too enraged to %s!", willtrain ? "train" : "rest");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// stop hiding
|
|
killflagsofid(lf->flags, F_HIDING);
|
|
|
|
// stop all spells
|
|
stopallspells(lf);
|
|
|
|
stopsprinting(lf);
|
|
|
|
killflagsofid(lf->flags, F_INTERRUPTED);
|
|
|
|
if (willtrain) {
|
|
f = lfhasflag(lf, F_TRAINING);
|
|
if (f) {
|
|
int trainloss;
|
|
trainloss = 25;
|
|
trainloss = modifybystat(trainloss, player, A_IQ);
|
|
f->val[0] -= trainloss;
|
|
limit(&(f->val[0]), 0, NA);
|
|
} else {
|
|
int traincounter;
|
|
traincounter = 50;
|
|
traincounter = modifybystat(traincounter, player, A_IQ);
|
|
addflag(lf->flags, F_TRAINING, 0, traincounter, NA, NULL);
|
|
}
|
|
} else {
|
|
if (gotosleep(lf, B_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,12);
|
|
break;
|
|
case AT_VLOW:
|
|
roll = rnd(13, 23);
|
|
break;
|
|
case AT_LOW:
|
|
roll = rnd(24, 34);
|
|
break;
|
|
case AT_LTAVERAGE:
|
|
roll = rnd(35, 45);
|
|
break;
|
|
case AT_AVERAGE:
|
|
roll = rnd(46, 56);
|
|
break;
|
|
case AT_GTAVERAGE:
|
|
roll = rnd(57, 67);
|
|
break;
|
|
case AT_HIGH:
|
|
roll = rnd(68, 78);
|
|
break;
|
|
case AT_VHIGH:
|
|
roll = rnd(79, 89);
|
|
break;
|
|
case AT_EXHIGH:
|
|
roll = rnd(90, 100);
|
|
break;
|
|
default:
|
|
roll = rolldie(3,6)*5;
|
|
break;
|
|
}
|
|
return roll;
|
|
}
|
|
|
|
int rollstat(lifeform_t *lf, enum ATTRIB attr) {
|
|
flag_t *f;
|
|
enum ATTRBRACKET bracket;
|
|
|
|
f = hasflagval(lf->flags, F_STARTATT, attr, NA, NA, NULL);
|
|
if (f) {
|
|
if (strlen(f->text)) {
|
|
int val;
|
|
if (strchr(f->text, '-')) {
|
|
int min,max;
|
|
char *p;
|
|
char buf[BUFLENSMALL];
|
|
// text is a range
|
|
p = readuntil(buf, f->text, '-');
|
|
min = atoi(buf);
|
|
p = readuntil(buf, p, '-');
|
|
max = atoi(buf);
|
|
val = rnd(min,max);
|
|
} else {
|
|
val = atoi(f->text);
|
|
}
|
|
lf->att[attr] = val;
|
|
return B_FALSE;
|
|
} else {
|
|
bracket = f->val[1];
|
|
}
|
|
|
|
} else {
|
|
bracket = AT_RANDOM;
|
|
}
|
|
|
|
lf->att[attr] = rollattr(bracket);
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
// safe to rest?
|
|
int safetorest(lifeform_t *lf) {
|
|
lifeform_t *l;
|
|
reason = E_OK;
|
|
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if ((l != lf) && (areenemies(lf, l) || !isknownpeaceful(lf) ) &&
|
|
!lfhasflag(l, F_HARMLESS) && !lfhasflag(l, F_FEIGNINGDEATH)) {
|
|
int monsternearby = B_FALSE;
|
|
|
|
if (isplayer(lf)) {
|
|
if (cansee(lf, l)) {
|
|
monsternearby = B_TRUE;
|
|
}
|
|
} else {
|
|
if (haslof(lf->cell, l->cell, LOF_WALLSTOP, NULL)) {
|
|
monsternearby = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (monsternearby) {
|
|
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];
|
|
char *localtext;
|
|
int rv;
|
|
|
|
localtext = strdup(text);
|
|
|
|
// adjust text and volume for gods
|
|
if (lf) {
|
|
switch (lf->race->id) {
|
|
case R_GODMAGIC:
|
|
volume = SV_TALK;
|
|
strrep(localtext, "You have", "One has", NULL);
|
|
strrep(localtext, "You", "One", NULL);
|
|
strrep(localtext, "you", "one", NULL);
|
|
break;
|
|
case R_GODTHIEVES:
|
|
case R_GODDEATH:
|
|
volume = SV_WHISPER;
|
|
break;
|
|
case R_GODFIRE:
|
|
volume = SV_TRUCK;
|
|
makeuppercase(localtext);
|
|
break;
|
|
case R_GODPURITY:
|
|
case R_GODLIFE:
|
|
case R_GODMERCY:
|
|
case R_GODNATURE:
|
|
volume = SV_TALK;
|
|
break;
|
|
case R_GODBATTLE:
|
|
if (localtext[strlen(localtext)-1] == '.') {
|
|
localtext[strlen(localtext)-1] = '!';
|
|
}
|
|
volume = SV_SHOUT;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
if (volume < 2) {
|
|
strcpy(verb, "whispers");
|
|
strcpy(noun, "a whisper:");
|
|
} else if (volume == 2) {
|
|
strcpy(verb, "says");
|
|
strcpy(noun, "a voice:");
|
|
} else if (volume == 3) {
|
|
strcpy(verb, "shouts");
|
|
strcpy(noun, "a shout:");
|
|
} else if (volume == 4) {
|
|
strcpy(verb, "roars");
|
|
strcpy(noun, "a roar:");
|
|
} else { // ie > 4
|
|
strcpy(verb, "bellows");
|
|
strcpy(noun, "a bellow:");
|
|
}
|
|
|
|
snprintf(seebuf, BUFLEN, "%s \"%s\"", verb, localtext);
|
|
snprintf(hearbuf, BUFLEN, "%s \"%s\"", noun, localtext);
|
|
|
|
rv = noise(lf->cell, lf, NC_SPEECH, volume, hearbuf, seebuf);
|
|
free(localtext);
|
|
return rv;
|
|
}
|
|
|
|
// 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];
|
|
char buf2[BUFLEN];
|
|
char buf3[BUFLEN];
|
|
char *p,*p2;
|
|
race_t *r;
|
|
switch (what) {
|
|
case SP_ALLY_ATTACK:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "I'm attacking %s!", text); break;
|
|
case 2: snprintf(buf, BUFLEN, "%s is mine!", text);
|
|
buf[0] = toupper(buf[0]);
|
|
break;
|
|
case 3: snprintf(buf, BUFLEN, "I'll take care of %s!", text); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_ALLY_ATTACKUNSEEN:
|
|
p = strdup(text);
|
|
strrep(p, "the ","a ", NULL);
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "There's %s over here!", p); break;
|
|
case 2: snprintf(buf, BUFLEN, "I'm attacking %s!", p); break;
|
|
case 3: snprintf(buf, BUFLEN, "Beware %s!", p); break;
|
|
}
|
|
free(p);
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_ALLY_INPAIN:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "I'm hurting here!"); break;
|
|
case 2: snprintf(buf, BUFLEN, "I need to rest soon!"); break;
|
|
case 3: snprintf(buf, BUFLEN, "Help me!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_ALLY_TARGETKILL:
|
|
switch (rnd(1,4)) {
|
|
case 1: snprintf(buf, BUFLEN, "Got it!"); break;
|
|
case 2: snprintf(buf, BUFLEN, "%s one, %s zero!", lf->race->name, noprefix(text));
|
|
buf[0] = toupper(buf[0]);
|
|
break;
|
|
case 3: snprintf(buf, BUFLEN, "Pow!"); break;
|
|
case 4: snprintf(buf, BUFLEN, "Take that!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_BEG:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "Spare a coin, mister?"); break;
|
|
case 2: snprintf(buf, BUFLEN, "Alms for the poor!"); break;
|
|
case 3: snprintf(buf, BUFLEN, "Alms!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_BEGATTACK:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "Now give me the everything else!"); break;
|
|
case 2: snprintf(buf, BUFLEN, "Rich fool!"); break;
|
|
case 3: snprintf(buf, BUFLEN, "Is that all?"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_BEGTHANKS:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "A thousand thanks, good sir!"); break;
|
|
case 2: snprintf(buf, BUFLEN, "Oh thank you!"); break;
|
|
case 3: snprintf(buf, BUFLEN, "My family shall eat tonight!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_DIE:
|
|
switch (rnd(1,4)) {
|
|
case 1:
|
|
if (ispetof(lf, player)) {
|
|
getplayername(buf2);
|
|
snprintf(buf, BUFLEN, "Avenge me, %s!", buf2);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "My death will be avenged!");
|
|
}
|
|
break;
|
|
case 2: snprintf(buf, BUFLEN, "Argh!"); break;
|
|
case 3: snprintf(buf, BUFLEN, "Nooooo!"); break;
|
|
case 4: snprintf(buf, BUFLEN, "This isn't over!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_DRUNK:
|
|
// random blurred speech
|
|
strcpy(buf, "");
|
|
for (i = 0; i < rnd(15,30); i++) {
|
|
if ((i != 0) && onein(4)) {
|
|
strcat(buf, " ");
|
|
} else {
|
|
char let[2];
|
|
let[0] = rnd('a', 'z');
|
|
let[1] = '\0';
|
|
strcat(buf, let);
|
|
}
|
|
}
|
|
if (volume >= SV_SHOUT) {
|
|
strcat(buf, "!");
|
|
} else {
|
|
strcat(buf, ".");
|
|
}
|
|
say(lf, buf, volume);
|
|
break;
|
|
case SP_LIFEOB_DESTROYED:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "NOOOOOOOOO!"); break;
|
|
case 2: snprintf(buf, BUFLEN, "NO! What have you done!?"); break;
|
|
case 3: snprintf(buf, BUFLEN, "It cannot be!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_MERCYACCEPT:
|
|
switch (rnd(1,2)) {
|
|
case 1:
|
|
r = findrace(val0);
|
|
snprintf(buf, BUFLEN, "Cowardly %s...", text);
|
|
break;
|
|
case 2:
|
|
snprintf(buf, BUFLEN, "Guess I don't need to actually kill you...");
|
|
break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_INFO_ACCEPT:
|
|
switch (rnd(1,2)) {
|
|
case 1: snprintf(buf, BUFLEN, "Okay, here's what I know..."); break;
|
|
case 2: snprintf(buf, BUFLEN, "Listen carefully..."); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_INFO_ASKPRICE:
|
|
switch (rnd(1,3)) {
|
|
case 1:
|
|
snprintf(buf, BUFLEN, "I'll tell you for $%d...",val0);
|
|
break;
|
|
case 2:
|
|
snprintf(buf, BUFLEN, "Is the info worth $%d to you?",val0);
|
|
break;
|
|
case 3:
|
|
snprintf(buf, BUFLEN, "$%d and I'll tell you...",val0);
|
|
break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_INFO_REFUSE:
|
|
switch (rnd(1,4)) {
|
|
case 1:
|
|
snprintf(buf, BUFLEN, "What do you think I am, a library?");
|
|
break;
|
|
case 2:
|
|
snprintf(buf, BUFLEN, "Can't help, sorry.");
|
|
break;
|
|
case 3:
|
|
snprintf(buf, BUFLEN, "Get lost!");
|
|
break;
|
|
case 4:
|
|
snprintf(buf, BUFLEN, "No time to talk!");
|
|
break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_INFO_REFUSE_AGAIN:
|
|
switch (rnd(1,3)) {
|
|
case 1:
|
|
snprintf(buf, BUFLEN, "Asking twice isn't going to change the answer!");
|
|
break;
|
|
case 2:
|
|
snprintf(buf, BUFLEN, "Still can't help, sorry.");
|
|
break;
|
|
case 3:
|
|
snprintf(buf, BUFLEN, "I already told you to go away!");
|
|
break;
|
|
case 4:
|
|
snprintf(buf, BUFLEN, "Stop pestering me!");
|
|
break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_INFO_DECLINE_WONTPAY:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "Cheapskate."); break;
|
|
case 2: snprintf(buf, BUFLEN, "Well, I'll be here if you change your mind."); break;
|
|
case 3: snprintf(buf, BUFLEN, "Your loss."); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_PAYWARN:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "Hey! Where do you think you're going?"); break;
|
|
case 2: snprintf(buf, BUFLEN, "AHEM!"); break;
|
|
case 3: snprintf(buf, BUFLEN, "I hope you are going to pay for %s!", text); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_PAYTHREAT:
|
|
switch (rnd(1,3)) {
|
|
case 1: rv = say(lf, "Stop thief!", volume); break;
|
|
case 2: rv = say(lf, "GUARDS!", volume); break;
|
|
case 3: rv = say(lf, "I've been robbed!", volume); break;
|
|
}
|
|
break;
|
|
case SP_RECRUIT_ACCEPT:
|
|
if (lf->race->id == R_PRISONER) {
|
|
if (text) {
|
|
snprintf(buf, BUFLEN, "Thank you! My name is %s.", text);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "Thank you!");
|
|
}
|
|
} else {
|
|
if (text) {
|
|
snprintf(buf, BUFLEN, "I will join you - my name is %s.", text);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "I will join you.");
|
|
}
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_RECRUIT_ASKPRICE:
|
|
switch (rnd(1,8)) {
|
|
case 1: snprintf(buf, BUFLEN, "My services will cost you $%d.", val0); break;
|
|
case 2: snprintf(buf, BUFLEN, "$%d and you have yourself a deal.", val0); break;
|
|
case 3: snprintf(buf, BUFLEN, "Okay. How does $%d sound?", val0); break;
|
|
case 4: snprintf(buf, BUFLEN, "I'll do it for $%d.", val0); break;
|
|
case 5: snprintf(buf, BUFLEN, "Let's see... $%d should do it.", val0); break;
|
|
case 6: snprintf(buf, BUFLEN, "My fee is $%d. Still interested?", val0); break;
|
|
case 7: snprintf(buf, BUFLEN, "I don't work for free. $%d.", val0); break;
|
|
case 8: snprintf(buf, BUFLEN, "$%d and your cause is mine.", val0); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_RECRUIT_DECLINE:
|
|
switch (rnd(1,8)) {
|
|
case 1: snprintf(buf, BUFLEN, "No, I regretfully decline your offer."); break;
|
|
case 2: snprintf(buf, BUFLEN, "You dare ask me for help?"); break;
|
|
case 3: snprintf(buf, BUFLEN, "I will never help you."); break;
|
|
case 4: snprintf(buf, BUFLEN, "No."); break;
|
|
case 5: snprintf(buf, BUFLEN, "Me? Help you?."); break;
|
|
case 6: snprintf(buf, BUFLEN, "Ahem. I think not."); break;
|
|
case 7: snprintf(buf, BUFLEN, "I don't think that would be a good idea."); break;
|
|
case 8: snprintf(buf, BUFLEN, "Sorry, but no."); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_RECRUIT_DECLINE_CANTPAY:
|
|
rv = say(lf, "...which I see you cannot afford.", volume);
|
|
break;
|
|
case SP_RECRUIT_DECLINE_WONTPAY:
|
|
rv = say(lf, "Perhaps another time, then.", volume);
|
|
break;
|
|
case SP_ROBBED:
|
|
switch (rnd(0,1)) {
|
|
case 0: rv = say(lf, "Hey! Where are my things?", volume); break;
|
|
case 1: rv = say(lf, "I've been robbed!", volume); break;
|
|
}
|
|
break;
|
|
case SP_SORRY:
|
|
if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) >= AT_HIGH) {
|
|
switch (rnd(0,1)) {
|
|
case 0: rv = say(lf, "My sincerest condolences!", volume); break;
|
|
case 1: rv = say(lf, "My mistake, I apologise.", volume); break;
|
|
}
|
|
} else {
|
|
switch (rnd(0,1)) {
|
|
case 0: rv = say(lf, "Whoops, sorry!", volume); break;
|
|
case 1: rv = say(lf, "Sorry 'bout that!", volume); break;
|
|
}
|
|
}
|
|
break;
|
|
case SP_PAYTHANKS:
|
|
switch (rnd(1,2)) {
|
|
case 1: snprintf(buf, BUFLEN, "Pleasure doing business with you!"); break;
|
|
case 2: snprintf(buf, BUFLEN, "Thank you, come again!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_THANKS:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "Why, thank you!"); break;
|
|
case 2: snprintf(buf, BUFLEN, "You have my gratitude!"); break;
|
|
case 3: snprintf(buf, BUFLEN, "Thanks!"); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_TRADEINFO_ACCEPT:
|
|
p = text;
|
|
p2 = buf2;
|
|
while (*p != '^') {
|
|
*p2 = *p;
|
|
p++; p2++;
|
|
}
|
|
*p2 = '\0';
|
|
p++;
|
|
// next one...
|
|
p2 = buf3;
|
|
while (*p != '\0') {
|
|
*p2 = *p;
|
|
p++; p2++;
|
|
}
|
|
*p2 = '\0';
|
|
switch (rnd(1,2)) {
|
|
case 1: snprintf(buf, BUFLEN, "I'll train you in %s...", buf3); break;
|
|
case 2: snprintf(buf, BUFLEN, "I can teach you %s...", buf3); break;
|
|
}
|
|
strcat(buf, "if you teach me ");
|
|
strcat(buf, buf2);
|
|
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_TRADEINFO_CANCEL:
|
|
switch (rnd(1,2)) {
|
|
case 1: snprintf(buf, BUFLEN, "Maybe another time, then."); break;
|
|
case 2: snprintf(buf, BUFLEN, "Fair enough."); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_TRADEINFO_DECLINE_ALREADYDONE:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "Thank you, but I have enough knowledge for now."); break;
|
|
case 2: snprintf(buf, BUFLEN, "I'm done with teaching for the moment."); break;
|
|
case 3: snprintf(buf, BUFLEN, "I'm sick of learning at the moment."); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_TRADEINFO_DECLINE_OTHERBAD:
|
|
switch (rnd(1,2)) {
|
|
case 1: snprintf(buf, BUFLEN, "Thank you, but there is nothing I could teach you."); break;
|
|
case 2: snprintf(buf, BUFLEN, "I fear there is nothing you could learn from me."); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
case SP_TRADEINFO_DECLINE_PLAYERBAD:
|
|
switch (rnd(1,3)) {
|
|
case 1: snprintf(buf, BUFLEN, "You teach ME something? I think not."); break;
|
|
case 2: snprintf(buf, BUFLEN, "Sorry, I don't believe there is anything you could teach me."); break;
|
|
case 3: snprintf(buf, BUFLEN, "No thank you, there's nothing I need from you."); break;
|
|
}
|
|
rv = say(lf, buf, volume);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
// returns TRUE if something happened
|
|
int scare(lifeform_t *lf, lifeform_t *scarer, int howlong, int scarerbonus) {
|
|
int nfailures = 0,nsuccesses = 0,scareebonus = 0,i;
|
|
int nchecks;
|
|
if (!scarer) return B_FALSE;
|
|
|
|
// immune to fear?
|
|
if (lfhasflag(lf, F_UNDEAD)) return B_FALSE;
|
|
if (isgod(lf)) return B_FALSE;
|
|
|
|
if (lfhasflag(lf, F_ASLEEP)) {
|
|
return B_FALSE;
|
|
}
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
return B_FALSE;
|
|
}
|
|
// 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) || islowhp(lf)) {
|
|
scareebonus -= 5;
|
|
}
|
|
if (lfhasflag(lf, F_HUMANOID)) {
|
|
object_t *o;
|
|
for (o = scarer->pack->first ; o; o = o->next) {
|
|
flag_t *scareflag;
|
|
scareflag = hasflag(o->flags, F_SCARY);
|
|
if (scareflag) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
getflags(o->flags, retflag, &nretflags, F_EQUIPPED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
// ie. equipped, but not primary or secondary weapon
|
|
if (retflag[i] && (retflag[i]->val[0] > BP_SECWEAPON)) {
|
|
scarerbonus += scareflag->val[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if you have morale left, you make 3 checks. chance of not fleeing at all.
|
|
// if you DONT have morale left, the first check always fails.
|
|
if (isplayer(lf) || lfhasflag(lf, F_MORALE)) {
|
|
nfailures = 0;
|
|
nchecks = 3;
|
|
} else {
|
|
nfailures = 1;
|
|
nchecks = 2;
|
|
// two checks - first one always fails.
|
|
}
|
|
|
|
// the person being scared gets a wisdom bonus
|
|
scareebonus += (getattr(lf, A_WIS)/20);
|
|
|
|
for (i = 0; i < nchecks; i++) {
|
|
if (!skillcheckvs(lf, SC_MORALE, scareebonus, scarer, SC_MORALE, scarerbonus)) {
|
|
nfailures++;
|
|
}
|
|
}
|
|
nsuccesses = 3 - nfailures;
|
|
|
|
// modify morale
|
|
modmorale(lf, nsuccesses - nfailures);
|
|
|
|
if (nfailures == 1) {
|
|
// cower
|
|
fleefrom(lf, scarer, rnd(1,3), B_FALSE);
|
|
return B_TRUE;
|
|
} else if (nfailures == 2) {
|
|
// flee for given time
|
|
fleefrom(lf, scarer, howlong, B_FALSE);
|
|
return B_TRUE;
|
|
} else if (nfailures == 3) {
|
|
object_t *wep;
|
|
// drop weapon and flee for given time
|
|
fleefrom(lf, scarer, howlong, B_FALSE);
|
|
wep = getweapon(lf);
|
|
if (wep) {
|
|
drop(wep, ALL);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void setalignment(lifeform_t *lf, enum ALIGNMENT al) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_ALIGNMENT);
|
|
if (f) {
|
|
f->val[0] = al;
|
|
} else {
|
|
addflag(lf->flags, F_ALIGNMENT, al, NA, NA, NULL);
|
|
}
|
|
}
|
|
|
|
void setattr(lifeform_t *lf, enum ATTRIB attr, int val) {
|
|
lf->att[attr] = val;
|
|
if (lf->att[attr] > lf->baseatt[attr]) {
|
|
lf->baseatt[attr] = lf->att[attr];
|
|
}
|
|
}
|
|
|
|
void setbodypartname(race_t *r, enum BODYPART bp, char *name) {
|
|
int i;
|
|
for (i = 0; i < r->nbodyparts; i++) {
|
|
if (r->bodypart[i].id == bp) {
|
|
free(r->bodypart[i].name);
|
|
r->bodypart[i].name = strdup(name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void setbodytype(race_t *r, enum BODYTYPE bt) {
|
|
int i;
|
|
switch (bt) {
|
|
case BT_BIRD:
|
|
addbodypart(r, BP_EYES, NULL);
|
|
addbodypart(r, BP_EARS, NULL);
|
|
addbodypart(r, BP_HEAD, NULL);
|
|
addbodypart(r, BP_NECK, NULL);
|
|
addbodypart(r, BP_BODY, NULL);
|
|
addbodypart(r, BP_LEGS, NULL);
|
|
addbodypart(r, BP_FEET, "talons");
|
|
addbodypart(r, BP_WINGS, NULL);
|
|
break;
|
|
case BT_HUMANOID:
|
|
for (i = BP_WEAPON; i <= BP_LEFTFINGER; i++) {
|
|
addbodypart(r, i, NULL);
|
|
}
|
|
break;
|
|
case BT_QUADRAPED:
|
|
addbodypart(r, BP_EYES, NULL);
|
|
addbodypart(r, BP_EARS, NULL);
|
|
addbodypart(r, BP_HEAD, NULL);
|
|
addbodypart(r, BP_NECK, NULL);
|
|
addbodypart(r, BP_BODY, NULL);
|
|
addbodypart(r, BP_BACKLEGS, "back legs");
|
|
addbodypart(r, BP_FRONTLEGS, "front legs");
|
|
addbodypart(r, BP_FEET, "paws");
|
|
break;
|
|
case BT_FISH:
|
|
addbodypart(r, BP_EYES, NULL);
|
|
addbodypart(r, BP_HEAD, NULL);
|
|
addbodypart(r, BP_BODY, NULL);
|
|
addbodypart(r, BP_TAIL, NULL);
|
|
break;
|
|
case BT_SNAKE:
|
|
addbodypart(r, BP_EYES, NULL);
|
|
addbodypart(r, BP_HEAD, NULL);
|
|
addbodypart(r, BP_TAIL, NULL);
|
|
break;
|
|
case BT_SPIDER:
|
|
addbodypart(r, BP_EYES, NULL);
|
|
addbodypart(r, BP_HEAD, "cephalothorax");
|
|
addbodypart(r, BP_BODY, "abdomen");
|
|
addbodypart(r, BP_LEGS, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int setfacing(lifeform_t *lf, int dir) {
|
|
if (isclimbing(lf)) { // can't change dir while climbing
|
|
return B_TRUE;
|
|
}
|
|
if (lf->facing == dir) { // already facing that way
|
|
return B_TRUE;
|
|
}
|
|
lf->facing = dir;
|
|
lf->losdirty = B_TRUE;
|
|
if (isplayer(lf)) needredraw = B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
void setfollowdistance(lifeform_t *lf, int min, int max) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_FOLLOWRANGE);
|
|
if (f) {
|
|
f->val[0] = min;
|
|
f->val[1] = max;
|
|
} else {
|
|
addflag(lf->flags, F_FOLLOWRANGE, min, max, NA, NULL);
|
|
}
|
|
}
|
|
|
|
void setguntarget(lifeform_t *lf, lifeform_t *targ) {
|
|
flag_t *f,*targflag = NULL;
|
|
char oldtargbuf[BUFLEN];
|
|
char targbuf[BUFLEN];
|
|
// have an existing target?
|
|
f = hasflag(lf->flags, F_GUNTARGET);
|
|
if (f) {
|
|
strcpy(oldtargbuf, f->text);
|
|
killflag(f);
|
|
} else {
|
|
strcpy(oldtargbuf, "");
|
|
}
|
|
|
|
if (targ) {
|
|
makegunaimstring(lf, targ->id, targbuf); // fill in the text
|
|
f = addflag(lf->flags, F_GUNTARGET, targ->id, NA, NA, targbuf);
|
|
} else {
|
|
targflag = NULL;
|
|
strcpy(targbuf, "nothing");
|
|
}
|
|
|
|
// announce if required
|
|
if (isplayer(lf)) {
|
|
if (!streq(targbuf, oldtargbuf)) {
|
|
warn("Targetted: %s", targbuf );
|
|
statdirty = B_TRUE;
|
|
}
|
|
} else {
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
char lfname[BUFLEN];
|
|
char targname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getlfname(targ, targname);
|
|
dblog("%s targetted %s.",lfname,targname);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sethomeroom(lifeform_t *lf) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags = 0,i;
|
|
|
|
if (gamemode == GM_LOADING) return;
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_STAYINROOM, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[1] == NA) {
|
|
if (lf->cell->room) {
|
|
retflag[i]->val[1] = getroomid(lf->cell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void setkillverb(lifeform_t *lf, char *buf) {
|
|
if (lf->killverb) {
|
|
free(lf->killverb);
|
|
}
|
|
lf->killverb = strdup(buf);
|
|
}
|
|
|
|
void setrace(lifeform_t *lf, enum RACE rid, int frompolymorph) {
|
|
flag_t *f,*nextf;
|
|
int i,retainhp = B_FALSE;
|
|
int nkilled = 0;
|
|
race_t *newrace;
|
|
char buf[BUFLEN];
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
race_t *origrace = NULL;
|
|
int nretflags;
|
|
int reverting = B_FALSE;
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
newrace = findrace(rid);
|
|
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
|
|
if (lfhasflag(lf, F_RETAINHPONPOLY)) {
|
|
retainhp = B_TRUE;
|
|
}
|
|
|
|
loseconcentration(lf);
|
|
loseaitargets(lf);
|
|
|
|
// were we already polymorphed?
|
|
f = lfhasflag(lf, F_ORIGRACE);
|
|
if (f) {
|
|
origrace = findrace(f->val[0]);
|
|
}
|
|
|
|
if (origrace && (newrace == origrace)) {
|
|
reverting = B_TRUE;
|
|
}
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
if (frompolymorph && (gamemode == GM_GAMESTARTED) && lf->race) {
|
|
// remove 'become a ghost' flag
|
|
killflagsofid(lf->flags, F_RISEASGHOST);
|
|
|
|
// announce
|
|
if (reverting) {
|
|
// reverting to original form....
|
|
//if (!isdead(lf)) {
|
|
if (isplayer(lf)) {
|
|
msg("^wYou revert to your original form!");
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
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,
|
|
(getgender(lf) == B_FEMALE) ? "Goddess" : "God", f->text);
|
|
} else {
|
|
msg("^w%s transforms into %s %s!", buf, isvowel(newrace->name[0]) ? "an" : "a", newrace->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// fix injuries
|
|
killflagsofid(lf->flags, F_INJURY);
|
|
|
|
// stop stoning.
|
|
killflagsofid(lf->flags, F_BEINGSTONED);
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
// 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)) {
|
|
// kill most temporary flags, with exceptions
|
|
switch (f->id) {
|
|
case F_FLEEFROM:
|
|
break;
|
|
default:
|
|
killflag(f);
|
|
nkilled++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
// set race
|
|
lf->race = newrace;
|
|
|
|
// set material
|
|
lf->material = lf->race->material;
|
|
|
|
// inherit most flags from race
|
|
for (f = lf->race->flags->first ; f ; f = f->next) {
|
|
int ignorethis = B_FALSE;
|
|
switch (f->id) {
|
|
case F_RARITY:
|
|
case F_MPDICE:
|
|
break;
|
|
default:
|
|
if ((f->condition == FC_IFMONSTER) && isplayer(lf)) {
|
|
ignorethis = B_TRUE;
|
|
} else if ((f->condition == FC_IFPLAYER) && !isplayer(lf)) {
|
|
ignorethis = B_TRUE;
|
|
}
|
|
// don't change hostility when polymorphing
|
|
if (frompolymorph) {
|
|
if (f->id == F_HOSTILE) ignorethis = B_TRUE;
|
|
}
|
|
if (!ignorethis) {
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
addflag_real(lf->flags, f->id, f->val[0], f->val[1], f->val[2], f->text, FROMRACE, f->known, -1);
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
copyflags(lf->flags, lf->race->flags, FROMRACE);
|
|
// don't want certain race only flags...
|
|
killflagsofid(lf->flags, F_RARITY);
|
|
*/
|
|
|
|
// certain other flags are rnadom
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
if (!frompolymorph) {
|
|
getflags(lf->flags, retflag, &nretflags, F_RNDHOSTILE, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_RNDHOSTILE) {
|
|
if (pctchance(f->val[0])) {
|
|
addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
killflag(f);
|
|
}
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
|
|
if (reverting) {
|
|
// restore stats - note that your "base" attrib score
|
|
// is set to your original one here.
|
|
for (i = 0; i < MAXATTS; i++) {
|
|
lf->att[i] = lf->origatt[i];
|
|
lf->baseatt[i] = lf->origatt[i];
|
|
}
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
} else {
|
|
// remember original atts
|
|
for (i = 0; i < MAXATTS; i++) {
|
|
lf->origatt[i] = lf->att[i];
|
|
}
|
|
|
|
// generate new stats
|
|
for (i = 0; i < MAXATTS; i++) {
|
|
rollstat(lf, i);
|
|
lf->baseatt[i] = lf->att[i];
|
|
}
|
|
}
|
|
|
|
|
|
if (!retainhp) {
|
|
// generate hp/maxhp from hit dice
|
|
lf->maxhp = 0;
|
|
if (isplayer(lf)) {
|
|
for (i = 0; i < lf->level; i++) {
|
|
int wantmax = B_FALSE;
|
|
if (i == 0) {
|
|
wantmax = B_TRUE;
|
|
}
|
|
lf->maxhp += rollhitdice(lf, wantmax);
|
|
assert(lf->maxhp > 0);
|
|
}
|
|
} else {
|
|
lf->maxhp += rollhitdice(lf, B_TRUE);
|
|
// one more hitdie for every level after the first
|
|
for (i = 1; i < lf->level; i++) {
|
|
lf->maxhp += rolldie(1, HITDIESIDES);
|
|
}
|
|
}
|
|
lf->hp = lf->maxhp;
|
|
|
|
// don't regenerate mp
|
|
/*
|
|
// generate mp, if you have it.
|
|
f = hasflag(lf->flags, F_MPDICE);
|
|
if (f) {
|
|
lf->maxmp = f->val[0] * 4;
|
|
if (f->val[1] != NA) lf->maxmp += f->val[1];
|
|
for (i = 0; i < lf->level-1; i++) {
|
|
lf->maxmp += rollmpdice(lf);
|
|
}
|
|
} else {
|
|
lf->maxmp = 0;
|
|
}
|
|
lf->mp = lf->maxmp;
|
|
*/
|
|
}
|
|
|
|
lf->born = B_TRUE;
|
|
|
|
setlosdirty(lf);
|
|
|
|
// check whether:
|
|
// new race can equip things (F_NOBODYPART xx)
|
|
// new race can hold objects (F_NOPACK xx)
|
|
// TODO: new race can use magic (F_NOSPELLS)
|
|
// new race can still hold all the items which you have
|
|
if ((gamemode == GM_GAMESTARTED)) {
|
|
object_t *o,*nexto;
|
|
int donemeldmsg = B_FALSE;
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
// no pack?
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
|
|
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
|
|
f = isequipped(o);
|
|
if (f) {
|
|
int stillok = B_TRUE;
|
|
if (!hasbp(lf, f->val[0])) {
|
|
stillok = B_FALSE;
|
|
} else if (isarmour(o) && !armourfits(lf, o, NULL)) {
|
|
stillok = B_FALSE;
|
|
}
|
|
if (!stillok) {
|
|
if (reverting) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
// drop it!
|
|
if (isplayer(lf)) {
|
|
msg("Your %s drops to the ground!",noprefix(obname));
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s%s %s drop to the ground!",buf, getpossessive(buf),
|
|
noprefix(obname));
|
|
}
|
|
moveob(o, lf->cell->obpile, o->amt);
|
|
} else {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
if (!donemeldmsg) {
|
|
if (isplayer(lf)) {
|
|
msg("Your equipment melds into your new form!");
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s%s equipment melds into its new form!",buf, getpossessive(buf));
|
|
}
|
|
donemeldmsg = B_TRUE;
|
|
}
|
|
// drop it!
|
|
moveob(o, lf->polypack, o->amt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
|
|
if (!canpickup(lf, o, o->amt) && !isequipped(o)) {
|
|
if (reverting) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
// drop it!
|
|
if (isplayer(lf)) {
|
|
msg("Your %s drops to the ground!",noprefix(obname));
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s%s %s drop to the ground!",buf, getpossessive(buf),
|
|
noprefix(obname));
|
|
}
|
|
moveob(o, lf->cell->obpile, o->amt);
|
|
} else {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
if (!donemeldmsg) {
|
|
if (isplayer(lf)) {
|
|
msg("Your equipment melds into your new form!");
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s%s equipment melds into its new form!",buf, getpossessive(buf));
|
|
}
|
|
donemeldmsg = B_TRUE;
|
|
}
|
|
moveob(o, lf->polypack, o->amt);
|
|
}
|
|
}
|
|
}
|
|
|
|
// drop/meld everything which isn't equipped.
|
|
// equipped objects are okay because if our new form lacked the required
|
|
// body part, they would have already been melded above.
|
|
if (lfhasflag(lf, F_NOPACK)) {
|
|
if (reverting) {
|
|
// drop everything which isn't equipped
|
|
if (countobs(lf->pack, B_FALSE) - countobswithflag(lf->pack, F_EQUIPPED)) {
|
|
if (isplayer(lf)) {
|
|
msg("Your possessions drop to the ground!");
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s%s possessions drop to the ground!",buf, getpossessive(buf));
|
|
}
|
|
}
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (!isequipped(o)) moveob(o, lf->cell->obpile, o->amt);
|
|
}
|
|
} else {
|
|
// meld
|
|
if (countobs(lf->pack, B_FALSE) - countobswithflag(lf->pack, F_EQUIPPED)) {
|
|
if (isplayer(lf)) {
|
|
msg("Your possessions meld into your new form!");
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s%s possessions meld into its new form!",buf, getpossessive(buf));
|
|
}
|
|
donemeldmsg = B_TRUE;
|
|
}
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (!isequipped(o)) moveob(o, lf->polypack, o->amt);
|
|
}
|
|
}
|
|
}
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
|
|
if (lfhasflag(lf, F_NATURALFLIGHT) && !isairborne(lf)) {
|
|
if (cancast(lf, OT_S_FLIGHT, NULL)) {
|
|
notime = B_TRUE;
|
|
castspell(lf, OT_S_FLIGHT, lf, NULL, lf->cell, NULL, NULL);
|
|
notime = B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
needredraw = B_TRUE;
|
|
statdirty = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
|
|
} // end if gamestarted
|
|
|
|
if (reverting) {
|
|
// restore objects
|
|
while (lf->polypack->first) {
|
|
moveob(lf->polypack->first, lf->pack, lf->polypack->first->amt);
|
|
}
|
|
// in case something which affects our vision came out of ->polypack
|
|
setlosdirty(lf);
|
|
}
|
|
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkmapflags(player->cell->map); // debugging
|
|
}
|
|
|
|
void setlastdam(lifeform_t *lf, char *buf) {
|
|
if (lf->lastdam) {
|
|
free(lf->lastdam);
|
|
}
|
|
lf->lastdam = strdup(buf);
|
|
setkillverb(lf, "Killed"); // default
|
|
}
|
|
|
|
|
|
void interrupt(lifeform_t *lf) {
|
|
stopeating(lf);
|
|
stopresting(lf);
|
|
stoprunning(lf);
|
|
killflagsofid(lf->flags, F_AUTOCMD);
|
|
killflagsofid(lf->flags, F_DIGGING);
|
|
}
|
|
|
|
int setlfmaterial(lifeform_t *lf, enum MATERIAL id, int wantannounce) {
|
|
if (getlfmaterial(lf) == id) {
|
|
return B_TRUE;
|
|
}
|
|
if (hasactivespell(lf, OT_S_BARKSKIN) && (id != MT_WOOD)) {
|
|
stopspell(lf, OT_S_BARKSKIN);
|
|
}
|
|
|
|
lf->material = findmaterial(id);
|
|
|
|
// announce
|
|
if (wantannounce && (gamemode == GM_GAMESTARTED)) {
|
|
if (isplayer(lf)) {
|
|
msg("^wYour body %s to %s%c", (id == lf->race->material->id) ? "reverts" : "turns", lf->material->name,
|
|
(id == lf->race->material->id) ? '.' : '!' );
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s%s body %s to %s%c", lfname, getpossessive(lfname),
|
|
(id == lf->race->material->id) ? "reverts" : "turns",
|
|
lf->material->name,
|
|
(id == lf->race->material->id) ? '.' : '!' );
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void setlosdirty(lifeform_t *lf) {
|
|
// already dirty?
|
|
if (lf->losdirty) return;
|
|
|
|
lf->losdirty = B_TRUE;
|
|
|
|
if (lf->losdirty && isplayer(lf)) {
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
}
|
|
}
|
|
|
|
void setstamina(lifeform_t *lf, float howmuch) {
|
|
float orig;
|
|
orig = getstamina(lf);
|
|
lf->stamina = howmuch;
|
|
limitf(&(lf->stamina), 0, getmaxstamina(lf));
|
|
if (getstamina(lf) != orig) {
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
drawstatus();
|
|
updatestatus();
|
|
if (getstamina(lf) == 0) {
|
|
msg("^BYou are exhausted.");
|
|
} else if (orig == 0) {
|
|
msg("You feel less exhausted now.");
|
|
}
|
|
}
|
|
/*else if (cansee(player, lf)) {
|
|
if (getstamina(lf) == 0) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s looks exhausted.", lfname);
|
|
}
|
|
}*/
|
|
}
|
|
|
|
if (getstamina(lf) == 0) {
|
|
stopsprinting(lf);
|
|
}
|
|
}
|
|
|
|
int shoot(lifeform_t *lf) {
|
|
object_t *gun,*ammo;
|
|
lifeform_t *targ;
|
|
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);
|
|
|
|
taketime(lf, getactspeed(lf));
|
|
|
|
fireat(lf, ammo, 1, targ->cell, firespeed, gun);
|
|
if (!getammo(gun)) {
|
|
if (!isplayer(lf) || getoption(OPT_AUTORELOAD)) {
|
|
// automatically try to reload our empty firearm/ranged weapon.
|
|
if (loadfirearmfast(lf, B_FALSE)) {
|
|
if (isplayer(lf)) {
|
|
char obname[BUFLEN];
|
|
getobname(gun, obname, 1);
|
|
msg("^bYour %s is now out of ammo!", noprefix(obname));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
int getskillcheckchance(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod) {
|
|
int attrib;
|
|
int levmod;
|
|
int othermod = 0;
|
|
int db = B_FALSE;
|
|
int luckmod = 0;
|
|
char mbuf[BUFLEN];
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
flag_t *f;
|
|
int pct;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
if (ct != SC_STEALTH) { // dont show debug info for stealth checks
|
|
db = B_TRUE;
|
|
}
|
|
}
|
|
|
|
switch (ct) {
|
|
case SC_STR:
|
|
attrib = getattr(lf, A_STR)/5;
|
|
break;
|
|
case SC_CON:
|
|
attrib = getattr(lf, A_CON)/5;
|
|
break;
|
|
case SC_DEX:
|
|
attrib = getattr(lf, A_AGI)/5;
|
|
break;
|
|
case SC_IQ:
|
|
attrib = getattr(lf, A_IQ)/5;
|
|
break;
|
|
case SC_CHA:
|
|
// if you're bleeding you're less attractive!
|
|
attrib = (pctof(gethppct(lf), getattr(lf, A_CHA)) / 5);
|
|
if (lfhasflagval(lf, F_INJURY, IJ_BLACKEYE, NA, NA, NULL)) {
|
|
attrib -= 3;
|
|
}
|
|
break;
|
|
case SC_WIS:
|
|
attrib = getattr(lf, A_WIS)/5;
|
|
break;
|
|
///////////////
|
|
case SC_OPENLOCKS:
|
|
attrib = (getattr(lf, A_AGI)/10);
|
|
break;
|
|
case SC_WILL:
|
|
attrib = getattr(lf, A_WIS)/5;
|
|
break;
|
|
case SC_TUMBLE:
|
|
attrib = getskill(lf, SK_ATHLETICS);
|
|
break;
|
|
case SC_LEARNMAGIC:
|
|
attrib = (getattr(lf, A_IQ) / 10) + lf->level;
|
|
break;
|
|
case SC_MORALE: // based on morale, level/hitdice and size.
|
|
attrib = getmorale(lf);
|
|
attrib += getlfsize(lf);
|
|
break;
|
|
case SC_SLIP:
|
|
case SC_FALL:
|
|
attrib = getattr(lf, A_AGI)/5;
|
|
break;
|
|
case SC_SHIELDBLOCK:
|
|
attrib = (getattr(lf, A_AGI) / 20);
|
|
break;
|
|
case SC_POISON:
|
|
attrib = getattr(lf, A_CON)/5;
|
|
break;
|
|
case SC_DISARM:
|
|
attrib = (getskill(lf, SK_TRAPS)*3);
|
|
if (attrib) {
|
|
attrib += (getattr(lf, A_AGI)/20);
|
|
}
|
|
break;
|
|
case SC_CLIMB:
|
|
attrib = (getskill(lf, SK_CLIMBING)*2);
|
|
break;
|
|
case SC_DODGE:
|
|
// getevasion returns 0-100 (ie. pct chance)
|
|
// convert this to 0-20
|
|
attrib = getevasion(lf) / 5;
|
|
break;
|
|
case SC_LISTEN:
|
|
// if you are asleep, listen check doesn't help.
|
|
if (lfhasflag(lf, F_ASLEEP) || isdeaf(lf)) {
|
|
attrib = 0;
|
|
} else {
|
|
attrib = getskill(lf, SK_LISTEN);
|
|
}
|
|
break;
|
|
case SC_RESISTMAG:
|
|
attrib = (getattr(lf, A_CON)/30) + (getattr(lf, A_WIS)/30) + (getmr(lf)/5);
|
|
break;
|
|
case SC_SEARCH:
|
|
attrib = (getskill(lf, SK_PERCEPTION)*2);
|
|
break;
|
|
case SC_SPEECH:
|
|
attrib = (getattr(lf, A_CHA)/10) + (getattr(lf, A_IQ)/10);
|
|
break;
|
|
case SC_STEAL:
|
|
attrib = (getskill(lf, SK_THIEVERY));
|
|
break;
|
|
case SC_STEALTH:
|
|
attrib = (getskill(lf, SK_STEALTH)*3);
|
|
break;
|
|
default:
|
|
// invalid checktype
|
|
dblog("warning: invalid checktype %d", (int)ct);
|
|
msg("warning: invalid checktype %d", (int)ct);
|
|
attrib = 0;
|
|
break;
|
|
}
|
|
|
|
// level modifier
|
|
levmod = (gettr(lf) / 2);
|
|
|
|
// other modifiers
|
|
if (ct == SC_CLIMB) {
|
|
object_t *o;
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
f = hasflag(o->flags, F_HELPSCLIMB);
|
|
if (f && isknown(o)) {
|
|
othermod += f->val[0];
|
|
}
|
|
}
|
|
|
|
// autopass if we have spiderclimb skill.
|
|
if (lfhasflag(lf, F_SPIDERCLIMB)) {
|
|
othermod = diff;
|
|
}
|
|
} else if (ct == SC_DODGE) {
|
|
if (attrib) {
|
|
// ie. -2 to 2
|
|
othermod += (getstatmod(lf, A_AGI) / 20);
|
|
}
|
|
} else if (ct == SC_SLIP) {
|
|
if (lfhasflagval(lf, F_INJURY, IJ_LEGBROKEN, NA, NA, NULL)) {
|
|
othermod -= 25;
|
|
} else {
|
|
if (getequippedob(lf->pack, BP_FEET)) {
|
|
othermod += 5;
|
|
}
|
|
if (lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET)) {
|
|
othermod += 25;
|
|
}
|
|
}
|
|
} else if (ct == SC_FALL) {
|
|
if (lfhasflag(lf, F_STABILITY) || !hasbp(lf, BP_FEET)) {
|
|
othermod += 10;
|
|
}
|
|
} else if (ct == SC_LISTEN) {
|
|
if (hasequippedobid(lf->pack, OT_AMU_LISTEN)) {
|
|
othermod += 20;
|
|
}
|
|
// nocturnal monsters asleep at night, or diurnal during the day
|
|
if (isasleep(lf) && issleepingtimefor(lf)) {
|
|
othermod -= 10;
|
|
}
|
|
} else if (ct == SC_MORALE) {
|
|
if (isbleeding(lf) || islowhp(lf)) {
|
|
othermod -= 5;
|
|
}
|
|
} else if (ct == SC_OPENLOCKS) {
|
|
enum SKILLLEVEL slev;
|
|
slev = getskill(lf, SK_LOCKPICKING);
|
|
if (slev == PR_INEPT) {
|
|
othermod -= 10;
|
|
} else {
|
|
othermod += (5 * slev);
|
|
}
|
|
|
|
if (hasequippedobid(lf->pack, OT_AMU_THIEF)) {
|
|
othermod += 25;
|
|
}
|
|
} else if (ct == SC_POISON) {
|
|
// auto pass if we are immune
|
|
if (isimmuneto(lf->flags, DT_POISON, B_FALSE)) {
|
|
othermod += (diff*2);
|
|
} else if (isresistantto(lf->flags, DT_POISON, B_FALSE)) {
|
|
othermod += 5;
|
|
} else if (isvulnto(lf->flags, DT_POISON, B_FALSE)) {
|
|
othermod -= 10;
|
|
}
|
|
} else if (ct == SC_SEARCH) {
|
|
int bonus = 0;
|
|
sumflags(lf->flags, F_ENHANCESEARCH, &bonus, NULL, NULL);
|
|
othermod += bonus;
|
|
} else if (ct == SC_SPEECH) {
|
|
othermod += (getskill(lf, SK_SPEECH)*2);
|
|
} else if (ct == SC_STEAL) {
|
|
if (attrib > 0) {
|
|
// ie. -3 to 3
|
|
othermod += (getstatmod(lf, A_AGI) / 15);
|
|
}
|
|
} else if (ct == SC_STEALTH) {
|
|
/*
|
|
if (attrib > 0) {
|
|
if (lfhasflag(lf, F_CAREFULMOVE)) {
|
|
othermod += 3;
|
|
}
|
|
}
|
|
*/
|
|
if (!islit(lf->cell)) {
|
|
othermod += 5;
|
|
}
|
|
} else if (ct == SC_WILL) {
|
|
// level counts for more
|
|
levmod *= 2;
|
|
} else if (ct == SC_TUMBLE) {
|
|
// ie. -3 to 3
|
|
othermod += (getstatmod(lf, A_AGI) / 15);
|
|
}
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_SKILLCHECKMOD, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[0] == ct) mod += retflag[i]->val[1];
|
|
}
|
|
|
|
// luck
|
|
sumflags(lf->flags, F_EXTRALUCK, &luckmod, NULL, NULL);
|
|
othermod += luckmod;
|
|
|
|
pct = 100 - (diff*5); // ie. diff 20 = 100%, 10 = 50%
|
|
pct += (attrib*5);
|
|
pct += (mod*5);
|
|
pct += (levmod*5);
|
|
pct += (othermod*5);
|
|
|
|
if (db) {
|
|
snprintf(mbuf, BUFLEN, "%s skcheck (%d): diff=%d, %d(attr)+%d(lvm)+%d(othmod)+%d(mod),pct=%d%%",
|
|
lf->race->name,
|
|
ct, diff, attrib,levmod, othermod,mod,pct);
|
|
msg(mbuf); more();
|
|
}
|
|
limit(&pct, 0, 100);
|
|
return pct;
|
|
}
|
|
|
|
int skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod) {
|
|
return real_skillcheck(lf, ct, diff, mod, NULL);
|
|
}
|
|
|
|
// positive mod makes it easier, negative makes it harder
|
|
// "result" will be filled with difference between our pct chance and what you rolled.
|
|
// positive "result" means you rolled higher than what you need.
|
|
// negative "result" means you rolled lower.
|
|
int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *result) {
|
|
int roll,db = B_FALSE, pct;
|
|
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
if (ct != SC_STEALTH) { // dont show debug info for stealth checks
|
|
db = B_TRUE;
|
|
}
|
|
}
|
|
|
|
pct = getskillcheckchance(lf, ct, diff, mod);
|
|
|
|
// lower roll is better
|
|
roll = rnd(1,100);
|
|
|
|
if (db) {
|
|
msg("rolled %d, need <= %d.",roll,pct);
|
|
}
|
|
|
|
// auto-fail some things
|
|
if (isimmobile(lf) || lfhasflag(lf, F_DOESNTMOVE)) {
|
|
switch (ct) {
|
|
case SC_CLIMB:
|
|
case SC_DODGE:
|
|
case SC_SHIELDBLOCK:
|
|
roll = 9999;
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
if (lfhasflag(lf, F_RAGE) && (ct == SC_STEALTH)) {
|
|
roll = 9999;
|
|
}
|
|
|
|
// auto pass some things
|
|
if (lfhasflag(lf, F_RAGE) && (ct == SC_MORALE)) {
|
|
roll = 0;
|
|
}
|
|
if (hasequippedobid(lf->pack, OT_AMU_ACROBAT) && (ct == SC_TUMBLE)) {
|
|
roll = 0;
|
|
}
|
|
|
|
// natural 20 will pass some checks
|
|
if (roll <= 5) {
|
|
switch (ct) {
|
|
case SC_DODGE:
|
|
case SC_STEALTH:
|
|
if (db) {
|
|
msg("%s skillcheck passed with natural 20.", lf->race->name);
|
|
}
|
|
roll = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result) {
|
|
*result = roll - pct;
|
|
}
|
|
|
|
if (roll <= pct) {
|
|
// 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,*nexto;
|
|
flag_t *f;
|
|
flag_t *asp, *sf;
|
|
char buf[BUFLEN];
|
|
lifeform_t *l;
|
|
int i;
|
|
int willvanish = B_FALSE;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
int movedlastturn = B_FALSE;
|
|
object_t *bloodamu = NULL;
|
|
|
|
map = lf->cell->map;
|
|
|
|
if (db) dblog("startlfturn for lf id %d %s", lf->id, lf->race->name);
|
|
|
|
// MOVE OUT OF STOMACHS WHICH DONT EXIST ANYMORE
|
|
sf = hasflag(map->flags, F_STOMACHOF);
|
|
if (sf) {
|
|
lifeform_t *stomachlf;
|
|
stomachlf = findlf(NULL, sf->val[0]);
|
|
if (!stomachlf || isdead(stomachlf)) {
|
|
object_t *exit;
|
|
cell_t *exitcell,*c;
|
|
map_t *exitmap;
|
|
|
|
exitcell = findobinmap(map, OT_STOMACHEXIT);
|
|
exit = hasob(exitcell->obpile, OT_STOMACHEXIT);
|
|
f = hasflag(exit->flags, F_MAPLINK);
|
|
exitmap = findmap(f->val[0]);
|
|
c = getcellat(exitmap, f->val[1], f->val[2]);
|
|
while (!cellwalkable(lf, c, NULL)) {
|
|
c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND);
|
|
}
|
|
// move lf out!
|
|
movelf(lf, c);
|
|
if (isplayer(lf)) {
|
|
msg("You get expelled from %s!", sf->text);
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s gets expelled from %s!", lfname, sf->text);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isasleep(lf) && ((f = lfhasflag(lf, F_WOKENBY)) != NULL)) {
|
|
lifeform_t *thief;
|
|
thief = findlf(lf->cell->map, f->val[0]);
|
|
if (isplayer(lf)) {
|
|
msg("%s", f->text);
|
|
}
|
|
if (thief) {
|
|
turntoface(lf, thief->cell);
|
|
}
|
|
killflagsofid(lf->flags, F_ASLEEP);
|
|
killflagsofid(lf->flags, F_WOKENBY);
|
|
}
|
|
|
|
|
|
if (!isdead(lf) && !isunconscious(lf) && !isasleep(lf) && hasflag(lf->flags, F_WASROBBED)) {
|
|
if (isplayer(lf)) {
|
|
if (countobs(lf->pack, B_FALSE)) {
|
|
msg("^wSome of your items are missing!^n"); more();
|
|
} else {
|
|
msg("^wAll of your items are missing!^n"); more();
|
|
}
|
|
} else {
|
|
sayphrase(lf, SP_ROBBED, SV_SHOUT, NA, NULL);
|
|
}
|
|
killflagsofid(lf->flags, F_WASROBBED);
|
|
}
|
|
|
|
if (isplayer(lf) && lfhasflag(lf, F_DRUNK)) statdirty = B_TRUE;
|
|
|
|
// clear one-turn-only flags
|
|
lf->rotated = B_FALSE;
|
|
killflagsofid(lf->flags, F_JUSTENTERED);
|
|
killflagsofid(lf->flags, F_UNSEENATTACKER);
|
|
killflagsofid(lf->flags, F_DONELISTEN);
|
|
killflagsofid(lf->flags, F_DONEBURNMSG);
|
|
killflagsofid(lf->flags, F_NOSWAP);
|
|
movedlastturn = 0;
|
|
movedlastturn += killflagsofid(lf->flags, F_HASBEENMOVED);
|
|
movedlastturn += killflagsofid(lf->flags, F_MOVED);
|
|
|
|
// if we didn't turn lsat move, kill our turncounter
|
|
if (!killflagsofid(lf->flags, F_TURNED)) {
|
|
lf->turncounter = 0;
|
|
}
|
|
|
|
// update where player knows
|
|
// (but without a map you will then slowly forget it)
|
|
if (isplayer(lf)) {
|
|
// update line of sight if required
|
|
if (lf->losdirty) precalclos(lf);
|
|
updateknowncells();
|
|
} else {
|
|
// ai start of turn code
|
|
killflagsofid(lf->flags, F_IGNORECELL);
|
|
}
|
|
|
|
// stuck inside solid cells?
|
|
if (!cellwalkable(lf, lf->cell, &error)) {
|
|
if ((error == E_WALLINWAY) && !isclimbing(lf)) {
|
|
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;
|
|
}
|
|
} else {
|
|
// amulet of swimming?
|
|
if (hasequippedobid(lf->pack, OT_AMU_SWIMMING) && (lf->race->id == R_SWAN) && ispolymorphed(lf)) {
|
|
// revert to normal form
|
|
abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL);
|
|
}
|
|
}
|
|
|
|
// suffocate?
|
|
if (needstobreath(lf)) {
|
|
if (lfhasflag(lf, F_NEEDSWATER) && !hasobwithflag(lf->cell->obpile, F_DEEPWATER)) {
|
|
int dam;
|
|
if (isplayer(lf)) {
|
|
msg("^BYou are suffocating without water to breath!");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%c%s is suffocating!", getlfcol(lf, CC_VBAD), lfname);
|
|
}
|
|
dam = lf->maxhp / 3;
|
|
limit(&dam, 1, NA);
|
|
losehp(lf, dam, DT_DIRECT, NULL, "suffocation");
|
|
if (isdead(lf)) return;
|
|
}
|
|
}
|
|
// vampire in sunlight?
|
|
if ((lf->race->id == R_VAMPIRE) && isoutdoors(lf->cell->map) && !isnighttime()) {
|
|
if (isplayer(lf)) {
|
|
msg("^bThe sunlight burns you!");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%cThe sunlight burns %s!", getlfcol(lf, CC_BAD), lfname);
|
|
}
|
|
losehp(lf, roll("6d6"), DT_DIRECT, NULL, "sunlight");
|
|
if (isdead(lf)) return;
|
|
}
|
|
|
|
// float up into space?
|
|
if (lf->cell->map->region->rtype->id == BH_WORLDMAP) {
|
|
if (lfhasflag(lf, F_LEVITATING)) {
|
|
if (isplayer(lf)) {
|
|
msg("You float up into the sky!");
|
|
msg("You float up through the atmosphere.");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s floats up into the sky!", lfname);
|
|
}
|
|
|
|
if (isplayer(lf)) {
|
|
lf->hp = 0;
|
|
lf->lastdamtype = DT_DIRECT;
|
|
setlastdam(lf, "high altitude suffocation");
|
|
} else {
|
|
killlf(lf);
|
|
}
|
|
}
|
|
}
|
|
|
|
// gods get angry
|
|
if (isplayer(lf)) {
|
|
f = lfhasflag(lf, F_GAVEMONEY);
|
|
if (f) {
|
|
int angeramt;
|
|
angeramt = f->val[0];
|
|
limit(&angeramt, 25, NA);
|
|
angergodmaybe(R_GODTHIEVES, angeramt, GA_MONEY);
|
|
killflag(f);
|
|
}
|
|
}
|
|
|
|
|
|
// oooooo replace with f_bleeding ?
|
|
if (lfhasflagval(lf, F_INJURY, IJ_ARTERYPIERCE, NA, NA, NULL)) {
|
|
if (!bleedfrom(lf, BP_HANDS, B_SPLATTER)) {
|
|
if (isplayer(lf)) msg("^BYou bleed!");
|
|
losehp(lf, rnd(2,8), DT_DIRECT, NULL, "blood loss");
|
|
}
|
|
}
|
|
|
|
// get more hungry
|
|
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.5);
|
|
} else if (isswimming(lf) && isplayer(lf)) {
|
|
int lossamt;
|
|
// players lose stamina based on swimming skill
|
|
// monsters don't.
|
|
switch (getskill(lf, SK_SWIMMING)) {
|
|
case PR_NOVICE: lossamt = 1; break;
|
|
case PR_BEGINNER: lossamt = 1; break;
|
|
case PR_ADEPT: lossamt = 0.5; break;
|
|
case PR_SKILLED: lossamt = 0; break;
|
|
case PR_EXPERT: lossamt = 0; break;
|
|
case PR_MASTER: lossamt = 0; break;
|
|
default:
|
|
case PR_INEPT: lossamt = 3; break;
|
|
}
|
|
if (lossamt) modstamina(lf, -lossamt);
|
|
} else if (isclimbing(lf) && !lfhasflag(lf, F_SPIDERCLIMB)) {
|
|
int lossamt;
|
|
// players lose stamina based on climbing skill
|
|
// monsters don't lose stamina to climb.
|
|
switch (getskill(lf, SK_CLIMBING)) {
|
|
case PR_INEPT: lossamt = 2; break;
|
|
case PR_NOVICE: lossamt = 1; break;
|
|
case PR_BEGINNER: lossamt = 1; break;
|
|
case PR_MASTER: lossamt = 0; break;
|
|
default:
|
|
lossamt = 0.5; break;
|
|
}
|
|
if (lossamt) modstamina(lf, -lossamt);
|
|
} else {
|
|
// if we didn't take action last turn, regenerate stamina.
|
|
if (!killflagsofid(lf->flags, F_TOOKACTION)) {
|
|
if (getstamina(lf) < getmaxstamina(lf)) {
|
|
modstamina(lf, getstamregen(lf));
|
|
}
|
|
}
|
|
}
|
|
|
|
// god piety gets restored over time
|
|
if (isplayer(lf)) {
|
|
for (i = 0; i < ngodlfs; i++) {
|
|
int piety;
|
|
if (!godlf[i]) continue;
|
|
if (!godprayedto(godlf[i]->race->id)) continue;
|
|
piety = getpiety(godlf[i]->race->id);
|
|
if ((piety < 100) || (piety > 199)) {
|
|
int dir,chance;
|
|
if (piety < 100) {
|
|
dir = 1;
|
|
chance = piety;
|
|
limit(&chance, 10, 100);
|
|
} else {
|
|
dir = -1;
|
|
chance = 100 - (piety-200); // ie. 100 max
|
|
limit(&chance, 10, 50);
|
|
}
|
|
// the further away from neutral you are, the less chance
|
|
// piety/anger has of 'expiring'
|
|
if (pctchance(chance)) {
|
|
// slowly move towards normal
|
|
modpiety(godlf[i]->race->id, dir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pctchance(25) && lfhasflag(lf, F_PARANOIA) && !lfhasflag(lf, F_AWARENESS)) {
|
|
if (isplayer(lf)) {
|
|
cell_t *c;
|
|
c = getcellindir(lf->cell, diropposite(lf->facing));
|
|
if (c && cellwalkable(NULL, c, NULL)) {
|
|
int monisreal = B_FALSE;
|
|
race_t *r;
|
|
lifeform_t *mon;
|
|
|
|
if (pctchance(15)) monisreal = B_TRUE;
|
|
|
|
// either make a random noise from right behind the player,
|
|
// or ACTUALL create a monster behind them!
|
|
|
|
// get random mosnter who makes walk noise
|
|
f = NULL;
|
|
while (!f) {
|
|
r = getreallyrandomrace(RC_ANY);
|
|
f = hasflagval(r->flags, F_NOISETEXT, N_WALK, NA, NA, NULL);
|
|
// don't want mosnters which will create objects, since we are going
|
|
// to palce a temporary one!
|
|
if (hasflag(r->flags, F_AUTOCREATEOB)) f = NULL;
|
|
}
|
|
// create the monster directly behind you
|
|
if (monisreal) {
|
|
// set autogen to false, we don't want minions appearing too.
|
|
mon = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL);
|
|
} else {
|
|
mon = addlf(c, r->id, 1);
|
|
}
|
|
|
|
if (mon) {
|
|
if (monisreal) {
|
|
killflagsofid(mon->flags, F_XPVAL);
|
|
addflag(mon->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
turntoface(mon, lf->cell);
|
|
}
|
|
// it makes noise
|
|
makenoise(mon, N_WALK);
|
|
if (!monisreal) {
|
|
// now kill the monster
|
|
killlf(mon);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// monster just turns around to look behind.
|
|
loseaitargets(lf);
|
|
setfacing(lf, diropposite(lf->facing));
|
|
}
|
|
}
|
|
|
|
// sixth sense spell warnings
|
|
f = lfhasflag(lf, F_SIXTHSENSE);
|
|
if (f) {
|
|
cell_t *retcell[MAXCANDIDATES];
|
|
int nretcells;
|
|
getradiuscells(lf->cell, 2, DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0);
|
|
for (i = 0; i < nretcells; i++) {
|
|
if (retcell[i]->lf && areenemies(lf, retcell[i]->lf)) {
|
|
int dir,reldir;
|
|
dir = getdirtowards(lf->cell, retcell[i], NULL, B_FALSE, DT_ORTH);
|
|
reldir = getrelativedir(lf, dir);
|
|
if (reldir != RD_FORWARDS) {
|
|
if (isplayer(lf)) {
|
|
if (f->val[0] >= 5) {
|
|
char buf[BUFLEN];
|
|
real_getlfnamea(retcell[i]->lf, buf, NULL, B_NOSHOWALL, B_CURRACE);
|
|
warn("^WYour sixth sense warns you of %s %s you!", buf,
|
|
(reldir == RD_BACKWARDS) ? "behind" : "beside" );
|
|
} else if (f->val[0] >= 3) {
|
|
warn("^WYour sixth sense warns you of %s %s %s you!",
|
|
needan(retcell[i]->lf->race->raceclass->name) ? "an" : "a",
|
|
retcell[i]->lf->race->raceclass->name,
|
|
(reldir == RD_BACKWARDS) ? "behind" : "beside" );
|
|
} else {
|
|
warn("^WYour sixth sense warns you of something %s you!",
|
|
(reldir == RD_BACKWARDS) ? "behind" : "beside" );
|
|
}
|
|
if (f->val[0] >= 7) {
|
|
char ch;
|
|
ch = askchar("Turn to face the threat", "yn","y", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
turntoface(lf, retcell[i]);
|
|
}
|
|
} else {
|
|
more();
|
|
}
|
|
}
|
|
//killflag(f);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (onein(3) && hasbp(lf, BP_TAIL)) {
|
|
cell_t *c;
|
|
c = getcellindir(lf->cell, diropposite(lf->facing));
|
|
if (c && c->lf) {
|
|
if (isplayer(lf)) {
|
|
warn("^WYour tail brushes up against something behind you!");
|
|
} else {
|
|
turntoface(lf, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasactivespell(lf, OT_S_SUMMONWEAPON) && !hasobwithflagval(lf->pack, F_CREATEDBYSPELL, OT_S_SUMMONWEAPON, NA, NA, NULL)) {
|
|
stopspell(lf, OT_S_SUMMONWEAPON);
|
|
}
|
|
if (hasactivespell(lf, OT_S_CRYSTALARM) && !hasobwithflagval(lf->pack, F_CREATEDBYSPELL, OT_S_CRYSTALARM, NA, NA, NULL)) {
|
|
stopspell(lf, OT_S_CRYSTALARM);
|
|
}
|
|
if (hasactivespell(lf, OT_S_CRYSTALSHIELD) && !hasobwithflagval(lf->pack, F_CREATEDBYSPELL, OT_S_CRYSTALSHIELD, NA, NA, NULL)) {
|
|
stopspell(lf, OT_S_CRYSTALSHIELD);
|
|
}
|
|
|
|
if (hasactivespell(lf, OT_S_SLIDE)) {
|
|
f = lfhasflag(lf, F_ICESLIDE);
|
|
if (f) {
|
|
f->val[0]--;
|
|
if (f->val[0] <= 0) {
|
|
stopspell(lf, OT_S_SLIDE);
|
|
}
|
|
} else {
|
|
stopspell(lf, OT_S_SLIDE);
|
|
}
|
|
}
|
|
|
|
if (hasactivespell(lf, OT_S_SIXTHSENSE) && !hasflag(lf->flags, F_SIXTHSENSE)) {
|
|
stopspell(lf, OT_S_SIXTHSENSE);
|
|
}
|
|
|
|
if (lfhasflag(lf, F_MAGSHIELD)) {
|
|
object_t *nexto;
|
|
// metal weapons?
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (ismetal(o->material->id)) {
|
|
if (isequippedon(o, BP_WEAPON) || isequippedon(o, BP_SECWEAPON)) {
|
|
cell_t *c;
|
|
// weapon flies away
|
|
c = getrandomadjcell(lf->cell, 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 %s out of your hands!",noprefix(buf), OB1(o,"flies","fly"));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getobname(o, buf, o->amt);
|
|
getlfname(lf, lfname);
|
|
msg("%s%s %s flies out of its hands!",lfname, getpossessive(lfname),
|
|
noprefix(buf), OB1(o,"flies","fly"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// objects on the ground?
|
|
for (o = lf->cell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (ismetal(o->material->id)) {
|
|
cell_t *c;
|
|
// object flies away
|
|
c = getrandomadjcell(lf->cell, 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);
|
|
}
|
|
} else if (o->material->id == MT_WATER) {
|
|
killtransitoryflags(lf->flags, F_HOTFEET);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// special effects
|
|
|
|
if (lf->race->id == R_ASHKARI) {
|
|
lifeform_t *otherlf;
|
|
for (i = 1; i < lf->nlos; i++) {
|
|
otherlf = lf->los[i]->lf;
|
|
if (otherlf) {
|
|
if (lfhasflag(otherlf, F_CANINE) || lfhasflag(otherlf, F_AVIAN)) {
|
|
f = lfhasflag(lf, F_RAGE);
|
|
if (f && f->lifetime > 0) {
|
|
f->lifetime = DEF_RAGETIME/2;
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(otherlf, lfname);
|
|
msg("The sight of %s makes your blood boil!", lfname);
|
|
}
|
|
addtempflag(lf->flags, F_RAGE, B_TRUE, NA, NA, NULL, DEF_RAGETIME/2);
|
|
if (getstamina(lf) < 1) setstamina(lf, 1);
|
|
forceredraw();
|
|
if (isplayer(lf)) more();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
f = lfhasflag(lf, F_AUTOROTATE);
|
|
if (f && (f->val[0] != 0)) {
|
|
if (!gettargetlf(lf)) {
|
|
int n,whichway;
|
|
if (f->val[0] > 0) whichway = 1;
|
|
else whichway = -1;
|
|
|
|
for (n = 0; n < abs(f->val[0]); n++) {
|
|
int newdir;
|
|
newdir = lf->facing + whichway;
|
|
if (newdir < DC_N) newdir = DC_NW;
|
|
if (newdir > DC_NW) newdir = DC_N;
|
|
|
|
setfacing(lf, newdir);
|
|
}
|
|
|
|
// announce
|
|
if (isplayer(lf)) {
|
|
msg("You turn to face %s.", getdirname(lf->facing));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s turns to face %s.", lfname, getdirname(lf->facing));
|
|
}
|
|
}
|
|
}
|
|
|
|
f = lfhasflag(lf, F_AUTOCREATEOB);
|
|
if (f) {
|
|
if (lf->cell->habitat->id != H_HEAVEN) {
|
|
int radius;
|
|
radius = f->val[0];
|
|
if (radius == -1) {
|
|
cell_t *c;
|
|
// add object in front.
|
|
c = getcellindir(lf->cell, lf->facing);
|
|
if (c && !c->type->solid) {
|
|
addob(c->obpile, f->text);
|
|
}
|
|
} else {
|
|
addobsinradius(lf->cell, f->val[0], DT_COMPASS, f->text, B_FALSE, lf);
|
|
}
|
|
}
|
|
}
|
|
|
|
// handle life objects
|
|
f = lfhasflag(lf, F_LIFEOB);
|
|
if (f) {
|
|
cell_t *corpseloc = NULL;
|
|
object_t *corpse;
|
|
char corpsename[BUFLEN];
|
|
|
|
corpseloc = findnearbylifeob(lf->cell, NA, f, &corpse);
|
|
if (corpse) {
|
|
// did we find a corpse in range ?
|
|
if (corpse->type->id == OT_CORPSE) {
|
|
strcpy(corpsename, "corpse");
|
|
} else {
|
|
strcpy(corpsename, corpse->type->name);
|
|
}
|
|
if (lf->race->id == R_GHOST) {
|
|
// give possession ability
|
|
if (isplayer(lf) && !lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL)) {
|
|
flag_t *f2;
|
|
char pwbuf[BUFLEN];
|
|
int power;
|
|
power = lf->level / 3;
|
|
if (power < 1) power = 1;
|
|
if (power > 10) power = 10;
|
|
snprintf(pwbuf, BUFLEN, "pw:%d;",power);
|
|
f2 = addflag(lf->flags, F_CANWILL, OT_S_POSSESSION, NA, NA, pwbuf);
|
|
f2->lifetime = FROMRACE;
|
|
}
|
|
}
|
|
} else {
|
|
// can't see corpse
|
|
if (lf->race->id == R_GHOST) {
|
|
flag_t *f2;
|
|
f2 = lfhasflagval(lf, F_CANWILL, OT_S_POSSESSION, NA, NA, NULL);
|
|
if (f2) killflag(f2);
|
|
}
|
|
// drain life
|
|
if (isplayer(lf)) {
|
|
msg("^BWithout your %s, you feel yourself fading into nothingness.", corpsename);
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s seems to be fading from view!", lfname);
|
|
}
|
|
losehp(lf, f->val[2], DT_DIRECT, NULL, "fading into nothingness");
|
|
}
|
|
}
|
|
|
|
//effects from other lifeforms
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if (!isdead(l) && (l != lf)) {
|
|
if (l->race->baseid != lf->race->baseid) { // can't smell your own race.
|
|
// gains/loses nauseation?
|
|
f = lfhasflag(l, F_STENCH);
|
|
if (f) {
|
|
int range,power;
|
|
range = f->val[0];
|
|
power = f->val[1];
|
|
if (getcelldist(l->cell, lf->cell) <= range) {
|
|
// you get nauseated
|
|
makenauseated(lf, power, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
asp = hasactivespell(l, OT_S_CALMINGSCENT);
|
|
if (asp && (getcelldist(l->cell,lf->cell) == 1)) {
|
|
if ((getraceclass(lf) == RC_ANIMAL) && lfhasflag(lf, F_HOSTILE)) {
|
|
int maxhitdice;
|
|
// calming strong enough?
|
|
maxhitdice = asp->val[2] + 1;
|
|
if (gettr(lf) <= maxhitdice) {
|
|
makepeaceful(lf, l);
|
|
}
|
|
}
|
|
}
|
|
|
|
// they are hiding, and you haven't spotted them yet
|
|
f = ishidingfrom(l, lf);
|
|
if (f && !isinbattle(lf, B_NODISTANT, B_FALSE)) {
|
|
// can you see their cell?
|
|
if (!lfhasflag(lf, F_TRAINING) && haslos(lf, l->cell)) {
|
|
int 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])) {
|
|
spot_hiding_lf(lf, l);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// secret doors, traps, etc?
|
|
if (isplayer(lf) && !isinbattle(lf, B_INCLUDEDISTANT, B_FALSE) && !isblind(lf) && !lfhasflag(lf, F_TRAINING)) {
|
|
for (i = 0; i < lf->nlos; i++) {
|
|
if (!lf->los[i]->lf || (lf->los[i]->lf == lf)) {
|
|
object_t *o;
|
|
int multiplier;
|
|
multiplier = getcelldist(lf->cell, lf->los[i]);
|
|
limit(&multiplier, 1, NA);
|
|
for (o = lf->los[i]->obpile->first; o ; o = o->next) {
|
|
flag_t *f;
|
|
int mod = 0;
|
|
// object which IS secret (ie a trap or secret door)
|
|
f = hasflag(o->flags, F_SECRET);
|
|
if (f && (f->val[0] != NA)) {
|
|
int diff;
|
|
if (hasflag(o->flags, F_TRAP)) {
|
|
mod += (getskill(lf, SK_TRAPS)*2);
|
|
}
|
|
diff = f->val[0] * multiplier;
|
|
|
|
if (skillcheck(lf, SC_SEARCH, diff, mod)) {
|
|
char obname[BUFLEN];
|
|
// reveal it
|
|
getobnametruebase(o, obname, o->amt);
|
|
msg("^wYou notice %s!",obname);
|
|
interrupt(lf);
|
|
killflag(f);
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
// train skills
|
|
practice(lf, SK_PERCEPTION, 1);
|
|
if (hasflag(o->flags, F_TRAP)) {
|
|
practice(lf, SK_TRAPS, 1);
|
|
}
|
|
}
|
|
}
|
|
// object which CONTAINS something secret (ie. trapped door/chest)
|
|
f = hasflag(o->flags, F_TRAPPED);
|
|
if (f && (f->val[2] != B_TRUE) && !hasflag(o->flags, F_SECRET)) {
|
|
objecttype_t *ot;
|
|
flag_t *trapflag;
|
|
int diff;
|
|
// find trap type
|
|
ot = findot(f->val[0]);
|
|
trapflag = hasflag(ot->flags, F_TRAP);
|
|
assert(trapflag);
|
|
diff = trapflag->val[0] * multiplier;
|
|
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;
|
|
|
|
|
|
if (lfhasflag(lf, F_DODGES)) {
|
|
enum ERROR e;
|
|
if (celldangerous(lf, lf->cell, B_TRUE, &e)) {
|
|
object_t *avoidob = NULL;
|
|
cell_t *adj;
|
|
if ((e == E_AVOIDOB) && rdata) {
|
|
// remember this sine getdodgecell() will call
|
|
// calldangerous() again which will overwrite rdata.
|
|
avoidob = (object_t *)rdata;
|
|
}
|
|
// autododge!
|
|
adj = getdodgecell(lf);
|
|
if (adj) {
|
|
char fromx[BUFLEN];
|
|
if ((e == E_AVOIDOB) && rdata) {
|
|
getobname(avoidob, fromx, avoidob->amt);
|
|
} else {
|
|
sprintf(fromx, "danger");
|
|
}
|
|
if (isplayer(lf)) {
|
|
msg("You reflexively dodge away from %s!", fromx);
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s reflexively dodges away from %s!", lfname, fromx);
|
|
}
|
|
f = addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL);
|
|
moveto(lf, adj, B_FALSE, B_FALSE);
|
|
killflag(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// IMPORTANT: any potentially damaging effects have to go after here...
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
o = hasequippedobid(lf->pack, OT_AMU_CHOKING);
|
|
if (o && needstobreath(lf)) {
|
|
int seen = B_FALSE;
|
|
if (isplayer(lf)) {
|
|
msg("Your amulet is choking you!");
|
|
seen = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s%s amulet is choking %s!", lfname, getpossessive(lfname), it(lf));
|
|
seen = B_TRUE;
|
|
}
|
|
if (seen && !isknown(o)) {
|
|
makeknown(o->type->id);
|
|
}
|
|
losehp(lf, rolldie(2,6), DT_DIRECT, NULL, "an amulet of choking");
|
|
setkillverb(lf, "Strangled");
|
|
}
|
|
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
killflagsofid(lf->flags, F_HIDING);
|
|
}
|
|
|
|
// if you run out of stamina while climbing, you fall
|
|
if (isclimbing(lf)) {
|
|
if ((getstamina(lf) <= 0) || isburdened(lf) || lfhasflag(lf, F_GRAVBOOSTED)) {
|
|
stopclimbing(lf, B_FALSE);
|
|
if (isdead(lf)) return;
|
|
}
|
|
}
|
|
|
|
// if you are stunned and flying, you fall
|
|
if (isairborne(lf) && lfhasflag(lf, F_STUNNED)) {
|
|
fall_from_air(lf);
|
|
}
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
poisontype_t *pt;
|
|
f = retflag[i];
|
|
pt = findpoisontype(f->val[0]);
|
|
// chance of fighting it off - gets easier over time.
|
|
//
|
|
if ((f->lifetime > 0) && skillcheck(lf, SC_POISON, (f->lifetime * 9), -(f->val[1]) )) {
|
|
killflag(f);
|
|
} else {
|
|
flag_t *asleep;
|
|
int chance;
|
|
int ko;
|
|
// being asleep helps.
|
|
asleep = hasflag(lf->flags, F_ASLEEP);
|
|
ko = isunconscious(lf);
|
|
// chance of losing hp
|
|
chance = pt->dampct;
|
|
if (f->val[1] > 1) {
|
|
//+10% chance per power > 1
|
|
chance = pctof(100 + ((f->val[1] - 1)*10), chance);
|
|
}
|
|
|
|
if (!ko && pctchance(chance)) {
|
|
char buf[BUFLEN];
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
char *p;
|
|
char lfname[BUFLEN],lfnameposs[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
sprintf(lfnameposs, "%s%s",lfname, getpossessive(lfname));
|
|
|
|
p = strdup(pt->damverb);
|
|
if (isplayer(lf)) {
|
|
p = strrep(p, "YOUR", "Your", NULL);
|
|
if (asleep) {
|
|
p = strrep(p, "YOU", "You wake up and", NULL);
|
|
} else {
|
|
p = strrep(p, "YOU", "You", NULL);
|
|
}
|
|
p = strrep(p, "#S", "", NULL);
|
|
} else {
|
|
p = strrep(p, "YOUR", lfnameposs, NULL);
|
|
if (asleep) {
|
|
char replacetext[BUFLEN];
|
|
sprintf(replacetext, "%s wakes and", lfname);
|
|
p = strrep(p, "YOU", replacetext, NULL);
|
|
} else {
|
|
p = strrep(p, "YOU", lfname, NULL);
|
|
}
|
|
p = strrep(p, "#S", "s", NULL);
|
|
}
|
|
|
|
msg("%s", p);
|
|
free(p);
|
|
|
|
if (asleep) {
|
|
int origborn;
|
|
origborn = lf->born;
|
|
lf->born = B_FALSE; // supress a second announcement of waking up
|
|
killflagsofid(lf->flags, F_ASLEEP);
|
|
lf->born = origborn;
|
|
} else {
|
|
taketime(lf, getactspeed(lf));
|
|
}
|
|
}
|
|
|
|
// special case
|
|
if (pt->id == P_TETANUS) {
|
|
enum BODYPART bp,poss[MAXBODYPARTS];
|
|
int nposs = 0;
|
|
for (bp = 0; bp < MAXBODYPARTS; bp++) {
|
|
int ok;
|
|
switch (bp) {
|
|
case BP_LEGS:
|
|
case BP_HEAD:
|
|
case BP_HANDS:
|
|
case BP_BODY:
|
|
case BP_TAIL:
|
|
case BP_WINGS:
|
|
ok = B_TRUE;
|
|
break;
|
|
default:
|
|
ok = B_FALSE;
|
|
break;
|
|
}
|
|
if (ok && hasbp(lf, bp)) {
|
|
poss[nposs++] = bp;
|
|
}
|
|
}
|
|
if (nposs) {
|
|
object_t *oo;
|
|
bp = poss[rnd(0,nposs-1)];
|
|
switch (bp) {
|
|
case BP_LEGS:
|
|
injure(lf, bp, DT_BASH, IJ_LEGBROKEN);
|
|
break;
|
|
case BP_HEAD: break; // no bad effects
|
|
case BP_HANDS: // drop your weapon
|
|
oo = getequippedob(lf->pack, bp);
|
|
if (oo) drop(oo, oo->amt);
|
|
break;
|
|
case BP_BODY:
|
|
if (onein(2)) {
|
|
injure(lf, bp, DT_BASH, IJ_RIBCRACKED);
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("^BYour spine snaps!");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^%c%s%s spine snaps!", getlfcol(lf, CC_VBAD),
|
|
lfname, getpossessive(lfname));
|
|
}
|
|
losehp_real(lf, lf->maxhp, DT_DIRECT, NULL, "tetanus", B_FALSE, NULL, B_FALSE, NULL, B_FALSE);
|
|
}
|
|
break;
|
|
case BP_TAIL:
|
|
injure(lf, bp, DT_BASH, IJ_TAILBROKEN);
|
|
break;
|
|
case BP_WINGS:
|
|
injure(lf, bp, DT_SLASH, IJ_WINGTORN);
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
} else {
|
|
snprintf(buf, BUFLEN, "%s^from %s",pt->name, f->text);
|
|
losehp(lf, pt->dam * f->val[1], DT_DIRECT, NULL, buf);
|
|
}
|
|
|
|
if (pt->vomitob != OT_NONE) {
|
|
addobfast(lf->cell->obpile, pt->vomitob);
|
|
}
|
|
loseconcentration(lf);
|
|
}
|
|
|
|
// extra effects
|
|
if (!ko && (f->val[0] == P_COLD)) {
|
|
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);
|
|
}
|
|
} else if (f->val[0] == P_MIGRAINE) {
|
|
// sleeping will avoid all migraine effects
|
|
if (!asleep && !ko) {
|
|
object_t *lamp = NULL;
|
|
int amt;
|
|
amt = lfproduceslight(lf, &lamp);
|
|
if (amt) {
|
|
int dam;
|
|
// note: amt will be doubled due to light vulnerability,
|
|
// so half it here.
|
|
dam = amt/2;
|
|
limit(&dam, 1, NA);
|
|
losehp(lf, amt, DT_LIGHT, NULL, "a migraine");
|
|
if (isplayer(lf)) {
|
|
char obname[BUFLEN];
|
|
if (lamp) {
|
|
getobname(lamp, obname, lamp->amt);
|
|
} else {
|
|
strcpy(obname, "a body"); // noprefix will strip the 'a '
|
|
}
|
|
msg("Your head explodes in pain at the light from your %s!", noprefix(obname));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
if (!movedlastturn) {
|
|
getflags(lf->flags, retflag, &nretflags, F_HOTFEET, F_NONE);
|
|
if (nretflags && !hasobofmaterial(lf->cell->obpile, MT_WATER) && !isimmuneto(lf->flags, DT_FIRE, B_FALSE)) {
|
|
for (i = 0; i < nretflags; i++) {
|
|
int dam;
|
|
enum DAMTYPE dt;
|
|
f = retflag[i];
|
|
dam = f->val[0];
|
|
dt = f->val[1];
|
|
o = getarmour(lf, BP_FEET);
|
|
if (o && ismetal(o->material->id)) {
|
|
dam *= 2;
|
|
}
|
|
if (losehp(lf, dam, dt, NULL, f->text)) {
|
|
if (isplayer(lf)) {
|
|
msg("^bYour %s burn!^n", getbodypartname(lf, BP_FEET));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("^b%s%s %s burn!^n", lfname, getpossessive(lfname), getbodypartname(lf, BP_FEET));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (isdead(lf)) return;
|
|
|
|
bloodamu = hasequippedobid(lf->pack, OT_AMU_BLOOD);
|
|
|
|
// effects from cell objects?
|
|
for (o = lf->cell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (bloodamu && (o->material->id == MT_BLOOD)) {
|
|
int amt;
|
|
amt = getobhp(o, NULL) * o->amt;
|
|
limit(&amt, 1, 10);
|
|
if (!isknown(bloodamu)) {
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
char obname[BUFLEN],bname[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getobname(bloodamu, obname, 1);
|
|
getobname(o, bname, 1);
|
|
msg("^%c%s%s %s sucks up %s!", getlfcol(lf, CC_GOOD), lfname, getpossessive(lfname), noprefix(obname), bname);
|
|
makeknown(bloodamu->type->id);
|
|
}
|
|
}
|
|
gainhp(lf, amt);
|
|
killob(o);
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
f = hasflag(o->flags, F_GODSTONE);
|
|
if (f && (f->val[2] == B_TRUE)) {
|
|
flag_t *f2;
|
|
lifeform_t *god;
|
|
// warn!
|
|
f2 = hasflag(o->flags, F_LINKGOD);
|
|
god = findgod(f2->val[0]);
|
|
godsay(god->id, B_TRUE, "Mortal! Do not touch that!"); more();
|
|
f->val[2] = B_FALSE;
|
|
}
|
|
*/
|
|
|
|
f = hasflag(o->flags, F_WALKDAM);
|
|
if (f) {
|
|
applywalkdam(lf, roll(f->text), f->val[0], o);
|
|
}
|
|
|
|
f = hasflag(o->flags, F_CAUSESCOUGH);
|
|
if (f && !isimmuneto(lf->flags, DT_POISONGAS, B_FALSE)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
if (!skillcheck(lf, SC_CON, f->val[0] * o->amt, 0)) {
|
|
if (isplayer(lf)) {
|
|
msg("^wYou cough on %s.", obname);
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s coughs on %s.",lfname, obname);
|
|
}
|
|
taketime(lf, 5);
|
|
loseconcentration(lf);
|
|
}
|
|
|
|
}
|
|
|
|
f = hasflag(o->flags, F_STENCH);
|
|
if (f) {
|
|
makenauseated(lf, f->val[1], 2);
|
|
}
|
|
|
|
// for flags which can occur multiple times
|
|
getflags(o->flags, retflag, &nretflags, F_WALKDAMBP, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_WALKDAMBP) {
|
|
if (!isairborne(lf)) {
|
|
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)) {
|
|
char warntext[BUFLEN];
|
|
sprintf(warntext,"Your %s get wet.", getbodypartname(lf, bp));
|
|
// only announce this if you've had a turn
|
|
// without your feet wet.
|
|
real_warnabout(warntext, 2, B_FALSE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// apply damage to lf instead.
|
|
applywalkdam(lf, roll(f->text), f->val[1], o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end foreach object flag
|
|
}
|
|
if (isdead(lf)) return;
|
|
|
|
// effects from pack objects
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
// touch your weapons/armour in case it became hot, it was blessed and you
|
|
// becaome undead, etc.
|
|
if (isequipped(o)) {
|
|
real_touch(lf, o, B_NOTONPURPOSE);
|
|
}
|
|
}
|
|
if (isdead(lf)) return;
|
|
|
|
// effects for/on your own flags
|
|
getflags(lf->flags, retflag, &nretflags, F_ANTICIPATE, F_ATTACHEDTO, F_CANCAST, F_CANWILL, F_CHARMEDBY, F_CLIMBING, F_FEIGNFOOLEDBY,F_FLEEFROM,
|
|
F_GRABBEDBY, F_GRABBING, F_HIDING, F_BOOSTSPELL, F_FEIGNINGDEATH, F_HPDRAIN, F_INCUBATING, F_INJURY,
|
|
F_NOFLEEFROM, F_PETOF, F_SIZETIMER, F_SPOTTED, F_STRIKETOKO, F_TARGETCELL, F_TARGETLF, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
// remove impossible/expired flags
|
|
if (f->id == F_ANTICIPATE) {
|
|
if (f->val[1] <= 0) {
|
|
killflag(f);
|
|
continue;
|
|
} else if (!findlf(lf->cell->map, f->val[0])) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((f->id == F_BOOSTSPELL) && (f->val[0] == OT_S_PASSWALL)) {
|
|
if (!lfhasflag(lf, F_NONCORPOREAL)) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((f->id == F_FEIGNINGDEATH) && !isprone(lf)) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
|
|
if (f->id == F_ATTACHEDTO) {
|
|
lifeform_t *lf2;
|
|
lf2 = findlf(NULL, f->val[0]);
|
|
if (!lf2) {
|
|
killflag(f);
|
|
continue;
|
|
} else if (getcelldist(lf2->cell, lf->cell) != 1) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (f->id == F_HIDING) {
|
|
if (lfhasflag(lf, F_SPRINTING) || lfhasflag(lf, F_PRODUCESLIGHT)) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (f->id == F_GRABBEDBY) {
|
|
lifeform_t *lf2;
|
|
lf2 = findlf(NULL, f->val[0]);
|
|
if (!lf2) {
|
|
killflag(f);
|
|
continue;
|
|
} else if (getcelldist(lf2->cell, lf->cell) != 1) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
if (f->id == F_GRABBING) {
|
|
lifeform_t *lf2;
|
|
lf2 = findlf(NULL, f->val[0]);
|
|
if (!lf2) {
|
|
killflag(f);
|
|
continue;
|
|
} else if (getcelldist(lf2->cell, lf->cell) != 1) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (f->id == F_INCUBATING) {
|
|
f->val[1]--;
|
|
if (f->val[1] <= 0) {
|
|
// parse text to get power & whatfrom
|
|
char *p;
|
|
char buf[BUFLEN];
|
|
int power;
|
|
enum POISONTYPE ptype;
|
|
ptype = f->val[0];
|
|
p = readuntil(buf, f->text, '^');
|
|
power = atoi(buf);
|
|
readuntil(buf, p, '^');
|
|
addtempflag(lf->flags, F_POISONED, ptype, power, f->obfrom, buf, f->val[2]);
|
|
poisoneffects(lf, ptype, power);
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// bleeding injuries can stain armour. also mark injuries as no longer new.
|
|
if ((f->id == F_INJURY) && (f->val[2] == DT_SLASH)) {
|
|
object_t *arm;
|
|
|
|
if (f->obfrom == B_NEWINJURY) f->obfrom = B_FALSE;
|
|
|
|
arm = getequippedob(lf->pack, f->val[1]);
|
|
if (arm && !hasobmod(arm, findobmod(OM_BLOODSTAINED)) && pctchance(5)) applyobmod(arm, findobmod(OM_BLOODSTAINED));
|
|
}
|
|
|
|
if (f->id == F_SIZETIMER) {
|
|
f->val[1]--;
|
|
if (f->val[1] <= 0) {
|
|
resizelf(lf, f->val[0], f->val[2]);
|
|
// resizelf will have now killed 'f'.
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (f->id == F_STABBEDBY) {
|
|
lifeform_t *lf2;
|
|
lf2 = findlf(NULL, f->val[0]);
|
|
// stabber can't see you anymore
|
|
if (!lf2 || !cansee(lf2, lf)) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
*/
|
|
if (f->id == F_SPOTTED) {
|
|
lifeform_t *lf2;
|
|
lf2 = findlf(NULL, f->val[0]);
|
|
|
|
if (!lf || !lf2 || !cansee(lf, lf2) || !lfhasflag(lf2, F_HIDING)) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (f->id == F_CLIMBING) {
|
|
if (!lf->cell->type->solid) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (f->id == F_FEIGNFOOLEDBY) {
|
|
if (!findlf(lf->cell->map, f->val[0])) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((f->id == F_CHARMEDBY) ||
|
|
(f->id == F_PETOF) ||
|
|
(f->id == F_FLEEFROM) ||
|
|
(f->id == F_NOFLEEFROM)) {
|
|
lifeform_t *lf2;
|
|
lf2 = findlf(NULL, f->val[0]);
|
|
if (!lf2) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (f->id == F_STRIKETOKO) {
|
|
object_t *weapon;
|
|
weapon = getweapon(lf);
|
|
if (weapon) { // (unarmed is ok)
|
|
skill_t *sk;
|
|
sk = getobskill(weapon->flags);
|
|
if (!sk || (sk->id != SK_CLUBS)) {
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// recharge abilities
|
|
if ((f->id == F_CANWILL) || (f->id == F_CANCAST)) {
|
|
if (f->val[2] != NA) {
|
|
if (f->val[1] < f->val[2]) {
|
|
f->val[1]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove invalid targets
|
|
if ((f->id == F_TARGETLF) || (f->id == F_TARGETCELL)) {
|
|
lifeform_t *targ = NULL;
|
|
if (f->id == F_TARGETLF) {
|
|
targ = findlf(lf->cell->map, f->val[0]);
|
|
} else if (f->id == F_TARGETCELL) {
|
|
cell_t *c;
|
|
c = getcellat(lf->cell->map, f->val[0], f->val[1]);
|
|
if (c && c->lf) {
|
|
targ = c->lf;
|
|
}
|
|
}
|
|
if (targ) {
|
|
if (areallies(lf, targ) || lfhasflagval(lf, F_FEIGNFOOLEDBY, targ->id, NA, NA, NULL)) {
|
|
if (lfhasflag(lf, F_DEBUG)) {
|
|
char lfname[BUFLEN];
|
|
char targname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getlfname(targ, targname);
|
|
dblog("db: %s no longer targetting %s.",lfname,targname);
|
|
}
|
|
killflag(f);
|
|
continue;
|
|
}
|
|
}
|
|
} // end if f_target or f_targetcell
|
|
|
|
// hp drain
|
|
if (f->id == F_HPDRAIN) {
|
|
losehp(lf, f->val[0], f->val[1], NULL, f->text);
|
|
if (isdead(lf)) {
|
|
break;
|
|
}
|
|
}
|
|
} // end loop through lf flags
|
|
|
|
// picked up your first godstone ?
|
|
if (isplayer(lf)) {
|
|
o = hasobofclass(lf->pack, OC_GODSTONE);
|
|
if (o && !lfhasflag(lf, F_FOUNDGODSTONE)) {
|
|
lifeform_t *god,*opposegod;
|
|
// get linked gods
|
|
f = hasflag(o->flags, F_LINKGOD);
|
|
god = findgod(f->val[0]);
|
|
opposegod = findgod(getopposinggod(god->race->id));
|
|
godstone_pickup_effects(god, opposegod, o);
|
|
addflag(lf->flags, F_FOUNDGODSTONE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns TRUE on failure (ie. nothing to steal)
|
|
int steal(lifeform_t *lf, obpile_t *op, enum FLAG wantflag) {
|
|
enum SKILLLEVEL slev;
|
|
object_t *o;
|
|
int i,nsteals;
|
|
int numgot = 0;
|
|
int fromground;
|
|
char letter = 'a';
|
|
slev = getskill(lf, SK_THIEVERY);
|
|
|
|
if (op->owner) {
|
|
fromground = B_FALSE;
|
|
} else {
|
|
fromground = B_TRUE;
|
|
}
|
|
//
|
|
if (slev >= PR_EXPERT) {
|
|
nsteals = 2;
|
|
} else {
|
|
nsteals = 1;
|
|
}
|
|
|
|
// what do we steal?
|
|
for (i = 0; i < nsteals; i++) {
|
|
char buf[BUFLEN];
|
|
snprintf(buf, BUFLEN, "Steal what (%d of %d)?", i+1, nsteals);
|
|
initprompt(&prompt, buf);
|
|
for (o = op->first ; o ; o = o->next) {
|
|
int ok = B_TRUE;
|
|
if ((slev < PR_SKILLED) && (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 (hasflag(o->flags, F_NOSTEAL)) {
|
|
ok = B_FALSE;
|
|
} else if ((wantflag != F_NONE) && !hasflag(o->flags, wantflag)) {
|
|
// don't have the right flag
|
|
ok = B_FALSE;
|
|
}
|
|
if (ok) {
|
|
getobname(o, buf, 1);
|
|
addchoice(&prompt, fromground ? letter++ : o->letter, buf, NULL, o, NULL);
|
|
}
|
|
}
|
|
if (prompt.nchoices > 1) {
|
|
if (slev >= PR_ADEPT) {
|
|
if (isplayer(lf)) {
|
|
addchoice(&prompt, '-', "Nothing", NULL, NULL, NULL);
|
|
// pick what you want
|
|
getchoice(&prompt);
|
|
o = (object_t *)prompt.result;
|
|
} else {
|
|
int nn,nposs = 0;
|
|
object_t *poss[MAXCHOICES*2];
|
|
// get something we want
|
|
for (nn = 0; nn < prompt.nchoices; nn++) {
|
|
int cov;
|
|
if (aiwants(lf, (object_t *)prompt.choice[nn].data, &cov)) {
|
|
if (cov) { // add twice
|
|
poss[nposs++] = o;
|
|
poss[nposs++] = o;
|
|
} else {
|
|
poss[nposs++] = o;
|
|
}
|
|
}
|
|
}
|
|
if (nposs) {
|
|
o = poss[rnd(0,nposs-1)];
|
|
} else {
|
|
o = (object_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data;
|
|
}
|
|
}
|
|
} else {
|
|
// random
|
|
o = (object_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data;
|
|
}
|
|
if (o) {
|
|
int amt = 1;
|
|
//killflagsofid(o->flags, F_SHOPITEM);
|
|
if (o->type->id == OT_GOLD) {
|
|
amt = rnd(1,slev*33);
|
|
}
|
|
o = moveob(o, lf->pack, amt);
|
|
if (o) {
|
|
char obname[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
char targname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getobname(o, obname, amt);
|
|
if (op->owner) {
|
|
getlfname(op->owner, targname);
|
|
if (isplayer(lf)) {
|
|
msg("^%cYou steal %s from %s!", getlfcol(op->owner, CC_BAD), obname, targname);
|
|
} else if (cansee(player, lf)) {
|
|
msg("^%c%s steals %s from %s!", getlfcol(op->owner, CC_BAD), lfname, obname, targname);
|
|
}
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("You steal %s!", obname);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s steals %s!", lfname, obname);
|
|
}
|
|
}
|
|
numgot++;
|
|
}
|
|
}
|
|
} else {
|
|
// nothing left to steal
|
|
|
|
if (numgot == 0) {
|
|
return B_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
} // end foreach steal
|
|
|
|
if (isplayer(lf)) pleasegodmaybe(R_GODTHIEVES, 5+numgot);
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
int stone(lifeform_t *lf) {
|
|
char lfname[BUFLEN];
|
|
char statname[BUFLEN];
|
|
int failed = B_FALSE;
|
|
|
|
if (!lfcanbestoned(lf)) {
|
|
failed = B_TRUE;
|
|
}
|
|
if (failed) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
|
|
|
|
// kill lifeform
|
|
addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
|
|
snprintf(statname, BUFLEN, "statue of a %s", lf->race->name);
|
|
|
|
killflagsofid(lf->flags, F_CORPSETYPE);
|
|
addflag(lf->flags, F_CORPSETYPE, B_TRUE, NA, NA, statname);
|
|
|
|
if (cansee(player, lf)) {
|
|
msg("^%c%s %s to stone!", getlfcol(lf, CC_VBAD), lfname, isplayer(lf) ? "turn" : "turns");
|
|
}
|
|
setlastdam(lf, "petrification");
|
|
lf->hp = 0;
|
|
return B_FALSE;
|
|
}
|
|
|
|
void stopeating(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_EATING);
|
|
if (f) {
|
|
if (isplayer(lf)){
|
|
msg("You stop eating.");
|
|
}
|
|
killflagsofid(lf->flags, F_EATING);
|
|
}
|
|
|
|
}
|
|
|
|
int stopclimbing(lifeform_t *lf, int onpurpose) {
|
|
cell_t *c;
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
if (!onpurpose) {
|
|
if (isplayer(lf)) {
|
|
msg("You fall off %s!", lf->cell->type->name);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s falls off %s!", lfname, lf->cell->type->name);
|
|
}
|
|
}
|
|
c = getcellindir(lf->cell, lf->facing);
|
|
if (!cellwalkable(lf, c, NULL)) {
|
|
if (onpurpose) {
|
|
if (isplayer(lf)) {
|
|
msg("There is no room for you to stop climbing!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
// if not on purpose, try to find another cell
|
|
c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND);
|
|
if (!c) {
|
|
if (isplayer(lf)) {
|
|
msg("Luckily, there is no room for you to fall.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
movelf(lf, c);
|
|
killflagsofid(lf->flags, F_CLIMBING);
|
|
if (onpurpose) {
|
|
if (isplayer(lf)) msg("You drop down to the ground.");
|
|
taketime(lf, getmovespeed(lf));
|
|
} else {
|
|
fall(lf, NULL, B_TRUE);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void stopresting(lifeform_t *lf) {
|
|
flag_t *f;
|
|
|
|
// stop training
|
|
f = hasflag(lf->flags, F_TRAINING);
|
|
if (f) {
|
|
killflag(f);
|
|
if (isplayer(lf)) {
|
|
msg("Your training is interrupted!");
|
|
} else if (cansee(player, lf)) {
|
|
char buf[BUFLEN];
|
|
getlfname(lf, buf);
|
|
capitalise(buf);
|
|
msg("%s stops training.",buf);
|
|
}
|
|
statdirty = B_TRUE;
|
|
}
|
|
// stop resting!
|
|
f = isresting(lf);
|
|
if (f) {
|
|
if (isplayer(lf)) {
|
|
msg("Your %s is interrupted!", (f->val[1] == ST_MEDITATING) ? "meditation" : "rest");
|
|
} else if (cansee(player, lf)) {
|
|
char buf[BUFLEN];
|
|
getlfname(lf, buf);
|
|
msg("%s stops %s.",buf, (f->val[1] == ST_MEDITATING) ? "meditating" : "resting");
|
|
}
|
|
killflag(f);
|
|
statdirty = B_TRUE;
|
|
}
|
|
killflagsofid(lf->flags, F_RESTUNTILALLIES);
|
|
killflagsofid(lf->flags, F_RESTUNTILBETTER);
|
|
/*
|
|
if (isplayer(lf)) {
|
|
f = lfhasflag(lf, F_RESTINGINMOTEL);
|
|
if (f) {
|
|
msg("You stayed for %d turns out of %d.", f->val[1], f->val[0]);
|
|
}
|
|
}
|
|
*/
|
|
killflagsofid(lf->flags, F_RESTINGINMOTEL);
|
|
}
|
|
|
|
void stoprunning(lifeform_t *lf) {
|
|
killflagsofid(lf->flags, F_RUNNING);
|
|
}
|
|
|
|
void stopsprinting(lifeform_t *lf) {
|
|
flag_t *f;
|
|
f = lfhasflag(lf, F_SPRINTING);
|
|
if (f && f->val[0]) {
|
|
killflag(f);
|
|
}
|
|
|
|
}
|
|
|
|
int stun(lifeform_t *lf, int nturns) {
|
|
if (lfhasflag(lf, F_ASLEEP) || lfhasflag(lf, F_STUNNED)) {
|
|
return B_TRUE;
|
|
}
|
|
addtempflag(lf->flags, F_STUNNED, B_TRUE, NA, NA, NULL, nturns);
|
|
loseaitargets(lf);
|
|
loseconcentration(lf);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// wrapper for addmonster(), but announce that it appears
|
|
// and make it worth zero xp.
|
|
//
|
|
// for unique monsters, they move from their current position.
|
|
lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, char *racename, int lifetime, int wantfriendly) {
|
|
lifeform_t *newlf = NULL;
|
|
char buf[BUFLEN];
|
|
|
|
newlf = addmonster(c, rid, racename, B_TRUE, 1, B_FALSE, NULL);
|
|
if (newlf) {
|
|
if (haslos(player, c)) {
|
|
//char *newbuf;
|
|
getlfnamea(newlf, buf);
|
|
capitalise(buf);
|
|
msg("%s appears!", buf);
|
|
}
|
|
|
|
if (!hasflag(newlf->flags, F_UNIQUE)) {
|
|
// summoned
|
|
if (caster) {
|
|
addflag(newlf->flags, F_SUMMONEDBY, caster->id, lifetime, NA, NULL);
|
|
if (wantfriendly) {
|
|
addflag(newlf->flags, F_PETOF, caster->id, NA, NA, NULL);
|
|
if (areallies(player, caster)) {
|
|
makefriendly(newlf, PERMENANT);
|
|
}
|
|
}
|
|
}
|
|
// not worth any xp
|
|
killflagsofid(newlf->flags, F_XPVAL);
|
|
addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
}
|
|
}
|
|
return newlf;
|
|
}
|
|
|
|
|
|
int takeoff(lifeform_t *lf, object_t *o) {
|
|
char obname[BUFLEN];
|
|
char buf[BUFLEN];
|
|
|
|
if (!isarmour(o)) {
|
|
return unweild(lf, o);
|
|
}
|
|
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
if (isplayer(lf)) msg("You are too enraged!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
getobname(o, obname, 1);
|
|
|
|
if (!cantakeoff(lf, o)) {
|
|
switch (reason) {
|
|
case E_CURSED:
|
|
if (isplayer(lf)) {
|
|
msg("Your %s appears to be stuck to you!", noprefix(obname));
|
|
o->blessknown = B_TRUE;
|
|
}
|
|
// still take time
|
|
taketime(lf, getactspeed(lf));
|
|
break;
|
|
case E_NOTEQUIPPED:
|
|
if (isplayer(lf)) {
|
|
msg("You are not wearing that!");
|
|
}
|
|
break;
|
|
case E_INJURED:
|
|
if (isplayer(lf)) {
|
|
msg("Your injury prevents you from removing your %s.", noprefix(obname));
|
|
}
|
|
break;
|
|
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
|
|
killflagsofid(o->flags, F_EQUIPPED);
|
|
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
unequipeffects(lf, o);
|
|
|
|
// if you don't have a pack, it goes to the ground.
|
|
if (lfhasflag(lf, F_NOPACK)) {
|
|
moveob(o, lf->cell->obpile, o->amt);
|
|
}
|
|
|
|
return B_FALSE;
|
|
|
|
}
|
|
|
|
|
|
void takerotationtime(lifeform_t *lf) {
|
|
// first rotation in a turn is free
|
|
if (lf->rotated) {
|
|
taketime(lf, getturnspeed(lf));
|
|
} else {
|
|
lf->rotated = B_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
void taketime(lifeform_t *lf, long howlong) {
|
|
int db = B_FALSE;
|
|
map_t *map;
|
|
|
|
if (notime || lfhasflag(lf, F_NOTIME)) {
|
|
return;
|
|
}
|
|
|
|
map = lf->cell->map;
|
|
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
if (!enteringmap && (map != player->cell->map)) {
|
|
// lfs not on the player's map don't take time.
|
|
// this avoids the assertion below failing when
|
|
// (for example) a monster falls through a pit.
|
|
//
|
|
// the exception is when we're simulating turns for
|
|
// monsters on the destination level when a player
|
|
// walks up/down stairs. in this case, "enteringmap"
|
|
// will be set.
|
|
return;
|
|
}
|
|
if (db && cansee(player, lf)) {
|
|
dblog("lfid %d (%s) spending %d time\n",lf->id,lf->race->name, howlong);
|
|
}
|
|
}
|
|
|
|
assert(howlong > 0);
|
|
|
|
// inc timespent
|
|
lf->timespent += howlong;
|
|
assert(lf->timespent >= 0);
|
|
|
|
// time-based effects on lifeforms (eg hp regeneration) go here...
|
|
|
|
// TODO: decrement lifeform's (or their object's) temporary flags
|
|
|
|
|
|
// if you don't have a map, start forgetting the dungeon
|
|
if (isplayer(lf)) {
|
|
if (!lfhasflag(lf, F_PHOTOMEM) && (getskill(lf, SK_CARTOGRAPHY) < PR_SKILLED)) {
|
|
lf->forgettimer += ((float)howlong / 500.0);
|
|
if (lf->forgettimer > 1) {
|
|
int amt;
|
|
|
|
// TODO: modify using race memory
|
|
amt = (int)floor(lf->forgettimer);
|
|
forgetcells(lf->cell->map, amt);
|
|
lf->forgettimer -= amt;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now move player up in linked list...
|
|
sortlf(map, lf);
|
|
}
|
|
|
|
int throwat(lifeform_t *thrower, object_t *o, cell_t *where) {
|
|
if (!hasbp(thrower, BP_HANDS)) {
|
|
if (isplayer(thrower)) msg("You have no hands to throw with!");
|
|
return B_TRUE;
|
|
}
|
|
if (lfhasflag(thrower, F_RAGE)) {
|
|
if (isplayer(thrower)) {
|
|
msg("You are too enraged to throw anything!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
taketime(thrower, getactspeed(thrower));
|
|
return fireat(thrower, o, 1, where, getthrowspeed(thrower), NULL);
|
|
}
|
|
|
|
// lf effects which happen every xx ticks
|
|
// IMPORTANT - don't call taketime() during this function.
|
|
void timeeffectslf(lifeform_t *lf) {
|
|
object_t *o, *nexto;
|
|
flag_t *f,*nextf;
|
|
int dir;
|
|
|
|
// 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);
|
|
notime = B_FALSE;
|
|
return;
|
|
}
|
|
|
|
// revert to original form if a polymorph just expired
|
|
if (lf->polyrevert) {
|
|
enum RACE rid = R_NONE;
|
|
race_t *r;
|
|
flag_t *ff;
|
|
// change back
|
|
ff = lfhasflag(lf, F_ORIGRACE);
|
|
if (ff) {
|
|
rid = ff->val[0];
|
|
} else {
|
|
rid = R_NONE; // should never happen!
|
|
}
|
|
r = findrace(rid);
|
|
if (r) {
|
|
setrace(lf, r->id, B_TRUE);
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("For some reason, you are unable to revert to your original form!");
|
|
}
|
|
}
|
|
lf->polyrevert = B_FALSE;
|
|
}
|
|
|
|
|
|
// time effects on lifeform's objects
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
|
|
f = hasflag(o->flags, F_CREATEDBYSPELL);
|
|
if (f && !hasactivespell(lf, f->val[0])) {
|
|
killob(o);
|
|
continue;
|
|
}
|
|
|
|
timeeffectsob(o);
|
|
}
|
|
|
|
|
|
// holes in the floor/roof
|
|
for (dir = D_UP; dir <= D_DOWN; dir++) {
|
|
int donesomething = B_TRUE;
|
|
o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL);
|
|
while (o && donesomething) {
|
|
int willfall = B_FALSE;
|
|
donesomething = B_FALSE;
|
|
if ((dir == D_DOWN) && !isairborne(lf)) {
|
|
object_t *amu;
|
|
|
|
willfall = B_TRUE;
|
|
|
|
amu = hasequippedobid(lf->pack, OT_AMU_EVOLUTION);
|
|
if (amu) {
|
|
if (!polymorphto(lf, R_AVIAD, 5)) {
|
|
makeknown(amu->type->id);
|
|
dospelleffects(lf, OT_S_FLIGHT, 1, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE, NULL);
|
|
willfall = B_FALSE;
|
|
}
|
|
}
|
|
} else if ((dir == D_UP) && lfhasflag(lf, F_LEVITATING)) {
|
|
willfall = B_TRUE;
|
|
}
|
|
|
|
if (willfall) {
|
|
usestairs(lf, o, B_FALSE, B_FALSE);
|
|
taketime(lf, getactspeed(lf)); // to avoid infinte loops
|
|
donesomething = B_TRUE;
|
|
o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
notime = B_FALSE;
|
|
}
|
|
|
|
|
|
// returns TRUE if lf declines to teach.
|
|
int tradeknowledge(lifeform_t *lf) {
|
|
int poss[MAXCANDIDATES], fromplayer, toplayer;
|
|
enum TRADEINFOTYPE tradetype[MAXCANDIDATES],fromplayertype, toplayertype;
|
|
int nposs = 0,sel;
|
|
char buf[BUFLEN],tradetext[BUFLEN],lfname[BUFLEN],ch;
|
|
char fromplayertext[BUFLEN],toplayertext[BUFLEN];
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
// already traded?
|
|
if (lfhasflag(lf, F_DONEKNOWLEDGETRADE)) {
|
|
sayphrase(lf, SP_TRADEINFO_DECLINE_ALREADYDONE, SV_TALK, NA, NULL);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// does the player have a skill which lf needs?
|
|
getteachableskills(player, lf, poss, tradetype, &nposs);
|
|
if (nposs) {
|
|
sel = rnd(0,nposs-1);
|
|
fromplayer = poss[sel];
|
|
fromplayertype = tradetype[sel];
|
|
} else {
|
|
sayphrase(lf, SP_TRADEINFO_DECLINE_PLAYERBAD, SV_TALK, NA, NULL);
|
|
return B_TRUE;
|
|
}
|
|
// does lf have a skill which the player needs?
|
|
getteachableskills(lf, player, poss, tradetype, &nposs);
|
|
if (nposs) {
|
|
if (getskill(player, SK_SPEECH) >= PR_MASTER) {
|
|
int i;
|
|
// you can pick which one to learn.
|
|
snprintf(buf, BUFLEN, "What would you like to learn from %s?",lfname);
|
|
initprompt(&prompt, buf);
|
|
ch = 'a';
|
|
for (i = 0; i < nposs; i++) {
|
|
gettradeinfoname(poss[i], tradetype[i], buf);
|
|
addchoice(&prompt, ch++, buf, NULL, NULL, NULL);
|
|
}
|
|
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
|
|
ch = getchoice(&prompt);
|
|
if (ch == '-') {
|
|
sayphrase(lf, SP_TRADEINFO_CANCEL, SV_TALK, NA, NULL);
|
|
return B_TRUE;
|
|
} else {
|
|
sel = ch - 'a';
|
|
}
|
|
} else {
|
|
sel = rnd(0,nposs-1);
|
|
}
|
|
toplayer = poss[sel];
|
|
toplayertype = tradetype[sel];
|
|
} else {
|
|
sayphrase(lf, SP_TRADEINFO_DECLINE_OTHERBAD, SV_TALK, NA, NULL);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// at this point we can do a valid trade. make the offer.
|
|
// encode skill/spell names into a single string of the form:
|
|
// fromplayer^toplayer
|
|
gettradeinfoname(fromplayer, fromplayertype, fromplayertext);
|
|
gettradeinfoname(toplayer, toplayertype, toplayertext);
|
|
sprintf(tradetext, "%s^%s",fromplayertext,toplayertext);
|
|
|
|
sayphrase(lf, SP_TRADEINFO_ACCEPT, SV_TALK, NA, tradetext);
|
|
more();
|
|
// confirm.
|
|
sprintf(buf, "Learn %s from %s", toplayertext, lfname);
|
|
ch = askchar(buf, "yn","y", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
// lf learns skill
|
|
if (fromplayertype == TI_SKILL) {
|
|
giveskill(lf, fromplayer);
|
|
} else { // ie. spell
|
|
sprintf(buf, "pw:%d;", getspellpower(lf, fromplayer));
|
|
addflag(lf->flags, F_CANCAST, fromplayer, NA, NA, buf);
|
|
}
|
|
// player learns skill
|
|
if (toplayertype == TI_SKILL) {
|
|
giveskill(player, toplayer);
|
|
} else { // ie. spell
|
|
addflag(player->flags, F_CANCAST, toplayer, NA, NA, NULL);
|
|
}
|
|
// can't trade knowledge anymore
|
|
addflag(lf->flags, F_DONEKNOWLEDGETRADE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// return B_TRUE on failure.
|
|
int tryclimb(lifeform_t *lf, cell_t *where, char *towhat, int onpurpose) {
|
|
// if you have a rope or there's an adjacent wall, you can try
|
|
// to climb up
|
|
int adjwalls;
|
|
char lfname[BUFLEN];
|
|
|
|
// climbing without climb skill?
|
|
if (isplayer(lf) && onpurpose &&
|
|
!getskill(lf, SK_CLIMBING) &&
|
|
!lfhasflag(lf, F_SPIDERCLIMB) &&
|
|
!hasobwithflag(lf->pack, F_HELPSCLIMB)) {
|
|
// you are about to do something foolish!
|
|
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) {
|
|
if (!warnabout(TEXT_WARN_CLIMB)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
getlfname(lf, lfname);
|
|
|
|
adjwalls = countadjwalls(where);
|
|
if (adjwalls || hasobwithflag(lf->pack, F_HELPSCLIMB)) {
|
|
if (isplayer(lf)) {
|
|
msg("You start climbing...");
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s starts climbing...", lfname);
|
|
}
|
|
|
|
taketime(lf, getactspeed(lf));
|
|
|
|
// base difficulty of 20
|
|
if (skillcheck(lf, SC_CLIMB, 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
|
|
if (!lfhasflag(lf, F_SPIDERCLIMB)) {
|
|
practice(lf, SK_CLIMBING, 1);
|
|
}
|
|
// continue...
|
|
} else {
|
|
// you fall.
|
|
if (isplayer(lf)) {
|
|
msg("You fall to the ground!");
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s falls to the ground!", lfname);
|
|
}
|
|
fall(lf, NULL, B_FALSE); // this will take some time.
|
|
losehp(lf, roll("1d6"), DT_FALL, NULL, "a fall while climbing");
|
|
return B_TRUE;
|
|
}
|
|
} else { // no rope or adjacent walls
|
|
if (isplayer(lf)) {
|
|
if (onpurpose) {
|
|
msg("You can't reach the roof!");
|
|
} else {
|
|
// ie. you were fleeing.
|
|
msg("You try to climb upwards, but can't reach the roof!");
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// success
|
|
return B_FALSE;
|
|
}
|
|
|
|
int touch(lifeform_t *lf, object_t *o) {
|
|
return real_touch(lf, o, B_ONPURPOSE);
|
|
}
|
|
|
|
// returns B_TRUE if the action which involved touching this should fail
|
|
int real_touch(lifeform_t *lf, object_t *o, int onpurpose) {
|
|
flag_t *f;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
char buf[BUFLEN];
|
|
char obname[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
object_t *gloves;
|
|
|
|
if ((gamemode != GM_GAMESTARTED)) return B_FALSE;
|
|
|
|
touch_battle_spoils(o);
|
|
|
|
getlfname(lf, lfname);
|
|
getobname(o, obname, o->amt);
|
|
|
|
// freezing touch?
|
|
f = hasflag(lf->flags, F_FREEZINGTOUCH);
|
|
if (f) {
|
|
// not wearing gloves?
|
|
if (!getequippedob(lf->pack, BP_HANDS)) {
|
|
// default power of 4
|
|
dospelleffects(lf, OT_S_FREEZEOB, 4, NULL, o, NULL, B_UNCURSED, NULL, B_FALSE, NULL);
|
|
|
|
// we use val[0] here rather than timeleft, because we don't
|
|
// want to decrement it each turn.
|
|
f->val[0]--;
|
|
if (f->val[0] <= 0) {
|
|
if (isplayer(lf)) {
|
|
msg("Your hands stop glowing blue.");
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s's hands stop glowing blue.");
|
|
}
|
|
killflag(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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),
|
|
OBS1(o), OB1(o, "it", "them") ) ;
|
|
o->blessknown = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s touches %s then recoils in pain!",lfname, obname);
|
|
o->blessknown = B_TRUE;
|
|
}
|
|
// use real name here...
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
snprintf(buf, BUFLEN, "touching %s",obname);
|
|
losehp(lf, 2, DT_HOLY, NULL, buf);
|
|
// drop the object if we're holding it
|
|
if ((o->pile->owner == lf) && !isequipped(o)) {
|
|
drop(o, ALL);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isedible(o)) {
|
|
int willrot = B_FALSE;
|
|
if (isplayer(lf) && godprayedto(R_GODNATURE) && godisangry(R_GODNATURE) ) {
|
|
willrot = B_TRUE;
|
|
} else if (lf->race->id == R_WRAITHBOG) {
|
|
willrot = B_TRUE;
|
|
}
|
|
if (willrot) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("A green miasma surrounds %s as %s touch%s %s.", obname, lfname,
|
|
isplayer(lf) ? "" : "es",
|
|
(o->amt == 1) ? "it" : "them");
|
|
if (!hasflag(o->flags, F_TAINTED)) {
|
|
addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
gloves = getequippedob(lf->pack, BP_HANDS);
|
|
|
|
f = hasflag(o->flags, F_SHARP);
|
|
if (f) {
|
|
object_t *gloves;
|
|
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);
|
|
// drop the object if we're holding it
|
|
if ((o->pile->owner == lf) && !isequipped(o)) {
|
|
drop(o, ALL);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
if (isequipped(o) && !isweapon(o) && !isimmuneto(lf->flags, DT_FIRE, B_FALSE)) {
|
|
f = hasflag(o->flags, F_HOT);
|
|
if (f) {
|
|
f->known = B_TRUE;
|
|
if (isplayer(lf)) {
|
|
getobname(o, buf, 1);
|
|
msg("^BYour %s burns you!^n", noprefix(buf));
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
getobname(o, buf, 1);
|
|
msg("^B%s%s %s burns it!^n", lfname, getpossessive(lfname), noprefix(buf));
|
|
}
|
|
losehp_real(lf, f->val[0], DT_HEAT, NULL, buf, B_TRUE, o, B_FALSE);
|
|
}
|
|
}
|
|
*/
|
|
|
|
// flaming or red-hot objects?
|
|
if (!isimmuneto(lf->flags, DT_FIRE, B_FALSE)) {
|
|
int resistant = B_FALSE;
|
|
if (isresistantto(lf->flags, DT_FIRE, B_FALSE)) {
|
|
resistant = B_TRUE;
|
|
}
|
|
|
|
getflags(o->flags, retflag, &nretflags, F_ONFIRE, F_HOT, F_NONE); // IMPORTANT - check ONFIRE first!
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
// flaming weapons are ok - only the blade is burning
|
|
if ((f->id == F_ONFIRE) && isweapon(o)) {
|
|
} else if ((f->id == F_HOT) && resistant) {
|
|
// fire resistance will stop F_HOT, but not F_ONFIRE.
|
|
} else {
|
|
// touching something onfire while wearing gloves? they get damaged.
|
|
if (gloves && onpurpose) {
|
|
if (f->id == F_ONFIRE) { // fire will damage your gloves, just being hot will not.
|
|
takedamage(gloves, 2, DT_FIRE);
|
|
if (hasflag(gloves->flags, F_DEAD)) {
|
|
gloves = NULL;
|
|
} else {
|
|
// if your gloves weren't destroyed the fire
|
|
// will go out.
|
|
killflagsofid(o->flags, F_ONFIRE);
|
|
}
|
|
}
|
|
} else {
|
|
enum DAMTYPE dt;
|
|
int dam = 3;
|
|
// otherwise YOU get damaged.
|
|
f->known = B_TRUE;
|
|
getobname(o, obname, o->amt); // get name again after making flag known
|
|
if (isplayer(lf)) {
|
|
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);
|
|
if (f->id == F_ONFIRE) {
|
|
dt = DT_FIRE;
|
|
dam = rnd(2,4);
|
|
} else if (f->id == F_HOT) {
|
|
dt = DT_HEAT;
|
|
dam = f->val[0];
|
|
}
|
|
losehp(lf, dam, dt, NULL, buf);
|
|
// drop the object if it's an equipped weapon
|
|
if ((o->pile->owner == lf) && isequipped(o) && isweapon(o)) {
|
|
drop(o, ALL);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
} // end if !immune to fire
|
|
return B_FALSE;
|
|
}
|
|
|
|
void turntoface(lifeform_t *lf, cell_t *dstcell) {
|
|
if (isdead(lf)) return;
|
|
if (lfhasflag(lf, F_AUTOROTATE)) return;
|
|
// not providing srclf, since this will make getdirtowards() not include
|
|
// directions in which the next cell is unwalkable. in this case we're
|
|
// not actually walking there, so we don't care.
|
|
setfacing(lf, getdirtowards(lf->cell, dstcell, NULL, B_FALSE, DT_ORTH) );
|
|
}
|
|
|
|
void unequipeffects(lifeform_t *lf, object_t *o) {
|
|
flag_t *f;
|
|
// lose flags
|
|
loseobflags(lf, o, F_EQUIPCONFER);
|
|
|
|
if (obproduceslight(o)) {
|
|
calclight((getoblocation(o))->map);
|
|
setlosdirty(lf);
|
|
//precalclos(lf);
|
|
drawscreen();
|
|
}
|
|
|
|
if (o->type->id == OT_ENERGYBLADE) {
|
|
stopspell(lf, OT_S_SUMMONWEAPON);
|
|
// object might be dead now, so stop.
|
|
return;
|
|
}
|
|
|
|
f = hasflag(o->flags, F_CREATEDBYSPELL);
|
|
if (f) {
|
|
stopspell(lf, f->val[0]);
|
|
}
|
|
|
|
if ((o->type->id == OT_AMU_SWIMMING) && (lf->race->id == R_SWAN) && ispolymorphed(lf)) {
|
|
// revert to normal form
|
|
abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL);
|
|
}
|
|
|
|
if (o->type->id == OT_AMU_TRAVEL) {
|
|
map_t *newmap = NULL;
|
|
cell_t *newcell = NULL;
|
|
f = hasflag(o->flags, F_ORIGMAP);
|
|
if (f) {
|
|
// should always be true...
|
|
newmap = findmap(f->val[0]);
|
|
newcell = getcellat(newmap, f->val[1], f->val[2]);
|
|
}
|
|
|
|
if (newcell) {
|
|
// go there!
|
|
teleportto(lf, newcell, B_FALSE);
|
|
}
|
|
|
|
makeknown(o->type->id);
|
|
}
|
|
|
|
}
|
|
|
|
void unsummon(lifeform_t *lf, int vanishobs) {
|
|
lifeform_t *creator = NULL;
|
|
flag_t *f;
|
|
char unsummonob[BUFLEN];
|
|
f = hasflag(lf->flags, F_SUMMONEDBY);
|
|
if (f) {
|
|
creator = findlf(NULL, f->val[0]);
|
|
}
|
|
|
|
if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
int doyour = B_FALSE;
|
|
|
|
if (creator && (creator == player)) {
|
|
if (!hasflag(lf->flags, F_UNIQUE)) {
|
|
doyour = B_TRUE;
|
|
}
|
|
}
|
|
|
|
getlfname(lf, lfname);
|
|
msg("%s%s vanishes.", doyour ? "Your " : "",
|
|
doyour ? noprefix(lfname) : lfname);
|
|
}
|
|
|
|
if (vanishobs) {
|
|
// all objects vanish
|
|
while (lf->pack->first) {
|
|
killob(lf->pack->first);
|
|
}
|
|
}
|
|
|
|
lf->hp = 0;
|
|
addflag(lf->flags, F_DEAD, B_TRUE, NA, NA, NULL);
|
|
addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
|
|
addflag(lf->flags, F_NODEATHSPEECH, B_TRUE, NA, NA, NULL);
|
|
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
|
|
|
|
f = lfhasflag(lf, F_UNSUMMONOB);
|
|
if (f && strlen(f->text)) {
|
|
strcpy(unsummonob, f->text);
|
|
} else {
|
|
strcpy(unsummonob, "puff of smoke");
|
|
}
|
|
if (strlen(unsummonob)) {
|
|
addob(lf->cell->obpile, unsummonob);
|
|
}
|
|
}
|
|
|
|
int unweild(lifeform_t *lf, object_t *o) {
|
|
char obname[BUFLEN];
|
|
char buf[BUFLEN];
|
|
|
|
getobname(o, obname, 1);
|
|
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
if (isplayer(lf)) msg("You are too enraged to unweild a weapon!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!cantakeoff(lf, o)) {
|
|
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
|
|
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);
|
|
}
|
|
}
|
|
|
|
unequipeffects(lf, o);
|
|
|
|
// if you don't have a pack, it goes to the ground.
|
|
if (lfhasflag(lf, F_NOPACK)) {
|
|
moveob(o, lf->cell->obpile, o->amt);
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int useability(lifeform_t *lf, enum OBTYPE aid, lifeform_t *who, cell_t *where) {
|
|
int rv;
|
|
flag_t *cwflag;
|
|
float stamcost = 0;
|
|
if (!cancast(lf, aid, NULL)) {
|
|
if (isplayer(lf)) {
|
|
// announce
|
|
switch (reason) {
|
|
case E_NEEDGRAB:
|
|
msg("You need to hold someone before using this ability.");
|
|
break;
|
|
case E_NOTREADY:
|
|
msg("This ability is not recharged yet.");
|
|
break;
|
|
case E_NOSTAM:
|
|
msg("You are too tired to do that.");
|
|
break;
|
|
case E_PRONE:
|
|
msg("You can't use abilities while prone.");
|
|
break;
|
|
case E_STUNNED:
|
|
msg("You can't use abilities while stunned.");
|
|
break;
|
|
default:
|
|
msg("For some reason, you can't use this ability.");
|
|
break;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
stamcost = getstamcost(lf, aid);
|
|
if (stamcost) {
|
|
if (getstamina(lf) < stamcost) {
|
|
if (isplayer(lf)) {
|
|
msg("You are too tired to do that right now.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// taketime() will happen during abiltiyeffects()
|
|
// use the ability
|
|
|
|
cwflag = lfhasflagval(lf, F_CANWILL, aid, NA, NA, NULL);
|
|
rv = abilityeffects(lf, aid, where, who, cwflag);
|
|
|
|
if (!rv) {
|
|
objecttype_t *ot;
|
|
int stamcost = 0;
|
|
// success - charge stamina
|
|
ot = findot(aid);
|
|
stamcost = getstamcost(lf, aid);
|
|
if (stamcost) {
|
|
modstamina(lf, -stamcost);
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
int usestairs(lifeform_t *lf, object_t *o, int onpurpose, int climb) {
|
|
flag_t *f;
|
|
map_t *curmap;
|
|
map_t *newmap;
|
|
cell_t *obcell;
|
|
cell_t *newcell;
|
|
int dir;
|
|
int newdepth = 0; // should always be replaced
|
|
char lfname[BUFLEN];
|
|
char obname[BUFLEN];
|
|
int isportal = B_FALSE;
|
|
lifeform_t *adjally[MAXFOLLOWLFS];
|
|
int seen[MAXFOLLOWLFS];
|
|
int nadjallies = 0;
|
|
int falling = B_FALSE;
|
|
int madenewmap = B_FALSE;
|
|
region_t *newregion = NULL;
|
|
|
|
// need up update 'dlev:'
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
|
|
getlfname(lf, lfname);
|
|
getobname(o, obname, 1);
|
|
obcell = getoblocation(o);
|
|
|
|
if (initiatemove(lf, NULL, onpurpose, NULL)) {
|
|
// failed?
|
|
return B_FALSE;
|
|
}
|
|
|
|
// locked?
|
|
if (hasflag(o->flags, F_LOCKED)) {
|
|
if (isplayer(lf)) {
|
|
msg("The %s seems to be locked.", noprefix(obname));
|
|
if (o->type->id == OT_VSTAIRSDOWN) {
|
|
object_t *key;
|
|
key = hasobwithflag(lf->pack, F_VAULTKEY);
|
|
if (key) {
|
|
char ques[BUFLEN],obname[BUFLEN],ch;
|
|
getobname(key, obname, 1);
|
|
sprintf(ques,"Use your %s to open it?", noprefix(obname));
|
|
ch = askchar(ques,"yn","n", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
operate(lf, key, lf->cell);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (onpurpose) taketime(lf, getmovespeed(lf));
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (o->type->id == OT_PORTAL) {
|
|
f = hasflag(o->flags, F_MAPLINK);
|
|
if (f->val[0] != NA) {
|
|
map_t *m;
|
|
m = findmap(f->val[0]);
|
|
if (m && (m->habitat->id == H_HEAVEN)) {
|
|
if (!hasobofclass(lf->pack, OC_GODSTONE)) {
|
|
if (isplayer(lf)) msg("The portal doesn't seem to take you anywhere.");
|
|
if (onpurpose) taketime(lf, getmovespeed(lf));
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
curmap = obcell->map;
|
|
|
|
if ((o->type->id == OT_GRATINGFLOOR) && !hasflag(o->flags, F_MAPLINK)) {
|
|
createbranchlink(curmap, obcell, o, NULL, BH_SEWER, curmap->region);
|
|
}
|
|
|
|
f = hasflag(o->flags, F_CLIMBABLE);
|
|
assert(f);
|
|
dir = f->val[0];
|
|
if (f->val[1] == NA) {
|
|
// use same region
|
|
newregion = obcell->map->region;
|
|
} else {
|
|
newregion = findregion(f->val[1]);
|
|
}
|
|
|
|
// depth of new level?
|
|
if (dir == D_UP) {
|
|
newdepth = curmap->depth - 1;
|
|
isportal = B_FALSE;
|
|
} else if (dir == D_DOWN) {
|
|
newdepth = curmap->depth + 1;
|
|
isportal = B_FALSE;
|
|
} else {
|
|
// portal!
|
|
isportal = B_TRUE;
|
|
}
|
|
|
|
if (!onpurpose) {
|
|
falling = B_TRUE;
|
|
}
|
|
|
|
// check...
|
|
if (dir == D_DOWN) {
|
|
if (lfhasflag(lf, F_LEVITATING)) {
|
|
if (isplayer(lf)) {
|
|
msg("You can't reach the ground from up here!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
} else if (dir == D_UP) {
|
|
if (hasflagval(o->flags, F_PIT, D_UP, NA, NA, NULL) || (o->type->id == OT_GRATINGROOF)) {
|
|
// 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, onpurpose)) {
|
|
// failed
|
|
return B_TRUE;
|
|
} else {
|
|
// success
|
|
climb = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// announce
|
|
curs_set(1);
|
|
if (isportal) {
|
|
if (isplayer(lf)) {
|
|
msg("You enter the %s...", f->text);
|
|
// move cursor to msgwindow while we create the new level...
|
|
wrefresh(msgwin);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s enters the %s...", lfname, f->text);
|
|
}
|
|
} else if (hasflag(o->flags, F_PIT) || (o->type->id == OT_GRATINGFLOOR) || (o->type->id == OT_GRATINGROOF)) {
|
|
//f = hasflag(o->flags, F_PIT);
|
|
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
msg("%s %s %s the %s...", lfname, getpitverb(lf, dir,onpurpose, climb), getdirname(dir), noprefix(obname));
|
|
// move cursor to msgwindow while we create the new level...
|
|
if (isplayer(lf)) wrefresh(msgwin);
|
|
}
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("You %s %s the %s...", getmoveverb(lf), getdirname(dir), f->text);
|
|
// move cursor to msgwindow while we create the new level...
|
|
wrefresh(msgwin);
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s %s %s the %s.", lfname, getmoveverbother(lf), getdirname(dir), f->text);
|
|
}
|
|
}
|
|
|
|
// find adjacent allies or enemies which will follow you
|
|
// (getwhowillfollow will handle following through pits)
|
|
if (isplayer(lf)) {
|
|
int n;
|
|
getwhowillfollow(lf, o, adjally, seen, &nadjallies);
|
|
for (n = 0; n < nadjallies; n++) {
|
|
if (seen[n]) {
|
|
char lname[BUFLEN];
|
|
real_getlfname(adjally[n], lname, NULL, B_NOSHOWALL, B_CURRACE);
|
|
msg("%s follows you.", lname);
|
|
}
|
|
}
|
|
}
|
|
|
|
// do stairs go somewhere? generate new maps if required.
|
|
newcell = getstairdestination(o, &madenewmap);
|
|
if (newcell) {
|
|
newmap = newcell->map;
|
|
} else {
|
|
if (isportal) {
|
|
// unconnected portal - make a destination.
|
|
if (curmap->depth < MAXDEPTH) {
|
|
newdepth = rnd(curmap->depth, curmap->depth + 5);
|
|
limit(&newdepth, curmap->depth, MAXDEPTH);
|
|
linkportal(o, newdepth);
|
|
newcell = getstairdestination(o, &madenewmap);
|
|
}
|
|
if (!newcell) {
|
|
if (isplayer(lf)) msg("This portal doesn't seem to go anywhere.");
|
|
}
|
|
}
|
|
}
|
|
|
|
curs_set(0);
|
|
if (newcell) {
|
|
int n;
|
|
|
|
// if we just climbed up through a hole, and are not flying, we want to
|
|
// end up adjacent to the hole in the ground. otherwise we'll just fall
|
|
// straight back down!
|
|
if (hasflag(o->flags, F_PIT) && (dir == D_UP) && !isairborne(lf)) {
|
|
cell_t *noholecell;
|
|
noholecell = real_getrandomadjcell(newcell, WE_WALKABLE, B_ALLOWEXPAND, LOF_NEED, NULL, NULL, NULL );
|
|
if (noholecell) {
|
|
// go here instead
|
|
newcell = noholecell;
|
|
} else {
|
|
// alert
|
|
if (isplayer(lf)) {
|
|
msg("You can't find anywhere safe to get out.");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// check noone is in the way
|
|
if (movelfsoutofway(newcell) || !cellwalkable(lf, newcell, NULL)) {
|
|
// TODO: handle this differently - ie always allow the player
|
|
// go there?
|
|
if (isplayer(lf)) msg("The %s seems to be blocked.",f->text);
|
|
return B_TRUE;
|
|
}
|
|
// announce
|
|
if (isplayer(lf)) {
|
|
announcearrival(lf, newcell->map);
|
|
f = hasflag(o->flags, F_MAPLINK);
|
|
if (f) f->known = B_KNOWN;
|
|
}
|
|
// move player to new map
|
|
moveto(lf, newcell, onpurpose, B_TRUE);
|
|
|
|
// take time
|
|
if ((dir == D_UP) && !isairborne(lf)) {
|
|
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, B_TRUE, 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 == BH_PIT)) {
|
|
// you just dug downwards and made a big hole, so you
|
|
// didn't actually fall.
|
|
falling = B_FALSE;
|
|
}
|
|
|
|
if (falling) {
|
|
if (dir == D_DOWN) {
|
|
if (hasobwithflagval(lf->cell->obpile, F_PIT, D_DOWN, NA, NA, NULL)) {
|
|
flag_t *ff;
|
|
// inc fall distance
|
|
ff = lfhasflag(lf, F_FALLDISTANCE);
|
|
if (ff) {
|
|
ff->val[0]++;
|
|
} else {
|
|
addflag(lf->flags, F_FALLDISTANCE, 1, NA, NA, NULL);
|
|
}
|
|
} else {
|
|
int howfar;
|
|
if (isplayer(lf)) {
|
|
if (isimmuneto(lf->flags, DT_FALL, B_FALSE)) {
|
|
msg("You land gently on the ground.");
|
|
} else {
|
|
msg("^bYou slam into the ground!");
|
|
}
|
|
} else if (cansee(player, lf)){
|
|
if (isimmuneto(lf->flags, DT_FALL, B_FALSE)) {
|
|
msg("%s lands gently on the ground.", lfname);
|
|
} else {
|
|
msg("^%c%s slams into the ground!", getlfcol(lf, CC_BAD), lfname);
|
|
}
|
|
}
|
|
// how far did you fall?
|
|
sumflags(lf->flags, F_FALLDISTANCE, &howfar, NULL, NULL);
|
|
howfar++;
|
|
// take fall damage. 2d6 per level.
|
|
losehp(lf, rolldie(howfar*2, 6), DT_FALL, NULL, "falling");
|
|
killflagsofid(lf->flags, F_FALLDISTANCE);
|
|
if (!isimmuneto(lf->flags, DT_FALL, B_FALSE)) {
|
|
// injure legs
|
|
injure(lf, BP_LEGS, DT_BASH, IJ_NONE);
|
|
// fall over
|
|
fall(lf, NULL, B_FALSE);
|
|
}
|
|
}
|
|
} else if (dir == D_UP) {
|
|
if (hasobwithflagval(lf->cell->obpile, F_PIT, D_UP, NA, NA, NULL)) {
|
|
flag_t *ff;
|
|
// inc fall distance
|
|
ff = lfhasflag(lf, F_FALLDISTANCE);
|
|
if (ff) {
|
|
ff->val[0]++;
|
|
} else {
|
|
addflag(lf->flags, F_FALLDISTANCE, 1, NA, NA, NULL);
|
|
}
|
|
} else if (newcell->map->region->rtype->id != BH_WORLDMAP) {
|
|
int howfar;
|
|
if (isplayer(lf)) {
|
|
msg("^bYou slam into the roof!");
|
|
} else if (cansee(player, lf)){
|
|
msg("^%c%s slams into the roof!",getlfcol(lf, CC_BAD), lfname);
|
|
}
|
|
// how far did you fall?
|
|
sumflags(lf->flags, F_FALLDISTANCE, &howfar, NULL, NULL);
|
|
howfar++;
|
|
// take hitting roof damage (less than floor). 1d4 per level.
|
|
losehp(lf, rolldie(howfar, 4), DT_FALL, NULL, "slamming into the roof");
|
|
killflagsofid(lf->flags, F_FALLDISTANCE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
needredraw = B_TRUE;
|
|
calclight(lf->cell->map);
|
|
setlosdirty(lf);
|
|
//precalclos(lf);
|
|
drawscreen();
|
|
}
|
|
*/
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
// use a ring of miracles (or godstone of mercy) if we have one, and drain 'charges'
|
|
// charges from it. if expired, ring of miracles will vanish.
|
|
// returns B_TRUE if we found one.
|
|
int useringofmiracles(lifeform_t *lf, int charges) {
|
|
object_t *o;
|
|
char lfname[BUFLEN];
|
|
|
|
if (lf->lastdamtype == DT_DIRECT) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
getlfname(lf, lfname);
|
|
for (o = lf->pack->first ; o ; o = o->next) {
|
|
int doit = B_FALSE;
|
|
// use ring of miracles first
|
|
if ( (o->type->id == OT_RING_MIRACLES) && isequipped(o) && getcharges(o) ) {
|
|
doit = B_TRUE;
|
|
} else if ((o->type->id == OT_GODSTONE_MERCY) && isfullycharged(o)) {
|
|
doit = B_TRUE;
|
|
}
|
|
if (doit) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, 1);
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
if (o->type->id == OT_RING_MIRACLES) {
|
|
msg("%s%s %s flashes!", lfname, getpossessive(lfname), noprefix(obname));
|
|
} else {
|
|
msg("%s flashes!", obname);
|
|
}
|
|
}
|
|
// you know know the obejct
|
|
makeknown(o->type->id);
|
|
getobname(o, obname, 1); // refresh its name in case you didnt know what it was.
|
|
|
|
// use a charge
|
|
if (o->type->id == OT_RING_MIRACLES) {
|
|
if (usecharge(o) <= 0) {
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
msg("%s%s %s crumbles to dust.", lfname, getpossessive(lfname), noprefix(obname));
|
|
}
|
|
removeob(o, ALL);
|
|
}
|
|
} else { // ie. godstone
|
|
usecharges(o, getcharges(o));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int validateraces(void) {
|
|
int goterror = B_FALSE;
|
|
race_t *r;
|
|
flag_t *f;
|
|
skill_t *sk;
|
|
job_t *j;
|
|
int i;
|
|
cell_t fakecell;
|
|
map_t fakemap;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
// generate xp list
|
|
genxplist();
|
|
|
|
createfakes(&fakemap, &fakecell);
|
|
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
lifeform_t *lf;
|
|
int thishd;
|
|
enum ATTRIB a;
|
|
|
|
// add a fake lf
|
|
lf = addlf(&fakecell, r->id, 1);
|
|
givestartskills(lf, lf->flags);
|
|
|
|
// remember max. hitdice for use in dumpmonsters()
|
|
thishd = gettr(lf);
|
|
if (thishd > maxmonhitdice) {
|
|
maxmonhitdice = thishd;
|
|
}
|
|
|
|
// all races must have fully defined stats
|
|
for (a = 0 ; a < MAXATTS; a++) {
|
|
if (!hasflagval(r->flags, F_STARTATT, a, NA, NA, NULL)) {
|
|
printf("ERROR in race '%s' - race has no f_startatt %s\n", r->name, getattrname(a));
|
|
goterror = B_TRUE;
|
|
}
|
|
}
|
|
// duplicate startatt flags?
|
|
if (countflagsofid(r->flags, F_STARTATT) != MAXATTS) {
|
|
printf("ERROR in race '%s' - duplicate f_startatt flags detected.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
|
|
if (strstr(r->desc, "wings") && !hasbp(lf, BP_WINGS)) {
|
|
printf("ERROR in race '%s' - description refers to wings but race has no bp_wings.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
|
|
|
|
if (!hasflag(r->flags, F_SIZE)) {
|
|
printf("ERROR in race '%s' - missing F_SIZE.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
if (!hasflag(r->flags, F_TR)) {
|
|
printf("ERROR in race '%s' - missing F_TR (threat level).\n", r->name);
|
|
goterror = B_TRUE;
|
|
|
|
}
|
|
|
|
if (hasflag(r->flags, F_CANCAST) && !hasflag(r->flags, F_CASTWITHOUTIQ)) {
|
|
if (getattr(lf, A_IQ) <= IQ_ANIMAL) {
|
|
printf("ERROR in race '%s' - has F_CANCAST but iq is too low. might need f_castwithoutiq?\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (hasflag(r->flags, F_NOCORPSE) && hasflag(r->flags, F_CORPSETYPE)) {
|
|
printf("ERROR in race '%s' - has both F_NOCORPSE and F_CORPSETYPE.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
|
|
for (f = r->flags->first ; f ; f = f->next) {
|
|
if (f->id == F_RNDSPELLCOUNT) {
|
|
if (!hasflag(r->flags, F_RNDSPELLSCHOOL) && !hasflag(r->flags, F_RNDSPELLPOSS)) {
|
|
printf("ERROR in race '%s' - F_RNDSPELLCOUNT but no spell candidates.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
}
|
|
if (f->id == F_FILLPOT) {
|
|
flag_t *f2;
|
|
if (!findot(f->val[0])) {
|
|
printf("ERROR in race '%s' - F_FILLPOT with bad object.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
f2 = hasflag(r->flags, F_BLOODOB);
|
|
if (f2 && !strlen(f2->text)) {
|
|
printf("ERROR in race '%s' - has F_FILLPOT but no bleed object.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
}
|
|
if (f->id == F_HASATTACK) {
|
|
if (!findot(f->val[0])) {
|
|
printf("ERROR in race '%s' - F_HASATTACK with bad object.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
if (f->val[1] == NA) {
|
|
printf("ERROR in race '%s' - F_HASATTACK with no DR\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_FLYING) {
|
|
if (!hasflagval(r->flags, F_CANWILL, OT_S_FLIGHT, NA, NA, NULL)) {
|
|
printf("ERROR in race '%s' - has F_FLYING but can't use FLIGHT ability.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_NOFLEE) {
|
|
if (lfhasflag(lf, F_FLEEONHPPCT)) {
|
|
printf("ERROR in race '%s' - has both F_NOFLEE and F_FLEEONHPPCT.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
if (lfhasflag(lf, F_FLEEONDAM)) {
|
|
printf("ERROR in race '%s' - has both F_NOFLEE and F_FLEEONDAM.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_STARTOB) {
|
|
if (!f->text || (strlen(f->text) == 0)) {
|
|
printf("ERROR in race '%s' - F_STARTOB with zero length text.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_MAXATTACKS) {
|
|
for (i = 0; i <= 1; i++) {
|
|
if (f->val[i] < 0) {
|
|
printf("ERROR in race '%s' - F_MAXATTACKS with v%d < 0.\n", r->name, i);
|
|
goterror = B_TRUE;
|
|
}
|
|
}
|
|
} else if (f->id == F_HITCONFER) {
|
|
if (!lfhasflag(lf, F_HITCONFERVALS)) {
|
|
printf("ERROR in race '%s' - F_HITCONFER, but no HITCONFERVALS defined.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_STARTATT) {
|
|
if (strlen(f->text) && (f->val[1] != NA)) {
|
|
printf("ERROR in race '%s' - F_STARTATT has both text range and val1.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_NOISETEXT) {
|
|
if (f->val[0] == N_FLY) {
|
|
if (!hasflag(r->flags, F_NATURALFLIGHT) && !hasflag(r->flags, F_LEVITATING)) {
|
|
printf("ERROR in race '%s' - has NOISETEXT N_FLY but isn't flying.\n", r->name);
|
|
goterror = B_TRUE;
|
|
|
|
}
|
|
}
|
|
if (f->val[1] == NA) {
|
|
printf("ERROR in race '%s' - has NOISETEXT but no volume.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_NOSLEEP) {
|
|
if (hasflag(r->flags, F_NOCTURNAL) || hasflag(r->flags, F_DIURNAL) || hasflag(r->flags, F_STARTASLEEPPCT)) {
|
|
printf("ERROR in race '%s' - has both NOSSLEEP and nocturnal/diurnal/startasleeppct.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_NOSMELL) {
|
|
if (hasflag(r->flags, F_ENHANCESMELL)) {
|
|
printf("ERROR in race '%s' - has both NOSMELL and ENHANCESMELL.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
} else if (f->id == F_STARTOBWEPSK) {
|
|
if (!findskill(f->val[1])) {
|
|
printf("ERROR in race '%s' - STARTOBWEPSK with invalid weapon skill.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
}
|
|
} // end foreach flag
|
|
|
|
|
|
getflags(lf->flags, retflag, &nretflags, F_MORALE, F_NONE);
|
|
if (nretflags > 1) {
|
|
printf("ERROR in race '%s' - has multiple F_MORALE flags.\n", r->name);
|
|
goterror = B_TRUE;
|
|
}
|
|
|
|
// xp check...
|
|
calcxp(lf);
|
|
|
|
// remove a fake lf
|
|
killlf(lf);
|
|
|
|
}
|
|
|
|
|
|
i = 0;
|
|
for (sk = firstskill ; sk ; sk = sk->next) {
|
|
i++;
|
|
}
|
|
if (i >= MAXSKILLS) {
|
|
printf("ERROR - MAXSKILLS is %d but found %d skills.\n",MAXSKILLS,i);
|
|
goterror = B_TRUE;
|
|
}
|
|
for (j = firstjob ; j ; j = j->next) {
|
|
f = hasflag(j->flags, F_HASPET);
|
|
if (f) {
|
|
race_t *r;
|
|
r = findracebyname(f->text);
|
|
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;
|
|
}
|
|
|
|
// returns TRUE on error
|
|
int resizelf(lifeform_t *lf, enum LFSIZE newsize, int doobs) {
|
|
flag_t *f;
|
|
enum LFSIZE origsize,racesize = SZ_ANY;
|
|
int changedir;
|
|
char lfname[BUFLEN];
|
|
flag_t *reverting = NULL;
|
|
int origstr,origmaxhp;
|
|
object_t *o,*nexto;
|
|
getlfname(lf, lfname);
|
|
|
|
// already resized?
|
|
reverting = lfhasflag(lf, F_SIZETIMER);
|
|
if (reverting) {
|
|
char buf[BUFLEN], *p;
|
|
// override size.
|
|
newsize = reverting->val[0];
|
|
doobs = reverting->val[2];
|
|
// will revert str/hp
|
|
p = readuntil(buf, reverting->text, ',');
|
|
origstr = atoi(buf);
|
|
readuntil(buf, p, ',');
|
|
origmaxhp = atoi(buf);
|
|
}
|
|
|
|
f = hasflag(lf->race->flags, F_SIZE);
|
|
if (f) {
|
|
racesize = f->val[0];
|
|
}
|
|
|
|
|
|
origsize = getlfsize(lf);
|
|
|
|
if (origsize == newsize) {
|
|
return B_TRUE;
|
|
} else if (newsize > origsize) {
|
|
changedir = 1;
|
|
} else {
|
|
changedir = -1;
|
|
}
|
|
|
|
// actually do the resize
|
|
f = lfhasflag(lf, F_SIZE);
|
|
if (f) {
|
|
f->val[0] = newsize;
|
|
} else {
|
|
addflag(lf->flags, F_SIZE, newsize, NA, NA, NULL);
|
|
}
|
|
|
|
// announce
|
|
if (racesize == newsize) {
|
|
if (isplayer(lf)) {
|
|
msg("Your body returns to its regular size."); more();
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s returns to its regular size.", lfname);
|
|
}
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("Your body %s unnaturally!", (changedir == 1) ? "grows" : "shrinks"); more();
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s %s unnaturally!", lfname, (changedir == 1) ? "grows" : "shrinks");
|
|
}
|
|
}
|
|
|
|
// effects on objects, armour etc
|
|
for (o = lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
|
|
if (isequipped(o)) {
|
|
// object is now the wrong size?
|
|
if (isarmour(o) && !armourfits(lf, o, NULL)) {
|
|
if (doobs) {
|
|
resizeobject(o, newsize);
|
|
} else {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
if (isplayer(lf)) {
|
|
msg("Your %s no longer fits!", noprefix(obname));
|
|
} else if (cansee(player, lf)) {
|
|
msg("%s%s %s no longer fits!", lfname, getpossessive(lfname), noprefix(obname));
|
|
}
|
|
killflagsofid(o->flags, F_EQUIPPED);
|
|
unequipeffects(lf, o);
|
|
if (isplayer(lf)) statdirty = B_TRUE; // might have impacted AR
|
|
}
|
|
}
|
|
|
|
if (isweapon(o)) {
|
|
flag_t *f;
|
|
// wasn't twohanded before, but is now?
|
|
if (istwohandedfor(o, lf)) {
|
|
f = hasflag(o->flags, F_TWOHANDED);
|
|
if ((f->val[0] > 0) && (origsize > f->val[0])) {
|
|
if (isplayer(lf)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("Your %s %s now too heavy to weild in one hand!", noprefix(obname),
|
|
(o->amt == 1) ? "is" : "are");
|
|
}
|
|
// just drop it - this avoids complication if you have something in your
|
|
// other hand.
|
|
drop(o, o->amt);
|
|
continue;
|
|
}
|
|
} else {
|
|
// was twohanded before, but isn't now?
|
|
f = hasflag(o->flags, F_TWOHANDED);
|
|
if (f && ((f->val[0] < 0) || (origsize <= f->val[0]))) {
|
|
flag_t *f2;
|
|
if (isplayer(lf)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("You can now weild your %s in one hand!", noprefix(obname));
|
|
}
|
|
f2 = hasflagval(o->flags, F_EQUIPPED, BP_SECWEAPON, NA, NA, NULL);
|
|
if (f2) killflag(f2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// object is now too big to hold?
|
|
if (!canpickup(lf, o, o->amt)) {
|
|
if (reason == E_TOOBIG) {
|
|
drop(o, o->amt);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (reverting) {
|
|
// restore str etc.
|
|
setattr(lf, A_STR, origstr);
|
|
lf->maxhp = origmaxhp;
|
|
limit(&(lf->hp), 0, lf->maxhp);
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
|
|
killflag(reverting);
|
|
}
|
|
|
|
// resizing an lf might change its glyph...
|
|
if (cansee(player, lf)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
lifeform_t *ressurect(object_t *o) {
|
|
flag_t *f;
|
|
race_t *r;
|
|
lifeform_t *lf;
|
|
cell_t *where;
|
|
int level = 1;
|
|
char obname[BUFLEN];
|
|
|
|
if (o->type->id != OT_CORPSE) return NULL;
|
|
if (hasflag(o->flags, F_HEADLESS)) return NULL;
|
|
|
|
f = hasflag(o->flags, F_CORPSEOF);
|
|
if (f) {
|
|
level = f->val[1];
|
|
} else {
|
|
return NULL;
|
|
}
|
|
|
|
r = findrace(f->val[0]);
|
|
if (!r) return NULL;
|
|
|
|
where = getoblocation(o);
|
|
getobname(o, obname, 1);
|
|
|
|
if (!cellwalkable(NULL, where, NULL)) {
|
|
where = getrandomadjcell(where, WE_WALKABLE, B_ALLOWEXPAND);
|
|
}
|
|
if (!where) return NULL;
|
|
|
|
lf = addlf(where, r->id, level);
|
|
|
|
// remove the corpse object
|
|
removeob(o,o->amt);
|
|
|
|
// redraw & announce
|
|
if (haslos(player, where)) {
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
msg("%s is restored to life!", obname);
|
|
}
|
|
|
|
return lf;
|
|
}
|
|
|
|
int rest(lifeform_t *lf, int onpurpose) {
|
|
flag_t *f;
|
|
flag_t *ff;
|
|
int healtime = 3;
|
|
int wantclearmsg = B_TRUE;
|
|
int mpheal = 1;
|
|
int hpheal = 1;
|
|
flag_t *rf;
|
|
int training = B_FALSE;
|
|
int resting = B_FALSE;
|
|
object_t *restob = NULL;
|
|
|
|
// special case
|
|
if ((lf->race->id == R_GASCLOUD) && lfhasflagval(lf, F_ORIGRACE, R_VAMPIRE, NA, NA, NULL)) {
|
|
if (hasob(lf->cell->obpile, OT_COFFIN)) {
|
|
// restore original form.
|
|
abilityeffects(lf, OT_A_POLYREVERT, lf->cell, lf, NULL);
|
|
// restore full hp
|
|
lf->hp = lf->maxhp;
|
|
// fall asleep for a while
|
|
fallasleep(lf, ST_ASLEEP, 50);
|
|
// mark screen as dirty
|
|
needredraw = B_TRUE;
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
rf = lfhasflag(lf, F_TRAINING);
|
|
if (rf) {
|
|
training = B_TRUE;
|
|
} else {
|
|
rf = isresting(lf);
|
|
if (rf) {
|
|
resting = B_TRUE;
|
|
// ie. resting via 'R'
|
|
restob = getrestob(lf);
|
|
}
|
|
}
|
|
|
|
taketime(lf, getactspeed(lf));
|
|
|
|
if (!lfhasflag(lf, F_POISONED)) {
|
|
// slowly heal hp/mp
|
|
if (!training) {
|
|
ff = lfhasflag(lf, F_RESTHEALAMT);
|
|
if (ff) {
|
|
hpheal = ff->val[0];
|
|
} else {
|
|
hpheal = 1;
|
|
}
|
|
|
|
ff = lfhasflag(lf, F_RESTHEALMPAMT);
|
|
if (ff) {
|
|
mpheal = ff->val[0];
|
|
} else {
|
|
mpheal = 1;
|
|
}
|
|
|
|
if (onpurpose || !isplayer(lf)) {
|
|
f = lfhasflag(lf, F_RESTCOUNT);
|
|
if (!f) {
|
|
f = addflag(lf->flags, F_RESTCOUNT, 0, NA, NA, NULL);
|
|
}
|
|
f->val[0]++;
|
|
f->lifetime = 2;
|
|
|
|
ff = lfhasflag(lf, F_RESTHEALTIME);
|
|
if (ff) {
|
|
healtime = ff->val[0];
|
|
} else {
|
|
// 3 turns = heal 1 hp
|
|
healtime = DEF_RESTHEALTIME;
|
|
}
|
|
|
|
// modify via restob if resting using 'R'
|
|
if (restob) {
|
|
if (rf->val[1] != NA) {
|
|
healtime -= rf->val[1];
|
|
limit(&healtime, 1, NA);
|
|
}
|
|
}
|
|
if (isplayer(lf) && godprayedto(R_GODLIFE) && godisangry(R_GODLIFE)) {
|
|
enum PIETYLEV plev;
|
|
plev = getpietylev(R_GODLIFE, NULL, NULL);
|
|
if (plev <= PL_FURIOUS) {
|
|
healtime = 0; // ie. never heal!
|
|
} else {
|
|
healtime *= 2;
|
|
}
|
|
}
|
|
|
|
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 very easily
|
|
difficulty = 5;
|
|
}
|
|
|
|
// modify difficulty if you're resting properly via 'R'
|
|
if (resting && restob) {
|
|
flag_t *f;
|
|
f = hasflag(restob->flags, F_HELPSREST);
|
|
if (f) {
|
|
difficulty -= f->val[0];
|
|
limit(&difficulty, 0, NA);
|
|
}
|
|
}
|
|
|
|
//if (isplayer(lf)) msg("hp given.");
|
|
if (lf->hp < lf->maxhp) {
|
|
// pass a skill check to regain hp
|
|
if (skillcheck(lf, SC_CON, difficulty, getskill(lf, SK_FIRSTAID))) {
|
|
gainhp(lf, hpheal);
|
|
if (isplayer(lf) && godprayedto(R_GODLIFE)) {
|
|
pleasegodmaybe(R_GODLIFE, 1);
|
|
}
|
|
practice(lf, SK_FIRSTAID, 1);
|
|
}
|
|
}
|
|
|
|
if (lf->mp < getmaxmp(lf)) {
|
|
// pass a skill check to regain mp
|
|
if (skillcheck(lf, SC_IQ, difficulty, (lf->level/2))) {
|
|
gainmp(lf, mpheal);
|
|
}
|
|
}
|
|
|
|
killflag(f);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (training) {
|
|
wantclearmsg = B_FALSE;
|
|
rf->val[0]++;
|
|
if (rf->val[0] >= rf->val[1]) {
|
|
// ask about gaining skills
|
|
if (isplayer(lf)) {
|
|
msg("You finish training.");
|
|
more();
|
|
}
|
|
killflag(rf); // kill resting/training flag.
|
|
|
|
enhanceskills(lf);
|
|
}
|
|
} else if (resting) {
|
|
// just asleep/resting
|
|
flag_t *hf;
|
|
int fullpartyrest = B_FALSE;
|
|
|
|
wantclearmsg = B_FALSE;
|
|
// resting
|
|
if (isfullyhealed(lf)) {
|
|
killflagsofid(lf->flags, F_RESTUNTILBETTER);
|
|
}
|
|
|
|
hf = lfhasflag(lf, F_RESTUNTILALLIES);
|
|
if (hf) {
|
|
int moretogo = B_FALSE;
|
|
lifeform_t *l;
|
|
|
|
fullpartyrest = B_TRUE;
|
|
|
|
for (l = lf->cell->map->lf ; l ; l = l->next) {
|
|
if ((l != lf) && areallies(l, lf)) {
|
|
if (!isfullyhealed(l)) {
|
|
moretogo = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!moretogo) {
|
|
killflag(hf);
|
|
}
|
|
}
|
|
|
|
if (!lfhasflag(lf, F_RESTUNTILBETTER) && !lfhasflag(lf, F_RESTUNTILALLIES)) {
|
|
if (isplayer(lf)) {
|
|
if (fullpartyrest) {
|
|
msg("Your party has finished resting.");
|
|
} else {
|
|
msg("You finish resting.");
|
|
}
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s finishes resting.",lfname);
|
|
}
|
|
if (isplayer(lf)) statdirty = B_TRUE;
|
|
|
|
killflagsofid(lf->flags, F_TRAINING);
|
|
rf = isresting(lf);
|
|
if (rf) {
|
|
// since you've woken up normally, turn your light source back on.
|
|
if (strlen(rf->text)) {
|
|
object_t *light;
|
|
light = hasobid(lf->pack, atol(rf->text));
|
|
if (light) {
|
|
turnon(lf, light);
|
|
}
|
|
}
|
|
// kill sleeping flag
|
|
killflag(rf);
|
|
}
|
|
wantclearmsg = B_FALSE;
|
|
}
|
|
}
|
|
// allies should wake up once healed
|
|
if (areallies(player, lf) && !isplayer(lf)) {
|
|
if ((lf->hp >= lf->maxhp) && (lf->mp >= getmaxmp(lf)) && (getstamina(lf) >= getmaxstamina(lf))) {
|
|
if (!hashealableinjuries(lf)) {
|
|
killflagsofid(lf->flags, F_ASLEEP);
|
|
}
|
|
}
|
|
}
|
|
} // end if !poisoned
|
|
|
|
if (isresting(lf)) {
|
|
f = hasflag(lf->flags, F_RESTINGINMOTEL);
|
|
if (f) {
|
|
f->val[1]++;
|
|
if (f->val[1] >= f->val[0]) {
|
|
killflag(f);
|
|
stopresting(lf);
|
|
msg("\"Your check-out time has arrived!\"");
|
|
wantclearmsg = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (statdirty || needredraw) {
|
|
drawscreen();
|
|
}
|
|
|
|
if (onpurpose && wantclearmsg) {
|
|
if (isplayer(lf)) {
|
|
// clear msg bar
|
|
clearmsg();
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int wear(lifeform_t *lf, object_t *o) {
|
|
int rv = B_FALSE,i;
|
|
char buf[BUFLEN],obname[BUFLEN];
|
|
flag_t *f;
|
|
enum BODYPART possbp[MAXBODYPARTS];
|
|
int nparts = 0,ncheckparts = 0;
|
|
enum BODYPART bp;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
int showpos = B_FALSE;
|
|
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
if (isplayer(lf)) msg("You are too enraged!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (o->amt > 1) {
|
|
// eg. for melted wax in your ears
|
|
o = splitob(o);
|
|
}
|
|
|
|
getobname(o, obname, 1);
|
|
|
|
// check if it is already equipped first!
|
|
f = hasflag(o->flags, F_EQUIPPED);
|
|
if (f) {
|
|
if (isplayer(lf)) {
|
|
if (f->val[0] == BP_WEAPON) {
|
|
msg("You're weilding that!");
|
|
} else {
|
|
msg("You're already wearing that!");
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// metal objects and magshield?
|
|
if (lfhasflag(lf, F_MAGSHIELD)) {
|
|
if (isplayer(lf)) {
|
|
msg("^wYour %s evades your grasp!", noprefix(obname));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
nparts = 0;
|
|
|
|
getflags(o->flags, retflag, &nretflags, F_GOESON, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_GOESON) {
|
|
possbp[nparts] = f->val[0];
|
|
nparts++;
|
|
}
|
|
}
|
|
|
|
if (nparts == 0) {
|
|
if (isplayer(lf)) {
|
|
msg("You can't wear that!");
|
|
}
|
|
return B_TRUE;
|
|
} else if (nparts == 1) {
|
|
// go on the only needed body part
|
|
ncheckparts = nparts;
|
|
} else { // multiple possible locations
|
|
int i;
|
|
|
|
showpos = B_TRUE;
|
|
|
|
|
|
if (hasflag(o->flags, F_GOESONMULTI)) {
|
|
// goes on ALL of the parts
|
|
ncheckparts = nparts;
|
|
} else {
|
|
// rings go in first possible place
|
|
if (o->type->obclass->id == OC_RING) {
|
|
bp = BP_NONE;
|
|
for (i = 0; i < nparts; i++) {
|
|
if (isfreebp(lf, possbp[i])) {
|
|
bp = possbp[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bp == BP_NONE) {
|
|
if (isplayer(lf)) msg("You have no room to wear that.");
|
|
return B_TRUE;
|
|
}
|
|
possbp[0] = bp;
|
|
nparts = 1;
|
|
ncheckparts = nparts;
|
|
|
|
} else {
|
|
char ch = 'a';
|
|
// ask where to put it
|
|
snprintf(buf, BUFLEN, "Where will you wear %s?",obname);
|
|
initprompt(&prompt, buf);
|
|
for (i = 0; i < nparts; i++) {
|
|
object_t *inway;
|
|
inway = 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);
|
|
|
|
if (prompt.nchoices == 1) {
|
|
if (isplayer(lf)) msg("You have nowhere to wear that!");
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(lf)) {
|
|
ch = getchoice(&prompt);
|
|
|
|
if (ch == '-') {
|
|
bp = BP_NONE;
|
|
if (isplayer(lf)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else bp = *((enum BODYPART *)prompt.result);
|
|
} else {
|
|
bp = *((enum BODYPART *)prompt.choice[rnd(0,prompt.nchoices - 1)].data);
|
|
}
|
|
|
|
possbp[0] = bp;
|
|
nparts = 1;
|
|
ncheckparts = nparts;
|
|
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
|
|
// make sure all required parts are free...
|
|
for (i = 0; i < ncheckparts; i++) {
|
|
bp = possbp[i];
|
|
while (!canwear(lf, o, bp)) {
|
|
object_t *inway = NULL;
|
|
int errresolved = B_FALSE;
|
|
if ((gamemode == GM_GAMESTARTED) && lf->created) {
|
|
switch (reason) {
|
|
case E_ALREADYUSING:
|
|
if (isplayer(lf)) msg("You're already wearing that!");
|
|
break;
|
|
case E_DOESNTFIT:
|
|
if (isplayer(lf)) msg("The unnatural shape of your %s prevents this from fitting.", getbodypartname(lf, bp));
|
|
break;
|
|
case E_PALADIN:
|
|
if (isplayer(lf)) msg("Your vows prevent you from wearing non-blessed armour.");
|
|
break;
|
|
case E_INJURED:
|
|
if (isplayer(lf)) msg("Your injuries prevent you from wearing this.");
|
|
break;
|
|
case E_WEARINGSOMETHINGELSE:
|
|
// find what else is there
|
|
inway = getequippedob(lf->pack, bp);
|
|
getobname(inway,buf, 1);
|
|
if (isplayer(lf)) {
|
|
int ch = '\0';
|
|
char buf2[BUFLEN];
|
|
if (inway->blessknown && iscursed(inway)) {
|
|
msg("You cannot remove your %s.", noprefix(buf));
|
|
} else {
|
|
// take offending item off first - this takes extra time.
|
|
snprintf(buf2, BUFLEN, "Remove your %s",noprefix(buf));
|
|
while (!ch) {
|
|
ch = askchar(buf2, "ynd?","y", B_TRUE, B_FALSE);
|
|
if (ch == '?') {
|
|
describeob(o);
|
|
ch = '\0';
|
|
}
|
|
}
|
|
if ((ch == 'y') || (ch == 'd')) {
|
|
if (isarmour(inway)) {
|
|
if (!takeoff(player, inway)) {
|
|
// took it off!
|
|
errresolved = B_TRUE;
|
|
}
|
|
} else { // weapon
|
|
if (!unweild(player, inway)) {
|
|
// took it off!
|
|
errresolved = B_TRUE;
|
|
}
|
|
}
|
|
if (errresolved && (ch == 'd')) {
|
|
drop(inway, inway->amt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case E_NOTKNOWN:
|
|
if (isplayer(lf)) msg("You can't wear that!"); // same message as wearing non-armour
|
|
break;
|
|
case E_NOBP:
|
|
if (isplayer(lf)) msg("You have no %s on which to wear that!", getbodypartname(lf, bp));
|
|
break;
|
|
case E_LOWCHA:
|
|
msg("You are not attractive enough to wear this.");
|
|
break;
|
|
case E_LOWCON:
|
|
msg("You are not healthy enough to wear this.");
|
|
break;
|
|
case E_LOWDEX:
|
|
msg("You are not dextrous enough to wear this.");
|
|
break;
|
|
case E_LOWIQ:
|
|
msg("You are not smart enough to wear this.");
|
|
break;
|
|
case E_LOWSTR:
|
|
msg("You are not strong enough to wear this.");
|
|
break;
|
|
case E_LOWWIS:
|
|
msg("You are not wise enough to wear this.");
|
|
break;
|
|
case E_TOOSMALL:
|
|
if (isplayer(lf)) msg("This armour is too small for you.");
|
|
break;
|
|
case E_TOOBIG:
|
|
if (isplayer(lf)) msg("This armour is too big for you.");
|
|
break;
|
|
default:
|
|
if (isplayer(lf)) {
|
|
msg("You can't wear that!");
|
|
}
|
|
break;
|
|
}
|
|
} // end if gamestarted
|
|
if (!errresolved) {
|
|
return B_TRUE;
|
|
}
|
|
// if we DID resolve the error, loop around and check if we can wear again...
|
|
} // end while !canwear
|
|
} // end for each required bodypart
|
|
|
|
// at this point, we're going to try to wear it...
|
|
killflagsofid(lf->flags, F_HIDING);
|
|
|
|
// some checks first...
|
|
if (touch(lf, o)) {
|
|
taketime(lf, getactspeed(lf));
|
|
return B_TRUE;
|
|
}
|
|
|
|
// probably don't need this now...
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
taketime(lf, getactspeed(lf));
|
|
return B_TRUE;
|
|
}
|
|
|
|
// actually wear it
|
|
for (i = 0; i < ncheckparts; i++) {
|
|
addflag(o->flags, F_EQUIPPED, possbp[i], -1, -1, NULL);
|
|
}
|
|
taketime(lf, getactspeed(lf));
|
|
|
|
|
|
if (isplayer(lf)) maketried(o->type->id, NULL);
|
|
|
|
// when you wear armour, you find out
|
|
// its bonuses.
|
|
if (isplayer(lf) && (o->type->obclass->id == OC_ARMOUR)) {
|
|
f = hasflag(o->flags, F_BONUS);
|
|
if (f && !f->known) {
|
|
f->known = B_TRUE;
|
|
getobname(o, obname, 1);
|
|
}
|
|
}
|
|
|
|
if ((gamemode == GM_GAMESTARTED) && lf->created) {
|
|
if (isplayer(lf)) {
|
|
// announce
|
|
if (showpos) {
|
|
char posbuf[BUFLEN];
|
|
makewearstring(lf, o, B_TRUE, posbuf );
|
|
msg("You are now wearing %s (%s).", obname, posbuf);
|
|
} else {
|
|
msg("You are now wearing %s.", obname);
|
|
}
|
|
} else if (cansee(player, lf)) {
|
|
char verb[BUFLEN];
|
|
getlfname(lf, buf);
|
|
capitalise(buf);
|
|
switch (rnd(1,2)) {
|
|
case 1: strcpy(verb, "puts on"); break;
|
|
case 2: strcpy(verb, "dons"); break;
|
|
}
|
|
msg("%s %s %s.", buf, verb, obname);
|
|
|
|
}
|
|
|
|
if (o->blessed == B_CURSED) {
|
|
if (isplayer(lf)) {
|
|
msg("^bOh no! The %s releases a pulse of evil!", noprefix(obname));
|
|
o->blessknown = B_TRUE;
|
|
} else if (cansee(player, lf)) {
|
|
getlfname(lf, buf);
|
|
msg("%s%s %s releases a pulse of evil!", buf, getpossessive(buf), obname);
|
|
o->blessknown = B_TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// warn if it will be cumbersome
|
|
if (isplayer(lf)) {
|
|
char howmuch[BUFLEN];
|
|
f = hasflagval(o->flags, F_EQUIPCONFER, F_SHIELDPENALTY, NA, NA, NULL);
|
|
if (f) {
|
|
int penalty;
|
|
penalty = adjustshieldpenalty(lf, f->val[1]);
|
|
if (penalty) {
|
|
if (penalty >= 40) {
|
|
strcpy(howmuch, "incredibly");
|
|
} else if (penalty >= 30) {
|
|
strcpy(howmuch, "extremely");
|
|
} else if (penalty >= 20) {
|
|
strcpy(howmuch, "very");
|
|
} else if (penalty >= 10) {
|
|
strcpy(howmuch, "quite");
|
|
} else {
|
|
strcpy(howmuch, "slightly");
|
|
}
|
|
msg("^wYou find this shield %s cumbersome to use.", howmuch);
|
|
}
|
|
}
|
|
}
|
|
|
|
// special cases:
|
|
if (isplayer(lf)) {
|
|
if (o->type->id == OT_RING_INVIS) {
|
|
// make ring of invis fully known - the HPDRAIN flag
|
|
// won't be announced, so since we don't know all the flags we would
|
|
// otherwise get "you turn invisible!" but still have the ring known
|
|
// as "a blue ring" (or whatever)
|
|
makeknown(o->type->id);
|
|
}
|
|
}
|
|
|
|
if (o->type->id == OT_AMU_TRAVEL) {
|
|
if (isplayer(lf) || cansee(player, lf)) {
|
|
makeknown(o->type->id);
|
|
}
|
|
} else if (o->type->id == OT_AMU_CHOKING) {
|
|
if (isplayer(lf)) {
|
|
msg("^%c%s starts to constrict around your neck!", getlfcol(lf, CC_VBAD), obname);
|
|
makeknown(o->type->id);
|
|
if (!needstobreath(lf)) {
|
|
msg("Luckily, you don't need to breath.");
|
|
}
|
|
}
|
|
}
|
|
|
|
// give flags
|
|
giveobflags(lf, o, F_EQUIPCONFER);
|
|
|
|
// special case...
|
|
if (o->type->id == OT_AMU_TRAVEL) {
|
|
map_t *newmap = NULL;
|
|
cell_t *newcell = NULL;
|
|
f = hasflag(o->flags, F_NEWMAP);
|
|
if (f) {
|
|
newmap = findmap(f->val[0]);
|
|
newcell = getcellat(newmap, f->val[1], f->val[2]);
|
|
} else {
|
|
int mindepth,maxdepth,newdepth;
|
|
region_t *newregion;
|
|
// TODO: add other planes here. they must be from branchs which ALWAYS
|
|
// exist (ie. which are created at the start of the game).
|
|
switch (rnd(0,1)) {
|
|
case 0:
|
|
case 1:
|
|
newregion = findregionbytype(BH_MAINDUNGEON);
|
|
break;
|
|
}
|
|
// pick a new map at least 10 levels below
|
|
maxdepth = newregion->rtype->maxdepth;
|
|
|
|
mindepth = lf->cell->map->depth + 10;
|
|
if (mindepth >= maxdepth) {
|
|
newdepth = maxdepth;
|
|
} else {
|
|
newdepth = rnd(mindepth, maxdepth);
|
|
}
|
|
newmap = findregionmap(newregion->id, newdepth);
|
|
if (!newmap) {
|
|
// create new map
|
|
newmap = addmap();
|
|
createmap(newmap, newdepth, newregion, NULL, D_NONE, NULL);
|
|
}
|
|
// find random cell
|
|
newcell = getrandomcell(newmap);
|
|
while (!cellwalkable(NULL, newcell, NULL) || celldangerous(lf, newcell, B_FALSE, NULL)) {
|
|
newcell = getrandomcell(newmap);
|
|
}
|
|
}
|
|
// remember orig cell
|
|
killflagsofid(o->flags, F_ORIGMAP);
|
|
addflag(o->flags, F_ORIGMAP, lf->cell->map->id, lf->cell->x, lf->cell->y, NULL);
|
|
|
|
// go there!
|
|
teleportto(lf, newcell, B_FALSE);
|
|
|
|
makeknown(o->type->id);
|
|
}
|
|
|
|
if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) {
|
|
// set statdirty since this might have impacted your AR / EV
|
|
statdirty = B_TRUE;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int weild(lifeform_t *lf, object_t *o) {
|
|
char buf[BUFLEN];
|
|
flag_t *f;
|
|
object_t *oo;
|
|
int twohanded = B_FALSE;
|
|
enum BODYPART weildloc,otherloc;
|
|
|
|
// this might impact your AR
|
|
if (isplayer(lf)) {
|
|
statdirty = B_TRUE;
|
|
}
|
|
if (lfhasflag(lf, F_RAGE)) {
|
|
if (isplayer(lf)) msg("You are too enraged to weild a weapon!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (o) {
|
|
getobname(o, buf, o->amt);
|
|
killflagsofid(o->flags, F_SECONDARY);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "nothing");
|
|
}
|
|
|
|
if (!canweild(lf, o)) {
|
|
if ((gamemode == GM_GAMESTARTED) && lf->created) {
|
|
if (isplayer(lf)) {
|
|
switch (reason) {
|
|
case E_ALREADYUSING:
|
|
msg("You are already weilding that!");
|
|
break;
|
|
case E_NOUNARMEDATTACK:
|
|
msg("You cannot fight without a weapon!");
|
|
break;
|
|
case E_IMPOSSIBLE:
|
|
msg("You cannot weild weapons!");
|
|
break;
|
|
case E_PALADIN:
|
|
if (isplayer(lf)) msg("Your vows prevent you from weilding non-blessed weapons.");
|
|
break;
|
|
case E_NOHANDS:
|
|
msg("You do not have enough free hands to weild this weapon.");
|
|
break;
|
|
case E_NOTKNOWN:
|
|
if (isplayer(lf)) msg("You can't weild that!"); // same message as wearing non-armour
|
|
break;
|
|
case E_LOWCHA:
|
|
msg("You are not attractive enough to use this weapon.");
|
|
break;
|
|
case E_LOWCON:
|
|
msg("You are not healthy enough to use this weapon.");
|
|
break;
|
|
case E_LOWDEX:
|
|
msg("You are not dextrous enough to use this weapon.");
|
|
break;
|
|
case E_LOWIQ:
|
|
msg("You are not smart enough to use this weapon.");
|
|
break;
|
|
case E_LOWSTR:
|
|
msg("You are not strong enough to use this weapon.");
|
|
break;
|
|
case E_LOWWIS:
|
|
msg("You are not wise enough to use this weapon.");
|
|
break;
|
|
case E_INJURED:
|
|
msg("Your injuries prevent you from using this weapon.");
|
|
break;
|
|
default:
|
|
msg("For some reason, you weild this!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// anything else weilded?
|
|
weildloc = getweildloc(o, lf, &otherloc, &twohanded);
|
|
|
|
// firearm in regular hand?
|
|
// note: this is the same logic as in canweild()
|
|
if (o && !hasbp(lf, weildloc) && isfirearm(o) && !getequippedob(lf->pack, otherloc)) {
|
|
int temp;
|
|
// firearm can go in other hand.
|
|
// swap locations.
|
|
temp = weildloc;
|
|
weildloc = otherloc;
|
|
otherloc = temp;
|
|
}
|
|
|
|
|
|
// metal objects and magshield?
|
|
if (lfhasflag(lf, F_MAGSHIELD)) {
|
|
if (isplayer(lf)) {
|
|
msg("^wYour %s evades your grasp!", noprefix(buf));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// if we are skilled at twoweaponing, and somehting is in the way,
|
|
// ask if we want to use our other hand.
|
|
if (!twohanded && hasbp(lf, otherloc)) {
|
|
oo = getequippedob(lf->pack, weildloc);
|
|
if (getskill(lf, SK_TWOWEAPON) && oo && !istwohandedfor(oo, lf)) {
|
|
char ch;
|
|
if (isplayer(lf)) {
|
|
char buf2[BUFLEN];
|
|
snprintf(buf2, BUFLEN, "Weild %s in your left hand?",buf);
|
|
ch = askchar(buf2, "yn","y", B_TRUE, B_FALSE);
|
|
} else {
|
|
if (getweaponskill(lf, o)) ch = 'y';
|
|
else ch = 'n';
|
|
}
|
|
if (ch == 'y') {
|
|
// make sure we are skilled in 2nd weapon
|
|
// (weilding null (nothing) there is always okay too.
|
|
if (!o || getweaponskill(lf, o)) {
|
|
enum BODYPART temp;
|
|
// swap locations.
|
|
temp = weildloc;
|
|
weildloc = otherloc;
|
|
otherloc = temp;
|
|
} else {
|
|
msg("You are not skilled enough to use this weapon in your left hand.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// any other objects in the way?
|
|
for (oo = lf->pack->first ; oo ; oo = oo->next) {
|
|
f = hasflagval(oo->flags, F_EQUIPPED, weildloc, -1, -1, NULL);
|
|
if (f) {
|
|
if (isarmour(oo)) {
|
|
char inwayname[BUFLEN];
|
|
char buf2[BUFLEN];
|
|
char ch = '\0';
|
|
if (isplayer(lf)) {
|
|
if (oo->blessknown && iscursed(oo)) {
|
|
msg("You cannot remove your %s.", noprefix(inwayname));
|
|
ch = 'n';
|
|
} else {
|
|
getobname(oo, inwayname, oo->amt);
|
|
// prompt before taking it off.
|
|
snprintf(buf2, BUFLEN, "Remove your %s",noprefix(inwayname));
|
|
while (!ch) {
|
|
ch = askchar(buf2, "ynd?","y", B_TRUE, B_FALSE);
|
|
if (ch == '?') {
|
|
describeob(o);
|
|
ch = '\0';
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ch = 'y';
|
|
}
|
|
if ((ch == 'y') || (ch == 'd')) {
|
|
if (isarmour(oo)) {
|
|
if (takeoff(lf, oo)) {
|
|
// if we can't remove it, stop.
|
|
return B_TRUE;
|
|
}
|
|
} else { // weapon
|
|
if (unweild(lf, oo)) {
|
|
// if we can't remove it, stop.
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
if (ch == 'd') {
|
|
drop(oo, oo->amt);
|
|
}
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
// 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, B_FALSE);
|
|
}
|
|
} 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 (istwohandedfor(o, lf)) {
|
|
addflag(o->flags, F_EQUIPPED, otherloc, -1, -1, NULL);
|
|
}
|
|
|
|
taketime(lf, getactspeed(lf));
|
|
|
|
if (isplayer(lf) && isweapon(o)) maketried(o->type->id, NULL);
|
|
|
|
if ((gamemode == GM_GAMESTARTED) && lf->created && (lf->race->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->flags);
|
|
if (sk && !getskill(lf, sk->id)) {
|
|
msg("^wYou feel rather inept with this weapon.");
|
|
} else {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i;
|
|
// warn about not meeting attrib reqs
|
|
getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
int pctmod;
|
|
meetsattreq(lf, retflag[i], o, &pctmod);
|
|
if (pctmod < 0) {
|
|
msg("^wYou low %s will lower your %s with this weapon.",
|
|
getattrname(retflag[i]->val[0]),
|
|
(retflag[i]->val[0] == A_AGI) ? "accuracy" : "damage");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (cansee(player, lf)) {
|
|
if (lf->race->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_ARMOURIGNORE);
|
|
if (f) {
|
|
msg("^gYour %s seems unnaturally sharp!",noprefix(buf));
|
|
f->known = B_TRUE;
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int willbackstab(lifeform_t *lf, lifeform_t *victim, object_t *wep) {
|
|
if (getraceclass(victim) == RC_PLANT) return B_FALSE;
|
|
|
|
if (wep && (getdamtype(wep) == DT_PIERCE) && // stabbing weapon
|
|
getskill(lf, SK_BACKSTAB) && // able to backstab
|
|
// !lfhasflagval(victim, F_STABBEDBY, lf->id, NA, NA, NULL) && // haven't stabbed them already
|
|
ishelplessvictim(victim, lf, NULL)) { // victim can't see us or is fleeing
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int willbleedfrom(lifeform_t *lf, enum BODYPART bp) {
|
|
object_t *o;
|
|
o = getequippedob(lf->pack, bp);
|
|
if (o && (o->type->id == OT_BANDAGE)) {
|
|
// don't bleed.
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (hasequippedobid(lf->pack, OT_AMU_BLOOD)) return B_FALSE;
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
// if lf picks up 'o', will they be burdened afterwards?
|
|
// (if they are _already_ burdened, then stil return true)
|
|
int willburden(lifeform_t *lf, object_t *o, int howmany) {
|
|
if (getobpileweight(lf->pack) + (getobunitweight(o)*howmany) > getmaxcarryweight(lf) ) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
int willeatlf(lifeform_t *eater, lifeform_t *eatee) {
|
|
if (isplayer(eater)) return B_FALSE;
|
|
if (eater == eatee) return B_FALSE; // no self-cannibulism!
|
|
|
|
// doesn't want to eat
|
|
if (!lfhasflagval(eater, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL)) {
|
|
return B_FALSE;
|
|
}
|
|
// does eater eat eatee's material?
|
|
if ((eatee->material->id == MT_FLESH) && lfhasflag(eater, F_CARNIVORE)) {
|
|
return B_TRUE;
|
|
}
|
|
if ((eatee->material->id == MT_PLANT) && lfhasflag(eater, F_VEGETARIAN)) {
|
|
return B_TRUE;
|
|
}
|
|
if (eater->race->id == R_BINGEBARK) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|