nexus/objects.c

7125 lines
208 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 objecttype_t *objecttype,*lastobjecttype;
extern objectclass_t *objectclass,*lastobjectclass;
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 long curtime;
extern lifeform_t *player;
extern int reason;
extern int needredraw;
char *scrollname[] = {
"scroll titled ABRA CA DABRA",
"scroll titled BARBAR",
"scroll titled CRATOL JEM",
"scroll titled DELENTH YIN",
"scroll titled ETEE NOM",
"scroll titled FIE JOOHM",
"scroll titled GREE VII",
"scroll titled MARIGON",
"scroll titled MAXIMOR BLATHUS",
"scroll titled REZZ KINETO",
"scroll titled SHAZZARIO",
"scroll titled THERIUM LARGOS",
"scroll titled VOLTR YI MEN",
"scroll titled ZAREL NOR",
NULL
};
int numscrollnames;
char *potionname[] = {
// colours
"aqua potion",
"azure potion",
"blue potion",
"brown potion",
"clear potion",
"cyan potion",
"green potion",
"indigo potion",
"luminous potion", // should produce light
"magenta potion",
"orange potion",
"red potion",
"violet potion",
"yellow potion",
// other descriptions
"bubbling potion",
"fizzy potion",
"gluggy potion",
"steaming potion",
NULL
};
int numpotnames;
char *ringname[] = {
// gems
"ruby ring",
"diamond ring",
"emerald ring",
"silver ring",
"gold ring",
NULL
};
int numringnames;
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_ARMOUR,
OC_POTION,
OC_SCROLL,
OC_FOOD,
OC_CORPSE,
OC_RING,
OC_TECH,
OC_ROCK,
OC_MISC,
// omitting OC_SPELL and OC_JUMP because it shouldn't ever be displayed
OC_NULL
};
long nextoid = 0;
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;
}
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;
char numstringmin[BUFLEN];
char numstringmax[BUFLEN];
int howmany = 1;
int i;
int db = B_FALSE;
//flag_t *f;
char *localname;
int wantblessed = B_UNCURSED;
localname = strdup(name);
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;
}
// }
// 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 );
// 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);
}
}
// 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;
}
}
}
if (o) {
char *p2;
int bonus = 0;
// check for premods. eg. "flaming xxx"
if (strstr(name, "flaming ")) {
addflag(o->flags, F_ONFIRE, B_TRUE, NA, NA, NULL);
if (o->type->obclass->id == OC_WEAPON) {
if (!hasflagval(o->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL)) {
addflag(o->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL);
}
}
}
// 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);
// check for postmods. eg. "xxx of pyromania"
if (strstr(name, " of pyromania")) {
addflag_real(o->flags, F_FLAMESTRIKE, B_TRUE, NA, NA, NULL, PERMENANT, B_UNKNOWN);
}
}
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;
}
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_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;
}
}
}
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)) {
*dam = 0;
return;
}
// immune?
if (hasflagval(o->flags, F_DTIMMUNE, damtype, NA, NA, NULL)) {
*dam = 0;
return;
}
// poison gas never hurts objects
if (damtype == DT_POISONGAS) {
*dam = 0;
return;
}
// adjust damage
if (o->blessed == B_BLESSED) {
// change 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;
}
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 *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;
}
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;
char *searchfor, *modname;
char *p;
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, "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 extra mods
modname = strrep(modname, "flaming ", "", NULL);
modname = strrep(modname, " of pyromania", "", 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);
}
dblog("findotn(): modname is '%s'",modname);
for (ot = objecttype ; ot ; ot = ot->next) {
// check for exact matches first...
if (!strcmp(ot->name, modname)) {
free(modname);
// found it!
return ot;
}
}
for (ot = objecttype ; ot ; ot = ot->next) {
// then partial matches
if (strstr(ot->name, modname)) {
free(modname);
// found it!
return ot;
}
}
searchfor = strdup(modname);
// haven't found it yet - check for plurals
// search for words ending in "es" (eg. tomatoes)
if ((searchfor[strlen(searchfor)-1] == 's') &&
(searchfor[strlen(searchfor)-2] == 'e') ) {
// remove trailing 'es'
searchfor[strlen(searchfor)-1] = '\0';
searchfor[strlen(searchfor)-2] = '\0';
dblog("findotn(): searchfor without 'es' is '%s'",searchfor);
// search again
for (ot = objecttype ; ot ; ot = ot->next) {
if (strstr(ot->name, searchfor)) {
free(modname);
free(searchfor);
return ot;
}
}
}
// reset back to inital word
free(searchfor);
searchfor = strdup(modname);
// search for words ending in "s" (eg. "swords")
if (searchfor[strlen(searchfor)-1] == 's') {
// remove trailing 's'
searchfor[strlen(searchfor)-1] = '\0';
dblog("findotn(): searchfor without 's' is '%s'",searchfor);
// search again
for (ot = objecttype ; ot ; ot = ot->next) {
if (strstr(ot->name, searchfor)) {
free(modname);
free(searchfor);
return ot;
}
}
}
free(modname);
free(searchfor);
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 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);
free(thisname);
}
}
}
char *genhiddenname(enum OBCLASS id) {
char temp[BUFLEN];
char *name;
int *numnames;
int sel,i;
char **namelist;
switch (id) {
case OC_SCROLL:
namelist = scrollname;
numnames = &numscrollnames;
break;
case OC_POTION:
namelist = potionname;
numnames = &numpotnames;
break;
case OC_RING:
namelist = ringname;
numnames = &numringnames;
break;
default:
dblog("ERROR! genhiddenname for unknown class %d\n",id);
exit(1);
}
assert((*numnames != 0));
sel = rnd(0,*numnames-1);
strcpy(temp, namelist[sel]);
name = strdup(temp);
// remove this name from the candidate list
for (i = sel; i < (*numnames-1); i++) {
namelist[i] = namelist[i+1];
}
(*numnames)--;
return name;
}
// 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;
}
// 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) {
flag_t *ff;
float mod = 1;
int min,max;
getdamrange(o->flags, &min, &max);
ff = hasflag(o->flags, F_OBATTACKSPEED);
if (ff) {
switch (ff->val[0]) {
case SP_FAST:
case SP_VERYFAST:
mod = 1.5;
break;
case SP_ULTRAFAST:
case SP_GODLIKE:
mod = 2;
default:
mod = 1;
break;
}
}
price += (max*mod*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_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";
default: return "unknown";
}
return "unknown";
}
char *getdamnamenoun(enum DAMTYPE damtype) {
switch (damtype) {
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";
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 getobattackspeed(object_t *o) {
int speed = SPEED_ATTACK;
flag_t *f;
f = hasflag(o->flags, F_OBATTACKSPEED);
if (f) {
speed = f->val[0];
}
return speed;
}
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;
// TODO: adjust for lifeform strength
maxdist = 10 - (getobunitweight(o) / 2);
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;
int nutrition;
if (isrotting(o)) {
nutrition = -(HUNGERCONST/2);
} else {
nutrpct = getnutritionbase(o);
if (nutrpct <= 0) {
nutrition = 0;
} else {
nutrition = pctof(nutrpct, (float) HUNGERCONST);
}
}
return nutrition;
}
char *getobdesc(object_t *o, char *buf) {
if (isknown(o)) {
sprintf(buf, "%s", o->type->desc);
} else {
sprintf(buf, "%s", o->type->obclass->desc);
}
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);
}
// buf must already be allocated
char *real_getobname(object_t *o, char *buf, int count, int wantcondition, int adjustforblind, int wantblesscurse) {
char *pluralname;
char prefix[BUFLEN];
char basename[BUFLEN];
char buf2[BUFLEN];
int shopitem = B_FALSE;
flag_t *f;
// 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");
}
// 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 (hasflag(o->flags, F_ONFIRE)) {
strcat(buf, "flaming ");
}
// 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
f = hasflag(o->flags, F_FLAMESTRIKE);
if (f && (f->known || shopitem)) {
strcat(buf, " of pyromania");
}
// make sure obname doesn't start with a space
while (buf && (buf[0] == ' ')) {
strcpy(buf, buf + 1);
}
// append inscription
if (o->inscription) {
strcat(buf, " {");
strcat(buf, o->inscription);
strcat(buf, "}");
}
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) {
flag_t *f;
float pct;
if (iscorpse(o)) {
if (isrotting(o)) {
sprintf(buf, "rotting");
} else {
strcpy(buf, "");
}
} else {
f = hasflag(o->flags, F_OBHP);
if (f) {
pct = ((float) f->val[0] / (float) f->val[1]) * 100.0;
} else {
pct = 100;
}
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");
}
}
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_TRUE;
char *pluralname;
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);
}
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;
}
/*
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);
}
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_ELEMENTAL: return "Elemental 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!";
}
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;
// 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));
// 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;
}
// misisle objects do extra damage
if (hasflag(o->flags, F_MISSILE)) {
dam *= 2;
}
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 initobjects(void) {
//int ch;
//int i;
//objecttype_t *ot;
// generate counts for hidden names
numscrollnames = countnames(scrollname);
numpotnames = countnames(potionname);
numringnames = countnames(ringname);
// 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_DTCONVERT, DT_FIRE, NA, NA, "pile of ash");
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_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);
addoc(OC_ARMOUR, "Armour/Clothing", "Protective gear.", ']');
addflag(lastobjectclass->flags, F_DAMAGABLE, 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_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, 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_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, B_TRUE, 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, 100, OC_DFEATURE);
addflag(lastot->flags, F_RARITY, H_ALL, 65, NA, "");
addflag(lastot->flags, F_GLYPH, NA, NA, NA, "'");
addflag(lastot->flags, F_IMPASSABLE, B_TRUE, 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_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, 25, 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);
// 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_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_RARITY, H_DUNGEON, 10, 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, 70, 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, 70, 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.5, OC_FOOD);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 50, NA, "");
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, 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, 75, 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, 80, 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, 60, 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, 60, 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, 50, 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_FLESHCHUNK, "chunk of flesh", "A chunk of flesh from something.", MT_FLESH, 1, OC_CORPSE);
addflag(lastot->flags, F_EDIBLE, B_TRUE, 5, NA, "");
// potions
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);
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, 80, 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, 25, 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, 80, 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_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);
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, 60, 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, 80, 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);
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, 60, 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_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_SANCTUARY, "potion of sanctuary", "Creates a temporary magical barrier abour the drinker.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, 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, 60, NA, NULL);
addot(OT_POT_SPEED, "potion of speed", "Temporarily increasees the drinker's speed.", MT_GLASS, 1, OC_POTION);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, 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_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);
// 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, 90, 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, 60, 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);
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, 65, 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, 80, NA, NULL);
addot(OT_SCR_FIREBALL, "scroll of fireball", "Creates a huge ball of fire.", MT_PAPER, 0.5, OC_SCROLL);
addflag(lastot->flags, F_LINKSPELL, OT_S_FIREBALL, NA, NA, NULL);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addot(OT_SCR_FLAMEPILLAR, "scroll of flame column", "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, 60, 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, 65, 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_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);
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, 75, 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);
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);
addot(OT_S_DETECTMETAL, "detect metal", "Senses any metal near the caster.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 1, 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);
// l3
addot(OT_S_DETONATE, "detonate", "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
///////////////////
// l3
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);
// 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);
// l7
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, 7, 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
///////////////////
// l2
addot(OT_S_FIREDART, "flame dart", "Fires a medium-sized dart of fire.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ELEMENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL);
addot(OT_S_CONECOLD, "cone of cold", "Shoots a blast of freezing air.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ELEMENTAL, 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_ELEMENTAL, 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_FIREBALL, "fireball", "Creates a huge ball of fire.", MT_NOTHING, 0, OC_SPELL);
addflag(lastot->flags, F_SPELLSCHOOL, SS_ELEMENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL);
addflag(lastot->flags, F_AICASTATVICTIM, NA, NA, NA, NULL); // TODO: should be "near victim"
///////////////////
// 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
///////////////////
// l2
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, 2, 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_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);
// l2
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);
// 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_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);
// 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
///////////////////
// 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_LEVTELEPORT, "level teleport", "Teleports the user 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 pure magical energy.", 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 pure magical energy.", 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, hitting 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_AICASTANYWHERE, 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);
// 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);
// abilities
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);
//addflag(lastot->flags, F_SPELLLETTER, NA, NA, NA, "j");
// assign letters to spells/abilities
/*
ch = 'a' - 1;
// foreach spell/abil school
for (i = 0; i < SS_LAST; i++) {
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id == OC_SPELL) || (ot->obclass->id == OC_ABILITY)) {
// if school matches
if (hasflagval(ot->flags, F_SPELLSCHOOL, i, NA, NA, NULL)) {
if (!hasflag(ot->flags, F_SPELLLETTER)) {
char buf[2];
int found = B_TRUE;
while (found) {
objecttype_t *ot2;
// next letter!
if (ch == 'z') {
ch = '1';
} else if (ch == ':') {
ch = 'A';
} else {
ch++;
}
buf[0] = ch;
buf[1] = '\0';
// check nothing else has our current letter...
found = B_FALSE;
for (ot2 = objecttype ; ot2 ; ot2 = ot2->next) {
if (hasflagval(ot2->flags, F_SPELLLETTER, NA, NA, NA, buf)) {
found = B_TRUE;
}
}
}
addflag(ot->flags, F_SPELLLETTER, NA, NA, NA, buf);
}
}
}
}
}
*/
/*
addflag(lastot->flags, F_SPELLLETTER, NA, NA, NA, "e");
*/
// 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, 5, 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, 50, 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, 25, 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_OBATTACKSPEED, SP_NORMAL, NA, NA, NULL);
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_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);
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);
addot(OT_BLOODSTAIN, "blood stain", "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);
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);
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, B_TRUE, 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);
// 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_DIECONVERTTEXT, NA, NA, NA, "thins out a little");
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, B_TRUE, 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, 70, 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_FLAKJACKET, "flak jacket", "Metal body armour, designed to stop bullets.", MT_METAL, 20, 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, 8, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -20, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 40, 40, 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);
// 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);
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_SHOULDERS, NA, NA, NULL);
addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL);
addflag(lastot->flags, F_EVASION, -20, NA, NA, NULL);
addflag(lastot->flags, F_OBHP, 4, 4, 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_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, 20, 20, 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_BASH, 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_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");
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_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);
// 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_OBATTACKSPEED, SP_FAST, 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);
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, 10, 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_OBATTACKSPEED, SP_FAST, 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, 10, 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_OBATTACKSPEED, SP_NORMAL, NA, 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_OBATTACKSPEED, SP_NORMAL, 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, 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_OBATTACKSPEED, SP_NORMAL, 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_OBATTACKSPEED, SP_ULTRAFAST, 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_OBATTACKSPEED, SP_FAST, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_PIERCE, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 2, 4, NA, 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, 10, B_BLUNTONFAIL, NA, NULL);
// chopping weapons
addot(OT_AXE, "axe", "A short pole with a heavy, wedge-shaped blade for chopping.", MT_METAL, 2, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL);
addflag(lastot->flags, F_OBATTACKSPEED, SP_SLOW, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_CHOP, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 5, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_BATTLEAXE, "battleaxe", "An axe specifically designed for combat.", MT_METAL, 3, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKSPEED, SP_SLOW, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_CHOP, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 10, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 80, NA, NA, NULL);
addot(OT_GREATAXE, "greataxe", "An enormous axe made designed for combat.", MT_METAL, 5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_OBATTACKSPEED, SP_VERYSLOW, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_CHOP, NA, NA, NULL);
addflag(lastot->flags, F_DAM, 1, 12, 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_OBATTACKSPEED, SP_FAST, NA, 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_PICKLOCKS, 10, 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_OBATTACKSPEED, SP_FAST, NA, 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_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_OBATTACKSPEED, SP_SLOW, 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, 60, 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_OBATTACKSPEED, SP_FAST, 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, 100, NA, NA, NULL);
addot(OT_SCIMITAR, "scimitar", "A fast, curved blade.", MT_METAL, 2, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL);
addflag(lastot->flags, F_OBATTACKSPEED, SP_NORMAL, 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_OBATTACKSPEED, SP_NORMAL, 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_OBATTACKSPEED, SP_NORMAL, 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, 100, 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_OBATTACKSPEED, SP_FAST, 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_OBATTACKSPEED, SP_SLOW, 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_OBATTACKSPEED, SP_NORMAL, NA, 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);
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_OBATTACKSPEED, SP_NORMAL, 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, 90, NA, NA, NULL);
addot(OT_CLUB, "club", "A heavy, blunt wooden instrument to hit things with.", MT_WOOD, 1.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastot->flags, F_OBATTACKSPEED, SP_NORMAL, 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_OBATTACKSPEED, SP_NORMAL, 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_MORNINGSTAR, "morningstar", "An spiked mace.", MT_METAL, 3.5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL);
addflag(lastot->flags, F_OBATTACKSPEED, SP_SLOW, 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_WOOD, 5, OC_WEAPON);
addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL);
addflag(lastot->flags, F_OBATTACKSPEED, SP_VERYSLOW, 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_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_OBATTACKSPEED, SP_SLOW, 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, 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_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_OBATTACKSPEED, SP_FAST, 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);
// ammo
addot(OT_BULLET, "bullet", "A regular gun bullet.", MT_STONE, 0.1, OC_MISC);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ";");
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_MISSILE, 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_MISC);
addflag(lastot->flags, F_GLYPH, NA, NA, NA, ";");
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_MISSILE, 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);
// 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_OBATTACKSPEED, SP_GODLIKE, NA, NA, NULL);
addflag(lastot->flags, F_DAMTYPE, DT_HOLY, 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;
}
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 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 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:
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_OBATTACKSPEED);
if (f) {
killflagsofid(newlf->flags, F_MOVESPEED);
addflag(newlf->flags, F_MOVESPEED, f->val[0], NA, NA, NULL);
}
}
return newlf;
}
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_TRUE;
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);
}
return 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 (dst->where && !dst->where->lf && haslos(player, dst->where)) {
needredraw = B_TRUE;
drawscreen();
}
//o = newobeffects(o);
return o;
}
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;
f = hasflag(o->flags, F_DIECONVERT);
if (f) {
flag_t *f2;
char desc[BUFLEN];
// announce the change
real_getobname(o, obname, o->amt, 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)) {
char monname[BUFLEN];
getlfname(o->pile->owner, monname);
msg("%s's %s %s!",monname, noprefix(obname), desc);
}
} else if (haslos(player, o->pile->where)) {
capitalise(obname);
msg("%s %s.",obname, desc);
}
// change into something else
addob(o->pile, f->text);
} else {
char desc[BUFLEN];
real_getobname(o, obname, o->amt, 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);
}
}
}
obloc = o->pile->where;
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;
}
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) {
char buf[BUFLEN],obname[BUFLEN];
int playercansee;
cell_t *where = NULL;
flag_t *f;
getobname(o, obname, 1);
if ((lf->controller == C_PLAYER) || haslos(player, lf->cell)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
// must know what a tool is before you can use it
if (!isknown(o)) {
if (lf->controller == C_PLAYER) {
msg("You don't know how to use that (yet)!");
}
return B_TRUE;
}
// ask for target, if required
f = hasflag(o->flags, F_OPERNEEDTARGET);
if (f) {
where = askcoords(f->text, f->val[0]);
if (!where) {
// cancel.
msg("Cancelled.");
return B_TRUE;
} else {
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;
}
}
}
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, getmovespeed(lf));
/*
if (lf->controller != C_PLAYER) {
if (haslos(player, lf->cell)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s operates %s.", buf, obname);
}
}
*/
// 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;
}
}
}
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->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) {
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
drawscreen();
// takes extra time
taketime(lf, getmovespeed(lf)*9);
} else {
if (isplayer(lf)) {
msg("This wall is too hard to dig.");
}
}
} else {
if (isplayer(lf)) {
msg("You swing your %s through the air.",noprefix(obname));
}
taketime(lf, getmovespeed(lf));
}
} 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(o, B_UNCURSED);
o->blessknown = B_TRUE;
// remove bonuses
killflagsofid(o->flags, F_BONUS);
// remove temporary flags
for (f = o->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->lifetime > 0) {
killflag(f);
}
}
if (!isknown(o)) makeknown(o->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, SPEED_DRINK);
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:
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_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();
}
}
// 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,15);
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 (!lfhasflagval(lf, F_DTIMMUNE, DT_FIRE, NA, NA, NULL)) {
addtempflag(lf->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL, i);
}
if (!lfhasflagval(lf, F_DTIMMUNE, DT_COLD, NA, NA, NULL)) {
addtempflag(lf->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL, i);
}
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)) nothinghappens();
if (seen) *seen = B_FALSE;
} else {
if (seen) *seen = B_TRUE;
}
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, NULL, B_BLESSED, NULL);
} else if (isblessed == B_CURSED) {
dospelleffects(lf, OT_S_SLOW, lf, NULL, NULL, B_UNCURSED, NULL);
} else { // uncursed
dospelleffects(lf, OT_S_HASTE, lf, NULL, NULL, 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;
}
}
void 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;
getobname(o, obname, 1);
if ((lf->controller == C_PLAYER) || haslos(player, lf->cell)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
taketime(lf, SPEED_READ);
// some checks first...
touch(lf, o);
if (hasflag(o->flags, F_DEAD)) {
return;
}
if (lf->controller != C_PLAYER) {
if (haslos(player, lf->cell)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s reads %s!", buf, obname);
}
}
// figure out whether to id the object
willid = B_FALSE;
if (playercansee) {
switch (o->type->id) {
case OT_SCR_IDENTIFY: // only id if it does something
case OT_SCR_CREATEMONSTER: // 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:
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_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);
}
}
}
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);
}
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_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)) {
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)) {
capitalise(obname);
msg("%s %s!", obname, getobhurtname(o, damtype));
}
}
// catches on fire?
if (damtype == DT_FIRE) {
if (isflammable(o)) {
f = hasflag(o->flags, F_ONFIRE);
if (!f) {
// on fire for 2 turns
addtempflag(o->flags, F_ONFIRE, B_TRUE, NA, NA, NULL, 2);
}
}
}
// 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 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;
reason = E_OK;
if (firearm) {
strcpy(throwverbpres, "fire");
strcpy(throwverbpast, "fired");
} else {
strcpy(throwverbpres, "throw");
strcpy(throwverbpast, "thrown");
}
if (isblind(player)) {
if (isplayer(thrower)) {
getobname(o, obname, 1);
} else {
strcpy(obname, "something");
}
} else {
getobname(o, obname, 1);
}
getlfname(thrower, throwername);
if (isplayer(thrower)) {
strcpy(throwernamea, throwername);
} else {
if (isvowel(*(noprefix(throwername)))) {
strcpy(throwernamea, "an ");
} else {
strcpy(throwernamea, "a ");
}
strcat(throwernamea, noprefix(throwername));
}
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 (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) {
getlfname(target, targetname);
}
// touch effects
if (thrower && !firearm) {
if (o->pile == thrower->pack) {
touch(thrower, o);
}
}
// announce it ("xx throws xx" "at yy")
if (isplayer(thrower)) {
if (target) {
msg("You %s %s at %s.", throwverbpres, obname, targetname);
} else {
msg("You %s %s.",throwverbpres, obname);
}
} else if (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 %s through the air", obname, (o->amt == 1) ? "flies" : "fly");
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;
}
// 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
acc = getmissileaccuracy(thrower, where, o, firearm, A_DEX);
// roll for hit
// metal weapon versus magnetic shield?
if (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;
} else {
youhit = B_FALSE;
}
// if someone is there, they take damage and the object might die
// this should be "if target && youhit"
if (target) {
if (youhit) {
int dam = 0,shatterdam = 0;
char damstring[BUFLEN];
dam = getthrowdam(o) * speed;
// 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, throwernamea);
// 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 (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, throwernamea);
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_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;
}
}
}
// 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 (o->blessed && 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");
return;
} else if (f2->val[0] <= 50) {
if (rnd(1,3)) {
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);
msglower("%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);
msglower("%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;
}
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;
}