fix(Core/Movement): (#7008)

- Get zone/area IDs from vmap data in the liquid update
- Add new method Map::getFullVMapDataForPosition to get area info and liquid info in a single vmap lookup
- Adjust GetZoneId/GetAreaId on WorldObject to always return these cached fields.
- Clean up liquid state handling on Unit and Player
- Implemented getting area id from gameobject spawns.
- Removed old core related to getting movement flags dependent on environment.
- Movement flags are now processed more precisely and dynamically.

Original source: TrinityCore.

- Closes #5086
- Updates #2208.
This commit is contained in:
UltraNix
2021-08-25 12:41:20 +02:00
committed by GitHub
parent 909c3e5799
commit a8c0a2cc89
47 changed files with 1086 additions and 883 deletions

View File

@@ -591,6 +591,8 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
InitTalentForLevel();
InitPrimaryProfessions(); // to max set before any spell added
UpdatePositionData();
// apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
UpdateMaxHealth(); // Update max Health (for add bonus from stamina)
SetFullHealth();
@@ -876,7 +878,7 @@ void Player::HandleDrowning(uint32 time_diff)
}
// In dark water
if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER)
if (m_MirrorTimerFlags & UNDERWATER_INDARKWATER)
{
// Fatigue timer not activated - activate it
if (m_MirrorTimer[FATIGUE_TIMER] == DISABLED_MIRROR_TIMER)
@@ -899,7 +901,7 @@ void Player::HandleDrowning(uint32 time_diff)
else if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) // Teleport ghost to graveyard
RepopAtGraveyard();
}
else if (!(m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER))
else if (!(m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER))
SendMirrorTimer(FATIGUE_TIMER, getMaxTimer(FATIGUE_TIMER), m_MirrorTimer[FATIGUE_TIMER], -1);
}
}
@@ -909,7 +911,7 @@ void Player::HandleDrowning(uint32 time_diff)
m_MirrorTimer[FATIGUE_TIMER] += 10 * time_diff;
if (m_MirrorTimer[FATIGUE_TIMER] >= DarkWaterTime || !IsAlive())
StopMirrorTimer(FATIGUE_TIMER);
else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)
else if (m_MirrorTimerFlagsLast & UNDERWATER_INDARKWATER)
SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10);
}
@@ -2066,31 +2068,6 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTy
return nullptr;
}
bool Player::IsInWater(bool allowAbove) const
{
if (m_isInWater || !allowAbove)
return m_isInWater;
float distsq = GetExactDistSq(&m_last_environment_position);
if (distsq < 3.0f * 3.0f)
return m_last_islittleabovewater_status;
else
{
LiquidData liqData;
liqData.level = INVALID_HEIGHT;
const_cast<Position*>(&m_last_environment_position)->Relocate(GetPositionX(), GetPositionY(), GetPositionZ());
bool inWater = GetBaseMap()->IsInWater(GetPositionX(), GetPositionY(), GetPositionZ(), &liqData);
*(const_cast<bool*>(&m_last_islittleabovewater_status)) = inWater || (liqData.level > INVALID_HEIGHT && liqData.level > liqData.depth_level && liqData.level <= GetPositionZ() + 3.0f && liqData.level > GetPositionZ() - 1.0f);
return m_last_islittleabovewater_status;
}
}
bool Player::IsUnderWater() const
{
return IsInWater() &&
GetPositionZ() < GetBaseMap()->GetWaterLevel(GetPositionX(), GetPositionY()) - GetCollisionHeight();
}
bool Player::IsFalling() const
{
// Xinef: Added !IsInFlight check
@@ -4355,7 +4332,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
// trigger update zone for alive state zone updates
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea, true);
GetZoneAndAreaId(newzone, newarea);
UpdateZone(newzone, newarea);
sOutdoorPvPMgr->HandlePlayerResurrects(this, newzone);
@@ -4508,6 +4485,8 @@ Corpse* Player::CreateCorpse()
// register for player, but not show
GetMap()->AddCorpse(corpse);
UpdatePositionData();
// we do not need to save corpses for BG/arenas
if (!GetMap()->IsBattlegroundOrArena())
corpse->SaveToDB();
@@ -5563,7 +5542,7 @@ void Player::CheckAreaExploreAndOutdoor()
return;
bool isOutdoor = IsOutdoors();
uint32 areaId = GetBaseMap()->GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor);
uint32 areaId = GetAreaId();
AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId);
if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor)
@@ -6159,7 +6138,7 @@ uint32 Player::GetZoneIdFromDB(ObjectGuid guid)
if (!sMapStore.LookupEntry(map))
return 0;
zone = sMapMgr->GetZoneId(map, posx, posy, posz);
zone = sMapMgr->GetZoneId(PHASEMASK_NORMAL, map, posx, posy, posz);
if (zone > 0)
{
@@ -6184,36 +6163,6 @@ uint32 Player::GetLevelFromStorage(ObjectGuid::LowType guid)
return 0;
}
uint32 Player::GetZoneId(bool forceRecalc) const
{
if (forceRecalc)
*(const_cast<uint32*>(&m_last_zone_id)) = WorldObject::GetZoneId();
return m_last_zone_id;
}
uint32 Player::GetAreaId(bool forceRecalc) const
{
if (forceRecalc)
*(const_cast<uint32*>(&m_last_area_id)) = WorldObject::GetAreaId();
return m_last_area_id;
}
void Player::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc) const
{
if (forceRecalc)
{
WorldObject::GetZoneAndAreaId(zoneid, areaid);
*(const_cast<uint32*>(&m_last_zone_id)) = zoneid;
*(const_cast<uint32*>(&m_last_area_id)) = areaid;
return;
}
zoneid = m_last_zone_id;
areaid = m_last_area_id;
}
//If players are too far away from the duel flag... they lose the duel
void Player::CheckDuelDistance(time_t currTime)
{
@@ -10849,7 +10798,7 @@ void Player::SendInitialPacketsAfterAddToMap()
// update zone
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea, true);
GetZoneAndAreaId(newzone, newarea);
UpdateZone(newzone, newarea); // also call SendInitWorldStates();
if (HasAuraType(SPELL_AURA_MOD_STUN))
@@ -13758,7 +13707,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
stmt->setUInt16(index++, GetZoneId(true));
stmt->setUInt16(index++, GetZoneId());
stmt->setUInt32(index++, uint32(m_deathExpireTime));
ss.str("");
@@ -13896,7 +13845,7 @@ void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
stmt->setUInt16(index++, (uint16)m_ExtraFlags);
stmt->setUInt8(index++, m_stableSlots);
stmt->setUInt16(index++, (uint16)m_atLoginFlags);
stmt->setUInt16(index++, GetZoneId(true));
stmt->setUInt16(index++, GetZoneId());
stmt->setUInt32(index++, uint32(m_deathExpireTime));
ss.str("");

