Fix "You are in combat" bug. (#123)

* Fix "You are in combat" bug.

Ideas taken from TC

* Correct previous commit.

* Improved previous commits

- Creatures in dungeons will not remove distant players from their threat list
- Creatures in non dungeon maps will properly remove the distant players from their threat lists and also players auras from them
This commit is contained in:
H0zen 2016-05-14 20:55:24 +03:00 committed by Antz
parent 1a3a238586
commit 6d77f18394
4 changed files with 88 additions and 41 deletions

View File

@ -3852,6 +3852,21 @@ void Unit::RemoveAura(uint32 spellId, SpellEffectIndex effindex, Aura* except)
{ ++iter; }
}
}
void Unit::RemoveAurasByCaster(ObjectGuid casterGuid)
{
for (SpellAuraHolderMap::iterator iter = m_spellAuraHolders.begin(); iter != m_spellAuraHolders.end();)
{
if (iter->second->GetCasterGuid() == casterGuid)
{
RemoveSpellAuraHolder(iter->second);
iter = m_spellAuraHolders.begin();
}
else
{ ++iter; }
}
}
void Unit::RemoveAurasByCasterSpell(uint32 spellId, ObjectGuid casterGuid)
{
SpellAuraHolderBounds spair = GetSpellAuraHolderBounds(spellId);
@ -6604,6 +6619,9 @@ void Unit::ClearInCombat()
if (cThis->GetCreatureInfo()->UnitFlags & UNIT_FLAG_OOC_NOT_ATTACKABLE && !(cThis->GetTemporaryFactionFlags() & TEMPFACTION_TOGGLE_OOC_NOT_ATTACK))
{ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE); }
if (cThis->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED))
{ RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED); }
clearUnitState(UNIT_STAT_ATTACK_PLAYER);
}
}

View File

