1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
20 #include "ai_player.h"
21 #include "ai_bot_fauna.h"
22 #include "ai_bot_npc.h"
24 using namespace MULTI_LINE_FORMATER
;
27 //////////////////////////////////////////////////////////////////////////////
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
)
39 nlassert(owner
->playerList().find(dataSetRow())==owner
->playerList().end());
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());
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
);
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
;
96 CAIEntityPhysical
const* const targetBot
= CAIS::instance().getEntityPhysical(event
._targetRow
);
100 CAIEntityPhysical
* targeter
= targetBot
->firstTargeter();
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);
115 CSpawnBotNpc
* const npc
= NLMISC::safe_cast
<CSpawnBotNpc
*>(targeter
);
116 npc
->addAggroFor(event
._originatorRow
, aggro
, true);
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;
137 _PlayerPosIsInvalid
= false;
139 linkEntityToMatrix(this->pos(),getOwner()->getOwner()->playerMatrix());
141 if (wpos
.getFlags()&RYAI_MAP_CRUNCH::Water
)
142 setActionFlags(RYZOMACTIONFLAGS::InWater
);
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
152 return CAIPos(pos());
155 void CBotPlayer::update()
160 CAIInstance
* CBotPlayer::getAIInstance() const
162 return getOwner()->getAIInstance();
165 bool CBotPlayer::spawn()
171 void CBotPlayer::despawnBot()
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
;
191 bool CBotPlayer::setPos(CAIPos
const& pos
)
199 float CBotPlayer::walkSpeed() const
201 nlerror("Non-virtual overriden function walkSpeed in CBotPlayer");
205 float CBotPlayer::runSpeed() const
207 nlerror("Non-virtual overriden function runSpeed in CBotPlayer");
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
)
220 for (sint32 i
=(sint32
)_AggroerList
.size()-1;i
>=0;i
--)
221 nlassert(_AggroerList
[i
]!=row
);
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();
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 //////////////////////////////////////////////////////////////////////////////
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
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 ?).
284 player
->linkToWorldMap(player
, player
->pos(), getOwner()->playerMatrix());
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");
305 nlerror("Player Despawn Error");
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())
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())
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
);
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
);
373 nlwarning("CManagerPlayer::getPlayerTeam can't find player from dataset %u", playerRow
.getIndex());
378 std::set
<TDataSetRow
> const& CManagerPlayer::getPlayerTeam(uint16 teamId
)
380 if (teamId
== CTEAM::InvalidTeamId
)
386 TTeamMap::iterator itTeam
= _teams
.find(teamId
);
387 if (itTeam
!= _teams
.end())
389 return itTeam
->second
;
393 nlwarning("CManagerPlayer::getPlayerTeam : no player in team %u", teamId
);
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
]);
415 CBotAggroOwner
* aggroOwner
= NULL
;
417 switch(phys
->getRyzomType())
419 case RYZOMID::creature
:
420 aggroOwner
= NLMISC::safe_cast
<CBotAggroOwner
*>(NLMISC::safe_cast
<CSpawnBotFauna
*>(phys
));
423 aggroOwner
= NLMISC::safe_cast
<CBotAggroOwner
*>(NLMISC::safe_cast
<CSpawnBotNpc
*>(phys
));
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");
442 sint32
CBotPlayer::getFame(std::string
const& faction
, bool modulated
, bool returnUnknownValue
) const
444 sint32 fame
= CAIEntityPhysical::getFame(faction
, modulated
, true);
447 fame
= CStaticFames::getInstance().getStaticFame(_Sheet
->Race(), faction
);
449 if (!returnUnknownValue
&& fame
==NO_FAME
)
454 sint32
CBotPlayer::getFameIndexed(uint32 factionIndex
, bool modulated
, bool returnUnknownValue
) const
456 sint32 fame
= CAIEntityPhysical::getFameIndexed(factionIndex
, modulated
, true);
459 uint32 playerFaction
= CStaticFames::getInstance().getFactionIndex(_Sheet
->Race());
460 fame
= CStaticFames::getInstance().getStaticFameIndexed(playerFaction
, factionIndex
);
462 if (!returnUnknownValue
&& fame
==NO_FAME
)