]> granicus.if.org Git - nethack/commitdiff
Gnomish Mines changes involving "Orctown" level variant
authornhmall <nhmall@nethack.org>
Tue, 18 Sep 2018 22:35:13 +0000 (18:35 -0400)
committernhmall <nhmall@nethack.org>
Tue, 18 Sep 2018 22:35:13 +0000 (18:35 -0400)
 Changes to be committed:
modified:   include/decl.h
modified:   include/dungeon.h
modified:   include/extern.h
modified:   include/hack.h
modified:   src/decl.c
modified:   src/do_name.c
modified:   src/dog.c
modified:   src/dokick.c
modified:   src/makemon.c
modified:   src/mkmaze.c
modified:   src/mkobj.c
modified:   src/pager.c

This commit is an attempt to address the complaints about
the orc town variation taking away lots of stuff that is
normally available in mine town. The statement in the level
description says "A tragic accident has occurred in Frontier
Town...It has been overrun by orcs."

The changes in this commit attempt to uphold that premise,
while making things a bit more interesting and perhaps
more palatable for the player.

This update does the following in keeping with the mythos:
- While many of the orcs still remain to wander about the
  level, many of the orcs took off deeper into the mines with
  some of the stuff that they plundered. You may now be
  able to hunt some of it down.

- Adds some appearance of this particular horde of marauding
  orcs working as part of a larger collective.

- This evolves the Orc Town mine town variation into a
  a feature over multiple levels of The Gnomish Mines,
  rather than just the single-level "feature" that it was
  previously.

- You may have to work longer and a bit harder for some
  things than other mine town variations, but at least with
  these changes, there is hope that some of it may be found
  elsewhere.

Game mechanics notes (maybe spoily?)

- Add mechanism to place objects into limbo (okay, really
  place them onto the migrating_objs list for transferring
  between levels etc.) and destine them
  to become part of the monster inventory of a particular
  species. In this particular usage case, it's using the
  M2_ORC flag setting to identify the recipients.

- At present, there is no mechanism in the level compiler
  for placing objects onto the migrating objects, nor
  with more sophisticated landing logic, so a somewhat
  kludgy hard-coded fixup and supporting routines were used.
  Some day the need for that might change if additional
  capabilities move to the level compiler.

This is a NetHack-3.6.2-beta01 update. Please give it a workout.

Fixes #127

12 files changed:
include/decl.h
include/dungeon.h
include/extern.h
include/hack.h
src/decl.c
src/do_name.c
src/dog.c
src/dokick.c
src/makemon.c
src/mkmaze.c
src/mkobj.c
src/pager.c

index f0d806dc85035d23025846d5dadd6c37b4d6cceb..53902a07fe85b24356f67f3203ba1103c64e64c2 100644 (file)
@@ -160,6 +160,7 @@ E NEARDATA struct sinfo {
 } program_state;
 
 E boolean restoring;
+E boolean ransacked;
 
 E const char quitchars[];
 E const char vowels[];
