/*==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 }