OSDN Git Service

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