]> granicus.if.org Git - nethack/commitdiff
Major amnesia revamp
authorPasi Kallinen <paxed@alt.org>
Sun, 15 Mar 2020 09:29:32 +0000 (11:29 +0200)
committerPasi Kallinen <paxed@alt.org>
Sun, 15 Mar 2020 09:57:34 +0000 (11:57 +0200)
Instead of forgetting maps and objects, make amnesia forget skills.
Forgetting maps and objects could be circumvented with taking notes,
or by using an external tool to remember the forgotten levels.

Forgetting skills allows the player to optionally go down another
skill path, if they trained the wrong weapon in the early game.

Amnesia still forgets spells.

As a replacement for the deja vu messages when entering a forgotten
level, those messages will now indicate a ghost with your own name
existing on the level, given only when the level is entered for
the first time.

These changes based on fiqhack, with some adjustments.

doc/fixes37.0
include/dungeon.h
include/extern.h
src/do.c
src/dungeon.c
src/mhitu.c
src/mon.c
src/read.c
src/spell.c
src/teleport.c
src/weapon.c

index 93599f07baebbd87d331b31a220115599a4f415c..56685a76ea07159fb9592a40b5cbdece50827d77 100644 (file)
@@ -79,6 +79,7 @@ fix priest created inside temple wall
 fix vault guard occasionally encasing monsters in stone
 tone down scare monster by excluding humans and uniques
 lock the castle chest
+revamp amnesia to forget skills instead of objects or maps
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index 7a5677761e4a44c62f3410276e42ac7dbec4c640..75577dbfa36bee5ac551e40ffed19759fdc75356 100644 (file)
@@ -159,7 +159,7 @@ typedef struct branch {
 struct linfo {
     unsigned char flags;
 #define VISITED 0x01      /* hero has visited this level */
-#define FORGOTTEN 0x02    /* hero will forget this level when reached */
+/* 0x02 was FORGOTTEN, when amnesia made you forget maps */
 #define LFILE_EXISTS 0x04 /* a level file exists for this level */
         /* Note:  VISITED and LFILE_EXISTS are currently almost always
          * set at the same time.  However they _mean_ different things.
index c2f891b939f00d9be6b5679420ec6bb5d4b92dff..4224e421cc30476e1a2118deae13b9c2d41678b3 100644 (file)
@@ -666,7 +666,6 @@ E char *FDECL(get_annotation, (d_level *));
 E int NDECL(donamelevel);
 E int NDECL(dooverview);
 E void FDECL(show_overview, (int, int));
-E void FDECL(forget_mapseen, (int));
 E void FDECL(rm_mapseen, (int));
 E void FDECL(init_mapseen, (d_level *));
 E void NDECL(recalc_mapseen);
@@ -1509,6 +1508,7 @@ E void NDECL(kill_genocided_monsters);
 E void FDECL(golemeffects, (struct monst *, int, int));
 E boolean FDECL(angry_guards, (BOOLEAN_P));
 E void NDECL(pacify_guards);
+E struct monst *FDECL(find_ghost_with_name, (char *));
 E void FDECL(decide_to_shapeshift, (struct monst *, int));
 E boolean FDECL(vamp_stone, (struct monst *));
 E void NDECL(monst_globals_init);
@@ -2166,10 +2166,6 @@ E char *FDECL(tshirt_text, (struct obj *, char *));
 E int NDECL(doread);
 E boolean FDECL(is_chargeable, (struct obj *));
 E void FDECL(recharge, (struct obj *, int));
-E void FDECL(forget_objects, (int));
-E void FDECL(forget_levels, (int));
-E void NDECL(forget_traps);
-E void FDECL(forget_map, (int));
 E int FDECL(seffects, (struct obj *));
 E void FDECL(drop_boulder_on_player,
              (BOOLEAN_P, BOOLEAN_P, BOOLEAN_P, BOOLEAN_P));
@@ -2956,6 +2952,7 @@ E void FDECL(unrestrict_weapon_skill, (int));
 E void FDECL(use_skill, (int, int));
 E void FDECL(add_weapon_skill, (int));
 E void FDECL(lose_weapon_skill, (int));
+E void FDECL(drain_weapon_skill, (int));
 E int FDECL(weapon_type, (struct obj *));
 E int NDECL(uwep_skill_type);
 E int FDECL(weapon_hit_bonus, (struct obj *));
index ab38386c8c4fd4b3a9d9f208f27eb713dafe335a..31407013f1e88902e446db0b0d105cbe225627df 100644 (file)
--- a/src/do.c
+++ b/src/do.c
@@ -1461,12 +1461,14 @@ boolean at_stairs, falling, portal;
 
     if (!(g.level_info[new_ledger].flags & LFILE_EXISTS)) {
         /* entering this level for first time; make it now */
-        if (g.level_info[new_ledger].flags & (FORGOTTEN | VISITED)) {
+        if (g.level_info[new_ledger].flags & (VISITED)) {
             impossible("goto_level: returning to discarded level?");
-            g.level_info[new_ledger].flags &= ~(FORGOTTEN | VISITED);
+            g.level_info[new_ledger].flags &= ~(VISITED);
         }
         mklev();
         new = TRUE; /* made the level */
+
+        familiar = (find_ghost_with_name(g.plname) != (struct monst *) 0);
     } else {
         /* returning to previously visited level; reload it */
         nhfp = open_levelfile(new_ledger, whynot);
@@ -1606,13 +1608,6 @@ boolean at_stairs, falling, portal;
     else if (Is_firelevel(&u.uz))
         fumaroles();
 
-    if (g.level_info[new_ledger].flags & FORGOTTEN) {
-        forget_map(ALL_MAP); /* forget the map */
-        forget_traps();      /* forget all traps too */
-        familiar = TRUE;
-        g.level_info[new_ledger].flags &= ~FORGOTTEN;
-    }
-
     /* Reset the screen. */
     vision_reset(); /* reset the blockages */
     g.glyphmap_perlevel_flags = 0L; /* force per-level mapglyph() changes */
index 94712d4651223b0e420cd3e288685cfa5ed68d36..26c28bf1ac63698467d5a227da245ecc9b2050c3 100644 (file)
@@ -1958,7 +1958,7 @@ const char *nam;
                  && dlev.dnum == valley_level.dnum))
             && (/* either wizard mode or else seen and not forgotten */
                 wizard
-                || (g.level_info[idx].flags & (FORGOTTEN | VISITED))
+                || (g.level_info[idx].flags & (VISITED))
                        == VISITED)) {
             lev = depth(&dlev);
         }
