nexus/objects.c

9152 lines
280 KiB
C

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.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 knowledge_t *knowledge, *lastknowledge;
extern hiddenname_t *firsthiddenname, *lasthiddenname;
extern objecttype_t *objecttype,*lastobjecttype;
extern objectclass_t *objectclass,*lastobjectclass;
extern brand_t *firstbrand,*lastbrand;
extern obmod_t *firstobmod,*lastobmod;
extern material_t *material,*lastmaterial;
extern object_t *retobs[MAXPILEOBS+1];
extern int retobscount[MAXPILEOBS+1];
extern int nretobs;
extern map_t *firstmap;
extern int gamestarted;
extern int loading;
extern long curtime;
extern lifeform_t *player;
extern int reason;
extern int needredraw;
char letorder[MAXPILEOBS] = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
};
objecttype_t *lastot = NULL;
enum OBCLASS sortorder[] = {
OC_EFFECT,
OC_DFEATURE,
OC_MONEY,
OC_WEAPON,
OC_MISSILE,
OC_ARMOUR,
OC_POTION,
OC_SCROLL,
OC_WAND,
OC_FOOD,
OC_CORPSE,
OC_RING,
OC_TECH,
OC_BOOK,
OC_ROCK,
OC_MISC,
// omitting OC_SPELL and OC_JUMP because it shouldn't ever be displayed
OC_NULL
};
long nextoid = 0;
brand_t *addbrand(enum BRAND id, char *suffix, enum BODYPART bp) {
brand_t *a, *om;
char buf[BUFLEN];
//flag_t *f;
// does this modj already exist?
om = findbrand(id);
assert(!om);
// add to the end of the list
if (firstbrand == NULL) {
firstbrand = malloc(sizeof(brand_t));
a = firstbrand;
a->prev = NULL;
} else {
// go to end of list
a = lastbrand;
a->next = malloc(sizeof(brand_t));
a->next->prev = a;
a = a->next;
}
lastbrand = a;
a->next = NULL;
// props
a->id = id;
a->bp = bp;
sprintf(buf, " %s",suffix);
a->suffix = strdup(buf);
a->flags = addflagpile(NULL, NULL);
return a;
}
object_t *addemptyob(obpile_t *where, object_t *o) {
char buf[BUFLEN];
object_t *empty;
// determine what kind of empty container to drop
if (strstr(o->type->name, "vial")) {
strcpy(buf, "empty vial");
} else {
strcpy(buf, "empty flask");
}
empty = addob(where, buf);
if (!empty) {
// try to drop on ground
if (where->owner) {
empty = addob(where->owner->cell->obpile, buf);
if (empty) {
char emptyname[BUFLEN];
getobname(empty, emptyname, 1);
if (isplayer(where->owner)) {
msg("You drop the %s on the ground.", noprefix(emptyname));
} else if (haslos(player, where->owner->cell)) {
getlfname(where->owner, buf);
capitalise(buf);
msg("%s drops the %s on the ground.", buf, noprefix(emptyname));
}
}
}
}
return empty;
}
hiddenname_t *addhiddenname(enum OBCLASS obclass, char *text) {
hiddenname_t *a;
// add to the end of the list
if (firsthiddenname == NULL) {
firsthiddenname = malloc(sizeof(hiddenname_t));
a = firsthiddenname;
a->prev = NULL;
} else {
// go to end of list
a = lasthiddenname;
a->next = malloc(sizeof(hiddenname_t));
a->next->prev = a;
a = a->next;
}
lasthiddenname = a;
a->next = NULL;
// props
a->obclass = obclass;
a->text = strdup(text);
a->used = B_FALSE;
return a;
}
knowledge_t *addknowledge(enum OBCLASS id, char *hiddenname, int known) {
knowledge_t *a;
// add to the end of the list
if (knowledge == NULL) {
knowledge = malloc(sizeof(knowledge_t));
a = knowledge;
a->prev = NULL;
} else {
// go to end of list
a = lastknowledge;
a->next = malloc(sizeof(knowledge_t));
a->next->prev = a;
a = a->next;
}
lastknowledge = a;
a->next = NULL;
// props
a->id = id;
a->hiddenname = strdup(hiddenname);
a->known = known;
return a;
}
material_t *addmaterial(enum MATERIAL id, char *name, float weightrating) {
material_t *a;
// add to the end of the list
if (material == NULL) {
material = malloc(sizeof(material_t));
a = material;
a->prev = NULL;
} else {
// go to end of list
a = lastmaterial;
a->next = malloc(sizeof(material_t));
a->next->prev = a;
a = a->next;
}
lastmaterial = a;
a->next = NULL;
// props
a->id = id;
a->name = strdup(name);
a->weightrating = weightrating;
a->flags = addflagpile(NULL, NULL);
return a;
}
objectclass_t *addoc(enum OBCLASS id, char *name, char *desc, char glyph) {
objectclass_t *a;
// add to the end of the list
if (objectclass == NULL) {
objectclass = malloc(sizeof(objectclass_t));
a = objectclass;
a->prev = NULL;
} else {
// go to end of list
a = lastobjectclass;
a->next = malloc(sizeof(objectclass_t));
a->next->prev = a;
a = a->next;
}
lastobjectclass = a;
a->next = NULL;
// props
a->id = id;
a->name = strdup(name);
a->desc = strdup(desc);
a->glyph = glyph;
a->flags = addflagpile(NULL, NULL);
return a;
}
// create a new object, stacking ok
object_t *addob(obpile_t *where, char *name) {
return addobject(where, name, B_TRUE);
}
// create a new object
// if canstack is true, we are allwoed
// to join similar objects together instead of
// creating new obejct entries.
object_t *addobject(obpile_t *where, char *name, int canstack) {
objecttype_t *ot;
object_t *o = NULL;
char *p,*nsp,*p2;
char numstringmin[BUFLEN];
char numstringmax[BUFLEN];
int howmany = 1;
int i;
int db = B_TRUE;
//flag_t *f;
char *localname;
int wantblessed = B_UNCURSED;
race_t *corpserace = NULL;
brand_t *br;
obmod_t *om;
obmod_t *wantom[MAXOBMODS];
int nom = 0;
int n;
localname = strdup(name);
// check for premods. eg. "flaming xxx"
for (om = firstobmod ; om ; om = om->next) {
if (db) dblog("DB: checking for '%s' in '%s'",om->prefix, localname);
if (strstr(localname, om->prefix)) {
localname = strrep(localname, om->prefix, "", NULL);
if (db) dblog("DB: found obmod prefix '%s'",om->prefix);
wantom[nom] = om;
nom++;
}
}
if (db) {
dblog("DB: called addobject() for %s, canstack = %d",localname, canstack);
}
if (where->owner && hasflag(where->owner->flags, F_NOPACK)) {
if (db) dblog("error giving ob '%s' - owner isn't allowed to carry objects!", name);
return NULL;
}
// we CAN place objects in solid cells - for example,
// placing a fire on top of a wooden door.
/*
if (where->where) {
if (where->where->type != (celltype_t *)DUMMYCELLTYPE) {
assert(!where->where->type->solid);
}
}
*/
// parse name string
nsp = numstringmin;
for (p = localname ; isdigit(*p) ; p++) {
*nsp = *p;
nsp++;
}
*nsp = '\0';
// we have a range...
if (*p == '-') {
nsp = numstringmax;
p++;
for ( ; isdigit(*p) ; p++) {
*nsp = *p;
nsp++;
}
*nsp = '\0';
} else {
strcpy(numstringmax,numstringmin);
}
// are we giving multiple objects?
if (strlen(numstringmin) > 0) {
int min,max;
// first increment name string pointer
// past any spaces
while (!isalpha(*p)) p++;
// now figure out how many
min = atoi(numstringmin);
max = atoi(numstringmax);
if (min == max) {
howmany = min;
} else {
howmany = rnd(min,max);
}
} else {
howmany = 1;
}
// new objects after the game starts have unknown
// blessed/cursed status, so we can never stack them
// if (gamestarted && !hasflag(player->flags, F_DETECTAURAS)) {
if (strstr(p, "blessed") || strstr(p, "holy water")) {
if (db) dblog("DB: ob is blessed (%s)",p);
wantblessed = B_BLESSED;
//canstack = B_FALSE;
} else if (strstr(p, "cursed") || strstr(p, "incompetence")) {
if (db) dblog("DB: ob is cursed (%s)",p);
wantblessed = B_CURSED;
//canstack = B_FALSE;
}
// }
if (strstr(p, "corpse")) {
int len;
char racename[BUFLEN];
p2 = strstr(p, "corpse");
len = p2 - p;
snprintf(racename, len, "%s",p);
corpserace = findracebyname(racename);
ot = findot(OT_CORPSE);
} else if (strstr(p, "statue of ")) {
char racename[BUFLEN];
// go to end
p2 = strstr(p, "statue of ");
p2 += 10; // now on either 'a' or 'an'
p2++; // now on either ' ' or 'n'
for (;*p2 != ' ';p2++);
p2++;
// now at start of name
sprintf(racename, "%s",p2);
corpserace = findracebyname(racename);
ot = findot(OT_STATUE);
} else if (strstr(p, " head")) {
int len;
char racename[BUFLEN];
p2 = strstr(p, " head");
len = p2 - p + 1;
snprintf(racename, len, "%s",p);
corpserace = findracebyname(racename);
ot = findot(OT_HEAD);
} else {
// make sure we can find the requested object type
if (db) dblog("DB: Looking for object name '%s'", p );
ot = findotn(p);
if (!ot) {
//if (gamestarted) msg("DB: No match for object name '%s'", p );
if (db) dblog("DB: No match for object name '%s'", p );
return NULL;
}
}
if (db) dblog("DB: FOUND: ot->name = '%s'", ot->name );
if (ot->id == OT_WOODENDOOR) {
dblog("xxx");
}
// don't give nopickup objects to lifeforms
if (hasflag(ot->flags, F_NOPICKUP) && where->owner) {
return NULL;
}
if (ot->obclass->id == OC_SPELL) {
if (db) dblog("DB: Cannot give a spell object! object name '%s'", p );
return NULL;
}
// override canstack if required
if (!hasflag(ot->flags, F_STACKABLE)) {
if (db) dblog("DB: setting canstack = false, objecttype '%s' not stackable", ot->name );
canstack = B_FALSE;
}
// special checks for unique objects
if (hasflag(ot->flags, F_UNIQUE)) {
// does this unique ob already exist?
if (obexists(ot->id)) {
if (db) dblog("DB: Unique ob %s already exists!", p );
return NULL;
}
// otherwise make sure we are only getting one
howmany = 1;
}
if (db) dblog("DB: '%s' -> adding %d x %s",name, howmany, ot->name);
for (i = 0; i < howmany; i++) {
int added = B_FALSE;
if (canstack) {
object_t *existob;
if (db) dblog("DB: Looking for stacks...");
// TODO: if object is stackable
// TODO: if (hasflag(ob,stackable) && hasobject(where, ot)) {
// does the pile already contain one?
existob = canstacknewot(where, ot);
if (existob) {
if (db) dblog("DB: STACK FOUND (%d x %s). Adding %d obs to existing stack.",existob->amt, existob->type->name, howmany);
existob->amt++;
added = B_TRUE;
o = existob;
} else {
if (db) dblog("DB: No stacks found.");
}
}
if (!added) {
flag_t *f;
if (db) dblog("DB: Creating new object (%s).",ot->name);
// otherwise add to list
if (where->first == NULL) {
where->first = malloc(sizeof(object_t));
o = where->first;
o->prev = NULL;
} else {
// go to end of list
o = where->last;
o->next = malloc(sizeof(object_t));
o->next->prev = o;
o = o->next;
}
where->last = o;
o->next = NULL;
// fill in props
o->id = nextoid++; // increment next ob id
o->type = ot;
o->pile = where;
o->birthtime = curtime;
// inherit props from objecttype
o->material = ot->material;
assert(o->material);
o->weight = ot->weight;
o->flags = addflagpile(NULL, o);
o->inscription = NULL; // non-inherited
// inherit flags from objecttype
copyflags(o->flags, ot->flags, NA);
// random flags...
f = hasflag(o->flags, F_RNDCHARGES);
if (f) {
int amt;
flag_t *chargeflag;
amt = rnd(f->val[0], f->val[1]);
chargeflag = hasflag(o->flags, F_CHARGES);
if (chargeflag) {
chargeflag->val[0] = amt;
chargeflag->val[1] = amt;
chargeflag->known = B_FALSE;
} else {
chargeflag = addflag_real(o->flags, F_CHARGES, amt, amt, NA, NULL, PERMENANT, B_UNKNOWN, -1);
}
}
// non-inherited from here on:
// if adding to a player...
if (where->owner) {
if (o->type->obclass->id == OC_MONEY) {
o->letter = '$';
} else {
o->letter = getnextletter(where, NULL);
}
} else {
// new object on the ground - has no letter yet.
o->letter = '\0';
}
if (canstack) {
// add the full amount!
o->amt = howmany;
} else {
o->amt = 1;
}
if (hasflag(o->flags, F_NOBLESS)) {
setblessed(o, B_UNCURSED);
} else {
setblessed(o, wantblessed);
}
o->blessknown = B_FALSE;
if (canstack) {
// stop looping through
break;
}
}
}
// other special changes we need to make based on what was
// asked for
if (!loading && o) {
char *p2;
int bonus = 0;
// corpses - fill in details
if (o->type->id == OT_CORPSE) {
flag_t *rf, *cf;
assert(corpserace);
o->weight = corpserace->weight;
o->material = corpserace->material;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL);
// override ot_corpse nutrition flag based on race's size
rf = hasflag(corpserace->flags, F_SIZE);
if (rf) {
cf = hasflag(o->flags, F_EDIBLE);
if (cf) {
switch (rf->val[0]) {
case SZ_MINI:
cf->val[1] = 5;
break;
case SZ_TINY:
cf->val[1] = 25;
break;
case SZ_SMALL:
cf->val[1] = 55;
break;
case SZ_MEDIUM:
cf->val[1] = 90;
break;
case SZ_HUMAN:
cf->val[1] = 100;
break;
case SZ_LARGE:
cf->val[1] = 150;
break;
case SZ_HUGE:
cf->val[1] = 200;
break;
case SZ_ENORMOUS:
cf->val[1] = 250;
break;
}
}
}
} else if (o->type->id == OT_STATUE) {
flag_t *f, *rf;
float ratio;
if (!corpserace) {
cell_t *where;
where = getoblocation(o);
// select random race
corpserace = getrandomrace(where->map);
}
ratio = o->material->weightrating / corpserace->material->weightrating;
o->weight = corpserace->weight * ratio;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL);
// set impassable size
f = hasflag(o->flags, F_IMPASSABLE);
if (f) {
rf = hasflag(corpserace->flags, F_SIZE);
if (rf) {
f->val[0] = rf->val[0];
} else {
killflag(f);
}
}
// set ob hp
f = hasflag(o->flags, F_OBHP);
if (f) {
rf = hasflag(corpserace->flags, F_HITDICE);
if (rf) {
int maxhp;
maxhp = (rf->val[0] * 4) + rf->val[1];
f->val[0] = maxhp;
f->val[1] = maxhp;
}
}
} else if (o->type->id == OT_HEAD) {
flag_t *rf, *cf;
assert(corpserace);
o->weight = corpserace->weight;
o->material = corpserace->material;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL);
// override ot_corpse nutrition flag based on race's size
rf = hasflag(corpserace->flags, F_SIZE);
if (rf) {
cf = hasflag(o->flags, F_EDIBLE);
if (cf) {
switch (rf->val[0]) {
case SZ_MINI:
cf->val[1] = 1;
break;
case SZ_TINY:
cf->val[1] = 2;
break;
case SZ_SMALL:
cf->val[1] = 5;
break;
case SZ_MEDIUM:
cf->val[1] = 25;
break;
case SZ_HUMAN:
cf->val[1] = 50;
break;
case SZ_LARGE:
cf->val[1] = 75;
break;
case SZ_HUGE:
cf->val[1] = 100;
break;
case SZ_ENORMOUS:
cf->val[1] = 150;
break;
}
}
}
}
for (n = 0; n < nom; n++) {
// add flags from obmod
copyflags(o->flags, wantom[n]->flags, FROMOBMOD);
// other effects...
switch (wantom[n]->id) {
case OM_FLAMING: // flaming weapons are immune to fire
if (o->type->obclass->id == OC_WEAPON) {
if (!isimmuneto(o->flags, DT_FIRE)) {
addflag(o->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL);
}
}
break;
case OM_MASTERWORK:
if (isweapon(o) || isarmour(o)) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] *= 2;
f->val[1] *= 2;
}
}
if (isweapon(o)) {
flag_t *f;
// always does near high end of damage range
f = hasflag(o->flags, F_DAM);
if (f) {
int ndice,dsides,bonus;
ndice = f->val[0];
dsides = f->val[1];
bonus = f->val[2]; if (bonus < 0) bonus = 0;
// half the number of sides on the dice (dsides).
dsides /= 2; if (dsides < 1) dsides = 1;
// increase modifier by (ndice * dsides)
bonus += (ndice * dsides);
if (bonus <= 0) {
bonus = NA;
}
f->val[0] = ndice;
f->val[1] = dsides;
f->val[2] = bonus;
}
}
break;
case OM_SHODDY:
if (isweapon(o) || isarmour(o)) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] /= 2; if (f->val[0] < 1) f->val[0] = 1;
f->val[1] /= 2; if (f->val[1] < 1) f->val[1] = 1;
}
}
if (isweapon(o)) {
flag_t *f;
// half the damage
f = hasflag(o->flags, F_DAM);
if (f) {
int dsides,bonus;
dsides = f->val[1];
bonus = f->val[2]; if (bonus < 0) bonus = 0;
// half the number of sides on the dice (dsides).
dsides /= 2;
// half modifier
if (bonus > 0) {
bonus /= 2; if (bonus <= 0) bonus = NA;
}
f->val[1] = dsides;
f->val[2] = bonus;
}
}
break;
default:
break;
}
}
// check for bonuses. eg. "+1"
p2 = strchr(p, '+');
if (p2) {
char *p3;
char numbuf[BUFLENSMALL];
p3 = numbuf;
p2++;
while (isdigit(*p2)) {
*p3 = *p2;
p2++;
p3++;
}
*p3 = '\0';
bonus += atoi(numbuf);
}
// check for penalties. eg. "-1"
p2 = strchr(p, '-');
if (p2) {
char *p3;
char numbuf[BUFLENSMALL];
p3 = numbuf;
p2++;
while (isdigit(*p2)) {
*p3 = *p2;
p2++;
p3++;
}
*p3 = '\0';
bonus -= atoi(numbuf);
}
addflag_real(o->flags, F_BONUS, bonus, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
// check for postmods. eg. "xxx of pyromania"
for (br = firstbrand ; br ; br = br->next) {
if (strstr(name, br->suffix)) {
// does this brand apply to this objecttype?
if (brandappliesto(br, o->type)) {
copyflags(o->flags, br->flags, FROMBRAND);
addflag(o->flags, F_HASBRAND, br->id, NA, NA, NULL);
} else {
// doesn't exist!
return NULL;
}
}
}
}
free(localname);
if (where->owner) {
// new owner gains "hold confer" flags conferred by this object
giveobflags(where->owner, o, F_HOLDCONFER);
}
// special cases
if (o->type->id == OT_VENDINGMACHINE) {
char buf[BUFLEN];
cell_t *loc;
loc = getoblocation(o);
// populate with objects
for (i = 0; i < 10; i++) {
objecttype_t *ot2;
strcpy(buf, "");
while (!strcmp(buf, "")) {
real_getrandomob(loc->map, buf, RO_NONE, NA, loc->map->depth + rnd(10,15));
// make sure you can hold it
ot2 = findotn(buf);
if (!ot2 || hasflag(ot2->flags, F_NOPICKUP)) {
strcpy(buf, "");
}
}
// replace "1 potion" with "a potion"
if (strstr(buf, "1 ") == buf) {
char temp[BUFLEN];
strcpy(temp, buf);
sprintf(buf, "a %s",temp + 2);
}
addflag(o->flags, F_CONTAINSOB, 'a' + i, NA, NA, buf);
}
}
// return the last object given
return o;
}
// add objects in a circle
// returns # added.
int addobburst(cell_t *where, int range, int dirtype, char *name) {
int (*distfunc)(cell_t *, cell_t *);
int x,y;
cell_t *c;
int nadded = 0;
if (!where) return 0;
if (dirtype == DT_ORTH) {
distfunc = getcelldistorth;
} else {
distfunc = getcelldist;
}
for (y = where->y - range; y <= where->y + range; y++) {
for (x = where->x - range; x <= where->x + range; x++) {
c = getcellat(where->map, x,y);
if (distfunc(where, c) <= range) {
if (cellwalkable(NULL, c, NULL)) {
if (addob(c->obpile, name)) nadded++;
}
}
}
}
return nadded;
}
obmod_t *addobmod(enum OBMOD id, char *prefix) {
obmod_t *a, *om;
char buf[BUFLEN];
//flag_t *f;
// does this modj already exist?
om = findobmod(id);
assert(!om);
// add to the end of the list
if (firstobmod == NULL) {
firstobmod = malloc(sizeof(obmod_t));
a = firstobmod;
a->prev = NULL;
} else {
// go to end of list
a = lastobmod;
a->next = malloc(sizeof(obmod_t));
a->next->prev = a;
a = a->next;
}
lastobmod = a;
a->next = NULL;
// props
a->id = id;
sprintf(buf, "%s ",prefix);
a->prefix = strdup(buf);
a->flags = addflagpile(NULL, NULL);
return a;
}
obpile_t *addobpile(lifeform_t *owner, cell_t *where) {
obpile_t *op;
op = malloc(sizeof(obpile_t));
op->first = NULL;
op->last = NULL;
op->owner = owner;
op->where = where;
return op;
}
objecttype_t *addot(int id, char *name, char *description, int material, float weight, int obclassid) {
objecttype_t *a, *ot;
//flag_t *f;
// does this ob already exist?
ot = findot(id);
assert(!ot);
// add to the end of the list
if (objecttype == NULL) {
objecttype = malloc(sizeof(objecttype_t));
a = objecttype;
a->prev = NULL;
} else {
// go to end of list
a = lastobjecttype;
a->next = malloc(sizeof(objecttype_t));
a->next->prev = a;
a = a->next;
}
lastobjecttype = a;
a->next = NULL;
// props
a->id = id;
a->name = strdup(name);
a->desc = strdup(description);
a->material = findmaterial(material);
a->weight = weight;
a->obclass = findoc(obclassid);
a->flags = addflagpile(NULL, NULL);
// inherit flags from object class
/*
for (f = a->obclass->flags->first ; f ; f = f->next) {
addflag(a->flags, f->id, f->val[0], f->val[1], f->val[2], f->text);
}
*/
copyflags(a->flags, a->obclass->flags, NA);
if (a->material) {
// inherit flags from material
/*
for (f = a->material->flags->first ; f ; f = f->next) {
addflag(a->flags, f->id, f->val[0], f->val[1], f->val[2], f->text);
}
*/
copyflags(a->flags, a->material->flags, FROMMAT);
}
// for easy addition of flags
lastot = a;
return a;
}
// adjust damage based on material being damaged
void adjustdammaterial(unsigned int *dam, enum DAMTYPE damtype, enum MATERIAL mat) {
// adjust based on material
if (mat == MT_MAGIC) {
switch (damtype) {
case DT_DIRECT:
case DT_NONE:
break;
default:
*dam = 0;
return;
}
} else if (mat == MT_GAS) {
switch (damtype) {
case DT_HOLY:
case DT_DIRECT:
case DT_NONE:
break;
default:
*dam = 0;
return;
}
}
// adjust based on damage type
if (damtype == DT_FIRE) {
switch (mat) {
case MT_PAPER:
*dam *= 3;
break;
case MT_WOOD:
case MT_ICE:
case MT_SLIME:
*dam *= 2;
break;
case MT_METAL:
case MT_BONE:
case MT_STONE:
case MT_BLOOD:
case MT_FIRE: // immune to itself
*dam = 0;
break;
default:
break;
}
} else if (damtype == DT_CHOP) {
switch (mat) {
case MT_WOOD:
*dam *= 2;
break;
case MT_SLIME:
*dam /= 2;
break;
default:
break;
}
} else if (damtype == DT_SLASH) {
switch (mat) {
case MT_WOOD:
case MT_SLIME:
case MT_BONE:
*dam /= 2;
break;
default:
break;
}
} else if (damtype == DT_PIERCE) {
switch (mat) {
case MT_WOOD:
case MT_SLIME:
case MT_BONE:
*dam /= 2;
break;
default:
break;
}
} else if (damtype == DT_BASH) {
switch (mat) {
case MT_PAPER:
*dam = 0;
break;
default:
break;
}
} else if (damtype == DT_WATER) {
switch (mat) {
case MT_WATER: // immune to itself
*dam = 0;
break;
default:
break;
}
}
}
void adjustdamob(object_t *o, unsigned int *dam, enum DAMTYPE damtype) {
// objects can't get hurt the turn they
// were created.
if (o->birthtime == curtime) {
*dam = 0;
return;
}
// only some objects can be hurt
if (!hasflag(o->flags, F_DAMAGABLE)) {
if (damtype != DT_DIRECT) {
*dam = 0;
return;
}
}
// immune?
if (isimmuneto(o->flags, damtype)) {
*dam = 0;
return;
}
if (isresistantto(o->flags, damtype)) {
*dam /= 2;
}
if (isvulnto(o->flags, damtype)) {
*dam *= 2;
}
// poison gas never hurts objects
if (damtype == DT_POISONGAS) {
*dam = 0;
return;
}
// adjust damage
if (o->blessed == B_BLESSED) {
// chance of no hp loss
if (rnd(1,2) == 1) {
lifeform_t *owner;
owner = o->pile->owner;
if (owner && isplayer(owner)) {
// become known if owned by player
if (!isblessknown(o)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("Your %s pulses with holy light as it is struck!",noprefix(obname));
}
o->blessknown = B_TRUE;
}
return;
}
} else if (o->blessed == B_CURSED) {
// chance of double damage!
if (rnd(1,2) == 1) {
lifeform_t *owner;
(*dam) *= 2;
owner = o->pile->owner;
if (owner && (owner->controller == C_PLAYER)) {
// become known if a player's
if (!isblessknown(o)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("Your %s emits a wave of dread as it is struck!",noprefix(obname));
}
o->blessknown = B_TRUE;
}
}
}
// adjust damage for material too
adjustdammaterial(dam, damtype, o->material->id);
}
// adjust price for magical effects etc
/*
void adjustprice(objecttype_t *ot, float *price) {
int min,max;
flag_t *f;
// bonuses/penalties
}
*/
void appendinscription(object_t *o, char *text) {
if (o->inscription) {
int len;
char *buf;
len = strlen(o->inscription) + strlen(text) + 1;
if (len > BUFLEN) len = BUFLEN;
buf = malloc(len * sizeof(char));
snprintf(buf, len, "%s%s",o->inscription, text);
setinscription(o, buf);
free(buf);
} else {
setinscription(o, text);
}
}
int blessob(object_t *o) {
char obname[BUFLEN];
int rv = B_FALSE;
lifeform_t *owner;
getobname(o, obname, o->amt);
switch (o->blessed) {
case B_BLESSED:
rv = B_TRUE;
break;
case B_CURSED:
case B_UNCURSED:
setblessed(o, B_BLESSED);
break;
}
if (rv == B_FALSE) {
int seen = B_FALSE;
owner = o->pile->owner;
if (owner) {
if (isplayer(owner)) {
msg("Your %s is bathed in a divine glow!", noprefix(obname));
seen = B_TRUE;
} else if (haslos(player, owner->cell)) {
char ownername[BUFLEN];
msg("%s%s %s is bathed in a divine glow!", ownername, getpossessive(ownername),
noprefix(obname));
seen = B_TRUE;
}
} else if (haslos(player, o->pile->where)) {
msg("%s is bathed in a divine glow!", obname);
seen = B_TRUE;
}
if (seen) {
o->blessknown = B_TRUE;
}
}
return rv;
}
void brightflash(cell_t *centre, int range, lifeform_t *immunelf) {
int x,y;
cell_t *c;
animradial(centre, range, '}');
// announce
if (haslos(player, centre) && !isblind(player)) {
msg("You see an intense flash of light!");
}
/*
if (getcelldist(player->cell, centre) <= range) {
// player is in range but can't see - announce it beacuse it
// will make you deaf!
youhear(centre, "a deafening bang!");
} else { // not in range of the flash
youhear(centre, "a loud bang!");
}
*/
// blind monsters
for (y = centre->y - range; y <= centre->y + range; y++) {
for (x = centre->x - range; x <= centre->x + range; x++) {
c = getcellat(centre->map, x, y);
if (c) {
lifeform_t *lf;
lf = haslf(c);
if (lf && (lf != immunelf)) {
if (haslos(lf, centre) && !isblind(lf)) {
if (!isplayer(lf)) { // we'll blind the player last
if (!eyesshaded(lf)) {
if (lfhasflag(lf, F_SEEINDARK)) {
// blind for 20-30 turns
addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(20,30));
} else {
// blind for 5-10 turns
addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(5,10));
}
}
}
} else {
// TODO: deafen
}
}
}
}
}
// handle the player last, so that you see all the
// 'xx is blinded' messages before losing your own
// sight.
if (player != immunelf) {
if (haslos(player, centre) && (getcelldist(player->cell, centre) <= range) && !isblind(player)) {
if (!eyesshaded(player)) {
if (lfhasflag(player, F_SEEINDARK)) {
msg("You eyes feel like they are burning!");
addtempflag(player->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(20,30));
} else {
addtempflag(player->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(5,10));
}
}
}
}
}
// does the pile "op" have an object we can
// stack "o" with
object_t *canstackob(obpile_t *op, object_t *match) {
object_t *o;
if (!hasflag(match->flags, F_STACKABLE)) {
return NULL;
}
for (o = op->first ; o ; o = o->next) {
if (o != match) {
if (obpropsmatch(o, match)) return o;
}
}
return NULL;
}
// does the pile "op" have an object we can
// stack a new object of type "ot" with
object_t *canstacknewot(obpile_t *op, objecttype_t *match) {
object_t *o;
if (!hasflag(match->flags, F_STACKABLE)) {
return NULL;
}
for (o = op->first ; o ; o = o->next) {
if (o->type == match) {
if (isplainob(o)) return o;
}
}
return NULL;
}
int changemat(object_t *o, enum MATERIAL mat) {
material_t *m;
flag_t *f, *nextf;
m = findmaterial(mat);
if (!m) {
return E_FAILED;
}
if (o->material->id == mat) return E_NOEFFECT;
// won't work on pyromania objects
f = hasflag(o->flags, F_FLAMESTRIKE);
if (f) {
if (isplayer(o->pile->owner)) {
f->known = B_TRUE;
} else if (o->pile->owner && haslos(player, o->pile->owner->cell)) {
f->known = B_TRUE;
} else if (haslos(player, o->pile->where)) {
f->known = B_TRUE;
}
return E_NOEFFECT;
}
// remove flags which came from old material
for (f = o->flags->first ; f; f = nextf) {
nextf = f->next;
if (f->lifetime == FROMMAT) {
killflag(f);
}
}
// change material
o->material = m;
// inherit flags from new material
copyflags(m->flags, o->flags, FROMMAT);
if (mat == MT_ICE) {
// if it turned to ice..
// it stops burning
extinguish(o);
// it will melt...
if (!hasflag(o->flags, F_OBHP)) {
int myhp;
// give hp
myhp = getobweight(o) * 20;
if (myhp <= 0) myhp = 2;
addtempflag(o->flags, F_OBHP, myhp, myhp, NA, NULL, FROMMAT);
}
if (!hasflag(o->flags, F_DAMAGABLE)) {
addtempflag(o->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL, FROMMAT);
}
// make it melt
addtempflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL, FROMMAT);
}
return B_FALSE;
}
objecttype_t *checkobnames(char *haystack, char *needle) {
objecttype_t *ot;
char *pluralname;
int db = B_TRUE;
// search for exact match
if (!strcmp(haystack, needle)) {
// found it!
if (db) dblog("checkobnames(): got exact match: '%s'",haystack);
return ot;
}
// search for words ending in "s" (eg. "swords")
pluralname = strdup(needle);
//if (db) dblog("findotn(): plural is '%s'",pluralname);
if (pluralname[strlen(pluralname)-1] == 's') {
// remove trailing 's'
pluralname[strlen(pluralname)-1] = '\0';
// search again (for exact matches)
if (!strcmp(haystack, pluralname)) {
if (db) dblog("checkobnames(): got match after stripping 's': '%s' -> '%s'",pluralname, needle);
free(pluralname);
return ot;
}
// search for words ending in "es" (eg. tomatoes)
if ((pluralname[strlen(pluralname)-1] == 'e') &&
(pluralname[strlen(pluralname)-2] == 'o')
) {
// remove trailing 'es'
pluralname[strlen(pluralname)-1] = '\0';
pluralname[strlen(pluralname)-2] = '\0';
//if (db) dblog("findotn(): pluralname without 'es' is '%s'",pluralname);
// search again
if (!strcmp(haystack, pluralname)) {
if (db) dblog("checkobnames(): got match after stripping 'es': '%s' -> '%s'",pluralname, needle);
free(pluralname);
return ot;
}
}
}
free(pluralname);
return NULL;
}
void copyobprops(object_t *dst, object_t *src) {
dst->material = src->material;
dst->weight = src->weight;
if (src->inscription) {
dst->inscription = strdup(src->inscription);
}
setblessed(dst, src->blessed);
dst->blessknown = src->blessknown;
// copy flags
while (dst->flags->first) {
killflag(dst->flags->first);
}
copyflags(dst->flags, src->flags, NA);
}
int countnames(char **list) {
int count;
for (count = 0; list[count]; count++);
return count;
}
int countobs(obpile_t *op) {
object_t *o;
int count = 0;
for (o = op->first ; o ; o = o->next) {
count++;
}
return count;
}
int curseob(object_t *o) {
int rv = B_FALSE;
switch (o->blessed) {
case B_BLESSED:
setblessed(o, B_UNCURSED);
break;
case B_CURSED:
rv = B_TRUE;
break;
case B_UNCURSED:
setblessed(o, B_CURSED);
break;
}
return rv;
}
void damageallobs(object_t *exception, obpile_t *op, int howmuch, int damtype) {
object_t *o, *nexto;
for (o = op->first ; o ; o = nexto) {
nexto = o->next;
if ((o != exception) && !hasflag(o->flags, F_DEAD)) {
takedamage(o, howmuch, damtype);
}
}
}
void explodeob(object_t *o, flag_t *f, int bigness) {
cell_t *c;
int dam;
char obname[BUFLEN];
dam = f->val[0];
c = getoblocation(o);
getobname(o, obname, o->amt);
// announce
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s explodes!", noprefix(obname));
} else if (haslos(player, o->pile->owner->cell)) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
msg("%s%s %s explodes!", lfname, getpossessive(lfname), noprefix(obname));
}
} else if (haslos(player, c)) {
msg("%s explodes!", obname);
}
explodecells(c, dam, bigness ? B_TRUE : B_FALSE, o, bigness ? 1 : 0, B_FALSE);
// hurt everything!
/*
if (bigness) {
int dir;
cell_t *cc;
explodecell(c, dam, (bigness) ? B_TRUE : B_FALSE, NULL);
for (dir = DC_N; dir <= DC_NW; dir++) {
cc = getcellindir(c, dir);
}
} else {
explodecell(c, dam, (bigness) ? B_TRUE : B_FALSE, NULL);
}
*/
}
void extinguish(object_t *o) {
flag_t *f;
char obname[BUFLEN];
f = hasflag(o->flags, F_ONFIRE);
getobname(o, obname, o->amt);
if (f) {
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s %s extinguished.", noprefix(obname),
(o->amt == 1) ? "is" : "are");
} else if (haslos(player, o->pile->owner->cell)) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
msg("%s%s %s %s extinguished.", lfname, getpossessive(lfname), noprefix(obname),
(o->amt == 1) ? "is" : "are");
}
} else if (o->pile->where && haslos(player, o->pile->where)) {
getobname(o, obname, o->amt);
msg("%s %s extinguished.", obname,
(o->amt == 1) ? "is" : "are");
}
killflag(f);
}
}
material_t *findmaterial(int id) {
material_t *m;
for (m = material ; m ; m = m->next) {
if (m->id == id) return m;
}
return NULL;
}
objectclass_t *findoc(int id) {
objectclass_t *oc;
for (oc = objectclass ; oc ; oc = oc->next) {
if (oc->id == id) return oc;
}
return NULL;
}
object_t *findobbyid(obpile_t *op, long oid) {
object_t *o;
for (o = op->first; o ; o = o->next) {
if (o->id == oid) return o;
}
return NULL;
}
object_t *findobl(obpile_t *op, char let) {
object_t *o;
for (o = op->first; o ; o = o->next) {
if (o->letter == let) return o;
}
return NULL;
}
brand_t *findbrand(enum BRAND id) {
brand_t *om;
for (om = firstbrand ; om ; om = om->next) {
if (om->id == id) return om;
}
return NULL;
}
obmod_t *findobmod(enum OBMOD id) {
obmod_t *om;
for (om = firstobmod ; om ; om = om->next) {
if (om->id == id) return om;
}
return NULL;
}
objecttype_t *findot(enum OBTYPE id) {
objecttype_t *ot;
for (ot = objecttype ; ot ; ot = ot->next) {
if (ot->id == id) return ot;
}
return NULL;
}
objecttype_t *findotn(char *name) {
objecttype_t *ot;
knowledge_t *k;
char *modname;
char *p;
int db = B_FALSE;
brand_t *om;
modname = strdup(name);
// make some replacements
//replace scrolls with scroll etc
modname = strrep(modname, "berries ", "berry ", NULL);
modname = strrep(modname, "blocks ", "block ", NULL);
modname = strrep(modname, "cans ", "can ", NULL);
modname = strrep(modname, "chunks ", "chunk ", NULL);
modname = strrep(modname, "gems ", "gem ", NULL);
modname = strrep(modname, "loaves ", "load ", NULL);
modname = strrep(modname, "lumps ", "lump ", NULL);
modname = strrep(modname, "pieces ", "piece ", NULL);
modname = strrep(modname, "piles ", "pile ", NULL);
modname = strrep(modname, "pools ", "pool ", NULL);
modname = strrep(modname, "potions ", "potion ", NULL);
modname = strrep(modname, "puddles ", "puddle ", NULL);
modname = strrep(modname, "rings ", "ring ", NULL);
modname = strrep(modname, "scrolls ", "scroll ", NULL);
modname = strrep(modname, "splashes ", "splash ", NULL);
modname = strrep(modname, "suits ", "suit ", NULL);
modname = strrep(modname, "vials ", "vial ", NULL);
// only at start...
if (strstr(modname, "the ") == modname) modname = strrep(modname, "the ", "", NULL);
if (strstr(modname, "an ") == modname) modname = strrep(modname, "an ", "", NULL);
if (strstr(modname, "a ") == modname) modname = strrep(modname, "a ", "", NULL);
modname = strrep(modname, "blessed ", "", NULL);
modname = strrep(modname, "uncursed ", "", NULL);
modname = strrep(modname, "cursed ", "", NULL);
//realloc(modname, strlen(temp)); strcpy(modname, temp); free(temp); // swap
// strip out pre mods
modname = strrep(modname, "flaming ", "", NULL);
modname = strrep(modname, "headless ", "", NULL);
// strip out post mods
for (om = firstbrand; om ; om = om->next) {
modname = strrep(modname, om->suffix, "", NULL);
}
// special cases
modname = strrep(modname, "holy water", "water", NULL);
modname = strrep(modname, "incompetence", "competence", NULL);
// skip past bonusses
p = strchr(modname, '+');
if (p) {
while (!isalpha(*p)) {
p++;
}
strcpy(modname, p);
}
p = strchr(modname, '-');
if (p) {
while (!isalpha(*p)) {
p++;
}
strcpy(modname, p);
}
if (db) dblog("findotn(): modname is '%s'",modname);
// check for exact matches on real name (and plural versions) first
for (ot = objecttype ; ot ; ot = ot->next) {
if (checkobnames(ot->name, modname)) {
free(modname);
return ot;
}
}
// then matches on hidden name
for (k = knowledge; k ; k = k->next) {
if (checkobnames(k->hiddenname, modname)) {
free(modname);
// found it!
return findot(k->id);
}
}
// then partial matches on real name
for (ot = objecttype ; ot ; ot = ot->next) {
if (strstr(ot->name, modname)) {
free(modname);
// found it!
return ot;
}
}
free(modname);
return NULL;
}
// ie. "the apple is xxx"
// ie. "the apples are xxx"
char *getfillingname(int nutrition) {
if (nutrition > 100) {
return "extremely substantial";
} else if (nutrition >= 90) {
return "very filling";
} else if (nutrition >= 70) {
return "ample for a meal";
} else if (nutrition >= 50) {
return "enough for a light meal";
} else if (nutrition >= 25) {
return "snack-sized";
} else if (nutrition > 0) {
return "barely worth eating";
}
// ie. < 0
return "of zero nutritional substance";
}
int getfirearmrange(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_RANGE);
if (f) {
return f->val[0];
}
return 0;
}
int getfirearmspeed(object_t *o) {
int firespeed;
flag_t *f;
f = hasflag(o->flags, F_FIRESPEED);
if (f) {
firespeed = f->val[0];
} else {
// default to very slow
firespeed = 1;
}
return firespeed;
}
char getglyph(object_t *o) {
flag_t *f;
int isopen;
char g = ' '; // default
if (isdoor(o, &isopen)) {
if (isopen) {
g = '-';
} else {
g = '+';
}
} else {
f = hasflag(o->flags, F_GLYPH);
if (f) {
g = f->text[0];
} else {
g = o->type->obclass->glyph;
}
}
return g;
}
void fragments(cell_t *centre, char *what, int speed) {
cell_t *c,*dst;
int n;
int dir;
for (dir = DC_N; dir <= DC_NW; dir++) {
int maxdist = 0;
int wantdist = 0;
int done = B_FALSE;
// get max distance
c = centre;
done = B_FALSE;
while (!done) {
c = getcellindir(c, dir);
if (c && cellwalkable(NULL, c, NULL)) {
maxdist++;
} else {
done = B_TRUE;
}
}
// pick random distance
if (maxdist == 0) {
wantdist = 0;
} else {
wantdist = rnd(1,maxdist);
}
// go that far
dst = centre;
for (n = 0; n < wantdist; n++) {
dst = getcellindir(dst, dir);
}
// add object
addob(dst->obpile, what);
}
}
void genhiddennames(void) {
objecttype_t *ot;
for (ot = objecttype ; ot ; ot = ot->next) {
if (hasflag(ot->flags, F_HASHIDDENNAME)) {
char *thisname;
thisname = genhiddenname(ot->obclass->id);
addknowledge(ot->id, thisname, B_UNKNOWN);
// some descriptions confer other effecst too...
if (strstr(thisname, "glowing")) {
addflag(ot->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
} else if (strstr(thisname, "flourescent")) {
addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL);
} else if (strstr(thisname, "luminous")) {
addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL);
}
free(thisname);
}
}
}
char *genhiddenname(enum OBCLASS id) {
hiddenname_t *hn;
for (hn = firsthiddenname ; hn ; hn = hn->next) {
if (hn->obclass == id) {
if (!hn->used) {
// found one
hn->used = B_TRUE;
return hn->text;
}
}
}
assert(1 == 0);
return NULL;
}
// returns -1 if object doesn't have the flag
int getcharges(object_t *o) {
flag_t *f;
int amt = -1;
f = hasflag(o->flags, F_CHARGES);
if (f) {
amt = f->val[0];
}
return amt;
}
int getobaccuracy(object_t *wep) {
int acc;
flag_t *f;
acc = 100; // default accuracy of 100%
if (wep) {
// blessed weapons have better base accuracy
if (wep->blessed == B_BLESSED) acc += 50;
// override with weapon's (lack of) accuracy
f = hasflag(wep->flags, F_ACCURACY);
if (f) {
// ie. accuracy of 100% means no penalty
// ie. accuracy of 75% means 25% penalty
// etc
acc = f->val[0];
}
// modify for attacker's level
if (wep->pile->owner) {
acc += (wep->pile->owner->level * 2);
}
}
return acc;
}
int getobbonus(object_t *o) {
flag_t *f;
int bonus = 0;
for (f = o->flags->first ; f ; f = f->next) {
if (f->id == F_BONUS) {
bonus += f->val[0];
}
}
return bonus;
}
// returns value of obejcts, in gold
int getobvalue(object_t *o) {
float price;
flag_t *f;
if (o->type->id == OT_GOLD) {
return o->amt;
}
// base value: weight * material value
price = (float)getobweight(o) * (float)getmaterialvalue(o->material->id);
//adjustprice(o->type, &price);
// fixed prices
f = hasflag(o->flags, F_VALUE);
if (f) {
price += f->val[0];
}
for (f = o->flags->first ; f ; f = f->next) {
// damage
if (f->id == F_DAM) {
int min,max;
getdamrange(o->flags, &min, &max);
price += (max*5);
}
// armour rating
if (f->id == F_ARMOURRATING) {
float rating;
rating = (float)f->val[0];
price += (rating * 20.0);
}
// bonus/penalties
if (f->id == F_BONUS) {
price += (f->val[0] * 100);
}
// food
if (f->id == F_EDIBLE) {
price += ((float)f->val[0] / 5.0);
}
// one-off magical effects (linkspell) - use spell price
if (f->id == F_LINKSPELL) {
//flag_t *ff;
objecttype_t *sp;
sp = findot(f->val[0]);
// TODO : spelllevel^2 * 20
// ooooooooo
}
}
// TODO: conferred intrinsics - depends on which one
// TODO: conferred spells - use spell price * multiplier
// TODO: potions - hardcode?
// speical material prices like velvet, silk
if (strstr(o->type->name, "velvet")) {
price *= 1.5;
}
if (strstr(o->type->name, "silk")) {
price *= 2;
}
// TODO: extra price for tech/tools - hardcode?
// rarity
f = hasflag(o->flags, F_RARITY);
if (f) {
if (f->val[1] >= 70) {
price /= 1.5;
} else if (f->val[1] <= 40) {
price *= 1.5;
} else if (f->val[1] <= 20) {
price *= 2;
} else if (f->val[1] <= 10) {
price *= 5;
}
}
// blessed/cursed
if (isblessed(o)) price *= 1.5;
if (iscursed(o)) price /= 2;
// minimum
if (price < 1) price = 1;
return (int) price;
}
/*
int getobtypevalue(objecttype_t *ot) {
float price;
if (ot->id == OT_GOLD) {
return 1;
}
// base value: weight * material value
price = (float)ot->weight * (float)getmaterialvalue(ot->material->id);
adjustprice(ot, &price);
return (int) price;
}
*/
// ie. "it has xxx accuracy"
char *getaccuracyname(int accpct) {
if (accpct >= 200) {
return "godlike";
} else if (accpct >= 150) {
return "incredible";
} else if (accpct >= 100) {
return "very good";
} else if (accpct >= 70) {
return "good";
} else if (accpct >= 50) {
return "average";
} else if (accpct >= 30) {
return "poor";
} else if (accpct >= 20) {
return "very poor";
} else if (accpct >= 0) {
return "incredibly poor";
} else {
return "a complete lack of";
}
}
object_t *getammo(lifeform_t *lf) {
object_t *o;
o = hasobwithflag(lf->pack, F_CURAMMO);
return o;
}
object_t *getrandomammo(lifeform_t *lf) {
object_t *gun;
object_t *o;
flag_t *f;
gun = getfirearm(lf);
if (!gun) {
return NULL;
}
// TODO: at the moment we are jsut picking the first
// possible ammo. Need to allow the player to
// pick a specific ammo to use. USe a flag on wep
// to do this? Or a flag on the player?
for (f = gun->flags->first ; f ; f = f->next) {
if (f->id == F_AMMOOB) {
for (o = lf->pack->first ; o ; o = o->next) {
if (o->type->id == f->val[0]) {
return o;
}
}
}
}
return NULL;
}
char *getdamname(enum DAMTYPE damtype) {
switch (damtype) {
case DT_ALL: return "all damage";
case DT_ACID: return "acid";
case DT_MELT: return "melting";
case DT_PIERCE: return "piercing";
case DT_SLASH: return "slashing";
case DT_CLAW: return "claw";
case DT_ELECTRIC: return "electricity";
case DT_EXPLOSIVE: return "explosive";
case DT_FIRE: return "fire";
case DT_BITE: return "bite";
case DT_BASH: return "bludgeoning";
case DT_CHOP: return "chopping";
case DT_COLD: return "cold";
case DT_POISONGAS: return "poison gas";
case DT_PROJECTILE: return "projectile";
case DT_HOLY: return "holy";
case DT_DIRECT: return "direct";
case DT_WATER: return "water";
case DT_DECAY: return "decay";
case DT_MAGIC: return "magical";
case DT_TOUCH: return "touch";
case DT_UNARMED: return "unarmed";
case DT_LIGHT: return "light";
case DT_CRUSH: return "crushing";
default: return "unknown";
}
return "unknown";
}
char *getdamnamenoun(enum DAMTYPE damtype) {
switch (damtype) {
case DT_ALL: return "all damage";
case DT_ACID: return "acid";
case DT_MELT: return "melting";
case DT_PIERCE: return "piercing damage";
case DT_POISONGAS: return "poison gas";
case DT_SLASH: return "slashing damage";
case DT_CLAW: return "claw damage";
case DT_ELECTRIC: return "electricity";
case DT_EXPLOSIVE: return "explosives";
case DT_FIRE: return "fire";
case DT_BITE: return "bite";
case DT_BASH: return "bludgeoning";
case DT_CHOP: return "chopping";
case DT_COLD: return "cold";
case DT_PROJECTILE: return "projectiles";
case DT_HOLY: return "holy damage";
case DT_DIRECT: return "direct damage";
case DT_DECAY: return "decay damage";
case DT_WATER: return "water";
case DT_MAGIC: return "magical damage";
case DT_TOUCH: return "touch effects";
case DT_UNARMED: return "unarmed damage";
case DT_LIGHT: return "light damage";
case DT_CRUSH: return "crushing damage";
default: return "unknown";
}
return "unkmown";
}
char *gethiddenname(object_t *o) {
knowledge_t *k;
// if id'd, return the full name
if (hasflag(o->flags, F_IDENTIFIED)) {
return o->type->name;
}
// otherwise check if it has a hidden name
for (k = knowledge; k ; k = k->next) {
if (k->id == o->type->id) {
// it DOES have a hidden name.
// does the player know about it?
if (k->known) {
// if so, return real name
return o->type->name;
} else {
// otherwise return hidden one
return k->hiddenname;
}
}
}
// no hidden name, return real one
return o->type->name;
}
int getobattackdelay(object_t *o) {
int delay = 100; // ie. 100%
flag_t *f;
f = hasflag(o->flags, F_OBATTACKDELAY);
if (f) {
delay = f->val[0];
}
return delay;
}
float getobhppct(object_t *o) {
float pct;
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
pct = ((float) f->val[0] / (float) f->val[1]) * 100.0;
} else {
pct = 100;
}
return pct;
}
int getletidx(char let) {
int i;
for (i = 0; i < MAXPILEOBS; i++) {
if (letorder[i] == let) return i;
}
return -1;
}
int getmaterialvalue(enum MATERIAL mat) {
switch (mat) {
case MT_NOTHING:
case MT_MAGIC:
case MT_FIRE:
case MT_GAS:
return 0;
case MT_FOOD:
case MT_ICE:
case MT_STONE:
return 1;
case MT_WATER:
return 1;
case MT_FLESH:
case MT_BONE:
case MT_BLOOD:
case MT_SLIME:
return 2;
case MT_CLOTH:
case MT_LEATHER:
return 2;
case MT_WOOD:
return 3;
case MT_PAPER:
case MT_WETPAPER:
return 4;
case MT_PLASTIC:
case MT_RUBBER:
case MT_GLASS:
return 5;
case MT_METAL:
return 6;
case MT_GOLD:
return 7;
}
// default
return 1;
}
int getmaxthrowrange(lifeform_t *lf, object_t *o) {
int maxdist;
float strmod;
float modobweight;
// adjust for lifeform strength
// mighty = div by 10
// this will give us a number in range -44 - 50
strmod = getstatmod(lf, A_STR);
// this will give us a number in range 1 - 10
strmod /= 5;
if (strmod < 1) strmod = 1;
modobweight = getobunitweight(o) / 2;
modobweight /= strmod;
maxdist = 10 - modobweight;
if (maxdist < 0) maxdist = 0;
return maxdist;
}
// select lowest possible letter
char getnextletter(obpile_t *op, char *wantletter) {
int curidx = -1;
char let;
//int db = B_FALSE;
// try 'wantletter' first
if (wantletter && (*wantletter != '\0')) {
if (!pilehasletter(op, *wantletter)) {
return *wantletter;
}
}
curidx = 0; // ie 'a'
for (curidx = 0; curidx < MAXPILEOBS; curidx++) {
// does any other object in the pile have this letter?
let = letorder[curidx];
if (!pilehasletter(op, let)) {
// if we didn't find it, this letter is okay.
return let;
}
}
return '\0';
}
int getnumshards(object_t *o) {
int numshards,maxshards;
maxshards = ceil(getobunitweight(o)) * 10;
if (maxshards < 1) maxshards = 1;
numshards = rnd(1,maxshards);
return numshards;
}
int getnutritionbase(object_t *o) {
float basenutr;
flag_t *f;
if (o->material->id == MT_ICE) {
// use the object's weight
basenutr = getobweight(o) * 10;
} else {
// use nutrition flag
f = hasflag(o->flags, F_EDIBLE);
if (f) {
basenutr = (float)f->val[1];
} else {
return 0;
}
}
return basenutr;
}
int getnutrition(object_t *o) {
float nutrpct;
float nutrition;
if (isrotting(o)) {
nutrition = -(HUNGERCONST/2);
} else {
nutrpct = getnutritionbase(o);
if (nutrpct <= 0) {
nutrition = 0;
} else {
nutrition = pctof(nutrpct, (float) HUNGERCONST);
}
}
if (hasflag(o->flags, F_HEADLESS)) {
// -25% nutrition
nutrition *= 0.75;
}
return (int)nutrition;
}
char *getobdesc(object_t *o, char *buf) {
if (isknown(o)) {
if (o->type->id == OT_CORPSE) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
sprintf(buf, "The dead body of %s %s.", isvowel(corpserace->name[0]) ? "an" : "a",
corpserace->name);
} else {
sprintf(buf, "%s", o->type->desc);
}
} else if (o->type->id == OT_HEAD) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
sprintf(buf, "The decapitated head of %s %s.", isvowel(corpserace->name[0]) ? "an" : "a",
corpserace->name);
} else {
sprintf(buf, "%s", o->type->desc);
}
} else if (o->type->id == OT_STATUE) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
sprintf(buf, "A stone statue of %s %s.", isvowel(corpserace->name[0]) ? "an" : "a",
corpserace->name);
} else {
sprintf(buf, "%s", o->type->desc);
}
} else if (o->type->id == OC_SCROLL) {
flag_t *f;
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
objecttype_t *spelltype;
spelltype = findot(f->val[0]);
if (spelltype) {
sprintf(buf, "%s", spelltype->desc);
} else {
sprintf(buf, "%s", o->type->desc);
}
} else {
sprintf(buf, "%s", o->type->desc);
}
} else {
sprintf(buf, "%s", o->type->desc);
}
} else {
sprintf(buf, "%s", o->type->obclass->desc);
}
return buf;
}
char *getobextrainfo(object_t *o, char *buf) {
object_t *ammo = NULL;
flag_t *f;
strcpy(buf, "");
if (o->pile->owner) {
ammo = getammo(o->pile->owner);
}
// charges
f = hasflag(o->flags, F_CHARGES);
if (f && f->known) {
if (!hasflag(o->flags, F_DONTSHOWCHARGES)) {
char chargestr[BUFLEN];
if (f->val[0] > 0) {
sprintf(chargestr, " (%d charges left)",f->val[0]);
} else {
sprintf(chargestr, " (empty)");
}
strcat(buf, chargestr);
}
}
// activated
f = hasflag(o->flags, F_ACTIVATED);
if (f) {
strcat(buf, " [activated]");
}
f = hasflag(o->flags,F_EQUIPPED);
if (f) {
if (f->val[0] == BP_WEAPON) {
if (hasflag(o->flags, F_TWOHANDED)) {
strcat(buf, " (two-handed weapon)");
} else {
strcat(buf, " (weapon)");
}
} else {
strcat(buf, " (");
strcat(buf, getbodypartequipname(f->val[0]));
strcat(buf, " ");
strcat(buf, getbodypartname(f->val[0]));
strcat(buf, ")");
}
}
// ammo?
if (ammo && (ammo == o)) {
strcat(buf, " (current ammo)");
}
return buf;
}
cell_t *getoblocation(object_t *o) {
if (o->pile->owner) return o->pile->owner->cell;
else return o->pile->where;
}
char *getobname(object_t *o, char *buf, int count) {
return real_getobname(o, buf, count, B_TRUE, B_TRUE, B_TRUE, B_TRUE);
}
// buf must already be allocated
char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wantcondition, int adjustforblind, int wantblesscurse) {
char *pluralname;
char prefix[BUFLEN];
char basename[BUFLEN];
char buf2[BUFLEN];
int shopitem = B_FALSE;
flag_t *f;
brand_t *br;
obmod_t *om;
int hasunknownmod = B_FALSE;
// default to normal name
if (hasflag(o->flags, F_SHOPITEM)) {
shopitem = B_TRUE;
}
if (shopitem) {
strcpy(basename,o->type->name);
} else {
strcpy(basename,gethiddenname(o));
}
if (!shopitem) {
if (gamestarted && adjustforblind && isblind(player) ) {
// override with onclass names
if (o->type->obclass->id == OC_SCROLL) {
strcpy(basename, "scroll");
} else if (o->type->obclass->id == OC_POTION) {
strcpy(basename, "potion");
} else if (o->type->obclass->id == OC_RING) {
strcpy(basename, "ring");
}
}
}
if ((o->type->id == OT_POT_WATER) && (o->blessed == B_BLESSED) && isblessknown(o) && isknown(o)) {
strcpy(basename, "potion of holy water");
}
if ((o->type->id == OT_POT_COMPETENCE) && (o->blessed == B_CURSED) && isblessknown(o) && isknown(o)) {
strcpy(basename, "potion of incompetence");
}
// override corpse name
if (o->type->id == OT_CORPSE) {
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
sprintf(basename, "%s corpse",corpserace->name);
}
} else if (o->type->id == OT_HEAD) {
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
sprintf(basename, "%s head",corpserace->name);
}
} else if (o->type->id == OT_STATUE) {
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
sprintf(basename, "statue of a %s",corpserace->name);
}
}
// handle ALL
if (count == ALL) {
count = o->amt;
}
// figure out prefix
if ((count == 1) && !hasflag(o->flags, F_NO_A)) {
pluralname = strdup(basename);
if (hasflag(o->flags, F_UNIQUE)) { // TODO: && o->identified
strcpy(prefix, "The");
} else {
if (!hasflag(o->flags, F_NOBLESS) && isblessknown(o)) {
if (o->blessed == B_UNCURSED) {
strcpy(prefix, "an");
} else {
strcpy(prefix, "a");
}
} else {
if (isvowel(basename[0])) {
strcpy(prefix, "an");
} else {
strcpy(prefix, "a");
}
}
}
} else {
// multiple objects?
if (hasflag(o->flags, F_NO_PLURAL)) {
pluralname = strdup(basename);
} else {
pluralname = makeplural(basename);
}
sprintf(prefix, "%d",count);
}
sprintf(buf, "%s ", prefix);
// blessed status
if (!hasflag(o->flags, F_NOBLESS) && wantblesscurse) {
if (shopitem || isblessknown(o)) {
switch (o->blessed) {
case B_BLESSED:
// blessed water is known as "holy water"
if ((o->type->id == OT_POT_WATER) && isknown(o)) {
} else {
strcat(buf, "blessed ");
}
break;
case B_UNCURSED:
strcat(buf, "uncursed ");
break;
case B_CURSED:
if ((o->type->id == OT_POT_COMPETENCE) && isknown(o)) {
} else {
strcat(buf, "cursed ");
}
break;
}
}
}
// material changed?
if (o->material != o->type->material) {
switch (o->material->id) {
case MT_ICE:
strcat(buf, "frozen ");
break;
case MT_GOLD:
strcat(buf, "golden ");
break;
case MT_WOOD:
strcat(buf, "wooden ");
break;
default:
strcat(buf, o->material->name);
strcat(buf, " ");
}
}
// include mods (ie. a flaming sword)
if (wantpremods) {
for (om = firstobmod ; om; om = om->next) {
flag_t *omf;
int found = B_FALSE;
for (omf = om->flags->first ; omf ; omf = omf->next){
if (hasflag(o->flags, omf->id)) {
found = B_TRUE;
}
}
if (found) {
strcat(buf, om->prefix);
}
}
}
// condition
if (wantcondition) {
if (!hasflag(o->flags, F_NOOBDAMTEXT)) {
getobconditionname(o, buf2);
if (strlen(buf2) > 0) {
strcat(buf, buf2);
strcat(buf, " ");
}
}
}
// include enchantments (ie. a blessed +5 sword)
f = hasflag(o->flags, F_BONUS);
if (f && (f->known || shopitem)) {
char buf2[BUFLENSMALL];
int bonus;
bonus = f->val[0];
if (bonus != 0) {
sprintf(buf2, "%s%d ", (bonus < 0) ? "" : "+", bonus);
strcat(buf, buf2);
}
}
// object name
strcat(buf, pluralname);
free(pluralname);
// include mods if identified - ie. xxx of pyromania
for (br = firstbrand; br; br = br->next) {
if (hasflagval(o->flags, F_HASBRAND, br->id, NA, NA, NULL)) {
flag_t *brf;
int ok = B_TRUE;
// are all of the brand flags known?
for (brf = br->flags->first ; brf ; brf = brf->next) {
for (f = o->flags->first; f ; f = f->next) {
if ((f->id == brf->id) && (f->lifetime == FROMBRAND)) {
if (f->known || shopitem) {
} else {
ok = B_FALSE;
hasunknownmod = B_TRUE;
break;
}
}
}
}
if (ok) {
strcat(buf, br->suffix);
}
}
}
// make sure obname doesn't start with a space
while (buf && (buf[0] == ' ')) {
strcpy(buf, buf + 1);
}
// show portal/stair destination
f = hasflag(o->flags, F_MAPLINK);
if (f && f->known) {
map_t *newmap;
newmap = findmap(f->val[0]);
if (newmap) {
char buf2[BUFLEN];
sprintf(buf2, " to level %d", newmap->depth);
strcat(buf, buf2);
}
}
// append inscription
if (o->inscription) {
strcat(buf, " {");
strcat(buf, o->inscription);
strcat(buf, "}");
}
// TODO: detect magic - append [magic]
if (gamestarted && lfhasflag(player, F_DETECTMAGIC)) {
if (hasunknownmod) {
strcat(buf, " [magic]");
}
}
return buf;
}
float getobpileweight(obpile_t *op) {
object_t *o;
float weight = 0;
for (o = op->first ; o ; o = o->next) {
weight += getobweight(o);
}
return weight;
}
char *getobconditionname(object_t *o, char *buf) {
float pct;
enum IQBRACKET iqb;
if (player) {
iqb = getiqname(getattr(player, A_IQ), NULL);
} else {
iqb = IQ_AVERAGE; // this should be sufficient to show everything
}
if (iscorpse(o)) {
// you only know it's rotting if you have average or higher iq
if (isrotting(o) && (iqb >= IQ_AVERAGE)) {
sprintf(buf, "rotting");
} else {
strcpy(buf, "");
}
} else {
if (iqb >= IQ_DIMWITTED) {
pct = getobhppct(o);
if (pct >= 100) {
strcpy(buf, "");
} else if (pct >= 75) {
sprintf(buf, "battered");
} else if (pct >= 50) {
sprintf(buf, "damaged");
} else if (pct >= 25) {
sprintf(buf, "very damaged");
} else {
sprintf(buf, "critically damaged");
}
} else {
strcpy(buf, "");
}
}
return buf;
}
char *getobhurtname(object_t *o, enum DAMTYPE damtype) {
switch (damtype) {
case DT_ACID:
if (o->amt == 1) {
return "corrodes";
} else {
return "corrode";
}
case DT_DECAY:
if (o->amt == 1) {
return "decays";
} else {
return "decay";
}
case DT_FIRE:
if (o->amt == 1) {
return "burns";
} else {
return "burn";
}
case DT_MELT:
if (o->amt == 1) {
return "is melting";
} else {
return "are melting";
}
default:
if (o->amt == 1) {
return "is damaged";
} else {
return "are damaged";
}
}
return "is damaged";
}
float getobweight(object_t *o) {
float weight;
weight = getobunitweight(o) * o->amt;
return weight;
}
float getobunitweight(object_t *o) {
float weight;
weight = o->weight;
// has its material been changed?
if (o->material != o->type->material) {
// changed - some materials will
// modify the item's weight
float ratio;
ratio = o->material->weightrating / o->type->material->weightrating;
weight *= ratio;
}
return weight;
}
objecttype_t *getoppositestairs(objecttype_t *ot) {
flag_t *f;
f = hasflag(ot->flags, F_OPPOSITESTAIRS);
assert(f);
return findot(f->val[0]);
}
char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth) {
objecttype_t *ot;
objecttype_t *poss[MAXRANDOMOBCANDIDATES];
int nposs = 0;
int selidx;
int amt;
flag_t *f;
int db = B_FALSE;
char *pluralname;
char brandname[BUFLEN];
char cursestr[BUFLEN];
int raritymin,raritymax;
int depth;
if (forcedepth != NA) {
depth = forcedepth;
} else if (map) {
depth = map->depth;
} else {
depth = rnd(1,MAXDEPTH);
}
getrarity(depth, &raritymin, &raritymax, 25);
if (db) dblog("adding random object with rarity value between %d - %d",raritymin,raritymax);
if (db) {
objectclass_t *oc;
switch (cond) {
case RO_DAMTYPE:
dblog(" (must have damtype = %s)",getdamname(cval));
break;
case RO_OBCLASS:
oc = findoc(cval);
dblog(" (must have obclass = %s)",oc->name);
break;
}
}
// try to find an object of this type which will
// fit in the map's habitat
nposs = 0;
while (nposs == 0) {
for (ot = objecttype ; ot ; ot = ot->next) {
int rarok = B_FALSE, condok = B_FALSE;
flag_t *rarflag = NULL;
// correct rarity?
rarflag = hasflagval(ot->flags, F_RARITY, H_ALL, NA, NA, NULL);
if (!rarflag) {
if (map) {
rarflag = hasflagval(ot->flags, F_RARITY, map->habitat, NA, NA, NULL);
} else {
rarflag = hasflagval(ot->flags, F_RARITY, NA, NA, NA, NULL);
}
}
if (rarflag) {
if ((rarflag->val[1] >= raritymin) && (rarflag->val[1] <= raritymax)) {
rarok = B_TRUE;
}
}
if (rarok) {
// matches condition?
if (cond == RO_NONE) {
condok = B_TRUE;
} else if (cond == RO_DAMTYPE) {
if (hasflagval(ot->flags, F_DAMTYPE, cval, NA, NA, NULL)) {
condok = B_TRUE;
}
} else if (cond == RO_OBCLASS) {
if (ot->obclass->id == cval) {
condok = B_TRUE;
}
}
}
if (rarok && condok) {
//if (db) dblog("-> possibility: %s, rarity=%d",ot->name, rarflag->val[1]);
poss[nposs] = ot;
nposs++;
if (nposs >= MAXRANDOMOBCANDIDATES) break;
}
}
// nothing found?
if (nposs == 0) {
// already at lowest rarity?
if ((raritymax >= 100) && (raritymin <= 0)) {
// give up
strcpy(buf, "");
if (db) dblog("no possible objects 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 objects like this. trying again with rarity %d-%d\n",raritymin,raritymax);
}
}
if (db) dblog("got %d possibilities.",nposs);
// pick a random object from our possiblities
selidx = rnd(0,nposs-1);
ot = poss[selidx];
// handle objects which appear in multiples (ie. rocks)
f = hasflag(ot->flags, F_NUMAPPEAR);
if (f) {
amt = rnd(f->val[0], f->val[1]);
} else {
amt = 1;
}
if (amt > 1) {
pluralname = makeplural(ot->name);
} else {
pluralname = strdup(ot->name);
}
// blessed?
strcpy(cursestr, "");
if (!hasflag(ot->flags, F_NOBLESS)) {
int num;
int bonus = 0;
num = rnd(1,100);
if (num <= 15) {
strcpy(cursestr, "blessed ");
// chance of bonus
while (rnd(1,100) <= 25) {
bonus++;
}
} else if (num <= 30) {
strcpy(cursestr, "cursed ");
// chance of penalty
while (rnd(1,100) <= 25) {
bonus--;
}
}
}
if ((ot->obclass->id == OC_WEAPON) ||
(ot->obclass->id == OC_ARMOUR)) {
char buf2[BUFLENSMALL];
int bonus = 0;
if (strlen(cursestr) > 0) {
// chance of bonus/penalty
while (rnd(1,100) <= 25) {
bonus++;
}
if (strstr(cursestr, "cursed")){
bonus = -bonus;
}
}
sprintf(buf2, "%s%d ", (bonus >= 0) ? "+" : "", bonus);
strcat(cursestr, buf2);
}
// weapons/armour have random chance of various obmods
if ((ot->obclass->id == OC_WEAPON) ||
(ot->obclass->id == OC_ARMOUR)) {
obmod_t *om;
// 1 in 6 of masterwork
if (rnd(1,6) == 1) {
om = findobmod(OM_MASTERWORK);
strcat(cursestr, om->prefix);
} else if (rnd(1,3) == 1) { // 1 in 3 of shoddy
om = findobmod(OM_SHODDY);
strcat(cursestr, om->prefix);
}
}
// get random chance of having a brand (1% per depth)...
// if so...
strcpy(brandname, "");
if (rnd(1,100) <= depth) {
brand_t *om, **poss;
int numom;
int nposs = 0;
// count number of brands
numom = 0;
for (om = firstbrand ; om ; om = om->next) {
numom++;
}
poss = malloc(numom * sizeof(brand_t *));
for (om = firstbrand ; om ; om = om->next) {
if (brandappliesto(om, ot)) {
poss[nposs] = om;
nposs++;
}
}
if (nposs > 0) {
om = poss[rnd(0,nposs-1)];
strcpy(brandname, om->suffix);
}
free(poss);
}
sprintf(buf, "%d %s%s%s", amt, cursestr, pluralname,brandname);
if (db) dblog("random ob: %d x %s ('%s')", amt, ot->name,pluralname);
free(pluralname);
return buf;
}
/*
OLD CODE
char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth) {
enum RARITY rarity;
objecttype_t *ot;
int roll;
objecttype_t *poss[MAXRANDOMOBCANDIDATES];
int nposs = 0;
int selidx;
int amt;
flag_t *f;
int db = B_TRUE;
char *pluralname;
char cursestr[BUFLEN];
int depth;
int getrarer;
int firstgo;
int moddir = 0;
// determine rarity of object to generate
if (rnd(1,2) == 1) {
rarity = RR_FREQUENT;
} else {
rarity = RR_COMMON;
}
if (forcedepth != NA) {
depth = forcedepth;
} else if (map) {
depth = map->depth;
} else {
depth = rnd(1,20);
}
getrarer = 40 + (depth * 2);
if (getrarer > 75) getrarer = 75;
// start with 'frequent'ly appearing items
// roll a die. if less than 'getrarer', get rarer.
// stop when we fail the die roll.
roll = rnd(1,100);
while (roll < getrarer) {
rarity++;
if (rarity == RR_VERYRARE) break;
roll = rnd(1,100);
}
if (db) dblog("adding random object of rarity %d",rarity);
if (db) {
objectclass_t *oc;
switch (cond) {
case RO_DAMTYPE:
dblog(" (must have damtype = %s)",getdamname(cval));
break;
case RO_OBCLASS:
oc = findoc(cval);
dblog(" (must have obclass = %s)",oc->name);
break;
}
}
// try to find an object of this type which will
// fit in the map's habitat
moddir = -1;
firstgo = B_TRUE;
nposs = 0;
while (nposs == 0) {
for (ot = objecttype ; ot ; ot = ot->next) {
int rarok = B_FALSE, condok = B_FALSE;
// correct rarity?
if ( map && hasflagval(ot->flags, F_RARITY, map->habitat, rarity, NA, NULL)) {
rarok = B_TRUE;
} else if (hasflagval(ot->flags, F_RARITY, NA, rarity, NA, NULL)) {
rarok = B_TRUE;
} else {
rarok = B_FALSE;
}
if (rarok) {
// matches condition?
if (cond == RO_NONE) {
condok = B_TRUE;
} else if (cond == RO_DAMTYPE) {
if (hasflagval(ot->flags, F_DAMTYPE, cval, NA, NA, NULL)) {
condok = B_TRUE;
}
} else if (cond == RO_OBCLASS) {
if (ot->obclass->id == cval) {
condok = B_TRUE;
}
}
}
if (rarok && condok) {
poss[nposs] = ot;
nposs++;
if (nposs >= MAXRANDOMOBCANDIDATES) break;
}
}
// nothing found?
if (nposs == 0) {
// already at lowest rarity?
if (rarity == RR_FREQUENT) {
if (firstgo) {
moddir = 1;
} else {
// give up
strcpy(buf, "");
if (db) dblog("no possible objects at all! giving up.");
return NULL;
}
}
firstgo = B_FALSE;
// lower/raise rarity and try again
rarity += moddir;
if (rarity >= RR_NEVER) {
// give up
strcpy(buf, "");
if (db) dblog("no possible objects at all! giving up.");
return NULL;
}
if (db) dblog("no possible objects like this. trying again with rarity %d\n",rarity);
}
}
if (db) dblog("got %d possibilities.",nposs);
// pick a random object from our possiblities
selidx = rnd(0,nposs-1);
ot = poss[selidx];
// handle objects which appear in multiples (ie. rocks)
f = hasflag(ot->flags, F_NUMAPPEAR);
if (f) {
amt = rnd(f->val[0], f->val[1]);
} else {
amt = 1;
}
if (amt > 1) {
pluralname = makeplural(ot->name);
} else {
pluralname = strdup(ot->name);
}
// blessed?
strcpy(cursestr, "");
if (!hasflag(ot->flags, F_NOBLESS)) {
int num;
int bonus = 0;
num = rnd(1,100);
if (num <= 15) {
strcpy(cursestr, "blessed ");
// chance of bonus
while (rnd(1,100) <= 25) {
bonus++;
}
} else if (num <= 30) {
strcpy(cursestr, "cursed ");
// chance of penalty
while (rnd(1,100) <= 25) {
bonus--;
}
}
}
if ((ot->obclass->id == OC_WEAPON) ||
(ot->obclass->id == OC_ARMOUR)) {
char buf2[BUFLENSMALL];
int bonus = 0;
if (strlen(cursestr) > 0) {
// chance of bonus/penalty
while (rnd(1,100) <= 25) {
bonus++;
}
if (strstr(cursestr, "cursed")){
bonus = -bonus;
}
}
sprintf(buf2, "%s%d ", (bonus >= 0) ? "+" : "", bonus);
strcat(cursestr, buf2);
}
sprintf(buf, "%d %s%s", amt, cursestr, pluralname);
if (db) dblog("random ob: %d x %s ('%s')", amt, ot->name,pluralname);
free(pluralname);
return buf;
}
*/
char *getrandomob(map_t *map, char *buf) {
return real_getrandomob(map, buf, RO_NONE, NA, NA);
}
char *getrandomobwithdt(map_t *map, enum DAMTYPE damtype, char *buf) {
return real_getrandomob(map, buf, RO_DAMTYPE, damtype, NA);
}
char *getrandomobwithclass(map_t *map, enum OBCLASS cid, char *buf) {
return real_getrandomob(map, buf, RO_OBCLASS, cid, NA);
}
enum SPELLSCHOOL getschool(enum OBTYPE sid) {
objecttype_t *ot;
ot = findot(sid);
if (ot) {
flag_t *f;
f = hasflag(ot->flags, F_SPELLSCHOOL);
if (f) {
return f->val[0];
}
}
return SS_NONE;
}
char *getschoolname(enum SPELLSCHOOL sch) {
switch (sch) {
case SS_ABILITY: return "Abilities";
case SS_ALLOMANCY: return "Allomancy";
case SS_DIVINE: return "Divine Powers";
case SS_WILD: return "Wild Magic";
case SS_MENTAL: return "Mental Powers";
case SS_AIR: return "Elemental/Air Magic";
case SS_FIRE: return "Elemental/Fire Magic";
case SS_ICE: return "Elemental/Ice Magic";
case SS_MODIFICATION: return "Transmutation Magic";
case SS_DEATH: return "Death Magic";
case SS_LIFE: return "Life Magic";
case SS_DIVINATION: return "Divination Magic";
case SS_TRANSLOCATION: return "Translocation Magic";
case SS_SUMMONING: return "Summoning Magic";
case SS_GRAVITY: return "Gravitation Magic";
case SS_LAST: return "!invalid school!";
default:
break;
}
return "unknown school";
}
int getshatterdam(object_t *o) {
int shatterdam = 0;
if (willshatter(o->material->id)) {
int maxshatterdam;
maxshatterdam = ceil(getobweight(o));
if (maxshatterdam < 1) maxshatterdam = 1;
shatterdam = rnd(1, maxshatterdam);
}
return shatterdam;
}
int getthrowdam(object_t *o) {
double dam = 0;
flag_t *f;
// modify if it has a damage value
f = hasflag(o->flags, F_MISSILEDAM);
if (f) {
dam = f->val[0];
} else {
// base damage is = kilograms.
// ie. 1 kg object does 1 damage
// ie. 5 kg object does 5 damage
// ie. 100 kg object does 100 damage (person)
// ie. 1 tonne object does 1000 damage (car)
dam = ceil((double)getobunitweight(o));
// missile objects do extra damage
if (hasflag(o->flags, F_MISSILE)) {
dam *= 2;
}
// max
if (dam > 20) dam = 20;
}
// modify for bonus
f = hasflag(o->flags, F_BONUS);
if (f) {
dam += f->val[0];
}
// note: damage will also be modified based on speed in fireat()
// adjust for material
switch (o->material->id) {
// soft materials do less damage
case MT_PAPER:
case MT_WETPAPER:
case MT_CLOTH:
dam /= 2;
break;
break;
default:
break;
}
return (int)dam;
}
int hasedibleob(obpile_t *op) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (isedible(o)) {
return B_TRUE;
}
}
return B_FALSE;
}
object_t *hasknownob(obpile_t *op, enum OBTYPE oid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->type->id == oid) {
if (isknown(o)) {
return o;
}
}
}
return NULL;
}
object_t *hasob(obpile_t *op, enum OBTYPE oid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->type->id == oid) return o;
}
return NULL;
}
object_t *hasobofclass(obpile_t *op, enum OBCLASS cid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->type->obclass->id == cid) return o;
}
return NULL;
}
object_t *hasobmulti(obpile_t *op, enum OBTYPE *oid, int noids) {
object_t *o;
int n;
for (o = op->first ; o ; o = o->next) {
for (n = 0; n < noids; n++) {
if (o->type->id == oid[n]) return o;
}
}
return NULL;
}
object_t *hasobwithflag(obpile_t *op, enum FLAG flagid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (hasflag(o->flags, flagid)) return o;
}
return NULL;
}
object_t *hasobwithflagval(obpile_t *op, enum FLAG flagid, int val0, int val1, int val2, char *text) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (hasflagval(o->flags, flagid, val0, val1, val2, text)) return o;
}
return NULL;
}
object_t *hasobid(obpile_t *op, int id) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->id == id) return o;
}
return NULL;
}
// fully identify a single object.
void identify(object_t *o) {
flag_t *f;
if (!isknown(o)) {
makeknown(o->type->id);
}
addflag(o->flags, F_IDENTIFIED, B_TRUE, -1, -1, NULL);
o->blessknown = B_TRUE;
for (f = o->flags->first ; f ; f = f->next) {
if (!f->known) f->known = B_TRUE;
}
}
void ignite(object_t *o) {
flag_t *f;
if (!hasflag(o->flags, F_DAMAGABLE)) {
addflag(o->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
}
f = hasflag(o->flags, F_ONFIRE);
if (!f) {
// on fire for 2 turns
addtempflag(o->flags, F_ONFIRE, B_TRUE, NA, NA, NULL, 2);
}
}
void initobjects(void) {
//int ch;
//int i;
objecttype_t *ot;
// generate hidden names
addhiddenname(OC_BOOK, "ancient book");
addhiddenname(OC_BOOK, "black book");
addhiddenname(OC_BOOK, "blue book");
addhiddenname(OC_BOOK, "clean book");
addhiddenname(OC_BOOK, "creepy book");
addhiddenname(OC_BOOK, "damp book");
addhiddenname(OC_BOOK, "dog-eared book");
addhiddenname(OC_BOOK, "dusty book");
addhiddenname(OC_BOOK, "eerie book");
addhiddenname(OC_BOOK, "fancy book");
addhiddenname(OC_BOOK, "frosty book");
addhiddenname(OC_BOOK, "furry book");
addhiddenname(OC_BOOK, "glowing book");
addhiddenname(OC_BOOK, "hot book");
addhiddenname(OC_BOOK, "humming book");
addhiddenname(OC_BOOK, "icy book");
addhiddenname(OC_BOOK, "jiggling book");
addhiddenname(OC_BOOK, "leatherbound book");
addhiddenname(OC_BOOK, "luminous book");
addhiddenname(OC_BOOK, "mouldy book");
addhiddenname(OC_BOOK, "muddy book");
addhiddenname(OC_BOOK, "night-black book");
addhiddenname(OC_BOOK, "orange book");
addhiddenname(OC_BOOK, "papyrus book");
addhiddenname(OC_BOOK, "pink book");
addhiddenname(OC_BOOK, "plain book");
addhiddenname(OC_BOOK, "quivering book");
addhiddenname(OC_BOOK, "rainbow-coloured book");
addhiddenname(OC_BOOK, "red book");
addhiddenname(OC_BOOK, "slimy book");
addhiddenname(OC_BOOK, "small book");
addhiddenname(OC_BOOK, "soggy book");
addhiddenname(OC_BOOK, "thin book");
addhiddenname(OC_BOOK, "thick book");
addhiddenname(OC_BOOK, "tiny book");
addhiddenname(OC_BOOK, "vibrating book");
addhiddenname(OC_BOOK, "vine-covered book");
addhiddenname(OC_BOOK, "warm book");
addhiddenname(OC_BOOK, "worn book");
addhiddenname(OC_BOOK, "wrinkled book");
addhiddenname(OC_SCROLL, "scroll titled ABRA CA DABRA");
addhiddenname(OC_SCROLL, "scroll titled BARBAR");
addhiddenname(OC_SCROLL, "scroll titled CRATOL JEM");
addhiddenname(OC_SCROLL, "scroll titled DELENTH YIN");
addhiddenname(OC_SCROLL, "scroll titled ETEE NOM");
addhiddenname(OC_SCROLL, "scroll titled FIE JOOHM");
addhiddenname(OC_SCROLL, "scroll titled GREE VII");
addhiddenname(OC_SCROLL, "scroll titled HTEB IH");
addhiddenname(OC_SCROLL, "scroll titled HOCUS POCUS");
addhiddenname(OC_SCROLL, "scroll titled ILU-MA ZEE");
addhiddenname(OC_SCROLL, "scroll titled JUNIPO CHECK");
addhiddenname(OC_SCROLL, "scroll titled KARELS");
addhiddenname(OC_SCROLL, "scroll titled LUMLEE GWON");
addhiddenname(OC_SCROLL, "scroll titled MARIGON");
addhiddenname(OC_SCROLL, "scroll titled MAXIMOR BLATHUS");
addhiddenname(OC_SCROLL, "scroll titled NORTH");
addhiddenname(OC_SCROLL, "scroll titled REZZ KINETO");
addhiddenname(OC_SCROLL, "scroll titled SHAZZARIO");
addhiddenname(OC_SCROLL, "scroll titled THERIUM LARGOS");
addhiddenname(OC_SCROLL, "scroll titled VOLTR YI MEN");
addhiddenname(OC_SCROLL, "scroll titled ZAREL NOR");
addhiddenname(OC_POTION, "aqua potion");
addhiddenname(OC_POTION, "azure potion");
addhiddenname(OC_POTION, "blue potion");
addhiddenname(OC_POTION, "brown potion");
addhiddenname(OC_POTION, "clear potion");
addhiddenname(OC_POTION, "cyan potion");
addhiddenname(OC_POTION, "dark potion");
addhiddenname(OC_POTION, "effervescent potion");
addhiddenname(OC_POTION, "fizzy potion");
addhiddenname(OC_POTION, "fuming potion");
addhiddenname(OC_POTION, "green potion");
addhiddenname(OC_POTION, "indigo potion");
addhiddenname(OC_POTION, "luminous potion"); // should produce light
addhiddenname(OC_POTION, "magenta potion");
addhiddenname(OC_POTION, "orange potion");
addhiddenname(OC_POTION, "red potion");
addhiddenname(OC_POTION, "violet potion");
addhiddenname(OC_POTION, "yellow potion");
// other descriptions
addhiddenname(OC_POTION, "bubbling potion");
addhiddenname(OC_POTION, "fizzy potion");
addhiddenname(OC_POTION, "gluggy potion");
addhiddenname(OC_POTION, "steaming potion");
addhiddenname(OC_WAND, "amethyst wand");
addhiddenname(OC_WAND, "brass wand");
addhiddenname(OC_WAND, "diamond wand");
addhiddenname(OC_WAND, "emerald wand");
addhiddenname(OC_WAND, "flourescent wand");
addhiddenname(OC_WAND, "gold wand");
addhiddenname(OC_WAND, "iridium wand");
addhiddenname(OC_WAND, "luminous wand");
addhiddenname(OC_WAND, "ruby wand");
addhiddenname(OC_WAND, "zinc wand");
addhiddenname(OC_WAND, "sapphire wand");
addhiddenname(OC_WAND, "wooden wand");
// gems
addhiddenname(OC_RING, "ruby ring");
addhiddenname(OC_RING, "diamond ring");
addhiddenname(OC_RING, "emerald ring");
addhiddenname(OC_RING, "silver ring");
addhiddenname(OC_RING, "gold ring");
// object modifiers - flags can be either known or not, depending on if it's obvious
addobmod(OM_FLAMING,"flaming");
addflag_real(lastobmod->flags, F_ONFIRE, B_TRUE, NA, NA, NULL, PERMENANT, B_KNOWN, -1);
addobmod(OM_HEADLESS,"headless");
addflag_real(lastobmod->flags, F_HEADLESS, B_TRUE, NA, NA, NULL, PERMENANT, B_KNOWN, -1);
addobmod(OM_MASTERWORK,"masterwork");
addflag_real(lastobmod->flags, F_MASTERWORK, B_TRUE, NA, NA, NULL, PERMENANT, B_KNOWN, -1);
addobmod(OM_SHODDY,"shoddy");
addflag_real(lastobmod->flags, F_SHODDY, B_TRUE, NA, NA, NULL, PERMENANT, B_KNOWN, -1);
// brands modifiers - flags should be UNKNOWN!
addbrand(BR_BALANCE, "of balance", BP_WEAPON);
addflag_real(lastbrand->flags, F_BALANCE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_PYROMANIA, "of pyromania", BP_WEAPON);
addflag_real(lastbrand->flags, F_FLAMESTRIKE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_REVENGE, "of revenge", BP_WEAPON);
addflag_real(lastbrand->flags, F_REVENGE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_SHARPNESS, "of sharpness", BP_WEAPON);
addflag_real(lastbrand->flags, F_ARMOURPIERCE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_LEVITATION, "of levitation", BP_FEET);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_LEVITATING, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_SWIFTNESS, "of swiftness", BP_FEET);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_FASTMOVE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_SLOTH, "of sloth", BP_FEET);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_SLOWMOVE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_STRENGTH, "of strength", BP_HANDS);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, 3, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_WEAKNESS, "of weakness", BP_HANDS);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, -3, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_DEXTERITY, "of dexterity", BP_HANDS);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_DEX, 3, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_IMPACT, "of impact", BP_WEAPON); // TODO: make thisonly go ont obashing weapons
addflag_real(lastbrand->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_INTELLIGENCE, "of intelligence", BP_HEAD);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRMOD, A_IQ, 3, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_KNOWLEDGE, "of knowledge", BP_HEAD);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_DETECTAURAS, B_TRUE, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_DETECTMAGIC, B_TRUE, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_TELEPATHY, "of telepathy", BP_HEAD);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_DETECTLIFE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_TELEKINESIS, "of telekinesis", BP_HEAD);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_CANWILL, OT_S_TELEKINESIS, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_GIANTSTRENGTH, "of giant strength", BP_WAIST);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRSET, A_STR, 18, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_FEEBLENESS, "of feebleness", BP_WAIST);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_ATTRSET, A_STR, 3, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_FLIGHT, "of flight", BP_WAIST);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_FLYING, B_TRUE, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_SPEED, "of speed", BP_WAIST);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_FASTACT, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1);
addbrand(BR_MAGRESIST, "of magic resistance", BP_SHOULDERS);
addflag_real(lastbrand->flags, F_EQUIPCONFER, F_RESISTMAG, B_TRUE, NA, NULL, PERMENANT, B_UNKNOWN, -1);
// materials
addmaterial(MT_NOTHING, "nothing", 0);
addmaterial(MT_MAGIC, "magical energy", 0);
addmaterial(MT_FIRE, "fire", 0);
addmaterial(MT_GAS, "gas", 0.5);
addmaterial(MT_PAPER, "paper", 1);
addflag(lastmaterial->flags, F_MATCONVERT, MT_WATER, NA, NA, "lump of soggy paper");
addflag(lastmaterial->flags, F_MATCONVERTTEXT, MT_WATER, NA, NA, "goes soggy");
addflag(lastmaterial->flags, F_MATCONVERTTEXTPL, MT_WATER, NA, NA, "go soggy");
addflag(lastmaterial->flags, F_FLAMMABLE, B_TRUE, NA, NA, NULL);
addmaterial(MT_FLESH, "flesh", 2);
addmaterial(MT_WETPAPER, "wet paper", 3);
addmaterial(MT_CLOTH, "cloth", 3);
addflag(lastmaterial->flags, F_FLAMMABLE, B_TRUE, NA, NA, NULL);
addmaterial(MT_FOOD, "food", 3);
addmaterial(MT_PLASTIC, "plastic", 3);
addmaterial(MT_RUBBER, "rubber", 4);
addmaterial(MT_LEATHER, "leather", 4);
addmaterial(MT_BONE, "bone", 5);
addmaterial(MT_ICE, "ice",6);
addmaterial(MT_WOOD, "wood", 6);
addflag(lastmaterial->flags, F_FLAMMABLE, B_TRUE, NA, NA, NULL);
addmaterial(MT_WATER, "water", 7);
addmaterial(MT_BLOOD, "blood", 8);
addmaterial(MT_SLIME, "slime", 9);
addmaterial(MT_STONE, "stone", 10);
addmaterial(MT_METAL, "metal", 13);
addmaterial(MT_GLASS, "glass", 13);
addmaterial(MT_GOLD, "gold", 16);
//addmaterial(MT_GOLD, "gold", 16);
// object classes
addoc(OC_DFEATURE, "Dungeon Features", "Doors, etc.", '\\');
addoc(OC_MONEY, "Money", "The standard currency of Nexus.", '$');
addoc(OC_SCROLL, "Scrolls", "An arcane roll of parchment, inscribed with many magical glyphs.", '?');
addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
//addflag(lastobjectclass->flags, F_DTCONVERT, DT_WATER, NA, NA, "lump of soggy paper");
//addflag(lastobjectclass->flags, F_MATCONVERT, MT_WATER, NA, NA, "lump of soggy paper");
//addflag(lastobjectclass->flags, F_MATCONVERTTEXT, MT_WATER, NA, NA, "goes soggy");
//addflag(lastobjectclass->flags, F_MATCONVERTTEXTPL, MT_WATER, NA, NA, "go soggy");
addoc(OC_WAND, "Wands", "A limited-use magical wand which casts the imbued spell.", '/');
addflag(lastobjectclass->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_OPERUSECHARGE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_RNDCHARGES, 2, 12, NA, NULL);
addoc(OC_POTION, "Potions", "A strange concoction contained within a small flask.", '!');
addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_POURABLE, B_TRUE, NA, NA, NULL);
addoc(OC_RING, "Rings", "A circular band, worn on the finger.", '=');
addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_GOESON, BP_RIGHTHAND, NA, NA, NULL);
addflag(lastobjectclass->flags, F_GOESON, BP_LEFTHAND, NA, NA, NULL);
addoc(OC_WEAPON, "Weapons", "An instrument used for the purpose of causing harm or death.", ')');
addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL);
addoc(OC_ARMOUR, "Armour/Clothing", "Protective gear.", ']');
addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL);
addoc(OC_MISSILE, "Missiles/Ammunition", "An instrument used for the purpose of causing harm or death.", ';');
addflag(lastobjectclass->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addoc(OC_ROCK, "Rocks/Gems", "Boring (or not so boring) rocks.", '*');
addoc(OC_FOOD, "Food", "Yum!", '%');
addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addoc(OC_CORPSE, "Corpses", "Dead flesh which was once living.", '%');
addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_OBHP, 50, 50, NA, NULL);
addflag(lastobjectclass->flags, F_OBHPDRAIN, 1, DT_DECAY, NA, NULL); // ie. corpses last for 50 turns
addoc(OC_TECH, "Tools/Technology", "Useful items, from the common to the obscure.", '~');
addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addoc(OC_MISC, "Miscellaneous", "This could be anything.", '\\');
addoc(OC_EFFECT, "Environmental Effects", "Smoke, fire, etc.", '}');
addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addoc(OC_BOOK, "Books", "Spellbooks, tomes or manuals.", '+');
addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL);
addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addoc(OC_SPELL, "Spells", "A magical spell", '&'); // this is a "virtual" object class
addoc(OC_ABILITY, "Abilities", "A special ability", '&'); // this is a "virtual" object class
// object types
// dungeon features
addot(OT_WOODENDOOR, "wooden door", "A sturdy wooden door.", MT_WOOD, 150, OC_DFEATURE);
// GLYPH here is a special case in getglyph
addflag(lastot->flags, F_DOOR, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_IMPASSABLE, SZ_MAX, NA, NA, NULL);
addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_LOCKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL);
addflag(lastot->flags, F_DTVULN, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DTVULN, DT_CHOP, NA, NA, NULL);
addot(OT_WOODENTABLE, "wooden table", "A waist-height wooden table.", MT_WOOD, 25, OC_DFEATURE);
addflag(lastot->flags, F_RARITY, H_ALL, 70, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "\\");
addflag(lastot->flags, F_IMPASSABLE, SZ_HUMAN, NA, NA, NULL);
addflag(lastot->flags, F_CRUSHABLE, SZ_LARGE, NA, NA, NULL);
addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL);
//addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
addflag(lastot->flags, F_DTVULN, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DTVULN, DT_CHOP, NA, NA, NULL);
addot(OT_BOULDER, "boulder", "A massive stone boulder.", MT_STONE, 80, OC_DFEATURE);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, "");
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "'");
addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL);
addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL);
// addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 80, 80, NA, NULL);
addot(OT_STATUE, "statue", "A stone statue of a monster.", MT_STONE, 80, OC_DFEATURE);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, "");
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "'");
addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL);
addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_BLOCKSTHROW, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 80, 80, NA, NULL);
addot(OT_STAIRSDOWN, "staircase going down", "A stone staircase winding downwards.", MT_STONE, 3000, OC_DFEATURE);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ">");
addflag(lastot->flags, F_CLIMBABLE, D_DOWN, NA, NA, NULL);
addflag(lastot->flags, F_OPPOSITESTAIRS, OT_STAIRSUP, NA, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addot(OT_STAIRSUP, "staircase going up", "A stone staircase climbing upwards.", MT_STONE, 3000, OC_DFEATURE);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "<");
addflag(lastot->flags, F_CLIMBABLE, D_UP, NA, NA, NULL);
addflag(lastot->flags, F_OPPOSITESTAIRS, OT_STAIRSDOWN, NA, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addot(OT_VENDINGMACHINE, "vending machine", "A gold-operated vending machine.", MT_METAL, 500, OC_DFEATURE);
addflag(lastot->flags, F_RARITY, H_ALL, 70, NA, "");
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "_");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addot(OT_PORTAL, "magic portal", "A magical portal to a different place...", MT_MAGIC, 0, OC_DFEATURE);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "&");
addflag(lastot->flags, F_CLIMBABLE, D_IN, NA, NA, "&");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
// money etc
addot(OT_GOLD, "gold coin", "Sparkling nuggets of gold, the standard currency of Nexus.", MT_GOLD, 0.1, OC_MONEY);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_ALL, 100, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 100, NA, "");
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addot(OT_STONE, "stone", "A medium-sized roundish stone.", MT_STONE, 0.5, OC_ROCK);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 10, NA, "");
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addot(OT_ASH, "pile of ash", "A pile of ash", MT_STONE, 0.1, OC_ROCK);
addflag(lastot->flags, F_GLYPH, B_TRUE, NA, NA, ",");
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, "");
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addot(OT_GEMOFSEEING, "gem of seeing", "Magically enhances your eyesight.", MT_STONE, 1, OC_ROCK);
addflag(lastot->flags, F_HOLDCONFER, F_XRAYVIS, 2, NA, NULL);
addflag(lastot->flags, F_HOLDCONFER, F_DETECTAURAS, B_TRUE, NA, NULL);
addflag(lastot->flags, F_HOLDCONFER, F_DETECTMAGIC, B_TRUE, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
// food
addot(OT_BERRY, "berry", "Juicy, brightly coloured berries.", MT_FOOD, 0.1, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 8, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_NUMAPPEAR, 1, 15, NA, "");
addot(OT_NUT, "peanut", "A species in the legume family.", MT_FOOD, 0.1, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 12, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_NUMAPPEAR, 1, 12, NA, "");
addot(OT_BANANA, "banana", "Ba-na-na-na-na-na na-na na-na-na.", MT_FOOD, 0.3, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 50, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addot(OT_BANANASKIN, "banana skin", "A slippery banana skin.", MT_FOOD, 0.1, OC_FOOD);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_SLIPPERY, 15, NA, NA, NULL);
addflag(lastot->flags, F_SLIPMOVE, 15, NA, NA, NULL);
addot(OT_APPLE, "apple", "A crunchy apple.", MT_FOOD, 0.5, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 50, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 85, NA, NULL);
addot(OT_MUSHROOM, "mushroom", "A large brown mushroom.", MT_FOOD, 0.2, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 50, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_NUMAPPEAR, 1, 3, NA, "");
addot(OT_BREADSTALE, "loaf of stale bread", "A small loaf of old, stale bread.", MT_FOOD, 0.5, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 80, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addot(OT_CHEESE, "chunk of cheese", "A chunk of hard cheese.", MT_FOOD, 0.5, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 85, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addot(OT_ROASTMEAT, "chunk of roast meat", "A chunk of flame-roasted flesh.", MT_FOOD, 1, OC_FOOD); // weight normally comes from corpse type
addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addot(OT_BREADFRESH, "loaf of fresh bread", "A freshly-baked loaf of bread.", MT_FOOD, 0.5, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 100, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addot(OT_CHOCOLATE, "block of chocolate", "An entire block of chocolate.", MT_FOOD, 0.5, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 110, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 40, NA, NULL);
// corpses
/*
addot(OT_CORPSEEYEBAT, "eyebat corpse", "The dead body of an eyebat.", MT_FLESH, 5, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 10, NA, "");
addot(OT_CORPSEBAT, "bat corpse", "The dead body of a bat.", MT_FLESH, 5, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 10, NA, "");
addot(OT_CORPSEFLY, "fly corpse", "The dead body of a giant flying insect.", MT_FLESH, 5, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 10, NA, "");
addot(OT_CORPSEGLOWBUG, "glowbug corpse", "The dead body of a glowbug.", MT_FLESH, 1, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 10, NA, "");
addot(OT_CORPSEGOBLIN, "goblin corpse", "The dead body of a goblin.", MT_FLESH, 20, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 25, NA, "");
addot(OT_CORPSEHUMAN, "human corpse", "The dead body of a human.", MT_FLESH, 50, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 50, NA, "");
addot(OT_CORPSEORK, "orc corpse", "The dead body of an orc.", MT_FLESH, 90, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 50, NA, "");
addot(OT_CORPSERODENT, "rodent corpse", "The dead body of some kind of rodent.", MT_FLESH, 5, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 20, NA, "");
addot(OT_CORPSETROLL, "troll corpse", "The dead body of a troll.", MT_FLESH, 90, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 50, NA, "");
addot(OT_CORPSEOGRE, "ogre corpse", "The dead body of an ogre.", MT_FLESH, 100, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 90, NA, "");
addot(OT_CORPSELIZARD, "lizard corpse", "The dead body of a lizard.", MT_FLESH, 1, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 20, NA, "");
*/
addot(OT_CORPSE, "corpse", "xxx", MT_FLESH, 1, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 1, NA, NULL);
addot(OT_HEAD, "head", "xxx", MT_FLESH, 1, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 1, NA, NULL);
addot(OT_FLESHCHUNK, "chunk of flesh", "A chunk of flesh from something.", MT_FLESH, 1, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 5, NA, "");
// potions (sorted by rarity)
addot(OT_POT_JUICE, "potion of fruit juice", "Tasty (but not very fresh) fruit juice!", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addot(OT_POT_WATER, "potion of water", "Plain, regular water.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addot(OT_POT_HEALINGMIN, "potion of minor healing", "Restores 1-8 health to whoever drinks it.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_AIHEALITEM, B_TRUE, NA, NA, NULL);
addot(OT_POT_HEALING, "potion of healing", "Restores 10-20 health to whoever drinks it.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastot->flags, F_AIHEALITEM, B_TRUE, NA, NA, NULL);
addot(OT_POT_OIL, "potion of oil", "A bottle of cooking oil.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addot(OT_POT_ACID, "flask of battery acid", "Causes massive internal burning if ingested.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addot(OT_POT_ACROBATICS, "potion of acrobatics", "Allows the drinker to leap large distances.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addot(OT_POT_ELEMENTENDURE, "potion of endure elements", "Grants the imbiber temporary resistance to both fire and cold.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addot(OT_POT_RESTORATION, "potion of restoration", "Restores lost abilities to the drinker.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addot(OT_POT_SPEED, "potion of haste", "Temporarily increasees the drinker's speed.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_AIBOOSTITEM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_AIFLEEITEM, B_TRUE, NA, NA, NULL);
addot(OT_POT_MAGIC, "potion of magic", "Fully restores the drinker's magical energy.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addot(OT_POT_ELEMENTIMMUNE, "potion of elemental immunity", "Grants the imbiber temporary immunity to both fire and cold.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addot(OT_POT_SANCTUARY, "potion of sanctuary", "Creates a temporary magical barrier abour the drinker.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addot(OT_POT_ETHEREALNESS, "potion of etherealness", "Allows the walker to temporarily pass through walls.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addot(OT_POT_EXPERIENCE, "potion of experience", "Instantly grants the imbiber the next experience level.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 40, NA, NULL);
addot(OT_POT_COMPETENCE, "potion of competence", "Permemantly increases the drinker's strength, intelligence or dexterity.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addot(OT_POT_GASEOUSFORM, "potion of gaseous form", "Turns the drinker into a cloud of gas.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_AIFLEEITEM, B_TRUE, NA, NA, NULL);
addot(OT_POT_POLYMORPH, "potion of polymorph self", "Transmutes the drinker into another living race.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addot(OT_POT_INVULN, "potion of invulnerability", "Grants the drinker temporary immunity to physical harm.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 40, NA, NULL);
addflag(lastot->flags, F_AIBOOSTITEM, B_TRUE, NA, NA, NULL);
addot(OT_POT_AMBROSIA, "vial of ambrosia", "The nectar of the gods, said to completely restore the drinker's health.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 35, NA, NULL);
addflag(lastot->flags, F_AIHEALITEM, B_TRUE, NA, NA, NULL);
// scrolls
addot(OT_SCR_NOTHING, "scroll of nothing", "Looks like a magic scroll, but doesn't do anything.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addot(OT_MAP, "piece of graph paper", "Paper containing a set of grid-lines, intended for mapping.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_HOLDCONFER, F_PHOTOMEM, NA, IFKNOWN, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addot(OT_SCR_CREATEMONSTER, "scroll of create monster", "Summons a (probably hostile) monster to a nearby location.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_CREATEMONSTER, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_AIBOOSTITEM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_AIFLEEITEM, B_TRUE, NA, NA, NULL);
addot(OT_SCR_DETECTAURA, "scroll of detect aura", "Senses holiness or evil near the caster.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTAURA, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 85, NA, NULL);
addot(OT_SCR_DETECTLIFE, "scroll of detect life", "Senses life near the caster.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTLIFE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 85, NA, NULL);
addot(OT_SCR_DETECTMAGIC, "scroll of detect magic", "Allows the reader to detect magical enchantments.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTMAGIC, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addot(OT_SCR_FLAMEPILLAR, "scroll of flame pillar", "Creates a tall pillar of flame.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_FLAMEPILLAR, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addot(OT_SCR_FLAMEBURST, "scroll of flame burst", "Creates a radial blast of fire out from the caster.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_FLAMEBURST, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addot(OT_SCR_ENCHANT, "scroll of enchantment", "Magically enhances a weapon or piece of armour.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_ENCHANT, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addot(OT_SCR_FREEZEOB, "scroll of freezing touch", "Permenantly changes the next object touched into solid ice.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_FREEZEOB, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addot(OT_SCR_IDENTIFY, "scroll of identify", "Completely identifies any one item.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_IDENTIFY, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 85, NA, NULL);
addot(OT_SCR_KNOCK, "scroll of knock", "Magically opens a barrier.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_KNOCK, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addot(OT_SCR_LIGHT, "scroll of light", "Creates a permenant light source centred on the caster.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_LIGHT, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_AIFLEEITEM, B_TRUE, NA, NA, NULL);
addot(OT_SCR_MAPPING, "scroll of sense surroundings", "Magically imbues the caster with a map of his/her surroundings.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_MAPPING, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addot(OT_SCR_MINDSCAN, "scroll of mind scan", "Reveals detailed information about the target.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_MINDSCAN, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addot(OT_SCR_TELEPORTRND, "scroll of random teleportation", "Causes the caster to teleport to a random location within the same level.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_TELEPORTRND, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastot->flags, F_AIFLEEITEM, B_TRUE, NA, NA, NULL);
addot(OT_SCR_REMOVECURSE, "scroll of remove curse", "Removes curses from all weilded equipment.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 85, NA, NULL);
addot(OT_SCR_TURNUNDEAD, "scroll of turn undead", "Instills fear in undead creatures.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_TURNUNDEAD, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addot(OT_SCR_WISH, "scroll of wishing", "Grants the caster any item of their choice (with some limitations).", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_WISH, NA, NA, NULL);
// spells - actually defined as object types
// MUST DEFINE THESE _AFTER_ SCROLLS
///////////////////
// allomancy
///////////////////
// l1
addot(OT_S_ABSORBMETAL, "absorb metal", "Draws mana from nearby metallic objects.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addflag(lastot->flags, F_MPCOST, 0, NA, NA, NULL);
// l2
addot(OT_S_PULLMETAL, "pull metal", "Pulls metal objects to the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addot(OT_S_ACCELMETAL, "accelerate metal", "Greatly accelerates a metal object thrown by the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addot(OT_S_METALHEAL, "metal healing", "Uses nearby metal for accelerated healing.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
// l3
addot(OT_S_EXPLODEMETAL, "explode metal", "Causes all metal objects in a location explode.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addot(OT_S_MAGSHIELD, "magnetic shield", "Surrounds the caster with magnetic force, repelling metal objects and attacks.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
// l4
addot(OT_S_ANIMATEMETAL, "animate metal", "Imbues a metallic weapon with temporary life, enabling it to fight on its own.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
///////////////////
// death
///////////////////
// l2
addot(OT_S_BLINDNESS, "blindness", "Temporarily blinds the target.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l3
addot(OT_S_ANIMATEDEAD, "animate dead", "Imbues nearby corpses with life, creating an undead zombie.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
// TODO: should be "castnearob ot_corpse"
addflag(lastot->flags, F_AICASTANYWHERE, NA, NA, NA, NULL);
addot(OT_S_WEAKEN, "weaken", "Temporarily lowers the target's muscle strength.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_S_PAIN, "pain", "Causes extreme pain in the target whenever they move.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l8
addot(OT_S_INFINITEDEATH, "infinite death", "Annihilates all life on the caster's level!", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 8, NA, NA, NULL);
addflag(lastot->flags, F_AICASTANYWHERE, NA, NA, NA, NULL);
///////////////////
// divination
///////////////////
// l1
addot(OT_S_DETECTLIFE, "detect life", "Senses life near the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
// l2
addot(OT_S_MAPPING, "sense surroundings", "Magically imbues the caster with a map of his/her surroundings.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
// l3
addot(OT_S_DETECTAURA, "detect aura", "Senses holiness or evil near the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addot(OT_S_DETECTMAGIC, "detect magic", "Allows the caster to detect magical enchantments.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
// l8
addot(OT_S_IDENTIFY, "identification", "Completely identifies any one item.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 8, NA, NA, NULL);
///////////////////
// elemental - air
///////////////////
// l3
addot(OT_S_CLOUDKILL, "cloudkill", "Creates a 3x3 cloud of poisonous gas.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
///////////////////
// elemental - fire
///////////////////
// l1
addot(OT_S_SPARK, "spark", "Creates a tiny spark of flame, sufficient to ignite flammable objects but not enough to deal direct damage.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
// l2
addot(OT_S_FIREDART, "flame dart", "Fires a medium-sized dart of fire, dealing 2-6 fire damage.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l3
addot(OT_S_FLAMEPILLAR, "flame pillar", "Creates a tall pillar of flame.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l4
addot(OT_S_FLAMEBURST, "flame burst", "Creates a radial blast of fire out from the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
addflag(lastot->flags, F_AICASTNEXTTOVICTIM, NA, NA, NA, NULL);
// l5
addot(OT_S_BURNINGWAVE, "burning wave", "Fire bursts from the ground in a line towards the target.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addflag(lastot->flags, F_RANGE, 3, NA, NA, NULL);
addot(OT_S_FIREBALL, "fireball", "Creates a huge ball of fire.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL); // TODO: should be "near victim"
///////////////////
// elemental - ice
///////////////////
// l2
addot(OT_S_CONECOLD, "cone of cold", "Shoots a blast of ice cold air, dealing 2-6 cold damage.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ICE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_S_FREEZEOB, "freezing touch", "Permenantly changes the next object touched into solid ice.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
///////////////////
// gravity
///////////////////
// l4
addot(OT_S_SLOW, "slowness", "Decreases the speed of the target.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l5
addot(OT_S_GRAVBOOST, "boost gravity", "Greatly increases gravity around the target, stopping them from moving.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l6
addot(OT_S_HASTE, "haste", "Increases the speed of the target.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
///////////////////
// life
///////////////////
// l1
addot(OT_S_HEALINGMIN, "minor healing", "Restores 1-8 health to the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
addot(OT_S_TURNUNDEAD, "turn undead", "Instills fear in undead creatures. Power depends on caster's skill.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
// l2
addot(OT_S_HEALING, "healing", "Restores 10-20 health to the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
///////////////////
// mental
///////////////////
// l1
addot(OT_S_MINDSCAN, "mind scan", "Reveals detailed information about the target.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
// l2
addot(OT_S_SLEEP, "sleep", "Puts the target creature to sleep.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_S_TELEKINESIS, "telekinesis", "Mentally move or manipulate nearby objects.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
// TODO: hardcode how ai casts this
///////////////////
// modification
///////////////////
// l1
addot(OT_S_INSCRIBE, "inscribe", "Creates a magical inscription viewable to anyone standing nearby.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addot(OT_S_KNOCK, "knock", "Magically opens doors or other such barriers.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addot(OT_S_LIGHT, "light", "Creates a permenant light source centred on the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addflag(lastot->flags, F_AICASTANYWHERE, NA, NA, NA, NULL);
addot(OT_S_DARKNESS, "darkness", "Permenantly darkens the area around the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addflag(lastot->flags, F_AICASTANYWHERE, NA, NA, NA, NULL);
// l3
addot(OT_S_PASSWALL, "passwall", "Allows the caster to temporarily walk through walls.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
// l4
addot(OT_S_GASEOUSFORM, "gaseous form", "Changes the caster into a cloud of gas.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
// l6
addot(OT_S_PETRIFY, "petrify", "Causes the target mosnter to turn into stone.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_S_POLYMORPHRND, "polymorph", "Transmutes the target into a random living race.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l7
addot(OT_S_POLYMORPH, "controlled polymorph", "Transmutes the target into a specified living race.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 7, NA, NA, NULL);
addot(OT_S_ENCHANT, "enchantment", "Magically enhances a weapon or piece of armour.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 7, NA, NA, NULL);
// TODO: hardcode how ai casts this spell
///////////////////
// summoning
///////////////////
// l2
addot(OT_S_CREATEMONSTER, "create monster", "Summons a (probably hostile) monster to a nearby location.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTANYWHERE, NA, NA, NA, NULL);
///////////////////
// translocation
///////////////////
// l1
addot(OT_S_BLINK, "blink", "Teleports the caster to a random location within view.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
// l4
addot(OT_S_TELEPORTRND, "random teleportation", "Causes the caster to teleport to a random location within the same level.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
// l5
addot(OT_S_DISPERSAL, "dispersal", "Scatters everything in the target cell around the area.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_S_TELEPORT, "teleportation", "Causes the caster to teleport to a specified location within the same level.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
// l6
addot(OT_S_GATE, "gate", "Creates a portal to a different dungeon level.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
///////////////////
// wild
///////////////////
// l1
addot(OT_S_MANASPIKE, "mana spike", "Fires a small bolt of wild magic, dealing 1-4 magical damage.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l2
addot(OT_S_ENERGYBOLT, "energy bolt", "Fires a medium-sized bolt of wild magic, dealing 2-6 damage.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// l3
addot(OT_S_ENERGYBLAST, "energy blast", "Causes a ring of energy to expand from the caster, causing 2-6 damage to anything in sight.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addflag(lastot->flags, F_AICASTNEXTTOVICTIM, NA, NA, NA, NULL);
addot(OT_S_FLASH, "flash", "Causes a very bright flash, stunning anyone who sees it.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addflag(lastot->flags, F_AICASTANYWHERE, NA, NA, NA, NULL);
// l4
addot(OT_S_DETONATE, "detonate", "Causes a given area to explode with massive force.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// divine powers
addot(OT_S_WISH, "wish", "Grants the caster any item of their choice (with some limitations).", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLLEVEL, 9, NA, NA, NULL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINE, NA, NA, NULL);
addot(OT_S_GIFT, "gift", "Grants the target any item of their choice (with some limitations).", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLLEVEL, 9, NA, NA, NULL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINE, NA, NA, NULL);
// abilities
addot(OT_A_GRAB, "grab", "You can grab hold of nearby enemies to prevent their escape.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATADJVICTIM, NA, NA, NA, NULL);
addot(OT_A_CRUSH, "crush", "You can crush enemies after grabbing them.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATADJVICTIM, NA, NA, NA, NULL);
addot(OT_A_JUMP, "jump", "You can leap large distances.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addot(OT_A_SPRINT, "sprint", "You can run at high speed over short distances.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
addot(OT_A_DEBUG, "debug", "You can toggle debugging for a lifeform.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addot(OT_A_EMPLOY, "employ", "Assigns a job to the target lifeform.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addot(OT_A_HEAVYBLOW, "heavy blow", "Mighty blow which knocks enemies backwards.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATADJVICTIM, NA, NA, NA, NULL);
addot(OT_A_INSPECT, "inspect", "Try to identify an unknown object from your pack.", MT_NOTHING, 0, OC_ABILITY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL);
// spellbooks
addot(OT_SB_ANIMATEDEAD, "spellbook of animate dead", "Teaches the spell 'animate dead'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_ANIMATEDEAD, NA, NA, NULL);
addot(OT_SB_PAIN, "spellbook of pain", "Teaches the spell 'pain'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_PAIN, NA, NA, NULL);
addot(OT_SB_WEAKEN, "spellbook of weaken", "Teaches the spell 'weaken'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_WEAKEN, NA, NA, NULL);
addot(OT_SB_BLINDNESS, "spellbook of blindness", "Teaches the spell 'blindness'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_BLINDNESS, NA, NA, NULL);
addot(OT_SB_INFINITEDEATH, "spellbook of infinite death", "Teaches the spell 'infinite death'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_INFINITEDEATH, NA, NA, NULL);
// divination
addot(OT_SB_DETECTLIFE, "spellbook of detect life", "Teaches the spell 'detect life'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTLIFE, NA, NA, NULL);
addot(OT_SB_MAPPING, "spellbook of sense surroundings", "Teaches the spell 'surroundings'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_MAPPING, NA, NA, NULL);
addot(OT_SB_DETECTAURA, "spellbook of detect aura", "Teaches the spell 'detect aura'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_DETECTAURA, NA, NA, NULL);
addot(OT_SB_IDENTIFY, "spellbook of identification", "Teaches the spell 'identification'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_IDENTIFY, NA, NA, NULL);
// elemental - air
addot(OT_SB_CLOUDKILL, "spellbook of cloudkill", "Teaches the spell 'cloudkill'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_CLOUDKILL, NA, NA, NULL);
// elemental - fire
addot(OT_SB_SPARK, "spellbook of spark", "Teaches the spell 'spark'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_SPARK, NA, NA, NULL);
addot(OT_SB_FIREDART, "spellbook of flame dart", "Teaches the spell 'flame dart'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_FIREDART, NA, NA, NULL);
addot(OT_SB_FLAMEPILLAR, "spellbook of flame pillar", "Teaches the spell 'flame pillar'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_FLAMEPILLAR, NA, NA, NULL);
addot(OT_SB_FLAMEBURST, "spellbook of flame burst", "Teaches the spell 'flame burst'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_FLAMEBURST, NA, NA, NULL);
addot(OT_SB_FIREBALL, "spellbook of fireball", "Teaches the spell 'fireball'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_FIREBALL, NA, NA, NULL);
addot(OT_SB_SLOW, "spellbook of slowness", "Teaches the spell 'slowness'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_SLOW, NA, NA, NULL);
addot(OT_SB_GRAVBOOST, "spellbook of boost gravity", "Teaches the spell 'boost gravity'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_GRAVBOOST, NA, NA, NULL);
addot(OT_SB_HASTE, "spellbook of haste", "Teaches the spell 'haste'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_HASTE, NA, NA, NULL);
// elemental - ice
addot(OT_SB_CONECOLD, "spellbook of cone of cold", "Teaches the spell 'cone of cold'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_CONECOLD, NA, NA, NULL);
// life
addot(OT_SB_HEALINGMIN, "spellbook of minor healing", "Teaches the spell 'minor healing'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_HEALINGMIN, NA, NA, NULL);
addot(OT_SB_TURNUNDEAD, "spellbook of turn undead", "Teaches the spell 'turn undead'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_TURNUNDEAD, NA, NA, NULL);
addot(OT_SB_HEALING, "spellbook of healing", "Teaches the spell 'healing'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_HEALING, NA, NA, NULL);
// mental
addot(OT_SB_MINDSCAN, "spellbook of mind scan", "Teaches the spell 'mind scan'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_MINDSCAN, NA, NA, NULL);
addot(OT_SB_TELEKINESIS, "spellbook of telekinesis", "Teaches the spell 'telekinesis'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_TELEKINESIS, NA, NA, NULL);
// modification
addot(OT_SB_INSCRIBE, "spellbook of inscribe", "Teaches the spell 'inscribe'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_INSCRIBE, NA, NA, NULL);
addot(OT_SB_KNOCK, "spellbook of knock", "Teaches the spell 'knock'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_KNOCK, NA, NA, NULL);
addot(OT_SB_LIGHT, "spellbook of light", "Teaches the spell 'light'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_LIGHT, NA, NA, NULL);
addot(OT_SB_DARKNESS, "spellbook of darkness", "Teaches the spell 'darkness'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_DARKNESS, NA, NA, NULL);
addot(OT_SB_FREEZEOB, "spellbook of freezing touch", "Teaches the spell 'freezing touch'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_FREEZEOB, NA, NA, NULL);
addot(OT_SB_GASEOUSFORM, "spellbook of gaseous form", "Teaches the spell 'gaseous form'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_GASEOUSFORM, NA, NA, NULL);
addot(OT_SB_PASSWALL, "spellbook of passwall", "Teaches the spell 'passwall'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_PASSWALL, NA, NA, NULL);
addot(OT_SB_PETRIFY, "spellbook of petrify", "Teaches the spell 'petrify'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_PETRIFY, NA, NA, NULL);
addot(OT_SB_POLYMORPHRND, "spellbook of polymorph", "Teaches the spell 'polymorph'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_POLYMORPHRND, NA, NA, NULL);
addot(OT_SB_POLYMORPH, "spellbook of controlled polymorph", "Teaches the spell 'controlled polymorph'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_POLYMORPH, NA, NA, NULL);
// summoning
addot(OT_SB_CREATEMONSTER, "spellbook of create monster", "Teaches the spell 'create monster'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_CREATEMONSTER, NA, NA, NULL);
// translocation
addot(OT_SB_BLINK, "spellbook of blink", "Teaches the spell 'blink'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_BLINK, NA, NA, NULL);
addot(OT_SB_TELEPORTRND, "spellbook of random teleportation", "Teaches the spell 'random teleportation'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_TELEPORTRND, NA, NA, NULL);
addot(OT_SB_DISPERSAL, "spellbook of dispersal", "Teaches the spell 'dispersal'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_DISPERSAL, NA, NA, NULL);
addot(OT_SB_TELEPORT, "spellbook of teleportation", "Teaches the spell 'teleportation'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_TELEPORT, NA, NA, NULL);
addot(OT_SB_GATE, "spellbook of gate", "Teaches the spell 'gate'.", MT_PAPER, 1.5, OC_BOOK);
addflag(lastot->flags, F_LINKSPELL, OT_S_GATE, NA, NA, NULL);
// assign rarity to spellbooks
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id == OC_BOOK) && !hasflag(ot->flags, F_RARITY)) {
flag_t *f;
f = hasflag(ot->flags, F_LINKSPELL);
if (f) {
// find the linked spell
objecttype_t *spelltype;
spelltype = findot(f->val[0]);
if (spelltype) {
flag_t *f2;
f2 = hasflag(spelltype->flags, F_SPELLLEVEL);
if (f2) {
int rarity;
rarity = 90 - (f2->val[0]*12);
addflag(ot->flags, F_RARITY, H_ALL, rarity, NA, NULL);
} else {
dblog("Spell %s has no spell level - can't determine rarity for spellbook.", spelltype->name);
exit(1);
}
} else {
dblog("Spellbook %s linked to a spell id that doesn't exist.",ot->name);
exit(1);
}
}
}
}
// wands
addot(OT_WAND_COLD, "wand of cold", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_CONECOLD, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_WAND_DETONATION, "wand of detonation", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_DETONATE, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER|TT_DOOR, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_WAND_FIREBALL, "wand of fireball", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_FIREBALL, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_WAND_FIRE, "wand of fire", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_FIREDART, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_WAND_HASTE, "wand of haste", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_HASTE, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATSELF, NA, NA, NA, NULL);
addot(OT_WAND_KNOCK, "wand of opening", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_KNOCK, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_DOOR, NA, NA, NULL);
addot(OT_WAND_LIGHT, "wand of light", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_LIGHT, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addot(OT_WAND_POLYMORPH, "wand of polymorph", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_POLYMORPHRND, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_WAND_SLOW, "wand of slowness", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_SLOW, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_WAND_WEAKNESS, "wand of weakness", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_LINKSPELL, OT_S_WEAKEN, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_WAND_WONDER, "wand of wonder", "Produces random effects.", MT_METAL, 0.5, OC_WAND);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_NONE, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
// tech/tools
addot(OT_POCKETWATCH, "pocket watch", "A portable timekeeping device made to be carried in a pocket.", MT_METAL, 0.1, OC_TECH);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addot(OT_DIGITALWATCH, "digital watch", "An electronic timekeeping device which shows the time as a number.", MT_METAL, 0.1, OC_TECH);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addot(OT_BLINDFOLD, "blindfold", "Short length of wide cloth, used for blocking eyesight.", MT_CLOTH, 0.01, OC_TECH);
addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_BLIND, B_TRUE, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addot(OT_C4, "block of c4", "A highly explosive plastic which explodes a medium time after activation.", MT_PLASTIC, 1, OC_TECH);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 25, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERONOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_CHARGES, 5, 5, NA, NULL);
addflag(lastot->flags, F_DONTSHOWCHARGES, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RECHARGEWHENOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_EXPLODEONDEATH, 30, B_BIG, B_IFACTIVATED, NULL);
addflag(lastot->flags, F_GRENADE, B_TRUE, NA, NA, NULL);
addot(OT_BUGLAMP, "glowing flask", "A glass flask with a glowbug corpse inside.", MT_GLASS, 0.3, OC_TECH);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
addflag(lastot->flags, F_HOLDCONFER, F_PRODUCESLIGHT, 2, IFKNOWN, NULL);
addot(OT_CREDITCARD, "credit card", "A rectangular plastic card.", MT_PLASTIC, 0.01, OC_TECH);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_PICKLOCKS, 2, NA, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addot(OT_FLASHBANG, "flashbang", "A stun grenade which temporarily blinds all within sight.", MT_METAL, 1, OC_TECH);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 40, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERONOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_CHARGES, 2, 2, NA, NULL);
addflag(lastot->flags, F_DONTSHOWCHARGES, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RECHARGEWHENOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_FLASHONDEATH, 4, NA, B_IFACTIVATED, NULL);
addflag(lastot->flags, F_FLASHONDAM, 4, NA, B_IFACTIVATED, NULL);
addflag(lastot->flags, F_GRENADE, B_TRUE, NA, NA, NULL);
addot(OT_GRENADE, "grenade", "An explosive weapon which explodes a short time after activation.", MT_METAL, 1, OC_TECH);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 30, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERONOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_CHARGES, 2, 2, NA, NULL);
addflag(lastot->flags, F_DONTSHOWCHARGES, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RECHARGEWHENOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_EXPLODEONDEATH, 10, NA, B_IFACTIVATED, NULL);
addflag(lastot->flags, F_EXPLODEONDAM, 10, NA, B_IFACTIVATED, NULL);
addflag(lastot->flags, F_GRENADE, B_TRUE, NA, NA, NULL);
addot(OT_INFOVISOR, "infovisor", "Sleek looking metal visor which displays info directly into the retina.", MT_METAL, 0.2, OC_TECH);
addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_EXTRAINFO, B_TRUE, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 30, NA, NULL);
addot(OT_INSECTICIDE, "can of insecticide", "A spraycan containing poisonous chemicals.", MT_METAL, 0.5, OC_TECH);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERUSECHARGE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDTARGET, TT_MONSTER, NA, NA, "Where will you spray?");
addflag(lastot->flags, F_RNDCHARGES, 5, 10, NA, NULL);
addot(OT_JETPACK, "jet pack", "A portable ion-thruster which allows the wearer to fly.", MT_METAL, 10, OC_TECH);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 30, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERONOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RNDCHARGES, 10, 30, NA, NULL);
addflag(lastot->flags, F_REFILLWITH, OT_POT_OIL, NA, NA, NULL);
addflag(lastot->flags, F_ACTIVATECONFER, F_FLYING, B_TRUE, NA, NULL);
addflag(lastot->flags, F_ACTIVATECONFER, F_PRODUCESLIGHT, 1, NA, NULL);
addot(OT_LOCKHACKER, "lock hacker", "A sophisticated machine to manipulate physical locks.", MT_METAL, 3, OC_TECH);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 20, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addot(OT_LANTERNLED, "LED lantern", "A low-powered but efficient lantern which will last almost forever.", MT_METAL, 0.5, OC_TECH);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 20, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERONOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_ACTIVATECONFER, F_PRODUCESLIGHT, 2, NA, NULL);
addot(OT_LANTERNOIL, "oil lantern", "An oil-powered lantern which produces a lot of light.", MT_METAL, 1, OC_TECH);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 55, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERONOFF, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_ACTIVATECONFER, F_PRODUCESLIGHT, 3, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 3, NA, IFACTIVE, NULL);
addflag(lastot->flags, F_RNDCHARGES, 100, 200, NA, NULL);
addflag(lastot->flags, F_REFILLWITH, OT_POT_OIL, NA, NA, NULL);
addot(OT_LOCKPICK, "lockpick", "An angled piece of metal, used to open locks.", MT_METAL, 0.05, OC_TECH);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_PICKLOCKS, 10, B_DIEONFAIL, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addot(OT_PAPERCLIP, "paperclip", "A thin, looped wire for holding paper together.", MT_METAL, 0.01, OC_TECH);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_PICKLOCKS, 4, B_DIEONFAIL, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
// can use as a (very bad) weapon too...
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 1, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 50, NA, NA, NULL);
addot(OT_PICKAXE, "pickaxe", "A heavy tool for breaking rock.", MT_METAL, 8, OC_TECH);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addot(OT_TELEPAD, "teleport beacon", "A metal cone which will teleport the user to the nearest similar cone.", MT_METAL, 3, OC_TECH);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 40, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addot(OT_XRAYGOGGLES, "pair of xray goggles", "Bulky looking goggles which allow you to see through walls.", MT_METAL, 0.3, OC_TECH);
addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_XRAYVIS, 2, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 25, NA, NULL);
// misc
addot(OT_EMPTYFLASK, "empty flask", "An empty glass flask.", MT_GLASS, 0.2, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "!");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addot(OT_EMPTYVIAL, "empty vial", "An empty glass vial.", MT_GLASS, 0.1, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "!");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addot(OT_BROKENGLASS, "piece of broken glass", "Sharp shards of broken glass.", MT_GLASS, 0.1, OC_MISC);
addflag(lastot->flags, F_STACKABLE, NA, NA, NA, NULL);
addflag(lastot->flags, F_NUMAPPEAR, 1, 8, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "^");
addflag(lastot->flags, F_SHARP, 1, 2, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addot(OT_CALTROP, "caltrop", "Connected metal spikes arranged such that one will always point upwards.", MT_METAL, 0.2, OC_MISC);
addflag(lastot->flags, F_STACKABLE, NA, NA, NA, NULL);
addflag(lastot->flags, F_NUMAPPEAR, 1, 3, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "^");
addflag(lastot->flags, F_SHARP, 2, 5, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addot(OT_ICESHEET, "sheet of ice", "A large sheet of slippery ice.", MT_ICE, 0.5, OC_MISC);
addflag(lastot->flags, F_STACKABLE, NA, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "_");
addflag(lastot->flags, F_DTCONVERT, DT_FIRE, NA, NA, "large puddle of water");
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "large puddle of water");
addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "melts");
addflag(lastot->flags, F_DIECONVERTTEXTPL, NA, NA, NA, "melt");
addflag(lastot->flags, F_OBHP, 50, 50, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_SLIPPERY, 15, NA, NA, NULL);
addot(OT_ICECHUNK, "chunk of ice", "A chunk of ice.", MT_ICE, 0.5, OC_MISC);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 3, NA, NULL);
addflag(lastot->flags, F_STACKABLE, NA, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ",");
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "small puddle of water");
addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "melts");
addflag(lastot->flags, F_DIECONVERTTEXTPL, NA, NA, NA, "melt");
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addot(OT_SOGGYPAPER, "lump of soggy paper", "A useless lump of soggy paper.", MT_WETPAPER, 0.1, OC_MISC);
addflag(lastot->flags, F_STACKABLE, NA, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "?");
addot(OT_VOMITPOOL, "pool of vomit", "A disgusting pool of regurgitated food.", MT_WATER, 0, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ",");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_SLIPPERY, 1, NA, NA, NULL);
addot(OT_SLIMEPOOL, "pool of slime", "A disgusting mass of sticky slime.", MT_WATER, 0, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ",");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 2, NA, NULL);
addot(OT_PUDDLEWATER, "small puddle of water", "A small puddle of water.", MT_WATER, 0, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DTCONVERT, DT_COLD, NA, NA, "sheet of ice");
addot(OT_PUDDLEWATERL, "large puddle of water", "A large pool of water.", MT_WATER, 0, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DTCONVERT, DT_COLD, NA, NA, "sheet of ice");
addot(OT_BLOODSTAIN, "blood stain", "A dried stain of blood.", MT_BLOOD, 0, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ",");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addot(OT_BLOODSPLASH, "splash of blood", "A small pool of blood.", MT_BLOOD, 0, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ",");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_SLIPPERY, 1, NA, NA, NULL);
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "blood stain");
addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dries up");
addflag(lastot->flags, F_DIECONVERTTEXTPL, NA, NA, NA, "dry up");
addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addot(OT_BLOODPOOL, "pool of blood", "A large pool of blood.", MT_BLOOD, 0, OC_MISC);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ",");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_SLIPPERY, 3, NA, NA, NULL);
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "blood stain");
addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dries up");
addflag(lastot->flags, F_DIECONVERTTEXTPL, NA, NA, NA, "dry up");
addflag(lastot->flags, F_OBHP, 60, 60, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addot(OT_WOODENBARREL, "wooden barrel", "A solid wooden barrel.", MT_WOOD, 20, OC_MISC);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "(");
addflag(lastot->flags, F_IMPASSABLE, SZ_LARGE, NA, NA, NULL);
addflag(lastot->flags, F_CRUSHABLE, SZ_HUGE, NA, NA, NULL);
addflag(lastot->flags, F_PUSHABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 3, 6, NA, NULL);
addot(OT_WOODENSTOOL, "wooden footstool", "A small, wooden footstool.", MT_WOOD, 5, OC_MISC);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "\\");
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL);
addflag(lastot->flags, F_DTVULN, DT_CHOP, NA, NA, NULL);
addot(OT_BONE, "bone", "A bone from an unknown creature.", MT_BONE, 0.1, OC_MISC);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ",");
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
// effects
addot(OT_FIRELARGE, "large fire", "A large, roaring inferno.", MT_FIRE, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "}");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dies down a little");
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "medium fire");
addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_WALKDAM, 12, DT_FIRE, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 3, NA, NA, NULL);
addot(OT_FIREMED, "medium fire", "A medium-sized roaring fire.", MT_FIRE, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "}");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "small fire");
addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "dies down a little");
addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_WALKDAM, 7, DT_FIRE, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
addot(OT_FIRESMALL, "small fire", "A small blaze.", MT_FIRE, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "}");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DIETEXT, B_TRUE, NA, NA, "goes out");
addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_WALKDAM, 3, DT_FIRE, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL);
addot(OT_SMOKECLOUD, "cloud of smoke", "A thick cloud of black smoke.", MT_GAS, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_NODIECONVERTTEXT, NA, NA, NA, NULL);
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "puff of smoke");
addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_BLOCKSVIEW, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addot(OT_SMOKEPUFF, "puff of smoke", "A small puff of black smoke.", MT_GAS, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DIETEXT, B_TRUE, NA, NA, "disperses");
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addot(OT_POISONCLOUD, "cloud of gas", "A thick cloud of poisonous gas.", MT_GAS, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DIECONVERTTEXT, NA, NA, NA, "thins out a little");
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "puff of gas");
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_WALKDAM, 2, DT_POISONGAS, NA, NULL);
addot(OT_POISONPUFF, "puff of gas", "A small puff of poisonous gas.", MT_GAS, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "{");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DIETEXT, B_TRUE, NA, NA, "disperses");
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_WALKDAM, 2, DT_POISONGAS, NA, NULL);
addot(OT_MAGICBARRIER, "magical barrier", "A glowing, impassable barrier of magical energy.", MT_MAGIC, 0, OC_EFFECT);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "#");
addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_IMPASSABLE, SZ_MAX, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DIETEXT, B_TRUE, NA, NA, "vanishes");
addflag(lastot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL);
// armour - body
addot(OT_COTTONSHIRT, "cotton shirt", "A comfortable white cotton shirt.", MT_CLOTH, 0.7, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
addot(OT_ARMOURLEATHER, "leather armour", "Body armour created from soft leather.", MT_LEATHER, 10, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 4, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL);
addot(OT_ARMOURRING, "suit of ring mail", "Body armour formed by a series of metallic rings sewn to a leather foundation.", MT_METAL, 15, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 6, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL);
addot(OT_ARMOURSCALE, "suit of scale armour", "Body armour consisting of many small scales attached to leather.", MT_METAL, 20, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 72, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 10, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -15, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 35, 35, NA, NULL);
addot(OT_ARMOURCHAIN, "suit of chainmail", "Heavy body armour consisting of tightly meshed metal rings.", MT_METAL, 25, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 70, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 15, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -20, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 45, 45, NA, NULL);
addot(OT_ARMOURSPLINT, "suit of splint mail", "Heavy armour, consisting of strips of metal attached to a leather backing.", MT_METAL, 35, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 67, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 20, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -25, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 50, 50, NA, NULL);
addot(OT_ARMOURPLATE, "suit of plate mail", "Heavy armour with embedded metal plates.", MT_METAL, 40, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 65, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 25, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -30, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 60, 60, NA, NULL);
addot(OT_FLAKJACKET, "flak jacket", "Heavy metal body armour, designed to stop a bullet.", MT_METAL, 30, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 25, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 10, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
addot(OT_OVERALLS, "pair of overalls", "Well-made, brightly coloured workman overalls.", MT_CLOTH, 1, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_LEGS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
addot(OT_SILKSHIRT, "silk shirt", "A lightweight, comfortable white silk shirt.", MT_CLOTH, 0.5, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 60, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
addot(OT_ROBE, "robe", "A plain robe.", MT_CLOTH, 4, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL);
addot(OT_VELVETROBE, "velvet robe", "A luxurious velvet robe.", MT_CLOTH, 4, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 65, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_BODY, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 4, 4, NA, NULL);
// armour - shoulders
addot(OT_CLOAK, "cloak", "A standard leather cloak.", MT_LEATHER, 4, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_SHOULDERS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -5, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 6, 6, NA, NULL);
// armour - waist
addot(OT_BELTLEATHER, "leather belt", "A plain leather belt.", MT_LEATHER, 0.2, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 85, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_WAIST, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
// armour - legs
addot(OT_CLOTHTROUSERS, "pair of cloth trousers", "A rough pair of cloth trousers.", MT_CLOTH, 2, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_LEGS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addot(OT_RIDINGTROUSERS, "pair of riding trousers", "A fitted pair of leather trousers.", MT_LEATHER, 2, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 70, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_LEGS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL);
addot(OT_COMBATPANTS, "pair of combat pants", "An armoured pair of camoflauged trousers.", MT_CLOTH, 2, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 65, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_LEGS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 3, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 8, 8, NA, NULL);
// armour - feet
addot(OT_SANDALS, "pair of sandals", "Comfortable pair of open leather sandals.", MT_LEATHER, 1, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_FEET, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
addot(OT_SHOESLEATHER, "pair of leather shoes", "Cheap and rather uncomfortable leather shoes.", MT_LEATHER, 2, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 85, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_FEET, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 3, 3, NA, NULL);
addot(OT_BOOTSRUBBER, "pair of rubber boots", "A waterproof (but somewhat cumbersome) pair of rubber boots.", MT_RUBBER, 6, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 60, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_FEET, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -10, NA, NA, NULL); // bulky
addflag(lastot->flags, F_OBHP, 8, 8, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_DTRESIST, DT_ELECTRIC, NA, NULL);
addot(OT_BOOTSSPIKED, "pair of spiked boots", "A plain pair of leather boots with spikes on the bottom.", MT_LEATHER, 3, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 70, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_FEET, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_STABILITY, NA, NA, NULL);
addot(OT_BOOTSLEATHER, "pair of leather boots", "A stout pair of leather boots.", MT_LEATHER, 4, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_FEET, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL);
// armour - gloves
addot(OT_GLOVESCLOTH, "pair of cloth gloves", "A pair of soft cloth gloves.", MT_CLOTH, 0.15, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HANDS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addot(OT_GLOVESLEATHER, "pair of leather gloves", "A pair of coarse leather gloves.", MT_LEATHER, 0.25, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 85, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HANDS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL);
addot(OT_GAUNTLETS, "pair of gauntlets", "A durable pair of metal gauntlets.", MT_METAL, 2, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 65, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HANDS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -3, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 15, 15, NA, NULL);
// armour - head
addot(OT_SUNHAT, "sun hat", "Wide-brimmed hat made for working in the sun.", MT_CLOTH, 1, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -5, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
addot(OT_CAP, "cap", "Close-fitting headwear with a short shade visor at the front.", MT_CLOTH, 1, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
addot(OT_GASMASK, "gas mask", "A full face mask which protects the wearer from toxic gasses.", MT_METAL, 1, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_DTIMMUNE, DT_POISONGAS, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -5, NA, NULL);
addot(OT_HELM, "helmet", "A plain metal helmet.", MT_METAL, 2, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 3, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 15, 15, NA, NULL);
addot(OT_HELMFOOTBALL, "football helmet", "A metal helmet with a grill in front of the face.", MT_METAL, 1, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 75, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -10, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -2, NA, NULL);
addot(OT_GOLDCROWN, "golden crown", "A heavy gold crown, encrusted with jewels.", MT_GOLD, 5, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 25, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL);
addot(OT_HELMBONE, "bone helmet", "Scary-looking helmet made from the bones of an animal (?).", MT_BONE, 1, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 85, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_HEAD, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 2, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
// armour - eyes
addot(OT_SUNGLASSES, "sunglasses", "Tinted eyewear to protect against sunlight.", MT_PLASTIC, 0.01, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 70, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 0, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_VISRANGEMOD, -4, NA, NULL);
addflag(lastot->flags, F_TINTED, B_TRUE, NA, NA, NULL);
addot(OT_MOTIONSCANNER, "motion scanner", "Small scanning device which detects nearby lifeforms.", MT_METAL, 1.5, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 30, NA, NULL);
addflag(lastot->flags, F_HOLDCONFER, F_DETECTLIFE, 10, NA, NULL);
addot(OT_NVGOGGLES, "nightvis goggles", "Special goggles which allow the wear to see in the dark.", MT_METAL, 1.5, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 25, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addflag(lastot->flags, F_EQUIPCONFER, F_SEEINDARK, B_TRUE, NA, NULL);
// armour - shields
addot(OT_BUCKLER, "buckler", "A small, unobtrusive wooden shield.", MT_WOOD, 3.00, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_SHIELD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_SECWEAPON, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 4, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 10, 10, NA, NULL);
addot(OT_SHIELD, "shield", "A medium-sized metal shield.", MT_METAL, 4.00, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_SHIELD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_SECWEAPON, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 6, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, -10, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL);
addot(OT_SHIELDLARGE, "large shield", "A large (if somewhat cumbersome) shield.", MT_METAL, 6.00, OC_ARMOUR);
addflag(lastot->flags, F_RARITY, H_ALL, 80, NA, NULL);
addflag(lastot->flags, F_SHIELD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_GOESON, BP_SECWEAPON, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 8, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, -15, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL);
// rings
addot(OT_RING_INVULN, "ring of invulnerability", "Grants the caster complete immunity to physical harm.", MT_METAL, 0.1, OC_RING);
addflag(lastot->flags, F_EQUIPCONFER, F_INVULNERABLE, NA, NA, NULL);
addot(OT_RING_MPREGEN, "ring of mana", "Slowly regenerates the wearer's mana.", MT_METAL, 0.1, OC_RING);
addflag(lastot->flags, F_RARITY, H_ALL, 50, NA, "");
addflag(lastot->flags, F_EQUIPCONFER, F_MPREGEN, 1, NA, NULL);
addot(OT_RING_PROTFIRE, "ring of fire immunity", "Grants the caster complete immunity to fire.", MT_METAL, 0.1, OC_RING);
addflag(lastot->flags, F_RARITY, H_ALL, 60, NA, "");
addflag(lastot->flags, F_EQUIPCONFER, F_DTIMMUNE, DT_FIRE, NA, NULL);
addot(OT_RING_REGENERATION, "ring of regeneration", "Slowly regenerates the wearer's health, even when not resting.", MT_METAL, 0.1, OC_RING);
addflag(lastot->flags, F_RARITY, H_ALL, 50, NA, "");
addflag(lastot->flags, F_EQUIPCONFER, F_REGENERATES, 1, NA, NULL);
addot(OT_RING_RESISTMAG, "ring of magic resistance", "Renders the wearer immune to most magical effects.", MT_METAL, 0.1, OC_RING);
addflag(lastot->flags, F_RARITY, H_ALL, 25, NA, "");
addflag(lastot->flags, F_EQUIPCONFER, F_RESISTMAG, 1, NA, NULL);
// unarmed weapons - note these damage/accuracys can be
// overridded with the lifeform flag F_HASATTACK
//
// DAMTYPE _cannot_ be overridden (yet)!
addot(OT_FISTS, "fists", "human fists", MT_FLESH, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_UNARMED, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addot(OT_TEETH, "teeth", "teeth object", MT_BONE, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_BITE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addot(OT_CLAWS, "claws", "claws object", MT_BONE, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_CLAW, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addot(OT_TAIL, "tail", "tail object", MT_FLESH, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 4, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_TENTACLE, "tentacle", "tentacle object", MT_FLESH, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 2, 6, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addot(OT_ZAPPER, "zapper", "zapper object", MT_NOTHING, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_ELECTRIC, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
// monster weapons
addot(OT_ACIDATTACK, "acidattack", "acid attack object", MT_WATER, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_ACID, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 60, NA, NA, NULL);
addot(OT_TOUCHPARALYZE, "paralyzing touch", "paralyzing touch object", MT_BONE, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_TOUCH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 0, 1, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_HITCONFER, F_PARALYZED, B_TRUE, NA, "2-4");
addot(OT_TOUCHPARALYZE2, "strong paralyzing touch", "strong paralyzing touch object", MT_BONE, 0, OC_WEAPON);
addflag(lastot->flags, F_DAMTYPE, DT_TOUCH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 0, 1, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_HITCONFER, F_PARALYZED, B_TRUE, NA, "5-10");
// missiles
addot(OT_DART, "dart", "A small, sharp projectile weapon.", MT_WOOD, 0.5, OC_MISSILE);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_MISSILEDAM, 2, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 10, NA, "");
addot(OT_NANODART, "nanodart", "A metal dart with a laser-sharpened point.", MT_METAL, 0.5, OC_MISSILE);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_MISSILEDAM, 2, NA, NA, "");
addflag(lastot->flags, F_ARMOURPIERCE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 10, NA, "");
addot(OT_JAVELIN, "javelin", "A long, sharp missile weapon.", MT_METAL, 1.5, OC_MISSILE);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_MISSILEDAM, 3, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 3, NA, "");
addflag(lastot->flags, F_OBHP, 5, 5, NA, "");
addot(OT_ARROW, "arrow", "A sharp wooden arrow.", MT_WOOD, 0.5, OC_MISSILE);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 10, NA, "");
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
addot(OT_BOLT, "bolt", "A sharp metal spike, meant for firing from a crossbow.", MT_METAL, 1, OC_MISSILE);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 5, NA, "");
addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
addot(OT_BULLET, "bullet", "A regular gun bullet.", MT_STONE, 0.1, OC_MISSILE);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 10, NA, "");
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
addot(OT_RUBBERBULLET, "rubber bullet", "A rubber gun bullet - does not do much damage.", MT_STONE, 0.1, OC_MISSILE);
addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, "");
addflag(lastot->flags, F_NUMAPPEAR, 1, 10, NA, "");
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 1, 1, NA, NULL);
addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
// stabbing weapons
addot(OT_DAGGER, "dagger", "A short stabbing weapon with a pointed blade.", MT_METAL, 1, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 4, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 2, 2, NA, NULL);
addflag(lastot->flags, F_PICKLOCKS, 7, B_BLUNTONFAIL, NA, NULL);
addot(OT_ORNDAGGER, "ornamental dagger", "This dagger is pretty, but not particularly effective.", MT_METAL, 1, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_SHINY, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 3, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PICKLOCKS, 7, B_BLUNTONFAIL, NA, NULL);
addot(OT_SHORTSWORD, "short sword", "A short blade for fighting. Better for stabbing.", MT_METAL, 2.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 6, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 90, NA, NA, NULL);
addot(OT_RAPIER, "rapier", "A long, narrow French sword lacking a cutting edge. Made for stabbing.", MT_METAL, 2.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 8, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 90, NA, NA, NULL);
addot(OT_TRIDENT, "trident", "A three-pronged stabbing weapon.", MT_METAL, 3, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 110, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 10, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_QUICKBLADE, "quickblade", "A short blade of exceptional quality, which somehow allows its bearer to attack faster.", MT_METAL, 1, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 35, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 90, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 4, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addot(OT_COMBATKNIFE, "combat knife", "A sharp knife designed for military use.", MT_METAL, 1, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 4, 1, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PICKLOCKS, 7, B_BLUNTONFAIL, NA, NULL);
// chopping weapons
addot(OT_AXE, "axe", "A short pole with a heavy, wedge-shaped blade for chopping.", MT_METAL, 4, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 150, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_CHOP, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 4, 1, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_HANDAXE, "hand axe", "A fast one-handed axe made for combat.", MT_METAL, 2, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 120, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_CHOP, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 6, 1, NULL);
addflag(lastot->flags, F_ACCURACY, 85, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addot(OT_BATTLEAXE, "battleaxe", "An axe specifically designed for combat.", MT_METAL, 5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 140, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_CHOP, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 9, 1, NULL);
addflag(lastot->flags, F_TWOHANDED, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_GREATAXE, "greataxe", "An enormous axe made designed for combat.", MT_METAL, 8, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 180, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_CHOP, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 10, 2, NULL);
addflag(lastot->flags, F_TWOHANDED, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 70, NA, NA, NULL);
// slashing weapons
addot(OT_KNIFE, "knife", "A moderately sharp stabbing tool.", MT_METAL, 1, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 3, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PICKLOCKS, 7, B_BLUNTONFAIL, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL);
addot(OT_STEAKKNIFE, "steak knife", "A common kitchen knife.", MT_METAL, 0.2, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL);
addot(OT_SCYTHE, "scythe", "An agricultural hand tool for mowing grass, or reaping crops.", MT_METAL, 3, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 150, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 2, 4, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 65, NA, NA, NULL);
addot(OT_SICKLE, "sickle", "A hand-held agricultural tool with a curved blade.", MT_METAL, 0.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 6, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 60, NA, NA, NULL);
addot(OT_SCIMITAR, "scimitar", "A sharp, curved blade.", MT_METAL, 2, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 130, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 8, 2, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_LONGSWORD, "longsword", "Standard issue long slashing weapon.", MT_METAL, 3, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 130, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 8, 3, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_ORNSWORD, "ornamental sword", "A gleaming (but quite blunt) blade.", MT_METAL, 2, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_SHINY, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 130, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 6, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 70, NA, NA, NULL);
addot(OT_GREATSWORD, "greatsword", "A massive two-handed sword.", MT_METAL, 10, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 180, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 12, 6, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_TWOHANDED, B_TRUE, NA, NA, NULL);
// polearms
addot(OT_QUARTERSTAFF, "quarterstaff", "A long, stout pole.", MT_WOOD, 2, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 130, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 8, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_TWOHANDED, B_TRUE, NA, NA, NULL);
addot(OT_SPEAR, "spear", "A long pole with a sharpened head.", MT_METAL, 4, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 140, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 8, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 75, NA, NA, NULL);
addflag(lastot->flags, F_MISSILE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 15, 15, NA, NULL);
// bashing weapons
addot(OT_STICK, "stick", "A sturdy wooden stick.", MT_WOOD, 0.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL);
addot(OT_SPANNER, "spanner", "A long, heavy metal wrench.", MT_METAL, 1, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 120, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 4, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 65, NA, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERNEEDDIR, B_TRUE, NA, NA, "Use your spanner in which direction");
addot(OT_CLUB, "club", "A heavy, blunt wooden instrument to hit things with.", MT_STONE, 1.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 140, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 6, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_MACE, "mace", "A weapon with a heavy head on a solid shaft used to bludgeon opponents.", MT_METAL, 3, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 140, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 8, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_FLAIL, "flail", "A flexible chain attached to a heavy weight.", MT_METAL, 6, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 140, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 2, 4, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_FLAILHEAVY, "heavy flail", "A flexible chain attached to a very heavy weight.", MT_METAL, 12, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 155, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 2, 6, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_MORNINGSTAR, "morningstar", "A heavy, spiked mace.", MT_METAL, 3.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 150, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 10, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_TWOHANDED, B_TRUE, NA, NA, NULL);
addot(OT_GREATCLUB, "great club", "An enormous, very heavy, blunt instrument to hit things with.", MT_STONE, 5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_OBATTACKDELAY, 180, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 10, 5, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_TWOHANDED, B_TRUE, NA, NA, NULL);
// projectile weapons
addot(OT_SLING, "sling", "Stretchy piece of rubber for launching projectiles.", MT_RUBBER, 0.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, NA, NULL);
addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_FIRESPEED, 3, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 70, NA, NA, NULL);
addflag(lastot->flags, F_RANGE, 5, NA, NA, NULL);
addflag(lastot->flags, F_AMMOOB, OT_STONE, NA, NA, NULL);
addot(OT_BOW, "bow", "A weapon which projects arrows via its elasticity.", MT_WOOD, 3, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_FIRESPEED, 5, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_RANGE, 5, NA, NA, NULL);
addflag(lastot->flags, F_AMMOOB, OT_ARROW, NA, NA, NULL);
addot(OT_LONGBOW, "longbow", "A very large (human-sized) bow, capable of firing arrows with great power.", MT_WOOD, 6, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_FIRESPEED, 7, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_RANGE, 10, NA, NA, NULL);
addflag(lastot->flags, F_AMMOOB, OT_ARROW, NA, NA, NULL);
addot(OT_CROSSBOWHAND, "hand crossbow", "A small one-handed crossbow. Fast to fire but slower than a full-sized one.", MT_WOOD, 3, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_FIRESPEED, 5, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_RANGE, 10, NA, NA, NULL);
addflag(lastot->flags, F_AMMOOB, OT_BOLT, NA, NA, NULL);
addot(OT_REVOLVER, "revolver", "Basic one-handed firearm.", MT_METAL, 1, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 60, NA, NULL);
addflag(lastot->flags, F_FIREARM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL);
//addflag(lastot->flags, F_DAMTYPE, DT_BASH, NA, NA, NULL);
//addflag(lastot->flags, F_DAM, 3, 4, NA, NULL);
addflag(lastot->flags, F_FIRESPEED, 10, NA, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addflag(lastot->flags, F_RANGE, 10, NA, NA, NULL);
addflag(lastot->flags, F_AMMOOB, OT_BULLET, NA, NA, NULL);
addflag(lastot->flags, F_AMMOOB, OT_RUBBERBULLET, NA, NA, NULL);
// holy weapons
addot(OT_HANDOFGOD, "hand of god", "The ultimate power.", MT_FLESH, 0.1, OC_WEAPON);
//addflag(lastot->flags, F_RARITY, H_DUNGEON, RR_UNIQUE, NA, NULL);
//addflag(lastot->flags, F_DAMTYPE, DT_HOLY, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_SLASH, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 6, 100, NULL);
addflag(lastot->flags, F_ACCURACY, 500, NA, NA, NULL);
addflag(lastot->flags, F_UNIQUE, B_TRUE, NA, NA, NULL);
}
// returns the 'armourrating' flag
flag_t *isarmour(object_t *o) {
flag_t *f;
if (hasflag(o->flags, F_GOESON)) {
f = hasflag(o->flags, F_ARMOURRATING);
if (f) {
return f;
}
}
return NULL;
}
int isactivated(object_t *o) {
if (hasflag(o->flags, F_ACTIVATED)) {
return B_TRUE;
}
return B_FALSE;
}
int isammofor(object_t *ammo, object_t *gun) {
if (hasflagval(gun->flags, F_AMMOOB, ammo->type->id, NA, NA, NULL)) {
return B_TRUE;
}
return B_FALSE;
}
// is armour 'a' better than armour 'b'?
int isbetterarmourthan(object_t *a, object_t *b) {
int arma, armb;
flag_t *f;
if (!a) return B_FALSE;
if (!b) return B_TRUE;
f = isarmour(a);
if (f) {
arma = f->val[0];
} else {
arma = 0;
}
f = isarmour(b);
if (f) {
armb = f->val[0];
} else {
armb = 0;
}
if (arma > armb) return B_TRUE;
return B_FALSE;
}
// compare weapons using max damage
int isbetterwepthan(object_t *a, object_t *b) {
flag_t *f;
int dama,damb;
if (!a) return B_FALSE;
if (!b) return B_TRUE;
f = hasflag(a->flags, F_DAM);
if (f) {
dama = (f->val[0] * f->val[1]) + f->val[2];
} else {
dama = 0;
}
f = hasflag(b->flags, F_DAM);
if (f) {
damb = (f->val[0] * f->val[1]) + f->val[2];
} else {
damb = 0;
}
// modify based on extra props
if (hasflag(a->flags, F_HASBRAND)) {
dama *= 3;
}
if (hasflag(b->flags, F_HASBRAND)) {
damb *= 3;
}
if (dama > damb) return B_TRUE;
return B_FALSE;
}
int isblessed(object_t *o) {
if (o->blessed == B_BLESSED) return B_TRUE;
return B_FALSE;
}
int isblessknown(object_t *o) {
if (o->blessknown) return B_TRUE;
if (gamestarted && hasflag(player->flags, F_DETECTAURAS)) {
return B_TRUE;
}
return B_FALSE;
}
int iscorpse(object_t *o) {
if (o->type->obclass->id == OC_CORPSE) {
return B_TRUE;
}
return B_FALSE;
}
int iscursed(object_t *o) {
if (o->blessed == B_CURSED) return B_TRUE;
return B_FALSE;
}
int isdangerousob(object_t *o, lifeform_t *lf, int onlyifknown) {
enum IQBRACKET iqb;
iqb = getiqname(getattr(lf, A_IQ), NULL);
if (!onlyifknown || (iqb >= IQ_AVERAGE)) {
if (hasflag(o->flags, F_SHARP)) {
if (!getequippedob(lf->pack, BP_HANDS) && !isimmuneto(lf->flags, DT_SLASH)) {
return B_TRUE;
}
}
}
if (!onlyifknown || (iqb >= IQ_ANIMAL)) {
if (hasflag(o->flags, F_ONFIRE)) {
if (!isimmuneto(lf->flags, DT_FIRE)) {
return B_TRUE;
}
}
}
return B_FALSE;
}
int isdrinkable(object_t *o) {
switch (o->type->obclass->id) {
case OC_POTION:
return B_TRUE;
default: break;
}
return B_FALSE;
}
int isedible(object_t *o) {
if (hasflag(o->flags, F_EDIBLE)) {
return B_TRUE;
}
if (o->material->id == MT_ICE) {
return B_TRUE;
}
return B_FALSE;
}
flag_t *isequipped(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_EQUIPPED);
if (f) {
return f;
}
return NULL;
}
int isequippedon(object_t *o, enum BODYPART bp) {
if (hasflagval(o->flags, F_EQUIPPED, bp, NA, NA, NULL)) {
return B_TRUE;
}
return B_FALSE;
}
int isfirearm(object_t *o) {
if (hasflag(o->flags, F_FIREARM)) {
return B_TRUE;
}
return B_FALSE;
}
int isflammable(object_t *o) {
if (hasflag(o->flags, F_FLAMMABLE)) {
return B_TRUE;
}
return B_FALSE;
}
int isknown(object_t *o) {
knowledge_t *k;
// if id'd, return the full name
if (hasflag(o->flags, F_IDENTIFIED)) {
return B_TRUE;
}
for (k = knowledge; k ; k = k->next) {
if (k->id == o->type->id) {
// it DOES have a hidden name.
// does the player know about it?
if (k->known) {
return B_TRUE;
} else {
return B_FALSE;
}
}
}
// no hidden name, return real one
return B_TRUE;
}
// is the object fully identified?
// ie. its type is known ("potion of healing" rather than "red potion")
// AND
// you know whether it is cursed or not
int isidentified(object_t *o) {
flag_t *f;
if (!isblessknown(o)) return B_FALSE;
if (!isknown(o)) return B_FALSE;
for (f = o->flags->first ; f ; f = f->next) {
if (!f->known) return B_FALSE;
}
return B_TRUE;
}
int isimpassableob(object_t *o, lifeform_t *lf) {
flag_t *f;
f = hasflag(o->flags, F_IMPASSABLE);
if (f) {
enum LFSIZE lfsize;
enum LFSIZE blocksize;
if (!lf) return B_TRUE;
lfsize = getlfsize(lf);
blocksize = f->val[0];
if (lfsize <= blocksize) {
return B_TRUE;
}
}
return B_FALSE;
}
int ismetal(enum MATERIAL mat) {
int metal = B_FALSE;
switch (mat) {
case MT_METAL:
case MT_GOLD:
metal = B_TRUE;
break;
default:
metal = B_FALSE;
break;
}
return metal;
}
int ismissile(object_t *o) {
if (hasflag(o->flags, F_MISSILE)) {
return B_TRUE;
}
// special cases...
switch (o->type->id) {
case OT_EMPTYFLASK: // (because it will shatter)
case OT_EMPTYVIAL: // (because it will shatter)
case OT_BROKENGLASS: // (because it will cut)
case OT_ICECHUNK: // (because it will cut)
return B_TRUE;
default:
break;
}
return B_FALSE;
}
int isoperable(object_t *o) {
if (hasflag(o->flags, F_OPERABLE)) return B_TRUE;
//if (o->type->obclass->id == OC_TECH) return B_TRUE;
return B_FALSE;
}
// has this object changed proerties from its
// parent objecttype?
int isplainob(object_t *o) {
if (o->material != o->type->material) return B_FALSE;
if (o->weight != o->type->weight) return B_FALSE;
if (o->inscription) return B_FALSE;
if (o->blessed != B_UNCURSED) return B_FALSE;
if (isblessknown(o)) return B_FALSE;
return B_TRUE;
}
int ispourable(object_t *o) {
if (hasflag(o->flags, F_POURABLE)) {
if (o->material->id != MT_ICE) {
return B_TRUE;
}
}
return B_FALSE;
}
int ispushable(object_t *o) {
if (hasflag(o->flags, F_PUSHABLE)) {
return B_TRUE;
}
return B_FALSE;
}
int isreadable(object_t *o) {
switch (o->type->obclass->id) {
case OC_SCROLL:
case OC_BOOK:
return B_TRUE;
default: break;
}
return B_FALSE;
}
int isrotting(object_t *o) {
flag_t *f;
float pct;
if (!iscorpse(o)) return B_FALSE;
f = hasflag(o->flags, F_OBHP);
if (f) {
pct = ((float) f->val[0] / (float) f->val[1]) * 100.0;
} else {
pct = 100;
}
if (pct < 25) {
return B_TRUE;
}
return B_FALSE;
}
int isweapon(object_t *o) {
//if ((o->type->obclass->id == OC_WEAPON) && hasflag(o->flags, F_DAM)) {
if (o->type->obclass->id == OC_WEAPON) {
return B_TRUE;
}
return B_FALSE;
}
int iswearable(object_t *o) {
if (hasflag(o->flags, F_GOESON)) {
return B_TRUE;
}
return B_FALSE;
}
void killmaterial(material_t *m) {
material_t *nextone, *lastone;
// free mem
free(m->name);
killflagpile(m->flags);
// remove from list
nextone = m->next;
if (nextone != NULL) {
nextone->prev = m->prev;
} else { /* last */
lastmaterial = m->prev;
}
if (m->prev == NULL) {
/* first */
nextone = m->next;
free(material);
material = nextone;
} else {
lastone = m->prev;
free (lastone->next );
lastone->next = nextone;
}
}
void killob(object_t *o) {
object_t *nextone, *lastone;
// remove flags conferred by this object
if (o->pile->owner) {
loseobflags(o->pile->owner, o, ALLCONFERRED);
}
// free mem
if (o->inscription) free(o->inscription);
// remove from list
nextone = o->next;
if (nextone != NULL) {
nextone->prev = o->prev;
} else { /* last */
o->pile->last = o->prev;
}
if (o->prev == NULL) {
/* first */
nextone = o->next;
o->pile->first = nextone;
free(o);
} else {
lastone = o->prev;
free (lastone->next );
lastone->next = nextone;
}
}
void killobpile(obpile_t *op) {
while (op->first) {
killob(op->first);
}
free(op);
}
void killoc(objectclass_t *oc) {
objectclass_t *nextone, *lastone;
// free mem
free(oc->name);
free(oc->desc);
if (oc->flags) killflagpile(oc->flags);
// remove from list
nextone = oc->next;
if (nextone != NULL) {
nextone->prev = oc->prev;
} else { /* last */
lastobjectclass = oc->prev;
}
if (oc->prev == NULL) {
/* first */
nextone = oc->next;
free(objectclass);
objectclass = nextone;
} else {
lastone = oc->prev;
free (lastone->next );
lastone->next = nextone;
}
}
void killot(objecttype_t *ot) {
objecttype_t *nextone, *lastone;
// free mem
free(ot->name);
free(ot->desc);
if (ot->flags) killflagpile(ot->flags);
// remove from list
nextone = ot->next;
if (nextone != NULL) {
nextone->prev = ot->prev;
} else { /* last */
lastobjecttype = ot->prev;
}
if (ot->prev == NULL) {
/* first */
nextone = ot->next;
free(objecttype);
objecttype = nextone;
} else {
lastone = ot->prev;
free (lastone->next );
lastone->next = nextone;
}
}
// animate a weapon
lifeform_t *makeanimated(lifeform_t *lf, object_t *o, int level) {
cell_t *where;
flag_t *f;
lifeform_t *newlf;
where = getrandomadjcell(lf->cell, WE_EMPTY);
if (!where) return NULL;
newlf = addlf(where, R_DANCINGWEAPON, level);
if (newlf) {
if (isplayer(lf)) {
addflag(newlf->flags, F_FRIENDLY, B_TRUE, NA, NA, NULL);
} else if (hasflag(lf->flags, F_HOSTILE)) {
addflag(newlf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
}
addflag(lf->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
o = relinkob(o, newlf->pack);
weild(lf, o);
f = hasflag(o->flags, F_OBHP);
if (f) {
newlf->maxhp = f->val[1];
newlf->hp = newlf->maxhp;
}
f = hasflag(o->flags, F_OBATTACKDELAY);
if (f) {
int origspeed;
int newspeed;
origspeed = getmovespeed(newlf);
killflagsofid(newlf->flags, F_MOVESPEED);
newspeed = (int)((float)origspeed * ((float)f->val[0] / 100.0));
addflag(newlf->flags, F_MOVESPEED, newspeed, NA, NA, NULL);
}
}
return newlf;
}
void makeduller(object_t *o, int howmuch) {
char obname[BUFLEN];
int oldbonus,newbonus;
// get object name before changing the bonus
getobname(o,obname, 1);
oldbonus = getobbonus(o);
modbonus(o, -howmuch);
newbonus = getobbonus(o);
if (newbonus < oldbonus) {
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s seems duller!",noprefix(obname));
} else if (haslos(player, o->pile->owner->cell)) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
msg("%s%s %s seems duller!",lfname, getpossessive(lfname), noprefix(obname));
}
} else {
cell_t *obloc;
obloc = getoblocation(o);
if (haslos(player, obloc)) {
msg("%s seems duller!",obname);
}
}
}
}
void makeknown(enum OBTYPE otid) {
knowledge_t *k;
object_t *o;
flag_t *f;
object_t *srcob[MAXPILEOBS*3];
enum FLAG fttogive[MAXPILEOBS*3];
int nobs = 0;
int i;
if (player) {
// if player is holding an object of that type with F_CONFER.. IFKNOWN... and isn't known...
// then by making the object known, we also need to give them the flag.
//
// keep a list of objects and flags here, then give them afterwards.
for (o = player->pack->first ; o ; o = o->next) {
if (o->type->id == otid) {
if (!isknown(o)) {
for (f = o->flags->first ; f ; f = f->next) {
if ((f->id == F_HOLDCONFER) && (f->val[2] == IFKNOWN)) {
srcob[nobs] = o;
fttogive[nobs] = F_HOLDCONFER;
nobs++;
} else if ((f->id == F_EQUIPCONFER) && (f->val[2] == IFKNOWN) && isequipped(o)) {
srcob[nobs] = o;
fttogive[nobs] = F_EQUIPCONFER;
nobs++;
} else if ((f->id == F_ACTIVATECONFER) && (f->val[2] == IFKNOWN) && isactivated(o)) {
srcob[nobs] = o;
fttogive[nobs] = F_ACTIVATECONFER;
nobs++;
}
}
}
}
}
}
for (k = knowledge; k ; k = k->next) {
if (k->id == otid) {
k->known = B_TRUE;
}
}
for (i = 0; i < nobs; i++) {
giveobflags(player, srcob[i], fttogive[i]);
}
}
object_t *moveob(object_t *src, obpile_t *dst, int howmany) {
object_t *o, *existob;
int i;
int db = B_FALSE;
reason = E_OK;
if (db) dblog("DB: moveob() - moving %d x %s",howmany, src->type->name);
existob = canstackob(dst, src);
if (existob) {
if (db) dblog("DB: moveob() - found stack to join");
if (howmany == ALL) howmany = src->amt;
existob->amt += howmany;
src->amt -= howmany;
if (src->amt == 0) {
killob(src);
}
o = existob;
} else {
if (db) dblog("DB: moveob() - no stack to join");
// space in destination pile?
if (getnextletter(dst, NULL) == '\0') {
reason = E_NOSPACE;
return NULL;
}
// no similar objects in the dest pile.
if ((howmany == ALL) || (howmany == src->amt)) {
if (db) dblog("DB: dropping ALL - relinking object to new pile");
// just move the whole object to the new pile
o = relinkob(src, dst);
} else {
obpile_t *tempop;
if (db) dblog("DB: moving only some of a stack.");
// create temporary object pile
tempop = addobpile(NULL, NULL);
// add new object to the temporary pile
o = addob(tempop, src->type->name);
// copy props from original object
copyobprops(o, src);
// just move some of them
for (i = 0; i < howmany; i++ ) {
// the first one is already there!
if (i != 0) {
// inc counter in temp pile
o->amt++;
}
// dec counter on original
src->amt--;
}
if (src->amt <= 0) {
killob(src);
}
// now move the object entry from the temporary pile to the
// real destination pile.
while (tempop->first) {
o = relinkob(tempop->first, dst);
}
free(tempop);
}
}
if (o) {
//if (dst->owner && isplayer(dst->owner) && lfhasflag(dst->owner, F_DETECTAURAS)) {
if (dst->owner && isplayer(dst->owner) && isblessknown(o)) {
o->blessknown = B_TRUE;
}
}
if (dst->where && !dst->where->lf && haslos(player, dst->where)) {
needredraw = B_TRUE;
drawscreen();
}
//o = newobeffects(o);
return o;
}
void modbonus(object_t *o, int amt) {
flag_t *f;
f = hasflag(o->flags, F_BONUS);
if (f) {
f->val[0] += amt;
} else {
cell_t *loc;
int known;
loc = getoblocation(o);
if (haslos(player, loc)) {
known = B_TRUE;
} else {
known = B_FALSE;
}
f = addflag_real(o->flags, F_BONUS, amt, NA, NA, NULL, PERMENANT, known, -1);
}
// enforce limit
limit(&f->val[0], -7, 7);
}
void obaction(object_t *o, char *text) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s %s!", noprefix(obname), text);
} else if (haslos(player, o->pile->owner->cell)) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
msg("%s%s %s %s!", lfname, getpossessive(lfname),
noprefix(obname), text);
}
} else if (haslos(player, o->pile->where)) { // on ground
msg("%s %s!", obname, text);
}
}
object_t *obexists(enum OBTYPE obid) {
map_t *m;
int x,y;
object_t *o;
lifeform_t *lf;
// do any objects of this id already exist?
for (m = firstmap ; m ; m = m->next) {
// check cells
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
if (c) {
o = hasob(c->obpile, obid);
if (o) return o;
}
}
}
// check lifeforms
for (lf = m->lf ; lf ; lf = lf->next) {
o = hasob(lf->pack, obid);
if (o) return o;
}
}
return NULL;
}
void obdie(object_t *o) {
char obname[BUFLEN];
flag_t *f;
cell_t *obloc;
obloc = o->pile->where;
f = hasflag(o->flags, F_DIECONVERT);
if (f) {
flag_t *f2;
object_t *newob;
char desc[BUFLEN];
if (!hasflag(o->flags, F_NODIECONVERTTEXT)) {
// announce the change
real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE);
f2 = NULL;
if (o->amt > 1) {
f2 = hasflag(o->flags, F_DIECONVERTTEXTPL);
}
if (!f2) {
f2 = hasflag(o->flags, F_DIECONVERTTEXT);
}
if (f2) {
sprintf(desc, "%s", f2->text);
} else if (oblastdamtype(o) == DT_DECAY) {
sprintf(desc, "%s completed rotted away", (o->amt == 1) ? "has" : "have");
} else {
sprintf(desc, "%s destroyed", (o->amt == 1) ? "is" : "are");
}
if (strstr(o->type->name, "stain") || (o->type->id == OT_ROASTMEAT)) {
assert(0 == 1);
}
if (o->pile->owner) {
if (o->pile->owner->controller == C_PLAYER) {
msg("Your %s %s!",noprefix(obname), desc);
} else if (haslos(player, o->pile->owner->cell)) {
// don't announce decay death unless we are holding it
if (oblastdamtype(o) != DT_DECAY) {
char monname[BUFLEN];
getlfname(o->pile->owner, monname);
msg("%s's %s %s!",monname, noprefix(obname), desc);
}
}
} else if (haslos(player, o->pile->where)) {
// don't announce decay death unless we are holding it
if (oblastdamtype(o) != DT_DECAY) {
capitalise(obname);
msg("%s %s.",obname, desc);
}
}
}
// change into something else
newob = addob(o->pile, f->text);
newob->amt = o->amt;
} else {
char desc[BUFLEN];
real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE);
if (!hasflag(o->flags, F_NOOBDIETEXT)) {
// announce the death
f = hasflag(o->flags, F_DIETEXT);
if (f) {
sprintf(desc, "%s", f->text);
} else if (oblastdamtype(o) == DT_DECAY) {
sprintf(desc, "%s completely rotted away", (o->amt == 1) ? "has" : "have" );
} else {
sprintf(desc, "%s destroyed", (o->amt == 1) ? "is" : "are");
}
if (o->pile->owner) {
if (o->pile->owner->controller == C_PLAYER) {
msg("Your %s %s!",noprefix(obname), desc);
} else if (haslos(player, o->pile->owner->cell)) {
char monname[BUFLEN];
getlfname(o->pile->owner, monname);
msg("%s's %s %s!",monname, noprefix(obname), desc);
}
} else if (haslos(player, o->pile->where)) {
msg("%s %s.",obname, desc);
}
}
// explodes?
f = hasflag(o->flags, F_EXPLODEONDEATH);
if (f) {
if (f->val[2] == B_IFACTIVATED) {
if (isactivated(o)) {
explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0);
}
} else {
explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0);
}
}
// flashes?
f = hasflag(o->flags, F_FLASHONDEATH);
if (f) {
if (f->val[2] == B_IFACTIVATED) {
if (isactivated(o)) {
brightflash(getoblocation(o),f->val[0], NULL);
}
} else {
brightflash(getoblocation(o),f->val[0], NULL);
}
}
// corpse decaying and on the ground?
if ((oblastdamtype(o) == DT_DECAY) && (o->type->id == OT_CORPSE)) {
if (o->pile->where) {
int minbones,maxbones;
char bonestr[BUFLEN];
minbones = o->weight / 10;
maxbones = o->weight / 5;
if (minbones <= 0) minbones = 1;
if (maxbones <= minbones) maxbones = 2;
sprintf(bonestr, "%d-%d bones",minbones,maxbones);
addob(o->pile, bonestr);
}
}
}
killob(o);
// redraw
if (obloc && !obloc->lf && haslos(player, obloc)) {
needredraw = B_TRUE;
drawscreen();
}
}
int obfits(object_t *o, obpile_t *op) {
if (countobs(op) >= MAXPILEOBS) {
return B_FALSE;
}
return B_TRUE;
}
enum DAMTYPE oblastdamtype(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_LASTDAMTYPE);
if (f) {
return f->val[0];
}
return DT_NONE;
}
int brandappliesto(brand_t *om, objecttype_t *ot) {
if (om->bp == BP_WEAPON) {
if (ot->obclass->id == OC_WEAPON) {
return B_TRUE;
}
} else {
// TODO: how do we differentiate shields from guns?
if (hasflagval(ot->flags, F_GOESON, om->bp, NA, NA, NULL)) {
return B_TRUE;
}
}
return B_FALSE;
}
int obmatchescondition(object_t *o, long opts) {
int ok = B_TRUE;
if ((opts & AO_ONLYEQUIPPED) && !hasflag(o->flags, F_EQUIPPED)) {
ok = B_FALSE;
}
if ((opts & AO_EQUIPPEDARMOUR)) {
flag_t *ff;
ff = hasflag(o->flags, F_EQUIPPED);
if (!ff) {
ok = B_FALSE;
} else if (ff->val[0] == BP_WEAPON) {
ok = B_FALSE;
}
}
if ((opts & AO_EDIBLE) && !isedible(o)) ok = B_FALSE;
if ((opts & AO_READABLE) && !isreadable(o)) ok = B_FALSE;
if ((opts & AO_ARMOUR) && (o->type->obclass->id != OC_ARMOUR)) ok = B_FALSE;
if ((opts & AO_WEARABLE) && !iswearable(o)) {
ok = B_FALSE;
}
if ((opts & AO_WEILDABLE) && !isweapon(o)) {
ok = B_FALSE;
}
if ((opts & AO_OPERABLE) && !isoperable(o)) {
ok = B_FALSE;
}
if ((opts & AO_POURABLE) && !ispourable(o)) {
ok = B_FALSE;
}
if ((opts & AO_NOTIDENTIFIED) && isidentified(o)) {
ok = B_FALSE;
}
if ((opts & AO_NOTKNOWN) && isknown(o)) {
ok = B_FALSE;
}
return ok;
}
flag_t *obproduceslight(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_PRODUCESLIGHT);
if (f) {
if (f->val[2] == IFACTIVE) {
if (isactivated(o)) return f;
} else {
return f;
}
}
return NULL;
}
// has this object changed proerties from its
// parent objecttype?
int obpropsmatch(object_t *a, object_t *b) {
if (a->type != b->type) return B_FALSE;
if (a->material != b->material) return B_FALSE;
if (a->weight != b->weight) return B_FALSE;
if (a->inscription || b->inscription) return B_FALSE;
// only really need to check one of them, because at this point
// we know they are of the same type.
if (!hasflag(a->flags, F_NOBLESS) && !hasflag(b->flags, F_NOBLESS)) {
if (a->blessed != b->blessed) return B_FALSE;
if (isblessknown(a) != isblessknown(b)) return B_FALSE;
}
return B_TRUE;
}
int operate(lifeform_t *lf, object_t *o, cell_t *where) {
char buf[BUFLEN],obname[BUFLEN];
int playercansee;
flag_t *f;
int willid = B_FALSE;
getobname(o, obname, 1);
if ((lf->controller == C_PLAYER) || haslos(player, lf->cell)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
// if not a wand, must know what a tool is before you can use it
if (!isknown(o) && (o->type->obclass->id != OC_WAND)) {
if (lf->controller == C_PLAYER) {
msg("You don't know how to use that (yet)!");
}
return B_TRUE;
}
// objects with charges...
if (hasflag(o->flags, F_OPERUSECHARGE)) { // operating toggles on/off
int chargesleft;
chargesleft = getcharges(o);
if (chargesleft > 0) {
chargesleft = usecharge(o);
// TODO: notify if getting low
} else {
if (isplayer(lf)) {
nothinghappens();
// you know it's out
f = hasflag(o->flags, F_CHARGES);
f->known = B_TRUE;
return B_FALSE;
}
}
}
// ask for target, if required
f = hasflag(o->flags, F_OPERNEEDTARGET);
if (f && !where) {
int ttype = TT_NONE;
// don't give hints about the object
if (isknown(o)) {
ttype = f->val[0];
}
if (strlen(f->text) > 0) {
where = askcoords(f->text, ttype);
} else {
sprintf(buf, "Where will you aim %s?",obname);
where = askcoords(buf, ttype);
if (!haslos(lf, where)) {
msg("You can't see there!");
return B_TRUE;
}
}
if (!where) {
// cancel.
msg("Cancelled.");
return B_TRUE;
} else {
if (f->val[1] != NA) {
if ((f->val[1] & TR_NEEDLOS) && !haslos(lf, where)) {
msg("You can't see there!");
return B_TRUE;
}
if ((f->val[1] & TR_NEEDLOF) && !haslof(lf, where)) {
msg("You have no line of fire to there!");
return B_TRUE;
}
}
}
}
f = hasflag(o->flags, F_OPERNEEDDIR);
if (f) {
char ch;
int dir;
// ask direction
ch = askchar(f->text, "yuhjklbn-","-", B_FALSE);
if (ch == '-') {
msg("Cancelled.");
return B_TRUE;
} else {
dir = chartodir(ch);
where = getcellindir(lf->cell, dir);
}
}
touch(lf, o);
if (hasflag(o->flags, F_DEAD)) return B_TRUE;
// TODO: change to be based on item type
// TODO: move this to the end in case 'operate' fails
taketime(lf, getactspeed(lf));
/*
if (lf->controller != C_PLAYER) {
if (haslos(player, lf->cell)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s operates %s.", buf, obname);
}
}
*/
if (hasflag(o->flags, F_OPERONOFF)) { // operating toggles on/off
flag_t *f;
f = hasflag(o->flags, F_ACTIVATED);
if (f) {
turnoff(lf, o);
} else {
turnon(lf, o);
}
} else if (hasflag(o->flags, F_FIREARM)) { // special case - change ammo
object_t *oo;
// construct list of possible ammo
clearretobs();
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (isammofor(oo, o)) {
retobs[nretobs] = oo;
nretobs++;
}
}
if (nretobs <= 0) {
sprintf(buf, "You have no ammo for your %s!",noprefix(obname));
msg(buf);
return B_TRUE;
} else {
sprintf(buf, "Load %s with what ammo",obname);
oo = askobject(lf->pack, buf, NULL, AO_SPECIFIED);
if (oo) {
setammo(lf, oo);
}
}
} else if (o->type->obclass->id == OC_WAND) {
if (!isplayer(lf) && haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s zaps %s.",lfname,obname);
}
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
int isblessed;
isblessed = o->blessed;
// certain wands always used the blessed version of spells
switch (o->type->id) {
case OT_WAND_LIGHT:
isblessed = B_TRUE;
break;
default:
break;
}
dospelleffects(lf, f->val[0], where ? where->lf : NULL, NULL, where, isblessed, &willid);
// special wands
} else if (o->type->id == OT_WAND_WONDER) {
// random effect
switch (rnd(0,6)) {
case 0: // slow
dospelleffects(lf, OT_S_SLOW, where ? where->lf : NULL, NULL, where, o->blessed, &willid);
break;
case 1: // gas
dospelleffects(lf, OT_S_CLOUDKILL, where ? where->lf : NULL, NULL, where, o->blessed, &willid);
break;
case 2: // butterflies around user
willid = B_TRUE;
where = getrandomadjcell(lf->cell, WE_NOTSOLID);
addmonster(where, R_BUTTERFLY, B_FALSE, rnd(10,20));
if (haslos(player, where)) {
msg("A swarm of butterflies appears!");
}
break;
case 3: // light
dospelleffects(lf, OT_S_LIGHT, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid);
break;
case 4: // dark
dospelleffects(lf, OT_S_DARKNESS, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid);
break;
case 5: // stone
dospelleffects(lf, OT_S_PETRIFY, where ? where->lf : NULL, NULL, where, o->blessed, &willid);
break;
case 6: // fireball
dospelleffects(lf, OT_S_FIREBALL, where ? where->lf : NULL, NULL, where, o->blessed, &willid);
break;
//oooooooooo
}
}
if (!isknown(o) && willid) {
if (isplayer(lf) || haslos(player, lf->cell)) {
// tell player
makeknown(o->type->id);
if (isplayer(lf)) {
getobname(o, obname, 1);
msg("This is %s!",obname);
}
}
}
// FROM HERE ON ARE INDIVIDUAL ones
} else if (o->type->id == OT_INSECTICIDE) {
int seen = B_FALSE;
// if above half charges, big spray
f = hasflag(o->flags, F_CHARGES);
if (f->val[0] >= (f->val[1] / 2)) {
// big spray
int dir;
cell_t *c;
for (dir = DC_N; dir <= DC_W; dir += 2) {
c = getcellindir(where, dir);
if (c && cellwalkable(NULL, c, NULL)) {
o = addob(c->obpile, "puff of gas");
}
}
// big spray
addob(where->obpile, "cloud of gas");
if (isplayer(lf)) {
msg("Psssssssst!");
seen = B_TRUE;
} else if (haslos(player, lf->cell)) {
getlfname(lf, buf);
msg("%s%s %s emits a spray of gas.", buf, getpossessive(buf), noprefix(obname));
seen = B_TRUE;
}
} else {
// little spray
addob(where->obpile, "puff of gas");
if (isplayer(lf)) {
msg("Psst!");
seen = B_TRUE;
} else if (haslos(player, lf->cell)) {
getlfname(lf, buf);
msg("%s%s %s emits a puff of gas.", buf, getpossessive(buf), noprefix(obname));
seen = B_TRUE;
}
}
// announce
if (!seen) {
youhear(where, "something spraying.");
}
} else if (o->type->id == OT_POCKETWATCH) {
if (lf->controller == C_PLAYER) {
// TODO: if above ground, use wantpm (ie. can see the sun)
gettimetextfuzzy(buf, B_FALSE);
msg("It is currently %s.",buf);
} else if (haslos(player, lf->cell)) {
getlfname(lf, buf);
msg("%s looks at %s.",buf, obname);
}
} else if (o->type->id == OT_DIGITALWATCH) {
if (lf->controller == C_PLAYER) {
gettimetext(buf);
msg("It is currently %s.",buf);
} else if (haslos(player, lf->cell)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s looks at %s.",buf, obname);
}
} else if ((o->type->id == OT_LOCKPICK)
|| (o->type->id == OT_PAPERCLIP)
|| (o->type->id == OT_CREDITCARD)) {
char ch;
int dir;
// ask direction
ch = askchar("Lockpick in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE);
dir = chartodir(ch);
if (dir == D_NONE) {
clearmsg();
return B_TRUE;
} else {
cell_t *c;
c = getcellindir(player->cell, dir);
if (c) {
object_t *targ;
// something to lockpick there?
targ = hasobwithflag(c->obpile, F_LOCKED);
if (targ) {
lockpick(player, targ, o);
} else {
// fail
msg("There is nothing locked there!");
}
} else {
clearmsg();
return B_TRUE;
}
}
} else if (o->type->id == OT_LOCKHACKER) {
char ch;
int dir;
// ask direction
ch = askchar("Manipulate lock in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE);
dir = chartodir(ch);
if (dir == D_NONE) {
clearmsg();
return B_TRUE;
} else {
cell_t *c;
c = getcellindir(lf->cell, dir);
if (c) {
object_t *oo,*nextoo;
// lock/unlock everything there
for (oo = c->obpile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
if (hasflag(oo->flags, F_LOCKABLE)) {
flag_t *f;
f = hasflag(oo->flags, F_LOCKED);
if (f) {
killflag(f);
if (isplayer(lf)) {
msg("Your %s beeps.", noprefix(obname));
} else if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s beeps.", lfname, getpossessive(lfname),
noprefix(obname));
}
} else {
addflag(oo->flags, F_LOCKED, B_TRUE, NA, NA, NULL);
if (isplayer(lf)) {
msg("Your %s buzzes.", noprefix(obname));
} else if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s buzzes.", lfname, getpossessive(lfname),
noprefix(obname));
}
}
}
}
} else {
clearmsg();
return B_TRUE;
}
}
} else if (o->type->id == OT_PICKAXE) {
int ch,dir;
cell_t *c;
ch = askchar("Dig in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE);
dir = chartodir(ch);
c = getcellindir(player->cell, dir);
if (!c) {
// cancel
clearmsg();
return B_TRUE;
}
if (c->type->solid) {
if (isdiggable(c)) {
if (isplayer(lf)) {
msg("You dig through the wall.");
} else if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s digs through a wall.",lfname);
}
// replace wall
setcelltype(c, getemptycelltype(c->map->habitat));
// redraw screen
needredraw = B_TRUE;
drawscreen();
// takes extra time
taketime(lf, getactspeed(lf)*9);
} else {
// fail
if (isplayer(lf)) {
msg("This wall is too hard to dig.");
}
}
} else { // not solid
int failed = B_FALSE;
object_t *door;
door = hasobwithflag(c->obpile, F_DOOR);
if (door) {
int dooropen;
// only closed doors!
isdoor(door, &dooropen);
if (dooropen) {
door = NULL;
}
}
if (door) {
// TODO: metal doors are immune to CHOP damage
if (!isimmuneto(door->flags, DT_CHOP)) {
taketime(lf, getactspeed(lf));
if (isplayer(lf)) {
msg("You smash open a door!");
} else if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s smashes open a door.",lfname);
}
removeob(door, door->amt);
needredraw = B_TRUE;
drawscreen();
failed = B_FALSE;
}
} else if (hasob(c->obpile, OT_STATUE)) {
int dam;
object_t *so;
char statname[BUFLEN];
so = hasob(c->obpile, OT_STATUE);
getobname(so, statname, so->amt);
taketime(lf, getactspeed(lf));
// statue takes 1/2 damage
f = hasflag(so->flags, F_OBHP);
if (f) {
dam = (f->val[1] / 2); // ie. half max hp
} else {
dam = 1;
}
// statue ?
if (isplayer(lf)) {
msg("You hit %s with your %s.", statname, noprefix(obname));
} else if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s hits %s with %s.", lfname, statname, obname);
}
takedamage(so, dam, DT_CHOP);
} else {
if (isplayer(lf)) {
msg("You swing your %s through the air.",noprefix(obname));
}
taketime(lf, getactspeed(lf));
}
}
} else if (o->type->id == OT_SPANNER) {
int donesomething = B_FALSE;
if (!where) {
if (isplayer(lf)) msg("There is nothing to use your spanner on there!");
} else {
object_t *o;
char ch;
flag_t *f;
for (o = where->obpile->first ; o ; o = o->next) {
int isopen;
if (isdoor(o, &isopen)) {
if (!isopen) { // ie. if closed.
f = hasflag(o->flags, F_JAMMED);
if (f) {
ch = askchar("The hinges seem jammed. Loosen them", "yn", "y", B_TRUE);
if (ch == 'y') {
char obname[BUFLEN];
getobname(o, obname, 1);
msg("You loosen the hinges on %s.", obname);
killflag(f);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
}
} else {
char obname[BUFLEN];
getobname(o, obname, 1);
sprintf(buf, "Tighten the hinges on %s",obname);
ch = askchar(buf, "yn", "y", B_TRUE);
if (ch == 'y') {
msg("You tighten the hinges on %s.", obname);
addflag(o->flags, F_JAMMED, rnd(1,5), NA, NA, NULL);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
}
}
}
}
}
}
if (!donesomething) {
if (isplayer(lf)) msg("There is nothing to use your spanner on there!");
}
} else if (o->type->id == OT_TELEPAD) {
map_t *m;
cell_t *c,*dst = NULL;
int x,y;
int closest = 99999;
m = lf->cell->map;
// find closest pad
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
c = getcellat(m, x, y);
if (hasob(c->obpile, OT_TELEPAD)) {
int thisdist;
thisdist = getcelldist(lf->cell, c);
if (thisdist < closest) {
closest = thisdist;
dst = c;
}
}
}
}
if (dst) {
// teleport there
teleportto(lf, dst);
} else {
// nothing happens
if (isplayer(lf)) {
nothinghappens();
}
}
}
return B_FALSE;
}
int pilehasletter(obpile_t *op, char let) {
object_t *o;
int found = B_FALSE;
for (o = op->first ; o ; o = o->next) {
if (o->letter == let) {
found = B_TRUE;
break;
}
}
return found;
}
int pour(lifeform_t *lf, object_t *o) {
char buf[BUFLEN],obname[BUFLEN],lfname[BUFLEN],dstname[BUFLEN];
int playercansee;
char ch;
object_t *dst;
getobname(o, obname, 1);
getlfname(lf, lfname);
if (isplayer(lf) || haslos(player, lf->cell)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
// tip onto what?
sprintf(buf, "Pour %s onto the ground", obname);
ch = askchar(buf, "yn", "y", B_TRUE);
if (ch == 'y') {
dst = NULL;
} else {
sprintf(buf, "Pour %s onto what", obname);
// tip onto another object
dst = askobject(lf->pack, buf, NULL, AO_NONE);
if (!dst) {
msg("Cancelled.");
return B_TRUE;
} else if (dst->type->obclass->id == OC_POTION) {
getobname(dst, buf, dst->amt);
msg("%s can't hold any more liquid!",buf);
return B_TRUE;
}
}
taketime(lf, SPEED_MOVE);
if (dst) {
flag_t *refillflag;
// pour 'o' onto 'dst'
getobname(dst, dstname, dst->amt);
// specal cases first...
refillflag = hasflag(dst->flags, F_REFILLWITH);
if (refillflag) {
flag_t *f;
if (refillflag->val[0] == o->type->id) {
// refill destination
f = hasflag(dst->flags, F_CHARGES);
if (f) {
f->val[0] = f->val[1];
if (isplayer(lf)) {
msg("You refill your %s.", noprefix(dstname));
// we now know what the potion was
if (!isknown(o)) makeknown(o->type->id);
} else if (haslos(player, lf->cell)) {
msg("%s refills %s with %s.", lfname, dstname, obname);
// we now know what the potion was
if (!isknown(o)) makeknown(o->type->id);
}
}
} else {
// nothing happens
if (isplayer(lf)) {
msg("You refill your %s with %s. It doesn't seem to do much.", obname);
} else if (haslos(player, lf->cell)) {
msg("%s refills %s with %s.", lfname, dstname, obname);
}
}
} else if ((o->type->id == OT_POT_WATER) && (o->blessed == B_BLESSED)) { // holy water!
if (isplayer(lf)) {
msg("You pour %s onto %s.", obname,dstname);
}
o->blessknown = B_TRUE;
// bless whatever we poured onto
blessob(dst);
// we now know that this is holy water
if (!isknown(o)) makeknown(o->type->id);
} else if (o->type->id == OT_POT_INVULN) {
flag_t *f;
f = hasflag(dst->flags, F_DAMAGABLE);
if (f) {
if (isplayer(lf)) {
msg("Your %s looks invulnerable!",noprefix(dstname));
if (!isknown(o)) makeknown(o->type->id);
appendinscription(dst, "invuln");
}
killflag(f);
}
} else if (o->type->id == OT_POT_RESTORATION) {
flag_t *f, *nextf;
if (isplayer(lf)) {
msg("Your %s looks as good as new!",noprefix(dstname));
}
// restore orig material
if (dst->material != dst->type->material) {
changemat(dst, dst->type->material->id);
}
// remove blessings/curses
setblessed(dst, B_UNCURSED);
dst->blessknown = B_TRUE;
// remove bonuses
killflagsofid(dst->flags, F_BONUS);
// remove temporary flags
for (f = dst->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->lifetime > 0) {
killflag(f);
}
}
if (!isknown(dst)) makeknown(dst->type->id);
} else { // default
if (isplayer(lf)) {
msg("You pour %s onto %s.", obname,dstname);
msg("%s gets wet.", dstname);
}
takedamage(dst, 0, DT_WATER);
}
} else {
// pour onto ground
if (isplayer(lf)) {
msg("You pour %s onto the ground.", obname);
} else if (haslof(player, lf->cell)) {
msg("%s pours %s onto the ground.", lfname, obname);
}
addob(lf->cell->obpile, "small puddle of water");
}
// empty the flask
addemptyob(lf->pack, o);
// remove src
removeob(o, 1);
return B_FALSE;
}
void quaff(lifeform_t *lf, object_t *o) {
char buf[BUFLEN],obname[BUFLEN];
int willid = B_FALSE;
int playercansee;
int forcedrop = B_FALSE;
int seen;
getobname(o, obname, 1);
if ((lf->controller == C_PLAYER) || haslos(player, lf->cell)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
taketime(lf, getactspeed(lf));
touch(lf, o);
if (hasflag(o->flags, F_DEAD)) return;
if (o->material->id == MT_ICE) {
if (lf->controller == C_PLAYER) {
msg("You can't drink this, it's frozen!");
}
return;
}
if (lf->controller != C_PLAYER) {
if (haslos(player, lf->cell)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s drinks %s!", buf, obname);
}
}
// figure out whether to id the object
willid = B_FALSE;
if (playercansee) {
switch (o->type->id) {
case OT_POT_HEALING:
case OT_POT_HEALINGMIN:
case OT_POT_RESTORATION:
if (lf->hp < lf->maxhp) {
willid = B_TRUE;
}
break;
default:
willid = B_TRUE;
break;
}
}
if (!isknown(o) && willid) {
// id the potion
makeknown(o->type->id);
real_getobname(o, obname, 1, B_FALSE, B_TRUE, B_FALSE, B_FALSE); // don't adjust for blindness
if (isplayer(lf)) {
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
}
// handle special cases where the quaff effect
// is different to the throw effect.
if (o->type->id == OT_POT_WATER) {
switch (o->blessed) {
case B_BLESSED:
if (hasflag(lf->flags, F_UNDEAD)) {
if (isplayer(lf)) {
msg("This tastes like water, but burns like acid!");
} else if (haslos(player, lf->cell)) {
getlfname(lf, buf);
msg("%s writhes in agony!", buf);
}
losehp(lf, rnd(5,15), DT_HOLY, NULL, "drinking holy water");
// we now know that it is blessed
o->blessknown = B_TRUE;
} else {
msg("Mmm, holy water.");
// we now know that it is blessed
o->blessknown = B_TRUE;
}
break;
case B_UNCURSED:
msg("Mmm, water.");
break;
case B_CURSED:
msg("Yuck! Something is wrong with this water.");
break;
}
if (isplayer(lf) || haslos(player, lf->cell)) {
seen = B_TRUE;
}
modhunger(lf, -pctof(0.05, (float)HUNGERCONST));
} else if (o->type->id == OT_POT_JUICE) {
if (isplayer(lf)) {
switch (o->blessed) {
case B_BLESSED:
msg("Mmm, fruit juice!");
break;
case B_UNCURSED:
msg("Mmm, fruit juice!");
break;
case B_CURSED:
msg("Yuck! This tastes like rotten fruit.");
break;
}
seen = B_TRUE;
}
if (o->blessed != B_CURSED) {
modhunger(lf, -pctof(10, (float)HUNGERCONST));
}
} else {
potioneffects(lf, o->type->id, o->blessed, &seen);
}
if (seen) {
o->blessknown = B_TRUE;
makeknown(o->type->id);
}
if (forcedrop) {
// try to add an empty container to the ground
addemptyob(lf->cell->obpile, o);
} else {
// try to add an empty container to our pack
addemptyob(lf->pack, o);
}
// lose the potion
removeob(o, 1);
}
void potioneffects(lifeform_t *lf, enum OBTYPE oid, enum BLESSTYPE isblessed, int *seen) {
char buf[BUFLEN];
unsigned int dam;
int i;
int first,dir;
int amt;
int failed;
int seenbyplayer;
if (isplayer(lf)) {
seenbyplayer = B_TRUE;
} else if (haslos(player, lf->cell)) {
seenbyplayer = B_TRUE;
} else {
seenbyplayer = B_FALSE;
}
if (seen) {
*seen = seenbyplayer;
}
switch (oid) {
case OT_POT_ACID:
dam = rnd(5,10);
losehp(lf, dam, DT_ACID, NULL, "drinking acid");
if (lf->controller == C_PLAYER) {
msg("Your suffer massive internal burning!");
} else if (haslos(player, lf->cell)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s writhes in agony!", buf);
}
break;
case OT_POT_ACROBATICS:
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel momentarily jumpy.");
}
break;
}
// how long for?
i = geteffecttime(5,10, isblessed);
if (lfhasflagval(lf, F_CANWILL, OT_A_JUMP, NA, NA, NULL)) {
nothinghappens();
seenbyplayer = B_FALSE;
if (seen) *seen = B_FALSE;
} else {
addtempflag(lf->flags, F_CANWILL, OT_A_JUMP, NA, NA, NULL, i);
}
break;
case OT_POT_AMBROSIA:
// fix hunger
i = gethungerlevel(gethungerval(lf));
if (i > 0) {
modhunger(lf, -i);
}
// fix hp
if ((lf->hp < lf->maxhp) && !hasmr(lf)) {
gainhp(lf, lf->maxhp); // ie. full hp
if (lf->controller == C_PLAYER) {
msg("You feel completely healed!");
} else if (haslos(player, lf->cell)) {
getlfname(lf, buf);
msg("%s looks completely healed!", buf);
}
} else {
if (isplayer(lf)) {
msg("This makes you feel incredible!");
}
}
break;
case OT_POT_COMPETENCE:
failed = B_TRUE;
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel momentarily %s.", (isblessed == B_CURSED) ? "incompetent" : "more competent");
}
break;
}
if (isblessed == B_CURSED) {
amt = -1;
} else {
amt = 1;
}
// select a random attribute
if (isblessed == B_BLESSED) {
// modify all attributes
if (!modattr(lf, A_STR, amt)) failed = B_FALSE;
if (!modattr(lf, A_DEX, amt)) failed = B_FALSE;
if (!modattr(lf, A_IQ, amt)) failed = B_FALSE;
} else {
// modify just one attribute
i = rnd(1,3);
switch (i) {
case 1:
if (!modattr(lf, A_STR, amt)) failed = B_FALSE;
break;
case 2:
if (!modattr(lf, A_DEX, amt)) failed = B_FALSE;
break;
case 3:
if (!modattr(lf, A_IQ, amt)) failed = B_FALSE;
break;
}
}
if (failed) {
if (isplayer(lf)) {
nothinghappens();
}
if (seen) *seen = B_FALSE;
} else {
if (seen) *seen = B_TRUE;
}
break;
case OT_POT_ELEMENTENDURE:
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel momentarily resistant to the elements.");
}
break;
}
// how long for?
i = geteffecttime(15,25,isblessed);
if (!lfhasflagval(lf, F_DTRESIST, DT_FIRE, NA, NA, NULL)) {
addtempflag(lf->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL, i);
}
if (!lfhasflagval(lf, F_DTRESIST, DT_COLD, NA, NA, NULL)) {
addtempflag(lf->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, i);
}
break;
case OT_POT_ELEMENTIMMUNE:
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel momentarily immune to the elements.");
}
break;
}
// how long for?
i = geteffecttime(15,25,isblessed);
if (!isimmuneto(lf->flags, DT_FIRE)) {
addtempflag(lf->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL, i);
}
if (!isimmuneto(lf->flags, DT_COLD)) {
addtempflag(lf->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL, i);
}
break;
case OT_POT_ETHEREALNESS:
dospelleffects(lf, OT_S_PASSWALL, lf, NULL, NULL, isblessed, seen);
break;
case OT_POT_EXPERIENCE:
// gain xp!
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel momentarily more experienced.");
}
break;
}
if (isplayer(lf)) {
msg("You feel more experienced!");
}
gainxp(lf, getxpforlev(lf->level+1) - lf->xp);
break;
case OT_POT_GASEOUSFORM:
dospelleffects(lf, OT_S_GASEOUSFORM, lf, NULL, NULL, isblessed, seen);
break;
case OT_POT_HEALING:
dospelleffects(lf, OT_S_HEALING, lf, NULL, NULL, isblessed, seen);
break;
case OT_POT_HEALINGMIN:
dospelleffects(lf, OT_S_HEALINGMIN, lf, NULL, NULL, isblessed, seen);
break;
case OT_POT_INVULN:
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel momentarily invulnerable.");
}
break;
}
i = geteffecttime(5,15,isblessed);
if (!lfhasflagval(lf, F_INVULNERABLE, B_TRUE, NA, NA, NULL)) {
addtempflag(lf->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL, i);
}
break;
case OT_POT_MAGIC:
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel your magical energy temporarily pulse.");
}
break;
}
if (lf->maxmp > 0) {
msg("Your magical energy is restored.");
lf->mp = lf->maxmp;
} else {
nothinghappens();
if (seen) *seen = B_FALSE;
}
break;
case OT_POT_OIL:
if (isplayer(lf)) {
char buf[BUFLEN];
switch (rnd(1,5)) {
case 1: strcpy(buf, "cooking"); break;
case 2: strcpy(buf, "olive"); break;
case 3: strcpy(buf, "peanut"); break;
case 4: strcpy(buf, "grapefruit"); break;
case 5: strcpy(buf, "vegetable"); break;
}
msg("Mmm, %s oil.", buf);
}
break;
case OT_POT_POLYMORPH:
if (isblessed == B_BLESSED) {
// controlled polymorph
dospelleffects(lf, OT_S_POLYMORPH, lf, NULL, NULL, isblessed, NULL);
} else {
// random polymorph
dospelleffects(lf, OT_S_POLYMORPHRND, lf, NULL, NULL, isblessed, NULL);
}
break;
case OT_POT_RESTORATION:
failed = B_TRUE;
if (hasmr(lf)) {
if (isplayer(lf)) {
msg("You feel momentarily restored.");
}
break;
}
if (getattr(lf,A_STR) < lf->baseatt[A_STR]) {
setattr(lf, A_STR, lf->baseatt[A_STR]);
if (isplayer(lf)) msg("Your strength is restored!");
failed = B_FALSE;
}
if (getattr(lf,A_DEX) < lf->baseatt[A_DEX]) {
setattr(lf, A_DEX, lf->baseatt[A_DEX]);
if (isplayer(lf)) msg("Your dexterity is restored!");
failed = B_FALSE;
}
if (getattr(lf,A_IQ) < lf->baseatt[A_IQ]) {
setattr(lf, A_IQ, lf->baseatt[A_IQ]);
if (isplayer(lf)) msg("Your intelligence is restored!");
failed = B_FALSE;
}
if (failed) {
if (isplayer(lf)) {
msg("You feel momentarily restored.");
}
}
break;
case OT_POT_SANCTUARY:
// how long for?
//i = gethealtime(lf); // TODO: change...
if (hasmr(lf)) {
if (isplayer(lf)) {
nothinghappens();
}
break;
}
i = 15;
first = B_TRUE;
// add holy barriers all around
for (dir = DC_N; dir <= DC_NW; dir++) {
cell_t *c;
c = getcellindir(lf->cell, dir);
if (c && isempty(c)) {
object_t *newob;
newob = addob(c->obpile, "magical barrier");
addflag(newob->flags, F_OBHP, i, i, NA, NULL);
if (first && haslos(player, c)) {
msg("A magical barrier appears!");
first = B_FALSE;
}
}
}
break;
case OT_POT_SPEED:
if (isblessed == B_BLESSED) {
dospelleffects(lf, OT_S_HASTE, lf, NULL, lf->cell, B_BLESSED, NULL);
} else if (isblessed == B_CURSED) {
dospelleffects(lf, OT_S_SLOW, lf, NULL, lf->cell, B_UNCURSED, NULL);
} else { // uncursed
dospelleffects(lf, OT_S_HASTE, lf, NULL, lf->cell, B_UNCURSED, NULL);
}
if (seen) *seen = B_TRUE;
break;
case OT_POT_WATER:
// this should never happen
msg("DB: OT_POT_WATER triggered in poteffects.");
break;
default: // nothing happens
break;
}
}
int readsomething(lifeform_t *lf, object_t *o) {
flag_t *f;
char buf[BUFLEN],obname[BUFLEN];
int playercansee;
int willid = B_FALSE;
int needsob = B_FALSE;
objecttype_t *linkspell;
int readtime;
if (isblind(lf)) {
return B_TRUE;
}
getobname(o, obname, 1);
if ((lf->controller == C_PLAYER) || haslos(player, lf->cell)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
if (o->type->obclass->id == OC_BOOK) {
readtime = SPEED_READ * 2;
} else {
readtime = SPEED_READ;
}
taketime(lf, readtime);
// some checks first...
touch(lf, o);
if (hasflag(o->flags, F_DEAD)) {
return B_TRUE;
}
if (lf->controller != C_PLAYER) {
if (haslos(player, lf->cell)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s reads %s!", buf, obname);
}
}
// find linked spell
linkspell = getlinkspell(o);
// figure out whether to id the object
willid = B_FALSE;
if (playercansee) {
if (o->type->obclass->id == OC_BOOK) {
// is this a spellbook?
if (linkspell) {
// if so, only id if we are able to read it
if (getiqreq(linkspell->id) <= getattr(lf, A_IQ)) {
willid = B_TRUE;
} else {
willid = B_FALSE;
}
} else {
willid = B_TRUE;
}
} else {
switch (o->type->id) {
case OT_SCR_IDENTIFY: // only id if it does something
case OT_SCR_ENCHANT: // only id if it does something
case OT_SCR_CREATEMONSTER: // only id if it does something
case OT_SCR_REMOVECURSE: // only id if it does something
willid = B_FALSE;
break;
default:
willid = B_TRUE;
break;
}
}
}
// ask for a target object of any type if scroll is unknown?
switch (o->type->id) {
case OT_SCR_IDENTIFY:
case OT_SCR_ENCHANT:
needsob = B_TRUE;
break;
default:
needsob = B_FALSE;
break;
}
if (!isknown(o) && willid) {
// id the scroll
makeknown(o->type->id);
real_getobname(o, obname, 1, B_FALSE, B_TRUE, B_FALSE, B_FALSE); // don't adjust for blindness
if (lf->controller == C_PLAYER) {
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
}
if (o->type->obclass->id == OC_SCROLL) { // cast linked spell
object_t *targob = NULL;
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
int seen = B_FALSE;
// for unidentified scrolls which target an object,
// let player select ANY object (even if it won't
// work).
if (needsob && isplayer(lf) && !isknown(o)) {
targob = askobject(lf->pack, "Target which object", NULL, AO_NONE);
}
// TODO: castspell the spell with blessed status
dospelleffects(lf, f->val[0], NULL, targob, NULL, o->blessed, &seen);
if (seen) {
// id the scroll now
makeknown(o->type->id);
}
// removeob one of the object
if (isplayer(lf)) msg("The scroll crumbles to dust.");
removeob(o, 1);
} else if (o->type->id == OT_MAP) {
if (lf->controller == C_PLAYER) {
msg("You study your map for a while.");
}
} else if (o->type->id == OT_SCR_NOTHING) {
if (isplayer(lf)) {
msg("The scroll crumbles to dust.");
}
// removeob one of the object
removeob(o, 1);
} else if (o->type->id == OT_SCR_REMOVECURSE) {
int seen = B_FALSE;
object_t *oo;
// remove curses!
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (iscursed(oo)) {
if (o->blessed || isequipped(oo)) {
// announce
if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
char obname[BUFLEN];
getlfname(lf, lfname);
getobname(oo, obname,oo->amt);
msg("%s%s %s %s softly.",lfname,getpossessive(lfname),noprefix(obname),
(oo->amt == 1) ? "glows" : "glow");
seen = B_TRUE;
}
// uncurse it
oo->blessed = B_UNCURSED;
oo->blessknown = B_TRUE;
}
}
}
if (seen) {
// id the scroll now
makeknown(o->type->id);
} else {
if (isplayer(lf)) {
nothinghappens();
}
}
if (isplayer(lf)) {
msg("The scroll crumbles to dust.");
}
// removeob one of the object
removeob(o, 1);
}
} else if (o->type->obclass->id == OC_BOOK) {
// is this a spellbook?
if (linkspell) {
// if so, can we read it?
if (getiqreq(linkspell->id) <= getattr(lf, A_IQ)) {
if (lfhasflagval(lf, F_CANCAST, linkspell->id, NA, NA, NULL)) {
if (isplayer(lf)) msg("You already know how to cast this spell!");
} else {
// learn it
addflag(lf->flags, F_CANCAST, linkspell->id, NA, NA, NULL);
}
} else {
if (isplayer(lf)) msg("This book is a bit too advanced for you.");
}
} else {
// TODO: manuals etc?
}
}
return B_FALSE;
}
object_t *relinkob(object_t *src, obpile_t *dst) {
flag_t *f,*nextf;
if (!obfits(src, dst)) return NULL;
if (src->pile->owner) {
// previous owner loses all flags conferred by this object
loseobflags(src->pile->owner, src, ALLCONFERRED);
}
// unweild
for (f = src->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->id == F_EQUIPPED) {
killflag(f);
} else if (f->id == F_CURAMMO) {
killflag(f);
}
}
// adjust letter...
// gold should always have letter '$'
if (src->type->obclass->id == OC_MONEY) {
src->letter = '$';
}
// adjust letter if going to player?
if (dst->owner && (dst->owner->controller == C_PLAYER)) {
src->letter = getnextletter(dst, &src->letter);
}
// unlink this object from the current list
if (src->prev == NULL) {
// first
src->pile->first = src->next;
} else {
// not first
src->prev->next = src->next;
}
if (src->next == NULL) {
// last
src->pile->last = src->prev;
} else {
// not last
src->next->prev = src->prev;
}
// add this object to the end of the list for the new pile
if (dst->first == NULL) {
// first element in new list
dst->first = src;
src->prev = NULL;
} else {
object_t *aa;
// go to end of list
aa = dst->last;
// not first in new list
src->prev = aa;
aa->next = src;
}
dst->last = src;
src->pile = dst;
src->next = NULL;
if (src->pile->owner) {
// new owner gains "hold confer" flags conferred by this object
giveobflags(src->pile->owner, src, F_HOLDCONFER);
// new owner gains "activate confer" flags conferred by this object if it's active
if (isactivated(src)) {
giveobflags(src->pile->owner, src, F_ACTIVATECONFER);
}
}
if (obproduceslight(src)) {
calclight((getoblocation(src))->map);
drawscreen();
}
return src;
}
void removedeadobs(obpile_t *op) {
object_t *o, *nexto;
for (o = op->first ; o ; o = nexto) {
nexto = o->next;
if (hasflag(o->flags, F_DEAD)) {
obdie(o);
continue;
}
}
}
// returns the amount left
int removeob(object_t *o,int howmany) {
if (howmany == ALL) {
howmany = o->amt;
}
if (howmany >= o->amt) {
killob(o);
return 0;
} else {
o->amt -= howmany;
}
return o->amt;
}
void setblessed(object_t *o, enum BLESSTYPE wantbless) {
o->blessed = wantbless;
if (wantbless != B_BLESSED) {
flag_t *f,*nextf;
// remove glowing from blessings
for (f = o->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->lifetime == FROMBLESSING) {
killflag(f);
}
}
}
}
void setinscription(object_t *o, char *text) {
if (o->inscription) {
free(o->inscription);
}
o->inscription = strdup(text);
}
object_t *splitob(object_t *o) {
object_t *newob;
// decrease count on original stack temporarily, in case we
// can't place the new object (at our weight limit?).
// doesn't matter if it goes down to zero, as we will put it back up soon.
o->amt--;
// give new object
newob = addobject(o->pile, o->type->name, B_NOSTACK);
// restore count
o->amt++;
if (newob) {
copyobprops(newob, o);
killflagsofid(newob->flags, F_STACKABLE);
// remove old ob
removeob(o, 1);
}
return newob;
}
// returns amount of damage taken
int takedamage(object_t *o, unsigned int howmuch, int damtype) {
char predamname[BUFLEN],postdamname[BUFLEN];
char obname[BUFLEN];
flag_t *hpflag, *f;
int damtaken = 0;
// some checks need to happen before
// making sure the damage will happen.
// for example, even if an object is
// immune to water damage, water will
// still put out fire
// water puts out fire
if (damtype == DT_WATER) {
extinguish(o);
}
// catches on fire?
if (damtype == DT_FIRE) {
if (isflammable(o)) {
ignite(o);
}
}
// damage type converts this into something else?
f = hasflagval(o->flags, F_DTCONVERT, damtype, NA, NA, NULL);
if (f) {
object_t *newob;
newob = addob(o->pile, f->text);
newob->amt = o->amt;
// object dies, but don't announce it
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
return howmuch;
}
adjustdamob(o, &howmuch, damtype);
if (howmuch <= 0) {
return 0;
}
// update lastdamtype
f = hasflag(o->flags, F_LASTDAMTYPE);
if (f) {
f->val[0] = damtype;
} else {
addflag(o->flags, F_LASTDAMTYPE, damtype, NA, NA, NULL);
}
real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE);
getobconditionname(o, predamname);
hpflag = hasflag(o->flags, F_OBHP);
if (hpflag) {
damtaken = MAXOF(hpflag->val[0], howmuch);
// object loses hp
hpflag->val[0] -= howmuch;
}
if (!hpflag || (hpflag->val[0] <= 0)) {
// special cases....
if (damtype == DT_FIRE) {
if (o->material->id == MT_FLESH) { // fire roasts flesh
object_t *meat;
meat = addob(o->pile, "chunk of roast meat");
// purposely don't use getweight!
meat->weight = o->weight;
} else { // fire turns other things to ash
addob(o->pile, "pile of ash");
}
} else if (damtype == DT_BASH) {
if (o->material->id == MT_GLASS) { // bashing damage breaks glass
int nshards;
char buf[BUFLEN];
nshards = getnumshards(o);
sprintf(buf, "%d pieces of broken glass", nshards);
addob(o->pile, buf);
} else if (o->material->id == MT_ICE) { // bashing breaks ice
int nshards;
char buf[BUFLEN];
nshards = getnumshards(o) / 15;
sprintf(buf, "%d chunks of ice", nshards);
addob(o->pile, buf);
}
}
// object dies!
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
} else if (hpflag) {
flag_t *f;
// object was just damaged
getobconditionname(o, postdamname);
// was it enough to change the status
if (!hasflag(o->flags, F_NOOBDAMTEXT) && strcmp(predamname, postdamname)) {
// if it was melting, drop some water here
if (damtype == DT_MELT) {
if (hasflag(o->flags, F_EQUIPPED) ) {
addob(o->pile->owner->cell->obpile, "small puddle of water");
} else {
// melts into the ob pile
addob(o->pile, "small puddle of water");
}
}
if (o->pile->owner) {
if (o->pile->owner->controller == C_PLAYER) {
msg("Your %s %s!",noprefix(obname), getobhurtname(o, damtype));
} else if (haslos(player, o->pile->owner->cell)) {
// don't announce decay damage for object you aren't holding
if (damtype != DT_DECAY) {
char monname[BUFLEN];
getlfname(o->pile->owner, monname);
msg("%s's %s %s!",monname, noprefix(obname), getobhurtname(o, damtype));
}
}
} else if (haslos(player, o->pile->where)) {
// don't announce decay damage for object you aren't holding
if (damtype != DT_DECAY) {
msg("%s %s!", obname, getobhurtname(o, damtype));
}
}
}
// explodes?
f = hasflag(o->flags, F_EXPLODEONDAM);
if (f) {
if (f->val[2] == B_IFACTIVATED) {
if (hasflag(o->flags, F_ACTIVATED)) {
if (hasflag(o->flags, F_EXPLODEONDEATH)) {
// object dies!
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
} else {
// explode
explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0);
}
}
} else {
if (hasflag(o->flags, F_EXPLODEONDEATH)) {
// object dies!
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
} else {
// explode
explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0);
}
}
}
// flashes?
f = hasflag(o->flags, F_FLASHONDAM);
if (f) {
if (f->val[2] == B_IFACTIVATED) {
if (hasflag(o->flags, F_ACTIVATED)) {
// flash, then object dies
brightflash(getoblocation(o),f->val[0], NULL);
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
}
} else {
// flash, then object dies
brightflash(getoblocation(o),f->val[0], NULL);
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
}
}
}
return damtaken;
}
// throw speed is the damage multiplier
int fireat(lifeform_t *thrower, object_t *o, cell_t *where, int speed, object_t *firearm) {
char throwername[BUFLEN];
char throwernamea[BUFLEN];
char realthrowername[BUFLEN];
char realthrowernamea[BUFLEN];
char obname[BUFLEN];
char targetname[BUFLEN];
char buf[BUFLEN];
lifeform_t *target;
cell_t *srcloc;
int seen;
int shattered = B_FALSE;
char throwverbpast[BUFLEN];
char throwverbpres[BUFLEN];
int acc;
int youhit;
int db = B_TRUE;
reason = E_OK;
if (firearm) {
strcpy(throwverbpres, "fire");
strcpy(throwverbpast, "fired");
} else {
strcpy(throwverbpres, "throw");
strcpy(throwverbpast, "thrown");
}
if (isblind(player)) {
if (thrower && isplayer(thrower)) {
getobname(o, obname, 1);
} else {
strcpy(obname, "something");
}
} else {
getobname(o, obname, 1);
}
if (thrower) {
getlfname(thrower, throwername);
real_getlfname(thrower, realthrowername, B_FALSE);
} else {
strcpy(obname, "something");
}
if (thrower) {
if (isplayer(thrower)) {
strcpy(throwernamea, throwername);
} else {
if (isvowel(*(noprefix(throwername)))) {
strcpy(throwernamea, "an ");
} else {
strcpy(throwernamea, "a ");
}
strcat(throwernamea, noprefix(throwername));
if (isvowel(*(noprefix(realthrowername)))) {
strcpy(realthrowernamea, "an ");
} else {
strcpy(realthrowernamea, "a ");
}
strcat(realthrowernamea, noprefix(realthrowername));
}
} else {
strcat(throwernamea, "something");
strcat(realthrowernamea, "something");
}
srcloc = getoblocation(o);
// can't throw weilded cursed objects
if ((o->blessed == B_CURSED) && (!firearm)) {
// is object in thrower's pack? we need to check this
// because of things like telekenises which let us throw
// things we don't have.
if (thrower && (o->pile->owner == thrower)) {
// object equipped?
if (hasflag(o->flags, F_EQUIPPED)) {
// throw will fail!
if (isplayer(thrower)) {
msg("You can't release your %s - it %s cursed!", noprefix(obname),
isblessknown(o) ? "is" : "must be");
o->blessknown = B_TRUE;
} else if (haslos(player, thrower->cell)) {
msg("%s tries to throw %s but can't!", throwername, obname);
o->blessknown = B_TRUE;
}
// take time anyway
//taketime(thrower, SPEED_THROW);
// fail.
reason = E_CURSED;
return B_TRUE;
}
}
// otherwise does player have haslos?
// identify as cursed!
}
if (haslos(player, where)) {
seen = B_TRUE;
} else {
seen = B_FALSE;
}
target = where->lf;
if (target && isdead(target)) {
target = NULL;
}
if (target) {
getlfname(target, targetname);
}
// touch effects
if (thrower && !firearm) {
if (o->pile == thrower->pack) {
touch(thrower, o);
}
}
// announce it ("xx throws xx" "at yy")
if (thrower && isplayer(thrower)) {
if (target) {
msg("You %s %s at %s.", throwverbpres, obname, targetname);
} else {
msg("You %s %s.",throwverbpres, obname);
}
} else if (thrower && haslos(player, srcloc) && (srcloc == thrower->cell)) {
char throwstring[BUFLEN];
sprintf(throwstring, "%s %ss %s", throwername, throwverbpres,
obname);
if (target && haslos(player, where)) {
strcat(throwstring, " at ");
strcat(throwstring, targetname);
}
strcat(throwstring, ".");
msg("%s", throwstring);
} else if (seen) {
char throwstring[BUFLEN];
/*
if (target) {
msg("Something %ss %s at %s.", throwverbpres, obname, targetname);
} else {
msg("Something %ss %s.",throwverbpres, obname);
}
*/
sprintf(throwstring, "%s flies through the air", obname);
if (target && haslos(player, where)) {
strcat(throwstring, " towards ");
strcat(throwstring, targetname);
}
strcat(throwstring, ".");
msg("%s", throwstring);
}
//taketime(thrower, SPEED_THROW);
// some obejcts will die when thrown.
if (o->type->id == OT_ASH) {
if (haslos(player, srcloc)) {
msg("%s disperses into the air.", obname);
}
removeob(o, 1);
return B_FALSE;
}
if (thrower) {
// gravboost?
if (lfhasflag(thrower, F_GRAVBOOSTED) && (srcloc == thrower->cell)) {
if (isplayer(thrower) || haslos(player, srcloc)) {
msg("%s drops and sticks to the ground!", obname);
}
moveob(o, thrower->cell->obpile, 1);
return B_FALSE;
}
}
// do throw animation
if (seen) {
anim(srcloc, where, getglyph(o));
}
// find out your chances of hitting
if (target) {
if (thrower) {
acc = getmissileaccuracy(thrower, where, o, firearm, A_DEX);
} else {
// purely based on saving throw...
acc = 100;
}
// roll for hit
youhit = B_FALSE;
// metal weapon versus magnetic shield?
if (target && lfhasflag(target, F_MAGSHIELD) && ismetal(o->material->id)) {
// announce
if (seen) {
msg("%s is repelled from %s!", obname, targetname);
}
youhit = B_FALSE;
} else if (rnd(1,100) <= acc) {
youhit = B_TRUE;
}
// saving throw
if (youhit) {
if (skillcheck(target, SC_DODGE, 20, 0)) {
youhit = B_FALSE;
}
}
// doesn't matter wheter you hit or ot...
if (lfhasflag(target, F_UNDEAD) && isblessed(o)) {
if (seen) {
msg("%s recoils in fear!", targetname);
}
o->blessknown = B_TRUE;
}
// if someone is there, they take damage and the object might die
// this should be "if target && youhit"
if (youhit) {
int dam = 0,shatterdam = 0;
char damstring[BUFLEN];
int reduceamt = 0;
int throwdam;
throwdam = getthrowdam(o);
dam = throwdam * speed;
if (db) dblog("fireat(): dam = throwdam(%d) * speed(%d) = %d",throwdam, speed, dam);
// special case
if (o->type->id == OT_RUBBERBULLET) {
dam = 1;
}
// deal extra cutting damage afterwards?
shatterdam = getshatterdam(o);
if (shatterdam > 0) {
shattered = B_TRUE;
}
// announce
if (seen) {
char buf2[BUFLEN];
sprintf(buf2, "%s hits %s%s",obname,targetname,
(willshatter(o->material->id)) ? " and shatters!" : "."
);
if (lfhasflag(player, F_EXTRAINFO)) {
char damstring[BUFLEN];
sprintf(damstring, " [%d dmg]",dam);
strcat(buf2, damstring);
}
msg("%s", buf2);
} else {
if (willshatter(o->material->id)) {
youhear(where, "shattering glass.");
}
}
sprintf(damstring, "%s (%s by %s)",obname,throwverbpast, realthrowernamea);
reduceamt = getarmourdamreduction(target, o, dam, DT_PROJECTILE);
applyarmourdamreduction(target, o, reduceamt, &dam, DT_PROJECTILE);
if (dam > 0) {
// TODO: at the moment you won't get experience if you telekenetically
// throw an object at something. is this okay?
losehp(target, dam, DT_PROJECTILE, (thrower->cell == srcloc) ? thrower : NULL, damstring);
}
if (reduceamt && (speed >= 3)) {
applyarmourdamage(target, o, reduceamt, DT_PROJECTILE);
}
if (shatterdam && !isdead(target)) {
// extra glass damage
if (seen) {
msg("%s %s showered in glass shards!", targetname, isplayer(target) ? "are" : "is");
}
sprintf(damstring, "%s (%s by %s)",obname,throwverbpast, realthrowernamea);
losehp(target, shatterdam, DT_SLASH, thrower, damstring);
}
} else {
if (isplayer(thrower)) {
msg("Your %s misses %s.", noprefix(obname), targetname);
} else if (haslos(player, where)) {
msg("%s misses %s.", obname, targetname);
}
}
} else { // no target
if (willshatter(o->material->id)) {
if (haslos(player, where)) {
char *obcaps;
obcaps = strdup(obname);
capitalise(obcaps);
obcaps = strrep(obcaps, "An ", "The ", NULL);
obcaps = strrep(obcaps, "A ", "The ", NULL);
msg("%s shatters!",obcaps);
free(obcaps);
} else {
youhear(where, "shattering glass.");
}
shattered = B_TRUE;
}
}
// either remove or move the object
if (shattered) {
if (o->material->id == MT_GLASS) {
int numshards;
numshards = getnumshards(o);
// place glass shards
sprintf(buf, "%d pieces of broken glass",numshards);
addob(where->obpile, buf);
} else if (o->material->id == MT_ICE) {
int numshards;
numshards = getnumshards(o) / 15;
if (numshards < 1) numshards = 1;
// ice
sprintf(buf, "%d chunks of ice",numshards);
addob(where->obpile, buf);
}
// potion effects?
if (shattered) {
if (o->type->obclass->id == OC_POTION) {
int observed;
switch (o->type->id) {
case OT_POT_ACID: // take acid damage
if (seen) {
makeknown(o->type->id);
}
if (target) {
if (seen) {
msg("%s %s splashed with acid!", targetname,
isplayer(target) ? "are" : "is");
}
losehp(target, rnd(1,5), DT_ACID, thrower, "a splash of acid");
} else {
// all objects take damage
object_t *oo,*nextoo;
// announce
if (seen) {
msg("Acid splashes all over the floor!");
}
// everything here takes acid damage
for (oo = where->obpile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
takedamage(oo, rnd(5,15), DT_ACID);
}
}
break;
case OT_POT_ELEMENTENDURE:
case OT_POT_ELEMENTIMMUNE:
case OT_POT_ETHEREALNESS:
case OT_POT_GASEOUSFORM:
case OT_POT_POLYMORPH:
case OT_POT_SANCTUARY: // always make them known
if (target) {
if (seen) {
makeknown(o->type->id);
}
potioneffects(target, o->type->id, o->blessed, &observed);
}
break;
case OT_POT_HEALING:
case OT_POT_HEALINGMIN: // only make them known if it had an effect
if (target) {
potioneffects(target, o->type->id, o->blessed, &observed);
if (observed) {
makeknown(o->type->id);
}
}
break;
case OT_POT_WATER:
if (seen) {
makeknown(o->type->id);
}
if (target) {
if (seen) {
msg("%s %s splashed with water.", targetname,
isplayer(target) ? "are" : "is");
}
if (hasflag(target->flags, F_UNDEAD) && isblessed(o)) {
if (isplayer(target)) {
msg("The water burns like acid!");
} else if (haslos(player, target->cell)) {
msg("%s writhes in agony!", targetname);
}
losehp(target, rnd(5,10), DT_HOLY, NULL, "holy water");
if (seen) {
// we now know that it is blessed
o->blessknown = B_TRUE;
}
} else {
losehp(target, 0, DT_WATER, thrower, "a splash of water");
}
} else {
// all objects take damage
object_t *oo,*nextoo;
// announce
if (seen) {
msg("Water splashes onto the floor.");
}
//TODO: holy water blesses everything?
// everything here takes water damage
for (oo = where->obpile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
takedamage(oo, 0, DT_WATER);
}
addob(where->obpile, "small puddle of water");
}
break;
default:
break;
}
}
}
}
// heavy ob?
if (youhit && target) {
if (getobweight(o) >= getlfweight(target, B_NOOBS)) {
int dir;
dir = getdirtowards(thrower->cell, target->cell, target, B_FALSE);
knockback(target, dir, 1, thrower);
}
}
if (shattered) {
// now remove the object
removeob(o, 1);
} else {
object_t *newob;
newob = moveob(o, where->obpile, 1);
// fake its birth time so that it can be damaged
newob->birthtime = -1;
takedamage(newob, speed, DT_BASH);
}
return B_FALSE;
}
void timeeffectsob(object_t *o) {
flag_t *f, *nextf;
cell_t *location;
lifeform_t *owner;
char obname[BUFLEN];
if (hasflag(o->flags, F_DEAD)) return;
getobname(o, obname, o->amt);
if (o->pile->where) {
location = o->pile->where;
} else {
location = NULL;
}
if (o->pile->owner) {
owner = o->pile->owner;
} else {
owner = NULL;
}
// expire flags
timeeffectsflags(o->flags);
// blessed weapons glow when held near undead
if (isblessed(o) && isweapon(o)) {
cell_t *ourcell;
flag_t *glowflag = NULL;
int nearundead = B_FALSE;
// are we glowing?
for (f = o->flags->first ; f ; f = nextf) {
nextf = f->next;
if ((f->id == F_PRODUCESLIGHT) && (f->lifetime == FROMBLESSING)) {
glowflag = f;
break;
}
}
if (isequipped(o)) {
// do we need to start/stop glowing?
ourcell = getoblocation(o);
if (ourcell) {
int x,y;
// check if we are near undead (ie. within 2 sq)
for (y = ourcell->y - 2; y <= ourcell->y + 2; y++) {
for (x = ourcell->x - 2; x <= ourcell->x + 2; x++) {
cell_t *c;
c = getcellat(ourcell->map, x, y);
if (c && haslf(c) && hasflag(c->lf->flags, F_UNDEAD)) {
nearundead = B_TRUE;
break;
}
}
}
if (nearundead) {
if (!glowflag) {
// start glowing
glowflag = addtempflag(o->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL, FROMBLESSING);
/*
if (isplayer(o->pile->owner)) {
msg("Your %s start%s glowing!",noprefix(obname), (o->amt == 1) ? "s" : "");
if (!o->blessknown) o->blessknown = B_TRUE;
} else if (haslos(player, ourcell)) {
msg("%s start%s glowing!",obname, (o->amt == 1) ? "s" : "");
if (!o->blessknown) o->blessknown = B_TRUE;
}
*/
if (haslos(player, ourcell)) {
if (!o->blessknown) o->blessknown = B_TRUE;
}
calclight(ourcell->map);
}
} else { // not near undead
if (glowflag) {
killflag(glowflag);
glowflag = NULL;
/*
if (isplayer(o->pile->owner)) {
msg("Your %s stop%s glowing.",noprefix(obname), (o->amt == 1) ? "s" : "");
if (!o->blessknown) o->blessknown = B_TRUE;
} else if (haslos(player, ourcell)) {
msg("%s stop%s glowing.",obname, (o->amt == 1) ? "s" : "");
if (!o->blessknown) o->blessknown = B_TRUE;
}
*/
}
}
}
} else { // not equipped
if (glowflag) { // if not equipped and glowing...
// stop glowing
killflag(glowflag);
glowflag = NULL;
}
}
}
if (location) {
// does object's material change cell type?
if (o->material->id == MT_FIRE) {
if (hasflag(location->type->material->flags, F_FLAMMABLE)) {
// burn!
// TODO: change to celltype of nearby floor
// OR to default map empty type.
if (haslos(player, location)) {
msg("The %s burns!", location->type->name);
}
setcelltype(location, CT_CORRIDOR);
addob(location->obpile, "pile of ash");
}
}
}
// check each flag for this object...
for (f = o->flags->first ; f ; f = nextf) {
object_t *oo,*nextoo;
nextf = f->next;
// CHECKS FOR CURRENT OBJECT
if (f->id == F_ACTIVATED) {
if (o->type->id == OT_JETPACK) {
// activated jetpack on the ground?
if (location) {
if (haslos(player, location)) {
char obname[BUFLEN];
getobname(o, obname, 1);
msg("%s shoots up into the roof and explodes!",obname);
}
removeob(o, o->amt);
return;
} else {
int chargeleft;
// use up power
chargeleft = usecharge(o);
// out of power?
if (chargeleft <= 0) {
if (owner) {
// announce power loss
if (isplayer(owner)) {
msg("Your %s is out of power!",noprefix(obname));
// you know it's out
f = hasflag(o->flags, F_CHARGES);
f->known = B_TRUE;
}
}
turnoff(NULL, o);
if (owner) {
// user will fall to the ground if not flying in some
// other way
if (!lfhasflag(owner, F_FLYING)) {
if (haslos(player, owner->cell)) {
char lfname[BUFLEN];
getlfname(owner, lfname);
msg("%s crash%s to the ground!",lfname,
isplayer(owner) ? "" : "es");
}
losehp(owner, rnd(1,4), DT_BASH, NULL, "falling");
}
}
} else if (chargeleft <= 10) { // about to run out of power?
if (isplayer(owner)) {
msg("Your %s splutters.", noprefix(obname));
}
// TODO: add smoke!
}
}
} else if (hasflag(o->flags, F_GRENADE)) {
flag_t *f2;
// countdown...
f2 = hasflag(o->flags, F_CHARGES);
if (f2) {
f2->val[0]--;
if (f2->val[0] <= 0) {
// blow up!
takedamage(o, 999, DT_DIRECT);
return;
}
}
} else if (o->type->id == OT_LANTERNOIL) {
flag_t *f2;
// countdown...
f2 = hasflag(o->flags, F_CHARGES);
if (f2) {
f2->val[0]--;
if (f2->val[0] <= 0) {
// turnoff
turnoff(NULL, o);
obaction(o, "goes out");
needredraw = B_TRUE;
return;
} else if (f2->val[0] <= 50) {
if (rnd(1,6) == 1) {
obaction(o, "flickers");
}
}
}
}
}
if (f->id == F_OBHPDRAIN) {
enum DAMTYPE damtype;
//takedamage(o, f->val[0] * firstlftime, DT_DIRECT);
if (f->val[1] == NA) {
damtype = DT_DIRECT;
} else {
damtype = f->val[1];
}
takedamage(o, f->val[0], damtype);
if (hasflag(o->flags, F_DEAD)) return;
}
// damaging objects here will damage other objects
if (f->id == F_WALKDAM) {
// everything here takes damage
//damageallobs(o, o->pile, f->val[0] * timespent, f->val[1]);
damageallobs(o, o->pile, f->val[0], f->val[1]);
//if (hasflag(o->flags, F_DEAD)) return;
}
// is object on fire?
if (f->id == F_ONFIRE) {
// water puts out fire
for (oo = o->pile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
if ((oo != o) && (oo->material->id == MT_WATER)) {
extinguish(o);
continue;
}
}
// if it hasn't been extinguished, fire burns
takedamage(o, 2, DT_FIRE); // TODO: don't hardcode
if (hasflag(o->flags, F_DEAD)) return;
}
// will current object convert other obs to something else?
if (f->id == F_MATCONVERT) {
enum MATERIAL mat;
mat = f->val[0];
for (oo = o->pile->first ; oo ; oo = nextoo) {
flag_t *f2;
nextoo = oo->next;
if ((oo != o) && (oo->material->id == mat)) {
char buf[BUFLEN];
object_t *newob;
// TODO: remove the old object first
// in case the obpile doesn't have space
// to fit both the new and old ones.
// place item of this type here!
sprintf(buf, "%d %s", o->amt, f->text);
newob = addob(o->pile, buf);
if (newob) {
int cansee;
// make the weight match.
newob->weight = o->weight;
// announce it?
cansee = B_FALSE;
if (location && haslos(player, location)) {
cansee = B_TRUE;
} else if (o->pile->owner == player) {
cansee = B_TRUE;
}
if (cansee) {
f2 = NULL;
if (o->amt > 1) {
f2 = hasflagval(o->flags, F_MATCONVERTTEXTPL, mat, NA, NA,NULL);
}
if (!f2) {
f2 = hasflagval(o->flags, F_MATCONVERTTEXT, mat, NA, NA, NULL);
}
if (f2) {
char *locbuf;
getobname(o, buf, o->amt);
locbuf = strdup(buf);
capitalise(locbuf);
if (o->pile->owner == player) {
// Remove prefix
locbuf = strrep(locbuf, "An ", "", NULL);
locbuf = strrep(locbuf, "A ", "", NULL);
}
msg("%s%s %s.", (o->pile->owner == player) ? "Your " : "", locbuf, f2->text);
free(locbuf);
}
}
// remove original object
removeob(o, o->amt);
return;
}
}
}
}
} // end for each object flag
}
void turnoff(lifeform_t *lf, object_t *o) {
char obname[BUFLEN];
flag_t *f;
f = hasflag(o->flags, F_ACTIVATED);
if (!f) {
// already off
return;
}
// reset charges
if (hasflag(o->flags, F_RECHARGEWHENOFF)) {
f = hasflag(o->flags, F_CHARGES);
if (f) {
f->val[0] = f->val[1];
}
}
getobname(o, obname, 1);
if (lf) {
if (isplayer(lf)) {
msg("You deactivate your %s.",noprefix(obname));
} else if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s deactivates %s.",lfname, obname);
}
}
if (hasflag(o->flags, F_GRENADE)) {
object_t *stackob;
// make it stackable again
addflag(o->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
// other stacks to join?
stackob = canstackob(o->pile, o);
if (stackob) {
char stackname[BUFLEN];
// remove it, and inc amt on the stack
removeob(o, 1);
stackob->amt++;
getobname(stackob, stackname, stackob->amt);
msgnocap("%c - %s.",stackob->letter, stackname);
} else {
// just turn off
killflag(f);
}
} else {
killflag(f);
}
if (o->pile->owner) {
loseobflags(o->pile->owner, o, F_ACTIVATECONFER);
}
}
void turnon(lifeform_t *lf, object_t *o) {
char obname[BUFLEN];
flag_t *f;
f = hasflag(o->flags, F_ACTIVATED);
if (f) {
// already on
if (isplayer(lf)) {
msg("Your %s is already activated!\n", noprefix(obname));
}
return;
}
// check charges
f = hasflag(o->flags, F_CHARGES);
if (f && (f->val[0] <= 0)) {
// out of power
if (isplayer(lf)) {
nothinghappens();
}
return;
}
getobname(o, obname, 1);
if (isplayer(lf)) {
msg("You activate your %s.",noprefix(obname));
} else if (haslos(player, lf->cell)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s activates %s.",lfname, obname);
}
// for grenades, give a new object which is activated
if (hasflag(o->flags, F_GRENADE)) {
object_t *newob;
newob = splitob(o);
if (newob) {
char newobname[BUFLEN];
// announce new ob
addflag(newob->flags, F_ACTIVATED, B_TRUE, NA, NA, NULL);
getobname(newob, newobname, 1);
msgnocap("%c - %s [activated].",newob->letter, newobname);
} else {
if (isplayer(lf)) {
msg("You don't have room for an activated %s!",noprefix(obname));
}
}
} else {
addflag(o->flags, F_ACTIVATED, B_TRUE, NA, NA, NULL);
}
giveobflags(lf, o, F_ACTIVATECONFER);
}
// returns charges remaining, -1 if object doesn't have the flag
int usecharge(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_CHARGES);
if (f) {
f->val[0]--;
return f->val[0];
}
return -1;
}
int validateobs(void) {
objecttype_t *ot;
int foundspells = B_FALSE;
int goterror = B_FALSE;
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id == OC_SPELL) || (ot->obclass->id == OC_ABILITY)) {
if (!foundspells) foundspells = B_TRUE;
if (!hasflag(ot->flags, F_SPELLSCHOOL)) {
printf("ERROR - spell %s doesn't have F_SPELLSCHOOL.\n", ot->name);
goterror = B_TRUE;
}
/*
if (!hasflag(ot->flags, F_SPELLLETTER)) {
printf("ERROR - spell %s doesn't have F_SPELLLETTER.\n", ot->name);
goterror = B_TRUE;
}
*/
if ((ot->obclass->id == OC_SPELL) && !hasflag(ot->flags, F_SPELLLEVEL)) {
printf("ERROR - spell %s doesn't have F_SPELLLEVEL.\n", ot->name);
goterror = B_TRUE;
}
} else if (ot->obclass->id == OC_SCROLL) {
if (foundspells) {
printf("ERROR in object '%s' - all Scrolls must be defined before Spells.", ot->name);
goterror = B_TRUE;
}
}
if (hasflagval(ot->flags, F_PICKLOCKS, NA, B_BLUNTONFAIL, NA, NULL) && hasflag(ot->flags, F_STACKABLE)) {
printf("ERROR in object '%s' - cannot have F_BLUNTONFAIL on stackable objects.", ot->name);
goterror = B_TRUE;
}
if (hasflag(ot->flags, F_FIREARM) && !hasflag(ot->flags, F_RANGE)) {
printf("ERROR in object '%s' - firearms need to have F_RANGE.", ot->name);
goterror = B_TRUE;
}
}
return goterror;
}
int willshatter(enum MATERIAL mat) {
switch (mat) {
case MT_GLASS:
case MT_ICE:
return B_TRUE;
default: break;
}
return B_FALSE;
}
// determine how long a conferred effect should last, based on its blessed/cursed status.
// blessed: always max
// uncursed: random number between min & max
// cursed: always min
int geteffecttime(int min, int max, enum BLESSTYPE isblessed) {
int howlong;
// how long for?
switch (isblessed) {
case B_BLESSED:
howlong = 15;
break;
case B_CURSED:
howlong = 5;
break;
default: // ie. B_UNCURSED
howlong = rnd(5,15);
break;
}
return howlong;
}
objecttype_t *getlinkspell(object_t *o) {
flag_t *f = NULL;
objecttype_t *spelltype = NULL;
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
// find the linked spell
spelltype = findot(f->val[0]);
}
return spelltype;
}
int getmissileaccuracy(lifeform_t *thrower, cell_t *where, object_t *missile, object_t *firearm, enum ATTRIB whichatt) {
int acc,maxrange,howfar;
// figure out if you miss or not, based on distance and
// dexterity
// base:
// if throwing, 50%
// if firing, gun accuracy
// adjust for range:
// pointblank = +30%
// max = -30%
// then modify using dexterity/int/whatever
// then penalise for throwing non-missiles
// base accuracy
if (firearm) {
acc = getobaccuracy(firearm);
} else {
acc = 50;
}
// adjust for range
if (firearm || missile) {
if (firearm) {
maxrange = getfirearmrange(firearm);
} else {
maxrange = getmaxthrowrange(thrower, missile);
}
} else {
maxrange = getvisrange(thrower);
}
howfar = getcelldist(thrower->cell, where);
if (howfar == 1) {
acc += 30;
} else if (howfar == maxrange) {
acc -= 30;
}
// modify for dexterity
if (whichatt != A_NONE) {
acc += getstatmod(thrower, whichatt);
}
// penalty for throwing non-missiles
if (missile && !firearm && !ismissile(missile)) {
acc -= 20;
}
return acc;
}