OSDN Git Service

Merge pull request #1527 from habu1010/feature/new-smith-activation
[hengbandforosx/hengbandosx.git] / src / view / status-first-page.cpp
1 /*!
2  * @file status-first-page.c
3  * @brief キャラ基本情報及び技能値の表示
4  * @date 2020/02/23
5  * @author Hourier
6  */
7
8 #include "view/status-first-page.h"
9 #include "artifact/fixed-art-types.h"
10 #include "combat/attack-power-table.h"
11 #include "combat/shoot.h"
12 #include "display-util.h"
13 #include "game-option/text-display-options.h"
14 #include "inventory/inventory-slot-types.h"
15 #include "mutation/mutation-flag-types.h"
16 #include "object-enchant/special-object-flags.h"
17 #include "object-enchant/tr-types.h"
18 #include "object/object-flags.h"
19 #include "perception/object-perception.h"
20 #include "player-info/equipment-info.h"
21 #include "player-status/player-hand-types.h"
22 #include "player/player-status-flags.h"
23 #include "player/special-defense-types.h"
24 #include "sv-definition/sv-weapon-types.h"
25 #include "system/object-type-definition.h"
26 #include "system/player-type-definition.h"
27 #include "term/term-color-types.h"
28 #include "util/bit-flags-calculator.h"
29
30 static TERM_COLOR likert_color = TERM_WHITE;
31
32 /*!
33  * @brief
34  * @param player_ptr プレイヤーへの参照ポインタ
35  * @param o_ptr 装備中の弓への参照ポインタ
36  * @param shots 射撃回数
37  * @param shot_frac 射撃速度
38  */
39 static void calc_shot_params(player_type *player_ptr, object_type *o_ptr, int *shots, int *shot_frac)
40 {
41     if (o_ptr->k_idx == 0)
42         return;
43
44     ENERGY energy_fire = bow_energy(o_ptr->sval);
45     *shots = player_ptr->num_fire * 100;
46     *shot_frac = ((*shots) * 100 / energy_fire) % 100;
47     *shots = (*shots) / energy_fire;
48     if (o_ptr->name1 != ART_CRIMSON)
49         return;
50
51     *shots = 1;
52     *shot_frac = 0;
53     if (player_ptr->pclass != CLASS_ARCHER)
54         return;
55
56     if (player_ptr->lev >= 10)
57         (*shots)++;
58     if (player_ptr->lev >= 30)
59         (*shots)++;
60     if (player_ptr->lev >= 45)
61         (*shots)++;
62 }
63
64 /*!
65  * @brief 武器装備に制限のあるクラスで、直接攻撃のダメージを計算する
66  * @param player_ptr プレイヤーへの参照ポインタ
67  * @param hand 手 (利き手が0、反対の手が1…のはず)
68  * @param damage 直接攻撃のダメージ
69  * @param basedam 素手における直接攻撃のダメージ
70  * @param o_ptr 装備中の武器への参照ポインタ
71  * @return 利き手ならTRUE、反対の手ならFALSE
72  */
73 static bool calc_weapon_damage_limit(player_type *player_ptr, int hand, int *damage, int *basedam, object_type *o_ptr)
74 {
75     PLAYER_LEVEL level = player_ptr->lev;
76     if (hand > 0) {
77         damage[hand] = 0;
78         return false;
79     }
80
81     if (player_ptr->pclass == CLASS_FORCETRAINER)
82         level = MAX(1, level - 3);
83     if (player_ptr->special_defense & KAMAE_BYAKKO)
84         *basedam = monk_ave_damage[level][1];
85     else if (player_ptr->special_defense & (KAMAE_GENBU | KAMAE_SUZAKU))
86         *basedam = monk_ave_damage[level][2];
87     else
88         *basedam = monk_ave_damage[level][0];
89
90     damage[hand] += *basedam;
91     if ((o_ptr->tval == TV_SWORD) && (o_ptr->sval == SV_POISON_NEEDLE))
92         damage[hand] = 1;
93     if (damage[hand] < 0)
94         damage[hand] = 0;
95
96     return true;
97 }
98
99 /*!
100  * @brief 片手あたりのダメージ量を計算する
101  * @param o_ptr 装備中の武器への参照ポインタ
102  * @param hand 手
103  * @param damage 直接攻撃のダメージ
104  * @param basedam 素手における直接攻撃のダメージ
105  * @return 素手ならFALSE、武器を持っていればTRUE
106  */
107 static bool calc_weapon_one_hand(object_type *o_ptr, int hand, int *damage, int *basedam)
108 {
109     if (o_ptr->k_idx == 0)
110         return false;
111
112     *basedam = 0;
113     damage[hand] += *basedam;
114     if ((o_ptr->tval == TV_SWORD) && (o_ptr->sval == SV_POISON_NEEDLE))
115         damage[hand] = 1;
116
117     if (damage[hand] < 0)
118         damage[hand] = 0;
119
120     return true;
121 }
122
123 /*!
124  * @brief ヴォーパル武器等によるダメージ強化
125  * @param player_ptr プレイヤーへの参照ポインタ
126  * @param o_ptr 装備中の武器への参照ポインタ
127  * @param basedam 素手における直接攻撃のダメージ
128  * @param flgs オブジェクトフラグ群
129  * @return 強化後の素手ダメージ
130  */
131 static int strengthen_basedam(player_type *player_ptr, object_type *o_ptr, int basedam, const TrFlags &flgs)
132 {
133     if (o_ptr->is_fully_known() && ((o_ptr->name1 == ART_VORPAL_BLADE) || (o_ptr->name1 == ART_CHAINSWORD))) {
134         /* vorpal blade */
135         basedam *= 5;
136         basedam /= 3;
137     } else if (flgs.has(TR_VORPAL)) {
138         /* vorpal flag only */
139         basedam *= 11;
140         basedam /= 9;
141     }
142
143     // 理力
144     bool is_force = player_ptr->pclass != CLASS_SAMURAI;
145     is_force &= flgs.has(TR_FORCE_WEAPON);
146     is_force &= player_ptr->csp > (o_ptr->dd * o_ptr->ds / 5);
147     if (is_force)
148         basedam = basedam * 7 / 2;
149
150     return basedam;
151 }
152
153 /*!
154  * @brief 技能ランクの表示基準を定める
155  * Returns a "rating" of x depending on y
156  * @param x 技能値
157  * @param y 技能値に対するランク基準比
158  */
159 static concptr likert(int x, int y)
160 {
161     static char dummy[20] = "", dummy2[20] = "";
162     memset(dummy, 0, strlen(dummy));
163     memset(dummy2, 0, strlen(dummy2));
164     if (y <= 0)
165         y = 1;
166
167     if (show_actual_value)
168         sprintf(dummy, "%3d-", x);
169
170     if (x < 0) {
171         likert_color = TERM_L_DARK;
172         strcat(dummy, _("最低", "Very Bad"));
173         return dummy;
174     }
175
176     switch ((x / y)) {
177     case 0:
178     case 1: {
179         likert_color = TERM_RED;
180         strcat(dummy, _("悪い", "Bad"));
181         break;
182     }
183     case 2: {
184         likert_color = TERM_L_RED;
185         strcat(dummy, _("劣る", "Poor"));
186         break;
187     }
188     case 3:
189     case 4: {
190         likert_color = TERM_ORANGE;
191         strcat(dummy, _("普通", "Fair"));
192         break;
193     }
194     case 5: {
195         likert_color = TERM_YELLOW;
196         strcat(dummy, _("良い", "Good"));
197         break;
198     }
199     case 6: {
200         likert_color = TERM_YELLOW;
201         strcat(dummy, _("大変良い", "Very Good"));
202         break;
203     }
204     case 7:
205     case 8: {
206         likert_color = TERM_L_GREEN;
207         strcat(dummy, _("卓越", "Excellent"));
208         break;
209     }
210     case 9:
211     case 10:
212     case 11:
213     case 12:
214     case 13: {
215         likert_color = TERM_GREEN;
216         strcat(dummy, _("超越", "Superb"));
217         break;
218     }
219     case 14:
220     case 15:
221     case 16:
222     case 17: {
223         likert_color = TERM_BLUE;
224         strcat(dummy, _("英雄的", "Heroic"));
225         break;
226     }
227     default: {
228         likert_color = TERM_VIOLET;
229         sprintf(dummy2, _("伝説的[%d]", "Legendary[%d]"), (int)((((x / y) - 17) * 5) / 2));
230         strcat(dummy, dummy2);
231         break;
232     }
233     }
234
235     return dummy;
236 }
237
238 /*!
239  * @brief 弓+両手の武器それぞれについてダメージを計算する
240  * @param player_ptr プレイヤーへの参照ポインタ
241  * @param damage 直接攻撃のダメージ
242  * @param to_h 命中補正
243  */
244 static void calc_two_hands(player_type *player_ptr, int *damage, int *to_h)
245 {
246     object_type *o_ptr;
247     o_ptr = &player_ptr->inventory_list[INVEN_BOW];
248
249     for (int i = 0; i < 2; i++) {
250         int basedam;
251         damage[i] = player_ptr->dis_to_d[i] * 100;
252         if (((player_ptr->pclass == CLASS_MONK) || (player_ptr->pclass == CLASS_FORCETRAINER)) && (empty_hands(player_ptr, true) & EMPTY_HAND_MAIN)) {
253             if (!calc_weapon_damage_limit(player_ptr, i, damage, &basedam, o_ptr))
254                 break;
255
256             continue;
257         }
258
259         o_ptr = &player_ptr->inventory_list[INVEN_MAIN_HAND + i];
260         if (!calc_weapon_one_hand(o_ptr, i, damage, &basedam))
261             continue;
262
263         to_h[i] = 0;
264         bool poison_needle = false;
265         if ((o_ptr->tval == TV_SWORD) && (o_ptr->sval == SV_POISON_NEEDLE))
266             poison_needle = true;
267         if (o_ptr->is_known()) {
268             damage[i] += o_ptr->to_d * 100;
269             to_h[i] += o_ptr->to_h;
270         }
271
272         basedam = ((o_ptr->dd + player_ptr->to_dd[i]) * (o_ptr->ds + player_ptr->to_ds[i] + 1)) * 50;
273         auto flgs = object_flags_known(o_ptr);
274
275         bool impact = player_ptr->impact != 0;
276         basedam = calc_expect_crit(player_ptr, o_ptr->weight, to_h[i], basedam, player_ptr->dis_to_h[i], poison_needle, impact);
277         basedam = strengthen_basedam(player_ptr, o_ptr, basedam, flgs);
278         damage[i] += basedam;
279         if ((o_ptr->tval == TV_SWORD) && (o_ptr->sval == SV_POISON_NEEDLE))
280             damage[i] = 1;
281         if (damage[i] < 0)
282             damage[i] = 0;
283     }
284 }
285
286 /*!
287  * @brief キャラ基本情報及び技能値をメインウィンドウに表示する
288  * @param player_ptr プレイヤーへの参照ポインタ
289  * @param xthb 武器等を含めた最終命中率
290  * @param damage 打撃修正
291  * @param shots 射撃回数
292  * @param shot_frac 射撃速度
293  * @param display_player_one_line 1行表示用のコールバック関数
294  */
295 static void display_first_page(player_type *player_ptr, int xthb, int *damage, int shots, int shot_frac)
296 {
297     int xthn = player_ptr->skill_thn + (player_ptr->to_h_m * BTH_PLUS_ADJ);
298
299     int muta_att = 0;
300     if (player_ptr->muta.has(MUTA::HORNS))
301         muta_att++;
302     if (player_ptr->muta.has(MUTA::SCOR_TAIL))
303         muta_att++;
304     if (player_ptr->muta.has(MUTA::BEAK))
305         muta_att++;
306     if (player_ptr->muta.has(MUTA::TRUNK))
307         muta_att++;
308     if (player_ptr->muta.has(MUTA::TENTACLES))
309         muta_att++;
310
311     int blows1 = can_attack_with_main_hand(player_ptr) ? player_ptr->num_blow[0] : 0;
312     int blows2 = can_attack_with_sub_hand(player_ptr) ? player_ptr->num_blow[1] : 0;
313     int xdis = player_ptr->skill_dis;
314     int xdev = player_ptr->skill_dev;
315     int xsav = player_ptr->skill_sav;
316     int xstl = player_ptr->skill_stl;
317     int xsrh = player_ptr->skill_srh;
318     int xfos = player_ptr->skill_fos;
319     int xdig = player_ptr->skill_dig;
320
321     concptr desc = likert(xthn, 12);
322     display_player_one_line(ENTRY_SKILL_FIGHT, desc, likert_color);
323
324     desc = likert(xthb, 12);
325     display_player_one_line(ENTRY_SKILL_SHOOT, desc, likert_color);
326
327     desc = likert(xsav, 7);
328     display_player_one_line(ENTRY_SKILL_SAVING, desc, likert_color);
329
330     desc = likert((xstl > 0) ? xstl : -1, 1);
331     display_player_one_line(ENTRY_SKILL_STEALTH, desc, likert_color);
332
333     desc = likert(xfos, 6);
334     display_player_one_line(ENTRY_SKILL_PERCEP, desc, likert_color);
335
336     desc = likert(xsrh, 6);
337     display_player_one_line(ENTRY_SKILL_SEARCH, desc, likert_color);
338
339     desc = likert(xdis, 8);
340     display_player_one_line(ENTRY_SKILL_DISARM, desc, likert_color);
341
342     desc = likert(xdev, 6);
343     display_player_one_line(ENTRY_SKILL_DEVICE, desc, likert_color);
344
345     desc = likert(xdev, 6);
346     display_player_one_line(ENTRY_SKILL_DEVICE, desc, likert_color);
347
348     desc = likert(xdig, 4);
349     display_player_one_line(ENTRY_SKILL_DIG, desc, likert_color);
350
351     if (!muta_att)
352         display_player_one_line(ENTRY_BLOWS, format("%d+%d", blows1, blows2), TERM_L_BLUE);
353     else
354         display_player_one_line(ENTRY_BLOWS, format("%d+%d+%d", blows1, blows2, muta_att), TERM_L_BLUE);
355
356     display_player_one_line(ENTRY_SHOTS, format("%d.%02d", shots, shot_frac), TERM_L_BLUE);
357
358     if ((damage[0] + damage[1]) == 0)
359         desc = "nil!";
360     else
361         desc = format("%d+%d", blows1 * damage[0] / 100, blows2 * damage[1] / 100);
362
363     display_player_one_line(ENTRY_AVG_DMG, desc, TERM_L_BLUE);
364     display_player_one_line(ENTRY_INFRA, format("%d feet", player_ptr->see_infra * 10), TERM_WHITE);
365 }
366
367 /*!
368  * @brief プレイヤーステータスの1ページ目各種詳細をまとめて表示する
369  * Prints ratings on certain abilities
370  * @param player_ptr プレイヤーへの参照ポインタ
371  * @param display_player_one_line 1行表示用のコールバック関数
372  * @details
373  * This code is "imitated" elsewhere to "dump" a character sheet.
374  */
375 void display_player_various(player_type *player_ptr)
376 {
377     object_type *o_ptr;
378     o_ptr = &player_ptr->inventory_list[INVEN_BOW];
379     int tmp = player_ptr->to_h_b + o_ptr->to_h;
380     int xthb = player_ptr->skill_thb + (tmp * BTH_PLUS_ADJ);
381     int shots = 0;
382     int shot_frac = 0;
383     calc_shot_params(player_ptr, o_ptr, &shots, &shot_frac);
384
385     int damage[2];
386     int to_h[2];
387     calc_two_hands(player_ptr, damage, to_h);
388     display_first_page(player_ptr, xthb, damage, shots, shot_frac);
389 }