nexus/objects.c

13737 lines
328 KiB
C

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ai.h"
#include "attack.h"
#include "defs.h"
#include "flag.h"
#include "god.h"
#include "io.h"
#include "lf.h"
#include "map.h"
#include "move.h"
#include "nexus.h"
#include "objects.h"
#include "shops.h"
#include "spell.h"
#include "text.h"
extern knowledge_t *knowledge, *lastknowledge;
extern hiddenname_t *firsthiddenname, *lasthiddenname;
extern objecttype_t *objecttype,*lastobjecttype;
extern objectclass_t *objectclass,*lastobjectclass;
extern brand_t *firstbrand,*lastbrand;
extern obmod_t *firstobmod,*lastobmod;
extern material_t *material,*lastmaterial;
extern recipe_t *firstrecipe,*lastrecipe;
extern skill_t *firstskill, *lastskill;
extern region_t *firstregion;
extern buildingusage_t buildingusage[];
extern int nbuildingusage;
extern int inaskcoords;
void (*precalclos)(lifeform_t *);
extern object_t *retobs[MAXPILEOBS+1];
extern int retobscount[MAXPILEOBS+1];
extern int nretobs;
extern prompt_t prompt;
extern glyph_t tempglyph;
extern map_t *firstmap;
extern enum GAMEMODE gamemode;
extern long curtime;
extern lifeform_t *player;
extern int reason;
extern int needredraw;
extern int statdirty;
extern int obdb;
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_TERRAIN,
OC_BUILDING,
OC_MONEY,
OC_WEAPON,
OC_MISSILE,
OC_ARMOUR,
OC_POTION,
OC_SCROLL,
OC_WAND,
OC_FOOD,
OC_CORPSE,
OC_RING,
OC_TECH,
OC_TOOLS,
OC_BOOK,
OC_FURNITURE,
OC_GODSTONE,
OC_ROCK,
OC_FLORA,
OC_DFEATURE,
OC_TRAP,
OC_MISC,
// omitting OC_SPELL and OC_JUMP because it shouldn't ever be displayed
OC_NULL
};
char *techadjective[] = {
"bizzare",
"crazy",
"curious",
"mysterious",
"odd",
"peculiar",
"strange",
"weird",
"",
};
char *technoun[] = {
"apparatus",
"contraption",
"device",
"doodad",
"doohickey",
"gadget",
"gizmo",
"thingamajig",
"object",
"widget",
"",
};
char *bookadjective[] = {
"ancient",
"clean",
"creepy",
"damp",
"dog-eared",
"dusty",
"eerie",
"fancy",
"frosty",
"furry",
"glowing",
"hot",
"humming",
"icy",
"jiggling",
"leatherbound",
"luminous",
"mouldy",
"papyrus",
"plain",
"quivering",
"muddy",
"slimy",
"small",
"soggy",
"thin",
"thick",
"tiny",
"vibrating",
"vine-covered",
"warm",
"worn",
"wrinkled",
"",
};
char *potadjective[] = {
"bubbling",
"effervescent",
"fizzy",
"fuming",
"gluggy",
"luminous", // should produce light
"steaming",
"",
};
hiddennamewithcol_t colour[] = {
{ "aqua", C_CYAN, },
{ "azure",C_BOLDBLUE },
{ "black",C_BLUE },
{ "blue",C_BLUE },
{ "brown",C_BROWN },
{ "cyan",C_CYAN },
{ "green",C_GREEN },
{ "indigo",C_MAGENTA },
{ "magenta",C_BOLDMAGENTA },
{ "night-black",C_BLUE },
{ "orange",C_ORANGE },
{ "pink",C_MAGENTA },
{ "rainbow-coloured",C_ORANGE },
{ "red",C_RED },
{ "violet",C_MAGENTA },
{ "yellow",C_YELLOW },
{ "",C_GREY },
};
hiddennamewithcol_t gemtype[] = {
{ "agate", C_MAGENTA, },
{ "amethyst", C_MAGENTA, }, // should be purple
{ "brass",C_BROWN },
{ "bronze",C_BROWN },
{ "copper",C_BROWN },
{ "diamond",C_BOLDCYAN },
{ "emerald",C_GREEN },
{ "flourite",C_RED },
{ "garnet",C_ORANGE },
{ "gold",C_YELLOW },
{ "iridium",C_WHITE },
{ "jade",C_GREEN },
{ "jasper",C_RED },
{ "lapis lazuli",C_BOLDBLUE },
{ "malachite",C_BOLDCYAN },
{ "onyx",C_BLUE },
{ "opal",C_CYAN },
{ "pearl",C_WHITE },
{ "quartz",C_GREY },
{ "ruby",C_RED },
{ "sapphire",C_BLUE },
{ "topaz", C_YELLOW },
{ "zinc",C_GREY },
{ "zircon",C_CYAN },
{ "",C_GREY },
};
char *ringadjective[] = {
"cracked",
"dusty",
"ornate",
"sparkling",
"twinkling",
"",
};
long nextoid = 0;
brand_t *addbrand(enum BRAND id, char *suffix, enum BODYPART bp, enum BLESSTYPE blessed, int blesschance) {
brand_t *a, *om;
char buf[BUFLEN];
//flag_t *f;
// does this modj already exist?
om = findbrand(id);
assert(!om);
// add to the end of the list
if (firstbrand == NULL) {
firstbrand = malloc(sizeof(brand_t));
a = firstbrand;
a->prev = NULL;
} else {
// go to end of list
a = lastbrand;
a->next = malloc(sizeof(brand_t));
a->next->prev = a;
a = a->next;
}
lastbrand = a;
a->next = NULL;
// props
a->id = id;
a->bp = bp;
snprintf(buf, BUFLEN, " %s",suffix);
a->suffix = strdup(buf);
a->blessed = blessed;
a->blesschance = blesschance;
a->flags = addflagpile(NULL, NULL);
return a;
}
object_t *addemptyob(obpile_t *where, object_t *o) {
char buf[BUFLEN];
object_t *empty;
// determine what kind of empty container to drop
if (strstr(o->type->name, "vial")) {
strcpy(buf, "empty vial");
} else if (strstr(o->type->name, "potion") || strstr(o->type->name, "flask")) {
strcpy(buf, "empty flask");
} else {
return NULL;
}
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 (cansee(player, where->owner)) {
getlfname(where->owner, buf);
capitalise(buf);
msg("%s drops the %s on the ground.", buf, noprefix(emptyname));
}
}
}
}
return empty;
}
hiddenname_t *addhiddenname(enum OBCLASS obclass, char *text) {
hiddenname_t *a;
// make sure it doesn't already exist
assert(counthiddennames(obclass, text) == 0);
// add to the end of the list
if (firsthiddenname == NULL) {
firsthiddenname = malloc(sizeof(hiddenname_t));
a = firsthiddenname;
a->prev = NULL;
} else {
// go to end of list
a = lasthiddenname;
a->next = malloc(sizeof(hiddenname_t));
a->next->prev = a;
a = a->next;
}
lasthiddenname = a;
a->next = NULL;
// props
a->obclass = obclass;
a->text = strdup(text);
a->used = B_FALSE;
return a;
}
knowledge_t *addknowledge(enum OBCLASS id, char *hiddenname, int known) {
knowledge_t *a;
// add to the end of the list
if (knowledge == NULL) {
knowledge = malloc(sizeof(knowledge_t));
a = knowledge;
a->prev = NULL;
} else {
// go to end of list
a = lastknowledge;
a->next = malloc(sizeof(knowledge_t));
a->next->prev = a;
a = a->next;
}
lastknowledge = a;
a->next = NULL;
// props
a->id = id;
a->hiddenname = strdup(hiddenname);
a->known = known;
return a;
}
material_t *addmaterial(enum MATERIAL id, char *name, float weightrating) {
material_t *a;
// add to the end of the list
if (material == NULL) {
material = malloc(sizeof(material_t));
a = material;
a->prev = NULL;
} else {
// go to end of list
a = lastmaterial;
a->next = malloc(sizeof(material_t));
a->next->prev = a;
a = a->next;
}
lastmaterial = a;
a->next = NULL;
// props
a->id = id;
a->name = strdup(name);
a->weightrating = weightrating;
a->flags = addflagpile(NULL, NULL);
return a;
}
objectclass_t *addoc(enum OBCLASS id, char *name, char *desc, char glyph, int glyphcolour, enum RARITY rarity) {
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->nnouns = 0;
a->desc = strdup(desc);
a->glyph.ch = glyph;
a->glyph.colour = glyphcolour;
a->flags = addflagpile(NULL, NULL);
a->rarity = rarity;
return a;
}
void addocnoun(objectclass_t *oc, char *text) {
assert (oc->nnouns < MAXOCNOUNS);
oc->noun[oc->nnouns] = strdup(text);
oc->nnouns++;
}
// create a new object, stacking ok
object_t *addob(obpile_t *where, char *name) {
return addobject(where, name, B_TRUE, B_TRUE, OT_NONE);
}
object_t *addobfast(obpile_t *where, enum OBTYPE oid) {
return addobject(where, NULL, B_TRUE, B_TRUE, oid);
}
// create a new object
// if canstack is true, we are allwoed
// to join similar objects together into a single
// object with o->amt set, instead of
// creating new obejct entries.
// NOTE: This function MUST return number of obs created via global "nretobs"
object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes, enum OBTYPE forceoid) {
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 = NULL;
int wantblessed = B_UNCURSED;
race_t *corpserace = NULL;
int dorandombrand = B_FALSE;
brand_t *wantbrand = NULL;
brand_t *br;
obmod_t *om;
obmod_t *wantom[MAXOBMODS];
regionthing_t *wantregionthing = NULL;
int bonus = 0;
int nom = 0;
int n;
int bookcontents = -1;// a skill for manuals, or a spellschool id for spellbooks
enum DEPTH wantdepth = DP_MAX;
int wantlit = B_FALSE;
int wantrarity = RR_NONE;
int wantgoodness = G_NA;
int wantfoodtaint = B_FALSE;
enum LFSIZE wantarmsize = SZ_ANY;
enum MATERIAL wantdiffmat = MT_NOTHING;
map_t *targetmap = NULL; // for portals
cell_t *targetcell = NULL; // for portals
int donesomething;
object_t *addedob[MAXPILEOBS];
int nadded = 0;
// for doors
enum FLAG doorflag[5];
int ndoorflags = 0;
char *signtext = NULL;
int trapchance = 0;
int lockchance = 0;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
// just in case we don't add any
addedob[0] = NULL;
nadded = 0;
nretobs = 0;
if ((gamemode == GM_GAMESTARTED) && where->where) {
assert(!where->where->type->solid);
}
if (where->owner && hasflag(where->owner->flags, F_NOPACK)) {
if (db) dblog("error giving ob '%s' - owner isn't allowed to carry objects!", name);
nretobs = 0;
return NULL;
}
if (forceoid != OT_NONE) {
ot = findot(forceoid);
howmany = 1;
if (db) {
dblog("DB: called addobject() for forceoid %s, canstack = %d",ot->name, canstack);
}
} else {
char *bonusstart,*p2;
int bonussign = 1;
localname = strdup(name);
if (db) {
dblog("DB: called addobject() for %s, canstack = %d",localname, canstack);
}
// check for premods. eg. "flaming xxx" "frozen xxx" etc
for (om = firstobmod ; om ; om = om->next) {
if (db) dblog("DB: checking for '%s' in '%s'",om->prefix, localname);
if (strstr(localname, om->prefix)) {
localname = strrep(localname, om->prefix, "", NULL);
if (db) dblog("DB: found obmod prefix '%s'",om->prefix);
wantom[nom] = om;
nom++;
}
}
// 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);
}
// "p" should now point at the start of the actual
// object name.
// 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;
}
bonus = 0;
// handle for bonuses. eg. "+1"
bonusstart = strchr(p, '+');
if (!bonusstart) {
bonussign = -1;
bonusstart = strchr(p, '-');
}
if (bonusstart) {
char *p3,*bonusend;
char numbuf[BUFLENSMALL];
p3 = numbuf;
bonusend = bonusstart+1; // go past the + or -
while (isdigit(*bonusend)) { // grab the full number
*p3 = *bonusend;
bonusend++;
p3++;
}
*p3 = '\0';
bonus += (atoi(numbuf) * bonussign);
// strip off the "+xxx" / "-xxx", as long as xxx was a number.
// might not be the case if it was part of the object name
// eg. "waist-deep water"
if (bonus) {
while (*bonusend == ' ') bonusend++; // go past spaces
strcpy(bonusstart, bonusend);
}
}
// handle prefixes. strip them off as we go.
donesomething = B_TRUE;
while (donesomething) {
int n;
donesomething = B_FALSE;
// water flags
for (n = DP_MAX; n >= DP_FIRST; n--) {
char wpre[BUFLEN];
snprintf(wpre, BUFLEN, "%s ", getwaterdepthname(n));
if (strstarts(p, wpre)) {
wantdepth = n;
p += strlen(wpre);
p++; // go past space
donesomething = B_TRUE;
break;
}
}
if (donesomething) continue;
if (strstarts(p, "blessed ")) {
if (db) dblog("DB: ob is blessed (%s)",p);
wantblessed = B_BLESSED;
p += strlen("blessed ");
donesomething = B_TRUE;
} else if (strstarts(p, "uncursed ")) {
if (db) dblog("DB: ob is uncursed (%s)",p);
wantblessed = B_UNCURSED;
p += strlen("uncursed ");
donesomething = B_TRUE;
} else if (strstarts(p, "cursed ")) {
if (db) dblog("DB: ob is cursed (%s)",p);
wantblessed = B_CURSED;
p += strlen("cursed ");
donesomething = B_TRUE;
// armour flags
} else if (strstarts(p, "gargantuan ")) {
wantarmsize = SZ_ENORMOUS;
p += strlen("gargantuan ");
donesomething = B_TRUE;
} else if (strstarts(p, "titan-sized ")) {
wantarmsize = SZ_HUGE;
p += strlen("titan-sized ");
donesomething = B_TRUE;
} else if (strstarts(p, "giant-sized ")) {
wantarmsize = SZ_LARGE;
p += strlen("giant-sized ");
donesomething = B_TRUE;
} else if (strstarts(p, "half-sized ")) {
wantarmsize = SZ_MEDIUM;
p += strlen("half-sized ");
donesomething = B_TRUE;
} else if (strstarts(p, "baby-sized ")) {
wantarmsize = SZ_SMALL;
p += strlen("baby-sized ");
donesomething = B_TRUE;
} else if (strstarts(p, "tiny-sized ")) {
wantarmsize = SZ_TINY;
p += strlen("tiny-sized ");
donesomething = B_TRUE;
} else if (strstarts(p, "minisicule ")) {
wantarmsize = SZ_MINI;
p += strlen("minisicule ");
donesomething = B_TRUE;
// door flags
} else if (strstarts(p, "locked ")) {
doorflag[ndoorflags++] = F_LOCKED; // for doors
lockchance = 100; // for other objects like chests
p += strlen("locked ");
donesomething = B_TRUE;
} else if (strstarts(p, "jammed ")) {
doorflag[ndoorflags++] = F_JAMMED;
p += strlen("jammed ");
donesomething = B_TRUE;
} else if (strstarts(p, "secret ")) {
doorflag[ndoorflags++] = F_SECRET;
p += strlen("secret ");
donesomething = B_TRUE;
// tool flags
} else if (strstarts(p, "lit ")) {
wantlit = B_TRUE;
p += strlen("lit ");
donesomething = B_TRUE;
// different materials
} else if (strstarts(p, "silver ")) {
wantdiffmat = MT_SILVER;
p += strlen("silver ");
donesomething = B_TRUE;
// rarity
} else if (strstarts(p, "frequent ")) {
wantrarity = RR_FREQUENT;
p += strlen("frequent ");
donesomething = B_TRUE;
} else if (strstarts(p, "common ")) {
wantrarity = RR_COMMON;
p += strlen("common ");
donesomething = B_TRUE;
} else if (strstarts(p, "uncommon ")) {
wantrarity = RR_UNCOMMON;
p += strlen("uncommon ");
donesomething = B_TRUE;
} else if (strstarts(p, "rare ")) {
wantrarity = RR_RARE;
p += strlen("rare ");
donesomething = B_TRUE;
} else if (strstarts(p, "very rare ")) {
wantrarity = RR_VERYRARE;
p += strlen("very rare ");
donesomething = B_TRUE;
// weapon "goodness"
} else if (strstarts(p, "average ")) {
wantgoodness = G_AVERAGE;
p += strlen("average ");
donesomething = B_TRUE;
} else if (strstarts(p, "good ")) {
wantgoodness = G_GOOD;
if (onein(4)) wantblessed = B_BLESSED;
p += strlen("good ");
donesomething = B_TRUE;
} else if (strstarts(p, "great ")) {
wantgoodness = G_GREAT;
if (onein(3)) wantblessed = B_BLESSED;
if (onein(10)) dorandombrand = B_TRUE;
p += strlen("great ");
donesomething = B_TRUE;
} else if (strstarts(p, "excellent ")) {
wantgoodness = G_EXCELLENT;
if (onein(2)) wantblessed = B_BLESSED;
if (onein(4)) dorandombrand = B_TRUE;
p += strlen("excellent ");
donesomething = B_TRUE;
// brands
} else if (strstarts(p, "branded ")) {
dorandombrand = B_TRUE;
p += strlen("branded ");
donesomething = B_TRUE;
} else if (strstarts(p, "trapped ")) {
trapchance = 100;
p += strlen("trapped ");
donesomething = B_TRUE;
// food
} else if (strstarts(p, "tainted ")) {
wantfoodtaint = B_TRUE;
p += strlen("tainted ");
donesomething = B_TRUE;
}
}
if (strstr(p, "holy water") || strstr(p, "incompetence")) {
if (db) dblog("DB: ob is blessed (%s)",p);
wantblessed = B_BLESSED;
}
////////////////////////////////////
// handle special object names
////////////////////////////////////
if (strstr(p, "chunk of roast ") || strstr(p, "roast meat")) {
char racename[BUFLEN];
p2 = strstr(p, "roast");
p2 += 6;
p2 = readuntil(racename, p2, ' ');
corpserace = findracebyname(racename);
ot = findot(OT_ROASTMEAT);
} else if (strstr(p, "corpse")) {
int len;
char racename[BUFLEN];
p2 = strstr(p, "corpse");
len = p2 - p;
snprintf(racename, len, "%s",p);
corpserace = findracebyname(racename);
ot = findot(OT_CORPSE);
} else if (strstr(p, "map to ")) {
regiontype_t *rt;
char regionname[BUFLEN];
p2 = strstr(p, "map to ");
p2 += strlen("map to");
p2++; // go past the space
// grab name of region this map leads to
strcpy(regionname, p2);
// find the regiontype which matches this.
// if not found, it'll be randoml selected later.
rt = findregiontypebyname(regionname);
if (rt) {
// find the regionthing ID of the RT_REGIONLINK entrance
wantregionthing = findregionlink(rt->id);
}
ot = findot(OT_MAP);
} else if (strstr(p, "statue of ")) {
char racename[BUFLEN];
// go to end
p2 = strstr(p, "statue of ");
p2 += 10; // now on either 'a' or 'an'
p2++; // now on either ' ' or 'n'
for (;*p2 != ' ';p2++);
p2++;
// now at start of name
snprintf(racename, BUFLEN, "%s",p2);
corpserace = findracebyname(racename);
ot = findot(OT_STATUE);
} else if (strstr(p, " head")) {
int len;
char racename[BUFLEN];
p2 = strstr(p, " head");
len = p2 - p + 1;
snprintf(racename, len, "%s",p);
corpserace = findracebyname(racename);
ot = findot(OT_HEAD);
} else if (strstarts(p, "portal to lv")) {
char *pp;
int dlev;
pp = p + strlen("portal to lv");
dlev = atoi(pp);
if (dlev > 0) {
cell_t *c;
c = getobpilelocation(where);
if (c) {
// find map within this region with the given depth
targetmap = findregionmap(c->map->region->id, dlev);
if (!targetmap) {
// create it
targetmap = addmap();
createmap(targetmap, dlev, c->map->region, NULL, D_NONE, NULL);
}
targetcell = getrandomroomcell(targetmap, ANYROOM);
while (!cellwalkable(NULL, targetcell, NULL)) {
targetcell = getrandomadjcell(targetcell, WE_WALKABLE, B_ALLOWEXPAND);
}
} else {
// ie. adding to dummy cell
targetmap = NULL;
targetcell = NULL;
}
} else {
return NULL;
}
ot = findot(OT_PORTAL);
} else if (strstarts(p, "sign ")) {
char *pp;
pp = strchr(p, '\"');
if (pp) {
char sbuf[BUFLEN];
char *sbp;
sbp = sbuf;
pp++;
while (*pp && (*pp != '\"')) {
*sbp = *pp;
sbp++;
pp++;
}
*sbp = '\0';
signtext = strdup(sbuf);
}
ot = findot(OT_SIGN);
} else if (strstr(p, "spellbook of ")) {
char *pp;
pp = p + 13;
if (*pp) {
enum SPELLSCHOOL school;
school = findspellschoolbyname(pp);
if (school != SS_NONE) {
bookcontents = school;
}
}
ot = findot(OT_SPELLBOOK);
} else if (strstr(p, "manual of ")) {
char *pp;
pp = p + 10;
if (*pp) {
skill_t *sk;
sk = findskillbyname(pp);
if (sk) {
bookcontents = sk->id;
}
}
ot = findot(OT_MANUAL);
////////////////////////////////////
// also handle generic names
////////////////////////////////////
} else {
objectclass_t *oc;
char tempname[BUFLEN];
int found = B_FALSE;
// check for things like "weapon" or "random ring"
for (oc = objectclass; oc ; oc = oc->next) {
int i;
int matched = B_FALSE;
for (i = 0; i < oc->nnouns; i++) {
snprintf(tempname, BUFLEN, "random %s", oc->noun[i]);
if (strstarts(p, tempname) || streq(p, oc->noun[i])) {
matched = B_TRUE;
}
if (matched) {
int minrarity,maxrarity;
// want a specific rarity?
rrtorarity(wantrarity, &minrarity, &maxrarity);
ot = getrandomobofclass(oc->id, minrarity, maxrarity);
if (ot) {
found = B_TRUE;
break;
}
}
}
if (found) break;
}
if (!found) {
// look up the object name
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 );
nretobs = 0;
return NULL;
}
}
}
if (db) dblog("DB: FOUND: ot->name = '%s'", ot->name );
// check for specific brands. eg. "xxx of pyromania"
// NOTE: this will override any random brands from "branded"
for (br = firstbrand ; br ; br = br->next) {
if (strstr(localname, br->suffix)) {
// does this brand apply to this objecttype?
if (brandappliesto(br, ot)) {
wantbrand = br;
break;
}
}
}
} // end if forceoid given
////////////////////////////////////
// we now have the objecttype!
////////////////////////////////////
// don't put traps on top of stairs
if (hasflag(ot->flags, F_TRAP)) {
if (hasobwithflag(where, F_CLIMBABLE) || hasobwithflag(where, F_SHOP)) {
ot = findot(OT_BLOODSTAIN);
}
}
// don't put floor gratings on low floors
if (ot->id == OT_GRATINGFLOOR) {
if (where->where && (where->where->type->id == CT_LOWFLOOR)) {
setcelltype(where->where, where->where->map->habitat->emptycelltype);
}
}
if (gamemode != GM_LOADING) {
if (hasflag(ot->flags, F_ONEPERCELL)) {
if (hasob(where, ot->id)) {
if (db) dblog("DB: trying to add >1 ONEPERCELL object to a cell. (%s) bailing out.", ot->name);
nretobs = 0;
return NULL;
}
}
// water ob onto dirt -> mud
if (where->where) {
if ((ot->material->id == MT_WATER) || (ot->id == OT_SPLASHWATER)) {
if (where->where->type->id == CT_DIRT) {
if (!hasob(where->where->obpile, OT_MUDPOOL)) {
ot = findot(OT_MUDPOOL);
}
}
}
}
if (ot->obclass->id == OC_SPELL) {
if (where->owner || where->where) {
if (db) dblog("DB: Cannot give a spell object to a player, or a cell! object name '%s'", ot->name );
nretobs = 0;
return NULL;
}
}
}
// override blessed status from flags...
f = hasflag(ot->flags, F_STARTBLESSED);
if (f) {
wantblessed = f->val[0];
}
// don't give nopickup objects to lifeforms
if (hasflag(ot->flags, F_NOPICKUP) && where->owner) {
if (db) dblog("DB: trying to give NOPICKUP object '%s' to a lifeform ('%s').", ot->name, where->owner->race->name );
nretobs = 0;
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;
}
if (where->parentob && hasflag(where->parentob->flags, F_SHOP)) {
if (db) dblog("DB: setting canstack = false, object is going into a shop");
canstack = B_FALSE;
// also override amount
howmany = 1;
}
// 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 );
nretobs = 0;
return NULL;
}
// otherwise make sure we are only getting one
howmany = 1;
}
// we asked for a different material. is this possible?
if ((wantdiffmat != MT_NOTHING) && !hasflagval(ot->flags, F_CANBEDIFFMAT, wantdiffmat, NA, NA, NULL)) {
wantdiffmat = MT_NOTHING;
}
// chance of being a different material based on ob flags
if ((gamemode != GM_GAMESTARTED) && isplayer(where->owner)) {
// ...but not in player's initial starting equipment
} else {
if (wantdiffmat == MT_NOTHING) {
getflags(ot->flags, retflag, &nretflags, F_CANBEDIFFMAT, F_NONE);
for (i = 0; i < nretflags; i++) {
if (pctchance(retflag[i]->val[1])) {
wantdiffmat = retflag[i]->val[0]; break;
}
}
}
}
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) {
int n,found = B_FALSE;
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;
// if this isn't already in our list of added obs, add it
for (n = 0; n < nadded; n++) {
if (addedob[n] == o) {
found = B_TRUE;
break;
}
}
if (!found) {
addedob[nadded++] = o;
}
} 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->contents = addobpile(NOOWNER, NOLOC, o);
o->birthtime = curtime;
o->dying = B_FALSE;
// 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);
// don't want certain objecttype only flags...
//killflagsofid(o->flags, F_RARITY);
if (gamemode != GM_LOADING) {
// 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; // randomly selected amt
chargeflag->val[1] = f->val[1]; // highest possible amt
chargeflag->known = B_FALSE;
} else {
chargeflag = addflag_real(o->flags, F_CHARGES, amt, f->val[1], NA, NULL, PERMENANT, B_UNKNOWN, -1);
}
}
// charge flag always starts unknown.
f = hasflag(o->flags, F_CHARGES);
if (f) {
f->known = B_FALSE;
}
// 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;
}
addedob[nadded++] = o;
if (canstack) {
// stop looping through
break;
}
}
}
/*
need to do the below for _all_ objects added!
*/
for (i = 0; i < nadded; i++) {
cell_t *obloc;
o = addedob[i];
obloc = getoblocation(o);
// inc usage counter for buildings
if (gamemode > GM_VALIDATION) {
if (o->type->obclass->id == OC_BUILDING) {
for (n = 0; n < nbuildingusage; n++) {
if (buildingusage[n].oid == o->type->id) {
buildingusage[n].count++;
}
}
}
}
if (gamemode != GM_LOADING) {
if (hasflag(o->flags, F_NOBLESS)) {
setblessed(o, B_UNCURSED);
} else {
setblessed(o, wantblessed);
}
o->blessknown = B_FALSE;
if (wantdiffmat != MT_NOTHING) {
changemat(o, wantdiffmat);
}
// extra chance of bone items being cursed
if (o->type->material->id == MT_BONE) {
if (pctchance(15)) {
o->blessed = B_CURSED;
}
}
// fill in sign text
if (signtext) {
addflag(o->flags, F_SIGNTEXT, NA, NA, NA, signtext);
}
// fill in portal destinations
if (targetmap) {
int tx = NA,ty = NA;
if (targetcell) {
tx = targetcell->x;
ty = targetcell->y;
}
addflag(o->flags, F_MAPLINK, targetmap->id, tx, ty, NULL);
}
// assign gods to temples
if (o->type->id == OT_TEMPLE) {
lifeform_t *god;
god = getrandomgod();
if (god) {
addflag(o->flags, F_LINKGOD, god->race->id, NA, NA, NULL);
}
}
// fill in door flags
if (ndoorflags && isdoor(o, NULL)) {
int n;
for (n = 0; n < ndoorflags; n++) {
int val[3];
int ok = B_FALSE;
if (obloc) {
// fill in flag vals
switch (doorflag[n]) {
case F_LOCKED:
val[0] = B_TRUE;
val[1] = getdoorlockdiff(obloc->map->depth);
val[2] = NA;
ok = B_TRUE;
break;
case F_JAMMED:
val[0] = rnd(1,obloc->map->depth+5);
val[1] = B_FALSE; // not known yet
val[2] = NA;
ok = B_TRUE;
break;
case F_SECRET:
val[0] = getdoorsecretdiff(obloc->map->depth);
val[1] = NA;
val[2] = NA;
ok = B_TRUE;
break;
default:
break;
}
if (ok) {
addflag(o->flags, doorflag[n], val[0], val[1], val[2], NULL);
}
}
}
}
// fountain flags
if (o && (o->type->id == OT_FOUNTAIN)) {
f = hasflag(o->flags, F_LINKOB);
if (where->where && (where->where->habitat->id != H_VILLAGE)) {
if (onein(3)) {
objecttype_t *ot;
int min,max;
getrarityrange(where->where->map->depth, &min, &max, RARITYVARIANCEOB, B_FALSE);
// random potion type
ot = getrandomobofclass(OC_POTION, min, max);
if (ot) {
f->val[0] = ot->id;
}
} else {
f->val[0] = OT_POT_WATER;
}
}
}
// food flags
if ((ot->obclass->id == OC_FOOD) && wantfoodtaint) {
addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL);
}
// tool flags
if (o && hasflag(o->flags, F_LIGHTSOURCE) && wantlit) {
turnon(NULL, o);
}
// fill in armour size
if (o && hasflag(o->flags, F_MULTISIZE)) {
if (wantarmsize == SZ_ANY) {
// if giving to a lifeform being created, match their size
if (where->owner) {
// note: this COULD result in weird sizings other than
// medium, human or large. this is okay - other code
// will understand it. just when randomly making armour,
// only megium/human/large will appear.
wantarmsize = getlfsize(where->owner);
} else {
// random size
if (onein(4)) {
// nonstandard size
if (onein(2)) {
wantarmsize = SZ_MEDIUM;
} else {
wantarmsize = SZ_LARGE;
}
} else {
wantarmsize = SZ_HUMAN;
}
}
}
addflag(o->flags, F_ARMOURSIZE, wantarmsize, NA, NA, NULL);
}
// fill in book types
if (o && (o->type->obclass->id == OC_BOOK)) {
hiddenname_t *hn,*selhn = NULL;
int numhiddennames;
int n,sel;
if (bookcontents == -1) {
if (o->type->id == OT_SPELLBOOK) {
// pick a random school
bookcontents = getrandomspellschool();
//bookcontents = getrandomspell(maxlev);
//while (!schoolappearsinbooks(getspellschool(bookcontents))) {
// bookcontents = getrandomspell(maxlev);
// }
} else { // ie. manual
bookcontents = getrandomskill();
assert(findskill(bookcontents));
}
}
// link
if (o->type->id == OT_SPELLBOOK) {
int nspells,firstlev;
// remember the book's school (used for description)
addflag(o->flags, F_LINKSCHOOL, bookcontents, NA, NA, NULL);
// add contents to the book
if (where->owner && isplayer(where->owner) && (gamemode == GM_CHARGEN)) {
enum OBTYPE firstspell;
// giving to player at start of game
nspells = 5;
firstlev = 2;
// fixed first spell
firstspell = getfirstwizspell(bookcontents);
assert(addobfast(o->contents, firstspell));
} else {
nspells = rnd(2,5);
firstlev = rnd(1,4);
}
for (i = 0; i < nspells; i++) {
enum OBTYPE oid;
int lev;
lev = firstlev + i;
if (lev > MAXSPELLLEV) break;
oid = getrandomspellfromschool(bookcontents,lev);
if (oid != OT_NONE) {
assert(addobfast(o->contents, oid));
}
}
} else {
assert(findskill(bookcontents));
addflag(o->flags, F_MANUALOF, bookcontents, NA, NA, NULL);
}
// count hidden names
numhiddennames = 0;
for (hn = firsthiddenname ; hn ; hn = hn->next) {
if (hn->obclass == o->type->obclass->id) {
numhiddennames++;
}
}
// assign hidden name
sel = rnd(0,numhiddennames-1);
n = 0;
for (hn = firsthiddenname ; hn ; hn = hn->next) {
if (hn->obclass == o->type->obclass->id) {
if (n == sel) {
selhn = hn;
break;
}
n++;
}
}
addflag(o->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, selhn->text);
// don't call sethiddenname, because it acts on OBJECT TYPES not OBJECTS.
// all books are unique
}
// create linked holes in adjacent maps
if (wantlinkholes && o && hasflag(o->flags, F_PIT)) {
cell_t *c;
map_t *adjmap = NULL;
f = hasflag(o->flags, F_PIT);
c = getoblocation(o);
adjmap = getmapindir(c->map, f->val[0]);
if ((c->map->region->rtype->id == RG_WORLDMAP) && !adjmap) {
// ie. going down from the surface, and no dungeon below.
// ( MUST be down because holes going up make no sense! )
createregionlink(c->map, c, o, NULL, RG_PIT, c->map->region);
} else {
// create linked holes on the map at the other end of this one.
if (adjmap) {
linkholes(adjmap);
}
}
}
// other special changes we need to make based on what was
// asked for
if (o) {
// corpses - fill in details
if (o->type->id == OT_CORPSE) {
flag_t *rf, *cf;
if (!corpserace || hasflag(corpserace->flags, F_NOCORPSE)) {
// random one.
corpserace = getrandomcorpserace(NULL);
}
o->weight = corpserace->weight;
o->material = corpserace->material;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL);
// override ot_corpse nutrition flag based on race's size
rf = hasflag(corpserace->flags, F_SIZE);
if (rf) {
cf = hasflag(o->flags, F_EDIBLE);
if (cf) {
cf->val[1] = sizetonutrition(rf->val[0]);
}
}
} else if (o->type->id == OT_MAP) {
region_t *srcregion;
regiontype_t *dstrt = NULL;
int srcdepth;
char buf[BUFLEN];
// fill in map destination.
if (!wantregionthing) {
region_t *r;
regionthing_t *rthing,*poss[MAXCANDIDATES];
int nposs = 0,i;
for (r = firstregion ; r ; r = r->next) {
if (!r->outline) continue;
for (i = 0; i < r->outline->nthings; i++ ){
// pick a random regionlink thing.
rthing = &r->outline->thing[i];
if (rthing->whatkind == RT_REGIONLINK) {
regiontype_t *rtype;
rtype = findregiontype(rthing->value);
if ( (rtype->id != RG_MAINDUNGEON) &&
(rtype->id != RG_WORLDMAP)) {
poss[nposs++] = rthing;
}
}
}
}
if (nposs) {
wantregionthing = poss[rnd(0,nposs-1)];
}
}
assert(wantregionthing);
// we now have the destination regionlink thing which the
// map will lead to.
// just using this to fill in srcregion
findregionthing(wantregionthing->id, &srcregion);
srcdepth = wantregionthing->depth;
dstrt = findregiontype(wantregionthing->value);
strcpy(buf, dstrt->name);
makelowercase(buf);
addflag(o->flags, F_MAPTO, srcregion->id, srcdepth, wantregionthing->id, buf);
} else if (o->type->id == OT_STATUE) {
flag_t *f, *rf;
float ratio;
if (!corpserace) {
cell_t *where;
where = getoblocation(o);
// select random race
if (where) {
corpserace = getrandomcorpserace(where);
}
if (!corpserace) {
// ie. vending machine, or inside another object/fake cell?
corpserace = getrandomcorpserace(NULL);
}
if (corpserace->id != corpserace->baseid) corpserace = findrace(corpserace->baseid);
}
ratio = o->material->weightrating / corpserace->material->weightrating;
o->weight = corpserace->weight * ratio;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL);
// set impassable size
f = hasflag(o->flags, F_IMPASSABLE);
if (f) {
rf = hasflag(corpserace->flags, F_SIZE);
if (rf) {
f->val[0] = SZ_MIN;
f->val[1] = rf->val[0];
} else {
killflag(f);
}
}
// set ob hp
f = hasflag(o->flags, F_OBHP);
if (f) {
rf = hasflag(corpserace->flags, F_HITDICE);
if (rf) {
int maxhp;
maxhp = roll(rf->text);
f->val[0] = maxhp;
f->val[1] = maxhp;
}
}
} else if (o->type->id == OT_HEAD) {
flag_t *rf, *cf;
assert(corpserace);
o->weight = pctof(8, corpserace->weight);
limitf(&o->weight, 0.01, NA);
o->material = corpserace->material;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL);
// override ot_corpse nutrition flag based on race's size
rf = hasflag(corpserace->flags, F_SIZE);
if (rf) {
cf = hasflag(o->flags, F_EDIBLE);
if (cf) {
cf->val[1] = sizetonutrition(rf->val[0]) / 3;
}
}
} else if (o->type->id == OT_JERKY) {
if (!corpserace) {
corpserace = getrandomcorpserace(NULL);
if (corpserace->id != corpserace->baseid) corpserace = findrace(corpserace->baseid);
}
addflag(o->flags, F_LINKRACE, corpserace->id, NA, NA, NULL);
} else if (o->type->id == OT_ROASTMEAT) {
flag_t *rf, *cf;
if (!corpserace || hasflag(corpserace->flags, F_NOCORPSE)) {
// random one.
corpserace = getrandomcorpserace(NULL);
if (corpserace->id != corpserace->baseid) corpserace = findrace(corpserace->baseid);
}
o->weight = corpserace->weight / 2;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL);
// override ot_roastmeat nutrition flag based on race's size
rf = hasflag(corpserace->flags, F_SIZE);
if (rf) {
cf = hasflag(o->flags, F_EDIBLE);
if (cf) {
cf->val[1] = sizetonutrition(rf->val[0]);
}
}
}
// depth
f = hasflag(o->flags, F_DEEPWATER);
if (f && (f->val[0] != wantdepth)) {
f->val[0] = wantdepth;
}
// chance of masterwork based on wantgoodness
switch (wantgoodness) {
case G_GREAT:
if (onein(6)) {
wantom[nom++] = findobmod(OM_MASTERWORK);
}
break;
case G_EXCELLENT:
if (onein(3)) {
wantom[nom++] = findobmod(OM_MASTERWORK);
}
break;
}
for (n = 0; n < nom; n++) {
// add flags from obmod
applyobmod(o, wantom[n]);
// other effects...
switch (wantom[n]->id) {
case OM_FLAMING: // flaming weapons are immune to fire
if (o->type->obclass->id == OC_WEAPON) {
if (!isimmuneto(o->flags, DT_FIRE, B_FALSE)) {
addflag(o->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL);
}
}
break;
case OM_FROZEN:
if (o->material->id != MT_FIRE) { // fire can't be frozen!
// made of ice
// note: not using changemat() here to avoid adding f_frozen twice.
o->material = findmaterial(MT_ICE);
}
break;
case OM_MASTERWORK:
if (isweapon(o) || isarmour(o) || isdoor(o, NULL)) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] *= 2;
f->val[1] *= 2;
}
}
break;
case OM_SHODDY:
if (isweapon(o) || isarmour(o) || isdoor(o, NULL)) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] /= 2; if (f->val[0] < 1) f->val[0] = 1;
f->val[1] /= 2; if (f->val[1] < 1) f->val[1] = 1;
}
}
break;
default:
break;
}
}
// if no bonus yet, get one based on 'wantgoodness'
if (!bonus) {
switch (wantgoodness) {
case G_GOOD: // 1 - 2
bonus = 1;
if (onein(2)) bonus++;
break;
case G_GREAT: // 1 - 3
bonus = 1;
while (onein(2) && (bonus < 3)) {
bonus++;
}
break;
case G_EXCELLENT: // 1 - 4
bonus = 1;
while (onein(2) && (bonus < 4)) {
bonus++;
}
break;
default: // no bonus
break;
}
}
if (bonus && hasflag(o->flags, F_ENCHANTABLE)) {
// for swords, armour etc
addflag_real(o->flags, F_BONUS, bonus, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1);
}
// special rings which get randomized...
if (o->type->id == OT_RING_CON) {
flag_t *f;
f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_CON, NA, NULL);
if (f) {
if (bonus) f->val[2] = bonus;
else f->val[2] = rnd(1,3);
}
if (o->blessed == B_CURSED) f->val[2] *= -1;
} else if (o->type->id == OT_RING_DEX) {
flag_t *f;
f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_AGI, NA, NULL);
if (f) {
if (bonus) f->val[2] = bonus;
else f->val[2] = rnd(1,3);
}
if (o->blessed == B_CURSED) f->val[2] *= -1;
} else if (o->type->id == OT_RING_IQ) {
flag_t *f;
f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_IQ, NA, NULL);
if (f) {
if (bonus) f->val[2] = bonus;
else f->val[2] = rnd(1,3);
}
if (o->blessed == B_CURSED) f->val[2] *= -1;
} else if (o->type->id == OT_RING_STR) {
flag_t *f;
f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, NA, NULL);
if (f) {
if (bonus) f->val[2] = bonus;
else f->val[2] = rnd(1,3);
}
if (o->blessed == B_CURSED) f->val[2] *= -1;
}
// now apply a random brand if we wanted one
if (!wantbrand && dorandombrand) {
wantbrand = getrandombrandfor(ot);
}
}
// apply the brand
if (wantbrand) {
if (brandappliesto(wantbrand, o->type)) {
copyflags(o->flags, wantbrand->flags, FROMBRAND);
addflag(o->flags, F_HASBRAND, wantbrand->id, NA, NA, NULL);
if (pctchance(wantbrand->blesschance)) {
o->blessed = wantbrand->blessed;
}
}
}
if (where->owner) {
// new owner gains "hold confer" flags conferred by this object
giveobflags(where->owner, o, F_HOLDCONFER);
}
// special cases
// ie. don't do these things when just creating objects
// for validation purposes
if (obloc) {
// containers
if (hasflag(o->flags, F_CONTAINER)) {
if (getoblocation(o)) {
givestartobs(NULL, o, o->flags);
}
}
// locked?
if (!lockchance) {
f = hasflag(o->flags, F_CANBELOCKED);
if (f) {
lockchance = f->val[0] + (f->val[1] * (obloc->map->depth/5));
limit(&lockchance, f->val[0], 100);
}
}
if (lockchance && pctchance(lockchance)) {
addflag(o->flags, F_LOCKED, B_TRUE, getdoorlockdiff(obloc->map->depth), NA, NULL);
}
// trapped? l1=10%, l5=20%, l10=30%, l15=40%, l20+=60%
if (!trapchance) {
f = hasflag(o->flags, F_CANBETRAPPED);
if (f) {
trapchance = f->val[0] + (f->val[1] * (obloc->map->depth/5));
limit(&trapchance, f->val[0], f->val[2]);
}
}
if (trapchance && pctchance(trapchance)) {
enum OBTYPE traptype;
// get a random trap
//
traptype = getrandomtrapforob();
addflag(o->flags, F_TRAPPED, traptype, NA, NA, NULL);
}
}
if (o->pile->where) {
vault_t *v;
v = getcellvault(o->pile->where);
// apply cost to shop items
if (v && hasflag(v->flags, F_VAULTISSHOP)) {
if (canpickup(NULL, o, 1)) {
addflag(o->flags, F_SHOPITEM, getobvalue(o), getroomid(o->pile->where), NA, NULL);
}
}
// blood will stain things on the ground
if (o->material->id == MT_BLOOD) {
object_t *poss[MAXPILEOBS],*oo;
int nposs = 0;
// bloodstain one piece of armour
for (oo = o->pile->first ; oo ; oo = oo->next) {
if ((oo != o) && isarmour(oo)) {
poss[nposs++] = oo;
}
}
if (nposs) {
oo = poss[rnd(0,nposs-1)];
applyobmod(oo, findobmod(OM_BLOODSTAINED));
}
}
}
} // end if !loading
if ((gamemode == GM_GAMESTARTED) && !inaskcoords) {
if (o && where->where && !hasflag(o->flags, F_NOGLYPH) && haslos(player, where->where) ) {
needredraw = B_TRUE;
}
}
} // end foreach added object
// don't need the name anymore.
if (localname) free(localname);
// populate retobs
for (i = 0; i < nadded; i++) {
if (addedob[i]) {
retobs[i] = addedob[i];
retobscount[i] = addedob[i]->amt;
}
}
nretobs = nadded;
// return the first object given
return addedob[0];
}
// add objects in a circle
// returns # added.
int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fromlf, enum LOFTYPE needlof) {
int nadded = 0;
cell_t *cell[MAXCANDIDATES];
int ncells,i;
if (!where) return 0;
redrawpause();
getradiuscells(where, range, dirtype, B_FALSE, needlof, B_TRUE, cell, &ncells, B_FALSE);
for (i = 0; i < ncells; i++) {
cell_t *c;
c = cell[i];
if (!c->type->solid) {
object_t *o;
o = addob(c->obpile, name);
if (o) {
if (fromlf) setobcreatedby(o, fromlf);
nadded++;
}
}
}
redrawresume();
return nadded;
}
obmod_t *addobmod(enum OBMOD id, char *prefix) {
obmod_t *a, *om;
char buf[BUFLEN];
//flag_t *f;
// does this modj already exist?
om = findobmod(id);
assert(!om);
// add to the end of the list
if (firstobmod == NULL) {
firstobmod = malloc(sizeof(obmod_t));
a = firstobmod;
a->prev = NULL;
} else {
// go to end of list
a = lastobmod;
a->next = malloc(sizeof(obmod_t));
a->next->prev = a;
a = a->next;
}
lastobmod = a;
a->next = NULL;
// props
a->id = id;
snprintf(buf, BUFLEN, "%s ",prefix);
a->prefix = strdup(buf);
a->flags = addflagpile(NULL, NULL);
return a;
}
obpile_t *addobpile(lifeform_t *owner, cell_t *where, object_t *parentob) {
obpile_t *op;
int i;
op = malloc(sizeof(obpile_t));
op->first = NULL;
op->last = NULL;
op->owner = owner;
op->where = where;
op->parentob = parentob;
for (i = 0;i < MAXPILEOBS; i++) {
op->oblist[i] = -1;
}
return op;
}
void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes) {
cell_t *cell[MAXCANDIDATES],*c;
int ncells,i;
objecttype_t *ot;
ot = findotn(name);
if (!ot) return;
getradiuscells(centre, radius, DT_ORTH, B_FALSE, LOF_WALLSTOP, (radius == 0) ? B_TRUE : B_FALSE, cell, &ncells, B_FALSE);
for (i = 0; i < ncells; i++) {
c = cell[i];
if (allowdupes || !hasob(c->obpile, ot->id)) {
addob(c->obpile, name);
}
}
}
objecttype_t *addot(enum OBTYPE id, char *name, char *description, int material, float weight, int obclassid, enum LFSIZE size) {
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;
if (a->id == OT_BERRY) {
int a;
a = 2;
}
a->name = strdup(name);
a->desc = strdup(description);
a->material = findmaterial(material);
a->weight = weight;
a->size = size;
a->obclass = findoc(obclassid);
a->flags = addflagpile(NULL, NULL);
// inherit flags from object class
copyflags(a->flags, a->obclass->flags, NA);
// ...but don'to inherit rarity
killflagsofid(a->flags, F_RARITY);
if (a->material) {
// inherit flags from material
copyflags(a->flags, a->material->flags, FROMMAT);
}
// for easy addition of flags
lastot = a;
return a;
}
// usage: addrecipe(result, [ ingred_id, ingred_count, ingred_willbeconsumed ], OT_NONE)
recipe_t *addrecipe(enum OBTYPE result, ...) {
recipe_t *a, *recipe_exists;
va_list ingreds;
enum OBTYPE thisob;
//flag_t *f;
// does this ob already exist?
recipe_exists = findrecipefor(result);
assert(!recipe_exists);
// add to the end of the list
if (firstrecipe == NULL) {
firstrecipe = malloc(sizeof(recipe_t));
a = firstrecipe;
a->prev = NULL;
} else {
// go to end of list
a = lastrecipe;
a->next = malloc(sizeof(recipe_t));
a->next->prev = a;
a = a->next;
}
lastrecipe = a;
a->next = NULL;
// props
a->result = result;
va_start(ingreds, result);
a->ningredients = 0;
thisob = va_arg(ingreds, enum OBTYPE);
while (thisob != OT_NONE) {
a->ingredient[a->ningredients] = thisob;
a->count[a->ningredients] = va_arg(ingreds, int);
a->consume[a->ningredients] = va_arg(ingreds, int);
a->ningredients++;
assert(a->ningredients < MAXRECIPEINGREDIENTS);
thisob = va_arg(ingreds, enum OBTYPE);
}
va_end(ingreds);
return a;
}
void adjustdamhardness(int *dam, enum DAMTYPE damtype, enum MATERIAL mat) {
// now check for hardness
if (isphysicaldam(damtype)) {
*dam -= gethardness(mat);
if (*dam < 0) *dam = 0;
/*
if (*dam < gethardness(mat)) {
*dam = 0;
}
*/
}
}
// adjust damage based on material being damaged
void adjustdammaterial(int *dam, enum DAMTYPE damtype, enum MATERIAL mat) {
// adjust based on material
if (mat == MT_MAGIC) {
switch (damtype) {
case DT_DIRECT:
case DT_NONE:
break;
default:
*dam = 0;
return;
}
} else if (mat == MT_GAS) {
switch (damtype) {
case DT_HOLY:
case DT_DIRECT:
case DT_NONE:
break;
default:
*dam = 0;
return;
}
} else if (mat == MT_DRAGONWOOD) {
switch (damtype) {
case DT_FIRE:
case DT_MELT:
case DT_DECAY:
case DT_COLD:
case DT_ELECTRIC:
case DT_HOLY:
case DT_WATER:
*dam = 0;
return;
default:
break;
}
}
// adjust based on damage type
if (damtype == DT_FIRE) {
switch (mat) {
case MT_WOOD:
case MT_ICE:
case MT_SLIME:
*dam *= 2;
break;
case MT_METAL:
case MT_BONE:
case MT_STONE:
case MT_BLOOD:
case MT_FIRE: // immune to itself
*dam = 0;
break;
default:
break;
}
} else if (damtype == DT_CHOP) {
switch (mat) {
case MT_WOOD:
*dam *= 2;
break;
case MT_SLIME:
*dam /= 2;
break;
default:
break;
}
} else if (damtype == DT_SLASH) {
switch (mat) {
case MT_WOOD:
case MT_SLIME:
case MT_BONE:
*dam /= 2;
break;
default:
break;
}
} else if (damtype == DT_PIERCE) {
switch (mat) {
case MT_WOOD:
case MT_SLIME:
case MT_BONE:
*dam /= 2;
break;
default:
break;
}
} else if (damtype == DT_BASH) {
switch (mat) {
case MT_PAPER:
*dam = 0;
break;
default:
break;
}
} else if (damtype == DT_WATER) {
switch (mat) {
case MT_WATER: // immune to itself
*dam = 0;
break;
default:
break;
}
}
}
void adjustdamob(object_t *o, int *dam, enum DAMTYPE damtype) {
// objects can't get hurt the turn they
// were created.
if (o->birthtime == curtime) {
*dam = 0;
return;
}
// only some objects can be hurt
if (!hasflag(o->flags, F_DAMAGABLE)) {
if (damtype != DT_DIRECT) {
*dam = 0;
return;
}
}
if (hasflag(o->flags, F_INVULNERABLE)) {
*dam = 0;
return;
}
// immune?
if (isimmuneto(o->flags, damtype, B_FALSE)) {
*dam = 0;
return;
}
if (isresistantto(o->flags, damtype, B_FALSE)) {
// no resistances etc if rusty...
if (!hasflag(o->flags, F_RUSTED)) {
*dam /= 2;
}
}
if (isvulnto(o->flags, damtype, B_FALSE)) {
*dam *= 2;
}
// some damage types never hurts objects
if (damtype == DT_POISONGAS) {
*dam = 0;
return;
}
if (damtype == DT_WATER) {
if (!isvulnto(o->flags, damtype, B_FALSE)) {
*dam = 0;
return;
}
}
// adjust damage
if (o->blessed == B_BLESSED) {
// high chance of no hp loss
if (pctchance(90)) {
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;
}
*dam = 0;
return;
}
} else if (o->blessed == B_CURSED) {
// 50% chance of double damage!
if (onein(2)) {
lifeform_t *owner;
(*dam) *= 2;
owner = o->pile->owner;
if (owner && isplayer(owner)) {
// 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 & hardness too
adjustdammaterial(dam, damtype, o->material->id);
adjustdamhardness(dam, damtype, o->material->id);
}
// adjusts armour's ac//evasion penalty based on skill
int adjustarmourpenalty(lifeform_t *lf, float amt) {
enum SKILLLEVEL slev;
// no penalties for monsters
if (!isplayer(lf)) {
return 0;
}
slev = getskill(lf, SK_ARMOUR);
amt -= (slev*10);
limitf(&amt, 0, NA);
return amt;
}
// adjusts shield's accuracy penalty based on skill
int adjustshieldpenalty(lifeform_t *lf, float amt) {
enum SKILLLEVEL slev;
// monsters suffer no penalties
if (!isplayer(lf)) {
return 0;
}
slev = getskill(lf, SK_SHIELDS);
switch (slev) {
case PR_INEPT:
amt *= 3;
break;
default:
amt -= (5*slev);
break;
}
limitf(&amt, 0, NA);
return amt;
}
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);
}
}
void applyobmod(object_t *o, obmod_t *om) {
flag_t *f;
if (hasobmod(o, om)) {
return;
}
if (om->id == OM_MASTERWORK) {
if (hasflag(o->flags, F_NOQUALITY) || hasflag(o->flags, F_SHODDY)) {
return;
}
}
if (om->id == OM_SHODDY) {
if (hasflag(o->flags, F_NOQUALITY) || hasflag(o->flags, F_MASTERWORK)) {
return;
}
}
if (om->id == OM_FROZEN) {
// frozen things don't decay...
f = hasflagval(o->flags, F_OBHPDRAIN, NA, DT_DECAY, NA, NULL);
if (f) {
killflag(f);
}
// ...but they do melt!
f = addtempflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL, FROMOBMOD);
// it needs HP to 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, FROMOBMOD);
}
if (!hasflag(o->flags, F_DAMAGABLE)) {
addtempflag(o->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL, FROMOBMOD);
}
}
copyflags(o->flags, om->flags, FROMOBMOD);
}
int blessob(object_t *o) {
char obname[BUFLEN];
int rv = B_FALSE;
lifeform_t *owner;
if (hasflag(o->flags, F_NOBLESS)) return B_TRUE;
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 (cansee(player, owner)) {
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;
char buf[BUFLEN];
sprintf(buf, "You see an intense flash of light!");
animradial(centre, range, '}', DT_ORTH, C_WHITE, buf, buf);
// 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));
}
}
}
}
}
void calcshopprice(object_t *o, flag_t *shopitemflag) {
// initial value
shopitemflag->val[0] = (int) getshopprice(o, o->pile->owner);
}
int canbepoisoned(enum OBTYPE oid) {
flag_t *f;
objecttype_t *ot;
ot = findot(oid);
if (!ot) return B_FALSE;
if (ot->obclass->id != OC_WEAPON) return B_FALSE;
f = hasflag(ot->flags, F_DAM);
if (f) {
switch (f->val[0]) {
case DT_SLASH:
case DT_PIERCE:
return B_TRUE;
default:
break;
}
}
if (hasflagval(ot->flags, F_CANHAVEOBMOD, OM_POISONED, NA, NA, NULL)) {
return B_TRUE;
}
return B_FALSE;
}
int canseeob(lifeform_t *lf, object_t *o) {
flag_t *f;
cell_t *obloc;
if (gamemode != GM_GAMESTARTED) {
return B_TRUE;
}
if (hasflag(o->flags, F_INVISOB)) return B_FALSE;
obloc = getoblocation(o);
if (hasflag(o->flags, F_SECRET) && isplayer(lf)) {
// can't see
return B_FALSE;
}
if (o->pile->where) {
object_t *blockob;
blockob = hasobwithflag(o->pile, F_BLOCKSVIEW);
if (blockob && (blockob != o)) {
return B_FALSE;
}
}
f = hasflag(o->flags, F_TRAIL);
if (f) {
if (f->val[2] == S_SIGHT) {
enum SKILLLEVEL slev;
int cutoffpct;
int cutoff;
slev = getskill(lf, SK_PERCEPTION);
switch (slev) {
case PR_NOVICE: cutoffpct = 80; break;
case PR_BEGINNER: cutoffpct = 65; break;
case PR_ADEPT: cutoffpct = 50; break;
case PR_SKILLED: cutoffpct = 35; break;
case PR_EXPERT: cutoffpct = 20; break;
case PR_MASTER: cutoffpct = 0; break;
default:
case PR_INEPT: cutoffpct = 200; break;
}
cutoff = pctof(cutoffpct, TM_FOOTPRINT);
if (f->lifetime >= cutoff) {
return B_TRUE;
} else {
return B_FALSE;
}
} else {
int smellrange;
// ie. SCENT
// special case: if lf is the player's pet, they can always "smell" the player
if (ispetof(lf, player) && strlen(f->text)) {
// scent belongs to the player?
if (atoi(f->text) == player->id) {
return B_TRUE;
}
}
smellrange = getsmellrange(lf);
// can't smell your own race...
if ((f->val[0] != lf->race->id) && smellrange && (getcelldist(lf->cell, obloc) <= smellrange)) {
return B_TRUE;
} else {
return B_FALSE;
}
}
}
if (isblind(player) && hasflag(o->flags, F_NOFEEL)) {
return B_FALSE;
}
return B_TRUE;
}
// does the pile "op" have an object we can
// stack "match" with
object_t *canstackob(obpile_t *op, object_t *match) {
object_t *o;
flag_t *f;
if (!hasflag(match->flags, F_STACKABLE)) {
return NULL;
}
// can't stack damaged objects
f = hasflag(match->flags, F_OBHP);
if (f) {
if (f->val[0] != f->val[1]) 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) {
int ok = B_TRUE;
if (!isplainob(o)) ok = B_FALSE;
if (ok) {
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 && cansee(player, o->pile->owner)) {
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(o->flags, m->flags, FROMMAT);
// other stuff...
if (mat != MT_FLESH) {
killflagsofid(o->flags, F_ISMEAT);
}
if (mat == MT_ICE) {
obmod_t *om;
// if it turned to ice..
// it stops burning
extinguish(o);
// it will melt...
// note that it is frozen
om = findobmod(OM_FROZEN);
applyobmod(o, om);
}
return B_FALSE;
}
int getdr(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_DAM);
if (f) {
return f->val[1];
}
return 0;
}
// returns true if the object protected you
int checkcritprotection(lifeform_t *lf, object_t *o) {
if (pctchance(getcritprotection(o))) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
/*
if (isplayer(lf)) {
msg("Your %s protects you.", noprefix(obname));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s protects it.", lfname, getpossessive(lfname), noprefix(obname));
}
*/
return B_TRUE;
}
return B_FALSE;
}
int checkobnames(char *haystack, char *needle) {
char *pluralname;
int db = B_FALSE;
// search for exact match
if (!strcmp(haystack, needle)) {
// found it!
if (db) dblog("checkobnames(): got exact match: '%s'",haystack);
return B_TRUE;
}
// search for words ending in "s" (eg. "swords")
pluralname = strdup(needle);
//if (db) dblog("findotn(): plural is '%s'",pluralname);
if (pluralname[strlen(pluralname)-1] == 's') {
// remove trailing 's'
pluralname[strlen(pluralname)-1] = '\0';
// search again (for exact matches)
if (!strcmp(haystack, pluralname)) {
if (db) dblog("checkobnames(): got match after stripping 's': '%s' -> '%s'",pluralname, needle);
free(pluralname);
return B_TRUE;
}
// search for words ending in "es" (eg. tomatoes)
if ((pluralname[strlen(pluralname)-1] == 'e') &&
(pluralname[strlen(pluralname)-2] == 'o')
) {
// remove trailing 'es'
pluralname[strlen(pluralname)-1] = '\0';
pluralname[strlen(pluralname)-2] = '\0';
//if (db) dblog("findotn(): pluralname without 'es' is '%s'",pluralname);
// search again
if (!strcmp(haystack, pluralname)) {
if (db) dblog("checkobnames(): got match after stripping 'es': '%s' -> '%s'",pluralname, needle);
free(pluralname);
return B_TRUE;
}
}
}
free(pluralname);
return B_FALSE;
}
void colourmatchob(object_t *o, lifeform_t *lf) {
flag_t *f;
glyph_t *glyph;
enum COLOUR wantcol;
glyph = getlfglyph(lf);
wantcol = glyph->colour;
f = hasflag(o->flags, F_GLYPH);
if (f) {
f->val[0] = wantcol;
} else {
glyph = getglyph(o);
addflag(o->flags, F_GLYPH, wantcol, glyph->ch, NA, NULL);
}
}
void copyobprops(object_t *dst, object_t *src) {
dst->material = src->material;
dst->weight = src->weight;
if (src->inscription) {
dst->inscription = strdup(src->inscription);
}
setblessed(dst, src->blessed);
dst->blessknown = src->blessknown;
// copy flags
while (dst->flags->first) {
killflag(dst->flags->first);
}
copyflags(dst->flags, src->flags, NA);
}
int counthiddennames(enum OBCLASS ocid, char *text) {
int count = 0;
hiddenname_t *hn;
for (hn = firsthiddenname ; hn ; hn = hn->next) {
if ((hn->obclass == ocid) && streq(hn->text, text)) {
count++;
}
}
return count;
}
int countmoney(obpile_t *op) {
object_t *o;
int amt = 0;
for (o = op->first ; o ; o = o->next) {
if (o->type->id == OT_GOLD) {
amt += o->amt;
}
}
return amt;
}
int countnames(char **list) {
int count;
for (count = 0; list[count]; count++);
return count;
}
int countobs(obpile_t *op, int onlyifknown) {
object_t *o;
int count = 0;
for (o = op->first ; o ; o = o->next) {
if (onlyifknown) {
if (canseeob(player, o)) {
count++;
}
} else {
count++;
}
}
return count;
}
int countobsoftype(obpile_t *op, enum OBTYPE oid) {
object_t *o;
int count = 0;
for (o = op->first ; o ; o = o->next) {
if (o->type->id == oid) {
count++;
}
}
return count;
}
int countobswithflag(obpile_t *op, enum FLAG flagid) {
object_t *o;
int count = 0;
for (o = op->first ; o ; o = o->next) {
if (hasflag(o->flags, flagid)) count++;
}
return count;
}
int countobswithflagval(obpile_t *op, enum FLAG flagid, int val0, int val1, int val2, char *text) {
object_t *o;
int count = 0;
for (o = op->first ; o ; o = o->next) {
if (hasflagval(o->flags, flagid, val0, val1, val2, text)) count++;
}
return count;
}
int countnoncosmeticobs(obpile_t *op, int onlyifknown) {
object_t *o;
int count = 0;
for (o = op->first ; o ; o = o->next) {
if (!hasflag(o->flags, F_COSMETIC) && !hasflag(o->flags, F_SECRET)) {
if (onlyifknown) {
if (canseeob(player, o)) {
count++;
}
} else {
count++;
}
}
}
return count;
}
int curseob(object_t *o) {
int rv = B_FALSE;
lifeform_t *lf;
lf = o->pile->owner;
// announce
if (gamemode == GM_GAMESTARTED) {
if (lf) {
if (cansee(player, lf)) {
char lfname[BUFLEN];
char obname[BUFLEN];
getlfname(lf, lfname);
getobname(o, obname,o->amt);
msg("A black aura surrounds %s%s %s.",lfname,getpossessive(lfname),noprefix(obname));
}
} else { // not held
cell_t *loc = NULL;
loc = getoblocation(o);
if (haslos(player, loc)) {
char obname[BUFLEN];
getobname(o, obname,o->amt);
msg("A black aura surrounds %s.",obname);
}
}
}
switch (o->blessed) {
case B_BLESSED: // uncurse it
setblessed(o, B_UNCURSED);
break;
case B_CURSED: // nothing happens
rv = B_TRUE;
break;
case B_UNCURSED: // curse it
setblessed(o, B_CURSED);
break;
}
return rv;
}
void damageallobs(object_t *srcob, obpile_t *op, int howmuch, int damtype) {
object_t *o, *nexto;
for (o = op->first ; o ; o = nexto) {
nexto = o->next;
// special case to stop steam from hurting water
if (srcob && (srcob->material->id == MT_GAS)) {
continue;
}
if ((o != srcob) && !hasflag(o->flags, F_DEAD)) {
takedamage(o, howmuch, damtype);
}
}
}
// returns TRUE if something happened
int doobdieconvert(object_t *o, int wantannounce) {
flag_t *f;
f = hasflag(o->flags, F_DIECONVERT);
if (f) {
flag_t *f2;
object_t *newob;
char desc[BUFLEN];
if (wantannounce && !hasflag(o->flags, F_NODIECONVERTTEXT)) {
char obname[BUFLEN];
// announce the change
real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE, B_FALSE);
strcpy(desc, "");
f2 = NULL;
if (o->amt > 1) {
f2 = hasflag(o->flags, F_DIECONVERTTEXTPL);
}
if (!f2) {
f2 = hasflag(o->flags, F_DIECONVERTTEXT);
}
if (f2) {
snprintf(desc, BUFLEN, "%s", f2->text);
} else if (oblastdamtype(o) == DT_DECAY) {
// don't announce devay death while traning
if (!lfhasflag(player, F_TRAINING)) {
snprintf(desc, BUFLEN, "%s completed rotted away", OB1(o,"has","have"));
}
} else {
snprintf(desc, BUFLEN, "%s destroyed", OB1(o,"is","are"));
}
if (strlen(desc)) {
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
if (!lfhasflag(o->pile->owner, F_ASLEEP)) {
msg("Your %s %s!",noprefix(obname), desc);
}
} else if (cansee(player, o->pile->owner)) {
// don't announce decay death unless we are holding it
if (oblastdamtype(o) != DT_DECAY) {
char monname[BUFLEN];
getlfname(o->pile->owner, monname);
msg("%s's %s %s!",monname, noprefix(obname), desc);
}
}
} else if (haslos(player, o->pile->where)) {
// don't announce decay death unless we are holding it
if (oblastdamtype(o) != DT_DECAY) {
capitalise(obname);
msg("%s %s.",obname, desc);
}
}
} // end if desc != ""
}
// change into something else
newob = addob(o->pile, f->text);
// only set amt if text wasn't "x-y somethings"
if (newob && !strchr(f->text, '-')) {
newob->amt = o->amt;
}
if (f->val[0] > 0) {
cell_t *centre;
centre = getoblocation(o);
if (centre) {
cell_t *cell[MAXCANDIDATES];
int ncells,i;
getradiuscells(centre, f->val[0], DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, cell, &ncells, B_FALSE);
for (i = 0; i < ncells; i++) {
newob = addob(cell[i]->obpile, f->text);
if (newob && !strchr(f->text, '-')) {
newob->amt = o->amt;
}
}
}
}
return B_TRUE;
}
return B_FALSE;
}
int doobtraps(object_t *o, lifeform_t *lf) {
flag_t *f;
f = hasflag(o->flags, F_TRAPPED);
if (f) {
enum OBTYPE trapid;
// announce...
if (isplayer(lf)) {
msg("^wA trap goes off!"); more();
}
trapid = f->val[0];
trapeffects(NULL, trapid, lf->cell);
killflag(f); // now the trap gets removed
// explosion traps kill the object
if (trapid == OT_TRAPMINE) {
killob(o);
}
return B_TRUE;
}
return B_FALSE;
}
void dumpobrarity(void) {
enum RARITY rr;
objecttype_t *ot;
flag_t *f;
int min,max;
for (rr = RR_FREQUENT; rr <= RR_VERYRARE; rr++) {
rrtorarity(rr, &min, &max);
dblog("Obs with rarity %s:", getrarityname(rr));
for (ot = objecttype ; ot ; ot = ot->next) {
if (ot->obclass->id == OC_ARMOUR) {
int thisrar;
f = hasflag(ot->flags, F_RARITY);
if (!f) continue;
thisrar = f->val[1];
if ((thisrar >= min) && (thisrar <= max)) {
dblog("\t%s", ot->name);
}
}
}
}
}
void explodeob(object_t *o, flag_t *f, int bigness) {
cell_t *c;
int dam;
char obname[BUFLEN];
dam = roll(f->text);
c = getoblocation(o);
getobname(o, obname, o->amt);
// announce
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s explode%s!", noprefix(obname),OBS1(o));
} else if (cansee(player, o->pile->owner)) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
msg("%s%s %s explode%s!", lfname, getpossessive(lfname), noprefix(obname),OBS1(o));
}
} else if (haslos(player, c)) {
msg("%s explode%s!", obname,OBS1(o));
}
explodecells(c, dam * o->amt, bigness ? B_TRUE : B_FALSE, o, bigness , DT_ORTH, B_FALSE);
// object dies.
removeob(o, o->amt);
}
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),
OB1(o,"is","are"));
} else if (cansee(player, o->pile->owner)) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
msg("%s%s %s %s extinguished.", lfname, getpossessive(lfname), noprefix(obname),
OB1(o,"is","are"));
}
} else if (o->pile->where && haslos(player, o->pile->where)) {
getobname(o, obname, o->amt);
msg("%s %s extinguished.", obname,
OB1(o,"is","are"));
}
killflag(f);
}
}
material_t *findmaterial(int id) {
material_t *m;
for (m = material ; m ; m = m->next) {
if (m->id == id) return m;
}
return NULL;
}
objectclass_t *findoc(int id) {
objectclass_t *oc;
for (oc = objectclass ; oc ; oc = oc->next) {
if (oc->id == id) return oc;
}
return NULL;
}
object_t *findobbyid(obpile_t *op, long oid) {
object_t *o;
for (o = op->first; o ; o = o->next) {
if (o->id == oid) return o;
}
return NULL;
}
object_t *findobl(obpile_t *op, char let) {
object_t *o;
for (o = op->first; o ; o = o->next) {
if (o->letter == let) return o;
}
return NULL;
}
brand_t *findbrand(enum BRAND id) {
brand_t *om;
for (om = firstbrand ; om ; om = om->next) {
if (om->id == id) return om;
}
return NULL;
}
obmod_t *findobmod(enum OBMOD id) {
obmod_t *om;
for (om = firstobmod ; om ; om = om->next) {
if (om->id == id) return om;
}
return NULL;
}
objecttype_t *findot(enum OBTYPE id) {
objecttype_t *ot;
for (ot = objecttype ; ot ; ot = ot->next) {
if (ot->id == id) return ot;
}
return NULL;
}
objecttype_t *findotn(char *name) {
objecttype_t *ot;
knowledge_t *k;
char *modname;
char *p;
int db = B_FALSE;
brand_t *om;
if (!strlen(name)) {
return NULL;
}
modname = strdup(name);
// make some replacements
//replace scrolls with scroll etc
modname = strrep(modname, "bags ", "bag ", NULL);
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, "cloves ", "clove ", NULL);
modname = strrep(modname, "flasks ", "flask ", NULL);
modname = strrep(modname, "gems ", "gem ", NULL);
modname = strrep(modname, "knives", "knife", NULL);
modname = strrep(modname, "leaves", "leaf", 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, "pinches ", "pinch ", NULL);
modname = strrep(modname, "pools ", "pool ", NULL);
modname = strrep(modname, "potions ", "potion ", NULL);
modname = strrep(modname, "puddles ", "puddle ", NULL);
modname = strrep(modname, "puffs ", "puff ", NULL);
modname = strrep(modname, "rings ", "ring ", NULL);
modname = strrep(modname, "rubies", "ruby", NULL);
modname = strrep(modname, "scrolls ", "scroll ", NULL);
modname = strrep(modname, "sets ", "set ", NULL);
modname = strrep(modname, "splashes ", "splash ", NULL);
modname = strrep(modname, "sprigs ", "sprig ", NULL);
modname = strrep(modname, "suits ", "suit ", NULL);
modname = strrep(modname, "vials ", "vial ", NULL);
// only at start...
if (strstr(modname, "the ") == modname) modname = strrep(modname, "the ", "", NULL);
if (strstr(modname, "an ") == modname) modname = strrep(modname, "an ", "", NULL);
if (strstr(modname, "a ") == modname) modname = strrep(modname, "a ", "", NULL);
modname = strrep(modname, "blessed ", "", NULL);
modname = strrep(modname, "uncursed ", "", NULL);
modname = strrep(modname, "cursed ", "", NULL);
//realloc(modname, strlen(temp)); strcpy(modname, temp); free(temp); // swap
// strip out pre mods
modname = strrep(modname, "flaming ", "", NULL);
modname = strrep(modname, "headless ", "", NULL);
// strip out brands (but not only for certain object classes)
for (om = firstbrand; om ; om = om->next) {
modname = strrep(modname, om->suffix, "", NULL);
}
// special cases
modname = strrep(modname, "holy water", "water", NULL);
modname = strrep(modname, "incompetence", "competence", NULL);
// skip past bonusses
p = strchr(modname, '+');
if (p) {
while (!isalpha(*p)) {
p++;
}
strcpy(modname, p);
}
p = strchr(modname, '-');
if (p) {
while (!isalpha(*p)) {
p++;
}
strcpy(modname, p);
}
if (db) dblog("findotn(): modname is '%s'",modname);
// check for exact matches on real name (and plural versions) first
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id != OC_SPELL) && (ot->obclass->id != OC_ABILITY)) {
if (checkobnames(ot->name, modname)) {
free(modname);
return ot;
}
}
}
// then matches on hidden name
for (k = knowledge; k ; k = k->next) {
if (checkobnames(k->hiddenname, modname)) {
free(modname);
// found it!
return findot(k->id);
}
}
// then partial matches on real name
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id != OC_SPELL) && (ot->obclass->id != OC_ABILITY)) {
if (strstr(ot->name, modname)) {
free(modname);
// found it!
return ot;
}
}
}
free(modname);
return NULL;
}
// ie. "the apple is xxx"
// ie. "the apples are xxx"
char *getfillingname(int nutrition) {
if (nutrition > 100) {
return "extremely substantial";
} else if (nutrition >= 90) {
return "very filling";
} else if (nutrition >= 70) {
return "ample for a meal";
} else if (nutrition >= 50) {
return "enough for a light meal";
} else if (nutrition >= 25) {
return "snack-sized";
} else if (nutrition > 0) {
return "barely worth eating";
}
// ie. < 0
return "of zero nutritional substance";
}
int getfirearmrange(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_RANGE);
if (f) {
return f->val[0];
}
return 0;
}
int getfirearmspeed(object_t *o) {
int firespeed;
flag_t *f;
f = hasflag(o->flags, F_FIRESPEED);
if (f) {
firespeed = f->val[0];
} else {
// default to very slow
firespeed = 1;
}
return firespeed;
}
glyph_t *getglyph(object_t *o) {
flag_t *f;
int isopen;
int g = ' '; // default
int col = C_GREY;
cell_t *obloc;
obloc = getoblocation(o);
if (isdoor(o, &isopen)) {
if (issecretdoor(o)) {
return &(findcelltype(obloc->map->habitat->solidcelltype)->glyph);
} else {
if (isopen) {
g = '-';
} else {
g = '+';
}
col = getmaterialcolour(o->material->id);
}
} else {
f = hasflag(o->flags, F_GLYPH);
if (f) {
g = f->val[1];
if (f->val[0] != NA) {
col = f->val[0];
}
} else {
g = o->type->obclass->glyph.ch;
//col = o->type->obclass->glyph.colour;
col = getmaterialcolour(o->material->id);
}
}
// special case
if (hasflag(o->flags, F_DEEPWATER)) {
cell_t *loc;
loc = getoblocation(o);
// override colour
//if (getobdepth(o, player) >= DP_HEAD) {
/*
if (getcellwaterdepth(loc, player) >= DP_HEAD) {
col = C_BOLDBLUE;
} else {
col = C_BLUE;
}
*/
if (getcellwaterdepth(loc, player) >= DP_WAIST) {
g = UNI_SOLID;
} else {
g = '~';
}
}
tempglyph.ch = g;
tempglyph.colour = col;
return &tempglyph;
}
recipe_t *findrecipefor(enum OBTYPE result) {
recipe_t *r;
for (r = firstrecipe ; r ; r = r->next) {
if (r->result == result) return r;
}
return NULL;
}
void fragments(cell_t *centre, char *what, int speed, int howfar) {
cell_t *c,*dst;
int n,dir;
for (dir = DC_N; dir <= DC_NW; dir++) {
int wantdist = 0;
int maxdist = 0;
int done = B_FALSE;
// get max distance
c = centre;
done = B_FALSE;
while (!done) {
c = getcellindir(c, dir);
if (c) {
if (cellwalkable(NULL, c, NULL)) {
maxdist++;
} else {
if (c->lf && !c->type->solid) {
maxdist++;
}
done = B_TRUE;
}
} else {
done = B_TRUE;
}
}
limit(&maxdist, NA, howfar);
// pick random distance
if (maxdist == 0) {
wantdist = 0;
} else {
/*
int realmax;
if (howfar > maxdist) {
realmax = (maxdist-1);
} else {
realmax = howfar-1;
}
*/
if (maxdist < 1) {
wantdist = 0;
} else {
wantdist = rnd(1,maxdist);
}
}
// go that far
dst = centre;
for (n = 0; n < wantdist; n++) {
cell_t *newdst;
newdst = getcellindir(dst, dir);
if (newdst) {
dst = newdst;
} else {
break;
}
}
if (speed) {
object_t *o;
// add object then fire it
o = addob(centre->obpile, what);
if (o) {
real_fireat(NULL, o, o->amt, dst, speed, NULL, B_FALSE);
}
} else {
// add object
addob(dst->obpile, what);
}
}
}
int gethardness(enum MATERIAL matid) {
material_t *m;
m = findmaterial(matid);
if (m) {
flag_t *f;
f = hasflag(m->flags, F_HARDNESS);
if (f) return f->val[0];
}
return 0;
}
void genhiddennames(void) {
objecttype_t *ot;
flag_t *f;
for (ot = objecttype ; ot ; ot = ot->next) {
f = hasflag(ot->flags, F_HASHIDDENNAME);
if (f) {
char *thisname;
if (strlen(f->text)) {
thisname = strdup(f->text);
} else {
thisname = strdup(genhiddenname(ot->obclass->id));
}
sethiddenname(ot, thisname);
free(thisname);
}
}
}
// get the first hidden name for this objectclass
char *genhiddenname(enum OBCLASS id) {
hiddenname_t *hn;
for (hn = firsthiddenname ; hn ; hn = hn->next) {
if (hn->obclass == id) {
if (!hn->used) {
// found one
hn->used = B_TRUE;
return hn->text;
}
}
}
assert("out of hidden names" == 0);
return NULL;
}
enum LFSIZE getarmoursize(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_MULTISIZE);
if (f) {
f = hasflag(o->flags, F_ARMOURSIZE);
if (f) {
return f->val[0];
}
return SZ_HUMAN;
}
return SZ_ANY;
}
// returns largest posisble container with free space
object_t *getbestcontainer(obpile_t *op) {
object_t *o,*poss[MAXPILEOBS],*poss2[MAXPILEOBS];
int nposs = 0,nposs2 = 0,i;
enum LFSIZE bestsize = SZ_MIN;
// find best container size
for (o = op->first ; o ; o = o->next) {
if (hasflag(o->flags, F_CONTAINER)) {
enum LFSIZE thissize;
poss[nposs] = o;
nposs++;
thissize = getobsize(o);
if (thissize > bestsize) bestsize = thissize;
}
}
if (!nposs) {
return NULL;
}
// now find all containers of this size
for (i = 0; i < nposs; i++) {
int valid = B_TRUE;
if (getobsize(poss[i]) != bestsize) {
valid = B_FALSE;
}
if (countobs(poss[i]->contents, B_FALSE) >= MAXPILEOBS) {
// no space
valid = B_FALSE;
}
if (valid) {
poss2[nposs2++] = poss[i];
}
}
if (!nposs2) {
return NULL;
}
return poss2[rnd(0,nposs2-1)];
}
// returns -1 if object doesn't have the flag
int getchargeinfo(object_t *o, int *cur, int *max) {
flag_t *f;
int amt = -1;
f = hasflag(o->flags, F_CHARGES);
if (f) {
amt = f->val[0];
if (cur) *cur = amt;
if (max) *max = f->val[1];
}
return amt;
}
// 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;
}
// return the base accuracy for the firearm 'wep', or for a throw if wep is null.
// (ie. the accuracy for a range of 0).
int getobaccuracy(object_t *wep, lifeform_t *weilder) {
int acc;
flag_t *f;
acc = 100; // default accuracy of 100%
if (wep) {
// 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];
}
// blessed weapons have better base accuracy
if (wep->blessed == B_BLESSED) acc += 10;
//bonusses?
acc += (getobbonus(wep, B_FALSE)*10);
}
return acc;
}
int getobbonus(object_t *o, int onlyknown) {
int bonus = 0,i;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
getflags(o->flags, retflag, &nretflags, F_BONUS, F_NONE);
for (i = 0; i < nretflags; i++) {
if (onlyknown && !retflag[i]->known) {
} else {
bonus += retflag[i]->val[0];
}
}
return bonus;
}
int getobpoints(object_t *o) {
if (hasflag(o->flags, F_NOPOINTS)) {
return 0;
}
return getobvalue(o);
}
// returns the skill associated with this object
skill_t *getobskill(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_USESSKILL);
if (f) {
return findskill(f->val[0]);
}
return NULL;
}
enum LFSIZE getobsize(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *r;
flag_t *ff;
r = findrace(f->val[0]);
ff = hasflag(r->flags, F_SIZE);
if (ff) {
return ff->val[0];
} else {
return SZ_MEDIUM;
}
}
return o->type->size;
}
int getobspellpower(object_t *o, lifeform_t *lf) {
flag_t *f;
int power = 1;
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
power = f->val[1];
if (power == NA) power = 1;
if (lf) {
// increase based on your magic item usage skill
enum SKILLLEVEL slev;
slev = getskill(lf, SK_CHANNELING);
power += slev;
if (slev >= PR_ADEPT) power += (slev - 2);
setskillused(lf, SK_CHANNELING);
}
// blessed objects are more powerful
if (isblessed(o)) power += 4;
// enforce limits
}
limit(&power, 1, 10);
return power;
}
// returns value of obejcts, in gold
int getobvalue(object_t *o) {
float price;
flag_t *f;
int rarity = 0,i;
enum RARITY rr = RR_FREQUENT;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
if (o->type->id == OT_GOLD) {
return o->amt;
}
if (o->type->id == OT_CREDITCARD) {
return getcharges(o);
}
// 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];
}
if (o->type->id == OT_SPELLBOOK) {
price += (89*countobs(o->contents, B_FALSE));
}
getflags(o->flags, retflag, &nretflags, F_ARMOURRATING, F_ARMOURSIZE, F_BONUS, F_DAM, F_EDIBLE, F_LINKSPELL, F_MANUALOF, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
// damage
if (f->id == F_DAM) {
int min,max;
getdamrange(o, f, &min, &max);
price += (max*5);
}
// armour rating
if (f->id == F_ARMOURRATING) {
float rating;
rating = (float)f->val[0];
price += (rating * 20.0);
}
// armour size (nonstandard costs more)
if (f->id == F_ARMOURSIZE) {
if (f->val[0] != SZ_HUMAN) {
price += 75;
}
}
// bonus/penalties
if (f->id == F_BONUS) {
price += (f->val[0] * 100);
}
// food
if (f->id == F_EDIBLE) {
price += ((float)f->val[1] / 5.0);
}
// one-off magical effects (linkspell) - use spell price
if (f->id == F_LINKSPELL) {
if (o->type->obclass->id == OC_SCROLL) {
price += (pow(getspelllevel(f->val[0]), 2) * 2);
} else if (o->type->obclass->id == OC_POTION) {
price += (pow(getspelllevel(f->val[0]), 2));
} else if (o->type->obclass->id == OC_WAND) {
price += (pow(getspelllevel(f->val[0]), 2) * 15);
}
} else if (f->id == F_MANUALOF) {
price *= 124;
}
}
// TODO: rings?
if (hasflag(o->flags, F_HASBRAND)) {
price += 1000;
}
// rarity
rarity = getobrarity(o, &rr);
/*
if (o->type->obclass->id == OC_POTION) {
// potion value is based on rarity
price += (rr * 112);
} else if (o->type->obclass->id == OC_TOOLS) {
// tool value is based on rarity
//price += ((100 - rarity)*3.75);
price += ((rr*43) + (r-1)*22);
} else if (o->type->obclass->id == OC_TECH) {
// tech value is based on tech level
float multiplier = 1;
switch (gettechlevel(o->type->id)) {
case PR_INEPT:
multiplier = 3.25;
break;
case PR_NOVICE:
multiplier = 5.25;
break;
case PR_BEGINNER:
multiplier = 6.25;
break;
case PR_ADEPT:
multiplier = 7.25;
break;
case PR_SKILLED:
multiplier = 8.25;
break;
case PR_EXPERT:
multiplier = 9.25;
break;
case PR_MASTER:
multiplier = 10.25;
break;
}
//price += ((100 - rarity)*multiplier);
price += ((rr*20)*multiplier);
}
*/
// TODO: conferred intrinsics - depends on which one
// TODO: conferred spells - use spell price * multiplier
// speical material prices like velvet, silk
if (strstr(o->type->name, "velvet")) {
price *= 1.5;
}
if (strstr(o->type->name, "silk")) {
price *= 2;
}
// scarcity...
switch (rr) {
case RR_UNIQUE:
price *= 5;
break;
case RR_VERYRARE:
price *= 3;
break;
case RR_RARE:
price *= 2;
break;
case RR_UNCOMMON:
break;
case RR_COMMON:
price *= 0.75;
break;
case RR_FREQUENT:
price *= 0.5;
break;
default:
break;
}
// blessed/cursed
if (isblessed(o) || iscursed(o)) price *= 1.25;
// minimum
limitf(&price, 1, NA);
price = ((int)price * o->amt);
return (int) price;
}
char *getoperateverb(object_t *o) {
if (hasflag(o->flags, F_SHOP)) {
return "enter";
} else if (hasflag(o->flags, F_CONTAINER)) {
return "open";
}
return "operate";
}
// get outermost object in a pile
// ie if you call this on a gem inside a bag inside
// a barrel, will return the barrel.
object_t *getoutercontainer(object_t *o) {
return getoutercontainerop(o->pile);
}
object_t *getoutercontainerop(obpile_t *op) {
object_t *o = NULL;
while (op->parentob) {
o = op->parentob;
op = o->pile;
}
return o;
}
object_t *getammo(object_t *gun) {
object_t *o;
o = gun->contents->first;
return o;
}
objecttype_t *getbasicweaponforskill(enum SKILL skid) {
switch (skid) {
case SK_AXES:
return findot(OT_AXE);
case SK_CLUBS:
return findot(OT_CLUB);
case SK_LONGBLADES:
return findot(OT_LONGSWORD);
case SK_POLEARMS:
return findot(OT_SPEAR);
case SK_SHORTBLADES:
return findot(OT_SHORTSWORD);
case SK_STAVES:
return findot(OT_QUARTERSTAFF);
default:
break;
}
return NULL;
}
object_t *getrandomammo(lifeform_t *lf) {
object_t *gun;
object_t *o;
flag_t *f;
int i;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
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?
getflags(gun->flags, retflag, &nretflags, F_AMMOOB, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
for (o = lf->pack->first ; o ; o = o->next) {
if (o->type->id == f->val[0]) {
return o;
}
}
}
return NULL;
}
objecttype_t *getrandomammofor(object_t *o) {
objecttype_t *ot;
objecttype_t *poss[MAXCANDIDATES];
int nposs = 0;
if (!o || !isfirearm(o)) {
return NULL;
}
for (ot = objecttype ; ot ; ot = ot->next) {
if (isammofor(ot, o)) {
poss[nposs++] = ot;
}
}
if (nposs) {
ot = poss[rnd(0,nposs-1)];
} else {
ot = NULL;
}
return ot;
}
brand_t *getrandombrandfor(objecttype_t *ot) {
brand_t *br, **poss;
brand_t *result = NULL;
int numbr;
int nposs = 0;
// count number of brands
numbr = 0;
for (br = firstbrand ; br ; br = br->next) {
numbr++;
}
poss = malloc(numbr * sizeof(brand_t *));
for (br = firstbrand ; br ; br = br->next) {
if (brandappliesto(br, ot)) {
poss[nposs] = br;
nposs++;
}
}
if (nposs > 0) {
result = poss[rnd(0,nposs-1)];
}
free(poss);
return result;
}
objecttype_t *getrandomobofclass(enum OBCLASS ocid, int minrarity, int maxrarity) {
objecttype_t *ot;
int count = 0,sel,n;
flag_t *f;
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id == ocid) && !hasflag(ot->flags, F_UNIQUE)) {
f = hasflag(ot->flags, F_RARITY);
if (f) {
int ok = B_FALSE;
if (f->val[1] == NA) {
ok = B_TRUE;
} else if ( ((minrarity == NA) || (f->val[1] >= minrarity)) &&
((maxrarity == NA) || (f->val[1] <= maxrarity)) ) {
ok = B_TRUE;
}
if (ok) {
count++;
}
}
}
}
if (count <= 0) {
return NULL;
}
sel = rnd(1,count);
n = 0;
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id == ocid) && !hasflag(ot->flags, F_UNIQUE)) {
f = hasflag(ot->flags, F_RARITY);
if (f) {
int ok = B_FALSE;
if (f->val[1] == NA) {
ok = B_TRUE;
} else if ( ((minrarity == NA) || (f->val[1] >= minrarity)) &&
((maxrarity == NA) || (f->val[1] <= maxrarity)) ) {
ok = B_TRUE;
}
if (ok) {
n++;
if (n == sel) {
return ot;
}
}
}
}
}
return NULL;
}
enum OBTYPE getrandomtrapforob(void) {
objecttype_t *ot;
enum OBTYPE poss[MAXCANDIDATES];
int nposs = 0;
for (ot = objecttype ; ot ;ot = ot->next) {
if ((ot->obclass->id == OC_TRAP) && hasflag(ot->flags, F_OBJECTTRAP)) {
poss[nposs++] = ot->id;
}
}
return poss[rnd(0,nposs-1)];
}
char *getdamname(enum DAMTYPE damtype) {
switch (damtype) {
case DT_ALL: return "all damage";
case DT_ACID: return "acid";
case DT_BASH: return "bludgeoning";
case DT_BITE: return "biting";
case DT_CHOP: return "chopping";
case DT_COLD: return "cold";
case DT_CRUSH: return "crushing";
case DT_DIRECT: return "direct";
case DT_DECAY: return "decay";
case DT_ELECTRIC: return "electricity";
case DT_EXPLOSIVE: return "explosive";
case DT_FALL: return "falling";
case DT_FIRE: return "fire";
case DT_HEAT: return "heat";
case DT_HOLY: return "holy damage";
case DT_LIGHT: return "light";
case DT_MAGIC: return "magical";
case DT_MELT: return "melting";
case DT_NECROTIC: return "lifedrain";
case DT_PETRIFY: return "petrification";
case DT_PIERCE: return "piercing";
case DT_POISON: return "poison";
case DT_POISONGAS: return "gas";
case DT_PROJECTILE: return "projectile";
case DT_SLASH: return "slashing";
case DT_SONIC: return "sonic";
case DT_TOUCH: return "touch";
case DT_UNARMED: return "unarmed";
case DT_WATER: return "water";
default: return "unknown";
}
return "unknown";
}
char *getdamnamenoun(enum DAMTYPE damtype) {
switch (damtype) {
case DT_ALL: return "all damage";
case DT_ACID: return "acid";
case DT_MELT: return "melting";
case DT_PETRIFY: return "petrification";
case DT_PIERCE: return "piercing damage";
case DT_POISONGAS: return "gas";
case DT_POISON: return "poison";
case DT_SLASH: return "slashing damage";
case DT_ELECTRIC: return "electricity";
case DT_EXPLOSIVE: return "explosives";
case DT_FIRE: return "fire";
case DT_HEAT: return "heat";
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_NECROTIC: return "lifedrain damage";
case DT_TOUCH: return "touch effects";
case DT_UNARMED: return "unarmed damage";
case DT_LIGHT: return "light damage";
case DT_CRUSH: return "crushing damage";
case DT_SONIC: return "sonic damage";
case DT_FALL: return "damage from falling";
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 special case for unidentified books...
//if (o->type->id == OT_SPELLBOOK) {
if (o->type->obclass->id == OC_BOOK) {
flag_t *f;
f = hasflag(o->flags, F_HASHIDDENNAME);
if (f) {
return f->text;
}
}
// 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 == B_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;
}
char *gethiddennameot(enum OBTYPE otid) {
knowledge_t *k;
objecttype_t *ot;
ot = findot(otid);
// otherwise special case for unidentified books...
if (ot->id == OT_SPELLBOOK) {
flag_t *f;
f = hasflag(ot->flags, F_HASHIDDENNAME);
if (f) {
return f->text;
}
}
for (k = knowledge; k ; k = k->next) {
if (k->id == ot->id) {
// it DOES have a hidden name.
// does the player know about it?
if (k->known == B_KNOWN) {
// if so, return real name
return ot->name;
} else {
// otherwise return hidden one
return k->hiddenname;
}
}
}
return ot->name;
}
int getobattackdelay(object_t *o) {
int delay = 100; // ie. 100%
flag_t *f;
f = hasflag(o->flags, F_OBATTACKDELAY);
if (f) {
delay = f->val[0];
}
return delay;
}
float getobhppct(object_t *o) {
float pct;
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
pct = ((float) f->val[0] / (float) f->val[1]) * 100.0;
} else {
pct = 100;
}
return pct;
}
// returns '1' if object has no F_OBHP
int getobmaxhp(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
return f->val[1];
}
return 1;
}
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:
case MT_ACID:
return 0;
case MT_WIRE:
case MT_FOOD:
case MT_PLANT:
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:
case MT_WAX:
case MT_OIL:
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:
case MT_SILK:
case MT_METAL:
return 5;
case MT_SILVER:
return 6;
case MT_GOLD:
case MT_DRAGONWOOD:
return 7;
}
// default
return 1;
}
int getmaxthrowrange(lifeform_t *lf, object_t *o) {
int maxdist;
float str;
float obweight;
// adjust for lifeform strength
// mighty = can throw 1kilo 10 metres
// mighty = can throw 10kilos 1 metre
// 11 - kilos = distance
str = getattr(lf, A_STR); // ie. 1 - 18
str -= 2;
limitf(&str, 1, NA); // ie. 1 to 16
obweight = getobunitweight(o);
maxdist = ceil(str - obweight);
if (maxdist < 1) maxdist = 0;
return maxdist;
}
// select lowest possible letter
char getnextletter(obpile_t *op, char *wantletter) {
int curidx = -1;
char let;
//int db = B_FALSE;
// try 'wantletter' first
if (wantletter && (*wantletter != '\0')) {
if (!pilehasletter(op, *wantletter)) {
return *wantletter;
}
}
curidx = 0; // ie 'a'
for (curidx = 0; curidx < MAXPILEOBS; curidx++) {
// does any other object in the pile have this letter?
let = letorder[curidx];
if (!pilehasletter(op, let)) {
// if we didn't find it, this letter is okay.
return let;
}
}
return '\0';
}
int getnumshards(object_t *o) {
int numshards,maxshards;
maxshards = ceil(getobunitweight(o)) * 10;
if (maxshards < 1) maxshards = 1;
numshards = rnd(1,maxshards);
numshards *= o->amt;
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) {
f = hasflag(o->flags, F_DRINKABLE);
}
if (f) {
basenutr = (float)f->val[1];
} else {
return 0;
}
}
return basenutr;
}
int getnutrition(object_t *o) {
float nutrpct;
float nutrition;
if (isrotting(o)) {
nutrition = -(HUNGERCONST/2);
} else {
nutrpct = getnutritionbase(o);
if (nutrpct <= 0) {
nutrition = 0;
} else {
nutrition = pctof(nutrpct, (float) HUNGERCONST);
}
}
if (hasflag(o->flags, F_HEADLESS)) {
// -25% nutrition
nutrition *= 0.75;
}
return (int)nutrition;
}
enum DEPTH getobdepth(object_t *o, lifeform_t *lf) {
int depth = DP_NONE;
flag_t *f;
f = hasflag(o->flags, F_DEEPWATER);
if (f) {
depth = f->val[0];
if (lf) {
int mod;
int lfsize;
lfsize = getlfsize(lf);
if (isprone(lf)) {
lfsize -= 2;
limit(&lfsize, SZ_MINI, NA);
}
mod = (SZ_HUMAN - lfsize);
limit(&mod, SZ_MINI, NA);
mod *= DP_CALF;
depth += mod;
limit(&depth, DP_NONE, DP_HEAD);
}
}
return depth;
}
char *getobdesc(object_t *o, char *buf) {
if (isknown(o)) {
if (o->type->id == OT_CORPSE) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
snprintf(buf, BUFLEN, "The dead body of %s %s.", isvowel(corpserace->name[0]) ? "an" : "a",
corpserace->name);
} else {
snprintf(buf, BUFLEN, "%s", o->type->desc);
}
} else if (o->type->id == OT_HEAD) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
snprintf(buf, BUFLEN, "The decapitated head of %s %s.", isvowel(corpserace->name[0]) ? "an" : "a",
corpserace->name);
} else {
snprintf(buf, BUFLEN, "%s", o->type->desc);
}
} else if (o->type->id == OT_ROASTMEAT) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
snprintf(buf, BUFLEN, "A chunk of flame-roasted flesh from %s %s.", isvowel(corpserace->name[0]) ? "an" : "a", corpserace->name);
} else {
snprintf(buf, BUFLEN, "%s", o->type->desc);
}
} else if (o->type->id == OT_STATUE) {
flag_t *f;
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
snprintf(buf, BUFLEN, "A stone statue of %s %s.", isvowel(corpserace->name[0]) ? "an" : "a",
corpserace->name);
} else {
snprintf(buf, BUFLEN, "%s", o->type->desc);
}
} else if (o->type->id == OC_SCROLL) {
flag_t *f;
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
objecttype_t *spelltype;
spelltype = findot(f->val[0]);
if (spelltype) {
snprintf(buf, BUFLEN, "%s", spelltype->desc);
} else {
snprintf(buf, BUFLEN, "%s", o->type->desc);
}
} else {
snprintf(buf, BUFLEN, "%s", o->type->desc);
}
} else if (hasflag(o->flags, F_DEEPWATER)) {
snprintf(buf, BUFLEN, "%s %s.", getwaterdepthname(getobdepth(o, player)), o->type->name);
capitalise(buf);
} else {
snprintf(buf, BUFLEN, "%s", o->type->desc);
}
} else {
objecttype_t *ot = NULL;
flag_t *f;
f = hasflag(o->flags, F_HASHIDDENNAME);
if (f) {
ot = findotn(f->text);
}
if (ot) {
snprintf(buf, BUFLEN, "%s", ot->desc);
} else {
snprintf(buf, BUFLEN, "%s", o->type->obclass->desc);
}
}
return buf;
}
char *getobequipinfo(object_t *o, char *buf) {
object_t *ammo = NULL;
flag_t *f;
strcpy(buf, "");
if (o->pile->owner) {
object_t *gun;
gun = getfirearm(o->pile->owner);
if (gun) {
ammo = getammo(gun);
}
}
f = hasflag(o->flags,F_EQUIPPED);
if (f) {
if (f->val[0] == BP_WEAPON) {
if (hasflag(o->flags, F_TWOHANDED)) {
strcat(buf, " (two-handed weapon)");
} else if (ismeleeweapon(o)) {
strcat(buf, " (weapon)");
} else {
strcat(buf, " (makeshift weapon)");
}
} else if (f->val[0] == BP_SECWEAPON) {
if (hasflag(o->flags, F_TWOHANDED)) {
strcat(buf, " (two-handed weapon)");
} else if (isshield(o)) {
strcat(buf, " (shield)");
} else if (ismeleeweapon(o)) {
strcat(buf, " (second weapon)");
} else if (isfirearm(o)) {
strcat(buf, " (firearm)");
} else {
strcat(buf, " (in left hand)");
}
} else {
char posbuf[BUFLEN];
strcat(buf, " (");
makewearstring(o->pile->owner, o, B_FALSE, posbuf);
/*
strcat(buf, getbodypartequipname(f->val[0]));
strcat(buf, " ");
strcat(buf, getbodypartname(o->pile->owner, f->val[0]));
*/
strcat(buf, posbuf);
strcat(buf, ")");
}
}
// ammo?
if (ammo && (ammo == o)) {
strcat(buf, " (current ammo)");
}
return buf;
}
char *getobextrainfo(object_t *o, char *buf) {
flag_t *f;
strcpy(buf, "");
// charges
f = hasflag(o->flags, F_CHARGES);
if (f) {
int flagknown = B_FALSE;
if (f->known) {
flagknown = B_TRUE;
} else if (ismagical(o) && (getskill(player, SK_CHANNELING) >= PR_MASTER)) {
flagknown = B_TRUE;
}
if (flagknown && !hasflag(o->flags, F_DONTSHOWCHARGES)) {
char chargestr[BUFLEN];
if (o->type->obclass->id == OC_GODSTONE) {
if (f->val[0] == f->val[1]) {
snprintf(chargestr, BUFLEN, " (charged)");
} else {
snprintf(chargestr, BUFLEN, " (depleted)");
}
} else if (o->type->id == OT_CREDITCARD) {
if (f->val[0] > 0) {
snprintf(chargestr, BUFLEN, " ($%d balance)",f->val[0]);
} else {
snprintf(chargestr, BUFLEN, " (maxed out)");
}
} else {
if (f->val[0] > 0) {
snprintf(chargestr, BUFLEN, " (%d charge%s left)",f->val[0], (f->val[0] == 1) ? "" : "s");
} else {
snprintf(chargestr, BUFLEN, " (empty)");
}
}
strcat(buf, chargestr);
}
}
// loaded guns
if (isfirearm(o) && getammo(o)) {
strcat(buf, " [loaded]");
}
// activated (unless it's unknown tech)
if ((o->type->obclass->id == OC_TECH) && !isknown(o)) {
} else {
if (!hasflag(o->flags, F_ACTIVATEPREFIX)) {
f = hasflag(o->flags, F_ACTIVATED);
if (f) {
strcat(buf, " [activated]");
}
}
}
return buf;
}
cell_t *getoblocation(object_t *o) {
return getobpilelocation(o->pile);
}
cell_t *getobpilelocation(obpile_t *op) {
if (op->owner) { // held by someone
return op->owner->cell;
} else if (op->where) { // on the ground
return op->where;
} else if (op->parentob) { // inside another object
object_t *outerob;
// get outside object
outerob = getoutercontainerop(op);
return getoblocation(outerob);
}
// in a dummy cell
return NULL;
}
// note: should have an entry here for everything which willshatter()
char *getshardobname(enum MATERIAL mid, char *buf) {
switch (mid) {
case MT_GLASS:
strcpy(buf, "piece of broken glass");
break;
case MT_ICE:
strcpy(buf, "chunk of ice");
break;
default: return NULL;
}
return buf;
}
char *getshopobname(object_t *o, char *buf, int count) {
if (gettechlevel(o->type->id) > getskill(player, SK_TECHUSAGE)) {
// unidentified tech - hide the name
return real_getobname(o, buf, o->amt, B_TRUE, B_TRUE, B_TRUE, B_TRUE, B_FALSE);
}
// anything else - show the real name
return real_getobname(o, buf, o->amt, B_TRUE, B_TRUE, B_TRUE, B_TRUE, B_TRUE);
}
char *getobname(object_t *o, char *buf, int count) {
return real_getobname(o, buf, count, B_TRUE, B_TRUE, B_TRUE, B_TRUE, B_FALSE);
}
char *getobnametrue(object_t *o, char *buf, int count) {
return real_getobname(o, buf, count, B_TRUE, B_TRUE, B_FALSE, B_TRUE, B_TRUE);
}
// buf must already be allocated
char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wantcondition, int adjustforblind, int wantblesscurse, int showall) {
char *pluralname;
char prefix[BUFLEN];
char basename[BUFLEN];
char localbuf[BUFLEN];
char buf2[BUFLEN];
char triedbuf[BUFLEN];
int venditem = B_FALSE;
flag_t *f;
brand_t *br;
int hasunknownmod = B_FALSE;
cell_t *where;
int no_a = B_FALSE;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
// default to normal name
if (hasflag(o->flags, F_VENDITEM)) {
venditem = B_TRUE;
}
where = getoblocation(o);
if (venditem) {
showall = B_TRUE;
}
f = hasflag(o->flags, F_TRAIL);
if (f) {
race_t *r = NULL;
enum SKILLLEVEL slev;
slev = getskill(player, SK_PERCEPTION);
r = findrace(f->val[0]);
assert(r);
if (f->val[2] == S_SMELL) {
char buf[BUFLEN];
float pct;
char dname[BUFLEN];
char adjective[BUFLEN];
lifeform_t *who = NULL;
pct = ((float)f->lifetime / (float)TM_SCENT)*100;
if (pct >= 66) {
strcpy(adjective, "strong ");
} else if (pct >= 33) {
strcpy(adjective, "");
} else {
strcpy(adjective, "weak ");
}
strcpy(basename, "");
if (strlen(f->text)) {
who = findlf(where->map, atoi(f->text));
}
if (who) {
char lfname[BUFLEN];
real_getlfnamea(who, lfname, B_FALSE, B_FALSE);
snprintf(buf, BUFLEN, "a %s%s scent",adjective,r->name);
} else {
snprintf(buf, BUFLEN, "%s%s scent",adjective, r->name );
}
strcat(basename, buf);
strcat(basename, " leading ");
snprintf(dname, BUFLEN, "%s", getdirname(f->val[1]));
dname[0] = tolower(dname[0]);
strcat(basename, dname);
} else {
char buf[BUFLEN];
// adept and upwards gets depth
if (slev >= PR_BEGINNER) {
float pct;
pct = ((float)f->lifetime / (float)TM_FOOTPRINT)*100;
if (pct >= 66) {
strcpy(basename, "fresh ");
} else if (pct >= 33) {
strcpy(basename, "");
} else {
strcpy(basename, "faint ");
}
} else {
strcpy(basename, "");
}
// adept and upwards get "monstername footprints"
if (slev >= PR_ADEPT) {
snprintf(buf, BUFLEN, "%s %s",r->name, o->type->name);
strcat(basename, buf);
} else {
strcat(basename, o->type->name);
}
// skilled and upwards get the direction
if (slev >= PR_SKILLED) {
char dname[BUFLEN];
strcat(basename, " leading ");
snprintf(dname, BUFLEN, "%s", getdirname(f->val[1]));
dname[0] = tolower(dname[0]);
strcat(basename, dname);
}
} // end if sight/smell
} else if ((o->type->id == OT_SIGN) && !hasflag(o->flags, F_SIGNTEXT)) {
strcpy(basename, "blank sign");
} else if ((o->type->id == OT_MAP) && isknown(o)) {
flag_t *f;
f = hasflag(o->flags, F_MAPTO);
if (f && getskill(player, SK_CARTOGRAPHY) && isidentified(o)) {
snprintf(basename, BUFLEN, "map to %s", f->text);
} else {
strcpy(basename, "map");
}
} else if (o->type->id == OT_TEMPLE) {
flag_t *f;
lifeform_t *god = NULL;
f = hasflag(o->flags, F_LINKGOD);
if (f) god = findgod(f->val[0]);
if (god) {
sprintf(basename, "temple of %s", god->race->name);
} else {
sprintf(basename, "abandoned temple");
}
} else if (hasflag(o->flags, F_DEEPWATER)) {
snprintf(basename, BUFLEN, "%s %s", getwaterdepthname(getobdepth(o, player)), o->type->name);
} else {
strcpy(basename, "");
// show "lit candle" etc
if (isactivated(o) && hasflag(o->flags, F_ACTIVATEPREFIX)) {
f = hasflag(o->flags, F_ACTIVATEPREFIX);
snprintf(basename, BUFLEN, "%s ", f->text);
} else if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) {
// known trap?
strcpy(basename, "trapped ");
}
if (showall) {
strcat(basename,o->type->name);
} else {
strcat(basename,gethiddenname(o));
}
}
if (o->type->obclass->id == OC_BOOK) {
if (streq(basename, o->type->name)) {
if (o->type->id == OT_SPELLBOOK) {
f = hasflag(o->flags, F_LINKSCHOOL);
if (f) {
strcat(basename, " of ");
strcat(basename, getschoolname(f->val[0]));
}
} else {
f = hasflag(o->flags, F_MANUALOF);
if (f) {
skill_t *sk;
sk = findskill(f->val[0]);
assert(sk);
strcat(basename, " of ");
strcat(basename, sk->name);
}
}
}
} else if (o->type->obclass->id == OC_SPELL) {
strcpy(buf, o->type->name);
return buf;
}
if (!showall) {
if ((gamemode == GM_GAMESTARTED) && adjustforblind && !haslos(player, where) ) {
f = hasflag(o->flags, F_FEELTEXT);
if (f) {
strcpy(basename, f->text);
} else {
// override with obclass names
switch (o->type->obclass->id) {
case OC_BOOK:
strcpy(basename, "book");
break;
case OC_POTION:
strcpy(basename, "potion");
break;
case OC_RING:
strcpy(basename, "ring");
break;
case OC_SCROLL:
strcpy(basename, "scroll");
break;
case OC_WAND:
strcpy(basename, "wand");
break;
default:
break;
}
}
}
}
if ((o->type->id == OT_POT_WATER) && (o->blessed == B_BLESSED) && isblessknown(o) && isknown(o)) {
strcpy(basename, "potion of holy water");
}
if ((o->type->id == OT_POT_COMPETENCE) && (o->blessed == B_CURSED) && isblessknown(o) && isknown(o)) {
strcpy(basename, "potion of incompetence");
}
// override corpse name
if ((o->type->id == OT_CORPSE) || (o->type->id == OT_HEAD)) {
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
flag_t *ff;
corpserace = findrace(f->val[0]);
ff = hasflag(corpserace->flags, F_NAME);
if (ff) {
snprintf(basename, BUFLEN, "%s%s %s",ff->text, getpossessive(ff->text), o->type->name);
no_a = B_TRUE;
} else {
snprintf(basename, BUFLEN, "%s %s",corpserace->name, o->type->name);
}
}
} else if (o->type->id == OT_ROASTMEAT) {
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
snprintf(basename, BUFLEN, "chunk of roast %s meat",corpserace->name);
}
} else if (o->type->id == OT_FOUNTAIN) {
objecttype_t *ot;
// find out what kind of fountain this is
f = hasflag(o->flags, F_LINKOB);
if (f) {
ot = findot(f->val[0]);
if (ot) {
char *srcp;
// get potion name (or hidden name if we don't recognise it)
srcp = gethiddennameot(ot->id);
// if this is "potion of xxx"
if (strstarts(srcp, "potion ") || strstarts(srcp, "vial ") || strstarts(srcp, "flask ")) {
// skip first word from potion name.
// ie. we're left with " of xxx"
while (*srcp != ' ') {
srcp++;
}
srcp++; // go past the space
// now copy the rest into buf
strcpy(buf, srcp);
if (streq(buf, "of water")) {
strcpy(basename, "water fountain");
} else {
snprintf(basename, BUFLEN, "fountain %s",buf);
}
} else {
char *dstp;
// ie. "orange potion"
// get rid of "potion" onwards
dstp = buf;
while (!streq(srcp, "potion")) {
*dstp = *srcp;
srcp++;
dstp++;
}
*dstp = '\0';
// buf should now be something like "orange "
snprintf(basename, BUFLEN, "%sfountain",buf);
}
} else {
// should never happen
strcpy(basename, "??strange fountain??");
}
} else { // end if f
// should never happen in gameplay, but might be triggered
// during object creation.
strcpy(basename, "??strange fountain2??");
}
} else if (o->type->id == OT_STATUE) {
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *corpserace;
corpserace = findrace(f->val[0]);
snprintf(basename, BUFLEN, "statue of a %s",corpserace->name);
}
} else if (o->type->id == OT_JERKY) {
f = hasflag(o->flags, F_LINKRACE);
if (f) {
race_t *r;
r = findrace(f->val[0]);
snprintf(basename, BUFLEN, "%s %s",r->name, o->type->name);
}
}
// handle armour sizes
f = hasflag(o->flags, F_ARMOURSIZE);
if (f) {
char sizetext[BUFLEN];
// prepend size
switch (f->val[0]) {
case SZ_MINI:
strcpy(sizetext, "miniscule");
break;
case SZ_TINY:
strcpy(sizetext, "tiny ");
break;
case SZ_SMALL:
strcpy(sizetext, "baby-sized");
break;
case SZ_MEDIUM:
strcpy(sizetext, "half-sized");
break;
case SZ_LARGE:
strcpy(sizetext, "giant-sized");
break;
case SZ_HUGE:
strcpy(sizetext, "titan-sized");
break;
case SZ_ENORMOUS:
strcpy(sizetext, "gargantuan");
break;
default:
strcpy(sizetext, "");
break;
}
if (strlen(sizetext)) {
char newname[BUFLEN];
sprintf(newname, "%s %s", sizetext, basename);
strcpy(basename, newname);
}
}
// handle ALL
if (count == ALL) {
count = o->amt;
}
strcpy(localbuf, "");
// figure out pluralname
if (count == 1) {
pluralname = strdup(basename);
} else {
// multiple objects?
if (hasflag(o->flags, F_NO_PLURAL)) {
pluralname = strdup(basename);
} else {
pluralname = makeplural(basename);
}
}
// blessed status
// ie. a ->blessed<- flaming +5 silver sword of pyromania
if (!hasflag(o->flags, F_NOBLESS) && wantblesscurse) {
if (showall || 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(localbuf, "blessed ");
}
break;
case B_UNCURSED:
strcat(localbuf, "uncursed ");
break;
case B_CURSED:
if ((o->type->id == OT_POT_COMPETENCE) && isknown(o)) {
} else {
strcat(localbuf, "cursed ");
}
break;
}
}
}
// eaten?
f = hasflag(o->flags, F_EDIBLE);
if (f && (f->val[2] != NA)) {
strcat(localbuf, "partially eaten ");
}
// condition
// include mods
// ie. a blessed ->damaged<- flaming +5 silver sword of pyromania
if (wantcondition) {
if (!hasflag(o->flags, F_NOOBDAMTEXT)) {
getobconditionname(o, buf2);
if (strlen(buf2) > 0) {
strcat(localbuf, buf2);
strcat(localbuf, " ");
}
}
}
// include mods
// ie. a blessed damaged ->flaming<- +5 silver sword of pyromania
if (wantpremods) {
obmod_t *om;
for (om = firstobmod ; om; om = om->next) {
if (hasobmod(o, om)) {
char *p;
p = getobmodprefix(o, om);
if (p && strlen(p)) {
strcat(localbuf, p);
}
}
}
}
// enchantments
// ie. a blessed damaged flaming ->+5<- silver sword of pyromania
f = hasflag(o->flags, F_BONUS);
if (f && (f->known || showall)) {
char buf2[BUFLENSMALL];
int bonus;
bonus = f->val[0];
if (bonus != 0) {
snprintf(buf2, BUFLENSMALL, "%s%d ", (bonus < 0) ? "-" : "+", abs(bonus));
strcat(localbuf, buf2);
}
}
// special rings
if (isidentified(o)) {
switch (o->type->id) {
char buf2[BUFLENSMALL];
case OT_RING_CON:
case OT_RING_DEX:
case OT_RING_IQ:
case OT_RING_STR:
f = hasflag(o->flags, F_EQUIPCONFER);
if (f) {
snprintf(buf2, BUFLENSMALL, "%s%d ",(f->val[2] < 0) ? "-" : "+", abs(f->val[2]));
strcat(localbuf, buf2);
}
break;
default:
break;
}
}
// material changed?
// ie. a blessed damaged flaming +5 ->silver<- sword of pyromania
if (o->material != o->type->material) {
int domaterialprefix = B_TRUE;
// exception: corpse taking on its lf's
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
race_t *r;
r = findrace(f->val[0]);
if (r->material->id == o->material->id) {
domaterialprefix = B_FALSE;
}
}
if (domaterialprefix) {
switch (o->material->id) {
case MT_GOLD:
strcat(localbuf, "golden ");
break;
case MT_WOOD:
strcat(localbuf, "wooden ");
break;
case MT_ICE: // we'll use the 'frozen' obmod instead
break;
default:
strcat(localbuf, o->material->name);
strcat(localbuf, " ");
break;
}
}
}
if (issecretdoor(o)) {
if (showall) {
strcat(localbuf, "secret ");
} else if (where) {
celltype_t *ct;
ct = findcelltype(where->map->habitat->solidcelltype);
// solid cell description
strcpy(pluralname, ct->name);
}
}
// object name
// ie. a blessed flaming +5 silver ->sword<- of pyromania
strcat(localbuf, pluralname);
free(pluralname);
// include mods if identified
// ie. a blessed flaming +5 silver sword ->of pyromania<-
for (br = firstbrand; br; br = br->next) {
if (hasflagval(o->flags, F_HASBRAND, br->id, NA, NA, NULL)) {
flag_t *brf;
int ok = B_TRUE;
// are all of the brand flags known?
for (brf = br->flags->first ; brf ; brf = brf->next) {
int i;
getflags(o->flags, retflag, &nretflags, brf->id, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (f->lifetime == FROMBRAND) {
if (f->known || showall) {
} else {
ok = B_FALSE;
hasunknownmod = B_TRUE;
break;
}
}
}
}
if (ok) {
strcat(localbuf, br->suffix);
}
}
}
// make sure obname doesn't start with a space
while (localbuf[0] == ' ') {
strcpy(localbuf, localbuf + 1);
}
// show portal/stair destination
f = hasflag(o->flags, F_MAPLINK);
if (f && f->known && !hasflag(o->flags, F_DONTSHOWDEST)) {
cell_t *thiscell;
map_t *thismap;
map_t *newmap;
thiscell = getoblocation(o);
thismap = thiscell->map;
newmap = findmap(f->val[0]);
if (newmap) {
if (newmap->region == thismap->region) {
char buf2[BUFLEN];
snprintf(buf2, BUFLEN, " to level %d", newmap->depth);
strcat(localbuf, buf2);
} else {
char buf2[BUFLEN];
strcat(localbuf, " to ");
getregionname(buf2, newmap, NULL, B_FALSE);
strcat(localbuf, buf2);
}
}
}
// show sign text
if (o->type->id == OT_SIGN) {
f = hasflag(o->flags, F_SIGNTEXT);
if (f) {
strcat(localbuf, " reading '");
strcat(localbuf, f->text);
strcat(localbuf, "'");
}
}
// append inscription
if (o->inscription) {
strcat(localbuf, " {");
strcat(localbuf, o->inscription);
strcat(localbuf, "}");
}
// show if we've tried this
if (gamemode == GM_GAMESTARTED) {
strcpy(triedbuf, "");
if (hasflag(o->flags, F_BEINGUSED)) {
if (strlen(triedbuf)) strcat(triedbuf, ", ");
else strcpy(triedbuf, " [");
strcat(triedbuf, "currently being read");
}
if (istried(o)) {
flag_t *f2;
if (strlen(triedbuf)) strcat(triedbuf, ", ");
else strcpy(triedbuf, " [");
f2 = hasflag(o->flags, F_SCROLLNEEDSOB);
if (f2) {
if ((f2->val[0] == B_IFNOTBLESSED) && !isblessed(o)) {
strcat(triedbuf, "tried");
} else {
strcat(triedbuf, "tried on object");
}
} else {
strcat(triedbuf, "tried");
}
}
if (!isknown(o)) {
if (lfhasflagval(player, F_FAILEDINSPECT, o->type->id, NA, NA, NULL)) {
if (strlen(triedbuf)) strcat(triedbuf, ", ");
else strcpy(triedbuf, " [");
strcat(triedbuf, "inspected");
}
}
if (strlen(triedbuf)) {
strcat(triedbuf, "]");
strcat(localbuf, triedbuf);
}
// detect magic - append [magic]
if (lfhasflag(player, F_DETECTMAGIC)) {
if (!isidentified(o) && ismagical(o) ) {
strcat(localbuf, " [magic]");
}
}
if (hasflag(o->flags, F_KNOWNBAD)) {
strcat(localbuf, " [badfeeling]");
}
if (getskill(player, SK_COOKING) >= PR_BEGINNER) {
if (isbadfood(o)) {
strcat(localbuf, " [badfood]");
}
}
}
// in a shop?
f = hasflag(o->flags, F_SHOPITEM);
if (f) {
char pricebuf[BUFLEN];
// get price for _player_
snprintf(pricebuf, BUFLEN, " [$%d%s]", (int)getshopprice(o, player), o->pile->owner ? ", unpaid" : "");
strcat(localbuf, pricebuf);
}
// apply prefix now!
if (count == 1) {
if (hasflag(o->flags, F_NO_A)) {
no_a = B_TRUE;
}
if (no_a) {
if (o->type->id == OT_GOLD) {
snprintf(prefix, BUFLEN, "%d ",count);
} else {
// nothing.
strcpy(prefix, "");
}
} else {
if (hasflag(o->flags, F_UNIQUE) && isknown(o)) {
strcpy(prefix, "The ");
} else {
if (needan(localbuf)) {
strcpy(prefix, "an ");
} else {
strcpy(prefix, "a ");
}
}
}
} else {
// multiple objects?
snprintf(prefix, BUFLEN, "%d ",count);
}
// prepend prefix on to buf
snprintf(buf, BUFLEN, "%s%s", prefix, localbuf);
return buf;
}
float getobpileweight(obpile_t *op) {
object_t *o;
float weight = 0;
if (op->parentob && hasflag(op->parentob->flags, F_HOLDING)) {
return 0;
}
for (o = op->first ; o ; o = o->next) {
weight += getobweight(o);
}
return weight;
}
char *getobconditionname(object_t *o, char *buf) {
float pct;
enum ATTRBRACKET iqb;
if (player) {
iqb = getattrbracket(getattr(player, A_IQ), A_IQ, NULL);
} else {
iqb = AT_AVERAGE; // this should be sufficient to show everything
}
if (iscorpse(o)) {
strcpy(buf, "");
if (hasflag(o->flags, F_PREPARED)) {
strcat(buf, "cooked");
}
// you only know it's rotting if you are smart or a cook
if (isrotting(o) &&
( (iqb >= AT_GTAVERAGE) || getskill(player, SK_COOKING)) ) {
if (strlen(buf)) strcat(buf, " ");
if (hasflag(o->flags, F_ISMEAT)) {
strcat(buf, "rotting");
} else {
strcat(buf, "mouldy");
}
}
} else {
if (iqb >= AT_LOW) {
pct = getobhppct(o);
if (pct >= 100) {
strcpy(buf, "");
} else if (pct >= 75) {
snprintf(buf, BUFLEN, "battered");
} else if (pct >= 50) {
snprintf(buf, BUFLEN, "damaged");
} else if (pct >= 25) {
snprintf(buf, BUFLEN, "very damaged");
} else {
snprintf(buf, BUFLEN, "critically damaged");
}
} else {
strcpy(buf, "");
}
}
return buf;
}
char *getobhurtname(object_t *o, enum DAMTYPE damtype) {
switch (damtype) {
case DT_ACID:
if (o->amt == 1) {
return "corrodes";
} else {
return "corrode";
}
case DT_DECAY:
if (o->amt == 1) {
return "decays";
} else {
return "decay";
}
case DT_FIRE:
if (o->amt == 1) {
return "burns";
} else {
return "burn";
}
case DT_MELT:
if (o->amt == 1) {
return "melts a bit";
} else {
return "melt a bit";
}
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;
// object contents
if (hasflag(o->flags, F_CONTAINER) && !hasflag(o->flags, F_HOLDING)) {
float containerweight = 0;
containerweight = getobpileweight(o->contents);
weight += containerweight;
}
return weight;
}
float getobunitweight(object_t *o) {
float weight;
flag_t *f;
// bag of holding?
if (o->pile->parentob && hasflag(o->pile->parentob->flags, F_HOLDING)) {
return 0;
}
if (hasflag(o->flags, F_DEEPWATER)) {
weight = 75 * getobdepth(o, NULL);
} else {
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;
}
f = hasflag(o->flags, F_ARMOURSIZE);
if (f) {
switch (f->val[0]) {
case SZ_LARGE:
weight *= 1.50;
break;
case SZ_MEDIUM:
weight *= 0.50;
break;
default:
break;
}
}
f = hasflag(o->flags, F_WET);
if (f) {
switch (f->val[0]) {
case W_DAMP:
weight *= 1.05;
break;
case W_WET:
weight *= 1.25;
break;
case W_SOAKED:
weight *= 1.5;
break;
default:
break;
}
}
return weight;
}
objecttype_t *getoppositestairs(objecttype_t *ot) {
flag_t *f;
f = hasflag(ot->flags, F_OPPOSITESTAIRS);
assert(f);
return findot(f->val[0]);
}
// varargs are:
// OC_CLASS1, OC_CLAS2, OC_NONE, ..., DT_DAMTYPE1, DT_DAMTYPE2, .., DT_NONE
//
// no objectclass given then it will be picked randomly, or (if wepsk isn't sk_none) set to oc_weapon.
char *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, enum LFSIZE maxsize, enum SKILL wepsk, int forpickup, ... ) {
va_list args;
objecttype_t *ot;
objecttype_t *poss[MAXRANDOMOBCANDIDATES];
enum OBCLASS wantclass[MAXCANDIDATES];
int nwantclass = 0;
enum DAMTYPE wantdt[MAXCANDIDATES];
int nwantdt = 0;
int nposs = 0;
int selidx;
int amt;
flag_t *f;
int db = B_FALSE;
int partdb = B_FALSE;
char *pluralname;
char brandname[BUFLEN];
char cursestr[BUFLEN];
int raritymin,raritymax;
int depth,i;
int done = B_FALSE;
obmod_t *om;
flag_t *omposs[MAXCANDIDATES];
int noms = 0;
enum RARITY wantrr = RR_FREQUENT,origwantrr;
habitat_t *hab;
char habname[BUFLEN];
int rrmoddir = -1;
skill_t *wantsk = NULL;
if (!db) db = obdb;
va_start(args, forpickup);
wantclass[nwantclass] = va_arg(args, enum OBCLASS);
while (wantclass[nwantclass] != OC_NONE) {
nwantclass++;
wantclass[nwantclass] = va_arg(args, enum OBCLASS);
}
wantdt[nwantdt] = va_arg(args, enum DAMTYPE);
while (wantdt[nwantdt] != DT_NONE) {
nwantdt++;
wantdt[nwantdt] = va_arg(args, enum DAMTYPE);
}
va_end(args);
if (wepsk) {
wantsk = findskill(wepsk);
}
if (forcehabitat != NA) {
hab = findhabitat(forcehabitat);
} else if (map) {
hab = map->habitat;
} else {
hab = NULL;
}
if (hab) {
strcpy(habname, hab->name);
} else {
strcpy(habname, "(any)");
}
//if (forcedepth != NA) {
if (forcedepth >= 0) {
depth = forcedepth;
} else {
depth = getmapdifficulty(map);
}
getrarityrange(depth, &raritymin, &raritymax, RARITYVARIANCEOB, B_TRUE);
// pick rr...
wantrr = pickrr(TT_OBJECT);
origwantrr = wantrr;
// no obclass given? pick one randomly.
if (!nwantclass) {
if (wantsk) {
wantclass[0] = OC_WEAPON;
} else {
wantclass[0] = getrandomobclass(hab->id);
}
nwantclass = 1;
}
if ((nwantclass == 1) && (wantclass[0] == OC_BUILDING)) {
int minused = 9999;
// find least used building
for (i = 0; i < nbuildingusage; i++) {
if (buildingusage[i].count < minused) {
minused = buildingusage[i].count;
}
}
// find possibilities
for (i = 0; i < nbuildingusage; i++) {
if (buildingusage[i].count == minused) {
poss[nposs++] = findot(buildingusage[i].oid);
}
}
assert(nposs > 0);
sprintf(buf, "%s", poss[rnd(0,nposs-1)]->name);
return buf;
}
while (!done) {
if (db || partdb) dblog("adding random object with rarity value between %d - %d and rr <= %d, for habitat %s",
raritymin,raritymax,wantrr, habname);
if (db || partdb) {
char dbuf[BUFLEN];
if (wantsk) {
sprintf(dbuf, " must have wepskill: %s", wantsk->name);
dblog("%s", dbuf);
}
if (nwantclass) {
objectclass_t *oc = NULL;
sprintf(dbuf, " must have obclass: ");
for (i = 0; i < nwantclass; i++) {
oc = findoc(wantclass[i]);
strcat(dbuf, oc->name);
if (i != nwantclass - 1) strcat(dbuf, ",");
}
dblog("%s", dbuf);
}
if (nwantdt) {
sprintf(dbuf, " must have damtype: ");
for (i = 0; i < nwantdt; i++) {
char dname[BUFLEN];
strcpy(dname, getdamname(wantdt[i]));
if (strstr(dname, "unknown")) {
assert("unknown wantdt in real_getrandomob()" == 0);
}
strcat(dbuf, dname);
if (i != nwantdt - 1) strcat(dbuf, ",");
}
dblog("%s", dbuf);
}
if (forpickup) {
dblog(" must be holdable");
}
}
// try to find an object of this type which will
// fit in the map's habitat
nposs = 0;
for (ot = objecttype ; ot ; ot = ot->next) {
int rarok = B_FALSE, condok = B_TRUE;
flag_t *rarflag = NULL;
// correct rarity number?
if (hab) {
rarflag = hasflagval(ot->flags, F_RARITY, hab->id, NA, NA, NULL);
if (!rarflag) {
rarflag = hasflagval(ot->flags, F_RARITY, H_ALL, NA, NA, NULL);
}
} else {
rarflag = hasflag(ot->flags, F_RARITY);
}
/*if (!rarflag) {
rarflag = hasflag(ot->flags, F_RARITY);
}*/
if (rarflag) {
if ((rarflag->val[1] >= raritymin) && (rarflag->val[1] <= raritymax)) {
enum RARITY thisrr;
thisrr = rarflag->val[2];
if (thisrr == NA) thisrr = RR_FREQUENT;
if (thisrr == wantrr) {
rarok = B_TRUE;
} else {
if (db) dblog(" %s rarity(%d) doesn't match wantrr(%d)", ot->name,
rarflag->val[2], wantrr);
}
} else {
if (db) dblog(" rarity of %s out of range (%d)", ot->name, rarflag->val[1]);
}
} else {
if (db) dblog(" %s doesn't have rarity flag", ot->name);
}
if (rarok) {
int found;
if (db) dblog(" %s passes rarity check.", ot->name);
// matches obclass?
if (nwantclass) {
found = B_FALSE;
for (i = 0; i < nwantclass; i++) {
if (ot->obclass->id == wantclass[i]) {
found = B_TRUE;
}
}
if (!found) {
condok = B_FALSE;
if (db) dblog(" %s fails obclass check.", ot->name);
}
}
// matches damtype?
if (nwantdt) {
found = B_FALSE;
for (i = 0; i < nwantdt; i++) {
if (hasflagval(ot->flags, F_DAM, wantdt[i], NA, NA, NULL)) {
found = B_TRUE;
break;
}
}
if (!found) {
condok = B_FALSE;
if (db) dblog(" %s fails damtype check.", ot->name);
}
}
// matches wanted weapon skill?
if (wantsk) {
if (!hasflagval(ot->flags, F_USESSKILL, wantsk->id, NA, NA, NULL)) {
condok = B_FALSE;
if (db) dblog(" %s doesn't use correct weapon skill.", ot->name);
}
}
if (forpickup) {
if (hasflag(ot->flags, F_NOPICKUP)) {
condok = B_FALSE;
if (db) dblog(" %s not pickupable.", ot->name);
}
}
}
if (rarok && condok && (ot->size <= maxsize)) {
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)) {
// now lower wantrr
if (rrmoddir == -1) {
if (wantrr > RR_FREQUENT) {
wantrr--;
if (db) dblog("rarity at min/max and no obs. lowering wantrr to %d.",wantrr);
} else {
// wantrr is already at rr_frequent
// start increasing it now.
wantrr = origwantrr + 1;
rrmoddir = 1;
if (db) dblog("rarity got below frequent. raising to original rr + 1 (%d)",wantrr);
}
} else {
if (wantrr < RR_VERYRARE) {
wantrr++;
if (db) dblog("rarity at min/max and no obs. raising wantrr to %d.",wantrr);
} else {
// give up
strcpy(buf, "");
if (db || partdb) dblog("no possible random objects at all for habitat %s! giving up.", habname);
return NULL;
}
}
} else {
// 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);
}
} else {
// something found
done = B_TRUE;
}
} // end while !done
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);
}
// chance to be blessed or cursed? 15% chance each way
strcpy(cursestr, "");
if (!hasflag(ot->flags, F_NOBLESS)) {
int num;
num = rnd(1,100);
if (num <= 15) {
strcpy(cursestr, "cursed ");
} else if (num >= 85) {
strcpy(cursestr, "blessed ");
}
}
if (hasflag(ot->flags, F_ENCHANTABLE)) {
char buf2[BUFLENSMALL];
int bonus = 0;
int dir,chance;
if (strstr(cursestr, "cursed")){ // cursed WILL have a negative bonus
bonus = -1; // always at least -1
chance = 25;
dir = -1;
} else if (strstr(cursestr, "blessed")) { // blessed/uncursed MAY have a bonus
chance = 30;
dir = 1;
} else {
chance = 25;
dir = 1;
}
while (rnd(1,100) <= chance) {
bonus += dir;
}
snprintf(buf2, BUFLENSMALL, "%s%d ", (bonus >= 0) ? "+" : "", bonus);
strcat(cursestr, buf2);
}
// random chance of having an obmod
for (om = firstobmod ; om ; om = om->next) {
if (!hasflag(ot->flags, F_NOQUALITY)) {
f = hasflagval(ot->flags, F_CANHAVEOBMOD, om->id, NA, NA, NULL);
if (f && (f->val[1] != NA)) {
omposs[noms] = f;
noms++;
}
}
}
om = NULL;
if (noms) {
// pick a random one to maybe apply
f = omposs[rnd(0,noms-1)];
if (rnd(1,100) <= f->val[1]) {
om = findobmod(f->val[0]);
}
}
if (!om) {
// in sewers, everythinng is shoddy
if ((map->habitat->id == H_SEWER) && hasflagval(ot->flags, F_CANHAVEOBMOD, OM_SHODDY, NA, NA, NULL)) {
om = findobmod(OM_SHODDY);
}
}
if (om) {
strcat(cursestr, om->prefix);
strcat(cursestr, " ");
}
if ((map->habitat->id == H_SEWER) && (ot->obclass->id == OC_FOOD)) {
strcat(cursestr, "tainted ");
}
// get random chance of having a brand (1% per depth)...
// if so...
strcpy(brandname, "");
if (rnd(1,100) <= depth) {
brand_t *br;
br = getrandombrandfor(ot);
if (br) strcpy(brandname, br->suffix);
}
snprintf(buf, BUFLEN, "%d %s%s%s", amt, cursestr, pluralname,brandname);
if (db || partdb) dblog("random ob for %s: %d x %s ('%s')", habname, amt, ot->name,pluralname);
free(pluralname);
return buf;
}
char *getrandomob(map_t *map, char *buf) {
return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE, B_FALSE, OC_NONE, DT_NONE);
}
char *getrandomobofsize(map_t *map, char *buf, enum LFSIZE maxsize) {
return real_getrandomob(map, buf, NA, NA, maxsize, SK_NONE, B_FALSE, OC_NONE, DT_NONE);
}
char *getrandomobwithdt(map_t *map, enum DAMTYPE damtype, char *buf) {
return real_getrandomob(map, buf, NA, NA, SZ_MAX, SK_NONE, B_TRUE, OC_NONE, damtype, DT_NONE);
}
char *getrandomobwithclass(map_t *map, enum OBCLASS cid, char *buf, int depthmod) {
//return real_getrandomob(map, buf, RO_OBCLASS, cid, map->depth + depthmod);
//if (depthmod == NA) depthmod = 0;
return real_getrandomob(map, buf, getmapdifficulty(map) + depthmod, NA, SZ_MAX, SK_NONE, B_FALSE, cid, OC_NONE, DT_NONE);
}
enum OBCLASS getrandomobclass(enum HABITAT hab) {
enum RARITY wantrr;
objectclass_t *oc,*poss[MAXCANDIDATES];
int nposs = 0;
wantrr = pickrr(TT_OBJECT);
while (!nposs) {
for (oc = objectclass ; oc ; oc = oc->next) {
enum RARITY thisrarity = RR_NONE;
// if we were given a map, check the objectclass for specific
// rarity flag for the maps' habitat.
if (hab != H_ALL) {
flag_t *f;
f = hasflagval(oc->flags, F_RARITY, hab, NA, NA, NULL);
if (f) thisrarity = f->val[2];
}
// otherwise just use the default objectclass rarity
if (thisrarity == RR_NONE) thisrarity = oc->rarity;
if (oc->rarity == wantrr) {
poss[nposs++] = oc;
}
}
if (!nposs) {
if (wantrr > RR_FREQUENT) {
wantrr--;
} else {
// should never happen!
assert("getrandomobclass failed" == 0);
}
}
}
oc = poss[rnd(0,nposs-1)];
return oc->id;
}
int getobrarity(object_t *o, enum RARITY *rr) {
cell_t *c;
map_t *m = NULL;
flag_t *f;
if (rr) *rr = RR_FREQUENT;
// check for rarity on this object's map first
c = getoblocation(o);
if (c) {
m = c->map;
}
if (m) {
f = hasflagval(o->flags, F_RARITY,m->habitat->id, NA, NA, NULL);
if (f) {
if (rr) {
*rr = (f->val[2] == NA) ? RR_COMMON : f->val[2];
}
return f->val[1];
}
}
// any rarity value at all?
f = hasflag(o->flags, F_RARITY);
if (f) {
if (rr) {
*rr = (f->val[2] == NA) ? RR_COMMON : f->val[2];
}
return f->val[1];
}
// ie. doesn't randomly appear
return 0;
}
enum SPELLSCHOOL getschool(enum OBTYPE sid) {
objecttype_t *ot;
ot = findot(sid);
if (ot) {
flag_t *f;
f = hasflag(ot->flags, F_SPELLSCHOOL);
if (f) {
return f->val[0];
}
}
return SS_NONE;
}
char *getschoolname(enum SPELLSCHOOL sch) {
switch (sch) {
case SS_ABILITY: return "Abilities";
case SS_ALLOMANCY: return "Allomancy";
case SS_DIVINE: return "Divine Powers";
case SS_ENCHANTMENT: return "Enchantments";
case SS_WILD: return "Wild Magic";
case SS_MENTAL: return "Psionic Powers";
case SS_AIR: return "Air Magic";
case SS_FIRE: return "Fire Magic";
case SS_COLD: return "Cold Magic";
case SS_MODIFICATION: return "Modification Magic";
case SS_DEATH: return "Necromancy";
case SS_NATURE: return "Enviromancy";
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";
default:
break;
}
return "badschool";
}
char *getschoolnameshort(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_AIR: return "Air Magic";
case SS_FIRE: return "Fire Magic";
case SS_COLD: return "Cold Magic";
case SS_DEATH: return "Necromancy";
case SS_ENCHANTMENT: return "Enchantment";
case SS_LIFE: return "Life Magic";
case SS_MENTAL: return "Psionic Powers";
case SS_MODIFICATION: return "Modification";
case SS_NATURE: return "Nature";
case SS_DIVINATION: return "Divination";
case SS_TRANSLOCATION: return "Translocation";
case SS_SUMMONING: return "Summoning";
case SS_GRAVITY: return "Gravitation";
case SS_LAST: return "!invalid school!";
default:
break;
}
return "unknown school";
}
void setwaterdepth(cell_t *c, int depth) {
object_t *o;
if (depth > 0) {
o = hasobwithflag(c->obpile, F_DEEPWATER);
if (o) {
flag_t *f;
// adjust depth
f = hasflag(o->flags, F_DEEPWATER);
f->val[0] = depth;
}
} else {
int nkilled = 0;
enum MATERIAL killedmat = MT_NOTHING;
// water depth is now zero.
o = hasobwithflag(c->obpile, F_DEEPWATER);
while (o) {
if (killedmat == MT_NOTHING) killedmat = o->material->id;
killob(o);
nkilled++;
o = hasobwithflag(c->obpile, F_DEEPWATER);
}
if (nkilled) {
if (killedmat == MT_WATER) {
addob(c->obpile, "large puddle of water");
} else if (killedmat == MT_SLIME) {
addob(c->obpile, "puddle of slime");
}
}
}
}
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;
}
float getshopprice(object_t *o, lifeform_t *buyer) {
float val;
val = applyshoppricemod(getobvalue(o), buyer);
return val;
}
int getstairdirection(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_CLIMBABLE);
if ((f->val[0] == D_UP) || (f->val[0] == D_DOWN)) {
return f->val[0];
}
return D_NONE;
}
enum SKILLLEVEL gettechlevel(enum OBTYPE oid) {
flag_t *f;
objecttype_t *ot;
enum SKILLLEVEL tlev = PR_INEPT;
ot = findot(oid);
if (ot) {
f = hasflag(ot->flags, F_TECHLEVEL);
if (f) {
tlev = f->val[0];
}
}
return tlev;
}
int getthrowdam(object_t *o) {
double dam = 0;
flag_t *f;
// modify if it has a damage value
f = hasflag(o->flags, F_MISSILEDAM);
if (f) {
dam = f->val[0];
} else {
// base damage is = kilograms/5.
// ie. 1 kg object does 0 damage
// ie. 5 kg object does 1 damage
// ie. 100 kg object does 20 damage (person)
// ie. 1 tonne object does 200 damage (car)
dam = ceil((double)getobunitweight(o)) / 2;
// non-missile objects do 25% less damage
if (!hasflag(o->flags, F_THROWMISSILE)) {
dam = pctof(75, dam);
}
// soft materials do less damage
if (gethardness(o->material->id) < 2) {
dam /= 2;
}
}
//if (dam > 20) dam = 20;
// modify for bonus
f = hasflag(o->flags, F_BONUS);
if (f) {
dam += f->val[0];
}
// note: damage will also be modified based on speed in fireat()
return (int)ceil(dam);
}
// get either name of top object, or cell type
char *gettopobname(cell_t *c, char *retbuf) {
char buf[BUFLEN];
int nother;
strcpy(retbuf, "");
/*
if (c->type->solid) {
strcpy(retbuf, c->type->name);
nother = countnoncosmeticobs(c->obpile, B_TRUE);
if (nother) {
snprintf(buf, BUFLEN, " (+%d other thing%s)", nother, (nother == 1) ? "" : "s");
strcat(retbuf, buf);
}
return retbuf;
} else {
}
*/
object_t *o;
o = gettopobject(c, B_FALSE);
if (o) {
getobname(o, buf, o->amt);
strcat(retbuf, buf);
// other obs here too?
nother = countnoncosmeticobs(c->obpile, B_TRUE) - 1;
if (nother >= 1) {
snprintf(buf, BUFLEN, " (+%d other thing%s)", nother, (nother == 1) ? "" : "s");
strcat(retbuf, buf);
}
} else {
// just print the cell's name
strcat(retbuf, c->type->name);
}
if (c->writing) {
strcat(retbuf, ", writing:");
strcat(retbuf, c->writing);
}
return retbuf;
}
enum BODYPART getweildloc(object_t *o, enum BODYPART *otherloc, int *twohanded) {
enum BODYPART weildloc;
if (o) {
if (hasflag(o->flags, F_FIREARM)) {
weildloc = BP_SECWEAPON;
} else {
weildloc = BP_WEAPON;
}
if (twohanded) {
if (hasflag(o->flags, F_TWOHANDED)) {
*twohanded = B_TRUE;
} else {
*twohanded = B_FALSE;
}
}
} else {
// ie. unarmed
weildloc = BP_WEAPON;
if (twohanded) *twohanded = B_FALSE;
}
if (otherloc) {
if (weildloc == BP_WEAPON) {
*otherloc = BP_SECWEAPON;
} else {
*otherloc = BP_WEAPON;
}
}
return weildloc;
}
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 *hasequippedobid(obpile_t *op, enum OBTYPE oid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if ((o->type->id == oid) && isequipped(o)) return o;
}
return NULL;
}
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 *hasobletter(obpile_t *op, char letter) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->letter == letter) 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 *hasobofmaterial(obpile_t *op, enum MATERIAL mid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->type->material->id == mid) return o;
}
return NULL;
}
int hasobmod(object_t *o, obmod_t *om) {
flag_t *omf;
int found = B_TRUE;
for (omf = om->flags->first ; omf ; omf = omf->next){
if (!hasflagval(o->flags, omf->id, omf->val[0], omf->val[1], omf->val[2], NULL)) {
found = B_FALSE;
break;
}
}
return found;
}
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, long id) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->id == id) return o;
}
return NULL;
}
object_t *hassecretdoor(obpile_t *op) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (isdoor(o, NULL) && hasflag(o->flags, F_SECRET)) {
return o;
}
}
return NULL;
}
// fully identify a single object.
void identify(object_t *o) {
flag_t *f;
if (!isknown(o) && (o->type->obclass->id != OC_BOOK)) {
makeknown(o->type->id);
}
addflag(o->flags, F_IDENTIFIED, B_TRUE, -1, -1, NULL);
o->blessknown = B_TRUE;
for (f = o->flags->first ; f ; f = f->next) {
if (!f->known) f->known = B_TRUE;
}
}
void ignite(object_t *o) {
flag_t *ff;
char convertto[BUFLEN];
int howlong = 3; // default
strcpy(convertto, ""); // default
ff = hasflag(o->flags, F_FLAMMABLE);
if (ff) {
howlong = ff->val[0];
strcpy(convertto, ff->text);
}
if (!hasflag(o->flags, F_ONFIRE)) {
if (strlen(convertto)) {
cell_t *where;
object_t *newob;
where = getoblocation(o);
// change
newob = addob(where->obpile, convertto);
if (newob) {
// announce
if (haslos(player, where)) {
char obname[BUFLEN];
char newobname[BUFLEN];
getobname(o, obname, o->amt);
getobname(newob, newobname, newob->amt);
msg("%s ignites into %s!",obname,newobname);
}
// kill old ob
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
}
} else {
// make it damagable, if it isn't already.
if (!hasflag(o->flags, F_DAMAGABLE)) {
addflag(o->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
}
// on fire for 3 turns
addtempflag(o->flags, F_ONFIRE, B_TRUE, NA, NA, NULL, howlong);
}
}
}
// 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(objecttype_t *ammo, object_t *gun) {
if (hasflagval(gun->flags, F_AMMOOB, ammo->id, NA, NA, NULL)) {
return B_TRUE;
}
return B_FALSE;
}
int isbadfood(object_t *o) {
if (hasflag(o->flags, F_TAINTED)) {
return B_TRUE;
}
if (isrotting(o)) {
return B_TRUE;
}
switch (o->type->id) {
case OT_POT_POISON:
case OT_POT_ACID:
return B_TRUE;
default:
break;
}
return B_FALSE;
}
int isunknownbadobject(object_t *o) {
if (iscursed(o) && !o->blessknown) {
return B_TRUE;
}
if (isbadfood(o) && (getskill(player, SK_COOKING) < PR_BEGINNER)) {
return B_TRUE;
}
if (hasflag(o->flags, F_BADOBJECT) && !isknown(o)) {
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, lifeform_t *owner) {
//flag_t *f;
int dama,damb;
float acca,accb;
int db = B_FALSE;
char namea[BUFLEN];
char nameb[BUFLEN];
if (!a) return B_FALSE;
if (!b) return B_TRUE;
if (db) {
getobname(a, namea, a->amt);
getobname(b, nameb, b->amt);
}
getdamrange(a, NULL, NULL, &dama);
getdamrange(b, NULL, NULL, &damb);
// modify based on extra props
if (hasflag(a->flags, F_HASBRAND)) {
dama *= 3;
}
if (hasflag(a->flags, F_ONFIRE)) {
dama += 8;
}
if (hasflag(b->flags, F_HASBRAND)) {
damb *= 3;
}
if (hasflag(b->flags, F_ONFIRE)) {
damb += 8;
}
// modify with accuracy
acca = getobaccuracy(a, owner);
accb = getobaccuracy(b, owner);
if (db) {
msg("PREACC:a=%s:%d(acc %d), b=%s:%d(acc %d)",namea,dama,(int)acca, nameb, damb,(int)accb);
}
// add on the (acc/2)*damage to each one
dama = dama + pctof(acca/2, dama);
damb = damb + pctof(accb/2, damb);
if (db) {
msg("POST:a=%s:%d(acc %d), b=%s:%d(acc %d)",namea,dama,(int)acca, nameb, damb,(int)accb);
}
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 ((gamemode == GM_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 isdamaged(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f && (f->val[0] < f->val[1])) {
return B_TRUE;
}
return B_FALSE;
}
int isdangerousob(object_t *o, lifeform_t *lf, int onlyifknown) {
enum ATTRBRACKET iqb;
iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL);
if (!onlyifknown || (iqb >= AT_AVERAGE)) {
if (hasflag(o->flags, F_SHARP)) {
if (!getequippedob(lf->pack, BP_HANDS) && !isimmuneto(lf->flags, DT_SLASH, B_FALSE)) {
return B_TRUE;
}
}
}
if (!onlyifknown || (iqb >= IQ_ANIMAL)) {
if (hasflag(o->flags, F_ONFIRE)) {
if (!isimmuneto(lf->flags, DT_FIRE, B_FALSE)) {
return B_TRUE;
}
}
}
// undead won't touch blessed things - don't worry about
// onlyifknown, they can sense this.
if (isundead(lf) && isblessed(o)) {
return B_TRUE;
}
return B_FALSE;
}
int isdeadob(object_t *o) {
if (o->dying) {
return B_TRUE;
}
if (hasflag(o->flags, F_DEAD)) {
return B_TRUE;
}
return B_FALSE;
}
int isdrinkable(object_t *o) {
switch (o->type->obclass->id) {
case OC_POTION:
return B_TRUE;
default: break;
}
if (hasflag(o->flags, F_DRINKABLE)) {
return B_TRUE;
}
return B_FALSE;
}
int isedible(object_t *o) {
if (hasflag(o->flags, F_CREATEDBYSPELL)) return B_FALSE;
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) {
return hasflag(o->flags, F_EQUIPPED);
}
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 (!o) return B_FALSE;
if (hasflag(o->flags, F_FIREARM)) {
return B_TRUE;
}
return B_FALSE;
}
int isflammable(object_t *o) {
if (hasflag(o->flags, F_WET)) {
return B_FALSE;
}
if (hasflag(o->flags, F_FLAMMABLE)) {
return B_TRUE;
}
return B_FALSE;
}
int isknown(object_t *o) {
// if id'd, return the full name
if (hasflag(o->flags, F_IDENTIFIED)) {
return B_TRUE;
}
// unidentified books are always unknown
//if (o->type->id == OT_SPELLBOOK) {
if (o->type->obclass->id == OC_BOOK) {
return B_FALSE;
}
return isknownot(o->type);
}
int isknownot(objecttype_t *ot) {
knowledge_t *k;
// if id'd, return the full name
for (k = knowledge; k ; k = k->next) {
if (k->id == ot->id) {
// it DOES have a hidden name.
// does the player know about it?
if (k->known == B_KNOWN) {
return B_TRUE;
} else {
return B_FALSE;
}
}
}
// no hidden name, return real one
return B_TRUE;
}
int isheavyweapon(object_t *o) {
if (getobunitweight(o) >= HEAVYWEPKG) {
return B_TRUE;
}
return B_FALSE;
}
// 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;
// blessed status not known?
if (!isblessknown(o)) return B_FALSE;
// unknown object type?
if (!isknown(o)) return B_FALSE;
// unknown flags?
for (f = o->flags->first ; f ; f = f->next) {
if (!f->known) return B_FALSE;
}
return B_TRUE;
}
int isimpassableob(object_t *o, lifeform_t *lf) {
flag_t *f;
f = hasflag(o->flags, F_IMPASSABLE);
if (f) {
enum LFSIZE lfsize;
enum LFSIZE blockmin, blockmax;
if (!lf) return B_TRUE;
lfsize = getlfsize(lf);
blockmin = f->val[0];
blockmax = f->val[1];
if ((lfsize >= blockmin) && (lfsize <= blockmax)) {
return B_TRUE;
}
}
if (lf && (lf->race->raceclass->id == RC_UNDEAD)) {
if (hasflagval(o->flags, F_REPELBLESSED, B_CURSED, NA, NA, NULL)) {
return B_TRUE;
}
}
return B_FALSE;
}
int ismagical(object_t *o) {
if (hasflag(o->flags, F_HASBRAND)) {
return B_TRUE;
}
switch (o->type->obclass->id) {
case OC_SCROLL:
switch (o->type->id) {
case OT_SCR_NOTHING:
// these scrolls are non-magical
break;
default:
return B_TRUE;
break;
}
break;
case OC_RING:
case OC_WAND:
// all rings/wands are magical
return B_TRUE;
break;
case OC_POTION:
switch (o->type->id) {
case OT_POT_ACID:
case OT_POT_OIL:
case OT_POT_WATER:
case OT_POT_BLOOD:
case OT_POT_RUM:
case OT_POT_JUICE:
// these potions are non-magical
break;
default:
return B_TRUE;
break;
}
break;
case OC_BOOK:
if (hasflag(o->flags, F_LINKSPELL)) {
// ie. spellbooks
return B_TRUE;
}
break;
default:
break;
}
if (hasflag(o->flags, F_ENCHANTABLE) && hasflag(o->flags, F_BONUS)) {
return B_TRUE;
}
switch (o->type->id) {
case OT_SHILLELAGH:
case OT_WIZARDSTAFF:
return B_TRUE;
default: break;
}
return B_FALSE;
}
int ismeleeweapon(object_t *o) {
if (hasflag(o->flags, F_DAM)) {
return B_TRUE;
}
return B_FALSE;
}
int ismetal(enum MATERIAL mat) {
int metal = B_FALSE;
switch (mat) {
case MT_METAL:
case MT_GOLD:
case MT_SILVER:
case MT_WIRE:
metal = B_TRUE;
break;
default:
metal = B_FALSE;
break;
}
return metal;
}
int isthrowmissile(object_t *o) {
if (hasflag(o->flags, F_THROWMISSILE)) {
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;
}
// shoudl we append {tried} to this object?
int istried(object_t *o) {
if (hasflag(o->flags, F_IDENTIFIED)) {
return B_FALSE;
}
return istriedot(o->type);
}
int istriedot(objecttype_t *ot) {
knowledge_t *k;
if (ot->obclass->id == OC_BOOK) return B_FALSE;
for (k = knowledge; k ; k = k->next) {
if (k->id == ot->id) {
// it DOES have a hidden name.
// has the player tried it?
if (k->known == B_TRIED) {
return B_TRUE;
} else {
return B_FALSE;
}
}
}
// no hidden name, so can't be "tried"
return B_FALSE;
}
int isoperable(object_t *o) {
if (hasflag(o->flags, F_OPERABLE)) return B_TRUE;
//if (o->type->obclass->id == OC_TECH) return B_TRUE;
return B_FALSE;
}
// has this object changed proerties from its
// parent objecttype?
int isplainob(object_t *o) {
if (o->material != o->type->material) return B_FALSE;
if (o->weight != o->type->weight) return B_FALSE;
if (o->inscription) return B_FALSE;
if (o->blessed != B_UNCURSED) return B_FALSE;
if (isblessknown(o)) return B_FALSE;
return B_TRUE;
}
int ispourable(object_t *o) {
if (hasflag(o->flags, F_POURABLE)) {
if (o->material->id != MT_ICE) {
return B_TRUE;
}
}
return B_FALSE;
}
int ispushable(object_t *o) {
if (hasflag(o->flags, F_PUSHABLE)) {
return B_TRUE;
}
return B_FALSE;
}
int isreadable(object_t *o) {
switch (o->type->obclass->id) {
case OC_SCROLL:
case OC_BOOK:
return B_TRUE;
default: break;
}
return B_FALSE;
}
int isrotting(object_t *o) {
flag_t *f;
float pct;
if (hasflag(o->flags, F_TAINTED)) {
return B_TRUE;
}
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;
}
flag_t *issecretdoor(object_t *o) {
if (isdoor(o, NULL)) {
return hasflag(o->flags, F_SECRET);
}
return NULL;
}
// returns the 'shield' flag
flag_t *isshield(object_t *o) {
return hasflag(o->flags, F_SHIELD);
}
int issmellableob(object_t *o) {
if (hasflag(o->flags, F_SMELLY)) {
return B_TRUE;
}
return B_FALSE;
}
int isweapon(object_t *o) {
if (o->type->obclass->id == OC_WEAPON) {
return B_TRUE;
} else if (hasflag(o->flags, F_DAM)) {
return B_TRUE;
}
return B_FALSE;
}
int iswearable(object_t *o) {
if (hasflag(o->flags, F_GOESON)) {
return B_TRUE;
}
return B_FALSE;
}
void killallobs(obpile_t *op) {
while (op->first) {
killob(op->first);
}
}
// returns number of obs killed.
// terminate args with OT_NONE
int killallobsexcept(obpile_t *op, ...) {
va_list args;
enum OBTYPE exception[MAXCANDIDATES],thisob;
int nexceptions = 0,i,nkilled = 0;
object_t *o,*nexto;
va_start(args, op);
thisob = va_arg(args, enum OBTYPE);
while (thisob != OT_NONE) {
exception[nexceptions++] = thisob;
thisob = va_arg(args, enum OBTYPE);
}
va_end(args);
for (o = op->first ; o; o = nexto) {
int killthis = B_TRUE;
nexto = o->next;
for (i = 0;i < nexceptions; i++) {
if (o->type->id == exception[i]) {
killthis = B_FALSE;
break;
}
}
if (killthis) {
killob(o);
nkilled++;
}
}
return nkilled;
}
void killbrand(brand_t *b) {
brand_t *nextone, *lastone;
// free mem
if (b->description) free(b->description);
if (b->suffix) free(b->suffix);
killflagpile(b->flags);
// remove from list
nextone = b->next;
if (nextone != NULL) {
nextone->prev = b->prev;
} else { /* last */
lastbrand = b->prev;
}
if (b->prev == NULL) {
/* first */
nextone = b->next;
free(firstbrand);
firstbrand = nextone;
} else {
lastone = b->prev;
free (lastone->next );
lastone->next = nextone;
}
}
void killhiddenname(hiddenname_t *hn) {
hiddenname_t *nextone, *lastone;
// free mem
if (hn->text) free(hn->text);
hn->text = NULL;
// remove from list
nextone = hn->next;
if (nextone != NULL) {
nextone->prev = hn->prev;
} else { /* last */
lasthiddenname = hn->prev;
}
if (hn->prev == NULL) {
/* first */
nextone = hn->next;
free(firsthiddenname);
firsthiddenname = nextone;
} else {
lastone = hn->prev;
free (lastone->next );
lastone->next = nextone;
}
}
void killknowledge(knowledge_t *k) {
knowledge_t *nextone, *lastone;
// free mem
if (k->hiddenname) free(k->hiddenname);
// remove from list
nextone = k->next;
if (nextone != NULL) {
nextone->prev = k->prev;
} else { /* last */
lastknowledge = k->prev;
}
if (k->prev == NULL) {
/* first */
nextone = k->next;
free(knowledge);
knowledge = nextone;
} else {
lastone = k->prev;
free (lastone->next );
lastone->next = nextone;
}
}
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;
//dblog("killing object id %ld (%d x %s)", o->id, o->amt, o->type->name);
//if (o->pile->where) {
// dblog(" from cell %d,%d", o->pile->where->x, o->pile->where->y);
//}
// debugging
/*
if (o->type->id == OT_STAIRSUP) {
msg("warning: removing an up staircase!");
dblog("warning: removing an up staircase!");
assert(1 == 0);
}
*/
o->dying = B_TRUE;
// remove flags conferred by this object
if (o->pile->owner) {
loseobflags(o->pile->owner, o, ALLCONFERRED);
} else if (o->pile->where && !o->pile->where->lf && !hasflag(o->flags, F_NOGLYPH) && haslos(player, o->pile->where)) {
needredraw = B_TRUE;
}
// free mem
if (o->inscription) {
free(o->inscription);
o->inscription = NULL;
}
if (o->contents) {
killobpile(o->contents);
o->contents = NULL;
}
if (o->flags) {
killflagpile(o->flags);
o->flags = NULL;
}
// 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 killobmod(obmod_t *om) {
obmod_t *nextone, *lastone;
// free mem
if (om->prefix) free(om->prefix);
killflagpile(om->flags);
// remove from list
nextone = om->next;
if (nextone != NULL) {
nextone->prev = om->prev;
} else { /* last */
lastobmod = om->prev;
}
if (om->prev == NULL) {
/* first */
nextone = om->next;
free(firstobmod);
firstobmod = nextone;
} else {
lastone = om->prev;
free (lastone->next );
lastone->next = nextone;
}
}
void killobpile(obpile_t *op) {
killallobs(op);
free(op);
}
void killoc(objectclass_t *oc) {
objectclass_t *nextone, *lastone;
int i;
// free mem
free(oc->name);
free(oc->desc);
if (oc->flags) killflagpile(oc->flags);
for (i = 0; i < oc->nnouns; i++) {
free(oc->noun[i]);
}
// 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;
}
}
int knockbackob(object_t *o, int dir, int howfar, int power, lifeform_t *pusher) {
int i;
char obname[BUFLEN];
int seen;
cell_t *obcell, *c;
getobname(o,obname, o->amt);
obcell = getoblocation(o);
if (haslos(player, obcell)) {
seen = B_TRUE;
} else {
seen = B_FALSE;
}
if (dir == D_NONE) {
// failed!
return B_TRUE;
}
c = obcell;
for (i = 0; i < howfar; i++) {
cell_t *newcell;
newcell = getcellindir(c, dir);
if (!newcell) {
// hit a wall - stop here
break;
} else if (newcell->lf) {
// hit them - stop here
c = newcell;
break;
} else if (!cellwalkable(NULL, newcell, NULL)) {
// hit a wall - stop here
break;
}
c = newcell;
}
fireat(NULL, o, o->amt, c, power, NULL);
return B_FALSE;
}
// 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, B_NOEXPAND);
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(newlf, o);
f = hasflag(o->flags, F_OBHP);
if (f) {
newlf->maxhp = f->val[1];
newlf->hp = newlf->maxhp;
}
f = hasflag(o->flags, F_OBATTACKDELAY);
if (f) {
int origspeed;
int newspeed;
origspeed = getmovespeed(newlf);
killflagsofid(newlf->flags, F_MOVESPEED);
newspeed = (int)((float)origspeed * ((float)f->val[0] / 100.0));
addflag(newlf->flags, F_MOVESPEED, newspeed, NA, NA, NULL);
}
}
if (newlf) {
petify(newlf, lf);
}
return newlf;
}
// returns true if something hapepned
int makeduller(object_t *o, int howmuch) {
char obname[BUFLEN];
int oldbonus,newbonus;
int rv = B_FALSE;
// get object name before changing the bonus
getobname(o,obname, 1);
oldbonus = getobbonus(o, B_FALSE);
modbonus(o, -howmuch);
newbonus = getobbonus(o, B_FALSE);
if (newbonus < oldbonus) {
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s seems duller!",noprefix(obname));
} else if (cansee(player, o->pile->owner)) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
msg("%s%s %s seems duller!",lfname, getpossessive(lfname), noprefix(obname));
}
} else {
cell_t *obloc;
obloc = getoblocation(o);
if (haslos(player, obloc)) {
msg("%s seems duller!",obname);
}
}
rv = B_TRUE;
} else {
rv = B_FALSE;
}
return rv;
}
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;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
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) && !isknown(o)) {
getflags(o->flags, retflag, &nretflags, F_ACTIVATECONFER, F_EQUIPCONFER, F_HOLDCONFER, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
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_KNOWN;
}
}
for (i = 0; i < nobs; i++) {
giveobflags(player, srcob[i], fttogive[i]);
}
f = lfhasflagval(player, F_FAILEDINSPECT, otid, NA, NA, NULL);
if (f) {
killflag(f);
}
}
void maketried(enum OBTYPE otid) {
knowledge_t *k;
objecttype_t *ot;
// avoid adding '[tried]' to certain objects
ot = findot(otid);
if (ot && hasflag(ot->flags, F_NOTRIED)) {
return;
}
for (k = knowledge; k ; k = k->next) {
if (k->id == otid) {
if (k->known == B_UNKNOWN) {
k->known = B_TRIED;
}
}
}
}
void makewet(object_t *o, int amt) {
cell_t *loc;
lifeform_t *owner;
char ownername[BUFLEN];
char obname[BUFLEN];
char obnamefull[BUFLEN];
flag_t *f;
if (hasflag(o->flags, F_WATERPROOF)) {
return;
}
loc = getoblocation(o);
owner = o->pile->owner;
if (owner) {
getlfname(owner, ownername);
}
getobname(o,obname,o->amt);
if (owner) {
snprintf(obnamefull, BUFLEN, "%s%s %s",ownername, getpossessive(ownername), noprefix(obname));
} else {
strcpy(obnamefull, obname);
}
if (o->material->id == MT_METAL) {
if (amt < R_RUSTY) amt = R_RUSTY;
if (amt > R_TRUSTY) amt = R_TRUSTY;
f = hasflag(o->flags, F_RUSTED);
if (f) {
if (f->val[0] < R_TRUSTY) {
// make more rusty
f->val[0] += amt;
}
} else {
// rust
if (haslos(player, loc) && !isdead(player)) {
msg("%s rust%s.",obnamefull,OBS1(o));
}
f = addflag(o->flags, F_RUSTED, amt, NA, NA, NULL);
}
} else {
if (hasflag(o->flags, F_CANGETWET)) {
if (amt < W_DAMP) amt = W_DAMP;
if (amt > W_SOAKED) amt = W_SOAKED;
f = hasflag(o->flags, F_WET);
if (f) {
if (f->val[0] < W_SOAKED) {
// make wetter
/*
if (haslos(player, loc)) {
msg("%s get%s wetter.",obnamefull,OBS1(o));
}
*/
f->val[0] += amt;
f->val[1] = TM_WETTIME;
} else {
// jsut reset wettime
f->val[1] = TM_WETTIME;
}
} else {
// get wet
if (!isdead(player)) {
int doannounce = B_FALSE;
if (owner) {
if (cansee(player, owner)) doannounce = B_TRUE;
} else {
if (haslos(player, loc)) doannounce = B_TRUE;
}
if (doannounce) {
msg("%s get%s wet.",obnamefull,OBS1(o));
}
}
f = addflag(o->flags, F_WET, amt, TM_WETTIME, NA, NULL);
}
}
}
}
object_t *moveob(object_t *src, obpile_t *dst, int howmany) {
return real_moveob(src, dst, howmany, B_TRUE);
}
object_t *real_moveob(object_t *src, obpile_t *dst, int howmany, int stackok) {
object_t *o, *existob;
int i,preburdened = B_FALSE;
int db = B_FALSE;
int redrawaftermove = B_FALSE;
lifeform_t *robbedlf = NULL;
flag_t *f;
reason = E_OK;
if (!obfits(src, dst)) {
reason = E_NOSPACE;
return NULL;
}
// object being taken from an unconscious lf?
if (src->pile->owner && isunconscious(src->pile->owner)) {
robbedlf = src->pile->owner;
}
// adjust destination for pits
if (dst->where) {
object_t *pit;
pit = hasobwithflagval(dst->where->obpile, F_PIT, D_DOWN, NA, NA, NULL);
if (pit) {
cell_t *newcell;
newcell = getstairdestination(pit, NULL);
if (newcell) {
if (haslos(player, dst->where)) {
char obname[BUFLEN];
char pitname[BUFLEN];
getobname(src, obname, src->amt);
getobname(pit, pitname, 1);
msg("%s fall%s down %s.", obname, OBS1(src), pitname);
}
dst = newcell->obpile;
}
}
} else if (dst->owner) {
preburdened = isburdened(dst->owner);
}
// is the object moving FROM a cell that the player can see?
if (src->pile->where && haslos(player, src->pile->where)) {
if ((src->pile->where->lf) && !cansee(player, src->pile->where->lf)) {
redrawaftermove = B_TRUE;
} else {
redrawaftermove = B_TRUE;
}
}
if (db) dblog("DB: moveob() - moving %d x %s",howmany, src->type->name);
if (stackok) {
existob = canstackob(dst, src);
} else {
existob = NULL;
}
if (existob) {
if (db) dblog("DB: moveob() - found stack to join");
if (howmany == ALL) howmany = src->amt;
existob->amt += howmany;
src->amt -= howmany;
if (src->amt == 0) {
killob(src);
}
o = existob;
} else {
if (db) dblog("DB: moveob() - no stack to join");
// space in destination pile?
if (getnextletter(dst, NULL) == '\0') {
reason = E_NOSPACE;
return NULL;
}
// no similar objects in the dest pile.
if ((howmany == ALL) || (howmany == src->amt)) {
if (db) dblog("DB: dropping ALL - relinking object to new pile");
// just move the whole object to the new pile
o = relinkob(src, dst);
} else {
obpile_t *tempop;
if (db) dblog("DB: moving only some of a stack.");
// create temporary object pile
tempop = addobpile(NULL, NULL, 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);
}
killobpile(tempop);
}
}
if (o) {
if (dst->owner && isplayer(dst->owner) && isblessknown(o)) {
o->blessknown = B_TRUE;
}
if (robbedlf && !lfhasflag(robbedlf, F_WASROBBED)) {
addflag(robbedlf->flags, F_WASROBBED, B_TRUE, NA, NA, NULL);
}
if (hasflag(o->flags, F_DOOR)) {
killflagsofid(o->flags, F_LOCKED);
killflagsofid(o->flags, F_JAMMED);
killflagsofid(o->flags, F_SECRET);
if (!hasflag(o->flags, F_OPEN)) {
addflag(o->flags, F_OPEN, B_TRUE, NA, NA, NULL);
killflagsofid(o->flags, F_IMPASSABLE);
}
}
}
if (redrawaftermove) {
needredraw = B_TRUE;
drawscreen();
} else if (o && !hasflag(o->flags, F_NOGLYPH) && dst->where && haslos(player, dst->where)) {
// the object is moving to a cell that the player can see
if ((dst->where->lf) && !cansee(player, dst->where->lf)) {
// we can see the floor here
// someone invisible there who we can't see
needredraw = B_TRUE;
drawscreen();
} else {
// we can see the floor here
// noone there
needredraw = B_TRUE;
drawscreen();
}
}
// special effects when an object moves
f = hasflag(o->flags, F_SHOPITEM);
if (f) {
// recalculate object price baesd on who is holding it
calcshopprice(o, f);
}
// special effects if a lifeform picked up an object
if (dst->owner) {
flag_t *f;
// picked up an object in a shop
f = hasflag(o->flags, F_SHOPITEM);
if (f) {
lifeform_t *shk;
shk = findshopkeeper(dst->owner->cell->map, f->val[1]);
if (shk) {
askforpayment(shk, dst->owner);
}
}
// did this make us burdened?
if (isplayer(dst->owner)) {
checkburdened(dst->owner, preburdened);
}
// in case you picked up money, something which changes your AR, etc
if (isplayer(dst->owner)) {
statdirty = B_TRUE;
}
} else if (dst->where) {
object_t *oo,*nextoo;
// object hit the ground?
// triggered a trap?
for (oo = dst->where->obpile->first ; oo ; oo = nextoo ) {
nextoo = oo->next;
if (oo != o) {
f = hasflag(oo->flags, F_TRAP);
if (f && (f->val[1] != curtime)) {
triggertrap(NULL, o, oo, dst->where);
}
// bless objects which land on a holy circle.
if ((oo->type->id == OT_HOLYCIRCLE) && !blessob(o)) {
// holy circle vanishes.
if (onein(3)) {
if (haslos(player, dst->where)) {
char ooname[BUFLEN];
getobname(oo, ooname, 1);
msg("%s fades and vanishes.", ooname);
}
removeob(oo, oo->amt);
}
}
// curse objects which land on a pentagram.
if ((oo->type->id == OT_PENTAGRAM) && !curseob(o)) {
// pentagrams don't vanish as often as holy circles
if (onein(6)) {
if (haslos(player, dst->where)) {
char ooname[BUFLEN];
getobname(oo, ooname, 1);
msg("%s fades and vanishes.", ooname);
}
removeob(oo, oo->amt);
}
}
if ((oo->type->id == OT_WATERDEEP) && (getmaterialstate(o->material->id) == MS_SOLID)) {
noise(dst->where, NULL, NC_OTHER, SV_TALK, "a splash.", "Splash!");
}
}
}
}
//o = newobeffects(o);
return o;
}
void modbonus(object_t *o, int amt) {
flag_t *f;
f = hasflag(o->flags, F_BONUS);
if (f) {
f->val[0] += amt;
} else {
cell_t *loc;
int known;
loc = getoblocation(o);
if (haslos(player, loc)) {
known = B_TRUE;
} else {
known = B_FALSE;
}
f = addflag_real(o->flags, F_BONUS, amt, NA, NA, NULL, PERMENANT, known, -1);
}
// enforce limit
limit(&f->val[0], -7, 7);
}
void obaction(object_t *o, char *text) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s %s!", noprefix(obname), text);
} else if (cansee(player, o->pile->owner)) {
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;
o->dying = B_TRUE;
// handle object conversion
if (!doobdieconvert(o, B_TRUE)) {
char desc[BUFLEN];
real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE, B_FALSE);
if (!hasflag(o->flags, F_NOOBDIETEXT)) {
// announce the death
strcpy(desc, "");
f = hasflag(o->flags, F_OBDIETEXT);
if (f) {
snprintf(desc, BUFLEN, "%s", f->text);
} else if (oblastdamtype(o) == DT_DECAY) {
if (!lfhasflag(player, F_TRAINING)) {
snprintf(desc, BUFLEN, "%s completely rotted away", OB1(o,"has","have") );
}
} else {
snprintf(desc, BUFLEN, "%s destroyed", OB1(o,"is","are"));
}
if (strlen(desc)) {
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("^wYour %s %s!",noprefix(obname), desc);
} else if (cansee(player, o->pile->owner)) {
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]);
}
} else {
explodeob(o, f, f->val[1]);
}
return;
}
if (hasflag(o->flags, F_CONTAINER)) {
// dump object's contents into parent container
while (o->contents->first) {
moveob(o->contents->first, o->pile, ALL);
}
}
// flashes?
f = hasflag(o->flags, F_FLASHONDEATH);
if (f) {
if (f->val[2] == B_IFACTIVATED) {
if (isactivated(o)) {
brightflash(getoblocation(o),f->val[0], NULL);
}
} else {
brightflash(getoblocation(o),f->val[0], NULL);
}
}
// corpse decaying and on the ground?
if ((oblastdamtype(o) == DT_DECAY) && (o->type->id == OT_CORPSE)) {
if (o->pile->where) {
int minbones,maxbones;
char bonestr[BUFLEN];
minbones = o->weight / 10;
maxbones = o->weight / 5;
if (minbones <= 0) minbones = 1;
if (maxbones <= minbones) maxbones = 2;
snprintf(bonestr, BUFLEN, "%d-%d bones",minbones,maxbones);
addob(o->pile, bonestr);
}
}
}
killob(o);
}
int obfits(object_t *o, obpile_t *op) {
if (countobs(op, B_FALSE) >= MAXPILEOBS) {
return B_FALSE;
}
if (op->owner && (getnextletter(op, NULL) == '\0')) {
reason = E_NOSPACE;
return B_FALSE;
}
if (op->parentob && (getobsize(o) > getobsize(op->parentob) )) {
reason = E_NOSPACE;
return B_FALSE;
}
return B_TRUE;
}
enum DAMTYPE oblastdamtype(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_LASTDAMTYPE);
if (f) {
return f->val[0];
}
return DT_NONE;
}
int brandappliesto(brand_t *br, objecttype_t *ot) {
flag_t *f,*f2;
flag_t *retflag[MAXCANDIDATES];
int i,nretflags = 0;
if (br->bp == BP_WEAPON) {
if (ot->obclass->id != OC_WEAPON) {
return B_FALSE;
}
} else {
// TODO: how do we differentiate shields from guns?
if (!hasflagval(ot->flags, F_GOESON, br->bp, NA, NA, NULL)) {
return B_FALSE;
}
}
// other restrictions?
if (hasflag(br->flags, F_ONLYFOROBTYPE)) {
if (!hasflagval(br->flags, F_ONLYFOROBTYPE, ot->id, NA, NA, NULL)) {
return B_FALSE;
}
}
getflags(br->flags, retflag, &nretflags, F_ONLYFOROBWITHFLAG, F_ONLYFORDAMTYPE, F_ONLYFORWEPSKILL, F_NONE);
for (i = 0; i < nretflags; i++) {
f2 = retflag[i];
if (f2->id == F_ONLYFOROBWITHFLAG) {
if (!hasflag(ot->flags, f2->val[0])) return B_FALSE;
} else if (f2->id == F_ONLYFORDAMTYPE) {
enum DAMTYPE dt;
f = hasflag(ot->flags, F_DAM);
if (!f) {
return B_FALSE;
}
dt = f->val[0];
if (!hasflagval(br->flags, F_ONLYFORDAMTYPE, dt, NA, NA, NULL)) {
return B_FALSE;
}
} else if (f2->id == F_ONLYFORWEPSKILL) {
enum SKILL skid;
f = hasflag(ot->flags, F_USESSKILL);
if (!f) {
return B_FALSE;
}
skid = f->val[0];
if (!hasflagval(br->flags, F_ONLYFORWEPSKILL, skid, NA, NA, NULL)) {
return B_FALSE;
}
}
}
return B_TRUE;
}
int obmatchescondition(object_t *o, long opts) {
int ok;
if (opts) {
// default to not ok
ok = B_FALSE;
} else {
// default to ok
ok = B_TRUE;
}
if ((opts & AO_ONLYEQUIPPED) && hasflag(o->flags, F_EQUIPPED)) {
ok = B_TRUE;
}
if ((opts & AO_EQUIPPEDNONWEAPON)) {
flag_t *ff;
ff = hasflag(o->flags, F_EQUIPPED);
if (ff && !isweapon(o)) {
ok = B_TRUE;
}
}
if ((opts & AO_EDIBLE) && isedible(o)) ok = B_TRUE;
if ((opts & AO_DRINKABLE) && isdrinkable(o)) ok = B_TRUE;
if ((opts & AO_READABLE) && isreadable(o)) ok = B_TRUE;
if ((opts & AO_ARMOUR) && (o->type->obclass->id == OC_ARMOUR)) ok = B_TRUE;
if (opts & AO_DAMAGED) {
if (isdamaged(o)) {
ok = B_TRUE;
}
}
if ((opts & AO_WEARABLE) && iswearable(o)) ok = B_TRUE;
if ((opts & AO_WEILDABLE) && isweapon(o)) ok = B_TRUE;
if ((opts & AO_OPERABLE) && isoperable(o)) ok = B_TRUE;
if ((opts & AO_POURABLE) && ispourable(o)) ok = B_TRUE;
if ((opts & AO_NOTIDENTIFIED) && !isidentified(o)) ok = B_TRUE;
if ((opts & AO_NOTKNOWN) && !isknown(o)) ok = B_TRUE;
if (opts & AO_SPECIFIED) {
int n;
int found = B_FALSE;
// does retlist contain this?
for (n = 0; n < nretobs; n++) {
if (retobs[n] == o) {
found = B_TRUE;
break;
}
}
if (found) {
ok = B_TRUE;
}
}
return ok;
}
// returns the amount of light produced
int obproduceslight(object_t *o) {
flag_t *f;
int amt = 0;
f = hasflag(o->flags, F_PRODUCESLIGHT);
if (f) {
int thisamt = 0;
if (f->val[2] == IFACTIVE) {
if (isactivated(o)) {
sumflags(o->flags, F_PRODUCESLIGHT, &thisamt, NULL, NULL);
}
} else {
sumflags(o->flags, F_PRODUCESLIGHT, &thisamt, NULL, NULL);
}
amt += thisamt;
}
// flaming things should produce fire
if (amt == 0) {
if (hasflag(o->flags, F_ONFIRE) || (o->material->id == MT_FIRE)) {
amt += 1;
}
}
return amt;
}
// 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;
}
flag_t *obrestrictsmovement(object_t *o, lifeform_t *lf) {
flag_t *f;
f = hasflag(o->flags, F_RESTRICTMOVEMENT);
if (f) {
if (!lf) return NULL;
if (lf) {
if ((o->type->id == OT_WEB) && (lf->race->baseid == R_SPIDER)) {
} else if ((o->type->id == OT_VINE) && hasjob(lf, J_DRUID)) {
} else if (isairborne(lf) && (f->val[2] != B_TRUE)) {
} else {
return f;
}
}
}
return NULL;
}
// returns # objects which fell through
int obsfallthrough(cell_t *c, object_t *pit) {
object_t *oo,*nextoo,*uphole = NULL;
cell_t *belowcell;
char obname[BUFLEN],downholename[BUFLEN],upholename[BUFLEN];
int nfallen = 0;
belowcell = getstairdestination(pit, NULL);
getobname(pit,downholename, 1);
if (belowcell) {
char oid[BUFLENSMALL];
sprintf(oid, "%ld",pit->id);
uphole = findmapobwithflagval(belowcell->map, F_MAPLINK, NA, NA, NA, oid);
assert(uphole);
getobname(uphole,upholename, 1);
}
for (oo = c->obpile->first ; oo ; oo = nextoo) {
int canseebelowlf = B_FALSE;
char verb[BUFLEN];
nextoo = oo->next;
if (oo == pit) continue;
if (hasflag(pit->flags, F_PIT)) {
//nopickup objects don't fall down pits.
if (hasflag(oo->flags, F_NOPICKUP)) continue;
strcpy(verb, "fall");
} else if (pit->type->id == OT_GRATINGFLOOR) {
// only liquid falls through gratings
if (getmaterialstate(oo->material->id) != MS_LIQUID) continue;
strcpy(verb, "drain");
} else {
strcpy(verb, "fall");
}
// fall through!
if (haslos(player, c)) {
// player can see the top of the hole
getobname(oo,obname, oo->amt);
msg("%s %s%s through %s.", obname, verb, OBS1(oo),
downholename);
}
nfallen++;
if (belowcell) {
// remember if we can see the bottom cell before the object drops
if (belowcell->lf && cansee(player, belowcell->lf)) {
canseebelowlf = B_TRUE;
}
oo = moveob(oo, belowcell->obpile, oo->amt);
if (haslos(player, belowcell)) {
// player can see the bottom of the hole
getobname(oo,obname, oo->amt);
msg("%s %s%s through %s.", obname, verb, OBS1(oo), upholename);
}
// does the object hit anyone?
if (belowcell->lf && (getmaterialstate(oo->material->id) == MS_SOLID)) {
lifeform_t *lf;
char dambuf[BUFLEN];
lf = belowcell->lf;
if (canseebelowlf) {
char lfname[BUFLEN];
getobname(oo,obname, oo->amt);
getlfname(lf, lfname);
msg("%s hit%s %s!", obname, OBS1(oo), lfname);
}
snprintf(dambuf, BUFLEN, "a falling %s", oo->type->name);
losehp(lf, getthrowdam(oo) * 6, DT_PROJECTILE, NULL, dambuf);
}
} else {
killob(oo);
}
}
return nfallen;
}
int operate(lifeform_t *lf, object_t *o, cell_t *where) {
char buf[BUFLEN],obname[BUFLEN];
int playercansee;
flag_t *f;
int willid = B_FALSE;
if (!lfhasflag(lf, F_HUMANOID) || !hasbp(lf, BP_HANDS)) {
// only humanoids can zap things
if (isplayer(lf)) {
msg("You lack the manual dexterity to operate this.");
}
return B_TRUE;
}
if (lfhasflag(lf, F_RAGE)) {
if (isplayer(lf)) {
msg("You are too enraged to operate anything!");
}
return B_TRUE;
}
getobname(o, obname, 1);
if ((isplayer(lf)) || cansee(player, lf)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
// if not a wand, must know what a tool is before you can use it
// also use the same message whn you try to operate something non-operable
// to avoid using this to identify mistletoe, etc.
if (!isoperable(o)) {
if (isplayer(lf)) {
msg("You don't know how to use %s!", obname);
}
return B_TRUE;
}
if (!isknown(o) && !hasflag(o->flags, F_OPERWITHOUTID)) {
if (isplayer(lf)) {
msg("You don't know how to use %s!", obname);
}
return B_TRUE;
}
if (gettechlevel(o->type->id) > getskill(lf, SK_TECHUSAGE)) {
if (isplayer(lf)) {
msg("This technology is beyond your understanding.");
}
return B_TRUE;
}
// has known trap?
if (isplayer(lf)) {
if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) {
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) {
char ch;
snprintf(buf, BUFLEN,"Really operate %s?", obname);
ch = askchar(buf,"yn","n", B_TRUE, B_FALSE);
if (ch != 'y') {
msg("Cancelled.");
return B_TRUE;
}
}
}
}
// ask for target, if required
f = hasflag(o->flags, F_OPERNEEDTARGET);
if (f && !where) {
int ttype = TT_NONE,range = UNLIMITED;
// don't give hints about the object
if (isknown(o)) {
ttype = f->val[0];
}
if (f->val[2] != NA) range = f->val[2];
if (isplayer(lf)) {
char subprompt[BUFLEN];
snprintf(subprompt, BUFLEN, "%s->Aim->", obname);
if (strlen(f->text) > 0) {
where = askcoords(f->text, subprompt, ttype, lf, range, LOF_NEED, B_TRUE);
} else {
snprintf(buf, BUFLEN, "Where will you aim %s?",obname);
where = askcoords(buf, subprompt, ttype, lf, range, LOF_NEED, B_TRUE);
if (!haslos(lf, where)) {
msg("You can't see there!");
return B_TRUE;
}
}
} else {
char lfname[BUFLEN];
getlfname(lf, lfname);
dblog("BUG - ai (%s, id %d) using wand (%s) without target.",
lfname, lf->id, obname);
where = NULL;
}
if (!where) {
// cancel.
if (isplayer(lf)) msg("Cancelled.");
return B_TRUE;
} else {
if (f->val[1] != NA) {
cell_t *newwhere = NULL;
if ((f->val[1] & TR_NEEDLOS) && !haslos(lf, where)) {
if (isplayer(lf)) msg("You can't see there!");
return B_TRUE;
}
if ((f->val[1] & TR_NEEDLOF) && !haslof(lf->cell, where, LOF_NEED, &newwhere)) {
if (newwhere) {
// update destination
where = newwhere;
} else {
if (isplayer(lf)) msg("You have no line of fire to there!");
return B_TRUE;
}
}
}
}
}
f = hasflag(o->flags, F_OPERNEEDDIR);
if (f) {
char ch;
int dir;
// ask direction
ch = askchar(f->text, "yuhjklbn-","-", B_FALSE, B_TRUE);
if ((ch == '-') || !ch) {
msg("Cancelled.");
return B_TRUE;
} else {
dir = chartodir(ch);
where = getcellindir(lf->cell, dir);
}
}
if (touch(lf, o)) {
taketime(lf, getactspeed(lf));
return B_TRUE;
}
if (hasflag(o->flags, F_DEAD)) {
taketime(lf, getactspeed(lf));
return B_TRUE;
}
// objects with charges...
if (hasflag(o->flags, F_OPERUSECHARGE)) { // operating toggles on/off
if (isplayer(lf)) {
int chargesleft;
chargesleft = usecharge(o);
f = hasflag(o->flags, F_CHARGES); // should always be true!
if (chargesleft == -1) {
if (f) f->known = B_TRUE;
msg("Nothing happens - this item is empty.");
// you know it's out
taketime(lf, getactspeed(lf));
return B_FALSE;
} else if (ismagical(o) && (o->pile->owner == lf)) {
enum SKILLLEVEL slev;
// depending on your channeling skill, you might
// find out that it is running low.
slev = getskill(lf, SK_CHANNELING);
if (slev == PR_EXPERT) {
if (f) f->known = B_TRUE;
}
if (slev && (chargesleft == 0)) {
if (f) f->known = B_TRUE;
msg("Your %s is now empty.", noprefix(obname));
} else if (slev && chargesknown(o)) {
if (f) f->known = B_TRUE;
if (chargesleft == 1) {
msg("Your %s only has one charge remaining.", noprefix(obname));
} else {
msg("Your %s now has %d charges remaining.", noprefix(obname), chargesleft);
}
} else {
switch (slev) {
case PR_INEPT:
break;
case PR_NOVICE: // notify when 1-3 charges left
if (chargesleft <= 3) {
msg("Your %s is running low on charges.", noprefix(obname));
}
break;
case PR_BEGINNER: // charges known at 1, notify at 1-3
if (chargesleft <= 3) {
msg("Your %s is running low on charges.", noprefix(obname));
}
break;
default:
break;
}
} // end if slev& chargesleft is 0
} // end if chargesleft== -1 / ismagical
} // end if isplayer
} // end if hasflag operusecharge
// TODO: change to be based on item type
// TODO: move this to the end in case 'operate' fails
taketime(lf, getactspeed(lf));
// check for gremlins
if ((o->type->obclass->id == OC_TECH) || (o->type->obclass->id == OC_TOOLS) ||
(o->type->obclass->id == OC_WAND)) {
if (lf->race->id != R_GREMLIN) {
cell_t *retcell[MAXCANDIDATES],*c;
int nretcells,i;
getradiuscells(lf->cell, 5, DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0);
for (i = 0; i < nretcells; i++) {
c = retcell[i];
if (c->lf && (c->lf->race->id == R_GREMLIN)) {
if (isplayer(lf)) {
msg("Inexplicably, your %s doesn't seem to work.", noprefix(obname));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s doesn't seem to work.", lfname, getpossessive(lfname),
noprefix(obname));
}
return B_TRUE;
}
}
}
} // end if techtype affected by gremlins
if (o->type->obclass->id == OC_TECH) {
setskillused(lf, SK_TECHUSAGE);
}
// mark obejct as tried
if (isplayer(lf)) maketried(o->type->id);
// trapped?
if (doobtraps(o, lf)) {
return B_TRUE;
}
if (hasflag(o->flags, F_CONTAINER) && !hasflag(o->flags, F_SHOP)) { // loot it
if (isplayer(lf)) { // only player can loot.
char ch;
if (hasflag(o->flags, F_LOCKED)) {
msg("The %s seems to be locked.", noprefix(obname));
return B_TRUE;
}
snprintf(buf, BUFLEN, "Looting %s. Will you:",obname);
initprompt(&prompt, buf);
if (countobs(lf->pack, B_FALSE)) {
snprintf(buf, BUFLEN, "Put items in %s",obname);
addchoice(&prompt, 'i', buf, NULL, NULL, NULL);
}
if (countobs(o->contents, B_FALSE)) {
snprintf(buf, BUFLEN, "Take items out of %s",obname);
addchoice(&prompt, 'o', buf, NULL, NULL, NULL);
}
snprintf(buf, BUFLEN, "Both");
addchoice(&prompt, 'b', buf, NULL, NULL, NULL);
snprintf(buf, BUFLEN, "Neither");
addchoice(&prompt, 'n', buf, NULL, NULL, NULL);
prompt.maycancel = B_TRUE;
ch = getchoice(&prompt);
switch (ch) {
case 'i':
dodrop(player->pack, B_MULTIPLE, o->contents);
break;
case 'o':
dopickup(o->contents, B_TRUE);
break;
case 'b':
dodrop(player->pack, B_MULTIPLE, o->contents);
dopickup(o->contents, B_TRUE);
break;
default:
case 'n':
msg("Cancelled.");
return B_FALSE;
}
}
} else if (hasflag(o->flags, F_OPERONOFF)) { // operating toggles on/off
if (isactivated(o)) {
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->type, o)) {
retobs[nretobs] = oo;
nretobs++;
}
}
if (nretobs <= 0) {
snprintf(buf, BUFLEN, "You have no ammo for your %s!",noprefix(obname));
msg(buf);
return B_TRUE;
} else {
snprintf(buf, BUFLEN, "Load %s with what ammo",obname);
oo = askobject(lf->pack, buf, NULL, 'l', AO_SPECIFIED);
if (oo) {
if (isammofor(oo->type, o)) {
loadfirearm(lf, o, oo);
} else {
if (isplayer(lf)) msg("That can't be used as ammo for %s.", obname);
return B_TRUE;
}
} else {
if (isplayer(lf)) msg("Cancelled.");
return B_TRUE;
}
}
} else if (o->type->obclass->id == OC_WAND) {
if (!isplayer(lf) && cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s zaps %s.",lfname,obname);
}
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
enum OBTYPE spelltocast;
int power;
enum SKILLLEVEL chanlev;
spelltocast = f->val[0];
power = f->val[1];
if (power == NA) power = 1;
if (isblessed(o)) power += 4;
// increase based on your magic item usage skill
chanlev = getskill(lf, SK_CHANNELING);
setskillused(lf, SK_CHANNELING);
power += chanlev;
if (chanlev >= PR_ADEPT) power += (chanlev - 2);
// certain wands always used the blessed version of spells
// certain wands have different effects when cursed
switch (o->type->id) {
case OT_WAND_DIGGING:
if (isplayer(lf) && !isknown(o)) {
msg("This is a wand of digging!"); more();
makeknown(o->type->id);
}
break;
default:
break;
}
dospelleffects(lf, spelltocast, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
// special wands
} else if (o->type->id == OT_WAND_WONDER) {
int power;
if (lf->race->id != R_GREMLIN) {
// override power
power = rnd(1,10);
// 1 in 3 chance of targetting yourself
if (onein(3)) {
where = lf->cell;
}
}
// random effect
switch (rnd(0,22)) {
case 0: // butterflies around user
willid = B_TRUE;
where = getrandomadjcell(lf->cell, WE_WALKABLE, B_ALLOWEXPAND);
addmonster(where, R_BUTTERFLY, NULL, B_FALSE, rnd(10,20), B_FALSE, NULL);
if (haslos(player, where)) {
msg("A swarm of butterflies appears!");
}
break;
case 1: // summon monster
dospelleffects(lf, OT_S_CREATEMONSTER, rnd(1,4), NULL, NULL, NULL, B_UNCURSED, &willid, B_FALSE);
break;
case 2: // animate dead
dospelleffects(lf, OT_S_ANIMATEDEAD, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 3: // blindness
dospelleffects(lf, OT_S_BLINDNESS, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 4: // levitate
dospelleffects(lf, OT_S_LEVITATION, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 5: // dispersal
dospelleffects(lf, OT_S_DISPERSAL, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 6: // flash
dospelleffects(lf, OT_S_FLASH, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 7: // light
dospelleffects(lf, OT_S_LIGHT, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 8: // heal
dospelleffects(lf, OT_S_HEALING, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 9: // minor heal
dospelleffects(lf, OT_S_HEALINGMIN, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 10: // invis
dospelleffects(lf, OT_S_INVISIBILITY, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 11: // haste
dospelleffects(lf, OT_S_HASTE, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 12: // pull
dospelleffects(lf, OT_S_SUCK, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 13: // blast
dospelleffects(lf, OT_S_AIRBLAST, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 14: // slow
dospelleffects(lf, OT_S_SLOW, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 15: // sleep
dospelleffects(lf, OT_S_SLEEP, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 16: // gas
dospelleffects(lf, OT_S_CLOUDKILL, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 17: // dark
dospelleffects(lf, OT_S_DARKNESS, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 18: // stone
dospelleffects(lf, OT_S_PETRIFY, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 19: // fireball
dospelleffects(lf, OT_S_FIREBALL, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 20: // poly
dospelleffects(lf, OT_S_POLYMORPH, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE);
break;
case 21: // teleport
dospelleffects(lf, OT_S_TELEPORT, power, lf , NULL, lf->cell, B_UNCURSED, &willid, B_FALSE);
break;
case 22: // create banana peel
if (where->type->solid) {
setcelltype(where, where->map->habitat->emptycelltype);
} else {
addob(where->obpile, "3-5 banana skins");
}
break;
//oooooooooo
}
}
if (!isknown(o) && willid) {
if (isplayer(lf) || cansee(player, lf)) {
// tell player
makeknown(o->type->id);
if (isplayer(lf)) {
real_getobname(o, obname, 1, B_FALSE, B_TRUE, B_FALSE, B_TRUE, B_FALSE); // don't adjust for blindness
msg("This is %s!",obname);
}
}
}
// FROM HERE ON ARE INDIVIDUAL ONES
} else if (o->type->id == OT_BUTANETORCH) {
int seen = B_FALSE;
// if above half charges, big spray
dospelleffects(lf, OT_S_SPARK, 1, NULL, NULL, where, B_UNCURSED, &seen, B_FALSE);
// announce
if (!seen) {
noise(where, NULL, NC_OTHER, SV_WHISPER, "something burning.", NULL);
}
} else if (o->type->obclass->id == OC_GODSTONE) {
f = hasflag(o->flags, F_CHARGES);
if (f && (f->val[0] == f->val[1])) {
int x,y;
// announce
if (isplayer(lf)){
msg("Your %s unleashes a blast of power!", noprefix(obname));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s unleashes a blast of power!", lfname, getpossessive(lfname), noprefix(obname));
}
noise(lf->cell, NULL, NC_OTHER, 10, "an ear-splitting crack", NULL);
switch (o->type->id) {
case OT_GODSTONEJ: // justice
// everyone in lof drops to same hp as user
for (y = 0; y < lf->cell->map->h; y++) {
for (x = 0; x < lf->cell->map->w; x++) {
cell_t *c;
c = getcellat(lf->cell->map, x, y);
if (c && c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_NEED, NULL)) {
c->lf->hp = lf->hp;
if (isplayer(c->lf)) {
msg("You are blasted with the power of justice!");
} else if (cansee(player, c->lf)) {
char lfname[BUFLEN];
getlfname(c->lf, lfname);
msg("%s is blasted with the power of justice!", lfname);
}
}
}
}
break;
case OT_GODSTONER: // rage
// everyone in lof gets f_rage, and hates everything
for (y = 0; y < lf->cell->map->h; y++) {
for (x = 0; x < lf->cell->map->w; x++) {
cell_t *c;
c = getcellat(lf->cell->map, x, y);
if (c && c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_NEED, NULL)) {
int howlong = 50;
addtempflag(c->lf->flags, F_RAGE, B_TRUE, NA, NA, NULL, howlong);
if (!isplayer(c->lf)) {
addtempflag(c->lf->flags, F_HATESALL, B_TRUE, NA, NA, NULL, howlong);
loseaitargets(c->lf);
}
}
}
}
break;
default:
break;
}
f->val[0] = 0; // use up all charges
} else {
if (isplayer(lf)) {
nothinghappens();
}
}
taketime(lf, getactspeed(lf));
} 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 poison gas");
if (isplayer(lf)) {
msg("Psssssssst!");
seen = B_TRUE;
} else if (cansee(player, lf)) {
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 (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s%s %s emits a puff of gas.", buf, getpossessive(buf), noprefix(obname));
seen = B_TRUE;
}
}
// announce
if (!seen) {
noise(where, NULL, NC_OTHER, SV_WHISPER, "something spraying.", NULL);
}
} else if ((o->type->id == OT_EMPTYFLASK) || (o->type->id == OT_EMPTYVIAL)) {
object_t *oo,*nextoo;
if (isplayer(lf)) {
int donedip = B_FALSE;
// anything here to fill with?
for (oo = lf->cell->obpile->first ; (oo && !donedip); oo = nextoo) {
int dippable = B_FALSE;
nextoo = oo->next;
switch (oo->type->id) {
case OT_BLOODSPLASH:
case OT_SPLASHWATER:
if (oo->amt >= 5) {
dippable = B_TRUE;
}
break;
case OT_BLOODPOOL:
case OT_PUDDLEWATER:
case OT_PUDDLEWATERL:
case OT_FOUNTAIN:
dippable = B_TRUE;
break;
default:
break;
}
if (dippable) {
char ch;
char ques[BUFLEN];
char liquidname[BUFLEN];
int autoid = B_FALSE;
object_t *newob;
getobname(oo, liquidname, 1);
snprintf(ques, BUFLEN, "Fill your %s from %s?", noprefix(obname), liquidname);
ch = askchar(ques, "yn", "y", B_TRUE, B_FALSE);
if (ch == 'y') {
char newobname[BUFLEN];
if (oo->type->id == OT_FOUNTAIN) {
objecttype_t *ot;
// same type as fountain
f = hasflag(oo->flags, F_LINKOB);
ot = findot(f->val[0]);
strcpy(newobname, ot->name);
// if you knew what the fountain was, you'll
// know what the potion object is.
if (f->val[2] == B_TRUE) autoid = B_TRUE;
} else {
switch (oo->material->id) {
case MT_BLOOD:
strcpy(newobname, "potion of blood");
break;
case MT_WATER:
strcpy(newobname, "potion of water");
break;
default:
strcpy(newobname, "");
break;
}
}
if (strlen(newobname)) {
// kill the empty flask
removeob(o, 1);
// give the new potion
newob = addob(lf->pack, newobname);
if (newob) {
if (autoid) makeknown(newob->type->id);
// overwrite newobname
getobname(newob, newobname, newob->amt);
msgnocap("%c - %s.",newob->letter, newobname);
}
// kill the ground object?
switch (oo->type->id) {
case OT_SPLASHWATER:
case OT_BLOODSPLASH:
removeob(oo, 5);
break;
case OT_PUDDLEWATER:
removeob(oo, 1);
break;
case OT_FOUNTAIN:
if (hasflagval(oo->flags, F_LINKOB, OT_POT_EXPERIENCE, NA, NA, NULL) ||
onein(ONEIN_FOUNTAINDRYUP)) {
cell_t *loc;
loc = getoblocation(oo);
if (haslos(player, loc)) {
char fname[BUFLEN];
getobname(oo, fname, oo->amt);
msg("%s dries up.", obname);
}
removeob(oo, oo->amt);
}
break;
default:
break;
}
} else {
msg("That doesn't seem like a very good idea.");
}
donedip = B_TRUE;
} // if (ch == y)
}
}
if (!donedip) {
msg("There is nothing here to fill your %s from.",noprefix(obname));
}
}
} else if (o->type->id == OT_MISTLETOE) {
if (hasjob(lf, J_DRUID)) {
int amt;
if (isplayer(lf)) {
msg("You sacrifice %s.", obname);
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s sacrifices %s.", lfname, obname);
}
removeob(o, 1);
// regain mp
amt = getspellduration(getmaxmp(lf)/2, getmaxmp(lf), o->blessed);
if (amt > 0) {
if (isplayer(lf)) {
msg("You feel a surge of magical power!");
}
gainmp(lf, amt);
}
} else {
if (isplayer(lf)) {
msg("You don't know how to use this.");
}
}
} else if (o->type->id == OT_ORBDUNGEONEXIT) {
map_t *m;
m = lf->cell->map;
if ((m->region->rtype->id == RG_MAINDUNGEON) && (m->depth == 1)) {
cell_t *cell[MAXCANDIDATES];
int ncells,i;
getradiuscells(lf->cell, 1, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_TRUE, cell, &ncells, B_FALSE);
for (i = 0; i < ncells; i++) {
if (hasob(cell[i]->obpile, OT_STAIRSUP)) {
object_t *o;
o = hasob(cell[i]->obpile, OT_MAGICBARRIER);
if (o) {
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
}
}
}
} else {
if (isplayer(lf)) {
nothinghappens();
}
}
} else if (o->type->id == OT_POCKETWATCH) {
if (isplayer(lf)) {
if (haslos(lf, lf->cell)) {
// if above ground, use wantpm (ie. can see the sun)
gettimetextfuzzy(buf, isoutdoors(lf->cell->map) ? B_TRUE : B_FALSE);
msg("It is currently %s.",buf);
} else {
msg("You cannot see!");
}
} else if (cansee(player, lf)) {
getlfname(lf, buf);
msg("%s looks at %s.",buf, obname);
}
} else if (o->type->id == OT_DIGITALWATCH) {
if (isplayer(lf)) {
if (haslos(lf, lf->cell)) {
gettimetext(buf);
msg("It is currently %s.",buf);
} else {
msg("You cannot see!");
}
} else if (cansee(player, lf)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s looks at %s.",buf, obname);
}
} 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, B_TRUE);
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;
flag_t *f;
if (hasflag(oo->flags, F_LOCKABLE)) {
f = hasflag(oo->flags, F_LOCKED);
if (f) {
killflag(f);
if (isplayer(lf)) {
msg("Your %s beeps.", noprefix(obname));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s beeps.", lfname, getpossessive(lfname),
noprefix(obname));
}
} else {
addflag(oo->flags, F_LOCKED, B_TRUE, 20, NA, NULL);
if (isplayer(lf)) {
msg("Your %s buzzes.", noprefix(obname));
} else if (cansee(player, lf)) {
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_PANPIPES) {
// announce
if (isplayer(lf)) {
msg("You play a few notes on your panpipes.");
} else {
noise(lf->cell, lf, NC_OTHER, SV_CAR, "the sound of panpipes.", "plays a tune on its panpipes.");
}
} else if (o->type->id == OT_PICKAXE) {
int ch,dir;
cell_t *c;
ch = askchar("Dig in which direction (- to cancel)", "yuhjklbn><-","-", B_FALSE, B_TRUE);
if ((ch == '-') || !ch) {
// cancel
clearmsg();
return B_TRUE;
} else if (ch == '>') {
if (digdown(lf, o)) {
// failed
return B_TRUE;
}
} else if (ch == '<') {
if (digup(lf, o)) {
// failed
return B_TRUE;
}
} else {
dir = chartodir(ch);
c = getcellindir(lf->cell, dir);
if (digcell(lf, c, o)) {
// failed
return B_TRUE;
}
} // end if ch is a direction
} else if (o->type->id == OT_SPANNER) {
int donesomething = B_FALSE;
if (!where) {
if (isplayer(lf)) msg("There is nothing to use your spanner on there!");
} else if (where->lf) {
if (isplayer(lf)) msg("There is someone in your way!");
} else {
object_t *o;
char ch;
flag_t *f;
for (o = where->obpile->first ; o ; o = o->next) {
int isopen;
// jammed doors
if (isdoor(o, &isopen)) {
if (!isopen) { // ie. if closed.
f = hasflag(o->flags, F_JAMMED);
if (f) {
ch = askchar("The hinges seem jammed. Loosen them", "yn", "y", B_TRUE, B_FALSE);
if (ch == 'y') {
char obname[BUFLEN];
getobname(o, obname, 1);
msg("You loosen the hinges on %s.", obname);
killflag(f);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
}
} else {
char obname[BUFLEN];
getobname(o, obname, 1);
snprintf(buf, BUFLEN, "Tighten the hinges on %s",obname);
ch = askchar(buf, "yn", "y", B_TRUE, B_FALSE);
if (ch == 'y') {
msg("You tighten the hinges on %s.", obname);
addflag(o->flags, F_JAMMED, rnd(5,10), B_TRUE, NA, NULL);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
}
}
}
}
// gratings
if ((o->type->id == OT_GRATINGFLOOR) && hasflag(o->flags, F_LOCKED)) {
char obname[BUFLEN];
getobname(o, obname, 1);
ch = askchar("It looks like you could remove the bolts. Do so", "yn", "y", B_TRUE, B_FALSE);
if (ch == 'y') {
msg("You remove the cover from %s.", obname);
killflagsofid(o->flags, F_LOCKED);
killflagsofid(o->flags, F_LOCKABLE);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
}
}
}
}
if (!donesomething) {
if (isplayer(lf)) msg("There is nothing to use your spanner on there!");
}
} else if (o->type->id == OT_STYPTIC) {
flag_t *retflag[MAXCANDIDATES];
int i,nretflags = 0;
int donesomething = B_FALSE;
getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE);
for (i = 0;i < nretflags; i++) {
switch (f->id) {
case IJ_ARTERYPIERCE:
case IJ_CHESTBLEED:
case IJ_HAMSTRUNG:
case IJ_HANDBLEED:
case IJ_LEGBLEED:
case IJ_EYELIDSCRAPED:
donesomething = B_TRUE;
killflag(f);
break;
default: break;
}
}
// fix bleeding too?
if (isbleeding(lf) == B_TRUE) {
donesomething = B_TRUE;
while (isbleeding(lf) == B_TRUE) {
gainhp(lf, 1);
}
}
if (donesomething) {
if (isplayer(lf)) msg("^gThe styptic has stopped your wounds from bleeding.");
removeob(o, 1);
taketime(lf, getactspeed(lf));
} else {
if (isplayer(lf)) msg("You are not bleeding right now.");
}
} 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, B_TRUE);
} else {
// nothing happens
if (isplayer(lf)) {
nothinghappens();
}
}
} else if (o->type->id == OT_TOWEL) {
object_t *oo;
if (isplayer(lf)) {
msg("You dry yourself off with %s.", obname);
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s dries itself off with %s.", lfname, obname);
}
// make objects dry
for (oo = lf->pack->first ;oo ; oo = oo->next) {
if (isequipped(oo)) {
killflagsofid(oo->flags, F_WET);
}
}
taketime(lf, getactspeed(lf));
} else if (hasflag(o->flags, F_SHOP)) {
shop(lf, o);
}
return B_FALSE;
}
enum RARITY pickrr(int whatfor) {
enum RARITY wantrr = RR_FREQUENT;
int chance = 2;
int mod = 0;
if ((gamemode == GM_GAMESTARTED) && hasflag(player->flags, F_EXTRALUCK)) {
if (whatfor == TT_OBJECT) {
wantrr++;
} else if (whatfor == TT_MONSTER) {
mod = 1;
}
}
// pick rr...
while ((wantrr < RR_VERYRARE) && onein(chance)) {
wantrr++;
//chance = wantrr + 1 + mod;
chance = 2 + mod;
}
return wantrr;
}
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 = NULL;
int doneask = B_FALSE;
getobname(o, obname, 1);
getlfname(lf, lfname);
if (isplayer(lf) || cansee(player, lf)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
// adjacent to a closed door?
if (isplayer(lf)) {
int d;
for (d = DC_N; d <= DC_NW; d++) {
cell_t *c;
c = getcellindir(lf->cell, d);
if (c) {
object_t *door;
door = hasobwithflag(c->obpile, F_DOOR);
if (door) {
snprintf(buf, BUFLEN, "Pour %s onto the door to the %s", obname, getdirname(d));
ch = askchar(buf, "yn", "n", B_TRUE, B_FALSE);
if (ch == 'y') {
// finished asking where to pour
doneask = B_TRUE;
dst = door;
}
}
}
}
}
if (!doneask) {
// tip onto what?
snprintf(buf, BUFLEN, "Pour %s onto the ground", obname);
ch = askchar(buf, "yn", "y", B_TRUE, B_FALSE);
if (ch == 'y') {
dst = NULL;
} else {
snprintf(buf, BUFLEN, "Pour %s onto what", obname);
// tip onto another object
dst = askobject(lf->pack, buf, NULL, '\0', 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) {
cell_t *dstloc;
flag_t *refillflag;
dstloc = getoblocation(dst);
// 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 (cansee(player, lf)) {
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 (cansee(player, lf)) {
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);
// god effects
if (isplayer(lf)) {
pleasegodmaybe(R_GODPURITY, 3);
angergodmaybe(R_GODDEATH, 15, GA_HERESY);
}
} else if ((o->type->id == OT_POT_WATER) && (o->blessed == B_CURSED)) { // unholy water
if (isplayer(lf)) {
msg("You pour %s onto %s.", obname,dstname);
}
o->blessknown = B_TRUE;
// curse whatever we poured onto
curseob(dst);
// we now know that this is unholy water
if (!isknown(o)) makeknown(o->type->id);
// god effects
if (isplayer(lf)) {
pleasegodmaybe(R_GODDEATH, 3);
angergodmaybe(R_GODPURITY, 25, GA_HERESY);
}
} else if (o->type->id == OT_POT_INVULN) {
flag_t *f;
f = hasflag(dst->flags, F_DAMAGABLE);
if (f) {
if (isplayer(lf)) {
msg("Your %s looks invulnerable!",noprefix(dstname));
if (!isknown(o)) makeknown(o->type->id);
appendinscription(dst, "invuln");
}
killflag(f);
}
} else if (o->type->id == OT_POT_RESTORATION) {
flag_t *f, *nextf;
if (isplayer(lf)) {
msg("Your %s looks as good as new!",noprefix(dstname));
}
// restore orig material
if (dst->material != dst->type->material) {
changemat(dst, dst->type->material->id);
}
// remove blessings/curses
setblessed(dst, B_UNCURSED);
dst->blessknown = B_TRUE;
// remove bonuses
killflagsofid(dst->flags, F_BONUS);
// remove temporary flags, obmod flags and modify some others
for (f = dst->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->lifetime > 0) {
killflag(f);
} else if (f->lifetime == FROMOBMOD) {
killflag(f);
} else if (f->id == F_FROZEN) {
killflag(f);
} else if (f->id == F_ONFIRE) {
killflag(f);
} else if (f->id == F_POISONED) {
killflag(f);
} else if (f->id == F_OBHP) {
f->val[0] = f->val[1];
}
}
// we now know what the potion was
if (!isknown(o)) makeknown(o->type->id);
//if (!isknown(dst)) makeknown(dst->type->id);
} else if (o->type->id == OT_POT_ACID) {
// damage destinaiton
msg("You pour %s onto %s.", obname,dstname);
takedamage(dst, rnd(5,15), DT_ACID);
makeknown(o->type->id);
} else if (isdoor(dst, NULL)) {
msg("Your pour %s all over %s.", obname, dstname);
if (o->type->id == OT_POT_OIL) {
flag_t *f;
// unjam doors
f = hasflag(dst->flags, F_JAMMED);
if (f) {
killflag(f);
}
}
} else { // default
if (isplayer(lf)) {
msg("You pour %s onto %s.", obname,dstname);
if (!hasflag(dst->flags, F_CANGETWET)) {
msg("%s gets wet.", dstname);
// if it DOES have this flag, then we'll announce this
// during takedamage.
}
}
takedamage(dst, 0, DT_WATER);
}
} else {
// pour onto ground
if (isplayer(lf)) {
msg("You pour %s onto the ground.", obname);
} else if (haslos(player, lf->cell)) {
msg("%s pours %s onto the ground.", lfname, obname);
}
if (!doobdieconvert(o, B_FALSE)) {
if (haslos(player, lf->cell)) {
msg("The contents evaporate.");
}
}
}
// empty the flask
if (lfhasflag(lf, F_NOPACK)) {
// ie. if you drunk a gaseous form potion
addemptyob(lf->cell->obpile, o);
} else {
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;
int killobwhendone = B_TRUE;
flag_t *drinkflag;
enum OBTYPE realobid = OT_NONE;
if (lfhasflag(lf, F_RAGE)) {
if (isplayer(lf)) msg("You are too enraged to drink!");
return;
}
getobname(o, obname, 1);
if (isplayer(lf) || cansee(player, lf)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
taketime(lf, getactspeed(lf));
if (o->type->id == OT_FOUNTAIN) {
flag_t *f;
// ie if not already identified...
f = hasflag(o->flags, F_LINKOB);
realobid = f->val[0];
} else {
realobid = o->type->id;
}
if (isplayer(lf)) maketried(realobid);
if (touch(lf, o)) {
return;
}
if (hasflag(o->flags, F_DEAD)) return;
if (o->material->id == MT_ICE) {
if (isplayer(lf)) {
msg("You can't drink this, it's frozen!");
}
return;
}
if (lf->controller != C_PLAYER) {
if (cansee(player, lf)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s drinks %s!", buf, obname);
}
}
// figure out whether to id the object
willid = B_FALSE;
if (playercansee) {
if (o->type->id == OT_FOUNTAIN) {
flag_t *f;
f = hasflag(o->flags, F_LINKOB);
if (f->val[2] == B_TRUE) {
// fountain which is already identified
willid = B_FALSE;
}
// otherwise identify based on potion effects...
}
switch (realobid) {
case OT_NONE:
willid = B_FALSE;
break;
case OT_POT_HEALING:
case OT_POT_HEALINGMIN:
if (lf->hp < lf->maxhp) {
willid = B_TRUE;
}
break;
case OT_FOUNTAIN:
break;
default:
willid = B_TRUE;
break;
}
}
if (willid) {
makeknown(realobid);
if (o->type->id == OT_FOUNTAIN) {
// refresh fountain name
getobname(o, obname, 1);
if (isplayer(lf)) {
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
} else if (!isknown(o)) {
real_getobname(o, obname, 1, B_FALSE, B_TRUE, B_FALSE, B_TRUE, B_FALSE); // don't adjust for blindness
if (isplayer(lf)) {
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
}
}
if (o->type->obclass->id == OC_POTION) {
potioneffects(lf, o->type->id, o, o->blessed, &seen);
} else {
drinkflag = hasflag(o->flags, F_DRINKABLE);
if (drinkflag) {
// this is a drinkable thing which isn't a potion
flag_t *f;
f = hasflag(o->flags, F_LINKOB);
if (f) {
potioneffects(lf, f->val[0], o, o->blessed, NULL);
} else {
eat(lf, o);
}
// fountains sometimes dry up
if ((o->type->id == OT_FOUNTAIN) && onein(ONEIN_FOUNTAINDRYUP)) {
cell_t *loc;
// dry up (ie. remove DONTKILL property)
drinkflag->val[2] = NA;
loc = getoblocation(o);
if (haslos(player, loc)) {
msg("%s dries up.", obname);
}
}
if (drinkflag->val[2] == B_DONTKILL) {
killobwhendone = B_FALSE;
}
}
}
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);
}
if (killobwhendone) {
// remove the potion (or whatever it is)
removeob(o, 1);
}
}
// o can be NULL if we're just doing potion EFFECTS, not actually drinking one.
void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE potblessed, int *seen) {
char lfname[BUFLEN];
int dam;
int i;
int first,dir;
int amt;
int failed;
int seenbyplayer;
int hpheal,mpheal;
flag_t *f;
if (isplayer(lf)) {
seenbyplayer = B_TRUE;
} else if (cansee(player, lf)) {
seenbyplayer = B_TRUE;
} else {
seenbyplayer = B_FALSE;
}
if (seen) {
*seen = seenbyplayer;
}
getlfname(lf, lfname);
switch (oid) {
case OT_POT_ACID:
if (isplayer(lf)) {
msg("Your suffer massive internal burning!");
} else if (cansee(player, lf)) {
msg("%s writhes in agony!", lfname);
}
dam = rnd(5,10);
losehp(lf, dam, DT_ACID, NULL, "drinking acid");
break;
case OT_POT_ACROBATICS:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 30, 0)) {
if (isplayer(lf)) {
msg("You feel momentarily jumpy.");
}
break;
}
// how long for?
i = geteffecttime(5,10, potblessed);
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) {
gainhp(lf, lf->maxhp); // ie. full hp
if (isplayer(lf)) {
msg("You feel completely healed!");
} else if (cansee(player, lf)) {
msg("%s looks completely healed!", lfname);
}
} else {
if (isplayer(lf)) {
msg("This makes you feel incredible!");
}
}
break;
case OT_POT_CANINETRACKING:
dospelleffects(lf, OT_S_CANINETRACKING, 5, lf, NULL, lf->cell, potblessed, seen, B_TRUE);
break;
case OT_POT_COFFEE:
if (isplayer(lf)) {
msg("This tastes like coffee.");
}
// sates hunger a tiny bit
modhunger(lf, -pctof(10, (float)HUNGERCONST));
// sobers you up
killtransitoryflags(lf->flags, F_DRUNK);
// no more sleeping for a while!
addtempflag(lf->flags, F_CAFFEINATED, B_TRUE, NA, NA, NULL,rnd(30,60));
break;
case OT_POT_COMPETENCE:
failed = B_TRUE;
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 30, 0)) {
if (isplayer(lf)) {
msg("You feel momentarily %s.", (potblessed == B_CURSED) ? "incompetent" : "more competent");
}
break;
}
if (potblessed == B_CURSED) {
amt = -1;
} else {
amt = 1;
}
// select a random attribute
if (potblessed == B_BLESSED) {
// modify all attributes
for (i = 0; i < MAXATTS; i++) {
if (!modattr(lf, i, amt)) failed = B_FALSE;
}
} else {
enum ATTRIB a;
// modify just one attribute
a = rnd(0,MAXATTS-1);
if (!modattr(lf, a, amt)) failed = B_FALSE;
}
if (failed) {
if (isplayer(lf)) {
nothinghappens();
}
if (seen) *seen = B_FALSE;
} else {
if (seen) *seen = B_TRUE;
}
break;
case OT_POT_RUM:
// kill coffee flags
killtransitoryflags(lf->flags, F_CAFFEINATED);
if (lfhasflag(lf, F_CAFFEINATED)) {
// ie. conferred by something equipped etc
if (isplayer(lf)) {
msg("For some reason, the alcohol has no effect on you.");
}
} else {
// get drunk(er)
f = lfhasflag(lf, F_DRUNK);
if (f) {
if (isplayer(lf)) {
msg("You feel more tipsy.");
} else if (cansee(player, lf)) {
msg("%s looks more tipsy.", lfname);
}
f->lifetime += TM_DRUNKTIME;
} else {
addtempflag(lf->flags, F_DRUNK, 1, NA, NA, NULL, TM_DRUNKTIME);
}
}
// also a bit filling
if (potblessed != B_CURSED) {
modhunger(lf, -pctof(25, (float)HUNGERCONST));
}
break;
case OT_POT_ELEMENTIMMUNE:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 30, 0)) {
if (isplayer(lf)) {
msg("You feel momentarily immune to the elements.");
}
break;
}
// how long for?
i = geteffecttime(15,25,potblessed);
if (!isimmuneto(lf->flags, DT_FIRE, B_FALSE)) {
addtempflag(lf->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL, i);
}
if (!isimmuneto(lf->flags, DT_COLD, B_FALSE)) {
addtempflag(lf->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL, i);
}
break;
case OT_POT_ETHEREALNESS:
dospelleffects(lf, OT_S_PASSWALL, (potblessed) ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE);
break;
case OT_POT_EXPERIENCE:
// gain xp!
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 30, 0)) {
if (isplayer(lf)) {
msg("You feel momentarily more experienced.");
}
break;
}
if (isplayer(lf)) {
msg("You feel more experienced!");
// purposely not using gainxp
lf->xp = getxpforlev(lf->level+1);
} else {
if (cansee(player, lf)) {
msg("%s looks more experienced!", lfname);
}
}
gainlevel(lf, B_TRUE);
break;
case OT_POT_FURY:
abilityeffects(lf, OT_A_RAGE, lf->cell, lf, NULL);
break;
case OT_POT_GASEOUSFORM:
dospelleffects(lf, OT_S_GASEOUSFORM, (potblessed) ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE);
break;
case OT_POT_GROWTH:
if (iscursed(o)) {
dospelleffects(lf, OT_S_SIZEDOWN, 1, lf, NULL, lf->cell, B_UNCURSED, seen, B_TRUE);
} else {
dospelleffects(lf, OT_S_SIZEUP, 1, lf, NULL, lf->cell, B_UNCURSED, seen, B_TRUE);
}
break;
case OT_POT_HEALING:
dospelleffects(lf, OT_S_HEALING,potblessed ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE);
break;
case OT_POT_HEALINGMIN:
dospelleffects(lf, OT_S_HEALINGMIN,potblessed ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE);
break;
case OT_POT_HEALINGMAJ:
dospelleffects(lf, OT_S_HEALINGMAJ,potblessed ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE);
break;
case OT_POT_INVIS:
dospelleffects(lf, OT_S_INVISIBILITY,potblessed ? 6 : 3, lf, NULL, lf->cell, potblessed, seen, B_TRUE);
break;
case OT_POT_INVULN:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 30, 0)) {
if (isplayer(lf)) {
msg("You feel momentarily invulnerable.");
}
break;
}
i = geteffecttime(5,15,potblessed);
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_LEVITATION:
i = rnd(5,20);
addtempflag(lf->flags, F_LEVITATING, B_TRUE, NA, NA, NULL, i);
break;
case OT_POT_MAGIC:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 30, -10)) {
if (isplayer(lf)) {
msg("You feel your magical energy temporarily pulse.");
}
break;
}
if (getmaxmp(lf) > 0) {
msg("Your magical energy is restored.");
lf->mp = getmaxmp(lf);
} 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_POISON:
poison(lf, rnd(10,20), P_FOOD, 1, "a potion of poison");
break;
case OT_POT_POLYMORPH:
if (potblessed == B_BLESSED) {
// controlled polymorph - you can chnage back.
dospelleffects(NULL, OT_S_POLYMORPH, 5, lf, NULL, lf->cell, potblessed, NULL, B_TRUE);
} else {
// random polymorph
dospelleffects(NULL, OT_S_POLYMORPH, 1, lf, NULL, lf->cell, potblessed, NULL, B_TRUE);
}
break;
case OT_POT_RESTORATION:
failed = B_TRUE;
for (i = 0; i < MAXATTS; i++ ){
if (getattr(lf,i) < lf->baseatt[i]) {
setattr(lf, i, lf->baseatt[i]);
failed = B_FALSE;
}
}
// blessed restores hp/mp to full
if (potblessed == B_BLESSED) {
hpheal = lf->maxhp;
mpheal = getmaxmp(lf);
} else if (potblessed == B_CURSED) {
hpheal = lf->maxhp / 3;
mpheal = getmaxmp(lf) / 3;
} else {
hpheal = lf->maxhp / 2;
mpheal = getmaxmp(lf) / 2;
}
if (lf->hp < hpheal) {
gainhp(lf, hpheal - lf->hp);
if (!isplayer(lf) && cansee(player, lf)) {
msg("%s%s health has been restored!", lfname, getpossessive(lfname));
}
failed = B_FALSE;
}
if (lf->mp < mpheal) {
gainmp(lf, mpheal - lf->mp);
failed = B_FALSE;
}
if (failed) {
if (isplayer(lf)) msg("You feel momentarily restored.");
} else {
if (isplayer(lf)) msg("You feel restored!");
}
break;
case OT_POT_SANCTUARY:
// how long for?
//i = gethealtime(lf); // TODO: change...
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 30, 0)) {
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");
if (first && haslos(player, c)) {
msg("A magical barrier appears!");
first = B_FALSE;
}
// it will disappear eventually
addflag(newob->flags, F_OBHP, i, i, NA, NULL);
addflag(newob->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(newob->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
}
}
break;
case OT_POT_SLEEP:
dospelleffects(lf, OT_S_SLEEP, 8, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE);
if (seen) *seen = B_TRUE;
break;
case OT_POT_SPEED:
if (potblessed == B_BLESSED) {
dospelleffects(lf, OT_S_HASTE, 5, lf, NULL, lf->cell, B_BLESSED, NULL, B_TRUE);
} else if (potblessed == B_CURSED) {
dospelleffects(lf, OT_S_SLOW, 5, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE);
} else { // uncursed
dospelleffects(lf, OT_S_HASTE, 5, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE);
}
if (seen) *seen = B_TRUE;
break;
case OT_POT_SPIDERCLIMB:
i = geteffecttime(20,50,potblessed);
addtempflag(lf->flags, F_SPIDERCLIMB, B_TRUE, NA, NA, NULL, i);
if (!lfhasflagval(lf, F_CANWILL, OT_A_CLIMB, NA, NA, NULL)) {
addtempflag(lf->flags, F_CANWILL, OT_A_CLIMB, NA, NA, NULL, i);
}
break;
case OT_POT_WATER:
switch (potblessed) {
case B_BLESSED:
if (isundead(lf)) {
if (isplayer(lf)) {
msg("This tastes like water, but burns like acid!");
} else if (cansee(player, lf)) {
msg("%s writhes in agony!", lfname);
}
losehp(lf, rnd(5,15), DT_HOLY, NULL, "drinking holy water");
} else {
if (isplayer(lf)) msg("Mmm, holy water.");
}
break;
case B_UNCURSED:
if (isplayer(lf)) msg("Mmm, water.");
break;
case B_CURSED:
if (isplayer(lf)) msg("Yuck! Something is wrong with this water.");
break;
}
if (seen) {
if (isplayer(lf) || cansee(player, lf)) {
*seen = B_TRUE;
}
}
if (!isundead(lf)) {
modhunger(lf, -pctof(0.05, (float)HUNGERCONST));
}
break;
case OT_POT_BLOOD:
if (lf->race->id == R_VAMPIRE) {
int b = B_UNCURSED;
if (isplayer(lf)) msg("This blood is delicious!");
if (potblessed == B_BLESSED) {
b = B_CURSED;
} else if (potblessed == B_CURSED) {
b = B_BLESSED;
}
dospelleffects(lf, OT_S_HEALINGMAJ,b ? 5 : 1, lf, NULL, lf->cell, b, seen, B_TRUE);
modhunger(lf, -HUNGERCONST);
} else {
if (isplayer(lf)) msg("Yuck, this tastes like blood!");
}
break;
case OT_POT_BLOODC:
f = lfhasflag(lf, F_BEINGSTONED);
if (f) {
killflag(f);
} else {
if (isplayer(lf)) msg("Yuck, this tastes like oddly-flavoured blood!");
}
break;
case OT_POT_JUICE:
if (isplayer(lf)) {
switch (potblessed) {
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;
}
if (seen) *seen = B_TRUE;
}
if (potblessed != B_CURSED) {
modhunger(lf, -pctof(25, (float)HUNGERCONST));
modstamina(lf, 4);
}
break;
/////////// potions from recipes............
case OT_POT_SOUPCHICKEN:
if (isplayer(lf)) {
msg("Mmm, chicken soup!");
if (seen) *seen = B_TRUE;
}
modhunger(lf, -((float)HUNGERCONST/4));
killflagsofid(lf->flags, F_POISONED); // cure poison
gainhp(lf,rnd(1,5));
break;
case OT_POT_SOUPMUSHROOM:
case OT_POT_SOUPTOMATO:
if (isplayer(lf)) {
msg("Mmm, soup!");
if (seen) *seen = B_TRUE;
}
modhunger(lf, -((float)HUNGERCONST/2));
modstamina(lf, 2);
gainhp(lf,rnd(1,5));
break;
case OT_POT_SUGARWATER:
if (isplayer(lf)) {
msg("You feel a little more energetic.");
if (seen) *seen = B_TRUE;
}
modstamina(lf, 4);
gainhp(lf,rnd(1,5));
break;
case OT_POT_STROGONOFF:
if (isplayer(lf)) {
msg("Mmm, beef strogonoff!");
if (seen) *seen = B_TRUE;
}
modhunger(lf, -((float)HUNGERCONST));
// boost fitness
addtempflag(lf->flags, F_ATTRMOD, A_CON, 3, NA, NULL, rnd(50,100));
gainhp(lf,rnd(2,12));
break;
default: // nothing happens
break;
}
}
int readsomething(lifeform_t *lf, object_t *o) {
flag_t *f;
char buf[BUFLEN],obname[BUFLEN];
int playercansee;
int willid = B_FALSE;
int needsob = B_FALSE;
objecttype_t *linkspell;
int readtime;
if (!haslos(lf, lf->cell)) {
if (isplayer(lf)) {
msg("You can't read anything, since you can't see!");
}
return B_TRUE;
}
if (lfhasflag(lf, F_RAGE)) {
if (isplayer(lf)) {
msg("You are too enraged to read anything!");
}
return B_TRUE;
}
getobname(o, obname, 1);
if ((isplayer(lf)) || cansee(player, lf)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
if (o->type->obclass->id == OC_BOOK) {
readtime = SPEED_READ * 2;
} else {
readtime = SPEED_READ;
}
taketime(lf, readtime);
if (isplayer(lf)) maketried(o->type->id);
// some checks first...
if (touch(lf, o)) {
return B_TRUE;
}
if (hasflag(o->flags, F_DEAD)) {
return B_TRUE;
}
if (lf->controller != C_PLAYER) {
if (cansee(player, lf)) {
getlfname(lf, buf);
capitalise(buf);
msg("%s reads %s!", buf, obname);
}
}
// find linked spell
linkspell = getlinkspell(o);
// figure out whether to id the object
willid = B_FALSE;
if (playercansee) {
if (o->type->obclass->id == OC_BOOK) {
// is this a spellbook?
if (o->type->id == OT_SPELLBOOK) {
// if so, only id it if we can understand it.
willid = B_FALSE;
} else {
// ie. a manual
willid = B_TRUE;
}
} else {
switch (o->type->id) {
case OT_SCR_IDENTIFY:
case OT_SCR_MENDING:
if (isblessed(o)) {
willid = B_TRUE;
} else {
// only id if it does something
willid = B_FALSE;
}
case OT_SCR_REPLENISHMENT:
case OT_SCR_ENCHANT:
case OT_SCR_CREATEMONSTER:
case OT_SCR_REMOVECURSE:
case OT_SCR_DETECTAURA:
// 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?
f = hasflag(o->flags, F_SCROLLNEEDSOB);
if (f) {
switch (f->val[0]) {
case B_ALWAYS:
needsob = B_TRUE;
break;
case B_IFNOTBLESSED:
if (isblessed(o)) {
needsob = B_FALSE;
} else {
needsob = B_TRUE;
}
break;
default: break;
}
}
if (!isknown(o) && willid) {
// id the object
if (o->type->obclass->id == OC_BOOK) {
identify(o);
} else {
makeknown(o->type->id);
}
o->blessknown = B_TRUE;
real_getobname(o, obname, 1, B_FALSE, B_TRUE, B_FALSE, B_TRUE, B_FALSE); // don't adjust for blindness
if (isplayer(lf)) {
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
}
// special scrolls
if ((o->type->id == OT_SCR_IDENTIFY) && isblessed(o)) {
int seen = B_FALSE;
if (isplayer(lf)) {
object_t *oo;
// identify evertthing!
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (!isidentified(oo)) {
dospelleffects(lf, OT_S_IDENTIFY, 10, NULL, oo, NULL, o->blessed, &seen, B_FALSE);
}
}
}
if (seen) {
makeknown(o->type->id); o->blessknown = B_TRUE;
}
} else if ((o->type->id == OT_SCR_MENDING) && isblessed(o)) {
int seen = B_FALSE;
object_t *oo;
int power;
// mend everything!
power = getobspellpower(o, lf);
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (isdamaged(oo)) {
dospelleffects(lf, OT_S_MENDING, power, NULL, oo, NULL, o->blessed, &seen, B_FALSE);
}
}
if (seen) {
makeknown(o->type->id); o->blessknown = B_TRUE;
} else {
if (isplayer(lf) || cansee(player, lf)) nothinghappens();
}
if (isplayer(lf)) msg("The scroll crumbles to dust.");
removeob(o, 1);
} else 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;
int noeffect = 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)) {
flag_t *f2;
f2 = addflag(o->flags, F_BEINGUSED, B_TRUE, NA, NA, NULL);
targob = askobject(lf->pack, "Target which object", NULL, '\0', AO_NONE);
killflag(f2);
if (!targob) {
noeffect = B_TRUE;
}
}
if (!noeffect) {
cell_t *targcell = NULL;
lifeform_t *targlf = NULL;
// cursed scrolls target yourself
if (iscursed(o)) {
targcell = lf->cell;
targlf = lf;
}
castspell(lf, f->val[0], targlf, targob, targcell, o, &seen);
/*
dospelleffects(lf, f->val[0], power, NULL, targob, NULL, o->blessed, &seen, B_FALSE);
*/
if (seen) {
// id the scroll now
makeknown(o->type->id); o->blessknown = B_TRUE;
}
}
// removeob one of the object
if (isplayer(lf)) msg("The scroll crumbles to dust.");
removeob(o, 1);
} else if (o->type->id == OT_GRAPHPAPER) {
if (isplayer(lf)) {
msg("You study your hand-drawn map for a while.");
}
} else if (o->type->id == OT_MAP) {
if (isplayer(lf)) {
region_t *srcregion = NULL;
regionthing_t *destthing = NULL;
regiontype_t *destregiontype = NULL;
int srcdepth = -1, plural = B_FALSE;
char isare[BUFLENSMALL];
f = hasflag(o->flags, F_MAPTO);
if (f) {
srcregion = findregion(f->val[0]);
srcdepth = f->val[1];
destthing = findregionthing(f->val[2], NULL);
destregiontype = findregiontype(destthing->value);
plural = destregiontype->pluralname;
if (plural) {
strcpy(isare, "are");
} else {
strcpy(isare, "is");
}
}
if (f && srcregion) {
enum SKILLLEVEL slev;
slev = getskill(lf, SK_CARTOGRAPHY);
if (!slev) {
msg("You can't comprehend this map.");
} else {
if (lf->cell->map->region == srcregion) {
if (lf->cell->map->depth == srcdepth) {
// on the correct map. at this point the f_climbable flag should
// have been filled in correctly, and the destination region created.
cell_t *destcell;
object_t *destob;
char distbuf[BUFLEN],distbufbad[BUFLEN];
char dirbuf[BUFLEN];
region_t *destregion = NULL;
int dist;
destregion = findregionbytype(destthing->value);
destob = findmapobwithflagval(lf->cell->map, F_CLIMBABLE, NA, destregion->id, NA, NULL);
destcell = getoblocation(destob);
dist = getcelldist(lf->cell, destcell);
getdisttext(lf->cell, destcell, distbuf, distbufbad, dirbuf);
switch (slev) {
default: break; // should never happen
case PR_NOVICE:
// here/not here
msg("%s %s in this area!", f->text, isare);
break;
case PR_BEGINNER:
// direction to cell
msg("%s %s to the %s.", f->text, isare, dirbuf);
break;
case PR_ADEPT:
// direction +
// near/far dist to cell
msg("%s %s %s to the %s.", f->text, isare, distbufbad, dirbuf);
break;
case PR_SKILLED:
// direction +
// good dist to cell
msg("%s %s %s to the %s.", f->text, isare, distbuf, dirbuf);
break;
case PR_EXPERT:
// direction +
// exact dist to cell
msg("%s %s %d metres away to the %s.", f->text, isare, dist, dirbuf);
break;
case PR_MASTER:
// reveal it
msg("You have located %s in this area.", f->text);
setcellknownradius(destcell, PR_MASTER, 5, DT_ORTH);
needredraw = B_TRUE;
break;
}
} else {
// correct region, wrong map
int dist;
char buf[BUFLEN];
dist = abs(lf->cell->map->depth - srcdepth);
switch (slev) {
default: break; // should never happen
case PR_NOVICE:
case PR_BEGINNER:
// here/not here
msg("%s %s in this area.", f->text, plural ? "aren't" : "isn't");
break;
case PR_ADEPT:
// up/down
if (lf->cell->map->depth > srcdepth) {
msg("%s %s somewhere above you.", f->text, isare);
} else {
msg("%s %s somewhere below you.", f->text, isare);
}
break;
case PR_SKILLED:
// up/down and how far
if (dist == 1) {
strcpy(buf, "just");
} else if (dist <= 3) {
strcpy(buf, "somewhere");
} else {
strcpy(buf, "very far");
}
msg("%s %s %s %s you.", f->text, isare, buf,
(lf->cell->map->depth > srcdepth) ? "above" : "below");
break;
case PR_EXPERT:
case PR_MASTER:
// level number
msg("%s %s on level %d.", f->text, isare, srcdepth);
break;
}
} // end correct map or not?
} else {
// wrong region and wrong map
switch (slev) {
default: break; // should never happen
case PR_NOVICE:
case PR_BEGINNER:
// here/not here
msg("%s %s in this area.", f->text, plural ? "aren't" : "isn't");
break;
case PR_ADEPT:
case PR_SKILLED:
// tell which area it is in (without level)
getregionname(buf, NULL, srcregion, B_FALSE);
msg("%s %s somewhere within %s.", f->text, isare, buf);
break;
case PR_EXPERT:
case PR_MASTER:
// which area it is in, plus which floor
getregionname(buf, NULL, srcregion, B_TRUE);
msg("%s %s in %s.", f->text, isare, buf);
break;
}
} // end if correct region etc
} // if slev
} else {
msg("This map doesn't seem to be finished.");
}
} //end if isplayer
} else if (o->type->id == OT_SCR_AWARENESS) {
addtempflag(lf->flags, F_AWARENESS, B_TRUE, NA, NA, NULL, getspellduration(30,60,o->blessed));
if (isplayer(lf)) msg("The scroll crumbles to dust.");
// removeob one of the object
removeob(o, 1);
} else if (o->type->id == OT_SCR_AMNESIA) {
// lose all skill points
lf->skillpoints = 0;
lf->skillxp = 0;
if (isplayer(lf)) msg("^%dYou can't seem to remember any of your recent experiences!", C_RED);
if (isplayer(lf)) msg("The scroll crumbles to dust.");
// removeob one of the object
removeob(o, 1);
if (isplayer(lf)) statdirty = B_TRUE;
} else if (o->type->id == OT_SCR_NOTHING) {
if (isplayer(lf)) {
msg("The scroll crumbles to dust.");
}
// removeob one of the object
removeob(o, 1);
} else if (o->type->id == OT_SCR_PERMENANCE) {
int ndone = 0;
flag_t *f;
// makes certain flags permenant
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->lifetime > 0) && canbemadepermenant(f->id)) {
f->lifetime = PERMENANT;
ndone++;
}
}
if (isplayer(lf)) {
if (ndone) {
msg("You are surrounded by a stabilising aura.");
} else {
nothinghappens();
}
}
if (isplayer(lf)) {
msg("The scroll crumbles to dust.");
}
// removeob one of the object
removeob(o, 1);
} else if (o->type->id == OT_SCR_REMOVECURSE) {
int seen = B_FALSE;
object_t *oo;
// remove curses!
for (oo = lf->pack->first ; oo ; oo = oo->next) {
// if this object is cursed
if (iscursed(oo)) {
// blessed scrolls remove curse from everything you're holding.
// otherwise only equipped objects will be uncursed.
if (o->blessed || isequipped(oo)) {
uncurseob(oo, &seen);
}
}
}
if (seen) {
// id the scroll now
makeknown(o->type->id); o->blessknown = B_TRUE;
} else {
if (isplayer(lf)) {
nothinghappens();
}
}
if (isplayer(lf)) {
msg("The scroll crumbles to dust.");
}
// removeob one of the object
removeob(o, 1);
}
} else if (o->type->obclass->id == OC_BOOK) {
// is this a spellbook?
if (o->type->id == OT_SPELLBOOK) {
object_t *oo;
char ch = 'a';
enum SPELLSCHOOL school;
enum SKILLLEVEL slev;
f = hasflag(o->flags, F_LINKSCHOOL);
school = f->val[0];
slev = getskill(lf, getschoolskill(school));
if (!slev) {
if (isplayer(lf)) msg("You cannot comprehend the contents of this book.");
return B_FALSE;
}
// player now knows what it is - id it!
if (isplayer(lf) && !isknown(o)) {
identify(o);
o->blessknown = B_TRUE;
real_getobname(o, obname, 1, B_FALSE, B_TRUE, B_FALSE, B_TRUE, B_FALSE); // don't adjust for blindness
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
// we are skilled in the right school. now ask which spell to learn.
initprompt(&prompt, "Which spell will you try to learn?");
for (oo = o->contents->first ;oo ; oo = oo->next) {
if (!hasflagval(lf->flags, F_CANCAST, oo->type->id, NA, NA, NULL)) {
char *longdesc;
char shortdesc[BUFLEN];
longdesc = malloc(HUGEBUFLEN * sizeof(char));
sprintf(shortdesc, "%s (Lv %d)", oo->type->name, getspelllevel(oo->type->id));
if (getspellpower(lf, oo->type->id) <= 0) { // not possible to cast?
strcat(shortdesc, " [TOO HARD]");
}
makedesc_spell(oo->type, longdesc);
addchoice(&prompt, ch++, oo->type->name, shortdesc, oo->type, longdesc);
free(longdesc);
}
}
addchoice(&prompt, '-', "none", NULL, NULL, NULL);
if (prompt.nchoices == 1) {
msg("You already know all the spells in this book!");
return B_FALSE;
}
prompt.maycancel = B_TRUE;
ch = getchoicestr(&prompt, B_FALSE, B_TRUE);
if ((ch == '\0') || (ch == '-')) {
// not 'cancelled' because we still took time
msg("You close the spellbook without reading it.");
return B_FALSE;
}
linkspell = (objecttype_t *) prompt.result;
// too hard?
if (getspellpower(lf, linkspell->id) > 0) {
// try to learn it
int difficulty, mod;
difficulty = 15 + (getspelllevel(linkspell->id)*3);
mod = slev * 2;
if (skillcheck(lf, SC_LEARNMAGIC, difficulty, mod)) {
// learn it
addflag(lf->flags, F_CANCAST, linkspell->id, NA, NA, NULL);
} else {
msg("^bYou fail to learn %s.",linkspell->name);
}
} else {
if (isplayer(lf)) msg("This spell is too hard for you to learn right now.");
}
} else {
// manuals
f = hasflag(o->flags, F_MANUALOF);
if (f) {
giveskill(lf, f->val[0]);
}
if (isplayer(lf)) {
msg("The book crumbles to dust.");
}
// removeob one of the object
removeob(o, 1);
}
}
return B_FALSE;
}
object_t *relinkob(object_t *src, obpile_t *dst) {
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
killflagsofid(src->flags, F_EQUIPPED);
// 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 && isplayer(dst->owner)) {
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)) {
cell_t *pos;
pos = getoblocation(src);
if (pos) {
calclight(pos->map);
if (gamemode == GM_GAMESTARTED) {
setlosdirty(player);
//precalclos(player);
}
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)) {
checkflagpile(o->flags);
obdie(o);
}
}
}
// returns the amount left
int removeob(object_t *o,int howmany) {
int preburdened = B_FALSE;
int rv = 0;
lifeform_t *owner;
owner = o->pile->owner;
if (owner) {
preburdened = isburdened(o->pile->owner);
}
if (howmany == ALL) {
howmany = o->amt;
}
if (howmany >= o->amt) {
o->dying = B_TRUE;
killob(o);
rv = 0;
} else {
o->amt -= howmany;
rv = o->amt;
}
// did this make us burdened?
if (owner && isplayer(owner)) checkburdened(owner, preburdened);
return rv;
}
void resizeobject(object_t *o, enum LFSIZE wantsize) {
flag_t *f;
f = hasflag(o->flags, F_ARMOURSIZE);
if (f) {
f->val[0] = wantsize;
} else {
addflag(o->flags, F_ARMOURSIZE, wantsize, NA, NA, NULL);
}
}
void rrtorarity(enum RARITY r, int *minr, int *maxr) {
switch (r) {
case RR_UNIQUE:
case RR_NEVER:
if (minr) *minr = 0;
if (maxr) *maxr = 0;
break;
case RR_VERYRARE:
if (minr) *minr = 0;
if (maxr) *maxr = 49;
break;
case RR_RARE:
if (minr) *minr = 50;
if (maxr) *maxr = 64;
break;
case RR_UNCOMMON:
if (minr) *minr = 65;
if (maxr) *maxr = 79;
break;
case RR_COMMON:
if (minr) *minr = 80;
if (maxr) *maxr = 100;
break;
case RR_FREQUENT:
if (minr) *minr = 90;
if (maxr) *maxr = 100;
break;
default:
if (minr) *minr = 0;
if (maxr) *maxr = 100; // ie. rarity can be anything
break;
}
}
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);
}
}
}
}
int sethiddenname(objecttype_t *ot, char *text) {
int n;
int gotcolour = B_FALSE;
// add knowledge for it (unless it's a book)
if (ot->obclass->id != OC_BOOK) {
addknowledge(ot->id, text, B_UNKNOWN);
}
// some descriptions confer other effecst too...
if (strstr(text, "glowing")) {
addflag(ot->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
} else if (strstr(text, "flourescent")) {
addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL);
} else if (strstr(text, "luminous")) {
addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL);
} else if (strstr(text, "sparkling")) {
addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL);
}
// set colour based on hiddenname
for (n = 0; strlen(colour[n].name); n++) {
if (strstr(text, colour[n].name)) {
flag_t *gf;
gf = hasflag(ot->flags, F_GLYPH);
if (gf) {
gf->val[0] = colour[n].col;
} else {
addflag(ot->flags, F_GLYPH, colour[n].col, ot->obclass->glyph.ch, NA, NULL);
gotcolour = B_TRUE;
//dblog("assigning colour %s to %s (%s)",colour[n].name, text, ot->name);
break;
}
}
}
if (!gotcolour) {
for (n = 0; strlen(gemtype[n].name); n++) {
if (strstr(text, gemtype[n].name)) {
flag_t *gf;
gf = hasflag(ot->flags, F_GLYPH);
if (gf) {
gf->val[0] = gemtype[n].col;
} else {
addflag(ot->flags, F_GLYPH, gemtype[n].col, ot->obclass->glyph.ch, NA, NULL);
break;
}
}
}
}
return B_FALSE;
}
void setinscription(object_t *o, char *text) {
if (o->inscription) {
free(o->inscription);
}
o->inscription = strdup(text);
}
void setobcreatedby(object_t *o, lifeform_t *lf) {
char lfname[BUFLEN];
if (!lf) {
return;
}
real_getlfnamea(lf, lfname, B_FALSE, B_TRUE);
addflag(o->flags, F_CREATEDBY, lf->id, NA, NA, lfname);
}
// returns TRUE if it did shatter
int shatter(object_t *o, int hitlf, char *damstring, lifeform_t *fromlf) {
int shatterdam;
cell_t *where = NULL;
lifeform_t *target = NULL;
char buf[BUFLEN];
char obname[BUFLEN];
char targetname[BUFLEN];
int seen = B_FALSE;
if (hasflag(o->flags, F_NOSHATTER)) {
return B_FALSE;
}
getobname(o,obname,o->amt);
where = getoblocation(o);
if (hitlf) {
target = where->lf;
} else {
target = NULL;
}
if (target) {
getlfname(target, targetname);
}
// announce
if (haslos(player, where)) {
char prefix[BUFLEN];
if (o->pile->owner) {
char lfname[BUFLEN];
getlfname(o->pile->owner, lfname);
snprintf(prefix, BUFLEN, "%s%s ", lfname, getpossessive(lfname));
// ie. "the kobold's"
} else {
strcpy(prefix, "");
}
msg("%s%s shatter%s!",prefix, strlen(prefix) ? noprefix(obname) : obname, OBS1(o));
seen = B_TRUE;
} else {
noise(where, NULL, NC_OTHER, SV_SHOUT, "shattering glass.", NULL);
}
if (target) {
shatterdam = getshatterdam(o);
if (shatterdam && !isdead(target)) {
// extra glass damage
if (seen) {
msg("%s %s showered in %s shards!", targetname, is(target),
o->material->name);
}
losehp(target, shatterdam, DT_SLASH, fromlf, damstring);
}
}
// place shards
if (o->material->id == MT_GLASS) {
int numshards;
numshards = getnumshards(o);
// place glass shards
snprintf(buf, BUFLEN, "%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
snprintf(buf, BUFLEN, "%d chunks of ice",numshards);
addob(where->obpile, buf);
}
// potion effects (only if you hit)?
if (o->type->obclass->id == OC_POTION) {
int howlong;
// effects on lifeforms
if (hitlf && target) {
int observed;
// some potions have special effects...
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,
is(target));
}
losehp(target, rnd(1,5)*o->amt, DT_ACID, fromlf, "a splash of acid");
} else {
// announce
if (seen) {
msg("Acid splashes all over the floor!");
}
}
break;
case OT_POT_BLOOD:
if (seen) {
makeknown(o->type->id);
}
if (target) {
if (seen) {
msg("%s %s splashed with blood.", targetname,
is(target));
}
} else {
// announce
if (seen) {
msg("Blood splashes onto the floor.");
}
}
break;
case OT_POT_ELEMENTIMMUNE:
case OT_POT_ETHEREALNESS:
case OT_POT_GASEOUSFORM:
case OT_POT_GROWTH:
case OT_POT_LEVITATION:
case OT_POT_POISON:
case OT_POT_POLYMORPH:
case OT_POT_INVIS:
case OT_POT_SANCTUARY: // apply regular potion effects, and make them known
case OT_POT_SLEEP:
if (target) {
if (seen) {
makeknown(o->type->id);
}
potioneffects(target, o->type->id, o, 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, o->blessed, &observed);
if (observed) {
makeknown(o->type->id);
}
}
break;
case OT_POT_OIL:
if (seen) {
makeknown(o->type->id);
msg("%s %s covered with oil!", targetname, is(target));
}
// target is temporarily vulnerable to fire.
howlong = rnd(5,10);
addtempflag(target->flags, F_DTVULN, DT_FIRE, NA, NA, "2d6", howlong);
break;
case OT_POT_RUM:
// target is temporarily flammable
howlong = rnd(5,10);
addtempflag(target->flags, F_DTVULN, DT_FIRE, NA, NA, "2d6", howlong);
addtempflag(target->flags, F_FLAMMABLELF, OT_FIRESMALL, NA, NA, "alcohol", howlong);
break;
case OT_POT_WATER:
if (seen) makeknown(o->type->id);
if (target) {
if (seen) {
msg("%s %s splashed with water.", targetname,
is(target));
}
if (isundead(target) && isblessed(o)) {
if (isplayer(target)) {
msg("The water burns like acid!");
} else if (cansee(player, target)) {
msg("%s writhes in agony!", targetname);
}
losehp(target, o->amt*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, fromlf, "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);
}
}
break;
default:
break;
}
} else { // ie. you didn't hit anyone
switch (o->type->id) {
case OT_POT_OIL:
if (seen) {
makeknown(o->type->id);
msg("The floor is covered with oil!");
}
break;
case OT_POT_WATER:
if (seen) {
makeknown(o->type->id);
msg("The floor is covered with water!");
}
break;
default:
break;
}
}
}
// object is dead.
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
if (fromlf && isplayer(fromlf)) {
switch (o->type->id) {
case OT_POT_HEALING:
case OT_POT_HEALINGMIN:
case OT_POT_HEALINGMAJ:
case OT_POT_AMBROSIA:
angergodmaybe(R_GODMERCY, 25, GA_ATTACKOBJECT);
break;
default:
break;
}
}
return B_TRUE;
}
// randomizes hidden names
void shufflehiddennames(void) {
int i,n;
int total;
hiddenname_t *a, *temp;
int shuffleamt = 500;
total = 0;
for (a = firsthiddenname ; a ; a = a->next) {
total++;
}
if (total <= 1) {
return;
}
for (i = 0; i < shuffleamt; i++) {
int which;
// select random element (but never the first)
which = (rand() % (total-1))+1;
// go there
a = firsthiddenname;
for (n = 0; n < which; n++) {
if (a->next != NULL) a = a->next;
}
temp = a;
// remove from list
temp->prev->next = temp->next;
if (temp->next) {
temp->next->prev = temp->prev;
} else {
lasthiddenname = temp->prev;
}
// re-add this element to the start
temp->next = firsthiddenname;
temp->prev = NULL;
firsthiddenname->prev = temp;
firsthiddenname = temp;
}
}
int sizetonutrition(enum LFSIZE size) {
switch (size) {
case SZ_MINI:
return 1;
case SZ_TINY:
return 10;
case SZ_SMALL:
return 25;
case SZ_MEDIUM:
return 75;
case SZ_HUMAN:
return 120;
case SZ_LARGE:
return 150;
case SZ_HUGE:
return 200;
case SZ_ENORMOUS:
return 250;
default: break;
}
return 0;
}
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, NULL, B_NOSTACK, B_FALSE, o->type->id);
// 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, int howmuch, int damtype) {
return real_takedamage(o, howmuch, damtype, B_TRUE);
}
// returns amount of damage taken
int real_takedamage(object_t *o, int howmuch, int damtype, int wantannounce) {
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);
makewet(o, howmuch);
}
// catches on fire?
if (damtype == DT_FIRE) {
if ( ((o->type->id == OT_CANDLE) || (o->type->id == OT_TORCH) || (o->type->id == OT_CANDELABRUM)) &&
!isactivated(o)) {
cell_t *c;
c = getoblocation(o);
if (haslos(player, c)) {
char buf[BUFLEN];
getobname(o, buf, o->amt);
msg("%s %s lit.", buf, OB1(o,"is","are"));
}
turnon(NULL, o);
// reduce damage a tiny bit
howmuch--;
} else if (isflammable(o) && !hasflag(o->flags, F_ONFIRE)) {
if (!isimmuneto(o->flags, DT_FIRE, B_FALSE)) {
ignite(o);
if (isdeadob(o)) {
return howmuch;
}
}
}
// actually take fire damage
// fire damage falls through to owner
if (o->pile->owner) {
flag_t *f;
lifeform_t *owner;
int lfdam = 0;
owner = o->pile->owner;
f = hasflag(o->flags, F_EQUIPPED);
if (f) {
// no damage from equipped _weapons_
if ((f->val[0] != BP_WEAPON) || (f->val[0] != BP_SECWEAPON)) {
} else {
// equipped clothes/armour? full damage.
lfdam = howmuch;
}
} else {
// objects in your pack? just a little bit of damage
lfdam = pctof(25,howmuch);
}
if (lfdam) {
// announce
getobname(o, obname, o->amt);
if (isplayer(owner)) {
msg("Your %s burn%s you!", noprefix(obname),OBS1(o));
} else if (cansee(player, owner)) {
char lfname[BUFLEN];
getlfname(owner, lfname);
msg("%s is burnt by %s!", lfname, obname);
}
// now use the REAL name
real_getobname(o, obname, o->amt, B_TRUE, B_FALSE, B_FALSE, B_FALSE, B_TRUE);
losehp_real(owner, howmuch , damtype, NULL, obname, B_TRUE, o, B_FALSE);
if (isdead(owner)) {
return howmuch;
}
}
}
} else if (damtype == DT_COLD) {
// cold will shatter glass
if (o->material->id == MT_GLASS) {
char buf[BUFLEN];
char buf2[BUFLEN];
real_getobname(o, buf2, 1, B_FALSE, B_FALSE, B_FALSE, B_FALSE, B_FALSE);
snprintf(buf, BUFLEN, "a shattering %s", noprefix(buf2));
shatter(o, B_TRUE, buf, B_FALSE);
return howmuch;
}
}
// damage type creates other objects?
f = hasflagval(o->flags, F_DTCREATEOB, damtype, NA, NA, NULL);
if (f) {
cell_t *loc;
int radius;
int dirtype;
objecttype_t *ot;
loc = getoblocation(o);
ot = findotn(f->text);
if (ot && !hasob(loc->obpile, ot->id)) {
if (f->val[1] == NA) {
radius = 0;
dirtype = NA;
} else {
radius = f->val[1];
dirtype = f->val[2];
}
addobburst(loc, radius, dirtype, f->text, NULL, LOF_WALLSTOP);
}
}
// damage type converts this into something else?
f = hasflagval(o->flags, F_DTCONVERT, damtype, NA, NA, NULL);
if (f && !hasflag(o->flags, F_NODTCONVERT)) {
object_t *newob;
newob = addob(o->pile, f->text);
newob->amt = o->amt;
// object dies, but don't announce it
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
return howmuch;
}
adjustdamob(o, &howmuch, damtype);
assert(howmuch < 1000);
// effects which have to happen before damage is applied...
// 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
if ((f->val[0] == NA) || (f->val[0] == damtype)) {
explodeob(o, f, f->val[1]);
return howmuch;
}
}
}
} else {
if (hasflag(o->flags, F_EXPLODEONDEATH)) {
// object dies!
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
} else {
if ((f->val[0] == NA) || (f->val[0] == damtype)) {
// explode
explodeob(o, f, f->val[1]);
return howmuch;
}
}
}
}
// 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 howmuch;
}
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_FALSE, B_TRUE, B_FALSE, B_FALSE);
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) {
assert(hpflag->val[0] <= hpflag->val[1]);
}
if (!hpflag || (hpflag->val[0] <= 0)) {
// special cases....
if (damtype == DT_FIRE) {
if ((o->material->id == MT_FLESH) && onein(3)) { // fire sometimes roasts flesh
object_t *meat;
meat = addob(o->pile, "chunk of roast meat");
// purposely don't use getweight!
meat->weight = o->weight;
copyflag(meat->flags, o->flags, F_CORPSEOF);
}
// fire turns 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);
snprintf(buf, BUFLEN, "%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;
snprintf(buf, BUFLEN, "%d chunks of ice", nshards);
addob(o->pile, buf);
}
}
// object dies!
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
// if we didn't want this announced, don't say that it died either
if (!wantannounce) {
addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
}
} else if (hpflag) {
// 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 (wantannounce) {
if (isplayer(o->pile->owner)) {
msg("^wYour %s %s!",noprefix(obname), getobhurtname(o, damtype));
} else if (cansee(player, o->pile->owner)) {
// avoid "the goblin corses's armour is damaged"
if (!lfhasflag(o->pile->owner, F_FEIGNINGDEATH)) {
// don't announce decay damage for object you aren't holding
if (damtype != DT_DECAY) {
char monname[BUFLEN];
getlfname(o->pile->owner, monname);
msg("%s's %s %s!",monname, noprefix(obname), getobhurtname(o, damtype));
}
}
}
}
} else if (haslos(player, o->pile->where)) {
if (wantannounce) {
// don't announce decay damage for object you aren't holding
if (damtype != DT_DECAY) {
msg("%s %s!", obname, getobhurtname(o, damtype));
}
}
// if you see a secret object get damaged, it's not secret anymore.
killflagsofid(o->flags, F_SECRET);
}
}
}
return damtaken;
}
int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, object_t *firearm) {
return real_fireat(thrower, o, amt, where, speed, firearm, B_TRUE);
}
// throw speed/2 is the damage multiplier
int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, object_t *firearm, int announcethrow) {
char throwername[BUFLEN];
char throwernamea[BUFLEN];
char realthrowername[BUFLEN];
char realthrowernamea[BUFLEN];
char obname[BUFLEN];
char targetname[BUFLEN];
lifeform_t *target;
cell_t *srcloc;
int seen;
int shattered = B_FALSE;
char throwverbpast[BUFLEN];
char throwverbpres[BUFLEN];
int acc,myroll;
int youhit = B_FALSE;
int missiledam = 0; // how much damage the missile itself will take
object_t *newob = NULL;
cell_t *newloc;
int db = B_TRUE;
int willcatch = B_FALSE;
int announcedmiss = B_FALSE;
int outofammo = B_FALSE;
obpile_t *op = NULL;
reason = E_OK;
// giving away money
if (isplayer(thrower) && (o->type->id == OT_GOLD)) {
flag_t *f;
f = lfhasflag(thrower, F_GAVEMONEY);
if (f) {
f->val[0] += o->amt;
} else {
addflag(thrower->flags, F_GAVEMONEY, o->amt, NA, NA, NULL);
}
}
// you can't throw with as much force while swimming.
if (isswimming(thrower) && !firearm) {
speed /= 2;
limit(&speed, 1, NA);
}
if (firearm) {
strcpy(throwverbpres, "fire");
strcpy(throwverbpast, "fired");
} else {
strcpy(throwverbpres, "throw");
strcpy(throwverbpast, "thrown");
}
if (isblind(player)) {
if (thrower && isplayer(thrower)) {
getobname(o, obname, amt);
} else {
strcpy(obname, "something");
}
} else {
getobname(o, obname, amt);
}
if (thrower) {
getlfname(thrower, throwername);
real_getlfname(thrower, realthrowername, B_FALSE, B_FALSE);
if (isplayer(thrower)) {
strcpy(throwernamea, throwername);
} else {
if (isvowel(*(noprefix(throwername)))) {
strcpy(throwernamea, "an ");
} else {
strcpy(throwernamea, "a ");
}
strcat(throwernamea, noprefix(throwername));
if (isvowel(*(noprefix(realthrowername)))) {
strcpy(realthrowernamea, "an ");
} else {
strcpy(realthrowernamea, "a ");
}
strcat(realthrowernamea, noprefix(realthrowername));
}
} else {
strcat(throwernamea, "something");
strcat(realthrowernamea, "something");
}
if (firearm) {
srcloc = getoblocation(firearm);
} else {
srcloc = getoblocation(o);
}
// can't throw weilded cursed objects
if ((o->blessed == B_CURSED) && (!firearm)) {
// is object in thrower's pack? we need to check this
// because of things like telekenises which let us throw
// things we don't have.
if (thrower && (o->pile->owner == thrower)) {
// object equipped?
if (hasflag(o->flags, F_EQUIPPED)) {
// throw will fail!
if (isplayer(thrower)) {
msg("You can't release your %s - it %s cursed!", noprefix(obname),
isblessknown(o) ? "is" : "must be");
o->blessknown = B_TRUE;
} else if (cansee(player, thrower)) {
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) || haslos(player, srcloc)) { // can see the src/dst location
seen = B_TRUE;
} else {
seen = B_FALSE;
}
target = where->lf;
if (target && isdead(target)) {
target = NULL;
}
if (thrower && target && !isprone(target) && !lfhasflag(target, F_CASTINGSPELL)) {
if (areallies(thrower, target) && !firearm) {
willcatch = B_TRUE;
}
}
if (target) {
getlfname(target, targetname);
}
// touch effects
if (thrower) {
if (firearm) {
if (touch(thrower, firearm)) {
return B_TRUE;
}
} else {
if (o->pile == thrower->pack) {
if (touch(thrower, o)) {
return B_TRUE;
}
}
}
}
// announce it ("xx throws xx" "at yy")
if (announcethrow) {
if (thrower && isplayer(thrower)) {
// player is throwing something
if (!firearm) {
if (target && !hasflag(o->flags, F_POWDER)) {
msg("You %s %s %s %s.", throwverbpres, obname, willcatch ? "to" : "at", targetname);
} else {
msg("You %s %s.",throwverbpres, obname);
}
}
} else if (thrower && haslos(player, srcloc) && (srcloc == thrower->cell)) {
char throwstring[BUFLEN];
// a monster is throwing something
snprintf(throwstring, BUFLEN, "%s %ss %s", throwername, throwverbpres,
obname);
if (target && haslos(player, where) && !hasflag(o->flags, F_POWDER)) {
strcat(throwstring, willcatch ? " to " : " at ");
strcat(throwstring, targetname);
}
strcat(throwstring, ".");
msg("%s", throwstring);
} else if (seen) {
char throwstring[BUFLEN];
// an object is moving on its own
if (o->pile->owner && cansee(player, o->pile->owner)) {
char ownername[BUFLEN];
getlfname(o->pile->owner, ownername);
snprintf(throwstring, BUFLEN, "%s%s %s %s through the air", ownername, getpossessive(ownername),
noprefix(obname), (amt == 1) ? "flies" : "fly");
} else {
snprintf(throwstring, BUFLEN, "%s %s through the air", obname, (amt == 1) ? "flies" : "fly");
}
if (target && haslos(player, where)) {
strcat(throwstring, " toward ");
strcat(throwstring, targetname);
}
strcat(throwstring, ".");
msg("%s", throwstring);
}
}
//taketime(thrower, SPEED_THROW);
// some obejcts will die when thrown.
if (hasflag(o->flags, F_POWDER)) {
// special cases first
if (o->type->id == OT_ASHCONCEAL) {
int radius;
// make smoke
if (haslos(player, srcloc)) {
msg("%s dispers%s into a thick cloud of smoke!", obname,
(amt == 1) ? "es" : "e",
(amt == 1) ? "es" : "e" );
if (!isknown(o)) makeknown(o->type->id);
}
radius = o->amt * 2;
if (radius > 10) radius = 10;
addobsinradius(thrower->cell, radius, DT_COMPASS, "cloud of smoke", B_FALSE);
} else if (o->type->id == OT_ASHEXPLODE) {
char diebuf[BUFLEN];
// explosion!
snprintf(diebuf, BUFLEN, "%dd6",o->amt);
if (haslos(player, srcloc)) {
msg("%s dispers%s and explod%s!", obname,
(amt == 1) ? "es" : "e",
(amt == 1) ? "es" : "e" );
if (!isknown(o)) makeknown(o->type->id);
}
explodecells(thrower->cell, roll(diebuf), B_FALSE, o, 1, DT_COMPASS, B_FALSE);
} else if (o->type->id == OT_ASHSLEEP) {
int radius;
char buf[BUFLEN];
// make smoke
sprintf(buf, "%s dispers%s into a wispy mist!", obname, (amt == 1) ? "es" : "e" );
if (haslos(player, srcloc)) {
if (!isknown(o)) makeknown(o->type->id);
}
radius = o->amt * 2;
if (radius > 10) radius = 10;
spellcloud(srcloc, radius, UNI_SHADELIGHT, C_MAGENTA, OT_S_SLEEP, radius, B_TRUE, buf, "A wispy mist appears!");
} else if (o->type->id == OT_SALT) {
if (target && (getcelldist(srcloc, where) <= 1)) {
// salt won't work if they're immersed in water
if (lfhasflag(target, F_VULNTOSALT) &&
(getcellwaterdepth(target->cell, target) < DP_WAIST)) {
// instakill
if (cansee(player, target)) {
msg("%s%s skin sizzles and bubbles!", targetname, getpossessive(targetname));
}
target->hp = 0;
setlastdam(target, "osmosis");
setkillverb(target, "Dehydrated");
} else {
if (hasbp(target, BP_EYES) &&
!isblind(target) &&
!getequippedob(target->pack, BP_EYES)) {
if (cansee(player, target)) {
msg("%s stings %s%s eyes!", obname, targetname, getpossessive(targetname));
}
// blind for 5-7 turns
addtempflag(target->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(5,10));
}
if (isbleeding(target)) {
if (cansee(player, target)) {
msg("%s irritates %s%s wounds!", obname, targetname, getpossessive(targetname));
}
// pain for 3-5 turns
addtempflag(target->flags, F_PAIN, DT_DIRECT, NA, NA, "1d4", rnd(3,5));
}
}
} else {
if (haslos(player, srcloc)) {
msg("%s dispers%s into the air.", obname, (amt == 1) ? "es" : "e");
if (!isknown(o)) makeknown(o->type->id);
}
}
} else {
if (haslos(player, srcloc)) {
msg("%s dispers%s into the air.", obname, (amt == 1) ? "es" : "e");
if (!isknown(o)) makeknown(o->type->id);
}
}
removeob(o, amt);
return B_FALSE;
}
if (thrower) {
// gravboost?
if (lfhasflag(thrower, F_GRAVBOOSTED) && (srcloc == thrower->cell)) {
if (isplayer(thrower) || haslos(player, srcloc)) {
msg("%s drops and sticks to the ground!", obname);
}
moveob(o, thrower->cell->obpile, amt);
return B_FALSE;
}
}
// adjust destination location in case something is in the way.
haslof(srcloc, where, LOF_NEED, &newloc);
if (newloc) {
where = newloc;
target = where->lf;
if (target && isdead(target)) {
target = NULL;
}
if (target) {
getlfname(target, targetname);
}
}
// do throw animation
if (seen) {
glyph_t *gl;
gl = getglyph(o);
anim(srcloc, where, gl->ch, gl->colour);
}
// reflection?
if ( target && lfhasflag(target, F_REFLECTION)) {
if (seen) {
glyph_t *gl;
gl = getglyph(o);
anim(where, srcloc, gl->ch, gl->colour);
msg("%s is reflected away from %s!", obname, targetname);
}
// adjust target
where = srcloc;
target = where->lf;
if (target && isdead(target)) {
target = NULL;
}
if (target) {
getlfname(target, targetname);
}
thrower = NULL;
}
// find out your chances of hitting
if (target) {
if (thrower) {
if (willcatch) {
acc = 100;
} else {
acc = getmissileaccuracy(thrower, where, o, firearm, lfhasflag(thrower, F_TKTHROW)) ;
}
} else {
// purely based on saving throw...
acc = 100;
}
// adjust for swimming
if (isswimming(thrower)) {
switch (getskill(thrower, SK_SWIMMING)) {
default:
case PR_INEPT: acc -= 40; break;
case PR_NOVICE: acc -= 30; break;
case PR_BEGINNER: acc -= 20; break;
case PR_ADEPT: acc -= 10; break;
case PR_SKILLED:
case PR_EXPERT:
case PR_MASTER:
break;
}
}
if (db && thrower) {
char fatext[BUFLEN];
if (firearm) {
char faname[BUFLEN];
getobname(firearm, faname, 1);
sprintf(fatext," (from %s)",faname);
} else {
strcpy(fatext,"");
}
dblog("%s is %s %s%s - acc = %d, speed = %d\n", realthrowername,
firearm ? "firing" : "throwing", obname, fatext, acc, speed);
}
// roll for hit
youhit = B_FALSE;
myroll = rnd(1,100);
// blessed projectile vs undead? 20% bonus.
if (isblessed(o) && isundead(target)) {
myroll -= 20;
}
// metal projectile versus magnetic shield?
if (target && lfhasflag(target, F_MAGSHIELD) && ismetal(o->material->id)) {
// announce
if (seen) {
msg("%s is repelled from %s!", obname, targetname);
announcedmiss = B_TRUE;
}
youhit = B_FALSE;
} else if (myroll <= acc) {
youhit = B_TRUE;
}
if (db && thrower) {
dblog("roll = %d, youhit = %s", myroll, youhit ? "YES" : "no");
}
if (youhit && target) {
flag_t *f;
char attackname[BUFLEN];
int difficulty;
// cyclone shield?
f = lfhasflag(target, F_WINDSHIELD);
if (f) {
if (speed <= f->val[0]) {
if (seen) {
msg("%s is repelled by air currents around %s!", obname, targetname);
announcedmiss = B_TRUE;
}
youhit = B_FALSE;
}
}
// an actual physical shield?
sprintf(attackname, "%s", obname);
difficulty = 14 + (speed*2);
if (check_for_block(thrower, target, getthrowdam(o) + speed, DT_PROJECTILE, difficulty, attackname)) {
announcedmiss = B_TRUE;
youhit = B_FALSE;
missiledam += ((speed*2)+1);
}
}
// saving throws
if (youhit && !willcatch && !isprone(target)) {
// undead can't dodge blessed missiles
if (isblessed(o) && isundead(target)) {
} else {
// can the victim see where the object came from?
if (haslos(target, srcloc)) {
int catchmod,dodgemod;
enum LFSIZE sz;
sz = getobsize(o);
// smaller things are harder to catch, but easier to dodge
if (sz >= SZ_HUMAN) {
catchmod = 4;
dodgemod = -3;
} else if (sz == SZ_MEDIUM) {
catchmod = 4;
dodgemod = -1;
} else if (sz == SZ_SMALL) {
catchmod = 0;
dodgemod = 0;
} else if (sz == SZ_TINY) {
catchmod = -3;
dodgemod = 1;
} else if (sz <= SZ_MINI) {
catchmod = -6;
dodgemod = 2;
}
// first check to see if you can catch it. this should be very hard!
if (!lfhasflag(target, F_NOPACK) && hasbp(target, BP_HANDS) &&
lfhasflag(target, F_HUMANOID) &&
canpickup(target, o, o->amt) &&
!willburden(target, o, o->amt) &&
!isimmobile(target) &&
skillcheck(target, SC_DEX, 27 + (speed*5), catchmod)) {
willcatch = B_TRUE;
} else if (hasfreeaction(target) && skillcheck(target, SC_DODGE, 27+(speed*2), dodgemod)) {
// then check if we dodge it...
if (db) dblog("target passed dodge check.");
youhit = B_FALSE;
if (seen) {
if (isplayer(target)) {
msg("You dodge %s.", obname);
} else if (cansee(player, target)) {
msg("%s dodges %s.", targetname, obname);
}
announcedmiss = B_TRUE;
}
practice(target, SK_EVASION, 1);
}
}
}
}
// doesn't matter wheter you hit or not...
if (isundead(target) && isblessed(o)) {
if (seen) {
msg("%s recoils in fear!", targetname);
}
o->blessknown = B_TRUE;
// ... but undead won't catch blessed things
willcatch = B_FALSE;
}
if (youhit && lfhasflag(target, F_NONCORPOREAL)) {
if (isvulnto(target->flags, DT_HOLY, B_FALSE) && isblessed(o)) {
} else {
youhit = B_FALSE;
willcatch = B_FALSE;
if (seen) {
msg("%s passes straight through %s.", obname, targetname);
announcedmiss = B_TRUE;
}
}
}
// if someone is there, they take damage and the object might die
// this should be "if target && youhit"
if (youhit) {
if (willcatch) {
if (seen) {
msg("^%c%s catch%s %s.", isplayer(target) ? 'g' : 'n',
targetname, isplayer(target) ? "" : "es", obname);
}
moveob(o, target->pack, amt);
return B_FALSE;
} else {
int dam = 0;
char damstring[BUFLEN];
int reduceamt = 0;
int willtangle = B_FALSE;
int throwdam;
flag_t *f;
op = addobpile(NOOWNER, NOLOC, NULL);
// split off new object into a fake obpile
// so we don't modify the original stack with
// things like poison rubbing off.
o = real_moveob(o, op, amt, B_FALSE);
o->birthtime = -1;
throwdam = getthrowdam(o);
//dam = (int)((float)throwdam * multiplier);
dam = throwdam + speed;
// firearms at pointblank range? +50% damage
if (firearm) {
if ((getcelldist(srcloc, where) <= 1)) {
dam = pctof(150, dam);
}
// master marksman ?
if (thrower && (getskill(thrower, SK_RANGED) >= PR_MASTER)) {
dam = pctof(150, dam);
}
}
// special case
if (o->type->id == OT_RUBBERBULLET) {
dam = 1;
}
if (db) dblog("fireat(): dam = throwdam(%d) + speed(%d)",throwdam, speed);
if (db) dblog("dealing %d damage", dam);
// deal extra cutting damage afterwards?
if (willshatter(o->material->id)) {
shattered = B_TRUE;
}
// will the missile trip them over?
f = hasflag(o->flags, F_TANGLEMISSILE);
if (f) {
if (isairborne(target) || !skillcheck(target, SC_SLIP, f->val[0], 0)) {
willtangle = B_TRUE;
}
}
// announce
if (haslos(player, where)) {
char buf2[BUFLEN];
char verb[BUFLEN];
if (willtangle) {
sprintf(verb, "wrap%s around", (amt == 1) ? "s" : "");
} else {
sprintf(verb, "hit%s", (amt == 1) ? "s" : "");
}
snprintf(buf2, BUFLEN, "%s %s %s.",obname, verb, targetname);
if (lfhasflag(player, F_EXTRAINFO)) {
char damstring[BUFLEN];
snprintf(damstring, BUFLEN, " [%d dmg]",dam);
strcat(buf2, damstring);
}
msg("^b%s", buf2);
}
snprintf(damstring, BUFLEN, "%s (%s by %s)",obname,throwverbpast, realthrowernamea);
reduceamt = getarmourdamreduction(target, o, dam, DT_PROJECTILE);
applyarmourdamreduction(target, o, reduceamt, &dam, DT_PROJECTILE);
if (dam > 0) {
lifeform_t *whogetsxp = NULL;
// TODO: at the moment you won't get experience if you telekenetically
// throw an object at something. is this okay?
if (thrower && (thrower->cell == srcloc)) {
whogetsxp = thrower;
}
losehp_real(target, dam, DT_PROJECTILE, whogetsxp, damstring, B_TRUE, o, B_TRUE);
}
if (reduceamt && (speed >= 3)) {
applyarmourdamage(target, o, reduceamt, DT_PROJECTILE, NULL);
}
wepeffects(o->flags, target->cell, hasflag(o->flags, F_DAM), dam);
missiledam += ((speed*2)+1);
if (willtangle) {
missiledam = 0; // don't damage the misisle
fall(target, NULL, B_TRUE);
taketime(target, getactspeed(target));
if (f->val[1] != NA) {
addflag(o->flags, F_RESTRICTMOVEMENT, f->val[1], f->val[2], B_FALSE, NULL);
}
}
f = hasflag(o->flags, F_TANGLEMISSILE);
if (f) {
missiledam = 0;
if (isairborne(target) || !skillcheck(target, SC_SLIP, f->val[0], 0)) {
fall(target, NULL, B_TRUE);
taketime(target, getactspeed(target)*2);
if (f->val[1] != NA) {
addflag(o->flags, F_RESTRICTMOVEMENT, f->val[1], f->val[2], B_FALSE, NULL);
}
}
}
if (thrower) {
if (firearm) {
practice(thrower, SK_THROWING, 1);
} else {
practice(thrower, SK_RANGED, 1);
}
}
}
} else { // ie. if !youhit
if (!announcedmiss) {
if (isplayer(thrower)) {
msg("Your %s misses %s.", noprefix(obname), targetname);
} else if (haslos(player, where)) {
msg("%s misses %s.", obname, targetname);
}
announcedmiss = B_TRUE;
}
}
} // end if target
// heavy ob?
if (youhit && target) {
if ((getobunitweight(o)*amt) >= getlfweight(target, B_NOOBS)) {
int dir;
dir = getdirtowards(srcloc, target->cell, target, B_FALSE, DT_COMPASS);
knockback(target, dir, 1, thrower, 0, B_TRUE);
}
}
// if we were throwing at a lifeform inside a wall, we need to make sure we don't
// now place the object inside the wall.
if (where->type->solid) {
newloc = NULL;
haslof_real(srcloc, where, LOF_NEED, &newloc, NULL, B_FALSE);
if (newloc) {
where = newloc;
}
}
// move the object to the cell then take dam or kill it. don't stack.
newob = real_moveob(o, where->obpile, amt, B_FALSE);
// fake its birth time so that it can be damaged
newob->birthtime = -1;
// now we can get rid of the fake obpile, if we used it
if (op) killobpile(op);
// gun out of ammo?
if (firearm && !countobs(firearm->contents, B_FALSE)) {
outofammo = B_TRUE;
}
if (willshatter(newob->material->id)) {
char dambuf[BUFLEN];
snprintf(dambuf, BUFLEN, "%s (%s by %s)",obname,throwverbpast, realthrowernamea);
shatter(newob, youhit, dambuf, thrower);
} else {
// object only gets damaged if it hit someone/something
if (missiledam) {
// don't announce damage to the thrown object
real_takedamage(newob, missiledam, DT_BASH, B_FALSE);
}
}
if (thrower && hasactivespell(thrower, OT_S_WHATGOESUP)) {
if (newob && !isdeadob(newob)) {
// on the ground?
if ((newob->pile->where == where) && haslof(newob->pile->where, thrower->cell, LOF_NEED, NULL)) {
if (isplayer(thrower)) {
msg("%s returns to you!", obname);
} else if (cansee(player, thrower)) {
msg("%s returns to %s!", obname, throwername);
} else if (haslos(player, newob->pile->where)) {
msg("%s returns to someone!", obname);
}
moveob(newob, thrower->pack, newob->amt);
}
}
}
/*
if (firearm && outofammo && isplayer(thrower)) {
char buf[BUFLEN];
getobname(firearm, buf, 1);
msg("Your %s is now out of ammo.", noprefix(buf));
}
*/
return B_FALSE;
}
void timeeffectsob(object_t *o) {
flag_t *f;
cell_t *location;
lifeform_t *owner;
object_t *sg;
char obname[BUFLEN],ownername[BUFLEN];
int i;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
if (hasflag(o->flags, F_DEAD)) return;
getobname(o, obname, o->amt);
location = getoblocation(o);
if (o->pile->owner) {
owner = o->pile->owner;
getlfname(owner, ownername);
} else {
owner = NULL;
}
checkflagpile(o->flags);
// special case for trail flags
f = hasflag(o->flags, F_TRAIL);
if (f && (f->lifetime > 0)) {
f->lifetime--;
if (f->lifetime <= 0) {
//killflag(f);
// object dies.
//killob(o);
removeob(o, o->amt);
return;
}
}
// expire flags
timeeffectsflags(o->flags);
checkflagpile(o->flags);
// blessed weapons glow when held near undead
if (isblessed(o) && isweapon(o)) {
cell_t *ourcell;
flag_t *glowflag = NULL;
int nearundead = B_FALSE;
// are we glowing?
getflags(o->flags, retflag, &nretflags, F_PRODUCESLIGHT, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
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) && isundead(c->lf)) {
nearundead = B_TRUE;
break;
}
}
}
if (nearundead) {
if (!glowflag) {
// start glowing
glowflag = addtempflag(o->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL, FROMBLESSING);
if (haslos(player, ourcell)) {
if (!o->blessknown) o->blessknown = B_TRUE;
}
calclight(ourcell->map);
setlosdirty(player);
//precalclos(player);
}
} else { // not near undead
if (glowflag) {
killflag(glowflag);
glowflag = NULL;
}
}
}
} else { // not equipped
if (glowflag) { // if not equipped and glowing...
// stop glowing
killflag(glowflag);
glowflag = NULL;
}
}
}
// sacred ground?
sg = hasobwithflagval(location->obpile, F_REPELBLESSED, o->blessed, NA, NA, NULL);
if (sg) {
char sgname[BUFLEN];
cell_t *newc;
int canseeloc = B_FALSE;
if (haslos(player, location) && canseeob(player, sg)) {
getobname(sg, sgname, sg->amt);
canseeloc = B_TRUE;
msg("The %s pulses %s!", noprefix(sgname),
(o->blessed == B_CURSED) ? "white" : "black");
}
// object gets thrown away
newc = getrandomadjcell(location, WE_NOTWALL, B_ALLOWEXPAND);
if (newc) {
//flag_t *inv;
// make sure the object doesn't take damage
//inv = addflag(o->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL);
fireat(NULL, o, o->amt, newc, 1, NULL);
if (canseeloc) {
// player now knows that this is blessed
o->blessknown = B_TRUE;
}
// update location
//if (inv) killflag(inv);
// further effects on this object will come from its new location
location = newc;
} else {
// object can't go anywhere - it disintegrates.
if (owner && cansee(player, owner)) {
msg("%s%s vanish%s in a surge of %s power!", ownername, getpossessive(ownername),
obname, OB1(o,"es",""),
(o->blessed = B_CURSED) ? "holy" : "evil");
} else if (haslos(player, location)) {
msg("%s vanish%s in a surge of %s power!", obname, OB1(o,"es",""),
(o->blessed = B_CURSED) ? "holy" : "evil");
}
removeob(o, o->amt);
return;
}
}
if (hasflag(o->flags, F_TANGLEMISSILE)) {
if (!location || !location->lf) {
// thrown entangling weapons lose their "stickiness"
// once the target escapes
killflagsofid(o->flags, F_RESTRICTMOVEMENT);
}
}
if (location && !owner) {
lifeform_t *who;
who = location->lf;
if (who && !lfhasflag(who, F_HASBEENMOVED) && !lfhasflag(who, F_MOVED)) {
f = hasflag(o->flags, F_MOVELFS);
if (f) {
// move them
trymove(who, f->val[0], B_FALSE, B_TRUE);
addflag(who->flags, F_HASBEENMOVED, B_TRUE, NA, NA, NULL);
}
}
}
if (location) {
// object makes noise?
getflags(o->flags, retflag, &nretflags, F_MAKESNOISE, F_NONE);
if (nretflags) {
f = retflag[rnd(0,nretflags-1)];
if (pctchance(f->val[0])) {
// these are generally just to notify the player that something
// is nearby, so don't make noises the the player is already there.
if (location != player->cell) {
noise(location, NULL, NC_OTHER, f->val[1], f->text, NULL);
}
}
}
// does object's material change cell type?
if (o->material->id == MT_FIRE) {
if (hasflag(location->type->material->flags, F_FLAMMABLE)) {
int changed = B_FALSE;
// burn by changing celltype...
switch (location->type->id) {
case CT_GRASS:
setcelltype(location, CT_DIRT);
break;
default:
break;
}
if (changed && haslos(player, location)) {
msg("The %s burns!", location->type->name);
needredraw = B_TRUE;
}
//setcelltype(location, CT_CORRIDOR);
addob(location->obpile, "pile of ash");
}
}
// checks based on object
if (o->type->id == OT_VINE) {
lifeform_t *creator;
int willvanish = B_FALSE;
// creator no longer has los?
f = hasflag(o->flags, F_CREATEDBY);
if (f) {
creator = findlf(NULL, f->val[0]);
if (!creator || !haslos(creator, location)) {
willvanish = B_TRUE;
}
// noone there?
if (!location->lf) {
willvanish = B_TRUE;
}
if (willvanish) {
// vanish.
if (haslos(player, location)) {
msg("%s vanish%s.", obname, OB1(o,"es",""));
}
removeob(o, o->amt);
return;
}
}
}
}
// check each flag for this object...
getflags(o->flags, retflag, &nretflags, F_ACTIVATED, F_EDIBLE, F_MATCONVERT, F_OBHPDRAIN, F_ONFIRE,
F_RECHARGE, F_REVIVETIMER, F_WALKDAM, F_WET, F_NONE);
for (i = 0; i < nretflags; i++) {
object_t *oo,*nextoo;
f = retflag[i];
// 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 == -1) {
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 (cansee(player, owner)) {
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;
}
}
}
if (hasflag(o->flags, F_LIGHTSOURCE)) {
flag_t *f2,*mf;
// countdown...
f2 = hasflag(o->flags, F_CHARGES);
if (f2) {
f2->val[0]--;
if (f2->val[0] <= 0) {
// turnoff
turnoff(NULL, o);
mf = hasflag(o->flags, F_CHARGEOUTMSG);
if (mf) obaction(o, mf->text);
needredraw = B_TRUE;
return;
} else if (f2->val[0] <= 50) {
if (onein(6)) {
mf = hasflag(o->flags, F_CHARGELOWMSG);
if (mf) obaction(o, mf->text);
}
}
}
}
}
if (f->id == F_OBHPDRAIN) {
enum DAMTYPE damtype;
int doit = B_TRUE;
//takedamage(o, f->val[0] * firstlftime, DT_DIRECT);
if (f->val[1] == NA) {
damtype = DT_DIRECT;
} else {
damtype = f->val[1];
}
// special case
if (o->type->id == OT_CORPSE) {
if (hasobofmaterial(o->pile, MT_ICE)) {
doit = B_FALSE;
}
}
if (doit) {
takedamage(o, f->val[0], damtype);
if (hasflag(o->flags, F_DEAD)) return;
}
}
// corpses rot away...
/*
if ((f->id == F_EDIBLE) && (o->type->obclass->id == OC_CORPSE)) {
f->val[1] -= 4;
if (f->val[1] < 0) {
f->val[1] = 0;
addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL);
}
}
*/
if (f->id == F_RECHARGE) {
flag_t *f2;
f2 = hasflag(o->flags, F_CHARGES);
if (f2 && (f2->val[0] < f2->val[1])) {
f2->val[0] += f->val[0];
if (f2->val[0] == f2->val[1]) {
if (isplayer(o->pile->owner)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("Your %s is now fully charged.", noprefix(obname));
}
}
}
}
// regenerates into a lf?
if (f->id == F_REVIVETIMER) {
cell_t *obloc = NULL;
obloc = getoblocation(o);
f->val[0]++;
limit(&f->val[0], 0, f->val[1]);
if (f->val[0] >= f->val[1]) {
lifeform_t *lf;
cell_t *lfloc = NULL;
if (obloc) {
if (obloc->lf) {
lfloc = getrandomadjcell(obloc, WE_WALKABLE, B_NOEXPAND);
} else {
lfloc = obloc;
}
}
if (lfloc) {
// revive!
lf = addmonster(lfloc, f->val[2], NULL, B_FALSE, 1, B_FALSE, NULL);
// corpse vanishes
removeob(o, o->amt);
// announce
if (haslos(player, lfloc) || haslos(player, obloc)) {
msg("%s comes to life!", obname);
interrupt(player);
}
return;
}
} else if ((f->val[1] - f->val[0]) <= 10) {
if (haslos(player, obloc)) {
// pass a perception chekc to see it moving...
if (skillcheck(player, SC_SEARCH, 20, 0)) {
msg("%s twitches.", obname);
}
}
}
}
// 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, roll(f->text), f->val[0]);
//if (hasflag(o->flags, F_DEAD)) return;
}
// is object on fire?
if (f->id == F_ONFIRE) {
int wasputout = B_FALSE;
// 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);
wasputout = B_TRUE;
break;
}
}
if (!wasputout) {
// 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) && !hasflag(o->flags, F_NOMATCONVERT)) {
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!
snprintf(buf, BUFLEN, "%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;
}
}
}
}
if (f->id == F_WET) {
if (isequipped(o) || !o->pile->owner) {
cell_t *ourcell;
object_t *splash;
ourcell = getoblocation(o);
// drip
if (!hasobwithflag(ourcell->obpile, F_DEEPWATER)) {
splash = addob(ourcell->obpile, "splash of water");
}
}
}
} // end for each object flag
checkflagpile(o->flags);
}
// both trapob and oid are passed, because trapob might be NULL if
// coming from a door/chest trap.
void trapeffects(object_t *trapob, enum OBTYPE oid, cell_t *c) {
lifeform_t *lf = NULL;
char lfname[BUFLEN];
int avoided = B_FALSE;
enum CHECKTYPE ct;
objecttype_t *temp = NULL;
lf = c->lf;
if (lf) getlfname(lf, lfname);
temp = findot(oid);
// saving throw?
if (temp && lf) {
flag_t *f;
f = hasflag(temp->flags, F_TRAP);
if (f && (f->val[2] != NA)) {
if (isplayer(lf) && hasflag(temp->flags, F_SECRET)) {
avoided = B_FALSE;
} else {
int mod = 0;
switch (oid) {
case OT_TRAPTRIP: ct = SC_FALL; break;
default:
ct = SC_DODGE;
break;
}
// easier to avoid if you're sneaking
if (lfhasflag(lf, F_SNEAK)) mod += 5;
mod += getskill(lf, SK_TRAPS);
avoided = skillcheck(lf, ct, f->val[2], mod);
}
}
}
// for actual trap objects (ie. not not doors etc), remember when we last
// triggered this trap. this is to avoid infinite loops with arrow traps!
if (trapob) {
flag_t *f;
f = hasflag(trapob->flags, F_TRAP);
if (f) f->val[1] = curtime;
}
if (oid == OT_TRAPWIND) {
// can't be dodged
dospelleffects(NULL, OT_S_GUSTOFWIND, 10, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPNEEDLEP) {
if (lf) {
if (isplayer(lf)) {
msg("A needle shoots out %s", avoided ? "at you, but misses." : "and hits you!");
} else if (cansee(player, lf)) {
if (avoided) {
msg("A needle shoots out at %s, but misses.",lfname);
} else {
msg("A needle shoots out and hits %s!",lfname);
}
}
if (!avoided) {
poison(lf, rnd(10,20), P_VENOM, 1, "a needle trap");
}
} else {
if (haslos(player, c)) {
// TODO: "...shoots out of xxx". need to pass obfrom to this.
msg("A poisoned needle fires, then falls to the ground.");
addob(c->obpile, "poisoned needle");
}
}
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPROCK) {
if (lf) {
if (haslos(player, c)) {
msg("A heavy rock drops onto %s%s", lfname,
avoided ? ", but misses." : "!");
}
if (!avoided) {
losehp(lf, roll("1d4"), DT_BASH, NULL, "a falling rock trap");
}
} else {
if (haslos(player, c)) {
msg("A heavy rock drops from the ceiling.");
}
}
addob(c->obpile, "stone");
} else if (oid == OT_TRAPTELEPORT) {
dospelleffects(NULL, OT_S_DISPERSAL, 10, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE);
} else if (oid == OT_TRAPPIT) {
cell_t *escapeto = NULL;
addob(c->obpile, "hole in the ground");
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
if (lf) {
if (avoided) {
escapeto = getrandomadjcell(lf->cell, WE_WALKABLE, B_NOEXPAND);
if (!escapeto) avoided = B_FALSE;
}
if (avoided) {
if (isplayer(lf)) {
msg("You leap away from the pit!");
} else if (cansee(player, lf)) {
msg("%s leaps away from a pit!", lfname);
}
movelf(lf, escapeto);
}
}
} else if (oid == OT_TRAPALARM) {
noise(c, NULL, NC_OTHER, 50, "a blaring siren!", "A blaring siren goes off!");
} else if ((oid == OT_TRAPARROW) || (oid == OT_TRAPARROWP)) {
int dir,bestdir = D_NONE;
cell_t *src = NULL;
int maxdist=-1;
// get furthest wall
for (dir = DC_N; dir <= DC_NW; dir++) {
cell_t *cc,*prevc;
int thisdist = 0;
prevc = NULL;
cc = c;
cc = getcellindir(cc, dir);
while (!cc->type->solid) {
thisdist++;
prevc = cc;
cc = getcellindir(cc, dir);
}
if (thisdist > maxdist) {
maxdist = thisdist;
bestdir = dir;
src = prevc;
}
}
if (src && (bestdir != D_NONE)) {
object_t *o;
if (oid == OT_TRAPARROWP) {
o = addob(src->obpile, "poisoned arrow");
} else {
o = addob(src->obpile, "arrow");
}
if (o) {
long oid;
// remember its id
oid = o->id;
// dodge check will happen in fireat(). ignore results of the
// one above.
fireat(NULL, o, 1, c, 5, NULL);
o = hasobid(c->obpile, oid);
if (o) removeob(o, 1);
} else {
msg("ERROR: arrow trap failed.");
dblog("ERROR: arrow trap failed.");
}
} else {
msg("ERROR: arrow trap failed (no dir).");
dblog("ERROR: arrow trap failed (no dir).");
}
} else if (oid == OT_TRAPEBLAST) {
if (isplayer(lf) && !haslos(player, c)) {
msg("A blast of energy comes out of nowhere!");
// if we DO have los, then spelleffects() will take care of the
// announcement.
}
// can't be dodged
dospelleffects(NULL, OT_S_ENERGYBLAST, 1, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPFIRE) {
// can't be dodged
dospelleffects(NULL, OT_S_FLAMEPILLAR, 3, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPGAS) {
// can't be dodged
dospelleffects(NULL, OT_S_CLOUDKILL, 3, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPMINE) {
// can't be dodged
explodecells(c, roll("2d6"), B_FALSE, trapob, 1, DT_ORTH, B_TRUE);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPSUMMON) {
cell_t *cc;
// can't be dodged
cc = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND);
if (cc) {
summonmonster(NULL, cc, R_SPECIFIED, "random", PERMENANT, B_FALSE);
}
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPTRIP) {
if (lf) {
if (avoided) {
if (isplayer(lf)) {
msg("You retain your balance.");
} else if (cansee(player, lf)) {
msg("%s retains its balance.",lfname);
}
} else {
fall(lf, NULL, B_TRUE);
}
}
}
}
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 (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s deactivates %s.",lfname, obname);
}
}
if (hasflag(o->flags, F_GRENADE)) {
object_t *stackob;
// make it stackable again
addflag(o->flags, F_STACKABLE, B_TRUE, NA, NA, NULL);
// other stacks to join?
stackob = canstackob(o->pile, o);
if (stackob) {
char stackname[BUFLEN];
// remove it, and inc amt on the stack
removeob(o, 1);
stackob->amt++;
getobname(stackob, stackname, stackob->amt);
msgnocap("%c - %s.",stackob->letter, stackname);
} else {
// just turn off
killflag(f);
}
} else {
killflag(f);
}
if (o->pile->owner) {
loseobflags(o->pile->owner, o, F_ACTIVATECONFER);
}
}
void turnon(lifeform_t *lf, object_t *o) {
char obname[BUFLEN];
flag_t *f;
int held = B_FALSE;
if (lf && (o->pile->owner == lf)) {
held = B_TRUE;
}
f = hasflag(o->flags, F_ACTIVATED);
if (f) {
// already on
if (lf && isplayer(lf)) {
if (held) {
msg("Your %s is already activated!\n", noprefix(obname));
} else {
msg("%s is already activated!\n", obname);
}
}
return;
}
// check charges
f = hasflag(o->flags, F_CHARGES);
if (f && (f->val[0] <= 0)) {
// out of power
if (lf && isplayer(lf)) {
nothinghappens();
}
return;
}
getobname(o, obname, 1);
if (lf) {
if (isplayer(lf)) {
msg("You activate%s %s.", held ? " your" : "",
held ? noprefix(obname) : obname);
} else if (cansee(player, lf)) {
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);
if (lf && isplayer(lf)) {
// announce.
if (held) {
msgnocap("%c - %s [activated].",newob->letter, newobname);
more();
// ask where to throw it
dothrow(NULL, newob);
} else {
msgnocap("You activate %s.",newob->letter, newobname);
}
}
o = newob;
} 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);
}
if (held) {
giveobflags(lf, o, F_ACTIVATECONFER);
}
}
int uncurseob(object_t *o, int *seen) {
lifeform_t *lf = NULL;
// default
if (seen) *seen = B_FALSE;
if (o->blessed != B_CURSED) {
return B_TRUE;
}
lf = o->pile->owner;
// announce
if (lf) {
if (cansee(player, lf)) {
char lfname[BUFLEN];
char obname[BUFLEN];
getlfname(lf, lfname);
getobname(o, obname,o->amt);
msg("A black aura breaks away from %s%s %s.",lfname,getpossessive(lfname),noprefix(obname));
if (seen) *seen = B_TRUE;
}
} else { // not held
cell_t *loc = NULL;
loc = getoblocation(o);
if (haslos(player, loc)) {
char obname[BUFLEN];
getobname(o, obname,o->amt);
msg("A black aura breaks away from %s.",obname);
if (seen) *seen = B_TRUE;
}
}
// uncurse it
o->blessed = B_UNCURSED;
o->blessknown = B_TRUE;
return B_FALSE;
}
// returns charges remaining, -1 if object doesn't have the flag or is
// out of charges.
int usecharge(object_t *o) {
flag_t *f = NULL;
object_t *b;
// use power from a battery first
b = hasob(o->pile, OT_BATTERY);
if (b && isknown(b)) {
f = hasflag(b->flags, F_CHARGES);
// does the battery have charges left?
if (f && (f->val[0] <= 0)) {
f = NULL;
}
}
// if no battery, use charges from the object itself.
if (!f) {
f = hasflag(o->flags, F_CHARGES);
}
// use up the charge.
if (f && (f->val[0] > 0)) {
f->val[0]--;
} else {
// couldn't use a charge - ran out.
return -1;
}
// always return the amount of charges the _object_ has left.
// not the battery.
f = hasflag(o->flags, F_CHARGES);
if (f) {
return f->val[0];
} else {
return 0; // since technically we didn't fail if we got here.
}
}
// returns charges remaining, -1 if object doesn't have the flag
int usecharges(object_t *o, int amt) {
flag_t *f;
int i;
// use charges one at a time.
for (i = 0; i < amt; i++) {
if (usecharge(o) == -1) {
// ran out of power!
return -1;
}
}
// return the amount of charges which the object has left.
f = hasflag(o->flags, F_CHARGES);
if (f) {
return f->val[0];
}
return -1;
}
int validatehiddennames(void) {
objectclass_t *oc;
hiddenname_t *hn;
int goterror = B_FALSE;
// check hidden names for duplicates
for (oc = objectclass ; oc ; oc = oc->next) {
for (hn = firsthiddenname ; hn ; hn = hn->next) {
if (hn->obclass == oc->id) {
int count;
count = counthiddennames(oc->id, hn->text);
if (count > 1) {
printf("Error - duplicate hiddenname found for class %s, text '%s'. (%d found)\n", oc->name, hn->text, count);
goterror = B_TRUE;
}
}
}
}
if (goterror) {
raise(SIGINT);
}
return goterror;
}
int validateobs(void) {
objecttype_t *ot;
flag_t *f;
int foundspells = B_FALSE;
int goterror = B_FALSE;
for (ot = objecttype ; ot ; ot = ot->next) {
// fix up glyphs
f = hasflag(ot->flags, F_GLYPH);
if (f) {
if (f->val[0] == NA) {
f->val[0] = getmaterialcolour(ot->material->id);
}
}
// remember buildings
if (ot->obclass->id == OC_BUILDING) {
buildingusage[nbuildingusage].oid = ot->id;
buildingusage[nbuildingusage].count = 0;
nbuildingusage++;
}
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 ((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;
}
f = hasflag(ot->flags, F_SPELLLEVEL);
if (f && (f->val[0] > MAXSPELLLEV)) {
printf("ERROR - spell %s level (%d) > MAXSPELLLEVEL (%d).\n", ot->name, f->val[0], MAXSPELLLEV);
goterror = B_TRUE;
}
} else if (ot->obclass->id == OC_SCROLL) {
if (foundspells) {
printf("ERROR in object '%s' - all Scrolls must be defined before Spells.\n", ot->name);
goterror = B_TRUE;
}
} else if (ot->obclass->id == OC_WEAPON) {
if (!hasflag(ot->flags, F_USESSKILL) && !hasflag(ot->flags, F_FIREARM)) {
if (ot->id != OT_ENERGYBLADE) {
printf("ERROR in object '%s' - weapon has no associated skill.\n", ot->name);
goterror = B_TRUE;
}
}
f = hasflag(ot->flags, F_ATTREQ);
if (f && (f->val[2] == NA)) {
printf("ERROR in object '%s' - f_attreq missing scale factor.\n", 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.\n", ot->name);
goterror = B_TRUE;
}
if (hasflag(ot->flags, F_FIREARM)) {
if (!hasflag(ot->flags, F_RANGE)) {
printf("ERROR in object '%s' - firearms need to have F_RANGE.\n", ot->name);
goterror = B_TRUE;
}
if (!hasflag(ot->flags, F_AMMOCAPACITY)) {
printf("ERROR in object '%s' - firearms need to have F_AMMOCAPACITY.\n", ot->name);
goterror = B_TRUE;
}
if (!hasflag(ot->flags, F_RELOADTURNS)) {
printf("ERROR in object '%s' - firearms need to have F_RELOADTURNS.\n", ot->name);
goterror = B_TRUE;
}
}
f = hasflag(ot->flags, F_TECHLEVEL);
if (f && (f->val[0] != PR_INEPT)) {
if (!hasflag(ot->flags, F_HASHIDDENNAME)) {
printf("ERROR in object '%s' - has a techlevel but doesn't have a hidden name.\n", ot->name);
goterror = B_TRUE;
}
}
f = hasflag(ot->flags, F_MANUALOF);
if (f && !findskill(f->val[0])) {
printf("ERROR in object '%s' - teachs a skill which doesn't exist.\n", ot->name);
goterror = B_TRUE;
}
f = hasflag(ot->flags, F_DAM);
if (f) {
// automatically add in the missing altdam flags.
if (hasflag(ot->flags, F_ALTDAM) && !hasflagval(ot->flags, F_ALTDAM, f->val[0], NA, NA, NULL)) {
addflag(ot->flags, F_ALTDAM, f->val[0], f->val[1], f->val[2], f->text );
}
if (f->val[0] < 0) {
printf("ERROR in object '%s' - F_DAM does not specify damage type.\n", ot->name);
goterror = B_TRUE;
}
if (f->val[1] < 0) {
printf("ERROR in object '%s' - F_DAM does not have DR.\n", ot->name);
goterror = B_TRUE;
}
if (strlen(f->text)) {
printf("ERROR in object '%s' - F_DAM still has old format dice string.\n", ot->name);
goterror = B_TRUE;
}
}
f = hasflag(ot->flags, F_EXPLODEONDAM);
if (f && !strlen(f->text)) {
printf("ERROR in object '%s' - F_EXPLODEONDAM does not have damage string.\n", ot->name);
goterror = B_TRUE;
}
f = hasflag(ot->flags, F_EXPLODEONDEATH);
if (f && !strlen(f->text)) {
printf("ERROR in object '%s' - F_EXPLODEONDEATH does not have damage string.\n", ot->name);
goterror = B_TRUE;
}
if (hasflag(ot->flags, F_HITCONFER) && !hasflag(ot->flags, F_HITCONFERVALS)) {
printf("ERROR in object '%s' - has F_HITCONFER, but no HITCONFERVALS defined.\n", ot->name);
goterror = B_TRUE;
}
if (hasflag(ot->flags, F_OBDIETEXT) && hasflag(ot->flags, F_NOOBDIETEXT)) {
printf("ERROR in object '%s' - has both dietext & noobdietext.\n", ot->name);
goterror = B_TRUE;
}
f = hasflag(ot->flags, F_THEREISHERE);
if (f && !strlen(f->text)) {
printf("ERROR in object '%s' - has f_thereishere but no ->text.\n", ot->name);
goterror = B_TRUE;
}
f = hasflag(ot->flags, F_IMPASSABLE);
if (f && ((f->val[0] == NA) || (f->val[1] == NA)) ) {
printf("ERROR in object '%s' - f_impassable missing either min or max.\n", ot->name);
goterror = B_TRUE;
}
f = hasflag(ot->flags, F_DOOR);
if (f) {
flag_t *f2;
f2 = hasflag(ot->flags, F_IMPASSABLE);
if (f2) {
if ((f->val[0] != f2->val[0]) || (f->val[1] != f2->val[1])) {
printf("ERROR in object '%s' - f_door vals don't match f_impassable vals.\n", ot->name);
goterror = B_TRUE;
}
} else {
printf("ERROR in object '%s' - has f_door but not f_impassable.\n", ot->name);
goterror = B_TRUE;
}
}
}
return goterror;
}
int wepdullable(object_t *o) {
enum DAMTYPE dt;
if (!o) return B_FALSE;
dt = getdamtype(o);
switch (dt) {
case DT_PIERCE:
case DT_SLASH:
return B_TRUE;
default:
break;
}
return B_FALSE;
}
int willshatter(enum MATERIAL mat) {
switch (mat) {
case MT_GLASS:
case MT_ICE:
return B_TRUE;
default: break;
}
return B_FALSE;
}
int getcritchance(lifeform_t *lf, object_t *o, lifeform_t *victim) {
flag_t *f;
int chance = 0;
if (!o) return 0;
if (hasflag(o->flags, F_MERCIFUL)) return 0;
f = hasflag(o->flags, F_CRITCHANCE);
if (f) {
chance += f->val[0];
} else {
return 0;
}
if (lf) {
enum SKILLLEVEL weplev = PR_INEPT;
flag_t *retflag[MAXCANDIDATES];
int nretflags,i;
weplev = getweaponskill(lf, o);
if (weplev != PR_INEPT) {
chance += (weplev*5); // ie. up to 30% bonus
}
// agi scaling on weapon
getflags(o->flags, retflag, &nretflags, F_ATTREQ, F_NONE);
for (i = 0; i < nretflags; i++) {
if (f->val[0] == A_AGI) {
int pctmod;
meetsattreq(lf, retflag[i], o, &pctmod);
chance += pctmod;
}
}
}
// double crit chance if victim is vulnerable to this weapon
if (victim && lfhasflagval(victim, F_MATVULN, o->material->id, NA, NA, NULL)) {
chance *= 2;
}
return chance;
}
int chargesknown(object_t *o) {
flag_t *f;
int cutoff = -2;
f = hasflag(o->flags, F_CHARGES);
if (f) {
if (f->known) return B_TRUE;
switch (getskill(player, SK_CHANNELING)) {
case PR_BEGINNER: cutoff = 1; break;
case PR_ADEPT: cutoff = 3; break;
case PR_SKILLED: cutoff = 6; break;
case PR_EXPERT: break; // f->known will be set after use.
case PR_MASTER: cutoff = 9999; break;
default: break;
}
}
if (f->val[0] <= cutoff) {
return B_TRUE;
}
return B_FALSE;
}
int getcritprotection(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_CRITPROTECTION);
if (f) return f->val[0];
return 0;
}
// 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;
}
// populate retob[] with all fire in the obpile
int getflamingobs(obpile_t *op, object_t **retob, int *nretobs) {
object_t *o;
int num = 0;
for (o = op->first ; o ; o = o->next) {
int addit = B_FALSE;
if (hasflag(o->flags, F_ONFIRE) || (o->material->id == MT_FIRE)) {
addit = B_TRUE;
}
if (addit && retob) {
retob[num++] = o;
}
}
if (nretobs) *nretobs = num;
return num;
}
// populate retob[] with ingredents for the given recipe, taken from the given object pile
int getingredients(obpile_t *op, recipe_t *rec, object_t **retob, int *retcount, int *retconsume, int *nretobs, int promptformulti) {
int i;
object_t *o;
if (nretobs) *nretobs = 0;
for (i = 0; i < rec->ningredients; i++) {
object_t *poss[MAXPILEOBS];
int nposs = 0;
for (o = op->first ; o ; o = o->next) {
int obmatches = B_FALSE;
if (o->type->id == rec->ingredient[i]) {
obmatches = B_TRUE;
} else if ((rec->ingredient[i] == OT_BREADSTALE) && (o->type->id == OT_BREADFRESH)) {
obmatches = B_TRUE;
} else if ((rec->ingredient[i] == OT_BREADFRESH) && (o->type->id == OT_BREADSTALE)) {
obmatches = B_TRUE;
}
if (obmatches) {
// chicken soup must have chicken
if ((rec->result == OT_POT_SOUPCHICKEN) && (rec->ingredient[i] == OT_ROASTMEAT)) {
if (!hasflagval(o->flags, F_CORPSEOF, R_CHICKEN, NA, NA, NULL)) {
obmatches = B_FALSE;
}
}
}
if (obmatches && (o->amt >= rec->count[i])) {
poss[nposs++] = o;
}
}
if ((nposs > 1) && promptformulti) {
int n,ch;
// ask which one to use
initprompt(&prompt, "Which item will you use in your cooking:");
// you MUST pick one since at this point you've already been given the recipe result!
for (n = 0; n < nposs; n++) {
char iname[BUFLEN];
getobname(poss[n], iname, 1);
addchoice(&prompt, poss[n]->letter, noprefix(iname), NULL, poss[n], NULL);
}
ch = getchoice(&prompt);
poss[0] = (object_t *)prompt.result;
nposs = 1;
}
if (nposs == 0) {
// missing an ingredient!
if (nretobs) *nretobs = 0;
return B_TRUE;
} else {
if (retob && nretobs) {
if (retob) retob[*nretobs] = poss[0];
if (retcount) retcount[*nretobs] = rec->count[i];
if (retconsume) retconsume[*nretobs] = rec->consume[i];
(*nretobs)++;
}
}
}
return B_FALSE;
}
objecttype_t *getlinkspell(object_t *o) {
flag_t *f = NULL;
objecttype_t *spelltype = NULL;
f = hasflag(o->flags, F_LINKSPELL);
if (f) {
// find the linked spell
spelltype = findot(f->val[0]);
}
return spelltype;
}
enum COLOUR getmaterialcolour(enum MATERIAL mat) {
enum COLOUR col;
switch (mat) {
case MT_WOOD:
case MT_LEATHER:
col = C_BROWN;
break;
case MT_FIRE:
case MT_BLOOD:
col = C_RED;
break;
case MT_GLASS:
case MT_MAGIC:
col = C_CYAN;
break;
case MT_ICE:
col = C_WHITE;
break;
case MT_GOLD:
col = C_YELLOW;
break;
case MT_WATER:
col = C_BLUE;
break;
case MT_SLIME:
case MT_ACID:
col = C_GREEN;
break;
default:
col = C_GREY;
}
return col;
}
// is the given material solid or liquid?
enum MATSTATE getmaterialstate(enum MATERIAL mat) {
switch (mat) {
case MT_FIRE:
case MT_MAGIC:
case MT_NOTHING:
return MS_OTHER;
case MT_ACID:
case MT_BLOOD:
case MT_OIL:
case MT_WATER:
return MS_LIQUID;
case MT_GAS:
return MS_GAS;
default:
return MS_SOLID;
}
return MS_OTHER; // should never happen
}
int getmissileaccuracy(lifeform_t *thrower, cell_t *where, object_t *missile, object_t *firearm, flag_t *tkthrow) {
int acc,howfar,cellpenalty;
enum SKILLLEVEL slev;
enum SKILL whichskill;
enum ATTRIB whichatt;
// base accuracy of 100% for hitting yourself
// then accuracy goes down for each cell based on skill + agility
if (tkthrow) {
whichatt = tkthrow->val[0];
whichskill = tkthrow->val[1];
} else {
whichatt = A_AGI;
if (firearm) {
whichskill = SK_RANGED;
} else {
whichskill = SK_THROWING;
}
}
// base accuracy to hit your own cell
// (firearm == null) means we are throwing.
acc = getobaccuracy(firearm, thrower);
// for each cell travelled, lower accuracy, based on skill.
slev = getskill(thrower, whichskill);
// masterwork firearms?
if (firearm && hasflag(firearm->flags, F_MASTERWORK)) {
if (slev < PR_ADEPT) slev++;
}
switch (slev) {
case PR_NOVICE: cellpenalty = 22; break;
case PR_BEGINNER: cellpenalty = 16; break;
case PR_ADEPT: cellpenalty = 12; break;
case PR_SKILLED: cellpenalty = 10; break;
case PR_EXPERT: cellpenalty = 8; break;
case PR_MASTER: cellpenalty = 6; break;
default:
case PR_INEPT: cellpenalty = 32; break;
}
howfar = getcelldist(thrower->cell, where);
acc -= (cellpenalty*howfar);
// modify based on attributes
if (whichatt != A_NONE) {
int mod;
mod = getstatmod(thrower, whichatt); // -50 to 50
mod /= 2; // -25 to +25
acc += mod;
}
// penalty for throwing non-missiles
if (missile && !firearm && !isthrowmissile(missile) && !tkthrow) {
if (!lfhasflagval(thrower, F_WILLTHROW, missile->type->id, NA, NA, NULL)) {
acc -= 20;
}
}
// modify for prone throwers
if (isprone(thrower)) {
acc -= 50;
}
// modify for prone defenders
if (where->lf && isprone(where->lf)) {
acc -= 30;
}
// 10% bonus for being higher than your target
if (where->lf) {
if (getflightsizemod(thrower) > getflightsizemod(where->lf)) {
acc += 15;
}
}
limit(&acc, 0, NA);
return acc;
}