/** * Remove an object from the gear list, leaving it unattached * \param obj the object being tested * \return whether an object was removed */ static bool gear_excise_object(struct object *obj) { int i; pile_excise(&player->gear_k, obj->known); pile_excise(&player->gear, obj); /* Change the weight */ player->upkeep->total_weight -= (obj->number * obj->weight); /* Make sure it isn't still equipped */ for (i = 0; i < player->body.count; i++) { if (slot_object(player, i) == obj) { player->body.slots[i].obj = NULL; player->upkeep->equip_cnt--; } } /* Update the gear */ calc_inventory(player->upkeep, player->gear, player->body); /* Housekeeping */ player->upkeep->update |= (PU_BONUS); player->upkeep->notice |= (PN_COMBINE); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); return true; }
/** * Check no currently worn items are stopping the action 'c' */ bool key_confirm_command(unsigned char c) { int i; /* Hack -- Scan equipment */ for (i = 0; i < player->body.count; i++) { char verify_inscrip[] = "^*"; unsigned n; struct object *obj = slot_object(player, i); if (!obj) continue; /* Set up string to look for, e.g. "^d" */ verify_inscrip[1] = c; /* Verify command */ n = check_for_inscrip(obj, "^*") + check_for_inscrip(obj, verify_inscrip); while (n--) { if (!get_check("Are you sure? ")) return false; } } return true; }
/** * Melee effect handler: Absorb the player's light. */ static void melee_effect_handler_EAT_LIGHT(melee_effect_handler_context_t *context) { int light_slot = slot_by_name(context->p, "light"); struct object *obj = slot_object(context->p, light_slot); /* Take damage */ take_hit(context->p, context->damage, context->ddesc); /* Player is dead */ if (context->p->is_dead) return; /* Drain fuel where applicable */ if (obj && !of_has(obj->flags, OF_NO_FUEL) && (obj->timeout > 0)) { /* Reduce fuel */ obj->timeout -= (250 + randint1(250)); if (obj->timeout < 1) obj->timeout = 1; /* Notice */ if (!context->p->timed[TMD_BLIND]) { msg("Your light dims."); context->obvious = TRUE; } /* Redraw stuff */ context->p->upkeep->redraw |= (PR_EQUIP); } }
/** * Equippy chars */ static void display_player_equippy(int y, int x) { int i; byte a; wchar_t c; struct object *obj; /* Dump equippy chars */ for (i = 0; i < player->body.count; ++i) { /* Object */ obj = slot_object(player, i); /* Skip empty objects */ if (!obj) continue; /* Get attr/char for display */ a = object_attr(obj); c = object_char(obj); /* Dump */ if ((tile_width == 1) && (tile_height == 1)) Term_putch(x + i, y, a, c); } }
/** * Returns information about objects that can be used for digging. * * `deciturns` will be filled in with the avg number of deciturns it will * take to dig through each type of diggable terrain, and must be at least * [DIGGING_MAX]. * * Returns FALSE if the object has no effect on digging, or if the specifics * are meaningless (i.e. the object is an ego template, not a real item). */ static bool obj_known_digging(struct object *obj, int deciturns[]) { player_state state; int i; int chances[DIGGING_MAX]; int slot = wield_slot(obj); struct object *current = slot_object(player, slot); if (!tval_is_wearable(obj) || (!tval_is_melee_weapon(obj) && (obj->modifiers[OBJ_MOD_TUNNEL] <= 0))) return FALSE; /* Pretend we're wielding the object */ player->body.slots[slot].obj = obj; /* Calculate the player's hypothetical state */ calc_bonuses(player->gear, &state, TRUE); /* Stop pretending */ player->body.slots[slot].obj = current; calc_digging_chances(&state, chances); /* Digging chance is out of 1600 */ for (i = DIGGING_RUBBLE; i < DIGGING_MAX; i++) { int chance = MIN(1600, chances[i]); deciturns[i] = chance ? (16000 / chance) : 0; } return TRUE; }
/** * Get a list of "valid" objects. * * Fills item_list[] with items that are "okay" as defined by the * provided tester function, etc. mode determines what combination of * inventory, equipment, quiver and player's floor location should be used * when drawing up the list. * * Returns the number of items placed into the list. * * Maximum space that can be used is * z_info->pack_size + z_info->quiver_size + player->body.count + * z_info->floor_size, * though practically speaking much smaller numbers are likely. */ int scan_items(struct object **item_list, size_t item_max, int mode, item_tester tester) { bool use_inven = ((mode & USE_INVEN) ? true : false); bool use_equip = ((mode & USE_EQUIP) ? true : false); bool use_quiver = ((mode & USE_QUIVER) ? true : false); bool use_floor = ((mode & USE_FLOOR) ? true : false); int floor_max = z_info->floor_size; struct object **floor_list = mem_zalloc(floor_max * sizeof(struct object *)); int floor_num; int i; size_t item_num = 0; if (use_inven) for (i = 0; i < z_info->pack_size && item_num < item_max; i++) { if (object_test(tester, player->upkeep->inven[i])) item_list[item_num++] = player->upkeep->inven[i]; } if (use_equip) for (i = 0; i < player->body.count && item_num < item_max; i++) { if (object_test(tester, slot_object(player, i))) item_list[item_num++] = slot_object(player, i); } if (use_quiver) for (i = 0; i < z_info->quiver_size && item_num < item_max; i++) { if (object_test(tester, player->upkeep->quiver[i])) item_list[item_num++] = player->upkeep->quiver[i]; } /* Scan all non-gold objects in the grid */ if (use_floor) { floor_num = scan_floor(floor_list, floor_max, OFLOOR_TEST | OFLOOR_SENSE | OFLOOR_VISIBLE, tester); for (i = 0; i < floor_num && item_num < item_max; i++) item_list[item_num++] = floor_list[i]; } mem_free(floor_list); return item_num; }
struct object *equipped_item_by_slot_name(struct player *p, const char *name) { /* Ensure a valid body */ if (!p->body.slots) return NULL; return slot_object(p, slot_by_name(p, name)); }
/** * Convert a label into an item in the equipment. * * Return NULL if the label does not indicate a real item. */ struct object *label_to_equip(int c) { int i; /* Convert */ i = (islower((unsigned char)c) ? A2I(c) : -1); /* Verify the index */ if ((i < 0) || (i >= player->body.count)) return NULL; /* Return the object */ return slot_object(player, i); }
/** * Build the object list. */ static void build_obj_list(int last, struct object **list, item_tester tester, olist_detail_t mode) { int i; bool gold_ok = (mode & OLIST_GOLD) ? true : false; bool in_term = (mode & OLIST_WINDOW) ? true : false; bool dead = (mode & OLIST_DEATH) ? true : false; bool show_empty = (mode & OLIST_SEMPTY) ? true : false; bool equip = list ? false : true; bool quiver = list == player->upkeep->quiver ? true : false; /* Build the object list */ for (i = 0; i <= last; i++) { char buf[80]; struct object *obj = equip ? slot_object(player, i) : list[i]; /* Acceptable items get a label */ if (object_test(tester, obj) || (obj && tval_is_money(obj) && gold_ok)) strnfmt(items[num_obj].label, sizeof(items[num_obj].label), "%c) ", quiver ? I2D(i) : I2A(i)); /* Unacceptable items are still sometimes shown */ else if ((!obj && show_empty) || in_term) my_strcpy(items[num_obj].label, " ", sizeof(items[num_obj].label)); /* Unacceptable items are skipped in the main window */ else continue; /* Show full slot labels for equipment (or quiver in subwindow) */ if (equip) { strnfmt(buf, sizeof(buf), "%-14s: ", equip_mention(player, i)); my_strcpy(items[num_obj].equip_label, buf, sizeof(items[num_obj].equip_label)); } else if ((in_term || dead) && quiver) { strnfmt(buf, sizeof(buf), "Slot %-9d: ", i); my_strcpy(items[num_obj].equip_label, buf, sizeof(items[num_obj].equip_label)); } else { strnfmt(items[num_obj].equip_label, sizeof(items[num_obj].equip_label), ""); } /* Save the object */ items[num_obj].object = obj; items[num_obj].key = (items[num_obj].label)[0]; num_obj++; } }
/** * Remove an object from the gear list, leaving it unattached * \param obj the object being tested * \return whether an object was removed */ bool gear_excise_object(struct object *obj) { int i; pile_excise(&player->gear, obj); /* Make sure it isn't still equipped */ for (i = 0; i < player->body.count; i++) { if (slot_object(player, i) == obj) player->body.slots[i].obj = NULL; } /* Housekeeping */ player->upkeep->update |= (PU_BONUS | PU_MANA | PU_INVEN); player->upkeep->notice |= (PN_COMBINE); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); return TRUE; }
/** * Returns information about objects that can be used for digging. * * `deciturns` will be filled in with the avg number of deciturns it will * take to dig through each type of diggable terrain, and must be at least * [DIGGING_MAX]. * * Returns false if the object has no effect on digging, or if the specifics * are meaningless (i.e. the object is an ego template, not a real item). */ static bool obj_known_digging(struct object *obj, int deciturns[]) { struct player_state state; int i; int chances[DIGGING_MAX]; int slot = wield_slot(obj); struct object *current = slot_object(player, slot); /* Doesn't remotely resemble a digger */ if (!tval_is_wearable(obj) || (!tval_is_melee_weapon(obj) && (obj->modifiers[OBJ_MOD_TUNNEL] <= 0))) return false; /* Player has no digging info */ if (!tval_is_melee_weapon(obj) && !obj->known->modifiers[OBJ_MOD_TUNNEL]) return false; /* Pretend we're wielding the object */ player->body.slots[slot].obj = obj; /* Calculate the player's hypothetical state */ memcpy(&state, &player->state, sizeof(state)); state.stat_ind[STAT_STR] = 0; //Hack - NRM state.stat_ind[STAT_DEX] = 0; //Hack - NRM calc_bonuses(player, &state, true, false); /* Stop pretending */ player->body.slots[slot].obj = current; calc_digging_chances(&state, chances); /* Digging chance is out of 1600 */ for (i = DIGGING_RUBBLE; i < DIGGING_MAX; i++) { int chance = MIN(1600, chances[i]); deciturns[i] = chance ? (16000 / chance) : 0; } 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); }
/** * 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, row, col, stat; struct object *obj; bitflag f[OF_SIZE]; byte a; char c; /* Row */ row = 2; /* Column */ col = 26; /* Header */ c_put_str(COLOUR_WHITE, "abcdefghijkl@", row-1, col); /* Process equipment */ for (i = 0; i < player->body.count; ++i) { /* Get the object */ obj = slot_object(player, i); if (!obj) { col++; continue; } /* Get the "known" flags */ object_flags_known(obj, f); /* Initialize color based on sign of modifier. */ for (stat = OBJ_MOD_MIN_STAT; stat < OBJ_MOD_MIN_STAT + STAT_MAX; stat++) { /* Default */ a = COLOUR_SLATE; c = '.'; /* Boosted or reduced */ if (obj->modifiers[stat] > 0) { /* Good */ a = COLOUR_L_GREEN; /* Label boost */ if (obj->modifiers[stat] < 10) c = I2D(obj->modifiers[stat]); } else if (obj->modifiers[stat] < 0) { /* Bad */ a = COLOUR_RED; /* Label boost */ if (obj->modifiers[stat] > -10) c = I2D(-(obj->modifiers[stat])); } /* Sustain */ if (of_has(f, sustain_flag(stat))) { /* Dark green */ a = COLOUR_GREEN; /* Convert '.' to 's' */ if (c == '.') c = 's'; } if ((c == '.') && obj && !object_flag_is_known(obj, sustain_flag(stat))) c = '?'; /* Dump proper character */ Term_putch(col, row+stat, a, c); } /* Advance */ col++; } /* Player flags */ player_flags(player, f); /* Check stats */ for (stat = 0; stat < STAT_MAX; ++stat) { /* Default */ a = COLOUR_SLATE; c = '.'; /* Sustain */ if (of_has(f, sustain_flag(stat))) { /* Dark green "s" */ a = COLOUR_GREEN; c = 's'; } /* Dump */ Term_putch(col, row+stat, a, c); } /* Column */ col = 26; /* Footer */ c_put_str(COLOUR_WHITE, "abcdefghijkl@", row+6, col); /* Equippy */ display_player_equippy(row+7, col); }
/** * Write a character dump */ void write_character_dump(ang_file *fff) { int i, x, y; int a; wchar_t c; struct store *home = &stores[STORE_HOME]; struct object **home_list = mem_zalloc(sizeof(struct object *) * z_info->store_inven_max); char o_name[80]; char buf[1024]; char *p; /* Begin dump */ file_putf(fff, " [%s Character Dump]\n\n", buildid); /* Display player */ display_player(0); /* Dump part of the screen */ for (y = 1; y < 23; y++) { p = buf; /* Dump each row */ for (x = 0; x < 79; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ p += wctomb(p, c); } /* Back up over spaces */ while ((p > buf) && (p[-1] == ' ')) --p; /* Terminate */ *p = '\0'; /* End the row */ file_putf(fff, "%s\n", buf); } /* Skip a line */ file_putf(fff, "\n"); /* Display player */ display_player(1); /* Dump part of the screen */ for (y = 11; y < 20; y++) { p = buf; /* Dump each row */ for (x = 0; x < 39; x++) { /* Get the attr/char */ (void)(Term_what(x, y, &a, &c)); /* Dump it */ p += wctomb(p, c); } /* Back up over spaces */ while ((p > buf) && (p[-1] == ' ')) --p; /* Terminate */ *p = '\0'; /* End the row */ file_putf(fff, "%s\n", buf); } /* Skip a line */ file_putf(fff, "\n"); /* Dump part of the screen */ for (y = 11; y < 20; y++) { p = buf; /* Dump each row */ for (x = 0; x < 39; x++) { /* Get the attr/char */ (void)(Term_what(x + 40, y, &a, &c)); /* Dump it */ p += wctomb(p, c); } /* Back up over spaces */ while ((p > buf) && (p[-1] == ' ')) --p; /* Terminate */ *p = '\0'; /* End the row */ file_putf(fff, "%s\n", buf); } /* Skip some lines */ file_putf(fff, "\n\n"); /* If dead, dump last messages -- Prfnoff */ if (player->is_dead) { i = messages_num(); if (i > 15) i = 15; file_putf(fff, " [Last Messages]\n\n"); while (i-- > 0) { file_putf(fff, "> %s\n", message_str((s16b)i)); } file_putf(fff, "\nKilled by %s.\n\n", player->died_from); } /* Dump the equipment */ file_putf(fff, " [Character Equipment]\n\n"); for (i = 0; i < player->body.count; i++) { struct object *obj = slot_object(player, i); if (!obj) continue; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", gear_to_label(obj), o_name); object_info_chardump(fff, obj, 5, 72); } file_putf(fff, "\n\n"); /* Dump the inventory */ file_putf(fff, "\n\n [Character Inventory]\n\n"); for (i = 0; i < z_info->pack_size; i++) { struct object *obj = player->upkeep->inven[i]; if (!obj) break; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", gear_to_label(obj), o_name); object_info_chardump(fff, obj, 5, 72); } file_putf(fff, "\n\n"); /* Dump the quiver */ file_putf(fff, "\n\n [Character Quiver]\n\n"); for (i = 0; i < z_info->quiver_size; i++) { struct object *obj = player->upkeep->quiver[i]; if (!obj) continue; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", gear_to_label(obj), o_name); object_info_chardump(fff, obj, 5, 72); } file_putf(fff, "\n\n"); /* Dump the Home -- if anything there */ store_stock_list(home, home_list, z_info->store_inven_max); if (home->stock_num) { /* Header */ file_putf(fff, " [Home Inventory]\n\n"); /* Dump all available items */ for (i = 0; i < z_info->store_inven_max; i++) { struct object *obj = home_list[i]; if (!obj) break; object_desc(o_name, sizeof(o_name), obj, ODESC_PREFIX | ODESC_FULL); file_putf(fff, "%c) %s\n", I2A(i), o_name); object_info_chardump(fff, obj, 5, 72); } /* Add an empty line */ file_putf(fff, "\n\n"); } /* Dump character history */ dump_history(fff); file_putf(fff, "\n\n"); /* Dump options */ file_putf(fff, " [Options]\n\n"); /* Dump options */ for (i = 0; i < OP_MAX; i++) { int opt; const char *title = ""; switch (i) { case OP_INTERFACE: title = "User interface"; break; case OP_BIRTH: title = "Birth"; break; default: continue; } file_putf(fff, " [%s]\n\n", title); for (opt = 0; opt < OPT_MAX; opt++) { if (option_type(opt) != i) continue; file_putf(fff, "%-45s: %s (%s)\n", option_desc(opt), player->opts.opt[opt] ? "yes" : "no ", option_name(opt)); } /* Skip some lines */ file_putf(fff, "\n"); } mem_free(home_list); }
/** * Attack the monster at the given location with a single blow. */ static bool py_attack_real(int y, int x, bool *fear) { size_t i; /* Information about the target of the attack */ struct monster *mon = square_monster(cave, y, x); char m_name[80]; bool stop = FALSE; /* The weapon used */ struct object *obj = equipped_item_by_slot_name(player, "weapon"); /* Information about the attack */ int chance = py_attack_hit_chance(obj); bool do_quake = FALSE; bool success = FALSE; /* Default to punching for one damage */ char verb[20]; int dmg = 1; u32b msg_type = MSG_HIT; /* Default to punching for one damage */ my_strcpy(verb, "punch", sizeof(verb)); /* Extract monster name (or "it") */ monster_desc(m_name, sizeof(m_name), mon, MDESC_OBJE | MDESC_IND_HID | MDESC_PRO_HID); /* Auto-Recall if possible and visible */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) monster_race_track(player->upkeep, mon->race); /* Track a new monster */ if (mflag_has(mon->mflag, MFLAG_VISIBLE)) health_track(player->upkeep, mon); /* Handle player fear (only for invisible monsters) */ if (player_of_has(player, OF_AFRAID)) { msgt(MSG_AFRAID, "You are too afraid to attack %s!", m_name); return FALSE; } /* Disturb the monster */ mon_clear_timed(mon, MON_TMD_SLEEP, MON_TMD_FLG_NOMESSAGE, FALSE); /* See if the player hit */ success = test_hit(chance, mon->race->ac, mflag_has(mon->mflag, MFLAG_VISIBLE)); /* If a miss, skip this hit */ if (!success) { msgt(MSG_MISS, "You miss %s.", m_name); return FALSE; } /* Handle normal weapon */ if (obj) { int j; const struct brand *b = NULL; const struct slay *s = NULL; my_strcpy(verb, "hit", sizeof(verb)); /* Get the best attack from all slays or * brands on all non-launcher equipment */ for (j = 2; j < player->body.count; j++) { struct object *obj = slot_object(player, j); if (obj) improve_attack_modifier(obj, mon, &b, &s, verb, FALSE, TRUE, FALSE); } improve_attack_modifier(obj, mon, &b, &s, verb, FALSE, TRUE, FALSE); dmg = melee_damage(obj, b, s); dmg = critical_norm(obj->weight, obj->to_h, dmg, &msg_type); /* Learn by use for the weapon */ object_notice_attack_plusses(obj); if (player_of_has(player, OF_IMPACT) && dmg > 50) { do_quake = TRUE; equip_notice_flag(player, OF_IMPACT); } } /* Learn by use for other equipped items */ equip_notice_on_attack(player); /* Apply the player damage bonuses */ dmg += player_damage_bonus(&player->state); /* No negative damage; change verb if no damage done */ if (dmg <= 0) { dmg = 0; msg_type = MSG_MISS; my_strcpy(verb, "fail to harm", sizeof(verb)); } for (i = 0; i < N_ELEMENTS(melee_hit_types); i++) { const char *dmg_text = ""; if (msg_type != melee_hit_types[i].msg) continue; if (OPT(show_damage)) dmg_text = format(" (%d)", dmg); if (melee_hit_types[i].text) msgt(msg_type, "You %s %s%s. %s", verb, m_name, dmg_text, melee_hit_types[i].text); else msgt(msg_type, "You %s %s%s.", verb, m_name, dmg_text); } /* Pre-damage side effects */ blow_side_effects(player, mon); /* Damage, check for fear and death */ stop = mon_take_hit(mon, dmg, fear, NULL); if (stop) (*fear) = FALSE; /* Post-damage effects */ if (blow_after_effects(y, x, do_quake)) stop = TRUE; return stop; }
static void display_resistance_panel(const struct player_flag_record *rec, size_t size, const region *bounds) { size_t i; int j; int col = bounds->col; int row = bounds->row; int res_cols = 5 + 2 + player->body.count; Term_putstr(col, row++, res_cols, COLOUR_WHITE, " abcdefghijkl@"); for (i = 0; i < size - 3; i++, row++) { byte name_attr = COLOUR_WHITE; Term_gotoxy(col + 6, row); /* Repeated extraction of flags is inefficient but more natural */ for (j = 0; j <= player->body.count; j++) { bitflag f[OF_SIZE]; byte attr = COLOUR_WHITE | (j % 2) * 8; /* alternating columns */ char sym = '.'; bool res = false, imm = false, vul = false, rune = false; bool timed = false; bool known = false; /* Object or player info? */ if (j < player->body.count) { int index = 0; struct object *obj = slot_object(player, j); struct curse_data *curse = obj ? obj->curses : NULL; while (obj) { /* Wipe flagset */ of_wipe(f); /* Get known properties */ object_flags_known(obj, f); if (rec[i].element != -1) { known = object_element_is_known(obj, rec[i].element); } else if (rec[i].flag != -1) { known = object_flag_is_known(obj, rec[i].flag); } else { known = true; } /* Get resistance, immunity and vulnerability info */ if (rec[i].mod != -1) { if (obj->modifiers[rec[i].mod] != 0) { res = true; } rune = (player->obj_k->modifiers[rec[i].mod] == 1); } else if (rec[i].flag != -1) { if (of_has(f, rec[i].flag)) { res = true; } rune = of_has(player->obj_k->flags, rec[i].flag); } else if (rec[i].element != -1) { if (known) { if (obj->el_info[rec[i].element].res_level == 3) { imm = true; } if (obj->el_info[rec[i].element].res_level == 1) { res = true; } if (obj->el_info[rec[i].element].res_level == -1) { vul = true; } } rune = (player->obj_k->el_info[rec[i].element].res_level == 1); } /* Move to any unprocessed curse object */ if (curse) { index++; obj = NULL; while (index < z_info->curse_max) { if (curse[index].power) { obj = curses[index].obj; break; } else { index++; } } } else { obj = NULL; } } } else { player_flags(player, f); known = true; /* Timed flags only in the player column */ if (rec[i].tmd_flag >= 0) { timed = player->timed[rec[i].tmd_flag] ? true : false; /* There has to be one special case... */ if ((rec[i].tmd_flag == TMD_AFRAID) && (player->timed[TMD_TERROR])) timed = true; } /* Set which (if any) symbol and color are used */ if (rec[i].mod != -1) { int k; /* Shape modifiers */ for (k = 0; k < OBJ_MOD_MAX; k++) { res = (player->shape->modifiers[i] > 0); vul = (player->shape->modifiers[i] > 0); } /* Messy special cases */ if (rec[i].mod == OBJ_MOD_INFRA) res |= (player->race->infra > 0); if (rec[i].mod == OBJ_MOD_TUNNEL) res |= (player->race->r_skills[SKILL_DIGGING] > 0); } else if (rec[i].flag != -1) { res = of_has(f, rec[i].flag); res |= (of_has(player->shape->flags, rec[i].flag) && of_has(player->obj_k->flags, rec[i].flag)); } else if (rec[i].element != -1) { int el = rec[i].element; imm = (player->race->el_info[el].res_level == 3) || ((player->shape->el_info[el].res_level == 3) && (player->obj_k->el_info[el].res_level)); res = (player->race->el_info[el].res_level == 1) || ((player->shape->el_info[el].res_level == 1) && (player->obj_k->el_info[el].res_level)); vul = (player->race->el_info[el].res_level == -1) || ((player->shape->el_info[el].res_level == -1) && (player->obj_k->el_info[el].res_level)); } } /* Colour the name appropriately */ if (imm) { name_attr = COLOUR_GREEN; } else if (res && (name_attr != COLOUR_GREEN)) { name_attr = COLOUR_L_BLUE; } else if (vul && (name_attr != COLOUR_GREEN)) { name_attr = COLOUR_RED; } /* Set the symbols and print them */ if (vul) { sym = '-'; } else if (imm) { sym = '*'; } else if (res) { sym = '+'; } else if (timed) { sym = '!'; attr = COLOUR_L_GREEN; } else if ((j < player->body.count) && slot_object(player, j) && !known && !rune) { sym = '?'; } Term_addch(attr, sym); } /* Check if the rune is known */ if (((rec[i].mod >= 0) && (player->obj_k->modifiers[rec[i].mod] == 0)) || ((rec[i].flag >= 0) && !of_has(player->obj_k->flags, rec[i].flag)) || ((rec[i].element >= 0) && (player->obj_k->el_info[rec[i].element].res_level == 0))) { name_attr = COLOUR_SLATE; } Term_putstr(col, row, 6, name_attr, format("%5s:", rec[i].name)); } Term_putstr(col, row++, res_cols, COLOUR_WHITE, " abcdefghijkl@"); /* Equippy */ display_player_equippy(row++, col + 6); }
/** * Let the user select an object, save its address * * Return true only if an acceptable item was chosen by the user. * * The user is allowed to choose acceptable items from the equipment, * inventory, quiver, or floor, respectively, if the proper flag was given, * and there are any acceptable items in that location. * * The equipment, inventory or quiver are displayed (even if no acceptable * items are in that location) if the proper flag was given. * * If there are no acceptable items available anywhere, and "str" is * not NULL, then it will be used as the text of a warning message * before the function returns. * * If a legal item is selected , we save it in "choice" and return true. * * If no item is available, we do nothing to "choice", and we display a * warning message, using "str" if available, and return false. * * If no item is selected, we do nothing to "choice", and return false. * * Global "player->upkeep->command_wrk" is used to choose between * equip/inven/quiver/floor listings. It is equal to USE_INVEN or USE_EQUIP or * USE_QUIVER or USE_FLOOR, except when this function is first called, when it * is equal to zero, which will cause it to be set to USE_INVEN. * * We always erase the prompt when we are done, leaving a blank line, * or a warning message, if appropriate, if no items are available. * * Note that only "acceptable" floor objects get indexes, so between two * commands, the indexes of floor objects may change. XXX XXX XXX */ bool textui_get_item(struct object **choice, const char *pmt, const char *str, cmd_code cmd, item_tester tester, int mode) { bool use_inven = ((mode & USE_INVEN) ? true : false); bool use_equip = ((mode & USE_EQUIP) ? true : false); bool use_quiver = ((mode & USE_QUIVER) ? true : false); bool use_floor = ((mode & USE_FLOOR) ? true : false); bool quiver_tags = ((mode & QUIVER_TAGS) ? true : false); bool allow_inven = false; bool allow_equip = false; bool allow_quiver = false; bool allow_floor = false; bool toggle = false; int floor_max = z_info->floor_size; int floor_num; floor_list = mem_zalloc(floor_max * sizeof(*floor_list)); olist_mode = 0; item_mode = mode; item_cmd = cmd; tester_m = tester; prompt = pmt; allow_all = str ? false : true; /* Object list display modes */ if (mode & SHOW_FAIL) olist_mode |= OLIST_FAIL; else olist_mode |= OLIST_WEIGHT; if (mode & SHOW_PRICES) olist_mode |= OLIST_PRICE; if (mode & SHOW_EMPTY) olist_mode |= OLIST_SEMPTY; if (mode & SHOW_QUIVER) olist_mode |= OLIST_QUIVER; if (mode & SHOW_RECHARGE) olist_mode |= OLIST_RECHARGE; /* Paranoia XXX XXX XXX */ event_signal(EVENT_MESSAGE_FLUSH); /* Full inventory */ i1 = 0; i2 = z_info->pack_size - 1; /* Forbid inventory */ if (!use_inven) i2 = -1; /* Restrict inventory indexes */ while ((i1 <= i2) && (!object_test(tester, player->upkeep->inven[i1]))) i1++; while ((i1 <= i2) && (!object_test(tester, player->upkeep->inven[i2]))) i2--; /* Accept inventory */ if ((i1 <= i2) || allow_all) allow_inven = true; else if (item_mode & USE_INVEN) item_mode -= USE_INVEN; /* Full equipment */ e1 = 0; e2 = player->body.count - 1; /* Forbid equipment */ if (!use_equip) e2 = -1; /* Restrict equipment indexes unless starting with no command */ if ((cmd != CMD_NULL) || (tester != NULL)) { while ((e1 <= e2) && (!object_test(tester, slot_object(player, e1)))) e1++; while ((e1 <= e2) && (!object_test(tester, slot_object(player, e2)))) e2--; } /* Accept equipment */ if ((e1 <= e2) || allow_all) allow_equip = true; else if (item_mode & USE_EQUIP) item_mode -= USE_EQUIP; /* Restrict quiver indexes */ q1 = 0; q2 = z_info->quiver_size - 1; /* Forbid quiver */ if (!use_quiver) q2 = -1; /* Restrict quiver indexes */ while ((q1 <= q2) && (!object_test(tester, player->upkeep->quiver[q1]))) q1++; while ((q1 <= q2) && (!object_test(tester, player->upkeep->quiver[q2]))) q2--; /* Accept quiver */ if ((q1 <= q2) || allow_all) allow_quiver = true; else if (item_mode & USE_QUIVER) item_mode -= USE_QUIVER; /* Scan all non-gold objects in the grid */ floor_num = scan_floor(floor_list, floor_max, OFLOOR_TEST | OFLOOR_SENSE | OFLOOR_VISIBLE, tester); /* Full floor */ f1 = 0; f2 = floor_num - 1; /* Forbid floor */ if (!use_floor) f2 = -1; /* Restrict floor indexes */ while ((f1 <= f2) && (!object_test(tester, floor_list[f1]))) f1++; while ((f1 <= f2) && (!object_test(tester, floor_list[f2]))) f2--; /* Accept floor */ if ((f1 <= f2) || allow_all) allow_floor = true; else if (item_mode & USE_FLOOR) item_mode -= USE_FLOOR; /* Require at least one legal choice */ if (allow_inven || allow_equip || allow_quiver || allow_floor) { /* Start where requested if possible */ if ((player->upkeep->command_wrk == USE_EQUIP) && allow_equip) player->upkeep->command_wrk = USE_EQUIP; else if ((player->upkeep->command_wrk == USE_INVEN) && allow_inven) player->upkeep->command_wrk = USE_INVEN; else if ((player->upkeep->command_wrk == USE_QUIVER) && allow_quiver) player->upkeep->command_wrk = USE_QUIVER; else if ((player->upkeep->command_wrk == USE_FLOOR) && allow_floor) player->upkeep->command_wrk = USE_FLOOR; /* If we are obviously using the quiver then start on quiver */ else if (quiver_tags && allow_quiver) player->upkeep->command_wrk = USE_QUIVER; /* Otherwise choose whatever is allowed */ else if (use_inven && allow_inven) player->upkeep->command_wrk = USE_INVEN; else if (use_equip && allow_equip) player->upkeep->command_wrk = USE_EQUIP; else if (use_quiver && allow_quiver) player->upkeep->command_wrk = USE_QUIVER; else if (use_floor && allow_floor) player->upkeep->command_wrk = USE_FLOOR; /* If nothing to choose, use (empty) inventory */ else player->upkeep->command_wrk = USE_INVEN; while (true) { int j; int ni = 0; int ne = 0; /* If inven or equip is on the main screen, and only one of them * is slated for a subwindow, we should show the opposite there */ for (j = 0; j < ANGBAND_TERM_MAX; j++) { /* Unused */ if (!angband_term[j]) continue; /* Count windows displaying inven */ if (window_flag[j] & (PW_INVEN)) ni++; /* Count windows displaying equip */ if (window_flag[j] & (PW_EQUIP)) ne++; } /* Are we in the situation where toggling makes sense? */ if ((ni && !ne) || (!ni && ne)) { if (player->upkeep->command_wrk == USE_EQUIP) { if ((ne && !toggle) || (ni && toggle)) { /* Main screen is equipment, so is subwindow */ toggle_inven_equip(); toggle = !toggle; } } else if (player->upkeep->command_wrk == USE_INVEN) { if ((ni && !toggle) || (ne && toggle)) { /* Main screen is inventory, so is subwindow */ toggle_inven_equip(); toggle = !toggle; } } else { /* Quiver or floor, go back to the original */ if (toggle) { toggle_inven_equip(); toggle = !toggle; } } } /* Redraw */ player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); /* Redraw windows */ redraw_stuff(player); /* Save screen */ screen_save(); /* Build object list */ wipe_obj_list(); if (player->upkeep->command_wrk == USE_INVEN) build_obj_list(i2, player->upkeep->inven, tester_m, olist_mode); else if (player->upkeep->command_wrk == USE_EQUIP) build_obj_list(e2, NULL, tester_m, olist_mode); else if (player->upkeep->command_wrk == USE_QUIVER) build_obj_list(q2, player->upkeep->quiver, tester_m,olist_mode); else if (player->upkeep->command_wrk == USE_FLOOR) build_obj_list(f2, floor_list, tester_m, olist_mode); /* Show the prompt */ menu_header(); if (pmt) { prt(pmt, 0, 0); prt(header, 0, strlen(pmt) + 1); } /* No menu change request */ newmenu = false; /* Get an item choice */ *choice = item_menu(cmd, MAX(pmt ? strlen(pmt) : 0, 15), mode); /* Fix the screen */ screen_load(); /* Update */ player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); redraw_stuff(player); /* Clear the prompt line */ prt("", 0, 0); /* We have a selection, or are backing out */ if (*choice || !newmenu) { if (toggle) toggle_inven_equip(); break; } } } else { /* Warning if needed */ if (str) msg("%s", str); *choice = NULL; } /* Clean up */ player->upkeep->command_wrk = 0; mem_free(floor_list); /* Result */ return (*choice != NULL) ? true : false; }
static void display_resistance_panel(const struct player_flag_record *rec, size_t size, const region *bounds) { size_t i; int j; int col = bounds->col; int row = bounds->row; int res_cols = 5 + 2 + player->body.count; Term_putstr(col, row++, res_cols, COLOUR_WHITE, " abcdefghijkl@"); for (i = 0; i < size - 3; i++, row++) { byte name_attr = COLOUR_WHITE; Term_gotoxy(col + 6, row); /* Repeated extraction of flags is inefficient but more natural */ for (j = 0; j <= player->body.count; j++) { struct object *obj; bitflag f[OF_SIZE]; byte attr = COLOUR_WHITE | (j % 2) * 8; /* alternating columns */ char sym = '.'; bool res = false, imm = false, vul = false, rune = false; bool timed = false; bool known = false; /* Wipe flagset */ of_wipe(f); /* Get the object or player info */ obj = j < player->body.count ? slot_object(player, j) : NULL; if (j < player->body.count && obj) { /* Get known properties */ object_flags_known(obj, f); if (rec[i].element != -1) known = object_element_is_known(obj, rec[i].element); else if (rec[i].flag != -1) known = object_flag_is_known(obj, rec[i].flag); else known = true; } else if (j == player->body.count) { player_flags(player, f); known = true; /* Timed flags only in the player column */ if (rec[i].tmd_flag >= 0) { timed = player->timed[rec[i].tmd_flag] ? true : false; /* There has to be one special case... */ if ((rec[i].tmd_flag == TMD_AFRAID) && (player->timed[TMD_TERROR])) timed = true; } } /* Set which (if any) symbol and color are used */ if (rec[i].mod != -1) { if (j != player->body.count) res = (obj && (obj->modifiers[rec[i].mod] != 0)); else { /* Messy special cases */ if (rec[i].mod == OBJ_MOD_INFRA) res = (player->race->infra > 0); if (rec[i].mod == OBJ_MOD_TUNNEL) res = (player->race->r_skills[SKILL_DIGGING] > 0); } rune = (player->obj_k->modifiers[rec[i].mod] == 1); } else if (rec[i].flag != -1) { res = of_has(f, rec[i].flag); rune = of_has(player->obj_k->flags, rec[i].flag); } else if (rec[i].element != -1) { if (j != player->body.count) { imm = obj && known && (obj->el_info[rec[i].element].res_level == 3); res = obj && known && (obj->el_info[rec[i].element].res_level == 1); vul = obj && known && (obj->el_info[rec[i].element].res_level == -1); } else { imm = player->race->el_info[rec[i].element].res_level == 3; res = player->race->el_info[rec[i].element].res_level == 1; vul = player->race->el_info[rec[i].element].res_level == -1; } rune = (player->obj_k->el_info[rec[i].element].res_level == 1); } /* Set the symbols and print them */ if (imm) name_attr = COLOUR_GREEN; else if (!rune) name_attr = COLOUR_SLATE; else if (res && (name_attr != COLOUR_GREEN)) name_attr = COLOUR_L_BLUE; if (vul) sym = '-'; else if (imm) sym = '*'; else if (res) sym = '+'; else if (timed) { sym = '!'; attr = COLOUR_L_GREEN; } else if ((j < player->body.count) && obj && !known && !rune) sym = '?'; Term_addch(attr, sym); } Term_putstr(col, row, 6, name_attr, format("%5s:", rec[i].name)); } Term_putstr(col, row++, res_cols, COLOUR_WHITE, " abcdefghijkl@"); /* Equippy */ display_player_equippy(row++, col + 6); }
void ResourceModel::fillMaterialInfo(Lumix::Material* material, Node& node) { auto object = Object<Lumix::Material>(material, &node); node.m_getter = [material]() -> QVariant { return material->getPath().c_str(); }; node.m_name = "Material"; object.getNode().onCreateEditor = [this, material]( QWidget* parent, const QStyleOptionViewItem&) -> QWidget* { auto* widget = new QWidget(parent); auto* layout = new QHBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); auto* label = new QLabel(material->getPath().c_str(), widget); layout->addWidget(label); layout->addStretch(); QPushButton* button = new QPushButton("Save", widget); connect(button, &QPushButton::clicked, [this, material]() { saveMaterial(material); }); layout->addWidget(button); return widget; }; object.getNode().enablePeristentEditor(); object.property("Alpha cutout", &Lumix::Material::isAlphaCutout, &Lumix::Material::enableAlphaCutout) .property("Backface culling", &Lumix::Material::isBackfaceCulling, &Lumix::Material::enableBackfaceCulling) .property("Shadow receiver", &Lumix::Material::isShadowReceiver, &Lumix::Material::enableShadowReceiving) .property("Shininess", &Lumix::Material::getShininess, &Lumix::Material::setShininess) .propertyColor("Specular", &Lumix::Material::getSpecular, &Lumix::Material::setSpecular) .property( "Z test", &Lumix::Material::isZTest, &Lumix::Material::enableZTest) .property("Shader", [](Lumix::Material* material) -> QVariant { return material->getShader() ? material->getShader()->getPath().c_str() : ""; }, [this](Lumix::Material* material, QVariant value) { setMaterialShader(material, value.toString()); }); auto shader_node = object.getNode().m_children.back(); shader_node->enablePeristentEditor(); shader_node->onCreateEditor = [shader_node](QWidget* parent, const QStyleOptionViewItem&) { auto input = new FileInput(parent); input->setValue(shader_node->m_getter().toString()); input->connect(input, &FileInput::valueChanged, [shader_node, input]() { shader_node->m_setter(input->value()); }); return input; }; for (int i = 0; i < material->getUniformCount(); ++i) { auto& uniform = material->getUniform(i); QString name = uniform.m_name; object.property(uniform.m_name, [name](Lumix::Material* material) -> QVariant { Lumix::Material::Uniform* uniform = getMaterialUniform(material, name); if (uniform) { switch (uniform->m_type) { case Lumix::Material::Uniform::FLOAT: return uniform->m_float; } } return QVariant(); }, [name](Lumix::Material* material, const QVariant& value) { Lumix::Material::Uniform* uniform = getMaterialUniform(material, name); if (uniform) { switch (uniform->m_type) { case Lumix::Material::Uniform::FLOAT: uniform->m_float = value.toFloat(); break; } } }); } for (int i = 0; material->getShader() && i < material->getShader()->getTextureSlotCount(); ++i) { const auto& slot = material->getShader()->getTextureSlot(i); Object<const Lumix::Shader::TextureSlot> slot_object( &slot, &object.getNode().addChild(slot.m_name)); auto& node = slot_object.getNode(); auto texture = material->getTexture(i); if (texture) { fillTextureInfo(texture, node); } node.m_name = slot.m_name; node.m_getter = [texture]() -> QVariant { return texture ? texture->getPath().c_str() : ""; }; node.onCreateEditor = [&node, i, texture, material]( QWidget* parent, const QStyleOptionViewItem&) -> QWidget* { auto input = new FileInput(parent); input->setValue(texture ? texture->getPath().c_str() : ""); input->connect(input, &FileInput::valueChanged, [&node, input]() { node.m_setter(input->value()); }); return input; }; node.m_setter = [material, i](const QVariant& value) { if (value.isValid()) { material->setTexturePath( i, Lumix::Path(value.toString().toLatin1().data())); } }; node.enablePeristentEditor(); } }
/** * 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, int *brand_damage, int *slay_damage, 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; bool *total_brands; bool *total_slays; bool has_brands_or_slays = false; 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 melee_adj_mult = ammo ? 0 : 1; 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 */ memcpy(&state, &player->state, sizeof(state)); state.stat_ind[STAT_STR] = 0; //Hack - NRM state.stat_ind[STAT_DEX] = 0; //Hack - NRM calc_bonuses(player, &state, true, false); /* Stop pretending */ player->body.slots[weapon_slot].obj = current_weapon; /* Finish if dice not known */ dice = obj->known->dd; sides = obj->known->ds; if (!dice || !sides) return false; /* Calculate damage */ dam = ((sides + 1) * dice * 5); if (weapon) { xtra_postcrit = state.to_d * 10; xtra_precrit += obj->known->to_d * 10; plus += obj->known->to_h; calculate_melee_crits(&state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); old_blows = state.num_blows; } else { /* Ammo */ plus += obj->known->to_h; calculate_missile_crits(&player->state, obj->weight, plus, &crit_mult, &crit_add, &crit_div); dam += (obj->known->to_d * 10); dam += (bow->known->to_d * 10); } if (ammo) multiplier = player->state.ammo_mult; /* Get the brands */ total_brands = mem_zalloc(z_info->brand_max * sizeof(bool)); copy_brands(&total_brands, obj->known->brands); if (ammo && bow->known) copy_brands(&total_brands, bow->known->brands); /* Get the slays */ total_slays = mem_zalloc(z_info->slay_max * sizeof(bool)); copy_slays(&total_slays, obj->known->slays); if (ammo && bow->known) copy_slays(&total_slays, bow->known->slays); /* 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); 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 */ copy_brands(&total_brands, slot_obj->known->brands); copy_slays(&total_slays, slot_obj->known->slays); } } /* Get damage for each brand on the objects */ for (i = 1; i < z_info->brand_max; i++) { /* Must have the brand */ if (total_brands[i]) has_brands_or_slays = true; else continue; /* Include bonus damage and brand in stated average */ total_dam = dam * (multiplier + brands[i].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[i] = total_dam; } /* Get damage for each slay on the objects */ for (i = 1; i < z_info->slay_max; i++) { /* Must have the slay */ if (total_slays[i]) has_brands_or_slays = true; else continue; /* Include bonus damage and slay in stated average */ total_dam = dam * (multiplier + slays[i].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[i] = 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; mem_free(total_brands); mem_free(total_slays); return has_brands_or_slays; }
/** * Acid has hit the player, attempt to affect some armor. * * Note that the "base armor" of an object never changes. * If any armor is damaged (or resists), the player takes less damage. */ int minus_ac(struct player *p) { int i, count = 0; struct object *obj = NULL; char o_name[80]; /* Avoid crash during monster power calculations */ if (!p->gear) return false; /* Count the armor slots */ for (i = 0; i < player->body.count; i++) { /* Ignore non-armor */ if (slot_type_is(i, EQUIP_WEAPON)) continue; if (slot_type_is(i, EQUIP_BOW)) continue; if (slot_type_is(i, EQUIP_RING)) continue; if (slot_type_is(i, EQUIP_AMULET)) continue; if (slot_type_is(i, EQUIP_LIGHT)) continue; /* Add */ count++; } /* Pick one at random */ for (i = player->body.count - 1; i >= 0; i--) { /* Ignore non-armor */ if (slot_type_is(i, EQUIP_WEAPON)) continue; if (slot_type_is(i, EQUIP_BOW)) continue; if (slot_type_is(i, EQUIP_RING)) continue; if (slot_type_is(i, EQUIP_AMULET)) continue; if (slot_type_is(i, EQUIP_LIGHT)) continue; if (one_in_(count--)) break; } /* Get the item */ obj = slot_object(player, i); /* Nothing to damage */ if (!obj) return (false); /* No damage left to be done */ if (obj->ac + obj->to_a <= 0) return (false); /* Describe */ object_desc(o_name, sizeof(o_name), obj, ODESC_BASE); /* Object resists */ if (obj->el_info[ELEM_ACID].flags & EL_INFO_IGNORE) { msg("Your %s is unaffected!", o_name); return (true); } /* Message */ msg("Your %s is damaged!", o_name); /* Damage the item */ obj->to_a--; if (p->obj_k->to_a) obj->known->to_a = obj->to_a; p->upkeep->update |= (PU_BONUS); p->upkeep->redraw |= (PR_EQUIP); /* Item was damaged */ return (true); }
/** * Gets information about the number of blows possible for the player with * the given object. * * Fills in whether the object is too_heavy to wield effectively, * and the possible_blows[] information of .str_plus and .dex_plus needed * to achieve the approximate number of blows in centiblows. * * `max_blows` must be at least 1 to hold the current number of blows * `possible_blows` must be at least [`max_blows`] in size, and will be limited * to that number of entries. The theoretical maximum is STAT_RANGE * 2 if * an extra blow/speed boost was given for each combination of STR and DEX. * * Returns the number of entries made in the possible_blows[] table, or 0 * if the object is not a weapon. * * Note that the results are meaningless if called on a fake ego object as * the actual ego may have different properties. */ static int obj_known_blows(const struct object *obj, int max_num, struct blow_info possible_blows[]) { 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; struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current_weapon = slot_object(player, weapon_slot); int num = 0; /* Not a weapon - no blows! */ if (!tval_is_melee_weapon(obj)) return 0; /* Pretend we're wielding the object */ 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; /* First entry is always the current num of blows. */ possible_blows[num].str_plus = 0; possible_blows[num].dex_plus = 0; possible_blows[num].centiblows = state.num_blows; num++; /* Check to see if extra STR or DEX would yield extra blows */ old_blows = state.num_blows; extra_blows = 0; /* Start with blows from the weapon being examined */ if (object_this_mod_is_visible(obj, OBJ_MOD_BLOWS)) extra_blows += obj->modifiers[OBJ_MOD_BLOWS]; /* Then we need to look for extra blows on other items, as * state does not track these */ for (i = 0; i < player->body.count; i++) { struct object *helper = slot_object(player, i); if ((i == slot_by_name(player, "weapon")) || !helper || !helper->kind) continue; if (object_this_mod_is_visible(helper, OBJ_MOD_BLOWS)) extra_blows += helper->modifiers[OBJ_MOD_BLOWS]; } dex_plus_bound = STAT_RANGE - state.stat_ind[STAT_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[STAT_STR]; /* 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++) { if (num == max_num) return num; state.stat_ind[STAT_STR] += str_plus; state.stat_ind[STAT_DEX] += dex_plus; new_blows = calc_blows(player, obj, &state, extra_blows); state.stat_ind[STAT_STR] -= str_plus; state.stat_ind[STAT_DEX] -= dex_plus; /* 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)) { possible_blows[num].str_plus = str_plus; possible_blows[num].dex_plus = dex_plus; possible_blows[num].centiblows = new_blows / 10; possible_blows[num].centiblows *= 10; num++; 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)) { possible_blows[num].str_plus = str_plus; possible_blows[num].dex_plus = dex_plus; possible_blows[num].centiblows = new_blows; num++; str_faster = str_plus; } } } return num; }
/** * Gets information about the number of blows possible for the player with * the given object. * * Fills in whether the object is too heavy to wield effectively, * and the possible_blows[] information of .str_plus and .dex_plus needed * to achieve the approximate number of blows in centiblows. * * `max_blows` must be at least 1 to hold the current number of blows * `possible_blows` must be at least [`max_blows`] in size, and will be limited * to that number of entries. The theoretical maximum is STAT_RANGE * 2 if * an extra blow/speed boost was given for each combination of STR and DEX. * * Returns the number of entries made in the possible_blows[] table, or 0 * if the object is not a weapon. * * Note that the results are meaningless if called on a fake ego object as * the actual ego may have different properties. */ static int obj_known_blows(const struct object *obj, int max_num, struct blow_info possible_blows[]) { int str_plus, dex_plus, old_blows = 0; int str_faster = -1, str_done = -1; int dex_plus_bound; int str_plus_bound; struct player_state state; int weapon_slot = slot_by_name(player, "weapon"); struct object *current_weapon = slot_object(player, weapon_slot); int num = 0; /* Not a weapon - no blows! */ if (!tval_is_melee_weapon(obj)) return 0; /* Pretend we're wielding the object */ player->body.slots[weapon_slot].obj = (struct object *) obj; /* Calculate the player's hypothetical state */ memcpy(&state, &player->state, sizeof(state)); state.stat_ind[STAT_STR] = 0; //Hack - NRM state.stat_ind[STAT_DEX] = 0; //Hack - NRM calc_bonuses(player, &state, true, false); /* First entry is always the current num of blows. */ possible_blows[num].str_plus = 0; possible_blows[num].dex_plus = 0; possible_blows[num].centiblows = state.num_blows; num++; /* Check to see if extra STR or DEX would yield extra blows */ old_blows = state.num_blows; dex_plus_bound = STAT_RANGE - state.stat_ind[STAT_DEX]; str_plus_bound = STAT_RANGE - state.stat_ind[STAT_STR]; /* Re-calculate with increased stats */ for (dex_plus = 0; dex_plus < dex_plus_bound; dex_plus++) { for (str_plus = 0; str_plus < str_plus_bound; str_plus++) { int new_blows = 0; /* Unlikely */ if (num == max_num) { player->body.slots[weapon_slot].obj = current_weapon; return num; } state.stat_ind[STAT_STR] = str_plus; //Hack - NRM state.stat_ind[STAT_DEX] = dex_plus; //Hack - NRM calc_bonuses(player, &state, true, false); new_blows = state.num_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)) { possible_blows[num].str_plus = str_plus; possible_blows[num].dex_plus = dex_plus; possible_blows[num].centiblows = new_blows / 10; possible_blows[num].centiblows *= 10; num++; 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)) { possible_blows[num].str_plus = str_plus; possible_blows[num].dex_plus = dex_plus; possible_blows[num].centiblows = new_blows; num++; str_faster = str_plus; } } } /* Stop pretending */ player->body.slots[weapon_slot].obj = current_weapon; return num; }