/* NetHack 5.0	monst.h	$NHDT-Date: 1738640524 2025/02/03 19:42:04 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.67 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2016. */
/* NetHack may be freely redistributed.  See license for details. */

#ifndef MONST_H
#define MONST_H

/* start with incomplete types in case these aren't defined yet;
   basic pointers to them don't need to know their details */
struct monst;
struct obj;

#ifndef MEXTRA_H
#include "mextra.h"
#endif

/* The weapon_check flag is used two ways:
 * 1) When calling mon_wield_item, is 2-6 depending on what is desired.
 * 2) Between calls to mon_wield_item, is 0 or 1 depending on whether or not
 *    the weapon is known by the monster to be cursed (so it shouldn't bother
 *    trying for another weapon).
 * I originally planned to also use 0 if the monster already had its best
 * weapon, to avoid the overhead of a call to mon_wield_item, but it turns out
 * that there are enough situations which might make a monster change its
 * weapon that this is impractical.  --KAA
 */
enum wpn_chk_flags {
    NO_WEAPON_WANTED    = 0,
    NEED_WEAPON         = 1,
    NEED_RANGED_WEAPON  = 2,
    NEED_HTH_WEAPON     = 3,
    NEED_PICK_AXE       = 4,
    NEED_AXE            = 5,
    NEED_PICK_OR_AXE    = 6
};

/* The following flags are used for the second argument to display_minventory
 * in invent.c:
 *
 * PICK_NONE, PICK_ONE, PICK_ANY (wintype.h)  0, 1, 2
 * MINV_NOLET  If set, don't display inventory letters on monster's inventory.
 * MINV_ALL    If set, display all items in monster's inventory, otherwise
 *             just display wielded weapons and worn items.
 */
#define MINV_PICKMASK 0x03 /* 1|2 */
#define MINV_NOLET    0x04
#define MINV_ALL      0x08

/* monster appearance types */
enum m_ap_types {
    M_AP_NOTHING   = 0, /* mappearance unused--monster appears as itself */
    M_AP_FURNITURE = 1, /* stairs, a door, an altar, etc. */
    M_AP_OBJECT    = 2, /* an object */
    M_AP_MONSTER   = 3  /* a monster; mostly used for cloned Wizard */
};

#define MON_FLOOR        0x00
#define MON_OFFMAP       0x01
#define MON_DETACH       0x02
#define MON_MIGRATING    0x04
#define MON_LIMBO        0x08
#define MON_BUBBLEMOVE   0x10
#define MON_ENDGAME_FREE 0x20
#define MON_ENDGAME_MIGR 0x40
#define MON_OBLITERATE   0x80
#define MON_STILL_ARRIVING 0x100

#define M_AP_TYPMASK  0x7
#define M_AP_F_DKNOWN 0x8
#define U_AP_TYPE (gy.youmonst.m_ap_type & M_AP_TYPMASK)
#define U_AP_FLAG (gy.youmonst.m_ap_type & ~M_AP_TYPMASK)
#define M_AP_TYPE(m) ((m)->m_ap_type & M_AP_TYPMASK)
#define M_AP_FLAG(m) ((m)->m_ap_type & ~M_AP_TYPMASK)

enum m_seen_resistance {
    M_SEEN_NOTHING = 0x0000,
    M_SEEN_MAGR    = 0x0001, /* Antimagic, AD_MAGM */
    M_SEEN_FIRE    = 0x0002, /* Fire_resistance, AD_FIRE */
    M_SEEN_COLD    = 0x0004, /* Cold_resistance, AD_COLD */
    M_SEEN_SLEEP   = 0x0008, /* Sleep_resistance, AD_SLEE */
    M_SEEN_DISINT  = 0x0010, /* Disint_resistance, AD_DISN */
    M_SEEN_ELEC    = 0x0020, /* Shock_resistance, AD_ELEC */
    M_SEEN_POISON  = 0x0040, /* AD_DRST */
    M_SEEN_ACID    = 0x0080, /* Acid_resistance, AD_ACID */
    M_SEEN_REFL    = 0x0100, /* reflection, no corresponding AD_foo */
};

#define m_seenres(mon, mask) ((mon)->seen_resistance & (mask))
#define m_setseenres(mon, mask) ((mon)->seen_resistance |= (mask))
#define m_clearseenres(mon, mask) ((mon)->seen_resistance &= ~(mask))
#define monstseesu_ad(adtyp) monstseesu(cvt_adtyp_to_mseenres(adtyp))
#define monstunseesu_ad(adtyp) monstunseesu(cvt_adtyp_to_mseenres(adtyp))
#define monstunseesu_prop(prop) monstunseesu(cvt_prop_to_mseenres(prop))

