/*==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 . 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==*/ /* Some interesting special rules: You can only transition from one animation to another at certain points. The climb, mount, and dismount animations must be at their beginning or end to transition. If a climb or mount animation finishes and the key is not being held down, the idle animation starts automatically. If a climb or mount finishes and the key is being held down, the brain will *try* to transition to the same stage, effectively looping it. The idle can transition at any point. The Release and FallOff aniamtions can forcibly transition *any* animation. */ ///////////////////////////////////////////////////////////////// // // INCLUDES // ///////////////////////////////////////////////////////////////// // singular #include "plAvBrainClimb.h" // local #include "plAnimStage.h" #include "plAGAnim.h" #include "plAGAnimInstance.h" #include "plArmatureMod.h" #include "plMatrixChannel.h" #include "plAvBrainHuman.h" // global #include "hsTimer.h" // other #include "../plPipeline/plDebugText.h" #include "../plMessage/plSimStateMsg.h" #include "../plMessage/plLOSHitMsg.h" #include "../plMessage/plLOSRequestMsg.h" #include "../plMessage/plClimbEventMsg.h" #include "../pnNetCommon/plSDLTypes.h" ///////////////////////////////////////////////////////////////// // // IMPLEMENTATION // ///////////////////////////////////////////////////////////////// // CTOR default plAvBrainClimb::plAvBrainClimb() : fCurMode(kInactive), fNextMode(kInactive), fDesiredDirection(plClimbMsg::kUp), fControlDir(0.0f), fAllowedDirections(plClimbMsg::kUp | plClimbMsg::kDown | plClimbMsg::kLeft | plClimbMsg::kRight), fPhysicallyBlockedDirections(0), fOldPhysicallyBlockedDirections(0), fAllowedDismounts(0), fCurStage(nil), fExitStage(nil), fVerticalProbeLength(0.0f), fHorizontalProbeLength(0.0f), fUp(nil), fDown(nil), fLeft(nil), fRight(nil), fMountUp(nil), fMountDown(nil), fMountLeft(nil), fMountRight(nil), fDismountUp(nil), fDismountDown(nil), fDismountLeft(nil), fDismountRight(nil), fIdle(nil), fRelease(nil), fFallOff(nil) { IInitAnimations(); } // PLAVBRAINCLIMB plAvBrainClimb::plAvBrainClimb(Mode nextMode) : fCurMode(kInactive), fNextMode(nextMode), fDesiredDirection(plClimbMsg::kUp), fControlDir(0.0f), fAllowedDirections(plClimbMsg::kUp | plClimbMsg::kDown | plClimbMsg::kLeft | plClimbMsg::kRight), fPhysicallyBlockedDirections(0), fOldPhysicallyBlockedDirections(0), fAllowedDismounts(0), fCurStage(nil), fExitStage(nil), fVerticalProbeLength(0.0f), fHorizontalProbeLength(0.0f), fUp(nil), fDown(nil), fLeft(nil), fRight(nil), fMountUp(nil), fMountDown(nil), fMountLeft(nil), fMountRight(nil), fDismountUp(nil), fDismountDown(nil), fDismountLeft(nil), fDismountRight(nil), fIdle(nil), fRelease(nil), fFallOff(nil) { IInitAnimations(); } plAvBrainClimb::~plAvBrainClimb() { if(fAvMod) { if(fCurStage) fCurStage->Detach(fAvMod); if(fExitStage) fExitStage->Detach(fAvMod); } if(fUp) delete fUp; if(fDown) delete fDown; if(fLeft) delete fLeft; if(fRight) delete fRight; if(fMountUp) delete fMountUp; if(fMountDown) delete fMountDown; if(fMountLeft) delete fMountLeft; if(fMountRight) delete fMountRight; if(fDismountUp) delete fDismountUp; if(fDismountLeft) delete fDismountLeft; if(fDismountRight) delete fDismountRight; if(fIdle) delete fIdle; // if(fRelease) delete fRelease; // if(fFallOff) delete fFallOff; } // ACTIVATE void plAvBrainClimb::Activate(plArmatureModBase *avMod) { plArmatureBrain::Activate(avMod); ICalcProbeLengths(); fAvMod->GetRootAnimator()->Enable(true); fAvMod->EnablePhysicsKinematic(true); } void plAvBrainClimb::Deactivate() { fAvMod->GetRootAnimator()->Enable(false); fAvMod->EnablePhysicsKinematic(false); } // APPLY hsBool plAvBrainClimb::Apply(double time, hsScalar elapsed) { hsBool result = true; IGetDesiredDirection(); float overage = 0.0f; // if we ran past the end of the current stage, remember how much bool done = false; if(fExitStage) done = IProcessExitStage(time, elapsed); else done = IAdvanceCurrentStage(time, elapsed, overage); if(done || fCurMode == kIdle) { // if the transition is to one of the terminal modes, we're going to abort result = ITryStageTransition(time, overage); } if(!result && fExitStage) { fExitStage->Detach(fAvMod); } fAvMod->ApplyAnimations(time, elapsed); IProbeEnvironment(); return result; } // MSGRECEIVE hsBool plAvBrainClimb::MsgReceive(plMessage *msg) { plClimbMsg *climbMsg; plLOSHitMsg *losMsg; if(climbMsg = plClimbMsg::ConvertNoRef(msg)) { return IHandleClimbMsg(climbMsg); } else if(losMsg = plLOSHitMsg::ConvertNoRef(msg)) { return IHandleLOSMsg(losMsg); } else { return plArmatureBrain::MsgReceive(msg); } } // IHANDLECLIMBMSG hsBool plAvBrainClimb::IHandleClimbMsg(plClimbMsg *msg) { switch(msg->fCommand) { case plClimbMsg::kEnableClimb: if(msg->fStatus) this->fAllowedDirections |= msg->fDirection; else this->fAllowedDirections &= ~msg->fDirection; break; case plClimbMsg::kEnableDismount: if(msg->fStatus) this->fAllowedDismounts |= msg->fDirection; else this->fAllowedDismounts &= ~msg->fDirection; break; case plClimbMsg::kRelease: IRelease(true); break; case plClimbMsg::kFallOff: { if(fCurMode != kReleasing && fCurMode != kFallingOff && fCurMode != kFinishing) { plClimbEventMsg* pMsg = TRACKED_NEW plClimbEventMsg; pMsg->SetSender(msg->fTarget); pMsg->SetBCastFlag(plMessage::kBCastByExactType); pMsg->SetBCastFlag(plMessage::kLocalPropagate); pMsg->SetBCastFlag(plMessage::kNetPropagate); pMsg->SetBCastFlag(plMessage::kNetForce); pMsg->Send(); } IRelease(false); break; } } return true; } // IHANDLELOSMSG hsBool plAvBrainClimb::IHandleLOSMsg(plLOSHitMsg *msg) { plClimbMsg::Direction blockDir = static_cast(msg->fRequestID); // this is a weak test because someone else could be using the same bits to mean something different // the real strategy is that we should only receive LOS messages of our own creation hsBool oneOfOurs = blockDir == plClimbMsg::kUp || blockDir == plClimbMsg::kDown || blockDir == plClimbMsg::kLeft || blockDir == plClimbMsg::kRight; if(oneOfOurs) { fPhysicallyBlockedDirections |= blockDir; return true; } else { return false; } } // IPROCESSEXITSTAGE bool plAvBrainClimb::IProcessExitStage(double time, float elapsed) { plAGAnimInstance *ai = fExitStage->GetAnimInstance(); hsBool animDone = ai->IsAtEnd(); float unused; // if we have an exit stage running, move it instead of the base stage if(!animDone) fExitStage->MoveRelative(time, elapsed, unused, fAvMod); // only advance if it's not finished yet float curBlend = ai->GetBlend(); if(fCurStage && curBlend > .99) // reached peak strength { fCurStage->Detach(fAvMod); // remove the (now completely masked) underlying anim fCurStage = nil; ai->Fade(0, 2.0f); // start fading the exit anim } else if(animDone && curBlend == 0.0f) { return true; // finished and faded; we're really done now } return false; } // IRELEASE void plAvBrainClimb::IRelease(bool intentional) { if(fCurMode != kReleasing && fCurMode != kFallingOff && fCurMode != kFinishing) { if(intentional) { // fNextMode = kReleasing; fCurMode = kReleasing; fExitStage = fRelease; } else { // fNextMode = kFallingOff; fCurMode = kFallingOff; fExitStage = fFallOff; } fNextMode = kFinishing; double time = hsTimer::GetSysSeconds(); // attach the exit stage atop the current stage. from here on out we'll only advance // the current stage. fAvMod->GetRootAnimator()->Enable(false); fAvMod->EnablePhysicsKinematic(false); fExitStage->Attach(fAvMod, this, 1.0f, time); } } // IADVANCECURRENTSTAGE bool plAvBrainClimb::IAdvanceCurrentStage(double time, float elapsed, float &overage) { bool stageDone = false; if(fCurStage) { // elapsed tells us how far in time to move the animation // we must combine it with the key state to figure out whether // we're moving forward or backward in the animation fControlDir = 0.0f; // 0 is still; -1 is backwards; 1 is forwards switch(fCurMode) { case kDismountingUp: // if dismounting or mounting become reversable, move case kMountingUp: // these cases to be with "kClimbingUp"; same for the rest case kDismountingRight: case kMountingRight: case kDismountingDown: case kMountingDown: case kDismountingLeft: case kMountingLeft: case kFallingOff: case kReleasing: case kFinishing: case kIdle: case kClimbingUp: case kClimbingRight: case kClimbingDown: case kClimbingLeft: fControlDir = 1.0f; // these guys all auto-advance break; case kInactive: case kUnknown: // fControlDir is already 0 break; default: hsStatusMessage("Unknown mode in plAvBrainClimb::IAdvanceCurrentStage"); } float delta = elapsed * fControlDir; stageDone = fCurStage->MoveRelative(time, delta, overage, fAvMod); } else { stageDone = true; } return stageDone; } // ITRYSTAGETRANSITION bool plAvBrainClimb::ITryStageTransition(double time, float overage) { // hsStatusMessageF("Got overage %f", overage); IChooseNextMode(); bool result = true; // and vice versa if(fCurStage && fCurStage != fIdle) { hsStatusMessage("Wrapping externally."); bool atStart = overage >= 0.0f ? true : false; // if we went off the end, back to start fCurStage->Reset(time, fAvMod, atStart); // any time we start a stage besides idle, clear the climbing and dismount restrictions // this->fAllowedDirections = plClimbMsg::kUp | plClimbMsg::kDown | plClimbMsg::kLeft | plClimbMsg::kRight; // this->fAllowedDismounts = 0; } if(fNextMode != fCurMode) { if(fCurStage) fCurStage->Detach(fAvMod); fCurStage = IGetStageFromMode(fNextMode); if(fCurStage) { hsAssert(fCurStage, "Couldn't get next stage - mode has no stage. (Matt)"); fCurMode = fNextMode; if(fCurStage) result = (fCurStage->Attach(fAvMod, this, 1.0f, time) != nil); fAvMod->DirtySynchState(kSDLAvatar, 0); // write our new stage to the server } else { result = false; } } else { // hsStatusMessage("Wrapping externally."); // bool atStart = overage >= 0.0f ? true : false; // if we went off the end, back to start // // and vice versa // if(fCurStage) // fCurStage->Reset(time, fAvMod, atStart); } fNextMode = kUnknown; if(fCurStage) { if(overage < 0.0f) { float length = fCurStage->GetLength(); fCurStage->SetLocalTime(length + overage); } else { fCurStage->SetLocalTime(overage); } fAvMod->GetRootAnimator()->Reset(time); } return result; } // ICHOOSENEXTMODE bool plAvBrainClimb::IChooseNextMode() { // bear in mind this is only called when we're at a point where // we can change direction (usually because a climb loop has // just finished) switch (fCurMode) { case kInactive: case kUnknown: case kFinishing: break; // no change case kDismountingUp: case kDismountingDown: case kDismountingLeft: case kDismountingRight: case kFallingOff: case kReleasing: fNextMode = kFinishing; break; case kMountingUp: case kClimbingUp: case kMountingDown: case kClimbingDown: case kMountingLeft: case kClimbingLeft: case kMountingRight: case kClimbingRight: case kIdle: fNextMode = kIdle; if(fAllowedDismounts & fDesiredDirection) { switch(fDesiredDirection) { case plClimbMsg::kUp: fNextMode = kDismountingUp; break; case plClimbMsg::kDown: fNextMode = kDismountingDown; break; case plClimbMsg::kLeft: fNextMode = kDismountingLeft; break; case plClimbMsg::kRight: fNextMode = kDismountingRight; break; case plClimbMsg::kCenter: fNextMode = kIdle; break; default: hsAssert(false, "Error in fDesiredDirection. (Matt)"); } } else if(fAllowedDirections & fDesiredDirection & ~fPhysicallyBlockedDirections) { switch(fDesiredDirection) { case plClimbMsg::kUp: fNextMode = kClimbingUp; break; case plClimbMsg::kDown: fNextMode = kClimbingDown; break; case plClimbMsg::kLeft: fNextMode = kClimbingLeft; break; case plClimbMsg::kRight: fNextMode = kClimbingRight; break; case plClimbMsg::kCenter: fNextMode = kIdle; break; default: hsAssert(false, "Error in fDesiredDirection. (Matt)"); } } break; default: hsAssert(false, "Error in fCurMode. (Matt)"); } return true; } // IGETSTAGE plAnimStage * plAvBrainClimb::IGetStageFromMode(Mode mode) { switch(mode) { case kClimbingUp: return fUp; case kClimbingDown: return fDown; case kClimbingLeft: return fLeft; case kClimbingRight: return fRight; case kMountingUp: return fMountUp; case kMountingDown: return fMountDown; case kMountingLeft: return fMountLeft; case kMountingRight: return fMountRight; case kDismountingUp: return fDismountUp; case kDismountingDown: return fDismountDown; case kDismountingLeft: return fDismountLeft; case kDismountingRight: return fDismountRight; case kIdle: return fIdle; case kReleasing: return fRelease; case kFallingOff: return fFallOff; case kInactive: case kFinishing: case kUnknown: case kDone: return nil; default: hsAssert(false, "Unknown mode."); return nil; } } plAvBrainClimb::Mode plAvBrainClimb::IGetModeFromStage(plAnimStage *stage) { if(stage == fUp) return kClimbingUp; else if(stage == fDown) return kClimbingDown; else if(stage == fLeft) return kClimbingLeft; else if(stage == fRight) return kClimbingRight; else if(stage == fMountUp) return kMountingUp; else if(stage == fMountDown) return kMountingDown; else if(stage == fMountLeft) return kMountingLeft; else if(stage == fMountRight) return kMountingRight; else if(stage == fDismountUp) return kDismountingUp; else if(stage == fDismountDown) return kDismountingDown; else if(stage == fDismountLeft) return kDismountingLeft; else if(stage == fDismountRight) return kDismountingRight; else if(stage == fIdle) return kIdle; else if(stage == fRelease) return kReleasing; else if(stage == fFallOff) return kFallingOff; else return kUnknown; } // IGETDESIREDDIRECTION void plAvBrainClimb::IGetDesiredDirection() { if(fAvMod->ForwardKeyDown()) { fDesiredDirection = plClimbMsg::kUp; } else if (fAvMod->BackwardKeyDown()) { fDesiredDirection = plClimbMsg::kDown; } else if (fAvMod->TurnLeftKeyDown()) { fDesiredDirection = plClimbMsg::kLeft; } else if (fAvMod->TurnRightKeyDown()) { fDesiredDirection = plClimbMsg::kRight; } else { fDesiredDirection = plClimbMsg::kCenter; } } /** Look left, right, up, and down to see which directions are clear for our movement. We could do this by positioning our actual collision body and testing for hits, but it gives a lot more false positives *and* we won't get the normals of intersection, so it will be more complex to figure out which directions are actually blocked. The approach here is to do a raycast in the aforementioned directions and fail that direction if the raycast hits anything. */ void plAvBrainClimb::IProbeEnvironment() { hsMatrix44 l2w = fAvMod->GetTarget(0)->GetLocalToWorld(); // we're just going to pull the axes out of the hsPoint3 up = hsPoint3(l2w.GetAxis(hsMatrix44::kUp) * fVerticalProbeLength); hsPoint3 down = -up; hsPoint3 right = hsPoint3(l2w.GetAxis(hsMatrix44::kRight) * fHorizontalProbeLength); hsPoint3 left = -right; hsPoint3 start = l2w.GetTranslate(); start.fZ += 3.0f; // move the origin from the feet to the bellybutton up += start; down += start; left += start; right += start; plKey ourKey = fAvMod->GetKey(); // *** would be cool if we could hint that these should be batched for spatial coherence optimization plLOSRequestMsg *upReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, up, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); upReq->SetRequestID(static_cast(plClimbMsg::kUp)); upReq->Send(); plLOSRequestMsg *downReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, down, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); downReq->SetRequestID(static_cast(plClimbMsg::kDown)); downReq->Send(); plLOSRequestMsg *leftReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, left, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); leftReq->SetRequestID(static_cast(plClimbMsg::kLeft)); leftReq->SetRequestType(plSimDefs::kLOSDBCustom); leftReq->Send(); plLOSRequestMsg *rightReq = TRACKED_NEW plLOSRequestMsg(ourKey, start, right, plSimDefs::kLOSDBCustom, plLOSRequestMsg::kTestAny, plLOSRequestMsg::kReportHit); rightReq->SetRequestID(static_cast(plClimbMsg::kRight)); rightReq->Send(); fOldPhysicallyBlockedDirections = fPhysicallyBlockedDirections; fPhysicallyBlockedDirections = 0; // clear our blocks until the new reports come in.... } // ICalcProbeLengths ------------------- // ----------------- void plAvBrainClimb::ICalcProbeLengths() { // we assume that the up and down climbs go the same distance; // same for the left and right climbs plAGAnim *up = fAvMod->FindCustomAnim("WallClimbUp"); plAGAnim *left = fAvMod->FindCustomAnim("WallClimbLeft"); hsMatrix44 upMove, leftMove; hsAssert(up, "Couldn't find ClimbUp animation."); if(up) { GetStartToEndTransform(up, &upMove, nil, "Handle"); fVerticalProbeLength = upMove.GetTranslate().fZ; } else fVerticalProbeLength = 4.0f; // guess hsAssert(left, "Couldn't find ClimbLeft animation."); if(left) { GetStartToEndTransform(left, &leftMove, nil, "Handle"); fHorizontalProbeLength = leftMove.GetTranslate().fX; } else fHorizontalProbeLength = 3.0f; // guess } // IInitAnimations --------------------- // --------------- hsBool plAvBrainClimb::IInitAnimations() { fUp = TRACKED_NEW plAnimStage("WallClimbUp", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, 0); fDown = TRACKED_NEW plAnimStage("WallClimbDown", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, 0); fLeft = TRACKED_NEW plAnimStage("WallClimbLeft", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, 0); fRight = TRACKED_NEW plAnimStage("WallClimbRight", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressAuto, 0); // the mounts fMountUp = TRACKED_NEW plAnimStage("WallClimbMountUp", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fMountDown = TRACKED_NEW plAnimStage("WallClimbMountDown", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fMountLeft = TRACKED_NEW plAnimStage("WallClimbMountLeft", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fMountRight = TRACKED_NEW plAnimStage("WallClimbMountRight", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); // and here's the dismount fDismountUp = TRACKED_NEW plAnimStage("WallClimbDismountUp", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fDismountDown = TRACKED_NEW plAnimStage("WallClimbDismountDown", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fDismountLeft = TRACKED_NEW plAnimStage("WallClimbDismountLeft", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fDismountRight = TRACKED_NEW plAnimStage("WallClimbDismountUp", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); // other fIdle = TRACKED_NEW plAnimStage("WallClimbIdle", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fRelease = TRACKED_NEW plAnimStage("WallClimbRelease", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); fFallOff = TRACKED_NEW plAnimStage("WallClimbFallOff", plAnimStage::kNotifyEnter, plAnimStage::kForwardAuto, plAnimStage::kBackNone, plAnimStage::kAdvanceAuto, plAnimStage::kRegressNone, 0); return true; } ///////////////////////////////////////////////////////////////////////////////////////// // // SDL-BASED PERSISTENCE // ///////////////////////////////////////////////////////////////////////////////////////// #include "../plSDL/plSDL.h" #include "plAvatarSDLModifier.h" // SaveToSDL ----------------------------------------- // --------- void plAvBrainClimb::SaveToSDL(plStateDataRecord *sdl) { sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurMode)->Set(fCurMode); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrNextMode)->Set(fNextMode); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDirections)->Set((int)fAllowedDirections); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDismounts)->Set((int)fAllowedDismounts); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrVertProbeLength)->Set(fVerticalProbeLength); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrHorizProbeLength)->Set(fHorizontalProbeLength); bool curStageAttached = fCurStage && fCurStage->GetIsAttached(); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageAttached)->Set(curStageAttached); if(curStageAttached) { // slightly abuse the "mode" semantics; it happens to work as a persistance format Mode curStageAsMode = IGetModeFromStage(fCurStage); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStage)->Set(curStageAsMode); float curStageTime = fCurStage->GetLocalTime(); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageTime)->Set(curStageTime); float curStageBlend = fCurStage->GetAnimInstance()->GetBlend(); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageStrength)->Set(curStageBlend); } bool exitStageAttached = fExitStage && fExitStage->GetIsAttached(); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageAttached)->Set(exitStageAttached); if(exitStageAttached) { Mode exitStageAsMode = IGetModeFromStage(fExitStage); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStage)->Set(exitStageAsMode); float exitStageTime = fExitStage->GetLocalTime(); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageTime)->Set(exitStageTime); float exitStageBlend = fExitStage->GetAnimInstance()->GetBlend(); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageStrength)->Set(exitStageBlend); } } // LoadFromSDL ----------------------------------------- // ----------- void plAvBrainClimb::LoadFromSDL(const plStateDataRecord *sdl) { double curTime = hsTimer::GetSysSeconds(); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStage)->Get((int*)&fCurMode); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrNextMode)->Get((int*)&fNextMode); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDirections)->Get((int*)&fAllowedDirections); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrAllowedDismounts)->Get((int*)&fAllowedDismounts); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrVertProbeLength)->Get(&fVerticalProbeLength); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrHorizProbeLength)->Get(&fHorizontalProbeLength); bool curStageAttached = false; sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageAttached)->Get(&curStageAttached); if(curStageAttached) { Mode *curStageMode; // distinct from curMode; this is just a mode-based representation of the current stage sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStage)->Get((int*)&curStageMode); plAnimStage *curStage = this->IGetStageFromMode(fCurMode); curStage->Attach(fAvMod, this, 0.0f, curTime); float curStageTime = 0.0f; sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageTime)->Get(&curStageTime); curStage->ResetAtTime(curTime, curStageTime, fAvMod); // restart the relative-position sampler float curStageBlend = 0.0f; sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrCurStageStrength)->Get(&curStageBlend); curStage->GetAnimInstance()->SetBlend(curStageBlend); } bool exitStageAttached; sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageAttached)->Get(&exitStageAttached); if(exitStageAttached) { Mode exitStageMode; // the exit stage, in mode form sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStage)->Get((int *)&exitStageMode); plAnimStage *exitStage = this->IGetStageFromMode(exitStageMode); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageTime)->Get((int *)&fNextMode); sdl->FindVar(plAvatarSDLModifier::ClimbBrainVarNames::kStrExitStageStrength)->Get((int *)&fNextMode); } } ///////////////////////////////////////////////////////////////////////////////////////// // // READABLE TEXT FOR DEBUGGING // ///////////////////////////////////////////////////////////////////////////////////////// // DumpToDebugDisplay -------------------------------------------------------------------------------------- // ------------------ void plAvBrainClimb::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) { debugTxt.DrawString(x, y, "Brain type: Climb"); y += lineHeight; const char * worldDir = WorldDirStr(fDesiredDirection); const char * modeStr = ModeStr(fCurMode); char buffy[256]; sprintf(buffy, "direction: %s mode: %s controlDir: %f", worldDir, modeStr, fControlDir); debugTxt.DrawString(x,y, buffy); y += lineHeight; IDumpClimbDirections(x, y, lineHeight, strBuf, debugTxt); IDumpDismountDirections(x, y, lineHeight, strBuf, debugTxt); IDumpBlockedDirections(x, y, lineHeight, strBuf, debugTxt); fUp->DumpDebug(fUp == fCurStage, x, y, lineHeight, strBuf, debugTxt); fDown->DumpDebug(fDown == fCurStage, x, y, lineHeight, strBuf, debugTxt); fLeft->DumpDebug(fLeft == fCurStage, x, y, lineHeight, strBuf, debugTxt); fRight->DumpDebug(fRight == fCurStage, x, y, lineHeight, strBuf, debugTxt); fMountUp->DumpDebug(fMountUp == fCurStage, x, y, lineHeight, strBuf, debugTxt); fMountDown->DumpDebug(fMountDown == fCurStage, x, y, lineHeight, strBuf, debugTxt); fMountLeft->DumpDebug(fMountLeft == fCurStage, x, y, lineHeight, strBuf, debugTxt); fMountRight->DumpDebug(fMountRight == fCurStage, x, y, lineHeight, strBuf, debugTxt); fDismountUp->DumpDebug(fDismountUp == fCurStage, x, y, lineHeight, strBuf, debugTxt); fDismountDown->DumpDebug(fDismountDown == fCurStage, x, y, lineHeight, strBuf, debugTxt); fDismountLeft->DumpDebug(fDismountLeft == fCurStage, x, y, lineHeight, strBuf, debugTxt); fDismountRight->DumpDebug(fDismountRight == fCurStage, x, y, lineHeight, strBuf, debugTxt); fIdle->DumpDebug(fIdle == fCurStage, x, y, lineHeight, strBuf, debugTxt); fRelease->DumpDebug(fRelease == fCurStage, x, y, lineHeight, strBuf, debugTxt); fFallOff->DumpDebug(fFallOff == fCurStage, x, y, lineHeight, strBuf, debugTxt); } // IDumpClimbDirections -------------------------------------------------------------------------------------- // -------------------- void plAvBrainClimb::IDumpClimbDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) { const char * prolog = "Allowed directions: "; std::string str; str = prolog; if(fAllowedDirections & plClimbMsg::kUp) str = str + "UP "; if(fAllowedDirections & plClimbMsg::kDown) str = str + "DOWN "; if(fAllowedDirections & plClimbMsg::kLeft) str = str + "LEFT "; if(fAllowedDirections & plClimbMsg::kRight) str = str + "RIGHT "; if(str.size() == strlen(prolog)) str = str + "- NONE -"; debugTxt.DrawString(x, y, str.c_str()); y += lineHeight; } // IDumpDismountDirections -------------------------------------------------------------------------------------- // ----------------------- void plAvBrainClimb::IDumpDismountDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) { const char * prolog = "Enabled dismounts: "; std::string str; str = prolog; if(fAllowedDismounts & plClimbMsg::kUp) str = str + "UP "; if(fAllowedDismounts & plClimbMsg::kDown) str = str + "DOWN "; if(fAllowedDismounts & plClimbMsg::kLeft) str = str + "LEFT "; if(fAllowedDismounts & plClimbMsg::kRight) str = str + "RIGHT "; if(str.size() == strlen(prolog)) str = str + "- NONE -"; debugTxt.DrawString(x, y, str.c_str()); y += lineHeight; } void plAvBrainClimb::IDumpBlockedDirections(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) { const char * prolog = "Physically blocked: "; std::string str; str = prolog; if(fOldPhysicallyBlockedDirections & plClimbMsg::kUp) str = str + "UP "; if(fOldPhysicallyBlockedDirections & plClimbMsg::kDown) str = str + "DOWN "; if(fOldPhysicallyBlockedDirections & plClimbMsg::kLeft) str = str + "LEFT "; if(fOldPhysicallyBlockedDirections & plClimbMsg::kRight) str = str + "RIGHT "; if(str.size() == strlen(prolog)) str = str + "- NONE -"; debugTxt.DrawString(x, y, str.c_str()); y += lineHeight; } const char * plAvBrainClimb::WorldDirStr(plClimbMsg::Direction dir) { switch(dir) { case plClimbMsg::kUp: return "Up"; case plClimbMsg::kDown: return "Down"; case plClimbMsg::kLeft: return "Left"; case plClimbMsg::kRight: return "Right"; case plClimbMsg::kCenter: return "Center"; default: return "WTF?"; } } const char *plAvBrainClimb::ModeStr(Mode mode) { switch(mode) { case kInactive: return "Inactive"; case kUnknown: return "Unknown"; case kFinishing: return "Finishing"; case kDone: return "Done"; case kClimbingUp: return "ClimbingUp"; case kClimbingDown: return "ClimbingDown"; case kClimbingLeft: return "ClimbingLeft"; case kClimbingRight: return "ClimbingRight"; case kMountingUp: return "MountingUp"; case kMountingDown: return "MountingDown"; case kMountingLeft: return "MountingLeft"; case kMountingRight: return "MountingRight"; case kDismountingUp: return "MountingUp"; case kDismountingDown: return "DismountingDown"; case kDismountingLeft: return "DismountingLeft"; case kDismountingRight: return "DismountingRight"; case kIdle: return "Idle"; case kReleasing: return "Releasing"; case kFallingOff: return "FallingOff"; default: return "WTF???!!!"; } }