From: PatR Date: Mon, 22 Mar 2021 22:31:29 +0000 (-0700) Subject: wizard mode wishing for doors X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e37d3d9f2d6f210c0bbe731d45e1d27d9da53e6d;p=nethack wizard mode wishing for doors It was possible to wish for a secret door, if done at a wall or door location, but not for a regular door. Add that. (Dig followed by locking magic possibly followed by open or by kick could cover most of the details without wish support, but there wasn't any way to force a closed or locked door to be trapped.) The wish request can include "trapped" (or "untrapped", the default) and/or one of the 5 door states: "locked", "closed", "open", "broken", or "doorless" (default is "closed"). If more than one state is specified, the last one in the wish text overrides others. If trapped is specified with open or broken or doorless, it will be ignored. Allow "looted" when wishing for a fountain, sink, throne, or tree. For the ones with multiple loot tracking bits, it sets them all. Explicitly reject a wish for a wall rather than claiming nothing of that description exists. Formatting: wrap various wide lines. --- diff --git a/src/objnam.c b/src/objnam.c index b349af529..b8578274e 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -24,6 +24,7 @@ struct _readobjnam_data { int eroded, eroded2, erodeproof, locked, unlocked, broken, real, fake; int halfeaten, mntmp, contents; int islit, unlabeled, ishistoric, isdiluted, trapped; + int doorless, open, closed, looted; int tmp, tinv, tvariety, mgend; int wetness, gsize; int ftype; @@ -44,7 +45,7 @@ static boolean badman(const char *, boolean); static boolean wishymatch(const char *, const char *, boolean); static short rnd_otyp_by_wpnskill(schar); static short rnd_otyp_by_namedesc(const char *, char, int); -static struct obj *wizterrainwish(char *, char *, int, int); +static struct obj *wizterrainwish(struct _readobjnam_data *); static void readobjnam_init(char *, struct _readobjnam_data *); static int readobjnam_preparse(struct _readobjnam_data *); static void readobjnam_parse_charges(struct _readobjnam_data *); @@ -308,7 +309,7 @@ fruit_from_indx(int indx) struct fruit * fruit_from_name( const char *fname, - boolean exact, /* False => prefix or exact match, True = exact match only */ + boolean exact, /* False: prefix or exact match, True: exact match only */ int *highest_fid) /* optional output; only valid if 'fname' isn't found */ { struct fruit *f, *tentativef; @@ -1120,7 +1121,8 @@ doname_base(struct obj* obj, unsigned int doname_flags) break; } if (obj->otyp == CANDELABRUM_OF_INVOCATION) { - Sprintf(eos(bp), " (%d of 7 candle%s%s)", obj->spe, plur(obj->spe), + Sprintf(eos(bp), " (%d of 7 candle%s%s)", + obj->spe, plur(obj->spe), !obj->lamplit ? " attached" : ", lit"); break; } else if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP @@ -2632,7 +2634,7 @@ makesingular(const char* oldstr) static boolean badman( const char *basestr, - boolean to_plural) /* true => makeplural, false => makesingular */ + boolean to_plural) /* True: makeplural, False: makesingular */ { /* these are all the prefixes for *man that don't have a *men plural */ static const char *no_men[] = { @@ -2887,7 +2889,8 @@ static short rnd_otyp_by_namedesc( const char *name, char oclass, - int xtra_prob) /* to force 0% random generation items to also be considered */ + int xtra_prob) /* add to item's chance of being chosen; non-zero causes + * 0% random generation items to also be considered */ { int i, n = 0; short validobjs[NUM_OBJECTS]; @@ -2967,11 +2970,12 @@ shiny_obj(char oclass) /* in wizard mode, readobjnam() can accept wishes for traps and terrain */ static struct obj * -wizterrainwish(char* bp, char* p, int locked, int trapped) +wizterrainwish(struct _readobjnam_data *d) { struct rm *lev; boolean madeterrain = FALSE, badterrain = FALSE, didblock; int trap, oldtyp, x = u.ux, y = u.uy; + char *bp = d->bp, *p = d->p; for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) { struct trap *t; @@ -3002,19 +3006,19 @@ wizterrainwish(char* bp, char* p, int locked, int trapped) if (!BSTRCMPI(bp, p - 8, "fountain")) { lev->typ = FOUNTAIN; g.level.flags.nfountains++; - lev->looted = 0; /* overlays 'flags' */ + lev->looted = d->looted ? F_LOOTED : 0; /* overlays 'flags' */ lev->blessedftn = !strncmpi(bp, "magic ", 6); pline("A %sfountain.", lev->blessedftn ? "magic " : ""); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 6, "throne")) { lev->typ = THRONE; - lev->looted = 0; /* overlays 'flags' */ + lev->looted = d->looted ? T_LOOTED : 0; /* overlays 'flags' */ pline("A throne."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "sink")) { lev->typ = SINK; g.level.flags.nsinks++; - lev->looted = 0; /* overlays 'flags' */ + lev->looted = d->looted ? (S_LPUDDING | S_LDWASHER | S_LRING) : 0; pline("A sink."); madeterrain = TRUE; @@ -3060,16 +3064,16 @@ wizterrainwish(char* bp, char* p, int locked, int trapped) make_grave(x, y, (char *) 0); if (IS_GRAVE(lev->typ)) { lev->looted = 0; /* overlays 'flags' */ - lev->disturbed = !strncmpi(bp, "disturbed ", 10); + lev->disturbed = d->looted ? 1 : 0; pline("A %sgrave.", lev->disturbed ? "disturbed " : ""); madeterrain = TRUE; } else { - pline("Can't place a grave here"); + pline("Can't place a grave here."); badterrain = TRUE; } } else if (!BSTRCMPI(bp, p - 4, "tree")) { lev->typ = TREE; - lev->looted = 0; /* overlays 'flags' */ + lev->looted = d->looted ? (TREE_LOOTED | TREE_SWARM) : 0; pline("A tree."); madeterrain = TRUE; } else if (!BSTRCMPI(bp, p - 4, "bars")) { @@ -3086,40 +3090,92 @@ wizterrainwish(char* bp, char* p, int locked, int trapped) lev->flags = 0; pline("A cloud."); madeterrain = TRUE; - } else if (!BSTRCMPI(bp, p - 11, "secret door")) { + } else if (!BSTRCMPI(bp, p - 4, "door") + || (d->doorless && !BSTRCMPI(bp, p - 7, "doorway"))) { + char dbuf[40]; + unsigned old_wall_info; + boolean secret = !BSTRCMPI(bp, p - 11, "secret door"); + /* require door or wall so that the 'horizontal' flag will - already have the correct value (it will matter once the - secret door is discovered and becomes a regular door); - player might choose to put SDOOR on top of existing SDOOR - to control its trapped state; iron bars are surrogate walls */ + already have the correct value; player might choose to put + DOOR on top of existing DOOR or SDOOR on top of existing SDOOR + to control its trapped state; iron bars are surrogate walls; + a previously dug wall looks like corridor but is actually a + doorless doorway so will be acceptable here */ if (lev->typ == DOOR || lev->typ == SDOOR || (IS_WALL(lev->typ) && lev->typ != DBWALL) || lev->typ == IRONBARS) { - lev->typ = SDOOR; + /* remember previous wall info [is this right for iron bars?] */ + old_wall_info = (lev->typ != DOOR) ? lev->wall_info : 0; + /* set the new terrain type */ + lev->typ = secret ? SDOOR : DOOR; lev->wall_info = 0; /* overlays 'flags' */ /* lev->horizontal stays as-is */ - /* no special handling for rogue level is necessary; - exposing a secret door there yields a doorless doorway */ -#if 0 /* - * Can't do this; secret doors want both doormask and - * wall_info but those both overload rm.flags which makes - * D_CLOSED conflict with WM_MASK. However, converting - * secret door to regular door sets D_CLOSED iff D_LOCKED - * isn't specified so the alternate code suffices. - */ - lev->doormask = locked ? D_LOCKED : D_CLOSED; -#else - /* cvt_sdoor_to_door() will change D_NODOOR to D_CLOSED */ - lev->doormask = locked ? D_LOCKED : D_NODOOR; -#endif - if (trapped) + if (Is_rogue_level(&u.uz)) { + /* all doors on the rogue level are doorless; locking magic + there converts them into walls rather than closed doors */ + d->doorless = 1; + d->locked = d->closed = d->open = d->broken = 0; + } + /* if not locked, secret doors are implicitly closed but + mustn't be set that way explicitly because they use both + doormask and wall_info which both overload rm[x][y].flags + (CLOSED overlaps wall_info bits, LOCKED and TRAPPED don't); + conversion from SDOOR to DOOR changes NODOOR to CLOSED */ + lev->doormask = d->locked ? D_LOCKED + : (d->doorless || secret) ? D_NODOOR + : d->open ? D_ISOPEN + : d->broken ? D_BROKEN + : D_CLOSED; + /* SDOOR uses wall_info, restore relevant bits. + * FIXME? if we're changing a regular door into a secret door, + * old_wall_info bits will be 0 instead of being set properly. + * Probably only matters if player uses Passes_walls and a wish + * to turn a T- or cross-wall into a door, losing wall info, + * and then another wish to turn that door into a secret door. */ + if (secret) + lev->wall_info |= (old_wall_info & WM_MASK); + /* set up trapped flag; open door states aren't eligible */ + if (d->trapped == 2 /* 2: wish includes explicit "untrapped" */ + || (!secret && ((lev->doormask & (D_LOCKED | D_CLOSED)) == 0))) + d->trapped = 0; + if (d->trapped) lev->doormask |= D_TRAPPED; - pline("Secret door."); + /* feedback */ + dbuf[0] = '\0'; + /* locked state and trapped flag can augment secret doors; other + states apply to normal doors only (see above about 'closed') */ + if (lev->doormask & D_TRAPPED) + Strcat(dbuf, "trapped "); + if (lev->doormask & D_LOCKED) + Strcat(dbuf, "locked "); + if (lev->typ == SDOOR) { + Strcat(dbuf, "secret door"); + } else { + /* these should be mutually exclusive but we describe them + as if they're independent to maybe catch future bugs... */ + if (lev->doormask & D_CLOSED) + Strcat(dbuf, "closed "); + if (lev->doormask & D_ISOPEN) + Strcat(dbuf, "open "); + if (lev->doormask & D_BROKEN) + Strcat(dbuf, "broken "); + if ((lev->doormask & ~D_TRAPPED) == D_NODOOR) + Strcat(dbuf, "doorless doorway"); + else + Strcat(dbuf, "door"); + } + pline("%s.", upstart(an(dbuf))); madeterrain = TRUE; } else { - pline("Secret door requires door or wall location."); + Strcpy(dbuf, secret ? "secret door" : "door"); + pline("%s requires door or wall location.", upstart(dbuf)); badterrain = TRUE; } + } else if (!BSTRCMPI(bp, p - 4, "wall") + && (bp == p - 4 || p[-4] == ' ')) { + pline("Wishing for walls is not implemented."); + badterrain = TRUE; } else if (!BSTRCMPI(bp, p - 15, "secret corridor")) { if (lev->typ == CORR) { lev->typ = SCORR; @@ -3201,8 +3257,12 @@ readobjnam_init(char* bp, struct _readobjnam_data* d) d->very = d->rechrg = d->blessed = d->uncursed = d->iscursed = d->ispoisoned = d->isgreased = d->eroded = d->eroded2 = d->erodeproof = d->halfeaten = d->islit = d->unlabeled - = d->ishistoric = d->isdiluted = d->trapped = d->locked - = d->unlocked = d->broken = d->real = d->fake = 0; + = d->ishistoric = d->isdiluted /* statues, potions */ + /* box/chest and wizard mode door */ + = d->trapped = d->locked = d->unlocked = d->broken + = d->open = d->closed = d->doorless /* wizard mode door */ + = d->looted /* wizard mode fountain/sink/throne/tree and grave */ + = d->real = d->fake = 0; /* Amulet */ d->tvariety = RANDOM_TIN; d->mgend = MALE; d->mntmp = NON_PM; @@ -3286,13 +3346,33 @@ readobjnam_preparse(struct _readobjnam_data* d) d->trapped = 1; } else if (!strncmpi(d->bp, "untrapped ", l = 10)) { d->trapped = 2; /* not trapped */ - /* locked, unlocked, broken: box/chest lock states */ + /* locked, unlocked, broken: box/chest lock states, also door states; + open, closed, doorless: additional door states */ } else if (!strncmpi(d->bp, "locked ", l = 7)) { - d->locked = 1, d->unlocked = d->broken = 0; + d->locked = d->closed = 1, + d->unlocked = d->broken = d->open = d->doorless = 0; } else if (!strncmpi(d->bp, "unlocked ", l = 9)) { - d->unlocked = 1, d->locked = d->broken = 0; + d->unlocked = d->closed = 1, + d->locked = d->broken = d->open = d->doorless = 0; } else if (!strncmpi(d->bp, "broken ", l = 7)) { - d->broken = 1, d->locked = d->unlocked = 0; + d->broken = 1, + d->locked = d->unlocked = d->open = d->closed + = d->doorless = 0; + } else if (!strncmpi(d->bp, "open ", l = 5)) { + d->open = 1, + d->closed = d->locked = d->broken = d->doorless = 0; + } else if (!strncmpi(d->bp, "closed ", l = 7)) { + d->closed = 1, + d->open = d->locked = d->broken = d->doorless = 0; + } else if (!strncmpi(d->bp, "doorless ", l = 9)) { + d->doorless = 1, + d->open = d->closed = d->locked = d->unlocked = d->broken = 0; + /* looted: fountain/sink/throne/tree; disturbed: grave */ + } else if (!strncmpi(d->bp, "looted ", l = 7) + /* overload disturbed grave with looted fountain here + even though they're separate in struct rm */ + || !strncmpi(d->bp, "disturbed ", l = 10)) { + d->looted = 1; } else if (!strncmpi(d->bp, "greased ", l = 8)) { d->isgreased = 1; } else if (!strncmpi(d->bp, "very ", l = 5)) { @@ -3526,7 +3606,8 @@ readobjnam_postparse1(struct _readobjnam_data* d) d->p = (char *) 0; /* check for "glob", " glob", and "glob of " */ if (!strcmpi(d->bp, "glob") || !BSTRCMPI(d->bp, d->bp + i - 5, " glob") - || !strcmpi(d->bp, "globs") || !BSTRCMPI(d->bp, d->bp + i - 6, " globs") + || !strcmpi(d->bp, "globs") + || !BSTRCMPI(d->bp, d->bp + i - 6, " globs") || (d->p = strstri(d->bp, "glob of ")) != 0 || (d->p = strstri(d->bp, "globs of ")) != 0) { d->mntmp = name_to_mon(!d->p ? d->bp @@ -3596,12 +3677,14 @@ readobjnam_postparse1(struct _readobjnam_data* d) if (*d->bp == ' ') { d->bp++; } else if (!strncmpi(d->bp, "s ", 2) - || (d->bp > d->origbp && !strncmpi(d->bp - 1, "s' ", 3))) { + || (d->bp > d->origbp + && !strncmpi(d->bp - 1, "s' ", 3))) { d->bp += 2; } else if (!strncmpi(d->bp, "es ", 3) || !strncmpi(d->bp, "'s ", 3)) { d->bp += 3; - } else if (!*d->bp && !d->actualn && !d->dn && !d->un && !d->oclass) { + } else if (!*d->bp && !d->actualn && !d->dn && !d->un + && !d->oclass) { /* no referent; they don't really mean a monster type */ d->bp = obp; d->mntmp = NON_PM; @@ -3765,7 +3848,8 @@ readobjnam_postparse1(struct _readobjnam_data* d) * " object", but " trap" is suggested--to either the trap * name or the object name. */ - if (wizard && (!strncmpi(d->bp, "bear", 4) || !strncmpi(d->bp, "land", 4))) { + if (wizard && (!strncmpi(d->bp, "bear", 4) + || !strncmpi(d->bp, "land", 4))) { boolean beartrap = (lowc(*d->bp) == 'b'); char *zp = d->bp + 4; /* skip "bear"/"land" */ @@ -3803,14 +3887,16 @@ readobjnam_postparse2(struct _readobjnam_data* d) return 2; /*goto typfnd;*/ } - if (!BSTRCMPI(d->bp, d->p - 6, " stone") || !BSTRCMPI(d->bp, d->p - 4, " gem")) { + if (!BSTRCMPI(d->bp, d->p - 6, " stone") + || !BSTRCMPI(d->bp, d->p - 4, " gem")) { d->p[!strcmpi(d->p - 4, " gem") ? -4 : -6] = '\0'; d->oclass = GEM_CLASS; d->dn = d->actualn = d->bp; return 1; /*goto srch;*/ } else if (!strcmpi(d->bp, "looking glass")) { ; /* avoid false hit on "* glass" */ - } else if (!BSTRCMPI(d->bp, d->p - 6, " glass") || !strcmpi(d->bp, "glass")) { + } else if (!BSTRCMPI(d->bp, d->p - 6, " glass") + || !strcmpi(d->bp, "glass")) { register char *s = d->bp; /* treat "broken glass" as a non-existent item; since "broken" is @@ -3873,10 +3959,13 @@ readobjnam_postparse3(struct _readobjnam_data* d) } } - if (((d->typ = rnd_otyp_by_namedesc(d->actualn, d->oclass, 1)) != STRANGE_OBJECT) + if (((d->typ = rnd_otyp_by_namedesc(d->actualn, d->oclass, 1)) + != STRANGE_OBJECT) || (d->dn != d->actualn - && (d->typ = rnd_otyp_by_namedesc(d->dn, d->oclass, 1)) != STRANGE_OBJECT) - || ((d->typ = rnd_otyp_by_namedesc(d->un, d->oclass, 1)) != STRANGE_OBJECT) + && ((d->typ = rnd_otyp_by_namedesc(d->dn, d->oclass, 1)) + != STRANGE_OBJECT)) + || ((d->typ = rnd_otyp_by_namedesc(d->un, d->oclass, 1)) + != STRANGE_OBJECT) || (d->origbp != d->actualn && ((d->typ = rnd_otyp_by_namedesc(d->origbp, d->oclass, 1)) != STRANGE_OBJECT))) @@ -4084,7 +4173,7 @@ readobjnam(char* bp, struct obj* no_wish) wiztrap: if (wizard && !g.program_state.wizkit_wishing) { /* [inline code moved to separate routine to unclutter readobjnam] */ - if ((d.otmp = wizterrainwish(d.bp, d.p, d.locked, d.trapped)) != 0) + if ((d.otmp = wizterrainwish(&d)) != 0) return d.otmp; }