/*==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 .
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 "hsTypes.h"
#include "hsStlUtils.h"
#include "hsTimer.h"
#include "plResponderModifier.h"
#include "plResponderSDLModifier.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "plPhysical.h"
#include "../pnKeyedObject/plKey.h"
#include "../pnKeyedObject/plFixedKey.h"
#include "../pnSceneObject/plSceneObject.h"
#include "../pnMessage/plNotifyMsg.h"
#include "../pnNetCommon/plNetApp.h"
// for localOnly cmd check
#include "../plMessage/plLinkToAgeMsg.h"
#include "../pnMessage/plCameraMsg.h"
#include "../pnMessage/plSoundMsg.h"
#include "../plMessage/plResponderMsg.h"
#include "../plMessage/plAnimCmdMsg.h"
#include "../plMessage/plLinkToAgeMsg.h"
#include "../pnMessage/plSDLModifierMsg.h"
#include "../../FeatureLib/pfMessage/plArmatureEffectMsg.h"
#include "../plStatusLog/plStatusLog.h"
#include "../plMessage/plTimerCallbackMsg.h"
#include "../pnTimer/plTimerCallbackManager.h"
#include "../plMessage/plSimStateMsg.h"
//#include "../plHavok1\plHKPhysical.h"
//#include "../plHavok1\plHKSubWorld.h"
#include "../plAvatar/plArmatureMod.h"
#include "../plAvatar/plAvatarMgr.h"
//#ifdef HS_DEBUGGING
#define STATUS_LOG
//#endif
#ifdef STATUS_LOG
#define ResponderLog(x) x
#else
#define ResponderLog(x)
#endif
void plResponderEnableMsg::Read(hsStream* stream, hsResMgr* mgr)
{
plMessage::IMsgRead(stream, mgr);
fEnable = stream->Readbool();
}
void plResponderEnableMsg::Write(hsStream* stream, hsResMgr* mgr)
{
plMessage::IMsgWrite(stream, mgr);
stream->Writebool(fEnable);
}
/////////////////////////////////////////////////////
/////////////////////////////////////////////////////
plResponderModifier::plResponderModifier() :
fCurState(0),
fCurCommand(-1),
fEnabled(true),
fFlags(0),
fEnter(false),
fResponderSDLMod(nil),
fGotFirstLoad(false),
fNotifyMsgFlags(0)
{
}
plResponderModifier::~plResponderModifier()
{
delete fResponderSDLMod;
fResponderSDLMod=nil;
for (int i = 0; i < fStates.Count(); i++)
{
for (int j = 0; j < fStates[i].fCmds.Count(); j++ )
hsRefCnt_SafeUnRef(fStates[i].fCmds[j].fMsg);
}
}
hsBool plResponderModifier::MsgReceive(plMessage* msg)
{
plNotifyMsg* pNMsg = plNotifyMsg::ConvertNoRef(msg);
if (pNMsg)
{
if (pNMsg->fType == plNotifyMsg::kResponderFF)
{
ISetResponderStateFromNotify(pNMsg);
IFastForward(true);
}
else if (pNMsg->fType == plNotifyMsg::kResponderChangeState)
{
ISetResponderStateFromNotify(pNMsg);
DirtySynchState(kSDLResponder, 0);
}
else
{
// assumes state of 0 means untriggered and state of 1 is triggered
if ((pNMsg->fState != 0 && (fFlags & kDetectTrigger)) ||
(pNMsg->fState == 0 && (fFlags & kDetectUnTrigger)))
{
Trigger(pNMsg);
DirtySynchState(kSDLResponder, 0);
}
}
return true;
}
plResponderEnableMsg *pEnableMsg = plResponderEnableMsg::ConvertNoRef(msg);
if (pEnableMsg)
{
fEnabled = pEnableMsg->fEnable;
DirtySynchState(kSDLResponder, 0);
return true;
}
plEventCallbackMsg *pEventMsg = plEventCallbackMsg::ConvertNoRef(msg);
plTimerCallbackMsg *timerMsg = plTimerCallbackMsg::ConvertNoRef(msg);
if (pEventMsg || timerMsg)
{
UInt32 waitID = pEventMsg ? pEventMsg->fUser : timerMsg->fID;
if (waitID != -1)
{
// Flag that this callback completed and try sending in case any commands were waiting on this
fCompletedEvents.SetBit(waitID);
ResponderLog(ILog(plStatusLog::kWhite, "Got callback from command %d(id:%d)", ICmdFromWait((Int8)waitID)+1, waitID));
IContinueSending();
DirtySynchState(kSDLResponder, 0);
}
// The is one of the stop callbacks we generated for debug mode
else if (fDebugAnimBox)
IDebugAnimBox(false);
return true;
}
// pass sdl msg to sdlMod
plSDLModifierMsg* sdlMsg = plSDLModifierMsg::ConvertNoRef(msg);
if (sdlMsg && fResponderSDLMod)
{
if (fResponderSDLMod->MsgReceive(sdlMsg))
return true; // msg handled
}
return plSingleModifier::MsgReceive(msg);
}
void plResponderModifier::AddCommand(plMessage* pMsg, int state)
{
fStates[state].fCmds.Append(plResponderCmd(pMsg, -1));
}
void plResponderModifier::AddCallback(Int8 state, Int8 cmd, Int8 callback)
{
fStates[state].fWaitToCmd[callback] = cmd;
}
//
// Decide if this cmd should only be run locally.
// If we are triggered remotely (netRequest==true), then
// we don't want to execute localOnly cmds (like cameraChanges)
//
bool plResponderModifier::IIsLocalOnlyCmd(plMessage* cmd)
{
if (plLinkToAgeMsg::ConvertNoRef(cmd)) // don't want to page out any rooms
return true;
if (plCameraMsg::ConvertNoRef(cmd)) // don't want to change our camera
return true;
plSoundMsg *snd = plSoundMsg::ConvertNoRef( cmd );
if( snd != nil && snd->Cmd( plSoundMsg::kIsLocalOnly ) )
return true;
return false;
}
void plResponderModifier::ISetResponderState(Int8 state)
{
// make sure that it is a valid state to switch to
if (state >= 0 && state < fStates.Count())
{
fCurState = state;
}
else
{
ResponderLog(ILog(plStatusLog::kRed, "Invalid state %d specified, will default to current state", state));
}
}
void plResponderModifier::ISetResponderStateFromNotify(plNotifyMsg* msg)
{
// set the state of the responder IF they want it to be
proResponderStateEventData* event = (proResponderStateEventData*)msg->FindEventRecord(proEventData::kResponderState);
if (event != nil)
ISetResponderState((Int8)(event->fState));
}
void plResponderModifier::Trigger(plNotifyMsg *msg)
{
#if 0
char str[256];
sprintf(str, "RM: Responder %s is triggering, num cmds=%d, enabled=%d, curCmd=%d, t=%f\n",
GetKeyName(), fStates[fCurState].fCmds.GetCount(),
((int)fEnabled), ((int)fCurCommand), hsTimer::GetSysSeconds());
plNetClientApp::GetInstance()->DebugMsg(str);
#endif
// If we're not in the middle of sending, reset and start sending commands
if (fCurCommand == Int8(-1) && fEnabled)
{
ResponderLog(ILog(plStatusLog::kGreen, "Trigger"));
fNotifyMsgFlags = msg->GetAllBCastFlags();
fTriggerer = msg->GetSender();
fPlayerKey = msg->GetAvatarKey();
ISetResponderStateFromNotify(msg);
proCollisionEventData *cEvent = (proCollisionEventData *)msg->FindEventRecord(proEventData::kCollision);
fEnter = (cEvent ? cEvent->fEnter : false);
fCompletedEvents.Reset();
fCurCommand = 0;
DirtySynchState(kSDLResponder, 0);
IContinueSending();
}
else
{
ResponderLog(ILog(plStatusLog::kRed, "Rejected Trigger, %s", !fEnabled ? "responder disabled" : "responder is running"));
}
}
bool plResponderModifier::IContinueSending()
{
// If we haven't been started, exit
if (fCurCommand == Int8(-1))
return false;
plResponderState& state = fStates[fCurState];
while (fCurCommand < state.fCmds.Count())
{
plMessage *msg = state.fCmds[fCurCommand].fMsg;
if (msg)
{
// If this command needs to wait, and it's condition hasn't been met yet, exit
Int8 wait = state.fCmds[fCurCommand].fWaitOn;
if (wait != -1 && !fCompletedEvents.IsBitSet(wait))
{
ResponderLog(ILog(plStatusLog::kWhite, "Command %d is waiting for command %d(id:%d)", Int8(fCurCommand)+1, ICmdFromWait(wait)+1, wait));
return false;
}
if (!(fNotifyMsgFlags & plMessage::kNetNonLocal)|| !IIsLocalOnlyCmd(msg))
{
// make sure outgoing msgs inherit net flags as part of cascade
UInt32 msgFlags = msg->GetAllBCastFlags();
plNetClientApp::InheritNetMsgFlags(fNotifyMsgFlags, &msgFlags, true);
msg->SetAllBCastFlags(msgFlags);
// If this is a responder message, let it know which player triggered this
if (plResponderMsg* responderMsg = plResponderMsg::ConvertNoRef(msg))
{
responderMsg->fPlayerKey = fPlayerKey;
}
else if (plNotifyMsg* notifyMsg = plNotifyMsg::ConvertNoRef(msg))
{
bool foundCollision = false;
// If we find a collision event, this message is meant to trigger a multistage
for (int i = 0; i < notifyMsg->GetEventCount(); i++)
{
proEventData* event = notifyMsg->GetEventRecord(i);
if (event->fEventType == proEventData::kCollision)
{
proCollisionEventData* collisionEvent = (proCollisionEventData*)event;
collisionEvent->fHitter = fPlayerKey;
foundCollision = true;
}
}
// No collision event, this message is for notifying the triggerer
if (!foundCollision)
{
notifyMsg->ClearReceivers();
notifyMsg->AddReceiver(fTriggerer);
}
notifyMsg->SetSender(GetKey());
}
else if (plLinkToAgeMsg* linkMsg = plLinkToAgeMsg::ConvertNoRef(msg))
{
if (linkMsg->GetNumReceivers() == 0)
{
plUoid netUoid(kNetClientMgr_KEY);
plKey netKey = hsgResMgr::ResMgr()->FindKey(netUoid);
hsAssert(netKey,"NetClientMgr not found");
linkMsg->AddReceiver(netKey);
}
}
else if (plArmatureEffectStateMsg* stateMsg = plArmatureEffectStateMsg::ConvertNoRef(msg))
{
stateMsg->ClearReceivers();
stateMsg->AddReceiver(fPlayerKey);
stateMsg->fAddSurface = fEnter;
}
else if (plSubWorldMsg* swMsg = plSubWorldMsg::ConvertNoRef(msg))
{
plArmatureMod *avatar = plAvatarMgr::GetInstance()->GetLocalAvatar();
if(avatar)
{
swMsg->AddReceiver(avatar->GetKey());
}
}
// If we're in anim debug mode, check if this is an anim play
// message so we can put up the cue
if (fDebugAnimBox)
{
plAnimCmdMsg* animMsg = plAnimCmdMsg::ConvertNoRef(msg);
if (animMsg && animMsg->Cmd(plAnimCmdMsg::kContinue))
IDebugPlayMsg(animMsg);
}
if (plTimerCallbackMsg *timerMsg = plTimerCallbackMsg::ConvertNoRef(msg))
{
hsRefCnt_SafeRef(timerMsg);
plgTimerCallbackMgr::NewTimer(timerMsg->fTime, timerMsg);
}
else
{
hsRefCnt_SafeRef(msg);
plgDispatch::MsgSend(msg);
}
}
}
fCurCommand++;
DirtySynchState(kSDLResponder, 0);
}
// Make sure all callbacks we need to wait on are done before allowing a state switch or restart
for (int i = 0; i < state.fNumCallbacks; i++)
{
if (!fCompletedEvents.IsBitSet(i))
{
ResponderLog(ILog(plStatusLog::kWhite, "Can't reset, waiting for command %d(id:%d)", ICmdFromWait(i)+1, i));
return false;
}
}
ResponderLog(ILog(plStatusLog::kGreen, "Reset"));
fCurCommand = -1;
ISetResponderState(state.fSwitchToState);
DirtySynchState(kSDLResponder, 0);
return true;
}
Int8 plResponderModifier::ICmdFromWait(Int8 waitIdx)
{
WaitToCmd& waitToCmd = fStates[fCurState].fWaitToCmd;
if (waitToCmd.find(waitIdx) != waitToCmd.end())
return waitToCmd[waitIdx];
return -1;
}
void plResponderModifier::Restore()
{
// If we're the first player in and we're loading old state where this responder
// was running, fast forward it
if (plNetClientApp::GetInstance()->GetJoinOrder() == 0 && fCurCommand != -1 && !fGotFirstLoad)
{
fGotFirstLoad = true;
IFastForward(false);
return;
}
ResponderLog(ILog(plStatusLog::kGreen, "Load SDL State"));
fGotFirstLoad = true;
plResponderState& state = fStates[fCurState];
for (int i = 0; i < state.fNumCallbacks; i++)
{
if (!fCompletedEvents[i])
{
int cmdIdx = state.fWaitToCmd[i];
plResponderCmd& cmd = state.fCmds[cmdIdx];
//
// If it's a callback message (anim or sound), just send the callbacks again
//
plMessageWithCallbacks* callbackMsg = plMessageWithCallbacks::ConvertNoRef(cmd.fMsg);
if (callbackMsg)
{
// Create a new message for just the callbacks
plMessageWithCallbacks* newCallbackMsg = nil;
if (plAnimCmdMsg* animMsg = plAnimCmdMsg::ConvertNoRef(callbackMsg))
{
plAnimCmdMsg* newAnimMsg = TRACKED_NEW plAnimCmdMsg;
newAnimMsg->SetCmd(plAnimCmdMsg::kAddCallbacks);
newCallbackMsg = newAnimMsg;
ResponderLog(ILog(plStatusLog::kGreen, "Restoring anim callback"));
}
else if (plSoundMsg* soundMsg = plSoundMsg::ConvertNoRef(callbackMsg))
{
plSoundMsg* newSoundMsg = TRACKED_NEW plSoundMsg;
newSoundMsg->SetCmd(plSoundMsg::kAddCallbacks);
newCallbackMsg = newSoundMsg;
ResponderLog(ILog(plStatusLog::kGreen, "Restoring sound callback"));
}
// Setup the sender and receiver
newCallbackMsg->SetSender(callbackMsg->GetSender());
for (int iReceiver = 0; i < callbackMsg->GetNumReceivers(); i++)
newCallbackMsg->AddReceiver(callbackMsg->GetReceiver(iReceiver));
// Add the callbacks
int numCallbacks = callbackMsg->GetNumCallbacks();
for (int iCallback = 0; iCallback < numCallbacks; iCallback++)
{
plMessage* callback = callbackMsg->GetCallback(iCallback);
// hsRefCnt_SafeRef(callback); AddCallback will ref this for us.
newCallbackMsg->AddCallback(callback);
}
newCallbackMsg->Send();
}
}
}
}
#include "plCreatableIndex.h"
plMessage* plResponderModifier::IGetFastForwardMsg(plMessage* msg, bool python)
{
if (!msg)
return nil;
if (plAnimCmdMsg* animMsg = plAnimCmdMsg::ConvertNoRef(msg))
{
if (animMsg->Cmd(plAnimCmdMsg::kContinue) ||
animMsg->Cmd(plAnimCmdMsg::kAddCallbacks))
{
plAnimCmdMsg* newAnimMsg = TRACKED_NEW plAnimCmdMsg;
newAnimMsg->fCmd = animMsg->fCmd;
newAnimMsg->fBegin = animMsg->fBegin;
newAnimMsg->fEnd = animMsg->fEnd;
newAnimMsg->fLoopEnd = animMsg->fLoopEnd;
newAnimMsg->fLoopBegin = animMsg->fLoopBegin;
newAnimMsg->fSpeed = animMsg->fSpeed;
newAnimMsg->fSpeedChangeRate = animMsg->fSpeedChangeRate;
newAnimMsg->fTime = animMsg->fTime;
newAnimMsg->SetAnimName(animMsg->GetAnimName());
newAnimMsg->SetLoopName(animMsg->GetLoopName());
// Remove the callbacks
newAnimMsg->fCmd.SetBit(plAnimCmdMsg::kAddCallbacks, false);
if (newAnimMsg->Cmd(plAnimCmdMsg::kContinue))
{
newAnimMsg->fCmd.SetBit(plAnimCmdMsg::kContinue, false);
newAnimMsg->fCmd.SetBit(plAnimCmdMsg::kFastForward, true);
}
for (int i = 0; i < animMsg->GetNumReceivers(); i++)
newAnimMsg->AddReceiver(animMsg->GetReceiver(i));
ResponderLog(ILog(plStatusLog::kWhite, "FF Animation Play Msg"));
return newAnimMsg;
}
ResponderLog(ILog(plStatusLog::kWhite, "FF Animation Non-Play Msg"));
hsRefCnt_SafeRef(msg);
return msg;
}
else if(plSoundMsg *soundMsg = plSoundMsg::ConvertNoRef(msg))
{
if( fFlags & kSkipFFSound )
{
return nil;
}
if(soundMsg->Cmd(plSoundMsg::kPlay) ||
soundMsg->Cmd(plSoundMsg::kToggleState) ||
soundMsg->Cmd(plAnimCmdMsg::kAddCallbacks))
{
plSoundMsg *newSoundMsg = TRACKED_NEW plSoundMsg;
newSoundMsg->fCmd = soundMsg->fCmd;
newSoundMsg->fBegin = soundMsg->fBegin;
newSoundMsg->fEnd = soundMsg->fEnd;
newSoundMsg->fLoop = soundMsg->fLoop;
newSoundMsg->fSpeed = soundMsg->fSpeed;
newSoundMsg->fTime = soundMsg->fTime;
newSoundMsg->fIndex = soundMsg->fIndex;
newSoundMsg->fRepeats = soundMsg->fRepeats;
newSoundMsg->fPlaying = soundMsg->fPlaying;
newSoundMsg->fNameStr = soundMsg->fNameStr;
newSoundMsg->fVolume = soundMsg->fVolume;
// Remove the callbacks
newSoundMsg->fCmd.SetBit(plSoundMsg::kAddCallbacks, false);
if(newSoundMsg->Cmd(plSoundMsg::kPlay))
{
newSoundMsg->fCmd.SetBit(plSoundMsg::kPlay, false);
newSoundMsg->fCmd.SetBit(plSoundMsg::kFastForwardPlay);
ResponderLog(ILog(plStatusLog::kWhite, "FF Sound Play Msg"));
}
else if(newSoundMsg->Cmd(plSoundMsg::kToggleState))
{
newSoundMsg->fCmd.SetBit(plSoundMsg::kToggleState, false);
newSoundMsg->fCmd.SetBit(plSoundMsg::kFastForwardToggle);
ResponderLog(ILog(plStatusLog::kWhite, "FF Sound Toggle State Msg"));
}
for (int i = 0; i < soundMsg->GetNumReceivers(); i++)
newSoundMsg->AddReceiver(soundMsg->GetReceiver(i));
return newSoundMsg;
}
ResponderLog(ILog(plStatusLog::kWhite, "FF Sound Non-Play/Toggle Msg"));
hsRefCnt_SafeRef(msg);
return msg;
}
else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plExcludeRegionMsg))
{
ResponderLog(ILog(plStatusLog::kWhite, "FF Exclude Region Msg"));
hsRefCnt_SafeRef(msg);
return msg;
}
else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plEnableMsg))
{
ResponderLog(ILog(plStatusLog::kWhite, "FF Visibility/Detector Enable Msg"));
hsRefCnt_SafeRef(msg);
return msg;
}
else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plResponderEnableMsg))
{
ResponderLog(ILog(plStatusLog::kWhite, "FF Responder Enable Msg"));
hsRefCnt_SafeRef(msg);
return msg;
}
else if (msg->ClassIndex() == CLASS_INDEX_SCOPED(plSimSuppressMsg))
{
ResponderLog(ILog(plStatusLog::kWhite, "FF Physical Enable Msg"));
hsRefCnt_SafeRef(msg);
return msg;
}
return nil;
}
void plResponderModifier::IFastForward(bool python)
{
ResponderLog(ILog(plStatusLog::kGreen, "Fast Forward"));
fCurCommand = 0;
plResponderState& state = fStates[fCurState];
while (fCurCommand < state.fCmds.Count())
{
plMessage *msg = state.fCmds[fCurCommand].fMsg;
msg = IGetFastForwardMsg(msg, python);
if (msg)
plgDispatch::MsgSend(msg);
fCurCommand++;
}
ResponderLog(ILog(plStatusLog::kGreen, "Reset"));
fCurCommand = -1;
ISetResponderState(state.fSwitchToState);
plSynchEnabler enable(true);
DirtySynchState(kSDLResponder, 0);
}
void plResponderModifier::Read(hsStream* stream, hsResMgr* mgr)
{
plSingleModifier::Read(stream, mgr);
Int8 numStates = stream->ReadByte();
fStates.SetCount(numStates);
for (Int8 i = 0; i < numStates; i++)
{
plResponderState& state = fStates[i];
state.fNumCallbacks = stream->ReadByte();
state.fSwitchToState = stream->ReadByte();
Int8 j;
Int8 numCmds = stream->ReadByte();
state.fCmds.SetCount(numCmds);
for (j = 0; j < numCmds; j++)
{
plResponderCmd& cmd = state.fCmds[j];
plMessage* pMsg = plMessage::ConvertNoRef(mgr->ReadCreatable(stream));
cmd.fMsg = pMsg;
cmd.fWaitOn = stream->ReadByte();
}
state.fWaitToCmd.clear();
Int8 mapSize = stream->ReadByte();
for (j = 0; j < mapSize; j++)
{
Int8 wait = stream->ReadByte();
Int8 cmd = stream->ReadByte();
state.fWaitToCmd[wait] = cmd;
}
}
ISetResponderState(stream->ReadByte());
fEnabled = stream->Readbool();
fFlags = stream->ReadByte();
// attach responderSDLMod
delete fResponderSDLMod;
fResponderSDLMod = TRACKED_NEW plResponderSDLModifier;
fResponderSDLMod->SetResponder(this);
}
void plResponderModifier::Write(hsStream* stream, hsResMgr* mgr)
{
plSingleModifier::Write(stream, mgr);
Int8 numStates = fStates.GetCount();
stream->WriteByte(numStates);
for (int i = 0; i < numStates; i++)
{
plResponderState& state = fStates[i];
stream->WriteByte(state.fNumCallbacks);
stream->WriteByte(state.fSwitchToState);
Int8 numCmds = state.fCmds.GetCount();
stream->WriteByte(numCmds);
for (int j = 0; j < numCmds; j++)
{
plResponderCmd& cmd = state.fCmds[j];
mgr->WriteCreatable(stream, cmd.fMsg);
stream->WriteByte(cmd.fWaitOn);
}
Int8 mapSize = state.fWaitToCmd.size();
stream->WriteByte(mapSize);
for (WaitToCmd::iterator it = state.fWaitToCmd.begin(); it != state.fWaitToCmd.end(); it++)
{
stream->WriteByte(it->first);
stream->WriteByte(it->second);
}
}
stream->WriteByte(fCurState);
stream->Writebool(fEnabled);
stream->WriteByte(fFlags);
}
#include "../plPipeline/plDebugText.h"
bool plResponderModifier::fDebugAnimBox = false;
void plResponderModifier::IDebugAnimBox(bool start)
{
plDebugText &debugTxt = plDebugText::Instance();
UInt32 scrnWidth, scrnHeight;
debugTxt.GetScreenSize(&scrnWidth, &scrnHeight);
// Box size is 1/8 screen size
UInt32 boxSize = scrnHeight / 8;
// Draw box in lower left corner
if (start)
debugTxt.DrawRect(0, (UInt16)(scrnHeight-boxSize), (UInt16)boxSize, (UInt16)scrnHeight, 0, 255, 0);
else
debugTxt.DrawRect((UInt16)boxSize, (UInt16)(scrnHeight-boxSize), (UInt16)(boxSize*2), (UInt16)scrnHeight, 255, 0, 0);
}
void plResponderModifier::IDebugPlayMsg(plAnimCmdMsg* msg)
{
// Create a stop callback so we can do a cue for that too
plEventCallbackMsg *eventMsg = TRACKED_NEW plEventCallbackMsg;
eventMsg->AddReceiver(GetKey());
eventMsg->fRepeats = 0;
eventMsg->fUser = -1;
eventMsg->fEvent = kStop;
msg->SetCmd(plAnimCmdMsg::kAddCallbacks);
msg->AddCallback(eventMsg);
hsRefCnt_SafeUnRef(eventMsg);
IDebugAnimBox(true);
}
////////////////////////////////////////////////////////////////////////////////
#ifdef STATUS_LOG
static plStatusLog *gLog = nil;
static std::vector gNoLogStrings;
#endif // STATUS_LOG
void plResponderModifier::NoLogString(const char* str)
{
#ifdef STATUS_LOG
gNoLogStrings.push_back(str);
#endif // STATUS_LOG
}
void plResponderModifier::ILog(UInt32 color, const char* format, ...)
{
#ifdef STATUS_LOG
if (!gLog)
gLog = plStatusLogMgr::GetInstance().CreateStatusLog(15, "Responder", plStatusLog::kFilledBackground | plStatusLog::kDeleteForMe | plStatusLog::kDontWriteFile | plStatusLog::kAlignToTop);
if (!format || *format == '\0')
return;
const char* keyName = GetKeyName();
// Make sure this key isn't in our list of keys to deny
for (int i = 0; i < gNoLogStrings.size(); i++)
{
if (strncmp(gNoLogStrings[i].c_str(), keyName, gNoLogStrings[i].length()) == 0)
return;
}
// Format the log text
char buf[256];
va_list args;
va_start(args, format);
int numWritten = _vsnprintf(buf, sizeof(buf), format, args);
hsAssert(numWritten > 0, "Buffer too small");
va_end(args);
// Strip the redundant part off the key name
char logLine[512];
const char* modPos = strstr("_ResponderModifier", keyName);
if (modPos)
strncpy(logLine, keyName, modPos - keyName);
else
strcpy(logLine, keyName);
strcat(logLine, ": ");
strcat(logLine, buf);
gLog->AddLine(logLine, color);
#endif // STATUS_LOG
}