diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 93599f07b..56685a76e 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -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 diff --git a/include/dungeon.h b/include/dungeon.h index 7a5677761..75577dbfa 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -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. diff --git a/include/extern.h b/include/extern.h index c2f891b93..4224e421c 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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 *)); diff --git a/src/do.c b/src/do.c index ab38386c8..31407013f 100644 --- 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 */ diff --git a/src/dungeon.c b/src/dungeon.c index 94712d465..26c28bf1a 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -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; diff --git a/src/mhitu.c b/src/mhitu.c index df2c1892c..8b9bf128a 100644 --- a/src/mhitu.c +++ b/src/mhitu.c @@ -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); diff --git a/src/mon.c b/src/mon.c index 8532d89f0..77c7bf073 100644 --- 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; diff --git a/src/read.c b/src/read.c index 87320dca8..abca909bb 100644 --- a/src/read.c +++ b/src/read.c @@ -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)) diff --git a/src/spell.c b/src/spell.c index 5424f468c..ebf2f5d7d 100644 --- a/src/spell.c +++ b/src/spell.c @@ -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 diff --git a/src/teleport.c b/src/teleport.c index 64bba0c77..ba931b51e 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -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; diff --git a/src/weapon.c b/src/weapon.c index 4eb0dd6da..d6e5f98b6 100644 --- a/src/weapon.c +++ b/src/weapon.c @@ -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;