diff options
Diffstat (limited to 'src')
50 files changed, 954 insertions, 564 deletions
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 23620bf1174..7cee0e20dd5 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -370,9 +370,7 @@ void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO { if (loadDefault) { - if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(me->GetEntry())) - me->LoadEquipment(creatureInfo->equipmentId, true); - + me->LoadEquipment(me->GetOriginalEquipmentId(), true); return; } diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index e37b9e777d8..bc94169687c 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -1459,15 +1459,16 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (Creature* npc = (*itr)->ToCreature()) { uint32 slot[3]; - if (e.action.equip.entry) + int8 equipId = (int8)e.action.equip.entry; + if (equipId) { - EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(e.action.equip.entry); + EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(npc->GetEntry(), equipId); if (!einfo) { - sLog->outError(LOG_FILTER_SQL, "SmartScript: SMART_ACTION_EQUIP uses non-existent equipment info entry %u", e.action.equip.entry); + sLog->outError(LOG_FILTER_SQL, "SmartScript: SMART_ACTION_EQUIP uses non-existent equipment info id %u for creature %u", equipId, npc->GetEntry()); return; } - npc->SetCurrentEquipmentId(e.action.equip.entry); + npc->SetCurrentEquipmentId(equipId); slot[0] = einfo->ItemEntry[0]; slot[1] = einfo->ItemEntry[1]; slot[2] = einfo->ItemEntry[2]; @@ -1478,11 +1479,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u slot[1] = e.action.equip.slot2; slot[2] = e.action.equip.slot3; } - if (!e.action.equip.mask || e.action.equip.mask & 1) + if (!e.action.equip.mask || (e.action.equip.mask & 1)) npc->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, slot[0]); - if (!e.action.equip.mask || e.action.equip.mask & 2) + if (!e.action.equip.mask || (e.action.equip.mask & 2)) npc->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, slot[1]); - if (!e.action.equip.mask || e.action.equip.mask & 4) + if (!e.action.equip.mask || (e.action.equip.mask & 4)) npc->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, slot[2]); } } @@ -2028,6 +2029,45 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u delete targets; break; } + case SMART_ACTION_SET_GO_FLAG: + { + ObjectList* targets = GetTargets(e, unit); + if (!targets) + break; + + for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) + if (IsGameObject(*itr)) + (*itr)->ToGameObject()->SetUInt32Value(GAMEOBJECT_FLAGS, e.action.goFlag.flag); + + delete targets; + break; + } + case SMART_ACTION_ADD_GO_FLAG: + { + ObjectList* targets = GetTargets(e, unit); + if (!targets) + break; + + for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) + if (IsGameObject(*itr)) + (*itr)->ToGameObject()->SetFlag(GAMEOBJECT_FLAGS, e.action.goFlag.flag); + + delete targets; + break; + } + case SMART_ACTION_REMOVE_GO_FLAG: + { + ObjectList* targets = GetTargets(e, unit); + if (!targets) + break; + + for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) + if (IsGameObject(*itr)) + (*itr)->ToGameObject()->RemoveFlag(GAMEOBJECT_FLAGS, e.action.goFlag.flag); + + delete targets; + break; + } default: sLog->outError(LOG_FILTER_SQL, "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Unhandled Action type %u", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); break; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 69902954fde..679d2d87ffc 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -907,6 +907,9 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_ACTION_SET_HOME_POS: case SMART_ACTION_SET_HEALTH_REGEN: case SMART_ACTION_SET_ROOT: + case SMART_ACTION_SET_GO_FLAG: + case SMART_ACTION_ADD_GO_FLAG: + case SMART_ACTION_REMOVE_GO_FLAG: break; default: sLog->outError(LOG_FILTER_SQL, "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id); diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index a3420071f5e..f117ab52e01 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -427,7 +427,6 @@ enum SMART_ACTION SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL = 42, // MinHpValue(+pct, -flat) SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL = 43, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to dismount) SMART_ACTION_SET_INGAME_PHASE_MASK = 44, // mask - SMART_ACTION_SET_DATA = 45, // Field, Data (only creature TODO) SMART_ACTION_MOVE_FORWARD = 46, // distance SMART_ACTION_SET_VISIBILITY = 47, // on/off @@ -450,7 +449,6 @@ enum SMART_ACTION SMART_ACTION_STORE_TARGET_LIST = 64, // varID, SMART_ACTION_WP_RESUME = 65, // none SMART_ACTION_SET_ORIENTATION = 66, // - SMART_ACTION_CREATE_TIMED_EVENT = 67, // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance SMART_ACTION_PLAYMOVIE = 68, // entry SMART_ACTION_MOVE_TO_POS = 69, // PointId, xyz @@ -488,8 +486,11 @@ enum SMART_ACTION SMART_ACTION_SET_HOME_POS = 101, // none SMART_ACTION_SET_HEALTH_REGEN = 102, // 0/1 SMART_ACTION_SET_ROOT = 103, // off/on + SMART_ACTION_SET_GO_FLAG = 104, // Flags + SMART_ACTION_ADD_GO_FLAG = 105, // Flags + SMART_ACTION_REMOVE_GO_FLAG = 106, // Flags - SMART_ACTION_END = 104 + SMART_ACTION_END = 107 }; struct SmartAction @@ -926,6 +927,11 @@ struct SmartAction uint32 root; } setRoot; + struct + { + uint32 flag; + } goFlag; + //! Note for any new future actions //! All parameters must have type uint32 diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index b1d0087c32c..bd560c8266e 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -1,4 +1,4 @@ -/* + /* * Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/> * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * @@ -56,7 +56,7 @@ AccountOpResult AccountMgr::CreateAccount(std::string username, std::string pass RBACData* rbac = new RBACData(GetId(username), username, -1); // No need to Load From DB, as it's new data - RBACGroupContainer const& groupsToAdd = _defaultGroups[0]; // 0: Default sec level + RBACGroupContainer const& groupsToAdd = _defaultSecGroups[0]; // 0: Default sec level for (RBACGroupContainer::const_iterator it = groupsToAdd.begin(); it != groupsToAdd.end(); ++it) rbac->AddGroup(*it, -1); @@ -426,13 +426,23 @@ void AccountMgr::LoadRBAC() uint8 secId = field[0].GetUInt8(); if (lastSecId != secId) - groups = &_defaultGroups[secId]; + groups = &_defaultSecGroups[secId]; groups->insert(field[1].GetUInt32()); } while (result->NextRow()); sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u permission definitions, %u role definitions and %u group definitions in %u ms", count1, count2, count3, GetMSTimeDiffToNow(oldMSTime)); + + // Load default groups to be added to any RBAC Object. + std::string defaultGroups = ConfigMgr::GetStringDefault("RBAC.DefaultGroups", ""); + Tokenizer tokens(defaultGroups, ','); + for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) + if (uint32 groupId = atoi(*itr)) + { + sLog->outError(LOG_FILTER_LFG, "Adding default group %u", groupId); + _defaultGroups.insert(groupId); + } } void AccountMgr::UpdateAccountAccess(RBACData* rbac, uint32 accountId, uint8 securityLevel, int32 realmId) @@ -459,7 +469,7 @@ void AccountMgr::UpdateAccountAccess(RBACData* rbac, uint32 accountId, uint8 sec uint8 secLevel = field[0].GetUInt8(); int32 realmId = field[1].GetUInt32(); - RBACGroupContainer const& groupsToRemove = _defaultGroups[secLevel]; + RBACGroupContainer const& groupsToRemove = _defaultSecGroups[secLevel]; for (RBACGroupContainer::const_iterator it = groupsToRemove.begin(); it != groupsToRemove.end(); ++it) rbac->RemoveGroup(*it, realmId); } @@ -467,7 +477,7 @@ void AccountMgr::UpdateAccountAccess(RBACData* rbac, uint32 accountId, uint8 sec } // Add new groups depending on the new security Level - RBACGroupContainer const& groupsToAdd = _defaultGroups[securityLevel]; + RBACGroupContainer const& groupsToAdd = _defaultSecGroups[securityLevel]; for (RBACGroupContainer::const_iterator it = groupsToAdd.begin(); it != groupsToAdd.end(); ++it) rbac->AddGroup(*it, realmId); diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h index 90c533ca5fa..440e012f1fc 100644 --- a/src/server/game/Accounts/AccountMgr.h +++ b/src/server/game/Accounts/AccountMgr.h @@ -77,12 +77,14 @@ class AccountMgr RBACGroupsContainer const& GetRBACGroupList() const { return _groups; } RBACRolesContainer const& GetRBACRoleList() const { return _roles; } RBACPermissionsContainer const& GetRBACPermissionList() const { return _permissions; } + RBACGroupContainer const& GetRBACDefaultGroups() const { return _defaultGroups; } private: RBACPermissionsContainer _permissions; RBACRolesContainer _roles; RBACGroupsContainer _groups; - RBACDefaultSecurityGroupContainer _defaultGroups; + RBACDefaultSecurityGroupContainer _defaultSecGroups; + RBACGroupContainer _defaultGroups; }; #define sAccountMgr ACE_Singleton<AccountMgr, ACE_Null_Mutex>::instance() diff --git a/src/server/game/Accounts/RBAC.cpp b/src/server/game/Accounts/RBAC.cpp index 4a069df05cd..121c9faae76 100644 --- a/src/server/game/Accounts/RBAC.cpp +++ b/src/server/game/Accounts/RBAC.cpp @@ -296,6 +296,11 @@ void RBACData::LoadFromDB() while (result->NextRow());
}
+ // Add default groups
+ RBACGroupContainer const& groups = sAccountMgr->GetRBACDefaultGroups();
+ for (RBACGroupContainer::const_iterator itr = groups.begin(); itr != groups.end(); ++itr)
+ AddGroup(*itr);
+
// Force calculation of permissions, it wasn't performed at load time
// while adding groups, roles and permissions
CalculateNewPermissions();
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index d2c76b71801..f6e494e7b70 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -135,7 +135,7 @@ class RBACGroup: public RBACObject RBACRoleContainer _roles; ///> Set of Roles
};
-/*
+/**
* @name RBACData
* @brief Contains all needed information about the acccount
*
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 97d414b9915..9cec59db168 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -145,7 +145,7 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapCreature(), lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(0), m_lootRecipientGroup(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), -m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_AlreadyCallAssistance(false), +m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(1), m_originalEquipmentId(1), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_creatureInfo(NULL), m_creatureData(NULL), m_path_id(0), m_formation(NULL) { @@ -323,8 +323,8 @@ bool Creature::InitEntry(uint32 Entry, uint32 /*team*/, const CreatureData* data // Load creature equipment if (!data || data->equipmentId == 0) // use default from the template - LoadEquipment(cinfo->equipmentId); - else if (data && data->equipmentId != -1) // override, -1 means no equipment + LoadEquipment(GetOriginalEquipmentId()); + else if (data && data->equipmentId != 0) // override, 0 means no equipment LoadEquipment(data->equipmentId); SetName(normalInfo->Name); // at normal entry always @@ -1080,7 +1080,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.mapid = mapid; data.phaseMask = phaseMask; data.displayid = displayId; - data.equipmentId = GetEquipmentId(); + data.equipmentId = GetCurrentEquipmentId(); data.posX = GetPositionX(); data.posY = GetPositionY(); data.posZ = GetPositionZMinusOffset(); @@ -1115,7 +1115,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) stmt->setUInt8(index++, spawnMask); stmt->setUInt16(index++, uint16(GetPhaseMask())); stmt->setUInt32(index++, displayId); - stmt->setInt32(index++, int32(GetEquipmentId())); + stmt->setInt32(index++, int32(GetCurrentEquipmentId())); stmt->setFloat(index++, GetPositionX()); stmt->setFloat(index++, GetPositionY()); stmt->setFloat(index++, GetPositionZ()); @@ -1270,14 +1270,14 @@ bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint3 if (!vehId) vehId = cinfo->VehicleId; - if (vehId && !CreateVehicleKit(vehId, Entry)) - vehId = 0; - Object::_Create(guidlow, Entry, vehId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT); if (!UpdateEntry(Entry, team, data)) return false; + if (vehId && !CreateVehicleKit(vehId, Entry)) + vehId = 0; + return true; } @@ -1355,24 +1355,24 @@ bool Creature::LoadCreatureFromDB(uint32 guid, Map* map, bool addToMap) return true; } -void Creature::LoadEquipment(uint32 equip_entry, bool force) +void Creature::LoadEquipment(int8 id, bool force /*= true*/) { - if (equip_entry == 0) + if (id == 0) { if (force) { - for (uint8 i = 0; i < 3; ++i) + for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i) SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, 0); m_equipmentId = 0; } return; } - EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(equip_entry); + EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(GetEntry(), id); if (!einfo) return; - m_equipmentId = equip_entry; + m_equipmentId = id; for (uint8 i = 0; i < 3; ++i) SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, einfo->ItemEntry[i]); } diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 255014a7bbf..6e737709f99 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -138,7 +138,6 @@ struct CreatureTemplate uint32 questItems[MAX_CREATURE_QUEST_ITEMS]; uint32 movementId; bool RegenHealth; - uint32 equipmentId; uint32 MechanicImmuneMask; uint32 flags_extra; uint32 ScriptID; @@ -234,7 +233,8 @@ struct EquipmentInfo }; // Benchmarked: Faster than std::map (insert/find) -typedef UNORDERED_MAP<uint16, EquipmentInfo> EquipmentInfoContainer; +typedef UNORDERED_MAP<uint8, EquipmentInfo> EquipmentInfoContainerInternal; +typedef UNORDERED_MAP<uint32, EquipmentInfoContainerInternal> EquipmentInfoContainer; // from `creature` table struct CreatureData @@ -244,7 +244,7 @@ struct CreatureData uint16 mapid; uint16 phaseMask; uint32 displayid; - int32 equipmentId; + int8 equipmentId; float posX; float posY; float posZ; @@ -453,13 +453,12 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature bool Create(uint32 guidlow, Map* map, uint32 phaseMask, uint32 Entry, uint32 vehId, uint32 team, float x, float y, float z, float ang, const CreatureData* data = NULL); bool LoadCreaturesAddon(bool reload = false); void SelectLevel(const CreatureTemplate* cinfo); - void LoadEquipment(uint32 equip_entry, bool force=false); + void LoadEquipment(int8 id = 1, bool force = false); uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; } void Update(uint32 time); // overwrited Unit::Update void GetRespawnPosition(float &x, float &y, float &z, float* ori = NULL, float* dist =NULL) const; - uint32 GetEquipmentId() const { return GetCreatureTemplate()->equipmentId; } void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; } uint32 GetCorpseDelay() const { return m_corpseDelay; } @@ -552,8 +551,11 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature void UpdateMaxPower(Powers power); void UpdateAttackPowerAndDamage(bool ranged = false); void UpdateDamagePhysical(WeaponAttackType attType); + + uint8 GetOriginalEquipmentId() const { return m_originalEquipmentId; } uint32 GetCurrentEquipmentId() { return m_equipmentId; } - void SetCurrentEquipmentId(uint32 entry) { m_equipmentId = entry; } + void SetCurrentEquipmentId(uint8 id) { m_equipmentId = id; } + float GetSpellDamageMod(int32 Rank); VendorItemData const* GetVendorItems() const; @@ -748,7 +750,8 @@ class Creature : public Unit, public GridObject<Creature>, public MapCreature void Regenerate(Powers power); MovementGeneratorType m_defaultMovementType; uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid - uint32 m_equipmentId; + uint8 m_equipmentId; + uint8 m_originalEquipmentId; bool m_AlreadyCallAssistance; bool m_AlreadySearchedAssistance; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 19de01ffd41..d72b906e6a5 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -26027,7 +26027,7 @@ bool Player::AddItem(uint32 itemId, uint32 count) ItemPosCountVec dest; InventoryResult msg = CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount); if (msg != EQUIP_ERR_OK) - count = noSpaceForCount; + count -= noSpaceForCount; if (count == 0 || dest.empty()) { diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 9034c26d4f1..920e0357bc5 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -29,6 +29,8 @@ #include "QuestDef.h" #include "SpellMgr.h" #include "Unit.h" +#include "Opcodes.h" +#include "WorldSession.h" #include <string> #include <vector> @@ -1813,6 +1815,11 @@ class Player : public Unit, public GridObject<Player> return mMitems.erase(id) ? true : false; } + void SendOnCancelExpectedVehicleRideAura() + { + WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); + GetSession()->SendPacket(&data); + } void PetSpellInitialize(); void CharmSpellInitialize(); void PossessSpellInitialize(); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f7bc6a99fb1..d1f42de5749 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -2936,7 +2936,9 @@ void Unit::DeMorph() Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint8 effMask, Unit* caster, int32* baseAmount /*= NULL*/, Item* castItem /*= NULL*/, uint64 casterGUID /*= 0*/) { ASSERT(casterGUID || caster); - if (!casterGUID) + + // Check if these can stack anyway + if (!casterGUID && !newAura->IsStackableOnOneSlotWithDifferentCasters()) casterGUID = caster->GetGUID(); // passive and Incanter's Absorption and auras with different type can stack with themselves any number of times @@ -7585,48 +7587,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg return false; break; } - // Shadow's Fate (Shadowmourne questline) - case 71169: - { - // Victim needs more checks so bugs, rats or summons can not be affected by the proc. - if (GetTypeId() != TYPEID_PLAYER || !victim || victim->GetTypeId() != TYPEID_UNIT || victim->GetCreatureType() == CREATURE_TYPE_CRITTER) - return false; - - Player* player = ToPlayer(); - if (player->GetQuestStatus(24547) == QUEST_STATUS_INCOMPLETE) - { - break; - } - else if (player->GetDifficulty(true) == RAID_DIFFICULTY_25MAN_NORMAL || player->GetDifficulty(true) == RAID_DIFFICULTY_25MAN_HEROIC) - { - uint32 spellId = 0; - uint32 questId = 0; - switch (victim->GetEntry()) - { - case 36678: // NPC: Professor Putricide - questId = 24749; // Quest: Unholy Infusion - spellId = 71516; // Spell: Shadow Infusion - break; - case 37955: // NPC: Blood-Queen Lana'thel - questId = 24756; // Quest: Blood Infusion - spellId = 72154; // Spell: Thirst Quenched - break; - case 36853: // NPC: Sindragosa - questId = 24757; // Quest: Frost Infusion - spellId = 72290; // Spell: Frost-Imbued Blade - break; - default: - return false; - } - - if (player->GetQuestStatus(questId) != QUEST_STATUS_INCOMPLETE || !player->HasAura(spellId)) - return false; - - break; - } - else - return false; - } } if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(trigger_spell_id)) @@ -10248,8 +10208,6 @@ void Unit::Mount(uint32 mount, uint32 VehicleId, uint32 creatureEntry) { if (CreateVehicleKit(VehicleId, creatureEntry)) { - GetVehicleKit()->Reset(); - // Send others that we now have a vehicle WorldPacket data(SMSG_PLAYER_VEHICLE_DATA, GetPackGUID().size()+4); data.appendPackGUID(GetGUID()); @@ -14823,7 +14781,10 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au // dismount players when charmed if (GetTypeId() == TYPEID_PLAYER) - Dismount(); + RemoveAurasByType(SPELL_AURA_MOUNTED); + + if (charmer->GetTypeId() == TYPEID_PLAYER) + charmer->RemoveAurasByType(SPELL_AURA_MOUNTED); ASSERT(type != CHARM_TYPE_POSSESS || charmer->GetTypeId() == TYPEID_PLAYER); ASSERT((type == CHARM_TYPE_VEHICLE) == IsVehicle()); @@ -16082,31 +16043,10 @@ void Unit::_EnterVehicle(Vehicle* vehicle, int8 seatId, AuraApplication const* a { if (vehicle->GetBase()->GetTypeId() == TYPEID_PLAYER && player->isInCombat()) return; - - InterruptNonMeleeSpells(false); - player->StopCastingCharm(); - player->StopCastingBindSight(); - Dismount(); - RemoveAurasByType(SPELL_AURA_MOUNTED); - - // drop flag at invisible in bg - if (Battleground* bg = player->GetBattleground()) - bg->EventPlayerDroppedFlag(player); - - WorldPacket data(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); - player->GetSession()->SendPacket(&data); - - player->UnsummonPetTemporaryIfAny(); } ASSERT(!m_vehicle); - m_vehicle = vehicle; - - if (!m_vehicle->AddPassenger(this, seatId)) - { - m_vehicle = NULL; - return; - } + (void)vehicle->AddPassenger(this, seatId); } void Unit::ChangeSeat(int8 seatId, bool next) @@ -16114,15 +16054,17 @@ void Unit::ChangeSeat(int8 seatId, bool next) if (!m_vehicle) return; - if (seatId < 0) - { - seatId = m_vehicle->GetNextEmptySeat(GetTransSeat(), next); - if (seatId < 0) - return; - } - else if (seatId == GetTransSeat() || !m_vehicle->HasEmptySeat(seatId)) + // Don't change if current and new seat are identical + if (seatId == GetTransSeat()) + return; + + SeatMap::const_iterator seat = (seatId < 0 ? m_vehicle->GetNextEmptySeat(GetTransSeat(), next) : m_vehicle->Seats.find(seatId)); + // The second part of the check will only return true if seatId >= 0. @Vehicle::GetNextEmptySeat makes sure of that. + if (seat == m_vehicle->Seats.end() || seat->second.Passenger) return; + // Todo: the functions below could be consolidated and refactored to take + // SeatMap::const_iterator as parameter, to save redundant map lookups. m_vehicle->RemovePassenger(this); if (!m_vehicle->AddPassenger(this, seatId)) ASSERT(false); @@ -16149,6 +16091,9 @@ void Unit::ExitVehicle(Position const* /*exitPosition*/) void Unit::_ExitVehicle(Position const* exitPosition) { + /// It's possible m_vehicle is NULL, when this function is called indirectly from @VehicleJoinEvent::Abort. + /// In that case it was not possible to add the passenger to the vehicle. The vehicle aura has already been removed + /// from the target in the aforementioned function and we don't need to do anything else at this point. if (!m_vehicle) return; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index a8e088b2dd3..f9a0ca80f90 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -332,6 +332,7 @@ class UnitAI; class Totem; class Transport; class Vehicle; +class VehicleJoinEvent; class TransportBase; class SpellCastTargets; @@ -2170,6 +2171,7 @@ class Unit : public WorldObject uint32 GetRedirectThreatPercent() { return _redirectThreadInfo.GetThreatPct(); } Unit* GetRedirectThreatTarget() { return _redirectThreadInfo.GetTargetGUID() ? GetUnit(*this, _redirectThreadInfo.GetTargetGUID()) : NULL; } + friend class VehicleJoinEvent; bool IsAIEnabled, NeedChangeAI; bool CreateVehicleKit(uint32 id, uint32 creatureEntry); void RemoveVehicleKit(); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 16a90018293..02c5e4a2e1a 100755..100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -30,9 +30,12 @@ #include "SpellInfo.h" #include "MoveSplineInit.h" #include "TemporarySummon.h" +#include "EventProcessor.h" +#include "Player.h" +#include "Battleground.h" Vehicle::Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry) : -_me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntry), _status(STATUS_NONE) +UsableSeatNum(0), _me(unit), _vehicleInfo(vehInfo), _creatureEntry(creatureEntry), _status(STATUS_NONE) { for (uint32 i = 0; i < MAX_VEHICLE_SEATS; ++i) { @@ -41,10 +44,16 @@ _me(unit), _vehicleInfo(vehInfo), _usableSeatNum(0), _creatureEntry(creatureEntr { Seats.insert(std::make_pair(i, VehicleSeat(veSeat))); if (veSeat->CanEnterOrExit()) - ++_usableSeatNum; + ++UsableSeatNum; } } + // Set or remove correct flags based on available seats. Will overwrite db data (if wrong). + if (UsableSeatNum) + _me->SetFlag(UNIT_NPC_FLAGS, (_me->GetTypeId() == TYPEID_PLAYER ? UNIT_NPC_FLAG_PLAYER_VEHICLE : UNIT_NPC_FLAG_SPELLCLICK)); + else + _me->RemoveFlag(UNIT_NPC_FLAGS, (_me->GetTypeId() == TYPEID_PLAYER ? UNIT_NPC_FLAG_PLAYER_VEHICLE : UNIT_NPC_FLAG_SPELLCLICK)); + InitMovementInfoForBase(); } @@ -56,6 +65,15 @@ Vehicle::~Vehicle() ASSERT(!itr->second.Passenger); } +/** + * @fn void Vehicle::Install() + * + * @brief Initializes power type for vehicle. Nothing more. + * + * @author Machiavelli + * @date 17-2-2013 + */ + void Vehicle::Install() { if (Creature* creature = _me->ToCreature()) @@ -114,15 +132,26 @@ void Vehicle::InstallAllAccessories(bool evading) InstallAccessory(itr->AccessoryEntry, itr->SeatId, itr->IsMinion, itr->SummonedType, itr->SummonTime); } +/** + * @fn void Vehicle::Uninstall() + * + * @brief Removes all passengers and sets status to STATUS_UNINSTALLING. + * No new passengers can be added to the vehicle after this call. + * + * @author Machiavelli + * @date 17-2-2013 + */ + void Vehicle::Uninstall() { /// @Prevent recursive uninstall call. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.) - if (_status == STATUS_UNINSTALLING) + if (_status == STATUS_UNINSTALLING && !GetBase()->HasUnitTypeMask(UNIT_MASK_MINION)) { sLog->outError(LOG_FILTER_VEHICLES, "Vehicle GuidLow: %u, Entry: %u attempts to uninstall, but already has STATUS_UNINSTALLING! " "Check Uninstall/PassengerBoarded script hooks for errors.", _me->GetGUIDLow(), _me->GetEntry()); return; } + _status = STATUS_UNINSTALLING; sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::Uninstall Entry: %u, GuidLow: %u", _creatureEntry, _me->GetGUIDLow()); RemoveAllPassengers(); @@ -131,26 +160,39 @@ void Vehicle::Uninstall() sScriptMgr->OnUninstall(this); } +/** + * @fn void Vehicle::Reset(bool evading ) + * + * @brief Reapplies immunities and reinstalls accessories. Only has effect for creatures. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param evading true if called from CreatureAI::EnterEvadeMode + */ + void Vehicle::Reset(bool evading /*= false*/) { - sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::Reset Entry: %u, GuidLow: %u", _creatureEntry, _me->GetGUIDLow()); - if (_me->GetTypeId() == TYPEID_PLAYER) - { - if (_usableSeatNum) - _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); - } - else - { - ApplyAllImmunities(); - InstallAllAccessories(evading); - if (_usableSeatNum) - _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } + if (GetBase()->GetTypeId() != TYPEID_UNIT) + return; - if (GetBase()->GetTypeId() == TYPEID_UNIT) - sScriptMgr->OnReset(this); + sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::Reset (Entry: %u, GuidLow: %u, DBGuid: %u)", GetCreatureEntry(), _me->GetGUIDLow(), _me->ToCreature()->GetDBTableGUIDLow()); + + ApplyAllImmunities(); + InstallAllAccessories(evading); + + sScriptMgr->OnReset(this); } +/** + * @fn void Vehicle::ApplyAllImmunities() + * + * @brief Applies specific immunities that cannot be set in DB. + * + * @author Machiavelli + * @date 17-2-2013 + */ + void Vehicle::ApplyAllImmunities() { // This couldn't be done in DB, because some spells have MECHANIC_NONE @@ -199,10 +241,28 @@ void Vehicle::ApplyAllImmunities() } } +/** + * @fn void Vehicle::RemoveAllPassengers() + * + * @brief Removes all current and pending passengers from the vehicle. + * + * @author Machiavelli + * @date 17-2-2013 + */ + void Vehicle::RemoveAllPassengers() { sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::RemoveAllPassengers. Entry: %u, GuidLow: %u", _creatureEntry, _me->GetGUIDLow()); + /// Setting to_Abort to true will cause @VehicleJoinEvent::Abort to be executed on next @Unit::UpdateEvents call + /// This will properly "reset" the pending join process for the passenger. + while (_pendingJoinEvents.size()) + { + VehicleJoinEvent* e = _pendingJoinEvents.front(); + e->to_Abort = true; + _pendingJoinEvents.pop_front(); + } + // Passengers always cast an aura with SPELL_AURA_CONTROL_VEHICLE on the vehicle // We just remove the aura and the unapply handler will make the target leave the vehicle. // We don't need to iterate over Seats @@ -215,6 +275,19 @@ void Vehicle::RemoveAllPassengers() // ASSERT(!itr->second.passenger); } +/** + * @fn bool Vehicle::HasEmptySeat(int8 seatId) const + * + * @brief Checks if vehicle's seat specified by 'seatId' is empty. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param seatId Identifier for the seat. + * + * @return true if empty seat, false if not. + */ + bool Vehicle::HasEmptySeat(int8 seatId) const { SeatMap::const_iterator seat = Seats.find(seatId); @@ -223,6 +296,19 @@ bool Vehicle::HasEmptySeat(int8 seatId) const return !seat->second.Passenger; } +/** + * @fn Unit* Vehicle::GetPassenger(int8 seatId) const + * + * @brief Gets a passenger on specified seat. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param seatId Seat to look on. + * + * @return null if it not found, else pointer to passenger if in world + */ + Unit* Vehicle::GetPassenger(int8 seatId) const { SeatMap::const_iterator seat = Seats.find(seatId); @@ -232,78 +318,105 @@ Unit* Vehicle::GetPassenger(int8 seatId) const return ObjectAccessor::GetUnit(*GetBase(), seat->second.Passenger); } -int8 Vehicle::GetNextEmptySeat(int8 seatId, bool next) const +/** + * @fn SeatMap::const_iterator Vehicle::GetNextEmptySeat(int8 seatId, bool next) const + * + * @brief Gets the next empty seat based on current seat. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param seatId Identifier for the current seat. + * @param next true if iterating forward, false means iterating backwards. + * + * @return The next empty seat. + */ + +SeatMap::const_iterator Vehicle::GetNextEmptySeat(int8 seatId, bool next) const { SeatMap::const_iterator seat = Seats.find(seatId); if (seat == Seats.end()) - return -1; + return seat; while (seat->second.Passenger || (!seat->second.SeatInfo->CanEnterOrExit() && !seat->second.SeatInfo->IsUsableByOverride())) { if (next) { - ++seat; - if (seat == Seats.end()) + if (++seat == Seats.end()) seat = Seats.begin(); } else { - if (seat == Seats.begin()) + if (seat-- == Seats.begin()) seat = Seats.end(); - --seat; } + // Make sure we don't loop indefinetly if (seat->first == seatId) - return -1; // no available seat + return Seats.end(); } - return seat->first; + return seat; } +/** + * @fn void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 type, + * uint32 summonTime) + * + * @brief Installs an accessory. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param entry The NPC entry of accessory. + * @param seatId Identifier for the seat to add the accessory to. + * @param minion true if accessory considered a 'minion'. Implies that the accessory will despawn when the vehicle despawns. + * Essentially that it has no life without the vehicle. Their fates are bound. + * @param type See enum @SummonType. + * @param summonTime Time after which the minion is despawned in case of a timed despawn @type specified. + */ + void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 type, uint32 summonTime) { /// @Prevent adding accessories when vehicle is uninstalling. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.) if (_status == STATUS_UNINSTALLING) { - sLog->outError(LOG_FILTER_VEHICLES, "Vehicle GuidLow: %u, Entry: %u attempts to install accessory Entry: %u on seat %d with STATUS_UNINSTALLING! " - "Check Uninstall/PassengerBoarded script hooks for errors.", _me->GetGUIDLow(), _me->GetEntry(), entry, (int32)seatId); + sLog->outError(LOG_FILTER_VEHICLES, "Vehicle (GuidLow: %u, DB GUID: %u, Entry: %u) attempts to install accessory (Entry: %u) on seat %i with STATUS_UNINSTALLING! " + "Check Uninstall/PassengerBoarded script hooks for errors.", _me->GetGUIDLow(), + (_me->GetTypeId() == TYPEID_UNIT ? _me->ToCreature()->GetDBTableGUIDLow() : _me->GetGUIDLow()), GetCreatureEntry(), entry, (int32)seatId); return; } - sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle: Installing accessory entry %u on vehicle entry %u (seat:%i)", entry, GetCreatureEntry(), seatId); - if (Unit* passenger = GetPassenger(seatId)) - { - // already installed - if (passenger->GetEntry() == entry) - { - ASSERT(passenger->GetTypeId() == TYPEID_UNIT); - if (_me->GetTypeId() == TYPEID_UNIT) - { - if (_me->ToCreature()->IsInEvadeMode() && passenger->ToCreature()->IsAIEnabled) - passenger->ToCreature()->AI()->EnterEvadeMode(); - return; - } - } - else - passenger->ExitVehicle(); // this should not happen - } + sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle (GuidLow: %u, DB Guid: %u, Entry %u): installing accessory (Entry: %u) on seat: %i", + _me->GetGUIDLow(), (_me->GetTypeId() == TYPEID_UNIT ? _me->ToCreature()->GetDBTableGUIDLow() : _me->GetGUIDLow()), GetCreatureEntry(), + entry, (int32)seatId); - if (TempSummon* accessory = _me->SummonCreature(entry, *_me, TempSummonType(type), summonTime)) - { - if (minion) - accessory->AddUnitTypeMask(UNIT_MASK_ACCESSORY); + TempSummon* accessory = _me->SummonCreature(entry, *_me, TempSummonType(type), summonTime); + ASSERT(accessory); - if (!_me->HandleSpellClick(accessory, seatId)) - { - accessory->UnSummon(); - return; - } + if (minion) + accessory->AddUnitTypeMask(UNIT_MASK_ACCESSORY); - if (GetBase()->GetTypeId() == TYPEID_UNIT) - sScriptMgr->OnInstallAccessory(this, accessory); - } + (void)_me->HandleSpellClick(accessory, seatId); + + /// If for some reason adding accessory to vehicle fails it will unsummon in + /// @VehicleJoinEvent::Abort } +/** + * @fn bool Vehicle::AddPassenger(Unit* unit, int8 seatId) + * + * @brief Attempts to add a passenger to the vehicle on 'seatId'. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param [in,out] The prospective passenger. + * @param seatId Identifier for the seat. Value of -1 indicates the next available seat. + * + * @return true if it succeeds, false if it fails. + */ + bool Vehicle::AddPassenger(Unit* unit, int8 seatId) { /// @Prevent adding passengers when vehicle is uninstalling. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.) @@ -314,10 +427,19 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) return false; } - if (unit->GetVehicle() != this) - return false; + sLog->outDebug(LOG_FILTER_VEHICLES, "Unit %s scheduling enter vehicle (entry: %u, vehicleId: %u, guid: %u (dbguid: %s) on seat %d", + unit->GetName().c_str(), _me->GetEntry(), _vehicleInfo->m_ID, _me->GetGUIDLow(), + (_me->GetTypeId() == TYPEID_UNIT ? _me->ToCreature()->GetDBTableGUIDLow() : 0), (int32)seatId); + // The seat selection code may kick other passengers off the vehicle. + // While the validity of the following may be arguable, it is possible that when such a passenger + // exits the vehicle will dismiss. That's why the actual adding the passenger to the vehicle is scheduled + // asynchronously, so it can be cancelled easily in case the vehicle is uninstalled meanwhile. SeatMap::iterator seat; + VehicleJoinEvent* e = new VehicleJoinEvent(this, unit); + unit->m_Events.AddEvent(e, unit->m_Events.CalculateTime(0)); + _pendingJoinEvents.push_back(e); + if (seatId < 0) // no specific seat requirement { for (seat = Seats.begin(); seat != Seats.end(); ++seat) @@ -325,86 +447,47 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) break; if (seat == Seats.end()) // no available seat + { + CancelJoinEvent(e); return false; + } + + e->Seat = seat; } else { seat = Seats.find(seatId); if (seat == Seats.end()) + { + CancelJoinEvent(e); return false; + } + e->Seat = seat; if (seat->second.Passenger) { - if (Unit* passenger = ObjectAccessor::GetUnit(*GetBase(), seat->second.Passenger)) - passenger->ExitVehicle(); - else - seat->second.Passenger = 0; + Unit* passenger = ObjectAccessor::GetUnit(*GetBase(), seat->second.Passenger); + ASSERT(passenger); + passenger->ExitVehicle(); } ASSERT(!seat->second.Passenger); } - sLog->outDebug(LOG_FILTER_VEHICLES, "Unit %s enter vehicle entry %u id %u dbguid %u seat %d", unit->GetName().c_str(), _me->GetEntry(), _vehicleInfo->m_ID, _me->GetGUIDLow(), (int32)seat->first); - - seat->second.Passenger = unit->GetGUID(); - if (seat->second.SeatInfo->CanEnterOrExit()) - { - ASSERT(_usableSeatNum); - --_usableSeatNum; - if (!_usableSeatNum) - { - if (_me->GetTypeId() == TYPEID_PLAYER) - _me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); - else - _me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } - } - - if (seat->second.SeatInfo->m_flags && !(seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING)) - unit->AddUnitState(UNIT_STATE_ONVEHICLE); - - VehicleSeatEntry const* veSeat = seat->second.SeatInfo; - unit->m_movementInfo.t_pos.m_positionX = veSeat->m_attachmentOffsetX; - unit->m_movementInfo.t_pos.m_positionY = veSeat->m_attachmentOffsetY; - unit->m_movementInfo.t_pos.m_positionZ = veSeat->m_attachmentOffsetZ; - unit->m_movementInfo.t_pos.SetOrientation(0); - unit->m_movementInfo.t_time = 0; // 1 for player - unit->m_movementInfo.t_seat = seat->first; - unit->m_movementInfo.t_guid = _me->GetGUID(); - - if (_me->GetTypeId() == TYPEID_UNIT && unit->GetTypeId() == TYPEID_PLAYER && - seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_CAN_CONTROL) - ASSERT(_me->SetCharmedBy(unit, CHARM_TYPE_VEHICLE)) - - if (_me->IsInWorld()) - { - unit->SendClearTarget(); // SMSG_BREAK_TARGET - unit->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures) - // also adds MOVEMENTFLAG_ROOT - Movement::MoveSplineInit init(unit); - init.DisableTransportPathTransformations(); - init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ); - init.SetFacing(0.0f); - init.SetTransportEnter(); - init.Launch(); - - if (_me->GetTypeId() == TYPEID_UNIT) - { - if (_me->ToCreature()->IsAIEnabled) - _me->ToCreature()->AI()->PassengerBoarded(unit, seat->first, true); - - // update all passenger's positions - //Passenger's spline OR vehicle movement will update positions - //RelocatePassengers(_me->GetPositionX(), _me->GetPositionY(), _me->GetPositionZ(), _me->GetOrientation()); - } - } - - if (GetBase()->GetTypeId() == TYPEID_UNIT) - sScriptMgr->OnAddPassenger(this, unit, seatId); - return true; } +/** + * @fn void Vehicle::RemovePassenger(Unit* unit) + * + * @brief Removes the passenger from the vehicle. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param [in,out] unit The passenger to remove.. + */ + void Vehicle::RemovePassenger(Unit* unit) { if (unit->GetVehicle() != this) @@ -413,20 +496,12 @@ void Vehicle::RemovePassenger(Unit* unit) SeatMap::iterator seat = GetSeatIteratorForPassenger(unit); ASSERT(seat != Seats.end()); - sLog->outDebug(LOG_FILTER_VEHICLES, "Unit %s exit vehicle entry %u id %u dbguid %u seat %d", unit->GetName().c_str(), _me->GetEntry(), _vehicleInfo->m_ID, _me->GetGUIDLow(), (int32)seat->first); + sLog->outDebug(LOG_FILTER_VEHICLES, "Unit %s exit vehicle entry %u id %u dbguid %u seat %d", + unit->GetName().c_str(), _me->GetEntry(), _vehicleInfo->m_ID, _me->GetGUIDLow(), (int32)seat->first); seat->second.Passenger = 0; - if (seat->second.SeatInfo->CanEnterOrExit()) - { - if (!_usableSeatNum) - { - if (_me->GetTypeId() == TYPEID_PLAYER) - _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); - else - _me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); - } - ++_usableSeatNum; - } + if (seat->second.SeatInfo->CanEnterOrExit() && ++UsableSeatNum) + _me->SetFlag(UNIT_NPC_FLAGS, (_me->GetTypeId() == TYPEID_PLAYER ? UNIT_NPC_FLAG_PLAYER_VEHICLE : UNIT_NPC_FLAG_SPELLCLICK)); unit->ClearUnitState(UNIT_STATE_ONVEHICLE); @@ -435,7 +510,7 @@ void Vehicle::RemovePassenger(Unit* unit) if (_me->IsInWorld()) { - unit->m_movementInfo.t_pos.Relocate(0, 0, 0, 0); + unit->m_movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); unit->m_movementInfo.t_time = 0; unit->m_movementInfo.t_seat = 0; } @@ -451,7 +526,15 @@ void Vehicle::RemovePassenger(Unit* unit) sScriptMgr->OnRemovePassenger(this, unit); } -//! Must be called after m_base::Relocate +/** + * @fn void Vehicle::RelocatePassengers() + * + * @brief Relocate passengers. Must be called after m_base::Relocate + * + * @author Machiavelli + * @date 17-2-2013 + */ + void Vehicle::RelocatePassengers() { ASSERT(_me->GetMap()); @@ -466,21 +549,40 @@ void Vehicle::RelocatePassengers() float px, py, pz, po; passenger->m_movementInfo.t_pos.GetPosition(px, py, pz, po); CalculatePassengerPosition(px, py, pz, po); + passenger->UpdatePosition(px, py, pz, po); } } } +/** + * @fn void Vehicle::Dismiss() + * + * @brief Dismiss the vehicle. Removes passengers and despawns self. Only valid for creatures. + * + * @author Machiavelli + * @date 17-2-2013 + */ + void Vehicle::Dismiss() { if (GetBase()->GetTypeId() != TYPEID_UNIT) return; - sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::Dismiss Entry: %u, GuidLow %u", _creatureEntry, _me->GetGUIDLow()); + sLog->outDebug(LOG_FILTER_VEHICLES, "Vehicle::Dismiss Entry: %u, GuidLow %u, DBGuid: %u", GetCreatureEntry(), _me->GetGUIDLow(), _me->ToCreature()->GetDBTableGUIDLow()); Uninstall(); GetBase()->ToCreature()->DespawnOrUnsummon(); } +/** + * @fn void Vehicle::InitMovementInfoForBase() + * + * @brief Sets correct MovementFlags2 based on VehicleFlags from DBC. + * + * @author Machiavelli + * @date 17-2-2013 + */ + void Vehicle::InitMovementInfoForBase() { uint32 vehicleFlags = GetVehicleInfo()->m_flags; @@ -497,6 +599,19 @@ void Vehicle::InitMovementInfoForBase() _me->AddExtraUnitMovementFlag(MOVEMENTFLAG2_FULL_SPEED_PITCHING); } +/** + * @fn VehicleSeatEntry const* Vehicle::GetSeatForPassenger(Unit* passenger) + * + * @brief Returns information on the seat of specified passenger, represented by the format in VehicleSeat.dbc + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param [in,out] The passenger for which we check the seat info. + * + * @return null if passenger not found on vehicle, else the DBC record for the seat. + */ + VehicleSeatEntry const* Vehicle::GetSeatForPassenger(Unit* passenger) { SeatMap::iterator itr; @@ -507,6 +622,19 @@ VehicleSeatEntry const* Vehicle::GetSeatForPassenger(Unit* passenger) return NULL; } +/** + * @fn SeatMap::iterator Vehicle::GetSeatIteratorForPassenger(Unit* passenger) + * + * @brief Gets seat iterator for specified passenger. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param [in,out] passenger Passenger to look up. + * + * @return The seat iterator for specified passenger if it's found on the vehicle. Otherwise Seats.end() (invalid iterator). + */ + SeatMap::iterator Vehicle::GetSeatIteratorForPassenger(Unit* passenger) { SeatMap::iterator itr; @@ -517,6 +645,17 @@ SeatMap::iterator Vehicle::GetSeatIteratorForPassenger(Unit* passenger) return Seats.end(); } +/** + * @fn uint8 Vehicle::GetAvailableSeatCount() const + * + * @brief Gets the available seat count. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @return The available seat count. + */ + uint8 Vehicle::GetAvailableSeatCount() const { uint8 ret = 0; @@ -547,3 +686,139 @@ void Vehicle::CalculatePassengerOffset(float& x, float& y, float& z, float& o) y = (iny - inx * tan(GetBase()->GetOrientation())) / (cos(GetBase()->GetOrientation()) + std::sin(GetBase()->GetOrientation()) * tan(GetBase()->GetOrientation())); x = (inx + iny * tan(GetBase()->GetOrientation())) / (cos(GetBase()->GetOrientation()) + std::sin(GetBase()->GetOrientation()) * tan(GetBase()->GetOrientation())); } + +/** + * @fn void Vehicle::CancelJoinEvent(VehicleJoinEvent* e) + * + * @brief Aborts delayed @VehicleJoinEvent objects. + * Implies that the related unit will not be boarding the vehicle after all. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param [in,out] e The VehicleJoinEvent* to process. + */ + +void Vehicle::CancelJoinEvent(VehicleJoinEvent* e) +{ + e->to_Abort = true; + _pendingJoinEvents.pop_back(); +} + +/** + * @fn bool VehicleJoinEvent::Execute(uint64, uint32) + * + * @brief Actually adds the passenger @Passenger to vehicle @Target. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param parameter1 Unused + * @param parameter2 Unused. + * + * @return true, cannot fail. + * + */ + +bool VehicleJoinEvent::Execute(uint64, uint32) +{ + ASSERT(Passenger->IsInWorld()); + ASSERT(Target->GetBase()->IsInWorld()); + + Passenger->m_vehicle = Target; + Seat->second.Passenger = Passenger->GetGUID(); + if (Seat->second.SeatInfo->CanEnterOrExit()) + { + ASSERT(Target->UsableSeatNum); + --(Target->UsableSeatNum); + if (!Target->UsableSeatNum) + { + if (Target->GetBase()->GetTypeId() == TYPEID_PLAYER) + Target->GetBase()->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); + else + Target->GetBase()->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + } + } + + Passenger->InterruptNonMeleeSpells(false); + + Player* player = Passenger->ToPlayer(); + if (player) + { + // drop flag + if (Battleground* bg = player->GetBattleground()) + bg->EventPlayerDroppedFlag(player); + + player->StopCastingCharm(); + player->StopCastingBindSight(); + player->SendOnCancelExpectedVehicleRideAura(); + player->UnsummonPetTemporaryIfAny(); + } + + if (Seat->second.SeatInfo->m_flags && !(Seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING)) + Passenger->AddUnitState(UNIT_STATE_ONVEHICLE); + + VehicleSeatEntry const* veSeat = Seat->second.SeatInfo; + Passenger->m_movementInfo.t_pos.Relocate(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ); + Passenger->m_movementInfo.t_time = 0; // 1 for player + Passenger->m_movementInfo.t_seat = Seat->first; + Passenger->m_movementInfo.t_guid = Target->GetBase()->GetGUID(); + + if (Target->GetBase()->GetTypeId() == TYPEID_UNIT && Passenger->GetTypeId() == TYPEID_PLAYER && + Seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_CAN_CONTROL) + ASSERT(Target->GetBase()->SetCharmedBy(Passenger, CHARM_TYPE_VEHICLE)) // SMSG_CLIENT_CONTROL + + Passenger->SendClearTarget(); // SMSG_BREAK_TARGET + Passenger->SetControlled(true, UNIT_STATE_ROOT); // SMSG_FORCE_ROOT - In some cases we send SMSG_SPLINE_MOVE_ROOT here (for creatures) + // also adds MOVEMENTFLAG_ROOT + + Movement::MoveSplineInit init(Passenger); + init.DisableTransportPathTransformations(); + init.MoveTo(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ, false, true); + init.SetFacing(0.0f); + init.SetTransportEnter(); + init.Launch(); + + if (Target->GetBase()->GetTypeId() == TYPEID_UNIT) + { + if (Target->GetBase()->ToCreature()->IsAIEnabled) + Target->GetBase()->ToCreature()->AI()->PassengerBoarded(Passenger, Seat->first, true); + + sScriptMgr->OnAddPassenger(Target, Passenger, Seat->first); + + // Actually quite a redundant hook. Could just use OnAddPassenger and check for unit typemask inside script. + if (Passenger->HasUnitTypeMask(UNIT_MASK_ACCESSORY)) + sScriptMgr->OnInstallAccessory(Target, Passenger->ToCreature()); + + // update all passenger's positions + //Passenger's spline OR vehicle movement will update positions + //RelocatePassengers(_me->GetPositionX(), _me->GetPositionY(), _me->GetPositionZ(), _me->GetOrientation()); + } + + return true; +} + +/** + * @fn void VehicleJoinEvent::Abort(uint64) + * + * @brief Aborts the event. Implies that unit @Passenger will not be boarding vehicle @Target after all. + * + * @author Machiavelli + * @date 17-2-2013 + * + * @param parameter1 Unused + */ + +void VehicleJoinEvent::Abort(uint64) +{ + sLog->outDebug(LOG_FILTER_VEHICLES, "Passenger GuidLow: %u, Entry: %u, board on vehicle GuidLow: %u, Entry: %u SeatId: %i cancelled", + Passenger->GetGUIDLow(), Passenger->GetEntry(), Target->GetBase()->GetGUIDLow(), Target->GetBase()->GetEntry(), (int32)Seat->first); + + /// @SPELL_AURA_CONTROL_VEHICLE auras can be applied even when the passenger is not (yet) on the vehicle. + /// When this code is triggered it means that something went wrong in @Vehicle::AddPassenger, and we should remove + /// the aura manually. + Target->GetBase()->RemoveAurasByType(SPELL_AURA_CONTROL_VEHICLE, Passenger->GetGUID()); + + if (Passenger->IsInWorld() && Passenger->HasUnitTypeMask(UNIT_MASK_ACCESSORY)) + Passenger->ToCreature()->DespawnOrUnsummon(); +} diff --git a/src/server/game/Entities/Vehicle/Vehicle.h b/src/server/game/Entities/Vehicle/Vehicle.h index 823fb72b8a8..7ec0df8e533 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -23,12 +23,12 @@ #include "Object.h" #include "VehicleDefines.h" #include "Unit.h" +#include <deque> struct VehicleEntry; - class Unit; - typedef std::set<uint64> GuidSet; +class VehicleJoinEvent; class Vehicle : public TransportBase { @@ -46,7 +46,7 @@ class Vehicle : public TransportBase bool HasEmptySeat(int8 seatId) const; Unit* GetPassenger(int8 seatId) const; - int8 GetNextEmptySeat(int8 seatId, bool next) const; + SeatMap::const_iterator GetNextEmptySeat(int8 seatId, bool next) const; uint8 GetAvailableSeatCount() const; bool AddPassenger(Unit* passenger, int8 seatId = -1); @@ -61,11 +61,15 @@ class Vehicle : public TransportBase void SetLastShootPos(Position const& pos) { m_lastShootPos.Relocate(pos); } Position GetLastShootPos() { return m_lastShootPos; } - SeatMap Seats; + SeatMap Seats; ///< The collection of all seats on the vehicle. Including vacant ones. VehicleSeatEntry const* GetSeatForPassenger(Unit* passenger); protected: + friend class VehicleJoinEvent; + uint32 UsableSeatNum; ///< Number of seats that match VehicleSeatEntry::UsableByPlayer, used for proper display flags + + protected: friend bool Unit::CreateVehicleKit(uint32 id, uint32 creatureEntry); Vehicle(Unit* unit, VehicleEntry const* vehInfo, uint32 creatureEntry); friend void Unit::RemoveVehicleKit(); @@ -88,12 +92,27 @@ class Vehicle : public TransportBase /// This method transforms supplied global coordinates into local offsets void CalculatePassengerOffset(float& x, float& y, float& z, float& o); - Unit* _me; - VehicleEntry const* _vehicleInfo; + Unit* _me; ///< The underlying unit with the vehicle kit. Can be player or creature. + VehicleEntry const* _vehicleInfo; ///< DBC data for vehicle GuidSet vehiclePlayers; - uint32 _usableSeatNum; // Number of seats that match VehicleSeatEntry::UsableByPlayer, used for proper display flags - uint32 _creatureEntry; // Can be different than me->GetBase()->GetEntry() in case of players - Status _status; + + uint32 _creatureEntry; ///< Can be different than the entry of _me in case of players + Status _status; ///< Internal variable for sanity checks Position m_lastShootPos; + std::deque<VehicleJoinEvent*> _pendingJoinEvents; ///< Collection of delayed join events for prospective passengers + void CancelJoinEvent(VehicleJoinEvent* e); +}; + +class VehicleJoinEvent : public BasicEvent +{ + friend class Vehicle; + protected: + VehicleJoinEvent(Vehicle* v, Unit* u) : Target(v), Passenger(u), Seat(Target->Seats.end()) {} + bool Execute(uint64, uint32); + void Abort(uint64); + + Vehicle* Target; + Unit* Passenger; + SeatMap::iterator Seat; }; #endif diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index 051d76f46ed..d06ff1fa533 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -450,8 +450,8 @@ void GameEventMgr::LoadFromDB() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 - QueryResult result = WorldDatabase.Query("SELECT creature.guid, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id " + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT creature.guid, creature.id, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id " "FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid"); if (!result) @@ -466,7 +466,8 @@ void GameEventMgr::LoadFromDB() Field* fields = result->Fetch(); uint32 guid = fields[0].GetUInt32(); - uint16 event_id = fields[1].GetUInt8(); + uint32 entry = fields[1].GetUInt32(); + uint16 event_id = fields[2].GetUInt8(); if (event_id >= mGameEventModelEquip.size()) { @@ -476,17 +477,18 @@ void GameEventMgr::LoadFromDB() ModelEquipList& equiplist = mGameEventModelEquip[event_id]; ModelEquip newModelEquipSet; - newModelEquipSet.modelid = fields[2].GetUInt32(); - newModelEquipSet.equipment_id = fields[3].GetUInt32(); + newModelEquipSet.modelid = fields[3].GetUInt32(); + newModelEquipSet.equipment_id = fields[4].GetUInt8(); newModelEquipSet.equipement_id_prev = 0; newModelEquipSet.modelid_prev = 0; if (newModelEquipSet.equipment_id > 0) { - if (!sObjectMgr->GetEquipmentInfo(newModelEquipSet.equipment_id)) + int8 equipId = static_cast<int8>(newModelEquipSet.equipment_id); + if (!sObjectMgr->GetEquipmentInfo(entry, equipId)) { - sLog->outError(LOG_FILTER_SQL, "Table `game_event_model_equip` have creature (Guid: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", - guid, newModelEquipSet.equipment_id); + sLog->outError(LOG_FILTER_SQL, "Table `game_event_model_equip` have creature (Guid: %u, entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", + guid, entry, newModelEquipSet.equipment_id); continue; } } @@ -1103,14 +1105,8 @@ void GameEventMgr::UnApplyEvent(uint16 event_id) void GameEventMgr::ApplyNewEvent(uint16 event_id) { - switch (sWorld->getIntConfig(CONFIG_EVENT_ANNOUNCE)) - { - case 0: // disable - break; - case 1: // announce events - sWorld->SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str()); - break; - } + if (sWorld->getBoolConfig(CONFIG_EVENT_ANNOUNCE)) + sWorld->SendWorldText(LANG_EVENTMESSAGE, mGameEvent[event_id].description.c_str()); sLog->outInfo(LOG_FILTER_GAMEEVENTS, "GameEvent %u \"%s\" started.", event_id, mGameEvent[event_id].description.c_str()); @@ -1332,7 +1328,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) itr->second.equipement_id_prev = creature->GetCurrentEquipmentId(); itr->second.modelid_prev = creature->GetDisplayId(); creature->LoadEquipment(itr->second.equipment_id, true); - if (itr->second.modelid >0 && itr->second.modelid_prev != itr->second.modelid) + if (itr->second.modelid > 0 && itr->second.modelid_prev != itr->second.modelid) { CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(itr->second.modelid); if (minfo) @@ -1347,7 +1343,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) else { creature->LoadEquipment(itr->second.equipement_id_prev, true); - if (itr->second.modelid_prev >0 && itr->second.modelid_prev != itr->second.modelid) + if (itr->second.modelid_prev > 0 && itr->second.modelid_prev != itr->second.modelid) { CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(itr->second.modelid_prev); if (minfo) @@ -1370,7 +1366,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate) sObjectMgr->GetCreatureModelRandomGender(&displayID); if (data2->equipmentId == 0) - itr->second.equipement_id_prev = cinfo->equipmentId; + itr->second.equipement_id_prev = 1; else if (data2->equipmentId != -1) itr->second.equipement_id_prev = data->equipmentId; itr->second.modelid_prev = displayID; diff --git a/src/server/game/Events/GameEventMgr.h b/src/server/game/Events/GameEventMgr.h index 25aa99f858a..192c441f50f 100644 --- a/src/server/game/Events/GameEventMgr.h +++ b/src/server/game/Events/GameEventMgr.h @@ -73,9 +73,9 @@ struct GameEventData struct ModelEquip { uint32 modelid; - uint32 equipment_id; uint32 modelid_prev; - uint32 equipement_id_prev; + uint8 equipment_id; + uint8 equipement_id_prev; }; struct NPCVendorEntry diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 8597b6098a9..85022c9164c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -427,8 +427,8 @@ void ObjectMgr::LoadCreatureTemplates() "spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, " // 71 72 73 74 75 76 77 78 79 80 81 82 "InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, " - // 83 84 85 86 87 88 89 - " questItem6, movementId, RegenHealth, equipment_id, mechanic_immune_mask, flags_extra, ScriptName " + // 83 84 85 86 87 88 + " questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName " "FROM creature_template;"); if (!result) @@ -525,10 +525,9 @@ void ObjectMgr::LoadCreatureTemplates() creatureTemplate.movementId = fields[82].GetUInt32(); creatureTemplate.RegenHealth = fields[83].GetBool(); - creatureTemplate.equipmentId = fields[84].GetUInt32(); - creatureTemplate.MechanicImmuneMask = fields[85].GetUInt32(); - creatureTemplate.flags_extra = fields[86].GetUInt32(); - creatureTemplate.ScriptID = GetScriptId(fields[87].GetCString()); + creatureTemplate.MechanicImmuneMask = fields[84].GetUInt32(); + creatureTemplate.flags_extra = fields[85].GetUInt32(); + creatureTemplate.ScriptID = GetScriptId(fields[86].GetCString()); ++count; } @@ -877,15 +876,6 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) const_cast<CreatureTemplate*>(cInfo)->MovementType = IDLE_MOTION_TYPE; } - if (cInfo->equipmentId > 0) // 0 no equipment - { - if (!GetEquipmentInfo(cInfo->equipmentId)) - { - sLog->outError(LOG_FILTER_SQL, "Table `creature_template` lists creature (Entry: %u) with `equipment_id` %u not found in table `creature_equip_template`, set to no equipment.", cInfo->Entry, cInfo->equipmentId); - const_cast<CreatureTemplate*>(cInfo)->equipmentId = 0; - } - } - /// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc if (cInfo->scale <= 0.0f) { @@ -1011,11 +1001,28 @@ CreatureAddon const* ObjectMgr::GetCreatureTemplateAddon(uint32 entry) return NULL; } -EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry) +EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry, int8& id) { EquipmentInfoContainer::const_iterator itr = _equipmentInfoStore.find(entry); - if (itr != _equipmentInfoStore.end()) - return &(itr->second); + if (itr == _equipmentInfoStore.end()) + return NULL; + + if (itr->second.empty()) + return NULL; + + if (id == -1) // select a random element + { + EquipmentInfoContainerInternal::const_iterator ritr = itr->second.begin(); + std::advance(ritr, urand(0u, itr->second.size())); + id = std::distance(itr->second.begin(), ritr) + 1; + return &ritr->second; + } + else + { + EquipmentInfoContainerInternal::const_iterator itr2 = itr->second.find(id); + if (itr2 != itr->second.end()) + return &itr2->second; + } return NULL; } @@ -1024,7 +1031,8 @@ void ObjectMgr::LoadEquipmentTemplates() { uint32 oldMSTime = getMSTime(); - QueryResult result = WorldDatabase.Query("SELECT entry, itemEntry1, itemEntry2, itemEntry3 FROM creature_equip_template"); + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT entry, id, itemEntry1, itemEntry2, itemEntry3 FROM creature_equip_template"); if (!result) { @@ -1037,13 +1045,21 @@ void ObjectMgr::LoadEquipmentTemplates() { Field* fields = result->Fetch(); - uint16 entry = fields[0].GetUInt16(); + uint32 entry = fields[0].GetUInt32(); + + if (!sObjectMgr->GetCreatureTemplate(entry)) + { + sLog->outError(LOG_FILTER_SQL, "Creature template (Entry: %u) does not exist but has a record in `creature_equip_template`", entry); + continue; + } + + uint8 id = fields[1].GetUInt8(); - EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry]; + EquipmentInfo& equipmentInfo = _equipmentInfoStore[entry][id]; - equipmentInfo.ItemEntry[0] = fields[1].GetUInt32(); - equipmentInfo.ItemEntry[1] = fields[2].GetUInt32(); - equipmentInfo.ItemEntry[2] = fields[3].GetUInt32(); + equipmentInfo.ItemEntry[0] = fields[2].GetUInt32(); + equipmentInfo.ItemEntry[1] = fields[3].GetUInt32(); + equipmentInfo.ItemEntry[2] = fields[4].GetUInt32(); for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i) { @@ -1054,8 +1070,8 @@ void ObjectMgr::LoadEquipmentTemplates() if (!dbcItem) { - sLog->outError(LOG_FILTER_SQL, "Unknown item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u, forced to 0.", - equipmentInfo.ItemEntry[i], i+1, entry); + sLog->outError(LOG_FILTER_SQL, "Unknown item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u and id=%u, forced to 0.", + equipmentInfo.ItemEntry[i], i+1, entry, id); equipmentInfo.ItemEntry[i] = 0; continue; } @@ -1070,8 +1086,8 @@ void ObjectMgr::LoadEquipmentTemplates() dbcItem->InventoryType != INVTYPE_THROWN && dbcItem->InventoryType != INVTYPE_RANGEDRIGHT) { - sLog->outError(LOG_FILTER_SQL, "Item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u is not equipable in a hand, forced to 0.", - equipmentInfo.ItemEntry[i], i+1, entry); + sLog->outError(LOG_FILTER_SQL, "Item (entry=%u) in creature_equip_template.itemEntry%u for entry = %u and id = %u is not equipable in a hand, forced to 0.", + equipmentInfo.ItemEntry[i], i+1, entry, id); equipmentInfo.ItemEntry[i] = 0; } } @@ -1518,13 +1534,13 @@ void ObjectMgr::LoadCreatures() if (!ok) continue; - // -1 no equipment, 0 use default - if (data.equipmentId > 0) + // -1 random, 0 no equipment, 1 is default (may or may not have equipment) + if (data.equipmentId != 0) { - if (!GetEquipmentInfo(data.equipmentId)) + if (!GetEquipmentInfo(data.id, data.equipmentId) && data.equipmentId != 1) { sLog->outError(LOG_FILTER_SQL, "Table `creature` have creature (Entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", data.id, data.equipmentId); - data.equipmentId = -1; + data.equipmentId = 1; } } @@ -1698,7 +1714,7 @@ uint32 ObjectMgr::AddCreData(uint32 entry, uint32 /*team*/, uint32 mapId, float data.id = entry; data.mapid = mapId; data.displayid = 0; - data.equipmentId = cInfo->equipmentId; + data.equipmentId = 1; data.posX = x; data.posY = y; data.posZ = z; diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 8c14f902c1f..d0963a224b7 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -672,7 +672,7 @@ class ObjectMgr CreatureModelInfo const* GetCreatureModelRandomGender(uint32* displayID); static uint32 ChooseDisplayId(uint32 team, const CreatureTemplate* cinfo, const CreatureData* data = NULL); static void ChooseCreatureFlags(const CreatureTemplate* cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, const CreatureData* data = NULL); - EquipmentInfo const* GetEquipmentInfo(uint32 entry); + EquipmentInfo const* GetEquipmentInfo(uint32 entry, int8& id); CreatureAddon const* GetCreatureAddon(uint32 lowguid); CreatureAddon const* GetCreatureTemplateAddon(uint32 entry); ItemTemplate const* GetItemTemplate(uint32 entry); diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 031a8ee96e7..90c97562185 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -665,7 +665,7 @@ void Group::ChangeLeader(uint64 guid) if (!player) return; - sScriptMgr->OnGroupChangeLeader(this, m_leaderGuid, guid); + sScriptMgr->OnGroupChangeLeader(this, guid, m_leaderGuid); if (!isBGGroup() && !isBFGroup()) { diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index e08a7a30699..4d72e79f4df 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -1650,8 +1650,7 @@ void WorldSession::HandleCancelMountAuraOpcode(WorldPacket& /*recvData*/) return; } - _player->Dismount(); - _player->RemoveAurasByType(SPELL_AURA_MOUNTED); + _player->RemoveAurasByType(SPELL_AURA_MOUNTED); // Calls Dismount() } void WorldSession::HandleMoveSetCanFlyAckOpcode(WorldPacket& recvData) diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 03884ae003e..99d36135977 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1041,7 +1041,8 @@ enum TrinityStrings LANG_COMMAND_NO_ACHIEVEMENT_CRITERIA_FOUND = 5033, LANG_COMMAND_NO_OUTDOOR_PVP_FORUND = 5034, LANG_CALL_FOR_HELP = 5035, - // Room for more Trinity strings 5036-9999 + LANG_NPCINFO_EQUIPMENT = 5036, + // Room for more Trinity strings 5037-9999 // Level requirement notifications LANG_SAY_REQ = 6604, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 5f93551a099..0ef69a38ea4 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5320,10 +5320,7 @@ void AuraEffect::HandleAuraSetVehicle(AuraApplication const* aurApp, uint8 mode, target->SendMessageToSet(&data, true); if (apply) - { - data.Initialize(SMSG_ON_CANCEL_EXPECTED_RIDE_VEHICLE_AURA, 0); - target->ToPlayer()->GetSession()->SendPacket(&data); - } + target->ToPlayer()->SendOnCancelExpectedVehicleRideAura(); } void AuraEffect::HandlePreventResurrection(AuraApplication const* aurApp, uint8 mode, bool apply) const diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 4abca023dd3..36e58287599 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -933,6 +933,10 @@ bool Aura::CanBeSaved() const if (HasEffectType(SPELL_AURA_OPEN_STABLE)) return false; + // Can't save vehicle auras, it requires both caster & target to be in world + if (HasEffectType(SPELL_AURA_CONTROL_VEHICLE)) + return false; + // Incanter's Absorbtion - considering the minimal duration and problems with aura stacking // we skip saving this aura // Also for some reason other auras put as MultiSlot crash core on keeping them after restart, @@ -1814,16 +1818,17 @@ void Aura::LoadScripts() bool Aura::CallScriptCheckAreaTargetHandlers(Unit* target) { + bool result = true; for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_AREA_TARGET); std::list<AuraScript::CheckAreaTargetHandler>::iterator hookItrEnd = (*scritr)->DoCheckAreaTarget.end(), hookItr = (*scritr)->DoCheckAreaTarget.begin(); for (; hookItr != hookItrEnd; ++hookItr) - if (!(*hookItr).Call(*scritr, target)) - return false; + result &= hookItr->Call(*scritr, target); + (*scritr)->_FinishScriptCall(); } - return true; + return result; } void Aura::CallScriptDispel(DispelInfo* dispelInfo) @@ -1833,7 +1838,8 @@ void Aura::CallScriptDispel(DispelInfo* dispelInfo) (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_DISPEL); std::list<AuraScript::AuraDispelHandler>::iterator hookItrEnd = (*scritr)->OnDispel.end(), hookItr = (*scritr)->OnDispel.begin(); for (; hookItr != hookItrEnd; ++hookItr) - (*hookItr).Call(*scritr, dispelInfo); + hookItr->Call(*scritr, dispelInfo); + (*scritr)->_FinishScriptCall(); } } @@ -1845,7 +1851,8 @@ void Aura::CallScriptAfterDispel(DispelInfo* dispelInfo) (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_AFTER_DISPEL); std::list<AuraScript::AuraDispelHandler>::iterator hookItrEnd = (*scritr)->AfterDispel.end(), hookItr = (*scritr)->AfterDispel.begin(); for (; hookItr != hookItrEnd; ++hookItr) - (*hookItr).Call(*scritr, dispelInfo); + hookItr->Call(*scritr, dispelInfo); + (*scritr)->_FinishScriptCall(); } } @@ -1858,14 +1865,15 @@ bool Aura::CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplicati (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_APPLY, aurApp); std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->OnEffectApply.end(), effItr = (*scritr)->OnEffectApply.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, mode); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, mode); + if (!preventDefault) preventDefault = (*scritr)->_IsDefaultActionPrevented(); + (*scritr)->_FinishScriptCall(); } + return preventDefault; } @@ -1877,12 +1885,12 @@ bool Aura::CallScriptEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplicat (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_REMOVE, aurApp); std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->OnEffectRemove.end(), effItr = (*scritr)->OnEffectRemove.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, mode); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, mode); + if (!preventDefault) preventDefault = (*scritr)->_IsDefaultActionPrevented(); + (*scritr)->_FinishScriptCall(); } return preventDefault; @@ -1895,10 +1903,9 @@ void Aura::CallScriptAfterEffectApplyHandlers(AuraEffect const* aurEff, AuraAppl (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_APPLY, aurApp); std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->AfterEffectApply.end(), effItr = (*scritr)->AfterEffectApply.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, mode); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, mode); + (*scritr)->_FinishScriptCall(); } } @@ -1910,10 +1917,9 @@ void Aura::CallScriptAfterEffectRemoveHandlers(AuraEffect const* aurEff, AuraApp (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_REMOVE, aurApp); std::list<AuraScript::EffectApplyHandler>::iterator effEndItr = (*scritr)->AfterEffectRemove.end(), effItr = (*scritr)->AfterEffectRemove.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, mode); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, mode); + (*scritr)->_FinishScriptCall(); } } @@ -1926,14 +1932,15 @@ bool Aura::CallScriptEffectPeriodicHandlers(AuraEffect const* aurEff, AuraApplic (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_PERIODIC, aurApp); std::list<AuraScript::EffectPeriodicHandler>::iterator effEndItr = (*scritr)->OnEffectPeriodic.end(), effItr = (*scritr)->OnEffectPeriodic.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff); + if (!preventDefault) preventDefault = (*scritr)->_IsDefaultActionPrevented(); + (*scritr)->_FinishScriptCall(); } + return preventDefault; } @@ -1944,10 +1951,9 @@ void Aura::CallScriptEffectUpdatePeriodicHandlers(AuraEffect* aurEff) (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_UPDATE_PERIODIC); std::list<AuraScript::EffectUpdatePeriodicHandler>::iterator effEndItr = (*scritr)->OnEffectUpdatePeriodic.end(), effItr = (*scritr)->OnEffectUpdatePeriodic.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff); + (*scritr)->_FinishScriptCall(); } } @@ -1959,10 +1965,9 @@ void Aura::CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32 & (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_CALC_AMOUNT); std::list<AuraScript::EffectCalcAmountHandler>::iterator effEndItr = (*scritr)->DoEffectCalcAmount.end(), effItr = (*scritr)->DoEffectCalcAmount.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, amount, canBeRecalculated); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, amount, canBeRecalculated); + (*scritr)->_FinishScriptCall(); } } @@ -1974,10 +1979,9 @@ void Aura::CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool & (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_CALC_PERIODIC); std::list<AuraScript::EffectCalcPeriodicHandler>::iterator effEndItr = (*scritr)->DoEffectCalcPeriodic.end(), effItr = (*scritr)->DoEffectCalcPeriodic.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, isPeriodic, amplitude); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, isPeriodic, amplitude); + (*scritr)->_FinishScriptCall(); } } @@ -1989,10 +1993,9 @@ void Aura::CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellM (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_CALC_SPELLMOD); std::list<AuraScript::EffectCalcSpellModHandler>::iterator effEndItr = (*scritr)->DoEffectCalcSpellMod.end(), effItr = (*scritr)->DoEffectCalcSpellMod.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, spellMod); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, spellMod); + (*scritr)->_FinishScriptCall(); } } @@ -2004,11 +2007,13 @@ void Aura::CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication co (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_ABSORB, aurApp); std::list<AuraScript::EffectAbsorbHandler>::iterator effEndItr = (*scritr)->OnEffectAbsorb.end(), effItr = (*scritr)->OnEffectAbsorb.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, dmgInfo, absorbAmount); - } - defaultPrevented = (*scritr)->_IsDefaultActionPrevented(); + + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, dmgInfo, absorbAmount); + + if (!defaultPrevented) + defaultPrevented = (*scritr)->_IsDefaultActionPrevented(); + (*scritr)->_FinishScriptCall(); } } @@ -2020,10 +2025,9 @@ void Aura::CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplicati (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_ABSORB, aurApp); std::list<AuraScript::EffectAbsorbHandler>::iterator effEndItr = (*scritr)->AfterEffectAbsorb.end(), effItr = (*scritr)->AfterEffectAbsorb.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, dmgInfo, absorbAmount); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, dmgInfo, absorbAmount); + (*scritr)->_FinishScriptCall(); } } @@ -2035,10 +2039,9 @@ void Aura::CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplicatio (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_MANASHIELD, aurApp); std::list<AuraScript::EffectManaShieldHandler>::iterator effEndItr = (*scritr)->OnEffectManaShield.end(), effItr = (*scritr)->OnEffectManaShield.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, dmgInfo, absorbAmount); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, dmgInfo, absorbAmount); + (*scritr)->_FinishScriptCall(); } } @@ -2050,10 +2053,9 @@ void Aura::CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraAppli (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_MANASHIELD, aurApp); std::list<AuraScript::EffectManaShieldHandler>::iterator effEndItr = (*scritr)->AfterEffectManaShield.end(), effItr = (*scritr)->AfterEffectManaShield.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, dmgInfo, absorbAmount); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, dmgInfo, absorbAmount); + (*scritr)->_FinishScriptCall(); } } @@ -2065,26 +2067,27 @@ void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication con (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_SPLIT, aurApp); std::list<AuraScript::EffectSplitHandler>::iterator effEndItr = (*scritr)->OnEffectSplit.end(), effItr = (*scritr)->OnEffectSplit.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, dmgInfo, splitAmount); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, dmgInfo, splitAmount); + (*scritr)->_FinishScriptCall(); } } bool Aura::CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) { + bool result = true; for (std::list<AuraScript*>::iterator scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_CHECK_PROC, aurApp); std::list<AuraScript::CheckProcHandler>::iterator hookItrEnd = (*scritr)->DoCheckProc.end(), hookItr = (*scritr)->DoCheckProc.begin(); for (; hookItr != hookItrEnd; ++hookItr) - if (!(*hookItr).Call(*scritr, eventInfo)) - return false; + result &= hookItr->Call(*scritr, eventInfo); + (*scritr)->_FinishScriptCall(); } - return true; + + return result; } bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo) @@ -2095,12 +2098,14 @@ bool Aura::CallScriptPrepareProcHandlers(AuraApplication const* aurApp, ProcEven (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PREPARE_PROC, aurApp); std::list<AuraScript::AuraProcHandler>::iterator effEndItr = (*scritr)->DoPrepareProc.end(), effItr = (*scritr)->DoPrepareProc.begin(); for (; effItr != effEndItr; ++effItr) - (*effItr).Call(*scritr, eventInfo); + effItr->Call(*scritr, eventInfo); + + if (prepare) + prepare = !(*scritr)->_IsDefaultActionPrevented(); - if (prepare && (*scritr)->_IsDefaultActionPrevented()) - prepare = false; (*scritr)->_FinishScriptCall(); } + return prepare; } @@ -2111,7 +2116,8 @@ void Aura::CallScriptProcHandlers(AuraApplication const* aurApp, ProcEventInfo& (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_PROC, aurApp); std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->OnProc.end(), hookItr = (*scritr)->OnProc.begin(); for (; hookItr != hookItrEnd; ++hookItr) - (*hookItr).Call(*scritr, eventInfo); + hookItr->Call(*scritr, eventInfo); + (*scritr)->_FinishScriptCall(); } } @@ -2123,7 +2129,8 @@ void Aura::CallScriptAfterProcHandlers(AuraApplication const* aurApp, ProcEventI (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_AFTER_PROC, aurApp); std::list<AuraScript::AuraProcHandler>::iterator hookItrEnd = (*scritr)->AfterProc.end(), hookItr = (*scritr)->AfterProc.begin(); for (; hookItr != hookItrEnd; ++hookItr) - (*hookItr).Call(*scritr, eventInfo); + hookItr->Call(*scritr, eventInfo); + (*scritr)->_FinishScriptCall(); } } @@ -2136,12 +2143,12 @@ bool Aura::CallScriptEffectProcHandlers(AuraEffect const* aurEff, AuraApplicatio (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_PROC, aurApp); std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->OnEffectProc.end(), effItr = (*scritr)->OnEffectProc.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, eventInfo); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, eventInfo); + if (!preventDefault) preventDefault = (*scritr)->_IsDefaultActionPrevented(); + (*scritr)->_FinishScriptCall(); } return preventDefault; @@ -2154,10 +2161,9 @@ void Aura::CallScriptAfterEffectProcHandlers(AuraEffect const* aurEff, AuraAppli (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_EFFECT_AFTER_PROC, aurApp); std::list<AuraScript::EffectProcHandler>::iterator effEndItr = (*scritr)->AfterEffectProc.end(), effItr = (*scritr)->AfterEffectProc.begin(); for (; effItr != effEndItr; ++effItr) - { - if ((*effItr).IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) - (*effItr).Call(*scritr, aurEff, eventInfo); - } + if (effItr->IsEffectAffected(m_spellInfo, aurEff->GetEffIndex())) + effItr->Call(*scritr, aurEff, eventInfo); + (*scritr)->_FinishScriptCall(); } } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 61e28a886ab..86d8f55c0ae 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3610,7 +3610,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) unitTarget->CastSpell(unitTarget, iTmpSpellId, true); Creature* npc = unitTarget->ToCreature(); - npc->LoadEquipment(npc->GetEquipmentId()); + npc->LoadEquipment(); return; } // Emblazon Runeblade diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index aadc30e3921..990b35d0862 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1188,6 +1188,12 @@ bool SpellInfo::IsMultiSlotAura() const return IsPassive() || Id == 55849 || Id == 40075 || Id == 44413; // Power Spark, Fel Flak Fire, Incanter's Absorption } +bool SpellInfo::IsStackableOnOneSlotWithDifferentCasters() const +{ + /// TODO: Re-verify meaning of SPELL_ATTR3_STACK_FOR_DIFF_CASTERS and update conditions here + return StackAmount > 1 && !IsChanneled() && !(AttributesEx3 & SPELL_ATTR3_STACK_FOR_DIFF_CASTERS); +} + bool SpellInfo::IsDeathPersistent() const { return AttributesEx3 & SPELL_ATTR3_DEATH_PERSISTENT; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 7852c5c7270..ad344f0463d 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -443,6 +443,7 @@ public: bool IsStackableWithRanks() const; bool IsPassiveStackableWithRanks() const; bool IsMultiSlotAura() const; + bool IsStackableOnOneSlotWithDifferentCasters() const; bool IsDeathPersistent() const; bool IsRequiringDeadTarget() const; bool IsAllowingDeadTarget() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index b648a14fce5..8ec3d7d3731 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3354,6 +3354,17 @@ void SpellMgr::LoadSpellInfoCorrections() case 64596: // Cosmic Smash (Algalon the Observer) spellInfo->RangeEntry = sSpellRangeStore.LookupEntry(6); // 100yd break; + case 64014: // Expedition Base Camp Teleport + case 64024: // Conservatory Teleport + case 64025: // Halls of Invention Teleport + case 64028: // Colossal Forge Teleport + case 64029: // Shattered Walkway Teleport + case 64030: // Antechamber Teleport + case 64031: // Scrapyard Teleport + case 64032: // Formation Grounds Teleport + case 65042: // Prison of Yogg-Saron Teleport + spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_DEST_DB); + break; // ENDOF ULDUAR SPELLS // // TRIAL OF THE CRUSADER SPELLS @@ -3447,7 +3458,7 @@ void SpellMgr::LoadSpellInfoCorrections() case 71518: // Unholy Infusion Quest Credit (Professor Putricide) case 72934: // Blood Infusion Quest Credit (Blood-Queen Lana'thel) case 72289: // Frost Infusion Quest Credit (Sindragosa) - spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_50000_YARDS); // another missing radius + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_200_YARDS); // another missing radius break; case 71708: // Empowered Flare (Blood Prince Council) case 72785: // Empowered Flare (Blood Prince Council) diff --git a/src/server/game/Weather/Weather.cpp b/src/server/game/Weather/Weather.cpp index ef1ee3ff08f..34a6ab35040 100644 --- a/src/server/game/Weather/Weather.cpp +++ b/src/server/game/Weather/Weather.cpp @@ -222,6 +222,9 @@ bool Weather::UpdateWeather() char const* wthstr; switch (state) { + case WEATHER_STATE_FOG: + wthstr = "fog"; + break; case WEATHER_STATE_LIGHT_RAIN: wthstr = "light rain"; break; diff --git a/src/server/game/Weather/Weather.h b/src/server/game/Weather/Weather.h index 3286d550157..bde4280cb25 100644 --- a/src/server/game/Weather/Weather.h +++ b/src/server/game/Weather/Weather.h @@ -46,7 +46,7 @@ struct WeatherData enum WeatherState { WEATHER_STATE_FINE = 0, - WEATHER_STATE_UNK = 1, // Used in some instance encounters. + WEATHER_STATE_FOG = 1, // Used in some instance encounters. WEATHER_STATE_LIGHT_RAIN = 3, WEATHER_STATE_MEDIUM_RAIN = 4, WEATHER_STATE_HEAVY_RAIN = 5, diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 4a1d1566af1..1a7f4b8c2a9 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -979,7 +979,7 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_CHATFLOOD_MESSAGE_DELAY] = ConfigMgr::GetIntDefault("ChatFlood.MessageDelay", 1); m_int_configs[CONFIG_CHATFLOOD_MUTE_TIME] = ConfigMgr::GetIntDefault("ChatFlood.MuteTime", 10); - m_int_configs[CONFIG_EVENT_ANNOUNCE] = ConfigMgr::GetIntDefault("Event.Announce", 0); + m_bool_configs[CONFIG_EVENT_ANNOUNCE] = ConfigMgr::GetIntDefault("Event.Announce", false); m_float_configs[CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS] = ConfigMgr::GetFloatDefault("CreatureFamilyFleeAssistanceRadius", 30.0f); m_float_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS] = ConfigMgr::GetFloatDefault("CreatureFamilyAssistanceRadius", 10.0f); @@ -1445,12 +1445,12 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Creature Model Based Info Data..."); sObjectMgr->LoadCreatureModelInfo(); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Equipment templates..."); - sObjectMgr->LoadEquipmentTemplates(); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Creature templates..."); sObjectMgr->LoadCreatureTemplates(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Equipment templates..."); // must be after LoadCreatureTemplates + sObjectMgr->LoadEquipmentTemplates(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Creature template addons..."); sObjectMgr->LoadCreatureTemplateAddons(); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 872a6c7afb3..e65eaf73983 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -174,6 +174,7 @@ enum WorldBoolConfigs CONFIG_WINTERGRASP_ENABLE, CONFIG_GUILD_LEVELING_ENABLED, CONFIG_UI_QUESTLEVELS_IN_DIALOGS, // Should we add quest levels to the title in the NPC dialogs? + CONFIG_EVENT_ANNOUNCE, BOOL_CONFIG_VALUE_COUNT }; @@ -265,7 +266,6 @@ enum WorldIntConfigs CONFIG_CHATFLOOD_MESSAGE_COUNT, CONFIG_CHATFLOOD_MESSAGE_DELAY, CONFIG_CHATFLOOD_MUTE_TIME, - CONFIG_EVENT_ANNOUNCE, CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY, CONFIG_CREATURE_FAMILY_FLEE_DELAY, CONFIG_WORLD_BOSS_LEVEL_DIFF, diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 6653439b703..6987e958831 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -649,6 +649,7 @@ public: handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetDBTableGUIDLow(), target->GetGUIDLow(), faction, npcflags, Entry, displayid, nativeid); handler->PSendSysMessage(LANG_NPCINFO_LEVEL, target->getLevel()); + handler->PSendSysMessage(LANG_NPCINFO_EQUIPMENT, target->GetCurrentEquipmentId(), target->GetOriginalEquipmentId()); handler->PSendSysMessage(LANG_NPCINFO_HEALTH, target->GetCreateHealth(), target->GetMaxHealth(), target->GetHealth()); handler->PSendSysMessage(LANG_NPCINFO_FLAGS, target->GetUInt32Value(UNIT_FIELD_FLAGS), target->GetUInt32Value(UNIT_DYNAMIC_FLAGS), target->getFaction()); handler->PSendSysMessage(LANG_COMMAND_RAWPAWNTIMES, defRespawnDelayStr.c_str(), curRespawnDelayStr.c_str()); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 52721f9866d..4dcbf204388 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -495,10 +495,9 @@ public: cInfo->questItems[5] = fields[78].GetUInt32(); cInfo->movementId = fields[79].GetUInt32(); cInfo->RegenHealth = fields[80].GetBool(); - cInfo->equipmentId = fields[81].GetUInt32(); - cInfo->MechanicImmuneMask = fields[82].GetUInt32(); - cInfo->flags_extra = fields[83].GetUInt32(); - cInfo->ScriptID = sObjectMgr->GetScriptId(fields[84].GetCString()); + cInfo->MechanicImmuneMask = fields[81].GetUInt32(); + cInfo->flags_extra = fields[82].GetUInt32(); + cInfo->ScriptID = sObjectMgr->GetScriptId(fields[83].GetCString()); sObjectMgr->CheckCreatureTemplate(cInfo); } diff --git a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp index fa1ab5528ca..1303f5c4f58 100644 --- a/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp +++ b/src/server/scripts/EasternKingdoms/BlackwingLair/boss_broodlord_lashlayer.cpp @@ -32,12 +32,20 @@ enum Say SAY_LEASH = 1, }; +enum Events +{ + EVENT_CLEAVE = 1, + EVENT_MORTAL_STRIKE = 2, + EVENT_BLAST_WAVE = 3, + EVENT_KNOCK_BACK = 4, +}; + enum Spells { SPELL_CLEAVE = 26350, - SPELL_BLASTWAVE = 23331, - SPELL_MORTALSTRIKE = 24573, - SPELL_KNOCKBACK = 25778 + SPELL_BLAST_WAVE = 23331, + SPELL_MORTAL_STRIKE = 24573, + SPELL_KNOCK_BACK = 25778 }; class boss_broodlord : public CreatureScript @@ -54,17 +62,13 @@ public: { boss_broodlordAI(Creature* creature) : ScriptedAI(creature) {} - uint32 Cleave_Timer; - uint32 BlastWave_Timer; - uint32 MortalStrike_Timer; - uint32 KnockBack_Timer; - void Reset() { - Cleave_Timer = 8000; // These times are probably wrong - BlastWave_Timer = 12000; - MortalStrike_Timer = 20000; - KnockBack_Timer = 30000; + // These timers are probably wrong + events.ScheduleEvent(EVENT_CLEAVE, 8000); + events.ScheduleEvent(EVENT_BLAST_WAVE, 12000); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, 20000); + events.ScheduleEvent(EVENT_KNOCK_BACK, 30000); } void EnterCombat(Unit* /*who*/) @@ -78,45 +82,51 @@ public: if (!UpdateVictim()) return; - //Cleave_Timer - if (Cleave_Timer <= diff) - { - DoCast(me->getVictim(), SPELL_CLEAVE); - Cleave_Timer = 7000; - } else Cleave_Timer -= diff; - - // BlastWave - if (BlastWave_Timer <= diff) - { - DoCast(me->getVictim(), SPELL_BLASTWAVE); - BlastWave_Timer = urand(8000, 16000); - } else BlastWave_Timer -= diff; + if (EnterEvadeIfOutOfCombatArea(diff)) + Talk(SAY_LEASH); - //MortalStrike_Timer - if (MortalStrike_Timer <= diff) - { - DoCast(me->getVictim(), SPELL_MORTALSTRIKE); - MortalStrike_Timer = urand(25000, 35000); - } else MortalStrike_Timer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (KnockBack_Timer <= diff) + while (uint32 eventId = events.ExecuteEvent()) { - if (Unit* target = me->getVictim()) + switch (eventId) { - DoCast(target, SPELL_KNOCKBACK); - // Drop 50% aggro - if (DoGetThreat(target)) - DoModifyThreatPercent(target, -50); + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, 8000); + break; + case EVENT_MORTAL_STRIKE: + DoCastVictim(SPELL_MORTAL_STRIKE); + events.ScheduleEvent(EVENT_MORTAL_STRIKE, 20000); + break; + case EVENT_BLAST_WAVE: + DoCastVictim(SPELL_BLAST_WAVE); + events.ScheduleEvent(EVENT_BLAST_WAVE, 12000); + break; + case EVENT_KNOCK_BACK: + if (Unit* target = me->getVictim()) + { + DoCast(target, SPELL_BLAST_WAVE); + // Drop 50% of threat + if (DoGetThreat(target)) + DoModifyThreatPercent(target, -50); + } + events.ScheduleEvent(EVENT_KNOCK_BACK, 30000); + break; + default: + break; } - - KnockBack_Timer = urand(15000, 30000); - } else KnockBack_Timer -= diff; + } if (EnterEvadeIfOutOfCombatArea(diff)) Talk(SAY_LEASH); DoMeleeAttackIfReady(); } + + private: + EventMap events; /// @todo: change BWL to instance script and bosses to BossAI }; }; diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp index 96293c635d6..14321873b0d 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp @@ -102,10 +102,8 @@ public: npc_unworthy_initiateAI(Creature* creature) : ScriptedAI(creature) { me->SetReactState(REACT_PASSIVE); - if (!me->GetEquipmentId()) - if (const CreatureTemplate* info = sObjectMgr->GetCreatureTemplate(28406)) - if (info->equipmentId) - const_cast<CreatureTemplate*>(me->GetCreatureTemplate())->equipmentId = info->equipmentId; + if (!me->GetCurrentEquipmentId()) + me->SetCurrentEquipmentId(me->GetOriginalEquipmentId()); } uint64 playerGUID; diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp index f508266434b..7e2515c42a7 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp @@ -237,7 +237,7 @@ public: case 2: me->SetStandState(UNIT_STAND_STATE_STAND); DoCast(me, SPELL_KOLTIRA_TRANSFORM); - me->LoadEquipment(me->GetEquipmentId()); + me->LoadEquipment(); break; case 3: SetEscortPaused(true); diff --git a/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp b/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp index 2169320621b..f27a5612a05 100644 --- a/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp +++ b/src/server/scripts/Kalimdor/HallsOfOrigination/boss_earthrager_ptah.cpp @@ -185,7 +185,7 @@ public: ++_summonDeaths; if (_summonDeaths == 11) // All summons died { - SendWeather(WEATHER_STATE_UNK, 0.0f); + SendWeather(WEATHER_STATE_FOG, 0.0f); me->RemoveAurasDueToSpell(SPELL_PTAH_EXPLOSION); events.SetPhase(PHASE_NORMAL); events.ScheduleEvent(EVENT_RAGING_SMASH, urand(7000, 12000), 0, PHASE_NORMAL); diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp index f4c0eed481e..ab37f94ce40 100644 --- a/src/server/scripts/Kalimdor/zone_desolace.cpp +++ b/src/server/scripts/Kalimdor/zone_desolace.cpp @@ -107,7 +107,7 @@ public: } } }; - + CreatureAI* GetAI(Creature* creature) const { return new npc_aged_dying_ancient_kodoAI(creature); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp index ab0c44aa6d0..4b1ccabe682 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_queen_lana_thel.cpp @@ -45,11 +45,13 @@ enum Spells SPELL_SHROUD_OF_SORROW = 70986, SPELL_FRENZIED_BLOODTHIRST_VISUAL = 71949, SPELL_VAMPIRIC_BITE = 71726, + SPELL_VAMPIRIC_BITE_DUMMY = 71837, SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_PLR = 70879, SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_HEAL = 70872, SPELL_FRENZIED_BLOODTHIRST = 70877, SPELL_UNCONTROLLABLE_FRENZY = 70923, - SPELL_PRESENCE_OF_THE_DARKFALLEN = 71952, + SPELL_PRESENCE_OF_THE_DARKFALLEN = 70994, + SPELL_PRESENCE_OF_THE_DARKFALLEN_2 = 71952, SPELL_BLOOD_MIRROR_DAMAGE = 70821, SPELL_BLOOD_MIRROR_VISUAL = 71510, SPELL_BLOOD_MIRROR_DUMMY = 70838, @@ -88,6 +90,7 @@ uint32 const vampireAuras[3][MAX_DIFFICULTY] = #define ESSENCE_OF_BLOOD_QUEEN_PLR RAID_MODE<uint32>(70879, 71525, 71530, 71531) #define FRENZIED_BLOODTHIRST RAID_MODE<uint32>(70877, 71474, 70877, 71474) #define DELIRIOUS_SLASH RAID_MODE<uint32>(71623, 71624, 71625, 71626) +#define PRESENCE_OF_THE_DARKFALLEN RAID_MODE<uint32>(70994, 71962, 71963, 71964) enum Events { @@ -220,6 +223,7 @@ class boss_blood_queen_lana_thel : public CreatureScript instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_BLOOD_MIRROR_DUMMY); instance->DoRemoveAurasDueToSpellOnPlayers(DELIRIOUS_SLASH); instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PACT_OF_THE_DARKFALLEN); + instance->DoRemoveAurasDueToSpellOnPlayers(PRESENCE_OF_THE_DARKFALLEN); } void DoAction(int32 const action) @@ -357,8 +361,11 @@ class boss_blood_queen_lana_thel : public CreatureScript { Unit* target = targets.front(); DoCast(target, SPELL_VAMPIRIC_BITE); + DoCastAOE(SPELL_VAMPIRIC_BITE_DUMMY, true); Talk(SAY_VAMPIRIC_BITE); _vampires.insert(target->GetGUID()); + target->CastSpell(target, SPELL_PRESENCE_OF_THE_DARKFALLEN, TRIGGERED_FULL_MASK); + target->CastSpell(target, SPELL_PRESENCE_OF_THE_DARKFALLEN_2, TRIGGERED_FULL_MASK); } break; } @@ -377,9 +384,10 @@ class boss_blood_queen_lana_thel : public CreatureScript _offtank->CastSpell(me->getVictim(), SPELL_BLOOD_MIRROR_DAMAGE, true); me->getVictim()->CastSpell(_offtank, SPELL_BLOOD_MIRROR_DUMMY, true); DoCastVictim(SPELL_BLOOD_MIRROR_VISUAL); - if (Item* shadowsEdge = _offtank->GetWeaponForAttack(BASE_ATTACK, true)) - if (!_offtank->HasAura(SPELL_THIRST_QUENCHED) && shadowsEdge->GetEntry() == ITEM_SHADOW_S_EDGE && !_offtank->HasAura(SPELL_GUSHING_WOUND)) - _offtank->CastSpell(_offtank, SPELL_GUSHING_WOUND, true); + if (Is25ManRaid() && _offtank->GetQuestStatus(QUEST_BLOOD_INFUSION) == QUEST_STATUS_INCOMPLETE && + _offtank->HasAura(SPELL_UNSATED_CRAVING) && !_offtank->HasAura(SPELL_THIRST_QUENCHED) && + !_offtank->HasAura(SPELL_GUSHING_WOUND)) + _offtank->CastSpell(_offtank, SPELL_GUSHING_WOUND, TRIGGERED_FULL_MASK); } } @@ -396,13 +404,7 @@ class boss_blood_queen_lana_thel : public CreatureScript { std::list<Player*> targets; SelectRandomTarget(false, &targets); - uint32 targetCount = 2; - // do not combine these checks! we want it incremented TWICE when both conditions are met - if (IsHeroic()) - ++targetCount; - if (Is25ManRaid()) - ++targetCount; - Trinity::Containers::RandomResizeList<Player*>(targets, targetCount); + Trinity::Containers::RandomResizeList(targets, Is25ManRaid() ? 3 : 2); if (targets.size() > 1) { Talk(SAY_PACT_OF_THE_DARKFALLEN); @@ -554,33 +556,36 @@ class spell_blood_queen_vampiric_bite : public SpellScriptLoader uint32 spellId = sSpellMgr->GetSpellIdForDifficulty(SPELL_FRENZIED_BLOODTHIRST, GetCaster()); GetCaster()->RemoveAura(spellId, 0, 0, AURA_REMOVE_BY_ENEMY_SPELL); - GetCaster()->CastSpell(GetCaster(), SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_PLR, true); - // Presence of the Darkfallen buff on Blood-Queen - if (GetCaster()->GetMap()->IsHeroic()) - GetCaster()->CastSpell(GetCaster(), SPELL_PRESENCE_OF_THE_DARKFALLEN, true); + GetCaster()->CastSpell(GetCaster(), SPELL_ESSENCE_OF_THE_BLOOD_QUEEN_PLR, TRIGGERED_FULL_MASK); + // Shadowmourne questline - if (GetCaster()->ToPlayer()->GetQuestStatus(QUEST_BLOOD_INFUSION) == QUEST_STATUS_INCOMPLETE) + if (Aura* aura = GetCaster()->GetAura(SPELL_GUSHING_WOUND)) { - if (Aura* aura = GetCaster()->GetAura(SPELL_GUSHING_WOUND)) + if (aura->GetStackAmount() == 3) { - if (aura->GetStackAmount() == 3) - { - GetCaster()->CastSpell(GetCaster(), SPELL_THIRST_QUENCHED, true); - GetCaster()->RemoveAura(aura); - } - else - GetCaster()->CastSpell(GetCaster(), SPELL_GUSHING_WOUND, true); + GetCaster()->CastSpell(GetCaster(), SPELL_THIRST_QUENCHED, TRIGGERED_FULL_MASK); + GetCaster()->RemoveAura(aura); } + else + GetCaster()->CastSpell(GetCaster(), SPELL_GUSHING_WOUND, TRIGGERED_FULL_MASK); } + if (InstanceScript* instance = GetCaster()->GetInstanceScript()) if (Creature* bloodQueen = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(DATA_BLOOD_QUEEN_LANA_THEL))) bloodQueen->AI()->SetGUID(GetHitUnit()->GetGUID(), GUID_VAMPIRE); } + void HandlePresence(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_PRESENCE_OF_THE_DARKFALLEN, TRIGGERED_FULL_MASK); + GetHitUnit()->CastSpell(GetHitUnit(), SPELL_PRESENCE_OF_THE_DARKFALLEN_2, TRIGGERED_FULL_MASK); + } + void Register() { OnCheckCast += SpellCheckCastFn(spell_blood_queen_vampiric_bite_SpellScript::CheckTarget); BeforeHit += SpellHitFn(spell_blood_queen_vampiric_bite_SpellScript::OnCast); + OnEffectHitTarget += SpellEffectFn(spell_blood_queen_vampiric_bite_SpellScript::HandlePresence, EFFECT_1, SPELL_EFFECT_TRIGGER_SPELL); } }; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index 1c6ed848158..5c7f575413a 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -84,7 +84,9 @@ enum Spells SPELL_CONCUSSIVE_SHOCK = 71337, // Frost Infusion - SPELL_FROST_INFUSION_CREDIT = 72289 + SPELL_FROST_INFUSION_CREDIT = 72289, + SPELL_FROST_IMBUED_BLADE = 72290, + SPELL_FROST_INFUSION = 72292, }; enum Events @@ -147,11 +149,7 @@ enum MovementPoints enum Shadowmourne { - QUEST_FROST_INFUSION = 24757, - ITEM_SHADOW_S_EDGE = 49888, - - SPELL_FROST_INFUSION = 72292, - SPELL_FROST_IMBUED_BLADE = 72290, + QUEST_FROST_INFUSION = 24757 }; Position const RimefangFlyPos = {4413.309f, 2456.421f, 233.3795f, 2.890186f}; @@ -390,47 +388,10 @@ class boss_sindragosa : public CreatureScript void SpellHitTarget(Unit* target, SpellInfo const* spell) { if (uint32 spellId = sSpellMgr->GetSpellIdForDifficulty(70127, me)) - { if (spellId == spell->Id) - { if (Aura const* mysticBuffet = target->GetAura(spell->Id)) _mysticBuffetStack = std::max<uint8>(_mysticBuffetStack, mysticBuffet->GetStackAmount()); - return; - } - } - - // Frost Infusion - if (Player* player = target->ToPlayer()) - { - if (uint32 spellId = sSpellMgr->GetSpellIdForDifficulty(_isThirdPhase ? SPELL_FROST_BREATH_P2 : SPELL_FROST_BREATH_P1, me)) - { - if (spellId == spell->Id) - { - Item* shadowsEdge = player->GetWeaponForAttack(BASE_ATTACK, true); - if (player->GetQuestStatus(QUEST_FROST_INFUSION) == QUEST_STATUS_INCOMPLETE && shadowsEdge) - { - if (!player->HasAura(SPELL_FROST_IMBUED_BLADE) && shadowsEdge->GetEntry() == ITEM_SHADOW_S_EDGE) - { - if (Aura* infusion = player->GetAura(SPELL_FROST_INFUSION)) - { - if (infusion->GetStackAmount() == 3) - { - player->CastSpell(player, SPELL_FROST_IMBUED_BLADE, true); - player->RemoveAura(infusion); - } - else - player->CastSpell(player, SPELL_FROST_INFUSION, true); - } - else - player->CastSpell(player, SPELL_FROST_INFUSION, true); - } - } - - return; - } - } - } } void UpdateAI(uint32 const diff) @@ -1155,6 +1116,50 @@ class spell_sindragosa_unchained_magic : public SpellScriptLoader } }; +class spell_sindragosa_frost_breath : public SpellScriptLoader +{ + public: + spell_sindragosa_frost_breath() : SpellScriptLoader("spell_sindragosa_frost_breath") { } + + class spell_sindragosa_frost_breath_SpellScript : public SpellScript + { + PrepareSpellScript(spell_sindragosa_frost_breath_SpellScript); + + void HandleInfusion() + { + Player* target = GetHitPlayer(); + if (!target) + return; + + if (target->GetQuestStatus(QUEST_FROST_INFUSION) != QUEST_STATUS_INCOMPLETE) + return; + + // Check if player has Shadow's Edge equipped and not ready for infusion + if (!target->HasAura(SPELL_UNSATED_CRAVING) || target->HasAura(SPELL_FROST_IMBUED_BLADE)) + return; + + Aura* infusion = target->GetAura(SPELL_FROST_INFUSION, target->GetGUID()); + if (infusion && infusion->GetStackAmount() >= 3) + { + target->RemoveAura(infusion); + target->CastSpell(target, SPELL_FROST_IMBUED_BLADE, TRIGGERED_FULL_MASK); + } + else + target->CastSpell(target, SPELL_FROST_INFUSION, TRIGGERED_FULL_MASK); + } + + void Register() + { + AfterHit += SpellHitFn(spell_sindragosa_frost_breath_SpellScript::HandleInfusion); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_sindragosa_frost_breath_SpellScript(); + } +}; + class spell_sindragosa_instability : public SpellScriptLoader { public: @@ -1577,6 +1582,7 @@ void AddSC_boss_sindragosa() new npc_sindragosa_trash(); new spell_sindragosa_s_fury(); new spell_sindragosa_unchained_magic(); + new spell_sindragosa_frost_breath(); new spell_sindragosa_instability(); new spell_sindragosa_frost_beacon(); new spell_sindragosa_ice_tomb(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index 1407568686f..887a31baf9a 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -56,6 +56,7 @@ enum SharedSpells SPELL_FROSTMOURNE_TELEPORT_VISUAL = 73078, // Shadowmourne questline + SPELL_UNSATED_CRAVING = 71168, SPELL_SHADOWS_FATE = 71169 }; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index 462708360e3..593d9586156 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -435,7 +435,7 @@ class instance_icecrown_citadel : public InstanceMapScript // these 2 gates are functional only on 25man modes case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_01: case GO_DOODAD_ICECROWN_ROOSTPORTCULLIS_04: - if (instance->GetSpawnMode() & 1) + if (instance->Is25ManRaid()) AddDoor(go, true); break; case GO_LADY_DEATHWHISPER_ELEVATOR: diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index d3c174841ee..125f66497bf 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -565,10 +565,6 @@ class instance_ulduar : public InstanceMapScript } HandleGameObject(KologarnBridgeGUID, false); } - if (state == IN_PROGRESS) - HandleGameObject(KologarnDoorGUID, false); - else - HandleGameObject(KologarnDoorGUID, true); break; case BOSS_HODIR: if (state == DONE) diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp index 2c8d067a865..37bad9c4e4f 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp @@ -316,7 +316,7 @@ public: me->SetDisplayId(MODEL_NIGHTELF); // and reseting equipment - me->LoadEquipment(me->GetEquipmentId()); + me->LoadEquipment(); if (instance && instance->GetData64(DATA_LEOTHERAS_EVENT_STARTER)) { @@ -410,7 +410,7 @@ public: if (me->HasAura(AURA_BANISH)) return; - me->LoadEquipment(me->GetEquipmentId()); + me->LoadEquipment(); } void UpdateAI(const uint32 diff) @@ -558,7 +558,7 @@ public: { //switch to nightelf form me->SetDisplayId(MODEL_NIGHTELF); - me->LoadEquipment(me->GetEquipmentId()); + me->LoadEquipment(); CastConsumingMadness(); DespawnDemon(); @@ -589,7 +589,7 @@ public: Talk(SAY_FINAL_FORM); me->SetDisplayId(MODEL_NIGHTELF); - me->LoadEquipment(me->GetEquipmentId()); + me->LoadEquipment(); } } }; diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index d57ed6e126b..c9c75cdb134 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -813,53 +813,65 @@ class spell_item_scroll_of_recall : public SpellScriptLoader enum ShadowsFate { SPELL_SOUL_FEAST = 71203, - QUEST_A_FEAST_OF_SOULS = 24547 }; -class spell_item_shadows_fate : public SpellScriptLoader +class spell_item_unsated_craving : public SpellScriptLoader { public: - spell_item_shadows_fate() : SpellScriptLoader("spell_item_shadows_fate") { } + spell_item_unsated_craving() : SpellScriptLoader("spell_item_unsated_craving") { } - class spell_item_shadows_fate_AuraScript : public AuraScript + class spell_item_unsated_craving_AuraScript : public AuraScript { - PrepareAuraScript(spell_item_shadows_fate_AuraScript); + PrepareAuraScript(spell_item_unsated_craving_AuraScript); - bool Validate(SpellInfo const* /*spellInfo*/) + bool CheckProc(ProcEventInfo& procInfo) { - if (!sSpellMgr->GetSpellInfo(SPELL_SOUL_FEAST)) + Unit* caster = procInfo.GetActor(); + if (!caster || caster->GetTypeId() != TYPEID_PLAYER) return false; - if (!sObjectMgr->GetQuestTemplate(QUEST_A_FEAST_OF_SOULS)) + + Unit* target = procInfo.GetActionTarget(); + if (!target || target->GetTypeId() != TYPEID_UNIT || target->GetCreatureType() == CREATURE_TYPE_CRITTER || target->isSummon()) return false; - return true; - } - bool Load() - { - _procTarget = NULL; return true; } - bool CheckProc(ProcEventInfo& /*eventInfo*/) + void Register() { - _procTarget = GetCaster(); - return _procTarget && _procTarget->GetTypeId() == TYPEID_PLAYER && _procTarget->ToPlayer()->GetQuestStatus(QUEST_A_FEAST_OF_SOULS) == QUEST_STATUS_INCOMPLETE; + DoCheckProc += AuraCheckProcFn(spell_item_unsated_craving_AuraScript::CheckProc); } + }; - void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) + AuraScript* GetAuraScript() const + { + return new spell_item_unsated_craving_AuraScript(); + } +}; + +class spell_item_shadows_fate : public SpellScriptLoader +{ + public: + spell_item_shadows_fate() : SpellScriptLoader("spell_item_shadows_fate") { } + + class spell_item_shadows_fate_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_shadows_fate_AuraScript); + + void HandleProc(ProcEventInfo& procInfo) { - PreventDefaultAction(); - GetTarget()->CastSpell(_procTarget, SPELL_SOUL_FEAST, true); + Unit* caster = procInfo.GetActor(); + Unit* target = GetCaster(); + if (!caster || !target) + return; + + caster->CastSpell(target, SPELL_SOUL_FEAST, TRIGGERED_FULL_MASK); } void Register() { - DoCheckProc += AuraCheckProcFn(spell_item_shadows_fate_AuraScript::CheckProc); - OnEffectProc += AuraEffectProcFn(spell_item_shadows_fate_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + OnProc += AuraProcFn(spell_item_shadows_fate_AuraScript::HandleProc); } - - private: - Unit* _procTarget; }; AuraScript* GetAuraScript() const @@ -2481,6 +2493,7 @@ void AddSC_item_spell_scripts() new spell_item_piccolo_of_the_flaming_fire(); new spell_item_savory_deviate_delight(); new spell_item_scroll_of_recall(); + new spell_item_unsated_craving(); new spell_item_shadows_fate(); new spell_item_shadowmourne(); new spell_item_shadowmourne_soul_fragment(); diff --git a/src/server/shared/Database/Implementation/WorldDatabase.cpp b/src/server/shared/Database/Implementation/WorldDatabase.cpp index 185ac647e33..8466e75bad2 100644 --- a/src/server/shared/Database/Implementation/WorldDatabase.cpp +++ b/src/server/shared/Database/Implementation/WorldDatabase.cpp @@ -78,7 +78,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_INS_CREATURE_TRANSPORT, "INSERT INTO creature_transport (guid, npc_entry, transport_entry, TransOffsetX, TransOffsetY, TransOffsetZ, TransOffsetO) values (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(WORLD_UPD_CREATURE_TRANSPORT_EMOTE, "UPDATE creature_transport SET emote = ? WHERE transport_entry = ? AND guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction_A, faction_H, npcflag, speed_walk, speed_run, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, equipment_id, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction_A, faction_H, npcflag, speed_walk, speed_run, scale, rank, mindmg, maxdmg, dmgschool, attackpower, dmg_multiplier, baseattacktime, rangeattacktime, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_class, trainer_race, minrangedmg, maxrangedmg, rangedattackpower, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, Health_mod, Mana_mod, Mana_mod_extra, Armor_mod, RacialLeader, questItem1, questItem2, questItem3, questItem4, questItem5, questItem6, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_IP2NATION_COUNTRY, "SELECT c.country FROM ip2nationCountries c, ip2nation i WHERE i.ip < ? AND c.code = i.country ORDER BY i.ip DESC LIMIT 0,1", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 35d37d97f79..96ad754a007 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1028,7 +1028,7 @@ Event.Announce = 0 # # BeepAtStart -# Description: Beep when the world server finished starting (Unix/Linux systems). +# Description: Beep when the world server finished starting. # Default: 1 - (Enabled) # 0 - (Disabled) @@ -1094,6 +1094,14 @@ DBC.EnforceItemAttributes = 1 AccountInstancesPerHour = 5 # +# RBAC.DefaultGroups +# Description: Comma separated list of groups to be added to any account +# Check auth.rbac_groups for correct ids +# Default: "" (No group) + +RBAC.DefaultGroups = "" + +# ################################################################################################### ################################################################################################### diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 671de25dfd3..09f23b844cf 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -12,4 +12,6 @@ add_subdirectory(map_extractor) add_subdirectory(vmap4_assembler) add_subdirectory(vmap4_extractor) add_subdirectory(mmaps_generator) -# add_subdirectory(mesh_extractor) +#if (WITH_MESHEXTRACTOR) +# add_subdirectory(mesh_extractor) +#endif() |