index f7b16cf2afaaa6e6b77333a559be1f74bf5e0d35..45246f15079387dac8dc280a17db0f0cac704abd 100644 (file)
@@ -146,7 +146,8 @@ typedef struct branch {
 #define MIGR_WITH_HERO 9    /* mon: followers; obj: trap door */
 #define MIGR_NOBREAK 1024   /* bitmask: don't break on delivery */
 #define MIGR_NOSCATTER 2048 /* don't scatter on delivery */
-
+#define MIGR_TO_SPECIES 4096 /* migrating to species as they are made */ 
+#define MIGR_LEFTOVERS 8192  /* grab remaining MIGR_TO_SPECIES objects */
 /* level information (saved via ledger number) */
 
 struct linfo {
index abd4be1180fdb704f5ff7f04be2620bdc3484121..ed186e66750aa5de90cc13dd9fa2dc0ef4757afa 100644 (file)
@@ -428,6 +428,8 @@ E const char *NDECL(roguename);
 E struct obj *FDECL(realloc_obj,
                     (struct obj *, int, genericptr_t, int, const char *));
 E char *FDECL(coyotename, (struct monst *, char *));
+E char *FDECL(rndorcname, (char *));
+E struct monst *FDECL(christen_orc, (struct monst *, char *));
 E const char *FDECL(noveltitle, (int *));
 E const char *FDECL(lookup_novel, (const char *, int *));
 
@@ -519,6 +521,7 @@ E void FDECL(container_impact_dmg, (struct obj *, XCHAR_P, XCHAR_P));
 E int NDECL(dokick);
 E boolean FDECL(ship_object, (struct obj *, XCHAR_P, XCHAR_P, BOOLEAN_P));
 E void FDECL(obj_delivery, (BOOLEAN_P));
+E void FDECL(deliver_obj_to_mon, (struct monst *mtmp, unsigned long));
 E schar FDECL(down_gate, (XCHAR_P, XCHAR_P));
 E void FDECL(impact_drop, (struct obj *, XCHAR_P, XCHAR_P, XCHAR_P));
 
@@ -1283,6 +1286,7 @@ E void FDECL(new_omailcmd, (struct obj *, const char *));
 E void FDECL(free_omailcmd, (struct obj *));
 E struct obj *FDECL(mkobj_at, (CHAR_P, int, int, BOOLEAN_P));
 E struct obj *FDECL(mksobj_at, (int, int, int, BOOLEAN_P, BOOLEAN_P));
+E struct obj *FDECL(mksobj_migr_to_species, (int, unsigned, BOOLEAN_P, BOOLEAN_P));
 E struct obj *FDECL(mkobj, (CHAR_P, BOOLEAN_P));
 E int NDECL(rndmonnum);
 E boolean FDECL(bogon_is_pname, (CHAR_P));
@@ -1753,7 +1757,7 @@ E char *FDECL(self_lookat, (char *));
 E void FDECL(mhidden_description, (struct monst *, BOOLEAN_P, char *));
 E boolean FDECL(object_from_map, (int,int,int,struct obj **));
 E int FDECL(do_screen_description, (coord, BOOLEAN_P, int, char *,
-                                    const char **));
+                                    const char **, struct permonst **));
 E int FDECL(do_look, (int, coord *));
 E int NDECL(dowhatis);
 E int NDECL(doquickwhatis);
index ed699a94348efb04947fe4df48b4a289561ac8e7..f65d2ddd1b879234be1f9ccb28acdde93a786d05 100644 (file)
@@ -270,6 +270,12 @@ typedef struct sortloot_item Loot;
 #define SHIFT_SEENMSG 0x01 /* put out a message if in sight */
 #define SHIFT_MSG 0x02     /* always put out a message */
 
+/* flags for deliver_obj_to_mon */
+#define DF_NONE     0x00
+#define DF_RANDOM2  0x01
+#define DF_RANDOM3  0x02
+#define DF_ALL      0x04
+
 /* special mhpmax value when loading bones monster to flag as extinct or
  * genocided */
 #define DEFUNCT_MONSTER (-100)
index 2a9c2874665219aef8a012449b9e67c6f656ef65..ffd9ef97414dde2b972b20cae6856efc4bac9448 100644 (file)
@@ -57,6 +57,7 @@ NEARDATA char pl_fruit[PL_FSIZ] = DUMMY;
 NEARDATA struct fruit *ffruit = (struct fruit *) 0;
 
 NEARDATA char tune[6] = DUMMY;
+NEARDATA boolean ransacked = 0;
 
 const char *occtxt = DUMMY;
 const char quitchars[] = " \r\n\033";
index e4a874126696d6d6321aab3b800776923739fc3d..ef187074289a7640a5018b5188094e0392561ee1 100644 (file)
@@ -543,7 +543,7 @@ int cx, cy;
 
     cc.x = cx;
     cc.y = cy;
