1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: r_tex.cpp 4327 2010-07-24 19:30:53Z firebrand_kh $
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, caching,
27 //**  retrieval by name.
28 //**
29 //** 	Graphics.
30 //**
31 //** 	DOOM graphics for walls and sprites is stored in vertical runs of
32 //**  opaque pixels (posts). A column is composed of zero or more posts, a
33 //**  patch or sprite is composed of zero or more columns.
34 //**
35 //** 	Texture definition.
36 //**
37 //** 	Each texture is composed of one or more patches, with patches being
38 //**  lumps stored in the WAD. The lumps are referenced by number, and
39 //**  patched into the rectangular texture space using origin and possibly
40 //**  other attributes.
41 //**
42 //** 	Texture definition.
43 //**
44 //** 	A DOOM wall texture is a list of patches which are to be combined in
45 //**  a predefined order.
46 //**
47 //** 	A single patch from a texture definition, basically a rectangular
48 //**  area within the texture rectangle.
49 //**
50 //** 	A maptexturedef_t describes a rectangular texture, which is composed
51 //**  of one or more mappatch_t structures that arrange graphic patches.
52 //**
53 //** 	MAPTEXTURE_T CACHING
54 //**
55 //** 	When a texture is first needed, it counts the number of composite
56 //**  columns required in the texture and allocates space for a column
57 //**  directory and any new columns. The directory will simply point inside
58 //**  other patches if there is only one patch in a given column, but any
59 //**  columns with multiple patches will have new column_ts generated.
60 //**
61 //**************************************************************************
62 
63 // HEADER FILES ------------------------------------------------------------
64 
65 #include "gamedefs.h"
66 #include "r_tex.h"
67 
68 // MACROS ------------------------------------------------------------------
69 
70 // TYPES -------------------------------------------------------------------
71 
72 enum
73 {
74 	ANIM_Normal,
75 	ANIM_Forward,
76 	ANIM_Backward,
77 	ANIM_OscillateUp,
78 	ANIM_OscillateDown,
79 };
80 
81 struct frameDef_t
82 {
83 	vint16		Index;
84 	vint16		BaseTime;
85 	vint16		RandomRange;
86 };
87 
88 struct animDef_t
89 {
90 	vint16		Index;
91 	vint16		NumFrames;
92 	float		Time;
93 	vint16		StartFrameDef;
94 	vint16		CurrentFrame;
95 	vuint8		Type;
96 };
97 
98 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
99 
100 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
101 
102 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
103 
104 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
105 
106 // PUBLIC DATA DEFINITIONS -------------------------------------------------
107 
108 //
109 //	Texture manager
110 //
111 VTextureManager		GTextureManager;
112 
113 //
114 // Flats data
115 //
116 int					skyflatnum;			// sky mapping
117 
118 //	Switches
119 TArray<TSwitch*>	Switches;
120 
121 VCvarI				r_hirestex("r_hirestex", "1", CVAR_Archive);
122 
123 // PRIVATE DATA DEFINITIONS ------------------------------------------------
124 
125 static TArray<animDef_t>	AnimDefs;
126 static TArray<frameDef_t>	FrameDefs;
127 static TArray<VAnimDoorDef>	AnimDoorDefs;
128 
129 // CODE --------------------------------------------------------------------
130 
131 //==========================================================================
132 //
133 //	VTextureManager::VTextureManager
134 //
135 //==========================================================================
136 
VTextureManager()137 VTextureManager::VTextureManager()
138 : DefaultTexture(-1)
139 , Time(0)
140 {
141 	for (int i = 0; i < HASH_SIZE; i++)
142 	{
143 		TextureHash[i] = -1;
144 	}
145 }
146 
147 //==========================================================================
148 //
149 //	VTextureManager::Init
150 //
151 //==========================================================================
152 
Init()153 void VTextureManager::Init()
154 {
155 	guard(VTextureManager::Init);
156 	//	Add a dummy texture.
157 	AddTexture(new VDummyTexture);
158 
159 	//	Initialise wall textures.
160 	AddTextures();
161 
162 	//	Initialise flats.
163 	AddGroup(TEXTYPE_Flat, WADNS_Flats);
164 
165 	//	Initialise overloaded textures.
166 	AddGroup(TEXTYPE_Overload, WADNS_NewTextures);
167 
168 	//	Initialise sprites.
169 	AddGroup(TEXTYPE_Sprite, WADNS_Sprites);
170 
171 	//	Initialise hires textures.
172 	AddHiResTextures();
173 
174 	//	Find default texture.
175 	DefaultTexture = CheckNumForName("-noflat-", TEXTYPE_Overload, false,
176 		false);
177 	if (DefaultTexture == -1)
178 	{
179 		Sys_Error("Default texture -noflat- not found");
180 	}
181 
182 	//	Find sky flat number.
183 	skyflatnum = CheckNumForName(NAME_f_sky, TEXTYPE_Flat, true, false);
184 	if (skyflatnum < 0)
185 		skyflatnum = CheckNumForName(NAME_f_sky001, TEXTYPE_Flat, true,
186 			false);
187 	if (skyflatnum < 0)
188 		skyflatnum = NumForName(NAME_f_sky1, TEXTYPE_Flat, true, false);
189 	unguard;
190 }
191 
192 //==========================================================================
193 //
194 //	VTextureManager::Shutdown
195 //
196 //==========================================================================
197 
Shutdown()198 void VTextureManager::Shutdown()
199 {
200 	guard(VTextureManager::Shutdown);
201 	for (int i = 0; i < Textures.Num(); i++)
202 		delete Textures[i];
203 	Textures.Clear();
204 	unguard;
205 }
206 
207 //==========================================================================
208 //
209 //	VTextureManager::AddTexture
210 //
211 //==========================================================================
212 
AddTexture(VTexture * Tex)213 int VTextureManager::AddTexture(VTexture* Tex)
214 {
215 	guard(VTextureManager::AddTexture);
216 	if (!Tex)
217 	{
218 		return -1;
219 	}
220 	Textures.Append(Tex);
221 	Tex->TextureTranslation = Textures.Num() - 1;
222 	AddToHash(Textures.Num() - 1);
223 	return Textures.Num() - 1;
224 	unguard;
225 }
226 
227 //==========================================================================
228 //
229 //	VTextureManager::ReplaceTexture
230 //
231 //==========================================================================
232 
ReplaceTexture(int Index,VTexture * NewTex)233 void VTextureManager::ReplaceTexture(int Index, VTexture* NewTex)
234 {
235 	guard(VTextureManager::ReplaceTexture);
236 	check(Index >= 0);
237 	check(Index < Textures.Num());
238 	check(NewTex);
239 	VTexture* OldTex = Textures[Index];
240 	NewTex->Name = OldTex->Name;
241 	NewTex->Type = OldTex->Type;
242 	NewTex->TextureTranslation = OldTex->TextureTranslation;
243 	NewTex->HashNext = OldTex->HashNext;
244 	Textures[Index] = NewTex;
245 	unguard;
246 }
247 
248 //==========================================================================
249 //
250 //	VTextureManager::AddToHash
251 //
252 //==========================================================================
253 
AddToHash(int Index)254 void VTextureManager::AddToHash(int Index)
255 {
256 	guard(VTextureManager::AddToHash);
257 	int HashIndex = GetTypeHash(Textures[Index]->Name) & (HASH_SIZE - 1);
258 	Textures[Index]->HashNext = TextureHash[HashIndex];
259 	TextureHash[HashIndex] = Index;
260 	unguard;
261 }
262 
263 //==========================================================================
264 //
265 //	VTextureManager::RemoveFromHash
266 //
267 //==========================================================================
268 
RemoveFromHash(int Index)269 void VTextureManager::RemoveFromHash(int Index)
270 {
271 	guard(VTextureManager::RemoveFromHash);
272 	int HashIndex = GetTypeHash(Textures[Index]->Name) & (HASH_SIZE - 1);
273 	int* Prev = &TextureHash[HashIndex];
274 	while (*Prev != -1 && *Prev != Index)
275 	{
276 		Prev = &Textures[*Prev]->HashNext;
277 	}
278 	check(*Prev != -1);
279 	*Prev = Textures[Index]->HashNext;
280 	unguard;
281 }
282 
283 //==========================================================================
284 //
285 //  VTextureManager::CheckNumForName
286 //
287 // 	Check whether texture is available. Filter out NoTexture indicator.
288 //
289 //==========================================================================
290 
CheckNumForName(VName Name,int Type,bool bOverload,bool bCheckAny)291 int VTextureManager::CheckNumForName(VName Name, int Type, bool bOverload,
292 	bool bCheckAny)
293 {
294 	guard(VTextureManager::CheckNumForName);
295 	//	Check for "NoTexture" marker.
296 	if ((*Name)[0] == '-' && (*Name)[1] == 0)
297 	{
298 		return 0;
299 	}
300 
301 	int HashIndex = GetTypeHash(Name) & (HASH_SIZE - 1);
302 	for (int i = TextureHash[HashIndex]; i >= 0; i = Textures[i]->HashNext)
303 	{
304 		if (Textures[i]->Name != Name)
305 		{
306 			continue;
307 		}
308 
309 		if (Type == TEXTYPE_Any || Textures[i]->Type == Type ||
310 			(bOverload && Textures[i]->Type == TEXTYPE_Overload))
311 		{
312 			if (Textures[i]->Type == TEXTYPE_Null)
313 			{
314 				return 0;
315 			}
316 			return i;
317 		}
318 	}
319 
320 	if (bCheckAny)
321 	{
322 		return CheckNumForName(Name, TEXTYPE_Any, bOverload, false);
323 	}
324 
325 	return -1;
326 	unguard;
327 }
328 
329 //==========================================================================
330 //
331 // 	VTextureManager::NumForName
332 //
333 // 	Calls R_CheckTextureNumForName, aborts with error message.
334 //
335 //==========================================================================
336 
NumForName(VName Name,int Type,bool bOverload,bool bCheckAny)337 int	VTextureManager::NumForName(VName Name, int Type, bool bOverload,
338 	bool bCheckAny)
339 {
340 	guard(VTextureManager::NumForName);
341 	int i = CheckNumForName(Name, Type, bOverload, bCheckAny);
342 	if (i == -1)
343 	{
344 		GCon->Logf("VTextureManager::NumForName: %s not found", *Name);
345 		i = DefaultTexture;
346 	}
347 	return i;
348 	unguard;
349 }
350 
351 //==========================================================================
352 //
353 //	VTextureManager::FindTextureByLumpNum
354 //
355 //==========================================================================
356 
FindTextureByLumpNum(int LumpNum)357 int VTextureManager::FindTextureByLumpNum(int LumpNum)
358 {
359 	guard(VTextureManager::FindTextureByLumpNum);
360 	for (int i = 0; i < Textures.Num(); i++)
361 	{
362 		if (Textures[i]->SourceLump == LumpNum)
363 		{
364 			return i;
365 		}
366 	}
367 	return -1;
368 	unguard;
369 }
370 
371 //==========================================================================
372 //
373 //	VTextureManager::TextureHeight
374 //
375 //==========================================================================
376 
GetTextureName(int TexNum)377 VName VTextureManager::GetTextureName(int TexNum)
378 {
379 	guard(VTextureManager::GetTextureName);
380 	if (TexNum < 0 || TexNum >= Textures.Num())
381 	{
382 		return NAME_None;
383 	}
384 	return Textures[TexNum]->Name;
385 	unguard;
386 }
387 
388 //==========================================================================
389 //
390 //	VTextureManager::TextureHeight
391 //
392 //==========================================================================
393 
TextureHeight(int TexNum)394 float VTextureManager::TextureHeight(int TexNum)
395 {
396 	guard(VTextureManager::TextureHeight);
397 	return Textures[TexNum]->GetHeight() / Textures[TexNum]->TScale;
398 	unguard;
399 }
400 
401 //==========================================================================
402 //
403 //	VTextureManager::TextureAnimation
404 //
405 //==========================================================================
406 
TextureAnimation(int InTex)407 int VTextureManager::TextureAnimation(int InTex)
408 {
409 	guard(VTextureManager::TextureAnimation);
410 	return Textures[InTex]->TextureTranslation;
411 	unguard;
412 }
413 
414 //==========================================================================
415 //
416 //	VTextureManager::SetFrontSkyLayer
417 //
418 //==========================================================================
419 
SetFrontSkyLayer(int tex)420 void VTextureManager::SetFrontSkyLayer(int tex)
421 {
422 	guard(VTextureManager::SetFrontSkyLayer);
423 	Textures[tex]->SetFrontSkyLayer();
424 	unguard;
425 }
426 
427 //==========================================================================
428 //
429 //	VTextureManager::GetTextureInfo
430 //
431 //==========================================================================
432 
GetTextureInfo(int TexNum,picinfo_t * info)433 void VTextureManager::GetTextureInfo(int TexNum, picinfo_t* info)
434 {
435 	guard(VTextureManager::GetTextureInfo);
436 	if (TexNum < 0)
437 	{
438 		memset(info, 0, sizeof(*info));
439 	}
440 	else
441 	{
442 		VTexture* Tex = Textures[TexNum];
443 		info->width = Tex->GetWidth();
444 		info->height = Tex->GetHeight();
445 		info->xoffset = Tex->SOffset;
446 		info->yoffset = Tex->TOffset;
447 	}
448 	unguard;
449 }
450 
451 //==========================================================================
452 //
453 //	VTextureManager::AddPatch
454 //
455 //==========================================================================
456 
AddPatch(VName Name,int Type,bool Silent)457 int VTextureManager::AddPatch(VName Name, int Type, bool Silent)
458 {
459 	guard(VTextureManager::AddPatch);
460 	//	Find the lump number.
461 	int LumpNum = W_CheckNumForName(Name, WADNS_Graphics);
462 	if (LumpNum < 0)
463 		LumpNum = W_CheckNumForName(Name, WADNS_Sprites);
464 	if (LumpNum < 0)
465 	{
466 		if (!Silent)
467 		{
468 			GCon->Logf("VTextureManager::AddPatch: Pic %s not found", *Name);
469 		}
470 		return -1;
471 	}
472 
473 	//	Check if it's already registered.
474 	int i = CheckNumForName(Name, Type);
475 	if (i >= 0)
476 	{
477 		return i;
478 	}
479 
480 	//	Create new patch texture.
481 	return AddTexture(VTexture::CreateTexture(Type, LumpNum));
482 	unguard;
483 }
484 
485 //==========================================================================
486 //
487 //	VTextureManager::AddRawWithPal
488 //
489 //	Adds a raw image with custom palette lump. It's here to support
490 // Heretic's episode 2 finale pic.
491 //
492 //==========================================================================
493 
AddRawWithPal(VName Name,VName PalName)494 int VTextureManager::AddRawWithPal(VName Name, VName PalName)
495 {
496 	guard(VTextureManager::AddRawWithPal);
497 	int LumpNum = W_CheckNumForName(Name, WADNS_Graphics);
498 	if (LumpNum < 0)
499 	{
500 		GCon->Logf("VTextureManager::AddRawWithPal: %s not found", *Name);
501 		return -1;
502 	}
503 	//	Check if lump's size to see if it really is a raw image. If not,
504 	// load it as regular image.
505 	if (W_LumpLength(LumpNum) != 64000)
506 	{
507 		GCon->Logf("VTextureManager::AddRawWithPal: %s doesn't appear to be"
508 			" a raw image", *Name);
509 		return AddPatch(Name, TEXTYPE_Pic);
510 	}
511 
512 	int i = CheckNumForName(Name, TEXTYPE_Pic);
513 	if (i >= 0)
514 	{
515 		return i;
516 	}
517 
518 	return AddTexture(new VRawPicTexture(LumpNum,
519 		W_GetNumForName(PalName)));
520 	unguard;
521 }
522 
523 //==========================================================================
524 //
525 //	VTextureManager::AddFileTexture
526 //
527 //==========================================================================
528 
AddFileTexture(VName Name,int Type)529 int VTextureManager::AddFileTexture(VName Name, int Type)
530 {
531 	guard(VTextureManager::AddFileTexture)
532 	int i = CheckNumForName(Name, Type);
533 	if (i >= 0)
534 	{
535 		return i;
536 	}
537 
538 	i = W_CheckNumForFileName(*Name);
539 	if (i >= 0)
540 	{
541 		VTexture* Tex = VTexture::CreateTexture(Type, i);
542 		if (Tex)
543 		{
544 			Tex->Name = Name;
545 			return AddTexture(Tex);
546 		}
547 	}
548 
549 	GCon->Logf("Couldn\'t create texture %s.", *Name);
550 	return DefaultTexture;
551 	unguard;
552 }
553 
554 //==========================================================================
555 //
556 //	VTextureManager::AddTextures
557 //
558 // 	Initialises the texture list with the textures from the world map.
559 //
560 //==========================================================================
561 
AddTextures()562 void VTextureManager::AddTextures()
563 {
564 	guard(VTextureManager::AddTextures);
565 	int NamesFile = -1;
566 	int LumpTex1 = -1;
567 	int LumpTex2 = -1;
568 	int FirstTex;
569 
570 	//	For each PNAMES lump load TEXTURE1 and TEXTURE2 from the same wad.
571 	for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
572 		Lump = W_IterateNS(Lump, WADNS_Global))
573 	{
574 		if (W_LumpName(Lump) != NAME_pnames)
575 		{
576 			continue;
577 		}
578 		NamesFile = W_LumpFile(Lump);
579 		LumpTex1 = W_CheckNumForNameInFile(NAME_texture1, NamesFile);
580 		LumpTex2 = W_CheckNumForNameInFile(NAME_texture2, NamesFile);
581 		FirstTex = Textures.Num();
582 		AddTexturesLump(Lump, LumpTex1, FirstTex, true);
583 		AddTexturesLump(Lump, LumpTex2, FirstTex, false);
584 	}
585 
586 	//	If last TEXTURE1 or TEXTURE2 are in a wad without a PNAMES, they
587 	// must be loaded too.
588 	int LastTex1 = W_CheckNumForName(NAME_texture1);
589 	int LastTex2 = W_CheckNumForName(NAME_texture2);
590 	if (LastTex1 >= 0 && (LastTex1 == LumpTex1 ||
591 		W_LumpFile(LastTex1) <= NamesFile))
592 	{
593 		LastTex1 = -1;
594 	}
595 	if (LastTex2 >= 0 && (LastTex2 == LumpTex2 ||
596 		W_LumpFile(LastTex2) <= NamesFile))
597 	{
598 		LastTex2 = -1;
599 	}
600 	FirstTex = Textures.Num();
601 	AddTexturesLump(W_GetNumForName(NAME_pnames), LastTex1, FirstTex, true);
602 	AddTexturesLump(W_GetNumForName(NAME_pnames), LastTex2, FirstTex, false);
603 	unguard;
604 }
605 
606 //==========================================================================
607 //
608 //	VTextureManager::AddTexturesLump
609 //
610 //==========================================================================
611 
AddTexturesLump(int NamesLump,int TexLump,int FirstTex,bool First)612 void VTextureManager::AddTexturesLump(int NamesLump, int TexLump,
613 	int FirstTex, bool First)
614 {
615 	guard(VTextureManager::AddTexturesLump);
616 	if (TexLump < 0)
617 	{
618 		return;
619 	}
620 
621 	//	Load the patch names from pnames.lmp.
622 	VStream* Strm = W_CreateLumpReaderNum(NamesLump);
623 	vint32 nummappatches = Streamer<vint32>(*Strm);
624 	VTexture** patchtexlookup = new VTexture*[nummappatches];
625 	for (int i = 0; i < nummappatches; i++)
626 	{
627 		//	Read patch name.
628 		char TmpName[12];
629 		Strm->Serialise(TmpName, 8);
630 		TmpName[8] = 0;
631 		VName PatchName(TmpName, VName::AddLower8);
632 
633 		//	Check if it's already has ben added.
634 		int PIdx = CheckNumForName(PatchName, TEXTYPE_WallPatch, false, false);
635 		if (PIdx >= 0)
636 		{
637 			patchtexlookup[i] = Textures[PIdx];
638 			continue;
639 		}
640 
641 		//	Get wad lump number.
642 		int LNum = W_CheckNumForName(PatchName, WADNS_Patches);
643 		//	Sprites also can be used as patches.
644 		if (LNum < 0)
645 		{
646 			LNum = W_CheckNumForName(PatchName, WADNS_Sprites);
647 		}
648 
649 		//	Add it to textures.
650 		if (LNum < 0)
651 		{
652 			patchtexlookup[i] = NULL;
653 		}
654 		else
655 		{
656 			patchtexlookup[i] = VTexture::CreateTexture(TEXTYPE_WallPatch,
657 				LNum);
658 			AddTexture(patchtexlookup[i]);
659 		}
660 	}
661 	delete Strm;
662 	Strm = NULL;
663 
664 	//	Load the map texture definitions from textures.lmp.
665 	//	The data is contained in one or two lumps, TEXTURE1 for shareware,
666 	// plus TEXTURE2 for commercial.
667 	Strm = W_CreateLumpReaderNum(TexLump);
668 	vint32 NumTex = Streamer<vint32>(*Strm);
669 
670 	//	Check the texture file format.
671 	bool IsStrife = false;
672 	vint32 PrevOffset = Streamer<vint32>(*Strm);
673 	for (int i = 0; i < NumTex - 1; i++)
674 	{
675 		vint32 Offset = Streamer<vint32>(*Strm);
676 		if (Offset - PrevOffset == 24)
677 		{
678 			IsStrife = true;
679 			GCon->Log(NAME_Init, "Strife textures detected");
680 			break;
681 		}
682 		PrevOffset = Offset;
683 	}
684 
685 	for (int i = 0; i < NumTex; i++)
686 	{
687 		VMultiPatchTexture* Tex = new VMultiPatchTexture(*Strm, i,
688 			patchtexlookup, nummappatches, FirstTex, IsStrife);
689 		AddTexture(Tex);
690 		if (i == 0 && First)
691 		{
692 			//	Copy dimensions of the first texture to the dummy texture in
693 			// case they are used.
694 			Textures[0]->Width = Tex->Width;
695 			Textures[0]->Height = Tex->Height;
696 			Tex->Type = TEXTYPE_Null;
697 		}
698 	}
699 	delete Strm;
700 	Strm = NULL;
701 	delete[] patchtexlookup;
702 	patchtexlookup = NULL;
703 	unguard;
704 }
705 
706 //==========================================================================
707 //
708 //	VTextureManager::AddGroup
709 //
710 //==========================================================================
711 
AddGroup(int Type,EWadNamespace Namespace)712 void VTextureManager::AddGroup(int Type, EWadNamespace Namespace)
713 {
714 	guard(VTextureManager::AddGroup);
715 	for (int Lump = W_IterateNS(-1, Namespace); Lump >= 0;
716 		Lump = W_IterateNS(Lump, Namespace))
717 	{
718 		//	To avoid duplicates, add only the last one.
719 		if (W_GetNumForName(W_LumpName(Lump), Namespace) != Lump)
720 		{
721 			continue;
722 		}
723 		AddTexture(VTexture::CreateTexture(Type, Lump));
724 	}
725 	unguard;
726 }
727 
728 //==========================================================================
729 //
730 //	VTextureManager::AddHiResTextures
731 //
732 //==========================================================================
733 
AddHiResTextures()734 void VTextureManager::AddHiResTextures()
735 {
736 	guard(VTextureManager::AddHiResTextures);
737 	for (int Lump = W_IterateNS(-1, WADNS_HiResTextures); Lump >= 0;
738 		Lump = W_IterateNS(Lump, WADNS_HiResTextures))
739 	{
740 		VName Name = W_LumpName(Lump);
741 		//	To avoid duplicates, add only the last one.
742 		if (W_GetNumForName(Name, WADNS_HiResTextures) != Lump)
743 		{
744 			continue;
745 		}
746 
747 		//	Create new texture.
748 		VTexture* NewTex = VTexture::CreateTexture(TEXTYPE_Any, Lump);
749 		if (!NewTex)
750 		{
751 			continue;
752 		}
753 
754 		//	Find texture to replace.
755 		int OldIdx = CheckNumForName(Name, TEXTYPE_Wall, true, true);
756 		if (OldIdx < 0)
757 		{
758 			OldIdx = AddPatch(Name, TEXTYPE_Pic, true);
759 		}
760 
761 		if (OldIdx < 0)
762 		{
763 			//	Add it as a new texture.
764 			NewTex->Type = TEXTYPE_Overload;
765 			AddTexture(NewTex);
766 		}
767 		else
768 		{
769 			//	Repalce existing texture by adjusting scale and offsets.
770 			VTexture* OldTex = Textures[OldIdx];
771 			NewTex->bWorldPanning = true;
772 			NewTex->SScale = NewTex->GetWidth() / OldTex->GetWidth();
773 			NewTex->TScale = NewTex->GetHeight() / OldTex->GetHeight();
774 			NewTex->SOffset = (int)floor(OldTex->SOffset * NewTex->SScale);
775 			NewTex->TOffset = (int)floor(OldTex->TOffset * NewTex->TScale);
776 			NewTex->Type = OldTex->Type;
777 			NewTex->TextureTranslation = OldTex->TextureTranslation;
778 			Textures[OldIdx] = NewTex;
779 			delete OldTex;
780 			OldTex = NULL;
781 		}
782 	}
783 
784 	for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
785 		Lump = W_IterateNS(Lump, WADNS_Global))
786 	{
787 		if (W_LumpName(Lump) != NAME_hirestex &&
788 			W_LumpName(Lump) != NAME_textures)
789 		{
790 			continue;
791 		}
792 
793 		VScriptParser* sc = new VScriptParser(*W_LumpName(Lump),
794 			W_CreateLumpReaderNum(Lump));
795 		while (!sc->AtEnd())
796 		{
797 			if (sc->Check("remap"))
798 			{
799 				int Type = TEXTYPE_Any;
800 				bool Overload = false;
801 				if (sc->Check("wall"))
802 				{
803 					Type = TEXTYPE_Wall;
804 					Overload = true;
805 				}
806 				else if (sc->Check("flat"))
807 				{
808 					Type = TEXTYPE_Flat;
809 					Overload = true;
810 				}
811 				else if (sc->Check("sprite"))
812 				{
813 					Type = TEXTYPE_Sprite;
814 				}
815 
816 				sc->ExpectName8();
817 				int OldIdx = CheckNumForName(sc->Name8, Type, Overload, false);
818 				if (OldIdx < 0)
819 				{
820 					OldIdx = AddPatch(sc->Name8, TEXTYPE_Pic, true);
821 				}
822 
823 				sc->ExpectName8();
824 				int LumpIdx = W_CheckNumForName(sc->Name8, WADNS_Graphics);
825 				if (OldIdx < 0 || LumpIdx < 0)
826 				{
827 					continue;
828 				}
829 
830 				//	Create new texture.
831 				VTexture* NewTex = VTexture::CreateTexture(TEXTYPE_Any,
832 					LumpIdx);
833 				if (!NewTex)
834 				{
835 					continue;
836 				}
837 				//	Repalce existing texture by adjusting scale and offsets.
838 				VTexture* OldTex = Textures[OldIdx];
839 				NewTex->bWorldPanning = true;
840 				NewTex->SScale = NewTex->GetWidth() / OldTex->GetWidth();
841 				NewTex->TScale = NewTex->GetHeight() / OldTex->GetHeight();
842 				NewTex->SOffset = (int)floor(OldTex->SOffset * NewTex->SScale);
843 				NewTex->TOffset = (int)floor(OldTex->TOffset * NewTex->TScale);
844 				NewTex->Name = OldTex->Name;
845 				NewTex->Type = OldTex->Type;
846 				NewTex->TextureTranslation = OldTex->TextureTranslation;
847 				Textures[OldIdx] = NewTex;
848 				delete OldTex;
849 				OldTex = NULL;
850 			}
851 			else if (sc->Check("define"))
852 			{
853 				sc->ExpectName8();
854 				VName Name = sc->Name8;
855 				int LumpIdx = W_CheckNumForName(sc->Name8, WADNS_Graphics);
856 				sc->Check("force32bit");
857 
858 				//	Dimensions
859 				sc->ExpectNumber();
860 				int Width = sc->Number;
861 				sc->ExpectNumber();
862 				int Height = sc->Number;
863 				if (LumpIdx < 0)
864 				{
865 					continue;
866 				}
867 
868 				//	Create new texture.
869 				VTexture* NewTex = VTexture::CreateTexture(TEXTYPE_Overload,
870 					LumpIdx);
871 				if (!NewTex)
872 				{
873 					continue;
874 				}
875 
876 				//	Repalce existing texture by adjusting scale and offsets.
877 				NewTex->bWorldPanning = true;
878 				NewTex->SScale = NewTex->GetWidth() / Width;
879 				NewTex->TScale = NewTex->GetHeight() / Height;
880 				NewTex->Name = Name;
881 
882 				int OldIdx = CheckNumForName(Name, TEXTYPE_Overload, false,
883 					false);
884 				if (OldIdx >= 0)
885 				{
886 					VTexture* OldTex = Textures[OldIdx];
887 					NewTex->TextureTranslation = OldTex->TextureTranslation;
888 					Textures[OldIdx] = NewTex;
889 					delete OldTex;
890 					OldTex = NULL;
891 				}
892 				else
893 				{
894 					AddTexture(NewTex);
895 				}
896 			}
897 			else if (sc->Check("walltexture"))
898 			{
899 				AddTexture(new VMultiPatchTexture(sc, TEXTYPE_Wall));
900 			}
901 			else if (sc->Check("flat"))
902 			{
903 				AddTexture(new VMultiPatchTexture(sc, TEXTYPE_Flat));
904 			}
905 			else if (sc->Check("texture"))
906 			{
907 				AddTexture(new VMultiPatchTexture(sc, TEXTYPE_Overload));
908 			}
909 			else if (sc->Check("sprite"))
910 			{
911 				AddTexture(new VMultiPatchTexture(sc, TEXTYPE_Sprite));
912 			}
913 			else if (sc->Check("graphic"))
914 			{
915 				AddTexture(new VMultiPatchTexture(sc, TEXTYPE_Pic));
916 			}
917 			else
918 			{
919 				sc->Error("Bad command");
920 			}
921 		}
922 		delete sc;
923 		sc = NULL;
924 	}
925 	unguard;
926 }
927 
928 //==========================================================================
929 //
930 //	P_InitAnimated
931 //
932 //	Load the table of animation definitions, checking for existence of
933 // the start and end of each frame. If the start doesn't exist the sequence
934 // is skipped, if the last doesn't exist, BOOM exits.
935 //
936 //	Wall/Flat animation sequences, defined by name of first and last frame,
937 // The full animation sequence is given using all lumps between the start
938 // and end entry, in the order found in the WAD file.
939 //
940 //	This routine modified to read its data from a predefined lump or
941 // PWAD lump called ANIMATED rather than a static table in this module to
942 // allow wad designers to insert or modify animation sequences.
943 //
944 //	Lump format is an array of byte packed animdef_t structures, terminated
945 // by a structure with istexture == -1. The lump can be generated from a
946 // text source file using SWANTBLS.EXE, distributed with the BOOM utils.
947 // The standard list of switches and animations is contained in the example
948 // source text file DEFSWANI.DAT also in the BOOM util distribution.
949 //
950 //==========================================================================
951 
P_InitAnimated()952 void P_InitAnimated()
953 {
954 	guard(P_InitAnimated);
955 	animDef_t 	ad;
956 	frameDef_t	fd;
957 
958 	if (W_CheckNumForName(NAME_animated) < 0)
959 	{
960 		return;
961 	}
962 
963 	VStream* Strm = W_CreateLumpReaderName(NAME_animated);
964 	while (Strm->TotalSize() - Strm->Tell() >= 23)
965 	{
966 		int pic1, pic2;
967 		vint8 Type;
968 		char TmpName1[9];
969 		char TmpName2[9];
970 		vint32 BaseTime;
971 
972 		*Strm << Type;
973 		Strm->Serialise(TmpName1, 9);
974 		Strm->Serialise(TmpName2, 9);
975 		*Strm << BaseTime;
976 
977 		if (Type == -1)
978 		{
979 			//	Terminator marker.
980 			break;
981 		}
982 
983 		if (Type & 1)
984 		{
985 			pic1 = GTextureManager.CheckNumForName(VName(TmpName2,
986 				VName::AddLower8), TEXTYPE_Wall, true, false);
987 			pic2 = GTextureManager.CheckNumForName(VName(TmpName1,
988 				VName::AddLower8), TEXTYPE_Wall, true, false);
989 		}
990 		else
991 		{
992 			pic1 = GTextureManager.CheckNumForName(VName(TmpName2,
993 				VName::AddLower8), TEXTYPE_Flat, true, false);
994 			pic2 = GTextureManager.CheckNumForName(VName(TmpName1,
995 				VName::AddLower8), TEXTYPE_Flat, true, false);
996 		}
997 
998 		// different episode ?
999 		if (pic1 == -1 || pic2 == -1)
1000 			continue;
1001 
1002 		memset(&ad, 0, sizeof(ad));
1003 		memset(&fd, 0, sizeof(fd));
1004 
1005 		ad.StartFrameDef = FrameDefs.Num();
1006 		ad.Type = ANIM_Forward;
1007 
1008 		// [RH] Allow for either forward or backward animations.
1009 		if (pic1 < pic2)
1010 		{
1011 			ad.Index = pic1;
1012 			fd.Index = pic2;
1013 		}
1014 		else
1015 		{
1016 			ad.Index = pic2;
1017 			fd.Index = pic1;
1018 			ad.Type = ANIM_Backward;
1019 		}
1020 
1021 		if (fd.Index - ad.Index < 1)
1022 			Sys_Error("P_InitPicAnims: bad cycle from %s to %s", TmpName2,
1023 				TmpName1);
1024 
1025 		fd.BaseTime = BaseTime;
1026 		fd.RandomRange = 0;
1027 		FrameDefs.Append(fd);
1028 
1029 		ad.NumFrames = FrameDefs[ad.StartFrameDef].Index - ad.Index + 1;
1030 		ad.CurrentFrame = ad.NumFrames - 1;
1031 		ad.Time = 0.01; // Force 1st game tic to animate
1032 		AnimDefs.Append(ad);
1033 	}
1034 	delete Strm;
1035 	Strm = NULL;
1036 	unguard;
1037 }
1038 
1039 //==========================================================================
1040 //
1041 //	ParseFTAnim
1042 //
1043 //	Parse flat or texture animation.
1044 //
1045 //==========================================================================
1046 
ParseFTAnim(VScriptParser * sc,int IsFlat)1047 static void ParseFTAnim(VScriptParser* sc, int IsFlat)
1048 {
1049 	guard(ParseFTAnim);
1050 	animDef_t 	ad;
1051 	frameDef_t	fd;
1052 
1053 	memset(&ad, 0, sizeof(ad));
1054 
1055 	//	Optional flag.
1056 	bool optional = false;
1057 	if (sc->Check("optional"))
1058 	{
1059 		optional = true;
1060 	}
1061 
1062 	//	Name
1063 	bool ignore = false;
1064 	sc->ExpectName8();
1065 	ad.Index = GTextureManager.CheckNumForName(sc->Name8,
1066 		IsFlat ? TEXTYPE_Flat : TEXTYPE_Wall, true, true);
1067 	if (ad.Index == -1)
1068 	{
1069 		ignore = true;
1070 		if (!optional)
1071 		{
1072 			GCon->Logf("ANIMDEFS: Can't find %s", *sc->String);
1073 		}
1074 	}
1075 	bool missing = ignore && optional;
1076 
1077 	int CurType = 0;
1078 	ad.StartFrameDef = FrameDefs.Num();
1079 	ad.Type = ANIM_Normal;
1080 	while (1)
1081 	{
1082 		if (sc->Check("allowdecals"))
1083 		{
1084 			//	Since we don't have decals yet, ignore it.
1085 			continue;
1086 		}
1087 
1088 		if (sc->Check("pic"))
1089 		{
1090 			if (CurType == 2)
1091 			{
1092 				sc->Error("You cannot use pic together with range.");
1093 			}
1094 			CurType = 1;
1095 		}
1096 		else if (sc->Check("range"))
1097 		{
1098 			if (CurType == 2)
1099 			{
1100 				sc->Error("You can only use range once in a single animation.");
1101 			}
1102 			if (CurType == 1)
1103 			{
1104 				sc->Error("You cannot use range together with pic.");
1105 			}
1106 			CurType = 2;
1107 			ad.Type = ANIM_Forward;
1108 		}
1109 		else
1110 		{
1111 			break;
1112 		}
1113 
1114 		memset(&fd, 0, sizeof(fd));
1115 		if (sc->CheckNumber())
1116 		{
1117 			fd.Index = ad.Index + sc->Number - 1;
1118 		}
1119 		else
1120 		{
1121 			sc->ExpectName8();
1122 			fd.Index = GTextureManager.CheckNumForName(sc->Name8,
1123 				IsFlat ? TEXTYPE_Flat : TEXTYPE_Wall, true, true);
1124 			if (fd.Index == -1 && !missing)
1125 			{
1126 				sc->Message(va("Unknown texture %s", *sc->String));
1127 			}
1128 		}
1129 		if (sc->Check("tics"))
1130 		{
1131 			sc->ExpectNumber();
1132 			fd.BaseTime = sc->Number;
1133 			fd.RandomRange = 0;
1134 		}
1135 		else if (sc->Check("rand"))
1136 		{
1137 			sc->ExpectNumber();
1138 			fd.BaseTime = sc->Number;
1139 			sc->ExpectNumber();
1140 			fd.RandomRange = sc->Number - fd.BaseTime + 1;
1141 		}
1142 		else
1143 		{
1144 			sc->Error("bad command");
1145 		}
1146 		if (ad.Type != ANIM_Normal)
1147 		{
1148 			if (fd.Index < ad.Index)
1149 			{
1150 				int tmp = ad.Index;
1151 				ad.Index = fd.Index;
1152 				fd.Index = tmp;
1153 				ad.Type = ANIM_Backward;
1154 			}
1155 			if (sc->Check("oscillate"))
1156 			{
1157 				ad.Type = ANIM_OscillateUp;
1158 			}
1159 		}
1160 		if (!ignore)
1161 		{
1162 			FrameDefs.Append(fd);
1163 		}
1164 	}
1165 
1166 	if (!ignore && ad.Type == ANIM_Normal &&
1167 		FrameDefs.Num() - ad.StartFrameDef < 2)
1168 	{
1169 		sc->Error("AnimDef has framecount < 2.");
1170 	}
1171 
1172 	if (!ignore)
1173 	{
1174 		if (ad.Type == ANIM_Normal)
1175 		{
1176 			ad.NumFrames = FrameDefs.Num() - ad.StartFrameDef;
1177 			ad.CurrentFrame = ad.NumFrames - 1;
1178 		}
1179 		else
1180 		{
1181 			ad.NumFrames = FrameDefs[ad.StartFrameDef].Index - ad.Index + 1;
1182 			ad.CurrentFrame = 0;
1183 		}
1184 		ad.Time = 0.01; // Force 1st game tic to animate
1185 		AnimDefs.Append(ad);
1186 	}
1187 	unguard;
1188 }
1189 
1190 //==========================================================================
1191 //
1192 //	AddSwitchDef
1193 //
1194 //==========================================================================
1195 
AddSwitchDef(TSwitch * Switch)1196 static int AddSwitchDef(TSwitch* Switch)
1197 {
1198 	guard(AddSwitchDef);
1199 	for (int i = 0; i < Switches.Num(); i++)
1200 	{
1201 		if (Switches[i]->Tex == Switch->Tex)
1202 		{
1203 			delete Switches[i];
1204 			Switches[i] = NULL;
1205 			Switches[i] = Switch;
1206 			return i;
1207 		}
1208 	}
1209 	return Switches.Append(Switch);
1210 	unguard;
1211 }
1212 
1213 //==========================================================================
1214 //
1215 //	ParseSwitchState
1216 //
1217 //==========================================================================
1218 
ParseSwitchState(VScriptParser * sc,bool IgnoreBad)1219 static TSwitch* ParseSwitchState(VScriptParser* sc, bool IgnoreBad)
1220 {
1221 	guard(ParseSwitchState);
1222 	TArray<TSwitchFrame>	Frames;
1223 	int						Sound = 0;
1224 	bool					Bad = false;
1225 
1226 	while (1)
1227 	{
1228 		if (sc->Check("sound"))
1229 		{
1230 			if (Sound)
1231 			{
1232 				sc->Error("Switch state already has a sound");
1233 			}
1234 			sc->ExpectString();
1235 			Sound = GSoundManager->GetSoundID(*sc->String);
1236 		}
1237 		else if (sc->Check("pic"))
1238 		{
1239 			sc->ExpectName8();
1240 			int Tex = GTextureManager.CheckNumForName(sc->Name8,
1241 				TEXTYPE_Wall, true, false);
1242 			if (Tex < 0 && !IgnoreBad)
1243 			{
1244 				Bad = true;
1245 			}
1246 			TSwitchFrame& F = Frames.Alloc();
1247 			F.Texture = Tex;
1248 			if (sc->Check("tics"))
1249 			{
1250 				sc->ExpectNumber();
1251 				F.BaseTime = sc->Number;
1252 				F.RandomRange = 0;
1253 			}
1254 			else if (sc->Check("range"))
1255 			{
1256 				sc->ExpectNumber();
1257 				int Min = sc->Number;
1258 				sc->ExpectNumber();
1259 				int Max = sc->Number;
1260 				if (Min < Max)
1261 				{
1262 					F.BaseTime = Min;
1263 					F.RandomRange = Max - Min + 1;
1264 				}
1265 				else
1266 				{
1267 					F.BaseTime = Max;
1268 					F.RandomRange = Min - Max + 1;
1269 				}
1270 			}
1271 			else
1272 			{
1273 				sc->Error("Must specify a duration for switch frame");
1274 			}
1275 		}
1276 		else
1277 		{
1278 			break;
1279 		}
1280 	}
1281 
1282 	if (!Frames.Num())
1283 	{
1284 		sc->Error("Switch state needs at least one frame");
1285 	}
1286 	if (Bad)
1287 	{
1288 		return NULL;
1289 	}
1290 
1291 	TSwitch* Def = new TSwitch();
1292 	Def->Sound = Sound;
1293 	Def->NumFrames = Frames.Num();
1294 	Def->Frames = new TSwitchFrame[Frames.Num()];
1295 	for (int i = 0; i < Frames.Num(); i++)
1296 	{
1297 		Def->Frames[i].Texture = Frames[i].Texture;
1298 		Def->Frames[i].BaseTime = Frames[i].BaseTime;
1299 		Def->Frames[i].RandomRange = Frames[i].RandomRange;
1300 	}
1301 	return Def;
1302 	unguard;
1303 }
1304 
1305 //==========================================================================
1306 //
1307 //	ParseSwitchDef
1308 //
1309 //==========================================================================
1310 
ParseSwitchDef(VScriptParser * sc)1311 static void ParseSwitchDef(VScriptParser* sc)
1312 {
1313 	guard(ParseSwitchDef);
1314 	//	Skip game specifier.
1315 	if (sc->Check("doom"))
1316 	{
1317 		sc->ExpectNumber();
1318 	}
1319 	else if (sc->Check("heretic"))
1320 	{
1321 	}
1322 	else if (sc->Check("hexen"))
1323 	{
1324 	}
1325 	else if (sc->Check("strife"))
1326 	{
1327 	}
1328 	else if (sc->Check("any"))
1329 	{
1330 	}
1331 
1332 	//	Switch texture
1333 	sc->ExpectName8();
1334 	int t1 = GTextureManager.CheckNumForName(sc->Name8, TEXTYPE_Wall, true,
1335 		false);
1336 	bool Quest = false;
1337 	TSwitch* Def1 = NULL;
1338 	TSwitch* Def2 = NULL;
1339 
1340 	//	Currently only basic switch definition is supported.
1341 	while (1)
1342 	{
1343 		if (sc->Check("quest"))
1344 		{
1345 			Quest = true;
1346 		}
1347 		else if (sc->Check("on"))
1348 		{
1349 			if (Def1)
1350 			{
1351 				sc->Error("Switch already has an on state");
1352 			}
1353 			Def1 = ParseSwitchState(sc, t1 == -1);
1354 		}
1355 		else if (sc->Check("off"))
1356 		{
1357 			if (Def2)
1358 			{
1359 				sc->Error("Switch already has an off state");
1360 			}
1361 			Def2 = ParseSwitchState(sc, t1 == -1);
1362 		}
1363 		else
1364 		{
1365 			break;
1366 		}
1367 	}
1368 
1369 	if (t1 < 0 || !Def1)
1370 	{
1371 		if (Def1)
1372 		{
1373 			delete Def1;
1374 			Def1 = NULL;
1375 		}
1376 		if (Def2)
1377 		{
1378 			delete Def2;
1379 			Def2 = NULL;
1380 		}
1381 		return;
1382 	}
1383 
1384 	if (!Def2)
1385 	{
1386 		//	If switch has no off state create one that just switches
1387 		// back to base texture.
1388 		Def2 = new TSwitch();
1389 		Def2->Sound = Def1->Sound;
1390 		Def2->NumFrames = 1;
1391 		Def2->Frames = new TSwitchFrame[1];
1392 		Def2->Frames[0].Texture = t1;
1393 		Def2->Frames[0].BaseTime = 0;
1394 		Def2->Frames[0].RandomRange = 0;
1395 	}
1396 
1397 	Def1->Tex = t1;
1398 	Def2->Tex = Def1->Frames[Def1->NumFrames - 1].Texture;
1399 	if (Def1->Tex == Def2->Tex)
1400 	{
1401 		sc->Error("On state must not end on base texture");
1402 	}
1403 	Def1->Quest = Quest;
1404 	Def2->Quest = Quest;
1405 	Def2->PairIndex = AddSwitchDef(Def1);
1406 	Def1->PairIndex = AddSwitchDef(Def2);
1407 	unguard;
1408 }
1409 
1410 //==========================================================================
1411 //
1412 //	ParseAnimatedDoor
1413 //
1414 //==========================================================================
1415 
ParseAnimatedDoor(VScriptParser * sc)1416 static void ParseAnimatedDoor(VScriptParser* sc)
1417 {
1418 	guard(ParseAnimatedDoor);
1419 	//	Get base texture name.
1420 	bool ignore = false;
1421 	sc->ExpectName8();
1422 	vint32 BaseTex = GTextureManager.CheckNumForName(sc->Name8,
1423 		TEXTYPE_Wall, true, true);
1424 	if (BaseTex == -1)
1425 	{
1426 		ignore = true;
1427 		GCon->Logf("ANIMDEFS: Can't find %s", *sc->String);
1428 	}
1429 
1430 	VName OpenSound(NAME_None);
1431 	VName CloseSound(NAME_None);
1432 	TArray<vint32> Frames;
1433 	while (!sc->AtEnd())
1434 	{
1435 		if (sc->Check("opensound"))
1436 		{
1437 			sc->ExpectString();
1438 			OpenSound = *sc->String;
1439 		}
1440 		else if (sc->Check("closesound"))
1441 		{
1442 			sc->ExpectString();
1443 			CloseSound = *sc->String;
1444 		}
1445 		else if (sc->Check("pic"))
1446 		{
1447 			vint32 v;
1448 			if (sc->CheckNumber())
1449 			{
1450 				v = BaseTex + sc->Number - 1;
1451 			}
1452 			else
1453 			{
1454 				sc->ExpectName8();
1455 				v = GTextureManager.CheckNumForName(sc->Name8,
1456 					TEXTYPE_Wall, true, true);
1457 				if (v == -1 && !ignore)
1458 				{
1459 					sc->Message(va("Unknown texture %s", *sc->String));
1460 				}
1461 			}
1462 			Frames.Append(v);
1463 		}
1464 		else
1465 		{
1466 			break;
1467 		}
1468 	}
1469 
1470 	if (!ignore)
1471 	{
1472 		VAnimDoorDef& A = AnimDoorDefs.Alloc();
1473 		A.Texture = BaseTex;
1474 		A.OpenSound = OpenSound;
1475 		A.CloseSound = CloseSound;
1476 		A.NumFrames = Frames.Num();
1477 		A.Frames = new vint32[Frames.Num()];
1478 		for (int i = 0; i < A.NumFrames; i++)
1479 			A.Frames[i] = Frames[i];
1480 	}
1481 	unguard;
1482 };
1483 
1484 //==========================================================================
1485 //
1486 //	ParseWarp
1487 //
1488 //==========================================================================
1489 
ParseWarp(VScriptParser * sc,int Type)1490 static void ParseWarp(VScriptParser* sc, int Type)
1491 {
1492 	guard(ParseWarp);
1493 	int TexType = TEXTYPE_Wall;
1494 	if (sc->Check("texture"))
1495 	{
1496 		TexType = TEXTYPE_Wall;
1497 	}
1498 	else if (sc->Check("flat"))
1499 	{
1500 		TexType = TEXTYPE_Flat;
1501 	}
1502 	else
1503 	{
1504 		sc->Error("Texture type expected");
1505 	}
1506 
1507 	sc->ExpectName8();
1508 	int TexNum = GTextureManager.CheckNumForName(sc->Name8, TexType, true, true);
1509 	if (TexNum < 0)
1510 	{
1511 		return;
1512 	}
1513 
1514 	VTexture* SrcTex = GTextureManager[TexNum];
1515 	VTexture* WarpTex = SrcTex;
1516 	//	Warp only once.
1517 	if (!SrcTex->WarpType)
1518 	{
1519 		if (Type == 1)
1520 			WarpTex = new VWarpTexture(SrcTex);
1521 		else
1522 			WarpTex = new VWarp2Texture(SrcTex);
1523 		GTextureManager.ReplaceTexture(TexNum, WarpTex);
1524 	}
1525 	//	Ignored for now.
1526 	sc->Check("allowdecals");
1527 	unguard;
1528 }
1529 
1530 //==========================================================================
1531 //
1532 //	ParseCameraTexture
1533 //
1534 //==========================================================================
1535 
ParseCameraTexture(VScriptParser * sc)1536 static void ParseCameraTexture(VScriptParser* sc)
1537 {
1538 	guard(ParseCameraTexture);
1539 	//	Name
1540 	sc->ExpectName8();
1541 	VName Name = sc->Name8;
1542 	//	Dimensions
1543 	sc->ExpectNumber();
1544 	int Width = sc->Number;
1545 	sc->ExpectNumber();
1546 	int Height = sc->Number;
1547 	int FitWidth = Width;
1548 	int FitHeight = Height;
1549 
1550 	//	Check for replacing an existing texture
1551 	VCameraTexture* Tex = new VCameraTexture(Name, Width, Height);
1552 	int TexNum = GTextureManager.CheckNumForName(Name, TEXTYPE_Flat, true,
1553 		true);
1554 	if (TexNum != -1)
1555 	{
1556 		//	By default camera texture will fit in old texture.
1557 		VTexture* OldTex = GTextureManager[TexNum];
1558 		FitWidth = OldTex->GetScaledWidth();
1559 		FitHeight = OldTex->GetScaledHeight();
1560 		GTextureManager.ReplaceTexture(TexNum, Tex);
1561 		delete OldTex;
1562 		OldTex = NULL;
1563 	}
1564 	else
1565 	{
1566 		GTextureManager.AddTexture(Tex);
1567 	}
1568 
1569 	//	Optionally specify desired scaled size.
1570 	if (sc->Check("fit"))
1571 	{
1572 		sc->ExpectNumber();
1573 		FitWidth = sc->Number;
1574 		sc->ExpectNumber();
1575 		FitHeight = sc->Number;
1576 	}
1577 	Tex->SScale = (float)Width / (float)FitWidth;
1578 	Tex->TScale = (float)Height / (float)FitHeight;
1579 	unguard;
1580 }
1581 
1582 //==========================================================================
1583 //
1584 //	ParseFTAnims
1585 //
1586 //	Initialise flat and texture animation lists.
1587 //
1588 //==========================================================================
1589 
ParseFTAnims(VScriptParser * sc)1590 static void ParseFTAnims(VScriptParser* sc)
1591 {
1592 	guard(ParseFTAnims);
1593 	while (!sc->AtEnd())
1594 	{
1595 		if (sc->Check("flat"))
1596 		{
1597 			ParseFTAnim(sc, true);
1598 		}
1599 		else if (sc->Check("texture"))
1600 		{
1601 			ParseFTAnim(sc, false);
1602 		}
1603 		else if (sc->Check("switch"))
1604 		{
1605 			ParseSwitchDef(sc);
1606 		}
1607 		else if (sc->Check("animateddoor"))
1608 		{
1609 			ParseAnimatedDoor(sc);
1610 		}
1611 		else if (sc->Check("warp"))
1612 		{
1613 			ParseWarp(sc, 1);
1614 		}
1615 		else if (sc->Check("warp2"))
1616 		{
1617 			ParseWarp(sc, 2);
1618 		}
1619 		else if (sc->Check("cameratexture"))
1620 		{
1621 			ParseCameraTexture(sc);
1622 		}
1623 		else
1624 		{
1625 			sc->Error("bad command");
1626 		}
1627 	}
1628 	delete sc;
1629 	sc = NULL;
1630 	unguard;
1631 }
1632 
1633 //==========================================================================
1634 //
1635 //	InitFTAnims
1636 //
1637 //	Initialise flat and texture animation lists.
1638 //
1639 //==========================================================================
1640 
InitFTAnims()1641 static void InitFTAnims()
1642 {
1643 	guard(InitFTAnims);
1644 	//	Process all animdefs lumps.
1645 	for (int Lump = W_IterateNS(-1, WADNS_Global); Lump >= 0;
1646 		Lump = W_IterateNS(Lump, WADNS_Global))
1647 	{
1648 		if (W_LumpName(Lump) == NAME_animdefs)
1649 		{
1650 			ParseFTAnims(new VScriptParser(*W_LumpName(Lump),
1651 				W_CreateLumpReaderNum(Lump)));
1652 		}
1653 	}
1654 
1655 	//	Optionally parse script file.
1656 	if (fl_devmode && FL_FileExists("scripts/animdefs.txt"))
1657 	{
1658 		ParseFTAnims(new VScriptParser("scripts/animdefs.txt",
1659 			FL_OpenFileRead("scripts/animdefs.txt")));
1660 	}
1661 
1662 	//	Read Boom's animated lump if present.
1663 	P_InitAnimated();
1664 
1665 	FrameDefs.Condense();
1666 	AnimDefs.Condense();
1667 	unguard;
1668 }
1669 
1670 //==========================================================================
1671 //
1672 //	P_InitSwitchList
1673 //
1674 //	Only called at game initialization.
1675 //	Parse BOOM style switches lump.
1676 //
1677 //==========================================================================
1678 
P_InitSwitchList()1679 void P_InitSwitchList()
1680 {
1681 	guard(P_InitSwitchList);
1682 	int lump = W_CheckNumForName(NAME_switches);
1683 	if (lump != -1)
1684 	{
1685 		VStream* Strm = W_CreateLumpReaderNum(lump);
1686 		while (Strm->TotalSize() - Strm->Tell() >= 20)
1687 		{
1688 			char TmpName1[9];
1689 			char TmpName2[9];
1690 			vint16 Episode;
1691 
1692 			//	Read data.
1693 			Strm->Serialise(TmpName1, 9);
1694 			Strm->Serialise(TmpName2, 9);
1695 			*Strm << Episode;
1696 			if (!Episode)
1697 			{
1698 				//	Terminator marker.
1699 				break;
1700 			}
1701 			TmpName1[8] = 0;
1702 			TmpName2[8] = 0;
1703 
1704 			// Check for switches that aren't really switches
1705 			if (!VStr::ICmp(TmpName1, TmpName2))
1706 			{
1707 				GCon->Logf(NAME_Init, "Switch %s in SWITCHES has the same 'on' state", TmpName1);
1708 				continue;
1709 			}
1710 			int t1 = GTextureManager.CheckNumForName(VName(TmpName1,
1711 				VName::AddLower8), TEXTYPE_Wall, true, false);
1712 			int t2 = GTextureManager.CheckNumForName(VName(TmpName2,
1713 				VName::AddLower8), TEXTYPE_Wall, true, false);
1714 			if (t1 < 0 || t2 < 0)
1715 			{
1716 				continue;
1717 			}
1718 			TSwitch* Def1 = new TSwitch();
1719 			TSwitch* Def2 = new TSwitch();
1720 			Def1->Sound = 0;
1721 			Def2->Sound = 0;
1722 			Def1->Tex = t1;
1723 			Def2->Tex = t2;
1724 			Def1->NumFrames = 1;
1725 			Def2->NumFrames = 1;
1726 			Def1->Quest = false;
1727 			Def2->Quest = false;
1728 			Def1->Frames = new TSwitchFrame[1];
1729 			Def2->Frames = new TSwitchFrame[1];
1730 			Def1->Frames[0].Texture = t2;
1731 			Def1->Frames[0].BaseTime = 0;
1732 			Def1->Frames[0].RandomRange = 0;
1733 			Def2->Frames[0].Texture = t1;
1734 			Def2->Frames[0].BaseTime = 0;
1735 			Def2->Frames[0].RandomRange = 0;
1736 			Def2->PairIndex = AddSwitchDef(Def1);
1737 			Def1->PairIndex = AddSwitchDef(Def2);
1738 		}
1739 		delete Strm;
1740 		Strm = NULL;
1741 	}
1742 	Switches.Condense();
1743 	unguard;
1744 }
1745 
1746 //==========================================================================
1747 //
1748 //	R_FindAnimDoor
1749 //
1750 //==========================================================================
1751 
R_FindAnimDoor(vint32 BaseTex)1752 VAnimDoorDef* R_FindAnimDoor(vint32 BaseTex)
1753 {
1754 	guard(R_FindAnimDoor);
1755 	for (int i = 0; i < AnimDoorDefs.Num(); i++)
1756 	{
1757 		if (AnimDoorDefs[i].Texture == BaseTex)
1758 		{
1759 			return &AnimDoorDefs[i];
1760 		}
1761 	}
1762 	return NULL;
1763 	unguard;
1764 }
1765 
1766 //==========================================================================
1767 //
1768 //	R_AnimateSurfaces
1769 //
1770 //==========================================================================
1771 
1772 #ifdef CLIENT
R_AnimateSurfaces()1773 void R_AnimateSurfaces()
1774 {
1775 	guard(R_AnimateSurfaces);
1776 	//	Animate flats and textures
1777 	for (int i = 0; i < AnimDefs.Num(); i++)
1778 	{
1779 		animDef_t& ad = AnimDefs[i];
1780 		ad.Time -= host_frametime;
1781 		if (ad.Time > 0.0)
1782 		{
1783 			continue;
1784 		}
1785 
1786 		switch (ad.Type)
1787 		{
1788 		case ANIM_Normal:
1789 		case ANIM_Forward:
1790 			ad.CurrentFrame = (ad.CurrentFrame + 1) % ad.NumFrames;
1791 			break;
1792 
1793 		case ANIM_Backward:
1794 			if (ad.CurrentFrame == 0)
1795 			{
1796 				ad.CurrentFrame = ad.NumFrames - 1;
1797 			}
1798 			else
1799 			{
1800 				ad.CurrentFrame--;
1801 			}
1802 			break;
1803 
1804 		case ANIM_OscillateUp:
1805 			ad.CurrentFrame++;
1806 			if (ad.CurrentFrame == ad.NumFrames - 1)
1807 			{
1808 				ad.Type = ANIM_OscillateDown;
1809 			}
1810 			break;
1811 
1812 		case ANIM_OscillateDown:
1813 			ad.CurrentFrame--;
1814 			if (ad.CurrentFrame == 0)
1815 			{
1816 				ad.Type = ANIM_OscillateUp;
1817 			}
1818 			break;
1819 		}
1820 		const frameDef_t& fd = FrameDefs[ad.StartFrameDef +
1821 			(ad.Type == ANIM_Normal ? ad.CurrentFrame : 0)];
1822 		ad.Time = fd.BaseTime / 35.0;
1823 		if (fd.RandomRange)
1824 		{
1825 			// Random tics
1826 			ad.Time += Random() * (fd.RandomRange / 35.0);
1827 		}
1828 		if (ad.Type == ANIM_Normal)
1829 		{
1830 			GTextureManager[ad.Index]->TextureTranslation = fd.Index;
1831 		}
1832 		else
1833 		{
1834 			for (int i = 0; i < ad.NumFrames; i++)
1835 			{
1836 				GTextureManager[ad.Index + i]->TextureTranslation =
1837 					ad.Index + (ad.CurrentFrame + i) % ad.NumFrames;
1838 			}
1839 		}
1840 	}
1841 	unguard;
1842 }
1843 #endif
1844 
1845 //==========================================================================
1846 //
1847 //	R_InitTexture
1848 //
1849 //==========================================================================
1850 
R_InitTexture()1851 void R_InitTexture()
1852 {
1853 	guard(R_InitTexture);
1854 	GTextureManager.Init();
1855 	InitFTAnims(); // Init flat and texture animations
1856 	unguard;
1857 }
1858 
1859 //==========================================================================
1860 //
1861 //	R_ShutdownTexture
1862 //
1863 //==========================================================================
1864 
R_ShutdownTexture()1865 void R_ShutdownTexture()
1866 {
1867 	guard(R_ShutdownTexture);
1868 	//	Clean up animation and switch definitions.
1869 	for (int i = 0; i < Switches.Num(); i++)
1870 	{
1871 		delete Switches[i];
1872 		Switches[i] = NULL;
1873 	}
1874 	Switches.Clear();
1875 	AnimDefs.Clear();
1876 	FrameDefs.Clear();
1877 	for (int i = 0; i < AnimDoorDefs.Num(); i++)
1878 	{
1879 		delete[] AnimDoorDefs[i].Frames;
1880 		AnimDoorDefs[i].Frames = NULL;
1881 	}
1882 	AnimDoorDefs.Clear();
1883 
1884 	//	Shut down texture manager.
1885 	GTextureManager.Shutdown();
1886 	unguard;
1887 }
1888