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