1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010-2019 EDuke32 developers and contributors
4 Copyright (C) 2019 sirlemonhead, Nuke.YKT
5
6 This file is part of PCExhumed.
7
8 PCExhumed is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License version 2
10 as published by the Free Software Foundation.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 //-------------------------------------------------------------------------
23
24 #include "typedefs.h"
25 #include "lighting.h"
26 #include "player.h"
27 #include "engine.h"
28 #include "exhumed.h"
29 #include "sound.h"
30 #include "light.h"
31 #include "random.h"
32 #include "save.h"
33 #include "status.h"
34 #include <string.h>
35 #include <assert.h>
36
37 #define kMaxFlashes 2000
38 #define kMaxFlickerMask 25
39 #define kMaxGlows 50
40 #define kMaxFlickers 100
41 #define kMaxFlows 375
42
43 struct Flash
44 {
45 char field_0;
46 short field_1;
47 int8_t shade;
48 };
49
50 struct Glow
51 {
52 short field_0;
53 short field_2;
54 short nSector;
55 short field_6;
56 };
57
58 struct Flicker
59 {
60 short field_0;
61 short nSector;
62 unsigned int field_4;
63 };
64
65 struct Flow
66 {
67 short field_0;
68 short field_2;
69 int field_4;
70 int field_8;
71 int field_C;
72 int field_10;
73 int field_14;
74 int field_18;
75 };
76
77 Flash sFlash[kMaxFlashes];
78 Glow sGlow[kMaxGlows];
79 short nNextFlash[kMaxFlashes];
80 Flicker sFlicker[kMaxFlickers];
81 short nFreeFlash[kMaxFlashes];
82 Flow sFlowInfo[kMaxFlows];
83 int flickermask[kMaxFlickerMask];
84
85 short bTorch = 0;
86 short nFirstFlash = -1;
87 short nLastFlash = -1;
88 short nFlashDepth = 2;
89 short nFlashes;
90 short nFlowCount;
91 short nFlickerCount;
92 short nGlowCount;
93
94 int bDoFlicks = 0;
95 int bDoGlows = 0;
96
97
GrabFlash()98 int GrabFlash()
99 {
100 if (nFlashes >= kMaxFlashes) {
101 return -1;
102 }
103
104 short nFlash = nFreeFlash[nFlashes];
105 nNextFlash[nFlash] = -1;
106
107 nFlashes++;
108
109 if (nLastFlash <= -1)
110 {
111 nFirstFlash = nFlash;
112 }
113 else
114 {
115 nNextFlash[nLastFlash] = nFlash;
116 }
117
118 nLastFlash = nFlash;
119
120 return nLastFlash;
121 }
122
InitLights()123 void InitLights()
124 {
125 int i;
126 nFlickerCount = 0;
127
128 for (i = 0; i < kMaxFlickerMask; i++) {
129 flickermask[i] = RandomSize(0x1F) * 2;
130 }
131
132 nGlowCount = 0;
133 nFlowCount = 0;
134 nFlashes = 0;
135 bDoFlicks = kFalse;
136 bDoGlows = kFalse;
137
138 for (i = 0; i < kMaxFlashes; i++) {
139 nFreeFlash[i] = i;
140 }
141
142 nFirstFlash = -1;
143 nLastFlash = -1;
144 }
145
AddFlash(short nSector,int x,int y,int z,int val)146 void AddFlash(short nSector, int x, int y, int z, int val)
147 {
148 assert(nSector >= 0 && nSector < kMaxSectors);
149
150 int var_28 = 0;
151 unsigned int var_1C = val >> 8;
152
153 if (var_1C >= nFlashDepth) {
154 return;
155 }
156
157 unsigned int var_20 = val & 0x80;
158 unsigned int var_18 = val & 0x40;
159
160 val = ((var_1C + 1) << 8) | (char)val;
161
162 int var_14 = 0;
163
164 short startwall = sector[nSector].wallptr;
165 short endwall = sector[nSector].wallptr + sector[nSector].wallnum;
166
167 for (int i = startwall; i < endwall; i++)
168 {
169 short wall2 = wall[i].point2;
170
171 int xAverage = (wall[i].x + wall[wall2].x) / 2;
172 int yAverage = (wall[i].y + wall[wall2].y) / 2;
173
174 sectortype *pNextSector = NULL;
175 if (wall[i].nextsector > -1) {
176 pNextSector = §or[wall[i].nextsector];
177 }
178
179 int ebx = -255;
180
181 if (!var_18)
182 {
183 int x2 = x - xAverage;
184 if (x2 < 0) {
185 x2 = -x2;
186 }
187
188 ebx = x2;
189
190 int y2 = y - yAverage;
191 if (y2 < 0) {
192 y2 = -y2;
193 }
194
195 ebx = ((y2 + ebx) >> 4) - 255;
196 }
197
198 if (ebx < 0)
199 {
200 var_14++;
201 var_28 += ebx;
202
203 if (wall[i].pal < 5)
204 {
205 if (!pNextSector || pNextSector->floorz < sector[nSector].floorz)
206 {
207 short nFlash = GrabFlash();
208 if (nFlash < 0) {
209 return;
210 }
211
212 sFlash[nFlash].field_0 = var_20 | 2;
213 sFlash[nFlash].shade = wall[i].shade;
214 sFlash[nFlash].field_1 = i;
215
216 wall[i].pal += 7;
217
218 ebx += wall[i].shade;
219 int eax = ebx;
220
221 if (ebx < -127) {
222 eax = -127;
223 }
224
225 wall[i].shade = eax;
226
227 if (!var_1C && !wall[i].overpicnum && pNextSector)
228 {
229 AddFlash(wall[i].nextsector, x, y, z, val);
230 }
231 }
232 }
233 }
234 }
235
236 if (var_14 && sector[nSector].floorpal < 4)
237 {
238 short nFlash = GrabFlash();
239 if (nFlash < 0) {
240 return;
241 }
242
243 sFlash[nFlash].field_0 = var_20 | 1;
244 sFlash[nFlash].field_1 = nSector;
245 sFlash[nFlash].shade = sector[nSector].floorshade;
246
247 sector[nSector].floorpal += 7;
248
249 int edx = sector[nSector].floorshade + var_28;
250 int eax = edx;
251
252 if (edx < -127) {
253 eax = -127;
254 }
255
256 sector[nSector].floorshade = eax;
257
258 if (!(sector[nSector].ceilingstat & 1))
259 {
260 if (sector[nSector].ceilingpal < 4)
261 {
262 short nFlash2 = GrabFlash();
263 if (nFlash2 >= 0)
264 {
265 sFlash[nFlash2].field_0 = var_20 | 3;
266 sFlash[nFlash2].field_1 = nSector;
267 sFlash[nFlash2].shade = sector[nSector].ceilingshade;
268
269 sector[nSector].ceilingpal += 7;
270
271 int edx = sector[nSector].ceilingshade + var_28;
272 int eax = edx;
273
274 if (edx < -127) {
275 eax = -127;
276 }
277
278 sector[nSector].ceilingshade = eax;
279 }
280 }
281 }
282
283 for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite])
284 {
285 if (sprite[nSprite].pal < 4)
286 {
287 short nFlash3 = GrabFlash();
288 if (nFlash3 >= 0)
289 {
290 sFlash[nFlash3].field_0 = var_20 | 4;
291 sFlash[nFlash3].shade = sprite[nSprite].shade;
292 sFlash[nFlash3].field_1 = nSprite;
293
294 sprite[nSprite].pal += 7;
295
296 int eax = -255;
297
298 if (!var_18)
299 {
300 int xDiff = x - sprite[nSprite].x;
301 if (xDiff < 0) {
302 xDiff = -xDiff;
303 }
304
305 int yDiff = y - sprite[nSprite].y;
306 if (yDiff < 0) {
307 yDiff = -yDiff;
308 }
309
310 eax = ((xDiff + yDiff) >> 4) - 255;
311 }
312
313 if (eax < 0)
314 {
315 short shade = sprite[nSprite].shade + eax;
316 if (shade < -127) {
317 shade = -127;
318 }
319
320 sprite[nSprite].shade = shade;
321 }
322 }
323 }
324 }
325 }
326 }
327
UndoFlashes()328 void UndoFlashes()
329 {
330 if (!nFlashes) {
331 return;
332 }
333
334 int var_24 = 0; // CHECKME - Watcom error "initializer for variable var_24 may not execute
335
336 int edi = -1;
337
338 for (short nFlash = nFirstFlash; nFlash >= 0; nFlash = nNextFlash[nFlash])
339 {
340 assert(nFlash < 2000 && nFlash >= 0);
341
342 uint8_t var_28 = sFlash[nFlash].field_0 & 0x3F;
343 short nIndex = sFlash[nFlash].field_1;
344
345 if (sFlash[nFlash].field_0 & 0x80)
346 {
347 int var_20 = var_28 - 1;
348 assert(var_20 >= 0);
349
350 int8_t *pShade = NULL;
351
352 switch (var_20)
353 {
354 case 0:
355 {
356 assert(nIndex >= 0 && nIndex < kMaxSectors);
357
358 pShade = §or[nIndex].floorshade;
359 break;
360 }
361
362 case 1:
363 {
364 assert(nIndex >= 0 && nIndex < kMaxWalls);
365
366 pShade = &wall[nIndex].shade;
367 break;
368 }
369
370 case 2:
371 {
372 assert(nIndex >= 0 && nIndex < kMaxSectors);
373
374 pShade = §or[nIndex].ceilingshade;
375 break;
376 }
377
378 case 3:
379 {
380 assert(nIndex >= 0 && nIndex < kMaxSprites);
381
382 if (sprite[nIndex].pal >= 7)
383 {
384 pShade = &sprite[nIndex].shade;
385 }
386 else {
387 goto loc_1868A;
388 }
389
390 break;
391 }
392
393 default:
394 break;
395 }
396
397 assert(pShade != NULL);
398
399 short var_2C = (*pShade) + 6;
400 int var_30 = sFlash[nFlash].shade;
401
402 if (var_2C < var_30)
403 {
404 *pShade = var_2C;
405 edi = nFlash;
406 continue;
407 }
408 }
409
410 // loc_185FE
411 var_24 = var_28 - 1; // CHECKME - Watcom error "initializer for variable var_24 may not execute
412 assert(var_24 >= 0);
413
414 switch (var_24)
415 {
416 default:
417 break;
418
419 case 0:
420 {
421 sector[nIndex].floorpal -= 7;
422 sector[nIndex].floorshade = sFlash[nFlash].shade;
423 break;
424 }
425
426 case 1:
427 {
428 wall[nIndex].pal -= 7;
429 wall[nIndex].shade = sFlash[nFlash].shade;
430 break;
431 }
432
433 case 2:
434 {
435 sector[nIndex].ceilingpal -= 7;
436 sector[nIndex].ceilingshade = sFlash[nFlash].shade;
437 break;
438 }
439
440 case 3:
441 {
442 if (sprite[nIndex].pal >= 7)
443 {
444 sprite[nIndex].pal -= 7;
445 sprite[nIndex].shade = sFlash[nFlash].shade;
446 }
447
448 break;
449 }
450 }
451
452 loc_1868A:
453
454 nFlashes--;
455 assert(nFlashes >= 0);
456
457 nFreeFlash[nFlashes] = nFlash;
458
459 if (edi != -1)
460 {
461 nNextFlash[edi] = nNextFlash[nFlash];
462 }
463
464 if (nFlash == nFirstFlash)
465 {
466 nFirstFlash = nNextFlash[nFirstFlash];
467 }
468
469 if (nFlash == nLastFlash)
470 {
471 nLastFlash = edi;
472 }
473 }
474 }
475
AddGlow(short nSector,int nVal)476 void AddGlow(short nSector, int nVal)
477 {
478 if (nGlowCount >= kMaxGlows) {
479 return;
480 }
481
482 sGlow[nGlowCount].field_6 = nVal;
483 sGlow[nGlowCount].nSector = nSector;
484 sGlow[nGlowCount].field_0 = -1;
485 sGlow[nGlowCount].field_2 = 0;
486
487 nGlowCount++;
488 }
489
AddFlicker(short nSector,int nVal)490 void AddFlicker(short nSector, int nVal)
491 {
492 if (nFlickerCount >= kMaxFlickers) {
493 return;
494 }
495
496 sFlicker[nFlickerCount].field_0 = nVal;
497 sFlicker[nFlickerCount].nSector = nSector;
498
499 if (nVal >= 25) {
500 nVal = 24;
501 }
502
503 sFlicker[nFlickerCount].field_4 = flickermask[nVal];
504
505 nFlickerCount++;
506 }
507
DoGlows()508 void DoGlows()
509 {
510 bDoGlows++;
511
512 if (bDoGlows < 3) {
513 return;
514 }
515
516 bDoGlows = 0;
517
518 for (int i = 0; i < nGlowCount; i++)
519 {
520 sGlow[i].field_2++;
521
522 short nSector = sGlow[i].nSector;
523 short nShade = sGlow[i].field_0;
524
525 if (sGlow[i].field_2 >= sGlow[i].field_6)
526 {
527 sGlow[i].field_2 = 0;
528 sGlow[i].field_0 = -sGlow[i].field_0;
529 }
530
531 sector[nSector].ceilingshade += nShade;
532 sector[nSector].floorshade += nShade;
533
534 int startwall = sector[nSector].wallptr;
535 int endwall = startwall + sector[nSector].wallnum - 1;
536
537 for (int nWall = startwall; nWall <= endwall; nWall++)
538 {
539 wall[nWall].shade += nShade;
540
541 // CHECKME - ASM has edx decreasing here. why?
542 }
543 }
544 }
545
DoFlickers()546 void DoFlickers()
547 {
548 bDoFlicks ^= 1;
549 if (!bDoFlicks) {
550 return;
551 }
552
553 for (int i = 0; i < nFlickerCount; i++)
554 {
555 short nSector = sFlicker[i].nSector;
556
557 unsigned int eax = (sFlicker[i].field_4 & 1);
558 unsigned int edx = (sFlicker[i].field_4 & 1) << 31;
559 unsigned int ebp = sFlicker[i].field_4 >> 1;
560
561 ebp |= edx;
562 edx = ebp & 1;
563
564 sFlicker[i].field_4 = ebp;
565
566 if (edx ^ eax)
567 {
568 short shade;
569
570 if (eax)
571 {
572 shade = sFlicker[i].field_0;
573 }
574 else
575 {
576 shade = -sFlicker[i].field_0;
577 }
578
579 sector[nSector].ceilingshade += shade;
580 sector[nSector].floorshade += shade;
581
582 int startwall = sector[nSector].wallptr;
583 int endwall = startwall + sector[nSector].wallnum - 1;
584
585 for (int nWall = endwall; nWall >= startwall; nWall--)
586 {
587 wall[nWall].shade += shade;
588
589 // CHECKME - ASM has edx decreasing here. why?
590 }
591 }
592 }
593 }
594
595 // nWall can also be passed in here via nSprite parameter - TODO - rename nSprite parameter :)
AddFlow(int nSprite,int nSpeed,int b)596 void AddFlow(int nSprite, int nSpeed, int b)
597 {
598 if (nFlowCount >= kMaxFlows)
599 return;
600
601 short nFlow = nFlowCount;
602 nFlowCount++;
603
604 short var_18;
605
606 if (b < 2)
607 {
608 var_18 = sprite[nSprite].sectnum;
609 short nPic = sector[var_18].floorpicnum;
610 short nAngle = sprite[nSprite].ang;
611
612 sFlowInfo[nFlow].field_14 = (tilesiz[nPic].x << 14) - 1;
613 sFlowInfo[nFlow].field_18 = (tilesiz[nPic].y << 14) - 1;
614 sFlowInfo[nFlow].field_C = -Cos(nAngle) * nSpeed;
615 sFlowInfo[nFlow].field_10 = Sin(nAngle) * nSpeed;
616 }
617 else
618 {
619 short nAngle;
620
621 if (b == 2) {
622 nAngle = 512;
623 }
624 else {
625 nAngle = 1536;
626 }
627
628 var_18 = nSprite;
629 short nPic = wall[var_18].picnum;
630
631 sFlowInfo[nFlow].field_14 = (tilesiz[nPic].x * wall[var_18].xrepeat) << 8;
632 sFlowInfo[nFlow].field_18 = (tilesiz[nPic].y * wall[var_18].yrepeat) << 8;
633 sFlowInfo[nFlow].field_C = -Cos(nAngle) * nSpeed;
634 sFlowInfo[nFlow].field_10 = Sin(nAngle) * nSpeed;
635 }
636
637 sFlowInfo[nFlow].field_8 = 0;
638 sFlowInfo[nFlow].field_4 = 0;
639 sFlowInfo[nFlow].field_0 = var_18;
640 sFlowInfo[nFlow].field_2 = b;
641 }
642
DoFlows()643 void DoFlows()
644 {
645 for (int i = 0; i < nFlowCount; i++)
646 {
647 sFlowInfo[i].field_4 += sFlowInfo[i].field_C;
648 sFlowInfo[i].field_8 += sFlowInfo[i].field_10;
649
650 switch (sFlowInfo[i].field_2)
651 {
652 case 0:
653 {
654 sFlowInfo[i].field_4 &= sFlowInfo[i].field_14;
655 sFlowInfo[i].field_8 &= sFlowInfo[i].field_18;
656
657 short nSector = sFlowInfo[i].field_0;
658 sector[nSector].floorxpanning = sFlowInfo[i].field_4 >> 14;
659 sector[nSector].floorypanning = sFlowInfo[i].field_8 >> 14;
660 break;
661 }
662
663 case 1:
664 {
665 short nSector = sFlowInfo[i].field_0;
666
667 sector[nSector].ceilingxpanning = sFlowInfo[i].field_4 >> 14;
668 sector[nSector].ceilingypanning = sFlowInfo[i].field_8 >> 14;
669
670 sFlowInfo[i].field_4 &= sFlowInfo[i].field_14;
671 sFlowInfo[i].field_8 &= sFlowInfo[i].field_18;
672 break;
673 }
674
675 case 2:
676 {
677 short nWall = sFlowInfo[i].field_0;
678
679 wall[nWall].xpanning = sFlowInfo[i].field_4 >> 14;
680 wall[nWall].ypanning = sFlowInfo[i].field_8 >> 14;
681
682 if (sFlowInfo[i].field_4 < 0)
683 {
684 sFlowInfo[i].field_4 += sFlowInfo[i].field_14;
685 }
686
687 if (sFlowInfo[i].field_8 < 0)
688 {
689 sFlowInfo[i].field_8 += sFlowInfo[i].field_18;
690 }
691
692 break;
693 }
694
695 case 3:
696 {
697 short nWall = sFlowInfo[i].field_0;
698
699 wall[nWall].xpanning = sFlowInfo[i].field_4 >> 14;
700 wall[nWall].ypanning = sFlowInfo[i].field_8 >> 14;
701
702 if (sFlowInfo[i].field_4 >= sFlowInfo[i].field_14)
703 {
704 sFlowInfo[i].field_4 -= sFlowInfo[i].field_14;
705 }
706
707 if (sFlowInfo[i].field_8 >= sFlowInfo[i].field_18)
708 {
709 sFlowInfo[i].field_8 -= sFlowInfo[i].field_18;
710 }
711
712 break;
713 }
714 }
715 }
716 }
717
DoLights()718 void DoLights()
719 {
720 DoFlickers();
721 DoGlows();
722 DoFlows();
723 }
724
SetTorch(int nPlayer,int bTorchOnOff)725 void SetTorch(int nPlayer, int bTorchOnOff)
726 {
727 char buf[40];
728
729 if (bTorchOnOff == bTorch) {
730 return;
731 }
732
733 if (nPlayer != nLocalPlayer) {
734 return;
735 }
736
737 // char *pTempPal = origpalookup[kPalTorch];
738 // palookup[kPalTorch] = palookup[kPalNoTorch];
739 // palookup[kPalNoTorch] = pTempPal;
740 //
741 // pTempPal = origpalookup[kPalTorch];
742 // origpalookup[kPalTorch] = origpalookup[kPalNoTorch];
743 // origpalookup[kPalNoTorch] = pTempPal;
744 //
745 // pTempPal = origpalookup[kPalTorch2];
746 // origpalookup[kPalTorch2] = origpalookup[kPalNoTorch2];
747 // origpalookup[kPalNoTorch2] = pTempPal;
748 //
749 // pTempPal = palookup[kPalTorch2];
750 // palookup[kPalNoTorch2] = palookup[kPalTorch2];
751 // palookup[kPalTorch2] = pTempPal;
752
753 if (bTorchOnOff == 2) {
754 bTorch = !bTorch;
755 }
756 else {
757 bTorch = bTorchOnOff;
758 }
759
760 if (bTorch) {
761 PlayLocalSound(kSoundTorchOn, 0);
762 }
763
764 strcpy(buf, "TORCH IS ");
765
766 if (bTorch) {
767 strcat(buf, "LIT");
768 }
769 else {
770 strcat(buf, "OUT");
771 }
772
773 StatusMessage(150, buf);
774 }
775
BuildFlash(short nPlayer,short UNUSED (nSector),int nVal)776 void BuildFlash(short nPlayer, short UNUSED(nSector), int nVal)
777 {
778 if (nPlayer == nLocalPlayer)
779 {
780 flash = nVal;
781 flash = -nVal; // ???
782 }
783 }
784
785 class LightingLoadSave : public LoadSave
786 {
787 public:
788 virtual void Load();
789 virtual void Save();
790 };
791
Load()792 void LightingLoadSave::Load()
793 {
794 Read(&bTorch, sizeof(bTorch));
795 Read(&nFirstFlash, sizeof(nFirstFlash));
796 Read(&nLastFlash, sizeof(nLastFlash));
797 Read(&nFlashDepth, sizeof(nFlashDepth));
798 Read(&nFlashes, sizeof(nFlashes));
799 Read(&nFlowCount, sizeof(nFlowCount));
800 Read(&nFlickerCount, sizeof(nFlickerCount));
801 Read(&nGlowCount, sizeof(nGlowCount));
802 Read(&bDoFlicks, sizeof(bDoFlicks));
803 Read(&bDoGlows, sizeof(bDoGlows));
804
805 Read(sFlash, sizeof(sFlash));
806 Read(sGlow, sizeof(sGlow));
807 Read(nNextFlash, sizeof(nNextFlash));
808 Read(sFlicker, sizeof(sFlicker));
809 Read(nFreeFlash, sizeof(nFreeFlash));
810 Read(sFlowInfo, sizeof(sFlowInfo));
811 Read(flickermask, sizeof(flickermask));
812 }
813
Save()814 void LightingLoadSave::Save()
815 {
816 Write(&bTorch, sizeof(bTorch));
817 Write(&nFirstFlash, sizeof(nFirstFlash));
818 Write(&nLastFlash, sizeof(nLastFlash));
819 Write(&nFlashDepth, sizeof(nFlashDepth));
820 Write(&nFlashes, sizeof(nFlashes));
821 Write(&nFlowCount, sizeof(nFlowCount));
822 Write(&nFlickerCount, sizeof(nFlickerCount));
823 Write(&nGlowCount, sizeof(nGlowCount));
824 Write(&bDoFlicks, sizeof(bDoFlicks));
825 Write(&bDoGlows, sizeof(bDoGlows));
826
827 Write(sFlash, sizeof(sFlash));
828 Write(sGlow, sizeof(sGlow));
829 Write(nNextFlash, sizeof(nNextFlash));
830 Write(sFlicker, sizeof(sFlicker));
831 Write(nFreeFlash, sizeof(nFreeFlash));
832 Write(sFlowInfo, sizeof(sFlowInfo));
833 Write(flickermask, sizeof(flickermask));
834 }
835
836 static LightingLoadSave* myLoadSave;
837
LightingLoadSaveConstruct()838 void LightingLoadSaveConstruct()
839 {
840 myLoadSave = new LightingLoadSave();
841 }
842