OSDN Git Service

[Refactor] #38997 player_move.c 微修正 / Fixed a little in player_move.c
[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(player_type *creature_ptr)
119 {
120         if ((int)creature_ptr->concent < (2 + (creature_ptr->lev + 5) / 10)) creature_ptr->concent++;
121
122         msg_format(_("集中した。(集中度 %d)", "You concentrate deeply. (lvl %d)"), creature_ptr->concent);
123         creature_ptr->reset_concent = FALSE;
124
125         creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
126         creature_ptr->redraw |= (PR_STATUS);
127         return TRUE;
128 }
129
130 /*! 
131  * @brief スナイパーの集中度リセット
132  * @param msg TRUEならばメッセージを表示する
133  * @return なし
134  */
135 void reset_concentration(player_type *creature_ptr, bool msg)
136 {
137         if (msg)
138         {
139                 msg_print(_("集中力が途切れてしまった。", "Stop concentrating."));
140         }
141
142         creature_ptr->concent = 0;
143         creature_ptr->reset_concent = FALSE;
144
145         creature_ptr->update |= (PU_BONUS | PU_MONSTERS);
146         creature_ptr->redraw |= (PR_STATUS);
147 }
148
149 /*! 
150  * @brief スナイパーの集中度によるダメージボーナスを加算する
151  * @param tdam 算出中のダメージ
152  * @return 集中度修正を加えたダメージ
153  */
154 int boost_concentration_damage(player_type *creature_ptr, int tdam)
155 {
156         tdam *= (10 + creature_ptr->concent);
157         tdam /= 10;
158
159         return (tdam);
160 }
161
162 /*! 
163  * @brief スナイパーの技能リストを表示する
164  * @return なし
165  */
166 void display_snipe_list(player_type *sniper_ptr)
167 {
168         int i;
169         TERM_LEN y = 1;
170         TERM_LEN x = 1;
171         PLAYER_LEVEL plev = sniper_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)sniper_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(player_type *sniper_ptr, 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 = sniper_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)sniper_ptr->concent))
239                 {
240                         /* Success */
241                         return TRUE;
242                 }
243         }
244
245         flag = FALSE;
246         redraw = FALSE;
247
248         for (i = 0; i < MAX_SNIPE_POWERS; i++)
249         {
250                 if ((snipe_powers[i].min_lev <= plev) &&
251                         ((only_browse) || (snipe_powers[i].mana_cost <= (int)sniper_ptr->concent)))
252                 {
253                         num = i;
254                 }
255         }
256
257         /* Build a prompt (accept all spells) */
258         if (only_browse)
259         {
260                 (void)strnfmt(out_val, 78, 
261                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
262                                         p, I2A(0), I2A(num), p);
263         }
264         else
265         {
266                 (void)strnfmt(out_val, 78, 
267                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
268                                         p, I2A(0), I2A(num), p);
269         }
270
271         choice = always_show_list ? ESCAPE : 1;
272         while (!flag)
273         {
274                 if(choice == ESCAPE) choice = ' ';
275                 else if( !get_com(out_val, &choice, FALSE) )break; 
276
277                 /* Request redraw */
278                 if ((choice == ' ') || (choice == '*') || (choice == '?'))
279                 {
280                         /* Show the list */
281                         if (!redraw)
282                         {
283                                 char psi_desc[80];
284                                 redraw = TRUE;
285                                 if (!only_browse) screen_save();
286
287                                 /* Display a list of spells */
288                                 prt("", y, x);
289                                 put_str(_("名前", "Name"), y, x + 5);
290                                 if (only_browse) put_str(_("Lv   集中度", "Lv Pow"), y, x + 35);
291
292                                 /* Dump the spells */
293                                 for (i = 0; i < MAX_SNIPE_POWERS; i++)
294                                 {
295                                         Term_erase(x, y + i + 1, 255);
296
297                                         /* Access the spell */
298                                         spell = snipe_powers[i];
299                                         if (spell.min_lev > plev) continue;
300                                         if (!only_browse && (spell.mana_cost > (int)sniper_ptr->concent)) continue;
301
302                                         /* Dump the spell --(-- */
303                                         if (only_browse)
304                                                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
305                                                         I2A(i), spell.name,     spell.min_lev, spell.mana_cost);
306                                         else
307                                                 sprintf(psi_desc, "  %c) %-30s", I2A(i), spell.name);
308                                         prt(psi_desc, y + i + 1, x);
309                                 }
310
311                                 /* Clear the bottom line */
312                                 prt("", y + i + 1, x);
313                         }
314
315                         /* Hide the list */
316                         else
317                         {
318                                 /* Hide list */
319                                 redraw = FALSE;
320                                 if (!only_browse) screen_load();
321                         }
322
323                         /* Redo asking */
324                         continue;
325                 }
326
327                 /* Note verify */
328                 ask = isupper(choice);
329
330                 /* Lowercase */
331                 if (ask) choice = (char)tolower(choice);
332
333                 /* Extract request */
334                 i = (islower(choice) ? A2I(choice) : -1);
335
336                 /* Totally Illegal */
337                 if ((i < 0) || (i > num) || 
338                         (!only_browse &&(snipe_powers[i].mana_cost > (int)sniper_ptr->concent)))
339                 {
340                         bell();
341                         continue;
342                 }
343
344                 /* Save the spell index */
345                 spell = snipe_powers[i];
346
347                 /* Verify it */
348                 if (ask)
349                 {
350                         char tmp_val[160];
351
352                         /* Prompt */
353                         (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
354
355                         /* Belay that order */
356                         if (!get_check(tmp_val)) continue;
357                 }
358
359                 /* Stop the loop */
360                 flag = TRUE;
361         }
362         if (redraw && !only_browse) screen_load();
363
364         sniper_ptr->window |= (PW_SPELL);
365         handle_stuff(sniper_ptr);
366
367         /* Abort if needed */
368         if (!flag) return FALSE;
369
370         /* Save the choice */
371         (*sn) = i;
372
373         repeat_push(*sn);
374
375         /* Success */
376         return TRUE;
377 }
378
379
380 /*!
381  * @brief スナイバー技能のスレイ倍率計算を行う /
382  * Calcurate magnification of snipe technics
383  * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
384  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
385  * @return スレイの倍率(/10倍)
386  */
387 MULTIPLY tot_dam_aux_snipe(player_type *sniper_ptr, MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
388 {
389         monster_race *r_ptr = &r_info[m_ptr->r_idx];
390         bool seen = is_seen(m_ptr);
391
392         switch (snipe_type)
393         {
394         case SP_LITE:
395                 if (r_ptr->flags3 & (RF3_HURT_LITE))
396                 {
397                         MULTIPLY n = 20 + sniper_ptr->concent;
398                         if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
399                         if (mult < n) mult = n;
400                 }
401                 break;
402         case SP_FIRE:
403                 if (r_ptr->flagsr & RFR_IM_FIRE)
404                 {
405                         if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
406                 }
407                 else
408                 {
409                         MULTIPLY n = 15 + (sniper_ptr->concent * 3);
410                         if (mult < n) mult = n;
411                 }
412                 break;
413         case SP_COLD:
414                 if (r_ptr->flagsr & RFR_IM_COLD)
415                 {
416                         if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
417                 }
418                 else
419                 {
420                         MULTIPLY n = 15 + (sniper_ptr->concent * 3);
421                         if (mult < n) mult = n;
422                 }
423                 break;
424         case SP_ELEC:
425                 if (r_ptr->flagsr & RFR_IM_ELEC)
426                 {
427                         if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
428                 }
429                 else
430                 {
431                         MULTIPLY n = 18 + (sniper_ptr->concent * 4);
432                         if (mult < n) mult = n;
433                 }
434                 break;
435         case SP_KILL_WALL:
436                 if (r_ptr->flags3 & RF3_HURT_ROCK)
437                 {
438                         MULTIPLY n = 15 + (sniper_ptr->concent * 2);
439                         if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
440                         if (mult < n) mult = n;
441                 }
442                 else if (r_ptr->flags3 & RF3_NONLIVING)
443                 {
444                         MULTIPLY n = 15 + (sniper_ptr->concent * 2);
445                         if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
446                         if (mult < n) mult = n;
447                 }
448                 break;
449         case SP_EVILNESS:
450                 if (r_ptr->flags3 & RF3_GOOD)
451                 {
452                         MULTIPLY n = 15 + (sniper_ptr->concent * 4);
453                         if (seen) r_ptr->r_flags3 |= RF3_GOOD;
454                         if (mult < n) mult = n;
455                 }
456                 break;
457         case SP_HOLYNESS:
458                 if (r_ptr->flags3 & RF3_EVIL)
459                 {
460                         MULTIPLY n = 12 + (sniper_ptr->concent * 3);
461                         if (seen) r_ptr->r_flags3 |= RF3_EVIL;
462                         if (r_ptr->flags3 & (RF3_HURT_LITE))
463                         {
464                                 n += (sniper_ptr->concent * 3);
465                                 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
466                         }
467                         if (mult < n) mult = n;
468                 }
469                 break;
470         case SP_FINAL:
471                 if (mult < 50) mult = 50;
472                 break;
473         }
474
475         return (mult);
476 }
477
478
479 /*!
480  * @brief スナイパー技能の発動 /
481  * do_cmd_cast calls this function if the player's class is 'snipe'.
482  * @param spell 発動する特殊技能のID
483  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
484  */
485 static bool cast_sniper_spell(player_type *sniper_ptr, int spell)
486 {
487         object_type *o_ptr = &sniper_ptr->inventory_list[INVEN_BOW];
488         SPELL_IDX snipe_type = SP_NONE;
489
490         if (o_ptr->tval != TV_BOW)
491         {
492                 msg_print(_("弓を装備していない!", "You wield no bow!"));
493                 return FALSE;
494         }
495
496         /* spell code */
497         switch (spell)
498         {
499         case 0: /* Concentration */
500                 if (!snipe_concentrate(sniper_ptr)) return FALSE;
501                 take_turn(sniper_ptr, 100);
502                 return TRUE;
503         case 1: snipe_type = SP_LITE; break;
504         case 2: snipe_type = SP_AWAY; break;
505         case 3: snipe_type = SP_KILL_TRAP; break;
506         case 4: snipe_type = SP_FIRE; break;
507         case 5: snipe_type = SP_KILL_WALL; break;
508         case 6: snipe_type = SP_COLD; break;
509         case 7: snipe_type = SP_RUSH; break;
510         case 8: snipe_type = SP_PIERCE; break;
511         case 9: snipe_type = SP_EVILNESS; break;
512         case 10: snipe_type = SP_HOLYNESS; break;
513         case 11: snipe_type = SP_EXPLODE; break;
514         case 12: snipe_type = SP_DOUBLE; break;
515         case 13: snipe_type = SP_ELEC; break;
516         case 14: snipe_type = SP_NEEDLE; break;
517         case 15: snipe_type = SP_FINAL; break;
518         default:
519                 msg_print(_("なに?", "Zap?"));
520         }
521
522         command_cmd = 'f';
523         do_cmd_fire(sniper_ptr, snipe_type);
524
525         return (sniper_ptr->is_fired);
526 }
527
528 /*!
529  * @brief スナイパー技能コマンドのメインルーチン /
530  * @return なし
531  */
532 void do_cmd_snipe(player_type *sniper_ptr)
533 {
534         COMMAND_CODE n = 0;
535         bool cast;
536
537         if(cmd_limit_confused(sniper_ptr)) return;
538         if(cmd_limit_image(sniper_ptr)) return;
539         if(cmd_limit_stun(sniper_ptr)) return;
540
541         if (!get_snipe_power(sniper_ptr, &n, FALSE)) return;
542
543         sound(SOUND_SHOOT);
544         cast = cast_sniper_spell(sniper_ptr, n);
545
546         if (!cast) return;
547         sniper_ptr->redraw |= (PR_HP | PR_MANA);
548         sniper_ptr->window |= (PW_PLAYER);
549         sniper_ptr->window |= (PW_SPELL);
550 }
551
552 /*!
553  * @brief スナイパー技能コマンドの表示 /
554  * @return なし
555  */
556 void do_cmd_snipe_browse(player_type *sniper_ptr)
557 {
558         COMMAND_CODE n = 0;
559         int j, line;
560         char temp[62 * 4];
561
562         screen_save();
563
564         while(1)
565         {
566                 if (!get_snipe_power(sniper_ptr, &n, TRUE))
567                 {
568                         screen_load();
569                         return;
570                 }
571
572                 /* Clear lines, position cursor  (really should use strlen here) */
573                 Term_erase(12, 22, 255);
574                 Term_erase(12, 21, 255);
575                 Term_erase(12, 20, 255);
576                 Term_erase(12, 19, 255);
577                 Term_erase(12, 18, 255);
578
579                 roff_to_buf(snipe_tips[n], 62, temp, sizeof(temp));
580                 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
581                 {
582                         prt(&temp[j], line, 15);
583                         line++;
584                 }
585         }
586 }