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