// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
/////////////
// INCLUDE //
/////////////
#include "stdpch.h" // First include for pre-compiled headers.
// Georges
#include "nel/georges/u_form_elm.h"
// Application
#include "character_sheet.h"
// Game Share
#include "game_share/georges_helper.h"
///////////
// USING //
///////////
using namespace std;
using namespace NLGEORGES;
using namespace NLMISC;
// ***************************************************************************
// Easy macro to translate value from georges
#define TRANSLATE_VAL( _Var_, _key_ ) \
if(!item.getValueByName(_Var_, _key_)) \
debug( string("key '") + string(_key_) + string("' not found.") );
/////////////
// METHODS //
/////////////
//-----------------------------------------------
// CCharacterSheet :
// Constructor.
//-----------------------------------------------
CCharacterSheet::CCharacterSheet()
{
CharacterScalePos = 1;
Scale = 1.f;
SoundFamily = 0;
SoundVariation = 0;
Type = CEntitySheet::FAUNA;
Gender = 0;
Race = EGSPD::CPeople::EndPeople;
IdSkelFilename = 0;
IdAnimSetBaseName = 0;
IdAutomaton = 0;
IdLodCharacterName = 0;
LodCharacterDistance = 0.0f;
IdFame = 0;
DisplayOSD = true;
DisplayInRadar = true;
DisplayOSDName = true;
DisplayOSDBars = true;
DisplayOSDForceOver = false;
Traversable = true;
ClipRadius= 0.f;
ClipHeight= 0.f;
R2Npc = false;
Selectable = false;
Talkable = false;
Attackable = false;
Givable = false;
Mountable = false;
Turn = false;
SelectableBySpace = false;
HLState = LHSTATE::NONE;
HairColor = 0;
Skin = 0;
EyesColor = 0;
DistToFront = 0.0f;
DistToBack = 0.0f;
DistToSide = 0.0f;
ColRadius = 0.0f;
ColHeight = 0.0f;
ColLength = 0.0f;
ColWidth = 0.0f;
MaxSpeed = 0.0f;
NamePosZLow = 0.0f;
NamePosZNormal = 0.0f;
NamePosZHigh = 0.0f;
IdStaticFX = 0;
SpellCastingPrefix = 0;
RegionForce = 0;
ForceLevel = 0;
Level = 0;
}// CCharacterSheet //
//-----------------------------------------------
// readEquipment :
// Read an equipment slot.
//-----------------------------------------------
void CCharacterSheet::readEquipment(const NLGEORGES::UFormElm &form, const string &key, CEquipment &slot)
{
// Get the item (or shape) name.
string itemName;
if(!form.getValueByName(itemName, string(key + ".Item").c_str() ))
debug(NLMISC::toString("Key '%s.Item' not found.", key.c_str()));
slot.IdItem = ClientSheetsStrings.add(NLMISC::toLower(itemName));
// Get the texture.
if(!form.getValueByName(slot.Texture, string(key + ".Texture").c_str() ))
debug(NLMISC::toString("Key '%s.Texture' not found.", key.c_str()));
// Get the color.
if(!form.getValueByName(slot.Color, string(key + ".Color").c_str() ))
debug(NLMISC::toString("Key '%s.Color' not found.", key.c_str()));
// Get the Bind point.
string bindPointName;
if(!form.getValueByName(bindPointName, string(key + ".Bind Point").c_str() ))
debug(NLMISC::toString("Key '%s.Bind Point' not found.", key.c_str()));
slot.IdBindPoint = ClientSheetsStrings.add(bindPointName);
}// readEquipment //
//-----------------------------------------------
// build :
// Build the sheet from an external script.
//-----------------------------------------------
void CCharacterSheet::build(const NLGEORGES::UFormElm &item)
{
// First Name
// string FirstName;
// if(!item.getValueByName(FirstName, "Basics.First Name"))
// debug("Key 'Basics.First Name' not found.");
// IdFirstName = ClientSheetsStrings.add(FirstName);
// // Character Name
// string LastName;
// if(!item.getValueByName(LastName, "Basics.CharacterName"))
// debug("Key 'Basics.CharacterName' not found.");
// IdLastName = ClientSheetsStrings.add(LastName);
// Fame Name
string FameName;
if(!item.getValueByName(FameName, "Basics.Fame"))
debug("Key 'Basics.Fame' not found.");
IdFame = ClientSheetsStrings.add(FameName);
// Character Race
string raceStr;
if(!item.getValueByName(raceStr, "Basics.Race"))
debug("Key 'Basics.Race' not found.");
else if (!raceStr.empty())
{
Race = EGSPD::CPeople::fromString(raceStr);
if (EGSPD::CPeople::toString(Race) != raceStr)
{
debug(toString("In sheet '%s': invalid race '%s', race is set to unknow",
Id.toString().c_str(),
raceStr.c_str()));
}
}
else
Race = EGSPD::CPeople::Unknown;
// Player Gender.
if(!item.getValueByName(Gender, "Basics.Gender"))
debug("Key 'Gender' not found.");
// Load the skel.
string SkelFilename;
if(!item.getValueByName(SkelFilename, "3d data.Skel"))
debug("Key '3d data.Skel' not found.");
IdSkelFilename = ClientSheetsStrings.add(SkelFilename);
// BODY
readEquipment(item, "Basics.Equipment.Body", Body);
// LEGS
readEquipment(item, "Basics.Equipment.Legs", Legs);
// ARMS
readEquipment(item, "Basics.Equipment.Arms", Arms);
// HANDS
readEquipment(item, "Basics.Equipment.Hands", Hands);
// FEET
readEquipment(item, "Basics.Equipment.Feet", Feet);
// HEAD
readEquipment(item, "Basics.Equipment.Head", Head);
// FACE
readEquipment(item, "Basics.Equipment.Face", Face);
// IN RIGHT HAND
readEquipment(item, "Basics.Equipment.HandR", ObjectInRightHand);
// IN LEFT HAND
readEquipment(item, "Basics.Equipment.HandL", ObjectInLeftHand);
// Get the animation set Base Name.
string AnimSetBaseName;
if(item.getValueByName(AnimSetBaseName, "3d data.AnimSetBaseName"))
{
if(AnimSetBaseName.empty())
debug("AnimSetBaseName is Empty.");
else
AnimSetBaseName = NLMISC::toLower(AnimSetBaseName); // Force the CASE in UPPER to not be CASE SENSITIVE.
}
else
debug("Key '3d data.AnimSetBaseName' not found.");
IdAnimSetBaseName = ClientSheetsStrings.add(AnimSetBaseName);
// AUTOMATON TYPE
string Automaton;
if(item.getValueByName(Automaton, "3d data.Automaton"))
{
// Check there is an automaton
if(Automaton.empty())
debug("Automaton is Empty.");
// Lower Case
else
Automaton = NLMISC::toLower(Automaton);
}
// Key not Found
else
debug("Key '3d data.Automaton' not found.");
IdAutomaton = ClientSheetsStrings.add(Automaton);
// Display OSD
if(!item.getValueByName(DisplayOSD, "3d data.DisplayOSD"))
{
debug("Key '3d data.DisplayOSD' not found -> set 'true'.");
DisplayOSD = true;
}
// New Bot Object flags
TRANSLATE_VAL(DisplayInRadar, "3d data.DisplayInRadar");
TRANSLATE_VAL(DisplayOSDName,"3d data.DisplayName");
TRANSLATE_VAL(DisplayOSDBars,"3d data.DisplayBars");
TRANSLATE_VAL(DisplayOSDForceOver, "3d data.DisplayAlwaysNameOver");
TRANSLATE_VAL(Traversable, "Collision.NotTraversable");
// invert
Traversable= !Traversable;
// CREATURE PROPERTIES (Possible Actions)
// Is the character selectable ?
if(!item.getValueByName(Selectable, "Properties.Selectable"))
{
debug("Key 'Properties.Selectable' not found -> set 'false'.");
Selectable = false;
}
// Is the character Talkable ?
if(!item.getValueByName(Talkable, "Properties.Talkable"))
{
debug("Key 'Properties.Talkable' not found -> set 'false'.");
Talkable = false;
}
// Is the character Attackable ?
if(!item.getValueByName(Attackable, "Properties.Attackable"))
{
debug("Key 'Properties.Attackable' not found -> set 'false'.");
Attackable = false;
}
// Is the character Givable ?
if(!item.getValueByName(Givable, "Properties.Givable"))
{
debug("Key 'Properties.Givable' not found -> set 'false'.");
Givable = false;
}
// Is the character Mountable ?
if(!item.getValueByName(Mountable, "Properties.Mountable"))
{
debug("Key 'Properties.Mountable' not found -> set 'false'.");
Mountable = false;
}
// Is the character allowed to turn ?
if(!item.getValueByName(Turn, "Properties.Turn"))
{
debug("Key 'Properties.Turn' not found -> set 'true'.");
Turn = true;
}
// Is the character selectable by pressing space (default) ?
if(!item.getValueByName(SelectableBySpace, "Properties.SelectableBySpace"))
{
debug("Key 'Properties.SelectableBySpace' not found -> set 'true'.");
SelectableBySpace = true;
}
//Get the harvest/loot state
string harvestLootStr;
if( !item.getValueByName(harvestLootStr, "Properties.LootHarvestState") )
debug("Key 'roperties.LootHarvestState' not found.");
else
HLState = LHSTATE::stringToLHState(harvestLootStr);
// Get the Hair Color.
if(!item.getValueByName(HairColor, "3d data.HairColor"))
debug("Key '3d data.HairColor' not found.");
// Get the Skin Texture.
if(!item.getValueByName(Skin, "3d data.Skin"))
debug("Key '3d data.Skin' not found.");
// Get the Eyes Color.
if(!item.getValueByName(EyesColor, "3d data.EyesColor"))
debug("Key '3d data.EyesColor' not found.");
// Load Lod character name
string LodCharacterName;
if(!item.getValueByName(LodCharacterName, "3d data.LodCharacterName"))
debug("Key '3d data.LodCharacterName' not found.");
IdLodCharacterName = ClientSheetsStrings.add(LodCharacterName);
// Load Lod character apparition distance
if(!item.getValueByName(LodCharacterDistance, "3d data.LodCharacterDistance"))
debug("Key '3d data.LodCharacterDistance' not found.");
// value to scale the "pos" channel of the animation of the player.
if(!item.getValueByName(CharacterScalePos, "3d data.CharacterScalePos"))
debug("Key '3d data.CharacterScalePos' not found.");
// value to scale the "pos" channel of the animation of the player.
if(!item.getValueByName(Scale, "3d data.Scale"))
debug("Key '3d data.Scale' not found.");
else
{
if(Scale <= 0.0f)
{
nlwarning("CCharacterSheet:build: Scale(%f) <= 0.0 so fix scale to 1.0", Scale);
Scale = 1.0f;
}
}
// Load name positions on Z axis
if(!item.getValueByName(NamePosZLow, "3d data.NamePosZLow"))
{
NamePosZLow = 0.f;
debug("Key '3d data.NamePosZLow' not found.");
}
if(!item.getValueByName(NamePosZNormal, "3d data.NamePosZNormal"))
{
NamePosZNormal = 0.f;
debug("Key '3d data.NamePosZNormal' not found.");
}
if(!item.getValueByName(NamePosZHigh, "3d data.NamePosZHigh"))
{
NamePosZHigh = 0.f;
debug("Key '3d data.NamePosZHigh' not found.");
}
// value to change sound familly
if(!item.getValueByName(SoundFamily, "3d data.SoundFamily"))
debug("Key '3d data.SoundFamily' not found.");
// value to change sound variation
if(!item.getValueByName(SoundVariation, "3d data.SoundVariation"))
debug("Key '3d data.SoundVariation' not found.");
// Get the dist fromm Bip to Mid
float tmpBip01ToMid;
if(!item.getValueByName(tmpBip01ToMid, "Collision.Dist Bip01 to mid"))
{
tmpBip01ToMid = 0.f;
debug("Key 'Collision.Dist Bip01 to mid' not found.");
}
// Get the distance from the bip01 to the front.
if(!item.getValueByName(DistToFront, "Collision.Dist Bip01 to front"))
{
DistToFront = 1.f;
debug("Key 'Collision.Dist Bip01 to front' not found.");
}
// Get the distance from the bip01 to the front.
if(!item.getValueByName(DistToBack, "Collision.Dist Bip01 to back"))
{
DistToBack = 1.f;
debug("Key 'Collision.Dist Bip01 to back' not found.");
}
// Get the creature Width.
if(!item.getValueByName(ColWidth, "Collision.Width"))
{
ColWidth = 1.f;
debug("Key 'Collision.Width' not found.");
}
DistToSide = ColWidth;
DistToFront = DistToFront-tmpBip01ToMid;
DistToBack = tmpBip01ToMid-DistToBack;
DistToSide = DistToSide/2.f;
// Get the creature collision Radius.
if(!item.getValueByName(ColRadius, "Collision.CollisionRadius"))
{
ColRadius = 0.5f;
debug("Key 'Collision.CollisionRadius' not found.");
}
// Get the creature collision Height.
if(!item.getValueByName(ColHeight, "Collision.Height"))
{
ColHeight = 2.f;
debug("Key 'Collision.Height' not found.");
}
// Get the creature collision Length.
if(!item.getValueByName(ColLength, "Collision.Length"))
{
ColLength = 1.f;
debug("Key 'Collision.Length' not found.");
}
// CLIP
if(!item.getValueByName(ClipRadius, "Collision.ClipRadius"))
{
ClipRadius = 0.f;
debug("Key 'Collision.ClipRadius' not found.");
}
if(!item.getValueByName(ClipHeight, "Collision.ClipHeight"))
{
ClipHeight = 0.f;
debug("Key 'Collision.ClipHeight' not found.");
}
// SPEED //
// Get the creature Max Speed (Run).
if(!item.getValueByName(MaxSpeed, "Basics.MovementSpeeds.RunSpeed"))
{
MaxSpeed = 10.f;
debug("Key 'Basics.MovementSpeeds.RunSpeed' not found.");
}
const UFormElm *elm = NULL;
// Get all alternative Clothes.
static const char alternativeClothesKey[] = "Basics.Alternative Clothes";
if(item.getNodeByName(&elm, alternativeClothesKey) && elm)
{
// Check array.
if(elm->isArray())
{
// Get Array Size
uint altClothesArraySize;
if(elm->getArraySize(altClothesArraySize))
{
// Get values.
string altClothes;
for(uint i=0; igetArrayValue(altClothes, i))
{
if(!altClothes.empty())
{
TSStringId IdAltClothes = ClientSheetsStrings.add(altClothes);
IdAlternativeClothes.push_back(IdAltClothes);
}
else
debug(toString("'%s' field empty for the index '%d'.", alternativeClothesKey, i));
}
else
debug(toString("'%s' cannot get the array value for the index '%d'.", alternativeClothesKey, i));
}
}
else
debug(toString("'%s' cannot get the array size.", alternativeClothesKey));
}
else
debug(toString("'%s' is not an array.", alternativeClothesKey));
}
else
debug(toString("'%s' key not found.", alternativeClothesKey));
// Hair item list
static const char hairItemList[] = "3d data.HairItem";
if(item.getNodeByName(&elm, hairItemList) && elm)
{
// Check array.
if(elm->isArray())
{
// Get Array Size
uint hairItemArraySize;
if(elm->getArraySize(hairItemArraySize))
{
if(hairItemArraySize > 0)
{
// Adjust the array size.
HairItemList.resize(hairItemArraySize);
// Get values.
for(uint i=0; igetArrayNodeName(arrayNodeName, i))
readEquipment(item, string(hairItemList)+"["+toString(i)+"]", HairItemList[i]);
}
}
}
else
debug(toString("'%s' cannot get the array size.", hairItemList));
}
else
debug(toString("'%s' is not an array.", hairItemList));
}
else
debug(toString("'%s' key not found.", hairItemList));
// ground fxs
static const char groundFXList[] = "3d data.GroundFX";
if(item.getNodeByName(&elm, groundFXList) && elm)
{
// Check array.
if(elm->isArray())
{
// Get Array Size
uint groundFXArraySize;
if(elm->getArraySize(groundFXArraySize))
{
if(groundFXArraySize > 0)
{
// Adjust the array size.
GroundFX.reserve(groundFXArraySize);
// Get values.
for(uint i=0; i< groundFXArraySize; ++i)
{
const UFormElm *node;
if (elm->getArrayNode(&node, i) && node != NULL)
{
CGroundFXSheet gfs;
if (!gfs.build(*node))
{
nlwarning("Error while building node %d", (int) i);
}
else
{
uint k;
for(k = 0; k < GroundFX.size(); ++k)
{
if (GroundFX[k].GroundID == gfs.GroundID)
{
debug("Duplicated material");
GroundFX[k] = gfs;
break;
}
}
if (k == GroundFX.size())
{
GroundFX.push_back(gfs);
}
}
}
}
}
}
else
debug(toString("'%s' cannot get the array size.", groundFXList));
}
else
debug(toString("'%s' is not an array.", groundFXList));
}
else
debug(toString("'%s' key not found.", groundFXList));
// sort fxs by ground type
std::sort(GroundFX.begin(), GroundFX.end());
// Load Ground FX
string staticFx;
if(!item.getValueByName(staticFx, "3d data.FX"))
debug("Key '3d data.FX' not found.");
IdStaticFX = ClientSheetsStrings.add(staticFx);
BodyToBone.build(item, "Localisation.");
// Attack lists
for(uint k = 0; k < NumAttackLists; ++k)
{
std::string attackListName;
if(item.getValueByName(attackListName, toString("attack_list%d", (int) k).c_str()) && !attackListName.empty())
{
AttackLists.push_back(ClientSheetsStrings.add(attackListName));
}
}
if(!item.getValueByName(RegionForce, "Basics.RegionForce"))
{
RegionForce = 0;
debug("Key 'Basics.RegionForce' not found.");
}
if(!item.getValueByName(ForceLevel, "Basics.ForceLevel"))
{
ForceLevel = 0;
debug("Key 'Basics.Regio Force' not found.");
}
if(!item.getValueByName(Level, "Basics.XPLevel"))
{
Level = 0;
debug("Key 'Basics.XPLevel' not found.");
}
// offset for projectiles
const UFormElm *pElt;
nlverify (item.getNodeByName (&pElt, "3d data.ProjectileCastRay"));
uint arraySize;
if (pElt != NULL)
{
nlverify (pElt->getArraySize (arraySize));
ProjectileCastRay.reserve(arraySize);
for (uint32 i = 0; i < arraySize; ++i)
{
const UFormElm *pEltOfList;
if (pElt->getArrayNode (&pEltOfList, i) && pEltOfList)
{
const UFormElm *pEltPos;
const UFormElm *pEltOrigin;
if (pEltOfList->getNodeByName(&pEltPos, "Pos") &&
pEltOfList->getNodeByName(&pEltOrigin, "Origin"))
{
CCharacterSheet::CCastRay cr;
if (pEltPos->getValueByName(cr.Pos.x, "X") &&
pEltPos->getValueByName(cr.Pos.y, "Y") &&
pEltPos->getValueByName(cr.Pos.z, "Z") &&
pEltOrigin->getValueByName(cr.Origin.x, "X") &&
pEltOrigin->getValueByName(cr.Origin.y, "Y") &&
pEltOrigin->getValueByName(cr.Origin.z, "Z")
)
{
ProjectileCastRay.push_back(cr);
}
}
}
}
}
if(!item.getValueByName(R2Npc, "r2_npc"))
{
R2Npc = false;
debug("Key 'R2Npc' not found.");
}
}// build //
//-----------------------------------------------
// serial :
// Serialize character sheet into binary data file.
//-----------------------------------------------
void CCharacterSheet::serial(class NLMISC::IStream &f) throw(NLMISC::EStream)
{
// Serialize class components.
// ClientSheetsStrings.serial(f, IdFirstName);
// ClientSheetsStrings.serial(f, IdLastName);
f.serial(Gender);
f.serialEnum(Race);
ClientSheetsStrings.serial(f, IdSkelFilename);
ClientSheetsStrings.serial(f, IdAnimSetBaseName);
ClientSheetsStrings.serial(f, IdAutomaton);
f.serial(Scale);
f.serial(SoundFamily);
f.serial(SoundVariation);
ClientSheetsStrings.serial(f, IdLodCharacterName);
f.serial(LodCharacterDistance);
f.serial(Selectable);
f.serial(Talkable);
f.serial(Attackable);
f.serial(Givable);
f.serial(Mountable);
f.serial(Turn);
f.serial(SelectableBySpace);
f.serialEnum(HLState);
f.serial(CharacterScalePos);
f.serial(NamePosZLow);
f.serial(NamePosZNormal);
f.serial(NamePosZHigh);
ClientSheetsStrings.serial(f, IdFame);
f.serial(Body);
f.serial(Legs);
f.serial(Arms);
f.serial(Hands);
f.serial(Feet);
f.serial(Head);
f.serial(Face);
f.serial(ObjectInRightHand);
f.serial(ObjectInLeftHand);
f.serial(HairColor);
f.serial(Skin);
f.serial(EyesColor);
f.serial(DistToFront);
f.serial(DistToBack);
f.serial(DistToSide);
// Collisions
f.serial(ColRadius);
f.serial(ColHeight);
f.serial(ColLength);
f.serial(ColWidth);
f.serial(MaxSpeed);
// Clip
f.serial(ClipRadius);
f.serial(ClipHeight);
// Alternative Look
ClientSheetsStrings.serial(f, IdAlternativeClothes);
// Hair Item List
f.serialCont(HairItemList);
// Ground fxs
f.serialCont(GroundFX);
// Display OSD
f.serial(DisplayOSD);
// static FX
ClientSheetsStrings.serial(f, IdStaticFX);
// body to bone
f.serial(BodyToBone);
// attack list
uint32 size = (uint32)AttackLists.size();
f.serial(size);
AttackLists.resize(size);
//
for(uint k = 0; k < size; ++k)
{
ClientSheetsStrings.serial(f, AttackLists[k]);
}
// bot object flags
f.serial(DisplayInRadar);
f.serial(DisplayOSDName);
f.serial(DisplayOSDBars);
f.serial(DisplayOSDForceOver);
f.serial(Traversable);
f.serial(RegionForce);
f.serial(ForceLevel);
f.serial(Level);
f.serialCont(ProjectileCastRay);
f.serial(R2Npc);
}// serial //
// ***************************************************************************
void CCharacterSheet::getWholeEquipmentList(std::vector &equipList) const
{
equipList.clear();
equipList.push_back(&Body);
equipList.push_back(&Legs);
equipList.push_back(&Arms);
equipList.push_back(&Hands);
equipList.push_back(&Feet);
equipList.push_back(&Head);
equipList.push_back(&Face);
equipList.push_back(&ObjectInRightHand);
equipList.push_back(&ObjectInLeftHand);
for(uint i=0;i