nexus/objects.c

18175 lines
443 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 race_t *firstrace, *lastrace;
extern recipe_t *firstrecipe,*lastrecipe;
extern skill_t *firstskill, *lastskill;
extern region_t *firstregion;
extern plural_t *firstplural;
extern buildingusage_t buildingusage[];
extern int nbuildingusage;
extern int inaskcoords;
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;
extern condset_t ccwalkable;
extern condset_t ccwalkableroom;
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_AMULET,
OC_RING,
OC_TECH,
OC_TOOLS,
OC_BOOK,
OC_FURNITURE,
OC_GODSTONE,
OC_FLORA,
OC_DFEATURE,
OC_ROCK,
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_AQUA, },
{ "azure",C_LIGHTCYAN },
{ "black",C_VDARKGREY },
{ "blue",C_BLUE },
{ "brown",C_BROWN },
{ "cyan",C_CYAN },
{ "green",C_GREEN },
{ "indigo",C_MAGENTA },
{ "magenta",C_LIGHTMAGENTA },
{ "night-black",C_VDARKGREY },
{ "orange",C_ORANGE },
{ "pink",C_PINK },
{ "rainbow-coloured",C_ORANGE },
{ "red",C_RED },
{ "violet",C_MAGENTA },
{ "yellow",C_YELLOW },
{ "",C_GREY },
};
hiddennamewithcol_t gemtype[] = {
{ "agate", C_MAGENTA, },
{ "amethyst", C_INDIGO, },
{ "brass",C_LIGHTYELLOW },
{ "bronze",C_DARKYELLOW },
{ "copper",C_DARKYELLOW },
{ "diamond",C_WHITE },
{ "emerald",C_DARKGREEN },
{ "flourite",C_LIGHTRED },
{ "garnet",C_ORANGE },
{ "gold",C_YELLOW },
{ "iridium",C_WHITE },
{ "jade",C_GREEN },
{ "jasper",C_RED },
{ "lapis lazuli",C_LIGHTBLUE },
{ "malachite",C_LIGHTCYAN },
{ "onyx",C_VDARKGREY },
{ "opal",C_CYAN },
{ "pearl",C_FLESH },
{ "quartz",C_GREY },
{ "ruby",C_DARKRED },
{ "sapphire",C_DARKBLUE },
{ "topaz", C_YELLOW },
{ "zinc",C_GREY },
{ "zircon",C_CYAN },
{ "",C_GREY },
};
char *ringadjective[] = {
"cracked",
"dusty",
"ornate",
"sparkling",
"twinkling",
"",
};
char *amuletadjective[] = {
"chipped",
"dusty",
"grotesque",
"lurid",
"mysterious",
"polished",
"twisted",
"",
};
char *amuletnoun[] = {
"charm",
"locket",
"medallion",
"necklace",
"pendant",
"talisman",
"",
};
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;
a->description = NULL;
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 OBTYPE 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;
a->triedon = NULL;
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, int 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.
// 'dolinks' determines whether or not to do things like liking holes, assigning destinations to maps, etc.
// basically, set this to false if we are generating temporary/test objects.
// NOTE: This function MUST return number of obs created via global "nretobs"
object_t *addobject(obpile_t *where, char *name, int canstack, int dolinks, 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];
int wanthppct = 100;
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
lifeform_t *matchlfskills = NULL;
int wantblessknown = B_FALSE;
enum DEPTH wantdepth = DP_MAX;
int wantlit = B_FALSE;
int wantrarity = RR_NONE;
int wantgoodness = G_NA;
int wantfoodtaint = B_FALSE;
race_t *linkrace = NULL;
enum OBTYPE wantfountaintype = OT_NONE;
enum LFSIZE wantarmsize = SZ_ANY;
enum MATERIAL wantdiffmat = MT_NOTHING;
int wantoriginal = B_FALSE;
int minar = NA,maxar = NA; // minimum AR for armour
int mindr = NA,maxdr = NA; // minimum DR for weapons
map_t *targetmap = NULL; // for portals
cell_t *targetcell = NULL; // for portals
int donesomething;
object_t *addedob[MAXPILEOBS];
race_t *wantbloodrace = NULL;
int nadded = 0;
flagpile_t *wantflags;
// for doors
enum FLAG doorflag[5];
int ndoorflags = 0;
int trapchance = 0;
int lockchance = 0;
int jamchance = 0;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
lifeform_t *firstlf;
if (player) firstlf = player->cell->map->lf;
else firstlf = NULL;
// just in case we don't add any
addedob[0] = NULL;
nadded = 0;
nretobs = 0;
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;
}
wantflags = addflagpile(NULL, 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;
char *namestart;
int bonussign = 1;
if (strstarts(name, "the ")) {
namestart = name + strlen("the ");
} else if (strstarts(name, "an ")) {
namestart = name + strlen("an ");
} else if (strstarts(name, "a ")) {
namestart = name + strlen("a ");
} else {
namestart = name;
}
localname = strdup(namestart);
if (db) {
dblog("DB: called addobject() for %s, canstack = %d",localname, canstack);
}
// check if we want a name. if so, remember it and strip off the suffix.
p2 = strstr(localname, " named ");
if (p2) {
char wantnamed[BUFLEN];
char *wantp;
char *copyfrom;
copyfrom = p2 + strlen(" named ");
wantp = wantnamed;
while (*copyfrom && (*copyfrom != ' ')) {
*wantp = *copyfrom;
copyfrom++;
wantp++;
}
*wantp = '\0';
// now strip the suffix
*p2 = '\0';
addflag(wantflags, F_NAMED, NA, NA, NA, wantnamed);
}
// 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)) {
strrep(&localname, om->prefix, "", NULL);
if (db) dblog("DB: found obmod prefix '%s'",om->prefix);
wantom[nom] = om;
nom++;
} else {
// check altprefixes.
int i;
for (i = 0; i < om->naltprefix; i++) {
if (strstr(localname, om->altprefix[i])) {
strrep(&localname, om->altprefix[i], "", NULL);
if (db) dblog("DB: found obmod altprefix '%s'",om->altprefix[i]);
wantom[nom] = om;
nom++;
break;
}
}
}
}
// parse number 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) {
int charsremoved;
while (*bonusend == ' ') bonusend++; // go past spaces
// using memmove instead
//strcpy(bonusstart, bonusend);
memmove(bonusstart, bonusend, strlen(bonusend));
charsremoved = strlen(bonusstart) - strlen(bonusend);
// NUL out remainder of buffer
p[strlen(p) - charsremoved] = '\0';
}
}
// handle prefixes. strip them off as we go.
donesomething = B_TRUE;
while (donesomething) {
int n;
material_t *mat = NULL;
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;
// blood
} else if (strends(p, " blood")) {
char *spaceblood,*pp,rname[BUFLEN],*rp,replacefrom[BUFLEN];
race_t *r = NULL;
spaceblood = strends(p, " blood");
pp = strstr(p, " of ");
if (pp) {
pp += 4;
}
rp = rname;
for (;pp < spaceblood; pp++, rp++) {
*rp = *pp;
}
*rp = '\0';
if (strlen(rname)) {
r = findracebyname(rname, NULL);
if (r) {
wantbloodrace = r;
}
}
if (r) {
// now replace the end of the string.
// initially we'll have (eg) 'splash of xxx blood'
// change it to 'splash of blood'
sprintf(replacefrom, "%s blood", r->name);
pp = strstr(p, replacefrom);
assert(pp);
strcpy(pp, "blood");
}
// food flags
} else if (strstarts(p, "bruised ")) {
addflag(wantflags, F_BRUISED, B_TRUE, NA, NA, NULL);
p += strlen("bruised ");
donesomething = B_TRUE;
} else if (strstarts(p, "cooked ")) {
addflag(wantflags, F_PREPARED, B_TRUE, NA, NA, NULL);
p += strlen("cooked ");
donesomething = B_TRUE;
// flags
} else if (strstarts(p, "red-hot ")) {
addflag(wantflags, F_HOT, 3, NA, NA, "1d4");
p += strlen("red-hot ");
donesomething = B_TRUE;
} else if (strstarts(p, "vorpal ")) {
addflag(wantflags, F_ARMOURPIERCE, 10, NA, NA, NULL);
p += strlen("vorpal ");
donesomething = B_TRUE;
// generic linkflags
} else if (strstarts(p, "linkrace:")) {
char racename[BUFLEN];
p += strlen("linkrace:");
// read next
p = readuntil(racename, p, ' ');
linkrace = findracebyname(racename, NULL);
donesomething = B_TRUE;
// condition flags
} else if (strstarts(p, "battered ")) {
wanthppct = 75;
canstack = B_FALSE;
p += strlen("battered ");
donesomething = B_TRUE;
} else if (strstarts(p, "damaged ")) {
wanthppct = 50;
canstack = B_FALSE;
p += strlen("damaged ");
donesomething = B_TRUE;
} else if (strstarts(p, "very damaged ")) {
wanthppct = 25;
canstack = B_FALSE;
p += strlen("very damaged ");
donesomething = B_TRUE;
} else if (strstarts(p, "critically damaged ")) {
wanthppct = 10;
canstack = B_FALSE;
p += strlen("critically damaged ");
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;
jamchance = 100;
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 (((mat = strmatchesmaterial(p)) != NULL) &&
isvalidoverridemat(mat->id) ) {
wantdiffmat = mat->id;
p += strlen(mat->name);
p++; // go past the space
donesomething = B_TRUE;
// special flags
} else if (strstarts(p, "immutable ")) {
f = addflag(wantflags, F_IMMUTABLE, B_TRUE, NA, NA, NULL);
f->known = B_FALSE;
p += strlen("immutable ");
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;
// force weapon/armour to be ones the holder is skilled in?
} else if (strstarts(p, "appropriate ")) {
if (where->owner) {
matchlfskills = where->owner;
} else {
matchlfskills = player;
}
p += strlen("appropriate ");
// also make sure armour fits
if (matchlfskills) {
wantarmsize = getlfsize(matchlfskills);
}
donesomething = B_TRUE;
} else if (strstarts(p, "original ")) {
// don't give different material etc
p += strlen("original ");
wantoriginal = B_TRUE;
donesomething = B_TRUE;
// weapon "goodness"
} else if (strstarts(p, "average ")) {
wantgoodness = G_AVERAGE;
p += strlen("average ");
donesomething = B_TRUE;
getgoodnessdr(wantgoodness, &mindr, &maxdr);
} else if (strstarts(p, "good ")) {
wantgoodness = G_GOOD;
if (onein(4)) wantblessed = B_BLESSED;
p += strlen("good ");
donesomething = B_TRUE;
limit(&minar, 1, NA);
getgoodnessdr(wantgoodness, &mindr, &maxdr);
} else if (strstarts(p, "great ")) {
wantgoodness = G_GREAT;
if (onein(3)) wantblessed = B_BLESSED;
if (onein(10)) dorandombrand = B_TRUE;
p += strlen("great ");
limit(&minar, 2, NA);
donesomething = B_TRUE;
getgoodnessdr(wantgoodness, &mindr, &maxdr);
} else if (strstarts(p, "excellent ")) {
wantgoodness = G_EXCELLENT;
if (onein(2)) wantblessed = B_BLESSED;
if (onein(4)) dorandombrand = B_TRUE;
p += strlen("excellent ");
limit(&minar, 3, NA);
donesomething = B_TRUE;
getgoodnessdr(wantgoodness, &mindr, &maxdr);
// object names
// 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;
} else if (strstarts(p, "untrapped ")) {
trapchance = 1;
p += strlen("untrapped ");
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, NULL);
ot = findot(OT_ROASTMEAT);
} else if (strstr(p, "water fountain")) {
wantfountaintype = OT_POT_WATER;
ot = findot(OT_FOUNTAIN);
} else if (strstarts(p, "fountain of ")) {
char potname[BUFLEN];
char *p2;
objecttype_t *pot;
p2 = p + strlen("fountain of ");
sprintf(potname, "potion of %s", p2);
pot = findotn(potname);
wantfountaintype = pot->id;
ot = findot(OT_FOUNTAIN);
} else if (strstr(p, "corpse")) {
int len;
char racename[BUFLEN];
p2 = strstr(p, "corpse");
len = p2 - p;
assert (len >= 0);
if (len == 0) {
// eg. "corpse"
cell_t *c;
c = getobpilelocation(where);
corpserace = getrandomrace(c, NA, NULL);
} else {
// eg. "goblin corpse"
snprintf(racename, len, "%s",p);
assert(strlen(racename) > 0);
corpserace = findracebyname(racename, NULL);
}
ot = findot(OT_CORPSE);
} else if (strstr(p, "map to ")) {
branch_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 branch which matches this.
// if not found, it'll be randoml selected later.
rt = findbranchbyname(regionname);
if (rt) {
// find the regionthing ID of the RT_BRANCHLINK entrance
wantregionthing = findbranchlink(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, NULL);
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, NULL);
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, WE_WALKABLE);
targetcell = getcell_cond(targetmap, &ccwalkableroom);
if (!targetcell) targetcell = getrandomcell(targetmap);
while (!cellwalkable(NULL, targetcell, NULL)) {
targetcell = getrandomadjcell(targetcell, &ccwalkable, B_ALLOWEXPAND);
}
} else {
// ie. adding to dummy cell
targetmap = NULL;
targetcell = NULL;
}
} else {
killflagpile(wantflags);
if (localname) free(localname);
return NULL;
}
ot = findot(OT_PORTAL);
} else if (strstarts(p, "generator \"")) {
char *pp;
char tempbuf[BUFLEN];
char whattomake[BUFLEN];
int radius,pct;
pp = p + strlen("generator \"");
pp = readuntil(tempbuf, pp, ',');
if (!pp) {
if (localname) free(localname);
return NULL;
}
strcpy(whattomake, tempbuf);
pp = readuntil(tempbuf, pp, ',');
if (!pp) {
if (localname) free(localname);
return NULL;
}
radius = atoi(tempbuf);
pp = readuntil(tempbuf, pp, '"');
if (!pp) {
if (localname) free(localname);
return NULL;
}
pct = atoi(tempbuf);
addflag(wantflags, F_GENERATES, pct, radius, NA, whattomake);
ot = findot(OT_GENERATOR);
} 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';
addflag(wantflags, F_SIGNTEXT, NA, NA, NA, 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 weapons and armour, "matchlfskills" being set
// means that we should only select weapons/armour which
// matchlfskills is skilled in.
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) {
condset_t cs;
initcond(&cs);
// want a specific rarity? (ie. "rare weapon")
//rrtorarity(wantrarity, &minrarity, &maxrarity);
if (strstr(p, "firearm")) {
addcond(&cs, CC_HASFLAG, B_TRUE, F_FIREARM);
}
// want actual armour as opposed to clothing?
if (oc->id == OC_ARMOUR) {
if (strstr(p, "armour")) {
limit(&minar, 1, NA);
} else if (strstr(p, "clothing")) {
minar = NA;
maxar = 0;
addcond(&cs, CC_HASFLAG, B_FALSE, F_SHIELD);
}
if (minar != NA) {
addcond(&cs, CC_MINAR, B_TRUE, minar);
}
if (maxar != NA) {
addcond(&cs, CC_MAXAR, B_TRUE, maxar);
}
}
if (oc->id == OC_WEAPON) {
if (mindr != NA) {
addcond(&cs, CC_MINDR, B_TRUE, mindr);
}
if (maxdr != NA) {
addcond(&cs, CC_MAXDR, B_TRUE, maxdr);
}
}
//ot = getrandomobofclass(oc->id, minrarity, maxrarity, matchlfskills, &cs);
ot = getrandomobofclass(oc->id, NA, NA, wantrarity, matchlfskills, &cs);
if (ot) {
found = B_TRUE;
break;
}
}
}
if (found) break;
}
// cope with "random _otherstuff_"
if (!found) {
if (strstarts(p, "random gem")) {
//int minrarity,maxrarity;
condset_t cs;
// want a specific rarity?
//rrtorarity(wantrarity, &minrarity, &maxrarity);
initcondv(&cs, CC_HASFLAG, B_TRUE, F_GEM,
CC_NONE);
//ot = getrandomobofclass(OC_ROCK, minrarity, maxrarity, NULL, &cs);
ot = getrandomobofclass(OC_ROCK, NA, NA, wantrarity, NULL, &cs);
if (ot) {
found = B_TRUE;
}
}
if (strstarts(p, "random container")) {
char buf[BUFLEN];
cell_t *cc;
cc = getobpilelocation(where);
ot = getrandomobwithflag(cc, F_CONTAINER, buf);
if (ot) {
found = B_TRUE;
}
}
if (strstarts(p, "random impassable")) {
char buf[BUFLEN];
cell_t *cc;
cc = getobpilelocation(where);
// really want: impassable and not a dfeature
ot = getrandomobwithflag(cc, F_IMPASSABLE, buf);
while (ot->obclass->id == OC_DFEATURE) {
ot = getrandomobwithflag(cc, F_IMPASSABLE, buf);
}
if (ot) {
found = B_TRUE;
}
}
}
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;
killflagpile(wantflags);
if (localname) free(localname);
return NULL;
}
}
}
if (db) dblog("DB: FOUND: ot->name = '%s'", ot->name );
// trying to place object on a solid cell?
if ((gamemode > GM_LOADING) && where->where) {
if (where->where->type->solid) {
// doors are okay - clear the solid cell first.
if (hasflag(ot->flags, F_DOOR)) {
setcelltype(where->where, getcellempty(where->where));
} else {
assert("BUG: trying to put object on solid cell." == 0);
}
}
}
// 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);
}
}
if (gamemode != GM_LOADING) {
// 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 (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;
killflagpile(wantflags);
if (localname) free(localname);
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;
killflagpile(wantflags);
if (localname) free(localname);
return NULL;
}
}
// don't put innate attack objects anywhere other than on a lifeform
if (!where->owner && !istempobpile(where) && hasflag(ot->flags, F_UNARMEDWEP)) {
if (db) dblog("DB: Cannot give innate attack objects to non-lifeforms. object name '%s'", ot->name );
nretobs = 0;
killflagpile(wantflags);
if (localname) free(localname);
return NULL;
}
}
if (bonus > 0) {
if (wantblessed == B_CURSED) {
wantblessed = B_UNCURSED;
}
} else if (bonus < 0) {
if (wantblessed == B_BLESSED) {
wantblessed = B_UNCURSED;
}
}
// override blessed status from flags...
f = hasflag(ot->flags, F_STARTBLESSED);
if (f) {
if ((f->val[1] == NA) || pctchance(f->val[1])) {
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;
killflagpile(wantflags);
if (localname) free(localname);
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;
wantblessknown = B_TRUE;
// also override amount
howmany = 1;
}
// special checks for unique objects
if (hasflag(ot->flags, F_UNIQUE)) {
object_t *origob;
// does this unique ob already exist?
origob = obexists(ot->id);
if (origob) {
if (db) dblog("DB: Unique ob %s already exists!", ot->name );
nretobs = 0;
killflagpile(wantflags);
if (localname) free(localname);
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;
}
if (!wantoriginal) {
// 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) && (gamemode != GM_CHARGEN)) {
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)) brace
// 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 default props
o->id = nextoid++; // increment next ob id
o->type = ot;
o->pile = where;
o->birthtime = curtime;
o->dying = B_FALSE;
o->flags = addflagpile(NULL, o);
o->contents = addobpile(NOOWNER, NOLOC, o);
// defaults
o->inscription = NULL; // non-inherited
o->letter = '\0';
o->blessed = B_UNCURSED;
o->blessknown = B_FALSE;
o->amt = 1;
// inherit props and flags from objecttype
o->material = ot->material;
assert(o->material);
o->weight = ot->weight;
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 (search "foreach object 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 (linkrace && (linkrace->id != R_NONE)) {
addflag(o->flags, F_LINKRACE, linkrace->id, NA, NA, NULL);
}
if (wantbloodrace && (wantbloodrace->id != R_NONE)) {
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0,i;
// don't duplicate this as then it will count as 'mixed blood'
if (!hasflagval(o->flags, F_LINKRACE, wantbloodrace->id, NA, NA, NULL)) {
addflag(o->flags, F_LINKRACE, wantbloodrace->id, NA, NA, NULL);
}
// not using copyflag, in order to avoid duplicates
getflags(wantbloodrace->flags, retflag, &nretflags, F_FILLPOT, F_NONE);
for (i = 0; i < nretflags; i++) {
addflag(o->flags, retflag[i]->id, retflag[i]->val[0], retflag[i]->val[1],
retflag[i]->val[2], retflag[i]->text);
//copyflag(o->flags, wantbloodrace->flags, F_FILLPOT);
}
}
if (hasflag(o->flags, F_NOBLESS)) {
setblessed(o, B_UNCURSED);
} else {
setblessed(o, wantblessed);
}
o->blessknown = wantblessknown;
if (wantdiffmat != MT_NOTHING) {
changemat(o, wantdiffmat);
}
if (wanthppct != 100) {
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] = pctof(wanthppct, f->val[1]);
limit(&(f->val[0]), 1, f->val[1]);
}
}
// hiding monsters?
f = hasflag(o->flags, F_ISMONSTER);
if (f) {
char *cp,nbuf[BUFLEN];
int amt;
object_t *oo;
oo = addobfast(o->contents, f->val[1]);
assert(oo);
cp = readuntil(nbuf,f->text, ',');
cp = readuntil(nbuf,cp, '-'); // really eol
amt = roll(nbuf);
if (amt < 0) amt = 1;
oo->amt = amt;
}
// extra chance of bone items being cursed
if (o->type->material->id == MT_BONE) {
if (pctchance(15)) {
o->blessed = B_CURSED;
}
}
if (where->owner && isundead(where->owner)) {
// never blessed!
o->blessed = B_UNCURSED;
// probably cursed
if (pctchance(80)) {
o->blessed = B_CURSED;
}
}
if (where->owner && isplayer(where->owner)) {
killflagsofid(o->flags, F_UNTOUCHED);
}
// 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, and set opening hours.
if (o->type->id == OT_TEMPLE) {
lifeform_t *god;
god = getrandomgod();
if (god) {
addflag(o->flags, F_LINKGOD, god->race->id, NA, NA, NULL);
switch (god->race->id) {
case R_GODDEATH:
case R_GODTHIEVES:
// open at night only
addflag(lastot->flags, F_OPENHOURS, 9, 17, SP_CLOSEDTILNIGHT, NULL);
break;
default: // always open
break;
}
} else {
// this should only ever happen when creating the
// surface map - because the realm of gods (and hence the
// gods themselves) won't exist yet.
// not a big deal, since abandoned temples on the surface
// fit the game theme anyway.
/*
msg("DB: created abandoned temple"); more();
dblog("DB: created abandoned temple");
raise(SIGINT);
*/
// abandoned tamples make no sound
killflagsofid(o->flags, F_MAKESNOISE);
}
}
// 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) {
if (!hasflag(o->flags, doorflag[n])) {
// 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_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)) {
if (wantfountaintype != OT_NONE) {
f = hasflag(o->flags, F_LINKOB); assert(f);
f->val[0] = wantfountaintype;
f = hasflag(o->flags, F_FILLPOT); assert(f);
f->val[0] = wantfountaintype;
} else if (where->where && (where->where->habitat->id != H_VILLAGE)) {
if (onein(3)) {
objecttype_t *ot;
int pctroll;
pctroll = rollforob(where->where->map->depth);
//getrarityrange(where->where->map->depth, &min, &max, RARITYVARIANCEOB, B_FALSE);
ot = getrandomobofclass(OC_POTION, 0, pctroll, NA, NULL, NULL);
if (ot) {
f = hasflag(o->flags, F_LINKOB); assert(f);
f->val[0] = ot->id;
f = hasflag(o->flags, F_FILLPOT); assert(f);
f->val[0] = ot->id;
}
} else {
f = hasflag(o->flags, F_LINKOB); assert(f);
f->val[0] = OT_POT_WATER;
f = hasflag(o->flags, F_FILLPOT); assert(f);
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);
}
// firearms usually come loaded
if (o && isfirearm(o)) {
objecttype_t *ammotype;
flag_t *ammoflag;
int ammoamt;
ammoflag = hasflag(o->flags, F_AMMOCAPACITY);
ammoamt = rnd(0,ammoflag->val[0]); // note: can be 0
if (gamemode == GM_CHARGEN) {
ammotype = getrandomammofor(o, B_TRUE);
} else {
ammotype = getrandomammofor(o, B_FALSE);
}
if (ammotype && (ammoamt > 0)) {
char ammostring[BUFLEN];
sprintf(ammostring, "%d %s",ammoamt,ammotype->name);
addob(o->contents, ammostring);
}
}
// 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) && (gamemode != GM_LOADING)) {
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(NULL, B_FALSE);
//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)) {
// giving to player at start of game
if (hasjobcat(where->owner, JC_MAGE)) {
enum OBTYPE firstspell;
nspells = 5;
firstlev = 2;
// fixed first spell
firstspell = getfirstwizspell(bookcontents);
assert(addobfast(o->contents, firstspell));
} else {
nspells = 5;
firstlev = 1;
}
} 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);
// special case - paladin's always have this spell
if ((lev == 3) && (where->owner) && hasjob(where->owner, J_PALADIN) &&
(bookcontents == SS_LIFE)) {
while (oid == OT_S_DISRUPTUNDEAD) {
oid = getrandomspellfromschool(bookcontents,lev);
}
}
if (oid != OT_NONE) {
assert(addobfast(o->contents, oid));
}
}
} else if (o->type->id == OT_GRIMOIRE) {
// 1 spell from each school
int i;
int nschools = -1;
enum SPELLSCHOOL school[MAXCANDIDATES];
if (where->owner && isplayer(where->owner) && (gamemode == GM_CHARGEN)) {
if (hasjob(where->owner, J_BATTLEMAGE)) {
nschools = 3;
} else if (hasjobcat(where->owner, JC_MAGE)) {
nschools = 6;
}
}
if (nschools == -1) {
enum SPELLSCHOOL ss;
nschools = 0;
// one spell from each school
for (ss = SS_NONE; ss <= SS_LAST; ss++) {
if (schoolappearsinbooks(ss) && !streq(getschoolname(ss), "badschool")) {
school[nschools] = ss;
nschools++;
}
}
} else {
// select actual random schools to use.
for (i = 0; i < nschools; i++) {
if (where->owner && hasjob(where->owner, J_BATTLEMAGE)) {
switch (rnd(1,5)) {
case 1: school[i] = SS_FIRE; break;
case 2: school[i] = SS_COLD; break;
case 3: school[i] = SS_AIR; break;
case 4: school[i] = SS_TRANSLOCATION; break;
case 5: school[i] = SS_WILD; break;
}
} else if (where->owner && hasjobcat(where->owner, JC_MAGE)) {
school[i] = getrandomspellschool(where->owner, B_TRUE);
} else {
// should never happen?
school[i] = getrandomspellschool(NULL, B_FALSE);
}
}
}
// now select the actual spells to give.
for (i = 0; i < nschools; i++) {
enum SPELLSCHOOL ss;
enum OBTYPE oid;
int wantlev = 1;
ss = school[i];
// giving to player at start of game? limit levels to
// 1-3.
if (where->owner && isplayer(where->owner) && (gamemode == GM_CHARGEN)) {
if (hasjobcat(where->owner, JC_MAGE)) {
switch (rnd(1,10)) {
case 1: case 2: case 3:
case 4: case 5: case 6:
wantlev = 1;
break;
case 7: case 8: case 9:
wantlev = 2;
break;
case 10:
wantlev = 3;
break;
}
} else if (hasjob(where->owner, J_BATTLEMAGE)) {
wantlev = 1;
} else {
wantlev = getrandomgrimoirelev();
}
} else { // not going to the player
wantlev = getrandomgrimoirelev();
}
// add a random spell from this school
oid = getrandomspellfromschool(ss,wantlev);
if (oid == OT_NONE) {
dblog("couldnt find grimoire contents for school=%s, lev=%d", getschoolname(ss), wantlev);
msg("couldnt find grimoire contents for school=%s, lev=%d", getschoolname(ss), wantlev);
} else if (!hasob(o->contents, oid)) {
assert(addobfast(o->contents, oid));
}
}
// now assign the grimoire a name
if (where->owner && isplayer(where->owner) && (gamemode == GM_CHARGEN)) {
char buf[BUFLEN];
getplayername(buf);
addflag(o->flags, F_NAME, NA, NA, NA, buf);
} else {
assignnpcname(o->flags);
}
} else { // ie. manual
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++;
}
}
// don't call sethiddenname, because it acts on OBJECT TYPES not OBJECTS.
// all books are unique
killflagsofid(o->flags, F_HASHIDDENNAME);
addflag(o->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, selhn->text);
// set the object's colour based on the hiddenname
setcolfromhiddenname(o->flags, selhn->text, o->type->obclass->glyph.ch);
}
// create linked holes in adjacent maps
if (dolinks && 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 == BH_WORLDMAP) && !adjmap) {
// ie. going down from the surface, and no dungeon below.
// ( MUST be down because holes going up make no sense! )
createbranchlink(c->map, c, o, NULL, BH_PIT, c->map->region);
} else {
// create linked holes on the map at the other end of this one.
if (adjmap) {
linkholes(adjmap);
}
}
}
// adjust nearby temperature
affect_temperature(o, B_ADD);
// 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, wantarmsize);
}
o->weight = corpserace->weight;
o->material = corpserace->material;
// remember the race type
addflag(o->flags, F_CORPSEOF, corpserace->id, 1, 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]);
}
}
// fill in animal subspecies
copyflag(o->flags, corpserace->flags, F_AVIAN);
copyflag(o->flags, corpserace->flags, F_CANINE);
copyflag(o->flags, corpserace->flags, F_EQUINE);
copyflag(o->flags, corpserace->flags, F_FELINE);
} else if (o->type->id == OT_MAP) {
region_t *srcregion;
branch_t *dstrt = NULL;
int srcdepth;
char buf[BUFLEN];
regionthing_t *poss[MAXCANDIDATES];
int nposs = 0;
// fill in map destination.
if (!wantregionthing) {
getbranchlinks(poss, &nposs, RT_BRANCHLINK, RT_NONE);
if (nposs) {
wantregionthing = poss[rnd(0,nposs-1)];
}
}
if (dolinks) {
if (wantregionthing) {
// we now have the destination branchlink thing which the
// map will lead to.
// just using this to fill in srcregion
findregionthing(wantregionthing->id, &srcregion);
srcdepth = wantregionthing->depth;
dstrt = findbranch(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, or correct size.
if (where) {
corpserace = getrandomcorpserace(where, wantarmsize);
}
if (!corpserace) {
// ie. vending machine, or inside another object/fake cell?
corpserace = getrandomcorpserace(NULL, wantarmsize);
}
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, 1, 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 = flagtomaxhp(f);
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, 1, 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, wantarmsize);
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, wantarmsize);
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, 1, 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]);
}
}
}
if (o->type->id == OT_CORPSE) {
if (corpserace) {
// inherit properties from race
copycorpseflags(o->flags, corpserace->flags);
}
}
// 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) {
// double hit points.
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) {
// half hit points.
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*5;
else f->val[2] = rnd(1,3) * 5;
}
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 * 5;
else f->val[2] = rnd(1,3) * 5;
}
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 * 5;
else f->val[2] = rnd(1,3) * 5;
}
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 * 5;
else f->val[2] = rnd(1,3) * 5;
}
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
f = hasflag(o->flags, F_CONTAINER);
if (f && getoblocation(o)) {
// randomly generate container contents if required.
gencontainercontents(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);
}
if (!jamchance) {
f = hasflag(o->flags, F_CANBEJAMMED);
if (f) {
jamchance = f->val[0] + (f->val[1] * (obloc->map->depth/5));
}
}
if (jamchance && pctchance(jamchance)) {
addflag(o->flags, F_JAMMED, 1, B_FALSE, getobjamdiff(obloc->map->depth), 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(o);
addflag(o->flags, F_TRAPPED, traptype, NA, NA, NULL);
}
// monster hiding inside?
f = hasflag(o->flags, F_CANHAVELFINSIDE);
if (f) {
enum LFSIZE maxsize;
maxsize = getobsize(o);
if (maxsize != SZ_MINI) {
int hidingchance;
hidingchance = f->val[0] + (f->val[1] * (obloc->map->depth/5));
limit(&hidingchance, f->val[0], f->val[2]);
if (pctchance(hidingchance)) {
race_t *r;
r = getrandomraceofsize(maxsize-1);
if (r) {
addflag(o->flags, F_LFINSIDE, r->id, 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));
}
}
}
// wantflags
if (o) {
copyflags(o->flags, wantflags, NA);
}
} // 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
killflagpile(wantflags);
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->naltprefix = 0;
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 addomprefix(enum OBMOD id, char *altprefix) {
char buf[BUFLEN];
obmod_t *om;
om = findobmod(id);
assert(om);
assert(om->naltprefix < MAXOMPREFIXES);
snprintf(buf, BUFLEN, "%s ",altprefix);
om->altprefix[om->naltprefix] = strdup(buf);
om->naltprefix++;
}
void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes, int includecentre, lifeform_t *creator, object_t **retob, int **retobcount, int *nretobs) {
cell_t *cell[MAXCANDIDATES],*c;
int ncells,i;
objecttype_t *ot;
object_t *o;
int nmade = 0;
ot = findotn(name);
if (!ot) return;
getradiuscells(centre, radius, DT_ORTH, B_FALSE, LOF_WALLSTOP,
(radius == 0) || includecentre ? B_TRUE : B_FALSE, cell, &ncells, B_FALSE);
if (nretobs) *nretobs = 0;
for (i = 0; i < ncells; i++) {
c = cell[i];
if (!c->type->solid) {
if (allowdupes || !hasob(c->obpile, ot->id)) {
o = addob(c->obpile, name);
if (o) {
if (creator) {
setobcreatedby(o, creator);
}
if (retob) retob[nmade] = o;
if (retobcount) *(retobcount[nmade]) = o->amt;
nmade++;
if (nretobs) (*nretobs) = nmade;
}
}
}
}
}
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 (ismeleedam(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_MAGIC:
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;
}
if (hasflag(o->flags, F_IMMUTABLE)) {
*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;
}
}
// cooked food won't decay to nothing
if ((damtype == DT_DECAY) && hasflag(o->flags, F_PREPARED)) {
int hpcur;
hpcur = getobhp(o, NULL);
if (hpcur <= *dam) {
// go to 1 hp
*dam = hpcur - 1;
return;
}
}
// adjust damage
if (o->blessed == B_BLESSED) {
// high chance of no hp loss
if (pctchance(80)) {
lifeform_t *owner;
owner = o->pile->owner;
if (owner && (isplayer(owner) || cansee(player, owner))) {
// become known if seen by player
if (!isblessknown(o)) {
char obname[BUFLEN],lfname[BUFLEN];
getlfname(owner, lfname);
getobname(o, obname, o->amt);
msg("%s%s %s pulses with holy light as it is struck!",
lfname, getpossessive(lfname),
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;
if (amt == NA) return 0;
// 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;
if (amt == NA) return 0;
// 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 affect_temperature(object_t *o, int action) {
flag_t *f;
int dir,howmuch;
cell_t *c,*c2;
f = hasflag(o->flags, F_TEMPMOD);
if (f) {
howmuch = f->val[0];
} else {
return;
}
if (action == B_REMOVE) {
// reverse
howmuch *= -1;
}
c = getoblocation(o);
if (!c) return;
// adjust this cell
modcelltemp(c, howmuch);
// adjust surrounding cells
for (dir = DC_N; dir <= DC_NW; dir++) {
c2 = getcellindir(c, dir);
if (c2) modcelltemp(c2, howmuch);
}
}
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) {
// ...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;
flag_t *f;
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;
if (gamemode == GM_GAMESTARTED) {
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;
}
// remove negative "bonuses". ie. "-3 sword" becomes "sword".
f = hasflag(o->flags, F_BONUS);
if (f && (f->val[0] < 0)) {
killflag(f);
}
} else {
// game not started yet.
owner = o->pile->owner;
if (owner && isplayer(owner)) {
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)) {
if (o->pile->where == lf->cell) {
flag_t *f;
f = hasflag(blockob->flags, F_BLOCKSVIEW);
if (f && (f->val[1] == B_TRUE)) {
// ok
} else {
return B_FALSE;
}
}
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 (o->pile->where && (o->pile->where->map == lf->cell->map)) {
if (isblind(lf) || (!haslos(lf, o->pile->where)) ) {
if (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);
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);
}
}
// returns TRUE if user chose to cancel the action
int confirmknowntraps(object_t *o) {
if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) {
if (getattrbracket(getattr(player, A_WIS), A_WIS, NULL) >= AT_AVERAGE) {
char ch;
char buf[BUFLEN],obname[BUFLEN];
getobname(o, obname, o->amt);
snprintf(buf, BUFLEN,"Really operate %s?", obname);
ch = askchar(buf,"yn","n", B_TRUE, B_FALSE);
if (ch != 'y') {
return B_TRUE;
}
}
}
return B_FALSE;
}
void copyobprops(object_t *dst, object_t *src) {
dst->material = src->material;
dst->weight = src->weight;
if (src->inscription) {
dst->inscription = strdup(src->inscription);
}
setblessed(dst, src->blessed);
dst->blessknown = src->blessknown;
// copy flags
while (dst->flags->first) {
killflag(dst->flags->first);
}
copyflags(dst->flags, src->flags, NA);
}
int 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;
}
// returns the value of the most valuable object with flag 'fid'
// if fid is F_NONE, just returns the most valuable object
int counthighestobflagvalue(obpile_t *op, enum FLAG fid) {
object_t *o;
int maxval = -1;
for (o = op->first ; o ; o = o->next) {
if ((fid == F_NONE) || hasflag(o->flags, fid)) {
int thisval;
thisval = getobvalue(o);
if (thisval > maxval) {
maxval = thisval;
}
}
}
return maxval;
}
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, int includetrails) {
object_t *o;
int count = 0;
for (o = op->first ; o ; o = o->next) {
if (hasflag(o->flags, F_TRAIL) && !includetrails) continue;
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));
o->blessknown = 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 surrounds %s.",obname);
o->blessknown = B_TRUE;
}
}
}
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, lifeform_t *srclf) {
object_t *o, *nexto;
int nhurt = 0;
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_TRAIL) && !hasflag(o->flags, F_DEAD)) {
if (takedamage(o, howmuch, damtype, srclf)) {
nhurt++;
}
}
}
if (srclf && nhurt) {
if (isplayer(srclf)) {
if (basedamagetype(damtype) == DT_FIRE) {
pleasegodmaybe(R_GODFIRE, nhurt*2);
}
}
}
}
void dodecay(object_t *o) {
flag_t *decayflag;
decayflag = hasflag(o->flags, F_DECAY);
if (!decayflag || (decayflag->val[2] <= 0) ) return;
if (isrotting(o)) {
// if food is rotting, then actually damage it.
takedamage(o, decayflag->val[2], DT_DECAY, NULL);
} else {
// increase decay level
decayflag->val[0] += decayflag->val[2];
addflag(o->flags, F_LASTDAMTYPE, DT_DECAY, NA, NA, NULL);
}
}
// 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_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
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], f->val[1], 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;
}
// trigger any traps attached to a regular object (ie. a door with a trap)
int triggerattachedtraps(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, o);
killflag(f); // now the trap gets removed
// some traps kill the object
switch (trapid) {
case OT_TRAPMINE:
case OT_TRAPDOORFALL:
killob(o);
break;
default: break;
}
return B_TRUE;
}
return B_FALSE;
}
void dumpobs(int ntogen) {
int depth, i;
char obname[BUFLEN];
for (depth = 1; depth <= MAXDEPTH; depth++) {
dblog("Sample obs for depth %d:", depth);
for (i = 0; i < ntogen; i++) {
condset_t cs;
initcond(&cs);
if (real_getrandomob(NULL, obname, depth, H_DUNGEON, RR_NONE, B_TRUE, &cs)) {
dblog(" %s", obname);
} else {
dblog(" (no object found)");
}
}
}
}
// this entire function doesn't really make sense anymore.
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_WEAPON) {
int thisrar;
f = hasflag(ot->flags, F_RARITY);
if (!f) continue;
thisrar = f->val[1];
if ((thisrar >= min) && (thisrar <= max)) {
skill_t *sk;
sk = getobskill(ot->flags);
dblog("\t%s - %s", ot->name, sk ? sk->name : "");
}
}
}
}
}
void explodeob(object_t *o, flag_t *f, int bigness, lifeform_t *causedby) {
cell_t *c;
int dam;
char obname[BUFLEN];
object_t *outerob;
dam = roll(f->text);
// inside a container? if so, it's the container
// which you will see exploding.
if (o->pile->parentob) {
outerob = o->pile->parentob;
} else {
outerob = o;
}
c = getoblocation(outerob);
// override causedby
if (!causedby) {
flag_t *f;
f = hasflag(o->flags, F_THROWNBY);
if (f) {
causedby = findlf(c->map, f->val[0]);
}
}
getobname(outerob, obname, outerob->amt);
// announce
if (outerob->pile->owner) {
if (isplayer(outerob->pile->owner)) {
msg("^BYour %s explode%s!^n", noprefix(obname),OBS1(outerob));
} else if (cansee(player, outerob->pile->owner)) {
char lfname[BUFLEN];
getlfname(outerob->pile->owner, lfname);
msg("^B%s%s %s explode%s!^n", lfname, getpossessive(lfname), noprefix(obname),OBS1(outerob));
}
} else if (haslos(player, c)) {
msg("^B%s explode%s!^n", obname,OBS1(outerob));
}
explodecells(c, dam * o->amt, bigness ? B_TRUE : B_FALSE, o, bigness , DT_ORTH, B_FALSE, causedby);
// object dies.
//removeob(o, o->amt);
// explode the container
removeob(outerob, outerob->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;
plural_t *pl;
knowledge_t *k;
char *modname;
char *p;
char *tempstr;
int db = B_FALSE;
brand_t *om;
if (!strlen(name)) {
return NULL;
}
modname = strdup(name);
// make some replacements
//replace scrolls with scroll etc
for (pl = firstplural ; pl ; pl = pl->next) {
strrep(&modname, pl->plural, pl->singular, NULL);
}
// only at start...
if (strstr(modname, "the ") == modname) strrep(&modname, "the ", "", NULL);
if (strstr(modname, "an ") == modname) strrep(&modname, "an ", "", NULL);
if (strstr(modname, "a ") == modname) strrep(&modname, "a ", "", NULL);
strrep(&modname, "blessed ", "", NULL);
strrep(&modname, "uncursed ", "", NULL);
strrep(&modname, "cursed ", "", NULL);
//realloc(modname, strlen(temp)); strcpy(modname, temp); free(temp); // swap
// strip out pre mods
strrep(&modname, "flaming ", "", NULL);
strrep(&modname, "headless ", "", NULL);
// strip out brands (but not only for certain object classes)
for (om = firstbrand; om ; om = om->next) {
strrep(&modname, om->suffix, "", NULL);
}
// special cases
strrep(&modname, "holy water", "water", NULL);
strrep(&modname, "incompetence", "competence", NULL);
// skip past bonusses
p = strchr(modname, '+');
if (p) {
while (*p && !isalpha(*p)) {
p++;
}
tempstr = strdup(p);
strcpy(modname, tempstr);
free(tempstr);
}
p = strchr(modname, '-');
if (p) {
while (!isalpha(*p)) {
p++;
}
tempstr = strdup(p);
strcpy(modname, tempstr);
free(tempstr);
}
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, but exclude certain things like innate attacks ("whipattack")
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id != OC_SPELL) && (ot->obclass->id != OC_ABILITY)) {
if (!hasflag(ot->flags, F_UNARMEDWEP)) {
if (strstr(ot->name, modname)) {
free(modname);
// found it!
return ot;
}
}
}
}
free(modname);
return NULL;
}
// this will return the number to subtract from our percentage die roll
// when determining objects.
//
// lower roll = better object possibilities
int getobrollmod(int depth, int range, int oodok) { // TODO: range currently unused
int upchance = 2;
//int downchance = 3;
int mod = depth; // ie. dlev1 = -1 to roll. dlev 25 = -25 to roll.
// adjust modifier for better objects
if (oodok && (onein(upchance))) {
mod++;
upchance++;
// repeated chances of getting better
while ((upchance < 10) && (mod < 100) && onein(upchance)) {
mod++;
upchance++;
}
}
return mod;
}
int getrustdampct(object_t *o) {
int pct = 100;
flag_t *rust;
rust = hasflag(o->flags, F_RUSTED);
if (rust) {
if (rust->val[0] >= R_TRUSTY) {
pct = 50;
} else if (rust->val[0] >= R_VRUSTY) {
pct = 75;
} else {
pct = 90;
}
}
return pct;
}
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(getmapsolid(obloc->map))->glyph);
return &(findcelltype(getcellsolid(obloc))->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_LIGHTBLUE;
} 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;
}
int fountain_will_dryup(object_t *o) {
if (hasflagval(o->flags, F_LINKOB, OT_POT_EXPERIENCE, NA, NA, NULL) ||
onein(ONEIN_FOUNTAINDRYUP)) {
cell_t *loc;
loc = getoblocation(o);
if (haslos(player, loc)) {
char fname[BUFLEN];
getobname(o, fname, o->amt);
msg("%s dries up.", fname);
}
return B_TRUE;
}
return B_FALSE;
}
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;
}
}
if (howfar != UNLIMITED) {
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, OT_NONE, NULL);
}
} else {
// add object
addob(dst->obpile, what);
}
}
}
void getgoodnessdr(enum GOODNESS gd, int *mindr, int *maxdr) {
// default
if (mindr) *mindr = NA;
if (maxdr) *maxdr = NA;
switch (gd) {
case G_AVERAGE:
if (mindr) *mindr = 3;
if (maxdr) *maxdr = 5;
break;
case G_GOOD:
if (mindr) *mindr = 6;
if (maxdr) *maxdr = 8;
break;
case G_GREAT:
if (mindr) *mindr = 9;
if (maxdr) *maxdr = 11;
break;
case G_EXCELLENT:
if (mindr) *mindr = 12;
if (maxdr) *maxdr = NA;
break;
default: break;
}
}
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 gencontainercontents(object_t *o) {
flag_t *retflag[MAXCANDIDATES],*f;
int nretflags = 0;
race_t *r;
char buf[BUFLEN];
getflags(o->flags, retflag, &nretflags, F_CONTENTS, F_NONE);
if (!nretflags) return;
f = retflag[rnd(0,nretflags-1)];
switch (f->val[0]) {
case CC_AMMO:
addflag(o->flags, F_STARTOB, 50, NA, NA, "1-10 arrows");
addflag(o->flags, F_STARTOB, 50, NA, NA, "1-10 arrows");
addflag(o->flags, F_STARTOB, 50, NA, NA, "1-10 arrows");
addflag(o->flags, F_STARTOB, 50, NA, NA, "1-10 bolts");
addflag(o->flags, F_STARTOB, 50, NA, NA, "1-10 bolts");
addflag(o->flags, F_STARTOB, 50, NA, NA, "1-10 bullets");
addflag(o->flags, F_STARTOB, 50, NA, NA, "1-10 bullets");
break;
case CC_CORPSE:
r = getrandomraceofsize(getobsize(o));
if (r) {
sprintf(buf, "%s corpse", r->name);
addflag(o->flags, F_STARTOB, 100, NA, NA, buf);
}
break;
case CC_CONDIMENTS:
switch (rnd(1,2)) {
default: case 1: sprintf(buf, "pinch of salt"); break;
case 2: sprintf(buf, "lump of sugar"); break;
}
addflag(o->flags, F_STARTOB, 100, 10, 20, buf);
break;
case CC_FOOD:
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
break;
case CC_FOODDRINK:
addflag(o->flags, F_STARTOBCLASS, 50, OC_POTION, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_POTION, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_POTION, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
addflag(o->flags, F_STARTOBCLASS, 50, OC_FOOD, NA, NULL);
break;
case CC_GUNPOWDER:
addflag(o->flags, F_STARTOB, 100, 5, 20, "pile of gunpowder");
break;
case CC_WATER:
addflag(o->flags, F_STARTOB, 100, NA, NA, "very deep water");
break;
default: break;
}
}
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 unused 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;
}
enum RARITY getavgrarity(flagpile_t *fp) {
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0,i;
int temprr = 0,nfound = 0;
enum RARITY finalrr = RR_NEVER;
getflags(fp, retflag, &nretflags, F_RARITY, F_NONE);
for (i = 0; i < nretflags; i++) {
if (retflag[i]->val[2] > 0) {
temprr += retflag[i]->val[2];
nfound++;
}
}
if (nfound) {
finalrr = (enum RARITY) (temprr / nfound);
}
return finalrr;
}
// 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 TRUE if an break obejct was found.
int getbreakob(object_t *o, char *bigobname, char *smallobname) {
enum MATERIAL mid;
enum DAMTYPE dt;
enum OBTYPE bigoid = OT_NONE,smalloid = OT_NONE;
objecttype_t *big = NULL,*small = NULL;
flag_t *f;
int nsmall = 0,nbig = 0;
double weight;
char bigadj[BUFLEN],smalladj[BUFLEN];
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0,i;
int found = B_FALSE;
mid = o->material->id;
dt = oblastdamtype(o);
// name to return.
strcpy(bigobname, "");
strcpy(smallobname, "");
// flag overrides code below
found = 0;
getflags(o->flags, retflag, &nretflags, F_BREAKOB, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (f && (f->val[0] == dt)) {
if (found == 0) {
strcpy(bigobname, f->text);
} else if (found == 1) {
strcpy(smallobname, f->text);
} else {
msg("ERROR - too many F_BREAKOB flags in %s",o->type->name);
dblog("ERROR - too many F_BREAKOB flags in %s",o->type->name);
}
found++;
}
}
if (found) {
return B_TRUE;
}
// adjectives
strcpy(bigadj, "");
strcpy(smalladj, "");
switch (mid) {
case MT_WOOD:
if (ismeleedam(dt) || (dt == DT_EXPLOSIVE)) {
bigoid = OT_WOODPLANK;
smalloid = OT_WOODSHARD;
} else {
switch (dt) {
case DT_FIRE:
case DT_HEAT:
case DT_ACID:
bigoid = OT_ASH;
smalloid = OT_ASH;
break;
default: break;
}
}
break;
case MT_GLASS:
if (ismeleedam(dt) || (dt == DT_EXPLOSIVE)) {
bigoid = OT_BROKENGLASS;
smalloid = OT_BROKENGLASS;
break;
} else {
switch (dt) {
case DT_FIRE:
case DT_HEAT:
case DT_ACID:
bigoid = OT_ASH;
smalloid = OT_ASH;
break;
default: break;
}
}
break;
case MT_ICE:
if (ismeleedam(dt) || (dt == DT_EXPLOSIVE)) {
bigoid = OT_ICECHUNK;
smalloid = OT_ICECHUNK;
break;
} else {
switch (dt) {
case DT_FIRE:
case DT_HEAT:
bigoid = OT_PUDDLEWATERL;
smalloid = OT_PUDDLEWATER;
break;
default: break;
}
}
break;
case MT_FLESH:
switch (dt) {
case DT_FIRE:
bigoid = OT_ASH;
smalloid = OT_ASH;
break;
default: break;
}
break;
default: break;
}
big = findot(bigoid);
small = findot(smalloid);
// now find out how many big obejcts make up the wight.
// NOTE: purposesly not using getobweight() here since
// we don't case about whther it is in water, etc.
weight = getobweight(o);
if (big) {
nbig = weight / big->weight;
weight -= (nbig*big->weight);
}
if (small){
nsmall = weight / small->weight;
weight -= (nsmall*small->weight);
}
if (nbig) {
sprintf(bigobname, "%d %s",nbig,big->name);
}
if (nsmall) {
sprintf(smallobname, "%d %s",nsmall,small->name);
}
if (strlen(bigobname) || strlen(smallobname)) {
return B_TRUE;
}
return B_FALSE;
}
// 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 weapon 'wep', or for a throw/unarmed attack if wep is null.
// (ie. the accuracy for a range of 0).
int getobaccuracy(object_t *wep, lifeform_t *weilder, int forthrow) {
int acc = -1;
flag_t *f;
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];
} else {
// if weapon doesn't have F_accuracy flag, use lifeform's agility.
acc = -1;
}
}
if (acc == -1) {
if (weilder) {
// initial accuracy is based on your agility.
acc = getattr(weilder, A_AGI) + 20;
limit(&acc, 20, 100);
} else {
acc = 100;
}
}
if (wep) {
// blessed weapons have better base accuracy
if (wep->blessed == B_BLESSED) acc += 10;
//bonusses?
acc += (getobbonus(wep, B_FALSE)*10);
}
if (weilder && !forthrow) {
enum SKILLLEVEL weplev = PR_INEPT;
// adjust for weapon skill
weplev = getweaponskill(weilder, wep);
switch (weplev) {
case PR_INEPT:
if (getskill(weilder, SK_COMBAT)) {
acc -= 5; break;
} else {
acc -= 15; break;
}
case PR_NOVICE: break; // no change
case PR_BEGINNER: acc += 5; break;
case PR_ADEPT: acc += 10; break;
case PR_SKILLED: acc += 15; break;
case PR_EXPERT: acc += 20; break;
case PR_MASTER: acc += 30; break;
default: break;
}
}
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;
}
lifeform_t *getobcreatedby(object_t *o) {
flag_t *f;
lifeform_t *creator = NULL;
f = hasflag(o->flags, F_CREATEDBY);
if (f) {
creator = findlf(NULL, f->val[0]);
}
return creator;
}
int getobjamdiff(int depth) {
return 50 + rnd(0,depth*5);
}
// returns the amount to adjust a skillcheck roll if you give object 'o' to lifeform 'lf'
//
// if 'why' is provided, it will be populated with the reason the monster wants it.
int getoboffermod(object_t *o, lifeform_t *lf, char *why) {
int mod = 0,covets = B_FALSE;
if (why) strcpy(why, "");
// healing object and they were bleeding?
if ((islowhp(lf) || isbleeding(lf)) && hasflag(o->flags, F_AIHEALITEM)) {
if (aiobok(lf, o, lf)) {
mod += 3;
if (why) strcpy(why, "healing object");
}
}
// object which ai wants?
if (aiwants(lf, o, &covets)) {
if (covets) {
mod += 5;
if (why) strcpy(why, "greatly desired object");
} else {
mod += 3;
if (why) strcpy(why, "desired object");
}
}
return mod;
}
int getobpoints(object_t *o) {
if (hasflag(o->flags, F_NOPOINTS)) {
return 0;
}
return getobvalue(o);
}
void getobs(obpile_t *op, condset_t *cs, object_t **ob, int *nobs) {
object_t *o;
*nobs = 0;
if (!op) return;
for (o = op->first ; o ; o = o->next) {
if (obmeets(o, cs)) {
ob[*nobs] = o;
(*nobs)++;
}
}
}
// returns the skill associated with this object
// (via its flagpile)
skill_t *getobskill(flagpile_t *fp) {
flag_t *f;
f = hasflag(fp, 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;
}
int getobvalue(object_t *o) {
return real_getobvalue(o, o->amt);
}
// returns value of obejcts, in gold
int real_getobvalue(object_t *o, int amt) {
float price = 1;
flag_t *f;
int i;
enum RARITY rr = RR_FREQUENT;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
if (o->type->id == OT_GOLD) {
return amt;
}
if (o->type->id == OT_CREDITCARD) {
return getcharges(o);
}
// rarity
getobrarity(o, &rr);
//adjustprice(o->type, &price);
// fixed prices
f = hasflag(o->flags, F_VALUE);
if (f) {
price = f->val[0];
} else {
// base value: weight * material value
price = (float)getobweight(o) * (float)getmaterialvalue(o->material->id);
}
if (o->type->id == OT_SPELLBOOK) {
price += (89*countobs(o->contents, B_FALSE));
} else if (o->type->id == OT_GRIMOIRE) {
price += (59*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*7);
}
// 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;
}
}
if (hasflagval(o->flags, F_USESSKILL, SK_EXOTICWEPS, NA, NA, NULL)) {
price *= 2;
}
// TODO: rings?
if (hasflag(o->flags, F_HASBRAND)) {
price += 1000;
}
// 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...
if (!hasflag(o->flags, F_VALUE)) {
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 (o->type->id == OT_POT_WATER) {
if (isblessed(o) || iscursed(o)) price *= 29.25;
} else {
if (isblessed(o) || iscursed(o)) price *= 1.25;
}
// minimum
limitf(&price, 1, NA);
price = ((int)price * amt);
//dblog("price for %s is %d", o->type->name, (int)price);
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 *getbasicammofor(object_t *gun) {
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0, i;
getflags(gun->flags, retflag, &nretflags, F_AMMOOB, F_NONE);
for (i = 0; i < nretflags; i++) {
if (retflag[i]->val[2] == B_TRUE) {
return findot(retflag[i]->val[0]);
}
}
return NULL;
}
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);
case SK_WHIPS:
return findot(OT_WHIPBULL);
default:
break;
}
return NULL;
}
object_t *getrandomammo(lifeform_t *lf) {
object_t *gun;
gun = getfirearm(lf);
if (!gun) {
return NULL;
}
return findammoinobpile(gun, lf->pack);
}
object_t *findammoinobpile(object_t *gun, obpile_t *op) {
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0, i;
flag_t *f;
object_t *o;
getflags(gun->flags, retflag, &nretflags, F_AMMOOB, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
for (o = op->first ; o ; o = o->next) {
if (o->type->id == f->val[0]) {
return o;
}
}
}
return NULL;
}
objecttype_t *getrandomammofor(object_t *o, int usebasic) {
objecttype_t *ot;
objecttype_t *poss[MAXCANDIDATES];
int nposs = 0;
if (!o || !isfirearm(o)) {
return NULL;
}
if (usebasic) {
ot = getbasicammofor(o);
} else {
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;
}
int getrandomgrimoirelev(void) {
int wantlev = 1;
switch (rnd(1,10)) {
case 1:
case 2:
case 3:
case 4:
case 5:
wantlev = rnd(1,2);
break;
case 6:
case 7:
case 8:
wantlev = rnd(3,4);
break;
case 9:
case 10:
wantlev = rnd(5,6);
break;
}
return wantlev;
}
objecttype_t *getrandomobofclass(enum OBCLASS ocid, int minrarity, int maxrarity, int wantrr, lifeform_t *forlf, condset_t *cs) {
objecttype_t *ot;
int totcount = 0, count = 0;
flag_t *f;
objecttype_t **poss;
int nposs = 0,tryagain = B_TRUE;
// count total # of objecttypes
for (ot = objecttype ; ot ; ot = ot->next) {
totcount++;
}
poss = malloc(totcount*sizeof(objecttype_t *));
tryagain = B_TRUE;
while (tryagain) {
tryagain = B_FALSE;
for (ot = objecttype ; ot ; ot = ot->next) {
if ((ot->obclass->id == ocid) && !hasflag(ot->flags, F_UNIQUE)) {
int rarityok = B_FALSE;
int skillok = B_FALSE;
int condok = B_FALSE;
// does rarity match?
f = hasflag(ot->flags, F_RARITY);
if (f) {
if (f->val[1] == NA) {
rarityok = B_TRUE;
} else if ( ((minrarity == NA) || (f->val[1] >= minrarity)) &&
((maxrarity == NA) || (f->val[1] <= maxrarity)) ) {
rarityok = B_TRUE;
}
if (wantrr) {
int rrok = B_FALSE;
if (f->val[2] == NA) {
rrok = B_TRUE;
} else if (f->val[2] == wantrr) {
rrok = B_TRUE;
}
if (!rrok) rarityok = B_FALSE;
}
} else {
}
// if we're randomly selecting an object for a lf, make sure it matches their
// skillset
if (forlf) {
skill_t *sk;
sk = getobskill(ot->flags);
if (!sk || getskill(forlf, sk->id)) {
skillok = B_TRUE;
}
} else skillok = B_TRUE;
if (cs) {
if (otmeets(ot, cs)) {
condok = B_TRUE;
}
} else condok = B_TRUE;
/*
// minar only applies if we're asking for armour
if (minar && (ocid == OC_ARMOUR)) {
flag_t *f;
f = hasflag(ot->flags, F_ARMOURRATING);
if (f && (f->val[0] >= minar )) {
armourok = B_TRUE;
}
} else armourok = B_TRUE;
// min/max dr only applies if we're asking for weapons
if (((mindr != NA) || (maxdr != NA)) && (ocid == OC_WEAPON)) {
flag_t *f;
f = hasflag(ot->flags, F_DAM);
if (f) {
int minok = B_FALSE,maxok = B_FALSE;
if ((mindr == NA) || (f->val[1] >= mindr)) {
minok = B_TRUE;
}
if ((maxdr == NA) || (f->val[1] <= maxdr)) {
maxok = B_TRUE;
}
if (minok && maxok) {
wepok = B_TRUE;
}
}
} else wepok = B_TRUE;
if (musthaveflag != F_NONE) {
if (hasflag(ot->flags, musthaveflag)) {
flagok = B_TRUE;
}
} else flagok = B_TRUE;
*/
if (skillok && rarityok && condok) {
poss[nposs++] = ot;
count++;
}
}
}
if (count <= 0) {
if ((wantrr != NA) && (wantrr > RR_NONE)) {
wantrr--;
tryagain = B_TRUE;
}
}
}
if (count <= 0) {
free(poss);
return NULL;
}
ot = poss[rnd(0,nposs-1)];
free(poss);
return ot;
/*
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 rarityok = B_FALSE;
int skillok = B_FALSE;
int armourok = B_FALSE;
int flagok = B_FALSE;
// does rarity match?
f = hasflag(ot->flags, F_RARITY);
if (f) {
if (f->val[1] == NA) {
rarityok = B_TRUE;
} else if ( ((minrarity == NA) || (f->val[1] >= minrarity)) &&
((maxrarity == NA) || (f->val[1] <= maxrarity)) ) {
rarityok = B_TRUE;
}
}
// if we're randomly selecting an objcet for a lf, make sure it matches their
// skillset
if (forlf) {
skill_t *sk;
sk = getobskill(ot->flags);
if (!sk || getskill(forlf, sk->id)) {
skillok = B_TRUE;
}
} else skillok = B_TRUE;
if (minar) {
flag_t *f;
f = hasflag(ot->flags, F_ARMOURRATING);
if (f && (f->val[0] > 0)) {
armourok = B_TRUE;
}
} else armourok = B_TRUE;
if (musthaveflag != F_NONE) {
if (hasflag(ot->flags, musthaveflag)) {
flagok = B_TRUE;
}
} else flagok = B_TRUE;
if (skillok && rarityok && armourok && flagok) {
n++;
if (n == sel) {
return ot;
}
}
}
}
}
return NULL;
*/
}
enum OBTYPE getrandomtrapforob(object_t *o) {
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)) {
int ok = B_TRUE;
if (o && !isdoor(o, NULL)) {
if (hasflag(ot->flags, F_DOORTRAPONLY)) {
ok = B_FALSE;
}
}
if (ok) {
poss[nposs++] = ot->id;
}
}
}
return poss[rnd(0,nposs-1)];
}
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) || (ot->id == OT_GRIMOIRE)) {
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;
}
int getobhp(object_t *o, int *max) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
if (max) *max = f->val[1];
return f->val[0];
}
// default
if (max) *max = 1;
return 1;
}
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:
case MT_DIRT:
return 0;
case MT_WIRE:
case MT_FOOD:
case MT_PLANT:
case MT_ICE:
case MT_STONE:
case MT_BRICK:
return 1;
case MT_WATER:
case MT_SNOW:
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:
case MT_CRYSTAL:
return 6;
case MT_GOLD:
case MT_DRAGONWOOD:
return 7;
case MT_DURANITE:
return 10;
}
// 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)/5; // ie. 1 - 20
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;
}
}
if (hasflag(o->flags, F_BRUISED)) {
basenutr = pctof(75, basenutr);
limitf(&basenutr, 1, NA);
}
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) {
int blind = B_FALSE;
int known = B_FALSE;
if (gamemode == GM_GAMESTARTED) {
if (hasflag(o->flags, F_ISMONSTER)) {
if (o->contents->first) {
o = o->contents->first;
}
}
// can't see the object ?
if (o->pile->owner == player) {
if (!haslos(player, player->cell) || isblind(player)) {
if (!hasflag(o->flags, F_FEELTEXT)) {
blind = B_TRUE;
}
}
}
}
if (isknown(o)) {
known = B_TRUE;
} else {
if (o->pile->parentob && hasflag(o->pile->parentob->flags, F_SHOP)) {
known = B_TRUE;
}
}
if (known && !blind) {
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 {
// show generic description
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_SECONDARY);
if (f) {
strcat(buf, " (spare weapon)");
}
f = hasflag(o->flags,F_EQUIPPED);
if (f) {
if (f->val[0] == BP_WEAPON) {
if (istwohandedfor(o, o->pile->owner)) {
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 (istwohandedfor(o, o->pile->owner)) {
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 getobname(o, buf, o->amt);
}
// anything else - show the real name
return getobnametrue(o, buf, o->amt);
}
enum SKILL getskilltorepair(object_t *o) {
switch (o->material->id) {
case MT_CLOTH:
case MT_LEATHER:
return SK_SEWING;
case MT_METAL:
return SK_METALWORK;
default: break;
}
return SK_NONE;
}
char *getobname(object_t *o, char *buf, int count) {
return real_getobname(o, buf, count, B_PREMODS, B_CONDITION, B_BLINDADJUST, B_BLESSINGS, B_USED, B_NOSHOWALL);
}
char *getobnametrue(object_t *o, char *buf, int count) {
return real_getobname(o, buf, count, B_PREMODS, B_CONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_SHOWALL);
}
char *getobnametruebase(object_t *o, char *buf, int count) {
return real_getobname(o, buf, count, B_NOPREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_SHOWALL);
}
// buf must already be allocated
char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wantcondition, int adjustforblind, int wantblesscurse, int wanttried, int showall) {
char *pluralname;
char prefix[BUFLEN];
char basename[BUFLEN];
char localbuf[BUFLEN];
char buf2[BUFLEN];
char triedbuf[BUFLEN];
//int shopitem = 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,i;
// default to normal name
/*
if (hasflag(o->flags, F_SHOPITEM)) {
shopitem = B_TRUE;
}
*/
where = getoblocation(o);
if (hasflag(o->flags, F_ISMONSTER)) {
if (o->contents->first) {
if ((count == o->amt) || (count == ALL)) {
count = o->contents->first->amt;
}
o = o->contents->first;
limit(&count, 1, o->amt);
}
}
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, NULL, B_NOSHOWALL, B_REALRACE);
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 { // ie. footprints
char buf[BUFLEN];
enum SKILLLEVEL lorelev;
// beginner perception 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, "");
}
// monster lorelevel of beginner or higher - you get "newt footprints"
lorelev = getlorelevel(player, r->raceclass->id);
if (( lorelev >= PR_BEGINNER) && !hasflag(r->flags, F_NAME)) {
snprintf(buf, BUFLEN, "%s %s",r->name, o->type->name);
strcat(basename, buf);
} else if (lorelev >= PR_NOVICE) {
flag_t *sf;
enum LFSIZE sz = SZ_HUMAN;
// novice - you just get 'small animal footprints' etc
sf = hasflag(r->flags, F_SIZE);
if (sf) sz = sf->val[0];
snprintf(buf, BUFLEN, "%s %s %s",getsizetext(sz),
r->raceclass->name, o->type->name);
strcat(basename, buf);
} else {
strcat(basename, o->type->name);
}
// beginner perception and upwards gets the direction
if (slev >= PR_BEGINNER) {
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 ((o->type->id == OT_BLOODSPLASH) || (o->type->id == OT_BLOODPOOL)) {
race_t *brace = NULL;
enum SKILLLEVEL lorelev = PR_INEPT;
char *p,xblood[BUFLEN];
if (countflagsofid(o->flags, F_LINKRACE) > 1) {
sprintf(xblood, "mixed blood");
} else {
// you might recognise the type of blood
f = hasflag(o->flags, F_LINKRACE);
if (f) {
brace = findrace(f->val[0]);
if (brace) lorelev = getlorelevel(player, brace->raceclass->id);
}
if (brace && (lorelev >= PR_BEGINNER)) {
sprintf(xblood, "%s blood", brace->name);
} else {
sprintf(xblood, "unidentifiable blood");
}
}
p = strdup(o->type->name);
strrep(&p, "blood", xblood, NULL);
snprintf(basename, BUFLEN, "%s", p);
free(p);
} 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 if (o->type->id == OT_GRIMOIRE) {
// check F_NAME field
f = hasflag(o->flags, F_NAME);
if (f) {
sprintf(basename, "%s%s grimoire", f->text, getpossessive(f->text));
}
} else {
f = hasflag(o->flags, F_MANUALOF);
if (f) {
skill_t *sk;
sk = findskill(f->val[0]);
assert(sk);
strcat(basename, " of ");
switch (sk->id) {
case SK_ARMOUR: strcat(basename, "Armour Usage"); break;
case SK_LISTEN: strcat(basename, "Listening"); break;
case SK_LORE_LANGUAGE: strcat(basename, "Linguistics"); break;
case SK_LORE_RELICS: strcat(basename, "Ancient Relics"); break;
default:
strcat(basename, sk->name);
break;
}
}
}
}
} 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) {
no_a = B_TRUE;
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)
if (showall) {
srcp = ot->name;
} else {
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?
pluralname = strdup(basename);
if (!hasflag(o->flags, F_NO_PLURAL)) {
makeplural(&pluralname);
}
}
// 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;
}
}
}
// condition
// include mods
// ie. a blessed ->damaged<- flaming +5 silver sword of pyromania
if (wantcondition) {
// eaten?
f = hasflag(o->flags, F_EDIBLE);
if (f && (f->val[2] != NA)) {
strcat(localbuf, "partially eaten ");
}
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 && (!isblind(player) || !adjustforblind)) {
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);
}
}
}
// other prefixes
if (hasflagknown(o->flags, F_HOT)) {
strcat(localbuf, "red-hot ");
}
if (hasflagknown(o->flags, F_ARMOURPIERCE) && !hasflag(o->type->flags, F_ARMOURPIERCE)) {
strcat(localbuf, "vorpal ");
}
}
// 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])/5);
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(getmapsolid(where->map));
free(pluralname);
// solid cell description
pluralname = strdup(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<-
getflags(o->flags, retflag, &nretflags, F_HASBRAND, F_NONE);
for (i = 0; i < nretflags; i++) {
flag_t *brf;
int ok = B_TRUE;
f = retflag[i];
br = findbrand(f->val[0]);
// are all of the brand flags known?
for (brf = br->flags->first ; brf ; brf = brf->next) {
flag_t *retflag2[MAXCANDIDATES],*f2;
int nretflags2 = 0,n;
getflags(o->flags, retflag2, &nretflags2, brf->id, F_NONE);
for (n = 0; n < nretflags2; n++) {
f2 = retflag2[n];
if (f2->lifetime == FROMBRAND) {
if (f2->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, RF_SHORT);
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, "'");
}
}
// show NAMED
f = hasflag(o->flags, F_NAMED);
if (f) {
strcat(localbuf, " named ");
strcat(localbuf, f->text);
}
// append inscription
if (o->inscription) {
strcat(localbuf, " {");
strcat(localbuf, o->inscription);
strcat(localbuf, "}");
}
// trap type known?
f = hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL);
if (f) {
if (getskill(player, SK_ENGINEERING) >= PR_BEGINNER) {
objecttype_t *traptype;
traptype = findot(f->val[0]);
if (traptype) {
strcat(localbuf, " (");
strcat(localbuf, traptype->name);
strcat(localbuf, ")");
}
}
}
// show if we've tried this
//if (!shopitem && (gamemode == GM_GAMESTARTED)) {
if ((gamemode == GM_GAMESTARTED) && wanttried) {
knowledge_t *k;
strcpy(triedbuf, "");
if (hasflag(o->flags, F_BEINGUSED)) {
if (strlen(triedbuf)) strcat(triedbuf, ", ");
else strcpy(triedbuf, " [");
strcat(triedbuf, "currently being read");
}
k = istried(o);
if (k) {
//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 (k->triedon) {
strcat(triedbuf, "tried on ");
strcat(triedbuf, k->triedon);
} 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?
if (shopitem) {
char pricebuf[BUFLEN];
// get price for _player_
snprintf(pricebuf, BUFLEN, " [$%d%s]", (int)getshopprice(o, player, NULL), o->pile->owner ? ", unpaid" : "");
strcat(localbuf, pricebuf);
}
*/
// apply prefix now!
if (count == 1) {
if (hasflag(o->flags, F_NO_A) && isknown(o)) {
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_THE) && 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);
}
if (op->owner) {
flag_t *f;
f = lfhasflag(op->owner, F_GRAVLESSENED);
if (f) {
weight -= (15 * f->val[0]);
}
}
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
}
// only show 'immutable' if this is abnormal.
if (hasflagknown(o->flags, F_IMMUTABLE) && !hasflag(o->type->flags, F_IMMUTABLE)) {
strcpy(buf, "immutable");
} else {
strcpy(buf, "");
}
if (hasflag(o->flags, F_BRUISED)) {
if (strlen(buf)) strcat(buf, " ");
strcat(buf, "bruised");
}
if (iscorpse(o)) {
if (hasflag(o->flags, F_PREPARED)) {
if (strlen(buf)) strcat(buf, " ");
strcat(buf, "cooked");
}
if (hasflag(o->flags, F_SALTED)) {
if (strlen(buf)) strcat(buf, " ");
strcat(buf, "salted ");
}
// 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");
}
*/
strcat(buf, "tainted");
}
} else {
char condbuf[BUFLEN];
if (iqb >= AT_LOW) {
pct = getobhppct(o);
if (pct >= 85) {
strcpy(condbuf, "");
} else if (pct >= 75) {
snprintf(condbuf, BUFLEN, "battered");
} else if (pct >= 50) {
snprintf(condbuf, BUFLEN, "damaged");
} else if (pct >= 25) {
snprintf(condbuf, BUFLEN, "very damaged");
} else {
snprintf(condbuf, BUFLEN, "critically damaged");
}
} else {
strcpy(condbuf, "");
}
if (strlen(condbuf)) {
if (strlen(buf)) strcat(buf, " ");
strcat(buf, condbuf);
}
}
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]);
}
// if no objectclass given then it will be picked randomly, or (if wepsk isn't sk_none) set to oc_weapon.
//objecttype_t *real_getrandomob(map_t *map, char *buf, int forcedepth, int forcehabitat, enum LFSIZE maxsize, enum SKILL wepsk, enum RARITY forcerr, int forpickup, ... ) {
objecttype_t *real_getrandomob(cell_t *cell, char *buf, int forcedepth, int forcehabitat, enum RARITY forcerr, int forpickup, condset_t *cs) {
objecttype_t *ot;
objecttype_t *poss[MAXRANDOMOBCANDIDATES];
int nposs = 0;
int selidx;
int amt;
flag_t *f;
int db = B_FALSE;
int partdb = B_FALSE;
char *pluralname = NULL;
char brandname[BUFLEN];
char cursestr[BUFLEN];
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;
int brandchance;
enum OBCLASS wantcl = OC_NONE;
int multiwantcl = B_FALSE;
int pctroll;
if (!db) db = obdb;
if (forcehabitat != NA) {
hab = findhabitat(forcehabitat);
} else if (cell) {
hab = cell->map->habitat;
} else {
hab = NULL;
}
if (hab) {
strcpy(habname, hab->name);
} else {
strcpy(habname, "(any)");
}
if (forcedepth >= 0) { // WAS: if (forcedepth != NA)
depth = forcedepth;
} else if (cell) {
depth = getmapdifficulty(cell->map);
} else {
depth = getmapdifficulty(NULL);
}
//getrarityrange(depth, &raritymin, &raritymax, RARITYVARIANCEOB, B_TRUE);
// pick rr...
if (forcerr == RR_NONE) {
wantrr = pickrr(TT_OBJECT);
} else {
wantrr = forcerr;
}
origwantrr = wantrr;
// no obclass given? pick one randomly.
wantcl = OC_NONE;
multiwantcl = B_FALSE;
for (i = 0; i < cs->nconds; i++) {
if (cs->cond[i] == CC_OBCLASS) {
if (wantcl == OC_NONE) wantcl = cs->arg[i];
else multiwantcl = B_TRUE;
}
}
if (wantcl == OC_NONE) {
addcond(cs, CC_OBCLASS, B_TRUE, getrandomobclass(hab->id));
}
/*
if (!nwantclass) {
if (wantsk || nwantdt) {
wantclass[0] = OC_WEAPON;
nwantclass = 1;
} else if (!nwantflag) {
wantclass[0] = getrandomobclass(hab->id);
nwantclass = 1;
//if (wantclass[0] == OC_WAND) {
//dblog("random WAND picked!");
//partdb = B_TRUE;
//}
}
}
*/
if (!multiwantcl && (wantcl == OC_BUILDING)) {
int minused = 9999;
objecttype_t *selot = NULL;
// find least used building
for (i = 0; i < nbuildingusage; i++) {
if (buildingusage[i].count < minused) {
minused = buildingusage[i].count;
}
}
// find possibilities
nposs = 0;
for (i = 0; i < nbuildingusage; i++) {
if (buildingusage[i].count == minused) {
enum OBTYPE thisoid;
thisoid = buildingusage[i].oid;
poss[nposs] = findot(thisoid);
assert(poss[nposs]);
nposs++;
}
}
assert(nposs > 0);
selot = poss[rnd(0,nposs-1)];
sprintf(buf, "%s", selot->name);
return selot;
}
// roll to determine object rarity
pctroll = rollforob(depth);
while (!done) {
if (db || partdb) dblog("adding random object with rarity value > %d rr <= %d(%s), for habitat %s",
pctroll,wantrr, getrarityname(wantrr), habname);
if (db || partdb) {
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) {
int rarnum;
rarnum = rarflag->val[1];
if (rarnum == NA) rarnum = 100;
//if ((rarnum >= raritymin) && (rarnum <= raritymax))
if (pctroll <= rarnum) {
// now check common, rare, etc
enum RARITY thisrr;
if (rarflag->val[2] == NA) {
thisrr = RR_FREQUENT;
} else {
thisrr = rarflag->val[2];
}
if (thisrr == wantrr) {
rarok = B_TRUE;
} else {
if (db) dblog(" %s rarity(%d) doesn't match wantrr(%d,%s)", ot->name,
rarflag->val[2], wantrr, getrarityname(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) {
if (db) dblog(" %s passes rarity check.", ot->name);
// matches conditions?
if (!otmeets(ot, cs)) {
condok = B_FALSE;
if (db) dblog(" %s doesn't match conditions.", ot->name);
}
if (forpickup) {
if (hasflag(ot->flags, F_NOPICKUP)) {
condok = B_FALSE;
if (db) dblog(" %s not pickupable.", ot->name);
}
}
}
if (rarok && condok) {
if (db) dblog("-> possibility: %s, rarity=%d",ot->name, rarflag->val[1]);
poss[nposs] = ot;
nposs++;
if (nposs >= MAXRANDOMOBCANDIDATES) break;
}
}
// nothing found?
if (nposs == 0) {
// already at lowest roll?
if (pctroll <= 0) {
// now lower wantrr
if (rrmoddir == -1) {
if (wantrr > RR_FREQUENT) {
wantrr--;
if (db || partdb) dblog("pctroll at min (0) and no obs. lowering wantrr to %d (%s).",wantrr,getrarityname(wantrr));
} else {
// wantrr is already at rr_frequent
// start increasing it now.
wantrr = origwantrr + 1;
rrmoddir = 1;
if (db || partdb) dblog("rarity got below frequent. raising to original rr + 1 (%d, %s)",wantrr,getrarityname(wantrr));
}
} else {
if (wantrr < RR_VERYRARE) {
wantrr++;
if (db || partdb) dblog("rarity at min/max and no obs. raising wantrr to %d(s).",wantrr,getrarityname(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 {
// lower roll and try again
pctroll -= 10;
if (db || partdb) dblog("no possible objects like this. trying again with pctroll=%d\n",pctroll);
}
} else {
// something found
done = B_TRUE;
}
} // end while !done
if (db || partdb) 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;
}
// chance to be blessed or cursed? 15% chance each way
strcpy(cursestr, "");
if (!hasflag(ot->flags, F_NOBLESS) && !hasflag(ot->flags, F_STARTSPLAIN)) {
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) && !hasflag(ot->flags, F_STARTSPLAIN)) {
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 = 10;
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 (hab && (hab->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 (hab && (hab->id == H_SEWER) && (ot->obclass->id == OC_FOOD)) {
strcat(cursestr, "tainted ");
}
// get random chance of having a brand (1% per depth, plus object adjustments)...
// if so...
strcpy(brandname, "");
sumflags(ot->flags, F_BRANDCHANCE, &brandchance, NULL, NULL);
brandchance += depth;
if (pctchance(brandchance)) {
brand_t *br;
br = getrandombrandfor(ot);
if (br) strcpy(brandname, br->suffix);
}
pluralname = strdup(ot->name);
if (amt > 1) {
makeplural(&pluralname);
}
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 ot;
}
objecttype_t *getrandomob(cell_t *cell, char *buf) {
condset_t cs;
initcond(&cs);
return real_getrandomob(cell, buf, NA, NA, RR_NONE, B_FALSE, &cs);
}
objecttype_t *getrandomobwithflag(cell_t *cell, enum FLAG fid, char *buf) {
condset_t cs;
initcondv(&cs, CC_HASFLAG, B_TRUE, fid,
CC_NONE);
return real_getrandomob(cell, buf, NA, NA, RR_NONE, B_FALSE, &cs);
}
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 (thisrarity == 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;
}
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, object_t *shop) {
float val;
val = applyshoppricemod(getobvalue(o), buyer, shop, SA_BUY);
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 = roll(f->text);
} 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()
if (dam < 0) dam = 0;
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, 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);
addengineeringinfo(player, retbuf, c);
}
if (c->writing) {
strcat(retbuf, ", writing:");
strcat(retbuf, c->writing);
}
return retbuf;
}
enum BODYPART getweildloc(object_t *o, lifeform_t *lf, 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 (istwohandedfor(o, lf)) {
*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 *hasequippedobidon(obpile_t *op, enum OBTYPE oid, enum BODYPART bp) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if ((o->type->id == oid) && isequippedon(o, bp)) return o;
}
return NULL;
}
int hasinteractableflags(flagpile_t *fp) {
if (hasflag(fp, F_IMPASSABLE)) {
return B_TRUE;
} else if (hasflag(fp, F_DOOR)) {
return B_TRUE;
} else if (hasflag(fp, F_CONTAINER)) {
return B_TRUE;
}
return B_FALSE;
}
object_t *hasknownob(obpile_t *op, enum OBTYPE oid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->type->id == oid) {
if (isknown(o)) {
return o;
}
}
}
return NULL;
}
object_t *hasob(obpile_t *op, enum OBTYPE oid) {
object_t *o;
for (o = op->first ; o ; o = o->next) {
if (o->type->id == oid) return o;
}
return NULL;
}
object_t *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;
}
// is there an object of type oid within 'dist' of cell
object_t *hasobflagwithin(cell_t *start, enum FLAG fid, int dist, int disttype) {
cell_t *retcell[MAXCANDIDATES],*c;
int nretcells,i,mindist=9999;
object_t *bestob = NULL;
getradiuscells(start, dist, disttype, B_FALSE, LOF_DONTNEED, B_INCLUDECENTRE, retcell, &nretcells, 0);
for (i = 0; i < nretcells; i++) {
int thisdist;
c = retcell[i];
thisdist = getcelldist(c, start);
if (thisdist < mindist) {
object_t *o = NULL;
o = hasobwithflag(c->obpile, fid);
if (o) {
bestob = o;
mindist = thisdist;
}
}
}
return bestob;
}
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 a while
howlong = rnd(DEF_BURNTIMEMIN,DEF_BURNTIMEMAX);
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
modifybetterwepdam(a, owner, &dama);
modifybetterwepdam(b, owner, &damb);
// modify with accuracy
acca = getobaccuracy(a, owner, B_FALSE);
accb = getobaccuracy(b, owner, B_FALSE);
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) {
if (o->type->id == OT_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) || hasflag(o->flags, F_HOT)) {
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_IMPORTANT)) return B_FALSE;
if (hasflag(o->flags, F_WET)) {
return B_FALSE;
}
if (hasflag(o->flags, F_FLAMMABLE)) {
return B_TRUE;
}
return B_FALSE;
}
int isfullycharged(object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_CHARGES);
if (f && (f->val[0] == f->val[1])) {
return B_TRUE;
}
return B_FALSE;
}
// returns amt of damage to do if touched while hot.
int isheatable(object_t *o) {
if (hasflag(o->flags, F_WET) || hasflag(o->flags, F_SHOP)) {
return 0;
}
if (hasflag(o->flags, F_IMMUTABLE)) {
return 0;
}
switch (o->material->id) {
case MT_STONE:
case MT_RUBBER:
case MT_WAX:
return 3;
case MT_GOLD:
case MT_SILVER:
case MT_PLASTIC:
case MT_METAL:
case MT_WIRE:
return 4;
default:
break;
}
return 0;
}
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, enum LFSIZE forcesize) {
flag_t *f;
f = hasflag(o->flags, F_IMPASSABLE);
if (f) {
enum LFSIZE lfsize;
enum LFSIZE blockmin, blockmax;
if (!lf && (forcesize == SZ_ANY)) return B_TRUE;
if (forcesize != SZ_ANY) {
lfsize = forcesize;
} else {
lfsize = getlfsize(lf);
}
blockmin = f->val[0];
blockmax = f->val[1];
if ((lfsize >= blockmin) && (lfsize <= blockmax)) {
// exception - if you're flying over it
if (getfeetheight(lf) >= blockmax) {
} else {
return B_TRUE;
}
}
}
if (lf) {
if (lf->race->raceclass->id == RC_UNDEAD) {
if (hasflagval(o->flags, F_REPELBLESSED, B_CURSED, NA, NA, NULL)) {
return B_TRUE;
}
}
}
return B_FALSE;
}
// 'Interacting' is using things without holding them.
//
// It's fine to interact with a computer, door or treasure chest.
// It's NOT fine to interact with a wand, even though it is operable.
//
int isinteractable(object_t *o) {
if (isoperable(o) && hasinteractableflags(o->flags)) {
return B_TRUE;
} else if (isdoor(o, NULL)) {
return B_TRUE;
}
return B_FALSE;
}
int ismagicalobtype(objecttype_t *ot) {
switch (ot->obclass->id) {
case OC_SCROLL:
switch (ot->id) {
case OT_SCR_NOTHING:
case OT_MAP:
// 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 (ot->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;
default:
break;
}
switch (ot->id) {
case OT_SHILLELAGH:
case OT_WIZARDSTAFF:
case OT_ARMOURTHORN:
case OT_PEACEPIPES:
return B_TRUE;
default: break;
}
return B_FALSE;
}
int ismagical(object_t *o) {
if (ismagicalobtype(o->type)) {
return B_TRUE;
}
if (o->type->obclass->id == OC_BOOK) {
if (hasflag(o->flags, F_LINKSPELL)) {
// ie. spellbooks
return B_TRUE;
}
}
if (hasflag(o->flags, F_HASBRAND)) {
return B_TRUE;
}
if (hasflag(o->flags, F_ENCHANTABLE) && hasflag(o->flags, F_BONUS)) {
return B_TRUE;
}
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 istempobpile(obpile_t *op) {
if ((op->owner == NULL) && (op->where == NULL) && (op->parentob == NULL)) {
return B_TRUE;
}
return B_FALSE;
}
int isorganicmat(enum MATERIAL mat) {
int organic = B_FALSE;
switch (mat) {
case MT_FLESH:
case MT_WOOD:
case MT_LEATHER:
case MT_PLANT:
organic = B_TRUE;
break;
default:
organic = B_FALSE;
break;
}
return organic;
}
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?
knowledge_t *istried(object_t *o) {
if (hasflag(o->flags, F_IDENTIFIED)) {
return B_FALSE;
}
return istriedot(o->type);
}
knowledge_t *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 k;
} else {
return NULL;
}
}
}
// no hidden name, so can't be "tried"
return NULL;
}
int isvalidoverridemat(enum MATERIAL mat) {
switch (mat) {
case MT_BONE:
case MT_GLASS:
case MT_SILVER:
return B_TRUE;
default: break;
}
return B_FALSE;
}
int isinroof(object_t *o) {
if (hasflagval(o->flags, F_PIT, D_UP, NA, NA, NULL) || (o->type->id == OT_GRATINGROOF)) {
return B_TRUE;
}
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;
if (isdamaged(o)) {
// splashes of liquid can combine even when 'damaged'
if (getmaterialstate(o->material->id) != MS_LIQUID) {
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 israwmeat(object_t *o) {
if (hasflag(o->flags, F_ISMEAT) && !hasflag(o->flags, F_PREPARED) && (o->type->id != OT_ROASTMEAT)) {
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) {
//int max;
flag_t *f;
if (hasflag(o->flags, F_TAINTED)) {
return B_TRUE;
}
if (!iscorpse(o)) return B_FALSE;
//getobhp(o, &max);
f = hasflag(o->flags, F_DECAY);
if (f && (f->val[0] >= 50)) {
// ie. food starts to be destroyed completely after 50 turns.
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 istwohandedfor(object_t *o, lifeform_t *lf) {
flag_t *f;
f = hasflag(o->flags, F_TWOHANDED);
if (f) {
// twohanded for everyone
if (f->val[0] <= 0) return B_TRUE;
if (!lf) return B_TRUE;
if (getlfsize(lf) <= f->val[0]) 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);
if (k->triedon) free(k->triedon);
// 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);
}
*/
affect_temperature(o, B_REMOVE);
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;
int i;
// free mem
if (om->prefix) free(om->prefix);
for (i = 0; i < om->naltprefix; i++) {
free(om->altprefix[i]);
}
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);
}
// returns # killed
int killobsofid(obpile_t *op, enum OBTYPE oid, int includecontainers) {
object_t *o,*nexto;
int count = 0;
for (o = op->first ; o ; o = nexto) {
nexto = o->next;
if (o->contents && includecontainers) {
count += killobsofid(o->contents, oid, includecontainers);
} else if (o->type->id == oid) {
killob(o);
count++;
continue;
}
}
return count;
}
// returns # killed
int killobswithflag(obpile_t *op, enum FLAG fid, int includecontainers) {
int count = 0;
object_t *o,*nexto;
for (o = op->first ; o ; o = nexto) {
nexto = o->next;
if (o->contents && includecontainers) {
count += killobswithflag(o->contents, fid, includecontainers);
} else if (hasflag(o->flags, fid)) {
killob(o);
count++;
continue;
}
}
return count;
}
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;
}
void obtodancing(lifeform_t *newlf, object_t *o) {
flag_t *f;
if (o->pile != newlf->pack) {
o = relinkob(o, newlf->pack);
}
if (!isequipped(o)) {
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_SIZE);
if (f) {
flag_t *f2;
f2 = lfhasflag(newlf, F_SIZE);
if (f2) f2->val[0] = f->val[0];
else addflag(newlf->flags, F_SIZE, f->val[0], NA, NA, NULL);
}
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);
}
}
// animate a weapon
lifeform_t *makeanimated(lifeform_t *lf, object_t *o, int level) {
cell_t *where;
lifeform_t *newlf;
where = getrandomadjcell(lf->cell, &ccwalkable, B_NOEXPAND);
if (!where) return NULL;
newlf = addlf(where, R_DANCINGWEAPON, level);
if (newlf) {
// remove existing weapon.
killallobs(newlf->pack);
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);
}
obtodancing(newlf, o);
}
if (newlf) {
petify(newlf, lf);
}
return newlf;
}
void makecool(object_t *o, int howmuch, int howlong) {
flag_t *f;
f = hasflag(o->flags, F_HOT);
if (f) {
int newlife,newpower;
newpower = f->val[0] - howmuch;
if (newpower <= 0) {
killflag(f);
return;
}
if (f->lifetime > 0) {
newlife = f->lifetime - howlong;
if (newlife <= 0) {
killflag(f);
return;
}
}
}
}
// returns true if something hapepned
int makeduller(object_t *o, int howmuch) {
char obname[BUFLEN];
int oldbonus,newbonus;
int rv = B_FALSE;
if (hasflag(o->flags, F_IMMUTABLE)) {
return 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;
}
int makedullermaybe(object_t *o, int howmuch) {
if (onein(3)) {
return makeduller(o, howmuch);
}
return B_FALSE;
}
void makehot(object_t *o, int howmuch, int howlong) {
flag_t *f;
int seen = B_FALSE;
f = hasflag(o->flags, F_HOT);
if (f) {
if (howmuch > f->val[0]) {
f->val[0] = howmuch;
}
if (f->lifetime < howlong) {
f->lifetime = howlong;
}
} else {
f = addtempflag(o->flags, F_HOT, howmuch, NA, NA, "1d2", howlong);
if (isequipped(o) && o->pile->owner) {
if (isplayer(o->pile->owner) || cansee(player, o->pile->owner)) {
seen = B_TRUE;
}
}
}
// you only know an object is hot if you notice the effects on the
// person wearing it.
if (seen) {
f->known = B_TRUE;
} else {
f->known = B_FALSE;
}
}
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);
}
}
// make objects of the given class & rarity be known
void makeknownobclass(enum OBCLASS ocid, enum RARITY rrlev) {
objecttype_t *ot;
// automatically make known all tech <= our skill level
for (ot = objecttype ; ot ; ot = ot->next) {
// if objectclass and rarity matches...
if (ot->obclass->id == ocid) {
enum RARITY thisrr;
thisrr = getavgrarity(ot->flags);
if ((thisrr <= rrlev) && !isknownot(ot)) {
//object_t *o;
// then make it known!
makeknown(ot->id);
/*
for (o = player->pack->first ; o ; o = o->next) {
if (o->type->id == ot->id) {
if (isplayer(lf)) {
char buf[BUFLEN];
getobname(o, buf, o->amt);
msgnocap("%c - %s", o->letter, buf);
}
// now confer effects...
giveobflags(lf, o, F_HOLDCONFER);
if (isactivated(o)) {
giveobflags(lf, o, F_ACTIVATECONFER);
}
if (isequipped(o)) {
giveobflags(lf, o, F_EQUIPCONFER);
}
}
}
*/
}
}
}
}
void makeopersound(cell_t *c, object_t *o) {
flag_t *df;
df = hasflag(o->flags, F_OPERSOUND);
if (df) {
noise(c, NULL, NC_OTHER, df->val[0], df->text, NULL);
}
}
void maketemporary(object_t *o, int howlong, char *obdietext) {
addflag(o->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_OBHP, howlong, howlong, NA, NULL);
addflag(o->flags, F_OBHPDRAIN, 1, DT_DIRECT, NA, NULL);
addflag(o->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_OBDIETEXT, B_TRUE, NA, NA, obdietext);
}
void maketried(enum OBTYPE otid, char *triedon) {
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) {
// test for B_TRIED too since we might be updating the text.
if (k->known == B_UNKNOWN) {
k->known = B_TRIED;
if (triedon) {
k->triedon = strdup(triedon);
}
}
}
}
}
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);
}
killflagsofid(o->flags, F_ONFIRE);
killflagsofid(o->flags, F_HOT);
if (willrust(o)) {
int rustchance = 25;
f = hasflag(o->flags, F_RUSTED);
if (f) rustchance += 25;
if (pctchance(rustchance)) {
if (amt < R_RUSTY) amt = R_RUSTY;
if (amt > R_TRUSTY) amt = R_TRUSTY;
if (f) {
if (f->val[0] < R_TRUSTY) {
// make more rusty
f->val[0] += amt;
}
} else {
// rust
if (haslos(player, loc) && !isdead(player)) {
msg("^%c%s rust%s.",
(o->pile->owner && isplayer(o->pile->owner)) ? 'b' : 'n',
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);
}
// water washes off bloodstain and stench
if (hasobmod(o, findobmod(OM_BLOODSTAINED)) ){
killflagsofid(o->flags, F_SCARY);
}
killflagsofid(o->flags, F_STENCH);
}
}
}
void modifybetterwepdam(object_t *o, lifeform_t *owner, int *dam) {
flag_t *f;
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0,i;
if (hasflag(o->flags, F_HASBRAND)) {
*dam *= 3;
}
if (hasflag(o->flags, F_ONFIRE)) {
*dam += 8;
}
getflags(o->flags, retflag, &nretflags, F_EXTRADAM, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (f->val[2] == NA) {
*dam += real_roll(f->text, B_TRUE);
} else {
*dam += f->val[2];
}
}
// prefer weapons "...of xxxslaying" which match our targets
if (owner) {
f = hasflag(o->flags, F_RACESLAY);
if (f) {
lifeform_t *target;
target = gettargetlf(owner);
if (target && (getraceclass(target) == f->val[0])) {
*dam *= 3;
}
}
}
}
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;
int pleasethiefgod = 0;
lifeform_t *robbedlf = NULL;
flag_t *f;
reason = E_OK;
if (!obfits(src, dst)) {
reason = E_NOSPACE;
return NULL;
}
if (dst->owner && isplayer(dst->owner) && hasflag(src->flags, F_UNTOUCHED)) {
// values here should be half as much as if you
// were to sacrifice it.
if (src->type->id == OT_GOLD) {
pleasethiefgod = (getobvalue(src) / 4);
} else if (hasflag(src->flags, F_GEM)) {
pleasethiefgod = (getobvalue(src) / 100);
}
}
touch_battle_spoils(src);
// object being taken from an unconscious lf?
if (src->pile->owner && (isunconscious(src->pile->owner) || isasleep(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;
}
}
// is it a godstone moving? if so, all other godstones will now vanish
if (src->type->obclass->id == OC_GODSTONE) {
object_t *oo,*nextoo;
for (oo = src->pile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
if ((oo != src) && (oo->type->obclass->id == OC_GODSTONE)) {
removeob(oo, ALL);
}
}
}
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) {
killflagsofid(o->flags, F_SECONDARY);
if (dst->owner && isplayer(dst->owner) && isblessknown(o)) {
o->blessknown = B_TRUE;
}
if (robbedlf) {
if (!lfhasflag(robbedlf, F_WASROBBED)) {
addflag(robbedlf->flags, F_WASROBBED, B_TRUE, NA, NA, NULL);
}
// if you were just asleep, you might be awoken.
if (!isunconscious(robbedlf) && dst->owner && !lfhasflag(robbedlf, F_WOKENBY)) {
if (!skillcheck(dst->owner, SC_STEALTH, 65, 0)) { //easy check since target is asleep!
addflag(robbedlf->flags, F_WOKENBY, dst->owner->id, NA, NA, "^WYou awaken to someone rummaging through your pack!^n");
}
}
}
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);
}
*/
if (!o) return NULL;
// 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);
}
}
*/
if (lfhasflag(dst->owner, F_HOLYTOUCH) && (o->blessed == B_UNCURSED)) {
if (isweapon(o) || isarmour(o)) {
blessob(o);
}
}
// did this make us burdened?
if (isplayer(dst->owner)) {
checkburdened(dst->owner, preburdened);
}
// sometime spicking up something will it id
if (isplayer(dst->owner)) {
if (lfhasflag(player, F_DETECTMAGIC)) {
// have detect magic and picking up non-magical object which doesn't show up as magic?
if (isknown(o) && !isidentified(o) && !ismagicalobtype(o->type) && !ismagical(o)) {
identify(o);
}
}
}
if (pleasethiefgod) {
pleasegodmaybe(R_GODTHIEVES, pleasethiefgod);
}
// 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;
cell_t *loc;
lifeform_t *killer = NULL;
f = hasflag(o->flags, F_LASTDAMTYPE);
if (f && (f->val[1] != NA)) {
killer = findlf(NULL, f->val[1]);
}
loc = getoblocation(o);
o->dying = B_TRUE;
// handle object conversion
if (!doobdieconvert(o, B_TRUE)) {
char desc[BUFLEN];
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
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], NULL);
return;
}
} else {
explodeob(o, f, f->val[1], NULL);
return;
}
}
// spell?
f = hasflag(o->flags, F_SPELLCLOUDONDEATH);
if (f) {
cell_t *where;
int power;
char obname[BUFLEN],seebuf[BUFLEN],noseebuf[BUFLEN];
char buf[BUFLEN],*p;
flag_t *glyphflag;
glyph_t cloudglyph;
where = getoblocation(o);
getobname(o, obname, 1);
p = readuntil(seebuf, f->text, '^');
p = readuntil(noseebuf, p, '^');
p = readuntil(buf, p, '^');
power = atoi(buf);
glyphflag = hasflag(o->flags, F_SPELLCLOUDGLYPH);
if (glyphflag) {
cloudglyph.colour = glyphflag->val[0];
cloudglyph.ch = glyphflag->val[1];
} else {
cloudglyph.colour = C_RANDOM;
cloudglyph.ch = UNI_SHADELIGHT;
}
if (f->val[2] == B_IFACTIVATED) {
if (isactivated(o)) {
spellcloud(where, f->val[1], DT_ORTH, cloudglyph.ch, cloudglyph.colour, f->val[0], power, B_TRUE,
seebuf, noseebuf, B_FALSE, o, B_INCLUDECENTRE);
removeob(o, o->amt);
return;
}
} else {
spellcloud(where, f->val[1], DT_ORTH, cloudglyph.ch, cloudglyph.colour, f->val[0], power, B_TRUE,
seebuf, noseebuf, B_FALSE, o, B_INCLUDECENTRE);
removeob(o, o->amt);
return;
}
}
if (hasflag(o->flags, F_CONTAINER)) {
object_t *oo, *nextoo;
// dump object's contents into parent container
for (oo = o->contents->first ; oo ; oo = nextoo) {
nextoo = oo->next;
moveob(oo, o->pile, ALL);
}
}
// flashes?
f = hasflag(o->flags, F_FLASHONDEATH);
if (f) {
if (f->val[2] == B_IFACTIVATED) {
if (isactivated(o)) {
brightflash(loc,f->val[0], NULL);
}
} else {
brightflash(loc,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);
}
} else {
char bigbreak[BUFLEN],smallbreak[BUFLEN];
// maybe create broken object based on material
getbreakob(o, bigbreak, smallbreak);
if (strlen(smallbreak)) {
addob(loc->obpile, smallbreak);
}
if (strlen(bigbreak)) {
addob(loc->obpile, bigbreak);
}
}
}
if (loc) {
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0,i;
getflags(o->flags, retflag, &nretflags, F_LIFEOBFOR, F_NONE);
for (i = 0; i < nretflags; i++) {
lifeform_t *l;
l = findlf(loc->map, retflag[i]->val[0]);
if (l) {
sayphrase(l, SP_LIFEOB_DESTROYED, SV_CAR, NA, NULL, NULL);
}
}
}
if (killer &&
(o->type->obclass->id == OC_FLORA) &&
(loc->map->region->rtype->id == BH_WOODS)) {
magicwoods_angry(killer);
}
if (killer && isplayer(killer)) {
if (oblastdamtype(o) == DT_FIRE) {
pleasegodmaybe(R_GODFIRE, 5);
} else {
pleasegodmaybe(R_GODFIRE, 2);
}
}
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;
}
int obgoesinbones(object_t *o) {
flag_t *retflag[MAXCANDIDATES];
int nretflags = 0;
getflags(o->flags, retflag, &nretflags, F_CLIMBABLE, F_TRAIL, F_UNIQUE, F_NONE);
if (nretflags) return B_FALSE;
return B_TRUE;
}
int obhpknown(object_t *o) {
int showfullhp = B_FALSE;
if (isarmour(o) && getskill(player, SK_ARMOUR)) {
showfullhp = B_TRUE;
} else if (isshield(o) && getskill(player, SK_SHIELDS)) {
showfullhp = B_TRUE;
} else if ((o->type->material->id == MT_METAL) && getskill(player, SK_METALWORK)) {
showfullhp = B_TRUE;
} else if ( ((o->type->material->id == MT_LEATHER) || (o->type->material->id == MT_CLOTH)) &&
getskill(player, SK_SEWING)) {
showfullhp = B_TRUE;
}
return showfullhp;
}
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_NONE) {
if (br->bp == BP_WEAPON) {
if (ot->obclass->id != OC_WEAPON) {
return B_FALSE;
}
} else {
// differentiate shields from guns using f_onlywithwepskill below.
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;
}
}
if (hasflag(br->flags, F_ONLYFOROBCLASS)) {
if (!hasflagval(br->flags, F_ONLYFOROBCLASS, ot->obclass->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;
}
// if val is B_ONEOF, then at least one of the conditions with ONEOF must match.
int obmeets(object_t *o, condset_t *cs) {
int i,n,found;
struct {
enum CELLCONDITION cond;
int got;
} oneof [MAXCANDIDATES];
int noneof = 0;
if (!cs) return B_TRUE;
for (i = 0; i < cs->nconds; i++ ){
if (cs->val[i] == B_ONEOF) {
found = B_FALSE;
for (n = 0; n < noneof; n++) {
if (oneof[n].cond == cs->cond[i]) {
found = B_TRUE;
}
}
if (!found) {
oneof[noneof].cond = cs->cond[i];
oneof[noneof].got = 0;
}
}
if (obmeetscondition(o, cs->cond[i], cs->arg[i], cs->val[i])) {
if (cs->val[i] == B_ONEOF) {
found = B_FALSE;
for (n = 0; n < noneof; n++) {
if (oneof[n].cond == cs->cond[i]) {
oneof[n].got = B_TRUE;
found = B_TRUE;
}
}
assert(found);
}
} else {
if (cs->val[i] != B_ONEOF) return B_FALSE;
}
}
if (noneof) {
for (n = 0; n < noneof; n++) {
if (oneof[n].got == B_FALSE) return B_FALSE;
}
}
return B_TRUE;
}
int obmeetscondition(object_t *o, enum CELLCONDITION cond, int arg, int value) {
flag_t *f;
int ok = B_FALSE,n;
skill_t *sk = NULL;
int check = B_FALSE;
assert(o);
switch (cond) {
case CC_ARMOUR:
if (o->type->obclass->id == OC_ARMOUR) check = B_TRUE;
break;
case CC_DAMAGED:
if (isdamaged(o)) check = B_TRUE;
break;
case CC_DAMTYPE:
if (hasflagval(o->flags, F_DAM, arg, NA, NA, NULL)) check = B_TRUE;
break;
case CC_DRINKABLE:
if (isdrinkable(o)) check = B_TRUE;
break;
case CC_EDIBLE:
if (isedible(o)) check = B_TRUE;
break;
case CC_EQUIPPED:
if (hasflag(o->flags, F_EQUIPPED)) check = B_TRUE;
break;
case CC_KNOWN:
if (isknown(o)) check = B_TRUE;
break;
case CC_HASFLAG:
if (hasflag(o->flags, arg)) check = B_TRUE;
break;
case CC_HASSIZE:
if (getobsize(o) == arg) check = B_TRUE;
break;
case CC_IDENTIFIED:
if (isidentified(o)) check = B_TRUE;
break;
case CC_MINAR:
f = hasflag(o->flags, F_ARMOURRATING);
if (f) {
if (f->val[0] >= arg) {
check = B_TRUE;
}
}
break;
case CC_MAXAR:
f = hasflag(o->flags, F_ARMOURRATING);
if (f) {
n = f->val[0];
} else {
n = 0;
}
if (n <= arg) {
check = B_TRUE;
}
break;
case CC_MINDR:
f = hasflag(o->flags, F_DAM);
if (f) {
if (f->val[1] >= arg) {
check = B_TRUE;
}
}
break;
case CC_MAXDR:
f = hasflag(o->flags, F_DAM);
if (f) {
if (f->val[1] <= arg) {
check = B_TRUE;
}
}
break;
case CC_MAXSIZE:
if (getobsize(o) <= arg) check = B_TRUE;
break;
case CC_OBCLASS:
if (o->type->obclass->id == arg) check = B_TRUE;
break;
case CC_OPERABLE:
if (isoperable(o)) check = B_TRUE;
break;
case CC_INTERACTABLE:
if (isinteractable(o)) check = B_TRUE;
break;
case CC_POURABLE:
if (ispourable(o)) check = B_TRUE;
break;
case CC_READABLE:
if (isreadable(o)) check = B_TRUE;
break;
case CC_SPECIFIED:
// does retlist contain this?
for (n = 0; n < nretobs; n++) {
if (retobs[n] == o) {
check = B_TRUE;
break;
}
}
break;
case CC_WEARABLE:
if (iswearable(o)) check = B_TRUE;
break;
case CC_WEILDABLE:
if (isweapon(o) || isfirearm(o)) check = B_TRUE;
break;
case CC_WEAPON:
if (isweapon(o)) check = B_TRUE;
break;
case CC_WEPSK:
sk = getobskill(o->flags);
if (sk && (sk->id == arg)) check = B_TRUE;
break;
case CC_NONE: ok = B_TRUE; break;
default: break;
}
if (value && check) {
ok = B_TRUE;
} else if (!value && !check) {
ok = B_TRUE;
}
return ok;
}
int otmeets(objecttype_t *ot, condset_t *cs) {
int i,n,found;
struct {
enum CELLCONDITION cond;
int got;
} oneof [MAXCANDIDATES];
int noneof = 0;
if (!cs) return B_TRUE;
for (i = 0; i < cs->nconds; i++ ){
if (cs->val[i] == B_ONEOF) {
found = B_FALSE;
for (n = 0; n < noneof; n++) {
if (oneof[n].cond == cs->cond[i]) {
found = B_TRUE;
}
}
if (!found) {
oneof[noneof].cond = cs->cond[i];
oneof[noneof].got = 0;
}
}
if (otmeetscondition(ot, cs->cond[i], cs->arg[i], cs->val[i])) {
if (cs->val[i] == B_ONEOF) {
found = B_FALSE;
for (n = 0; n < noneof; n++) {
if (oneof[n].cond == cs->cond[i]) {
oneof[n].got = B_TRUE;
found = B_TRUE;
}
}
assert(found);
}
} else {
if (cs->val[i] != B_ONEOF) return B_FALSE;
}
}
if (noneof) {
for (n = 0; n < noneof; n++) {
if (oneof[n].got == B_FALSE) return B_FALSE;
}
}
return B_TRUE;
}
int otmeetscondition(objecttype_t *ot, enum CELLCONDITION cond, int arg, int value) {
int ok = B_FALSE;
int check = B_FALSE;
int n;
skill_t *sk = NULL;
flag_t *f;
assert(ot);
switch (cond) {
case CC_ARMOUR:
if (ot->obclass->id == OC_ARMOUR) check = B_TRUE;
break;
case CC_DAMAGED:
assert("CC_DAMAGED not applicable to objecttypes");
break;
case CC_DAMTYPE:
if (hasflagval(ot->flags, F_DAM, arg, NA, NA, NULL)) check = B_TRUE;
break;
case CC_DRINKABLE:
if ((ot->obclass->id == OC_POTION) || hasflag(ot->flags, F_DRINKABLE)) check = B_TRUE;
break;
case CC_EDIBLE:
if (hasflag(ot->flags, F_EDIBLE)) check = B_TRUE;
break;
case CC_EQUIPPED:
assert("CC_EQUIPPED not applicable to objecttypes");
break;
case CC_KNOWN:
if (isknownot(ot)) check = B_TRUE;
break;
case CC_HASFLAG:
if (hasflag(ot->flags, arg)) check = B_TRUE;
break;
case CC_HASSIZE:
if (ot->size == arg) check = B_TRUE;
break;
case CC_IDENTIFIED:
assert("CC_IDENTIFIED not applicable to objecttypes");
break;
case CC_MINAR:
f = hasflag(ot->flags, F_ARMOURRATING);
if (f) {
if (f->val[0] >= arg) {
check = B_TRUE;
}
}
break;
case CC_MAXAR:
f = hasflag(ot->flags, F_ARMOURRATING);
if (f) {
n = f->val[0];
} else {
n = 0;
}
if (n <= arg) {
check = B_TRUE;
}
break;
case CC_MINDR:
f = hasflag(ot->flags, F_DAM);
if (f) {
if (f->val[1] >= arg) {
check = B_TRUE;
}
}
break;
case CC_MAXDR:
f = hasflag(ot->flags, F_DAM);
if (f) {
if (f->val[1] <= arg) {
check = B_TRUE;
}
}
break;
case CC_MAXSIZE:
if (ot->size <= arg) check = B_TRUE;
break;
case CC_OBCLASS:
if (ot->obclass->id == arg) check = B_TRUE;
break;
case CC_OPERABLE:
if (hasflag(ot->flags, F_OPERABLE)) check = B_TRUE;
break;
case CC_INTERACTABLE:
if (hasinteractableflags(ot->flags)) check = B_TRUE;
break;
case CC_POURABLE:
if (hasflag(ot->flags, F_POURABLE)) check = B_TRUE;
break;
case CC_READABLE:
if ((ot->obclass->id == OC_SCROLL) || (ot->obclass->id == OC_BOOK)) check = B_TRUE;
break;
case CC_SPECIFIED:
assert("CC_SPECIFIED not applicable to objecttypes");
break;
case CC_WEARABLE:
if (hasflag(ot->flags, F_GOESON)) check = B_TRUE;
break;
case CC_WEAPON:
if ((ot->obclass->id == OC_WEAPON) || hasflag(ot->flags, F_DAM)) check = B_TRUE;
break;
case CC_WEPSK:
sk = getobskill(ot->flags);
if (sk && (sk->id == arg)) check = B_TRUE;
break;
case CC_NONE: ok = B_TRUE; break;
default: break;
}
if (value && check) {
ok = B_TRUE;
} else if (!value && !check) {
ok = B_TRUE;
}
return ok;
}
/*
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;
if (isdeadob(o)) return B_FALSE;
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, NULL) && (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);
uphole = hasobwithflagval(belowcell->obpile, F_MAPLINK, NA, NA, NA, oid);
if (!uphole) {
uphole = hasobwithflagval(belowcell->obpile, F_MAPLINK, NA, c->x, c->y, NULL);
}
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;
//neither do gasses
if (getmaterialstate(oo->material->id) == MS_GAS) 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 (gamemode == GM_GAMESTARTED) {
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 (gamemode == GM_GAMESTARTED) {
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 ((gamemode == GM_GAMESTARTED) && 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;
int wastech = B_FALSE;
enum ERROR why;
if (!lfhasflag(lf, F_HUMANOID) || !hasbp(lf, BP_HANDS)) {
if (!hasflag(o->flags, F_OPERWITHOUTHANDS)) {
// only humanoids can zap things
if (isplayer(lf)) {
msg("You lack the manual dexterity to operate this.");
}
return B_TRUE;
}
}
getobname(o, obname, 1);
if (!canoperate(lf, o, &why)) {
if (isplayer(lf)) {
switch (why) {
case E_NOHANDS: msg("You lack the manual dexterity to operate %s.", obname); break;
case E_RAGE: msg("You are too enraged to operate anything!"); break;
case E_STUNNED: msg("You cannot operate anything while stunned."); break;
case E_NOSKILL: msg("You lack the skill to operate %s.", obname); break;
default: msg("For some reason, you can't operate %s.", obname); break;
}
return B_TRUE;
}
}
if ((isplayer(lf)) || cansee(player, lf)) {
playercansee = B_TRUE;
} else {
playercansee = B_FALSE;
}
if (o->type->obclass->id == OC_TECH) {
wastech = B_TRUE;
}
// 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 (confirmknowntraps(o)) {
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;
flag_t *f2;
// 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];
enum LOFTYPE askltype;
enum LOFTYPE ltype;
f2 = hasflag(o->flags, F_LINKSPELL);
if (f2) {
ltype = getspellloftype(f2->val[0]);
} else {
ltype = LOF_NEED;
}
if (isknown(o)) {
askltype = ltype;
} else {
// default to needing lof, lfs don't count
askltype = LOF_WALLSTOP;
}
snprintf(subprompt, BUFLEN, "%s->Aim->", obname);
if (strlen(f->text) > 0) {
where = askcoords(f->text, subprompt, ttype, lf, range, askltype, B_TRUE);
} else {
snprintf(buf, BUFLEN, "Where will you aim %s?",obname);
where = askcoords(buf, subprompt, ttype, lf, range, askltype, B_TRUE);
if (!haslos(lf, where)) {
msg("You can't see there!");
return B_TRUE;
}
}
// now adjust target cell for line-of-fire....
if (ltype != askltype) {
cell_t *newcell;
if (ltype != LOF_DONTNEED) {
if (!haslof(lf->cell, where, ltype, &newcell)) {
where = newcell;
if (!where) {
msg("For some reason you can't aim 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);
if (dir == D_NONE) {
where = lf->cell;
} else {
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));
more();
} else if (slev && chargesknown(o)) {
if (f) f->known = B_TRUE;
if (chargesleft == 1) {
msg("Your %s only has one charge remaining.", noprefix(obname));
more();
} else {
msg("Your %s now has %d charges remaining.", noprefix(obname), chargesleft);
more();
}
} 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));
more();
}
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));
more();
}
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, NULL);
// trapped?
if (triggerattachedtraps(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;
f = hasflag(o->flags, F_JAMMED);
if (f) {
int openit = B_FALSE;
if (f->val[1] != B_TRUE) { // not known yet
msg("The %s seems to be jammed.", noprefix(obname));
f->val[1] = B_TRUE;
} else {
// try to force
openit = unjam(lf, o);
}
if (!openit) {
return B_TRUE;
}
}
if (hasflag(o->flags, F_LOCKED)) {
msg("The %s seems to be locked.", noprefix(obname));
return B_TRUE;
}
// monster inside?
f = hasflag(o->flags, F_LFINSIDE);
if (f) {
char theob[BUFLEN];
cell_t *where;
lifeform_t *mon = NULL;
if (o->pile->owner) {
char ownername[BUFLEN];
getlfname(o->pile->owner,ownername);
sprintf(theob, "%s%s %s",ownername,getpossessive(ownername),
noprefix(obname));
} else {
sprintf(theob, "the %s", noprefix(obname));
}
where = getrandomadjcell(lf->cell, &ccwalkable, B_ALLOWEXPAND);
if (where) {
mon = addmonster(where, f->val[0], NULL, B_TRUE, 1, B_FALSE, B_NOEXTRA, NULL);
}
if (mon) {
char monname[BUFLEN];
getlfnamea(mon, monname);
msg("^w%s leaps out of %s!", monname, theob);
// it gets a free attack
if (isadjacent(mon->cell, player->cell)) {
attackcell(mon, player->cell, B_TRUE);
}
} else {
msg("^vSomething swipes at you as you open %s!", theob);
}
} else {
snprintf(buf, BUFLEN, "Looting %s. Will you:",obname);
addflagifneeded(o->flags, F_BEENOPENED, B_TRUE, NA, NA, NULL);
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);
}
if (haschoice(&prompt, 'i') && haschoice(&prompt, 'o')) {
snprintf(buf, BUFLEN, "Both");
addchoice(&prompt, 'b', buf, NULL, NULL, NULL);
snprintf(buf, BUFLEN, "Neither");
addchoice(&prompt, 'n', buf, NULL, NULL, NULL);
} else {
snprintf(buf, BUFLEN, "Do nothing");
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 {
condset_t cs;
initcondv(&cs, CC_SPECIFIED, B_TRUE, NA,
CC_NONE);
snprintf(buf, BUFLEN, "Load %s with what ammo",obname);
oo = askobject(lf->pack, buf, NULL, NULL, 'l', &cs, B_FALSE);
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, o);
// 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, &ccwalkable, B_ALLOWEXPAND);
addmonster(where, R_BUTTERFLY, NULL, B_FALSE, rnd(10,20), B_FALSE, B_NOEXTRA, 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, NULL);
break;
case 2: // animate dead
dospelleffects(lf, OT_S_ANIMATEDEAD, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 3: // blindness
dospelleffects(lf, OT_S_BLINDNESS, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 4: // levitate
dospelleffects(lf, OT_S_LEVITATION, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 5: // dispersal
dospelleffects(lf, OT_S_DISPERSAL, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 6: // flash
dospelleffects(lf, OT_S_FLASH, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 7: // light
dospelleffects(lf, OT_S_LIGHT, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 8: // heal
dospelleffects(lf, OT_S_HEALING, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 9: // minor heal
dospelleffects(lf, OT_S_HEALINGMIN, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 10: // invis
dospelleffects(lf, OT_S_INVISIBILITY, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 11: // haste
dospelleffects(lf, OT_S_HASTE, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 12: // pull
dospelleffects(lf, OT_S_SUCK, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 13: // blast
dospelleffects(lf, OT_S_AIRBLAST, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 14: // slow
dospelleffects(lf, OT_S_SLOW, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 15: // sleep
dospelleffects(lf, OT_S_SLEEP, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 16: // gas
dospelleffects(lf, OT_S_CLOUDKILL, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 17: // dark
dospelleffects(lf, OT_S_DARKNESS, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 18: // stone
dospelleffects(lf, OT_S_PETRIFY, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 19: // fireball
dospelleffects(lf, OT_S_FIREBALL, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 20: // poly
dospelleffects(lf, OT_S_POLYMORPH, power, where ? where->lf : NULL, NULL, where, B_UNCURSED, &willid, B_FALSE, NULL);
break;
case 21: // teleport
dospelleffects(lf, OT_S_TELEPORT, power, lf , NULL, lf->cell, B_UNCURSED, &willid, B_FALSE, NULL);
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;
}
}
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_NOPREMODS, B_CONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
msg("This is %s!",obname);
}
}
}
// bjorn doesn't like magic, even from wands
angergodmaybe(R_GODBATTLE, 25, GA_SPELL);
// lumara does.
pleasegodmaybe(R_GODMAGIC, 2);
} else if (hasflag(o->flags, F_VAULTKEY)) {
if (isplayer(lf)) {
object_t *stairs;
// vault stairs here?
stairs = hasob(lf->cell->obpile, OT_VSTAIRSDOWN);
if (stairs && hasflag(stairs->flags, F_LOCKED)) {
char stairsname[BUFLEN];
getobname(stairs, stairsname, 1);
msg("You unlock %s with your %s.", stairsname, noprefix(obname));
more();
killflagsofid(stairs->flags, F_LOCKED);
msg("Your %s vanishes!", noprefix(obname));
removeob(o, ALL);
} else {
msg("Your %s doesn't fit anything here.", noprefix(obname));
}
} else {
return B_TRUE;
}
// 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, o);
// announce
if (!seen) {
noise(where, NULL, NC_OTHER, SV_WHISPER, "something burning.", NULL);
}
} else if (o->type->id == OT_COMPUTER) {
if (isplayer(lf)) {
enum SKILLLEVEL slev;
char allchoices[BUFLEN],ch;
int i;
strcpy(allchoices, "moldip");
slev = getskill(lf, SK_TECHUSAGE);
if (slev >= PR_ADEPT) {
msg("You input a login sequence to %s.", obname);
} else {
msg("You poke a few random buttons on %s.",obname);
}
if (getcharges(o) <= 0) {
nothinghappens();
} else {
more();
// populate choices...
initprompt(&prompt, "WELCOME TO NXS-OS v8.44. ENTER COMMAND CODE:");
for (i = 0;i < slev; i++) {
int idx;
char *p,cmdtext[BUFLEN];
// pick one
idx = rnd(0,strlen(allchoices)-1);
ch = allchoices[idx];
// remove it from the list
for (p = allchoices + idx; *(p) ; p++) {
*p = *(p+1);
}
// add it to the prompt
switch (ch) {
case 'm': strcpy(cmdtext, "MAP AREA"); break;
case 'o': strcpy(cmdtext, "OBJECT SCAN"); break;
case 'l': strcpy(cmdtext, "LIFEFORM SCAN"); break;
case 'd': strcpy(cmdtext, "DESTRUCTIVE SANITISATION FUNCTION"); break;
case 'i': strcpy(cmdtext, "IDENTIFICATION ROUTINE"); break;
case 'p': strcpy(cmdtext, "PHYSICAL SECURITY BYPASS"); break;
default: strcpy(cmdtext, "UNKNOWN COMMAND"); break;
}
addchoice(&prompt, ch, cmdtext, NULL, NULL, NULL);
}
ch = '\0';
if (prompt.nchoices == 0) {
msg("Nothing seems to happen.");
} else if (prompt.nchoices == 1) {
// it just happens.
ch = prompt.choice[0].ch;
} else {
// you can pick one
addchoice(&prompt, '-', "LOGOUT", NULL, NULL, NULL);
prompt.maycancel = B_TRUE;
ch = getchoice(&prompt);
}
if ((ch == '\0') || (ch == '-')) {
msg("You log off %s.", obname);
} else {
int n,x,y;
switch (ch) {
case 'm':
dospelleffects(NULL, OT_S_MAPPING, 5, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, o);
dospelleffects(NULL, OT_S_REVEALHIDDEN, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, o);
msg("\"AREA MAPPING COMPLETE.\"");
break;
case 'o':
// locate all objects on the level
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 && countnoncosmeticobs(c->obpile, B_FALSE, B_FALSE)) {
setcellknown(c, PR_MASTER);
}
}
}
setlosdirty(lf);
msg("\"OBJECT SCAN COMPLETE.\"");
break;
case 'l':
// locate all objects on the level
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->known = B_TRUE;
c->knownglyph = *(getlfglyph(c->lf));
}
}
}
setlosdirty(lf);
msg("\"LIFEFORM SCAN COMPLETE.\"");
break;
case 'd':
if (slev >= PR_ADEPT) {
char ch2;
ch2 = askchar("SET DELAY TIMER (0-9):", "0123456789", "3", B_FALSE, B_FALSE);
// ie. typing '0' sets a value of 1.
// ie. typing '9' sets a value of 10.
n = (ch2 - '0')+1;
} else {
n = rnd(2,6);
}
msg("\"DESTRUCTIVE SANITISATION INITIATED. PLEASE VACATE AREA.\"");
addflag(o->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL);
addflag(o->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
addflag(o->flags, F_EXPLODEONDEATH, NA, 1, NA, "10d2");
addflag(o->flags, F_OBHP, n, n, NA, NULL);
addflag(lastot->flags, F_MAKESNOISE, 33, SV_SHOUT, NC_DANGER, "something sparking.");
break;
case 'i':
msg("\"SCAN & IDENTIFY ROUTINE INITIATED.\"");
more();
dospelleffects(NULL, OT_S_IDENTIFY, 10, lf, NULL, NULL, B_UNCURSED, NULL, B_TRUE, o);
break;
case 'p':
// unlock everything on the level
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) {
object_t *oo;
for (oo = c->obpile->first ;oo ; oo = oo->next) {
if (killflagsofid(oo->flags, F_LOCKED)) {
noise(c, NULL, NC_OTHER, SV_TALK, "a loud 'click'.", NULL);
}
}
}
}
}
msg("\"PHYSICAL SECURITY BYPASS COMPLETE.\"");
break;
default:
msg("\"not implemented yet.\"");
break;
}
// chance of computer turning off...
if (usecharge(o) == 0) {
msg("\"ALERT - UNAUTHORISED ACCESS. SHUTTING DOWN.\"");
}
}
} // end if has charges
} else {
return B_TRUE;
}
} else if (o->type->id == OT_ENERGYPACK) {
object_t *targob;
if (isplayer(lf)) {
condset_t cs;
// ask for an object
initcondv(&cs, CC_HASFLAG, B_TRUE, F_REPLENISHABLE,
CC_HASFLAG, B_TRUE, F_TECHLEVEL,
CC_NONE);
targob = doaskobject(lf->pack, "Recharge which object", NULL, NULL, B_TRUE, B_TRUE, B_FALSE, '^', NULL, SA_NONE, &cs, B_FALSE);
if (targob) {
f = hasflag(targob->flags, F_CHARGES);
if (f) {
char obname[BUFLEN];
getobname(targob, obname, 1);
f->val[0] = f->val[1];
msg("^gYour %s is now fully charged.", noprefix(obname));
// energy pack vanishes.
getobname(o, obname, 1);
msg("%s crumbles to dust.", obname);
removeob(o, ALL);
}
} else {
// cancel
msg("Cancelled.");
return B_TRUE;
}
} else {
return B_TRUE;
}
} else if (o->type->obclass->id == OC_GODSTONE) {
if (isfullycharged(o)) {
int i;
object_t *oo;
cell_t *c;
// 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_GODSTONE_BATTLE:
// bless weapon
oo = getweapon(lf);
if (oo && (oo->blessed == B_UNCURSED)) {
blessob(oo);
}
// uncurse wep/arm
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (isequipped(oo) && iscursed(oo)) {
if (isweapon(oo) || isarmour(oo)) {
uncurseob(oo, NULL);
}
}
}
// haste
dospelleffects(NULL, OT_S_HASTE, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE, o);
// everyone in lof...
for (i = 0; i < lf->cell->map->w * lf->cell->map->h; i++) {
cell_t *c;
c = lf->cell->map->cell[i];
if (c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_WALLSTOP, NULL)) {
// nullify
dospelleffects(NULL, OT_S_NULLIFY, 10, c->lf, NULL, c->lf->cell, B_BLESSED, NULL, B_TRUE, o);
}
}
// summon 3 warriors
for (i = 0;i < 3; i++) {
// find a race which can be a warrior
race_t *poss[MAXCANDIDATES],*r;
int nposs = 0;
for (r = firstrace ; r ; r = r->next) {
if (hasflagval(r->flags, F_STARTJOB, NA, J_WARRIOR, NA, NULL)) {
poss[nposs++] = r;
}
}
if (nposs) {
cell_t *c;
char wname[BUFLEN];
r = poss[rnd(0,nposs-1)];
sprintf(wname, "%s warrior", r->name);
c = real_getrandomadjcell(lf->cell, &ccwalkable, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, lf);
if (c) {
summonmonster(lf, c, R_SPECIFIED, wname, rnd(50,90), B_TRUE);
}
}
}
break;
case OT_GODSTONE_PURITY:
// 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 Purity!");
} else if (cansee(player, c->lf)) {
char lfname[BUFLEN];
getlfname(c->lf, lfname);
msg("%s is blasted with the power of Purity!", lfname);
}
}
}
}
*/
for (i = 0; i < lf->cell->map->w * lf->cell->map->h; i++) {
cell_t *c;
c = lf->cell->map->cell[i];
if (c->lf && haslof(lf->cell, c, LOF_WALLSTOP, NULL)) {
// announce
if (isplayer(c->lf)) {
msg("You are blasted with the power of Purity!");
} else if (cansee(player, c->lf)) {
char lfname[BUFLEN];
getlfname(c->lf, lfname);
msg("%s is blasted with the power of Purity!", lfname);
}
// revert polymorphs
if (lfhasflag(c->lf, F_ORIGRACE)) {
dospelleffects(NULL, OT_A_POLYREVERT, 1,
c->lf, NULL, c->lf->cell, B_BLESSED, NULL, B_TRUE, o);
}
// fix injuries ("deformities")
killflagsofid(c->lf->flags, F_INJURY);
// destroy undead
if (isundead(c->lf)) {
losehp(c->lf, 50, DT_DIRECT, lf, "the Godstone of Purity");
}
}
}
break;
case OT_GODSTONE_DEATH:
dospelleffects(lf, OT_S_INFINITEDEATH, 10, lf, NULL, lf->cell, B_BLESSED, NULL, B_TRUE, o);
break;
case OT_GODSTONE_DESTRUCTION: // rage
// everyone in lof gets f_rage, and hates everything
for (i = 0; i < lf->cell->map->w * lf->cell->map->h; i++) {
cell_t *c;
c = lf->cell->map->cell[i];
if (c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_WALLSTOP, NULL)) {
int howlong = 50;
// create fire
addobfast(c->obpile, OT_FIRELARGE);
// enrage
enrage(c->lf, howlong);
}
}
break;
case OT_GODSTONE_LIFE: // life
// everything in sight is restored
// all corpses in sight are revived
// everyone in lof gets f_rage, and hates everything
for (i = 0; i < lf->cell->map->w * lf->cell->map->h; i++) {
cell_t *c;
c = lf->cell->map->cell[i];
if (haslof(lf->cell, c, LOF_WALLSTOP, NULL)) {
if (c->lf) {
// restore
dospelleffects(NULL, OT_S_RESTORATION, 1,
c->lf, NULL, c->lf->cell, B_BLESSED, NULL, B_TRUE, o);
} else {
object_t *oo, *nextoo;
// revive corpses
for (oo = c->obpile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
if (oo->type->id == OT_CORPSE) {
dospelleffects(NULL, OT_S_RESSURECTION, 1,
NULL, oo, c, B_BLESSED, NULL, B_TRUE, o);
}
}
}
}
}
break;
case OT_GODSTONE_MAGIC:
// restore all mp
gainmp(lf, lf->maxmp);
// identify all objects
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (!isidentified(oo)) {
dospelleffects(lf, OT_S_IDENTIFY, 10, NULL, oo, NULL, B_BLESSED, NULL, B_FALSE, o);
}
}
// learn any spell
select_new_spell(SS_NONE, NA);
break;
case OT_GODSTONE_NATURE:
// cure diseases
dospelleffects(NULL, OT_S_CUREPOISON, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_FALSE, o);
// everyone in lof...
for (i = 0; i < lf->cell->map->w * lf->cell->map->h; i++) {
cell_t *c;
c = lf->cell->map->cell[i];
if (c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_WALLSTOP, NULL)) {
// plants/animals become peaceful
if ((getraceclass(c->lf) == RC_PLANT) || (getraceclass(c->lf) == RC_ANIMAL)) {
makepeaceful(c->lf, lf);
}
}
}
// all in LOS
for (i = 0; i < lf->nlos; i++) {
object_t *oo,*nextoo;
cell_t *c;
c = lf->los[i];
if (!issolid(c)) {
// destroy fires
for (oo = c->obpile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
if (oo->material->id == MT_FIRE) {
removeob(oo, ALL);
}
}
// ground turns to dirt
setcelltype(c, CT_DIRT);
// flower grows here.
addobfast(c->obpile, OT_FLOWER);
}
}
// summon treant
c = real_getrandomadjcell(lf->cell, &ccwalkable, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, lf);
if (c) {
summonmonster(lf, c, R_TREANT, NULL, rnd(50,90), B_TRUE);
}
break;
case OT_GODSTONE_REVENGE:
// everyone's objects appear underneath you
// everyone else attacks themself
for (i = 0; i < lf->cell->map->w * lf->cell->map->h; i++) {
cell_t *c;
char lfname[BUFLEN],othername[BUFLEN];
getlfname(lf, lfname);
c = lf->cell->map->cell[i];
if (c->lf && (c->lf != lf) && haslof(lf->cell, c, LOF_WALLSTOP, NULL)) {
object_t *oo, *nextoo;
// they get an injury
dospelleffects(NULL, OT_S_FLAYFLESH, 1, c->lf, NULL, c, B_BLESSED, NULL, B_TRUE, o);
// they attack themself.
attackcell(c->lf, c, B_TRUE);
// then all their objects appear under you
if (isplayer(c->lf)) {
msg("All your objects appear at %s%s feet!", lfname,
getpossessive(lfname));
} else if (cansee(player, c->lf)) {
getlfname(c->lf, othername);
msg("All %s%s objects appear at %s%s feet!", othername,
getpossessive(othername),
lfname, getpossessive(lfname));
}
for (oo = c->lf->pack->first ; oo ; oo = nextoo) {
cell_t *where;
nextoo = oo->next;
where = lf->cell;
if (!moveob(oo, where->obpile, ALL) ){
// try somewhere nearby
where = getrandomadjcell(where, &ccwalkable, B_NOEXPAND);
if (where) {
moveob(oo, where->obpile, ALL);
}
}
}
}
}
break;
default:
break;
}
// use up all charges
usecharges(o, getcharges(o));
} 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;
object_t *notenough = NULL;
if (isplayer(lf)) {
int donedip = B_FALSE;
int anydippable = B_FALSE;
// anything here to fill with?
for (oo = lf->cell->obpile->first ; (oo && !donedip); oo = nextoo) {
flag_t *fillflag;
int dippable = B_FALSE;
nextoo = oo->next;
fillflag = hasflag(oo->flags, F_FILLPOT);
if (fillflag) {
if (getobsize(oo) < getobsize(o)) {
// not dippable, but display a different message
if (!notenough) {
notenough = oo;
}
} else {
flag_t *rf;
int ok = B_FALSE;
rf = hasflag(oo->flags, F_LINKRACE);
if (rf) {
if (getlorelevel(player, rf->val[0]) >= PR_SKILLED) {
ok = B_TRUE;
} else if (getskill(player, SK_LORE_CHEMISTRY)) {
ok = B_TRUE;
}
} else if (!rf) {
ok = B_TRUE;
}
if (ok) {
dippable = B_TRUE;
anydippable = B_TRUE;
notenough = NULL;
}
}
}
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];
flag_t *bfob;
// get a random fillpot flag.
bfob = getrandomflag(oo->flags, F_FILLPOT);
if (bfob) {
objecttype_t *ot;
ot = findot(bfob->val[0]);
if (ot) {
strcpy(newobname, ot->name);
} else {
strcpy(newobname, "potion of blood");
}
} else 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);
}
if (oo->type->id == OT_FOUNTAIN) {
if (fountain_will_dryup(oo)) {
removeob(oo, oo->amt);
}
} else {
if (fillflag->val[1] != NA) {
removeob(oo, fillflag->val[1]);
} else {
removeob(oo, 1);
}
}
} else {
msg("That doesn't seem like a very good idea.");
}
donedip = B_TRUE;
} // if (ch == y)
}
}
if (!donedip) {
if (anydippable) {
msg("There is nothing else here to fill your %s from.",noprefix(obname));
} else if (notenough) {
char poolname[BUFLEN];
getobname(notenough, poolname, notenough->amt);
msg("%s %s not sufficient to fill your %s.",poolname,
(notenough->amt == 1) ? "is" : "are", noprefix(obname));
} else {
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 == BH_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) {
int dir;
// ask direction
dir = askdir("Manipulate lock in which direction (- to cancel)", B_TRUE, B_TRUE);
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, 100, 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 {
char nbuf[BUFLEN];
sprintf(nbuf, "plays a tune on %ss panpipes.", it(lf));
noise(lf->cell, lf, NC_OTHER, SV_SHOUT, "the sound of panpipes.", nbuf);
}
} else if (o->type->id == OT_PEACEPIPES) {
lifeform_t *l;
if (isplayer(lf)) {
msg("You play an enchanting melody on your panpipes!");
} else {
char nbuf[BUFLEN];
sprintf(nbuf, "plays a calming melody on %ss panpipes.", it(lf));
noise(lf->cell, lf, NC_OTHER, SV_SHOUT, "an eerily calming melody.", nbuf);
}
for (l = lf->cell->map->lf ; l ; l = l->next) {
if (l == lf) continue;
if (iscursed(o)) {
// enrage
enrage(l, DEF_RAGETIME*2);
} else {
// calm
makepeaceful(l, lf);
}
}
makeknown(o->type->id);
} else if (hasflag(o->flags, F_HELPSDIG)) {
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, B_FALSE)) {
// failed
return B_TRUE;
}
} // end if ch is a direction
} else if (o->type->id == OT_CHEWINGGUM) {
int donesomething = B_FALSE;
if (!where) {
if (isplayer(lf)) msg("There is nothing to use your gum on there!");
} else if (where->lf) {
if (isplayer(lf)) msg("There is someone in your way!");
} else {
object_t *o;
for (o = where->obpile->first ; o ; o = o->next) {
int isopen = B_FALSE;
// jammed doors
if (isdoor(o, &isopen) && !isopen) {
char obname[BUFLEN];
getobname(o, obname, 1);
msg("You stick your gum into the hinges of %s.", obname);
addflag(o->flags, F_JAMMED, 1, B_TRUE, 100, NULL);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
}
}
}
if (donesomething) {
removeob(o, 1);
} else {
taketime(lf, getactspeed(lf));
if (isplayer(lf)) msg("There is nothing to use your gum on there!");
}
} 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 && (where->lf != lf)) {
if (isplayer(lf)) msg("There is someone in your way!");
} else {
int isopen;
object_t *o2;
char ch = '\0',qbuf[BUFLEN],spannername[BUFLEN];
flag_t *f;
getobname(o, spannername, 1);
snprintf(qbuf, BUFLEN, "What will you use %s on?", spannername);
initprompt(&prompt, qbuf);
for (o2 = where->obpile->first ; o2 ; o2 = o2->next) {
// can use the spanner on...
if (isdoor(o2, &isopen)) { // doors
getobname(o2, obname, 1);
addchoice(&prompt, incletter(&ch), obname, NULL, o2 , NULL);
} else if (hasflag(o2->flags, F_LOCKABLE)) {
getobname(o2, obname, 1);
addchoice(&prompt, incletter(&ch), obname, NULL, o2 , NULL);
}
}
if (prompt.nchoices >= 1) {
int isopen;
if (prompt.nchoices == 1) {
o2 = (object_t *)prompt.choice[0].data;
} else {
prompt.maycancel = B_TRUE;
getchoice(&prompt);
o2 = (object_t *) prompt.result;
}
if (o2) {
if (isplayer(lf)) {
if (confirmknowntraps(o2)) {
msg("Cancelled.");
return B_TRUE;
}
}
snprintf(qbuf, BUFLEN, "What will you do to %s?", obname);
initprompt(&prompt, qbuf);
// figure out what you can do to the object
if (isdoor(o2, &isopen) && !isopen) {
getobname(o2, obname, 1);
f = hasflagknown(o2->flags, F_JAMMED);
if (f) {
// you know it's jammed...
snprintf(qbuf, BUFLEN, "Unjam it");
addchoice(&prompt, 'j', qbuf, NULL, o2 , NULL);
} else {
// you don't _think_ it's jammed...
snprintf(qbuf, BUFLEN, "Jam it");
addchoice(&prompt, 'j', qbuf, NULL, o2 , NULL);
}
}
f = hasflag(o2->flags, F_LOCKABLE);
if (f) {
snprintf(qbuf, BUFLEN, "Dismantle the lock");
addchoice(&prompt, 'l', qbuf, NULL, o2 , NULL);
}
if (prompt.nchoices >= 1) {
prompt.maycancel = B_TRUE;
ch = getchoice(&prompt);
if (ch != '\0') {
// traps go off
if (triggerattachedtraps(o2, lf)) {
return B_TRUE;
}
}
switch (ch) {
case 'j':
f = hasflagknown(o2->flags, F_JAMMED);
if (f) {
// unjam it
msg("You loosen the hinges on %s.", obname);
killflag(f);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
} else {
// you don't _think_ it's jammed...
// try to jam it.
if (hasflag(o2->flags, F_JAMMED)) {
msg("The hinges on %s cannot be tightened any more.", obname);
f->known = B_TRUE;
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
} else {
msg("You tighten the hinges on %s.", obname);
addflag(o2->flags, F_JAMMED, 2, B_TRUE, 120, NULL); // very tight
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
}
}
break;
case 'l':
f = hasflagknown(o2->flags, F_LOCKED);
if (f) {
msg("You remove the locking bolts from %s.", obname);
killflagsofid(o2->flags, F_LOCKED);
killflagsofid(o2->flags, F_LOCKABLE);
taketime(lf, getactspeed(lf));
donesomething = B_TRUE;
} else {
// should never happen.
msg("%s is already unlocked.",obname);
}
break;
default:
msg("Cancelled.");
return B_TRUE;
}
} else {
msg("There doesn't seem to be much you can do to %s.",obname);
}
} else {
msg("Cancelled.");
return B_TRUE;
}
} else {
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);
} else if ((o->type->id == OT_POISONSAC) || (o->type->id == OT_POISONSACBL)) {
object_t *oo;
if (!getskill(lf, SK_LORE_CHEMISTRY)) {
if (isplayer(lf)) msg("You don't have enough Chemistry knowledge to use this.");
return B_TRUE;
}
// construct list of possible potions
clearretobs();
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (oo->type->obclass->id == OC_POTION) {
retobs[nretobs] = oo;
nretobs++;
}
}
if (nretobs <= 0) {
if (isplayer(lf)) {
snprintf(buf, BUFLEN, "You have nothing to mix %s into.",obname);
msg(buf);
}
return B_TRUE;
} else {
enum OBTYPE newoid = OT_POT_POISON;
switch (o->type->id) {
case OT_POISONSACBL:
newoid = OT_POT_BLINDNESS;
break;
default:
case OT_POISONSAC:
newoid = OT_POT_POISON;
break;
}
snprintf(buf, BUFLEN, "Mix %s into what",obname);
if (isplayer(lf)) {
condset_t cs;
initcondv(&cs, CC_SPECIFIED, B_TRUE, NA,
CC_NONE);
oo = askobject(lf->pack, buf, NULL, NULL, '\0', &cs, B_FALSE);
} else {
oo = retobs[rnd(0,nretobs-1)];
}
if (oo) {
char oldname[BUFLEN];
// remember letter
getobname(oo, oldname, 1);
if (isplayer(lf)) {
msg("You mix %s into %s.", obname, oldname);
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s mixes %s into %s.", lfname, obname, oldname);
}
if (oo->amt > 1) {
oo = splitob(o);
assert(oo);
}
killob(oo); oo = NULL;
oo = addobfast(lf->pack, newoid);
if (oo) {
if (isplayer(lf)) {
char newname[BUFLEN];
makeknown(OT_POT_POISON);
getobname(oo, newname, oo->amt);
msgnocap("%c - %s", oo->letter, newname);
}
} else {
// should never happen...
if (isplayer(lf)) {
msg("Your %s explodes!", noprefix(oldname));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s%s %s explodes!", lfname, getpossessive(lfname), oldname);
}
}
} else {
if (isplayer(lf)) msg("Cancelled.");
return B_TRUE;
}
}
}
if (wastech && isplayer(lf)) {
angergodmaybe(R_GODMAGIC, 10, GA_HERESY);
}
return B_FALSE;
}
enum RARITY pickrr(int whatfor) {
enum RARITY wantrr = RR_FREQUENT;
int chance = 3;
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 = 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 && haslos(lf, 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, NULL, '\0', NULL, B_FALSE);
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);
}
} 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)) {
angergodmaybe(R_GODPURITY, 25, GA_HERESY);
}
} else if ((o->type->id == OT_POT_POISON) && canbepoisoned(dst->type->id)) {
applyobmod(dst, findobmod(OM_POISONED));
msg("Your %s is now covered with a layer of venom.", noprefix(dstname));
} 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 (hasflag(dst->flags, F_IMMUTABLE)) {
if (isplayer(lf)) {
msg("For a moment, your %s looks as good as new.",noprefix(dstname));
}
} else {
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, lf);
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) {
// unjam doors
killflagsofid(dst->flags, F_JAMMED);
}
} 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, lf);
}
} 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 = B_FALSE;
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, NULL);
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_POT_MAGIC:
if (lf->mp < getmaxmp(lf)) {
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_NOPREMODS, B_CONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); // 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) {
if (fountain_will_dryup(o)) {
// dry up (ie. remove DONTKILL property)
drinkflag->val[2] = NA;
}
}
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];
char origstats[BUFLEN];
int dam;
int i;
int first,dir;
int amt;
int failed;
int seenbyplayer;
object_t *o2;
race_t *r;
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);
// override?
if ((oid == OT_POT_BLOODC) && !lfhasflag(lf, F_BEINGSTONED)) {
if ((lf->race->id == R_VAMPIRE) || hasequippedobid(lf->pack, OT_AMU_BLOOD)) {
oid = OT_POT_BLOOD;
}
}
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_real(lf, dam, DT_ACID, NULL, "drinking acid", B_FALSE, o, B_FALSE, NULL, B_FALSE, BP_NONE, B_FALSE);
break;
case OT_POT_ACROBATICS:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, 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 diseases (even magical ones)
killflagsofid(lf->flags, F_POISONED);
// 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_BLINDNESS:
switch (potblessed) {
case B_BLESSED: i = rnd(5,10); break;
case B_CURSED: i = rnd(20,30); break;
default:
case B_UNCURSED: i = rnd(10,20); break;
}
addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, i);
break;
case OT_POT_CANINETRACKING:
dospelleffects(lf, OT_S_CANINETRACKING, 8, lf, NULL, lf->cell, potblessed, seen, B_TRUE, NULL);
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, 150, 0)) {
if (isplayer(lf)) {
msg("You feel momentarily %s.", (potblessed == B_CURSED) ? "incompetent" : "more competent");
}
break;
}
if (potblessed == B_CURSED) {
amt = -5;
} else {
amt = 5;
}
// 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, 150, 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, NULL);
break;
case OT_POT_EXPERIENCE:
if (potblessed == B_CURSED) {
leveldrain(lf, 1, SC_CON, 999, NULL);
} else {
// gain xp!
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, 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_FISHLUNG:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, 0)) {
if (isplayer(lf)) {
msg("You lungs feel momentarily watery.");
}
break;
}
// how long for?
i = geteffecttime(50,100,potblessed);
if (!lfhasflag(lf, F_BREATHWATER)) {
addtempflag(lf->flags, F_BREATHWATER, B_TRUE, NA, NA, NULL, i);
}
break;
case OT_POT_FURY:
lf->stamina = getmaxstamina(lf);
abilityeffects(lf, OT_A_RAGE, lf->cell, lf, NULL);
pleasegodmaybe(R_GODBATTLE, 15);
break;
case OT_POT_GASEOUSFORM:
dospelleffects(lf, OT_S_GASEOUSFORM, (potblessed) ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE, NULL);
break;
case OT_POT_GROWTH:
i = getlfsize(lf);
sprintf(origstats, "%d,%d", lf->att[A_STR], lf->maxhp);
if (iscursed(o)) {
dospelleffects(lf, OT_S_SIZEDOWN, 1, lf, NULL, lf->cell, B_UNCURSED, seen, B_TRUE, NULL);
} else {
dospelleffects(lf, OT_S_SIZEUP, 1, lf, NULL, lf->cell, B_UNCURSED, seen, B_TRUE, NULL);
}
// revert in a little while...
addflag(lf->flags, F_SIZETIMER, i, rnd(10,20), NA, origstats);
break;
case OT_POT_HEALING:
dospelleffects(lf, OT_S_HEALING,potblessed ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE, NULL);
break;
case OT_POT_HEALINGMIN:
dospelleffects(lf, OT_S_HEALINGMIN,potblessed ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE, NULL);
break;
case OT_POT_HEALINGMAJ:
dospelleffects(lf, OT_S_HEALINGMAJ,potblessed ? 5 : 1, lf, NULL, lf->cell, potblessed, seen, B_TRUE, NULL);
break;
case OT_POT_INVIS:
dospelleffects(lf, OT_S_INVISIBILITY,potblessed ? 6 : 3, lf, NULL, lf->cell, potblessed, seen, B_TRUE, NULL);
break;
case OT_POT_INVULN:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, 0)) {
if (isplayer(lf)) {
msg("You feel momentarily invulnerable.");
}
break;
}
i = geteffecttime(20,40,potblessed);
if (potblessed == B_CURSED) {
if (isplayer(lf)) {
msg("^%cYou feel invulnerable!", getlfcol(lf, CC_VGOOD));
// but you don't actually become invulnerable!
}
} else {
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_LYCANTHROPY:
if (isplayer(lf)) msg("Yuck, this tastes like blood!");
// find random lycanthrope type
r = getrandomracewithflag(F_LYCANTHROPE);
poison(lf, PERMENANT, P_LYCANTHROPY, 1, r->name, R_NONE, B_FALSE);
break;
case OT_POT_MAGIC:
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, -10)) {
if (isplayer(lf)) {
msg("You feel your magical energy temporarily pulse.");
}
break;
}
if (getmaxmp(lf) > 0) {
msg("Your magical energy is restored.");
gainmp(lf, 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:
if (isplayer(lf)) {
msg("^%cThis tastes like poison!", getlfcol(lf, CC_BAD));
}
if (poison(lf, rnd(10,20), P_VENOM, 1, "a potion of poison", R_NONE, B_TRUE)) {
if (isplayer(lf)) msg("Luckily, it has no effect.");
}
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, NULL);
} else {
// random polymorph
dospelleffects(NULL, OT_S_POLYMORPH, 1, lf, NULL, lf->cell, potblessed, NULL, B_TRUE, NULL);
}
break;
case OT_POT_RESTORATION:
dospelleffects(NULL, OT_S_RESTORATION, 1, lf, NULL, lf->cell, potblessed, NULL, B_TRUE, NULL);
break;
case OT_POT_SANCTUARY:
// how long for?
//i = gethealtime(lf); // TODO: change...
if (getmr(lf) && skillcheck(lf, SC_RESISTMAG, 150, 0)) {
if (isplayer(lf)) {
nothinghappens();
}
break;
}
i = 15;
first = B_TRUE;
// remove damaging objects underneath
if (killobswithflag(lf->cell->obpile, F_WALKDAM, B_FALSE)) {
if (haslos(player, lf->cell)) {
msg("Some nearby objects disappear!");
}
}
// add holy barriers all around
for (dir = DC_N; dir <= DC_NW; dir++) {
cell_t *c;
c = getcellindir(lf->cell, dir);
if (c && !c->type->solid) {
object_t *newob = NULL;
// get lifeforms out of the way
if (c->lf) {
knockback(c->lf, getdiraway(c, lf->cell, NULL, B_FALSE, DT_ORTH, B_FALSE),
1, lf, 0, B_DOANNOUNCE, B_DODAM);
}
if (!c->lf) {
newob = addob(c->obpile, "magical barrier");
if (newob) {
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);
addflag(newob->flags, F_CREATEDBY, lf->id, NA, NA, NULL);
}
}
}
}
break;
case OT_POT_SLEEP:
dospelleffects(lf, OT_S_SLEEP, 8, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, NULL);
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, NULL);
} else if (potblessed == B_CURSED) {
dospelleffects(lf, OT_S_SLOW, 5, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, NULL);
} else { // uncursed
dospelleffects(lf, OT_S_HASTE, 5, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE, NULL);
}
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("A feeling of holiness spreads through your body.");
// cure any curses, and lycanthropy
fixcurses(lf);
}
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:
o2 = hasequippedobid(lf->pack, OT_AMU_BLOOD);
if ((lf->race->id == R_VAMPIRE) || o2) {
int b = B_UNCURSED;
if (isplayer(lf)) {
if (o2 && !isknown(o2)) {
char o2name[BUFLEN];
getobname(o2, o2name, o2->amt);
msg("^gYour %s pulses as your drink!^n", noprefix(o2name));
makeknown(o2->type->id);
}
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, NULL);
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, 15, 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],triedonbuf[BUFLEN];
int playercansee;
int willid = B_FALSE;
int needsob = B_FALSE;
objecttype_t *linkspell;
int readtime;
int pleasemagicgod = B_FALSE;
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;
}
if (lfhasflag(lf, F_SILENCED)) {
if (o->type->obclass->id != OC_BOOK) {
if (isplayer(lf)) {
msg("You are unable to make a sound!");
}
return B_TRUE;
}
}
strcpy(triedonbuf, "");
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;
}
stopsprinting(lf);
killflagsofid(lf->flags, F_HIDING);
taketime(lf, readtime);
// 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) || (o->type->id == OT_GRIMOIRE)) {
// 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 (willid &&
(!isknown(o) || ((o->type->id == OT_MAP) && !isidentified(o)) ) ) {
// id the object
if ((o->type->obclass->id == OC_BOOK) || (o->type->id == OT_MAP)) {
identify(o);
} else {
makeknown(o->type->id);
}
// don't show that scolls of wishing are cursed!
if (o->type->id != OT_SCR_WISH) {
o->blessknown = B_TRUE;
}
real_getobname(o, obname, 1, B_NOPREMODS, B_CONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
if (isplayer(lf)) {
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
}
if (ismagical(o)) {
pleasemagicgod = B_TRUE;
///... this might be changed back to false later
// if a scroll causes us to cast a spell, so that we
// dont double up the god bonus for using magic.
}
// 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, NULL);
}
}
}
if (seen) {
makeknown(o->type->id); o->blessknown = B_TRUE;
}
if (isplayer(lf)) msg("The scroll crumbles to dust.");
removeob(o, 1);
} 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, NULL);
}
}
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;
pleasemagicgod = 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, NULL, '\0', NULL, B_FALSE);
killflag(f2);
if (targob) {
if (isplayer(lf)) {
// this will be used by maketried() later
real_getobname(targob, triedonbuf, 1, B_NOPREMODS, B_NOCONDITION,
B_BLINDADJUST, B_NOBLESSINGS, B_USED, B_NOSHOWALL);
}
} else {
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;
branch_t *destbranch = 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);
destbranch = findbranch(destthing->value);
plural = destbranch->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 don't know enough cartography to interpret 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);
if (!destob) {
// doesn't exist for some reason?!?!?
dblog("Error - can't find entrance object for region!");
msg("Error - can't find entrance object for region!"); more();
return B_TRUE;
}
destcell = getoblocation(destob);
dist = getcelldist(lf->cell, destcell);
// right under you?
if (lf->cell == destcell) {
msg("%s %s right here, according to the map!", f->text, isare);
if (slev == PR_MASTER) {
// still reveal around it
setcellknownradius(destcell, PR_MASTER, 5, DT_ORTH);
needredraw = B_TRUE;
}
} else {
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, RF_SHORT);
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, RF_WITHLEVEL);
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) {
if (iscursed(o)) {
addtempflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(20,30));
} else {
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) {
if (o->blessed == B_BLESSED) {
if (isplayer(lf)) msg("You momentarily feel like you have forgotten something.");
} else {
// 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.");
// remove 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;
enum ATTRIB a;
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++;
}
}
// make enhanced/reduced attributes permenant
for (a = 0; a < MAXATTS; a++) {
int innate,actual;
innate = lf->att[a];
actual = getattr(lf, a);
if (actual != innate) {
lf->att[a] = actual;
ndone++;
if (isplayer(lf)) statdirty = B_TRUE;
}
}
if (isplayer(lf)) {
msg("You are surrounded by a stabilising aura.");
}
if (!ndone) {
object_t *oo,*poss[MAXPILEOBS];
int nposs = 0;
// make a weapon or piece of armour invulnerable
for (oo = lf->pack->first ; oo ; oo = oo->next) {
if (isweapon(oo) || isarmour(oo)) {
if (isequipped(oo) && !hasflag(oo->flags, F_INVULNERABLE)) {
poss[nposs++] = oo;
}
}
}
if (nposs) {
oo = poss[rnd(0,nposs-1)];
if (isplayer(lf)) {
char ooname[BUFLEN];
getobname(oo, ooname, oo->amt);
msg("Your %s become%s ultra-dense!", noprefix(ooname), OBS1(oo));
} else if (cansee(player, lf)) {
char lfname[BUFLEN];
char ooname[BUFLEN];
getlfname(lf, lfname);
getobname(oo, ooname, oo->amt);
msg("%s%s %s become%s ultra-dense!", lfname, getpossessive(lfname),
noprefix(ooname), OBS1(oo));
}
addflag(oo->flags, F_IMMUTABLE, B_TRUE, NA, NA, NULL);
killflagsofid(oo->flags, F_DAMAGABLE);
ndone++;
}
// still nothing done?
if (!ndone) {
if (isplayer(lf)) msg("Unfortunately, it doesn't seem to affect you.");
}
}
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);
}
}
}
// fix player curses
if (fixcurses(lf)) {
if (isplayer(lf) || cansee(player, lf)) seen = B_TRUE;
}
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) || (o->type->id == OT_GRIMOIRE)) {
object_t *oo;
char ch = 'a';
enum SPELLSCHOOL school;
enum SKILLLEVEL slev;
maketried(o->type->id, NULL);
// identify right away.
if (isplayer(lf) && !isknown(o)) {
identify(o);
o->blessknown = B_TRUE;
real_getobname(o, obname, 1, B_NOPREMODS, B_CONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
// tell the player
msg("This is %s!",obname);
more();
drawmsg();
}
if (o->type->id == OT_SPELLBOOK) {
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;
}
} else { // ie. grimoire
object_t *oo;
enum SPELLSCHOOL ss;
int found = B_FALSE;
// grimoire - do you know ANY spell school?
for (oo = o->contents->first; oo ; oo = oo->next) {
ss = getspellschoolknown(lf, oo->type->id);
if ((ss != SS_NONE) && getskill(lf, getschoolskill(ss))) {
found = B_TRUE;
break;
}
}
if (!found) {
if (isplayer(lf)) {
msg("You cannot comprehend the contents of this book.");
}
return B_FALSE;
}
}
// 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) {
// do you know _this_ spell's school?
// is it one you can't already cast?
if ((getspellschoolknown(lf, oo->type->id) != SS_NONE) &&
!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!");
maketried(o->type->id, NULL);
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 %s without reading it.", o->type->name);
maketried(o->type->id, NULL);
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);
}
*/
// learn it
addflag(lf->flags, F_CANCAST, linkspell->id, NA, NA, NULL);
} 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) {
if (ismaxedskill(lf, f->val[0])) {
if (isplayer(lf)) msg("This manual is too advanced for you.");
} else {
if (!giveskill(lf, f->val[0])) {
// can't learn this skill. maybe you have f_noskill?
if (isplayer(lf)) msg("You are incapable of learning this skill.");
}
}
}
if (isplayer(lf)) {
msg("The book crumbles to dust.");
}
// removeob one of the object
removeob(o, 1);
}
}
if (pleasemagicgod && isplayer(lf)) {
pleasegodmaybe(R_GODMAGIC, 2);
}
if (strlen(triedonbuf)) {
maketried(o->type->id, triedonbuf);
} else {
maketried(o->type->id, NULL);
}
return B_FALSE;
}
object_t *relinkob(object_t *src, obpile_t *dst) {
/*
if (!obfits(src, dst)) return NULL;
if (isdeadob(src)) return NULL;
*/
if (!obfits(src, dst)) return src;
if (isdeadob(src)) return src;
if (src->pile->owner) {
// previous owner loses all flags conferred by this object
loseobflags(src->pile->owner, src, ALLCONFERRED);
}
if (dst->owner) {
if (isplayer(dst->owner)) {
// can't now sacrifice this money!
killflagsofid(src->flags, F_UNTOUCHED);
}
}
affect_temperature(src, B_REMOVE);
// unweild
killflagsofid(src->flags, F_EQUIPPED);
if (src->pile->owner) {
unequipeffects(src->pile->owner, src);
// this might have killed the object - eg. unequipping
// a summoned energy blade.
if (isdeadob(src)) {
return src; // was null
}
}
// 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) {
if (haslos(player, pos)) {
setlosdirty(player);
}
//precalclos(player);
}
//drawscreen();
}
}
affect_temperature(src, B_ADD);
return src;
}
int removedeadobs(obpile_t *op) {
object_t *o, *nexto;
int nremoved = 0;
for (o = op->first ; o ; o = nexto) {
nexto = o->next;
if (hasflag(o->flags, F_DEAD)) {
checkflagpile(o->flags);
obdie(o);
nremoved++;
}
}
return nremoved;
}
// returns the amount left
int removeob(object_t *o,int howmany) {
int preburdened = B_FALSE;
int rv = 0;
lifeform_t *owner;
if ((howmany != ALL) && howmany <= 0) return o->amt;
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);
}
}
int rollforob(int depth) {
int pctroll,mod;
pctroll = rnd(1,100);
mod = getobrollmod(depth, RARITYVARIANCEOB, B_TRUE);
pctroll -= mod;
limit(&pctroll,1,100);
return pctroll;
}
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 = 25;
break;
case RR_RARE:
if (minr) *minr = 25;
if (maxr) *maxr = 45;
break;
case RR_UNCOMMON:
if (minr) *minr = 45;
if (maxr) *maxr = 65;
break;
case RR_COMMON:
if (minr) *minr = 65;
if (maxr) *maxr = 85;
break;
case RR_FREQUENT:
if (minr) *minr = 85;
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);
}
}
}
}
// returns TRUE if we set something
int setcolfromhiddenname(flagpile_t *fp, char *text, int defaultglyph) {
int n,gotcolour = B_FALSE;
// set colour based on hiddenname
for (n = 0; strlen(colour[n].name); n++) {
if (strstr(text, colour[n].name)) {
flag_t *gf;
gf = hasflag(fp, F_GLYPH);
if (gf) {
gf->val[0] = colour[n].col;
} else {
addflag(fp, F_GLYPH, colour[n].col, defaultglyph, NA, NULL);
//dblog("assigning colour %s to %s (%s)",colour[n].name, text, ot->name);
}
gotcolour = B_TRUE;
break;
}
}
if (!gotcolour) {
for (n = 0; strlen(gemtype[n].name); n++) {
if (strstr(text, gemtype[n].name)) {
flag_t *gf;
gf = hasflag(fp, F_GLYPH);
if (gf) {
gf->val[0] = gemtype[n].col;
} else {
addflag(fp, F_GLYPH, gemtype[n].col, defaultglyph, NA, NULL);
}
gotcolour = B_TRUE;
break;
}
}
}
return gotcolour;
}
int sethiddenname(objecttype_t *ot, char *text) {
// add knowledge for it (unless it's a book)
if (ot->obclass->id != OC_BOOK) {
//addknowledge(ot->obclass->id, text, B_UNKNOWN);
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);
}
if (strstr(text, "humming")) {
addflag(ot->flags, F_MAKESNOISE, 20, 2, NC_ENVIRONMENTAL, "humming.");
}
setcolfromhiddenname(ot->flags, text, ot->obclass->glyph.ch);
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, NULL, B_SHOWALL, B_REALRACE);
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;
flag_t *f;
if (hasflag(o->flags, F_NOSHATTER)) {
return B_FALSE;
}
f = hasflag(o->flags, F_EXPLODEONDEATH);
if (!f) f = hasflag(o->flags, F_EXPLODEONDAM);
if (f) {
// with explodeondam, only happen if damtype is any.
if ((f->id == F_EXPLODEONDAM) && (f->val[0] != NA)) {
} else {
explodeob(o, f, f->val[1], fromlf);
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) && !o->pile->parentob) {
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 && !o->pile->parentob) {
shatterdam = getshatterdam(o);
if (shatterdam && !isdead(target)) {
// extra glass damage
if (seen) {
msg("^%c%s %s showered in %s shards!", getlfcol(target, CC_BAD),
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(o->pile, 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(o->pile, 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_BLINDNESS:
case OT_POT_ELEMENTIMMUNE:
case OT_POT_ETHEREALNESS:
case OT_POT_FISHLUNG:
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);
if (fromlf && isplayer(fromlf) && (o->type->id == OT_POT_POISON)) {
god_usepoison_response();
}
}
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("^%c%s %s covered with oil!", getlfcol(target, CC_BAD),
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 = o->pile->first ; oo ; oo = nextoo) {
nextoo = oo->next;
takedamage(oo, 0, DT_WATER, fromlf);
}
}
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_GODLIFE, 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, lifeform_t *attacker) {
return real_takedamage(o, howmuch, damtype, B_TRUE, attacker);
}
// returns amount of damage taken
int real_takedamage(object_t *o, int howmuch, int damtype, int wantannounce, lifeform_t *attacker) {
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) {
int prehot = B_FALSE;
if (hasflag(o->flags, F_HOT) || hasflag(o->flags, F_ONFIRE) ||
hasflag(o->flags, F_FLAMESTRIKE)) {
prehot = B_TRUE;
}
extinguish(o);
makewet(o, howmuch);
if (prehot) {
cell_t *c;
c = getoblocation(o);
if (c) {
addob(c->obpile, "puff of steam");
}
}
}
// initial effects based on damage type
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_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_SHOWALL);
losehp_real(owner, howmuch , damtype, NULL, obname, B_DAMADJUST, o, B_NORETALIATE, NULL, B_DAMEFFECTS, f ? f->val[0] : BP_NONE, B_NOCRIT);
if (isdead(owner)) {
return howmuch;
}
}
}
} else if (damtype == DT_COLD) {
// cold might shatter glass
if ((o->material->id == MT_GLASS) && pctchance(howmuch*5)) {
char buf[BUFLEN];
char buf2[BUFLEN];
real_getobname(o, buf2, 1, B_NOPREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL);
snprintf(buf, BUFLEN, "a shattering %s", noprefix(buf2));
shatter(o, B_TRUE, buf, B_FALSE);
return howmuch;
}
}
// ie fire/heat
if (basedamagetype(damtype) == DT_FIRE) {
int heatamt;
heatamt = isheatable(o);
if (heatamt) {
makehot(o, heatamt, rnd(DEF_BURNTIMEMIN*4,DEF_BURNTIMEMAX*4));
}
}
if ((damtype == DT_BASH) || (damtype == DT_PROJECTILE)) {
if (hasflag(o->flags, F_BRUISABLE) && !hasflag(o->flags, F_BRUISED)) {
addflag(o->flags, F_BRUISED, B_TRUE, NA, NA, NULL);
}
}
// 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], NULL);
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], NULL);
return howmuch;
}
}
}
}
// spell cloud ?
f = hasflag(o->flags, F_SPELLCLOUDONDAM);
if (f) {
int power;
char obname[BUFLEN],seebuf[BUFLEN],noseebuf[BUFLEN];
char buf[BUFLEN],*p;
flag_t *glyphflag;
cell_t *where;
glyph_t cloudglyph;
where = getoblocation(o);
getobname(o, obname, 1);
p = readuntil(seebuf, f->text, '^');
p = readuntil(noseebuf, p, '^');
p = readuntil(buf, p, '^');
power = atoi(buf);
glyphflag = hasflag(o->flags, F_SPELLCLOUDGLYPH);
if (glyphflag) {
cloudglyph.colour = glyphflag->val[0];
cloudglyph.ch = glyphflag->val[1];
} else {
cloudglyph.colour = C_RANDOM;
cloudglyph.ch = UNI_SHADELIGHT;
}
if (f->val[2] == B_IFACTIVATED) {
if (isactivated(o)) {
if (!hasflag(o->flags, F_SPELLCLOUDONDEATH)) {
spellcloud(where, f->val[1], DT_ORTH, cloudglyph.ch, cloudglyph.colour, f->val[0], power, B_TRUE,
seebuf, noseebuf, B_FALSE, o, B_INCLUDECENTRE);
}
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
}
} else {
if (!hasflag(o->flags, F_SPELLCLOUDONDEATH)) {
spellcloud(where, f->val[1], DT_ORTH, cloudglyph.ch, cloudglyph.colour, f->val[0], power, B_TRUE,
seebuf, noseebuf, B_FALSE, o, B_INCLUDECENTRE);
}
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
}
return howmuch;
}
// flashes?
f = hasflag(o->flags, F_FLASHONDAM);
if (f) {
flag_t *f2;
lifeform_t *thrower = NULL;
cell_t *where;
where = getoblocation(o);
f2 = hasflag(o->flags, F_THROWNBY);
if (f2) {
thrower = findlf(where->map, f2->val[0]);
}
if (f->val[2] == B_IFACTIVATED) {
if (hasflag(o->flags, F_ACTIVATED)) {
// flash, then object dies
brightflash(where,f->val[0], thrower);
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
}
} else {
// flash, then object dies
brightflash(where,f->val[0], thrower);
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
}
return howmuch;
}
if (howmuch <= 0) {
return 0;
}
// monster pretending to be object?
if (hasflag(o->flags, F_ISMONSTER)) {
lifeform_t *newlf;
newlf = reveal_pretendob(o);
if (newlf && attacker) {
turntoface(newlf, attacker->cell);
aiattack(newlf, attacker, aigetchasetime(newlf));
}
return 0;
}
// update lastdamtype
f = hasflag(o->flags, F_LASTDAMTYPE);
if (f) {
f->val[0] = damtype;
if (attacker) {
f->val[1] = attacker->id;
}
} else {
addflag(o->flags, F_LASTDAMTYPE, damtype, attacker ? attacker->id : NA, NA, NULL);
}
real_getobname(o, obname, o->amt, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL);
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(2)) { // fire sometimes roasts flesh
object_t *meat;
flag_t *ff;
meat = addob(o->pile, "chunk of roast meat");
// purposely don't use getweight!
meat->weight = o->weight;
copyflag(meat->flags, o->flags, F_CORPSEOF);
ff = hasflag(o->flags, F_OBHP);
if (ff) {
killflagsofid(meat->flags, F_OBHP);
addflag(meat->flags, F_OBHP, ff->val[1], ff->val[1], NA, NULL);
}
if (hasflag(o->flags, F_DECAY)) {
copyflag(meat->flags, o->flags, F_OBHP);
ff = hasflag(meat->flags, F_DECAY);
if (ff) {
ff->val[2] = 0;
}
}
copyflag(meat->flags, o->flags, F_TAINTED);
}
}
// 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)) {
//&& !streq(postdamname, "battered")) {
// 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, OT_NONE, NULL);
}
// 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, enum OBTYPE fromspell, object_t **newobptr) {
char throwername[BUFLEN];
char throwernamea[BUFLEN];
char realthrowername[BUFLEN];
char realthrowernamea[BUFLEN];
char obname[BUFLEN],realobname[BUFLEN];
char targetname[BUFLEN];
lifeform_t *target;
cell_t *srcloc;
int seen,seensrc,seendst;
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 stuck = B_FALSE;
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);
}
getobnametruebase(o, realobname, amt);
if (thrower) {
getlfname(thrower, throwername);
real_getlfname(thrower, realthrowername, NULL, B_NOSHOWALL, B_CURRACE);
if (isplayer(thrower)) {
strcpy(throwernamea, throwername);
strcpy(realthrowernamea, 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 {
strcpy(throwernamea, "something");
strcpy(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;
if (newobptr) *newobptr = o;
return B_TRUE;
}
}
// otherwise does player have haslos?
// identify as cursed!
}
seen = B_FALSE;
seensrc = B_FALSE;
seendst = B_FALSE;
if (haslos(player, where)) {
seendst = B_TRUE;
}
if (haslos(player, srcloc)) {
seensrc = B_TRUE;
}
if (seensrc || seendst) {
seen = B_TRUE;
}
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)) {
if (newobptr) *newobptr = o;
return B_TRUE;
}
} else {
if (o->pile == thrower->pack) {
if (touch(thrower, o)) {
if (newobptr) {
if (!isdeadob(o)) *newobptr = o;
}
return B_TRUE;
}
}
}
}
// adjust destination location in case something is in the way.
// but DONT modify 'where' yet, as we still want to say (for example)
// "the orc throws a knife at you", even if something is in the way.
haslof(srcloc, where, LOF_NEED, &newloc);
// 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];
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 {
// an object is moving on its own
snprintf(throwstring, BUFLEN, "%s %s through the air", obname, (amt == 1) ? "flies" : "fly");
}
if (target) {
int showtoward = B_FALSE;
if (!newloc && haslos(player, where)) {
// player can see the destination?.
showtoward = B_TRUE;
} else if (haslos(player, srcloc)) {
// player can see the source..
showtoward = B_TRUE;
}
if (showtoward) {
strcat(throwstring, " toward ");
strcat(throwstring, targetname);
}
}
strcat(throwstring, ".");
msg("%s", throwstring);
}
}
// now that announcement is done, adjust the destination if anyone
// is in the way.
if (newloc) {
where = newloc;
}
//taketime(thrower, SPEED_THROW);
// special case
if (thrower && target && (o->type->id == OT_JAVELINLT)) {
if (seen) {
msg("%s transforms into a bolt of lightning!", obname);
makeknown(o->type->id);
// refresh name
getobname(o, obname, 1);
}
dospelleffects(thrower, OT_S_LIGHTNINGBOLT, 8, target, NULL, where, B_UNCURSED, NULL, B_FALSE, o);
// use a charge.
if (usecharge(o) <= 0) {
// no charges left? change into a normal javelin.
o->type = findot(OT_JAVELIN);
// move it to target cell
newob = real_moveob(o, where->obpile, amt, B_FALSE);
if (newobptr) *newobptr = newob;
if (newob) {
killflagsofid(newob->flags, F_RNDCHARGES);
killflagsofid(newob->flags, F_CHARGES);
getobname(newob, obname, 1);
if (haslos(player, where)) {
msg("%s reforms on the ground.", obname);
}
}
} else {
// go back to caster's hand
if (isplayer(thrower)) {
msg("%s reappears in your hands!", obname);
} else if (cansee(player, thrower)) {
msg("%s reappears in %s%s hands!", obname, throwername, getpossessive(throwername));
}
}
return B_FALSE;
}
if (thrower && isplayer(thrower)) {
addflag(o->flags, F_PLAYERMISSILE, B_TRUE, NA, NA, NULL);
}
// 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, B_INCLUDECENTRE, thrower, NULL, NULL, NULL);
} 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, thrower);
} else if (o->type->id == OT_ASHINVIS) {
int radius;
char buf[BUFLEN];
// make everyone invisible
sprintf(buf, "%s dispers%s into twinkling light!", 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, DT_ORTH, UNI_SHADELIGHT, C_RANDOM, OT_S_INVISIBILITY, radius, B_TRUE, buf, "A cloud of twinkling lights appear!", B_FALSE, NULL, B_INCLUDECENTRE);
} 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, DT_ORTH, UNI_SHADELIGHT, C_MAGENTA, OT_S_SLEEP, radius, B_TRUE, buf, "A wispy mist appears!", B_FALSE, NULL, B_INCLUDECENTRE);
} else if (o->type->id == OT_SALT) {
int dist;
dist = getcelldist(srcloc, where);
if (target && (dist <= 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 {
int donesalt = B_FALSE;
if (!target && (dist <= 1)) {
object_t *oo;
for (oo = where->obpile->first ; oo ; oo = oo->next) {
if ((oo->type->obclass->id == OC_CORPSE) && !hasflag(oo->flags, F_SALTED)) {
flag_t *decayflag;
decayflag = hasflag(oo->flags, F_DECAY);
if (haslos(player, srcloc)) {
char corpsename[BUFLEN];
getobname(oo, corpsename, oo->amt);
msg("%s %s covered in salt!", corpsename, (amt == 1) ? "is" : "are");
}
if (decayflag) {
// stop decaying.
decayflag->val[2] = 0;
}
addflag(oo->flags, F_SALTED, B_TRUE, NA, NA, NULL);
donesalt = B_TRUE;
}
}
}
if (!donesalt) {
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);
if (newobptr) *newobptr = NULL;
return B_FALSE;
}
if (thrower) {
object_t *tempo;
// 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);
if (newobptr) *newobptr = o;
return B_FALSE;
}
// thrower had a ring of deceleration?
tempo = hasequippedobid(thrower->pack, OT_RING_DECELERATION);
if (tempo) {
cell_t *retcell[MAXRETCELLS];
int nretcells;
if (seensrc) {
msg("%s suddenly slows down!", obname);
makeknown(tempo->type->id);
}
// change dest cell.
calcbresnham(where->map, thrower->cell->x, thrower->cell->y,
where->x, where->y, retcell, &nretcells );
if (nretcells > 1) {
// 1 cell in front of thrower.
where = retcell[1];
}
// also slow it down
speed = 1;
}
}
// ... in case the target cell changed...
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 %s reflected away from %s!", obname, (amt == 1) ? "is" : "are", 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);
// target has 'nudge missiles' spell? penalty, and the missile
// gets slightly slowed.
if (target && hasactivespell(target, OT_S_SLOWMISSILES)) {
myroll += 10;
if (speed > 1) speed--;
}
// easier to hit with faster projectiles.
myroll -= (speed*2);
// blessed projectile vs undead? 20% bonus.
if (isblessed(o) && isundead(target)) {
myroll -= 20;
}
// "of homing" missiles always hit.
if (hasflag(o->flags, F_HOMING)) {
myroll = acc;
}
// 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?
if (thrower != target) {
int dist;
dist = getcelldist(srcloc, where);
sprintf(attackname, "%s", obname);
difficulty = 60 + (speed*10);
// harder the closer the projectile was shot from.
if (dist <= 1) {
difficulty += 40;
} else if (dist == 2) {
difficulty += 10;
}
if (check_for_block(thrower, target, getthrowdam(o) + speed,
DT_PROJECTILE, difficulty, attackname, B_RANGED)) {
announcedmiss = B_TRUE;
youhit = B_FALSE;
missiledam += ((speed*2)+rnd(1,4));
}
}
}
// 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;
}
if (hasfreeaction(target) && skillcheck(target, SC_DODGE, 100+(speed*5), dodgemod)) {
// if we passed the dodge check, now see if we caught it...
// 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, 100 + (speed*10), catchmod)) {
if (db) dblog("target passed catch check.");
willcatch = B_TRUE;
} else {
// 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) {
if (isplayer(target)) {
msg("%s recoil in fear!", targetname);
} else {
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);
if (newobptr) *newobptr = o;
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);
adjustdamforblessings(NULL, &throwdam, target, o->blessed);
//dam = (int)((float)throwdam * multiplier);
dam = throwdam + (speed/2);
// firearm adjustments
if (firearm) {
// firearms at pointblank range? +50% damage
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, NULL) || !skillcheck(target, SC_SLIP, f->val[0], 0)) {
willtangle = B_TRUE;
}
}
reduceamt = getarmourdamreduction(target, o, dam, DT_PROJECTILE);
applyarmourdamreduction(target, o, reduceamt, &dam, DT_PROJECTILE);
// 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);
}
if (streq(realthrowernamea, "something")) {
snprintf(damstring, BUFLEN, "%s",realobname);
} else {
snprintf(damstring, BUFLEN, "%s (%s by %s)",realobname,throwverbpast, realthrowernamea);
}
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_DAMADJUST, o,
B_RETALIATE, NULL, B_DAMEFFECTS, BP_NONE, B_NOCRIT);
}
if (reduceamt && (speed >= 3)) {
applyarmourdamage(target, o, reduceamt, DT_PROJECTILE, NULL);
}
wepeffects(o->flags, target->cell, hasflag(o->flags, F_DAM), dam, B_FALSE);
missiledam += ((speed*2)+rnd(1,4));
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);
}
}
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_DOANNOUNCE, B_DODAM);
}
}
// 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.
if (target && lfhasflag(target, F_ADHESIVE)) {
newob = real_moveob(o, target->pack, amt, B_FALSE);
stuck = B_TRUE;
if (cansee(player, target)) {
if (thrower) {
msg("^%c%s sticks to %s!", getlfcol(thrower, CC_BAD), obname, targetname);
} else {
msg("%s sticks to %s!", obname, targetname);
}
}
killflagsofid(newob->flags, F_PLAYERMISSILE);
} else {
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 (thrower) {
addflag(newob->flags, F_THROWNBY, thrower->id, NA, NA, NULL);
}
if (!stuck) {
if (willshatter(newob->material->id)) {
char dambuf[BUFLEN];
snprintf(dambuf, BUFLEN, "%s (%s by %s)",obname,throwverbpast, realthrowernamea);
shatter(newob, youhit, dambuf, thrower);
if (thrower && isplayer(thrower)) {
angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT);
pleasegodmaybe(R_GODFIRE, 1);
}
} else if (hasflag(newob->flags, F_MISSILEALWAYSDIES)) {
killob(newob);
newob = NULL;
} 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, thrower);
} else {
// ie things like bruising
real_takedamage(newob, 0, DT_BASH, B_FALSE, thrower);
}
}
if (thrower && hasactivespell(thrower, OT_S_WHATGOESUP)) {
// 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 (newob && !isdeadob(newob)) {
killflagsofid(newob->flags, F_THROWNBY);
}
/*
if (firearm && outofammo && isplayer(thrower)) {
char buf[BUFLEN];
getobname(firearm, buf, 1);
msg("Your %s is now out of ammo.", noprefix(buf));
}
*/
if (!fromspell && thrower && isplayer(thrower)) {
angergodmaybe(R_GODMAGIC, 10, GA_HERESY);
}
// if you sa something miss you, stop eating/training/digging/etc
//
// we use 'announcedmiss' here because we shouldn't wake up we were
// asleep (ie. couldn't see the missile missing us)
// If it hit us, losehp() will have taken care of the interruption.
if (target && isplayer(target) && announcedmiss) {
interrupt(target);
}
if (newobptr) *newobptr = newob;
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;
int onground = B_FALSE;
if (hasflag(o->flags, F_DEAD)) return;
location = getoblocation(o);
if (o->pile->owner) {
owner = o->pile->owner;
getlfname(owner, ownername);
} else {
owner = NULL;
}
if (o->pile->where) {
onground = B_TRUE;
}
if (haslos(player, location)) {
getobname(o, obname, o->amt);
} else {
strcpy(obname, "?some_ob?");
}
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;
}
}
// objects combine?
f = hasflag(o->flags, F_NUMCONVERT);
if (f && (o->amt >= f->val[0])) {
int newamt,i;
enum OBTYPE newoid = OT_NONE;
obpile_t *op;
char *newobname = NULL;
flagpile_t *fp;
newamt = o->amt / f->val[0];
op = o->pile;
fp = addflagpile(NULL, NULL);
if (strlen(f->text)) {
newobname = strdup(f->text);
} else {
newoid = f->val[1];
}
// some flags will be inherited by the new object
// remember certain flags from the objects which are combining.
copyflag(fp, o->flags, F_FILLPOT);
copyflag(fp, o->flags, F_LINKRACE);
killflagsofid(o->flags, F_FILLPOT);
killflagsofid(o->flags, F_LINKRACE);
// remove some instances of the original object.
removeob(o, newamt * f->val[0]);
for (i = 0; i < newamt; i++) {
object_t *newob = NULL;
if (newobname) {
newob = addob(op, newobname);
} else {
newob = addobfast(op, newoid);
}
if (newob) {
killflagsofid(newob->flags, F_FILLPOT);
killflagsofid(newob->flags, F_LINKRACE);
// restore remembered flags
copyflags(newob->flags, fp, NA);
}
}
if (newobname) free(newobname);
killflagpile(fp);
return;
}
// mixed blood loses other properties.
// shoudl never happen - but check just in case
if (countflagsofid(o->flags, F_LINKRACE) > 1) {
killflagsofid(o->flags, F_FILLPOT);
}
// mosnters pretending to be objects?
if (onground && hasflag(o->flags, F_ISMONSTER)) {
f = hasflag(o->flags, F_ISMONSTER);
if (strlen(f->text)) {
int dist;
char dbuf[BUFLEN];
readuntil(dbuf, f->text, ',');
dist = atoi(dbuf);
// player within reveal distance?
if ((getcelldist(player->cell, location) <= dist) && !haslos(player, location)) {
lifeform_t *newlf;
newlf = reveal_pretendob(o);
if (newlf) {
turntoface(newlf, player->cell);
aiattack(newlf, player, aigetchasetime(newlf));
}
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;
int disttocheck = 3;
// check if we are near undead (ie. within 2 sq)
for (y = ourcell->y - disttocheck; y <= ourcell->y + disttocheck; y++) {
for (x = ourcell->x - disttocheck; x <= ourcell->x + disttocheck; 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;
setlosdirty(player);
}
//calclight(ourcell->map);
//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 object repel
sg = hasobwithflagval(location->obpile, F_REPELBLESSED, o->blessed, NA, NA, NULL);
if (sg) {
char sgname[BUFLEN];
cell_t *newc;
int canseeloc = B_FALSE;
condset_t cs;
initcondv(&cs, CC_IMPASSABLE, B_FALSE, NA, CC_NONE);
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, &cs, B_ALLOWEXPAND);
if (newc) {
//flag_t *inv;
object_t *newob = NULL;
// make sure the object doesn't take damage
//inv = addflag(o->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL);
real_fireat(NULL, o, o->amt, newc, 1, NULL, B_TRUE, OT_NONE, &newob);
if (newob && canseeloc) {
// player now knows that this is blessed
newob->blessknown = B_TRUE;
}
// no other effects for the object this turn.
// (in case it died when thrown away)
return;
} 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 (isplayer(o->pile->owner)) {
// sacred ground bless/curse detect
sg = hasobwithflagval(location->obpile, F_REPELBLESSED, NA, o->blessed, NA, NULL);
if (sg && !o->blessknown) {
// announce
msg("Your %s feel%s very %s!", noprefix(obname), (o->amt == 1) ? "s" : "", (o->blessed == B_BLESSED) ? "warm" : "cold");
// player now knows that this is blessed/cursed
o->blessknown = B_TRUE;
}
}
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 (owner && !onground) {
if (hasflag(o->flags, F_DIMONDISTURB)) {
f = hasflag(o->flags, F_PRODUCESLIGHT);
getobname(o, obname, o->amt);
if (f) f->val[0]--;
if (!f || (f->val[0] <= 0)) {
if (isplayer(owner)) {
msg("Your %s dim%s and crumbles.",noprefix(obname),
(o->amt == 1) ? "s" : "");
}
removeob(o, ALL);
return;
} else {
if (isplayer(owner)) {
msg("Your %s dim%s slightly.",noprefix(obname),
(o->amt == 1) ? "s" : "");
}
}
}
}
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) {
int chance;
f = retflag[rnd(0,nretflags-1)];
chance = f->val[0];
// closed shops don't make cash register noises!
if (hasflag(o->flags, F_SHOP) && shopisclosed(o)) {
chance = 0;
}
if (pctchance(chance)) {
// 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, f->val[2], 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)) {
enum OBTYPE burnoid = OT_NONE;
if (haslos(player, location)) {
if (!lfhasflag(player, F_DONEBURNMSG)) {
msg("The %s burns!", location->type->name);
addflag(player->flags, F_DONEBURNMSG, B_TRUE, NA, NA, NULL);
needredraw = B_TRUE;
}
}
// burn by changing celltype...
switch (location->type->id) {
case CT_GRASS:
setcelltype(location, CT_DIRT);
burnoid = OT_ASH;
break;
case CT_FLOORWOOD:
setcelltype(location, CT_CORRIDOR);
if (location->map->depth < location->map->region->rtype->maxdepth) {
burnoid = OT_HOLEINGROUND;
}
break;
default:
// stone floor
setcelltype(location, CT_CORRIDOR);
burnoid = OT_ASH;
break;
}
if (burnoid != OT_NONE) {
addobfast(location->obpile, burnoid);
}
//setcelltype(location, CT_CORRIDOR);
}
}
// checks based on object
if (o->type->id == OT_VINE) {
lifeform_t *creator;
int willvanish = B_FALSE;
// creator no longer has los?
creator = getobcreatedby(o);
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;
}
}
}
if (location && onground) {
enum TEMPERATURE temp;
temp = getcelltemp(location, NULL);
if (temp <= T_VCOLD) {
// stuff freezes
if ((o->type->material->id == MT_WATER) || (o->type->id == OT_SPLASHWATER) || (o->type->id == OT_FOUNTAIN)) {
if (hasflagval(o->flags, F_DTCONVERT, DT_COLD, NA, NA, NULL)) {
takedamage(o, 1, DT_COLD, NULL);
} else {
changemat(o, MT_ICE);
}
if (haslos(player, location)) {
msg("%s freeze%s!", obname, OB1(o, "s",""));
}
}
}
// affect hot
i = isheatable(o);
if (i) {
switch (temp) {
case T_VCOLD:
makecool(o, 1, 3);
break;
case T_COLD:
makecool(o, 1, 1);
break;
case T_HOT:
makehot(o, i, 10);
break;
case T_VHOT:
makehot(o, pctof(150,i), 20);
break;
default: break;
}
}
if ((getmaterialstate(o->type->material->id) == MS_LIQUID) || (o->type->id == OT_SPLASHWATER)) {
if (location->type->absorbent && !hasflag(o->flags, F_NOABSORB)) {
if (haslos(player, location)) {
msg("%s %s absorbed into the %s.", obname,
OB1(o,"is","are"), location->type->name);
}
removeob(o, ALL);
return;
}
}
getflags(o->flags, retflag, &nretflags, F_GENERATES, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
if (pctchance(f->val[0])) {
cell_t *where;
if (f->val[2] == NA) {
where = location;
} else {
condset_t cs;
initcondv(&cs, CC_IMPASSABLE, B_FALSE, NA, CC_NONE);
where = getrandomadjcell(location, &cs, B_NOEXPAND);
}
if (where) {
if (f->val[1] == 0) {
addob(where->obpile, f->text);
} else {
addobsinradius(where, f->val[1], DT_COMPASS, f->text, B_TRUE, B_INCLUDECENTRE,
NULL, NULL, NULL, NULL);
}
}
}
}
}
// check each flag for this object...
getflags(o->flags, retflag, &nretflags, F_ACTIVATED, F_EDIBLE, F_EXPLODEONDEATH, F_MATCONVERT,
F_HOT, F_KNOCKAWAY, F_LFINSIDE, 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 (o->type->id == OT_JETSKATES) {
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 are out of power!",noprefix(obname));
// you know it's out
f = hasflag(o->flags, F_CHARGES);
f->known = B_TRUE;
}
}
turnoff(NULL, o);
} else if (chargeleft <= 10) { // about to run out of power?
if (isplayer(owner)) {
msg("Your %s splutter.", 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, NULL);
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_EXPLODEONDEATH) {
if (hasflag(o->flags, F_OBHPDRAIN)) {
if (o->pile->where && haslos(player, o->pile->where)) {
// pass a perception check to see it sparking...
if (skillcheck(player, SC_SEARCH, 75, 0)) {
msg("^w%s sparks.^n", obname);
}
}
}
}
if ((f->id == F_KNOCKAWAY) && (o->pile->where)) {
lifeform_t *lf,*creator;
object_t *oo;
lf = o->pile->where->lf;
creator = getobcreatedby(o);
if (lf && (lf != creator) && (getmaterialstate(getlfmaterial(lf)) == MS_SOLID)) {
char damstring[BUFLEN];
if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("^%cPowerful winds pummel %s!^n", getlfcol(lf, CC_BAD), lfname);
}
// lfs here take damage
if (creator) {
char cname[BUFLEN];
getlfname(creator, cname);
// ie. "the djinn's whirlwind"
sprintf(damstring, "%s%s %s", cname, getpossessive(cname), noprefix(obname));
} else {
// ie. "a whirlwind"
sprintf(damstring, "%s", obname);
}
losehp(lf, roll(f->text), DT_PROJECTILE, creator, damstring);
// lfs then get knocked away (and might take further damage from hitting something)
knockback(lf, getrandomdir(DT_COMPASS), f->val[0], creator, f->val[1],
B_DOANNOUNCE, B_DODAM);
}
// objects get knocked away
for (oo = o->pile->where->obpile->first ; oo ; oo = oo->next) {
if (oo == o) continue;
// note: f_nopickup obs will still get moved!
if (hasflag(oo->flags, F_COSMETIC) || hasflag(oo->flags, F_CLIMBABLE) ||
(hasflag(oo->flags, F_TRAIL)) ||
(getmaterialstate(o->material->id) != MS_SOLID) ||
(hasflag(oo->flags, F_KNOCKAWAY)) || // don't affect other wind knockaway objects
(oo->type->obclass->id == OC_BUILDING)) {
} else {
cell_t *retcell[MAXRETCELLS],*c;
int nretcells;
getradiuscells(oo->pile->where, f->val[0], DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0);
if (nretcells) {
c = retcell[rnd(0,nretcells-1)];
fireat(NULL, oo, oo->amt, c, f->val[2], NULL);
}
}
}
}
if (f->id == F_OBHPDRAIN) {
enum DAMTYPE damtype;
int damamt;
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];
}
damamt = f->val[0];
// special case - corpses don't decay when on ice or on fire.
if (o->type->id == OT_CORPSE) {
if (hasflag(o->flags, F_ONFIRE)) {
doit = B_FALSE;
//} else if (hasobofmaterial(o->pile, MT_ICE)) {
} else if (location) {
enum TEMPERATURE ct;
ct = getcelltemp(location, NULL);
if (ct <= T_COLD) {
doit = B_FALSE;
} else if (ct >= T_WARM) {
// ie. warm = +3 dam
// ie. hot = +6 dam
// ie. vhot = +9 dam
damamt += ((ct - T_NORMAL)*3);
}
}
}
// stuff doesn't melt in cold cells
if ((damtype == DT_MELT) && location) {
enum TEMPERATURE ct;
ct = getcelltemp(location, NULL);
if (ct <= T_CHILLY) {
doit = B_FALSE;
} else if (ct >= T_WARM) {
// ie. warm = +3 dam
// ie. hot = +6 dam
// ie. vhot = +9 dam
damamt += ((ct - T_NORMAL)*3);
}
}
// special case - fire doesn't naturally die down
// if there are larger fires in adjacent cells.
if ((o->type->material->id == MT_FIRE) && (o->pile->where)) {
int dir,myhp;
cell_t *c;
object_t *otherfire;
myhp = getobhp(o, NULL);
for (dir = DC_N; dir <= DC_NW; dir++) {
c = getcellindir(location, dir);
if (c) {
otherfire = hasobofmaterial(c->obpile, MT_FIRE);
if (otherfire && (getobhp(otherfire, NULL) > myhp)) {
doit = B_FALSE;
}
}
}
}
if (doit) {
takedamage(o, f->val[0], damtype, NULL);
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, &ccwalkable, B_NOEXPAND);
} else {
lfloc = obloc;
}
}
if (lfloc) {
char *revivetext;
revivetext = strdup(f->text);
if ((f->val[2] == R_ZOMBIE) || (f->val[2] == R_ZOMBIECON)) {
int contagious = B_FALSE;
if (f->val[2] == R_ZOMBIECON) {
contagious = B_TRUE;
}
// turn into a zombified version of itself.
lf = makezombie(o, 10, revivetext, NULL); // this will do the announcement
if (lf && contagious) {
addflag(lf->flags, F_HITCONFER, F_REVIVETIMER, SC_POISON, 165, NULL);
addflag(lf->flags, F_HITCONFERVALS, 0, 1, R_ZOMBIECON, "rises up as a zombie!");
}
addflag(lf->flags, F_HATESALL, B_TRUE, NA, NA, NULL);
} else {
// revive!
lf = addmonster(lfloc, f->val[2], NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL);
// gain flags form corpse
copyflag(lf->flags, o->flags, F_CANWILL);
copyflag(lf->flags, o->flags, F_CANCAST);
copyflag(lf->flags, o->flags, F_JOB);
// no more xp.
killflagsofid(lf->flags, F_XPVAL);
addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL);
// corpse vanishes
removeob(o, o->amt);
// announce
if (haslos(player, lfloc) || haslos(player, obloc)) {
msg("^W%s %s!^n", obname, revivetext);
interrupt(player);
}
}
free(revivetext);
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, 100 + getdistspotmod(player, obloc), 0)) {
msg("%s twitches.", obname);
}
}
}
}
// lf hiding inside?
if (f->id == F_LFINSIDE) {
cell_t *obloc = NULL;
obloc = getoblocation(o);
if (haslos(player, obloc) && (o->pile->owner == player)) {
// pass a perception chekc to see it moving...
if (skillcheck(player, SC_SEARCH, 100 + getdistspotmod(player, obloc), 0)) {
if (o->pile->owner == player) {
msg("%s moves slightly.", obname);
} else {
msg("Your %s moves slightly.", noprefix(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], getobcreatedby(o));
//if (hasflag(o->flags, F_DEAD)) return;
}
// is object on fire or hot?
if ((f->id == F_ONFIRE) || (f->id == F_HOT)) {
int willputout = 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)) {
willputout = B_TRUE;
break;
}
}
if (willputout) {
if (f->id == F_ONFIRE) {
extinguish(o);
continue;
} else if (f->id == F_HOT) {
killflag(f);
continue;
}
} else {
// if it hasn't been extinguished, the object burns
if (f->id == F_ONFIRE) {
if (isweapon(o) && isequipped(o)) {
// don't deal damage for flaming equipped weapons. if we do,
// it will make them hot, and then they'll get dropped. the idea
// with weapons is that only the blade is on fire.
} else {
takedamage(o, rnd(2,4), DT_FIRE, NULL); // 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
strrep(&locbuf, "An ", "", NULL);
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 (!ourcell->type->solid && !hasobwithflag(ourcell->obpile, F_DEEPWATER)) {
splash = addob(ourcell->obpile, "splash of water");
}
}
}
} // end for each object flag
// do this one after others.
if (o->pile->where) {
f = hasflag(o->flags, F_OBMOVESRANDOMLY);
if (f) {
cell_t *newcell,*c;
object_t *adjob[MAXPILEOBS*8];
int nadjobs = 0;
int possdir[8];
int npossdir = 0;
int dir;
// before moving, get a list of other adjacent objects of the same type
for (dir = DC_N; dir <= DC_NW; dir++) {
c = getcellindir(o->pile->where, dir);
if (c) {
object_t *oo;
for (oo = c->obpile->first ; oo ; oo = oo->next) {
if (oo->type->id == o->type->id) {
adjob[nadjobs++] = oo;
}
}
}
}
// now pick a direction to move.
for (dir = DC_N; dir <= DC_NW; dir++) {
c = getcellindir(o->pile->where, dir);
if (c) {
int ok = B_TRUE;
if (c->type->solid && (f->val[0] != B_TRUE)) {
ok = B_FALSE;
}
if (ok) {
possdir[npossdir++] = dir;
}
}
}
if (npossdir) {
int i;
dir = possdir[rnd(0,npossdir-1)];
newcell = getcellindir(o->pile->where, dir);
if (newcell->type->solid) {
// this should always kill it!
damagecell(newcell, newcell->hp, DT_DIRECT, NULL);
}
moveob(o, newcell->obpile, o->amt);
// now move other adjacent objects in the same direction (if we can).
for (i = 0; i < nadjobs; i++) {
int dead = B_FALSE;
c = getcellindir(adjob[i]->pile->where, dir);
if (c) {
if (c->type->solid) {
if (f->val[0] == B_TRUE) {
// this should always kill it!
damagecell(c, c->hp, DT_DIRECT, NULL);
} else {
killob(adjob[i]);
dead = B_TRUE;
}
}
if (!dead) {
moveob(adjob[i], c->obpile, adjob[i]->amt);
}
}
}
}
}
}
checkflagpile(o->flags);
}
int touch_battle_spoils(object_t *o) {
int ndone = 0;
ndone = killflagsofid(o->flags, F_BATTLESPOILS);
if (o->pile->where) {
object_t *oo;
for (oo = o->pile->first; oo ; oo = oo->next) {
ndone += killflagsofid(oo->flags, F_BATTLESPOILS);
}
}
return ndone;
}
// 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, object_t *trappedob) {
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_CAREFULMOVE)) mod += 5;
mod += getskill(lf, SK_ENGINEERING);
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 (lf && (getskill(lf, SK_ENGINEERING) == PR_MASTER)) {
avoided = B_TRUE;
}
if (oid == OT_TRAPWIND) {
// can't be dodged
dospelleffects(NULL, OT_S_GUSTOFWIND, 10, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE, NULL);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPDOORFALL) {
if (lf) {
if (isplayer(lf)) {
msg("The door falls inwards%s", avoided ? " but misses you." : " and lands on top of you!");
} else if (cansee(player, lf)) {
if (avoided) {
msg("A door falls inwards!");
} else {
msg("A door falls inwards onto %s!",lfname);
}
}
if (!avoided) {
losehp(lf, roll("2d4"), DT_CRUSH, NULL, "a falling door trap");
fall(lf, NULL, B_FALSE);
}
} else {
if (haslos(player, c)) {
msg("A door falls inwards!");
}
}
if (trappedob) {
flag_t *f2;
char fallobtext[BUFLEN];
strcpy(fallobtext, "");
f2 = hasflag(trappedob->flags, F_DOORFALLOB);
if (f2) {
strcpy(fallobtext, f2->text);
} else {
switch (trappedob->material->id) {
case MT_WOOD: strcpy(fallobtext, "plank of wood"); break;
case MT_METAL: strcpy(fallobtext, "sheet of metal"); break;
default: break;
}
}
if (strlen(fallobtext)) {
object_t *newob;
newob = addob(c->obpile, fallobtext);
if (newob) {
flag_t *f, *f2;
// inherit weight
newob->weight = trappedob->weight;
// inherit hp from original ob
f = hasflag(trappedob->flags, F_OBHP);
if (f) {
f2 = hasflag(newob->flags, F_OBHP);
if (f2) {
f2->val[0] = f->val[0];
f2->val[1] = f->val[1];
} else {
addflag(newob->flags, F_OBHP, f->val[0], f->val[1], NA, NULL);
}
}
}
}
}
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) {
addob(c->obpile, "poisoned needle");
} else {
poison(lf, rnd(10,20), P_VENOM, 1, "a needle trap", R_NONE, B_TRUE);
}
} 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, NULL);
} 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, &ccwalkable, 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, B_TRUE);
}
}
} else if (oid == OT_TRAPALARM) {
// 50 is stupidly loud, intentionally
noise(c, NULL, NC_OTHER, 50, "a blaring siren!", "A blaring siren goes off!");
} else if (oid == OT_TRAPNOISE) {
noise(c, NULL, NC_OTHER, 3, "a loud squeak!", "The hinges squeak loudly!");
} 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, NULL);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPFIRE) {
dospelleffects(NULL, OT_S_FLAMEPILLAR, 3, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE, NULL);
if (lf && avoided) {
cell_t *escapeto = NULL;
escapeto = getrandomadjcell(c, &ccwalkable, B_NOEXPAND);
if (escapeto) {
if (isplayer(lf)) {
msg("You leap out of the way!");
} else if (cansee(player, lf)) {
msg("%s leaps out of the way!", lfname);
}
movelf(lf, escapeto, B_TRUE);
}
}
if (lf && isplayer(lf)) {
pleasegodmaybe(R_GODFIRE, 10);
}
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPLIGHTNING) {
// can't be dodged
dospelleffects(NULL, OT_S_CHAINLIGHTNING, 3, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE, NULL);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPGAS) {
// can't be dodged
dospelleffects(NULL, OT_S_CLOUDKILL, 1, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE, NULL);
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, NULL);
if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards
} else if (oid == OT_TRAPSUMMON) {
cell_t *cc;
// can't be dodged
cc = getrandomadjcell(c, &ccwalkable, 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);
}
}
}
}
// kill all objcts in the pile, as long as it doesn't contain
// anything important like stairs.
//
// returns FALSE if we cleared all objects, or didn't need to.
// returns TRUE if there were objects which we couldn't clear
int trytokillobs(obpile_t *op) {
object_t *o, *nexto;
int failed = B_FALSE;
// objects here? destroy them if we can.
for (o = op->first ; o ; o = nexto) {
nexto = o->next;
if (hasflag(o->flags, F_IMPORTANT)) {
failed = B_TRUE;
continue;
}
killob(o);
}
return failed;
}
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;
}
// try to unjam a door/container
//
// returns TRUE if the unjam was successful
int unjam(lifeform_t *lf, object_t *o) {
char obname[BUFLEN];
int openit = B_FALSE;
int amt = 0;
flag_t *f;
cell_t *c;
c = getoblocation(o);
f = hasflag(o->flags, F_JAMMED);
if (!f) {
return B_TRUE;
}
getobname(o, obname, 1);
if (skillcheck(lf, SC_STR, f->val[2], 0)) {
amt = 1;
}
// loosen a bit
if (amt > 0) {
f->val[0] -= amt;
}
if (isplayer(lf)) {
if (amt > 0) {
if (f->val[0] > 0) {
msg("The %s moves slightly but remains jammed.", noprefix(obname));
}
// otherwise we opened it! unjammed doors will be announced seperately.
if (!isdoor(o, NULL)) {
msg("You force %s open!",obname);
}
} else {
msg("You cannot budge the jammed %s.", noprefix(obname));
}
} else {
// ai chasing someone and not strong enough to unjam the door?
if (isdoor(o, NULL) && (amt == 0) && willattackdoors(lf)) {
attackcell(lf, c, B_TRUE);
return B_FALSE;
} else {
// try to force it
if (cansee(player, lf)) {
char lfname[BUFLEN];
getlfname(lf, lfname);
msg("%s tries to open %s, but fails.", lfname, obname);
} else if (haslos(player, c)) {
msg("Something tries to open %s.", obname);
} else {
if (isdoor(o, NULL)) {
char noisebuf[BUFLEN];
sprintf(noisebuf, "%s jiggling against its hinges.", obname);
noise(c, NULL, NC_OTHER, SV_SHOUT, noisebuf, NULL);
}
}
}
}
if (f->val[0] <= 0) {
killflag(f);
f = NULL;
openit = B_TRUE;
} else {
openit = B_FALSE; // don't open the door
}
return openit;
}
// 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);
}
}
if (hasflag(ot->flags, F_ISMONSTER) && !hasflag(ot->flags, F_IMPASSABLE)) {
printf("ERROR in object '%s' - objects with f_ismonster MUST also have f_impassable too!\n", ot->name);
goterror = B_TRUE;
}
// remember buildings
if (ot->obclass->id == OC_BUILDING) {
if (hasflag(ot->flags, F_RARITY)) {
buildingusage[nbuildingusage].oid = ot->id;
buildingusage[nbuildingusage].count = 0;
nbuildingusage++;
assert(nbuildingusage <= MAXBUILDINGTYPES);
}
}
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;
}
}
}
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_DOOR) && !hasflag(ot->flags, F_DOORFALLOB)) {
printf("ERROR in object '%s' - door objects must have F_DOORFALLOB.\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_HOMEOB);
if (f && (f->val[0] == NA)) {
printf("ERROR in object '%s' - has F_HOMEOB but missing pctchance in v0.\n", ot->name);
goterror = B_TRUE;
}
f = hasflag(ot->flags, F_HOMELEVOB);
if (f) {
if ((f->val[0] == NA) || (f->val[1] == NA)) {
printf("ERROR in object '%s' - has F_HOMELEVOB but missing count in v0/v1.\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;
}
}
if (hasflag(ot->flags, F_OPERONOFF) && !hasflag(ot->flags, F_OPERABLE)) {
printf("ERROR in object '%s' - has F_OPERONOFF but not F_OPERABLE.\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_ATTREQ);
if (f && (f->val[2] != NA) && !strlen(f->text)) {
printf("ERROR in object '%s' - has val2(BONUS_AT) but no ->text.\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;
}
// note: we don't check for F_IMMUTABLE here because we don't
// want to (for example) give away the flag on a weapon if the
// player attacks a door/wall with it, and doesn't know that
// it's immutable yet.
int wepdullable(object_t *o) {
enum DAMTYPE dt;
if (!o) return B_FALSE;
if (o->blessed) return B_FALSE;
// exceptions
switch (o->type->id) {
case OT_NANOBLADE:
case OT_LASERSWORD:
return B_FALSE;
default:
break;
}
if (hasflagval(o->flags, F_USESSKILL, SK_WHIPS, NA, NA, NULL)) {
return B_FALSE;
}
dt = getdamtype(o);
switch (dt) {
case DT_PIERCE:
case DT_SLASH:
return B_TRUE;
default:
break;
}
return B_FALSE;
}
int cancrush(lifeform_t *lf, object_t *o) {
flag_t *f;
f = hasflag(o->flags, F_CRUSHABLE);
if (f) {
enum LFSIZE crushsize;
crushsize = f->val[0];
if (getlfsize(lf) >= crushsize) {
return B_TRUE;
}
}
return B_FALSE;
}
int willrust(object_t *o) {
if (o->material->id == MT_METAL) {
if (!hasflag(o->flags, F_WATERPROOF)) {
return B_TRUE;
}
}
return B_FALSE;
}
int willshatter(enum MATERIAL mat) {
switch (mat) {
case MT_GLASS:
case MT_ICE:
case MT_CRYSTAL:
return B_TRUE;
default: break;
}
return B_FALSE;
}
enum SKILL getclassloreskill(enum OBCLASS ocid) {
switch (ocid) {
case OC_POTION: return SK_LORE_CHEMISTRY;
case OC_SCROLL: return SK_LORE_LANGUAGE;
case OC_BOOK: return SK_LORE_LANGUAGE;
case OC_WAND: return SK_LORE_ARCANA;
case OC_RING: return SK_LORE_RELICS;
case OC_AMULET: return SK_LORE_RELICS;
default: break;
}
return SK_NONE;
}
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;
}
// extra crit chance if you can sever victim's limbs
if (victim && lfhasflag(victim, F_CANSEVER)) {
if (o && (getdamtype(o) == DT_SLASH)) {
chance += 30;
}
}
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 = max;
break;
case B_CURSED:
howlong = min;
break;
default: // ie. B_UNCURSED
howlong = rnd(min,max);
break;
}
return howlong;
}
enum BODYPART getequiploc(object_t *o) {
flag_t *f;
f = isequipped(o);
if (!f) return BP_NONE;
return f->val[0];
}
// 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_BRICK:
col = C_BRICK;
break;
case MT_METAL:
col = C_METAL;
break;
case MT_WOOD:
col = C_DARKBROWN;
break;
case MT_LEATHER:
col = C_BROWN;
break;
case MT_FIRE:
col = C_RED;
break;
case MT_BLOOD:
col = C_DARKRED;
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:
col = C_DARKGREEN;
break;
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,plus=0;
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, B_TRUE);
sumflags(thrower->flags, F_ACCURACYMOD, &plus, NULL, NULL);
acc += plus;
// for each cell travelled after the first, 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) - 1;
limit(&howfar, 0, NA);
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 (getfeetheight(thrower) > getfeetheight(where->lf)) {
acc += 15;
}
}
// blessed/cursed firearms affect accuracy
if (firearm) {
if (isblessed(firearm)) {
acc += 10;
} else if (iscursed(firearm)) {
acc -= 10;
}
}
limit(&acc, 0, NA);
return acc;
}
int wouldgivepenalty(lifeform_t *lf, flagpile_t *fp) {
flag_t *retflag[MAXCANDIDATES];
int nretflags,i,pen = 0;
getflags(lf->flags, retflag, &nretflags, F_ARMOURPENALTY, F_SHIELDPENALTY, F_NONE);
for (i = 0; i < nretflags; i++) {
pen += adjustarmourpenalty(lf, retflag[i]->val[0]);
pen += adjustarmourpenalty(lf, retflag[i]->val[1]);
}
if (pen) return B_TRUE;
return B_FALSE;
}