struct monst {
    struct monst *nmon;
    struct permonst *data;
    unsigned m_id;
    short mnum;           /* permanent monster index number */
    short cham;           /* if shapeshifter, orig mons[] idx goes here */
    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
                           * player (positive = good to kill) */
    coordxy mx, my;
    coordxy mux, muy;     /* where the monster thinks you are */
#define MTSZ 4
    /* mtrack[0..2] is used to keep extra data when migrating the monster */
    coord mtrack[MTSZ];   /* monster track */
    int mhp, mhpmax;
    unsigned mappearance; /* for undetected mimics and the wiz */
    uchar m_ap_type;      /* what mappearance is describing, m_ap_types */

    schar mtame;          /* level of tameness, implies peaceful */
    unsigned short mintrinsics; /* low 8 correspond to mresists */
    unsigned short mextrinsics; /* low 8 correspond to mresists */
    unsigned long seen_resistance; /* M_SEEN_x; saw you resist an effect */
    int mspec_used;       /* monster's special ability attack timeout */

    Bitfield(female, 1);      /* is female */
    Bitfield(minvis, 1);      /* currently invisible */
    Bitfield(invis_blkd, 1);  /* invisibility blocked */
    Bitfield(perminvis, 1);   /* intrinsic minvis value */
    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(mcansee, 1);   /* cansee 1, temp.blinded 0, blind 0 */

    Bitfield(mspeed, 2);    /* current speed */
    Bitfield(permspeed, 2); /* intrinsic mspeed value */
    Bitfield(mrevived, 1);  /* has been revived from the dead */
    Bitfield(mcloned, 1);   /* has been cloned from another */
    Bitfield(mavenge, 1);   /* did something to deserve retaliation */
    Bitfield(mflee, 1);     /* fleeing */

    Bitfield(mfleetim, 7);  /* timeout for mflee */
    Bitfield(msleeping, 1); /* asleep until woken */

    Bitfield(mblinded, 7);  /* cansee 0, temp.blinded n, blind 0 */
    Bitfield(mstun, 1);     /* stunned (off balance) */

    Bitfield(mfrozen, 7);
    Bitfield(mcanmove, 1);  /* paralysis, similar to mblinded */

    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 an aligned priest or high priest */

    Bitfield(iswiz, 1);     /* is the Wizard of Yendor */
#define MAX_NUM_WORMS 32    /* should be 2^(wormno bitfield size) */
    Bitfield(wormno, 5);    /* at most 31 worms on any level */
    Bitfield(mtemplit, 1);  /* temporarily seen; only valid during bhit() */
    Bitfield(meverseen, 1); /* mon has been seen at some point */

    Bitfield(mspotted, 1);  /* mon is currently seen by hero */
    Bitfield(mwandexp, 1);  /* mon has experience with wands */
    Bitfield(mgenmklev, 1); /* made by the level generation */
    /* 5 spare bits */

    unsigned long mstrategy; /* for monsters with mflag3: current strategy */
#ifdef NHSTDC
#define STRAT_APPEARMSG 0x80000000UL
#else
#define STRAT_APPEARMSG 0x80000000L
#endif
#define STRAT_ARRIVE    0x40000000L /* just arrived on current level */
#define STRAT_WAITFORU  0x20000000L
#define STRAT_CLOSE     0x10000000L
#define STRAT_WAITMASK  (STRAT_CLOSE | STRAT_WAITFORU)
#define STRAT_HEAL      0x08000000L
#define STRAT_GROUND    0x04000000L
#define STRAT_MONSTR    0x02000000L
#define STRAT_PLAYER    0x01000000L
#define STRAT_NONE      0x00000000L
#define STRAT_STRATMASK 0x0f000000L
    /* mstrategy unused 0x00ffff00L */
#define STRAT_GOAL      0x000000ffL

    coord mgoal;           /* monster strategy, target location */
    long mtrapseen;        /* bitmap of traps we've been trapped in */
    long mlstmv;           /* for catching up with lost time */
    long mstate;           /* debugging info on monsters stored here */
    long migflags;         /* migrating flags */
    long mspare1;
    struct obj *minvent;   /* mon's inventory */
    struct obj *mw;        /* mon's weapon */
    long misc_worn_check;  /* mon's wornmask */
    xint16 weapon_check;   /* flag for whether to try switching weapons */

    int meating;           /* monster is eating timeout */
    struct mextra *mextra; /* point to mextra struct */
};

#define newmonst() (struct monst *) alloc(sizeof (struct monst))

