From d785f7a649af8240fa4d7b6b5b9e74d63270a09e Mon Sep 17 00:00:00 2001 From: nhmall Date: Sun, 2 Feb 2025 09:00:05 -0500 Subject: [PATCH] add two unused fields for hardfought save compatability There are two hardfought code additions that render save and bones files incompatible with the upstream NetHack-3.7, and that makes testing with hardfought save and bones files more challenging than it needs to be, when investigating and troubleshooting bug reports. Add some unused fields to advance towards achieving save file parity with hardfought, which is a significant source of play-testing for NetHack-3.7. 1) the elbereth field addition to u_conduct This adds an unused placeholder field named 'hf_reserved1', at the appropriate place in u_conduct to achieve struct field parity with the one in use on hardfought. 2) hardfought adds a field to struct monst: char former_rank[25]; /* for bones' ghost rank in their former life */ Instead of adding that to every monst, this adds a new mextra struct named 'former', which currently contains the equivalent 25-character field called 'rank' which can hold the content that was in the former_rank[25] field. That way, the field will only be added when it is needed. A pull request https://github.com/k21971/NetHack37/pull/2 has been done on hardfought to do it the same way (untested there as of yet). Even though NetHack-3.7 does not utilize that information presently, this will be a further step toward allowing hardfought-generated save and bones files to be used for troubleshooting, without modification, on a similar architecture running stock NetHack-3.7 code. That savefile parity won't be achieved until the after the hardfought pull-request mentioned above (or equivalent) is merged. As this change will not be compatible with existing save and bones files, it will be accompanied with an EDITLEVEL increment. --- include/extern.h | 3 +++ include/mextra.h | 7 +++++++ include/mondata.h | 8 ++++++++ include/you.h | 1 + src/bones.c | 7 +++++++ src/botl.c | 3 +-- src/makemon.c | 22 ++++++++++++++++++++++ src/mon.c | 26 ++++++++++++++++++++++++-- src/restore.c | 9 +++++++++ src/save.c | 7 +++++++ src/wizcmds.c | 2 ++ 11 files changed, 91 insertions(+), 4 deletions(-) diff --git a/include/extern.h b/include/extern.h index d14e6d30c..265b69259 100644 --- a/include/extern.h +++ b/include/extern.h @@ -257,6 +257,7 @@ extern char *do_statusline2(void); extern void bot(void); extern void timebot(void); extern int xlev_to_rank(int); +extern const char *rank(void); extern int rank_to_xlev(int); extern const char *rank_of(int, short, boolean); extern int title_to_mon(const char *, int *, int *); @@ -1442,6 +1443,8 @@ extern void mkmonmoney(struct monst *, long) NONNULLARG1; extern int bagotricks(struct obj *, boolean, int *); extern boolean propagate(int, boolean, boolean); extern void summon_furies(int); +extern void newformer(struct monst *) NONNULLARG1; +extern void free_former(struct monst *) NONNULLARG1; /* ### mcastu.c ### */ diff --git a/include/mextra.h b/include/mextra.h index f47010324..0089991af 100644 --- a/include/mextra.h +++ b/include/mextra.h @@ -182,6 +182,10 @@ struct edog { Bitfield(killed_by_u, 1); /* you attempted to kill him */ }; +struct former_incarnation { + char rank[25]; /* for bones' ghost rank in their former life */ +}; + /*** ** mextra.h -- collection of all monster extensions */ @@ -192,6 +196,7 @@ struct mextra { struct eshk *eshk; struct emin *emin; struct edog *edog; + struct former_incarnation *former; int mcorpsenm; /* obj->corpsenm for mimic posing as statue or corpse, * obj->spe (fruit index) for one posing as a slime mold, * or an alignment mask for one posing as an altar */ @@ -203,6 +208,7 @@ struct mextra { #define ESHK(mon) ((mon)->mextra->eshk) #define EMIN(mon) ((mon)->mextra->emin) #define EDOG(mon) ((mon)->mextra->edog) +#define FORMER(mon) ((mon)->mextra->former) #define MCORPSENM(mon) ((mon)->mextra->mcorpsenm) #define has_mgivenname(mon) ((mon)->mextra && MGIVENNAME(mon)) @@ -211,6 +217,7 @@ struct mextra { #define has_eshk(mon) ((mon)->mextra && ESHK(mon)) #define has_emin(mon) ((mon)->mextra && EMIN(mon)) #define has_edog(mon) ((mon)->mextra && EDOG(mon)) +#define has_former(mon) ((mon)->mextra && FORMER(mon)) #define has_mcorpsenm(mon) ((mon)->mextra && MCORPSENM(mon) != NON_PM) #endif /* MEXTRA_H */ diff --git a/include/mondata.h b/include/mondata.h index 80a177f76..5296361fa 100644 --- a/include/mondata.h +++ b/include/mondata.h @@ -260,6 +260,14 @@ || objects[(obj)->otyp].oc_material == VEGGY \ || ((obj)->otyp == CORPSE && (obj)->corpsenm == PM_LICHEN)))) +/* is_bones_monster() is currently only used by hardfought livelog, + * but is included as part of savefile field compatibility adjustments. + */ +#define is_bones_monster(ptr) \ + ((ptr) == &mons[PM_GHOST] || (ptr) == &mons[PM_GHOUL] \ + || (ptr) == &mons[PM_VAMPIRE] || (ptr) == &mons[PM_WRAITH] \ + || (ptr) == &mons[PM_GREEN_SLIME] || (ptr)->mlet == S_MUMMY) + #ifdef PMNAME_MACROS #define pmname(ptr,g) ((((g) == MALE || (g) == FEMALE) && (ptr)->pmnames[g]) \ ? (ptr)->pmnames[g] : (ptr)->pmnames[NEUTRAL]) diff --git a/include/you.h b/include/you.h index cd734ca42..2683eec8f 100644 --- a/include/you.h +++ b/include/you.h @@ -153,6 +153,7 @@ struct u_conduct { /* number of times... */ long polyselfs; /* transformed yourself */ long wishes; /* used a wish */ long wisharti; /* wished for an artifact */ + long hf_reserved1; /* hf uses for elbereth;for hf savefile compatiblity */ long sokocheat; /* violated special 'rules' in Sokoban */ long pets; /* obtained a pet */ /* genocides already listed at end of game */ diff --git a/src/bones.c b/src/bones.c index 3ecd93125..34adac268 100644 --- a/src/bones.c +++ b/src/bones.c @@ -506,6 +506,13 @@ savebones(int how, time_t when, struct obj *corpse) mtmp->mhp = mtmp->mhpmax = u.uhpmax; mtmp->female = flags.female; mtmp->msleeping = 1; + if (!has_former(mtmp)) + newformer(mtmp); + if (has_former(mtmp)) { + Snprintf(FORMER(mtmp)->rank, + sizeof FORMER(mtmp)->rank, + "%s", rank()); + } } for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) { set_ghostly_objlist(mtmp->minvent); diff --git a/src/botl.c b/src/botl.c index 51f15f19d..406796782 100644 --- a/src/botl.c +++ b/src/botl.c @@ -16,7 +16,6 @@ const char *const enc_stat[] = { "Strained", "Overtaxed", "Overloaded" }; -staticfn const char *rank(void); staticfn void bot_via_windowport(void); staticfn void stat_update_time(void); staticfn char *get_strength_str(void); @@ -361,7 +360,7 @@ rank_of(int lev, short monnum, boolean female) return "Player"; } -staticfn const char * +const char * rank(void) { return rank_of(u.ulevel, Role_switch, flags.female); diff --git a/src/makemon.c b/src/makemon.c index a7e52bf47..62de076b6 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1070,6 +1070,28 @@ newmextra(void) return mextra; } +void +newformer(struct monst *mtmp) +{ + if (!mtmp->mextra) + mtmp->mextra = newmextra(); + if (!FORMER(mtmp)) { + FORMER(mtmp) = (struct former_incarnation *) alloc( + sizeof(struct former_incarnation *)); + (void) memset((genericptr_t) FORMER(mtmp), 0, + sizeof(struct former_incarnation *)); + } +} + +void +free_former(struct monst *mtmp) +{ + if (mtmp->mextra && FORMER(mtmp)) { + free((genericptr_t) FORMER(mtmp)); + FORMER(mtmp) = (struct former_incarnation *) 0; + } +} + staticfn boolean makemon_rnd_goodpos( struct monst *mon, diff --git a/src/mon.c b/src/mon.c index a59b1a13a..819cb7bdc 100644 --- a/src/mon.c +++ b/src/mon.c @@ -2567,6 +2567,12 @@ copy_mextra(struct monst *mtmp2, struct monst *mtmp1) assert(has_edog(mtmp2)); *EDOG(mtmp2) = *EDOG(mtmp1); } + if (FORMER(mtmp1)) { + if (!FORMER(mtmp2)) + newformer(mtmp2); + assert(has_former(mtmp2)); + *FORMER(mtmp2) = *FORMER(mtmp1); + } if (has_mcorpsenm(mtmp1)) MCORPSENM(mtmp2) = MCORPSENM(mtmp1); } @@ -2589,6 +2595,8 @@ dealloc_mextra(struct monst *m) free((genericptr_t) x->emin), x->emin = 0; if (x->edog) free((genericptr_t) x->edog), x->edog = 0; + if (x->former) + free((genericptr_t) x->former), x->former = 0; x->mcorpsenm = NON_PM; /* no allocation to release */ free((genericptr_t) x); @@ -2991,8 +2999,6 @@ logdeadmon(struct monst *mtmp, int mndx) } } -#undef livelog_mon_nam - /* monster 'mtmp' has died; maybe life-save, otherwise unshapeshift and update vanquished stats and update map */ void @@ -3635,10 +3641,26 @@ xkilled( /* malign was already adjusted for u.ualign.type and randomization */ adjalign(mtmp->malign); + +#if 0 /* HARDFOUGHT-only at present */ +#ifdef LIVELOG + if (is_bones_monster(mtmp->data) + && has_former(mtmp) && *FORMER(mtmp)->rank + && strlen(FORMER(mtmp)->rank) > 0) { + if (mtmp->data == &mons[PM_GHOST]) + livelog_printf(LL_UMONST, "destroyed %s, the former %s", + livelog_mon_nam(mtmp), FORMER(mtmp)->rank); + else + livelog_printf(LL_UMONST, "destroyed %s, and former %s", + livelog_mon_nam(mtmp), FORMER(mtmp)->rank); + } +#endif /* LIVELOG */ +#endif return; } #undef LEVEL_SPECIFIC_NOCORPSE +#undef livelog_mon_nam /* changes the monster into a stone monster of the same type this should only be called when poly_when_stoned() is true */ diff --git a/src/restore.c b/src/restore.c index 67be7cebb..5ab92311e 100644 --- a/src/restore.c +++ b/src/restore.c @@ -370,6 +370,15 @@ restmon(NHFILE *nhfp, struct monst *mtmp) EDOG(mtmp)->apport = 1; } } + /* former - info about former self, primarily for bones files */ + if (nhfp->structlevel) + Mread(nhfp->fd, &buflen, sizeof buflen); + if (buflen > 0) { + newformer(mtmp); + if (nhfp->structlevel) + Mread(nhfp->fd, FORMER(mtmp), + sizeof (struct former_incarnation)); + } /* mcorpsenm - obj->corpsenm for mimic posing as corpse or statue (inline int rather than pointer to something) */ if (nhfp->structlevel) diff --git a/src/save.c b/src/save.c index a178fbdcd..e383b52b8 100644 --- a/src/save.c +++ b/src/save.c @@ -943,6 +943,13 @@ savemon(NHFILE *nhfp, struct monst *mtmp) if (nhfp->structlevel) bwrite(nhfp->fd, (genericptr_t) EDOG(mtmp), buflen); } + buflen = FORMER(mtmp) ? (int) sizeof (struct former_incarnation) : 0; + if (nhfp->structlevel) + bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof (int)); + if (buflen > 0) { + if (nhfp->structlevel) + bwrite(nhfp->fd, (genericptr_t) FORMER(mtmp), buflen); + } /* mcorpsenm is inline int rather than pointer to something, so doesn't need to be preceded by a length field */ if (nhfp->structlevel) diff --git a/src/wizcmds.c b/src/wizcmds.c index 17dc08c5b..a37c469aa 100644 --- a/src/wizcmds.c +++ b/src/wizcmds.c @@ -1246,6 +1246,8 @@ size_monst(struct monst *mtmp, boolean incl_wsegs) sz += (int) sizeof (struct emin); if (EDOG(mtmp)) sz += (int) sizeof (struct edog); + if (FORMER(mtmp)) + sz += (int) sizeof (struct former_incarnation); /* mextra->mcorpsenm doesn't point to more memory */ } return sz;