3 * @brief スナイパー技能の実装 / Sniping
6 * 2014 Deskull rearranged comment for Doxygen.\n
12 #include "main/sound-definitions-table.h"
15 #include "player-status.h"
16 #include "cmd-basic.h"
18 #include "monsterrace.h"
19 #include "view/display-main-window.h"
21 #define MAX_SNIPE_POWERS 16
24 typedef struct snipe_power
31 /*! スナイパー技能の解説メッセージ */
32 static concptr const snipe_tips[MAX_SNIPE_POWERS] =
35 "精神を集中する。射撃の威力、精度が上がり、高度な射撃術が使用できるようになる。",
36 "光る矢を放つ。光に弱いモンスターに威力を発揮する。",
37 "射撃を行った後、短距離の瞬間移動を行う。",
38 "軌道上の罠をすべて無効にする低空飛行の矢を放つ。",
40 "壁を粉砕する矢を放つ。岩でできたモンスターと無生物のモンスターに威力を発揮する。",
44 "善良なモンスターに威力を発揮する矢を放つ。",
45 "邪悪なモンスターに威力を発揮する矢を放つ。",
49 "敵の急所にめがけて矢を放つ。成功すると敵を一撃死させる。失敗すると1ダメージ。",
50 "全てのモンスターに高威力を発揮する矢を放つ。反動による副次効果を受ける。",
52 "Concentrate your mind fot shooting.",
53 "Shot an allow with brightness.",
54 "Blink after shooting.",
55 "Shot an allow able to shatter traps.",
56 "Deals extra damege of fire.",
57 "Shot an allow able to shatter rocks.",
58 "Deals extra damege of ice.",
59 "An allow rushes away a target.",
60 "An allow pierces some monsters.",
61 "Deals more damage to good monsters.",
62 "Deals more damage to evil monsters.",
63 "An allow explodes when it hits a monster.",
65 "Deals extra damege of lightning.",
66 "Deals quick death or 1 damage.",
67 "Deals great damage to all monsters, and some side effects to you.",
72 static snipe_power const snipe_powers[MAX_SNIPE_POWERS] =
74 /* Level gained, cost, name */
78 { 3, 1, "シュート&アウェイ" },
90 { 40, 3, "ニードルショット" },
91 { 48, 7, "セイントスターアロー" },
93 { 1, 0, "Concentration" },
94 { 2, 1, "Flush Arrow" },
95 { 3, 1, "Shoot & Away" },
96 { 5, 1, "Disarm Shot" },
97 { 8, 2, "Fire Shot" },
98 { 10, 2, "Shatter Arrow" },
99 { 13, 2, "Ice Shot" },
100 { 18, 2, "Rushing Arrow"},
101 { 22, 3, "Piercing Shot" },
102 { 25, 4, "Evil Shot"},
103 { 26, 4, "Holy Shot" },
105 { 32, 4, "Double Shot" },
106 { 36, 3, "Plasma Bolt" },
107 { 40, 3, "Needle Shot" },
108 { 48, 7, "Saint Stars Arrow" },
116 static bool snipe_concentrate(player_type *creature_ptr)
118 if ((int)creature_ptr->concent < (2 + (creature_ptr->lev + 5) / 10)) creature_ptr->concent++;
120 msg_format(_("集中した。(集中度 %d)", "You concentrate deeply. (lvl %d)"), creature_ptr->concent);
121 creature_ptr->reset_concent = FALSE;
123 creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
124 creature_ptr->redraw |= (PR_STATUS);
129 * @brief スナイパーの集中度リセット
130 * @param msg TRUEならばメッセージを表示する
133 void reset_concentration(player_type *creature_ptr, bool msg)
137 msg_print(_("集中力が途切れてしまった。", "Stop concentrating."));
140 creature_ptr->concent = 0;
141 creature_ptr->reset_concent = FALSE;
143 creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
144 creature_ptr->redraw |= (PR_STATUS);
148 * @brief スナイパーの集中度によるダメージボーナスを加算する
149 * @param tdam 算出中のダメージ
150 * @return 集中度修正を加えたダメージ
152 int boost_concentration_damage(player_type *creature_ptr, int tdam)
154 tdam *= (10 + creature_ptr->concent);
161 * @brief スナイパーの技能リストを表示する
164 void display_snipe_list(player_type *sniper_ptr)
169 PLAYER_LEVEL plev = sniper_ptr->lev;
173 /* Display a list of spells */
175 put_str(_("名前", "Name"), y, x + 5);
176 put_str(_("Lv MP", "Lv Mana"), y, x + 35);
178 for (i = 0; i < MAX_SNIPE_POWERS; i++)
180 /* Access the available spell */
181 spell = snipe_powers[i];
182 if (spell.min_lev > plev) continue;
183 if (spell.mana_cost > (int)sniper_ptr->concent) continue;
185 sprintf(psi_desc, " %c) %-30s%2d %4d",
186 I2A(i), spell.name, spell.min_lev, spell.mana_cost);
188 Term_putstr(x, y + i + 1, -1, TERM_WHITE, psi_desc);
195 * @brief スナイパー技能を選択する
196 * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
197 * @param only_browse 一覧を見るだけの場合TRUEを返す
198 * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
199 * Allow user to choose a mindcrafter power.\n
201 * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
202 * If the user hits escape, returns FALSE, and set '*sn' to -1\n
203 * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
205 * The "prompt" should be "cast", "recite", or "study"\n
206 * The "known" should be TRUE for cast/pray, FALSE for study\n
208 * nb: This function has a (trivial) display bug which will be obvious\n
209 * when you run it. It's probably easy to fix but I haven't tried,\n
212 static int get_snipe_power(player_type *sniper_ptr, COMMAND_CODE *sn, bool only_browse)
218 PLAYER_LEVEL plev = sniper_ptr->lev;
222 concptr p = _("射撃術", "power");
228 /* Assume cancelled */
231 /* Repeat previous command */
232 /* Get the spell, if available */
235 /* Verify the spell */
236 if ((snipe_powers[*sn].min_lev <= plev) && (snipe_powers[*sn].mana_cost <= (int)sniper_ptr->concent))
246 for (i = 0; i < MAX_SNIPE_POWERS; i++)
248 if ((snipe_powers[i].min_lev <= plev) &&
249 ((only_browse) || (snipe_powers[i].mana_cost <= (int)sniper_ptr->concent)))
255 /* Build a prompt (accept all spells) */
258 (void)strnfmt(out_val, 78,
259 _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
260 p, I2A(0), I2A(num), p);
264 (void)strnfmt(out_val, 78,
265 _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
266 p, I2A(0), I2A(num), p);
269 choice = always_show_list ? ESCAPE : 1;
272 if(choice == ESCAPE) choice = ' ';
273 else if( !get_com(out_val, &choice, FALSE) )break;
276 if ((choice == ' ') || (choice == '*') || (choice == '?'))
283 if (!only_browse) screen_save();
285 /* Display a list of spells */
287 put_str(_("名前", "Name"), y, x + 5);
288 if (only_browse) put_str(_("Lv 集中度", "Lv Pow"), y, x + 35);
290 /* Dump the spells */
291 for (i = 0; i < MAX_SNIPE_POWERS; i++)
293 Term_erase(x, y + i + 1, 255);
295 /* Access the spell */
296 spell = snipe_powers[i];
297 if (spell.min_lev > plev) continue;
298 if (!only_browse && (spell.mana_cost > (int)sniper_ptr->concent)) continue;
300 /* Dump the spell --(-- */
302 sprintf(psi_desc, " %c) %-30s%2d %4d",
303 I2A(i), spell.name, spell.min_lev, spell.mana_cost);
305 sprintf(psi_desc, " %c) %-30s", I2A(i), spell.name);
306 prt(psi_desc, y + i + 1, x);
309 /* Clear the bottom line */
310 prt("", y + i + 1, x);
318 if (!only_browse) screen_load();
326 ask = isupper(choice);
329 if (ask) choice = (char)tolower(choice);
331 /* Extract request */
332 i = (islower(choice) ? A2I(choice) : -1);
334 /* Totally Illegal */
335 if ((i < 0) || (i > num) ||
336 (!only_browse &&(snipe_powers[i].mana_cost > (int)sniper_ptr->concent)))
342 /* Save the spell index */
343 spell = snipe_powers[i];
351 (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
353 /* Belay that order */
354 if (!get_check(tmp_val)) continue;
360 if (redraw && !only_browse) screen_load();
362 sniper_ptr->window |= (PW_SPELL);
363 handle_stuff(sniper_ptr);
365 /* Abort if needed */
366 if (!flag) return FALSE;
368 /* Save the choice */
379 * @brief スナイバー技能のスレイ倍率計算を行う /
380 * Calcurate magnification of snipe technics
381 * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
382 * @param m_ptr 目標となるモンスターの構造体参照ポインタ
383 * @return スレイの倍率(/10倍)
385 MULTIPLY tot_dam_aux_snipe(player_type *sniper_ptr, MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
387 monster_race *r_ptr = &r_info[m_ptr->r_idx];
388 bool seen = is_seen(m_ptr);
393 if (r_ptr->flags3 & (RF3_HURT_LITE))
395 MULTIPLY n = 20 + sniper_ptr->concent;
396 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
397 if (mult < n) mult = n;
401 if (r_ptr->flagsr & RFR_IM_FIRE)
403 if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
407 MULTIPLY n = 15 + (sniper_ptr->concent * 3);
408 if (mult < n) mult = n;
412 if (r_ptr->flagsr & RFR_IM_COLD)
414 if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
418 MULTIPLY n = 15 + (sniper_ptr->concent * 3);
419 if (mult < n) mult = n;
423 if (r_ptr->flagsr & RFR_IM_ELEC)
425 if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
429 MULTIPLY n = 18 + (sniper_ptr->concent * 4);
430 if (mult < n) mult = n;
434 if (r_ptr->flags3 & RF3_HURT_ROCK)
436 MULTIPLY n = 15 + (sniper_ptr->concent * 2);
437 if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
438 if (mult < n) mult = n;
440 else if (r_ptr->flags3 & RF3_NONLIVING)
442 MULTIPLY n = 15 + (sniper_ptr->concent * 2);
443 if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
444 if (mult < n) mult = n;
448 if (r_ptr->flags3 & RF3_GOOD)
450 MULTIPLY n = 15 + (sniper_ptr->concent * 4);
451 if (seen) r_ptr->r_flags3 |= RF3_GOOD;
452 if (mult < n) mult = n;
456 if (r_ptr->flags3 & RF3_EVIL)
458 MULTIPLY n = 12 + (sniper_ptr->concent * 3);
459 if (seen) r_ptr->r_flags3 |= RF3_EVIL;
460 if (r_ptr->flags3 & (RF3_HURT_LITE))
462 n += (sniper_ptr->concent * 3);
463 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
465 if (mult < n) mult = n;
469 if (mult < 50) mult = 50;
478 * @brief スナイパー技能の発動 /
479 * do_cmd_cast calls this function if the player's class is 'snipe'.
480 * @param spell 発動する特殊技能のID
481 * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
483 static bool cast_sniper_spell(player_type *sniper_ptr, int spell)
485 object_type *o_ptr = &sniper_ptr->inventory_list[INVEN_BOW];
486 SPELL_IDX snipe_type = SP_NONE;
488 if (o_ptr->tval != TV_BOW)
490 msg_print(_("弓を装備していない!", "You wield no bow!"));
497 case 0: /* Concentration */
498 if (!snipe_concentrate(sniper_ptr)) return FALSE;
499 take_turn(sniper_ptr, 100);
501 case 1: snipe_type = SP_LITE; break;
502 case 2: snipe_type = SP_AWAY; break;
503 case 3: snipe_type = SP_KILL_TRAP; break;
504 case 4: snipe_type = SP_FIRE; break;
505 case 5: snipe_type = SP_KILL_WALL; break;
506 case 6: snipe_type = SP_COLD; break;
507 case 7: snipe_type = SP_RUSH; break;
508 case 8: snipe_type = SP_PIERCE; break;
509 case 9: snipe_type = SP_EVILNESS; break;
510 case 10: snipe_type = SP_HOLYNESS; break;
511 case 11: snipe_type = SP_EXPLODE; break;
512 case 12: snipe_type = SP_DOUBLE; break;
513 case 13: snipe_type = SP_ELEC; break;
514 case 14: snipe_type = SP_NEEDLE; break;
515 case 15: snipe_type = SP_FINAL; break;
517 msg_print(_("なに?", "Zap?"));
521 do_cmd_fire(sniper_ptr, snipe_type);
523 return (sniper_ptr->is_fired);
527 * @brief スナイパー技能コマンドのメインルーチン /
530 void do_cmd_snipe(player_type *sniper_ptr)
535 if(cmd_limit_confused(sniper_ptr)) return;
536 if(cmd_limit_image(sniper_ptr)) return;
537 if(cmd_limit_stun(sniper_ptr)) return;
539 if (!get_snipe_power(sniper_ptr, &n, FALSE)) return;
542 cast = cast_sniper_spell(sniper_ptr, n);
545 sniper_ptr->redraw |= (PR_HP | PR_MANA);
546 sniper_ptr->window |= (PW_PLAYER);
547 sniper_ptr->window |= (PW_SPELL);
551 * @brief スナイパー技能コマンドの表示 /
554 void do_cmd_snipe_browse(player_type *sniper_ptr)
564 if (!get_snipe_power(sniper_ptr, &n, TRUE))
570 /* Clear lines, position cursor (really should use strlen here) */
571 Term_erase(12, 22, 255);
572 Term_erase(12, 21, 255);
573 Term_erase(12, 20, 255);
574 Term_erase(12, 19, 255);
575 Term_erase(12, 18, 255);
577 roff_to_buf(snipe_tips[n], 62, temp, sizeof(temp));
578 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
580 prt(&temp[j], line, 15);