@@ -1973,9 +1973,9 @@ const char *nam;
             idx &= 0x00FF;
             /* either wizard mode, or else _both_ sides of branch seen */
             if (wizard
-                || (((g.level_info[idx].flags & (FORGOTTEN | VISITED))
+                || (((g.level_info[idx].flags & (VISITED))
                      == VISITED)
-                    && ((g.level_info[idxtoo].flags & (FORGOTTEN | VISITED))
+                    && ((g.level_info[idxtoo].flags & (VISITED))
                         == VISITED))) {
                 if (ledger_to_dnum(idxtoo) == u.uz.dnum)
                     idx = idxtoo;
@@ -2392,35 +2392,6 @@ const char *s;
 }
 
 
-void
-forget_mapseen(ledger_num)
-int ledger_num;
-{
-    mapseen *mptr;
-    struct cemetery *bp;
-
-    for (mptr = g.mapseenchn; mptr; mptr = mptr->next)
-        if (g.dungeons[mptr->lev.dnum].ledger_start + mptr->lev.dlevel
-            == ledger_num)
-            break;
-
-    /* if not found, then nothing to forget */
-    if (mptr) {
-        mptr->flags.forgot = 1;
-        mptr->br = (branch *) 0;
-
-        /* custom names are erased, not just forgotten until revisited */
-        if (mptr->custom) {
-            mptr->custom_lth = 0;
-            free((genericptr_t) mptr->custom);
-            mptr->custom = (char *) 0;
-        }
-        (void) memset((genericptr_t) mptr->msrooms, 0, sizeof mptr->msrooms);
-        for (bp = mptr->final_resting_place; bp; bp = bp->next)
-            bp->bonesknown = FALSE;
-    }
-}
-
 void
 rm_mapseen(ledger_num)
 int ledger_num;
index df2c1892c3fe78a6986179fae8b569be200fc884..8b9bf128a578ca119c74a39fe415bbe180c6ee0b 100644 (file)
@@ -1175,8 +1175,6 @@ register struct attack *mattk;
         }
         /* adjattrib gives dunce cap message when appropriate */
         (void) adjattrib(A_INT, -rnd(2), FALSE);
-        forget_levels(25);  /* lose memory of 25% of levels */
-        forget_objects(25); /* lose memory of 25% of objects */
         break;
     case AD_PLYS:
         hitmsg(mtmp, mattk);
index 8532d89f070940346d873af39ff18c429308fdf9..77c7bf073815b2355c1fbeb642821586492bd6c7 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -4170,6 +4170,22 @@ pacify_guards()
     }
 }
 
