nexus/lf.c

8503 lines
220 KiB
C

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ai.h"
#include "attack.h"
#include "defs.h"
#include "flag.h"
#include "io.h"
#include "lf.h"
#include "map.h"
#include "move.h"
#include "nexus.h"
#include "objects.h"
#include "spell.h"
#include "text.h"
extern map_t *firstmap;
extern race_t *firstrace, *lastrace;
extern job_t *firstjob, *lastjob;
extern skill_t *firstskill, *lastskill;
extern objecttype_t *objecttype;
extern lifeform_t *player;
extern int needredraw;
extern prompt_t prompt;
extern long nextlfid;
extern WINDOW *msgwin;
extern WINDOW *statwin;
extern int statdirty;
extern int needredraw;
extern int gamestarted;
extern int loading;
extern enum ERROR reason;
job_t *addjob(enum JOB id, char *name) {
job_t *a;
// add to the end of the list
if (firstjob == NULL) {
firstjob = malloc(sizeof(job_t));
a = firstjob;
a->prev = NULL;
} else {
// go to end of list
a = lastjob;
a->next = malloc(sizeof(job_t));
a->next->prev = a;
a = a->next;
}
lastjob = a;
a->next = NULL;
// props
a->id = id;
a->name = strdup(name);
a->flags = addflagpile(NULL, NULL);
return a;
}
lifeform_t *addlf(cell_t *cell, enum RACE rid, int level) {
return real_addlf(cell, rid, level, C_AI);
}
lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) {
map_t *m;
lifeform_t *a;
int i;
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->xp = getxpforlev(a->level);
a->skillpoints = 0;
a->cell = cell;
a->alive = B_TRUE;
a->lastdam = strdup("nothing");
a->lastdamtype = DT_NONE;
if (gamestarted && a->prev) {
a->timespent = a->prev->timespent + 1;
} else {
a->timespent = 0;
}
a->sorted = B_FALSE;
a->forgettimer = 0;
a->polyrevert = B_FALSE;
// for precalcing line of sight
a->nlos = 0;
a->los = NULL;
// for ai
a->ignorecell = NULL;
// avoid messages when equipping initial obs
a->created = B_FALSE;
a->pack = addobpile(a, NOLOC);
// clear laoding variables
for (i = 0; i < MAXPILEOBS; i++) {
a->oblist[i] = -1;
}
a->x = -1;
a->y = -1;
// init flags
a->flags = addflagpile(a, NULL);
// set race - this will inherit race flags
a->race = NULL;
setrace(a, rid);
// update other things
cell->lf = a;
// give start objetcs
if (!loading) {
outfitlf(a);
}
a->created = B_TRUE;
a->born = B_TRUE; // now finished creating it.
return a;
}
race_t *addrace(enum RACE id, char *name, float weight, char glyph, enum MATERIAL mat) {
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->material = findmaterial(mat);
assert(a->material);
a->name = strdup(name);
a->weight = weight;
a->glyph = glyph;
a->flags = addflagpile(NULL, NULL);
return a;
}
skill_t *addskill(enum SKILL id, char *name, char *desc) {
skill_t *a;
assert(!findskill(id));
// add to the end of the list
if (firstskill == NULL) {
firstskill = malloc(sizeof(skill_t));
a = firstskill;
a->prev = NULL;
} else {
// go to end of list
a = lastskill;
a->next = malloc(sizeof(skill_t));
a->next->prev = a;
a = a->next;
}
lastskill = a;
a->next = NULL;
// props
a->id = id;
a->name = strdup(name);
a->desc = strdup(desc);
return a;
}
void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype) {
flag_t *f;
if (isimmuneto(lf->flags, damtype)) {
*amt = 0;
return;
}
if ((damtype == DT_MAGIC) && hasmr(lf)) {
*amt = 0;
return;
}
if (lfhasflag(lf, F_INVULNERABLE)) {
switch (damtype) {
case DT_DIRECT:
case DT_NONE:
break;
default:
*amt = 0;
break;
}
return;
}
if ((damtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) {
(*amt) *= 2;
}
if (isresistantto(lf->flags, damtype)) {
(*amt) /= 2;
}
f = isvulnto(lf->flags, damtype);
if (f) {
if ((*amt == 0) && strlen(f->text)) {
int ndice,nsides,bonus;
texttodice(f->text, &ndice,&nsides,&bonus);
*amt = rolldie(ndice,nsides) + bonus;
} else {
(*amt) *= 2;
}
}
// adjust for lifeform material
//adjustdammaterial(amt, damtype, getlfmaterial(lf));
if (*amt < 0) *amt = 0;
}
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);
lf = addlf(where, r->id, 1);
addflag(lf->flags, F_LFSUFFIX, B_TRUE, NA, NA, "zombie");
addflag(lf->flags, F_GLYPH, NA, NA, NA, "Z");
addflag(lf->flags, F_UNDEAD, B_TRUE, NA, NA, NULL);
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lf->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL);
addflag(lf->flags, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL);
addflag(lf->flags, F_DTIMMUNE, DT_DECAY, NA, NA, NULL);
addflag(lf->flags, F_DTVULN, DT_HOLY, NA, NA, NULL);
if (hasflag(o->flags, F_HEADLESS)) {
// remove the head
addflag(lf->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL);
// remove the eyes (this will make the creature blind)
addflag(lf->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL);
// need HEADLESS too to show that this monster normally
// _does_ have a head. this will cause getlfname
// to add "headless " to the description.
addflag(lf->flags, F_HEADLESS, B_TRUE, NA, NA, NULL);
}
killflagsofid(lf->flags, F_WANTSBETTERWEP);
killflagsofid(lf->flags, F_WANTSBETTERARM);
killflagsofid(lf->flags, F_WANTSOBFLAG);
killflagsofid(lf->flags, F_WANTS);
killflagsofid(lf->flags, F_NOISETEXT);
killflagsofid(lf->flags, F_SEEINDARK);
killflagsofid(lf->flags, F_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] = rolliq(IQ_MINDLESS);
lf->att[A_IQ] = lf->baseatt[A_IQ];
// no magic
lf->maxmp = 0;
lf->mp = 0;
// no objects
while (lf->pack->first) {
killob(lf->pack->first);
}
// no abilities
killflagsofid(lf->flags, F_CANCAST);
killflagsofid(lf->flags, F_CANWILL);
// remove the object
removeob(o,o->amt);
// redraw & announce
if (haslos(player, where)) {
needredraw = B_TRUE;
drawscreen();
msg("%s rises from the dead!", obname);
}
return lf;
}
void autotarget(lifeform_t *lf) {
object_t *gun;
lifeform_t *targ,*newtarg;
int closest;
int i;
int gunrange;
int targid;
gun = getfirearm(lf);
if (!gun) return;
if (!getammo(lf)) return;
gunrange = getfirearmrange(gun);
// already got a target?
targid = getguntargetid(lf);
if (targid == -1) {
targ = NULL;
} else {
targ = findlf(NULL, targid);
if (targ) {
// dead? remove target and keep going.
if (isdead(targ)) {
// clear target ?
targ = NULL;
} else if (!haslof(lf, targ->cell)) {
// clear target ?
targ = NULL;
} else {
// already got a valid target. return.
return;
}
} else {
// target no longer exists?
// clear target ?
}
}
// find new target
newtarg = NULL;
closest = 9999;
for (i = 0; i < lf->nlos; i++) {
cell_t *c;
c = lf->los[i];
if (c->lf && (c->lf != lf) && isingunrange(lf, c)) {
int valid = B_TRUE;
if (isplayer(lf)) {
if (isfriendly(c->lf) || ispeaceful(c->lf)) {
valid = B_FALSE;
}
}
if (valid) {
int thisdist;
thisdist = getcelldist(lf->cell, c);
if (thisdist < closest) {
newtarg = c->lf;
closest = thisdist;
}
}
}
}
if (newtarg && (newtarg != targ)) {
setguntarget(lf, newtarg);
}
}
void autoweild(lifeform_t *lf) {
object_t *bestwep,*bestfirearm;
object_t *o;
int pretimespent;
pretimespent = lf->timespent;
// weild weapons if required
bestwep = getbestweapon(lf);
if (bestwep) {
weild(lf, bestwep);
}
bestfirearm = getbestfirearm(lf);
if (bestfirearm) {
weild(lf, bestfirearm);
}
// weild armour if required
for (o = lf->pack->first ; o ; o = o->next) {
if (canwear(lf, o, BP_NONE)) {
wear(lf, o);
} else {
if (isplayer(lf) && (o->type->id == OT_RING_PROTFIRE)) {
dblog("zz");
}
}
}
// start using ammo if required
//if (isplayer(lf)) {
if (getfirearm(lf)) {
for (o = lf->pack->first ; o ; o = o->next) {
testammo(lf, o);
if (getammo(lf)) break;
}
}
//}
// 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 bleed(lifeform_t *lf) {
flag_t *f;
char obname[BUFLEN];
f = lfhasflag(lf, F_BLOODOB);
if (f) {
if (f->text) {
strcpy(obname, f->text);
} else {
strcpy(obname, "");
}
} else {
strcpy(obname, "splash of blood");
}
if (strlen(obname) > 0) {
addob(lf->cell->obpile, obname);
}
}
// figure out how much xp a race is worth
int calcxp(lifeform_t *lf) {
float xpval = 0;
float offense = 0;
float defence = 0;
object_t *o;
obpile_t *op;
flag_t *f;
float avgdam = 0;
float acc;
int db = B_FALSE;
int maxhdroll;
float xpconstant = 1;
if (db) dblog("calcxp: calculating xpval for %s",lf->race->name);
// attack
// - get average attack damage
op = addobpile(NULL, NULL);
avgdam = 0;
for (f = lf->flags->first ; f ; f = f->next) {
if (f->id == F_HASATTACK) {
int min,max;
float thisavg;
getdamrangeunarmed(f, &min,&max);
thisavg = ((float)min + (float)max) / 2.0;
if (db) {
char obname[BUFLEN];
getobname(o,obname,1);
if (db) dblog("calcxp: %s: == %d-%d dam, avg is %0.1f",obname, min, max, thisavg);
}
if (thisavg > avgdam) {
if (db) dblog("calcxp: this is best so far.");
avgdam = thisavg;
}
}
}
if (op) killobpile(op);
if (db) dblog("calcxp: avg damage dealt is %0.1f",avgdam);
// -- avg damage in 10 turns
avgdam *= 10;
// -- modify with accuracy
acc = getlfaccuracy(lf);
offense = pctof(acc,avgdam);
if (db) dblog("calcxp: ATTACKVAL IS %0.1f",offense);
// defense
// -- hitdice
f = lfhasflag(lf, F_HITDICE);
if (f) {
maxhdroll = f->val[0] * 4;
if (f->val[1] != NA) maxhdroll += f->val[1];
} else {
maxhdroll = 4;
}
defence = maxhdroll * lf->level;
defence /= 2;
// -- evasion ?
if (db) dblog("calcxp: DEFENCE IS %0.1f",defence);
xpval = offense + defence;
f = lfhasflag(lf, F_XPMOD);
if (f) {
xpval += f->val[0];
if (db) dblog("calcxp: F_XPMOD is %d",f->val[0]);
}
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;
lifeform_t *lf;
// make a fake cell
setcelltype(&c, CT_CORRIDOR);
c.lf = NULL;
c.map = firstmap;
// make a fake lf
lf = addlf(&c, rid, 1);
xpval = calcxp(lf);
killlf(lf);
return xpval;
}
int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
int castable = B_FALSE;
flag_t *f;
// TODO: check for mute?
reason = E_OK;
f = lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL);
if (f) {
// 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;
}
} else if (lfhasflagval(lf, F_CANCAST, oid, NA, NA, NULL)) {
int cost,power;
// 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?
cost = getmpcost(oid);
if (mpcost) *mpcost = cost;
if (lf->mp >= cost) {
castable = B_TRUE;
} else {
reason = E_NOMP;
return B_FALSE;
}
}
return castable;
}
int candrink(lifeform_t *lf, object_t *o) {
if (!isdrinkable(o)) {
return B_FALSE;
}
return B_TRUE;
}
// TODO: reason = xx ?
int caneat(lifeform_t *lf, object_t *o) {
if (!isedible(o)) {
return B_FALSE;
}
return B_TRUE;
}
int canhear(lifeform_t *lf, cell_t *c) {
int hrange;
hrange = gethearingrange(lf);
if (getcelldist(lf->cell, c) <= hrange) {
return B_TRUE;
}
return B_FALSE;
}
//
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 canpickup(lifeform_t *lf, object_t *o, int amt) {
reason = E_OK;
if (amt == ALL) {
amt = o->amt;
}
if (hasflag(o->flags, F_NOPICKUP)) {
reason = E_NOPICKUP;
return B_FALSE;
}
if (isimpassableob(o, lf)) {
reason = E_TOOBIG;
return B_FALSE;
}
if (lfhasflag(lf, F_NOPACK)) {
reason = E_NOPACK;
return B_FALSE;
}
if (lfhasflag(lf, F_GRAVBOOSTED)) {
reason = E_GRAVBOOSTED;
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 (countobs(lf->pack) >= MAXPILEOBS) {
reason = E_NOSPACE;
return B_FALSE;
}
if (getnextletter(lf->pack, NULL) == '\0') {
reason = E_NOSPACE;
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) {
reason = E_INSUBSTANTIAL;
return B_FALSE;
}
// check lf weight
if (getobweight(o) > getmaxpushweight(lf)) {
reason = E_TOOHEAVY;
return B_FALSE;
}
// check cell behind object
obcell = o->pile->where;
if (!obcell) {
reason = E_FAILED;
return B_FALSE;
}
dstcell = getcellindir(obcell, dir);
if (!cellwalkable(NULL, dstcell, NULL)) {
reason = E_FAILED;
return B_FALSE;
}
return B_TRUE;
}
int canquaff(lifeform_t *lf, object_t *o) {
if (!isdrinkable(o)) {
reason = E_WRONGOBTYPE;
return B_FALSE;
}
if (getlfmaterial(lf) == MT_GAS) {
reason = E_INSUBSTANTIAL;
return B_FALSE;
}
return B_TRUE;
}
// safe to rest?
int canrest(lifeform_t *lf) {
lifeform_t *l;
reason = E_OK;
if (isplayer(lf)) {
for (l = lf->cell->map->lf ; l ; l = l->next) {
if ((l != lf) && cansee(lf, l) && !isfriendly(l)) {
reason = E_MONSTERNEARBY;
return B_FALSE;
}
}
} else if (isfriendly(lf)) { // ally
for (l = lf->cell->map->lf ; l ; l = l->next) {
if ((l != lf) && cansee(lf, l) && !isplayer(l)) {
reason = E_MONSTERNEARBY;
return B_FALSE;
}
}
} else { // monster
for (l = lf->cell->map->lf ; l ; l = l->next) {
if ((l != lf) && cansee(lf, l) &&
(isplayer(l) || isfriendly(l)) ) {
reason = E_MONSTERNEARBY;
return B_FALSE;
}
}
}
return B_TRUE;
}
int cansee(lifeform_t *viewer, lifeform_t *viewee) {
// no line of sight?
if (!haslos(viewer, viewee->cell)) {
return B_FALSE;
}
// viewee is invisible?
if (lfhasflag(viewee, F_INVISIBLE)) {
if (!lfhasflag(viewer, F_SEEINVIS)) {
return B_FALSE;
}
}
return B_TRUE;
}
int canwear(lifeform_t *lf, object_t *o, enum BODYPART where) {
object_t *oo;
flag_t *f;
reason = E_OK;
// already equipped?
if (hasflag(o->flags, F_EQUIPPED)) {
reason = E_ALREADYUSING;
return B_FALSE;
}
// wearable at all?
if (!iswearable(o)) {
reason = E_IMPOSSIBLE;
return B_FALSE;
}
if (gettechlevel(o) > getskill(lf, SK_TECHUSAGE)) {
reason = E_NOTKNOWN;
return B_FALSE;
}
if (where == BP_NONE) {
// can we wear it ANYWHERE?
enum BODYPART possbp[MAXBODYPARTS];
int nparts = 0;
int i;
for (f = o->flags->first ; f ; f = f->next) {
if (f->id == F_GOESON) {
possbp[nparts] = f->val[0];
nparts++;
}
}
if (nparts == 0) {
// can't wear anywhere!
reason = E_IMPOSSIBLE;
return B_FALSE;
}
// are any of these body parts free?
for (i = 0; i < nparts; i++) {
if (isfreebp(lf, possbp[i])) {
return B_TRUE;
}
}
// no parts free
reason = E_WEARINGSOMETHINGELSE;
return B_FALSE;
} else {
// does this object go there?
f = hasflagval(o->flags, F_GOESON, where, NA, NA, NULL);
if (!f) {
// can't wear there!
reason = E_IMPOSSIBLE;
return B_FALSE;
}
}
// anything else worn there?
for (oo = lf->pack->first ; oo ; oo = oo->next) {
f = hasflagval(oo->flags, F_EQUIPPED, where, -1, -1, NULL);
if (f) {
reason = E_WEARINGSOMETHINGELSE;
return B_FALSE;
}
}
return B_TRUE;
}
int canweild(lifeform_t *lf, object_t *o) {
object_t *oo;
flag_t *f;
if (lfhasflagval(lf, F_NOBODYPART, BP_WEAPON, NA, NA, NULL)) {
reason = E_NOHANDS;
return B_FALSE;
}
if (o) {
if (hasflag(o->flags, F_TWOHANDED)) {
if (lfhasflagval(lf, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL)) {
reason = E_NOHANDS;
return B_FALSE;
}
}
}
reason = E_OK;
// already weilding it?
for (oo = lf->pack->first ; oo ; oo = oo->next) {
f = hasflagval(oo->flags, F_EQUIPPED, BP_WEAPON, -1, -1, NULL);
if (f) {
if (oo == o) { // already weilding it
reason = E_ALREADYUSING;
return B_FALSE;
}
}
}
// trying to fight unarmed, but no unarmed attack?
if (o == NULL) {
if (!hasflag(lf->flags, F_HASATTACK)) {
reason = E_NOUNARMEDATTACK;
return B_FALSE;
}
}
return B_TRUE;
}
int cantakeoff(lifeform_t *lf, object_t *o) {
flag_t *f;
reason = E_OK;
f = hasflag(o->flags, F_EQUIPPED);
if (!f) {
reason = E_NOTEQUIPPED;
return B_FALSE;
}
// cursed?
if (o->blessed == B_CURSED) {
reason = E_CURSED;
return B_FALSE;
}
return B_TRUE;
}
int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *targob, cell_t *targcell) {
int rv;
int cost;
flag_t *f,*willflag;
// 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;
default:
msg("For some reason, you can't cast that.");
break;
}
}
return B_TRUE;
}
willflag = lfhasflagval(lf, F_CANWILL, sid, NA, NA, NULL);
// take time
taketime(lf, getspellspeed(lf));
// lose mp
lf->mp -= cost;
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("Your spell seems to have failed.");
}
return B_FALSE;
}
// announce
if (!isplayer(lf) && cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
f = lfhasflag(lf,F_SPELLCASTTEXT);
if (f) {
msg("%s %s.", lfname, f->text);
} else {
msg("%s casts a spell.", lfname);
}
}
// cast the spell
addflag(lf->flags, F_CASTINGSPELL, sid, NA, NA, NULL);
rv = dospelleffects(lf, sid, getspellpower(lf, sid), targlf, targob, targcell, B_UNCURSED, NULL);
f = lfhasflag(lf, F_CASTINGSPELL);
if (f) {
killflag(f);
}
// willing this spell? reset counter!
if (willflag) {
if (willflag->val[2] != NA) {
willflag->val[1] = -1;
}
}
return rv;
}
int countmoney(lifeform_t *lf) {
object_t *o;
int amt = 0;
for (o = lf->pack->first ; o ; o = o->next) {
if (o->type->id == OT_GOLD) {
amt += o->amt;
}
}
return amt;
}
void die(lifeform_t *lf) {
char buf[BUFLEN];
flag_t *f;
cell_t *where;
//int dropobs = B_TRUE;
int vaporised = B_FALSE;
if (isplayer(lf) && (lf->race->id == J_GOD)) {
char ch;
ch = askchar("Die", "yn", "n", B_TRUE);
if (ch == 'n') {
lf->hp = lf->maxhp;
msg("Not dying.");
return;
}
}
lf->alive = B_FALSE;
if (isplayer(lf)) {
// force screen redraw so you see your hp = 0
drawscreen();
if (lf->lastdamtype == DT_EXPLOSIVE) {
msg("You are vaporised!");
vaporised = B_TRUE;
} else {
msg("You die.");
}
more();
// force msg redraw!
drawmsg();
} else {
if (!hasflag(lf->flags, F_NODEATHANNOUNCE)) {
if (cansee(player, lf)) {
getlfname(lf, buf);
if (lf->lastdamtype == DT_EXPLOSIVE) {
msg("%s is vaporised!",buf);
vaporised = B_TRUE;
} else if (lf->lastdamtype == DT_MELT) {
msg("%s completely melts.",buf);
} else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) {
msg("%s shatters!",buf);
} else {
msg("%s dies.",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)) {
int xpval;
xpval = calcxp(lf);
//assert(xpval > 0);
gainxp(player, xpval);
}
}
// drop/kill all objects
if (!isplayer(lf)) {
while (lf->pack->first) {
if (vaporised) {
killob(lf->pack->first);
} else {
if (!moveob(lf->pack->first, lf->cell->obpile, ALL)) {
killob(lf->pack->first);
}
}
}
}
// drop corpse/splatter blood
if (vaporised) {
switch (rnd(1,2)) {
case 1:
fragments(lf->cell, "chunk of flesh", 2);
break;
case 2:
fragments(lf->cell, "pool of blood", 2);
break;
}
} else if ((lf->lastdamtype == DT_BASH) && lfhasflag(lf, F_FROZEN)) {
// shattered
fragments(lf->cell, "chunk of ice", 2);
} else {
if (lfhasflag(lf, F_NOCORPSE)) {
if (lfhasflag(lf, F_UNDEAD) && cansee(player, lf)) {
getlfname(lf, buf);
msg("%s crumbles to dust.", buf);
}
} else if (lf->lastdamtype == DT_MELT) {
// drop a pool of water
addob(lf->cell->obpile, "large puddle of water");
} else {
char corpseprefix[BUFLEN];
char corpsename[BUFLEN];
object_t *corpse;
strcpy(corpseprefix, "");
switch (lf->lastdamtype) {
case DT_FIRE:
strcat(corpseprefix, "flaming ");
break;
case DT_COLD:
strcat(corpseprefix, "frozen ");
break;
default:
break;
}
if (lfhasflag(lf, F_BEHEADED)) {
strcat(corpseprefix, "headless ");
}
f = lfhasflag(lf, F_CORPSETYPE);
if (f) {
sprintf(corpsename, "%s%s", corpseprefix, f->text);
} else {
sprintf(corpsename, "%s%s corpse", corpseprefix, lf->race->name);
}
corpse = addob(lf->cell->obpile, corpsename);
if (corpse && (lf->lastdamtype == DT_FIRE) && isflammable(corpse)) {
addflag(corpse->flags, F_ONFIRE, B_TRUE, NA, NA, NULL);
}
if (hasflag(corpse->flags, F_HEADLESS)) {
object_t *headob;
char headname[BUFLEN];
// drop head too
sprintf(headname, "%s head",lf->race->name);
headob = addob(lf->cell->obpile, headname);
}
}
}
where = lf->cell;
if (lf->controller != C_PLAYER) {
// kill lifeform
killlf(lf);
assert(where->lf == NULL);
}
}
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 dumpxp(void) {
race_t *r;
race_t **raceposs;
int *xpposs;
race_t *racetemp;
int xptemp;
int count = 0;
int nposs;
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
nposs = 0;
for (r = firstrace ; r; r = r->next) {
raceposs[nposs] = r;
xpposs[nposs] = calcxprace(r->id);
nposs++;
}
// bubblesort
donesomething = B_TRUE;
while (donesomething) {
donesomething = B_FALSE;
for (i = 0; i < (nposs-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;
}
}
}
// dump
for (i = 0; i < nposs; i++) {
dblog("%-10d%s",xpposs[i], raceposs[i]->name);
}
// free mem
free(xpposs);
free(raceposs);
// dump xp for levels
dblog("");
dblog("");
for (i = 2; i < 30; i++) {
char buf[BUFLEN];
sprintf(buf, "Lev %d",i);
dblog("%-10s%ld",buf, getxpforlev(i));
}
}
int eat(lifeform_t *lf, object_t *o) {
char lfname[BUFLEN];
char obname[BUFLEN];
flag_t *f;
int nutrition;
double amt;
double mod;
int drinking = B_FALSE;
enum LFSIZE sz;
if (hasflag(o->flags, F_DRINKABLE)) {
drinking = B_TRUE;
}
if (drinking) {
if (!candrink(lf, o)) {
if (isplayer(lf)) {
msg("You can't drink that!");
}
return B_TRUE;
}
} else {
if (!caneat(lf, o)) {
if (isplayer(lf)) {
msg("You can't eat that!");
}
return B_TRUE;
}
}
getobname(o, obname, 1);
getlfname(lf, lfname);
nutrition = getnutrition(o);
if (nutrition == 0) {
// should never happen!
if (lf->controller == C_PLAYER) {
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 (lf->controller == C_PLAYER) {
f = hasflag(lf->flags, F_HUNGER);
if (!f) {
msg("You don't need to %s!", drinking ? "drink" : "eat");
return B_TRUE;
}
}
taketime(lf, getactspeed(lf));
if (touch(lf, o)) {
return B_TRUE;
}
// announce
if (lf->controller == C_PLAYER) {
msg("You %s %s.%s", drinking ? "drink" : "eat", obname,
(f->val[1] >= 20) ? " Yum!" : "");
} else if (cansee(player, lf)) {
msg("%s %s %s.", lfname, drinking ? "drinks" : "eats", obname);
}
if (isrotting(o)) {
// lose hp
if (isplayer(lf)) {
msg("That %s was bad!", drinking ? "liquid" : "food");
}
// food poisoning for 20 turns
addtempflag(lf->flags, F_FOODPOISONED, B_TRUE, NA, NA, obname, 20);
}
// change hunger
modhunger(lf, -nutrition);
// special case
if (hasflagval(o->flags, F_CORPSEOF, R_GLOWBUG, NA, NA, NULL)) {
addtempflag(lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, 30);
}
// special case for bananas
if (o->type->id == OT_BANANA) {
object_t *skin;
skin = addob(lf->pack, "banana skin");
if (skin) {
if (isplayer(lf)) {
char skinname[BUFLEN];
getobname(skin, skinname, 1);
msgnocap("%c - %s", skin->letter, skinname);
}
} else {
skin = addob(lf->cell->obpile, "banana skin");
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);
}
}
}
// remove object
removeob(o, 1);
// how long will it take?
// ie. picking up off ground
if (o->pile->owner != lf) {
amt = SPEED_PICKUP;
} else {
amt = 0;
}
// select modifier based on creature size
sz = getlfsize(lf);
switch (sz) {
case SZ_MINI:
mod = 3; break;
case SZ_TINY:
mod = 2.5; break;
case SZ_SMALL:
mod = 2; break;
case SZ_MEDIUM:
mod = 1.5; break;
case SZ_HUMAN:
mod = 1; break;
case SZ_LARGE:
mod = 0.5; break;
case SZ_HUGE:
mod = 0.25; break;
case SZ_ENORMOUS:
mod = 0.125; break;
default:
mod = 1; break;
}
amt += ((double)getactspeed(lf) * mod);
amt -= getactspeed(lf); // because we already used 1 time before.
if (amt < 1) amt = 1;
taketime(lf, amt);
if (isplayer(lf)) {
drawstatus();
wrefresh(statwin);
}
return B_FALSE;
}
void enhanceskills(lifeform_t *lf) {
enum SKILL whichsk;
flag_t *f;
skill_t *sk;
char ch = 'a';
int moretraining = B_FALSE;
// increase str/int etc if we can
f = lfhasflag(lf, F_STATGAINREADY);
if (f) {
enum ATTRIB att;
if (isplayer(lf)) {
char ch;
ch = askchar("Increase your Strength, Dexterity, Constitution or Intelligence?", "sdci",NULL, B_TRUE);
switch (ch) {
case 's': att = A_STR; break;
case 'd': att = A_DEX; break;
case 'c': att = A_CON; break;
case 'i': att = A_IQ; break;
}
} else {
// pick randomly
att = rnd(0,MAXATTS-1);
}
modattr(lf, att, 1);
f->val[2]--;
if (f->val[2] > 0) {
if (isplayer(lf)) {
moretraining = B_TRUE;
}
} else {
killflag(f);
}
if (isplayer(lf)) {
// update status bar with new stats
drawstatus();
wrefresh(statwin);
// wait for player to acknowledge 'you feel stronger' etc
more();
}
}
if (!lf->skillpoints) {
return;
}
if (moretraining && isplayer(lf)) {
msg("You still feel capable of more training.");
more();
}
if (isplayer(lf)) {
char eorl = 'e';
int skillstoenhance = 0;
int skillstolearn = 0;
skillstoenhance = 0;
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) {
skillstoenhance++;
}
}
skillstolearn = 0;
for (sk = firstskill ; sk ; sk = sk->next) {
// TODO: && canlearn(player, sk->id)
if (!getskill(player, sk->id)) {
skillstolearn++;
}
}
if (skillstolearn && (lf->skillpoints >= 2)) {
if (skillstoenhance) {
eorl = askchar("Enhance your current skills or Learn a new one?","el","e", B_TRUE);
} else {
ch = askchar("Learn a new skill?","yn","y", B_TRUE);
if (ch == 'y') eorl = 'l';
else eorl = 'n';
}
} else if (skillstoenhance) {
ch = askchar("Enhance your current skills?","yn","y", B_TRUE);
if (ch == 'y') eorl = 'e';
else eorl = 'n';
} else {
eorl = 'n';
}
if (eorl == 'e') {
// enhance an existing skill
// any skills to get?
if (skillstoenhance) {
char ques[BUFLEN];
sprintf(ques, "Which skill will you enhance (%d points left)?", lf->skillpoints);
initprompt(&prompt, ques);
ch = 'a';
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) {
char buf[BUFLEN];
sprintf(buf, "%s (%s -> %s)", getskillname(f->val[0]),
getskilllevelname(f->val[1]), getskilllevelname(f->val[1] + 1));
addchoice(&prompt, ch++, buf, buf, f);
}
}
addchoice(&prompt, '-', "None", "None", NULL);
getchoice(&prompt);
f = (flag_t *)prompt.result;
if (f) {
whichsk = f->val[0];
giveskill(lf, whichsk);
lf->skillpoints--;
}
} else {
msg("You have already mastered all your current skills.");
}
} else if (eorl == 'l') {
// learn a new skill
// enough points?
if (player->skillpoints < 2) {
msg("You need at least 2 skill points to learn a new skill.");
return;
}
if (skillstolearn) {
char ques[BUFLEN];
sprintf(ques, "Which new skill will you learn (%d points left)?", player->skillpoints);
initprompt(&prompt, ques);
ch = 'a';
for (sk = firstskill ; sk ; sk = sk->next) {
// TODO: && canlearn(player, sk->id)
if (!getskill(player, sk->id)) {
char buf[BUFLEN];
sprintf(buf, "%s (%s)", getskillname(sk->id), getskilldesc(sk->id));
addchoice(&prompt, ch++, buf, buf, sk);
}
}
addchoice(&prompt, '-', "None", "None", NULL);
getchoice(&prompt);
sk = (skill_t *)prompt.result;
if (sk) {
giveskill(player, sk->id);
player->skillpoints -= 2;
}
} else {
msg("There is nothing more that you can learn.");
}
} // end enhance/learnnew
} else {
enum SKILL poss[MAXSKILLS];
int nposs = 0;
int sel;
// monsters will just enhance a random skill, they never learn new ones.
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) {
poss[nposs] = f->val[0];
nposs++;
}
}
sel = rnd(0,nposs-1);
giveskill(lf, poss[sel]);
lf->skillpoints--;
} // end if isplayer
}
// 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;
}
void fightback(lifeform_t *lf, lifeform_t *attacker) {
// respond to damage
if (attacker && !isdead(lf)) {
// wake up
killflagsofid(lf->flags, F_ASLEEP);
// monsters might flee, fight back, etc
if (!isplayer(lf)) {
if (willflee(lf)) {
scare(lf, attacker, PERMENANT);
} else {
// they will now fight back!
if (!hasflagval(lf->flags, F_TARGET, attacker->id, NA, NA, NULL)) {
addflag(lf->flags, F_TARGET, attacker->id, attacker->cell->x, attacker->cell->x, NULL);
// announce
/*
if (haslos(player, lf->cell)) {
char attackername[BUFLEN];
char lfname[BUFLEN];
getlfname(attacker, attackername);
getlfname(lf, lfname);
capitalise(lfname);
msg("%s turns towards %s!", lfname,
haslos(player, attacker->cell) ? attackername : "something");
}
*/
}
}
}
}
}
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;
}
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;
// first check for exact matches
for (r = firstrace; r ; r = r->next) {
if (!strcmp(r->name, name)) {
return r;
}
}
// ...then partial matches
for (r = firstrace; r ; r = r->next) {
if (strstr(r->name, name)) {
return r;
}
}
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;
}
// returns TRUE if we ran away from something
int flee(lifeform_t *lf) {
flag_t *f, *nextf;
lifeform_t *fleefrom = NULL;
// are we fleeing?
for (f = lf->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->id == F_FLEEFROM) {
lifeform_t *thisone;
thisone = findlf(lf->cell->map, f->val[0]);
if (thisone) {
if (cansee(lf, thisone)) {
// 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 can't see 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) {
killflag(f);
}
// if the flag is temporary, wait for it to time out normally
}
}
}
}
if (fleefrom) {
if (!isplayer(lf)) {
enum OBTYPE spell;
// if AI, try to use specific spells like teleport self
spell = aigetfleespell(lf);
if (spell != OT_NONE) {
return castspell(lf, spell, lf, NULL, lf->cell);
}
// if AI, use helpful fleeing items
if (!useitemwithflag(lf, F_AIFLEEITEM)) {
return B_FALSE;
}
}
// move away from them
if (!moveawayfrom(lf, fleefrom->cell)) {
// announce
if (isplayer(lf)) {
char buf[BUFLEN];
drawscreen();
getlfname(fleefrom, buf);
msg("You flee from %s!",buf);
}
return B_TRUE;
}
}
// if we get here, it means we didn't need to or couldn't flee
return B_FALSE;
}
int freezelf(lifeform_t *freezee, lifeform_t *freezer, int howlong) {
if (isimmuneto(freezee->flags, DT_COLD)) {
if (isplayer(freezee)) {
msg("You feel a slight chill.");
}
return B_TRUE;
} else if (isresistantto(freezee->flags, DT_COLD)) {
char buf[BUFLEN];
// note: damage value here will be halved due to resistance
// so this really means rnd( 5,10)
if (isplayer(freezee)) {
msg("You feel freezing cold!");
}
if (freezer) {
char lfname[BUFLEN];
getlfname(freezer, lfname);
sprintf(buf, "being frozen by %s",lfname);
} else {
strcpy(buf, "being frozen");
}
losehp(freezee, rnd(10,20), DT_COLD, freezer, buf);
return B_TRUE;
}
// 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 (maxed) {
msg("You are now fully healed.");
}
// update screen
drawstatus();
updatestatus();
}
}
void gainlevel(lifeform_t *lf) {
char buf[BUFLEN];
float hpratio,mpratio;
if (isplayer(lf)) {
drawstatus();
wrefresh(statwin);
}
lf->level++;
if (isplayer(lf)) {
msg("Welcome to level %d!",lf->level);
more();
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s looks more confident!",buf);
}
// update hp
hpratio = ((float)lf->hp / (float)lf->maxhp);
lf->maxhp += rollhitdice(lf);
lf->hp = hpratio * (float)lf->maxhp;
// update mp
if (lfhasflag(lf, F_MPDICE)) {
mpratio = ((float)lf->mp / (float)lf->maxmp);
lf->maxmp += rollmpdice(lf);
lf->mp = mpratio * (float)lf->maxmp;
}
// stat gain
if ((lf->level % 3) == 0) {
flag_t *f;
f = lfhasflag(lf, F_STATGAINREADY);
if (f) {
f->val[2]++;
msg("You feel more ready for a training session.");
} else {
f = addflag(lf->flags, F_STATGAINREADY, NA, NA, 1, NULL);
msg("You feel ready for a training session.");
}
}
// new skill at level 3, 5, 7, etc
if (isplayer(lf) && (((lf->level - 1) % 2) == 0)) {
lf->skillpoints++;
msg("You feel ready to learn something new.");
}
// you can now re-attempt identification of objects
killflagsofid(lf->flags, F_FAILEDINSPECT);
// TODO: hand out effects based on job
}
void gainmp(lifeform_t *lf, int amt) {
int gained = B_FALSE;
int maxed = B_FALSE;
// magic resistance means you can't regenerate mana!
if (hasmr(lf)) return;
if (lf->mp < lf->maxmp) {
lf->mp += amt;
gained = B_TRUE;
}
if (lf->mp >= lf->maxmp) {
lf->mp = lf->maxmp;
if (gained) maxed = B_TRUE;
}
if (isplayer(lf)) {
if (maxed) {
msg("Your mana is now fully restored.");
}
// update screen
drawstatus();
updatestatus();
}
}
void gainxp(lifeform_t *lf, long amt) {
if (isplayer(lf)) {
statdirty = B_TRUE;
}
// level 0 lifeforms can't gain xp
if (lf->level == 0) return;
lf->xp += amt;
// ready for next level?
while (lf->xp >= getxpforlev(lf->level + 1)) {
gainlevel(lf);
}
}
int getactspeed(lifeform_t *lf) {
int speed = 0;
flag_t *f;
f = lfhasflag(lf, F_ACTIONSPEED);
if (f) {
speed = f->val[0];
} else {
speed = SPEED_ACTION; // default
}
// modifier?
for (f = lf->flags->first ; f ; f = f->next ){
if (f->id == F_SLOWACT) {
speed += f->val[0];
} else if (f->id == F_FASTACT) {
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;
}
return speed;
}
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 getarmourrating(lifeform_t *lf) {
object_t *o;
flag_t *f;
int ar = 0;
f = hasflag(lf->flags, F_ARMOURRATING);
if (f) {
ar += f->val[0];
}
f = hasflag(lf->flags, F_PHALANX);
if (f) {
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) {
if (hasflag(o->flags, F_EQUIPPED)) {
f = hasflag(o->flags, F_ARMOURRATING);
if (f) {
int thisar;
float pct;
pct = getobhppct(o);
thisar = pctof(pct, f->val[0]);
ar += thisar;
ar += getobbonus(o);
}
}
}
return ar;
}
int getattackspeed(lifeform_t *lf) {
object_t *w;
float speed;
speed = getactspeed(lf);
w = getweapon(lf);
if (w) {
speed *= ((float)getobattackdelay(w) / 100.0);
}
return (int)speed;
}
int getattr(lifeform_t *lf, enum ATTRIB attr) {
int val = 0;
flag_t *f;
// override?
f = lfhasflagval(lf, F_ATTRSET, attr, NA, NA, NULL);
if (f) {
return f->val[1];
}
// base attribute
val = lf->att[attr];
// modified?
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_ATTRMOD) && (f->val[0] == attr)) {
val += f->val[1];
}
}
if (val < 0) val = 0;
return val;
}
int getevasion(lifeform_t *lf) {
object_t *o;
flag_t *f;
int ev = 0;
// no evasion if you can't move!
if (isimmobile(lf)) {
return 0;
}
// natural evasion
f = hasflag(lf->flags, F_EVASION);
if (f) {
// evasion 10 means -10% penalty to hit you
ev += (f->val[0]);
if (ev < 0) ev = 0;
}
// now get object penalties/bonuses
for (o = lf->pack->first ; o ; o = o->next) {
// armour/weapons must be worn to do anything
if (hasflag(o->flags, F_EQUIPPED)) {
f = hasflag(o->flags, F_EVASION);
if (f) {
ev += (f->val[0]);
if (ev < 0) ev = 0;
}
}
}
// dexterity mod
ev += (getstatmod(lf, A_DEX) / 2);
if (ev < 0) ev = 0;
// you are easier to hit if you're glowing
if (hasflag(lf->flags, F_PRODUCESLIGHT)) {
ev -= 5;
if (ev < 0) ev = 0;
}
// modify for blindness
if (isblind(lf)) {
ev -= 15;
}
if (ev < 0) ev = 0;
return ev;
}
object_t *getbestmissile(lifeform_t *lf) {
object_t *bestwep = NULL;
int bestdam = -1;
object_t *o;
for (o = lf->pack->first ; o ; o = o->next) {
if (!isequipped(o) && (ismissile(o) || hasflag(o->flags, F_MISSILE))) {
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) && 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;
bestwep = getweapon(lf);
if (!bestwep) {
op = getunarmedweapon(lf, NULL);
bestwep = op->first;
}
/*
if (curwep) {
f = hasflag(curwep->flags, F_DAM);
//bestmaxdam = f->val[1];
} else {
// no current weapon, and no unarmed weapon
// anything will be better!
//bestmaxdam = -999;
}
*/
for (o = lf->pack->first ; o ; o = o->next) {
// if it does damage and we can weild it...
if (isweapon(o) && !isfirearm(o) && canweild(lf, o)) {
if (isbetterwepthan(o, bestwep)) {
bestwep = o;
}
}
}
if (op && (bestwep == op->first)) {
// ie. use no weapon
bestwep = NULL;
}
if (op) {
killobpile(op);
}
return bestwep;
}
int getbodyparthitchance(enum BODYPART bp) {
switch (bp) {
case BP_EYES: return 1;
case BP_HEAD: return 2;
case BP_WAIST: return 3;
case BP_HANDS: return 3;
case BP_FEET: return 3;
case BP_LEGS: return 4;
case BP_SHOULDERS: return 4;
case BP_BODY: return 5;
default: break;
}
return 0; // ie rings, weapon
}
char *getbodypartname(enum BODYPART bp) {
switch (bp) {
case BP_WEAPON:
return "right hand";
case BP_SECWEAPON:
return "left hand";
case BP_RIGHTHAND:
return "right finger";
case BP_LEFTHAND:
return "left finger";
case BP_HANDS:
return "hands";
case BP_EYES:
return "eyes";
case BP_HEAD:
return "head";
case BP_BODY:
return "body";
case BP_SHOULDERS:
return "shoulders";
case BP_WAIST:
return "waist";
case BP_LEGS:
return "legs";
case BP_FEET:
return "feet";
}
return "unknown";
}
char *getbodypartequipname(enum BODYPART bp) {
switch (bp) {
case BP_WEAPON:
case BP_SECWEAPON:
return "in"; // ie. 'in right hand'
case BP_RIGHTHAND:
case BP_LEFTHAND:
case BP_HANDS:
case BP_HEAD:
case BP_BODY:
case BP_LEGS:
case BP_FEET:
return "on";
case BP_EYES:
case BP_SHOULDERS:
return "over";
case BP_WAIST:
return "around";
}
return "unknown";
}
object_t *getequippedob(obpile_t *op, enum BODYPART bp) {
object_t *o;
for (o = op->first; o ; o = o->next) {
if (hasflagval(o->flags, F_EQUIPPED, bp, NA, NA, NULL)) {
return o;
}
}
return NULL;
}
object_t *getfirearm(lifeform_t *lf) {
object_t *o;
o = getequippedob(lf->pack, BP_SECWEAPON);
if (o && hasflag(o->flags, F_FIREARM)) {
return o;
}
return NULL;
}
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];
}
/*
// get time to heal 1 hp
int gethealtime(lifeform_t *lf) {
int healtime;
flag_t *f;
f = lfhasflag(lf, F_HEALTIME);
if (f) {
healtime = f->val[0];
} else {
healtime = DEF_HEALTIME;
}
return healtime;
}
*/
int gethearingrange(lifeform_t *lf) {
int range = 15; // deafult
return range;
}
int gethppct(lifeform_t *lf) {
float pct;
pct = (int)(((float)lf->hp / (float)lf->maxhp) * 100);
return pct;
}
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(enum HUNGER hunger, char *buf) {
switch (hunger) {
case H_STUFFED:
strcpy(buf, "stuffed");
break;
case H_FULL:
strcpy(buf, "full");
break;
case H_NONE:
strcpy(buf, "not hungry");
break;
case H_PECKISH:
strcpy(buf, "peckish");
break;
case H_HUNGRY:
strcpy(buf, "hungry");
break;
case H_VHUNGRY:
strcpy(buf, "very hungry");
break;
case H_STARVING:
strcpy(buf, "starving");
break;
case H_STARVED:
strcpy(buf, "starved");
break;
}
return buf;
}
int gethungerval(lifeform_t *lf) {
flag_t *f;
f = hasflag(lf->flags, F_HUNGER);
if (f) {
return f->val[0];
}
return 0;
}
job_t *getjob(lifeform_t *lf) {
flag_t *f;
// no job if polymorphed
if (lfhasflag(lf, F_POLYMORPHED)) {
return NULL;
}
f = hasflag(lf->flags, F_JOB);
if (f) {
return findjob(f->val[0]);
}
return NULL;
}
int getlfaccuracy(lifeform_t *lf) {
obpile_t *op = NULL;
object_t *wep;
int acc = 0;
// get weapon
wep = getweapon(lf);
if (wep) {
acc = getobaccuracy(wep);
} else {
// ie. unarmed
op = getunarmedweapon(lf, NULL);
if (op->first) {
wep = op->first;
acc = getobaccuracy(wep);
} else {
// cannot attack
acc = 0;
}
killobpile(op);
}
// modify with dexterity
acc += getstatmod(lf, A_DEX);
// modify for blindness
if (isblind(lf)) {
acc -= 50;
}
// modify for nausea
if (lfhasflag(lf, F_NAUSEATED)) {
acc -= 20;
}
if (acc < 0) acc = 0;
return acc;
}
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 getnightvisrange(lifeform_t *lf) {
int range = 0; // default
flag_t *f;
f = lfhasflag(lf, F_SEEINDARK);
if (f) {
range = getvisrange(lf);
}
return range;
}
char *getlfconditionname(enum LFCONDITION cond) {
switch (cond) {
case C_CRITICAL:
return "critically wounded";
case C_SERIOUS:
return "seriously wounded";
case C_WOUNDED:
return "wounded";
case C_HURT:
return "hurt";
case C_HEALTHY:
return "healthy";
case C_DEAD:
return "dead";
}
return "?unknown condition?";
}
char *getseenlfconditionname(lifeform_t *lf, lifeform_t *viewer) {
enum IQBRACKET iqb;
enum LFCONDITION cond,cutoff;
iqb = getiqname(getattr(viewer, A_IQ), NULL);
// figure out health cutoff - condition > cutoff gets no description
if (iqb >= IQ_AVERAGE) {
cutoff = C_HURT; // ie. no cutoff
} else if (iqb >= IQ_DOPEY) {
cutoff = C_WOUNDED;
} else if (iqb >= IQ_ANIMAL) {
cutoff = C_SERIOUS;
} else {
cutoff = C_DEAD;
}
cond = getlfcondition(lf);
if (cond > cutoff) {
return "";
}
return getlfconditionname(cond);
}
char getlfglyph(lifeform_t *lf) {
flag_t *f;
if (isplayer(lf) && !ispolymorphed(lf)) {
return '@';
}
f = lfhasflag(lf, F_GLYPH);
if (f) {
return f->text[0];
}
return lf->race->glyph;
}
enum MATERIAL getlfmaterial(lifeform_t *lf) {
if (lf->race->id == R_DANCINGWEAPON) {
object_t *wep;
wep = getweapon(lf);
if (wep) {
return wep->material->id;
}
}
return lf->race->material->id;
}
float getmaxcarryweight(lifeform_t *lf) {
float max;
float mod;
enum STRBRACKET sbrack;
sbrack = getstrname(getattr(lf, A_STR), NULL);
switch (sbrack) {
case ST_HELPLESS:
mod = 0.1; break;
case ST_FEEBLE:
mod = 0.25; break;
case ST_VWEAK:
mod = 0.5; break;
case ST_WEAK:
mod = 0.75; break;
case ST_AVERAGE:
mod = 1; break; // your body weight
case ST_STRONG:
mod = 1.25; break;
case ST_MIGHTY:
mod = 1.5; break;
case ST_TITANIC:
mod = 2; break; // twice your own body weight
default:
mod = 1; break; // your body weight
}
max = getlfweight(lf, B_NOOBS) * mod;
return max;
}
/*
float getmaxliftweight(lifeform_t *lf) {
float max;
float mod;
enum STRBRACKET sbrack;
sbrack = getstrname(getattr(lf, A_STR), NULL);
switch (sbrack) {
case ST_HELPLESS:
mod = 0.05; break;
case ST_FEEBLE:
mod = 0.1; break;
case ST_VWEAK:
mod = 0.25; break;
case ST_WEAK:
mod = 0.4; break;
case ST_AVERAGE:
mod = 0.5; break; // half your body weight
case ST_STRONG:
mod = 0.75; break;
case ST_MIGHTY:
mod = 1; break; // your own body weight
case ST_TITANIC:
mod = 1.5; break; // more than your own body weight
}
max = getlfweight(lf, B_NOOBS) * mod;
return max;
}
*/
float getmaxpushweight(lifeform_t *lf) {
float max;
max = getlfweight(lf, B_NOOBS) * 2; // twice your body weight
return max;
}
// TODO: take darkness into account
int getvisrange(lifeform_t *lf) {
int range;
flag_t *f;
if (isblind(lf)) {
return 0;
}
f = lfhasflag(lf, F_VISRANGE);
if (f) {
range = f->val[0];
} else {
range = MAXVISRANGE;
}
// modifications?
for (f = lf->flags->first ; f ; f = f->next) {
if (f->id == F_VISRANGEMOD) {
range += f->val[0];
}
}
if (range < 0) range = 0;
return range;
}
int getmovespeed(lifeform_t *lf) {
int speed = 0;
flag_t *f;
f = lfhasflag(lf, F_MOVESPEED);
if (f) {
speed = f->val[0];
} else {
speed = SPEED_MOVE; // default
}
// modifier?
for (f = lf->flags->first ; f ; f = f->next ){
if (f->id == F_SLOWMOVE) {
speed += f->val[0];
} else if (f->id == F_FASTMOVE) {
speed -= f->val[0];
} else if (f->id == F_SPRINTING) {
speed -= 5;
} else if (f->id == F_TIRED) {
speed += 5;
}
}
switch (isburdened(lf)) {
case BR_NONE:
break;
case BR_BURDENED:
speed += 5;
break;
case BR_STRAINED:
case BR_OVERLOADED:
speed += 10;
break;
}
return speed;
}
char *getmoveverb(lifeform_t *lf) {
if (lfhasflag(lf, F_FLYING)) {
return "fly";
} else if (lfhasflag(lf, F_LEVITATING)) {
return "float";
}
return "walk";
}
char *getmoveverbother(lifeform_t *lf) {
if (lfhasflag(lf, F_FLYING)) {
return "flies";
} else if (lfhasflag(lf, F_LEVITATING)) {
return "floats";
}
return "walks";
}
char *getlfname(lifeform_t *lf, char *buf) {
return real_getlfname(lf, buf, B_TRUE);
}
char *real_getlfname(lifeform_t *lf, char *buf, int usevis) {
char descstring[BUFLEN];
char jobstring[BUFLEN];
job_t *j;
flag_t *f;
// construct description string
strcpy(descstring, "");
if (lfhasflag(lf, F_FROZEN)) {
strcat(descstring, "frozen ");
}
if (lfhasflag(lf, F_HEADLESS)) {
strcat(descstring, "headless ");
}
// construct job string
strcpy(jobstring, "");
j = getjob(lf);
if (j) {
sprintf(jobstring, " %s", j->name);
jobstring[1] = tolower(jobstring[1]);
}
if (lf == player) {
sprintf(buf, "you");
} else {
//if (isblind(player)) {
if (usevis && !cansee(player, lf)) {
sprintf(buf, "something");
} else {
if (lf->race->id == R_DANCINGWEAPON) {
object_t *wep;
wep = getweapon(lf);
if (wep) {
char obname[BUFLEN];
real_getobname(wep, obname, 1, B_TRUE, B_FALSE, B_FALSE, B_FALSE);
sprintf(buf, "the %s%s",descstring,noprefix(obname));
} else {
sprintf(buf, "the %s%s%s",descstring,lf->race->name,jobstring);
}
} else {
char zombiestring[BUFLEN];
f = hasflag(lf->flags, F_LFSUFFIX);
strcpy(zombiestring, "");
if (f) {
sprintf(zombiestring, " %s", f->text);
}
sprintf(buf, "the %s%s%s%s",descstring,lf->race->name,jobstring,zombiestring);
}
}
}
return buf;
}
char *getlfnamea(lifeform_t *lf, char *buf) {
if (lf == player) {
sprintf(buf, "you");
} else {
char buf2[BUFLEN];
getlfname(lf, buf2);
sprintf(buf, "%s %s",
isvowel(lf->race->name[0]) ? "an" : "a",
noprefix(buf2));
}
return buf;
}
enum LFSIZE getlfsize(lifeform_t *lf) {
flag_t *f;
f = hasflag(lf->flags, F_SIZE);
assert(f);
return f->val[0];
}
float getlfweight(lifeform_t *lf, int withobs) {
float weight = 0;
weight = lf->race->weight;
if (lfhasflag(lf, F_OBESE)) {
weight *= 2;
}
if (withobs) {
weight += getobpileweight(lf->pack);
}
return weight;
}
int getspellspeed(lifeform_t *lf) {
int speed = 0;
flag_t *f;
f = lfhasflag(lf, F_SPELLSPEED);
if (f) {
speed = f->val[0];
} else {
f = lfhasflag(lf, F_MOVESPEED);
if (f) {
speed = f->val[0];
} else {
speed = SPEED_MOVE; // default
}
}
// don't use movement speed modifier!
return speed;
}
char *getplayername(char *buf) {
flag_t *f;
f = hasflag(player->flags, F_NAME);
if (f) {
strcpy(buf, f->text);
} else{
// should never happen!
strcpy(buf, "");
}
return buf;
}
char *getplayernamefull(char *buf) {
char pname[BUFLEN];
job_t *j;
getplayername(pname);
j = getjob(player);
if (j) {
sprintf(buf, "%s the %s", pname, j->name);
} else {
sprintf(buf, "%s the %s", pname, player->race->name);
}
return buf;
}
object_t *getrandomarmour(lifeform_t *lf) {
object_t *o;
flag_t *f;
object_t *poss[MAXBODYPARTS];
object_t **hitposition;
int hitchance[MAXBODYPARTS];
int nposs = 0;
int maxroll = 0;
int i,n,idx;
int sel;
int anyfound = B_FALSE;
// make a list of all valid armour
for (o = lf->pack->first ; o ; o = o->next) {
if (hasflag(o->flags, F_ARMOURRATING)) {
f = hasflag(o->flags, F_EQUIPPED);
if (f) {
anyfound = B_TRUE;
poss[nposs] = o;
if (hasflag(o->flags, F_SHIELD)) {
hitchance[nposs] = getbodyparthitchance(BP_BODY);
} else {
hitchance[nposs] = getbodyparthitchance(f->val[0]);
}
maxroll += hitchance[nposs];
nposs++;
}
}
}
if (anyfound == B_FALSE) 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++) {
for (n = 0; n < hitchance[i]; n++) {
hitposition[idx] = poss[i];
idx++;
}
}
sel = rnd(0, maxroll-1);
o = hitposition[sel];
free(hitposition);
return o;
}
/*
int getrandommonlevel(int depth) {
int lev;
int max;
max = (depth / 3);
if (max < 1) max = 1;
lev = rnd(1,max);
return lev;
}
*/
race_t *getrandomrace(map_t *map) {
//int rarity;
race_t *r;
race_t *poss[MAXRANDOMLFCANDIDATES];
int nposs = 0;
int selidx;
int db = B_TRUE;
int depth;
int raritymin,raritymax;
// determine rarity of lf to generate
if (map) {
depth = map->depth;
} else {
depth = rnd(1,MAXDEPTH);
}
getrarity(depth, &raritymin, &raritymax, 5);
if (db) dblog("finding random lf with rarity val %d-%d\n",raritymin,raritymax);
// 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;
flag_t *rarflag = NULL;
// correct rarity?
rarflag = hasflagval(r->flags, F_RARITY, H_ALL, NA, NA, NULL);
if (!rarflag) {
if (map) {
rarflag = hasflagval(r->flags, F_RARITY, map->habitat, NA, NA, NULL);
} else {
rarflag = hasflagval(r->flags, F_RARITY, NA, NA, NA, NULL);
}
}
if (rarflag) {
if ((rarflag->val[1] >= raritymin) && (rarflag->val[1] <= raritymax)) {
valid = B_TRUE;
}
}
if (valid) {
if (db) dblog("-> possibility: %s, rarity=%d",r->name, rarflag->val[1]);
poss[nposs] = r;
nposs++;
if (nposs >= MAXRANDOMLFCANDIDATES) break;
}
}
// nothing found?
if (nposs == 0) {
// already at lowest rarity?
if ((raritymax >= 100) && (raritymin <= 0)) {
// give up
if (db) dblog("no possible lf at all! giving up.");
return NULL;
}
// expand range and try again
raritymax += 10; if (raritymax > 100) raritymax = 100;
raritymin -= 10; if (raritymin < 0) raritymin = 0;
if (db) dblog("no possible lfs like this. trying again with rarity %d-%d\n",raritymin,raritymax);
}
}
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(void) {
race_t **poss;
race_t *r;
int nposs = 0;
int sel;
int count = 0;
// count races
for (r = firstrace ; r ; r = r->next) {
count++;
}
poss = malloc(count * sizeof(race_t *));
for (r = firstrace ; r ; r = r->next) {
if (appearsrandomly(r->id)) {
poss[nposs] = r;
nposs++;
}
}
sel = rnd(0,nposs-1);
r = poss[sel];
free(poss);
return r;
}
// returns proficiency level in a given skill
enum SKILLLEVEL getskill(lifeform_t *lf, enum SKILL id) {
flag_t *f;
f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL);
if (f) {
return f->val[1];
}
return PR_INEPT;
}
char *getspeedname(int speed, char *buf) {
sprintf(buf, "unknownspeed");
if (speed <= SP_GODLIKE) {
sprintf(buf, "insanely fast");
} else if (speed <= SP_ULTRAFAST) {
sprintf(buf, "extremely fast");
} else if (speed <= SP_VERYFAST) {
sprintf(buf, "very fast");
} else if (speed <= SP_FAST) {
sprintf(buf, "fast");
} else if (speed <= SP_NORMAL) {
sprintf(buf, "normal");
} else if (speed <= SP_SLOW) {
sprintf(buf, "slow");
} else if (speed <= SP_VERYSLOW) {
sprintf(buf, "very slow");
} else if (speed <= SP_ULTRASLOW) {
sprintf(buf, "extremely slow");
} else {
sprintf(buf, "insanely slow");
}
return buf;
}
// ie. intelligence level: xxx
enum IQBRACKET getiqname(int iq, char *buf) {
if (iq <= 0) {
if (buf) strcpy(buf, "mindless");
return IQ_MINDLESS;
} else if (iq == 1) {
if (buf) strcpy(buf, "vegetable");
return IQ_VEGETABLE;
} else if (iq <= 3) {
if (buf) strcpy(buf, "animal");
return IQ_ANIMAL;
} else if (iq <= 6) {
if (buf) strcpy(buf, "dim-witted");
return IQ_DIMWITTED;
} else if (iq <= 9) {
if (buf) strcpy(buf, "dopey");
return IQ_DOPEY;
} else if (iq <= 12) {
if (buf) strcpy(buf, "average");
return IQ_AVERAGE;
} else if (iq <= 15) {
if (buf) strcpy(buf, "smart");
return IQ_SMART;
} else if (iq <= 17) {
if (buf) strcpy(buf, "enlightened");
return IQ_ENLIGHTENED;
}
if (buf) strcpy(buf, "genius");
return IQ_GENIUS;
}
enum DEXBRACKET getdexname(int dex, char *buf) {
if (dex <= 0) {
if (buf) strcpy(buf, "incompetent");
return DX_INCOMPETENT;
} else if (dex <= 2) {
if (buf) strcpy(buf, "oafish");
return DX_OAFISH;
} else if (dex <= 4) {
if (buf) strcpy(buf, "inept");
return DX_INEPT;
} else if (dex <= 6) {
if (buf) strcpy(buf, "clumsy");
return DX_CLUMSY;
} else if (dex <= 8) {
if (buf) strcpy(buf, "awkward");
return DX_AWKWARD;
} else if (dex == 9) {
if (buf) strcpy(buf, "average");
return DX_AVERAGE;
} else if (dex <= 11) {
if (buf) strcpy(buf, "dextrous");
return DX_DEXTROUS;
} else if (dex <= 13) {
if (buf) strcpy(buf, "nimble");
return DX_NIMBLE;
} else if (dex <= 15) {
if (buf) strcpy(buf, "agile");
return DX_AGILE;
} else if (dex <= 17) {
if (buf) strcpy(buf, "swift");
return DX_SWIFT;
}
if (buf) strcpy(buf, "supersonic");
return DX_SUPERSONIC;
}
// returns a pct addition for things like:
// accuracy, evasion, lockpicking
// result will be between -50 and 50
float getstatmod(lifeform_t *lf, enum ATTRIB att) {
float mod = 0;
float base;
// <9 = penalty
// 9 = average
// >9 = bonus up to 50%
base = getattr(lf, att) - 9;
mod = (int)((base / 9.0) * 50.0);
return mod;
}
enum CONBRACKET getconname(int str, char *buf) {
if (str <= 2) {
if (buf) strcpy(buf, "frail");
return CN_FRAIL;
} else if (str <= 4) {
if (buf) strcpy(buf, "sickly");
return CN_SICKLY;
} else if (str <= 6) {
if (buf) strcpy(buf, "unhealthy");
return CN_UNHEALTHY;
} else if (str <= 8) {
if (buf) strcpy(buf, "unfit");
return CN_UNFIT;
} else if (str <= 11) {
if (buf) strcpy(buf, "average");
return CN_AVERAGE;
} else if (str <= 14) {
if (buf) strcpy(buf, "healthy");
return CN_HEALTHY;
} else if (str <= 16) {
if (buf) strcpy(buf, "very fit");
return CN_FIT;
} else if (str <= 18) {
if (buf) strcpy(buf, "hardy");
return CN_HARDY;
}
if (buf) strcpy(buf, "hardy");
return CN_HARDY;
}
enum STRBRACKET getstrname(int str, char *buf) {
if (str <= 0) {
if (buf) strcpy(buf, "helpless");
return ST_HELPLESS;
} else if (str <= 3) {
if (buf) strcpy(buf, "feeble");
return ST_FEEBLE;
} else if (str <= 6) {
if (buf) strcpy(buf, "very weak");
return ST_VWEAK;
} else if (str <= 8) {
if (buf) strcpy(buf, "weak");
return ST_WEAK;
} else if (str <= 12) {
if (buf) strcpy(buf, "average");
return ST_AVERAGE;
} else if (str <= 15) {
if (buf) strcpy(buf, "strong");
return ST_STRONG;
} else if (str <= 17) {
if (buf) strcpy(buf, "mighty");
return ST_MIGHTY;
}
if (buf) strcpy(buf, "titanic");
return ST_TITANIC;
}
char *getskilldesc(enum SKILL id) {
skill_t *s;
s = findskill(id);
if (s) {
return s->desc;
}
return "?unknownskilldesc?";
}
char *getskillname(enum SKILL id) {
skill_t *s;
s = findskill(id);
if (s) {
return s->name;
}
return "?unknownskill?";
}
char *getskilllevelname(enum SKILLLEVEL sl) {
switch (sl) {
case PR_INEPT:
return "Inept";
case PR_NOVICE:
return "Novice";
case PR_BEGINNER:
return "Beginner";
case PR_ADEPT:
return "Adept";
case PR_SKILLED:
return "Skilled";
case PR_EXPERT:
return "Expert";
case PR_MASTER:
return "Master";
}
return "?Unknownskilllevel?";
}
// get throw speed (ie. damage multiplier)
// based on strength.
int getthrowspeed(int str) {
enum STRBRACKET sb;
int speed = 0;
sb = getstrname(str, NULL);
switch (sb) {
case ST_HELPLESS:
case ST_FEEBLE:
speed = 0;
break;
case ST_VWEAK:
case ST_WEAK:
speed = 1;
break;
default:
case ST_AVERAGE:
case ST_STRONG:
speed = 2;
break;
case ST_MIGHTY:
speed = 3;
break;
case ST_TITANIC:
speed = 4;
break;
// gun is 10
}
return speed;
}
object_t *getweapon(lifeform_t *lf) {
object_t *o;
flag_t *f;
for (o = lf->pack->first ; o ; o = o->next) {
f = hasflagval(o->flags, F_EQUIPPED, BP_WEAPON, NA, NA, NULL);
if (f) {
return o;
}
}
return NULL;
}
long getxpforlev(int level) {
long needxp = 0;
// no xp needed for level 1
/*
for (i = 0; i < level - 1; i++) {
//needxp += (20 * pow(2,i));
needxp += (20 * (pow(i,2.8)));
}
*/
//needxp = (20 * (pow(level,2.8) - 1));
needxp = (10 * (pow(level,2.8) - 1));
return needxp;
}
void givejob(lifeform_t *lf, enum JOB jobid) {
job_t *j;
flag_t *f;
int i;
int condition = NOCONDITION;
int rollhp = B_FALSE, rollmp = B_FALSE;
int rollatt[MAXATTS];
int ignorenext;
int ignoredprev;
int db = B_TRUE;
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);
// override hitdice from race
if (hasflag(j->flags, F_HITDICE)) {
rollhp = B_TRUE;
f = lfhasflag(lf, F_HITDICE);
if (f) {
killflag(f);
}
}
// override mpdice from race
if (hasflag(j->flags, F_MPDICE)) {
rollmp = B_TRUE;
f = lfhasflag(lf, F_MPDICE);
if (f) {
killflag(f);
}
}
// inherit all flags except startob ones
ignorenext = B_FALSE;
ignoredprev = B_FALSE;
for (f = j->flags->first ; f ; f = f->next) {
if (condition == IFPLAYER) {
if (f->id == F_ENDIFPLAYER) {
if (db) dblog("ending ifplayer condition");
condition = NOCONDITION;
} else if (!isplayer(lf)) {
if (db) dblog("setting ignorenext cause of ifplayer");
ignorenext = B_TRUE; // ie. ignore this one.
}
} else if (condition == IFMONSTER) {
if (f->id == F_ENDIFMONSTER) {
if (db) dblog("ending ifmonster condition");
condition = NOCONDITION;
} else if (isplayer(lf)) {
if (db) dblog("setting ignorenext cause of ifmonster");
ignorenext = B_TRUE; // ie. ignore this one.
}
}
if (ignorenext) {
if (db) dblog("ignoring this flag: %d",f->id);
ignorenext = B_FALSE;
ignoredprev = B_TRUE;
} else {
if (f->id == F_IFPCT) {
if (db) dblog("ifpct flag...");
if (rnd(0,100) > f->val[0]) {
if (db) dblog(" failed.");
ignorenext = B_TRUE;
} else {
if (db) dblog(" passed.");
}
/*
if ((f->val[2] == IFMONSTER) && isplayer(lf)) {
ignorenext = B_TRUE;
} else if ((f->val[2] == IFPLAYER) && !isplayer(lf)) {
ignorenext = B_TRUE;
}
*/
} else if (f->id == F_IFPLAYER) {
if (db) dblog("starting ifplayer condition");
condition = IFPLAYER;
} else if (f->id == F_IFMONSTER) {
if (db) dblog("starting ifmonster condition");
condition = IFMONSTER;
} else if (f->id == F_ELSE) {
if (db) dblog("else flag...");
if (ignoredprev) {
if (db) dblog("... MATCHED.");
} else {
if (db) dblog("... ignoring next.");
ignorenext = B_TRUE;
}
//
} else {
if (db) dblog("processing normal flag: %d",f->id);
/*
switch (f->id) {
case F_STARTOB:
case F_STARTOBDT:
case F_STARTOBCLASS:
break;
default:
addflag_real(lf->flags, f->id, f->val[0], f->val[1], f->val[2], f->text, FROMJOB,B_TRUE, -1);
if (f->id == F_STARTSTR) rollatt[A_STR] = B_TRUE;
if (f->id == F_STARTDEX) rollatt[A_DEX] = B_TRUE;
if (f->id == F_STARTIQ) rollatt[A_IQ] = B_TRUE;
break;
}
*/
addflag_real(lf->flags, f->id, f->val[0], f->val[1], f->val[2], f->text, FROMJOB,B_TRUE, -1);
if (f->id == F_STARTATT) {
rollatt[f->val[0]] = B_TRUE;
}
}
ignoredprev = B_FALSE;
}
}
// now give start obs/skills from it
givestartobs(lf, lf->flags);
givestartskills(lf, lf->flags);
// override hp/mp from race
if (rollhp) {
lf->maxhp = 0;
for (i = 0; i < lf->level; i++) {
lf->maxhp += rollhitdice(lf);
assert(lf->maxhp > 0);
}
lf->hp = lf->maxhp;
}
if (rollmp) {
f = hasflag(lf->flags, F_MPDICE);
if (f) {
lf->maxmp = f->val[0] * 4;
if (f->val[1] != NA) lf->maxmp += f->val[1];
for (i = 0; i < lf->level-1; i++) {
lf->maxmp += rollmpdice(lf);
}
lf->mp = lf->maxmp;
}
}
// re-roll attributes if required
for (i = 0; i < MAXATTS; i++) {
if (rollatt[i]) {
rollstat(lf, i);
lf->baseatt[i] = lf->att[i];
}
}
if (!gamestarted) {
autoweild(lf);
}
}
void giveobflags(lifeform_t *lf, object_t *o, enum FLAG whattype) {
int flagsknown = 0, flagsfound = 0;
flag_t *f,*newflag;
int held, equipped,activated;
int lifetimeval;
if (o->pile->owner == lf) held = B_TRUE;
if (held && hasflag(o->flags, F_EQUIPPED)) {
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;
}
}
for (f = o->flags->first ; f ; f = f->next) {
if (f->id == whattype) {
int known = B_FALSE;
int addit = B_FALSE;
if (f->val[2] == IFKNOWN) {
// only confer if known
if (isknown(o)) {
addit = B_TRUE;
}
} else {
addit = B_TRUE;
}
if (isknown(o)) {
known = B_TRUE;
}
if (addit) {
newflag = addflag_real(lf->flags, f->val[0], f->val[1], f->val[2], NA, f->text, lifetimeval, known, o->id);
if (newflag->known) { // ie. if we found out about it through it being announced
flagsknown++;
}
flagsfound++;
}
}
}
// if all conferred flags now known, object is known
if (flagsfound && (flagsknown == flagsfound)) {
makeknown(o->type->id);
}
}
int giveskill(lifeform_t *lf, enum SKILL id) {
flag_t *f;
skill_t *sk;
sk = findskill(id);
if (!sk) {
return B_TRUE;
}
f = lfhasflagval(lf, F_HASSKILL, id, NA, NA, NULL);
if (f) {
// already have the skill - make it better
if (f->val[1] < PR_MASTER) {
f->val[1]++;
}
if (isplayer(lf) && gamestarted) {
msg("You have learned the %s %s skill!", getskilllevelname(f->val[1]), getskillname(sk->id));
}
} else {
// gaining a new skill
f = addflag(lf->flags, F_HASSKILL, id, PR_NOVICE, NA, NULL);
if (isplayer(lf) && gamestarted) {
msg("You have learned the %s %s skill!", getskilllevelname(PR_NOVICE), getskillname(sk->id));
}
// special effects...
if (id == SK_RESEARCH) {
addflag(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL);
} else if (id == SK_ATHLETICS) {
addflag(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL);
}
}
if (id == SK_TECHUSAGE) {
objecttype_t *ot;
// automatically make known all tech <= our skill level
for (ot = objecttype ; ot ; ot = ot->next) {
// if objecttype is not known...
if (!isknownot(ot)) {
flag_t *tf;
tf = hasflag(ot->flags, F_TECHLEVEL);
// if objecttype has a tech level , and it is
// lower (or equal to) our tech knowledge...
if (tf && !isknownot(ot) && (tf->val[0] <= f->val[1])) {
// then make it known!
makeknown(ot->id);
}
}
}
}
return B_FALSE;
}
// give start objects from a particular flagpile
void givestartobs(lifeform_t *lf, flagpile_t *fp) {
object_t *o;
flag_t *f;
char buf[BUFLEN];
int ignorenext = B_FALSE;
// give start objects and id them
ignorenext = B_FALSE;
for (f = fp->first ; f ; f = f->next) {
o = NULL;
if (ignorenext) {
continue;
}
if (f->id == F_IFPCT) {
if (rnd(0,100) > f->val[0]) {
ignorenext = B_TRUE;
}
if ((f->val[2] == IFMONSTER) && isplayer(lf)) {
ignorenext = B_TRUE;
} else if ((f->val[2] == IFPLAYER) && !isplayer(lf)) {
ignorenext = B_TRUE;
}
} else {
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 monster, DONT inherit specific STARTOB flags from race
if ((f->lifetime == FROMJOB) && (f->id == F_STARTOB)) {
continue;
}
}
if (f->id == F_STARTOB) {
assert(strlen(f->text) > 0);
if (rnd(1,100) <= f->val[0]) {
o = addob(lf->pack, f->text);
}
} else if (f->id == F_STARTOBDT) {
if (rnd(1,100) <= f->val[0]) {
while (!getrandomobwithdt(lf->cell->map, f->val[1], buf));
//assert(strlen(buf) > 0);
o = addob(lf->pack, buf);
}
} else if (f->id == F_STARTOBCLASS) {
if (rnd(1,100) <= f->val[0]) {
while (!getrandomobwithclass(lf->cell->map, f->val[1], buf));
//if (strlen(buf) <= 0);
o = addob(lf->pack, buf);
}
}
if (o) {
if (isplayer(lf)) {
identify(o);
}
}
}
}
// now remove startob flags so we don't get them again!
killflagsofid(fp, F_STARTOB);
killflagsofid(fp, F_STARTOBDT);
killflagsofid(fp, F_STARTOBCLASS);
// 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;
for (f = fp->first ; f ; f = f->next) {
if (f->id == F_STARTSKILL) {
int wantval;
wantval = f->val[1];
while (getskill(lf, f->val[0]) < wantval) {
giveskill(lf, f->val[0]);
}
}
}
// now remove startskill flags so we don't get them again!
killflagsofid(fp, F_STARTSKILL);
}
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;
}
// 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;
// note: we check conferred flags first because we want
// these to override ones direclty on the player.
// does a held object give us this flag?
/*
o = hasobwithflagval(lf->pack, F_HOLDCONFER, fid, NA, NA, NULL);
if (o) {
// return the flag, not the objcet
f = hasflagval(o->flags, F_HOLDCONFER, fid, NA, NA, NULL);
if (f) return f;
}
for (o = lf->pack->first ; o ; o = o->next) {
f = hasflagval(o->flags, F_HOLDCONFER, fid, NA, NA, NULL);
if (f) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return f;
}
}
}
*/
/*
// does an equipped object give us this flag?
for (o = lf->pack->first ; o ; o = o->next) {
if (hasflag(o->flags, F_EQUIPPED)) {
f = hasflagval(o->flags, F_EQUIPCONFER, fid, NA, NA, NULL);
if (f) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return f;
}
}
}
}
*/
// do we have this flag directly?
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;
// does a held object give us this flag?
/*
o = hasobwithflagval(lf->pack, F_HOLDCONFER, fid, val0, val1, text);
if (o) {
// return the flag, not the objcet
f = hasflagval(o->flags, F_HOLDCONFER, fid, val0, val1, text);
if (f) return f;
}
for (o = lf->pack->first ; o ; o = o->next) {
f = hasflagval(o->flags, F_HOLDCONFER, fid, val0, val1, text);
if (f) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return f;
}
}
}
*/
// does an equipped object give us this flag?
/*
for (o = lf->pack->first ; o ; o = o->next) {
if (hasflag(o->flags, F_EQUIPPED)) {
f = hasflagval(o->flags, F_EQUIPCONFER, fid, val0, val1, NULL);
if (f) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return f;
}
}
}
}
*/
return NULL;
}
flag_t *lfhasknownflag(lifeform_t *lf, enum FLAG fid) {
flag_t *f;
// note: we check conferred flags first because we want
// these to override ones direclty on the player.
/*
for (o = lf->pack->first ; o ; o = o->next) {
if (isknown(o)) {
f = hasflagval(o->flags, F_HOLDCONFER, fid, NA, NA, NULL);
if (f && f->known) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return f;
}
}
}
}
*/
// does an equipped object give us this flag?
/*
for (o = lf->pack->first ; o ; o = o->next) {
if (isknown(o)) {
if (hasflag(o->flags, F_EQUIPPED)) {
f = hasflagval(o->flags, F_EQUIPCONFER, fid, NA, NA, NULL);
if (f && f->known) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return 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;
/*
for (o = lf->pack->first ; o ; o = o->next) {
if (isknown(o)) {
f = hasflagval(o->flags, F_HOLDCONFER, fid, val0, val1, text);
if (f && f->known) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return f;
}
}
}
}
// does an equipped object give us this flag?
for (o = lf->pack->first ; o ; o = o->next) {
if (isknown(o)) {
if (hasflag(o->flags, F_EQUIPPED)) {
f = hasflagval(o->flags, F_EQUIPCONFER, fid, val0, val1, NULL);
if (f && f->known) {
if (f->val[2] == B_IFIDENT) {
if (isknown(o)) return f;
} else {
return f;
}
}
}
}
}
*/
// got the flag directly?
f = hasflagvalknown(lf->flags, fid, val0, val1, val2, text);
if (f && f->known) return f;
return NULL;
}
// return true on failure
int lockpick(lifeform_t *lf, object_t *target, object_t *device) {
flag_t *f;
char lfname[BUFLEN];
char obname[BUFLEN];
int faileffect;
getlfname(lf,lfname);
getobname(target,obname, 1);
f = hasflag(device->flags, F_PICKLOCKS);
assert(f);
faileffect = f->val[1];
// take time
taketime(lf, getactspeed(lf) );
// TODO: different difficulty based on doors (maybe part of F_LOCKED flag)
if (skillcheck(lf, SC_OPENLOCKS, 15, f->val[0])) {
// success!
// announce
if (isplayer(lf) || cansee(player, lf)) {
msg("%s unlock%s %s.",lfname, isplayer(lf) ? "" : "s", obname);
}
// xp
gainxp(lf, 1);
// unlock it
killflagsofid(target->flags, F_LOCKED);
} else {
// failed!
if (faileffect == B_DIEONFAIL) {
char devname[BUFLEN];
getobname(device,devname, 1);
// kill object
if (isplayer(lf)) {
msg("Your %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) {
makeduller(device, 1);
} else {
if (isplayer(lf) || cansee(player, lf)) {
msg("%s fail%s to unlock %s.",lfname, isplayer(lf) ? "" : "s", obname);
}
}
return B_TRUE;
}
return B_FALSE;
}
void loseobflags(lifeform_t *lf, object_t *o, int kind) {
flag_t *f,*ff,*nextff;
for (f = o->flags->first ; f ; f = f->next) {
int checkthis = B_FALSE;
if (kind == ALLCONFERRED) {
if ((f->id == F_EQUIPCONFER) || (f->id == F_HOLDCONFER) || (f->id == F_ACTIVATECONFER)) {
checkthis = B_TRUE;
}
} else {
if (f->id == kind) {
checkthis = B_TRUE;
}
}
if (checkthis) {
int lifetimecheck;
// probably don't need this now that we are using
// flag->obfrom...
if (f->id == F_EQUIPCONFER) {
lifetimecheck = FROMOBEQUIP;
} else if (f->id == F_ACTIVATECONFER) {
lifetimecheck = FROMOBACTIVATE;
} else { // ie. F_HOLDCONFER
lifetimecheck = FROMOBHOLD;
}
// not using killflagsofid() because we only want
// to kill flags which came from this object
for (ff = lf->flags->first ; ff ; ff = nextff) {
nextff = ff->next;
if ( (ff->id == f->val[0]) &&
(ff->val[0] == f->val[1]) &&
(ff->val[1] == f->val[2]) &&
(ff->lifetime == lifetimecheck) ) {
if (ff->obfrom == o->id) {
killflag(ff);
}
}
}
}
}
}
int haslof(lifeform_t *viewer, cell_t *dest) {
int deltax, deltay;
int numpixels;
int d;
int dinc1,dinc2,xinc1,xinc2,yinc1,yinc2;
int xinc,yinc,dinc;
int i;
int x1,y1;
int x;
int y;
//int maxvisrange;
//int lightrange;
//int xray = 0;
//int wentuphill = B_FALSE;
//int origheight;
int x2,y2;
map_t *map;
reason = E_OK;
// must have line of sight to fire...
if (!haslos(viewer, dest)) {
reason = E_NOLOS;
return B_FALSE;
}
if (viewer->cell->map != dest->map) {
reason = E_NOLOS;
return B_FALSE;
}
map = dest->map;
x1 = viewer->cell->x;
y1 = viewer->cell->y;
x2 = dest->x;
y2 = dest->y;
deltax = (x2 - x1);
if (deltax < 0) deltax = -deltax;
deltay = (y2 - y1);
if (deltay < 0) deltay = -deltay;
// can always fire at your own cell
if ((deltax == 0) && (deltay == 0)) {
return B_TRUE;
}
if (deltax >= deltay) {
numpixels = deltax + 1;
d = (deltay*2) - deltax;
dinc1 = deltay << 1;
dinc2 = (deltay-deltax) << 1;
xinc1 = 1;
xinc2 = 1;
yinc1 = 0;
yinc2 = 1;
} else {
numpixels = deltay + 1;
d = (deltax*2) - deltay;
dinc1 = deltax << 1;
dinc2 = (deltax - deltay) << 1;
xinc1 = 0;
xinc2 = 1;
yinc1 = 1;
yinc2 = 1;
}
if (x1 > x2) {
xinc1 = - xinc1;
xinc2 = - xinc2;
}
if (y1 > y2) {
yinc1 = - yinc1;
yinc2 = - yinc2;
}
x = x1; y = y1;
for (i = 0; i < numpixels ; i++) {
cell_t *cell;
object_t *blockob;
/*
// we _DO_ need to move out of last cell for line of fire.
if ((x == x2) && (y == y2)) {
break;
}
*/
if (d < 0) {
xinc = xinc1;
yinc = yinc1;
dinc = dinc1;
} else {
xinc = xinc2;
yinc = yinc2;
dinc = dinc2;
}
// solid cells stop lof
cell = getcellat(map, x, y);
if (cell->type->solid) {
reason = E_NOLOF;
return B_FALSE;
}
// lifeforms block lof unless they're in our destination
if (cell->lf) {
// if not in first cell...
if ((x != x1) || (y != y1)) {
// if not in last cell...
if ((x != x2) || (y != y2)) {
reason = E_NOLOF;
return B_FALSE;
}
}
}
// certain objects block lof
blockob = hasobwithflag(cell->obpile, F_BLOCKSTHROW);
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;
}
}
// move to next cell
d += dinc;
x += xinc;
y += yinc;
}
// made it to the target cell!
return B_TRUE;
}
int haslos(lifeform_t *viewer, cell_t *dest) {
int deltax, deltay;
int numpixels;
int d;
int dinc1,dinc2,xinc1,xinc2,yinc1,yinc2;
int xinc,yinc,dinc;
int i;
int x1,y1;
int x;
int y;
int maxvisrange;
int nightvisrange;
int xray = 0;
//int wentuphill = B_FALSE;
//int origheight;
//int shopwall;
flag_t *f;
int x2,y2;
map_t *map;
object_t *o;
if (!dest) return B_FALSE;
// let the player see when dead, otherwise the screen wil
// go black when "You die" appears.
if (isdead(viewer) && !isplayer(viewer)) return B_FALSE;
if (viewer->cell->map != dest->map) return B_FALSE;
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;
}
deltax = (x2 - x1);
if (deltax < 0) deltax = -deltax;
deltay = (y2 - y1);
if (deltay < 0) deltay = -deltay;
// can always see your own cell
if ((deltax == 0) && (deltay == 0)) {
//if (viewer->controller == C_HUMAN) wreck->mazelev[z].maze[y2*MAZEW+x2].known = B_PERM;
return B_TRUE;
}
// can't see if you're blind
if (lfhasflag(viewer, F_BLIND)) {
return B_FALSE;
}
/*
if (hasproplf(viewer, P_BLINDED)) {
return B_FALSE;
}
*/
if (deltax >= deltay) {
numpixels = deltax + 1;
d = (deltay*2) - deltax;
dinc1 = deltay << 1;
dinc2 = (deltay-deltax) << 1;
xinc1 = 1;
xinc2 = 1;
yinc1 = 0;
yinc2 = 1;
} else {
numpixels = deltay + 1;
d = (deltax*2) - deltay;
dinc1 = deltax << 1;
dinc2 = (deltax - deltay) << 1;
xinc1 = 0;
xinc2 = 1;
yinc1 = 1;
yinc2 = 1;
}
if (x1 > x2) {
xinc1 = - xinc1;
xinc2 = - xinc2;
}
if (y1 > y2) {
yinc1 = - yinc1;
yinc2 = - yinc2;
}
x = x1; y = y1;
//maxvisrange = getvisrange(viewer, B_LIGHTDOESNTMATTER);
maxvisrange = getvisrange(viewer);
nightvisrange = getnightvisrange(viewer);
/*
if (hasproplf(viewer, P_SEEINDARK) || wreck->mazelev[viewer->z].lightlevel >= 100) {
lightrange = maxvisrange;
} else {
lightrange = getvisrange(viewer, B_LIGHTMATTERS);
}
*/
//origheight = getheight(x1,y1,z);
// too far away
if (maxvisrange != UNLIMITED) {
if (getcelldist(viewer->cell, dest) > maxvisrange) {
return B_FALSE;
}
}
// outside the range of our light, and not lit
if (nightvisrange != UNLIMITED) {
if (getcelldist(viewer->cell, dest) > nightvisrange) {
if (!islit(dest)) {
return B_FALSE;
}
}
}
//shopwall = B_FALSE;
for (i = 0; i < numpixels ; i++) {
cell_t *cell;
// don't need to move out of the last one
if ((x == x2) && (y == y2)) {
break;
}
if (d < 0) {
xinc = xinc1;
yinc = yinc1;
dinc = dinc1;
} else {
xinc = xinc2;
yinc = yinc2;
dinc = dinc2;
}
// you can always see your own cell
if (i != 0) {
// if we went uphill, stop here
/*
if (wentuphill) {
return B_FALSE;
}
*/
/*
if (debug) {
printf("checking LOS at %d,%d going (%d,%d)\n",x,y,xinc,yinc);
fflush(stdout);
}
*/
// solid cells stop los - but if you are standing on a solid
// cell you can still see out.
cell = getcellat(map, x, y);
if (!cell->type->transparent) {
if (xray) xray--;
else return B_FALSE;
}
/*
// check for smoke
if ((x != x1) || (y != y1)) { // if not in first cell
if (hasopaqueobject(viewer, x,y,z) && (getheight(x,y,z) >= origheight)) {
if (!hasproplf(viewer, P_SEEINSMOKE)) {
return B_FALSE;
}
}
}
*/
// check for objects which block view
for (o = cell->obpile->first ; o ; o = o->next) {
if (hasflag(o->flags, F_BLOCKSVIEW)) {
if (!lfhasflagval(viewer, F_CANSEETHROUGHMAT, o->material->id, NA, NA, NULL)) {
if (xray) xray--;
else return B_FALSE;
}
}
}
// 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;
}
*/
}
// move to next cell
d += dinc;
x += xinc;
y += yinc;
}
// made it to the target cell!
/*
if (viewer->controller == C_HUMAN) wreck->mazelev[z].maze[y2*MAZEW+x2].known = B_PERM;
if (viewer->controller == C_HUMAN) wreck->mazelev[z].maze[y2*MAZEW+x2].known = B_PERM;
*/
return B_TRUE;
}
int hasmr(lifeform_t *lf) {
if (lfhasflag(lf, F_RESISTMAG)) {
return B_TRUE;
}
return B_FALSE;
}
void initjobs(void) {
int i;
// job definitions
// NOTE: try to always make the job's weapon be the first object defined.
// this will make sure that they have the letter 'a'.
addjob(J_GOD, "Diety");
//addflag(lastjob->flags, F_OMNIPOTENT, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_MPDICE, 100, NA, NA, NULL);
addflag(lastjob->flags, F_MPREGEN, 100, NA, NA, NULL);
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "short sword of pyromania");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "hand of god");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 blocks of chocolate");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 vials of ambrosia");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather boots");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather gloves");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "graph paper");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "a digital watch");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 blessed scrolls of create monster");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 blessed potions of experience");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "ring of invulnerability");
addflag(lastjob->flags, F_STARTSKILL, SK_SPELLCASTING, PR_MASTER, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_RESEARCH, PR_NOVICE, NA, NULL);
// gods may use all abilities and cast any spell at will
for (i = SS_NONE+1; i < SS_LAST; i++) {
if ((i == SS_ABILITY) || (i == SS_DIVINE)) {
mayusespellschool(lastjob->flags, i, F_CANWILL);
} else {
mayusespellschool(lastjob->flags, i, F_CANCAST);
}
}
addjob(J_ADVENTURER, "Adventurer");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "short sword");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "2 bananas");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 gold coins");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "3 potions of healing");
addflag(lastjob->flags, F_MPDICE, 1, NA, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_LOCKPICKING, PR_NOVICE, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_ATHLETICS, PR_NOVICE, NA, NULL);
addjob(J_ALLOMANCER, "Allomancer");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "1 gold coins");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "club");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "cloak");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "potion of magic");
addflag(lastjob->flags, F_MPDICE, 1, 1, NA, NULL);
//addflag(lastjob->flags, F_MPDICE, 5, 6, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_ABSORBMETAL, NA, NA, NULL);
addflag(lastjob->flags, F_STARTATT, A_STR, ST_AVERAGE, NA, NULL);
addflag(lastjob->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastjob->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastjob->flags, F_DETECTMETAL, B_TRUE, NA, NA, NULL);
mayusespellschool(lastjob->flags, SS_ALLOMANCY, F_CANCAST);
addflag(lastjob->flags, F_STARTSKILL, SK_LOCKPICKING, PR_NOVICE, NA, NULL);
addjob(J_PLUMBER, "Plumber");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "spanner");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 gold coins");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "overalls");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "cap");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "3-5 mushrooms");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "rubber boots");
addflag(lastjob->flags, F_EVASION, 30, NA, NA, NULL);
addflag(lastjob->flags, F_OBESE, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_CANWILL, OT_A_JUMP, 3, 3, NULL);
addjob(J_PRINCE, "Prince");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "blessed ornamental sword");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "100 gold coins");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "golden crown");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "ornamental dagger");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "velvet robe");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "silk shirt");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "riding trousers");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather boots");
// TODO skill: court knowledge ??
// TODO skill: bartering 2
addjob(J_COMMANDO, "Commando");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "combat knife");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "revolver");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "helmet");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "flak jacket");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "combat pants");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "leather boots");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "nightvis goggles");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "1-2 blocks of c4");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "1-3 grenades");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "1-3 flashbangs");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "digital watch");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 bullets");
addflag(lastjob->flags, F_STARTATT, A_STR, ST_STRONG, NA, NULL);
addflag(lastjob->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_ATHLETICS, PR_ADEPT, NA, NULL);
// TODO skill: athletics 2
addjob(J_WIZARD, "Wizard");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "knife");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "robe");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "2 potions of magic");
addflag(lastjob->flags, F_CANCAST, OT_S_MANASPIKE, NA, NA, NULL);
/*
addflag(lastjob->flags, F_IFPLAYER, NA, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 50, NA, NA, NULL);
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "spellbook of flame dart");
addflag(lastjob->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "spellbook of cone of cold");
addflag(lastjob->flags, F_ENDIFPLAYER, NA, NA, NA, NULL);
*/
addflag(lastjob->flags, F_IFMONSTER, NA, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 50, NA, NA, NULL); // monsters sometimes start with a random book
addflag(lastjob->flags, F_STARTOBCLASS, 100, OC_BOOK, NA, NULL);
addflag(lastjob->flags, F_ENDIFMONSTER, NA, NA, NA, NULL);
addflag(lastjob->flags, F_HITDICE, 1, 1, NA, NULL); // low hp
addflag(lastjob->flags, F_MPDICE, 2, 1, NA, NULL);
addflag(lastjob->flags, F_STARTATT, A_STR, ST_WEAK, NA, NULL);
addflag(lastjob->flags, F_STARTATT, A_DEX, DX_AVERAGE, NA, NULL);
addflag(lastjob->flags, F_STARTATT, A_IQ, IQ_ENLIGHTENED, NA, NULL);
// wizard heals slowly, but regenerates mp
addflag(lastjob->flags, F_RESTHEALTIME, 6, NA, NA, NULL);
addflag(lastjob->flags, F_MPREGEN, 1, NA, NA, NULL);
// can detect magic objects
addflag(lastjob->flags, F_DETECTMAGIC, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_RESEARCH, PR_BEGINNER, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_SPELLCASTING, PR_NOVICE, NA, NULL);
// TODO skill: magic knowledge
// for monster wizards only:
addflag(lastjob->flags, F_IFMONSTER, NA, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 50, NA, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_FIREDART, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 50, NA, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_CONECOLD, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 33, NA, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_HEALINGMIN, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 33, NA, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_BLINK, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 33, NA, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_TELEKINESIS, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 20, NA, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_HASTE, NA, NA, NULL);
addflag(lastjob->flags, F_IFPCT, 20, NA, NA, NULL);
addflag(lastjob->flags, F_CANCAST, OT_S_HEALING, NA, NA, NULL);
addflag(lastjob->flags, F_ENDIFMONSTER, NA, NA, NA, NULL);
}
void initrace(void) {
// races / monsters
addrace(R_HUMAN, "human", 75, '@', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 2, 2, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, NA, NA, NA, "fists");
addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "1-50 gold coins");
addflag(lastrace->flags, F_STARTOBCLASS, 65, OC_WEAPON, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 65, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 65, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 65, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_SCROLL, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_SCROLL, NA, NULL);
// TODO: humans start with a random job sometimes?
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
// addflag(lastrace->flags, F_RESTHEALMPAMT, 1, NA, NA, NULL);
// monsters
addrace(R_BUGBEAR, "bugbear", 120, 'G', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 17, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 10, NA, "claws");
addflag(lastrace->flags, F_STARTATT, A_STR, ST_STRONG, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "heavy flail");
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "roars^a roars");
addflag(lastrace->flags, F_SEEINDARK, 3, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_HEAVYBLOW, 2, 2, NULL);
addrace(R_DARKMANTLE, "darkmantle", 70, 'Q', MT_FLESH);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_SUPERSONIC, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_CON, CN_HARDY, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 9, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 10, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 15, NA, NA, NULL);
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_MOVESPEED, SP_VERYSLOW, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLSPEED, SP_VERYSLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_VERYSLOW, NA, NA, "");
addflag(lastrace->flags, F_CANWILL, OT_S_DARKNESS, 7, 7, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_GRAB, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_CRUSH, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "waves its tentacles");
addflag(lastrace->flags, F_HASATTACK, 2, 6, NA, "tentacle");
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_LIGHT, NA, NA, "0d6+5");
addrace(R_EYEBAT, "eyebat", 5, 'e', MT_FLESH);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_SMART, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, 3, NA, NULL);
addflag(lastrace->flags, F_EVASION, 20, NA, NA, NULL);
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLSPEED, SP_VERYSLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_MPDICE, 0, 25, NA, NULL);
addflag(lastrace->flags, F_MPREGEN, 12, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_DISPERSAL, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_GRAVBOOST, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "gazes at you");
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_EXPERT, NA, NULL);
addrace(R_GIANTHILL, "hill giant", 160, 'H', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 55, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUGE, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 19, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 12, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, -20, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 2, 5, NA, "fists");
addflag(lastrace->flags, F_STARTOB, 90, NA, NA, "25-100 gold coins");
addflag(lastrace->flags, F_STARTOB, 70, NA, NA, "1-2 boulders");
addflag(lastrace->flags, F_IFPCT, 70, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "great club");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 70, NA, NA, "club");
addflag(lastrace->flags, F_STARTOBCLASS, 65, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 65, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_CLUMSY, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "bellows^a bellow");
addflag(lastrace->flags, F_CANWILL, OT_A_HEAVYBLOW, NA, NA, NULL);
addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL);
addrace(R_GIANTFIRE, "fire giant", 160, 'H', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUGE, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 21, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 13, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, -20, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 2, 5, NA, "fists");
addflag(lastrace->flags, F_STARTOB, 90, NA, NA, "25-100 gold coins");
addflag(lastrace->flags, F_IFPCT, 70, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming greatsword");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming longsword");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "javelin");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "javelin");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "javelin");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "javelin");
addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "plate mail");
addflag(lastrace->flags, F_STARTOBCLASS, 70, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "bellows^a bellow");
addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL);
addrace(R_GIANTFIREFC, "fire giant forgecaller", 160, 'H', MT_FLESH);
lastrace->baseid = R_GIANTFIRE;
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "fire giant corpse");
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUGE, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 17, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 12, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, -20, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 2, 5, NA, "fists");
addflag(lastrace->flags, F_STARTOB, 90, NA, NA, "25-100 gold coins");
addflag(lastrace->flags, F_IFPCT, 70, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming morningstar");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming mace");
addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "plate mail");
addflag(lastrace->flags, F_STARTOBCLASS, 70, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "bellows^a bellow");
addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL);
addflag(lastrace->flags, F_MPDICE, 0, 9, NA, NULL);
addflag(lastrace->flags, F_MPREGEN, 1, NA, NA, NULL);
addflag(lastrace->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_FIREDART, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_FLAMEPILLAR, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_S_FLAMEBURST, 4, 4, NULL);
addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_ADEPT, NA, NULL);
addrace(R_GIANTFIRETITAN, "fire titan", 160, 'H', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 40, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_ENORMOUS, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 49, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 20, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, -30, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 2, 5, 8, "fists");
addflag(lastrace->flags, F_STARTOB, 90, NA, NA, "100-300 gold coins");
addflag(lastrace->flags, F_IFPCT, 65, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming greatsword of pyromania");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flaming greatsword");
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_SWIFT, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "bellows^a bellow");
addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL);
addflag(lastrace->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_S_BURNINGWAVE, 3, 3, NULL);
addflag(lastrace->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL);
// TODO: storm giant
// TODO: storm titan
addrace(R_GNOLL, "gnoll", 130, 'h', MT_FLESH);
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "gnoll corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NUMAPPEAR, 2, 3, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 8, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 9, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_STRONG, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 6, 4, "claws");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-40 gold coins");
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 5, NA, 2, "gnoll");
addrace(R_GNOLLHM, "gnoll huntmaster", 130, 'h', MT_FLESH);
lastrace->baseid = R_GNOLL;
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "gnoll corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 6, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 9, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_STRONG, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 6, 4, "claws");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "longbow");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "10-20 arrows");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "hand axe");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-40 gold coins");
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 5, NA, 2, "gnoll");
addrace(R_GNOLLMR, "gnoll marauder", 130, 'h', MT_FLESH);
lastrace->baseid = R_GNOLL;
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "gnoll corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 10, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 9, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_STRONG, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 4, 2, "claws");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "spear");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "buckler");
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL);
addflag(lastrace->flags, F_QUICKBITE, 1, 6, 2, NULL);
addflag(lastrace->flags, F_PACKATTACK, 5, NA, 2, "gnoll");
addrace(R_GOBLIN, "goblin", 20, 'g', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, 3, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 4, NA, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "short sword");
addflag(lastrace->flags, F_STARTOBCLASS, 40, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 3, NA, NA, NULL);
//addflag(lastrace->flags, F_STARTJOB, 25, J_WIZARD, NA, NULL);
addflag(lastrace->flags, F_DODGES, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 2, DT_CLAW, 3, "goblin");
addrace(R_GOBLINWAR, "goblin warrior", 30, 'g', MT_FLESH);
lastrace->baseid = R_GOBLIN;
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "goblin corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 3, 5, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 4, NA, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "spear");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "1-5 javelins");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 3, NA, NA, NULL);
addflag(lastrace->flags, F_DODGES, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 2, DT_CLAW, 3, "goblin");
addrace(R_GOBLINSHOOTER, "goblin sharpshooter", 20, 'g', MT_FLESH);
lastrace->baseid = R_GOBLIN;
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "goblin corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 3, 7, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 4, NA, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "short sword");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "hand crossbow");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "1-15 bolts");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL);
addflag(lastrace->flags, F_DODGES, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 2, DT_CLAW, 3, "goblin");
// TODO: can hide in shadows (need cansee function first)
addrace(R_GOBLINHEXER, "goblin hexer", 20, 'g', MT_FLESH);
lastrace->baseid = R_GOBLIN;
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "goblin corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 5, 6, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 4, NA, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "club");
addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "leather armour");
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL);
addflag(lastrace->flags, F_DODGES, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 2, DT_CLAW, 3, "goblin");
addflag(lastrace->flags, F_MPDICE, 0, 10, NA, NULL);
addflag(lastrace->flags, F_MPREGEN, 3, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_BLINDNESS, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_PAIN, NA, NA, NULL);
addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_ADEPT, NA, NULL);
addrace(R_HOBGOBLIN, "hobgoblin", 90, 'g', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastrace->flags, F_NUMAPPEAR, 1, 2, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 2, 3, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 8, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 6, NA, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_SMART, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "longsword");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "buckler");
addflag(lastrace->flags, F_STARTOBCLASS, 100, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 10, NA, NA, NULL);
addflag(lastrace->flags, F_PHALANX, 5, NA, 1, "hobgoblin");
addrace(R_HOBGOBLINWAR, "hobgoblin warrior", 90, 'g', MT_FLESH);
lastrace->baseid = R_HOBGOBLIN;
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_NUMAPPEAR, 1, 2, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 2, 3, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 10, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 6, NA, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_SMART, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "flail");
addflag(lastrace->flags, F_STARTOB, 90, NA, NA, "large shield");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "scale armour");
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_ARMOUR, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 10, NA, NA, NULL);
addflag(lastrace->flags, F_PHALANX, 8, NA, 1, "hobgoblin");
addflag(lastrace->flags, F_NOFLEE, B_TRUE, NA, NA, NULL);
// TODO: hobgoblin archer
// TODO: hobgoblin warcaster
addrace(R_KOBOLD, "kobold", 18, 'k', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastrace->flags, F_NUMAPPEAR, 1, 3, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 15, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 0, 1, 4, "claws");
addflag(lastrace->flags, F_STARTATT, A_STR, ST_WEAK, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_NIMBLE, NA, NULL);
addflag(lastrace->flags, F_STARTOBDT, 20, DT_PIERCE, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 40, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-5 darts");
addflag(lastrace->flags, F_STARTOB, 33, NA, NA, "1-3 javelins");
addflag(lastrace->flags, F_STARTOB, 33, NA, NA, "leather armour");
addflag(lastrace->flags, F_STARTOB, 33, NA, NA, "buckler");
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL);
addrace(R_TROGLODYTE, "troglodyte", 20, 'z', MT_FLESH);
addflag(lastrace->flags, F_COLDBLOOD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DIMWITTED, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 2, 0, "fists");
addflag(lastrace->flags, F_HASATTACK, 1, 2, 0, "tail");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "club");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "buckler");
addflag(lastrace->flags, F_DTVULN, DT_COLD, B_TRUE, NA, NULL);
addflag(lastrace->flags, F_DTRESIST, DT_FIRE, B_TRUE, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, 3, NA, NA, NULL);
addflag(lastrace->flags, F_STENCH, 2, 2, NA, NULL);
addrace(R_LIZARDMAN, "lizardman", 100, 'z', MT_FLESH);
addflag(lastrace->flags, F_COLDBLOOD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 6, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 15, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 5, 1, "claws");
addflag(lastrace->flags, F_HASATTACK, 1, 4, 0, "tail");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "spear");
addflag(lastrace->flags, F_DTVULN, DT_COLD, B_TRUE, NA, NULL);
addflag(lastrace->flags, F_DTRESIST, DT_FIRE, B_TRUE, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, 3, NA, NA, NULL);
addrace(R_LURKINGHORROR, "lurking horror", 100, 'U', MT_FLESH);
addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 3, 0, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
addflag(lastrace->flags, F_INDUCEFEAR, B_TRUE, NA, NA, NULL);
addrace(R_OGRE, "ogre", 160, 'O', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 7, 0, NA, NULL);
addflag(lastrace->flags, F_EVASION, -5, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 11, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 2, 4, 0, "fists");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_STUPID, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_IFPCT, 80, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "great club");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "morningstar");
addflag(lastrace->flags, F_STARTOB, 70, NA, NA, "leather armour");
addrace(R_OGRESAVAGE, "ogre savage", 160, 'O', MT_FLESH);
lastrace->baseid = R_OGRE;
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 55, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 13, 8, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 11, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 3, 4, 0, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_STUPID, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL);
addflag(lastrace->flags, F_IFPCT, 80, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "blessed great club");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "great club");
addflag(lastrace->flags, F_STARTOB, 80, NA, NA, "leather armour");
addrace(R_OGREWARHULK, "ogre warhulk", 160, 'O', MT_FLESH);
lastrace->baseid = R_OGRE;
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 35, 0, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 11, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 2, 4, NA, "fists");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_STUPID, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_DEX, DX_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_MIGHTY, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL);
addflag(lastrace->flags, F_IFPCT, 80, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "heavy flail");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "great club");
addflag(lastrace->flags, F_STARTOB, 70, NA, NA, "leather armour");
addflag(lastrace->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL);
addrace(R_ORC, "orc", 90, 'o', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, 3, NA, NULL);
addflag(lastrace->flags, F_EVASION, 5, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 5, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 4, 0, "claws");
addflag(lastrace->flags, F_IFPCT, 70, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "club");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOBDT, 50, DT_SLASH, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "leather armour");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "bone helmet");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-50 gold coins");
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL);
//addflag(lastrace->flags, F_STARTJOB, 20, J_WIZARD, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_SPRINT, 5, 5, NULL);
addrace(R_ORCWARRIOR, "orc warrior", 90, 'o', MT_FLESH);
lastrace->baseid = R_ORC;
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 2, 3, NA, NULL);
addflag(lastrace->flags, F_EVASION, 5, NA, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DOPEY, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 4, 0, "claws");
addflag(lastrace->flags, F_IFPCT, 70, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "battleaxe");
addflag(lastrace->flags, F_ELSE, NA, NA, NA, NULL);
addflag(lastrace->flags, F_STARTOBDT, 50, DT_CHOP, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastrace->flags, F_STARTOB, 75, NA, NA, "buckler");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "bone helmet");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-50 gold coins");
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL);
//addflag(lastrace->flags, F_STARTJOB, 20, J_WIZARD, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL);
addrace(R_ORK, "ork", 90, 'o', MT_FLESH);
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "orc corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 3, 3, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 4, 0, "claws");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTOBDT, 100, DT_SLASH, NA, NULL);
addflag(lastrace->flags, F_STARTOBDT, 30, DT_BASH, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 30, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "flak jacket");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "leather boots");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "football helmet");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-25 gold coins");
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_POTION, NA, NULL);
addflag(lastrace->flags, F_STARTOBCLASS, 50, OC_TECH, NA, NULL);
addflag(lastrace->flags, F_WANTS, OT_GOLD, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_OPERABLE, B_COVETS, NA, NULL); // ie. tech
addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 3, NA, NA, NULL);
addrace(R_POLTERGEIST, "poltergeist", 50, 'p', MT_FLESH);
addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 2, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLSPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_MPDICE, 0, 4, NA, NULL);
addflag(lastrace->flags, F_MPREGEN, 4, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_TELEKINESIS, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "gestures");
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_EXPERT, NA, NULL);
addrace(R_SHADOWCAT, "shadowcat", 5, 'f', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 2, 3, NA, NULL);
addflag(lastrace->flags, F_EVASION, 40, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 4, 0, "claws");
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "hisses^a hiss");
addflag(lastrace->flags, F_SEEINDARK, 8, NA, NA, NULL);
addflag(lastrace->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL);
addflag(lastrace->flags, F_AUTOCREATEOB, 1, NA, NA, "cloud of smoke");
addrace(R_OOZEGREY, "grey ooze", 10, 'j', MT_SLIME);
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "pool of slime");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 5, 0, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_EYES, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 6, 5, "acidattack");
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
addflag(lastrace->flags, F_SEEWITHOUTEYES, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_ACID, B_TRUE, NA, NULL);
addrace(R_SPRITEFIRE, "fire sprite", 5, 'n', MT_FIRE);
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "small fire");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_AVERAGE, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 15, NA, NA, NULL);
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_MPDICE, 0, 4, NA, NULL);
addflag(lastrace->flags, F_MPREGEN, 1, NA, NA, NULL);
addflag(lastrace->flags, F_CANCAST, OT_S_FIREDART, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "gestures");
addflag(lastrace->flags, F_HASATTACK, 1, 3, 0, "claws");
addflag(lastrace->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_NOVICE, NA, NULL);
addrace(R_TROLL, "troll", 100, 't', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 3, 0, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DIMWITTED, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_STRONG, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 6, 0, "claws");
addflag(lastrace->flags, F_REGENERATES, 2, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL);
addrace(R_XAT, "xat", 2, 'x', MT_FLESH);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_TINY, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, 0, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_DIMWITTED, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_WEAK, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 3, 0, "teeth");
// end monsters
// small animals
addrace(R_NEWT, "newt", 0.1, ':', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastrace->flags, F_COLDBLOOD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_ANIMAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, 0, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MINI, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_FLEEONDAM, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_DTRESIST, DT_FIRE, B_TRUE, NA, NULL);
addrace(R_BAT, "bat", 1, 'B', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 90, NA, "");
addflag(lastrace->flags, F_ANIMAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_VERYFAST, NA, NA, "");
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_HITDICE, 0, 2, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 2, 0, "teeth");
addflag(lastrace->flags, F_EVASION, 30, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
addrace(R_RAT, "giant rat", 0.2, 'r', MT_FLESH);
addflag(lastrace->flags, F_ANIMAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_VWEAK, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 90, NA, "");
addflag(lastrace->flags, F_HITDICE, 0, 1, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 2, 0, "teeth");
addflag(lastrace->flags, F_HASATTACK, 1, 3, 0, "claws");
addflag(lastrace->flags, F_EVASION, 20, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, 4, NA, NA, NULL);
addrace(R_WOLF, "wolf", 10, 'd', MT_FLESH);
addflag(lastrace->flags, F_ANIMAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 80, NA, "");
addflag(lastrace->flags, F_HITDICE, 2, 0, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 5, 0, "teeth");
addflag(lastrace->flags, F_HASATTACK, 1, 5, 0, "claws");
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_SPRINT, 5, 5, NULL);
// insects
addrace(R_BUTTERFLY, "butterfly", 0.01, 'i', MT_FLESH);
addflag(lastrace->flags, F_INSECT, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_VERYFAST, NA, NA, "");
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_TINY, NA, NA, NULL);
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_HITDICE, 0, 1, NA, NULL);
addflag(lastrace->flags, F_EVASION, 60, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_POISONGAS, NA, NA, NULL);
addrace(R_GIANTFLY, "giant fly", 1, 'i', MT_FLESH);
addflag(lastrace->flags, F_INSECT, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_NUMAPPEAR, 3, 3, NA, "");
addflag(lastrace->flags, F_MOVESPEED, SP_VERYFAST, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 85, NA, "");
addflag(lastrace->flags, F_HITDICE, 1, 0, NA, "");
addflag(lastrace->flags, F_EVASION, 50, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 2, 0, "teeth");
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "buzzes angrily^an angry buzzing");
addflag(lastrace->flags, F_DTVULN, DT_POISONGAS, NA, NA, NULL);
addrace(R_GIANTBLOWFLY, "giant blowfly", 2, 'i', MT_FLESH);
addflag(lastrace->flags, F_INSECT, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "giant fly corpse");
addflag(lastrace->flags, F_NUMAPPEAR, 1, 2, NA, "");
addflag(lastrace->flags, F_MOVESPEED, SP_VERYFAST, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_FAST, NA, NA, "");
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 60, NA, "");
addflag(lastrace->flags, F_HITDICE, 1, 1, NA, "");
addflag(lastrace->flags, F_EVASION, 40, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 3, 0, "teeth");
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, NA, NA, "buzzes angrily^an angry buzzing");
addflag(lastrace->flags, F_DTVULN, DT_POISONGAS, NA, NA, NULL);
addrace(R_GLOWBUG, "glowbug", 1, 'i', MT_FLESH);
addflag(lastrace->flags, F_INSECT, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_VERYFAST, NA, NA, "");
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 85, NA, "");
addflag(lastrace->flags, F_HITDICE, 1, 0, NA, "");
addflag(lastrace->flags, F_EVASION, 60, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 2, 0, "zapper");
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_POISONGAS, NA, NA, NULL);
// undead
addrace(R_ZOMBIE, "zombie", 50, 'Z', MT_FLESH);
addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL);
addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL);
addflag(lastrace->flags, F_NUMAPPEAR, 2, 4, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 5, 4, NA, NULL);
addflag(lastrace->flags, F_ARMOURRATING, 5, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, 1, 4, NA, "claws");
addflag(lastrace->flags, F_HASATTACK, 1, 5, NA, "teeth");
addflag(lastrace->flags, F_CANWILL, OT_A_GRAB, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_DECAY, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_HOLY, NA, NA, NULL);
addrace(R_SKELETON, "skeleton", 20, 'Z', MT_BONE);
addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL);
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "5-20 bones");
addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 2, 4, NA, NULL);
addflag(lastrace->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 5, NA, "teeth");
addflag(lastrace->flags, F_STARTOBDT, 50, DT_CHOP, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_DECAY, NA, NA, NULL);
addflag(lastrace->flags, F_DTRESIST, DT_SLASH, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_BASH, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_HOLY, NA, NA, NULL);
addrace(R_GHAST, "ghast", 50, 'Z', MT_FLESH);
addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_SMART, NA, NULL);
addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL);
addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 55, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 4, 2, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 4, 4, "teeth");
addflag(lastrace->flags, F_HASATTACK, NA, NA, NA, "strong paralyzing touch");
addflag(lastrace->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_DECAY, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_HOLY, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, NA, NA, NULL);
addrace(R_GHOUL, "ghoul", 50, 'Z', MT_FLESH);
addflag(lastrace->flags, F_UNDEAD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL);
addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL);
addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 3, 2, NA, NULL);
addflag(lastrace->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_SLOW, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_SLOW, NA, NA, "");
addflag(lastrace->flags, F_HASATTACK, 1, 5, 3, "teeth");
addflag(lastrace->flags, F_HASATTACK, NA, NA, NA, "paralyzing touch");
addflag(lastrace->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_POISONGAS, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_DECAY, NA, NA, NULL);
addflag(lastrace->flags, F_DTVULN, DT_HOLY, NA, NA, NULL);
addflag(lastrace->flags, F_WANTSOBFLAG, F_EDIBLE, B_COVETS, NA, NULL);
// special monsters
addrace(R_GASCLOUD, "cloud of gas", 0.1, '}', MT_GAS);
addflag(lastrace->flags, F_MOVESPEED, SP_ULTRAFAST, NA, NA, "");
addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL);
addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_MINDLESS, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL);
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_HITDICE, 1, 1, NA, "");
addflag(lastrace->flags, F_EVASION, 300, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_WEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_BODY, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, 4, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_ALL, NA, NA, NULL);
addrace(R_DANCINGWEAPON, "dancing weapon", 0, ')', MT_METAL);
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
addflag(lastrace->flags, F_FLYING, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_HITDICE, 2, NA, NA, "");
addflag(lastrace->flags, F_EVASION, 50, NA, NA, NULL);
addflag(lastrace->flags, F_BLOODOB, NA, NA, NA, NULL);
addflag(lastrace->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SECWEAPON, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_RIGHTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HANDS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_HEAD, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_BODY, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_SHOULDERS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_LEGS, NA, NA, NULL);
addflag(lastrace->flags, F_NOBODYPART, BP_FEET, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
}
int isairborne(lifeform_t *lf) {
if (lfhasflag(lf, F_FLYING)) {
return B_TRUE;
} else if (lfhasflag(lf, F_LEVITATING)) {
return B_TRUE;
}
return B_FALSE;
}
int isbleeding(lifeform_t *lf) {
float hppct;
hppct = ((float)lf->hp / (float) lf->maxhp) * 100;
if (hppct <= 30) return B_TRUE;
return B_FALSE;
}
int isblind(lifeform_t *lf) {
if (!lf) return B_FALSE;
if (lfhasflag(lf, F_BLIND)) {
return B_TRUE;
}
if (lfhasflagval(lf, F_NOBODYPART, BP_EYES, NA, NA, NULL)) {
if (!lfhasflag(lf, F_SEEWITHOUTEYES)) {
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 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;
m = lf->cell->map;
// remove references
lf->cell->lf = NULL;
// shouldn't need this...
lf->cell = NULL;
// check if anyone is targetting us.
// if so, stop targetting us now that
// we are dead.
// TODO: check on all maps?
for (l = m->lf ; l ; l = l->next) {
f = lfhasflagval(l, F_TARGET, 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);
}
// free mem
if (lf->lastdam) free(lf->lastdam);
// kill any remaining obs
while (lf->pack->first) {
killob(lf->pack->first);
}
// kill object pile
free(lf->pack);
// kill flags
killflagpile(lf->flags);
// remove from list
nextone = lf->next;
if (nextone != NULL) {
nextone->prev = lf->prev;
} else { /* last */
m->lastlf = lf->prev;
}
if (lf->prev == NULL) {
/* first */
nextone = lf->next;
free(m->lf);
m->lf = nextone;
} else {
lastone = lf->prev;
free (lastone->next );
lastone->next = nextone;
}
}
int 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 ((bp == BP_WEAPON) || (bp == BP_SECWEAPON)) {
object_t *o;
enum BODYPART otherbp;
// check for 2handed weapons in other hand too
if (bp == BP_WEAPON) otherbp = BP_SECWEAPON;
else otherbp = BP_WEAPON;
o = hasobwithflagval(lf->pack, F_EQUIPPED, otherbp, NA, NA, NULL);
if (o && hasflag(o->flags, F_TWOHANDED)) {
return B_FALSE;
}
}
return B_TRUE;
}
int isfriendly(lifeform_t *lf) {
if (lfhasflag(lf, F_FRIENDLY)) {
return B_TRUE;
}
return B_FALSE;
}
int isgenius(lifeform_t *lf) {
enum IQBRACKET iqb;
iqb = getiqname(getattr(lf, A_IQ), NULL);
if (lfhasflag(lf, F_OMNIPOTENT) ||
lfhasflag(lf, F_EXTRAINFO) ||
iqb >= IQ_GENIUS) {
return B_TRUE;
}
return B_FALSE;
}
flag_t *isimmuneto(flagpile_t *fp, enum DAMTYPE dt) {
flag_t *f;
f = hasflagval(fp, F_DTIMMUNE, dt, NA, NA, NULL);
if (f) return f;
f = hasflagval(fp, F_DTIMMUNE, DT_ALL, NA, NA, NULL);
if (f) return f;
return NULL;
}
int 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;
}
// 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 ismaxedskill(lifeform_t *lf, enum SKILL skid) {
// TODO: implement skill maximums per job
if (getskill(lf, skid) == PR_MASTER) {
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_TARGET, player->id, NA, NA, NULL)) {
return B_FALSE;
}
return B_TRUE;
}
int isplayer(lifeform_t *lf) {
if (lf && (lf->controller == C_PLAYER)) {
return B_TRUE;
}
return B_FALSE;
}
int ispolymorphed(lifeform_t *lf) {
if (lfhasflag(lf, F_POLYMORPHED)) {
return B_TRUE;
} else {
return B_FALSE;
}
}
flag_t *isresistantto(flagpile_t *fp, enum DAMTYPE dt) {
flag_t *f;
f = hasflagval(fp, F_DTRESIST, dt, NA, NA, NULL);
if (f) return f;
f = hasflagval(fp, F_DTRESIST, DT_ALL, NA, NA, NULL);
if (f) return f;
return NULL;
}
flag_t *isvulnto(flagpile_t *fp, enum DAMTYPE dt) {
flag_t *f;
f = hasflagval(fp, F_DTVULN, dt, NA, NA, NULL);
if (f) return f;
f = hasflagval(fp, F_DTVULN, DT_ALL, NA, NA, NULL);
if (f) return f;
return NULL;
}
void killjob(job_t *job) {
job_t *nextone, *lastone;
// free mem
free(job->name);
killflagpile(job->flags);
// remove from list
nextone = job->next;
if (nextone != NULL) {
nextone->prev = job->prev;
} else { /* last */
lastjob = job->prev;
}
if (job->prev == NULL) {
/* first */
nextone = job->next;
free(job);
job = nextone;
} else {
lastone = job->prev;
free (lastone->next );
lastone->next = nextone;
}
}
void killrace(race_t *race) {
race_t *nextone, *lastone;
// free mem
free(race->name);
killflagpile(race->flags);
// remove from list
nextone = race->next;
if (nextone != NULL) {
nextone->prev = race->prev;
} else { /* last */
lastrace = race->prev;
}
if (race->prev == NULL) {
/* first */
nextone = race->next;
free(race);
race = nextone;
} else {
lastone = race->prev;
free (lastone->next );
lastone->next = nextone;
}
}
// lose hp, and adjust damage based on resistances
int losehp(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc) {
return losehp_real(lf, amt, damtype, fromlf, damsrc, B_TRUE);
}
int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *fromlf, char *damsrc, int reducedam) {
char buf[BUFLEN];
char buf2[BUFLEN];
if (isplayer(lf)) {
statdirty = B_TRUE;
}
// adjust damage!
if (reducedam) {
adjustdamlf(lf, &amt, damtype);
}
// stop resting/running!
interrupt(lf);
// occasionally drop blood
if (rnd(1,3) == 1) {
bleed(lf);
}
if (hasflag(lf->flags, F_DEBUG)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("[%s takes %d dam]",lfname, amt);
}
// take damage
lf->hp -= amt;
// fill in lastdam...
lf->lastdamtype = damtype;
// if they died
if (lf->hp <= 0) {
//if (!fromlf || (fromlf == player)) {
if (fromlf && isplayer(fromlf)) {
addflag(lf->flags, F_KILLEDBYPLAYER, B_TRUE, NA, NA, NULL);
}
} else {
// effects based on damage
if ((damtype == DT_COLD) && lfhasflag(lf, F_COLDBLOOD)) {
// slow them
addtempflag(lf->flags, F_SLOWMOVE, 5, NA, NA, NULL, 10);
}
}
// replace 'the' at start of damsrc with 'a'
if (strstr(damsrc, "the ") == damsrc) {
sprintf(buf, "a %s", (damsrc+4));
} else {
strcpy(buf, damsrc);
}
// fill in damage amount
sprintf(buf2, "^%d damage",amt);
strcat(buf, buf2);
setlastdam(lf, buf);
// fight back if required
if (fromlf) {
fightback(lf, fromlf);
}
// player low hitpoint warning
if ((lf->hp > 0) && isplayer(lf)) {
int warnthresh;
warnthresh = (int)((float)0.25 * (float)lf->maxhp);
if (lf->hp <= warnthresh) {
warn("*** LOW HITPOINT WARNING ***");
more();
}
}
// update screen
drawstatus();
updatestatus();
return amt;
}
void makefriendly(lifeform_t *lf) {
killflagsofid(lf->flags, F_HOSTILE);
if (!hasflag(lf->flags, F_FRIENDLY)) {
addflag(lf->flags, F_FRIENDLY, B_TRUE, NA, NA, NULL);
}
}
void makenauseated(lifeform_t *lf, int amt, int howlong) {
flag_t *f;
if (lfhasflag(lf, F_UNDEAD)) return;
if (isdead(lf)) return;
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);
}
}
void makenoise(lifeform_t *lf, enum NOISETYPE nid) {
flag_t *f;
char buf[BUFLEN];
if (lfhasflag(lf, F_FROZEN)) {
// can't make noise if frozen!
return;
}
f = hasflagval(lf->flags, F_NOISETEXT, nid, NA, NA, NULL);
if (f) {
char *dummy;
char *verb, *noun;
verb = strtok_r(f->text, "^", &dummy);
noun = strtok_r(NULL, "^", &dummy);
if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s %s.", buf, verb);
} else {
youhear(lf->cell, noun);
}
}
}
void mayusespellschool(flagpile_t *fp, enum SPELLSCHOOL ss, enum FLAG how) {
objecttype_t *ot;
for (ot = objecttype ; ot ; ot = ot->next) {
//if (ot->obclass->id == OC_SPELL) {
if (hasflagval(ot->flags, F_SPELLSCHOOL, ss, NA, NA, NULL)) {
addflag(fp, how, ot->id, NA, NA, NULL);
}
//}
}
}
int modattr(lifeform_t *lf, enum ATTRIB attr, int amt) {
// already at max/min?
if ((amt > 0) && (lf->att[attr] >= 18)) {
return B_TRUE;
}
if ((amt < 0) && (lf->att[attr] <= 0)) {
return B_TRUE;
}
lf->att[attr] += amt;
// increase base if required
if (lf->att[attr] > lf->baseatt[attr]) {
lf->baseatt[attr] = lf->att[attr];
}
// enforce limits
if (lf->att[attr] > 18) lf->att[attr] = 18;
if (lf->att[attr] < 0) lf->att[attr] = 0;
if ((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_CON:
strcpy(adverb, "healthier");
break;
case A_DEX:
strcpy(adverb, "more agile");
break;
case A_IQ:
strcpy(adverb, "smarter");
break;
default:
break;
}
} else { // ie. amt < 0
switch (attr) {
case A_STR:
strcpy(adverb, "weaker");
break;
case A_CON:
strcpy(adverb, "less healthy");
break;
case A_DEX:
strcpy(adverb, "sluggish");
break;
case A_IQ:
strcpy(adverb, "foolish");
break;
default:
break;
}
}
msg("%s %s %s!", lfname, verb, adverb);
}
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;
}
prehlev = gethungerlevel(f->val[0]);
f->val[0] += amt;
posthlev = gethungerlevel(f->val[0]);
// announce
if (isplayer(lf)) {
if (posthlev == H_STARVED) {
msg("You collapse from starvation.");
setlastdam(lf, "starvation");
lf->hp = 0;
} else if (prehlev != posthlev) {
if (posthlev != H_NONE) {
char buf[BUFLEN];
int needfeeling = B_FALSE;
int needexclam = B_FALSE;
switch (posthlev) {
case H_PECKISH:
case H_HUNGRY:
case H_VHUNGRY:
needfeeling = B_TRUE;
break;
default:
needfeeling = B_FALSE;
break;
}
switch (posthlev) {
case H_STUFFED:
case H_STARVING:
needexclam = B_TRUE;
break;
default:
needexclam = B_FALSE;
break;
}
gethungername(posthlev, buf);
msg("You are %s%s%s%c",
((amt < 0) && (posthlev > H_NONE)) ? "still " : "",
needfeeling ? "feeling " : "",
buf,
(needexclam) ? '!' : '.');
}
}
}
f->val[0] += amt;
}
float modifybystat(float num, lifeform_t *lf, enum ATTRIB att) {
int newnum;
newnum = num + ( num * (getstatmod(lf, att) / 100.0) );
return newnum;
}
// give initial equiment / skills to a lifeform
void outfitlf(lifeform_t *lf) {
givestartobs(lf, lf->flags);
givestartskills(lf, lf->flags);
// weild/wear stuff
autoweild(lf);
}
int pickup(lifeform_t *lf, object_t *what, int howmany, int fromground) {
char obname[BUFLEN];
object_t *o;
//flag_t *f;
int failed = B_FALSE;
if (!what) {
return B_TRUE;
}
if (howmany == 0) {
return B_TRUE;
}
getobname(what, obname, howmany);
if (howmany == ALL) howmany = what->amt;
if (fromground) {
if (lfhasflag(lf, F_LEVITATING)) {
if (isplayer(lf)) {
msg("You can't reach the ground from here!");
}
return B_TRUE;
}
}
if (!canpickup(lf, what,howmany)){
// tell the player why!
if (lf->controller == C_PLAYER) {
switch (reason) {
case E_NOSPACE:
msg("Your pack is too full to fit any more objects.");
break;
case E_NOPACK:
msg("You lack the ability to carry things!");
break;
case E_NOPICKUP:
msg("You can't pick up %s!",obname);
break;
case E_TOOBIG:
msg("%s %s too large for you to lift.",obname, (what->amt == 1) ? "is" : "are");
break;
case E_TOOHEAVY:
msg("%s %s too heavy to lift!",obname, (what->amt == 1) ? "is" : "are");
break;
case E_GRAVBOOSTED:
msg("The %s feels too heavy to lift!",noprefix(obname));
break;
default:
msg("For some reason, you cannot pick up %s!",obname);
break;
}
}
failed = B_TRUE;
}
if (!failed) {
if (touch(lf, what)) {
taketime(lf, SPEED_PICKUP);
return B_TRUE;
}
if (hasflag(what->flags, F_DEAD)) {
taketime(lf, SPEED_PICKUP);
return B_TRUE;
}
}
// special case
if (!failed) {
// try to move whatever was selected
o = moveob(what, lf->pack, howmany);
if (o) { // if pickup was successful...
if (isplayer(lf)) {
msgnocap("%c - %s.",o->letter, obname);
} else if (cansee(player, lf)) {
char buf[BUFLEN];
getlfname(lf, buf);
msg("%s picks up %s.",buf, obname);
}
/*
taketime(lf, (SPEED_PICKUP * howmany));
*/
taketime(lf, SPEED_PICKUP);
// TODO: update burdened status
// use ammo rightaway if required.
testammo(lf, o);
} else {
// tell the player why!
if (lf->controller == C_PLAYER) {
switch (reason) {
case E_NOSPACE:
msg("Your pack is too full to fit any more objects.");
break;
default:
msg("For some reason, you cannot pick up %s!",obname);
break;
}
}
failed = B_TRUE;
}
}
if (failed) {
// if object isn't where player is, move it to player's pile
// for example, if obejct was being picked up via telekenesis
if (what->pile->where != lf->cell) {
moveob(what, lf->cell->obpile, howmany);
}
return B_TRUE;
}
return B_FALSE;
}
void precalclos(lifeform_t *lf) {
int x,y;
cell_t *c;
int maxvisrange;
cell_t *los[MAX_MAPW * MAX_MAPH];
int nlos;
int i;
// free existing structures
if (lf->los) {
free(lf->los);
}
nlos = 0;
maxvisrange = getvisrange(lf);
for (y = lf->cell->y - maxvisrange; y <= lf->cell->y + maxvisrange ; y++) {
for (x = lf->cell->x - maxvisrange; x <= lf->cell->x + maxvisrange ; x++) {
c = getcellat(lf->cell->map, x, y);
//
if ((c != lf->cell) && haslos(lf, c)) {
los[nlos] = c;
nlos++;
}
}
}
// now fill in lifeform structure
lf->los = malloc(sizeof(cell_t *) * nlos);
for (i = 0; i < nlos; i++) {
lf->los[i] = los[i];
}
lf->nlos = nlos;
}
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);
if (!obcell || !dstcell) {
return B_TRUE;
}
// move object
o = moveob(o, dstcell->obpile, o->amt);
if (!o) {
return B_TRUE;
}
// move player
moveto(lf, obcell);
// take time - twice normal
taketime(lf, getactspeed(lf) * 2);
// announce
if (lf->controller == C_PLAYER) {
msg("You push %s.", obname);
} else if (haslos(player, dstcell)) {
char buf[BUFLEN];
getlfname(lf, buf);
capitalise(buf);
msg("%s pushes %s.", cansee(player, lf) ? buf : "Something", obname);
}
touch(lf, o);
return B_FALSE;
}
// move lf to the _START_ of the destination map list
void relinklf(lifeform_t *src, map_t *dst) {
map_t *srcmap;
srcmap = src->cell->map;
// unlink this player from the current list
if (src->prev == NULL) {
// first
srcmap->lf = src->next;
} else {
// not first
src->prev->next = src->next;
}
if (src->next == NULL) {
// last
srcmap->lastlf = src->prev;
} else {
// not last
src->next->prev = src->prev;
}
// add this lf to the START of the list on the new map
if (dst->lf == NULL) {
// first (and only) element in new list
dst->lf = src;
src->prev = NULL;
src->next = NULL;
dst->lastlf = src;
} else {
lifeform_t *aa;
// go to start of list
aa = dst->lf;
dst->lf = src;
src->prev = NULL;
src->next = aa;
aa->prev = src;
}
// note this function will NOT set the lifeform's cell to one on the new map.
// now rise up
sortlf(dst, src);
}
// strat resting...
void startresting(lifeform_t *lf, int willtrain) {
int traincounter;
if (willtrain) {
traincounter = 50;
traincounter = modifybystat(traincounter, player, A_IQ);
} else {
traincounter = NA;
}
addflag(lf->flags, F_RESTING, B_TRUE, NA, traincounter, NULL);
if (lf->controller == C_PLAYER) {
msg("You start %s...",(traincounter == NA) ? "resting" : "training");
drawmsg();
drawcursor();
} else if (cansee(player, lf)) {
char buf[BUFLEN];
getlfname(lf, buf);
capitalise(buf);
msg("%s starts resting.");
}
}
int rolliq(enum IQBRACKET bracket) {
int roll = 0;
switch (bracket) {
case IQ_MINDLESS:
roll = 0;
break;
case IQ_VEGETABLE:
roll = rnd(1,3);
break;
case IQ_ANIMAL:
roll = rnd(4,6);
break;
case IQ_DIMWITTED:
roll = rnd(4,6);
break;
case IQ_DOPEY:
roll = rnd(7,9);
break;
case IQ_AVERAGE:
roll = rnd(10,12);
break;
case IQ_SMART:
roll = rnd(13,15);
break;
case IQ_ENLIGHTENED:
roll = rnd(16,17);
break;
case IQ_GENIUS:
roll = 18;
break;
default:
roll = rolldie(3,6);
break;
}
return roll;
}
int rollcon(enum CONBRACKET bracket) {
int roll = 0;
switch (bracket) {
case CN_FRAIL:
roll = rnd(1,2);
break;
case CN_SICKLY:
roll = rnd(3,4);
break;
case CN_UNHEALTHY:
roll = rnd(5,6);
break;
case CN_UNFIT:
roll = rnd(7,8);
break;
case CN_AVERAGE:
roll = rnd(9,11);
break;
case CN_HEALTHY:
roll = rnd(12,14);
break;
case CN_FIT:
roll = rnd(15,16);
break;
case CN_HARDY:
roll = rnd(17,18);
break;
default:
roll = rolldie(3,6);
break;
}
return roll;
}
int rolldex(enum DEXBRACKET bracket) {
int roll = 0;
switch (bracket) {
case DX_INCOMPETENT:
roll = 0;
break;
case DX_OAFISH:
roll = rnd(1,2);
break;
case DX_INEPT:
roll = rnd(3,4);
break;
case DX_CLUMSY:
roll = rnd(5,6);
break;
case DX_AWKWARD:
roll = rnd(7,8);
break;
case DX_AVERAGE:
roll = 9;
break;
case DX_DEXTROUS:
roll = rnd(10,11);
break;
case DX_NIMBLE:
roll = rnd(12,13);
break;
case DX_AGILE:
roll = rnd(14,15);
break;
case DX_SWIFT:
roll = rnd(16,17);
break;
case DX_SUPERSONIC:
roll = 18;
break;
default:
roll = rolldie(3,6);
break;
}
return roll;
}
int rollstr(enum STRBRACKET bracket) {
int roll = 0;
switch (bracket) {
case ST_HELPLESS:
roll = 0;
break;
case ST_FEEBLE:
roll = rnd(1,3);
break;
case ST_VWEAK:
roll = rnd(4,6);
break;
case ST_WEAK:
roll = rnd(7,9);
break;
case ST_AVERAGE:
roll = rnd(10,12);
break;
case ST_STRONG:
roll = rnd(13,15);
break;
case ST_MIGHTY:
roll = rnd(16,17);
break;
case ST_TITANIC:
roll = 18;
break;
default:
roll = rolldie(3,6);
break;
}
return roll;
}
int rollstat(lifeform_t *lf, enum ATTRIB attr) {
flag_t *f;
int bracket;
f = hasflagval(lf->flags, F_STARTATT, attr, NA, NA, NULL);
if (f) {
bracket = f->val[1];
} else {
switch (attr) {
case A_STR:
bracket = ST_RANDOM; break;
case A_CON:
bracket = CN_RANDOM; break;
case A_DEX:
bracket = DX_RANDOM; break;
case A_IQ:
bracket = IQ_RANDOM; break;
default:
return B_TRUE;
}
}
switch (attr) {
case A_STR:
lf->att[attr] = rollstr(bracket);
break;
case A_DEX:
lf->att[attr] = rolldex(bracket);
break;
case A_IQ:
lf->att[attr] = rolliq(bracket);
break;
case A_CON:
lf->att[attr] = rollcon(bracket);
break;
default:
return B_TRUE;
}
return B_FALSE;
}
// returns TRUE if something happened
int scare(lifeform_t *lf, lifeform_t *scarer, int howlong) {
if (!scarer) return B_FALSE;
// immune to fear?
if (lfhasflag(lf, F_UNDEAD)) {
return B_FALSE;
}
// not intelligent enough to be scared?
if (getdexname(getattr(scarer, A_IQ), NULL) <= IQ_VEGETABLE) {
return B_FALSE;
}
if (!skillcheck(lf, SC_MORALE, 15, 0)) {
flag_t *f;
// already fleeing?
f = hasflagval(lf->flags, F_FLEEFROM, scarer->id, NA, NA, NULL);
if (f) {
// update time
if (f->lifetime != PERMENANT) {
if (f->lifetime < howlong) {
f->lifetime = howlong;
}
}
} else {
addtempflag(lf->flags, F_FLEEFROM, scarer->id, NA, NA, NULL, howlong);
}
return B_TRUE;
}
return B_FALSE;
}
int setammo(lifeform_t *lf, object_t *ammo) {
object_t *gun;
object_t *o;
char gunname[BUFLEN],ammoname[BUFLEN];
if (ammo) {
getobname(ammo, ammoname, ammo->amt);
}
gun = getfirearm(lf);
if (!gun) {
if (isplayer(lf)) {
msg("You have no firearm weilded!");
}
return B_TRUE;
}
getobname(gun, gunname, gun->amt);
if (ammo && !isammofor(ammo, gun)) {
if (isplayer(lf)) {
msg("You can't load %s with %s!", gunname, ammoname);
}
return B_TRUE;
}
// remove current ammo
for (o = lf->pack->first ; o ; o = o->next) {
flag_t *f;
f = hasflag(o->flags, F_CURAMMO);
if (f) {
killflag(f);
}
}
if (ammo) {
addflag(ammo->flags, F_CURAMMO, B_TRUE, NA, NA, NULL);
if (gamestarted && isplayer(lf)) {
msg("Using %s as ammo for %s.", ammoname, gunname);
}
} else {
if (gamestarted && isplayer(lf)) {
msg("No ammo equipped for %s.", gunname);
}
}
return B_FALSE;
}
void setattr(lifeform_t *lf, enum ATTRIB attr, int val) {
lf->att[attr] = val;
if (lf->att[attr] > lf->baseatt[attr]) {
lf->baseatt[attr] = lf->att[attr];
}
}
void setguntarget(lifeform_t *lf, lifeform_t *targ) {
flag_t *f;
f = hasflag(lf->flags, F_GUNTARGET);
if (f) {
killflag(f);
}
if (targ) {
addflag(lf->flags, F_GUNTARGET, targ->id, NA, NA, NULL);
if (isplayer(lf)) {
// announce
char targname[BUFLEN];
getlfname(targ, targname);
msg("Targetted: %s.",noprefix(targname)); // remove 'the '
} else {
if (lfhasflag(lf, F_DEBUG)) {
char lfname[BUFLEN];
char targname[BUFLEN];
getlfname(lf, lfname);
getlfname(targ, targname);
dblog("%s targetted %s.",lfname,targname);
}
}
} else {
if (isplayer(lf)) {
// announce
msg("Targetted: nothing.");
} else {
if (lfhasflag(lf, F_DEBUG)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
dblog("%s target set to nothing.",lfname);
}
}
}
}
void setrace(lifeform_t *lf, enum RACE rid) {
flag_t *f,*nextf;
int i;
race_t *newrace;
char buf[BUFLEN];
newrace = findrace(rid);
if (gamestarted && lf->race) {
race_t *origrace;
statdirty = B_TRUE;
f = lfhasflag(lf, F_ORIGRACE);
if (f) {
origrace = findrace(f->val[0]);
}
// announce
if (origrace && (newrace == origrace)) {
if (isplayer(lf)) {
msg("You revert to your original form!");
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("The %s transforms back to its original form!", newrace->name);
}
} else {
if (isplayer(lf)) {
msg("You transform into %s %s!", isvowel(newrace->name[0]) ? "an" : "a", newrace->name);
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s transforms into %s %s!", buf, isvowel(newrace->name[0]) ? "an" : "a", newrace->name);
}
}
}
// 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);
} else if ((f->lifetime > 0) && (f->id != F_POLYMORPHED)) {
killflag(f);
}
}
// set race
lf->race = newrace;
// inherit flags from race
copyflags(lf->flags, lf->race->flags, FROMRACE);
// generate stats
for (i = 0; i < MAXATTS; i++) {
rollstat(lf, i);
lf->baseatt[i] = lf->att[i];
}
// generate hp/maxhp from hit dice
lf->maxhp = 0;
for (i = 0; i < lf->level; i++) {
lf->maxhp += rollhitdice(lf);
assert(lf->maxhp > 0);
}
lf->hp = lf->maxhp;
// 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);
}
lf->mp = lf->maxmp;
}
lf->born = B_TRUE;
// check whether:
// new race can equip things (F_NOBODYPART xx)
// new race can hold objects (F_NOPACK xx)
// TODO: new race can use magic (F_NOSPELLS)
// new race can still hold all the items which you have
if (gamestarted) {
enum BODYPART bp;
object_t *o,*nexto;
// no pack?
if (lfhasflag(lf, F_NOPACK)) {
// drop everything
if (countobs(lf->pack)) {
if (isplayer(lf)) {
msg("Your possessions drop to the ground!");
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s%s possessions drop to the ground!",buf, getpossessive(buf));
}
}
for (o = lf->pack->first ; o ; o = nexto) {
nexto = o->next;
moveob(o, lf->cell->obpile, o->amt);
}
}
for (bp = BP_WEAPON; bp < MAXBODYPARTS; bp++) {
if (lfhasflagval(lf, F_NOBODYPART, bp, NA, NA, NULL)) {
for (o = lf->pack->first; o ; o = nexto ) {
nexto = o->next;
if (hasflagval(o->flags, F_EQUIPPED, bp, NA, NA, NULL)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
// drop it!
if (isplayer(lf)) {
msg("Your %s drops to the ground!",noprefix(obname));
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s%s %s drop to the ground!",buf, getpossessive(buf),
noprefix(obname));
}
moveob(o, lf->cell->obpile, o->amt);
}
}
}
}
for (o = lf->pack->first ; o ; o = nexto) {
nexto = o->next;
if (!canpickup(lf, o, o->amt)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
// drop it!
if (isplayer(lf)) {
msg("Your %s drops to the ground!",noprefix(obname));
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s%s %s drop to the ground!",buf, getpossessive(buf),
noprefix(obname));
}
moveob(o, lf->cell->obpile, o->amt);
}
}
} // end if gamestarted
}
void setlastdam(lifeform_t *lf, char *buf) {
if (lf->lastdam) {
free(lf->lastdam);
}
lf->lastdam = strdup(buf);
}
void initskills(void) {
addskill(SK_ATHLETICS, "Athletics", "Assists with sprinting and recovery.");
addskill(SK_LOCKPICKING, "Lockpicking", "Enhances your ability to pick locks.");
addskill(SK_RESEARCH, "Research", "Allows you a chance of recognising unknown objects.");
addskill(SK_SPELLCASTING, "Spellcasting", "Determines your ability to cast magical spells.");
addskill(SK_TECHUSAGE, "Tech Usage", "Lets you comprehend use modern technological items.");
}
void interrupt(lifeform_t *lf) {
stopresting(lf);
stoprunning(lf);
killflagsofid(lf->flags, F_AUTOCMD);
}
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(lf);
if (!ammo) {
reason = E_NOAMMO;
return B_TRUE;
}
// get fire speed
firespeed = getfirearmspeed(gun);
taketime(lf, getattackspeed(lf));
fireat(lf, ammo, targ->cell, firespeed, gun);
return B_FALSE;
}
int skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod) {
return real_skillcheck(lf, ct, diff, mod, NULL);
}
// positive mod makes it easier, negative makes it harder
int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *result) {
int attrib;
int levmod;
int othermod = 0;
int db = B_FALSE;
int roll;
char mbuf[BUFLEN];
if (lfhasflag(lf, F_DEBUG)) {
db = B_TRUE;
}
switch (ct) {
case SC_STR:
attrib = getattr(lf, A_STR);
break;
case SC_CON:
attrib = getattr(lf, A_CON);
break;
case SC_DEX:
attrib = getattr(lf, A_DEX);
break;
case SC_OPENLOCKS:
attrib = getattr(lf, A_DEX);
break;
case SC_IQ:
case SC_MORALE:
attrib = getattr(lf, A_IQ);
break;
case SC_SLIP:
attrib = getattr(lf, A_DEX);
break;
case SC_DODGE:
// getevasion returns 0-100 (ie. pct chance)
// convert this to 0-20
attrib = getevasion(lf) / 5;
break;
}
// other modifiers
if (ct == SC_SLIP) {
if (getequippedob(lf->pack, BP_FEET)) {
othermod += 5;
}
if (lfhasflag(lf, F_STABILITY)) {
othermod += 10;
}
}
if (ct == SC_OPENLOCKS) {
enum SKILLLEVEL slev;
slev = getskill(lf, SK_LOCKPICKING);
if (slev == PR_INEPT) {
othermod -= 10;
} else {
othermod += (5 * slev);
}
}
levmod = (lf->level / 3);
roll = rolldie(1, 20);
if (db) {
sprintf(mbuf, "%s skillcheck - need %d, got %d(rll)+%d(attr)+%d(lvm)+%d(othmod)+%d(mod)=",lf->race->name, diff, roll, attrib,levmod, othermod,mod);
}
roll += attrib;
roll += mod;
roll += levmod;
roll += othermod;
if (db) {
msg("%s%d.",mbuf,roll);
}
if (result) {
*result = roll;
}
if (roll >= diff) {
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;
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];
getlfname(lf,lfname);
getobname(o, obname, 1);
// slip!
if (isplayer(lf) || cansee(player, lf)) {
char damstring[BUFLEN];
msg("%s slip%s on %s.",lfname, isplayer(lf) ? "" : "s", obname);
sprintf(damstring, "slipping on %s",obname);
losehp(lf, 1, DT_FALL, NULL, damstring);
}
taketime(lf, getactspeed(lf));
// object moves?
if (hasflag(o->flags, F_SLIPMOVE)) {
cell_t *cur, *new;
cur = getoblocation(o);
new = getrandomadjcell(cur, WE_NOTSOLID);
if (new) {
if (haslos(player, cur) || haslos(player, new)) {
msg("%s slips across the floor.", obname);
}
moveob(o, new->obpile, 1);
}
}
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;
}
}
}
int stone(lifeform_t *lf) {
char lfname[BUFLEN];
char statname[BUFLEN];
int failed = B_FALSE;
// SAVING THROW ?
getlfname(lf, lfname);
// check for immune to stoning
switch (getlfmaterial(lf)) {
case MT_STONE:
case MT_GAS:
failed = B_TRUE;
break;
default:
break;
}
if (failed) {
return B_TRUE;
}
sprintf(statname, "statue of a %s", lf->race->name);
addob(lf->cell->obpile, statname);
// kill lifeform
addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
if (cansee(player, lf)) {
msg("%s %s to stone!", lfname, isplayer(lf) ? "turn" : "turns");
}
setlastdam(lf, "petrification");
die(lf);
return B_FALSE;
}
void stopresting(lifeform_t *lf) {
flag_t *f;
// stop resting!
f = hasflag(lf->flags, F_RESTING);
if (f) {
int training = B_FALSE;
if (f->val[2] != NA) {
training = B_TRUE;
}
killflag(f);
//lf->timespent = 0;
if (isplayer(lf)) {
msg("Your %s is interrupted!", training ? "training" : "rest");
} else if (cansee(player, lf)) {
char buf[BUFLEN];
getlfname(lf, buf);
capitalise(buf);
msg("%s stops %s.",buf, training ? "training" : "resting");
}
}
}
void stoprunning(lifeform_t *lf) {
flag_t *f;
f = hasflag(lf->flags, F_RUNNING);
if (f) {
killflag(f);
}
}
// if this object is ammo, and we are using a gun
// with no ammo, then equip it.
int testammo(lifeform_t *lf, object_t *o) {
object_t *gun;
gun = getfirearm(lf);
if (gun) {
if (isammofor(o, gun)) {
object_t *curammo;
curammo = getammo(lf);
if (!curammo) {
setammo(lf, o);
return B_TRUE;
}
}
}
return B_FALSE;
}
int takeoff(lifeform_t *lf, object_t *o) {
flag_t *f;
char obname[BUFLEN];
char buf[BUFLEN];
if (!isarmour(o)) {
return unweild(lf, o);
}
getobname(o, obname, 1);
if (!cantakeoff(lf, o)) {
switch (reason) {
case E_CURSED:
if (lf->controller == C_PLAYER) {
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 (lf->controller == C_PLAYER) {
msg("You are not wearing that!");
}
break;
default:
if (lf->controller == C_PLAYER) {
msg("For some reason, you cannot remove your %s!", noprefix(obname));
}
// still take time
taketime(lf, getactspeed(lf));
break;
}
return B_TRUE;
}
// remove the equipped flag
f = hasflag(o->flags, F_EQUIPPED);
killflag(f);
taketime(lf, getactspeed(lf));
if (gamestarted) {
if (lf->controller == C_PLAYER) {
msg("You take off %s.", obname);
} else if (cansee(player, lf)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s takes off %s.", buf, obname);
}
}
// lose flags
loseobflags(lf, o, F_EQUIPCONFER);
if (obproduceslight(o)) {
calclight((getoblocation(o))->map);
drawscreen();
}
return B_FALSE;
}
void taketime(lifeform_t *lf, long howlong) {
int db = B_FALSE;
map_t *map;
if (lfhasflag(lf, F_NOTIME)) {
return;
}
if (isplayer(lf)) {
statdirty = B_TRUE;
}
map = lf->cell->map;
assert(howlong > 0);
if (db && gamestarted && cansee(player, lf)) {
dblog("lfid %d (%s) spending %d time\n",lf->id,lf->race->name, howlong);
}
// inc timespent
lf->timespent += howlong;
assert(lf->timespent >= 0);
// time-based effects on lifeforms (eg hp regeneration) go here...
modhunger(lf, howlong);
// 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)) {
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) {
taketime(thrower, SPEED_THROW);
return fireat(thrower, o, where, getthrowspeed(getattr(thrower, A_STR)), NULL);
}
// lf effects which happen every xx ticks
void timeeffectslf(lifeform_t *lf) {
flag_t *f;
f = hasflag(lf->flags, F_FOODPOISONED);
if (f) {
//sprintf(buf, "eating a bad %s",strchr(obname, ' '));
// chance of losing hp
if (rnd(0,3)) {
char buf[BUFLEN];
if (isplayer(lf)) {
msg("You vomit violently.");
}
sprintf(buf, "food poisoning^from a bad %s",noprefix(f->text));
losehp(lf, 2, DT_DIRECT, NULL, buf);
}
}
// decrement flags
timeeffectsflags(lf->flags);
// 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);
} else {
if (isplayer(lf)) {
msg("For some reason, you are unable to revert to your original form!");
}
}
lf->polyrevert = B_FALSE;
}
}
//////////////////////////////////
// effects which happen before every TURN
// (ie. the faster the player is, the faster they happen)
// eg. damage from walking on things
//////////////////////////////////
void turneffectslf(lifeform_t *lf) {
int db = B_FALSE;
map_t *map;
enum ERROR error;
object_t *o;
flag_t *f, *nextf;
char buf[BUFLEN];
lifeform_t *l;
int i;
map = lf->cell->map;
if (db) dblog("starting turneffectslf for lf id %d %s", lf->id, lf->race->name);
// update where player knows
// (but without a map you will then slowly forget it)
if (isplayer(lf)) {
updateknowncells();
} else {
lf->ignorecell = NULL;
}
// stuck inside solid cells?
if (!cellwalkable(lf, lf->cell, &error)) {
if (error == E_WALLINWAY) {
if (isplayer(lf)) {
msg("You reintegrate inside a solid object!");
}
losehp(lf, 9999, DT_DIRECT, NULL, "re-integration inside a solid object");
//}
}
}
// regeneration
sumflags(lf->flags, F_REGENERATES, &i, NULL, NULL);
if (i > 0) {
// heal hp
gainhp(lf, i);
}
// MP regeneration
sumflags(lf->flags, F_MPREGEN, &i, NULL, NULL);
if (i > 0) {
// heal mp
gainmp(lf, i);
}
if (lfhasflag(lf, F_MAGSHIELD)) {
object_t *nexto;
// metal weapons?
for (o = lf->pack->first ; o ; o = nexto) {
nexto = o->next;
if (ismetal(o->material->id)) {
f = isequipped(o);
if (f && ((f->val[0] == BP_WEAPON) || (f->val[0] == BP_SECWEAPON))) {
cell_t *c;
// weapon flies away
c = getrandomadjcell(lf->cell, WE_NOTSOLID);
if (!c) c = lf->cell;
moveob(o, c->obpile, o->amt);
if (isplayer(lf)) {
getobname(o, buf, o->amt);
msg("Your %s flies out of your hands!",noprefix(buf), (o->amt == 1) ? "flies" : "fly");
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getobname(o, buf, o->amt);
getlfname(lf, lfname);
msg("%s%s %s flies out of its hands!",lfname, getpossessive(lfname),
noprefix(buf), (o->amt == 1) ? "flies" : "fly");
}
}
}
}
// objects on the ground?
for (o = lf->cell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (ismetal(o->material->id)) {
cell_t *c;
// object flies away
c = getrandomadjcell(lf->cell, WE_NOTSOLID);
if (!c) c = lf->cell;
moveob(o, c->obpile, o->amt);
if (isplayer(lf)) {
getobname(o, buf, o->amt);
msg("%s is repelled away from you!",buf);
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getobname(o, buf, o->amt);
getlfname(lf, lfname);
msg("%s is repelled away from %s!",buf, lfname);
}
}
}
}
// special effects
f = lfhasflag(lf, F_AUTOCREATEOB);
if (f) {
int x,y;
int radius;
objecttype_t *ot;
radius = f->val[0];
ot = findotn(f->text);
if (ot) {
for (y = lf->cell->y - radius ; y < lf->cell->y + radius; y++) {
for (x = lf->cell->x - radius ; x < lf->cell->x + radius; x++) {
cell_t *c;
c = getcellat(lf->cell->map, x, y);
if (c && !c->type->solid && (getcelldist(lf->cell, c) <= radius)) {
if (!hasob(c->obpile, ot->id)) {
addob(c->obpile, f->text);
}
}
}
}
}
}
// gains/loses stench?
for (l = lf->cell->map->lf ; l ; l = l->next) {
if (!isdead(l) && (l != lf)) {
// TODO: race shoudl be "subspecies"
if (l->race->baseid != lf->race->baseid) { // can't smell your own race.
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);
}
}
}
}
}
// any potentiall damaging effects have to go after here...
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("You are melting!");
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s melts a little.",lfname);
}
}
}
if (isdead(lf)) return;
// damage from cell objects?
for (o = lf->cell->obpile->first ; o ; o = o->next) {
f = hasflag(o->flags, F_WALKDAM);
if (f) {
int dam;
dam = f->val[0];
getobname(o, buf, o->amt);
dam = losehp(lf, dam, f->val[1], NULL, buf);
if (dam > 0) {
if (f->val[1] == DT_POISONGAS) {
if (isplayer(lf) || cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s choke%s on %s!", lfname, isplayer(lf) ? "" : "s", buf);
}
} else {
if (isplayer(lf)) {
msg("%s %ss you!", buf, getattackverb(f->val[1], dam,lf->maxhp));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s %ss %s!", buf, getattackverb(f->val[1], dam,lf->maxhp), lfname);
}
}
}
}
}
if (isdead(lf)) return;
for (f = lf->flags->first ; f ; f = nextf) {
nextf = f->next;
// remove impossible flags
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;
}
}
// recharge abilities
if (f->id == F_CANWILL) {
if (f->val[2] != NA) {
if (f->val[1] < f->val[2]) {
f->val[1]++;
}
}
}
}
}
// returns B_TRUE if the action which involved touching this should fail
int touch(lifeform_t *lf, object_t *o) {
flag_t *f;
char buf[BUFLEN];
char obname[BUFLEN];
char lfname[BUFLEN];
if (!gamestarted) return B_FALSE;
getlfname(lf, lfname);
getobname(o, obname, o->amt);
// freezing touch?
f = hasflag(lf->flags, F_FREEZINGTOUCH);
if (f) {
// not wearing gloves?
if (!getequippedob(lf->pack, BP_HANDS)) {
// default power of 4
dospelleffects(lf, OT_S_FREEZEOB, 4, NULL, o, NULL, B_UNCURSED, NULL);
// 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 (lf->controller == C_PLAYER) {
msg("Your hands stop glowing blue.");
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s's hands stop glowing blue.");
}
killflag(f);
}
}
}
f = hasflag(o->flags, F_SHARP);
if (f) {
object_t *gloves;
gloves = getequippedob(lf->pack, BP_HANDS);
if (!gloves) {
if (isplayer(lf)) {
msg("Ow! You cut your finger on %s.", obname);
}
sprintf(buf, "touching %s", obname);
losehp(lf, rnd(1,2), DT_SLASH, NULL, buf);
}
}
// flaming objects?
if (hasflag(o->flags, F_ONFIRE)) {
// flaming weapons are ok
if (!isweapon(o)) {
object_t *gloves;
// wearing gloves? they get damaged.
gloves = getequippedob(lf->pack, BP_HANDS);
if (gloves) {
takedamage(gloves, 2, DT_FIRE);
} else {
// otherwise YOU get damaged.
if (isplayer(lf)) {
msg("Ow! You burn your hands on %s.",obname);
} else if (cansee(player, lf)) {
msg("%s burns itself on %s.",lfname, obname);
}
sprintf(buf, "touching %s",obname);
losehp(lf, 2, DT_FIRE, NULL, buf);
return B_TRUE;
}
}
}
return B_FALSE;
}
int unweild(lifeform_t *lf, object_t *o) {
flag_t *f;
char obname[BUFLEN];
char buf[BUFLEN];
getobname(o, obname, 1);
if (!cantakeoff(lf, o)) {
switch (reason) {
case E_CURSED:
if (lf->controller == C_PLAYER) {
msg("Your %s appears to be stuck to your hand!", noprefix(obname));
if (!o->blessknown) {
o->blessknown = B_TRUE;
}
}
break;
case E_NOTEQUIPPED:
if (lf->controller == C_PLAYER) {
msg("You are not weilding that!");
}
break;
default:
if (lf->controller == C_PLAYER) {
msg("For some reason, you cannot stop weilding your %s!", noprefix(obname));
}
break;
}
return B_TRUE;
}
// remove the equipped flag
f = hasflag(o->flags, F_EQUIPPED);
killflagsofid(o->flags, F_EQUIPPED);
// unweilding doesn't take any time
if (gamestarted) {
if (lf->controller == C_PLAYER) {
msg("You are no longer weilding %s.", obname);
} else if (cansee(player, lf)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s stops weilding %s.", buf, obname);
}
}
// lose flags
loseobflags(lf, o, F_EQUIPCONFER);
if (obproduceslight(o)) {
calclight((getoblocation(o))->map);
drawscreen();
}
return B_FALSE;
}
int useability(lifeform_t *lf, enum OBTYPE aid, lifeform_t *who, cell_t *where) {
int rv;
if (!cancast(lf, aid, NULL)) {
if (isplayer(lf)) {
// announce
switch (reason) {
case E_NOTREADY:
msg("This ability is not recharged yet.");
break;
default:
msg("For some reason, you can't use this ability.");
break;
}
}
return B_TRUE;
}
// taketime() will happen during abiltiyeffects()
// use the ability
rv = abilityeffects(lf, aid, where, who);
return rv;
}
int usestairs(lifeform_t *lf, object_t *o) {
flag_t *f;
map_t *curmap;
map_t *newmap;
int newx,newy;
cell_t *newcell;
objecttype_t *ot;
int dir;
int newdepth;
char lfname[BUFLEN];
char obname[BUFLEN];
int isportal = B_FALSE;
getlfname(lf, lfname);
getobname(o, obname, 1);
curmap = lf->cell->map;
f = hasflag(o->flags, F_CLIMBABLE);
assert(f);
dir = f->val[0];
// 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;
}
// announce
if (isportal) {
if (isplayer(lf)) {
msg("You enter %s...", obname);
// move cursor to msgwindow while we create the new level...
wrefresh(msgwin);
} else if (cansee(player, lf)) {
msg("%s enters %s...", lfname, obname);
}
} else {
if (isplayer(lf)) {
msg("You %s %s the staircase...", getmoveverb(lf), getdirname(dir));
// move cursor to msgwindow while we create the new level...
wrefresh(msgwin);
} else if (cansee(player, lf)) {
msg("%s %s %s the staircase...", lfname, getmoveverbother(lf), getdirname(dir));
}
}
// do stairs already go somewhere?
f = hasflag(o->flags, F_MAPLINK);
if (f) {
newmap = findmap(f->val[0]);
newx = f->val[1];
newy = f->val[2];
assert(newmap);
// find dst x/y
newcell = getcellat(newmap, newx, newy);
} else {
if (isportal) {
// unconnected portal
if (isplayer(lf)) msg("This portal doesn't seem to go anywhere.");
} else {
// is there already a level of the correct depth?
newmap = findmapofdepth(newdepth);
if (newmap) {
// find other end of stairs
ot = getoppositestairs(o->type);
newcell = findobinmap(newmap, ot->id);
assert(newcell);
// link these stairs to it
addflag_real(o->flags, F_MAPLINK, newmap->id, newcell->x, newcell->y, NULL, PERMENANT, B_UNKNOWN, -1);
} else {
// need to create a new map!
// get return stair ob type
ot = getoppositestairs(o->type);
// generate a new map!
newmap = addmap();
createmap(newmap, newdepth, curmap->habitat, curmap, ot);
newcell = findobinmap(newmap, ot->id);
assert(newcell);
// link current map to new one
addflag_real(o->flags, F_MAPLINK, newmap->id, newcell->x, newcell->y, NULL, PERMENANT, B_UNKNOWN, -1);
}
}
}
if (newcell) {
if (isplayer(lf)) {
msg("You arrive at level %d.", newcell->map->depth);
f = hasflag(o->flags, F_MAPLINK);
if (f) f->known = B_KNOWN;
}
// move player to new map
moveto(lf, newcell);
taketime(lf, getmovespeed(lf));
} else {
dblog("ERROR - can't find opposite end of stairs/portal!");
msg("ERROR - can't find opposite end of stairs/portal!");
}
if (isplayer(lf)) {
statdirty = B_TRUE;
needredraw = B_TRUE;
calclight(player->cell->map);
precalclos(lf);
drawscreen();
}
return B_FALSE;
}
int validateraces(void) {
int goterror = B_FALSE;
race_t *r;
flag_t *f;
for (r = firstrace ; r ; r = r->next) {
if (!hasflag(r->flags, F_SIZE)) {
printf("ERROR in race '%s' - missing F_SIZE.\n", r->name);
goterror = B_TRUE;
}
for (f = r->flags->first ; f ; f = f->next) {
if (f->id == F_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_CANCAST) {
flag_t *ff;
ff = hasflagval(r->flags, F_HASSKILL, SK_SPELLCASTING, NA, NA, NULL);
if (ff) {
int power;
power = (1 + ff->val[1]) / getspelllevel(f->val[0]);
if (power <= 0) {
objecttype_t *sp;
sp = findot(f->val[0]);
printf("ERROR in race '%s' - F_CANCAST %s (l%d) but insufficient spell power.\n",
r->name, sp->name,getspelllevel(sp->id));
goterror = B_TRUE;
}
} else {
objecttype_t *sp;
sp = findot(f->val[0]);
printf("ERROR in race '%s' - F_CANCAST %s (l%d) but no spellcasting skill\n", r->name, sp->name, getspelllevel(sp->id));
goterror = B_TRUE;
}
}
}
}
return goterror;
}
int rest(lifeform_t *lf, int onpurpose) {
flag_t *f;
flag_t *ff;
int healtime = 3;
int wantclearmsg = B_TRUE;
int mpheal = 1;
int hpheal = 1;
flag_t *rf;
int training = B_FALSE;
rf = lfhasflag(lf, F_RESTING);
if (rf && rf->val[2] != NA) {
training = B_TRUE;
}
taketime(lf, getactspeed(lf));
if (!lfhasflag(lf, F_FOODPOISONED)) {
// slowly heal hp/mp
if (!training) {
ff = lfhasflag(lf, F_RESTHEALAMT);
if (ff) {
hpheal = ff->val[0];
} else {
hpheal = 1;
}
ff = lfhasflag(lf, F_RESTHEALMPAMT);
if (ff) {
mpheal = ff->val[0];
} else {
mpheal = 1;
}
if (onpurpose) {
f = lfhasflag(lf, F_RESTCOUNT);
if (!f) {
f = addflag(lf->flags, F_RESTCOUNT, 0, NA, NA, NULL);
}
f->val[0]++;
f->lifetime = 2;
ff = lfhasflag(lf, F_RESTHEALTIME);
if (ff) {
healtime = ff->val[0];
} else {
// 3 turns = heal 1 hp
healtime = 3;
}
if (f->val[0] >= healtime) {
//if (isplayer(lf)) msg("hp given.");
if (lf->hp < lf->maxhp) {
gainhp(lf, hpheal);
}
if (lf->mp < lf->maxmp) {
gainmp(lf, mpheal);
}
/*
// heal mp too?
ff = lfhasflag(lf, F_RESTHEALMPAMT);
if (ff) {
if (lf->mp < lf->maxmp) {
gainmp(lf, ff->val[0]);
}
}
*/
killflag(f);
}
}
}
// if resting/training with 'R'...
if (rf) {
wantclearmsg = B_FALSE;
if (training) {
rf->val[2]--;
if (rf->val[2] == 0) {
// ask about gaining skills
if (isplayer(lf)) msg("You finish training.");
if (lf->skillpoints) {
if (isplayer(lf)) more();
enhanceskills(lf);
}
killflag(rf);
}
} else {
// resting
if ((lf->hp >= lf->maxhp) || !hpheal) {
if ((lf->mp >= lf->maxmp) || !mpheal) {
if (isplayer(lf)) {
msg("You finish resting.");
wantclearmsg = B_FALSE;
}
killflag(rf);
}
}
}
}
}
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;
char buf[BUFLEN],obname[BUFLEN];
flag_t *f;
enum BODYPART possbp[MAXBODYPARTS];
int nparts = 0;
enum BODYPART bp;
getobname(o, obname, 1);
// check for already equipped first!
f = hasflag(o->flags, F_EQUIPPED);
if (f) {
if (lf->controller == C_PLAYER) {
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("Your %s evades your grasp!", noprefix(buf));
}
return B_TRUE;
}
nparts = 0;
for (f = o->flags->first ; f ; f = f->next) {
if (f->id == F_GOESON) {
possbp[nparts] = f->val[0];
nparts++;
}
}
if (nparts == 0) {
if (lf->controller == C_PLAYER) {
msg("You can't wear that!");
}
return B_TRUE;
} else if (nparts == 1) {
bp = possbp[0];
} else {
int i;
/*
if (gamestarted && isplayer(lf)) {
int i;
// ask where
sprintf(buf, "Wear %s on which body part?", obname);
initprompt(&prompt, buf);
for (i = 0; i < nparts; i++) {
if (!hasobwithflagval(lf->pack, F_EQUIPPED, possbp[i], NA, NA, NULL)) {
addchoice(&prompt, i, getbodypartname(possbp[i]), NULL);
}
}
addchoice(&prompt, '^', "Cancel", NULL);
i = getchoicestr(&prompt);
if (i == '^') {
return B_TRUE;
} else {
bp = possbp[i];
}
} else { // autoequip
*/
// go in first possible place
bp = BP_NONE;
for (i = 0; i < nparts; i++) {
if (!hasobwithflagval(lf->pack, F_EQUIPPED, possbp[i], NA, NA, NULL)) {
bp = possbp[i];
break;
}
}
if (bp == BP_NONE) return B_TRUE;
//}
}
while (!canwear(lf, o, bp)) {
int errresolved = B_FALSE;
if (gamestarted && lf->created) {
switch (reason) {
case E_ALREADYUSING:
if (isplayer(lf)) {
msg("You're already wearing that!");
}
break;
case E_WEARINGSOMETHINGELSE:
f = hasflag(o->flags, F_GOESON);
if (f) {
object_t *inway;
// find what else is there
inway = getequippedob(lf->pack, f->val[0]);
getobname(inway,buf, 1);
if (lf->controller == C_PLAYER) {
int ch;
char buf2[BUFLEN];
// take offending item off first - this takes extra time.
sprintf(buf2, "Remove your %s",noprefix(buf));
ch = askchar(buf2, "yn","y", B_TRUE);
if (ch == 'y') {
if (isarmour(inway)) {
if (!takeoff(player, inway)) {
// took it off!
errresolved = B_TRUE;
}
} else { // weapon
if (!unweild(player, inway)) {
// took it off!
errresolved = B_TRUE;
}
}
}
}
} else {
// should never happen
if (isplayer(lf)) msg("You can't wear that!");
}
break;
case E_NOTKNOWN:
if (isplayer(lf)) msg("You can't wear that!");
break;
default:
if (isplayer(lf)) {
msg("You can't wear that!");
}
break;
}
} // end if gamestarted
if (!errresolved) {
return B_TRUE;
}
// if we DID resolve the error, loop around and check if we can wear again...
} // end while !canwear
// some checks first...
touch(lf, o);
if (hasflag(o->flags, F_DEAD)) {
taketime(lf, getactspeed(lf));
return B_TRUE;
}
// wear it
//f = hasflag(o->flags, F_GOESON);
//bp = f->val[0];
addflag(o->flags, F_EQUIPPED, bp, -1, -1, NULL);
taketime(lf, getactspeed(lf));
/*
if (isplayer(lf)) {
// equipping unknown objects makes them known
if (!isknown(o)) {
// announce
announceob(o->type->id);
// make the obejct type known
makeknown(o->type->id);
getobname(o, obname, 1);
}
}
*/
// 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 (gamestarted && lf->created) {
if (lf->controller == C_PLAYER) {
msg("You are now wearing %s.", obname);
} else if (cansee(player, lf)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s puts on %s.", buf, obname);
}
if (o->blessed == B_CURSED) {
if (lf->controller == C_PLAYER) {
msg("Oh 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;
}
}
}
// give flags
giveobflags(lf, o, F_EQUIPCONFER);
return rv;
}
int weild(lifeform_t *lf, object_t *o) {
char buf[BUFLEN];
flag_t *f;
object_t *oo;
int twohanded = B_FALSE;
enum BODYPART weildloc,otherloc;
if (o) {
getobname(o, buf, o->amt);
}
if (!canweild(lf, o)) {
if (gamestarted && lf->created) {
if (lf->controller == C_PLAYER) {
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_NOHANDS:
msg("You are not capable of weilding a weapon!");
break;
default:
msg("For some reason, you weild this!");
break;
}
}
}
return B_TRUE;
}
// metal objects and magshield?
if (lfhasflag(lf, F_MAGSHIELD)) {
if (isplayer(lf)) {
msg("Your %s evades your grasp!", noprefix(buf));
}
return B_TRUE;
}
// anything else weilded?
if (o) {
if (hasflag(o->flags, F_FIREARM)) {
weildloc = BP_SECWEAPON;
otherloc = BP_WEAPON;
} else {
weildloc = BP_WEAPON;
otherloc = BP_SECWEAPON;
}
if (hasflag(o->flags, F_TWOHANDED)) {
twohanded = B_TRUE;
} else {
twohanded = B_FALSE;
}
} else { // ie. asked to fight unarmed
weildloc = BP_WEAPON;
twohanded = B_FALSE;
}
for (oo = lf->pack->first ; oo ; oo = oo->next) {
f = hasflagval(oo->flags, F_EQUIPPED, weildloc, -1, -1, NULL);
if (f) {
if (isarmour(oo)) {
char inwayname[BUFLEN];
char buf2[BUFLEN];
char ch;
if (isplayer(lf)) {
getobname(oo, inwayname, oo->amt);
// prompt before taking it off.
sprintf(buf2, "Remove your %s",noprefix(inwayname));
ch = askchar(buf2, "yn","y", B_TRUE);
} else {
ch = 'y';
}
if (ch == 'y') {
if (isarmour(oo)) {
if (takeoff(lf, oo)) {
// if we can't remove it, stop.
return B_TRUE;
}
} else { // weapon
if (unweild(lf, oo)) {
// if we can't remove it, stop.
return B_TRUE;
}
}
} else {
return B_TRUE;
}
} else {
// just unweild it
if (unweild(lf, oo)) {
// if we can't unweild old weapon, stop
return B_TRUE;
}
}
}
/*
// this weapon is two handed and uses the other hand
if (hasflag(oo->flags, F_TWOHANDED)) {
f = hasflagval(oo->flags, F_EQUIPPED, otherloc, -1, -1, NULL);
if (f) {
// 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)) {
// prompt before taking it off.
getobname(oo, inwayname, oo->amt);
sprintf(buf2, "Remove your %s",noprefix(inwayname));
ch = askchar(buf2, "yn","y", B_TRUE);
} else {
ch = 'y';
}
if (ch == 'y') {
if (isarmour(oo)) {
if (takeoff(lf, oo)) {
// if we can't remove it, stop.
return B_TRUE;
}
} else {
if (unweild(lf, oo)) {
// if we can't remove it, stop.
return B_TRUE;
}
}
} else {
return B_TRUE;
}
}
}
}
}
// if we asked to just unweild our weapon, exit now
// with no error.
if (!o) {
if (gamestarted && lf->created && (lf->race->id != R_DANCINGWEAPON)) {
if (isplayer(lf)) {
msg("You are now fighting unarmed.");
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s is now fighting unarmed.",buf);
}
}
return B_FALSE;
}
// now weild this
addflag(o->flags, F_EQUIPPED, weildloc, -1, -1, NULL);
if (hasflag(o->flags, F_TWOHANDED)) {
addflag(o->flags, F_EQUIPPED, otherloc, -1, -1, NULL);
}
taketime(lf, getactspeed(lf));
if (gamestarted && lf->created && (lf->race->id != R_DANCINGWEAPON)) {
if (lf->controller == C_PLAYER) {
char buf2[BUFLEN];
sprintf(buf2, "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 (!hasflag(o->flags, F_DAM) && !hasflag(o->flags, F_FIREARM)) {
msg("You have a feeling that this weapon will not be very effective...");
}
} else if (cansee(player, lf)) {
char buf2[BUFLEN];
getlfname(lf, buf2);
msg("%s weilds %s.", buf2, buf);
}
touch(lf, o);
if (o->blessed == B_CURSED) {
if (lf->controller == C_PLAYER) {
msg("Oh 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;
}
}
if (isfirearm(o)) {
// select ammo
setammo(lf, getrandomammo(lf));
if (getammo(lf)) {
autotarget(lf);
}
}
}
// give flags
giveobflags(lf, o, F_EQUIPCONFER);
// make certain flags known
if (isplayer(lf)) {
f = hasflag(o->flags, F_ARMOURPIERCE);
if (f) {
msg("Your %s seems unnaturally sharp!",noprefix(buf));
f->known = B_TRUE;
}
}
return B_FALSE;
}
// will the lf flee after taking damage?
int willflee(lifeform_t *lf) {
enum IQBRACKET iqb;
flag_t *f;
if (hasflag(lf->flags, F_NOFLEE)) {
return B_FALSE;
}
if (hasflag(lf->flags, F_FLEEONDAM)) {
return B_TRUE;
}
iqb = getiqname(getattr(lf, A_IQ), NULL);
if ((iqb >= IQ_SMART) && isbleeding(lf)) {
return B_TRUE;
}
f = hasflag(lf->flags, F_FLEEONHPPCT);
if (f) {
if (gethppct(lf) <= f->val[0]) {
return B_TRUE;
}
}
return B_FALSE;
}
int youhear(cell_t *c, char *text) {
if (canhear(player, c)) {
msg("You hear %s.", text);
return B_TRUE;
}
return B_FALSE;
}