/* these are in mspeed */
#define MSLOW 1 /* slowed monster */
#define MFAST 2 /* speeded monster */

#define MON_WEP(mon) ((mon)->mw)
#define MON_NOWEP(mon) ((mon)->mw = (struct obj *) 0)

/* dead monsters stay on the fmon list until dmonsfree() at end of turn */
#define DEADMONSTER(mon) ((mon)->mhp < 1)

#define is_starting_pet(mon) ((mon)->m_id == svc.context.startingpet_mid)
#define is_vampshifter(mon) \
    ((mon)->cham == PM_VAMPIRE || (mon)->cham == PM_VAMPIRE_LEADER \
     || (mon)->cham == PM_VLAD_THE_IMPALER)
#define vampshifted(mon) (is_vampshifter((mon)) && !is_vampire((mon)->data))
/* Vlad might be vampshifted so just checking monst->data is insufficient */
#define is_Vlad(m) ((m)->data == &mons[PM_VLAD_THE_IMPALER]  \
                    || (m)->cham == PM_VLAD_THE_IMPALER)

/* monsters which cannot be displaced: temple priests, shopkeepers,
   vault guards, the Oracle, quest leader */
#define mundisplaceable(mon) \
    ((mon)->ispriest || (mon)->isshk                    \
     || (mon)->isgd || (mon)->data == &mons[PM_ORACLE]  \
     || (mon)->m_id == svq.quest_status.leader_m_id)

/* mimic appearances that block vision/light */
#define is_lightblocker_mappear(mon) \
    (is_obj_mappear(mon, BOULDER)                          \
     || (M_AP_TYPE(mon) == M_AP_FURNITURE                  \
         && ((mon)->mappearance == S_hcdoor                \
             || (mon)->mappearance == S_vcdoor             \
             || (mon)->mappearance < S_ndoor /* = walls */ \
             || (mon)->mappearance == S_tree)))
#define is_door_mappear(mon) (M_AP_TYPE(mon) == M_AP_FURNITURE   \
                              && ((mon)->mappearance == S_hcdoor \
                                  || (mon)->mappearance == S_vcdoor))
#define is_obj_mappear(mon,otyp) (M_AP_TYPE(mon) == M_AP_OBJECT \
                                  && (mon)->mappearance == (otyp))

/* is mon m (presumably just killed) a troll and obj o Trollsbane? */
#define troll_baned(m,o) \
    ((m)->data->mlet == S_TROLL && (o) && (o)->oartifact == ART_TROLLSBANE)

#define engulfing_u(mon) (u.uswallow && (u.ustuck == (mon)))
#define helpless(mon) ((mon)->msleeping || !(mon)->mcanmove)

#define mon_perma_blind(mon) (!mon->mcansee && !mon->mblinded)

#define mon_offmap(mon) ((mon)->mstate != MON_FLOOR)

/* Get the maximum difficulty monsters that can currently be generated,
   given the current level difficulty and the hero's level. */
#define monmax_difficulty(levdif) (((levdif) + u.ulevel) / 2)
#define monmin_difficulty(levdif) ((levdif) / 6)
#define monmax_difficulty_lev() (monmax_difficulty(level_difficulty()))

/* Macros for whether a type of monster is too strong for a specific level. */
#define montoostrong(monindx, lev) (mons[monindx].difficulty > lev)
#define montooweak(monindx, lev) (mons[monindx].difficulty < lev)

#ifdef PMNAME_MACROS
#define Mgender(mon) ((mon)->female ? FEMALE : MALE)
#endif
#define mon_resistancebits(mon) \
    ((mon)->data->mresists | (mon)->mextrinsics | (mon)->mintrinsics)
#define resists_fire(mon)   Resists_Elem(mon, FIRE_RES)
#define resists_cold(mon)   Resists_Elem(mon, COLD_RES)
#define resists_sleep(mon)  Resists_Elem(mon, SLEEP_RES)
#define resists_disint(mon) Resists_Elem(mon, DISINT_RES)
#define resists_elec(mon)   Resists_Elem(mon, SHOCK_RES)
#define resists_poison(mon) Resists_Elem(mon, POISON_RES)
#define resists_acid(mon)   Resists_Elem(mon, ACID_RES)
#define resists_ston(mon)   Resists_Elem(mon, STONE_RES)

#define is_lminion(mon) \
    (is_minion((mon)->data) && mon_aligntyp(mon) == A_LAWFUL)

/* x is a valid index into mons[] array */
#define ismnum(x) ((x) >= LOW_PM && (x) < NUMMONS)

#endif /* MONST_H */
