1 //**************************************************************************
2 //**
3 //** ## ## ## ## ## #### #### ### ###
4 //** ## ## ## ## ## ## ## ## ## ## #### ####
5 //** ## ## ## ## ## ## ## ## ## ## ## ## ## ##
6 //** ## ## ######## ## ## ## ## ## ## ## ### ##
7 //** ### ## ## ### ## ## ## ## ## ##
8 //** # ## ## # #### #### ## ##
9 //**
10 //** $Id: r_data.cpp 4340 2010-12-15 21:48:23Z dj_jl $
11 //**
12 //** Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //** This program is free software; you can redistribute it and/or
15 //** modify it under the terms of the GNU General Public License
16 //** as published by the Free Software Foundation; either version 2
17 //** of the License, or (at your option) any later version.
18 //**
19 //** This program is distributed in the hope that it will be useful,
20 //** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 //** GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 //**
26 //** Preparation of data for rendering, generation of lookups.
27 //**
28 //**************************************************************************
29
30 // HEADER FILES ------------------------------------------------------------
31
32 #include "gamedefs.h"
33 #include "r_local.h"
34
35 // MACROS ------------------------------------------------------------------
36
37 // TYPES -------------------------------------------------------------------
38
39 struct VTempSpriteEffectDef
40 {
41 VStr Sprite;
42 VStr Light;
43 VStr Part;
44 };
45
46 struct VTempClassEffects
47 {
48 VStr ClassName;
49 VStr StaticLight;
50 TArray<VTempSpriteEffectDef> SpriteEffects;
51 };
52
53 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
54
55 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
56
57 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
58
59 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
60
61 // PUBLIC DATA DEFINITIONS -------------------------------------------------
62
63 //
64 // Main palette
65 //
66 rgba_t r_palette[256];
67 vuint8 r_black_colour;
68
69 vuint8 r_rgbtable[32 * 32 * 32 + 4];
70
71 // variables used to look up
72 // and range check thing_t sprites patches
73 spritedef_t sprites[MAX_SPRITE_MODELS];
74
75 VTextureTranslation** TranslationTables;
76 int NumTranslationTables;
77 VTextureTranslation IceTranslation;
78 TArray<VTextureTranslation*> DecorateTranslations;
79 TArray<VTextureTranslation*> BloodTranslations;
80
81 // They basicly work the same as translations.
82 VTextureTranslation ColourMaps[CM_Max];
83
84 // PRIVATE DATA DEFINITIONS ------------------------------------------------
85
86 // Temporary variables for sprite installing
87 static spriteframe_t sprtemp[30];
88 static int maxframe;
89 static const char* spritename;
90
91 static TArray<VLightEffectDef> GLightEffectDefs;
92 static TArray<VParticleEffectDef> GParticleEffectDefs;
93
94 // CODE --------------------------------------------------------------------
95
96 //==========================================================================
97 //
98 // InitPalette
99 //
100 //==========================================================================
101
InitPalette()102 static void InitPalette()
103 {
104 guard(InitPalette);
105 // We use colour 0 as transparent colour, so we must find an alternate
106 // index for black colour. In Doom, Heretic and Strife there is another
107 // black colour, in Hexen it's almost black.
108 // I think that originaly Doom uses colour 255 as transparent colour,
109 // but utilites created by others uses the alternate black colour and
110 // these graphics can contain pixels of colour 255.
111 // Heretic and Hexen also uses colour 255 as transparent, even more - in
112 // colourmaps it's maped to colour 0. Posibly this can cause problems
113 // with modified graphics.
114 // Strife uses colour 0 as transparent. I already had problems with fact
115 // that colour 255 is normal colour, now there shouldn't be any problems.
116 VStream* Strm = W_CreateLumpReaderName(NAME_playpal);
117 check(Strm);
118 rgba_t* pal = r_palette;
119 int best_dist = 0x10000;
120 for (int i = 0; i < 256; i++)
121 {
122 *Strm << pal[i].r
123 << pal[i].g
124 << pal[i].b;
125 if (i == 0)
126 {
127 pal[i].a = 0;
128 }
129 else
130 {
131 pal[i].a = 255;
132 int dist = pal[i].r * pal[i].r + pal[i].g * pal[i].g +
133 pal[i].b * pal[i].b;
134 if (dist < best_dist)
135 {
136 r_black_colour = i;
137 best_dist = dist;
138 }
139 }
140 }
141 delete Strm;
142 Strm = NULL;
143 unguard;
144 }
145
146 //==========================================================================
147 //
148 // InitRgbTable
149 //
150 //==========================================================================
151
InitRgbTable()152 static void InitRgbTable()
153 {
154 guard(InitRgbTable);
155 for (int ir = 0; ir < 32; ir++)
156 {
157 for (int ig = 0; ig < 32; ig++)
158 {
159 for (int ib = 0; ib < 32; ib++)
160 {
161 int r = (int)(ir * 255.0 / 31.0 + 0.5);
162 int g = (int)(ig * 255.0 / 31.0 + 0.5);
163 int b = (int)(ib * 255.0 / 31.0 + 0.5);
164 int best_colour = 0;
165 int best_dist = 0x1000000;
166 for (int i = 1; i < 256; i++)
167 {
168 int dist = (r_palette[i].r - r) * (r_palette[i].r - r) +
169 (r_palette[i].g - g) * (r_palette[i].g - g) +
170 (r_palette[i].b - b) * (r_palette[i].b - b);
171 if (dist < best_dist)
172 {
173 best_colour = i;
174 best_dist = dist;
175 if (!dist)
176 break;
177 }
178 }
179 r_rgbtable[(ir << 10) + (ig << 5) + ib] = best_colour;
180 }
181 }
182 }
183 r_rgbtable[32 * 32 * 32] = 0;
184 unguard;
185 }
186
187 //==========================================================================
188 //
189 // InitTranslationTables
190 //
191 //==========================================================================
192
InitTranslationTables()193 static void InitTranslationTables()
194 {
195 guard(InitTranslationTables);
196 VStream* Strm = W_CreateLumpReaderName(NAME_translat);
197 NumTranslationTables = Strm->TotalSize() / 256;
198 TranslationTables = new VTextureTranslation*[NumTranslationTables];
199 for (int j = 0; j < NumTranslationTables; j++)
200 {
201 VTextureTranslation* Trans = new VTextureTranslation;
202 TranslationTables[j] = Trans;
203 Strm->Serialise(Trans->Table, 256);
204 // Make sure that 0 always maps to 0.
205 Trans->Table[0] = 0;
206 Trans->Palette[0] = r_palette[0];
207 for (int i = 1; i < 256; i++)
208 {
209 // Make sure that normal colours doesn't map to colour 0.
210 if (Trans->Table[i] == 0)
211 {
212 Trans->Table[i] = r_black_colour;
213 }
214 Trans->Palette[i] = r_palette[Trans->Table[i]];
215 }
216 }
217 delete Strm;
218 Strm = NULL;
219
220 // Calculate ice translation.
221 IceTranslation.Table[0] = 0;
222 IceTranslation.Palette[0] = r_palette[0];
223 for (int i = 1; i < 256; i++)
224 {
225 int r = int(r_palette[i].r * 0.5 + 64 * 0.5);
226 int g = int(r_palette[i].g * 0.5 + 64 * 0.5);
227 int b = int(r_palette[i].b * 0.5 + 255 * 0.5);
228 IceTranslation.Palette[i].r = r;
229 IceTranslation.Palette[i].g = g;
230 IceTranslation.Palette[i].b = b;
231 IceTranslation.Palette[i].a = 255;
232 IceTranslation.Table[i] = R_LookupRGB(r, g, b);
233 }
234 unguard;
235 }
236
237 //==========================================================================
238 //
239 // InitColourMaps
240 //
241 //==========================================================================
242
InitColourMaps()243 static void InitColourMaps()
244 {
245 guard(InitColourMaps);
246 // Calculate inverse colourmap.
247 VTextureTranslation* T = &ColourMaps[CM_Inverse];
248 T->Table[0] = 0;
249 T->Palette[0] = r_palette[0];
250 for (int i = 1; i < 256; i++)
251 {
252 int Gray = (r_palette[i].r * 77 + r_palette[i].g * 143 +
253 r_palette[i].b * 37) >> 8;
254 int Val = 255 - Gray;
255 T->Palette[i].r = Val;
256 T->Palette[i].g = Val;
257 T->Palette[i].b = Val;
258 T->Palette[i].a = 255;
259 T->Table[i] = R_LookupRGB(Val, Val, Val);
260 }
261
262 // Calculate gold colourmap.
263 T = &ColourMaps[CM_Gold];
264 T->Table[0] = 0;
265 T->Palette[0] = r_palette[0];
266 for (int i = 1; i < 256; i++)
267 {
268 int Gray = (r_palette[i].r * 77 + r_palette[i].g * 143 +
269 r_palette[i].b * 37) >> 8;
270 T->Palette[i].r = MIN(255, Gray + Gray / 2);
271 T->Palette[i].g = Gray;
272 T->Palette[i].b = 0;
273 T->Palette[i].a = 255;
274 T->Table[i] = R_LookupRGB(T->Palette[i].r, T->Palette[i].g,
275 T->Palette[i].b);
276 }
277
278 // Calculate red colourmap.
279 T = &ColourMaps[CM_Red];
280 T->Table[0] = 0;
281 T->Palette[0] = r_palette[0];
282 for (int i = 1; i < 256; i++)
283 {
284 int Gray = (r_palette[i].r * 77 + r_palette[i].g * 143 +
285 r_palette[i].b * 37) >> 8;
286 T->Palette[i].r = MIN(255, Gray + Gray / 2);
287 T->Palette[i].g = 0;
288 T->Palette[i].b = 0;
289 T->Palette[i].a = 255;
290 T->Table[i] = R_LookupRGB(T->Palette[i].r, T->Palette[i].g,
291 T->Palette[i].b);
292 }
293
294 // Calculate green colourmap.
295 T = &ColourMaps[CM_Green];
296 T->Table[0] = 0;
297 T->Palette[0] = r_palette[0];
298 for (int i = 1; i < 256; i++)
299 {
300 int Gray = (r_palette[i].r * 77 + r_palette[i].g * 143 +
301 r_palette[i].b * 37) >> 8;
302 T->Palette[i].r = MIN(255, Gray + Gray / 2);
303 T->Palette[i].g = MIN(255, Gray + Gray / 2);
304 T->Palette[i].b = Gray;
305 T->Palette[i].a = 255;
306 T->Table[i] = R_LookupRGB(T->Palette[i].r, T->Palette[i].g,
307 T->Palette[i].b);
308 }
309 unguard;
310 }
311
312 //==========================================================================
313 //
314 // InstallSpriteLump
315 //
316 // Local function for R_InitSprites.
317 //
318 //==========================================================================
319
InstallSpriteLump(int lumpnr,int frame,char Rot,bool flipped)320 static void InstallSpriteLump(int lumpnr, int frame, char Rot, bool flipped)
321 {
322 guard(InstallSpriteLump);
323 int r;
324 int rotation;
325
326 if (Rot >= '0' && Rot <= '9')
327 {
328 rotation = Rot - '0';
329 }
330 else if (Rot >= 'a')
331 {
332 rotation = Rot - 'a' + 10;
333 }
334 else
335 {
336 rotation = 17;
337 }
338
339 VTexture* Tex = GTextureManager[lumpnr];
340 if ((vuint32)frame >= 30 || (vuint32)rotation > 16)
341 {
342 Sys_Error("InstallSpriteLump: Bad frame characters in lump %s",
343 *Tex->Name);
344 }
345
346 if (frame > maxframe)
347 maxframe = frame;
348
349 if (rotation == 0)
350 {
351 // the lump should be used for all rotations
352 sprtemp[frame].rotate = false;
353 for (r = 0; r < 16; r++)
354 {
355 sprtemp[frame].lump[r] = lumpnr;
356 sprtemp[frame].flip[r] = flipped;
357 }
358 return;
359 }
360
361 if (rotation <= 8)
362 {
363 rotation = (rotation - 1) * 2;
364 }
365 else
366 {
367 rotation = (rotation - 9) * 2 + 1;
368 }
369
370 // the lump is only used for one rotation
371 if (sprtemp[frame].rotate == false)
372 {
373 for (r = 0; r < 16; r++)
374 {
375 sprtemp[frame].lump[r] = -1;
376 sprtemp[frame].flip[r] = false;
377 }
378 }
379
380 sprtemp[frame].rotate = true;
381 sprtemp[frame].lump[rotation] = lumpnr;
382 sprtemp[frame].flip[rotation] = flipped;
383 unguard;
384 }
385
386 //==========================================================================
387 //
388 // R_InstallSprite
389 //
390 // Builds the sprite rotation matrixes to account for horizontally flipped
391 // sprites. Will report an error if the lumps are inconsistant.
392 // Sprite lump names are 4 characters for the actor, a letter for the frame,
393 // and a number for the rotation. A sprite that is flippable will have an
394 // additional letter/number appended. The rotation character can be 0 to
395 // signify no rotations.
396 //
397 //==========================================================================
398
R_InstallSprite(const char * name,int index)399 void R_InstallSprite(const char *name, int index)
400 {
401 guard(R_InstallSprite);
402 if ((vuint32)index >= MAX_SPRITE_MODELS)
403 {
404 Host_Error("Invalid sprite index %d for sprite %s", index, name);
405 }
406 spritename = name;
407 memset(sprtemp, -1, sizeof(sprtemp));
408 maxframe = -1;
409
410 // scan all the lump names for each of the names,
411 // noting the highest frame letter.
412 // Just compare 4 characters as ints
413 int intname = *(int*)*VName(spritename, VName::AddLower8);
414
415 // scan the lumps, filling in the frames for whatever is found
416 for (int l = 0; l < GTextureManager.GetNumTextures(); l++)
417 {
418 if (GTextureManager[l]->Type == TEXTYPE_Sprite)
419 {
420 const char* lumpname = *GTextureManager[l]->Name;
421 if (*(int*)lumpname == intname)
422 {
423 InstallSpriteLump(l, VStr::ToUpper(lumpname[4]) - 'A',
424 lumpname[5], false);
425
426 if (lumpname[6])
427 {
428 InstallSpriteLump(l, VStr::ToUpper(lumpname[6]) - 'A',
429 lumpname[7], true);
430 }
431 }
432 }
433 }
434
435 // check the frames that were found for completeness
436 if (maxframe == -1)
437 {
438 sprites[index].numframes = 0;
439 return;
440 }
441
442 maxframe++;
443
444 for (int frame = 0 ; frame < maxframe ; frame++)
445 {
446 switch ((int)sprtemp[frame].rotate)
447 {
448 case -1:
449 // no rotations were found for that frame at all
450 Sys_Error("R_InstallSprite: No patches found "
451 "for %s frame %c", spritename, frame + 'A');
452 break;
453
454 case 0:
455 // only the first rotation is needed
456 break;
457
458 case 1:
459 // Copy missing frames for 16-angle rotation.
460 for (int rotation = 0; rotation < 8; rotation++)
461 {
462 if (sprtemp[frame].lump[rotation * 2 + 1] == -1)
463 {
464 sprtemp[frame].lump[rotation * 2 + 1] =
465 sprtemp[frame].lump[rotation * 2];
466 sprtemp[frame].flip[rotation * 2 + 1] =
467 sprtemp[frame].flip[rotation * 2];
468 }
469 if (sprtemp[frame].lump[rotation * 2] == -1)
470 {
471 sprtemp[frame].lump[rotation * 2] =
472 sprtemp[frame].lump[rotation * 2 + 1];
473 sprtemp[frame].flip[rotation * 2] =
474 sprtemp[frame].flip[rotation * 2 + 1];
475 }
476 }
477 // must have all 8 frames
478 for (int rotation = 0; rotation < 8; rotation++)
479 {
480 if (sprtemp[frame].lump[rotation] == -1)
481 {
482 Sys_Error("R_InstallSprite: Sprite %s frame %c "
483 "is missing rotations", spritename, frame + 'A');
484 }
485 }
486 break;
487 }
488 }
489
490 if (sprites[index].spriteframes)
491 {
492 Z_Free(sprites[index].spriteframes);
493 sprites[index].spriteframes = NULL;
494 }
495 // allocate space for the frames present and copy sprtemp to it
496 sprites[index].numframes = maxframe;
497 sprites[index].spriteframes = (spriteframe_t*)
498 Z_Malloc(maxframe * sizeof(spriteframe_t));
499 memcpy(sprites[index].spriteframes, sprtemp, maxframe * sizeof(spriteframe_t));
500 unguard;
501 }
502
503 //==========================================================================
504 //
505 // FreeSpriteData
506 //
507 //==========================================================================
508
FreeSpriteData()509 static void FreeSpriteData()
510 {
511 guard(FreeSpriteData);
512 for (int i = 0; i < MAX_SPRITE_MODELS; i++)
513 {
514 if (sprites[i].spriteframes)
515 {
516 Z_Free(sprites[i].spriteframes);
517 }
518 }
519 unguard;
520 }
521
522 //==========================================================================
523 //
524 // R_AreSpritesPresent
525 //
526 //==========================================================================
527
R_AreSpritesPresent(int Index)528 bool R_AreSpritesPresent(int Index)
529 {
530 guardSlow(R_AreSpritesPresent);
531 return sprites[Index].numframes > 0;
532 unguardSlow;
533 }
534
535 //==========================================================================
536 //
537 // R_InitData
538 //
539 //==========================================================================
540
R_InitData()541 void R_InitData()
542 {
543 guard(R_InitData);
544 // Load palette.
545 InitPalette();
546
547 // Calculate RGB table.
548 InitRgbTable();
549
550 // Init standard translation tables.
551 InitTranslationTables();
552
553 // Init colour maps.
554 InitColourMaps();
555 unguard;
556 }
557
558 //==========================================================================
559 //
560 // R_ShutdownData
561 //
562 //==========================================================================
563
R_ShutdownData()564 void R_ShutdownData()
565 {
566 guard(R_ShutdownData);
567 if (TranslationTables)
568 {
569 for (int i = 0; i < NumTranslationTables; i++)
570 {
571 delete TranslationTables[i];
572 TranslationTables[i] = NULL;
573 }
574 delete[] TranslationTables;
575 TranslationTables = NULL;
576 }
577
578 for (int i = 0; i < DecorateTranslations.Num(); i++)
579 {
580 delete DecorateTranslations[i];
581 DecorateTranslations[i] = NULL;
582 }
583 DecorateTranslations.Clear();
584
585 FreeSpriteData();
586
587 GLightEffectDefs.Clear();
588 GParticleEffectDefs.Clear();
589 unguard;
590 }
591
592 //==========================================================================
593 //
594 // VTextureTranslation::VTextureTranslation
595 //
596 //==========================================================================
597
VTextureTranslation()598 VTextureTranslation::VTextureTranslation()
599 : Crc(0)
600 , TranslStart(0)
601 , TranslEnd(0)
602 , Colour(0)
603 {
604 Clear();
605 }
606
607 //==========================================================================
608 //
609 // VTextureTranslation::Clear
610 //
611 //==========================================================================
612
Clear()613 void VTextureTranslation::Clear()
614 {
615 guard(VTextureTranslation::Clear);
616 for (int i = 0; i < 256; i++)
617 {
618 Table[i] = i;
619 Palette[i] = r_palette[i];
620 }
621 Commands.Clear();
622 CalcCrc();
623 unguard;
624 }
625
626 //==========================================================================
627 //
628 // VTextureTranslation::CalcCrc
629 //
630 //==========================================================================
631
CalcCrc()632 void VTextureTranslation::CalcCrc()
633 {
634 guard(VTextureTranslation::CalcCrc);
635 TCRC Work;
636 Work.Init();
637 for (int i = 1; i < 256; i++)
638 {
639 Work + Palette[i].r;
640 Work + Palette[i].g;
641 Work + Palette[i].b;
642 }
643 Crc = Work;
644 unguard;
645 }
646
647 //==========================================================================
648 //
649 // VTextureTranslation::Serialise
650 //
651 //==========================================================================
652
Serialise(VStream & Strm)653 void VTextureTranslation::Serialise(VStream& Strm)
654 {
655 guard(VTextureTranslation::Serialise);
656 Strm.Serialise(Table, 256);
657 Strm.Serialise(Palette, sizeof(Palette));
658 Strm << Crc
659 << TranslStart
660 << TranslEnd
661 << Colour;
662 int CmdsSize = Commands.Num();
663 Strm << STRM_INDEX(CmdsSize);
664 if (Strm.IsLoading())
665 {
666 Commands.SetNum(CmdsSize);
667 }
668 for (int i = 0; i < CmdsSize; i++)
669 {
670 VTransCmd& C = Commands[i];
671 Strm << C.Type << C.Start << C.End << C.R1 << C.G1 << C.B1 <<
672 C.R2 << C.G2 << C.B2;
673 }
674 unguard;
675 }
676
677 //==========================================================================
678 //
679 // VTextureTranslation::BuildPlayerTrans
680 //
681 //==========================================================================
682
BuildPlayerTrans(int Start,int End,int Col)683 void VTextureTranslation::BuildPlayerTrans(int Start, int End, int Col)
684 {
685 guard(VTextureTranslation::BuildPlayerTrans);
686 int Count = End - Start + 1;
687 vuint8 r = (Col >> 16) & 0xff;
688 vuint8 g = (Col >> 8) & 0xff;
689 vuint8 b = Col & 0xff;
690 vuint8 h, s, v;
691 M_RgbToHsv(r, g, b, h, s, v);
692 for (int i = 0; i < Count; i++)
693 {
694 int Idx = Start + i;
695 vuint8 TmpH, TmpS, TmpV;
696 M_RgbToHsv(Palette[Idx].r, Palette[Idx].g,Palette[Idx].b, TmpH, TmpS, TmpV);
697 M_HsvToRgb(h, s, v * TmpV / 255, Palette[Idx].r,
698 Palette[Idx].g, Palette[Idx].b);
699 Table[Idx] = R_LookupRGB(Palette[Idx].r, Palette[Idx].g,
700 Palette[Idx].b);
701 }
702 CalcCrc();
703 TranslStart = Start;
704 TranslEnd = End;
705 Colour = Col;
706 unguard;
707 }
708
709 //==========================================================================
710 //
711 // VTextureTranslation::MapToRange
712 //
713 //==========================================================================
714
MapToRange(int AStart,int AEnd,int ASrcStart,int ASrcEnd)715 void VTextureTranslation::MapToRange(int AStart, int AEnd, int ASrcStart,
716 int ASrcEnd)
717 {
718 guard(VTextureTranslation::MapToRange);
719 int Start;
720 int End;
721 int SrcStart;
722 int SrcEnd;
723 // Swap range if necesary.
724 if (AStart > AEnd)
725 {
726 Start = AEnd;
727 End = AStart;
728 SrcStart = ASrcEnd;
729 SrcEnd = ASrcStart;
730 }
731 else
732 {
733 Start = AStart;
734 End = AEnd;
735 SrcStart = ASrcStart;
736 SrcEnd = ASrcEnd;
737 }
738 // Check for single colour change.
739 if (Start == End)
740 {
741 Table[Start] = SrcStart;
742 Palette[Start] = r_palette[SrcStart];
743 return;
744 }
745 float CurCol = SrcStart;
746 float ColStep = (float(SrcEnd) - float(SrcStart)) /
747 (float(End) - float(Start));
748 for (int i = Start; i < End; i++, CurCol += ColStep)
749 {
750 Table[i] = int(CurCol);
751 Palette[i] = r_palette[Table[i]];
752 }
753 VTransCmd& C = Commands.Alloc();
754 C.Type = 0;
755 C.Start = Start;
756 C.End = End;
757 C.R1 = SrcStart;
758 C.R2 = SrcEnd;
759 CalcCrc();
760 unguard;
761 }
762
763 //==========================================================================
764 //
765 // VTextureTranslation::MapToColours
766 //
767 //==========================================================================
768
MapToColours(int AStart,int AEnd,int AR1,int AG1,int AB1,int AR2,int AG2,int AB2)769 void VTextureTranslation::MapToColours(int AStart, int AEnd, int AR1, int AG1,
770 int AB1, int AR2, int AG2, int AB2)
771 {
772 guard(VTextureTranslation::MapToColours);
773 int Start;
774 int End;
775 int R1;
776 int G1;
777 int B1;
778 int R2;
779 int G2;
780 int B2;
781 // Swap range if necesary.
782 if (AStart > AEnd)
783 {
784 Start = AEnd;
785 End = AStart;
786 R1 = AR2;
787 G1 = AG2;
788 B1 = AB2;
789 R2 = AR1;
790 G2 = AG1;
791 B2 = AB1;
792 }
793 else
794 {
795 Start = AStart;
796 End = AEnd;
797 R1 = AR1;
798 G1 = AG1;
799 B1 = AB1;
800 R2 = AR2;
801 G2 = AG2;
802 B2 = AB2;
803 }
804 // Check for single colour change.
805 if (Start == End)
806 {
807 Palette[Start].r = R1;
808 Palette[Start].g = G1;
809 Palette[Start].b = B1;
810 Table[Start] = R_LookupRGB(R1, G1, B1);
811 return;
812 }
813 float CurR = R1;
814 float CurG = G1;
815 float CurB = B1;
816 float RStep = (float(R2) - float(R1)) / (float(End) - float(Start));
817 float GStep = (float(G2) - float(G1)) / (float(End) - float(Start));
818 float BStep = (float(B2) - float(B1)) / (float(End) - float(Start));
819 for (int i = Start; i < End; i++, CurR += RStep, CurG += GStep,
820 CurB += BStep)
821 {
822 Palette[i].r = int(CurR);
823 Palette[i].g = int(CurG);
824 Palette[i].b = int(CurB);
825 Table[i] = R_LookupRGB(Palette[i].r, Palette[i].g, Palette[i].b);
826 }
827 VTransCmd& C = Commands.Alloc();
828 C.Type = 1;
829 C.Start = Start;
830 C.End = End;
831 C.R1 = R1;
832 C.G1 = G1;
833 C.B1 = B1;
834 C.R2 = R2;
835 C.G2 = G2;
836 C.B2 = B2;
837 CalcCrc();
838 unguard;
839 }
840
841 //==========================================================================
842 //
843 // VTextureTranslation::BuildBloodTrans
844 //
845 //==========================================================================
846
BuildBloodTrans(int Col)847 void VTextureTranslation::BuildBloodTrans(int Col)
848 {
849 guard(VTextureTranslation::BuildBloodTrans);
850 vuint8 r = (Col >> 16) & 0xff;
851 vuint8 g = (Col >> 8) & 0xff;
852 vuint8 b = Col & 0xff;
853 // Don't remap colour 0.
854 for (int i = 1; i < 256; i++)
855 {
856 int Bright = MAX(MAX(r_palette[i].r, r_palette[i].g), r_palette[i].b);
857 Palette[i].r = r * Bright / 255;
858 Palette[i].g = g * Bright / 255;
859 Palette[i].b = b * Bright / 255;
860 Table[i] = R_LookupRGB(Palette[i].r, Palette[i].g, Palette[i].b);
861 }
862 CalcCrc();
863 Colour = Col;
864 unguard;
865 }
866
867 //==========================================================================
868 //
869 // CheckChar
870 //
871 //==========================================================================
872
CheckChar(char * & pStr,char Chr)873 static bool CheckChar(char*& pStr, char Chr)
874 {
875 // Skip whitespace
876 while (*pStr && *pStr <= ' ')
877 {
878 pStr++;
879 }
880 if (*pStr != Chr)
881 {
882 return false;
883 }
884 pStr++;
885 return true;
886 }
887
888 //==========================================================================
889 //
890 // VTextureTranslation::AddTransString
891 //
892 //==========================================================================
893
AddTransString(const VStr & Str)894 void VTextureTranslation::AddTransString(const VStr& Str)
895 {
896 guard(VTextureTranslation::AddTransString);
897 char* pStr = const_cast<char*>(*Str);
898
899 // Parse start and end of the range
900 int Start = strtol(pStr, &pStr, 10);
901 if (!CheckChar(pStr, ':'))
902 {
903 return;
904 }
905 int End = strtol(pStr, &pStr, 10);
906 if (!CheckChar(pStr, '='))
907 {
908 return;
909 }
910 if (!CheckChar(pStr, '['))
911 {
912 int SrcStart = strtol(pStr, &pStr, 10);
913 if (!CheckChar(pStr, ':'))
914 {
915 return;
916 }
917 int SrcEnd = strtol(pStr, &pStr, 10);
918 MapToRange(Start, End, SrcStart, SrcEnd);
919 }
920 else
921 {
922 int R1 = strtol(pStr, &pStr, 10);
923 if (!CheckChar(pStr, ','))
924 {
925 return;
926 }
927 int G1 = strtol(pStr, &pStr, 10);
928 if (!CheckChar(pStr, ','))
929 {
930 return;
931 }
932 int B1 = strtol(pStr, &pStr, 10);
933 if (!CheckChar(pStr, ']'))
934 {
935 return;
936 }
937 if (!CheckChar(pStr, ':'))
938 {
939 return;
940 }
941 if (!CheckChar(pStr, '['))
942 {
943 return;
944 }
945 int R2 = strtol(pStr, &pStr, 10);
946 if (!CheckChar(pStr, ','))
947 {
948 return;
949 }
950 int G2 = strtol(pStr, &pStr, 10);
951 if (!CheckChar(pStr, ','))
952 {
953 return;
954 }
955 int B2 = strtol(pStr, &pStr, 10);
956 if (!CheckChar(pStr, ']'))
957 {
958 return;
959 }
960 MapToColours(Start, End, R1, G1, B1, R2, G2, B2);
961 }
962 unguard;
963 }
964
965 //==========================================================================
966 //
967 // R_ParseDecorateTranslation
968 //
969 //==========================================================================
970
R_ParseDecorateTranslation(VScriptParser * sc,int GameMax)971 int R_ParseDecorateTranslation(VScriptParser* sc, int GameMax)
972 {
973 guard(R_ParseDecorateTranslation);
974 // First check for standard translation.
975 if (sc->CheckNumber())
976 {
977 if (sc->Number < 0 || sc->Number >= MAX(NumTranslationTables, GameMax))
978 {
979 sc->Error(va("Translation must be in range [0, %d]",
980 MAX(NumTranslationTables, GameMax) - 1));
981 }
982 return (TRANSL_Standard << TRANSL_TYPE_SHIFT) + sc->Number;
983 }
984
985 // Check for special ice translation
986 if (sc->Check("Ice"))
987 {
988 return (TRANSL_Standard << TRANSL_TYPE_SHIFT) + 7;
989 }
990
991 VTextureTranslation* Tr = new VTextureTranslation;
992
993 do
994 {
995 sc->ExpectString();
996 Tr->AddTransString(sc->String);
997 }
998 while (sc->Check(","));
999
1000 // See if we already have this translation.
1001 for (int i = 0; i < DecorateTranslations.Num(); i++)
1002 {
1003 if (DecorateTranslations[i]->Crc != Tr->Crc)
1004 {
1005 continue;
1006 }
1007 if (memcmp(DecorateTranslations[i]->Palette, Tr->Palette,
1008 sizeof(Tr->Palette)))
1009 {
1010 continue;
1011 }
1012 // Found a match.
1013 delete Tr;
1014 Tr = NULL;
1015 return (TRANSL_Decorate << TRANSL_TYPE_SHIFT) + i;
1016 }
1017
1018 // Add it.
1019 if (DecorateTranslations.Num() >= MAX_DECORATE_TRANSLATIONS)
1020 {
1021 sc->Error("Too many translations in DECORATE scripts");
1022 }
1023 DecorateTranslations.Append(Tr);
1024 return (TRANSL_Decorate << TRANSL_TYPE_SHIFT) +
1025 DecorateTranslations.Num() - 1;
1026 unguard;
1027 }
1028
1029 //==========================================================================
1030 //
1031 // R_GetBloodTranslation
1032 //
1033 //==========================================================================
1034
R_GetBloodTranslation(int Col)1035 int R_GetBloodTranslation(int Col)
1036 {
1037 guard(R_GetBloodTranslation);
1038 // Check for duplicate blood translation.
1039 for (int i = 0; i < BloodTranslations.Num(); i++)
1040 {
1041 if (BloodTranslations[i]->Colour == Col)
1042 {
1043 return (TRANSL_Blood << TRANSL_TYPE_SHIFT) + i;
1044 }
1045 }
1046
1047 // Create new translation.
1048 VTextureTranslation* Tr = new VTextureTranslation;
1049 Tr->BuildBloodTrans(Col);
1050
1051 // Add it.
1052 if (BloodTranslations.Num() >= MAX_BLOOD_TRANSLATIONS)
1053 {
1054 Sys_Error("Too many blood colours in DECORATE scripts");
1055 }
1056 BloodTranslations.Append(Tr);
1057 return (TRANSL_Blood << TRANSL_TYPE_SHIFT) +
1058 BloodTranslations.Num() - 1;
1059 unguard;
1060 }
1061
1062 //==========================================================================
1063 //
1064 // FindLightEffect
1065 //
1066 //==========================================================================
1067
FindLightEffect(const VStr & Name)1068 static VLightEffectDef* FindLightEffect(const VStr& Name)
1069 {
1070 guard(FindLightEffect);
1071 for (int i = 0; i < GLightEffectDefs.Num(); i++)
1072 {
1073 if (GLightEffectDefs[i].Name == *Name)
1074 {
1075 return &GLightEffectDefs[i];
1076 }
1077 }
1078 return NULL;
1079 unguard;
1080 }
1081
1082 //==========================================================================
1083 //
1084 // ParseLightDef
1085 //
1086 //==========================================================================
1087
ParseLightDef(VScriptParser * sc,int LightType)1088 static void ParseLightDef(VScriptParser* sc, int LightType)
1089 {
1090 guard(ParseLightDef);
1091 // Get name, find it in the list or add it if it's not there yet.
1092 sc->ExpectString();
1093 VLightEffectDef* L = FindLightEffect(sc->String);
1094 if (!L)
1095 {
1096 L = &GLightEffectDefs.Alloc();
1097 }
1098
1099 // Set default values.
1100 L->Name = *sc->String.ToLower();
1101 L->Type = LightType;
1102 L->Colour = 0xffffffff;
1103 L->Radius = 0.0;
1104 L->Radius2 = 0.0;
1105 L->MinLight = 0.0;
1106 L->Offset = TVec(0, 0, 0);
1107 L->Chance = 0.0;
1108 L->Interval = 0.0;
1109 L->Scale = 0.0;
1110
1111 // Parse light def.
1112 sc->Expect("{");
1113 while (!sc->Check("}"))
1114 {
1115 if (sc->Check("colour"))
1116 {
1117 sc->ExpectFloat();
1118 float r = MID(0, sc->Float, 1);
1119 sc->ExpectFloat();
1120 float g = MID(0, sc->Float, 1);
1121 sc->ExpectFloat();
1122 float b = MID(0, sc->Float, 1);
1123 L->Colour = ((int)(r * 255) << 16) | ((int)(g * 255) << 8) |
1124 (int)(b * 255) | 0xff000000;
1125 }
1126 else if (sc->Check("radius"))
1127 {
1128 sc->ExpectFloat();
1129 L->Radius = sc->Float;
1130 }
1131 else if (sc->Check("radius2"))
1132 {
1133 sc->ExpectFloat();
1134 L->Radius2 = sc->Float;
1135 }
1136 else if (sc->Check("minlight"))
1137 {
1138 sc->ExpectFloat();
1139 L->MinLight = sc->Float;
1140 }
1141 else if (sc->Check("offset"))
1142 {
1143 sc->ExpectFloat();
1144 L->Offset.x = sc->Float;
1145 sc->ExpectFloat();
1146 L->Offset.y = sc->Float;
1147 sc->ExpectFloat();
1148 L->Offset.z = sc->Float;
1149 }
1150 else
1151 {
1152 sc->Error("Bad point light parameter");
1153 }
1154 }
1155 unguard;
1156 }
1157
1158 //==========================================================================
1159 //
1160 // IntensityToRadius
1161 //
1162 //==========================================================================
1163
IntensityToRadius(float Val)1164 float IntensityToRadius(float Val)
1165 {
1166 if (Val <= 20.0)
1167 {
1168 return Val * 4.5;
1169 }
1170 if (Val <= 30.0)
1171 {
1172 return Val * 3.6;
1173 }
1174 if (Val <= 40.0)
1175 {
1176 return Val * 3.3;
1177 }
1178 if (Val <= 60.0)
1179 {
1180 return Val * 2.8;
1181 }
1182 return Val * 2.5;
1183 }
1184
1185 //==========================================================================
1186 //
1187 // ParseGZLightDef
1188 //
1189 //==========================================================================
1190
ParseGZLightDef(VScriptParser * sc,int LightType)1191 static void ParseGZLightDef(VScriptParser* sc, int LightType)
1192 {
1193 guard(ParseGZLightDef);
1194 // Get name, find it in the list or add it if it's not there yet.
1195 sc->ExpectString();
1196 VLightEffectDef* L = FindLightEffect(sc->String);
1197 if (!L)
1198 {
1199 L = &GLightEffectDefs.Alloc();
1200 }
1201
1202 // Set default values.
1203 L->Name = *sc->String.ToLower();
1204 L->Type = LightType;
1205 L->Colour = 0xffffffff;
1206 L->Radius = 0.0;
1207 L->Radius2 = 0.0;
1208 L->MinLight = 0.0;
1209 L->Offset = TVec(0, 0, 0);
1210 L->Chance = 0.0;
1211 L->Interval = 0.0;
1212 L->Scale = 0.0;
1213
1214 // Parse light def.
1215 sc->Expect("{");
1216 while (!sc->Check("}"))
1217 {
1218 if (sc->Check("color"))
1219 {
1220 sc->ExpectFloat();
1221 float r = MID(0, sc->Float, 1);
1222 sc->ExpectFloat();
1223 float g = MID(0, sc->Float, 1);
1224 sc->ExpectFloat();
1225 float b = MID(0, sc->Float, 1);
1226 L->Colour = ((int)(r * 255) << 16) | ((int)(g * 255) << 8) |
1227 (int)(b * 255) | 0xff000000;
1228 }
1229 else if (sc->Check("size"))
1230 {
1231 sc->ExpectNumber();
1232 L->Radius = IntensityToRadius(float(sc->Number));
1233 }
1234 else if (sc->Check("secondarySize"))
1235 {
1236 sc->ExpectNumber();
1237 L->Radius2 = IntensityToRadius(float(sc->Number));
1238 }
1239 else if (sc->Check("offset"))
1240 {
1241 // GZDoom manages Z offset as Y offset...
1242 sc->ExpectNumber();
1243 L->Offset.x = float(sc->Number);
1244 sc->ExpectNumber();
1245 L->Offset.z = float(sc->Number);
1246 sc->ExpectNumber();
1247 L->Offset.y = float(sc->Number);
1248 }
1249 else if (sc->Check("subtractive"))
1250 {
1251 sc->ExpectNumber();
1252 sc->Message("Subtractive lights not supported.");
1253 }
1254 else if (sc->Check("chance"))
1255 {
1256 sc->ExpectFloat();
1257 L->Chance = sc->Float;
1258 }
1259 else if (sc->Check("scale"))
1260 {
1261 sc->ExpectFloat();
1262 L->Scale = sc->Float;
1263 }
1264 else if (sc->Check("interval"))
1265 {
1266 sc->ExpectFloat();
1267 L->Interval = sc->Float;
1268 }
1269 else if (sc->Check("additive"))
1270 {
1271 sc->ExpectNumber();
1272 sc->Message("Additive parameter not supported.");
1273 }
1274 else if (sc->Check("halo"))
1275 {
1276 sc->ExpectNumber();
1277 sc->Message("Halo parameter not supported.");
1278 }
1279 else if (sc->Check("dontlightself"))
1280 {
1281 sc->ExpectNumber();
1282 sc->Message("DontLightSelf parameter not supported.");
1283 }
1284 else
1285 {
1286 sc->Error("Bad point light parameter");
1287 }
1288 }
1289 unguard;
1290 }
1291
1292 //==========================================================================
1293 //
1294 // FindParticleEffect
1295 //
1296 //==========================================================================
1297
FindParticleEffect(const VStr & Name)1298 static VParticleEffectDef* FindParticleEffect(const VStr& Name)
1299 {
1300 guard(FindParticleEffect);
1301 for (int i = 0; i < GParticleEffectDefs.Num(); i++)
1302 {
1303 if (GParticleEffectDefs[i].Name == *Name)
1304 {
1305 return &GParticleEffectDefs[i];
1306 }
1307 }
1308 return NULL;
1309 unguard;
1310 }
1311
1312 //==========================================================================
1313 //
1314 // ParseParticleEffect
1315 //
1316 //==========================================================================
1317
ParseParticleEffect(VScriptParser * sc)1318 static void ParseParticleEffect(VScriptParser* sc)
1319 {
1320 guard(ParseParticleEffect);
1321 // Get name, find it in the list or add it if it's not there yet.
1322 sc->ExpectString();
1323 VParticleEffectDef* P = FindParticleEffect(sc->String);
1324 if (!P)
1325 {
1326 P = &GParticleEffectDefs.Alloc();
1327 }
1328
1329 // Set default values.
1330 P->Name = *sc->String.ToLower();
1331 P->Type = 0;
1332 P->Type2 = 0;
1333 P->Colour = 0xffffffff;
1334 P->Offset = TVec(0, 0, 0);
1335 P->Count = 0;
1336 P->OrgRnd = 0;
1337 P->Velocity = TVec(0, 0, 0);
1338 P->VelRnd = 0;
1339 P->Accel = 0;
1340 P->Grav = 0;
1341 P->Duration = 0;
1342 P->Ramp = 0;
1343
1344 // Parse light def.
1345 sc->Expect("{");
1346 while (!sc->Check("}"))
1347 {
1348 if (sc->Check("type"))
1349 {
1350 if (sc->Check("static"))
1351 {
1352 P->Type = 0;
1353 }
1354 else if (sc->Check("explode"))
1355 {
1356 P->Type = 1;
1357 }
1358 else if (sc->Check("explode2"))
1359 {
1360 P->Type = 2;
1361 }
1362 else
1363 {
1364 sc->Error("Bad type");
1365 }
1366 }
1367 else if (sc->Check("type2"))
1368 {
1369 if (sc->Check("static"))
1370 {
1371 P->Type2 = 0;
1372 }
1373 else if (sc->Check("explode"))
1374 {
1375 P->Type2 = 1;
1376 }
1377 else if (sc->Check("explode2"))
1378 {
1379 P->Type2 = 2;
1380 }
1381 else
1382 {
1383 sc->Error("Bad type");
1384 }
1385 }
1386 else if (sc->Check("colour"))
1387 {
1388 sc->ExpectFloat();
1389 float r = MID(0, sc->Float, 1);
1390 sc->ExpectFloat();
1391 float g = MID(0, sc->Float, 1);
1392 sc->ExpectFloat();
1393 float b = MID(0, sc->Float, 1);
1394 P->Colour = ((int)(r * 255) << 16) | ((int)(g * 255) << 8) |
1395 (int)(b * 255) | 0xff000000;
1396 }
1397 else if (sc->Check("offset"))
1398 {
1399 sc->ExpectFloat();
1400 P->Offset.x = sc->Float;
1401 sc->ExpectFloat();
1402 P->Offset.y = sc->Float;
1403 sc->ExpectFloat();
1404 P->Offset.z = sc->Float;
1405 }
1406 else if (sc->Check("count"))
1407 {
1408 sc->ExpectNumber();
1409 P->Count = sc->Number;
1410 }
1411 else if (sc->Check("originrandom"))
1412 {
1413 sc->ExpectFloat();
1414 P->OrgRnd = sc->Float;
1415 }
1416 else if (sc->Check("velocity"))
1417 {
1418 sc->ExpectFloat();
1419 P->Velocity.x = sc->Float;
1420 sc->ExpectFloat();
1421 P->Velocity.y = sc->Float;
1422 sc->ExpectFloat();
1423 P->Velocity.z = sc->Float;
1424 }
1425 else if (sc->Check("velocityrandom"))
1426 {
1427 sc->ExpectFloat();
1428 P->VelRnd = sc->Float;
1429 }
1430 else if (sc->Check("acceleration"))
1431 {
1432 sc->ExpectFloat();
1433 P->Accel = sc->Float;
1434 }
1435 else if (sc->Check("gravity"))
1436 {
1437 sc->ExpectFloat();
1438 P->Grav = sc->Float;
1439 }
1440 else if (sc->Check("duration"))
1441 {
1442 sc->ExpectFloat();
1443 P->Duration = sc->Float;
1444 }
1445 else if (sc->Check("ramp"))
1446 {
1447 sc->ExpectFloat();
1448 P->Ramp = sc->Float;
1449 }
1450 else
1451 {
1452 sc->Error("Bad point light parameter");
1453 }
1454 }
1455 unguard;
1456 }
1457
1458 //==========================================================================
1459 //
1460 // ParseClassEffects
1461 //
1462 //==========================================================================
1463
ParseClassEffects(VScriptParser * sc,TArray<VTempClassEffects> & ClassDefs)1464 static void ParseClassEffects(VScriptParser* sc,
1465 TArray<VTempClassEffects>& ClassDefs)
1466 {
1467 guard(ParseClassEffects);
1468 // Get name, find it in the list or add it if it's not there yet.
1469 sc->ExpectString();
1470 VTempClassEffects* C = NULL;
1471 for (int i = 0; i < ClassDefs.Num(); i++)
1472 {
1473 if (ClassDefs[i].ClassName == sc->String)
1474 {
1475 C = &ClassDefs[i];
1476 break;
1477 }
1478 }
1479 if (!C)
1480 {
1481 C = &ClassDefs.Alloc();
1482 }
1483
1484 // Set defaults.
1485 C->ClassName = sc->String;
1486 C->StaticLight.Clean();
1487 C->SpriteEffects.Clear();
1488
1489 // Parse
1490 sc->Expect("{");
1491 while (!sc->Check("}"))
1492 {
1493 if (sc->Check("frame"))
1494 {
1495 sc->ExpectString();
1496 VTempSpriteEffectDef& S = C->SpriteEffects.Alloc();
1497 S.Sprite = sc->String;
1498 sc->Expect("{");
1499 while (!sc->Check("}"))
1500 {
1501 if (sc->Check("light"))
1502 {
1503 sc->ExpectString();
1504 S.Light = sc->String.ToLower();
1505 }
1506 else if (sc->Check("particles"))
1507 {
1508 sc->ExpectString();
1509 S.Part = sc->String.ToLower();
1510 }
1511 else
1512 {
1513 sc->Error("Bad frame parameter");
1514 }
1515 }
1516 }
1517 else if (sc->Check("static_light"))
1518 {
1519 sc->ExpectString();
1520 C->StaticLight = sc->String.ToLower();
1521 }
1522 else
1523 {
1524 sc->Error("Bad class parameter");
1525 }
1526 }
1527 unguard;
1528 }
1529
1530 //==========================================================================
1531 //
1532 // ParseEffectDefs
1533 //
1534 //==========================================================================
1535
ParseEffectDefs(VScriptParser * sc,TArray<VTempClassEffects> & ClassDefs)1536 static void ParseEffectDefs(VScriptParser* sc,
1537 TArray<VTempClassEffects>& ClassDefs)
1538 {
1539 guard(ParseEffectDefs);
1540 while (!sc->AtEnd())
1541 {
1542 if (sc->Check("#include"))
1543 {
1544 sc->ExpectString();
1545 int Lump = W_CheckNumForFileName(sc->String);
1546 // Check WAD lump only if it's no longer than 8 characters and
1547 // has no path separator.
1548 if (Lump < 0 && sc->String.Length() <= 8 &&
1549 sc->String.IndexOf('/') < 0)
1550 {
1551 Lump = W_CheckNumForName(VName(*sc->String, VName::AddLower8));
1552 }
1553 if (Lump < 0)
1554 {
1555 sc->Error(va("Lump %s not found", *sc->String));
1556 }
1557 ParseEffectDefs(new VScriptParser(sc->String,
1558 W_CreateLumpReaderNum(Lump)), ClassDefs);
1559 continue;
1560 }
1561 else if (sc->Check("pointlight"))
1562 {
1563 ParseLightDef(sc, DLTYPE_Point);
1564 }
1565 else if (sc->Check("muzzleflashlight"))
1566 {
1567 ParseLightDef(sc, DLTYPE_MuzzleFlash);
1568 }
1569 else if (sc->Check("particleeffect"))
1570 {
1571 ParseParticleEffect(sc);
1572 }
1573 else if (sc->Check("class"))
1574 {
1575 ParseClassEffects(sc, ClassDefs);
1576 }
1577 else if (sc->Check("clear"))
1578 {
1579 ClassDefs.Clear();
1580 }
1581 else if (sc->Check("pwad"))
1582 {
1583 sc->Message("Tried to parse a WAD file, aborting parsing...");
1584 break;
1585 }
1586 else
1587 {
1588 sc->Error(va("Unknown command %s", *sc->String));
1589 }
1590 }
1591 delete sc;
1592 sc = NULL;
1593 unguard;
1594 }
1595
1596 //==========================================================================
1597 //
1598 // ParseGZDoomEffectDefs
1599 //
1600 //==========================================================================
1601
ParseGZDoomEffectDefs(VScriptParser * sc,TArray<VTempClassEffects> & ClassDefs)1602 static void ParseGZDoomEffectDefs(VScriptParser* sc,
1603 TArray<VTempClassEffects>& ClassDefs)
1604 {
1605 guard(ParseEffectDefs);
1606 while (!sc->AtEnd())
1607 {
1608 if (sc->Check("#include"))
1609 {
1610 sc->ExpectString();
1611 int Lump = W_CheckNumForFileName(sc->String);
1612 // Check WAD lump only if it's no longer than 8 characters and
1613 // has no path separator.
1614 if (Lump < 0 && sc->String.Length() <= 8 &&
1615 sc->String.IndexOf('/') < 0)
1616 {
1617 Lump = W_CheckNumForName(VName(*sc->String, VName::AddLower8));
1618 }
1619 if (Lump < 0)
1620 {
1621 sc->Error(va("Lump %s not found", *sc->String));
1622 }
1623 ParseGZDoomEffectDefs(new VScriptParser(sc->String,
1624 W_CreateLumpReaderNum(Lump)), ClassDefs);
1625 continue;
1626 }
1627 else if (sc->Check("pointlight"))
1628 {
1629 ParseGZLightDef(sc, DLTYPE_Point);
1630 }
1631 else if (sc->Check("pulselight"))
1632 {
1633 ParseGZLightDef(sc, DLTYPE_Pulse);
1634 }
1635 else if (sc->Check("flickerlight"))
1636 {
1637 ParseGZLightDef(sc, DLTYPE_Flicker);
1638 }
1639 else if (sc->Check("flickerlight2"))
1640 {
1641 ParseGZLightDef(sc, DLTYPE_FlickerRandom);
1642 }
1643 else if (sc->Check("sectorlight"))
1644 {
1645 ParseGZLightDef(sc, DLTYPE_Sector);
1646 }
1647 else if (sc->Check("object"))
1648 {
1649 ParseClassEffects(sc, ClassDefs);
1650 }
1651 else if (sc->Check("skybox"))
1652 {
1653 sc->Message("Skybox parsing isn't implemented yet.");
1654 }
1655 else if (sc->Check("brightmap"))
1656 {
1657 sc->Message("Brightmaps are not supported.");
1658 }
1659 else if (sc->Check("glow"))
1660 {
1661 sc->Message("Glowing textures aren't supported yet.");
1662 }
1663 else if (sc->Check("hardwareshader"))
1664 {
1665 sc->Message("Shaders are not supported");
1666 }
1667 else if (sc->Check("pwad"))
1668 {
1669 sc->Message("Tried to parse a WAD file, aborting parsing...");
1670 break;
1671 }
1672 else
1673 {
1674 sc->Error(va("Unknown command %s", *sc->String));
1675 }
1676 }
1677 delete sc;
1678 sc = NULL;
1679 unguard;
1680 }
1681
1682 //==========================================================================
1683 //
1684 // SetClassFieldInt
1685 //
1686 //==========================================================================
1687
SetClassFieldInt(VClass * Class,const char * FieldName,int Value,int Idx=0)1688 static void SetClassFieldInt(VClass* Class, const char* FieldName,
1689 int Value, int Idx = 0)
1690 {
1691 guard(SetClassFieldInt);
1692 VField* F = Class->FindFieldChecked(FieldName);
1693 vint32* Ptr = (vint32*)(Class->Defaults + F->Ofs);
1694 Ptr[Idx] = Value;
1695 unguard;
1696 }
1697
1698 //==========================================================================
1699 //
1700 // SetClassFieldBool
1701 //
1702 //==========================================================================
1703
SetClassFieldBool(VClass * Class,const char * FieldName,int Value)1704 static void SetClassFieldBool(VClass* Class, const char* FieldName, int Value)
1705 {
1706 guard(SetClassFieldBool);
1707 VField* F = Class->FindFieldChecked(FieldName);
1708 vuint32* Ptr = (vuint32*)(Class->Defaults + F->Ofs);
1709 if (Value)
1710 *Ptr |= F->Type.BitMask;
1711 else
1712 *Ptr &= ~F->Type.BitMask;
1713 unguard;
1714 }
1715
1716 //==========================================================================
1717 //
1718 // SetClassFieldFloat
1719 //
1720 //==========================================================================
1721
SetClassFieldFloat(VClass * Class,const char * FieldName,float Value)1722 static void SetClassFieldFloat(VClass* Class, const char* FieldName,
1723 float Value)
1724 {
1725 guard(SetClassFieldFloat);
1726 VField* F = Class->FindFieldChecked(FieldName);
1727 float* Ptr = (float*)(Class->Defaults + F->Ofs);
1728 *Ptr = Value;
1729 unguard;
1730 }
1731
1732 //==========================================================================
1733 //
1734 // SetClassFieldVec
1735 //
1736 //==========================================================================
1737
SetClassFieldVec(VClass * Class,const char * FieldName,const TVec & Value)1738 static void SetClassFieldVec(VClass* Class, const char* FieldName,
1739 const TVec& Value)
1740 {
1741 guard(SetClassFieldVec);
1742 VField* F = Class->FindFieldChecked(FieldName);
1743 TVec* Ptr = (TVec*)(Class->Defaults + F->Ofs);
1744 *Ptr = Value;
1745 unguard;
1746 }
1747
1748 //==========================================================================
1749 //
1750 // R_ParseEffectDefs
1751 //
1752 //==========================================================================
1753
R_ParseEffectDefs()1754 void R_ParseEffectDefs()
1755 {
1756 guard(R_ParseEffectDefs);
1757 GCon->Log(NAME_Init, "Parsing effect defs");
1758
1759 TArray<VTempClassEffects> ClassDefs;
1760
1761 // Parse VFXDEFS, GLDEFS, etc. scripts.
1762 for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
1763 Lump = W_IterateNS(Lump, WADNS_Global))
1764 {
1765 if (W_LumpName(Lump) == NAME_vfxdefs)
1766 {
1767 ParseEffectDefs(new VScriptParser(*W_LumpName(Lump),
1768 W_CreateLumpReaderNum(Lump)), ClassDefs);
1769 }
1770 if (W_LumpName(Lump) == NAME_gldefs ||
1771 W_LumpName(Lump) == NAME_doomdefs || W_LumpName(Lump) == NAME_hticdefs ||
1772 W_LumpName(Lump) == NAME_hexndefs || W_LumpName(Lump) == NAME_strfdefs)
1773 {
1774 ParseGZDoomEffectDefs(new VScriptParser(*W_LumpName(Lump),
1775 W_CreateLumpReaderNum(Lump)), ClassDefs);
1776 }
1777 }
1778
1779 // Add effects to the classes.
1780 for (int i = 0; i < ClassDefs.Num(); i++)
1781 {
1782 VTempClassEffects& CD = ClassDefs[i];
1783 VClass* Cls = VClass::FindClass(*CD.ClassName);
1784 if (Cls)
1785 {
1786 // Get class replacement
1787 Cls = Cls->GetReplacement();
1788 }
1789 else
1790 {
1791 GCon->Logf(NAME_Init, "No such class %s", *CD.ClassName);
1792 continue;
1793 }
1794
1795 if (CD.StaticLight.IsNotEmpty())
1796 {
1797 VLightEffectDef* SLight = FindLightEffect(CD.StaticLight);
1798 if (SLight)
1799 {
1800 SetClassFieldBool(Cls, "bStaticLight", true);
1801 SetClassFieldInt(Cls, "LightColour", SLight->Colour);
1802 SetClassFieldFloat(Cls, "LightRadius", SLight->Radius);
1803 SetClassFieldVec(Cls, "LightOffset", SLight->Offset);
1804 }
1805 else
1806 {
1807 GCon->Logf("Light \"%s\" not found", *CD.StaticLight);
1808 }
1809 }
1810
1811 for (int j = 0; j < CD.SpriteEffects.Num(); j++)
1812 {
1813 VTempSpriteEffectDef& SprDef = CD.SpriteEffects[j];
1814 // Sprite name must be either 4 or 5 chars.
1815 if (SprDef.Sprite.Length() != 4 && SprDef.Sprite.Length() != 5)
1816 {
1817 GCon->Logf(NAME_Init, "Bad sprite name length");
1818 continue;
1819 }
1820
1821 // Find sprite index.
1822 char SprName[8];
1823 SprName[0] = VStr::ToLower(SprDef.Sprite[0]);
1824 SprName[1] = VStr::ToLower(SprDef.Sprite[1]);
1825 SprName[2] = VStr::ToLower(SprDef.Sprite[2]);
1826 SprName[3] = VStr::ToLower(SprDef.Sprite[3]);
1827 SprName[4] = 0;
1828 int SprIdx = VClass::FindSprite(SprName, false);
1829 if (SprIdx == -1)
1830 {
1831 GCon->Logf(NAME_Init, "No such sprite %s", SprName);
1832 continue;
1833 }
1834
1835 VSpriteEffect& SprFx = Cls->SpriteEffects.Alloc();
1836 SprFx.SpriteIndex = SprIdx;
1837 SprFx.Frame = SprDef.Sprite.Length() == 4 ? -1 :
1838 (VStr::ToUpper(SprDef.Sprite[4]) - 'A');
1839 SprFx.LightDef = NULL;
1840 if (SprDef.Light.IsNotEmpty())
1841 {
1842 SprFx.LightDef = FindLightEffect(SprDef.Light);
1843 if (!SprFx.LightDef)
1844 {
1845 GCon->Logf("Light \"%s\" not found", *SprDef.Light);
1846 }
1847 }
1848 SprFx.PartDef = NULL;
1849 if (SprDef.Part.IsNotEmpty())
1850 {
1851 SprFx.PartDef = FindParticleEffect(SprDef.Part);
1852 if (!SprFx.PartDef)
1853 {
1854 GCon->Logf("Particle effect \"%s\" not found", *SprDef.Part);
1855 }
1856 }
1857 }
1858 }
1859 unguard;
1860 }
1861