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 *)
73 function g_Anim_GetTotalFrames (const a
: TAnimInfo
): LongWord;
74 function g_Anim_GetTotalTime (const a
: TAnimInfo
): LongWord;
75 function g_Anim_GetCountByTime (const a
: TAnimInfo
; time
: LongWord): LongInt;
76 procedure g_Anim_GetFrameByTime (const a
: TAnimInfo
; time
: LongWord; out count
, frame
: LongInt);
77 procedure g_Anim_GetState (const anim
: TAnimInfo
; time
: LongWord; out state
: TAnimState
);
81 uses Math
, utils
, xstreams
;
83 constructor TAnimState
.Create (aloop
: Boolean; aspeed
: Byte; len
: Integer);
87 self
:= Default(TAnimState
);
91 self
.mSpeed
:= aspeed
;
92 self
.mEnabled
:= true;
93 self
.mCurrentFrame
:= 0;
94 self
.mPlayed
:= false;
97 procedure TAnimState
.Invalidate
;
99 self
:= Default(TAnimState
);
102 procedure TAnimState
.Update
;
104 ASSERT(self
.IsValid());
105 if self
.mEnabled
then
108 if self
.mCounter
>= self
.mSpeed
then
112 if (self
.mCurrentFrame
<> 0) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
114 DEC(self
.mCurrentFrame
);
115 self
.mPlayed
:= self
.mCurrentFrame
< 0;
118 if self
.mLoop
then self
.mCurrentFrame
:= self
.mLength
- 1 else INC(self
.mCurrentFrame
);
125 if (self
.mCurrentFrame
<> self
.mLength
- 1) or (mLength
* mSpeed
+ mCounter
>= mMinLength
) then
127 INC(self
.mCurrentFrame
);
128 self
.mPlayed
:= self
.mCurrentFrame
> self
.mLength
- 1;
131 if self
.mLoop
then self
.mCurrentFrame
:= 0 else DEC(self
.mCurrentFrame
);
140 procedure TAnimState
.Reset
;
142 ASSERT(self
.IsValid());
143 if self
.mRevert
then self
.mCurrentFrame
:= self
.mLength
- 1 else self
.mCurrentFrame
:= 0;
145 self
.mPlayed
:= false;
148 procedure TAnimState
.Disable
;
150 ASSERT(self
.IsValid());
151 self
.mEnabled
:= false;
154 procedure TAnimState
.Enable
;
156 ASSERT(self
.IsValid());
157 self
.mEnabled
:= true;
160 procedure TAnimState
.revert (r
: Boolean);
162 ASSERT(self
.IsValid());
167 function TAnimState
.TotalFrames (): Integer;
169 ASSERT(self
.IsValid());
170 result
:= self
.mLength
;
173 function TAnimState
.IsInvalid (): Boolean;
175 result
:= self
.mLength
<= 0
178 function TAnimState
.IsValid (): Boolean;
180 result
:= self
.mLength
> 0;
183 procedure TAnimState
.SaveState (st
: TStream
; mAlpha
: Byte; mBlending
: Boolean);
187 utils
.writeSign(st
, 'ANIM');
188 utils
.writeInt(st
, Byte(0)); // version
189 utils
.writeInt(st
, Byte(mCounter
));
190 utils
.writeInt(st
, LongInt(mCurrentFrame
));
191 utils
.writeBool(st
, mPlayed
);
192 utils
.writeInt(st
, Byte(mAlpha
));
193 utils
.writeInt(st
, Byte(mBlending
));
194 utils
.writeInt(st
, Byte(mSpeed
));
195 utils
.writeBool(st
, mLoop
);
196 utils
.writeBool(st
, mEnabled
);
197 utils
.writeInt(st
, Byte(mMinLength
));
198 utils
.writeBool(st
, mRevert
);
202 procedure TAnimState
.LoadState (st
: TStream
; out mAlpha
: Byte; out mBlending
: Boolean);
206 if utils
.checkSign(st
, 'ANIM') = false then
207 raise XStreamError
.Create('animation chunk expected');
208 if utils
.readByte(st
) <> 0 then
209 raise XStreamError
.Create('invalid animation chunk version');
210 mCounter
:= utils
.readByte(st
);
211 mCurrentFrame
:= utils
.readLongInt(st
);
212 mPlayed
:= utils
.readBool(st
);
213 mAlpha
:= utils
.readByte(st
);
214 mBlending
:= utils
.readBool(st
);
215 mSpeed
:= utils
.readByte(st
);
216 mLoop
:= utils
.readBool(st
);
217 mEnabled
:= utils
.readBool(st
);
218 mMinLength
:= utils
.readByte(st
);
219 mRevert
:= utils
.readBool(st
);
223 function g_Anim_GetTotalFrames (const a
: TAnimInfo
): LongWord;
225 ASSERT(a
.frames
> 0);
227 if a
.back
then result
:= MAX(1, a
.frames
* 2 - 2) else result
:= a
.frames
;
230 function g_Anim_GetTotalTime (const a
: TAnimInfo
): LongWord;
232 ASSERT(a
.frames
> 0);
234 result
:= g_Anim_GetTotalFrames(a
) * a
.delay
;
237 function g_Anim_GetCountByTime (const a
: TAnimInfo
; time
: LongWord): LongInt;
238 var n
, f
, t
: LongWord;
240 ASSERT(a
.frames
> 0);
242 n
:= g_Anim_GetTotalFrames(a
);
243 t
:= g_Anim_GetTotalTime(a
);
245 if a
.loop
then result
:= f
div n
246 else if f
>= n
then result
:= 1
250 procedure g_Anim_GetFrameByTime (const a
: TAnimInfo
; time
: LongWord; out count
, frame
: LongInt);
251 var n
, f
, t
: LongWord;
253 ASSERT(a
.frames
> 0);
255 (* 1. Get total number frames for one animation cycle *)
256 n
:= g_Anim_GetTotalFrames(a
);
257 (* 2. Get time for one animation cycle *)
258 t
:= g_Anim_GetTotalTime(a
);
259 (* 3. Get frame for specified time *)
261 (* 4. Get how many times is played *)
262 if a
.loop
then count
:= f
div n
263 else if f
>= n
then count
:= 1
265 (* 5. Normalize loop animation *)
266 if a
.loop
then f
:= f
mod n
else f
:= MIN(f
, n
- 1);
267 (* 6. Normalize back animation *)
268 if a
.back
and (f
>= a
.frames
) then f
:= n
- f
;
272 procedure g_Anim_GetState (const anim
: TAnimInfo
; time
: LongWord; out state
: TAnimState
);
273 var count
, frame
: LongInt; a
: TAnimInfo
;
275 ASSERT(anim
.frames
> 0);
276 ASSERT(anim
.delay
> 0);
280 a
.frames
:= MAX(1, a
.frames
* 2 - 2);
283 g_Anim_GetFrameByTime(a
, time
, count
, frame
);
284 state
:= TAnimState
.Create(a
.loop
, a
.delay
, a
.frames
);
285 state
.mCounter
:= time
MOD a
.delay
;
286 state
.mCurrentFrame
:= frame
;
287 state
.mPlayed
:= count
>= 1;