Comm animation processing rewrite; cleaner and meaner; a bunch of bugs fixed, and some probably added ;)

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@3305 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
avolkov
2009-11-15 06:43:03 +00:00
parent 0b39fbb0b2
commit 39bde10b0c
9 changed files with 706 additions and 566 deletions
+1
View File
@@ -1,4 +1,5 @@
Changes towards version 0.7: Changes towards version 0.7:
- Comm animation processing rewrite, bugs fixed - Alex
- Added graphics context debugging function - SvdB - Added graphics context debugging function - SvdB
- Thread down-throttling and game sleep when inactive (currently disabled), - Thread down-throttling and game sleep when inactive (currently disabled),
(bug #1070), from Flandry (bug #1070), from Flandry
+52 -111
View File
@@ -53,6 +53,8 @@ LOCDATA CommData;
int cur_comm; int cur_comm;
UNICODE shared_phrase_buf[2048]; UNICODE shared_phrase_buf[2048];
static BOOLEAN TalkingFinished;
typedef struct response_entry typedef struct response_entry
{ {
RESPONSE_REF response_ref; RESPONSE_REF response_ref;
@@ -87,7 +89,6 @@ static const UNICODE * volatile last_subtitle;
static CONTEXT TextCacheContext; static CONTEXT TextCacheContext;
static FRAME TextCacheFrame; static FRAME TextCacheFrame;
volatile BOOLEAN ClearSummary;
RECT CommWndRect = { RECT CommWndRect = {
// default values; actually inited by HailAlien() // default values; actually inited by HailAlien()
@@ -382,51 +383,6 @@ DrawSISComWindow (void)
} }
} }
void
DrawAlienFrame (FRAME aframe, SEQUENCE *pSeq)
{
COUNT i;
STAMP s;
ANIMATION_DESC *ADPtr;
s.origin.x = -SAFE_X;
s.origin.y = 0;
s.frame = CommData.AlienFrame;
if (s.frame == 0)
s.frame = aframe;
BatchGraphics ();
DrawStamp (&s);
i = CommData.NumAnimations;
ADPtr = &CommData.AlienAmbientArray[i];
while (i--)
{
--ADPtr;
if (!(ADPtr->AnimFlags & ANIM_MASK))
{
s.frame = SetAbsFrameIndex (s.frame, ADPtr->StartIndex);
DrawStamp (&s);
ADPtr->AnimFlags |= ANIM_DISABLED;
}
else if (pSeq)
{
if (pSeq->AnimType == PICTURE_ANIM)
{
s.frame = pSeq->AnimObj.CurFrame;
DrawStamp (&s);
}
--pSeq;
}
}
if (aframe && CommData.AlienFrame && aframe != CommData.AlienFrame)
{
s.frame = aframe;
DrawStamp (&s);
}
UnbatchGraphics ();
}
void void
init_communication (void) init_communication (void)
{ {
@@ -553,13 +509,11 @@ SpewPhrases (COUNT wait_track)
BOOLEAN ContinuityBreak; BOOLEAN ContinuityBreak;
DWORD TimeIn; DWORD TimeIn;
COUNT which_track; COUNT which_track;
FRAME F;
BOOLEAN rewind = FALSE; BOOLEAN rewind = FALSE;
TimeIn = GetTimeCounter (); TimeIn = GetTimeCounter ();
ContinuityBreak = FALSE; ContinuityBreak = FALSE;
F = CommData.AlienFrame;
if (wait_track == 0) if (wait_track == 0)
{ // Restarting with a rewind { // Restarting with a rewind
wait_track = (COUNT)~0; wait_track = (COUNT)~0;
@@ -636,12 +590,10 @@ SpewPhrases (COUNT wait_track)
else if (optSmoothScroll == OPT_3DO) else if (optSmoothScroll == OPT_3DO)
FastForward_Smooth (); FastForward_Smooth ();
ContinuityBreak = TRUE; ContinuityBreak = TRUE;
// XXX: Ugly hack: This causes all animations (talking and ambient) // XXX: This causes all animations (talking and ambient)
// in ambient_anim_task to stop progressing. I see no reason why // in ambient_anim_task to stop progressing. I see no reason why
// the animations cannot continue while seeking. This hack has // the animations cannot continue while seeking.
// spawned a multitude of workarounds in the comm code, and IMHO PauseAnimTask = TRUE;
// should be removed.
CommData.AlienFrame = 0;
} }
else if (left || rewind) else if (left || rewind)
{ {
@@ -652,8 +604,8 @@ SpewPhrases (COUNT wait_track)
else if (optSmoothScroll == OPT_3DO) else if (optSmoothScroll == OPT_3DO)
FastReverse_Smooth (); FastReverse_Smooth ();
ContinuityBreak = TRUE; ContinuityBreak = TRUE;
// XXX: See ugly hack discussion above // XXX: See pause discussion above
CommData.AlienFrame = 0; PauseAnimTask = TRUE;
} }
else if (ContinuityBreak) else if (ContinuityBreak)
{ {
@@ -663,20 +615,20 @@ SpewPhrases (COUNT wait_track)
SetSliderImage (SetAbsFrameIndex (ActivityFrame, 2)); SetSliderImage (SetAbsFrameIndex (ActivityFrame, 2));
} }
else else
{ // XXX: See ugly hack discussion above { // XXX: See pause discussion above
// Additionally, this used to have a buggy guard condition, which // Additionally, this used to have a buggy guard condition, which
// would cause the animations to remain paused in a couple cases // would cause the animations to remain paused in a couple cases
// after seeking back to the beginning. // after seeking back to the beginning.
// Broken cases were: Syreen "several hours later" and Starbase // Broken cases were: Syreen "several hours later" and Starbase
// VUX Beast analysis by the scientist. // VUX Beast analysis by the scientist.
CommData.AlienFrame = F; PauseAnimTask = FALSE;
} }
which_track = PlayingTrack (); which_track = PlayingTrack ();
} while (ContinuityBreak || (which_track && which_track <= wait_track)); } while (ContinuityBreak || (which_track && which_track <= wait_track));
CommData.AlienFrame = F; PauseAnimTask = FALSE;
ClearSubtitles (); ClearSubtitles ();
if (!which_track || wait_track == (COUNT)~0) if (!which_track || wait_track == (COUNT)~0)
@@ -696,22 +648,17 @@ DoTalkSegue (COUNT wait_track)
BOOLEAN done; BOOLEAN done;
// Transition animation to talking state, if necessary // Transition animation to talking state, if necessary
if (CommData.AlienTalkDesc.NumFrames) if (wantTalkingAnim () && haveTalkingAnim ())
{ {
if (!(CommData.AlienTransitionDesc.AnimFlags & TALK_INTRO)) if (haveTransitionAnim ())
{ setRunIntroAnim ();
CommData.AlienTransitionDesc.AnimFlags |= TALK_INTRO;
if (CommData.AlienTransitionDesc.NumFrames)
CommData.AlienTalkDesc.AnimFlags |= TALK_INTRO;
}
CommData.AlienTransitionDesc.AnimFlags &= ~PAUSE_TALKING; setRunTalkingAnim ();
if (CommData.AlienTalkDesc.NumFrames)
CommData.AlienTalkDesc.AnimFlags |= WAIT_TALKING; while (runningIntroAnim ())
while (CommData.AlienTalkDesc.AnimFlags & TALK_INTRO)
{ // wait until the transition finishes { // wait until the transition finishes
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
TaskSwitch (); SleepThread (ONE_SECOND / 30);
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
} }
} }
@@ -719,14 +666,8 @@ DoTalkSegue (COUNT wait_track)
done = !SpewPhrases (wait_track); done = !SpewPhrases (wait_track);
// transition back to silent, if necessary // transition back to silent, if necessary
if (CommData.AlienTalkDesc.NumFrames) if (runningTalkingAnim ())
{ setStopTalkingAnim ();
// must set the TALK_DONE flag so that the animation task
// releases WAIT_TALKING from AlienTalkDesc
CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
if ((CommData.AlienTalkDesc.AnimFlags & WAIT_TALKING))
CommData.AlienTalkDesc.AnimFlags |= PAUSE_TALKING;
}
return done; return done;
} }
@@ -734,13 +675,15 @@ DoTalkSegue (COUNT wait_track)
static void static void
FlushTalkSegue (void) FlushTalkSegue (void)
{ {
WaitForNoInput (ONE_SECOND / 2);
FlushInput (); FlushInput ();
while (AnyButtonPress (TRUE))
TaskSwitch ();
// Wait until the animation task stops "talking"
do do
TaskSwitch (); SleepThread (ONE_SECOND / 30);
while (CommData.AlienTalkDesc.AnimFlags & PAUSE_TALKING); while (runningTalkingAnim ());
TalkingFinished = TRUE;
} }
void void
@@ -749,8 +692,7 @@ AlienTalkSegue (COUNT wait_track)
BOOLEAN done; BOOLEAN done;
// this skips any talk segues that follow an aborted one // this skips any talk segues that follow an aborted one
if ((GLOBAL (CurrentActivity) & CHECK_ABORT) if ((GLOBAL (CurrentActivity) & CHECK_ABORT) || TalkingFinished)
|| (CommData.AlienTransitionDesc.AnimFlags & TALK_DONE))
return; return;
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
@@ -758,7 +700,7 @@ AlienTalkSegue (COUNT wait_track)
if (!pCurInputState->Initialized) if (!pCurInputState->Initialized)
{ {
SetColorMap (GetColorMapAddress (CommData.AlienColorMap)); SetColorMap (GetColorMapAddress (CommData.AlienColorMap));
DrawAlienFrame (CommData.AlienFrame, NULL); DrawAlienFrame (NULL, 0, TRUE);
UpdateSpeechGraphics (TRUE); UpdateSpeechGraphics (TRUE);
if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE if (LOBYTE (GLOBAL (CurrentActivity)) == WON_LAST_BATTLE
@@ -807,8 +749,9 @@ AlienTalkSegue (COUNT wait_track)
DWORD TimeOut; DWORD TimeOut;
TimeOut = GetTimeCounter () + (ONE_SECOND >> 1); TimeOut = GetTimeCounter () + (ONE_SECOND >> 1);
/* if (CommData.NumAnimations) */ // Anim task processes not only ambient animations, but also
pCurInputState->AnimTask = StartCommAnimTask (); // talking animations and subtitles
pCurInputState->AnimTask = StartCommAnimTask ();
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
SleepThreadUntil (TimeOut); SleepThreadUntil (TimeOut);
@@ -825,15 +768,9 @@ AlienTalkSegue (COUNT wait_track)
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
FlushTalkSegue (); FlushTalkSegue ();
if (done || wait_track == (COUNT)~0) if (!done && wait_track != (COUNT)~0)
{ // all done talking here
CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
}
else
{ // there is more to come { // there is more to come
CommData.AlienTransitionDesc.AnimFlags &= ~TALK_DONE; TalkingFinished = FALSE;
// allow a transition to talking state again later
CommData.AlienTransitionDesc.AnimFlags &= ~TALK_INTRO;
} }
} }
@@ -1000,7 +937,7 @@ SelectResponse (ENCOUNTER_STATE *pES)
FadeMusic (BACKGROUND_VOL, ONE_SECOND); FadeMusic (BACKGROUND_VOL, ONE_SECOND);
CommData.AlienTransitionDesc.AnimFlags &= ~(TALK_INTRO | TALK_DONE); TalkingFinished = FALSE;
pES->num_responses = 0; pES->num_responses = 0;
(*pES->response_list[pES->cur_response].response_func) (*pES->response_list[pES->cur_response].response_func)
(pES->response_list[pES->cur_response].response_ref); (pES->response_list[pES->cur_response].response_ref);
@@ -1024,7 +961,7 @@ SelectConversationSummary (ENCOUNTER_STATE *pES)
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
RefreshResponses (pES); RefreshResponses (pES);
ClearSummary = TRUE; clear_subtitles = TRUE;
PauseAnimTask = FALSE; PauseAnimTask = FALSE;
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
} }
@@ -1060,9 +997,7 @@ PlayerResponseInput (ENCOUNTER_STATE *pES)
FadeMusic (BACKGROUND_VOL, ONE_SECOND); FadeMusic (BACKGROUND_VOL, ONE_SECOND);
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
FeedbackPlayerPhrase (pES->phrase_buf); FeedbackPlayerPhrase (pES->phrase_buf);
// reset transition state TalkingFinished = FALSE;
CommData.AlienTransitionDesc.AnimFlags &=
~(TALK_INTRO | TALK_DONE);
DoTalkSegue (0); DoTalkSegue (0);
if (!(GLOBAL (CurrentActivity) & CHECK_ABORT)) if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
@@ -1073,9 +1008,6 @@ PlayerResponseInput (ENCOUNTER_STATE *pES)
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
FlushTalkSegue (); FlushTalkSegue ();
// done one way or the other
CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
} }
else if (PulsedInputState.menu[KEY_MENU_UP]) else if (PulsedInputState.menu[KEY_MENU_UP])
response = (BYTE)((response + (BYTE)(pES->num_responses - 1)) response = (BYTE)((response + (BYTE)(pES->num_responses - 1))
@@ -1119,7 +1051,8 @@ DoCommunication (ENCOUNTER_STATE *pES)
{ {
SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN, MENU_SOUND_SELECT); SetMenuSounds (MENU_SOUND_UP | MENU_SOUND_DOWN, MENU_SOUND_SELECT);
if (!(CommData.AlienTransitionDesc.AnimFlags & TALK_DONE)) // First, finish playing all queued tracks if not done yet
if (!TalkingFinished)
AlienTalkSegue ((COUNT)~0); AlienTalkSegue ((COUNT)~0);
if (GLOBAL (CurrentActivity) & CHECK_ABORT) if (GLOBAL (CurrentActivity) & CHECK_ABORT)
@@ -1141,14 +1074,10 @@ DoCommunication (ENCOUNTER_STATE *pES)
{ {
FadeMusic (BACKGROUND_VOL, ONE_SECOND); FadeMusic (BACKGROUND_VOL, ONE_SECOND);
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
// reset transition state TalkingFinished = FALSE;
CommData.AlienTransitionDesc.AnimFlags &=
~(TALK_INTRO | TALK_DONE);
DoTalkSegue (0); DoTalkSegue (0);
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
FlushTalkSegue (); FlushTalkSegue ();
// done one way or the other
CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
if (GLOBAL (CurrentActivity) & CHECK_ABORT) if (GLOBAL (CurrentActivity) & CHECK_ABORT)
break; break;
@@ -1172,7 +1101,6 @@ DoCommunication (ENCOUNTER_STATE *pES)
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
pES->AnimTask = 0; pES->AnimTask = 0;
} }
CommData.AlienTransitionDesc.AnimFlags &= ~(TALK_INTRO | TALK_DONE);
SetContext (SpaceContext); SetContext (SpaceContext);
DestroyContext (TaskContext); DestroyContext (TaskContext);
@@ -1243,6 +1171,8 @@ HailAlien (void)
pCurInputState = &ES; pCurInputState = &ES;
memset (pCurInputState, 0, sizeof (*pCurInputState)); memset (pCurInputState, 0, sizeof (*pCurInputState));
TalkingFinished = FALSE;
ES.InputFunc = DoCommunication; ES.InputFunc = DoCommunication;
PlayerFont = LoadFont (PLAYER_FONT); PlayerFont = LoadFont (PLAYER_FONT);
@@ -1443,7 +1373,9 @@ InitCommunication (CONVERSATION which_comm)
status != YEHAT_REBEL_SHIP ? which_comm : status != YEHAT_REBEL_SHIP ? which_comm :
YEHAT_REBEL_CONVERSATION); YEHAT_REBEL_CONVERSATION);
if (LocDataPtr) if (LocDataPtr)
{ // We make a copy here
CommData = *LocDataPtr; CommData = *LocDataPtr;
}
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
@@ -1717,3 +1649,12 @@ CheckSubtitles (void)
} }
UnlockMutex (subtitle_mutex); UnlockMutex (subtitle_mutex);
} }
void
EnableTalkingAnim (BOOLEAN enable)
{
if (enable)
CommData.AlienTalkDesc.AnimFlags &= ~PAUSE_TALKING;
else
CommData.AlienTalkDesc.AnimFlags |= PAUSE_TALKING;
}
+68 -3
View File
@@ -28,10 +28,73 @@
#include "commanim.h" #include "commanim.h"
extern void DrawAlienFrame (FRAME aframe, SEQUENCE *pSeq);
extern LOCDATA CommData; extern LOCDATA CommData;
extern volatile BOOLEAN ClearSummary;
static inline BOOLEAN
haveTalkingAnim (void)
{
return CommData.AlienTalkDesc.NumFrames > 0;
}
static inline BOOLEAN
haveTransitionAnim (void)
{
return CommData.AlienTransitionDesc.NumFrames > 0;
}
static inline BOOLEAN
wantTalkingAnim (void)
{
return !(CommData.AlienTalkDesc.AnimFlags & PAUSE_TALKING);
}
static inline void
setRunTalkingAnim (void)
{
CommData.AlienTalkDesc.AnimFlags |= WAIT_TALKING;
}
static inline void
clearRunTalkingAnim (void)
{
CommData.AlienTalkDesc.AnimFlags &= ~WAIT_TALKING;
}
static inline BOOLEAN
runningTalkingAnim (void)
{
return (CommData.AlienTalkDesc.AnimFlags & WAIT_TALKING);
}
static inline void
setRunIntroAnim (void)
{
CommData.AlienTransitionDesc.AnimFlags |= TALK_INTRO;
}
static inline BOOLEAN
runningIntroAnim (void)
{
return (CommData.AlienTransitionDesc.AnimFlags & TALK_INTRO);
}
static inline void
setStopTalkingAnim (void)
{
CommData.AlienTalkDesc.AnimFlags |= TALK_DONE;
}
static inline void
clearStopTalkingAnim (void)
{
CommData.AlienTalkDesc.AnimFlags &= ~TALK_DONE;
}
static inline BOOLEAN
signaledStopTalkingAnim (void)
{
return CommData.AlienTalkDesc.AnimFlags & TALK_DONE;
}
#endif #endif
@@ -45,6 +108,8 @@ extern BOOLEAN HaveSubtitlesChanged (void);
extern RECT CommWndRect; /* comm window rect */ extern RECT CommWndRect; /* comm window rect */
extern void EnableTalkingAnim (BOOLEAN enable);
#endif /* _COMM_H */ #endif /* _COMM_H */
+4 -5
View File
@@ -838,14 +838,13 @@ Intro (void)
if (!GET_GAME_STATE (MET_ORZ_BEFORE)) if (!GET_GAME_STATE (MET_ORZ_BEFORE))
{ {
BYTE N;
SET_GAME_STATE (MET_ORZ_BEFORE, 1); SET_GAME_STATE (MET_ORZ_BEFORE, 1);
N = CommData.AlienTalkDesc.NumFrames; // Disable talking anim and run the computer report
CommData.AlienTalkDesc.NumFrames = 0; EnableTalkingAnim (FALSE);
AlienTalkSegue (1); AlienTalkSegue (1);
CommData.AlienTalkDesc.NumFrames = N; // Run whatever is left with talking anim
EnableTalkingAnim (TRUE);
} }
} }
+2 -3
View File
@@ -1291,11 +1291,10 @@ DiscussDevices (BOOLEAN TalkAbout)
// Run all tracks upto the Vux Beast scientist's report // Run all tracks upto the Vux Beast scientist's report
AlienTalkSegue (VuxBeastIndex - 1); AlienTalkSegue (VuxBeastIndex - 1);
// Disable Commander's speech animation and run the report // Disable Commander's speech animation and run the report
i = CommData.AlienTalkDesc.NumFrames; EnableTalkingAnim (FALSE);
CommData.AlienTalkDesc.NumFrames = 0;
AlienTalkSegue (VuxBeastIndex); AlienTalkSegue (VuxBeastIndex);
// Enable Commander's speech animation and run the rest // Enable Commander's speech animation and run the rest
CommData.AlienTalkDesc.NumFrames = i; EnableTalkingAnim (TRUE);
AlienTalkSegue ((COUNT)~0); AlienTalkSegue ((COUNT)~0);
} }
} }
+8 -2
View File
@@ -123,9 +123,12 @@ SelectAlienZOQ (void)
{ {
if (LastAlien != ZOQ_ALIEN) if (LastAlien != ZOQ_ALIEN)
{ {
// Transition to neutral state first if Pik was talking
if (LastAlien != FOT_ALIEN)
CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
LastAlien = ZOQ_ALIEN; LastAlien = ZOQ_ALIEN;
CommData.AlienTransitionDesc.AnimFlags |= TALK_INTRO;
CommData.AlienTransitionDesc.StartIndex = FOT_TO_ZOQ; CommData.AlienTransitionDesc.StartIndex = FOT_TO_ZOQ;
CommData.AlienTalkDesc.AnimFlags |= TALK_INTRO;
CommData.AlienTalkDesc.StartIndex = ZOQ_TALK_INDEX; CommData.AlienTalkDesc.StartIndex = ZOQ_TALK_INDEX;
CommData.AlienTalkDesc.NumFrames = ZOQ_TALK_FRAMES; CommData.AlienTalkDesc.NumFrames = ZOQ_TALK_FRAMES;
CommData.AlienAmbientArray[1].AnimFlags &= ~WAIT_TALKING; CommData.AlienAmbientArray[1].AnimFlags &= ~WAIT_TALKING;
@@ -142,9 +145,12 @@ SelectAlienPIK (void)
{ {
if (LastAlien != PIK_ALIEN) if (LastAlien != PIK_ALIEN)
{ {
// Transition to neutral state first if Zoq was talking
if (LastAlien != FOT_ALIEN)
CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
LastAlien = PIK_ALIEN; LastAlien = PIK_ALIEN;
CommData.AlienTransitionDesc.AnimFlags |= TALK_INTRO;
CommData.AlienTransitionDesc.StartIndex = FOT_TO_PIK; CommData.AlienTransitionDesc.StartIndex = FOT_TO_PIK;
CommData.AlienTalkDesc.AnimFlags |= TALK_INTRO;
CommData.AlienTalkDesc.StartIndex = PIK_TALK_INDEX; CommData.AlienTalkDesc.StartIndex = PIK_TALK_INDEX;
CommData.AlienTalkDesc.NumFrames = PIK_TALK_FRAMES; CommData.AlienTalkDesc.NumFrames = PIK_TALK_FRAMES;
CommData.AlienAmbientArray[1].AnimFlags |= WAIT_TALKING; CommData.AlienAmbientArray[1].AnimFlags |= WAIT_TALKING;
+484 -382
View File
@@ -44,118 +44,371 @@ static int ambient_anim_task (void *data);
volatile BOOLEAN PauseAnimTask = FALSE; volatile BOOLEAN PauseAnimTask = FALSE;
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 static void
SetUpSequence (SEQUENCE *pSeq) SetupAmbientSequences (SEQUENCE *pSeq, COUNT Num)
{ {
COUNT i; COUNT i;
ANIMATION_DESC *ADPtr;
for (i = 0; i < Num; ++i, ++pSeq)
i = CommData.NumAnimations;
pSeq = &pSeq[i];
ADPtr = &CommData.AlienAmbientArray[i];
while (i--)
{ {
--ADPtr; ANIMATION_DESC *ADPtr = &CommData.AlienAmbientArray[i];
--pSeq;
memset (pSeq, 0, sizeof (*pSeq));
pSeq->ADPtr = ADPtr;
if (ADPtr->AnimFlags & COLORXFORM_ANIM) if (ADPtr->AnimFlags & COLORXFORM_ANIM)
pSeq->AnimType = COLOR_ANIM; pSeq->AnimType = COLOR_ANIM;
else else
pSeq->AnimType = PICTURE_ANIM; pSeq->AnimType = PICTURE_ANIM;
pSeq->Direction = UP_DIR; pSeq->Direction = UP_DIR;
pSeq->FramesLeft = ADPtr->NumFrames; pSeq->FramesLeft = ADPtr->NumFrames;
if (pSeq->AnimType == COLOR_ANIM) // Default: first frame is neutral
pSeq->AnimObj.CurCMap = SetAbsColorMapIndex (
CommData.AlienColorMap, ADPtr->StartIndex);
else
pSeq->AnimObj.CurFrame = SetAbsFrameIndex (
CommData.AlienFrame, ADPtr->StartIndex);
if (ADPtr->AnimFlags & RANDOM_ANIM) if (ADPtr->AnimFlags & RANDOM_ANIM)
{ { // Set a random frame/colormap
if (pSeq->AnimType == COLOR_ANIM) pSeq->NextIndex = TFB_Random () % ADPtr->NumFrames;
pSeq->AnimObj.CurCMap =
SetRelColorMapIndex (pSeq->AnimObj.CurCMap,
(COUNT)((COUNT)TFB_Random () % pSeq->FramesLeft));
else
pSeq->AnimObj.CurFrame =
SetRelFrameIndex (pSeq->AnimObj.CurFrame,
(COUNT)((COUNT)TFB_Random () % pSeq->FramesLeft));
} }
else if (ADPtr->AnimFlags & YOYO_ANIM) else if (ADPtr->AnimFlags & YOYO_ANIM)
{ { // Skip the first frame/colormap (it's neutral)
pSeq->NextIndex = 1;
--pSeq->FramesLeft; --pSeq->FramesLeft;
if (pSeq->AnimType == COLOR_ANIM) }
pSeq->AnimObj.CurCMap = else if (ADPtr->AnimFlags & CIRCULAR_ANIM)
SetRelColorMapIndex (pSeq->AnimObj.CurCMap, 1); { // Exception that makes everything more painful:
else // *Last* frame is neutral
pSeq->AnimObj.CurFrame = pSeq->CurIndex = ADPtr->NumFrames - 1;
IncFrameIndex (pSeq->AnimObj.CurFrame); pSeq->NextIndex = 0;
} }
pSeq->Alarm = ADPtr->BaseRestartRate pSeq->Alarm = randomRestartRate (pSeq) + 1;
+ ((COUNT)TFB_Random () % (ADPtr->RandomRestartRate + 1)) + 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
{ // animation ended
active = FALSE;
pSeq->Alarm = randomRestartRate (pSeq) + 1;
}
// 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;
}
static int static int
ambient_anim_task (void *data) ambient_anim_task (void *data)
{ {
SIZE TalkAlarm;
FRAME TalkFrame;
FRAME ResetTalkFrame = NULL;
FRAME TransitionFrame = NULL;
FRAME AnimFrame[MAX_ANIMATIONS];
COUNT i; COUNT i;
DWORD LastTime; TimeCount LastTime;
FRAME CommFrame; SEQUENCE Sequences[MAX_ANIMATIONS + 2];
SEQUENCE Sequencer[MAX_ANIMATIONS]; // 2 extra for Talk and Transition animations
ANIMATION_DESC TalkBuffer = CommData.AlienTalkDesc;
SEQUENCE *pSeq; SEQUENCE *pSeq;
ANIMATION_DESC *ADPtr;
DWORD ActiveMask; DWORD ActiveMask;
// Bit mask of all animations that are currently active. // Bit mask of all animations that are currently active.
// Bit 'i' is set if the animation with index 'i' is active. // Bit 'i' is set if the animation with index 'i' is active.
DWORD LastOscillTime; DWORD LastOscillTime;
Task task = (Task) data; Task task = (Task) data;
BOOLEAN TransitionDone = FALSE;
BOOLEAN TalkFrameChanged = FALSE;
BOOLEAN FrameChanged[MAX_ANIMATIONS];
BOOLEAN ColorChange = FALSE; BOOLEAN ColorChange = FALSE;
while ((CommFrame = CommData.AlienFrame) == 0 ANIMATION_DESC TalkDesc;
&& !Task_ReadState (task, TASK_EXIT)) ANIMATION_DESC TransitDesc;
SEQUENCE* Talk;
SEQUENCE* Transit;
COUNT FirstAmbient;
COUNT TotalSequences;
while (!CommData.AlienFrame && !Task_ReadState (task, TASK_EXIT))
TaskSwitch (); TaskSwitch ();
LockMutex (GraphicsLock);
memset (&DisplayArray[0], 0, sizeof (DisplayArray));
SetUpSequence (Sequencer);
UnlockMutex (GraphicsLock);
ActiveMask = 0; ActiveMask = 0;
TalkAlarm = 0;
TalkFrame = 0; // Certain data must be accessed under GraphicsLock
LockMutex (GraphicsLock);
memset (&DisplayArray[0], 0, sizeof (DisplayArray));
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;
UnlockMutex (GraphicsLock);
LastTime = GetTimeCounter (); LastTime = GetTimeCounter ();
LastOscillTime = LastTime; LastOscillTime = LastTime;
memset (FrameChanged, 0, sizeof (FrameChanged));
memset (AnimFrame, 0, sizeof (AnimFrame));
for (i = 0; i < CommData.NumAnimations; i++)
{
COUNT nextIndex;
if (CommData.AlienAmbientArray[i].AnimFlags & YOYO_ANIM)
nextIndex = CommData.AlienAmbientArray[i].StartIndex;
else
nextIndex = (CommData.AlienAmbientArray[i].StartIndex
+ CommData.AlienAmbientArray[i].NumFrames - 1);
AnimFrame[i] = SetAbsFrameIndex (CommFrame, nextIndex);
}
while (!Task_ReadState (task, TASK_EXIT)) while (!Task_ReadState (task, TASK_EXIT))
{ {
BOOLEAN Change, CanTalk; BOOLEAN CanTalk;
DWORD CurTime, ElapsedTicks; TimeCount CurTime;
DWORD ElapsedTicks;
SleepThreadUntil (LastTime + ONE_SECOND / AMBIENT_ANIM_RATE); SleepThreadUntil (LastTime + ONE_SECOND / AMBIENT_ANIM_RATE);
@@ -165,368 +418,153 @@ ambient_anim_task (void *data)
ElapsedTicks = CurTime - LastTime; ElapsedTicks = CurTime - LastTime;
LastTime = CurTime; LastTime = CurTime;
Change = FALSE; if (PauseAnimTask)
i = CommData.NumAnimations; { // anims not processed
if (CommData.AlienFrame) i = CommData.NumAnimations;
CanTalk = TRUE; CanTalk = FALSE;
}
else else
{ {
i = 0; i = 0;
CanTalk = FALSE; CanTalk = TRUE;
} }
pSeq = &Sequencer[i]; // Process ambient animations
ADPtr = &CommData.AlienAmbientArray[i]; pSeq = Sequences + FirstAmbient;
while (i-- && !Task_ReadState (task, TASK_EXIT)) for ( ; i < CommData.NumAnimations; ++i, ++pSeq)
{ {
--ADPtr; ANIMATION_DESC *ADPtr = pSeq->ADPtr;
--pSeq; DWORD ActiveBit = 1L << i;
if (ADPtr->AnimFlags & ANIM_DISABLED) if (ADPtr->AnimFlags & ANIM_DISABLED)
continue; continue;
if (pSeq->Direction == NO_DIR) if (pSeq->Direction == NO_DIR)
{ { // animation is paused
if (!(ADPtr->AnimFlags if (!conflictsWithTalkingAnim (pSeq))
& CommData.AlienTalkDesc.AnimFlags & WAIT_TALKING)) { // start it up
{
// This animation does not conflict with the talking
// animation right now.
pSeq->Direction = UP_DIR; pSeq->Direction = UP_DIR;
} }
} }
else if ((DWORD)pSeq->Alarm > ElapsedTicks) else if (pSeq->Alarm > ElapsedTicks)
pSeq->Alarm -= (COUNT)ElapsedTicks; { // not time yet
pSeq->Alarm -= ElapsedTicks;
}
else if (ActiveMask & ADPtr->BlockMask)
{ // animation is blocked
assert (!(ActiveMask & ActiveBit));
assert (animAtNeutralIndex (pSeq));
// reschedule
pSeq->Alarm = randomRestartRate (pSeq) + 1;
continue;
}
else else
{ { // Time to start or advance the animation
if (!(ActiveMask & ADPtr->BlockMask) if (AdvanceAmbientSequence (pSeq))
&& (--pSeq->FramesLeft ActiveMask |= ActiveBit;
|| ((ADPtr->AnimFlags & YOYO_ANIM)
&& pSeq->Direction == UP_DIR)))
{
ActiveMask |= 1L << i;
pSeq->Alarm = ADPtr->BaseFrameRate
+ ((COUNT)TFB_Random ()
% (ADPtr->RandomFrameRate + 1)) + 1;
}
else else
{ ActiveMask &= ~ActiveBit;
ActiveMask &= ~(1L << i);
pSeq->Alarm = ADPtr->BaseRestartRate
+ ((COUNT)TFB_Random ()
% (ADPtr->RandomRestartRate + 1)) + 1;
if (ActiveMask & ADPtr->BlockMask)
continue;
}
if (pSeq->AnimType == COLOR_ANIM)
{
XFormColorMap (GetColorMapAddress (pSeq->AnimObj.CurCMap),
(COUNT) (pSeq->Alarm - 1));
}
else
{
Change = TRUE;
AnimFrame[i] = pSeq->AnimObj.CurFrame;
FrameChanged[i] = 1;
}
if (pSeq->FramesLeft == 0)
{
pSeq->FramesLeft = (BYTE)(ADPtr->NumFrames - 1);
if (pSeq->Direction == DOWN_DIR)
pSeq->Direction = UP_DIR;
else if (ADPtr->AnimFlags & YOYO_ANIM)
pSeq->Direction = DOWN_DIR;
else
{
++pSeq->FramesLeft;
if (pSeq->AnimType == COLOR_ANIM)
pSeq->AnimObj.CurCMap = SetRelColorMapIndex (
pSeq->AnimObj.CurCMap,
(SWORD) (-pSeq->FramesLeft));
else
{
pSeq->AnimObj.CurFrame = SetRelFrameIndex (
pSeq->AnimObj.CurFrame,
(SWORD) (-pSeq->FramesLeft));
}
}
}
if (ADPtr->AnimFlags & RANDOM_ANIM)
{
COUNT nextIndex = ADPtr->StartIndex +
(TFB_Random () % ADPtr->NumFrames);
if (pSeq->AnimType == COLOR_ANIM)
pSeq->AnimObj.CurCMap = SetAbsColorMapIndex (
pSeq->AnimObj.CurCMap, nextIndex);
else
pSeq->AnimObj.CurFrame = SetAbsFrameIndex (
pSeq->AnimObj.CurFrame, nextIndex);
}
else if (pSeq->AnimType == COLOR_ANIM)
{
if (pSeq->Direction == UP_DIR)
pSeq->AnimObj.CurCMap = SetRelColorMapIndex (
pSeq->AnimObj.CurCMap, 1);
else
pSeq->AnimObj.CurCMap = SetRelColorMapIndex (
pSeq->AnimObj.CurCMap, -1);
}
else
{
if (pSeq->Direction == UP_DIR)
pSeq->AnimObj.CurFrame =
IncFrameIndex (pSeq->AnimObj.CurFrame);
else
pSeq->AnimObj.CurFrame =
DecFrameIndex (pSeq->AnimObj.CurFrame);
}
} }
if (pSeq->AnimType == PICTURE_ANIM if (pSeq->AnimType == PICTURE_ANIM && pSeq->Direction != NO_DIR
&& (ADPtr->AnimFlags && conflictsWithTalkingAnim (pSeq))
& CommData.AlienTalkDesc.AnimFlags & WAIT_TALKING)
&& pSeq->Direction != NO_DIR)
{ {
// This is a picture animation which would conflict with // We want to talk, but this is a running picture animation
// the talking animation, and it is not stopped yet. // which conflicts with the talking animation
COUNT index; // See if it is safe to stop it now.
if (animAtNeutralIndex (pSeq))
CanTalk = FALSE; { // pause the animation
if (!(pSeq->Direction != UP_DIR
|| (index = GetFrameIndex (pSeq->AnimObj.CurFrame)) >
ADPtr->StartIndex + 1
|| (index == ADPtr->StartIndex + 1
&& (ADPtr->AnimFlags & CIRCULAR_ANIM))))
pSeq->Direction = NO_DIR; pSeq->Direction = NO_DIR;
ActiveMask &= ~ActiveBit;
}
else
{ // Otherwise, let the animation run until it's safe
CanTalk = FALSE;
}
} }
} }
ADPtr = &CommData.AlienTalkDesc; // Process the talking and transition animations
if (CanTalk if (CanTalk && haveTalkingAnim () && runningTalkingAnim ())
&& ADPtr->NumFrames
&& (ADPtr->AnimFlags & WAIT_TALKING)
&& !(CommData.AlienTransitionDesc.AnimFlags & PAUSE_TALKING))
{ {
BOOLEAN done = FALSE; BOOLEAN done = FALSE;
for (i = 0; i < CommData.NumAnimations; i++)
if (ActiveMask & (1L << i)
&& CommData.AlienAmbientArray[i].AnimFlags & WAIT_TALKING)
done = TRUE;
if (!done)
{
if (ADPtr->StartIndex != TalkBuffer.StartIndex) if (signaledStopTalkingAnim () && haveTransitionAnim ())
{ { // Run the transition. We will clear everything
Change = TRUE; // when it is done
ResetTalkFrame = SetAbsFrameIndex (CommFrame, CommData.AlienTransitionDesc.AnimFlags |= TALK_DONE;
TalkBuffer.StartIndex);
TalkBuffer = CommData.AlienTalkDesc;
}
if ((long)TalkAlarm > (long)ElapsedTicks)
TalkAlarm -= (SIZE)ElapsedTicks;
else
{
BYTE AFlags;
SIZE FrameRate;
if (TalkAlarm > 0)
TalkAlarm = 0;
else
TalkAlarm = -1;
AFlags = ADPtr->AnimFlags;
if (!(AFlags & (TALK_INTRO | TALK_DONE)))
{
FrameRate =
ADPtr->BaseFrameRate
+ ((COUNT)TFB_Random ()
% (ADPtr->RandomFrameRate + 1));
if (TalkAlarm < 0
|| GetFrameIndex (TalkFrame) ==
ADPtr->StartIndex)
{
TalkFrame = SetAbsFrameIndex (CommFrame,
(COUNT) (ADPtr->StartIndex + 1
+ ((COUNT)TFB_Random ()
% (ADPtr->NumFrames - 1))));
FrameRate += ADPtr->BaseRestartRate
+ ((COUNT)TFB_Random ()
% (ADPtr->RandomRestartRate + 1));
}
else
{
TalkFrame = SetAbsFrameIndex (CommFrame,
ADPtr->StartIndex);
if (ADPtr->AnimFlags & PAUSE_TALKING)
{
if (!(CommData.AlienTransitionDesc.AnimFlags
& TALK_DONE))
{
CommData.AlienTransitionDesc.AnimFlags |=
PAUSE_TALKING;
ADPtr->AnimFlags &= ~PAUSE_TALKING;
}
else if (CommData.AlienTransitionDesc.NumFrames)
ADPtr->AnimFlags |= TALK_DONE;
else
ADPtr->AnimFlags &=
~(WAIT_TALKING | PAUSE_TALKING);
FrameRate = 0;
}
}
}
else
{
ADPtr = &CommData.AlienTransitionDesc;
if (AFlags & TALK_INTRO)
{
FrameRate = ADPtr->BaseFrameRate
+ ((COUNT)TFB_Random ()
% (ADPtr->RandomFrameRate + 1));
if (TalkAlarm < 0 || TransitionDone)
{
TalkFrame = SetAbsFrameIndex (CommFrame,
ADPtr->StartIndex);
TransitionDone = FALSE;
}
else
TalkFrame = IncFrameIndex (TalkFrame);
if ((BYTE)(GetFrameIndex (TalkFrame)
- ADPtr->StartIndex + 1) ==
ADPtr->NumFrames)
{
CommData.AlienTalkDesc.AnimFlags &=
~TALK_INTRO;
TransitionDone = TRUE;
}
TransitionFrame = TalkFrame;
}
else /* if (AFlags & TALK_DONE) */
{
FrameRate = ADPtr->BaseFrameRate
+ ((COUNT)TFB_Random ()
% (ADPtr->RandomFrameRate + 1));
if (TalkAlarm < 0)
TalkFrame = SetAbsFrameIndex (CommFrame,
(COUNT) (ADPtr->StartIndex
+ ADPtr->NumFrames - 1));
else
TalkFrame = DecFrameIndex (TalkFrame);
if (GetFrameIndex (TalkFrame) ==
ADPtr->StartIndex)
{
CommData.AlienTalkDesc.AnimFlags &=
~(PAUSE_TALKING | TALK_DONE);
if (ADPtr->AnimFlags & TALK_INTRO)
CommData.AlienTalkDesc.AnimFlags &=
~WAIT_TALKING;
else
{
ADPtr->AnimFlags |= PAUSE_TALKING;
ADPtr->AnimFlags &= ~TALK_DONE;
}
FrameRate = 0;
}
TransitionFrame = NULL;
}
}
TalkFrameChanged = TRUE;
Change = TRUE;
TalkAlarm = FrameRate;
}
} }
else if (done && (ADPtr->AnimFlags & PAUSE_TALKING))
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)
{ {
ADPtr->AnimFlags &= ~(WAIT_TALKING | PAUSE_TALKING); clearRunTalkingAnim ();
clearStopTalkingAnim ();
} }
} }
else
{ // Not talking (task may be paused)
// Disable talking anim if it is done
if (Talk->Direction == NO_DIR)
TalkDesc.AnimFlags |= ANIM_DISABLED;
}
// Draw all animations
if (!PauseAnimTask) if (!PauseAnimTask)
{ {
CONTEXT OldContext; CONTEXT OldContext;
BOOLEAN CheckSub = FALSE;
BOOLEAN SubtitleChange; BOOLEAN SubtitleChange;
BOOLEAN FullRedraw;
BOOLEAN Change;
// Clearing any active subtitles counts as 'change' // Clearing any active subtitles counts as 'change'
SubtitleChange = HaveSubtitlesChanged (); SubtitleChange = HaveSubtitlesChanged ();
FullRedraw = ColorChange || SubtitleChange;
OldContext = SetContext (TaskContext); OldContext = SetContext (TaskContext);
if (ColorChange || ClearSummary) // Colormap animations are processed separately
{ // Redraw the whole comm screen // from picture anims (see XFormColorMap_step)
FRAME F; ProcessColormapAnims (Sequences + FirstAmbient,
F = CommData.AlienFrame; CommData.NumAnimations);
CommData.AlienFrame = CommFrame;
DrawAlienFrame (TalkFrame,
&Sequencer[CommData.NumAnimations - 1]);
CommData.AlienFrame = F;
CheckSub = TRUE;
SubtitleChange = ClearSummary;
ColorChange = FALSE;
ClearSummary = FALSE;
}
if (Change || SubtitleChange)
{
STAMP s;
s.origin.x = -SAFE_X;
s.origin.y = 0;
if (SubtitleChange)
{
s.frame = CommFrame;
DrawStamp (&s);
}
i = CommData.NumAnimations;
while (i--)
{
if (SubtitleChange || FrameChanged[i])
{
s.frame = AnimFrame[i];
DrawStamp (&s);
FrameChanged[i] = 0;
}
}
if (SubtitleChange && TransitionFrame)
{
s.frame = TransitionFrame;
DrawStamp (&s);
}
if (ResetTalkFrame)
{
s.frame = ResetTalkFrame;
DrawStamp (&s);
ResetTalkFrame = NULL;
}
if (TalkFrame && TalkFrameChanged)
{
s.frame = TalkFrame;
DrawStamp (&s);
TalkFrameChanged = FALSE;
}
Change = FALSE;
CheckSub = TRUE;
}
if (CheckSub) Change = DrawAlienFrame (Sequences, TotalSequences, FullRedraw);
if (FullRedraw || Change)
RedrawSubtitles (); RedrawSubtitles ();
SetContext (OldContext); SetContext (OldContext);
ColorChange = FALSE;
} }
if (LastOscillTime + (ONE_SECOND / OSCILLOSCOPE_RATE) < CurTime) if (LastOscillTime + (ONE_SECOND / OSCILLOSCOPE_RATE) < CurTime)
{ {
LastOscillTime = CurTime; LastOscillTime = CurTime;
UpdateSpeechGraphics (FALSE); UpdateSpeechGraphics (FALSE);
} }
UnbatchGraphics (); UnbatchGraphics ();
UnlockMutex (GraphicsLock); UnlockMutex (GraphicsLock);
ColorChange = XFormColorMap_step (); ColorChange = XFormColorMap_step ();
} }
FinishTask (task); FinishTask (task);
@@ -539,4 +577,68 @@ StartCommAnimTask (void)
return AssignTask (ambient_anim_task, 3072, "ambient animations"); return AssignTask (ambient_anim_task, 3072, "ambient animations");
} }
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;
}
+81 -12
View File
@@ -21,14 +21,81 @@
#include "libs/gfxlib.h" #include "libs/gfxlib.h"
#include "libs/tasklib.h" #include "libs/tasklib.h"
// Some background: every animation has a neutral frame which returns
// the image to the state it was in before the animation began. Which
// frame is neutral depends on the animation type.
// Animation types:
#define RANDOM_ANIM (1 << 0)
// The next index is randomly chosen.
// The first frame is neutral
#define CIRCULAR_ANIM (1 << 1)
// After the last index has been reached, the animation starts over.
// The last frame is neutral. This complicates the animation task.
#define YOYO_ANIM (1 << 2)
// After the last index has been reached, the order that the
// animation frames are used is reversed.
// The first frame is neutral
#define ANIM_MASK (RANDOM_ANIM | CIRCULAR_ANIM | YOYO_ANIM)
// Mask of all animation types.
// Static frames do not have any of these flags set.
#define WAIT_TALKING (1 << 3)
// This is set in AlienTalkDesc when the talking animation is active
// or should be active.
// In AlienAmbientArray, this is set for those ambient animations
// which can not be active while the talking animation is active.
// Such animations stop at the end of the current animation cycle
// when the talking animation activates.
#define PAUSE_TALKING (1 << 4)
// Set in AlienTalkDesc when we do not want the talking animation
#define TALK_INTRO (1 << 5)
// In AlienTransitionDesc: indicates a transition to talking state
#define TALK_DONE (1 << 6)
// In AlienTransitionDesc: indicates a transition to silent state
// In AlienTalkDesc: signals the end of talking animation
#define ANIM_DISABLED (1 << 7)
#define COLORXFORM_ANIM PAUSE_TALKING
typedef struct
{
COUNT StartIndex;
// Index of the first image (for image animation) or
// index of the first color map (for palette animation)
BYTE NumFrames;
// Number of frames in the animation.
BYTE AnimFlags;
// One of RANDOM_ANIM, CIRCULAR_ANIM, or YOYO_ANIM
// plus flags (WAIT_TALKING, ANIM_DISABLED)
COUNT BaseFrameRate;
// Minimum interframe delay
COUNT RandomFrameRate;
// Maximum additional interframe delay
// Actual delay: BaseFrameRate + Random(0..RandomFrameRate)
COUNT BaseRestartRate;
// Minimum delay before restarting animation
COUNT RandomRestartRate;
// Maximum additional delay before restarting animation
// Actual delay: BaseRestartRate + Random(0..RandomRestartRate)
DWORD BlockMask;
// Bit mask of the indices of all animations that can not
// be active at the same time as this animation, usually,
// due to the image overlap conflicts.
} ANIMATION_DESC;
#define MAX_ANIMATIONS 20
#ifdef COMM_INTERNAL #ifdef COMM_INTERNAL
typedef enum typedef enum
{ {
UP_DIR, DOWN_DIR = -1, // Animation indices are decreasing
// Animation indices are increasing NO_DIR = 0,
DOWN_DIR, UP_DIR = 1, // Animation indices are increasing
// Animation indices are decreasing
NO_DIR
} ANIM_DIR; } ANIM_DIR;
typedef enum typedef enum
@@ -42,20 +109,22 @@ typedef enum
// Describes an active animation. // Describes an active animation.
struct SEQUENCE struct SEQUENCE
{ {
COUNT Alarm; ANIMATION_DESC *ADPtr;
DWORD Alarm;
ANIM_DIR Direction; ANIM_DIR Direction;
BYTE FramesLeft; COUNT CurIndex;
COUNT NextIndex;
COUNT FramesLeft;
ANIM_TYPE AnimType; ANIM_TYPE AnimType;
union BOOLEAN Change;
{
FRAME CurFrame;
COLORMAP CurCMap;
} AnimObj;
}; };
#endif #endif
typedef struct SEQUENCE SEQUENCE; typedef struct SEQUENCE SEQUENCE;
// Returns TRUE if there was an animation change
extern BOOLEAN DrawAlienFrame (SEQUENCE *pSeq, COUNT Num, BOOLEAN fullRedraw);
void UpdateSpeechGraphics (BOOLEAN Initialize); void UpdateSpeechGraphics (BOOLEAN Initialize);
Task StartCommAnimTask(void); Task StartCommAnimTask(void);
+6 -48
View File
@@ -24,56 +24,9 @@
#include "libs/reslib.h" #include "libs/reslib.h"
#include "sis.h" #include "sis.h"
#include "velocity.h" #include "velocity.h"
#include "commanim.h"
// XXX: the following should be moved to commanim.h
// Animation types:
#define RANDOM_ANIM (1 << 0)
// The next index is randomly chosen.
#define CIRCULAR_ANIM (1 << 1)
// After the last index has been reached, the animation starts over.
#define YOYO_ANIM (1 << 2)
// After the last index has been reached, the order that the
// animation frames are used is reversed.
#define ANIM_MASK (RANDOM_ANIM | CIRCULAR_ANIM | YOYO_ANIM)
// Mask of all animation types.
#define WAIT_TALKING (1 << 3)
// This is set in AlienTalkDesc when the ambient animations should
// stop at the end of the current animation cycle.
// In AlienAmbientArray, this is set for those ambient animations
// which can not be active while the talking animation is active.
#define PAUSE_TALKING (1 << 4)
#define TALK_INTRO (1 << 5)
#define TALK_DONE (1 << 6)
#define ANIM_DISABLED (1 << 7)
#define COLORXFORM_ANIM PAUSE_TALKING
typedef struct
{
COUNT StartIndex;
// Index of the first image (for image animation) or
// index of the first color map (for palette animation)
BYTE NumFrames;
// Number of frames in the animation.
BYTE AnimFlags;
// One of RANDOM_ANIM, CIRCULAR_ANIM, or YOYO_ANIM
COUNT BaseFrameRate;
COUNT RandomFrameRate;
COUNT BaseRestartRate;
COUNT RandomRestartRate;
DWORD BlockMask;
// Bit mask of the indices of all animations that can not
// be active at the same time as this animation.
} ANIMATION_DESC;
#define MAX_ANIMATIONS 20
// general numbers-speech generator info // general numbers-speech generator info
// should accomodate most common base-10 languages // should accomodate most common base-10 languages
// many languages require various plural forms // many languages require various plural forms
@@ -160,7 +113,12 @@ typedef struct
COUNT NumAnimations; COUNT NumAnimations;
ANIMATION_DESC AlienAmbientArray[MAX_ANIMATIONS]; ANIMATION_DESC AlienAmbientArray[MAX_ANIMATIONS];
// Transition animation to/from talking state;
// the first frame is neutral (sort of like YOYO_ANIM)
ANIMATION_DESC AlienTransitionDesc; ANIMATION_DESC AlienTransitionDesc;
// Talking animation, like RANDOM_ANIM, except random frames
// always alternate with a neutral frame;
// the first frame is neutral
ANIMATION_DESC AlienTalkDesc; ANIMATION_DESC AlienTalkDesc;
NUMBER_SPEECH AlienNumberSpeech; NUMBER_SPEECH AlienNumberSpeech;