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) 2014-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 "nel/net/service.h"
28 #include "nel/georges/u_form.h"
29 #include "nel/georges/u_form_elm.h"
30 #include "nel/georges/u_form_loader.h"
31 #include "nel/georges/load_form.h"
35 #include "nel/misc/o_xml.h"
37 using namespace MULTI_LINE_FORMATER
;
42 using namespace NLGEORGES
;
43 using namespace NLMISC
;
44 using namespace NLNET
;
46 using namespace AITYPES
;
48 //////////////////////////////////////////////////////////////////////////////
50 //////////////////////////////////////////////////////////////////////////////
53 CVariable
<string
> debugSheet("ai", "debugSheet", "The sheet to break onto", "", 0, true);
56 char const* AISPackedSheetsFilename
="ais.packed_sheets";
57 char const* AISPackedFightConfigSheetsFilename
="ais_fight_config.packed_sheets";
58 char const* AISPackedActionSheetsFilename
="ais_action.packed_sheets";
59 char const* AISPackedRaceStatsSheetsFilename
="ais_race_stats.packed_sheets";
61 static AISHEETS::CCreature EmptySheet
;
63 sint32
AISHEETS::ICreature::InvalidFameForGuardAttack
= 0x7FFFFFFF;
66 //////////////////////////////////////////////////////////////////////////////
68 //////////////////////////////////////////////////////////////////////////////
70 AISHEETS::CAIAction::CAIAction()
75 void AISHEETS::CAIAction::readGeorges(NLMISC::CSmartPtr
<NLGEORGES::UForm
> const& form
, NLMISC::CSheetId
const& sheetId
)
77 NLGEORGES::UFormElm
const& item
= form
->getRootNode();
78 // the form was found so read the true values from George
80 item
.getValueByName(_SelfAction
, "SelfAction");
83 uint
AISHEETS::CAIAction::getVersion()
88 void AISHEETS::CAIAction::serial(NLMISC::IStream
& s
)
91 s
.serial(_SelfAction
);
94 std::vector
<std::string
> AISHEETS::CAIAction::getMultiLineInfoString() const
96 std::vector
<std::string
> container
;
98 pushTitle(container
, "AISHEETS::CAIAction");
99 pushEntry(container
, "ai_action sheet");
100 pushFooter(container
);
105 //////////////////////////////////////////////////////////////////////////////
107 //////////////////////////////////////////////////////////////////////////////
109 void AISHEETS::CActionList::computeAbilities()
111 _HasNormalAction
= false;
112 _HasSelfAction
= false;
113 FOREACH(itAction
, std::vector
<IAIActionCPtr
>, _Actions
)
115 IAIActionCPtr
const& action
= *itAction
;
116 if (!action
.isNull() && action
->SelfAction())
117 _HasSelfAction
= true;
119 _HasNormalAction
= true;
123 void AISHEETS::CActionList::readGeorges(NLMISC::CSmartPtr
<NLGEORGES::UForm
> const& form
, NLMISC::CSheetId
const& sheetId
)
125 NLGEORGES::UFormElm
const& item
= form
->getRootNode();
126 // the form was found so read the true values from George
130 NLGEORGES::UFormElm
const* actionListNode
= NULL
;
131 item
.getNodeByName(&actionListNode
, "actions");
136 actionListNode
->getArraySize(arraySize
);
137 for (uint i
=0; i
<arraySize
; ++i
)
140 actionListNode
->getArrayValue(action
, i
);
143 addAction(NLMISC::CSheetId(action
), action
);
151 uint
AISHEETS::CActionList::getVersion()
156 void AISHEETS::CActionList::serial(NLMISC::IStream
& s
)
164 for (uint32 i
=0; i
<nbSheet
; ++i
)
166 NLMISC::CSheetId sheetId
;
168 addAction(sheetId
, std::string());
173 uint32 nbSheet
= (uint32
)_Actions
.size();
175 for (uint32 i
=0; i
<nbSheet
; ++i
)
177 NLMISC::CSheetId sheetId
= _Actions
[i
]->SheetId();
184 void AISHEETS::CActionList::addAction(NLMISC::CSheetId
const& sheetId
, std::string
const& actionName
)
186 IAIActionCPtr action
= CSheets::getInstance()->lookupAction(sheetId
);
188 if (!action
.isNull())
190 _Actions
.push_back(action
);
194 if (!actionName
.empty())
195 nlwarning("action %s doesnt exist", actionName
.c_str());
199 std::vector
<std::string
> AISHEETS::CActionList::getMultiLineInfoString() const
201 std::vector
<std::string
> container
;
203 pushTitle(container
, "AISHEETS::CActionList");
204 pushEntry(container
, "action_list sheet");
205 pushFooter(container
);
210 //////////////////////////////////////////////////////////////////////////////
211 // CGroupProperties //
212 //////////////////////////////////////////////////////////////////////////////
214 AISHEETS::CGroupProperties::CGroupProperties()
220 //////////////////////////////////////////////////////////////////////////////
222 //////////////////////////////////////////////////////////////////////////////
224 AISHEETS::CCreature::CCreature()
226 , _Radius(0.5f
), _Height(2.0f
), _Width(1.0f
), _Length(1.0f
)
227 , _BoundingRadius(0.5)
228 , _BonusAggroHungry(0.0), _BonusAggroVeryHungry(0.0)
231 , _FactionIndex(CStaticFames::INVALID_FACTION_INDEX
)
232 , _FameForGuardAttack(ICreature::InvalidFameForGuardAttack
)
233 , _GroupPropertiesIndex(0)
234 , _DynamicGroupCountMultiplier(1)
238 void AISHEETS::CCreature::calcFightAndVisualValues(std::string
* left
, std::string
* right
)
240 CVisualSlotManager
* visualSlotManager
= CVisualSlotManager::getInstance();
242 nlassert(visualSlotManager
);
244 uint32 leftAsInt
= visualSlotManager
->leftItem2Index(LeftItem());
245 uint32 rightAsInt
= visualSlotManager
->rightItem2Index(RightItem());
247 if (LeftItem()!=NLMISC::CSheetId::Unknown
&& leftAsInt
==0 && left
!=NULL
)
249 if (left
->size() < 3 || left
->at(3) != 'p')
250 nlwarning("Left item '%s' not allowed, if ammo, this is normal", left
->c_str());
253 if (RightItem()!=NLMISC::CSheetId::Unknown
&& rightAsInt
==0 && right
!=NULL
)
255 if (right
->size() < 3 || right
->at(3) != 'p')
256 nlwarning("Right item '%s' not allowed, if ammo, this is normal", right
->c_str());
258 _MinFightDist
= (!FightConfig(FIGHTCFG_RANGE
).isNULL()||!FightConfig(FIGHTCFG_NUKE
).isNULL())?20:0;
261 void AISHEETS::CCreature::parseFightConfig(NLGEORGES::UForm
const* form
, std::string
const& fightConfigString
, uint32 actionListIndex
, NLMISC::CDbgPtr
<CActionList
>& fightConfig
)
263 NLGEORGES::UFormElm
const* actionListNode
= NULL
;
264 const_cast<NLGEORGES::UFormElm
&>(form
->getRootNode()).getNodeByName(&actionListNode
, fightConfigString
);
269 actionListNode
->getArraySize(arraySize
);
271 if (actionListIndex
<arraySize
)
273 std::string actionListFileName
;
274 actionListNode
->getArrayValue(actionListFileName
,actionListIndex
);
275 addActionConfig (actionListFileName
, fightConfig
);
280 void AISHEETS::CCreature::readFightConfig(NLMISC::IStream
& s
, NLMISC::CDbgPtr
<CActionList
>& fightConfig
)
282 NLMISC::CSheetId sheetId
;
284 if (sheetId
!=NLMISC::CSheetId::Unknown
)
285 addActionConfig(sheetId
, fightConfig
);
288 void AISHEETS::CCreature::saveFightConfig(NLMISC::IStream
& s
, NLMISC::CDbgPtr
<CActionList
>& fightConfig
)
290 if (!fightConfig
.isNULL())
292 s
.serial(fightConfig
->_SheetId
);
301 bool AISHEETS::CCreature::mustAssist(CCreature
const& creature
) const
303 return getPropertiesCst(creature
.GroupPropertiesIndex()).assist();
306 void AISHEETS::CCreature::setAssisGroupIndexs()
308 _GroupPropertiesIndex
= CSheets::getInstance()->getGroupPropertiesIndex(GroupIndexStr());
309 if (_GroupPropertiesIndex
==std::numeric_limits
<uint32
>::max())
312 std::vector
<uint32
> groupList
;
313 getGroupStr(groupList
, AssistGroupIndexStr());
315 FOREACH(it
, std::vector
<uint32
>, groupList
)
316 getProperties(*it
).setAssist(true);
319 void AISHEETS::CCreature::setAttackGroupIndexs()
321 _GroupPropertiesIndex
= CSheets::getInstance()->getGroupPropertiesIndex(GroupIndexStr());
322 if (_GroupPropertiesIndex
==std::numeric_limits
<uint32
>::max())
325 std::vector
<uint32
> groupList
;
326 getGroupStr(groupList
, AttackGroupIndexStr());
328 FOREACH(it
, std::vector
<uint32
>, groupList
)
329 getProperties(*it
).setAttack(true);
332 void AISHEETS::CCreature::addActionConfig(std::string
const& sheetIdName
, NLMISC::CDbgPtr
<CActionList
>& actionConfigList
)
334 if (sheetIdName
.empty())
336 nlwarning("sheetIdName is empty");
340 if (!addActionConfig(NLMISC::CSheetId(sheetIdName
), actionConfigList
))
343 nlwarning("Error in actionConfig Reference for %s", sheetIdName
.c_str());
348 bool AISHEETS::CCreature::addActionConfig(NLMISC::CSheetId
const& sheetId
, NLMISC::CDbgPtr
<CActionList
>& actionConfigList
)
350 CActionList
const* actionConfig
= CSheets::getInstance()->lookupActionList(sheetId
);
353 actionConfigList
= actionConfig
; // fightConfigList.push_back(actionConfig);
359 AISHEETS::CGroupProperties
& AISHEETS::CCreature::getProperties(uint32 groupIndex
)
362 nlassert(groupIndex
!=std::numeric_limits
<uint32
>::max());
364 if (_GroupPropertiesTbl
.size()<=groupIndex
&& groupIndex
!=std::numeric_limits
<uint32
>::max())
366 uint32
const resizeSize
= std::max((uint32
)CSheets::getInstance()->_NameToGroupIndex
.size(), (uint32
)(groupIndex
+1));
367 _GroupPropertiesTbl
.resize(resizeSize
);
369 return _GroupPropertiesTbl
[groupIndex
];
372 AISHEETS::CGroupProperties
const& AISHEETS::CCreature::getPropertiesCst(uint32 groupIndex
) const
374 if (groupIndex
<_GroupPropertiesTbl
.size())
375 return _GroupPropertiesTbl
[groupIndex
];
377 return CSheets::getInstance()->_DefaultGroupProp
;
380 std::vector
<std::string
> AISHEETS::CCreature::getMultiLineInfoString() const
382 std::vector
<std::string
> container
;
384 pushTitle(container
, "AISHEETS::CCreature");
385 pushEntry(container
, "sheet=" + SheetId().toString());
386 pushEntry(container
, "level=" + toString("%d", Level()));
387 container
.back() += "radii=" + toString("%4.1f,%4.1f", Radius(), BoundingRadius());
388 container
.back() += "height=" + toString("%4.1f", Height());
389 pushFooter(container
);
394 void AISHEETS::CCreature::readGeorges(NLMISC::CSmartPtr
<NLGEORGES::UForm
> const& form
, NLMISC::CSheetId
const& sheetId
)
396 NLGEORGES::UFormElm
const& item
= form
->getRootNode();
398 // the form was found so read the true values from George
401 nlassert(debugSheet
.get().empty() || _SheetId
!=NLMISC::CSheetId(debugSheet
.get()));
404 item
.getValueByName(_Level
,"Basics.Level");
406 if (!item
.getValueByName(_DynamicGroupCountMultiplier
, "Basics.Characteristics.DynGroupCountMultiplier"))
407 _DynamicGroupCountMultiplier
= 1;
409 item
.getValueByName(_ColorHead
,"Basics.Equipment.Head.Color");
410 item
.getValueByName(_ColorArms
,"Basics.Equipment.Arms.Color");
411 item
.getValueByName(_ColorHands
,"Basics.Equipment.Hands.Color");
412 item
.getValueByName(_ColorBody
,"Basics.Equipment.Body.Color");
413 item
.getValueByName(_ColorLegs
,"Basics.Equipment.Legs.Color");
414 item
.getValueByName(_ColorFeets
,"Basics.Equipment.Feet.Color");
416 item
.getValueByName(_Radius
,"Collision.CollisionRadius");
417 item
.getValueByName(_Height
,"Collision.Height");
418 item
.getValueByName(_Width
,"Collision.Width");
419 item
.getValueByName(_Length
,"Collision.Length");
420 item
.getValueByName(_BoundingRadius
,"Collision.BoundingRadius");
422 item
.getValueByName(_NotTraversable
, "Collision.NotTraversable");
424 item
.getValueByName(_BonusAggroHungry
,"Combat.BonusAggroHungry");
425 item
.getValueByName(_BonusAggroVeryHungry
,"Combat.BonusAggroVeryHungry");
427 item
.getValueByName(_AggroRadiusNotHungry
,"Combat.AggroRadiusNotHungry");
428 item
.getValueByName(_AggroRadiusHungry
,"Combat.AggroRadiusHungry");
429 item
.getValueByName(_AggroRadiusHunting
,"Combat.AggroRadiusHunting");
431 if (!item
.getValueByName(_AggroReturnDistCheck
,"Combat.AggroReturnDistCheck"))
432 _AggroReturnDistCheck
= -1.f
;
433 if (!item
.getValueByName(_AggroRadiusD1
,"Combat.AggroRadiusD1"))
434 _AggroRadiusD1
= -1.f
;
435 if (!item
.getValueByName(_AggroRadiusD2
,"Combat.AggroRadiusD2"))
436 _AggroRadiusD2
= -1.f
;
437 if (!item
.getValueByName(_AggroPrimaryGroupDist
,"Combat.AggroPrimaryGroupDist"))
438 _AggroPrimaryGroupDist
= -1.f
;
439 if (!item
.getValueByName(_AggroPrimaryGroupCoef
,"Combat.AggroPrimaryGroupCoef"))
440 _AggroPrimaryGroupCoef
= -1.f
;
441 if (!item
.getValueByName(_AggroSecondaryGroupDist
,"Combat.AggroSecondaryGroupDist"))
442 _AggroSecondaryGroupDist
= -1.f
;
443 if (!item
.getValueByName(_AggroSecondaryGroupCoef
,"Combat.AggroSecondaryGroupCoef"))
444 _AggroSecondaryGroupCoef
= -1.f
;
445 if (!item
.getValueByName(_AggroPropagationRadius
,"Combat.AggroPropagationRadius"))
446 _AggroPropagationRadius
= -1.f
;
448 item
.getValueByName(_AssistDist
,"Combat.AssistDist");
450 item
.getValueByName(_Scale
, "3d data.Scale");
452 std::string faunaTypeStr
;
453 item
.getValueByName(faunaTypeStr
, "Basics.type");
454 _FaunaType
= getType
<TFaunaType
>(faunaTypeStr
.c_str());
457 item
.getValueByName(_ForceDisplayCreatureName
, "3d data.ForceDisplayCreatureName");
459 // Get the dist fromm Bip to Mid
461 if (!item
.getValueByName(tmpBip01ToMid
, "Collision.Dist Bip01 to mid"))
463 // Get the distance from the bip01 to the front.
464 if (!item
.getValueByName(_DistToFront
, "Collision.Dist Bip01 to front"))
466 // Get the distance from the bip01 to the front.
467 if (!item
.getValueByName(_DistToBack
, "Collision.Dist Bip01 to back"))
469 // Get the creature Width.
470 if (!item
.getValueByName(_DistToSide
, "Collision.Width"))
473 _DistToFront
= _DistToFront
-tmpBip01ToMid
;
474 _DistToBack
= tmpBip01ToMid
-_DistToBack
;
475 _DistToSide
= _DistToSide
/2.f
;
477 _DistToFront
*= _Scale
;
478 _DistToBack
*= _Scale
;
479 _DistToSide
*= _Scale
;
481 if (!item
.getValueByName(_DistModulator
, "Combat.DistModulator"))
482 _DistModulator
= 0.5f
; // (0) - (1) - (n).
484 if (!item
.getValueByName(_TargetModulator
, "Combat.TargetModulator"))
485 _TargetModulator
= 1.f
; // (0) - (1).
487 if (!item
.getValueByName(_ScoreModulator
, "Combat.ScoreModulator"))
488 _ScoreModulator
= 0.01f
; // (0) - (1).
490 if (!item
.getValueByName(_FearModulator
, "Combat.FearModulator"))
491 _FearModulator
= 0.01f
; // (0) - (1).
493 if (!item
.getValueByName(_LifeLevelModulator
, "Combat.LifeLevelModulator"))
494 _LifeLevelModulator
= 0.5f
; // (0) - (1).
496 if (!item
.getValueByName(_CourageModulator
, "Combat.CourageModulator"))
497 _CourageModulator
= 2.f
; // (-n) - (0) - (+n).
499 if (!item
.getValueByName(_GroupCohesionModulator
, "Combat.GroupCohesionModulator"))
500 _GroupCohesionModulator
= 0.5f
; // (0) - (1)
502 if (!item
.getValueByName(_GroupDispersion
, "Basics.MovementSpeeds.GroupDispersion"))
503 _GroupDispersion
= 0.5f
; // (0) - (1).
505 if (!item
.getValueByName(_XPLevel
, "Basics.XPLevel"))
508 if (!item
.getValueByName(_NbPlayers
, "Basics.NbPlayers"))
511 nlassert(_DistModulator
>=0);
512 nlassert(_TargetModulator
>=0);
513 nlassert(_ScoreModulator
>=0 && _ScoreModulator
<=1);
514 nlassert(_FearModulator
>=0 && _FearModulator
<=1);
515 nlassert(_LifeLevelModulator
>=0 && _LifeLevelModulator
<=1);
516 nlassert(_GroupCohesionModulator
>=0 && _GroupCohesionModulator
<=1);
517 nlassert(_GroupDispersion
>=0 && _GroupDispersion
<=1);
519 _EnergyValue
= uint32(0.01f
* ENERGY_SCALE
);
521 if (item
.getValueByName(v
, "Basics.Characteristics.DynamicEnergyValue") && v
!=0)
522 _EnergyValue
= uint32(v
* ENERGY_SCALE
);
524 if (!item
.getValueByName(_CanTurn
, "Properties.Turn"))
527 uint32 meleeConfigChoice
= 0;
528 uint32 rangeConfigChoice
= 0;
529 uint32 nukeConfigChoice
= 0;
530 uint32 healConfigChoice
= 0;
534 std::string actionConfigStr
;
535 item
.getValueByName(actionConfigStr
, "action_cfg");
537 if (actionConfigStr
.length()!=5) // 4numbers + "f".
541 a
[0] = actionConfigStr
[0];
542 meleeConfigChoice
= atol(a
);
543 a
[0] = actionConfigStr
[1];
544 rangeConfigChoice
= atol(a
);
545 a
[0] = actionConfigStr
[2];
546 nukeConfigChoice
= atol(a
);
547 a
[0] = actionConfigStr
[3];
548 healConfigChoice
= atol(a
);
551 static std::string
meleeFightConfigString("melee_cfg");
552 static std::string
rangeFightConfigString("range_cfg");
553 static std::string
nukeFightConfigString("nuke_cfg");
554 static std::string
healFightConfigString("heal_cfg");
556 if (meleeConfigChoice
>0)
557 parseFightConfig(form
, meleeFightConfigString
, meleeConfigChoice
-1, _FightConfig
[FIGHTCFG_MELEE
]);
558 if (rangeConfigChoice
>0)
559 parseFightConfig(form
, rangeFightConfigString
, rangeConfigChoice
-1, _FightConfig
[FIGHTCFG_RANGE
]);
560 if (nukeConfigChoice
>0)
561 parseFightConfig(form
, nukeFightConfigString
, nukeConfigChoice
-1, _FightConfig
[FIGHTCFG_NUKE
]);
562 if (healConfigChoice
>0)
563 parseFightConfig(form
, healFightConfigString
, healConfigChoice
-1, _FightConfig
[FIGHTCFG_HEAL
]);
565 // reads left & right item.
568 item
.getValueByName(left
, "item_left");
570 _LeftItem
= NLMISC::CSheetId(left
);
573 item
.getValueByName(right
, "item_right");
575 _RightItem
= NLMISC::CSheetId(right
);
577 calcFightAndVisualValues(&left
, &right
);
581 if (item
.getValueByName(s
, "Basics.Fame"))
583 _FactionIndex
= CStaticFames::getInstance().getFactionIndex(s
);
585 if (item
.getValueByName(s
, "Basics.FameForGuardAttack") && !s
.empty())
588 sscanf(s
.c_str(), "%f", &tmp
);
589 _FameForGuardAttack
= (sint32
)tmp
;
592 _FameForGuardAttack
= ICreature::InvalidFameForGuardAttack
;
594 // Assist Group Indexs.
596 item
.getValueByName(_GroupIndexStr
,"group_id");
597 if (_GroupIndexStr
.empty())
600 std::string raceCode
;
601 if (item
.getValueByName(cat
, "category") && item
.getValueByName(raceCode
, "race_code"))
602 _GroupIndexStr
= cat
+ raceCode
;
605 item
.getValueByName(_AssistGroupIndexStr
, "group_assist");
606 setAssisGroupIndexs();
607 item
.getValueByName(_AttackGroupIndexStr
, "group_attack");
608 setAttackGroupIndexs();
612 item
.getValueByName(_BotName
, "Basics.BotName");
614 //////////////////////////////////////////////////////////////////////////
615 // Reads Script Comps.
618 NLGEORGES::UFormElm
const* scriptCompNode
= NULL
;
619 const_cast<NLGEORGES::UFormElm
&>(form
->getRootNode()).getNodeByName(&scriptCompNode
, "special_comp");
625 scriptCompNode
->getArraySize(arraySize
);
627 for (uint arrayIndex
=0; arrayIndex
<arraySize
; ++arrayIndex
)
629 std::string scriptCompStr
;
630 scriptCompNode
->getArrayValue(scriptCompStr
, arrayIndex
);
632 CFightScriptComp
* scriptComp
;
635 scriptComp
= CFightScriptCompReader::createScriptComp(scriptCompStr
);
636 registerScriptComp(scriptComp
);
638 catch (const ReadFightActionException
& ex
)
640 nlwarning("script read error (ignored): %s", ex
.what());
649 if(item
.getValueByName(raceStr
, "Basics.Race") && !raceStr
.empty())
650 _Race
= EGSPD::CPeople::fromString(raceStr
);
652 _Race
= EGSPD::CPeople::Unknown
;
656 void AISHEETS::CCreature::registerScriptComp(CFightScriptComp
* scriptComp
)
658 _ScriptCompList
.push_back(scriptComp
);
660 CFightSelectFilter
* filter
= dynamic_cast<CFightSelectFilter
*>(scriptComp
);
664 std::string
const& param
= filter
->getParam();
665 if (param
=="ON_UPDATE")
666 _UpdateScriptList
.push_back(scriptComp
);
667 if (param
=="ON_DEATH")
668 _DeathScriptList
.push_back(scriptComp
);
669 if (param
=="ON_BIRTH")
670 _BirthScriptList
.push_back(scriptComp
);
674 uint
AISHEETS::CCreature::getVersion()
679 void AISHEETS::CCreature::serial(NLMISC::IStream
&s
)
681 s
.serial(_SheetId
, _Level
);
683 nlassert(debugSheet
.get().empty() || _SheetId
!=NLMISC::CSheetId(debugSheet
.get()));
686 s
.serial(_DynamicGroupCountMultiplier
);
687 s
.serial(_ColorHead
, _ColorArms
, _ColorHands
),
688 s
.serial(_ColorBody
, _ColorLegs
, _ColorFeets
);
690 s
.serial(_Radius
, _Height
, _Width
, _Length
);
691 s
.serial(_BoundingRadius
);
692 s
.serial(_BonusAggroHungry
, _BonusAggroVeryHungry
);
693 s
.serial(_AggroRadiusNotHungry
, _AggroRadiusHungry
, _AggroRadiusHunting
);
694 s
.serial(_AggroReturnDistCheck
);
695 s
.serial(_AggroRadiusD1
, _AggroRadiusD2
);
696 s
.serial(_AggroPrimaryGroupDist
);
697 s
.serial(_AggroPrimaryGroupCoef
);
698 s
.serial(_AggroSecondaryGroupDist
);
699 s
.serial(_AggroSecondaryGroupCoef
);
700 s
.serial(_AggroPropagationRadius
);
702 s
.serial(_ForceDisplayCreatureName
);
703 s
.serialEnum(_FaunaType
);
705 s
.serial(_DistToFront
);
706 s
.serial(_DistToBack
);
707 s
.serial(_DistToSide
);
709 s
.serial(_DistModulator
);
710 s
.serial(_TargetModulator
);
711 s
.serial(_ScoreModulator
);
712 s
.serial(_FearModulator
);
713 s
.serial(_LifeLevelModulator
);
714 s
.serial(_CourageModulator
);
715 s
.serial(_GroupCohesionModulator
);
717 s
.serial(_GroupDispersion
);
720 s
.serial(_NbPlayers
);
722 s
.serial(_EnergyValue
);
728 readFightConfig(s
, _FightConfig
[FIGHTCFG_MELEE
]);
729 readFightConfig(s
, _FightConfig
[FIGHTCFG_RANGE
]);
730 readFightConfig(s
, _FightConfig
[FIGHTCFG_NUKE
]);
731 readFightConfig(s
, _FightConfig
[FIGHTCFG_HEAL
]);
735 saveFightConfig(s
, _FightConfig
[FIGHTCFG_MELEE
]);
736 saveFightConfig(s
, _FightConfig
[FIGHTCFG_RANGE
]);
737 saveFightConfig(s
, _FightConfig
[FIGHTCFG_NUKE
]);
738 saveFightConfig(s
, _FightConfig
[FIGHTCFG_HEAL
]);
741 s
.serial(_AssistDist
);
743 // serialize left & right item.
744 s
.serial(_LeftItem
, _RightItem
);
745 s
.serial(_FactionIndex
);
746 s
.serial(_FameForGuardAttack
);
748 s
.serial(_GroupIndexStr
);
749 s
.serial(_AssistGroupIndexStr
);
750 s
.serial(_AttackGroupIndexStr
);
757 setAssisGroupIndexs();
758 setAttackGroupIndexs();
765 for (uint32 index
=0; index
<nbScript
; ++index
)
767 string scriptCompStr
;
768 s
.serial(scriptCompStr
);
771 CFightScriptComp
* scriptComp
;
774 scriptComp
= CFightScriptCompReader::createScriptComp(scriptCompStr
);
775 registerScriptComp(scriptComp
);
777 catch (const ReadFightActionException
& ex
)
779 nlwarning("script read error (ignored): %s", ex
.what());
786 uint32 nbScript
= (uint32
)_ScriptCompList
.size();
788 for (uint32 index
=0; index
<nbScript
; ++index
)
790 string str
= _ScriptCompList
[index
]->toString();
795 calcFightAndVisualValues();
798 void AISHEETS::CCreature::getGroupStr(std::vector
<uint32
>& groupIndexStrList
, std::string
const& groupIndexStr
)
800 if (groupIndexStr
.empty())
803 size_t firstIndex
= 0;
804 size_t lastIndex
= firstIndex
- 1;
808 firstIndex
= lastIndex
+ 1;
809 lastIndex
= groupIndexStr
.find_first_of(',',firstIndex
);
812 if (lastIndex
==std::string::npos
)
813 str
= groupIndexStr
.substr(firstIndex
, groupIndexStr
.size()-firstIndex
);
815 str
= groupIndexStr
.substr(firstIndex
, lastIndex
-firstIndex
);
817 uint32
const otherGroupIndex
= CSheets::getInstance()->getGroupPropertiesIndex(str
);
818 if (otherGroupIndex
!=std::numeric_limits
<uint32
>::max())
819 groupIndexStrList
.push_back(otherGroupIndex
);
820 } while (lastIndex
!=std::string::npos
);
823 //////////////////////////////////////////////////////////////////////////////
825 //////////////////////////////////////////////////////////////////////////////
827 void AISHEETS::CRaceStats::readGeorges(NLMISC::CSmartPtr
<NLGEORGES::UForm
> const& form
, NLMISC::CSheetId
const& sheetId
)
829 NLGEORGES::UFormElm
const& item
= form
->getRootNode();
831 // the form was found so read the true values from George
834 nlassert(debugSheet
.get().empty() || _SheetId
!=NLMISC::CSheetId(debugSheet
.get()));
837 item
.getValueByName(_Race
, "Race");
840 uint
AISHEETS::CRaceStats::getVersion()
845 void AISHEETS::CRaceStats::serial(NLMISC::IStream
&s
)
849 nlassert(debugSheet
.get().empty() || _SheetId
!=NLMISC::CSheetId(debugSheet
.get()));
856 //////////////////////////////////////////////////////////////////////////////
858 //////////////////////////////////////////////////////////////////////////////
860 AISHEETS::CSheets
* AISHEETS::CSheets::_Instance
= NULL
;
862 AISHEETS::CSheets
* AISHEETS::CSheets::getInstance()
865 _Instance
= new AISHEETS::CSheets
;
869 void AISHEETS::CSheets::destroyInstance()
875 AISHEETS::CSheets::CSheets()
876 : _Initialised(false)
880 void AISHEETS::CSheets::init()
885 _PlayerGroupIndex
=getGroupPropertiesIndex("zp");
887 nlassert(_PlayerGroupIndex
!=std::numeric_limits
<uint32
>::max());
890 packSheets(IService::getInstance()->WriteFilesDirectory
.toString());
895 void AISHEETS::CSheets::packSheets(const std::string
&writeFilesDirectoryName
)
897 CConfigFile::CVar
*varPtr
= IService::isServiceInitialized() ? IService::getInstance()->ConfigFile
.getVarPtr(std::string("GeorgePaths")) : NULL
;
899 // if config file variable 'GeorgePaths' exists then only do a minimal loadForms otherwise do the full works
903 bool addSearchPath
=false;
905 loadForm2("aiaction", writeFilesDirectoryName
+AISPackedActionSheetsFilename
, _ActionSheets
, false, false);
906 if (_ActionSheets
.empty())
911 for (uint32 i
=0;i
<varPtr
->size();++i
)
912 CPath::addSearchPath(NLMISC::expandEnvironmentVariables(varPtr
->asString(i
)), true, false);
914 loadForm2("aiaction", writeFilesDirectoryName
+AISPackedActionSheetsFilename
, _ActionSheets
, true);
917 loadForm("actionlist", writeFilesDirectoryName
+AISPackedFightConfigSheetsFilename
, _ActionListSheets
, false, false);
918 if (_ActionListSheets
.empty())
923 for (uint32 i
=0;i
<varPtr
->size();++i
)
924 CPath::addSearchPath(NLMISC::expandEnvironmentVariables(varPtr
->asString(i
)), true, false);
926 loadForm("actionlist", writeFilesDirectoryName
+AISPackedFightConfigSheetsFilename
, _ActionListSheets
, true);
930 loadForm2("creature", writeFilesDirectoryName
+AISPackedSheetsFilename
, _Sheets
, false, false);
936 for (uint32 i
=0;i
<varPtr
->size();++i
)
937 CPath::addSearchPath(NLMISC::expandEnvironmentVariables(varPtr
->asString(i
)), true, false);
939 loadForm2("creature", writeFilesDirectoryName
+AISPackedSheetsFilename
, _Sheets
, true);
942 loadForm2("race_stats", writeFilesDirectoryName
+AISPackedRaceStatsSheetsFilename
, _RaceStatsSheets
, false, false);
943 if (_RaceStatsSheets
.empty())
948 for (uint32 i
=0;i
<varPtr
->size();++i
)
949 CPath::addSearchPath(NLMISC::expandEnvironmentVariables(varPtr
->asString(i
)), true, false);
951 loadForm2("race_stats", writeFilesDirectoryName
+AISPackedRaceStatsSheetsFilename
, _RaceStatsSheets
, true);
957 loadForm2("aiaction", writeFilesDirectoryName
+AISPackedActionSheetsFilename
, _ActionSheets
, true);
958 loadForm("actionlist", writeFilesDirectoryName
+AISPackedFightConfigSheetsFilename
, _ActionListSheets
, true);
959 loadForm2("creature", writeFilesDirectoryName
+AISPackedSheetsFilename
, _Sheets
, true);
960 loadForm2("race_stats", writeFilesDirectoryName
+AISPackedRaceStatsSheetsFilename
, _RaceStatsSheets
, true);
964 void AISHEETS::CSheets::release()
967 _ActionListSheets
.clear();
968 _ActionSheets
.clear();
969 _RaceStatsSheets
.clear();
972 uint32
AISHEETS::CSheets::getGroupPropertiesIndex(const std::string
&groupIndexName
)
974 if (groupIndexName
.empty())
975 return std::numeric_limits
<uint32
>::max();
977 std::map
<string
, uint32
>::iterator it
= _NameToGroupIndex
.find(NLMISC::toUpperAscii(groupIndexName
));
978 if (it
==_NameToGroupIndex
.end())
980 uint32 groupIndex
= (uint32
)_NameToGroupIndex
.size();
981 _NameToGroupIndex
.insert(make_pair(groupIndexName
, groupIndex
));
983 nldebug("GroupIndex Entry: %s %d", groupIndexName
.c_str(), groupIndex
);
986 it
= _NameToGroupIndex
.find(groupIndexName
);
988 nlassert(it
!=_NameToGroupIndex
.end());
990 // Resize other group table. Better imp should be done with listeners.
995 void AISHEETS::CSheets::display(CSmartPtr
<CStringWriter
> stringWriter
, uint infoSelect
)
997 nlassert(_Initialised
);
999 std::map
<CSheetId
, AISHEETS::CCreaturePtr
>::iterator it
;
1000 for(it
=_Sheets
.begin(); it
!=_Sheets
.end(); ++it
)
1002 std::vector
<std::string
> strings
;
1003 strings
= it
->second
->getMultiLineInfoString();
1004 FOREACHC(itString
, std::vector
<std::string
>, strings
)
1005 stringWriter
->append(toString("%04x", it
->second
->SheetId().asInt()) + " " + *itString
);
1009 AISHEETS::ICreatureCPtr
AISHEETS::CSheets::lookup(CSheetId
const& id
)
1011 // setup an iterator and lookup the sheet id in the map
1012 std::map
<CSheetId
, AISHEETS::CCreaturePtr
>::iterator it
=_Sheets
.find(id
);
1014 // if we found a valid entry return a pointer to the creature record otherwise 0
1015 if (it
!=_Sheets
.end())
1016 return (AISHEETS::CCreature
*)it
->second
;
1019 nlwarning("Unknow creature sheet '%s'", id
.toString().c_str());
1024 AISHEETS::IAIActionCPtr
AISHEETS::CSheets::lookupAction(CSheetId
const& id
)
1026 // setup an iterator and lookup the sheet id in the map
1027 std::map
<CSheetId
, CAIActionPtr
>::iterator it
= _ActionSheets
.find(id
);
1029 // if we found a valid entry return a pointer to the creature record otherwise 0
1030 if (it
!=_ActionSheets
.end())
1031 return (AISHEETS::CAIAction
*)it
->second
;
1036 AISHEETS::CActionList
const* AISHEETS::CSheets::lookupActionList(CSheetId
const& id
)
1038 // setup an iterator and lookup the sheet id in the map
1039 std::map
<CSheetId
, AISHEETS::CActionList
>::iterator it
=_ActionListSheets
.find(id
);
1041 // if we found a valid entry return a pointer to the creature record otherwise 0
1042 if (it
!=_ActionListSheets
.end())
1043 return &((*it
).second
);
1048 AISHEETS::IRaceStatsCPtr
AISHEETS::CSheets::lookupRaceStats(CSheetId
const& id
)
1050 // setup an iterator and lookup the sheet id in the map
1051 std::map
<CSheetId
, AISHEETS::CRaceStatsPtr
>::iterator it
=_RaceStatsSheets
.find(id
);
1053 if (it
!=_RaceStatsSheets
.end())
1054 return (AISHEETS::CRaceStats
*)it
->second
;
1057 nlwarning("Unknow race_stats sheet '%s'", id
.toString().c_str());
1062 //////////////////////////////////////////////////////////////////////////////
1063 // Console commands //
1064 //////////////////////////////////////////////////////////////////////////////
1066 NLMISC_COMMAND(displaySheetNames
,"display sheet data for all sheets","")
1068 if(args
.size() !=0) return false;
1070 AISHEETS::CSheets::getInstance()->display(new CLogStringWriter(&log
),0);
1075 NLMISC_COMMAND(displaySheetBasics
,"display sheet data for all sheets","")
1077 if(args
.size() !=0) return false;
1079 AISHEETS::CSheets::getInstance()->display(new CLogStringWriter(&log
),1);
1084 NLMISC_COMMAND(displaySheetCombat
,"display sheet data for all sheets","")
1086 if(args
.size() !=0) return false;
1088 AISHEETS::CSheets::getInstance()->display(new CLogStringWriter(&log
),2);
1093 NLMISC_COMMAND(displaySheetByName
,"display sheet data for given sheets","<sheet> [<sheet>...]")
1098 for (uint i
=0;i
<args
.size();++i
)
1100 // lookup the sheet id
1101 AISHEETS::ICreatureCPtr sheet
= AISHEETS::CSheets::getInstance()->lookup(NLMISC::CSheetId(args
[i
]));
1104 log
.displayNL("Failed to find sheet: %s",args
[0].c_str());
1107 std::vector
<std::string
> strings
= sheet
->getMultiLineInfoString();
1108 FOREACHC(it
, std::vector
<std::string
>, strings
)
1109 log
.displayNL("%s", it
->c_str());
1114 NLMISC_COMMAND(setSheetProperty,"change a value read from a sheet","<sheet> level|walk|run|radius|bounding|height|aggro|attack|danger|flight|survive|initSurv|crit <value>")
1116 if (args.size() !=3)
1119 // lookup the sheet id
1120 AISHEETS::CCreature* sheet = const_cast<AISHEETS::CCreature*>(dynamic_cast<AISHEETS::CCreature const*>(AISHEETS::CSheets::getInstance()->lookup(NLMISC::CSheetId(args[0]))));
1123 log.displayNL("Failed to find sheet: %s",args[0].c_str());
1129 NLMISC::fromString(args[2], val);
1130 if (val==0 && args[2]!="0" && args[2]!="0.0")
1132 log.displayNL("'%s' is not a valid value",args[2].c_str());
1138 if (nlstricmp(args[1].c_str(),"level")==0) { sheet->_Level=(uint32)val; break; }
1139 if (nlstricmp(args[1].c_str(),"radius")==0) { sheet->_Radius=val; break; }
1140 if (nlstricmp(args[1].c_str(),"bounding")==0) { sheet->_BoundingRadius=val; break; }
1141 if (nlstricmp(args[1].c_str(),"height")==0) { sheet->_Height=val; break; }
1143 log.displayNL("variable name not recognised: %s", args[1].c_str());
1147 std::vector<std::string> strings = sheet->getMultiLineInfoString();
1148 FOREACHC(it, std::vector<std::string>, strings)
1149 log.displayNL("%s", it->c_str());