2 * @brief スナイパー技能の実装 / Sniping
5 * 2014 Deskull rearranged comment for Doxygen.\n
8 #include "mind/mind-sniper.h"
9 #include "action/action-limited.h"
10 #include "cmd-action/cmd-shoot.h"
11 #include "core/asking-player.h"
12 #include "core/player-redraw-types.h"
13 #include "core/player-update-types.h"
14 #include "core/stuff-handler.h"
15 #include "core/window-redrawer.h"
16 #include "game-option/text-display-options.h"
17 #include "grid/grid.h"
18 #include "inventory/inventory-slot-types.h"
19 #include "io/command-repeater.h"
20 #include "io/input-key-requester.h"
21 #include "main/sound-definitions-table.h"
22 #include "main/sound-of-music.h"
23 #include "mind/snipe-types.h"
24 #include "monster-race/race-flags-resistance.h"
25 #include "monster-race/race-flags3.h"
26 #include "monster-race/monster-race.h"
27 #include "player/player-status.h"
28 #include "system/object-type-definition.h"
29 #include "term/screen-processor.h"
30 #include "term/term-color-types.h"
31 #include "util/buffer-shaper.h"
32 #include "util/int-char-converter.h"
33 #include "view/display-messages.h"
35 #define MAX_SNIPE_POWERS 16
38 typedef struct snipe_power
45 /*! スナイパー技能の解説メッセージ */
46 static concptr const snipe_tips[MAX_SNIPE_POWERS] =
49 "精神を集中する。射撃の威力、精度が上がり、高度な射撃術が使用できるようになる。",
50 "光る矢を放つ。光に弱いモンスターに威力を発揮する。",
51 "射撃を行った後、短距離の瞬間移動を行う。",
52 "軌道上の罠をすべて無効にする低空飛行の矢を放つ。",
54 "壁を粉砕する矢を放つ。岩でできたモンスターと無生物のモンスターに威力を発揮する。",
58 "善良なモンスターに威力を発揮する矢を放つ。",
59 "邪悪なモンスターに威力を発揮する矢を放つ。",
63 "敵の急所にめがけて矢を放つ。成功すると敵を一撃死させる。失敗すると1ダメージ。",
64 "全てのモンスターに高威力を発揮する矢を放つ。反動による副次効果を受ける。",
66 "Concentrate your mind fot shooting.",
67 "Shot an allow with brightness.",
68 "Blink after shooting.",
69 "Shot an allow able to shatter traps.",
70 "Deals extra damege of fire.",
71 "Shot an allow able to shatter rocks.",
72 "Deals extra damege of ice.",
73 "An allow rushes away a target.",
74 "An allow pierces some monsters.",
75 "Deals more damage to good monsters.",
76 "Deals more damage to evil monsters.",
77 "An allow explodes when it hits a monster.",
79 "Deals extra damege of lightning.",
80 "Deals quick death or 1 damage.",
81 "Deals great damage to all monsters, and some side effects to you.",
86 static snipe_power const snipe_powers[MAX_SNIPE_POWERS] =
88 /* Level gained, cost, name */
92 { 3, 1, "シュート&アウェイ" },
102 { 32, 4, "ダブルショット" },
103 { 36, 3, "プラズマボルト" },
104 { 40, 3, "ニードルショット" },
105 { 48, 7, "セイントスターアロー" },
107 { 1, 0, "Concentration" },
108 { 2, 1, "Flush Arrow" },
109 { 3, 1, "Shoot & Away" },
110 { 5, 1, "Disarm Shot" },
111 { 8, 2, "Fire Shot" },
112 { 10, 2, "Shatter Arrow" },
113 { 13, 2, "Ice Shot" },
114 { 18, 2, "Rushing Arrow"},
115 { 22, 3, "Piercing Shot" },
116 { 25, 4, "Evil Shot"},
117 { 26, 4, "Holy Shot" },
119 { 32, 4, "Double Shot" },
120 { 36, 3, "Plasma Bolt" },
121 { 40, 3, "Needle Shot" },
122 { 48, 7, "Saint Stars Arrow" },
130 static bool snipe_concentrate(player_type *creature_ptr)
132 if ((int)creature_ptr->concent < (2 + (creature_ptr->lev + 5) / 10)) creature_ptr->concent++;
134 msg_format(_("集中した。(集中度 %d)", "You concentrate deeply. (lvl %d)"), creature_ptr->concent);
135 creature_ptr->reset_concent = FALSE;
137 creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
138 creature_ptr->redraw |= (PR_STATUS);
143 * @brief スナイパーの集中度リセット
144 * @param msg TRUEならばメッセージを表示する
147 void reset_concentration(player_type *creature_ptr, bool msg)
151 msg_print(_("集中力が途切れてしまった。", "Stop concentrating."));
154 creature_ptr->concent = 0;
155 creature_ptr->reset_concent = FALSE;
157 creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
158 creature_ptr->redraw |= (PR_STATUS);
162 * @brief スナイパーの集中度によるダメージボーナスを加算する
163 * @param tdam 算出中のダメージ
164 * @return 集中度修正を加えたダメージ
166 int boost_concentration_damage(player_type *creature_ptr, int tdam)
168 tdam *= (10 + creature_ptr->concent);
175 * @brief スナイパーの技能リストを表示する
178 void display_snipe_list(player_type *sniper_ptr)
183 PLAYER_LEVEL plev = sniper_ptr->lev;
187 /* Display a list of spells */
189 put_str(_("名前", "Name"), y, x + 5);
190 put_str(_("Lv MP", "Lv Mana"), y, x + 35);
192 for (i = 0; i < MAX_SNIPE_POWERS; i++)
194 /* Access the available spell */
195 spell = snipe_powers[i];
196 if (spell.min_lev > plev) continue;
198 sprintf(psi_desc, " %c) %-30s%2d %4d",
199 I2A(i), spell.name, spell.min_lev, spell.mana_cost);
201 if (spell.mana_cost > (int)sniper_ptr->concent)
202 term_putstr(x, y + i + 1, -1, TERM_SLATE, psi_desc);
204 term_putstr(x, y + i + 1, -1, TERM_WHITE, psi_desc);
211 * @brief スナイパー技能を選択する
212 * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
213 * @param only_browse 一覧を見るだけの場合TRUEを返す
214 * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
215 * Allow user to choose a mindcrafter power.\n
217 * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
218 * If the user hits escape, returns FALSE, and set '*sn' to -1\n
219 * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
221 * The "prompt" should be "cast", "recite", or "study"\n
222 * The "known" should be TRUE for cast/pray, FALSE for study\n
224 * nb: This function has a (trivial) display bug which will be obvious\n
225 * when you run it. It's probably easy to fix but I haven't tried,\n
228 static int get_snipe_power(player_type *sniper_ptr, COMMAND_CODE *sn, bool only_browse)
234 PLAYER_LEVEL plev = sniper_ptr->lev;
238 concptr p = _("射撃術", "power");
244 /* Assume cancelled */
247 /* Repeat previous command */
248 /* Get the spell, if available */
251 /* Verify the spell */
252 if ((snipe_powers[*sn].min_lev <= plev) && (snipe_powers[*sn].mana_cost <= (int)sniper_ptr->concent))
262 for (i = 0; i < MAX_SNIPE_POWERS; i++)
264 if ((snipe_powers[i].min_lev <= plev) &&
265 ((only_browse) || (snipe_powers[i].mana_cost <= (int)sniper_ptr->concent)))
271 /* Build a prompt (accept all spells) */
274 (void)strnfmt(out_val, 78,
275 _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
276 p, I2A(0), I2A(num), p);
280 (void)strnfmt(out_val, 78,
281 _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
282 p, I2A(0), I2A(num), p);
285 choice = always_show_list ? ESCAPE : 1;
288 if(choice == ESCAPE) choice = ' ';
289 else if( !get_com(out_val, &choice, FALSE) )break;
292 if ((choice == ' ') || (choice == '*') || (choice == '?'))
300 if (!only_browse) screen_save();
302 /* Display a list of spells */
304 put_str(_("名前", "Name"), y, x + 5);
305 put_str(_("Lv 集中度", "Lv Pow"), y, x + 35);
307 /* Dump the spells */
308 for (i = 0; i < MAX_SNIPE_POWERS; i++)
310 term_color_type tcol = TERM_WHITE;
311 term_erase(x, y + i + 1, 255);
313 /* Access the spell */
314 spell = snipe_powers[i];
316 /* Dump the spell --(-- */
317 if (spell.min_lev > plev)
318 sprintf(psi_index, " ) ");
320 sprintf(psi_index, " %c) ", I2A(i));
322 sprintf(psi_desc, "%-30s%2d %4d",
323 spell.name, spell.min_lev, spell.mana_cost);
325 if (spell.min_lev > plev)
327 else if (spell.mana_cost > (int)sniper_ptr->concent)
330 term_putstr(x, y + i + 1, -1, tcol, psi_index);
331 term_putstr(x + 5, y + i + 1, -1, tcol, psi_desc);
334 /* Clear the bottom line */
335 prt("", y + i + 1, x);
343 if (!only_browse) screen_load();
351 ask = isupper(choice);
354 if (ask) choice = (char)tolower(choice);
356 /* Extract request */
357 i = (islower(choice) ? A2I(choice) : -1);
359 /* Totally Illegal */
360 if ((i < 0) || (i > num) ||
361 (!only_browse &&(snipe_powers[i].mana_cost > (int)sniper_ptr->concent)))
367 /* Save the spell index */
368 spell = snipe_powers[i];
376 (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
378 /* Belay that order */
379 if (!get_check(tmp_val)) continue;
385 if (redraw && !only_browse) screen_load();
387 sniper_ptr->window |= (PW_SPELL);
388 handle_stuff(sniper_ptr);
390 /* Abort if needed */
391 if (!flag) return FALSE;
393 /* Save the choice */
404 * @brief スナイバー技能のスレイ倍率計算を行う /
405 * Calcurate magnification of snipe technics
406 * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
407 * @param m_ptr 目標となるモンスターの構造体参照ポインタ
408 * @return スレイの倍率(/10倍)
410 MULTIPLY calc_snipe_damage_with_slay(player_type *sniper_ptr, MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
412 monster_race *r_ptr = &r_info[m_ptr->r_idx];
413 bool seen = is_seen(sniper_ptr, m_ptr);
418 if (r_ptr->flags3 & (RF3_HURT_LITE))
420 MULTIPLY n = 20 + sniper_ptr->concent;
421 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
422 if (mult < n) mult = n;
426 if (r_ptr->flagsr & RFR_IM_FIRE)
428 if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
432 MULTIPLY n = 15 + (sniper_ptr->concent * 3);
433 if (mult < n) mult = n;
437 if (r_ptr->flagsr & RFR_IM_COLD)
439 if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
443 MULTIPLY n = 15 + (sniper_ptr->concent * 3);
444 if (mult < n) mult = n;
448 if (r_ptr->flagsr & RFR_IM_ELEC)
450 if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
454 MULTIPLY n = 18 + (sniper_ptr->concent * 4);
455 if (mult < n) mult = n;
459 if (r_ptr->flags3 & RF3_HURT_ROCK)
461 MULTIPLY n = 15 + (sniper_ptr->concent * 2);
462 if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
463 if (mult < n) mult = n;
465 else if (r_ptr->flags3 & RF3_NONLIVING)
467 MULTIPLY n = 15 + (sniper_ptr->concent * 2);
468 if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
469 if (mult < n) mult = n;
473 if (r_ptr->flags3 & RF3_GOOD)
475 MULTIPLY n = 15 + (sniper_ptr->concent * 4);
476 if (seen) r_ptr->r_flags3 |= RF3_GOOD;
477 if (mult < n) mult = n;
481 if (r_ptr->flags3 & RF3_EVIL)
483 MULTIPLY n = 12 + (sniper_ptr->concent * 3);
484 if (seen) r_ptr->r_flags3 |= RF3_EVIL;
485 if (r_ptr->flags3 & (RF3_HURT_LITE))
487 n += (sniper_ptr->concent * 3);
488 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
490 if (mult < n) mult = n;
494 if (mult < 50) mult = 50;
503 * @brief スナイパー技能の発動 /
504 * do_cmd_cast calls this function if the player's class is 'snipe'.
505 * @param spell 発動する特殊技能のID
506 * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
508 static bool cast_sniper_spell(player_type *sniper_ptr, int spell)
510 object_type *o_ptr = &sniper_ptr->inventory_list[INVEN_BOW];
511 SPELL_IDX snipe_type = SP_NONE;
513 if (o_ptr->tval != TV_BOW)
515 msg_print(_("弓を装備していない!", "You wield no bow!"));
522 case 0: /* Concentration */
523 if (!snipe_concentrate(sniper_ptr)) return FALSE;
524 take_turn(sniper_ptr, 100);
526 case 1: snipe_type = SP_LITE; break;
527 case 2: snipe_type = SP_AWAY; break;
528 case 3: snipe_type = SP_KILL_TRAP; break;
529 case 4: snipe_type = SP_FIRE; break;
530 case 5: snipe_type = SP_KILL_WALL; break;
531 case 6: snipe_type = SP_COLD; break;
532 case 7: snipe_type = SP_RUSH; break;
533 case 8: snipe_type = SP_PIERCE; break;
534 case 9: snipe_type = SP_EVILNESS; break;
535 case 10: snipe_type = SP_HOLYNESS; break;
536 case 11: snipe_type = SP_EXPLODE; break;
537 case 12: snipe_type = SP_DOUBLE; break;
538 case 13: snipe_type = SP_ELEC; break;
539 case 14: snipe_type = SP_NEEDLE; break;
540 case 15: snipe_type = SP_FINAL; break;
542 msg_print(_("なに?", "Zap?"));
546 do_cmd_fire(sniper_ptr, snipe_type);
548 return (sniper_ptr->is_fired);
552 * @brief スナイパー技能コマンドのメインルーチン /
555 void do_cmd_snipe(player_type *sniper_ptr)
560 if(cmd_limit_confused(sniper_ptr)) return;
561 if(cmd_limit_image(sniper_ptr)) return;
562 if(cmd_limit_stun(sniper_ptr)) return;
564 if (!get_snipe_power(sniper_ptr, &n, FALSE)) return;
567 cast = cast_sniper_spell(sniper_ptr, n);
570 sniper_ptr->redraw |= (PR_HP | PR_MANA);
571 sniper_ptr->window |= (PW_PLAYER);
572 sniper_ptr->window |= (PW_SPELL);
576 * @brief スナイパー技能コマンドの表示 /
579 void do_cmd_snipe_browse(player_type *sniper_ptr)
589 if (!get_snipe_power(sniper_ptr, &n, TRUE))
595 /* Clear lines, position cursor (really should use strlen here) */
596 term_erase(12, 22, 255);
597 term_erase(12, 21, 255);
598 term_erase(12, 20, 255);
599 term_erase(12, 19, 255);
600 term_erase(12, 18, 255);
602 shape_buffer(snipe_tips[n], 62, temp, sizeof(temp));
603 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
605 prt(&temp[j], line, 15);