-    if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch)) {
+    if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch, (struct permonst **)0)) {
         (void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords);
         custompline(SUPPRESS_HISTORY,
                     "%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf,
@@ -593,7 +593,8 @@ int gloc;
         any.a_int = i + 1;
         tmpcc.x = garr[i].x;
         tmpcc.y = garr[i].y;
-        if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, &firstmatch)) {
+        if (do_screen_description(tmpcc, TRUE, sym, tmpbuf,
+                              &firstmatch, (struct permonst **)0)) {
             (void) coord_desc(garr[i].x, garr[i].y, tmpbuf,
                               iflags.getpos_coords);
             Sprintf(fullbuf, "%s%s%s", firstmatch,
@@ -2067,6 +2068,47 @@ char *buf;
     return buf;
 }
 
+char *
+rndorcname(s)
+char *s;
+{
+    int i;
+    const char *v[] = {"a", "ai", "og", "u"};
+    const char *snd[] = {"gor", "gris", "un", "bane", "ruk",
+                         "oth","ul", "z", "thos","akh","hai"};
+    int vstart = rn2(2);
+    
+    if (s) {
+        *s = '\0';
+        for (i = 0; i  < rn2(2) + 3; ++i) { 
+            vstart = 1 - vstart;                /* 0 -> 1, 1 -> 0 */
+            if (!rn2(30) && i > 0)
+                (void) strcat(s, "-");
+            (void) sprintf(eos(s), "%s", vstart ? v[rn2(SIZE(v))] :
+                            snd[rn2(SIZE(snd))]);
+        }
+    }
+    return s;
+}
+
+struct monst *
+christen_orc(mtmp, gang)
+struct monst *mtmp;
+char *gang;
+{
+    size_t sz = 0;
+    char buf[BUFSZ], buf2[BUFSZ], *orcname;
+
+    orcname = rndorcname(buf2);
+    sz = strlen(gang) + strlen(orcname) + strlen(" of ");
+    if (buf && gang && orcname && (sz < (BUFSZ - 1))) {
+        Sprintf(buf, "%s of %s",
+                upstart(orcname), upstart(gang));
+        mtmp = christen_monst(mtmp, buf);
+    }
+    return mtmp;
+}
+
 /* make sure "The Colour of Magic" remains the first entry in here */
 static const char *const sir_Terry_novels[] = {
     "The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
index 4bcdd772280416c14a38194f1731505907f07972..46cd832eff1be31968f35eb5f6aedabfb17e0090 100644 (file)
--- a/src/dog.c
+++ b/src/dog.c
@@ -407,6 +407,12 @@ boolean with_you;
         break;
     }
 
+    if ((mtmp->mspare1 & MIGR_LEFTOVERS) != 0L) {
+        /* Pick up the rest of the MIGR_TO_SPECIES objects */
+        if (migrating_objs)
+            deliver_obj_to_mon(mtmp, DF_ALL);
+    }
+    
     if (xlocale && wander) {
         /* monster moved a bit; pick a nearby location */
         /* mnearto() deals w/stone, et al */
index 874db822a47dee4f1b10c85f4b198fe13c90c864..b2d36de70b11a366bfd7b170df59a08b09b45b47 100644 (file)
@@ -1612,6 +1612,9 @@ boolean near_hero;
             continue;
 
         where = (int) (otmp->owornmask & 0x7fffL); /* destination code */
+        if ((where & MIGR_TO_SPECIES) != 0)
+            continue;
+
         nobreak = (where & MIGR_NOBREAK) != 0;
         noscatter = (where & MIGR_WITH_HERO) != 0;
         where &= ~(MIGR_NOBREAK | MIGR_NOSCATTER);
@@ -1667,6 +1670,48 @@ boolean near_hero;
     }
 }
 
+void
+deliver_obj_to_mon(mtmp, deliverflags)
+struct monst *mtmp;
+unsigned long deliverflags;
+{
+    struct obj *otmp, *otmp2;
+    int where, cnt = 0, maxobj = 0;
+
+    if (deliverflags & DF_RANDOM3)
+        maxobj = rn2(3) + 1;
+    else if (deliverflags & DF_RANDOM2)
+        maxobj = rn2(2) + 1;
+    else if (deliverflags == DF_NONE)
+        maxobj = 1;
+
+    for (otmp = migrating_objs; otmp; otmp = otmp2) {
+        otmp2 = otmp->nobj;
+        where = (int) (otmp->owornmask & 0x7fffL); /* destination code */
+        if ((where & MIGR_TO_SPECIES) == 0)
+            continue;
+        
+        if ((mtmp->data->mflags2 & otmp->corpsenm) != 0) {
+            obj_extract_self(otmp);
+            otmp->owornmask = 0L;
+            otmp->ox = otmp->oy = 0;
+
+            /* special treatment for orcs and their kind */
+            if ((otmp->corpsenm & M2_ORC) != 0 && has_oname(otmp)) {
+                if (!has_mname(mtmp))
+                    mtmp = christen_orc(mtmp, ONAME(otmp));
+                free_oname(otmp);
+            }
+            otmp->corpsenm = 0;
+            (void) add_to_minv(mtmp, otmp);
+            cnt++;
+            if (maxobj && cnt >= maxobj)
+                break;
+            /* getting here implies DF_ALL */
+        }
+    }
+}
+
 STATIC_OVL void
 otransit_msg(otmp, nodrop, num)
 register struct obj *otmp;
index 0d38383dc2ca3c8a8538a5c8d53e77121bae8396..e4cece72d68411991b6021ae8bcaf319d9f429ea 100644 (file)
@@ -1390,6 +1390,9 @@ int mmflags;
             mtmp->mstrategy |= STRAT_APPEARMSG;
     }
 