+struct monst *
+find_ghost_with_name(str)
+char *str;
+{
+    struct monst *mtmp;
+
+    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+        if (DEADMONSTER(mtmp)
+            || mtmp->data != &mons[PM_GHOST] || !has_mname(mtmp))
+            continue;
+        if (!strcmpi(MNAME(mtmp), str))
+            return mtmp;
+    }
+    return (struct monst *) 0;
+}
+
 void
 mimic_hit_msg(mtmp, otyp)
 struct monst *mtmp;
index 87320dca82532295dfd3c0f35a8841f5c0011866..abca909bb0f6ef13b2755ae223e5929ee9a51c7a 100644 (file)
@@ -22,10 +22,6 @@ static char *FDECL(apron_text, (struct obj *, char *buf));
 static void FDECL(stripspe, (struct obj *));
 static void FDECL(p_glow1, (struct obj *));
 static void FDECL(p_glow2, (struct obj *, const char *));
-static void FDECL(forget_single_object, (int));
-#if 0 /* not used */
-static void FDECL(forget_objclass, (int));
-#endif
 static void FDECL(randomize, (int *, int));
 static void FDECL(forget, (int));
 static int FDECL(maybe_tame, (struct monst *, struct obj *));
@@ -711,36 +707,6 @@ int curse_bless;
     }
 }
 
-/* Forget known information about this object type. */
-static void
-forget_single_object(obj_id)
-int obj_id;
-{
-    objects[obj_id].oc_name_known = 0;
-    objects[obj_id].oc_pre_discovered = 0; /* a discovery when relearned */
-    if (objects[obj_id].oc_uname) {
-        free((genericptr_t) objects[obj_id].oc_uname);
-        objects[obj_id].oc_uname = 0;
-    }
-    undiscover_object(obj_id); /* after clearing oc_name_known */
-
-    /* clear & free object names from matching inventory items too? */
-}
-
-#if 0 /* here if anyone wants it.... */
-/* Forget everything known about a particular object class. */
-static void
-forget_objclass(oclass)
-int oclass;
-{
-    int i;
-
-    for (i = g.bases[oclass];
-         i < NUM_OBJECTS && objects[i].oc_class == oclass; i++)
-        forget_single_object(i);
-}
-#endif
-
 /* randomize the given list of numbers  0 <= i < count */
 static void
 randomize(indices, count)
@@ -758,136 +724,13 @@ int count;
     }
 }
 
