diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index 74e18ead..0563cfe8 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -84,6 +84,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plListenerMsg.h" #include "plMessage/plAgeLoadedMsg.h" #include "plMessage/plParticleUpdateMsg.h" +#include "plMessage/plLoadClothingMsg.h" #include "plParticleSystem/plParticleSystem.h" #include "plParticleSystem/plParticleSDLMod.h" @@ -1293,6 +1294,8 @@ bool plArmatureMod::MsgReceive(plMessage* msg) } } + // We also want to use the trigger msg when loading an avatar + MsgReceive(avLoadMsg->GetTriggerMsg()); return true; } @@ -1334,6 +1337,15 @@ bool plArmatureMod::MsgReceive(plMessage* msg) } } + plLoadClothingMsg *clothingMsg = plLoadClothingMsg::ConvertNoRef(msg); + if (clothingMsg) + { + // We got a clothing file and are supposed to load our avatar from it. + // Let's tell our outfit to do so! + fClothingOutfit->SetClothingFile(clothingMsg->GetClothingFile()); + return true; + } + plLinkEffectBCMsg *linkBCMsg = plLinkEffectBCMsg::ConvertNoRef(msg); if (linkBCMsg) { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp index 20fc9cc7..a2c0ed97 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp @@ -193,8 +193,10 @@ void plAvBrainHuman::Activate(plArmatureModBase *avMod) if (fAvMod->GetClothingOutfit() && fAvMod->GetClothingOutfit()->fGroup != plClothingMgr::kClothingBaseNoOptions) { - if (fAvMod->IsLocalAvatar()) - fAvMod->GetClothingOutfit()->ReadFromVault(); + if (fAvMod->IsLocalAvatar()) + { + fAvMod->GetClothingOutfit()->ReadClothing(); + } else { fAvMod->GetClothingOutfit()->WearDefaultClothing(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp index e72adad5..687acd30 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp @@ -430,7 +430,8 @@ bool plClothingBase::MsgReceive(plMessage* msg) ///////////////////////////////////////////////////////////////////////////// plClothingOutfit::plClothingOutfit() : - fTargetLayer(nil), fBase(nil), fGroup(0), fAvatar(nil), fSynchClients(false), fMaterial(nil), fVaultSaveEnabled(true), fMorphsInitDone(false) + fTargetLayer(nullptr), fBase(nullptr), fGroup(0), fAvatar(nullptr), fSynchClients(false), fMaterial(nullptr), + fVaultSaveEnabled(true), fMorphsInitDone(false) { fSkinTint.Set(1.f, 0.84, 0.71, 1.f); fItems.Reset(); @@ -802,15 +803,24 @@ void plClothingOutfit::IHandleMorphSDR(plStateDataRecord *sdr) morph->SetCurrentStateFrom(sdr); } -void plClothingOutfit::ReadFromVault() +bool plClothingOutfit::ReadClothing() +{ + // Have we set a clothing file? If that's the case, load from there. + if (fClothingFile.IsValid()) + return IReadFromFile(fClothingFile); + else + return IReadFromVault(); +} + +bool plClothingOutfit::IReadFromVault() { SetupMorphSDL(); WearDefaultClothing(); - RelVaultNode * rvn; - if (nil == (rvn = VaultGetAvatarOutfitFolderIncRef())) - return; + RelVaultNode * rvn = VaultGetAvatarOutfitFolderIncRef(); + if (!rvn) + return false; ARRAY(RelVaultNode*) nodes; rvn->GetChildNodesIncRef(plVault::kNodeType_SDL, 1, &nodes); @@ -844,6 +854,7 @@ void plClothingOutfit::ReadFromVault() ForceUpdate(true); rvn->DecRef(); + return true; } void plClothingOutfit::SaveCustomizations(bool retry /* = true */) @@ -1135,7 +1146,7 @@ void plClothingOutfit::WearMaintainerOutfit() void plClothingOutfit::RemoveMaintainerOutfit() { - ReadFromVault(); + ReadClothing(); fVaultSaveEnabled = true; } @@ -1482,6 +1493,91 @@ void plClothingOutfit::SetupMorphSDL() } } +bool plClothingOutfit::WriteToFile(const plFileName &filename) +{ + if (!filename.IsValid()) + return false; + + RelVaultNode* rvn = VaultGetAvatarOutfitFolderIncRef(); + if (!rvn) + return false; + + hsUNIXStream S; + if (!S.Open(filename, "wb")) { + rvn->DecRef(); + return false; + } + + S.WriteByte(fGroup); + + ARRAY(RelVaultNode*) nodes; + rvn->GetChildNodesIncRef(plVault::kNodeType_SDL, 1, &nodes); + S.WriteLE32(nodes.Count()); + for (size_t i = 0; i < nodes.Count(); i++) { + VaultSDLNode sdl(nodes[i]); + S.WriteLE32(sdl.GetSDLDataLength()); + if (sdl.GetSDLDataLength()) + S.Write(sdl.GetSDLDataLength(), sdl.GetSDLData()); + nodes[i]->DecRef(); + } + rvn->DecRef(); + + S.Close(); + return true; +} + +bool plClothingOutfit::IReadFromFile(const plFileName &filename) +{ + if (!filename.IsValid()) + return false; + + hsUNIXStream S; + if (!S.Open(filename)) + return false; + + bool isLocalAvatar = plAvatarMgr::GetInstance()->GetLocalAvatar()->GetClothingOutfit() == this; + + uint8_t gender = S.ReadByte(); + if (gender != fGroup) { + if (isLocalAvatar) { + if (gender == plClothingMgr::kClothingBaseMale) + plClothingMgr::ChangeAvatar("Male", filename); + else if (gender == plClothingMgr::kClothingBaseFemale) + plClothingMgr::ChangeAvatar("Female", filename); + } + S.Close(); + return true; + } + + StripAccessories(); + + uint32_t nodeCount = S.ReadLE32(); + for (size_t i = 0; i < nodeCount; i++) { + uint32_t dataLen = S.ReadLE32(); + if (dataLen) { + plString sdlRecName; + int sdlRecVersion; + plStateDataRecord::ReadStreamHeader(&S, &sdlRecName, &sdlRecVersion); + plStateDescriptor* desc = plSDLMgr::GetInstance()->FindDescriptor(sdlRecName, sdlRecVersion); + if (desc) { + plStateDataRecord sdlDataRec(desc); + if (sdlDataRec.Read(&S, 0)) { + if (sdlRecName == kSDLMorphSequence) + IHandleMorphSDR(&sdlDataRec); + else + plClothingSDLModifier::HandleSingleSDR(&sdlDataRec, this); + } + } + } + } + + S.Close(); + fSynchClients = true; + ForceUpdate(true); + SaveCustomizations(); // Sync with the vault + return true; +} + ///////////////////////////////////////////////////////////////////////////// diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h index 92cd096a..d99595a6 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h @@ -215,25 +215,49 @@ public: void IInstanceSharedMeshes(plClothingItem *item); void IRemoveSharedMeshes(plClothingItem *item); - void ReadFromVault(); + /** This will load the avatar clothing. If a clothing file is set, + * we will load from the file, otherwise from the vault. + */ + bool ReadClothing(); + void WriteToVault(); void WriteToVault(const ARRAY(plStateDataRecord*) & SDRs); + /** Write the avatar clothing to a file */ + bool WriteToFile(const plFileName &filename); + void SetupMorphSDL(); // XXX Don't use this. Temp function for a temp HACK console command. void DirtyTileset(int tileset); + /** Instruct this plClothingOutfit to read clothing from the given file */ + void SetClothingFile(const plFileName &file) { fClothingFile = file; } + + /** Returns the clothing file of this outfit. If there is none, an empty string + * will be returned. + */ + plFileName GetClothingFile() const { return fClothingFile; } + protected: hsBitVector fDirtyItems; bool fVaultSaveEnabled; bool fMorphsInitDone; + plFileName fClothingFile; void IAddItem(plClothingItem *item); void IRemoveItem(plClothingItem *item); bool ITintItem(plClothingItem *item, hsColorRGBA color, uint8_t layer); bool IMorphItem(plClothingItem *item, uint8_t layer, uint8_t delta, float weight); void IHandleMorphSDR(plStateDataRecord *sdr); + + bool IReadFromVault(); + + /** Read the avatar clothing from a file. + * A local avatar will change the clothing group to the one in the file. + * A local avatar will be invisible if the file does not exist. (used in the Startup age) + */ + bool IReadFromFile(const plFileName &filename); void IUpdate();