1 #include "autopick/autopick-entry.h"
2 #include "autopick/autopick-flags-table.h"
3 #include "autopick/autopick-key-flag-process.h"
4 #include "autopick/autopick-keys-table.h"
5 #include "autopick/autopick-methods-table.h"
6 #include "autopick/autopick-util.h"
7 #include "core/show-file.h"
8 #include "flavor/flavor-describer.h"
9 #include "flavor/object-flavor-types.h"
10 #include "floor/floor-object.h"
11 #include "monster-race/monster-race.h"
12 #include "monster-race/race-flags1.h"
13 #include "object-enchant/item-feeling.h"
14 #include "object-enchant/object-ego.h"
15 #include "object-enchant/special-object-flags.h"
16 #include "object-hook/hook-quest.h"
17 #include "object-hook/hook-weapon.h"
18 #include "object/item-use-flags.h"
19 #include "object/object-info.h"
20 #include "perception/object-perception.h"
21 #include "player-base/player-class.h"
22 #include "player/player-realm.h"
23 #include "system/baseitem-info.h"
24 #include "system/item-entity.h"
25 #include "system/monster-race-info.h"
26 #include "system/player-type-definition.h"
27 #include "util/string-processor.h"
33 static char kanji_colon[] = ":";
37 * @brief A function to create new entry
39 bool autopick_new_entry(autopick_type *entry, concptr str, bool allow_default)
41 if (str[0] && str[1] == ':') {
52 entry->flags[0] = entry->flags[1] = 0L;
56 byte act = DO_AUTOPICK | DO_DISPLAY;
58 if ((act & DO_AUTOPICK) && *str == '!') {
60 act |= DO_AUTODESTROY;
65 if ((act & DO_AUTOPICK) && *str == '~') {
72 if ((act & DO_AUTOPICK) && *str == ';') {
74 act |= DO_QUERY_AUTOPICK;
79 if ((act & DO_DISPLAY) && *str == '(') {
88 concptr insc = nullptr;
89 char buf[MAX_LINELEN];
91 for (i = 0; *str; i++) {
107 c = (char)tolower(c);
114 if (!allow_default && *buf == 0) {
117 if (*buf == 0 && insc) {
121 concptr prev_ptr, ptr;
122 ptr = prev_ptr = buf;
123 concptr old_ptr = nullptr;
124 while (old_ptr != ptr) {
126 if (MATCH_KEY(KEY_ALL)) {
129 if (MATCH_KEY(KEY_COLLECTING)) {
130 entry->add(FLG_COLLECTING);
132 if (MATCH_KEY(KEY_UNAWARE)) {
133 entry->add(FLG_UNAWARE);
135 if (MATCH_KEY(KEY_UNIDENTIFIED)) {
136 entry->add(FLG_UNIDENTIFIED);
138 if (MATCH_KEY(KEY_IDENTIFIED)) {
139 entry->add(FLG_IDENTIFIED);
141 if (MATCH_KEY(KEY_STAR_IDENTIFIED)) {
142 entry->add(FLG_STAR_IDENTIFIED);
144 if (MATCH_KEY(KEY_BOOSTED)) {
145 entry->add(FLG_BOOSTED);
148 /*** Weapons whose dd*ds is more than nn ***/
149 if (MATCH_KEY2(KEY_MORE_THAN)) {
153 while (' ' == *ptr) {
157 while ('0' <= *ptr && *ptr <= '9') {
158 entry->dice = 10 * entry->dice + (*ptr - '0');
163 if (k > 0 && k <= 2) {
164 (void)MATCH_KEY(KEY_DICE);
165 entry->add(FLG_MORE_DICE);
171 /*** Items whose magical bonus is more than n ***/
172 if (MATCH_KEY2(KEY_MORE_BONUS)) {
176 while (' ' == *ptr) {
180 while ('0' <= *ptr && *ptr <= '9') {
181 entry->bonus = 10 * entry->bonus + (*ptr - '0');
186 if (k > 0 && k <= 2) {
188 (void)MATCH_KEY(KEY_MORE_BONUS2);
194 entry->add(FLG_MORE_BONUS);
200 if (MATCH_KEY(KEY_WORTHLESS)) {
201 entry->add(FLG_WORTHLESS);
203 if (MATCH_KEY(KEY_EGO)) {
206 if (MATCH_KEY(KEY_GOOD)) {
207 entry->add(FLG_GOOD);
209 if (MATCH_KEY(KEY_NAMELESS)) {
210 entry->add(FLG_NAMELESS);
212 if (MATCH_KEY(KEY_AVERAGE)) {
213 entry->add(FLG_AVERAGE);
215 if (MATCH_KEY(KEY_RARE)) {
216 entry->add(FLG_RARE);
218 if (MATCH_KEY(KEY_COMMON)) {
219 entry->add(FLG_COMMON);
221 if (MATCH_KEY(KEY_WANTED)) {
222 entry->add(FLG_WANTED);
224 if (MATCH_KEY(KEY_UNIQUE)) {
225 entry->add(FLG_UNIQUE);
227 if (MATCH_KEY(KEY_HUMAN)) {
228 entry->add(FLG_HUMAN);
230 if (MATCH_KEY(KEY_UNREADABLE)) {
231 entry->add(FLG_UNREADABLE);
233 if (MATCH_KEY(KEY_REALM1)) {
234 entry->add(FLG_REALM1);
236 if (MATCH_KEY(KEY_REALM2)) {
237 entry->add(FLG_REALM2);
239 if (MATCH_KEY(KEY_FIRST)) {
240 entry->add(FLG_FIRST);
242 if (MATCH_KEY(KEY_SECOND)) {
243 entry->add(FLG_SECOND);
245 if (MATCH_KEY(KEY_THIRD)) {
246 entry->add(FLG_THIRD);
248 if (MATCH_KEY(KEY_FOURTH)) {
249 entry->add(FLG_FOURTH);
253 std::optional<int> previous_flag = std::nullopt;
254 if (MATCH_KEY2(KEY_ARTIFACT)) {
255 entry->add(FLG_ARTIFACT);
256 previous_flag = FLG_ARTIFACT;
259 if (MATCH_KEY2(KEY_ITEMS)) {
260 entry->add(FLG_ITEMS);
261 previous_flag = FLG_ITEMS;
262 } else if (MATCH_KEY2(KEY_WEAPONS)) {
263 entry->add(FLG_WEAPONS);
264 previous_flag = FLG_WEAPONS;
265 } else if (MATCH_KEY2(KEY_FAVORITE_WEAPONS)) {
266 entry->add(FLG_FAVORITE_WEAPONS);
267 previous_flag = FLG_FAVORITE_WEAPONS;
268 } else if (MATCH_KEY2(KEY_ARMORS)) {
269 entry->add(FLG_ARMORS);
270 previous_flag = FLG_ARMORS;
271 } else if (MATCH_KEY2(KEY_MISSILES)) {
272 entry->add(FLG_MISSILES);
273 previous_flag = FLG_MISSILES;
274 } else if (MATCH_KEY2(KEY_DEVICES)) {
275 entry->add(FLG_DEVICES);
276 previous_flag = FLG_DEVICES;
277 } else if (MATCH_KEY2(KEY_LIGHTS)) {
278 entry->add(FLG_LIGHTS);
279 previous_flag = FLG_LIGHTS;
280 } else if (MATCH_KEY2(KEY_JUNKS)) {
281 entry->add(FLG_JUNKS);
282 previous_flag = FLG_JUNKS;
283 } else if (MATCH_KEY2(KEY_CORPSES)) {
284 entry->add(FLG_CORPSES);
285 previous_flag = FLG_CORPSES;
286 } else if (MATCH_KEY2(KEY_SPELLBOOKS)) {
287 entry->add(FLG_SPELLBOOKS);
288 previous_flag = FLG_SPELLBOOKS;
289 } else if (MATCH_KEY2(KEY_HAFTED)) {
290 entry->add(FLG_HAFTED);
291 previous_flag = FLG_HAFTED;
292 } else if (MATCH_KEY2(KEY_SHIELDS)) {
293 entry->add(FLG_SHIELDS);
294 previous_flag = FLG_SHIELDS;
295 } else if (MATCH_KEY2(KEY_BOWS)) {
296 entry->add(FLG_BOWS);
297 previous_flag = FLG_BOWS;
298 } else if (MATCH_KEY2(KEY_RINGS)) {
299 entry->add(FLG_RINGS);
300 previous_flag = FLG_RINGS;
301 } else if (MATCH_KEY2(KEY_AMULETS)) {
302 entry->add(FLG_AMULETS);
303 previous_flag = FLG_AMULETS;
304 } else if (MATCH_KEY2(KEY_SUITS)) {
305 entry->add(FLG_SUITS);
306 previous_flag = FLG_SUITS;
307 } else if (MATCH_KEY2(KEY_CLOAKS)) {
308 entry->add(FLG_CLOAKS);
309 previous_flag = FLG_CLOAKS;
310 } else if (MATCH_KEY2(KEY_HELMS)) {
311 entry->add(FLG_HELMS);
312 previous_flag = FLG_HELMS;
313 } else if (MATCH_KEY2(KEY_GLOVES)) {
314 entry->add(FLG_GLOVES);
315 previous_flag = FLG_GLOVES;
316 } else if (MATCH_KEY2(KEY_BOOTS)) {
317 entry->add(FLG_BOOTS);
318 previous_flag = FLG_BOOTS;
325 else if (ptr[0] == kanji_colon[0] && ptr[1] == kanji_colon[1]) {
329 else if (*ptr == '\0') {
330 if (!previous_flag.has_value()) {
331 entry->add(FLG_ITEMS);
332 previous_flag = FLG_ITEMS;
335 if (previous_flag.has_value()) {
336 entry->remove(previous_flag.value());
343 entry->insc = insc != nullptr ? insc : "";
349 * @brief Get auto-picker entry from o_ptr.
351 void autopick_entry_from_object(PlayerType *player_ptr, autopick_type *entry, ItemEntity *o_ptr)
353 /* Assume that object name is to be added */
356 entry->insc = o_ptr->inscription.value_or("");
357 entry->action = DO_AUTOPICK | DO_DISPLAY;
358 entry->flags[0] = entry->flags[1] = 0L;
361 // エゴ銘が邪魔かもしれないので、デフォルトで「^」は付けない.
362 // We can always use the ^ mark in English.
363 bool is_hat_added = _(false, true);
364 if (!o_ptr->is_aware()) {
365 entry->add(FLG_UNAWARE);
367 } else if (!o_ptr->is_known()) {
368 if (!(o_ptr->ident & IDENT_SENSE)) {
369 entry->add(FLG_UNIDENTIFIED);
372 switch (o_ptr->feeling) {
375 entry->add(FLG_NAMELESS);
381 entry->add(FLG_NAMELESS);
382 entry->add(FLG_WORTHLESS);
388 entry->add(FLG_WORTHLESS);
403 if (o_ptr->is_ego()) {
404 if (o_ptr->is_weapon_armour_ammo()) {
405 auto &ego = o_ptr->get_ego();
407 /* エゴ銘には「^」マークが使える */
409 entry->name.append(ego.name);
411 /* We omit the basename and cannot use the ^ mark */
412 entry->name = ego.name;
415 if (!o_ptr->is_rare()) {
416 entry->add(FLG_COMMON);
421 } else if (o_ptr->is_fixed_or_random_artifact()) {
422 entry->add(FLG_ARTIFACT);
424 if (o_ptr->is_equipment()) {
425 entry->add(FLG_NAMELESS);
432 if (o_ptr->is_melee_weapon()) {
433 const auto &baseitem = o_ptr->get_baseitem();
434 if ((o_ptr->dd != baseitem.dd) || (o_ptr->ds != baseitem.ds)) {
435 entry->add(FLG_BOOSTED);
439 if (object_is_bounty(player_ptr, o_ptr)) {
440 entry->remove(FLG_WORTHLESS);
441 entry->add(FLG_WANTED);
444 const auto r_idx = i2enum<MonsterRaceId>(o_ptr->pval);
445 const auto &bi_key = o_ptr->bi_key;
446 const auto tval = bi_key.tval();
447 if ((tval == ItemKindType::CORPSE || tval == ItemKindType::STATUE) && monraces_info[r_idx].kind_flags.has(MonsterKindType::UNIQUE)) {
448 entry->add(FLG_UNIQUE);
451 if (tval == ItemKindType::CORPSE && angband_strchr("pht", monraces_info[r_idx].d_char)) {
452 entry->add(FLG_HUMAN);
455 if (o_ptr->is_spell_book() && !check_book_realm(player_ptr, bi_key)) {
456 entry->add(FLG_UNREADABLE);
457 if (tval != ItemKindType::ARCANE_BOOK) {
462 PlayerClass pc(player_ptr);
463 auto realm_except_class = pc.equals(PlayerClassType::SORCERER) || pc.equals(PlayerClassType::RED_MAGE);
465 if ((get_realm1_book(player_ptr) == tval) && !realm_except_class) {
466 entry->add(FLG_REALM1);
470 if ((get_realm2_book(player_ptr) == tval) && !realm_except_class) {
471 entry->add(FLG_REALM2);
475 const auto sval = bi_key.sval();
476 if (o_ptr->is_spell_book() && (sval == 0)) {
477 entry->add(FLG_FIRST);
479 if (o_ptr->is_spell_book() && (sval == 1)) {
480 entry->add(FLG_SECOND);
482 if (o_ptr->is_spell_book() && (sval == 2)) {
483 entry->add(FLG_THIRD);
485 if (o_ptr->is_spell_book() && (sval == 3)) {
486 entry->add(FLG_FOURTH);
489 if (o_ptr->is_ammo()) {
490 entry->add(FLG_MISSILES);
491 } else if (tval == ItemKindType::SCROLL || o_ptr->is_wand_staff() || o_ptr->is_wand_rod()) {
492 entry->add(FLG_DEVICES);
493 } else if (tval == ItemKindType::LITE) {
494 entry->add(FLG_LIGHTS);
495 } else if (o_ptr->is_junk()) {
496 entry->add(FLG_JUNKS);
497 } else if (tval == ItemKindType::CORPSE) {
498 entry->add(FLG_CORPSES);
499 } else if (o_ptr->is_spell_book()) {
500 entry->add(FLG_SPELLBOOKS);
501 } else if (o_ptr->is_melee_weapon()) {
502 entry->add(FLG_WEAPONS);
503 } else if (tval == ItemKindType::SHIELD) {
504 entry->add(FLG_SHIELDS);
505 } else if (tval == ItemKindType::BOW) {
506 entry->add(FLG_BOWS);
507 } else if (tval == ItemKindType::RING) {
508 entry->add(FLG_RINGS);
509 } else if (tval == ItemKindType::AMULET) {
510 entry->add(FLG_AMULETS);
511 } else if (o_ptr->is_armour()) {
512 entry->add(FLG_SUITS);
513 } else if (tval == ItemKindType::CLOAK) {
514 entry->add(FLG_CLOAKS);
515 } else if (tval == ItemKindType::HELM) {
516 entry->add(FLG_HELMS);
517 } else if (tval == ItemKindType::GLOVES) {
518 entry->add(FLG_GLOVES);
519 } else if (tval == ItemKindType::BOOTS) {
520 entry->add(FLG_BOOTS);
524 str_tolower(entry->name.data());
528 const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL | OD_NAME_ONLY));
531 * If necessary, add a '^' which indicates the
534 entry->name = std::string(is_hat_added ? "^" : "").append(item_name);
535 str_tolower(entry->name.data());
538 std::string shape_autopick_key(const std::string &key)
543 std::stringstream ss;
550 * @brief Reconstruct preference line from entry
552 concptr autopick_line_from_entry(const autopick_type &entry)
554 std::stringstream ss;
555 if (!(entry.action & DO_DISPLAY)) {
559 if (entry.action & DO_QUERY_AUTOPICK) {
563 if (entry.action & DO_AUTODESTROY) {
567 if (entry.action & DONT_AUTOPICK) {
571 if (entry.has(FLG_ALL)) {
572 ss << shape_autopick_key(KEY_ALL);
574 if (entry.has(FLG_COLLECTING)) {
575 ss << shape_autopick_key(KEY_COLLECTING);
577 if (entry.has(FLG_UNAWARE)) {
578 ss << shape_autopick_key(KEY_UNAWARE);
580 if (entry.has(FLG_UNIDENTIFIED)) {
581 ss << shape_autopick_key(KEY_UNIDENTIFIED);
583 if (entry.has(FLG_IDENTIFIED)) {
584 ss << shape_autopick_key(KEY_IDENTIFIED);
586 if (entry.has(FLG_STAR_IDENTIFIED)) {
587 ss << shape_autopick_key(KEY_STAR_IDENTIFIED);
589 if (entry.has(FLG_BOOSTED)) {
590 ss << shape_autopick_key(KEY_BOOSTED);
593 if (entry.has(FLG_MORE_DICE)) {
594 ss << shape_autopick_key(KEY_MORE_THAN);
596 ss << shape_autopick_key(KEY_DICE);
599 if (entry.has(FLG_MORE_BONUS)) {
600 ss << shape_autopick_key(KEY_MORE_BONUS);
602 ss << shape_autopick_key(KEY_MORE_BONUS2);
605 if (entry.has(FLG_UNREADABLE)) {
606 ss << shape_autopick_key(KEY_UNREADABLE);
609 if (entry.has(FLG_REALM1)) {
610 ss << shape_autopick_key(KEY_REALM1);
613 if (entry.has(FLG_REALM2)) {
614 ss << shape_autopick_key(KEY_REALM2);
617 if (entry.has(FLG_FIRST)) {
618 ss << shape_autopick_key(KEY_FIRST);
621 if (entry.has(FLG_SECOND)) {
622 ss << shape_autopick_key(KEY_SECOND);
625 if (entry.has(FLG_THIRD)) {
626 ss << shape_autopick_key(KEY_THIRD);
629 if (entry.has(FLG_FOURTH)) {
630 ss << shape_autopick_key(KEY_FOURTH);
633 if (entry.has(FLG_WANTED)) {
634 ss << shape_autopick_key(KEY_WANTED);
637 if (entry.has(FLG_UNIQUE)) {
638 ss << shape_autopick_key(KEY_UNIQUE);
641 if (entry.has(FLG_HUMAN)) {
642 ss << shape_autopick_key(KEY_HUMAN);
645 if (entry.has(FLG_WORTHLESS)) {
646 ss << shape_autopick_key(KEY_WORTHLESS);
649 if (entry.has(FLG_GOOD)) {
650 ss << shape_autopick_key(KEY_GOOD);
653 if (entry.has(FLG_NAMELESS)) {
654 ss << shape_autopick_key(KEY_NAMELESS);
657 if (entry.has(FLG_AVERAGE)) {
658 ss << shape_autopick_key(KEY_AVERAGE);
661 if (entry.has(FLG_RARE)) {
662 ss << shape_autopick_key(KEY_RARE);
665 if (entry.has(FLG_COMMON)) {
666 ss << shape_autopick_key(KEY_COMMON);
669 if (entry.has(FLG_EGO)) {
670 ss << shape_autopick_key(KEY_EGO);
673 if (entry.has(FLG_ARTIFACT)) {
674 ss << shape_autopick_key(KEY_ARTIFACT);
677 auto should_separate = true;
678 if (entry.has(FLG_ITEMS)) {
680 } else if (entry.has(FLG_WEAPONS)) {
682 } else if (entry.has(FLG_FAVORITE_WEAPONS)) {
683 ss << KEY_FAVORITE_WEAPONS;
684 } else if (entry.has(FLG_ARMORS)) {
686 } else if (entry.has(FLG_MISSILES)) {
688 } else if (entry.has(FLG_DEVICES)) {
690 } else if (entry.has(FLG_LIGHTS)) {
692 } else if (entry.has(FLG_JUNKS)) {
694 } else if (entry.has(FLG_CORPSES)) {
696 } else if (entry.has(FLG_SPELLBOOKS)) {
697 ss << KEY_SPELLBOOKS;
698 } else if (entry.has(FLG_HAFTED)) {
700 } else if (entry.has(FLG_SHIELDS)) {
702 } else if (entry.has(FLG_BOWS)) {
704 } else if (entry.has(FLG_RINGS)) {
706 } else if (entry.has(FLG_AMULETS)) {
708 } else if (entry.has(FLG_SUITS)) {
710 } else if (entry.has(FLG_CLOAKS)) {
712 } else if (entry.has(FLG_HELMS)) {
714 } else if (entry.has(FLG_GLOVES)) {
716 } else if (entry.has(FLG_BOOTS)) {
718 } else if (!entry.has(FLG_ARTIFACT)) {
719 should_separate = false;
722 if (!entry.name.empty()) {
723 if (should_separate) {
730 if (entry.insc.empty()) {
732 return string_make(str.data());
735 ss << '#' << entry.insc;
737 return string_make(str.data());
741 * @brief Choose an item and get auto-picker entry from it.
743 bool entry_from_choosed_object(PlayerType *player_ptr, autopick_type *entry)
745 constexpr auto q = _("どのアイテムを登録しますか? ", "Enter which item? ");
746 constexpr auto s = _("アイテムを持っていない。", "You have nothing to enter.");
747 auto *o_ptr = choose_object(player_ptr, nullptr, q, s, USE_INVEN | USE_FLOOR | USE_EQUIP);
752 autopick_entry_from_object(player_ptr, entry, o_ptr);