1 /*
2 
3 	Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
4 	and the "Aleph One" developers.
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 3 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	This license is contained in the file "COPYING",
17 	which is included with this source code; it is available online at
18 	http://www.gnu.org/licenses/gpl.html
19 
20 	February 21, 2000 (Loren Petrich)
21 
22 	Animated-textures file. This implements animated wall textures,
23 	which are now read off of XML configuration files.
24 
25 	May 12, 2000 (Loren Petrich)
26 
27 	Modified to abolish the ATxr resources, and also to add XML support.
28 	A "select" feature was added, so as to be able to translate only some specified texture.
29 
30 Oct 13, 2000 (Loren Petrich)
31 	Converted the animated-texture accounting into Standard Template Library vectors
32 */
33 
34 #include <vector>
35 #include <string.h>
36 #include "cseries.h"
37 #include "AnimatedTextures.h"
38 #include "interface.h"
39 #include "InfoTree.h"
40 
41 
42 class AnimTxtr
43 {
44 	// Frame data: private, because the frame list is only supposed
45 	vector<short> FrameList;
46 
47 	// Control info: private, because they need to stay self-consistent
48 	// How long the animator stays on a frame;
49 	// negative means animate in reverse direction
50 	short NumTicks;
51 
52 	// Phases: which frame, and which tick in a frame
53 	// These must be in ranges (0 to NumFrames - 1) and (0 to abs(NumTicks) - 1)
54 	size_t FramePhase, TickPhase;
55 
56 public:
57 	// How many frames
GetNumFrames()58 	size_t GetNumFrames() {return FrameList.size();}
59 
60 	// Frame ID (writable)
GetFrame(size_t Index)61 	short& GetFrame(size_t Index) {return FrameList[Index];}
62 
63 	// Clear the list
Clear()64 	void Clear() {FrameList.clear();}
65 
66 	// Load from list:
67 	void Load(vector<short>& _FrameList);
68 
69 	// Translate the frame; indicate whether the frame was translated.
70 	// Its argument is the frame ID, which gets changed in place
71 	bool Translate(short& Frame);
72 
73 	// Set the timing info: number of ticks per frame, and tick and frame phases
74 	void SetTiming(short _NumTicks, size_t _FramePhase, size_t _TickPhase);
75 
76 	// Update the phases:
77 	void Update();
78 
79 	// Which texture to select (if -1 [NONE], then select all those in the frame list);
80 	// the selected texture is the one that gets translated,
81 	// and if this is set, then it gets translated into the equivalent of the
82 	// first member of the loop.
83 	short Select;
84 
85 	// Possible addition:
86 	// whether the texture is active or not;
87 	// one could include some extra data to indicate whether to activate
88 	// the texture or not, as when a switch tag changes state.
89 
90 	// Constructor has defaults of everything
AnimTxtr()91 	AnimTxtr()
92 	{
93 		NumTicks = 30;	// Once a second
94 		FramePhase = 0;
95 		TickPhase = 0;
96 		Select = -1;
97 	}
98 };
99 
100 
101 
Load(vector<short> & _FrameList)102 void AnimTxtr::Load(vector<short>& _FrameList)
103 {
104 	// Quick way to transfer the frame data
105 	FrameList.swap(_FrameList);
106 	if (FrameList.empty()) return;
107 
108 	// Just in case it was set to something out-of-range...
109 	FramePhase = FramePhase % FrameList.size();
110 }
111 
112 
Translate(short & Frame)113 bool AnimTxtr::Translate(short& Frame)
114 {
115 	// Sanity check
116 	size_t NumFrames = FrameList.size();
117 	if (NumFrames == 0) return false;
118 
119 	// Find the index in the loop; default: first one.
120 	size_t FrameIndex = 0;
121 	if (Select >= 0)
122 	{
123 		if (Frame != Select) return false;
124 	}
125 	else
126 	{
127 		bool FrameFound = false;
128 		for (size_t f=0; f<NumFrames; f++)
129 			if (FrameList[f] == Frame)
130 			{
131 				FrameFound = true;
132 				FrameIndex = f;
133 			}
134 		if (!FrameFound) return false;
135 	}
136 	FrameIndex += FramePhase;
137 	FrameIndex %= NumFrames;
138 
139 	Frame = FrameList[FrameIndex];
140 	return true;
141 }
142 
143 
SetTiming(short _NumTicks,size_t _FramePhase,size_t _TickPhase)144 void AnimTxtr::SetTiming(short _NumTicks, size_t _FramePhase, size_t _TickPhase)
145 {
146 	NumTicks = _NumTicks;
147 	FramePhase = _FramePhase;
148 	TickPhase = _TickPhase;
149 
150 	// Correct for possible overshooting of limits:
151 	if (NumTicks)
152 	{
153 		int TickPhaseFrames = static_cast<int>(TickPhase) / NumTicks;
154 		if (static_cast<int>(TickPhase) < NumTicks*TickPhaseFrames)
155 		{
156 			TickPhase += abs(NumTicks) - NumTicks*TickPhaseFrames;
157 			TickPhaseFrames--;
158 		}
159 		else
160 		{	TickPhase -= NumTicks*TickPhaseFrames; }
161 
162 		FramePhase += TickPhaseFrames;
163 	}
164 
165 	size_t NumFrames = FrameList.size();
166 	if (NumFrames != 0)
167 	{	FramePhase = FramePhase % NumFrames; }
168 }
169 
170 
Update()171 void AnimTxtr::Update()
172 {
173 	// Be careful to wrap around in the appropriate direction
174 	size_t NumFrames = FrameList.size();
175 	if (NumTicks > 0)
176 	{
177 		if (static_cast<int>(++TickPhase) >= NumTicks)
178 		{
179 			TickPhase = 0;
180 			if (++FramePhase >= NumFrames)
181 				FramePhase = 0;
182 		}
183 	}
184 	else if (NumTicks < 0)
185 	{
186 		if (TickPhase == 0)
187 		{
188 			TickPhase = - NumTicks - 1;
189 			if (FramePhase != 0)
190 				FramePhase--;
191 			else
192 				FramePhase = NumFrames - 1;
193 		}
194 		else
195 		{	TickPhase--; }
196 	}
197 }
198 
199 
200 // Separate animated-texture sequence lists for each collection ID,
201 // to speed up searching
202 static vector<AnimTxtr> AnimTxtrList[NUMBER_OF_COLLECTIONS];
203 
204 
205 // Deletes a collection's animated-texture sequences
ATDelete(int c)206 static void ATDelete(int c)
207 {
208 	AnimTxtrList[c].clear();
209 }
210 
211 
212 // Deletes all of them
ATDeleteAll()213 static void ATDeleteAll()
214 {
215 	for (int c=0; c<NUMBER_OF_COLLECTIONS; c++) ATDelete(c);
216 }
217 
218 
219 // Updates the animated textures
AnimTxtr_Update()220 void AnimTxtr_Update()
221 {
222 	for (int c=0; c<NUMBER_OF_COLLECTIONS; c++)
223 	{
224 		vector<AnimTxtr>& ATL = AnimTxtrList[c];
225 		for (vector<AnimTxtr>::iterator ATIter = ATL.begin(); ATIter < ATL.end(); ATIter++)
226 			ATIter->Update();
227 	}
228 }
229 
230 
231 // Does animated-texture translation in place
AnimTxtr_Translate(shape_descriptor Texture)232 shape_descriptor AnimTxtr_Translate(shape_descriptor Texture)
233 {
234 	if (Texture == UNONE) return UNONE;
235 
236 	// Pull out frame and collection ID's:
237 	short Frame = GET_DESCRIPTOR_SHAPE(Texture);
238 	short CollCT = GET_DESCRIPTOR_COLLECTION(Texture);
239 	short Collection = GET_COLLECTION(CollCT);
240 	short ColorTable = GET_COLLECTION_CLUT(CollCT);
241 
242 	// This will assume that the collection is loaded;
243 	// that could be handled as map preprocessing, by turning
244 	// all shape descriptors that refer to unloaded shapes to NONE
245 
246 	vector<AnimTxtr>& ATL = AnimTxtrList[Collection];
247 	for (vector<AnimTxtr>::iterator ATIter = ATL.begin(); ATIter < ATL.end(); ATIter++)
248 		if (ATIter->Translate(Frame)) break;
249 
250 	// Check the frame for being in range
251 	if (Frame < 0) return UNONE;
252 	if (Frame >= get_number_of_collection_frames(Collection)) return UNONE;
253 
254 	// All done:
255 	CollCT = BUILD_COLLECTION(Collection,ColorTable);
256 	Texture = BUILD_DESCRIPTOR(CollCT,Frame);
257 	return Texture;
258 }
259 
reset_mml_animated_textures()260 void reset_mml_animated_textures()
261 {
262 	ATDeleteAll();
263 }
264 
parse_mml_animated_textures(const InfoTree & root)265 void parse_mml_animated_textures(const InfoTree& root)
266 {
267 	BOOST_FOREACH(const InfoTree::value_type &v, root)
268 	{
269 		std::string name = v.first;
270 		const InfoTree& child = v.second;
271 
272 		if (v.first == "clear")
273 		{
274 			int16 coll = -1;
275 			if (child.read_indexed("coll", coll, NUMBER_OF_COLLECTIONS))
276 				ATDelete(coll);
277 			else
278 				ATDeleteAll();
279 		}
280 		else if (v.first == "sequence")
281 		{
282 			int16 coll = -1;
283 			int16 numticks = -1;
284 			if (!child.read_indexed("coll", coll, NUMBER_OF_COLLECTIONS) ||
285 				!child.read_attr("numticks", numticks))
286 				continue;
287 
288 			vector<short> frames;
289 			BOOST_FOREACH(InfoTree frame, child.children_named("frame"))
290 			{
291 				int16 index = -1;
292 				if (frame.read_indexed("index", index, 255))
293 					frames.push_back(index);
294 			}
295 			if (!frames.size())
296 				continue;
297 
298 			uint16 frame_phase = 0;
299 			child.read_attr("framephase", frame_phase);
300 			uint16 tick_phase = 0;
301 			child.read_attr("tickphase", tick_phase);
302 			int16 select = -1;
303 			child.read_attr("select", select);
304 
305 			AnimTxtr new_anim;
306 			new_anim.Load(frames);
307 			new_anim.SetTiming(numticks, frame_phase, tick_phase);
308 			new_anim.Select = select;
309 			AnimTxtrList[coll].push_back(new_anim);
310 		}
311 	}
312 }
313 
314