Resolve "Toggle Free Look with Hotkey"
[ryzomcore.git] / ryzom / server / src / ai_service / ai_player.cpp
blob3870cfa60bca3154e37e3bd4dbe6bb0ec2504ed3
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_player.h"
21 #include "ai_bot_fauna.h"
22 #include "ai_bot_npc.h"
24 using namespace MULTI_LINE_FORMATER;
27 //////////////////////////////////////////////////////////////////////////////
28 // CBotPlayer //
29 //////////////////////////////////////////////////////////////////////////////
31 CBotPlayer::CBotPlayer(CManagerPlayer* owner, TDataSetRow const& DataSetRow, NLMISC::CEntityId const& id, uint32 level)
32 : CChild<CManagerPlayer>(owner)
33 , CAIEntityPhysical(static_cast<CPersistentOfPhysical&>(*this), DataSetRow, id, 0.5f, level, RYAI_MAP_CRUNCH::Nothing)
34 , _CurrentTeamId(CTEAM::InvalidTeamId)
35 , _FollowMode(false)
36 , _Aggroable(true)
38 #ifdef NL_DEBUG
39 nlassert(owner->playerList().find(dataSetRow())==owner->playerList().end());
40 #endif
41 owner->playerList().insert(std::make_pair(dataSetRow(), this));
42 NLMISC::CSheetId sheetId = CMirrors::sheet(DataSetRow);
43 _Sheet = AISHEETS::CSheets::getInstance()->lookupRaceStats(sheetId);
46 CBotPlayer::~CBotPlayer()
48 getOwner()->playerList().erase(dataSetRow());
49 if (isSpawned())
51 despawnBot();
55 std::string CBotPlayer::getIndexString() const
57 return getOwner()->getIndexString()+NLMISC::toString(":p%u", getChildIndex());
60 std::string CBotPlayer::getEntityIdString() const
62 return getEntityId().toString() ;
65 std::string CBotPlayer::getOneLineInfoString() const
67 return std::string("Player '") + getEntityId().toString() + "'";
70 std::vector<std::string> CBotPlayer::getMultiLineInfoString() const
72 std::vector<std::string> container;
75 pushTitle(container, "CBotPlayer");
76 pushEntry(container, "id=" + getIndexString());
77 container.back() += " eid=" + getEntityIdString();
78 container.back() += " teamid=" + NLMISC::toString("%u", _CurrentTeamId);
79 pushFooter(container);
82 return container;
85 void CBotPlayer::processEvent(CCombatInterface::CEvent const& event)
87 // if heal happends, dispatch aggro on targetters.
88 if (event._nature==ACTNATURE::CURATIVE_MAGIC && event._weight>=0)
90 float aggro = -event._weight;
91 if (aggro>-0.5f)
92 aggro = -0.5f;
93 else if (aggro<-1.f)
94 aggro = -1.f;
96 CAIEntityPhysical const* const targetBot = CAIS::instance().getEntityPhysical(event._targetRow);
98 if (targetBot)
100 CAIEntityPhysical* targeter = targetBot->firstTargeter();
101 while (targeter)
103 if (targeter->dataSetRow()!=event._originatorRow)
105 switch (targeter->getRyzomType())
107 case RYZOMID::creature:
109 CSpawnBotFauna* const fauna = NLMISC::safe_cast<CSpawnBotFauna*>(targeter);
110 fauna->addAggroFor(event._originatorRow, aggro, true);
112 break;
113 case RYZOMID::npc:
115 CSpawnBotNpc* const npc = NLMISC::safe_cast<CSpawnBotNpc*>(targeter);
116 npc->addAggroFor(event._originatorRow, aggro, true);
118 break;
119 default:
120 break;
123 targeter = targeter->nextTargeter();
129 void CBotPlayer::updatePos()
131 RYAI_MAP_CRUNCH::CWorldPosition wpos;
132 if (!CWorldContainer::getWorldMap().setWorldPosition(pos().h(), wpos,CAIVector(pos())))
134 _PlayerPosIsInvalid = true;
135 return;
137 _PlayerPosIsInvalid = false;
138 setWPos(wpos);
139 linkEntityToMatrix(this->pos(),getOwner()->getOwner()->playerMatrix());
141 if (wpos.getFlags()&RYAI_MAP_CRUNCH::Water)
142 setActionFlags(RYZOMACTIONFLAGS::InWater);
143 else
144 removeActionFlags(RYZOMACTIONFLAGS::InWater);
147 CAIPos CBotPlayer::aipos() const
149 if (_PlayerPosIsInvalid)
150 return CAIPos(wpos().toAIVector(), wpos().h(), 0); // This is last valid position on AI collision map
151 else
152 return CAIPos(pos());
155 void CBotPlayer::update()
157 updatePos();
160 CAIInstance* CBotPlayer::getAIInstance() const
162 return getOwner()->getAIInstance();
165 bool CBotPlayer::spawn()
167 setSpawn(this);
168 return true;
171 void CBotPlayer::despawnBot()
173 setSpawn(NULL);
176 bool CBotPlayer::isUnReachable() const
178 // _PlayerPosIsInvalid does not reflect the fact that the player is unreachable
179 // _PlayerPosIsInvalid is true when player is in delta between PACS and WorldMap
180 // collisions, and is reachable in those cases if the bot is near
181 if (useOldUnreachable)
183 return _PlayerPosIsInvalid;
185 else
187 return false;
191 bool CBotPlayer::setPos(CAIPos const& pos)
193 #ifdef NL_DEBUG
194 nlassert(1==0);
195 #endif
196 return true;
199 float CBotPlayer::walkSpeed() const
201 nlerror("Non-virtual overriden function walkSpeed in CBotPlayer");
202 return 3.f/10.f;
205 float CBotPlayer::runSpeed() const
207 nlerror("Non-virtual overriden function runSpeed in CBotPlayer");
208 return 6.f/10.f;
211 bool CBotPlayer::isAggressive() const
213 MBEHAV::TMode const mode = getMode();
214 return mode==MBEHAV::COMBAT_FLOAT || mode==MBEHAV::COMBAT;
217 void CBotPlayer::addAggroer(TDataSetRow const& row)
219 #if !FINAL_VERSION
220 for (sint32 i=(sint32)_AggroerList.size()-1;i>=0;i--)
221 nlassert(_AggroerList[i]!=row);
222 #endif
223 _AggroerList.push_back(row);
226 void CBotPlayer::removeAggroer(TDataSetRow const& row)
228 for (sint32 i=(sint32)_AggroerList.size()-1;i>=0;i--)
230 if (_AggroerList[i]==row)
232 _AggroerList.at(i)=_AggroerList.back();
233 _AggroerList.pop_back();
234 break;
239 void CBotPlayer::updateInsideTriggerZones(const std::set<uint32>& newInsideTriggerZone, std::vector<uint32>& onEnterZone, std::vector<uint32>& onLeaveZone)
241 std::set<uint32>::const_iterator firstInside(_InsideTriggerZones.begin()), lastInside( _InsideTriggerZones.end());
242 std::set<uint32>::const_iterator firstNewInside(newInsideTriggerZone.begin()), lastNewInside( newInsideTriggerZone.end());
244 std::set_difference(firstInside, lastInside, firstNewInside, lastNewInside, std::back_inserter(onLeaveZone));
245 std::set_difference(firstNewInside, lastNewInside, firstInside, lastInside, std::back_inserter(onEnterZone));
247 _InsideTriggerZones = newInsideTriggerZone;
250 //////////////////////////////////////////////////////////////////////////////
251 // CManagerPlayer //
252 //////////////////////////////////////////////////////////////////////////////
254 CManagerPlayer::~CManagerPlayer()
256 TPlayerMap::iterator it = _spawnedPlayers.begin();
257 while (it != _spawnedPlayers.end())
259 CBotPlayer* player = (*it).second;
261 // a CBotPlayer object removes itself from _spawnedPlayers at destruction
262 // increment the iterator before it becomes invalid
263 ++it;
264 player->despawnBot();
265 removeChildByIndex(player->getChildIndex());
266 // now the player object is destroyed
270 void CManagerPlayer::update()
272 FOREACH(it, TPlayerMap, _spawnedPlayers)
274 it->second->CBotPlayer::update();
278 void CManagerPlayer::addSpawnedPlayer(TDataSetRow const& dataSetRow, NLMISC::CEntityId const& id)
280 CBotPlayer* player = new CBotPlayer(this,dataSetRow,id,1); // :TODO: default player level calculation (skill & hp ?).
281 addChild(player);
282 player->spawn();
284 player->linkToWorldMap(player, player->pos(), getOwner()->playerMatrix());
285 player->updatePos();
287 // update team id and composition
288 CMirrorPropValueRO<TYPE_TEAM_ID> value( *CMirrors::DataSet, dataSetRow, DSPropertyTEAM_ID );
289 player->setCurrentTeamId(value());
290 if (value() != CTEAM::InvalidTeamId)
292 _teams[value()].insert(dataSetRow);
296 void CManagerPlayer::removeDespawnedPlayer(TDataSetRow const& dataSetRow)
298 // Remove player from Manager.
299 TPlayerMap::iterator it = _spawnedPlayers.find(dataSetRow);
300 if (it==_spawnedPlayers.end())
302 // need to log some warning
303 nlwarning("Player Despawn Error");
304 #ifdef NL_DEBUG
305 nlerror("Player Despawn Error");
306 #endif
307 return;
309 else
311 CBotPlayer* const player = (*it).second;
313 // update team composition
314 if (player->getCurrentTeamId() != CTEAM::InvalidTeamId)
316 CHashMap<int, std::set<TDataSetRow> >::iterator it(_teams.find(player->getCurrentTeamId()));
317 if (it != _teams.end())
319 it->second.erase(dataSetRow);
320 if (it->second.empty())
321 _teams.erase(it);
324 player->despawnBot();
325 removeChildByIndex(player->getChildIndex());
329 void CManagerPlayer::updatePlayerTeam(TDataSetRow const& dataSetRow)
331 TPlayerMap::iterator it(_spawnedPlayers.find(dataSetRow));
332 if (it!=_spawnedPlayers.end())
334 uint16 const oldTeam = it->second->getCurrentTeamId();
335 if (oldTeam!=CTEAM::InvalidTeamId)
337 CHashMap<int, std::set<TDataSetRow> >::iterator it(_teams.find(oldTeam));
338 if (it != _teams.end())
340 it->second.erase(dataSetRow);
341 if (it->second.empty())
342 _teams.erase(it);
345 // update team id and composition
346 CMirrorPropValueRO<TYPE_TEAM_ID> value(*CMirrors::DataSet, dataSetRow, DSPropertyTEAM_ID);
347 it->second->setCurrentTeamId(value());
348 if (value() != CTEAM::InvalidTeamId)
350 _teams[value()].insert(dataSetRow);
353 else
355 nlwarning("CManagerPlayer::updatePlayerTeam : dataSetRow %u, can't find spawned player !", dataSetRow.getIndex());
359 // This static data is just to have a ref return type anytime, bad habit.
360 std::set<TDataSetRow> CManagerPlayer::emptySet;
362 std::set<TDataSetRow> const& CManagerPlayer::getPlayerTeam(TDataSetRow const& playerRow)
364 TPlayerMap::iterator it(_spawnedPlayers.find(playerRow));
366 if (it != _spawnedPlayers.end())
368 uint16 const teamId = it->second->getCurrentTeamId();
369 return getPlayerTeam(teamId);
371 else
373 nlwarning("CManagerPlayer::getPlayerTeam can't find player from dataset %u", playerRow.getIndex());
374 return emptySet;
378 std::set<TDataSetRow> const& CManagerPlayer::getPlayerTeam(uint16 teamId)
380 if (teamId == CTEAM::InvalidTeamId)
382 return emptySet;
384 else
386 TTeamMap::iterator itTeam = _teams.find(teamId);
387 if (itTeam != _teams.end())
389 return itTeam->second;
391 else
393 nlwarning("CManagerPlayer::getPlayerTeam : no player in team %u", teamId);
394 return emptySet;
399 void CManagerPlayer::getTeamIds(std::vector<uint16>& teamIds)
401 FOREACH(itTeam, TTeamMap, _teams)
403 teamIds.push_back(itTeam->first);
407 void CBotPlayer::forgotAggroForAggroer()
410 for (sint32 i=(sint32)_AggroerList.size()-1; i>=0; --i)
412 CAIEntityPhysical* const phys = CAIS::instance().getEntityPhysical(_AggroerList[i]);
413 if (!phys)
414 continue;
415 CBotAggroOwner* aggroOwner = NULL;
417 switch(phys->getRyzomType())
419 case RYZOMID::creature:
420 aggroOwner = NLMISC::safe_cast<CBotAggroOwner*>(NLMISC::safe_cast<CSpawnBotFauna*>(phys));
421 break;
422 case RYZOMID::npc:
423 aggroOwner = NLMISC::safe_cast<CBotAggroOwner*>(NLMISC::safe_cast<CSpawnBotNpc*>(phys));
424 break;
426 if (!aggroOwner)
427 continue;
428 aggroOwner->forgetAggroFor(dataSetRow());
432 bool CBotPlayer::useOldUnreachable = false;
434 NLMISC_COMMAND(playerUseOldUnreachable, "Old unreachable state computing is used","")
436 if(args.size()>1) return false;
437 if(args.size()==1) StrToBool(CBotPlayer::useOldUnreachable, args[0]);
438 log.displayNL("playerUseOldUnreachable is %s", CBotPlayer::useOldUnreachable?"true":"false");
439 return true;
442 sint32 CBotPlayer::getFame(std::string const& faction, bool modulated, bool returnUnknownValue) const
444 sint32 fame = CAIEntityPhysical::getFame(faction, modulated, true);
445 if (fame==NO_FAME)
447 fame = CStaticFames::getInstance().getStaticFame(_Sheet->Race(), faction);
449 if (!returnUnknownValue && fame==NO_FAME)
450 fame = 0;
451 return fame;
454 sint32 CBotPlayer::getFameIndexed(uint32 factionIndex, bool modulated, bool returnUnknownValue) const
456 sint32 fame = CAIEntityPhysical::getFameIndexed(factionIndex, modulated, true);
457 if (fame==NO_FAME)
459 uint32 playerFaction = CStaticFames::getInstance().getFactionIndex(_Sheet->Race());
460 fame = CStaticFames::getInstance().getStaticFameIndexed(playerFaction, factionIndex);
462 if (!returnUnknownValue && fame==NO_FAME)
463 fame = 0;
464 return fame;