/** * Remove an amount of an object from the inventory or quiver, returning * a detached object which can be used. * * Optionally describe what remains. */ struct object *gear_object_for_use(struct object *obj, int num, bool message) { struct object *usable; char name[80]; char label = gear_to_label(obj); bool artifact = obj->artifact && (object_is_known(obj) || object_name_is_visible(obj)); /* Bounds check */ num = MIN(num, obj->number); /* Prepare a name if necessary */ if (message) { /* Artifacts */ if (artifact) object_desc(name, sizeof(name), obj, ODESC_FULL | ODESC_SINGULAR); else { /* Describe as if it's already reduced */ obj->number -= num; object_desc(name, sizeof(name), obj, ODESC_PREFIX | ODESC_FULL); obj->number += num; } } /* Split off a usable object if necessary */ if (obj->number > num) { usable = object_split(obj, num); } else { /* We're using the entire stack */ usable = obj; gear_excise_object(usable); /* Stop tracking item */ if (tracked_object_is(player->upkeep, obj)) track_object(player->upkeep, NULL); /* Inventory has changed, so disable repeat command */ cmd_disable_repeat(); } /* Change the weight */ player->upkeep->total_weight -= (num * obj->weight); /* Housekeeping */ player->upkeep->update |= (PU_BONUS); player->upkeep->notice |= (PN_COMBINE); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); /* Print a message if desired */ if (message) { if (artifact) msg("You no longer have the %s (%c).", name, label); else msg("You have %s (%c).", name, label); } return usable; }
/** * Drop (some of) a non-cursed inventory/equipment item "near" the current * location * * There are two cases here - a single object or entire stack is being dropped, * or part of a stack is being split off and dropped */ void inven_drop(struct object *obj, int amt) { int py = player->py; int px = player->px; struct object *dropped; char o_name[80]; char label; /* Error check */ if (amt <= 0) return; /* Check it is still held, in case there were two drop commands queued * for this item. This is in theory not ideal, but in practice should * be safe. */ if (!pile_contains(player->gear, obj)) return; /* Get where the object is now */ label = gear_to_label(obj); /* Not too many */ if (amt > obj->number) amt = obj->number; /* Take off equipment */ if (object_is_equipped(player->body, obj)) inven_takeoff(obj); /* Get the object */ dropped = gear_object_for_use(obj, amt, TRUE); /* Describe the dropped object */ object_desc(o_name, sizeof(o_name), dropped, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You drop %s (%c).", o_name, label); /* Drop it near the player */ drop_near(cave, dropped, 0, py, px, FALSE); event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); }
/** * Drop (some of) a non-cursed inventory/equipment item "near" the current * location * * There are two cases here - a single object or entire stack is being dropped, * or part of a stack is being split off and dropped */ void inven_drop(struct object *obj, int amt) { int py = player->py; int px = player->px; struct object *dropped; char o_name[80]; /* Error check */ if (amt <= 0) return; /* This should not happen - ask for report */ if (!pile_contains(player->gear, obj)) { /* Describe the dropped object */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); msg("Bug - attempt to drop %s when not held!", o_name); return; } /* Not too many */ if (amt > obj->number) amt = obj->number; /* Take off equipment */ if (object_is_equipped(player->body, obj)) inven_takeoff(obj); /* Get the object */ dropped = gear_object_for_use(obj, amt, TRUE); /* Describe the dropped object */ object_desc(o_name, sizeof(o_name), dropped, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You drop %s (%c).", o_name, gear_to_label(obj)); /* Drop it near the player */ drop_near(cave, dropped, 0, py, px, FALSE); event_signal(EVENT_INVENTORY); }
/** * Destroys a type of item on a given percent chance. * The chance 'cperc' is in hundredths of a percent (1-in-10000) * Note that missiles are no longer necessarily all destroyed * * Returns number of items destroyed. */ int inven_damage(struct player *p, int type, int cperc) { int j, k, amt; struct object *obj = p->gear; char o_name[80]; bool damage; /* No chance means no damage */ if (cperc <= 0) return 0; /* Count the casualties */ k = 0; /* Scan through the gear */ while (obj) { struct object *next = obj->next; if (object_is_equipped(p->body, obj)) { obj = next; continue; } /* Hack -- for now, skip artifacts */ if (obj->artifact) { obj = next; continue; } /* Give this item slot a shot at death if it is vulnerable */ if ((obj->el_info[type].flags & EL_INFO_HATES) && !(obj->el_info[type].flags & EL_INFO_IGNORE)) { /* Chance to destroy this item */ int chance = cperc; /* Track if it is damaged instead of destroyed */ damage = false; /* Analyze the type to see if we just damage it * - we also check for rods to reduce chance */ if (tval_is_weapon(obj) && !tval_is_ammo(obj)) { /* Chance to damage it */ if (randint0(10000) < cperc) { /* Damage the item */ obj->to_h--; if (p->obj_k->to_h) obj->known->to_h = obj->to_h; obj->to_d--; if (p->obj_k->to_d) obj->known->to_d = obj->to_d; /* Damaged! */ damage = true; } else { obj = next; continue; } } else if (tval_is_armor(obj)) { /* Chance to damage it */ if (randint0(10000) < cperc) { /* Damage the item */ obj->to_a--; if (p->obj_k->to_a) obj->known->to_a = obj->to_a; /* Damaged! */ damage = true; } else { obj = next; continue; } } else if (tval_is_rod(obj)) { chance = (chance / 4); } /* Damage instead of destroy */ if (damage) { p->upkeep->update |= (PU_BONUS); p->upkeep->redraw |= (PR_EQUIP); /* Casualty count */ amt = obj->number; } else /* ... or count the casualties */ for (amt = j = 0; j < obj->number; ++j) if (randint0(10000) < chance) amt++; /* Some casualities */ if (amt) { struct object *destroyed; bool none_left = false; /* Get a description */ object_desc(o_name, sizeof(o_name), obj, ODESC_BASE); /* Message */ msgt(MSG_DESTROY, "%sour %s (%c) %s %s!", ((obj->number > 1) ? ((amt == obj->number) ? "All of y" : (amt > 1 ? "Some of y" : "One of y")) : "Y"), o_name, gear_to_label(obj), ((amt > 1) ? "were" : "was"), (damage ? "damaged" : "destroyed")); /* Damage already done? */ if (damage) continue; /* Destroy "amt" items */ destroyed = gear_object_for_use(obj, amt, false, &none_left); if (destroyed->known) object_delete(&destroyed->known); object_delete(&destroyed); /* Count the casualties */ k += amt; } } obj = next; } /* Return the casualty count */ return (k); }
/** * Drop (some of) a non-cursed inventory/equipment item "near" the current * location * * There are two cases here - a single object or entire stack is being dropped, * or part of a stack is being split off and dropped */ void inven_drop(struct object *obj, int amt) { int py = player->py; int px = player->px; struct object *dropped; bool none_left = false; bool quiver = false; char name[80]; char label; /* Error check */ if (amt <= 0) return; /* Check it is still held, in case there were two drop commands queued * for this item. This is in theory not ideal, but in practice should * be safe. */ if (!object_is_carried(player, obj)) return; /* Get where the object is now */ label = gear_to_label(obj); /* Is it in the quiver? */ if (object_is_in_quiver(player, obj)) quiver = true; /* Not too many */ if (amt > obj->number) amt = obj->number; /* Take off equipment, don't combine */ if (object_is_equipped(player->body, obj)) inven_takeoff(obj); /* Get the object */ dropped = gear_object_for_use(obj, amt, false, &none_left); /* Describe the dropped object */ object_desc(name, sizeof(name), dropped, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You drop %s (%c).", name, label); /* Describe what's left */ if (dropped->artifact) { object_desc(name, sizeof(name), dropped, ODESC_FULL | ODESC_SINGULAR); msg("You no longer have the %s (%c).", name, label); } else if (none_left) { /* Play silly games to get the right description */ int number = dropped->number; dropped->number = 0; object_desc(name, sizeof(name), dropped, ODESC_PREFIX | ODESC_FULL); msg("You have %s (%c).", name, label); dropped->number = number; } else { object_desc(name, sizeof(name), obj, ODESC_PREFIX | ODESC_FULL); msg("You have %s (%c).", name, label); } /* Drop it near the player */ drop_near(cave, &dropped, 0, py, px, false); /* Sound for quiver objects */ if (quiver) sound(MSG_QUIVER); event_signal(EVENT_INVENTORY); event_signal(EVENT_EQUIPMENT); }
/** * Add an item to the players inventory. * * If the new item can combine with an existing item in the inventory, * it will do so, using object_similar() and object_absorb(), else, * the item will be placed into the first available gear array index. * * This function can be used to "over-fill" the player's pack, but only * once, and such an action must trigger the "overflow" code immediately. * Note that when the pack is being "over-filled", the new item must be * placed into the "overflow" slot, and the "overflow" must take place * before the pack is reordered, but (optionally) after the pack is * combined. This may be tricky. See "dungeon.c" for info. * * Note that this code removes any location information from the object once * it is placed into the inventory, but takes no responsibility for removing * the object from any other pile it was in. */ void inven_carry(struct player *p, struct object *obj, bool absorb, bool message) { struct object *gear_obj; char o_name[80]; /* Check for combining, if appropriate */ if (absorb) { for (gear_obj = p->gear; gear_obj; gear_obj = gear_obj->next) { /* Can't stack equipment */ if (object_is_equipped(p->body, gear_obj)) continue; /* Check if the two items can be combined */ if (object_similar(gear_obj, obj, OSTACK_PACK)) { /* Increase the weight */ p->upkeep->total_weight += (obj->number * obj->weight); /* Combine the items, and their known versions */ object_absorb(gear_obj->known, obj->known); obj->known = NULL; object_absorb(gear_obj, obj); /* Describe the combined object */ object_desc(o_name, sizeof(o_name), gear_obj, ODESC_PREFIX | ODESC_FULL); /* Recalculate bonuses */ p->upkeep->update |= (PU_BONUS | PU_INVEN); /* Redraw stuff */ p->upkeep->redraw |= (PR_INVEN); /* Inventory will need updating */ update_stuff(player); /* Optionally, display a message */ if (message) msg("You have %s (%c).", o_name, gear_to_label(gear_obj)); /* Sound for quiver objects */ if (object_is_in_quiver(p, gear_obj)) sound(MSG_QUIVER); /* Success */ return; } } } /* Paranoia */ assert(pack_slots_used(p) <= z_info->pack_size); /* Add to the end of the list */ gear_insert_end(obj); /* Apply an autoinscription */ apply_autoinscription(obj); /* Remove cave object details */ obj->held_m_idx = 0; obj->iy = obj->ix = 0; obj->known->iy = obj->known->ix = 0; /* Update the inventory */ p->upkeep->total_weight += (obj->number * obj->weight); p->upkeep->update |= (PU_BONUS | PU_INVEN); p->upkeep->notice |= (PN_COMBINE); p->upkeep->redraw |= (PR_INVEN); /* Inventory will need updating */ update_stuff(player); /* Hobbits ID mushrooms on pickup, gnomes ID wands and staffs on pickup */ if (!object_flavor_is_aware(obj)) { if (player_has(player, PF_KNOW_MUSHROOM) && tval_is_mushroom(obj)) { object_flavor_aware(obj); msg("Mushrooms for breakfast!"); } else if (player_has(player, PF_KNOW_ZAPPER) && tval_is_zapper(obj)) object_flavor_aware(obj); } /* Optionally, display a message */ if (message) { /* Describe the object */ object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You have %s (%c).", o_name, gear_to_label(obj)); } /* Sound for quiver objects */ if (object_is_in_quiver(p, obj)) sound(MSG_QUIVER); }
/** * Write a character dump */ void write_character_dump(ang_file *fff) { int i, x, y; int a; wchar_t c; struct store *home = &stores[STORE_HOME]; struct object **home_list = mem_zalloc(sizeof(struct object *) * z_info->store_inven_max); char o_name[80]; char buf[1024]; char *p; /* Begin dump */ file_putf(fff, " [%s Character Dump]\n\n", buildid); /* Display player */ display_player(0); /* Dump part of the screen */ for (y = 1; y < 23; y++) { p = buf; /* Dump each row */ for (x = 0; x < 79; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ p += wctomb(p, c); } /* Back up over spaces */ while ((p > buf) && (p[-1] == ' ')) --p; /* Terminate */ *p = '\0'; /* End the row */ file_putf(fff, "%s\n", buf); } /* Skip a line */ file_putf(fff, "\n"); /* Display player */ display_player(1); /* Dump part of the screen */ for (y = 11; y < 20; y++) { p = buf; /* Dump each row */ for (x = 0; x < 39; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ p += wctomb(p, c); } /* Back up over spaces */ while ((p > buf) && (p[-1] == ' ')) --p; /* Terminate */ *p = '\0'; /* End the row */ file_putf(fff, "%s\n", buf); } /* Skip a line */ file_putf(fff, "\n"); /* Dump part of the screen */ for (y = 11; y < 20; y++) { p = buf; /* Dump each row */ for (x = 0; x < 39; x++) { /* Get the attr/char */ (void)(Term_what(x + 40, y, &a, &c)); /* Dump it */ p += wctomb(p, c); } /* Back up over spaces */ while ((p > buf) && (p[-1] == ' ')) --p; /* Terminate */ *p = '\0'; /* End the row */ file_putf(fff, "%s\n", buf); } /* Skip some lines */ file_putf(fff, "\n\n"); /* If dead, dump last messages -- Prfnoff */ if (player->is_dead) { i = messages_num(); if (i > 15) i = 15; file_putf(fff, " [Last Messages]\n\n"); while (i-- > 0) { file_putf(fff, "> %s\n", message_str((s16b)i)); } file_putf(fff, "\nKilled by %s.\n\n", player->died_from); } /* Dump the equipment */ file_putf(fff, " [Character Equipment]\n\n"); for (i = 0; i < player->body.count; i++) { struct object *obj = slot_object(player, i); if (!obj) continue; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", gear_to_label(obj), o_name); object_info_chardump(fff, obj, 5, 72); } file_putf(fff, "\n\n"); /* Dump the inventory */ file_putf(fff, "\n\n [Character Inventory]\n\n"); for (i = 0; i < z_info->pack_size; i++) { struct object *obj = player->upkeep->inven[i]; if (!obj) break; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", gear_to_label(obj), o_name); object_info_chardump(fff, obj, 5, 72); } file_putf(fff, "\n\n"); /* Dump the quiver */ file_putf(fff, "\n\n [Character Quiver]\n\n"); for (i = 0; i < z_info->quiver_size; i++) { struct object *obj = player->upkeep->quiver[i]; if (!obj) continue; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", gear_to_label(obj), o_name); object_info_chardump(fff, obj, 5, 72); } file_putf(fff, "\n\n"); /* Dump the Home -- if anything there */ store_stock_list(home, home_list, z_info->store_inven_max); if (home->stock_num) { /* Header */ file_putf(fff, " [Home Inventory]\n\n"); /* Dump all available items */ for (i = 0; i < z_info->store_inven_max; i++) { struct object *obj = home_list[i]; if (!obj) break; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", I2A(i), o_name); object_info_chardump(fff, obj, 5, 72); } /* Add an empty line */ file_putf(fff, "\n\n"); } /* Dump character history */ dump_history(fff); file_putf(fff, "\n\n"); /* Dump options */ file_putf(fff, " [Options]\n\n"); /* Dump options */ for (i = 0; i < OP_MAX; i++) { int opt; const char *title = ""; switch (i) { case OP_INTERFACE: title = "User interface"; break; case OP_BIRTH: title = "Birth"; break; default: continue; } file_putf(fff, " [%s]\n\n", title); for (opt = 0; opt < OPT_MAX; opt++) { if (option_type(opt) != i) continue; file_putf(fff, "%-45s: %s (%s)\n", option_desc(opt), player->opts.opt[opt] ? "yes" : "no ", option_name(opt)); } /* Skip some lines */ file_putf(fff, "\n"); } mem_free(home_list); }
/** * Add an item to the players inventory. * * If the new item can combine with an existing item in the inventory, * it will do so, using object_similar() and object_absorb(), else, * the item will be placed into the first available gear array index. * * This function can be used to "over-fill" the player's pack, but only * once, and such an action must trigger the "overflow" code immediately. * Note that when the pack is being "over-filled", the new item must be * placed into the "overflow" slot, and the "overflow" must take place * before the pack is reordered, but (optionally) after the pack is * combined. This may be tricky. See "dungeon.c" for info. * * Note that this code removes any location information from the object once * it is placed into the inventory, but takes no responsibility for removing * the object from any other pile it was in. */ void inven_carry(struct player *p, struct object *obj, bool absorb, bool message) { bool combining = false; /* Check for combining, if appropriate */ if (absorb) { struct object *combine_item = NULL; struct object *gear_obj = p->gear; while (combine_item == false && gear_obj) { if (!object_is_equipped(p->body, gear_obj) && object_similar(gear_obj, obj, OSTACK_PACK)) { combine_item = gear_obj; } gear_obj = gear_obj->next; } if (combine_item) { /* Increase the weight */ p->upkeep->total_weight += (obj->number * obj->weight); /* Combine the items, and their known versions */ object_absorb(combine_item->known, obj->known); obj->known = NULL; object_absorb(combine_item, obj); obj = combine_item; combining = true; } } /* We didn't manage the find an object to combine with */ if (!combining) { /* Paranoia */ assert(pack_slots_used(p) <= z_info->pack_size); gear_insert_end(obj); apply_autoinscription(obj); /* Remove cave object details */ obj->held_m_idx = 0; obj->grid = loc(0, 0); obj->known->grid = loc(0, 0); /* Update the inventory */ p->upkeep->total_weight += (obj->number * obj->weight); p->upkeep->notice |= (PN_COMBINE); /* Hobbits ID mushrooms on pickup, gnomes ID wands and staffs on pickup */ if (!object_flavor_is_aware(obj)) { if (player_has(player, PF_KNOW_MUSHROOM) && tval_is_mushroom(obj)) { object_flavor_aware(obj); msg("Mushrooms for breakfast!"); } else if (player_has(player, PF_KNOW_ZAPPER) && tval_is_zapper(obj)) object_flavor_aware(obj); } } p->upkeep->update |= (PU_BONUS | PU_INVEN); p->upkeep->redraw |= (PR_INVEN); update_stuff(player); if (message) { char o_name[80]; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); msg("You have %s (%c).", o_name, gear_to_label(obj)); } if (object_is_in_quiver(p, obj)) sound(MSG_QUIVER); }