1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "stdpch.h" // First include for pre-compiled headers.
28 #include "nel/georges/u_form_elm.h"
30 #include "character_sheet.h"
32 #include "game_share/georges_helper.h"
39 using namespace NLGEORGES
;
40 using namespace NLMISC
;
43 // ***************************************************************************
44 // Easy macro to translate value from georges
45 #define TRANSLATE_VAL( _Var_, _key_ ) \
46 if(!item.getValueByName(_Var_, _key_)) \
47 debug( string("key '") + string(_key_) + string("' not found.") );
54 //-----------------------------------------------
57 //-----------------------------------------------
58 CCharacterSheet::CCharacterSheet()
60 CharacterScalePos
= 1;
64 Type
= CEntitySheet::FAUNA
;
66 Race
= EGSPD::CPeople::EndPeople
;
68 IdAnimSetBaseName
= 0;
70 IdLodCharacterName
= 0;
71 LodCharacterDistance
= 0.0f
;
74 DisplayInRadar
= true;
75 DisplayOSDName
= true;
76 DisplayOSDBars
= true;
77 DisplayOSDForceOver
= false;
89 SelectableBySpace
= false;
91 HLState
= LHSTATE::NONE
;
109 NamePosZNormal
= 0.0f
;
114 SpellCastingPrefix
= 0;
120 }// CCharacterSheet //
122 //-----------------------------------------------
124 // Read an equipment slot.
125 //-----------------------------------------------
126 void CCharacterSheet::readEquipment(const NLGEORGES::UFormElm
&form
, const string
&key
, CEquipment
&slot
)
128 // Get the item (or shape) name.
130 if(!form
.getValueByName(itemName
, string(key
+ ".Item").c_str() ))
131 debug(NLMISC::toString("Key '%s.Item' not found.", key
.c_str()));
132 slot
.IdItem
= ClientSheetsStrings
.add(NLMISC::toLowerAscii(itemName
));
135 if(!form
.getValueByName(slot
.Texture
, string(key
+ ".Texture").c_str() ))
136 debug(NLMISC::toString("Key '%s.Texture' not found.", key
.c_str()));
139 if(!form
.getValueByName(slot
.Color
, string(key
+ ".Color").c_str() ))
140 debug(NLMISC::toString("Key '%s.Color' not found.", key
.c_str()));
142 // Get the Bind point.
143 string bindPointName
;
144 if(!form
.getValueByName(bindPointName
, string(key
+ ".Bind Point").c_str() ))
145 debug(NLMISC::toString("Key '%s.Bind Point' not found.", key
.c_str()));
146 slot
.IdBindPoint
= ClientSheetsStrings
.add(bindPointName
);
149 //-----------------------------------------------
151 // Build the sheet from an external script.
152 //-----------------------------------------------
153 void CCharacterSheet::build(const NLGEORGES::UFormElm
&item
)
157 // if(!item.getValueByName(FirstName, "Basics.First Name"))
158 // debug("Key 'Basics.First Name' not found.");
159 // IdFirstName = ClientSheetsStrings.add(FirstName);
162 // if(!item.getValueByName(LastName, "Basics.CharacterName"))
163 // debug("Key 'Basics.CharacterName' not found.");
164 // IdLastName = ClientSheetsStrings.add(LastName);
168 if(!item
.getValueByName(FameName
, "Basics.Fame"))
169 debug("Key 'Basics.Fame' not found.");
170 IdFame
= ClientSheetsStrings
.add(FameName
);
174 if(!item
.getValueByName(raceStr
, "Basics.Race"))
175 debug("Key 'Basics.Race' not found.");
176 else if (!raceStr
.empty())
178 Race
= EGSPD::CPeople::fromString(raceStr
);
180 if (EGSPD::CPeople::toString(Race
) != raceStr
)
182 debug(toString("In sheet '%s': invalid race '%s', race is set to unknow",
183 Id
.toString().c_str(),
188 Race
= EGSPD::CPeople::Unknown
;
191 if(!item
.getValueByName(Gender
, "Basics.Gender"))
192 debug("Key 'Gender' not found.");
196 if(!item
.getValueByName(SkelFilename
, "3d data.Skel"))
197 debug("Key '3d data.Skel' not found.");
198 IdSkelFilename
= ClientSheetsStrings
.add(SkelFilename
);
201 readEquipment(item
, "Basics.Equipment.Body", Body
);
203 readEquipment(item
, "Basics.Equipment.Legs", Legs
);
205 readEquipment(item
, "Basics.Equipment.Arms", Arms
);
207 readEquipment(item
, "Basics.Equipment.Hands", Hands
);
209 readEquipment(item
, "Basics.Equipment.Feet", Feet
);
211 readEquipment(item
, "Basics.Equipment.Head", Head
);
213 readEquipment(item
, "Basics.Equipment.Face", Face
);
215 readEquipment(item
, "Basics.Equipment.HandR", ObjectInRightHand
);
217 readEquipment(item
, "Basics.Equipment.HandL", ObjectInLeftHand
);
219 if (!ObjectInRightHand
.IdItem
)
222 item
.getValueByName(right
, "item_right");
224 ObjectInRightHand
.IdItem
= ClientSheetsStrings
.add(NLMISC::toLowerAscii(right
));
227 if (!ObjectInLeftHand
.IdItem
)
230 item
.getValueByName(left
, "item_left");
232 ObjectInLeftHand
.IdItem
= ClientSheetsStrings
.add(NLMISC::toLowerAscii(left
));
235 // Get the animation set Base Name.
236 string AnimSetBaseName
;
237 if(item
.getValueByName(AnimSetBaseName
, "3d data.AnimSetBaseName"))
239 if(AnimSetBaseName
.empty())
240 debug("AnimSetBaseName is Empty.");
242 AnimSetBaseName
= NLMISC::toLowerAscii(AnimSetBaseName
); // Force the CASE in UPPER to not be CASE SENSITIVE.
245 debug("Key '3d data.AnimSetBaseName' not found.");
246 IdAnimSetBaseName
= ClientSheetsStrings
.add(AnimSetBaseName
);
249 if(item
.getValueByName(Automaton
, "3d data.Automaton"))
251 // Check there is an automaton
252 if(Automaton
.empty())
253 debug("Automaton is Empty.");
256 Automaton
= NLMISC::toLowerAscii(Automaton
);
260 debug("Key '3d data.Automaton' not found.");
261 IdAutomaton
= ClientSheetsStrings
.add(Automaton
);
263 if(!item
.getValueByName(DisplayOSD
, "3d data.DisplayOSD"))
265 debug("Key '3d data.DisplayOSD' not found -> set 'true'.");
269 // New Bot Object flags
270 TRANSLATE_VAL(DisplayInRadar
, "3d data.DisplayInRadar");
271 TRANSLATE_VAL(DisplayOSDName
,"3d data.DisplayName");
272 TRANSLATE_VAL(DisplayOSDBars
,"3d data.DisplayBars");
273 TRANSLATE_VAL(DisplayOSDForceOver
, "3d data.DisplayAlwaysNameOver");
274 TRANSLATE_VAL(Traversable
, "Collision.NotTraversable");
276 Traversable
= !Traversable
;
278 // CREATURE PROPERTIES (Possible Actions)
279 // Is the character selectable ?
280 if(!item
.getValueByName(Selectable
, "Properties.Selectable"))
282 debug("Key 'Properties.Selectable' not found -> set 'false'.");
285 // Is the character Talkable ?
286 if(!item
.getValueByName(Talkable
, "Properties.Talkable"))
288 debug("Key 'Properties.Talkable' not found -> set 'false'.");
291 // Is the character Attackable ?
292 if(!item
.getValueByName(Attackable
, "Properties.Attackable"))
294 debug("Key 'Properties.Attackable' not found -> set 'false'.");
297 // Is the character Givable ?
298 if(!item
.getValueByName(Givable
, "Properties.Givable"))
300 debug("Key 'Properties.Givable' not found -> set 'false'.");
303 // Is the character Mountable ?
304 if(!item
.getValueByName(Mountable
, "Properties.Mountable"))
306 debug("Key 'Properties.Mountable' not found -> set 'false'.");
309 // Is the character allowed to turn ?
310 if(!item
.getValueByName(Turn
, "Properties.Turn"))
312 debug("Key 'Properties.Turn' not found -> set 'true'.");
315 // Is the character selectable by pressing space (default) ?
316 if(!item
.getValueByName(SelectableBySpace
, "Properties.SelectableBySpace"))
318 debug("Key 'Properties.SelectableBySpace' not found -> set 'true'.");
319 SelectableBySpace
= true;
321 //Get the harvest/loot state
322 string harvestLootStr
;
323 if( !item
.getValueByName(harvestLootStr
, "Properties.LootHarvestState") )
324 debug("Key 'roperties.LootHarvestState' not found.");
326 HLState
= LHSTATE::stringToLHState(harvestLootStr
);
330 // Get the Hair Color.
331 if(!item
.getValueByName(HairColor
, "3d data.HairColor"))
332 debug("Key '3d data.HairColor' not found.");
334 // Get the Skin Texture.
335 if(!item
.getValueByName(Skin
, "3d data.Skin"))
336 debug("Key '3d data.Skin' not found.");
338 // Get the Eyes Color.
339 if(!item
.getValueByName(EyesColor
, "3d data.EyesColor"))
340 debug("Key '3d data.EyesColor' not found.");
342 // Load Lod character name
343 string LodCharacterName
;
344 if(!item
.getValueByName(LodCharacterName
, "3d data.LodCharacterName"))
345 debug("Key '3d data.LodCharacterName' not found.");
346 IdLodCharacterName
= ClientSheetsStrings
.add(LodCharacterName
);
348 // Load Lod character apparition distance
349 if(!item
.getValueByName(LodCharacterDistance
, "3d data.LodCharacterDistance"))
350 debug("Key '3d data.LodCharacterDistance' not found.");
352 // value to scale the "pos" channel of the animation of the player.
353 if(!item
.getValueByName(CharacterScalePos
, "3d data.CharacterScalePos"))
354 debug("Key '3d data.CharacterScalePos' not found.");
356 // value to scale the "pos" channel of the animation of the player.
357 if(!item
.getValueByName(Scale
, "3d data.Scale"))
358 debug("Key '3d data.Scale' not found.");
363 nlwarning("CCharacterSheet:build: Scale(%f) <= 0.0 so fix scale to 1.0", Scale
);
368 // Load name positions on Z axis
369 if(!item
.getValueByName(NamePosZLow
, "3d data.NamePosZLow"))
372 debug("Key '3d data.NamePosZLow' not found.");
374 if(!item
.getValueByName(NamePosZNormal
, "3d data.NamePosZNormal"))
376 NamePosZNormal
= 0.f
;
377 debug("Key '3d data.NamePosZNormal' not found.");
379 if(!item
.getValueByName(NamePosZHigh
, "3d data.NamePosZHigh"))
382 debug("Key '3d data.NamePosZHigh' not found.");
385 // value to change sound familly
386 if(!item
.getValueByName(SoundFamily
, "3d data.SoundFamily"))
387 debug("Key '3d data.SoundFamily' not found.");
388 // value to change sound variation
389 if(!item
.getValueByName(SoundVariation
, "3d data.SoundVariation"))
390 debug("Key '3d data.SoundVariation' not found.");
393 // Get the dist fromm Bip to Mid
395 if(!item
.getValueByName(tmpBip01ToMid
, "Collision.Dist Bip01 to mid"))
398 debug("Key 'Collision.Dist Bip01 to mid' not found.");
400 // Get the distance from the bip01 to the front.
401 if(!item
.getValueByName(DistToFront
, "Collision.Dist Bip01 to front"))
404 debug("Key 'Collision.Dist Bip01 to front' not found.");
406 // Get the distance from the bip01 to the front.
407 if(!item
.getValueByName(DistToBack
, "Collision.Dist Bip01 to back"))
410 debug("Key 'Collision.Dist Bip01 to back' not found.");
412 // Get the creature Width.
413 if(!item
.getValueByName(ColWidth
, "Collision.Width"))
416 debug("Key 'Collision.Width' not found.");
418 DistToSide
= ColWidth
;
420 DistToFront
= DistToFront
-tmpBip01ToMid
;
421 DistToBack
= tmpBip01ToMid
-DistToBack
;
422 DistToSide
= DistToSide
/2.f
;
424 // Get the creature collision Radius.
425 if(!item
.getValueByName(ColRadius
, "Collision.CollisionRadius"))
428 debug("Key 'Collision.CollisionRadius' not found.");
430 // Get the creature collision Height.
431 if(!item
.getValueByName(ColHeight
, "Collision.Height"))
434 debug("Key 'Collision.Height' not found.");
436 // Get the creature collision Length.
437 if(!item
.getValueByName(ColLength
, "Collision.Length"))
440 debug("Key 'Collision.Length' not found.");
445 if(!item
.getValueByName(ClipRadius
, "Collision.ClipRadius"))
448 debug("Key 'Collision.ClipRadius' not found.");
450 if(!item
.getValueByName(ClipHeight
, "Collision.ClipHeight"))
453 debug("Key 'Collision.ClipHeight' not found.");
458 // Get the creature Max Speed (Run).
459 if(!item
.getValueByName(MaxSpeed
, "Basics.MovementSpeeds.RunSpeed"))
462 debug("Key 'Basics.MovementSpeeds.RunSpeed' not found.");
465 const UFormElm
*elm
= NULL
;
466 // Get all alternative Clothes.
467 static const char alternativeClothesKey
[] = "Basics.Alternative Clothes";
468 if(item
.getNodeByName(&elm
, alternativeClothesKey
) && elm
)
474 uint altClothesArraySize
;
475 if(elm
->getArraySize(altClothesArraySize
))
479 for(uint i
=0; i
<altClothesArraySize
; ++i
)
481 if(elm
->getArrayValue(altClothes
, i
))
483 if(!altClothes
.empty())
485 TSStringId IdAltClothes
= ClientSheetsStrings
.add(altClothes
);
486 IdAlternativeClothes
.push_back(IdAltClothes
);
489 debug(toString("'%s' field empty for the index '%d'.", alternativeClothesKey
, i
));
492 debug(toString("'%s' cannot get the array value for the index '%d'.", alternativeClothesKey
, i
));
496 debug(toString("'%s' cannot get the array size.", alternativeClothesKey
));
499 debug(toString("'%s' is not an array.", alternativeClothesKey
));
502 debug(toString("'%s' key not found.", alternativeClothesKey
));
505 static const char hairItemList
[] = "3d data.HairItem";
506 if(item
.getNodeByName(&elm
, hairItemList
) && elm
)
512 uint hairItemArraySize
;
513 if(elm
->getArraySize(hairItemArraySize
))
515 if(hairItemArraySize
> 0)
517 // Adjust the array size.
518 HairItemList
.resize(hairItemArraySize
);
521 for(uint i
=0; i
<hairItemArraySize
; ++i
)
523 string arrayNodeName
;
524 if(elm
->getArrayNodeName(arrayNodeName
, i
))
525 readEquipment(item
, string(hairItemList
)+"["+toString(i
)+"]", HairItemList
[i
]);
530 debug(toString("'%s' cannot get the array size.", hairItemList
));
533 debug(toString("'%s' is not an array.", hairItemList
));
536 debug(toString("'%s' key not found.", hairItemList
));
539 static const char groundFXList
[] = "3d data.GroundFX";
540 if(item
.getNodeByName(&elm
, groundFXList
) && elm
)
546 uint groundFXArraySize
;
547 if(elm
->getArraySize(groundFXArraySize
))
549 if(groundFXArraySize
> 0)
551 // Adjust the array size.
552 GroundFX
.reserve(groundFXArraySize
);
555 for(uint i
=0; i
< groundFXArraySize
; ++i
)
557 const UFormElm
*node
;
558 if (elm
->getArrayNode(&node
, i
) && node
!= NULL
)
561 if (!gfs
.build(*node
))
563 nlwarning("Error while building node %d", (int) i
);
568 for(k
= 0; k
< GroundFX
.size(); ++k
)
570 if (GroundFX
[k
].GroundID
== gfs
.GroundID
)
572 debug("Duplicated material");
577 if (k
== GroundFX
.size())
579 GroundFX
.push_back(gfs
);
587 debug(toString("'%s' cannot get the array size.", groundFXList
));
590 debug(toString("'%s' is not an array.", groundFXList
));
593 debug(toString("'%s' key not found.", groundFXList
));
594 // sort fxs by ground type
595 std::sort(GroundFX
.begin(), GroundFX
.end());
599 if(!item
.getValueByName(staticFx
, "3d data.FX"))
600 debug("Key '3d data.FX' not found.");
601 IdStaticFX
= ClientSheetsStrings
.add(staticFx
);
603 BodyToBone
.build(item
, "Localisation.");
606 for(uint k
= 0; k
< NumAttackLists
; ++k
)
608 std::string attackListName
;
609 if(item
.getValueByName(attackListName
, toString("attack_list%d", (int) k
).c_str()) && !attackListName
.empty())
611 AttackLists
.push_back(ClientSheetsStrings
.add(attackListName
));
615 if(!item
.getValueByName(RegionForce
, "Basics.RegionForce"))
618 debug("Key 'Basics.RegionForce' not found.");
621 if(!item
.getValueByName(ForceLevel
, "Basics.ForceLevel"))
624 debug("Key 'Basics.Regio Force' not found.");
627 if(!item
.getValueByName(Level
, "Basics.XPLevel"))
630 debug("Key 'Basics.XPLevel' not found.");
633 // offset for projectiles
634 const UFormElm
*pElt
;
635 nlverify (item
.getNodeByName (&pElt
, "3d data.ProjectileCastRay"));
639 nlverify (pElt
->getArraySize (arraySize
));
640 ProjectileCastRay
.reserve(arraySize
);
641 for (uint32 i
= 0; i
< arraySize
; ++i
)
643 const UFormElm
*pEltOfList
;
644 if (pElt
->getArrayNode (&pEltOfList
, i
) && pEltOfList
)
646 const UFormElm
*pEltPos
;
647 const UFormElm
*pEltOrigin
;
648 if (pEltOfList
->getNodeByName(&pEltPos
, "Pos") &&
649 pEltOfList
->getNodeByName(&pEltOrigin
, "Origin"))
651 CCharacterSheet::CCastRay cr
;
652 if (pEltPos
->getValueByName(cr
.Pos
.x
, "X") &&
653 pEltPos
->getValueByName(cr
.Pos
.y
, "Y") &&
654 pEltPos
->getValueByName(cr
.Pos
.z
, "Z") &&
655 pEltOrigin
->getValueByName(cr
.Origin
.x
, "X") &&
656 pEltOrigin
->getValueByName(cr
.Origin
.y
, "Y") &&
657 pEltOrigin
->getValueByName(cr
.Origin
.z
, "Z")
660 ProjectileCastRay
.push_back(cr
);
667 if(!item
.getValueByName(R2Npc
, "r2_npc"))
670 debug("Key 'R2Npc' not found.");
675 //-----------------------------------------------
677 // Serialize character sheet into binary data file.
678 //-----------------------------------------------
679 void CCharacterSheet::serial(NLMISC::IStream
&f
)
681 // Serialize class components.
682 // ClientSheetsStrings.serial(f, IdFirstName);
683 // ClientSheetsStrings.serial(f, IdLastName);
686 ClientSheetsStrings
.serial(f
, IdSkelFilename
);
687 ClientSheetsStrings
.serial(f
, IdAnimSetBaseName
);
688 ClientSheetsStrings
.serial(f
, IdAutomaton
);
690 f
.serial(SoundFamily
);
691 f
.serial(SoundVariation
);
692 ClientSheetsStrings
.serial(f
, IdLodCharacterName
);
694 f
.serial(LodCharacterDistance
);
695 f
.serial(Selectable
);
697 f
.serial(Attackable
);
701 f
.serial(SelectableBySpace
);
702 f
.serialEnum(HLState
);
703 f
.serial(CharacterScalePos
);
704 f
.serial(NamePosZLow
);
705 f
.serial(NamePosZNormal
);
706 f
.serial(NamePosZHigh
);
707 ClientSheetsStrings
.serial(f
, IdFame
);
716 f
.serial(ObjectInRightHand
);
717 f
.serial(ObjectInLeftHand
);
723 f
.serial(DistToFront
);
724 f
.serial(DistToBack
);
725 f
.serial(DistToSide
);
735 f
.serial(ClipRadius
);
736 f
.serial(ClipHeight
);
739 ClientSheetsStrings
.serial(f
, IdAlternativeClothes
);
742 f
.serialCont(HairItemList
);
744 f
.serialCont(GroundFX
);
746 f
.serial(DisplayOSD
);
748 ClientSheetsStrings
.serial(f
, IdStaticFX
);
750 f
.serial(BodyToBone
);
752 uint32 size
= (uint32
)AttackLists
.size();
754 AttackLists
.resize(size
);
756 for(uint k
= 0; k
< size
; ++k
)
758 ClientSheetsStrings
.serial(f
, AttackLists
[k
]);
762 f
.serial(DisplayInRadar
);
763 f
.serial(DisplayOSDName
);
764 f
.serial(DisplayOSDBars
);
765 f
.serial(DisplayOSDForceOver
);
766 f
.serial(Traversable
);
768 f
.serial(RegionForce
);
769 f
.serial(ForceLevel
);
772 f
.serialCont(ProjectileCastRay
);
778 // ***************************************************************************
779 void CCharacterSheet::getWholeEquipmentList(std::vector
<const CEquipment
*> &equipList
) const
782 equipList
.push_back(&Body
);
783 equipList
.push_back(&Legs
);
784 equipList
.push_back(&Arms
);
785 equipList
.push_back(&Hands
);
786 equipList
.push_back(&Feet
);
787 equipList
.push_back(&Head
);
788 equipList
.push_back(&Face
);
789 equipList
.push_back(&ObjectInRightHand
);
790 equipList
.push_back(&ObjectInLeftHand
);
791 for(uint i
=0;i
<HairItemList
.size();i
++)
793 equipList
.push_back(&HairItemList
[i
]);