OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / status / base-status.cpp
1 #include "status/base-status.h"
2 #include "avatar/avatar.h"
3 #include "core/window-redrawer.h"
4 #include "game-option/birth-options.h"
5 #include "inventory/inventory-slot-types.h"
6 #include "object-enchant/item-feeling.h"
7 #include "object-enchant/special-object-flags.h"
8 #include "perception/object-perception.h"
9 #include "player/player-status-flags.h"
10 #include "spell-kind/spells-floor.h"
11 #include "system/item-entity.h"
12 #include "system/player-type-definition.h"
13 #include "system/redrawing-flags-updater.h"
14 #include "util/bit-flags-calculator.h"
15 #include "view/display-messages.h"
16
17 /* Array of stat "descriptions" */
18 static concptr desc_stat_pos[] = {
19     _("強く", "stronger"),
20     _("知的に", "smarter"),
21     _("賢く", "wiser"),
22     _("器用に", "more dextrous"),
23     _("健康に", "healthier"),
24     _("美しく", "cuter")
25 };
26
27 /* Array of stat "descriptions" */
28 static concptr desc_stat_neg[] = {
29     _("弱く", "weaker"),
30     _("無知に", "stupider"),
31     _("愚かに", "more naive"),
32     _("不器用に", "clumsier"),
33     _("不健康に", "more sickly"),
34     _("醜く", "uglier")
35 };
36
37 /*!
38  * @brief プレイヤーの基本能力値を増加させる / Increases a stat by one randomized level -RAK-
39  * @param stat 上昇させるステータスID
40  * @return 実際に上昇した場合TRUEを返す。
41  * @details
42  * Note that this function (used by stat potions) now restores\n
43  * the stat BEFORE increasing it.\n
44  */
45 bool inc_stat(PlayerType *player_ptr, int stat)
46 {
47     auto value = player_ptr->stat_cur[stat];
48     if (value >= player_ptr->stat_max_max[stat]) {
49         return false;
50     }
51
52     if (value < 18) {
53         value += (randint0(100) < 75) ? 1 : 2;
54     } else if (value < (player_ptr->stat_max_max[stat] - 2)) {
55         auto gain = (((player_ptr->stat_max_max[stat]) - value) / 2 + 3) / 2;
56         if (gain < 1) {
57             gain = 1;
58         }
59
60         value += randint1(gain) + gain / 2;
61         if (value > (player_ptr->stat_max_max[stat] - 1)) {
62             value = player_ptr->stat_max_max[stat] - 1;
63         }
64     } else {
65         value++;
66     }
67
68     player_ptr->stat_cur[stat] = value;
69     if (value > player_ptr->stat_max[stat]) {
70         player_ptr->stat_max[stat] = value;
71     }
72
73     RedrawingFlagsUpdater::get_instance().set_flag(StatusRedrawingFlag::BONUS);
74     return true;
75 }
76
77 /*!
78  * @brief プレイヤーの基本能力値を減少させる / Decreases a stat by an amount indended to vary from 0 to 100 percent.
79  * @param stat 減少させるステータスID
80  * @param amount 減少させる基本量
81  * @param permanent TRUEならば現在の最大値を減少させる
82  * @return 実際に減少した場合TRUEを返す。
83  * @details
84  *\n
85  * Amount could be a little higher in extreme cases to mangle very high\n
86  * stats from massive assaults.  -CWS\n
87  *\n
88  * Note that "permanent" means that the *given* amount is permanent,\n
89  * not that the new value becomes permanent.  This may not work exactly\n
90  * as expected, due to "weirdness" in the algorithm, but in general,\n
91  * if your stat is already drained, the "max" value will not drop all\n
92  * the way down to the "cur" value.\n
93  */
94 bool dec_stat(PlayerType *player_ptr, int stat, int amount, int permanent)
95 {
96     auto res = false;
97     auto cur = player_ptr->stat_cur[stat];
98     auto max = player_ptr->stat_max[stat];
99     int same = (cur == max);
100     if (cur > 3) {
101         if (cur <= 18) {
102             if (amount > 90) {
103                 cur--;
104             }
105             if (amount > 50) {
106                 cur--;
107             }
108             if (amount > 20) {
109                 cur--;
110             }
111             cur--;
112         } else {
113             int loss = (((cur - 18) / 2 + 1) / 2 + 1);
114             if (loss < 1) {
115                 loss = 1;
116             }
117
118             loss = ((randint1(loss) + loss) * amount) / 100;
119             if (loss < amount / 2) {
120                 loss = amount / 2;
121             }
122
123             cur = cur - loss;
124             if (cur < 18) {
125                 cur = (amount <= 20) ? 18 : 17;
126             }
127         }
128
129         if (cur < 3) {
130             cur = 3;
131         }
132
133         if (cur != player_ptr->stat_cur[stat]) {
134             res = true;
135         }
136     }
137
138     if (permanent && (max > 3)) {
139         chg_virtue(player_ptr, Virtue::SACRIFICE, 1);
140         if (stat == A_WIS || stat == A_INT) {
141             chg_virtue(player_ptr, Virtue::ENLIGHTEN, -2);
142         }
143
144         if (max <= 18) {
145             if (amount > 90) {
146                 max--;
147             }
148             if (amount > 50) {
149                 max--;
150             }
151             if (amount > 20) {
152                 max--;
153             }
154             max--;
155         } else {
156             int loss = (((max - 18) / 2 + 1) / 2 + 1);
157             loss = ((randint1(loss) + loss) * amount) / 100;
158             if (loss < amount / 2) {
159                 loss = amount / 2;
160             }
161
162             max = max - loss;
163             if (max < 18) {
164                 max = (amount <= 20) ? 18 : 17;
165             }
166         }
167
168         if (same || (max < cur)) {
169             max = cur;
170         }
171
172         if (max != player_ptr->stat_max[stat]) {
173             res = true;
174         }
175     }
176
177     if (res) {
178         player_ptr->stat_cur[stat] = cur;
179         player_ptr->stat_max[stat] = max;
180         auto &rfu = RedrawingFlagsUpdater::get_instance();
181         rfu.set_flag(MainWindowRedrawingFlag::ABILITY_SCORE);
182         rfu.set_flag(StatusRedrawingFlag::BONUS);
183     }
184
185     return res;
186 }
187
188 /*!
189  * @brief プレイヤーの基本能力値を回復させる / Restore a stat.  Return TRUE only if this actually makes a difference.
190  * @param stat 回復ステータスID
191  * @return 実際に回復した場合TRUEを返す。
192  */
193 bool res_stat(PlayerType *player_ptr, int stat)
194 {
195     if (player_ptr->stat_cur[stat] != player_ptr->stat_max[stat]) {
196         player_ptr->stat_cur[stat] = player_ptr->stat_max[stat];
197         auto &rfu = RedrawingFlagsUpdater::get_instance();
198         rfu.set_flag(StatusRedrawingFlag::BONUS);
199         rfu.set_flag(MainWindowRedrawingFlag::ABILITY_SCORE);
200         return true;
201     }
202
203     return false;
204 }
205
206 /*
207  * Lose a "point"
208  */
209 bool do_dec_stat(PlayerType *player_ptr, int stat)
210 {
211     bool sust = false;
212     switch (stat) {
213     case A_STR:
214         if (has_sustain_str(player_ptr)) {
215             sust = true;
216         }
217         break;
218     case A_INT:
219         if (has_sustain_int(player_ptr)) {
220             sust = true;
221         }
222         break;
223     case A_WIS:
224         if (has_sustain_wis(player_ptr)) {
225             sust = true;
226         }
227         break;
228     case A_DEX:
229         if (has_sustain_dex(player_ptr)) {
230             sust = true;
231         }
232         break;
233     case A_CON:
234         if (has_sustain_con(player_ptr)) {
235             sust = true;
236         }
237         break;
238     case A_CHR:
239         if (has_sustain_chr(player_ptr)) {
240             sust = true;
241         }
242         break;
243     }
244
245     if (sust && (!ironman_nightmare || randint0(13))) {
246         msg_format(_("%sなった気がしたが、すぐに元に戻った。", "You feel %s for a moment, but the feeling passes."), desc_stat_neg[stat]);
247         return true;
248     }
249
250     if (dec_stat(player_ptr, stat, 10, (ironman_nightmare && !randint0(13)))) {
251         msg_format(_("ひどく%sなった気がする。", "You feel %s."), desc_stat_neg[stat]);
252         return true;
253     }
254
255     return false;
256 }
257
258 /*
259  * Restore lost "points" in a stat
260  */
261 bool do_res_stat(PlayerType *player_ptr, int stat)
262 {
263     if (res_stat(player_ptr, stat)) {
264         msg_format(_("元通りに%sなった気がする。", "You feel %s."), desc_stat_pos[stat]);
265         return true;
266     }
267
268     return false;
269 }
270
271 /*
272  * Gain a "point" in a stat
273  */
274 bool do_inc_stat(PlayerType *player_ptr, int stat)
275 {
276     bool res = res_stat(player_ptr, stat);
277     if (inc_stat(player_ptr, stat)) {
278         if (stat == A_WIS) {
279             chg_virtue(player_ptr, Virtue::ENLIGHTEN, 1);
280             chg_virtue(player_ptr, Virtue::FAITH, 1);
281         } else if (stat == A_INT) {
282             chg_virtue(player_ptr, Virtue::KNOWLEDGE, 1);
283             chg_virtue(player_ptr, Virtue::ENLIGHTEN, 1);
284         } else if (stat == A_CON) {
285             chg_virtue(player_ptr, Virtue::VITALITY, 1);
286         }
287
288         msg_format(_("ワーオ!とても%sなった!", "Wow! You feel %s!"), desc_stat_pos[stat]);
289         return true;
290     }
291
292     if (res) {
293         msg_format(_("元通りに%sなった気がする。", "You feel %s."), desc_stat_pos[stat]);
294         return true;
295     }
296
297     return false;
298 }
299
300 /*
301  * Forget everything
302  */
303 bool lose_all_info(PlayerType *player_ptr)
304 {
305     chg_virtue(player_ptr, Virtue::KNOWLEDGE, -5);
306     chg_virtue(player_ptr, Virtue::ENLIGHTEN, -5);
307     for (int i = 0; i < INVEN_TOTAL; i++) {
308         auto *o_ptr = &player_ptr->inventory_list[i];
309         if (!o_ptr->is_valid() || o_ptr->is_fully_known()) {
310             continue;
311         }
312
313         o_ptr->feeling = FEEL_NONE;
314         o_ptr->ident &= ~(IDENT_EMPTY);
315         o_ptr->ident &= ~(IDENT_KNOWN);
316         o_ptr->ident &= ~(IDENT_SENSE);
317     }
318
319     auto &rfu = RedrawingFlagsUpdater::get_instance();
320     const auto flags_srf = {
321         StatusRedrawingFlag::BONUS,
322         StatusRedrawingFlag::COMBINATION,
323         StatusRedrawingFlag::REORDER,
324     };
325     rfu.set_flags(flags_srf);
326     set_bits(player_ptr->window_flags, PW_INVENTORY | PW_EQUIPMENT | PW_PLAYER | PW_FLOOR_ITEMS | PW_FOUND_ITEMS);
327     wiz_dark(player_ptr);
328     return true;
329 }