/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "plClothingSDLModifier.h"
#include "plAvatarClothing.h"
#include "plClothingLayout.h"
#include "plArmatureMod.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnMessage/plSDLModifierMsg.h"
#include "plSDL/plSDL.h"
#include "pnKeyedObject/plKeyImp.h"
// static vars
char plClothingSDLModifier::kStrItem[]="item";
char plClothingSDLModifier::kStrTint[]="tint";
char plClothingSDLModifier::kStrTint2[]="tint2";
char plClothingSDLModifier::kStrWardrobe[]="wardrobe";
char plClothingSDLModifier::kStrSkinTint[]="skinTint";
char plClothingSDLModifier::kStrFaceBlends[]="faceBlends";
char plClothingSDLModifier::kStrAppearance[]="appearance";
char plClothingSDLModifier::kStrClothingDescName[]="clothingItem";
char plClothingSDLModifier::kStrAppearanceDescName[]="appearanceOptions";
char plClothingSDLModifier::kStrLinkInAnim[] = "linkInAnim";
//
// init latest data
//
plClothingSDLModifier::plClothingSDLModifier() :
fClothingOutfit(nil)
{
}
//
// find armature mod and cache it's clothingOutfit
//
plClothingOutfit* plClothingSDLModifier::GetClothingOutfit()
{
if (fClothingOutfit)
return fClothingOutfit;
int i;
plSceneObject* target = GetTarget();
hsAssert(target, "plClothingSDLModifier: nil target");
for (i=0;iGetNumModifiers();i++)
{
const plArmatureMod* am = plArmatureMod::ConvertNoRef(target->GetModifier(i));
if (am)
{
fClothingOutfit=am->GetClothingOutfit();
break;
}
}
return fClothingOutfit;
}
void plClothingSDLModifier::PutCurrentStateIn(plStateDataRecord* dstState)
{
IPutCurrentStateIn(dstState);
}
//
// get current state from clothingOutfit and put it in dstState
//
void plClothingSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState)
{
plSceneObject* sobj=GetTarget();
hsAssert(sobj, "plClothingSDLModifier, nil target");
plClothingOutfit* clothing = GetClothingOutfit();
hsAssert(clothing, "nil clothingOutfit");
hsTArray SDRs;
hsTArray items=clothing->GetItemList();
hsTArray options=clothing->GetOptionList();
plSDStateVariable* clothesStateDesc = dstState->FindSDVar(kStrWardrobe); // find clothes list
int itemCount=items.GetCount();
int optionsCount = options.GetCount();
hsAssert(optionsCount==itemCount, "number of items should match number of options according to clothing.sdl");
if (clothesStateDesc->GetCount() != itemCount)
clothesStateDesc->Alloc(itemCount); // set appropriate list size
int lowerCount = (itemCount <= optionsCount ? itemCount : optionsCount);
int i;
for(i = 0; i < lowerCount; i++)
{
plClosetItem closetItem;
closetItem.fItem = items[i];
closetItem.fOptions = *options[i];
PutSingleItemIntoSDR(&closetItem, clothesStateDesc->GetStateDataRecord(i));
SDRs.Append(clothesStateDesc->GetStateDataRecord(i));
}
// skin tint
plSDStateVariable* appearanceStateDesc = dstState->FindSDVar(kStrAppearance); // for skin tint
UInt8 skinTint[3];
skinTint[0] = (UInt8)(clothing->fSkinTint.r * 255);
skinTint[1] = (UInt8)(clothing->fSkinTint.g * 255);
skinTint[2] = (UInt8)(clothing->fSkinTint.b * 255);
appearanceStateDesc->GetStateDataRecord(0)->FindVar(kStrSkinTint)->Set(skinTint);
plSimpleStateVariable* faceBlends = appearanceStateDesc->GetStateDataRecord(0)->FindVar(kStrFaceBlends);
int numBlends = plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst;
if (faceBlends->GetCount() != numBlends)
faceBlends->Alloc(numBlends);
for(i = 0; i < numBlends; i++)
faceBlends->Set((UInt8)(clothing->fSkinBlends[i] * 255), i);
SDRs.Append(appearanceStateDesc->GetStateDataRecord(0));
// This logically belongs in the avatar.sdl file, but clothing.sdl is already broadcast to
// other players when you join an age, and I don't want to broadcast all of avatar.sdl for
// just this one value.
plSimpleStateVariable *var = dstState->FindVar(kStrLinkInAnim);
if (var)
var->Set(clothing->fAvatar->fLinkInAnimKey);
}
void plClothingSDLModifier::PutSingleItemIntoSDR(plClosetItem *item, plStateDataRecord *sdr)
{
plKey key = item->fItem->GetKey();
sdr->FindVar(kStrItem)->Set(key);
//hsColorRGBA c = item->fOptions.fTint1;
//hsColorRGBA c2 = item->fOptions.fTint2;
UInt8 c[3];
UInt8 c2[3];
c[0] = (UInt8)(item->fOptions.fTint1.r * 255);
c[1] = (UInt8)(item->fOptions.fTint1.g * 255);
c[2] = (UInt8)(item->fOptions.fTint1.b * 255);
c2[0] = (UInt8)(item->fOptions.fTint2.r * 255);
c2[1] = (UInt8)(item->fOptions.fTint2.g * 255);
c2[2] = (UInt8)(item->fOptions.fTint2.b * 255);
sdr->FindVar(kStrTint)->Set(c);
sdr->FindVar(kStrTint2)->Set(c2);
}
//
// Apply the SDL state that is passed in to the current clothing state.
//
void plClothingSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState)
{
plSceneObject* sobj=GetTarget();
hsAssert(sobj, "plClothingSDLModifier, nil target");
plClothingOutfit* clothing = GetClothingOutfit();
if( clothing == nil )
{
hsAssert(clothing, "nil clothingOutfit");
return;
}
plSDStateVariable* clothesStateDesc = srcState->FindSDVar(kStrWardrobe); // find clothes list
int num=clothesStateDesc->GetCount(); // size of clothes list
bool update=true;
// We just remove the accessories. Any regular items will be replaced
// when we try to wear something else in the same category. (And in
// case the player lies, this guarantees they'll at least be wearing something.)
clothing->StripAccessories();
int i;
for(i=0;iGetStateDataRecord(i);
HandleSingleSDR(clothingItemState, clothing);
}
plSDStateVariable* appearanceStateDesc = srcState->FindSDVar(kStrAppearance); // for skin tint
plStateDataRecord* appearanceState = appearanceStateDesc->GetStateDataRecord(0);
HandleSingleSDR(appearanceState, clothing);
if (update)
clothing->ForceUpdate(true /* retry */);
plSimpleStateVariable *var;
var = srcState->FindVar(kStrLinkInAnim);
if (var)
var->Get(&clothing->fAvatar->fLinkInAnimKey);
}
void plClothingSDLModifier::HandleSingleSDR(const plStateDataRecord *sdr, plClothingOutfit *clothing /* = nil */, plClosetItem *closetItem /* = nil */)
{
if (sdr == nil)
return;
int i;
UInt8 tint[3];
hsScalar tintScalar[3];
if (!strcmp(sdr->GetDescriptor()->GetName(), kStrClothingDescName))
{
// get item from clothesItem
plSimpleStateVariable* itemVar = sdr->FindVar(kStrItem);
plClothingItem* clothingItem = nil; // clothing->GetItemList()[i];
if (itemVar->IsUsed())
{
plKey key;
itemVar->Get(&key);
clothingItem = plClothingItem::ConvertNoRef(key ? key->GetObjectPtr() : nil);
if (clothingItem)
{
if (clothing)
clothing->AddItem(clothingItem, false, false /*bcast */);
if (closetItem)
closetItem->fItem = clothingItem;
}
}
// tints
if (clothingItem)
{
// get item from clothesItem
plSimpleStateVariable* tintVar = sdr->FindVar(kStrTint);
if (tintVar->IsUsed())
{
tintVar->Get(tint);
tintScalar[0] = tint[0] / 255.f;
tintScalar[1] = tint[1] / 255.f;
tintScalar[2] = tint[2] / 255.f;
if (clothing)
clothing->TintItem(clothingItem, tintScalar[0], tintScalar[1], tintScalar[2],
false /*update*/, false /*broadcast*/, false /*netForce*/, true, plClothingElement::kLayerTint1);
if (closetItem)
closetItem->fOptions.fTint1.Set(tintScalar[0], tintScalar[1], tintScalar[2], 1.f);
}
tintVar=sdr->FindVar(kStrTint2);
if (tintVar->IsUsed())
{
tintVar->Get(tint);
tintScalar[0] = tint[0] / 255.f;
tintScalar[1] = tint[1] / 255.f;
tintScalar[2] = tint[2] / 255.f;
if (clothing)
clothing->TintItem(clothingItem, tintScalar[0], tintScalar[1], tintScalar[2],
false /*update*/, false /*broadcast*/, false /*netForce*/, true, plClothingElement::kLayerTint2);
if (closetItem)
closetItem->fOptions.fTint2.Set(tintScalar[0], tintScalar[1], tintScalar[2], 1.f);
}
}
}
else if (!strcmp(sdr->GetDescriptor()->GetName(), kStrAppearanceDescName))
{
// skin tints
plSimpleStateVariable* skinVar = sdr->FindVar(kStrSkinTint);
if (skinVar->IsUsed())
{
skinVar->Get(tint);
if (clothing)
clothing->TintSkin(tint[0] / 255.f, tint[1] / 255.f, tint[2] / 255.f, false /* update */, false /*broadcast*/);
}
plSimpleStateVariable* faceBlends = sdr->FindVar(kStrFaceBlends);
if (faceBlends->IsUsed())
{
int numBlends = plClothingElement::kLayerSkinLast - plClothingElement::kLayerSkinFirst;
for(i = 0; i < numBlends && i < faceBlends->GetCount(); i++)
{
UInt8 blend;
faceBlends->Get(&blend, i);
clothing->fSkinBlends[i] = (hsScalar)blend / 255;
}
}
}
}
// FINDARMATUREMOD
const plClothingSDLModifier *plClothingSDLModifier::FindClothingSDLModifier(const plSceneObject *obj)
{
int count = obj->GetNumModifiers();
for (int i = 0; i < count; i++)
{
const plModifier *mod = obj->GetModifier(i);
const plClothingSDLModifier *sdlMod = plClothingSDLModifier::ConvertNoRef(mod);
if(sdlMod)
return sdlMod;
}
return nil;
}