+    if (allow_minvent && migrating_objs)
+        deliver_obj_to_mon(mtmp, DF_RANDOM3); /* in case there's waiting items */
+    
     if (!in_mklev)
         newsym(mtmp->mx, mtmp->my); /* make sure the mon shows up */
 
index 22fb42d03bdb649d4a3892c51dc9099194c8899d..2e2646647afd705ad0ac2d87b3965b03a8e56097 100644 (file)
@@ -25,6 +25,10 @@ STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
 STATIC_DCL void NDECL(baalz_fixup);
 STATIC_DCL void NDECL(setup_waterlevel);
 STATIC_DCL void NDECL(unsetup_waterlevel);
+STATIC_DCL void FDECL(check_ransacked, (char *));
+STATIC_DCL void FDECL(migr_booty_item, (int, const char *));
+STATIC_DCL void FDECL(migrate_orc, (struct monst *, unsigned long));
+STATIC_DCL void NDECL(stolen_booty);
 
 /* adjust a coordinate one step in the specified direction */
 #define mz_move(X, Y, dir) \
@@ -610,6 +614,8 @@ fixup_special()
     } else if (on_level(&u.uz, &baalzebub_level)) {
         /* custom wallify the "beetle" potion of the level */
         baalz_fixup();
+    } else if (u.uz.dnum == mines_dnum && ransacked) {
+       stolen_booty();
     }
 
     if (lregions)
@@ -617,6 +623,141 @@ fixup_special()
     num_lregions = 0;
 }
 
