/** * 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; }
/** * Delete an object and free its memory, and set its pointer to NULL */ void object_delete(struct object **obj_address) { struct object *obj = *obj_address; struct object *prev = obj->prev; struct object *next = obj->next; /* Free slays and brands */ if (obj->slays) free_slay(obj->slays); if (obj->brands) free_brand(obj->brands); /* Check any next and previous objects */ if (next) { if (prev) { prev->next = next; next->prev = prev; } else { next->prev = NULL; } } else if (prev) { prev->next = NULL; } /* If we're tracking the object, stop */ if (player && player->upkeep && obj == player->upkeep->object) player->upkeep->object = NULL; mem_free(obj); *obj_address = NULL; }
/** * Delete an object and free its memory, and set its pointer to NULL */ void object_delete(struct object **obj_address) { struct object *obj = *obj_address; struct object *prev = obj->prev; struct object *next = obj->next; /* Check any next and previous objects */ if (next) { if (prev) { prev->next = next; next->prev = prev; } else { next->prev = NULL; } } else if (prev) { prev->next = NULL; } /* If we're tracking the object, stop */ if (player && player->upkeep && obj == player->upkeep->object) player->upkeep->object = NULL; /* Orphan rather than actually delete if we still have a known object */ if (cave && player && player->cave && obj->oidx && (obj == cave->objects[obj->oidx]) && player->cave->objects[obj->oidx]) { obj->iy = 0; obj->ix = 0; obj->held_m_idx = 0; obj->mimicking_m_idx = 0; /* Object is now purely imaginary to the player */ obj->known->notice |= OBJ_NOTICE_IMAGINED; return; } /* Remove from any lists */ if (player && player->cave && player->cave->objects && obj->oidx && (obj == player->cave->objects[obj->oidx])) player->cave->objects[obj->oidx] = NULL; if (cave && cave->objects && obj->oidx && (obj == cave->objects[obj->oidx])) cave->objects[obj->oidx] = NULL; if (obj->slays) { free_slay(obj->slays); } if (obj->brands) { free_brand(obj->brands); } mem_free(obj); *obj_address = NULL; }
/** * Wipe an object clean. */ void object_wipe(struct object *obj, bool free_curse_objects) { /* Free slays and brands */ free_slay(obj->slays); free_brand(obj->brands); free_curse(obj->curses, free_curse_objects, false); /* Wipe the structure */ memset(obj, 0, sizeof(*obj)); }
/** * Wipe an object clean. */ void object_wipe(struct object *obj) { /* Free slays and brands */ if (obj->slays) free_slay(obj->slays); if (obj->brands) free_brand(obj->brands); /* Wipe the structure */ memset(obj, 0, sizeof(*obj)); }
/** * Free up an object * * This doesn't affect any game state outside of the object itself */ void object_free(struct object *obj) { if (obj->slays) { free_slay(obj->slays); } if (obj->brands) { free_brand(obj->brands); } if (obj->curses) { free_curse(obj->curses, true, true); } mem_free(obj); }
/** * Calculate the rating for a given slay combination */ static s32b slay_power(const object_type *obj, int p, int verbose, int dice_pwr, bool known) { u32b sv = 0; int i, q, num_brands = 0, num_slays = 0, num_kills = 0; int mult; int tot_mon_power = 0; struct brand *brands = obj->brands; struct slay *slays = obj->slays; /* Count the known brands and slays */ while (brands) { if (known || brands->known) num_brands++; brands = brands->next; } while (slays) { if (known || slays->known) { if (slays->multiplier <= 3) num_slays++; else num_kills++; } slays = slays->next; } /* If there are no slays or brands return */ if ((num_slays + num_brands + num_kills) == 0) return p; /* Look in the cache to see if we know this one yet */ sv = check_slay_cache(obj); /* If it's cached (or there are no slays), return the value */ if (sv) { log_obj("Slay cache hit\n"); } else { /* * Otherwise we need to calculate the expected average multiplier * for this combination (multiplied by the total number of * monsters, which we'll divide out later). */ for (i = 0; i < z_info->r_max; i++) { monster_type *mon = mem_zalloc(sizeof(*mon)); const struct brand *b = NULL; const struct slay *s = NULL; char verb[20]; mult = 1; mon->race = &r_info[i]; /* Find the best multiplier against this monster */ improve_attack_modifier((object_type *)obj, mon, &b, &s, verb, FALSE, FALSE, !known); if (s) mult = s->multiplier; else if (b) mult = b->multiplier; /* Add up totals */ tot_mon_power += mon->race->scaled_power; sv += mult * mon->race->scaled_power; mem_free(mon); } /* * To get the expected damage for this weapon, multiply the * average damage from base dice by sv, and divide by the * total number of monsters. */ if (verbose) { struct brand *b, *brands = NULL; struct slay *s, *slays = NULL; /* Write info about the slay combination and multiplier */ log_obj("Slay multiplier for: "); brands = brand_collect(obj->brands, NULL, !known); slays = slay_collect(obj->slays, NULL, !known); for (b = brands; b; b = b->next) { log_obj(format("%sx%d ", b->name, b->multiplier)); } for (s = slays; s; s = s->next) { log_obj(format("%sx%d ", s->name, s->multiplier)); } log_obj(format("\nsv is: %d\n", sv)); log_obj(format(" and t_m_p is: %d \n", tot_mon_power)); log_obj(format("times 1000 is: %d\n", (1000 * sv) / tot_mon_power)); free_brand(brands); free_slay(slays); } /* Add to the cache */ if (fill_slay_cache(obj, sv)) log_obj("Added to slay cache\n"); } q = (dice_pwr * (sv / 100)) / (tot_mon_power / 100); p += q; log_obj(format("Add %d for slay power, total is %d\n", q, p)); /* Bonuses for multiple brands and slays */ if (num_slays > 1) { q = (num_slays * num_slays * dice_pwr) / (DAMAGE_POWER * 5); p += q; log_obj(format("Add %d power for multiple slays, total is %d\n", q, p)); } if (num_brands > 1) { q = (2 * num_brands * num_brands * dice_pwr) / (DAMAGE_POWER * 5); p += q; log_obj(format("Add %d power for multiple brands, total is %d\n",q, p)); } if (num_kills > 1) { q = (3 * num_kills * num_kills * dice_pwr) / (DAMAGE_POWER * 5); p += q; log_obj(format("Add %d power for multiple kills, total is %d\n", q, p)); } if (num_slays == 8) { p += 10; log_obj(format("Add 10 power for full set of slays, total is %d\n", p)); } if (num_brands == 5) { p += 20; log_obj(format("Add 20 power for full set of brands, total is %d\n",p)); } if (num_kills == 3) { p += 20; log_obj(format("Add 20 power for full set of kills, total is %d\n", p)); } return p; }
/** * Describe damage. */ static bool describe_damage(textblock *tb, const struct object *obj) { bool nonweap_slay = false; int normal_damage; struct brand *brand, *brands = NULL; struct slay *slay, *slays = NULL; bool has_brands_or_slays; /* Collect brands and slays */ has_brands_or_slays = obj_known_damage(obj, &normal_damage, &brands, &slays, &nonweap_slay); /* Mention slays and brands from other items */ if (nonweap_slay) textblock_append(tb, "This weapon may benefit from one or more off-weapon brands or slays.\n"); textblock_append(tb, "Average damage/round: "); /* Output damage for creatures effected by the brands */ brand = brands; while (brand) { if (brand->damage <= 0) textblock_append_c(tb, COLOUR_L_RED, "%d", 0); else if (brand->damage % 10) textblock_append_c(tb, COLOUR_L_GREEN, "%d.%d", brand->damage / 10, brand->damage % 10); else textblock_append_c(tb, COLOUR_L_GREEN, "%d",brand->damage / 10); textblock_append(tb, " vs. creatures not resistant to %s, ", brand->name); brand = brand->next; } /* Output damage for creatures effected by the slays */ slay = slays; while (slay) { if (slay->damage <= 0) textblock_append_c(tb, COLOUR_L_RED, "%d", 0); else if (slay->damage % 10) textblock_append_c(tb, COLOUR_L_GREEN, "%d.%d", slay->damage / 10, slay->damage % 10); else textblock_append_c(tb, COLOUR_L_GREEN, "%d", slay->damage / 10); textblock_append(tb, " vs. %s, ", slay->name); slay = slay->next; } if (has_brands_or_slays) textblock_append(tb, "and "); if (normal_damage <= 0) textblock_append_c(tb, COLOUR_L_RED, "%d", 0); else if (normal_damage % 10) textblock_append_c(tb, COLOUR_L_GREEN, "%d.%d", normal_damage / 10, normal_damage % 10); else textblock_append_c(tb, COLOUR_L_GREEN, "%d", normal_damage / 10); if (has_brands_or_slays) textblock_append(tb, " vs. others"); textblock_append(tb, ".\n"); free_brand(brands); free_slay(slays); return true; }
/** * Gets information about the average damage/turn that can be inflicted if * the player wields the given weapon. * * Fills in the damage against normal adversaries in `normal_damage`, as well * as the slays on the weapon in slay_list[] and corresponding damages in * slay_damage[]. These must both be at least SL_MAX long to be safe. * `nonweap_slay` is set to whether other items being worn could add to the * damage done by branding attacks. * * Returns the number of slays populated in slay_list[] and slay_damage[]. * * Note that the results are meaningless if called on a fake ego object as * the actual ego may have different properties. */ static bool obj_known_damage(const struct object *obj, int *normal_damage, struct brand **brand_list, struct slay **slay_list, bool *nonweap_slay) { int i; int dice, sides, dam, total_dam, plus = 0; int xtra_postcrit = 0, xtra_precrit = 0; int crit_mult, crit_div, crit_add; int old_blows = 0; struct brand *brand; struct slay *slay; struct object *bow = equipped_item_by_slot_name(player, "shooting"); bool weapon = tval_is_melee_weapon(obj); bool ammo = (player->state.ammo_tval == obj->tval) && (bow); int multiplier = 1; struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current_weapon = slot_object(player, weapon_slot); /* Pretend we're wielding the object if it's a weapon */ if (weapon) player->body.slots[weapon_slot].obj = (struct object *) obj; /* Calculate the player's hypothetical state */ calc_bonuses(player, &state, true, false); /* Stop pretending */ player->body.slots[weapon_slot].obj = current_weapon; /* Use displayed dice if real dice not known */ if (object_attack_plusses_are_visible(obj)) { dice = obj->dd; sides = obj->ds; } else { dice = obj->kind->dd; sides = obj->kind->ds; } /* Calculate damage */ dam = ((sides + 1) * dice * 5); if (weapon) { xtra_postcrit = state.to_d * 10; if (object_attack_plusses_are_visible(obj)) { xtra_precrit += obj->to_d * 10; plus += obj->to_h; } calculate_melee_crits(&state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); old_blows = state.num_blows; } else { /* Ammo */ if (object_attack_plusses_are_visible(obj)) plus += obj->to_h; calculate_missile_crits(&player->state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); if (object_attack_plusses_are_visible(obj)) dam += (obj->to_d * 10); if (object_attack_plusses_are_visible(bow)) dam += (bow->to_d * 10); } if (ammo) multiplier = player->state.ammo_mult; /* Get the brands */ *brand_list = brand_collect(obj->known->brands, ammo ? bow->known : NULL); /* Get the slays */ *slay_list = slay_collect(obj->known->slays, ammo ? bow->known : NULL); /* Melee weapons may get slays and brands from other items */ *nonweap_slay = false; if (weapon) { for (i = 2; i < player->body.count; i++) { struct object *slot_obj = slot_object(player, i); struct brand *new_brand; struct slay *new_slay; if (!slot_obj) continue; if (slot_obj->known->brands || slot_obj->known->slays) *nonweap_slay = true; else continue; /* Replace the old lists with new ones */ new_brand = brand_collect(*brand_list, slot_obj->known); new_slay = slay_collect(*slay_list, slot_obj->known); free_brand(*brand_list); free_slay(*slay_list); *brand_list = new_brand; *slay_list = new_slay; } } /* Get damage for each brand on the objects */ for (brand = *brand_list; brand; brand = brand->next) { /* ammo mult adds fully, melee mult is times 1, so adds 1 less */ int melee_adj_mult = ammo ? 0 : 1; /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + brand->multiplier - 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 *= player->state.num_shots; brand->damage = total_dam; } /* Get damage for each slay on the objects */ for (slay = *slay_list; slay; slay = slay->next) { /* ammo mult adds fully, melee mult is times 1, so adds 1 less */ int melee_adj_mult = ammo ? 0 : 1; /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + slay->multiplier - 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 *= player->state.num_shots; slay->damage = total_dam; } /* 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; /* Normal damage, not considering brands or slays */ if (weapon) total_dam = (total_dam * old_blows) / 100; else total_dam *= player->state.num_shots; *normal_damage = total_dam; return (*slay_list || *brand_list); }