/** * Describe slays and brands on weapons */ static bool describe_brands(textblock *tb, const struct object *obj) { struct brand *known_brands = brand_collect(obj, NULL, TRUE); struct brand *b; if (!known_brands) return FALSE; if (tval_is_weapon(obj) || tval_is_fuel(obj)) textblock_append(tb, "Branded with "); else textblock_append(tb, "It brands your melee attacks with "); b = known_brands; while (b) { if (b->multiplier < 3) textblock_append(tb, "weak "); textblock_append(tb, b->name); if (b->next) textblock_append(tb, ", "); else textblock_append(tb, ".\n"); b = b->next; } free_brand(known_brands); return TRUE; }
/** * Describe slays and brands on weapons */ static bool describe_slays(textblock *tb, const struct object *obj) { struct slay *known_slays = slay_collect(obj, NULL, TRUE); struct slay *s; if (!known_slays) return FALSE; if (tval_is_weapon(obj) || tval_is_fuel(obj)) textblock_append(tb, "Slays "); else textblock_append(tb, "It causes your melee attacks to slay "); s = known_slays; while (s) { textblock_append(tb, s->name); if (s->multiplier > 3) textblock_append(tb, " (powerfully)"); if (s->next) textblock_append(tb, ", "); else textblock_append(tb, ".\n"); s = s->next; } free_slay(known_slays); return TRUE; }
/** * Describe slays and brands on weapons */ static bool describe_brands(textblock *tb, const struct object *obj) { int i, count = 0; bool *b = obj->known->brands; if (!b) return false; if (tval_is_weapon(obj) || tval_is_fuel(obj)) textblock_append(tb, "Branded with "); else textblock_append(tb, "It brands your melee attacks with "); for (i = 1; i < z_info->brand_max; i++) { if (b[i]) { count++; } } assert(count >= 1); for (i = 1; i < z_info->brand_max; i++) { if (!b[i]) continue; if (brands[i].multiplier < 3) textblock_append(tb, "weak "); textblock_append(tb, brands[i].name); if (count > 1) textblock_append(tb, ", "); else textblock_append(tb, ".\n"); count--; } return true; }
/** * Describe slays and brands on weapons */ static bool describe_slays(textblock *tb, const struct object *obj) { int i, count = 0; bool *s = obj->known->slays; if (!s) return false; if (tval_is_weapon(obj) || tval_is_fuel(obj)) textblock_append(tb, "Slays "); else textblock_append(tb, "It causes your melee attacks to slay "); for (i = 1; i < z_info->slay_max; i++) { if (s[i]) { count++; } } assert(count >= 1); for (i = 1; i < z_info->slay_max; i++) { if (!s[i]) continue; textblock_append(tb, slays[i].name); if (slays[i].multiplier > 3) textblock_append(tb, " (powerfully)"); if (count > 1) textblock_append(tb, ", "); else textblock_append(tb, ".\n"); count--; } return true; }
/** * 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; }
/** * Describe combat properties of an item - damage dice, to-hit, to-dam, armor * class, missile multipler */ static size_t obj_desc_combat(const struct object *obj, char *buf, size_t max, size_t end, bool spoil) { bitflag flags_known[OF_SIZE]; object_flags_known(obj, flags_known); if (kf_has(obj->kind->kind_flags, KF_SHOW_DICE)) { /* Only display the real damage dice if the combat stats are known */ if (spoil || object_attack_plusses_are_visible(obj)) strnfcat(buf, max, &end, " (%dd%d)", obj->dd, obj->ds); else strnfcat(buf, max, &end, " (%dd%d)", obj->kind->dd, obj->kind->ds); } if (kf_has(obj->kind->kind_flags, KF_SHOW_MULT)) { /* Display shooting power as part of the multiplier */ if ((obj->modifiers[OBJ_MOD_MIGHT] > 0) && (spoil || object_this_mod_is_visible(obj, OBJ_MOD_MIGHT))) strnfcat(buf, max, &end, " (x%d)", obj->pval + obj->modifiers[OBJ_MOD_MIGHT]); else strnfcat(buf, max, &end, " (x%d)", obj->pval); } /* Show weapon bonuses */ if (spoil || object_attack_plusses_are_visible(obj)) { if (tval_is_weapon(obj) || obj->to_d || obj->to_h) { /* Make an exception for body armor with only a to-hit penalty */ if (obj->to_h < 0 && obj->to_d == 0 && tval_is_body_armor(obj)) strnfcat(buf, max, &end, " (%+d)", obj->to_h); /* Otherwise, always use the full tuple */ else strnfcat(buf, max, &end, " (%+d,%+d)", obj->to_h, obj->to_d); } } /* Show armor bonuses */ if (spoil || object_defence_plusses_are_visible(obj)) { if (obj_desc_show_armor(obj)) strnfcat(buf, max, &end, " [%d,%+d]", obj->ac, obj->to_a); else if (obj->to_a) strnfcat(buf, max, &end, " [%+d]", obj->to_a); } else if (obj_desc_show_armor(obj)) strnfcat(buf, max, &end, " [%d]", object_was_sensed(obj) ? obj->ac : obj->kind->ac); return end; }
/** * Describe slays and brands on weapons */ static bool describe_brands(textblock *tb, const struct object *obj) { struct brand *b = obj->known->brands; if (!b) return false; if (tval_is_weapon(obj) || tval_is_fuel(obj)) textblock_append(tb, "Branded with "); else textblock_append(tb, "It brands your melee attacks with "); while (b) { if (b->multiplier < 3) textblock_append(tb, "weak "); textblock_append(tb, b->name); if (b->next) textblock_append(tb, ", "); else textblock_append(tb, ".\n"); b = b->next; } return true; }
/** * Describe slays and brands on weapons */ static bool describe_slays(textblock *tb, const struct object *obj) { struct slay *s = obj->known->slays; if (!s) return false; if (tval_is_weapon(obj) || tval_is_fuel(obj)) textblock_append(tb, "Slays "); else textblock_append(tb, "It causes your melee attacks to slay "); while (s) { textblock_append(tb, s->name); if (s->multiplier > 3) textblock_append(tb, " (powerfully)"); if (s->next) textblock_append(tb, ", "); else textblock_append(tb, ".\n"); s = s->next; } return true; }
/** * 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); }
/** * Applying magic to an object, which includes creating ego-items, and applying * random bonuses, * * The `good` argument forces the item to be at least `good`, and the `great` * argument does likewise. Setting `allow_artifacts` to true allows artifacts * to be created here. * * If `good` or `great` are not set, then the `lev` argument controls the * quality of item. * * Returns 0 if a normal object, 1 if a good object, 2 if an ego item, 3 if an * artifact. */ int apply_magic(struct object *obj, int lev, bool allow_artifacts, bool good, bool great, bool extra_roll) { int i; s16b power = 0; /* Chance of being `good` and `great` */ /* This has changed over the years: * 3.0.0: good = MIN(75, lev + 10); great = MIN(20, lev / 2); * 3.3.0: good = (lev + 2) * 3; great = MIN(lev / 4 + lev, 50); * 3.4.0: good = (2 * lev) + 5 * 3.4 was in between 3.0 and 3.3, 3.5 attempts to keep the same * area under the curve as 3.4, but make the generation chances * flatter. This depresses good items overall since more items * are created deeper. * This change is meant to go in conjunction with the changes * to ego item allocation levels. (-fizzix) */ int good_chance = (33 + lev); int great_chance = 30; /* Roll for "good" */ if (good || (randint0(100) < good_chance)) { power = 1; /* Roll for "great" */ if (great || (randint0(100) < great_chance)) power = 2; } /* Roll for artifact creation */ if (allow_artifacts) { int rolls = 0; /* Get one roll if excellent */ if (power >= 2) rolls = 1; /* Get two rolls if forced great */ if (great) rolls = 2; /* Give some extra rolls for uniques and acq scrolls */ if (extra_roll) rolls += 2; /* Roll for artifacts if allowed */ for (i = 0; i < rolls; i++) if (make_artifact(obj)) return 3; } /* Try to make an ego item */ if (power == 2) make_ego_item(obj, lev); /* Apply magic */ if (tval_is_weapon(obj)) { apply_magic_weapon(obj, lev, power); } else if (tval_is_armor(obj)) { apply_magic_armour(obj, lev, power); } else if (tval_is_ring(obj)) { if (obj->sval == lookup_sval(obj->tval, "Speed")) { /* Super-charge the ring */ while (one_in_(2)) obj->modifiers[OBJ_MOD_SPEED]++; } } else if (tval_is_chest(obj)) { /* Hack -- skip ruined chests */ if (obj->kind->level > 0) { /* Hack -- pick a "difficulty" */ obj->pval = randint1(obj->kind->level); /* Never exceed "difficulty" of 55 to 59 */ if (obj->pval > 55) obj->pval = (s16b)(55 + randint0(5)); } } /* Apply minima from ego items if necessary */ ego_apply_minima(obj); return power; }
/** * 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; }