OSDN Git Service

8e12d6538918af84ce18d3b4992cfd633dca04fa
[hengbandforosx/hengbandosx.git] / src / perception / simple-perception.cpp
1 /*!
2  * @brief 疑似鑑定処理
3  * @date 2020/05/15
4  * @author Hourier
5  */
6
7 #include "perception/simple-perception.h"
8 #include "autopick/autopick.h"
9 #include "avatar/avatar.h"
10 #include "core/disturbance.h"
11 #include "core/player-update-types.h"
12 #include "core/window-redrawer.h"
13 #include "flavor/flag-inscriptions-table.h"
14 #include "flavor/flavor-describer.h"
15 #include "flavor/object-flavor-types.h"
16 #include "game-option/auto-destruction-options.h"
17 #include "game-option/disturbance-options.h"
18 #include "inventory/inventory-describer.h"
19 #include "inventory/inventory-slot-types.h"
20 #include "mutation/mutation-flag-types.h"
21 #include "object-enchant/special-object-flags.h"
22 #include "object/object-info.h"
23 #include "perception/object-perception.h"
24 #include "player/player-status-flags.h"
25 #include "system/item-entity.h"
26 #include "system/player-type-definition.h"
27 #include "timed-effect/player-confusion.h"
28 #include "timed-effect/timed-effects.h"
29 #include "view/display-messages.h"
30
31 /*!
32  * @brief 擬似鑑定を実際に行い判定を反映する
33  * @param slot 擬似鑑定を行うプレイヤーの所持リストID
34  * @param player_ptr プレイヤーへの参照ポインタ
35  * @param heavy 重度の擬似鑑定を行うならばTRUE
36  */
37 static void sense_inventory_aux(PlayerType *player_ptr, INVENTORY_IDX slot, bool heavy)
38 {
39     auto *o_ptr = &player_ptr->inventory_list[slot];
40     if (o_ptr->ident & (IDENT_SENSE)) {
41         return;
42     }
43     if (o_ptr->is_known()) {
44         return;
45     }
46
47     item_feel_type feel = (heavy ? pseudo_value_check_heavy(o_ptr) : pseudo_value_check_light(o_ptr));
48     if (!feel) {
49         return;
50     }
51
52     if ((player_ptr->muta.has(PlayerMutationType::BAD_LUCK)) && !randint0(13)) {
53         switch (feel) {
54         case FEEL_TERRIBLE: {
55             feel = FEEL_SPECIAL;
56             break;
57         }
58         case FEEL_WORTHLESS: {
59             feel = FEEL_EXCELLENT;
60             break;
61         }
62         case FEEL_CURSED: {
63             if (heavy) {
64                 feel = randint0(3) ? FEEL_GOOD : FEEL_AVERAGE;
65             } else {
66                 feel = FEEL_UNCURSED;
67             }
68             break;
69         }
70         case FEEL_AVERAGE: {
71             feel = randint0(2) ? FEEL_CURSED : FEEL_GOOD;
72             break;
73         }
74         case FEEL_GOOD: {
75             if (heavy) {
76                 feel = randint0(3) ? FEEL_CURSED : FEEL_AVERAGE;
77             } else {
78                 feel = FEEL_CURSED;
79             }
80             break;
81         }
82         case FEEL_EXCELLENT: {
83             feel = FEEL_WORTHLESS;
84             break;
85         }
86         case FEEL_SPECIAL: {
87             feel = FEEL_TERRIBLE;
88             break;
89         }
90
91         default:
92             break;
93         }
94     }
95
96     if (disturb_minor) {
97         disturb(player_ptr, false, false);
98     }
99
100     const auto item_name = describe_flavor(player_ptr, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
101     if (slot >= INVEN_MAIN_HAND) {
102 #ifdef JP
103         msg_format("%s%s(%c)は%sという感じがする...", describe_use(player_ptr, slot), item_name.data(), index_to_label(slot), game_inscriptions[feel]);
104 #else
105         msg_format("You feel the %s (%c) you are %s %s %s...", item_name.data(), index_to_label(slot), describe_use(player_ptr, slot),
106             ((o_ptr->number == 1) ? "is" : "are"), game_inscriptions[feel]);
107 #endif
108
109     } else {
110 #ifdef JP
111         msg_format("ザックの中の%s(%c)は%sという感じがする...", item_name.data(), index_to_label(slot), game_inscriptions[feel]);
112 #else
113         msg_format("You feel the %s (%c) in your pack %s %s...", item_name.data(), index_to_label(slot), ((o_ptr->number == 1) ? "is" : "are"), game_inscriptions[feel]);
114 #endif
115     }
116
117     o_ptr->ident |= (IDENT_SENSE);
118     o_ptr->feeling = feel;
119
120     autopick_alter_item(player_ptr, slot, destroy_feeling);
121     player_ptr->update |= (PU_COMBINE | PU_REORDER);
122     player_ptr->window_flags |= (PW_INVEN | PW_EQUIP);
123 }
124
125 /*!
126  * @brief 1プレイヤーターン毎に武器、防具の擬似鑑定が行われるかを判定する。
127  * @details
128  * Sense the inventory\n
129  *\n
130  *   Class 0 = Warrior --> fast and heavy\n
131  *   Class 1 = Mage    --> slow and light\n
132  *   Class 2 = Priest  --> fast but light\n
133  *   Class 3 = Rogue   --> okay and heavy\n
134  *   Class 4 = Ranger  --> slow but heavy  (changed!)\n
135  *   Class 5 = Paladin --> slow but heavy\n
136  */
137 void sense_inventory1(PlayerType *player_ptr)
138 {
139     PLAYER_LEVEL plev = player_ptr->lev;
140     bool heavy = false;
141     ItemEntity *o_ptr;
142     if (player_ptr->effects()->confusion()->is_confused()) {
143         return;
144     }
145
146     switch (player_ptr->pclass) {
147     case PlayerClassType::WARRIOR:
148     case PlayerClassType::ARCHER:
149     case PlayerClassType::SAMURAI:
150     case PlayerClassType::CAVALRY: {
151         if (0 != randint0(9000L / (plev * plev + 40))) {
152             return;
153         }
154
155         heavy = true;
156         break;
157     }
158     case PlayerClassType::SMITH: {
159         if (0 != randint0(6000L / (plev * plev + 50))) {
160             return;
161         }
162
163         heavy = true;
164         break;
165     }
166     case PlayerClassType::MAGE:
167     case PlayerClassType::HIGH_MAGE:
168     case PlayerClassType::SORCERER:
169     case PlayerClassType::MAGIC_EATER:
170     case PlayerClassType::ELEMENTALIST: {
171         if (0 != randint0(240000L / (plev + 5))) {
172             return;
173         }
174
175         break;
176     }
177     case PlayerClassType::PRIEST:
178     case PlayerClassType::BARD: {
179         if (0 != randint0(10000L / (plev * plev + 40))) {
180             return;
181         }
182
183         break;
184     }
185     case PlayerClassType::ROGUE:
186     case PlayerClassType::NINJA: {
187         if (0 != randint0(20000L / (plev * plev + 40))) {
188             return;
189         }
190
191         heavy = true;
192         break;
193     }
194     case PlayerClassType::RANGER: {
195         if (0 != randint0(95000L / (plev * plev + 40))) {
196             return;
197         }
198
199         heavy = true;
200         break;
201     }
202     case PlayerClassType::PALADIN:
203     case PlayerClassType::SNIPER: {
204         if (0 != randint0(77777L / (plev * plev + 40))) {
205             return;
206         }
207
208         heavy = true;
209         break;
210     }
211     case PlayerClassType::WARRIOR_MAGE:
212     case PlayerClassType::RED_MAGE: {
213         if (0 != randint0(75000L / (plev * plev + 40))) {
214             return;
215         }
216
217         break;
218     }
219     case PlayerClassType::MINDCRAFTER:
220     case PlayerClassType::IMITATOR:
221     case PlayerClassType::BLUE_MAGE:
222     case PlayerClassType::MIRROR_MASTER: {
223         if (0 != randint0(55000L / (plev * plev + 40))) {
224             return;
225         }
226
227         break;
228     }
229     case PlayerClassType::CHAOS_WARRIOR: {
230         if (0 != randint0(80000L / (plev * plev + 40))) {
231             return;
232         }
233
234         heavy = true;
235         break;
236     }
237     case PlayerClassType::MONK:
238     case PlayerClassType::FORCETRAINER: {
239         if (0 != randint0(20000L / (plev * plev + 40))) {
240             return;
241         }
242
243         break;
244     }
245     case PlayerClassType::TOURIST: {
246         if (0 != randint0(20000L / ((plev + 50) * (plev + 50)))) {
247             return;
248         }
249
250         heavy = true;
251         break;
252     }
253     case PlayerClassType::BEASTMASTER: {
254         if (0 != randint0(65000L / (plev * plev + 40))) {
255             return;
256         }
257
258         break;
259     }
260     case PlayerClassType::BERSERKER: {
261         heavy = true;
262         break;
263     }
264
265     case PlayerClassType::MAX:
266         break;
267     }
268
269     if (compare_virtue(player_ptr, V_KNOWLEDGE, 100, VIRTUE_LARGE)) {
270         heavy = true;
271     }
272
273     for (INVENTORY_IDX i = 0; i < INVEN_TOTAL; i++) {
274         o_ptr = &player_ptr->inventory_list[i];
275
276         if (!o_ptr->bi_id) {
277             continue;
278         }
279
280         auto okay = false;
281         switch (o_ptr->bi_key.tval()) {
282         case ItemKindType::SHOT:
283         case ItemKindType::ARROW:
284         case ItemKindType::BOLT:
285         case ItemKindType::BOW:
286         case ItemKindType::DIGGING:
287         case ItemKindType::HAFTED:
288         case ItemKindType::POLEARM:
289         case ItemKindType::SWORD:
290         case ItemKindType::BOOTS:
291         case ItemKindType::GLOVES:
292         case ItemKindType::HELM:
293         case ItemKindType::CROWN:
294         case ItemKindType::SHIELD:
295         case ItemKindType::CLOAK:
296         case ItemKindType::SOFT_ARMOR:
297         case ItemKindType::HARD_ARMOR:
298         case ItemKindType::DRAG_ARMOR:
299         case ItemKindType::CARD:
300             okay = true;
301             break;
302         default:
303             break;
304         }
305
306         if (!okay) {
307             continue;
308         }
309
310         if ((i < INVEN_MAIN_HAND) && (0 != randint0(5))) {
311             continue;
312         }
313
314         if (has_good_luck(player_ptr) && !randint0(13)) {
315             heavy = true;
316         }
317
318         sense_inventory_aux(player_ptr, i, heavy);
319     }
320 }
321
322 /*!
323  * @brief 1プレイヤーターン毎に武器、防具以外の擬似鑑定が行われるかを判定する。
324  */
325 void sense_inventory2(PlayerType *player_ptr)
326 {
327     PLAYER_LEVEL plev = player_ptr->lev;
328     ItemEntity *o_ptr;
329
330     if (player_ptr->effects()->confusion()->is_confused()) {
331         return;
332     }
333
334     switch (player_ptr->pclass) {
335     case PlayerClassType::WARRIOR:
336     case PlayerClassType::ARCHER:
337     case PlayerClassType::SAMURAI:
338     case PlayerClassType::CAVALRY:
339     case PlayerClassType::BERSERKER:
340     case PlayerClassType::SNIPER: {
341         return;
342     }
343     case PlayerClassType::SMITH:
344     case PlayerClassType::PALADIN:
345     case PlayerClassType::CHAOS_WARRIOR:
346     case PlayerClassType::IMITATOR:
347     case PlayerClassType::BEASTMASTER:
348     case PlayerClassType::NINJA: {
349         if (0 != randint0(240000L / (plev + 5))) {
350             return;
351         }
352
353         break;
354     }
355     case PlayerClassType::RANGER:
356     case PlayerClassType::WARRIOR_MAGE:
357     case PlayerClassType::RED_MAGE:
358     case PlayerClassType::MONK: {
359         if (0 != randint0(95000L / (plev * plev + 40))) {
360             return;
361         }
362
363         break;
364     }
365     case PlayerClassType::PRIEST:
366     case PlayerClassType::BARD:
367     case PlayerClassType::ROGUE:
368     case PlayerClassType::FORCETRAINER:
369     case PlayerClassType::MINDCRAFTER: {
370         if (0 != randint0(20000L / (plev * plev + 40))) {
371             return;
372         }
373
374         break;
375     }
376     case PlayerClassType::MAGE:
377     case PlayerClassType::HIGH_MAGE:
378     case PlayerClassType::SORCERER:
379     case PlayerClassType::MAGIC_EATER:
380     case PlayerClassType::MIRROR_MASTER:
381     case PlayerClassType::BLUE_MAGE:
382     case PlayerClassType::ELEMENTALIST: {
383         if (0 != randint0(9000L / (plev * plev + 40))) {
384             return;
385         }
386
387         break;
388     }
389     case PlayerClassType::TOURIST: {
390         if (0 != randint0(20000L / ((plev + 50) * (plev + 50)))) {
391             return;
392         }
393
394         break;
395     }
396
397     case PlayerClassType::MAX:
398         break;
399     }
400
401     for (INVENTORY_IDX i = 0; i < INVEN_TOTAL; i++) {
402         bool okay = false;
403         o_ptr = &player_ptr->inventory_list[i];
404         if (!o_ptr->bi_id) {
405             continue;
406         }
407
408         switch (o_ptr->bi_key.tval()) {
409         case ItemKindType::RING:
410         case ItemKindType::AMULET:
411         case ItemKindType::LITE:
412         case ItemKindType::FIGURINE: {
413             okay = true;
414             break;
415         }
416
417         default:
418             break;
419         }
420
421         if (!okay) {
422             continue;
423         }
424
425         if ((i < INVEN_MAIN_HAND) && (0 != randint0(5))) {
426             continue;
427         }
428
429         sense_inventory_aux(player_ptr, i, true);
430     }
431 }
432
433 /*!
434  * @brief 重度擬似鑑定の判断処理 / Return a "feeling" (or nullptr) about an item.  Method 1 (Heavy).
435  * @param o_ptr 擬似鑑定を行うオブジェクトの参照ポインタ。
436  * @return 擬似鑑定結果のIDを返す。
437  */
438 item_feel_type pseudo_value_check_heavy(ItemEntity *o_ptr)
439 {
440     if (o_ptr->is_fixed_or_random_artifact()) {
441         if (o_ptr->is_cursed() || o_ptr->is_broken()) {
442             return FEEL_TERRIBLE;
443         }
444
445         return FEEL_SPECIAL;
446     }
447
448     if (o_ptr->is_ego()) {
449         if (o_ptr->is_cursed() || o_ptr->is_broken()) {
450             return FEEL_WORTHLESS;
451         }
452
453         return FEEL_EXCELLENT;
454     }
455
456     if (o_ptr->is_cursed()) {
457         return FEEL_CURSED;
458     }
459
460     if (o_ptr->is_broken()) {
461         return FEEL_BROKEN;
462     }
463
464     const auto tval = o_ptr->bi_key.tval();
465     if ((tval == ItemKindType::RING) || (tval == ItemKindType::AMULET)) {
466         return FEEL_AVERAGE;
467     }
468
469     if (o_ptr->to_a > 0) {
470         return FEEL_GOOD;
471     }
472
473     if (o_ptr->to_h + o_ptr->to_d > 0) {
474         return FEEL_GOOD;
475     }
476
477     return FEEL_AVERAGE;
478 }
479
480 /*!
481  * @brief 軽度擬似鑑定の判断処理 / Return a "feeling" (or nullptr) about an item.  Method 2 (Light).
482  * @param o_ptr 擬似鑑定を行うオブジェクトの参照ポインタ。
483  * @return 擬似鑑定結果のIDを返す。
484  */
485 item_feel_type pseudo_value_check_light(ItemEntity *o_ptr)
486 {
487     if (o_ptr->is_cursed()) {
488         return FEEL_CURSED;
489     }
490
491     if (o_ptr->is_broken()) {
492         return FEEL_BROKEN;
493     }
494
495     if (o_ptr->is_fixed_or_random_artifact()) {
496         return FEEL_UNCURSED;
497     }
498
499     if (o_ptr->is_ego()) {
500         return FEEL_UNCURSED;
501     }
502
503     if (o_ptr->to_a > 0) {
504         return FEEL_UNCURSED;
505     }
506
507     if (o_ptr->to_h + o_ptr->to_d > 0) {
508         return FEEL_UNCURSED;
509     }
510
511     return FEEL_NONE;
512 }