+void
+check_ransacked(s)
+char *s;
+{
+    /* this kludge only works as long as orctown is minetn-1 */
+    if (dungeons[u.uz.dnum].dname
+        && !strcmp(dungeons[u.uz.dnum].dname, "The Gnomish Mines")
+        && !strcmp(s, "minetn-1"))
+        ransacked = 1;
+    else
+        ransacked = 0;
+}
+
+#define ORC_LEADER 1
+
+void
+migrate_orc(mtmp, flags)
+struct monst *mtmp;
+unsigned long flags;
+{
+    int nlev, max_depth, cur_depth;
+    d_level dest;
+
+    cur_depth = (int) depth(&u.uz);
+    max_depth = dunlevs_in_dungeon(&u.uz) +
+                (dungeons[u.uz.dnum].depth_start - 1);
+    if (flags == ORC_LEADER) {
+        /* Note that the orc leader will take possession of any
+         * remaining stuff not already delivered to other
+         * orcs between here and the bottom of the mines.
+         */
+        nlev = max_depth;
+        mtmp->mspare1 = MIGR_LEFTOVERS;
+    } else {
+        nlev = rn2(max_depth - cur_depth) + cur_depth + 1;
+        if (nlev == cur_depth)
+            nlev++;
+        if (nlev > max_depth)
+            nlev = max_depth;
+        mtmp->mspare1 = 0L;
+    }
+    get_level(&dest, nlev);
+    migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0);
+}
+
+void
+migr_booty_item(otyp, gang)
+int otyp;
+const char *gang;
+{
+    struct obj *otmp;
+    otmp = mksobj_migr_to_species(otyp, (unsigned long) M2_ORC, FALSE, FALSE);
+    if (otmp && gang) {
+        new_oname(otmp, strlen(gang) + 1); /* removes old name if one is present */
+        Strcpy(ONAME(otmp), gang);
+    }
+}
+
+void
+stolen_booty(VOID_ARGS)
+{
+    char *gang, gang_name[BUFSZ];
+    struct monst *mtmp;
+    int cnt, i, otyp;
+
+    /*
+     * --------------------------------------------------------
+     * Mythos:
+     *
+     *      A tragic accident has occurred in Frontier Town...
+     *      It has been overrun by orcs.
+     *
+     *      The booty that the orcs took from the town is now
+     *      in the possession of the orcs that did this and
+     *      have long since fled the level.
+     * --------------------------------------------------------
+     */
+
+    gang = rndorcname(gang_name);
+    /* create the leader of the orc gang */
+    mtmp = makemon(&mons[PM_ORC_CAPTAIN], 0, 0, MM_NONAME);
+    if (mtmp) {
+        mtmp = christen_monst(mtmp, upstart(gang));
+        mtmp->mpeaceful = 0;
+        migrate_orc(mtmp, ORC_LEADER);
+    }
+    /* create the stuff that the rest of the gang took */
+    cnt = rn2(3) + 1;
+    for (i = 0; i < cnt; ++i)
+        migr_booty_item(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, gang);
+    cnt = rn2(2) + 1;
+    for (i = 0; i < cnt; ++i)
+        migr_booty_item(SKELETON_KEY, gang);
+    migr_booty_item(rn2(2) ? LONG_SWORD : SILVER_SABER, gang);
+    otyp = rn2((GAUNTLETS_OF_DEXTERITY - LEATHER_GLOVES) + 1) + LEATHER_GLOVES;
+    migr_booty_item(otyp, gang);
+    cnt = rn2(9) + 1;
+    for (i = 0; i < cnt; ++i) {
+        /* Food items - but no lembas! (or some other weird things) */
+        otyp = rn2((TIN - TRIPE_RATION) + 1) + TRIPE_RATION;
+        if (otyp != LEMBAS_WAFER && otyp != GLOB_OF_GRAY_OOZE &&
+              otyp != GLOB_OF_BROWN_PUDDING && otyp != GLOB_OF_GREEN_SLIME &&
+              otyp != GLOB_OF_BLACK_PUDDING && otyp != MEAT_STICK &&
+              otyp != MEATBALL && otyp != MEAT_STICK && otyp != MEAT_RING &&
+              otyp != HUGE_CHUNK_OF_MEAT && otyp != CORPSE)
+            migr_booty_item(otyp, gang);
+    }
+    /* Make most of the orcs on the level be part of the invading gang */
+    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+        if (DEADMONSTER(mtmp))
+            continue;
+
+        if (is_orc(mtmp->data) && !has_mname(mtmp) && rn2(10))
+            mtmp = christen_orc(mtmp, upstart(gang));
+    }
+    /* Lastly, ensure there's a few more orcs from the gang along the way
+     * The mechanics are such that they aren't actually identified as
+     * members of the invading gang until they get their spoils assigned
+     * to the inventory; handled during that assignment.
+     */
+    cnt = rn2(6) + 1;
+    for (i = 0; i < cnt; ++i) {
+        int mtyp;
+
+        mtyp = rn2((PM_ORC_SHAMAN - PM_ORC) + 1) + PM_ORC;
+        mtmp = makemon(&mons[mtyp], 0, 0, MM_NONAME);
+        if (mtmp)
+            migrate_orc(mtmp, 0L);
+    }
+       
+    ransacked = 0;
+}
+
+#undef ORC_LEADER
+
 boolean
 maze_inbounds(x, y)
 int x, y;
@@ -819,6 +960,7 @@ const char *s;
     }
 
     if (*protofile) {
+        check_ransacked(protofile);
         Strcat(protofile, LEV_EXT);
         if (load_special(protofile)) {
             /* some levels can end up with monsters
index cf7a77233b2c21982ef23df56dc4cfe23471b810..558be1dc6add8a1bea46c0aef2c9a89b1dee057f 100644 (file)
@@ -224,6 +224,23 @@ boolean init, artif;
     return otmp;
 }
 
+struct obj *
+mksobj_migr_to_species(otyp, mflags2, init, artif)
+int otyp;
+unsigned mflags2;
+boolean init, artif;
+{
+    struct obj *otmp;
+
+    otmp = mksobj(otyp, init, artif);
+    if (otmp) {
+        add_to_migration(otmp);
+        otmp->owornmask = (long) MIGR_TO_SPECIES;
+        otmp->corpsenm = mflags2;
+    }
+    return otmp;
+}
+
 /* mkobj(): select a type of item from a class, use mksobj() to create it */
 struct obj *
 mkobj(oclass, artif)
index 9c2d74946dc8916dea150023d3ccefe24cd77513..6ef7bf6ce47e3d0e57357f2ca262e4446dc7f22d 100644 (file)
@@ -16,8 +16,9 @@ STATIC_DCL void FDECL(look_at_monster, (char *, char *,
                                         struct monst *, int, int));
 STATIC_DCL struct permonst *FDECL(lookat, (int, int, char *, char *));
 STATIC_DCL void FDECL(checkfile, (char *, struct permonst *,
-                                  BOOLEAN_P, BOOLEAN_P));
+                                  BOOLEAN_P, BOOLEAN_P, char *));
 STATIC_DCL void FDECL(look_all, (BOOLEAN_P,BOOLEAN_P));
