OSDN Git Service

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