-/* Forget % of known objects. */
-void
-forget_objects(percent)
-int percent;
-{
-    int i, count;
-    int indices[NUM_OBJECTS];
-
-    if (percent == 0)
-        return;
-    if (percent <= 0 || percent > 100) {
-        impossible("forget_objects: bad percent %d", percent);
-        return;
-    }
-
-    indices[0] = 0; /* lint suppression */
-    for (count = 0, i = 1; i < NUM_OBJECTS; i++)
-        if (OBJ_DESCR(objects[i])
-            && (objects[i].oc_name_known || objects[i].oc_uname))
-            indices[count++] = i;
-
-    if (count > 0) {
-        randomize(indices, count);
-
-        /* forget first % of randomized indices */
-        count = ((count * percent) + rn2(100)) / 100;
-        for (i = 0; i < count; i++)
-            forget_single_object(indices[i]);
-    }
-}
-
-/* Forget some or all of map (depends on parameters). */
-void
-forget_map(howmuch)
-int howmuch;
-{
-    register int zx, zy;
-
-    if (Sokoban)
-        return;
-
-    g.known = TRUE;
-    for (zx = 0; zx < COLNO; zx++)
-        for (zy = 0; zy < ROWNO; zy++)
-            if (howmuch & ALL_MAP || rn2(7)) {
-                /* Zonk all memory of this location. */
-                levl[zx][zy].seenv = 0;
-                levl[zx][zy].waslit = 0;
-                levl[zx][zy].glyph = GLYPH_UNEXPLORED;
-                g.lastseentyp[zx][zy] = STONE;
-            }
-    /* forget overview data for this level */
-    forget_mapseen(ledger_no(&u.uz));
-}
-
-/* Forget all traps on the level. */
-void
-forget_traps()
-{
-    register struct trap *trap;
-
-    /* forget all traps (except the one the hero is in :-) */
-    for (trap = g.ftrap; trap; trap = trap->ntrap)
-        if ((trap->tx != u.ux || trap->ty != u.uy) && (trap->ttyp != HOLE))
-            trap->tseen = 0;
-}
-
-/*
- * Forget given % of all levels that the hero has visited and not forgotten,
- * except this one.
- */
-void
-forget_levels(percent)
-int percent;
-{
-    int i, count;
-    xchar maxl, this_lev;
-    int indices[MAXLINFO];
-
-    if (percent == 0)
-        return;
-
-    if (percent <= 0 || percent > 100) {
-        impossible("forget_levels: bad percent %d", percent);
-        return;
-    }
-
-    this_lev = ledger_no(&u.uz);
-    maxl = maxledgerno();
-
-    /* count & save indices of non-forgotten visited levels */
-    /* Sokoban levels are pre-mapped for the player, and should stay
-     * so, or they become nearly impossible to solve.  But try to
-     * shift the forgetting elsewhere by fiddling with percent
-     * instead of forgetting fewer levels.
-     */
-    indices[0] = 0; /* lint suppression */
-    for (count = 0, i = 0; i <= maxl; i++)
-        if ((g.level_info[i].flags & VISITED)
-            && !(g.level_info[i].flags & FORGOTTEN) && i != this_lev) {
-            if (ledger_to_dnum(i) == sokoban_dnum)
-                percent += 2;
-            else
-                indices[count++] = i;
-        }
-
-    if (percent > 100)
-        percent = 100;
-
-    if (count > 0) {
-        randomize(indices, count);
-
-        /* forget first % of randomized indices */
-        count = ((count * percent) + 50) / 100;
-        for (i = 0; i < count; i++) {
-            g.level_info[indices[i]].flags |= FORGOTTEN;
-            forget_mapseen(indices[i]);
-        }
-    }
-}
-
 /*
  * Forget some things (e.g. after reading a scroll of amnesia).  When called,
  * the following are always forgotten:
  *      - felt ball & chain
- *      - traps
- *      - part (6 out of 7) of the map
+ *      - skill training
  *
  * Other things are subject to flags:
- *      howmuch & ALL_MAP       = forget whole map
  *      howmuch & ALL_SPELLS    = forget all spells
  */
 static void
@@ -897,30 +740,11 @@ int howmuch;
     if (Punished)
         u.bc_felt = 0; /* forget felt ball&chain */
 
-    forget_map(howmuch);
-    forget_traps();
-
-    /* 1 in 3 chance of forgetting some levels */
-    if (!rn2(3))
-        forget_levels(rn2(25));
-
-    /* 1 in 3 chance of forgetting some objects */
-    if (!rn2(3))
-        forget_objects(rn2(25));
-
     if (howmuch & ALL_SPELLS)
         losespells();
-    /*
-     * Make sure that what was seen is restored correctly.  To do this,
-     * we need to go blind for an instant --- turn off the display,
-     * then restart it.  All this work is needed to correctly handle
-     * walls which are stone on one side and wall on the other.  Turning
-     * off the seen bits above will make the wall revert to stone,  but
-     * there are cases where we don't want this to happen.  The easiest
-     * thing to do is to run it through the vision system again, which
-     * is always correct.
-     */
-    docrt(); /* this correctly will reset vision */
+
+    /* Forget some skills. */
+    drain_weapon_skill(rnd(howmuch ? 5 : 3));
 }
 
 /* monster is hit by scroll of taming's effect */
