/** * Prepare an object `dst` representing `amt` objects, based on an existing * object `src` representing at least `amt` objects. * * Takes care of the charge redistribution concerns of stacked items. */ void object_copy_amt(struct object *dest, struct object *src, int amt) { int charge_time = randcalc(src->time, 0, AVERAGE), max_time; /* Get a copy of the object */ object_copy(dest, src); /* Modify quantity */ dest->number = amt; dest->note = src->note; /* * If the item has charges/timeouts, set them to the correct level * too. We split off the same amount as distribute_charges. */ if (tval_can_have_charges(src)) dest->pval = src->pval * amt / src->number; if (tval_can_have_timeout(src)) { max_time = charge_time * amt; if (src->timeout > max_time) dest->timeout = max_time; else dest->timeout = src->timeout; } }
/** * 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; }
/** * Add player-defined inscriptions or game-defined descriptions */ static size_t obj_desc_inscrip(const struct object *obj, char *buf, size_t max, size_t end) { const char *u[4] = { 0, 0, 0, 0 }; int n = 0; int feel = object_pseudo(obj); bitflag flags_known[OF_SIZE], f2[OF_SIZE]; object_flags_known(obj, flags_known); /* Get inscription */ if (obj->note) u[n++] = quark_str(obj->note); /* Use special inscription, if any */ if (!object_is_known(obj)) { if (feel) { /* cannot tell excellent vs strange vs splendid until wield */ if (!object_was_worn(obj) && obj->ego) u[n++] = "ego"; else u[n++] = inscrip_text[feel]; } else if (tval_can_have_charges(obj) && (obj->pval == 0)) u[n++] = "empty"; else if (object_was_worn(obj)) u[n++] = (tval_is_weapon(obj)) ? "wielded" : "worn"; else if (!object_flavor_is_aware(obj) && object_flavor_was_tried(obj)) u[n++] = "tried"; } /* Note curses */ create_mask(f2, FALSE, OFT_CURSE, OFT_MAX); if (of_is_inter(flags_known, f2)) u[n++] = "cursed"; /* Note ignore */ if (ignore_item_ok(obj)) u[n++] = "ignore"; if (n) { int i; for (i = 0; i < n; i++) { if (i == 0) strnfcat(buf, max, &end, " {"); strnfcat(buf, max, &end, "%s", u[i]); if (i < n-1) strnfcat(buf, max, &end, ", "); } strnfcat(buf, max, &end, "}"); } return end; }
/** * Allow one item to "absorb" another, assuming they are similar. * * The blending of the "note" field assumes that either (1) one has an * inscription and the other does not, or (2) neither has an inscription. * In both these cases, we can simply use the existing note, unless the * blending object has a note, in which case we use that note. * * These assumptions are enforced by the "object_similar()" code. */ static void object_absorb_merge(struct object *obj1, const struct object *obj2) { int total; /* Blend all knowledge */ of_union(obj1->known_flags, obj2->known_flags); of_union(obj1->id_flags, obj2->id_flags); /* Merge inscriptions */ if (obj2->note) obj1->note = obj2->note; /* Combine timeouts for rod stacking */ if (tval_can_have_timeout(obj1)) obj1->timeout += obj2->timeout; /* Combine pvals for wands and staves */ if (tval_can_have_charges(obj1) || tval_is_money(obj1)) { total = obj1->pval + obj2->pval; obj1->pval = total >= MAX_PVAL ? MAX_PVAL : total; } /* Combine origin data as best we can */ if (obj1->origin != obj2->origin || obj1->origin_depth != obj2->origin_depth || obj1->origin_xtra != obj2->origin_xtra) { int act = 2; if (obj1->origin_xtra && obj2->origin_xtra) { monster_race *r_ptr = &r_info[obj1->origin_xtra]; monster_race *s_ptr = &r_info[obj2->origin_xtra]; bool r_uniq = rf_has(r_ptr->flags, RF_UNIQUE) ? TRUE : FALSE; bool s_uniq = rf_has(s_ptr->flags, RF_UNIQUE) ? TRUE : FALSE; if (r_uniq && !s_uniq) act = 0; else if (s_uniq && !r_uniq) act = 1; else act = 2; } switch (act) { /* Overwrite with obj2 */ case 1: { obj1->origin = obj2->origin; obj1->origin_depth = obj2->origin_depth; obj1->origin_xtra = obj2->origin_xtra; } /* Set as "mixed" */ case 2: { obj1->origin = ORIGIN_MIXED; } } } }
/** * Describe the charges on an item in the inventory. */ void inven_item_charges(struct object *obj) { /* Require staff/wand */ if (tval_can_have_charges(obj) && object_flavor_is_aware(obj)) { msg("You have %d charge%s remaining.", obj->pval, PLURAL(obj->pval)); } }
/** * Describe the charges on an item in the inventory. */ void inven_item_charges(struct object *obj) { /* Require staff/wand */ if (!tval_can_have_charges(obj)) return; /* Require known item */ if (!object_flavor_is_aware(obj)) return; /* Print a message */ msg("You have %d charge%s remaining.", obj->pval, (obj->pval != 1) ? "s" : ""); }
/** * Describe the charges on an item on the floor. */ void floor_item_charges(struct object *obj) { /* Require staff/wand */ if (!tval_can_have_charges(obj)) return; /* Require known item */ if (!object_is_known(obj)) return; /* Print a message */ msg("There %s %d charge%s remaining.", (obj->pval != 1) ? "are" : "is", obj->pval, (obj->pval != 1) ? "s" : ""); }
/** * 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); }
/** * Melee effect handler: Drain charges from the player's inventory. */ static void melee_effect_handler_DRAIN_CHARGES(melee_effect_handler_context_t *context) { struct object *obj; struct monster *monster = context->m_ptr; struct player *player = context->p; int tries; int unpower = 0, newcharge; /* Take damage */ take_hit(context->p, context->damage, context->ddesc); /* Find an item */ for (tries = 0; tries < 10; tries++) { /* Pick an item */ obj = context->p->upkeep->inven[randint0(z_info->pack_size)]; /* Skip non-objects */ if (obj == NULL) continue; /* Drain charged wands/staves */ if (tval_can_have_charges(obj)) { /* Charged? */ if (obj->pval) { /* Get number of charge to drain */ unpower = (context->rlev / (obj->kind->level + 2)) + 1; /* Get new charge value, don't allow negative */ newcharge = MAX((obj->pval - unpower),0); /* Remove the charges */ obj->pval = newcharge; } } if (unpower) { int heal = context->rlev * unpower; msg("Energy drains from your pack!"); context->obvious = TRUE; /* Don't heal more than max hp */ heal = MIN(heal, monster->maxhp - monster->hp); /* Heal */ monster->hp += heal; /* Redraw (later) if needed */ if (player->upkeep->health_who == monster) player->upkeep->redraw |= (PR_HEALTH); /* Combine the pack */ player->upkeep->notice |= (PN_COMBINE); /* Redraw stuff */ player->upkeep->redraw |= (PR_INVEN); /* Affect only a single inventory slot */ break; } } }
/** * Determine the price of an object (qty one) in a store. * * store_buying == TRUE means the shop is buying, player selling * == FALSE means the shop is selling, player buying * * This function never lets a shop-keeper lose money in a transaction. * * The "greed" value should exceed 100 when the player is "buying" the * object, and should be less than 100 when the player is "selling" it. * * Hack -- the black market always charges twice as much as it should. */ int price_item(struct store *store, const struct object *obj, bool store_buying, int qty) { int adjust = 100; int price; struct owner *proprietor; if (!store) return 0L; proprietor = store->owner; /* Get the value of the stack of wands, or a single item */ if (tval_can_have_charges(obj)) price = object_value(obj, qty, FALSE); else price = object_value(obj, 1, FALSE); /* Worthless items */ if (price <= 0) return (0L); /* The black market is always a worse deal */ if (store->sidx == STORE_B_MARKET) adjust = 150; /* Shop is buying */ if (store_buying) { /* Set the factor */ adjust = 100 + (100 - adjust); if (adjust > 100) adjust = 100; /* Shops now pay 2/3 of true value */ price = price * 2 / 3; /* Black market sucks */ if (store->sidx == STORE_B_MARKET) price = price / 2; /* Check for no_selling option */ if (OPT(birth_no_selling)) return (0L); } else { /* Recalculate if the player doesn't know the flavour */ if (!obj->kind->aware) { obj->kind->aware = TRUE; if (tval_can_have_charges(obj)) price = object_value(obj, qty, FALSE); else price = object_value(obj, 1, FALSE); obj->kind->aware = FALSE; } /* Black market sucks */ if (store->sidx == STORE_B_MARKET) price = price * 2; } /* Compute the final price (with rounding) */ price = (price * adjust + 50L) / 100L; /* Now convert price to total price for non-wands */ if (!tval_can_have_charges(obj)) price *= qty; /* Now limit the price to the purse limit */ if (store_buying && (price > proprietor->max_cost * qty)) price = proprietor->max_cost * qty; /* Note -- Never become "free" */ if (price <= 0L) return (qty); /* Return the price */ return (price); }
/** * 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; } }
/** * Allow one item to "absorb" another, assuming they are similar. * * The blending of the "note" field assumes that either (1) one has an * inscription and the other does not, or (2) neither has an inscription. * In both these cases, we can simply use the existing note, unless the * blending object has a note, in which case we use that note. * * These assumptions are enforced by the "object_similar()" code. */ static void object_absorb_merge(struct object *obj1, const struct object *obj2) { int total; /* First object gains any extra knowledge from second */ if (obj1->known && obj2->known) { if (obj2->known->effect) obj1->known->effect = obj1->effect; player_know_object(player, obj1); } /* Merge inscriptions */ if (obj2->note) obj1->note = obj2->note; /* Combine timeouts for rod stacking */ if (tval_can_have_timeout(obj1)) obj1->timeout += obj2->timeout; /* Combine pvals for wands and staves */ if (tval_can_have_charges(obj1) || tval_is_money(obj1)) { total = obj1->pval + obj2->pval; obj1->pval = total >= MAX_PVAL ? MAX_PVAL : total; } /* Combine origin data as best we can */ if (obj1->origin != obj2->origin || obj1->origin_depth != obj2->origin_depth || obj1->origin_xtra != obj2->origin_xtra) { int act = 2; if (obj1->origin_xtra && obj2->origin_xtra) { struct monster_race *race1 = &r_info[obj1->origin_xtra]; struct monster_race *race2 = &r_info[obj2->origin_xtra]; bool r1_uniq = rf_has(race1->flags, RF_UNIQUE) ? true : false; bool r2_uniq = rf_has(race2->flags, RF_UNIQUE) ? true : false; if (r1_uniq && !r2_uniq) act = 0; else if (r2_uniq && !r1_uniq) act = 1; else act = 2; } switch (act) { /* Overwrite with obj2 */ case 1: { obj1->origin = obj2->origin; obj1->origin_depth = obj2->origin_depth; obj1->origin_xtra = obj2->origin_xtra; } /* Set as "mixed" */ case 2: { obj1->origin = ORIGIN_MIXED; } } } }
/** * 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; }