/* Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. and the "Aleph One" developers. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. This license is contained in the file "COPYING", which is included with this source code; it is available online at http://www.gnu.org/licenses/gpl.html February 21, 2000 (Loren Petrich) Animated-textures file. This implements animated wall textures, which are now read off of XML configuration files. May 12, 2000 (Loren Petrich) Modified to abolish the ATxr resources, and also to add XML support. A "select" feature was added, so as to be able to translate only some specified texture. Oct 13, 2000 (Loren Petrich) Converted the animated-texture accounting into Standard Template Library vectors */ #include #include #include "cseries.h" #include "AnimatedTextures.h" #include "interface.h" class AnimTxtr { // Frame data: private, because the frame list is only supposed vector FrameList; // Control info: private, because they need to stay self-consistent // How long the animator stays on a frame; // negative means animate in reverse direction short NumTicks; // Phases: which frame, and which tick in a frame // These must be in ranges (0 to NumFrames - 1) and (0 to abs(NumTicks) - 1) size_t FramePhase, TickPhase; public: // How many frames size_t GetNumFrames() {return FrameList.size();} // Frame ID (writable) short& GetFrame(size_t Index) {return FrameList[Index];} // Clear the list void Clear() {FrameList.clear();} // Load from list: void Load(vector& _FrameList); // Translate the frame; indicate whether the frame was translated. // Its argument is the frame ID, which gets changed in place bool Translate(short& Frame); // Set the timing info: number of ticks per frame, and tick and frame phases void SetTiming(short _NumTicks, size_t _FramePhase, size_t _TickPhase); // Update the phases: void Update(); // Which texture to select (if -1 [NONE], then select all those in the frame list); // the selected texture is the one that gets translated, // and if this is set, then it gets translated into the equivalent of the // first member of the loop. short Select; // Possible addition: // whether the texture is active or not; // one could include some extra data to indicate whether to activate // the texture or not, as when a switch tag changes state. // Constructor has defaults of everything AnimTxtr() { NumTicks = 30; // Once a second FramePhase = 0; TickPhase = 0; Select = -1; } }; void AnimTxtr::Load(vector& _FrameList) { // Quick way to transfer the frame data FrameList.swap(_FrameList); if (FrameList.empty()) return; // Just in case it was set to something out-of-range... FramePhase = FramePhase % FrameList.size(); } bool AnimTxtr::Translate(short& Frame) { // Sanity check size_t NumFrames = FrameList.size(); if (NumFrames == 0) return false; // Find the index in the loop; default: first one. size_t FrameIndex = 0; if (Select >= 0) { if (Frame != Select) return false; } else { bool FrameFound = false; for (size_t f=0; f(TickPhase) / NumTicks; if (static_cast(TickPhase) < NumTicks*TickPhaseFrames) { TickPhase += abs(NumTicks) - NumTicks*TickPhaseFrames; TickPhaseFrames--; } else { TickPhase -= NumTicks*TickPhaseFrames; } FramePhase += TickPhaseFrames; } size_t NumFrames = FrameList.size(); if (NumFrames != 0) { FramePhase = FramePhase % NumFrames; } } void AnimTxtr::Update() { // Be careful to wrap around in the appropriate direction size_t NumFrames = FrameList.size(); if (NumTicks > 0) { if (static_cast(++TickPhase) >= NumTicks) { TickPhase = 0; if (++FramePhase >= NumFrames) FramePhase = 0; } } else if (NumTicks < 0) { if (TickPhase == 0) { TickPhase = - NumTicks - 1; if (FramePhase != 0) FramePhase--; else FramePhase = NumFrames - 1; } else { TickPhase--; } } } // Separate animated-texture sequence lists for each collection ID, // to speed up searching static vector AnimTxtrList[NUMBER_OF_COLLECTIONS]; // Deletes a collection's animated-texture sequences static void ATDelete(int c) { AnimTxtrList[c].clear(); } // Deletes all of them static void ATDeleteAll() { for (int c=0; c& ATL = AnimTxtrList[c]; for (vector::iterator ATIter = ATL.begin(); ATIter < ATL.end(); ATIter++) ATIter->Update(); } } // Does animated-texture translation in place shape_descriptor AnimTxtr_Translate(shape_descriptor Texture) { if (Texture == UNONE) return UNONE; // Pull out frame and collection ID's: short Frame = GET_DESCRIPTOR_SHAPE(Texture); short CollCT = GET_DESCRIPTOR_COLLECTION(Texture); short Collection = GET_COLLECTION(CollCT); short ColorTable = GET_COLLECTION_CLUT(CollCT); // This will assume that the collection is loaded; // that could be handled as map preprocessing, by turning // all shape descriptors that refer to unloaded shapes to NONE vector& ATL = AnimTxtrList[Collection]; for (vector::iterator ATIter = ATL.begin(); ATIter < ATL.end(); ATIter++) if (ATIter->Translate(Frame)) break; // Check the frame for being in range if (Frame < 0) return UNONE; if (Frame >= get_number_of_collection_frames(Collection)) return UNONE; // All done: CollCT = BUILD_COLLECTION(Collection,ColorTable); Texture = BUILD_DESCRIPTOR(Collection,Frame); return Texture; } // Temporary array for storing frames // when doing XML parsing static vector TempFrameList; class XML_AT_ClearParser: public XML_ElementParser { bool IsPresent; short Collection; public: bool Start(); bool HandleAttribute(const char *Tag, const char *Value); bool AttributesDone(); XML_AT_ClearParser(): XML_ElementParser("clear") {} }; bool XML_AT_ClearParser::Start() { IsPresent = false; return true; } bool XML_AT_ClearParser::HandleAttribute(const char *Tag, const char *Value) { if (StringsEqual(Tag,"coll")) { if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) { IsPresent = true; return true; } else return false; } UnrecognizedTag(); return false; } bool XML_AT_ClearParser::AttributesDone() { if (IsPresent) ATDelete(Collection); else ATDeleteAll(); return true; } static XML_AT_ClearParser AT_ClearParser; class XML_AT_FrameParser: public XML_ElementParser { bool IsPresent; short Index; public: bool Start(); bool HandleAttribute(const char *Tag, const char *Value); bool AttributesDone(); XML_AT_FrameParser(): XML_ElementParser("frame") {} }; bool XML_AT_FrameParser::Start() { IsPresent = false; return true; } bool XML_AT_FrameParser::HandleAttribute(const char *Tag, const char *Value) { if (StringsEqual(Tag,"index")) { if (ReadBoundedInt16Value(Value,Index,0,254)) { IsPresent = true; return true; } else return false; } UnrecognizedTag(); return false; } bool XML_AT_FrameParser::AttributesDone() { // Verify... if (!IsPresent) { AttribsMissing(); return false; } TempFrameList.push_back(Index); return true; } static XML_AT_FrameParser AT_FrameParser; class XML_AT_SequenceParser: public XML_ElementParser { // Mandatory attributes enum {NumberMandatory = 2}; bool IsPresent[NumberMandatory]; short Collection, NumTicks; // Optional Attributes uint16 FramePhase, TickPhase; short Select; public: bool Start(); bool HandleAttribute(const char *Tag, const char *Value); bool AttributesDone(); bool ResetValues(); bool End(); XML_AT_SequenceParser(): XML_ElementParser("sequence") {} }; bool XML_AT_SequenceParser::Start() { for (int k=0; k