Mercurial > hg > early-roguelike
view urogue/things.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 | c495a4f288c6 |
children | 5a94c9b3181e |
line wrap: on
line source
/* things.c - functions for dealing with things like potions and scrolls UltraRogue: The Ultimate Adventure in the Dungeons of Doom Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong All rights reserved. Based on "Advanced Rogue" Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka 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. */ #include <string.h> #include <ctype.h> #include "rogue.h" /* inv_name() return the name of something as it would appear in an inventory. */ char * inv_name(struct object *obj, int lowercase) { char *pb; char buf[1024]; switch(obj->o_type) { case SCROLL: if (obj->o_count == 1) sprintf(prbuf, "A %s%sscroll ", obj->o_flags & CANRETURN ? "claimed " : "", blesscurse(obj->o_flags)); else sprintf(prbuf, "%d %s%sscrolls ", obj->o_count, obj->o_flags & CANRETURN ? "claimed " : "", blesscurse(obj->o_flags)); pb = &prbuf[strlen(prbuf)]; if (know_items[TYP_SCROLL][obj->o_which]) sprintf(pb, "of %s", s_magic[obj->o_which].mi_name); else if (guess_items[TYP_SCROLL][obj->o_which]) sprintf(pb, "called %s", guess_items[TYP_SCROLL][obj->o_which]); else sprintf(pb, "titled '%s'", s_names[obj->o_which]); break; case POTION: if (obj->o_count == 1) sprintf(prbuf, "A %s%spotion ", obj->o_flags & CANRETURN ? "claimed " : "", blesscurse(obj->o_flags)); else sprintf(prbuf, "%d %s%spotions ", obj->o_count, obj->o_flags & CANRETURN ? "claimed " : "", blesscurse(obj->o_flags)); pb = &prbuf[strlen(prbuf)]; if (know_items[TYP_POTION][obj->o_which]) sprintf(pb, "of %s(%s)", p_magic[obj->o_which].mi_name, p_colors[obj->o_which]); else if (guess_items[TYP_POTION][obj->o_which]) sprintf(pb, "called %s(%s)", guess_items[TYP_POTION][obj->o_which], p_colors[obj->o_which]); else { if (obj->o_count == 1) sprintf(prbuf, "A%s %s potion", obj->o_flags & CANRETURN ? " claimed" : (char *) vowelstr(p_colors[obj->o_which]), p_colors[obj->o_which]); else sprintf(prbuf, "%d %s%s potions", obj->o_count, obj->o_flags & CANRETURN ? "claimed " : "", (char *) p_colors[obj->o_which]); } break; case FOOD: if (obj->o_count == 1) sprintf(prbuf, "A%s %s", obj->o_flags & CANRETURN ? " claimed" : (char *) vowelstr(fd_data[obj->o_which].mi_name), fd_data[obj->o_which].mi_name); else sprintf(prbuf, "%d %s%ss", obj->o_count, obj->o_flags & CANRETURN ? "claimed " : "", fd_data[obj->o_which].mi_name); break; case WEAPON: if (obj->o_count > 1) sprintf(prbuf, "%d ", obj->o_count); else if ((obj->o_flags & (ISZAPPED | CANRETURN | ISPOISON | ISSILVER | ISTWOH)) || ((obj->o_flags & (ISKNOW | ISZAPPED)) == (ISKNOW | ISZAPPED))) strcpy(prbuf, "A "); else sprintf(prbuf, "A%s ", vowelstr(weaps[obj->o_which].w_name)); pb = &prbuf[strlen(prbuf)]; if ((obj->o_flags & ISKNOW) && (obj->o_flags & ISZAPPED)) sprintf(pb, "charged%s ", charge_str(obj,buf)); pb = &prbuf[strlen(prbuf)]; if (obj->o_flags & CANRETURN) sprintf(pb, "claimed "); pb = &prbuf[strlen(prbuf)]; if (obj->o_flags & ISPOISON) sprintf(pb, "poisoned "); pb = &prbuf[strlen(prbuf)]; if (obj->o_flags & ISSILVER) sprintf(pb, "silver "); if (obj->o_flags & ISTWOH) sprintf(pb, "two-handed "); pb = &prbuf[strlen(prbuf)]; if (obj->o_flags & ISKNOW) sprintf(pb, "%s %s", num(obj->o_hplus, obj->o_dplus, buf), weaps[obj->o_which].w_name); else sprintf(pb, "%s", weaps[obj->o_which].w_name); if (obj->o_count > 1) strcat(prbuf, "s"); break; case ARMOR: if (obj->o_flags & ISKNOW) sprintf(prbuf, "%s%s %s", obj->o_flags & CANRETURN ? "claimed " : "", num(armors[obj->o_which].a_class - obj->o_ac, 0, buf), armors[obj->o_which].a_name); else sprintf(prbuf, "%s%s", obj->o_flags & CANRETURN ? "claimed " : "", armors[obj->o_which].a_name); break; case ARTIFACT: sprintf(prbuf, "the %s", arts[obj->o_which].ar_name); if (obj->o_flags & CANRETURN) strcat(prbuf, " (claimed)"); break; case STICK: sprintf(prbuf, "A %s%s%s ", obj->o_flags & CANRETURN ? "claimed " : "", blesscurse(obj->o_flags), ws_type[obj->o_which]); pb = &prbuf[strlen(prbuf)]; if (know_items[TYP_STICK][obj->o_which]) sprintf(pb, "of %s%s(%s)", ws_magic[obj->o_which].mi_name, charge_str(obj,buf), ws_made[obj->o_which]); else if (guess_items[TYP_STICK][obj->o_which]) sprintf(pb, "called %s(%s)", guess_items[TYP_STICK][obj->o_which], ws_made[obj->o_which]); else sprintf(&prbuf[2], "%s%s %s", obj->o_flags & CANRETURN ? "claimed " : "", ws_made[obj->o_which], ws_type[obj->o_which]); break; case RING: if (know_items[TYP_RING][obj->o_which]) sprintf(prbuf, "A%s%s ring of %s(%s)", obj->o_flags & CANRETURN ? " claimed" : "", ring_num(obj,buf), r_magic[obj->o_which].mi_name, r_stones[obj->o_which]); else if (guess_items[TYP_RING][obj->o_which]) sprintf(prbuf, "A %sring called %s(%s)", obj->o_flags & CANRETURN ? "claimed " : "", guess_items[TYP_RING][obj->o_which], r_stones[obj->o_which]); else sprintf(prbuf, "A%s %s ring", obj->o_flags & CANRETURN ? "claimed " : (char *)vowelstr(r_stones[obj->o_which]), r_stones[obj->o_which]); break; case GOLD: sprintf(prbuf, "%d gold pieces", obj->o_count); break; default: debug("Picked up something funny."); sprintf(prbuf, "Something bizarre %s", unctrl(obj->o_type)); break; } /* Is it marked? */ if (obj->o_mark[0]) { pb = &prbuf[strlen(prbuf)]; sprintf(pb, " <%s>", obj->o_mark); } if (obj == cur_armor) strcat(prbuf, " (being worn)"); if (obj == cur_weapon) strcat(prbuf, " (weapon in hand)"); if (obj == cur_ring[LEFT_1]) strcat(prbuf, " (on left hand)"); else if (obj == cur_ring[LEFT_2]) strcat(prbuf, " (on left hand)"); else if (obj == cur_ring[LEFT_3]) strcat(prbuf, " (on left hand)"); else if (obj == cur_ring[LEFT_4]) strcat(prbuf, " (on left hand)"); else if (obj == cur_ring[LEFT_5]) strcat(prbuf, " (on left hand)"); else if (obj == cur_ring[RIGHT_1]) strcat(prbuf, " (on right hand)"); else if (obj == cur_ring[RIGHT_2]) strcat(prbuf, " (on right hand)"); else if (obj == cur_ring[RIGHT_3]) strcat(prbuf, " (on right hand)"); else if (obj == cur_ring[RIGHT_4]) strcat(prbuf, " (on right hand)"); else if (obj == cur_ring[RIGHT_5]) strcat(prbuf, " (on right hand)"); if (obj->o_flags & ISPROT) strcat(prbuf, " [protected]"); if (lowercase && isupper(prbuf[0])) prbuf[0] = (char) tolower(prbuf[0]); else if (!lowercase && islower(*prbuf)) *prbuf = (char) toupper(*prbuf); if (!lowercase) strcat(prbuf, "."); return(prbuf); } /* rem_obj() Remove an object from the level. */ void rem_obj(struct linked_list *item, int dis) { struct linked_list *llptr; struct object *objptr, *op; int x, y; if (item == NULL) return; detach(lvl_obj, item); objptr = OBJPTR(item); if ( (llptr = objptr->next_obj) != NULL ) { detach((objptr->next_obj), llptr); attach(lvl_obj, llptr); op = OBJPTR(llptr); op->next_obj = objptr->next_obj; if (op->next_obj) objptr->next_obj->l_prev = NULL; y = op->o_pos.y; x = op->o_pos.x; if (cansee(y, x)) mvwaddch(cw, y, x, op->o_type); mvaddch(y, x, op->o_type); } else { y = objptr->o_pos.y; x = objptr->o_pos.x; /* problems if dropped in rock */ mvaddch(y,x,(roomin((objptr->o_pos)) == NULL ? PASSAGE : FLOOR)); } if (dis) discard(item); } /* add_obj() adds an object to the level */ void add_obj(struct linked_list *item, int y, int x) { struct linked_list *llptr; struct object*objptr; llptr = find_obj(y, x); if (llptr) { objptr = OBJPTR(llptr); attach((objptr->next_obj), item); } else { attach(lvl_obj, item); objptr = OBJPTR(item); objptr->next_obj = NULL; objptr->o_pos.y = y; objptr->o_pos.x = x; mvaddch(y, x, objptr->o_type); } } /* drop() put something down */ int drop(struct linked_list *item) { char ch = CCHAR( mvwinch(stdscr, hero.y, hero.x) ); struct object *obj; switch (ch) { case GOLD: case POTION: case SCROLL: case FOOD: case WEAPON: case ARMOR: case RING: case STICK: case FLOOR: case PASSAGE: case POOL: case ARTIFACT: if (item == NULL && (item = get_item("drop", 0)) == NULL) return(FALSE); break; default: msg("You can't drop something here."); return(FALSE); } obj = OBJPTR(item); if (!dropcheck(obj)) return(FALSE); /* Curse a dropped scare monster scroll */ if (obj->o_type == SCROLL && obj->o_which == S_SCARE) { if (obj->o_flags & ISBLESSED) obj->o_flags &= ~ISBLESSED; else obj->o_flags |= ISCURSED; } /* Take it out of the pack */ if (obj->o_count >= 2 && obj->o_group == 0) { struct linked_list *nitem = new_item(sizeof *obj); obj->o_count--; obj = OBJPTR(nitem); *obj = *(OBJPTR(item)); obj->o_count = 1; item = nitem; } else rem_pack(obj); if (ch == POOL) { msg("The pool bubbles briefly as your %s sinks out of sight.", inv_name(obj, TRUE)); discard(item); item = NULL; } else /* Link it into the level object list */ { add_obj(item, hero.y, hero.x); mvaddch(hero.y, hero.x, obj->o_type); } if (obj->o_type == ARTIFACT) has_artifact &= ~(1 << obj->o_which); updpack(); return(TRUE); } /* dropcheck() do special checks for dropping or unweilding|unwearing|unringing */ int dropcheck(struct object *op) { if (op == NULL) return(TRUE); if (op != cur_armor && op != cur_weapon && op != cur_ring[LEFT_1] && op != cur_ring[LEFT_2] && op != cur_ring[LEFT_3] && op != cur_ring[LEFT_4] && op != cur_ring[LEFT_5] && op != cur_ring[RIGHT_1] && op != cur_ring[RIGHT_2] && op != cur_ring[RIGHT_3] && op != cur_ring[RIGHT_4] && op != cur_ring[RIGHT_5]) return(TRUE); if (op->o_flags & ISCURSED) { msg("You can't. It appears to be cursed."); return(FALSE); } if (op == cur_weapon) cur_weapon = NULL; else if (op == cur_armor) { waste_time(); cur_armor = NULL; } else if (op == cur_ring[LEFT_1] || op == cur_ring[LEFT_2] || op == cur_ring[LEFT_3] || op == cur_ring[LEFT_4] || op == cur_ring[LEFT_5] || op == cur_ring[RIGHT_1] || op == cur_ring[RIGHT_2] || op == cur_ring[RIGHT_3] || op == cur_ring[RIGHT_4] || op == cur_ring[RIGHT_5]) { if (op == cur_ring[LEFT_1]) cur_ring[LEFT_1] = NULL; else if (op == cur_ring[LEFT_2]) cur_ring[LEFT_2] = NULL; else if (op == cur_ring[LEFT_3]) cur_ring[LEFT_3] = NULL; else if (op == cur_ring[LEFT_4]) cur_ring[LEFT_4] = NULL; else if (op == cur_ring[LEFT_5]) cur_ring[LEFT_5] = NULL; else if (op == cur_ring[RIGHT_1]) cur_ring[RIGHT_1] = NULL; else if (op == cur_ring[RIGHT_2]) cur_ring[RIGHT_2] = NULL; else if (op == cur_ring[RIGHT_3]) cur_ring[RIGHT_3] = NULL; else if (op == cur_ring[RIGHT_4]) cur_ring[RIGHT_4] = NULL; else if (op == cur_ring[RIGHT_5]) cur_ring[RIGHT_5] = NULL; switch (op->o_which) { case R_ADDSTR: chg_str(-op->o_ac, FALSE, FALSE); break; case R_ADDHIT: chg_dext(-op->o_ac, FALSE, FALSE); break; case R_ADDINTEL: pstats.s_intel -= op->o_ac; break; case R_ADDWISDOM: pstats.s_wisdom -= op->o_ac; break; case R_SEEINVIS: if (find_slot(FUSE_UNSEE,FUSE) == NULL) { turn_off(player, CANSEE); msg("The tingling feeling leaves your eyes."); } light(&hero); mvwaddch(cw, hero.y, hero.x, PLAYER); break; case R_LIGHT: if (roomin(hero) != NULL) { light(&hero); mvwaddch(cw, hero.y, hero.x, PLAYER); } break; } } return(TRUE); } /* new_thing() return a new thing */ struct linked_list * new_thing(void) { int j, k; struct linked_list *item; struct object *cur; short blesschance = srnd(100); short cursechance = srnd(100); item = new_item(sizeof *cur); cur = OBJPTR(item); cur->o_hplus = cur->o_dplus = 0; cur->o_damage = cur->o_hurldmg = "0d0"; cur->o_ac = 11; cur->o_count = 1; cur->o_group = 0; cur->o_flags = 0; cur->o_weight = 0; cur->o_mark[0] = '\0'; /* * Decide what kind of object it will be If we haven't had food for a * while, let it be food. */ switch (no_food > 3 ? TYP_FOOD : pick_one(things, numthings)) { case TYP_POTION: cur->o_type = POTION; cur->o_which = pick_one(p_magic, maxpotions); cur->o_weight = things[TYP_POTION].mi_wght; if (cursechance < p_magic[cur->o_which].mi_curse) cur->o_flags |= ISCURSED; else if (blesschance < p_magic[cur->o_which].mi_bless) cur->o_flags |= ISBLESSED; break; case TYP_SCROLL: cur->o_type = SCROLL; cur->o_which = pick_one(s_magic, maxscrolls); cur->o_weight = things[TYP_SCROLL].mi_wght; if (cursechance < s_magic[cur->o_which].mi_curse) cur->o_flags |= ISCURSED; else if (blesschance < s_magic[cur->o_which].mi_bless) cur->o_flags |= ISBLESSED; break; case TYP_FOOD: no_food = 0; cur->o_type = FOOD; cur->o_which = pick_one(fd_data, maxfoods); cur->o_weight = 2; cur->o_count += extras(); break; case TYP_WEAPON: cur->o_type = WEAPON; cur->o_which = rnd(maxweapons); init_weapon(cur, cur->o_which); if (cursechance < 10) { short bad = (rnd(10) < 1) ? 2 : 1; cur->o_flags |= ISCURSED; cur->o_hplus -= bad; cur->o_dplus -= bad; } else if (blesschance < 15) { short good = (rnd(10) < 1) ? 2 : 1; cur->o_hplus += good; cur->o_dplus += good; } break; case TYP_ARMOR: cur->o_type = ARMOR; for (j = 0; j < maxarmors; j++) if (blesschance < armors[j].a_prob) break; if (j == maxarmors) { debug("Picked a bad armor %d", blesschance); j = 0; } cur->o_which = j; cur->o_ac = armors[j].a_class; if (((k = rnd(100)) < 20) && j != MITHRIL) { cur->o_flags |= ISCURSED; cur->o_ac += rnd(3) + 1; } else if (k < 28 || j == MITHRIL) cur->o_ac -= rnd(3) + 1; if (j == MITHRIL) cur->o_flags |= ISPROT; cur->o_weight = armors[j].a_wght; break; case TYP_RING: cur->o_type = RING; cur->o_which = pick_one(r_magic, maxrings); cur->o_weight = things[TYP_RING].mi_wght; if (cursechance < r_magic[cur->o_which].mi_curse) cur->o_flags |= ISCURSED; else if (blesschance < r_magic[cur->o_which].mi_bless) cur->o_flags |= ISBLESSED; switch (cur->o_which) { case R_ADDSTR: case R_ADDWISDOM: case R_ADDINTEL: case R_PROTECT: case R_ADDHIT: case R_ADDDAM: case R_CARRYING: cur->o_ac = rnd(2) + 1; /* From 1 to 3 */ if (cur->o_flags & ISCURSED) cur->o_ac = -cur->o_ac; if (cur->o_flags & ISBLESSED) cur->o_ac++; break; case R_RESURRECT: case R_TELCONTROL: case R_VREGEN: case R_REGEN: case R_PIETY: case R_WIZARD: cur->o_ac = 0; if (cur->o_flags & ISCURSED) cur->o_ac = -1; if (cur->o_flags & ISBLESSED) cur->o_ac = 1; break; case R_DIGEST: if (cur->o_flags & ISCURSED) cur->o_ac = -1; else if (cur->o_flags & ISBLESSED) cur->o_ac = 2; else cur->o_ac = 1; break; default: cur->o_ac = 0; break; } break; case TYP_STICK: cur->o_type = STICK; cur->o_which = pick_one(ws_magic, maxsticks); fix_stick(cur); if (cursechance < ws_magic[cur->o_which].mi_curse) cur->o_flags |= ISCURSED; else if (blesschance < ws_magic[cur->o_which].mi_bless) cur->o_flags |= ISBLESSED; break; default: debug("Picked a bad kind of object"); wait_for(' '); } return(item); } /* spec_item() provide a new item tailored to specification */ struct linked_list * spec_item(char type, int which, int hitp, int damage) { struct linked_list *item; struct object *obj; item = new_item(sizeof *obj); obj = OBJPTR(item); obj->o_count = 1; obj->o_group = 0; obj->o_type = type; obj->o_which = which; obj->o_damage = obj->o_hurldmg = "0d0"; obj->o_hplus = 0; obj->o_dplus = 0; obj->o_flags = 0; obj->o_mark[0] = '\0'; obj->o_text = NULL; obj->o_launch = 0; obj->o_weight = 0; switch(type) /* Handle special characteristics */ { case WEAPON: init_weapon(obj, which); obj->o_hplus = hitp; obj->o_dplus = damage; obj->o_ac = 10; break; case ARMOR: obj->o_ac = armors[which].a_class - hitp; break; case RING: obj->o_ac = hitp; break; case STICK: fix_stick(obj); obj->o_charges = hitp; break; case GOLD: obj->o_type = GOLD; obj->o_count = GOLDCALC; obj->o_ac = 11; break; } if (hitp > 0 || damage > 0) obj->o_flags |= ISBLESSED; else if (hitp < 0 || damage < 0) obj->o_flags |= ISCURSED; return(item); } /* pick_one() pick an item out of a list of nitems possible magic items */ int pick_one(struct magic_item *magic, int nitems) { int i; struct magic_item *end; struct magic_item *start = magic; for (end = &magic[nitems], i = rnd(1000); magic < end; magic++) if (i <= magic->mi_prob) break; if (magic == end) { if (wizard) { add_line("Bad pick_one: %d from %d items", i, nitems); for (magic = start; magic < end; magic++) add_line("%s: %d%%", magic->mi_name, magic->mi_prob); end_line(); } magic = start; } return((int)(magic - start)); } /* blesscurse() returns whether the object is blessed or cursed */ char * blesscurse(long flags) { if (flags & ISKNOW) { if (flags & ISCURSED) return("cursed ");