diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 2e08061b77..5e5b9da5cc 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -345,6 +345,14 @@ enum AreaFlags AREA_FLAG_UNK9 = 0x40000000 }; +enum AreaMountFlags +{ + AREA_MOUNT_FLAG_GROUND_ALLOWED = 0x1, + AREA_MOUNT_FLAG_FLYING_ALLOWED = 0x2, + AREA_MOUNT_FLAG_FLOAT_ALLOWED = 0x4, + AREA_MOUNT_FLAG_UNDERWATER_ALLOWED = 0x8 +}; + enum Difficulty { REGULAR_DIFFICULTY = 0, @@ -449,10 +457,13 @@ enum ItemLimitCategoryMode ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1 // limit applied to amount equipped items (including used gems) }; -enum MountFlags +enum MountCapabilityFlags { - MOUNT_FLAG_CAN_PITCH = 0x4, // client checks MOVEMENTFLAG2_FULL_SPEED_PITCHING - MOUNT_FLAG_CAN_SWIM = 0x8, // client checks MOVEMENTFLAG_SWIMMING + MOUNT_CAPABILITY_FLAG_GROUND = 0x1, + MOUNT_CAPABILITY_FLAG_FLYING = 0x2, + MOUNT_CAPABILITY_FLAG_FLOAT = 0x4, + MOUNT_CAPABILITY_FLAG_UNDERWATER = 0x8, + MOUNT_CAPABIILTY_FLAG_IGNORE_RESTRICTIONS = 0x20, }; enum SkillRaceClassInfoFlags diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 47ed0da65b..821cacebae 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -1385,3 +1385,20 @@ EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uin auto itr = sEmotesTextSoundMap.find(EmotesTextSoundKey(emote, race, gender)); return itr != sEmotesTextSoundMap.end() ? itr->second : nullptr; } + +bool IsInArea(uint32 objectAreaId, uint32 areaId) +{ + do + { + if (objectAreaId == areaId) + return true; + + AreaTableEntry const* objectArea = sAreaTableStore.LookupEntry(objectAreaId); + if (!objectArea) + break; + + objectAreaId = objectArea->zone; + } while (objectAreaId); + + return false; +} diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index a4fc6fe567..0e2e32e552 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -76,6 +76,7 @@ TC_GAME_API CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint TC_GAME_API CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color); uint32 GetPowerIndexByClass(uint32 powerType, uint32 classId); +TC_GAME_API bool IsInArea(uint32 objectAreaId, uint32 areaId); TC_GAME_API LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty); uint32 GetDefaultMapLight(uint32 mapId); diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index e8cef2879f..f7636bbdb6 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -629,7 +629,7 @@ struct AreaTableEntry float MaxDepth; // 17, float AmbientMultiplier; // 18 client only? uint32 LightId; // 19 - //uint32 unk20; // 20 4.0.0 - Mounting related + uint32 MountFlags; // 20 //uint32 unk21; // 21 4.0.0 //uint32 unk22; // 22 4.0.0 //uint32 unk23; // 23 4.0.0 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 3500858c89..7fd82ed1f9 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -27,7 +27,7 @@ const std::string CustomAchievementfmt = "pppaaaapapaapp"; const std::string CustomAchievementIndex = "ID"; char const AnimKitfmt[] = "nxx"; char const AchievementCriteriafmt[] = "niiiliiiisiiiiixxiiiiii"; -char const AreaTableEntryfmt[] = "niiiixxxxxisiiiiiffixxxxxx"; +char const AreaTableEntryfmt[] = "niiiixxxxxisiiiiiffiixxxxx"; char const AreaGroupEntryfmt[] = "niiiiiii"; char const AreaPOIEntryfmt[] = "niiiiiiiiiiiffixixxixx"; char const AreaTriggerEntryfmt[] = "nifffxxxfffff"; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index bb349c8494..7724c4738f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -8085,18 +8085,35 @@ void Unit::Dismount() MountCapabilityEntry const* Unit::GetMountCapability(uint32 mountType) const { if (!mountType) - return NULL; + return nullptr; MountTypeEntry const* mountTypeEntry = sMountTypeStore.LookupEntry(mountType); if (!mountTypeEntry) - return NULL; + return nullptr; - uint32 zoneId, areaId; - GetZoneAndAreaId(zoneId, areaId); + + uint32 areaId = GetAreaId(); uint32 ridingSkill = 5000; + uint32 mountFlags = 0; + bool isSubmerged = false; + bool isInWater = false; + if (GetTypeId() == TYPEID_PLAYER) ridingSkill = ToPlayer()->GetSkillValue(SKILL_RIDING); + if (HasAuraType(SPELL_AURA_MOUNT_RESTRICTIONS)) + { + for (AuraEffect const* auraEffect : GetAuraEffectsByType(SPELL_AURA_MOUNT_RESTRICTIONS)) + mountFlags |= auraEffect->GetMiscValue(); + } + else if (AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(areaId)) + mountFlags = areaTable->MountFlags; + + LiquidData liquid; + ZLiquidStatus liquidStatus = GetMap()->GetLiquidStatus(GetPositionX(), GetPositionY(), GetPositionZ(), MAP_ALL_LIQUIDS, &liquid); + isSubmerged = (liquidStatus & LIQUID_MAP_UNDER_WATER) != 0 || HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING); + isInWater = (liquidStatus & (LIQUID_MAP_IN_WATER | LIQUID_MAP_UNDER_WATER)) != 0; + for (uint32 i = MAX_MOUNT_CAPABILITIES; i > 0; --i) { MountCapabilityEntry const* mountCapability = sMountCapabilityStore.LookupEntry(mountTypeEntry->MountCapability[i - 1]); @@ -8106,38 +8123,55 @@ MountCapabilityEntry const* Unit::GetMountCapability(uint32 mountType) const if (ridingSkill < mountCapability->RequiredRidingSkill) continue; - if (HasExtraUnitMovementFlag(MOVEMENTFLAG2_FULL_SPEED_PITCHING)) + if (!(mountCapability->Flags & MOUNT_CAPABIILTY_FLAG_IGNORE_RESTRICTIONS)) { - if (!(mountCapability->Flags & MOUNT_FLAG_CAN_PITCH)) + if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_GROUND && !(mountFlags & AREA_MOUNT_FLAG_GROUND_ALLOWED)) continue; - } - else if (HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING)) - { - if (!(mountCapability->Flags & MOUNT_FLAG_CAN_SWIM)) + if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLYING && !(mountFlags & AREA_MOUNT_FLAG_FLYING_ALLOWED)) continue; - } - else if (!(mountCapability->Flags & 0x1)) // unknown flags, checked in 4.2.2 14545 client - { - if (!(mountCapability->Flags & 0x2)) + if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLOAT && !(mountFlags & AREA_MOUNT_FLAG_FLOAT_ALLOWED)) + continue; + if (mountCapability->Flags & MOUNT_CAPABILITY_FLAG_UNDERWATER && !(mountFlags & AREA_MOUNT_FLAG_UNDERWATER_ALLOWED)) continue; } - if (mountCapability->RequiredMap != -1 && int32(GetMapId()) != mountCapability->RequiredMap) + if (!isSubmerged) + { + if (!isInWater) + { + // player is completely out of water + if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_GROUND)) + continue; + } + else if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_UNDERWATER)) + continue; + } + else if (isInWater) + { + if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_UNDERWATER)) + continue; + } + else if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_FLOAT)) continue; - if (mountCapability->RequiredArea && (mountCapability->RequiredArea != zoneId && mountCapability->RequiredArea != areaId)) + if (mountCapability->RequiredMap != -1 && + int32(GetMapId()) != mountCapability->RequiredMap && + GetMap()->GetEntry()->rootPhaseMap != mountCapability->RequiredMap) + continue; + + if (mountCapability->RequiredArea && !IsInArea(areaId, mountCapability->RequiredArea)) continue; if (mountCapability->RequiredAura && !HasAura(mountCapability->RequiredAura)) continue; - if (mountCapability->RequiredSpell && (GetTypeId() != TYPEID_PLAYER || !ToPlayer()->HasSpell(mountCapability->RequiredSpell))) + if (mountCapability->RequiredSpell && !HasSpell(mountCapability->RequiredSpell)) continue; return mountCapability; } - return NULL; + return nullptr; } bool Unit::IsServiceProvider() const diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 08fb1e0249..6960d20246 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -32,6 +32,7 @@ #include "MovementStructures.h" #include "Vehicle.h" #include "GameTime.h" +#include "SpellAuraEffects.h" #define MOVEMENT_PACKET_TIME_DELAY 0 @@ -187,10 +188,6 @@ void WorldSession::HandleMoveWorldportAckOpcode() GetPlayer()->GetZoneAndAreaId(newzone, newarea); GetPlayer()->UpdateZone(newzone, newarea); - // can fly check - if (!_player->CanFlyInZone(newMap->GetId(), newarea)) - _player->RemoveAurasByType(SPELL_AURA_FLY); - // honorless target if (GetPlayer()->pvpInfo.IsHostile) GetPlayer()->CastSpell(GetPlayer(), 2479, true); @@ -266,10 +263,6 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recvPacket) // in friendly area else if (plMover->IsPvP() && !plMover->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP)) plMover->UpdatePvP(false, false); - - // can fly check - if (!plMover->CanFlyInZone(plMover->GetMapId(), newzone)) - plMover->RemoveAurasByType(SPELL_AURA_FLY); } // resummon pet diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h index c34b321651..411b0aaf0f 100644 --- a/src/server/game/Spells/Auras/SpellAuraDefines.h +++ b/src/server/game/Spells/Auras/SpellAuraDefines.h @@ -395,7 +395,7 @@ enum AuraType SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_TRIGGERED = 333, // Spells cast with this override have no cast time or power cost SPELL_AURA_MOD_BLIND = 334, // NYI SPELL_AURA_335 = 335, - SPELL_AURA_MOD_FLYING_RESTRICTIONS = 336, // NYI + SPELL_AURA_MOUNT_RESTRICTIONS = 336, SPELL_AURA_MOD_VENDOR_ITEMS_PRICES = 337, SPELL_AURA_MOD_DURABILITY_LOSS = 338, SPELL_AURA_INCREASE_SKILL_GAIN_CHANCE = 339, // NYI diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 6aae2023c6..a990705159 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -394,7 +394,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //333 SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS_TRIGGERED implemented in Unit::GetCastSpellInfo &AuraEffect::HandleNULL, //334 SPELL_AURA_MOD_BLIND &AuraEffect::HandleNULL, //335 SPELL_AURA_335 - &AuraEffect::HandleNULL, //336 SPELL_AURA_MOD_FLYING_RESTRICTIONS + &AuraEffect::HandleNULL, //336 SPELL_AURA_MOUNT_RESTRICTIONS implemented in Unit::GetMountCapability &AuraEffect::HandleNoImmediateEffect, //337 SPELL_AURA_MOD_VENDOR_ITEMS_PRICES &AuraEffect::HandleNoImmediateEffect, //338 SPELL_AURA_MOD_DURABILITY_LOSS &AuraEffect::HandleNULL, //339 SPELL_AURA_INCREASE_SKILL_GAIN_CHANCE diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 546b33d5a3..64731c06e3 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1649,11 +1649,10 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a // continent limitation (virtual continent) if (HasAttribute(SPELL_ATTR4_CAST_ONLY_IN_OUTLAND)) { - AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(area_id); - if (!areaEntry) - areaEntry = sAreaTableStore.LookupEntry(zone_id); + uint32 v_map = GetVirtualMapForMapAndZone(map_id, zone_id); + MapEntry const* mapEntry = sMapStore.LookupEntry(v_map); - if (!areaEntry || !areaEntry->IsFlyable() || !player->CanFlyInZone(map_id, zone_id)) + if (!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent()) return SPELL_FAILED_INCORRECT_AREA; }