From 6d77f1839400204c3d8cb16f8a74b2af1415df6e Mon Sep 17 00:00:00 2001 From: H0zen Date: Sat, 14 May 2016 20:55:24 +0300 Subject: [PATCH] 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 --- src/game/Object/Unit.cpp | 18 ++++++ src/game/Object/Unit.h | 6 +- src/game/WorldHandlers/Map.cpp | 102 ++++++++++++++++++++------------- src/game/WorldHandlers/Map.h | 3 + 4 files changed, 88 insertions(+), 41 deletions(-) diff --git a/src/game/Object/Unit.cpp b/src/game/Object/Unit.cpp index 1136bba4..fd911ecb 100644 --- a/src/game/Object/Unit.cpp +++ b/src/game/Object/Unit.cpp @@ -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); } } diff --git a/src/game/Object/Unit.h b/src/game/Object/Unit.h index 0ecc2c7b..0b357568 100644 --- a/src/game/Object/Unit.h +++ b/src/game/Object/Unit.h @@ -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); /** diff --git a/src/game/WorldHandlers/Map.cpp b/src/game/WorldHandlers/Map.cpp index 2336b275..db624ddf 100644 --- a/src/game/WorldHandlers/Map.cpp +++ b/src/game/WorldHandlers/Map.cpp @@ -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 &gridVisitor, + TypeContainerVisitor &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 _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::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); } } diff --git a/src/game/WorldHandlers/Map.h b/src/game/WorldHandlers/Map.h index 8ac7fee4..4b89da07 100644 --- a/src/game/WorldHandlers/Map.h +++ b/src/game/WorldHandlers/Map.h @@ -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 return i_grids[x][y]; } + void VisitNearbyCellsOf(WorldObject* obj, TypeContainerVisitor &gridVisitor, TypeContainerVisitor &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); }