/* * Implementation of armours. * * Note that the curse-handling code here depends on armour using * never_collapsible and never_identical, so that we can manipulate * INVOBJs without worrying about their getting collapsed. */ #include #include "obj.h" #include "vars.h" #include "dice.h" #include "util.h" #include "pline.h" #include "fuses.h" #include "format.h" #include "structs.h" #include "objtypes.h" #include "obj-armour.h" typedef struct armour ARMOUR; typedef struct alist ALIST; /* * Private data for a piece of armour. Consists of flags, its armour * class (innate plusses to defense), its bonus (plusses if positive, * minuses if negative), a string for passing to roll() for its * defense, and a backpointer to the monster, if any, that's wearing * it. */ struct armour { unsigned int flags; #define AF_KNOWN 0x00000001 #define AF_CURSED 0x00000002 const char *roll_type; int plus; char *roll_plus; MONST *wornby; } ; /* * A list of armour. This is part of the armour curse implementation. */ struct alist { ALIST *link; OBJ *ao; } ; /* * The list of cursed armours whose curses have been activated. Armour * curses affect only the player, which is why we need only one of * these (or, to put it another way, why there's no monster pointer in * an ALIST). * * This also means we don't need to worry about killing off an * activated curse on monster death. */ static ALIST *active_acurse = 0; /* * you_were_attacked as of last time the armour curse code ran. This * is used to detect combat and boost the chance of armour curse * activation. */ static unsigned int last_attacked_a; /* * Return the ARMOUR for an OBJ (or, if the OBJ isn't armour, panic). */ static ARMOUR *armour_for_obj(OBJ *o) { if (objtypes[o->type].class != OC_ARMOUR) panic("non-armour where armour needed"); return(o->private); } /* * The new method for armour. */ static OBJ *new_armour(int type, OBJ *o) { ARMOUR *a; a = malloc(sizeof(ARMOUR)); a->flags = 0; a->roll_type = objtypes[type].name2; a->plus = 0; a->roll_plus = 0; a->wornby = 0; reset_roll_string(&a->roll_plus,a->plus); o->private = a; return(o); } /* * The old method for armour. */ static void old_armour(OBJ *o) { ARMOUR *a; a = armour_for_obj(o); free(a->roll_plus); free(a); } /* * Armour-specific format conditionals. * * C - is this armour cursed? * * K - does this armour have its status known? * * W - is this armour currently being worn? */ static int fmt_cond_armour(char ch, INVOBJ *io) { switch (ch) { case 'C': return(armour_for_obj(io->v[0])->flags&AF_CURSED); break; case 'K': return(armour_for_obj(io->v[0])->flags&AF_KNOWN); break; case 'W': return(!!armour_for_obj(io->v[0])->wornby); break; } panic("invalid conditional +%c for armour",ch); } /* * Armour-specific formats. * * P - this armour's plusses (or minuses). */ static void fmt_spec_armour(FILE *f, char ch, INVOBJ *io) { switch (ch) { case 'P': fprintf(f,"%+d",armour_for_obj(io->v[0])->plus); break; default: panic("invalid format %%+%c for armour",ch); break; } } /* * Return true iff a piece of armour has its plusses known. */ static int identified_armour(INVOBJ *io) { return(armour_for_obj(io->v[0])->flags & AF_KNOWN); } /* * Identify a piece of armour. */ static INVOBJ *identify_armour(INVOBJ *io) { armour_for_obj(io->v[0])->flags |= AF_KNOWN; return(io); } /* * Set a piece of armour's curse status. */ static void setcursed_armour(OBJ *o, int c) { ARMOUR *a; a = armour_for_obj(o); if (c) a->flags |= AF_CURSED; else a->flags &= ~AF_CURSED; } /* * The OBJOPS for armour. */ #define format_armour std_format #define collapsible_armour never_collapsible #define identical_armour never_identical #define split_armour std_split #define moved_armour noop_moved #define cursable_armour 1 OBJOPS objops_armour = OBJOPS_INIT(armour); /* * This is called from a fuse periodically when an armour's curse is * active. (There is only one fuse calling this, not one per * activated curse.) Every tick, there is a 1% chance that a randomly * chosen piece of cursed armour is silently put in place as the * player's armour-in-use, warping it into the player's inventory if * necessary. If the player has been attacked, this chance goes up to * 50% (ie, in combat it is far more likely for the curse to cause a * switch). */ static void armour_curse_tick(long int argli __attribute__((__unused__)), void *argvp __attribute__((__unused__))) { int n; OBJ *o; ARMOUR *a; ALIST *al; ALIST **alp; if (last_attacked_a == you_were_attacked) { n = 100; } else { n = 2; last_attacked_a = you_were_attacked; } if (onein(n)) { n = 0; alp = &active_acurse; while ((al = *alp)) { a = armour_for_obj(al->ao); if (a->flags & AF_CURSED) { if ((n < 1) || onein(n+1)) o = al->ao; n ++; alp = &al->link; } else { *alp = al->link; free(al); } } if (n) { if (o->io->inv != &you->invent) { if (o->io->inv->type == IT_MON) armour_stop_using(o->io->inv->u.m,o->io); inv_move_1(o->io->inv,o->io,&you->invent); } if (you->armour != o->io->v[0]) { if (you->armour) armour_take_off(you,you->armour); armour_wear(you,o->io->v[0]); } } } if (active_acurse) addfuse_rel(&armour_curse_tick,0,0,TIMESCALE); } /* * Activate an armour's curse, if it isn't already active. */ static void activate_acurse(OBJ *o) { ALIST *al; last_attacked_a = you_were_attacked; for (al=active_acurse;al;al=al->link) if (al->ao == o) return; if (! active_acurse) addfuse_rel(&armour_curse_tick,0,0,TIMESCALE); al = malloc(sizeof(ALIST)); al->ao = o; al->link = active_acurse; active_acurse = al; } /* * Return the defense bonus for a piece of armour. This is a random * number suitable for directly adding to the wearer's innate defense * roll in the wearer's defense method. */ int armour_def_bonus(OBJ *o) { ARMOUR *a; if (! o) return(0); a = armour_for_obj(o); return(roll(a->roll_type)+roll(a->roll_plus)); } /* * Test whether a piece of armour is being worn (true) or not (false). */ int armour_being_worn(OBJ *o) { return(!!armour_for_obj(o)->wornby); } /* * Make the monster wear the armour. */ void armour_wear(MONST *m, OBJ *o) { ARMOUR *a; INVOBJ *io; int find_it(INVOBJ *io) { return(io->v[0]==o); } a = armour_for_obj(o); if (a->wornby) panic("wearing already-worn armour"); if (m->armour) panic("monster is already wearing armour"); m->armour = o; a->wornby = m; if ((a->flags & AF_CURSED) && (m == you)) { io = inv_scan(&you->invent,&find_it); if (! io) panic("wearing armour not in inventory"); activate_acurse(io->v[0]); } } /* * Make the monster take the armour off. */ void armour_take_off(MONST *m, OBJ *o) { ARMOUR *a; if (m->armour != o) panic("taking off armour monster isn't wearing"); a = armour_for_obj(o); if (a->wornby != m) panic("armour wornby pointer wrong"); m->armour = 0; a->wornby = 0; } /* * Inventory test function for armour_show(). */ static int armour_show_test(INVOBJ *io) { if (objtypes[io->v[0]->type].class != OC_ARMOUR) return(0); return(!!armour_for_obj(io->v[0])->wornby); } /* * Show what armour, if any, the monster is wearing. */ void armour_show(MONST *m) { show_inventory(&m->invent,"",&armour_show_test,"Not wearing any armour"); } /* * Stop the monster from using the armour object. The object must be * an armour object, must be carried by the monster, and must not be * worn by any other monster, but is a no-op if none of those trip and * the armour simply isn't worn. */ void armour_stop_using(MONST *m, INVOBJ *io) { ARMOUR *a; a = armour_for_obj(io->v[0]); if (io->inv != &m->invent) panic("armour_stop_using, not in inventory"); if (! a->wornby) return; if (a->wornby != m) panic("armour_stop_using, worn by someone else"); if (m->armour != io->v[0]) panic("armour_stop_using, wearing something else"); a->wornby = 0; m->armour = 0; } /* * Set a piece of armour's plusses. */ void armour_set_plusses(OBJ *o, int plus) { ARMOUR *a; a = armour_for_obj(o); a->plus = plus; reset_roll_string(&a->roll_plus,plus); }