diff --git a/src/game/BattleGround/BattleGroundHandler.cpp b/src/game/BattleGround/BattleGroundHandler.cpp index 8899c16b..68949a3c 100644 --- a/src/game/BattleGround/BattleGroundHandler.cpp +++ b/src/game/BattleGround/BattleGroundHandler.cpp @@ -62,6 +62,12 @@ void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket& recv_data) if (bgTypeId == BATTLEGROUND_TYPE_NONE) { return; } + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId)) + { + SendNotification(LANG_BG_IS_DISABLED); + return; + } + if (!_player->GetBGAccessByLevel(bgTypeId)) { // temp, must be gossip message... @@ -103,12 +109,6 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recv_data) DEBUG_LOG("WORLD: Received opcode CMSG_BATTLEMASTER_JOIN from %s", guid.GetString().c_str()); - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId)) - { - ChatHandler(this).SendSysMessage(LANG_BG_IS_DISABLED); - return; - } - // can do this, since it's battleground, not arena BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId); diff --git a/src/game/ChatCommands/Level2.cpp b/src/game/ChatCommands/Level2.cpp index 6022bbeb..89b9c8d9 100644 --- a/src/game/ChatCommands/Level2.cpp +++ b/src/game/ChatCommands/Level2.cpp @@ -274,7 +274,7 @@ bool ChatHandler::HandleTriggerCommand(char* args) AreaTrigger const* at = sObjectMgr.GetAreaTrigger(atEntry->id); if (at) - { PSendSysMessage(LANG_TRIGGER_REQ_LEVEL, at->requiredLevel); } + { PSendSysMessage(LANG_TRIGGER_CONDITION, at->condition); } if (uint32 quest_id = sObjectMgr.GetQuestForAreaTrigger(atEntry->id)) { @@ -282,25 +282,6 @@ bool ChatHandler::HandleTriggerCommand(char* args) ShowQuestListHelper(quest_id, loc_idx, pl); } - if (at) - { - if (at->requiredItem || at->requiredItem2) - { - SendSysMessage(LANG_TRIGGER_REQ_ITEMS); - - if (at->requiredItem) - { ShowItemListHelper(at->requiredItem, loc_idx, pl); } - if (at->requiredItem2) - { ShowItemListHelper(at->requiredItem2, loc_idx, pl); } - } - - if (at->requiredQuest) - { - SendSysMessage(LANG_TRIGGER_REQ_QUEST); - ShowQuestListHelper(at->requiredQuest, loc_idx, pl); - } - } - return true; } diff --git a/src/game/Object/Creature.cpp b/src/game/Object/Creature.cpp index b659e1e0..8ed43d9c 100644 --- a/src/game/Object/Creature.cpp +++ b/src/game/Object/Creature.cpp @@ -55,6 +55,7 @@ #include "CellImpl.h" #include "movement/MoveSplineInit.h" #include "CreatureLinkingMgr.h" +#include "DisableMgr.h" #ifdef ENABLE_ELUNA #include "LuaEngine.h" #endif /* ENABLE_ELUNA */ @@ -150,7 +151,7 @@ Creature::Creature(CreatureSubtype subtype) : Unit(), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_AI_locked(false), m_IsDeadByDefault(false), m_temporaryFactionFlags(TEMPFACTION_NONE), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), - m_creatureInfo(NULL) + m_creatureInfo(NULL), m_PlayerDamageReq(0) { /* Loot data */ hasBeenLootedOnce = false; @@ -187,7 +188,7 @@ void Creature::AddToWorld() #endif /* ENABLE_ELUNA */ ///- Register the creature for guid lookup - if (!IsInWorld() && GetObjectGuid().GetHigh() == HIGHGUID_UNIT) + if (!IsInWorld() && GetObjectGuid().IsCreature()) { GetMap()->GetObjectsStore().insert(GetObjectGuid(), (Creature*)this); } Unit::AddToWorld(); @@ -211,7 +212,7 @@ void Creature::RemoveFromWorld() #endif /* ENABLE_ELUNA */ ///- Remove the creature from the accessor - if (IsInWorld() && GetObjectGuid().GetHigh() == HIGHGUID_UNIT) + if (IsInWorld() && GetObjectGuid().IsCreature()) { GetMap()->GetObjectsStore().erase(GetObjectGuid(), (Creature*)NULL); } Unit::RemoveFromWorld(); @@ -1251,6 +1252,8 @@ void Creature::SelectLevel(const CreatureInfo* cinfo, float percentHealth /*= 10 else { SetHealthPercent(percentHealth); } + ResetPlayerDamageReq(); + SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, float(health)); // all power types @@ -1335,6 +1338,12 @@ float Creature::_GetDamageMod(int32 Rank) } } +void Creature::LowerPlayerDamageReq(uint32 unDamage) +{ + if (m_PlayerDamageReq) + m_PlayerDamageReq > unDamage ? m_PlayerDamageReq -= unDamage : m_PlayerDamageReq = 0; +} + float Creature::_GetSpellDamageMod(int32 Rank) { switch (Rank) // define rates for each elite rank @@ -1651,6 +1660,7 @@ void Creature::SetDeathState(DeathState s) // Dynamic flags must be set on Tapped by default. SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_NONE); LoadCreatureAddon(true); + ResetPlayerDamageReq(); // Flags after LoadCreatureAddon. Any spell in *addon // will not be able to adjust these. @@ -2116,7 +2126,7 @@ bool Creature::MeetsSelectAttackingRequirement(Unit* pTarget, SpellEntry const* if (selectFlags & SELECT_FLAG_NOT_IN_MELEE_RANGE && CanReachWithMeleeAttack(pTarget)) { return false; } - if (selectFlags & SELECT_FLAG_IN_LOS && !IsWithinLOSInMap(pTarget)) + if (selectFlags & SELECT_FLAG_IN_LOS && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, pSpellInfo->Id, pTarget, SPELL_DISABLE_LOS) && !IsWithinLOSInMap(pTarget)) { return false; } if (pSpellInfo) diff --git a/src/game/Object/Creature.h b/src/game/Object/Creature.h index edfba0ce..ebf11da9 100644 --- a/src/game/Object/Creature.h +++ b/src/game/Object/Creature.h @@ -684,6 +684,9 @@ class Creature : public Unit Player* GetLootRecipient() const; // use group cases as prefered Group* GetGroupLootRecipient() const; bool IsTappedBy(Player const* player) const; + bool IsDamageEnoughForLootingAndReward() const { return m_PlayerDamageReq == 0; } + void LowerPlayerDamageReq(uint32 unDamage); + void ResetPlayerDamageReq() { m_PlayerDamageReq = GetHealth() / 2; } /** * function indicating whether the whether the creature has a looter recipient defined (either a group ID, either a player GUID). @@ -842,6 +845,7 @@ class Creature : public Unit MovementGeneratorType m_defaultMovementType; Cell m_currentCell; // store current cell where creature listed uint32 m_equipmentId; + uint32 m_PlayerDamageReq; // below fields has potential for optimization bool m_AlreadyCallAssistance; diff --git a/src/game/Object/CreatureAI.h b/src/game/Object/CreatureAI.h index ec9e4e34..b68262c5 100644 --- a/src/game/Object/CreatureAI.h +++ b/src/game/Object/CreatureAI.h @@ -147,7 +147,7 @@ class CreatureAI * Called for reaction at stopping attack at no attackers or targets * This is called usually in Unit::SelectHostileTarget, if no more target exists */ - virtual void EnterEvadeMode() {} + virtual void EnterEvadeMode() { m_creature->ResetPlayerDamageReq(); } /** * Called at reaching home after MoveTargetedHome diff --git a/src/game/Object/CreatureEventAI.cpp b/src/game/Object/CreatureEventAI.cpp index 0ff406a0..b9a1967f 100644 --- a/src/game/Object/CreatureEventAI.cpp +++ b/src/game/Object/CreatureEventAI.cpp @@ -1163,6 +1163,7 @@ void CreatureEventAI::EnterEvadeMode() if (i->Event.event_type == EVENT_T_EVADE) { ProcessEvent(*i); } } + m_creature->ResetPlayerDamageReq(); } void CreatureEventAI::JustDied(Unit* killer) diff --git a/src/game/Object/LootMgr.cpp b/src/game/Object/LootMgr.cpp index 4738d9fc..879df85f 100644 --- a/src/game/Object/LootMgr.cpp +++ b/src/game/Object/LootMgr.cpp @@ -31,6 +31,7 @@ #include "SharedDefines.h" #include "DBCStores.h" #include "SQLStorages.h" +#include "DisableMgr.h" static eConfigFloatValues const qualityToRate[MAX_ITEM_QUALITY] = { @@ -471,7 +472,7 @@ void Loot::AddItem(LootStoreItem const& item) if (m_questItems.size() < MAX_NR_QUEST_ITEMS) { m_questItems.push_back(LootItem(item)); } } - else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop + else if (items.size() < MAX_NR_LOOT_ITEMS && !DisableMgr::IsDisabledFor(DISABLE_TYPE_ITEM_DROP, item.itemid)) // Non-quest drop { items.push_back(LootItem(item)); @@ -1003,7 +1004,7 @@ bool LootTemplate::LootGroup::HasStartingQuestDropForPlayer(Player const* player void LootTemplate::LootGroup::Process(Loot& loot) const { LootStoreItem const* item = Roll(); - if (item != NULL) + if (item != NULL && !DisableMgr::IsDisabledFor(DISABLE_TYPE_ITEM_DROP, item->itemid)) { loot.AddItem(*item); } } @@ -1101,7 +1102,7 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8 // Rolling non-grouped items for (LootStoreItemList::const_iterator i = Entries.begin() ; i != Entries.end() ; ++i) { - if (!i->Roll(rate)) + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_ITEM_DROP, i->itemid) || !i->Roll(rate)) { continue; } // Bad luck for the entry if (i->mincountOrRef < 0) // References processing diff --git a/src/game/Object/Object.cpp b/src/game/Object/Object.cpp index d81b4be4..aaa2b086 100644 --- a/src/game/Object/Object.cpp +++ b/src/game/Object/Object.cpp @@ -251,72 +251,49 @@ void Object::DestroyForPlayer(Player* target) const void Object::BuildMovementUpdate(ByteBuffer* data, uint8 updateFlags) const { - uint32 moveFlags = MOVEFLAG_NONE; + Unit const* unit = NULL; + uint32 highGuid = 0; + MovementFlags moveflags = MOVEFLAG_NONE; - *data << uint8(updateFlags); // update flags + switch (m_objectTypeId) + { + case TYPEID_OBJECT: + case TYPEID_ITEM: + case TYPEID_CONTAINER: + case TYPEID_GAMEOBJECT: + case TYPEID_DYNAMICOBJECT: + case TYPEID_CORPSE: + highGuid = uint32(GetObjectGuid().GetHigh()); + break; + + case TYPEID_PLAYER: + // TODO: this code must not be here + if (static_cast(this)->GetTransport()) + ((Unit*)this)->m_movementInfo.AddMovementFlag(MOVEFLAG_ONTRANSPORT); + else + ((Unit*)this)->m_movementInfo.RemoveMovementFlag(MOVEFLAG_ONTRANSPORT); + + case TYPEID_UNIT: + unit = static_cast(this); + moveflags = unit->m_movementInfo.GetMovementFlags(); + break; + + default: + break; + } + + *data << uint8(updateFlags); if (updateFlags & UPDATEFLAG_LIVING) { - if (m_objectTypeId == TYPEID_PLAYER && ((Player*)this)->GetTransport()) + MANGOS_ASSERT(unit); + if (unit->IsStopped() && unit->m_movementInfo.HasMovementFlag(MOVEFLAG_SPLINE_ENABLED)) { - moveFlags |= MOVEFLAG_ONTRANSPORT; - } - - float x; // not used anywhere - if (m_objectTypeId == TYPEID_UNIT && ((Unit*)this)->movespline && ((Unit*)this)->GetMotionMaster()->GetDestination(x, x, x)) - { - moveFlags |= MOVEFLAG_WALK_MODE | MOVEFLAG_FORWARD | MOVEFLAG_SPLINE_ENABLED; - } - - *data << uint32(moveFlags); // movement flags - *data << uint32(WorldTimer::getMSTime()); // time (in milliseconds) - } - - if (updateFlags & UPDATEFLAG_HAS_POSITION) // 0x40 - { - if (m_objectTypeId == TYPEID_PLAYER && ((Player*)this)->GetTransport()) - { - *data << float(((Player*)this)->GetTransport()->GetPositionX()); - *data << float(((Player*)this)->GetTransport()->GetPositionY()); - *data << float(((Player*)this)->GetTransport()->GetPositionZ()); - *data << float(((Player*)this)->GetTransport()->GetOrientation()); - - *data << ObjectGuid(((Player*)this)->GetTransport()->GetObjectGuid()); - *data << float(((Player*)this)->GetTransOffsetX()); - *data << float(((Player*)this)->GetTransOffsetY()); - *data << float(((Player*)this)->GetTransOffsetZ()); - *data << float(((Player*)this)->GetTransOffsetO()); - } - else if (GetObjectGuid().GetHigh() == HIGHGUID_TRANSPORT) - { - *data << float(0); - *data << float(0); - *data << float(0); - *data << float(((WorldObject*)this)->GetOrientation()); - } - else - { - *data << float(((WorldObject*)this)->GetPositionX()); - *data << float(((WorldObject*)this)->GetPositionY()); - *data << float(((WorldObject*)this)->GetPositionZ()); - *data << float(((WorldObject*)this)->GetOrientation()); - } - } - - if (updateFlags & UPDATEFLAG_LIVING) // 0x20 - { - Unit* unit = ((Unit*)this); - - *data << (float)0; - - if (moveFlags & 0x02000) // update self MOVEFLAG_FALLING - { - *data << (float)0; - *data << (float)1.0; - *data << (float)0; - *data << (float)0; + sLog.outError("%s is not moving but have spline movement enabled!", GetGuidStr().c_str()); + ((Unit*)this)->m_movementInfo.RemoveMovementFlag(MovementFlags(MOVEFLAG_SPLINE_ENABLED | MOVEFLAG_FORWARD)); } + *data << unit->m_movementInfo; // Unit speeds *data << float(unit->GetSpeed(MOVE_WALK)); *data << float(unit->GetSpeed(MOVE_RUN)); @@ -325,25 +302,33 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint8 updateFlags) const *data << float(unit->GetSpeed(MOVE_SWIM_BACK)); *data << float(unit->GetSpeed(MOVE_TURN_RATE)); - if (m_objectTypeId == TYPEID_UNIT) - { - if (moveFlags & MOVEFLAG_SPLINE_ENABLED && ((Unit*)this)->movespline) // 0x00400000 - { - Movement::PacketBuilder::WriteCreate((*((Unit*)this)->movespline), *data); - } - } + if (unit->m_movementInfo.HasMovementFlag(MOVEFLAG_SPLINE_ENABLED)) + Movement::PacketBuilder::WriteCreate(*unit->movespline, *data); + } + else if (updateFlags & UPDATEFLAG_HAS_POSITION) + { + *data << ((WorldObject*)this)->GetPositionX(); + *data << ((WorldObject*)this)->GetPositionY(); + *data << ((WorldObject*)this)->GetPositionZ(); + *data << ((WorldObject*)this)->GetOrientation(); } - if (updateFlags & UPDATEFLAG_ALL) // 0x10 - { + if (updateFlags & UPDATEFLAG_HIGHGUID) + *data << highGuid; + + if (updateFlags & UPDATEFLAG_ALL) *data << (uint32)0x1; + + if (updateFlags & UPDATEFLAG_FULLGUID) + { + if (unit && unit->getVictim()) + *data << unit->getVictim()->GetPackGUID(); + else + data->appendPackGUID(0); } - // 0x2 if (updateFlags & UPDATEFLAG_TRANSPORT) - { - *data << uint32(WorldTimer::getMSTime()); // ms time - } + *data << uint32(WorldTimer::getMSTime()); } void Object::BuildValuesUpdate(uint8 updatetype, ByteBuffer* data, UpdateMask* updateMask, Player* target) const diff --git a/src/game/Object/ObjectMgr.cpp b/src/game/Object/ObjectMgr.cpp index fa1f8ef0..8f5828f0 100644 --- a/src/game/Object/ObjectMgr.cpp +++ b/src/game/Object/ObjectMgr.cpp @@ -131,6 +131,38 @@ bool operator < (const HonorStanding& lhs, const HonorStanding& rhs) return lhs.honorPoints > rhs.honorPoints; } +// TODO improve the algorithm based on conditions +bool AreaTrigger::IsLessOrEqualThan(AreaTrigger const* l) const // Expected to have same map +{ + MANGOS_ASSERT(target_mapId == l->target_mapId); + if (!condition) + { return true; } + + if (!l->condition) + { return false; } + + if (condition == l->condition) + { return true; } + + // most conditions for AT have level requirement + // let's order by the least restrictive + const PlayerCondition* pCond1 = sConditionStorage.LookupEntry(condition); + const PlayerCondition* pCond2 = sConditionStorage.LookupEntry(l->condition); + if (pCond1->m_condition == CONDITION_LEVEL && pCond2->m_condition == CONDITION_LEVEL) + { + return (pCond1->m_value1 <= pCond2->m_value1); + } + if (pCond1->m_condition == CONDITION_LEVEL) + { + return false; + } + if (pCond2->m_condition == CONDITION_LEVEL) + { + return true; + } + return false; +} + ObjectMgr::ObjectMgr() : m_AuctionIds("Auction ids"), m_GuildIds("Guild ids"), @@ -1062,7 +1094,7 @@ void ObjectMgr::LoadCreatures() uint32 guid = fields[ 0].GetUInt32(); uint32 entry = fields[ 1].GetUInt32(); - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_CREATURE_SPAWN, guid)) + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_CREATURE_SPAWN, entry, NULL, 0, guid)) { sLog.outDebug("Creature guid %u (entry %u) spawning is disabled.", guid, entry); continue; @@ -1236,7 +1268,7 @@ void ObjectMgr::LoadGameObjects() uint32 guid = fields[ 0].GetUInt32(); uint32 entry = fields[ 1].GetUInt32(); - if (DisableMgr::IsDisabledFor(DISABLE_TYPE_GAMEOBJECT_SPAWN, guid)) + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_GAMEOBJECT_SPAWN, entry, NULL, 0, guid)) { sLog.outDebug("Gameobject guid %u (entry %u) spawning is disabled.", guid, entry); continue; @@ -5065,8 +5097,8 @@ void ObjectMgr::LoadAreaTriggerTeleports() uint32 count = 0; - // 0 1 2 3 4 5 6 7 8 9 - QueryResult* result = WorldDatabase.Query("SELECT id, required_level, required_item, required_item2, required_quest_done, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); + // 0 1 2 3 4 5 6 7 8 9 + QueryResult* result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation, condition_id FROM areatrigger_teleport"); if (!result) { BarGoLink bar(1); @@ -5090,15 +5122,12 @@ void ObjectMgr::LoadAreaTriggerTeleports() AreaTrigger at; - at.requiredLevel = fields[1].GetUInt8(); - at.requiredItem = fields[2].GetUInt32(); - at.requiredItem2 = fields[3].GetUInt32(); - at.requiredQuest = fields[4].GetUInt32(); - at.target_mapId = fields[5].GetUInt32(); - at.target_X = fields[6].GetFloat(); - at.target_Y = fields[7].GetFloat(); - at.target_Z = fields[8].GetFloat(); - at.target_Orientation = fields[9].GetFloat(); + at.target_mapId = fields[1].GetUInt32(); + at.target_X = fields[2].GetFloat(); + at.target_Y = fields[3].GetFloat(); + at.target_Z = fields[4].GetFloat(); + at.target_Orientation = fields[5].GetFloat(); + at.condition = fields[6].GetUInt16(); AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID); if (!atEntry) @@ -5107,36 +5136,6 @@ void ObjectMgr::LoadAreaTriggerTeleports() continue; } - if (at.requiredItem) - { - ItemPrototype const* pProto = GetItemPrototype(at.requiredItem); - if (!pProto) - { - sLog.outError("Table `areatrigger_teleport` has nonexistent key item %u for trigger %u, removing key requirement.", at.requiredItem, Trigger_ID); - at.requiredItem = 0; - } - } - - if (at.requiredItem2) - { - ItemPrototype const* pProto = GetItemPrototype(at.requiredItem2); - if (!pProto) - { - sLog.outError("Table `areatrigger_teleport` has nonexistent second key item %u for trigger %u, remove key requirement.", at.requiredItem2, Trigger_ID); - at.requiredItem2 = 0; - } - } - - if (at.requiredQuest) - { - QuestMap::iterator qReqItr = mQuestTemplates.find(at.requiredQuest); - if (qReqItr == mQuestTemplates.end()) - { - sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent required quest %u for trigger %u, remove quest done requirement.", at.requiredQuest, Trigger_ID); - at.requiredQuest = 0; - } - } - MapEntry const* mapEntry = sMapStore.LookupEntry(at.target_mapId); if (!mapEntry) { @@ -5150,6 +5149,12 @@ void ObjectMgr::LoadAreaTriggerTeleports() continue; } + if (at.condition && !sConditionStorage.LookupEntry(at.condition)) + { + sLog.outErrorDb("Table `areatrigger_teleport` has nonexistent condition (ID:%u) for Area trigger (ID:%u).", at.condition, Trigger_ID); + continue; + } + mAreaTriggers[Trigger_ID] = at; } while (result->NextRow()); @@ -6870,10 +6875,10 @@ void ObjectMgr::LoadFishingBaseSkillLevel() } // Check if a player meets condition conditionId -bool ObjectMgr::IsPlayerMeetToCondition(uint16 conditionId, Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const +bool ObjectMgr::IsPlayerMeetToCondition(uint16 conditionId, Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType, ConditionEntry* entry) const { if (const PlayerCondition* condition = sConditionStorage.LookupEntry(conditionId)) - { return condition->Meets(pPlayer, map, source, conditionSourceType); } + { return condition->Meets(pPlayer, map, source, conditionSourceType, entry); } return false; } @@ -6891,15 +6896,23 @@ char const* conditionSourceToStr[] = "vendor's item check", "spell_area check", "npc_spellclick_spells check", // Unused. For 3.x and later. - "DBScript engine" + "DBScript engine", + "area trigger check" }; // Checks if player meets the condition -bool PlayerCondition::Meets(Player const* player, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const +bool PlayerCondition::Meets(Player const* player, Map const* map, WorldObject const* source, ConditionSource conditionSourceType, ConditionEntry* entry) const { DEBUG_LOG("Condition-System: Check condition %u, type %i - called from %s with params plr: %s, map %i, src %s", m_entry, m_condition, conditionSourceToStr[conditionSourceType], player ? player->GetGuidStr().c_str() : "", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : ""); + if (entry) + { + entry->type = m_condition; + entry->param1 = m_value1; + entry->param2 = m_value2; + } + if (!CheckParamRequirements(player, map, source, conditionSourceType)) { return false; } @@ -6907,13 +6920,13 @@ bool PlayerCondition::Meets(Player const* player, Map const* map, WorldObject co { case CONDITION_NOT: // Checked on load - return !sConditionStorage.LookupEntry(m_value1)->Meets(player, map, source, conditionSourceType); + return !sConditionStorage.LookupEntry(m_value1)->Meets(player, map, source, conditionSourceType, entry); case CONDITION_OR: // Checked on load - return sConditionStorage.LookupEntry(m_value1)->Meets(player, map, source, conditionSourceType) || sConditionStorage.LookupEntry(m_value2)->Meets(player, map, source, conditionSourceType); + return sConditionStorage.LookupEntry(m_value1)->Meets(player, map, source, conditionSourceType, entry) || sConditionStorage.LookupEntry(m_value2)->Meets(player, map, source, conditionSourceType, entry); case CONDITION_AND: // Checked on load - return sConditionStorage.LookupEntry(m_value1)->Meets(player, map, source, conditionSourceType) && sConditionStorage.LookupEntry(m_value2)->Meets(player, map, source, conditionSourceType); + return sConditionStorage.LookupEntry(m_value1)->Meets(player, map, source, conditionSourceType, entry) && sConditionStorage.LookupEntry(m_value2)->Meets(player, map, source, conditionSourceType, entry); case CONDITION_NONE: return true; // empty condition, always met case CONDITION_AURA: @@ -7168,6 +7181,15 @@ bool PlayerCondition::Meets(Player const* player, Map const* map, WorldObject co } return pGo; } + case CONDITION_PVP_RANK: + { + switch (m_value2) + { + case 0: return player->GetHonorRankInfo().rank == m_value1; + case 1: return player->GetHonorRankInfo().rank >= m_value1; + case 2: return player->GetHonorRankInfo().rank <= m_value1; + } + } default: return false; } @@ -7238,6 +7260,14 @@ bool PlayerCondition::CheckParamRequirements(Player const* pPlayer, Map const* m return true; } break; + case CONDITION_PVP_RANK: + if (!pPlayer) + { + sLog.outErrorDb("CONDITION %u type %u used with bad parameters, called from %s, used with %s, map %i, src %s", + m_entry, m_condition, conditionSourceToStr[conditionSourceType], pPlayer ? pPlayer->GetGuidStr().c_str() : "NULL", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "NULL"); + return false; + } + break; default: if (!pPlayer) { @@ -7600,6 +7630,15 @@ bool PlayerCondition::IsValid(uint16 entry, ConditionType condition, uint32 valu } break; } + case CONDITION_PVP_RANK: + { + if (value2 > 2) + { + sLog.outErrorDb("PVP rank condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", entry, condition, value2); + return false; + } + break; + } case CONDITION_NONE: break; default: diff --git a/src/game/Object/ObjectMgr.h b/src/game/Object/ObjectMgr.h index a74cad2b..3e2cd61e 100644 --- a/src/game/Object/ObjectMgr.h +++ b/src/game/Object/ObjectMgr.h @@ -66,10 +66,7 @@ typedef UNORDERED_MAP GameTeleMap; struct AreaTrigger { - uint8 requiredLevel; - uint32 requiredItem; - uint32 requiredItem2; - uint32 requiredQuest; + uint32 condition; uint32 target_mapId; float target_X; float target_Y; @@ -79,15 +76,10 @@ struct AreaTrigger // Operators bool IsMinimal() const { - return requiredLevel == 0 && requiredItem == 0 && requiredItem2 == 0 && requiredQuest == 0; + return condition == 0; } - bool IsLessOrEqualThan(AreaTrigger const* l) const // Expected to have same map - { - MANGOS_ASSERT(target_mapId == l->target_mapId); - return requiredLevel <= l->requiredLevel && requiredItem <= l->requiredItem && requiredItem2 <= l->requiredItem2 - && requiredQuest <= l->requiredQuest; - } + bool IsLessOrEqualThan(AreaTrigger const* l) const; }; typedef std::map < uint32/*player guid*/, uint32/*instance*/ > CellCorpseSet; @@ -330,6 +322,7 @@ enum ConditionType // value2: if != 0 only consider players in range of this value CONDITION_CREATURE_IN_RANGE = 37, // value1: creature entry; value2: range; returns only alive creatures CONDITION_GAMEOBJECT_IN_RANGE = 38, // value1: gameobject entry; value2: range + CONDITION_PVP_RANK = 39, // value1: rank; value2: 0 = eq, 1 = equal or higher, 2 = equal or less }; enum ConditionSource // From where was the condition called? @@ -344,10 +337,20 @@ enum ConditionSource // From where was th CONDITION_FROM_SPELL_AREA = 7, // Used to check a condition from spell_area table CONDITION_FROM_RESERVED_1 = 8, // reserved for 3.x and later CONDITION_FROM_DBSCRIPTS = 9, // Used to check a condition from DB Scripts Engine + CONDITION_AREA_TRIGGER = 10, // Used to check a condition from CMSG_AREATRIGGER +}; + +struct ConditionEntry +{ + ConditionEntry() : type(CONDITION_NONE), param1(0), param2(0) {} + ConditionType type; + uint32 param1; + uint32 param2; }; class PlayerCondition { + friend struct AreaTrigger; public: // Default constructor, required for SQL Storage (Will give errors if used elsewise) PlayerCondition() : m_entry(0), m_condition(CONDITION_AND), m_value1(0), m_value2(0) {} @@ -365,7 +368,11 @@ class PlayerCondition static bool CanBeUsedWithoutPlayer(uint16 entry); // Checks if the player meets the condition - bool Meets(Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const; + // if the param entry is not null, it will be filled at return as follows: + // - if function fails, entry will contain the first faulty condition + // - if function succeeds, entry will contain the last condition checked (if chained) + // entry is only useful on failure case + bool Meets(Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType, ConditionEntry* entry = NULL) const; private: bool CheckParamRequirements(Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const; @@ -987,7 +994,7 @@ class ObjectMgr LocaleConstant GetLocaleForIndex(int i); // Check if a player meets condition conditionId - bool IsPlayerMeetToCondition(uint16 conditionId, Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const; + bool IsPlayerMeetToCondition(uint16 conditionId, Player const* pPlayer, Map const* map, WorldObject const* source, ConditionSource conditionSourceType, ConditionEntry* entry = NULL) const; GameTele const* GetGameTele(uint32 id) const { diff --git a/src/game/Object/Pet.cpp b/src/game/Object/Pet.cpp index 84e5f9bd..b2ee637b 100644 --- a/src/game/Object/Pet.cpp +++ b/src/game/Object/Pet.cpp @@ -2073,7 +2073,6 @@ void Pet::UpdateSpeed(UnitMoveType mtype, bool forced, float ratio) case MOVE_WALK: break; case MOVE_RUN: - { if (!m_attacking && owner->HasAura(19596)) // Bestial Swiftness: prevent while following { AuraList const& auras = GetAurasByType(SPELL_AURA_MOD_INCREASE_SPEED); @@ -2084,10 +2083,9 @@ void Pet::UpdateSpeed(UnitMoveType mtype, bool forced, float ratio) else main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_SPEED); - stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_SPEED_ALWAYS); + stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_SPEED_ALWAYS); non_stack_bonus = (100.0f + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_SPEED_NOT_STACK)) / 100.0f; break; - } case MOVE_RUN_BACK: return; case MOVE_SWIM: diff --git a/src/game/Object/PetAI.cpp b/src/game/Object/PetAI.cpp index 0d9ecd1d..175331e2 100644 --- a/src/game/Object/PetAI.cpp +++ b/src/game/Object/PetAI.cpp @@ -85,6 +85,7 @@ void PetAI::AttackStart(Unit* u) // thus with the following clear the original TMG gets invalidated and crash, doh // hope it doesn't start to leak memory without this :-/ // i_pet->Clear(); + m_creature->UpdateSpeed(MOVE_RUN, false); HandleMovementOnAttackStart(u); inCombat = true; } @@ -117,6 +118,7 @@ void PetAI::_stopAttack() if (owner && m_creature->GetCharmInfo() && m_creature->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW)) { m_creature->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + m_creature->UpdateSpeed(MOVE_RUN, false); } else { diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index 134ea99e..60e29be2 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -14071,8 +14071,8 @@ bool Player::IsTappedByMeOrMyGroup(Creature* creature) * Called from Object::BuildValuesUpdate */ bool Player::isAllowedToLoot(Creature* creature) { - /* Nobody tapped the monster (solo kill by another NPC) */ - if (!creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED)) + /* Nobody tapped the monster (kill either solo or mostly by another NPC) */ + if (!creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED) || !creature->IsDamageEnoughForLootingAndReward()) { return false; } /* If we there is a loot recipient, assign it to recipient */ @@ -17678,15 +17678,27 @@ void Player::SendTransferAbortedByLockStatus(MapEntry const* mapEntry, AreaLockS switch (lockStatus) { - case AREA_LOCKSTATUS_TOO_LOW_LEVEL: + case AREA_LOCKSTATUS_LEVEL_TOO_LOW: GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_LEVEL_MINREQUIRED), miscRequirement); break; + case AREA_LOCKSTATUS_LEVEL_TOO_HIGH: + GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_LEVEL_MAXREQUIRED), miscRequirement); + break; + case AREA_LOCKSTATUS_LEVEL_NOT_EQUAL: + GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_LEVEL_EQUALREQUIRED), miscRequirement); + break; case AREA_LOCKSTATUS_ZONE_IN_COMBAT: GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_ZONE_IN_COMBAT); break; case AREA_LOCKSTATUS_INSTANCE_IS_FULL: GetSession()->SendTransferAborted(mapEntry->MapID, TRANSFER_ABORT_MAX_PLAYERS); break; + case AREA_LOCKSTATUS_WRONG_TEAM: + if (miscRequirement == 469) + GetSession()->SendAreaTriggerMessage("%s", GetSession()->GetMangosString(LANG_WRONG_TEAM_ALLIANCE)); + else + GetSession()->SendAreaTriggerMessage("%s", GetSession()->GetMangosString(LANG_WRONG_TEAM_HORDE)); + break; case AREA_LOCKSTATUS_QUEST_NOT_COMPLETED: if (mapEntry->IsContinent()) // do not report anything for quest areatrigge { @@ -17697,7 +17709,7 @@ void Player::SendTransferAbortedByLockStatus(MapEntry const* mapEntry, AreaLockS break; case AREA_LOCKSTATUS_MISSING_ITEM: if (AreaTrigger const* at = sObjectMgr.GetMapEntranceTrigger(mapEntry->MapID)) - { GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_LEVEL_MINREQUIRED_AND_ITEM), at->requiredLevel, sObjectMgr.GetItemPrototype(miscRequirement)->Name1); } + { GetSession()->SendAreaTriggerMessage(GetSession()->GetMangosString(LANG_REQUIRED_ITEM), sObjectMgr.GetItemPrototype(miscRequirement)->Name1); } break; case AREA_LOCKSTATUS_NOT_ALLOWED: case AREA_LOCKSTATUS_RAID_LOCKED: @@ -19430,39 +19442,62 @@ AreaLockStatus Player::GetAreaTriggerLockStatus(AreaTrigger const* at, uint32& m if (isGameMaster()) { return AREA_LOCKSTATUS_OK; } - // Level Requirements - if (getLevel() < at->requiredLevel && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL)) - { - miscRequirement = at->requiredLevel; - return AREA_LOCKSTATUS_TOO_LOW_LEVEL; - } - // Raid Requirements if (mapEntry->IsRaid() && !sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_RAID)) if (!GetGroup() || !GetGroup()->isRaidGroup()) { return AREA_LOCKSTATUS_RAID_LOCKED; } - // Item Requirements: must have requiredItem OR requiredItem2, report the first one that's missing - if (at->requiredItem) + if (at->condition) //condition validity is checked at startup { - if (!HasItemCount(at->requiredItem, 1) && - (!at->requiredItem2 || !HasItemCount(at->requiredItem2, 1))) + ConditionEntry fault; + if (!sObjectMgr.IsPlayerMeetToCondition(at->condition, this, GetMap(),NULL, CONDITION_AREA_TRIGGER, &fault)) { - miscRequirement = at->requiredItem; - return AREA_LOCKSTATUS_MISSING_ITEM; - } - } - else if (at->requiredItem2 && !HasItemCount(at->requiredItem2, 1)) - { - miscRequirement = at->requiredItem2; - return AREA_LOCKSTATUS_MISSING_ITEM; - } + switch (fault.type) + { + case CONDITION_LEVEL: + { + if (sWorld.getConfig(CONFIG_BOOL_INSTANCE_IGNORE_LEVEL)) + break; + else + { + miscRequirement = fault.param1; + switch (fault.param2) + { + case 0: { return AREA_LOCKSTATUS_LEVEL_NOT_EQUAL; } + case 1: { return AREA_LOCKSTATUS_LEVEL_TOO_LOW; } + case 2: { return AREA_LOCKSTATUS_LEVEL_TOO_HIGH; } + } + } + } - // Quest Requirements - if (at->requiredQuest && !GetQuestRewardStatus(at->requiredQuest)) - { - miscRequirement = at->requiredQuest; - return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED; + case CONDITION_ITEM: + { + miscRequirement = fault.param1; + return AREA_LOCKSTATUS_MISSING_ITEM; + } + + case CONDITION_QUESTREWARDED: + { + miscRequirement = fault.param1; + return AREA_LOCKSTATUS_QUEST_NOT_COMPLETED; + } + + case CONDITION_TEAM: + { + miscRequirement = fault.param1; + return AREA_LOCKSTATUS_WRONG_TEAM; + } + + case CONDITION_PVP_RANK: + { + miscRequirement = fault.param1; + return AREA_LOCKSTATUS_NOT_ALLOWED; + } + + default: + return AREA_LOCKSTATUS_UNKNOWN_ERROR; + } + } } // If the map is not created, assume it is possible to enter it. diff --git a/src/game/Object/SocialMgr.cpp b/src/game/Object/SocialMgr.cpp index 60548e6e..cf210b0f 100644 --- a/src/game/Object/SocialMgr.cpp +++ b/src/game/Object/SocialMgr.cpp @@ -230,7 +230,7 @@ void SocialMgr::GetFriendInfo(Player* player, uint32 friend_lowguid, FriendInfo& PlayerSocialMap::iterator itr = player->GetSocial()->m_playerSocialMap.find(friend_lowguid); if (itr != player->GetSocial()->m_playerSocialMap.end()) - + { // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all if (pFriend && pFriend->GetName() && @@ -254,6 +254,7 @@ void SocialMgr::GetFriendInfo(Player* player, uint32 friend_lowguid, FriendInfo& friendInfo.Level = 0; friendInfo.Class = 0; } + } } void SocialMgr::MakeFriendStatusPacket(FriendsResult result, uint32 guid, WorldPacket* data) diff --git a/src/game/Object/Unit.cpp b/src/game/Object/Unit.cpp index 0bf808c0..792a1341 100644 --- a/src/game/Object/Unit.cpp +++ b/src/game/Object/Unit.cpp @@ -631,8 +631,14 @@ uint32 Unit::DealDamage(Unit* pVictim, uint32 damage, CleanDamage const* cleanDa { SetContestedPvP(attackedPlayer); } } - if (pVictim->GetTypeId() == TYPEID_UNIT && !((Creature*)pVictim)->IsPet() && !((Creature*)pVictim)->HasLootRecipient()) - { ((Creature*)pVictim)->SetLootRecipient(this); } + if (Creature* victim = pVictim->ToCreature()) + { + if (!victim->IsPet() && !victim->HasLootRecipient()) + victim->SetLootRecipient(this); + + if (IsControlledByPlayer()) // more narrow: IsPet(), IsGuardian() ? + victim->LowerPlayerDamageReq(health < damage ? health : damage); + } if (health <= damage) { @@ -681,23 +687,36 @@ uint32 Unit::DealDamage(Unit* pVictim, uint32 damage, CleanDamage const* cleanDa /* * Generic Actions (ProcEvents, Combat-Log, Kill Rewards, Stop Combat) */ + bool isRewardAllowed = true; + if (Creature* creature = pVictim->ToCreature()) + { + isRewardAllowed = creature->IsDamageEnoughForLootingAndReward(); + if (!isRewardAllowed) + creature->SetLootRecipient(NULL); + } + // call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop) if (player_tap && player_tap != pVictim) { player_tap->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0); - WorldPacket data(SMSG_PARTYKILLLOG, (8 + 8)); // send event PARTY_KILL - data << player_tap->GetObjectGuid(); // player with killing blow - data << pVictim->GetObjectGuid(); // victim + if (isRewardAllowed) + { + WorldPacket data(SMSG_PARTYKILLLOG, (8 + 8)); // send event PARTY_KILL + data << player_tap->GetObjectGuid(); // player with killing blow + data << pVictim->GetObjectGuid(); // victim - if (group_tap) - { group_tap->BroadcastPacket(&data, false, group_tap->GetMemberGroup(player_tap->GetObjectGuid()), player_tap->GetObjectGuid()); } + if (group_tap) + { + group_tap->BroadcastPacket(&data, false, group_tap->GetMemberGroup(player_tap->GetObjectGuid()), player_tap->GetObjectGuid()); + } - player_tap->SendDirectMessage(&data); + player_tap->SendDirectMessage(&data); + } } // Reward player, his pets, and group/raid members - if (player_tap != pVictim) + if (isRewardAllowed && player_tap != pVictim) { if (group_tap) { group_tap->RewardGroupAtKill(pVictim, player_tap); } diff --git a/src/game/Object/Unit.h b/src/game/Object/Unit.h index 8f13a5cc..a3099ee6 100644 --- a/src/game/Object/Unit.h +++ b/src/game/Object/Unit.h @@ -577,32 +577,32 @@ enum NPCFlags */ enum MovementFlags { - MOVEFLAG_NONE = 0x00000000, - MOVEFLAG_FORWARD = 0x00000001, - MOVEFLAG_BACKWARD = 0x00000002, - MOVEFLAG_STRAFE_LEFT = 0x00000004, - MOVEFLAG_STRAFE_RIGHT = 0x00000008, - MOVEFLAG_TURN_LEFT = 0x00000010, - MOVEFLAG_TURN_RIGHT = 0x00000020, - MOVEFLAG_PITCH_UP = 0x00000040, - MOVEFLAG_PITCH_DOWN = 0x00000080, - MOVEFLAG_WALK_MODE = 0x00000100, // Walking + MOVEFLAG_NONE = 0x00000000, + MOVEFLAG_FORWARD = 0x00000001, + MOVEFLAG_BACKWARD = 0x00000002, + MOVEFLAG_STRAFE_LEFT = 0x00000004, + MOVEFLAG_STRAFE_RIGHT = 0x00000008, + MOVEFLAG_TURN_LEFT = 0x00000010, + MOVEFLAG_TURN_RIGHT = 0x00000020, + MOVEFLAG_PITCH_UP = 0x00000040, + MOVEFLAG_PITCH_DOWN = 0x00000080, + MOVEFLAG_WALK_MODE = 0x00000100, // Walking - MOVEFLAG_LEVITATING = 0x00000400, - MOVEFLAG_ROOT = 0x00000800, // [-ZERO] is it really need and correct value - MOVEFLAG_FALLING = 0x00002000, - MOVEFLAG_FALLINGFAR = 0x00004000, - MOVEFLAG_SWIMMING = 0x00200000, // appears with fly flag also - MOVEFLAG_SPLINE_ENABLED = 0x00400000, - MOVEFLAG_CAN_FLY = 0x00800000, // [-ZERO] is it really need and correct value - MOVEFLAG_FLYING = 0x01000000, // [-ZERO] is it really need and correct value + MOVEFLAG_LEVITATING = 0x00000400, + MOVEFLAG_FLYING = 0x00000800, // [-ZERO] is it really need and correct value + MOVEFLAG_FALLING = 0x00002000, + MOVEFLAG_FALLINGFAR = 0x00004000, + MOVEFLAG_SWIMMING = 0x00200000, // appears with fly flag also + MOVEFLAG_SPLINE_ENABLED = 0x00400000, + MOVEFLAG_CAN_FLY = 0x00800000, // [-ZERO] is it really need and correct value + MOVEFLAG_FLYING_OLD = 0x01000000, // [-ZERO] is it really need and correct value - MOVEFLAG_ONTRANSPORT = 0x02000000, // Used for flying on some creatures - MOVEFLAG_SPLINE_ELEVATION = 0x04000000, // [-ZERO] checkme! used for flight paths - //MOVEFLAG_SPLINE_ENABLED = 0x08000000, // [-ZERO] wrong! - MOVEFLAG_WATERWALKING = 0x10000000, // prevent unit from falling through water - MOVEFLAG_SAFE_FALL = 0x20000000, // active rogue safe fall spell (passive) - MOVEFLAG_HOVER = 0x40000000 + MOVEFLAG_ONTRANSPORT = 0x02000000, // Used for flying on some creatures + MOVEFLAG_SPLINE_ELEVATION = 0x04000000, // used for flight paths + MOVEFLAG_ROOT = 0x08000000, // used for flight paths + MOVEFLAG_WATERWALKING = 0x10000000, // prevent unit from falling through water + MOVEFLAG_SAFE_FALL = 0x20000000, // active rogue safe fall spell (passive) + MOVEFLAG_HOVER = 0x40000000 }; // flags that use in movement check for example at spell casting diff --git a/src/game/Server/SharedDefines.h b/src/game/Server/SharedDefines.h index 6df62e9f..5b185d3a 100644 --- a/src/game/Server/SharedDefines.h +++ b/src/game/Server/SharedDefines.h @@ -331,7 +331,7 @@ enum SpellAttributesEx SPELL_ATTR_EX_UNAFFECTED_BY_SCHOOL_IMMUNE = 0x00010000, // 16 unaffected by school immunity SPELL_ATTR_EX_UNK17 = 0x00020000, // 17 for auras SPELL_AURA_TRACK_CREATURES, SPELL_AURA_TRACK_RESOURCES and SPELL_AURA_TRACK_STEALTHED select non-stacking tracking spells SPELL_ATTR_EX_UNK18 = 0x00040000, // 18 - SPELL_ATTR_EX_UNK19 = 0x00080000, // 19 + SPELL_ATTR_EX_CANT_TARGET_SELF = 0x00080000, // 19 spells that exclude the caster SPELL_ATTR_EX_REQ_TARGET_COMBO_POINTS = 0x00100000, // 20 Req combo points on target SPELL_ATTR_EX_UNK21 = 0x00200000, // 21 SPELL_ATTR_EX_REQ_COMBO_POINTS = 0x00400000, // 22 Use combo points (in 4.x not required combo point target selected) @@ -2548,15 +2548,17 @@ enum AreaLockStatus { AREA_LOCKSTATUS_OK = 0, AREA_LOCKSTATUS_UNKNOWN_ERROR = 1, - AREA_LOCKSTATUS_TOO_LOW_LEVEL = 2, - AREA_LOCKSTATUS_TOO_HIGH_LEVEL = 3, - AREA_LOCKSTATUS_RAID_LOCKED = 4, - AREA_LOCKSTATUS_QUEST_NOT_COMPLETED = 5, - AREA_LOCKSTATUS_MISSING_ITEM = 6, - AREA_LOCKSTATUS_ZONE_IN_COMBAT = 7, - AREA_LOCKSTATUS_INSTANCE_IS_FULL = 8, - AREA_LOCKSTATUS_NOT_ALLOWED = 9, - AREA_LOCKSTATUS_HAS_BIND = 10, + AREA_LOCKSTATUS_LEVEL_NOT_EQUAL = 2, + AREA_LOCKSTATUS_LEVEL_TOO_LOW = 3, + AREA_LOCKSTATUS_LEVEL_TOO_HIGH = 4, + AREA_LOCKSTATUS_RAID_LOCKED = 5, + AREA_LOCKSTATUS_QUEST_NOT_COMPLETED = 6, + AREA_LOCKSTATUS_MISSING_ITEM = 7, + AREA_LOCKSTATUS_ZONE_IN_COMBAT = 8, + AREA_LOCKSTATUS_INSTANCE_IS_FULL = 9, + AREA_LOCKSTATUS_NOT_ALLOWED = 10, + AREA_LOCKSTATUS_HAS_BIND = 11, + AREA_LOCKSTATUS_WRONG_TEAM = 12, }; enum TrackedAuraType diff --git a/src/game/Tools/Language.h b/src/game/Tools/Language.h index db2eb5cb..570b1e45 100644 --- a/src/game/Tools/Language.h +++ b/src/game/Tools/Language.h @@ -79,7 +79,7 @@ enum MangosStrings LANG_NON_EXIST_CHARACTER = 47, LANG_FRIEND_IGNORE_UNKNOWN = 48, LANG_LEVEL_MINREQUIRED = 49, - LANG_LEVEL_MINREQUIRED_AND_ITEM = 50, + LANG_REQUIRED_ITEM = 50, LANG_NPC_TAINER_HELLO = 51, LANG_COMMAND_INVALID_ITEM_COUNT = 52, LANG_COMMAND_MAIL_ITEMS_LIMIT = 53, @@ -96,7 +96,11 @@ enum MangosStrings LANG_GM_NO_WHISPER = 64, LANG_USING_SCRIPT_LIB_UNKNOWN = 65, LANG_USING_SCRIPT_LIB_NONE = 66, - // Room for more level 0 67-99 not used + LANG_WRONG_TEAM_HORDE = 67, + LANG_WRONG_TEAM_ALLIANCE = 68, + LANG_LEVEL_MAXREQUIRED = 69, + LANG_LEVEL_EQUALREQUIRED = 70, + // Room for more level 0 71-99 not used // level 1 chat LANG_GLOBAL_NOTIFY = 100, @@ -363,9 +367,9 @@ enum MangosStrings LANG_TRIGGER_TAVERN = 364, LANG_TRIGGER_QUEST = 365, LANG_TRIGGER_EXPLORE_QUEST = 366, - LANG_TRIGGER_REQ_LEVEL = 367, - LANG_TRIGGER_REQ_ITEMS = 368, - LANG_TRIGGER_REQ_QUEST = 369, + LANG_TRIGGER_CONDITION = 367, + // 368 + // 369 // 370 used in master branch // 371 used in master branch // 372 used in master branch diff --git a/src/game/WorldHandlers/Channel.cpp b/src/game/WorldHandlers/Channel.cpp index fe88f0bf..34ed183f 100644 --- a/src/game/WorldHandlers/Channel.cpp +++ b/src/game/WorldHandlers/Channel.cpp @@ -54,6 +54,7 @@ Channel::Channel(const std::string& name) else // it's custom channel { m_flags |= CHANNEL_FLAG_CUSTOM; + m_announce = (name.compare("world") != 0); } } diff --git a/src/game/WorldHandlers/DisableMgr.cpp b/src/game/WorldHandlers/DisableMgr.cpp index 52f9c4aa..246be6e2 100644 --- a/src/game/WorldHandlers/DisableMgr.cpp +++ b/src/game/WorldHandlers/DisableMgr.cpp @@ -44,6 +44,8 @@ namespace DisableMap m_DisableMap; } +#define CONTINUE if (newData) delete data; continue + void LoadDisables() { // reload case @@ -79,10 +81,14 @@ void LoadDisables() uint32 data0 = fields[3].GetUInt32(); DisableData* data; + bool newData = false; if (m_DisableMap[type].find(entry) != m_DisableMap[type].end()) data = &m_DisableMap[type][entry]; else + { data = new DisableData(); + newData = true; + } data->flags = flags; @@ -92,13 +98,13 @@ void LoadDisables() if (!(sSpellStore.LookupEntry(entry) || flags & SPELL_DISABLE_DEPRECATED_SPELL)) { ERROR_DB_STRICT_LOG("Spell entry %u from `disables` doesn't exist in dbc, skipped.", entry); - continue; + CONTINUE; } if (!flags || flags > MAX_SPELL_DISABLE_TYPE) { ERROR_DB_STRICT_LOG("Disable flags for spell %u are invalid, skipped.", entry); - continue; + CONTINUE; } if (flags & SPELL_DISABLE_MAP) @@ -117,7 +123,7 @@ void LoadDisables() if (!mapEntry) { ERROR_DB_STRICT_LOG("Map entry %u from `disables` doesn't exist in dbc, skipped.", entry); - continue; + CONTINUE; } bool isFlagInvalid = false; switch (mapEntry->map_type) @@ -131,12 +137,12 @@ void LoadDisables() case MAP_BATTLEGROUND: //case MAP_ARENA: [-ZERO] ERROR_DB_STRICT_LOG("Battleground map %u specified to be disabled in map case, skipped.", entry); - continue; + CONTINUE; } if (isFlagInvalid) { ERROR_DB_STRICT_LOG("Disable flags for map %u are invalid, skipped.", entry); - continue; + CONTINUE; } break; } @@ -144,7 +150,7 @@ void LoadDisables() if (!sBattleGroundMgr.GetBattleGroundTemplate(BattleGroundTypeId(entry))) { ERROR_DB_STRICT_LOG("Battleground entry %u from `disables` doesn't exist in dbc, skipped.", entry); - continue; + CONTINUE; } if (flags) ERROR_DB_STRICT_LOG("Disable flags specified for battleground %u, useless data.", entry); @@ -153,7 +159,7 @@ void LoadDisables() if (entry > MAX_OPVP_ID) { ERROR_DB_STRICT_LOG("OutdoorPvPTypes value %u from `disables` is invalid, skipped.", entry); - continue; + CONTINUE; } if (flags) ERROR_DB_STRICT_LOG("Disable flags specified for outdoor PvP %u, useless data.", entry); @@ -173,7 +179,7 @@ void LoadDisables() if (!mapEntry) { ERROR_DB_STRICT_LOG("Map entry %u from `disables` doesn't exist in dbc, skipped.", entry); - continue; + CONTINUE; } switch (mapEntry->map_type) { @@ -213,7 +219,7 @@ void LoadDisables() if (!mapEntry) { ERROR_DB_STRICT_LOG("Map entry %u from `disables` doesn't exist in dbc, skipped.", entry); - continue; + CONTINUE; } switch (mapEntry->map_type) { @@ -237,6 +243,18 @@ void LoadDisables() } case DISABLE_TYPE_CREATURE_SPAWN: case DISABLE_TYPE_GAMEOBJECT_SPAWN: + if ((flags & SPAWN_DISABLE_CHECK_GUID) != 0) + { + if (data0) + data->params[0].insert(data0); + else + { + ERROR_DB_STRICT_LOG("Disables type %u: required GUID is missing for entry %u, ignoring disable entry.", type, entry); + CONTINUE; + } + } + break; + case DISABLE_TYPE_ITEM_DROP: break; default: break; @@ -278,7 +296,7 @@ void CheckQuestDisables() sLog.outString(">> Checked %u quest disables", count); } -bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags) +bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags, uint32 adData) { MANGOS_ASSERT(type < MAX_DISABLE_TYPES); if (m_DisableMap[type].empty()) @@ -366,11 +384,13 @@ bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags case DISABLE_TYPE_OUTDOORPVP: case DISABLE_TYPE_ACHIEVEMENT_CRITERIA: case DISABLE_TYPE_MMAP: - case DISABLE_TYPE_CREATURE_SPAWN: - case DISABLE_TYPE_GAMEOBJECT_SPAWN: + case DISABLE_TYPE_ITEM_DROP: return true; case DISABLE_TYPE_VMAP: return (flags & itr->second.flags) != 0; + case DISABLE_TYPE_CREATURE_SPAWN: + case DISABLE_TYPE_GAMEOBJECT_SPAWN: + return (itr->second.flags & SPAWN_DISABLE_CHECK_GUID) == 0 || itr->second.params[0].count(adData) > 0; } return false; diff --git a/src/game/WorldHandlers/DisableMgr.h b/src/game/WorldHandlers/DisableMgr.h index ad2a98e4..8866a955 100644 --- a/src/game/WorldHandlers/DisableMgr.h +++ b/src/game/WorldHandlers/DisableMgr.h @@ -36,7 +36,8 @@ enum DisableType DISABLE_TYPE_MMAP = 7, DISABLE_TYPE_CREATURE_SPAWN = 8, DISABLE_TYPE_GAMEOBJECT_SPAWN = 9, - MAX_DISABLE_TYPES = 10 + DISABLE_TYPE_ITEM_DROP = 10, + MAX_DISABLE_TYPES = 11 }; enum SpellDisableTypes @@ -55,14 +56,13 @@ enum SpellDisableTypes enum SpawnDisableTypes { - SPAWN_DISABLE_FOR_ENTRY = 0, - SPAWN_DISABLE_FOR_GUID = 1 + SPAWN_DISABLE_CHECK_GUID = 0x1 }; namespace DisableMgr { void LoadDisables(); - bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit = NULL, uint8 flags = 0); + bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit = NULL, uint8 flags = 0, uint32 data = 0); void CheckQuestDisables(); bool IsVMAPDisabledFor(uint32 entry, uint8 flags); bool IsPathfindingEnabled(uint32 mapId); diff --git a/src/game/WorldHandlers/MiscHandler.cpp b/src/game/WorldHandlers/MiscHandler.cpp index 7aae194e..d4291a32 100644 --- a/src/game/WorldHandlers/MiscHandler.cpp +++ b/src/game/WorldHandlers/MiscHandler.cpp @@ -430,6 +430,12 @@ void WorldSession::HandleSetSelectionOpcode(WorldPacket& recv_data) _player->SetSelectionGuid(guid); + if (guid.IsEmpty()) // TODO this is probably a wrong place for such action, so it's a "hacky" or "wrong" fix + { + _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL, false); + return; + } + // update reputation list if need Unit* unit = ObjectAccessor::GetUnit(*_player, guid); // can select group members at diff maps if (!unit) diff --git a/src/game/WorldHandlers/SkillHandler.cpp b/src/game/WorldHandlers/SkillHandler.cpp index b2b4b6b9..97e50a39 100644 --- a/src/game/WorldHandlers/SkillHandler.cpp +++ b/src/game/WorldHandlers/SkillHandler.cpp @@ -56,6 +56,9 @@ void WorldSession::HandleTalentWipeConfirmOpcode(WorldPacket& recv_data) return; } + if (!unit->CanTrainAndResetTalentsOf(_player)) + { return; } + // remove fake death if (GetPlayer()->hasUnitState(UNIT_STAT_DIED)) { GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); } diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index be1b0be7..39284726 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -722,7 +722,7 @@ void Spell::prepareDataForTriggerSystem() // avoid triggering negative hit for only positive targets m_negativeEffectMask = 0x0; for (int i = 0; i < MAX_EFFECT_INDEX; ++i) - if (!IsPositiveEffect(m_spellInfo, SpellEffectIndex(i))) + if (m_spellInfo->Effect[i] != SPELL_EFFECT_NONE && !IsPositiveEffect(m_spellInfo, SpellEffectIndex(i))) { m_negativeEffectMask |= (1 << i); } // Hunter traps spells (for Entrapment trigger) @@ -962,6 +962,9 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) { DoSpellHitOnUnit(m_caster, mask, true); unitTarget = m_caster; + + if (m_caster->GetTypeId() == TYPEID_UNIT) + m_caster->ToCreature()->LowerPlayerDamageReq(target->damage); } } else // in 1.12.1 we need explicit miss info @@ -1749,8 +1752,6 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& } } - // exclude caster - targetUnitMap.remove(m_caster); break; } case TARGET_AREAEFFECT_CUSTOM: @@ -2449,6 +2450,9 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& break; } + if (targetMode != TARGET_SELF && m_spellInfo->HasAttribute(SPELL_ATTR_EX_CANT_TARGET_SELF)) + { targetUnitMap.remove(m_caster); } + if (unMaxTargets && targetUnitMap.size() > unMaxTargets) { // make sure one unit is always removed per iteration @@ -3018,12 +3022,25 @@ void Spell::update(uint32 difftime) // update pointers based at it's GUIDs UpdatePointers(); - if (m_targets.getUnitTargetGuid() && !m_targets.getUnitTarget()) + if (!m_targets.getUnitTargetGuid().IsEmpty() && !m_targets.getUnitTarget()) { cancel(); return; } + // check for target going invisiblity/fake death + if (Unit* target = m_targets.getUnitTarget()) + { + if (!target->IsVisibleForOrDetect(m_caster, m_caster, true) || target->HasAuraType(SPELL_AURA_FEIGN_DEATH)) + { + if (m_caster->GetTargetGuid() == target->GetObjectGuid()) + m_caster->SetTargetGuid(ObjectGuid()); + cancel(); + return; + } + } + + if (m_targets.getUnitTarget() && (m_targets.getUnitTarget() != m_caster) && IsSingleTargetSpell(m_spellInfo) && !IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell) { @@ -4671,11 +4688,6 @@ SpellCastResult Spell::CheckCast(bool strict) if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetHealth() > m_targets.getUnitTarget()->GetMaxHealth() * 0.2) { return SPELL_FAILED_BAD_TARGETS; } } - else if (m_spellInfo->Id == 51582) // Rocket Boots Engaged - { - if (m_caster->IsInWater()) - { return SPELL_FAILED_ONLY_ABOVEWATER; } - } else if (m_spellInfo->SpellIconID == 156) // Holy Shock { // spell different for friends and enemies diff --git a/src/game/WorldHandlers/Weather.cpp b/src/game/WorldHandlers/Weather.cpp index b1b9cac6..50c3b145 100644 --- a/src/game/WorldHandlers/Weather.cpp +++ b/src/game/WorldHandlers/Weather.cpp @@ -176,7 +176,7 @@ bool Weather::ReGenerate() uint32 chance2 = chance1 + m_weatherChances->data[season].snowChance; uint32 chance3 = chance2 + m_weatherChances->data[season].stormChance; - uint32 rnd = urand(0, 99); + uint32 rnd = urand(1, 100); if (rnd <= chance1) { m_type = WEATHER_TYPE_RAIN; } else if (rnd <= chance2) diff --git a/src/modules/Eluna b/src/modules/Eluna index a3b20b24..849d26e3 160000 --- a/src/modules/Eluna +++ b/src/modules/Eluna @@ -1 +1 @@ -Subproject commit a3b20b248e2e26e41bd03edb8cf2e024bcf35ec8 +Subproject commit 849d26e3a097bb7e6d7e09b7414d2e523c8d0b6b diff --git a/src/shared/revision.h b/src/shared/revision.h index 0f4f4f0a..2f8fe51a 100644 --- a/src/shared/revision.h +++ b/src/shared/revision.h @@ -37,7 +37,7 @@ #define CHAR_DB_UPDATE_DESCRIPTION "Fix SoR paladin" #define WORLD_DB_VERSION_NR 21 - #define WORLD_DB_STRUCTURE_NR 3 - #define WORLD_DB_CONTENT_NR 2 - #define WORLD_DB_UPDATE_DESCRIPTION "Fix Random MMGen Scripts" + #define WORLD_DB_STRUCTURE_NR 4 + #define WORLD_DB_CONTENT_NR 1 + #define WORLD_DB_UPDATE_DESCRIPTION "Refactor areatrigger_teleport" #endif // __REVISION_H__