+STATIC_DCL void FDECL(do_supplemental_info, (char *, struct permonst *,BOOLEAN_P));
 STATIC_DCL void NDECL(whatdoes_help);
 STATIC_DCL void NDECL(docontact);
 STATIC_DCL void NDECL(dispfile_help);
@@ -509,10 +510,11 @@ char *buf, *monbuf;
  *       Therefore, we create a copy of inp _just_ for data.base lookup.
  */
 STATIC_OVL void
-checkfile(inp, pm, user_typed_name, without_asking)
+checkfile(inp, pm, user_typed_name, without_asking, supplemental_name)
 char *inp;
 struct permonst *pm;
 boolean user_typed_name, without_asking;
+char *supplemental_name;
 {
     dlb *fp;
     char buf[BUFSZ], newstr[BUFSZ], givenname[BUFSZ];
@@ -619,7 +621,7 @@ boolean user_typed_name, without_asking;
         int chk_skip, pass = 1;
         boolean yes_to_moreinfo, found_in_file, pass1found_in_file,
                 skipping_entry;
-        char *ap, *alt = 0; /* alternate description */
+        char *sp, *ap, *alt = 0; /* alternate description */
 
         /* adjust the input to remove "named " and "called " */
         if ((ep = strstri(dbase_str, " named ")) != 0) {
@@ -629,6 +631,8 @@ boolean user_typed_name, without_asking;
         } else if ((ep = strstri(dbase_str, " called ")) != 0) {
             copynchars(givenname, ep + 8, BUFSZ - 1);
             alt = givenname;
+            if (supplemental_name && (sp = strstri(inp, " called ")) != 0)
+                copynchars(supplemental_name, sp + 8, BUFSZ - 1);
         } else
             ep = strstri(dbase_str, ", ");
         if (ep && ep > dbase_str)
@@ -760,12 +764,13 @@ boolean user_typed_name, without_asking;
 }
 
 int
-do_screen_description(cc, looked, sym, out_str, firstmatch)
+do_screen_description(cc, looked, sym, out_str, firstmatch, for_supplement)
 coord cc;
 boolean looked;
 int sym;
 char *out_str;
 const char **firstmatch;
