/** * Describe things that look like lights. */ static bool describe_light(textblock *tb, const struct object *obj, oinfo_detail_t mode) { int rad = 0; bool uses_fuel = false; int refuel_turns = 0; bool terse = mode & OINFO_TERSE ? true : false; if (!obj_known_light(obj, mode, &rad, &uses_fuel, &refuel_turns)) return false; textblock_append(tb, "Radius "); textblock_append_c(tb, COLOUR_L_GREEN, format("%d", rad)); textblock_append(tb, " light."); if (tval_is_light(obj)) { if (!obj->artifact && !uses_fuel) textblock_append(tb, " No fuel required."); if (!terse) { if (refuel_turns) textblock_append(tb, " Refills other lanterns up to %d turns of fuel.", refuel_turns); else textblock_append(tb, " Cannot be refueled."); } } textblock_append(tb, "\n"); return true; }
/** * Determine which equipment slot (if any) an item likes. The slot might (or * might not) be open, but it is a slot which the object could be equipped in. * * For items where multiple slots could work (e.g. rings), the function * will try to return an open slot if possible. */ int wield_slot(const struct object *obj) { /* Slot for equipment */ switch (obj->tval) { case TV_BOW: return slot_by_type(player, EQUIP_BOW, false); case TV_AMULET: return slot_by_type(player, EQUIP_AMULET, false); case TV_CLOAK: return slot_by_type(player, EQUIP_CLOAK, false); case TV_SHIELD: return slot_by_type(player, EQUIP_SHIELD, false); case TV_GLOVES: return slot_by_type(player, EQUIP_GLOVES, false); case TV_BOOTS: return slot_by_type(player, EQUIP_BOOTS, false); } if (tval_is_melee_weapon(obj)) return slot_by_type(player, EQUIP_WEAPON, false); else if (tval_is_ring(obj)) return slot_by_type(player, EQUIP_RING, false); else if (tval_is_light(obj)) return slot_by_type(player, EQUIP_LIGHT, false); else if (tval_is_body_armor(obj)) return slot_by_type(player, EQUIP_BODY_ARMOR, false); else if (tval_is_head_armor(obj)) return slot_by_type(player, EQUIP_HAT, false); /* No slot available */ return (-1); }
/** * Describe charges or charging status for re-usable items with magic effects */ static size_t obj_desc_charges(const struct object *obj, char *buf, size_t max, size_t end, int mode) { bool aware = object_flavor_is_aware(obj) || (mode & ODESC_STORE); /* Wands and Staffs have charges */ if (aware && tval_can_have_charges(obj)) strnfcat(buf, max, &end, " (%d charge%s)", obj->pval, PLURAL(obj->pval)); /* Charging things */ else if (obj->timeout > 0) { if (tval_is_rod(obj) && obj->number > 1) { strnfcat(buf, max, &end, " (%d charging)", number_charging(obj)); } /* Artifacts, single rods */ else if (!(tval_is_light(obj) && !obj->artifact)) { strnfcat(buf, max, &end, " (charging)"); } } return end; }
/** * Gives the known light-sourcey characteristics of the given object. * * Fills in the radius of the light in `rad`, whether it uses fuel and * how many turns light it can refuel in similar items. * * Return false if the object is not known to be a light source (which * includes it not actually being a light source). */ static bool obj_known_light(const struct object *obj, oinfo_detail_t mode, int *rad, bool *uses_fuel, int *refuel_turns) { bitflag flags[OF_SIZE]; bool no_fuel; bool is_light = tval_is_light(obj); get_known_flags(obj, mode, flags); if (!is_light && (obj->modifiers[OBJ_MOD_LIGHT] <= 0)) return false; /* Prevent unidentified objects (especially artifact lights) from showing * bad radius and refueling info. */ if (!object_is_known(obj)) return false; /* Work out radius */ *rad = obj->modifiers[OBJ_MOD_LIGHT]; no_fuel = of_has(flags, OF_NO_FUEL) ? true : false; if (no_fuel || obj->artifact) { *uses_fuel = false; } else { *uses_fuel = true; } if (is_light && of_has(flags, OF_TAKES_FUEL)) { *refuel_turns = z_info->fuel_lamp; } else { *refuel_turns = 0; } return true; }
/** * Describes item `obj` into buffer `buf` of size `max`. * * ODESC_PREFIX prepends a 'the', 'a' or number * ODESC_BASE results in a base description. * ODESC_COMBAT will add to-hit, to-dam and AC info. * ODESC_EXTRA will add pval/charge/inscription/ignore info. * ODESC_PLURAL will pluralise regardless of the number in the stack. * ODESC_STORE turns off ignore markers, for in-store display. * ODESC_SPOIL treats the object as fully identified. * * Setting 'prefix' to TRUE prepends a 'the', 'a' or the number in the stack, * respectively. * * \returns The number of bytes used of the buffer. */ size_t object_desc(char *buf, size_t max, const struct object *obj, int mode) { bool prefix = mode & ODESC_PREFIX ? TRUE : FALSE; bool spoil = mode & ODESC_SPOIL ? TRUE : FALSE; bool terse = mode & ODESC_TERSE ? TRUE : FALSE; size_t end = 0; /* Simple description for null item */ if (!obj) return strnfmt(buf, max, "(nothing)"); /* Egos whose name we know are seen */ if (object_name_is_visible(obj) && obj->ego && !spoil) obj->ego->everseen = TRUE; /*** Some things get really simple descriptions ***/ if (obj->marked == MARK_AWARE) { if (prefix) return strnfmt(buf, max, "an unknown item"); return strnfmt(buf, max, "unknown item"); } if (tval_is_money(obj)) return strnfmt(buf, max, "%d gold pieces worth of %s%s", obj->pval, obj->kind->name, ignore_item_ok(obj) ? " {ignore}" : ""); /** Construct the name **/ /* Copy the base name to the buffer */ end = obj_desc_name(buf, max, end, obj, prefix, mode, spoil, terse); if (mode & ODESC_COMBAT) { if (tval_is_chest(obj)) end = obj_desc_chest(obj, buf, max, end); else if (tval_is_light(obj)) end = obj_desc_light(obj, buf, max, end); end = obj_desc_combat(obj, buf, max, end, spoil); } if (mode & ODESC_EXTRA) { end = obj_desc_mods(obj, buf, max, end, spoil); end = obj_desc_charges(obj, buf, max, end, mode); if (mode & ODESC_STORE) end = obj_desc_aware(obj, buf, max, end); else end = obj_desc_inscrip(obj, buf, max, end); } return end; }
/** * Describe remaining light for refuellable lights */ static size_t obj_desc_light(const struct object *obj, char *buf, size_t max, size_t end) { /* Fuelled light sources get number of remaining turns appended */ if (tval_is_light(obj) && !of_has(obj->flags, OF_NO_FUEL)) strnfcat(buf, max, &end, " (%d turns)", obj->timeout); return end; }
/** * Try to find an ego-item for an object, setting o_ptr->ego if successful and * applying various bonuses. */ static void make_ego_item(struct object *o_ptr, int level) { /* Cannot further improve artifacts or ego items */ if (o_ptr->artifact || o_ptr->ego) return; /* Occasionally boost the generation level of an item */ if (level > 0 && one_in_(z_info->great_ego)) level = 1 + (level * z_info->max_depth / randint1(z_info->max_depth)); /* Try to get a legal ego type for this item */ o_ptr->ego = ego_find_random(o_ptr, level); /* Actually apply the ego template to the item */ if (o_ptr->ego) ego_apply_magic(o_ptr, level); /* Ego lights are always known as such (why? - NRM) */ if (tval_is_light(o_ptr)) id_on(o_ptr->id_flags, ID_EGO_ITEM); return; }
/** * Return the real price of a known (or partly known) item. * * Wand and staffs get cost for each charge. * * Wearable items (weapons, launchers, jewelry, lights, armour) and ammo * are priced according to their power rating. All ammo, and normal (non-ego) * torches are scaled down by AMMO_RESCALER to reflect their impermanence. */ s32b object_value_real(const object_type *obj, int qty, int verbose, bool known) { s32b value, total_value; s32b power; int a = 1; int b = 5; static file_mode pricing_mode = MODE_WRITE; /* Wearables and ammo have prices that vary by individual item properties */ if (tval_has_variable_power(obj)) { char buf[1024]; ang_file *log_file = NULL; /* Logging */ if (verbose) { path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "pricing.log"); log_file = file_open(buf, pricing_mode, FTYPE_TEXT); if (!log_file) { msg("Error - can't open pricing.log for writing."); exit(1); } pricing_mode = MODE_APPEND; } file_putf(log_file, "object is %s\n", obj->kind->name); /* Calculate power and value */ power = object_power(obj, verbose, log_file, known); value = SGN(power) * ((a * power * power) + (b * power)); /* Rescale for expendables */ if ((tval_is_light(obj) && of_has(obj->flags, OF_BURNS_OUT) && !obj->ego) || tval_is_ammo(obj)) { value = value / AMMO_RESCALER; if (value < 1) value = 1; } /* More logging */ file_putf(log_file, "a is %d and b is %d\n", a, b); file_putf(log_file, "value is %d\n", value); if (verbose) { if (!file_close(log_file)) { msg("Error - can't close pricing.log file."); exit(1); } } /* Get the total value */ total_value = value * qty; if (total_value < 0) total_value = 0; } else { /* Worthless items */ if (!obj->kind->cost) return (0L); /* Base cost */ value = obj->kind->cost; /* Analyze the item type and quantity */ if (tval_can_have_charges(obj)) { int charges; total_value = value * qty; /* Calculate number of charges, rounded up */ charges = obj->pval * qty / obj->number; if ((obj->pval * qty) % obj->number != 0) charges++; /* Pay extra for charges, depending on standard number of charges */ total_value += value * charges / 20; } else total_value = value * qty; /* No negative value */ if (total_value < 0) total_value = 0; } /* Return the value */ return (total_value); }
/** * Wield or wear a single item from the pack or floor */ void inven_wield(struct object *obj, int slot) { struct object *wielded, *old = player->body.slots[slot].obj; const char *fmt; char o_name[80]; bool dummy = false; /* Increase equipment counter if empty slot */ if (old == NULL) player->upkeep->equip_cnt++; /* Take a turn */ player->upkeep->energy_use = z_info->move_energy; /* It's either a gear object or a floor object */ if (object_is_carried(player, obj)) { /* Split off a new object if necessary */ if (obj->number > 1) { wielded = gear_object_for_use(obj, 1, false, &dummy); /* The new item needs new gear and known gear entries */ wielded->next = obj->next; obj->next = wielded; wielded->prev = obj; if (wielded->next) (wielded->next)->prev = wielded; wielded->known->next = obj->known->next; obj->known->next = wielded->known; wielded->known->prev = obj->known; if (wielded->known->next) (wielded->known->next)->prev = wielded->known; } else { /* Just use the object directly */ wielded = obj; } } else { /* Get a floor item and carry it */ wielded = floor_object_for_use(obj, 1, false, &dummy); inven_carry(player, wielded, false, false); } /* Wear the new stuff */ player->body.slots[slot].obj = wielded; /* Do any ID-on-wield */ object_learn_on_wield(player, wielded); /* Where is the item now */ if (tval_is_melee_weapon(wielded)) fmt = "You are wielding %s (%c)."; else if (wielded->tval == TV_BOW) fmt = "You are shooting with %s (%c)."; else if (tval_is_light(wielded)) fmt = "Your light source is %s (%c)."; else fmt = "You are wearing %s (%c)."; /* Describe the result */ object_desc(o_name, sizeof(o_name), wielded, ODESC_PREFIX | ODESC_FULL); /* Message */ msgt(MSG_WIELD, fmt, o_name, I2A(slot)); /* Cursed! */ if (wielded->curses) { /* Warn the player */ msgt(MSG_CURSED, "Oops! It feels deathly cold!"); } /* See if we have to overflow the pack */ combine_pack(); pack_overflow(old); /* Recalculate bonuses, torch, mana, gear */ player->upkeep->notice |= (PN_IGNORE); player->upkeep->update |= (PU_BONUS | PU_INVEN); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP | PR_ARMOR); player->upkeep->redraw |= (PR_STATS | PR_HP | PR_MANA | PR_SPEED); /* Disable repeats */ cmd_disable_repeat(); }
/** * Wipe an object clean and make it a standard object of the specified kind. */ void object_prep(struct object *obj, struct object_kind *k, int lev, aspect rand_aspect) { int i; /* Clean slate */ memset(obj, 0, sizeof(*obj)); /* Assign the kind and copy across data */ obj->kind = k; obj->tval = k->tval; obj->sval = k->sval; obj->ac = k->ac; obj->dd = k->dd; obj->ds = k->ds; obj->weight = k->weight; obj->effect = k->effect; obj->time = k->time; /* Default number */ obj->number = 1; /* Copy flags */ of_copy(obj->flags, k->base->flags); of_copy(obj->flags, k->flags); /* Assign modifiers */ for (i = 0; i < OBJ_MOD_MAX; i++) obj->modifiers[i] = randcalc(k->modifiers[i], lev, rand_aspect); /* Assign charges (wands/staves only) */ if (tval_can_have_charges(obj)) obj->pval = randcalc(k->charge, lev, rand_aspect); /* Assign pval for food, oil and launchers */ if (tval_is_edible(obj) || tval_is_potion(obj) || tval_is_fuel(obj) || tval_is_launcher(obj)) obj->pval = randcalc(k->pval, lev, rand_aspect); /* Default fuel */ if (tval_is_light(obj)) { if (of_has(obj->flags, OF_BURNS_OUT)) obj->timeout = z_info->fuel_torch; else if (of_has(obj->flags, OF_TAKES_FUEL)) obj->timeout = z_info->default_lamp; } /* Default magic */ obj->to_h = randcalc(k->to_h, lev, rand_aspect); obj->to_d = randcalc(k->to_d, lev, rand_aspect); obj->to_a = randcalc(k->to_a, lev, rand_aspect); /* Default slays and brands */ copy_slay(&obj->slays, k->slays); copy_brand(&obj->brands, k->brands); /* Default resists */ for (i = 0; i < ELEM_MAX; i++) { obj->el_info[i].res_level = k->el_info[i].res_level; obj->el_info[i].flags = k->el_info[i].flags; obj->el_info[i].flags |= k->base->el_info[i].flags; } }
/** * Determine if an item can "absorb" a second item * * See "object_absorb()" for the actual "absorption" code. * * If permitted, we allow weapons/armor to stack, if "known". * * Missiles will combine if both stacks have the same "known" status. * This is done to make unidentified stacks of missiles useful. * * Food, potions, scrolls, and "easy know" items always stack. * * Chests, and activatable items, except rods, never stack (for various * reasons). */ bool object_stackable(const struct object *obj1, const struct object *obj2, object_stack_t mode) { int i; /* Equipment items don't stack */ if (object_is_equipped(player->body, obj1)) return false; if (object_is_equipped(player->body, obj2)) return false; /* If either item is unknown, do not stack */ if (mode & OSTACK_LIST && obj1->kind != obj1->known->kind) return false; if (mode & OSTACK_LIST && obj2->kind != obj2->known->kind) return false; /* Hack -- identical items cannot be stacked */ if (obj1 == obj2) return false; /* Require identical object kinds */ if (obj1->kind != obj2->kind) return false; /* Different flags don't stack */ if (!of_is_equal(obj1->flags, obj2->flags)) return false; /* Different elements don't stack */ for (i = 0; i < ELEM_MAX; i++) { if (obj1->el_info[i].res_level != obj2->el_info[i].res_level) return false; if ((obj1->el_info[i].flags & (EL_INFO_HATES | EL_INFO_IGNORE)) != (obj2->el_info[i].flags & (EL_INFO_HATES | EL_INFO_IGNORE))) return false; } /* Artifacts never stack */ if (obj1->artifact || obj2->artifact) return false; /* Analyze the items */ if (tval_is_chest(obj1)) { /* Chests never stack */ return false; } else if (tval_is_edible(obj1) || tval_is_potion(obj1) || tval_is_scroll(obj1) || tval_is_rod(obj1)) { /* Food, potions, scrolls and rods all stack nicely, since the kinds are identical, either both will be aware or both will be unaware */ } else if (tval_can_have_charges(obj1) || tval_is_money(obj1)) { /* Gold, staves and wands stack most of the time */ /* Too much gold or too many charges */ if (obj1->pval + obj2->pval > MAX_PVAL) return false; /* ... otherwise ok */ } else if (tval_is_weapon(obj1) || tval_is_armor(obj1) || tval_is_jewelry(obj1) || tval_is_light(obj1)) { bool obj1_is_known = object_fully_known((struct object *)obj1); bool obj2_is_known = object_fully_known((struct object *)obj2); /* Require identical values */ if (obj1->ac != obj2->ac) return false; if (obj1->dd != obj2->dd) return false; if (obj1->ds != obj2->ds) return false; /* Require identical bonuses */ if (obj1->to_h != obj2->to_h) return false; if (obj1->to_d != obj2->to_d) return false; if (obj1->to_a != obj2->to_a) return false; /* Require all identical modifiers */ for (i = 0; i < OBJ_MOD_MAX; i++) if (obj1->modifiers[i] != obj2->modifiers[i]) return (false); /* Require identical ego-item types */ if (obj1->ego != obj2->ego) return false; /* Require identical curses */ if (!curses_are_equal(obj1->curses, obj2->curses)) return false; /* Hack - Never stack recharging wearables ... */ if ((obj1->timeout || obj2->timeout) && !tval_is_light(obj1)) return false; /* ... and lights must have same amount of fuel */ else if ((obj1->timeout != obj2->timeout) && tval_is_light(obj1)) return false; /* Prevent unIDd items stacking with IDd items in the object list */ if (mode & OSTACK_LIST && (obj1_is_known != obj2_is_known)) return false; } else { /* Anything else probably okay */ } /* Require compatible inscriptions */ if (obj1->note && obj2->note && (obj1->note != obj2->note)) return false; /* They must be similar enough */ return true; }
/** * Determine the ignore level of an object, which is similar to its pseudo. * * The main point is when the value is undetermined given current info, * return the maximum possible value. */ byte ignore_level_of(const struct object *obj) { byte value = 0; bitflag f[OF_SIZE], f2[OF_SIZE]; int i; bool negative_mod = FALSE; object_flags_known(obj, f); /* Deal with jewelry specially. */ if (tval_is_jewelry(obj)) { /* CC: average jewelry has at least one known positive modifier */ for (i = 0; i < OBJ_MOD_MAX; i++) if ((object_this_mod_is_visible(obj, i)) && (obj->modifiers[i] > 0)) return IGNORE_AVERAGE; if ((obj->to_h > 0) || (obj->to_d > 0) || (obj->to_a > 0)) return IGNORE_AVERAGE; if ((object_attack_plusses_are_visible(obj) && ((obj->to_h < 0) || (obj->to_d < 0))) || (object_defence_plusses_are_visible(obj) && obj->to_a < 0)) return IGNORE_BAD; return IGNORE_AVERAGE; } /* And lights */ if (tval_is_light(obj)) { create_mask(f2, TRUE, OFID_WIELD, OFT_MAX); if (of_is_inter(f, f2)) return IGNORE_ALL; if ((obj->to_h > 0) || (obj->to_d > 0) || (obj->to_a > 0)) return IGNORE_GOOD; if ((obj->to_h < 0) || (obj->to_d < 0) || (obj->to_a < 0)) return IGNORE_BAD; return IGNORE_AVERAGE; } /* We need to redefine "bad" * At the moment we use "all modifiers known and negative" */ for (i = 0; i < OBJ_MOD_MAX; i++) { if (!object_this_mod_is_visible(obj, i) || (obj->modifiers[i] > 0)) break; if (obj->modifiers[i] < 0) negative_mod = TRUE; } if ((i == OBJ_MOD_MAX) && negative_mod) return IGNORE_BAD; if (object_was_sensed(obj)) { obj_pseudo_t pseudo = object_pseudo(obj); switch (pseudo) { case INSCRIP_AVERAGE: { value = IGNORE_AVERAGE; break; } case INSCRIP_EXCELLENT: { /* have to assume splendid until you have tested it */ if (object_was_worn(obj)) { if (object_high_resist_is_possible(obj)) value = IGNORE_EXCELLENT_NO_SPL; else value = IGNORE_EXCELLENT_NO_HI; } else { value = IGNORE_ALL; } break; } case INSCRIP_SPLENDID: value = IGNORE_ALL; break; case INSCRIP_NULL: case INSCRIP_SPECIAL: value = IGNORE_MAX; break; /* This is the interesting case */ case INSCRIP_STRANGE: case INSCRIP_MAGICAL: { value = IGNORE_GOOD; if ((object_attack_plusses_are_visible(obj) || randcalc_valid(obj->kind->to_h, obj->to_h) || randcalc_valid(obj->kind->to_d, obj->to_d)) && (object_defence_plusses_are_visible(obj) || randcalc_valid(obj->kind->to_a, obj->to_a))) { int isgood = is_object_good(obj); if (isgood > 0) { value = IGNORE_GOOD; } else if (isgood < 0) { value = IGNORE_BAD; } else { value = IGNORE_AVERAGE; } } break; } default: /* do not handle any other possible pseudo values */ assert(0); } } else { if (object_was_worn(obj)) value = IGNORE_EXCELLENT_NO_SPL; /* object would be sensed if it were splendid */ else if (object_is_known_not_artifact(obj)) value = IGNORE_ALL; else value = IGNORE_MAX; } return value; }
/** * Wield or wear a single item from the pack or floor */ void inven_wield(struct object *obj, int slot) { struct object *wielded, *old = player->body.slots[slot].obj; const char *fmt; char o_name[80]; /* Increase equipment counter if empty slot */ if (old == NULL) player->upkeep->equip_cnt++; /* Take a turn */ player->upkeep->energy_use = z_info->move_energy; /* Split off a new object if necessary */ if (obj->number > 1) { /* Split off a new single object */ wielded = object_split(obj, 1); /* If it's a gear object, give the split item a list entry */ if (pile_contains(player->gear, obj)) { wielded->next = obj->next; obj->next = wielded; wielded->prev = obj; if (wielded->next) (wielded->next)->prev = wielded; } } else wielded = obj; /* Carry floor items, don't allow combining */ if (square_holds_object(cave, player->py, player->px, wielded)) { square_excise_object(cave, player->py, player->px, wielded); inven_carry(player, wielded, FALSE, FALSE); } /* Wear the new stuff */ player->body.slots[slot].obj = wielded; /* Do any ID-on-wield */ object_notice_on_wield(wielded); /* Where is the item now */ if (tval_is_melee_weapon(wielded)) fmt = "You are wielding %s (%c)."; else if (wielded->tval == TV_BOW) fmt = "You are shooting with %s (%c)."; else if (tval_is_light(wielded)) fmt = "Your light source is %s (%c)."; else fmt = "You are wearing %s (%c)."; /* Describe the result */ object_desc(o_name, sizeof(o_name), wielded, ODESC_PREFIX | ODESC_FULL); /* Message */ msgt(MSG_WIELD, fmt, o_name, I2A(slot)); /* Cursed! */ if (cursed_p(wielded->flags)) { /* Warn the player */ msgt(MSG_CURSED, "Oops! It feels deathly cold!"); /* Sense the object */ object_notice_curses(wielded); } /* See if we have to overflow the pack */ combine_pack(); pack_overflow(old); /* Recalculate bonuses, torch, mana, gear */ player->upkeep->notice |= (PN_IGNORE); player->upkeep->update |= (PU_BONUS | PU_INVEN); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP | PR_ARMOR); player->upkeep->redraw |= (PR_STATS | PR_HP | PR_MANA | PR_SPEED); /* Disable repeats */ cmd_disable_repeat(); }