DEADSOFTWARE

render: use TAnimationState for monsters
[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;
28 width, height: Word;
29 case anim: Boolean of
30 false: (textureID: LongWord);
31 true: (framesID: LongWord; framesCount: Byte; speed: Byte);
32 end;
34 TLevelTextureArray = array of TLevelTexture;
36 TAnimationState = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
37 private
38 mAlpha: Byte;
39 mBlending: Boolean;
40 mCounter: Byte; // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
41 mSpeed: Byte; // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
42 mCurrentFrame: Integer; // Òåêóùèé êàäð (íà÷èíàÿ ñ 0)
43 mLoop: Boolean; // Ïåðåõîäèòü íà ïåðâûé êàäð ïîñëå ïîñëåäíåãî?
44 mEnabled: Boolean; // Ðàáîòà ðàçðåøåíà?
45 mPlayed: Boolean; // Ïðîèãðàíà âñÿ õîòÿ áû ðàç?
46 mMinLength: Byte; // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
47 mRevert: Boolean; // Ñìåíà êàäðîâ îáðàòíàÿ?
49 mLength: Integer;
51 public
52 constructor Create (aloop: Boolean; aspeed: Byte; len: Integer);
53 destructor Destroy (); override;
55 procedure reset ();
56 procedure update ();
57 procedure enable ();
58 procedure disable ();
59 procedure revert (r: Boolean);
61 procedure saveState (st: TStream);
62 procedure loadState (st: TStream);
64 function totalFrames (): Integer; inline;
66 public
67 property played: Boolean read mPlayed;
68 property enabled: Boolean read mEnabled;
69 property isReverse: Boolean read mRevert;
70 property loop: Boolean read mLoop write mLoop;
71 property speed: Byte read mSpeed write mSpeed;
72 property minLength: Byte read mMinLength write mMinLength;
73 property currentFrame: Integer read mCurrentFrame write mCurrentFrame;
74 property currentCounter: Byte read mCounter write mCounter;
75 property counter: Byte read mCounter;
76 property blending: Boolean read mBlending write mBlending;
77 property alpha: Byte read mAlpha write mAlpha;
78 end;
80 TAnimation = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
81 private
82 mId: LongWord;
83 mAlpha: Byte;
84 mBlending: Boolean;
85 mCounter: Byte; // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
86 mSpeed: Byte; // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
87 mCurrentFrame: Integer; // Òåêóùèé êàäð (íà÷èíàÿ ñ 0)
88 mLoop: Boolean; // Ïåðåõîäèòü íà ïåðâûé êàäð ïîñëå ïîñëåäíåãî?
89 mEnabled: Boolean; // Ðàáîòà ðàçðåøåíà?
90 mPlayed: Boolean; // Ïðîèãðàíà âñÿ õîòÿ áû ðàç?
91 mHeight: Word;
92 mWidth: Word;
93 mMinLength: Byte; // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
94 mRevert: Boolean; // Ñìåíà êàäðîâ îáðàòíàÿ?
96 public
97 constructor Create (aframesID: LongWord; aloop: Boolean; aspeed: Byte);
98 destructor Destroy (); override;
100 procedure reset ();
101 procedure update ();
102 procedure enable ();
103 procedure disable ();
104 procedure revert (r: Boolean);
106 procedure saveState (st: TStream);
107 procedure loadState (st: TStream);
109 function totalFrames (): Integer; inline;
111 public
112 property played: Boolean read mPlayed;
113 property enabled: Boolean read mEnabled;
114 property isReverse: Boolean read mRevert;
115 property loop: Boolean read mLoop write mLoop;
116 property speed: Byte read mSpeed write mSpeed;
117 property minLength: Byte read mMinLength write mMinLength;
118 property currentFrame: Integer read mCurrentFrame write mCurrentFrame;
119 property currentCounter: Byte read mCounter write mCounter;
120 property counter: Byte read mCounter;
121 property blending: Boolean read mBlending write mBlending;
122 property alpha: Byte read mAlpha write mAlpha;
123 property framesId: LongWord read mId;
124 property width: Word read mWidth;
125 property height: Word read mHeight;
127 property id: LongWord read mId;
128 end;
130 implementation
132 uses
133 g_game, e_log, g_basic, g_console, wadreader, r_animations,
134 g_language, utils, xstreams;
139 constructor TAnimationState.Create (aloop: Boolean; aspeed: Byte; len: Integer);
140 begin
141 assert(len >= 0);
142 mLength := len;
144 mMinLength := 0;
145 mLoop := aloop;
146 mSpeed := aspeed;
147 mEnabled := true;
148 mCurrentFrame := 0;
149 mAlpha := 0;
150 mPlayed := false;
151 end;
153 destructor TAnimationState.Destroy;
154 begin
155 inherited;
156 end;
158 procedure TAnimationState.update;
159 begin
160 if (not mEnabled) then exit;
162 mCounter += 1;
164 if (mCounter >= mSpeed) then
165 begin
166 // Îæèäàíèå ìåæäó êàäðàìè çàêîí÷èëîñü
167 // Îáðàòíûé ïîðÿäîê êàäðîâ?
168 if mRevert then
169 begin
170 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
171 if (mCurrentFrame = 0) then
172 begin
173 if (mLength * mSpeed + mCounter < mMinLength) then exit;
174 end;
176 mCurrentFrame -= 1;
177 mPlayed := (mCurrentFrame < 0);
179 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
180 if mPlayed then
181 begin
182 if mLoop then
183 mCurrentFrame := mLength - 1
184 else
185 mCurrentFrame += 1
186 end;
188 mCounter := 0;
189 end
190 else
191 begin
192 // Ïðÿìîé ïîðÿäîê êàäðîâ
193 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
194 if (mCurrentFrame = mLength - 1) then
195 begin
196 if (mLength * mSpeed + mCounter < mMinLength) then exit;
197 end;
199 mCurrentFrame += 1;
200 mPlayed := (mCurrentFrame > mLength - 1);
202 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
203 if mPlayed then
204 begin
205 if mLoop then mCurrentFrame := 0 else mCurrentFrame -= 1;
206 end;
208 mCounter := 0;
209 end;
210 end;
211 end;
213 procedure TAnimationState.reset;
214 begin
215 if mRevert then
216 mCurrentFrame := mLength - 1
217 else
218 mCurrentFrame := 0;
219 mCounter := 0;
220 mPlayed := false
221 end;
223 procedure TAnimationState.disable;
224 begin
225 mEnabled := false
226 end;
228 procedure TAnimationState.enable;
229 begin
230 mEnabled := true
231 end;
233 procedure TAnimationState.revert (r: Boolean);
234 begin
235 mRevert := r;
236 reset
237 end;
239 function TAnimationState.totalFrames (): Integer; inline;
240 begin
241 result := mLength
242 end;
244 procedure TAnimationState.saveState (st: TStream);
245 begin
246 if (st = nil) then exit;
248 utils.writeSign(st, 'ANIM');
249 utils.writeInt(st, Byte(0)); // version
250 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
251 utils.writeInt(st, Byte(mCounter));
252 // Òåêóùèé êàäð
253 utils.writeInt(st, LongInt(mCurrentFrame));
254 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
255 utils.writeBool(st, mPlayed);
256 // Alpha-êàíàë âñåé òåêñòóðû
257 utils.writeInt(st, Byte(mAlpha));
258 // Ðàçìûòèå òåêñòóðû
259 utils.writeInt(st, Byte(mBlending));
260 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
261 utils.writeInt(st, Byte(mSpeed));
262 // Çàöèêëåíà ëè àíèìàöèÿ
263 utils.writeBool(st, mLoop);
264 // Âêëþ÷åíà ëè
265 utils.writeBool(st, mEnabled);
266 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
267 utils.writeInt(st, Byte(mMinLength));
268 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
269 utils.writeBool(st, mRevert);
270 end;
273 procedure TAnimationState.loadState (st: TStream);
274 begin
275 if (st = nil) then exit;
277 if not utils.checkSign(st, 'ANIM') then raise XStreamError.Create('animation chunk expected');
278 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid animation chunk version');
279 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
280 mCounter := utils.readByte(st);
281 // Òåêóùèé êàäð
282 mCurrentFrame := utils.readLongInt(st);
283 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
284 mPlayed := utils.readBool(st);
285 // Alpha-êàíàë âñåé òåêñòóðû
286 mAlpha := utils.readByte(st);
287 // Ðàçìûòèå òåêñòóðû
288 mBlending := utils.readBool(st);
289 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
290 mSpeed := utils.readByte(st);
291 // Çàöèêëåíà ëè àíèìàöèÿ
292 mLoop := utils.readBool(st);
293 // Âêëþ÷åíà ëè
294 mEnabled := utils.readBool(st);
295 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
296 mMinLength := utils.readByte(st);
297 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
298 mRevert := utils.readBool(st);
299 end;
306 constructor TAnimation.Create (aframesID: LongWord; aloop: Boolean; aspeed: Byte);
307 begin
308 if (aframesID >= Length(framesArray)) then
309 begin
310 //raise Exception.Create('trying to create inexisting frame: something is very wrong here');
311 e_LogWritefln('trying to create inexisting frame %u of %u: something is very wrong here', [aframesID, LongWord(Length(framesArray))], TMsgType.Warning);
312 aframesID := 0;
313 if (Length(framesArray) = 0) then raise Exception.Create('trying to create inexisting frame: something is very wrong here');
314 end;
315 mId := aframesID;
316 mMinLength := 0;
317 mLoop := aloop;
318 mSpeed := aspeed;
319 mEnabled := true;
320 mCurrentFrame := 0;
321 mPlayed := false;
322 mAlpha := 0;
323 mWidth := framesArray[mId].FrameWidth;
324 mHeight := framesArray[mId].FrameHeight;
325 end;
328 destructor TAnimation.Destroy ();
329 begin
330 inherited;
331 end;
334 procedure TAnimation.update ();
335 begin
336 if (not mEnabled) then exit;
338 mCounter += 1;
340 if (mCounter >= mSpeed) then
341 begin
342 // Îæèäàíèå ìåæäó êàäðàìè çàêîí÷èëîñü
343 // Îáðàòíûé ïîðÿäîê êàäðîâ?
344 if mRevert then
345 begin
346 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
347 if (mCurrentFrame = 0) then
348 begin
349 if (Length(framesArray[mId].TexturesID)*mSpeed+mCounter < mMinLength) then exit;
350 end;
352 mCurrentFrame -= 1;
353 mPlayed := (mCurrentFrame < 0);
355 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
356 if mPlayed then
357 begin
358 if mLoop then mCurrentFrame := High(framesArray[mId].TexturesID) else mCurrentFrame += 1;
359 end;
361 mCounter := 0;
362 end
363 else
364 begin
365 // Ïðÿìîé ïîðÿäîê êàäðîâ
366 // Äîøëè äî êîíöà àíèìàöèè. Âîçìîæíî, æäåì åùå
367 if (mCurrentFrame = High(framesArray[mId].TexturesID)) then
368 begin
369 if (Length(framesArray[mId].TexturesID)*mSpeed+mCounter < mMinLength) then exit;
370 end;
372 mCurrentFrame += 1;
373 mPlayed := (mCurrentFrame > High(framesArray[mId].TexturesID));
375 // Ïîâòîðÿòü ëè àíèìàöèþ ïî êðóãó?
376 if mPlayed then
377 begin
378 if mLoop then mCurrentFrame := 0 else mCurrentFrame -= 1;
379 end;
381 mCounter := 0;
382 end;
383 end;
384 end;
387 procedure TAnimation.reset ();
388 begin
389 if mRevert then mCurrentFrame := High(framesArray[mId].TexturesID) else mCurrentFrame := 0;
390 mCounter := 0;
391 mPlayed := false;
392 end;
395 procedure TAnimation.disable (); begin mEnabled := false; end;
396 procedure TAnimation.enable (); begin mEnabled := true; end;
399 function TAnimation.totalFrames (): Integer; inline; begin result := Length(framesArray[mId].TexturesID); end;
402 procedure TAnimation.revert (r: Boolean);
403 begin
404 mRevert := r;
405 reset();
406 end;
409 procedure TAnimation.saveState (st: TStream);
410 begin
411 if (st = nil) then exit;
413 utils.writeSign(st, 'ANIM');
414 utils.writeInt(st, Byte(0)); // version
415 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
416 utils.writeInt(st, Byte(mCounter));
417 // Òåêóùèé êàäð
418 utils.writeInt(st, LongInt(mCurrentFrame));
419 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
420 utils.writeBool(st, mPlayed);
421 // Alpha-êàíàë âñåé òåêñòóðû
422 utils.writeInt(st, Byte(mAlpha));
423 // Ðàçìûòèå òåêñòóðû
424 utils.writeInt(st, Byte(mBlending));
425 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
426 utils.writeInt(st, Byte(mSpeed));
427 // Çàöèêëåíà ëè àíèìàöèÿ
428 utils.writeBool(st, mLoop);
429 // Âêëþ÷åíà ëè
430 utils.writeBool(st, mEnabled);
431 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
432 utils.writeInt(st, Byte(mMinLength));
433 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
434 utils.writeBool(st, mRevert);
435 end;
438 procedure TAnimation.loadState (st: TStream);
439 begin
440 if (st = nil) then exit;
442 if not utils.checkSign(st, 'ANIM') then raise XStreamError.Create('animation chunk expected');
443 if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid animation chunk version');
444 // Ñ÷åò÷èê îæèäàíèÿ ìåæäó êàäðàìè
445 mCounter := utils.readByte(st);
446 // Òåêóùèé êàäð
447 mCurrentFrame := utils.readLongInt(st);
448 // Ïðîèãðàíà ëè àíèìàöèÿ öåëèêîì
449 mPlayed := utils.readBool(st);
450 // Alpha-êàíàë âñåé òåêñòóðû
451 mAlpha := utils.readByte(st);
452 // Ðàçìûòèå òåêñòóðû
453 mBlending := utils.readBool(st);
454 // Âðåìÿ îæèäàíèÿ ìåæäó êàäðàìè
455 mSpeed := utils.readByte(st);
456 // Çàöèêëåíà ëè àíèìàöèÿ
457 mLoop := utils.readBool(st);
458 // Âêëþ÷åíà ëè
459 mEnabled := utils.readBool(st);
460 // Îæèäàíèå ïîñëå ïðîèãðûâàíèÿ
461 mMinLength := utils.readByte(st);
462 // Îáðàòíûé ëè ïîðÿäîê êàäðîâ
463 mRevert := utils.readBool(st);
464 end;
466 end.