diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index 90fc261c..476cd0b0 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" @@ -1309,6 +1310,9 @@ hsBool plArmatureMod::MsgReceive(plMessage* msg) } } + // We also want to use the trigger msg when loading an avatar + MsgReceive(avLoadMsg->GetTriggerMsg()); + // copy the user string over const char* userStr = avLoadMsg->GetUserStr(); if (userStr) @@ -1356,6 +1360,15 @@ hsBool 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 6ba9da21..3eaaa22a 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp @@ -192,8 +192,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()->ReadFromFile(); + } else { fAvMod->GetClothingOutfit()->WearDefaultClothing(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp index 9d66b8f1..887ac9bc 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp @@ -431,7 +431,8 @@ hsBool 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::ReadFromFile() +{ + // 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); @@ -845,6 +855,7 @@ void plClothingOutfit::ReadFromVault() ForceUpdate(true); rvn->DecRef(); + return true; } void plClothingOutfit::SaveCustomizations(hsBool retry /* = true */) @@ -1146,7 +1157,7 @@ void plClothingOutfit::WearMaintainerOutfit() void plClothingOutfit::RemoveMaintainerOutfit() { - ReadFromVault(); + ReadFromFile(); fVaultSaveEnabled = true; } @@ -1493,6 +1504,90 @@ 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 42a167ac..31db6de4 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h @@ -215,26 +215,48 @@ 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 ReadFromFile(); + 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; hsBool fVaultSaveEnabled; bool fMorphsInitDone; + plFileName fClothingFile; void IAddItem(plClothingItem *item); void IRemoveItem(plClothingItem *item); hsBool ITintItem(plClothingItem *item, hsColorRGBA color, UInt8 layer); hsBool IMorphItem(plClothingItem *item, UInt8 layer, UInt8 delta, hsScalar 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(); };