1 /*
2 ** r_anim.cpp
3 ** Routines for handling texture animation.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2006 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include "wl_def.h"
36 //#include "cmdlib.h"
37 //#include "i_system.h"
38 //#include "r_local.h"
39 //#include "r_sky.h"
40 #include "m_random.h"
41 //#include "d_player.h"
42 //#include "p_spec.h"
43 #include "scanner.h"
44 #include "templates.h"
45 #include "w_wad.h"
46 //#include "g_level.h"
47 #include "textures.h"
48 #include "farchive.h"
49 
50 // MACROS ------------------------------------------------------------------
51 
52 // TYPES -------------------------------------------------------------------
53 
54 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
55 
56 // PUBLIC DATA DEFINITIONS -------------------------------------------------
57 
58 // PRIVATE DATA DEFINITIONS ------------------------------------------------
59 
60 static FRandom pr_animatepictures ("AnimatePics");
61 
62 // CODE --------------------------------------------------------------------
63 
64 //==========================================================================
65 //
66 // FTextureManager :: AddAnim
67 //
68 // Adds a new animation to the array. If one with the same basepic as the
69 // new one already exists, it is replaced.
70 //
71 //==========================================================================
72 
AddAnim(FAnimDef * anim)73 void FTextureManager::AddAnim (FAnimDef *anim)
74 {
75 	// Search for existing duplicate.
76 	for (unsigned int i = 0; i < mAnimations.Size(); ++i)
77 	{
78 		if (mAnimations[i]->BasePic == anim->BasePic)
79 		{
80 			// Found one!
81 			free (mAnimations[i]);
82 			mAnimations[i] = anim;
83 			return;
84 		}
85 	}
86 	// Didn't find one, so add it at the end.
87 	mAnimations.Push (anim);
88 }
89 
90 //==========================================================================
91 //
92 // FTextureManager :: AddSimpleAnim
93 //
94 // Creates an animation with simple characteristics. This is used for
95 // original Doom (non-ANIMDEFS-style) animations and Build animations.
96 //
97 //==========================================================================
98 
AddSimpleAnim(FTextureID picnum,int animcount,int animtype,DWORD speedmin,DWORD speedrange)99 void FTextureManager::AddSimpleAnim (FTextureID picnum, int animcount, int animtype, DWORD speedmin, DWORD speedrange)
100 {
101 	if (AreTexturesCompatible(picnum, picnum + (animcount - 1)))
102 	{
103 		FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef));
104 		anim->CurFrame = 0;
105 		anim->BasePic = picnum;
106 		anim->NumFrames = animcount;
107 		anim->AnimType = animtype;
108 		anim->SwitchTime = 0;
109 		anim->Frames[0].SpeedMin = speedmin;
110 		anim->Frames[0].SpeedRange = speedrange;
111 		anim->Frames[0].FramePic = anim->BasePic;
112 		AddAnim (anim);
113 	}
114 }
115 
116 //==========================================================================
117 //
118 // FTextureManager :: AddComplexAnim
119 //
120 // Creates an animation with individually defined frames.
121 //
122 //==========================================================================
123 
AddComplexAnim(FTextureID picnum,const TArray<FAnimDef::FAnimFrame> & frames)124 void FTextureManager::AddComplexAnim (FTextureID picnum, const TArray<FAnimDef::FAnimFrame> &frames)
125 {
126 	FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef) + (frames.Size()-1) * sizeof(frames[0]));
127 	anim->BasePic = picnum;
128 	anim->NumFrames = frames.Size();
129 	anim->CurFrame = 0;
130 	anim->AnimType = FAnimDef::ANIM_DiscreteFrames;
131 	anim->SwitchTime = 0;
132 	memcpy (&anim->Frames[0], &frames[0], frames.Size() * sizeof(frames[0]));
133 	AddAnim (anim);
134 }
135 
136 // ANIMATED comaptibility has no business in Wolf3D
137 #if 0
138 //==========================================================================
139 //
140 // FTextureManager :: Initanimated
141 //
142 // [description copied from BOOM]
143 // Load the table of animation definitions, checking for existence of
144 // the start and end of each frame. If the start doesn't exist the sequence
145 // is skipped, if the last doesn't exist, BOOM exits.
146 //
147 // Wall/Flat animation sequences, defined by name of first and last frame,
148 // The full animation sequence is given using all lumps between the start
149 // and end entry, in the order found in the WAD file.
150 //
151 // This routine modified to read its data from a predefined lump or
152 // PWAD lump called ANIMATED rather than a static table in this module to
153 // allow wad designers to insert or modify animation sequences.
154 //
155 // Lump format is an array of byte packed animdef_t structures, terminated
156 // by a structure with istexture == -1. The lump can be generated from a
157 // text source file using SWANTBLS.EXE, distributed with the BOOM utils.
158 // The standard list of switches and animations is contained in the example
159 // source text file DEFSWANI.DAT also in the BOOM util distribution.
160 //
161 // [RH] Rewritten to support BOOM ANIMATED lump but also make absolutely
162 //		no assumptions about how the compiler packs the animdefs array.
163 //
164 //==========================================================================
165 
166 CVAR(Bool, debuganimated, false, 0)
167 
168 void FTextureManager::InitAnimated (void)
169 {
170 	const BITFIELD texflags = TEXMAN_Overridable;
171 		// I think better not! This is only for old ANIMATED definition that
172 		// don't know about ZDoom's more flexible texture system.
173 		// | FTextureManager::TEXMAN_TryAny;
174 
175 	if (Wads.CheckNumForName ("ANIMATED") != -1)
176 	{
177 		FMemLump animatedlump = Wads.ReadLump ("ANIMATED");
178 		const char *animdefs = (const char *)animatedlump.GetMem();
179 		const char *anim_p;
180 		FTextureID pic1, pic2;
181 		int animtype;
182 		DWORD animspeed;
183 
184 		// Init animation
185 		animtype = FAnimDef::ANIM_Forward;
186 
187 		for (anim_p = animdefs; *anim_p != -1; anim_p += 23)
188 		{
189 			if (*anim_p /* .istexture */ & 1)
190 			{
191 				// different episode ?
192 				if (!(pic1 = CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Wall, texflags)).Exists() ||
193 					!(pic2 = CheckForTexture (anim_p + 1 /* .endname */, FTexture::TEX_Wall, texflags)).Exists())
194 					continue;
195 
196 				// [RH] Bit 1 set means allow decals on walls with this texture
197 				Texture(pic2)->bNoDecals = Texture(pic1)->bNoDecals = !(*anim_p & 2);
198 			}
199 			else
200 			{
201 				if (!(pic1 = CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Flat, texflags)).Exists() ||
202 					!(pic2 = CheckForTexture (anim_p + 1 /* .startname */, FTexture::TEX_Flat, texflags)).Exists())
203 					continue;
204 			}
205 
206 			FTexture *tex1 = Texture(pic1);
207 			FTexture *tex2 = Texture(pic2);
208 
209 			animspeed = (BYTE(anim_p[19]) << 0)  | (BYTE(anim_p[20]) << 8) |
210 						(BYTE(anim_p[21]) << 16) | (BYTE(anim_p[22]) << 24);
211 
212 			// SMMU-style swirly hack? Don't apply on already-warping texture
213 			if (animspeed > 65535 && tex1 != NULL && !tex1->bWarped)
214 			{
215 				FTexture *warper = new FWarp2Texture (tex1);
216 				ReplaceTexture (pic1, warper, false);
217 			}
218 
219 			// These tests were not really relevant for swirling textures, or even potentially
220 			// harmful, so they have been moved to the else block.
221 			else
222 			{
223 				if (tex1->UseType != tex2->UseType)
224 				{
225 					// not the same type -
226 					continue;
227 				}
228 
229 				if (debuganimated)
230 				{
231 					Printf("Defining animation '%s' (texture %d, lump %d, file %d) to '%s' (texture %d, lump %d, file %d)\n",
232 						tex1->Name, pic1.GetIndex(), tex1->GetSourceLump(), Wads.GetLumpFile(tex1->GetSourceLump()),
233 						tex2->Name, pic2.GetIndex(), tex2->GetSourceLump(), Wads.GetLumpFile(tex2->GetSourceLump()));
234 				}
235 
236 				if (pic1 == pic2)
237 				{
238 					// This animation only has one frame. Skip it. (Doom aborted instead.)
239 					Printf ("Animation %s in ANIMATED has only one frame\n", anim_p + 10);
240 					continue;
241 				}
242 				// [RH] Allow for backward animations as well as forward.
243 				else if (pic1 > pic2)
244 				{
245 					swapvalues (pic1, pic2);
246 					animtype = FAnimDef::ANIM_Backward;
247 				}
248 
249 				// Speed is stored as tics, but we want ms so scale accordingly.
250 				AddSimpleAnim (pic1, pic2 - pic1 + 1, animtype, Scale (animspeed, 1000, 35));
251 			}
252 		}
253 	}
254 }
255 #endif
256 
257 //==========================================================================
258 //
259 // FTextureManager :: InitAnimDefs
260 //
261 // This uses a Hexen ANIMDEFS lump to define the animation sequences
262 //
263 //==========================================================================
264 
InitAnimDefs()265 void FTextureManager::InitAnimDefs ()
266 {
267 	const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny;
268 	int lump, lastlump = 0;
269 
270 	while ((lump = Wads.FindLump ("ANIMDEFS", &lastlump)) != -1)
271 	{
272 		FMemLump lmp = Wads.ReadLump(lump);
273 		Scanner sc((const char*)lmp.GetMem(), lmp.GetSize());
274 		sc.SetScriptIdentifier(Wads.GetLumpFullName(lump));
275 
276 		while (sc.GetNextString ())
277 		{
278 			if (sc->str.Compare ("flat") == 0)
279 			{
280 				ParseAnim (sc, FTexture::TEX_Flat);
281 			}
282 			else if (sc->str.Compare ("texture") == 0)
283 			{
284 				ParseAnim (sc, FTexture::TEX_Wall);
285 			}
286 			else if (sc->str.Compare ("switch") == 0)
287 			{
288 				ProcessSwitchDef (sc);
289 			}
290 			// [GRB] Added warping type 2
291 			else if (sc->str.Compare ("warp") == 0 || sc->str.Compare ("warp2") == 0)
292 			{
293 				ParseWarp(sc);
294 			}
295 			else if (sc->str.Compare ("cameratexture") == 0)
296 			{
297 				ParseCameraTexture(sc);
298 			}
299 			else if (sc->str.Compare ("animatedDoor") == 0)
300 			{
301 				ParseAnimatedDoor (sc);
302 			}
303 			else if (sc->str.Compare("skyoffset") == 0)
304 			{
305 				if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
306 				FTextureID picnum = CheckForTexture (sc->str, FTexture::TEX_Wall, texflags);
307 				sc.MustGetToken(TK_IntConst);
308 				if (picnum.Exists())
309 				{
310 					Texture(picnum)->SkyOffset = sc->number;
311 				}
312 			}
313 			else
314 			{
315 				sc.ScriptMessage (Scanner::ERROR, "");
316 			}
317 		}
318 	}
319 }
320 
321 //==========================================================================
322 //
323 // FTextureManager :: ParseAnim
324 //
325 // Parse a single animation definition out of an ANIMDEFS lump and
326 // create the corresponding animation structure.
327 //
328 //==========================================================================
329 
ParseAnim(Scanner & sc,int usetype)330 void FTextureManager::ParseAnim (Scanner &sc, int usetype)
331 {
332 	const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny;
333 	TArray<FAnimDef::FAnimFrame> frames (32);
334 	FTextureID picnum;
335 	int defined = 0;
336 	bool optional = false, missing = false;
337 
338 	if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
339 	if (sc->str.Compare ("optional") == 0)
340 	{
341 		optional = true;
342 		if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
343 	}
344 	picnum = CheckForTexture (sc->str, usetype, texflags);
345 
346 	if (!picnum.Exists())
347 	{
348 		if (optional)
349 		{
350 			missing = true;
351 		}
352 		else
353 		{
354 			//Printf (PRINT_BOLD, "ANIMDEFS: Can't find %s\n", sc->str);
355 			Printf ("ANIMDEFS: Can't find %s\n", sc->str.GetChars());
356 		}
357 	}
358 
359 	// no decals on animating textures, by default
360 	if (picnum.isValid())
361 	{
362 		Texture(picnum)->bNoDecals = true;
363 	}
364 
365 	while (sc.GetNextString ())
366 	{
367 		if (sc->str.Compare ("allowdecals") == 0)
368 		{
369 			if (picnum.isValid())
370 			{
371 				Texture(picnum)->bNoDecals = false;
372 			}
373 			continue;
374 		}
375 		else if (sc->str.Compare ("range") == 0)
376 		{
377 			if (defined == 2)
378 			{
379 				sc.ScriptMessage (Scanner::ERROR, "You cannot use \"pic\" and \"range\" together in a single animation.");
380 			}
381 			if (defined == 1)
382 			{
383 				sc.ScriptMessage (Scanner::ERROR, "You can only use one \"range\" per animation.");
384 			}
385 			defined = 1;
386 			ParseRangeAnim (sc, picnum, usetype, missing);
387 		}
388 		else if (sc->str.Compare ("pic") == 0)
389 		{
390 			if (defined == 1)
391 			{
392 				sc.ScriptMessage (Scanner::ERROR, "You cannot use \"pic\" and \"range\" together in a single animation.");
393 			}
394 			defined = 2;
395 			ParsePicAnim (sc, picnum, usetype, missing, frames);
396 		}
397 		else
398 		{
399 			sc.Rewind ();
400 			break;
401 		}
402 	}
403 
404 	// If base pic is not present, don't add this anim
405 	// ParseRangeAnim adds the anim itself, but ParsePicAnim does not.
406 	if (picnum.isValid() && defined == 2)
407 	{
408 		if (frames.Size() < 2)
409 		{
410 			sc.ScriptMessage (Scanner::ERROR, "Animation needs at least 2 frames");
411 		}
412 		AddComplexAnim (picnum, frames);
413 	}
414 }
415 
416 //==========================================================================
417 //
418 // FTextureManager :: ParseRangeAnim
419 //
420 // Parse an animation defined using "range". Not that one range entry is
421 // enough to define a complete animation, unlike "pic".
422 //
423 //==========================================================================
424 
ParseRangeAnim(Scanner & sc,FTextureID picnum,int usetype,bool missing)425 void FTextureManager::ParseRangeAnim (Scanner &sc, FTextureID picnum, int usetype, bool missing)
426 {
427 	int type;
428 	FTextureID framenum;
429 	DWORD min, max;
430 
431 	type = FAnimDef::ANIM_Forward;
432 	framenum = ParseFramenum (sc, picnum, usetype, missing);
433 	ParseTime (sc, min, max);
434 
435 	if (framenum == picnum || !picnum.Exists())
436 	{
437 		return;		// Animation is only one frame or does not exist
438 	}
439 	if (framenum < picnum)
440 	{
441 		type = FAnimDef::ANIM_Backward;
442 		Texture(framenum)->bNoDecals = Texture(picnum)->bNoDecals;
443 		swapvalues (framenum, picnum);
444 	}
445 	if (sc.GetNextString())
446 	{
447 		if (sc->str.Compare ("Oscillate") == 0)
448 		{
449 			type = type == FAnimDef::ANIM_Forward ? FAnimDef::ANIM_OscillateUp : FAnimDef::ANIM_OscillateDown;
450 		}
451 		else
452 		{
453 			sc.Rewind ();
454 		}
455 	}
456 	AddSimpleAnim (picnum, framenum - picnum + 1, type, min, max - min);
457 }
458 
459 //==========================================================================
460 //
461 // FTextureManager :: ParsePicAnim
462 //
463 // Parse a single frame from ANIMDEFS defined using "pic".
464 //
465 //==========================================================================
466 
ParsePicAnim(Scanner & sc,FTextureID picnum,int usetype,bool missing,TArray<FAnimDef::FAnimFrame> & frames)467 void FTextureManager::ParsePicAnim (Scanner &sc, FTextureID picnum, int usetype, bool missing, TArray<FAnimDef::FAnimFrame> &frames)
468 {
469 	FTextureID framenum;
470 	DWORD min, max;
471 
472 	framenum = ParseFramenum (sc, picnum, usetype, missing);
473 	ParseTime (sc, min, max);
474 
475 	if (picnum.isValid())
476 	{
477 		FAnimDef::FAnimFrame frame;
478 
479 		frame.SpeedMin = min;
480 		frame.SpeedRange = max - min;
481 		frame.FramePic = framenum;
482 		frames.Push (frame);
483 	}
484 }
485 
486 //==========================================================================
487 //
488 // FTextureManager :: ParseFramenum
489 //
490 // Reads a frame's texture from ANIMDEFS. It can either be an integral
491 // offset from basepicnum or a specific texture name.
492 //
493 //==========================================================================
494 
ParseFramenum(Scanner & sc,FTextureID basepicnum,int usetype,bool allowMissing)495 FTextureID FTextureManager::ParseFramenum (Scanner &sc, FTextureID basepicnum, int usetype, bool allowMissing)
496 {
497 	const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny;
498 	FTextureID framenum;
499 
500 	if (sc.CheckToken(TK_IntConst))
501 	{
502 		framenum = basepicnum + (atoi(sc->str) - 1);
503 	}
504 	else
505 	{
506 		if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
507 		framenum = CheckForTexture (sc->str, usetype, texflags);
508 		if (!framenum.Exists() && !allowMissing)
509 		{
510 			sc.ScriptMessage (Scanner::ERROR, "Unknown texture %s", sc->str.GetChars());
511 		}
512 	}
513 	return framenum;
514 }
515 
516 //==========================================================================
517 //
518 // FTextureManager :: ParseTime
519 //
520 // Reads a tics or rand time definition from ANIMDEFS.
521 //
522 //==========================================================================
523 
ParseTime(Scanner & sc,DWORD & min,DWORD & max)524 void FTextureManager::ParseTime (Scanner &sc, DWORD &min, DWORD &max)
525 {
526 	if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
527 	if (sc->str.Compare ("tics") == 0)
528 	{
529 		sc.MustGetToken (TK_FloatConst);
530 		min = max = DWORD(sc->decimal * 1000 / 35);
531 	}
532 	else if (sc->str.Compare ("rand") == 0)
533 	{
534 		sc.MustGetToken (TK_FloatConst);
535 		min = DWORD(sc->decimal * 1000 / 35);
536 		sc.MustGetToken (TK_FloatConst);
537 		max = DWORD(sc->decimal * 1000 / 35);
538 	}
539 	else
540 	{
541 		min = max = 1;
542 		sc.ScriptMessage (Scanner::ERROR, "Must specify a duration for animation frame");
543 	}
544 }
545 
546 //==========================================================================
547 //
548 // FTextureManager :: ParseWarp
549 //
550 // Parses a warping texture definition
551 //
552 //==========================================================================
553 
ParseWarp(Scanner & sc)554 void FTextureManager::ParseWarp(Scanner &sc)
555 {
556 	const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny;
557 	bool isflat = false;
558 	bool type2 = sc->str.Compare ("warp2") == 0;	// [GRB]
559 	if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
560 	if (sc->str.Compare ("flat") == 0)
561 	{
562 		isflat = true;
563 		if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
564 	}
565 	else if (sc->str.Compare ("texture") == 0)
566 	{
567 		isflat = false;
568 		if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
569 	}
570 	else
571 	{
572 		sc.ScriptMessage (Scanner::ERROR, NULL);
573 	}
574 	FTextureID picnum = CheckForTexture (sc->str, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags);
575 	if (picnum.isValid())
576 	{
577 		FTexture *warper = Texture(picnum);
578 
579 		// don't warp a texture more than once
580 		if (!warper->bWarped)
581 		{
582 			if (type2) warper = new FWarp2Texture (warper);
583 			else warper = new FWarpTexture (warper);
584 
585 			ReplaceTexture (picnum, warper, false);
586 		}
587 
588 		if (sc.CheckToken(TK_FloatConst))
589 		{
590 			static_cast<FWarpTexture*>(warper)->SetSpeed(float(sc->decimal));
591 		}
592 
593 		// No decals on warping textures, by default.
594 		// Warping information is taken from the last warp
595 		// definition for this texture.
596 		warper->bNoDecals = true;
597 		if (sc.GetNextString ())
598 		{
599 			if (sc->str.Compare ("allowdecals") == 0)
600 			{
601 				warper->bNoDecals = false;
602 			}
603 			else
604 			{
605 				sc.Rewind ();
606 			}
607 		}
608 	}
609 }
610 
611 //==========================================================================
612 //
613 // ParseCameraTexture
614 //
615 // Parses a camera texture definition
616 //
617 //==========================================================================
618 
ParseCameraTexture(Scanner & sc)619 void FTextureManager::ParseCameraTexture(Scanner &sc)
620 {
621 	sc.ScriptMessage(Scanner::ERROR, "Not ready yet to do cameras!");
622 #if 0
623 	const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny;
624 	int width, height;
625 	int fitwidth, fitheight;
626 	FString picname;
627 
628 	if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
629 	picname = sc->str;
630 	sc.MustGetToken(TK_IntConst);
631 	width = sc->number;
632 	sc.MustGetToken(TK_IntConst);
633 	height = sc->number;
634 	FTextureID picnum = CheckForTexture (picname, FTexture::TEX_Flat, texflags);
635 	FTexture *viewer = new FCanvasTexture (picname, width, height);
636 	if (picnum.Exists())
637 	{
638 		FTexture *oldtex = Texture(picnum);
639 		fitwidth = oldtex->GetScaledWidth ();
640 		fitheight = oldtex->GetScaledHeight ();
641 		viewer->UseType = oldtex->UseType;
642 		ReplaceTexture (picnum, viewer, true);
643 	}
644 	else
645 	{
646 		fitwidth = width;
647 		fitheight = height;
648 		// [GRB] No need for oldtex
649 		viewer->UseType = FTexture::TEX_Wall;
650 		AddTexture (viewer);
651 	}
652 	if (sc.GetNextString())
653 	{
654 		if (sc->str.Compare ("fit") == 0)
655 		{
656 			sc.MustGetToken(TK_IntConst);
657 			fitwidth = sc->number;
658 			sc.MustGetToken(TK_IntConst);
659 			fitheight = sc->number;
660 		}
661 		else
662 		{
663 			sc.Rewind ();
664 		}
665 	}
666 	viewer->SetScaledSize(fitwidth, fitheight);
667 #endif
668 }
669 
670 //==========================================================================
671 //
672 // FTextureManager :: FixAnimations
673 //
674 // Copy the "front sky" flag from an animated texture to the rest
675 // of the textures in the animation, and make every texture in an
676 // animation range use the same setting for bNoDecals.
677 //
678 //==========================================================================
679 
FixAnimations()680 void FTextureManager::FixAnimations ()
681 {
682 	unsigned int i;
683 	int j;
684 
685 	for (i = 0; i < mAnimations.Size(); ++i)
686 	{
687 		FAnimDef *anim = mAnimations[i];
688 		if (anim->AnimType == FAnimDef::ANIM_DiscreteFrames)
689 		{
690 			if (Texture(anim->BasePic)->bNoRemap0)
691 			{
692 				for (j = 0; j < anim->NumFrames; ++j)
693 				{
694 					Texture(anim->Frames[j].FramePic)->SetFrontSkyLayer ();
695 				}
696 			}
697 		}
698 		else
699 		{
700 			bool nodecals;
701 			bool noremap = false;
702 			const char *name;
703 
704 			name = Texture(anim->BasePic)->Name;
705 			nodecals = Texture(anim->BasePic)->bNoDecals;
706 			for (j = 0; j < anim->NumFrames; ++j)
707 			{
708 				FTexture *tex = Texture(anim->BasePic + j);
709 				noremap |= tex->bNoRemap0;
710 				tex->bNoDecals = nodecals;
711 			}
712 			if (noremap)
713 			{
714 				for (j = 0; j < anim->NumFrames; ++j)
715 				{
716 					Texture(anim->BasePic + j)->SetFrontSkyLayer ();
717 				}
718 			}
719 		}
720 	}
721 }
722 
723 //==========================================================================
724 //
725 // ParseAnimatedDoor
726 //
727 // Parses an animated door definition
728 //
729 //==========================================================================
730 
ParseAnimatedDoor(Scanner & sc)731 void FTextureManager::ParseAnimatedDoor(Scanner &sc)
732 {
733 	const BITFIELD texflags = TEXMAN_Overridable | TEXMAN_TryAny;
734 	FDoorAnimation anim;
735 	TArray<FTextureID> frames;
736 	bool error = false;
737 	FTextureID v;
738 
739 	if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
740 	anim.BaseTexture = CheckForTexture (sc->str, FTexture::TEX_Wall, texflags);
741 
742 	if (!anim.BaseTexture.Exists())
743 	{
744 		error = true;
745 	}
746 
747 	while (sc.GetNextString ())
748 	{
749 		if (sc->str.Compare ("opensound") == 0)
750 		{
751 			if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
752 			anim.OpenSound = sc->str;
753 		}
754 		else if (sc->str.Compare ("closesound") == 0)
755 		{
756 			if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
757 			anim.CloseSound = sc->str;
758 		}
759 		else if (sc->str.Compare ("pic") == 0)
760 		{
761 			if (sc.CheckToken(TK_IntConst))
762 			{
763 				v = anim.BaseTexture + (atoi(sc->str) - 1);
764 			}
765 			else
766 			{
767 				if(!sc.GetNextString()) sc.ScriptMessage(Scanner::ERROR, "Expected string.");
768 				v = CheckForTexture (sc->str, FTexture::TEX_Wall, texflags);
769 				if (!v.Exists() && anim.BaseTexture.Exists() && !error)
770 				{
771 					sc.ScriptMessage (Scanner::ERROR, "Unknown texture %s", sc->str.GetChars());
772 				}
773 				frames.Push (v);
774 			}
775 		}
776 		else
777 		{
778 			sc.Rewind ();
779 			break;
780 		}
781 	}
782 	if (!error)
783 	{
784 		anim.TextureFrames = new FTextureID[frames.Size()];
785 		memcpy (anim.TextureFrames, &frames[0], sizeof(FTextureID) * frames.Size());
786 		anim.NumTextureFrames = frames.Size();
787 		mAnimatedDoors.Push (anim);
788 	}
789 }
790 
791 //==========================================================================
792 //
793 // Return index into "DoorAnimations" array for which door type to use
794 //
795 //==========================================================================
796 
FindAnimatedDoor(FTextureID picnum)797 FDoorAnimation *FTextureManager::FindAnimatedDoor (FTextureID picnum)
798 {
799 	unsigned int i;
800 
801 	for (i = 0; i < mAnimatedDoors.Size(); ++i)
802 	{
803 		if (picnum == mAnimatedDoors[i].BaseTexture)
804 			return &mAnimatedDoors[i];
805 	}
806 
807 	return NULL;
808 }
809 
810 //==========================================================================
811 //
812 // FAnimDef :: SetSwitchTime
813 //
814 // Determines when to switch to the next frame.
815 //
816 //==========================================================================
817 
SetSwitchTime(DWORD mstime)818 void FAnimDef::SetSwitchTime (DWORD mstime)
819 {
820 	int speedframe = (AnimType == FAnimDef::ANIM_DiscreteFrames) ? CurFrame : 0;
821 
822 	SwitchTime = mstime + Frames[speedframe].SpeedMin;
823 	if (Frames[speedframe].SpeedRange != 0)
824 	{
825 		SwitchTime += pr_animatepictures(Frames[speedframe].SpeedRange);
826 	}
827 }
828 
829 
830 //==========================================================================
831 //
832 // FTextureManager :: SetTranslation
833 //
834 // Sets animation translation for a texture
835 //
836 //==========================================================================
837 
SetTranslation(FTextureID fromtexnum,FTextureID totexnum)838 void FTextureManager::SetTranslation (FTextureID fromtexnum, FTextureID totexnum)
839 {
840 	if ((size_t)fromtexnum.texnum < Translation.Size())
841 	{
842 		if ((size_t)totexnum.texnum >= Textures.Size())
843 		{
844 			totexnum.texnum = fromtexnum.texnum;
845 		}
846 		Translation[fromtexnum.texnum] = totexnum.texnum;
847 	}
848 }
849 
850 
851 //==========================================================================
852 //
853 // FTextureManager :: UpdateAnimations
854 //
855 // Updates texture translations for each animation and scrolls the skies.
856 //
857 //==========================================================================
858 
UpdateAnimations(DWORD mstime)859 void FTextureManager::UpdateAnimations (DWORD mstime)
860 {
861 	for (unsigned int j = 0; j < mAnimations.Size(); ++j)
862 	{
863 		FAnimDef *anim = mAnimations[j];
864 
865 		// If this is the first time through R_UpdateAnimations, just
866 		// initialize the anim's switch time without actually animating.
867 		if (anim->SwitchTime == 0)
868 		{
869 			anim->SetSwitchTime (mstime);
870 		}
871 		else while (anim->SwitchTime <= mstime)
872 		{ // Multiple frames may have passed since the last time calling
873 		  // R_UpdateAnimations, so be sure to loop through them all.
874 
875 			switch (anim->AnimType)
876 			{
877 			default:
878 			case FAnimDef::ANIM_Forward:
879 			case FAnimDef::ANIM_DiscreteFrames:
880 				anim->CurFrame = (anim->CurFrame + 1) % anim->NumFrames;
881 				break;
882 
883 			case FAnimDef::ANIM_Backward:
884 				if (anim->CurFrame == 0)
885 				{
886 					anim->CurFrame = anim->NumFrames - 1;
887 				}
888 				else
889 				{
890 					anim->CurFrame -= 1;
891 				}
892 				break;
893 
894 			case FAnimDef::ANIM_OscillateUp:
895 				anim->CurFrame = anim->CurFrame + 1;
896 				if (anim->CurFrame >= anim->NumFrames - 1)
897 				{
898 					anim->AnimType = FAnimDef::ANIM_OscillateDown;
899 				}
900 				break;
901 
902 			case FAnimDef::ANIM_OscillateDown:
903 				anim->CurFrame = anim->CurFrame - 1;
904 				if (anim->CurFrame == 0)
905 				{
906 					anim->AnimType = FAnimDef::ANIM_OscillateUp;
907 				}
908 				break;
909 			}
910 			anim->SetSwitchTime (mstime);
911 		}
912 
913 		if (anim->AnimType == FAnimDef::ANIM_DiscreteFrames)
914 		{
915 			SetTranslation (anim->BasePic, anim->Frames[anim->CurFrame].FramePic);
916 		}
917 		else
918 		{
919 			for (unsigned int i = 0; i < anim->NumFrames; i++)
920 			{
921 				SetTranslation (anim->BasePic + i, anim->BasePic + (i + anim->CurFrame) % anim->NumFrames);
922 			}
923 		}
924 	}
925 }
926 
927 //==========================================================================
928 //
929 // operator<<
930 //
931 //==========================================================================
932 
operator <<(FArchive & arc,FDoorAnimation * & Doorani)933 template<> FArchive &operator<< (FArchive &arc, FDoorAnimation* &Doorani)
934 {
935 	if (arc.IsStoring())
936 	{
937 		arc << Doorani->BaseTexture;
938 	}
939 	else
940 	{
941 		FTextureID tex;
942 		arc << tex;
943 		Doorani = TexMan.FindAnimatedDoor(tex);
944 	}
945 	return arc;
946 }
947 
948