OSDN Git Service

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