@ -3243,12 +3243,16 @@ class Unit : public WorldObject
* @param spellId id of the spell causing the \ref Aura s you would like to remove
*/
void RemoveAurasDueToItemSpell(Item* castItem, uint32 spellId);
/**
* Removes all \ref Aura s applied by spells casted by a certain \ref Player / \ref Unit
* @param casterGuid \ref ObjectGuid of the caster
*/
void RemoveAurasByCaster(ObjectGuid casterGuid);
/**
* Removes all \ref Aura s that a certain spell cast by a certain \ref Player / \ref Unit
* would cause via it's effects (up to 3 of them per \ref Aura)
* @param spellId id of the \ref Spell causing the \ref Aura s you would like to remove
* @param casterGuid \ref ObjectGuid of the caster
* @param mode reason for removal
*/
void RemoveAurasByCasterSpell(uint32 spellId, ObjectGuid casterGuid);
/**

View File

@ -469,6 +469,36 @@ bool Map::loaded(const GridPair& p) const
return (getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord));
}
void Map::VisitNearbyCellsOf(WorldObject* obj,
TypeContainerVisitor<MaNGOS::ObjectUpdater, GridTypeMapContainer> &gridVisitor,
TypeContainerVisitor<MaNGOS::ObjectUpdater, WorldTypeMapContainer> &worldVisitor)
{
if (!obj->IsPositionValid())
return;
// lets update mobs/objects in ALL visible cells around player!
CellArea area = Cell::CalculateCellArea(obj->GetPositionX(), obj->GetPositionY(), GetVisibilityDistance());
for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x)
{
for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y)
{
// marked cells are those that have been visited
// don't visit the same cell twice
uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
if (!isCellMarked(cell_id))
{
markCell(cell_id);
CellPair pair(x, y);
Cell cell(pair);
cell.SetNoCreate();
Visit(cell, gridVisitor);
Visit(cell, worldVisitor);
}
}
}
}
void Map::Update(const uint32& t_diff)
{
m_dyn_tree.update(t_diff);
@ -512,30 +542,42 @@ void Map::Update(const uint32& t_diff)
{
Player* plr = m_mapRefIter->getSource();
if (!plr->IsInWorld() || !plr->IsPositionValid())
if (!plr || !plr->IsInWorld())
{ continue; }
// lets update mobs/objects in ALL visible cells around player!
CellArea area = Cell::CalculateCellArea(plr->GetPositionX(), plr->GetPositionY(), GetVisibilityDistance());
VisitNearbyCellsOf(plr, grid_object_update, world_object_update);
for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x)
// Collect and remove references to creatures too far away from player's m_HostileRefManager
// Combat state will change on next tick, if case
if (!IsDungeon() && plr->IsInCombat())
{
for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y)
std::vector<Creature*> _removeList;
HostileRefManager& href = plr->GetHostileRefManager();
HostileReference* ref = href.getFirst();
while (ref)
{
// marked cells are those that have been visited
// don't visit the same cell twice
uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
if (!isCellMarked(cell_id))
{
markCell(cell_id);
CellPair pair(x, y);
Cell cell(pair);
cell.SetNoCreate();
Visit(cell, grid_object_update);
Visit(cell, world_object_update);
}
if (Unit* unit = ref->getSource()->getOwner())
if (unit->ToCreature() && unit->GetMapId() == plr->GetMapId() && !unit->IsWithinDistInMap(plr, GetVisibilityDistance(), false))
_removeList.push_back(unit->ToCreature());
ref = ref->next();
}
for (std::vector<Creature*>::iterator it = _removeList.begin(); it != _removeList.end(); ++it)
{
if ((*it)->IsTappedBy(plr))
{ (*it)->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED); }
(*it)->RemoveAurasByCaster(plr->GetObjectGuid());
(*it)->_removeAttacker(plr);
(*it)->GetHostileRefManager().deleteReference(plr);
href.deleteReference(*it);
VisitNearbyCellsOf(*it, grid_object_update, world_object_update);
}
}
}
// non-player active objects
@ -543,37 +585,17 @@ void Map::Update(const uint32& t_diff)
{
for (m_activeNonPlayersIter = m_activeNonPlayers.begin(); m_activeNonPlayersIter != m_activeNonPlayers.end();)
{
// skip not in world
WorldObject* obj = *m_activeNonPlayersIter;
// step before processing, in this case if Map::Remove remove next object we correctly
// step to next-next, and if we step to end() then newly added objects can wait next update.
++m_activeNonPlayersIter;
if (!obj->IsInWorld() || !obj->IsPositionValid())
// skip not in world
if (!obj || !obj->IsInWorld())
{ continue; }
// lets update mobs/objects in ALL visible cells around player!
CellArea area = Cell::CalculateCellArea(obj->GetPositionX(), obj->GetPositionY(), GetVisibilityDistance());
for (uint32 x = area.low_bound.x_coord; x <= area.high_bound.x_coord; ++x)
{
for (uint32 y = area.low_bound.y_coord; y <= area.high_bound.y_coord; ++y)
{
// marked cells are those that have been visited
// don't visit the same cell twice
uint32 cell_id = (y * TOTAL_NUMBER_OF_CELLS_PER_MAP) + x;
if (!isCellMarked(cell_id))
{
markCell(cell_id);
CellPair pair(x, y);
Cell cell(pair);
cell.SetNoCreate();
Visit(cell, grid_object_update);
Visit(cell, world_object_update);
}
}
}
VisitNearbyCellsOf(obj, grid_object_update, world_object_update);
}
}

View File

@ -64,6 +64,8 @@ class GridMap;
class GameObjectModel;
class WeatherSystem;
namespace MaNGOS { struct ObjectUpdater; }
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
#if defined( __GNUC__ )
#pragma pack(1)
@ -321,6 +323,7 @@ class Map : public GridRefManager<NGridType>
return i_grids[x][y];
}
void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor<MaNGOS::ObjectUpdater, GridTypeMapContainer> &gridVisitor, TypeContainerVisitor<MaNGOS::ObjectUpdater, WorldTypeMapContainer> &worldVisitor);
bool isGridObjectDataLoaded(uint32 x, uint32 y) const { return getNGrid(x, y)->isGridObjectDataLoaded(); }
void setGridObjectDataLoaded(bool pLoaded, uint32 x, uint32 y) { getNGrid(x, y)->setGridObjectDataLoaded(pLoaded); }