OSDN Git Service

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