OSDN Git Service

ca210898b02804ccced70b811e57e65a96670fa3
[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         ItemEntity *o_ptr;
93         o_ptr = &player_ptr->inventory_list[i];
94         auto flags = o_ptr->get_flags();
95
96         if (!o_ptr->is_valid()) {
97             continue;
98         }
99
100         if (o_ptr->ego_idx == EgoType::LITE_SHINE) {
101             player_ptr->cur_lite++;
102         }
103
104         if (flags.has_not(TR_DARK_SOURCE)) {
105             if (o_ptr->bi_key.tval() == ItemKindType::LITE) {
106                 const auto sval = o_ptr->bi_key.sval();
107                 if ((sval == SV_LITE_TORCH) && (o_ptr->fuel <= 0)) {
108                     continue;
109                 }
110
111                 if ((sval == SV_LITE_LANTERN) && (o_ptr->fuel <= 0)) {
112                     continue;
113                 }
114             }
115         }
116
117         POSITION rad = 0;
118         if (flags.has(TR_LITE_1) && flags.has_not(TR_DARK_SOURCE)) {
119             rad += 1;
120         }
121
122         if (flags.has(TR_LITE_2) && flags.has_not(TR_DARK_SOURCE)) {
123             rad += 2;
124         }
125
126         if (flags.has(TR_LITE_3) && flags.has_not(TR_DARK_SOURCE)) {
127             rad += 3;
128         }
129
130         if (flags.has(TR_LITE_M1)) {
131             rad -= 1;
132         }
133
134         if (flags.has(TR_LITE_M2)) {
135             rad -= 2;
136         }
137
138         if (flags.has(TR_LITE_M3)) {
139             rad -= 3;
140         }
141
142         player_ptr->cur_lite += rad;
143     }
144
145     if (player_ptr->current_floor_ptr->get_dungeon_definition().flags.has(DungeonFeatureType::DARKNESS) && player_ptr->cur_lite > 1) {
146         player_ptr->cur_lite = 1;
147     }
148
149     if (player_ptr->cur_lite <= 0 && player_ptr->lite) {
150         player_ptr->cur_lite++;
151     }
152
153     if (player_ptr->cur_lite > 14) {
154         player_ptr->cur_lite = 14;
155     }
156
157     if (player_ptr->cur_lite < 0) {
158         player_ptr->cur_lite = 0;
159     }
160
161     if (player_ptr->old_lite == player_ptr->cur_lite) {
162         return;
163     }
164
165     static constexpr auto flags = {
166         StatusRecalculatingFlag::LITE,
167         StatusRecalculatingFlag::MONSTER_LITE,
168         StatusRecalculatingFlag::MONSTER_STATUSES,
169     };
170     RedrawingFlagsUpdater::get_instance().set_flags(flags);
171     player_ptr->old_lite = player_ptr->cur_lite;
172     if (player_ptr->cur_lite > 0) {
173         set_superstealth(player_ptr, false);
174     }
175 }
176
177 /*
178  * Update the set of grids "illuminated" by the player's lite.
179  *
180  * This routine needs to use the results of "update_view()"
181  *
182  * Note that "blindness" does NOT affect "torch lite".  Be careful!
183  *
184  * We optimize most lites (all non-artifact lites) by using "obvious"
185  * facts about the results of "small" lite radius, and we attempt to
186  * list the "nearby" grids before the more "distant" ones in the
187  * array of torch-lit grids.
188  *
189  * We assume that "radius zero" lite is in fact no lite at all.
190  *
191  *     Torch     Lantern     Artifacts
192  *     (etc)
193  *                              ***
194  *                 ***         *****
195  *      ***       *****       *******
196  *      *@*       **@**       ***@***
197  *      ***       *****       *******
198  *                 ***         *****
199  *                              ***
200  */
201 void update_lite(PlayerType *player_ptr)
202 {
203     // 前回照らされていた座標たちを格納する配列。
204     std::vector<Pos2D> points;
205
206     POSITION p = player_ptr->cur_lite;
207     FloorType *const floor_ptr = player_ptr->current_floor_ptr;
208
209     // 前回照らされていた座標たちを記録。
210     for (int i = 0; i < floor_ptr->lite_n; i++) {
211         const POSITION y = floor_ptr->lite_y[i];
212         const POSITION x = floor_ptr->lite_x[i];
213
214         floor_ptr->grid_array[y][x].info &= ~(CAVE_LITE);
215         floor_ptr->grid_array[y][x].info |= CAVE_TEMP;
216
217         points.emplace_back(y, x);
218     }
219
220     floor_ptr->lite_n = 0;
221     if (p >= 1) {
222         cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x);
223         cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x);
224         cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x);
225         cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x + 1);
226         cave_lite_hack(floor_ptr, player_ptr->y, 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         cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x + 1);
230         cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x - 1);
231     }
232
233     if (p >= 2) {
234         if (cave_los_bold(floor_ptr, player_ptr->y + 1, player_ptr->x)) {
235             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x);
236             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x + 1);
237             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x - 1);
238         }
239
240         if (cave_los_bold(floor_ptr, player_ptr->y - 1, player_ptr->x)) {
241             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x);
242             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x + 1);
243             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x - 1);
244         }
245
246         if (cave_los_bold(floor_ptr, player_ptr->y, player_ptr->x + 1)) {
247             cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x + 2);
248             cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x + 2);
249             cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x + 2);
250         }
251
252         if (cave_los_bold(floor_ptr, player_ptr->y, player_ptr->x - 1)) {
253             cave_lite_hack(floor_ptr, player_ptr->y, player_ptr->x - 2);
254             cave_lite_hack(floor_ptr, player_ptr->y + 1, player_ptr->x - 2);
255             cave_lite_hack(floor_ptr, player_ptr->y - 1, player_ptr->x - 2);
256         }
257     }
258
259     if (p >= 3) {
260         int d;
261         if (p > 14) {
262             p = 14;
263         }
264
265         if (cave_los_bold(floor_ptr, player_ptr->y + 1, player_ptr->x + 1)) {
266             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x + 2);
267         }
268
269         if (cave_los_bold(floor_ptr, player_ptr->y + 1, player_ptr->x - 1)) {
270             cave_lite_hack(floor_ptr, player_ptr->y + 2, player_ptr->x - 2);
271         }
272
273         if (cave_los_bold(floor_ptr, player_ptr->y - 1, player_ptr->x + 1)) {
274             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x + 2);
275         }
276
277         if (cave_los_bold(floor_ptr, player_ptr->y - 1, player_ptr->x - 1)) {
278             cave_lite_hack(floor_ptr, player_ptr->y - 2, player_ptr->x - 2);
279         }
280
281         POSITION min_y = player_ptr->y - p;
282         if (min_y < 0) {
283             min_y = 0;
284         }
285
286         POSITION max_y = player_ptr->y + p;
287         if (max_y > floor_ptr->height - 1) {
288             max_y = floor_ptr->height - 1;
289         }
290
291         POSITION min_x = player_ptr->x - p;
292         if (min_x < 0) {
293             min_x = 0;
294         }
295
296         POSITION max_x = player_ptr->x + p;
297         if (max_x > floor_ptr->width - 1) {
298             max_x = floor_ptr->width - 1;
299         }
300
301         for (POSITION y = min_y; y <= max_y; y++) {
302             for (POSITION x = min_x; x <= max_x; x++) {
303                 int dy = (player_ptr->y > y) ? (player_ptr->y - y) : (y - player_ptr->y);
304                 int dx = (player_ptr->x > x) ? (player_ptr->x - x) : (x - player_ptr->x);
305                 if ((dy <= 2) && (dx <= 2)) {
306                     continue;
307                 }
308
309                 d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
310                 if (d > p) {
311                     continue;
312                 }
313
314                 if (floor_ptr->grid_array[y][x].info & CAVE_VIEW) {
315                     cave_lite_hack(floor_ptr, y, x);
316                 }
317             }
318         }
319     }
320
321     for (int i = 0; i < floor_ptr->lite_n; i++) {
322         POSITION y = floor_ptr->lite_y[i];
323         POSITION x = floor_ptr->lite_x[i];
324         auto *g_ptr = &floor_ptr->grid_array[y][x];
325         if (g_ptr->info & CAVE_TEMP) {
326             continue;
327         }
328
329         cave_note_and_redraw_later(floor_ptr, y, x);
330     }
331
332     // 前回照らされていた座標たちのうち、状態が変わったものについて再描画フラグを立てる。
333     for (const auto &[y, x] : points) {
334         auto *g_ptr = &floor_ptr->grid_array[y][x];
335         g_ptr->info &= ~(CAVE_TEMP);
336         if (g_ptr->info & CAVE_LITE) {
337             continue;
338         }
339
340         cave_redraw_later(floor_ptr, y, x);
341     }
342
343     RedrawingFlagsUpdater::get_instance().set_flag(StatusRecalculatingFlag::DELAY_VISIBILITY);
344 }