static object_type *find_artifact(struct artifact *artifact) { int i, j; /* Look for the artifact, either in inventory, store or the object list */ for (i = 0; i < z_info->o_max; i++) { if (object_byid(i)->artifact == artifact) return object_byid(i); } for (i = 0; i < INVEN_TOTAL; i++) { if (p_ptr->inventory[i].artifact == artifact) return &p_ptr->inventory[i]; } for (j = 1; j < (FEAT_SHOP_TAIL - FEAT_SHOP_HEAD + 1); j++) { for (i = 0; i < stores[j].stock_size; i++) { if (stores[j].stock[i].artifact == artifact) return &stores[j].stock[i]; } } return NULL; }
/* * Determine if a grid contains a chest */ static s16b chest_check(int y, int x) { s16b this_o_idx, next_o_idx = 0; /* Scan all objects in the grid */ for (this_o_idx = cave->o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Skip unknown chests XXX XXX */ /* if (!o_ptr->marked) continue; */ /* Check for chest */ if (o_ptr->tval == TV_CHEST) return (this_o_idx); } /* No chest */ return (0); }
/* * Display the floor. Builds a list of objects and passes them * off to show_obj_list() for display. Mode flags documented in * object.h */ void show_floor(const int *floor_list, int floor_num, int mode) { int i; object_type *o_ptr; int num_obj = 0; char labels[50][80]; object_type *objects[50]; if (floor_num > MAX_FLOOR_STACK) floor_num = MAX_FLOOR_STACK; /* Build the object list */ for (i = 0; i < floor_num; i++) { o_ptr = object_byid(floor_list[i]); /* Tester always skips gold. When gold should be displayed, * only test items that are not gold. */ if ((o_ptr->tval != TV_GOLD || !(mode & OLIST_GOLD)) && !item_tester_okay(o_ptr)) continue; strnfmt(labels[num_obj], sizeof(labels[num_obj]), "%c) ", index_to_label(i)); /* Save the object */ objects[num_obj] = o_ptr; num_obj++; } /* Display the object list */ show_obj_list(num_obj, 0, labels, objects, mode); }
/* * Verify the choice of an item. * * The item can be negative to mean "item on floor". */ bool verify_item(const char *prompt, int item) { char o_name[80]; char out_val[160]; object_type *o_ptr; /* Inventory */ if (item >= 0) { o_ptr = &p_ptr->inventory[item]; } /* Floor */ else { o_ptr = object_byid(0 - item); } /* Describe */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Prompt */ strnfmt(out_val, sizeof(out_val), "%s %s? ", prompt, o_name); /* Query */ return (get_check(out_val)); }
/* * Hack -- allow user to "prevent" certain choices. * * The item can be negative to mean "item on floor". */ static bool get_item_allow(int item, unsigned char ch, bool is_harmless) { object_type *o_ptr; char verify_inscrip[] = "!*"; unsigned n; /* Inventory or floor */ if (item >= 0) o_ptr = &p_ptr->inventory[item]; else o_ptr = object_byid(0 - item); /* Check for a "prevention" inscription */ verify_inscrip[1] = ch; /* Find both sets of inscriptions, add together, and prompt that number of times */ n = check_for_inscrip(o_ptr, verify_inscrip); if (!is_harmless) n += check_for_inscrip(o_ptr, "!*"); while (n--) { if (!verify_item("Really try", item)) return (FALSE); } /* Allow it */ return (TRUE); }
void autoinscribe_ground(void) { int py = p_ptr->py; int px = p_ptr->px; s16b this_o_idx, next_o_idx = 0; /* Scan the pile of objects */ for (this_o_idx = cave->o_idx[py][px]; this_o_idx; this_o_idx = next_o_idx) { /* Get the next object */ next_o_idx = object_byid(this_o_idx)->next_o_idx; /* Apply an autoinscription */ apply_autoinscription(object_byid(this_o_idx)); } }
/* * Carry an object and delete it. */ static void py_pickup_aux(int o_idx, bool domsg) { int slot, quiver_slot = 0; char o_name[80]; object_type *o_ptr = object_byid(o_idx); /* Carry the object */ slot = inven_carry(p_ptr, o_ptr); /* Handle errors (paranoia) */ if (slot < 0) return; /* If we have picked up ammo which matches something in the quiver, note * that it so that we can wield it later (and suppress pick up message) */ if (obj_is_ammo(o_ptr)) { int i; for (i = QUIVER_START; i < QUIVER_END; i++) { if (!p_ptr->inventory[i].kind) continue; if (!object_similar(&p_ptr->inventory[i], o_ptr, OSTACK_QUIVER)) continue; quiver_slot = i; break; } } /* Get the new object */ o_ptr = &p_ptr->inventory[slot]; /* Set squelch status */ p_ptr->notice |= PN_SQUELCH; /* Automatically sense artifacts */ object_notice_artifact(o_ptr); /* Optionally, display a message */ if (domsg && !quiver_slot) { /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Message */ msg("You have %s (%c).", o_name, index_to_label(slot)); } /* Update object_idx if necessary */ if (p_ptr->object_idx == (0 - o_idx)) { track_object(slot); } /* Delete the object */ delete_object_idx(o_idx); /* If we have a quiver slot that this ammo matches, use it */ if (quiver_slot) wield_item(o_ptr, slot, quiver_slot); }
/* * Hack -- prevent certain choices depending on the inscriptions on the item. * * The item can be negative to mean "item on floor". */ bool get_item_allow(int item, unsigned char ch, cmd_code cmd, bool is_harmless) { object_type *o_ptr; char verify_inscrip[] = "!*"; unsigned n; /* Inventory or floor */ if (item >= 0) o_ptr = &p_ptr->inventory[item]; else o_ptr = object_byid(0 - item); /* Hack - Only shift the command key if it actually needs to be shifted. */ if (ch < 0x20) ch = UN_KTRL(ch); /* The inscription to look for */ verify_inscrip[1] = ch; /* Look for the inscription */ n = check_for_inscrip(o_ptr, verify_inscrip); /* Also look for for the inscription '!*' */ if (!is_harmless) n += check_for_inscrip(o_ptr, "!*"); /* Choose string for the prompt */ if (n) { char prompt[1024]; const char *verb = cmd_get_verb(cmd); if (!verb) verb = "do that with"; strnfmt(prompt, sizeof(prompt), "Really %s", verb); /* Promt for confirmation n times */ while (n--) { if (!verify_item(prompt, item)) return (FALSE); } } /* Allow it */ return (TRUE); }
/* * Return the number of chests around (or under) the character. * If requested, count only trapped chests. */ int count_chests(int *y, int *x, bool trapped) { int d, count, o_idx; object_type *o_ptr; /* Count how many matches */ count = 0; /* Check around (and under) the character */ for (d = 0; d < 9; d++) { /* Extract adjacent (legal) location */ int yy = p_ptr->py + ddy_ddd[d]; int xx = p_ptr->px + ddx_ddd[d]; /* No (visible) chest is there */ if ((o_idx = chest_check(yy, xx)) == 0) continue; /* Grab the object */ o_ptr = object_byid(o_idx); /* Already open */ if (o_ptr->pval[DEFAULT_PVAL] == 0) continue; /* No (known) traps here */ if (trapped && (!object_is_known(o_ptr) || (o_ptr->pval[DEFAULT_PVAL] < 0) || !chest_traps[o_ptr->pval[DEFAULT_PVAL]])) { continue; } /* Count it */ ++count; /* Remember the location of the last chest found */ *y = yy; *x = xx; } /* All done */ return count; }
void wr_objects(void) { int i; if (p_ptr->is_dead) return; /* Total objects */ wr_u16b(o_max); /* Dump the objects */ for (i = 1; i < o_max; i++) { object_type *o_ptr = object_byid(i); /* Dump it */ wr_item(o_ptr); } }
/* * Determine if a grid contains a chest matching the query type */ s16b chest_check(int y, int x, enum chest_query check_type) { s16b this_o_idx, next_o_idx = 0; /* Scan all objects in the grid */ for (this_o_idx = cave->o_idx[y][x]; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Skip unknown chests XXX XXX */ /* if (!o_ptr->marked) continue; */ /* Check for chests */ switch (check_type) { case CHEST_ANY: if (o_ptr->tval == TV_CHEST) return this_o_idx; break; case CHEST_OPENABLE: if ((o_ptr->tval == TV_CHEST) && (o_ptr->pval[DEFAULT_PVAL] != 0)) return this_o_idx; break; case CHEST_TRAPPED: if (is_trapped_chest(o_ptr) && object_is_known(o_ptr)) return this_o_idx; break; } } /* No chest */ return (0); }
int do_autopickup(void) { int py = p_ptr->py; int px = p_ptr->px; s16b this_o_idx, next_o_idx = 0; object_type *o_ptr; /* Objects picked up. Used to determine time cost of command. */ byte objs_picked_up = 0; size_t floor_num = 0; int floor_list[MAX_FLOOR_STACK + 1]; /* Nothing to pick up -- return */ if (!cave->o_idx[py][px]) return (0); /* Always pickup gold, effortlessly */ py_pickup_gold(); /* Scan the remaining objects */ for (this_o_idx = cave->o_idx[py][px]; this_o_idx; this_o_idx = next_o_idx) { /* Get the object and the next object */ o_ptr = object_byid(this_o_idx); next_o_idx = o_ptr->next_o_idx; /* Ignore all hidden objects and non-objects */ if (squelch_item_ok(o_ptr) || !o_ptr->kind) continue; /* XXX Hack -- Enforce limit */ if (floor_num >= N_ELEMENTS(floor_list)) break; /* Hack -- disturb */ disturb(p_ptr, 0, 0); /* Automatically pick up items into the backpack */ if (auto_pickup_okay(o_ptr)) { /* Pick up the object with message */ py_pickup_aux(this_o_idx, TRUE); objs_picked_up++; continue; } /* Tally objects and store them in an array. */ /* Remember this object index */ floor_list[floor_num] = this_o_idx; /* Count non-gold objects that remain on the floor. */ floor_num++; } return objs_picked_up; }
/* * Pickup all gold at the player's current location. */ static void py_pickup_gold(void) { int py = p_ptr->py; int px = p_ptr->px; s32b total_gold = 0L; byte *treasure; s16b this_o_idx = 0; s16b next_o_idx = 0; object_type *o_ptr; int sound_msg; bool verbal = FALSE; /* Allocate an array of ordinary gold objects */ treasure = C_ZNEW(SV_GOLD_MAX, byte); /* Pick up all the ordinary gold objects */ for (this_o_idx = cave->o_idx[py][px]; this_o_idx; this_o_idx = next_o_idx) { /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Ignore if not legal treasure */ if ((o_ptr->tval != TV_GOLD) || (o_ptr->sval >= SV_GOLD_MAX)) continue; /* Note that we have this kind of treasure */ treasure[o_ptr->sval]++; /* Remember whether feedback message is in order */ if (!squelch_item_ok(o_ptr)) verbal = TRUE; /* Increment total value */ total_gold += (s32b)o_ptr->pval[DEFAULT_PVAL]; /* Delete the gold */ delete_object_idx(this_o_idx); } /* Pick up the gold, if present */ if (total_gold) { char buf[1024]; char tmp[80]; int i, count, total; object_kind *kind; /* Build a message */ (void)strnfmt(buf, sizeof(buf), "You have found %ld gold pieces worth of ", (long)total_gold); /* Count the types of treasure present */ for (total = 0, i = 0; i < SV_GOLD_MAX; i++) { if (treasure[i]) total++; } /* List the treasure types */ for (count = 0, i = 0; i < SV_GOLD_MAX; i++) { /* Skip if no treasure of this type */ if (!treasure[i]) continue; /* Get this object index */ kind = lookup_kind(TV_GOLD, i); if (!kind) continue; /* Get the object name */ object_kind_name(tmp, sizeof tmp, kind, TRUE); /* Build up the pickup string */ my_strcat(buf, tmp, sizeof(buf)); /* Added another kind of treasure */ count++; /* Add a comma if necessary */ if ((total > 2) && (count < total)) my_strcat(buf, ",", sizeof(buf)); /* Add an "and" if necessary */ if ((total >= 2) && (count == total-1)) my_strcat(buf, " and", sizeof(buf)); /* Add a space or period if necessary */ if (count < total) my_strcat(buf, " ", sizeof(buf)); else my_strcat(buf, ".", sizeof(buf)); } /* Determine which sound to play */ if (total_gold < 200) sound_msg = MSG_MONEY1; else if (total_gold < 600) sound_msg = MSG_MONEY2; else sound_msg = MSG_MONEY3; /* Display the message */ if (verbal) msgt(sound_msg, "%s", buf); /* Add gold to purse */ p_ptr->au += total_gold; /* Redraw gold */ p_ptr->redraw |= (PR_GOLD); } /* Free the gold array */ FREE(treasure); }
/** * Deletes a monster by index. * * When a monster is deleted, all of its objects are deleted. */ void delete_monster_idx(int m_idx) { int x, y; s16b this_o_idx, next_o_idx = 0; monster_type *m_ptr; assert(m_idx > 0); m_ptr = cave_monster(cave, m_idx); /* Monster location */ y = m_ptr->fy; x = m_ptr->fx; /* Hack -- Reduce the racial counter */ m_ptr->race->cur_num--; /* Hack -- count the number of "reproducers" */ if (rf_has(m_ptr->race->flags, RF_MULTIPLY)) num_repro--; /* Hack -- remove target monster */ if (target_get_monster() == m_ptr) target_set_monster(NULL); /* Hack -- remove tracked monster */ if (p_ptr->health_who == m_ptr) health_track(p_ptr, NULL); /* Monster is gone */ cave->m_idx[y][x] = 0; /* Delete objects */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Preserve unseen artifacts (we assume they were created as this * monster's drop) - this will cause unintended behaviour in preserve * off mode if monsters can pick up artifacts */ if (o_ptr->artifact && !object_was_sensed(o_ptr)) o_ptr->artifact->created = FALSE; /* Clear held_m_idx now to avoid wasting time in delete_object_idx */ o_ptr->held_m_idx = 0; /* Delete the object */ delete_object_idx(this_o_idx); } /* Delete mimicked objects */ if (m_ptr->mimicked_o_idx > 0) delete_object_idx(m_ptr->mimicked_o_idx); /* Wipe the Monster */ (void)WIPE(m_ptr, monster_type); /* Count monsters */ cave->mon_cnt--; /* Visual update */ cave_light_spot(cave, y, x); }
/** * Handles the "death" of a monster. * * Disperses treasures carried by the monster centered at the monster location. * Note that objects dropped may disappear in crowded rooms. * * Checks for "Quest" completion when a quest monster is killed. * * Note that only the player can induce "monster_death()" on Uniques. * Thus (for now) all Quest monsters should be Uniques. * * If `stats` is true, then we skip updating the monster memory. This is * used by stats-generation code, for efficiency. */ void monster_death(struct monster *m_ptr, bool stats) { int i; int dump_item = 0; int dump_gold = 0; int total = 0; s16b this_o_idx, next_o_idx = 0; object_type *i_ptr; object_type object_type_body; bool visible = (m_ptr->ml || rf_has(m_ptr->race->flags, RF_UNIQUE)); int y = m_ptr->fy; int x = m_ptr->fx; /* Delete any mimicked objects */ if (m_ptr->mimicked_o_idx > 0) delete_object_idx(m_ptr->mimicked_o_idx); /* Drop objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Line up the next object */ next_o_idx = o_ptr->next_o_idx; /* Paranoia */ o_ptr->held_m_idx = 0; /* Get local object, copy it and delete the original */ i_ptr = &object_type_body; object_copy(i_ptr, o_ptr); delete_object_idx(this_o_idx); /* Count it and drop it - refactor once origin is a bitflag */ if (!stats) { if ((i_ptr->tval == TV_GOLD) && (i_ptr->origin != ORIGIN_STOLEN)) dump_gold++; else if ((i_ptr->tval != TV_GOLD) && ((i_ptr->origin == ORIGIN_DROP) || (i_ptr->origin == ORIGIN_DROP_PIT) || (i_ptr->origin == ORIGIN_DROP_VAULT) || (i_ptr->origin == ORIGIN_DROP_SUMMON) || (i_ptr->origin == ORIGIN_DROP_SPECIAL) || (i_ptr->origin == ORIGIN_DROP_BREED) || (i_ptr->origin == ORIGIN_DROP_POLY) || (i_ptr->origin == ORIGIN_DROP_WIZARD))) dump_item++; } /* Change origin if monster is invisible, unless we're in stats mode */ if (!visible && !stats) i_ptr->origin = ORIGIN_DROP_UNKNOWN; drop_near(cave, i_ptr, 0, y, x, TRUE); } /* Forget objects */ m_ptr->hold_o_idx = 0; /* Take note of any dropped treasure */ if (visible && (dump_item || dump_gold)) lore_treasure(m_ptr, dump_item, dump_gold); /* Update monster list window */ p_ptr->redraw |= PR_MONLIST; /* Nothing else to do for non-"Quest Monsters" */ if (!rf_has(m_ptr->race->flags, RF_QUESTOR)) return; /* Mark quests as complete */ for (i = 0; i < MAX_Q_IDX; i++) { /* Note completed quests */ if (q_list[i].level == m_ptr->race->level) q_list[i].level = 0; /* Count incomplete quests */ if (q_list[i].level) total++; } /* Build magical stairs */ build_quest_stairs(y, x); /* Nothing left, game over... */ if (total == 0) { p_ptr->total_winner = TRUE; p_ptr->redraw |= (PR_TITLE); msg("*** CONGRATULATIONS ***"); msg("You have won the game!"); msg("You may retire (commit suicide) when you are ready."); } }
/** * Move a monster from index i1 to index i2 in the monster list. */ static void compact_monsters_aux(int i1, int i2) { int y, x; monster_type *m_ptr; s16b this_o_idx, next_o_idx = 0; /* Do nothing */ if (i1 == i2) return; /* Old monster */ m_ptr = cave_monster(cave, i1); y = m_ptr->fy; x = m_ptr->fx; /* Update the cave */ cave->m_idx[y][x] = i2; /* Update midx */ m_ptr->midx = i2; /* Repair objects being carried by monster */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Reset monster pointer */ o_ptr->held_m_idx = i2; } /* Move mimicked objects */ if (m_ptr->mimicked_o_idx > 0) { object_type *o_ptr; /* Get the object */ o_ptr = object_byid(m_ptr->mimicked_o_idx); /* Reset monster pointer */ o_ptr->mimicking_m_idx = i2; } /* Hack -- Update the target */ if (target_get_monster() == m_ptr) target_set_monster(cave_monster(cave, i2)); /* Hack -- Update the health bar */ if (p_ptr->health_who == m_ptr) p_ptr->health_who = cave_monster(cave, i2); /* Hack -- move monster */ COPY(cave_monster(cave, i2), cave_monster(cave, i1), struct monster); /* Hack -- wipe hole */ (void)WIPE(cave_monster(cave, i1), monster_type); }
/* * Attempt to disarm the chest at the given location * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ bool do_cmd_disarm_chest(int y, int x, s16b o_idx) { int i, j; bool more = FALSE; object_type *o_ptr = object_byid(o_idx); /* Get the "disarm" factor */ i = p_ptr->state.skills[SKILL_DISARM]; /* Penalize some conditions */ if (p_ptr->timed[TMD_BLIND] || no_light()) i = i / 10; if (p_ptr->timed[TMD_CONFUSED] || p_ptr->timed[TMD_IMAGE]) i = i / 10; /* Extract the difficulty */ j = i - o_ptr->pval[DEFAULT_PVAL]; /* Always have a small chance of success */ if (j < 2) j = 2; /* Must find the trap first. */ if (!object_is_known(o_ptr)) { msg("I don't see any traps."); } /* Already disarmed/unlocked or no traps */ else if (!is_trapped_chest(o_ptr)) { msg("The chest is not trapped."); } /* Success (get a lot of experience) */ else if (randint0(100) < j) { msgt(MSG_DISARM, "You have disarmed the chest."); player_exp_gain(p_ptr, o_ptr->pval[DEFAULT_PVAL]); o_ptr->pval[DEFAULT_PVAL] = (0 - o_ptr->pval[DEFAULT_PVAL]); } /* Failure -- Keep trying */ else if ((i > 5) && (randint1(i) > 5)) { /* We may keep trying */ more = TRUE; flush(); msg("You failed to disarm the chest."); } /* Failure -- Set off the trap */ else { msg("You set off a trap!"); chest_trap(y, x, o_idx); } /* Result */ return (more); }
/* * Attempt to open the given chest at the given location * * Assume there is no monster blocking the destination * * Returns TRUE if repeated commands may continue */ bool do_cmd_open_chest(int y, int x, s16b o_idx) { int i, j; bool flag = TRUE; bool more = FALSE; object_type *o_ptr = object_byid(o_idx); /* Attempt to unlock it */ if (o_ptr->pval[DEFAULT_PVAL] > 0) { /* Assume locked, and thus not open */ flag = FALSE; /* Get the "disarm" factor */ i = p_ptr->state.skills[SKILL_DISARM]; /* Penalize some conditions */ if (p_ptr->timed[TMD_BLIND] || no_light()) i = i / 10; if (p_ptr->timed[TMD_CONFUSED] || p_ptr->timed[TMD_IMAGE]) i = i / 10; /* Extract the difficulty */ j = i - o_ptr->pval[DEFAULT_PVAL]; /* Always have a small chance of success */ if (j < 2) j = 2; /* Success -- May still have traps */ if (randint0(100) < j) { msgt(MSG_LOCKPICK, "You have picked the lock."); player_exp_gain(p_ptr, 1); flag = TRUE; } /* Failure -- Keep trying */ else { /* We may continue repeating */ more = TRUE; flush(); msgt(MSG_LOCKPICK_FAIL, "You failed to pick the lock."); } } /* Allowed to open */ if (flag) { /* Apply chest traps, if any */ chest_trap(y, x, o_idx); /* Let the Chest drop items */ chest_death(y, x, o_idx); /* Squelch chest if autosquelch calls for it */ p_ptr->notice |= PN_SQUELCH; } /* * empty chests were always squelched in squelch_item_okay so we * might as well squelch it here */ if (o_ptr->pval[DEFAULT_PVAL] == 0) { o_ptr->ignore = TRUE; } /* Redraw chest, to be on the safe side (it may have been squelched) */ cave_light_spot(cave, y, x); /* Refresh */ Term_fresh(); /* Result */ return (more); }
/* * Chests have traps too. * * Exploding chest destroys contents (and traps). * Note that the chest itself is never destroyed. */ static void chest_trap(int y, int x, s16b o_idx) { int i, trap; object_type *o_ptr = object_byid(o_idx); /* Ignore disarmed chests */ if (o_ptr->pval[DEFAULT_PVAL] <= 0) return; /* Obtain the traps */ trap = chest_traps[o_ptr->pval[DEFAULT_PVAL]]; /* Lose strength */ if (trap & (CHEST_LOSE_STR)) { msg("A small needle has pricked you!"); take_hit(p_ptr, damroll(1, 4), "a poison needle"); (void)do_dec_stat(A_STR, FALSE); } /* Lose constitution */ if (trap & (CHEST_LOSE_CON)) { msg("A small needle has pricked you!"); take_hit(p_ptr, damroll(1, 4), "a poison needle"); (void)do_dec_stat(A_CON, FALSE); } /* Poison */ if (trap & (CHEST_POISON)) { msg("A puff of green gas surrounds you!"); (void)player_inc_timed(p_ptr, TMD_POISONED, 10 + randint1(20), TRUE, TRUE); } /* Paralyze */ if (trap & (CHEST_PARALYZE)) { msg("A puff of yellow gas surrounds you!"); (void)player_inc_timed(p_ptr, TMD_PARALYZED, 10 + randint1(20), TRUE, TRUE); } /* Summon monsters */ if (trap & (CHEST_SUMMON)) { int num = 2 + randint1(3); msg("You are enveloped in a cloud of smoke!"); sound(MSG_SUM_MONSTER); for (i = 0; i < num; i++) { (void)summon_specific(y, x, p_ptr->depth, 0, 1); } } /* Explode */ if (trap & (CHEST_EXPLODE)) { msg("There is a sudden explosion!"); msg("Everything inside the chest is destroyed!"); o_ptr->pval[DEFAULT_PVAL] = 0; take_hit(p_ptr, damroll(5, 8), "an exploding chest"); } }
/* * Allocate objects upon opening a chest * * Disperse treasures from the given chest, centered at (x,y). * * Small chests often contain "gold", while Large chests always contain * items. Wooden chests contain 2 items, Iron chests contain 4 items, * and Steel chests contain 6 items. The "value" of the items in a * chest is based on the level on which the chest is generated. */ static void chest_death(int y, int x, s16b o_idx) { int number, value; bool tiny; object_type *o_ptr; object_type *i_ptr; object_type object_type_body; /* Get the chest */ o_ptr = object_byid(o_idx); /* Small chests often hold "gold" */ tiny = (o_ptr->sval < SV_CHEST_MIN_LARGE); /* Determine how much to drop (see above) */ number = (o_ptr->sval % SV_CHEST_MIN_LARGE) * 2; /* Zero pval means empty chest */ if (!o_ptr->pval[DEFAULT_PVAL]) number = 0; /* Determine the "value" of the items */ value = o_ptr->origin_depth - 10 + 2 * o_ptr->sval; if (value < 1) value = 1; /* Drop some objects (non-chests) */ for (; number > 0; --number) { /* Get local object */ i_ptr = &object_type_body; /* Wipe the object */ object_wipe(i_ptr); /* Small chests often drop gold */ if (tiny && (randint0(100) < 75)) make_gold(i_ptr, value, SV_GOLD_ANY); /* Otherwise drop an item, as long as it isn't a chest */ else { if (!make_object(cave, i_ptr, value, FALSE, FALSE, NULL)) continue; if (i_ptr->tval == TV_CHEST) continue; } /* Record origin */ i_ptr->origin = ORIGIN_CHEST; i_ptr->origin_depth = o_ptr->origin_depth; /* Drop it in the dungeon */ drop_near(cave, i_ptr, 0, y, x, TRUE); } /* Empty */ o_ptr->pval[DEFAULT_PVAL] = 0; /* Known */ object_notice_everything(o_ptr); }
//static struct keypress target_set_interactive_aux(int y, int x, int mode) static ui_event target_set_interactive_aux(int y, int x, int mode) { s16b this_o_idx = 0, next_o_idx = 0; const char *s1, *s2, *s3; bool boring; int floor_list[MAX_FLOOR_STACK]; int floor_num; //struct keypress query; ui_event press; char out_val[256]; char coords[20]; const char *name; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ press.type = EVT_KBRD; press.key.code = ' '; press.key.mods = 0; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* The player */ if (cave->m_idx[y][x] < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hallucination messes things up */ if (p_ptr->timed[TMD_IMAGE]) { const char *name = "something strange"; /* Display a message */ if (p_ptr->wizard) strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); else strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); prt(out_val, 0, 0); move_cursor_relative(y, x); //input = inkey_m(); //if ( press.key = inkey(); /* Stop on everything but "return" */ if (press.key.code == KC_ENTER) continue; return press; } /* Actual monsters */ if (cave->m_idx[y][x] > 0) { monster_type *m_ptr = cave_monster_at(cave, y, x); const monster_lore *l_ptr = get_lore(m_ptr->race); /* Visible */ if (m_ptr->ml && !m_ptr->unaware) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND2); /* Hack -- track this monster race */ monster_race_track(m_ptr->race); /* Hack -- health bar for this monster */ health_track(p_ptr, m_ptr); /* Hack -- handle stuff */ handle_stuff(p_ptr); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save screen */ screen_save(); /* Recall on screen */ screen_roff(m_ptr->race, l_ptr); /* Command */ press = inkey_m(); /* Load screen */ screen_load(); } /* Normal */ else { char buf[80]; /* Describe the monster */ look_mon_desc(buf, sizeof(buf), cave->m_idx[y][x]); /* Describe, and prompt for recall */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s (%d:%d).", s1, s2, s3, m_name, buf, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s.", s1, s2, s3, m_name, buf, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ press = inkey_m(); } /* Normal commands */ if ((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) recall = !recall; else if ((press.type == EVT_KBRD) && (press.key.code == 'r')) recall = !recall; else break; } if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if (press.key.code != KC_ENTER && press.key.code != ' ') break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Take account of gender */ if (rf_has(m_ptr->race->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(m_ptr->race->flags, RF_MALE)) s1 = "He is "; else s1 = "It is "; /* Use a verb */ s2 = "carrying "; /* Scan all objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { char o_name[80]; object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } /* Disabled since monsters now carry their drops else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } */ prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; /* Sometimes stop at "space" key */ if (press.mouse.button && !(mode & (TARGET_LOOK))) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; } /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (this_o_idx) break; /* Use a preposition */ s2 = "on "; } } /* Assume not floored */ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x0A); /* Scan all marked objects in the grid */ if ((floor_num > 0) && (!(p_ptr->timed[TMD_BLIND]) || (y == p_ptr->py && x == p_ptr->px))) { /* Not boring */ boring = FALSE; track_object(-floor_list[0]); handle_stuff(p_ptr); /* If there is more than one item... */ if (floor_num > 1) while (1) { /* Describe the pile */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s (%d:%d).", s1, s2, s3, floor_num, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s.", s1, s2, s3, floor_num, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); /* Display objects */ if (((press.type == EVT_MOUSE) && (press.mouse.button == 1) && (KEY_GRID_X(press) == x) && (KEY_GRID_Y(press) == y)) || ((press.type == EVT_KBRD) && (press.key.code == 'r'))) { int rdone = 0; int pos; while (!rdone) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD)); /* Describe the pile */ prt(out_val, 0, 0); press = inkey_m(); /* Load screen */ screen_load(); if (press.type == EVT_MOUSE) { pos = press.mouse.y-1; } else { pos = press.key.code - 'a'; } if (0 <= pos && pos < floor_num) { track_object(-floor_list[pos]); handle_stuff(p_ptr); continue; } rdone = 1; } /* Now that the user's done with the display loop, let's */ /* the outer loop over again */ continue; } /* Done */ break; } /* Only one object to display */ else { char o_name[80]; /* Get the single object in the list */ object_type *o_ptr = object_byid(floor_list[0]); /* Not boring */ boring = FALSE; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; /* Sometimes stop at "space" key */ if ((press.key.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } /* Double break */ if (this_o_idx) break; name = cave_apparent_name(cave, p_ptr, y, x); /* Terrain feature if needed */ if (boring || cave_isinteresting(cave, y, x)) { /* Hack -- handle unknown grids */ /* Pick a prefix */ if (*s2 && cave_isdoor(cave, y, x)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if (cave_isshop(cave, y, x)) { s3 = "the entrance to the "; } /* Display a message */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); press = inkey_m(); if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button == 2) break; } else { /* Stop on everything but "return"/"space" */ if ((press.key.code != KC_ENTER) && (press.key.code != ' ')) break; } } /* Stop on everything but "return" */ if (press.type == EVT_MOUSE) { /* Stop on right click */ if (press.mouse.button != 2) break; } else { if (press.key.code != KC_ENTER) break; } } /* Keep going */ return (press); }
/* * Perform the basic "tunnel" command * * Assumes that no monster is blocking the destination * * Uses "twall" (above) to do all "terrain feature changing". * * Returns TRUE if repeated commands may continue */ static bool do_cmd_tunnel_aux(int y, int x) { bool more = FALSE; /* Verify legality */ if (!do_cmd_tunnel_test(y, x)) return (FALSE); /* Sound XXX XXX XXX */ /* sound(MSG_DIG); */ /* Titanium */ if (cave->feat[y][x] >= FEAT_PERM_EXTRA) { msg("This seems to be permanent rock."); } /* Granite */ else if (cave->feat[y][x] >= FEAT_WALL_EXTRA) { /* Tunnel */ if ((p_ptr->state.skills[SKILL_DIGGING] > 40 + randint0(1600)) && twall(y, x)) { msg("You have finished the tunnel."); } /* Keep trying */ else { /* We may continue tunelling */ msg("You tunnel into the granite wall."); more = TRUE; } } /* Quartz / Magma */ else if (cave->feat[y][x] >= FEAT_MAGMA) { bool okay = FALSE; bool gold = FALSE; bool hard = FALSE; /* Found gold */ if (cave->feat[y][x] >= FEAT_MAGMA_H) { gold = TRUE; } /* Extract "quartz" flag XXX XXX XXX */ if ((cave->feat[y][x] - FEAT_MAGMA) & 0x01) { hard = TRUE; } /* Quartz */ if (hard) { okay = (p_ptr->state.skills[SKILL_DIGGING] > 20 + randint0(800)); } /* Magma */ else { okay = (p_ptr->state.skills[SKILL_DIGGING] > 10 + randint0(400)); } /* Success */ if (okay && twall(y, x)) { /* Found treasure */ if (gold) { /* Place some gold */ place_gold(cave, y, x, p_ptr->depth, ORIGIN_FLOOR); /* Message */ msg("You have found something!"); } /* Found nothing */ else { /* Message */ msg("You have finished the tunnel."); } } /* Failure (quartz) */ else if (hard) { /* Message, continue digging */ msg("You tunnel into the quartz vein."); more = TRUE; } /* Failure (magma) */ else { /* Message, continue digging */ msg("You tunnel into the magma vein."); more = TRUE; } } /* Rubble */ else if (cave->feat[y][x] == FEAT_RUBBLE) { /* Remove the rubble */ if ((p_ptr->state.skills[SKILL_DIGGING] > randint0(200)) && twall(y, x)) { /* Message */ msg("You have removed the rubble."); /* Hack -- place an object */ if (randint0(100) < 10) { /* Create a simple object */ place_object(cave, y, x, p_ptr->depth, FALSE, FALSE, ORIGIN_RUBBLE); /* Observe the new object */ if (!squelch_item_ok(object_byid(cave->o_idx[y][x])) && player_can_see_bold(y, x)) msg("You have found something!"); } } else { /* Message, keep digging */ msg("You dig in the rubble."); more = TRUE; } } /* Secret doors */ else if (cave->feat[y][x] >= FEAT_SECRET) { /* Tunnel */ if ((p_ptr->state.skills[SKILL_DIGGING] > 30 + randint0(1200)) && twall(y, x)) { msg("You have finished the tunnel."); } /* Keep trying */ else { /* We may continue tunelling */ msg("You tunnel into the granite wall."); more = TRUE; /* Occasional Search XXX XXX */ if (randint0(100) < 25) search(FALSE); } } /* Doors */ else { /* Tunnel */ if ((p_ptr->state.skills[SKILL_DIGGING] > 30 + randint0(1200)) && twall(y, x)) { msg("You have finished the tunnel."); } /* Keep trying */ else { /* We may continue tunelling */ msg("You tunnel into the door."); more = TRUE; } } /* Result */ return (more); }
/* * Pick up objects and treasure on the floor. -LM- * * Called with pickup: * 0 to act according to the player's settings * 1 to quickly pickup single objects or present a menu for more * 2 to force a menu for any number of objects * * Scan the list of objects in that floor grid. Pick up gold automatically. * Pick up objects automatically until backpack space is full if * auto-pickup option is on, Otherwise, store objects on * floor in an array, and tally both how many there are and can be picked up. * * If not picking up anything, indicate objects on the floor. Show more * details if the "OPT(pickup_detail)" option is set. Do the same thing if we * don't have room for anything. * * [This paragraph is not true, intentional?] * If we are picking up objects automatically, and have room for at least * one, allow the "OPT(pickup_detail)" option to display information about objects * and prompt the player. Otherwise, automatically pick up a single object * or use a menu for more than one. * * Pick up multiple objects using Tim Baker's menu system. Recursively * call this function (forcing menus for any number of objects) until * objects are gone, backpack is full, or player is satisfied. * * We keep track of number of objects picked up to calculate time spent. * This tally is incremented even for automatic pickup, so we are careful * (in "dungeon.c" and elsewhere) to handle pickup as either a separate * automated move or a no-cost part of the stay still or 'g'et command. * * Note the lack of chance for the character to be disturbed by unmarked * objects. They are truly "unknown". */ byte py_pickup(int pickup) { int py = p_ptr->py; int px = p_ptr->px; s16b this_o_idx = 0; size_t floor_num = 0; int floor_list[MAX_FLOOR_STACK + 1]; size_t i; int can_pickup = 0; bool call_function_again = FALSE; bool domsg = TRUE; /* Objects picked up. Used to determine time cost of command. */ byte objs_picked_up = 0; /* Nothing else to pick up -- return */ if (!cave->o_idx[py][px]) return objs_picked_up; /* Tally objects that can be picked up.*/ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), py, px, 0x03); for (i = 0; i < floor_num; i++) { can_pickup += inven_carry_okay(object_byid(floor_list[i])); } if (!can_pickup) { /* Can't pick up, but probably want to know what's there. */ event_signal(EVENT_SEEFLOOR); return objs_picked_up; } /* Use a menu interface for multiple objects, or pickup single objects */ if (pickup == 1) { if (floor_num > 1) pickup = 2; else this_o_idx = floor_list[0]; } /* Display a list if requested. */ if (pickup == 2) { const char *q, *s; int item; /* Restrict the choices */ item_tester_hook = inven_carry_okay; /* Get an object or exit. */ q = "Get which item?"; s = "You see nothing there."; if (!get_item(&item, q, s, CMD_PICKUP, USE_FLOOR)) return (objs_picked_up); this_o_idx = 0 - item; call_function_again = TRUE; /* With a list, we do not need explicit pickup messages */ domsg = FALSE; } /* Pick up object, if legal */ if (this_o_idx) { /* Pick up the object */ py_pickup_aux(this_o_idx, domsg); /* Indicate an object picked up. */ objs_picked_up = 1; } /* * If requested, call this function recursively. Count objects picked * up. Force the display of a menu in all cases. */ if (call_function_again) objs_picked_up += py_pickup(2); /* Indicate how many objects have been picked up. */ return (objs_picked_up); }
int context_menu_cave(struct cave *c, int y, int x, int adjacent, int mx, int my) { menu_type *m; region r; int selected; char *labels; bool allowed = TRUE; int mode = OPT(rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG; unsigned char cmdkey; m = menu_dynamic_new(); if (!m) { return 0; } labels = string_make(lower_case); m->selections = labels; /* Looking has different keys, but we don't have a way to look them up (see cmd-process.c). */ cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'l' : 'x'; menu_dynamic_add_label(m, "Look At", cmdkey, MENU_VALUE_LOOK, labels); if (c->m_idx[y][x]) { /* '/' is used for recall in both keymaps. */ menu_dynamic_add_label(m, "Recall Info", '/', MENU_VALUE_RECALL, labels); } ADD_LABEL("Use Item On", CMD_USE_ANY, MN_ROW_VALID); if (player_can_cast(p_ptr, FALSE)) { ADD_LABEL("Cast On", CMD_CAST, MN_ROW_VALID); } if (adjacent) { ADD_LABEL((c->m_idx[y][x]) ? "Attack" : "Alter", CMD_ALTER, MN_ROW_VALID); if (c->o_idx[y][x]) { s16b o_idx = chest_check(y,x, CHEST_ANY); if (o_idx) { object_type *o_ptr = object_byid(o_idx); if (!squelch_item_ok(o_ptr)) { if (object_is_known(o_ptr)) { if (is_locked_chest(o_ptr)) { ADD_LABEL("Disarm Chest", CMD_DISARM, MN_ROW_VALID); ADD_LABEL("Open Chest", CMD_OPEN, MN_ROW_VALID); } else { ADD_LABEL("Open Disarmed Chest", CMD_OPEN, MN_ROW_VALID); } } else { ADD_LABEL("Open Chest", CMD_OPEN, MN_ROW_VALID); } } } } if (cave_istrap(c, y, x)) { ADD_LABEL("Disarm", CMD_DISARM, MN_ROW_VALID); ADD_LABEL("Jump Onto", CMD_JUMP, MN_ROW_VALID); } if (cave_isopendoor(c, y, x)) { ADD_LABEL("Close", CMD_CLOSE, MN_ROW_VALID); } else if (cave_iscloseddoor(c, y, x)) { ADD_LABEL("Open", CMD_OPEN, MN_ROW_VALID); ADD_LABEL("Lock", CMD_DISARM, MN_ROW_VALID); } else if (cave_isdiggable(c, y, x)) { ADD_LABEL("Tunnel", CMD_TUNNEL, MN_ROW_VALID); } ADD_LABEL("Search", CMD_SEARCH, MN_ROW_VALID); ADD_LABEL("Walk Towards", CMD_WALK, MN_ROW_VALID); } else { /* ',' is used for squelch in rogue keymap, so we'll just swap letters. */ cmdkey = (mode == KEYMAP_MODE_ORIG) ? ',' : '.'; menu_dynamic_add_label(m, "Pathfind To", cmdkey, CMD_PATHFIND, labels); ADD_LABEL("Walk Towards", CMD_WALK, MN_ROW_VALID); ADD_LABEL("Run Towards", CMD_RUN, MN_ROW_VALID); } if (player_can_fire(p_ptr, FALSE)) { ADD_LABEL("Fire On", CMD_FIRE, MN_ROW_VALID); } ADD_LABEL("Throw To", CMD_THROW, MN_ROW_VALID); /* work out display region */ r.width = (int)menu_dynamic_longest_entry(m) + 3 + 2; /* +3 for tag, 2 for pad */ if (mx > Term->wid - r.width - 1) { r.col = Term->wid - r.width - 1; } else { r.col = mx + 1; } r.page_rows = m->count; if (my > Term->hgt - r.page_rows - 1) { if (my - r.page_rows - 1 <= 0) { /* menu has too many items, so put in upper right corner */ r.row = 1; r.col = Term->wid - r.width - 1; } else { r.row = Term->hgt - r.page_rows - 1; } } else { r.row = my + 1; } /* Hack -- no flush needed */ msg_flag = FALSE; screen_save(); menu_layout(m, &r); region_erase_bordered(&r); if (p_ptr->timed[TMD_IMAGE]) { prt("(Enter to select command, ESC to cancel) You see something strange:", 0, 0); } else if (c->m_idx[y][x]) { char m_name[80]; monster_type *m_ptr = cave_monster_at(c, y, x); /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND_VIS); prt(format("(Enter to select command, ESC to cancel) You see %s:", m_name), 0, 0); } else if (c->o_idx[y][x] && !squelch_item_ok(object_byid(c->o_idx[y][x]))) { char o_name[80]; /* Get the single object in the list */ object_type *o_ptr = object_byid(c->o_idx[y][x]); /* Obtain an object description */ object_desc(o_name, sizeof (o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); prt(format("(Enter to select command, ESC to cancel) You see %s:", o_name), 0, 0); } else { /* Feature (apply mimic) */ const char *name = cave_apparent_name(c, p_ptr, y, x); /* Hack -- special introduction for store doors */ if (cave_isshop(cave, y, x)) { prt(format("(Enter to select command, ESC to cancel) You see the entrance to the %s:", name), 0, 0); } else { prt(format("(Enter to select command, ESC to cancel) You see %s %s:", (is_a_vowel(name[0])) ? "an" : "a", name), 0, 0); } } selected = menu_dynamic_select(m); menu_dynamic_free(m); string_free(labels); screen_load(); cmdkey = cmd_lookup_key(selected, mode); /* Check the command to see if it is allowed. */ switch (selected) { case -1: /* User cancelled the menu. */ return 3; case MENU_VALUE_LOOK: case MENU_VALUE_RECALL: case CMD_PATHFIND: allowed = TRUE; break; case CMD_SEARCH: case CMD_ALTER: case CMD_DISARM: case CMD_JUMP: case CMD_CLOSE: case CMD_OPEN: case CMD_TUNNEL: case CMD_WALK: case CMD_RUN: case CMD_CAST: case CMD_FIRE: case CMD_THROW: case CMD_USE_ANY: /* Only check for ^ inscriptions, since we don't have an object selected (if we need one). */ allowed = key_confirm_command(cmdkey); break; default: /* Invalid command; prevent anything from happening. */ bell("Invalid context menu command."); allowed = FALSE; break; } if (!allowed) return 1; /* Perform the command. */ switch (selected) { case MENU_VALUE_LOOK: /* look at the spot */ if (target_set_interactive(TARGET_LOOK, x, y)) { msg("Target Selected."); } break; case MENU_VALUE_RECALL: { /* recall monster Info */ monster_type *m_ptr = cave_monster_at(c, y, x); if (m_ptr) { monster_lore *lore = get_lore(m_ptr->race); lore_show_interactive(m_ptr->race, lore); } } break; case CMD_SEARCH: cmd_insert(selected); break; case CMD_PATHFIND: cmd_insert(selected); cmd_set_arg_point(cmd_get_top(), 0, x, y); break; case CMD_ALTER: case CMD_DISARM: case CMD_JUMP: case CMD_CLOSE: case CMD_OPEN: case CMD_TUNNEL: case CMD_WALK: case CMD_RUN: cmd_insert(selected); cmd_set_arg_direction(cmd_get_top(), 0, coords_to_dir(y,x)); break; case CMD_CAST: if (textui_obj_cast_ret() >= 0) { cmd_set_arg_target(cmd_get_top(), 1, DIR_TARGET); } break; case CMD_FIRE: case CMD_THROW: case CMD_USE_ANY: cmd_insert(selected); cmd_set_arg_target(cmd_get_top(), 1, DIR_TARGET); break; default: break; } return 1; }
int context_menu_player(int mx, int my) { menu_type *m; region r; int selected; char *labels; bool allowed = TRUE; int mode = OPT(rogue_like_commands) ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG; unsigned char cmdkey; m = menu_dynamic_new(); if (!m) { return 0; } labels = string_make(lower_case); m->selections = labels; ADD_LABEL("Use", CMD_USE_ANY, MN_ROW_VALID); /* if player can cast, add casting option */ if (player_can_cast(p_ptr, FALSE)) { ADD_LABEL("Cast", CMD_CAST, MN_ROW_VALID); } /* if player is on stairs add option to use them */ if (cave_isupstairs(cave, p_ptr->py, p_ptr->px)) { ADD_LABEL("Go Up", CMD_GO_UP, MN_ROW_VALID); } else if (cave_isdownstairs(cave, p_ptr->py, p_ptr->px)) { ADD_LABEL("Go Down", CMD_GO_DOWN, MN_ROW_VALID); } ADD_LABEL("Search", CMD_SEARCH, MN_ROW_VALID); /* Looking has different keys, but we don't have a way to look them up (see cmd-process.c). */ cmdkey = (mode == KEYMAP_MODE_ORIG) ? 'l' : 'x'; menu_dynamic_add_label(m, "Look", cmdkey, MENU_VALUE_LOOK, labels); /* 'R' is used for resting in both keymaps. */ menu_dynamic_add_label(m, "Rest", 'R', MENU_VALUE_REST, labels); /* 'i' is used for inventory in both keymaps. */ menu_dynamic_add_label(m, "Inventory", 'i', MENU_VALUE_INVENTORY, labels); /* if object under player add pickup option */ if (cave->o_idx[p_ptr->py][p_ptr->px]) { object_type *o_ptr = object_byid(cave->o_idx[p_ptr->py][p_ptr->px]); if (!squelch_item_ok(o_ptr)) { menu_row_validity_t valid; /* 'f' isn't in rogue keymap, so we can use it here. */ menu_dynamic_add_label(m, "Floor", 'f', MENU_VALUE_FLOOR, labels); valid = (inven_carry_okay(o_ptr)) ? MN_ROW_VALID : MN_ROW_INVALID; ADD_LABEL("Pick up", CMD_PICKUP, valid); } } /* 'C' is used for the character sheet in both keymaps. */ menu_dynamic_add_label(m, "Character", 'C', MENU_VALUE_CHARACTER, labels); /* XXX Don't show the keymap line until the keymap list is implemented, to * avoid confusion as to what should be there */ /*menu_dynamic_add(m, "Keymaps", 10);*/ if (!OPT(center_player)) { menu_dynamic_add_label(m, "^Center Map", 'L', MENU_VALUE_CENTER_MAP, labels); } menu_dynamic_add_label(m, "Other", ' ', MENU_VALUE_OTHER, labels); /* work out display region */ r.width = (int)menu_dynamic_longest_entry(m) + 3 + 2; /* +3 for tag, 2 for pad */ if (mx > Term->wid - r.width - 1) { r.col = Term->wid - r.width - 1; } else { r.col = mx + 1; } r.page_rows = m->count; if (my > Term->hgt - r.page_rows - 1) { if (my - r.page_rows - 1 <= 0) { /* menu has too many items, so put in upper right corner */ r.row = 1; r.col = Term->wid - r.width - 1; } else { r.row = Term->hgt - r.page_rows - 1; } } else { r.row = my + 1; } /* Hack -- no flush needed */ msg_flag = FALSE; screen_save(); menu_layout(m, &r); region_erase_bordered(&r); prt("(Enter to select, ESC) Command:", 0, 0); selected = menu_dynamic_select(m); menu_dynamic_free(m); string_free(labels); screen_load(); cmdkey = cmd_lookup_key(selected, mode); /* Check the command to see if it is allowed. */ switch(selected) { case -1: /* User cancelled the menu. */ return 3; case CMD_USE_ANY: case CMD_CAST: case CMD_SEARCH: case CMD_GO_UP: case CMD_GO_DOWN: case CMD_PICKUP: /* Only check for ^ inscriptions, since we don't have an object selected (if we need one). */ allowed = key_confirm_command(cmdkey); break; case MENU_VALUE_REST: allowed = key_confirm_command('R'); break; case MENU_VALUE_INVENTORY: case MENU_VALUE_LOOK: case MENU_VALUE_CHARACTER: case MENU_VALUE_OTHER: case MENU_VALUE_FLOOR: case MENU_VALUE_CENTER_MAP: allowed = TRUE; break; default: /* Invalid command; prevent anything from happening. */ bell("Invalid context menu command."); allowed = FALSE; break; } if (!allowed) return 1; /* Perform the command. */ switch(selected) { case CMD_USE_ANY: case CMD_CAST: cmdkey = cmd_lookup_key(selected, mode); Term_keypress(cmdkey, 0); break; case CMD_SEARCH: case CMD_GO_UP: case CMD_GO_DOWN: case CMD_PICKUP: cmd_insert(selected); break; case MENU_VALUE_REST: Term_keypress('R', 0); break; case MENU_VALUE_INVENTORY: Term_keypress('i', 0); break; case MENU_VALUE_LOOK: if (target_set_interactive(TARGET_LOOK, p_ptr->px, p_ptr->py)) { msg("Target Selected."); } break; case MENU_VALUE_CHARACTER: Term_keypress('C', 0); break; case MENU_VALUE_OTHER: context_menu_player_2(mx, my); break; case MENU_VALUE_FLOOR: context_menu_player_display_floor(); break; case MENU_VALUE_CENTER_MAP: do_cmd_center_map(); break; default: break; } return 1; }
/** * Draw a visible path over the squares between (x1,y1) and (x2,y2). * * The path consists of "*", which are white except where there is a * monster, object or feature in the grid. * * This routine has (at least) three weaknesses: * - remembered objects/walls which are no longer present are not shown, * - squares which (e.g.) the player has walked through in the dark are * treated as unknown space. * - walls which appear strange due to hallucination aren't treated correctly. * * The first two result from information being lost from the dungeon arrays, * which requires changes elsewhere */ static int draw_path(u16b path_n, u16b *path_g, wchar_t *c, int *a, int y1, int x1) { int i; bool on_screen; /* No path, so do nothing. */ if (path_n < 1) return 0; /* The starting square is never drawn, but notice if it is being * displayed. In theory, it could be the last such square. */ on_screen = panel_contains(y1, x1); /* Draw the path. */ for (i = 0; i < path_n; i++) { byte colour; /* Find the co-ordinates on the level. */ int y = GRID_Y(path_g[i]); int x = GRID_X(path_g[i]); /* * As path[] is a straight line and the screen is oblong, * there is only section of path[] on-screen. * If the square being drawn is visible, this is part of it. * If none of it has been drawn, continue until some of it * is found or the last square is reached. * If some of it has been drawn, finish now as there are no * more visible squares to draw. */ if (panel_contains(y,x)) on_screen = TRUE; else if (on_screen) break; else continue; /* Find the position on-screen */ move_cursor_relative(y,x); /* This square is being overwritten, so save the original. */ Term_what(Term->scr->cx, Term->scr->cy, a+i, c+i); /* Choose a colour. */ if (cave->m_idx[y][x] && cave_monster_at(cave, y, x)->ml) { /* Visible monsters are red. */ monster_type *m_ptr = cave_monster_at(cave, y, x); /* Mimics act as objects */ if (rf_has(m_ptr->race->flags, RF_UNAWARE)) colour = TERM_YELLOW; else colour = TERM_L_RED; } else if (cave->o_idx[y][x] && object_byid(cave->o_idx[y][x])->marked) /* Known objects are yellow. */ colour = TERM_YELLOW; else if (!cave_ispassable(cave, y,x) && ((cave->info[y][x] & (CAVE_MARK)) || player_can_see_bold(y,x))) /* Known walls are blue. */ colour = TERM_BLUE; else if (!(cave->info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) /* Unknown squares are grey. */ colour = TERM_L_DARK; else /* Unoccupied squares are white. */ colour = TERM_WHITE; /* Draw the path segment */ (void)Term_addch(colour, L'*'); } return i; }
/* * Examine a grid, return a keypress. * * The "mode" argument contains the "TARGET_LOOK" bit flag, which * indicates that the "space" key should scan through the contents * of the grid, instead of simply returning immediately. This lets * the "look" command get complete information, without making the * "target" command annoying. * * The "info" argument contains the "commands" which should be shown * inside the "[xxx]" text. This string must never be empty, or grids * containing monsters will be displayed with an extra comma. * * Note that if a monster is in the grid, we update both the monster * recall info and the health bar info to track that monster. * * This function correctly handles multiple objects per grid, and objects * and terrain features in the same grid, though the latter never happens. * * This function must handle blindness/hallucination. */ static struct keypress target_set_interactive_aux(int y, int x, int mode) { s16b this_o_idx = 0, next_o_idx = 0; const char *s1, *s2, *s3; bool boring; int feat; int floor_list[MAX_FLOOR_STACK]; int floor_num; struct keypress query; char out_val[256]; char coords[20]; /* Describe the square location */ coords_desc(coords, sizeof(coords), y, x); /* Repeat forever */ while (1) { /* Paranoia */ query.code = ' '; /* Assume boring */ boring = TRUE; /* Default */ s1 = "You see "; s2 = ""; s3 = ""; /* The player */ if (cave->m_idx[y][x] < 0) { /* Description */ s1 = "You are "; /* Preposition */ s2 = "on "; } /* Hack -- hallucination */ if (p_ptr->timed[TMD_IMAGE]) { const char *name = "something strange"; /* Display a message */ if (p_ptr->wizard) strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); else strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return" */ if (query.code == '\n' || query.code == '\r') continue; return query; } /* Actual monsters */ 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]; /* Visible */ if (m_ptr->ml && !m_ptr->unaware) { bool recall = FALSE; char m_name[80]; /* Not boring */ boring = FALSE; /* Get the monster name ("a kobold") */ monster_desc(m_name, sizeof(m_name), m_ptr, MDESC_IND2); /* Hack -- track this monster race */ monster_race_track(m_ptr->r_idx); /* Hack -- health bar for this monster */ health_track(p_ptr, cave->m_idx[y][x]); /* Hack -- handle stuff */ handle_stuff(); /* Interact */ while (1) { /* Recall */ if (recall) { /* Save screen */ screen_save(); /* Recall on screen */ screen_roff(m_ptr->r_idx); /* Command */ query = inkey(); /* Load screen */ screen_load(); } /* Normal */ else { char buf[80]; /* Describe the monster */ look_mon_desc(buf, sizeof(buf), cave->m_idx[y][x]); /* Describe, and prompt for recall */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s (%d:%d).", s1, s2, s3, m_name, buf, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s (%s), %s.", s1, s2, s3, m_name, buf, coords); } prt(out_val, 0, 0); /* Place cursor */ move_cursor_relative(y, x); /* Command */ query = inkey(); } /* Normal commands */ if (query.code == 'r') recall = !recall; else break; } /* Stop on everything but "return"/"space" */ if (query.code != '\n' && query.code != '\r' && query.code != ' ') break; /* Sometimes stop at "space" key */ if ((query.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Take account of gender */ if (rf_has(r_ptr->flags, RF_FEMALE)) s1 = "She is "; else if (rf_has(r_ptr->flags, RF_MALE)) s1 = "He is "; else s1 = "It is "; /* Use a preposition */ s2 = "carrying "; /* Scan all objects being carried */ for (this_o_idx = m_ptr->hold_o_idx; this_o_idx; this_o_idx = next_o_idx) { char o_name[80]; object_type *o_ptr; /* Get the object */ o_ptr = object_byid(this_o_idx); /* Get the next object */ next_o_idx = o_ptr->next_o_idx; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return"/"space" */ if ((query.code != '\n') && (query.code != '\r') && (query.code != ' ')) break; /* Sometimes stop at "space" key */ if ((query.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s2 = "also carrying "; } /* Double break */ if (this_o_idx) break; /* Use a preposition */ s2 = "on "; } } /* Assume not floored */ floor_num = scan_floor(floor_list, N_ELEMENTS(floor_list), y, x, 0x02); /* Scan all marked objects in the grid */ if ((floor_num > 0) && (!(p_ptr->timed[TMD_BLIND]) || (y == p_ptr->py && x == p_ptr->px))) { /* Not boring */ boring = FALSE; track_object(-floor_list[0]); handle_stuff(); /* If there is more than one item... */ if (floor_num > 1) while (1) { /* Describe the pile */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s (%d:%d).", s1, s2, s3, floor_num, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%sa pile of %d objects, %s.", s1, s2, s3, floor_num, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Display objects */ if (query.code == 'r') { int rdone = 0; int pos; while (!rdone) { /* Save screen */ screen_save(); /* Display */ show_floor(floor_list, floor_num, (OLIST_WEIGHT | OLIST_GOLD)); /* Describe the pile */ prt(out_val, 0, 0); query = inkey(); /* Load screen */ screen_load(); pos = query.code - 'a'; if (0 <= pos && pos < floor_num) { track_object(-floor_list[pos]); handle_stuff(); continue; } rdone = 1; } /* Now that the user's done with the display loop, let's */ /* the outer loop over again */ continue; } /* Done */ break; } /* Only one object to display */ else { char o_name[80]; /* Get the single object in the list */ object_type *o_ptr = object_byid(floor_list[0]); /* Not boring */ boring = FALSE; /* Obtain an object description */ object_desc(o_name, sizeof(o_name), o_ptr, ODESC_PREFIX | ODESC_FULL); /* Describe the object */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, o_name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, o_name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return"/"space" */ if ((query.code != '\n') && (query.code != '\r') && (query.code != ' ')) break; /* Sometimes stop at "space" key */ if ((query.code == ' ') && !(mode & (TARGET_LOOK))) break; /* Change the intro */ s1 = "It is "; /* Plurals */ if (o_ptr->number != 1) s1 = "They are "; /* Preposition */ s2 = "on "; } } /* Double break */ if (this_o_idx) break; /* Feature (apply "mimic") */ feat = f_info[cave->feat[y][x]].mimic; /* Require knowledge about grid, or ability to see grid */ if (!(cave->info[y][x] & (CAVE_MARK)) && !player_can_see_bold(y,x)) { /* Forget feature */ feat = FEAT_NONE; } /* Terrain feature if needed */ if (boring || (feat > FEAT_INVIS)) { const char *name = f_info[feat].name; /* Hack -- handle unknown grids */ if (feat == FEAT_NONE) name = "unknown grid"; /* Pick a prefix */ if (*s2 && (feat >= FEAT_DOOR_HEAD)) s2 = "in "; /* Pick proper indefinite article */ s3 = (is_a_vowel(name[0])) ? "an " : "a "; /* Hack -- special introduction for store doors */ if ((feat >= FEAT_SHOP_HEAD) && (feat <= FEAT_SHOP_TAIL)) { s3 = "the entrance to the "; } /* Display a message */ if (p_ptr->wizard) { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s (%d:%d).", s1, s2, s3, name, coords, y, x); } else { strnfmt(out_val, sizeof(out_val), "%s%s%s%s, %s.", s1, s2, s3, name, coords); } prt(out_val, 0, 0); move_cursor_relative(y, x); query = inkey(); /* Stop on everything but "return"/"space" */ if ((query.code != '\n') && (query.code != '\r') && (query.code != ' ')) break; } /* Stop on everything but "return" */ if ((query.code != '\n') && (query.code != '\r')) break; } /* Keep going */ return (query); }