From: PatR Date: Wed, 9 Feb 2022 12:16:27 +0000 (-0800) Subject: enhanced water description X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=445573a01fddc111624a5d6d56ab4ecd955d8864;p=nethack enhanced water description Redo how do_screen_description() handles water. Mainly by using waterbody_name() for farlook/quicklook and autodescribe to get hallucinatory descriptions, but it replaces some old hackery with quite a bit of new hackery. Applies to lava and ice too. The latter is now "frozen " when hallucinating, where has a small chance to be "water" but will usually be something like "tea" or "clotted blood". --- diff --git a/src/pager.c b/src/pager.c index 8abb64e8d..d63a1dd13 100644 --- a/src/pager.c +++ b/src/pager.c @@ -15,8 +15,10 @@ static void trap_description(char *, int, int, int); static void look_at_object(char *, int, int, int); static void look_at_monster(char *, char *, struct monst *, int, int); static struct permonst *lookat(int, int, char *, char *); -static void checkfile(char *, struct permonst *, boolean, boolean, - char *); +static void checkfile(char *, struct permonst *, boolean, boolean, char *); +static int add_cmap_descr(int, int, int, int, coord, + const char *, const char *, + boolean *, const char **, char *); static void look_region_nearby(int *, int *, int *, int *, boolean); static void look_all(boolean, boolean); static void look_traps(boolean); @@ -457,6 +459,7 @@ waterbody_name(xchar x, xchar y) static char pooltype[40]; struct rm *lev; schar ltyp; + boolean hallucinate = Hallucination && !g.program_state.gameover; if (!isok(x, y)) return "drink"; /* should never happen */ @@ -466,33 +469,38 @@ waterbody_name(xchar x, xchar y) ltyp = db_under_typ(lev->drawbridgemask); if (ltyp == LAVAPOOL) { - if (!Hallucination) - return "lava"; Snprintf(pooltype, sizeof pooltype, "molten %s", hliquid("lava")); return pooltype; } else if (ltyp == ICE) { - return "ice"; + if (!hallucinate) + return "ice"; + Snprintf(pooltype, sizeof pooltype, "frozen %s", hliquid("water")); + return pooltype; } else if (ltyp == POOL) { Snprintf(pooltype, sizeof pooltype, "pool of %s", hliquid("water")); return pooltype; } else if (ltyp == MOAT) { /* a bit of extra flavor over general moat */ - if (Is_medusa_level(&u.uz)) + if (hallucinate) { + Snprintf(pooltype, sizeof pooltype, "deep %s", hliquid("water")); + return pooltype; + } else if (Is_medusa_level(&u.uz)) { /* somewhat iffy since ordinary stairs can take you beneath, but previous generic "water" was rather anti-climactic */ return "shallow sea"; - else if (Is_juiblex_level(&u.uz)) + } else if (Is_juiblex_level(&u.uz)) { return "swamp"; - else if (Role_if(PM_SAMURAI) && Is_qstart(&u.uz)) + } else if (Role_if(PM_SAMURAI) && Is_qstart(&u.uz)) { /* samurai quest home level has two isolated moat spots; they sound silly if farlook describes them as such */ return "pond"; - else + } else { return "moat"; + } } else if (ltyp == WATER) { - Snprintf(pooltype, sizeof pooltype, "%s %s", - !Is_waterlevel(&u.uz) ? "wall of" : "limitless", - hliquid("water")); + if (Is_waterlevel(&u.uz)) + return "limitless water"; /* even if hallucinating */ + Snprintf(pooltype, sizeof pooltype, "wall of %s", hliquid("water")); return pooltype; } /* default; should be unreachable */ @@ -590,8 +598,9 @@ lookat(int x, int y, char *buf, char *monbuf) } else { int amsk; aligntyp algn; + short symidx = glyph_to_cmap(glyph); - switch (glyph_to_cmap(glyph)) { + switch (symidx) { case S_altar: amsk = altarmask_at(x, y); algn = Amask2align(amsk & AM_MASK); @@ -619,6 +628,9 @@ lookat(int x, int y, char *buf, char *monbuf) Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud"); break; case S_pool: + case S_water: + case S_lava: + case S_ice: /* for hallucination; otherwise defsyms[] would be fine */ Strcpy(buf, waterbody_name(x, y)); break; case S_stone: @@ -636,7 +648,7 @@ lookat(int x, int y, char *buf, char *monbuf) } /*FALLTHRU*/ default: - Strcpy(buf, defsyms[glyph_to_cmap(glyph)].explanation); + Strcpy(buf, defsyms[symidx].explanation); break; } } @@ -937,6 +949,106 @@ checkfile(char *inp, struct permonst *pm, boolean user_typed_name, return; } +/* extracted from do_screen_description() */ +static int +add_cmap_descr( + int found, /* number of matching descriptions so far */ + int idx, /* cmap index into defsyms[] */ + int glyph, /* map glyph of screen symbol being described; + * anything other than NO_GLYPH implies 'looked' */ + int article, /* 0: (none), 1: a/an, 2: the */ + coord cc, /* map location */ + const char *x_str, /* description of defsyms[idx] */ + const char *prefix, /* text to insert in front of first match */ + boolean *hit_trap, /* intput/output: True if a trap has been described */ + const char **firstmatch, /* output: pointer to 1st matching description */ + char *out_str) /* input/output: current description gets appended */ +{ + char *mbuf = NULL; + int absidx = abs(idx); + + if (glyph == NO_GLYPH) { + /* use x_str [almost] as-is */ + if (!strcmp(x_str, "water")) { + /* duplicate some transformations performed by waterbody_name() */ + if (idx == S_pool) + x_str = "pool of water"; + else if (idx == S_water) + x_str = !Is_waterlevel(&u.uz) ? "wall of water" + : "limitless water"; + } + if (absidx == S_pool) + idx = S_pool; + } else if (absidx == S_pool || idx == S_water + || idx == S_lava || idx == S_ice) { + /* replace some descriptions (x_str) with waterbody_name() */ + schar save_ltyp = levl[cc.x][cc.y].typ; + long save_prop = EHalluc_resistance; + + /* grab a scratch buffer we can safely return (via *firstmatch + when applicable) */ + mbuf = mon_nam(&g.youmonst); + + if (absidx == S_pool) { + levl[cc.x][cc.y].typ = (idx == S_pool) ? POOL : MOAT; + idx = S_pool; /* force fake negative moat value to be positive */ + } else { + /* we might be examining a pool location but trying to match + water or lava; override the terrain with what we're matching + because that's what waterbody_name() bases its result on; + it's not pool so must be one of water/lava/ice to get here */ + levl[cc.x][cc.y].typ = (idx == S_water) ? WATER + : (idx == S_lava) ? LAVAPOOL + : ICE; + } + EHalluc_resistance = 1; + Strcpy(mbuf, waterbody_name(cc.x, cc.y)); + EHalluc_resistance = save_prop; + levl[cc.x][cc.y].typ = save_ltyp; + + /* shorten the feedback for farlook/quicklook: "a pool or ..." */ + if (!strcmp(mbuf, "pool of water")) + mbuf[4] = '\0'; + else if (!strcmp(mbuf, "molten lava")) + Strcpy(mbuf, "lava"); + x_str = mbuf; + article = !(!strncmp(x_str, "water", 5) + || !strncmp(x_str, "lava", 4) + || !strncmp(x_str, "swamp", 5) + || !strncmp(x_str, "molten", 6) + || !strncmp(x_str, "shallow", 7) + || !strncmp(x_str, "limitless", 9)); + } + + if (!found) { + /* this is the first match */ + if (is_cmap_trap(idx)) { + Sprintf(out_str, "%sa trap", prefix); + *hit_trap = TRUE; + } else { + Sprintf(out_str, "%s%s", prefix, + article == 2 ? the(x_str) + : article == 1 ? an(x_str) : x_str); + } + *firstmatch = x_str; + found = 1; + } else if (!(*hit_trap && is_cmap_trap(idx)) + && !(found >= 3 && is_cmap_drawbridge(idx)) + /* don't mention vibrating square outside of Gehennom + unless this happens to be one (hallucination?) */ + && (idx != S_vibrating_square || Inhell + || (glyph_is_trap(glyph) + && glyph_to_trap(glyph) == VIBRATING_SQUARE))) { + /* append unless out_str already contains the string to append */ + found += append_str(out_str, (article == 2) ? the(x_str) + : (article == 1) ? an(x_str) + : x_str); + if (is_cmap_trap(idx)) + *hit_trap = TRUE; + } + return found; +} + int do_screen_description(coord cc, boolean looked, int sym, char *out_str, const char **firstmatch, @@ -945,8 +1057,8 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, static const char mon_interior[] = "the interior of a monster", unreconnoitered[] = "unreconnoitered"; static char look_buf[BUFSZ]; - char prefix[BUFSZ], gobbledygook[33]; - int i, j, glyph = NO_GLYPH, + char prefix[BUFSZ]; + int i, j, alt_i, glyph = NO_GLYPH, skipped_venom = 0, found = 0; /* count of matching syms found */ boolean hit_trap, need_to_look = FALSE, submerged = (Underwater && !Is_waterlevel(&u.uz)), @@ -955,7 +1067,6 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, nhsym tmpsym; glyph_info glyphinfo = nul_glyphinfo; - gobbledygook[0] = '\0'; /* no hallucinatory liquid (yet) */ if (looked) { glyph = glyph_at(cc.x, cc.y); /* Convert glyph at selected position to a symbol for use below. */ @@ -1105,59 +1216,48 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, } /* Now check for graphics symbols */ for (hit_trap = FALSE, i = 0; i < MAXPCHARS; i++) { - x_str = defsyms[i].explanation; - if (sym == (looked ? g.showsyms[i] : defsyms[i].sym) && *x_str) { - /* POOL, MOAT, and WATER are "water", LAVAPOOL is "molten lava" */ - boolean water_or_lava = (!strcmp(x_str, "water") - || !strcmp(x_str, "molten lava")); - /* avoid "an unexplored", "an stone", "an air", "a water", - "a molten lava", "a floor of a room", "a dark part of a room"; - article==2 => "the", 1 => "an", 0 => (none) */ - int article = strstri(x_str, " of a room") ? 2 - : !(i == S_stone - || strcmp(x_str, "air") == 0 - || strcmp(x_str, "land") == 0 - || water_or_lava); + /* + * Index hackery: we want + * "a pool or a moat or a wall of water or molten lava" + * rather than + * "a pool or a moat or molten lava or a wall of water" + * but S_lava comes before S_water so 'i' reaches it sooner. + */ + alt_i = i; + if (alt_i == S_water || alt_i == S_lava) + alt_i = (S_water + S_lava) - alt_i; + x_str = defsyms[alt_i].explanation; + if (sym == (looked ? g.showsyms[alt_i] : defsyms[alt_i].sym) + && *x_str) { + int article; /* article==2 => "the", 1 => "an", 0 => (none) */ /* check if dark part of a room was already included above */ - if (i == S_darkroom && glyph && glyph_is_nothing(glyph)) + if (alt_i == S_darkroom && glyph && glyph_is_nothing(glyph)) continue; - /* substitute for "water" and "molten lava" when hallucinating */ - if (water_or_lava && hallucinate) { - if (*gobbledygook) - continue; /* just 1 or player could tell h2o from lava */ - x_str = strncpy(gobbledygook, hliquid(x_str), - (int) sizeof gobbledygook - 1); - gobbledygook[sizeof gobbledygook - 1] = '\0'; - } - - if (!found) { - if (is_cmap_trap(i)) { - Sprintf(out_str, "%sa trap", prefix); - hit_trap = TRUE; - } else { - Sprintf(out_str, "%s%s", prefix, - article == 2 ? the(x_str) - : article == 1 ? an(x_str) : x_str); - } - *firstmatch = x_str; - found++; - } else if (!(hit_trap && is_cmap_trap(i)) - && !(found >= 3 && is_cmap_drawbridge(i)) - /* don't mention vibrating square outside of Gehennom - unless this happens to be one (hallucination?) */ - && (i != S_vibrating_square || Inhell - || (looked && glyph_is_trap(glyph) - && glyph_to_trap(glyph) == VIBRATING_SQUARE))) { - found += append_str(out_str, (article == 2) ? the(x_str) - : (article == 1) ? an(x_str) - : x_str); - if (is_cmap_trap(i)) - hit_trap = TRUE; + /* avoid "an unexplored", "an stone", "an air", + "a floor of a room", "a dark part of a room" */ + article = strstri(x_str, " of a room") ? 2 + : !(alt_i == S_stone + || strcmp(x_str, "air") == 0 + || strcmp(x_str, "land") == 0); + found = add_cmap_descr(found, alt_i, glyph, article, + cc, x_str, prefix, + &hit_trap, firstmatch, out_str); + if (alt_i == S_pool) { + /* "pool of water" and "moat" use the same symbol and glyph + but have different descriptions; when handling pool, add + it a second time for moat but pass an alternate symbol; + skip incrementing 'found' to avoid "can be many things" */ + (void) add_cmap_descr(found, -S_pool, glyph, 1, + cc, "moat", prefix, + &hit_trap, firstmatch, out_str); + need_to_look = TRUE; } - if (i == S_altar || is_cmap_trap(i)) + if (alt_i == S_altar || is_cmap_trap(alt_i) + || (hallucinate && (alt_i == S_water /* S_pool already done */ + || alt_i == S_lava || alt_i == S_ice))) need_to_look = TRUE; } } @@ -1201,15 +1301,18 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, : g.ov_primary_syms[j]; if (tmpsym && sym == tmpsym) { switch (j) { - case SYM_BOULDER + SYM_OFF_X: + case SYM_BOULDER + SYM_OFF_X: { + static const char boulder[] = "boulder"; + if (!found) { - *firstmatch = "boulder"; + *firstmatch = boulder; Sprintf(out_str, "%s%s", prefix, an(*firstmatch)); found++; } else { - found += append_str(out_str, "boulder"); + found += append_str(out_str, boulder); } break; + } case SYM_PET_OVERRIDE + SYM_OFF_X: if (looked) { /* convert to symbol without override in effect */ @@ -1261,27 +1364,21 @@ do_screen_description(coord cc, boolean looked, int sym, char *out_str, pm = lookat(cc.x, cc.y, look_buf, monbuf); if (pm && for_supplement) *for_supplement = pm; - /* lookat() doesn't hallucinate liquids; substitute ours */ - if (*gobbledygook && (!strcmp(look_buf, "water") - || !strcmp(look_buf, "molten lava"))) - Strcpy(look_buf, gobbledygook); *firstmatch = look_buf; if (*(*firstmatch)) { - Snprintf(temp_buf, sizeof(temp_buf), " (%s)", *firstmatch); + Snprintf(temp_buf, sizeof temp_buf, " (%s)", *firstmatch); (void) strncat(out_str, temp_buf, BUFSZ - strlen(out_str) - 1); found = 1; /* we have something to look up */ } if (monbuf[0]) { - Snprintf(temp_buf, sizeof(temp_buf), " [seen: %s]", monbuf); + Snprintf(temp_buf, sizeof temp_buf, " [seen: %s]", monbuf); (void) strncat(out_str, temp_buf, BUFSZ - strlen(out_str) - 1); } } } - if (*firstmatch == gobbledygook) /* fixup for 'found==1' */ - *firstmatch = strcpy(look_buf, gobbledygook); return found; }