View File

@@ -85,7 +85,7 @@ enum PlayerUnderwaterState
UNDERWATER_INWATER = 0x01, // terrain type is water and player is afflicted by it
UNDERWATER_INLAVA = 0x02, // terrain type is lava and player is afflicted by it
UNDERWATER_INSLIME = 0x04, // terrain type is lava and player is afflicted by it
UNDERWARER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it
UNDERWATER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it
UNDERWATER_EXIST_TIMERS = 0x10
};
@@ -1021,8 +1021,7 @@ public:
void SetInWater(bool apply);
[[nodiscard]] bool IsInWater(bool allowAbove = false) const override;
[[nodiscard]] bool IsUnderWater() const override;
[[nodiscard]] bool IsInWater() const override { return m_isInWater; }
[[nodiscard]] bool IsFalling() const;
bool IsInAreaTriggerRadius(const AreaTrigger* trigger) const;
@@ -1737,10 +1736,6 @@ public:
void UpdateZone(uint32 newZone, uint32 newArea);
void UpdateArea(uint32 newArea);
[[nodiscard]] uint32 GetZoneId(bool forceRecalc = false) const override;
[[nodiscard]] uint32 GetAreaId(bool forceRecalc = false) const override;
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, bool forceRecalc = false) const override;
void UpdateZoneDependentAuras(uint32 zone_id); // zones
void UpdateAreaDependentAuras(uint32 area_id); // subzones
@@ -1905,7 +1900,8 @@ public:
bool UpdatePosition(float x, float y, float z, float orientation, bool teleport = false) override;
bool UpdatePosition(const Position& pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); }
void UpdateUnderwaterState(Map* m, float x, float y, float z) override;
void ProcessTerrainStatusUpdate() override;
void SendMessageToSet(WorldPacket* data, bool self) override { SendMessageToSetInRange(data, GetVisibilityRange(), self, true); } // pussywizard!
void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool includeMargin = false, Player const* skipped_rcvr = nullptr) override; // pussywizard!

