OSDN Git Service

[Refactor] #37353 cmd2.c を cmd-basic.c/h に整理。
[hengband/hengband.git] / src / snipe.c
1 /*!
2  * @file snipe.c
3  * @brief スナイパー技能の実装 / Sniping
4  * @date 2014/01/18
5  * @author
6  * 2014 Deskull rearranged comment for Doxygen.\n
7  */
8
9 #include "angband.h"
10 #include "player-status.h"
11 #include "cmd-basic.h"
12
13 #define MAX_SNIPE_POWERS 16
14
15 /*! スナイパー技能情報のtypedef */
16 typedef struct snipe_power snipe_power;
17
18 /*! スナイパー技能情報の構造体 */
19 struct snipe_power
20 {
21         PLAYER_LEVEL min_lev;
22         MANA_POINT mana_cost;
23         concptr name;
24 };
25
26 /*! スナイパー技能の解説メッセージ */
27 static concptr const snipe_tips[MAX_SNIPE_POWERS] =
28 {
29 #ifdef JP
30         "精神を集中する。射撃の威力、精度が上がり、高度な射撃術が使用できるようになる。",
31         "光る矢を放つ。光に弱いモンスターに威力を発揮する。",
32         "射撃を行った後、短距離の瞬間移動を行う。",
33         "軌道上の罠をすべて無効にする低空飛行の矢を放つ。",
34         "火炎属性の矢を放つ。",
35         "壁を粉砕する矢を放つ。岩でできたモンスターと無生物のモンスターに威力を発揮する。",
36         "冷気属性の矢を放つ。",
37         "敵を突き飛ばす矢を放つ。",
38         "複数の敵を貫通する矢を放つ。",
39         "善良なモンスターに威力を発揮する矢を放つ。",
40         "邪悪なモンスターに威力を発揮する矢を放つ。",
41         "当たると爆発する矢を放つ。",
42         "2回射撃を行う。",
43         "電撃属性の矢を放つ。",
44         "敵の急所にめがけて矢を放つ。成功すると敵を一撃死させる。失敗すると1ダメージ。",
45         "全てのモンスターに高威力を発揮する矢を放つ。反動による副次効果を受ける。",
46 #else
47         "Concentrate your mind fot shooting.",
48         "Shot an allow with brightness.",
49         "Blink after shooting.",
50         "Shot an allow able to shatter traps.",
51         "Deals extra damege of fire.",
52         "Shot an allow able to shatter rocks.",
53         "Deals extra damege of ice.",
54         "An allow rushes away a target.",
55         "An allow pierces some monsters.",
56         "Deals more damage to good monsters.",
57         "Deals more damage to evil monsters.",
58         "An allow explodes when it hits a monster.",
59         "Shot allows twice.",
60         "Deals extra damege of lightning.",
61         "Deals quick death or 1 damage.",
62         "Deals great damage to all monsters, and some side effects to you.",
63 #endif
64 };
65
66 /*! スナイパー技能テーブル */
67 static snipe_power const snipe_powers[MAX_SNIPE_POWERS] =
68 {
69         /* Level gained,  cost,  name */
70 #ifdef JP
71         {  1,  0,  "精神集中" },
72         {  2,  1,  "フラッシュアロー" },
73         {  3,  1,  "シュート&アウェイ" },
74         {  5,  1,  "解除の矢" },
75         {  8,  2,  "火炎の矢" },
76         { 10,  2,  "岩砕き" },
77         { 13,  2,  "冷気の矢" },
78         { 18,  2,  "烈風弾"},
79         { 22,  3,  "貫通弾" },
80         { 25,  4,  "邪念弾"},
81         { 26,  4,  "破魔矢" },
82         { 30,  3,  "爆発の矢"},
83         { 32,  4,  "ダブルショット" },
84         { 36,  3,  "プラズマボルト" },
85         { 40,  3,  "ニードルショット" },
86         { 48,  7,  "セイントスターアロー" },
87 #else
88         {  1,  0,  "Concentration" },
89         {  2,  1,  "Flush Arrow" },
90         {  3,  1,  "Shoot & Away" },
91         {  5,  1,  "Disarm Shot" },
92         {  8,  2,  "Fire Shot" },
93         { 10,  2,  "Shatter Arrow" },
94         { 13,  2,  "Ice Shot" },
95         { 18,  2,  "Rushing Arrow"},
96         { 22,  3,  "Piercing Shot" },
97         { 25,  4,  "Evil Shot"},
98         { 26,  4,  "Holy Shot" },
99         { 30,  3,  "Missile"},
100         { 32,  4,  "Double Shot" },
101         { 36,  3,  "Plasma Bolt" },
102         { 40,  3,  "Needle Shot" },
103         { 48,  7,  "Saint Stars Arrow" },
104 #endif
105 };
106
107 /*! 
108  * @brief スナイパーの集中度加算
109  * @return 常にTRUEを返す
110  */
111 static bool snipe_concentrate(void)
112 {
113         if ((int)p_ptr->concent < (2 + (p_ptr->lev + 5) / 10)) p_ptr->concent++;
114
115         msg_format(_("集中した。(集中度 %d)", "You concentrate deeply. (lvl %d)"), p_ptr->concent);
116         reset_concent = FALSE;
117
118         p_ptr->update |= (PU_BONUS | PU_MONSTERS);
119         p_ptr->redraw |= (PR_STATUS);
120         return (TRUE);
121 }
122
123 /*! 
124  * @brief スナイパーの集中度リセット
125  * @param msg TRUEならばメッセージを表示する
126  * @return なし
127  */
128 void reset_concentration(bool msg)
129 {
130         if (msg)
131         {
132                 msg_print(_("集中力が途切れてしまった。", "Stop concentrating."));
133         }
134
135         p_ptr->concent = 0;
136         reset_concent = FALSE;
137
138         p_ptr->update |= (PU_BONUS | PU_MONSTERS);
139         p_ptr->redraw |= (PR_STATUS);
140 }
141
142 /*! 
143  * @brief スナイパーの集中度によるダメージボーナスを加算する
144  * @param tdam 算出中のダメージ
145  * @return 集中度修正を加えたダメージ
146  */
147 int boost_concentration_damage(int tdam)
148 {
149         tdam *= (10 + p_ptr->concent);
150         tdam /= 10;
151
152         return (tdam);
153 }
154
155 /*! 
156  * @brief スナイパーの技能リストを表示する
157  * @return なし
158  */
159 void display_snipe_list(void)
160 {
161         int i;
162         TERM_LEN y = 1;
163         TERM_LEN x = 1;
164         PLAYER_LEVEL plev = p_ptr->lev;
165         snipe_power     spell;
166         char            psi_desc[80];
167
168         /* Display a list of spells */
169         prt("", y, x);
170         put_str(_("名前", "Name"), y, x + 5);
171         put_str(_("Lv   MP", "Lv Mana"), y, x + 35);
172
173         for (i = 0; i < MAX_SNIPE_POWERS; i++)
174         {
175                 /* Access the available spell */
176                 spell = snipe_powers[i];
177                 if (spell.min_lev > plev) continue;
178                 if (spell.mana_cost > (int)p_ptr->concent) continue;
179
180                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
181                         I2A(i), spell.name, spell.min_lev, spell.mana_cost);
182
183                 Term_putstr(x, y + i + 1, -1, TERM_WHITE, psi_desc);
184         }
185         return;
186 }
187
188
189 /*! 
190  * @brief スナイパー技能を選択する
191  * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
192  * @param only_browse 一覧を見るだけの場合TRUEを返す
193  * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
194  * Allow user to choose a mindcrafter power.\n
195  *\n
196  * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
197  * If the user hits escape, returns FALSE, and set '*sn' to -1\n
198  * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
199  *\n
200  * The "prompt" should be "cast", "recite", or "study"\n
201  * The "known" should be TRUE for cast/pray, FALSE for study\n
202  *\n
203  * nb: This function has a (trivial) display bug which will be obvious\n
204  * when you run it. It's probably easy to fix but I haven't tried,\n
205  * sorry.\n
206  */
207 static int get_snipe_power(COMMAND_CODE *sn, bool only_browse)
208 {
209         COMMAND_CODE i;
210         int             num = 0;
211         TERM_LEN y = 1;
212         TERM_LEN x = 20;
213         PLAYER_LEVEL plev = p_ptr->lev;
214         int             ask;
215         char            choice;
216         char            out_val[160];
217         concptr            p = _("射撃術", "power");
218         snipe_power     spell;
219         bool            flag, redraw;
220
221         repeat_push(*sn);
222
223         /* Assume cancelled */
224         *sn = (-1);
225
226         /* Repeat previous command */
227         /* Get the spell, if available */
228         if (repeat_pull(sn))
229         {
230                 /* Verify the spell */
231                 if ((snipe_powers[*sn].min_lev <= plev) && (snipe_powers[*sn].mana_cost <= (int)p_ptr->concent))
232                 {
233                         /* Success */
234                         return (TRUE);
235                 }
236         }
237
238         /* Nothing chosen yet */
239         flag = FALSE;
240
241         /* No redraw yet */
242         redraw = FALSE;
243
244         for (i = 0; i < MAX_SNIPE_POWERS; i++)
245         {
246                 if ((snipe_powers[i].min_lev <= plev) &&
247                         ((only_browse) || (snipe_powers[i].mana_cost <= (int)p_ptr->concent)))
248                 {
249                         num = i;
250                 }
251         }
252
253         /* Build a prompt (accept all spells) */
254         if (only_browse)
255         {
256                 (void)strnfmt(out_val, 78, 
257                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
258                                         p, I2A(0), I2A(num), p);
259         }
260         else
261         {
262                 (void)strnfmt(out_val, 78, 
263                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
264                                         p, I2A(0), I2A(num), p);
265         }
266
267         choice = always_show_list ? ESCAPE : 1;
268         while (!flag)
269         {
270                 if(choice == ESCAPE) choice = ' ';
271                 else if( !get_com(out_val, &choice, FALSE) )break; 
272
273                 /* Request redraw */
274                 if ((choice == ' ') || (choice == '*') || (choice == '?'))
275                 {
276                         /* Show the list */
277                         if (!redraw)
278                         {
279                                 char psi_desc[80];
280                                 redraw = TRUE;
281                                 if (!only_browse) screen_save();
282
283                                 /* Display a list of spells */
284                                 prt("", y, x);
285                                 put_str(_("名前", "Name"), y, x + 5);
286                                 if (only_browse) put_str(_("Lv   集中度", "Lv Pow"), y, x + 35);
287
288                                 /* Dump the spells */
289                                 for (i = 0; i < MAX_SNIPE_POWERS; i++)
290                                 {
291                                         Term_erase(x, y + i + 1, 255);
292
293                                         /* Access the spell */
294                                         spell = snipe_powers[i];
295                                         if (spell.min_lev > plev) continue;
296                                         if (!only_browse && (spell.mana_cost > (int)p_ptr->concent)) continue;
297
298                                         /* Dump the spell --(-- */
299                                         if (only_browse)
300                                                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
301                                                         I2A(i), spell.name,     spell.min_lev, spell.mana_cost);
302                                         else
303                                                 sprintf(psi_desc, "  %c) %-30s", I2A(i), spell.name);
304                                         prt(psi_desc, y + i + 1, x);
305                                 }
306
307                                 /* Clear the bottom line */
308                                 prt("", y + i + 1, x);
309                         }
310
311                         /* Hide the list */
312                         else
313                         {
314                                 /* Hide list */
315                                 redraw = FALSE;
316                                 if (!only_browse) screen_load();
317                         }
318
319                         /* Redo asking */
320                         continue;
321                 }
322
323                 /* Note verify */
324                 ask = isupper(choice);
325
326                 /* Lowercase */
327                 if (ask) choice = (char)tolower(choice);
328
329                 /* Extract request */
330                 i = (islower(choice) ? A2I(choice) : -1);
331
332                 /* Totally Illegal */
333                 if ((i < 0) || (i > num) || 
334                         (!only_browse &&(snipe_powers[i].mana_cost > (int)p_ptr->concent)))
335                 {
336                         bell();
337                         continue;
338                 }
339
340                 /* Save the spell index */
341                 spell = snipe_powers[i];
342
343                 /* Verify it */
344                 if (ask)
345                 {
346                         char tmp_val[160];
347
348                         /* Prompt */
349                         (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
350
351                         /* Belay that order */
352                         if (!get_check(tmp_val)) continue;
353                 }
354
355                 /* Stop the loop */
356                 flag = TRUE;
357         }
358         if (redraw && !only_browse) screen_load();
359
360         p_ptr->window |= (PW_SPELL);
361         handle_stuff();
362
363         /* Abort if needed */
364         if (!flag) return (FALSE);
365
366         /* Save the choice */
367         (*sn) = i;
368
369         repeat_push(*sn);
370
371         /* Success */
372         return (TRUE);
373 }
374
375 /*!
376  * @brief スナイバー技能のスレイ倍率計算を行う /
377  * Calcurate magnification of snipe technics
378  * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
379  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
380  * @return スレイの倍率(/10倍)
381  */
382 MULTIPLY tot_dam_aux_snipe(MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
383 {
384         monster_race *r_ptr = &r_info[m_ptr->r_idx];
385         bool seen = is_seen(m_ptr);
386
387         switch (snipe_type)
388         {
389         case SP_LITE:
390                 if (r_ptr->flags3 & (RF3_HURT_LITE))
391                 {
392                         MULTIPLY n = 20 + p_ptr->concent;
393                         if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
394                         if (mult < n) mult = n;
395                 }
396                 break;
397         case SP_FIRE:
398                 if (r_ptr->flagsr & RFR_IM_FIRE)
399                 {
400                         if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
401                 }
402                 else
403                 {
404                         MULTIPLY n = 15 + (p_ptr->concent * 3);
405                         if (mult < n) mult = n;
406                 }
407                 break;
408         case SP_COLD:
409                 if (r_ptr->flagsr & RFR_IM_COLD)
410                 {
411                         if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
412                 }
413                 else
414                 {
415                         MULTIPLY n = 15 + (p_ptr->concent * 3);
416                         if (mult < n) mult = n;
417                 }
418                 break;
419         case SP_ELEC:
420                 if (r_ptr->flagsr & RFR_IM_ELEC)
421                 {
422                         if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
423                 }
424                 else
425                 {
426                         MULTIPLY n = 18 + (p_ptr->concent * 4);
427                         if (mult < n) mult = n;
428                 }
429                 break;
430         case SP_KILL_WALL:
431                 if (r_ptr->flags3 & RF3_HURT_ROCK)
432                 {
433                         MULTIPLY n = 15 + (p_ptr->concent * 2);
434                         if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
435                         if (mult < n) mult = n;
436                 }
437                 else if (r_ptr->flags3 & RF3_NONLIVING)
438                 {
439                         MULTIPLY n = 15 + (p_ptr->concent * 2);
440                         if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
441                         if (mult < n) mult = n;
442                 }
443                 break;
444         case SP_EVILNESS:
445                 if (r_ptr->flags3 & RF3_GOOD)
446                 {
447                         MULTIPLY n = 15 + (p_ptr->concent * 4);
448                         if (seen) r_ptr->r_flags3 |= RF3_GOOD;
449                         if (mult < n) mult = n;
450                 }
451                 break;
452         case SP_HOLYNESS:
453                 if (r_ptr->flags3 & RF3_EVIL)
454                 {
455                         MULTIPLY n = 12 + (p_ptr->concent * 3);
456                         if (seen) r_ptr->r_flags3 |= RF3_EVIL;
457                         if (r_ptr->flags3 & (RF3_HURT_LITE))
458                         {
459                                 n += (p_ptr->concent * 3);
460                                 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
461                         }
462                         if (mult < n) mult = n;
463                 }
464                 break;
465         case SP_FINAL:
466                 if (mult < 50) mult = 50;
467                 break;
468         }
469
470         return (mult);
471 }
472
473
474 /*!
475  * @brief スナイパー技能の発動 /
476  * do_cmd_cast calls this function if the player's class is 'snipe'.
477  * @param spell 発動する特殊技能のID
478  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
479  */
480 static bool cast_sniper_spell(int spell)
481 {
482         object_type *o_ptr = &inventory[INVEN_BOW];
483         SPELL_IDX snipe_type = SP_NONE;
484
485         if (o_ptr->tval != TV_BOW)
486         {
487                 msg_print(_("弓を装備していない!", "You wield no bow!"));
488                 return (FALSE);
489         }
490
491         /* spell code */
492         switch (spell)
493         {
494         case 0: /* Concentration */
495                 if (!snipe_concentrate()) return (FALSE);
496                 take_turn(p_ptr, 100);
497                 return (TRUE);
498         case 1: snipe_type = SP_LITE; break;
499         case 2: snipe_type = SP_AWAY; break;
500         case 3: snipe_type = SP_KILL_TRAP; break;
501         case 4: snipe_type = SP_FIRE; break;
502         case 5: snipe_type = SP_KILL_WALL; break;
503         case 6: snipe_type = SP_COLD; break;
504         case 7: snipe_type = SP_RUSH; break;
505         case 8: snipe_type = SP_PIERCE; break;
506         case 9: snipe_type = SP_EVILNESS; break;
507         case 10: snipe_type = SP_HOLYNESS; break;
508         case 11: snipe_type = SP_EXPLODE; break;
509         case 12: snipe_type = SP_DOUBLE; break;
510         case 13: snipe_type = SP_ELEC; break;
511         case 14: snipe_type = SP_NEEDLE; break;
512         case 15: snipe_type = SP_FINAL; break;
513         default:
514                 msg_print(_("なに?", "Zap?"));
515         }
516
517         command_cmd = 'f';
518         do_cmd_fire(snipe_type);
519
520         return (is_fired);
521 }
522
523 /*!
524  * @brief スナイパー技能コマンドのメインルーチン /
525  * @return なし
526  */
527 void do_cmd_snipe(void)
528 {
529         COMMAND_CODE n = 0;
530         bool cast;
531
532         if(cmd_limit_confused(p_ptr)) return;
533         if(cmd_limit_image(p_ptr)) return;
534         if(cmd_limit_stun(p_ptr)) return;
535
536         if (!get_snipe_power(&n, FALSE)) return;
537
538         sound(SOUND_SHOOT);
539         cast = cast_sniper_spell(n);
540
541         if (!cast) return;
542         p_ptr->redraw |= (PR_HP | PR_MANA);
543         p_ptr->window |= (PW_PLAYER);
544         p_ptr->window |= (PW_SPELL);
545 }
546
547 /*!
548  * @brief スナイパー技能コマンドの表示 /
549  * @return なし
550  */
551 void do_cmd_snipe_browse(void)
552 {
553         COMMAND_CODE n = 0;
554         int j, line;
555         char temp[62 * 4];
556
557         screen_save();
558
559         while(1)
560         {
561                 if (!get_snipe_power(&n, TRUE))
562                 {
563                         screen_load();
564                         return;
565                 }
566
567                 /* Clear lines, position cursor  (really should use strlen here) */
568                 Term_erase(12, 22, 255);
569                 Term_erase(12, 21, 255);
570                 Term_erase(12, 20, 255);
571                 Term_erase(12, 19, 255);
572                 Term_erase(12, 18, 255);
573
574                 roff_to_buf(snipe_tips[n], 62, temp, sizeof(temp));
575                 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
576                 {
577                         prt(&temp[j], line, 15);
578                         line++;
579                 }
580         }
581 }