This is a foundation patch for patches to follow.
- use a full short index for mon->cham field.
- The current system of providing CHAM_XXX values
was limited to the 3 bits allocated in the bitfield and invalidated
save/bones if the field was expanded.
- The current system didn't provide an easy backwards change
if multiple monster types wanted to use the bit, there was a one
to one mapping: For instance, if you wanted a CHAM_VAMPIRE,
and you wanted vampires, vampire lords, and Vlad to use it, you
would have to have CHAM_VAMPIRE, CHAM_VAMPIRE_LORD,
and CHAM_VLAD defined to achieve that with the one-to-one backward
mapping.
- This new way just uses the mon[] index in the mon->cham field and
eliminates the need for CHAM_XXX (CHAM_ORDINARY is still used).
- no longer requires the cham_to_pm mappings
move all flags that are system or port specific from flag struct to sysflags
struct which is used only if SYSFLAGS is defined
all fields in flags struct are unconditionally present
+monst cham field now a short and uses mons[] index
-/* SCCS Id: @(#)extern.h 3.4 2003/03/14 */
+/* SCCS Id: @(#)extern.h 3.4 2004/06/12 */
/* Copyright (c) Steve Creps, 1988. */
/* NetHack may be freely redistributed. See license for details. */
E void FDECL(restore_cham, (struct monst *));
E boolean FDECL(hideunder, (struct monst*));
E void FDECL(mon_animal_list, (BOOLEAN_P));
+E int FDECL(select_newcham_form, (struct monst *));
E int FDECL(newcham, (struct monst *,struct permonst *,BOOLEAN_P,BOOLEAN_P));
E int FDECL(can_be_hatched, (int));
E int FDECL(egg_type_from_parent, (int,BOOLEAN_P));
E void NDECL(set_uasmon);
E void NDECL(change_sex);
-E void FDECL(polyself, (BOOLEAN_P));
+E void FDECL(polyself, (int));
E int FDECL(polymon, (int));
E void NDECL(rehumanize);
E int NDECL(dobreathe);
#define herbivorous(ptr) (((ptr)->mflags1 & M1_HERBIVORE) != 0L)
#define metallivorous(ptr) (((ptr)->mflags1 & M1_METALLIVORE) != 0L)
#define polyok(ptr) (((ptr)->mflags2 & M2_NOPOLY) == 0L)
+#define is_shapeshifter(ptr) (((ptr)->mflags2 & M2_SHAPESHIFTER) != 0L)
#define is_undead(ptr) (((ptr)->mflags2 & M2_UNDEAD) != 0L)
#define is_were(ptr) (((ptr)->mflags2 & M2_WERE) != 0L)
#define is_elf(ptr) (((ptr)->mflags2 & M2_ELF) != 0L)
#define M2_PRINCE 0x00000800L /* is an overlord to its kind */
#define M2_MINION 0x00001000L /* is a minion of a deity */
#define M2_GIANT 0x00002000L /* is a giant */
+#define M2_SHAPESHIFTER 0x00004000L /* is a shapeshifting species */
#define M2_MALE 0x00010000L /* always male */
#define M2_FEMALE 0x00020000L /* always female */
#define M2_NEUTER 0x00040000L /* neither male nor female */
-/* SCCS Id: @(#)monst.h 3.4 1999/01/04 */
+/* SCCS Id: @(#)monst.h 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
struct permonst *data;
unsigned m_id;
short mnum; /* permanent monster index number */
+ short cham; /* if shapeshifter, orig mons[] idx goes here */
+#define CHAM_ORDINARY 0 /* not a shapechanger */
short movement; /* movement points (derived from permonst definition and added effects */
uchar m_lev; /* adjusted difficulty level of monster */
aligntyp malign; /* alignment of this monster, relative to the
Bitfield(minvis,1); /* currently invisible */
Bitfield(invis_blkd,1); /* invisibility blocked */
Bitfield(perminvis,1); /* intrinsic minvis value */
- Bitfield(cham,3); /* shape-changer */
-/* note: lychanthropes are handled elsewhere */
-#define CHAM_ORDINARY 0 /* not a shapechanger */
-#define CHAM_CHAMELEON 1 /* animal */
-#define CHAM_DOPPELGANGER 2 /* demi-human */
-#define CHAM_SANDESTIN 3 /* demon */
-#define CHAM_MAX_INDX CHAM_SANDESTIN
+ Bitfield(mcan,1); /* has been cancelled */
+ Bitfield(mburied,1); /* has been buried */
Bitfield(mundetected,1); /* not seen in present hiding place */
/* implies one of M1_CONCEAL or M1_HIDE,
* but not mimic (that is, snake, spider,
* trapper, piercer, eel)
*/
- Bitfield(mcan,1); /* has been cancelled */
- Bitfield(mburied,1); /* has been buried */
Bitfield(mspeed,2); /* current speed */
Bitfield(permspeed,2); /* intrinsic mspeed value */
Bitfield(mrevived,1); /* has been revived from the dead */
Bitfield(mavenge,1); /* did something to deserve retaliation */
-
Bitfield(mflee,1); /* fleeing */
+ Bitfield(mcansee,1); /* cansee 1, temp.blinded 0, blind 0 */
+
Bitfield(mfleetim,7); /* timeout for mflee */
+ Bitfield(msleeping,1); /* asleep until woken */
- Bitfield(mcansee,1); /* cansee 1, temp.blinded 0, blind 0 */
Bitfield(mblinded,7); /* cansee 0, temp.blinded n, blind 0 */
+ Bitfield(mstun,1); /* stunned (off balance) */
- Bitfield(mcanmove,1); /* paralysis, similar to mblinded */
Bitfield(mfrozen,7);
+ Bitfield(mcanmove,1); /* paralysis, similar to mblinded */
- Bitfield(msleeping,1); /* asleep until woken */
- Bitfield(mstun,1); /* stunned (off balance) */
Bitfield(mconf,1); /* confused */
Bitfield(mpeaceful,1); /* does not attack unprovoked */
Bitfield(mtrapped,1); /* trapped in a pit, web or bear trap */
Bitfield(mleashed,1); /* monster is on a leash */
Bitfield(isshk,1); /* is shopkeeper */
Bitfield(isminion,1); /* is a minion */
-
Bitfield(isgd,1); /* is guard */
Bitfield(ispriest,1); /* is a priest */
+
Bitfield(iswiz,1); /* is the Wizard of Yendor */
Bitfield(wormno,5); /* at most 31 worms on any level */
+ /* 2 free bits */
+
#define MAX_NUM_WORMS 32 /* should be 2^(wormno bitfield size) */
long mstrategy; /* for monsters with mflag3: current strategy */
-/* SCCS Id: @(#)patchlevel.h 3.4 2004/05/24 */
+/* SCCS Id: @(#)patchlevel.h 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
-#define EDITLEVEL 12
+#define EDITLEVEL 14
#define COPYRIGHT_BANNER_A \
"NetHack, Copyright 1985-2004"
stop_occupation();
else
nomul(0);
- if (change == 1) polyself(FALSE);
+ if (change == 1) polyself(0);
else you_were();
change = 0;
}
-/* SCCS Id: @(#)apply.c 3.4 2003/11/18 */
+/* SCCS Id: @(#)apply.c 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
STATIC_PTR int
wiz_polyself()
{
- polyself(TRUE);
+ polyself(1);
return 0;
}
struct monst *mtmp;
{
mtmp->meating = 0;
- if (mtmp->m_ap_type && mtmp->mappearance && !mtmp->cham) {
+ if (mtmp->m_ap_type && mtmp->mappearance && mtmp->cham == CHAM_ORDINARY) {
/* was eating a mimic and now appearance needs resetting */
mtmp->m_ap_type = 0;
mtmp->mappearance = 0;
/* case PM_SANDESTIN: */
if (!Unchanging) {
You_feel("a change coming over you.");
- polyself(FALSE);
+ polyself(0);
}
break;
case PM_MIND_FLAYER:
case 10: pline("This water contains toxic wastes!");
if (!Unchanging) {
You("undergo a freakish metamorphosis!");
- polyself(FALSE);
+ polyself(0);
}
break;
/* more odd messages --JJB */
-/* SCCS Id: @(#)mhitu.c 3.4 2003/11/26 */
+/* SCCS Id: @(#)mhitu.c 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
}
/* Special demon handling code */
- if(!mtmp->cham && is_demon(mdat) && !range2
+ if((mtmp->cham == CHAM_ORDINARY) && is_demon(mdat) && !range2
&& mtmp->data != &mons[PM_BALROG]
&& mtmp->data != &mons[PM_SUCCUBUS]
&& mtmp->data != &mons[PM_INCUBUS])
if(!mtmp->mcan && !rn2(13)) msummon(mtmp);
/* Special lycanthrope handling code */
- if(!mtmp->cham && is_were(mdat) && !range2) {
+ if((mtmp->cham == CHAM_ORDINARY) && is_were(mdat) && !range2) {
if(is_human(mdat)) {
if(!rn2(5 - (night() * 2)) && !mtmp->mcan) new_were(mtmp);
}
}
-#define Athome (Inhell && !mtmp->cham)
+#define Athome (Inhell && (mtmp->cham == CHAM_ORDINARY))
int
demon_talk(mtmp) /* returns 1 if it won't attack. */
-/* SCCS Id: @(#)mon.c 3.4 2004/05/21 */
+/* SCCS Id: @(#)mon.c 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
return mndx;
}
-/* convert monster index to chameleon index */
+/* return monster index if chameleon, or CHAM_ORDINARY if not */
int
pm_to_cham(mndx)
int mndx;
{
- int mcham;
+ int mcham = CHAM_ORDINARY;
- switch (mndx) {
- case PM_CHAMELEON: mcham = CHAM_CHAMELEON; break;
- case PM_DOPPELGANGER: mcham = CHAM_DOPPELGANGER; break;
- case PM_SANDESTIN: mcham = CHAM_SANDESTIN; break;
- default: mcham = CHAM_ORDINARY; break;
- }
+ /*
+ * As of 3.5.0 we just check M2_SHAPESHIFTER instead of having a
+ * big switch statement with hardcoded shapeshifter types here.
+ */
+ if (mndx > LOW_PM && is_shapeshifter(&mons[mndx])) mcham = mndx;
return mcham;
}
-/* convert chameleon index to monster index */
-STATIC_VAR short cham_to_pm[] = {
- NON_PM, /* placeholder for CHAM_ORDINARY */
- PM_CHAMELEON,
- PM_DOPPELGANGER,
- PM_SANDESTIN,
-};
-
/* for deciding whether corpse will carry along full monster data */
#define KEEPTRAITS(mon) ((mon)->isshk || (mon)->mtame || \
unique_corpstat(mon->data) || \
mptr = mtmp->data; /* save this for m_detach() */
/* restore chameleon, lycanthropes to true form at death */
- if (mtmp->cham)
- set_mon_data(mtmp, &mons[cham_to_pm[mtmp->cham]], -1);
+ if (mtmp->cham != CHAM_ORDINARY) {
+ set_mon_data(mtmp, &mons[mtmp->cham], -1);
+ mtmp->cham = CHAM_ORDINARY;
+ }
else if (mtmp->data == &mons[PM_WEREJACKAL])
set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL], -1);
else if (mtmp->data == &mons[PM_WEREWOLF])
for(mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (DEADMONSTER(mtmp)) continue;
mcham = (int) mtmp->cham;
- if (mcham) {
- mtmp->cham = CHAM_ORDINARY;
- (void) newcham(mtmp, &mons[cham_to_pm[mcham]],
+ if (mcham != CHAM_ORDINARY) {
+ (void) newcham(mtmp, &mons[mcham],
FALSE, FALSE);
+ mtmp->cham = CHAM_ORDINARY;
}
if(is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN)
new_were(mtmp);
if (Protection_from_shape_changers) {
mcham = (int) mon->cham;
- if (mcham) {
+ if (mcham != CHAM_ORDINARY) {
mon->cham = CHAM_ORDINARY;
- (void) newcham(mon, &mons[cham_to_pm[mcham]], FALSE, FALSE);
+ (void) newcham(mon, &mons[mcham], FALSE, FALSE);
} else if (is_were(mon->data) && !is_human(mon->data)) {
new_were(mon);
}
restrap(mtmp)
register struct monst *mtmp;
{
- if(mtmp->cham || mtmp->mcan || mtmp->m_ap_type ||
+ if((mtmp->cham != CHAM_ORDINARY) || mtmp->mcan || mtmp->m_ap_type ||
cansee(mtmp->mx, mtmp->my) || rn2(3) || (mtmp == u.ustuck) ||
(sensemon(mtmp) && distu(mtmp->mx, mtmp->my) <= 2))
return(FALSE);
return animal_list[rn2(animal_list_count)];
}
-STATIC_OVL int
+int
select_newcham_form(mon)
struct monst *mon;
{
int mndx = NON_PM;
switch (mon->cham) {
- case CHAM_SANDESTIN:
+ case PM_SANDESTIN:
if (rn2(7)) mndx = pick_nasty();
break;
- case CHAM_DOPPELGANGER:
+ case PM_DOPPELGANGER:
if (!rn2(7)) mndx = pick_nasty();
else if (rn2(3)) mndx = rn1(PM_WIZARD - PM_ARCHEOLOGIST + 1,
PM_ARCHEOLOGIST);
break;
- case CHAM_CHAMELEON:
+ case PM_CHAMELEON:
if (!rn2(3)) mndx = pick_animal();
break;
+ case PM_VLAD_THE_IMPALER:
+ case PM_VAMPIRE_LORD:
+ case PM_VAMPIRE:
+ if (mon_has_special(mon) && /* ensure Vlad can carry it still */
+ mon->cham == PM_VLAD_THE_IMPALER) {
+ mndx = PM_VLAD_THE_IMPALER;
+ break;
+ }
+ if (!rn2(10) && mon->cham != PM_VAMPIRE) {
+ /* VAMPIRE_LORD || VLAD */
+ mndx = PM_WOLF;
+ break;
+ }
+ mndx = !rn2(4) ? PM_FOG_CLOUD : PM_VAMPIRE_BAT;
+ break;
case CHAM_ORDINARY:
{
struct obj *m_armr = which_armor(mon, W_ARM);
kill_genocided_monsters()
{
struct monst *mtmp, *mtmp2;
- boolean kill_cham[CHAM_MAX_INDX+1];
+ boolean kill_cham[NUMMONS];
int mndx;
kill_cham[CHAM_ORDINARY] = FALSE; /* (this is mndx==0) */
- for (mndx = 1; mndx <= CHAM_MAX_INDX; mndx++)
- kill_cham[mndx] = (mvitals[cham_to_pm[mndx]].mvflags & G_GENOD) != 0;
+ for (mndx = LOW_PM; mndx < NUMMONS; mndx++)
+ kill_cham[mndx] = (mvitals[mndx].mvflags & G_GENOD) != 0;
/*
* Called during genocide, and again upon level change. The latter
* catches up with any migrating monsters as they finally arrive at
if (DEADMONSTER(mtmp)) continue;
mndx = monsndx(mtmp->data);
if ((mvitals[mndx].mvflags & G_GENOD) || kill_cham[mtmp->cham]) {
- if (mtmp->cham && !kill_cham[mtmp->cham])
+ if ((mtmp->cham != CHAM_ORDINARY) && !kill_cham[mtmp->cham])
(void) newcham(mtmp, (struct permonst *)0, FALSE, FALSE);
else
mondead(mtmp);
/*
* ants
*/
+ /* Never use M2_SHAPESHIFTER for mons[0] as long as CHAM_ORDINARY==0 */
MON("giant ant", S_ANT,
LVL(2, 18, 3, 0, 0), (G_GENO|G_SGROUP|3),
A(ATTK(AT_BITE, AD_PHYS, 1, 4),
NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
SIZ(WT_HUMAN, 400, 0, MS_IMITATE, MZ_HUMAN), MR_SLEEP, 0,
M1_HUMANOID|M1_OMNIVORE,
- M2_NOPOLY|M2_HUMAN|M2_HOSTILE|M2_STRONG|M2_COLLECT,
+ M2_NOPOLY|M2_HUMAN|M2_HOSTILE|M2_STRONG|M2_COLLECT|M2_SHAPESHIFTER,
M3_INFRAVISIBLE, HI_DOMESTIC),
MON("nurse", S_HUMAN,
LVL(11, 6, 0, 0, 0), (G_GENO|3),
A(ATTK(AT_WEAP, AD_PHYS, 2, 6), ATTK(AT_WEAP, AD_PHYS, 2, 6),
NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
SIZ(1500, 400, 0, MS_CUSS, MZ_HUMAN), MR_STONE, 0,
- M1_HUMANOID, M2_NOPOLY|M2_STALK|M2_STRONG|M2_COLLECT,
+ M1_HUMANOID, M2_NOPOLY|M2_STALK|M2_STRONG|M2_COLLECT|M2_SHAPESHIFTER,
M3_INFRAVISIBLE|M3_INFRAVISION, CLR_GRAY),
/*
* sea monsters
A(ATTK(AT_BITE, AD_PHYS, 4, 2),
NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK, NO_ATTK),
SIZ(100, 100, 0, MS_SILENT, MZ_TINY), 0, 0,
- M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_NOPOLY|M2_HOSTILE, 0, CLR_BROWN),
+ M1_ANIMAL|M1_NOHANDS|M1_CARNIVORE, M2_NOPOLY|M2_HOSTILE|M2_SHAPESHIFTER,
+ 0, CLR_BROWN),
MON("crocodile", S_LIZARD,
LVL(6, 9, 5, 0, 0), (G_GENO|1),
A(ATTK(AT_BITE, AD_PHYS, 4, 2), ATTK(AT_CLAW, AD_PHYS, 1,12),
-/* SCCS Id: @(#)muse.c 3.4 2002/12/23 */
+/* SCCS Id: @(#)muse.c 3.4 2004/06/12 */
/* Copyright (C) 1990 by Ken Arromdee */
/* NetHack may be freely redistributed. See license for details. */
if(dist2(x, y, mtmp->mux, mtmp->muy) > 36)
return FALSE;
- if (!stuck && !immobile && !mtmp->cham && monstr[monsndx(mdat)] < 6) {
+ if (!stuck && !immobile &&
+ (mtmp->cham == CHAM_ORDINARY) && monstr[monsndx(mdat)] < 6) {
boolean ignore_boulders = (verysmall(mdat) ||
throws_rocks(mdat) ||
passes_walls(mdat));
m.has_misc = MUSE_POT_SPEED;
}
nomore(MUSE_WAN_POLYMORPH);
- if(obj->otyp == WAN_POLYMORPH && obj->spe > 0 && !mtmp->cham
+ if(obj->otyp == WAN_POLYMORPH && obj->spe > 0
+ && (mtmp->cham == CHAM_ORDINARY)
&& monstr[monsndx(mdat)] < 6) {
m.misc = obj;
m.has_misc = MUSE_WAN_POLYMORPH;
}
nomore(MUSE_POT_POLYMORPH);
- if(obj->otyp == POT_POLYMORPH && !mtmp->cham
+ if(obj->otyp == POT_POLYMORPH
+ && (mtmp->cham == CHAM_ORDINARY)
&& monstr[monsndx(mdat)] < 6) {
m.misc = obj;
m.has_misc = MUSE_POT_POLYMORPH;
}
else if (mtmp->mpeaceful) Strcat(info, ", peaceful");
if (mtmp->meating) Strcat(info, ", eating");
- if (mtmp->meating && !mtmp->cham &&
+ if (mtmp->meating && (mtmp->cham == CHAM_ORDINARY) &&
mtmp->mappearance && mtmp->m_ap_type) {
Sprintf(eos(info), ", mimicing %s",
(mtmp->m_ap_type == M_AP_FURNITURE) ?
}
void
-polyself(forcecontrol)
-boolean forcecontrol;
+polyself(psflags)
+int psflags;
{
char buf[BUFSZ];
int old_light, new_light;
int mntmp = NON_PM;
int tries=0;
+ boolean forcecontrol = (psflags == 1);
boolean draconian = (uarm &&
uarm->otyp >= GRAY_DRAGON_SCALE_MAIL &&
uarm->otyp <= YELLOW_DRAGON_SCALES);
-/* SCCS Id: @(#)potion.c 3.4 2003/11/26 */
+/* SCCS Id: @(#)potion.c 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
break;
case POT_POLYMORPH:
You_feel("a little %s.", Hallucination ? "normal" : "strange");
- if (!Unchanging) polyself(FALSE);
+ if (!Unchanging) polyself(0);
break;
default:
impossible("What a funny potion! (%u)", otmp->otyp);
break;
case POT_POLYMORPH:
You_feel("a little %s.", Hallucination ? "normal" : "strange");
- if (!Unchanging && !Antimagic) polyself(FALSE);
+ if (!Unchanging && !Antimagic) polyself(0);
break;
case POT_ACID:
if (!Acid_resistance) {
-/* SCCS Id: @(#)trap.c 3.4 2003/12/26 */
+/* SCCS Id: @(#)trap.c 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
deltrap(trap); /* delete trap before polymorph */
newsym(u.ux,u.uy); /* get rid of trap symbol */
You_feel("a change coming over you.");
- polyself(FALSE);
+ polyself(0);
}
break;
}
-/* SCCS Id: @(#)zap.c 3.4 2003/11/26 */
+/* SCCS Id: @(#)zap.c 3.4 2004/06/12 */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/* NetHack may be freely redistributed. See license for details. */
mtmp = makemon(&mons[montype], x, y,
NO_MINVENT | MM_NOWAIT);
if (mtmp) {
- if (mtmp->cham == CHAM_DOPPELGANGER) {
+ if (mtmp->cham == PM_DOPPELGANGER) {
/* change shape to match the corpse */
(void) newcham(mtmp, mptr, FALSE, FALSE);
} else if (mtmp->data->mlet == S_ZOMBIE) {
makeknown(WAN_POLYMORPH);
case SPE_POLYMORPH:
if (!Unchanging)
- polyself(FALSE);
+ polyself(0);
break;
case WAN_CANCELLATION: