3 * @brief 自動拾い機能の実装 / Object Auto-picker/Destroyer
6 * Copyright (c) 2002 Mogami\n
8 * This software may be copied and distributed for educational, research, and\n
9 * not for profit purposes provided that this copyright and statement are\n
10 * included in all such copies.\n
11 * 2014 Deskull rearranged comment for Doxygen.\n
16 #include "autopick/autopick-commands-table.h"
17 #include "autopick/autopick-dirty-flags.h"
18 #include "autopick/autopick-flags-table.h"
19 #include "autopick/autopick-initializer.h"
20 #include "autopick/autopick-key-flag-process.h"
21 #include "autopick/autopick-matcher.h"
22 #include "autopick/autopick-menu-data-table.h"
23 #include "autopick/autopick-methods-table.h"
24 #include "autopick/autopick-keys-table.h"
25 #include "autopick/autopick-entry.h"
27 #include "autopick/autopick.h"
29 #include "core/show-file.h"
30 #include "cmd/cmd-save.h"
31 #include "io/read-pref-file.h"
35 #include "market/store.h"
36 #include "player-status.h"
37 #include "player-move.h"
38 #include "player-class.h"
39 #include "player-race.h"
40 #include "player-inventory.h"
41 #include "view/display-player.h"
42 #include "object/object-kind.h"
43 #include "object-ego.h"
44 #include "object-flavor.h"
45 #include "object-hook.h"
51 #include "monsterrace.h"
52 #include "view-mainwindow.h" // 暫定。後で消す
55 * Automatically destroy an item if it is to be destroyed
57 * When always_pickup is 'yes', we disable auto-destroyer function of
58 * auto-picker/destroyer, and do only easy-auto-destroyer.
60 static object_type autopick_last_destroyed_object;
63 * Get file name for autopick preference
65 static concptr pickpref_filename(player_type *player_ptr, int filename_mode)
67 static const char namebase[] = _("picktype", "pickpref");
69 switch (filename_mode)
72 return format("%s.prf", namebase);
75 return format("%s-%s.prf", namebase, player_ptr->base_name);
84 * Load an autopick preference file
86 void autopick_load_pref(player_type *player_ptr, bool disp_mes)
90 my_strcpy(buf, pickpref_filename(player_ptr, PT_WITH_PNAME), sizeof(buf));
91 errr err = process_autopick_file(player_ptr, buf);
92 if (err == 0 && disp_mes)
94 msg_format(_("%sを読み込みました。", "Loaded '%s'."), buf);
99 my_strcpy(buf, pickpref_filename(player_ptr, PT_DEFAULT), sizeof(buf));
100 err = process_autopick_file(player_ptr, buf);
101 if (err == 0 && disp_mes)
103 msg_format(_("%sを読み込みました。", "Loaded '%s'."), buf);
109 msg_print(_("自動拾い設定ファイルの読み込みに失敗しました。", "Failed to reload autopick preference."));
115 * Add one line to autopick_list[]
117 static void add_autopick_list(autopick_type *entry)
119 if (max_autopick >= max_max_autopick)
121 int old_max_max_autopick = max_max_autopick;
122 autopick_type *old_autopick_list = autopick_list;
123 max_max_autopick += MAX_AUTOPICK_DEFAULT;
124 C_MAKE(autopick_list, max_max_autopick, autopick_type);
125 (void)C_COPY(autopick_list, old_autopick_list, old_max_max_autopick, autopick_type);
126 C_KILL(old_autopick_list, old_max_max_autopick, autopick_type);
129 autopick_list[max_autopick] = *entry;
135 * Process line for auto picker/destroyer.
137 void process_autopick_file_command(char *buf)
139 autopick_type an_entry, *entry = &an_entry;
141 for (i = 0; buf[i]; i++)
150 if (iswspace(buf[i]) && buf[i] != ' ')
155 if (!autopick_new_entry(entry, buf, FALSE)) return;
157 for (i = 0; i < max_autopick; i++)
159 if (!strcmp(entry->name, autopick_list[i].name)
160 && entry->flag[0] == autopick_list[i].flag[0]
161 && entry->flag[1] == autopick_list[i].flag[1]
162 && entry->dice == autopick_list[i].dice
163 && entry->bonus == autopick_list[i].bonus)
165 autopick_free_entry(entry);
170 add_autopick_list(entry);
176 * A function for Auto-picker/destroyer
177 * Examine whether the object matches to the list of keywords or not.
179 int is_autopick(player_type *player_ptr, object_type *o_ptr)
181 GAME_TEXT o_name[MAX_NLEN];
182 if (o_ptr->tval == TV_GOLD) return -1;
184 object_desc(player_ptr, o_name, o_ptr, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
186 for (int i = 0; i < max_autopick; i++)
188 autopick_type *entry = &autopick_list[i];
189 if (is_autopick_match(player_ptr, o_ptr, entry, o_name))
200 static void auto_inscribe_item(player_type *player_ptr, object_type *o_ptr, int idx)
202 if (idx < 0 || !autopick_list[idx].insc) return;
204 if (!o_ptr->inscription)
205 o_ptr->inscription = quark_add(autopick_list[idx].insc);
207 player_ptr->window |= (PW_EQUIP | PW_INVEN);
208 player_ptr->update |= (PU_BONUS);
213 * Automatically destroy items in this grid.
215 static bool is_opt_confirm_destroy(player_type *player_ptr, object_type *o_ptr)
217 if (!destroy_items) return FALSE;
220 if (object_value(o_ptr) > 0) return FALSE;
223 if (object_is_weapon_armour_ammo(o_ptr)) return FALSE;
226 if ((o_ptr->tval == TV_CHEST) && o_ptr->pval) return FALSE;
230 if (object_is_bounty(o_ptr)) return FALSE;
234 if (o_ptr->tval == TV_CORPSE) return FALSE;
237 if ((o_ptr->tval == TV_SKELETON) || (o_ptr->tval == TV_BOTTLE) || (o_ptr->tval == TV_JUNK) || (o_ptr->tval == TV_STATUE)) return FALSE;
241 if (player_ptr->prace == RACE_DEMON)
243 if (o_ptr->tval == TV_CORPSE &&
244 o_ptr->sval == SV_CORPSE &&
245 my_strchr("pht", r_info[o_ptr->pval].d_char))
249 if (player_ptr->pclass == CLASS_ARCHER)
251 if (o_ptr->tval == TV_SKELETON ||
252 (o_ptr->tval == TV_CORPSE && o_ptr->sval == SV_SKELETON))
255 else if (player_ptr->pclass == CLASS_NINJA)
257 if (o_ptr->tval == TV_LITE &&
258 o_ptr->name2 == EGO_LITE_DARKNESS && object_is_known(o_ptr))
261 else if (player_ptr->pclass == CLASS_BEASTMASTER ||
262 player_ptr->pclass == CLASS_CAVALRY)
264 if (o_ptr->tval == TV_WAND &&
265 o_ptr->sval == SV_WAND_HEAL_MONSTER && object_is_aware(o_ptr))
270 if (o_ptr->tval == TV_GOLD) return FALSE;
276 static void auto_destroy_item(player_type *player_ptr, object_type *o_ptr, int autopick_idx)
278 bool destroy = FALSE;
279 if (is_opt_confirm_destroy(player_ptr, o_ptr)) destroy = TRUE;
281 if (autopick_idx >= 0 &&
282 !(autopick_list[autopick_idx].action & DO_AUTODESTROY))
287 if (autopick_idx >= 0 &&
288 (autopick_list[autopick_idx].action & DO_AUTODESTROY))
292 if (!destroy) return;
294 disturb(player_ptr, FALSE, FALSE);
295 if (!can_player_destroy_object(o_ptr))
297 GAME_TEXT o_name[MAX_NLEN];
298 object_desc(player_ptr, o_name, o_ptr, 0);
299 msg_format(_("%sは破壊不能だ。", "You cannot auto-destroy %s."), o_name);
303 (void)COPY(&autopick_last_destroyed_object, o_ptr, object_type);
304 o_ptr->marked |= OM_AUTODESTROY;
305 player_ptr->update |= PU_AUTODESTROY;
310 * Auto-destroy marked item
312 static void autopick_delayed_alter_aux(player_type *player_ptr, INVENTORY_IDX item)
315 o_ptr = REF_ITEM(player_ptr, player_ptr->current_floor_ptr, item);
317 if (o_ptr->k_idx == 0 || !(o_ptr->marked & OM_AUTODESTROY)) return;
319 GAME_TEXT o_name[MAX_NLEN];
320 object_desc(player_ptr, o_name, o_ptr, 0);
323 inven_item_increase(player_ptr, item, -(o_ptr->number));
324 inven_item_optimize(player_ptr, item);
328 delete_object_idx(player_ptr, 0 - item);
331 msg_format(_("%sを自動破壊します。", "Auto-destroying %s."), o_name);
336 * Auto-destroy marked items in inventry and on floor
338 void autopick_delayed_alter(player_type *owner_ptr)
343 * Scan inventry in reverse order to prevent
344 * skipping after inven_item_optimize()
346 for (item = INVEN_TOTAL - 1; item >= 0; item--)
347 autopick_delayed_alter_aux(owner_ptr, item);
349 floor_type *floor_ptr = owner_ptr->current_floor_ptr;
350 item = floor_ptr->grid_array[owner_ptr->y][owner_ptr->x].o_idx;
353 OBJECT_IDX next = floor_ptr->o_list[item].next_o_idx;
354 autopick_delayed_alter_aux(owner_ptr, -item);
361 * Auto-inscription and/or destroy
363 * Auto-destroyer works only on inventory or on floor stack only when
366 void autopick_alter_item(player_type *player_ptr, INVENTORY_IDX item, bool destroy)
369 o_ptr = REF_ITEM(player_ptr, player_ptr->current_floor_ptr, item);
370 int idx = is_autopick(player_ptr, o_ptr);
371 auto_inscribe_item(player_ptr, o_ptr, idx);
372 if (destroy && item <= INVEN_PACK)
373 auto_destroy_item(player_ptr, o_ptr, idx);
378 * Automatically pickup/destroy items in this grid.
380 void autopick_pickup_items(player_type* player_ptr, grid_type *g_ptr)
382 OBJECT_IDX this_o_idx, next_o_idx = 0;
383 for (this_o_idx = g_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
385 object_type *o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
386 next_o_idx = o_ptr->next_o_idx;
387 int idx = is_autopick(player_ptr, o_ptr);
388 auto_inscribe_item(player_ptr, o_ptr, idx);
389 bool is_auto_pickup = idx >= 0;
390 is_auto_pickup &= (autopick_list[idx].action & (DO_AUTOPICK | DO_QUERY_AUTOPICK)) != 0;
393 auto_destroy_item(player_ptr, o_ptr, idx);
397 disturb(player_ptr, FALSE, FALSE);
398 if (!inven_carry_okay(o_ptr))
400 GAME_TEXT o_name[MAX_NLEN];
401 object_desc(player_ptr, o_name, o_ptr, 0);
402 msg_format(_("ザックには%sを入れる隙間がない。", "You have no room for %s."), o_name);
403 o_ptr->marked |= OM_NOMSG;
407 if (!(autopick_list[idx].action & DO_QUERY_AUTOPICK))
409 py_pickup_aux(player_ptr, this_o_idx);
413 char out_val[MAX_NLEN + 20];
414 GAME_TEXT o_name[MAX_NLEN];
415 if (o_ptr->marked & OM_NO_QUERY)
420 object_desc(player_ptr, o_name, o_ptr, 0);
421 sprintf(out_val, _("%sを拾いますか? ", "Pick up %s? "), o_name);
422 if (!get_check(out_val))
424 o_ptr->marked |= OM_NOMSG | OM_NO_QUERY;
428 py_pickup_aux(player_ptr, this_o_idx);
433 static const char autoregister_header[] = "?:$AUTOREGISTER";
436 * Clear auto registered lines in the picktype.prf .
438 static bool clear_auto_register(player_type *player_ptr)
441 char pref_file[1024];
446 bool autoregister = FALSE;
449 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_WITH_PNAME));
450 pref_fff = my_fopen(pref_file, "r");
454 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_DEFAULT));
455 pref_fff = my_fopen(pref_file, "r");
463 tmp_fff = my_fopen_temp(tmp_file, sizeof(tmp_file));
467 msg_format(_("一時ファイル %s を作成できませんでした。", "Failed to create temporary file %s."), tmp_file);
474 if (my_fgets(pref_fff, buf, sizeof(buf))) break;
478 if (buf[0] != '#' && buf[0] != '?') num++;
482 if (streq(buf, autoregister_header))
488 fprintf(tmp_fff, "%s\n", buf);
497 msg_format(_("以前のキャラクター用の自動設定(%d行)が残っています。",
498 "Auto registered lines (%d lines) for previous character are remaining."), num);
499 strcpy(buf, _("古い設定行は削除します。よろしいですか?", "These lines will be deleted. Are you sure? "));
504 autoregister = FALSE;
506 msg_print(_("エディタのカット&ペースト等を使って必要な行を避難してください。",
507 "Use cut & paste of auto picker editor (_) to keep old prefs."));
513 tmp_fff = my_fopen(tmp_file, "r");
514 pref_fff = my_fopen(pref_file, "w");
516 while (!my_fgets(tmp_fff, buf, sizeof(buf)))
517 fprintf(pref_fff, "%s\n", buf);
529 * Automatically register an auto-destroy preference line
531 bool autopick_autoregister(player_type *player_ptr, object_type *o_ptr)
534 char pref_file[1024];
536 autopick_type an_entry, *entry = &an_entry;
537 int match_autopick = is_autopick(player_ptr, o_ptr);
538 if (match_autopick != -1)
541 byte act = autopick_list[match_autopick].action;
542 if (act & DO_AUTOPICK) what = _("自動で拾う", "auto-pickup");
543 else if (act & DO_AUTODESTROY) what = _("自動破壊する", "auto-destroy");
544 else if (act & DONT_AUTOPICK) what = _("放置する", "leave on floor");
545 else what = _("確認して拾う", "query auto-pickup");
547 msg_format(_("そのアイテムは既に%sように設定されています。", "The object is already registered to %s."), what);
551 if ((object_is_known(o_ptr) && object_is_artifact(o_ptr)) ||
552 ((o_ptr->ident & IDENT_SENSE) &&
553 (o_ptr->feeling == FEEL_TERRIBLE || o_ptr->feeling == FEEL_SPECIAL)))
555 GAME_TEXT o_name[MAX_NLEN];
556 object_desc(player_ptr, o_name, o_ptr, 0);
557 msg_format(_("%sは破壊不能だ。", "You cannot auto-destroy %s."), o_name);
561 if (!player_ptr->autopick_autoregister)
563 if (!clear_auto_register(player_ptr)) return FALSE;
566 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_WITH_PNAME));
567 pref_fff = my_fopen(pref_file, "r");
571 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_DEFAULT));
572 pref_fff = my_fopen(pref_file, "r");
579 if (my_fgets(pref_fff, buf, sizeof(buf)))
581 player_ptr->autopick_autoregister = FALSE;
585 if (streq(buf, autoregister_header))
587 player_ptr->autopick_autoregister = TRUE;
597 * File could not be opened for reading. Assume header not
600 player_ptr->autopick_autoregister = FALSE;
603 pref_fff = my_fopen(pref_file, "a");
606 msg_format(_("%s を開くことができませんでした。", "Failed to open %s."), pref_file);
611 if (!player_ptr->autopick_autoregister)
613 fprintf(pref_fff, "%s\n", autoregister_header);
615 fprintf(pref_fff, "%s\n", _("# *警告!!* 以降の行は自動登録されたものです。",
616 "# *Warning!* The lines below will be deleted later."));
617 fprintf(pref_fff, "%s\n", _("# 後で自動的に削除されますので、必要な行は上の方へ移動しておいてください。",
618 "# Keep it by cut & paste if you need these lines for future characters."));
619 player_ptr->autopick_autoregister = TRUE;
622 autopick_entry_from_object(player_ptr, entry, o_ptr);
623 entry->action = DO_AUTODESTROY;
624 add_autopick_list(entry);
626 concptr tmp = autopick_line_from_entry(entry);
627 fprintf(pref_fff, "%s\n", tmp);
635 * Describe which kind of object is Auto-picked/destroyed
637 static void describe_autopick(char *buff, autopick_type *entry)
639 concptr str = entry->name;
640 byte act = entry->action;
641 concptr insc = entry->insc;
647 concptr before_str[100], body_str;
651 if (IS_FLG(FLG_COLLECTING))
652 before_str[before_n++] = "収集中で既に持っているスロットにまとめられる";
654 if (IS_FLG(FLG_UNAWARE))
655 before_str[before_n++] = "未鑑定でその効果も判明していない";
657 if (IS_FLG(FLG_UNIDENTIFIED))
658 before_str[before_n++] = "未鑑定の";
660 if (IS_FLG(FLG_IDENTIFIED))
661 before_str[before_n++] = "鑑定済みの";
663 if (IS_FLG(FLG_STAR_IDENTIFIED))
664 before_str[before_n++] = "完全に鑑定済みの";
666 if (IS_FLG(FLG_BOOSTED))
668 before_str[before_n++] = "ダメージダイスが通常より大きい";
672 if (IS_FLG(FLG_MORE_DICE))
674 static char more_than_desc_str[] = "___";
675 before_str[before_n++] = "ダメージダイスの最大値が";
678 sprintf(more_than_desc_str, "%d", entry->dice);
679 before_str[before_n++] = more_than_desc_str;
680 before_str[before_n++] = "以上の";
683 if (IS_FLG(FLG_MORE_BONUS))
685 static char more_bonus_desc_str[] = "___";
686 before_str[before_n++] = "修正値が(+";
688 sprintf(more_bonus_desc_str, "%d", entry->bonus);
689 before_str[before_n++] = more_bonus_desc_str;
690 before_str[before_n++] = ")以上の";
693 if (IS_FLG(FLG_WORTHLESS))
694 before_str[before_n++] = "店で無価値と判定される";
696 if (IS_FLG(FLG_ARTIFACT))
698 before_str[before_n++] = "アーティファクトの";
704 before_str[before_n++] = "エゴアイテムの";
708 if (IS_FLG(FLG_GOOD))
710 before_str[before_n++] = "上質の";
714 if (IS_FLG(FLG_NAMELESS))
716 before_str[before_n++] = "エゴでもアーティファクトでもない";
720 if (IS_FLG(FLG_AVERAGE))
722 before_str[before_n++] = "並の";
726 if (IS_FLG(FLG_RARE))
728 before_str[before_n++] = "ドラゴン装備やカオス・ブレード等を含む珍しい";
732 if (IS_FLG(FLG_COMMON))
734 before_str[before_n++] = "ありふれた(ドラゴン装備やカオス・ブレード等の珍しい物ではない)";
738 if (IS_FLG(FLG_WANTED))
740 before_str[before_n++] = "ハンター事務所で賞金首とされている";
744 if (IS_FLG(FLG_HUMAN))
746 before_str[before_n++] = "悪魔魔法で使うための人間やヒューマノイドの";
750 if (IS_FLG(FLG_UNIQUE))
752 before_str[before_n++] = "ユニークモンスターの";
756 if (IS_FLG(FLG_UNREADABLE))
758 before_str[before_n++] = "あなたが読めない領域の";
762 if (IS_FLG(FLG_REALM1))
764 before_str[before_n++] = "第一領域の";
768 if (IS_FLG(FLG_REALM2))
770 before_str[before_n++] = "第二領域の";
774 if (IS_FLG(FLG_FIRST))
776 before_str[before_n++] = "全4冊の内の1冊目の";
780 if (IS_FLG(FLG_SECOND))
782 before_str[before_n++] = "全4冊の内の2冊目の";
786 if (IS_FLG(FLG_THIRD))
788 before_str[before_n++] = "全4冊の内の3冊目の";
792 if (IS_FLG(FLG_FOURTH))
794 before_str[before_n++] = "全4冊の内の4冊目の";
798 if (IS_FLG(FLG_ITEMS))
799 ; /* Nothing to do */
800 else if (IS_FLG(FLG_WEAPONS))
802 else if (IS_FLG(FLG_FAVORITE_WEAPONS))
804 else if (IS_FLG(FLG_ARMORS))
806 else if (IS_FLG(FLG_MISSILES))
807 body_str = "弾や矢やクロスボウの矢";
808 else if (IS_FLG(FLG_DEVICES))
809 body_str = "巻物や魔法棒や杖やロッド";
810 else if (IS_FLG(FLG_LIGHTS))
811 body_str = "光源用のアイテム";
812 else if (IS_FLG(FLG_JUNKS))
813 body_str = "折れた棒等のガラクタ";
814 else if (IS_FLG(FLG_CORPSES))
816 else if (IS_FLG(FLG_SPELLBOOKS))
818 else if (IS_FLG(FLG_HAFTED))
820 else if (IS_FLG(FLG_SHIELDS))
822 else if (IS_FLG(FLG_BOWS))
823 body_str = "スリングや弓やクロスボウ";
824 else if (IS_FLG(FLG_RINGS))
826 else if (IS_FLG(FLG_AMULETS))
828 else if (IS_FLG(FLG_SUITS))
830 else if (IS_FLG(FLG_CLOAKS))
832 else if (IS_FLG(FLG_HELMS))
833 body_str = "ヘルメットや冠";
834 else if (IS_FLG(FLG_GLOVES))
836 else if (IS_FLG(FLG_BOOTS))
842 else for (i = 0; i < before_n && before_str[i]; i++)
843 strcat(buff, before_str[i]);
845 strcat(buff, body_str);
855 strcat(buff, "で、名前が「");
856 strncat(buff, str, 80);
858 strcat(buff, "」で始まるもの");
860 strcat(buff, "」を含むもの");
865 strncat(buff, format("に「%s」", insc), 80);
867 if (my_strstr(insc, "%%all"))
868 strcat(buff, "(%%allは全能力を表す英字の記号で置換)");
869 else if (my_strstr(insc, "%all"))
870 strcat(buff, "(%allは全能力を表す記号で置換)");
871 else if (my_strstr(insc, "%%"))
872 strcat(buff, "(%%は追加能力を表す英字の記号で置換)");
873 else if (my_strstr(insc, "%"))
874 strcat(buff, "(%は追加能力を表す記号で置換)");
876 strcat(buff, "と刻んで");
881 if (act & DONT_AUTOPICK)
882 strcat(buff, "放置する。");
883 else if (act & DO_AUTODESTROY)
884 strcat(buff, "破壊する。");
885 else if (act & DO_QUERY_AUTOPICK)
886 strcat(buff, "確認の後に拾う。");
890 if (act & DO_DISPLAY)
892 if (act & DONT_AUTOPICK)
893 strcat(buff, "全体マップ('M')で'N'を押したときに表示する。");
894 else if (act & DO_AUTODESTROY)
895 strcat(buff, "全体マップ('M')で'K'を押したときに表示する。");
897 strcat(buff, "全体マップ('M')で'M'を押したときに表示する。");
900 strcat(buff, "全体マップには表示しない。");
904 concptr before_str[20], after_str[20], which_str[20], whose_str[20], body_str;
905 int before_n = 0, after_n = 0, which_n = 0, whose_n = 0;
907 if (IS_FLG(FLG_COLLECTING))
908 which_str[which_n++] = "can be absorbed into an existing inventory list slot";
910 if (IS_FLG(FLG_UNAWARE))
912 before_str[before_n++] = "unidentified";
913 whose_str[whose_n++] = "basic abilities are not known";
916 if (IS_FLG(FLG_UNIDENTIFIED))
917 before_str[before_n++] = "unidentified";
919 if (IS_FLG(FLG_IDENTIFIED))
920 before_str[before_n++] = "identified";
922 if (IS_FLG(FLG_STAR_IDENTIFIED))
923 before_str[before_n++] = "fully identified";
925 if (IS_FLG(FLG_RARE))
927 before_str[before_n++] = "very rare";
928 body_str = "equipments";
929 after_str[after_n++] = "such as Dragon armor, Blades of Chaos, etc.";
932 if (IS_FLG(FLG_COMMON))
934 before_str[before_n++] = "relatively common";
935 body_str = "equipments";
936 after_str[after_n++] = "compared to very rare Dragon armor, Blades of Chaos, etc.";
939 if (IS_FLG(FLG_WORTHLESS))
941 before_str[before_n++] = "worthless";
942 which_str[which_n++] = "can not be sold at stores";
945 if (IS_FLG(FLG_ARTIFACT))
947 before_str[before_n++] = "artifact";
952 before_str[before_n++] = "ego";
955 if (IS_FLG(FLG_GOOD))
957 body_str = "equipment";
958 which_str[which_n++] = "have good quality";
961 if (IS_FLG(FLG_NAMELESS))
963 body_str = "equipment";
964 which_str[which_n++] = "is neither ego-item nor artifact";
967 if (IS_FLG(FLG_AVERAGE))
969 body_str = "equipment";
970 which_str[which_n++] = "have average quality";
973 if (IS_FLG(FLG_BOOSTED))
975 body_str = "weapons";
976 whose_str[whose_n++] = "damage dice is bigger than normal";
979 if (IS_FLG(FLG_MORE_DICE))
981 static char more_than_desc_str[] =
982 "maximum damage from dice is bigger than __";
983 body_str = "weapons";
985 sprintf(more_than_desc_str + sizeof(more_than_desc_str) - 3,
987 whose_str[whose_n++] = more_than_desc_str;
990 if (IS_FLG(FLG_MORE_BONUS))
992 static char more_bonus_desc_str[] =
993 "magical bonus is bigger than (+__)";
995 sprintf(more_bonus_desc_str + sizeof(more_bonus_desc_str) - 4,
996 "%d)", entry->bonus);
997 whose_str[whose_n++] = more_bonus_desc_str;
1000 if (IS_FLG(FLG_WANTED))
1002 body_str = "corpse or skeletons";
1003 which_str[which_n++] = "is wanted at the Hunter's Office";
1006 if (IS_FLG(FLG_HUMAN))
1008 before_str[before_n++] = "humanoid";
1009 body_str = "corpse or skeletons";
1010 which_str[which_n++] = "can be used for Daemon magic";
1013 if (IS_FLG(FLG_UNIQUE))
1015 before_str[before_n++] = "unique monster's";
1016 body_str = "corpse or skeletons";
1019 if (IS_FLG(FLG_UNREADABLE))
1021 body_str = "spellbooks";
1022 after_str[after_n++] = "of different realms from yours";
1025 if (IS_FLG(FLG_REALM1))
1027 body_str = "spellbooks";
1028 after_str[after_n++] = "of your first realm";
1031 if (IS_FLG(FLG_REALM2))
1033 body_str = "spellbooks";
1034 after_str[after_n++] = "of your second realm";
1037 if (IS_FLG(FLG_FIRST))
1039 before_str[before_n++] = "first one of four";
1040 body_str = "spellbooks";
1043 if (IS_FLG(FLG_SECOND))
1045 before_str[before_n++] = "second one of four";
1046 body_str = "spellbooks";
1049 if (IS_FLG(FLG_THIRD))
1051 before_str[before_n++] = "third one of four";
1052 body_str = "spellbooks";
1055 if (IS_FLG(FLG_FOURTH))
1057 before_str[before_n++] = "fourth one of four";
1058 body_str = "spellbooks";
1061 if (IS_FLG(FLG_ITEMS))
1062 ; /* Nothing to do */
1063 else if (IS_FLG(FLG_WEAPONS))
1064 body_str = "weapons";
1065 else if (IS_FLG(FLG_FAVORITE_WEAPONS))
1066 body_str = "favorite weapons";
1067 else if (IS_FLG(FLG_ARMORS))
1068 body_str = "armors";
1069 else if (IS_FLG(FLG_MISSILES))
1070 body_str = "shots, arrows or crossbow bolts";
1071 else if (IS_FLG(FLG_DEVICES))
1072 body_str = "scrolls, wands, staffs or rods";
1073 else if (IS_FLG(FLG_LIGHTS))
1074 body_str = "light sources";
1075 else if (IS_FLG(FLG_JUNKS))
1076 body_str = "junk such as broken sticks";
1077 else if (IS_FLG(FLG_CORPSES))
1078 body_str = "corpses or skeletons";
1079 else if (IS_FLG(FLG_SPELLBOOKS))
1080 body_str = "spellbooks";
1081 else if (IS_FLG(FLG_HAFTED))
1082 body_str = "hafted weapons";
1083 else if (IS_FLG(FLG_SHIELDS))
1084 body_str = "shields";
1085 else if (IS_FLG(FLG_BOWS))
1086 body_str = "slings, bows or crossbows";
1087 else if (IS_FLG(FLG_RINGS))
1089 else if (IS_FLG(FLG_AMULETS))
1090 body_str = "amulets";
1091 else if (IS_FLG(FLG_SUITS))
1092 body_str = "body armors";
1093 else if (IS_FLG(FLG_CLOAKS))
1094 body_str = "cloaks";
1095 else if (IS_FLG(FLG_HELMS))
1096 body_str = "helms or crowns";
1097 else if (IS_FLG(FLG_GLOVES))
1098 body_str = "gloves";
1099 else if (IS_FLG(FLG_BOOTS))
1108 whose_str[whose_n++] = "name begins with \"";
1111 which_str[which_n++] = "have \"";
1115 if (act & DONT_AUTOPICK)
1116 strcpy(buff, "Leave on floor ");
1117 else if (act & DO_AUTODESTROY)
1118 strcpy(buff, "Destroy ");
1119 else if (act & DO_QUERY_AUTOPICK)
1120 strcpy(buff, "Ask to pick up ");
1122 strcpy(buff, "Pickup ");
1126 strncat(buff, format("and inscribe \"%s\"", insc), 80);
1128 if (my_strstr(insc, "%all"))
1129 strcat(buff, ", replacing %all with code string representing all abilities,");
1130 else if (my_strstr(insc, "%"))
1131 strcat(buff, ", replacing % with code string representing extra random abilities,");
1133 strcat(buff, " on ");
1137 strcat(buff, "all ");
1138 else for (i = 0; i < before_n && before_str[i]; i++)
1140 strcat(buff, before_str[i]);
1144 strcat(buff, body_str);
1146 for (i = 0; i < after_n && after_str[i]; i++)
1149 strcat(buff, after_str[i]);
1152 for (i = 0; i < whose_n && whose_str[i]; i++)
1155 strcat(buff, " whose ");
1157 strcat(buff, ", and ");
1159 strcat(buff, whose_str[i]);
1168 if (whose_n && which_n)
1169 strcat(buff, ", and ");
1171 for (i = 0; i < which_n && which_str[i]; i++)
1174 strcat(buff, " which ");
1176 strcat(buff, ", and ");
1178 strcat(buff, which_str[i]);
1183 strncat(buff, str, 80);
1184 strcat(buff, "\" as part of its name");
1188 if (act & DO_DISPLAY)
1190 if (act & DONT_AUTOPICK)
1191 strcat(buff, " Display these items when you press the N key in the full 'M'ap.");
1192 else if (act & DO_AUTODESTROY)
1193 strcat(buff, " Display these items when you press the K key in the full 'M'ap.");
1195 strcat(buff, " Display these items when you press the M key in the full 'M'ap.");
1198 strcat(buff, " Not displayed in the full map.");
1204 * Read whole lines of a file to memory
1206 static concptr *read_text_lines(concptr filename)
1208 concptr *lines_list = NULL;
1214 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
1215 fff = my_fopen(buf, "r");
1216 if (!fff) return NULL;
1218 C_MAKE(lines_list, MAX_LINES, concptr);
1219 while (my_fgets(fff, buf, sizeof(buf)) == 0)
1221 lines_list[lines++] = string_make(buf);
1222 if (lines >= MAX_LINES - 1) break;
1226 lines_list[0] = string_make("");
1234 * Copy the default autopick file to the user directory
1236 static void prepare_default_pickpref(player_type *player_ptr)
1238 const concptr messages[] = {
1239 _("あなたは「自動拾いエディタ」を初めて起動しました。", "You have activated the Auto-Picker Editor for the first time."),
1240 _("自動拾いのユーザー設定ファイルがまだ書かれていないので、", "Since user pref file for autopick is not yet created,"),
1241 _("基本的な自動拾い設定ファイルをlib/pref/picktype.prfからコピーします。", "the default setting is loaded from lib/pref/pickpref.prf ."),
1245 concptr filename = pickpref_filename(player_ptr, PT_DEFAULT);
1246 for (int i = 0; messages[i]; i++)
1248 msg_print(messages[i]);
1253 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
1255 user_fp = my_fopen(buf, "w");
1256 if (!user_fp) return;
1258 fprintf(user_fp, "#***\n");
1259 for (int i = 0; messages[i]; i++)
1261 fprintf(user_fp, "#*** %s\n", messages[i]);
1264 fprintf(user_fp, "#***\n\n\n");
1265 path_build(buf, sizeof(buf), ANGBAND_DIR_PREF, filename);
1267 pref_fp = my_fopen(buf, "r");
1275 while (!my_fgets(pref_fp, buf, sizeof(buf)))
1277 fprintf(user_fp, "%s\n", buf);
1285 * Read an autopick prefence file to memory
1286 * Prepare default if no user file is found
1288 static concptr *read_pickpref_text_lines(player_type *player_ptr, int *filename_mode_p)
1290 /* Try a filename with player name */
1291 *filename_mode_p = PT_WITH_PNAME;
1293 strcpy(buf, pickpref_filename(player_ptr, *filename_mode_p));
1294 concptr *lines_list;
1295 lines_list = read_text_lines(buf);
1299 *filename_mode_p = PT_DEFAULT;
1300 strcpy(buf, pickpref_filename(player_ptr, *filename_mode_p));
1301 lines_list = read_text_lines(buf);
1306 prepare_default_pickpref(player_ptr);
1307 lines_list = read_text_lines(buf);
1312 C_MAKE(lines_list, MAX_LINES, concptr);
1313 lines_list[0] = string_make("");
1321 * Write whole lines of memory to a file.
1323 static bool write_text_lines(concptr filename, concptr *lines_list)
1326 path_build(buf, sizeof(buf), ANGBAND_DIR_USER, filename);
1328 fff = my_fopen(buf, "w");
1329 if (!fff) return FALSE;
1331 for (int lines = 0; lines_list[lines]; lines++)
1333 my_fputs(fff, lines_list[lines], 1024);
1342 * Free memory of lines_list.
1344 static void free_text_lines(concptr *lines_list)
1346 for (int lines = 0; lines_list[lines]; lines++)
1348 string_free(lines_list[lines]);
1351 /* free list of pointers */
1352 C_KILL(lines_list, MAX_LINES, concptr);
1357 * Delete or insert string
1359 static void toggle_keyword(text_body_type *tb, BIT_FLAGS flg)
1366 by1 = MIN(tb->my, tb->cy);
1367 by2 = MAX(tb->my, tb->cy);
1369 else /* if (!tb->mark) */
1374 for (int y = by1; y <= by2; y++)
1376 autopick_type an_entry, *entry = &an_entry;
1377 if (!autopick_new_entry(entry, tb->lines_list[y], !fixed)) continue;
1379 string_free(tb->lines_list[y]);
1382 if (!IS_FLG(flg)) add = TRUE;
1388 if (FLG_NOUN_BEGIN <= flg && flg <= FLG_NOUN_END)
1391 for (i = FLG_NOUN_BEGIN; i <= FLG_NOUN_END; i++)
1394 else if (FLG_UNAWARE <= flg && flg <= FLG_STAR_IDENTIFIED)
1397 for (i = FLG_UNAWARE; i <= FLG_STAR_IDENTIFIED; i++)
1400 else if (FLG_ARTIFACT <= flg && flg <= FLG_AVERAGE)
1403 for (i = FLG_ARTIFACT; i <= FLG_AVERAGE; i++)
1406 else if (FLG_RARE <= flg && flg <= FLG_COMMON)
1409 for (i = FLG_RARE; i <= FLG_COMMON; i++)
1413 if (add) ADD_FLG(flg);
1416 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
1417 tb->dirty_flags |= DIRTY_ALL;
1424 * Change command letter
1426 static void toggle_command_letter(text_body_type *tb, byte flg)
1428 autopick_type an_entry, *entry = &an_entry;
1434 by1 = MIN(tb->my, tb->cy);
1435 by2 = MAX(tb->my, tb->cy);
1437 else /* if (!tb->mark) */
1442 for (y = by1; y <= by2; y++)
1446 if (!autopick_new_entry(entry, tb->lines_list[y], FALSE)) continue;
1448 string_free(tb->lines_list[y]);
1452 if (!(entry->action & flg)) add = TRUE;
1458 if (entry->action & DONT_AUTOPICK) wid--;
1459 else if (entry->action & DO_AUTODESTROY) wid--;
1460 else if (entry->action & DO_QUERY_AUTOPICK) wid--;
1461 if (!(entry->action & DO_DISPLAY)) wid--;
1463 if (flg != DO_DISPLAY)
1465 entry->action &= ~(DO_AUTOPICK | DONT_AUTOPICK | DO_AUTODESTROY | DO_QUERY_AUTOPICK);
1466 if (add) entry->action |= flg;
1467 else entry->action |= DO_AUTOPICK;
1471 entry->action &= ~(DO_DISPLAY);
1472 if (add) entry->action |= flg;
1477 if (entry->action & DONT_AUTOPICK) wid++;
1478 else if (entry->action & DO_AUTODESTROY) wid++;
1479 else if (entry->action & DO_QUERY_AUTOPICK) wid++;
1480 if (!(entry->action & DO_DISPLAY)) wid++;
1482 if (wid > 0) tb->cx++;
1483 if (wid < 0 && tb->cx > 0) tb->cx--;
1486 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
1487 tb->dirty_flags |= DIRTY_ALL;
1494 * Delete or insert string
1496 static void add_keyword(text_body_type *tb, BIT_FLAGS flg)
1501 by1 = MIN(tb->my, tb->cy);
1502 by2 = MAX(tb->my, tb->cy);
1509 for (int y = by1; y <= by2; y++)
1511 autopick_type an_entry, *entry = &an_entry;
1512 if (!autopick_new_entry(entry, tb->lines_list[y], FALSE)) continue;
1516 autopick_free_entry(entry);
1520 string_free(tb->lines_list[y]);
1521 if (FLG_NOUN_BEGIN <= flg && flg <= FLG_NOUN_END)
1524 for (i = FLG_NOUN_BEGIN; i <= FLG_NOUN_END; i++)
1529 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
1530 tb->dirty_flags |= DIRTY_ALL;
1537 * Check if this line is expression or not.
1538 * And update it if it is.
1540 static void check_expression_line(text_body_type *tb, int y)
1542 concptr s = tb->lines_list[y];
1544 if ((s[0] == '?' && s[1] == ':') ||
1545 (tb->states[y] & LSTAT_BYPASS))
1547 tb->dirty_flags |= DIRTY_EXPRESSION;
1553 * Add an empty line at the last of the file
1555 static bool add_empty_line(text_body_type *tb)
1558 for (num_lines = 0; tb->lines_list[num_lines]; num_lines++);
1560 if (num_lines >= MAX_LINES - 2) return FALSE;
1561 if (!tb->lines_list[num_lines - 1][0]) return FALSE;
1563 tb->lines_list[num_lines] = string_make("");
1564 tb->dirty_flags |= DIRTY_EXPRESSION;
1571 * Insert return code and split the line
1573 static bool insert_return_code(text_body_type *tb)
1575 char buf[MAX_LINELEN];
1576 int i, j, num_lines;
1578 for (num_lines = 0; tb->lines_list[num_lines]; num_lines++);
1580 if (num_lines >= MAX_LINES - 2) return FALSE;
1583 for (; tb->cy < num_lines; num_lines--)
1585 tb->lines_list[num_lines + 1] = tb->lines_list[num_lines];
1586 tb->states[num_lines + 1] = tb->states[num_lines];
1589 for (i = j = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
1592 if (iskanji(tb->lines_list[tb->cy][i]))
1593 buf[j++] = tb->lines_list[tb->cy][i++];
1595 buf[j++] = tb->lines_list[tb->cy][i];
1599 tb->lines_list[tb->cy + 1] = string_make(&tb->lines_list[tb->cy][i]);
1600 string_free(tb->lines_list[tb->cy]);
1601 tb->lines_list[tb->cy] = string_make(buf);
1602 tb->dirty_flags |= DIRTY_EXPRESSION;
1609 * Choose an item and get auto-picker entry from it.
1611 static bool entry_from_choosed_object(player_type *player_ptr, autopick_type *entry)
1613 concptr q = _("どのアイテムを登録しますか? ", "Enter which item? ");
1614 concptr s = _("アイテムを持っていない。", "You have nothing to enter.");
1616 o_ptr = choose_object(player_ptr, NULL, q, s, USE_INVEN | USE_FLOOR | USE_EQUIP, 0);
1617 if (!o_ptr) return FALSE;
1619 autopick_entry_from_object(player_ptr, entry, o_ptr);
1625 * Choose an item for search
1627 static bool get_object_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
1629 concptr q = _("どのアイテムを検索しますか? ", "Enter which item? ");
1630 concptr s = _("アイテムを持っていない。", "You have nothing to enter.");
1632 o_ptr = choose_object(player_ptr, NULL, q, s, USE_INVEN | USE_FLOOR | USE_EQUIP, 0);
1633 if (!o_ptr) return FALSE;
1636 string_free(*search_strp);
1637 char buf[MAX_NLEN + 20];
1638 object_desc(player_ptr, buf, *o_handle, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
1639 *search_strp = string_make(format("<%s>", buf));
1645 * Prepare for search by destroyed object
1647 static bool get_destroyed_object_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
1649 if (!autopick_last_destroyed_object.k_idx) return FALSE;
1651 *o_handle = &autopick_last_destroyed_object;
1652 string_free(*search_strp);
1653 char buf[MAX_NLEN + 20];
1654 object_desc(player_ptr, buf, *o_handle, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
1655 *search_strp = string_make(format("<%s>", buf));
1661 * Choose an item or string for search
1663 static byte get_string_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
1667 * TERM_YELLOW : Overwrite mode
1668 * TERM_WHITE : Insert mode
1670 byte color = TERM_YELLOW;
1671 char buf[MAX_NLEN + 20];
1673 char prompt[] = _("検索(^I:持ち物 ^L:破壊された物): ", "Search key(^I:inven ^L:destroyed): ");
1674 int col = sizeof(prompt) - 1;
1675 if (*search_strp) strcpy(buf, *search_strp);
1678 if (*o_handle) color = TERM_L_GREEN;
1687 Term_erase(col, 0, 255);
1688 Term_putstr(col, 0, -1, color, buf);
1689 Term_gotoxy(col + pos, 0);
1691 skey = inkey_special(TRUE);
1699 if (pos == 0) break;
1703 int next_pos = i + 1;
1706 if (iskanji(buf[i])) next_pos++;
1708 if (next_pos >= pos) break;
1720 if ('\0' == buf[pos]) break;
1723 if (iskanji(buf[pos])) pos += 2;
1740 if (*o_handle) return (back ? -1 : 1);
1741 string_free(*search_strp);
1742 *search_strp = string_make(buf);
1744 return (back ? -1 : 1);
1747 return get_object_for_search(player_ptr, o_handle, search_strp);
1750 if (get_destroyed_object_for_search(player_ptr, o_handle, search_strp))
1758 if (pos == 0) break;
1762 int next_pos = i + 1;
1764 if (iskanji(buf[i])) next_pos++;
1766 if (next_pos >= pos) break;
1780 if (buf[pos] == '\0') break;
1784 if (iskanji(buf[pos])) src++;
1787 while ('\0' != (buf[dst++] = buf[src++]));
1796 if (skey & SKEY_MASK) break;
1799 if (color != TERM_WHITE)
1801 if (color == TERM_L_GREEN)
1804 string_free(*search_strp);
1805 *search_strp = NULL;
1812 strcpy(tmp, buf + pos);
1834 if (pos < len && (isprint(c) || iskana(c)))
1836 if (pos < len && isprint(c))
1848 my_strcat(buf, tmp, len + 1);
1854 if (*o_handle == NULL || color == TERM_L_GREEN) continue;
1858 string_free(*search_strp);
1859 *search_strp = NULL;
1865 * Search next line matches for o_ptr
1867 static void search_for_object(player_type *player_ptr, text_body_type *tb, object_type *o_ptr, bool forward)
1869 autopick_type an_entry, *entry = &an_entry;
1870 GAME_TEXT o_name[MAX_NLEN];
1871 int bypassed_cy = -1;
1873 object_desc(player_ptr, o_name, o_ptr, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
1874 str_tolower(o_name);
1881 if (!tb->lines_list[++i]) break;
1888 if (!autopick_new_entry(entry, tb->lines_list[i], FALSE)) continue;
1890 match = is_autopick_match(player_ptr, o_ptr, entry, o_name);
1891 autopick_free_entry(entry);
1892 if (!match) continue;
1894 if (tb->states[i] & LSTAT_BYPASS)
1896 if (bypassed_cy == -1) bypassed_cy = i;
1902 if (bypassed_cy != -1)
1904 tb->dirty_flags |= DIRTY_SKIP_INACTIVE;
1910 if (bypassed_cy == -1)
1912 tb->dirty_flags |= DIRTY_NOT_FOUND;
1917 tb->cy = bypassed_cy;
1918 tb->dirty_flags |= DIRTY_INACTIVE;
1923 * Search next line matches to the string
1925 static void search_for_string(text_body_type *tb, concptr search_str, bool forward)
1927 int bypassed_cy = -1;
1928 int bypassed_cx = 0;
1936 if (!tb->lines_list[++i]) break;
1943 pos = my_strstr(tb->lines_list[i], search_str);
1946 if ((tb->states[i] & LSTAT_BYPASS) &&
1947 !(tb->states[i] & LSTAT_EXPRESSION))
1949 if (bypassed_cy == -1)
1952 bypassed_cx = (int)(pos - tb->lines_list[i]);
1958 tb->cx = (int)(pos - tb->lines_list[i]);
1961 if (bypassed_cy != -1)
1963 tb->dirty_flags |= DIRTY_SKIP_INACTIVE;
1969 if (bypassed_cy == -1)
1971 tb->dirty_flags |= DIRTY_NOT_FOUND;
1975 tb->cx = bypassed_cx;
1976 tb->cy = bypassed_cy;
1977 tb->dirty_flags |= DIRTY_INACTIVE;
1982 * Find a command by 'key'.
1984 static int get_com_id(char key)
1986 for (int i = 0; menu_data[i].name; i++)
1988 if (menu_data[i].key == key)
1990 return menu_data[i].com_id;
1999 * Display the menu, and get a command
2001 static int do_command_menu(int level, int start)
2004 int col0 = 5 + level * 7;
2005 int row0 = 1 + level * 3;
2006 int menu_id_list[26];
2008 char linestr[MAX_LINELEN];
2011 for (int i = start; menu_data[i].level >= level; i++)
2015 /* Ignore lower level sub menus */
2016 if (menu_data[i].level > level) continue;
2018 len = strlen(menu_data[i].name);
2019 if (len > max_len) max_len = len;
2021 menu_id_list[menu_key] = i;
2025 while (menu_key < 26)
2027 menu_id_list[menu_key] = -1;
2031 int max_menu_wid = max_len + 3 + 3;
2033 /* Prepare box line */
2035 strcat(linestr, "+");
2036 for (int i = 0; i < max_menu_wid + 2; i++)
2038 strcat(linestr, "-");
2041 strcat(linestr, "+");
2051 int row1 = row0 + 1;
2052 Term_putstr(col0, row0, -1, TERM_WHITE, linestr);
2055 for (int i = start; menu_data[i].level >= level; i++)
2057 char com_key_str[3];
2059 if (menu_data[i].level > level) continue;
2061 if (menu_data[i].com_id == -1)
2063 strcpy(com_key_str, _("▼", ">"));
2065 else if (menu_data[i].key != -1)
2067 com_key_str[0] = '^';
2068 com_key_str[1] = menu_data[i].key + '@';
2069 com_key_str[2] = '\0';
2073 com_key_str[0] = '\0';
2076 str = format("| %c) %-*s %2s | ", menu_key + 'a', max_len, menu_data[i].name, com_key_str);
2078 Term_putstr(col0, row1++, -1, TERM_WHITE, str);
2083 Term_putstr(col0, row1, -1, TERM_WHITE, linestr);
2087 prt(format(_("(a-%c) コマンド:", "(a-%c) Command:"), menu_key + 'a' - 1), 0, 0);
2090 if (key == ESCAPE) return 0;
2092 bool is_alphabet = key >= 'a' && key <= 'z';
2095 com_id = get_com_id(key);
2104 menu_id = menu_id_list[key - 'a'];
2106 if (menu_id < 0) continue;
2108 com_id = menu_data[menu_id].com_id;
2112 com_id = do_command_menu(level + 1, menu_id + 1);
2114 if (com_id) return com_id;
2125 static chain_str_type *new_chain_str(concptr str)
2127 chain_str_type *chain;
2128 size_t len = strlen(str);
2129 chain = (chain_str_type *)ralloc(sizeof(chain_str_type) + len * sizeof(char));
2130 strcpy(chain->s, str);
2136 static void kill_yank_chain(text_body_type *tb)
2138 chain_str_type *chain = tb->yank;
2140 tb->yank_eol = TRUE;
2144 chain_str_type *next = chain->next;
2145 size_t len = strlen(chain->s);
2147 rnfree(chain, sizeof(chain_str_type) + len * sizeof(char));
2154 static void add_str_to_yank(text_body_type *tb, concptr str)
2156 tb->yank_eol = FALSE;
2157 if (NULL == tb->yank)
2159 tb->yank = new_chain_str(str);
2163 chain_str_type *chain;
2170 chain->next = new_chain_str(str);
2175 chain = chain->next;
2181 * Do work for the copy editor-command
2183 static void copy_text_to_yank(text_body_type *tb)
2185 int len = strlen(tb->lines_list[tb->cy]);
2186 if (tb->cx > len) tb->cx = len;
2195 kill_yank_chain(tb);
2196 if (tb->my != tb->cy)
2198 int by1 = MIN(tb->my, tb->cy);
2199 int by2 = MAX(tb->my, tb->cy);
2201 for (int y = by1; y <= by2; y++)
2203 add_str_to_yank(tb, tb->lines_list[y]);
2206 add_str_to_yank(tb, "");
2208 tb->dirty_flags |= DIRTY_ALL;
2212 char buf[MAX_LINELEN];
2213 int bx1 = MIN(tb->mx, tb->cx);
2214 int bx2 = MAX(tb->mx, tb->cx);
2215 if (bx2 > len) bx2 = len;
2217 if (bx1 == 0 && bx2 == len)
2219 add_str_to_yank(tb, tb->lines_list[tb->cy]);
2220 add_str_to_yank(tb, "");
2224 int end = bx2 - bx1;
2225 for (int i = 0; i < bx2 - bx1; i++)
2227 buf[i] = tb->lines_list[tb->cy][bx1 + i];
2231 add_str_to_yank(tb, buf);
2235 tb->dirty_flags |= DIRTY_ALL;
2242 static void draw_text_editor(player_type *player_ptr, text_body_type *tb)
2245 int by1 = 0, by2 = 0;
2247 Term_get_size(&tb->wid, &tb->hgt);
2250 * Top line (-1), description line (-3), separator (-1)
2253 tb->hgt -= 2 + DESCRIPT_HGT;
2256 /* Don't let cursor at second byte of kanji */
2257 for (i = 0; tb->lines_list[tb->cy][i]; i++)
2258 if (iskanji(tb->lines_list[tb->cy][i]))
2264 * Move to a correct position in the
2267 if (i & 1) tb->cx--;
2273 if (tb->cy < tb->upper || tb->upper + tb->hgt <= tb->cy)
2274 tb->upper = tb->cy - (tb->hgt) / 2;
2277 if ((tb->cx < tb->left + 10 && tb->left > 0) || tb->left + tb->wid - 5 <= tb->cx)
2278 tb->left = tb->cx - (tb->wid) * 2 / 3;
2282 if (tb->old_wid != tb->wid || tb->old_hgt != tb->hgt)
2283 tb->dirty_flags |= DIRTY_SCREEN;
2284 else if (tb->old_upper != tb->upper || tb->old_left != tb->left)
2285 tb->dirty_flags |= DIRTY_ALL;
2287 if (tb->dirty_flags & DIRTY_SCREEN)
2289 tb->dirty_flags |= (DIRTY_ALL | DIRTY_MODE);
2293 if (tb->dirty_flags & DIRTY_MODE)
2295 char buf[MAX_LINELEN];
2296 int sepa_length = tb->wid;
2297 for (i = 0; i < sepa_length; i++)
2300 Term_putstr(0, tb->hgt + 1, sepa_length, TERM_WHITE, buf);
2303 if (tb->dirty_flags & DIRTY_EXPRESSION)
2306 for (int y = 0; tb->lines_list[y]; y++)
2310 concptr s = tb->lines_list[y];
2314 tb->states[y] = state;
2316 if (*s++ != '?') continue;
2317 if (*s++ != ':') continue;
2319 if (streq(s, "$AUTOREGISTER"))
2320 state |= LSTAT_AUTOREGISTER;
2323 ss = (char *)string_make(s);
2326 v = process_pref_file_expr(player_ptr, &ss, &f);
2328 if (streq(v, "0")) state |= LSTAT_BYPASS;
2329 else state &= ~LSTAT_BYPASS;
2331 C_KILL(s_keep, s_len + 1, char);
2333 tb->states[y] = state | LSTAT_EXPRESSION;
2336 tb->dirty_flags |= DIRTY_ALL;
2341 tb->dirty_flags |= DIRTY_ALL;
2343 by1 = MIN(tb->my, tb->cy);
2344 by2 = MAX(tb->my, tb->cy);
2347 for (i = 0; i < tb->hgt; i++)
2353 int y = tb->upper + i;
2355 if (!(tb->dirty_flags & DIRTY_ALL) && (tb->dirty_line != y))
2358 msg = tb->lines_list[y];
2361 for (j = 0; *msg; msg++, j++)
2363 if (j == tb->left) break;
2378 Term_erase(0, i + 1, tb->wid);
2379 if (tb->states[y] & LSTAT_AUTOREGISTER)
2385 if (tb->states[y] & LSTAT_BYPASS) color = TERM_SLATE;
2386 else color = TERM_WHITE;
2389 if (!tb->mark || (y < by1 || by2 < y))
2391 Term_putstr(leftcol, i + 1, tb->wid - 1, color, msg);
2393 else if (by1 != by2)
2395 Term_putstr(leftcol, i + 1, tb->wid - 1, TERM_YELLOW, msg);
2399 int x0 = leftcol + tb->left;
2400 int len = strlen(tb->lines_list[tb->cy]);
2401 int bx1 = MIN(tb->mx, tb->cx);
2402 int bx2 = MAX(tb->mx, tb->cx);
2404 if (bx2 > len) bx2 = len;
2406 Term_gotoxy(leftcol, i + 1);
2407 if (x0 < bx1) Term_addstr(bx1 - x0, color, msg);
2408 if (x0 < bx2) Term_addstr(bx2 - bx1, TERM_YELLOW, msg + (bx1 - x0));
2409 Term_addstr(-1, color, msg + (bx2 - x0));
2413 for (; i < tb->hgt; i++)
2415 Term_erase(0, i + 1, tb->wid);
2418 bool is_dirty_diary = (tb->dirty_flags & (DIRTY_ALL | DIRTY_NOT_FOUND | DIRTY_NO_SEARCH)) != 0;
2419 bool is_updated = tb->old_cy != tb->cy || is_dirty_diary || tb->dirty_line == tb->cy;
2420 if (is_updated) return;
2422 autopick_type an_entry, *entry = &an_entry;
2423 concptr str1 = NULL, str2 = NULL;
2424 for (i = 0; i < DESCRIPT_HGT; i++)
2426 Term_erase(0, tb->hgt + 2 + i, tb->wid);
2429 if (tb->dirty_flags & DIRTY_NOT_FOUND)
2431 str1 = format(_("パターンが見つかりません: %s", "Pattern not found: %s"), tb->search_str);
2433 else if (tb->dirty_flags & DIRTY_SKIP_INACTIVE)
2435 str1 = format(_("無効状態の行をスキップしました。(%sを検索中)",
2436 "Some inactive lines are skipped. (Searching %s)"), tb->search_str);
2438 else if (tb->dirty_flags & DIRTY_INACTIVE)
2440 str1 = format(_("無効状態の行だけが見付かりました。(%sを検索中)",
2441 "Found only an inactive line. (Searching %s)"), tb->search_str);
2443 else if (tb->dirty_flags & DIRTY_NO_SEARCH)
2445 str1 = _("検索するパターンがありません(^S で検索)。", "No pattern to search. (Press ^S to search.)");
2447 else if (tb->lines_list[tb->cy][0] == '#')
2449 str1 = _("この行はコメントです。", "This line is a comment.");
2451 else if (tb->lines_list[tb->cy][0] && tb->lines_list[tb->cy][1] == ':')
2453 switch (tb->lines_list[tb->cy][0])
2456 str1 = _("この行は条件分岐式です。", "This line is a Conditional Expression.");
2459 str1 = _("この行はマクロの実行内容を定義します。", "This line defines a Macro action.");
2462 str1 = _("この行はマクロのトリガー・キーを定義します。", "This line defines a Macro trigger key.");
2465 str1 = _("この行はキー配置を定義します。", "This line defines a Keymap.");
2469 switch (tb->lines_list[tb->cy][0])
2472 if (tb->states[tb->cy] & LSTAT_BYPASS)
2474 str2 = _("現在の式の値は「偽(=0)」です。", "The expression is 'False'(=0) currently.");
2478 str2 = _("現在の式の値は「真(=1)」です。", "The expression is 'True'(=1) currently.");
2483 if (tb->states[tb->cy] & LSTAT_AUTOREGISTER)
2485 str2 = _("この行は後で削除されます。", "This line will be delete later.");
2488 else if (tb->states[tb->cy] & LSTAT_BYPASS)
2490 str2 = _("この行は現在は無効な状態です。", "This line is bypassed currently.");
2495 else if (autopick_new_entry(entry, tb->lines_list[tb->cy], FALSE))
2497 char buf[MAX_LINELEN];
2498 char temp[MAX_LINELEN];
2501 describe_autopick(buf, entry);
2503 if (tb->states[tb->cy] & LSTAT_AUTOREGISTER)
2505 strcat(buf, _("この行は後で削除されます。", " This line will be delete later."));
2508 if (tb->states[tb->cy] & LSTAT_BYPASS)
2510 strcat(buf, _("この行は現在は無効な状態です。", " This line is bypassed currently."));
2513 roff_to_buf(buf, 81, temp, sizeof(temp));
2515 for (i = 0; i < 3; i++)
2521 prt(t, tb->hgt + 1 + 1 + i, 0);
2525 autopick_free_entry(entry);
2528 if (str1) prt(str1, tb->hgt + 1 + 1, 0);
2529 if (str2) prt(str2, tb->hgt + 1 + 2, 0);
2534 * Kill segment of a line
2536 static void kill_line_segment(text_body_type *tb, int y, int x0, int x1, bool whole)
2538 concptr s = tb->lines_list[y];
2539 if (whole && x0 == 0 && s[x1] == '\0' && tb->lines_list[y + 1])
2541 string_free(tb->lines_list[y]);
2544 for (i = y; tb->lines_list[i + 1]; i++)
2545 tb->lines_list[i] = tb->lines_list[i + 1];
2546 tb->lines_list[i] = NULL;
2548 tb->dirty_flags |= DIRTY_EXPRESSION;
2553 if (x0 == x1) return;
2555 char buf[MAX_LINELEN];
2557 for (int x = 0; x < x0; x++)
2560 for (int x = x1; s[x]; x++)
2564 string_free(tb->lines_list[y]);
2565 tb->lines_list[y] = string_make(buf);
2566 check_expression_line(tb, y);
2572 * Get a trigger key and insert ASCII string for the trigger
2574 static bool insert_macro_line(text_body_type *tb)
2593 ascii_to_text(tmp, buf);
2594 if (!tmp[0]) return FALSE;
2597 insert_return_code(tb);
2598 string_free(tb->lines_list[tb->cy]);
2599 tb->lines_list[tb->cy] = string_make(format("P:%s", tmp));
2601 i = macro_find_exact(buf);
2608 ascii_to_text(tmp, macro__act[i]);
2611 insert_return_code(tb);
2612 string_free(tb->lines_list[tb->cy]);
2613 tb->lines_list[tb->cy] = string_make(format("A:%s", tmp));
2620 * Get a command key and insert ASCII string for the key
2622 static bool insert_keymap_line(text_body_type *tb)
2625 if (rogue_like_commands)
2627 mode = KEYMAP_MODE_ROGUE;
2631 mode = KEYMAP_MODE_ORIG;
2641 ascii_to_text(tmp, buf);
2642 if (!tmp[0]) return FALSE;
2645 insert_return_code(tb);
2646 string_free(tb->lines_list[tb->cy]);
2647 tb->lines_list[tb->cy] = string_make(format("C:%d:%s", mode, tmp));
2649 concptr act = keymap_act[mode][(byte)(buf[0])];
2652 ascii_to_text(tmp, act);
2655 insert_return_code(tb);
2656 string_free(tb->lines_list[tb->cy]);
2657 tb->lines_list[tb->cy] = string_make(format("A:%s", tmp));
2664 * Execute a single editor command
2666 static bool do_editor_command(player_type *player_ptr, text_body_type *tb, int com_id)
2673 if (!get_check(_("全ての変更を破棄してから終了します。よろしいですか? ",
2674 "Discard all changes and quit. Are you sure? "))) break;
2677 return QUIT_WITHOUT_SAVE;
2680 return QUIT_AND_SAVE;
2683 if (!get_check(_("全ての変更を破棄して元の状態に戻します。よろしいですか? ",
2684 "Discard all changes and revert to original file. Are you sure? "))) break;
2686 free_text_lines(tb->lines_list);
2687 tb->lines_list = read_pickpref_text_lines(player_ptr, &tb->filename_mode);
2688 tb->dirty_flags |= DIRTY_ALL | DIRTY_MODE | DIRTY_EXPRESSION;
2689 tb->cx = tb->cy = 0;
2692 tb->changed = FALSE;
2696 (void)show_file(player_ptr, TRUE, _("jeditor.txt", "editor.txt"), NULL, 0, 0);
2697 tb->dirty_flags |= DIRTY_SCREEN;
2705 tb->dirty_flags |= DIRTY_ALL;
2708 insert_return_code(tb);
2712 tb->dirty_flags |= DIRTY_ALL;
2724 len = strlen(tb->lines_list[tb->cy]);
2725 if (len < tb->cx) tb->cx = len;
2727 for (i = 0; tb->lines_list[tb->cy][i]; i++)
2729 if (iskanji(tb->lines_list[tb->cy][i]))
2741 else if (tb->cy > 0)
2744 tb->cx = strlen(tb->lines_list[tb->cy]);
2750 if (!tb->lines_list[tb->cy + 1])
2752 if (!add_empty_line(tb)) break;
2759 if (tb->cy > 0) tb->cy--;
2765 if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
2768 int len = strlen(tb->lines_list[tb->cy]);
2772 if (!tb->lines_list[tb->cy + 1])
2774 if (!add_empty_line(tb)) break;
2788 tb->cx = strlen(tb->lines_list[tb->cy]);
2792 while (0 < tb->cy && tb->upper <= tb->cy)
2794 while (0 < tb->upper && tb->cy + 1 < tb->upper + tb->hgt)
2799 while (tb->cy < tb->upper + tb->hgt)
2801 if (!tb->lines_list[tb->cy + 1])
2803 if (!add_empty_line(tb)) break;
2819 if (!tb->lines_list[tb->cy + 1])
2821 if (!add_empty_line(tb)) break;
2832 copy_text_to_yank(tb);
2833 if (tb->my == tb->cy)
2835 int bx1 = MIN(tb->mx, tb->cx);
2836 int bx2 = MAX(tb->mx, tb->cx);
2837 int len = strlen(tb->lines_list[tb->cy]);
2838 if (bx2 > len) bx2 = len;
2840 kill_line_segment(tb, tb->cy, bx1, bx2, TRUE);
2845 int by1 = MIN(tb->my, tb->cy);
2846 int by2 = MAX(tb->my, tb->cy);
2848 for (int y = by2; y >= by1; y--)
2850 int len = strlen(tb->lines_list[y]);
2852 kill_line_segment(tb, y, 0, len, TRUE);
2860 tb->dirty_flags |= DIRTY_ALL;
2865 copy_text_to_yank(tb);
2868 * Move cursor position to the end of the selection
2870 * Pressing ^C ^V correctly duplicates the selection.
2872 if (tb->my != tb->cy)
2874 tb->cy = MAX(tb->cy, tb->my);
2875 if (!tb->lines_list[tb->cy + 1])
2877 if (!add_empty_line(tb)) break;
2884 tb->cx = MAX(tb->cx, tb->mx);
2885 if (!tb->lines_list[tb->cy][tb->cx])
2887 if (!tb->lines_list[tb->cy + 1])
2889 if (!add_empty_line(tb)) break;
2900 chain_str_type *chain = tb->yank;
2901 int len = strlen(tb->lines_list[tb->cy]);
2903 if (tb->cx > len) tb->cx = len;
2908 tb->dirty_flags |= DIRTY_ALL;
2913 concptr yank_str = chain->s;
2914 char buf[MAX_LINELEN];
2916 char rest[MAX_LINELEN], *rest_ptr = rest;
2917 for (i = 0; i < tb->cx; i++)
2918 buf[i] = tb->lines_list[tb->cy][i];
2920 strcpy(rest, &(tb->lines_list[tb->cy][i]));
2921 while (*yank_str && i < MAX_LINELEN - 1)
2923 buf[i++] = *yank_str++;
2927 chain = chain->next;
2928 if (chain || tb->yank_eol)
2930 insert_return_code(tb);
2931 string_free(tb->lines_list[tb->cy]);
2932 tb->lines_list[tb->cy] = string_make(buf);
2939 tb->cx = strlen(buf);
2940 while (*rest_ptr && i < MAX_LINELEN - 1)
2942 buf[i++] = *rest_ptr++;
2946 string_free(tb->lines_list[tb->cy]);
2947 tb->lines_list[tb->cy] = string_make(buf);
2951 tb->dirty_flags |= DIRTY_ALL;
2952 tb->dirty_flags |= DIRTY_EXPRESSION;
2961 tb->dirty_flags |= DIRTY_ALL;
2965 tb->mark = MARK_MARK;
2966 if (com_id == tb->old_com_id)
2974 tb->dirty_flags |= DIRTY_ALL;
2978 int len = strlen(tb->lines_list[tb->cy]);
2982 if (tb->cx > len) tb->mx = len;
2987 int len = strlen(tb->lines_list[tb->cy]);
2988 if (tb->cx > len) tb->cx = len;
2993 tb->dirty_flags |= DIRTY_ALL;
2996 if (tb->old_com_id != com_id)
2998 kill_yank_chain(tb);
3004 add_str_to_yank(tb, &(tb->lines_list[tb->cy][tb->cx]));
3005 kill_line_segment(tb, tb->cy, tb->cx, len, FALSE);
3006 tb->dirty_line = tb->cy;
3010 if (tb->yank_eol) add_str_to_yank(tb, "");
3012 tb->yank_eol = TRUE;
3013 do_editor_command(player_ptr, tb, EC_DELETE_CHAR);
3016 case EC_DELETE_CHAR:
3021 tb->dirty_flags |= DIRTY_ALL;
3025 if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
3028 int len = strlen(tb->lines_list[tb->cy]);
3031 do_editor_command(player_ptr, tb, EC_BACKSPACE);
3035 if (tb->lines_list[tb->cy + 1])
3046 do_editor_command(player_ptr, tb, EC_BACKSPACE);
3052 char buf[MAX_LINELEN];
3056 tb->dirty_flags |= DIRTY_ALL;
3059 len = strlen(tb->lines_list[tb->cy]);
3060 if (len < tb->cx) tb->cx = len;
3064 if (tb->cy == 0) break;
3065 tb->cx = strlen(tb->lines_list[tb->cy - 1]);
3066 strcpy(buf, tb->lines_list[tb->cy - 1]);
3067 strcat(buf, tb->lines_list[tb->cy]);
3068 string_free(tb->lines_list[tb->cy - 1]);
3069 string_free(tb->lines_list[tb->cy]);
3070 tb->lines_list[tb->cy - 1] = string_make(buf);
3072 for (i = tb->cy; tb->lines_list[i + 1]; i++)
3073 tb->lines_list[i] = tb->lines_list[i + 1];
3075 tb->lines_list[i] = NULL;
3077 tb->dirty_flags |= DIRTY_ALL;
3078 tb->dirty_flags |= DIRTY_EXPRESSION;
3083 for (i = j = k = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
3087 if (iskanji(tb->lines_list[tb->cy][i]))
3088 buf[j++] = tb->lines_list[tb->cy][i++];
3090 buf[j++] = tb->lines_list[tb->cy][i];
3099 for (; tb->lines_list[tb->cy][i]; i++)
3101 buf[j++] = tb->lines_list[tb->cy][i];
3105 string_free(tb->lines_list[tb->cy]);
3106 tb->lines_list[tb->cy] = string_make(buf);
3107 tb->dirty_line = tb->cy;
3108 check_expression_line(tb, tb->cy);
3115 tb->dirty_flags |= DIRTY_SCREEN;
3116 search_dir = get_string_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str);
3118 if (!search_dir) break;
3120 if (search_dir == 1) do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
3121 else do_editor_command(player_ptr, tb, EC_SEARCH_BACK);
3124 case EC_SEARCH_FORW:
3125 if (tb->search_o_ptr)
3127 search_for_object(player_ptr, tb, tb->search_o_ptr, TRUE);
3131 if (tb->search_str && tb->search_str[0])
3133 search_for_string(tb, tb->search_str, TRUE);
3137 tb->dirty_flags |= DIRTY_NO_SEARCH;
3140 case EC_SEARCH_BACK:
3141 if (tb->search_o_ptr)
3143 search_for_object(player_ptr, tb, tb->search_o_ptr, FALSE);
3147 if (tb->search_str && tb->search_str[0])
3149 search_for_string(tb, tb->search_str, FALSE);
3153 tb->dirty_flags |= DIRTY_NO_SEARCH;
3157 tb->dirty_flags |= DIRTY_SCREEN;
3159 if (!get_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str)) break;
3161 do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
3164 case EC_SEARCH_DESTROYED:
3165 if (!get_destroyed_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str))
3167 tb->dirty_flags |= DIRTY_NO_SEARCH;
3171 do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
3174 case EC_INSERT_OBJECT:
3176 autopick_type an_entry, *entry = &an_entry;
3177 if (!entry_from_choosed_object(player_ptr, entry))
3179 tb->dirty_flags |= DIRTY_SCREEN;
3184 insert_return_code(tb);
3185 string_free(tb->lines_list[tb->cy]);
3186 tb->lines_list[tb->cy] = autopick_line_from_entry_kill(entry);
3187 tb->dirty_flags |= DIRTY_SCREEN;
3190 case EC_INSERT_DESTROYED:
3191 if (!tb->last_destroyed) break;
3194 insert_return_code(tb);
3195 string_free(tb->lines_list[tb->cy]);
3196 tb->lines_list[tb->cy] = string_make(tb->last_destroyed);
3197 tb->dirty_flags |= DIRTY_ALL;
3201 case EC_INSERT_BLOCK:
3203 char expression[80];
3204 sprintf(expression, "?:[AND [EQU $RACE %s] [EQU $CLASS %s] [GEQ $LEVEL %02d]]",
3206 rp_ptr->E_title, cp_ptr->E_title,
3208 rp_ptr->title, cp_ptr->title,
3212 insert_return_code(tb);
3213 string_free(tb->lines_list[tb->cy]);
3214 tb->lines_list[tb->cy] = string_make(expression);
3216 insert_return_code(tb);
3217 string_free(tb->lines_list[tb->cy]);
3218 tb->lines_list[tb->cy] = string_make("?:1");
3219 tb->dirty_flags |= DIRTY_ALL;
3224 case EC_INSERT_MACRO:
3225 draw_text_editor(player_ptr, tb);
3226 Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
3227 Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW, _("P:<トリガーキー>: ", "P:<Trigger key>: "));
3228 if (!insert_macro_line(tb)) break;
3231 tb->dirty_flags |= DIRTY_ALL;
3235 case EC_INSERT_KEYMAP:
3236 draw_text_editor(player_ptr, tb);
3237 Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
3238 Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW,
3239 format(_("C:%d:<コマンドキー>: ", "C:%d:<Keypress>: "), (rogue_like_commands ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG)));
3241 if (!insert_keymap_line(tb)) break;
3244 tb->dirty_flags |= DIRTY_ALL;
3248 case EC_CL_AUTOPICK: toggle_command_letter(tb, DO_AUTOPICK); break;
3249 case EC_CL_DESTROY: toggle_command_letter(tb, DO_AUTODESTROY); break;
3250 case EC_CL_LEAVE: toggle_command_letter(tb, DONT_AUTOPICK); break;
3251 case EC_CL_QUERY: toggle_command_letter(tb, DO_QUERY_AUTOPICK); break;
3252 case EC_CL_NO_DISP: toggle_command_letter(tb, DO_DISPLAY); break;
3254 case EC_IK_UNAWARE: toggle_keyword(tb, FLG_UNAWARE); break;
3255 case EC_IK_UNIDENTIFIED: toggle_keyword(tb, FLG_UNIDENTIFIED); break;
3256 case EC_IK_IDENTIFIED: toggle_keyword(tb, FLG_IDENTIFIED); break;
3257 case EC_IK_STAR_IDENTIFIED: toggle_keyword(tb, FLG_STAR_IDENTIFIED); break;
3258 case EC_KK_WEAPONS: toggle_keyword(tb, FLG_WEAPONS); break;
3259 case EC_KK_FAVORITE_WEAPONS: toggle_keyword(tb, FLG_FAVORITE_WEAPONS); break;
3260 case EC_KK_ARMORS: toggle_keyword(tb, FLG_ARMORS); break;
3261 case EC_KK_MISSILES: toggle_keyword(tb, FLG_MISSILES); break;
3262 case EC_KK_DEVICES: toggle_keyword(tb, FLG_DEVICES); break;
3263 case EC_KK_LIGHTS: toggle_keyword(tb, FLG_LIGHTS); break;
3264 case EC_KK_JUNKS: toggle_keyword(tb, FLG_JUNKS); break;
3265 case EC_KK_CORPSES: toggle_keyword(tb, FLG_CORPSES); break;
3266 case EC_KK_SPELLBOOKS: toggle_keyword(tb, FLG_SPELLBOOKS); break;
3267 case EC_KK_SHIELDS: toggle_keyword(tb, FLG_SHIELDS); break;
3268 case EC_KK_BOWS: toggle_keyword(tb, FLG_BOWS); break;
3269 case EC_KK_RINGS: toggle_keyword(tb, FLG_RINGS); break;
3270 case EC_KK_AMULETS: toggle_keyword(tb, FLG_AMULETS); break;
3271 case EC_KK_SUITS: toggle_keyword(tb, FLG_SUITS); break;
3272 case EC_KK_CLOAKS: toggle_keyword(tb, FLG_CLOAKS); break;
3273 case EC_KK_HELMS: toggle_keyword(tb, FLG_HELMS); break;
3274 case EC_KK_GLOVES: toggle_keyword(tb, FLG_GLOVES); break;
3275 case EC_KK_BOOTS: toggle_keyword(tb, FLG_BOOTS); break;
3276 case EC_OK_COLLECTING: toggle_keyword(tb, FLG_COLLECTING); break;
3277 case EC_OK_BOOSTED: toggle_keyword(tb, FLG_BOOSTED); break;
3278 case EC_OK_MORE_DICE: toggle_keyword(tb, FLG_MORE_DICE); break;
3279 case EC_OK_MORE_BONUS: toggle_keyword(tb, FLG_MORE_BONUS); break;
3280 case EC_OK_WORTHLESS: toggle_keyword(tb, FLG_WORTHLESS); break;
3281 case EC_OK_ARTIFACT: toggle_keyword(tb, FLG_ARTIFACT); break;
3282 case EC_OK_EGO: toggle_keyword(tb, FLG_EGO); break;
3283 case EC_OK_GOOD: toggle_keyword(tb, FLG_GOOD); break;
3284 case EC_OK_NAMELESS: toggle_keyword(tb, FLG_NAMELESS); break;
3285 case EC_OK_AVERAGE: toggle_keyword(tb, FLG_AVERAGE); break;
3286 case EC_OK_RARE: toggle_keyword(tb, FLG_RARE); break;
3287 case EC_OK_COMMON: toggle_keyword(tb, FLG_COMMON); break;
3288 case EC_OK_WANTED: toggle_keyword(tb, FLG_WANTED); break;
3289 case EC_OK_UNIQUE: toggle_keyword(tb, FLG_UNIQUE); break;
3290 case EC_OK_HUMAN: toggle_keyword(tb, FLG_HUMAN); break;
3291 case EC_OK_UNREADABLE:
3292 toggle_keyword(tb, FLG_UNREADABLE);
3293 add_keyword(tb, FLG_SPELLBOOKS);
3296 toggle_keyword(tb, FLG_REALM1);
3297 add_keyword(tb, FLG_SPELLBOOKS);
3300 toggle_keyword(tb, FLG_REALM2);
3301 add_keyword(tb, FLG_SPELLBOOKS);
3304 toggle_keyword(tb, FLG_FIRST);
3305 add_keyword(tb, FLG_SPELLBOOKS);
3308 toggle_keyword(tb, FLG_SECOND);
3309 add_keyword(tb, FLG_SPELLBOOKS);
3312 toggle_keyword(tb, FLG_THIRD);
3313 add_keyword(tb, FLG_SPELLBOOKS);
3316 toggle_keyword(tb, FLG_FOURTH);
3317 add_keyword(tb, FLG_SPELLBOOKS);
3321 tb->old_com_id = com_id;
3327 * Insert single letter at cursor position.
3329 static void insert_single_letter(text_body_type *tb, int key)
3332 char buf[MAX_LINELEN];
3334 for (i = j = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
3336 buf[j++] = tb->lines_list[tb->cy][i];
3346 if (j + 2 < MAX_LINELEN)
3348 buf[j++] = (char)key;
3349 buf[j++] = (char)next;
3358 if (j + 1 < MAX_LINELEN)
3359 buf[j++] = (char)key;
3363 for (; tb->lines_list[tb->cy][i] && j + 1 < MAX_LINELEN; i++)
3364 buf[j++] = tb->lines_list[tb->cy][i];
3367 string_free(tb->lines_list[tb->cy]);
3368 tb->lines_list[tb->cy] = string_make(buf);
3369 len = strlen(tb->lines_list[tb->cy]);
3370 if (len < tb->cx) tb->cx = len;
3372 tb->dirty_line = tb->cy;
3373 check_expression_line(tb, tb->cy);
3379 * Check special key code and get a movement command id
3381 static int analyze_move_key(text_body_type *tb, int skey)
3384 if (!(skey & SKEY_MASK)) return 0;
3386 switch (skey & ~SKEY_MOD_MASK)
3388 case SKEY_DOWN: com_id = EC_DOWN; break;
3389 case SKEY_LEFT: com_id = EC_LEFT; break;
3390 case SKEY_RIGHT: com_id = EC_RIGHT; break;
3391 case SKEY_UP: com_id = EC_UP; break;
3392 case SKEY_PGUP: com_id = EC_PGUP; break;
3393 case SKEY_PGDOWN: com_id = EC_PGDOWN; break;
3394 case SKEY_TOP: com_id = EC_TOP; break;
3395 case SKEY_BOTTOM: com_id = EC_BOTTOM; break;
3400 if (!(skey & SKEY_MOD_SHIFT))
3403 * Un-shifted cursor keys cancells
3404 * selection created by shift+cursor.
3406 if (tb->mark & MARK_BY_SHIFT)
3409 tb->dirty_flags |= DIRTY_ALL;
3415 if (tb->mark) return com_id;
3417 int len = strlen(tb->lines_list[tb->cy]);
3418 tb->mark = MARK_MARK | MARK_BY_SHIFT;
3421 if (tb->cx > len) tb->mx = len;
3423 if (com_id == EC_UP || com_id == EC_DOWN)
3425 tb->dirty_flags |= DIRTY_ALL;
3429 tb->dirty_line = tb->cy;
3436 * In-game editor of Object Auto-picker/Destoryer
3437 * @param player_ptr プレーヤーへの参照ポインタ
3439 void do_cmd_edit_autopick(player_type *player_ptr)
3441 static int cx_save = 0;
3442 static int cy_save = 0;
3443 text_body_type text_body, *tb = &text_body;
3444 autopick_type an_entry, *entry = &an_entry;
3445 char buf[MAX_LINELEN];
3448 static s32b old_autosave_turn = 0L;
3451 tb->changed = FALSE;
3454 tb->upper = tb->left = 0;
3456 tb->mx = tb->my = 0;
3457 tb->old_cy = tb->old_upper = tb->old_left = -1;
3458 tb->old_wid = tb->old_hgt = -1;
3462 tb->search_o_ptr = NULL;
3463 tb->search_str = NULL;
3464 tb->last_destroyed = NULL;
3465 tb->dirty_flags = DIRTY_ALL | DIRTY_MODE | DIRTY_EXPRESSION;
3466 tb->dirty_line = -1;
3467 tb->filename_mode = PT_DEFAULT;
3469 if (current_world_ptr->game_turn < old_autosave_turn)
3471 while (old_autosave_turn > current_world_ptr->game_turn) old_autosave_turn -= TURNS_PER_TICK * TOWN_DAWN;
3474 if (current_world_ptr->game_turn > old_autosave_turn + 100L)
3476 do_cmd_save_game(player_ptr, TRUE);
3477 old_autosave_turn = current_world_ptr->game_turn;
3482 if (autopick_last_destroyed_object.k_idx)
3484 autopick_entry_from_object(player_ptr, entry, &autopick_last_destroyed_object);
3485 tb->last_destroyed = autopick_line_from_entry_kill(entry);
3488 tb->lines_list = read_pickpref_text_lines(player_ptr, &tb->filename_mode);
3489 for (i = 0; i < tb->cy; i++)
3491 if (!tb->lines_list[i])
3493 tb->cy = tb->cx = 0;
3502 draw_text_editor(player_ptr, tb);
3503 prt(_("(^Q:終了 ^W:セーブして終了, ESC:メニュー, その他:入力)",
3504 "(^Q:Quit, ^W:Save&Quit, ESC:Menu, Other:Input text)"), 0, 0);
3507 prt(format("(%d,%d)", tb->cx, tb->cy), 0, 60);
3511 prt(format("(%d,%d)-(%d,%d)", tb->mx, tb->my, tb->cx, tb->cy), 0, 60);
3514 Term_gotoxy(tb->cx - tb->left, tb->cy - tb->upper + 1);
3515 tb->dirty_flags = 0;
3516 tb->dirty_line = -1;
3517 tb->old_cy = tb->cy;
3518 tb->old_upper = tb->upper;
3519 tb->old_left = tb->left;
3520 tb->old_wid = tb->wid;
3521 tb->old_hgt = tb->hgt;
3523 key = inkey_special(TRUE);
3525 if (key & SKEY_MASK)
3527 com_id = analyze_move_key(tb, key);
3529 else if (key == ESCAPE)
3531 com_id = do_command_menu(0, 0);
3532 tb->dirty_flags |= DIRTY_SCREEN;
3534 else if (!iscntrl((unsigned char)key))
3539 tb->dirty_flags |= DIRTY_ALL;
3542 insert_single_letter(tb, key);
3547 com_id = get_com_id((char)key);
3550 if (com_id) quit = do_editor_command(player_ptr, tb, com_id);
3554 strcpy(buf, pickpref_filename(player_ptr, tb->filename_mode));
3556 if (quit == QUIT_AND_SAVE)
3557 write_text_lines(buf, tb->lines_list);
3559 free_text_lines(tb->lines_list);
3560 string_free(tb->search_str);
3561 string_free(tb->last_destroyed);
3562 kill_yank_chain(tb);
3564 process_autopick_file(player_ptr, buf);
3565 current_world_ptr->start_time = (u32b)time(NULL);