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.
2856 lines
75 KiB
2856 lines
75 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 "plArmatureMod.h" |
|
|
|
// local |
|
#include "plAvBrain.h" |
|
#include "plAvBrainUser.h" |
|
#include "plAvatarMgr.h" |
|
#include "plAGModifier.h" |
|
#include "plAvatarClothing.h" |
|
#include "plClothingSDLModifier.h" |
|
#include "plAvatarSDLModifier.h" |
|
#include "plAGAnim.h" |
|
#include "plArmatureEffects.h" |
|
#include "plAvBrainHuman.h" |
|
#include "plMatrixChannel.h" |
|
#include "plAvatarTasks.h" |
|
#include "plAvCallbackAction.h" |
|
#include "plAvBrainCritter.h" |
|
|
|
// global |
|
#include "plgDispatch.h" |
|
#include "hsQuat.h" |
|
#include "hsTimer.h" |
|
|
|
// other |
|
#include "../pnSceneObject/plCoordinateInterface.h" |
|
#include "../pnSceneObject/plAudioInterface.h" |
|
#include "../plInterp/plAnimTimeConvert.h" |
|
|
|
#include "../pnMessage/plEnableMsg.h" |
|
#include "../pnMessage/plTimeMsg.h" |
|
#include "../pnMessage/plSDLModifierMsg.h" |
|
#include "../pnMessage/plAttachMsg.h" |
|
#include "../pnMessage/plWarpMsg.h" |
|
#include "../pnMessage/plCorrectionMsg.h" |
|
#include "../pnMessage/plCameraMsg.h" |
|
#include "../pnMessage/plPipeResMakeMsg.h" |
|
|
|
#include "../plMessage/plAvatarMsg.h" |
|
#include "../plMessage/plAvatarFootMsg.h" |
|
#include "../plMessage/plInputEventMsg.h" |
|
#include "../plMessage/plLoadAgeMsg.h" |
|
#include "../plMessage/plAnimCmdMsg.h" |
|
#include "../plMessage/plListenerMsg.h" |
|
#include "../plMessage/plAgeLoadedMsg.h" |
|
#include "../plMessage/plParticleUpdateMsg.h" |
|
|
|
#include "../plParticleSystem/plParticleSystem.h" |
|
#include "../plParticleSystem/plParticleSDLMod.h" |
|
|
|
#include "../pfMessage/plArmatureEffectMsg.h" |
|
#include "../pfMessage/pfKIMsg.h" |
|
#include "../plVault/plVault.h" |
|
|
|
#include "../pnKeyedObject/plFixedKey.h" |
|
#include "../pnKeyedObject/plKey.h" |
|
#include "../pnKeyedObject/plKeyImp.h" |
|
|
|
#include "../plDrawable/plInstanceDrawInterface.h" |
|
#include "../plDrawable/plDrawableSpans.h" |
|
#include "../plSurface/plLayerAnimation.h" |
|
#include "../plSurface/hsGMaterial.h" |
|
|
|
#include "../pnNetCommon/plNetApp.h" |
|
#include "../plNetClient/plNetClientMgr.h" // for CCR stuff.. |
|
#include "../plNetClient/plNetLinkingMgr.h" |
|
#include "../plModifier/plSpawnModifier.h" |
|
#include "../plPipeline/plDebugText.h" |
|
#include "../plResMgr/plKeyFinder.h" |
|
#include "../plAudio/plWin32StaticSound.h" |
|
#include "../plAudio/plAudioSystem.h" |
|
#include "../plNetMessage/plNetMessage.h" |
|
#include "../plInputCore/plAvatarInputInterface.h" |
|
#include "../plInputCore/plSceneInputInterface.h" |
|
#include "../plInputCore/plInputDevice.h" |
|
#include "../pfCamera/plVirtualCamNeu.h" |
|
#include "../plScene/plRelevanceMgr.h" |
|
#include "../plMessage/plSimStateMsg.h" |
|
|
|
#include "../plGImage/plLODMipmap.h" |
|
#include "plPipeline.h" |
|
#include "plTweak.h" |
|
#include "../plDrawable/plVisLOSMgr.h" |
|
|
|
int plArmatureModBase::fMinLOD = 0; // standard is 3 levels of LOD |
|
double plArmatureModBase::fLODDistance = 50.0; |
|
|
|
|
|
plArmatureModBase::plArmatureModBase() : |
|
fWaitFlags(kNeedMesh | kNeedPhysics | kNeedApplicator | kNeedBrainActivation), |
|
fController(nil), |
|
fCurLOD(-1), |
|
fRootAnimator(nil), |
|
fDisabledPhysics(0), |
|
fDisabledDraw(0) |
|
{ |
|
} |
|
|
|
plArmatureModBase::~plArmatureModBase() |
|
{ |
|
delete fController; |
|
|
|
while (fUnusedBones.size() > 0) |
|
{ |
|
delete fUnusedBones.back(); |
|
fUnusedBones.pop_back(); |
|
} |
|
} |
|
|
|
hsBool plArmatureModBase::MsgReceive(plMessage* msg) |
|
{ |
|
hsBool result = false; |
|
|
|
plArmatureBrain *curBrain = nil; |
|
if (fBrains.size() > 0) |
|
{ |
|
curBrain = fBrains.back(); |
|
if(curBrain->MsgReceive(msg)) |
|
return true; |
|
} |
|
|
|
return plAGMasterMod::MsgReceive(msg); |
|
} |
|
|
|
void plArmatureModBase::AddTarget(plSceneObject* so) |
|
{ |
|
plAGMasterMod::AddTarget(so); |
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey()); |
|
} |
|
|
|
void plArmatureModBase::RemoveTarget(plSceneObject* so) |
|
{ |
|
int count = fBrains.size(); |
|
for(int i = count - 1; i >= 0; i--) |
|
{ |
|
plArmatureBrain *brain = fBrains[i]; |
|
if (brain) |
|
brain->Deactivate(); |
|
delete brain; |
|
fBrains.pop_back(); |
|
} |
|
|
|
plAGMasterMod::RemoveTarget(so); |
|
} |
|
|
|
hsBool plArmatureModBase::IEval(double time, hsScalar elapsed, UInt32 dirty) |
|
{ |
|
if (IsFinal()) |
|
{ |
|
if (fBrains.size()) |
|
{ |
|
plArmatureBrain *curBrain = fBrains.back(); |
|
if (curBrain) |
|
{ |
|
hsBool result = curBrain->Apply(time, elapsed); |
|
if (!result) |
|
{ |
|
PopBrain(); |
|
delete curBrain; |
|
} |
|
} |
|
} |
|
AdjustLOD(); |
|
} |
|
else |
|
IFinalize(); |
|
|
|
return true; |
|
} |
|
|
|
void plArmatureModBase::Read(hsStream * stream, hsResMgr *mgr) |
|
{ |
|
plAGMasterMod::Read(stream, mgr); |
|
|
|
int i; |
|
int meshKeyCount = stream->ReadSwap32(); |
|
for(i = 0; i < meshKeyCount; i++) |
|
{ |
|
plKey meshKey = mgr->ReadKey(stream); |
|
fMeshKeys.push_back(meshKey); |
|
|
|
plKeyVector *vec = TRACKED_NEW plKeyVector; |
|
int boneCount = stream->ReadSwap32(); |
|
for(int j = 0; j < boneCount; j++) |
|
vec->push_back(mgr->ReadKey(stream)); |
|
fUnusedBones.push_back(vec); |
|
} |
|
|
|
int nBrains = stream->ReadSwap32(); |
|
for (i = 0; i < nBrains; i++) |
|
{ |
|
plArmatureBrain * brain = (plArmatureBrain *)mgr->ReadCreatable(stream); |
|
this->PushBrain(brain); |
|
} |
|
} |
|
|
|
void plArmatureModBase::Write(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
plAGMasterMod::Write(stream, mgr); |
|
|
|
int i; |
|
int meshKeyCount = fMeshKeys.size(); |
|
stream->WriteSwap32(meshKeyCount); |
|
for (i = 0; i < meshKeyCount; i++) |
|
{ |
|
plKey meshKey = fMeshKeys[i]; |
|
mgr->WriteKey(stream, meshKey); |
|
|
|
// Should be a list per mesh key |
|
stream->WriteSwap32(fUnusedBones[i]->size()); |
|
for(int j = 0; j < fUnusedBones[i]->size(); j++) |
|
mgr->WriteKey(stream, (*fUnusedBones[i])[j]); |
|
} |
|
|
|
int nBrains = fBrains.size(); |
|
stream->WriteSwap32(nBrains); |
|
for (i = 0; i < nBrains; i++) |
|
{ |
|
mgr->WriteCreatable(stream, fBrains[i]); |
|
} |
|
} |
|
|
|
void plArmatureModBase::AddressMessageToDescendants(const plCoordinateInterface * CI, plMessage *msg) |
|
{ |
|
if (!CI) |
|
return; |
|
|
|
msg->AddReceiver(CI->GetOwnerKey()); |
|
for (int i = 0; i < CI->GetNumChildren(); i++) |
|
AddressMessageToDescendants(CI->GetChild(i), msg); |
|
} |
|
|
|
void plArmatureModBase::EnableDrawingTree(const plSceneObject *object, hsBool status) |
|
{ |
|
if (!object) |
|
return; |
|
|
|
plEnableMsg *msg = TRACKED_NEW plEnableMsg; |
|
if (status) |
|
msg->SetCmd( plEnableMsg::kEnable ); |
|
else |
|
msg->SetCmd( plEnableMsg::kDisable ); |
|
msg->SetCmd( plEnableMsg::kDrawable ); |
|
|
|
const plCoordinateInterface *pCI = object->GetCoordinateInterface(); |
|
AddressMessageToDescendants(pCI, msg); |
|
plgDispatch::MsgSend(msg); |
|
} |
|
|
|
plKey plArmatureModBase::GetWorldKey() const |
|
{ |
|
if (fController) |
|
return fController->GetSubworld(); |
|
else |
|
return nil; |
|
} |
|
|
|
hsBool plArmatureModBase::ValidatePhysics() |
|
{ |
|
if (!fTarget) |
|
return false; |
|
|
|
if (fController) |
|
{ |
|
EnablePhysics(true); |
|
fWaitFlags &= ~kNeedPhysics; |
|
} |
|
|
|
return !(fWaitFlags & kNeedPhysics); |
|
} |
|
|
|
hsBool plArmatureModBase::ValidateMesh() |
|
{ |
|
if (fWaitFlags & kNeedMesh) |
|
{ |
|
fWaitFlags &= ~kNeedMesh; |
|
int n = fMeshKeys.size(); |
|
|
|
for(int i = 0; i < n; i++) |
|
{ |
|
plKey meshKey = fMeshKeys[i]; |
|
plSceneObject *meshObj = (plSceneObject *)meshKey->GetObjectPtr(); |
|
|
|
if (!meshObj) |
|
{ |
|
fWaitFlags |= kNeedMesh; |
|
break; |
|
} |
|
hsBool visible = (i == fCurLOD ? true : false); |
|
EnableDrawingTree(meshObj, visible); |
|
} |
|
} |
|
|
|
return !(fWaitFlags & kNeedMesh); |
|
} |
|
|
|
void plArmatureModBase::PushBrain(plArmatureBrain *brain) |
|
{ |
|
plArmatureBrain *oldBrain = GetCurrentBrain(); |
|
if (oldBrain) |
|
oldBrain->Suspend(); |
|
|
|
|
|
fBrains.push_back(brain); |
|
// don't activate brains until we are attached to our target |
|
// addTarget will do activation... |
|
if (GetTarget(0)) |
|
brain->Activate(this); |
|
|
|
DirtySynchState(kSDLAvatar, 0); |
|
} |
|
|
|
void plArmatureModBase::PopBrain() |
|
{ |
|
plArmatureBrain *oldBrain = nil; |
|
if (fBrains.size() > 0) |
|
{ |
|
oldBrain = fBrains.back(); |
|
oldBrain->Deactivate(); |
|
fBrains.pop_back(); |
|
} |
|
|
|
plArmatureBrain *newBrain = GetCurrentBrain(); |
|
if (newBrain) |
|
newBrain->Resume(); |
|
|
|
DirtySynchState(kSDLAvatar, 0); |
|
} |
|
|
|
plArmatureBrain *plArmatureModBase::GetCurrentBrain() const |
|
{ |
|
plArmatureBrain *result = nil; |
|
if (fBrains.size() > 0) |
|
result = fBrains.back(); |
|
|
|
return result; |
|
} |
|
|
|
plDrawable *plArmatureModBase::FindDrawable() const |
|
{ |
|
if (fMeshKeys[0] == nil) |
|
return nil; |
|
|
|
plSceneObject *so = plSceneObject::ConvertNoRef(fMeshKeys[0]->ObjectIsLoaded()); |
|
if (so == nil) |
|
return nil; |
|
|
|
const plDrawInterface *di = so->GetDrawInterface(); |
|
if (di && di->GetNumDrawables() > 0) |
|
{ |
|
plDrawable *spans = plDrawable::ConvertNoRef(di->GetDrawable(0)); |
|
if (spans) |
|
return spans; |
|
} |
|
|
|
const plInstanceDrawInterface *idi = plInstanceDrawInterface::ConvertNoRef(di); |
|
if (idi) |
|
return idi->GetInstanceDrawable(); |
|
|
|
return nil; |
|
} |
|
|
|
void plArmatureModBase::LeaveAge() |
|
{ |
|
int nBrains = fBrains.size(); |
|
for (int i = nBrains - 1; i >= 0; i--) |
|
{ |
|
plArmatureBrain * curBrain = fBrains[i]; |
|
if (curBrain->LeaveAge()) |
|
{ |
|
if (curBrain == GetCurrentBrain()) |
|
{ |
|
PopBrain(); |
|
delete curBrain; |
|
} |
|
} |
|
} |
|
} |
|
|
|
hsBool plArmatureModBase::IsFinal() |
|
{ |
|
return !fWaitFlags; |
|
} |
|
|
|
void plArmatureModBase::AdjustLOD() |
|
{ |
|
if (!IsDrawEnabled()) |
|
return; |
|
|
|
hsPoint3 camPos = plVirtualCam1::Instance()->GetCameraPos(); |
|
|
|
plSceneObject * SO = GetTarget(0); |
|
if (SO) |
|
{ |
|
hsMatrix44 l2w = SO->GetLocalToWorld(); |
|
hsPoint3 ourPos = l2w.GetTranslate(); |
|
hsPoint3 delta = ourPos - camPos; |
|
hsScalar distanceSquared = delta.MagnitudeSquared(); |
|
if (distanceSquared < fLODDistance * fLODDistance) |
|
SetLOD(__max(0, fMinLOD)); |
|
else if (distanceSquared < fLODDistance * fLODDistance * 4.0) |
|
SetLOD(__max(1, fMinLOD)); |
|
else |
|
SetLOD(2); |
|
} |
|
} |
|
|
|
// Should always be called from AdjustLOD |
|
hsBool plArmatureModBase::SetLOD(int iNewLOD) |
|
{ |
|
if (iNewLOD >= fMeshKeys.size()) |
|
iNewLOD = fMeshKeys.size() - 1; |
|
|
|
int oldLOD = fCurLOD; |
|
if (iNewLOD != fCurLOD) |
|
{ |
|
int oldLOD = fCurLOD; |
|
if(fMeshKeys.size() > iNewLOD) |
|
{ |
|
if (fCurLOD != -1) |
|
{ |
|
plSceneObject * oldMesh = (plSceneObject *)fMeshKeys[oldLOD]->GetObjectPtr(); |
|
EnableDrawingTree(oldMesh, false); |
|
} |
|
else |
|
{ |
|
// just starting up; turn all except current off |
|
for (int i = 0; i < fMeshKeys.size(); i++) |
|
{ |
|
if (i != iNewLOD) |
|
{ |
|
plKey offKey = fMeshKeys[i]; |
|
plSceneObject * offMesh = (plSceneObject *)offKey->GetObjectPtr(); |
|
EnableDrawingTree(offMesh, false); |
|
} |
|
} |
|
} |
|
|
|
fCurLOD = iNewLOD; |
|
plSceneObject * newMesh = (plSceneObject *)fMeshKeys[fCurLOD]->GetObjectPtr(); |
|
EnableDrawingTree(newMesh, true); |
|
|
|
int boneLOD; |
|
for (boneLOD = 0; boneLOD < fMeshKeys.size(); boneLOD++) |
|
IEnableBones(boneLOD, boneLOD <= iNewLOD ? false: true); |
|
} |
|
} |
|
return oldLOD; |
|
} |
|
|
|
void plArmatureModBase::RefreshTree() |
|
{ |
|
fCurLOD = -1; |
|
AdjustLOD(); |
|
} |
|
|
|
int plArmatureModBase::AppendMeshKey(plKey meshKey) |
|
{ |
|
fMeshKeys.push_back(meshKey); |
|
return fMeshKeys.size() - 1; |
|
} |
|
|
|
int plArmatureModBase::AppendBoneVec(plKeyVector *boneVec) |
|
{ |
|
fUnusedBones.push_back(boneVec); |
|
return fUnusedBones.size() - 1; |
|
} |
|
|
|
UInt8 plArmatureModBase::GetNumLOD() const |
|
{ |
|
return fMeshKeys.size(); |
|
} |
|
|
|
void plArmatureModBase::EnablePhysics(hsBool status, UInt16 reason /* = kDisableReasonUnknown */) |
|
{ |
|
if (status) |
|
fDisabledPhysics &= ~reason; |
|
else |
|
fDisabledPhysics |= reason; |
|
|
|
hsBool newStatus = !fDisabledPhysics; |
|
|
|
if (fController) |
|
fController->Enable(newStatus); |
|
} |
|
|
|
// |
|
// Enabling Kinematics (state=true) will have the affect of: |
|
// - disabling outside forces (gravity and other physics objects pushing us) |
|
// - but leave on collisions with detector regions |
|
// Disabling Kinematics (state=false) means: |
|
// - all outside forces will affect us and collisions on |
|
// i.e. normal enabled physical |
|
void plArmatureModBase::EnablePhysicsKinematic(hsBool status) |
|
{ |
|
if (fController) |
|
fController->Kinematic(status); |
|
} |
|
|
|
void plArmatureModBase::EnableDrawing(hsBool status, UInt16 reason /* = kDisableReasonUnknown */) |
|
{ |
|
hsBool oldStatus = !fDisabledDraw; |
|
if (status) |
|
fDisabledDraw &= ~reason; |
|
else |
|
fDisabledDraw |= reason; |
|
|
|
hsBool newStatus = !fDisabledDraw; |
|
if (oldStatus == newStatus) |
|
return; |
|
|
|
int i; |
|
for (i = 0; i < fMeshKeys.size(); i++) |
|
{ |
|
plSceneObject *obj = plSceneObject::ConvertNoRef(fMeshKeys[i]->ObjectIsLoaded()); |
|
if (obj) |
|
EnableDrawingTree(obj, newStatus); |
|
} |
|
|
|
if (status) |
|
fCurLOD = -1; // We just enabled all LOD. Need to force ourselves to recompute current LOD |
|
} |
|
|
|
void plArmatureModBase::IFinalize() |
|
{ |
|
if (fWaitFlags & kNeedMesh) |
|
ValidateMesh(); |
|
if (fWaitFlags & kNeedPhysics) |
|
ValidatePhysics(); |
|
if (fWaitFlags & kNeedApplicator) |
|
ICustomizeApplicator(); |
|
if (fWaitFlags & kNeedBrainActivation) |
|
{ |
|
int nBrains = fBrains.size(); |
|
for (int i = 0; i < nBrains; i++) |
|
{ |
|
// every brain gets activated, but all except the top brain |
|
// also get suspended when the one above them is activated. |
|
if (i > 0) |
|
fBrains[i - 1]->Suspend(); |
|
|
|
fBrains[i]->Activate(this); |
|
} |
|
fWaitFlags &= ~kNeedBrainActivation; |
|
} |
|
} |
|
|
|
void plArmatureModBase::ICustomizeApplicator() |
|
{ |
|
const plAGModifier *agMod = plAGModifier::ConvertNoRef(FindModifierByClass(GetTarget(0), plAGModifier::Index())); |
|
if (agMod) |
|
{ |
|
plAGApplicator *app = agMod->GetApplicator(kAGPinTransform); |
|
if (app) |
|
{ |
|
plMatrixDifferenceApp *differ = plMatrixDifferenceApp::ConvertNoRef(app); |
|
|
|
if (differ) |
|
{ |
|
fRootAnimator = differ; |
|
fWaitFlags &= ~kNeedApplicator; |
|
return; // already there |
|
} |
|
} |
|
plAGModifier *volAGMod = const_cast<plAGModifier *>(agMod); |
|
plMatrixDifferenceApp *differ = TRACKED_NEW plMatrixDifferenceApp(); |
|
|
|
fRootAnimator = differ; |
|
volAGMod->SetApplicator(differ); |
|
differ->Enable(false); |
|
fWaitFlags &= ~kNeedApplicator; |
|
} |
|
} |
|
|
|
void plArmatureModBase::IEnableBones(int lod, hsBool enable) |
|
{ |
|
if (lod < fUnusedBones.size()) |
|
{ |
|
plKeyVector *vec = fUnusedBones[lod]; |
|
int i; |
|
for (i = 0; i < vec->size(); i++) |
|
((plAGModifier *)(*vec)[i]->GetObjectPtr())->Enable(enable); |
|
} |
|
} |
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////// |
|
|
|
const char *plArmatureMod::BoneStrings[] = {"Male", "Female", "Critter", "Actor"}; |
|
|
|
const hsScalar plArmatureMod::kAvatarInputSynchThreshold = 10.f; |
|
hsScalar plArmatureMod::fMouseTurnSensitivity = 1.f; |
|
hsBool plArmatureMod::fClickToTurn = true; |
|
|
|
void plArmatureMod::IInitDefaults() |
|
{ |
|
fBoneRootAnimator = nil; |
|
fRootAGMod = nil; |
|
fFootSoundSOKey = nil; |
|
fLinkSoundSOKey = nil; |
|
fBodyType = kBoneBaseMale; |
|
fClothingOutfit = nil; |
|
fClothingSDLMod = nil; |
|
fAvatarSDLMod = nil; |
|
fAvatarPhysicalSDLMod = nil; |
|
fEffects = nil; |
|
fDebugOn = false; |
|
fBoneMap = nil; |
|
fStealthMode = plAvatarStealthModeMsg::kStealthVisible; |
|
fStealthLevel = 0; |
|
fMouseFrameTurnStrength = 0.f; |
|
fSuspendInputCount = 0; |
|
fMidLink = false; |
|
fAlreadyPanicLinking = false; |
|
fReverseFBOnIdle = false; |
|
fFollowerParticleSystemSO = nil; |
|
fPendingSynch = false; |
|
fLastInputSynch = 0; |
|
fOpaque = true; |
|
fPhysHeight = 0.f; |
|
fPhysWidth = 0.f; |
|
fUpdateMsg = nil; |
|
fRootName = nil; |
|
fDontPanicLink = false; |
|
fBodyAgeName = "GlobalAvatars"; |
|
fBodyFootstepSoundPage = "Audio"; |
|
fAnimationPrefix = "Male"; |
|
fUserStr = ""; |
|
} |
|
|
|
plArmatureMod::plArmatureMod() : plArmatureModBase() |
|
{ |
|
IInitDefaults(); |
|
fWaitFlags |= kNeedAudio | kNeedCamera | kNeedSpawn; |
|
} |
|
|
|
plArmatureMod::~plArmatureMod() |
|
{ |
|
delete fBoneMap; |
|
delete [] fRootName; |
|
|
|
if (fUpdateMsg) |
|
fUpdateMsg->UnRef(); |
|
} |
|
|
|
void plArmatureMod::SetPositionAndRotationSim(const hsPoint3* position, const hsQuat* rotation) |
|
{ |
|
const plCoordinateInterface* subworldCI = nil; |
|
if (fController) |
|
subworldCI = fController->GetSubworldCI(); |
|
|
|
hsMatrix44 l2w, w2l; |
|
|
|
// If we need the position or rotation, grab it from the avatar |
|
if (!position || !rotation) |
|
{ |
|
// Get the current position of the avatar in sim space |
|
hsMatrix44 avatarL2S = GetTarget(0)->GetLocalToWorld(); |
|
if (subworldCI) |
|
avatarL2S = subworldCI->GetWorldToLocal() * avatarL2S; |
|
|
|
if (!rotation) |
|
l2w = avatarL2S; |
|
else |
|
rotation->MakeMatrix(&l2w); |
|
|
|
if (!position) |
|
{ |
|
hsPoint3 simPos; |
|
avatarL2S.GetTranslate(&simPos); |
|
l2w.SetTranslate(&simPos); |
|
} |
|
else |
|
l2w.SetTranslate(position); |
|
} |
|
else |
|
{ |
|
rotation->MakeMatrix(&l2w); |
|
l2w.SetTranslate(position); |
|
} |
|
|
|
// We've got the requested position of the avatar in subworld space, convert |
|
// it to world space if necessary |
|
if (subworldCI) |
|
l2w = subworldCI->GetLocalToWorld() * l2w; |
|
l2w.GetInverse(&w2l); |
|
|
|
GetTarget(0)->SetTransform(l2w, w2l); |
|
GetTarget(0)->FlushTransform(); |
|
} |
|
|
|
void plArmatureMod::GetPositionAndRotationSim(hsPoint3* position, hsQuat* rotation) |
|
{ |
|
hsMatrix44 l2s = GetTarget(0)->GetLocalToWorld(); |
|
|
|
if (fController) |
|
{ |
|
const plCoordinateInterface* subworldCI = fController->GetSubworldCI(); |
|
if (subworldCI) |
|
l2s = subworldCI->GetWorldToLocal() * l2s; |
|
|
|
if (position) |
|
l2s.GetTranslate(position); |
|
if (rotation) |
|
rotation->SetFromMatrix(&l2s); |
|
} |
|
} |
|
|
|
const plSceneObject *plArmatureMod::FindBone(const char * name) const |
|
{ |
|
plSceneObject *result = nil; |
|
|
|
plAGModifier * mod = GetChannelMod(name); |
|
|
|
if (mod) |
|
result = mod->GetTarget(0); |
|
|
|
return result; |
|
} |
|
|
|
const plSceneObject *plArmatureMod::FindBone(UInt32 id) const |
|
{ |
|
if(fBoneMap) |
|
return fBoneMap->FindBone(id); |
|
else |
|
return nil; |
|
} |
|
|
|
void plArmatureMod::AddBoneMapping(UInt32 id, const plSceneObject *bone) |
|
{ |
|
if(!fBoneMap) |
|
fBoneMap = TRACKED_NEW plAvBoneMap(); |
|
|
|
fBoneMap->AddBoneMapping(id, bone); |
|
} |
|
|
|
void plArmatureMod::WindowActivate(bool active) |
|
{ |
|
if (!active) // We don't want the avatar to move while we don't have focus |
|
{ |
|
if (!plAvatarMgr::GetInstance()) |
|
return; |
|
|
|
plArmatureMod *localAv = plAvatarMgr::GetInstance()->GetLocalAvatar(); |
|
if (localAv) |
|
localAv->ClearInputFlags(true, true); |
|
} |
|
} |
|
|
|
char *plArmatureMod::fSpawnPointOverride = nil; |
|
|
|
void plArmatureMod::SetSpawnPointOverride( const char *overrideObjName ) |
|
{ |
|
delete [] fSpawnPointOverride; |
|
if( overrideObjName == nil ) |
|
fSpawnPointOverride = nil; |
|
else |
|
{ |
|
fSpawnPointOverride = hsStrcpy( overrideObjName ); |
|
strlwr( fSpawnPointOverride ); |
|
} |
|
} |
|
|
|
int plArmatureMod::IFindSpawnOverride( void ) |
|
{ |
|
if( fSpawnPointOverride == nil || fSpawnPointOverride[ 0 ] == 0 ) |
|
return -1; |
|
int i; |
|
plAvatarMgr *mgr = plAvatarMgr::GetInstance(); |
|
for( i = 0; i < mgr->NumSpawnPoints(); i++ ) |
|
{ |
|
char str2[ 256 ]; |
|
strcpy(str2, mgr->GetSpawnPoint( i )->GetTarget(0)->GetKeyName()); |
|
strlwr(str2); |
|
|
|
if (strstr(str2, fSpawnPointOverride) != nil) |
|
return i; // Found it! |
|
} |
|
return -1; |
|
} |
|
|
|
void plArmatureMod::Spawn(double timeNow) |
|
{ |
|
plAvatarMgr *mgr = plAvatarMgr::GetInstance(); |
|
int numSpawnPoints = mgr->NumSpawnPoints(); |
|
|
|
int spawnNum = IFindSpawnOverride(); |
|
if( spawnNum == -1 ) |
|
{ |
|
spawnNum = plAvatarMgr::GetInstance()->FindSpawnPoint( "LinkInPointDefault" ); |
|
if( spawnNum == -1 ) |
|
{ |
|
spawnNum = plNetClientApp::GetInstance()->GetJoinOrder(); |
|
} |
|
} |
|
|
|
hsAssert(numSpawnPoints, "No spawn points! You are about to crash!"); |
|
|
|
if ( numSpawnPoints ) |
|
spawnNum %= numSpawnPoints; |
|
|
|
ValidatePhysics(); |
|
|
|
SpawnAt(spawnNum, timeNow); |
|
} |
|
|
|
void plArmatureMod::SpawnAt(int spawnNum, double time) |
|
{ |
|
plAvatarMgr *mgr = plAvatarMgr::GetInstance(); |
|
const plSpawnModifier * spawn = mgr->GetSpawnPoint(spawnNum); |
|
|
|
hsMatrix44 l2w; |
|
hsMatrix44 w2l; |
|
if ( spawn ) |
|
{ |
|
const plSceneObject * spawnObj = spawn->GetTarget(0); |
|
l2w = spawnObj->GetLocalToWorld(); |
|
w2l = spawnObj->GetWorldToLocal(); |
|
} |
|
|
|
if (fController) |
|
fController->ResetAchievedLinearVelocity(); |
|
plCoordinateInterface* ci = (plCoordinateInterface*)GetTarget(0)->GetCoordinateInterface(); |
|
l2w.RemoveScale(); |
|
w2l.RemoveScale(); |
|
ci->SetTransform(l2w, w2l); |
|
ci->FlushTransform(); |
|
|
|
if (plVirtualCam1::Instance()) |
|
plVirtualCam1::Instance()->SetCutNextTrans(); |
|
|
|
if (GetFollowerParticleSystemSO()) |
|
{ |
|
// Since particles are in world space, if we've got some surrounding us, we've got to translate them to compensate for our warp. |
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(GetFollowerParticleSystemSO()->GetModifierByType(plParticleSystem::Index()))); |
|
if (sys) |
|
{ |
|
hsPoint3 trans = l2w.GetTranslate() - GetTarget(0)->GetLocalToWorld().GetTranslate(); |
|
sys->TranslateAllParticles(trans); |
|
} |
|
} |
|
|
|
int nBrains = fBrains.size(); |
|
for (int i = 0; i < nBrains; i++) |
|
{ |
|
plArmatureBrain * curBrain = fBrains[i]; |
|
curBrain->Spawn(time); |
|
} |
|
fWaitFlags &= ~kNeedSpawn; |
|
|
|
plAvatarSpawnNotifyMsg *notify = TRACKED_NEW plAvatarSpawnNotifyMsg(); |
|
notify->SetTimeStamp(hsTimer::GetSysSeconds() + 0.1); |
|
notify->SetBCastFlag(plMessage::kBCastByExactType); |
|
notify->Send(); |
|
|
|
EnablePhysics(true); |
|
} |
|
|
|
void plArmatureMod::SetFollowerParticleSystemSO(plSceneObject *follower) |
|
{ |
|
// TODO: Check for old one and clean up. |
|
hsPoint3 trans = GetTarget(0)->GetLocalToWorld().GetTranslate() - follower->GetLocalToWorld().GetTranslate(); |
|
|
|
plWarpMsg *warp = TRACKED_NEW plWarpMsg(GetKey(), follower->GetKey(), plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity, |
|
GetTarget(0)->GetLocalToWorld()); |
|
warp->Send(); |
|
hsgResMgr::ResMgr()->AddViaNotify(follower->GetKey(), TRACKED_NEW plAttachMsg(GetTarget(0)->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef); |
|
fFollowerParticleSystemSO = follower; |
|
|
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(follower->GetModifierByType(plParticleSystem::Index()))); |
|
if (sys) |
|
{ |
|
sys->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate; |
|
sys->TranslateAllParticles(trans); |
|
sys->SetAttachedToAvatar(true); |
|
} |
|
} |
|
|
|
plSceneObject *plArmatureMod::GetFollowerParticleSystemSO() |
|
{ |
|
return fFollowerParticleSystemSO; |
|
} |
|
|
|
void plArmatureMod::RegisterForBehaviorNotify(plKey key) |
|
{ |
|
if (fNotifyKeys.Find(key) == fNotifyKeys.kMissingIndex) |
|
fNotifyKeys.Append(key); |
|
} |
|
|
|
void plArmatureMod::UnRegisterForBehaviorNotify(plKey key) |
|
{ |
|
fNotifyKeys.RemoveItem(key); |
|
} |
|
|
|
void plArmatureMod::IFireBehaviorNotify(UInt32 type, hsBool behaviorStart) |
|
{ |
|
if (fNotifyKeys.GetCount() > 0) |
|
{ |
|
plAvatarBehaviorNotifyMsg *msg = TRACKED_NEW plAvatarBehaviorNotifyMsg(); |
|
msg->SetSender(GetKey()); |
|
msg->AddReceivers(fNotifyKeys); |
|
msg->fType = type; |
|
msg->state = behaviorStart; |
|
msg->Send(); |
|
} |
|
} |
|
|
|
void plArmatureMod::EnterAge(hsBool reSpawn) |
|
{ |
|
fMidLink = false; |
|
fAlreadyPanicLinking = false; |
|
EnablePhysics(true, kDisableReasonLinking); |
|
ValidatePhysics(); |
|
|
|
// force regions to send an update whenever we enter an age |
|
fOldRegionsICareAbout.Clear(); |
|
fOldRegionsImIn.Clear(); |
|
|
|
if (GetFollowerParticleSystemSO()) |
|
{ |
|
const plParticleSystem *sys = plParticleSystem::ConvertNoRef(GetFollowerParticleSystemSO()->GetModifierByType(plParticleSystem::Index())); |
|
hsAssert(sys, "We have a particle system SO, but no system?"); |
|
if (sys) |
|
{ |
|
// Need to tell other clients about this |
|
plLoadCloneMsg *clone = TRACKED_NEW plLoadCloneMsg(GetFollowerParticleSystemSO()->GetKey(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID(), true); |
|
clone->SetBCastFlag(plMessage::kLocalPropagate, false); |
|
clone->Send(); |
|
|
|
GetFollowerParticleSystemSO()->DirtySynchState(plParticleSDLMod::kStrNumParticles, plSynchedObject::kBCastToClients); |
|
} |
|
} |
|
|
|
ClearInputFlags(true, false); |
|
if (reSpawn) |
|
fWaitFlags |= kNeedSpawn; |
|
|
|
// In case we personal age linked out of a situation and didn't properly |
|
// re-enable input... |
|
plAvatarInputInterface::GetInstance()->EnableForwardMovement(true); |
|
plAvatarInputInterface::GetInstance()->EnableJump(true); |
|
while (fSuspendInputCount > 0) |
|
ResumeInput(); |
|
plAvatarInputInterface::GetInstance()->EnableMouseMovement(); |
|
} |
|
|
|
void plArmatureMod::LeaveAge() |
|
{ |
|
plArmatureModBase::LeaveAge(); |
|
|
|
fMidLink = true; |
|
|
|
if (fController) |
|
{ |
|
fController->LeaveAge(); |
|
} |
|
|
|
if (GetFollowerParticleSystemSO()) |
|
{ |
|
// Need to tell other clients to remove this |
|
plLoadCloneMsg *clone = TRACKED_NEW plLoadCloneMsg(GetFollowerParticleSystemSO()->GetKey(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID(), false); |
|
clone->SetBCastFlag(plMessage::kLocalPropagate, false); |
|
clone->Send(); |
|
} |
|
|
|
GetArmatureEffects()->ResetEffects(); |
|
ClearInputFlags(true, false); |
|
} |
|
|
|
void plArmatureMod::PanicLink(hsBool playLinkOutAnim /* = true */) |
|
{ |
|
// console override... just go back to the beginning |
|
if (fDontPanicLink) |
|
{ |
|
Spawn(0.f); |
|
return; |
|
} |
|
|
|
if (fAlreadyPanicLinking) |
|
return; |
|
fAlreadyPanicLinking = true; |
|
|
|
|
|
plNetApp::StaticDebugMsg("plArmatureMod::PanicLink()"); |
|
|
|
// make the player book blink as they are linking out |
|
pfKIMsg *msg = TRACKED_NEW pfKIMsg( pfKIMsg::kStartBookAlert ); |
|
plgDispatch::MsgSend( msg ); |
|
|
|
// Can't depend on the anim to link if the human brain isn't ready to deal with it |
|
plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(GetCurrentBrain()); |
|
// If things break then uncomment the code below |
|
if (!brain)// || brain->IsRunningTask()) |
|
playLinkOutAnim = false; |
|
|
|
if (playLinkOutAnim) |
|
{ |
|
plAvOneShotLinkTask *task = TRACKED_NEW plAvOneShotLinkTask; |
|
|
|
char *animName = MakeAnimationName("FallingLinkOut"); |
|
task->SetAnimName(animName); |
|
task->SetMarkerName("touch"); |
|
|
|
plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(GetKey(), GetKey(), task); |
|
taskMsg->Send(); |
|
|
|
delete [] animName; |
|
} |
|
else |
|
{ |
|
EnablePhysics(false, plArmatureMod::kDisableReasonLinking); |
|
ILinkToPersonalAge(); |
|
} |
|
} |
|
|
|
void plArmatureMod::PersonalLink() |
|
{ |
|
// Can't depend on the anim to link if the human brain isn't ready to deal with it |
|
plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(GetCurrentBrain()); |
|
if (!brain || brain->IsRunningTask()) |
|
ILinkToPersonalAge(); |
|
else |
|
{ |
|
plAvOneShotLinkTask *task = TRACKED_NEW plAvOneShotLinkTask; |
|
char *animName = MakeAnimationName("PersonalLink"); |
|
task->SetAnimName(animName); |
|
task->SetMarkerName("touch"); |
|
delete [] animName; |
|
|
|
plAvTaskMsg *taskMsg = TRACKED_NEW plAvTaskMsg(GetKey(), GetKey(), task); |
|
taskMsg->SetBCastFlag(plMessage::kNetPropagate); |
|
taskMsg->Send(); |
|
} |
|
} |
|
|
|
hsBool plArmatureMod::MsgReceive(plMessage* msg) |
|
{ |
|
hsBool result = false; |
|
|
|
plArmatureBrain *curBrain = nil; |
|
if (fBrains.size() > 0) |
|
{ |
|
curBrain = fBrains.back(); |
|
if(curBrain->MsgReceive(msg)) |
|
return true; |
|
} |
|
|
|
plAvatarInputStateMsg *aisMsg = plAvatarInputStateMsg::ConvertNoRef(msg); |
|
if (aisMsg) |
|
{ |
|
IHandleInputStateMsg(aisMsg); |
|
return true; |
|
} |
|
|
|
plControlEventMsg *control = plControlEventMsg::ConvertNoRef(msg); |
|
if(control) |
|
{ |
|
IHandleControlMsg(control); |
|
return true; |
|
} |
|
|
|
plCorrectionMsg *corMsg = plCorrectionMsg::ConvertNoRef(msg); |
|
if (corMsg) |
|
{ |
|
hsMatrix44 &correction = corMsg->fWorldToLocal * GetTarget(0)->GetLocalToWorld(); |
|
if (fBoneRootAnimator) |
|
fBoneRootAnimator->SetCorrection(correction); |
|
} |
|
|
|
plGenRefMsg *refMsg = plGenRefMsg::ConvertNoRef(msg); |
|
if (refMsg) |
|
{ |
|
plClothingOutfit *outfit = plClothingOutfit::ConvertNoRef(refMsg->GetRef()); |
|
if (outfit) |
|
{ |
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) |
|
{ |
|
fClothingOutfit = outfit; |
|
fClothingOutfit->fAvatar = this; |
|
plgDispatch::Dispatch()->RegisterForExactType(plPipeRTMakeMsg::Index(), fClothingOutfit->GetKey()); |
|
} |
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) |
|
{ |
|
if (fClothingOutfit) |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plPipeRTMakeMsg::Index(), fClothingOutfit->GetKey()); |
|
fClothingOutfit = nil; |
|
outfit->fAvatar = nil; |
|
} |
|
return true; |
|
} |
|
plArmatureEffectsMgr *effects = plArmatureEffectsMgr::ConvertNoRef(refMsg->GetRef()); |
|
if (effects) |
|
{ |
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) |
|
{ |
|
fEffects = effects; |
|
fEffects->fArmature = this; |
|
} |
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) |
|
{ |
|
fEffects->fArmature = nil; |
|
fEffects = nil; |
|
} |
|
return true; |
|
} |
|
plSceneObject *so = plSceneObject::ConvertNoRef(refMsg->GetRef()); |
|
if (so) |
|
{ |
|
if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) ) |
|
{ |
|
fClothToSOMap.ExpandAndZero(refMsg->fWhich + 1); |
|
fClothToSOMap[refMsg->fWhich] = so; |
|
} |
|
else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) ) |
|
{ |
|
fClothToSOMap[refMsg->fWhich] = nil; |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
if (fEffects) |
|
{ |
|
plArmatureEffectMsg *aeMsg = plArmatureEffectMsg::ConvertNoRef(msg); |
|
plArmatureEffectStateMsg *aesMsg = plArmatureEffectStateMsg::ConvertNoRef(msg); |
|
if (aeMsg || aesMsg) |
|
return fEffects->MsgReceive(msg); |
|
} |
|
|
|
plAttachMsg *aMsg = plAttachMsg::ConvertNoRef(msg); |
|
if (aMsg) |
|
{ |
|
GetTarget(0)->MsgReceive(aMsg); |
|
return true; |
|
} |
|
|
|
plEnableMsg *enMsg = plEnableMsg::ConvertNoRef(msg); |
|
if(enMsg && enMsg->Type(plEnableMsg::kDrawable)) |
|
{ |
|
hsBool enable = enMsg->Cmd(plEnableMsg::kEnable); |
|
hsBool disable = enMsg->Cmd(plEnableMsg::kDisable); |
|
|
|
hsAssert(enable != disable, "Conflicting or missing commands to enable message"); |
|
|
|
if(enable != disable) |
|
EnableDrawing(enable); |
|
return true; |
|
} |
|
if(enMsg && enMsg->Cmd(plEnableMsg::kPhysical)) |
|
{ |
|
EnablePhysics( enMsg->Cmd(plEnableMsg::kEnable)); |
|
} |
|
|
|
plAvatarOpacityCallbackMsg *opacMsg = plAvatarOpacityCallbackMsg::ConvertNoRef(msg); |
|
if (opacMsg) |
|
{ |
|
plLayerLinkAnimation *linkAnim = IFindLayerLinkAnim(); |
|
if (linkAnim) |
|
{ |
|
bool trans = (linkAnim->GetTimeConvert().CurrentAnimTime() != 0.f); |
|
ISetTransparentDrawOrder(trans); |
|
} |
|
return true; |
|
} |
|
|
|
plAvatarPhysicsEnableCallbackMsg *epMsg = plAvatarPhysicsEnableCallbackMsg::ConvertNoRef(msg); |
|
if (epMsg) |
|
{ |
|
EnablePhysics(true); |
|
return true; |
|
} |
|
|
|
plAvatarStealthModeMsg *stealthMsg = plAvatarStealthModeMsg::ConvertNoRef(msg); |
|
if (stealthMsg && stealthMsg->GetSender() == GetTarget(0)->GetKey() && |
|
(stealthMsg->fLevel != fStealthLevel || stealthMsg->fMode != fStealthMode)) |
|
{ |
|
fStealthMode = stealthMsg->fMode; |
|
fStealthLevel = (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthVisible) ? 0 : stealthMsg->fLevel; |
|
|
|
if (fStealthMode == plAvatarStealthModeMsg::kStealthCloaked) |
|
{ |
|
if (fUpdateMsg) |
|
fUpdateMsg->SetInvis(true); |
|
} |
|
else |
|
{ |
|
if (fUpdateMsg) |
|
fUpdateMsg->SetInvis(false); |
|
} |
|
|
|
if (fEffects) |
|
fEffects->MsgReceive(stealthMsg); |
|
|
|
if (stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked) |
|
EnableDrawing(false, kDisableReasonCCR); |
|
else |
|
EnableDrawing(true, kDisableReasonCCR); |
|
|
|
DirtySynchState(kSDLAvatar, 0); // changed invisibility state |
|
plNetApp::StaticDebugMsg("ArmatureMod: rcvd avatarStealth msg, cloaked=%d", stealthMsg->fMode == plAvatarStealthModeMsg::kStealthCloaked); |
|
return true; |
|
} |
|
|
|
plParticleTransferMsg *partMsg = plParticleTransferMsg::ConvertNoRef(msg); |
|
if (partMsg) |
|
{ |
|
// First, do we have the system? |
|
plSceneObject *dstSysSO = GetFollowerParticleSystemSO(); |
|
if (!dstSysSO || ((plKeyImp*)dstSysSO->GetKey())->GetCloneOwner() != partMsg->fSysSOKey) |
|
{ |
|
// Need to clone and resend. |
|
if (plNetClientApp::GetInstance()->GetLocalPlayer() != GetTarget(0)) |
|
return true; // Only the local player can create the clone. |
|
|
|
// Clone is sent to all players. |
|
plLoadCloneMsg *cloneMsg = TRACKED_NEW plLoadCloneMsg(partMsg->fSysSOKey->GetUoid(), plAvatarMgr::GetInstance()->GetKey(), GetKey()->GetUoid().GetClonePlayerID()); |
|
cloneMsg->SetTriggerMsg(partMsg); |
|
cloneMsg->SetBCastFlag(plMessage::kNetForce); |
|
cloneMsg->Send(); |
|
|
|
// Expect to receive a clone message later. Return for now. |
|
return true; |
|
} |
|
else |
|
{ |
|
plParticleSystem *dstSys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(dstSysSO->GetModifierByType(plParticleSystem::Index()))); |
|
if (dstSys) |
|
{ |
|
// Got the system. Time to steal particles! |
|
plParticleSystem *srcSys = nil; |
|
plSceneObject *srcSysSO = plSceneObject::ConvertNoRef(partMsg->fSysSOKey->ObjectIsLoaded()); |
|
if (srcSysSO) |
|
srcSys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(srcSysSO->GetModifierByType(plParticleSystem::Index()))); |
|
|
|
// A nil source system is ok. It just won't copy anything. |
|
int numToGen = partMsg->fNumToTransfer - dstSys->StealParticlesFrom(srcSys, partMsg->fNumToTransfer); |
|
if (numToGen > 0) |
|
{ |
|
dstSys->GenerateParticles(numToGen); |
|
} |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
plLoadAvatarMsg *avLoadMsg = plLoadAvatarMsg::ConvertNoRef(msg); |
|
if (avLoadMsg) |
|
{ |
|
hsBool isPlayer = avLoadMsg->GetIsPlayer(); |
|
if (!isPlayer) |
|
plgDispatch::Dispatch()->UnRegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); |
|
|
|
// see if we're being spawned explictly |
|
plKey spawnPoint = avLoadMsg->GetSpawnPoint(); |
|
|
|
if (spawnPoint) |
|
{ |
|
hsKeyedObject * spawnKO = spawnPoint->ObjectIsLoaded(); |
|
if (spawnKO) |
|
{ |
|
plSceneObject * spawnSO = plSceneObject::ConvertNoRef(spawnKO); |
|
if(spawnSO) |
|
{ |
|
hsMatrix44 l2w = spawnSO->GetLocalToWorld(); |
|
plWarpMsg *warpM = TRACKED_NEW plWarpMsg(nil, GetTarget(0)->GetKey(), 0, l2w); |
|
warpM->Send(); |
|
fWaitFlags &= ~kNeedSpawn; |
|
} |
|
} |
|
} |
|
|
|
// copy the user string over |
|
const char* userStr = avLoadMsg->GetUserStr(); |
|
if (userStr) |
|
fUserStr = userStr; |
|
else |
|
fUserStr = ""; |
|
|
|
return true; |
|
} |
|
|
|
plLoadCloneMsg *cloneMsg = plLoadCloneMsg::ConvertNoRef(msg); |
|
if (cloneMsg) |
|
{ |
|
if (cloneMsg->GetIsLoading()) |
|
{ |
|
plSceneObject *so = plSceneObject::ConvertNoRef(cloneMsg->GetCloneKey()->ObjectIsLoaded()); |
|
if (so) |
|
{ |
|
so->SetSynchFlagsBit(plSynchedObject::kAllStateIsVolatile); |
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(so->GetModifierByType(plParticleSystem::Index()))); |
|
hsAssert(sys, "Modifier not loaded yet."); |
|
if (sys) |
|
{ |
|
sys->DisableGenerators(); |
|
sys->MsgReceive(cloneMsg); // Let the system know to finish its init stuff |
|
SetFollowerParticleSystemSO(so); |
|
if (!plNetClientApp::GetInstance()->IsLoadingInitialAgeState()) |
|
{ |
|
// Just happened. Redirect the trigger and transfer the particles |
|
MsgReceive(cloneMsg->GetTriggerMsg()); |
|
} |
|
// otherwise we'll get SDL state about it and handle it in plParticleSDLMod |
|
|
|
return true; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
if (cloneMsg->GetCloneKey() == GetFollowerParticleSystemSO()) |
|
{ |
|
SetFollowerParticleSystemSO(nil); |
|
return true; |
|
} |
|
} |
|
} |
|
|
|
plLinkEffectBCMsg *linkBCMsg = plLinkEffectBCMsg::ConvertNoRef(msg); |
|
if (linkBCMsg) |
|
{ |
|
if (GetTarget(0)->GetKey() == linkBCMsg->fLinkKey) |
|
{ |
|
if (linkBCMsg->HasLinkFlag(plLinkEffectBCMsg::kLeavingAge)) |
|
{ |
|
fMidLink = true; |
|
IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkOut, true); |
|
} |
|
else |
|
IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, true); |
|
} |
|
return true; |
|
} |
|
|
|
plLinkInDoneMsg *doneMsg = plLinkInDoneMsg::ConvertNoRef(msg); |
|
if (doneMsg) |
|
{ |
|
IFireBehaviorNotify(plHBehavior::kBehaviorTypeLinkIn, false); |
|
return true; |
|
} |
|
|
|
plAgeLoadedMsg *ageLoadMsg = plAgeLoadedMsg::ConvertNoRef(msg); |
|
if (ageLoadMsg && ageLoadMsg->fLoaded) |
|
{ |
|
// only the local player gets these |
|
NetworkSynch(hsTimer::GetSysSeconds(), true); |
|
EnablePhysics(true); |
|
return true; |
|
} |
|
|
|
plAnimCmdMsg *cmdMsg = plAnimCmdMsg::ConvertNoRef(msg); |
|
if (cmdMsg) |
|
{ |
|
hsAssert(false, "Illegal use of plAnimCmdMsg on an avatar."); |
|
return true; |
|
} |
|
|
|
plSubWorldMsg* subMsg = plSubWorldMsg::ConvertNoRef(msg); |
|
if (subMsg) |
|
{ |
|
if (fController) |
|
{ |
|
fController->SetSubworld(subMsg->fWorldKey); |
|
DirtySynchState(kSDLAvatar, plSynchedObject::kBCastToClients); |
|
} |
|
return true; |
|
} |
|
|
|
return plAGMasterMod::MsgReceive(msg); |
|
} |
|
|
|
hsBool plArmatureMod::IHandleControlMsg(plControlEventMsg* pMsg) |
|
{ |
|
// Slight change in design here... |
|
// Avatar input control messages are only sent locally. |
|
// When things change (that a remote client would care about), |
|
// we call SynchInputState and that sends off the bit vector |
|
// for the state of all avatar input controls. |
|
// |
|
// This means a remote player will only know which controls are |
|
// active at a certain point. It might miss a particular keypress. |
|
// Don't rely on them receiving it. |
|
|
|
if (fSuspendInputCount > 0) |
|
{ |
|
fQueuedCtrlMessages.push_back(pMsg); |
|
pMsg->Ref(); |
|
} |
|
else |
|
{ |
|
ControlEventCode moveCode = pMsg->GetControlCode(); |
|
|
|
hsBool flagChanged = false; |
|
if(pMsg->ControlActivated()) |
|
{ |
|
if (moveCode == B_CONTROL_TURN_TO && fClickToTurn) |
|
{ |
|
#if 0 |
|
// This will do an LOS and call TurnToPoint() |
|
plSceneInputInterface::GetInstance()->RequestAvatarTurnToPointLOS(); |
|
#else |
|
plVisHit hit; |
|
if( plVisLOSMgr::Instance()->CursorCheck(hit) ) |
|
TurnToPoint(hit.fPos); |
|
#endif |
|
return true; |
|
} |
|
|
|
// This control is intended for turning while walking/running. |
|
// It is never net propagated. We just flag our physics state |
|
// as dirty, and let the physics synch (with dead reckoning) handle it. |
|
if (moveCode == A_CONTROL_TURN) |
|
{ |
|
// Filter out the messages that are just the mouse recentering |
|
if (pMsg->GetPct() < 0.4 && pMsg->GetPct() > -0.4) |
|
{ |
|
fMouseFrameTurnStrength += pMsg->GetPct() * fMouseTurnSensitivity; |
|
SynchIfLocal(hsTimer::GetSysSeconds(), false); |
|
} |
|
} |
|
|
|
if (!GetInputFlag( moveCode ) ) |
|
{ |
|
SetInputFlag( moveCode, true ); |
|
flagChanged = true; |
|
|
|
if (moveCode == B_CONTROL_JUMP) |
|
SetInputFlag(B_CONTROL_CONSUMABLE_JUMP, true); |
|
|
|
if(plNetClientMgr::GetInstance()->AmCCR()) |
|
{ |
|
// special case for clipping: synch every key change |
|
SynchIfLocal( hsTimer::GetSysSeconds(), false); |
|
} |
|
} |
|
} else { |
|
if ( GetInputFlag( moveCode ) ) |
|
{ |
|
SetInputFlag( moveCode, false ); |
|
flagChanged = true; |
|
|
|
if (fReverseFBOnIdle && (moveCode == B_CONTROL_MOVE_FORWARD || moveCode == B_CONTROL_MOVE_BACKWARD)) |
|
{ |
|
if (!GetInputFlag(B_CONTROL_MOVE_FORWARD) && !GetInputFlag(B_CONTROL_MOVE_BACKWARD)) |
|
SetInputFlag(B_CONTROL_LADDER_INVERTED, true); |
|
} |
|
} |
|
} |
|
if (flagChanged && plAvatarInputStateMsg::IsCodeInMap(moveCode)) |
|
SynchInputState(); |
|
} |
|
return true; |
|
} |
|
|
|
void plArmatureMod::IHandleInputStateMsg(plAvatarInputStateMsg *msg) |
|
{ |
|
int i; |
|
UInt32 curBit; |
|
for (i = 0, curBit = 0x1; i < plAvatarInputStateMsg::fMapSize; i++, curBit <<= 1) |
|
{ |
|
SetInputFlag(msg->fCodeMap[i], msg->fState & curBit); |
|
} |
|
|
|
} |
|
|
|
void plArmatureMod::SynchInputState(UInt32 rcvID /* = kInvalidPlayerID */) |
|
{ |
|
if (plAvatarMgr::GetInstance()->GetLocalAvatar() != this) |
|
return; |
|
|
|
plAvatarInputStateMsg *msg = TRACKED_NEW plAvatarInputStateMsg(); |
|
int i; |
|
UInt32 curBit; |
|
for (i = 0, curBit = 0x1; i < plAvatarInputStateMsg::fMapSize; i++, curBit <<= 1) |
|
{ |
|
if (GetInputFlag(msg->fCodeMap[i])) |
|
msg->fState |= curBit; |
|
} |
|
msg->AddReceiver(GetKey()); |
|
msg->SetBCastFlag(plMessage::kNetPropagate); |
|
msg->SetBCastFlag(plMessage::kNetUseRelevanceRegions); |
|
msg->SetBCastFlag(plMessage::kLocalPropagate, false); |
|
msg->SetBCastFlag(plMessage::kNetSendUnreliable, true); |
|
if (rcvID != kInvalidPlayerID) |
|
msg->AddNetReceiver(rcvID); |
|
|
|
msg->Send(); |
|
|
|
fLastInputSynch = hsTimer::GetSysSeconds(); |
|
} |
|
|
|
void plArmatureMod::ILinkToPersonalAge() |
|
{ |
|
plNetClientMgr * nc = plNetClientMgr::GetInstance(); |
|
|
|
plAgeLinkStruct link; |
|
link.GetAgeInfo()->SetAgeFilename( kPersonalAgeFilename ); |
|
link.GetAgeInfo()->SetAgeInstanceName( kPersonalAgeInstanceName ); |
|
|
|
plSpawnPointInfo hutSpawnPoint; |
|
hutSpawnPoint.SetName(kPersonalAgeLinkInPointCloset); |
|
link.SetSpawnPoint(hutSpawnPoint); |
|
|
|
link.SetLinkingRules( plNetCommon::LinkingRules::kOriginalBook ); |
|
plLinkToAgeMsg* pMsg = TRACKED_NEW plLinkToAgeMsg( &link ); |
|
pMsg->SetLinkInAnimName("PersonalBookEnter"); |
|
pMsg->AddReceiver(nc->GetKey()); |
|
pMsg->Send(); |
|
} |
|
|
|
hsBool plArmatureMod::IEval(double time, hsScalar elapsed, UInt32 dirty) |
|
{ |
|
if (IsFinal()) |
|
{ |
|
bool noOverlap = false; |
|
|
|
const plArmatureMod *localPlayer = plAvatarMgr::GetInstance()->GetLocalAvatar(); |
|
if (plRelevanceMgr::Instance()->GetEnabled() && (localPlayer != nil)) |
|
{ |
|
// (May decide to update this elsewhere instead.) |
|
plRelevanceMgr::Instance()->SetRegionVectors(GetTarget(0)->GetLocalToWorld().GetTranslate(), fRegionsImIn, fRegionsICareAbout); |
|
if (localPlayer != this) |
|
{ |
|
if (!fRegionsImIn.Overlap(localPlayer->fRegionsICareAbout)) |
|
{ |
|
noOverlap = true; |
|
} |
|
} |
|
else // Bookkeeping for the local player... |
|
{ |
|
hsBool update = false; |
|
if (fOldRegionsICareAbout != fRegionsICareAbout) |
|
{ |
|
update = true; |
|
fOldRegionsICareAbout = fRegionsICareAbout; |
|
} |
|
if (fOldRegionsImIn != fRegionsImIn) |
|
{ |
|
update = true; |
|
fOldRegionsImIn = fRegionsImIn; |
|
} |
|
if (update) |
|
{ |
|
// Send message to the server here. |
|
plNetMsgRelevanceRegions relRegionsNetMsg; |
|
relRegionsNetMsg.SetNetProtocol(kNetProtocolCli2Game); |
|
relRegionsNetMsg.SetRegionsICareAbout(fRegionsICareAbout); |
|
relRegionsNetMsg.SetRegionsImIn(fRegionsImIn); |
|
plNetClientApp::GetInstance()->SendMsg(&relRegionsNetMsg); |
|
} |
|
} |
|
} |
|
|
|
if (localPlayer == this) |
|
{ |
|
if (time - fLastInputSynch > kAvatarInputSynchThreshold) |
|
SynchInputState(); |
|
} |
|
|
|
if (noOverlap) |
|
{ |
|
EnablePhysics(false, kDisableReasonRelRegion); |
|
EnableDrawing(false, kDisableReasonRelRegion); |
|
} |
|
else |
|
{ |
|
EnablePhysics(true, kDisableReasonRelRegion); |
|
EnableDrawing(true, kDisableReasonRelRegion); |
|
} |
|
|
|
if (fMouseFrameTurnStrength == 0 && GetInputFlag(A_CONTROL_TURN)) |
|
SetInputFlag(A_CONTROL_TURN, false); |
|
|
|
if (!fMidLink) |
|
plArmatureModBase::IEval(time, elapsed, dirty); |
|
|
|
fUpdateMsg->Ref(); |
|
fUpdateMsg->Send(); |
|
|
|
if (fPendingSynch) |
|
NetworkSynch(time, false); |
|
if (fDebugOn) |
|
RefreshDebugDisplay(); |
|
|
|
fMouseFrameTurnStrength = 0.f; // Processed this frame. Clear it. |
|
|
|
// update our attached particle system if necessary |
|
if (GetFollowerParticleSystemSO()) |
|
{ |
|
plSceneObject* follower = GetFollowerParticleSystemSO(); |
|
hsPoint3 trans = GetTarget(0)->GetLocalToWorld().GetTranslate() - follower->GetLocalToWorld().GetTranslate(); |
|
if (trans.MagnitudeSquared() > 1) // we can be a bit fuzzy about this, since the particle system is rather large and people won't notice it being off |
|
{ |
|
plWarpMsg *warp = TRACKED_NEW plWarpMsg(GetKey(), follower->GetKey(), plWarpMsg::kFlushTransform | plWarpMsg::kZeroVelocity, |
|
GetTarget(0)->GetLocalToWorld()); |
|
warp->Send(); |
|
|
|
plParticleSystem *sys = const_cast<plParticleSystem*>(plParticleSystem::ConvertNoRef(follower->GetModifierByType(plParticleSystem::Index()))); |
|
if (sys) |
|
{ |
|
sys->fMiscFlags |= plParticleSystem::kParticleSystemAlwaysUpdate; |
|
sys->TranslateAllParticles(trans); |
|
} |
|
} |
|
} |
|
} |
|
else |
|
IFinalize(); |
|
|
|
return true; |
|
} |
|
|
|
void plArmatureMod::AddTarget(plSceneObject* so) |
|
{ |
|
plArmatureModBase::AddTarget(so); |
|
|
|
plAvatarMgr::GetInstance()->AddAvatar(this); // register for easy lookup |
|
plgDispatch::Dispatch()->RegisterForExactType(plAvatarMsg::Index(), GetKey()); |
|
plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey()); |
|
|
|
// all avatars will register for the age loaded message. |
|
// only players need it, but we don't know that we're a player until it's too late to get it. |
|
// non-players will unregister when they learn the truth. |
|
if (IsLocallyOwned()) |
|
plgDispatch::Dispatch()->RegisterForExactType(plAgeLoadedMsg::Index(), GetKey()); |
|
|
|
// attach a clothingSDLModifier to handle clothing saveState |
|
delete fClothingSDLMod; |
|
fClothingSDLMod = TRACKED_NEW plClothingSDLModifier; |
|
fClothingSDLMod->SetClothingOutfit(GetClothingOutfit()); // ok if clothingOutfit is nil at this point |
|
so->AddModifier(fClothingSDLMod); |
|
|
|
// add avatar sdl modifier |
|
delete fAvatarSDLMod; |
|
fAvatarSDLMod = TRACKED_NEW plAvatarSDLModifier; |
|
so->AddModifier(fAvatarSDLMod); |
|
|
|
delete fAvatarPhysicalSDLMod; |
|
fAvatarPhysicalSDLMod = TRACKED_NEW plAvatarPhysicalSDLModifier; |
|
so->AddModifier(fAvatarPhysicalSDLMod); |
|
|
|
// At export time, this key will be nil. This is important, or else we'll overwrite the page the key comes from. |
|
if (fFootSoundSOKey != nil) |
|
hsgResMgr::ResMgr()->AddViaNotify(fFootSoundSOKey, TRACKED_NEW plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef); |
|
if (fLinkSoundSOKey != nil) |
|
hsgResMgr::ResMgr()->AddViaNotify(fLinkSoundSOKey, TRACKED_NEW plAttachMsg(so->GetKey(), nil, plRefMsg::kOnRequest), plRefFlags::kActiveRef); |
|
|
|
if (fUpdateMsg) |
|
fUpdateMsg->UnRef(); // delete an old one. |
|
|
|
fUpdateMsg = TRACKED_NEW plArmatureUpdateMsg(GetKey(), so->IsLocallyOwned(), true, this); |
|
} |
|
|
|
void plArmatureMod::RemoveTarget(plSceneObject* so) |
|
{ |
|
if (so) |
|
{ |
|
if (fClothingSDLMod) |
|
so->RemoveModifier(fClothingSDLMod); |
|
if (fAvatarSDLMod) |
|
so->RemoveModifier(fAvatarSDLMod); |
|
if (fAvatarPhysicalSDLMod) |
|
so->RemoveModifier(fAvatarPhysicalSDLMod); |
|
} |
|
delete fClothingSDLMod; |
|
fClothingSDLMod = nil; |
|
delete fAvatarSDLMod; |
|
fAvatarSDLMod = nil; |
|
delete fAvatarPhysicalSDLMod; |
|
fAvatarPhysicalSDLMod = nil; |
|
|
|
plArmatureModBase::RemoveTarget(so); |
|
} |
|
|
|
void plArmatureMod::Write(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
// Temporarily going around plArmatureModBase so that we don't |
|
// break format |
|
plAGMasterMod::Write(stream, mgr); |
|
|
|
mgr->WriteKey(stream, fMeshKeys[0]); |
|
stream->WriteSafeString(fRootName); |
|
int nBrains = fBrains.size(); |
|
stream->WriteSwap32(nBrains); |
|
for (int i = 0; i < nBrains; i++) |
|
mgr->WriteCreatable(stream, fBrains[i]); |
|
|
|
if (fClothingOutfit == nil) |
|
{ |
|
stream->WriteBool( false ); |
|
} |
|
else |
|
{ |
|
stream->WriteBool( true ); |
|
mgr->WriteKey(stream, fClothingOutfit->GetKey()); |
|
} |
|
|
|
stream->WriteSwap32(fBodyType); |
|
if( fEffects == nil ) |
|
stream->WriteBool( false ); |
|
else |
|
{ |
|
stream->WriteBool( true ); |
|
mgr->WriteKey(stream, fEffects->GetKey()); |
|
} |
|
|
|
stream->WriteSwapFloat(fPhysHeight); |
|
stream->WriteSwapFloat(fPhysWidth); |
|
|
|
stream->WriteSafeString(fAnimationPrefix.c_str()); |
|
stream->WriteSafeString(fBodyAgeName.c_str()); |
|
stream->WriteSafeString(fBodyFootstepSoundPage.c_str()); |
|
} |
|
|
|
void plArmatureMod::Read(hsStream * stream, hsResMgr *mgr) |
|
{ |
|
// Temporarily going around plArmatureModBase so that we don't |
|
// break format |
|
plAGMasterMod::Read(stream, mgr); |
|
|
|
fMeshKeys.push_back(mgr->ReadKey(stream)); |
|
|
|
// read the root name string |
|
fRootName = stream->ReadSafeString(); |
|
|
|
// read in the brains |
|
int nBrains = stream->ReadSwap32(); |
|
for (int i = 0; i < nBrains; i++) |
|
{ |
|
plArmatureBrain * brain = (plArmatureBrain *)mgr->ReadCreatable(stream); |
|
this->PushBrain(brain); |
|
} |
|
|
|
if( stream->ReadBool() ) |
|
mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plClothingBase |
|
else |
|
fClothingOutfit = nil; |
|
|
|
fBodyType = stream->ReadSwap32(); |
|
|
|
if( stream->ReadBool() ) |
|
{ |
|
plKey effectMgrKey = mgr->ReadKey(stream); |
|
mgr->AddViaNotify(effectMgrKey, TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); // plArmatureEffects |
|
|
|
// Attach the Footstep emitter scene object |
|
hsResMgr *mgr = hsgResMgr::ResMgr(); |
|
const char *age = fBodyAgeName.c_str(); |
|
const char *page = fBodyFootstepSoundPage.c_str(); |
|
const plLocation &gLoc = plKeyFinder::Instance().FindLocation(age, page); |
|
|
|
if (gLoc.IsValid()) |
|
{ |
|
const plUoid &myUoid = GetKey()->GetUoid(); |
|
plUoid SOUoid(gLoc, plSceneObject::Index(), "FootstepSoundObject"); |
|
fFootSoundSOKey = mgr->FindKey(SOUoid); |
|
if (fFootSoundSOKey) |
|
{ |
|
// So it exists... but FindKey won't properly create our clone. So we do. |
|
SOUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID()); |
|
fFootSoundSOKey = mgr->ReRegister(nil, SOUoid); |
|
} |
|
|
|
// Add the effect to our effects manager |
|
plUoid effectUoid(gLoc, plArmatureEffectFootSound::Index(), "FootstepSounds" ); |
|
plKey effectKey = mgr->FindKey(effectUoid); |
|
if (effectKey) |
|
{ |
|
effectUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID()); |
|
effectKey = mgr->ReRegister(nil, effectUoid); |
|
} |
|
if (effectKey != nil) |
|
mgr->AddViaNotify(effectKey, TRACKED_NEW plGenRefMsg(effectMgrKey, plRefMsg::kOnCreate, -1, -1), plRefFlags::kActiveRef); |
|
|
|
// Get the linking sound |
|
plUoid LinkUoid(gLoc, plSceneObject::Index(), "LinkSoundSource"); |
|
fLinkSoundSOKey = mgr->FindKey(LinkUoid); |
|
if (fLinkSoundSOKey) |
|
{ |
|
LinkUoid.SetClone(myUoid.GetClonePlayerID(), myUoid.GetCloneID()); |
|
fLinkSoundSOKey = mgr->ReRegister(nil, LinkUoid); |
|
} |
|
} |
|
} |
|
else |
|
fEffects = nil; |
|
|
|
fPhysHeight = stream->ReadSwapFloat(); |
|
fPhysWidth = stream->ReadSwapFloat(); |
|
|
|
char* temp = stream->ReadSafeString(); |
|
fAnimationPrefix = temp; |
|
delete [] temp; |
|
|
|
temp = stream->ReadSafeString(); |
|
fBodyAgeName = temp; |
|
delete [] temp; |
|
|
|
temp = stream->ReadSafeString(); |
|
fBodyFootstepSoundPage = temp; |
|
delete [] temp; |
|
|
|
plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey()); |
|
} |
|
|
|
hsBool plArmatureMod::DirtySynchState(const char* SDLStateName, UInt32 synchFlags) |
|
{ |
|
// skip requests to synch non-avatar state |
|
if (SDLStateName && stricmp(SDLStateName, kSDLAvatar)) |
|
{ |
|
return false; |
|
} |
|
|
|
synchFlags |= plSynchedObject::kForceFullSend; // TEMP |
|
|
|
if(GetNumTargets() > 0) |
|
{ |
|
plSceneObject *sObj = GetTarget(0); |
|
if(sObj) |
|
sObj->DirtySynchState(SDLStateName, synchFlags); |
|
} |
|
return false; |
|
} |
|
|
|
hsBool plArmatureMod::DirtyPhysicalSynchState(UInt32 synchFlags) |
|
{ |
|
synchFlags |= plSynchedObject::kForceFullSend; // TEMP |
|
synchFlags |= plSynchedObject::kBCastToClients; |
|
|
|
if(GetNumTargets() > 0) |
|
{ |
|
plSceneObject *sObj = GetTarget(0); |
|
if(sObj) |
|
sObj->DirtySynchState(kSDLAvatarPhysical, synchFlags); |
|
} |
|
return false; |
|
} |
|
|
|
void plArmatureMod::IFinalize() |
|
{ |
|
plArmatureModBase::IFinalize(); |
|
|
|
if (fWaitFlags & kNeedAudio) |
|
{ |
|
plSetListenerMsg *msg = TRACKED_NEW plSetListenerMsg( plSetListenerMsg::kVelocity, GetTarget(0)->GetKey(), true ); |
|
msg->Send(); |
|
fWaitFlags &= ~kNeedAudio; |
|
} |
|
|
|
if (fWaitFlags & kNeedCamera) |
|
{ |
|
plCameraMsg* pMsg = TRACKED_NEW plCameraMsg; |
|
pMsg->SetCmd(plCameraMsg::kCreateNewDefaultCam); |
|
pMsg->SetCmd(plCameraMsg::kSetSubject); |
|
pMsg->SetSubject(GetTarget(0)); |
|
pMsg->SetBCastFlag( plMessage::kBCastByExactType ); |
|
pMsg->Send(); |
|
fWaitFlags &= ~kNeedCamera; |
|
} |
|
|
|
if (fWaitFlags & kNeedSpawn) |
|
{ |
|
Spawn(hsTimer::GetSysSeconds()); |
|
fWaitFlags &= ~kNeedSpawn; |
|
} |
|
} |
|
|
|
void plArmatureMod::ICustomizeApplicator() |
|
{ |
|
plArmatureModBase::ICustomizeApplicator(); |
|
|
|
const plAGModifier *agMod = GetChannelMod("Bone_Root", true); |
|
if (agMod) |
|
{ |
|
// are there any applicators that manipulate the transform? |
|
plAGApplicator *app = agMod->GetApplicator(kAGPinTransform); |
|
if(app) |
|
{ |
|
plMatrixDelayedCorrectionApplicator *corApp = plMatrixDelayedCorrectionApplicator::ConvertNoRef(app); |
|
if (corApp) |
|
{ |
|
fBoneRootAnimator = corApp; |
|
fWaitFlags &= ~kNeedApplicator; |
|
return; // already there |
|
} |
|
} |
|
plAGModifier *volAGMod = const_cast<plAGModifier *>(agMod); |
|
fBoneRootAnimator = TRACKED_NEW plMatrixDelayedCorrectionApplicator(); |
|
volAGMod->SetApplicator(fBoneRootAnimator); |
|
fWaitFlags &= ~kNeedApplicator; |
|
} |
|
} |
|
|
|
const plSceneObject *plArmatureMod::GetClothingSO(UInt8 lod) const |
|
{ |
|
if (fClothToSOMap.GetCount() <= lod) |
|
return nil; |
|
|
|
return fClothToSOMap[lod]; |
|
} |
|
|
|
#define kSynchInterval 1 // synch once per second |
|
|
|
void plArmatureMod::NetworkSynch(double timeNow, int force) |
|
{ |
|
if (force || ((timeNow - fLastSynch) > kSynchInterval)) |
|
{ |
|
// make sure state change gets sent out over the network |
|
// avatar state should use relevance region filtering |
|
UInt32 flags = kBCastToClients | kUseRelevanceRegions; |
|
if (force) |
|
flags |= kForceFullSend; |
|
DirtyPhysicalSynchState(flags); |
|
fLastSynch = timeNow; |
|
fPendingSynch = false; |
|
} |
|
else |
|
fPendingSynch = true; |
|
} |
|
|
|
hsBool plArmatureMod::IsLocalAvatar() |
|
{ |
|
return plAvatarMgr::GetInstance()->GetLocalAvatar() == this; |
|
} |
|
|
|
hsBool plArmatureMod::IsLocalAI() |
|
{ |
|
plAvBrainCritter* ai = plAvBrainCritter::ConvertNoRef(FindBrainByClass(plAvBrainCritter::Index())); |
|
if (ai) |
|
return ai->LocallyControlled(); |
|
return false; // not an AI, obviously not local |
|
} |
|
|
|
void plArmatureMod::SynchIfLocal(double timeNow, int force) |
|
{ |
|
if (IsLocalAvatar() || IsLocalAI()) |
|
{ |
|
NetworkSynch(timeNow, force); |
|
} |
|
} |
|
|
|
plLayerLinkAnimation *plArmatureMod::IFindLayerLinkAnim() |
|
{ |
|
int i; |
|
hsGMaterial *mat = fClothingOutfit->fMaterial; |
|
if (!mat) |
|
return nil; |
|
|
|
for (i = 0; i < mat->GetNumLayers(); i++) |
|
{ |
|
plLayerInterface *li = mat->GetLayer(i); |
|
while (li != nil) |
|
{ |
|
plLayerLinkAnimation *anim = plLayerLinkAnimation::ConvertNoRef(li); |
|
if (anim) |
|
return anim; |
|
|
|
li = li->GetUnderLay(); |
|
} |
|
} |
|
return nil; |
|
} |
|
|
|
hsBool plArmatureMod::ValidatePhysics() |
|
{ |
|
if (!fTarget) |
|
return false; |
|
|
|
if (!fController) |
|
fController = plPhysicalControllerCore::Create(GetTarget(0)->GetKey(), fPhysHeight, fPhysWidth); |
|
|
|
if (fController) |
|
{ |
|
if (GetTarget(0)->GetKey() == plNetClientApp::GetInstance()->GetLocalPlayerKey()) |
|
{ |
|
// local avatars get added to a special LOS db just for them. |
|
fController->SetLOSDB(plSimDefs::kLOSDBLocalAvatar); |
|
} |
|
else |
|
{ |
|
// non-local avatars are in the same LOS db as clickables |
|
fController->SetLOSDB(plSimDefs::kLOSDBUIItems); |
|
} |
|
|
|
hsClearBits(fWaitFlags, kNeedPhysics); |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
|
|
} |
|
|
|
hsBool plArmatureMod::ValidateMesh() |
|
{ |
|
if (fWaitFlags & kNeedMesh) |
|
{ |
|
fWaitFlags &= ~kNeedMesh; |
|
int n = fMeshKeys.size(); |
|
|
|
for(int i = 0; i < n; i++) |
|
{ |
|
plKey meshKey = fMeshKeys[i]; |
|
plSceneObject * meshObj = (plSceneObject *)meshKey->GetObjectPtr(); |
|
|
|
if( ! meshObj) |
|
{ |
|
fWaitFlags |= kNeedMesh; |
|
break; |
|
} |
|
hsBool visible = (i == fCurLOD) ? true : false; |
|
|
|
EnableDrawingTree(meshObj, visible); |
|
|
|
// If we haven't created the mapping yet... |
|
if (fClothToSOMap.GetCount() <= i || fClothToSOMap[i] == nil) |
|
{ |
|
plGenRefMsg *refMsg = TRACKED_NEW plGenRefMsg(GetKey(), plRefMsg::kOnRequest, i, 0); |
|
hsgResMgr::ResMgr()->SendRef(meshObj->GetKey(), refMsg, plRefFlags::kPassiveRef); |
|
} |
|
} |
|
if (!strcmp(GetTarget(0)->GetKeyName(), "Yeesha")) |
|
ISetTransparentDrawOrder(true); |
|
else |
|
ISetTransparentDrawOrder(false); |
|
} |
|
|
|
return !(fWaitFlags & kNeedMesh); |
|
} |
|
|
|
plArmatureBrain * plArmatureMod::GetNextBrain(plArmatureBrain *brain) |
|
{ |
|
plArmatureBrain * result = nil; |
|
bool passedTarget = false; |
|
|
|
int count = fBrains.size(); |
|
for(int i = count - 1; i >= 0; i--) |
|
{ |
|
plArmatureBrain *curBrain = fBrains.at(i); |
|
if(passedTarget) |
|
result = curBrain; |
|
else { |
|
if(curBrain == brain) |
|
passedTarget = true; |
|
} |
|
} |
|
|
|
return result; |
|
} |
|
|
|
int plArmatureMod::GetBrainCount() |
|
{ |
|
return fBrains.size(); |
|
} |
|
|
|
plArmatureBrain * plArmatureMod::FindBrainByClass(UInt32 classID) const |
|
{ |
|
int n = fBrains.size(); |
|
|
|
for(int i = 0; i < n; i++) |
|
{ |
|
plArmatureBrain *brain = fBrains.at(i); |
|
|
|
if(brain->ClassIndex() == classID) |
|
{ |
|
return brain; |
|
} |
|
} |
|
return nil; |
|
} |
|
|
|
void plArmatureMod::TurnToPoint(hsPoint3 &point) |
|
{ |
|
plAvBrainHuman *brain = plAvBrainHuman::ConvertNoRef(FindBrainByClass(plAvBrainHuman::Index())); |
|
if (brain) |
|
brain->TurnToPoint(point); |
|
} |
|
|
|
void plArmatureMod::SuspendInput() |
|
{ |
|
if (fSuspendInputCount == 0 && plNetClientApp::GetInstance()->GetLocalPlayer() == GetTarget(0)) |
|
{ |
|
plAvatarInputInterface::GetInstance()->SuspendMouseMovement(); |
|
PreserveInputState(); |
|
ClearInputFlags(false, false); |
|
} |
|
|
|
fSuspendInputCount++; |
|
} |
|
|
|
void plArmatureMod::ResumeInput() |
|
{ |
|
if (fSuspendInputCount > 0) // decrementing an unsigned variable set to 0 is BAD |
|
fSuspendInputCount--; |
|
if(fSuspendInputCount == 0) |
|
{ |
|
RestoreInputState(); |
|
IProcessQueuedInput(); |
|
if (plNetClientApp::GetInstance()->GetLocalPlayer() == GetTarget(0)) |
|
plAvatarInputInterface::GetInstance()->EnableMouseMovement(); |
|
} |
|
} |
|
|
|
void plArmatureMod::IProcessQueuedInput() |
|
{ |
|
CtrlMessageVec::iterator i = fQueuedCtrlMessages.begin(); |
|
CtrlMessageVec::iterator end = fQueuedCtrlMessages.end(); |
|
while(i != end) |
|
{ |
|
plControlEventMsg *ctrlMsg = *i; |
|
IHandleControlMsg(ctrlMsg); |
|
ctrlMsg->UnRef(); |
|
(*i) = nil; |
|
i++; |
|
} |
|
fQueuedCtrlMessages.clear(); |
|
} |
|
|
|
void plArmatureMod::PreserveInputState() |
|
{ |
|
fMoveFlagsBackup = fMoveFlags; |
|
} |
|
|
|
void plArmatureMod::RestoreInputState() |
|
{ |
|
fMoveFlags = fMoveFlagsBackup; |
|
|
|
} |
|
|
|
hsBool plArmatureMod::GetInputFlag (int which) const |
|
{ |
|
return fMoveFlags.IsBitSet(which); |
|
} |
|
|
|
void plArmatureMod::SetInputFlag(int which, hsBool status) |
|
{ |
|
if(status) |
|
{ |
|
fMoveFlags.SetBit(which); |
|
} else { |
|
fMoveFlags.ClearBit(which); |
|
} |
|
} |
|
|
|
hsBool plArmatureMod::HasMovementFlag() const |
|
{ |
|
return (fMoveFlags.IsBitSet(B_CONTROL_MOVE_FORWARD) || |
|
fMoveFlags.IsBitSet(B_CONTROL_MOVE_BACKWARD) || |
|
fMoveFlags.IsBitSet(B_CONTROL_ROTATE_LEFT) || |
|
fMoveFlags.IsBitSet(B_CONTROL_ROTATE_RIGHT) || |
|
fMoveFlags.IsBitSet(A_CONTROL_TURN) || |
|
fMoveFlags.IsBitSet(B_CONTROL_STRAFE_LEFT) || |
|
fMoveFlags.IsBitSet(B_CONTROL_STRAFE_RIGHT)); |
|
} |
|
|
|
bool plArmatureMod::ForwardKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_MOVE_FORWARD) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::BackwardKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_MOVE_BACKWARD) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::StrafeLeftKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_STRAFE_LEFT) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::StrafeRightKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_STRAFE_RIGHT) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::FastKeyDown() const |
|
{ |
|
return ((GetInputFlag(B_CONTROL_MODIFIER_FAST) && !GetInputFlag(B_CONTROL_ALWAYS_RUN)) || |
|
(!GetInputFlag(B_CONTROL_MODIFIER_FAST) && GetInputFlag(B_CONTROL_ALWAYS_RUN))); |
|
} |
|
|
|
bool plArmatureMod::StrafeKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_MODIFIER_STRAFE) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::TurnLeftKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_ROTATE_LEFT) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::TurnRightKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_ROTATE_RIGHT) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::JumpKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_JUMP) ? true : false; |
|
} |
|
|
|
bool plArmatureMod::ExitModeKeyDown() const |
|
{ |
|
return GetInputFlag(B_CONTROL_EXIT_MODE) ? true : false; |
|
} |
|
|
|
void plArmatureMod::SetForwardKeyDown() |
|
{ |
|
SetInputFlag(B_CONTROL_MOVE_FORWARD, true); |
|
} |
|
|
|
void plArmatureMod::SetBackwardKeyDown() |
|
{ |
|
SetInputFlag(B_CONTROL_MOVE_BACKWARD, true); |
|
} |
|
|
|
void plArmatureMod::SetStrafeLeftKeyDown(bool status) |
|
{ |
|
SetInputFlag(B_CONTROL_STRAFE_LEFT, status); |
|
} |
|
|
|
void plArmatureMod::SetStrafeRightKeyDown(bool status) |
|
{ |
|
SetInputFlag(B_CONTROL_STRAFE_RIGHT, status); |
|
} |
|
|
|
void plArmatureMod::SetFastKeyDown() |
|
{ |
|
SetInputFlag(B_CONTROL_MODIFIER_FAST, true); |
|
} |
|
|
|
void plArmatureMod::SetTurnLeftKeyDown(bool status) |
|
{ |
|
SetInputFlag(B_CONTROL_ROTATE_LEFT, status); |
|
} |
|
|
|
void plArmatureMod::SetTurnRightKeyDown(bool status) |
|
{ |
|
SetInputFlag(B_CONTROL_ROTATE_RIGHT, status); |
|
} |
|
|
|
void plArmatureMod::SetJumpKeyDown() |
|
{ |
|
SetInputFlag(B_CONTROL_JUMP, true); |
|
} |
|
|
|
hsScalar plArmatureMod::GetTurnStrength() const |
|
{ |
|
return GetKeyTurnStrength() + GetAnalogTurnStrength(); |
|
} |
|
|
|
hsScalar plArmatureMod::GetKeyTurnStrength() const |
|
{ |
|
if (StrafeKeyDown()) |
|
return 0.f; |
|
|
|
return (TurnLeftKeyDown() ? 1.f : 0.f) + (TurnRightKeyDown() ? -1.f: 0.f); |
|
} |
|
|
|
hsScalar plArmatureMod::GetAnalogTurnStrength() const |
|
{ |
|
if (StrafeKeyDown()) |
|
return 0.f; |
|
|
|
hsScalar elapsed = hsTimer::GetDelSysSeconds(); |
|
if (elapsed > 0) |
|
return fMouseFrameTurnStrength / elapsed; |
|
else |
|
return 0; |
|
} |
|
|
|
void plArmatureMod::SetReverseFBOnIdle(bool val) |
|
{ |
|
fReverseFBOnIdle = val; |
|
if (val) |
|
SetInputFlag(B_CONTROL_LADDER_INVERTED, !(ForwardKeyDown() || BackwardKeyDown())); |
|
else |
|
SetInputFlag(B_CONTROL_LADDER_INVERTED, false); |
|
} |
|
|
|
hsBool plArmatureMod::IsFBReversed() |
|
{ |
|
return GetInputFlag(B_CONTROL_LADDER_INVERTED); |
|
} |
|
|
|
void plArmatureMod::ClearInputFlags(bool saveAlwaysRun, bool clearBackup) |
|
{ |
|
bool alwaysRun = (fMoveFlags.IsBitSet(B_CONTROL_ALWAYS_RUN) != 0); |
|
bool fast = (fMoveFlags.IsBitSet(B_CONTROL_MODIFIER_FAST) != 0); |
|
fMoveFlags.Clear(); |
|
|
|
if (saveAlwaysRun) |
|
{ |
|
if (alwaysRun) |
|
fMoveFlags.SetBit(B_CONTROL_ALWAYS_RUN); |
|
if (fast) |
|
fMoveFlags.SetBit(B_CONTROL_MODIFIER_FAST); |
|
} |
|
|
|
if (clearBackup) |
|
{ |
|
alwaysRun = (fMoveFlagsBackup.IsBitSet(B_CONTROL_ALWAYS_RUN) != 0); |
|
fast = (fMoveFlagsBackup.IsBitSet(B_CONTROL_MODIFIER_FAST) != 0); |
|
fMoveFlagsBackup.Clear(); |
|
|
|
if (saveAlwaysRun) |
|
{ |
|
if (alwaysRun) |
|
fMoveFlagsBackup.SetBit(B_CONTROL_ALWAYS_RUN); |
|
if (fast) |
|
fMoveFlagsBackup.SetBit(B_CONTROL_MODIFIER_FAST); |
|
} |
|
} |
|
} |
|
|
|
plAGModifier * plArmatureMod::GetRootAGMod() |
|
{ |
|
if(fRootAGMod) |
|
return fRootAGMod; |
|
|
|
if(fTarget) |
|
{ |
|
const plAGModifier * shit = plAGModifier::ConvertNoRef(FindModifierByClass(fTarget, plAGModifier::Index())); |
|
fRootAGMod = const_cast<plAGModifier *>(shit); |
|
} |
|
|
|
return fRootAGMod; |
|
} |
|
|
|
int plArmatureMod::GetCurrentGenericType() |
|
{ |
|
plAvBrainGeneric *brain = plAvBrainGeneric::ConvertNoRef(GetCurrentBrain()); |
|
|
|
if (!brain) |
|
return plAvBrainGeneric::kNonGeneric; |
|
else |
|
return brain->GetType(); |
|
} |
|
|
|
bool plArmatureMod::FindMatchingGenericBrain(const char *names[], int count) |
|
{ |
|
int i; |
|
for (i = 0; i < GetBrainCount(); i++) |
|
{ |
|
plAvBrainGeneric *brain = plAvBrainGeneric::ConvertNoRef(GetBrain(i)); |
|
if (brain && brain->MatchAnimNames(names, count)) |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
char *plArmatureMod::MakeAnimationName(const char *baseName) const |
|
{ |
|
std::string temp = fAnimationPrefix + baseName; |
|
char *result = hsStrcpy(temp.c_str()); // why they want a new string I'll never know... but hey, too late to change it now |
|
return result; |
|
} |
|
|
|
char *plArmatureMod::GetRootName() |
|
{ |
|
return fRootName; |
|
} |
|
|
|
void plArmatureMod::SetRootName(const char *name) |
|
{ |
|
delete [] fRootName; |
|
fRootName = hsStrcpy(name); |
|
} |
|
|
|
plAGAnim *plArmatureMod::FindCustomAnim(const char *baseName) const |
|
{ |
|
char *customName = MakeAnimationName(baseName); |
|
plAGAnim *result = plAGAnim::FindAnim(customName); |
|
delete[] customName; |
|
return result; |
|
} |
|
|
|
void plArmatureMod::ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc) |
|
{ |
|
std::vector<char*> markers; |
|
anim->CopyMarkerNames(markers); |
|
|
|
int i; |
|
for (i = 0; i < markers.size(); i++) |
|
{ |
|
|
|
hsScalar time = -1; |
|
hsBool isLeft = false; |
|
if (strstr(markers[i], "SndLeftFootDown") == markers[i]) |
|
{ |
|
isLeft = true; |
|
time = anim->GetMarker(markers[i]); |
|
} |
|
if (strstr(markers[i], "SndRightFootDown") == markers[i]) |
|
time = anim->GetMarker(markers[i]); |
|
|
|
if (time >= 0) |
|
{ |
|
plEventCallbackInterceptMsg *iMsg; |
|
|
|
plArmatureEffectMsg *msg = TRACKED_NEW plArmatureEffectMsg(fEffects->GetKey(), kTime); |
|
msg->fEventTime = time; |
|
msg->fTriggerIdx = AnimNameToIndex(anim->GetName()); |
|
|
|
iMsg = TRACKED_NEW plEventCallbackInterceptMsg(); |
|
iMsg->AddReceiver(fEffects->GetKey()); |
|
iMsg->fEventTime = time; |
|
iMsg->fEvent = kTime; |
|
iMsg->SetMessage(msg); |
|
atc->AddCallback(iMsg); |
|
hsRefCnt_SafeUnRef(msg); |
|
hsRefCnt_SafeUnRef(iMsg); |
|
|
|
plAvatarFootMsg* foot = TRACKED_NEW plAvatarFootMsg(GetKey(), this, isLeft); |
|
foot->fEventTime = time; |
|
|
|
iMsg = TRACKED_NEW plEventCallbackInterceptMsg(); |
|
iMsg->AddReceiver(fEffects->GetKey()); |
|
iMsg->fEventTime = time; |
|
iMsg->fEvent = kTime; |
|
iMsg->SetMessage(foot); |
|
atc->AddCallback(iMsg); |
|
hsRefCnt_SafeUnRef(foot); |
|
hsRefCnt_SafeUnRef(iMsg); |
|
} |
|
|
|
delete [] markers[i]; // done with this string, nuke it |
|
} |
|
} |
|
|
|
const char *plArmatureMod::GetAnimRootName(const char *name) |
|
{ |
|
return name + fAnimationPrefix.length(); |
|
} |
|
|
|
Int8 plArmatureMod::AnimNameToIndex(const char *name) |
|
{ |
|
const char *rootName = GetAnimRootName(name); |
|
Int8 result = -1; |
|
|
|
if (!strcmp(rootName, "Walk") || !strcmp(rootName, "WalkBack") || |
|
!strcmp(rootName, "LadderDown") || !strcmp(rootName, "LadderDownOn") || |
|
!strcmp(rootName, "LadderDownOff") || !strcmp(rootName, "LadderUp") || |
|
!strcmp(rootName, "LadderUpOn") || !strcmp(rootName, "LadderUpOff") || |
|
!strcmp(rootName, "SwimSlow") || !strcmp(rootName, "SwimBackward") || |
|
!strcmp(rootName, "BallPushWalk")) |
|
result = kWalk; |
|
else if (!strcmp(rootName, "Run") || !strcmp(rootName, "SwimFast")) |
|
result = kRun; |
|
else if (!strcmp(rootName, "TurnLeft") || !strcmp(rootName, "TurnRight") || |
|
!strcmp(rootName, "StepLeft") || !strcmp(rootName, "StepRight") || |
|
!strcmp(rootName, "SideSwimLeft") || !strcmp(rootName, "SideSwimRight") || |
|
!strcmp(rootName, "TreadWaterTurnLeft") || !strcmp(rootName, "TreadWaterTurnRight")) |
|
result = kTurn; |
|
else if (!strcmp(rootName, "GroundImpact") || !strcmp(rootName, "RunningImpact")) |
|
result = kImpact; |
|
else if (strstr(rootName, "Run")) // Critters |
|
result = kRun; |
|
else if (strstr(rootName, "Idle")) // Critters |
|
result = kWalk; |
|
|
|
return result; |
|
} |
|
|
|
hsBool plArmatureMod::IsInStealthMode() const |
|
{ |
|
return (fStealthMode != plAvatarStealthModeMsg::kStealthVisible); |
|
} |
|
|
|
bool plArmatureMod::IsOpaque() |
|
{ |
|
return fOpaque; |
|
} |
|
|
|
bool plArmatureMod::IsMidLink() |
|
{ |
|
return fMidLink; |
|
} |
|
|
|
hsBool plArmatureMod::ConsumeJump() |
|
{ |
|
if (!GetInputFlag(B_CONTROL_CONSUMABLE_JUMP)) |
|
return false; |
|
|
|
SetInputFlag(B_CONTROL_CONSUMABLE_JUMP, false); |
|
return true; |
|
} |
|
|
|
void plArmatureMod::ISetTransparentDrawOrder(bool val) |
|
{ |
|
if (fOpaque != val) |
|
return; |
|
|
|
fOpaque = !val; |
|
|
|
plDrawableSpans *spans = plDrawableSpans::ConvertNoRef(FindDrawable()); |
|
if (spans) |
|
{ |
|
spans->SetNativeProperty(plDrawable::kPropPartialSort, true); |
|
spans->SetNativeProperty(plDrawable::kPropSortAsOne, true); |
|
if (val) |
|
{ |
|
spans->SetNativeProperty(plDrawable::kPropSortSpans, true); |
|
spans->SetRenderLevel(plRenderLevel(plRenderLevel::kBlendRendMajorLevel, plRenderLevel::kDefRendMinorLevel)); |
|
} |
|
else |
|
{ |
|
spans->SetNativeProperty(plDrawable::kPropSortSpans, false); |
|
spans->SetRenderLevel(plRenderLevel(0, plRenderLevel::kAvatarRendMinorLevel)); |
|
} |
|
} |
|
} |
|
|
|
bool plArmatureMod::IsKILowestLevel() |
|
{ |
|
if ( GetKILevel() == pfKIMsg::kNanoKI ) |
|
return true; |
|
else |
|
return false; |
|
} |
|
|
|
int plArmatureMod::GetKILevel() |
|
{ |
|
return VaultGetKILevel(); |
|
} |
|
|
|
void plArmatureMod::SetLinkInAnim(const char *animName) |
|
{ |
|
if (animName) |
|
{ |
|
plAGAnim *anim = FindCustomAnim(animName); |
|
fLinkInAnimKey = (anim ? anim->GetKey() : nil); |
|
} |
|
else |
|
fLinkInAnimKey = nil; |
|
} |
|
|
|
plKey plArmatureMod::GetLinkInAnimKey() const |
|
{ |
|
return fLinkInAnimKey; |
|
} |
|
|
|
|
|
////////////////////// |
|
// |
|
// PLARMATURELODMOD |
|
// |
|
////////////////////// |
|
|
|
plArmatureLODMod::plArmatureLODMod() |
|
{ |
|
} |
|
|
|
// CTOR (physical, name) |
|
plArmatureLODMod::plArmatureLODMod(const char* root_name) |
|
: plArmatureMod() |
|
{ |
|
fRootName = hsStrcpy(root_name); |
|
} |
|
|
|
plArmatureLODMod::~plArmatureLODMod() |
|
{ |
|
} |
|
|
|
void plArmatureLODMod::Read(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
plArmatureMod::Read(stream, mgr); |
|
|
|
fMeshKeys.clear(); |
|
int meshKeyCount = stream->ReadSwap32(); |
|
for(int i = 0; i < meshKeyCount; i++) |
|
{ |
|
plKey meshKey = mgr->ReadKey(stream); |
|
fMeshKeys.push_back(meshKey); |
|
|
|
plKeyVector *vec = TRACKED_NEW plKeyVector; |
|
int boneCount = stream->ReadSwap32(); |
|
for(int j = 0; j < boneCount; j++) |
|
vec->push_back(mgr->ReadKey(stream)); |
|
fUnusedBones.push_back(vec); |
|
} |
|
} |
|
|
|
// WRITE |
|
void plArmatureLODMod::Write(hsStream *stream, hsResMgr *mgr) |
|
{ |
|
plArmatureMod::Write(stream, mgr); |
|
|
|
int meshKeyCount = fMeshKeys.size(); |
|
stream->WriteSwap32(meshKeyCount); |
|
|
|
for(int i = 0; i < meshKeyCount; i++) |
|
{ |
|
plKey meshKey = fMeshKeys[i]; |
|
mgr->WriteKey(stream, meshKey); |
|
|
|
// Should be a list per mesh key |
|
stream->WriteSwap32(fUnusedBones[i]->size()); |
|
for(int j = 0; j < fUnusedBones[i]->size(); j++) |
|
mgr->WriteKey(stream, (*fUnusedBones[i])[j]); |
|
} |
|
} |
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////////////////////// |
|
/////////////////////////////////////////////////////////////////////////////////////////////// |
|
// DEBUG SUPPORT |
|
|
|
int plArmatureMod::RefreshDebugDisplay() |
|
{ |
|
plDebugText &debugTxt = plDebugText::Instance(); |
|
char strBuf[ 2048 ]; |
|
int lineHeight = debugTxt.GetFontSize() + 4; |
|
UInt32 scrnWidth, scrnHeight; |
|
|
|
debugTxt.GetScreenSize( &scrnWidth, &scrnHeight ); |
|
int y = 10; |
|
int x = 10; |
|
|
|
DumpToDebugDisplay(x, y, lineHeight, strBuf, debugTxt); |
|
return y; |
|
} |
|
|
|
void plArmatureMod::DumpToDebugDisplay(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) |
|
{ |
|
sprintf(strBuf, "Armature <%s>:", fRootName); |
|
debugTxt.DrawString(x, y, strBuf, 255, 128, 128); |
|
y += lineHeight; |
|
|
|
plSceneObject * SO = GetTarget(0); |
|
if(SO) |
|
{ |
|
// global location |
|
hsMatrix44 l2w = SO->GetLocalToWorld(); |
|
hsPoint3 worldPos = l2w.GetTranslate(); |
|
|
|
char *opaque = IsOpaque() ? "yes" : "no"; |
|
sprintf(strBuf, "position(world): %.2f, %.2f, %.2f Opaque: %3s", |
|
worldPos.fX, worldPos.fY, worldPos.fZ, opaque); |
|
debugTxt.DrawString(x, y, strBuf); |
|
y += lineHeight; |
|
|
|
hsPoint3 kPos; |
|
char *kinematic = "n.a."; |
|
const char* frozen = "n.a."; |
|
if (fController) |
|
{ |
|
fController->GetKinematicPosition(kPos); |
|
kinematic = fController->IsKinematic() ? "on" : "off"; |
|
} |
|
sprintf(strBuf, "kinematc(world): %.2f, %.2f, %.2f Kinematic: %3s", |
|
kPos.fX, kPos.fY, kPos.fZ,kinematic); |
|
debugTxt.DrawString(x, y, strBuf); |
|
y += lineHeight; |
|
|
|
if (fController) |
|
frozen = fController->IsEnabled() ? "no" : "yes"; |
|
|
|
// are we in a subworld? |
|
plKey world = nil; |
|
if (fController) |
|
world = fController->GetSubworld(); |
|
sprintf(strBuf, "In world: %s Frozen: %s", world ? world->GetName() : "nil", frozen); |
|
debugTxt.DrawString(x,y, strBuf); |
|
y+= lineHeight; |
|
|
|
if (fController) |
|
{ |
|
hsPoint3 physPos; |
|
GetPositionAndRotationSim(&physPos, nil); |
|
const hsVector3& vel = fController->GetLinearVelocity(); |
|
sprintf(strBuf, "position(physical): <%.2f, %.2f, %.2f> velocity: <%5.2f, %5.2f, %5.2f>", physPos.fX, physPos.fY, physPos.fZ, vel.fX, vel.fY, vel.fZ); |
|
} |
|
else |
|
{ |
|
sprintf(strBuf, "position(physical): no controller"); |
|
} |
|
debugTxt.DrawString(x, y, strBuf); |
|
y += lineHeight; |
|
} |
|
|
|
DebugDumpMoveKeys(x, y, lineHeight, strBuf, debugTxt); |
|
|
|
int i; |
|
for(i = 0; i < fBrains.size(); i++) |
|
{ |
|
plArmatureBrain *brain = fBrains[i]; |
|
brain->DumpToDebugDisplay(x, y, lineHeight, strBuf, debugTxt); |
|
} |
|
|
|
if (fClothingOutfit) |
|
{ |
|
y += lineHeight; |
|
|
|
debugTxt.DrawString(x, y, "ItemsWorn:"); |
|
y += lineHeight; |
|
strBuf[0] = '\0'; |
|
int itemCount = 0; |
|
for (i = 0; i < fClothingOutfit->fItems.GetCount(); i++) |
|
{ |
|
if (itemCount == 0) |
|
strcat(strBuf, " "); |
|
|
|
strcat(strBuf, fClothingOutfit->fItems[i]->fName); |
|
itemCount++; |
|
|
|
if (itemCount == 4) |
|
{ |
|
debugTxt.DrawString(x, y, strBuf); |
|
itemCount = 0; |
|
strBuf[0] = '\0'; |
|
y += lineHeight; |
|
} |
|
|
|
if (itemCount > 0) |
|
strcat(strBuf, ", "); |
|
} |
|
if (itemCount > 0) |
|
{ |
|
debugTxt.DrawString(x, y, strBuf); |
|
y += lineHeight; |
|
} |
|
} |
|
|
|
if (plRelevanceMgr::Instance()->GetEnabled()) |
|
{ |
|
y += lineHeight; |
|
|
|
debugTxt.DrawString(x, y, "Relevance Regions:"); |
|
y += lineHeight; |
|
sprintf(strBuf, " In: %s", plRelevanceMgr::Instance()->GetRegionNames(fRegionsImIn).c_str()); |
|
debugTxt.DrawString(x, y, strBuf); |
|
y += lineHeight; |
|
sprintf(strBuf, " Care about: %s", plRelevanceMgr::Instance()->GetRegionNames(fRegionsICareAbout).c_str()); |
|
debugTxt.DrawString(x, y, strBuf); |
|
y += lineHeight; |
|
} |
|
} |
|
|
|
class plAvBoneMap::BoneMapImp |
|
{ |
|
public: |
|
typedef std::map<UInt32, const plSceneObject *> id2SceneObjectMap; |
|
id2SceneObjectMap fMap; |
|
}; |
|
|
|
plAvBoneMap::plAvBoneMap() |
|
{ |
|
fImp = TRACKED_NEW BoneMapImp; |
|
} |
|
|
|
plAvBoneMap::~plAvBoneMap() |
|
{ |
|
delete fImp; |
|
} |
|
|
|
const plSceneObject * plAvBoneMap::FindBone(UInt32 boneID) |
|
{ |
|
BoneMapImp::id2SceneObjectMap::iterator i = fImp->fMap.find(boneID); |
|
const plSceneObject *result = nil; |
|
|
|
if(i != fImp->fMap.end()) |
|
{ |
|
result = (*i).second; |
|
} |
|
return result; |
|
} |
|
|
|
void plAvBoneMap::AddBoneMapping(UInt32 boneID, const plSceneObject *SO) |
|
{ |
|
(fImp->fMap)[boneID] = SO; |
|
} |
|
|
|
void plArmatureMod::DebugDumpMoveKeys(int &x, int &y, int lineHeight, char *strBuf, plDebugText &debugTxt) |
|
{ |
|
char buff[256]; |
|
sprintf(buff, "Mouse Input Map: %s", plAvatarInputInterface::GetInstance()->GetInputMapName()); |
|
debugTxt.DrawString(x, y, buff); |
|
y += lineHeight; |
|
|
|
sprintf(buff, "Turn strength: %.2f (key: %.2f, analog: %.2f)", GetTurnStrength(), GetKeyTurnStrength(), GetAnalogTurnStrength()); |
|
debugTxt.DrawString(x, y, buff); |
|
y += lineHeight; |
|
|
|
GetMoveKeyString(buff); |
|
debugTxt.DrawString(x, y, buff); |
|
y += lineHeight; |
|
} |
|
|
|
void plArmatureMod::GetMoveKeyString(char *buff) |
|
{ |
|
sprintf(buff, "Move keys: "); |
|
|
|
if(FastKeyDown()) |
|
strcat(buff, "FAST "); |
|
if(StrafeKeyDown()) |
|
strcat(buff, "STRAFE "); |
|
if(ForwardKeyDown()) |
|
strcat(buff, "FORWARD "); |
|
if(BackwardKeyDown()) |
|
strcat(buff, "BACKWARD "); |
|
if(TurnLeftKeyDown()) |
|
strcat(buff, "TURNLEFT "); |
|
if(TurnRightKeyDown()) |
|
strcat(buff, "TURNRIGHT "); |
|
if(StrafeLeftKeyDown()) |
|
strcat(buff, "STRAFELEFT "); |
|
if(StrafeRightKeyDown()) |
|
strcat(buff, "STRAFERIGHT "); |
|
if(JumpKeyDown()) |
|
strcat(buff, "JUMP "); |
|
}
|
|
|