diff --git a/src/c_cmds.cpp b/src/c_cmds.cpp index 6194f59193..79f9aed7d5 100644 --- a/src/c_cmds.cpp +++ b/src/c_cmds.cpp @@ -785,5 +785,4 @@ CCMD(changesky) sky1texture = TexMan.GetTexture (sky1name, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable); } R_InitSkyMap (); - sky1pos = sky2pos = 0; } diff --git a/src/g_level.cpp b/src/g_level.cpp index fcd0cd866e..3182e0f111 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -865,12 +865,11 @@ static void ParseMapInfoLower (MapInfoHandler *handlers, SC_MustGetFloat (); // get scroll speed if (HexenHack) { - *((fixed_t *)(info + handler->data2)) = sc_Number << 8; - } - else - { - *((fixed_t *)(info + handler->data2)) = (fixed_t)(sc_Float * 65536.0f); + sc_Float /= 256; } + // Sky scroll speed is specified as pixels per tic, but we + // want pixels per millisecond. + *((float *)(info + handler->data2)) = sc_Float * 35 / 1000; break; case MITYPE_SETFLAG: @@ -1774,7 +1773,6 @@ void G_DoLoadLevel (int position, bool autosave) // [RH] Fetch sky parameters from level_locals_t. sky1texture = TexMan.GetTexture (level.skypic1, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable); sky2texture = TexMan.GetTexture (level.skypic2, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable); - sky1pos = sky2pos = 0; // [RH] Set up details about sky rendering R_InitSkyMap (); @@ -2420,7 +2418,6 @@ void G_SerializeLevel (FArchive &arc, bool hubLoad) strncpy (level.skypic2, arc.ReadName(), 8); sky1texture = TexMan.GetTexture (level.skypic1, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable); sky2texture = TexMan.GetTexture (level.skypic2, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable); - sky1pos = sky2pos = 0; R_InitSkyMap (); } diff --git a/src/g_level.h b/src/g_level.h index 0356cd0494..26bf5db7a7 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -137,8 +137,8 @@ struct level_info_s DWORD snapshotVer; struct acsdefered_s *defered; char skypic2[9]; - fixed_t skyspeed1; - fixed_t skyspeed2; + float skyspeed1; + float skyspeed2; DWORD fadeto; DWORD outsidefog; int cdtrack; @@ -203,8 +203,8 @@ struct level_locals_s char skypic1[9]; char skypic2[9]; - fixed_t skyspeed1; // Scrolling speed of first sky texture - fixed_t skyspeed2; + float skyspeed1; // Scrolling speed of sky textures, in pixels per ms + float skyspeed2; int total_secrets; int found_secrets; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 5aaec2f382..a71b13b1c5 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -3403,7 +3403,7 @@ void P_Init () atterm (P_Shutdown); P_InitEffects (); // [RH] - P_InitPicAnims (); + R_InitPicAnims (); P_InitSwitchList (); P_InitTerrainTypes (); P_InitKeyMessages (); diff --git a/src/p_spec.cpp b/src/p_spec.cpp index 4fe8cfb24f..a580056cb7 100644 --- a/src/p_spec.cpp +++ b/src/p_spec.cpp @@ -68,7 +68,6 @@ #include "r_sky.h" static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); -static FRandom pr_animatepictures ("AnimatePics"); // [GrafZahl] Make this message changable by the user! ;) CVAR(String, secretmessage, "A Secret is revealed!", CVAR_ARCHIVE) @@ -113,563 +112,11 @@ void DPusher::Serialize (FArchive &arc) << m_Affectee; } -// -// Animating textures and planes -// -// [RH] Expanded to work with a Hexen ANIMDEFS lump -// - -struct FAnimDef -{ - WORD BasePic; - WORD NumFrames; - WORD CurFrame; - BYTE bUniqueFrames:1; - BYTE AnimType:2; - BYTE Countdown; - struct FAnimFrame - { - BYTE SpeedMin; - BYTE SpeedRange; - WORD FramePic; - } Frames[1]; - enum - { - ANIM_Forward, - ANIM_Backward, - ANIM_OscillateUp, - ANIM_OscillateDown, - }; -}; - -class DeletingAnimArray : public TArray -{ -public: - ~DeletingAnimArray() - { - for(unsigned i=0;ibWarped) - { - if (type2) // [GRB] - warper = new FWarp2Texture (warper); - else - warper = new FWarpTexture (warper); - TexMan.ReplaceTexture (picnum, warper, false); - } - - // No decals on warping textures, by default. - // Warping information is taken from the last warp - // definition for this texture. - warper->bNoDecals = true; - if (SC_GetString ()) - { - if (SC_Compare ("allowdecals")) - { - warper->bNoDecals = false; - } - else - { - SC_UnGet (); - } - } - } - } - else if (SC_Compare ("cameratexture")) - { - int width, height; - int fitwidth, fitheight; - char *picname; - - SC_MustGetString (); - picname = copystring (sc_String); - SC_MustGetNumber (); - width = sc_Number; - SC_MustGetNumber (); - height = sc_Number; - int picnum = TexMan.CheckForTexture (picname, FTexture::TEX_Flat, texflags); - FTexture *viewer = new FCanvasTexture (picname, width, height); - if (picnum != -1) - { - FTexture *oldtex = TexMan[picnum]; - fitwidth = DivScale3 (oldtex->GetWidth (), oldtex->ScaleX ? oldtex->ScaleX : 8); - fitheight = DivScale3 (oldtex->GetHeight (), oldtex->ScaleY ? oldtex->ScaleY : 8); - viewer->UseType = oldtex->UseType; - TexMan.ReplaceTexture (picnum, viewer, true); - } - else - { - fitwidth = width; - fitheight = height; - // [GRB] No need for oldtex - viewer->UseType = FTexture::TEX_Wall; - TexMan.AddTexture (viewer); - } - delete[] picname; - if (SC_GetString()) - { - if (SC_Compare ("fit")) - { - SC_MustGetNumber (); - fitwidth = sc_Number; - SC_MustGetNumber (); - fitheight = sc_Number; - } - else - { - SC_UnGet (); - } - } - viewer->ScaleX = width * 8 / fitwidth; - viewer->ScaleY = height * 8 / fitheight; - } - else if (SC_Compare ("animatedDoor")) - { - P_ParseAnimatedDoor (); - } - else - { - SC_ScriptError (NULL); - } - } - SC_Close (); - } -} - -static void ParseAnim (bool istex) -{ - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; - TArray frames (32); - FAnimDef::FAnimFrame frame; - FAnimDef sink; - FAnimDef *newAnim; - int picnum; - int framenum; - int min, max; - unsigned int i; - bool optional = false, missing = false; - - SC_MustGetString (); - if (SC_Compare ("optional")) - { - optional = true; - SC_MustGetString (); - } - picnum = TexMan.CheckForTexture (sc_String, istex ? FTexture::TEX_Wall : FTexture::TEX_Flat, texflags); - - if (picnum < 0) - { - if (optional) - { - missing = true; - } - else - { - Printf (PRINT_BOLD, "ANIMDEFS: Can't find %s\n", sc_String); - } - } - - sink.CurFrame = 0; - sink.BasePic = picnum; - sink.bUniqueFrames = true; - sink.AnimType = FAnimDef::ANIM_Forward; - - // no decals on animating textures, by default - if (picnum >= 0) - { - TexMan[picnum]->bNoDecals = true; - } - - while (SC_GetString ()) - { - bool range = false; - - if (SC_Compare ("allowdecals")) - { - if (picnum >= 0) - { - TexMan[picnum]->bNoDecals = false; - } - continue; - } - else if (SC_Compare ("range")) - { - range = true; - if (!sink.bUniqueFrames) - { - SC_ScriptError ("You can only use range once in a single animation."); - } - if (frames.Size() != 0) - { - SC_ScriptError ("You cannot use range together with pic."); - } - } - else if (SC_Compare ("pic")) - { - if (!sink.bUniqueFrames) - { - SC_ScriptError ("You cannot use pic together with range."); - } - } - else - { - SC_UnGet (); - break; - } - - min = max = 1; - - SC_MustGetString (); - if (IsNum (sc_String)) - { - framenum = picnum + atoi(sc_String) - 1; - } - else - { - framenum = TexMan.CheckForTexture (sc_String, istex ? FTexture::TEX_Wall : FTexture::TEX_Flat, texflags); - if (framenum < 0 && !missing) - { - SC_ScriptError ("Unknown texture %s", sc_String); - } - } - SC_MustGetString (); - if (SC_Compare ("tics")) - { - SC_MustGetNumber (); - if (sc_Number < 0) - sc_Number = 0; - else if (sc_Number > 255) - sc_Number = 255; - min = max = sc_Number; - } - else if (SC_Compare ("rand") && !range) - { - SC_MustGetNumber (); - min = clamp (sc_Number, 0, 255); - SC_MustGetNumber (); - max = clamp (sc_Number, min, min+255); - } - else - { - SC_ScriptError ("Must specify a duration for animation frame"); - } - - if (picnum >= 0) - { - if (!range) - { - frame.SpeedMin = min; - frame.SpeedRange = max - min; - frame.FramePic = framenum; - frames.Push (frame); - } - else - { - int spread = framenum - picnum; - - sink.bUniqueFrames = false; - sink.NumFrames = abs(spread) + 1; - sink.Frames[0].SpeedMin = min; - sink.Frames[0].SpeedRange = max - min; - sink.Countdown = min - 1; - sink.AnimType = FAnimDef::ANIM_Forward; - if (spread < 0) - { - sink.AnimType = FAnimDef::ANIM_Backward; - TexMan[framenum]->bNoDecals = TexMan[picnum]->bNoDecals; - picnum = framenum; - sink.BasePic = picnum; - } - sink.Frames[0].FramePic = picnum; - } - } - } - - if (picnum >= 0) // If base pic is not present, don't add this anim - { - if (frames.Size() < 2 && (sink.bUniqueFrames || sink.NumFrames < 2)) - { - SC_ScriptError ("Animation needs at least 2 frames"); - } - - if (sink.bUniqueFrames) - { - sink.Countdown = frames[0].SpeedMin; - sink.NumFrames = frames.Size(); - - newAnim = (FAnimDef *)M_Malloc (sizeof(FAnimDef) + (frames.Size()-1)*sizeof(FAnimDef::FAnimFrame)); - *newAnim = sink; - - for (i = 0; i < frames.Size(); ++i) - { - newAnim->Frames[i] = frames[i]; - } - } - else - { - newAnim = (FAnimDef *)M_Malloc (sizeof(FAnimDef)); - *newAnim = sink; - } - - // See if there is already an anim with the same base pic. - // If so, replace it - for (i = Anims.Size(); i-- > 0; ) - { - if (Anims[i]->BasePic == picnum) - { - free (Anims[i]); - Anims[i] = newAnim; - return; - } - } - Anims.Push (newAnim); - } -} - -// -// P_InitPicAnims -// -// Load the table of animation definitions, checking for existence of -// the start and end of each frame. If the start doesn't exist the sequence -// is skipped, if the last doesn't exist, BOOM exits. -// -// Wall/Flat animation sequences, defined by name of first and last frame, -// The full animation sequence is given using all lumps between the start -// and end entry, in the order found in the WAD file. -// -// This routine modified to read its data from a predefined lump or -// PWAD lump called ANIMATED rather than a static table in this module to -// allow wad designers to insert or modify animation sequences. -// -// Lump format is an array of byte packed animdef_t structures, terminated -// by a structure with istexture == -1. The lump can be generated from a -// text source file using SWANTBLS.EXE, distributed with the BOOM utils. -// The standard list of switches and animations is contained in the example -// source text file DEFSWANI.DAT also in the BOOM util distribution. -// -// [RH] Rewritten to support BOOM ANIMATED lump but also make absolutely -// no assumptions about how the compiler packs the animdefs array. -// -void P_InitPicAnims (void) -{ - const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; - - if (Wads.CheckNumForName ("ANIMATED") != -1) - { - FAnimDef anim, *newAnim; - FMemLump animatedlump = Wads.ReadLump ("ANIMATED"); - const char *animdefs = (const char *)animatedlump.GetMem(); - const char *anim_p; - int pic1, pic2; - - // Init animation - - anim.bUniqueFrames = false; - anim.CurFrame = 0; - anim.AnimType = FAnimDef::ANIM_Forward; - - for (anim_p = animdefs; *anim_p != -1; anim_p += 23) - { - if (*anim_p /* .istexture */ & 1) - { - // different episode ? - if ((pic1 = TexMan.CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Wall, texflags)) == -1 || - (pic2 = TexMan.CheckForTexture (anim_p + 1 /* .endname */, FTexture::TEX_Wall, texflags)) == -1) - continue; - - if (*anim_p & 2) - { // [RH] Bit 1 set means allow decals on walls with this texture - TexMan[pic1]->bNoDecals = false; - } - else - { - TexMan[pic1]->bNoDecals = true; - } - } - else - { - if ((pic1 = TexMan.CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Flat, texflags)) == -1 || - (pic2 = TexMan.CheckForTexture (anim_p + 1 /* .startname */, FTexture::TEX_Flat, texflags)) == -1) - continue; - } - // [RH] Allow for either forward or backward animations. - if (pic1 < pic2) - { - anim.BasePic = pic1; - anim.NumFrames = pic2 - pic1 + 1; - } - else - { - anim.BasePic = pic2; - anim.NumFrames = pic1 - pic2 + 1; - anim.AnimType = FAnimDef::ANIM_Backward; - } - - anim.bUniqueFrames = false; - anim.CurFrame = 0; - - if (anim.NumFrames < 2) - I_FatalError ("P_InitPicAnims: bad cycle from %s to %s", - anim_p + 10 /* .startname */, - anim_p + 1 /* .endname */); - - anim.Frames[0].SpeedMin = - /* .speed */ - (anim_p[19] << 0) | - (anim_p[20] << 8) | - (anim_p[21] << 16) | - (anim_p[22] << 24); - - anim.Frames[0].SpeedRange = 0; - anim.Frames[0].FramePic = anim.BasePic; - anim.Countdown = anim.Frames[0].SpeedMin - 1; - - newAnim = (FAnimDef *)M_Malloc (sizeof(FAnimDef)); - *newAnim = anim; - Anims.Push (newAnim); - } - } - // [RH] Load any ANIMDEFS lumps - P_InitAnimDefs (); - P_FixAnimations (); -} - -void P_AddSimpleAnim (int picnum, int animcount, int animtype, int animspeed) -{ - FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef)); - anim->bUniqueFrames = false; - anim->CurFrame = 0; - anim->BasePic = picnum; - anim->NumFrames = animcount; - anim->AnimType = animtype; - anim->Frames[0].SpeedMin = animspeed; - anim->Frames[0].SpeedRange = 0; - anim->Frames[0].FramePic = anim->BasePic; - anim->Countdown = anim->Frames[0].SpeedMin - 1; - Anims.Push (anim); -} - -// [RH] Copy the "front sky" flag from an animated texture to the rest -// of the textures in the animation, and make every texture in an -// animation range use the same setting for bNoDecals. -static void P_FixAnimations () -{ - unsigned int i; - int j; - - for (i = 0; i < Anims.Size(); ++i) - { - FAnimDef *anim = Anims[i]; - if (anim->bUniqueFrames) - { - if (TexMan[anim->BasePic]->bNoRemap0) - { - for (j = 0; j < anim->NumFrames; ++j) - { - TexMan[anim->Frames[j].FramePic]->SetFrontSkyLayer (); - } - } - } - else - { - bool nodecals; - bool noremap = false; - const char *name; - - name = TexMan[anim->BasePic]->Name; - nodecals = TexMan[anim->BasePic]->bNoDecals; - for (j = 0; j < anim->NumFrames; ++j) - { - FTexture *tex = TexMan[anim->BasePic + j]; - noremap |= tex->bNoRemap0; - tex->bNoDecals = nodecals; - } - if (noremap) - { - for (j = 0; j < anim->NumFrames; ++j) - { - TexMan[anim->BasePic + j]->SetFrontSkyLayer (); - } - } - } - } -} // [RH] Check dmflags for noexit and respond accordingly bool CheckIfExitIsGood (AActor *self) @@ -1121,9 +568,6 @@ EXTERN_CVAR (Float, timelimit) void P_UpdateSpecials () { - unsigned int j; - int i; - // LEVEL TIMER if (deathmatch && timelimit) { @@ -1133,79 +577,6 @@ void P_UpdateSpecials () G_ExitLevel(0, false); } } - - // ANIMATE FLATS AND TEXTURES GLOBALLY - // [RH] Changed significantly to work with ANIMDEFS lumps - for (j = 0; j < Anims.Size(); ++j) - { - FAnimDef *anim = Anims[j]; - if (--anim->Countdown == 0) - { - int speedframe; - - switch (anim->AnimType) - { - case FAnimDef::ANIM_Forward: - anim->CurFrame = (anim->CurFrame + 1) % anim->NumFrames; - break; - - case FAnimDef::ANIM_Backward: - if (anim->CurFrame == 0) - { - anim->CurFrame = anim->NumFrames - 1; - } - else - { - anim->CurFrame -= 1; - } - break; - - case FAnimDef::ANIM_OscillateUp: - anim->CurFrame = anim->CurFrame + 1; - if (anim->CurFrame >= anim->NumFrames - 1) - { - anim->AnimType = FAnimDef::ANIM_OscillateDown; - } - break; - - case FAnimDef::ANIM_OscillateDown: - anim->CurFrame = anim->CurFrame - 1; - if (anim->CurFrame == 0) - { - anim->AnimType = FAnimDef::ANIM_OscillateUp; - } - break; - } - - speedframe = (anim->bUniqueFrames) ? anim->CurFrame : 0; - - if (anim->Frames[speedframe].SpeedRange == 0) - { - anim->Countdown = anim->Frames[speedframe].SpeedMin; - } - else - { - anim->Countdown = pr_animatepictures() % anim->Frames[speedframe].SpeedRange - + anim->Frames[speedframe].SpeedMin; - } - } - - if (anim->bUniqueFrames) - { - TexMan.SetTranslation (anim->BasePic, anim->Frames[anim->CurFrame].FramePic); - } - else - { - for (i = 0; i < anim->NumFrames; i++) - { - TexMan.SetTranslation (anim->BasePic + i, anim->BasePic + (i + anim->CurFrame) % anim->NumFrames); - } - } - } - - // [RH] Scroll the sky - sky1pos = sky1pos + level.skyspeed1; - sky2pos = sky2pos + level.skyspeed2; } diff --git a/src/p_spec.h b/src/p_spec.h index 21e9272947..fc4065399d 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -155,10 +155,6 @@ inline FArchive &operator<< (FArchive &arc, DPusher::EPusher &type) // it returns true, and the player is allowed to live. bool CheckIfExitIsGood (AActor *self); -// at game start -void P_InitPicAnims (void); -void P_AddSimpleAnim (int picnum, int animcount, int animtype, int animspeed); - // at map load void P_SpawnSpecials (void); diff --git a/src/r_anim.cpp b/src/r_anim.cpp new file mode 100644 index 0000000000..786fbdc3af --- /dev/null +++ b/src/r_anim.cpp @@ -0,0 +1,808 @@ +/* +** r_anim.cpp +** Routines for handling texture animation. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2006 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "cmdlib.h" +#include "i_system.h" +#include "r_local.h" +#include "r_sky.h" +#include "m_random.h" +#include "d_player.h" +#include "p_spec.h" +#include "sc_man.h" +#include "templates.h" +#include "w_wad.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// +// Animating textures and planes +// +// [RH] Expanded to work with a Hexen ANIMDEFS lump +// + +struct FAnimDef +{ + WORD BasePic; + WORD NumFrames; + WORD CurFrame; + BYTE AnimType; + DWORD SwitchTime; // Time to advance to next frame + struct FAnimFrame + { + DWORD SpeedMin; // Speeds are in ms, not tics + DWORD SpeedRange; + WORD FramePic; + } Frames[1]; + enum + { + ANIM_Forward, + ANIM_Backward, + ANIM_OscillateUp, + ANIM_OscillateDown, + ANIM_DiscreteFrames + }; + + void SetSwitchTime (DWORD mstime); +}; + +// This is an array of pointers to animation definitions. +// When it is destroyed, it deletes any animations it points to as well. +class AnimArray : public TArray +{ +public: + ~AnimArray(); + void AddAnim (FAnimDef *anim); + void FixAnimations (); +}; + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void R_InitAnimDefs (); +static void R_AddComplexAnim (int picnum, const TArray &frames); +static void ParseAnim (bool istex); +static void ParseRangeAnim (int picnum, int usetype, bool missing); +static void ParsePicAnim (int picnum, int usetype, bool missing, TArray &frames); +static int ParseFramenum (int basepicnum, int usetype, bool allowMissing); +static void ParseTime (DWORD &min, DWORD &max); + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static AnimArray Anims; +static FRandom pr_animatepictures ("AnimatePics"); + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// R_InitPicAnims +// +// [description copied from BOOM] +// Load the table of animation definitions, checking for existence of +// the start and end of each frame. If the start doesn't exist the sequence +// is skipped, if the last doesn't exist, BOOM exits. +// +// Wall/Flat animation sequences, defined by name of first and last frame, +// The full animation sequence is given using all lumps between the start +// and end entry, in the order found in the WAD file. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called ANIMATED rather than a static table in this module to +// allow wad designers to insert or modify animation sequences. +// +// Lump format is an array of byte packed animdef_t structures, terminated +// by a structure with istexture == -1. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// [RH] Rewritten to support BOOM ANIMATED lump but also make absolutely +// no assumptions about how the compiler packs the animdefs array. +// +//========================================================================== + +void R_InitPicAnims (void) +{ + const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; + + if (Wads.CheckNumForName ("ANIMATED") != -1) + { + FMemLump animatedlump = Wads.ReadLump ("ANIMATED"); + const char *animdefs = (const char *)animatedlump.GetMem(); + const char *anim_p; + int pic1, pic2; + int animtype; + DWORD animspeed; + + // Init animation + animtype = FAnimDef::ANIM_Forward; + + for (anim_p = animdefs; *anim_p != -1; anim_p += 23) + { + if (*anim_p /* .istexture */ & 1) + { + // different episode ? + if ((pic1 = TexMan.CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Wall, texflags)) == -1 || + (pic2 = TexMan.CheckForTexture (anim_p + 1 /* .endname */, FTexture::TEX_Wall, texflags)) == -1) + continue; + + // [RH] Bit 1 set means allow decals on walls with this texture + TexMan[pic2]->bNoDecals = TexMan[pic1]->bNoDecals = !(*anim_p & 2); + } + else + { + if ((pic1 = TexMan.CheckForTexture (anim_p + 10 /* .startname */, FTexture::TEX_Flat, texflags)) == -1 || + (pic2 = TexMan.CheckForTexture (anim_p + 1 /* .startname */, FTexture::TEX_Flat, texflags)) == -1) + continue; + } + if (pic1 == pic2) + { + // This animation only has one frame. Skip it. (Doom aborted instead.) + Printf ("Animation %s in ANIMATED has only one frame", anim_p + 10); + continue; + } + // [RH] Allow for backward animations as well as forward. + if (pic1 > pic2) + { + swap (pic1, pic2); + animtype = FAnimDef::ANIM_Backward; + } + + // Speed is stored as tics, but we want ms so scale accordingly. + animspeed = /* .speed */ + Scale ((anim_p[19] << 0) | + (anim_p[20] << 8) | + (anim_p[21] << 16) | + (anim_p[22] << 24), 1000, 35); + + R_AddSimpleAnim (pic1, pic2 - pic1 + 1, animtype, animspeed); + } + } + // [RH] Load any ANIMDEFS lumps + R_InitAnimDefs (); + Anims.FixAnimations (); +} + +//========================================================================== +// +// R_AddSimpleAnim +// +// Creates an animation with simple characteristics. This is used for +// original Doom (non-ANIMDEFS-style) animations and Build animations. +// +//========================================================================== + +void R_AddSimpleAnim (int picnum, int animcount, int animtype, DWORD speedmin, DWORD speedrange) +{ + FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef)); + anim->CurFrame = 0; + anim->BasePic = picnum; + anim->NumFrames = animcount; + anim->AnimType = animtype; + anim->SwitchTime = 0; + anim->Frames[0].SpeedMin = speedmin; + anim->Frames[0].SpeedRange = speedrange; + anim->Frames[0].FramePic = anim->BasePic; + Anims.AddAnim (anim); +} + +//========================================================================== +// +// R_AddComplexAnim +// +// Creates an animation with individually defined frames. +// +//========================================================================== + +static void R_AddComplexAnim (int picnum, const TArray &frames) +{ + FAnimDef *anim = (FAnimDef *)M_Malloc (sizeof(FAnimDef) + (frames.Size()-1) * sizeof(frames[0])); + anim->BasePic = picnum; + anim->NumFrames = frames.Size(); + anim->CurFrame = 0; + anim->AnimType = FAnimDef::ANIM_DiscreteFrames; + anim->SwitchTime = 0; + memcpy (&anim->Frames[0], &frames[0], frames.Size() * sizeof(frames[0])); + Anims.AddAnim (anim); +} + +//========================================================================== +// +// R_InitAnimDefs +// +// This uses a Hexen ANIMDEFS lump to define the animation sequences +// +//========================================================================== + +static void R_InitAnimDefs () +{ + const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; + int lump, lastlump = 0; + + while ((lump = Wads.FindLump ("ANIMDEFS", &lastlump)) != -1) + { + SC_OpenLumpNum (lump, "ANIMDEFS"); + + while (SC_GetString ()) + { + if (SC_Compare ("flat")) + { + ParseAnim (false); + } + else if (SC_Compare ("texture")) + { + ParseAnim (true); + } + else if (SC_Compare ("switch")) + { + P_ProcessSwitchDef (); + } + // [GRB] Added warping type 2 + else if (SC_Compare ("warp") || SC_Compare ("warp2")) + { + bool isflat = false; + BOOL type2 = SC_Compare ("warp2"); // [GRB] + SC_MustGetString (); + if (SC_Compare ("flat")) + { + isflat = true; + SC_MustGetString (); + } + else if (SC_Compare ("texture")) + { + isflat = false; + SC_MustGetString (); + } + else + { + SC_ScriptError (NULL); + } + int picnum = TexMan.CheckForTexture (sc_String, isflat ? FTexture::TEX_Flat : FTexture::TEX_Wall, texflags); + if (picnum != -1) + { + FTexture * warper = TexMan[picnum]; + + // don't warp a texture more than once + if (!warper->bWarped) + { + if (type2) // [GRB] + warper = new FWarp2Texture (warper); + else + warper = new FWarpTexture (warper); + TexMan.ReplaceTexture (picnum, warper, false); + } + + // No decals on warping textures, by default. + // Warping information is taken from the last warp + // definition for this texture. + warper->bNoDecals = true; + if (SC_GetString ()) + { + if (SC_Compare ("allowdecals")) + { + warper->bNoDecals = false; + } + else + { + SC_UnGet (); + } + } + } + } + else if (SC_Compare ("cameratexture")) + { + int width, height; + int fitwidth, fitheight; + FString picname; + + SC_MustGetString (); + picname = sc_String; + SC_MustGetNumber (); + width = sc_Number; + SC_MustGetNumber (); + height = sc_Number; + int picnum = TexMan.CheckForTexture (picname, FTexture::TEX_Flat, texflags); + FTexture *viewer = new FCanvasTexture (picname, width, height); + if (picnum != -1) + { + FTexture *oldtex = TexMan[picnum]; + fitwidth = DivScale3 (oldtex->GetWidth (), oldtex->ScaleX ? oldtex->ScaleX : 8); + fitheight = DivScale3 (oldtex->GetHeight (), oldtex->ScaleY ? oldtex->ScaleY : 8); + viewer->UseType = oldtex->UseType; + TexMan.ReplaceTexture (picnum, viewer, true); + } + else + { + fitwidth = width; + fitheight = height; + // [GRB] No need for oldtex + viewer->UseType = FTexture::TEX_Wall; + TexMan.AddTexture (viewer); + } + if (SC_GetString()) + { + if (SC_Compare ("fit")) + { + SC_MustGetNumber (); + fitwidth = sc_Number; + SC_MustGetNumber (); + fitheight = sc_Number; + } + else + { + SC_UnGet (); + } + } + viewer->ScaleX = width * 8 / fitwidth; + viewer->ScaleY = height * 8 / fitheight; + } + else if (SC_Compare ("animatedDoor")) + { + P_ParseAnimatedDoor (); + } + else + { + SC_ScriptError (NULL); + } + } + SC_Close (); + } +} + +//========================================================================== +// +// ParseAnim +// +// Parse a single animation definition out of an ANIMDEFS lump and +// create the corresponding animation structure. +// +//========================================================================== + +static void ParseAnim (bool istex) +{ + const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; + TArray frames (32); + int picnum; + int usetype; + int defined = 0; + bool optional = false, missing = false; + + usetype = istex ? FTexture::TEX_Wall : FTexture::TEX_Flat; + + SC_MustGetString (); + if (SC_Compare ("optional")) + { + optional = true; + SC_MustGetString (); + } + picnum = TexMan.CheckForTexture (sc_String, usetype, texflags); + + if (picnum < 0) + { + if (optional) + { + missing = true; + } + else + { + Printf (PRINT_BOLD, "ANIMDEFS: Can't find %s\n", sc_String); + } + } + + // no decals on animating textures, by default + if (picnum >= 0) + { + TexMan[picnum]->bNoDecals = true; + } + + while (SC_GetString ()) + { + if (SC_Compare ("allowdecals")) + { + if (picnum >= 0) + { + TexMan[picnum]->bNoDecals = false; + } + continue; + } + else if (SC_Compare ("range")) + { + if (defined == 2) + { + SC_ScriptError ("You cannot use \"pic\" and \"range\" together in a single animation."); + } + if (defined == 1) + { + SC_ScriptError ("You can only use one \"range\" per animation."); + } + defined = 1; + ParseRangeAnim (picnum, usetype, missing); + } + else if (SC_Compare ("pic")) + { + if (defined == 1) + { + SC_ScriptError ("You cannot use \"pic\" and \"range\" together in a single animation."); + } + defined = 2; + ParsePicAnim (picnum, usetype, missing, frames); + } + else + { + SC_UnGet (); + break; + } + } + + // If base pic is not present, don't add this anim + // ParseRangeAnim adds the anim itself, but ParsePicAnim does not. + if (picnum >= 0 && defined == 2) + { + if (frames.Size() < 2) + { + SC_ScriptError ("Animation needs at least 2 frames"); + } + R_AddComplexAnim (picnum, frames); + } +} + +//========================================================================== +// +// ParseRangeAnim +// +// Parse an animation defined using "range". Not that one range entry is +// enough to define a complete animation, unlike "pic". +// +//========================================================================== + +static void ParseRangeAnim (int picnum, int usetype, bool missing) +{ + int type, framenum; + DWORD min, max; + + type = FAnimDef::ANIM_Forward; + framenum = ParseFramenum (picnum, usetype, missing); + ParseTime (min, max); + + if (framenum == picnum || picnum < 0) + { + return; // Animation is only one frame or does not exist + } + if (framenum < picnum) + { + type = FAnimDef::ANIM_Backward; + TexMan[framenum]->bNoDecals = TexMan[picnum]->bNoDecals; + swap (framenum, picnum); + } + if (SC_GetString()) + { + if (SC_Compare ("Oscillate")) + { + type = type == FAnimDef::ANIM_Forward ? FAnimDef::ANIM_OscillateUp : FAnimDef::ANIM_OscillateDown; + } + else + { + SC_UnGet (); + } + } + R_AddSimpleAnim (picnum, framenum - picnum + 1, type, min, max - min); +} + +//========================================================================== +// +// ParsePicAnim +// +// Parse a single frame from ANIMDEFS defined using "pic". +// +//========================================================================== + +static void ParsePicAnim (int picnum, int usetype, bool missing, TArray &frames) +{ + int framenum; + DWORD min, max; + + framenum = ParseFramenum (picnum, usetype, missing); + ParseTime (min, max); + + if (picnum >= 0) + { + FAnimDef::FAnimFrame frame; + + frame.SpeedMin = min; + frame.SpeedRange = max - min; + frame.FramePic = framenum; + frames.Push (frame); + } +} + +//========================================================================== +// +// ParseFramenum +// +// Reads a frame's texture from ANIMDEFS. It can either be an integral +// offset from basepicnum or a specific texture name. +// +//========================================================================== + +static int ParseFramenum (int basepicnum, int usetype, bool allowMissing) +{ + const BITFIELD texflags = FTextureManager::TEXMAN_Overridable | FTextureManager::TEXMAN_TryAny; + int framenum; + + SC_MustGetString (); + if (IsNum (sc_String)) + { + framenum = basepicnum + atoi(sc_String) - 1; + } + else + { + framenum = TexMan.CheckForTexture (sc_String, usetype, texflags); + if (framenum < 0 && !allowMissing) + { + SC_ScriptError ("Unknown texture %s", sc_String); + } + } + return framenum; +} + +//========================================================================== +// +// ParseTime +// +// Reads a tics or rand time definition from ANIMDEFS. +// +//========================================================================== + +static void ParseTime (DWORD &min, DWORD &max) +{ + SC_MustGetString (); + if (SC_Compare ("tics")) + { + SC_MustGetFloat (); + min = max = DWORD(sc_Float * 1000 / 35); + } + else if (SC_Compare ("rand")) + { + SC_MustGetFloat (); + min = DWORD(sc_Float * 1000 / 35); + SC_MustGetFloat (); + max = DWORD(sc_Float * 1000 / 35); + } + else + { + SC_ScriptError ("Must specify a duration for animation frame"); + } +} + +//========================================================================== +// +// AnimArray :: ~AnimArray +// +// Frees all animations held in this array before freeing the array. +// +//========================================================================== + +AnimArray::~AnimArray() +{ + for (unsigned i = 0; i < Size(); i++) + { + if ((*this)[i] != NULL) + { + free ((*this)[i]); + (*this)[i] = NULL; + } + } +} + +//========================================================================== +// +// AnimArray :: AddAnim +// +// Adds a new animation to the array. If one with the same basepic as the +// new one already exists, it is replaced. +// +//========================================================================== + +void AnimArray::AddAnim (FAnimDef *anim) +{ + // Search for existing duplicate. + for (unsigned int i = 0; i < Anims.Size(); ++i) + { + if ((*this)[i]->BasePic == anim->BasePic) + { + // Found one! + free ((*this)[i]); + (*this)[i] = anim; + return; + } + } + // Didn't find one, so add it at the end. + Push (anim); +} + +//========================================================================== +// +// AnimArray :: FixAnimations +// +// Copy the "front sky" flag from an animated texture to the rest +// of the textures in the animation, and make every texture in an +// animation range use the same setting for bNoDecals. +// +//========================================================================== + +void AnimArray::FixAnimations () +{ + unsigned int i; + int j; + + for (i = 0; i < Size(); ++i) + { + FAnimDef *anim = operator[] (i); + if (anim->AnimType == FAnimDef::ANIM_DiscreteFrames) + { + if (TexMan[anim->BasePic]->bNoRemap0) + { + for (j = 0; j < anim->NumFrames; ++j) + { + TexMan[anim->Frames[j].FramePic]->SetFrontSkyLayer (); + } + } + } + else + { + bool nodecals; + bool noremap = false; + const char *name; + + name = TexMan[anim->BasePic]->Name; + nodecals = TexMan[anim->BasePic]->bNoDecals; + for (j = 0; j < anim->NumFrames; ++j) + { + FTexture *tex = TexMan[anim->BasePic + j]; + noremap |= tex->bNoRemap0; + tex->bNoDecals = nodecals; + } + if (noremap) + { + for (j = 0; j < anim->NumFrames; ++j) + { + TexMan[anim->BasePic + j]->SetFrontSkyLayer (); + } + } + } + } +} + +//========================================================================== +// +// FAnimDef :: SetSwitchTime +// +// Determines when to switch to the next frame. +// +//========================================================================== + +void FAnimDef::SetSwitchTime (DWORD mstime) +{ + int speedframe = (AnimType == FAnimDef::ANIM_DiscreteFrames) ? CurFrame : 0; + + SwitchTime = mstime + Frames[speedframe].SpeedMin; + if (Frames[speedframe].SpeedRange != 0) + { + SwitchTime += pr_animatepictures(Frames[speedframe].SpeedRange); + } +} + +//========================================================================== +// +// R_UpdateAnimations +// +// Updates texture translations for each animation and scrolls the skies. +// +//========================================================================== + +void R_UpdateAnimations (DWORD mstime) +{ + for (unsigned int j = 0; j < Anims.Size(); ++j) + { + FAnimDef *anim = Anims[j]; + + // If this is the first time through R_UpdateAnimations, just + // initialize the anim's switch time without actually animating. + if (anim->SwitchTime == 0) + { + anim->SetSwitchTime (mstime); + } + else while (anim->SwitchTime <= mstime) + { // Multiple frames may have passed since the last time calling + // R_UpdateAnimations, so be sure to loop through them all. + + switch (anim->AnimType) + { + default: + case FAnimDef::ANIM_Forward: + case FAnimDef::ANIM_DiscreteFrames: + anim->CurFrame = (anim->CurFrame + 1) % anim->NumFrames; + break; + + case FAnimDef::ANIM_Backward: + if (anim->CurFrame == 0) + { + anim->CurFrame = anim->NumFrames - 1; + } + else + { + anim->CurFrame -= 1; + } + break; + + case FAnimDef::ANIM_OscillateUp: + anim->CurFrame = anim->CurFrame + 1; + if (anim->CurFrame >= anim->NumFrames - 1) + { + anim->AnimType = FAnimDef::ANIM_OscillateDown; + } + break; + + case FAnimDef::ANIM_OscillateDown: + anim->CurFrame = anim->CurFrame - 1; + if (anim->CurFrame == 0) + { + anim->AnimType = FAnimDef::ANIM_OscillateUp; + } + break; + } + anim->SetSwitchTime (mstime); + } + + if (anim->AnimType == FAnimDef::ANIM_DiscreteFrames) + { + TexMan.SetTranslation (anim->BasePic, anim->Frames[anim->CurFrame].FramePic); + } + else + { + for (unsigned int i = 0; i < anim->NumFrames; i++) + { + TexMan.SetTranslation (anim->BasePic + i, anim->BasePic + (i + anim->CurFrame) % anim->NumFrames); + } + } + } + + // Scroll the sky + double ms = mstime * FRACUNIT; + sky1pos = fixed_t(ms * level.skyspeed1); + sky2pos = fixed_t(ms * level.skyspeed2); +} diff --git a/src/r_data.cpp b/src/r_data.cpp index 44599a94ed..92b3bcdd80 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -505,9 +505,9 @@ void FTextureManager::AddTiles (void *tiles) } speed = (anm >> 24) & 15; - speed = MAX (1, (1 << speed) * TICRATE / 120); + speed = MAX (1, (1 << speed) * 1000 / 120); // Convert from 120 Hz to 1000 Hz. - P_AddSimpleAnim (texnum, picanm[pic] & 63, type, speed); + R_AddSimpleAnim (texnum, picanm[pic] & 63, type, speed); } // Blood's rotation types: diff --git a/src/r_local.h b/src/r_local.h index b7b4f799d4..944dade8ee 100644 --- a/src/r_local.h +++ b/src/r_local.h @@ -43,4 +43,10 @@ #include "r_things.h" #include "r_draw.h" +// r_anim.cpp + +void R_InitPicAnims (); +void R_AddSimpleAnim (int picnum, int animcount, int animtype, DWORD animspeed /* in ms */, DWORD speedrange=0); +void R_UpdateAnimations (DWORD mstime); + #endif // __R_LOCAL_H__ diff --git a/src/r_main.cpp b/src/r_main.cpp index a72640ee5b..d59fd6adc1 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -1045,6 +1045,7 @@ void R_SetupFrame (AActor *actor) iview->otic = nowtic; } + R_UpdateAnimations (I_MSTime()); r_TicFrac = I_GetTimeFrac (&r_FrameTime); if (cl_capfps || r_NoInterpolate) {