#include #include #include #include #include #include #include #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' snprintf(replacefrom, BUFLEN, "%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; } else if (strstarts(p, "vrare ")) { wantrarity = RR_VERYRARE; p += strlen("vrare "); 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 "); snprintf(potname, BUFLEN, "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 (strstarts(p, "random thing")) { char buf[BUFLEN]; cell_t *cc; condset_t cs; initcond(&cs); cc = getobpilelocation(where); ot = real_getrandomob(cc, buf, NA, NA, wantrarity, B_TRUE, &cs); 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 { // the main thing which will cause this is placing objects // in a radius - eg. clouds of steam. if (localname) free(localname); return NULL; } } } // 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 (pileisinshop(where)) { 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->mass = ot->mass; 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]; snprintf(ammostring, BUFLEN, "%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)) { int isnew = B_FALSE; while (!isnew) { 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; } isnew = B_TRUE; // make sure it's not a school we already have for (n = 0; n < i; n++) { if (school[n] == school[i]) { isnew = B_FALSE; 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->mass = corpserace->mass; 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->mass = corpserace->mass * 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->mass = pctof(8, corpserace->mass); limitf(&o->mass, 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->mass = corpserace->mass / 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); } } */ // affect other objects here. o = obaffectsothers(o); } // 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->mass = 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 = getobmass(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]; snprintf(buf, BUFLEN, "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; int b; 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 foreach_bucket(b) { for (f = o->flags->first[b] ; 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) { int b; dst->material = src->material; dst->mass = src->mass; if (src->inscription) { dst->inscription = strdup(src->inscription); } setblessed(dst, src->blessed); dst->blessknown = src->blessknown; // kill existing flags foreach_bucket(b) { while (dst->flags->first[b]) { killflag(dst->flags->first[b]); } } // copy flags 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; } int countpilewetness(obpile_t *op, int equippedonly, object_t *exclude) { object_t *oo; int count = 0; for (oo = op->first ;oo ; oo = oo->next) { if (oo == exclude) continue; if (!equippedonly || isequipped(oo)) { count += getobwetness(oo); } } return count; } 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, int announce) { flag_t *f; f = hasflag(o->flags, F_TRAPPED); if (f) { enum OBTYPE trapid; // announce... if (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) { snprintf(buf, BUFLEN, "%s corpse", r->name); addflag(o->flags, F_STARTOB, 100, NA, NA, buf); } break; case CC_CONDIMENTS: switch (rnd(1,2)) { default: case 1: snprintf(buf, BUFLEN, "pinch of salt"); break; case 2: snprintf(buf, BUFLEN, "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)]; } // will ALLOCATE breakobs. remember to free afterwards // // returns TRUE if an break obejct was found. int getbreakob(object_t *o, char **breakobname, int *nbreakobs) { enum MATERIAL mid; enum DAMTYPE dt; enum OBTYPE breakoid[MAXBREAKOBTYPES]; objecttype_t *breakob[MAXBREAKOBTYPES]; char adj[BUFLEN][MAXBREAKOBTYPES]; int nobs[MAXBREAKOBTYPES]; //enum OBTYPE bigoid = OT_NONE,smalloid = OT_NONE; //objecttype_t *big = NULL,*small = NULL; flag_t *f; double totmass; //char bigadj[BUFLEN],smalladj[BUFLEN]; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0,i; int found = B_FALSE; mid = o->material->id; dt = oblastdamtype(o); // flag overrides code below found = 0; getflags(o->flags, retflag, &nretflags, F_BREAKOB, F_NONE); *nbreakobs = 0; for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f && (f->val[0] == dt)) { breakobname[*nbreakobs] = strdup(f->text); strcpy(adj[*nbreakobs], ""); if (++(*nbreakobs) >= MAXBREAKOBTYPES) { msg("ERROR - too many F_BREAKOB flags in %s",o->type->name); dblog("ERROR - too many F_BREAKOB flags in %s",o->type->name); raise(SIGINT); } } } if (*nbreakobs) { // flags have told us what the name is return B_TRUE; } // determine breakobs based on materials // adjectives switch (mid) { case MT_WOOD: if (ismeleedam(dt) || (dt == DT_EXPLOSIVE)) { breakoid[0] = OT_WOODPLANK; breakoid[1] = OT_WOODSHARD; (*nbreakobs) = 2; } else { switch (dt) { case DT_FIRE: case DT_HEAT: case DT_ACID: breakoid[0] = OT_ASHHUGE; breakoid[1] = OT_ASHLARGE; breakoid[2] = OT_ASH; (*nbreakobs) = 3; break; default: break; } } break; case MT_GLASS: if (ismeleedam(dt) || (dt == DT_EXPLOSIVE)) { breakoid[0] = OT_WOODPLANK; (*nbreakobs) = 1; break; } else { switch (dt) { case DT_FIRE: case DT_HEAT: case DT_ACID: breakoid[0] = OT_ASHHUGE; breakoid[1] = OT_ASHLARGE; breakoid[2] = OT_ASH; (*nbreakobs) = 3; break; default: break; } } break; case MT_ICE: if (ismeleedam(dt) || (dt == DT_EXPLOSIVE)) { breakoid[0] = OT_ICECHUNK; (*nbreakobs) = 1; break; } else { switch (dt) { case DT_FIRE: case DT_HEAT: breakoid[0] = OT_PUDDLEWATERL; breakoid[1] = OT_PUDDLEWATER; (*nbreakobs) = 2; break; default: break; } } break; case MT_FLESH: switch (dt) { case DT_FIRE: breakoid[0] = OT_ASHHUGE; breakoid[1] = OT_ASHLARGE; breakoid[2] = OT_ASH; (*nbreakobs) = 3; break; default: break; } break; default: break; } for (i = 0; i < *nbreakobs; i++) { breakob[i] = findot(breakoid[i]); } // now find out how many obejcts of each type make up the wight. totmass = getobmass(o); for (i = 0; i < *nbreakobs; i++) { float thismass; thismass = breakob[i]->mass; nobs[i] = totmass / thismass; totmass -= (nobs[i] * thismass); } for (i = 0; i < *nbreakobs; i++) { if (nobs[i] > 0) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%d %s", nobs[i], breakob[i]->name); breakobname[i] = strdup(buf); } else { breakobname[i] = NULL; } } if (*nbreakobs) { 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)getobmass(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 *fillpotfrom(object_t *flask, object_t *fillfrom, int reduceliquid) { char liquidname[BUFLEN]; int autoid = B_FALSE; object_t *newob = NULL; char newobname[BUFLEN]; flag_t *bfob,*fillflag; fillflag = hasflag(fillfrom->flags, F_FILLPOT); if (!fillflag) return NULL; getobname(fillfrom, liquidname, 1); // get a random fillpot flag. bfob = getrandomflag(fillfrom->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 (fillfrom->type->id == OT_FOUNTAIN) { objecttype_t *ot; flag_t *f; // same type as fountain f = hasflag(fillfrom->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 (fillfrom->material->id) { case MT_BLOOD: strcpy(newobname, "potion of blood"); break; case MT_WATER: strcpy(newobname, "potion of water"); break; default: strcpy(newobname, ""); break; } } // is there enough liquid ? if (fillflag->val[1] == NA) { // ok } else if (fillfrom->amt >= fillflag->val[1]) { // ok } else { // fail, not enough liquid return NULL; } if (strlen(newobname)) { obpile_t *op; op = flask->pile; // kill the empty flask removeob(flask, 1); // give the new potion newob = addob(op, newobname); if (newob) { if (autoid) makeknown(newob->type->id); // overwrite newobname getobname(newob, newobname, newob->amt); if (op->owner == player) { msgnocap("%c - %s.",newob->letter, newobname); } } if (reduceliquid) { if (fillfrom->type->id == OT_FOUNTAIN) { if (fountain_will_dryup(fillfrom)) { removeob(fillfrom, fillfrom->amt); } } else { if (fillflag->val[1] == NA) { // no effect on original ob } else { removeob(fillfrom, fillflag->val[1]); } } } } else { return NULL; } return newob; } 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 *)); if (wantrr == RR_NONE) { wantrr = NA; } 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) && !hasflag(ot->flags, F_NORANDOM)) { 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 != NA) { 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 { rarityok = B_TRUE; } // 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 = getobweight(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(getobweight(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 = getobmass(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->obclass->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 credit)",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,b; // 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) { snprintf(basename, BUFLEN, "temple of %s", god->race->name); } else { snprintf(basename, BUFLEN, "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) { snprintf(xblood, BUFLEN, "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)) { snprintf(xblood, BUFLEN, "%s blood", brace->name); } else { snprintf(xblood, BUFLEN, "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) { snprintf(basename, BUFLEN, "%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]; snprintf(newname, BUFLEN, "%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? foreach_bucket(b) { for (brf = br->flags->first[b] ; 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 += getobmass(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 getobmass(object_t *o) { float weight; weight = getobweight(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 getobweight(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->mass; } // 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, NA); } 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) { if (hab) { addcond(cs, CC_OBCLASS, B_TRUE, getrandomobclass(hab->id)); } } 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) { objecttype_t *bt; enum OBTYPE thisoid; thisoid = buildingusage[i].oid; bt = findot(thisoid); assert(bt); if (!hasflag(bt->flags, F_NORANDOM) && !hasflag(bt->flags, F_UNIQUE)) { poss[nposs] = bt; nposs++; } } } assert(nposs > 0); selot = poss[rnd(0,nposs-1)]; snprintf(buf, BUFLEN, "%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; if (hasflag(ot->flags, F_NORANDOM)) continue; // 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, NA); 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(getobmass(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)getobweight(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 getobwetness(object_t *o) { flag_t *wetflag; int count = 0; wetflag = hasflag(o->flags, F_WET); if (!wetflag) { wetflag = hasflag(o->flags, F_DRYABLE); } if (wetflag) { // towel will get slightly wetter. switch (wetflag->val[0]) { case W_DAMP: count += 1; break; case W_WET: count += TM_WETTIME; // 10 damp objects = 1 wet object break; case W_SOAKED: count += (TM_WETTIME*TM_WETTIME); // 10 wet objects = 1 soaked object break; default: break; } } return count; } 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) && hasflag(fp, F_OPERABLE)) { return B_TRUE; } else if (hasflag(fp, F_CONTAINER) && hasflag(fp, F_OPERABLE)) { return B_TRUE; } else if (hasflag(fp, F_CLIMBABLE) && hasflag(fp, F_LOCKABLE)) { // ie. locked stairs return B_TRUE; } else if (hasflag(fp, F_DOOR)) { 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,b; foreach_bucket(b) { for (omf = om->flags->first[b] ; omf ; omf = omf->next){ int val[3],i; for (i = 0; i < 3; i++) { if (omf->obmodignoreval[i]) val[i] = NA; else val[i] = omf->val[i]; } if (!hasflagval(o->flags, omf->id, val[0], val[1], 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) { int b; 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; foreach_bucket(b) { for (f = o->flags->first[b] ; 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 isfillable(object_t *o) { if ((o->type->id == OT_EMPTYFLASK) || (o->type->id == OT_EMPTYVIAL)) { 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) { // objects in shops are always fully known. if (pileisinshop(o->pile)) { return B_TRUE; } // 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 (getobweight(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) { int b; flag_t *f; // blessed status not known? if (!isblessknown(o)) return B_FALSE; // unknown object type? if (!isknown(o)) return B_FALSE; // unknown flags? foreach_bucket(b) { for (f = o->flags->first[b] ; 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 (hasinteractableflags(o->flags)) { 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->mass != o->type->mass) 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 makeunequipped(lifeform_t *lf, object_t *o) { // remove the equipped flag killflagsofid(o->flags, F_EQUIPPED); unequipeffects(lf, o); // if you don't have a pack, it goes to the ground. if (lfhasflag(lf, F_NOPACK)) { moveob(o, lf->cell->obpile, o->amt); } } 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; } if (amt == W_DRY) 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; 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 hit the ground? o = obaffectsothers(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); } } // affect other objects on the ground // // returns the source objects, or NULL if it was destroyed // TODO: optimise to not go through every object heaps of times. object_t *obaffectsothers(object_t *o) { object_t *poss[MAXPILEOBS],*oo,*nextoo; cell_t *loc; int nposs = 0; loc = o->pile->where; if (!loc) return o; // blood will stain things on the ground if (o->material->id == MT_BLOOD) { 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)); } } // liquids will fill flasks on the ground if (hasflag(o->flags, F_FILLPOT)) { nposs = 0; for (oo = o->pile->first ; oo ; oo = oo->next) { if ((oo != o) && isfillable(oo)) { poss[nposs++] = oo; } } if (nposs) { oo = poss[rnd(0,nposs-1)]; fillpotfrom(oo, o, B_FALSE); } } for (oo = loc->obpile->first ; o && oo ; oo = nextoo ) { nextoo = oo->next; if (oo != o) { flag_t *f; if ((oo->type->id == OT_WATERDEEP) && (getmaterialstate(o->material->id) == MS_SOLID)) { noise(loc, NULL, NC_OTHER, SV_TALK, "a splash.", "Splash!"); } // triggered a trap? f = hasflag(oo->flags, F_TRAP); if (f && (f->val[1] != curtime)) { long origid; // an explosion trap or similar might kill the source object. origid = o->id; triggertrap(NULL, o, oo, loc); o = hasobid(loc->obpile, origid); if (!o) return NULL; } // 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, loc)) { 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, loc)) { char ooname[BUFLEN]; getobname(oo, ooname, 1); msg("%s fades and vanishes.", ooname); } removeob(oo, oo->amt); } } } } return o; } 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->mass / 10; maxbones = o->mass / 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 *breakobname[MAXBREAKOBTYPES]; int nbreakobs,i; // maybe create broken object based on material getbreakob(o, breakobname, &nbreakobs); for (i = 0; i < nbreakobs; i++) { if (breakobname[i]) { addob(loc->obpile, breakobname[i]); free(breakobname[i]); } } } } 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++ ){ // add to list of "one of these must match" conditions if (cs->val[i] == B_ONEOF) { found = B_FALSE; // alreay in the list? 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; noneof++; } } 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; } // 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->mass != b->mass) 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; if (isdeadob(o)) return NULL; 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]; snprintf(oid, BUFLEN, "%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, B_DOANNOUNCE)) { 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); snprintf(theob, BUFLEN, "%s%s %s",ownername,getpossessive(ownername), noprefix(obname)); } else { snprintf(theob, BUFLEN, "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 = KG_MISC; 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)]; snprintf(wname, BUFLEN, "%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_LOCKPICK) { lockpick(lf, where, NULL, o); } else if (isfillable(o)) { 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 if ((fillflag->val[1] != NA) && (o->amt < fillflag->val[1])) { 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]; 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') { if (fillpotfrom(o, oo, B_TRUE)) { msg("That doesn't seem like a very good idea."); } else { donedip = B_TRUE; } } } } 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]; snprintf(nbuf, BUFLEN, "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]; snprintf(nbuf, BUFLEN, "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, B_DOANNOUNCE)) { 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) { int count = 0; enum WETNESS howwet = W_DRY; char ch = 'n'; object_t *oo = NULL; char obname2[BUFLEN]; if (hasflag(o->flags, F_WET)) { if (isplayer(lf)) { msg("Your %s is too wet to be effective.", noprefix(obname)); } // fail return B_TRUE; } // what will we dry? if (isplayer(lf)) { int nwet, ndryable; char buf[BUFLEN]; nwet = countobswithflag(lf->cell->obpile, F_WET); ndryable = countobswithflag(lf->cell->obpile, F_DRYABLE); if (nwet + ndryable == 1) { // only one possibility if (nwet) { oo = hasobwithflag(lf->cell->obpile, F_WET); } else { oo = hasobwithflag(lf->cell->obpile, F_DRYABLE); } assert(oo); getobname(oo, obname2, oo->amt); snprintf(buf, BUFLEN, "There %s %s. Use your %s on %s?", OB1(oo, "is", "are"), obname2, noprefix(obname), OB1(oo,"it","them")); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); } else if (nwet || ndryable) { snprintf(buf, BUFLEN, "There are wet objects here. Use your %s on them?", noprefix(obname)); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); } } if (ch == 'y') { if (!oo) { condset_t cs; initcondv(&cs, CC_HASFLAG, B_ONEOF, F_WET, CC_HASFLAG, B_ONEOF, F_DRYABLE, CC_NONE); oo = doaskobject(lf->cell->obpile, "Dry off which object", "There are no wet objects here.", NULL, B_SHOWLONG, B_NOTFORPICKUP, B_DONTSHOWPOINTS, '\0', NULL, SA_NONE, &cs, B_INCLUDENOTHING); if (oo) { getobname(oo, obname2, oo->amt); } } if (oo) { count = getobwetness(oo); if (!count) { if (isplayer(lf)) msg("%s doesn't need drying!", obname2); return B_TRUE; } // announce if (hasflag(oo->flags, F_DRYABLE)) { if (isplayer(lf)) { msg("You mop up %s with %s.", obname2, obname); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s mop up %s with %s.", lfname, obname2, obname); } } else { if (isplayer(lf)) { msg("You dry off %s with %s.", obname2, obname); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s dries off %s with %s.", lfname, obname2, obname); } } if (hasflag(oo->flags, F_DRYABLE)) { // mop it up completely killob(oo); oo = NULL; } else { // make it dry killflagsofid(oo->flags, F_WET); } } else { if (isplayer(lf)) msg("Cancelled."); return B_TRUE; } } else { // equipped objects? (all at once) count = countpilewetness(lf->pack, B_EQUIPPEDONLY, o); if (count) { object_t *oo,*nextoo; // announce 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); } // actually make objects dry for (oo = lf->pack->first ;oo ; oo = nextoo) { nextoo = oo->next; if (oo == o) continue; if (isequipped(oo)) { killflagsofid(oo->flags, F_WET); } } } else { // announce if (isplayer(lf)) { msg("You're already dry."); } return B_TRUE; } } // make the towel wet (but it absorbs some of the moisture) count /= TM_WETTIME; if (count >= (TM_WETTIME * TM_WETTIME)) { howwet = W_SOAKED; } else if (count >= TM_WETTIME) { howwet = W_WET; } else if (count > 0) { howwet = W_DAMP; } else { howwet = W_DRY; // ie. do nothing } makewet(o, howwet); 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, int startrr) { enum RARITY wantrr = RR_FREQUENT; int chance = 3; int mod = 0; if (startrr != NA) { wantrr = startrr; } if ((gamemode == GM_GAMESTARTED) && hasflag(player->flags, F_EXTRALUCK)) { if (whatfor == TT_OBJECT) { wantrr++; } else if (whatfor == TT_VAULT) { 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 pileisinshop(obpile_t *op) { if (op && op->parentob && hasflag(op->parentob->flags, F_SHOP)) { return B_TRUE; } return B_FALSE; } 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,b; 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 foreach_bucket(b) { for (f = dst->flags->first[b] ; 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); snprintf(origstats, BUFLEN, "%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,b; 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 foreach_bucket(b) { for (f = lf->flags->first[b] ; 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); if (isequipped(oo)) { // forcibly unequip it. makeunequipped(lf, oo); if (isplayer(lf)) { char obname[BUFLEN]; // announce getobname(oo,obname,oo->amt); if (oo->pile->owner == lf) { msg("Your %s vanishes and reappears in your pack.", noprefix(obname)); } else { msg("Your %s vanishes and appears on the ground.", noprefix(obname)); } } else if (cansee(player, lf)) { char obname[BUFLEN],lfname[BUFLEN]; // announce getobname(oo,obname,oo->amt); getlfname(lf, lfname); if (oo->pile->owner == lf) { msg("%s%s %s vanishes and reappears in %ss pack.", lfname, getpossessive(lfname), noprefix(obname), it(lf)); } else { msg("%s%s %s vanishes and appears on the ground.", lfname, getpossessive(lfname), noprefix(obname)); } } } } } } // 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)); snprintf(shortdesc, BUFLEN, "%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) { int b; o->blessed = wantbless; if (wantbless != B_BLESSED) { flag_t *f,*nextf; // remove glowing from blessings foreach_bucket(b) { for (f = o->flags->first[b] ; 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 effects 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; if (isdeadob(o)) { return howmuch; } // 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 if (damtype == DT_WATER) { // water puts out fire 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"); } } // water dissolves powders if (hasflag(o->flags, F_POWDER)) { cell_t *c; c = getoblocation(o); if (haslos(player, c)) { char buf[BUFLEN]; getobname(o, buf, o->amt); msg("%s dissolve%s.", buf, OBS1(o)); } addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); return howmuch; } } // 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)) { // 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->mass = o->mass; 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 snprintf(buf, BUFLEN, "%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 snprintf(buf, BUFLEN, "%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); snprintf(fatext, BUFLEN," (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); snprintf(attackname, BUFLEN, "%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) { snprintf(verb, BUFLEN, "wrap%s around", (amt == 1) ? "s" : ""); } else { snprintf(verb, BUFLEN, "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 ((getobweight(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)) { if (newob && !isdeadob(newob)) { // on the ground? if ((newob->pile->where == where) && haslof(newob->pile->where, thrower->cell, LOF_NEED, NULL)) { if (isplayer(thrower)) { msg("%s returns to you!", obname); } else if (cansee(player, thrower)) { msg("%s returns to %s!", obname, throwername); } else if (haslos(player, newob->pile->where)) { msg("%s returns to someone!", obname); } moveob(newob, thrower->pack, newob->amt); } } } } if (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 (getoption(OPT_ABSORBNOTIFY)) { 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_POWDER, 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_POWDER) && (o->pile->where)) { object_t *oo; for (oo = o->pile->where->obpile->first ; oo ; oo = oo->next) { if (oo == o) continue; if (oo->material->id == MT_WATER) { if (haslos(player, o->pile->where)) { msg("%s dissolve%s.", obname, OBS1(o)); } removeob(o, o->amt); return; } } } 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" snprintf(damstring, BUFLEN, "%s%s %s", cname, getpossessive(cname), noprefix(obname)); } else { // ie. "a whirlwind" snprintf(damstring, BUFLEN, "%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->mass = o->mass; // 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->mass = trappedob->mass; // 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]; snprintf(noisebuf, BUFLEN, "%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; }