OSDN Git Service

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