mirror of
https://github.com/mangosfour/server.git
synced 2025-12-19 13:37:01 +00:00
[12195] Improve AreaTrigger teleport requirement checks
Implement AreaLockStatus concept by rsa Also drop basicly unneeded `areatrigger_teleport`.required_failed_text field. This concept is still in testing phase, please feedback results of some glitches that might exist! TODO: Use Player::GetAreaLockStatus or GetAreaTriggerLockStatus for other "CanEnter" checks as well. Signed-off-by: Schmoozerd <schmoozerd@scriptdev2.com>
This commit is contained in:
parent
7901613472
commit
9022705faf
22 changed files with 304 additions and 162 deletions
|
|
@ -688,42 +688,42 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
|
|||
|
||||
recv_data >> Trigger_ID;
|
||||
DEBUG_LOG("Trigger ID: %u", Trigger_ID);
|
||||
Player* player = GetPlayer();
|
||||
|
||||
if (GetPlayer()->IsTaxiFlying())
|
||||
if (player->IsTaxiFlying())
|
||||
{
|
||||
DEBUG_LOG("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID: %u", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), Trigger_ID);
|
||||
DEBUG_LOG("Player '%s' (GUID: %u) in flight, ignore Area Trigger ID: %u", player->GetName(), player->GetGUIDLow(), Trigger_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
|
||||
if (!atEntry)
|
||||
{
|
||||
DEBUG_LOG("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID: %u", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow(), Trigger_ID);
|
||||
DEBUG_LOG("Player '%s' (GUID: %u) send unknown (by DBC) Area Trigger ID: %u", player->GetName(), player->GetGUIDLow(), Trigger_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
// delta is safe radius
|
||||
const float delta = 5.0f;
|
||||
// check if player in the range of areatrigger
|
||||
Player* pl = GetPlayer();
|
||||
|
||||
if (!IsPointInAreaTriggerZone(atEntry, pl->GetMapId(), pl->GetPositionX(), pl->GetPositionY(), pl->GetPositionZ(), delta))
|
||||
// check if player in the range of areatrigger
|
||||
if (!IsPointInAreaTriggerZone(atEntry, player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), delta))
|
||||
{
|
||||
DEBUG_LOG("Player '%s' (GUID: %u) too far, ignore Area Trigger ID: %u", pl->GetName(), pl->GetGUIDLow(), Trigger_ID);
|
||||
DEBUG_LOG("Player '%s' (GUID: %u) too far, ignore Area Trigger ID: %u", player->GetName(), player->GetGUIDLow(), Trigger_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sScriptMgr.OnAreaTrigger(pl, atEntry))
|
||||
if (sScriptMgr.OnAreaTrigger(player, atEntry))
|
||||
return;
|
||||
|
||||
uint32 quest_id = sObjectMgr.GetQuestForAreaTrigger(Trigger_ID);
|
||||
if (quest_id && pl->isAlive() && pl->IsActiveQuest(quest_id))
|
||||
if (quest_id && player->isAlive() && player->IsActiveQuest(quest_id))
|
||||
{
|
||||
Quest const* pQuest = sObjectMgr.GetQuestTemplate(quest_id);
|
||||
if (pQuest)
|
||||
{
|
||||
if (pl->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE)
|
||||
pl->AreaExploredOrEventHappens(quest_id);
|
||||
if (player->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE)
|
||||
player->AreaExploredOrEventHappens(quest_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -731,19 +731,19 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
|
|||
if (sObjectMgr.IsTavernAreaTrigger(Trigger_ID))
|
||||
{
|
||||
// set resting flag we are in the inn
|
||||
if (pl->GetRestType() != REST_TYPE_IN_CITY)
|
||||
pl->SetRestType(REST_TYPE_IN_TAVERN, Trigger_ID);
|
||||
if (player->GetRestType() != REST_TYPE_IN_CITY)
|
||||
player->SetRestType(REST_TYPE_IN_TAVERN, Trigger_ID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (BattleGround* bg = pl->GetBattleGround())
|
||||
if (BattleGround* bg = player->GetBattleGround())
|
||||
{
|
||||
bg->HandleAreaTrigger(pl, Trigger_ID);
|
||||
bg->HandleAreaTrigger(player, Trigger_ID);
|
||||
return;
|
||||
}
|
||||
else if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(pl->GetCachedZoneId()))
|
||||
else if (OutdoorPvP* outdoorPvP = sOutdoorPvPMgr.GetScript(player->GetCachedZoneId()))
|
||||
{
|
||||
if (outdoorPvP->HandleAreaTrigger(pl, Trigger_ID))
|
||||
if (outdoorPvP->HandleAreaTrigger(player, Trigger_ID))
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -756,111 +756,103 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recv_data)
|
|||
if (!targetMapEntry)
|
||||
return;
|
||||
|
||||
if (!pl->isGameMaster())
|
||||
// ghost resurrected at enter attempt to dungeon with corpse (including fail enter cases)
|
||||
if (!player->isAlive() && targetMapEntry->IsDungeon())
|
||||
{
|
||||
// ghost resurrected at enter attempt to dungeon with corpse (including fail enter cases)
|
||||
if (!pl->isAlive() && targetMapEntry->IsDungeon())
|
||||
int32 corpseMapId = 0;
|
||||
if (Corpse* corpse = player->GetCorpse())
|
||||
corpseMapId = corpse->GetMapId();
|
||||
|
||||
// check back way from corpse to entrance
|
||||
uint32 instance_map = corpseMapId;
|
||||
do
|
||||
{
|
||||
int32 corpseMapId = 0;
|
||||
if (Corpse* corpse = pl->GetCorpse())
|
||||
corpseMapId = corpse->GetMapId();
|
||||
// most often fast case
|
||||
if (instance_map == targetMapEntry->MapID)
|
||||
break;
|
||||
|
||||
// check back way from corpse to entrance
|
||||
uint32 instance_map = corpseMapId;
|
||||
do
|
||||
{
|
||||
// most often fast case
|
||||
if (instance_map == targetMapEntry->MapID)
|
||||
break;
|
||||
|
||||
InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(instance_map);
|
||||
instance_map = instance ? instance->parent : 0;
|
||||
}
|
||||
while (instance_map);
|
||||
|
||||
// corpse not in dungeon or some linked deep dungeons
|
||||
if (!instance_map)
|
||||
{
|
||||
WorldPacket data(SMSG_AREA_TRIGGER_NO_CORPSE);
|
||||
pl->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// need find areatrigger to inner dungeon for landing point
|
||||
if (at->target_mapId != corpseMapId)
|
||||
{
|
||||
if (AreaTrigger const* corpseAt = sObjectMgr.GetMapEntranceTrigger(corpseMapId))
|
||||
{
|
||||
at = corpseAt;
|
||||
targetMapEntry = sMapStore.LookupEntry(at->target_mapId);
|
||||
}
|
||||
}
|
||||
|
||||
// now we can resurrect player, and then check teleport requirements
|
||||
pl->ResurrectPlayer(0.5f);
|
||||
pl->SpawnCorpseBones();
|
||||
InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(instance_map);
|
||||
instance_map = instance ? instance->parent : 0;
|
||||
}
|
||||
while (instance_map);
|
||||
|
||||
// check trigger requirements
|
||||
bool missingItem = false;
|
||||
bool missingLevel = false;
|
||||
bool missingQuest = false;
|
||||
|
||||
if (pl->getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL))
|
||||
missingLevel = true;
|
||||
|
||||
// must have one or the other, report the first one that's missing
|
||||
if (at->requiredItem)
|
||||
// corpse not in dungeon or some linked deep dungeons
|
||||
if (!instance_map)
|
||||
{
|
||||
if (!pl->HasItemCount(at->requiredItem, 1) &&
|
||||
(!at->requiredItem2 || !GetPlayer()->HasItemCount(at->requiredItem2, 1)))
|
||||
missingItem = true;
|
||||
}
|
||||
else if (at->requiredItem2 && !pl->HasItemCount(at->requiredItem2, 1))
|
||||
missingItem = true;
|
||||
|
||||
bool isRegularTargetMap = !targetMapEntry->IsDungeon() || pl->GetDifficulty(targetMapEntry->IsRaid()) == REGULAR_DIFFICULTY;
|
||||
|
||||
if (!isRegularTargetMap)
|
||||
{
|
||||
if (at->heroicKey)
|
||||
{
|
||||
if (!pl->HasItemCount(at->heroicKey, 1) &&
|
||||
(!at->heroicKey2 || !pl->HasItemCount(at->heroicKey2, 1)))
|
||||
missingItem = true;
|
||||
}
|
||||
else if (at->heroicKey2 && !pl->HasItemCount(at->heroicKey2, 1))
|
||||
missingItem = true;
|
||||
}
|
||||
|
||||
if (!isRegularTargetMap)
|
||||
{
|
||||
if (at->requiredQuestHeroic && !pl->GetQuestRewardStatus(at->requiredQuestHeroic))
|
||||
missingQuest = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (at->requiredQuest && !pl->GetQuestRewardStatus(at->requiredQuest))
|
||||
missingQuest = true;
|
||||
}
|
||||
|
||||
if (missingItem || missingLevel || missingQuest)
|
||||
{
|
||||
// hack for "Opening of the Dark Portal"
|
||||
if (missingQuest && at->target_mapId == 269)
|
||||
SendAreaTriggerMessage("%s", at->requiredFailedText.c_str());
|
||||
else if (missingQuest && targetMapEntry->IsContinent())// do not report anything for quest areatriggers
|
||||
return;
|
||||
// hack for TBC heroics
|
||||
else if (missingLevel && !targetMapEntry->IsRaid() && GetPlayer()->GetDifficulty(false) == DUNGEON_DIFFICULTY_HEROIC && targetMapEntry->addon == 1)
|
||||
SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), at->requiredLevel);
|
||||
else
|
||||
pl->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, pl->GetDifficulty(targetMapEntry->IsRaid()));
|
||||
WorldPacket data(SMSG_AREA_TRIGGER_NO_CORPSE);
|
||||
player->GetSession()->SendPacket(&data);
|
||||
return;
|
||||
}
|
||||
|
||||
// need find areatrigger to inner dungeon for landing point
|
||||
if (at->target_mapId != corpseMapId)
|
||||
{
|
||||
if (AreaTrigger const* corpseAt = sObjectMgr.GetMapEntranceTrigger(corpseMapId))
|
||||
{
|
||||
at = corpseAt;
|
||||
targetMapEntry = sMapStore.LookupEntry(at->target_mapId);
|
||||
}
|
||||
}
|
||||
|
||||
// now we can resurrect player, and then check teleport requirements
|
||||
player->ResurrectPlayer(0.5f);
|
||||
player->SpawnCorpseBones();
|
||||
}
|
||||
|
||||
GetPlayer()->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT);
|
||||
// check trigger requirements
|
||||
uint32 miscRequirement = 0;
|
||||
AreaLockStatus lockStatus = player->GetAreaTriggerLockStatus(at, player->GetDifficulty(targetMapEntry->IsRaid()), miscRequirement);
|
||||
switch (lockStatus)
|
||||
{
|
||||
case AREA_LOCKSTATUS_OK:
|
||||
player->TeleportTo(at->target_mapId, at->target_X, at->target_Y, at->target_Z, at->target_Orientation, TELE_TO_NOT_LEAVE_TRANSPORT);
|
||||
break;
|
||||
case AREA_LOCKSTATUS_TOO_LOW_LEVEL:
|
||||
SendAreaTriggerMessage(GetMangosString(LANG_LEVEL_MINREQUIRED), miscRequirement);
|
||||
break;
|
||||
case AREA_LOCKSTATUS_ZONE_IN_COMBAT:
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ZONE_IN_COMBAT);
|
||||
break;
|
||||
case AREA_LOCKSTATUS_INSTANCE_IS_FULL:
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_MAX_PLAYERS);
|
||||
break;
|
||||
case AREA_LOCKSTATUS_QUEST_NOT_COMPLETED:
|
||||
if (at->target_mapId == 269) // Exception for Black Morass
|
||||
{
|
||||
SendAreaTriggerMessage(GetMangosString(LANG_TELEREQ_QUEST_BLACK_MORASS));
|
||||
break;
|
||||
}
|
||||
else if (targetMapEntry->IsContinent()) // do not report anything for quest areatrigge
|
||||
{
|
||||
DEBUG_LOG("HandleAreaTriggerOpcode: LockAreaStatus %u, do not teleport, no message sent (trigger %u)", lockStatus, Trigger_ID);
|
||||
break;
|
||||
}
|
||||
// No break here!
|
||||
case AREA_LOCKSTATUS_MISSING_ITEM:
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, player->GetDifficulty(targetMapEntry->IsRaid()));
|
||||
break;
|
||||
case AREA_LOCKSTATUS_MISSING_DIFFICULTY:
|
||||
{
|
||||
Difficulty difficulty = player->GetDifficulty(targetMapEntry->IsRaid());
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_DIFFICULTY, difficulty > RAID_DIFFICULTY_10MAN_HEROIC ? RAID_DIFFICULTY_10MAN_HEROIC : difficulty);
|
||||
break;
|
||||
}
|
||||
case AREA_LOCKSTATUS_INSUFFICIENT_EXPANSION:
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_INSUF_EXPAN_LVL, miscRequirement);
|
||||
break;
|
||||
case AREA_LOCKSTATUS_NOT_ALLOWED:
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_MAP_NOT_ALLOWED);
|
||||
break;
|
||||
case AREA_LOCKSTATUS_RAID_LOCKED:
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_NEED_GROUP);
|
||||
break;
|
||||
case AREA_LOCKSTATUS_UNKNOWN_ERROR:
|
||||
player->SendTransferAborted(at->target_mapId, TRANSFER_ABORT_ERROR);
|
||||
break;
|
||||
default:
|
||||
sLog.outError("HandleAreaTriggerOpcode: unhandled LockAreaStatus %u, when %s attempts to use area-trigger %u", lockStatus, player->GetGuidStr().c_str(), Trigger_ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleUpdateAccountData(WorldPacket& recv_data)
|
||||
|
|
@ -1416,7 +1408,8 @@ void WorldSession::HandleSetDungeonDifficultyOpcode(WorldPacket& recv_data)
|
|||
return;
|
||||
}
|
||||
|
||||
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC)
|
||||
// Exception to set mode to normal for low-level players
|
||||
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC && mode > REGULAR_DIFFICULTY)
|
||||
return;
|
||||
|
||||
if (Group* pGroup = _player->GetGroup())
|
||||
|
|
@ -1460,7 +1453,8 @@ void WorldSession::HandleSetRaidDifficultyOpcode(WorldPacket& recv_data)
|
|||
return;
|
||||
}
|
||||
|
||||
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC)
|
||||
// Exception to set mode to normal for low-level players
|
||||
if (_player->getLevel() < LEVELREQUIREMENT_HEROIC && mode > REGULAR_DIFFICULTY)
|
||||
return;
|
||||
|
||||
if (Group* pGroup = _player->GetGroup())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue