/** * Remove an amount of an object from the inventory or quiver, returning * a detached object which can be used. * * Optionally describe what remains. */ struct object *gear_object_for_use(struct object *obj, int num, bool message) { struct object *usable; char name[80]; char label = gear_to_label(obj); bool artifact = obj->artifact && (object_is_known(obj) || object_name_is_visible(obj)); /* Bounds check */ num = MIN(num, obj->number); /* Prepare a name if necessary */ if (message) { /* Artifacts */ if (artifact) object_desc(name, sizeof(name), obj, ODESC_FULL | ODESC_SINGULAR); else { /* Describe as if it's already reduced */ obj->number -= num; object_desc(name, sizeof(name), obj, ODESC_PREFIX | ODESC_FULL); obj->number += num; } } /* Split off a usable object if necessary */ if (obj->number > num) { usable = object_split(obj, num); } else { /* We're using the entire stack */ usable = obj; gear_excise_object(usable); /* Stop tracking item */ if (tracked_object_is(player->upkeep, obj)) track_object(player->upkeep, NULL); /* Inventory has changed, so disable repeat command */ cmd_disable_repeat(); } /* Change the weight */ player->upkeep->total_weight -= (num * obj->weight); /* Housekeeping */ player->upkeep->update |= (PU_BONUS); player->upkeep->notice |= (PN_COMBINE); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); /* Print a message if desired */ if (message) { if (artifact) msg("You no longer have the %s (%c).", name, label); else msg("You have %s (%c).", name, label); } return usable; }
/** * Remove an amount of an object from the floor, returning a detached object * which can be used - it is assumed that the object is on the player grid. * * Optionally describe what remains. */ struct object *floor_object_for_use(struct object *obj, int num, bool message, bool *none_left) { struct object *usable; char name[80]; /* Bounds check */ num = MIN(num, obj->number); /* Split off a usable object if necessary */ if (obj->number > num) { usable = object_split(obj, num); } else { usable = obj; square_excise_object(player->cave, usable->iy, usable->ix, usable->known); delist_object(player->cave, usable->known); square_excise_object(cave, usable->iy, usable->ix, usable); delist_object(cave, usable); *none_left = true; /* Stop tracking item */ if (tracked_object_is(player->upkeep, obj)) track_object(player->upkeep, NULL); /* Inventory has changed, so disable repeat command */ cmd_disable_repeat(); } /* Object no longer has a location */ usable->known->iy = 0; usable->known->ix = 0; usable->iy = 0; usable->ix = 0; /* Housekeeping */ player->upkeep->update |= (PU_BONUS | PU_INVEN); player->upkeep->notice |= (PN_COMBINE); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP); /* Print a message if requested and there is anything left */ if (message) { if (usable == obj) obj->number = 0; /* Get a description */ object_desc(name, sizeof(name), obj, ODESC_PREFIX | ODESC_FULL); if (usable == obj) obj->number = num; /* Print a message */ msg("You see %s.", name); } return usable; }
/** * This is a helper function used by do_cmd_throw and do_cmd_fire. * * It abstracts out the projectile path, display code, identify and clean up * logic, while using the 'attack' parameter to do work particular to each * kind of attack. */ static void ranged_helper(int item, int dir, int range, int shots, ranged_attack attack) { /* Get the ammo */ object_type *o_ptr = object_from_item_idx(item); int i, j; byte missile_attr = object_attr(o_ptr); char missile_char = object_char(o_ptr); object_type object_type_body; object_type *i_ptr = &object_type_body; char o_name[80]; int path_n; u16b path_g[256]; int msec = op_ptr->delay_factor; /* Start at the player */ int x = p_ptr->px; int y = p_ptr->py; /* Predict the "target" location */ s16b ty = y + 99 * ddy[dir]; s16b tx = x + 99 * ddx[dir]; bool hit_target = FALSE; /* Check for target validity */ if ((dir == 5) && target_okay()) { int taim; char msg[80]; target_get(&tx, &ty); taim = distance(y, x, ty, tx); if (taim > range) { sprintf (msg, "Target out of range by %d squares. Fire anyway? ", taim - range); if (!get_check(msg)) return; } } /* Sound */ sound(MSG_SHOOT); object_notice_on_firing(o_ptr); /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_FULL | ODESC_SINGULAR); /* Actually "fire" the object -- Take a partial turn */ p_ptr->energy_use = (100 / shots); /* Calculate the path */ path_n = project_path(path_g, range, y, x, ty, tx, 0); /* Hack -- Handle stuff */ handle_stuff(p_ptr); /* Start at the player */ x = p_ptr->px; y = p_ptr->py; /* Project along the path */ for (i = 0; i < path_n; ++i) { int ny = GRID_Y(path_g[i]); int nx = GRID_X(path_g[i]); /* Hack -- Stop before hitting walls */ if (!cave_floor_bold(ny, nx)) break; /* Advance */ x = nx; y = ny; /* Only do visuals if the player can "see" the missile */ if (player_can_see_bold(y, x)) { print_rel(missile_char, missile_attr, y, x); move_cursor_relative(y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(p_ptr); Term_xtra(TERM_XTRA_DELAY, msec); cave_light_spot(cave, y, x); Term_fresh(); if (p_ptr->redraw) redraw_stuff(p_ptr); } else { /* Delay anyway for consistency */ Term_xtra(TERM_XTRA_DELAY, msec); } /* Handle monster */ if (cave->m_idx[y][x] > 0) break; } /* Try the attack on the monster at (x, y) if any */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster(cave, cave->m_idx[y][x]); monster_race *r_ptr = &r_info[m_ptr->r_idx]; int visible = m_ptr->ml; bool fear = FALSE; char m_name[80]; const char *note_dies = monster_is_unusual(r_ptr) ? " is destroyed." : " dies."; struct attack_result result = attack(o_ptr, y, x); int dmg = result.dmg; u32b msg_type = result.msg_type; const char *hit_verb = result.hit_verb; if (result.success) { hit_target = TRUE; /* Get "the monster" or "it" */ monster_desc(m_name, sizeof(m_name), m_ptr, 0); object_notice_attack_plusses(o_ptr); /* No negative damage; change verb if no damage done */ if (dmg <= 0) { dmg = 0; hit_verb = "fail to harm"; } if (!visible) { /* Invisible monster */ msgt(MSG_SHOOT_HIT, "The %s finds a mark.", o_name); } else { /* Visible monster */ if (msg_type == MSG_SHOOT_HIT) msgt(MSG_SHOOT_HIT, "The %s %s %s.", o_name, hit_verb, m_name); else if (msg_type == MSG_HIT_GOOD) { msgt(MSG_HIT_GOOD, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a good hit!"); } else if (msg_type == MSG_HIT_GREAT) { msgt(MSG_HIT_GREAT, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a great hit!"); } else if (msg_type == MSG_HIT_SUPERB) { msgt(MSG_HIT_SUPERB, "The %s %s %s. %s", o_name, hit_verb, m_name, "It was a superb hit!"); } /* Track this monster */ if (m_ptr->ml) monster_race_track(m_ptr->r_idx); if (m_ptr->ml) health_track(p_ptr, cave->m_idx[y][x]); } /* Complex message */ if (p_ptr->wizard) msg("You do %d (out of %d) damage.", dmg, m_ptr->hp); /* Hit the monster, check for death */ if (!mon_take_hit(cave->m_idx[y][x], dmg, &fear, note_dies)) { message_pain(cave->m_idx[y][x], dmg); if (fear && m_ptr->ml) add_monster_message(m_name, cave->m_idx[y][x], MON_MSG_FLEE_IN_TERROR, TRUE); } } } /* Obtain a local object */ object_copy(i_ptr, o_ptr); object_split(i_ptr, o_ptr, 1); /* See if the ammunition broke or not */ j = breakage_chance(i_ptr, hit_target); /* Drop (or break) near that location */ drop_near(cave, i_ptr, j, y, x, TRUE); if (item >= 0) { /* The ammo is from the inventory */ inven_item_increase(item, -1); inven_item_describe(item); inven_item_optimize(item); } else { /* The ammo is from the floor */ floor_item_increase(0 - item, -1); floor_item_optimize(0 - item); } }
/** * Wield or wear a single item from the pack or floor */ void inven_wield(struct object *obj, int slot) { struct object *wielded, *old = player->body.slots[slot].obj; const char *fmt; char o_name[80]; /* Increase equipment counter if empty slot */ if (old == NULL) player->upkeep->equip_cnt++; /* Take a turn */ player->upkeep->energy_use = z_info->move_energy; /* Split off a new object if necessary */ if (obj->number > 1) { /* Split off a new single object */ wielded = object_split(obj, 1); /* If it's a gear object, give the split item a list entry */ if (pile_contains(player->gear, obj)) { wielded->next = obj->next; obj->next = wielded; wielded->prev = obj; if (wielded->next) (wielded->next)->prev = wielded; } } else wielded = obj; /* Carry floor items, don't allow combining */ if (square_holds_object(cave, player->py, player->px, wielded)) { square_excise_object(cave, player->py, player->px, wielded); inven_carry(player, wielded, FALSE, FALSE); } /* Wear the new stuff */ player->body.slots[slot].obj = wielded; /* Do any ID-on-wield */ object_notice_on_wield(wielded); /* Where is the item now */ if (tval_is_melee_weapon(wielded)) fmt = "You are wielding %s (%c)."; else if (wielded->tval == TV_BOW) fmt = "You are shooting with %s (%c)."; else if (tval_is_light(wielded)) fmt = "Your light source is %s (%c)."; else fmt = "You are wearing %s (%c)."; /* Describe the result */ object_desc(o_name, sizeof(o_name), wielded, ODESC_PREFIX | ODESC_FULL); /* Message */ msgt(MSG_WIELD, fmt, o_name, I2A(slot)); /* Cursed! */ if (cursed_p(wielded->flags)) { /* Warn the player */ msgt(MSG_CURSED, "Oops! It feels deathly cold!"); /* Sense the object */ object_notice_curses(wielded); } /* See if we have to overflow the pack */ combine_pack(); pack_overflow(old); /* Recalculate bonuses, torch, mana, gear */ player->upkeep->notice |= (PN_IGNORE); player->upkeep->update |= (PU_BONUS | PU_INVEN); player->upkeep->redraw |= (PR_INVEN | PR_EQUIP | PR_ARMOR); player->upkeep->redraw |= (PR_STATS | PR_HP | PR_MANA | PR_SPEED); /* Disable repeats */ cmd_disable_repeat(); }