diff --git a/src/mondata.c b/src/mondata.c new file mode 100644 index 000000000..a3ebe0789 --- /dev/null +++ b/src/mondata.c @@ -0,0 +1,655 @@ +/* SCCS Id: @(#)mondata.c 3.3 2001/12/05 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#include "hack.h" +#include "eshk.h" +#include "epri.h" + +/* fake attack and damage types */ +#define AT_ANY (-1) +#define AD_ANY (-1) + +/* These routines provide basic data for any type of monster. */ + +#ifdef OVLB + +void +set_mon_data(mon, ptr, flag) +struct monst *mon; +struct permonst *ptr; +int flag; +{ + mon->data = ptr; + if (flag == -1) return; /* "don't care" */ + + if (flag == 1) + mon->mintrinsics |= (ptr->mresists & 0x00FF); + else + mon->mintrinsics = (ptr->mresists & 0x00FF); + return; +} + +#endif /* OVLB */ +#ifdef OVL0 + +struct attack * +attacktype_fordmg(ptr, atyp, dtyp) +struct permonst *ptr; +int atyp, dtyp; +{ + struct attack *a; + + for (a = &ptr->mattk[0]; a < &ptr->mattk[NATTK]; a++) + if (a->aatyp == atyp && (dtyp == AD_ANY || a->adtyp == dtyp)) + return a; + + return (struct attack *)0; +} + +boolean +attacktype(ptr, atyp) +struct permonst *ptr; +int atyp; +{ + return attacktype_fordmg(ptr, atyp, AD_ANY) ? TRUE : FALSE; +} + +#endif /* OVL0 */ +#ifdef OVLB + +boolean +poly_when_stoned(ptr) + struct permonst *ptr; +{ + return((boolean)(is_golem(ptr) && ptr != &mons[PM_STONE_GOLEM] && + !(mvitals[PM_STONE_GOLEM].mvflags & G_GENOD))); + /* allow G_EXTINCT */ +} + +boolean +resists_drli(mon) /* returns TRUE if monster is drain-life resistant */ +struct monst *mon; +{ + struct permonst *ptr = mon->data; + struct obj *wep = ((mon == &youmonst) ? uwep : MON_WEP(mon)); + + return (boolean)(is_undead(ptr) || is_demon(ptr) || is_were(ptr) || + ptr == &mons[PM_DEATH] || + (wep && wep->oartifact && defends(AD_DRLI, wep))); +} + +boolean +resists_magm(mon) /* TRUE if monster is magic-missile resistant */ +struct monst *mon; +{ + struct permonst *ptr = mon->data; + struct obj *o; + + /* as of 3.2.0: gray dragons, Angels, Oracle, Yeenoghu */ + if (dmgtype(ptr, AD_MAGM) || ptr == &mons[PM_BABY_GRAY_DRAGON] || + dmgtype(ptr, AD_RBRE)) /* Chromatic Dragon */ + return TRUE; + /* check for magic resistance granted by wielded weapon */ + o = (mon == &youmonst) ? uwep : MON_WEP(mon); + if (o && o->oartifact && defends(AD_MAGM, o)) + return TRUE; + /* check for magic resistance granted by worn or carried items */ + o = (mon == &youmonst) ? invent : mon->minvent; + for ( ; o; o = o->nobj) + if ((o->owornmask && objects[o->otyp].oc_oprop == ANTIMAGIC) || + (o->oartifact && protects(AD_MAGM, o))) + return TRUE; + return FALSE; +} + +/* TRUE iff monster is resistant to light-induced blindness */ +boolean +resists_blnd(mon) +struct monst *mon; +{ + struct permonst *ptr = mon->data; + boolean is_you = (mon == &youmonst); + struct obj *o; + + if (is_you ? (Blind || u.usleep) : + (mon->mblinded || !mon->mcansee || !haseyes(ptr) || + /* BUG: temporary sleep sets mfrozen, but since + paralysis does too, we can't check it */ + mon->msleeping)) + return TRUE; + /* yellow light, Archon; !dust vortex, !cobra, !raven */ + if (dmgtype_fromattack(ptr, AD_BLND, AT_EXPL) || + dmgtype_fromattack(ptr, AD_BLND, AT_GAZE)) + return TRUE; + o = is_you ? uwep : MON_WEP(mon); + if (o && o->oartifact && defends(AD_BLND, o)) + return TRUE; + o = is_you ? invent : mon->minvent; + for ( ; o; o = o->nobj) + if ((o->owornmask && objects[o->otyp].oc_oprop == BLINDED) || + (o->oartifact && protects(AD_BLND, o))) + return TRUE; + return FALSE; +} + +/* TRUE iff monster can be blinded by the given attack */ +/* Note: may return TRUE when mdef is blind (e.g. new cream-pie attack) */ +boolean +can_blnd(magr, mdef, aatyp, obj) +struct monst *magr; /* NULL == no specific aggressor */ +struct monst *mdef; +uchar aatyp; +struct obj *obj; /* aatyp == AT_WEAP, AT_SPIT */ +{ + boolean is_you = (mdef == &youmonst); + boolean check_visor = FALSE; + struct obj *o; + const char *s; + + /* no eyes protect against all attacks for now */ + if (!haseyes(mdef->data)) + return FALSE; + + switch(aatyp) { + case AT_EXPL: case AT_BOOM: case AT_GAZE: case AT_MAGC: + case AT_BREA: /* assumed to be lightning */ + /* light-based attacks may be cancelled or resisted */ + if (magr && magr->mcan) + return FALSE; + return !resists_blnd(mdef); + + case AT_WEAP: case AT_SPIT: case AT_NONE: + /* an object is used (thrown/spit/other) */ + if (obj && (obj->otyp == CREAM_PIE)) { + if (is_you && Blindfolded) + return FALSE; + } else if (obj && (obj->otyp == BLINDING_VENOM)) { + /* all ublindf, including LENSES, protect, cream-pies too */ + if (is_you && (ublindf || u.ucreamed)) + return FALSE; + check_visor = TRUE; + } else if (obj && (obj->otyp == POT_BLINDNESS)) { + return TRUE; /* no defense */ + } else + return FALSE; /* other objects cannot cause blindness yet */ + if ((magr == &youmonst) && u.uswallow) + return FALSE; /* can't affect eyes while inside monster */ + break; + + case AT_ENGL: + if (is_you && (Blindfolded || u.usleep || u.ucreamed)) + return FALSE; + if (!is_you && mdef->msleeping) + return FALSE; + break; + + case AT_CLAW: + /* e.g. raven: all ublindf, including LENSES, protect */ + if (is_you && ublindf) + return FALSE; + if ((magr == &youmonst) && u.uswallow) + return FALSE; /* can't affect eyes while inside monster */ + check_visor = TRUE; + break; + + case AT_TUCH: case AT_STNG: + /* some physical, blind-inducing attacks can be cancelled */ + if (magr && magr->mcan) + return FALSE; + break; + + default: + break; + } + + /* check if wearing a visor (only checked if visor might help) */ + if (check_visor) { + o = (mdef == &youmonst) ? invent : mdef->minvent; + for ( ; o; o = o->nobj) + if ((o->owornmask & W_ARMH) && + (s = OBJ_DESCR(objects[o->otyp])) != (char *)0 && + !strcmp(s, "visored helmet")) + return FALSE; + } + + return TRUE; +} + +#endif /* OVLB */ +#ifdef OVL0 + +boolean +ranged_attk(ptr) /* returns TRUE if monster can attack at range */ +struct permonst *ptr; +{ + register int i, atyp; + long atk_mask = (1L << AT_BREA) | (1L << AT_SPIT) | (1L << AT_GAZE); + + /* was: (attacktype(ptr, AT_BREA) || attacktype(ptr, AT_WEAP) || + attacktype(ptr, AT_SPIT) || attacktype(ptr, AT_GAZE) || + attacktype(ptr, AT_MAGC)); + but that's too slow -dlc + */ + for (i = 0; i < NATTK; i++) { + atyp = ptr->mattk[i].aatyp; + if (atyp >= AT_WEAP) return TRUE; + /* assert(atyp < 32); */ + if ((atk_mask & (1L << atyp)) != 0L) return TRUE; + } + + return FALSE; +} + +boolean +hates_silver(ptr) +register struct permonst *ptr; +/* returns TRUE if monster is especially affected by silver weapons */ +{ + return((boolean)(is_were(ptr) || ptr->mlet==S_VAMPIRE || is_demon(ptr) || + ptr == &mons[PM_SHADE] || + (ptr->mlet==S_IMP && ptr != &mons[PM_TENGU]))); +} + +#endif /* OVL0 */ +#ifdef OVL1 + +boolean +can_track(ptr) /* returns TRUE if monster can track well */ + register struct permonst *ptr; +{ + if (uwep && uwep->oartifact == ART_EXCALIBUR) + return TRUE; + else + return((boolean)haseyes(ptr)); +} + +#endif /* OVL1 */ +#ifdef OVLB + +boolean +sliparm(ptr) /* creature will slide out of armor */ + register struct permonst *ptr; +{ + return((boolean)(is_whirly(ptr) || ptr->msize <= MZ_SMALL || + noncorporeal(ptr))); +} + +boolean +breakarm(ptr) /* creature will break out of armor */ + register struct permonst *ptr; +{ + return ((bigmonst(ptr) || (ptr->msize > MZ_SMALL && !humanoid(ptr)) || + /* special cases of humanoids that cannot wear body armor */ + ptr == &mons[PM_MARILITH] || ptr == &mons[PM_WINGED_GARGOYLE]) + && !sliparm(ptr)); +} +#endif /* OVLB */ +#ifdef OVL1 + +boolean +sticks(ptr) /* creature sticks other creatures it hits */ + register struct permonst *ptr; +{ + return((boolean)(dmgtype(ptr,AD_STCK) || dmgtype(ptr,AD_WRAP) || + attacktype(ptr,AT_HUGS))); +} + +struct attack * +dmgtype_fromattack(ptr, dtyp, atyp) +struct permonst *ptr; +int dtyp, atyp; +{ + struct attack *a; + + for (a = &ptr->mattk[0]; a < &ptr->mattk[NATTK]; a++) + if (a->adtyp == dtyp && (atyp == AT_ANY || a->aatyp == atyp)) + return a; + + return (struct attack *)0; +} + +boolean +dmgtype(ptr, dtyp) +struct permonst *ptr; +int dtyp; +{ + return dmgtype_fromattack(ptr, dtyp, AT_ANY) ? TRUE : FALSE; +} + +/* returns the maximum damage a defender can do to the attacker via + * a passive defense */ +int +max_passive_dmg(mdef, magr) + register struct monst *mdef, *magr; +{ + int i, dmg = 0; + uchar adtyp; + + for(i = 0; i < NATTK; i++) + if(mdef->data->mattk[i].aatyp == AT_NONE || + mdef->data->mattk[i].aatyp == AT_BOOM) { + adtyp = mdef->data->mattk[i].adtyp; + if ((adtyp == AD_ACID && !resists_acid(magr)) || + (adtyp == AD_COLD && !resists_cold(magr)) || + (adtyp == AD_FIRE && !resists_fire(magr)) || + (adtyp == AD_ELEC && !resists_elec(magr)) || + adtyp == AD_PHYS) { + dmg = mdef->data->mattk[i].damn; + if(!dmg) dmg = mdef->data->mlevel+1; + dmg *= mdef->data->mattk[i].damd; + } else dmg = 0; + + return dmg; + } + return 0; +} + +#endif /* OVL1 */ +#ifdef OVL0 + +int +monsndx(ptr) /* return an index into the mons array */ + struct permonst *ptr; +{ + register int i; + + i = (int)(ptr - &mons[0]); + if (i < LOW_PM || i >= NUMMONS) { + /* ought to switch this to use `fmt_ptr' */ + panic("monsndx - could not index monster (%lx)", + (unsigned long)ptr); + return FALSE; /* will not get here */ + } + + return(i); +} + +#endif /* OVL0 */ +#ifdef OVL1 + + +int +name_to_mon(in_str) +const char *in_str; +{ + /* Be careful. We must check the entire string in case it was + * something such as "ettin zombie corpse". The calling routine + * doesn't know about the "corpse" until the monster name has + * already been taken off the front, so we have to be able to + * read the name with extraneous stuff such as "corpse" stuck on + * the end. + * This causes a problem for names which prefix other names such + * as "ettin" on "ettin zombie". In this case we want the _longest_ + * name which exists. + * This also permits plurals created by adding suffixes such as 's' + * or 'es'. Other plurals must still be handled explicitly. + */ + register int i; + register int mntmp = NON_PM; + register char *s, *str, *term; + char buf[BUFSZ]; + int len, slen; + + str = strcpy(buf, in_str); + + if (!strncmp(str, "a ", 2)) str += 2; + else if (!strncmp(str, "an ", 3)) str += 3; + + slen = strlen(str); + term = str + slen; + + if ((s = strstri(str, "vortices")) != 0) + Strcpy(s+4, "ex"); + /* be careful with "ies"; "priest", "zombies" */ + else if (slen > 3 && !strcmpi(term-3, "ies") && + (slen < 7 || strcmpi(term-7, "zombies"))) + Strcpy(term-3, "y"); + /* luckily no monster names end in fe or ve with ves plurals */ + else if (slen > 3 && !strcmpi(term-3, "ves")) + Strcpy(term-3, "f"); + + slen = strlen(str); /* length possibly needs recomputing */ + + { + static const struct alt_spl { const char* name; short pm_val; } + names[] = { + /* Alternate spellings */ + { "grey dragon", PM_GRAY_DRAGON }, + { "baby grey dragon", PM_BABY_GRAY_DRAGON }, + { "grey unicorn", PM_GRAY_UNICORN }, + { "grey ooze", PM_GRAY_OOZE }, + { "gray-elf", PM_GREY_ELF }, + /* Hyphenated names */ + { "ki rin", PM_KI_RIN }, + { "uruk hai", PM_URUK_HAI }, + { "orc captain", PM_ORC_CAPTAIN }, + { "woodland elf", PM_WOODLAND_ELF }, + { "green elf", PM_GREEN_ELF }, + { "grey elf", PM_GREY_ELF }, + { "gray elf", PM_GREY_ELF }, + { "elf lord", PM_ELF_LORD }, +#if 0 /* OBSOLETE */ + { "high elf", PM_HIGH_ELF }, +#endif + { "olog hai", PM_OLOG_HAI }, + { "arch lich", PM_ARCH_LICH }, + /* Some irregular plurals */ + { "incubi", PM_INCUBUS }, + { "succubi", PM_SUCCUBUS }, + { "violet fungi", PM_VIOLET_FUNGUS }, + { "homunculi", PM_HOMUNCULUS }, + { "baluchitheria", PM_BALUCHITHERIUM }, + { "lurkers above", PM_LURKER_ABOVE }, + { "cavemen", PM_CAVEMAN }, + { "cavewomen", PM_CAVEWOMAN }, + { "djinn", PM_DJINNI }, + { "mumakil", PM_MUMAK }, + { "erinyes", PM_ERINYS }, + /* falsely caught by -ves check above */ + { "master of thief", PM_MASTER_OF_THIEVES }, + /* end of list */ + { 0, 0 } + }; + register const struct alt_spl *namep; + + for (namep = names; namep->name; namep++) + if (!strncmpi(str, namep->name, (int)strlen(namep->name))) + return namep->pm_val; + } + + for (len = 0, i = LOW_PM; i < NUMMONS; i++) { + register int m_i_len = strlen(mons[i].mname); + if (m_i_len > len && !strncmpi(mons[i].mname, str, m_i_len)) { + if (m_i_len == slen) return i; /* exact match */ + else if (slen > m_i_len && + (str[m_i_len] == ' ' || + !strcmpi(&str[m_i_len], "s") || + !strncmpi(&str[m_i_len], "s ", 2) || + !strcmpi(&str[m_i_len], "es") || + !strncmpi(&str[m_i_len], "es ", 3))) { + mntmp = i; + len = m_i_len; + } + } + } + if (mntmp == NON_PM) mntmp = title_to_mon(str, (int *)0, (int *)0); + return mntmp; +} + +#endif /* OVL1 */ +#ifdef OVL2 + +/* returns 3 values (0=male, 1=female, 2=none) */ +int +gender(mtmp) +register struct monst *mtmp; +{ + if (is_neuter(mtmp->data)) return 2; + return mtmp->female; +} + +/* Like gender(), but lower animals and such are still "it". */ +/* This is the one we want to use when printing messages. */ +int +pronoun_gender(mtmp) +register struct monst *mtmp; +{ + if (!canspotmon(mtmp) || !humanoid(mtmp->data)) + return 2; + return mtmp->female; +} + +#endif /* OVL2 */ +#ifdef OVLB + +boolean +levl_follower(mtmp) +register struct monst *mtmp; +{ + return((boolean)(mtmp->mtame || (mtmp->data->mflags2 & M2_STALK) || is_fshk(mtmp) + || (mtmp->iswiz && !mon_has_amulet(mtmp)))); +} + +static const short grownups[][2] = { + {PM_CHICKATRICE, PM_COCKATRICE}, + {PM_LITTLE_DOG, PM_DOG}, {PM_DOG, PM_LARGE_DOG}, + {PM_HELL_HOUND_PUP, PM_HELL_HOUND}, + {PM_WINTER_WOLF_CUB, PM_WINTER_WOLF}, + {PM_KITTEN, PM_HOUSECAT}, {PM_HOUSECAT, PM_LARGE_CAT}, + {PM_PONY, PM_HORSE}, {PM_HORSE, PM_WARHORSE}, + {PM_KOBOLD, PM_LARGE_KOBOLD}, {PM_LARGE_KOBOLD, PM_KOBOLD_LORD}, + {PM_GNOME, PM_GNOME_LORD}, {PM_GNOME_LORD, PM_GNOME_KING}, + {PM_DWARF, PM_DWARF_LORD}, {PM_DWARF_LORD, PM_DWARF_KING}, + {PM_OGRE, PM_OGRE_LORD}, {PM_OGRE_LORD, PM_OGRE_KING}, + {PM_ELF, PM_ELF_LORD}, {PM_WOODLAND_ELF, PM_ELF_LORD}, + {PM_GREEN_ELF, PM_ELF_LORD}, {PM_GREY_ELF, PM_ELF_LORD}, + {PM_ELF_LORD, PM_ELVENKING}, + {PM_LICH, PM_DEMILICH}, {PM_DEMILICH, PM_MASTER_LICH}, + {PM_MASTER_LICH, PM_ARCH_LICH}, + {PM_VAMPIRE, PM_VAMPIRE_LORD}, {PM_BAT, PM_GIANT_BAT}, + {PM_BABY_GRAY_DRAGON, PM_GRAY_DRAGON}, + {PM_BABY_SILVER_DRAGON, PM_SILVER_DRAGON}, +#if 0 /* DEFERRED */ + {PM_BABY_SHIMMERING_DRAGON, PM_SHIMMERING_DRAGON}, +#endif + {PM_BABY_RED_DRAGON, PM_RED_DRAGON}, + {PM_BABY_WHITE_DRAGON, PM_WHITE_DRAGON}, + {PM_BABY_ORANGE_DRAGON, PM_ORANGE_DRAGON}, + {PM_BABY_BLACK_DRAGON, PM_BLACK_DRAGON}, + {PM_BABY_BLUE_DRAGON, PM_BLUE_DRAGON}, + {PM_BABY_GREEN_DRAGON, PM_GREEN_DRAGON}, + {PM_BABY_YELLOW_DRAGON, PM_YELLOW_DRAGON}, + {PM_RED_NAGA_HATCHLING, PM_RED_NAGA}, + {PM_BLACK_NAGA_HATCHLING, PM_BLACK_NAGA}, + {PM_GOLDEN_NAGA_HATCHLING, PM_GOLDEN_NAGA}, + {PM_GUARDIAN_NAGA_HATCHLING, PM_GUARDIAN_NAGA}, + {PM_SMALL_MIMIC, PM_LARGE_MIMIC}, {PM_LARGE_MIMIC, PM_GIANT_MIMIC}, + {PM_BABY_LONG_WORM, PM_LONG_WORM}, + {PM_BABY_PURPLE_WORM, PM_PURPLE_WORM}, + {PM_BABY_CROCODILE, PM_CROCODILE}, + {PM_SOLDIER, PM_SERGEANT}, + {PM_SERGEANT, PM_LIEUTENANT}, + {PM_LIEUTENANT, PM_CAPTAIN}, + {PM_WATCHMAN, PM_WATCH_CAPTAIN}, + {PM_ALIGNED_PRIEST, PM_HIGH_PRIEST}, + {PM_STUDENT, PM_ARCHEOLOGIST}, + {PM_ATTENDANT, PM_HEALER}, + {PM_PAGE, PM_KNIGHT}, + {PM_ACOLYTE, PM_PRIEST}, + {PM_APPRENTICE, PM_WIZARD}, + {PM_MANES,PM_LEMURE}, +#ifdef KOPS + {PM_KEYSTONE_KOP, PM_KOP_SERGEANT}, + {PM_KOP_SERGEANT, PM_KOP_LIEUTENANT}, + {PM_KOP_LIEUTENANT, PM_KOP_KAPTAIN}, +#endif + {NON_PM,NON_PM} +}; + +int +little_to_big(montype) +int montype; +{ +#ifndef AIXPS2_BUG + register int i; + + for (i = 0; grownups[i][0] >= LOW_PM; i++) + if(montype == grownups[i][0]) return grownups[i][1]; + return montype; +#else +/* AIX PS/2 C-compiler 1.1.1 optimizer does not like the above for loop, + * and causes segmentation faults at runtime. (The problem does not + * occur if -O is not used.) + * lehtonen@cs.Helsinki.FI (Tapio Lehtonen) 28031990 + */ + int i; + int monvalue; + + monvalue = montype; + for (i = 0; grownups[i][0] >= LOW_PM; i++) + if(montype == grownups[i][0]) monvalue = grownups[i][1]; + + return monvalue; +#endif +} + +int +big_to_little(montype) +int montype; +{ + register int i; + + for (i = 0; grownups[i][0] >= LOW_PM; i++) + if(montype == grownups[i][1]) return grownups[i][0]; + return montype; +} + +static const char *levitate[4] = { "float", "Float", "wobble", "Wobble" }; +static const char *fly[4] = { "fly", "Fly", "flutter", "Flutter" }; +static const char *slither[4] = { "slither", "Slither", "falter", "Falter" }; +static const char *ooze[4] = { "ooze", "Ooze", "tremble", "Tremble" }; +static const char *immobile[4] = { "wiggle", "Wiggle", "pulsate", "Pulsate" }; +static const char *crawl[4] = { "crawl", "Crawl", "falter", "Falter" }; + +const char * +locomotion(ptr, def) +const struct permonst *ptr; +const char *def; +{ + int capitalize = (*def == highc(*def)); + + return ( + is_floater(ptr) ? levitate[capitalize] : + is_flyer(ptr) ? fly[capitalize] : + slithy(ptr) ? slither[capitalize] : + amorphous(ptr) ? ooze[capitalize] : + !ptr->mmove ? immobile[capitalize] : + nolimbs(ptr) ? crawl[capitalize] : + def + ); + +} + +const char * +stagger(ptr, def) +const struct permonst *ptr; +const char *def; +{ + int capitalize = 2 + (*def == highc(*def)); + + return ( + is_floater(ptr) ? levitate[capitalize] : + is_flyer(ptr) ? fly[capitalize] : + slithy(ptr) ? slither[capitalize] : + amorphous(ptr) ? ooze[capitalize] : + !ptr->mmove ? immobile[capitalize] : + nolimbs(ptr) ? crawl[capitalize] : + def + ); + +} + +#endif /* OVLB */ + +/*mondata.c*/