OSDN Git Service

Merge branch 'master' of https://github.com/hengband/hengband
[hengbandforosx/hengbandosx.git] / src / specific-object / torch.cpp
1 #include "specific-object/torch.h"
2 #include "dungeon/dungeon-flag-types.h"
3 #include "floor/cave.h"
4 #include "grid/grid.h"
5 #include "inventory/inventory-slot-types.h"
6 #include "mind/mind-ninja.h"
7 #include "object-enchant/object-ego.h"
8 #include "object-enchant/tr-types.h"
9 #include "object/tval-types.h"
10 #include "player/special-defense-types.h"
11 #include "sv-definition/sv-lite-types.h"
12 #include "system/dungeon-info.h"
13 #include "system/floor-type-definition.h"
14 #include "system/grid-type-definition.h"
15 #include "system/item-entity.h"
16 #include "system/player-type-definition.h"
17 #include "system/redrawing-flags-updater.h"
18 #include "util/bit-flags-calculator.h"
19 #include "util/point-2d.h"
20 #include <vector>
21
22 /*!
23  * @brief 該当オブジェクトが残量アリの松明か否かを判定。
24  * @param o_ptr オブジェクトの構造体参照ポインタ
25  * @return 残量アリの松明ならtrue
26  */
27 bool is_active_torch(ItemEntity *o_ptr)
28 {
29     return (o_ptr->bi_key == BaseitemKey(ItemKindType::LITE, SV_LITE_TORCH)) && (o_ptr->fuel > 0);
30 }
31
32 /*!
33  * @brief 投擲時たいまつに投げやすい/焼棄/アンデッドスレイの特別効果を返す。
34  * Torches have special abilities when they are flaming.
35  * @param o_ptr 投擲するオブジェクトの構造体参照ポインタ
36  * @param flags 特別に追加するフラグを返す参照ポインタ
37  */
38 void torch_flags(ItemEntity *o_ptr, TrFlags &flags)
39 {
40     if (!is_active_torch(o_ptr)) {
41         return;
42     }
43
44     flags.set(TR_BRAND_FIRE);
45     flags.set(TR_KILL_UNDEAD);
46     flags.set(TR_THROW);
47 }
48
49 /*!
50  * @brief 投擲時たいまつにダイスを与える。
51  * Torches have special abilities when they are flaming.
52  * @param o_ptr 投擲するオブジェクトの構造体参照ポインタ
53  * @param dd 特別なダイス数を返す参照ポインタ
54  * @param ds 特別なダイス面数を返す参照ポインタ
55  */
56 void torch_dice(ItemEntity *o_ptr, DICE_NUMBER *dd, DICE_SID *ds)
57 {
58     if (!is_active_torch(o_ptr)) {
59         return;
60     }
61
62     *dd = 1;
63     *ds = 6;
64 }
65
66 /*!
67  * @brief 投擲時命中したたいまつの寿命を縮める。
68  * Torches have special abilities when they are flaming.
69  * @param o_ptr 投擲するオブジェクトの構造体参照ポインタ
70  */
71 void torch_lost_fuel(ItemEntity *o_ptr)
72 {
73     if (!is_active_torch(o_ptr)) {
74         return;
75     }
76
77     o_ptr->fuel -= FUEL_TORCH / 25;
78     if (o_ptr->fuel < 0) {
79         o_ptr->fuel = 0;
80     }
81 }
82
83 /*!
84  * @brief プレイヤーの光源半径を計算する / Extract and set the current "lite radius"
85  * @details
86  * SWD: Experimental modification: multiple light sources have additive effect.
87  */
88 void update_lite_radius(PlayerType *player_ptr)
89 {
90     player_ptr->cur_lite = 0;
91     for (int i = INVEN_MAIN_HAND; i < INVEN_TOTAL; i++) {
92         const auto *o_ptr = &player_ptr->inventory_list[i];
93         if (!o_ptr->is_valid()) {
94             continue;
95         }
96
97         if (o_ptr->ego_idx == EgoType::LITE_SHINE) {
98             player_ptr->cur_lite++;
99         }
100
101         const auto flags = o_ptr->get_flags();
102         if (flags.has_not(TR_DARK_SOURCE)) {
103             if (o_ptr->bi_key.tval() == ItemKindType::LITE) {
104                 const auto sval = o_ptr->bi_key.sval();
105                 if ((sval == SV_LITE_TORCH) && (o_ptr->fuel <= 0)) {
106                     continue;
107                 }
108
109                 if ((sval == SV_LITE_LANTERN) && (o_ptr->fuel <= 0)) {
110                     continue;
111                 }
112             }
113         }
114
115         POSITION rad = 0;
116         if (flags.has(TR_LITE_1) && flags.has_not(TR_DARK_SOURCE)) {
117             rad += 1;
118         }
119
120         if (flags.has(TR_LITE_2) && flags.has_not(TR_DARK_SOURCE)) {
121             rad += 2;
122         }
123
124         if (flags.has(TR_LITE_3) && flags.has_not(TR_DARK_SOURCE)) {
125             rad += 3;
126         }
127
128         if (flags.has(TR_LITE_M1)) {
129             rad -= 1;
130         }
131
132         if (flags.has(TR_LITE_M2)) {
133             rad -= 2;
134         }
135
136         if (flags.has(TR_LITE_M3)) {
137             rad -= 3;
138         }
139
140         player_ptr->cur_lite += rad;
141     }
142
143     if (player_ptr->current_floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS) && player_ptr->cur_lite > 1) {
144         player_ptr->cur_lite = 1;
145     }
146
147     if (player_ptr->cur_lite <= 0 && player_ptr->lite) {
148         player_ptr->cur_lite++;
149     }
150
151     if (player_ptr->cur_lite > 14) {
152         player_ptr->cur_lite = 14;
153     }
154
155     if (player_ptr->cur_lite < 0) {
156         player_ptr->cur_lite = 0;
157     }
158
159     if (player_ptr->old_lite == player_ptr->cur_lite) {
160         return;
161     }
162
163     static constexpr auto flags = {
164         StatusRecalculatingFlag::LITE,
165         StatusRecalculatingFlag::MONSTER_LITE,
166         StatusRecalculatingFlag::MONSTER_STATUSES,
167     };
168     RedrawingFlagsUpdater::get_instance().set_flags(flags);
169     player_ptr->old_lite = player_ptr->cur_lite;
170     if (player_ptr->cur_lite > 0) {
171         set_superstealth(player_ptr, false);
172     }
173 }
174
175 /*
176  * Update the set of grids "illuminated" by the player's lite.
177  *
178  * This routine needs to use the results of "update_view()"
179  *
180  * Note that "blindness" does NOT affect "torch lite".  Be careful!
181  *
182  * We optimize most lites (all non-artifact lites) by using "obvious"
183  * facts about the results of "small" lite radius, and we attempt to
184  * list the "nearby" grids before the more "distant" ones in the
185  * array of torch-lit grids.
186  *
187  * We assume that "radius zero" lite is in fact no lite at all.
188  *
189  *     Torch     Lantern     Artifacts
190  *     (etc)
191  *                              ***
192  *                 ***         *****
193  *      ***       *****       *******
194  *      *@*       **@**       ***@***
195  *      ***       *****       *******
196  *                 ***         *****
197  *                              ***
198  */
199 void update_lite(PlayerType *player_ptr)
200 {
201     // 前回照らされていた座標たちを格納する配列。
202     std::vector<Pos2D> points;
203
204     POSITION p = player_ptr->cur_lite;
205     FloorType *const floor_ptr = player_ptr->current_floor_ptr;
206
207     // 前回照らされていた座標たちを記録。
208     for (int i = 0; i < floor_ptr->lite_n; i++) {
209         const POSITION y = floor_ptr->lite_y[i];
210         const POSITION x = floor_ptr->lite_x[i];
211
212         floor_ptr->grid_array[y][x].info &= ~(CAVE_LITE);
213         floor_ptr->grid_array[y][x].info |= CAVE_TEMP;
214
215         points.emplace_back(y, x);
216     }
217
218     floor_ptr->lite_n = 0;
219     if (p >= 1) {
220         cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x);
221         cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x);
222         cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x);
223         cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x + 1);
224         cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x - 1);
225         cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x + 1);
226         cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x - 1);
227         cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x + 1);
228         cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x - 1);
229     }
230
231     if (p >= 2) {
232         if (cave_los_bold(floor_ptr, player_ptr->y + 1, player_ptr->x)) {
233             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x);
234             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x + 1);
235             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x - 1);
236         }
237
238         if (cave_los_bold(floor_ptr, player_ptr->y - 1, player_ptr->x)) {
239             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x);
240             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x + 1);
241             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x - 1);
242         }
243
244         if (cave_los_bold(floor_ptr, player_ptr->y, player_ptr->x + 1)) {
245             cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x + 2);
246             cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x + 2);
247             cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x + 2);
248         }
249
250         if (cave_los_bold(floor_ptr, player_ptr->y, player_ptr->x - 1)) {
251             cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x - 2);
252             cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x - 2);
253             cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x - 2);
254         }
255     }
256
257     if (p >= 3) {
258         int d;
259         if (p > 14) {
260             p = 14;
261         }
262
263         if (cave_los_bold(floor_ptr, player_ptr->y + 1, player_ptr->x + 1)) {
264             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x + 2);
265         }
266
267         if (cave_los_bold(floor_ptr, player_ptr->y + 1, player_ptr->x - 1)) {
268             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x - 2);
269         }
270
271         if (cave_los_bold(floor_ptr, player_ptr->y - 1, player_ptr->x + 1)) {
272             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x + 2);
273         }
274
275         if (cave_los_bold(floor_ptr, player_ptr->y - 1, player_ptr->x - 1)) {
276             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x - 2);
277         }
278
279         POSITION min_y = player_ptr->y - p;
280         if (min_y < 0) {
281             min_y = 0;
282         }
283
284         POSITION max_y = player_ptr->y + p;
285         if (max_y > floor_ptr->height - 1) {
286             max_y = floor_ptr->height - 1;
287         }
288
289         POSITION min_x = player_ptr->x - p;
290         if (min_x < 0) {
291             min_x = 0;
292         }
293
294         POSITION max_x = player_ptr->x + p;
295         if (max_x > floor_ptr->width - 1) {
296             max_x = floor_ptr->width - 1;
297         }
298
299         for (POSITION y = min_y; y <= max_y; y++) {
300             for (POSITION x = min_x; x <= max_x; x++) {
301                 int dy = (player_ptr->y > y) ? (player_ptr->y - y) : (y - player_ptr->y);
302                 int dx = (player_ptr->x > x) ? (player_ptr->x - x) : (x - player_ptr->x);
303                 if ((dy <= 2) && (dx <= 2)) {
304                     continue;
305                 }
306
307                 d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
308                 if (d > p) {
309                     continue;
310                 }
311
312                 if (floor_ptr->grid_array[y][x].info & CAVE_VIEW) {
313                     cave_lite_hack(floor_ptr, y, x);
314                 }
315             }
316         }
317     }
318
319     for (int i = 0; i < floor_ptr->lite_n; i++) {
320         POSITION y = floor_ptr->lite_y[i];
321         POSITION x = floor_ptr->lite_x[i];
322         auto *g_ptr = &floor_ptr->grid_array[y][x];
323         if (g_ptr->info & CAVE_TEMP) {
324             continue;
325         }
326
327         cave_note_and_redraw_later(floor_ptr, y, x);
328     }
329
330     // 前回照らされていた座標たちのうち、状態が変わったものについて再描画フラグを立てる。
331     for (const auto &[y, x] : points) {
332         auto *g_ptr = &floor_ptr->grid_array[y][x];
333         g_ptr->info &= ~(CAVE_TEMP);
334         if (g_ptr->info & CAVE_LITE) {
335             continue;
336         }
337
338         cave_redraw_later(floor_ptr, y, x);
339     }
340
341     RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::DELAY_VISIBILITY);
342 }