--- include/extern.h.orig 2004-09-03 23:22:31.000000000 -0500 +++ include/extern.h 2004-09-05 19:57:41.791219016 -0500 @@ -55,10 +55,19 @@ E void FDECL(save_artifacts, (int)); E void FDECL(restore_artifacts, (int)); E const char *FDECL(artiname, (int)); -E struct obj *FDECL(mk_artifact, (struct obj *,ALIGNTYP_P)); E const char *FDECL(artifact_name, (const char *,short *)); +#ifdef EVENTLOG +E XCHAR_P FDECL(exist_artifact, (int,const char *)); +E void FDECL(artifact_exists, (struct obj *,const char *,XCHAR_P)); +E struct obj *FDECL(mk_artifact, (struct obj *,ALIGNTYP_P,XCHAR_P)); +E XCHAR_P FDECL(align2artgen, (ALIGNTYP_P)); +E ALIGNTYP_P FDECL(artgen2align, (XCHAR_P)); +E XCHAR_P FDECL(how_arti_was_created, (char)); +#else E boolean FDECL(exist_artifact, (int,const char *)); E void FDECL(artifact_exists, (struct obj *,const char *,BOOLEAN_P)); +E struct obj *FDECL(mk_artifact, (struct obj *,ALIGNTYP_P)); +#endif E int NDECL(nartifact_exist); E boolean FDECL(spec_ability, (struct obj *,unsigned long)); E boolean FDECL(confers_luck, (struct obj *)); @@ -179,6 +188,17 @@ E void NDECL(sanity_check); #endif E char FDECL(yn_function, (const char *, const char *, CHAR_P)); +#ifdef EVENTLOG +E void FDECL(show_log, (int)); +E void FDECL(addEventToLog, (struct u_log *, LogEntryType, boolean, unsigned long, unsigned long, unsigned long)); +E void FDECL(addMonsterKillToLog, (boolean, short, unsigned long, unsigned long)); +E void FDECL(addArtifactToLog, (boolean, boolean, short)); +E void NDECL(solveSokobanIfNecessary); +E void FDECL(loseVirginity, (short, boolean)); +E void FDECL(forgetLog, (struct u_log *)); +E void FDECL(recordLevelIfNecessary, (struct d_level *)); +E void FDECL(learnedPasstune, (BOOLEAN_P, ALIGNTYP_P)); +#endif /* ### dbridge.c ### */ @@ -322,6 +342,10 @@ E int FDECL(getpos, (coord *,BOOLEAN_P,const char *)); E struct monst *FDECL(christen_monst, (struct monst *,const char *)); E int NDECL(do_mname); +#ifdef EVENTLOG +/* This is here because you can't use default arguments with FDECL. */ +E struct obj *FDECL(oname2, (struct obj *,const char *, XCHAR_P)); +#endif E struct obj *FDECL(oname, (struct obj *,const char *)); E int NDECL(ddocall); E void FDECL(docall, (struct obj *)); @@ -1036,14 +1060,25 @@ /* ### mkobj.c ### */ + +#ifdef EVENTLOG +E struct obj *FDECL(mkobj_at, (CHAR_P,int,int,XCHAR_P)); +E struct obj *FDECL(mksobj_at, (int,int,int,BOOLEAN_P,XCHAR_P)); +E struct obj *FDECL(mkobj, (CHAR_P,XCHAR_P)); +#else 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(mkobj, (CHAR_P,BOOLEAN_P)); +#endif E int NDECL(rndmonnum); E struct obj *FDECL(splitobj, (struct obj *,long)); E void FDECL(replace_object, (struct obj *,struct obj *)); E void FDECL(bill_dummy_object, (struct obj *)); +#ifdef EVENTLOG +E struct obj *FDECL(mksobj, (int,BOOLEAN_P,XCHAR_P)); +#else E struct obj *FDECL(mksobj, (int,BOOLEAN_P,BOOLEAN_P)); +#endif E int FDECL(bcsign, (struct obj *)); E int FDECL(weight, (struct obj *)); E struct obj *FDECL(mkgold, (long,int,int)); @@ -1620,6 +1655,9 @@ E void NDECL(unload_qtlist); E short FDECL(quest_info, (int)); E const char *NDECL(ldrname); +#ifdef EVENTLOG +E const char *NDECL(neminame); +#endif E boolean FDECL(is_quest_artifact, (struct obj*)); E void FDECL(com_pager, (int)); E void FDECL(qt_pager, (int)); --- include/you.h.orig 2004-09-02 22:17:51.000000000 -0500 +++ include/you.h 2004-09-06 00:17:32.660046688 -0500 @@ -72,6 +72,52 @@ /* genocides already listed at end of game */ }; +#ifdef EVENTLOG +/* jgibson, event log + * These are logs of arbitrary events. + */ +typedef enum { + evt_EnteredDungeon = 1, + evt_FirstKill = 2, + evt_StartQuest = 3, + evt_EjectedQuest = 4, + evt_FinishQuest = 5, + evt_KillMonster = 6, + evt_GetArtifact = 7, +#ifdef SEDUCE + evt_LoseVirginity = 8, +#endif + evt_ReachDungeonLevel = 9, + evt_SolveSokobanLevel = 10, + evt_LearnPasstune = 11, +#ifdef ELBERETH + evt_Crowned = 12, +#endif + evt_Converted = 13, + evt_OpenedSanctum = 14 +} LogEntryType; + +struct u_log_entry { + LogEntryType type; + unsigned long move; + boolean hidden; + struct u_log_entry *next; + /* jgibson, this is an arbitrary + * choice of 3 data fields */ + unsigned long field1; + unsigned long field2; + unsigned long field3; +}; + +struct u_log { + struct u_log_entry *first; + struct u_log_entry *last; + long entry_count; + Bitfield(lostvirginity,1); + Bitfield(unused,7); +}; +#endif + /*** Unified structure containing role information ***/ struct Role { /*** Strings that name various things ***/ @@ -309,6 +355,9 @@ struct u_event uevent; /* certain events have happened */ struct u_have uhave; /* you're carrying special objects */ struct u_conduct uconduct; /* KMH, conduct */ +#if (EVENTLOG && 0) + struct u_log ulog; /* jgibson, event log */ +#endif struct attribs acurr, /* your current attributes (eg. str)*/ aexe, /* for gain/loss via "exercise" */ abon, /* your bonus attributes (eg. str) */ --- include/decl.h.orig 2004-09-05 22:56:33.805704824 -0500 +++ include/decl.h 2004-09-06 00:15:09.231851088 -0500 @@ -58,6 +58,13 @@ d_level d_wiz3_level; d_level d_juiblex_level; d_level d_orcus_level; +#ifdef EVENTLOG +/* These are all used by the EVENTLOG patch. + d_baalzebub_level; + d_asmodeus_level; + d_portal_level; +*/ +#endif d_level d_baalzebub_level; /* unused */ d_level d_asmodeus_level; /* unused */ d_level d_portal_level; /* only in goto_level() [do.c] */ @@ -72,6 +79,11 @@ xchar d_mines_dnum, d_quest_dnum; d_level d_qstart_level, d_qlocate_level, d_nemesis_level; d_level d_knox_level; +#if (EVENTLOG && 0) + /* Deferred because it breaks bones compatibility. */ + d_level d_minend_level; + d_level d_minetn_level; +#endif } dungeon_topology; /* macros for accesing the dungeon levels by their old names */ #define oracle_level (dungeon_topology.d_oracle_level) @@ -104,6 +116,11 @@ #define qlocate_level (dungeon_topology.d_qlocate_level) #define nemesis_level (dungeon_topology.d_nemesis_level) #define knox_level (dungeon_topology.d_knox_level) +#if (EVENTLOG && 0) + /* Deferred because it breaks bones compatibility. */ +#define minend_level (dungeon_topology.d_minend_level) +#define minetn_level (dungeon_topology.d_minetn_level) +#endif E NEARDATA stairway dnstair, upstair; /* stairs up and down */ #define xdnstair (dnstair.sx) @@ -385,6 +402,10 @@ }; #endif /* AUTOPICKUP_EXCEPTIONS */ +#ifdef EVENTLOG +E NEARDATA struct u_log ulog; +#endif + #undef E #endif /* DECL_H */ --- include/dungeon.h.orig 2004-09-05 21:00:36.295407160 -0500 +++ include/dungeon.h 2004-09-05 22:49:54.829358448 -0500 @@ -121,6 +121,12 @@ #define Is_qlocate(x) (on_level(x, &qlocate_level)) #define Is_nemesis(x) (on_level(x, &nemesis_level)) #define Is_knox(x) (on_level(x, &knox_level)) +#ifdef EVENTLOG +#define Is_orcus_level(x) (on_level(x, &orcus_level)) +#define Is_minetown_level(x) (on_level(x, &minetn_level)) +#define Is_minesend_level(x) (on_level(x, &minend_level)) +#endif + #define In_sokoban(x) ((x)->dnum == sokoban_dnum) #define Inhell In_hell(&u.uz) /* now gehennom */ --- include/flag.h.orig 2004-09-05 01:30:41.000000000 -0500 +++ include/flag.h 2004-09-05 01:29:46.000000000 -0500 @@ -100,7 +100,11 @@ int pickup_burden; /* maximum burden before prompt */ char inv_order[MAXOCLASSES]; char pickup_types[MAXOCLASSES]; +#ifdef EVENTLOG +#define NUM_DISCLOSURE_OPTIONS 6 +#else #define NUM_DISCLOSURE_OPTIONS 5 +#endif #define DISCLOSE_PROMPT_DEFAULT_YES 'y' #define DISCLOSE_PROMPT_DEFAULT_NO 'n' #define DISCLOSE_YES_WITHOUT_PROMPT '+' --- include/hack.h.orig 2004-09-04 22:00:45.000000000 -0500 +++ include/hack.h 2004-09-04 22:04:26.000000000 -0500 @@ -111,6 +111,9 @@ #include "engrave.h" #include "rect.h" #include "region.h" +#ifdef EVENTLOG +# include "artifact.h" +#endif #ifdef USE_TRAMPOLI /* This doesn't belong here, but we have little choice */ #undef NDECL --- include/artifact.h.orig 2004-09-04 17:55:10.000000000 -0500 +++ include/artifact.h 2004-09-04 22:04:38.000000000 -0500 @@ -35,6 +35,20 @@ #define SPFX_XRAY 0x2000000L /* gives X-RAY vision to player */ #define SPFX_REFLECT 0x4000000L /* Reflection */ +// This should be here but artifact structure interferes +#ifdef EVENTLOG +# define ARTGEN_NOTEXISTS 0 /* the artifact does not exist, + this must remain 0 to make for easier + iffing by the program. */ +# define ARTGEN_GOD_LAWFUL 1 /* the artifact was created by a lawful god */ +# define ARTGEN_GOD_NEUTRAL 2 /* the artifact was created by a neutral god */ +# define ARTGEN_GOD_CHAOTIC 3 /* the artifact was created by a chaotic god */ +# define ARTGEN_GOD_EVIL 4 /* the artifact was created by an evil god (this probably won't happen too often)*/ +# define ARTGEN_PLAYER 5 /* the artifact was created by a player */ +# define ARTGEN_RANDOM 6 /* the artifact was created randomly */ +# define ARTGEN_ALWAYS 7 /* the artifact always exists */ +# define ARTGEN_WISH 8 /* the artifact was created by a wish */ +#endif struct artifact { short otyp; --- src/do.c.orig 2004-09-05 04:08:57.074091408 -0500 +++ src/do.c 2004-09-05 04:17:23.120160712 -0500 @@ -1353,6 +1353,10 @@ save_currentstate(); #endif +#ifdef EVENTLOG + if(new) + recordLevelIfNecessary(&u.uz); +#endif /* assume this will always return TRUE when changing level */ (void) in_out_region(u.ux, u.uy); (void) pickup(1); --- src/invent.c.orig 2004-09-03 23:22:57.000000000 -0500 +++ src/invent.c 2004-09-05 19:19:42.285756520 -0500 @@ -181,6 +181,9 @@ if (otmp->oclass == COIN_CLASS) otmp->owt = weight(otmp); else otmp->owt += obj->owt; if(!otmp->onamelth && obj->onamelth) +#ifdef EVENTLOG + /* Don't worry about this. Aritfacts can't be merged. */ +#endif otmp = *potmp = oname(otmp, ONAME(obj)); obj_extract_self(obj); @@ -336,6 +339,14 @@ addinv_core2(obj); carry_obj_effects(obj); /* carrying affects the obj */ update_inventory(); +#ifdef EVENTLOG + if(!obj->known) { + if(obj->oartifact) + addArtifactToLog(TRUE, TRUE, obj->oartifact); + else if(objects[obj->otyp].oc_unique) + addArtifactToLog(TRUE, FALSE, obj->otyp); + } +#endif return(obj); } @@ -1469,7 +1480,15 @@ struct obj *otmp; { makeknown(otmp->otyp); - if (otmp->oartifact) discover_artifact((xchar)otmp->oartifact); + if (otmp->oartifact) + discover_artifact((xchar)otmp->oartifact); +#ifdef EVENTLOG + /* Unhide unique object or add it, if not found. It should + * have already been added by picking it up. + */ + else if(objects[otmp->otyp].oc_unique) + addArtifactToLog(FALSE, FALSE, otmp->otyp); +#endif otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1; if (otmp->otyp == EGG && otmp->corpsenm != NON_PM) learn_egg_type(otmp->corpsenm); --- src/dokick.c.orig 2004-09-04 21:54:08.000000000 -0500 +++ src/dokick.c 2004-09-04 21:08:49.000000000 -0500 @@ -982,7 +982,12 @@ if(!(maploc->looted & S_LRING)) { /* once per sink */ if (!Blind) You("see a ring shining in its midst."); - (void) mkobj_at(RING_CLASS, x, y, TRUE); + (void) mkobj_at(RING_CLASS, x, y, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif newsym(x, y); exercise(A_DEX, TRUE); exercise(A_WIS, TRUE); /* a discovery! */ --- src/shknam.c.orig 2004-09-04 21:54:27.000000000 -0500 +++ src/shknam.c 2004-09-04 21:09:48.000000000 -0500 @@ -263,9 +263,19 @@ } else { atype = get_shop_item(shp - shtypes); if (atype < 0) - (void) mksobj_at(-atype, sx, sy, TRUE, TRUE); + (void) mksobj_at(-atype, sx, sy, TRUE, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif else - (void) mkobj_at(atype, sx, sy, TRUE); + (void) mkobj_at(atype, sx, sy, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif } } --- src/allmain.c.orig 2004-09-03 01:28:08.000000000 -0500 +++ src/allmain.c 2004-09-06 00:20:10.910988888 -0500 @@ -537,6 +537,10 @@ #endif program_state.something_worth_saving++; /* useful data now exists */ +#ifdef EVENTLOG + ulog.entry_count = 0; + addEventToLog(&ulog, evt_EnteredDungeon, FALSE, u.ualign.type, 0, 0); +#endif /* Success! */ welcome(TRUE); return; --- src/sp_lev.c.orig 2004-09-04 21:54:39.000000000 -0500 +++ src/sp_lev.c 2004-09-04 22:42:03.000000000 -0500 @@ -931,9 +931,19 @@ c = 0; if (!c) - otmp = mkobj_at(RANDOM_CLASS, x, y, !named); + otmp = mkobj_at(RANDOM_CLASS, x, y, +#ifdef EVENTLOG + named ? ARTGEN_NOTEXISTS : ARTGEN_ALWAYS); +#else + !named); +#endif else if (o->id != -1) - otmp = mksobj_at(o->id, x, y, TRUE, !named); + otmp = mksobj_at(o->id, x, y, TRUE, +#ifdef EVENTLOG + named ? ARTGEN_NOTEXISTS : ARTGEN_ALWAYS); +#else + !named); +#endif else { /* * The special levels are compiled with the default "text" object @@ -976,7 +986,11 @@ } if (named) +#ifdef EVENTLOG + otmp = oname2(otmp, o->name.str, ARTGEN_ALWAYS); +#else otmp = oname(otmp, o->name.str); +#endif switch(o->containment) { static struct obj *container = 0; @@ -2590,7 +2604,11 @@ for(x = rnd((int) (20 * mapfact) / 100); x; x--) { maze1xy(&mm, DRY); (void) mkobj_at(rn2(2) ? GEM_CLASS : RANDOM_CLASS, +#ifdef EVENTLOG + mm.x, mm.y, ARTGEN_RANDOM); +#else mm.x, mm.y, TRUE); +#endif } for(x = rnd((int) (12 * mapfact) / 100); x; x--) { maze1xy(&mm, DRY); --- src/makemon.c.orig 2004-09-04 21:54:52.000000000 -0500 +++ src/makemon.c 2004-09-04 23:33:36.000000000 -0500 @@ -611,7 +611,12 @@ (void)mongets(mtmp, (rn2(7) ? ATHAME : WAN_NOTHING)); else if (ptr == &mons[PM_ARCH_LICH] && !rn2(3)) { otmp = mksobj(rn2(3) ? ATHAME : QUARTERSTAFF, - TRUE, rn2(13) ? FALSE : TRUE); + TRUE, rn2(13) ? +#ifdef EVENTLOG + ARTGEN_NOTEXISTS : ARTGEN_RANDOM); +#else + FALSE : TRUE); +#endif if (otmp->spe < 2) otmp->spe = rnd(3); if (!rn2(4)) otmp->oerodeproof = 1; (void) mpickobj(mtmp, otmp); @@ -960,7 +965,12 @@ case S_SNAKE: if(in_mklev) if(x && y) - (void) mkobj_at(0, x, y, TRUE); + (void) mkobj_at(0, x, y, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif if(hides_under(ptr) && OBJ_AT(x, y)) mtmp->mundetected = TRUE; break; --- src/cmd.c.orig 2004-09-02 22:17:33.000000000 -0500 +++ src/cmd.c 2004-09-06 00:20:10.316079328 -0500 @@ -142,6 +142,9 @@ STATIC_PTR int NDECL(enter_explore_mode); STATIC_PTR int NDECL(doattributes); STATIC_PTR int NDECL(doconduct); /**/ +#ifdef EVENTLOG +STATIC_PTR int NDECL(dolog); +#endif STATIC_PTR boolean NDECL(minimal_enlightenment); #ifdef OVLB @@ -1230,6 +1233,504 @@ return (n != -1); } +#ifdef EVENTLOG +/* Find an event in the log. Returns NULL if one can not + * be found. + * Matches by type and by field values. + * field values of -1 are considered wildcards. + */ +struct u_log_entry* +findEvent(log, type, field1, field2, field3) + struct u_log *log; + LogEntryType type; + unsigned long field1; + unsigned long field2; + unsigned long field3; +{ + struct u_log_entry *entry = log->first; + for(entry = log->first; NULL != entry; entry = entry->next) + { + if((entry->type == type) && + ((-1 == field1) || (entry->field1 == field1)) && + ((-1 == field2) || (entry->field2 == field2)) && + ((-1 == field3) || (entry->field3 == field3))) + { + return entry; + } + } + + return NULL; +} + +void +addEventToLogF(log, type, hidden, move, field1, field2, field3) + struct u_log *log; + LogEntryType type; + boolean hidden; + unsigned long move; + unsigned long field1; + unsigned long field2; + unsigned long field3; +{ + struct u_log_entry *new_entry = (struct u_log_entry*) alloc(sizeof(struct u_log_entry)); + new_entry->type = type; + new_entry->hidden = hidden; + new_entry->move = move; + new_entry->next = NULL; + new_entry->field1 = field1; + new_entry->field2 = field2; + new_entry->field3 = field3; + if(log->first == NULL) { + log->first = new_entry; + log->last = new_entry; + } else { + log->last->next = new_entry; + log->last = new_entry; + } + + log->entry_count += 1; +} + +void +addEventToLog(log, type, hidden, field1, field2, field3) + struct u_log *log; + LogEntryType type; + boolean hidden; + unsigned long field1; + unsigned long field2; + unsigned long field3; +{ + addEventToLogF(log, type, hidden, moves, field1, field2, field3); +} + +/* Forgets upto 1/3 of a log. */ +void +forgetLog(log) +struct u_log *log; +{ + // TODO exempt artifacts from this if it turns out + // that amnesia shouldn't affect them + struct u_log_entry *entry = log->first; + for(entry = log->first; NULL != entry; entry = entry->next) + { + if(!rn2(3)) { + entry->hidden = TRUE; + } + } +} +/*boolean +setEventHidden(log, type, hidden, field1, field2, field3) + struct u_log *log; + LogEntryType type; + boolean hidden; + long field1; + long field2; + long field3; +{ + struct u_log_entry *entry = findEvent(log, type, field1, field2, field3); + if(!entry) return FALSE; + + entry->hidden = hidden; + return TRUE; +}*/ +/** + * Add a monster to the kill log. + * This is intended for uniques, so it makes sure that a + * monster's death is never listed twice. + * + * how: How the monster died: + * 0: player killed, who = 0 + * 1: something else besides a trap killed, who = 0 + * 2: trap killed, who = trap type + * + * If monkill() is ever changed to include more info on + * who did the killing, the above should change to include: + * 1: a monster killed the monster, who = killing mnum + * 3: drawbridge killed, who = 0 + */ +void +addMonsterKillToLog(hidden, mnum, how, who) + boolean hidden; + short mnum; + unsigned long how; + unsigned long who; +{ + /* Don't record the same monster being killed twice. */ + if(findEvent(&ulog, evt_KillMonster, mnum, -1, -1)) return; + + addEventToLog(&ulog, evt_KillMonster, hidden, mnum, how, who); +} + +void +addArtifactToLog(hidden, isartifact, anum) + boolean hidden; + boolean isartifact; + short anum; +{ + /* Don't record the same artifact being discovered twice. + * Instead, just update the hidden field from false to true if + * necessary. + */ + struct u_log_entry *entry = findEvent(&ulog, evt_GetArtifact, anum, isartifact, -1); + if(entry) { + if(!hidden) + entry->hidden = FALSE; + } else { + addEventToLog(&ulog, evt_GetArtifact, hidden, anum, isartifact, how_arti_was_created((char) anum)); + } +} + +void +solveSokobanIfNecessary() +{ + /* This assumes that you are in sokoban. */ + if(findEvent(&ulog, evt_SolveSokobanLevel, u.uz.dlevel, -1, -1)) return; + if(ftrap == NULL) { + addEventToLog(&ulog, evt_SolveSokobanLevel, FALSE, u.uz.dlevel, 0, 0); + } +} + +void +loseVirginity(mnum, bad) + short mnum; + boolean bad; +{ + ulog.lostvirginity = 1; + addEventToLog(&ulog, evt_LoseVirginity, FALSE, mnum, bad, 0); +} + +/** Record the passtune if you haven't heard it already. + * The god argument is ignored if yourself is true. */ +void +learnedPasstune(yourself, god) + boolean yourself; + aligntyp god; +{ + if(!findEvent(&ulog, evt_LearnPasstune, -1, -1, -1)) + addEventToLog(&ulog, evt_LearnPasstune, FALSE, yourself, god, 0); +} + +void +recordLevelIfNecessary(dlev) + d_level *dlev; +{ + /* Levels that are obvious. */ + if(Is_valley(dlev) || + Is_sanctum(dlev) || +#ifdef REINCARNATION + Is_rogue_level(dlev) || +#endif + /* Not really obvious, but don't have a good way to detect it. */ + Is_stronghold(dlev) || + Is_bigroom(dlev) || + Is_qstart(dlev) || + Is_qlocate(dlev) || + Is_nemesis(dlev) || + Is_knox(dlev) || + Is_earthlevel(dlev) || + Is_firelevel(dlev) || + Is_airlevel(dlev) || + Is_waterlevel(dlev) || + Is_astralevel(dlev)) + { + addEventToLog(&ulog, evt_ReachDungeonLevel, FALSE, + dlev->dnum, dlev->dlevel, 0); + return; + } + + /* Levels that are not obvious. */ + /* These levels also present the problem of when they need + * to be revealed to the player. When they meet the named + * demon that rules them? What if the demon isn't there? + * What about the wizard levels? + * For minetown, we need to wait until the player actually enters + * the town portion. + * When should the player learn that they are at the bottom + * of the mines? When they can't dig anymore? + * At any rate they will be revealed in the final log. + */ + if(Is_medusa_level(dlev) + || Is_oracle_level(dlev) + || Is_juiblex_level(dlev) + || Is_asmo_level(dlev) + || Is_baal_level(dlev) + || Is_orcus_level(dlev) +#if 0 + /* Deferred because it breaks bones compatibility. */ + || Is_minesend_level(dlev) +#endif + /* These are definitely off until I figure out how to + * detect the first entrance to the inside of the tower levels. + */ + /* + || Is_minetown_level(dlev) + || Is_wiz1_level(dlev) + || Is_wiz2_level(dlev) + || Is_wiz3_level(dlev) + || Is_portal_level(dlev)*/) + { + addEventToLog(&ulog, evt_ReachDungeonLevel, TRUE, dlev->dnum, dlev->dlevel, 0); + return; + } + + /* Dungeons. These are at the end so that if a special level within + * them has already been reached then they won't be added. + */ + if(In_sokoban(dlev) && + !findEvent(&ulog, evt_ReachDungeonLevel, dlev->dnum, -1, -1)) + { + addEventToLog(&ulog, evt_ReachDungeonLevel, FALSE, dlev->dnum, 0, 0); + } else if(In_mines(dlev) && + !findEvent(&ulog, evt_ReachDungeonLevel, dlev->dnum, -1, -1)) + { + addEventToLog(&ulog, evt_ReachDungeonLevel, FALSE, dlev->dnum, dlev->dlevel, 0); + } else if(In_V_tower(dlev) && + !findEvent(&ulog, evt_ReachDungeonLevel, dlev->dnum, -1, -1)) + { + addEventToLog(&ulog, evt_ReachDungeonLevel, TRUE, dlev->dnum, dlev->dlevel, 0); + } +} + +static winid lg_win; + +void +show_log(final) + int final; +{ + static const int kill_mon_len = 3; + static const char *kill_mon[] = + {"killed", + "vanquished", + "dispatched"}; + struct permonst *pmptr = NULL; + + // These should be centralized, damn it. + static const char *crown[] = + {"the Hand of Elbereth", + "the Envoy of Balance", + "the Glory of Arioch"}; + + int offset; + int entry_count = 0; + char buf[BUFSZ]; + /* 50 characters should handle more turns than anyone will + * ever reach. */ + char prefix[50]; + char *bufp; + const char *extptr; + struct d_level dlv; + struct s_level *slv; + + /* Create the log window */ + lg_win = create_nhwindow(NHW_MENU); + putstr(lg_win, 0, "Major Events:"); + putstr(lg_win, 0, ""); + + struct u_log_entry *entry = ulog.last; + Sprintf(buf, "%d", entry->move); + Sprintf(prefix, "T: %%%dd ", strlen(buf)); + + for(entry = ulog.first; entry != NULL; entry = entry->next) { + if((!final) && entry->hidden) continue; + entry_count += 1; + Sprintf(buf, prefix, entry->move); + offset = strlen(buf); + bufp = buf + offset; + switch(entry->type) { + case evt_EnteredDungeon: + Sprintf(bufp, "Entered Dungeon to retrieve amulet for %s", align_gname((aligntyp) entry->field1)); + break; + case evt_FirstKill: + Sprintf(bufp, "First Blood: %s", mons[entry->field1].mname); + break; + case evt_StartQuest: + Sprintf(bufp, "You swore to vanquish %s and retreive the %s", neminame(), artiname(urole.questarti)); + break; + case evt_EjectedQuest: + Sprintf(bufp, "%s banished you from your homeland", ldrname()); + break; + case evt_FinishQuest: + Sprintf(bufp, "Reclaimed the %s for %s", artiname(urole.questarti), ldrname()); + break; + case evt_KillMonster: + switch(entry->field2) { + case 0: + Sprintf(bufp, "You %s %s", kill_mon[rn2(kill_mon_len)], mons[entry->field1].mname); + break; + case 1: + Sprintf(bufp, "%s was killed", mons[entry->field1].mname); + break; + case 2: + Sprintf(bufp, "A %s killed the %s", defsyms[trap_to_defsym(entry->field3)].explanation, mons[entry->field1].mname); + break; + default: + impossible("Unknown who killed type: %d", entry->field2); + continue; + } + + break; + case evt_GetArtifact: + if(entry->field2) { + extptr = artiname(entry->field1); + switch(entry->field3) { + case ARTGEN_NOTEXISTS: + impossible("An nonexistant artifact: %d is in the list, this is bad.", entry->field3); + continue; + break; + case ARTGEN_GOD_LAWFUL: + case ARTGEN_GOD_NEUTRAL: + case ARTGEN_GOD_CHAOTIC: + case ARTGEN_GOD_EVIL: + Sprintf(bufp, "%s bestowed %s upon you", align_gname(artgen2align(entry->field3)), extptr); + break; + case ARTGEN_PLAYER: + Sprintf(bufp, "You created %s", extptr); + break; + case ARTGEN_RANDOM: + Sprintf(bufp, "You found %s", extptr); + break; + case ARTGEN_ALWAYS: + Sprintf(bufp, "You acquired %s", extptr); + break; + case ARTGEN_WISH: + Sprintf(bufp, "The RNG granted your wish for %s", extptr); + break; + default: + impossible("Unknown artifact how field: %d", entry->field3); + continue; + } + } else { + Sprintf(bufp, "You acquired The %s", OBJ_NAME(objects[entry->field1])); + } + break; +#ifdef SEDUCE + case evt_LoseVirginity: + pmptr = &mons[entry->field1]; + boolean bad = entry->field2; + if(pmptr == &mons[PM_SUCCUBUS]) { + if(bad) { + Sprintf(bufp, "You should have used protection with that skanky %s", pmptr->mname); + } else { + Sprintf(bufp, "You ravished a sexy %s", pmptr->mname); + } + } else { + if(bad) { + Sprintf(bufp, "You should have used protection with that dirty %s", pmptr->mname); + } else { + Sprintf(bufp, "You seduced a hunky %s", pmptr->mname); + } + } + break; +#endif + case evt_ReachDungeonLevel: + dlv.dnum = entry->field1; + dlv.dlevel = entry->field2; + slv = Is_special(&dlv); + if(Is_valley(&dlv)) + Sprintf(bufp, "You entered the horrific Valley of the Dead"); + else if(Is_sanctum(&dlv)) + Sprintf(bufp, "You breached Moloch's inner sanctum"); +#ifdef REINCARNATION + else if(Is_rogue_level(&dlv)) + Sprintf(bufp, "You discovered part of a more primitive world"); +#endif + else if(Is_stronghold(&dlv)) + Sprintf(bufp, "You came upon a massive castle deep within the dungeon"); + else if(Is_bigroom(&dlv)) + Sprintf(bufp, "You found a vast cavern"); + else if(Is_qstart(&dlv)) + Sprintf(bufp, "You returned to your homeland"); + else if(Is_qlocate(&dlv)) + Sprintf(bufp, "You advanced further on your quest"); + else if(Is_nemesis(&dlv)) + Sprintf(bufp, "You entered the lair of the vile %s", neminame()); + else if(Is_knox(&dlv)) + Sprintf(bufp, "You penetrated a high security area"); + else if(Is_earthlevel(&dlv)) + Sprintf(bufp, "You reached the elemental plane of earth."); + else if(Is_firelevel(&dlv)) + Sprintf(bufp, "You entered a vast fiery realm"); + else if(Is_airlevel(&dlv)) + Sprintf(bufp, "You touched the top of the sky"); + else if(Is_waterlevel(&dlv)) + Sprintf(bufp, "You plunged into an endless ocean"); + else if(Is_astralevel(&dlv)) + Sprintf(bufp, "You arrived at the domain of the gods themselves"); + else if(Is_medusa_level(&dlv)) + Sprintf(bufp, "You discovered Medusa's isles"); + else if(Is_oracle_level(&dlv)) + Sprintf(bufp, "You found the Oracle's sanctuary"); + else if(Is_juiblex_level(&dlv)) + Sprintf(bufp, "You discovered Juiblex's foul swamp"); + else if(Is_asmo_level(&dlv)) + Sprintf(bufp, "You breached Asmodeous' vile lair"); + else if(Is_baal_level(&dlv)) + Sprintf(bufp, "You entered Baal's vile lair"); + else if(Is_orcus_level(&dlv)) + Sprintf(bufp, "You arrived in Orcus' dark city"); +#if 0 + /* Deferred because it breaks bones compatibility. */ + else if(Is_minesend_level(&dlv)) + Sprintf(bufp, "You found the headquarters of the dwarven mining operation"); +#endif + else if(In_sokoban(&dlv)) + Sprintf(bufp, "You discovered a game within the dungeon"); + else if(In_mines(&dlv)) + Sprintf(bufp, "You entered an vast Dwarven mining operation"); + else if(In_V_tower(&dlv)) + Sprintf(bufp, "You discovered Vlad the Impaler's evil tower"); + else + Sprintf(bufp, "You reached level %d of %s", entry->field2, (dungeons + entry->field1)->dname); + break; + case evt_SolveSokobanLevel: + Sprintf(bufp, "You solved level %d of Sokoban", entry->field1); + break; + case evt_LearnPasstune: + if(entry->field1) + Sprintf(bufp, "You figured out the passtune to enter the Castle"); + else + Sprintf(bufp, "%s taught you the passtune to enter the Castle", align_gname(entry->field2)); + break; +#ifdef ELBERETH + case evt_Crowned: + Sprintf(bufp, "You were crowned %s by %s", crown[entry->field1 - 1], align_gname(entry->field2)); + break; +#endif + case evt_Converted: + Sprintf(bufp, "You turned your back on %s to follow %s", align_gname(entry->field1), align_gname(entry->field2)); + break; + case evt_OpenedSanctum: + Sprintf(bufp, "You perfomed the dark rites of invocation"); + break; + default: + Sprintf(bufp, "Unknown event %d, %d, %d, %d", + entry->type, entry->field1, entry->field2, + entry->field3); + } + putstr(lg_win, 0, buf); + } + + if(0 == entry_count) { + putstr(lg_win, 0, Hallucination ? + "Bummer, nothing's happened." : + "Your existence has been uneventful."); + } + + /* Pop up the window and wait for a key */ + display_nhwindow(lg_win, TRUE); + destroy_nhwindow(lg_win); +} + +STATIC_PTR int +dolog() +{ + show_log(0); + return 0; +} + + +#endif + STATIC_PTR int doattributes() { @@ -1484,6 +1985,9 @@ {"force", "force a lock", doforce, FALSE}, {"invoke", "invoke an object's powers", doinvoke, TRUE}, {"jump", "jump to a location", dojump, FALSE}, +#ifdef EVENTLOG + {"log", "view a log of events", dolog, TRUE}, +#endif {"loot", "loot a box on the floor", doloot, FALSE}, {"monster", "use a monster's special ability", domonability, TRUE}, {"name", "name an item or type of object", ddocall, TRUE}, --- src/end.c.orig 2004-09-05 00:07:19.000000000 -0500 +++ src/end.c 2004-09-05 00:06:11.000000000 -0500 @@ -405,6 +405,17 @@ show_conduct(how >= PANICKED ? 1 : 2); if (c == 'q') done_stopprint++; } + +#ifdef EVENTLOG + ask = should_query_disclose_option('l', &defquery); + if(!done_stopprint) { + c = ask ? yn_function("Do you want to see the events of your quest?", + ynqchars, defquery) : defquery; + if ('y' == c) + show_log(how >= PANICKED ? 1 : 2); + if ('q' == c) done_stopprint++; + } +#endif } /* try to get the player back in a viable state after being killed */ --- src/mon.c.orig 2004-09-03 23:23:11.000000000 -0500 +++ src/mon.c 2004-09-06 00:20:11.092961224 -0500 @@ -1703,9 +1703,19 @@ mondead(mdef); else mondied(mdef); - +#ifdef EVENTLOG + /* make sure it is actually dead before adding to the log */ + if (mdef->mhp <= 0) { + if(mdef->data->geno & G_UNIQ) + addMonsterKillToLog(cansee(mdef->mx, mdef->my), mdef->mnum, 1, 0); + + if(be_sad) + You("have a sad feeling for a moment, then it passes."); + } +#else if (be_sad && mdef->mhp <= 0) You("have a sad feeling for a moment, then it passes."); +#endif } void @@ -1750,7 +1760,9 @@ register struct trap *t; boolean redisp = FALSE; boolean wasinside = u.uswallow && (u.ustuck == mtmp); - +#ifdef EVENTLOG + boolean firsttime = (0 == u.uconduct.killer); +#endif /* KMH, conduct */ u.uconduct.killer++; @@ -1830,7 +1842,12 @@ ) { int typ; - otmp = mkobj_at(RANDOM_CLASS, x, y, TRUE); + otmp = mkobj_at(RANDOM_CLASS, x, y, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif /* Don't create large objects from small monsters */ typ = otmp->otyp; if (mdat->msize < MZ_HUMAN && typ != FOOD_RATION @@ -1901,6 +1918,13 @@ /* malign was already adjusted for u.ualign.type and randomization */ adjalign(mtmp->malign); +#ifdef EVENTLOG + if(firsttime) + addEventToLog(&ulog, evt_FirstKill, FALSE, mtmp->mnum, 0, 0); + + if(mdat->geno & G_UNIQ) + addMonsterKillToLog(FALSE, mtmp->mnum, 0, 0); +#endif } /* changes the monster into a stone monster of the same type */ --- src/quest.c.orig 2004-09-03 23:23:24.000000000 -0500 +++ src/quest.c 2004-09-06 00:20:10.162102736 -0500 @@ -156,6 +156,10 @@ schedule_goto(dest, FALSE, FALSE, portal_flag, (char *)0, (char *)0); if (seal) { /* remove the portal to the quest - sealing it off */ int reexpelled = u.uevent.qexpelled; +#ifdef EVENTLOG + if(!reexpelled) + addEventToLog(&ulog, evt_EjectedQuest, FALSE, 0, 0, 0); +#endif u.uevent.qexpelled = 1; /* Delete the near portal now; the far (main dungeon side) portal will be deleted as part of arrival on that level. @@ -199,6 +203,9 @@ quest artifact */ fully_identify_obj(obj); update_inventory(); +#ifdef EVENTLOG + addEventToLog(&ulog, evt_FinishQuest, FALSE, 0, 0, 0); +#endif } } @@ -265,6 +272,9 @@ qt_pager(QT_ASSIGNQUEST); exercise(A_WIS, TRUE); Qstat(got_quest) = TRUE; +#ifdef EVENTLOG + addEventToLog(&ulog, evt_StartQuest, FALSE, 0, 0, 0); +#endif } } } --- src/questpgr.c.orig 2004-09-04 04:43:12.000000000 -0500 +++ src/questpgr.c 2004-09-04 23:29:14.000000000 -0500 @@ -16,7 +16,6 @@ static void FDECL(Fread, (genericptr_t,int,int,dlb *)); STATIC_DCL struct qtmsg * FDECL(construct_qtlist, (long)); STATIC_DCL const char * NDECL(intermed); -STATIC_DCL const char * NDECL(neminame); STATIC_DCL const char * NDECL(guardname); STATIC_DCL const char * NDECL(homebase); STATIC_DCL struct qtmsg * FDECL(msg_in, (struct qtmsg *,int)); @@ -184,7 +183,7 @@ return((boolean)(otmp->oartifact == urole.questarti)); } -STATIC_OVL const char * +const char * neminame() /* return your role nemesis' name */ { int i = urole.neminum; --- src/mplayer.c.orig 2004-09-04 21:55:00.000000000 -0500 +++ src/mplayer.c 2004-09-04 20:58:08.000000000 -0500 @@ -230,7 +230,11 @@ if (!rn2(3)) otmp->oerodeproof = 1; else if (!rn2(2)) otmp->greased = 1; if (special && rn2(2)) +#ifdef EVENTLOG + otmp = mk_artifact(otmp, A_NONE, ARTGEN_RANDOM); +#else otmp = mk_artifact(otmp, A_NONE); +#endif /* mplayers knew better than to overenchant Magicbane */ if (otmp->oartifact == ART_MAGICBANE) otmp->spe = rnd(4); --- src/spell.c.orig 2004-09-04 16:01:42.000000000 -0500 +++ src/spell.c 2004-09-06 00:20:00.376590360 -0500 @@ -250,6 +250,9 @@ /* successful invocation */ mkinvokearea(); u.uevent.invoked = 1; +#ifdef EVENTLOG + addEventToLog(&ulog, evt_OpenedSanctum, FALSE, 0, 0, 0); +#endif /* in case you haven't killed the Wizard yet, behave as if you just did */ u.uevent.udemigod = 1; /* wizdead() */ --- src/mhitu.c.orig 2004-09-04 15:47:06.000000000 -0500 +++ src/mhitu.c 2004-09-06 00:20:00.386588840 -0500 @@ -2279,7 +2279,8 @@ /* by this point you have discovered mon's identity, blind or not... */ pline("Time stands still while you and %s lie in each other's arms...", noit_mon_nam(mon)); - if (rn2(35) > ACURR(A_CHA) + ACURR(A_INT)) { + boolean bad = rn2(35) > ACURR(A_CHA) + ACURR(A_INT); + if (bad) { /* Don't bother with mspec_used here... it didn't get tired! */ pline("%s seems to have enjoyed it more than you...", noit_Monnam(mon)); @@ -2349,7 +2350,11 @@ break; } } - + +#ifdef EVENTLOG + if(!ulog.lostvirginity) + loseVirginity(mon->mnum, bad); +#endif if (mon->mtame) /* don't charge */ ; else if (rn2(20) < ACURR(A_CHA)) { pline("%s demands that you pay %s, but you refuse...", --- src/mklev.c.orig 2004-09-04 21:55:11.000000000 -0500 +++ src/mklev.c 2004-09-04 21:24:34.000000000 -0500 @@ -502,7 +502,12 @@ if (!level.flags.noteleport) (void) mksobj_at(SCR_TELEPORTATION, xx, yy+dy, TRUE, FALSE); - if (!rn2(3)) (void) mkobj_at(0, xx, yy+dy, TRUE); + if (!rn2(3)) (void) mkobj_at(0, xx, yy+dy, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif } } return; @@ -819,14 +824,24 @@ skip_nonrogue: #endif if(!rn2(3)) { - (void) mkobj_at(0, somex(croom), somey(croom), TRUE); + (void) mkobj_at(0, somex(croom), somey(croom), +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif tryct = 0; while(!rn2(5)) { if(++tryct > 100) { impossible("tryct overflow4"); break; } - (void) mkobj_at(0, somex(croom), somey(croom), TRUE); + (void) mkobj_at(0, somex(croom), somey(croom), +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif } } } --- src/mkobj.c.orig 2004-09-04 21:55:16.000000000 -0500 +++ src/mkobj.c 2004-09-04 21:34:05.000000000 -0500 @@ -82,7 +82,11 @@ mkobj_at(let, x, y, artif) char let; int x, y; +#ifdef EVENTLOG +xchar artif; +#else boolean artif; +#endif { struct obj *otmp; @@ -94,7 +98,12 @@ struct obj * mksobj_at(otyp, x, y, init, artif) int otyp, x, y; -boolean init, artif; +boolean init; +#ifdef EVENTLOG +xchar artif; +#else +boolean artif; +#endif { struct obj *otmp; @@ -106,7 +115,11 @@ struct obj * mkobj(oclass, artif) char oclass; +#ifdef EVENTLOG +xchar artif; +#else boolean artif; +#endif { int tprob, i, prob = rnd(1000); @@ -363,7 +376,11 @@ mksobj(otyp, init, artif) int otyp; boolean init; +#ifdef EVENTLOG +xchar artif; +#else boolean artif; +#endif { int mndx, tryct; struct obj *otmp; @@ -401,7 +418,11 @@ otmp->opoisoned = 1; if (artif && !rn2(20)) +#ifdef EVENTLOG + otmp = mk_artifact(otmp, (aligntyp)A_NONE, artif); +#else otmp = mk_artifact(otmp, (aligntyp)A_NONE); +#endif break; case FOOD_CLASS: otmp->oeaten = 0; @@ -559,8 +580,12 @@ otmp->blessed = rn2(2); otmp->spe = rne(3); } else blessorcurse(otmp, 10); - if (artif && !rn2(40)) + if (artif && !rn2(40)) +#ifdef EVENTLOG + otmp = mk_artifact(otmp, (aligntyp)A_NONE, artif); +#else otmp = mk_artifact(otmp, (aligntyp)A_NONE); +#endif /* simulate lacquered armor for samurai */ if (Role_if(PM_SAMURAI) && otmp->otyp == SPLINT_MAIL && (moves <= 1 || In_quest(&u.uz))) { @@ -627,7 +652,11 @@ /* unique objects may have an associated artifact entry */ if (objects[otyp].oc_unique && !otmp->oartifact) +#ifdef EVENTLOG + otmp = mk_artifact(otmp, (aligntyp)A_NONE, artif); +#else otmp = mk_artifact(otmp, (aligntyp)A_NONE); +#endif otmp->owt = weight(otmp); return(otmp); } --- src/decl.c.orig 2004-09-05 00:46:29.000000000 -0500 +++ src/decl.c 2004-09-06 00:16:07.884934464 -0500 @@ -75,7 +75,11 @@ const char ynNaqchars[] = "yn#aq"; NEARDATA long yn_number = 0L; +#ifdef EVENTLOG +const char disclosure_options[] = "iavgcl"; +#else const char disclosure_options[] = "iavgc"; +#endif #if defined(MICRO) || defined(WIN32) char hackdir[PATHLEN]; /* where rumors, help, record are */ @@ -271,6 +275,10 @@ "lockdir", "configdir", "troubledir" }; #endif +#ifdef EVENTLOG +NEARDATA struct u_log ulog; +#endif + /* dummy routine used to force linkage */ void decl_init() --- src/hack.c.orig 2004-09-03 23:23:32.000000000 -0500 +++ src/hack.c 2004-09-06 00:20:00.409585344 -0500 @@ -159,6 +159,11 @@ place_object(otmp, rx, ry); } if (mtmp && !Blind) newsym(rx, ry); +#ifdef EVENTLOG + if(In_sokoban(&u.uz)) { + solveSokobanIfNecessary(); + } +#endif continue; case HOLE: case TRAPDOOR: @@ -177,6 +182,11 @@ delobj(otmp); bury_objs(rx, ry); if (cansee(rx,ry)) newsym(rx,ry); +#ifdef EVENTLOG + if(In_sokoban(&u.uz)) { + solveSokobanIfNecessary(); + } +#endif continue; case LEVEL_TELEP: case TELEP_TRAP: @@ -1356,6 +1366,10 @@ /* you killed your pet by direct action. * minliquid and mintrap don't know to do this */ +#ifdef EVENTLOG + if(!u.uconduct.killer) + addEventToLog(&ulog, evt_FirstKill, FALSE, mtmp->mnum, 0, 0); +#endif u.uconduct.killer++; break; default: --- src/music.c.orig 2004-09-05 20:03:03.917248416 -0500 +++ src/music.c 2004-09-05 20:00:39.875146144 -0500 @@ -546,6 +546,9 @@ if(isok(x,y)) if(find_drawbridge(&x,&y)) { u.uevent.uheard_tune = 2; /* tune now fully known */ +#ifdef EVENTLOG + learnedPasstune(TRUE, A_NONE); +#endif if(levl[x][y].typ == DRAWBRIDGE_DOWN) close_drawbridge(x,y); else @@ -599,7 +602,15 @@ /* could only get `gears == 5' by playing five correct notes followed by excess; otherwise, tune would have matched above */ - if (gears == 5) u.uevent.uheard_tune = 2; + if (gears == 5) +#ifdef EVENTLOG + { + learnedPasstune(TRUE, A_NONE); +#endif + u.uevent.uheard_tune = 2; +#ifdef EVENTLOG + } +#endif } } } --- src/pray.c.orig 2004-09-04 04:40:40.000000000 -0500 +++ src/pray.c 2004-09-06 00:20:10.535046040 -0500 @@ -672,6 +672,10 @@ already_exists && !in_hand ? "take lives" : "steal souls"); break; } + +#ifdef EVENTLOG + addEventToLog(&ulog, evt_Crowned, FALSE, u.uevent.uhand_of_elbereth, u.ualign.type, 0); +#endif class_gift = STRANGE_OBJECT; /* 3.3.[01] had this in the A_NEUTRAL case below, @@ -709,7 +713,11 @@ ; /* already got bonus above */ } else if (obj && obj->otyp == LONG_SWORD && !obj->oartifact) { if (!Blind) Your("sword shines brightly for a moment."); +#ifdef EVENTLOG + obj = oname2(obj, artiname(ART_EXCALIBUR), ARTGEN_GOD_LAWFUL); +#else obj = oname(obj, artiname(ART_EXCALIBUR)); +#endif if (obj && obj->oartifact == ART_EXCALIBUR) u.ugifts++; } /* acquire Excalibur's skill regardless of weapon or gift */ @@ -725,7 +733,11 @@ obj->dknown = TRUE; } else if (!already_exists) { obj = mksobj(LONG_SWORD, FALSE, FALSE); +#ifdef EVENTLOG + obj = oname2(obj, artiname(ART_VORPAL_BLADE), ARTGEN_GOD_NEUTRAL); +#else obj = oname(obj, artiname(ART_VORPAL_BLADE)); +#endif obj->spe = 1; at_your_feet("A sword"); dropy(obj); @@ -748,7 +760,11 @@ obj->dknown = TRUE; } else if (!already_exists) { obj = mksobj(RUNESWORD, FALSE, FALSE); +#ifdef EVENTLOG + obj = oname2(obj, artiname(ART_STORMBRINGER), ARTGEN_GOD_CHAOTIC); +#else obj = oname(obj, artiname(ART_STORMBRINGER)); +#endif at_your_feet(An(swordbuf)); obj->spe = 1; dropy(obj); @@ -908,6 +924,9 @@ You_hear("a divine music..."); pline("It sounds like: \"%s\".", tune); u.uevent.uheard_tune++; +#ifdef EVENTLOG + learnedPasstune(FALSE, g_align); +#endif break; } } @@ -1354,7 +1373,14 @@ if (uarmh && uarmh->otyp == HELM_OF_OPPOSITE_ALIGNMENT) u.ualignbase[A_CURRENT] = altaralign; else +#ifdef EVENTLOG + { + addEventToLog(&ulog, evt_Converted, FALSE, u.ualign.type, altaralign, 0); +#endif u.ualign.type = u.ualignbase[A_CURRENT] = altaralign; +#ifdef EVENTLOG + } +#endif u.ublessed = 0; flags.botl = 1; @@ -1468,7 +1494,12 @@ /* The chance goes down as the number of artifacts goes up */ if (u.ulevel > 2 && u.uluck >= 0 && !rn2(10 + (2 * u.ugifts * nartifacts))) { +#ifdef EVENTLOG + aligntyp algn = a_align(u.ux,u.uy); + otmp = mk_artifact((struct obj *)0, a_align(u.ux,u.uy), align2artgen(algn)); +#else otmp = mk_artifact((struct obj *)0, a_align(u.ux,u.uy)); +#endif if (otmp) { if (otmp->spe < 0) otmp->spe = 0; if (otmp->cursed) uncurse(otmp); --- src/read.c.orig 2004-09-04 23:40:16.000000000 -0500 +++ src/read.c 2004-09-06 00:20:00.504570904 -0500 @@ -591,7 +591,9 @@ * - felt ball & chain * - traps * - part (6 out of 7) of the map - * +#ifdef EVENTLOG + * - 1/3 of the event log +#endif * Other things are subject to flags: * * howmuch & ALL_MAP = forget whole map @@ -614,6 +616,9 @@ if (!rn2(3)) forget_objects(rn2(25)); if (howmuch & ALL_SPELLS) losespells(); +#ifdef EVENTLOG + forgetLog(&ulog); +#endif /* * Make sure that what was seen is restored correctly. To do this, * we need to go blind for an instant --- turn off the display, --- src/save.c.orig 2004-09-04 01:09:25.000000000 -0500 +++ src/save.c 2004-09-06 00:20:10.726017008 -0500 @@ -27,6 +27,9 @@ #endif STATIC_DCL void FDECL(savelevchn, (int,int)); STATIC_DCL void FDECL(savedamage, (int,int)); +#ifdef EVENTLOG +STATIC_DCL void FDECL(savelog, (int)); +#endif STATIC_DCL void FDECL(saveobjchn, (int,struct obj *,int)); STATIC_DCL void FDECL(savemonchn, (int,struct monst *,int)); STATIC_DCL void FDECL(savetrapchn, (int,struct trap *,int)); @@ -282,6 +285,9 @@ bwrite(fd, (genericptr_t) &uid, sizeof uid); bwrite(fd, (genericptr_t) &flags, sizeof(struct flag)); bwrite(fd, (genericptr_t) &u, sizeof(struct you)); +#ifdef EVENTLOG + savelog(fd); +#endif /* must come before migrating_objs and migrating_mons are freed */ save_timers(fd, mode, RANGE_GLOBAL); @@ -835,6 +841,22 @@ level.damagelist = 0; } +#ifdef EVENTLOG +STATIC_OVL void +savelog(fd) +register int fd; +{ + int i; + struct u_log_entry *entry = ulog.first; + bwrite(fd, (genericptr_t) &ulog.entry_count, sizeof(long)); + for(i = 0; i < ulog.entry_count; i += 1) + { + bwrite(fd, (genericptr_t) entry, sizeof(struct u_log_entry)); + entry = entry->next; + } +} +#endif + STATIC_OVL void saveobjchn(fd, otmp, mode) register int fd, mode; --- src/trap.c.orig 2004-09-03 23:23:42.000000000 -0500 +++ src/trap.c 2004-09-03 23:48:06.000000000 -0500 @@ -1650,6 +1650,8 @@ boolean trapkilled = FALSE; struct permonst *mptr = mtmp->data; struct obj *otmp; + /* EVENTLOG changed scope of seeit. */ + boolean see_it; if (!trap) { mtmp->mtrapped = 0; /* perhaps teleported? */ @@ -1693,7 +1695,7 @@ } } else { register int tt = trap->ttyp; - boolean in_sight, tear_web, see_it, + boolean in_sight, tear_web, inescapable = ((tt == HOLE || tt == PIT) && In_sokoban(&u.uz) && !trap->madeby_u); const char *fallverb; @@ -2153,7 +2155,16 @@ impossible("Some monster encountered a strange trap of type %d.", tt); } } - if(trapkilled) return 2; + if(trapkilled) +#ifdef EVENTLOG + { + if(mptr->geno & G_UNIQ) + addMonsterKillToLog(see_it, mtmp->mnum, 2, trap->ttyp); +#endif + return 2; +#ifdef EVENTLOG + } +#endif return mtmp->mtrapped; } --- src/do_name.c.orig 2004-09-04 21:55:32.000000000 -0500 +++ src/do_name.c 2004-09-05 00:40:01.000000000 -0500 @@ -321,7 +321,11 @@ display_nhwindow(WIN_MESSAGE, FALSE); You("engrave: \"%s\".",buf); } +#ifdef EVENTLOG + obj = oname2(obj, buf, ARTGEN_PLAYER); +#else obj = oname(obj, buf); +#endif } /* @@ -397,11 +401,28 @@ return otmp; } +#ifdef EVENTLOG struct obj * oname(obj, name) struct obj *obj; const char *name; { + return oname2(obj, name, ARTGEN_RANDOM); +} +#endif + +struct obj * +#ifdef EVENTLOG +oname2(obj, name, how) +#else +oname(obj, name) +#endif +struct obj *obj; +const char *name; +#ifdef EVENTLOG +xchar how; +#endif +{ int lth; char buf[PL_PSIZ]; @@ -425,7 +446,12 @@ obj = realloc_obj(obj, obj->oxlth, (genericptr_t)obj->oextra, lth, name); } - if (lth) artifact_exists(obj, name, TRUE); + if (lth) +#ifdef EVENTLOG + artifact_exists(obj, name, how); +#else + artifact_exists(obj, name, TRUE); +#endif if (obj->oartifact) { /* can't dual-wield with artifact as secondary weapon */ if (obj == uswapwep) untwoweapon(); --- src/objnam.c.orig 2004-09-04 21:55:47.000000000 -0500 +++ src/objnam.c 2004-09-04 22:39:21.000000000 -0500 @@ -2682,7 +2682,11 @@ aname = artifact_name(name, &objtyp); if (aname && objtyp == otmp->otyp) name = aname; +#ifdef EVENTLOG + otmp = oname2(otmp, name, ARTGEN_WISH); +#else otmp = oname(otmp, name); +#endif if (otmp->oartifact) { otmp->quan = 1L; u.uconduct.wisharti++; /* KMH, conduct */ @@ -2697,7 +2701,12 @@ && !wizard #endif ) { - artifact_exists(otmp, ONAME(otmp), FALSE); + artifact_exists(otmp, ONAME(otmp), +#ifdef EVENTLOG + ARTGEN_NOTEXISTS); +#else + FALSE); +#endif obfree(otmp, (struct obj *) 0); otmp = &zeroobj; pline("For a moment, you feel %s in your %s, but it disappears!", --- src/fountain.c.orig 2004-09-04 21:55:59.000000000 -0500 +++ src/fountain.c 2004-09-04 23:29:51.000000000 -0500 @@ -376,7 +376,11 @@ /* Be *REAL* nice */ pline("From the murky depths, a hand reaches up to bless the sword."); pline("As the hand retreats, the fountain disappears!"); +#ifdef EVENTLOG + obj = oname2(obj, artiname(ART_EXCALIBUR), ARTGEN_PLAYER); +#else obj = oname(obj, artiname(ART_EXCALIBUR)); +#endif discover_artifact(ART_EXCALIBUR); bless(obj); obj->oeroded = obj->oeroded2 = 0; @@ -556,7 +560,12 @@ break; case 5: if (!(levl[u.ux][u.uy].looted & S_LRING)) { You("find a ring in the sink!"); - (void) mkobj_at(RING_CLASS, u.ux, u.uy, TRUE); + (void) mkobj_at(RING_CLASS, u.ux, u.uy, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif levl[u.ux][u.uy].looted |= S_LRING; exercise(A_WIS, TRUE); newsym(u.ux,u.uy); --- src/restore.c.orig 2004-09-04 01:09:41.000000000 -0500 +++ src/restore.c 2004-09-06 00:20:00.590557832 -0500 @@ -21,6 +21,9 @@ STATIC_DCL void NDECL(find_lev_obj); STATIC_DCL void FDECL(restlevchn, (int)); STATIC_DCL void FDECL(restdamage, (int,BOOLEAN_P)); +#ifdef EVENTLOG +STATIC_DCL void FDECL(restlog, (int)); +#endif STATIC_DCL struct obj *FDECL(restobjchn, (int,BOOLEAN_P,BOOLEAN_P)); STATIC_DCL struct monst *FDECL(restmonchn, (int,BOOLEAN_P)); STATIC_DCL struct fruit *FDECL(loadfruitchn, (int)); @@ -190,6 +193,33 @@ free((genericptr_t)tmp_dam); } +#ifdef EVENTLOG +STATIC_OVL void +restlog(fd) +register int fd; +{ + int i; + mread(fd, (genericptr_t) &ulog.entry_count, sizeof(long)); + struct u_log_entry *entry = NULL; + struct u_log_entry *last = NULL; + ulog.first = entry; + + for(i = 0; i < ulog.entry_count; i += 1) { + entry = (struct u_log_entry*) alloc(sizeof(struct u_log_entry)); + mread(fd, (genericptr_t) entry, sizeof(struct u_log_entry)); + if(NULL == last) { + ulog.first = entry; + } else { + last->next = entry; + last = entry; + } + } + + entry->next = NULL; + ulog.last = entry; +} +#endif + STATIC_OVL struct obj * restobjchn(fd, ghostly, frozen) register int fd; @@ -398,6 +428,9 @@ u.uz.dlevel = 1; return(FALSE); } +#ifdef EVENTLOG + restlog(fd); +#endif /* this stuff comes after potential aborted restore attempts */ restore_timers(fd, RANGE_GLOBAL, FALSE, 0L); --- src/artifact.c.orig 2004-09-04 04:43:24.000000000 -0500 +++ src/artifact.c 2004-09-05 23:42:06.566262072 -0500 @@ -39,12 +39,19 @@ #ifndef OVLB STATIC_DCL int spec_dbon_applies; STATIC_DCL xchar artidisco[NROFARTIFACTS]; +# ifdef EVENTLOG +STATIC_DCL xchar artiexist[1+NROFARTIFACTS+1]; +# endif #else /* OVLB */ /* coordinate effects from spec_dbon() with messages in artifact_hit() */ STATIC_OVL int spec_dbon_applies = 0; /* flags including which artifacts have already been created */ +#ifdef EVENTLOG +STATIC_OVL xchar artiexist[1+NROFARTIFACTS+1]; +#else static boolean artiexist[1+NROFARTIFACTS+1]; +#endif /* and a discovery list for them (no dummy first entry here) */ STATIC_OVL xchar artidisco[NROFARTIFACTS]; @@ -79,7 +86,14 @@ void init_artifacts() { - (void) memset((genericptr_t) artiexist, 0, sizeof artiexist); + (void) memset((genericptr_t) artiexist, +#ifdef EVENTLOG + ARTGEN_NOTEXISTS, +#else + 0, +#endif + sizeof artiexist); + (void) memset((genericptr_t) artidisco, 0, sizeof artidisco); hack_artifacts(); } @@ -118,11 +132,23 @@ into an artifact of matching type, or returned as-is if that's not possible. For the 2nd case, caller should use ``obj = mk_artifact(obj, A_NONE);'' for the 1st, ``obj = mk_artifact((struct obj *)0, some_alignment);''. +#ifdef EVENTLOG + When using the event log, how is how the object was created. It must be + one of the #defines: ARTGEN_* + It probably shouldn't be ARTGEN_NOTEXISTS that will set it to nonexistant. +#endif */ struct obj * +#ifdef EVENTLOG +mk_artifact(otmp, alignment, how) +#else mk_artifact(otmp, alignment) +#endif struct obj *otmp; /* existing object; ignored if alignment specified */ aligntyp alignment; /* target alignment, or A_NONE */ +#ifdef EVENTLOG +xchar how; /* How was this artifact created */ +#endif { const struct artifact *a; int n, m; @@ -136,7 +162,13 @@ if ((!by_align ? a->otyp == o_typ : (a->alignment == alignment || (a->alignment == A_NONE && u.ugifts > 0))) && - (!(a->spfx & SPFX_NOGEN) || unique) && !artiexist[m]) { + (!(a->spfx & SPFX_NOGEN) || unique) +#ifdef EVENTLOG + && (ARTGEN_NOTEXISTS == artiexist[m])) +#else + && !artiexist[m]) +#endif + { if (by_align && a->race != NON_PM && race_hostile(&mons[a->race])) continue; /* skip enemies' equipment */ else if (by_align && Role_if(a->role)) @@ -153,7 +185,26 @@ make_artif: if (by_align) otmp = mksobj((int)a->otyp, TRUE, FALSE); otmp = oname(otmp, a->name); otmp->oartifact = m; +#ifdef EVENTLOG + /* normally we'd fix oname above to use how, but since we're + * fixing it right here, it shouldn't be a problem. */ + artiexist[m] = how; + /* If the artifact is not random or always generated then + * add it to the log at creation time. Visiblity will be + * updated in artidisco. + */ + switch(how) { + case ARTGEN_GOD_LAWFUL: + case ARTGEN_GOD_NEUTRAL: + case ARTGEN_GOD_CHAOTIC: + case ARTGEN_GOD_EVIL: + case ARTGEN_PLAYER: + case ARTGEN_WISH: + addArtifactToLog(TRUE, TRUE, m); + } +#else artiexist[m] = TRUE; +#endif } else { /* nothing appropriate could be found; return the original object */ if (by_align) otmp = 0; /* (there was no original object) */ @@ -190,7 +241,11 @@ return (char *)0; } +#ifdef EVENTLOG +XCHAR_P +#else boolean +#endif exist_artifact(otyp, name) register int otyp; register const char *name; @@ -202,14 +257,22 @@ for (a = artilist+1,arex = artiexist+1; a->otyp; a++,arex++) if ((int) a->otyp == otyp && !strcmp(a->name, name)) return *arex; +#ifdef EVENTLOG + return ARTGEN_NOTEXISTS; +#else return FALSE; +#endif } void artifact_exists(otmp, name, mod) register struct obj *otmp; register const char *name; +#ifdef EVENTLOG +register xchar mod; +#else register boolean mod; +#endif { register const struct artifact *a; @@ -222,6 +285,21 @@ if(otmp->otyp == RIN_INCREASE_DAMAGE) otmp->spe = 0; artiexist[m] = mod; +#ifdef EVENTLOG + /* If the artifact is not random or always generated then + * add it to the log at creation time. Visiblity will be + * updated in artidisco. + */ + switch(mod) { + case ARTGEN_GOD_LAWFUL: + case ARTGEN_GOD_NEUTRAL: + case ARTGEN_GOD_CHAOTIC: + case ARTGEN_GOD_EVIL: + case ARTGEN_PLAYER: + case ARTGEN_WISH: + addArtifactToLog(TRUE, TRUE, m); + } +#endif break; } return; @@ -673,6 +751,12 @@ for (i = 0; i < NROFARTIFACTS; i++) if (artidisco[i] == 0 || artidisco[i] == m) { artidisco[i] = m; +#ifdef EVENTLOG + /* Unhide artifact or add it, if not found. It should + * have already been added by the creation process. + */ + addArtifactToLog(FALSE, TRUE, m); +#endif return; } /* there is one slot per artifact, so we should never reach the @@ -1456,6 +1540,56 @@ return (100L * (long)objects[otmp->otyp].oc_cost); } +#ifdef EVENTLOG +XCHAR_P +align2artgen(algn) +aligntyp algn; +{ + switch(algn) { + case A_LAWFUL: + return ARTGEN_GOD_LAWFUL; + case A_NEUTRAL: + return ARTGEN_GOD_NEUTRAL; + case A_CHAOTIC: + return ARTGEN_GOD_CHAOTIC; + case A_NONE: + return ARTGEN_GOD_EVIL; + default: + impossible("Unknown alignment!"); + return ARTGEN_NOTEXISTS; + } +} + +ALIGNTYP_P +artgen2align(artg) +xchar artg; +{ + switch(artg) { + case ARTGEN_GOD_LAWFUL: + return A_LAWFUL; + case ARTGEN_GOD_NEUTRAL: + return A_NEUTRAL; + case ARTGEN_GOD_CHAOTIC: + return A_CHAOTIC; + case ARTGEN_GOD_EVIL: + return A_NONE; + default: + impossible("Unknown alignment!"); + return A_NONE; + } +} + +XCHAR_P +how_arti_was_created(anum) +char anum; +{ + if((anum < 0) || (anum >= NROFARTIFACTS)) + return ARTGEN_NOTEXISTS; + + return artiexist[anum]; +} +#endif + #endif /* OVLB */ /*artifact.c*/ --- src/mkmaze.c.orig 2004-09-04 21:56:13.000000000 -0500 +++ src/mkmaze.c 2004-09-04 21:25:21.000000000 -0500 @@ -630,7 +630,12 @@ for(x = rn1(8,11); x; x--) { mazexy(&mm); - (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, TRUE); + (void) mkobj_at(rn2(2) ? GEM_CLASS : 0, mm.x, mm.y, +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif } for(x = rn1(10,2); x; x--) { mazexy(&mm); --- src/bones.c.orig 2004-09-04 21:56:21.000000000 -0500 +++ src/bones.c 2004-09-04 20:03:16.000000000 -0500 @@ -73,7 +73,12 @@ otmp->onamelth = 0; *ONAME(otmp) = '\0'; } else if (otmp->oartifact && restore) - artifact_exists(otmp,ONAME(otmp),TRUE); + artifact_exists(otmp,ONAME(otmp), +#ifdef EVENTLOG + ARTGEN_RANDOM); +#else + TRUE); +#endif if (!restore) { /* do not zero out o_ids for ghost levels anymore */