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