OSDN Git Service

[Refactor] Shift-JISのファイルをUTF-8に変換 / Changed Shift-JIS based files to UTF-8 based
[hengbandforosx/hengbandosx.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                 if (spell.mana_cost > (int)sniper_ptr->concent) continue;
198
199                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
200                         I2A(i), spell.name, spell.min_lev, spell.mana_cost);
201
202                 term_putstr(x, y + i + 1, -1, TERM_WHITE, psi_desc);
203         }
204         return;
205 }
206
207
208 /*! 
209  * @brief スナイパー技能を選択する
210  * @param sn 選択した特殊技能ID、キャンセルの場合-1、不正な選択の場合-2を返す
211  * @param only_browse 一覧を見るだけの場合TRUEを返す
212  * @return 発動可能な魔法を選択した場合TRUE、キャンセル処理か不正な選択が行われた場合FALSEを返す。
213  * Allow user to choose a mindcrafter power.\n
214  *\n
215  * If a valid spell is chosen, saves it in '*sn' and returns TRUE\n
216  * If the user hits escape, returns FALSE, and set '*sn' to -1\n
217  * If there are no legal choices, returns FALSE, and sets '*sn' to -2\n
218  *\n
219  * The "prompt" should be "cast", "recite", or "study"\n
220  * The "known" should be TRUE for cast/pray, FALSE for study\n
221  *\n
222  * nb: This function has a (trivial) display bug which will be obvious\n
223  * when you run it. It's probably easy to fix but I haven't tried,\n
224  * sorry.\n
225  */
226 static int get_snipe_power(player_type *sniper_ptr, COMMAND_CODE *sn, bool only_browse)
227 {
228         COMMAND_CODE i;
229         int num = 0;
230         TERM_LEN y = 1;
231         TERM_LEN x = 20;
232         PLAYER_LEVEL plev = sniper_ptr->lev;
233         int ask;
234         char choice;
235         char out_val[160];
236         concptr p = _("射撃術", "power");
237         snipe_power spell;
238         bool flag, redraw;
239
240         repeat_push(*sn);
241
242         /* Assume cancelled */
243         *sn = (-1);
244
245         /* Repeat previous command */
246         /* Get the spell, if available */
247         if (repeat_pull(sn))
248         {
249                 /* Verify the spell */
250                 if ((snipe_powers[*sn].min_lev <= plev) && (snipe_powers[*sn].mana_cost <= (int)sniper_ptr->concent))
251                 {
252                         /* Success */
253                         return TRUE;
254                 }
255         }
256
257         flag = FALSE;
258         redraw = FALSE;
259
260         for (i = 0; i < MAX_SNIPE_POWERS; i++)
261         {
262                 if ((snipe_powers[i].min_lev <= plev) &&
263                         ((only_browse) || (snipe_powers[i].mana_cost <= (int)sniper_ptr->concent)))
264                 {
265                         num = i;
266                 }
267         }
268
269         /* Build a prompt (accept all spells) */
270         if (only_browse)
271         {
272                 (void)strnfmt(out_val, 78, 
273                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
274                                         p, I2A(0), I2A(num), p);
275         }
276         else
277         {
278                 (void)strnfmt(out_val, 78, 
279                                         _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
280                                         p, I2A(0), I2A(num), p);
281         }
282
283         choice = always_show_list ? ESCAPE : 1;
284         while (!flag)
285         {
286                 if(choice == ESCAPE) choice = ' ';
287                 else if( !get_com(out_val, &choice, FALSE) )break; 
288
289                 /* Request redraw */
290                 if ((choice == ' ') || (choice == '*') || (choice == '?'))
291                 {
292                         /* Show the list */
293                         if (!redraw)
294                         {
295                                 char psi_desc[80];
296                                 redraw = TRUE;
297                                 if (!only_browse) screen_save();
298
299                                 /* Display a list of spells */
300                                 prt("", y, x);
301                                 put_str(_("名前", "Name"), y, x + 5);
302                                 if (only_browse) put_str(_("Lv   集中度", "Lv Pow"), y, x + 35);
303
304                                 /* Dump the spells */
305                                 for (i = 0; i < MAX_SNIPE_POWERS; i++)
306                                 {
307                                         term_erase(x, y + i + 1, 255);
308
309                                         /* Access the spell */
310                                         spell = snipe_powers[i];
311                                         if (spell.min_lev > plev) continue;
312                                         if (!only_browse && (spell.mana_cost > (int)sniper_ptr->concent)) continue;
313
314                                         /* Dump the spell --(-- */
315                                         if (only_browse)
316                                                 sprintf(psi_desc, "  %c) %-30s%2d %4d",
317                                                         I2A(i), spell.name,     spell.min_lev, spell.mana_cost);
318                                         else
319                                                 sprintf(psi_desc, "  %c) %-30s", I2A(i), spell.name);
320                                         prt(psi_desc, y + i + 1, x);
321                                 }
322
323                                 /* Clear the bottom line */
324                                 prt("", y + i + 1, x);
325                         }
326
327                         /* Hide the list */
328                         else
329                         {
330                                 /* Hide list */
331                                 redraw = FALSE;
332                                 if (!only_browse) screen_load();
333                         }
334
335                         /* Redo asking */
336                         continue;
337                 }
338
339                 /* Note verify */
340                 ask = isupper(choice);
341
342                 /* Lowercase */
343                 if (ask) choice = (char)tolower(choice);
344
345                 /* Extract request */
346                 i = (islower(choice) ? A2I(choice) : -1);
347
348                 /* Totally Illegal */
349                 if ((i < 0) || (i > num) || 
350                         (!only_browse &&(snipe_powers[i].mana_cost > (int)sniper_ptr->concent)))
351                 {
352                         bell();
353                         continue;
354                 }
355
356                 /* Save the spell index */
357                 spell = snipe_powers[i];
358
359                 /* Verify it */
360                 if (ask)
361                 {
362                         char tmp_val[160];
363
364                         /* Prompt */
365                         (void) strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), snipe_powers[i].name);
366
367                         /* Belay that order */
368                         if (!get_check(tmp_val)) continue;
369                 }
370
371                 /* Stop the loop */
372                 flag = TRUE;
373         }
374         if (redraw && !only_browse) screen_load();
375
376         sniper_ptr->window |= (PW_SPELL);
377         handle_stuff(sniper_ptr);
378
379         /* Abort if needed */
380         if (!flag) return FALSE;
381
382         /* Save the choice */
383         (*sn) = i;
384
385         repeat_push(*sn);
386
387         /* Success */
388         return TRUE;
389 }
390
391
392 /*!
393  * @brief スナイバー技能のスレイ倍率計算を行う /
394  * Calcurate magnification of snipe technics
395  * @param mult スナイバー技能のスレイ効果以前に算出している多要素の倍率(/10倍)
396  * @param m_ptr 目標となるモンスターの構造体参照ポインタ
397  * @return スレイの倍率(/10倍)
398  */
399 MULTIPLY calc_snipe_damage_with_slay(player_type *sniper_ptr, MULTIPLY mult, monster_type *m_ptr, SPELL_IDX snipe_type)
400 {
401         monster_race *r_ptr = &r_info[m_ptr->r_idx];
402         bool seen = is_seen(sniper_ptr, m_ptr);
403
404         switch (snipe_type)
405         {
406         case SP_LITE:
407                 if (r_ptr->flags3 & (RF3_HURT_LITE))
408                 {
409                         MULTIPLY n = 20 + sniper_ptr->concent;
410                         if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
411                         if (mult < n) mult = n;
412                 }
413                 break;
414         case SP_FIRE:
415                 if (r_ptr->flagsr & RFR_IM_FIRE)
416                 {
417                         if (seen) r_ptr->r_flagsr |= RFR_IM_FIRE;
418                 }
419                 else
420                 {
421                         MULTIPLY n = 15 + (sniper_ptr->concent * 3);
422                         if (mult < n) mult = n;
423                 }
424                 break;
425         case SP_COLD:
426                 if (r_ptr->flagsr & RFR_IM_COLD)
427                 {
428                         if (seen) r_ptr->r_flagsr |= RFR_IM_COLD;
429                 }
430                 else
431                 {
432                         MULTIPLY n = 15 + (sniper_ptr->concent * 3);
433                         if (mult < n) mult = n;
434                 }
435                 break;
436         case SP_ELEC:
437                 if (r_ptr->flagsr & RFR_IM_ELEC)
438                 {
439                         if (seen) r_ptr->r_flagsr |= RFR_IM_ELEC;
440                 }
441                 else
442                 {
443                         MULTIPLY n = 18 + (sniper_ptr->concent * 4);
444                         if (mult < n) mult = n;
445                 }
446                 break;
447         case SP_KILL_WALL:
448                 if (r_ptr->flags3 & RF3_HURT_ROCK)
449                 {
450                         MULTIPLY n = 15 + (sniper_ptr->concent * 2);
451                         if (seen) r_ptr->r_flags3 |= RF3_HURT_ROCK;
452                         if (mult < n) mult = n;
453                 }
454                 else if (r_ptr->flags3 & RF3_NONLIVING)
455                 {
456                         MULTIPLY n = 15 + (sniper_ptr->concent * 2);
457                         if (seen) r_ptr->r_flags3 |= RF3_NONLIVING;
458                         if (mult < n) mult = n;
459                 }
460                 break;
461         case SP_EVILNESS:
462                 if (r_ptr->flags3 & RF3_GOOD)
463                 {
464                         MULTIPLY n = 15 + (sniper_ptr->concent * 4);
465                         if (seen) r_ptr->r_flags3 |= RF3_GOOD;
466                         if (mult < n) mult = n;
467                 }
468                 break;
469         case SP_HOLYNESS:
470                 if (r_ptr->flags3 & RF3_EVIL)
471                 {
472                         MULTIPLY n = 12 + (sniper_ptr->concent * 3);
473                         if (seen) r_ptr->r_flags3 |= RF3_EVIL;
474                         if (r_ptr->flags3 & (RF3_HURT_LITE))
475                         {
476                                 n += (sniper_ptr->concent * 3);
477                                 if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
478                         }
479                         if (mult < n) mult = n;
480                 }
481                 break;
482         case SP_FINAL:
483                 if (mult < 50) mult = 50;
484                 break;
485         }
486
487         return (mult);
488 }
489
490
491 /*!
492  * @brief スナイパー技能の発動 /
493  * do_cmd_cast calls this function if the player's class is 'snipe'.
494  * @param spell 発動する特殊技能のID
495  * @return 処理を実行したらTRUE、キャンセルした場合FALSEを返す。
496  */
497 static bool cast_sniper_spell(player_type *sniper_ptr, int spell)
498 {
499         object_type *o_ptr = &sniper_ptr->inventory_list[INVEN_BOW];
500         SPELL_IDX snipe_type = SP_NONE;
501
502         if (o_ptr->tval != TV_BOW)
503         {
504                 msg_print(_("弓を装備していない!", "You wield no bow!"));
505                 return FALSE;
506         }
507
508         /* spell code */
509         switch (spell)
510         {
511         case 0: /* Concentration */
512                 if (!snipe_concentrate(sniper_ptr)) return FALSE;
513                 take_turn(sniper_ptr, 100);
514                 return TRUE;
515         case 1: snipe_type = SP_LITE; break;
516         case 2: snipe_type = SP_AWAY; break;
517         case 3: snipe_type = SP_KILL_TRAP; break;
518         case 4: snipe_type = SP_FIRE; break;
519         case 5: snipe_type = SP_KILL_WALL; break;
520         case 6: snipe_type = SP_COLD; break;
521         case 7: snipe_type = SP_RUSH; break;
522         case 8: snipe_type = SP_PIERCE; break;
523         case 9: snipe_type = SP_EVILNESS; break;
524         case 10: snipe_type = SP_HOLYNESS; break;
525         case 11: snipe_type = SP_EXPLODE; break;
526         case 12: snipe_type = SP_DOUBLE; break;
527         case 13: snipe_type = SP_ELEC; break;
528         case 14: snipe_type = SP_NEEDLE; break;
529         case 15: snipe_type = SP_FINAL; break;
530         default:
531                 msg_print(_("なに?", "Zap?"));
532         }
533
534         command_cmd = 'f';
535         do_cmd_fire(sniper_ptr, snipe_type);
536
537         return (sniper_ptr->is_fired);
538 }
539
540 /*!
541  * @brief スナイパー技能コマンドのメインルーチン /
542  * @return なし
543  */
544 void do_cmd_snipe(player_type *sniper_ptr)
545 {
546         COMMAND_CODE n = 0;
547         bool cast;
548
549         if(cmd_limit_confused(sniper_ptr)) return;
550         if(cmd_limit_image(sniper_ptr)) return;
551         if(cmd_limit_stun(sniper_ptr)) return;
552
553         if (!get_snipe_power(sniper_ptr, &n, FALSE)) return;
554
555         sound(SOUND_SHOOT);
556         cast = cast_sniper_spell(sniper_ptr, n);
557
558         if (!cast) return;
559         sniper_ptr->redraw |= (PR_HP | PR_MANA);
560         sniper_ptr->window |= (PW_PLAYER);
561         sniper_ptr->window |= (PW_SPELL);
562 }
563
564 /*!
565  * @brief スナイパー技能コマンドの表示 /
566  * @return なし
567  */
568 void do_cmd_snipe_browse(player_type *sniper_ptr)
569 {
570         COMMAND_CODE n = 0;
571         int j, line;
572         char temp[62 * 4];
573
574         screen_save();
575
576         while (TRUE)
577         {
578                 if (!get_snipe_power(sniper_ptr, &n, TRUE))
579                 {
580                         screen_load();
581                         return;
582                 }
583
584                 /* Clear lines, position cursor  (really should use strlen here) */
585                 term_erase(12, 22, 255);
586                 term_erase(12, 21, 255);
587                 term_erase(12, 20, 255);
588                 term_erase(12, 19, 255);
589                 term_erase(12, 18, 255);
590
591                 shape_buffer(snipe_tips[n], 62, temp, sizeof(temp));
592                 for(j = 0, line = 19; temp[j]; j += (1 + strlen(&temp[j])))
593                 {
594                         prt(&temp[j], line, 15);
595                         line++;
596                 }
597         }
598 }