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