]> granicus.if.org Git - nethack/commitdiff
Some traps on early depths were triggered already
authorAlex Smith <ais523@nethack4.org>
Fri, 24 Nov 2017 00:42:42 +0000 (00:42 +0000)
committerAlex Smith <ais523@nethack4.org>
Fri, 24 Nov 2017 00:42:42 +0000 (00:42 +0000)
The hero isn't the only adventurer seeking the Amulet. It's clear
from various other events in the game that others have been there
beforehand. As such, we can expect many of the traps on the first
few levels to already have been triggered repeatedly by questing
adventurers.

This commit allows for the creation of adventurer corpses in
early-game traps, together with a small amount of cursed junk
(i.e. a miniature bones pile) and any items created by the trap
itself. On dungeon level 1, this is guaranteed for the vast
majority of harmful traps, in order to avoid near-unavoidable
deaths in the very early game due to not having enough max HP to
survive a trap hit.

Wizard mode testing shows that this case doesn't trigger very
often; maybe once a game on average. (Traps are rare on filler
levels, at least early on, and many types of trap would leave no
evidence, e.g. a teleportation trap won't kill people on its own
square.) As a result, the balance impact from the actual items
here is likely to be minimal (it may help out ranged combat roles
slightly but they could do with the boost). The main change,
therefore, is to reduce the number of unfair very early deaths
(replacing them with fairer "you shouldn't have investigated
what created that corpse!" deaths).

src/mklev.c

index 6fd50f7fe0ffc4311096c8323d5254013ffc60a3..a0e6a3ed7ac92af79e9cc614f8a78ed75e079580 100644 (file)
@@ -1253,6 +1253,7 @@ struct mkroom *croom;
 coord *tm;
 {
     register int kind;
+    unsigned lvl = level_difficulty();
     coord m;
 
     /* no traps in pools */
@@ -1289,8 +1290,6 @@ coord *tm;
         /* bias the frequency of fire traps in Gehennom */
         kind = FIRE_TRAP;
     } else {
-        unsigned lvl = level_difficulty();
-
         do {
             kind = rnd(TRAPNUM - 1);
             /* reject "too hard" traps */
@@ -1366,6 +1365,134 @@ coord *tm;
     (void) maketrap(m.x, m.y, kind);
     if (kind == WEB)
         (void) makemon(&mons[PM_GIANT_SPIDER], m.x, m.y, NO_MM_FLAGS);
+
+    /* The hero isn't the only person who's entered the dungeon in
+       search of treasure. On the very shallowest levels, there's a
+       chance that a created trap will have killed something already
+       (and this is guaranteed on the first level).
+
+       This isn't meant to give any meaningful treasure (in fact, any
+       items we drop here are typically cursed, other than ammo fired
+       by the trap). Rather, it's mostly just for flavour and to give
+       players on very early levels a sufficient chance to avoid traps
+       that may end up killing them before they have a fair chance to
+       build max HP. Including cursed items gives the same fair chance
+       to the starting pet, and fits the rule that possessions of the
+       dead are normally cursed.
+
+       Some types of traps are excluded because they're entirely
+       nonlethal, even indirectly. We also exclude all of the
+       later/fancier traps because they tend to have special
+       considerations (e.g. webs, portals), often are indirectly
+       lethal, and tend not to generate on shallower levels anyway.
+       Finally, pits are excluded because it's weird to see an item
+       in a pit and yet not be able to identify that the pit is there. */
+    if (lvl <= rnd(4) && kind != SQKY_BOARD && kind != RUST_TRAP &&
+        kind != PIT && kind != SPIKED_PIT && kind < HOLE) {
+        /* Object generated by the trap; initially NULL, stays NULL if
+           we fail to generate an object or if the trap doesn't
+           generate objects */
+        struct obj *otmp = NULL;
+        /* Race of the victim */
+        int victim_mnum;
+
+        /* Not all trap types have special handling here; only the
+           ones that kill in a specific way that's obvious after the
+           fact. */
+        switch (kind) {
+        case ARROW_TRAP:
+            otmp = mksobj(ARROW, TRUE, FALSE);
+            otmp->opoisoned = 0;
+            /* don't adjust the quantity; maybe the trap shot multiple
+               times, there was an untrapping attempt, etc... */
+            break;
+        case DART_TRAP:
+            otmp = mksobj(DART, TRUE, FALSE);
+            break;
+        case ROCKTRAP:
+            otmp = mksobj(ROCK, TRUE, FALSE);
+            break;
+        default:
+            /* no item dropped by the trap */
+            break;
+        }
+        if (otmp) {
+            place_object(otmp, m.x, m.y);
+        }
+
+        /* now otmp is reused for other items we're placing */
+
+        /* Place a random possession. This could be a weapon, tool,
+           food, or gem, i.e. the item classes that are typically
+           nonmagical and not worthless. */
+        do {
+            int poss_class;
+            switch (rn2(4)) {
+            case 0:
+                poss_class = WEAPON_CLASS;
+                break;
+            case 1:
+                poss_class = TOOL_CLASS;
+                break;
+            case 2:
+                poss_class = FOOD_CLASS;
+                break;
+            case 3:
+                poss_class = GEM_CLASS;
+                break;
+            }
+
+            otmp = mkobj(poss_class, FALSE);
+            /* these items are always cursed, both for flavour (owned
+               by a dead adventurer, bones-pile-style) and for balance
+               (less useful to use, and encourage pets to avoid the
+               trap) */
+            if (otmp) {
+                otmp->blessed = 0;
+                otmp->cursed = 1;
+                otmp->owt = weight(otmp);
+                place_object(otmp, m.x, m.y);
+            }
+
+            /* 20% chance of placing an additional item, recursively */
+        } while (!rn2(5));
+
+        /* Place a corpse. */
+        switch (rn2(15)) {
+        case 0:
+            /* elf corpses are the rarest as they're the most useful */
+            victim_mnum = PM_ELF;
+            break;
+        case 1: case 2:
+            victim_mnum = PM_DWARF;
+            break;
+        case 3: case 4: case 5:
+            victim_mnum = PM_ORC;
+            break;
+        case 6: case 7: case 8: case 9:
+            /* more common as they could have come from the Mines */
+            victim_mnum = PM_GNOME;
+            /* 10% chance of a candle too */
+            if (!rn2(10)) {
+                otmp = mksobj(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE,
+                              TRUE, FALSE);
+                otmp->quan = 1;
+                otmp->blessed = 0;
+                otmp->cursed = 1;
+                otmp->owt = weight(otmp);
+                place_object(otmp, m.x, m.y);
+            }
+            break;
+        default:
+            /* the most common race */
+            victim_mnum = PM_HUMAN;
+            break;
+        }
+        otmp = mkcorpstat(CORPSE, NULL, &mons[victim_mnum], m.x, m.y,
+                          CORPSTAT_INIT);
+        if (otmp)
+            otmp->age -= 51; /* died too long ago to eat */
+    }
 }
 
 void