624 lines
15 KiB
C
624 lines
15 KiB
C
//Copyright Paul Reiche, Fred Ford. 1992-2002
|
|
|
|
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#define COMM_INTERNAL
|
|
#include "commanim.h"
|
|
|
|
#include "comm.h"
|
|
#include "element.h"
|
|
#include "setup.h"
|
|
#include "libs/compiler.h"
|
|
#include "libs/graphics/cmap.h"
|
|
#include "libs/mathlib.h"
|
|
|
|
|
|
static TimeCount LastTime;
|
|
static SEQUENCE Sequences[MAX_ANIMATIONS + 2];
|
|
// 2 extra for Talk and Transition animations
|
|
static DWORD ActiveMask;
|
|
// Bit mask of all animations that are currently active.
|
|
// Bit 'i' is set if the animation with index 'i' is active.
|
|
static ANIMATION_DESC TalkDesc;
|
|
static ANIMATION_DESC TransitDesc;
|
|
static SEQUENCE* Talk;
|
|
static SEQUENCE* Transit;
|
|
static COUNT FirstAmbient;
|
|
static COUNT TotalSequences;
|
|
|
|
|
|
static inline DWORD
|
|
randomFrameRate (SEQUENCE *pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
return ADPtr->BaseFrameRate +
|
|
TFB_Random () % (ADPtr->RandomFrameRate + 1);
|
|
}
|
|
|
|
static inline DWORD
|
|
randomRestartRate (SEQUENCE *pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
return ADPtr->BaseRestartRate +
|
|
TFB_Random () % (ADPtr->RandomRestartRate + 1);
|
|
}
|
|
|
|
static inline COUNT
|
|
randomFrameIndex (SEQUENCE *pSeq, COUNT from)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
return from + TFB_Random () % (ADPtr->NumFrames - from);
|
|
}
|
|
|
|
static void
|
|
SetupAmbientSequences (SEQUENCE *pSeq, COUNT Num)
|
|
{
|
|
COUNT i;
|
|
|
|
for (i = 0; i < Num; ++i, ++pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = &CommData.AlienAmbientArray[i];
|
|
|
|
memset (pSeq, 0, sizeof (*pSeq));
|
|
|
|
pSeq->ADPtr = ADPtr;
|
|
if (ADPtr->AnimFlags & COLORXFORM_ANIM)
|
|
pSeq->AnimType = COLOR_ANIM;
|
|
else
|
|
pSeq->AnimType = PICTURE_ANIM;
|
|
pSeq->Direction = UP_DIR;
|
|
pSeq->FramesLeft = ADPtr->NumFrames;
|
|
// Default: first frame is neutral
|
|
if (ADPtr->AnimFlags & RANDOM_ANIM)
|
|
{ // Set a random frame/colormap
|
|
pSeq->NextIndex = TFB_Random () % ADPtr->NumFrames;
|
|
}
|
|
else if (ADPtr->AnimFlags & YOYO_ANIM)
|
|
{ // Skip the first frame/colormap (it's neutral)
|
|
pSeq->NextIndex = 1;
|
|
--pSeq->FramesLeft;
|
|
}
|
|
else if (ADPtr->AnimFlags & CIRCULAR_ANIM)
|
|
{ // Exception that makes everything more painful:
|
|
// *Last* frame is neutral
|
|
pSeq->CurIndex = ADPtr->NumFrames - 1;
|
|
pSeq->NextIndex = 0;
|
|
}
|
|
|
|
pSeq->Alarm = randomRestartRate (pSeq) + 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
SetupTalkSequence (SEQUENCE *pSeq, ANIMATION_DESC *ADPtr)
|
|
{
|
|
memset (pSeq, 0, sizeof (*pSeq));
|
|
// Initially disabled, and until needed
|
|
ADPtr->AnimFlags |= ANIM_DISABLED;
|
|
pSeq->ADPtr = ADPtr;
|
|
pSeq->AnimType = PICTURE_ANIM;
|
|
}
|
|
|
|
static inline BOOLEAN
|
|
animAtNeutralIndex (SEQUENCE *pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
if (ADPtr->AnimFlags & CIRCULAR_ANIM)
|
|
{ // CIRCULAR_ANIM's neutral frame is the last
|
|
return pSeq->NextIndex == 0;
|
|
}
|
|
else
|
|
{ // All others, neutral frame is the first
|
|
return pSeq->CurIndex == 0;
|
|
}
|
|
}
|
|
|
|
static inline BOOLEAN
|
|
conflictsWithTalkingAnim (SEQUENCE *pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
return ADPtr->AnimFlags & CommData.AlienTalkDesc.AnimFlags & WAIT_TALKING;
|
|
}
|
|
|
|
static void
|
|
ProcessColormapAnims (SEQUENCE *pSeq, COUNT Num)
|
|
{
|
|
COUNT i;
|
|
|
|
for (i = 0; i < Num; ++i, ++pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
if ((ADPtr->AnimFlags & ANIM_DISABLED)
|
|
|| pSeq->AnimType != COLOR_ANIM
|
|
|| !pSeq->Change)
|
|
continue;
|
|
|
|
XFormColorMap (GetColorMapAddress (
|
|
SetAbsColorMapIndex (CommData.AlienColorMap,
|
|
ADPtr->StartIndex + pSeq->CurIndex)),
|
|
pSeq->Alarm - 1);
|
|
pSeq->Change = FALSE;
|
|
}
|
|
}
|
|
|
|
static BOOLEAN
|
|
AdvanceAmbientSequence (SEQUENCE *pSeq)
|
|
{
|
|
BOOLEAN active;
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
--pSeq->FramesLeft;
|
|
// YOYO_ANIM does not actually end until it comes back
|
|
// in reverse direction, even if FramesLeft gets to 0 here
|
|
if (pSeq->FramesLeft
|
|
|| ((ADPtr->AnimFlags & YOYO_ANIM) && pSeq->NextIndex != 0))
|
|
{
|
|
active = TRUE;
|
|
pSeq->Alarm = randomFrameRate (pSeq) + 1;
|
|
}
|
|
else
|
|
{ // last animation frame
|
|
active = FALSE;
|
|
pSeq->Alarm = randomRestartRate (pSeq) + 1;
|
|
|
|
// RANDOM_ANIM must end on a neutral frame
|
|
if (ADPtr->AnimFlags & RANDOM_ANIM)
|
|
pSeq->NextIndex = 0;
|
|
}
|
|
|
|
// Will draw the next frame or change to next colormap
|
|
pSeq->CurIndex = pSeq->NextIndex;
|
|
pSeq->Change = TRUE;
|
|
|
|
if (pSeq->FramesLeft == 0)
|
|
{ // Animation ended
|
|
// Set it up for the next round
|
|
pSeq->FramesLeft = ADPtr->NumFrames;
|
|
|
|
if (ADPtr->AnimFlags & YOYO_ANIM)
|
|
{ // YOYO_ANIM never draws the first frame
|
|
// ("first" depends on direction)
|
|
--pSeq->FramesLeft;
|
|
pSeq->Direction = -pSeq->Direction;
|
|
}
|
|
else if (ADPtr->AnimFlags & CIRCULAR_ANIM)
|
|
{ // Rewind the CIRCULAR_ANIM
|
|
// NextIndex will be brought to 0 just below
|
|
pSeq->NextIndex = -1;
|
|
}
|
|
// RANDOM_ANIM is setup just below
|
|
}
|
|
|
|
if (ADPtr->AnimFlags & RANDOM_ANIM)
|
|
pSeq->NextIndex = randomFrameIndex (pSeq, 0);
|
|
else
|
|
pSeq->NextIndex += pSeq->Direction;
|
|
|
|
return active;
|
|
}
|
|
|
|
static void
|
|
ResetSequence (SEQUENCE *pSeq)
|
|
{
|
|
// Reset the animation and cause a redraw of the neutral frame,
|
|
// assuming it is not ANIM_DISABLED
|
|
// NOTE: This does not handle CIRCULAR_ANIM properly
|
|
pSeq->Direction = NO_DIR;
|
|
pSeq->CurIndex = 0;
|
|
pSeq->Change = TRUE;
|
|
}
|
|
|
|
static void
|
|
AdvanceTalkingSequence (SEQUENCE *pSeq, DWORD ElapsedTicks)
|
|
{
|
|
// We use the actual descriptor for flags processing and
|
|
// a copied one for drawing. A copied one is updated only
|
|
// when it is safe to do so.
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
if (pSeq->Direction == NO_DIR)
|
|
{ // just starting now
|
|
pSeq->Direction = UP_DIR;
|
|
// It's now safe to pick up new Talk descriptor if changed
|
|
// (e.g. Zoq and Pik taking turns to talk)
|
|
if (CommData.AlienTalkDesc.StartIndex != ADPtr->StartIndex)
|
|
{ // copy the new one
|
|
*ADPtr = CommData.AlienTalkDesc;
|
|
}
|
|
|
|
assert (pSeq->CurIndex == 0);
|
|
pSeq->Alarm = 0; // now!
|
|
ADPtr->AnimFlags &= ~ANIM_DISABLED;
|
|
}
|
|
|
|
if (pSeq->Alarm > ElapsedTicks)
|
|
{ // Not time yet
|
|
pSeq->Alarm -= ElapsedTicks;
|
|
return;
|
|
}
|
|
|
|
// Time to start or advance the animation
|
|
pSeq->Alarm = randomFrameRate (pSeq);
|
|
pSeq->Change = TRUE;
|
|
// Talking animation is like RANDOM_ANIM, except that
|
|
// random frames always alternate with the neutral one
|
|
// The animation does not stop until we reset it
|
|
if (pSeq->CurIndex == 0)
|
|
{ // random frame next
|
|
pSeq->CurIndex = randomFrameIndex (pSeq, 1);
|
|
pSeq->Alarm += randomRestartRate (pSeq);
|
|
}
|
|
else
|
|
{ // neutral frame next
|
|
pSeq->CurIndex = 0;
|
|
}
|
|
}
|
|
|
|
static BOOLEAN
|
|
AdvanceTransitSequence (SEQUENCE *pSeq, DWORD ElapsedTicks)
|
|
{
|
|
BOOLEAN done = FALSE;
|
|
// We use the actual descriptor for flags processing and
|
|
// a copied one for drawing. A copied one is updated only
|
|
// when it is safe to do so.
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
if (pSeq->Direction == NO_DIR)
|
|
{ // just starting now
|
|
pSeq->Alarm = 0; // now!
|
|
ADPtr->AnimFlags &= ~ANIM_DISABLED;
|
|
}
|
|
|
|
if (pSeq->Alarm > ElapsedTicks)
|
|
{ // Not time yet
|
|
pSeq->Alarm -= ElapsedTicks;
|
|
return FALSE;
|
|
}
|
|
|
|
// Time to start or advance the animation
|
|
pSeq->Change = TRUE;
|
|
|
|
if (pSeq->Direction == NO_DIR)
|
|
{ // just starting now
|
|
pSeq->FramesLeft = ADPtr->NumFrames;
|
|
// Both INTRO and DONE may be set at the same time,
|
|
// when e.g. Zoq and Pik are taking turns to talk
|
|
// Process the DONE transition first to go into
|
|
// a neutral state before switching over.
|
|
if (CommData.AlienTransitionDesc.AnimFlags & TALK_DONE)
|
|
{
|
|
pSeq->Direction = DOWN_DIR;
|
|
pSeq->CurIndex = ADPtr->NumFrames - 1;
|
|
}
|
|
else if (CommData.AlienTransitionDesc.AnimFlags & TALK_INTRO)
|
|
{
|
|
pSeq->Direction = UP_DIR;
|
|
// It's now safe to pick up new Transition descriptor if changed
|
|
// (e.g. Zoq and Pik taking turns to talk)
|
|
if (CommData.AlienTransitionDesc.StartIndex
|
|
!= ADPtr->StartIndex)
|
|
{ // copy the new one
|
|
*ADPtr = CommData.AlienTransitionDesc;
|
|
}
|
|
|
|
pSeq->CurIndex = 0;
|
|
}
|
|
}
|
|
|
|
--pSeq->FramesLeft;
|
|
if (pSeq->FramesLeft == 0)
|
|
{ // animation is done
|
|
if (pSeq->Direction == UP_DIR)
|
|
{ // done with TALK_INTRO transition
|
|
CommData.AlienTransitionDesc.AnimFlags &= ~TALK_INTRO;
|
|
}
|
|
else if (pSeq->Direction == DOWN_DIR)
|
|
{ // done with TALK_DONE transition
|
|
CommData.AlienTransitionDesc.AnimFlags &= ~TALK_DONE;
|
|
|
|
// Done with all transition frames
|
|
ADPtr->AnimFlags |= ANIM_DISABLED;
|
|
done = TRUE;
|
|
}
|
|
pSeq->Direction = NO_DIR;
|
|
}
|
|
else
|
|
{ // next frame
|
|
pSeq->Alarm = randomFrameRate (pSeq);
|
|
pSeq->CurIndex += pSeq->Direction;
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
void
|
|
InitCommAnimations (void)
|
|
{
|
|
ActiveMask = 0;
|
|
|
|
TalkDesc = CommData.AlienTalkDesc;
|
|
TransitDesc = CommData.AlienTransitionDesc;
|
|
|
|
// Animation sequences have to be drawn in reverse, and
|
|
// talk animations have to be drawn last (so we add them first)
|
|
TotalSequences = 0;
|
|
// Transition animation last
|
|
Transit = Sequences + TotalSequences;
|
|
SetupTalkSequence (Transit, &TransitDesc);
|
|
++TotalSequences;
|
|
// Talk animation second last
|
|
Talk = Sequences + TotalSequences;
|
|
SetupTalkSequence (Talk, &TalkDesc);
|
|
++TotalSequences;
|
|
FirstAmbient = TotalSequences;
|
|
SetupAmbientSequences (Sequences + FirstAmbient, CommData.NumAnimations);
|
|
TotalSequences += CommData.NumAnimations;
|
|
|
|
LastTime = GetTimeCounter ();
|
|
}
|
|
|
|
BOOLEAN
|
|
ProcessCommAnimations (BOOLEAN FullRedraw, BOOLEAN paused)
|
|
{
|
|
if (paused)
|
|
{ // Drive colormap xforms and nothing else
|
|
XFormColorMap_step ();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
COUNT i;
|
|
SEQUENCE *pSeq;
|
|
BOOLEAN Change;
|
|
BOOLEAN CanTalk = TRUE;
|
|
TimeCount CurTime;
|
|
DWORD ElapsedTicks;
|
|
DWORD NextActiveMask;
|
|
|
|
CurTime = GetTimeCounter ();
|
|
ElapsedTicks = CurTime - LastTime;
|
|
LastTime = CurTime;
|
|
|
|
// Process ambient animations
|
|
NextActiveMask = ActiveMask;
|
|
pSeq = Sequences + FirstAmbient;
|
|
for (i = 0; i < CommData.NumAnimations; ++i, ++pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
DWORD ActiveBit = 1L << i;
|
|
|
|
if (ADPtr->AnimFlags & ANIM_DISABLED)
|
|
continue;
|
|
|
|
if (pSeq->Direction == NO_DIR)
|
|
{ // animation is paused
|
|
if (!conflictsWithTalkingAnim (pSeq))
|
|
{ // start it up
|
|
pSeq->Direction = UP_DIR;
|
|
}
|
|
}
|
|
else if (pSeq->Alarm > ElapsedTicks)
|
|
{ // not time yet
|
|
pSeq->Alarm -= ElapsedTicks;
|
|
}
|
|
else if (ActiveMask & ADPtr->BlockMask)
|
|
{ // animation is blocked
|
|
assert (!(ActiveMask & ActiveBit) &&
|
|
"Check animations' mutual blocking masks");
|
|
assert (animAtNeutralIndex (pSeq));
|
|
// reschedule
|
|
pSeq->Alarm = randomRestartRate (pSeq) + 1;
|
|
continue;
|
|
}
|
|
else
|
|
{ // Time to start or advance the animation
|
|
if (AdvanceAmbientSequence (pSeq))
|
|
{ // Animation is active this frame and the next
|
|
ActiveMask |= ActiveBit;
|
|
NextActiveMask |= ActiveBit;
|
|
}
|
|
else
|
|
{ // Animation remains active this frame but not the next
|
|
// This keeps any conflicting animations (BlockMask)
|
|
// from activating in the same frame and scribbling over
|
|
// our last image.
|
|
NextActiveMask &= ~ActiveBit;
|
|
}
|
|
}
|
|
|
|
if (pSeq->AnimType == PICTURE_ANIM && pSeq->Direction != NO_DIR
|
|
&& conflictsWithTalkingAnim (pSeq))
|
|
{
|
|
// We want to talk, but this is a running picture animation
|
|
// which conflicts with the talking animation
|
|
// See if it is safe to stop it now.
|
|
if (animAtNeutralIndex (pSeq))
|
|
{ // pause the animation
|
|
pSeq->Direction = NO_DIR;
|
|
NextActiveMask &= ~ActiveBit;
|
|
// Talk animation is drawn last, so it's not a conflict
|
|
// for this frame. The talk animation will be drawn
|
|
// over the neutral frame.
|
|
}
|
|
else
|
|
{ // Otherwise, let the animation run until it's safe
|
|
CanTalk = FALSE;
|
|
}
|
|
}
|
|
}
|
|
// All ambient animations have been processed. Advance the mask.
|
|
ActiveMask = NextActiveMask;
|
|
|
|
// Process the talking and transition animations
|
|
if (CanTalk && haveTalkingAnim () && runningTalkingAnim ())
|
|
{
|
|
BOOLEAN done = FALSE;
|
|
|
|
if (signaledStopTalkingAnim () && haveTransitionAnim ())
|
|
{ // Run the transition. We will clear everything
|
|
// when it is done
|
|
CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
|
|
}
|
|
|
|
if (CommData.AlienTransitionDesc.AnimFlags
|
|
& (TALK_INTRO | TALK_DONE))
|
|
{ // Transitioning in or out of talking
|
|
if ((CommData.AlienTransitionDesc.AnimFlags & TALK_DONE)
|
|
&& Transit->Direction == NO_DIR)
|
|
{ // This is needed when switching talking anims
|
|
ResetSequence (Talk);
|
|
}
|
|
done = AdvanceTransitSequence (Transit, ElapsedTicks);
|
|
}
|
|
else if (!signaledStopTalkingAnim ())
|
|
{ // Talking, transition is done
|
|
AdvanceTalkingSequence (Talk, ElapsedTicks);
|
|
}
|
|
else
|
|
{ // Not talking
|
|
ResetSequence (Talk);
|
|
done = TRUE;
|
|
}
|
|
|
|
if (signaledStopTalkingAnim () && done)
|
|
{
|
|
clearRunTalkingAnim ();
|
|
clearStopTalkingAnim ();
|
|
}
|
|
}
|
|
else
|
|
{ // Not talking -- disable talking anim if it is done
|
|
if (Talk->Direction == NO_DIR)
|
|
TalkDesc.AnimFlags |= ANIM_DISABLED;
|
|
}
|
|
|
|
BatchGraphics ();
|
|
|
|
// Draw all animations
|
|
{
|
|
BOOLEAN ColorChange = XFormColorMap_step ();
|
|
|
|
if (ColorChange)
|
|
FullRedraw = TRUE;
|
|
|
|
// Colormap animations are processed separately
|
|
// from picture anims (see XFormColorMap_step)
|
|
ProcessColormapAnims (Sequences + FirstAmbient,
|
|
CommData.NumAnimations);
|
|
|
|
Change = DrawAlienFrame (Sequences, TotalSequences, FullRedraw);
|
|
if (FullRedraw)
|
|
Change = TRUE;
|
|
}
|
|
|
|
UnbatchGraphics ();
|
|
|
|
// Post-process ambient animations
|
|
pSeq = Sequences + FirstAmbient;
|
|
for (i = 0; i < CommData.NumAnimations; ++i, ++pSeq)
|
|
{
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
DWORD ActiveBit = 1L << i;
|
|
|
|
if (ADPtr->AnimFlags & ANIM_DISABLED)
|
|
continue;
|
|
|
|
// We can only disable a one-shot anim here, otherwise the
|
|
// last frame will not be drawn
|
|
if ((ADPtr->AnimFlags & ONE_SHOT_ANIM)
|
|
&& !(NextActiveMask & ActiveBit))
|
|
{ // One-shot animation, inactive next frame
|
|
ADPtr->AnimFlags |= ANIM_DISABLED;
|
|
}
|
|
}
|
|
|
|
return Change;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
DrawAlienFrame (SEQUENCE *Sequences, COUNT Num, BOOLEAN fullRedraw)
|
|
{
|
|
int i;
|
|
STAMP s;
|
|
BOOLEAN Change = FALSE;
|
|
|
|
BatchGraphics ();
|
|
|
|
s.origin.x = -SAFE_X;
|
|
s.origin.y = 0;
|
|
|
|
if (fullRedraw)
|
|
{
|
|
// Draw the main frame
|
|
s.frame = CommData.AlienFrame;
|
|
DrawStamp (&s);
|
|
|
|
// Draw any static frames (has to be in reverse)
|
|
for (i = CommData.NumAnimations - 1; i >= 0; --i)
|
|
{
|
|
ANIMATION_DESC *ADPtr = &CommData.AlienAmbientArray[i];
|
|
|
|
if (ADPtr->AnimFlags & ANIM_MASK)
|
|
continue;
|
|
|
|
ADPtr->AnimFlags |= ANIM_DISABLED;
|
|
|
|
if (!(ADPtr->AnimFlags & COLORXFORM_ANIM))
|
|
{ // It's a static frame (e.g. Flagship picture at Starbase)
|
|
s.frame = SetAbsFrameIndex (CommData.AlienFrame,
|
|
ADPtr->StartIndex);
|
|
DrawStamp (&s);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Sequences)
|
|
{ // Draw the animation sequences (has to be in reverse)
|
|
for (i = Num - 1; i >= 0; --i)
|
|
{
|
|
SEQUENCE *pSeq = &Sequences[i];
|
|
ANIMATION_DESC *ADPtr = pSeq->ADPtr;
|
|
|
|
if ((ADPtr->AnimFlags & ANIM_DISABLED)
|
|
|| pSeq->AnimType != PICTURE_ANIM)
|
|
continue;
|
|
|
|
// Draw current animation frame only if changed
|
|
if (!fullRedraw && !pSeq->Change)
|
|
continue;
|
|
|
|
s.frame = SetAbsFrameIndex (CommData.AlienFrame,
|
|
ADPtr->StartIndex + pSeq->CurIndex);
|
|
DrawStamp (&s);
|
|
pSeq->Change = FALSE;
|
|
|
|
Change = TRUE;
|
|
}
|
|
}
|
|
|
|
UnbatchGraphics ();
|
|
|
|
return Change;
|
|
}
|