Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / ai_generic_fight.cpp
blobeeec81ffc2d2852b46a664ecf7529e96495f0531
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 "ai_generic_fight.h"
21 #include "ai_player.h"
22 #include "server_share/msg_brick_service.h"
23 #include "ai_profile_npc.h"
25 extern bool simulateBug(int bugId);
27 RYZOMID::TTypeId ai_generic_fight_debugCheckedType = RYZOMID::unknown;
28 #define debugCheckedType ai_generic_fight_debugCheckedType
30 RYAI_REGISTER_PROFILE_FACTORY(CBotProfileFleeFactory, "bot_flee");
31 RYAI_REGISTER_PROFILE_FACTORY(CBotProfileFightFactory, "bot_fight");
33 using namespace NLMISC;
34 //using namespace std;
36 std::string CBotProfileFlee::getOneLineInfoString() const
38 return std::string("flee profile: bot="+_Bot->dataSetRow().toString()+" "+CMirrors::getEntityId(_Bot->dataSetRow()).toString());
41 std::string CBotProfileFight::getOneLineInfoString() const
43 return std::string("fight profile: bot="+_Bot->dataSetRow().toString()+" ennemy="+_Ennemy->dataSetRow().toString()+", Hitting: "+toString(isHitting())+" SearchPath: "+toString(_SearchAlternativePath));
46 std::string CBotProfileHeal::getOneLineInfoString() const
48 return std::string("heal profile: bot="+_Row.toString()+" "+CMirrors::getEntityId(_Row).toString()+", Hitting: "+toString(isHitting())+" SearchPath: "+toString(_SearchAlternativePath));
51 //////////////////////////////////////////////////////////////////////////////
52 // CBotProfileFight //
53 //////////////////////////////////////////////////////////////////////////////
55 void CBotProfileFight::beginProfile()
57 _Engaged = false;
58 _Bot->setTarget(_Ennemy);
59 eventBeginFight ();
62 void CBotProfileFight::resumeProfile()
64 _Engaged = false;
65 _Bot->setTarget(_Ennemy);
68 CBotProfileFight::CBotProfileFight(CProfileOwner* owner, CAIEntityPhysical* ennemy)
69 : CBotProfileFightHeal()
70 , _Bot(NLMISC::safe_cast<CSpawnBot*>(owner))
71 , _Ennemy(ennemy)
72 , _PathPos(NLMISC::safe_cast<CSpawnBot*>(owner)->theta())
73 , _PathCont(NLMISC::safe_cast<CSpawnBot*>(owner)->getAStarFlag())
74 , _RangeCalculated(false)
75 , _SearchAlternativePath(false)
77 #ifdef NL_DEBUG_PTR
78 _Bot.setData(this);
79 #endif
82 CBotProfileFight::~CBotProfileFight()
84 if ( _Bot.isNULL()
85 || _Bot->isAlive())
86 return;
88 AISHEETS::ICreature::TScriptCompList const& scriptList = _Bot->getPersistent().getSheet()->DeathScriptList();
89 FOREACHC(it, AISHEETS::ICreature::TScriptCompList, scriptList)
90 (*it)->update(*_Bot);
93 float CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_MAX] = { 0.5f, 80.f, 80.f, 80.f };
94 float CBotProfileFightHeal::fightDefaultMinRange = 0.01f;
95 float CBotProfileFightHeal::fightDefaultMaxRange = 0.5f;
96 float CBotProfileFightHeal::fightMeleeMinRange = 0.01f;
97 float CBotProfileFightHeal::fightMeleeMaxRange = 0.5f;
98 float CBotProfileFightHeal::fightRangeMinRange = 0.4f;
99 float CBotProfileFightHeal::fightRangeMaxRange = 80.f;
100 float CBotProfileFightHeal::fightMixedMinRange = 0.01f;
101 float CBotProfileFightHeal::fightMixedMaxRange = 45.f;
102 float CBotProfileFightHeal::giveUpDistance = 100.f;
103 bool CBotProfileFightHeal::fleeUnreachableTargets = true;
106 NLMISC_COMMAND(fightMeleeDist, "Generic fight melee attack range","")
108 if(args.size()>1) return false;
109 if(args.size()==1) CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_MELEE] = (float)atof(args[0].c_str());
110 log.displayNL("Melee fight range is %f", CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_MELEE]);
111 return true;
113 NLMISC_COMMAND(fightRangeDist, "Generic fight range attack range","")
115 if(args.size()>1) return false;
116 if(args.size()==1) CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_RANGE] = (float)atof(args[0].c_str());
117 log.displayNL("Range fight range is %f", CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_RANGE]);
118 return true;
120 NLMISC_COMMAND(fightNukeDist, "Generic fight nuke attack range","")
122 if(args.size()>1) return false;
123 if(args.size()==1) CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_NUKE] = (float)atof(args[0].c_str());
124 log.displayNL("Nuke fight range is %f", CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_NUKE]);
125 return true;
127 NLMISC_COMMAND(fightHealDist, "Generic fight heal range","")
129 if(args.size()>1) return false;
130 if(args.size()==1) CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_HEAL] = (float)atof(args[0].c_str());
131 log.displayNL("Heal fight range is %f", CBotProfileFightHeal::fightDists[AISHEETS::FIGHTCFG_HEAL]);
132 return true;
134 NLMISC_COMMAND(fightDefaultRange, "Generic default fight range","")
136 if (args.size()==0 || args.size()==2)
138 if (args.size()==2)
140 NLMISC::fromString(args[0], CBotProfileFightHeal::fightDefaultMinRange);
141 NLMISC::fromString(args[1], CBotProfileFightHeal::fightDefaultMaxRange);
143 log.displayNL("Generic default fight range is [%f;%f]", CBotProfileFightHeal::fightDefaultMinRange, CBotProfileFightHeal::fightDefaultMaxRange);
144 return true;
146 return false;
148 NLMISC_COMMAND(fightMeleeRange, "Generic melee fight range","")
150 if (args.size()==0 || args.size()==2)
152 if (args.size()==2)
154 CBotProfileFightHeal::fightMeleeMinRange = (float)atof(args[0].c_str());
155 CBotProfileFightHeal::fightMeleeMaxRange = (float)atof(args[1].c_str());
157 log.displayNL("Generic melee fight range is [%f;%f]", CBotProfileFightHeal::fightMeleeMinRange, CBotProfileFightHeal::fightMeleeMaxRange);
158 return true;
160 return false;
162 NLMISC_COMMAND(fightRangeRange, "Generic range fight range","")
164 if (args.size()==0 || args.size()==2)
166 if (args.size()==2)
168 CBotProfileFightHeal::fightRangeMinRange = (float)atof(args[0].c_str());
169 CBotProfileFightHeal::fightRangeMaxRange = (float)atof(args[1].c_str());
171 log.displayNL("Generic range fight range is [%f;%f]", CBotProfileFightHeal::fightRangeMinRange, CBotProfileFightHeal::fightRangeMaxRange);
172 return true;
174 return false;
176 NLMISC_COMMAND(fightMixedRange, "Generic mixed fight range","")
178 if (args.size()==0 || args.size()==2)
180 if (args.size()==2)
182 CBotProfileFightHeal::fightMixedMinRange = (float)atof(args[0].c_str());
183 CBotProfileFightHeal::fightMixedMaxRange = (float)atof(args[1].c_str());
185 log.displayNL("Generic mixed fight range is [%f;%f]", CBotProfileFightHeal::fightMixedMinRange, CBotProfileFightHeal::fightMixedMaxRange);
186 return true;
188 return false;
190 NLMISC_COMMAND(fightGiveUpDistance, "Generic fight give up distance","")
192 if(args.size()>1) return false;
193 if(args.size()==1) CBotProfileFightHeal::giveUpDistance = (float)atof(args[0].c_str());
194 log.displayNL("Give up distance is %f", CBotProfileFightHeal::giveUpDistance);
195 return true;
197 NLMISC_COMMAND(fleeUnreachableTargets, "Tells if creatures flee an unreachable player","")
199 if(args.size()>1) return false;
200 if(args.size()==1)
202 bool b;
203 fromString(args[0], b);
204 CBotProfileFightHeal::fleeUnreachableTargets = b;
206 log.displayNL("Creatures do%s flee unreachable targets", CBotProfileFightHeal::fleeUnreachableTargets?"":" not");
207 return true;
210 //////////////////////////////////////////////////////////////////////////////
211 // CBotProfileHeal //
212 //////////////////////////////////////////////////////////////////////////////
214 void CBotProfileHeal::beginProfile()
216 _Engaged=false;
217 _Bot->setTarget(CAIS::instance().getEntityPhysical(_Row));
218 // eventBeginFight(); // :TODO: Reactivate this
221 void CBotProfileHeal::resumeProfile()
223 _Engaged=false;
224 _Bot->setTarget(CAIS::instance().getEntityPhysical(_Row));
227 CBotProfileHeal::CBotProfileHeal(const TDataSetRow &row, CProfileOwner *owner)
228 : CBotProfileFightHeal()
229 , _Bot(NLMISC::safe_cast<CSpawnBot*>(owner))
230 , _PathPos(NLMISC::safe_cast<CSpawnBot*>(owner)->theta())
231 , _PathCont(NLMISC::safe_cast<CSpawnBot*>(owner)->getAStarFlag())
232 , _Row(row)
233 , _RangeCalculated(false)
234 , _SearchAlternativePath(false)
238 CBotProfileHeal::~CBotProfileHeal()
240 if ( _Bot.isNULL()
241 || _Bot->isAlive())
242 return;
244 AISHEETS::ICreature::TScriptCompList const& scriptList = _Bot->getPersistent().getSheet()->DeathScriptList();
245 for (AISHEETS::ICreature::TScriptCompList::const_iterator it=scriptList.begin(), itEnd=scriptList.end(); it!=itEnd; ++it)
246 (*it)->update(*_Bot);
249 //////////////////////////////////////////////////////////////////////////////
250 // CBotProfileFlee //
251 //////////////////////////////////////////////////////////////////////////////
253 CBotProfileFlee::CBotProfileFlee(CProfileOwner *owner)
254 : CAIBaseProfile()
255 , _DenyFlags(NLMISC::safe_cast<CSpawnBot*>(owner)->getAStarFlag())
256 , _PathPos(NLMISC::safe_cast<CSpawnBot*>(owner)->theta())
257 , _fightFleePathContainer(NLMISC::safe_cast<CSpawnBot*>(owner)->getAStarFlag())
258 , _Bot(NLMISC::safe_cast<CSpawnBot*>(owner))
262 CBotProfileFlee::~CBotProfileFlee()
266 void CBotProfileFlee::beginProfile()
268 _LastDir = RYAI_MAP_CRUNCH::CDirection(RYAI_MAP_CRUNCH::CDirection::UNDEFINED);
271 void CBotProfileFlee::updateProfile(uint ticksSinceLastUpdate)
273 H_AUTO(BotFleeProfileUpdate);
274 CFollowPathContext fpcBotFleeProfileUpdate("BotFleeProfileUpdate");
276 if (!_Bot->getUnreachableTarget().isNULL())
278 CAIVector delta = CAIVector(_Bot->getUnreachableTarget()->aipos());
279 delta -= CAIVector(_Bot->pos());
280 if (delta.quickNorm()>giveUpDistanceUnreachable)
281 _Bot->setUnreachableTarget((CAIEntityPhysical*)NULL);
283 if (!_Bot->canMove())
284 return;
286 bool calcDone=true;
288 CAIVector fleeVector(_Bot->moveDecalage());
289 _Bot->resetDecalage();
290 if (fleeVector.isNull())
291 fleeVector.setX(1+fleeVector.x()); // hum ..
292 RYAI_MAP_CRUNCH::CDirection startDir(fleeVector.x(), fleeVector.y(), true);
294 // if we need to change our destination.
295 if ( startDir!=_LastDir
296 || !_LastStartPos.hasSameFullCellId(_Bot->wpos()))
298 const RYAI_MAP_CRUNCH::CWorldMap &worldMap=CWorldContainer::getWorldMap();
299 calcDone=false;
301 for (sint nbStep=0;nbStep<8;nbStep++)
303 // try to find a direction around startDir.
304 RYAI_MAP_CRUNCH::CDirection dir(startDir);
305 dir.addStep((RYAI_MAP_CRUNCH::CDirection::TDeltaDirection) ((nbStep&1)?(nbStep>>1):(-(nbStep>>1))));
307 const RYAI_MAP_CRUNCH::CRootCell *rootCell=worldMap.getRootCellCst(_Bot->wpos().stepCell(dir.dx(),dir.dy()));
308 if (rootCell)
310 RYAI_MAP_CRUNCH::CWorldPosition wpos=rootCell->getWorldPosition(_Bot->getPersistent().getChildIndex()&3);
311 if ( wpos.isValid()
312 && (wpos.getFlags()&_DenyFlags)==0 ) // verify that we got some compatible flags ..
314 _LastDir=startDir;
315 _LastStartPos=_Bot->wpos();
317 calcDone=true;
318 _fightFleePathContainer.setDestination(/*AITYPES::vp_auto, */wpos);
319 break;
328 // if we found somewhere to go, then go there ..
329 if (calcDone)
331 float dist=_Bot->runSpeed()*ticksSinceLastUpdate;
332 CFollowPath::TFollowStatus const status = CFollowPath::getInstance()->followPath(
333 _Bot,
334 _PathPos,
335 _fightFleePathContainer,
336 dist,
337 dist*.71f,
338 .5f);
339 if (status==CFollowPath::FOLLOW_NO_PATH)
341 // :KLUDGE: Warning has been removed to avoid flooding, without solving the problem
342 // nlwarning("Flee problem with destination properties (Water, Nogo)");
343 // :TODO: Rework that case
344 _LastDir=RYAI_MAP_CRUNCH::CDirection(RYAI_MAP_CRUNCH::CDirection::UNDEFINED);
347 else
349 _LastDir=RYAI_MAP_CRUNCH::CDirection(RYAI_MAP_CRUNCH::CDirection::UNDEFINED);
354 float CBotProfileFlee::giveUpDistanceUnreachable = 75.f;
356 NLMISC_COMMAND(fleeGiveUpDistanceUnreachable, "Generic flee give up distance when fleeing an unreachable player","")
358 if(args.size()>1) return false;
359 if(args.size()==1) CBotProfileFlee::giveUpDistanceUnreachable = (float)atof(args[0].c_str());
360 log.displayNL("Give up distance is %f", CBotProfileFlee::giveUpDistanceUnreachable);
361 return true;
364 //////////////////////////////////////////////////////////////////////////////
365 // CFightOrganizer //
366 //////////////////////////////////////////////////////////////////////////////
368 CFightOrganizer::CFightOrganizer()
369 : _HaveEnnemy(true)
373 bool CFightOrganizer::healIteration(CBot* bot, CBot* otherBot)
375 if (bot && otherBot)
377 CSpawnBot* spBot = bot->getSpawnObj();
378 CSpawnBot* otherSpBot = otherBot->getSpawnObj();
379 if (spBot && otherSpBot && otherSpBot->isAlive())
381 float hp = otherSpBot->hpPercentage();
382 int neededHealers = 0;
383 if (hp<.90f) ++neededHealers;
384 if (hp<.75f) ++neededHealers;
385 if (hp<.50f) ++neededHealers;
386 if (hp<.25f) ++neededHealers;
387 if (neededHealers > otherSpBot->getHealerCount())
389 IAIProfile* profile = spBot->getAIProfile();
390 AITYPES::TProfiles profileType = profile?profile->getAIProfileType():AITYPES::BAD_TYPE;
391 if (profileType!=AITYPES::BOT_HEAL)
392 setHeal(spBot, otherSpBot);
393 spBot->setTarget(otherSpBot);
394 return true;
398 return false;
401 bool CFightOrganizer::reorganizeIteration(CBot* bot)
403 CSpawnBot *spawnBot=bot->getSpawnObj();
404 if ( !spawnBot
405 || !spawnBot->isAlive ())
406 return true;
408 IAIProfile *profile=spawnBot->getAIProfile();
409 AITYPES::TProfiles profileType=profile?profile->getAIProfileType():AITYPES::BAD_TYPE;
411 // special comp if feared bypass every other comp .. (panic mode !)
412 if (spawnBot->isFeared())
414 CAIVector fleeVect;
415 CAIEntityPhysical* aggroer = spawnBot->firstVisualTargeter();
416 while (aggroer!=NULL)
418 CAIVector delta(spawnBot->aipos());
419 delta -= aggroer->aipos();
420 fleeVect += delta;
421 aggroer = aggroer->nextTargeter();
424 CAIS& inst = CAIS::instance();
426 FOREACHC(itEntry, CBotAggroOwner::TBotAggroList, spawnBot->getBotAggroList())
428 CAIEntityPhysical* const phys = inst.getEntityPhysical(itEntry->second->getBot());
429 if (!phys)
430 continue;
431 CAIVector delta(spawnBot->aipos());
432 delta -= phys->aipos();
433 fleeVect += delta;
436 fleeVect.normalize(1000);
437 CAIVector toGroup = spawnBot->spawnGrp().getCenterPos();
438 toGroup -= spawnBot->aipos();
439 toGroup.normalize(1000);
440 fleeVect += toGroup;
441 // :TODO: Uncomment following line and test extensively (may reduce flee speed or dist, but is more correct)
442 // toGroup.normalize(1000);
443 setFlee(spawnBot, fleeVect);
444 return true;
446 // special comp if healer bypass every other comp
447 if (profileType==AITYPES::BOT_HEAL)
449 CBotProfileHeal* healProfile = NLMISC::safe_cast<CBotProfileHeal*>(profile);
450 if (healProfile->isHitting())
452 _HaveEnnemy = true;
453 return true;
456 if (bot->isHealer())
458 CGroup* group = bot->getOwner();
459 CBot* otherBot = NULL;
460 CSpawnBot* otherSp = NULL;
461 // Heal leader
462 if (spawnBot->canHeal() && healIteration(bot, group->getSquadLeader(true)))
463 return true;
464 // Heal self
465 if (spawnBot->canSelfHeal() && healIteration(bot, bot))
466 return true;
467 // Heal healers
468 if (spawnBot->canHeal())
470 FOREACH(itBot, CCont<CBot>, group->bots())
472 if (*itBot && bot!=*itBot && (*itBot)->isHealer())
473 if (healIteration(bot, *itBot))
474 return true;
477 // Heal others
478 FOREACH(itBot, CCont<CBot>, group->bots())
480 if (*itBot && bot!=*itBot)
481 if (healIteration(bot, *itBot))
482 return true;
485 // Heal others groups
486 CGroupNpc* npcGroup = NLMISC::safe_cast<CGroupNpc*>(group);
487 if (npcGroup) {
488 std::vector<CGroupNpc*> grps = npcGroup->getHealGroups();
489 for (uint i=0; i<grps.size(); ++i)
491 FOREACH(itBot, CCont<CBot>, grps[i]->bots())
493 if (*itBot && bot!=*itBot)
494 if (healIteration(bot, *itBot)) {
495 return true;
503 if (profileType==AITYPES::BOT_FIGHT)
505 CBotProfileFight *fightProfile=NLMISC::safe_cast<CBotProfileFight*>(profile);
506 if (fightProfile->isHitting())
508 _HaveEnnemy=true;
509 return true;
514 std::vector<CAIEntityPhysical*> botList;
515 AISHEETS::ICreatureCPtr botSheet = bot->getSheet();
517 float grpAggroCoef = 0.5f*botSheet->GroupCohesionModulator();
519 spawnBot->updateListAndMarkBot(botList, 1.f-grpAggroCoef);
521 // unmarkBot list and choose target.
522 double BestChooseScore=0; //botSheet.ScoreModulator;
524 CAIEntityPhysical * ennemy = NULL;
525 CAIEntityPhysical const * fleeEnnemy = NULL;
526 double BestFleeScore=0; //botSheet.FearModulator;
528 CAIVector movingVector;
529 double fear=1.0f;
531 CAIEntityPhysical* target = (CAIEntityPhysical*)spawnBot->getTarget();
533 FOREACH(it, std::vector<CAIEntityPhysical*>, botList)
535 CAIEntityPhysical *const entity=(*it);
537 if (!entity->isAlive())
539 if (ai_profile_npc_VerboseLog)
541 nldebug("<FIGHT>Entity %s have aggro for dead entity %s, forgetting it.", spawnBot->getEntityId().toString().c_str(), entity->getEntityId().toString().c_str());
544 spawnBot->forgetAggroFor(entity->dataSetRow());
545 continue;
547 if (!entity->isBotAttackable())
549 if (ai_profile_npc_VerboseLog)
551 nldebug("<FIGHT>Entity %s have aggro for non bot attackable entity %s, forgetting it.", spawnBot->getEntityId().toString().c_str(), entity->getEntityId().toString().c_str());
554 spawnBot->forgetAggroFor(entity->dataSetRow());
555 continue;
558 // is there a problem.
559 if (entity->_AggroScore>0)
561 CAIVector targetToPos(spawnBot->aipos());
562 targetToPos -= entity->aipos();
564 double slotCoef;
566 // 1 near - 0 far.
567 slotCoef = 1.f/(1.f+targetToPos.quickNorm()*botSheet->DistModulator()); // melee consideration. (don't know correct dist for caster or range may be in munition sheet !?).
569 if (((CAIEntityPhysical*)entity->getTarget())!=spawnBot)
571 int nbOtherTargeter = entity->targeterCount();
572 if (entity==((CAIEntityPhysical*)spawnBot->getTarget()))
573 --nbOtherTargeter;
574 float targetCoef = 1.f/(1.f+nbOtherTargeter*nbOtherTargeter*botSheet->TargetModulator());
575 slotCoef *= targetCoef;
577 slotCoef *= entity->getFreeFightSpaceRatio();
580 //////////////////////////////////////////////////////////////////////////////
582 float score = (float)(entity->_AggroScore*slotCoef);
584 if (target && target->getRyzomType() == RYZOMID::player)
586 if (entity != target)
588 CBotPlayer const* const ptarget = NLMISC::safe_cast<CBotPlayer const*>(target);
589 if (entity->getRyzomType() == RYZOMID::player)
591 CBotPlayer const* const player = NLMISC::safe_cast<CBotPlayer const*>(entity);
592 if ( ptarget && player && spawnBot->getAggroFor(entity->dataSetRow()) <= spawnBot->getAggroFor(ptarget->dataSetRow()) && (
593 ptarget->getCurrentTeamId() == CTEAM::InvalidTeamId ||
594 player->getCurrentTeamId() == CTEAM::InvalidTeamId ||
595 player->getCurrentTeamId() != ptarget->getCurrentTeamId()
599 score = 0;
605 if (score>=BestChooseScore) // add distance and bot profile compatibility.
607 BestChooseScore=score;
608 ennemy=entity;
611 entity->_ChooseLastTime = std::numeric_limits<uint32>::max();
614 if (fleeEnnemy==NULL && !spawnBot->getUnreachableTarget().isNULL())
616 fleeEnnemy = spawnBot->getUnreachableTarget();
619 if (ennemy)
621 _HaveEnnemy=true;
622 nlassert(ennemy->getRyzomType()!=debugCheckedType);
623 if (target == ennemy)
625 return true;
628 // set the correct profile, if its not the case, otherwise, just change the target.
629 if (profileType!=AITYPES::BOT_FIGHT)
631 setFight(spawnBot, ennemy);
633 spawnBot->setTarget(ennemy);
635 else if (fleeEnnemy)
637 CAIVector fleeVect(spawnBot->aipos());
638 fleeVect -= fleeEnnemy->aipos();
639 // :TODO: Check if following block is wanted by game design
641 CAIVector toGroup = spawnBot->spawnGrp().getCenterPos();
642 toGroup -= spawnBot->aipos();
643 fleeVect += toGroup;
645 fleeVect.normalize(1000);
646 setFlee(spawnBot, fleeVect);
648 else if (spawnBot->isReturning())
650 _HaveEnnemy=true;
651 setReturnAfterFight(spawnBot);
653 else
655 setNoFight(spawnBot);
657 return true;
660 //////////////////////////////////////////////////////////////////////////////
661 // CBotProfileReturnAfterFight //
662 //////////////////////////////////////////////////////////////////////////////
664 CBotProfileReturnAfterFight::CBotProfileReturnAfterFight(CProfileOwner* owner)
665 : CAIBaseProfile()
666 //, _PathCont(NLMISC::safe_cast<CSpawnBot*>(owner)->getPersistent().getOwner()->getAStarFlag())
668 _Bot = static_cast<CSpawnBot*>(owner);
669 // PROFILE_LOG("bot", "return_after_fight", "ctor", "");
670 // CSpawnBot* spawnBot = static_cast<CSpawnBot*>(owner);
671 // _PathCont.setDestination(spawnBot->getReturnPos());
672 // _MoveProfile = new CBotProfileFollowPos(&_PathCont, owner);
675 CBotProfileReturnAfterFight::~CBotProfileReturnAfterFight()
677 // PROFILE_LOG("bot", "return_after_fight", "dtor", "");
680 void CBotProfileReturnAfterFight::beginProfile()
682 // PROFILE_LOG("bot", "return_after_fight", "begin", "");
683 // _MoveProfile->beginProfile();
684 _Bot->ignoreReturnAggro(true);
687 void CBotProfileReturnAfterFight::endProfile()
689 // PROFILE_LOG("bot", "return_after_fight", "end", "");
690 // _MoveProfile->endProfile();
691 _Bot->ignoreReturnAggro(false);
694 void CBotProfileReturnAfterFight::stateChangeProfile()
696 // _MoveProfile->stateChangeProfile();
699 void CBotProfileReturnAfterFight::updateProfile(uint ticksSinceLastUpdate)
701 H_AUTO(CBotProfileReturnAfterFightUpdate);
702 // _MoveProfile->updateProfile(ticksSinceLastUpdate);
705 std::string CBotProfileReturnAfterFight::getOneLineInfoString() const
707 std::string info = "return_after_fight bot profile";
708 // info += " (";
709 // info += _MoveProfile?_MoveProfile->getOneLineInfoString():std::string("<no move profile>");
710 // info += ")";
711 return info;