@@ -1579,8 +1403,7 @@ struct obj *sobj; /* scroll, or fake spellbook object for scroll-like spell */
         break;
     case SCR_AMNESIA:
         g.known = TRUE;
-        forget((!sblessed ? ALL_SPELLS : 0)
-               | (!confused || scursed ? ALL_MAP : 0));
+        forget((!sblessed ? ALL_SPELLS : 0));
         if (Hallucination) /* Ommmmmm! */
             Your("mind releases itself from mundane concerns.");
         else if (!strncmpi(g.plname, "Maud", 4))
index 5424f468c03afc003aa5473897d8b367bf8c0f88..ebf2f5d7d19381b9158dda1fb8a194b681346a0c 100644 (file)
@@ -397,8 +397,6 @@ learn(VOID_ARGS)
             book->spestudied++;
             exercise(A_WIS, TRUE); /* extra study */
         }
-        /* make book become known even when spell is already
-           known, in case amnesia made you forget the book */
         makeknown((int) booktype);
     } else { /* (spellid(i) == NO_SPELL) */
         /* for a normal book, spestudied will be zero, but for
index 64bba0c77e557ffad5fd6261c7298485d921b53d..ba931b51ebdb0f18de363ad1b11fd8e1997a3728 100644 (file)
@@ -719,11 +719,7 @@ boolean break_the_rules; /* True: wizard mode ^T */
 
         if (!Teleportation || (u.ulevel < (Role_if(PM_WIZARD) ? 8 : 12)
                                && !can_teleport(g.youmonst.data))) {
-            /* Try to use teleport away spell.
-               Prior to 3.6.2 this used to require that you know the spellbook
-               (probably just intended as an optimization to skip the
-               lookup loop) but it is possible to know and cast a spell
-               after forgetting its book due to amnesia. */
+            /* Try to use teleport away spell. */
             for (sp_no = 0; sp_no < MAXSPELL; sp_no++)
                 if (g.spl_book[sp_no].sp_id == SPE_TELEPORT_AWAY)
                     break;
index 4eb0dd6da79240ee3d810ba055199362e874d758..d6e5f98b67c0fe5ee363237531a8cddaf1f9e4e6 100644 (file)
@@ -1373,6 +1373,45 @@ int n; /* number of slots to lose; normally one */
     }
 }
 
+void
+drain_weapon_skill(n)
+int n; /* number of skills to drain */
+{
+    int skill;
+    int i;
+    int tmpskills[P_NUM_SKILLS];
+    int tmpidx = 0;
+
+    (void) memset((genericptr_t) tmpskills, 0, sizeof(tmpskills));
+
+    while (--n >= 0) {
+        if (u.skills_advanced) {
+            /* Pick a random skill, deleting it from the list. */
+            i = rn2(u.skills_advanced);
+            skill = u.skill_record[i];
+            tmpskills[skill] = 1;
+            for (; i < u.skills_advanced - 1; i++) {
+                u.skill_record[i] = u.skill_record[i + 1];
+            }
+            u.skills_advanced--;
+            if (P_SKILL(skill) <= P_UNSKILLED)
+                panic("drain_weapon_skill (%d)", skill);
+            P_SKILL(skill)--;   /* drop skill one level */
+            /* refund slots used for skill */
+            u.weapon_slots += slots_required(skill);
+            /* drain a random proportion of skill training */
+            if (P_ADVANCE(skill))
+                P_ADVANCE(skill) = rn2(P_ADVANCE(skill));
+        }
+    }
+
+    for (skill = 0; skill < P_NUM_SKILLS; skill++)
+        if (tmpskills[skill]) {
+            You("forget %syour training in %s.",
+                P_SKILL(skill) >= P_BASIC ? "some of " : "", P_NAME(skill));
+        }
+}
+
 int
 weapon_type(obj)
 struct obj *obj;