/* * The implementation of rings. */ #include #include "obj.h" #include "mon.h" #include "util.h" #include "dice.h" #include "vars.h" #include "pline.h" #include "param.h" #include "effect.h" #include "format.h" #include "structs.h" #include "objtypes.h" #include "obj-ring.h" typedef struct ring RING; typedef struct ringeffect RINGEFFECT; /* * Private data for a ring. */ struct ring { int plusses; unsigned int flags; #define RF_KNOWN 0x00000001 #define RF_CURSED 0x00000002 MONST *wornby; union { RINGEFFECT *most; // most rings struct { // rings of protection char *rollstr; BONUSROLL *roll; } prot; } ; } ; /* * Private data for a ring-generated effect, for most rings. */ struct ringeffect { MONST *m; EFFECT *eff; OBJ *o; } ; /* * Return the RING * for an OBJ * (or, if the OBJ isn't a ring, panic). */ static RING *ring_for_obj(OBJ *o) { if (objtypes[o->type].class != OC_RING) panic("non-ring where ring needed"); return(o->private); } /* * Apply the effect for a ring (most rings). */ static void apply_ring(MONST *m, void *rev) { RINGEFFECT *re; re = rev; switch (re->o->type) { // case OBJ_RING_OF_FIRE_RESISTANCE: case OBJ_RING_OF_PROTECTION: panic("applying ring of protection"); break; case OBJ_RING_OF_REGENERATION: m->heal += 150; break; case OBJ_RING_OF_SEE_INVISIBLE: m->flags |= MF_SEE_INVISIBLE; break; // case OBJ_RING_OF_SLOW_DIGESTION: // case OBJ_RING_OF_STEALTH: // case OBJ_RING_OF_TELEPORT_CONTROL: case OBJ_RING_OF_VAMPIRIC_REGENERATION: m->flags |= MF_VAMP_REGEN; break; } } /* * Tear down the effect for a ring (most rings). */ static void death_ring(MONST *m __attribute__((__unused__)), void *rev) { free(rev); } static EFFECTOPS effops_ring = EFFECTOPS_INIT(ring); int ringtype_has_plusses(int type) { switch (type) { case OBJ_RING_OF_PROTECTION: return(1); break; } return(0); } void ring_remove_all(MONST *m) { int i; for (i=MAXRINGS-1;i>=0;i--) if (m->rings[i]) ring_remove(m,m->rings[i]); } static void ring_prot_unbonus(BONUSROLL *br __attribute__((__unused__))) { } void ring_put(MONST *m, OBJ *o) { RING *r; int i; r = ring_for_obj(o); if (m->rings[MAXRINGS-1]) panic("putting on too many rings"); if (r->wornby) panic("putting on already-worn ring"); for (i=MAXRINGS-1;(i>=0)&&!m->rings[i];i--) ; i ++; pline("using rings[%d]",i); if ((i < 0) || (i >= MAXRINGS) || m->rings[i]) panic("impossible ring slot"); m->rings[i] = o; r->wornby = m; switch (o->type) { case OBJ_RING_OF_PROTECTION: r->prot.roll = add_bonus(r->prot.rollstr,&m->bonus_def,&ring_prot_unbonus); break; default: { RINGEFFECT *e; e = malloc(sizeof(RINGEFFECT)); e->m = m; e->eff = effect_add(m,e,&effops_ring); e->o = o; r->most = e; } break; } } void ring_remove(MONST *m, OBJ *o) { RING *r; int i; r = ring_for_obj(o); if (! r->wornby) panic("removing non-worn ring"); if (r->wornby != m) panic("removing ring from wrong monster"); for (i=MAXRINGS-1;(i>=0)&&(m->rings[i]!=o);i--) ; if (i < 0) panic("worn ring isn't in rings[]"); pline("found in rings[%d]",i); for (;irings[i] = m->rings[i+1]; m->rings[MAXRINGS-1] = 0; r->wornby = 0; switch (o->type) { case OBJ_RING_OF_PROTECTION: if (r->prot.roll) { remove_bonus(r->prot.roll); r->prot.roll = 0; } break; default: effect_remove(m,r->most->eff); free(r->most); r->most = 0; break; } } int ring_being_worn(OBJ *o) { return(!!ring_for_obj(o)->wornby); } /* * The new method for rings. */ static OBJ *new_ring(int type, OBJ *o) { RING *r; r = malloc(sizeof(RING)); r->flags = 0; r->wornby = 0; if (ringtype_has_plusses(type)) { if (onein(3)) { r->plusses = - roll("d20"); r->flags |= RF_CURSED; } else { r->plusses = roll("2d10-1"); } } else { if (onein(5)) r->flags |= RF_CURSED; } o->private = r; return(o); } /* * The old method for rings. */ static void old_ring(OBJ *o) { RING *r; r = ring_for_obj(o); if (r->wornby) panic("freeing worn ring"); free(r); } static int fmt_cond_ring(char ch, INVOBJ *io) { switch (ch) { case 'C': return(ring_for_obj(io->v[0])->flags&RF_CURSED); break; case 'K': return(ring_for_obj(io->v[0])->flags&RF_KNOWN); break; case 'P': return(ringtype_has_plusses(io->v[0]->type)); break; case 'W': return(ring_being_worn(io->v[0])); break; } panic("invalid conditional +%c for ring",ch); } static void fmt_spec_ring(FILE *f, char ch, INVOBJ *io) { switch (ch) { case 'P': if (! ringtype_has_plusses(io->v[0]->type)) panic("invalid ring plusses"); fprintf(f,"%+d",ring_for_obj(io->v[0])->plusses); break; default: panic("invalid format %%+%c for ring",ch); break; } } static int identified_ring(INVOBJ *io) { return((objtypes[io->v[0]->type].flags&OTF_KNOWN)&&(ring_for_obj(io->v[0])->flags&RF_KNOWN)); } static INVOBJ *identify_ring(INVOBJ *io) { objtypes[io->v[0]->type].flags |= OTF_KNOWN; ring_for_obj(io->v[0])->flags |= RF_KNOWN; return(io); } #define format_ring std_format #define collapsible_ring never_collapsible #define identical_ring never_identical #define split_ring std_split #define moved_ring noop_moved OBJOPS objops_ring = OBJOPS_INIT(ring); /* * Inventory test function for ring_show(). */ static int ring_show_test(INVOBJ *io) { if (objtypes[io->v[0]->type].class != OC_RING) return(0); return(!!ring_for_obj(io->v[0])->wornby); } /* * Show what ring(s), if any, the monster is wearing. This really * makes sense only when the monster is the player. */ void ring_show(MONST *m) { show_inventory(&m->invent,"",&ring_show_test,"Not wearing any rings"); } /* * Stop the monster from using the ring. The object must be a ring, * must be carried by the monster, and must not be worn by any other * monster (this last is a can't-happen), but is a no-op if none of * those trip and the ring simply isn't being worn. */ void ring_stop_using(MONST *m, INVOBJ *io) { OBJ *o; o = io->v[0]; if (io->inv != &m->invent) panic("ring_stop_using, not in inventory"); if (! ring_for_obj(o)->wornby) return; ring_remove(m,o); } void ring_set_plusses(OBJ *o, int plus) { if (! ringtype_has_plusses(o->type)) panic("setting nonexistent ring plusses"); ring_for_obj(o)->plusses = plus; } void ring_set_cursed(OBJ *o, int c) { if (c) ring_for_obj(o)->flags |= RF_CURSED; else ring_for_obj(o)->flags &= ~RF_CURSED; }