OSDN Git Service

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