diff --git a/doc/fixes37.0 b/doc/fixes37.0 index fb02de618..315847881 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -541,6 +541,7 @@ change valkyrie and warrior (valk quest) monsters from chaotic to lawful change attendant (healer quest) monster from lawful to neutral quit is not longer bound to M-q change default value of autopickup to off and color to on +resurrected corpse of mon could end up with different gender from original mon Fixes to 3.7.0-x Problems that Were Exposed Via git Repository @@ -710,6 +711,8 @@ stair revamp unintentionally resulted in cursed potion of levitation no longer causing hero to hit head on ceiling enlightenment/disclosure when wielding two weapons with two-weap skill higher than secondary weapon's skill had "with" duplicated in the feedback +corpse of monster with gender specific names (king vs queen and so forth) + was always described by the neutral name (ruler and such) curses: 'msg_window' option wasn't functional for curses unless the binary also included tty support diff --git a/include/hack.h b/include/hack.h index 42b1282e2..17aaa1243 100644 --- a/include/hack.h +++ b/include/hack.h @@ -285,10 +285,19 @@ typedef struct sortloot_item Loot; * to make an extra call to goodpos()] */ #define GP_ALLOW_U 0x080000L /* don't reject hero's location */ -/* flags for make_corpse() and mkcorpstat() */ -#define CORPSTAT_NONE 0x00 -#define CORPSTAT_INIT 0x01 /* pass init flag to mkcorpstat */ -#define CORPSTAT_BURIED 0x02 /* bury the corpse or statue */ +/* flags for make_corpse() and mkcorpstat(); 0..7 are recorded in obj->spe */ +#define CORPSTAT_NONE 0x00 +#define CORPSTAT_GENDER 0x03 /* 0x01 | 0x02 */ +#define CORPSTAT_HISTORIC 0x04 /* historic statue; not used for corpse */ +#define CORPSTAT_SPE_VAL 0x07 /* 0x03 | 0x04 */ +#define CORPSTAT_INIT 0x08 /* pass init flag to mkcorpstat */ +#define CORPSTAT_BURIED 0x10 /* bury the corpse or statue */ +/* note: gender flags have different values from those used for monsters + so that 0 can be unspecified/random instead of male */ +#define CORPSTAT_RANDOM 0 +#define CORPSTAT_FEMALE 1 +#define CORPSTAT_MALE 2 +#define CORPSTAT_NEUTER 3 /* flags for decide_to_shift() */ #define SHIFT_SEENMSG 0x01 /* put out a message if in sight */ diff --git a/include/obj.h b/include/obj.h index bc00a7388..40e5db202 100644 --- a/include/obj.h +++ b/include/obj.h @@ -55,10 +55,8 @@ struct obj { * candy bar wrapper index; * scroll of mail (normal==0, bones or wishing==1, written==2); * splash of venom (normal==0, wishing==1); - * historic flag and gender for statues */ -#define STATUE_HISTORIC 0x01 -#define STATUE_MALE 0x02 -#define STATUE_FEMALE 0x04 + * gender for corpses and statues (0..3, CORPSTAT_GENDER), + * historic flag (4, CORPSTAT_HISTORIC) for statues */ char oclass; /* object class */ char invlet; /* designation in inventory */ char oartifact; /* artifact array index */ diff --git a/src/display.c b/src/display.c index 3a1a8d111..fa4a59e6e 100644 --- a/src/display.c +++ b/src/display.c @@ -2169,7 +2169,8 @@ map_glyphinfo(xchar x, xchar y, int glyph, special |= MG_STATUE; if (is_objpile(x,y)) special |= MG_OBJPILE; - if ((obj = sobj_at(STATUE, x, y)) && (obj->spe & STATUE_FEMALE)) + if ((obj = sobj_at(STATUE, x, y)) != 0 + && (obj->spe & CORPSTAT_GENDER) == CORPSTAT_FEMALE) special |= MG_FEMALE; } else if ((offset = (glyph - GLYPH_WARNING_OFF)) >= 0) { /* warn flash */ idx = offset + SYM_OFF_W; diff --git a/src/mkobj.c b/src/mkobj.c index 4873c85e4..bd4e1d973 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -1520,7 +1520,9 @@ mkcorpstat( } else { otmp = mksobj_at(objtype, x, y, init, FALSE); } - otmp->norevive = g.mkcorpstat_norevive; + /* record gender and 'historic statue' in overloaded enchantment field */ + otmp->spe = (corpstatflags & CORPSTAT_SPE_VAL); + otmp->norevive = g.mkcorpstat_norevive; /* via envrmt rather than flags */ /* when 'mtmp' is non-null save the monster's details with the corpse or statue; it will also force the 'ptr' override below */ @@ -1685,10 +1687,10 @@ mk_tt_object( never returns Null */ struct obj * mk_named_object( -int objtype, /* CORPSE or STATUE */ -struct permonst *ptr, -int x, int y, -const char *nm) + int objtype, /* CORPSE or STATUE */ + struct permonst *ptr, + int x, int y, + const char *nm) { struct obj *otmp; unsigned corpstatflags = (objtype != STATUE) ? CORPSTAT_INIT diff --git a/src/mon.c b/src/mon.c index 0578921f9..9cd1c28f3 100644 --- a/src/mon.c +++ b/src/mon.c @@ -480,7 +480,7 @@ pm_to_cham(int mndx) * etc.... */ static struct obj * -make_corpse(register struct monst* mtmp, unsigned int corpseflags) +make_corpse(struct monst *mtmp, unsigned int corpseflags) { register struct permonst *mdat = mtmp->data; int num; @@ -491,6 +491,11 @@ make_corpse(register struct monst* mtmp, unsigned int corpseflags) unsigned corpstatflags = corpseflags; boolean burythem = ((corpstatflags & CORPSTAT_BURIED) != 0); + if (mtmp->female) + corpstatflags |= CORPSTAT_FEMALE; + else if (!is_neuter(mtmp->data)) + corpstatflags |= CORPSTAT_MALE; + switch (mndx) { case PM_GRAY_DRAGON: case PM_SILVER_DRAGON: @@ -578,8 +583,8 @@ make_corpse(register struct monst* mtmp, unsigned int corpseflags) break; case PM_STONE_GOLEM: corpstatflags &= ~CORPSTAT_INIT; - obj = - mkcorpstat(STATUE, (struct monst *) 0, mdat, x, y, corpstatflags); + obj = mkcorpstat(STATUE, (struct monst *) 0, mdat, x, y, + corpstatflags); break; case PM_WOOD_GOLEM: num = d(2, 4); diff --git a/src/mondata.c b/src/mondata.c index a85f4dea8..cf367dd49 100644 --- a/src/mondata.c +++ b/src/mondata.c @@ -655,7 +655,7 @@ struct alt_spl { /* figure out what type of monster a user-supplied string is specifying; ingore anything past the monster name */ int -name_to_mon(const char *in_str, int * gender_name_var) +name_to_mon(const char *in_str, int *gender_name_var) { return name_to_monplus(in_str, (const char **) 0, gender_name_var); } @@ -691,6 +691,8 @@ name_to_monplus( if (remainder_p) *remainder_p = (const char *) 0; + if (gender_name_var) + *gender_name_var = 0; str = strcpy(buf, in_str); @@ -740,13 +742,16 @@ name_to_monplus( { "master of assassin", PM_MASTER_ASSASSIN, NEUTRAL }, /* Outdated names */ { "invisible stalker", PM_STALKER, NEUTRAL }, - { "high-elf", PM_ELVEN_MONARCH, NEUTRAL }, /* PM_HIGH_ELF is obsolete */ + { "high-elf", PM_ELVEN_MONARCH, NEUTRAL }, /* PM_HIGH_ELF is + * obsolete */ /* other misspellings or incorrect words */ { "wood-elf", PM_WOODLAND_ELF, NEUTRAL }, { "wood elf", PM_WOODLAND_ELF, NEUTRAL }, { "woodland nymph", PM_WOOD_NYMPH, NEUTRAL }, - { "halfling", PM_HOBBIT, NEUTRAL }, /* potential guess for polyself */ - { "genie", PM_DJINNI, NEUTRAL }, /* potential guess for ^G/#wizgenesis */ + { "halfling", PM_HOBBIT, NEUTRAL }, /* potential guess for + * polyself */ + { "genie", PM_DJINNI, NEUTRAL }, /* potential guess for + * ^G/#wizgenesis */ /* prefix used to workaround duplicate monster names for monsters with alternate forms */ { "human wererat", PM_HUMAN_WERERAT, NEUTRAL }, diff --git a/src/nhlobj.c b/src/nhlobj.c index 23c053cc8..88ef6d87b 100644 --- a/src/nhlobj.c +++ b/src/nhlobj.c @@ -257,10 +257,14 @@ l_obj_to_table(lua_State *L) nhl_add_table_entry_int(L, "quan", obj->quan); nhl_add_table_entry_int(L, "spe", obj->spe); - if (obj->otyp == STATUE) { - nhl_add_table_entry_int(L, "historic", (obj->spe & STATUE_HISTORIC)); - nhl_add_table_entry_int(L, "statue_male", (obj->spe & STATUE_MALE)); - nhl_add_table_entry_int(L, "statue_female", (obj->spe & STATUE_FEMALE)); + if (obj->otyp == STATUE) + nhl_add_table_entry_int(L, "historic", + (obj->spe & CORPSTAT_HISTORIC) != 0); + if (obj->otyp == CORPSE || obj->otyp == STATUE) { + nhl_add_table_entry_int(L, "male", + (obj->spe & CORPSTAT_MALE) != 0); + nhl_add_table_entry_int(L, "female", + (obj->spe & CORPSTAT_FEMALE) != 0); } nhl_add_table_entry_char(L, "oclass", diff --git a/src/objnam.c b/src/objnam.c index d11f5e472..be991fba3 100644 --- a/src/objnam.c +++ b/src/objnam.c @@ -606,18 +606,16 @@ xname_flags( case ROCK_CLASS: if (typ == STATUE && omndx != NON_PM) { char anbuf[10]; - int mgend = (obj->spe & STATUE_FEMALE) ? FEMALE : MALE; + int mgend = (((obj->spe & CORPSTAT_GENDER) == CORPSTAT_FEMALE) + ? FEMALE : MALE); Sprintf(buf, "%s%s of %s%s", - (Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC)) - ? "historic " - : "", + (Role_if(PM_ARCHEOLOGIST) + && (obj->spe & CORPSTAT_HISTORIC)) ? "historic " : "", actualn, - type_is_pname(&mons[omndx]) - ? "" - : the_unique_pm(&mons[omndx]) - ? "the " - : just_an(anbuf, pmname(&mons[omndx], mgend)), + type_is_pname(&mons[omndx]) ? "" + : the_unique_pm(&mons[omndx]) ? "the " + : just_an(anbuf, pmname(&mons[omndx], mgend)), pmname(&mons[omndx], mgend)); } else Strcpy(buf, actualn); @@ -1423,7 +1421,12 @@ corpse_xname( /* avoid "aligned priest"; it just exposes internal details */ mnam = "priest"; } else { - mnam = mons[omndx].pmnames[NEUTRAL]; + int cspe = (otmp->spe & CORPSTAT_GENDER), + mgend = (cspe == CORPSTAT_FEMALE) ? FEMALE + : (cspe == CORPSTAT_MALE) ? MALE + : NEUTRAL; + + mnam = pmname(&mons[omndx], mgend); if (the_unique_pm(&mons[omndx]) || type_is_pname(&mons[omndx])) { mnam = s_suffix(mnam); possessive = TRUE; @@ -3264,7 +3267,7 @@ readobjnam_init(char* bp, struct _readobjnam_data* d) = d->looted /* wizard mode fountain/sink/throne/tree and grave */ = d->real = d->fake = 0; /* Amulet */ d->tvariety = RANDOM_TIN; - d->mgend = MALE; + d->mgend = NEUTRAL; d->mntmp = NON_PM; d->contents = UNDEFINED; d->oclass = 0; @@ -3650,8 +3653,8 @@ readobjnam_postparse1(struct _readobjnam_data* d) d->typ = TIN; return 2; /*goto typfnd;*/ } else if ((d->p = strstri(d->bp, " of ")) != 0 - && (d->mntmp = name_to_mon(d->p + 4, - &d->mgend)) >= LOW_PM) + && ((d->mntmp = name_to_mon(d->p + 4, &d->mgend)) + >= LOW_PM)) *d->p = 0; } } @@ -3665,8 +3668,8 @@ readobjnam_postparse1(struct _readobjnam_data* d) const char *rest = 0; if (d->mntmp < LOW_PM && strlen(d->bp) > 2 - && (d->mntmp = name_to_monplus(d->bp, &rest, - &d->mgend)) >= LOW_PM) { + && ((d->mntmp = name_to_monplus(d->bp, &rest, &d->mgend)) + >= LOW_PM)) { char *obp = d->bp; /* 'rest' is a pointer past the matching portion; if that was @@ -4302,11 +4305,20 @@ readobjnam(char* bp, struct obj* no_wish) case HEAVY_IRON_BALL: case IRON_CHAIN: break; - case STATUE: - /* otmp->cobj already done in mksobj() */ - if (d.mgend) - d.otmp->spe |= STATUE_FEMALE; + case STATUE: /* otmp->cobj already done in mksobj() */ + case CORPSE: { + struct permonst *P = (d.mntmp >= LOW_PM) ? &mons[d.mntmp] : 0; + + d.otmp->spe = !P ? CORPSTAT_RANDOM + /* if neuter, force neuter regardless of wish request */ + : is_neuter(P) ? CORPSTAT_NEUTER + /* not neuter, honor wish unless it conflicts */ + : (d.mgend == FEMALE && !is_male(P)) ? CORPSTAT_FEMALE + : (d.mgend == MALE && !is_female(P)) ? CORPSTAT_MALE + /* unspecified or wish conflicts */ + : CORPSTAT_RANDOM; break; + }; #ifdef MAIL_STRUCTURES /* scroll of mail: 0: delivered in-game via external event (or randomly for fake mail); 1: from bones or wishing; 2: written with marker */ @@ -4379,7 +4391,7 @@ readobjnam(char* bp, struct obj* no_wish) d.otmp->corpsenm = d.mntmp; if (Has_contents(d.otmp) && verysmall(&mons[d.mntmp])) delete_contents(d.otmp); /* no spellbook */ - d.otmp->spe |= d.ishistoric ? STATUE_HISTORIC : 0; + d.otmp->spe |= d.ishistoric ? CORPSTAT_HISTORIC : 0; break; case SCALE_MAIL: /* Dragon mail - depends on the order of objects & dragons. */ diff --git a/src/sp_lev.c b/src/sp_lev.c index 5f89efa39..ac07aa6bc 100755 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -2002,6 +2002,7 @@ create_monster(monster* m, struct mkroom* croom) block_point(x, y); } + mtmp->female = m->female; if (m->peaceful >= 0) { mtmp->mpeaceful = m->peaceful; /* changed mpeaceful again; have to reset malign */ @@ -2020,8 +2021,6 @@ create_monster(monster* m, struct mkroom* croom) } if (m->seentraps) mtmp->mtrapseen = m->seentraps; - if (m->female) - mtmp->female = 1; if (m->cancelled) mtmp->mcan = 1; if (m->revived) @@ -3379,20 +3378,21 @@ lspo_object(lua_State *L) || tmpobj.id == CORPSE || tmpobj.id == TIN || tmpobj.id == FIGURINE) { struct permonst *pm = NULL; - int i, lflags = 0; + int i; char *montype = get_table_str_opt(L, "montype", NULL); if (montype) { if (strlen(montype) == 1 && def_char_to_monclass(*montype) != MAXMCLASSES) { - pm = mkclass(def_char_to_monclass(*montype), G_NOGEN|G_IGNORE); + pm = mkclass(def_char_to_monclass(*montype), + G_NOGEN | G_IGNORE); } else { for (i = LOW_PM; i < NUMMONS; i++) if (!strcmpi(mons[i].pmnames[NEUTRAL], montype) || (mons[i].pmnames[MALE] != 0 - && !strcmpi(mons[i].pmnames[MALE], montype)) + && !strcmpi(mons[i].pmnames[MALE], montype)) || (mons[i].pmnames[FEMALE] != 0 - && !strcmpi(mons[i].pmnames[FEMALE], montype))) { + && !strcmpi(mons[i].pmnames[FEMALE], montype))) { pm = &mons[i]; break; } @@ -3403,16 +3403,20 @@ lspo_object(lua_State *L) else nhl_error(L, "Unknown montype"); } - if (tmpobj.id == STATUE) { + if (tmpobj.id == STATUE || tmpobj.id == CORPSE) { + int lflags = 0; + if (get_table_boolean_opt(L, "historic", 0)) - lflags |= STATUE_HISTORIC; + lflags |= CORPSTAT_HISTORIC; if (get_table_boolean_opt(L, "male", 0)) - lflags |= STATUE_MALE; + lflags |= CORPSTAT_MALE; if (get_table_boolean_opt(L, "female", 0)) - lflags |= STATUE_FEMALE; + lflags |= CORPSTAT_FEMALE; tmpobj.spe = lflags; } else if (tmpobj.id == EGG) { tmpobj.spe = get_table_boolean_opt(L, "laid_by_you", 0) ? 1 : 0; + } else { + tmpobj.spe = 0; } } @@ -3719,7 +3723,8 @@ lspo_room(lua_State *L) tmproom.chance = get_table_int_opt(L, "chance", 100); tmproom.rlit = get_table_int_opt(L, "lit", -1); /* theme rooms default to unfilled */ - tmproom.needfill = get_table_int_opt(L, "filled", g.in_mk_themerooms ? 0 : 1); + tmproom.needfill = get_table_int_opt(L, "filled", + g.in_mk_themerooms ? 0 : 1); tmproom.joined = get_table_boolean_opt(L, "joined", TRUE); if (!g.coder->failed_room[g.coder->n_subroom - 1]) { @@ -4055,8 +4060,7 @@ lspo_trap(lua_State *L) get_table_xy_or_coord(L, &x, &y); tmptrap.type = get_table_traptype_opt(L, "type", -1); - tmptrap.spider_on_web - = get_table_boolean_opt(L, "spider_on_web", 1); + tmptrap.spider_on_web = get_table_boolean_opt(L, "spider_on_web", 1); } if (tmptrap.type == NO_TRAP) diff --git a/src/trap.c b/src/trap.c index a536f0f39..18bd505e2 100644 --- a/src/trap.c +++ b/src/trap.c @@ -603,7 +603,7 @@ animate_statue( struct obj *item; coord cc; boolean historic = (Role_if(PM_ARCHEOLOGIST) - && (statue->spe & STATUE_HISTORIC) != 0), + && (statue->spe & CORPSTAT_HISTORIC) != 0), golem_xform = FALSE, use_saved_traits; const char *comes_to_life; char statuename[BUFSZ], tmpbuf[BUFSZ]; @@ -645,10 +645,16 @@ animate_statue( be NON_PM; otherwise, set form to match the statue */ if (mon && mon->cham >= LOW_PM) (void) newcham(mon, mptr, FALSE, FALSE); - } else + } else { mon = makemon(mptr, x, y, (cause == ANIMATE_SPELL) ? (NO_MINVENT | MM_ADJACENTOK) : NO_MINVENT); + } + /* a non-montraits() statue might specify gender */ + if ((statue->spe & CORPSTAT_MALE) != 0) + mon->female = 0; + else if ((statue->spe & CORPSTAT_FEMALE) != 0) + mon->female = 1; } if (!mon) { @@ -659,11 +665,6 @@ animate_statue( return (struct monst *) 0; } - /* a non-montraits() statue might specify gender */ - if (statue->spe & STATUE_MALE) - mon->female = FALSE; - else if (statue->spe & STATUE_FEMALE) - mon->female = TRUE; /* if statue has been named, give same name to the monster */ if (has_oname(statue) && !unique_corpstat(mon->data)) mon = christen_monst(mon, ONAME(statue)); diff --git a/src/zap.c b/src/zap.c index f8fbc5e89..c0e71db89 100644 --- a/src/zap.c +++ b/src/zap.c @@ -856,6 +856,16 @@ revive(struct obj *corpse, boolean by_hero) if (!mtmp) return (struct monst *) 0; + /* if we didn't use montraits, corpse might specify mon's gender */ + if (!has_omonst(corpse)) { + int cspe = (corpse->spe & CORPSTAT_GENDER); + + if (cspe == CORPSTAT_MALE) + mtmp->female = 0; + else if (cspe == CORPSTAT_FEMALE) + mtmp->female = 1; + } + /* hiders shouldn't already be re-hidden when they revive */ if (mtmp->mundetected) { mtmp->mundetected = 0; @@ -4896,7 +4906,8 @@ break_statue(struct obj *obj) obj_extract_self(item); place_object(item, obj->ox, obj->oy); } - if (by_you && Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC)) { + if (by_you && Role_if(PM_ARCHEOLOGIST) + && (obj->spe & CORPSTAT_HISTORIC)) { You_feel("guilty about damaging such a historic statue."); adjalign(-1); }