DEADSOFTWARE

render: fix animated textures
[d2df-sdl.git] / src / game / g_textures.pas
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, version 3 of the License ONLY.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *)
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_textures;
18 interface
20 uses
21 SysUtils, Classes,
22 {$IFDEF USE_MEMPOOL}mempool,{$ENDIF}
23 g_base, r_graphics, MAPDEF, ImagingTypes, Imaging, ImagingUtility;
25 type
26 TLevelTexture = record
27 TextureName: AnsiString; // as stored in wad
28 FullName: AnsiString; // full path to texture // !!! merge it with TextureName
29 framesCount, speed: Byte;
30 end;
32 TLevelTextureArray = array of TLevelTexture;
34 TAnimationState = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
35 private
36 mAlpha: Byte;
37 mBlending: Boolean;
38 mCounter: Byte; // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
39 mSpeed: Byte; // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
40 mCurrentFrame: Integer; // Òåêóùèé êàäð (íà÷èíàÿ ñ 0)
41 mLoop: Boolean; // Ïåðåõîäèòü íà ïåðâûé êàäð ïîñëå ïîñëåäíåãî?
42 mEnabled: Boolean; // Ðàáîòà ðàçðåøåíà?
43 mPlayed: Boolean; // Ïðîèãðàíà âñÿ õîòÿ áû ðàç?
44 mMinLength: Byte; // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
45 mRevert: Boolean; // Ñìåíà êàäðîâ îáðàòíàÿ?
47 mLength: Integer;
49 public
50 constructor Create (aloop: Boolean; aspeed: Byte; len: Integer);
51 destructor Destroy (); override;
53 procedure reset ();
54 procedure update ();
55 procedure enable ();
56 procedure disable ();
57 procedure revert (r: Boolean);
59 procedure saveState (st: TStream);
60 procedure loadState (st: TStream);
62 function totalFrames (): Integer; inline;
64 public
65 property played: Boolean read mPlayed;
66 property enabled: Boolean read mEnabled;
67 property isReverse: Boolean read mRevert;
68 property loop: Boolean read mLoop write mLoop;
69 property speed: Byte read mSpeed write mSpeed;
70 property minLength: Byte read mMinLength write mMinLength;
71 property currentFrame: Integer read mCurrentFrame write mCurrentFrame;
72 property currentCounter: Byte read mCounter write mCounter;
73 property counter: Byte read mCounter;
74 property blending: Boolean read mBlending write mBlending;
75 property alpha: Byte read mAlpha write mAlpha;
76 end;
78 TAnimation = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
79 private
80 mId: LongWord;
81 mAlpha: Byte;
82 mBlending: Boolean;
83 mCounter: Byte; // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
84 mSpeed: Byte; // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
85 mCurrentFrame: Integer; // Òåêóùèé êàäð (íà÷èíàÿ ñ 0)
86 mLoop: Boolean; // Ïåðåõîäèòü íà ïåðâûé êàäð ïîñëå ïîñëåäíåãî?
87 mEnabled: Boolean; // Ðàáîòà ðàçðåøåíà?
88 mPlayed: Boolean; // Ïðîèãðàíà âñÿ õîòÿ áû ðàç?
89 mHeight: Word;
90 mWidth: Word;
91 mMinLength: Byte; // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
92 mRevert: Boolean; // Ñìåíà êàäðîâ îáðàòíàÿ?
94 public
95 constructor Create (aframesID: LongWord; aloop: Boolean; aspeed: Byte);
96 destructor Destroy (); override;
98 procedure reset ();
99 procedure update ();
100 procedure enable ();
101 procedure disable ();
102 procedure revert (r: Boolean);
104 procedure saveState (st: TStream);
105 procedure loadState (st: TStream);
107 function totalFrames (): Integer; inline;
109 public
110 property played: Boolean read mPlayed;
111 property enabled: Boolean read mEnabled;
112 property isReverse: Boolean read mRevert;
113 property loop: Boolean read mLoop write mLoop;
114 property speed: Byte read mSpeed write mSpeed;
115 property minLength: Byte read mMinLength write mMinLength;
116 property currentFrame: Integer read mCurrentFrame write mCurrentFrame;
117 property currentCounter: Byte read mCounter write mCounter;
118 property counter: Byte read mCounter;
119 property blending: Boolean read mBlending write mBlending;
120 property alpha: Byte read mAlpha write mAlpha;
121 property framesId: LongWord read mId;
122 property width: Word read mWidth;
123 property height: Word read mHeight;
125 property id: LongWord read mId;
126 end;
128 implementation
130 uses
131 g_game, e_log, g_basic, g_console, wadreader, r_animations,
132 g_language, utils, xstreams;
137 constructor TAnimationState.Create (aloop: Boolean; aspeed: Byte; len: Integer);
138 begin
139 assert(len >= 0);
140 mLength := len;
142 mMinLength := 0;
143 mLoop := aloop;
144 mSpeed := aspeed;
145 mEnabled := true;
146 mCurrentFrame := 0;
147 mAlpha := 0;
148 mPlayed := false;
149 end;
151 destructor TAnimationState.Destroy;
152 begin
153 inherited;
154 end;
156 procedure TAnimationState.update;
157 begin
158 if (not mEnabled) then exit;
160 mCounter += 1;
162 if (mCounter >= mSpeed) then
163 begin
164 // Îæèäàíèå ìåæäó êàäðàìè çàêîí÷èëîñü
165 // Îáðàòíûé ïîðÿäîê êàäðîâ?
166 if mRevert then
167 begin
168 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
169 if (mCurrentFrame = 0) then
170 begin
171 if (mLength * mSpeed + mCounter < mMinLength) then exit;
172 end;
174 mCurrentFrame -= 1;
175 mPlayed := (mCurrentFrame < 0);
177 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
178 if mPlayed then
179 begin
180 if mLoop then
181 mCurrentFrame := mLength - 1
182 else
183 mCurrentFrame += 1
184 end;
186 mCounter := 0;
187 end
188 else
189 begin
190 // Ïðÿìîé ïîðÿäîê êàäðîâ
191 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
192 if (mCurrentFrame = mLength - 1) then
193 begin
194 if (mLength * mSpeed + mCounter < mMinLength) then exit;
195 end;
197 mCurrentFrame += 1;
198 mPlayed := (mCurrentFrame > mLength - 1);
200 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
201 if mPlayed then
202 begin
203 if mLoop then mCurrentFrame := 0 else mCurrentFrame -= 1;
204 end;
206 mCounter := 0;
207 end;
208 end;
209 end;
211 procedure TAnimationState.reset;
212 begin
213 if mRevert then
214 mCurrentFrame := mLength - 1
215 else
216 mCurrentFrame := 0;
217 mCounter := 0;
218 mPlayed := false
219 end;
221 procedure TAnimationState.disable;
222 begin
223 mEnabled := false
224 end;
226 procedure TAnimationState.enable;
227 begin
228 mEnabled := true
229 end;
231 procedure TAnimationState.revert (r: Boolean);
232 begin
233 mRevert := r;
234 reset
235 end;
237 function TAnimationState.totalFrames (): Integer; inline;
238 begin
239 result := mLength
240 end;
242 procedure TAnimationState.saveState (st: TStream);
243 begin
244 if (st = nil) then exit;
246 utils.writeSign(st, 'ANIM');
247 utils.writeInt(st, Byte(0)); // version
248 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
249 utils.writeInt(st, Byte(mCounter));
250 // Òåêóùèé êàäð
251 utils.writeInt(st, LongInt(mCurrentFrame));
252 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
253 utils.writeBool(st, mPlayed);
254 // Alpha-êàíàë âñåé òåêñòóðû
255 utils.writeInt(st, Byte(mAlpha));
256 // Ðàçìûòèå òåêñòóðû
257 utils.writeInt(st, Byte(mBlending));
258 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
259 utils.writeInt(st, Byte(mSpeed));
260 // Çàöèêëåíà ëè àíèìàöèÿ
261 utils.writeBool(st, mLoop);
262 // Âêëþ÷åíà ëè
263 utils.writeBool(st, mEnabled);
264 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
265 utils.writeInt(st, Byte(mMinLength));
266 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
267 utils.writeBool(st, mRevert);
268 end;
271 procedure TAnimationState.loadState (st: TStream);
272 begin
273 if (st = nil) then exit;
275 if not utils.checkSign(st, 'ANIM') then raise XStreamError.Create('animation chunk expected');
276 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid animation chunk version');
277 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
278 mCounter := utils.readByte(st);
279 // Òåêóùèé êàäð
280 mCurrentFrame := utils.readLongInt(st);
281 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
282 mPlayed := utils.readBool(st);
283 // Alpha-êàíàë âñåé òåêñòóðû
284 mAlpha := utils.readByte(st);
285 // Ðàçìûòèå òåêñòóðû
286 mBlending := utils.readBool(st);
287 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
288 mSpeed := utils.readByte(st);
289 // Çàöèêëåíà ëè àíèìàöèÿ
290 mLoop := utils.readBool(st);
291 // Âêëþ÷åíà ëè
292 mEnabled := utils.readBool(st);
293 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
294 mMinLength := utils.readByte(st);
295 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
296 mRevert := utils.readBool(st);
297 end;
304 constructor TAnimation.Create (aframesID: LongWord; aloop: Boolean; aspeed: Byte);
305 begin
306 if (aframesID >= Length(framesArray)) then
307 begin
308 //raise Exception.Create('trying to create inexisting frame: something is very wrong here');
309 e_LogWritefln('trying to create inexisting frame %u of %u: something is very wrong here', [aframesID, LongWord(Length(framesArray))], TMsgType.Warning);
310 aframesID := 0;
311 if (Length(framesArray) = 0) then raise Exception.Create('trying to create inexisting frame: something is very wrong here');
312 end;
313 mId := aframesID;
314 mMinLength := 0;
315 mLoop := aloop;
316 mSpeed := aspeed;
317 mEnabled := true;
318 mCurrentFrame := 0;
319 mPlayed := false;
320 mAlpha := 0;
321 mWidth := framesArray[mId].FrameWidth;
322 mHeight := framesArray[mId].FrameHeight;
323 end;
326 destructor TAnimation.Destroy ();
327 begin
328 inherited;
329 end;
332 procedure TAnimation.update ();
333 begin
334 if (not mEnabled) then exit;
336 mCounter += 1;
338 if (mCounter >= mSpeed) then
339 begin
340 // Îæèäàíèå ìåæäó êàäðàìè çàêîí÷èëîñü
341 // Îáðàòíûé ïîðÿäîê êàäðîâ?
342 if mRevert then
343 begin
344 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
345 if (mCurrentFrame = 0) then
346 begin
347 if (Length(framesArray[mId].TexturesID)*mSpeed+mCounter < mMinLength) then exit;
348 end;
350 mCurrentFrame -= 1;
351 mPlayed := (mCurrentFrame < 0);
353 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
354 if mPlayed then
355 begin
356 if mLoop then mCurrentFrame := High(framesArray[mId].TexturesID) else mCurrentFrame += 1;
357 end;
359 mCounter := 0;
360 end
361 else
362 begin
363 // Ïðÿìîé ïîðÿäîê êàäðîâ
364 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
365 if (mCurrentFrame = High(framesArray[mId].TexturesID)) then
366 begin
367 if (Length(framesArray[mId].TexturesID)*mSpeed+mCounter < mMinLength) then exit;
368 end;
370 mCurrentFrame += 1;
371 mPlayed := (mCurrentFrame > High(framesArray[mId].TexturesID));
373 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
374 if mPlayed then
375 begin
376 if mLoop then mCurrentFrame := 0 else mCurrentFrame -= 1;
377 end;
379 mCounter := 0;
380 end;
381 end;
382 end;
385 procedure TAnimation.reset ();
386 begin
387 if mRevert then mCurrentFrame := High(framesArray[mId].TexturesID) else mCurrentFrame := 0;
388 mCounter := 0;
389 mPlayed := false;
390 end;
393 procedure TAnimation.disable (); begin mEnabled := false; end;
394 procedure TAnimation.enable (); begin mEnabled := true; end;
397 function TAnimation.totalFrames (): Integer; inline; begin result := Length(framesArray[mId].TexturesID); end;
400 procedure TAnimation.revert (r: Boolean);
401 begin
402 mRevert := r;
403 reset();
404 end;
407 procedure TAnimation.saveState (st: TStream);
408 begin
409 if (st = nil) then exit;
411 utils.writeSign(st, 'ANIM');
412 utils.writeInt(st, Byte(0)); // version
413 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
414 utils.writeInt(st, Byte(mCounter));
415 // Òåêóùèé êàäð
416 utils.writeInt(st, LongInt(mCurrentFrame));
417 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
418 utils.writeBool(st, mPlayed);
419 // Alpha-êàíàë âñåé òåêñòóðû
420 utils.writeInt(st, Byte(mAlpha));
421 // Ðàçìûòèå òåêñòóðû
422 utils.writeInt(st, Byte(mBlending));
423 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
424 utils.writeInt(st, Byte(mSpeed));
425 // Çàöèêëåíà ëè àíèìàöèÿ
426 utils.writeBool(st, mLoop);
427 // Âêëþ÷åíà ëè
428 utils.writeBool(st, mEnabled);
429 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
430 utils.writeInt(st, Byte(mMinLength));
431 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
432 utils.writeBool(st, mRevert);
433 end;
436 procedure TAnimation.loadState (st: TStream);
437 begin
438 if (st = nil) then exit;
440 if not utils.checkSign(st, 'ANIM') then raise XStreamError.Create('animation chunk expected');
441 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid animation chunk version');
442 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
443 mCounter := utils.readByte(st);
444 // Òåêóùèé êàäð
445 mCurrentFrame := utils.readLongInt(st);
446 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
447 mPlayed := utils.readBool(st);
448 // Alpha-êàíàë âñåé òåêñòóðû
449 mAlpha := utils.readByte(st);
450 // Ðàçìûòèå òåêñòóðû
451 mBlending := utils.readBool(st);
452 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
453 mSpeed := utils.readByte(st);
454 // Çàöèêëåíà ëè àíèìàöèÿ
455 mLoop := utils.readBool(st);
456 // Âêëþ÷åíà ëè
457 mEnabled := utils.readBool(st);
458 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
459 mMinLength := utils.readByte(st);
460 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
461 mRevert := utils.readBool(st);
462 end;
464 end.