Added ai command setEquipment
[ryzomcore.git] / ryzom / server / src / ai_service / sheets.cpp
blob924b2998556c4fba3f296c4f7e3588fc05e6fa5c
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
23 #include "stdpch.h"
25 // Net
26 #include "nel/net/service.h"
27 // Georges
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"
33 #include "sheets.h"
35 #include "nel/misc/o_xml.h"
37 using namespace MULTI_LINE_FORMATER;
39 ///////////
40 // USING //
41 ///////////
42 using namespace NLGEORGES;
43 using namespace NLMISC;
44 using namespace NLNET;
45 using namespace std;
46 using namespace AITYPES;
48 //////////////////////////////////////////////////////////////////////////////
49 // Constants //
50 //////////////////////////////////////////////////////////////////////////////
52 #ifdef NL_DEBUG
53 CVariable<string> debugSheet("ai", "debugSheet", "The sheet to break onto", "", 0, true);
54 #endif
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 //////////////////////////////////////////////////////////////////////////////
67 // CAIAction //
68 //////////////////////////////////////////////////////////////////////////////
70 AISHEETS::CAIAction::CAIAction()
71 : _SelfAction(false)
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
79 _SheetId = sheetId;
80 item.getValueByName(_SelfAction, "SelfAction");
83 uint AISHEETS::CAIAction::getVersion()
85 return 2;
88 void AISHEETS::CAIAction::serial(NLMISC::IStream& s)
90 s.serial(_SheetId);
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);
102 return container;
105 //////////////////////////////////////////////////////////////////////////////
106 // CActionList //
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;
118 else
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
127 _SheetId = sheetId;
130 NLGEORGES::UFormElm const* actionListNode = NULL;
131 item.getNodeByName(&actionListNode, "actions");
133 if (actionListNode)
135 uint arraySize = 0;
136 actionListNode->getArraySize(arraySize);
137 for (uint i=0; i<arraySize; ++i)
139 std::string action;
140 actionListNode->getArrayValue(action, i);
141 if (!action.empty())
143 addAction(NLMISC::CSheetId(action), action);
148 computeAbilities();
151 uint AISHEETS::CActionList::getVersion()
153 return 4;
156 void AISHEETS::CActionList::serial(NLMISC::IStream& s)
158 s.serial(_SheetId);
160 if (s.isReading())
162 uint32 nbSheet;
163 s.serial(nbSheet);
164 for (uint32 i=0; i<nbSheet; ++i)
166 NLMISC::CSheetId sheetId;
167 s.serial(sheetId);
168 addAction(sheetId, std::string());
171 else
173 uint32 nbSheet = (uint32)_Actions.size();
174 s.serial(nbSheet);
175 for (uint32 i=0; i<nbSheet; ++i)
177 NLMISC::CSheetId sheetId = _Actions[i]->SheetId();
178 s.serial(sheetId);
181 computeAbilities();
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);
192 else
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);
207 return container;
210 //////////////////////////////////////////////////////////////////////////////
211 // CGroupProperties //
212 //////////////////////////////////////////////////////////////////////////////
214 AISHEETS::CGroupProperties::CGroupProperties()
215 : _Assist(false)
216 , _Attack(false)
220 //////////////////////////////////////////////////////////////////////////////
221 // CCreature //
222 //////////////////////////////////////////////////////////////////////////////
224 AISHEETS::CCreature::CCreature()
225 : _Level(1)
226 , _Radius(0.5f), _Height(2.0f), _Width(1.0f), _Length(1.0f)
227 , _BoundingRadius(0.5)
228 , _BonusAggroHungry(0.0), _BonusAggroVeryHungry(0.0)
229 , _AssistDist(10)
230 , _MinFightDist(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();
241 #ifdef NL_DEBUG
242 nlassert(visualSlotManager);
243 #endif
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);
266 if (actionListNode)
268 uint arraySize = 0;
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;
283 s.serial(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);
294 else
296 NLMISC::CSheetId id;
297 s.serial(id);
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())
310 return;
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())
323 return;
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");
337 return;
340 if (!addActionConfig(NLMISC::CSheetId(sheetIdName), actionConfigList))
342 #ifdef NL_DEBUG
343 nlwarning("Error in actionConfig Reference for %s", sheetIdName.c_str());
344 #endif
348 bool AISHEETS::CCreature::addActionConfig(NLMISC::CSheetId const& sheetId, NLMISC::CDbgPtr<CActionList>& actionConfigList)
350 CActionList const* actionConfig = CSheets::getInstance()->lookupActionList(sheetId);
351 if (actionConfig)
353 actionConfigList = actionConfig; // fightConfigList.push_back(actionConfig);
354 return true;
356 return false;
359 AISHEETS::CGroupProperties& AISHEETS::CCreature::getProperties(uint32 groupIndex)
361 #if !FINAL_VERSION
362 nlassert(groupIndex!=std::numeric_limits<uint32>::max());
363 #endif
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];
376 else
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);
391 return 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
399 _SheetId = sheetId;
400 #ifdef NL_DEBUG
401 nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet.get()));
402 #endif
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
460 float tmpBip01ToMid;
461 if (!item.getValueByName(tmpBip01ToMid, "Collision.Dist Bip01 to mid"))
462 tmpBip01ToMid = 0.f;
463 // Get the distance from the bip01 to the front.
464 if (!item.getValueByName(_DistToFront, "Collision.Dist Bip01 to front"))
465 _DistToFront = 1.f;
466 // Get the distance from the bip01 to the front.
467 if (!item.getValueByName(_DistToBack, "Collision.Dist Bip01 to back"))
468 _DistToBack = 1.f;
469 // Get the creature Width.
470 if (!item.getValueByName(_DistToSide, "Collision.Width"))
471 _DistToSide = 1.f;
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"))
506 _XPLevel = 1;
508 if (!item.getValueByName(_NbPlayers, "Basics.NbPlayers"))
509 _NbPlayers = 1;
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);
520 float v;
521 if (item.getValueByName(v, "Basics.Characteristics.DynamicEnergyValue") && v!=0)
522 _EnergyValue = uint32(v * ENERGY_SCALE);
524 if (!item.getValueByName(_CanTurn, "Properties.Turn"))
525 _CanTurn = true;
527 uint32 meleeConfigChoice = 0;
528 uint32 rangeConfigChoice = 0;
529 uint32 nukeConfigChoice = 0;
530 uint32 healConfigChoice = 0;
532 breakable
534 std::string actionConfigStr;
535 item.getValueByName(actionConfigStr, "action_cfg");
537 if (actionConfigStr.length()!=5) // 4numbers + "f".
538 break;
540 char a[2] = "0";
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.
567 std::string left;
568 item.getValueByName(left, "item_left");
569 if (!left.empty())
570 _LeftItem = NLMISC::CSheetId(left);
572 std::string right;
573 item.getValueByName(right, "item_right");
574 if (!right.empty())
575 _RightItem = NLMISC::CSheetId(right);
577 calcFightAndVisualValues(&left, &right);
580 std::string s;
581 if (item.getValueByName(s, "Basics.Fame"))
583 _FactionIndex = CStaticFames::getInstance().getFactionIndex(s);
585 if (item.getValueByName(s, "Basics.FameForGuardAttack") && !s.empty())
587 float tmp;
588 sscanf(s.c_str(), "%f", &tmp);
589 _FameForGuardAttack = (sint32)tmp;
591 else
592 _FameForGuardAttack = ICreature::InvalidFameForGuardAttack;
594 // Assist Group Indexs.
596 item.getValueByName(_GroupIndexStr,"group_id");
597 if (_GroupIndexStr.empty())
599 std::string cat;
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();
611 // Bot name
612 item.getValueByName(_BotName, "Basics.BotName");
614 //////////////////////////////////////////////////////////////////////////
615 // Reads Script Comps.
616 breakable
618 NLGEORGES::UFormElm const* scriptCompNode = NULL;
619 const_cast<NLGEORGES::UFormElm&>(form->getRootNode()).getNodeByName(&scriptCompNode, "special_comp");
621 if (!scriptCompNode)
622 break;
624 uint arraySize = 0;
625 scriptCompNode->getArraySize(arraySize);
627 for (uint arrayIndex=0; arrayIndex<arraySize; ++arrayIndex)
629 std::string scriptCompStr;
630 scriptCompNode->getArrayValue(scriptCompStr, arrayIndex);
631 #ifndef NO_AI_COMP
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());
642 #endif
645 // Creature race
646 breakable
648 string raceStr;
649 if(item.getValueByName(raceStr, "Basics.Race") && !raceStr.empty())
650 _Race = EGSPD::CPeople::fromString(raceStr);
651 else
652 _Race = EGSPD::CPeople::Unknown;
656 void AISHEETS::CCreature::registerScriptComp(CFightScriptComp* scriptComp)
658 _ScriptCompList.push_back(scriptComp);
660 CFightSelectFilter* filter = dynamic_cast<CFightSelectFilter*>(scriptComp);
661 if (!filter)
662 return;
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()
676 return 45;
679 void AISHEETS::CCreature::serial(NLMISC::IStream &s)
681 s.serial(_SheetId, _Level);
682 #ifdef NL_DEBUG
683 nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet.get()));
684 #endif
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);
701 s.serial(_Scale);
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);
719 s.serial(_XPLevel);
720 s.serial(_NbPlayers);
722 s.serial(_EnergyValue);
724 s.serial(_CanTurn);
726 if (s.isReading())
728 readFightConfig(s, _FightConfig[FIGHTCFG_MELEE]);
729 readFightConfig(s, _FightConfig[FIGHTCFG_RANGE]);
730 readFightConfig(s, _FightConfig[FIGHTCFG_NUKE]);
731 readFightConfig(s, _FightConfig[FIGHTCFG_HEAL]);
733 else
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);
752 s.serial(_BotName);
753 s.serialEnum(_Race);
755 if (s.isReading())
757 setAssisGroupIndexs();
758 setAttackGroupIndexs();
761 if (s.isReading())
763 uint32 nbScript;
764 s.serial(nbScript);
765 for (uint32 index=0; index<nbScript; ++index)
767 string scriptCompStr;
768 s.serial(scriptCompStr);
770 #ifndef NO_AI_COMP
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());
781 #endif
784 else
786 uint32 nbScript = (uint32)_ScriptCompList.size();
787 s.serial(nbScript);
788 for (uint32 index=0; index<nbScript; ++index)
790 string str = _ScriptCompList[index]->toString();
791 s.serial(str);
795 calcFightAndVisualValues();
798 void AISHEETS::CCreature::getGroupStr(std::vector<uint32>& groupIndexStrList, std::string const& groupIndexStr)
800 if (groupIndexStr.empty())
801 return;
803 size_t firstIndex = 0;
804 size_t lastIndex = firstIndex - 1;
808 firstIndex = lastIndex + 1;
809 lastIndex = groupIndexStr.find_first_of(',',firstIndex);
811 std::string str;
812 if (lastIndex==std::string::npos)
813 str = groupIndexStr.substr(firstIndex, groupIndexStr.size()-firstIndex);
814 else
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 //////////////////////////////////////////////////////////////////////////////
824 // CRaceStats //
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
832 _SheetId = sheetId;
833 #ifdef NL_DEBUG
834 nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet.get()));
835 #endif
837 item.getValueByName(_Race, "Race");
840 uint AISHEETS::CRaceStats::getVersion()
842 return 1;
845 void AISHEETS::CRaceStats::serial(NLMISC::IStream &s)
847 s.serial(_SheetId);
848 #ifdef NL_DEBUG
849 nlassert(debugSheet.get().empty() || _SheetId!=NLMISC::CSheetId(debugSheet.get()));
850 #endif
852 s.serial(_SheetId);
853 s.serial(_Race);
856 //////////////////////////////////////////////////////////////////////////////
857 // CSheets //
858 //////////////////////////////////////////////////////////////////////////////
860 AISHEETS::CSheets* AISHEETS::CSheets::_Instance = NULL;
862 AISHEETS::CSheets* AISHEETS::CSheets::getInstance()
864 if (!_Instance)
865 _Instance = new AISHEETS::CSheets;
866 return _Instance;
869 void AISHEETS::CSheets::destroyInstance()
871 delete _Instance;
872 _Instance = NULL;
875 AISHEETS::CSheets::CSheets()
876 : _Initialised(false)
880 void AISHEETS::CSheets::init()
882 if (_Initialised)
883 return;
885 _PlayerGroupIndex=getGroupPropertiesIndex("zp");
886 #if !FINAL_VERSION
887 nlassert(_PlayerGroupIndex!=std::numeric_limits<uint32>::max());
888 #endif
890 packSheets(IService::getInstance()->WriteFilesDirectory.toString());
892 _Initialised=true;
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
900 if (varPtr!=NULL)
903 bool addSearchPath=false;
905 loadForm2("aiaction", writeFilesDirectoryName+AISPackedActionSheetsFilename, _ActionSheets, false, false);
906 if (_ActionSheets.empty())
908 if (!addSearchPath)
910 addSearchPath=true;
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())
920 if (!addSearchPath)
922 addSearchPath=true;
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);
931 if (_Sheets.empty())
933 if (!addSearchPath)
935 addSearchPath=true;
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())
945 if (!addSearchPath)
947 addSearchPath=true;
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);
955 else
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()
966 _Sheets.clear();
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));
982 #if !FINAL_VERSION
983 nldebug("GroupIndex Entry: %s %d", groupIndexName.c_str(), groupIndex);
984 #endif
986 it = _NameToGroupIndex.find(groupIndexName);
987 #ifdef NL_DEBUG
988 nlassert(it!=_NameToGroupIndex.end());
989 #endif
990 // Resize other group table. Better imp should be done with listeners.
992 return it->second;
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;
1017 else
1019 nlwarning("Unknow creature sheet '%s'", id.toString().c_str());
1020 return NULL;
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;
1032 else
1033 return NULL;
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);
1044 else
1045 return NULL;
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;
1055 else
1057 nlwarning("Unknow race_stats sheet '%s'", id.toString().c_str());
1058 return NULL;
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);
1072 return true;
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);
1081 return true;
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);
1090 return true;
1093 NLMISC_COMMAND(displaySheetByName,"display sheet data for given sheets","<sheet> [<sheet>...]")
1095 if (args.size() <1)
1096 return false;
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]));
1102 if (!sheet)
1104 log.displayNL("Failed to find sheet: %s",args[0].c_str());
1105 continue;
1107 std::vector<std::string> strings = sheet->getMultiLineInfoString();
1108 FOREACHC(it, std::vector<std::string>, strings)
1109 log.displayNL("%s", it->c_str());
1111 return true;
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)
1117 return false;
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]))));
1121 if (!sheet)
1123 log.displayNL("Failed to find sheet: %s",args[0].c_str());
1124 return false;
1127 // get the value
1128 float val;
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());
1133 return false;
1136 breakable
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());
1144 return false;
1147 std::vector<std::string> strings = sheet->getMultiLineInfoString();
1148 FOREACHC(it, std::vector<std::string>, strings)
1149 log.displayNL("%s", it->c_str());
1150 return true;