/* * Implementation of potions. */ #include #include #include "mon.h" #include "obj.h" #include "see.h" #include "vars.h" #include "util.h" #include "dice.h" #include "pline.h" #include "fuses.h" #include "debug.h" #include "effect.h" #include "format.h" #include "structs.h" #include "display.h" #include "scrsyms.h" #include "disputil.h" #include "objtypes.h" #include "obj-ring.h" #include "obj-wand.h" #include "mon-@-you.h" #include "obj-potion.h" typedef struct potioneffect POTIONEFFECT; typedef struct clairvoy_state CLAIRVOY_STATE; #define CLAIRVOY_RADIUS 6 /* * Clairvoyance continuing-action private data. */ struct clairvoy_state { int duration; unsigned int attacked; LOC *loc; unsigned char fa[LEV_X][LEV_Y]; char ca[LEV_X][LEV_Y]; } ; /* * A potion effect private data. */ struct potioneffect { MONST *m; EFFECT *eff; int potion; TIME expires; int expfuse; const char *endmsg; } ; /* * Apply a potion effect. */ static void apply_potion(MONST *m, void *ev) { POTIONEFFECT *e; e = ev; switch (e->potion) { // case OBJ_POTION_OF_CLAIRVOYANCE: // case OBJ_POTION_OF_CONFUSION: case OBJ_POTION_OF_FIRE_RESISTANCE: m->effflags |= MEF_RESIST_FIRE; break; // case OBJ_POTION_OF_GIANT_STRENGTH: // case OBJ_POTION_OF_HEROISM: // case OBJ_POTION_OF_INFRAVISION: case OBJ_POTION_OF_INVISIBILITY: m->effflags |= MEF_INVISIBLE; break; // case OBJ_POTION_OF_LEVITATION: // case OBJ_POTION_OF_MEMORY: // case OBJ_POTION_OF_PERCEPTION: case OBJ_POTION_OF_SEE_INVISIBLE: m->effflags |= MEF_SEE_INVISIBLE; break; // case OBJ_POTION_OF_SLEEP: case OBJ_POTION_OF_SPEED: m->speed /= 2; break; case OBJ_POTION_OF_WATER_BREATHING: m->effflags |= MEF_BREATHE_WATER; break; } } /* * Tear a potion effect down prematurely. */ static void death_potion(MONST *m __attribute__((__unused__)), void *ev) { cancelfuse(((POTIONEFFECT *)ev)->expfuse); free(ev); } static EFFECTOPS effops_potion = EFFECTOPS_INIT(potion); /* * Expire a potion effect. */ static void potion_expire(long int liarg __attribute__((__unused__)), void *ev) { POTIONEFFECT *e; e = ev; if ((e->m == you) && e->endmsg) pline("%s",e->endmsg); effect_remove(e->m,e->eff); free(e); } /* * Add a timed potion effect. */ static void add_potion_effect(MONST *m, int ticks, int potion, const char *endmsg) { POTIONEFFECT *e; e = malloc(sizeof(POTIONEFFECT)); e->m = m; e->potion = potion; e->expires = curtime + (ticks * TIMESCALE); e->expfuse = addfuse_abs(&potion_expire,0,e,e->expires); e->endmsg = endmsg; // Null out eff, and set it last, so that apply_potion(), when called // from within effect_add(), finds e set up. e->eff = 0; e->eff = effect_add(m,e,&effops_potion); } /* * Implement potion of amnesia. */ static void potion_of_amnesia(void) { int i; for (i=OBJ__N-1;i>=0;i--) { switch (objtypes[i].class) { case OC_POTION: case OC_SCROLL: case OC_WAND: case OC_RING: case OC_SCARAB: if (! onein(3)) objtypes[i].flags &= ~OTF_KNOWN; break; } } wand_amnesia(); ring_amnesia(); // XXX should we do something for weapons and armour too? } /* * Display the potion-of-clairvoyance display. */ static void clairvoy_display(CLAIRVOY_STATE *s) { int lx; int ly; void saw(LOC *sl) { unsigned char owd; unsigned char wd; int sx; int sy; unsigned char c; if ((sl->type == LOC_WALL) || (sl->type == LOC_SDOOR)) { owd = sl->walldoor; checkwall_from(sl,s->loc); wd = sl->walldoor; } sx = (sl->x - s->loc->x) + (LEV_X / 2); sy = (sl->y - s->loc->y) + (LEV_Y / 2); if (sl->monst && !(sl->monst->baseflags & MBF_INVISIBLE) && !(sl->monst->effflags & MEF_INVISIBLE)) { c = sl->monst->symbol; } else if (sl->objs.inv) { c = objtypes[sl->objs.inv->v[0]->type].sym; } else { c = loc_char(sl,LC_ORDINARY); } if ((sl->type == LOC_WALL) || (sl->type == LOC_SDOOR)) sl->walldoor = owd; s->ca[sx][sy] = c; s->fa[sx][sy] = (sl->flags & LF_VISIBLE) ? DF_LIT : 0; } move(0,0); clrtobot(); showmsgbuf(0); for (lx=-CLAIRVOY_RADIUS;lx<=CLAIRVOY_RADIUS;lx++) { for (ly=-CLAIRVOY_RADIUS;ly<=CLAIRVOY_RADIUS;ly++) { s->ca[lx+(LEV_X/2)][ly+(LEV_Y/2)] = ' '; s->fa[lx+(LEV_X/2)][ly+(LEV_Y/2)] = 0; } } see(s->loc,CLAIRVOY_RADIUS,0,&saw); showdisp2(&s->fa[0],&s->ca[0],0,0); } /* * Potion of clairvoyance action routine. */ static int clairvoy(void *state) { CLAIRVOY_STATE *s; int c; int x; int y; JMP j; LOC *n; do <"move"> { do <"over"> { if (setjmp(j.b)) { pline("Interrupted"); break; } s = state; while <"input"> (1) { if (s->duration < 1) { pline("The clairvoyance runs out..."); break <"over">; } if ((you_were_attacked != s->attacked) && onein(3)) { pline("Your concentration is broken!"); break <"over">; } clairvoy_display(s); move(LEV_Y/2,LEV_X/2); c = tget(); if (c == '\33') { if (confirm("End clairvoyance early?")) break; continue <"input">; } if (dirkey(c,&x,&y,0,0,DK_NOCTL|DK_NOCAPS)) { x += s->loc->x; y += s->loc->y; if (s->loc->on->flags & LVF_WRAPAROUND) { if (x < 0) x += LEV_X; else if (x >= LEV_X) x -= LEV_X; if (y < 0) y += LEV_Y; else if (y >= LEV_Y) y -= LEV_Y; } else { if ((x < 0) || (y < 0) || (x >= LEV_X) || (y >= LEV_Y)) panic("clairvoyance off level"); } n = &s->loc->on->cells[x][y]; } else { switch (c) { case '<': if (s->loc->cavetype != LOC_STAIRS_U) break; if (0) { case '>': if (s->loc->cavetype != LOC_STAIRS_D) break; } n = s->loc->to; if (! n) { pline("Those are the stairs exiting the dungeon."); } else { break <"move">; } break; } beep(); continue <"input">; } if ( (n->type == LOC_CAVE) || ( (n->type == LOC_DOOR) && (n->walldoor & LOC_DOOR_OPEN) ) ) break <"move">; beep(); } } while (0); free(state); set_continuing(0,0,0); return(0); } while (0); s->duration --; s->loc = n; set_continuing(&clairvoy,state,CONT_NODISP); return(1); } /* * Implement potion of clairvoyance. */ static void potion_of_clairvoyance(void) { CLAIRVOY_STATE *s; int x; int y; pline("This is a potion of clairvoyance!"); s = malloc(sizeof(CLAIRVOY_STATE)); s->duration = roll("100+d100"); s->attacked = you_were_attacked; s->loc = you->loc; for (x=LEV_X-1;x>=0;x--) { for (y=LEV_Y-1;y>=0;y--) { s->fa[x][y] = 0; s->ca[x][y] = SYM_OOS; } } (*clairvoy)(s); } /* * Quaff a potion. This is also responsible for destroying the potion, * when appropriate. */ void quaff_it(INVOBJ *io) { OBJ *o; if (io->n > 1) abort(); o = io->v[0]; if (objtypes[o->type].class != OC_POTION) { pline("Quaffing non-potion?"); impossible(); return; } switch (o->type) { case OBJ_POTION_OF_AMNESIA: pline("You seem forgetful..."); potion_of_amnesia(); objtypes[OBJ_POTION_OF_AMNESIA].flags |= OTF_KNOWN; break; case OBJ_POTION_OF_CLAIRVOYANCE: potion_of_clairvoyance(); objtypes[OBJ_POTION_OF_CLAIRVOYANCE].flags |= OTF_KNOWN; break; // case OBJ_POTION_OF_CONFUSION: // case OBJ_POTION_OF_EXTRA_HEALING: case OBJ_POTION_OF_FIRE_RESISTANCE: objtypes[OBJ_POTION_OF_FIRE_RESISTANCE].flags |= OTF_KNOWN; pline("You feel an affinity to fire."); add_potion_effect(you,roll("200+d150"),OBJ_POTION_OF_FIRE_RESISTANCE,"Your affinity to fire fades."); break; // case OBJ_POTION_OF_GAIN_STRENGTH: // case OBJ_POTION_OF_GIANT_STRENGTH: case OBJ_POTION_OF_HEALING: objtypes[OBJ_POTION_OF_HEALING].flags |= OTF_KNOWN; pline("You feel better!"); gainhp(you,roll("10000+d5000")); break; // case OBJ_POTION_OF_HEROISM: // case OBJ_POTION_OF_INFRAVISION: case OBJ_POTION_OF_INVISIBILITY: objtypes[OBJ_POTION_OF_INVISIBILITY].flags |= OTF_KNOWN; pline("You can see right through yourself!"); add_potion_effect(you,roll("100+d75"),OBJ_POTION_OF_INVISIBILITY,"You are opaque again."); break; // case OBJ_POTION_OF_LEVITATION: // case OBJ_POTION_OF_MEMORY: // case OBJ_POTION_OF_PERCEPTION: // case OBJ_POTION_OF_RESTORATION: case OBJ_POTION_OF_SEE_INVISIBLE: objtypes[OBJ_POTION_OF_SEE_INVISIBLE].flags |= OTF_KNOWN; pline("Your vision seems exceptionally sharp!"); add_potion_effect(you,roll("200+d150"),OBJ_POTION_OF_SEE_INVISIBLE,"Your vision returns to normal."); break; // case OBJ_POTION_OF_SLEEP: case OBJ_POTION_OF_SPEED: objtypes[OBJ_POTION_OF_SPEED].flags |= OTF_KNOWN; pline("You seem to be moving faster!"); add_potion_effect(you,roll("20+d10"),OBJ_POTION_OF_SPEED,"You seem to be slowing down."); break; case OBJ_POTION_OF_WATER_BREATHING: objtypes[OBJ_POTION_OF_WATER_BREATHING].flags |= OTF_KNOWN; pline("You seem to breathe very freely."); add_potion_effect(you,roll("200+d150"),OBJ_POTION_OF_WATER_BREATHING,"Your breathing returns to normal."); break; default: objtypes[o->type].flags |= OTF_KNOWN; pline("This potion type is not yet implemented."); break; } inv_remove(io->inv,io); invobjfree(io); } /* * The identical method for potions. */ static int identical_potion(OBJ *o1, OBJ *o2) { return(o1->type==o2->type); } /* * The collapsible method for potions. */ static int collapsible_potion(OBJ *o1, OBJ *o2) { return(identical_potion(o1,o2)); } /* * The identified method for potions. * * knowledge_show() knows that potions use OTF_KNOWN to indicate * knowledge of the colour<->function mapping. */ static int identified_potion(INVOBJ *io) { return(!!(objtypes[io->v[0]->type].flags&OTF_KNOWN)); } /* * The identify method for potions. */ static INVOBJ *identify_potion(INVOBJ *io) { objtypes[io->v[0]->type].flags |= OTF_KNOWN; return(io); } /* * The OBJOPS vector for potions. */ #define new_potion noop_new #define old_potion noop_old #define format_potion std_format #define fmt_cond_potion panic_fmt_cond #define fmt_spec_potion panic_fmt_spec #define split_potion std_split #define moved_potion noop_moved #define cursable_potion 0 #define setcursed_potion setcursed_uncursable OBJOPS objops_potion = OBJOPS_INIT(potion);