Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / family_behavior.cpp
blobfc0d12a4f8df316d129134011a35f6ff737f5291
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "stdpch.h"
20 #include "family_behavior.h"
21 #include "game_share/fame.h"
22 #include "continent.h"
23 #include "ai_instance.h"
24 #include "ai_grp_npc.h"
25 #include "ai_grp_fauna.h"
27 #include "continent_inline.h"
28 #include "dyn_grp_inline.h"
30 using namespace MULTI_LINE_FORMATER;
32 using namespace std;
33 using namespace NLMISC;
34 using namespace NLNET;
35 using namespace AITYPES;
39 CFamilyBehavior::CFamilyBehavior(CCellZone *owner, const CGroupFamily *grpFamily)
40 : CChild<CCellZone>(owner)
41 ,_BaseLevel(0)
42 ,_EffectiveLevel(0)
43 ,_PlayerEffect(0)
44 ,_CurrentLevel(0)
45 ,_TheoricalLevel(0)
46 ,_LastUpdateTime(CTimeInterface::gameCycle()+CAIS::rand32(100))
47 ,_UpdatePeriod(1)
48 ,_GrpFamily(grpFamily)
50 // create the family behavior
51 _FamilyProfile = IFamilyProfile::createFamilyProfile(grpFamily->profileName(),IFamilyProfile::CtorParam(this));
53 _ManagerNpc = new CMgrNpc(this, 0, getName()+":npc_manager", "");
54 _ManagerFauna = new CMgrFauna(this, 0, getName()+":fauna_manager", "");
55 _ManagerNpc->spawn();
56 _ManagerFauna->spawn();
57 for (uint32 i=0;i<4;i++ )
59 _Modifier[i]=1;
61 // tmp nico
63 nlinfo("creating new family beahviour, activities : ");
64 nlinfo("FOOD");
66 std::set<NLMISC::TStringId> &props = grpFamily->getProfileProperty("food").properties();
67 std::set<NLMISC::TStringId>::iterator it;
68 for (it = props.begin(); it != props.end(); ++it)
70 nlinfo(NLMISC::CStringMapper::unmap(*it).c_str());
73 nlinfo("REST");
75 std::set<NLMISC::TStringId> &props = grpFamily->getProfileProperty("rest").properties();
76 std::set<NLMISC::TStringId>::iterator it;
77 for (it = props.begin(); it != props.end(); ++it)
79 nlinfo(NLMISC::CStringMapper::unmap(*it).c_str());
86 std::string CFamilyBehavior::getName() const
88 return _GrpFamily->getName();
92 CAIInstance* CFamilyBehavior::getAIInstance() const
94 return getOwner()->getAIInstance();
98 const std::string &getLevelString (const uint32 &levelIndex)
100 static std::string s0("0-25");
101 static std::string s1("25-50");
102 static std::string s2("50-75");
103 static std::string s3("75-100");
104 static std::string invalid("InvalidLevel");
106 switch (levelIndex)
108 case 0:
109 return s0;
110 case 1:
111 return s1;
112 case 2:
113 return s2;
114 case 3:
115 return s3;
116 default:
117 return invalid;
121 uint32 CFamilyBehavior::energyScale (uint32 levelIndex) const
123 if (levelIndex==~0)
124 levelIndex=getLevelIndex();
126 return (uint32)(_GrpFamily->levelEnergyValue(levelIndex)*(double)_Modifier[levelIndex]*(double)AITYPES::ENERGY_SCALE);
129 void CFamilyBehavior::displayLogOld (CStringWriter &stringWriter, bool detailled)
132 string res="in "+getCellZone()->getAliasFullName();
133 res+=", GroupFamily"+getName();
134 res+="\t NrjLvl="+toString(getLevelIndex());
135 res+=":"+getLevelString(getLevelIndex());
136 res+="("+toString(effectiveLevel()/(float)ENERGY_SCALE);
137 res+=") CurNrjScale="+toString(_CurrentLevel/(float)ENERGY_SCALE);
138 res+=" FinalNrjScale["+getLevelString(getLevelIndex());
139 res+="]="+toString(energyScale()/(float)ENERGY_SCALE);
140 res+=" (NrjScale="+toString(_GrpFamily->levelEnergyValue(getLevelIndex()));
141 res+="*Modifier="+toString(_Modifier[getLevelIndex()]);
142 res+=") Theorical="+toString(_TheoricalLevel/(float)ENERGY_SCALE);
144 stringWriter.append(res);
146 if (!detailled)
147 return;
149 for (uint32 i=0;i<4;i++)
151 stringWriter.append(" > "+getLevelString(i)+" \t: FinalEnergyScale "
152 +toString(energyScale(i)/(float)ENERGY_SCALE)
153 +" (EnergyScale="+toString(_GrpFamily->levelEnergyValue(i))
154 +" Modifier="+toString(_Modifier[i])
155 +")");
159 string const& celZon = getCellZone()->getAliasFullName();
160 string const& grpFam = getName();
161 string const& lvlIdx = toString(getLevelIndex());
162 string const& lvlIdxStr = getLevelString(getLevelIndex());
163 string const& effLvl = toString(effectiveLevel()/(float)ENERGY_SCALE);
164 string const& curLvl = toString(_CurrentLevel/(float)ENERGY_SCALE);
165 string const& finLvl = toString(energyScale()/(float)ENERGY_SCALE);
166 string const& teoLvl = toString(_TheoricalLevel/(float)ENERGY_SCALE);
167 string const& lvlNrgVal = toString(_GrpFamily->levelEnergyValue(getLevelIndex()));
168 string const& modifier = toString(_Modifier[getLevelIndex()]);
170 string log =
171 "in "+celZon+", GroupFamily"+grpFam+"\t NrjLvl="+lvlIdx+":"+lvlIdxStr+"("+effLvl+") CurNrjScale="+curLvl
172 +" FinalNrjScale["+lvlIdxStr+"]="+finLvl+" (NrjScale="+lvlNrgVal+"*Modifier="+modifier+") Theorical="+teoLvl;
174 stringWriter.append(log);
176 if (!detailled)
177 return;
179 for (uint32 i=0;i<4;i++)
181 stringWriter.append(" > "+getLevelString(i)+" \t: FinalEnergyScale "
182 +toString(energyScale(i)/(float)ENERGY_SCALE)
183 +" (EnergyScale="+toString(_GrpFamily->levelEnergyValue(i))
184 +" Modifier="+toString(_Modifier[i])
185 +")");
189 void CFamilyBehavior::displayLogHeaders(CStringWriter& stringWriter, int index, bool detailled, std::vector<size_t> widths)
191 vector<string> cols(9, "");
192 cols[0] = "CellZone";
193 cols[1] = "GroupFamily";
194 cols[2] = "Levels for";
195 cols[3] = "effective";
196 cols[4] = "current";
197 cols[5] = "final";
198 cols[6] = "theorical";
199 cols[7] = "value";
200 cols[8] = "modifier";
202 for (size_t j=0; j<cols.size(); ++j)
203 for (size_t i=cols[j].length(); i<widths[j]; ++i)
204 cols[j] += " ";
206 string log = string("| ")+cols[0]+" | "+cols[1]+" | "+cols[2]+" | "+cols[3]+" | "+cols[4]+" | "+cols[5]+" | "+cols[6]+" | "+cols[7]+" | "+cols[8]+" |";
207 stringWriter.append(log);
210 void CFamilyBehavior::displayLogLine(CStringWriter& stringWriter, int index, bool detailled, std::vector<size_t> widths)
212 vector<string> cols(9, "");
214 for (size_t j=0; j<cols.size(); ++j)
215 for (size_t i=cols[j].length(); i<widths[j]; ++i)
216 cols[j] += "-";
218 string log = string("+-")+cols[0]+"-+-"+cols[1]+"-+-"+cols[2]+"-+-"+cols[3]+"-+-"+cols[4]+"-+-"+cols[5]+"-+-"+cols[6]+"-+-"+cols[7]+"-+-"+cols[8]+"-+";
219 stringWriter.append(log);
222 void CFamilyBehavior::displayLog(CStringWriter& stringWriter, int index, bool detailled, std::vector<size_t> widths)
224 vector<string> cols(9, "");
225 cols[0] = getCellZone()->getAliasFullName();
226 cols[1] = getName();
227 cols[2] = toString(getLevelIndex()) + ":" + getLevelString(getLevelIndex());
228 cols[3] = toString(effectiveLevel()/(float)ENERGY_SCALE);
229 cols[4] = toString(_CurrentLevel/(float)ENERGY_SCALE);
230 cols[5] = toString(energyScale()/(float)ENERGY_SCALE);
231 cols[6] = toString(_TheoricalLevel/(float)ENERGY_SCALE);
232 cols[7] = toString(_GrpFamily->levelEnergyValue(getLevelIndex()));
233 cols[8] = toString(_Modifier[getLevelIndex()]);
235 for (size_t j=0; j<cols.size(); ++j)
236 for (size_t i=cols[j].length(); i<widths[j]; ++i)
237 cols[j] += " ";
239 string log = string("| ")+cols[0]+" | "+cols[1]+" | "+cols[2]+" | "+cols[3]+" | "+cols[4]+" | "+cols[5]+" | "+cols[6]+" | "+cols[7]+" | "+cols[8]+" |";
241 stringWriter.append(log);
243 if (!detailled)
244 return;
246 for (uint32 i=0;i<4;i++)
248 stringWriter.append(" > "+getLevelString(i)+" \t: FinalEnergyScale "
249 +toString(energyScale(i)/(float)ENERGY_SCALE)
250 +" (EnergyScale="+toString(_GrpFamily->levelEnergyValue(i))
251 +" Modifier="+toString(_Modifier[i])
252 +")");
257 void CFamilyBehavior::checkLogHeadersWidths(std::vector<size_t>& widths, int index, bool detailled)
259 vector<string> cols(9, "");
260 cols[0] = "CellZone";
261 cols[1] = "GroupFamily";
262 cols[2] = "Levels for";
263 cols[3] = "effective";
264 cols[4] = "current";
265 cols[5] = "final";
266 cols[6] = "theorical";
267 cols[7] = "value";
268 cols[8] = "modifier";
270 for (size_t j=0; j<cols.size(); ++j)
271 widths[j] = std::max(widths[j], cols[j].length());
274 void CFamilyBehavior::checkLogWidths(std::vector<size_t>& widths, int index, bool detailled)
276 vector<string> cols(9, "");
277 cols[0] = getCellZone()->getAliasFullName();
278 cols[1] = getName();
279 cols[2] = toString(getLevelIndex()) + ":" + getLevelString(getLevelIndex());
280 cols[3] = toString(effectiveLevel()/(float)ENERGY_SCALE);
281 cols[4] = toString(_CurrentLevel/(float)ENERGY_SCALE);
282 cols[5] = toString(energyScale()/(float)ENERGY_SCALE);
283 cols[6] = toString(_TheoricalLevel/(float)ENERGY_SCALE);
284 cols[7] = toString(_GrpFamily->levelEnergyValue(getLevelIndex()));
285 cols[8] = toString(_Modifier[getLevelIndex()]);
287 for (size_t j=0; j<cols.size(); ++j)
288 widths[j] = std::max(widths[j], cols[j].length());
291 std::string CFamilyBehavior::getIndexString() const
293 return getOwner()->getIndexString()+toString(":fb%u", getChildIndex());
296 std::string CFamilyBehavior::getOneLineInfoString() const
298 return std::string("Family behaviour '") + getName() + "'";
301 std::vector<std::string> CFamilyBehavior::getMultiLineInfoString() const
303 std::vector<std::string> container;
306 pushTitle(container, "CFamilyBehavior");
307 pushEntry(container, "id=" + getIndexString());
308 container.back() += " name=" + getName();
309 pushFooter(container);
312 return container;
315 std::string CFamilyBehavior::getManagerIndexString(const CManager *child) const
317 if (child == _ManagerNpc)
318 return getIndexString() + ":mnpc";
320 if (child == _ManagerFauna)
321 return getIndexString() + ":mfauna";
323 return getIndexString() + ":munknown";
326 void CFamilyBehavior::updateManagers()
328 // update the manager
329 // NLMEMORY::CheckHeap(true);
330 mgrNpc()->update();
331 mgrFauna()->update();
332 // NLMEMORY::CheckHeap(true);
336 void CFamilyBehavior::getNpcFlags(AITYPES::CPropertySet &flags)
338 flags = grpFamily()->getProfileProperty(string("npc"));
341 void CFamilyBehavior::getActivities (CPropertySet &food, CPropertySet &rest/*,bool &plante, const CGroupDesc<CGroupFamily>*const gd*/) const
343 food=grpFamily()->getProfileProperty(string("food"));
344 rest=grpFamily()->getProfileProperty(string("rest"));
347 extern CVariable<TGameCycle> DynamicMaxUpdatePeriod;
348 void CFamilyBehavior::update(uint32 nbTicks)
350 // calcs _UpdatePeriod to avoid pingpong problems ..
351 breakable
353 if (energyScale()==0)
355 _UpdatePeriod=1+DynamicMaxUpdatePeriod;
356 break;
359 double delta=((double)((sint32)energyScale()-(sint32)_TheoricalLevel))/((double)energyScale());
360 clamp(delta,0.0,1.0);
361 delta=1.0-delta;
362 delta*=delta; // ^3
363 delta*=delta;
364 delta*=delta;
365 _UpdatePeriod=1+(uint32)(delta*DynamicMaxUpdatePeriod);
368 CManager *Manager;
370 IAliasCont *cont0, *cont1;
371 cont0 = mgrNpc()->getAliasCont(AITypeGrp);
372 cont1 = mgrFauna()->getAliasCont(AITypeGrp);
373 Manager=(cont0->size()>cont1->size())?NLMISC::safe_cast<CManager*>(mgrNpc()):NLMISC::safe_cast<CManager*>(mgrFauna());
377 // TODO : reactivate group deletion
378 // delete group that are dead
379 while (!_GroupToDelete.empty())
381 // NLMEMORY::CheckHeap(true);
382 CGroup *const grp=(CGroup*)_GroupToDelete.back();
383 _GroupToDelete.pop_back();
384 grp->getManager().getAliasCont(AITypeGrp)->removeChildByIndex(grp->getChildIndex());
385 // NLMEMORY::CheckHeap(true);
389 // check to despawn groups that are no more valid in current energy or season
390 breakable
392 H_AUTO(FamilyDespawnGroup)
394 const IGroupDesc *gd = NULL;
395 CGroup *grp=NULL;
397 breakable
399 const uint32 nbGroups=Manager->groups().size();
401 if (nbGroups==0)
402 break;
404 grp = Manager->getGroup(CAIS::rand16(nbGroups));
405 if (!grp)
406 break;
408 CDynGrpBase *const grpDynBase=grp->getGrpDynBase();
409 #if !FINAL_VERSION
410 nlassert(grpDynBase!=NULL);
411 #endif
412 if ( grpDynBase
413 || grpDynBase->getDiscardable())
414 gd=grpDynBase->getGroupDesc();
416 break;
419 if ( !grp
420 || !gd
421 || !grp->isSpawned())
422 break;
424 // add a check if group is valid related to used regions flags to know if we need to despawn it
426 bool alreadyDespawned=false;
427 breakable
429 CGrpFauna *const grpFauna=dynamic_cast<CGrpFauna*>(grp);
430 if (!grpFauna)
431 break;
433 const CFaunaZone *faunaZone;
434 CPropertySet food, rest;
435 // bool plante;
436 getActivities (food, rest/*, plante, gd*/);
437 // {
438 //#if !FINAL_VERSION
439 // nlwarning("there is a problem with getActivities for %s", gd->getFullName().c_str());
440 //#endif
441 // break;
442 // }
444 const CAIPlace *place=grpFauna->places()[CGrpFauna::EAT_PLACE];
445 place=NLMISC::safe_cast<const CAIRefPlaceXYR *>(place)->getZone();
446 faunaZone=NLMISC::safe_cast<const CFaunaZone *>(place);
447 if (!faunaZone)
449 #if !FINAL_VERSION
450 nlassert(faunaZone);
451 #endif
452 break;
455 if (!faunaZone->haveActivity(food))
457 grp->getSpawnObj()->despawnBots(true); // not ok, despawn this group ..
458 alreadyDespawned=true;
459 break;
462 place=grpFauna->places()[CGrpFauna::EAT_PLACE];
463 place=NLMISC::safe_cast<const CAIRefPlaceXYR *>(place)->getZone();
464 faunaZone=NLMISC::safe_cast<const CFaunaZone *>(place);
465 if (!faunaZone)
467 #if !FINAL_VERSION
468 nlassert(faunaZone);
469 #endif
470 break;
473 if (!faunaZone->haveActivity(rest))
475 grp->getSpawnObj()->despawnBots(true); // not ok, despawn this group ..
476 alreadyDespawned=true;
477 break;
481 // deals with npcs dyn groups
482 breakable
484 CGroupNpc *const grpNpc=dynamic_cast<CGroupNpc *>(grp);
485 if (!grpNpc)
486 break;
488 const CNpcZone *npcZone = grpNpc->getSpawnZone();
489 CPropertySet flags;
490 getNpcFlags(flags);
492 if (!npcZone || !npcZone->properties().containsAllOf(flags))
494 // must despawn group
495 grp->getSpawnObj()->despawnBots(true); // not ok, despawn this group ..
496 alreadyDespawned=true;
497 break;
501 if (alreadyDespawned)
502 break;
504 if (gd->getWeightForEnergy(getLevelIndex())!=0)
505 break;
507 const EGSPD::CSeason::TSeason season=CTimeInterface::season();
509 if ( season<EGSPD::CSeason::Invalid // if valid season
510 && gd->isValidForSeason(season) )
511 break; // no reason to despawn
513 if (!alreadyDespawned)
514 grp->getSpawnObj()->despawnBots(false); // not ok, despawn this group ..
517 // check for spawning new group to equilibrate energy level.
518 breakable
520 H_AUTO(FamilyGroup)
521 if (_TheoricalLevel< energyScale())
523 H_AUTO(FamilyGroupeSpawn)
524 // need to spawn some group ?
525 if (_FamilyProfile)
526 _FamilyProfile->spawnGroup();
528 break;
530 // or check for despawning
532 if (_TheoricalLevel <= (energyScale()+(uint32)(0.01*(double)ENERGY_SCALE)))
533 break;
535 CGroup *grp=NULL;
536 const IGroupDesc *gd = NULL;
537 // need to despawn some group ?
540 H_AUTO(FamilyGroupDespawn)
542 // try to despawn some group in the manager
543 const uint32 start = CAIS::rand16(Manager->groups().size());
545 grp = Manager->getGroup(start);
546 if (!grp)
547 grp = Manager->groups().getNextValidChild(grp);
549 if (grp)
551 CDynGrpBase *const grpDynBase=grp->getGrpDynBase();
552 if ( grpDynBase
553 && grp->getSpawnObj()
554 && grpDynBase->getDiscardable())
556 gd=grpDynBase->getGroupDesc();
560 else
562 Manager->groups().setChildSize(start); // There's no group after start, so we can resize the group.
568 if (grp && gd)
570 H_AUTO(FamilyGroupDespawn)
572 if ((_TheoricalLevel - gd->groupEnergyValue()) >= energyScale())
574 // ok, we can despawn this group
575 grp->despawnBots(false);
583 H_AUTO(FamilyProfileUpdate);
584 CFollowPathContext fpcFamilyProfileUpdate("FamilyProfileUpdate");
586 // update the family profile (if any)
587 if (_FamilyProfile)
588 _FamilyProfile->update();
593 void CFamilyBehavior::fillOutpostNames(std::vector<NLMISC::TStringId> outpostNames)
595 if (_FamilyProfile)
596 _FamilyProfile->fillOutpostNames(outpostNames);
600 void CFamilyBehavior::outpostAdd(NLMISC::TStringId outpostName)
602 if (_FamilyProfile)
603 _FamilyProfile->outpostAdd(outpostName);
605 void CFamilyBehavior::outpostRemove(NLMISC::TStringId outpostName)
607 if (_FamilyProfile)
608 _FamilyProfile->outpostRemove(outpostName);
611 void CFamilyBehavior::outpostEvent(NLMISC::TStringId outpostName, ZCSTATE::TZcState state)
613 if (_FamilyProfile)
614 _FamilyProfile->outpostEvent(outpostName,state);
617 void CFamilyBehavior::spawnBoss(NLMISC::TStringId outpostName)
619 if (_FamilyProfile)
620 _FamilyProfile->spawnBoss(outpostName);
624 void CFamilyBehavior::groupDead(CGroup *grp)
626 #ifdef NL_DEBUG
627 for (uint32 i=0;i<_GroupToDelete.size();i++)
629 nlassert(_GroupToDelete[i].ptr() != grp);
631 #endif
632 // ok, we can delete this group
633 _GroupToDelete.push_back(grp);
636 void CFamilyBehavior::addEnergy (uint32 energy)
638 _CurrentLevel += energy;
641 void CFamilyBehavior::removeEnergy (uint32 energy)
643 #ifdef NL_DEBUG
644 nlassert(_CurrentLevel>=energy);
645 #endif
646 _CurrentLevel -= energy;
650 void CFamilyBehavior::serviceEvent (const CServiceEvent &info)
652 mgrNpc()->serviceEvent (info);
653 mgrFauna()->serviceEvent (info);
657 CGroupNpc *CFamilyBehavior::createNpcGroup(const CNpcZone *const zone, const CGroupDesc<CGroupFamily> *const groupDesc)
659 // const TPopulationFamily &family=getFamily()
660 // if ( family.FamilyTag == family_fauna_herbivore
661 // || family.FamilyTag == family_fauna_carnivore
662 // || family.FamilyTag == family_flora
663 // || family.FamilyTag == family_kitin
664 // || family.FamilyTag == family_kitin_invasion
665 // || family.FamilyTag == family_degen
666 // || family.FamilyTag == family_goo )
667 // {
668 // nlwarning("CRegion::createGroup can't create a npc group for family '%s', energy level %f in region '%s'", family.getFamilyName().c_str(), effectiveLevel(), getOwner()->getOwner()->getAliasFullName().c_str());
669 // return NULL;
670 // }
672 CGroupNpc *grp=groupDesc->createNpcGroup (mgrNpc(), zone->midPos());
674 if (grp)
676 grp->initDynGrp (groupDesc, this);
677 grp->setSpawnZone(zone);
679 return grp;
682 bool CFamilyBehavior::spawn()
684 // Spawn dyn NPCs
685 _ManagerNpc->spawn();
686 // Spawn dyn fauna
687 _ManagerFauna->spawn();
688 // We should check individual errors, but fake success here :)
689 return true;
692 bool CFamilyBehavior::despawn()
694 // Despawn dyn NPCs
695 _ManagerNpc->despawnMgr();
696 // Despawn dyn fauna
697 _ManagerFauna->despawnMgr();
698 // We should check individual errors, but fake success here :)
699 return true;