OSDN Git Service

[Fix] 呪術と歌の詠唱時の配列範囲外アクセス
[hengbandforosx/hengbandosx.git] / src / object / object-info.cpp
1 /*!
2  * @brief オブジェクトの実装 / Object code, part 1
3  * @date 2014/01/10
4  * @author
5  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke\n
6  *\n
7  * This software may be copied and distributed for educational, research,\n
8  * and not for profit purposes provided that this copyright and statement\n
9  * are included in all such copies.  Other copyrights may also apply.\n
10  * 2014 Deskull rearranged comment for Doxygen.\n
11  */
12
13 #include "object/object-info.h"
14 #include "artifact/artifact-info.h"
15 #include "artifact/fixed-art-types.h"
16 #include "artifact/random-art-effects.h"
17 #include "inventory/inventory-slot-types.h"
18 #include "monster-race/monster-race.h"
19 #include "object-enchant/activation-info-table.h"
20 #include "object-enchant/dragon-breaths-table.h"
21 #include "object-enchant/object-ego.h"
22 #include "object/object-flags.h"
23 #include "player-base/player-class.h"
24 #include "player/player-realm.h"
25 #include "realm/realm-names-table.h"
26 #include "sv-definition/sv-other-types.h"
27 #include "sv-definition/sv-ring-types.h"
28 #include "system/baseitem-info.h"
29 #include "system/floor-type-definition.h"
30 #include "system/item-entity.h"
31 #include "system/monster-race-info.h"
32 #include "system/player-type-definition.h"
33 #include "term/term-color-types.h"
34 #include "util/bit-flags-calculator.h"
35 #include "util/int-char-converter.h"
36 #include <sstream>
37
38 /*!
39  * @brief オブジェクトの発動効果名称を返す(サブルーチン/ブレス)
40  * @param o_ptr 名称を取得する元のオブジェクト構造体参照ポインタ
41  * @return std::string 発動名称を返す文字列ポインタ
42  */
43 static std::string item_activation_dragon_breath(ItemEntity *o_ptr)
44 {
45     std::string desc = _("", "breathe ");
46     int n = 0;
47
48     auto flags = object_flags(o_ptr);
49
50     for (int i = 0; dragonbreath_info[i].flag != 0; i++) {
51         if (flags.has(dragonbreath_info[i].flag)) {
52             if (n > 0) {
53                 desc.append(_("、", ", "));
54             }
55
56             desc.append(dragonbreath_info[i].name);
57             n++;
58         }
59     }
60
61     desc.append(_("のブレス(250)", " (250)"));
62     return desc;
63 }
64
65 /*!
66  * @brief オブジェクトの発動効果名称を返す(サブルーチン/汎用)
67  * @param o_ptr 名称を取得する元のオブジェクト構造体参照ポインタ
68  * @return concptr 発動名称を返す文字列ポインタ
69  */
70 static concptr item_activation_aux(ItemEntity *o_ptr)
71 {
72     static std::string activation_detail;
73     auto tmp_act_ptr = find_activation_info(o_ptr);
74     if (!tmp_act_ptr.has_value()) {
75         return _("未定義", "something undefined");
76     }
77
78     auto *act_ptr = tmp_act_ptr.value();
79     concptr desc = act_ptr->desc;
80     std::string dragon_breath;
81     switch (act_ptr->index) {
82     case RandomArtActType::NONE:
83         break;
84     case RandomArtActType::BR_FIRE:
85         if (o_ptr->bi_key == BaseitemKey(ItemKindType::RING, SV_RING_FLAMES)) {
86             desc = _("火炎のブレス (200) と火への耐性", "breathe fire (200) and resist fire");
87         }
88         break;
89     case RandomArtActType::BR_COLD:
90         if (o_ptr->bi_key == BaseitemKey(ItemKindType::RING, SV_RING_ICE)) {
91             desc = _("冷気のブレス (200) と冷気への耐性", "breathe cold (200) and resist cold");
92         }
93         break;
94     case RandomArtActType::BR_DRAGON:
95         dragon_breath = item_activation_dragon_breath(o_ptr);
96         desc = dragon_breath.data();
97         break;
98     case RandomArtActType::AGGRAVATE:
99         if (o_ptr->is_specific_artifact(FixedArtifactId::HYOUSIGI)) {
100             desc = _("拍子木を打ちならす", "beat wooden clappers");
101         }
102         break;
103     case RandomArtActType::ACID_BALL_AND_RESISTANCE:
104         desc = _("アシッド・ボール (100) と酸への耐性", "ball of acid (100) and resist acid");
105         break;
106     case RandomArtActType::FIRE_BALL_AND_RESISTANCE:
107         desc = _("ファイア・ボール (100) と火への耐性", "ball of fire (100) and resist fire");
108         break;
109     case RandomArtActType::COLD_BALL_AND_RESISTANCE:
110         desc = _("アイス・ボール (100) と冷気への耐性", "ball of cold (100) and resist cold");
111         break;
112     case RandomArtActType::ELEC_BALL_AND_RESISTANCE:
113         desc = _("サンダー・ボール (100) と電撃への耐性", "ball of elec (100) and resist elec");
114         break;
115     case RandomArtActType::POIS_BALL_AND_RESISTANCE:
116         desc = _("ポイズン・ボール (100) と毒への耐性", "ball of poison (100) and resist elec");
117         break;
118     case RandomArtActType::RESIST_ACID:
119         desc = _("一時的な酸への耐性", "temporary resist acid");
120         break;
121     case RandomArtActType::RESIST_FIRE:
122         desc = _("一時的な火への耐性", "temporary resist fire");
123         break;
124     case RandomArtActType::RESIST_COLD:
125         desc = _("一時的な冷気への耐性", "temporary resist cold");
126         break;
127     case RandomArtActType::RESIST_ELEC:
128         desc = _("一時的な電撃への耐性", "temporary resist elec");
129         break;
130     case RandomArtActType::RESIST_POIS:
131         desc = _("一時的な毒への耐性", "temporary resist elec");
132         break;
133     default:
134         break;
135     }
136
137     /* Timeout description */
138     std::stringstream timeout;
139     int constant = act_ptr->timeout.constant;
140     int dice = act_ptr->timeout.dice;
141     if (constant == 0 && dice == 0) {
142         /* We can activate it every turn */
143         timeout << _("いつでも", "every turn");
144     } else if (constant < 0) {
145         /* Activations that have special timeout */
146         switch (act_ptr->index) {
147         case RandomArtActType::BR_FIRE:
148             timeout << _("", "every ") << (o_ptr->bi_key == BaseitemKey(ItemKindType::RING, SV_RING_FLAMES) ? 200 : 250) << _(" ターン毎", " turns");
149             break;
150         case RandomArtActType::BR_COLD:
151             timeout << _("", "every ") << (o_ptr->bi_key == BaseitemKey(ItemKindType::RING, SV_RING_ICE) ? 200 : 250) << _(" ターン毎", " turns");
152             break;
153         case RandomArtActType::TERROR:
154             timeout << _("3*(レベル+10) ターン毎", "every 3 * (level+10) turns");
155             break;
156         case RandomArtActType::MURAMASA:
157             timeout << _("確率50%で壊れる", "(destroyed 50%)");
158             break;
159         default:
160             timeout << "undefined";
161             break;
162         }
163     } else {
164         timeout << _("", "every ");
165         if (constant > 0) {
166             timeout << constant;
167             if (dice > 0) {
168                 timeout << '+';
169             }
170         }
171         if (dice > 0) {
172             timeout << 'd' << dice;
173         }
174         timeout << _(" ターン毎", " turns");
175     }
176
177     activation_detail = desc;
178     activation_detail.append(_(" : ", " ")).append(timeout.str());
179     return activation_detail.data();
180 }
181
182 /*!
183  * @brief オブジェクトの発動効果名称を返す(メインルーチン) /
184  * Determine the "Activation" (if any) for an artifact Return a string, or nullptr for "no activation"
185  * @param o_ptr 名称を取得する元のオブジェクト構造体参照ポインタ
186  * @return concptr 発動名称を返す文字列ポインタ
187  */
188 concptr activation_explanation(ItemEntity *o_ptr)
189 {
190     auto flags = object_flags(o_ptr);
191     if (flags.has_not(TR_ACTIVATE)) {
192         return _("なし", "nothing");
193     }
194
195     if (activation_index(o_ptr) > RandomArtActType::NONE) {
196         return item_activation_aux(o_ptr);
197     }
198
199     const auto tval = o_ptr->bi_key.tval();
200     if (tval == ItemKindType::WHISTLE) {
201         return _("ペット呼び寄せ : 100+d100ターン毎", "call pet every 100+d100 turns");
202     }
203
204     if (tval == ItemKindType::CAPTURE) {
205         return _("モンスターを捕える、又は解放する。", "captures or releases a monster.");
206     }
207
208     return _("何も起きない", "Nothing");
209 }
210
211 /*!
212  * @brief オブジェクト選択時の選択アルファベットラベルを返す /
213  * Convert an inventory index into a one character label
214  * @param i プレイヤーの所持/装備オブジェクトID
215  * @return 対応するアルファベット
216  * @details Note that the label does NOT distinguish inven/equip.
217  */
218 char index_to_label(int i)
219 {
220     return i < INVEN_MAIN_HAND ? I2A(i) : I2A(i - INVEN_MAIN_HAND);
221 }
222
223 /*!
224  * @brief オブジェクトの該当装備部位IDを返す /
225  * Determine which equipment slot (if any) an item likes
226  * @param o_ptr 名称を取得する元のオブジェクト構造体参照ポインタ
227  * @return 対応する装備部位ID
228  */
229 int16_t wield_slot(PlayerType *player_ptr, const ItemEntity *o_ptr)
230 {
231     switch (o_ptr->bi_key.tval()) {
232     case ItemKindType::DIGGING:
233     case ItemKindType::HAFTED:
234     case ItemKindType::POLEARM:
235     case ItemKindType::SWORD:
236         if (!player_ptr->inventory_list[INVEN_MAIN_HAND].bi_id) {
237             return INVEN_MAIN_HAND;
238         }
239
240         if (player_ptr->inventory_list[INVEN_SUB_HAND].bi_id) {
241             return INVEN_MAIN_HAND;
242         }
243
244         return INVEN_SUB_HAND;
245     case ItemKindType::CAPTURE:
246     case ItemKindType::CARD:
247     case ItemKindType::SHIELD:
248         if (!player_ptr->inventory_list[INVEN_SUB_HAND].bi_id) {
249             return INVEN_SUB_HAND;
250         }
251
252         if (player_ptr->inventory_list[INVEN_MAIN_HAND].bi_id) {
253             return INVEN_SUB_HAND;
254         }
255
256         return INVEN_MAIN_HAND;
257     case ItemKindType::BOW:
258         return INVEN_BOW;
259     case ItemKindType::RING:
260         if (!player_ptr->inventory_list[INVEN_MAIN_RING].bi_id) {
261             return INVEN_MAIN_RING;
262         }
263
264         return INVEN_SUB_RING;
265     case ItemKindType::AMULET:
266     case ItemKindType::WHISTLE:
267         return INVEN_NECK;
268     case ItemKindType::LITE:
269         return INVEN_LITE;
270     case ItemKindType::DRAG_ARMOR:
271     case ItemKindType::HARD_ARMOR:
272     case ItemKindType::SOFT_ARMOR:
273         return INVEN_BODY;
274     case ItemKindType::CLOAK:
275         return INVEN_OUTER;
276     case ItemKindType::CROWN:
277     case ItemKindType::HELM:
278         return INVEN_HEAD;
279     case ItemKindType::GLOVES:
280         return INVEN_ARMS;
281     case ItemKindType::BOOTS:
282         return INVEN_FEET;
283     default:
284         return -1;
285     }
286 }
287
288 /*!
289  * @brief tval/sval指定のベースアイテムがプレイヤーの使用可能な魔法書かどうかを返す
290  * @param player_ptr プレイヤーへの参照ポインタ
291  * @param bi_key ベースアイテム特定キー
292  * @return 使用可能な魔法書ならばTRUEを返す。
293  */
294 bool check_book_realm(PlayerType *player_ptr, const BaseitemKey &bi_key)
295 {
296     if (!bi_key.is_spell_book()) {
297         return false;
298     }
299
300     const auto tval = bi_key.tval();
301     PlayerClass pc(player_ptr);
302     if (pc.equals(PlayerClassType::SORCERER)) {
303         return is_magic(tval2realm(tval));
304     } else if (pc.equals(PlayerClassType::RED_MAGE)) {
305         if (is_magic(tval2realm(tval))) {
306             return ((tval == ItemKindType::ARCANE_BOOK) || (bi_key.sval() < 2));
307         }
308     }
309
310     return (get_realm1_book(player_ptr) == tval) || (get_realm2_book(player_ptr) == tval);
311 }
312
313 ItemEntity *ref_item(PlayerType *player_ptr, INVENTORY_IDX item)
314 {
315     auto *floor_ptr = player_ptr->current_floor_ptr;
316     return item >= 0 ? &player_ptr->inventory_list[item] : &(floor_ptr->o_list[0 - item]);
317 }