OSDN Git Service

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