/** * Check how many missiles can be put in the quiver without increasing the * number of pack slots used. * * Returns the quantity from a given stack of missiles that can be added. */ static int quiver_absorb_num(const struct object *obj) { int i, quiver_count = 0, space_free = 0; /* Must be ammo */ if (!tval_is_ammo(obj)) return 0; /* Count the current space this object could go into */ for (i = 0; i < z_info->quiver_size; i++) { struct object *quiver_obj = player->upkeep->quiver[i]; if (quiver_obj) { quiver_count += quiver_obj->number; if (object_stackable(quiver_obj, obj, OSTACK_PACK)) space_free += z_info->stack_size - quiver_obj->number; } else { space_free += z_info->stack_size; } } /* No space */ if (!space_free) return 0; /* Check we won't need another pack slot */ quiver_count += z_info->stack_size; while (quiver_count > z_info->stack_size) quiver_count -= z_info->stack_size; /* Return the number, or the number that will fit */ space_free = MIN(space_free, z_info->stack_size - quiver_count); return MIN(obj->number, space_free); }
/** * Calculate the number of pack slots used by the current gear. * * Note that this function does not check that there are adequate slots in the * quiver, just the total quantity of missiles. */ int pack_slots_used(struct player *p) { struct object *obj; int quiver_slots = 0, pack_slots = 0, quiver_ammo = 0; int maxsize = z_info->stack_size; for (obj = p->gear; obj; obj = obj->next) { /* Equipment doesn't count */ if (object_is_equipped(p->body, obj)) continue; /* Check if it could be in the quiver */ if (tval_is_ammo(obj)) if (quiver_slots < z_info->quiver_size) { quiver_slots++; quiver_ammo += obj->number; continue; } /* Count regular slots */ pack_slots++; } /* Full slots */ pack_slots += quiver_ammo / maxsize; /* Plus one for any remainder */ if (quiver_ammo % maxsize) pack_slots++; return pack_slots; }
/** * Check if we have space to put an item in a new quiver slot without * increasing the number of pack slots used */ static bool new_quiver_slot_okay(const object_type *obj) { int i, quiver_count = 0; bool empty_slot = FALSE; /* Must be ammo */ if (!tval_is_ammo(obj)) return FALSE; /* Count the current space */ for (i = 0; i < z_info->quiver_size; i++) { struct object *quiver_obj = player->upkeep->quiver[i]; if (quiver_obj) quiver_count += quiver_obj->number; else empty_slot = TRUE; } /* Check for free quiver slots */ if (!empty_slot) return FALSE; /* Check we won't need another pack slot */ quiver_count = ((quiver_count - 1) % z_info->stack_size) + 1; if (quiver_count + obj->number > z_info->stack_size) return FALSE; /* Good to go */ return TRUE; }
/** * Calculate the number of pack slots used by the current gear. * * Note that this function does not check that there are adequate slots in the * quiver, just the total quantity of missiles. */ int pack_slots_used(struct player *p) { struct object *obj; int quiver_slots = 0; int pack_slots = 0; int quiver_ammo = 0; for (obj = p->gear; obj; obj = obj->next) { /* Equipment doesn't count */ if (!object_is_equipped(p->body, obj)) { /* Check if it could be in the quiver */ if (tval_is_ammo(obj) && quiver_slots < z_info->quiver_size) { quiver_slots++; quiver_ammo += obj->number; } else { /* Count regular slots */ pack_slots++; } } } /* Full slots */ pack_slots += quiver_ammo / z_info->quiver_slot_size; /* Plus one for any remainder */ if (quiver_ammo % z_info->quiver_slot_size) { pack_slots++; } return pack_slots; }
/** * Describe the kind */ static void kind_info(char *buf, size_t buf_len, char *dam, size_t dam_len, char *wgt, size_t wgt_len, int *lev, s32b *val, int k) { struct object_kind *kind = &k_info[k]; struct object *obj = object_new(), *known_obj = object_new(); int i; /* Prepare a fake item */ object_prep(obj, kind, 0, MAXIMISE); /* Cancel bonuses */ for (i = 0; i < OBJ_MOD_MAX; i++) obj->modifiers[i] = 0; obj->to_a = 0; obj->to_h = 0; obj->to_d = 0; /* Level */ (*lev) = kind->level; /* Make known */ object_copy(known_obj, obj); obj->known = known_obj; /* Value */ (*val) = object_value(obj, 1, false); /* Description (too brief) */ if (buf) object_desc(buf, buf_len, obj, ODESC_BASE | ODESC_SPOIL); /* Weight */ if (wgt) strnfmt(wgt, wgt_len, "%3d.%d", obj->weight / 10, obj->weight % 10); /* Hack */ if (!dam) return; /* Misc info */ dam[0] = '\0'; /* Damage */ if (tval_is_ammo(obj) || tval_is_melee_weapon(obj)) strnfmt(dam, dam_len, "%dd%d", obj->dd, obj->ds); else if (tval_is_armor(obj)) strnfmt(dam, dam_len, "%d", obj->ac); object_delete(&known_obj); object_delete(&obj); }
/** * Add launcher bonus for ego ammo, multiply for launcher and rescale */ static int launcher_ammo_damage_power(const object_type *obj, int p) { int ammo_type = 0; if (tval_is_ammo(obj)) { if (obj->tval == TV_ARROW) ammo_type = 1; if (obj->tval == TV_BOLT) ammo_type = 2; if (obj->ego) p += (archery[ammo_type].launch_dam * DAMAGE_POWER / 2); p = p * archery[ammo_type].launch_mult / (2 * MAX_BLOWS); log_obj(format("After multiplying ammo and rescaling, power is %d\n", p)); } return p; }
/** * To damage power */ static int to_damage_power(const object_type *obj) { int p; p = (obj->to_d * DAMAGE_POWER / 2); if (p) log_obj(format("%d power from to_dam\n", p)); /* Add second lot of damage power for non-weapons */ if ((wield_slot(obj) != slot_by_name(player, "shooting")) && !tval_is_melee_weapon(obj) && !tval_is_ammo(obj)) { int q = (obj->to_d * DAMAGE_POWER); p += q; if (q) log_obj(format("Add %d from non-weapon to_dam, total %d\n", q, p)); } return p; }
/** * Damage dice power or equivalent */ static int damage_dice_power(const object_type *obj) { int dice = 0; /* Add damage from dice for any wieldable weapon or ammo */ if (tval_is_melee_weapon(obj) || tval_is_ammo(obj)) { dice = (obj->dd * (obj->ds + 1) * DAMAGE_POWER / 4); log_obj(format("Add %d power for damage dice, ", dice)); } else if (wield_slot(obj) != slot_by_name(player, "shooting")) { /* Add power boost for nonweapons with combat flags */ if (obj->brands || obj->slays || (obj->modifiers[OBJ_MOD_BLOWS] > 0) || (obj->modifiers[OBJ_MOD_SHOTS] > 0) || (obj->modifiers[OBJ_MOD_MIGHT] > 0)) { dice = (WEAP_DAMAGE * DAMAGE_POWER); log_obj(format("Add %d power for non-weapon combat bonuses, ", dice)); } } return dice; }
/** * Apply magic to a weapon. */ static void apply_magic_weapon(struct object *obj, int level, int power) { if (power <= 0) return; obj->to_h += randint1(5) + m_bonus(5, level); obj->to_d += randint1(5) + m_bonus(5, level); if (power > 1) { obj->to_h += m_bonus(10, level); obj->to_d += m_bonus(10, level); if (tval_is_melee_weapon(obj) || tval_is_ammo(obj)) { /* Super-charge the damage dice */ while ((obj->dd * obj->ds > 0) && one_in_(10L * obj->dd * obj->ds)) obj->dd++; /* But not too high */ if (obj->dd > 9) obj->dd = 9; } } }
/** * 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); }
/** * 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); }