/* * Implementation of weapons. */ #include #include "obj.h" #include "vars.h" #include "util.h" #include "dice.h" #include "pline.h" #include "format.h" #include "structs.h" #include "objtypes.h" #include "obj-weapon.h" typedef struct weapon WEAPON; /* * Private data for a weapon. Consists of flags (currently indicating * whether its plusses are known), its base to-hit and damage rolls, * its plusses to-hit and damage, the rolls for plusses to-hit and * damage, and a backpointer to the monster, if any, that's wielding * it. */ struct weapon { unsigned int flags; #define WF_KNOWN 0x00000001 const char *roll_base_hit; const char *roll_base_dmg; int bonus_hit; int bonus_dmg; char *roll_extra_hit; char *roll_extra_dmg; MONST *wielder; } ; /* * Return the WEAPON * for an OBJ * (or, if the OBJ isn't a weapon, * panic). */ static WEAPON *weapon_for_obj(OBJ *o) { if (objtypes[o->type].class != OC_WEAPON) panic("non-weapon where weapon needed"); return(o->private); } /* * Reset a weapon's roll_extra_hit and roll_extra_dmg strings. */ static void reset_extra_rolls(WEAPON *w) { reset_roll_string(&w->roll_extra_hit,w->bonus_hit); reset_roll_string(&w->roll_extra_dmg,w->bonus_dmg); } /* * The new method for weapons. */ static OBJ *new_weapon(int type, OBJ *o) { WEAPON *w; w = malloc(sizeof(WEAPON)); w->flags = 0; w->roll_base_hit = objtypes[type].name4; w->roll_base_dmg = objtypes[type].name5; w->bonus_hit = 0; w->bonus_dmg = 0; w->roll_extra_hit = 0; w->roll_extra_dmg = 0; reset_extra_rolls(w); o->private = w; return(o); } /* * The old method for weapnos. */ static void old_weapon(OBJ *o) { WEAPON *w; w = weapon_for_obj(o); free(w->roll_extra_hit); free(w->roll_extra_dmg); free(w); } /* * Weapon-specific format conditionals. */ static int fmt_cond_weapon(char ch, INVOBJ *io) { switch (ch) { case 'K': return(weapon_for_obj(io->v[0])->flags&WF_KNOWN); break; case 'W': return(!!weapon_for_obj(io->v[0])->wielder); break; } panic("invalid conditional +%c for weapon",ch); } /* * Weapon-specific formats. */ static void fmt_spec_weapon(FILE *f, char ch, INVOBJ *io) { WEAPON *w; w = weapon_for_obj(io->v[0]); switch (ch) { case 'P': if (w->bonus_hit == w->bonus_dmg) { fprintf(f,"%+d",w->bonus_hit); } else { fprintf(f,"%+d,%+d",w->bonus_hit,w->bonus_dmg); } break; default: panic("invalid format %%+%c for weapon",ch); break; } } /* * The collapsible method for weapons. * * Weapons are collapsible if they look the same, which means, if they * are the same type and either (a) neither one has its plusses known * or (b) their plusses are identical. However, wielded weapons are * never collapsible with anything. */ static int collapsible_weapon(OBJ *o1, OBJ *o2) { WEAPON *w1; WEAPON *w2; int t1; int t2; w1 = weapon_for_obj(o1); w2 = weapon_for_obj(o2); if (w1->wielder || w2->wielder) return(0); t1 = o1->type; t2 = o2->type; #if 0 if ((t1 == OBJ_ARROW_OF_DEATH) && !(w1->flags & WF_KNOWN)) t1 = OBJ_ARROW; if ((t2 == OBJ_ARROW_OF_DEATH) && !(w2->flags & WF_KNOWN)) t2 = OBJ_ARROW; #endif if (t1 != t2) return(0); if (w1->flags != w2->flags) return(0); if ((w1->flags & WF_KNOWN) && ((w1->bonus_hit != w2->bonus_hit) || (w1->bonus_dmg != w2->bonus_dmg))) return(0); return(1); } /* * The identical method for weapons. */ static int identical_weapon(OBJ *o1, OBJ *o2) { WEAPON *w1; WEAPON *w2; if (o1->type != o2->type) return(0); w1 = weapon_for_obj(o1); w2 = weapon_for_obj(o2); if ( w1->wielder || w2->wielder || (w1->flags != w2->flags) || (w1->bonus_hit != w2->bonus_hit) || (w1->bonus_dmg != w2->bonus_dmg) ) return(0); return(1); } /* * The identified method for weapons. */ static int identified_weapon(INVOBJ *io) { return(weapon_for_obj(io->v[0])->flags & WF_KNOWN); } /* * The identify method for weapons. If the object is singular, just * identify it. Otherwise, pick a representative, split it off into * its own INVOBJ, and identify it. */ static INVOBJ *identify_weapon(INVOBJ *io) { int i; int r; OBJ *o; if (io->n == 1) { weapon_for_obj(io->v[0])->flags |= WF_KNOWN; return(io); } r = rnd(io->dispn); for (i=0;in;i++) { r -= io->v[i]->number; if (r < 0) break; } if (i >= io->n) panic("invobj overrun in weapon identify"); o = remove_obj_from_invobj(i,io); weapon_for_obj(o)->flags |= WF_KNOWN; return(add_obj_to_inv(o,io->inv)); } /* * The OBJOPS vector for weapons. */ #define format_weapon std_format #define split_weapon std_split #define moved_weapon noop_moved OBJOPS objops_weapon = OBJOPS_INIT(weapon); /* * Test whether a weapon is being wielded (return true) or not (false). */ int weapon_being_wielded(OBJ *o) { return(!!weapon_for_obj(o)->wielder); } /* * Make a monster wield a weapon. Anything currently wielded is * unwielded first; if o is nil, nothing is wielded. */ void weapon_wield(MONST *m, OBJ *o) { WEAPON *w; INVOBJ *io; int find_weapon(INVOBJ *io) { return(io->v[0]==m->weapon); } if (o) { w = weapon_for_obj(o); if (w->wielder) panic("wielding already-wielded weapon"); } if (m->weapon) { WEAPON *ww; ww = weapon_for_obj(m->weapon); if (ww->wielder != m) panic("weapon wielder wrong"); io = inv_scan(&m->invent,&find_weapon); if (! io) panic("weapon isn't carried"); ww->wielder = 0; m->weapon = 0; inv_move_1(&m->invent,io,&m->invent); } m->weapon = o; if (o) w->wielder = m; } /* * Return a weapon's to-hit bonus. */ int weapon_hit_bonus(OBJ *o) { WEAPON *w; w = weapon_for_obj(o); return(roll(w->roll_base_hit)+roll(w->roll_extra_hit)); } /* * Return a weapon's damage bonus. */ int weapon_dmg_bonus(OBJ *o) { WEAPON *w; w = weapon_for_obj(o); return(roll(w->roll_base_dmg)+roll(w->roll_extra_dmg)); } /* * Inventory test function for weapon_show(). */ static int weapon_show_test(INVOBJ *io) { if (objtypes[io->v[0]->type].class != OC_WEAPON) return(0); return(!!weapon_for_obj(io->v[0])->wielder); } /* * Show what weapon, if any, the monster is wielding. */ void weapon_show(MONST *m) { show_inventory(&m->invent,"",&weapon_show_test,"Not wielding anything"); } /* * Stop the monster from using the weapon object. The object must be a * weapon, must be carried by the monster, and must not be wielded by * any other monster, but is a no-op if none of those trip and the * weapon simply isn't wielded. */ void weapon_stop_using(MONST *m, INVOBJ *io) { WEAPON *w; w = weapon_for_obj(io->v[0]); if (io->inv != &m->invent) panic("weapon_stop_using, not in inventory"); if (! w->wielder) return; if (w->wielder != m) panic("weapon_stop_using, wielded by someone else"); if (m->weapon != io->v[0]) panic("weapon_stop_using, wielding something else"); w->wielder = 0; m->weapon = 0; }