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);
85 uses Math
, utils
, xstreams
;
87 constructor TAnimState
.Create (aloop
: Boolean; aspeed
: Byte; len
: Integer);
91 self
:= Default(TAnimState
);
95 self
.mSpeed
:= aspeed
;
96 self
.mEnabled
:= true;
97 self
.mCurrentFrame
:= 0;
98 self
.mPlayed
:= false;
101 procedure TAnimState
.Invalidate
;
103 self
:= Default(TAnimState
);
106 procedure TAnimState
.Update
;
108 ASSERT(self
.IsValid());
109 if self
.mEnabled
then
112 if self
.mCounter
>= self
.mSpeed
then
116 if (self
.mCurrentFrame
<> 0) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
118 DEC(self
.mCurrentFrame
);
119 self
.mPlayed
:= self
.mCurrentFrame
< 0;
122 if self
.mLoop
then self
.mCurrentFrame
:= self
.mLength
- 1 else INC(self
.mCurrentFrame
);
129 if (self
.mCurrentFrame
<> self
.mLength
- 1) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
131 INC(self
.mCurrentFrame
);
132 self
.mPlayed
:= self
.mCurrentFrame
> self
.mLength
- 1;
135 if self
.mLoop
then self
.mCurrentFrame
:= 0 else DEC(self
.mCurrentFrame
);
144 procedure TAnimState
.Reset
;
146 ASSERT(self
.IsValid());
147 if self
.mRevert
then self
.mCurrentFrame
:= self
.mLength
- 1 else self
.mCurrentFrame
:= 0;
149 self
.mPlayed
:= false;
152 procedure TAnimState
.Disable
;
154 ASSERT(self
.IsValid());
155 self
.mEnabled
:= false;
158 procedure TAnimState
.Enable
;
160 ASSERT(self
.IsValid());
161 self
.mEnabled
:= true;
164 procedure TAnimState
.revert (r
: Boolean);
166 ASSERT(self
.IsValid());
171 function TAnimState
.TotalFrames (): Integer;
173 ASSERT(self
.IsValid());
174 result
:= self
.mLength
;
177 function TAnimState
.IsInvalid (): Boolean;
179 result
:= self
.mLength
<= 0
182 function TAnimState
.IsValid (): Boolean;
184 result
:= self
.mLength
> 0;
187 procedure TAnimState
.SaveState (st
: TStream
; mAlpha
: Byte; mBlending
: Boolean);
191 utils
.writeSign(st
, 'ANIM');
192 utils
.writeInt(st
, Byte(0)); // version
193 utils
.writeInt(st
, Byte(mCounter
));
194 utils
.writeInt(st
, LongInt(mCurrentFrame
));
195 utils
.writeBool(st
, mPlayed
);
196 utils
.writeInt(st
, Byte(mAlpha
));
197 utils
.writeInt(st
, Byte(mBlending
));
198 utils
.writeInt(st
, Byte(mSpeed
));
199 utils
.writeBool(st
, mLoop
);
200 utils
.writeBool(st
, mEnabled
);
201 utils
.writeInt(st
, Byte(mMinLength
));
202 utils
.writeBool(st
, mRevert
);
206 procedure TAnimState
.LoadState (st
: TStream
; out mAlpha
: Byte; out mBlending
: Boolean);
210 if utils
.checkSign(st
, 'ANIM') = false then
211 raise XStreamError
.Create('animation chunk expected');
212 if utils
.readByte(st
) <> 0 then
213 raise XStreamError
.Create('invalid animation chunk version');
214 mCounter
:= utils
.readByte(st
);
215 mCurrentFrame
:= utils
.readLongInt(st
);
216 mPlayed
:= utils
.readBool(st
);
217 mAlpha
:= utils
.readByte(st
);
218 mBlending
:= utils
.readBool(st
);
219 mSpeed
:= utils
.readByte(st
);
220 mLoop
:= utils
.readBool(st
);
221 mEnabled
:= utils
.readBool(st
);
222 mMinLength
:= utils
.readByte(st
);
223 mRevert
:= utils
.readBool(st
);
227 function g_Anim_GetTotalFrames (const a
: TAnimInfo
): LongWord;
229 ASSERT(a
.frames
> 0);
231 if a
.back
then result
:= MAX(1, a
.frames
* 2 - 2) else result
:= a
.frames
;
234 function g_Anim_GetTotalTime (const a
: TAnimInfo
): LongWord;
236 ASSERT(a
.frames
> 0);
238 result
:= g_Anim_GetTotalFrames(a
) * a
.delay
;
241 function g_Anim_GetCountByTime (const a
: TAnimInfo
; time
: LongWord): LongInt;
242 var n
, f
, t
: LongWord;
244 ASSERT(a
.frames
> 0);
246 n
:= g_Anim_GetTotalFrames(a
);
247 t
:= g_Anim_GetTotalTime(a
);
249 if a
.loop
then result
:= f
div n
250 else if f
>= n
then result
:= 1
254 procedure g_Anim_GetFrameByTime (const a
: TAnimInfo
; time
: LongWord; out count
, frame
: LongInt);
255 var n
, f
, t
: LongWord;
257 ASSERT(a
.frames
> 0);
259 (* 1. Get total number frames for one animation cycle *)
260 n
:= g_Anim_GetTotalFrames(a
);
261 (* 2. Get time for one animation cycle *)
262 t
:= g_Anim_GetTotalTime(a
);
263 (* 3. Get frame for specified time *)
265 (* 4. Get how many times is played *)
266 if a
.loop
then count
:= f
div n
267 else if f
>= n
then count
:= 1
269 (* 5. Normalize loop animation *)
270 if a
.loop
then f
:= f
mod n
else f
:= MIN(f
, n
- 1);
271 (* 6. Normalize back animation *)
272 if a
.back
and (f
>= a
.frames
) then f
:= n
- f
;
276 procedure g_Anim_GetState (const anim
: TAnimInfo
; time
: LongWord; out state
: TAnimState
);
277 var count
, frame
: LongInt; a
: TAnimInfo
;
279 ASSERT(anim
.frames
> 0);
280 ASSERT(anim
.delay
> 0);
284 a
.frames
:= MAX(1, a
.frames
* 2 - 2);
287 g_Anim_GetFrameByTime(a
, time
, count
, frame
);
288 state
:= TAnimState
.Create(a
.loop
, a
.delay
, a
.frames
);
289 state
.mCounter
:= time
MOD a
.delay
;
290 state
.mCurrentFrame
:= frame
;
291 state
.mPlayed
:= count
>= 1;
294 procedure g_Anim_GetFrameFromState (const s
: TAnimState
; backanim
: Boolean; out frame
: LongInt);
297 ASSERT(s
.length
> 0);
298 frame
:= s
.CurrentFrame
mod s
.length
;
301 total
:= (s
.length
+ 1) div 2;
302 if frame
>= total
then
303 frame
:= s
.length
- frame
- 1;
307 procedure g_Anim_GetInterplatedFrameFromState (const s
: TAnimState
; newlength
: LongInt; out frame
: LongInt);
308 var delay
, curframe
, curcount
, fulltime
, curtime
, newtime
: LongInt;
310 ASSERT(s
.length
> 0);
311 ASSERT(newlength
> 0);
312 (* 1. normalize state values *)
313 delay
:= MAX(1, s
.speed
);
314 curframe
:= MIN(MAX(s
.CurrentFrame
, 0), s
.length
- 1);
315 curcount
:= MIN(MAX(s
.CurrentCounter
, 0), delay
- 1);
316 (* 2. calc current time (normalized) *)
317 fulltime
:= s
.length
* delay
;
318 curtime
:= MIN(curframe
* delay
+ curcount
, fulltime
);
319 (* 3. calc interpolated frame *)
320 newtime
:= curtime
* newlength
div s
.length
;
321 frame
:= newtime
div delay
;
323 ASSERT(frame
< newlength
);