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/quarks.h"
28 #include "util/string-processor.h"
31 static char kanji_colon[] = ":";
35 * @brief A function to create new entry
37 bool autopick_new_entry(autopick_type *entry, concptr str, bool allow_default)
39 if (str[0] && str[1] == ':') {
50 entry->flags[0] = entry->flags[1] = 0L;
54 byte act = DO_AUTOPICK | DO_DISPLAY;
56 if ((act & DO_AUTOPICK) && *str == '!') {
58 act |= DO_AUTODESTROY;
63 if ((act & DO_AUTOPICK) && *str == '~') {
70 if ((act & DO_AUTOPICK) && *str == ';') {
72 act |= DO_QUERY_AUTOPICK;
77 if ((act & DO_DISPLAY) && *str == '(') {
86 concptr insc = nullptr;
87 char buf[MAX_LINELEN];
89 for (i = 0; *str; i++) {
105 c = (char)tolower(c);
112 if (!allow_default && *buf == 0) {
115 if (*buf == 0 && insc) {
119 concptr prev_ptr, ptr;
120 ptr = prev_ptr = buf;
121 concptr old_ptr = nullptr;
122 while (old_ptr != ptr) {
124 if (MATCH_KEY(KEY_ALL)) {
127 if (MATCH_KEY(KEY_COLLECTING)) {
128 ADD_FLG(FLG_COLLECTING);
130 if (MATCH_KEY(KEY_UNAWARE)) {
131 ADD_FLG(FLG_UNAWARE);
133 if (MATCH_KEY(KEY_UNIDENTIFIED)) {
134 ADD_FLG(FLG_UNIDENTIFIED);
136 if (MATCH_KEY(KEY_IDENTIFIED)) {
137 ADD_FLG(FLG_IDENTIFIED);
139 if (MATCH_KEY(KEY_STAR_IDENTIFIED)) {
140 ADD_FLG(FLG_STAR_IDENTIFIED);
142 if (MATCH_KEY(KEY_BOOSTED)) {
143 ADD_FLG(FLG_BOOSTED);
146 /*** Weapons whose dd*ds is more than nn ***/
147 if (MATCH_KEY2(KEY_MORE_THAN)) {
151 while (' ' == *ptr) {
155 while ('0' <= *ptr && *ptr <= '9') {
156 entry->dice = 10 * entry->dice + (*ptr - '0');
161 if (k > 0 && k <= 2) {
162 (void)MATCH_KEY(KEY_DICE);
163 ADD_FLG(FLG_MORE_DICE);
169 /*** Items whose magical bonus is more than n ***/
170 if (MATCH_KEY2(KEY_MORE_BONUS)) {
174 while (' ' == *ptr) {
178 while ('0' <= *ptr && *ptr <= '9') {
179 entry->bonus = 10 * entry->bonus + (*ptr - '0');
184 if (k > 0 && k <= 2) {
186 (void)MATCH_KEY(KEY_MORE_BONUS2);
192 ADD_FLG(FLG_MORE_BONUS);
198 if (MATCH_KEY(KEY_WORTHLESS)) {
199 ADD_FLG(FLG_WORTHLESS);
201 if (MATCH_KEY(KEY_EGO)) {
204 if (MATCH_KEY(KEY_GOOD)) {
207 if (MATCH_KEY(KEY_NAMELESS)) {
208 ADD_FLG(FLG_NAMELESS);
210 if (MATCH_KEY(KEY_AVERAGE)) {
211 ADD_FLG(FLG_AVERAGE);
213 if (MATCH_KEY(KEY_RARE)) {
216 if (MATCH_KEY(KEY_COMMON)) {
219 if (MATCH_KEY(KEY_WANTED)) {
222 if (MATCH_KEY(KEY_UNIQUE)) {
225 if (MATCH_KEY(KEY_HUMAN)) {
228 if (MATCH_KEY(KEY_UNREADABLE)) {
229 ADD_FLG(FLG_UNREADABLE);
231 if (MATCH_KEY(KEY_REALM1)) {
234 if (MATCH_KEY(KEY_REALM2)) {
237 if (MATCH_KEY(KEY_FIRST)) {
240 if (MATCH_KEY(KEY_SECOND)) {
243 if (MATCH_KEY(KEY_THIRD)) {
246 if (MATCH_KEY(KEY_FOURTH)) {
252 if (MATCH_KEY2(KEY_ARTIFACT)) {
253 ADD_FLG_NOUN(FLG_ARTIFACT);
256 if (MATCH_KEY2(KEY_ITEMS)) {
257 ADD_FLG_NOUN(FLG_ITEMS);
258 } else if (MATCH_KEY2(KEY_WEAPONS)) {
259 ADD_FLG_NOUN(FLG_WEAPONS);
260 } else if (MATCH_KEY2(KEY_FAVORITE_WEAPONS)) {
261 ADD_FLG_NOUN(FLG_FAVORITE_WEAPONS);
262 } else if (MATCH_KEY2(KEY_ARMORS)) {
263 ADD_FLG_NOUN(FLG_ARMORS);
264 } else if (MATCH_KEY2(KEY_MISSILES)) {
265 ADD_FLG_NOUN(FLG_MISSILES);
266 } else if (MATCH_KEY2(KEY_DEVICES)) {
267 ADD_FLG_NOUN(FLG_DEVICES);
268 } else if (MATCH_KEY2(KEY_LIGHTS)) {
269 ADD_FLG_NOUN(FLG_LIGHTS);
270 } else if (MATCH_KEY2(KEY_JUNKS)) {
271 ADD_FLG_NOUN(FLG_JUNKS);
272 } else if (MATCH_KEY2(KEY_CORPSES)) {
273 ADD_FLG_NOUN(FLG_CORPSES);
274 } else if (MATCH_KEY2(KEY_SPELLBOOKS)) {
275 ADD_FLG_NOUN(FLG_SPELLBOOKS);
276 } else if (MATCH_KEY2(KEY_HAFTED)) {
277 ADD_FLG_NOUN(FLG_HAFTED);
278 } else if (MATCH_KEY2(KEY_SHIELDS)) {
279 ADD_FLG_NOUN(FLG_SHIELDS);
280 } else if (MATCH_KEY2(KEY_BOWS)) {
281 ADD_FLG_NOUN(FLG_BOWS);
282 } else if (MATCH_KEY2(KEY_RINGS)) {
283 ADD_FLG_NOUN(FLG_RINGS);
284 } else if (MATCH_KEY2(KEY_AMULETS)) {
285 ADD_FLG_NOUN(FLG_AMULETS);
286 } else if (MATCH_KEY2(KEY_SUITS)) {
287 ADD_FLG_NOUN(FLG_SUITS);
288 } else if (MATCH_KEY2(KEY_CLOAKS)) {
289 ADD_FLG_NOUN(FLG_CLOAKS);
290 } else if (MATCH_KEY2(KEY_HELMS)) {
291 ADD_FLG_NOUN(FLG_HELMS);
292 } else if (MATCH_KEY2(KEY_GLOVES)) {
293 ADD_FLG_NOUN(FLG_GLOVES);
294 } else if (MATCH_KEY2(KEY_BOOTS)) {
295 ADD_FLG_NOUN(FLG_BOOTS);
302 else if (ptr[0] == kanji_colon[0] && ptr[1] == kanji_colon[1]) {
306 else if (*ptr == '\0') {
307 if (prev_flg == -1) {
308 ADD_FLG_NOUN(FLG_ITEMS);
311 if (prev_flg != -1) {
312 entry->flags[prev_flg / 32] &= ~(1UL << (prev_flg % 32));
319 entry->insc = insc != nullptr ? insc : "";
325 * @brief Get auto-picker entry from o_ptr.
327 void autopick_entry_from_object(PlayerType *player_ptr, autopick_type *entry, ItemEntity *o_ptr)
329 /* Assume that object name is to be added */
332 entry->insc = o_ptr->inscription.value_or("");
333 entry->action = DO_AUTOPICK | DO_DISPLAY;
334 entry->flags[0] = entry->flags[1] = 0L;
337 // エゴ銘が邪魔かもしれないので、デフォルトで「^」は付けない.
338 // We can always use the ^ mark in English.
339 bool is_hat_added = _(false, true);
340 if (!o_ptr->is_aware()) {
341 ADD_FLG(FLG_UNAWARE);
343 } else if (!o_ptr->is_known()) {
344 if (!(o_ptr->ident & IDENT_SENSE)) {
345 ADD_FLG(FLG_UNIDENTIFIED);
348 switch (o_ptr->feeling) {
351 ADD_FLG(FLG_NAMELESS);
357 ADD_FLG(FLG_NAMELESS);
358 ADD_FLG(FLG_WORTHLESS);
364 ADD_FLG(FLG_WORTHLESS);
379 if (o_ptr->is_ego()) {
380 if (o_ptr->is_weapon_armour_ammo()) {
382 * Base name of ego weapons and armors
383 * are almost meaningless.
384 * Register the ego type only.
386 auto *e_ptr = &egos_info[o_ptr->ego_idx];
388 /* エゴ銘には「^」マークが使える */
390 entry->name.append(e_ptr->name);
392 /* We omit the basename and cannot use the ^ mark */
393 entry->name = e_ptr->name;
396 if (!o_ptr->is_rare()) {
402 } else if (o_ptr->is_fixed_or_random_artifact()) {
403 ADD_FLG(FLG_ARTIFACT);
405 if (o_ptr->is_equipment()) {
406 ADD_FLG(FLG_NAMELESS);
413 if (o_ptr->is_melee_weapon()) {
414 const auto &baseitem = baseitems_info[o_ptr->bi_id];
415 if ((o_ptr->dd != baseitem.dd) || (o_ptr->ds != baseitem.ds)) {
416 ADD_FLG(FLG_BOOSTED);
420 if (object_is_bounty(player_ptr, o_ptr)) {
421 REM_FLG(FLG_WORTHLESS);
425 const auto r_idx = i2enum<MonsterRaceId>(o_ptr->pval);
426 const auto &bi_key = o_ptr->bi_key;
427 const auto tval = bi_key.tval();
428 if ((tval == ItemKindType::CORPSE || tval == ItemKindType::STATUE) && monraces_info[r_idx].kind_flags.has(MonsterKindType::UNIQUE)) {
432 if (tval == ItemKindType::CORPSE && angband_strchr("pht", monraces_info[r_idx].d_char)) {
436 if (o_ptr->is_spell_book() && !check_book_realm(player_ptr, bi_key)) {
437 ADD_FLG(FLG_UNREADABLE);
438 if (tval != ItemKindType::ARCANE_BOOK) {
443 PlayerClass pc(player_ptr);
444 auto realm_except_class = pc.equals(PlayerClassType::SORCERER) || pc.equals(PlayerClassType::RED_MAGE);
446 if ((get_realm1_book(player_ptr) == tval) && !realm_except_class) {
451 if ((get_realm2_book(player_ptr) == tval) && !realm_except_class) {
456 const auto sval = bi_key.sval();
457 if (o_ptr->is_spell_book() && (sval == 0)) {
460 if (o_ptr->is_spell_book() && (sval == 1)) {
463 if (o_ptr->is_spell_book() && (sval == 2)) {
466 if (o_ptr->is_spell_book() && (sval == 3)) {
470 if (o_ptr->is_ammo()) {
471 ADD_FLG(FLG_MISSILES);
472 } else if (tval == ItemKindType::SCROLL || o_ptr->is_wand_staff() || o_ptr->is_wand_rod()) {
473 ADD_FLG(FLG_DEVICES);
474 } else if (tval == ItemKindType::LITE) {
476 } else if (o_ptr->is_junk()) {
478 } else if (tval == ItemKindType::CORPSE) {
479 ADD_FLG(FLG_CORPSES);
480 } else if (o_ptr->is_spell_book()) {
481 ADD_FLG(FLG_SPELLBOOKS);
482 } else if (o_ptr->is_melee_weapon()) {
483 ADD_FLG(FLG_WEAPONS);
484 } else if (tval == ItemKindType::SHIELD) {
485 ADD_FLG(FLG_SHIELDS);
486 } else if (tval == ItemKindType::BOW) {
488 } else if (tval == ItemKindType::RING) {
490 } else if (tval == ItemKindType::AMULET) {
491 ADD_FLG(FLG_AMULETS);
492 } else if (o_ptr->is_armour()) {
494 } else if (tval == ItemKindType::CLOAK) {
496 } else if (tval == ItemKindType::HELM) {
498 } else if (tval == ItemKindType::GLOVES) {
500 } else if (tval == ItemKindType::BOOTS) {
505 str_tolower(entry->name.data());
509 const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL | OD_NAME_ONLY));
512 * If necessary, add a '^' which indicates the
515 entry->name = std::string(is_hat_added ? "^" : "").append(item_name);
516 str_tolower(entry->name.data());
520 * @brief Reconstruct preference line from entry
522 concptr autopick_line_from_entry(const autopick_type &entry)
524 char buf[MAX_LINELEN]{};
525 if (!(entry.action & DO_DISPLAY)) {
528 if (entry.action & DO_QUERY_AUTOPICK) {
531 if (entry.action & DO_AUTODESTROY) {
534 if (entry.action & DONT_AUTOPICK) {
540 if (entry.has(FLG_ALL)) {
543 if (entry.has(FLG_COLLECTING)) {
544 ADD_KEY(KEY_COLLECTING);
546 if (entry.has(FLG_UNAWARE)) {
547 ADD_KEY(KEY_UNAWARE);
549 if (entry.has(FLG_UNIDENTIFIED)) {
550 ADD_KEY(KEY_UNIDENTIFIED);
552 if (entry.has(FLG_IDENTIFIED)) {
553 ADD_KEY(KEY_IDENTIFIED);
555 if (entry.has(FLG_STAR_IDENTIFIED)) {
556 ADD_KEY(KEY_STAR_IDENTIFIED);
558 if (entry.has(FLG_BOOSTED)) {
559 ADD_KEY(KEY_BOOSTED);
562 if (entry.has(FLG_MORE_DICE)) {
563 ADD_KEY(KEY_MORE_THAN);
564 strcat(ptr, format("%d", entry.dice).data());
568 if (entry.has(FLG_MORE_BONUS)) {
569 ADD_KEY(KEY_MORE_BONUS);
570 strcat(ptr, format("%d", entry.bonus).data());
571 ADD_KEY(KEY_MORE_BONUS2);
574 if (entry.has(FLG_UNREADABLE)) {
575 ADD_KEY(KEY_UNREADABLE);
577 if (entry.has(FLG_REALM1)) {
580 if (entry.has(FLG_REALM2)) {
583 if (entry.has(FLG_FIRST)) {
586 if (entry.has(FLG_SECOND)) {
589 if (entry.has(FLG_THIRD)) {
592 if (entry.has(FLG_FOURTH)) {
595 if (entry.has(FLG_WANTED)) {
598 if (entry.has(FLG_UNIQUE)) {
601 if (entry.has(FLG_HUMAN)) {
604 if (entry.has(FLG_WORTHLESS)) {
605 ADD_KEY(KEY_WORTHLESS);
607 if (entry.has(FLG_GOOD)) {
610 if (entry.has(FLG_NAMELESS)) {
611 ADD_KEY(KEY_NAMELESS);
613 if (entry.has(FLG_AVERAGE)) {
614 ADD_KEY(KEY_AVERAGE);
616 if (entry.has(FLG_RARE)) {
619 if (entry.has(FLG_COMMON)) {
622 if (entry.has(FLG_EGO)) {
626 if (entry.has(FLG_ARTIFACT)) {
627 ADD_KEY(KEY_ARTIFACT);
630 bool sepa_flag = true;
631 if (entry.has(FLG_ITEMS)) {
633 } else if (entry.has(FLG_WEAPONS)) {
634 ADD_KEY2(KEY_WEAPONS);
635 } else if (entry.has(FLG_FAVORITE_WEAPONS)) {
636 ADD_KEY2(KEY_FAVORITE_WEAPONS);
637 } else if (entry.has(FLG_ARMORS)) {
638 ADD_KEY2(KEY_ARMORS);
639 } else if (entry.has(FLG_MISSILES)) {
640 ADD_KEY2(KEY_MISSILES);
641 } else if (entry.has(FLG_DEVICES)) {
642 ADD_KEY2(KEY_DEVICES);
643 } else if (entry.has(FLG_LIGHTS)) {
644 ADD_KEY2(KEY_LIGHTS);
645 } else if (entry.has(FLG_JUNKS)) {
647 } else if (entry.has(FLG_CORPSES)) {
648 ADD_KEY2(KEY_CORPSES);
649 } else if (entry.has(FLG_SPELLBOOKS)) {
650 ADD_KEY2(KEY_SPELLBOOKS);
651 } else if (entry.has(FLG_HAFTED)) {
652 ADD_KEY2(KEY_HAFTED);
653 } else if (entry.has(FLG_SHIELDS)) {
654 ADD_KEY2(KEY_SHIELDS);
655 } else if (entry.has(FLG_BOWS)) {
657 } else if (entry.has(FLG_RINGS)) {
659 } else if (entry.has(FLG_AMULETS)) {
660 ADD_KEY2(KEY_AMULETS);
661 } else if (entry.has(FLG_SUITS)) {
663 } else if (entry.has(FLG_CLOAKS)) {
664 ADD_KEY2(KEY_CLOAKS);
665 } else if (entry.has(FLG_HELMS)) {
667 } else if (entry.has(FLG_GLOVES)) {
668 ADD_KEY2(KEY_GLOVES);
669 } else if (entry.has(FLG_BOOTS)) {
671 } else if (!entry.has(FLG_ARTIFACT)) {
675 if (!entry.name.empty()) {
682 while (entry.name[j] && i < MAX_LINELEN - 2 - 1) {
684 if (iskanji(entry.name[j])) {
685 buf[i++] = entry.name[j++];
688 buf[i++] = entry.name[j++];
693 if (entry.insc.empty()) {
694 return string_make(buf);
701 while (entry.insc[j] && i < MAX_LINELEN - 2) {
703 if (iskanji(entry.insc[j])) {
704 buf[i++] = entry.insc[j++];
707 buf[i++] = entry.insc[j++];
711 return string_make(buf);
715 * @brief Reconstruct preference line from entry and kill entry
717 concptr autopick_line_from_entry_kill(autopick_type *entry)
719 return autopick_line_from_entry(*entry);
723 * @brief Choose an item and get auto-picker entry from it.
725 bool entry_from_choosed_object(PlayerType *player_ptr, autopick_type *entry)
727 constexpr auto q = _("どのアイテムを登録しますか? ", "Enter which item? ");
728 constexpr auto s = _("アイテムを持っていない。", "You have nothing to enter.");
729 auto *o_ptr = choose_object(player_ptr, nullptr, q, s, USE_INVEN | USE_FLOOR | USE_EQUIP);
734 autopick_entry_from_object(player_ptr, entry, o_ptr);