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 - 1])
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);
81 procedure g_Anim_GetInterplatedFrameFromState (const s
: TAnimState
; newlength
: LongInt; out frame
: LongInt);
82 procedure g_Anim_GetTimeFromState (const s
: TAnimState
; out curtime
, fulltime
: LongInt);
86 uses Math
, utils
, xstreams
;
88 constructor TAnimState
.Create (aloop
: Boolean; aspeed
: Byte; len
: Integer);
92 self
:= Default(TAnimState
);
96 self
.mSpeed
:= aspeed
;
97 self
.mEnabled
:= true;
98 self
.mCurrentFrame
:= 0;
99 self
.mPlayed
:= false;
102 procedure TAnimState
.Invalidate
;
104 self
:= Default(TAnimState
);
107 procedure TAnimState
.Update
;
109 ASSERT(self
.IsValid());
110 if self
.mEnabled
then
113 if self
.mCounter
>= self
.mSpeed
then
117 if (self
.mCurrentFrame
<> 0) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
119 DEC(self
.mCurrentFrame
);
120 self
.mPlayed
:= self
.mCurrentFrame
< 0;
123 if self
.mLoop
then self
.mCurrentFrame
:= self
.mLength
- 1 else INC(self
.mCurrentFrame
);
130 if (self
.mCurrentFrame
<> self
.mLength
- 1) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
132 INC(self
.mCurrentFrame
);
133 self
.mPlayed
:= self
.mCurrentFrame
> self
.mLength
- 1;
136 if self
.mLoop
then self
.mCurrentFrame
:= 0 else DEC(self
.mCurrentFrame
);
145 procedure TAnimState
.Reset
;
147 ASSERT(self
.IsValid());
148 if self
.mRevert
then self
.mCurrentFrame
:= self
.mLength
- 1 else self
.mCurrentFrame
:= 0;
150 self
.mPlayed
:= false;
153 procedure TAnimState
.Disable
;
155 ASSERT(self
.IsValid());
156 self
.mEnabled
:= false;
159 procedure TAnimState
.Enable
;
161 ASSERT(self
.IsValid());
162 self
.mEnabled
:= true;
165 procedure TAnimState
.revert (r
: Boolean);
167 ASSERT(self
.IsValid());
172 function TAnimState
.TotalFrames (): Integer;
174 ASSERT(self
.IsValid());
175 result
:= self
.mLength
;
178 function TAnimState
.IsInvalid (): Boolean;
180 result
:= self
.mLength
<= 0
183 function TAnimState
.IsValid (): Boolean;
185 result
:= self
.mLength
> 0;
188 procedure TAnimState
.SaveState (st
: TStream
; mAlpha
: Byte; mBlending
: Boolean);
192 utils
.writeSign(st
, 'ANIM');
193 utils
.writeInt(st
, Byte(0)); // version
194 utils
.writeInt(st
, Byte(mCounter
));
195 utils
.writeInt(st
, LongInt(mCurrentFrame
));
196 utils
.writeBool(st
, mPlayed
);
197 utils
.writeInt(st
, Byte(mAlpha
));
198 utils
.writeInt(st
, Byte(mBlending
));
199 utils
.writeInt(st
, Byte(mSpeed
));
200 utils
.writeBool(st
, mLoop
);
201 utils
.writeBool(st
, mEnabled
);
202 utils
.writeInt(st
, Byte(mMinLength
));
203 utils
.writeBool(st
, mRevert
);
207 procedure TAnimState
.LoadState (st
: TStream
; out mAlpha
: Byte; out mBlending
: Boolean);
211 if utils
.checkSign(st
, 'ANIM') = false then
212 raise XStreamError
.Create('animation chunk expected');
213 if utils
.readByte(st
) <> 0 then
214 raise XStreamError
.Create('invalid animation chunk version');
215 mCounter
:= utils
.readByte(st
);
216 mCurrentFrame
:= utils
.readLongInt(st
);
217 mPlayed
:= utils
.readBool(st
);
218 mAlpha
:= utils
.readByte(st
);
219 mBlending
:= utils
.readBool(st
);
220 mSpeed
:= utils
.readByte(st
);
221 mLoop
:= utils
.readBool(st
);
222 mEnabled
:= utils
.readBool(st
);
223 mMinLength
:= utils
.readByte(st
);
224 mRevert
:= utils
.readBool(st
);
228 function g_Anim_GetTotalFrames (const a
: TAnimInfo
): LongWord;
230 ASSERT(a
.frames
> 0);
232 if a
.back
then result
:= MAX(1, a
.frames
* 2 - 2) else result
:= a
.frames
;
235 function g_Anim_GetTotalTime (const a
: TAnimInfo
): LongWord;
237 ASSERT(a
.frames
> 0);
239 result
:= g_Anim_GetTotalFrames(a
) * a
.delay
;
242 function g_Anim_GetCountByTime (const a
: TAnimInfo
; time
: LongWord): LongInt;
243 var n
, f
, t
: LongWord;
245 ASSERT(a
.frames
> 0);
247 n
:= g_Anim_GetTotalFrames(a
);
248 t
:= g_Anim_GetTotalTime(a
);
250 if a
.loop
then result
:= f
div n
251 else if f
>= n
then result
:= 1
255 procedure g_Anim_GetFrameByTime (const a
: TAnimInfo
; time
: LongWord; out count
, frame
: LongInt);
256 var n
, f
, t
: LongWord;
258 ASSERT(a
.frames
> 0);
260 (* 1. Get total number frames for one animation cycle *)
261 n
:= g_Anim_GetTotalFrames(a
);
262 (* 2. Get time for one animation cycle *)
263 t
:= g_Anim_GetTotalTime(a
);
264 (* 3. Get frame for specified time *)
266 (* 4. Get how many times is played *)
267 if a
.loop
then count
:= f
div n
268 else if f
>= n
then count
:= 1
270 (* 5. Normalize loop animation *)
271 if a
.loop
then f
:= f
mod n
else f
:= MIN(f
, n
- 1);
272 (* 6. Normalize back animation *)
273 if a
.back
and (f
>= a
.frames
) then f
:= n
- f
;
277 procedure g_Anim_GetState (const anim
: TAnimInfo
; time
: LongWord; out state
: TAnimState
);
278 var count
, frame
: LongInt; a
: TAnimInfo
;
280 ASSERT(anim
.frames
> 0);
281 ASSERT(anim
.delay
> 0);
285 a
.frames
:= MAX(1, a
.frames
* 2 - 2);
288 g_Anim_GetFrameByTime(a
, time
, count
, frame
);
289 state
:= TAnimState
.Create(a
.loop
, a
.delay
, a
.frames
);
290 state
.mCounter
:= time
MOD a
.delay
;
291 state
.mCurrentFrame
:= frame
;
292 state
.mPlayed
:= count
>= 1;
295 procedure g_Anim_GetFrameFromState (const s
: TAnimState
; backanim
: Boolean; out frame
: LongInt);
298 ASSERT(s
.length
> 0);
299 frame
:= s
.CurrentFrame
mod s
.length
;
302 total
:= (s
.length
+ 1) div 2;
303 if frame
>= total
then
304 frame
:= s
.length
- frame
- 1;
308 procedure g_Anim_GetInterplatedFrameFromState (const s
: TAnimState
; newlength
: LongInt; out frame
: LongInt);
309 var delay
, curframe
, curcount
, fulltime
, curtime
, newtime
: LongInt;
311 ASSERT(s
.length
> 0);
312 ASSERT(newlength
> 0);
313 (* 1. normalize state values *)
314 delay
:= MAX(1, s
.speed
);
315 curframe
:= MIN(MAX(s
.CurrentFrame
, 0), s
.length
- 1);
316 curcount
:= MIN(MAX(s
.CurrentCounter
, 0), delay
- 1);
317 (* 2. calc current time (normalized) *)
318 fulltime
:= s
.length
* delay
;
319 curtime
:= MIN(curframe
* delay
+ curcount
, fulltime
);
320 (* 3. calc interpolated frame *)
321 newtime
:= curtime
* newlength
div s
.length
;
322 frame
:= newtime
div delay
;
324 ASSERT(frame
< newlength
);
327 procedure g_Anim_GetTimeFromState (const s
: TAnimState
; out curtime
, fulltime
: LongInt);
328 var delay
, curframe
, curcount
: LongInt;
330 ASSERT(s
.length
> 0);
331 (* 1. normalize state values *)
332 delay
:= MAX(1, s
.speed
);
333 curframe
:= MIN(MAX(s
.CurrentFrame
, 0), s
.length
- 1);
334 curcount
:= MIN(MAX(s
.CurrentCounter
, 0), delay
- 1);
335 (* 2. calc current time (normalized) *)
336 fulltime
:= s
.length
* delay
;
337 curtime
:= MIN(curframe
* delay
+ curcount
, fulltime
);