Mercurial > hg > early-roguelike
view arogue7/scrolls.c @ 296:000b1c5b8d63
UltraRogue: fix inventory collision after save and restore.
Inventory letters are based on "identifiers" stored in objects' o_ident
field. Identifiers are allocated by get_ident(), which keeps a list of
objects that have them, to avoid giving the same identifier to multiple
objects.
The list is not stored in the savefile, so after restore, get_ident()
was not aware of existing identifiers. This resulted in picked-up
objects having the same inventory letters as objects restored from the
file.
The restore code now adds all objects with identifiers to the list.
author | John "Elwin" Edwards |
---|---|
date | Mon, 15 Jan 2018 20:20:35 -0500 |
parents | f9ef86cf22b2 |
children | e52a8a7ad4c5 |
line wrap: on
line source
/* * scrolls.c - Functions for dealing with scrolls * * Advanced Rogue * Copyright (C) 1984, 1985, 1986 Michael Morgan, Ken Dalka and AT&T * All rights reserved. * * Based on "Rogue: Exploring the Dungeons of Doom" * Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman * All rights reserved. * * See the file LICENSE.TXT for full copyright and licensing information. */ /* * Read a scroll and let it happen * */ #include "curses.h" #include <stdlib.h> #include <ctype.h> #include <string.h> #include "rogue.h" /* * let the hero get rid of some type of monster (but not a UNIQUE!) */ void genocide(void) { register struct linked_list *ip; register struct thing *mp; register struct linked_list *nip; register int num_monst = NUMMONST-NUMUNIQUE-1, /* cannot genocide uniques */ pres_monst=1, num_lines=2*(lines-3); register int which_monst; which_monst = makemonster(FALSE, "Genocide", "wipe out"); if (which_monst <= 0) { msg(""); return; } /* Remove this monster from the present level */ for (ip = mlist; ip; ip = nip) { mp = THINGPTR(ip); nip = next(ip); if (mp->t_index == which_monst) { killed(ip, FALSE, FALSE, TRUE); } } /* Remove from available monsters */ monsters[which_monst].m_normal = FALSE; monsters[which_monst].m_wander = FALSE; mpos = 0; msg("You have wiped out the %s.", monsters[which_monst].m_name); } void read_scroll(int which, int flag, bool is_scroll) { register struct object *obj = NULL, *nobj; register struct linked_list *item, *nitem; register int i,j; register char ch, nch; bool cursed, blessed; blessed = FALSE; cursed = FALSE; item = NULL; if (which < 0) { if (on(player, ISBLIND)) { msg("You can't see to read anything"); return; } if (on(player, ISINWALL)) { msg("You can't see the scroll while inside rock!"); return; } /* This is a scroll or book. */ if (player.t_action != C_READ) { int units; item = get_item(pack, "read", READABLE, FALSE, FALSE); /* * Make certain that it is somethings that we want to read */ if (item == NULL) return; /* How long does it take to read? */ units = usage_time(item); if (units < 0) return; player.t_using = item; /* Remember what it is */ player.t_no_move = units * movement(&player); if ((OBJPTR(item))->o_type == SCROLL) player.t_action = C_READ; else player.t_action = C_USE; return; } /* We have waited our time, let's quaff the potion */ item = player.t_using; player.t_using = NULL; player.t_action = A_NIL; obj = OBJPTR(item); /* remove it from the pack */ inpack--; detach(pack, item); msg("As you read the scroll, it vanishes."); cursed = (obj->o_flags & ISCURSED) != 0; blessed = (obj->o_flags & ISBLESSED) != 0; which = obj->o_which; } else { cursed = flag & ISCURSED; blessed = flag & ISBLESSED; } switch (which) { case S_CONFUSE: /* * Scroll of monster confusion. Give him that power. */ msg("Your hands begin to glow red"); turn_on(player, CANHUH); when S_CURING: /* * A cure disease spell */ if (on(player, HASINFEST) || on(player, HASDISEASE)|| on(player, DOROT)) { if (on(player, HASDISEASE)) { extinguish(cure_disease); cure_disease(); } if (on(player, HASINFEST)) { msg(terse ? "You feel yourself improving." : "You begin to feel yourself improving again."); turn_off(player, HASINFEST); infest_dam = 0; } if (on(player, DOROT)) { msg("You feel your skin returning to normal."); turn_off(player, DOROT); } } else { msg(nothing); break; } if (is_scroll) s_know[S_CURING] = TRUE; when S_LIGHT: if (blue_light(blessed, cursed) && is_scroll) s_know[S_LIGHT] = TRUE; when S_HOLD: if (cursed) { /* * This scroll aggravates all the monsters on the current * level and sets them running towards the hero */ aggravate(TRUE, TRUE); msg("You hear a high pitched humming noise."); } else if (blessed) { /* Hold all monsters on level */ if (mlist == NULL) msg(nothing); else { register struct linked_list *mon; register struct thing *th; for (mon = mlist; mon != NULL; mon = next(mon)) { th = THINGPTR(mon); turn_off(*th, ISRUN); turn_on(*th, ISHELD); } msg("A sudden peace comes over the dungeon."); } } else { /* * Hold monster scroll. Stop all monsters within two spaces * from chasing after the hero. */ register int x,y; register struct linked_list *mon; bool gotone=FALSE; for (x = hero.x-2; x <= hero.x+2; x++) { for (y = hero.y-2; y <= hero.y+2; y++) { if (y < 1 || x < 0 || y > lines - 3 || x > cols - 1) continue; if (isalpha(mvwinch(mw, y, x))) { if ((mon = find_mons(y, x)) != NULL) { register struct thing *th; gotone = TRUE; th = THINGPTR(mon); turn_off(*th, ISRUN); turn_on(*th, ISHELD); } } } } if (gotone) msg("A sudden peace surrounds you."); else msg(nothing); } when S_SLEEP: /* * if cursed, you fall asleep */ if (is_scroll) s_know[S_SLEEP] = TRUE; if (cursed) { if (ISWEARING(R_ALERT)) msg("You feel drowsy for a moment."); else { msg("You fall asleep."); player.t_no_move += movement(&player)*(4 + rnd(SLEEPTIME)); player.t_action = A_FREEZE; } } else { /* * sleep monster scroll. * puts all monsters within 2 spaces asleep */ register int x,y; register struct linked_list *mon; bool gotone=FALSE; for (x = hero.x-2; x <= hero.x+2; x++) { for (y = hero.y-2; y <= hero.y+2; y++) { if (y < 1 || x < 0 || y > lines - 3 || x > cols - 1) continue; if (isalpha(mvwinch(mw, y, x))) { if ((mon = find_mons(y, x)) != NULL) { register struct thing *th; th = THINGPTR(mon); if (on(*th, ISUNDEAD)) continue; th->t_no_move += movement(th)*(SLEEPTIME+4); th->t_action = A_FREEZE; gotone = TRUE; } } } } if (gotone) msg("The monster(s) around you seem to have fallen asleep"); else msg(nothing); } when S_CREATE: /* * Create a monster * First look in a circle around him, next try his room * otherwise give up */ creat_mons(&player, (short) 0, TRUE); light(&hero); when S_IDENT: /* * if its blessed then identify everything in the pack */ if (blessed) { msg("You feel more Knowledgeable!"); idenpack(); } else { /* * Identify, let the rogue figure something out */ if (is_scroll && s_know[S_IDENT] != TRUE) { msg("This scroll is an identify scroll"); } whatis(NULL); } if (is_scroll) s_know[S_IDENT] = TRUE; when S_MAP: /* * Scroll of magic mapping. */ waddstr(msgw, morestr); clearok(msgw, FALSE); draw(msgw); wait_for(' '); msg(""); overwrite(stdscr, hw); /* * Take all the things we want to keep hidden out of the window */ for (i = 1; i < lines-2; i++) for (j = 0; j < cols; j++) { switch (nch = ch = CCHAR( mvwinch(hw, i, j) )) { case SECRETDOOR: nch = secretdoor (i, j); break; case '-': case '|': case DOOR: case PASSAGE: case ' ': case STAIRS: if (mvwinch(mw, i, j) != ' ') { register struct thing *it; it = THINGPTR(find_mons(i, j)); if (it && it->t_oldch == ' ') it->t_oldch = nch; } break; default: nch = ' '; } if (nch != ch) waddch(hw, nch); } /* * Copy in what he has discovered */ overlay(cw, hw); /* * And set up for display */ overwrite(hw, cw); draw(cw); if (is_scroll && s_know[S_MAP] != TRUE) { msg("Oh, now this scroll has a map on it."); s_know[S_MAP] = TRUE; } when S_GFIND: /* * Scroll of gold detection */ { int gtotal = 0; wclear(hw); for (nitem = lvl_obj; nitem != NULL; nitem = next(nitem)) { nobj = OBJPTR(nitem); if (nobj->o_type == GOLD) { gtotal += nobj->o_count; mvwaddch(hw, nobj->o_pos.y, nobj->o_pos.x, GOLD); } } for (nitem = mlist; nitem != NULL; nitem = next(nitem)) { register struct linked_list *gitem; register struct thing *th; th = THINGPTR(nitem); if (on(*th, NODETECT)) continue; for(gitem = th->t_pack; gitem != NULL; gitem = next(gitem)){ nobj = OBJPTR(gitem); if (nobj->o_type == GOLD) { gtotal += nobj->o_count; mvwaddch(hw, th->t_pos.y, th->t_pos.x, GOLD); } } } if (gtotal) { if (is_scroll) s_know[S_GFIND] = TRUE; waddstr(msgw, morestr); clearok(msgw, FALSE); draw(msgw); wait_for(' '); msg(""); overlay(hw, cw); draw(cw); msg("You begin to feel greedy and you sense gold."); break; } } msg("You begin to feel a pull downward"); when S_TELEP: /* * Scroll of teleportation: * Make him disappear and reappear */ if (cursed) { int old_max = cur_max; turns = (vlevel * 3) * LEVEL; level = nfloors; new_level(NORMLEV); status(TRUE); mpos = 0; msg("You are banished to the lower regions."); if (old_max == cur_max) /* if he's been here, make it harder */ aggravate(TRUE, TRUE); } else if (blessed) { int old_level, much = rnd(4) - 4; old_level = level; if (much != 0) { level += much; if (level < 1) level = 1; mpos = 0; cur_max = level; turns += much*LEVEL; if (turns < 0) turns = 0; new_level(NORMLEV); /* change levels */ if (level == old_level) status(TRUE); msg("You are whisked away to another region."); } } else { teleport(); } if (is_scroll) s_know[S_TELEP] = TRUE; when S_SCARE: /* * A monster will refuse to step on a scare monster scroll * if it is dropped. Thus reading it is a mistake and produces * laughter at the poor rogue's boo boo. */ msg("You hear maniacal laughter in the distance."); when S_REMOVE: if (cursed) { /* curse all player's possessions */ for (nitem = pack; nitem != NULL; nitem = next(nitem)) { nobj = OBJPTR(nitem); if (nobj->o_flags & ISBLESSED) nobj->o_flags &= ~ISBLESSED; else nobj->o_flags |= ISCURSED; } msg("The smell of fire and brimstone fills the air."); } else if (blessed) { for (nitem = pack; nitem != NULL; nitem = next(nitem)) { nobj = OBJPTR(nitem); nobj->o_flags &= ~ISCURSED; } msg("Your pack glistens brightly"); } else { nitem = get_item(pack, "remove the curse on",ALL,FALSE,FALSE); if (nitem != NULL) { nobj = OBJPTR(nitem); nobj->o_flags &= ~ISCURSED; msg("Removed the curse from %s",inv_name(nobj,TRUE)); } } if (is_scroll) s_know[S_REMOVE] = TRUE; when S_PETRIFY: switch (mvinch(hero.y, hero.x)) { case TRAPDOOR: case DARTTRAP: case TELTRAP: case ARROWTRAP: case SLEEPTRAP: case BEARTRAP: { register int i; /* Find the right trap */ for (i=0; i<ntraps && !ce(traps[i].tr_pos, hero); i++); ntraps--; if (!ce(traps[i].tr_pos, hero)) msg("What a strange trap!"); else { while (i < ntraps) { traps[i] = traps[i + 1]; i++; } } } goto pet_message; case DOOR: case SECRETDOOR: case FLOOR: case PASSAGE: pet_message: msg("The dungeon begins to rumble and shake!"); addch(WALL); /* If the player is phased, unphase him */ if (on(player, CANINWALL)) { extinguish(unphase); turn_off(player, CANINWALL); msg("Your dizzy feeling leaves you."); } /* Mark the player as in a wall */ turn_on(player, ISINWALL); break; default: msg(nothing); } when S_GENOCIDE: msg("You have been granted the boon of genocide!--More--"); wait_for(' '); msg(""); genocide(); if (is_scroll) s_know[S_GENOCIDE] = TRUE; when S_PROTECT: { struct linked_list *ll; struct object *lb; bool did_it = FALSE; msg("You are granted the power of protection."); if ((ll=get_item(pack,"protect",PROTECTABLE,FALSE,FALSE)) != NULL) { lb = OBJPTR(ll); mpos = 0; if (cursed) { switch(lb->o_type) { /* ruin it completely */ case RING: if (lb->o_ac > 0) { if (is_current(lb)) { switch (lb->o_which) { case R_ADDWISDOM: pstats.s_wisdom -= lb->o_ac; when R_ADDINTEL: pstats.s_intel -= lb->o_ac; when R_ADDSTR: pstats.s_str -= lb->o_ac; when R_ADDHIT: pstats.s_dext -= lb->o_ac; } } did_it = TRUE; lb->o_ac = 0; } when ARMOR: if (lb->o_ac > 10) { did_it = TRUE; lb->o_ac = 10; } when STICK: if (lb->o_charges > 0) { did_it = TRUE; lb->o_charges = 0; } when WEAPON:if (lb->o_hplus > 0) { did_it = TRUE; lb->o_hplus = 0; } if (lb->o_dplus > 0) { did_it = TRUE; lb->o_dplus = 0; } } if (lb->o_flags & ISPROT) { did_it = TRUE; lb->o_flags &= ~ISPROT; } if (lb->o_flags & ISBLESSED) { did_it = TRUE; lb->o_flags &= ~ISBLESSED; } if (did_it) msg("Your %s glows red for a moment",inv_name(lb,TRUE)); else { msg(nothing); break; } } else { lb->o_flags |= ISPROT; msg("Protected %s.",inv_name(lb,TRUE)); } } if (is_scroll) s_know[S_PROTECT] = TRUE; } when S_MAKEIT: msg("You have been endowed with the power of creation."); if (is_scroll) s_know[S_MAKEIT] = TRUE; create_obj(TRUE, 0, 0); when S_ALLENCH: { struct linked_list *ll; struct object *lb; int howmuch, flags; if (is_scroll && s_know[S_ALLENCH] == FALSE) { msg("You are granted the power of enchantment."); msg("You may enchant anything(weapon,ring,armor,scroll,potion)"); } if ((ll = get_item(pack, "enchant", ALL, FALSE, FALSE)) != NULL) { lb = OBJPTR(ll); lb->o_flags &= ~ISCURSED; if (blessed) { howmuch = 2; flags = ISBLESSED; } else if (cursed) { howmuch = -1; flags = ISCURSED; } else { howmuch = 1; flags = ISBLESSED; } switch(lb->o_type) { case RING: if (lb->o_ac + howmuch > MAXENCHANT) { msg("The enchantment doesn't seem to work!"); break; } lb->o_ac += howmuch; if (lb==cur_ring[LEFT_1] || lb==cur_ring[LEFT_2] || lb==cur_ring[LEFT_3] || lb==cur_ring[LEFT_4] || lb==cur_ring[RIGHT_1] || lb==cur_ring[RIGHT_2] || lb==cur_ring[RIGHT_3] || lb==cur_ring[RIGHT_4]) { switch (lb->o_which) { case R_ADDWISDOM: pstats.s_wisdom += howmuch; when R_ADDINTEL: pstats.s_intel += howmuch; when R_ADDSTR: pstats.s_str += howmuch; when R_ADDHIT: pstats.s_dext += howmuch; } } msg("Enchanted %s.",inv_name(lb,TRUE)); when ARMOR: if ((armors[lb->o_which].a_class - lb->o_ac) + howmuch > MAXENCHANT) { msg("The enchantment doesn't seem to work!"); break; } else lb->o_ac -= howmuch; msg("Enchanted %s.",inv_name(lb,TRUE)); when STICK: lb->o_charges += (howmuch * 10) + rnd(5); if (lb->o_charges < 0) lb->o_charges = 0; if (EQUAL(ws_type[lb->o_which], "staff")) { if (lb->o_charges > 100) lb->o_charges = 100; } else { if (lb->o_charges > 50) lb->o_charges = 50; } msg("Enchanted %s.",inv_name(lb,TRUE)); when WEAPON: if(lb->o_hplus+lb->o_dplus+howmuch > MAXENCHANT * 2){ msg("The enchantment doesn't seem to work!"); break; } if (rnd(100) < 50) lb->o_hplus += howmuch; else lb->o_dplus += howmuch; msg("Enchanted %s.",inv_name(lb,TRUE)); when MM: switch (lb->o_which) { case MM_BRACERS: if (lb->o_ac + howmuch > MAXENCHANT) { msg("The enchantment doesn't seem to work!"); break; } else lb->o_ac += howmuch; msg("Enchanted %s.",inv_name(lb,TRUE)); when MM_PROTECT: if (lb->o_ac + howmuch > MAXENCHANT/2) { msg("The enchantment doesn't seem to work!"); break; } else lb->o_ac += howmuch; msg("Enchanted %s.",inv_name(lb,TRUE)); } lb->o_flags |= flags; when POTION: case SCROLL: default: lb->o_flags |= flags; msg("Enchanted %s.",inv_name(lb,TRUE)); } } if (is_scroll) s_know[S_ALLENCH] = TRUE; if (!is_scroll) { pstats.s_const--; max_stats.s_const--; if (pstats.s_const <= 0) death(D_CONSTITUTION); msg("You feel less healthy now"); } } when S_FINDTRAPS: for (i=0; i<ntraps; i++) { if (!(traps[i].tr_flags & ISFOUND)) { traps[i].tr_flags |= ISFOUND; if (cansee(traps[i].tr_pos.y, traps[i].tr_pos.x)) mvwaddch(cw,traps[i].tr_pos.y,traps[i].tr_pos.x, traps[i].tr_type); } } if (ntraps > 0) { msg("You sense the presence of traps"); if (is_scroll) s_know[S_FINDTRAPS] = TRUE; } else msg(nothing); when S_RUNES: { register struct linked_list *sitem; msg("The scroll explodes in a ball of fire!"); if (on(player, NOFIRE)) { msg("The fire does not seem to affect you"); break; } explode(&player); if (pstats.s_hpt <= 0) death(D_SCROLL); for (sitem = pack; sitem != NULL; sitem = nitem) { nitem = next(sitem); /* in case we delete it */ nobj = OBJPTR(sitem); /* * check for loss of all scrolls and give them * a save versus fire */ if (nobj->o_type == SCROLL && roll(1,20) < 19) { msg("%s burns up!", inv_name(nobj, TRUE)); inpack--; detach(pack, sitem); o_discard(sitem); } } } when S_CHARM: { bool spots[9]; int x, y, spot, count, numcharmed, something, bonus; struct linked_list *item; register struct thing *tp; /* Initialize the places where we look around us */ for (i=0; i<9; i++) spots[i] = FALSE; count = 0; /* No spots tried yet */ numcharmed = 0; /* Nobody charmed yet */ something = 0; /* Nothing has been seen yet */ bonus = 0; /* no bonus yet */ /* Now look around us randomly for a charmee */ while (count < 9) { do { spot = rnd(9); } while (spots[spot] == TRUE); /* We found a place */ count++; spots[spot] = TRUE; y = hero.y - 1 + (spot / 3); x = hero.x - 1 + (spot % 3); /* Be sure to stay on the board! */ if (x < 0 || x >= cols || (y < 1) || (y >= lines - 2)) continue; /* Is there a monster here? */ if (!isalpha(mvwinch(mw, y, x))) continue; /* What kind is it? */ item = find_mons(y, x); if (item == NULL) continue; tp = THINGPTR(item); if (on(*tp,ISCHARMED) || on(*tp,ISUNIQUE) || on(*tp,ISUNDEAD)) continue; /* Will the monster be charmed? */ if (blessed) bonus -= 3; bonus -= (pstats.s_charisma - 13) / 3; if ((player.t_ctype==C_PALADIN || player.t_ctype==C_RANGER) && off(*tp, ISMEAN)) bonus -= 3; if (save(VS_MAGIC, tp, bonus)) continue; /* We got him! */ numcharmed++; /* Let the player know (maybe) */ if ((off(*tp, ISINVIS) || on(player, CANSEE)) && (off(*tp, ISSHADOW) || on(player, CANSEE)) && cansee(y, x)) { if (on(*tp, CANSURPRISE)) { turn_off(*tp, CANSURPRISE); msg("Woah!"); } msg("The eyes of %s glaze over!", prname(monster_name(tp), FALSE)); something++; } /* Charm him and turn off any side effects */ turn_on(*tp, ISCHARMED); runto(tp, &hero); tp->t_action = A_NIL; /* If monster was suffocating us, stop it */ if (on(*tp, DIDSUFFOCATE)) { turn_off(*tp, DIDSUFFOCATE); extinguish(suffocate); } /* If monster held us, stop it */ if (on(*tp, DIDHOLD) && (--hold_count == 0)) turn_off(player, ISHELD); turn_off(*tp, DIDHOLD); /* If frightened of this monster, stop */ if (on(player, ISFLEE) && player.t_dest == &tp->t_pos) turn_off(player, ISFLEE); if ((blessed && numcharmed >= 2) || numcharmed > 0) break; } if (something == 0) msg(nothing); } otherwise: msg("What a puzzling scroll!"); return; } look(TRUE, FALSE); /* put the result of the scroll on the screen */ status(FALSE); if (is_scroll && item && s_know[which] && s_guess[which]) { free(s_guess[which]); s_guess[which] = NULL; } else if (is_scroll && !s_know[which] && item && askme && (obj->o_flags & ISKNOW) == 0 && (obj->o_flags & ISPOST) == 0 && s_guess[which] == NULL) { nameitem(item, FALSE); } if (item != NULL) o_discard(item); updpack(TRUE, &player); }