/* * Describe stat modifications. */ static bool describe_stats(textblock *tb, const object_type *o_ptr, bitflag flags[MAX_PVALS][OF_SIZE], oinfo_detail_t mode) { const char *descs[N_ELEMENTS(pval_flags)]; size_t count, i; bool full = mode & OINFO_FULL; bool dummy = mode & OINFO_DUMMY; bool search = FALSE; if (!o_ptr->num_pvals && !dummy) return FALSE; for (i = 0; i < o_ptr->num_pvals; i++) { count = info_collect(tb, pval_flags, N_ELEMENTS(pval_flags), flags[i], descs); if (count) { if ((object_this_pval_is_visible(o_ptr, i) || full) && !dummy) textblock_append_c(tb, (o_ptr->pval[i] > 0) ? TERM_L_GREEN : TERM_RED, "%+i ", o_ptr->pval[i]); else textblock_append(tb, "Affects your "); info_out_list(tb, descs, count); } if (of_has(flags[i], OF_SEARCH)) search = TRUE; } if (search) { if ((object_this_pval_is_visible(o_ptr, which_pval(o_ptr, OF_SEARCH)) || full) && !dummy) { textblock_append_c(tb, (o_ptr->pval[which_pval(o_ptr, OF_SEARCH)] > 0) ? TERM_L_GREEN : TERM_RED, "%+i%% ", o_ptr->pval[which_pval(o_ptr, OF_SEARCH)] * 5); textblock_append(tb, "to searching.\n"); } else if (count) textblock_append(tb, "Also affects your searching skill.\n"); else textblock_append(tb, "Affects your searching skill.\n"); } return TRUE; }
static size_t obj_desc_combat(const object_type *o_ptr, char *buf, size_t max, size_t end, bool spoil) { bitflag flags[OF_SIZE]; bitflag flags_known[OF_SIZE]; object_flags(o_ptr, flags); object_flags_known(o_ptr, flags_known); if (of_has(flags, OF_SHOW_DICE)) { /* Only display the real damage dice if the combat stats are known */ if (spoil || object_attack_plusses_are_visible(o_ptr)) strnfcat(buf, max, &end, " (%dd%d)", o_ptr->dd, o_ptr->ds); else strnfcat(buf, max, &end, " (%dd%d)", o_ptr->kind->dd, o_ptr->kind->ds); } if (of_has(flags, OF_SHOW_MULT)) { /* Display shooting power as part of the multiplier */ if (of_has(flags, OF_MIGHT) && (spoil || object_flag_is_known(o_ptr, OF_MIGHT))) strnfcat(buf, max, &end, " (x%d)", (o_ptr->sval % 10) + o_ptr->pval[which_pval(o_ptr, OF_MIGHT)]); else strnfcat(buf, max, &end, " (x%d)", o_ptr->sval % 10); } /* Show weapon bonuses */ if (spoil || object_attack_plusses_are_visible(o_ptr)) { if (wield_slot(o_ptr) == INVEN_WIELD || wield_slot(o_ptr) == INVEN_BOW || obj_is_ammo(o_ptr) || o_ptr->to_d || o_ptr->to_h) { /* Make an exception for body armor with only a to-hit penalty */ if (o_ptr->to_h < 0 && o_ptr->to_d == 0 && (o_ptr->tval == TV_SOFT_ARMOR || o_ptr->tval == TV_HARD_ARMOR || o_ptr->tval == TV_DRAG_ARMOR)) strnfcat(buf, max, &end, " (%+d)", o_ptr->to_h); /* Otherwise, always use the full tuple */ else strnfcat(buf, max, &end, " (%+d,%+d)", o_ptr->to_h, o_ptr->to_d); } } /* Show armor bonuses */ if (spoil || object_defence_plusses_are_visible(o_ptr)) { if (obj_desc_show_armor(o_ptr)) strnfcat(buf, max, &end, " [%d,%+d]", o_ptr->ac, o_ptr->to_a); else if (o_ptr->to_a) strnfcat(buf, max, &end, " [%+d]", o_ptr->to_a); } else if (obj_desc_show_armor(o_ptr)) strnfcat(buf, max, &end, " [%d]", object_was_sensed(o_ptr) ? o_ptr->ac : o_ptr->kind->ac); return end; }
/* * Describe things that look like lights. */ static bool describe_light(textblock *tb, const object_type *o_ptr, const bitflag flags[OF_SIZE], oinfo_detail_t mode) { int rad = 0; bool artifact = o_ptr->artifact ? TRUE : FALSE; bool no_fuel = of_has(flags, OF_NO_FUEL) ? TRUE : FALSE; bool is_light = (o_ptr->tval == TV_LIGHT) ? TRUE : FALSE; bool terse = mode & OINFO_TERSE; if (!is_light && !of_has(flags, OF_LIGHT)) return FALSE; /* Work out radius */ if (of_has(flags, OF_LIGHT)) rad = o_ptr->pval[which_pval(o_ptr, OF_LIGHT)]; /* Describe here */ textblock_append(tb, "Radius "); textblock_append_c(tb, TERM_L_GREEN, format("%d", rad)); if (no_fuel && !artifact) textblock_append(tb, " light. No fuel required."); /* else if (is_light && o_ptr->sval == SV_LIGHT_TORCH) textblock_append(tb, " light, reduced when running out of fuel."); */ else textblock_append(tb, " light."); if (!terse && is_light && !no_fuel && o_ptr->sval != SV_LIGHT_TORCH) { const char *name = (o_ptr->sval == SV_LIGHT_TORCH) ? "torches" : "lanterns"; int turns = (o_ptr->sval == SV_LIGHT_TORCH) ? FUEL_TORCH : FUEL_LAMP; textblock_append(tb, " Refills other %s up to %d turns of fuel.", name, turns); } textblock_append(tb, "\n"); return TRUE; }
/* * Describe combat advantages. */ static bool describe_combat(textblock *tb, const object_type *o_ptr, oinfo_detail_t mode) { bool full = mode & OINFO_FULL; const char *desc[SL_MAX] = { 0 }; int i; int mult[SL_MAX]; int cnt, dam, total_dam, plus = 0; int xtra_postcrit = 0, xtra_precrit = 0; int crit_mult, crit_div, crit_add; int str_plus, dex_plus, old_blows = 0, new_blows, extra_blows; int str_faster = -1, str_done = -1; object_type *bow = &p_ptr->inventory[INVEN_BOW]; bitflag f[OF_SIZE], tmp_f[OF_SIZE], mask[OF_SIZE]; bool weapon = (wield_slot(o_ptr) == INVEN_WIELD); bool ammo = (p_ptr->state.ammo_tval == o_ptr->tval) && (bow->kind); int multiplier = 1; /* Create the "all slays" mask */ create_mask(mask, FALSE, OFT_SLAY, OFT_KILL, OFT_BRAND, OFT_MAX); /* Abort if we've nothing to say */ if (mode & OINFO_DUMMY) return FALSE; if (!weapon && !ammo) { /* Potions can have special text */ if (o_ptr->tval != TV_POTION || o_ptr->dd == 0 || o_ptr->ds == 0 || !object_flavor_is_aware(o_ptr)) return FALSE; textblock_append(tb, "It can be thrown at creatures with damaging effect.\n"); return TRUE; } if (full) { object_flags(o_ptr, f); } else { object_flags_known(o_ptr, f); } textblock_append_c(tb, TERM_L_WHITE, "Combat info:\n"); if (weapon) { /* * Get the player's hypothetical state, were they to be * wielding this item. */ player_state state; int dex_plus_bound; int str_plus_bound; object_type inven[INVEN_TOTAL]; memcpy(inven, p_ptr->inventory, INVEN_TOTAL * sizeof(object_type)); inven[INVEN_WIELD] = *o_ptr; if (full) object_know_all_flags(&inven[INVEN_WIELD]); calc_bonuses(inven, &state, TRUE); dex_plus_bound = STAT_RANGE - state.stat_ind[A_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[A_STR]; dam = ((o_ptr->ds + 1) * o_ptr->dd * 5); xtra_postcrit = state.dis_to_d * 10; if (object_attack_plusses_are_visible(o_ptr)) { xtra_precrit += o_ptr->to_d * 10; plus += o_ptr->to_h; } calculate_melee_crits(&state, o_ptr->weight, plus, &crit_mult, &crit_add, &crit_div); /* Warn about heavy weapons */ if (adj_str_hold[state.stat_ind[A_STR]] < o_ptr->weight / 10) textblock_append_c(tb, TERM_L_RED, "You are too weak to use this weapon.\n"); textblock_append_c(tb, TERM_L_GREEN, "%d.%d ", state.num_blows / 100, (state.num_blows / 10) % 10); textblock_append(tb, "blow%s/round.\n", (state.num_blows > 100) ? "s" : ""); /* Check to see if extra STR or DEX would yield extra blows */ old_blows = state.num_blows; extra_blows = 0; /* First we need to look for extra blows on other items, as * state does not track these */ for (i = INVEN_BOW; i < INVEN_TOTAL; i++) { if (!p_ptr->inventory[i].kind) continue; object_flags_known(&p_ptr->inventory[i], tmp_f); if (of_has(tmp_f, OF_BLOWS)) extra_blows += p_ptr->inventory[i].pval[which_pval(&p_ptr->inventory[i], OF_BLOWS)]; } /* Then we add blows from the weapon being examined */ if (of_has(f, OF_BLOWS)) extra_blows += o_ptr->pval[which_pval(o_ptr, OF_BLOWS)]; /* Then we check for extra "real" blows */ for (dex_plus = 0; dex_plus < dex_plus_bound; dex_plus++) { for (str_plus = 0; str_plus < str_plus_bound; str_plus++) { state.stat_ind[A_STR] += str_plus; state.stat_ind[A_DEX] += dex_plus; new_blows = calc_blows(o_ptr, &state, extra_blows); /* Test to make sure that this extra blow is a * new str/dex combination, not a repeat */ if ((new_blows - new_blows % 10) > (old_blows - old_blows % 10) && (str_plus < str_done || str_done == -1)) { textblock_append(tb, "With +%d STR and +%d DEX you would get %d.%d blows\n", str_plus, dex_plus, (new_blows / 100), (new_blows / 10) % 10); state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; str_done = str_plus; break; } /* If the combination doesn't increment * the displayed blows number, it might still * take a little less energy */ if (new_blows > old_blows && (str_plus < str_faster || str_faster == -1) && (str_plus < str_done || str_done == -1)) { textblock_append(tb, "With +%d STR and +%d DEX you would attack a bit faster\n", str_plus, dex_plus); state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; str_faster = str_plus; continue; } state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; } } } else { int tdis = 6 + 2 * p_ptr->state.ammo_mult; if (object_attack_plusses_are_visible(o_ptr)) plus += o_ptr->to_h; calculate_missile_crits(&p_ptr->state, o_ptr->weight, plus, &crit_mult, &crit_add, &crit_div); /* Calculate damage */ dam = ((o_ptr->ds + 1) * o_ptr->dd * 5); if (object_attack_plusses_are_visible(o_ptr)) dam += (o_ptr->to_d * 10); if (object_attack_plusses_are_visible(bow)) dam += (bow->to_d * 10); /* Apply brands/slays from the shooter to the ammo, but only if known * Note that this is not dependent on mode, so that viewing shop-held * ammo (fully known) does not leak information about launcher */ object_flags_known(bow, tmp_f); of_union(f, tmp_f); textblock_append(tb, "Hits targets up to "); textblock_append_c(tb, TERM_L_GREEN, format("%d", tdis * 10)); textblock_append(tb, " feet away.\n"); } /* Collect slays */ /* Melee weapons get slays and brands from other items now */ if (weapon) { bool nonweap = FALSE; for (i = INVEN_LEFT; i < INVEN_TOTAL; i++) { if (!p_ptr->inventory[i].kind) continue; object_flags_known(&p_ptr->inventory[i], tmp_f); of_inter(tmp_f, mask); /* strip out non-slays */ if (of_union(f, tmp_f)) nonweap = TRUE; } if (nonweap) textblock_append(tb, "This weapon may benefit from one or more off-weapon brands or slays.\n"); } textblock_append(tb, "Average damage/round: "); if (ammo) multiplier = p_ptr->state.ammo_mult; cnt = list_slays(f, mask, desc, NULL, mult, TRUE); for (i = 0; i < cnt; i++) { int melee_adj_mult = ammo ? 0 : 1; /* ammo mult adds fully, melee mult is times 1, so adds 1 less */ /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + mult[i] - melee_adj_mult) + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= p_ptr->state.num_shots; if (total_dam <= 0) textblock_append_c(tb, TERM_L_RED, "%d", 0); else if (total_dam % 10) textblock_append_c(tb, TERM_L_GREEN, "%d.%d", total_dam / 10, total_dam % 10); else textblock_append_c(tb, TERM_L_GREEN, "%d", total_dam / 10); textblock_append(tb, " vs. %s, ", desc[i]); } if (cnt) textblock_append(tb, "and "); /* Include bonus damage in stated average */ total_dam = dam * multiplier + xtra_precrit; total_dam = (total_dam * crit_mult + crit_add) / crit_div; total_dam += xtra_postcrit; if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= p_ptr->state.num_shots; if (total_dam <= 0) textblock_append_c(tb, TERM_L_RED, "%d", 0); else if (total_dam % 10) textblock_append_c(tb, TERM_L_GREEN, "%d.%d", total_dam / 10, total_dam % 10); else textblock_append_c(tb, TERM_L_GREEN, "%d", total_dam / 10); if (cnt) textblock_append(tb, " vs. others"); textblock_append(tb, ".\n"); /* Note the impact flag */ if (of_has(f, OF_IMPACT)) textblock_append(tb, "Sometimes creates earthquakes on impact.\n"); /* Add breakage chance */ if (ammo) { int chance = breakage_chance(o_ptr, TRUE); textblock_append_c(tb, TERM_L_GREEN, "%d%%", chance); textblock_append(tb, " chance of breaking upon contact.\n"); } /* You always have something to say... */ return TRUE; }
/* * Describe blows. */ static bool describe_blows(textblock *tb, const object_type *o_ptr, player_state state, bitflag f[OF_SIZE]) { int str_plus, dex_plus, old_blows = 0, new_blows, extra_blows; int str_faster = -1, str_done = -1; int dex_plus_bound; int str_plus_bound; int i; bitflag tmp_f[OF_SIZE]; dex_plus_bound = STAT_RANGE - state.stat_ind[A_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[A_STR]; /* Write to the text block */ textblock_append_c(tb, TERM_L_GREEN, "%d.%d ", state.num_blows / 100, (state.num_blows / 10) % 10); textblock_append(tb, "blow%s/round.\n", (state.num_blows > 100) ? "s" : ""); /* Check to see if extra STR or DEX would yield extra blows */ old_blows = state.num_blows; extra_blows = 0; /* First we need to look for extra blows on other items, as * state does not track these */ for (i = INVEN_BOW; i < INVEN_TOTAL; i++) { if (!p_ptr->inventory[i].kind) continue; object_flags_known(&p_ptr->inventory[i], tmp_f); if (of_has(tmp_f, OF_BLOWS)) extra_blows += p_ptr->inventory[i].pval[which_pval(&p_ptr->inventory[i], OF_BLOWS)]; } /* Then we add blows from the weapon being examined */ if (of_has(f, OF_BLOWS)) extra_blows += o_ptr->pval[which_pval(o_ptr, OF_BLOWS)]; /* Then we check for extra "real" blows */ for (dex_plus = 0; dex_plus < dex_plus_bound; dex_plus++) { for (str_plus = 0; str_plus < str_plus_bound; str_plus++) { state.stat_ind[A_STR] += str_plus; state.stat_ind[A_DEX] += dex_plus; new_blows = calc_blows(o_ptr, &state, extra_blows); /* Test to make sure that this extra blow is a * new str/dex combination, not a repeat */ if ((new_blows - new_blows % 10) > (old_blows - old_blows % 10) && (str_plus < str_done || str_done == -1)) { textblock_append(tb, "With +%d STR and +%d DEX you would get %d.%d blows\n", str_plus, dex_plus, (new_blows / 100), (new_blows / 10) % 10); state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; str_done = str_plus; break; } /* If the combination doesn't increment * the displayed blows number, it might still * take a little less energy */ if (new_blows > old_blows && (str_plus < str_faster || str_faster == -1) && (str_plus < str_done || str_done == -1)) { textblock_append(tb, "With +%d STR and +%d DEX you would attack a bit faster\n", str_plus, dex_plus); state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; str_faster = str_plus; continue; } state.stat_ind[A_STR] -= str_plus; state.stat_ind[A_DEX] -= dex_plus; } } return TRUE; }
/** * 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. */ s16b apply_magic(object_type *o_ptr, 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(o_ptr)) return 3; } /* Try to make an ego item */ if (power == 2) make_ego_item(o_ptr, lev); /* Apply magic */ switch (o_ptr->tval) { case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOW: case TV_SHOT: case TV_ARROW: case TV_BOLT: apply_magic_weapon(o_ptr, lev, power); break; case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: case TV_SHIELD: case TV_HELM: case TV_CROWN: case TV_CLOAK: case TV_GLOVES: case TV_BOOTS: apply_magic_armour(o_ptr, lev, power); break; case TV_RING: if (o_ptr->sval == SV_RING_SPEED) { /* Super-charge the ring */ while (one_in_(2)) o_ptr->pval[which_pval(o_ptr, OF_SPEED)]++; } break; case TV_CHEST: /* Hack -- skip ruined chests */ if (o_ptr->kind->level <= 0) break; /* Hack -- pick a "difficulty" */ o_ptr->pval[DEFAULT_PVAL] = randint1(o_ptr->kind->level); /* Never exceed "difficulty" of 55 to 59 */ if (o_ptr->pval[DEFAULT_PVAL] > 55) o_ptr->pval[DEFAULT_PVAL] = (s16b)(55 + randint0(5)); break; } /* Apply minima from ego items if necessary */ ego_apply_minima(o_ptr); return power; }
/** * 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. */ s16b apply_magic(object_type *o_ptr, int lev, bool allow_artifacts, bool good, bool great) { 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); * The calculations below are somewhere between the two. -AS- */ int good_chance = (2 * lev) + 5; int great_chance = MIN(40, (lev * 3) / 4); /* 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 four rolls if forced great */ if (great) rolls = 4; /* Roll for artifacts if allowed */ for (i = 0; i < rolls; i++) if (make_artifact(o_ptr)) return 3; } /* Try to make an ego item */ if (power == 2) make_ego_item(o_ptr, lev); /* Apply magic */ switch (o_ptr->tval) { case TV_DIGGING: case TV_HAFTED: case TV_POLEARM: case TV_SWORD: case TV_BOW: case TV_SHOT: case TV_ARROW: case TV_BOLT: apply_magic_weapon(o_ptr, lev, power); break; case TV_DRAG_ARMOR: case TV_HARD_ARMOR: case TV_SOFT_ARMOR: case TV_SHIELD: case TV_HELM: case TV_CROWN: case TV_CLOAK: case TV_GLOVES: case TV_BOOTS: apply_magic_armour(o_ptr, lev, power); break; case TV_RING: if (o_ptr->sval == SV_RING_SPEED) { /* Super-charge the ring */ while (one_in_(2)) o_ptr->pval[which_pval(o_ptr, OF_SPEED)]++; } break; case TV_CHEST: /* Hack -- skip ruined chests */ if (o_ptr->kind->level <= 0) break; /* Hack -- pick a "difficulty" */ o_ptr->pval[DEFAULT_PVAL] = randint1(o_ptr->kind->level); /* Never exceed "difficulty" of 55 to 59 */ if (o_ptr->pval[DEFAULT_PVAL] > 55) o_ptr->pval[DEFAULT_PVAL] = (s16b)(55 + randint0(5)); break; } /* Apply minima from ego items if necessary */ ego_apply_minima(o_ptr); return power; }
/* * Special display, part 2c * * How to print out the modifications and sustains. * Positive mods with no sustain will be light green. * Positive mods with a sustain will be dark green. * Sustains (with no modification) will be a dark green 's'. * Negative mods (from a curse) will be red. * Huge mods (>9), like from MICoMorgoth, will be a '*' * No mod, no sustain, will be a slate '.' */ static void display_player_sust_info(void) { int i, j, row, col, stat; object_type *o_ptr; bitflag f[OF_SIZE]; int stat_flags[A_MAX]; int sustain_flags[A_MAX]; byte a; char c; /* Row */ row = 2; /* Column */ col = 26; /* Build the stat flags tables */ stat_flags[A_STR] = OF_STR; stat_flags[A_INT] = OF_INT; stat_flags[A_WIS] = OF_WIS; stat_flags[A_DEX] = OF_DEX; stat_flags[A_CON] = OF_CON; sustain_flags[A_STR] = OF_SUST_STR; sustain_flags[A_INT] = OF_SUST_INT; sustain_flags[A_WIS] = OF_SUST_WIS; sustain_flags[A_DEX] = OF_SUST_DEX; sustain_flags[A_CON] = OF_SUST_CON; /* Header */ c_put_str(TERM_WHITE, "abcdefghijkl@", row-1, col); /* Process equipment */ for (i = INVEN_WIELD; i < INVEN_TOTAL; ++i) { /* Get the object */ o_ptr = &p_ptr->inventory[i]; if (!o_ptr->kind) { col++; continue; } /* Get the "known" flags */ object_flags_known(o_ptr, f); /* Initialize color based of sign of pval. */ for (stat = 0; stat < A_MAX; stat++) { /* Default */ a = TERM_SLATE; c = '.'; /* Boost */ if (of_has(f, stat_flags[stat])) { /* Default */ c = '*'; /* Work out which pval we're talking about */ j = which_pval(o_ptr, stat_flags[stat]); /* Good */ if (o_ptr->pval[j] > 0) { /* Good */ a = TERM_L_GREEN; /* Label boost */ if (o_ptr->pval[j] < 10) c = I2D(o_ptr->pval[j]); } /* Bad */ if (o_ptr->pval[j] < 0) { /* Bad */ a = TERM_RED; /* Label boost */ if (o_ptr->pval[j] > -10) c = I2D(-(o_ptr->pval[j])); } } /* Sustain */ if (of_has(f, sustain_flags[stat])) { /* Dark green */ a = TERM_GREEN; /* Convert '.' to 's' */ if (c == '.') c = 's'; } if ((c == '.') && o_ptr->kind && !object_flag_is_known(o_ptr, sustain_flags[stat])) c = '?'; /* Dump proper character */ Term_putch(col, row+stat, a, c); } /* Advance */ col++; } /* Player flags */ player_flags(f); /* Check stats */ for (stat = 0; stat < A_MAX; ++stat) { /* Default */ a = TERM_SLATE; c = '.'; /* Sustain */ if (of_has(f, sustain_flags[stat])) { /* Dark green "s" */ a = TERM_GREEN; c = 's'; } /* Dump */ Term_putch(col, row+stat, a, c); } /* Column */ col = 26; /* Footer */ c_put_str(TERM_WHITE, "abcdefghijkl@", row+6, col); /* Equippy */ display_player_equippy(row+7, col); }
/* * Evaluate the object's overall power level. */ s32b object_power(const object_type* o_ptr, int verbose, ang_file *log_file, bool known) { s32b p = 0, q = 0, slay_pwr = 0; unsigned int i, j; int extra_stat_bonus = 0, mult = 1, num_slays = 0, k = 1; bitflag flags[OF_SIZE], mask[OF_SIZE]; /* Zero the flag counts */ for (i = 0; i < N_ELEMENTS(sets); i++) sets[i].count = 0; /* Extract the flags */ if (known) { file_putf(log_file, "Object is deemed known\n"); object_flags(o_ptr, flags); } else { file_putf(log_file, "Object may not be fully known\n"); object_flags_known(o_ptr, flags); } /* Log the flags in human-readable form */ if (verbose) log_flags(flags, log_file); /* Get the slay power and number of slay/brand types */ create_mask(mask, FALSE, OFT_SLAY, OFT_KILL, OFT_BRAND, OFT_MAX); num_slays = list_slays(flags, mask, NULL, NULL, NULL, TRUE); if (num_slays) slay_pwr = slay_power(o_ptr, verbose, log_file, known); /* Start with any damage boost from the item itself */ p += (o_ptr->to_d * DAMAGE_POWER / 2); file_putf(log_file, "Adding power from to_dam, total is %d\n", p); /* Add damage from dice for any wieldable weapon or ammo */ if (wield_slot(o_ptr) == INVEN_WIELD || obj_is_ammo(o_ptr)) { p += (o_ptr->dd * (o_ptr->ds + 1) * DAMAGE_POWER / 4); file_putf(log_file, "Adding power for dam dice, total is %d\n", p); /* Add 2nd lot of damage power for nonweapons */ } else if (wield_slot(o_ptr) != INVEN_BOW) { p += (o_ptr->to_d * DAMAGE_POWER); file_putf(log_file, "Adding power from nonweap to_dam, total is %d\n", p); /* Add power boost for nonweapons with combat flags */ if (num_slays || of_has(flags, OF_BLOWS) || of_has(flags, OF_SHOTS) || of_has(flags, OF_MIGHT)) { p += (WEAP_DAMAGE * DAMAGE_POWER); file_putf(log_file, "Adding power for nonweap combat flags, total is %d\n", p); } } /* Add ammo damage for launchers, get multiplier and rescale */ if (wield_slot(o_ptr) == INVEN_BOW) { p += (archery[o_ptr->sval / 10].ammo_dam * DAMAGE_POWER / 2); file_putf(log_file, "Adding power from ammo, total is %d\n", p); mult = bow_multiplier(o_ptr->sval); file_putf(log_file, "Base mult for this weapon is %d\n", mult); } /* Add launcher bonus for ego ammo, multiply for launcher and rescale */ if (obj_is_ammo(o_ptr)) { if (o_ptr->ego) p += (archery[o_ptr->tval - TV_SHOT].launch_dam * DAMAGE_POWER / 2); p = p * archery[o_ptr->tval - TV_SHOT].launch_mult / (2 * MAX_BLOWS); file_putf(log_file, "After multiplying ammo and rescaling, power is %d\n", p); } /* Add power for extra blows */ if (of_has(flags, OF_BLOWS)) { j = which_pval(o_ptr, OF_BLOWS); if (known || object_this_pval_is_visible(o_ptr, j)) { if (o_ptr->pval[j] >= INHIBIT_BLOWS) { p += INHIBIT_POWER; file_putf(log_file, "INHIBITING - too many extra blows\n"); } else { p = p * (MAX_BLOWS + o_ptr->pval[j]) / MAX_BLOWS; /* Add boost for assumed off-weapon damage */ p += (NONWEAP_DAMAGE * o_ptr->pval[j] * DAMAGE_POWER / 2); file_putf(log_file, "Adding power for extra blows, total is %d\n", p); } } } /* Add power for extra shots - note that we cannot handle negative shots */ if (of_has(flags, OF_SHOTS)) { j = which_pval(o_ptr, OF_SHOTS); if (known || object_this_pval_is_visible(o_ptr, j)) { if (o_ptr->pval[j] >= INHIBIT_SHOTS) { p += INHIBIT_POWER; file_putf(log_file, "INHIBITING - too many extra shots\n"); } else if (o_ptr->pval[j] > 0) { p = (p * (1 + o_ptr->pval[j])); file_putf(log_file, "Extra shots: multiplying power by 1 + %d, total is %d\n", o_ptr->pval[j], p); } } } /* Add power for extra might */ if (of_has(flags, OF_MIGHT)) { j = which_pval(o_ptr, OF_MIGHT); if (known || object_this_pval_is_visible(o_ptr, j)) { if (o_ptr->pval[j] >= INHIBIT_MIGHT) { p += INHIBIT_POWER; mult = 1; /* don't overflow */ file_putf(log_file, "INHIBITING - too much extra might\n"); } else mult += o_ptr->pval[j]; file_putf(log_file, "Mult after extra might is %d\n", mult); } } p *= mult; file_putf(log_file, "After multiplying power for might, total is %d\n", p); /* Apply the correct slay multiplier */ if (slay_pwr) { p = (p * (slay_pwr / 10)) / (tot_mon_power / 10); file_putf(log_file, "Adjusted for slay power, total is %d\n", p); } /* Melee weapons assume MAX_BLOWS per turn, so we must divide by MAX_BLOWS * to get equal ratings for launchers. */ if (wield_slot(o_ptr) == INVEN_BOW) { p /= MAX_BLOWS; file_putf(log_file, "Rescaling bow power, total is %d\n", p); } /* Add power for +to_hit */ p += (o_ptr->to_h * TO_HIT_POWER / 2); file_putf(log_file, "Adding power for to hit, total is %d\n", p); /* Add power for base AC and adjust for weight */ if (o_ptr->ac) { q += BASE_ARMOUR_POWER; q += (o_ptr->ac * BASE_AC_POWER / 2); file_putf(log_file, "Adding %d power for base AC value\n", q); /* Add power for AC per unit weight */ if (o_ptr->weight > 0) { i = 800 * (o_ptr->ac + o_ptr->to_a) / o_ptr->weight; /* Avoid overpricing Elven Cloaks */ if (i > 450) i = 450; q *= i; q /= 100; /* Weightless (ethereal) armour items get fixed boost */ } else q *= 5; p += q; file_putf(log_file, "Adding power for AC per unit weight, now %d\n", p); } /* Add power for +to_ac */ p += (o_ptr->to_a * TO_AC_POWER / 2); file_putf(log_file, "Adding power for to_ac of %d, total is %d\n", o_ptr->to_a, p); if (o_ptr->to_a > HIGH_TO_AC) { p += ((o_ptr->to_a - (HIGH_TO_AC - 1)) * TO_AC_POWER); file_putf(log_file, "Adding power for high to_ac value, total is %d\n", p); } if (o_ptr->to_a > VERYHIGH_TO_AC) { p += ((o_ptr->to_a - (VERYHIGH_TO_AC -1)) * TO_AC_POWER * 2); file_putf(log_file, "Adding power for very high to_ac value, total is %d\n", p); } if (o_ptr->to_a >= INHIBIT_AC) { p += INHIBIT_POWER; file_putf(log_file, "INHIBITING: AC bonus too high\n"); } /* Add power for light sources by radius XXX Hack - rewrite calc_torch! */ if (wield_slot(o_ptr) == INVEN_LIGHT) { p += BASE_LIGHT_POWER; /* Artifact lights have larger radius so add more */ if (o_ptr->artifact) p += BASE_LIGHT_POWER; file_putf(log_file, "Adding power for light radius, total is %d\n", p); } /* Add base power for jewelry */ if (object_is_jewelry(o_ptr)) { p += BASE_JEWELRY_POWER; file_putf(log_file, "Adding power for jewelry, total is %d\n", p); } /* Add power for non-derived flags (derived flags have flag_power 0) */ for (i = 0; i < OF_MAX; i++) { if (of_has(flags, i)) { if (flag_uses_pval(i)) { j = which_pval(o_ptr, i); if (known || object_this_pval_is_visible(o_ptr, j)) { k = o_ptr->pval[j]; extra_stat_bonus += (k * pval_mult(i)); } } else k = 1; if (flag_power(i)) { p += (k * flag_power(i) * slot_mult(i, wield_slot(o_ptr))); file_putf(log_file, "Adding power for %s, total is %d\n", flag_name(i), p); } /* Track combinations of flag types - note we ignore SUST_CHR */ for (j = 0; j < N_ELEMENTS(sets); j++) if ((sets[j].type == obj_flag_type(i)) && (i != OF_SUST_CHR)) sets[j].count++; } } /* Add extra power term if there are a lot of ability bonuses */ if (extra_stat_bonus > 249) { file_putf(log_file, "Inhibiting! (Total ability bonus of %d is too high)\n", extra_stat_bonus); p += INHIBIT_POWER; } else { p += ability_power[extra_stat_bonus / 10]; file_putf(log_file, "Adding power for pval total of %d, total is %d\n", extra_stat_bonus, p); } /* Add extra power for multiple flags of the same type */ for (i = 0; i < N_ELEMENTS(sets); i++) { if (sets[i].count > 1) { p += (sets[i].factor * sets[i].count * sets[i].count); file_putf(log_file, "Adding power for multiple flags of type %d, total is %d\n", i, p); } /* Add bonus if item has a full set of these flags */ if (sets[i].count == sets[i].size) { p += sets[i].bonus; file_putf(log_file, "Adding power for full set of type %d, total is %d\n", i, p); } } /* add power for effect */ if (known || object_effect_is_known(o_ptr)) { if (o_ptr->artifact && o_ptr->artifact->effect) { p += effect_power(o_ptr->artifact->effect); file_putf(log_file, "Adding power for artifact activation, total is %d\n", p); } else { p += effect_power(o_ptr->kind->effect); file_putf(log_file, "Adding power for item activation, total is %d\n", p); } } file_putf(log_file, "FINAL POWER IS %d\n", p); return p; }
/** * Applying magic to an object, which includes creating ego-items, and applying * random bonuses, * * The `good` and `great` arguments affect the number of affixes on the item. * Setting `allow_artifacts` to TRUE allows artifacts to be created here - * this is used if we call directly with a pre-specified object kind; it is * not used if we come via make_object. * * If `good` or `great` are not set, then the `lev` argument controls the * number of affixes on the item. * * Returns the number of affixes on the object: 0 for a normal object, * MAX_AFFIXES+1 for an artifact. */ s16b apply_magic(object_type *o_ptr, int lev, bool allow_artifacts, bool good, bool great) { int i, max_lev = 3, min_lev = 1, affixes; bool art = FALSE; /* Set the number and quality of affixes this item will have */ if (randint0(100) < 20 + lev) max_lev++; if (great || (good && one_in_(4))) max_lev++; if (great || good || randint0(100) < 10 + lev) min_lev++; if (great) min_lev++; affixes = randint0(3 + lev / 25); if (great) affixes += 2 + randint1(2); else if (good) affixes += randint1(2); if (of_has(o_ptr->flags, OF_GOOD)) affixes--; if (affixes > MAX_AFFIXES) affixes = MAX_AFFIXES; /* Roll for artifact creation - n.b. this is now only used if we were called directly and not via make_object */ if (allow_artifacts) { if (great && one_in_(ART_GREAT)) art = TRUE; else if (max_lev > 3 && one_in_(ART_GOOD)) art = TRUE; else if (one_in_(ART_NORMAL)) art = TRUE; if (art && make_artifact(o_ptr, lev)) return MAX_AFFIXES + 1; } /* Generate and apply affixes, stopping if we acquire a theme */ for (i = 0; i < affixes && !o_ptr->theme; i++) obj_add_affix(o_ptr, lev, max_lev, min_lev); /* Apply special-case magic */ switch (o_ptr->tval) { case TV_RING: if (o_ptr->sval == SV_RING_SPEED) { /* Super-charge the ring */ while (one_in_(2)) o_ptr->pval[which_pval(o_ptr, OF_SPEED)]++; } break; case TV_CHEST: /* Skip ruined chests */ if (o_ptr->kind->level <= 0) break; /* Pick a difficulty */ o_ptr->extent = randint1(o_ptr->kind->level); /* Never exceed difficulty of 55 to 59 */ if (o_ptr->extent > 55) o_ptr->extent = (s16b)(55 + randint0(5)); break; } return affixes; }
/** * Apply an affix to an ego-item, checking minima as we go. * * \param o_ptr is the object we're modifying * \param level is the generation level * \param affix is the affix we're applying */ void ego_apply_magic(object_type *o_ptr, int level, int affix) { int i, j, flag, pval, amt; bitflag flags[OF_SIZE], f2[OF_SIZE]; ego_item_type *ego; ego = &e_info[affix]; /* Random powers */ for (i = 0; i < ego->num_randlines; i++) for (j = 0; j < ego->num_randflags[i]; j++) of_on(o_ptr->flags, get_new_attr(o_ptr->flags, ego->randmask[i])); /* Apply extra combat bonuses */ amt = randcalc(ego->to_h, level, RANDOMISE); o_ptr->to_h += amt; if (ego->min_to_h != NO_MINIMUM) { if (amt < ego->min_to_h) o_ptr->to_h += ego->min_to_h - amt; if (o_ptr->to_h < ego->min_to_h) o_ptr->to_h = ego->min_to_h; } amt = randcalc(ego->to_d, level, RANDOMISE); o_ptr->to_d += amt; if (ego->min_to_d != NO_MINIMUM) { if (amt < ego->min_to_d) o_ptr->to_d += ego->min_to_d - amt; if (o_ptr->to_d < ego->min_to_d) o_ptr->to_d = ego->min_to_d; } amt = randcalc(ego->to_a, level, RANDOMISE); o_ptr->to_a += amt; if (ego->min_to_a != NO_MINIMUM) { if (amt < ego->min_to_a) o_ptr->to_a += ego->min_to_a - amt; if (o_ptr->to_a < ego->min_to_a) o_ptr->to_a = ego->min_to_a; } /* Apply pvals */ of_copy(f2, ego->flags); for (i = 0; i < ego->num_pvals; i++) { of_copy(flags, ego->pval_flags[i]); pval = randcalc(ego->pval[i], level, RANDOMISE); if (ego->min_pval[i] != NO_MINIMUM && pval < ego->min_pval[i]) pval = ego->min_pval[i]; for (flag = of_next(flags, FLAG_START); flag != FLAG_END; flag = of_next(flags, flag + 1)) /* Prevent phantom flags, and check minima after adding */ if (pval) { object_add_pval(o_ptr, pval, flag); if (ego->min_pval[i] != NO_MINIMUM && of_has(o_ptr->flags, flag)) if (o_ptr->pval[which_pval(o_ptr, flag)] < ego->min_pval[i]) object_add_pval(o_ptr, ego->min_pval[i] - o_ptr->pval[which_pval(o_ptr, flag)], flag); } else of_off(f2, flag); } /* Apply remaining flags */ of_union(o_ptr->flags, f2); /* Adjust AC, weight, dice and sides */ if (o_ptr->ac && ego->ac_mod) o_ptr->ac = ((100 + ego->ac_mod) * o_ptr->ac) / 100; o_ptr->weight = ((100 + ego->wgt_mod) * o_ptr->weight) / 100; o_ptr->dd += ego->dd; if (o_ptr->dd < 1) o_ptr->dd = 1; o_ptr->ds += ego->ds; if (o_ptr->ds < 1) o_ptr->ds = 1; /* Tidy up and de-duplicate flags, and set the correct prefix/suffix */ check_flags(o_ptr); obj_affix_names(o_ptr); return; }