+struct permonst **for_supplement;
 {
     static const char mon_interior[] = "the interior of a monster",
                       unreconnoitered[] = "unreconnoitered";
@@ -1008,11 +1013,15 @@ const char **firstmatch;
 
  didlook:
     if (looked) {
+        struct permonst *pm = (struct permonst *)0;
+
         if (found > 1 || need_to_look) {
             char monbuf[BUFSZ];
             char temp_buf[BUFSZ];
 
-            (void) lookat(cc.x, cc.y, look_buf, monbuf);
+            pm = lookat(cc.x, cc.y, look_buf, monbuf);
+            if (pm && for_supplement)
+                *for_supplement = pm;
             *firstmatch = look_buf;
             if (*(*firstmatch)) {
                 Sprintf(temp_buf, " (%s)", *firstmatch);
@@ -1043,7 +1052,7 @@ coord *click_cc;
     boolean clicklook = (mode == 2); /* right mouse-click method */
     char out_str[BUFSZ] = DUMMY;
     const char *firstmatch = 0;
-    struct permonst *pm = 0;
+    struct permonst *pm = 0, *supplemental_pm = 0;
     int i = '\0', ans = 0;
     int sym;              /* typed symbol or converted glyph */
     int found;            /* count of matching syms found */
@@ -1137,7 +1146,7 @@ coord *click_cc;
                     break;
                 }
             if (*out_str)
-                checkfile(out_str, pm, TRUE, TRUE);
+                checkfile(out_str, pm, TRUE, TRUE, (char *) 0);
             return 0;
           }
         case '?':
@@ -1151,7 +1160,7 @@ coord *click_cc;
                 return 0;
 
             if (out_str[1]) { /* user typed in a complete string */
-                checkfile(out_str, pm, TRUE, TRUE);
+                checkfile(out_str, pm, TRUE, TRUE, (char *) 0);
                 return 0;
             }
             sym = out_str[0];
@@ -1204,7 +1213,7 @@ coord *click_cc;
         }
 
         found = do_screen_description(cc, (from_screen || clicklook), sym,
-                                      out_str, &firstmatch);
+                                  out_str, &firstmatch, &supplemental_pm);
 
         /* Finally, print out our explanation. */
         if (found) {
@@ -1215,16 +1224,18 @@ coord *click_cc;
             if (found == 1 && ans != LOOK_QUICK && ans != LOOK_ONCE
                 && (ans == LOOK_VERBOSE || (flags.help && !quick))
                 && !clicklook) {
-                char temp_buf[BUFSZ];
+                char temp_buf[BUFSZ], supplemental_name[BUFSZ];
 
                 Strcpy(temp_buf, firstmatch);
                 checkfile(temp_buf, pm, FALSE,
-                          (boolean) (ans == LOOK_VERBOSE));
+                          (boolean) (ans == LOOK_VERBOSE), supplemental_name);
+                if (supplemental_pm && supplemental_name)
+                    do_supplemental_info(supplemental_name, supplemental_pm,
+                                         (boolean) (ans == LOOK_VERBOSE));
             }
         } else {
             pline("I've never heard of such things.");
         }
-
     } while (from_screen && !quick && ans != LOOK_ONCE && !clicklook);
 
     flags.verbose = save_verbose;
@@ -1319,6 +1330,68 @@ boolean do_mons; /* True => monsters, False => objects */
     destroy_nhwindow(win);
 }
 
+void
+do_supplemental_info(name, pm, without_asking)
+char *name;
+struct permonst *pm;
+boolean without_asking;
+{
+    winid datawin = WIN_ERR;
+    char *entrytext = name, *bp;
+    char question[QBUFSZ];
+    boolean yes_to_moreinfo = FALSE;
+
+    /*
+     * Provide some info on some specific things
+     * meant to support in-game mythology, and not
+     * available from data.base or other sources.
+     */
+    if (name && pm && is_orc(pm) &&
+                (strlen(name) < (BUFSZ - 1)) &&
+                (bp = strstri(name, " of ")) != 0) {
+        char fullname[BUFSZ];
+
+        Strcpy(fullname, name);
+        if (!without_asking) {
+            Strcpy(question, "More info about \"");
+            /* +2 => length of "\"?" */
+            copynchars(eos(question), entrytext,
+                (int) (sizeof question - 1 - (strlen(question) + 2)));
+            Strcat(question, "\"?");
+            if (yn(question) == 'y')
+            yes_to_moreinfo = TRUE;
+        }
+        if (yes_to_moreinfo) {
+            int i, subs = 0;
+            char *gang = bp + 4;
+            char *text[] = {
+                "%s is a member of a marauding horde of orcs",
+                "rumored to have brutally attacked and plundered the ordinarily",
+                "sheltered town that is located deep within The Gnomish Mines.",
+                "",
+                "The members of that vicious horde proudly and defiantly acclaim their",
+                "allegiance to their leader %s in their names.",
+            };
+
+            *bp = '\0';
+            datawin = create_nhwindow(NHW_MENU);
+            for (i = 0; i < SIZE(text); i++) {
+                char buf[BUFSZ], *txt;
+
+                if (strstri(text[i], "%s") != 0) {
+                    Sprintf(buf, text[i],
+                            subs++ ? gang : fullname);
+                    txt = buf;
+                } else
+                    txt = text[i];
+                putstr(datawin, 0, txt);
+            }
+            display_nhwindow(datawin, FALSE);
+            destroy_nhwindow(datawin), datawin = WIN_ERR;
+       }
+    }
+}
+
 /* the '/' command */
 int
 dowhatis()