1 /** @file p_lights.cpp Handle Sector base lighting effects.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 1999 Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman (PrBoom 2.2.6)
6 * @authors Copyright © 1999-2000 Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze (PrBoom 2.2.6)
7 * @authors Copyright © 1993-1996 id Software, Inc.
8 *
9 * @par License
10 * GPL: http://www.gnu.org/licenses/gpl.html
11 *
12 * <small>This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. This program is distributed in the hope that it
16 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
17 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
18 * Public License for more details. You should have received a copy of the GNU
19 * General Public License along with this program; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA</small>
22 */
23
24 #include "jheretic.h"
25
26 #include "dmu_lib.h"
27 #include "mapstatereader.h"
28 #include "mapstatewriter.h"
29 #include "p_mapsetup.h"
30 #include "p_mapspec.h"
31
32 /**
33 * Broken light flashing.
34 */
T_LightFlash(lightflash_t * flash)35 void T_LightFlash(lightflash_t *flash)
36 {
37 float lightlevel = P_GetFloatp(flash->sector, DMU_LIGHT_LEVEL);
38
39 if(--flash->count)
40 return;
41
42 if(lightlevel == flash->maxLight)
43 {
44 P_SetFloatp(flash->sector, DMU_LIGHT_LEVEL, flash->minLight);
45 flash->count = (P_Random() & flash->minTime) + 1;
46 }
47 else
48 {
49 P_SetFloatp(flash->sector, DMU_LIGHT_LEVEL, flash->maxLight);
50 flash->count = (P_Random() & flash->maxTime) + 1;
51 }
52 }
53
write(MapStateWriter * msw) const54 void lightflash_s::write(MapStateWriter *msw) const
55 {
56 Writer1 *writer = msw->writer();
57
58 Writer_WriteByte(writer, 1); // Write a version byte.
59
60 // Note we don't bother to save a byte to tell if the function
61 // is present as we ALWAYS add one when loading.
62
63 Writer_WriteInt32(writer, P_ToIndex(sector));
64
65 Writer_WriteInt32(writer, count);
66 Writer_WriteInt32(writer, (int) (255.0f * maxLight));
67 Writer_WriteInt32(writer, (int) (255.0f * minLight));
68 Writer_WriteInt32(writer, maxTime);
69 Writer_WriteInt32(writer, minTime);
70 }
71
read(MapStateReader * msr)72 int lightflash_s::read(MapStateReader *msr)
73 {
74 Reader1 *reader = msr->reader();
75 int mapVersion = msr->mapVersion();
76
77 if(mapVersion >= 5)
78 {
79 // Note: the thinker class byte has already been read.
80 /*int ver =*/ Reader_ReadByte(reader); // version byte.
81
82 // Start of used data members.
83 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
84 DENG_ASSERT(sector != 0);
85
86 count = Reader_ReadInt32(reader);
87 maxLight = (float) Reader_ReadInt32(reader) / 255.0f;
88 minLight = (float) Reader_ReadInt32(reader) / 255.0f;
89 maxTime = Reader_ReadInt32(reader);
90 minTime = Reader_ReadInt32(reader);
91 }
92 else
93 {
94 // Its in the old pre V5 format which serialized lightflash_t
95 // Padding at the start (an old thinker_t struct)
96 byte junk[16]; // sizeof thinker_t
97 Reader_Read(reader, junk, 16);
98
99 // Start of used data members.
100 // A 32bit pointer to sector, serialized.
101 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
102 DENG_ASSERT(sector != 0);
103
104 count = Reader_ReadInt32(reader);
105 maxLight = (float) Reader_ReadInt32(reader) / 255.0f;
106 minLight = (float) Reader_ReadInt32(reader) / 255.0f;
107 maxTime = Reader_ReadInt32(reader);
108 minTime = Reader_ReadInt32(reader);
109 }
110
111 thinker.function = (thinkfunc_t) T_LightFlash;
112
113 return true; // Add this thinker.
114 }
115
116 /**
117 * After the map has been loaded, scan each sector for specials that spawn
118 * thinkers.
119 */
P_SpawnLightFlash(Sector * sector)120 void P_SpawnLightFlash(Sector *sector)
121 {
122 float lightLevel = P_GetFloatp(sector, DMU_LIGHT_LEVEL);
123 float otherLevel = DDMAXFLOAT;
124
125 // Nothing special about it during gameplay.
126 P_ToXSector(sector)->special = 0;
127
128 lightflash_t *flash = (lightflash_t *)Z_Calloc(sizeof(*flash), PU_MAP, 0);
129 flash->thinker.function = (thinkfunc_t) T_LightFlash;
130 Thinker_Add(&flash->thinker);
131
132 flash->sector = sector;
133 flash->maxLight = lightLevel;
134
135 P_FindSectorSurroundingLowestLight(sector, &otherLevel);
136 if(otherLevel < lightLevel)
137 flash->minLight = otherLevel;
138 else
139 flash->minLight = lightLevel;
140 flash->maxTime = 64;
141 flash->minTime = 7;
142 flash->count = (P_Random() & flash->maxTime) + 1;
143 }
144
145 /**
146 * Strobe light flashing.
147 */
T_StrobeFlash(strobe_t * flash)148 void T_StrobeFlash(strobe_t *flash)
149 {
150 float lightLevel;
151
152 if(--flash->count)
153 return;
154
155 lightLevel = P_GetFloatp(flash->sector, DMU_LIGHT_LEVEL);
156 if(lightLevel == flash->minLight)
157 {
158 P_SetFloatp(flash->sector, DMU_LIGHT_LEVEL, flash->maxLight);
159 flash->count = flash->brightTime;
160 }
161 else
162 {
163 P_SetFloatp(flash->sector, DMU_LIGHT_LEVEL, flash->minLight);
164 flash->count = flash->darkTime;
165 }
166 }
167
write(MapStateWriter * msw) const168 void strobe_s::write(MapStateWriter *msw) const
169 {
170 Writer1 *writer = msw->writer();
171
172 Writer_WriteByte(writer, 1); // Write a version byte.
173
174 // Note we don't bother to save a byte to tell if the function
175 // is present as we ALWAYS add one when loading.
176
177 Writer_WriteInt32(writer, P_ToIndex(sector));
178
179 Writer_WriteInt32(writer, count);
180 Writer_WriteInt32(writer, (int) (255.0f * maxLight));
181 Writer_WriteInt32(writer, (int) (255.0f * minLight));
182 Writer_WriteInt32(writer, darkTime);
183 Writer_WriteInt32(writer, brightTime);
184 }
185
read(MapStateReader * msr)186 int strobe_s::read(MapStateReader *msr)
187 {
188 Reader1 *reader = msr->reader();
189 int mapVersion = msr->mapVersion();
190
191 if(mapVersion >= 5)
192 {
193 // Note: the thinker class byte has already been read.
194 /*int ver =*/ Reader_ReadByte(reader); // version byte.
195
196 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
197 DENG_ASSERT(sector != 0);
198
199 count = Reader_ReadInt32(reader);
200 maxLight = (float) Reader_ReadInt32(reader) / 255.0f;
201 minLight = (float) Reader_ReadInt32(reader) / 255.0f;
202 darkTime = Reader_ReadInt32(reader);
203 brightTime = Reader_ReadInt32(reader);
204 }
205 else
206 {
207 // Its in the old pre V5 format which serialized strobe_t
208 // Padding at the start (an old thinker_t struct)
209 byte junk[16]; // sizeof thinker_t
210 Reader_Read(reader, junk, 16);
211
212 // Start of used data members.
213 // A 32bit pointer to sector, serialized.
214 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
215 DENG_ASSERT(sector != 0);
216
217 count = Reader_ReadInt32(reader);
218 minLight = (float) Reader_ReadInt32(reader) / 255.0f;
219 maxLight = (float) Reader_ReadInt32(reader) / 255.0f;
220 darkTime = Reader_ReadInt32(reader);
221 brightTime = Reader_ReadInt32(reader);
222 }
223
224 thinker.function = (thinkfunc_t) T_StrobeFlash;
225
226 return true; // Add this thinker.
227 }
228
229 /**
230 * After the map has been loaded, scan each sector for specials that spawn
231 * thinkers.
232 */
P_SpawnStrobeFlash(Sector * sector,int fastOrSlow,int inSync)233 void P_SpawnStrobeFlash(Sector *sector, int fastOrSlow, int inSync)
234 {
235 float lightLevel = P_GetFloatp(sector, DMU_LIGHT_LEVEL);
236 float otherLevel = DDMAXFLOAT;
237
238 strobe_t *flash = (strobe_t *)Z_Calloc(sizeof(*flash), PU_MAP, 0);
239 flash->thinker.function = (thinkfunc_t) T_StrobeFlash;
240 Thinker_Add(&flash->thinker);
241
242 flash->sector = sector;
243 flash->darkTime = fastOrSlow;
244 flash->brightTime = STROBEBRIGHT;
245 flash->maxLight = lightLevel;
246 P_FindSectorSurroundingLowestLight(sector, &otherLevel);
247 if(otherLevel < lightLevel)
248 flash->minLight = otherLevel;
249 else
250 flash->minLight = lightLevel;
251
252 if(flash->minLight == flash->maxLight)
253 flash->minLight = 0;
254
255 // nothing special about it during gameplay
256 P_ToXSector(sector)->special = 0;
257
258 if(!inSync)
259 {
260 flash->count = (P_Random() & 7) + 1;
261 }
262 else
263 {
264 flash->count = 1;
265 }
266 }
267
268 /**
269 * Start strobing lights (usually from a trigger).
270 */
EV_StartLightStrobing(Line * line)271 void EV_StartLightStrobing(Line *line)
272 {
273 iterlist_t *list = P_GetSectorIterListForTag(P_ToXLine(line)->tag, false);
274 if(!list) return;
275
276 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
277 IterList_RewindIterator(list);
278
279 Sector *sec;
280 while((sec = (Sector *)IterList_MoveIterator(list)))
281 {
282 if(P_ToXSector(sec)->specialData)
283 continue;
284
285 P_SpawnStrobeFlash(sec, SLOWDARK, 0);
286 }
287 }
288
EV_TurnTagLightsOff(Line * line)289 void EV_TurnTagLightsOff(Line *line)
290 {
291 iterlist_t *list = P_GetSectorIterListForTag(P_ToXLine(line)->tag, false);
292 if(!list) return;
293
294 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
295 IterList_RewindIterator(list);
296
297 Sector *sec;
298 while((sec = (Sector *)IterList_MoveIterator(list)))
299 {
300 float lightLevel = P_GetFloatp(sec, DMU_LIGHT_LEVEL);
301 float otherLevel = DDMAXFLOAT;
302 P_FindSectorSurroundingLowestLight(sec, &otherLevel);
303 if(otherLevel < lightLevel)
304 lightLevel = otherLevel;
305
306 P_SetFloatp(sec, DMU_LIGHT_LEVEL, lightLevel);
307 }
308 }
309
EV_LightTurnOn(Line * line,float max)310 void EV_LightTurnOn(Line *line, float max)
311 {
312 iterlist_t *list = P_GetSectorIterListForTag(P_ToXLine(line)->tag, false);
313 if(!list) return;
314
315 float lightLevel = 0;
316
317 if(max != 0)
318 lightLevel = max;
319
320 IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
321 IterList_RewindIterator(list);
322
323 Sector *sec = 0;
324 while((sec = (Sector *)IterList_MoveIterator(list)))
325 {
326 // If Max = 0 means to search for the highest light level in the
327 // surrounding sector.
328 if(max == 0)
329 {
330 lightLevel = P_GetFloatp(sec, DMU_LIGHT_LEVEL);
331 float otherLevel = DDMINFLOAT;
332 P_FindSectorSurroundingHighestLight(sec, &otherLevel);
333 if(otherLevel > lightLevel)
334 lightLevel = otherLevel;
335 }
336
337 P_SetFloatp(sec, DMU_LIGHT_LEVEL, lightLevel);
338 }
339 }
340
T_Glow(glow_t * g)341 void T_Glow(glow_t *g)
342 {
343 float lightlevel = P_GetFloatp(g->sector, DMU_LIGHT_LEVEL);
344 float glowdelta = (1.0f / 255.0f) * (float) GLOWSPEED;
345
346 switch(g->direction)
347 {
348 case -1: // Down.
349 lightlevel -= glowdelta;
350 if(lightlevel <= g->minLight)
351 {
352 lightlevel += glowdelta;
353 g->direction = 1;
354 }
355 break;
356
357 case 1: // Up.
358 lightlevel += glowdelta;
359 if(lightlevel >= g->maxLight)
360 {
361 lightlevel -= glowdelta;
362 g->direction = -1;
363 }
364 break;
365 }
366
367 P_SetFloatp(g->sector, DMU_LIGHT_LEVEL, lightlevel);
368 }
369
write(MapStateWriter * msw) const370 void glow_s::write(MapStateWriter *msw) const
371 {
372 Writer1 *writer = msw->writer();
373
374 Writer_WriteByte(writer, 1); // Write a version byte.
375
376 // Note we don't bother to save a byte to tell if the function
377 // is present as we ALWAYS add one when loading.
378
379 Writer_WriteInt32(writer, P_ToIndex(sector));
380
381 Writer_WriteInt32(writer, (int) (255.0f * maxLight));
382 Writer_WriteInt32(writer, (int) (255.0f * minLight));
383 Writer_WriteInt32(writer, direction);
384 }
385
read(MapStateReader * msr)386 int glow_s::read(MapStateReader *msr)
387 {
388 Reader1 *reader = msr->reader();
389 int mapVersion = msr->mapVersion();
390
391 if(mapVersion >= 5)
392 {
393 // Note: the thinker class byte has already been read.
394 /*int ver =*/ Reader_ReadByte(reader); // version byte.
395
396 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
397 DENG_ASSERT(sector != 0);
398
399 maxLight = (float) Reader_ReadInt32(reader) / 255.0f;
400 minLight = (float) Reader_ReadInt32(reader) / 255.0f;
401 direction = Reader_ReadInt32(reader);
402 }
403 else
404 {
405 // Its in the old pre V5 format which serialized strobe_t
406 // Padding at the start (an old thinker_t struct)
407 byte junk[16]; // sizeof thinker_t
408 Reader_Read(reader, junk, 16);
409
410 // Start of used data members.
411 // A 32bit pointer to sector, serialized.
412 sector = (Sector *)P_ToPtr(DMU_SECTOR, (int) Reader_ReadInt32(reader));
413 DENG_ASSERT(sector != 0);
414
415 minLight = (float) Reader_ReadInt32(reader) / 255.0f;
416 maxLight = (float) Reader_ReadInt32(reader) / 255.0f;
417 direction = Reader_ReadInt32(reader);
418 }
419
420 thinker.function = (thinkfunc_t) T_Glow;
421
422 return true; // Add this thinker.
423 }
424
P_SpawnGlowingLight(Sector * sector)425 void P_SpawnGlowingLight(Sector *sector)
426 {
427 float lightLevel = P_GetFloatp(sector, DMU_LIGHT_LEVEL);
428 float otherLevel = DDMAXFLOAT;
429
430 glow_t *g = (glow_t *)Z_Calloc(sizeof(*g), PU_MAP, 0);
431 g->thinker.function = (thinkfunc_t) T_Glow;
432 Thinker_Add(&g->thinker);
433
434 g->sector = sector;
435 P_FindSectorSurroundingLowestLight(sector, &otherLevel);
436 if(otherLevel < lightLevel)
437 g->minLight = otherLevel;
438 else
439 g->minLight = lightLevel;
440 g->maxLight = lightLevel;
441 g->direction = -1;
442
443 P_ToXSector(sector)->special = 0;
444 }
445