diff --git a/doc/fixes36.2 b/doc/fixes36.2 index 183b012c0..3f6bc996f 100644 --- a/doc/fixes36.2 +++ b/doc/fixes36.2 @@ -205,6 +205,8 @@ successfully paying for shop damage with shop credit would be followed by if a migrating monster was killed off because there was no room on the destination level, it would leave a corpse even if it was a type which should never leave one (demon, golem, blob, &c) +end of game while carrying Schroedinger's Box would reveal cat-or-corpse + for inventory disclosure or put that info into dumplog, but not both Fixes to Post-3.6.1 Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index ff0976e18..89910b0bb 100644 --- a/include/extern.h +++ b/include/extern.h @@ -1,4 +1,4 @@ -/* NetHack 3.6 extern.h $NHDT-Date: 1541719965 2018/11/08 23:32:45 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.647 $ */ +/* NetHack 3.6 extern.h $NHDT-Date: 1542798602 2018/11/21 11:10:02 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.650 $ */ /* Copyright (c) Steve Creps, 1988. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1848,6 +1848,7 @@ E struct obj *FDECL(pick_obj, (struct obj *)); E int NDECL(encumber_msg); E int FDECL(container_at, (int, int, BOOLEAN_P)); E int NDECL(doloot); +E void FDECL(observe_quantum_cat, (struct obj *, BOOLEAN_P, BOOLEAN_P)); E boolean FDECL(container_gone, (int (*)(OBJ_P))); E boolean NDECL(u_handsy); E int FDECL(use_container, (struct obj **, int, BOOLEAN_P)); diff --git a/src/end.c b/src/end.c index 7d89bd03a..a433acc07 100644 --- a/src/end.c +++ b/src/end.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 end.c $NHDT-Date: 1541902951 2018/11/11 02:22:31 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.150 $ */ +/* NetHack 3.6 end.c $NHDT-Date: 1542798619 2018/11/21 11:10:19 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.152 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -49,7 +49,6 @@ STATIC_DCL void FDECL(get_valuables, (struct obj *)); STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *, int)); STATIC_DCL void FDECL(artifact_score, (struct obj *, BOOLEAN_P, winid)); STATIC_DCL void FDECL(really_done, (int)) NORETURN; -STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int)); STATIC_DCL void FDECL(savelife, (int)); STATIC_PTR int FDECL(CFDECLSPEC vanqsort_cmp, (const genericptr, const genericptr)); @@ -954,6 +953,13 @@ int size; /* max value is less than 20 */ return; } +#if 0 +/* + * odds_and_ends() was used for 3.6.0 and 3.6.1. + * Schroedinger's Cat is handled differently starting with 3.6.2. + */ +STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int)); + #define CAT_CHECK 2 STATIC_OVL boolean @@ -962,6 +968,7 @@ struct obj *list; int what; { struct obj *otmp; + for (otmp = list; otmp; otmp = otmp->nobj) { switch (what) { case CAT_CHECK: /* Schroedinger's Cat */ @@ -975,6 +982,7 @@ int what; } return FALSE; } +#endif /* called twice; first to calculate total, then to list relevant items */ STATIC_OVL void @@ -1218,6 +1226,20 @@ int how; obj->known = obj->bknown = obj->dknown = obj->rknown = 1; if (Is_container(obj) || obj->otyp == STATUE) obj->cknown = obj->lknown = 1; + /* we resolve Schroedinger's cat now in case of both + disclosure and dumplog, where the 50:50 chance for + live cat has to be the same both times */ + if (SchroedingersBox(obj)) { + if (!Schroedingers_cat) { + /* tell observe_quantum_cat() not to create a cat; if it + chooses live cat in this situation, it will leave the + SchroedingersBox flag set (for container_contents()) */ + observe_quantum_cat(obj, FALSE, FALSE); + if (SchroedingersBox(obj)) + Schroedingers_cat = TRUE; + } else + obj->spe = 0; /* ordinary box with cat corpse in it */ + } } if (strcmp(flags.end_disclose, "none")) @@ -1228,7 +1250,7 @@ int how; /* if pets will contribute to score, populate mydogs list now (bones creation isn't a factor, but pline() messaging is; used to - be done ever sooner, but we need it to come after dump_everything() + be done even sooner, but we need it to come after dump_everything() so that any accompanying pets are still on the map during dump) */ if (how == ESCAPED || how == ASCENDED) keepdogs(TRUE); @@ -1389,22 +1411,22 @@ int how; viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */ mtmp = mydogs; Strcpy(pbuf, "You"); - if (!Schroedingers_cat) /* check here in case disclosure was off */ - Schroedingers_cat = odds_and_ends(invent, CAT_CHECK); - if (Schroedingers_cat) { - int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]); - - mhp = d(m_lev, 8); - nowrap_add(u.urexp, mhp); - Strcat(eos(pbuf), " and Schroedinger's cat"); - } - if (mtmp) { + if (mtmp || Schroedingers_cat) { while (mtmp) { Sprintf(eos(pbuf), " and %s", mon_nam(mtmp)); if (mtmp->mtame) nowrap_add(u.urexp, mtmp->mhp); mtmp = mtmp->nmon; } + /* [it might be more robust to create a housecat and add it to + mydogs; it doesn't have to be placed on the map for that] */ + if (Schroedingers_cat) { + int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]); + + mhp = d(m_lev, 8); + nowrap_add(u.urexp, mhp); + Strcat(eos(pbuf), " and Schroedinger's cat"); + } dump_forward_putstr(endwin, 0, pbuf, done_stopprint); pbuf[0] = '\0'; } else { @@ -1513,23 +1535,18 @@ boolean identified, all_containers, reportempty; { register struct obj *box, *obj; char buf[BUFSZ]; - boolean cat, deadcat; + boolean cat, + dumping = +#ifdef DUMPLOG + iflags.in_dumplog ? TRUE : +#endif + FALSE; for (box = list; box; box = box->nobj) { if (Is_container(box) || box->otyp == STATUE) { box->cknown = 1; /* we're looking at the contents now */ if (identified) box->lknown = 1; - cat = deadcat = FALSE; - if (SchroedingersBox(box) && !Schroedingers_cat) { - /* Schroedinger's Cat? */ - cat = odds_and_ends(box, CAT_CHECK); - if (cat) - Schroedingers_cat = TRUE; - else - deadcat = TRUE; - box->spe = 0; - } if (box->otyp == BAG_OF_TRICKS) { continue; /* wrong type of container */ } else if (box->cobj) { @@ -1537,38 +1554,49 @@ boolean identified, all_containers, reportempty; Loot *sortedcobj, *srtc; unsigned sortflags; + /* at this stage, the SchroedingerBox() flag is only set + if the cat inside the box is alive; the box actually + contains a cat corpse that we'll pretend is not there; + for dead cat, the flag will be clear and there'll be + a cat corpse inside the box; either way, inventory + reports the box as containing "1 item" */ + cat = SchroedingersBox(box); + Sprintf(buf, "Contents of %s:", the(xname(box))); putstr(tmpwin, 0, buf); - putstr(tmpwin, 0, ""); - sortflags = (((flags.sortloot == 'l' || flags.sortloot == 'f') - ? SORTLOOT_LOOT : 0) - | (flags.sortpack ? SORTLOOT_PACK : 0)); - sortedcobj = sortloot(&box->cobj, sortflags, FALSE, - (boolean FDECL((*), (OBJ_P))) 0); - for (srtc = sortedcobj; ((obj = srtc->obj) != 0); ++srtc) { - if (identified) { - discover_object(obj->otyp, TRUE, FALSE); - obj->known = obj->bknown = obj->dknown - = obj->rknown = 1; - if (Is_container(obj) || obj->otyp == STATUE) - obj->cknown = obj->lknown = 1; + if (!dumping) + putstr(tmpwin, 0, ""); + buf[0] = buf[1] = ' '; /* two leading spaces */ + if (box->cobj && !cat) { + sortflags = (((flags.sortloot == 'l' + || flags.sortloot == 'f') + ? SORTLOOT_LOOT : 0) + | (flags.sortpack ? SORTLOOT_PACK : 0)); + sortedcobj = sortloot(&box->cobj, sortflags, FALSE, + (boolean FDECL((*), (OBJ_P))) 0); + for (srtc = sortedcobj; ((obj = srtc->obj) != 0); ++srtc) { + if (identified) { + discover_object(obj->otyp, TRUE, FALSE); + obj->known = obj->bknown = obj->dknown + = obj->rknown = 1; + if (Is_container(obj) || obj->otyp == STATUE) + obj->cknown = obj->lknown = 1; + } + Strcpy(&buf[2], doname(obj)); + putstr(tmpwin, 0, buf); } - putstr(tmpwin, 0, doname(obj)); + unsortloot(&sortedcobj); + } else if (cat) { + Strcpy(&buf[2], "Schroedinger's cat!"); + putstr(tmpwin, 0, buf); } - unsortloot(&sortedcobj); - if (cat) - putstr(tmpwin, 0, "Schroedinger's cat"); - else if (deadcat) - putstr(tmpwin, 0, "Schroedinger's dead cat"); + if (dumping) + putstr(0, 0, ""); display_nhwindow(tmpwin, TRUE); destroy_nhwindow(tmpwin); if (all_containers) container_contents(box->cobj, identified, TRUE, reportempty); - } else if (cat || deadcat) { - pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"), - deadcat ? "dead " : ""); - display_nhwindow(WIN_MESSAGE, FALSE); } else if (reportempty) { pline("%s is empty.", upstart(thesimpleoname(box))); display_nhwindow(WIN_MESSAGE, FALSE); diff --git a/src/makemon.c b/src/makemon.c index 30630fb16..33cc7709c 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 makemon.c $NHDT-Date: 1539804904 2018/10/17 19:35:04 $ $NHDT-Branch: keni-makedefsm $:$NHDT-Revision: 1.127 $ */ +/* NetHack 3.6 makemon.c $NHDT-Date: 1542798623 2018/11/21 11:10:23 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.128 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -280,7 +280,8 @@ register struct monst *mtmp; if (rn2(2)) (void) mongets(mtmp, rn2(3) ? DAGGER : KNIFE); if (rn2(5)) - (void) mongets(mtmp, rn2(3) ? LEATHER_JACKET : LEATHER_CLOAK); + (void) mongets(mtmp, rn2(3) ? LEATHER_JACKET + : LEATHER_CLOAK); if (rn2(3)) (void) mongets(mtmp, rn2(3) ? LOW_BOOTS : HIGH_BOOTS); if (rn2(3)) @@ -304,7 +305,8 @@ register struct monst *mtmp; case PM_HUNTER: (void) mongets(mtmp, rn2(3) ? SHORT_SWORD : DAGGER); if (rn2(2)) - (void) mongets(mtmp, rn2(2) ? LEATHER_JACKET : LEATHER_ARMOR); + (void) mongets(mtmp, rn2(2) ? LEATHER_JACKET + : LEATHER_ARMOR); (void) mongets(mtmp, BOW); m_initthrow(mtmp, ARROW, 12); break; @@ -744,9 +746,21 @@ register struct monst *mtmp; break; case S_QUANTMECH: if (!rn2(20)) { + struct obj *catcorpse; + otmp = mksobj(LARGE_BOX, FALSE, FALSE); - otmp->spe = 1; /* flag for special box */ - otmp->owt = weight(otmp); + /* we used to just set the flag, which resulted in weight() + treating the box as being heavier by the weight of a cat; + now we include a cat corpse that won't rot; when opening or + disclosing the box's contents, the corpse might be revived, + otherwise it's given a rot timer; weight is now ordinary */ + if ((catcorpse = mksobj(CORPSE, TRUE, FALSE)) != 0) { + otmp->spe = 1; /* flag for special SchroedingersBox */ + set_corpsenm(catcorpse, PM_HOUSECAT); + (void) stop_timer(ROT_CORPSE, obj_to_any(catcorpse)); + add_to_container(otmp, catcorpse); + otmp->owt = weight(otmp); + } (void) mpickobj(mtmp, otmp); } break; diff --git a/src/mkobj.c b/src/mkobj.c index 8442f680c..9a0ec48f2 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 mkobj.c $NHDT-Date: 1518053380 2018/02/08 01:29:40 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.130 $ */ +/* NetHack 3.6 mkobj.c $NHDT-Date: 1542798624 2018/11/21 11:10:24 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.136 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Derek S. Ray, 2015. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1388,8 +1388,6 @@ register struct obj *obj; when we assume this is a brand new glob so use objects[].oc_weight */ if (obj->globby && obj->owt > 0) wt = obj->owt; - if (SchroedingersBox(obj)) - wt += mons[PM_HOUSECAT].cwt; if (Is_container(obj) || obj->otyp == STATUE) { struct obj *contents; register int cwt = 0; diff --git a/src/pickup.c b/src/pickup.c index 319f1f031..d68def38d 100644 --- a/src/pickup.c +++ b/src/pickup.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 pickup.c $NHDT-Date: 1541312259 2018/11/04 06:17:39 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.201 $ */ +/* NetHack 3.6 pickup.c $NHDT-Date: 1542798625 2018/11/21 11:10:25 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.202 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2012. */ /* NetHack may be freely redistributed. See license for details. */ @@ -34,7 +34,6 @@ STATIC_PTR int FDECL(in_container, (struct obj *)); STATIC_PTR int FDECL(out_container, (struct obj *)); STATIC_DCL void FDECL(removed_from_icebox, (struct obj *)); STATIC_DCL long FDECL(mbag_item_gone, (int, struct obj *)); -STATIC_DCL void FDECL(observe_quantum_cat, (struct obj *)); STATIC_DCL void FDECL(explain_container_prompt, (BOOLEAN_P)); STATIC_DCL int FDECL(traditional_loot, (BOOLEAN_P)); STATIC_DCL int FDECL(menu_loot, (int, BOOLEAN_P)); @@ -2321,45 +2320,66 @@ struct obj *item; return loss; } -STATIC_OVL void -observe_quantum_cat(box) +/* used for #loot/apply, #tip, and final disclosure */ +void +observe_quantum_cat(box, makecat, givemsg) struct obj *box; +boolean makecat, givemsg; { static NEARDATA const char sc[] = "Schroedinger's Cat"; struct obj *deadcat; - struct monst *livecat; + struct monst *livecat = 0; xchar ox, oy; + boolean itsalive = !rn2(2); - box->spe = 0; /* box->owt will be updated below */ if (get_obj_location(box, &ox, &oy, 0)) box->ox = ox, box->oy = oy; /* in case it's being carried */ /* this isn't really right, since any form of observation (telepathic or monster/object/food detection) ought to - force the determination of alive vs dead state; but basing - it just on opening the box is much simpler to cope with */ - livecat = rn2(2) - ? makemon(&mons[PM_HOUSECAT], box->ox, box->oy, NO_MINVENT) - : 0; - if (livecat) { - livecat->mpeaceful = 1; - set_malign(livecat); - if (!canspotmon(livecat)) - You("think %s brushed your %s.", something, body_part(FOOT)); - else - pline("%s inside the box is still alive!", Monnam(livecat)); - (void) christen_monst(livecat, sc); - } else { - deadcat = mk_named_object(CORPSE, &mons[PM_HOUSECAT], - box->ox, box->oy, sc); - if (deadcat) { - obj_extract_self(deadcat); - (void) add_to_container(box, deadcat); + force the determination of alive vs dead state; but basing it + just on opening or disclosing the box is much simpler to cope with */ + + /* SchroedingersBox already has a cat corpse in it */ + deadcat = box->cobj; + if (itsalive) { + if (makecat) + livecat = makemon(&mons[PM_HOUSECAT], box->ox, box->oy, + NO_MINVENT | MM_ADJACENTOK); + if (livecat) { + livecat->mpeaceful = 1; + set_malign(livecat); + if (givemsg) { + if (!canspotmon(livecat)) + You("think %s brushed your %s.", something, + body_part(FOOT)); + else + pline("%s inside the box is still alive!", + Monnam(livecat)); + } + (void) christen_monst(livecat, sc); + if (deadcat) { + obj_extract_self(deadcat); + obfree(deadcat, (struct obj *) 0), deadcat = 0; + } + box->owt = weight(box); + box->spe = 0; } - pline_The("%s inside the box is dead!", - Hallucination ? rndmonnam((char *) 0) : "housecat"); + } else { + box->spe = 0; /* now an ordinary box (with a cat corpse inside) */ + if (deadcat) { + /* set_corpsenm() will start the rot timer that was removed + when makemon() created SchroedingersBox; start it from + now rather than from when this special corpse got created */ + deadcat->age = monstermoves; + set_corpsenm(deadcat, PM_HOUSECAT); + deadcat = oname(deadcat, sc); + } + if (givemsg) + pline_The("%s inside the box is dead!", + Hallucination ? rndmonnam((char *) 0) : "housecat"); } - box->owt = weight(box); + nhUse(deadcat); return; } @@ -2472,7 +2492,7 @@ boolean more_containers; /* True iff #loot multiple and this isn't last one */ /* check for Schroedinger's Cat */ quantum_cat = SchroedingersBox(current_container); if (quantum_cat) { - observe_quantum_cat(current_container); + observe_quantum_cat(current_container, TRUE, TRUE); used = 1; } @@ -3096,7 +3116,7 @@ struct obj *box; /* or bag */ } else if (SchroedingersBox(box)) { char yourbuf[BUFSZ]; - observe_quantum_cat(box); + observe_quantum_cat(box, TRUE, TRUE); if (!Has_contents(box)) /* evidently a live cat came out */ /* container type of "large box" is inferred */ pline("%sbox is now empty.", Shk_Your(yourbuf, box)); diff --git a/src/restore.c b/src/restore.c index f7a1e6a26..f26379967 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 restore.c $NHDT-Date: 1451082255 2015/12/25 22:24:15 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.103 $ */ +/* NetHack 3.6 restore.c $NHDT-Date: 1542798626 2018/11/21 11:10:26 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.109 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Michael Allison, 2009. */ /* NetHack may be freely redistributed. See license for details. */ @@ -298,10 +298,32 @@ boolean ghostly, frozen; /* get contents of a container or statue */ if (Has_contents(otmp)) { struct obj *otmp3; + otmp->cobj = restobjchn(fd, ghostly, Is_IceBox(otmp)); /* restore container back pointers */ for (otmp3 = otmp->cobj; otmp3; otmp3 = otmp3->nobj) otmp3->ocontainer = otmp; + } else if (SchroedingersBox(otmp)) { + struct obj *catcorpse; + + /* + * TODO: Remove this after 3.6.x save compatibility is dropped. + * + * For 3.6.2, SchroedingersBox() always has a cat corpse in it. + * For 3.6.[01], it was empty and its weight was falsified + * to have the value it would have had if there was one inside. + * Put a non-rotting cat corpse in this box to convert to 3.6.2. + * + * [Note: after this fix up, future save/restore of this object + * will take the Has_contents() code path above.] + */ + if ((catcorpse = mksobj(CORPSE, TRUE, FALSE)) != 0) { + otmp->spe = 1; /* flag for special SchroedingersBox */ + set_corpsenm(catcorpse, PM_HOUSECAT); + (void) stop_timer(ROT_CORPSE, obj_to_any(catcorpse)); + add_to_container(otmp, catcorpse); + otmp->owt = weight(otmp); + } } if (otmp->bypass) otmp->bypass = 0; diff --git a/src/zap.c b/src/zap.c index 5781d8789..d53468fe3 100644 --- a/src/zap.c +++ b/src/zap.c @@ -1,4 +1,4 @@ -/* NetHack 3.6 zap.c $NHDT-Date: 1537234123 2018/09/18 01:28:43 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.287 $ */ +/* NetHack 3.6 zap.c $NHDT-Date: 1542798627 2018/11/21 11:10:27 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.289 $ */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /*-Copyright (c) Robert Patrick Rankin, 2013. */ /* NetHack may be freely redistributed. See license for details. */ @@ -1916,14 +1916,17 @@ struct obj *obj, *otmp; if (Is_container(obj) || obj->otyp == STATUE) { obj->cknown = obj->lknown = 1; if (!obj->cobj) { - boolean catbox = SchroedingersBox(obj); - + pline("%s empty.", Tobjnam(obj, "are")); + } else if (SchroedingersBox(obj)) { /* we don't want to force alive vs dead determination for Schroedinger's Cat here, so just make probing be inconclusive for it */ - if (catbox) - obj->cknown = 0; - pline("%s empty.", Tobjnam(obj, catbox ? "seem" : "are")); + You("aren't sure whether %s has %s or its corpse inside.", + the(xname(obj)), + /* unfortunately, we can't tell whether rndmonnam() + picks a form which can't leave a corpse */ + an(Hallucination ? rndmonnam((char *) 0) : "cat")); + obj->cknown = 0; } else { struct obj *o; /* view contents (not recursively) */