1 (* Copyright (C) Doom 2D: Forever Developers
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.
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.
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/>.
15 {$INCLUDE ../shared/a_modes.inc}
25 mCounter
: Byte; // delay counter (normally [0..mSpeed])
26 mSpeed
: Byte; // delay between frames
27 mCurrentFrame
: Integer; // current frame (normally [0..mLength - 1])
28 mLoop
: Boolean; // looped animation
29 mEnabled
: Boolean; // allow update state
30 mPlayed
: Boolean; // anmation played at least once
31 mMinLength
: Byte; // delay at animation end
32 mRevert
: Boolean; // reverse play
33 mLength
: Integer; // total frames (normally mLength > 0)
36 constructor Create (aloop
: Boolean; aspeed
: Byte; len
: Integer);
43 procedure Revert (r
: Boolean);
45 procedure SaveState (st
: TStream
; mAlpha
: Byte; mBlending
: Boolean);
46 procedure LoadState (st
: TStream
; out mAlpha
: Byte; out mBlending
: Boolean);
48 function TotalFrames (): Integer; inline;
49 function IsInvalid (): Boolean; inline;
50 function IsValid (): Boolean; inline;
53 property played
: Boolean read mPlayed
;
54 property enabled
: Boolean read mEnabled
;
55 property isReverse
: Boolean read mRevert
;
56 property loop
: Boolean read mLoop write mLoop
;
57 property speed
: Byte read mSpeed write mSpeed
;
58 property minLength
: Byte read mMinLength write mMinLength
;
59 property currentFrame
: Integer read mCurrentFrame write mCurrentFrame
;
60 property currentCounter
: Byte read mCounter write mCounter
;
61 property counter
: Byte read mCounter
;
62 property length
: Integer read mLength
;
67 loop
: Boolean; (* loop animation normalization *)
68 delay
: Byte; (* delay between frames [1..255] *)
69 frames
: Word; (* number of frames in animation stream [1..65535] *)
70 back
: Boolean; (* back animation normalization *)
74 function g_Anim_GetTotalFrames (const a
: TAnimInfo
): LongWord;
75 function g_Anim_GetTotalTime (const a
: TAnimInfo
): LongWord;
76 function g_Anim_GetCountByTime (const a
: TAnimInfo
; time
: LongWord): LongInt;
77 procedure g_Anim_GetFrameByTime (const a
: TAnimInfo
; time
: LongWord; out count
, frame
: LongInt);
78 procedure g_Anim_GetState (const anim
: TAnimInfo
; time
: LongWord; out state
: TAnimState
);
80 procedure g_Anim_GetFrameFromState (const s
: TAnimState
; backanim
: Boolean; out frame
: LongInt);
84 uses Math
, utils
, xstreams
;
86 constructor TAnimState
.Create (aloop
: Boolean; aspeed
: Byte; len
: Integer);
90 self
:= Default(TAnimState
);
94 self
.mSpeed
:= aspeed
;
95 self
.mEnabled
:= true;
96 self
.mCurrentFrame
:= 0;
97 self
.mPlayed
:= false;
100 procedure TAnimState
.Invalidate
;
102 self
:= Default(TAnimState
);
105 procedure TAnimState
.Update
;
107 ASSERT(self
.IsValid());
108 if self
.mEnabled
then
111 if self
.mCounter
>= self
.mSpeed
then
115 if (self
.mCurrentFrame
<> 0) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
117 DEC(self
.mCurrentFrame
);
118 self
.mPlayed
:= self
.mCurrentFrame
< 0;
121 if self
.mLoop
then self
.mCurrentFrame
:= self
.mLength
- 1 else INC(self
.mCurrentFrame
);
128 if (self
.mCurrentFrame
<> self
.mLength
- 1) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
130 INC(self
.mCurrentFrame
);
131 self
.mPlayed
:= self
.mCurrentFrame
> self
.mLength
- 1;
134 if self
.mLoop
then self
.mCurrentFrame
:= 0 else DEC(self
.mCurrentFrame
);
143 procedure TAnimState
.Reset
;
145 ASSERT(self
.IsValid());
146 if self
.mRevert
then self
.mCurrentFrame
:= self
.mLength
- 1 else self
.mCurrentFrame
:= 0;
148 self
.mPlayed
:= false;
151 procedure TAnimState
.Disable
;
153 ASSERT(self
.IsValid());
154 self
.mEnabled
:= false;
157 procedure TAnimState
.Enable
;
159 ASSERT(self
.IsValid());
160 self
.mEnabled
:= true;
163 procedure TAnimState
.revert (r
: Boolean);
165 ASSERT(self
.IsValid());
170 function TAnimState
.TotalFrames (): Integer;
172 ASSERT(self
.IsValid());
173 result
:= self
.mLength
;
176 function TAnimState
.IsInvalid (): Boolean;
178 result
:= self
.mLength
<= 0
181 function TAnimState
.IsValid (): Boolean;
183 result
:= self
.mLength
> 0;
186 procedure TAnimState
.SaveState (st
: TStream
; mAlpha
: Byte; mBlending
: Boolean);
190 utils
.writeSign(st
, 'ANIM');
191 utils
.writeInt(st
, Byte(0)); // version
192 utils
.writeInt(st
, Byte(mCounter
));
193 utils
.writeInt(st
, LongInt(mCurrentFrame
));
194 utils
.writeBool(st
, mPlayed
);
195 utils
.writeInt(st
, Byte(mAlpha
));
196 utils
.writeInt(st
, Byte(mBlending
));
197 utils
.writeInt(st
, Byte(mSpeed
));
198 utils
.writeBool(st
, mLoop
);
199 utils
.writeBool(st
, mEnabled
);
200 utils
.writeInt(st
, Byte(mMinLength
));
201 utils
.writeBool(st
, mRevert
);
205 procedure TAnimState
.LoadState (st
: TStream
; out mAlpha
: Byte; out mBlending
: Boolean);
209 if utils
.checkSign(st
, 'ANIM') = false then
210 raise XStreamError
.Create('animation chunk expected');
211 if utils
.readByte(st
) <> 0 then
212 raise XStreamError
.Create('invalid animation chunk version');
213 mCounter
:= utils
.readByte(st
);
214 mCurrentFrame
:= utils
.readLongInt(st
);
215 mPlayed
:= utils
.readBool(st
);
216 mAlpha
:= utils
.readByte(st
);
217 mBlending
:= utils
.readBool(st
);
218 mSpeed
:= utils
.readByte(st
);
219 mLoop
:= utils
.readBool(st
);
220 mEnabled
:= utils
.readBool(st
);
221 mMinLength
:= utils
.readByte(st
);
222 mRevert
:= utils
.readBool(st
);
226 function g_Anim_GetTotalFrames (const a
: TAnimInfo
): LongWord;
228 ASSERT(a
.frames
> 0);
230 if a
.back
then result
:= MAX(1, a
.frames
* 2 - 2) else result
:= a
.frames
;
233 function g_Anim_GetTotalTime (const a
: TAnimInfo
): LongWord;
235 ASSERT(a
.frames
> 0);
237 result
:= g_Anim_GetTotalFrames(a
) * a
.delay
;
240 function g_Anim_GetCountByTime (const a
: TAnimInfo
; time
: LongWord): LongInt;
241 var n
, f
, t
: LongWord;
243 ASSERT(a
.frames
> 0);
245 n
:= g_Anim_GetTotalFrames(a
);
246 t
:= g_Anim_GetTotalTime(a
);
248 if a
.loop
then result
:= f
div n
249 else if f
>= n
then result
:= 1
253 procedure g_Anim_GetFrameByTime (const a
: TAnimInfo
; time
: LongWord; out count
, frame
: LongInt);
254 var n
, f
, t
: LongWord;
256 ASSERT(a
.frames
> 0);
258 (* 1. Get total number frames for one animation cycle *)
259 n
:= g_Anim_GetTotalFrames(a
);
260 (* 2. Get time for one animation cycle *)
261 t
:= g_Anim_GetTotalTime(a
);
262 (* 3. Get frame for specified time *)
264 (* 4. Get how many times is played *)
265 if a
.loop
then count
:= f
div n
266 else if f
>= n
then count
:= 1
268 (* 5. Normalize loop animation *)
269 if a
.loop
then f
:= f
mod n
else f
:= MIN(f
, n
- 1);
270 (* 6. Normalize back animation *)
271 if a
.back
and (f
>= a
.frames
) then f
:= n
- f
;
275 procedure g_Anim_GetState (const anim
: TAnimInfo
; time
: LongWord; out state
: TAnimState
);
276 var count
, frame
: LongInt; a
: TAnimInfo
;
278 ASSERT(anim
.frames
> 0);
279 ASSERT(anim
.delay
> 0);
283 a
.frames
:= MAX(1, a
.frames
* 2 - 2);
286 g_Anim_GetFrameByTime(a
, time
, count
, frame
);
287 state
:= TAnimState
.Create(a
.loop
, a
.delay
, a
.frames
);
288 state
.mCounter
:= time
MOD a
.delay
;
289 state
.mCurrentFrame
:= frame
;
290 state
.mPlayed
:= count
>= 1;
293 procedure g_Anim_GetFrameFromState (const s
: TAnimState
; backanim
: Boolean; out frame
: LongInt);
296 ASSERT(s
.length
> 0);
297 frame
:= s
.CurrentFrame
mod s
.length
;
300 total
:= (s
.length
+ 1) div 2;
301 if frame
>= total
then
302 frame
:= s
.length
- frame
- 1;