OSDN Git Service

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