/*==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 #include "hsTypes.h" #include "plRandomCommandMod.h" #include "../pnSceneObject/plSceneObject.h" #include "../plMessage/plAnimCmdMsg.h" #include "../pnMessage/plEventCallbackMsg.h" #include "plgDispatch.h" #include "hsTimer.h" #include "hsUtils.h" static const hsScalar kRandNormalize = 1.f / 32767.f; plRandomCommandMod::plRandomCommandMod() { fState = 0; fMode = kNormal; fCurrent = -1; fMinDelay = 0; fMaxDelay = 0; } plRandomCommandMod::~plRandomCommandMod() { } // return how many are left to choose from int plRandomCommandMod::IExcludeSelections(int ncmds) { if( fMode & kCoverall ) { int nLeft = ncmds; fExcluded.SetBit(fCurrent); // Count how many haven't been played. int i; for( i = ncmds-1; i >= 0; --i ) { if( fExcluded.IsBitSet(i) ) nLeft--; } // If we're out of cmds, and OneCycle is set, // we're out of cmds until the next play. // Go ahead and reset for that. // If we're out and OneCycle isn't set, then go ahead // and set up for a new cycle. if( !nLeft ) { fExcluded.Clear(); if( fMode & kNoRepeats ) fExcluded.SetBit(fCurrent); if( fMode & kOneCycle ) return 0; nLeft = ncmds; if( ( fMode & kNoRepeats ) && ncmds > 1 ) nLeft--; } return nLeft; } double currTime = hsTimer::GetSysSeconds(); fExcluded.Clear(); int i; for( i = 0; i < fEndTimes.GetCount(); i++ ) { if( fEndTimes[i] > currTime ) { ncmds--; fExcluded.SetBit(i); } } if( fMode & kNoRepeats ) { ncmds--; fExcluded.SetBit(fCurrent); return ncmds; } return ncmds; } hsScalar plRandomCommandMod::IGetDelay(hsScalar len) const { hsScalar r = float(hsRand() * kRandNormalize); hsScalar delay = fMinDelay + (fMaxDelay - fMinDelay) * r; if( fMode & kDelayFromEnd ) delay += len; if( delay < 0 ) delay = fmodf(len, -delay); return delay; } hsBool plRandomCommandMod::ISelectNext(int ncmds) { if( fMode & kSequential ) { if( ++fCurrent >= ncmds ) { if( fMode & kOneCycle ) { fCurrent = -1; return false; } fCurrent = 0; } return true; } hsScalar r = float(hsRand() * kRandNormalize); int nSelect = ncmds; if( fCurrent >= 0 ) nSelect = IExcludeSelections(ncmds); if( !nSelect ) return false; int nth = int(r * (float(nSelect)-1.e-3f)); int iNext = 0; int i; for( i = 0; i < ncmds; i++ ) { if( !fExcluded.IsBitSet(i) ) { if( !nth-- ) { iNext = i; break; } } } fCurrent = iNext; return true; } void plRandomCommandMod::IStart() { if( !IStopped() ) return; fState &= ~kStopped; IPlayNextIfMaster(); } void plRandomCommandMod::IStop() { fState |= kStopped; } hsBool plRandomCommandMod::IStopped() const { return 0 != (fState & kStopped); } void plRandomCommandMod::IPlayNextIfMaster() { if( !fTarget ) IRetry(2.f); if( fTarget->IsLocallyOwned() == plSynchedObject::kNo ) // if this object is a proxy, it should just wait for network cmds return; if( IStopped() ) return; IPlayNext(); } hsBool plRandomCommandMod::MsgReceive(plMessage* msg) { // plAnimCmdMsg - interpret start/stop appropriately. // could overinterpret set loop points to limit range of // cmds we use to a window of the total set. plAnimCmdMsg* anim = plAnimCmdMsg::ConvertNoRef(msg); if( anim ) { if( anim->GetSender() != GetKey() ) { #if 0 hsStatusMessageF("someone triggered me, remote=%d\n", msg->HasBCastFlag(plMessage::kNetNonLocal)); #endif if( anim->Cmd(plAnimCmdMsg::kContinue) ) IStart(); if( anim->Cmd(plAnimCmdMsg::kStop) ) IStop(); if( anim->Cmd(plAnimCmdMsg::kToggleState) ) { if( IStopped() ) IStart(); else IStop(); } } else { #if 0 hsStatusMessageF("play next if master, remote=%d\n", msg->HasBCastFlag(plMessage::kNetNonLocal)); #endif IPlayNextIfMaster(); } return true; } // Don't understand, pass on to base class. return plSingleModifier::MsgReceive(msg); } void plRandomCommandMod::IReset() { fCurrent = -1; fExcluded.Clear(); if( !IStopped() ) IRetry(0); } void plRandomCommandMod::Read(hsStream* s, hsResMgr* mgr) { plSingleModifier::Read(s, mgr); fMode = s->ReadByte(); fState = s->ReadByte(); fMinDelay = s->ReadSwapScalar(); fMaxDelay = s->ReadSwapScalar(); IReset(); } void plRandomCommandMod::Write(hsStream* s, hsResMgr* mgr) { plSingleModifier::Write(s, mgr); s->WriteByte(fMode); s->WriteByte(fState); s->WriteSwapScalar(fMinDelay); s->WriteSwapScalar(fMaxDelay); } void plRandomCommandMod::IRetry(hsScalar secs) { IStop(); double t = hsTimer::GetSysSeconds() + secs; plAnimCmdMsg* msg = TRACKED_NEW plAnimCmdMsg(nil, GetKey(), &t); msg->SetCmd(plAnimCmdMsg::kContinue); plgDispatch::MsgSend(msg); }