You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

974 lines
26 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
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 3 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, see <http://www.gnu.org/licenses/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plAvCallbackAction.h" // havok-contaminated file: must go first
// singular
#include "plAvBrainGeneric.h"
// local
#include "plAnimStage.h"
#include "plArmatureMod.h"
// #include "plAvatarTasks.h"
#include "plAvTask.h"
#include "plAvTaskBrain.h"
#include "plAvBrainHuman.h"
#include "plAGAnimInstance.h"
#include "plMatrixChannel.h"
// global
#include "hsTimer.h"
#include "plgDispatch.h"
// other
#include "../pnNetCommon/plSDLTypes.h"
#include "../pnMessage/plCameraMsg.h"
#include "../pnMessage/plNotifyMsg.h"
#include "../plMessage/plAvatarMsg.h"
#include "../plMessage/plInputEventMsg.h"
#include "../plMessage/plSimStateMsg.h"
#include "../plMessage/plConsoleMsg.h"
#include "../plPipeline/plDebugText.h"
#include "../plInputCore/plAvatarInputInterface.h"
#include "../plMessage/plInputIfaceMgrMsg.h"
#ifdef DEBUG_MULTISTAGE
#include "plAvatarMgr.h"
#include "../plStatusLog/plStatusLog.h"
#endif
hsBool plAvBrainGeneric::fForce3rdPerson = true;
const hsScalar plAvBrainGeneric::kDefaultFadeIn = 6.f; // 1/6th of a second to fade in
const hsScalar plAvBrainGeneric::kDefaultFadeOut = 0.f; // instant fade out.
// plAvBrainGeneric ----------------
// -----------------
plAvBrainGeneric::plAvBrainGeneric()
: fRecipient(nil),
fStages(TRACKED_NEW plAnimStageVec),
fCurStage(0),
fType(kGeneric),
fExitFlags(kExitNormal),
fMode(kEntering),
fForward(true),
fStartMessage(nil),
fEndMessage(nil),
fFadeIn(0.0f),
fFadeOut(0.0f),
fMoveMode(kMoveRelative),
fCallbackAction(nil),
fBodyUsage(plAGAnim::kBodyUnknown)
{
}
// plAvBrainGeneric --------------------------------------
// -----------------
plAvBrainGeneric::plAvBrainGeneric(plAnimStageVec *stages,
plMessage *startMessage,
plMessage *endMessage,
plKey recipient,
UInt32 exitFlags,
float fadeIn,
float fadeOut,
MoveMode moveMode)
: plArmatureBrain(),
fRecipient(recipient),
fStages(stages),
fCurStage(0),
fType(kGeneric),
fExitFlags(exitFlags),
fMode(kEntering),
fForward(true),
fStartMessage(startMessage),
fEndMessage(endMessage),
fFadeIn(fadeIn),
fFadeOut(fadeOut),
fMoveMode(moveMode),
fCallbackAction(nil),
fBodyUsage(plAGAnim::kBodyUnknown)
{
}
// plAvBrainGeneric
plAvBrainGeneric::plAvBrainGeneric(UInt32 exitFlags, float fadeIn, float fadeOut, MoveMode moveMode)
: fRecipient(nil),
fStages(nil),
fCurStage(0),
fType(kGeneric),
fExitFlags(exitFlags),
fMode(kEntering),
fForward(true),
fStartMessage(nil),
fEndMessage(nil),
fFadeIn(fadeIn),
fFadeOut(fadeOut),
fMoveMode(moveMode),
fCallbackAction(nil),
fBodyUsage(plAGAnim::kBodyUnknown)
{
}
// ~plAvBrainGeneric ----------------
// ------------------
plAvBrainGeneric::~plAvBrainGeneric()
{
int fNumStages = fStages->size();
for(int i = 0; i < fNumStages; i++)
{
plAnimStage *stage = (*fStages)[i];
(*fStages)[i] = nil;
stage->Detach(fAvMod);
delete stage;
}
delete fStages;
}
// Activate -------------------------------------------
// ---------
void plAvBrainGeneric::Activate(plArmatureModBase *avMod)
{
plArmatureBrain::Activate(avMod);
if ((GetType() == kEmote || GetType() == kAFK || GetType() == kSitOnGround) && fAvMod->IsLocalAvatar())
{
plInputIfaceMgrMsg* msg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kDisableClickables );
plgDispatch::MsgSend(msg);
}
int numStages = fStages->size();
if (!numStages)
return;
plAnimStage *stage = (*fStages)[fCurStage];
bool useFadeIn = fFadeIn > 0.0f;
float initialBlend = useFadeIn ? 0.0f : 1.0f;
if (GetType() == kEmote)
((plArmatureMod*)avMod)->SendBehaviorNotify(plHBehavior::kBehaviorTypeEmote,true);
double worldTime = hsTimer::GetSysSeconds();
if (fMoveMode == kMoveRelative || fMoveMode == kMoveAbsolute)
{
// enable kinematic... ignore outside forces... but still collide with detector regions
fAvMod->EnablePhysicsKinematic( true );
}
else if(fMoveMode == kMoveStandstill)
{
// Avatar stands still automatically now, so we do nothing here
}
if (stage->Attach(fAvMod, this, initialBlend, worldTime))
{
if(fStartMessage)
{
fStartMessage->Send();
fStartMessage = nil;
}
if (plAvBrainGeneric::fForce3rdPerson && fAvMod->IsLocalAvatar())
{
// create message to force 3rd person mode
plCameraMsg* pMsg = TRACKED_NEW plCameraMsg;
pMsg->SetBCastFlag(plMessage::kBCastByExactType);
pMsg->SetCmd(plCameraMsg::kResponderSetThirdPerson);
pMsg->SetBCastFlag(plMessage::kNetPropagate, false);
plgDispatch::MsgSend( pMsg ); // whoosh... off it goes
}
}
if (fType == kLadder && fAvMod->IsLocalAvatar())
plAvatarInputInterface::GetInstance()->SetLadderMode();
if (fReverseFBControlsOnRelease)
fAvMod->SetReverseFBOnIdle(true);
}
hsBool plAvBrainGeneric::IsRunningTask()
{
if ( fStages->size() > 0 )
return true;
return false;
}
bool plAvBrainGeneric::MatchAnimNames(const char *names[], int count)
{
if (count != GetStageCount())
return false;
int i;
for (i = 0; i < count; i++)
{
if (strcmp(names[i], GetStage(i)->GetAnimName()))
return false; // different names.
}
return true;
}
// Apply ----------------------------------------------------
// ------
hsBool plAvBrainGeneric::Apply(double time, hsScalar elapsed)
{
hsBool result = false;
switch(fMode)
{
case kAbort:
break;
case kEntering:
case kFadingIn:
result = IProcessFadeIn(time, elapsed);
break;
case kExit:
case kFadingOut:
// go through the fade logic whether or not we actually need a fade;
// centralizes some exit conditions.
result = IProcessFadeOut(time, elapsed);
break;
case kNormal:
result = IProcessNormal(time, elapsed);
break;
}
plArmatureBrain::Apply(time, elapsed);
return result;
}
// Deactivate -----------------------
// -----------
void plAvBrainGeneric::Deactivate()
{
if (fEndMessage)
{
fEndMessage->Send();
fEndMessage = nil;
}
if (fMode != kAbort) // we're being forcibly removed...
IExitMoveMode();
if (fMoveMode == kMoveRelative || fMoveMode == kMoveAbsolute)
{
// re-enable normal physics... outside forces affect us
fAvMod->EnablePhysicsKinematic( false );
}
else if (fMoveMode == kMoveStandstill)
{
// Avatar stands still automaticaly now, so we do nothing here
}
if (fType == plAvBrainGeneric::kLadder && fAvMod->IsLocalAvatar())
{
plAvatarInputInterface::GetInstance()->ClearLadderMode();
}
if (fReverseFBControlsOnRelease)
fAvMod->SetReverseFBOnIdle(false);
if (plAvBrainGeneric::fForce3rdPerson && fAvMod->IsLocalAvatar())
{
// create message to force 3rd person mode
plCameraMsg* pMsg = TRACKED_NEW plCameraMsg;
pMsg->SetBCastFlag(plMessage::kBCastByExactType);
pMsg->SetBCastFlag(plMessage::kNetPropagate, false);
pMsg->SetCmd(plCameraMsg::kResponderUndoThirdPerson);
plgDispatch::MsgSend( pMsg ); // whoosh... off it goes
}
plArmatureBrain::Deactivate();
if ((GetType() == kEmote || GetType() == kAFK || GetType() == kSitOnGround) && fAvMod->IsLocalAvatar())
{
plInputIfaceMgrMsg* msg = TRACKED_NEW plInputIfaceMgrMsg(plInputIfaceMgrMsg::kEnableClickables );
plgDispatch::MsgSend(msg);
}
}
// GETRECIPIENT
plKey plAvBrainGeneric::GetRecipient()
{
return fRecipient;
}
// SETRECIPIENT
void plAvBrainGeneric::SetRecipient(const plKey &recipient)
{
fRecipient = recipient;
}
// RELAYNOTIFYMSG
bool plAvBrainGeneric::RelayNotifyMsg(plNotifyMsg *msg)
{
if(fRecipient)
{
msg->AddReceiver(fRecipient);
msg->Send();
return true;
} else {
return false;
}
}
// IGetAnimDelta ------------------------------------------------
// --------------
float plAvBrainGeneric::IGetAnimDelta(double time, float elapsed)
{
float delta = 0.0f;
plAnimStage *curStage = (*fStages)[fCurStage];
plAnimStage::ForwardType forward = curStage->GetForwardType();
plAnimStage::BackType back = curStage->GetBackType();
bool fwdIsDown = (fReverseFBControlsOnRelease && fAvMod->IsFBReversed()) ? fAvMod->BackwardKeyDown() : fAvMod->ForwardKeyDown();
hsBool backIsDown = (fReverseFBControlsOnRelease && fAvMod->IsFBReversed()) ? fAvMod->ForwardKeyDown() : fAvMod->BackwardKeyDown();
// forward with a key down gets top priority
if(forward == plAnimStage::kForwardKey && fwdIsDown)
{
// key drive forward, forward key is down
delta = elapsed;
fForward = true;
} else if(back == plAnimStage::kBackKey && backIsDown)
{
// key drive back, back key is down
delta = -elapsed;
fForward = false;
} else if (forward == plAnimStage::kForwardAuto && fForward)
{
// auto drive forward
delta = elapsed;
} else if (back == plAnimStage::kBackAuto && ! fForward)
{
// auto drive backward
delta = -elapsed;
}
return delta;
}
// IProcessNormal -------------------------------------------------
// ---------------
hsBool plAvBrainGeneric::IProcessNormal(double time, float elapsed)
{
plAnimStage *curStage = (*fStages)[fCurStage];
if(curStage)
{
float animDelta = IGetAnimDelta(time, elapsed); // how far to move the anim (may be negative)
float overage;
hsBool done = curStage->MoveRelative(time, animDelta, overage, fAvMod);
if(done)
{
bool forward = animDelta > 0.0f;
int nextStage = forward ? curStage->GetNextStage(fCurStage) : curStage->GetPrevStage(fCurStage);
if((nextStage == -1) || (nextStage >= fStages->size()))
{
// ran off one end; we're done.
fMode = kExit;
} else {
ISwitchStages(fCurStage, nextStage, overage, false, 0.0f, 1.0f, -1.0f, time);
}
}
return true;
} else {
// current stage is missing; abort
return false;
}
}
// IProcessFadeIn -------------------------------------------------
// ---------------
hsBool plAvBrainGeneric::IProcessFadeIn(double time, float elapsed)
{
plAnimStage *curStage = (*fStages)[fCurStage];
if(fMode != kFadingIn)
{
bool needFade = fFadeIn != 0.0f;
if(fFadeIn == 0.0f)
{
IEnterMoveMode(time); // if fadeIn's not zero, we have to wait until fade's done
// before animating
} else {
curStage->GetAnimInstance()->Fade(1.0f, fFadeIn);
}
fMode = kFadingIn;
} else {
float curBlend = curStage->GetAnimInstance()->GetBlend();
if(curBlend == 1.0f)
{
IEnterMoveMode(time);
}
}
return true;
}
// IProcessFadeOut -------------------------------------------------
// ----------------
hsBool plAvBrainGeneric::IProcessFadeOut(double time, float elapsed)
{
plAnimStage *curStage = (*fStages)[fCurStage];
if(fMode != kFadingOut)
{
// haven't actually started fading; see if we need to
if(fFadeOut > 0.0f)
{
plAGAnimInstance *curAnim = curStage->GetAnimInstance();
if(curAnim)
{
curStage->GetAnimInstance()->Fade(0.0f, fFadeOut);
IExitMoveMode();
fMode = kFadingOut;
} else {
fMode = kAbort;
return false;
}
} else {
curStage->Detach(fAvMod);
IExitMoveMode();
fMode = kAbort;
}
} else {
// already fading; just keeping looking for the anim to zero out.
float curBlend = curStage->GetAnimInstance()->GetBlend();
if(curBlend == 0.0f)
{
curStage->Detach(fAvMod);
fMode = kAbort;
}
}
return true;
}
// ISwitchStages ---------------------------------------------------------------------------------------------------
// --------------
hsBool plAvBrainGeneric::ISwitchStages(int oldStageNum, int newStageNum, float delta, hsBool setTime, float newTime,
float fadeNew, hsScalar fadeOld, double worldTime)
{
#ifdef DEBUG_MULTISTAGE
char sbuf[256];
sprintf(sbuf,"ISwitchStage - old=%d new=%d (fCurStage=%d)",oldStageNum,newStageNum,fCurStage);
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
#endif
if(oldStageNum != newStageNum) {
plAnimStage *newStage = fStages->at(newStageNum);
plAnimStage *oldStage = fStages->at(oldStageNum);
if(setTime)
newStage->SetLocalTime(newTime);
hsAssert(oldStageNum < fStages->size(), "PLAVBRAINGENERIC: Stage out of range.");
oldStage->Detach(fAvMod);
newStage->Attach(fAvMod, this, 1.0f, worldTime);
fCurStage = newStageNum;
fAvMod->DirtySynchState(kSDLAvatar, 0); // write our new stage to the server
}
if(setTime) {
plAnimStage *curStage = fStages->at(fCurStage);
curStage->SetLocalTime(newTime);
}
if(fMoveMode == kMoveRelative)
fAvMod->GetRootAnimator()->Reset(worldTime);
return true;
}
void plAvBrainGeneric::IEnterMoveMode(double time)
{
if(fMoveMode == kMoveRelative)
{
fAvMod->GetRootAnimator()->Enable(true);
fAvMod->GetRootAnimator()->Reset(time);
}
fMode = kNormal;
}
void plAvBrainGeneric::IExitMoveMode()
{
if(fAvMod)
{
if(fMoveMode == kMoveRelative)
{
if(fAvMod->GetRootAnimator())
fAvMod->GetRootAnimator()->Enable(false);
}
if (fFadeOut == 0.f)
{
// if we're exiting instantly (no fade out) then the end of the animation expects to line up with
// the first frame of the idle animation, so we need to reset it.
plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(fAvMod->FindBrainByClass(plAvBrainHuman::Index()));
if (brain)
brain->ResetIdle();
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// MESSAGE HANDLING
//
/////////////////////////////////////////////////////////////////////////////////////////
// MsgReceive -------------------------------------
// -----------
hsBool plAvBrainGeneric::MsgReceive(plMessage *msg)
{
hsBool result = false;
plAvBrainGenericMsg *genMsg = plAvBrainGenericMsg::ConvertNoRef(msg);
//plAvExitModeMsg *exitMsg = plAvExitModeMsg::ConvertNoRef(msg);
plAvTaskMsg *taskMsg = plAvTaskMsg::ConvertNoRef(msg);
plControlEventMsg *ctrlMsg = plControlEventMsg::ConvertNoRef(msg);
if(genMsg)
{
result = IHandleGenBrainMsg(genMsg);
}
// else if(exitMsg) {
// fMode = kExit;
// result = true;
// }
else if (taskMsg) {
result = IHandleTaskMsg(taskMsg);
}
else if (ctrlMsg && (fExitFlags & kExitAnyInput) ) {
fMode = kExit;
}
if(result == false) // if still haven't handled msg
{
if(fMode == kExit) // if we're exiting
{
result = fAvMod->GetNextBrain(this)->MsgReceive(msg); // pass msg to next brain
} else { // otherwise
result = plArmatureBrain::MsgReceive(msg); // pass msg to base class
}
}
return result;
}
// IHandleGenBrainMsg -----------------------------------------------------
// -------------------
hsBool plAvBrainGeneric::IHandleGenBrainMsg(const plAvBrainGenericMsg *msg)
{
hsBool setTime = msg->fSetTime;
float newTime = msg->fNewTime;
hsBool setDirection = msg->fSetDirection;
bool newDirection = msg->fNewDirection ? true : false;
double worldTime = hsTimer::GetSysSeconds();
switch(msg->fType)
{
case plAvBrainGenericMsg::kGotoStage:
{
int wantStage = msg->fWhichStage;
#ifdef DEBUG_MULTISTAGE
char sbuf[256];
sprintf(sbuf,"GenericMsg - Goto Stage %d (oldstage %d)",wantStage,fCurStage);
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
#endif
if(wantStage == -1) {
fMode = kExit;
} else {
int count = fStages->size();
if(wantStage < count && wantStage >= 0)
{
ISwitchStages(fCurStage, wantStage, 0.0f, setTime, newTime, 1.0f, -1.0f, worldTime);
// direction is set within the brain, not the stage
if(setDirection)
fForward = newDirection;
}
}
}
break;
case plAvBrainGenericMsg::kNextStage:
{
int wantStage = fCurStage + 1;
#ifdef DEBUG_MULTISTAGE
char sbuf[256];
sprintf(sbuf,"GenericMsg - Next Stage %d (oldstage %d)",wantStage,fCurStage);
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
#endif
if(wantStage == fStages->size())
{
fMode = kExit; // walked off the end of the brain
} else {
ISwitchStages(fCurStage, wantStage, 0.0f, setTime, newTime, 1.0f, -1.0f, worldTime);
if(setDirection)
fForward = newDirection;
}
}
break;
case plAvBrainGenericMsg::kPrevStage:
{
int wantStage = fCurStage - 1;
#ifdef DEBUG_MULTISTAGE
char sbuf[256];
sprintf(sbuf,"GenericMsg - PrevStage %d (oldstage %d)",wantStage,fCurStage);
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
#endif
if(wantStage < 0)
{
fMode = kExit; // walked off the beginning of the brain
} else {
ISwitchStages(fCurStage, wantStage, 0.0f, setTime, 0.0f, 1.0f, -1.0f, worldTime);
if(setDirection)
fForward = newDirection;
}
}
break;
#ifdef DEBUG_MULTISTAGE
default:
{
char sbuf[256];
sprintf(sbuf,"GenericMsg - Unknown command %d ",msg->fType);
plAvatarMgr::GetInstance()->GetLog()->AddLine(sbuf);
}
break;
#endif
}
return true;
}
hsBool plAvBrainGeneric::IHandleTaskMsg(plAvTaskMsg *msg)
{
plAvTask *task = msg->GetTask();
plAvTaskBrain *brainTask = plAvTaskBrain::ConvertNoRef(task);
if(brainTask)
{
plArmatureBrain * brain = brainTask->GetBrain();
if(brain)
{
if(fExitFlags & kExitNewBrain)
{
// RULE 1: if kExitNewBrain, exit on any new brain
fMode = kExit;
return false; // we didn't consume the message
} else {
plAvBrainGeneric * gBrain = plAvBrainGeneric::ConvertNoRef(brain);
if(gBrain && IBrainIsCompatible(gBrain))
{
// RULE 2: if not kExitNewBrain and brain is compatible, apply it
QueueTask(brainTask);
return true;
} else {
if(fMode == kExit || fMode == kFadingOut)
{
// RULE 3: if brain is incompatible and we're exiting anyway,
// queue it to be next
fAvMod->GetNextBrain(this)->QueueTask(brainTask);
return true;
}
// RULE 4: if brain is incompatible and we're still running, ignore.
}
}
} else {
// no brain; it's an exit task, exit and CONSUME it.
fMode = kExit;
return true;
}
} else {
// note that this check has to come after the brain task check; if it's a brain
// task we need to examine it so we can say whether we consumed it or not.
// popbrain messages get consumed, even if we exit on any task.
if(fExitFlags & kExitAnyTask)
{
// RULE 4: if kExitAnyTask, exit on any task (but if it was an exit brain task,
// make sure to consume it )
fMode = kExit;
return false;
}
}
// RULE 4: if brain is incompatible and we're still running, ignore.
return false;
}
bool plAvBrainGeneric::IBrainIsCompatible(plAvBrainGeneric *otherBrain)
{
plAGAnim::BodyUsage otherUsage = otherBrain->GetBodyUsage();
switch(fBodyUsage)
{
case plAGAnim::kBodyUnknown:
return false;
break;
case plAGAnim::kBodyFull:
return false;
break;
case plAGAnim::kBodyUpper:
if(otherUsage == plAGAnim::kBodyLower)
return true;
else
return false;
break;
case plAGAnim::kBodyLower:
if(otherUsage == plAGAnim::kBodyUpper)
return true;
else
return false;
break;
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// READ/WRITE
//
/////////////////////////////////////////////////////////////////////////////////////////
// Write ----------------------------------------------------
// ------
void plAvBrainGeneric::Write(hsStream *stream, hsResMgr *mgr)
{
plArmatureBrain::Write(stream, mgr);
int numStages = fStages->size();
stream->WriteSwap32(numStages);
for(int i = 0; i < numStages; i++)
{
plAnimStage *stage = (*fStages)[i];
plCreatable *cre = reinterpret_cast<plCreatable *>(stage);
mgr->WriteCreatable(stream, cre); // save base state
// ** replace this with Write(..)
stage->SaveAux(stream, mgr); // save ephemeral state
}
stream->WriteSwap32(fCurStage);
stream->WriteSwap32(fType);
stream->WriteSwap32(fExitFlags);
stream->WriteByte(fMode);
stream->Writebool(fForward);
if(fStartMessage) {
stream->WriteBool(true);
mgr->WriteCreatable(stream, fStartMessage);
} else {
stream->WriteBool(false);
}
if(fEndMessage) {
stream->WriteBool(true);
mgr->WriteCreatable(stream, fEndMessage);
} else {
stream->WriteBool(false);
}
stream->WriteSwapScalar(fFadeIn);
stream->WriteSwapScalar(fFadeOut);
stream->WriteByte(fMoveMode);
stream->WriteByte(fBodyUsage);
mgr->WriteKey(stream, fRecipient);
}
// Read ----------------------------------------------------
// -----
void plAvBrainGeneric::Read(hsStream *stream, hsResMgr *mgr)
{
plArmatureBrain::Read(stream, mgr);
int numStages = stream->ReadSwap32();
for(int i = 0; i < numStages; i++)
{
plCreatable *created = mgr->ReadCreatable(stream); // load base state
plAnimStage *stage = reinterpret_cast<plAnimStage *>(created);
// Replace this with Read(..)
stage->LoadAux(stream, mgr, 0.0); // load ephemeral state
fStages->push_back(stage);
}
fCurStage = stream->ReadSwap32();
fType = static_cast<plAvBrainGeneric::BrainType>(stream->ReadSwap32());
fExitFlags = stream->ReadSwap32();
fMode = static_cast<Mode>(stream->ReadByte());
fForward = stream->Readbool();
if(stream->ReadBool()) {
fStartMessage = plMessage::ConvertNoRef(mgr->ReadCreatable(stream));
} else {
fStartMessage = nil;
}
if(stream->ReadBool()) {
fEndMessage = plMessage::ConvertNoRef(mgr->ReadCreatable(stream));
} else {
fEndMessage = nil;
}
fFadeIn = stream->ReadSwapScalar();
fFadeOut = stream->ReadSwapScalar();
fMoveMode = static_cast<MoveMode>(stream->ReadByte());
fBodyUsage = static_cast<plAGAnim::BodyUsage>(stream->ReadByte());
fRecipient = mgr->ReadKey(stream);
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// MINOR FNs / GETTERS & SETTERS
//
/////////////////////////////////////////////////////////////////////////////////////////
// LeaveAge ---------------------
// ---------
hsBool plAvBrainGeneric::LeaveAge()
{
IExitMoveMode();
fMode = kAbort;
return true;
}
// AddStage --------------------------------------
// ---------
int plAvBrainGeneric::AddStage(plAnimStage *stage)
{
if(!fStages)
fStages = TRACKED_NEW plAnimStageVec;
fStages->push_back(stage);
return fStages->size() - 1;
}
// GetStageNum --------------------------------------
// ------------
int plAvBrainGeneric::GetStageNum(plAnimStage *stage)
{
int count = fStages->size();
for(int i = 0; i < count; i++)
{
plAnimStage *any = (*fStages)[i];
if(any == stage)
{
return i;
}
}
return -1;
}
// GetCurStageNum --------------------
// ---------------
int plAvBrainGeneric::GetCurStageNum()
{
return fCurStage;
}
// GetStageCount --------------------
// --------------
int plAvBrainGeneric::GetStageCount()
{
return fStages->size();
}
// GetStage ---------------------------------------
// ---------
plAnimStage * plAvBrainGeneric::GetStage(int which)
{
return fStages->at(which);
}
// GetCurStage ------------------------------
// ------------
plAnimStage * plAvBrainGeneric::GetCurStage()
{
return fStages->at(fCurStage);
}
// SetType -------------------------------------------------------------------------------
// --------
plAvBrainGeneric::BrainType plAvBrainGeneric::SetType(plAvBrainGeneric::BrainType newType)
{
BrainType oldType = fType;
fType = newType;
return oldType;
}
// GetType --------------------------------------------
// --------
plAvBrainGeneric::BrainType plAvBrainGeneric::GetType()
{
return fType;
}
plAGAnim::BodyUsage plAvBrainGeneric::GetBodyUsage()
{
return fBodyUsage;
}
void plAvBrainGeneric::SetBodyUsage(plAGAnim::BodyUsage bodyUsage)
{
fBodyUsage = bodyUsage;
}
/////////////////////////////////////////////////////////////////////////////////////////
//
// DEBUGGING
//
/////////////////////////////////////////////////////////////////////////////////////////
// DumpToDebugDisplay ----------------------------------------------------------------------------------------
// -------------------
void plAvBrainGeneric::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt)
{
debugTxt.DrawString(x, y, "Brain type: Generic AKA Multistage");
y += lineHeight;
int stageCount = fStages->size();
for(int i = 0; i < stageCount; i++)
{
plAnimStage *stage = (*fStages)[i];
stage->DumpDebug(i == fCurStage, x, y, lineHeight, strBuf, debugTxt);
}
}