View File

@@ -5241,6 +5241,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
SetMap(map);
StoreRaidMapDifficulty();
UpdatePositionData();
SaveRecallPosition();
time_t now = time(nullptr);
@@ -5772,7 +5774,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
if (result)
{
uint32 zoneId = GetZoneId(true);
uint32 zoneId = GetZoneId();
std::map<ObjectGuid::LowType, Bag*> bagMap; // fast guid lookup for bags
std::map<ObjectGuid::LowType, Item*> invalidBagMap; // fast guid lookup for bags

View File

@@ -268,9 +268,7 @@ void Player::Update(uint32 p_time)
}
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea, true);
m_last_zone_id = newzone;
m_last_area_id = newarea;
GetZoneAndAreaId(newzone, newarea);
if (m_zoneUpdateId != newzone)
UpdateZone(newzone, newarea); // also update area
@@ -354,7 +352,7 @@ void Player::Update(uint32 p_time)
}
// not auto-free ghost from body in instances
if (m_deathTimer > 0 && !GetBaseMap()->Instanceable() &&
if (m_deathTimer > 0 && !GetMap()->Instanceable() &&
!HasAuraType(SPELL_AURA_PREVENT_RESURRECTION))
{
if (p_time >= m_deathTimer)
@@ -1975,96 +1973,6 @@ void Player::UpdateCorpseReclaimDelay()
m_deathExpireTime = now + DEATH_EXPIRE_STEP;
}
void Player::UpdateUnderwaterState(Map* m, float x, float y, float z)
{
// pussywizard: optimization
if (GetExactDistSq(&m_last_underwaterstate_position) < 3.0f * 3.0f)
return;
m_last_underwaterstate_position.Relocate(m_positionX, m_positionY,
m_positionZ);
if (!IsPositionValid()) // pussywizard: crashfix if calculated grid coords
// would be out of range 0-64
return;
LiquidData liquid_status;
ZLiquidStatus res = m->getLiquidStatus(
x, y, z, MAP_ALL_LIQUIDS, &liquid_status, GetCollisionHeight());
if (!res)
{
m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA |
UNDERWATER_INSLIME | UNDERWARER_INDARKWATER);
if (_lastLiquid && _lastLiquid->SpellId)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
_lastLiquid = nullptr;
return;
}
if (uint32 liqEntry = liquid_status.entry)
{
LiquidTypeEntry const* liquid = sLiquidTypeStore.LookupEntry(liqEntry);
if (_lastLiquid && _lastLiquid->SpellId && _lastLiquid->Id != liqEntry)
RemoveAurasDueToSpell(_lastLiquid->SpellId);
if (liquid && liquid->SpellId)
{
if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER))
{
if (!HasAura(liquid->SpellId))
CastSpell(this, liquid->SpellId, true);
}
else
RemoveAurasDueToSpell(liquid->SpellId);
}
_lastLiquid = liquid;
}
else if (_lastLiquid && _lastLiquid->SpellId)
{
RemoveAurasDueToSpell(_lastLiquid->SpellId);
_lastLiquid = nullptr;
}
// All liquids type - check under water position
if (liquid_status.type_flags &
(MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA |
MAP_LIQUID_TYPE_SLIME))
{
if (res & LIQUID_MAP_UNDER_WATER)
m_MirrorTimerFlags |= UNDERWATER_INWATER;
else
m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
}
// Allow travel in dark water on taxi or transport
if ((liquid_status.type_flags & MAP_LIQUID_TYPE_DARK_WATER) &&
!IsInFlight() && !GetTransport())
m_MirrorTimerFlags |= UNDERWARER_INDARKWATER;
else
m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER;
// in lava check, anywhere in lava level
if (liquid_status.type_flags & MAP_LIQUID_TYPE_MAGMA)
{
if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER |
LIQUID_MAP_WATER_WALK))
m_MirrorTimerFlags |= UNDERWATER_INLAVA;
else
m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
}
// in slime check, anywhere in slime level
if (liquid_status.type_flags & MAP_LIQUID_TYPE_SLIME)
{
if (res & (LIQUID_MAP_UNDER_WATER | LIQUID_MAP_IN_WATER |
LIQUID_MAP_WATER_WALK))
m_MirrorTimerFlags |= UNDERWATER_INSLIME;
else
m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
}
}
void Player::UpdateCharmedAI()
{
// Xinef: maybe passed as argument?
@@ -2361,3 +2269,58 @@ void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
data << Value;
GetSession()->SendPacket(&data);
}
void Player::ProcessTerrainStatusUpdate()
{
// process liquid auras using generic unit code
Unit::ProcessTerrainStatusUpdate();
LiquidData const& liquidData = GetLiquidData();
// player specific logic for mirror timers
if (liquidData.Status != LIQUID_MAP_NO_WATER)
{
// Breath bar state (under water in any liquid type)
if ((liquidData.Flags & MAP_ALL_LIQUIDS) != 0)
{
if ((liquidData.Status & LIQUID_MAP_UNDER_WATER) != 0)
m_MirrorTimerFlags |= UNDERWATER_INWATER;
else
m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
}
// Fatigue bar state (if not on flight path or transport)
if ((liquidData.Flags & MAP_LIQUID_TYPE_DARK_WATER) && !IsInFlight() && !GetTransport())
{
// Exclude also uncontrollable vehicles
Vehicle* vehicle = GetVehicle();
VehicleSeatEntry const* vehicleSeat = vehicle ? vehicle->GetSeatForPassenger(this) : nullptr;
if (!vehicleSeat || vehicleSeat->CanControl())
m_MirrorTimerFlags |= UNDERWATER_INDARKWATER;
else
m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER;
}
else
m_MirrorTimerFlags &= ~UNDERWATER_INDARKWATER;
// Lava state (any contact)
if (liquidData.Flags & MAP_LIQUID_TYPE_MAGMA)
{
if (liquidData.Status & MAP_LIQUID_STATUS_IN_CONTACT)
m_MirrorTimerFlags |= UNDERWATER_INLAVA;
else
m_MirrorTimerFlags &= ~UNDERWATER_INLAVA;
}
// Slime state (any contact)
if (liquidData.Flags & MAP_LIQUID_TYPE_SLIME)
{
if (liquidData.Status & MAP_LIQUID_STATUS_IN_CONTACT)
m_MirrorTimerFlags |= UNDERWATER_INSLIME;
else
m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
}
}
else
m_MirrorTimerFlags &= ~(UNDERWATER_INWATER | UNDERWATER_INLAVA | UNDERWATER_INSLIME | UNDERWATER_INDARKWATER);
}