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-destroyer.h"
26 #include "autopick/autopick-describer.h"
27 #include "autopick/autopick-entry.h"
28 #include "autopick/autopick-reader-writer.h"
29 #include "autopick/autopick-finder.h"
30 #include "autopick/autopick-adder.h"
31 #include "autopick/autopick-pref-processor.h"
33 #include "autopick/autopick.h"
35 #include "core/show-file.h"
36 #include "cmd/cmd-save.h"
37 #include "io/read-pref-file.h"
41 #include "market/store.h"
42 #include "player-status.h"
43 #include "player-move.h"
44 #include "player-class.h"
45 #include "player-race.h"
46 #include "player-inventory.h"
47 #include "view/display-player.h" // 暫定。後で消す.
48 #include "object/object-kind.h"
49 #include "object-ego.h"
50 #include "object-flavor.h"
51 #include "object-hook.h"
56 #include "monsterrace.h"
57 #include "view/display-main-window.h" // 暫定。後で消す.
62 static void auto_inscribe_item(player_type *player_ptr, object_type *o_ptr, int idx)
64 if (idx < 0 || !autopick_list[idx].insc) return;
66 if (!o_ptr->inscription)
67 o_ptr->inscription = quark_add(autopick_list[idx].insc);
69 player_ptr->window |= (PW_EQUIP | PW_INVEN);
70 player_ptr->update |= (PU_BONUS);
75 * Auto-destroy marked item
77 static void autopick_delayed_alter_aux(player_type *player_ptr, INVENTORY_IDX item)
80 o_ptr = REF_ITEM(player_ptr, player_ptr->current_floor_ptr, item);
82 if (o_ptr->k_idx == 0 || !(o_ptr->marked & OM_AUTODESTROY)) return;
84 GAME_TEXT o_name[MAX_NLEN];
85 object_desc(player_ptr, o_name, o_ptr, 0);
88 inven_item_increase(player_ptr, item, -(o_ptr->number));
89 inven_item_optimize(player_ptr, item);
93 delete_object_idx(player_ptr, 0 - item);
96 msg_format(_("%sを自動破壊します。", "Auto-destroying %s."), o_name);
101 * Auto-destroy marked items in inventry and on floor
103 void autopick_delayed_alter(player_type *owner_ptr)
108 * Scan inventry in reverse order to prevent
109 * skipping after inven_item_optimize()
111 for (item = INVEN_TOTAL - 1; item >= 0; item--)
112 autopick_delayed_alter_aux(owner_ptr, item);
114 floor_type *floor_ptr = owner_ptr->current_floor_ptr;
115 item = floor_ptr->grid_array[owner_ptr->y][owner_ptr->x].o_idx;
118 OBJECT_IDX next = floor_ptr->o_list[item].next_o_idx;
119 autopick_delayed_alter_aux(owner_ptr, -item);
126 * Auto-inscription and/or destroy
128 * Auto-destroyer works only on inventory or on floor stack only when
131 void autopick_alter_item(player_type *player_ptr, INVENTORY_IDX item, bool destroy)
134 o_ptr = REF_ITEM(player_ptr, player_ptr->current_floor_ptr, item);
135 int idx = find_autopick_list(player_ptr, o_ptr);
136 auto_inscribe_item(player_ptr, o_ptr, idx);
137 if (destroy && item <= INVEN_PACK)
138 auto_destroy_item(player_ptr, o_ptr, idx);
143 * Automatically pickup/destroy items in this grid.
145 void autopick_pickup_items(player_type* player_ptr, grid_type *g_ptr)
147 OBJECT_IDX this_o_idx, next_o_idx = 0;
148 for (this_o_idx = g_ptr->o_idx; this_o_idx; this_o_idx = next_o_idx)
150 object_type *o_ptr = &player_ptr->current_floor_ptr->o_list[this_o_idx];
151 next_o_idx = o_ptr->next_o_idx;
152 int idx = find_autopick_list(player_ptr, o_ptr);
153 auto_inscribe_item(player_ptr, o_ptr, idx);
154 bool is_auto_pickup = idx >= 0;
155 is_auto_pickup &= (autopick_list[idx].action & (DO_AUTOPICK | DO_QUERY_AUTOPICK)) != 0;
158 auto_destroy_item(player_ptr, o_ptr, idx);
162 disturb(player_ptr, FALSE, FALSE);
163 if (!inven_carry_okay(o_ptr))
165 GAME_TEXT o_name[MAX_NLEN];
166 object_desc(player_ptr, o_name, o_ptr, 0);
167 msg_format(_("ザックには%sを入れる隙間がない。", "You have no room for %s."), o_name);
168 o_ptr->marked |= OM_NOMSG;
172 if (!(autopick_list[idx].action & DO_QUERY_AUTOPICK))
174 py_pickup_aux(player_ptr, this_o_idx);
178 char out_val[MAX_NLEN + 20];
179 GAME_TEXT o_name[MAX_NLEN];
180 if (o_ptr->marked & OM_NO_QUERY)
185 object_desc(player_ptr, o_name, o_ptr, 0);
186 sprintf(out_val, _("%sを拾いますか? ", "Pick up %s? "), o_name);
187 if (!get_check(out_val))
189 o_ptr->marked |= OM_NOMSG | OM_NO_QUERY;
193 py_pickup_aux(player_ptr, this_o_idx);
198 static const char autoregister_header[] = "?:$AUTOREGISTER";
201 * Clear auto registered lines in the picktype.prf .
203 static bool clear_auto_register(player_type *player_ptr)
205 char pref_file[1024];
206 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_WITH_PNAME));
208 pref_fff = my_fopen(pref_file, "r");
212 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_DEFAULT));
213 pref_fff = my_fopen(pref_file, "r");
223 tmp_fff = my_fopen_temp(tmp_file, sizeof(tmp_file));
227 msg_format(_("一時ファイル %s を作成できませんでした。", "Failed to create temporary file %s."), tmp_file);
232 bool autoregister = FALSE;
237 if (my_fgets(pref_fff, buf, sizeof(buf))) break;
241 if (buf[0] != '#' && buf[0] != '?') num++;
245 if (streq(buf, autoregister_header))
251 fprintf(tmp_fff, "%s\n", buf);
261 msg_format(_("以前のキャラクター用の自動設定(%d行)が残っています。",
262 "Auto registered lines (%d lines) for previous character are remaining."), num);
263 strcpy(buf, _("古い設定行は削除します。よろしいですか?", "These lines will be deleted. Are you sure? "));
268 autoregister = FALSE;
270 msg_print(_("エディタのカット&ペースト等を使って必要な行を避難してください。",
271 "Use cut & paste of auto picker editor (_) to keep old prefs."));
277 tmp_fff = my_fopen(tmp_file, "r");
278 pref_fff = my_fopen(pref_file, "w");
280 while (!my_fgets(tmp_fff, buf, sizeof(buf)))
281 fprintf(pref_fff, "%s\n", buf);
293 * Automatically register an auto-destroy preference line
295 bool autopick_autoregister(player_type *player_ptr, object_type *o_ptr)
298 char pref_file[1024];
300 autopick_type an_entry, *entry = &an_entry;
301 int match_autopick = find_autopick_list(player_ptr, o_ptr);
302 if (match_autopick != -1)
305 byte act = autopick_list[match_autopick].action;
306 if (act & DO_AUTOPICK) what = _("自動で拾う", "auto-pickup");
307 else if (act & DO_AUTODESTROY) what = _("自動破壊する", "auto-destroy");
308 else if (act & DONT_AUTOPICK) what = _("放置する", "leave on floor");
309 else what = _("確認して拾う", "query auto-pickup");
311 msg_format(_("そのアイテムは既に%sように設定されています。", "The object is already registered to %s."), what);
315 if ((object_is_known(o_ptr) && object_is_artifact(o_ptr)) ||
316 ((o_ptr->ident & IDENT_SENSE) &&
317 (o_ptr->feeling == FEEL_TERRIBLE || o_ptr->feeling == FEEL_SPECIAL)))
319 GAME_TEXT o_name[MAX_NLEN];
320 object_desc(player_ptr, o_name, o_ptr, 0);
321 msg_format(_("%sは破壊不能だ。", "You cannot auto-destroy %s."), o_name);
325 if (!player_ptr->autopick_autoregister)
327 if (!clear_auto_register(player_ptr)) return FALSE;
330 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_WITH_PNAME));
331 pref_fff = my_fopen(pref_file, "r");
335 path_build(pref_file, sizeof(pref_file), ANGBAND_DIR_USER, pickpref_filename(player_ptr, PT_DEFAULT));
336 pref_fff = my_fopen(pref_file, "r");
343 if (my_fgets(pref_fff, buf, sizeof(buf)))
345 player_ptr->autopick_autoregister = FALSE;
349 if (streq(buf, autoregister_header))
351 player_ptr->autopick_autoregister = TRUE;
361 * File could not be opened for reading. Assume header not
364 player_ptr->autopick_autoregister = FALSE;
367 pref_fff = my_fopen(pref_file, "a");
370 msg_format(_("%s を開くことができませんでした。", "Failed to open %s."), pref_file);
375 if (!player_ptr->autopick_autoregister)
377 fprintf(pref_fff, "%s\n", autoregister_header);
379 fprintf(pref_fff, "%s\n", _("# *警告!!* 以降の行は自動登録されたものです。",
380 "# *Warning!* The lines below will be deleted later."));
381 fprintf(pref_fff, "%s\n", _("# 後で自動的に削除されますので、必要な行は上の方へ移動しておいてください。",
382 "# Keep it by cut & paste if you need these lines for future characters."));
383 player_ptr->autopick_autoregister = TRUE;
386 autopick_entry_from_object(player_ptr, entry, o_ptr);
387 entry->action = DO_AUTODESTROY;
388 add_autopick_list(entry);
390 concptr tmp = autopick_line_from_entry(entry);
391 fprintf(pref_fff, "%s\n", tmp);
399 * Delete or insert string
401 static void toggle_keyword(text_body_type *tb, BIT_FLAGS flg)
408 by1 = MIN(tb->my, tb->cy);
409 by2 = MAX(tb->my, tb->cy);
411 else /* if (!tb->mark) */
416 for (int y = by1; y <= by2; y++)
418 autopick_type an_entry, *entry = &an_entry;
419 if (!autopick_new_entry(entry, tb->lines_list[y], !fixed)) continue;
421 string_free(tb->lines_list[y]);
424 if (!IS_FLG(flg)) add = TRUE;
430 if (FLG_NOUN_BEGIN <= flg && flg <= FLG_NOUN_END)
433 for (i = FLG_NOUN_BEGIN; i <= FLG_NOUN_END; i++)
436 else if (FLG_UNAWARE <= flg && flg <= FLG_STAR_IDENTIFIED)
439 for (i = FLG_UNAWARE; i <= FLG_STAR_IDENTIFIED; i++)
442 else if (FLG_ARTIFACT <= flg && flg <= FLG_AVERAGE)
445 for (i = FLG_ARTIFACT; i <= FLG_AVERAGE; i++)
448 else if (FLG_RARE <= flg && flg <= FLG_COMMON)
451 for (i = FLG_RARE; i <= FLG_COMMON; i++)
455 if (add) ADD_FLG(flg);
458 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
459 tb->dirty_flags |= DIRTY_ALL;
466 * Change command letter
468 static void toggle_command_letter(text_body_type *tb, byte flg)
470 autopick_type an_entry;
471 autopick_type *entry = &an_entry;
477 by1 = MIN(tb->my, tb->cy);
478 by2 = MAX(tb->my, tb->cy);
480 else /* if (!tb->mark) */
485 for (int y = by1; y <= by2; y++)
489 if (!autopick_new_entry(entry, tb->lines_list[y], FALSE)) continue;
491 string_free(tb->lines_list[y]);
495 if (!(entry->action & flg)) add = TRUE;
501 if (entry->action & DONT_AUTOPICK) wid--;
502 else if (entry->action & DO_AUTODESTROY) wid--;
503 else if (entry->action & DO_QUERY_AUTOPICK) wid--;
504 if (!(entry->action & DO_DISPLAY)) wid--;
506 if (flg != DO_DISPLAY)
508 entry->action &= ~(DO_AUTOPICK | DONT_AUTOPICK | DO_AUTODESTROY | DO_QUERY_AUTOPICK);
509 if (add) entry->action |= flg;
510 else entry->action |= DO_AUTOPICK;
514 entry->action &= ~(DO_DISPLAY);
515 if (add) entry->action |= flg;
520 if (entry->action & DONT_AUTOPICK) wid++;
521 else if (entry->action & DO_AUTODESTROY) wid++;
522 else if (entry->action & DO_QUERY_AUTOPICK) wid++;
523 if (!(entry->action & DO_DISPLAY)) wid++;
525 if (wid > 0) tb->cx++;
526 if (wid < 0 && tb->cx > 0) tb->cx--;
529 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
530 tb->dirty_flags |= DIRTY_ALL;
537 * Delete or insert string
539 static void add_keyword(text_body_type *tb, BIT_FLAGS flg)
544 by1 = MIN(tb->my, tb->cy);
545 by2 = MAX(tb->my, tb->cy);
552 for (int y = by1; y <= by2; y++)
554 autopick_type an_entry, *entry = &an_entry;
555 if (!autopick_new_entry(entry, tb->lines_list[y], FALSE)) continue;
559 autopick_free_entry(entry);
563 string_free(tb->lines_list[y]);
564 if (FLG_NOUN_BEGIN <= flg && flg <= FLG_NOUN_END)
567 for (i = FLG_NOUN_BEGIN; i <= FLG_NOUN_END; i++)
572 tb->lines_list[y] = autopick_line_from_entry_kill(entry);
573 tb->dirty_flags |= DIRTY_ALL;
580 * Check if this line is expression or not.
581 * And update it if it is.
583 static void check_expression_line(text_body_type *tb, int y)
585 concptr s = tb->lines_list[y];
587 if ((s[0] == '?' && s[1] == ':') ||
588 (tb->states[y] & LSTAT_BYPASS))
590 tb->dirty_flags |= DIRTY_EXPRESSION;
596 * Add an empty line at the last of the file
598 static bool add_empty_line(text_body_type *tb)
601 for (num_lines = 0; tb->lines_list[num_lines]; num_lines++);
603 if (num_lines >= MAX_LINES - 2) return FALSE;
604 if (!tb->lines_list[num_lines - 1][0]) return FALSE;
606 tb->lines_list[num_lines] = string_make("");
607 tb->dirty_flags |= DIRTY_EXPRESSION;
614 * Insert return code and split the line
616 static bool insert_return_code(text_body_type *tb)
618 char buf[MAX_LINELEN];
621 for (num_lines = 0; tb->lines_list[num_lines]; num_lines++);
623 if (num_lines >= MAX_LINES - 2) return FALSE;
626 for (; tb->cy < num_lines; num_lines--)
628 tb->lines_list[num_lines + 1] = tb->lines_list[num_lines];
629 tb->states[num_lines + 1] = tb->states[num_lines];
632 for (i = j = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
635 if (iskanji(tb->lines_list[tb->cy][i]))
636 buf[j++] = tb->lines_list[tb->cy][i++];
638 buf[j++] = tb->lines_list[tb->cy][i];
642 tb->lines_list[tb->cy + 1] = string_make(&tb->lines_list[tb->cy][i]);
643 string_free(tb->lines_list[tb->cy]);
644 tb->lines_list[tb->cy] = string_make(buf);
645 tb->dirty_flags |= DIRTY_EXPRESSION;
652 * Choose an item for search
654 static bool get_object_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
656 concptr q = _("どのアイテムを検索しますか? ", "Enter which item? ");
657 concptr s = _("アイテムを持っていない。", "You have nothing to enter.");
659 o_ptr = choose_object(player_ptr, NULL, q, s, USE_INVEN | USE_FLOOR | USE_EQUIP, 0);
660 if (!o_ptr) return FALSE;
663 string_free(*search_strp);
664 char buf[MAX_NLEN + 20];
665 object_desc(player_ptr, buf, *o_handle, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
666 *search_strp = string_make(format("<%s>", buf));
672 * Prepare for search by destroyed object
674 static bool get_destroyed_object_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
676 if (!autopick_last_destroyed_object.k_idx) return FALSE;
678 *o_handle = &autopick_last_destroyed_object;
679 string_free(*search_strp);
680 char buf[MAX_NLEN + 20];
681 object_desc(player_ptr, buf, *o_handle, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
682 *search_strp = string_make(format("<%s>", buf));
688 * Choose an item or string for search
690 static byte get_string_for_search(player_type *player_ptr, object_type **o_handle, concptr *search_strp)
694 * TERM_YELLOW : Overwrite mode
695 * TERM_WHITE : Insert mode
697 byte color = TERM_YELLOW;
698 char buf[MAX_NLEN + 20];
700 char prompt[] = _("検索(^I:持ち物 ^L:破壊された物): ", "Search key(^I:inven ^L:destroyed): ");
701 int col = sizeof(prompt) - 1;
702 if (*search_strp) strcpy(buf, *search_strp);
705 if (*o_handle) color = TERM_L_GREEN;
712 Term_erase(col, 0, 255);
713 Term_putstr(col, 0, -1, color, buf);
714 Term_gotoxy(col + pos, 0);
716 int skey = inkey_special(TRUE);
728 int next_pos = i + 1;
731 if (iskanji(buf[i])) next_pos++;
733 if (next_pos >= pos) break;
745 if ('\0' == buf[pos]) break;
748 if (iskanji(buf[pos])) pos += 2;
765 if (*o_handle) return (back ? -1 : 1);
766 string_free(*search_strp);
767 *search_strp = string_make(buf);
769 return (back ? -1 : 1);
772 return get_object_for_search(player_ptr, o_handle, search_strp);
775 if (get_destroyed_object_for_search(player_ptr, o_handle, search_strp))
787 int next_pos = i + 1;
789 if (iskanji(buf[i])) next_pos++;
791 if (next_pos >= pos) break;
805 if (buf[pos] == '\0') break;
809 if (iskanji(buf[pos])) src++;
812 while ('\0' != (buf[dst++] = buf[src++]));
821 if (skey & SKEY_MASK) break;
824 if (color != TERM_WHITE)
826 if (color == TERM_L_GREEN)
829 string_free(*search_strp);
837 strcpy(tmp, buf + pos);
859 if (pos < len && (isprint(c) || iskana(c)))
861 if (pos < len && isprint(c))
873 my_strcat(buf, tmp, len + 1);
879 if (*o_handle == NULL || color == TERM_L_GREEN) continue;
883 string_free(*search_strp);
890 * Search next line matches for o_ptr
892 static void search_for_object(player_type *player_ptr, text_body_type *tb, object_type *o_ptr, bool forward)
894 autopick_type an_entry, *entry = &an_entry;
895 GAME_TEXT o_name[MAX_NLEN];
896 int bypassed_cy = -1;
898 object_desc(player_ptr, o_name, o_ptr, (OD_NO_FLAVOR | OD_OMIT_PREFIX | OD_NO_PLURAL));
906 if (!tb->lines_list[++i]) break;
913 if (!autopick_new_entry(entry, tb->lines_list[i], FALSE)) continue;
915 match = is_autopick_match(player_ptr, o_ptr, entry, o_name);
916 autopick_free_entry(entry);
917 if (!match) continue;
919 if (tb->states[i] & LSTAT_BYPASS)
921 if (bypassed_cy == -1) bypassed_cy = i;
927 if (bypassed_cy != -1)
929 tb->dirty_flags |= DIRTY_SKIP_INACTIVE;
935 if (bypassed_cy == -1)
937 tb->dirty_flags |= DIRTY_NOT_FOUND;
942 tb->cy = bypassed_cy;
943 tb->dirty_flags |= DIRTY_INACTIVE;
948 * Search next line matches to the string
950 static void search_for_string(text_body_type *tb, concptr search_str, bool forward)
952 int bypassed_cy = -1;
961 if (!tb->lines_list[++i]) break;
968 pos = my_strstr(tb->lines_list[i], search_str);
971 if ((tb->states[i] & LSTAT_BYPASS) &&
972 !(tb->states[i] & LSTAT_EXPRESSION))
974 if (bypassed_cy == -1)
977 bypassed_cx = (int)(pos - tb->lines_list[i]);
983 tb->cx = (int)(pos - tb->lines_list[i]);
986 if (bypassed_cy != -1)
988 tb->dirty_flags |= DIRTY_SKIP_INACTIVE;
994 if (bypassed_cy == -1)
996 tb->dirty_flags |= DIRTY_NOT_FOUND;
1000 tb->cx = bypassed_cx;
1001 tb->cy = bypassed_cy;
1002 tb->dirty_flags |= DIRTY_INACTIVE;
1007 * Display the menu, and get a command
1009 static int do_command_menu(int level, int start)
1012 int col0 = 5 + level * 7;
1013 int row0 = 1 + level * 3;
1014 int menu_id_list[26];
1016 char linestr[MAX_LINELEN];
1019 for (int i = start; menu_data[i].level >= level; i++)
1021 /* Ignore lower level sub menus */
1022 if (menu_data[i].level > level) continue;
1024 int len = strlen(menu_data[i].name);
1025 if (len > max_len) max_len = len;
1027 menu_id_list[menu_key] = i;
1031 while (menu_key < 26)
1033 menu_id_list[menu_key] = -1;
1037 int max_menu_wid = max_len + 3 + 3;
1039 /* Prepare box line */
1041 strcat(linestr, "+");
1042 for (int i = 0; i < max_menu_wid + 2; i++)
1044 strcat(linestr, "-");
1047 strcat(linestr, "+");
1053 int row1 = row0 + 1;
1054 Term_putstr(col0, row0, -1, TERM_WHITE, linestr);
1057 for (int i = start; menu_data[i].level >= level; i++)
1059 char com_key_str[3];
1061 if (menu_data[i].level > level) continue;
1063 if (menu_data[i].com_id == -1)
1065 strcpy(com_key_str, _("▼", ">"));
1067 else if (menu_data[i].key != -1)
1069 com_key_str[0] = '^';
1070 com_key_str[1] = menu_data[i].key + '@';
1071 com_key_str[2] = '\0';
1075 com_key_str[0] = '\0';
1078 str = format("| %c) %-*s %2s | ", menu_key + 'a', max_len, menu_data[i].name, com_key_str);
1080 Term_putstr(col0, row1++, -1, TERM_WHITE, str);
1085 Term_putstr(col0, row1, -1, TERM_WHITE, linestr);
1089 prt(format(_("(a-%c) コマンド:", "(a-%c) Command:"), menu_key + 'a' - 1), 0, 0);
1092 if (key == ESCAPE) return 0;
1095 bool is_alphabet = key >= 'a' && key <= 'z';
1098 com_id = get_com_id(key);
1107 int menu_id = menu_id_list[key - 'a'];
1109 if (menu_id < 0) continue;
1111 com_id = menu_data[menu_id].com_id;
1115 com_id = do_command_menu(level + 1, menu_id + 1);
1117 if (com_id) return com_id;
1128 static chain_str_type *new_chain_str(concptr str)
1130 chain_str_type *chain;
1131 size_t len = strlen(str);
1132 chain = (chain_str_type *)ralloc(sizeof(chain_str_type) + len * sizeof(char));
1133 strcpy(chain->s, str);
1139 static void kill_yank_chain(text_body_type *tb)
1141 chain_str_type *chain = tb->yank;
1143 tb->yank_eol = TRUE;
1147 chain_str_type *next = chain->next;
1148 size_t len = strlen(chain->s);
1150 rnfree(chain, sizeof(chain_str_type) + len * sizeof(char));
1157 static void add_str_to_yank(text_body_type *tb, concptr str)
1159 tb->yank_eol = FALSE;
1160 if (NULL == tb->yank)
1162 tb->yank = new_chain_str(str);
1166 chain_str_type *chain;
1173 chain->next = new_chain_str(str);
1178 chain = chain->next;
1184 * Do work for the copy editor-command
1186 static void copy_text_to_yank(text_body_type *tb)
1188 int len = strlen(tb->lines_list[tb->cy]);
1189 if (tb->cx > len) tb->cx = len;
1198 kill_yank_chain(tb);
1199 if (tb->my != tb->cy)
1201 int by1 = MIN(tb->my, tb->cy);
1202 int by2 = MAX(tb->my, tb->cy);
1204 for (int y = by1; y <= by2; y++)
1206 add_str_to_yank(tb, tb->lines_list[y]);
1209 add_str_to_yank(tb, "");
1211 tb->dirty_flags |= DIRTY_ALL;
1215 char buf[MAX_LINELEN];
1216 int bx1 = MIN(tb->mx, tb->cx);
1217 int bx2 = MAX(tb->mx, tb->cx);
1218 if (bx2 > len) bx2 = len;
1220 if (bx1 == 0 && bx2 == len)
1222 add_str_to_yank(tb, tb->lines_list[tb->cy]);
1223 add_str_to_yank(tb, "");
1227 int end = bx2 - bx1;
1228 for (int i = 0; i < bx2 - bx1; i++)
1230 buf[i] = tb->lines_list[tb->cy][bx1 + i];
1234 add_str_to_yank(tb, buf);
1238 tb->dirty_flags |= DIRTY_ALL;
1245 static void draw_text_editor(player_type *player_ptr, text_body_type *tb)
1248 int by1 = 0, by2 = 0;
1250 Term_get_size(&tb->wid, &tb->hgt);
1253 * Top line (-1), description line (-3), separator (-1)
1256 tb->hgt -= 2 + DESCRIPT_HGT;
1259 /* Don't let cursor at second byte of kanji */
1260 for (i = 0; tb->lines_list[tb->cy][i]; i++)
1261 if (iskanji(tb->lines_list[tb->cy][i]))
1267 * Move to a correct position in the
1270 if (i & 1) tb->cx--;
1276 if (tb->cy < tb->upper || tb->upper + tb->hgt <= tb->cy)
1277 tb->upper = tb->cy - (tb->hgt) / 2;
1280 if ((tb->cx < tb->left + 10 && tb->left > 0) || tb->left + tb->wid - 5 <= tb->cx)
1281 tb->left = tb->cx - (tb->wid) * 2 / 3;
1285 if (tb->old_wid != tb->wid || tb->old_hgt != tb->hgt)
1286 tb->dirty_flags |= DIRTY_SCREEN;
1287 else if (tb->old_upper != tb->upper || tb->old_left != tb->left)
1288 tb->dirty_flags |= DIRTY_ALL;
1290 if (tb->dirty_flags & DIRTY_SCREEN)
1292 tb->dirty_flags |= (DIRTY_ALL | DIRTY_MODE);
1296 if (tb->dirty_flags & DIRTY_MODE)
1298 char buf[MAX_LINELEN];
1299 int sepa_length = tb->wid;
1300 for (i = 0; i < sepa_length; i++)
1303 Term_putstr(0, tb->hgt + 1, sepa_length, TERM_WHITE, buf);
1306 if (tb->dirty_flags & DIRTY_EXPRESSION)
1309 for (int y = 0; tb->lines_list[y]; y++)
1313 concptr s = tb->lines_list[y];
1317 tb->states[y] = state;
1319 if (*s++ != '?') continue;
1320 if (*s++ != ':') continue;
1322 if (streq(s, "$AUTOREGISTER"))
1323 state |= LSTAT_AUTOREGISTER;
1326 ss = (char *)string_make(s);
1329 v = process_pref_file_expr(player_ptr, &ss, &f);
1331 if (streq(v, "0")) state |= LSTAT_BYPASS;
1332 else state &= ~LSTAT_BYPASS;
1334 C_KILL(s_keep, s_len + 1, char);
1336 tb->states[y] = state | LSTAT_EXPRESSION;
1339 tb->dirty_flags |= DIRTY_ALL;
1344 tb->dirty_flags |= DIRTY_ALL;
1346 by1 = MIN(tb->my, tb->cy);
1347 by2 = MAX(tb->my, tb->cy);
1350 for (i = 0; i < tb->hgt; i++)
1356 int y = tb->upper + i;
1358 if (!(tb->dirty_flags & DIRTY_ALL) && (tb->dirty_line != y))
1361 msg = tb->lines_list[y];
1364 for (j = 0; *msg; msg++, j++)
1366 if (j == tb->left) break;
1381 Term_erase(0, i + 1, tb->wid);
1382 if (tb->states[y] & LSTAT_AUTOREGISTER)
1388 if (tb->states[y] & LSTAT_BYPASS) color = TERM_SLATE;
1389 else color = TERM_WHITE;
1392 if (!tb->mark || (y < by1 || by2 < y))
1394 Term_putstr(leftcol, i + 1, tb->wid - 1, color, msg);
1396 else if (by1 != by2)
1398 Term_putstr(leftcol, i + 1, tb->wid - 1, TERM_YELLOW, msg);
1402 int x0 = leftcol + tb->left;
1403 int len = strlen(tb->lines_list[tb->cy]);
1404 int bx1 = MIN(tb->mx, tb->cx);
1405 int bx2 = MAX(tb->mx, tb->cx);
1407 if (bx2 > len) bx2 = len;
1409 Term_gotoxy(leftcol, i + 1);
1410 if (x0 < bx1) Term_addstr(bx1 - x0, color, msg);
1411 if (x0 < bx2) Term_addstr(bx2 - bx1, TERM_YELLOW, msg + (bx1 - x0));
1412 Term_addstr(-1, color, msg + (bx2 - x0));
1416 for (; i < tb->hgt; i++)
1418 Term_erase(0, i + 1, tb->wid);
1421 bool is_dirty_diary = (tb->dirty_flags & (DIRTY_ALL | DIRTY_NOT_FOUND | DIRTY_NO_SEARCH)) != 0;
1422 bool is_updated = tb->old_cy != tb->cy || is_dirty_diary || tb->dirty_line == tb->cy;
1423 if (is_updated) return;
1425 autopick_type an_entry, *entry = &an_entry;
1426 concptr str1 = NULL, str2 = NULL;
1427 for (i = 0; i < DESCRIPT_HGT; i++)
1429 Term_erase(0, tb->hgt + 2 + i, tb->wid);
1432 if (tb->dirty_flags & DIRTY_NOT_FOUND)
1434 str1 = format(_("パターンが見つかりません: %s", "Pattern not found: %s"), tb->search_str);
1436 else if (tb->dirty_flags & DIRTY_SKIP_INACTIVE)
1438 str1 = format(_("無効状態の行をスキップしました。(%sを検索中)",
1439 "Some inactive lines are skipped. (Searching %s)"), tb->search_str);
1441 else if (tb->dirty_flags & DIRTY_INACTIVE)
1443 str1 = format(_("無効状態の行だけが見付かりました。(%sを検索中)",
1444 "Found only an inactive line. (Searching %s)"), tb->search_str);
1446 else if (tb->dirty_flags & DIRTY_NO_SEARCH)
1448 str1 = _("検索するパターンがありません(^S で検索)。", "No pattern to search. (Press ^S to search.)");
1450 else if (tb->lines_list[tb->cy][0] == '#')
1452 str1 = _("この行はコメントです。", "This line is a comment.");
1454 else if (tb->lines_list[tb->cy][0] && tb->lines_list[tb->cy][1] == ':')
1456 switch (tb->lines_list[tb->cy][0])
1459 str1 = _("この行は条件分岐式です。", "This line is a Conditional Expression.");
1462 str1 = _("この行はマクロの実行内容を定義します。", "This line defines a Macro action.");
1465 str1 = _("この行はマクロのトリガー・キーを定義します。", "This line defines a Macro trigger key.");
1468 str1 = _("この行はキー配置を定義します。", "This line defines a Keymap.");
1472 switch (tb->lines_list[tb->cy][0])
1475 if (tb->states[tb->cy] & LSTAT_BYPASS)
1477 str2 = _("現在の式の値は「偽(=0)」です。", "The expression is 'False'(=0) currently.");
1481 str2 = _("現在の式の値は「真(=1)」です。", "The expression is 'True'(=1) currently.");
1486 if (tb->states[tb->cy] & LSTAT_AUTOREGISTER)
1488 str2 = _("この行は後で削除されます。", "This line will be delete later.");
1491 else if (tb->states[tb->cy] & LSTAT_BYPASS)
1493 str2 = _("この行は現在は無効な状態です。", "This line is bypassed currently.");
1498 else if (autopick_new_entry(entry, tb->lines_list[tb->cy], FALSE))
1500 char buf[MAX_LINELEN];
1501 char temp[MAX_LINELEN];
1504 describe_autopick(buf, entry);
1506 if (tb->states[tb->cy] & LSTAT_AUTOREGISTER)
1508 strcat(buf, _("この行は後で削除されます。", " This line will be delete later."));
1511 if (tb->states[tb->cy] & LSTAT_BYPASS)
1513 strcat(buf, _("この行は現在は無効な状態です。", " This line is bypassed currently."));
1516 roff_to_buf(buf, 81, temp, sizeof(temp));
1518 for (i = 0; i < 3; i++)
1524 prt(t, tb->hgt + 1 + 1 + i, 0);
1528 autopick_free_entry(entry);
1531 if (str1) prt(str1, tb->hgt + 1 + 1, 0);
1532 if (str2) prt(str2, tb->hgt + 1 + 2, 0);
1537 * Kill segment of a line
1539 static void kill_line_segment(text_body_type *tb, int y, int x0, int x1, bool whole)
1541 concptr s = tb->lines_list[y];
1542 if (whole && x0 == 0 && s[x1] == '\0' && tb->lines_list[y + 1])
1544 string_free(tb->lines_list[y]);
1547 for (i = y; tb->lines_list[i + 1]; i++)
1548 tb->lines_list[i] = tb->lines_list[i + 1];
1549 tb->lines_list[i] = NULL;
1551 tb->dirty_flags |= DIRTY_EXPRESSION;
1556 if (x0 == x1) return;
1558 char buf[MAX_LINELEN];
1560 for (int x = 0; x < x0; x++)
1563 for (int x = x1; s[x]; x++)
1567 string_free(tb->lines_list[y]);
1568 tb->lines_list[y] = string_make(buf);
1569 check_expression_line(tb, y);
1575 * Get a trigger key and insert ASCII string for the trigger
1577 static bool insert_macro_line(text_body_type *tb)
1596 ascii_to_text(tmp, buf);
1597 if (!tmp[0]) return FALSE;
1600 insert_return_code(tb);
1601 string_free(tb->lines_list[tb->cy]);
1602 tb->lines_list[tb->cy] = string_make(format("P:%s", tmp));
1604 i = macro_find_exact(buf);
1611 ascii_to_text(tmp, macro__act[i]);
1614 insert_return_code(tb);
1615 string_free(tb->lines_list[tb->cy]);
1616 tb->lines_list[tb->cy] = string_make(format("A:%s", tmp));
1623 * Get a command key and insert ASCII string for the key
1625 static bool insert_keymap_line(text_body_type *tb)
1628 if (rogue_like_commands)
1630 mode = KEYMAP_MODE_ROGUE;
1634 mode = KEYMAP_MODE_ORIG;
1644 ascii_to_text(tmp, buf);
1645 if (!tmp[0]) return FALSE;
1648 insert_return_code(tb);
1649 string_free(tb->lines_list[tb->cy]);
1650 tb->lines_list[tb->cy] = string_make(format("C:%d:%s", mode, tmp));
1652 concptr act = keymap_act[mode][(byte)(buf[0])];
1655 ascii_to_text(tmp, act);
1658 insert_return_code(tb);
1659 string_free(tb->lines_list[tb->cy]);
1660 tb->lines_list[tb->cy] = string_make(format("A:%s", tmp));
1667 * Execute a single editor command
1669 static bool do_editor_command(player_type *player_ptr, text_body_type *tb, int com_id)
1676 if (!get_check(_("全ての変更を破棄してから終了します。よろしいですか? ",
1677 "Discard all changes and quit. Are you sure? "))) break;
1680 return QUIT_WITHOUT_SAVE;
1683 return QUIT_AND_SAVE;
1686 if (!get_check(_("全ての変更を破棄して元の状態に戻します。よろしいですか? ",
1687 "Discard all changes and revert to original file. Are you sure? "))) break;
1689 free_text_lines(tb->lines_list);
1690 tb->lines_list = read_pickpref_text_lines(player_ptr, &tb->filename_mode);
1691 tb->dirty_flags |= DIRTY_ALL | DIRTY_MODE | DIRTY_EXPRESSION;
1692 tb->cx = tb->cy = 0;
1695 tb->changed = FALSE;
1699 (void)show_file(player_ptr, TRUE, _("jeditor.txt", "editor.txt"), NULL, 0, 0);
1700 tb->dirty_flags |= DIRTY_SCREEN;
1708 tb->dirty_flags |= DIRTY_ALL;
1711 insert_return_code(tb);
1715 tb->dirty_flags |= DIRTY_ALL;
1727 len = strlen(tb->lines_list[tb->cy]);
1728 if (len < tb->cx) tb->cx = len;
1730 for (i = 0; tb->lines_list[tb->cy][i]; i++)
1732 if (iskanji(tb->lines_list[tb->cy][i]))
1744 else if (tb->cy > 0)
1747 tb->cx = strlen(tb->lines_list[tb->cy]);
1753 if (!tb->lines_list[tb->cy + 1])
1755 if (!add_empty_line(tb)) break;
1762 if (tb->cy > 0) tb->cy--;
1768 if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
1771 int len = strlen(tb->lines_list[tb->cy]);
1775 if (!tb->lines_list[tb->cy + 1])
1777 if (!add_empty_line(tb)) break;
1791 tb->cx = strlen(tb->lines_list[tb->cy]);
1795 while (0 < tb->cy && tb->upper <= tb->cy)
1797 while (0 < tb->upper && tb->cy + 1 < tb->upper + tb->hgt)
1802 while (tb->cy < tb->upper + tb->hgt)
1804 if (!tb->lines_list[tb->cy + 1])
1806 if (!add_empty_line(tb)) break;
1822 if (!tb->lines_list[tb->cy + 1])
1824 if (!add_empty_line(tb)) break;
1835 copy_text_to_yank(tb);
1836 if (tb->my == tb->cy)
1838 int bx1 = MIN(tb->mx, tb->cx);
1839 int bx2 = MAX(tb->mx, tb->cx);
1840 int len = strlen(tb->lines_list[tb->cy]);
1841 if (bx2 > len) bx2 = len;
1843 kill_line_segment(tb, tb->cy, bx1, bx2, TRUE);
1848 int by1 = MIN(tb->my, tb->cy);
1849 int by2 = MAX(tb->my, tb->cy);
1851 for (int y = by2; y >= by1; y--)
1853 int len = strlen(tb->lines_list[y]);
1855 kill_line_segment(tb, y, 0, len, TRUE);
1863 tb->dirty_flags |= DIRTY_ALL;
1868 copy_text_to_yank(tb);
1871 * Move cursor position to the end of the selection
1873 * Pressing ^C ^V correctly duplicates the selection.
1875 if (tb->my != tb->cy)
1877 tb->cy = MAX(tb->cy, tb->my);
1878 if (!tb->lines_list[tb->cy + 1])
1880 if (!add_empty_line(tb)) break;
1887 tb->cx = MAX(tb->cx, tb->mx);
1888 if (!tb->lines_list[tb->cy][tb->cx])
1890 if (!tb->lines_list[tb->cy + 1])
1892 if (!add_empty_line(tb)) break;
1903 chain_str_type *chain = tb->yank;
1904 int len = strlen(tb->lines_list[tb->cy]);
1906 if (tb->cx > len) tb->cx = len;
1911 tb->dirty_flags |= DIRTY_ALL;
1916 concptr yank_str = chain->s;
1917 char buf[MAX_LINELEN];
1919 char rest[MAX_LINELEN], *rest_ptr = rest;
1920 for (i = 0; i < tb->cx; i++)
1921 buf[i] = tb->lines_list[tb->cy][i];
1923 strcpy(rest, &(tb->lines_list[tb->cy][i]));
1924 while (*yank_str && i < MAX_LINELEN - 1)
1926 buf[i++] = *yank_str++;
1930 chain = chain->next;
1931 if (chain || tb->yank_eol)
1933 insert_return_code(tb);
1934 string_free(tb->lines_list[tb->cy]);
1935 tb->lines_list[tb->cy] = string_make(buf);
1942 tb->cx = strlen(buf);
1943 while (*rest_ptr && i < MAX_LINELEN - 1)
1945 buf[i++] = *rest_ptr++;
1949 string_free(tb->lines_list[tb->cy]);
1950 tb->lines_list[tb->cy] = string_make(buf);
1954 tb->dirty_flags |= DIRTY_ALL;
1955 tb->dirty_flags |= DIRTY_EXPRESSION;
1964 tb->dirty_flags |= DIRTY_ALL;
1968 tb->mark = MARK_MARK;
1969 if (com_id == tb->old_com_id)
1977 tb->dirty_flags |= DIRTY_ALL;
1981 int len = strlen(tb->lines_list[tb->cy]);
1985 if (tb->cx > len) tb->mx = len;
1990 int len = strlen(tb->lines_list[tb->cy]);
1991 if (tb->cx > len) tb->cx = len;
1996 tb->dirty_flags |= DIRTY_ALL;
1999 if (tb->old_com_id != com_id)
2001 kill_yank_chain(tb);
2007 add_str_to_yank(tb, &(tb->lines_list[tb->cy][tb->cx]));
2008 kill_line_segment(tb, tb->cy, tb->cx, len, FALSE);
2009 tb->dirty_line = tb->cy;
2013 if (tb->yank_eol) add_str_to_yank(tb, "");
2015 tb->yank_eol = TRUE;
2016 do_editor_command(player_ptr, tb, EC_DELETE_CHAR);
2019 case EC_DELETE_CHAR:
2024 tb->dirty_flags |= DIRTY_ALL;
2028 if (iskanji(tb->lines_list[tb->cy][tb->cx])) tb->cx++;
2031 int len = strlen(tb->lines_list[tb->cy]);
2034 do_editor_command(player_ptr, tb, EC_BACKSPACE);
2038 if (tb->lines_list[tb->cy + 1])
2049 do_editor_command(player_ptr, tb, EC_BACKSPACE);
2055 char buf[MAX_LINELEN];
2059 tb->dirty_flags |= DIRTY_ALL;
2062 len = strlen(tb->lines_list[tb->cy]);
2063 if (len < tb->cx) tb->cx = len;
2067 if (tb->cy == 0) break;
2068 tb->cx = strlen(tb->lines_list[tb->cy - 1]);
2069 strcpy(buf, tb->lines_list[tb->cy - 1]);
2070 strcat(buf, tb->lines_list[tb->cy]);
2071 string_free(tb->lines_list[tb->cy - 1]);
2072 string_free(tb->lines_list[tb->cy]);
2073 tb->lines_list[tb->cy - 1] = string_make(buf);
2075 for (i = tb->cy; tb->lines_list[i + 1]; i++)
2076 tb->lines_list[i] = tb->lines_list[i + 1];
2078 tb->lines_list[i] = NULL;
2080 tb->dirty_flags |= DIRTY_ALL;
2081 tb->dirty_flags |= DIRTY_EXPRESSION;
2086 for (i = j = k = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
2090 if (iskanji(tb->lines_list[tb->cy][i]))
2091 buf[j++] = tb->lines_list[tb->cy][i++];
2093 buf[j++] = tb->lines_list[tb->cy][i];
2102 for (; tb->lines_list[tb->cy][i]; i++)
2104 buf[j++] = tb->lines_list[tb->cy][i];
2108 string_free(tb->lines_list[tb->cy]);
2109 tb->lines_list[tb->cy] = string_make(buf);
2110 tb->dirty_line = tb->cy;
2111 check_expression_line(tb, tb->cy);
2118 tb->dirty_flags |= DIRTY_SCREEN;
2119 search_dir = get_string_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str);
2121 if (!search_dir) break;
2123 if (search_dir == 1) do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
2124 else do_editor_command(player_ptr, tb, EC_SEARCH_BACK);
2127 case EC_SEARCH_FORW:
2128 if (tb->search_o_ptr)
2130 search_for_object(player_ptr, tb, tb->search_o_ptr, TRUE);
2134 if (tb->search_str && tb->search_str[0])
2136 search_for_string(tb, tb->search_str, TRUE);
2140 tb->dirty_flags |= DIRTY_NO_SEARCH;
2143 case EC_SEARCH_BACK:
2144 if (tb->search_o_ptr)
2146 search_for_object(player_ptr, tb, tb->search_o_ptr, FALSE);
2150 if (tb->search_str && tb->search_str[0])
2152 search_for_string(tb, tb->search_str, FALSE);
2156 tb->dirty_flags |= DIRTY_NO_SEARCH;
2160 tb->dirty_flags |= DIRTY_SCREEN;
2162 if (!get_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str)) break;
2164 do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
2167 case EC_SEARCH_DESTROYED:
2168 if (!get_destroyed_object_for_search(player_ptr, &tb->search_o_ptr, &tb->search_str))
2170 tb->dirty_flags |= DIRTY_NO_SEARCH;
2174 do_editor_command(player_ptr, tb, EC_SEARCH_FORW);
2177 case EC_INSERT_OBJECT:
2179 autopick_type an_entry, *entry = &an_entry;
2180 if (!entry_from_choosed_object(player_ptr, entry))
2182 tb->dirty_flags |= DIRTY_SCREEN;
2187 insert_return_code(tb);
2188 string_free(tb->lines_list[tb->cy]);
2189 tb->lines_list[tb->cy] = autopick_line_from_entry_kill(entry);
2190 tb->dirty_flags |= DIRTY_SCREEN;
2193 case EC_INSERT_DESTROYED:
2194 if (!tb->last_destroyed) break;
2197 insert_return_code(tb);
2198 string_free(tb->lines_list[tb->cy]);
2199 tb->lines_list[tb->cy] = string_make(tb->last_destroyed);
2200 tb->dirty_flags |= DIRTY_ALL;
2204 case EC_INSERT_BLOCK:
2206 char expression[80];
2207 sprintf(expression, "?:[AND [EQU $RACE %s] [EQU $CLASS %s] [GEQ $LEVEL %02d]]",
2209 rp_ptr->E_title, cp_ptr->E_title,
2211 rp_ptr->title, cp_ptr->title,
2215 insert_return_code(tb);
2216 string_free(tb->lines_list[tb->cy]);
2217 tb->lines_list[tb->cy] = string_make(expression);
2219 insert_return_code(tb);
2220 string_free(tb->lines_list[tb->cy]);
2221 tb->lines_list[tb->cy] = string_make("?:1");
2222 tb->dirty_flags |= DIRTY_ALL;
2227 case EC_INSERT_MACRO:
2228 draw_text_editor(player_ptr, tb);
2229 Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
2230 Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW, _("P:<トリガーキー>: ", "P:<Trigger key>: "));
2231 if (!insert_macro_line(tb)) break;
2234 tb->dirty_flags |= DIRTY_ALL;
2238 case EC_INSERT_KEYMAP:
2239 draw_text_editor(player_ptr, tb);
2240 Term_erase(0, tb->cy - tb->upper + 1, tb->wid);
2241 Term_putstr(0, tb->cy - tb->upper + 1, tb->wid - 1, TERM_YELLOW,
2242 format(_("C:%d:<コマンドキー>: ", "C:%d:<Keypress>: "), (rogue_like_commands ? KEYMAP_MODE_ROGUE : KEYMAP_MODE_ORIG)));
2244 if (!insert_keymap_line(tb)) break;
2247 tb->dirty_flags |= DIRTY_ALL;
2251 case EC_CL_AUTOPICK: toggle_command_letter(tb, DO_AUTOPICK); break;
2252 case EC_CL_DESTROY: toggle_command_letter(tb, DO_AUTODESTROY); break;
2253 case EC_CL_LEAVE: toggle_command_letter(tb, DONT_AUTOPICK); break;
2254 case EC_CL_QUERY: toggle_command_letter(tb, DO_QUERY_AUTOPICK); break;
2255 case EC_CL_NO_DISP: toggle_command_letter(tb, DO_DISPLAY); break;
2257 case EC_IK_UNAWARE: toggle_keyword(tb, FLG_UNAWARE); break;
2258 case EC_IK_UNIDENTIFIED: toggle_keyword(tb, FLG_UNIDENTIFIED); break;
2259 case EC_IK_IDENTIFIED: toggle_keyword(tb, FLG_IDENTIFIED); break;
2260 case EC_IK_STAR_IDENTIFIED: toggle_keyword(tb, FLG_STAR_IDENTIFIED); break;
2261 case EC_KK_WEAPONS: toggle_keyword(tb, FLG_WEAPONS); break;
2262 case EC_KK_FAVORITE_WEAPONS: toggle_keyword(tb, FLG_FAVORITE_WEAPONS); break;
2263 case EC_KK_ARMORS: toggle_keyword(tb, FLG_ARMORS); break;
2264 case EC_KK_MISSILES: toggle_keyword(tb, FLG_MISSILES); break;
2265 case EC_KK_DEVICES: toggle_keyword(tb, FLG_DEVICES); break;
2266 case EC_KK_LIGHTS: toggle_keyword(tb, FLG_LIGHTS); break;
2267 case EC_KK_JUNKS: toggle_keyword(tb, FLG_JUNKS); break;
2268 case EC_KK_CORPSES: toggle_keyword(tb, FLG_CORPSES); break;
2269 case EC_KK_SPELLBOOKS: toggle_keyword(tb, FLG_SPELLBOOKS); break;
2270 case EC_KK_SHIELDS: toggle_keyword(tb, FLG_SHIELDS); break;
2271 case EC_KK_BOWS: toggle_keyword(tb, FLG_BOWS); break;
2272 case EC_KK_RINGS: toggle_keyword(tb, FLG_RINGS); break;
2273 case EC_KK_AMULETS: toggle_keyword(tb, FLG_AMULETS); break;
2274 case EC_KK_SUITS: toggle_keyword(tb, FLG_SUITS); break;
2275 case EC_KK_CLOAKS: toggle_keyword(tb, FLG_CLOAKS); break;
2276 case EC_KK_HELMS: toggle_keyword(tb, FLG_HELMS); break;
2277 case EC_KK_GLOVES: toggle_keyword(tb, FLG_GLOVES); break;
2278 case EC_KK_BOOTS: toggle_keyword(tb, FLG_BOOTS); break;
2279 case EC_OK_COLLECTING: toggle_keyword(tb, FLG_COLLECTING); break;
2280 case EC_OK_BOOSTED: toggle_keyword(tb, FLG_BOOSTED); break;
2281 case EC_OK_MORE_DICE: toggle_keyword(tb, FLG_MORE_DICE); break;
2282 case EC_OK_MORE_BONUS: toggle_keyword(tb, FLG_MORE_BONUS); break;
2283 case EC_OK_WORTHLESS: toggle_keyword(tb, FLG_WORTHLESS); break;
2284 case EC_OK_ARTIFACT: toggle_keyword(tb, FLG_ARTIFACT); break;
2285 case EC_OK_EGO: toggle_keyword(tb, FLG_EGO); break;
2286 case EC_OK_GOOD: toggle_keyword(tb, FLG_GOOD); break;
2287 case EC_OK_NAMELESS: toggle_keyword(tb, FLG_NAMELESS); break;
2288 case EC_OK_AVERAGE: toggle_keyword(tb, FLG_AVERAGE); break;
2289 case EC_OK_RARE: toggle_keyword(tb, FLG_RARE); break;
2290 case EC_OK_COMMON: toggle_keyword(tb, FLG_COMMON); break;
2291 case EC_OK_WANTED: toggle_keyword(tb, FLG_WANTED); break;
2292 case EC_OK_UNIQUE: toggle_keyword(tb, FLG_UNIQUE); break;
2293 case EC_OK_HUMAN: toggle_keyword(tb, FLG_HUMAN); break;
2294 case EC_OK_UNREADABLE:
2295 toggle_keyword(tb, FLG_UNREADABLE);
2296 add_keyword(tb, FLG_SPELLBOOKS);
2299 toggle_keyword(tb, FLG_REALM1);
2300 add_keyword(tb, FLG_SPELLBOOKS);
2303 toggle_keyword(tb, FLG_REALM2);
2304 add_keyword(tb, FLG_SPELLBOOKS);
2307 toggle_keyword(tb, FLG_FIRST);
2308 add_keyword(tb, FLG_SPELLBOOKS);
2311 toggle_keyword(tb, FLG_SECOND);
2312 add_keyword(tb, FLG_SPELLBOOKS);
2315 toggle_keyword(tb, FLG_THIRD);
2316 add_keyword(tb, FLG_SPELLBOOKS);
2319 toggle_keyword(tb, FLG_FOURTH);
2320 add_keyword(tb, FLG_SPELLBOOKS);
2324 tb->old_com_id = com_id;
2330 * Insert single letter at cursor position.
2332 static void insert_single_letter(text_body_type *tb, int key)
2335 char buf[MAX_LINELEN];
2337 for (i = j = 0; tb->lines_list[tb->cy][i] && i < tb->cx; i++)
2339 buf[j++] = tb->lines_list[tb->cy][i];
2349 if (j + 2 < MAX_LINELEN)
2351 buf[j++] = (char)key;
2352 buf[j++] = (char)next;
2361 if (j + 1 < MAX_LINELEN)
2362 buf[j++] = (char)key;
2366 for (; tb->lines_list[tb->cy][i] && j + 1 < MAX_LINELEN; i++)
2367 buf[j++] = tb->lines_list[tb->cy][i];
2370 string_free(tb->lines_list[tb->cy]);
2371 tb->lines_list[tb->cy] = string_make(buf);
2372 len = strlen(tb->lines_list[tb->cy]);
2373 if (len < tb->cx) tb->cx = len;
2375 tb->dirty_line = tb->cy;
2376 check_expression_line(tb, tb->cy);
2382 * Check special key code and get a movement command id
2384 static int analyze_move_key(text_body_type *tb, int skey)
2387 if (!(skey & SKEY_MASK)) return 0;
2389 switch (skey & ~SKEY_MOD_MASK)
2391 case SKEY_DOWN: com_id = EC_DOWN; break;
2392 case SKEY_LEFT: com_id = EC_LEFT; break;
2393 case SKEY_RIGHT: com_id = EC_RIGHT; break;
2394 case SKEY_UP: com_id = EC_UP; break;
2395 case SKEY_PGUP: com_id = EC_PGUP; break;
2396 case SKEY_PGDOWN: com_id = EC_PGDOWN; break;
2397 case SKEY_TOP: com_id = EC_TOP; break;
2398 case SKEY_BOTTOM: com_id = EC_BOTTOM; break;
2403 if (!(skey & SKEY_MOD_SHIFT))
2406 * Un-shifted cursor keys cancells
2407 * selection created by shift+cursor.
2409 if (tb->mark & MARK_BY_SHIFT)
2412 tb->dirty_flags |= DIRTY_ALL;
2418 if (tb->mark) return com_id;
2420 int len = strlen(tb->lines_list[tb->cy]);
2421 tb->mark = MARK_MARK | MARK_BY_SHIFT;
2424 if (tb->cx > len) tb->mx = len;
2426 if (com_id == EC_UP || com_id == EC_DOWN)
2428 tb->dirty_flags |= DIRTY_ALL;
2432 tb->dirty_line = tb->cy;
2439 * In-game editor of Object Auto-picker/Destoryer
2440 * @param player_ptr プレーヤーへの参照ポインタ
2442 void do_cmd_edit_autopick(player_type *player_ptr)
2444 static int cx_save = 0;
2445 static int cy_save = 0;
2446 text_body_type text_body, *tb = &text_body;
2447 autopick_type an_entry, *entry = &an_entry;
2448 char buf[MAX_LINELEN];
2451 static s32b old_autosave_turn = 0L;
2454 tb->changed = FALSE;
2457 tb->upper = tb->left = 0;
2459 tb->mx = tb->my = 0;
2460 tb->old_cy = tb->old_upper = tb->old_left = -1;
2461 tb->old_wid = tb->old_hgt = -1;
2465 tb->search_o_ptr = NULL;
2466 tb->search_str = NULL;
2467 tb->last_destroyed = NULL;
2468 tb->dirty_flags = DIRTY_ALL | DIRTY_MODE | DIRTY_EXPRESSION;
2469 tb->dirty_line = -1;
2470 tb->filename_mode = PT_DEFAULT;
2472 if (current_world_ptr->game_turn < old_autosave_turn)
2474 while (old_autosave_turn > current_world_ptr->game_turn) old_autosave_turn -= TURNS_PER_TICK * TOWN_DAWN;
2477 if (current_world_ptr->game_turn > old_autosave_turn + 100L)
2479 do_cmd_save_game(player_ptr, TRUE);
2480 old_autosave_turn = current_world_ptr->game_turn;
2485 if (autopick_last_destroyed_object.k_idx)
2487 autopick_entry_from_object(player_ptr, entry, &autopick_last_destroyed_object);
2488 tb->last_destroyed = autopick_line_from_entry_kill(entry);
2491 tb->lines_list = read_pickpref_text_lines(player_ptr, &tb->filename_mode);
2492 for (i = 0; i < tb->cy; i++)
2494 if (!tb->lines_list[i])
2496 tb->cy = tb->cx = 0;
2505 draw_text_editor(player_ptr, tb);
2506 prt(_("(^Q:終了 ^W:セーブして終了, ESC:メニュー, その他:入力)",
2507 "(^Q:Quit, ^W:Save&Quit, ESC:Menu, Other:Input text)"), 0, 0);
2510 prt(format("(%d,%d)", tb->cx, tb->cy), 0, 60);
2514 prt(format("(%d,%d)-(%d,%d)", tb->mx, tb->my, tb->cx, tb->cy), 0, 60);
2517 Term_gotoxy(tb->cx - tb->left, tb->cy - tb->upper + 1);
2518 tb->dirty_flags = 0;
2519 tb->dirty_line = -1;
2520 tb->old_cy = tb->cy;
2521 tb->old_upper = tb->upper;
2522 tb->old_left = tb->left;
2523 tb->old_wid = tb->wid;
2524 tb->old_hgt = tb->hgt;
2526 key = inkey_special(TRUE);
2528 if (key & SKEY_MASK)
2530 com_id = analyze_move_key(tb, key);
2532 else if (key == ESCAPE)
2534 com_id = do_command_menu(0, 0);
2535 tb->dirty_flags |= DIRTY_SCREEN;
2537 else if (!iscntrl((unsigned char)key))
2542 tb->dirty_flags |= DIRTY_ALL;
2545 insert_single_letter(tb, key);
2550 com_id = get_com_id((char)key);
2553 if (com_id) quit = do_editor_command(player_ptr, tb, com_id);
2557 strcpy(buf, pickpref_filename(player_ptr, tb->filename_mode));
2559 if (quit == QUIT_AND_SAVE)
2560 write_text_lines(buf, tb->lines_list);
2562 free_text_lines(tb->lines_list);
2563 string_free(tb->search_str);
2564 string_free(tb->last_destroyed);
2565 kill_yank_chain(tb);
2567 process_autopick_file(player_ptr, buf);
2568 current_world_ptr->start_time = (u32b)time(NULL);