diff options
Diffstat (limited to 'src')
64 files changed, 786 insertions, 744 deletions
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 7c640f9a66d..8032568434f 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -183,7 +183,11 @@ void PetAI::UpdateAI(uint32 diff) } if (spellInfo->HasEffect(SPELL_EFFECT_JUMP_DEST)) + { + if (!spellUsed) + delete spell; continue; // Pets must only jump to target + } // No enemy, check friendly if (!spellUsed) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 97fe3b80f10..23386f82d73 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -488,9 +488,6 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u } case SMART_ACTION_CAST: { - if (!me) - break; - ObjectList* targets = GetTargets(e, unit); if (!targets) break; @@ -502,31 +499,36 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (!(e.action.cast.flags & SMARTCAST_AURA_NOT_PRESENT) || !(*itr)->ToUnit()->HasAura(e.action.cast.spell)) { - if (e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS) - me->InterruptNonMeleeSpells(false); - - if (e.action.cast.flags & SMARTCAST_COMBAT_MOVE) + if (me) { - // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed - // unless target is outside spell range, out of mana, or LOS. + if (e.action.cast.flags & SMARTCAST_INTERRUPT_PREVIOUS) + me->InterruptNonMeleeSpells(false); - bool _allowMove = false; - SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell); - int32 mana = me->GetPower(POWER_MANA); + if (e.action.cast.flags & SMARTCAST_COMBAT_MOVE) + { + // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed + // unless target is outside spell range, out of mana, or LOS. + + bool _allowMove = false; + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(e.action.cast.spell); + int32 mana = me->GetPower(POWER_MANA); - if (me->GetDistance(*itr) > spellInfo->GetMaxRange(true) || - me->GetDistance(*itr) < spellInfo->GetMinRange(true) || - !me->IsWithinLOSInMap(*itr) || - mana < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask())) + if (me->GetDistance(*itr) > spellInfo->GetMaxRange(true) || + me->GetDistance(*itr) < spellInfo->GetMinRange(true) || + !me->IsWithinLOSInMap(*itr) || + mana < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask())) _allowMove = true; - CAST_AI(SmartAI, me->AI())->SetCombatMove(_allowMove); - } + CAST_AI(SmartAI, me->AI())->SetCombatMove(_allowMove); + } - me->CastSpell((*itr)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED)); + me->CastSpell((*itr)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED)); + } + else if (go) + go->CastSpell((*itr)->ToUnit(), e.action.cast.spell, (e.action.cast.flags & SMARTCAST_TRIGGERED)); - TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CAST:: Creature %u casts spell %u on target %u with castflags %u", - me->GetGUIDLow(), e.action.cast.spell, (*itr)->GetGUIDLow(), e.action.cast.flags); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CAST:: %s: %u casts spell %u on target %u with castflags %u", + GetLogNameForGuid(me ? me->GetGUID() : go->GetGUID()), me ? me->GetGUIDLow() : go->GetGUIDLow(), e.action.cast.spell, (*itr)->GetGUIDLow(), e.action.cast.flags); } else TC_LOG_DEBUG("scripts.ai", "Spell %u not cast because it has flag SMARTCAST_AURA_NOT_PRESENT and the target (Guid: " UI64FMTD " Entry: %u Type: %u) already has the aura", e.action.cast.spell, (*itr)->GetGUID(), (*itr)->GetEntry(), uint32((*itr)->GetTypeId())); diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index c3cebcdaeb1..08de27010db 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -550,6 +550,7 @@ enum VehicleSeatFlagsB VEHICLE_SEAT_FLAG_B_EJECTABLE = 0x00000020, // ejectable VEHICLE_SEAT_FLAG_B_USABLE_FORCED_2 = 0x00000040, VEHICLE_SEAT_FLAG_B_USABLE_FORCED_3 = 0x00000100, + VEHICLE_SEAT_FLAG_B_KEEP_PET = 0x00020000, VEHICLE_SEAT_FLAG_B_USABLE_FORCED_4 = 0x02000000, VEHICLE_SEAT_FLAG_B_CAN_SWITCH = 0x04000000, VEHICLE_SEAT_FLAG_B_VEHICLE_PLAYERFRAME_UI = 0x80000000 // Lua_UnitHasVehiclePlayerFrameUI - actually checked for flagsb &~ 0x80000000 diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 517464fef4a..0f86669f14c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -568,13 +568,13 @@ void Creature::Update(uint32 diff) if (!IsInEvadeMode() && (!bInCombat || IsPolymorphed())) // regenerate health if not in combat or if polymorphed RegenerateHealth(); - if (getPowerType() == POWER_ENERGY) + if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER)) { - if (!IsVehicle() || GetVehicleKit()->GetVehicleInfo()->m_powerDisplayId != POWER_PYRITE) + if (getPowerType() == POWER_ENERGY) Regenerate(POWER_ENERGY); + else + RegenerateMana(); } - else - RegenerateMana(); /*if (!bIsPolymorphed) // only increase the timer if not polymorphed m_regenTimer += CREATURE_REGEN_INTERVAL - diff; @@ -1180,14 +1180,22 @@ bool Creature::CreateFromProto(uint32 guidlow, uint32 entry, CreatureData const* SetOriginalEntry(entry); - if (!vehId) - vehId = cinfo->VehicleId; - - Object::_Create(guidlow, entry, vehId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT); + Object::_Create(guidlow, entry, (vehId || cinfo->VehicleId) ? HIGHGUID_VEHICLE : HIGHGUID_UNIT); if (!UpdateEntry(entry, data)) return false; + if (!vehId) + { + if (GetCreatureTemplate()->VehicleId) + { + vehId = GetCreatureTemplate()->VehicleId; + entry = GetCreatureTemplate()->Entry; + } + else + vehId = cinfo->VehicleId; + } + if (vehId) CreateVehicleKit(vehId, entry); diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index 47ec8fceb0f..00d555c5e9c 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -48,18 +48,6 @@ DynamicObject::~DynamicObject() delete _removedAura; } -void DynamicObject::CleanupsBeforeDelete(bool finalCleanup /* = true */) -{ - WorldObject::CleanupsBeforeDelete(finalCleanup); - - if (Transport* transport = GetTransport()) - { - transport->RemovePassenger(this); - SetTransport(NULL); - m_movementInfo.transport.Reset(); - } -} - void DynamicObject::AddToWorld() { ///- Register the dynamicObject for guid lookup and for caster @@ -118,14 +106,11 @@ bool DynamicObject::CreateDynamicObject(uint32 guidlow, Unit* caster, SpellInfo Transport* transport = caster->GetTransport(); if (transport) { - m_movementInfo.transport.guid = GetGUID(); - float x, y, z, o; pos.GetPosition(x, y, z, o); transport->CalculatePassengerOffset(x, y, z, &o); m_movementInfo.transport.pos.Relocate(x, y, z, o); - SetTransport(transport); // This object must be added to transport before adding to map for the client to properly display it transport->AddPassenger(this); } diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.h b/src/server/game/Entities/DynamicObject/DynamicObject.h index 735199db484..ed9b5a3af68 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.h +++ b/src/server/game/Entities/DynamicObject/DynamicObject.h @@ -41,8 +41,6 @@ class DynamicObject : public WorldObject, public GridObject<DynamicObject>, publ void AddToWorld(); void RemoveFromWorld(); - void CleanupsBeforeDelete(bool finalCleanup = true) override; - bool CreateDynamicObject(uint32 guidlow, Unit* caster, SpellInfo const* spell, Position const& pos, float radius, DynamicObjectType type); void Update(uint32 p_time); void Remove(); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index f473428d41b..4e1b8ed29b2 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -98,20 +98,12 @@ std::string GameObject::GetAIName() const return ""; } -void GameObject::CleanupsBeforeDelete(bool /*finalCleanup*/) +void GameObject::CleanupsBeforeDelete(bool finalCleanup) { - if (IsInWorld()) - RemoveFromWorld(); + WorldObject::CleanupsBeforeDelete(finalCleanup); if (m_uint32Values) // field array can be not exist if GameOBject not loaded RemoveFromOwner(); - - if (GetTransport() && !ToTransport()) - { - GetTransport()->RemovePassenger(this); - SetTransport(NULL); - m_movementInfo.transport.Reset(); - } } void GameObject::RemoveFromOwner() @@ -697,12 +689,39 @@ void GameObject::getFishLoot(Loot* fishloot, Player* loot_owner) fishloot->clear(); uint32 zone, subzone; + uint32 defaultzone = 1; GetZoneAndAreaId(zone, subzone); // if subzone loot exist use it - if (!fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner, true, true)) - // else use zone loot (must exist in like case) - fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner, true); + fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner, true, true); + if (fishloot->empty()) //use this becase if zone or subzone has set LOOT_MODE_JUNK_FISH,Even if no normal drop, fishloot->FillLoot return true. it wrong. + { + //subzone no result,use zone loot + fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner, true, true); + //use zone 1 as default, somewhere fishing got nothing,becase subzone and zone not set, like Off the coast of Storm Peaks. + if (fishloot->empty()) + fishloot->FillLoot(defaultzone, LootTemplates_Fishing, loot_owner, true, true); + } +} + +void GameObject::getFishLootJunk(Loot* fishloot, Player* loot_owner) +{ + fishloot->clear(); + + uint32 zone, subzone; + uint32 defaultzone = 1; + GetZoneAndAreaId(zone, subzone); + + // if subzone loot exist use it + fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner, true, true, LOOT_MODE_JUNK_FISH); + if (fishloot->empty()) //use this becase if zone or subzone has normal mask drop, then fishloot->FillLoot return true. + { + //use zone loot + fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner, true, true, LOOT_MODE_JUNK_FISH); + if (fishloot->empty()) + //use zone 1 as default + fishloot->FillLoot(defaultzone, LootTemplates_Fishing, loot_owner, true, true, LOOT_MODE_JUNK_FISH); + } } void GameObject::SaveToDB() @@ -1417,10 +1436,8 @@ void GameObject::Use(Unit* user) else player->SendLoot(GetGUID(), LOOT_FISHING); } - /// @todo else: junk - else - m_respawnTime = time(NULL); - + else // else: junk + player->SendLoot(GetGUID(), LOOT_FISHING_JUNK); break; } case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update @@ -1731,7 +1748,7 @@ void GameObject::Use(Unit* user) CastSpell(user, spellId); } -void GameObject::CastSpell(Unit* target, uint32 spellId) +void GameObject::CastSpell(Unit* target, uint32 spellId, bool triggered /*= true*/) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) @@ -1750,7 +1767,7 @@ void GameObject::CastSpell(Unit* target, uint32 spellId) if (self) { if (target) - target->CastSpell(target, spellInfo, true); + target->CastSpell(target, spellInfo, triggered); return; } @@ -1764,14 +1781,14 @@ void GameObject::CastSpell(Unit* target, uint32 spellId) trigger->setFaction(owner->getFaction()); // needed for GO casts for proper target validation checks trigger->SetOwnerGUID(owner->GetGUID()); - trigger->CastSpell(target ? target : trigger, spellInfo, true, 0, 0, owner->GetGUID()); + trigger->CastSpell(target ? target : trigger, spellInfo, triggered, 0, 0, owner->GetGUID()); } else { trigger->setFaction(14); // Set owner guid for target if no owner available - needed by trigger auras // - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell()) - trigger->CastSpell(target ? target : trigger, spellInfo, true, 0, 0, target ? target->GetGUID() : 0); + trigger->CastSpell(target ? target : trigger, spellInfo, triggered, 0, 0, target ? target->GetGUID() : 0); } } diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 640d5718186..47a92ee4fa5 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -714,6 +714,7 @@ class GameObject : public WorldObject, public GridObject<GameObject>, public Map void Refresh(); void Delete(); void getFishLoot(Loot* loot, Player* loot_owner); + void getFishLootJunk(Loot* loot, Player* loot_owner); GameobjectTypes GetGoType() const { return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1)); } void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); } @@ -791,7 +792,7 @@ class GameObject : public WorldObject, public GridObject<GameObject>, public Map GameObject* LookupFishingHoleAround(float range); - void CastSpell(Unit* target, uint32 spell); + void CastSpell(Unit* target, uint32 spell, bool triggered = true); void SendCustomAnim(uint32 anim); bool IsInRange(float x, float y, float z, float radius) const; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a2fd19c443c..28839775732 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1335,6 +1335,9 @@ void WorldObject::CleanupsBeforeDelete(bool /*finalCleanup*/) { if (IsInWorld()) RemoveFromWorld(); + + if (Transport* transport = GetTransport()) + transport->RemovePassenger(this); } void WorldObject::_Create(uint32 guidlow, HighGuid guidhigh, uint32 phaseMask) @@ -1387,7 +1390,7 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool float sizefactor = GetObjectSize() + obj->GetObjectSize(); float maxdist = dist2compare + sizefactor; - if (m_transport && obj->GetTransport() && obj->GetTransport()->GetGUIDLow() == m_transport->GetGUIDLow()) + if (GetTransport() && obj->GetTransport() && obj->GetTransport()->GetGUIDLow() == GetTransport()->GetGUIDLow()) { float dtx = m_movementInfo.transport.pos.m_positionX - obj->m_movementInfo.transport.pos.m_positionX; float dty = m_movementInfo.transport.pos.m_positionY - obj->m_movementInfo.transport.pos.m_positionY; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 2e2f771daef..0325c288944 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2150,11 +2150,9 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati { TC_LOG_DEBUG("maps", "Player %s using client without required expansion tried teleport to non accessible map %u", GetName().c_str(), mapid); - if (GetTransport()) + if (Transport* transport = GetTransport()) { - m_transport->RemovePassenger(this); - m_transport = NULL; - m_movementInfo.ResetTransport(); + transport->RemovePassenger(this); RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :) } @@ -2173,14 +2171,10 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati m_movementInfo.ResetJump(); DisableSpline(); - if (m_transport) + if (Transport* transport = GetTransport()) { if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT)) - { - m_transport->RemovePassenger(this); - m_transport = NULL; - m_movementInfo.ResetTransport(); - } + transport->RemovePassenger(this); } // The player was ported to another map and loses the duel immediately. @@ -2314,10 +2308,10 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // send transfer packets WorldPacket data(SMSG_TRANSFER_PENDING, 4 + 4 + 4); data.WriteBit(0); // unknown - if (m_transport) + if (Transport* transport = GetTransport()) { data.WriteBit(1); // has transport - data << GetMapId() << m_transport->GetEntry(); + data << GetMapId() << transport->GetEntry(); } else data.WriteBit(0); // has transport @@ -9020,7 +9014,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) // not check distance for GO in case owned GO (fishing bobber case, for example) // And permit out of range GO with no owner in case fishing hole - if (!go || (loot_type != LOOT_FISHINGHOLE && (loot_type != LOOT_FISHING || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this, INTERACTION_DISTANCE)) || (loot_type == LOOT_CORPSE && go->GetRespawnTime() && go->isSpawnedByDefault())) + if (!go || (loot_type != LOOT_FISHINGHOLE && ((loot_type != LOOT_FISHING && loot_type != LOOT_FISHING_JUNK) || go->GetOwnerGUID() != GetGUID()) && !go->IsWithinDistInMap(this, INTERACTION_DISTANCE)) || (loot_type == LOOT_CORPSE && go->GetRespawnTime() && go->isSpawnedByDefault())) { SendLootRelease(guid); return; @@ -9058,6 +9052,8 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (loot_type == LOOT_FISHING) go->getFishLoot(loot, this); + else if (loot_type == LOOT_FISHING_JUNK) + go->getFishLootJunk(loot, this); if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules) { @@ -9300,6 +9296,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type) { case LOOT_INSIGNIA: loot_type = LOOT_SKINNING; break; case LOOT_FISHINGHOLE: loot_type = LOOT_FISHING; break; + case LOOT_FISHING_JUNK: loot_type = LOOT_FISHING; break; default: break; } @@ -17502,15 +17499,15 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) { uint64 transGUID = MAKE_NEW_GUID(transLowGUID, 0, HIGHGUID_MO_TRANSPORT); + Transport* transport = NULL; if (GameObject* go = HashMapHolder<GameObject>::Find(transGUID)) - m_transport = go->ToTransport(); + transport = go->ToTransport(); - if (m_transport) + if (transport) { - m_movementInfo.transport.guid = transGUID; float x = fields[27].GetFloat(), y = fields[28].GetFloat(), z = fields[29].GetFloat(), o = fields[30].GetFloat(); m_movementInfo.transport.pos.Relocate(x, y, z, o); - m_transport->CalculatePassengerPosition(x, y, z, &o); + transport->CalculatePassengerPosition(x, y, z, &o); if (!Trinity::IsValidMapCoord(x, y, z, o) || // transport size limited @@ -17521,7 +17518,6 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) TC_LOG_ERROR("entities.player", "Player (guidlow %d) have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", guid, x, y, z, o); - m_transport = NULL; m_movementInfo.transport.Reset(); RelocateToHomebind(); @@ -17529,9 +17525,9 @@ bool Player::LoadFromDB(uint32 guid, SQLQueryHolder *holder) else { Relocate(x, y, z, o); - mapId = m_transport->GetMapId(); + mapId = transport->GetMapId(); - m_transport->AddPassenger(this); + transport->AddPassenger(this); } } else diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 5f0ae6ecdea..4509d0f4d12 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -35,7 +35,7 @@ Transport::Transport() : GameObject(), _transportInfo(NULL), _isMoving(true), _pendingStop(false), - _triggeredArrivalEvent(false), _triggeredDepartureEvent(false) + _triggeredArrivalEvent(false), _triggeredDepartureEvent(false), _passengerTeleportItr(_passengers.begin()) { m_updateFlag = UPDATEFLAG_TRANSPORT | UPDATEFLAG_LOWGUID | UPDATEFLAG_STATIONARY_POSITION | UPDATEFLAG_ROTATION; } @@ -107,8 +107,6 @@ void Transport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) while (!_passengers.empty()) { WorldObject* obj = *_passengers.begin(); - obj->m_movementInfo.transport.Reset(); - obj->SetTransport(NULL); RemovePassenger(obj); } @@ -230,6 +228,8 @@ void Transport::AddPassenger(WorldObject* passenger) if (_passengers.insert(passenger).second) { + passenger->SetTransport(this); + passenger->m_movementInfo.transport.guid = GetGUID(); TC_LOG_DEBUG("entities.transport", "Object %s boarded transport %s.", passenger->GetName().c_str(), GetName().c_str()); if (Player* plr = passenger->ToPlayer()) @@ -239,8 +239,26 @@ void Transport::AddPassenger(WorldObject* passenger) void Transport::RemovePassenger(WorldObject* passenger) { - if (_passengers.erase(passenger) || _staticPassengers.erase(passenger)) // static passenger can remove itself in case of grid unload + bool erased = false; + if (_passengerTeleportItr != _passengers.end()) { + PassengerSet::iterator itr = _passengers.find(passenger); + if (itr != _passengers.end()) + { + if (itr == _passengerTeleportItr) + ++_passengerTeleportItr; + + _passengers.erase(itr); + erased = true; + } + } + else + erased = _passengers.erase(passenger) > 0; + + if (erased || _staticPassengers.erase(passenger)) // static passenger can remove itself in case of grid unload + { + passenger->SetTransport(NULL); + passenger->m_movementInfo.transport.Reset(); TC_LOG_DEBUG("entities.transport", "Object %s removed from transport %s.", passenger->GetName().c_str(), GetName().c_str()); if (Player* plr = passenger->ToPlayer()) @@ -567,9 +585,9 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl GetMap()->RemoveFromMap<Transport>(this, false); SetMap(newMap); - for (std::set<WorldObject*>::iterator itr = _passengers.begin(); itr != _passengers.end();) + for (_passengerTeleportItr = _passengers.begin(); _passengerTeleportItr != _passengers.end();) { - WorldObject* obj = (*itr++); + WorldObject* obj = (*_passengerTeleportItr++); float destX, destY, destZ, destO; obj->m_movementInfo.transport.pos.GetPosition(destX, destY, destZ, destO); @@ -592,7 +610,7 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl } case TYPEID_PLAYER: if (!obj->ToPlayer()->TeleportTo(newMapid, destX, destY, destZ, destO, TELE_TO_NOT_LEAVE_TRANSPORT)) - _passengers.erase(obj); + RemovePassenger(obj); break; case TYPEID_DYNAMICOBJECT: obj->AddObjectToRemoveList(); @@ -610,7 +628,7 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl else { // Teleport players, they need to know it - for (std::set<WorldObject*>::iterator itr = _passengers.begin(); itr != _passengers.end(); ++itr) + for (PassengerSet::iterator itr = _passengers.begin(); itr != _passengers.end(); ++itr) { if ((*itr)->GetTypeId() == TYPEID_PLAYER) { @@ -627,9 +645,9 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl } } -void Transport::UpdatePassengerPositions(std::set<WorldObject*>& passengers) +void Transport::UpdatePassengerPositions(PassengerSet& passengers) { - for (std::set<WorldObject*>::iterator itr = passengers.begin(); itr != passengers.end(); ++itr) + for (PassengerSet::iterator itr = passengers.begin(); itr != passengers.end(); ++itr) { WorldObject* passenger = *itr; // transport teleported but passenger not yet (can happen for players) diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 293d4334a2e..e644417f1ac 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -31,6 +31,8 @@ class Transport : public GameObject, public TransportBase Transport(); public: + typedef std::set<WorldObject*> PassengerSet; + ~Transport(); bool Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress); @@ -42,7 +44,7 @@ class Transport : public GameObject, public TransportBase void AddPassenger(WorldObject* passenger); void RemovePassenger(WorldObject* passenger); - std::set<WorldObject*> const& GetPassengers() const { return _passengers; } + PassengerSet const& GetPassengers() const { return _passengers; } Creature* CreateNPCPassenger(uint32 guid, CreatureData const* data); GameObject* CreateGOPassenger(uint32 guid, GameObjectData const* data); @@ -99,7 +101,7 @@ class Transport : public GameObject, public TransportBase void MoveToNextWaypoint(); float CalculateSegmentPos(float perc); bool TeleportTransport(uint32 newMapid, float x, float y, float z, float o); - void UpdatePassengerPositions(std::set<WorldObject*>& passengers); + void UpdatePassengerPositions(PassengerSet& passengers); void DoEventIfAny(KeyFrame const& node, bool departure); //! Helpers to know if stop frame was reached @@ -118,8 +120,9 @@ class Transport : public GameObject, public TransportBase bool _triggeredArrivalEvent; bool _triggeredDepartureEvent; - std::set<WorldObject*> _passengers; - std::set<WorldObject*> _staticPassengers; + PassengerSet _passengers; + PassengerSet::iterator _passengerTeleportItr; + PassengerSet _staticPassengers; }; #endif diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 98183d9594a..e7dd71590eb 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -7222,21 +7222,26 @@ void Unit::setPowerType(Powers new_powertype) } } + float powerMultiplier = 1.0f; + if (!IsPet()) + if (Creature* creature = ToCreature()) + powerMultiplier = creature->GetCreatureTemplate()->ModMana; + switch (new_powertype) { default: case POWER_MANA: break; case POWER_RAGE: - SetMaxPower(POWER_RAGE, GetCreatePowers(POWER_RAGE)); + SetMaxPower(POWER_RAGE, uint32(std::ceil(GetCreatePowers(POWER_RAGE) * powerMultiplier))); SetPower(POWER_RAGE, 0); break; case POWER_FOCUS: - SetMaxPower(POWER_FOCUS, GetCreatePowers(POWER_FOCUS)); - SetPower(POWER_FOCUS, GetCreatePowers(POWER_FOCUS)); + SetMaxPower(POWER_FOCUS, uint32(std::ceil(GetCreatePowers(POWER_FOCUS) * powerMultiplier))); + SetPower(POWER_FOCUS, uint32(std::ceil(GetCreatePowers(POWER_FOCUS) * powerMultiplier))); break; case POWER_ENERGY: - SetMaxPower(POWER_ENERGY, GetCreatePowers(POWER_ENERGY)); + SetMaxPower(POWER_ENERGY, uint32(std::ceil(GetCreatePowers(POWER_ENERGY) * powerMultiplier))); break; } } @@ -11214,19 +11219,24 @@ int32 Unit::ModSpellDuration(SpellInfo const* spellProto, Unit const* target, in return std::max(duration, 0); } -void Unit::ModSpellCastTime(SpellInfo const* spellProto, int32 & castTime, Spell* spell) +void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell* spell) { - if (!spellProto || castTime < 0) + if (!spellInfo || castTime < 0) + return; + + if (spellInfo->IsChanneled() && !(spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION)) return; + // called from caster if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CASTING_TIME, castTime, spell); + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell); - if (!(spellProto->Attributes & (SPELL_ATTR0_ABILITY|SPELL_ATTR0_TRADESPELL)) && ((GetTypeId() == TYPEID_PLAYER && spellProto->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) + if (!((spellInfo->Attributes & (SPELL_ATTR0_ABILITY | SPELL_ATTR0_TRADESPELL)) || (spellInfo->AttributesEx3 & SPELL_ATTR3_NO_DONE_BONUS)) && + ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) castTime = int32(float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED)); - else if (spellProto->Attributes & SPELL_ATTR0_REQ_AMMO && !(spellProto->AttributesEx2 & SPELL_ATTR2_AUTOREPEAT_FLAG)) + else if (spellInfo->Attributes & SPELL_ATTR0_REQ_AMMO && !(spellInfo->AttributesEx2 & SPELL_ATTR2_AUTOREPEAT_FLAG)) castTime = int32(float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]); - else if (spellProto->SpellVisual[0] == 3881 && HasAura(67556)) // cooking with Chef Hat. + else if (spellInfo->SpellVisual[0] == 3881 && HasAura(67556)) // cooking with Chef Hat. castTime = 500; } @@ -11928,12 +11938,7 @@ void Unit::CleanupsBeforeDelete(bool finalCleanup) { CleanupBeforeRemoveFromMap(finalCleanup); - if (GetTransport()) - { - GetTransport()->RemovePassenger(this); - SetTransport(NULL); - m_movementInfo.transport.Reset(); - } + WorldObject::CleanupsBeforeDelete(finalCleanup); } void Unit::UpdateCharmAI() @@ -15810,12 +15815,14 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel bool turn = (GetOrientation() != orientation); bool relocated = (teleport || GetPositionX() != x || GetPositionY() != y || GetPositionZ() != z); + // TODO: Check if orientation transport offset changed instead of only global orientation if (turn) RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); if (relocated) { - RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE); + if (!GetVehicle()) + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE); // move and update visible state if need if (GetTypeId() == TYPEID_PLAYER) @@ -16570,3 +16577,27 @@ void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns c data << uint32(itr->second); } } + +int32 Unit::GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool sameMiscValue /*= false*/) const +{ + int32 val = 0; + SpellSpellGroupMapBounds spellGroup = sSpellMgr->GetSpellSpellGroupMapBounds(aurEff->GetSpellInfo()->GetFirstRankSpell()->Id); + for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second ; ++itr) + { + if (sSpellMgr->GetSpellGroupStackRule(itr->second) == SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT) + { + AuraEffectList const& auraEffList = GetAuraEffectsByType(auraType); + for (AuraEffectList::const_iterator auraItr = auraEffList.begin(); auraItr != auraEffList.end(); ++auraItr) + { + if (aurEff != (*auraItr) && (!sameMiscValue || aurEff->GetMiscValue() == (*auraItr)->GetMiscValue()) && + sSpellMgr->IsSpellMemberOfSpellGroup((*auraItr)->GetSpellInfo()->Id, itr->second)) + { + // absolute value only + if (abs(val) < abs((*auraItr)->GetAmount())) + val = (*auraItr)->GetAmount(); + } + } + } + } + return val; +} diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index a12fa54d671..7a51541ddbf 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1306,7 +1306,7 @@ class Unit : public WorldObject void RemoveFromWorld(); void CleanupBeforeRemoveFromMap(bool finalCleanup); - void CleanupsBeforeDelete(bool finalCleanup = true); // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units) + void CleanupsBeforeDelete(bool finalCleanup = true) override; // used in ~Creature/~Player (or before mass creature delete to remove cross-references to already deleted units) DiminishingLevels GetDiminishing(DiminishingGroup group); void IncrDiminishing(DiminishingGroup group); @@ -2164,6 +2164,8 @@ class Unit : public WorldObject time_t GetLastDamagedTime() const { return _lastDamagedTime; } void SetLastDamagedTime(time_t val) { _lastDamagedTime = val; } + int32 GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool sameMiscValue = false) const; + protected: explicit Unit (bool isWorldObject); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 930c4721f62..1e7779065d2 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -77,8 +77,12 @@ Vehicle::~Vehicle() void Vehicle::Install() { if (_me->GetTypeId() == TYPEID_UNIT) + { if (PowerDisplayEntry const* powerDisplay = sPowerDisplayStore.LookupEntry(_vehicleInfo->m_powerDisplayId)) _me->setPowerType(Powers(powerDisplay->PowerType)); + else if (_me->getClass() == CLASS_ROGUE) + _me->setPowerType(POWER_ENERGY); + } _status = STATUS_INSTALLED; if (GetBase()->GetTypeId() == TYPEID_UNIT) @@ -773,6 +777,8 @@ bool VehicleJoinEvent::Execute(uint64, uint32) Passenger->InterruptNonMeleeSpells(false); Passenger->RemoveAurasByType(SPELL_AURA_MOUNTED); + VehicleSeatEntry const* veSeat = Seat->second.SeatInfo; + Player* player = Passenger->ToPlayer(); if (player) { @@ -783,13 +789,13 @@ bool VehicleJoinEvent::Execute(uint64, uint32) player->StopCastingCharm(); player->StopCastingBindSight(); player->SendOnCancelExpectedVehicleRideAura(); - player->UnsummonPetTemporaryIfAny(); + if (!(veSeat->m_flagsB & VEHICLE_SEAT_FLAG_B_KEEP_PET)) + player->UnsummonPetTemporaryIfAny(); } if (Seat->second.SeatInfo->m_flags & VEHICLE_SEAT_FLAG_PASSENGER_NOT_SELECTABLE) Passenger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - VehicleSeatEntry const* veSeat = Seat->second.SeatInfo; Passenger->m_movementInfo.transport.pos.Relocate(veSeat->m_attachmentOffsetX, veSeat->m_attachmentOffsetY, veSeat->m_attachmentOffsetZ); Passenger->m_movementInfo.transport.time = 0; Passenger->m_movementInfo.transport.seat = Seat->first; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 454d79a0ee1..f64f57602c7 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2852,32 +2852,32 @@ void ObjectMgr::LoadVehicleTemplateAccessories() { Field* fields = result->Fetch(); - uint32 uiEntry = fields[0].GetUInt32(); - uint32 uiAccessory = fields[1].GetUInt32(); - int8 uiSeat = int8(fields[2].GetInt8()); - bool bMinion = fields[3].GetBool(); - uint8 uiSummonType = fields[4].GetUInt8(); - uint32 uiSummonTimer= fields[5].GetUInt32(); + uint32 entry = fields[0].GetUInt32(); + uint32 accessory = fields[1].GetUInt32(); + int8 seatId = fields[2].GetInt8(); + bool isMinion = fields[3].GetBool(); + uint8 summonType = fields[4].GetUInt8(); + uint32 summonTimer = fields[5].GetUInt32(); - if (!sObjectMgr->GetCreatureTemplate(uiEntry)) + if (!sObjectMgr->GetCreatureTemplate(entry)) { - TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u does not exist.", uiEntry); + TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u does not exist.", entry); continue; } - if (!sObjectMgr->GetCreatureTemplate(uiAccessory)) + if (!sObjectMgr->GetCreatureTemplate(accessory)) { - TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory %u does not exist.", uiAccessory); + TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: Accessory %u does not exist.", accessory); continue; } - if (_spellClickInfoStore.find(uiEntry) == _spellClickInfoStore.end()) + if (_spellClickInfoStore.find(entry) == _spellClickInfoStore.end()) { - TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u has no data in npc_spellclick_spells", uiEntry); + TC_LOG_ERROR("sql.sql", "Table `vehicle_template_accessory`: creature template entry %u has no data in npc_spellclick_spells", entry); continue; } - _vehicleTemplateAccessoryStore[uiEntry].push_back(VehicleAccessory(uiAccessory, uiSeat, bMinion, uiSummonType, uiSummonTimer)); + _vehicleTemplateAccessoryStore[entry].push_back(VehicleAccessory(accessory, seatId, isMinion, summonType, summonTimer)); ++count; } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 5cf1dc4a8ef..08b681ae53f 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -35,7 +35,8 @@ void VisibleNotifier::SendToSelf() // at this moment i_clientGUIDs have guids that not iterate at grid level checks // but exist one case when this possible and object not out of range: transports if (Transport* transport = i_player.GetTransport()) - for (std::set<WorldObject*>::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end();++itr) + { + for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) { if (vis_guids.find((*itr)->GetGUID()) != vis_guids.end()) { @@ -54,11 +55,15 @@ void VisibleNotifier::SendToSelf() case TYPEID_UNIT: i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow); break; + case TYPEID_DYNAMICOBJECT: + i_player.UpdateVisibilityOf((*itr)->ToDynObject(), i_data, i_visibleNow); + break; default: break; } } } + } for (Player::ClientGUIDs::const_iterator it = vis_guids.begin();it != vis_guids.end(); ++it) { diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index dcaa2727b5e..971daa550c0 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -854,8 +854,6 @@ void WorldSession::HandleGroupAssistantLeaderOpcode(WorldPacket& recvData) recvData >> apply; group->SetGroupMemberFlag(guid, apply, MEMBER_FLAG_ASSISTANT); - - group->SendUpdate(); } void WorldSession::HandlePartyAssignmentOpcode(WorldPacket& recvData) diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp index 5821cba77d6..d40f2102c77 100644 --- a/src/server/game/Handlers/GuildHandler.cpp +++ b/src/server/game/Handlers/GuildHandler.cpp @@ -578,7 +578,7 @@ void WorldSession::HandleGuildBankLogQuery(WorldPacket& recvPacket) uint32 tabId; recvPacket >> tabId; - TC_LOG_DEBUG("guild", "MSG_GUILD_BANK_LOG_QUERY [%s]: TabId: %u", GetPlayerInfo().c_str(), tabId); + TC_LOG_DEBUG("guild", "CMSG_GUILD_BANK_LOG_QUERY [%s]: TabId: %u", GetPlayerInfo().c_str(), tabId); if (Guild* guild = GetPlayer()->GetGuild()) guild->SendBankLog(this, tabId); @@ -589,7 +589,7 @@ void WorldSession::HandleQueryGuildBankTabText(WorldPacket &recvPacket) uint8 tabId; recvPacket >> tabId; - TC_LOG_DEBUG("guild", "MSG_QUERY_GUILD_BANK_TEXT [%s]: TabId: %u", GetPlayerInfo().c_str(), tabId); + TC_LOG_DEBUG("guild", "CMSG_GUILD_BANK_QUERY_TEXT [%s]: TabId: %u", GetPlayerInfo().c_str(), tabId); if (Guild* guild = GetPlayer()->GetGuild()) guild->SendBankTabText(this, tabId); diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index cb6b4096226..dabe40e427e 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -31,6 +31,7 @@ #include "InstanceSaveMgr.h" #include "ObjectMgr.h" #include "MovementStructures.h" +#include "Vehicle.h" #define MOVEMENT_PACKET_TIME_DELAY 0 @@ -320,27 +321,15 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) if (!plrMover->GetTransport()) { if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) - { - plrMover->m_transport = transport; transport->AddPassenger(plrMover); - } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { - bool foundNewTransport = false; - plrMover->m_transport->RemovePassenger(plrMover); + plrMover->GetTransport()->RemovePassenger(plrMover); if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) - { - foundNewTransport = true; - plrMover->m_transport = transport; transport->AddPassenger(plrMover); - } - - if (!foundNewTransport) - { - plrMover->m_transport = NULL; + else movementInfo.ResetTransport(); - } } } @@ -352,11 +341,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave - { plrMover->m_transport->RemovePassenger(plrMover); - plrMover->m_transport = NULL; - movementInfo.ResetTransport(); - } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) @@ -379,12 +364,20 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; - /*----------------------*/ - /* process position-change */ - // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() - if (mover->GetVehicle()) + // Some vehicles allow the passenger to turn by himself + if (Vehicle* vehicle = mover->GetVehicle()) { - mover->SetOrientation(movementInfo.pos.GetOrientation()); + if (VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(mover)) + { + if (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING) + { + if (movementInfo.pos.GetOrientation() != mover->GetOrientation()) + { + mover->SetOrientation(movementInfo.pos.GetOrientation()); + mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING); + } + } + } return; } diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index ed0f3b9717b..9be745e622d 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -86,7 +86,8 @@ enum LootType LOOT_MILLING = 8, LOOT_FISHINGHOLE = 20, // unsupported by client, sending LOOT_FISHING instead - LOOT_INSIGNIA = 21 // unsupported by client, sending LOOT_CORPSE instead + LOOT_INSIGNIA = 21, // unsupported by client, sending LOOT_CORPSE instead + LOOT_FISHING_JUNK = 22 // unsupported by client, sending LOOT_FISHING instead }; // type of Loot Item in Loot View diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index cd91af11a92..0203c7e0821 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2506,15 +2506,9 @@ void Map::SendInitSelf(Player* player) // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map if (Transport* transport = player->GetTransport()) - { - for (std::set<WorldObject*>::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) - { + for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) if (player != (*itr) && player->HaveAtClient(*itr)) - { (*itr)->BuildCreateUpdateBlockForPlayer(&data, player); - } - } - } WorldPacket packet; data.BuildPacket(&packet); diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index b841b1195af..68a0687dc24 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1233,7 +1233,8 @@ enum TrinityStrings LANG_BAN_ACCOUNT_YOUBANNEDMESSAGE_WORLD = 11006, LANG_BAN_ACCOUNT_YOUPERMBANNEDMESSAGE_WORLD = 11007, - LANG_NPCINFO_INHABIT_TYPE = 11008 + LANG_NPCINFO_INHABIT_TYPE = 11008, + LANG_NPCINFO_FLAGS_EXTRA = 11009 // NOT RESERVED IDS 12000-1999999999 // `db_script_string` table index 2000000000-2000009999 (MIN_DB_SCRIPT_STRING_ID-MAX_DB_SCRIPT_STRING_ID) diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index cbcf509747b..94e7d9e5d64 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -41,7 +41,8 @@ enum LootModes LOOT_MODE_HARD_MODE_1 = 0x2, LOOT_MODE_HARD_MODE_2 = 0x4, LOOT_MODE_HARD_MODE_3 = 0x8, - LOOT_MODE_HARD_MODE_4 = 0x10 + LOOT_MODE_HARD_MODE_4 = 0x10, + LOOT_MODE_JUNK_FISH = 0x8000 }; enum Expansions diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 1b8f0ff9f2c..d996a7b633e 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -473,7 +473,6 @@ void AddSC_boss_xt002(); void AddSC_boss_kologarn(); void AddSC_boss_assembly_of_iron(); void AddSC_boss_general_vezax(); -void AddSC_ulduar_teleporter(); void AddSC_boss_mimiron(); void AddSC_boss_hodir(); void AddSC_boss_freya(); @@ -1323,7 +1322,6 @@ void AddNorthrendScripts() AddSC_boss_general_vezax(); AddSC_boss_assembly_of_iron(); AddSC_boss_kologarn(); - AddSC_ulduar_teleporter(); AddSC_boss_mimiron(); AddSC_boss_hodir(); AddSC_boss_freya(); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 28e87de0468..2ece2942a56 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1187,6 +1187,12 @@ void WorldSession::InvalidateRBACData() bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) const { + uint32 maxPacketCounterAllowed = GetMaxPacketCounterAllowed(p.GetOpcode()); + + // Return true if there no limit for the opcode + if (!maxPacketCounterAllowed) + return true; + PacketCounter& packetCounter = _PacketThrottlingMap[p.GetOpcode()]; if (packetCounter.lastReceiveTime != time) { @@ -1194,31 +1200,14 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co packetCounter.amountCounter = 0; } - uint32 maxPacketCounterAllowed = GetMaxPacketCounterAllowed(p.GetOpcode()); - - bool dosTriggered = false; // Check if player is flooding some packets - if (++packetCounter.amountCounter > maxPacketCounterAllowed) - { - dosTriggered = true; - TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, Ping: %u, Character: %s, flooding packet (opc: %s (0x%X), count: %u)", - Session->GetAccountId(), Session->GetRemoteAddress().c_str(), Session->GetLatency(), Session->GetPlayerName().c_str(), - opcodeTable[p.GetOpcode()]->Name, p.GetOpcode(), packetCounter.amountCounter); - } - - // Then check if player is sending packets not allowed - if (!IsOpcodeAllowed(p.GetOpcode())) - { - dosTriggered = true; - // Opcode not allowed, let the punishment begin - TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, sent unacceptable packet (opc: %u, size: %u)", - Session->GetAccountId(), Session->GetRemoteAddress().c_str(), p.GetOpcode(), (uint32)p.size()); - } - - // Return true if everything is fine, otherwise apply the configured policy - if (!dosTriggered) + if (++packetCounter.amountCounter <= maxPacketCounterAllowed) return true; + TC_LOG_WARN("network", "AntiDOS: Account %u, IP: %s, Ping: %u, Character: %s, flooding packet (opc: %s (0x%X), count: %u)", + Session->GetAccountId(), Session->GetRemoteAddress().c_str(), Session->GetLatency(), Session->GetPlayerName().c_str(), + opcodeTable[p.GetOpcode()]->Name, p.GetOpcode(), packetCounter.amountCounter); + switch (_policy) { case POLICY_LOG: @@ -1253,220 +1242,231 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co uint32 maxPacketCounterAllowed; switch (opcode) { - case CMSG_MESSAGECHAT_ADDON_BATTLEGROUND: - case CMSG_MESSAGECHAT_ADDON_GUILD: - case CMSG_MESSAGECHAT_ADDON_OFFICER: - case CMSG_MESSAGECHAT_ADDON_PARTY: - case CMSG_MESSAGECHAT_ADDON_RAID: - case CMSG_MESSAGECHAT_ADDON_WHISPER: - case CMSG_MESSAGECHAT_AFK: - case CMSG_MESSAGECHAT_BATTLEGROUND: - case CMSG_MESSAGECHAT_CHANNEL: - case CMSG_MESSAGECHAT_DND: - case CMSG_MESSAGECHAT_EMOTE: - case CMSG_MESSAGECHAT_GUILD: - case CMSG_MESSAGECHAT_OFFICER: - case CMSG_MESSAGECHAT_PARTY: - case CMSG_MESSAGECHAT_RAID: - case CMSG_MESSAGECHAT_RAID_WARNING: - case CMSG_MESSAGECHAT_SAY: - case CMSG_MESSAGECHAT_WHISPER: - case CMSG_MESSAGECHAT_YELL: + // CPU usage sending 2000 packets/second on a 3.70 GHz 4 cores on Win x64 + // [% CPU mysqld] [%CPU worldserver RelWithDebInfo] + case CMSG_PLAYER_LOGIN: // 0 0.5 + case CMSG_NAME_QUERY: // 0 1 + case CMSG_PET_NAME_QUERY: // 0 1 + case CMSG_NPC_TEXT_QUERY: // 0 1 + case CMSG_ATTACKSTOP: // 0 1 + case CMSG_QUERY_QUESTS_COMPLETED: // 0 1 + case CMSG_QUERY_TIME: // 0 1 + case CMSG_CORPSE_MAP_POSITION_QUERY: // 0 1 + case CMSG_MOVE_TIME_SKIPPED: // 0 1 + case MSG_QUERY_NEXT_MAIL_TIME: // 0 1 + case CMSG_SETSHEATHED: // 0 1 + case MSG_RAID_TARGET_UPDATE: // 0 1 + case CMSG_PLAYER_LOGOUT: // 0 1 + case CMSG_LOGOUT_REQUEST: // 0 1 + case CMSG_PET_RENAME: // 0 1 + case CMSG_QUESTGIVER_REQUEST_REWARD: // 0 1 + case CMSG_COMPLETE_CINEMATIC: // 0 1 + case CMSG_BANKER_ACTIVATE: // 0 1 + case CMSG_BUY_BANK_SLOT: // 0 1 + case CMSG_OPT_OUT_OF_LOOT: // 0 1 + case CMSG_DUEL_ACCEPTED: // 0 1 + case CMSG_DUEL_CANCELLED: // 0 1 + case CMSG_CALENDAR_COMPLAIN: // 0 1 + case CMSG_QUEST_QUERY: // 0 1.5 + case CMSG_GAMEOBJECT_QUERY: // 0 1.5 + case CMSG_CREATURE_QUERY: // 0 1.5 + case CMSG_QUESTGIVER_STATUS_QUERY: // 0 1.5 + case CMSG_GUILD_QUERY: // 0 1.5 + case CMSG_ARENA_TEAM_QUERY: // 0 1.5 + case CMSG_TAXINODE_STATUS_QUERY: // 0 1.5 + case CMSG_TAXIQUERYAVAILABLENODES: // 0 1.5 + case CMSG_QUESTGIVER_QUERY_QUEST: // 0 1.5 + case CMSG_PAGE_TEXT_QUERY: // 0 1.5 + case CMSG_GUILD_BANK_QUERY_TEXT: // 0 1.5 + case MSG_CORPSE_QUERY: // 0 1.5 + case MSG_MOVE_SET_FACING: // 0 1.5 + case CMSG_REQUEST_PARTY_MEMBER_STATS: // 0 1.5 + case CMSG_QUESTGIVER_COMPLETE_QUEST: // 0 1.5 + case CMSG_SET_ACTION_BUTTON: // 0 1.5 + case CMSG_RESET_INSTANCES: // 0 1.5 + case CMSG_HEARTH_AND_RESURRECT: // 0 1.5 + case CMSG_TOGGLE_PVP: // 0 1.5 + case CMSG_PET_ABANDON: // 0 1.5 + case CMSG_ACTIVATETAXIEXPRESS: // 0 1.5 + case CMSG_ACTIVATETAXI: // 0 1.5 + case CMSG_SELF_RES: // 0 1.5 + case CMSG_UNLEARN_SKILL: // 0 1.5 + case CMSG_EQUIPMENT_SET_SAVE: // 0 1.5 + case CMSG_EQUIPMENT_SET_DELETE: // 0 1.5 + case CMSG_DISMISS_CRITTER: // 0 1.5 + case CMSG_REPOP_REQUEST: // 0 1.5 + case CMSG_GROUP_INVITE: // 0 1.5 + case CMSG_GROUP_INVITE_RESPONSE: // 0 1.5 + case CMSG_GROUP_UNINVITE_GUID: // 0 1.5 + case CMSG_GROUP_DISBAND: // 0 1.5 + case CMSG_BATTLEMASTER_JOIN_ARENA: // 0 1.5 + case CMSG_BATTLEFIELD_LEAVE: // 0 1.5 + case CMSG_GUILD_BANK_LOG_QUERY: // 0 2 + case CMSG_LOGOUT_CANCEL: // 0 2 + case CMSG_REALM_SPLIT: // 0 2 + case CMSG_ALTER_APPEARANCE: // 0 2 + case CMSG_QUEST_CONFIRM_ACCEPT: // 0 2 + case CMSG_GUILD_EVENT_LOG_QUERY: // 0 2.5 + case CMSG_READY_FOR_ACCOUNT_DATA_TIMES: // 0 2.5 + case CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY: // 0 2.5 + case CMSG_BEGIN_TRADE: // 0 2.5 + case CMSG_INITIATE_TRADE: // 0 3 + case CMSG_MESSAGECHAT_ADDON_BATTLEGROUND: // 0 3.5 + case CMSG_MESSAGECHAT_ADDON_GUILD: // 0 3.5 + case CMSG_MESSAGECHAT_ADDON_OFFICER: // 0 3.5 + case CMSG_MESSAGECHAT_ADDON_PARTY: // 0 3.5 + case CMSG_MESSAGECHAT_ADDON_RAID: // 0 3.5 + case CMSG_MESSAGECHAT_ADDON_WHISPER: // 0 3.5 + case CMSG_MESSAGECHAT_AFK: // 0 3.5 + case CMSG_MESSAGECHAT_BATTLEGROUND: // 0 3.5 + case CMSG_MESSAGECHAT_CHANNEL: // 0 3.5 + case CMSG_MESSAGECHAT_DND: // 0 3.5 + case CMSG_MESSAGECHAT_EMOTE: // 0 3.5 + case CMSG_MESSAGECHAT_GUILD: // 0 3.5 + case CMSG_MESSAGECHAT_OFFICER: // 0 3.5 + case CMSG_MESSAGECHAT_PARTY: // 0 3.5 + case CMSG_MESSAGECHAT_RAID: // 0 3.5 + case CMSG_MESSAGECHAT_RAID_WARNING: // 0 3.5 + case CMSG_MESSAGECHAT_SAY: // 0 3.5 + case CMSG_MESSAGECHAT_WHISPER: // 0 3.5 + case CMSG_MESSAGECHAT_YELL: // 0 3.5 + case CMSG_INSPECT: // 0 3.5 + case CMSG_AREA_SPIRIT_HEALER_QUERY: // not profiled { - maxPacketCounterAllowed = 500; + // "0" is a magic number meaning there's no limit for the opcode. + // All the opcodes above must cause little CPU usage and no sync/async database queries at all + maxPacketCounterAllowed = 0; break; } - case CMSG_ATTACKSTOP: - case CMSG_GUILD_QUERY: - case CMSG_NAME_QUERY: - case CMSG_PET_NAME_QUERY: - case CMSG_CREATURE_QUERY: - case CMSG_NPC_TEXT_QUERY: - case CMSG_QUESTGIVER_STATUS_QUERY: + case CMSG_QUESTGIVER_ACCEPT_QUEST: // 0 4 + case CMSG_QUESTLOG_REMOVE_QUEST: // 0 4 + case CMSG_QUESTGIVER_CHOOSE_REWARD: // 0 4 + case CMSG_CONTACT_LIST: // 0 5 + case CMSG_LEARN_PREVIEW_TALENTS: // 0 6 + case CMSG_AUTOBANK_ITEM: // 0 6 + case CMSG_AUTOSTORE_BANK_ITEM: // 0 6 + case CMSG_WHO: // 0 7 + case CMSG_PLAYER_VEHICLE_ENTER: // 0 8 + case CMSG_LEARN_PREVIEW_TALENTS_PET: // not profiled + case MSG_MOVE_HEARTBEAT: { - maxPacketCounterAllowed = 5000; + maxPacketCounterAllowed = 200; break; } - case CMSG_ARENA_TEAM_QUERY: - case CMSG_TAXINODE_STATUS_QUERY: - case CMSG_TAXIQUERYAVAILABLENODES: - case CMSG_QUESTGIVER_QUERY_QUEST: - case CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY: - case CMSG_QUERY_QUESTS_COMPLETED: - case CMSG_QUEST_POI_QUERY: - case CMSG_QUERY_TIME: - case CMSG_PAGE_TEXT_QUERY: - case CMSG_PETITION_QUERY: - case CMSG_QUERY_INSPECT_ACHIEVEMENTS: - case CMSG_AREA_SPIRIT_HEALER_QUERY: - case CMSG_CORPSE_MAP_POSITION_QUERY: - case CMSG_MOVE_TIME_SKIPPED: - case CMSG_GUILD_BANK_QUERY_TAB: - case CMSG_GUILD_BANK_LOG_QUERY: - case CMSG_GUILD_EVENT_LOG_QUERY: - case MSG_CORPSE_QUERY: - case MSG_QUERY_NEXT_MAIL_TIME: - case MSG_MOVE_SET_FACING: - case CMSG_INSPECT: + case CMSG_GUILD_SET_NOTE: // 1 2 1 async db query + case CMSG_SET_CONTACT_NOTES: // 1 2.5 1 async db query + case CMSG_CALENDAR_GET_CALENDAR: // 0 1.5 medium upload bandwidth usage + case CMSG_GUILD_BANK_QUERY_TAB: // 0 3.5 medium upload bandwidth usage + case CMSG_QUERY_INSPECT_ACHIEVEMENTS: // 0 13 high upload bandwidth usage { - maxPacketCounterAllowed = 500; + maxPacketCounterAllowed = 50; break; } - case CMSG_REQUEST_PARTY_MEMBER_STATS: - case CMSG_WHO: - case CMSG_SETSHEATHED: - case CMSG_CONTACT_LIST: - case CMSG_GUILD_MOTD: + case CMSG_QUEST_POI_QUERY: // 0 25 very high upload bandwidth usage { - maxPacketCounterAllowed = 50; + maxPacketCounterAllowed = MAX_QUEST_LOG_SIZE; break; } - case CMSG_SPELLCLICK: - case CMSG_GAMEOBJ_USE: - case CMSG_GAMEOBJ_REPORT_USE: - case MSG_RAID_TARGET_UPDATE: - case CMSG_QUESTGIVER_COMPLETE_QUEST: - case CMSG_PLAYER_VEHICLE_ENTER: - case CMSG_PETITION_SIGN: + case CMSG_GM_REPORT_LAG: // 1 3 1 async db query + case CMSG_SPELLCLICK: // not profiled + case CMSG_GAMEOBJ_USE: // not profiled + case CMSG_GAMEOBJ_REPORT_USE: // not profiled { maxPacketCounterAllowed = 20; break; } - case CMSG_PLAYER_LOGOUT: - case CMSG_LOGOUT_REQUEST: - case CMSG_LOGOUT_CANCEL: - case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE: - case CMSG_REQUEST_VEHICLE_PREV_SEAT: - case CMSG_REQUEST_VEHICLE_NEXT_SEAT: - case CMSG_REQUEST_VEHICLE_SWITCH_SEAT: - case CMSG_TOGGLE_PVP: - case CMSG_ADD_FRIEND: - case CMSG_DEL_FRIEND: - case CMSG_SET_CONTACT_NOTES: - case CMSG_RESET_INSTANCES: - case CMSG_HEARTH_AND_RESURRECT: - case CMSG_CHAR_CREATE: - case CMSG_READY_FOR_ACCOUNT_DATA_TIMES: - case CMSG_CHAR_ENUM: - case CMSG_REALM_SPLIT: - case CMSG_CHAR_DELETE: - case CMSG_PLAYER_LOGIN: - case CMSG_PET_ABANDON: - case CMSG_PET_RENAME: - case CMSG_CHAR_RENAME: - case CMSG_CHAR_CUSTOMIZE: - case CMSG_CHAR_RACE_CHANGE: - case CMSG_CHAR_FACTION_CHANGE: - case CMSG_GMTICKET_CREATE: - case CMSG_GMTICKET_UPDATETEXT: - case CMSG_GMTICKET_DELETETICKET: - case CMSG_GMSURVEY_SUBMIT: - case CMSG_GM_REPORT_LAG: - case CMSG_BUG: - case CMSG_GMRESPONSE_RESOLVE: - case CMSG_ACTIVATETAXIEXPRESS: - case CMSG_ACTIVATETAXI: - case CMSG_SELF_RES: - case CMSG_INITIATE_TRADE: - case CMSG_BEGIN_TRADE: - case CMSG_UNLEARN_SKILL: - case CMSG_DISMISS_CONTROLLED_VEHICLE: - case CMSG_REQUEST_VEHICLE_EXIT: - case CMSG_LEARN_PREVIEW_TALENTS: - case CMSG_LEARN_PREVIEW_TALENTS_PET: - case CMSG_EJECT_PASSENGER: - case CMSG_EQUIPMENT_SET_SAVE: - case CMSG_EQUIPMENT_SET_DELETE: - case CMSG_ALTER_APPEARANCE: - case CMSG_QUESTGIVER_ACCEPT_QUEST: - case CMSG_QUESTGIVER_CHOOSE_REWARD: - case CMSG_QUESTGIVER_REQUEST_REWARD: - //case CMSG_QUESTGIVER_CANCEL: - case CMSG_QUESTLOG_REMOVE_QUEST: - case CMSG_QUEST_CONFIRM_ACCEPT: - case CMSG_DISMISS_CRITTER: - case CMSG_REPOP_REQUEST: - case CMSG_PETITION_BUY: - case CMSG_TURN_IN_PETITION: - case CMSG_COMPLETE_CINEMATIC: - case CMSG_ITEM_REFUND: - case CMSG_SOCKET_GEMS: - case CMSG_WRAP_ITEM: - case CMSG_BUY_BANK_SLOT: - case CMSG_GROUP_INVITE_RESPONSE: - //case CMSG_GROUP_UNINVITE: - case CMSG_GROUP_UNINVITE_GUID: - case CMSG_GROUP_SET_LEADER: - case CMSG_GROUP_DISBAND: - case CMSG_GROUP_RAID_CONVERT: - case CMSG_GROUP_CHANGE_SUB_GROUP: - case CMSG_GROUP_ASSISTANT_LEADER: - case CMSG_OPT_OUT_OF_LOOT: - case CMSG_BATTLEMASTER_JOIN_ARENA: - case CMSG_BATTLEFIELD_LEAVE: - case CMSG_REPORT_PVP_AFK: - case CMSG_DUEL_ACCEPTED: - case CMSG_DUEL_CANCELLED: - case CMSG_CALENDAR_GET_CALENDAR: - case CMSG_CALENDAR_ADD_EVENT: - case CMSG_CALENDAR_UPDATE_EVENT: - case CMSG_CALENDAR_REMOVE_EVENT: - case CMSG_CALENDAR_COPY_EVENT: - case CMSG_CALENDAR_EVENT_INVITE: - case CMSG_CALENDAR_EVENT_SIGNUP: - case CMSG_CALENDAR_EVENT_RSVP: - case CMSG_CALENDAR_EVENT_REMOVE_INVITE: - case CMSG_CALENDAR_EVENT_MODERATOR_STATUS: - case CMSG_CALENDAR_COMPLAIN: - case CMSG_ARENA_TEAM_INVITE: - case CMSG_ARENA_TEAM_ACCEPT: - case CMSG_ARENA_TEAM_DECLINE: - case CMSG_ARENA_TEAM_LEAVE: - case CMSG_ARENA_TEAM_DISBAND: - case CMSG_ARENA_TEAM_REMOVE: - case CMSG_ARENA_TEAM_LEADER: - case CMSG_LOOT_METHOD: - case CMSG_GUILD_INVITE: - case CMSG_GUILD_ACCEPT: - case CMSG_GUILD_DECLINE: - case CMSG_GUILD_LEAVE: - case CMSG_GUILD_DISBAND: - case CMSG_GUILD_SET_GUILD_MASTER: - case CMSG_GUILD_QUERY_RANKS: - case CMSG_GUILD_ADD_RANK: - case CMSG_GUILD_DEL_RANK: - case CMSG_GUILD_INFO_TEXT: - case CMSG_GUILD_BANK_DEPOSIT_MONEY: - case CMSG_GUILD_BANK_WITHDRAW_MONEY: - case CMSG_GUILD_BANK_BUY_TAB: - case CMSG_GUILD_BANK_UPDATE_TAB: - case CMSG_SET_GUILD_BANK_TEXT: - case MSG_SAVE_GUILD_EMBLEM: - case MSG_PETITION_RENAME: - case MSG_PETITION_DECLINE: - case MSG_TALENT_WIPE_CONFIRM: - case MSG_SET_DUNGEON_DIFFICULTY: - case MSG_SET_RAID_DIFFICULTY: - case MSG_RANDOM_ROLL: - case MSG_PARTY_ASSIGNMENT: - case MSG_RAID_READY_CHECK: + case CMSG_PETITION_SIGN: // 9 4 2 sync 1 async db queries + case CMSG_TURN_IN_PETITION: // 8 5.5 2 sync db query + case CMSG_GROUP_CHANGE_SUB_GROUP: // 6 5 1 sync 1 async db queries + case CMSG_PETITION_QUERY: // 4 3.5 1 sync db query + case CMSG_CHAR_RACE_CHANGE: // 5 4 1 sync db query + case CMSG_CHAR_CUSTOMIZE: // 5 5 1 sync db query + case CMSG_CHAR_FACTION_CHANGE: // 5 5 1 sync db query + case CMSG_CHAR_DELETE: // 4 4 1 sync db query + case CMSG_DEL_FRIEND: // 7 5 1 async db query + case CMSG_ADD_FRIEND: // 6 4 1 async db query + case CMSG_CHAR_RENAME: // 5 3 1 async db query + case CMSG_GMSURVEY_SUBMIT: // 2 3 1 async db query + case CMSG_BUG: // 1 1 1 async db query + case CMSG_GROUP_SET_LEADER: // 1 2 1 async db query + case CMSG_GROUP_RAID_CONVERT: // 1 5 1 async db query + case CMSG_GROUP_ASSISTANT_LEADER: // 1 2 1 async db query + case CMSG_CALENDAR_ADD_EVENT: // 21 10 2 async db query + case CMSG_PETITION_BUY: // not profiled 1 sync 1 async db queries + case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE: // not profiled + case CMSG_REQUEST_VEHICLE_PREV_SEAT: // not profiled + case CMSG_REQUEST_VEHICLE_NEXT_SEAT: // not profiled + case CMSG_REQUEST_VEHICLE_SWITCH_SEAT: // not profiled + case CMSG_DISMISS_CONTROLLED_VEHICLE: // not profiled + case CMSG_REQUEST_VEHICLE_EXIT: // not profiled + case CMSG_EJECT_PASSENGER: // not profiled + case CMSG_ITEM_REFUND: // not profiled + case CMSG_SOCKET_GEMS: // not profiled + case CMSG_WRAP_ITEM: // not profiled + case CMSG_REPORT_PVP_AFK: // not profiled { - maxPacketCounterAllowed = 3; + maxPacketCounterAllowed = 10; break; } - case CMSG_SET_ACTION_BUTTON: + case CMSG_CHAR_CREATE: // 7 5 3 async db queries + case CMSG_CHAR_ENUM: // 22 3 2 async db queries + case CMSG_GMTICKET_CREATE: // 1 25 1 async db query + case CMSG_GMTICKET_UPDATETEXT: // 0 15 1 async db query + case CMSG_GMTICKET_DELETETICKET: // 1 25 1 async db query + case CMSG_GMRESPONSE_RESOLVE: // 1 25 1 async db query + case CMSG_CALENDAR_UPDATE_EVENT: // not profiled + case CMSG_CALENDAR_REMOVE_EVENT: // not profiled + case CMSG_CALENDAR_COPY_EVENT: // not profiled + case CMSG_CALENDAR_EVENT_INVITE: // not profiled + case CMSG_CALENDAR_EVENT_SIGNUP: // not profiled + case CMSG_CALENDAR_EVENT_RSVP: // not profiled + case CMSG_CALENDAR_EVENT_REMOVE_INVITE: // not profiled + case CMSG_CALENDAR_EVENT_MODERATOR_STATUS: // not profiled + case CMSG_ARENA_TEAM_INVITE: // not profiled + case CMSG_ARENA_TEAM_ACCEPT: // not profiled + case CMSG_ARENA_TEAM_DECLINE: // not profiled + case CMSG_ARENA_TEAM_LEAVE: // not profiled + case CMSG_ARENA_TEAM_DISBAND: // not profiled + case CMSG_ARENA_TEAM_REMOVE: // not profiled + case CMSG_ARENA_TEAM_LEADER: // not profiled + case CMSG_LOOT_METHOD: // not profiled + case CMSG_GUILD_INVITE: // not profiled + case CMSG_GUILD_ACCEPT: // not profiled + case CMSG_GUILD_DECLINE: // not profiled + case CMSG_GUILD_LEAVE: // not profiled + case CMSG_GUILD_DISBAND: // not profiled + case CMSG_GUILD_SET_GUILD_MASTER: // not profiled + case CMSG_GUILD_MOTD: // not profiled + case CMSG_GUILD_SET_RANK_PERMISSIONS: // not profiled + case CMSG_GUILD_ADD_RANK: // not profiled + case CMSG_GUILD_DEL_RANK: // not profiled + case CMSG_GUILD_INFO_TEXT: // not profiled + case CMSG_GUILD_BANK_DEPOSIT_MONEY: // not profiled + case CMSG_GUILD_BANK_WITHDRAW_MONEY: // not profiled + case CMSG_GUILD_BANK_BUY_TAB: // not profiled + case CMSG_GUILD_BANK_UPDATE_TAB: // not profiled + case CMSG_SET_GUILD_BANK_TEXT: // not profiled + case MSG_SAVE_GUILD_EMBLEM: // not profiled + case MSG_PETITION_RENAME: // not profiled + case MSG_PETITION_DECLINE: // not profiled + case MSG_TALENT_WIPE_CONFIRM: // not profiled + case MSG_SET_DUNGEON_DIFFICULTY: // not profiled + case MSG_SET_RAID_DIFFICULTY: // not profiled + case MSG_RANDOM_ROLL: // not profiled + case MSG_PARTY_ASSIGNMENT: // not profiled + case MSG_RAID_READY_CHECK: // not profiled { - maxPacketCounterAllowed = MAX_ACTION_BUTTONS; + maxPacketCounterAllowed = 3; break; } - case CMSG_ITEM_REFUND_INFO: + case CMSG_ITEM_REFUND_INFO: // not profiled { maxPacketCounterAllowed = PLAYER_SLOTS_COUNT; break; diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index f7fe3a967e2..97bc0d7c4c9 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -995,7 +995,6 @@ class WorldSession public: DosProtection(WorldSession* s) : Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { } bool EvaluateOpcode(WorldPacket& p, time_t time) const; - void AllowOpcode(uint16 opcode, bool allow) { _isOpcodeAllowed[opcode] = allow; } protected: enum Policy { @@ -1004,22 +1003,11 @@ class WorldSession POLICY_BAN, }; - bool IsOpcodeAllowed(uint16 opcode) const - { - OpcodeStatusMap::const_iterator itr = _isOpcodeAllowed.find(opcode); - if (itr == _isOpcodeAllowed.end()) - return true; // No presence in the map indicates this is the first time the opcode was sent this session, so allow - - return itr->second; - } - uint32 GetMaxPacketCounterAllowed(uint16 opcode) const; WorldSession* Session; private: - typedef std::unordered_map<uint16, bool> OpcodeStatusMap; - OpcodeStatusMap _isOpcodeAllowed; // could be bool array, but wouldn't be practical for game versions with non-linear opcodes Policy _policy; typedef std::unordered_map<uint16, PacketCounter> PacketThrottlingMap; // mark this member as "mutable" so it can be modified even in const functions diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 7a3be387385..cf0c81cd319 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -434,8 +434,9 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= AuraEffect::AuraEffect(Aura* base, uint8 effIndex, int32 *baseAmount, Unit* caster): m_base(base), m_spellInfo(base->GetSpellInfo()), m_baseAmount(baseAmount ? *baseAmount : m_spellInfo->Effects[effIndex].BasePoints), +m_damage(0), m_critChance(0.0f), m_donePct(1.0f), m_spellmod(NULL), m_periodicTimer(0), m_tickNumber(0), m_effIndex(effIndex), -m_canBeRecalculated(true), m_damage(0), m_critChance(0.0f), m_donePct(1.0f), m_isPeriodic(false) +m_canBeRecalculated(true), m_isPeriodic(false) { CalculatePeriodic(caster, true, false); @@ -3578,11 +3579,23 @@ void AuraEffect::HandleModResistancePercent(AuraApplication const* aurApp, uint8 return; Unit* target = aurApp->GetTarget(); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_RESISTANCE_PCT); + if (abs(spellGroupVal) >= abs(GetAmount())) + return; for (int8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++) { if (GetMiscValue() & int32(1<<i)) { + if (spellGroupVal) + { + target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, (float)spellGroupVal, !apply); + if (target->GetTypeId() == TYPEID_PLAYER || target->IsPet()) + { + target->ApplyResistanceBuffModsPercentMod(SpellSchools(i), true, (float)spellGroupVal, !apply); + target->ApplyResistanceBuffModsPercentMod(SpellSchools(i), false, (float)spellGroupVal, !apply); + } + } target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_PCT, float(GetAmount()), apply); if (target->GetTypeId() == TYPEID_PLAYER || target->ToCreature()->IsPet()) { @@ -3642,19 +3655,29 @@ void AuraEffect::HandleAuraModStat(AuraApplication const* aurApp, uint8 mode, bo if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - Unit* target = aurApp->GetTarget(); - if (GetMiscValue() < -2 || GetMiscValue() > 4) { TC_LOG_ERROR("spells", "WARNING: Spell %u effect %u has an unsupported misc value (%i) for SPELL_AURA_MOD_STAT ", GetId(), GetEffIndex(), GetMiscValue()); return; } + Unit* target = aurApp->GetTarget(); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_STAT, true); + if (abs(spellGroupVal) >= abs(GetAmount())) + return; + for (int32 i = STAT_STRENGTH; i < MAX_STATS; i++) { // -1 or -2 is all stats (misc < -2 checked in function beginning) if (GetMiscValue() < 0 || GetMiscValue() == i) { + if (spellGroupVal) + { + target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(spellGroupVal), !apply); + if (target->GetTypeId() == TYPEID_PLAYER || target->ToCreature()->IsPet()) + target->ApplyStatBuffMod(Stats(i), float(spellGroupVal), !apply); + } + //target->ApplyStatMod(Stats(i), m_amount, apply); target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), TOTAL_VALUE, float(GetAmount()), apply); if (target->GetTypeId() == TYPEID_PLAYER || target->ToCreature()->IsPet()) @@ -4175,7 +4198,17 @@ void AuraEffect::HandleModCombatSpeedPct(AuraApplication const* aurApp, uint8 mo return; Unit* target = aurApp->GetTarget(); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MELEE_SLOW); + if (abs(spellGroupVal) >= abs(GetAmount())) + return; + if (spellGroupVal) + { + target->ApplyCastTimePercentMod(float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(BASE_ATTACK, float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(OFF_ATTACK, float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(RANGED_ATTACK, float(spellGroupVal), !apply); + } target->ApplyCastTimePercentMod(float(m_amount), apply); target->ApplyAttackTimePercentMod(BASE_ATTACK, float(GetAmount()), apply); target->ApplyAttackTimePercentMod(OFF_ATTACK, float(GetAmount()), apply); @@ -4200,7 +4233,15 @@ void AuraEffect::HandleModMeleeSpeedPct(AuraApplication const* aurApp, uint8 mod //! ToDo: Haste auras with the same handler _CAN'T_ stack together Unit* target = aurApp->GetTarget(); + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_MELEE_HASTE); + if (abs(spellGroupVal) >= abs(GetAmount())) + return; + if (spellGroupVal) + { + target->ApplyAttackTimePercentMod(BASE_ATTACK, float(spellGroupVal), !apply); + target->ApplyAttackTimePercentMod(OFF_ATTACK, float(spellGroupVal), !apply); + } target->ApplyAttackTimePercentMod(BASE_ATTACK, float(GetAmount()), apply); target->ApplyAttackTimePercentMod(OFF_ATTACK, float(GetAmount()), apply); } @@ -4408,7 +4449,8 @@ void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 return; Unit* target = aurApp->GetTarget(); - if (!target) + int32 spellGroupVal = target->GetHighestExclusiveSameEffectSpellGroupValue(this, SPELL_AURA_MOD_DAMAGE_PERCENT_DONE); + if (abs(spellGroupVal) >= abs(GetAmount())) return; if (target->GetTypeId() == TYPEID_PLAYER) @@ -4420,12 +4462,23 @@ void AuraEffect::HandleModDamagePercentDone(AuraApplication const* aurApp, uint8 if ((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) && (GetSpellInfo()->EquippedItemClass == -1 || target->GetTypeId() != TYPEID_PLAYER)) { + if (spellGroupVal) + { + target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(spellGroupVal), !apply); + target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(spellGroupVal), !apply); + target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(spellGroupVal), !apply); + } target->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, float(GetAmount()), apply); target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(GetAmount()), apply); target->HandleStatModifier(UNIT_MOD_DAMAGE_RANGED, TOTAL_PCT, float(GetAmount()), apply); - if (target->GetTypeId() == TYPEID_PLAYER) - target->ToPlayer()->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT, float (GetAmount()), apply); + if (Player* player = target->ToPlayer()) + { + if (spellGroupVal) + player->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT, float(spellGroupVal), !apply); + + player->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT, float(GetAmount()), apply); + } } else { diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 93bd4855585..b884dc0a02a 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2598,10 +2598,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA { // Haste modifies duration of channeled spells if (m_spellInfo->IsChanneled()) - { - if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION) - m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this); - } + m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this); else if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION) { int32 origDuration = duration; @@ -3242,9 +3239,9 @@ void Spell::handle_immediate() // Apply duration mod if (Player* modOwner = m_caster->GetSpellModOwner()) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration); + // Apply haste mods - if (m_spellInfo->AttributesEx5 & SPELL_ATTR5_HASTE_AFFECT_DURATION) - m_caster->ModSpellCastTime(m_spellInfo, duration, this); + m_caster->ModSpellCastTime(m_spellInfo, duration, this); m_spellState = SPELL_STATE_CASTING; m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags); @@ -3770,7 +3767,7 @@ void Spell::SendSpellStart() castFlags |= CAST_FLAG_POWER_LEFT_SELF; if (m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNES) - castFlags |= CAST_FLAG_UNKNOWN_19; + castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it WorldPacket data(SMSG_SPELL_START, (8+8+4+4+2)); if (m_CastItem) @@ -3859,21 +3856,22 @@ void Spell::SendSpellGo() if ((m_caster->GetTypeId() == TYPEID_PLAYER) && (m_caster->getClass() == CLASS_DEATH_KNIGHT) && m_spellInfo->RuneCostID - && m_spellInfo->PowerType == POWER_RUNES) + && m_spellInfo->PowerType == POWER_RUNES + && !(_triggeredCastFlags & TRIGGERED_IGNORE_POWER_AND_REAGENT_COST)) { - castFlags |= CAST_FLAG_UNKNOWN_19; // same as in SMSG_SPELL_START + castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it castFlags |= CAST_FLAG_RUNE_LIST; // rune cooldowns list } if (m_spellInfo->HasEffect(SPELL_EFFECT_ACTIVATE_RUNE)) - { castFlags |= CAST_FLAG_RUNE_LIST; // rune cooldowns list - castFlags |= CAST_FLAG_UNKNOWN_19; // same as in SMSG_SPELL_START - } if (m_targets.HasTraj()) castFlags |= CAST_FLAG_ADJUST_MISSILE; + if (!m_spellInfo->StartRecoveryTime) + castFlags |= CAST_FLAG_NO_GCD; + WorldPacket data(SMSG_SPELL_GO, 50); // guess size if (m_CastItem) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 2b026b77160..ae5bba9ed1a 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -57,7 +57,7 @@ enum SpellCastFlags CAST_FLAG_UNKNOWN_16 = 0x00008000, CAST_FLAG_UNKNOWN_17 = 0x00010000, CAST_FLAG_ADJUST_MISSILE = 0x00020000, - CAST_FLAG_UNKNOWN_19 = 0x00040000, + CAST_FLAG_NO_GCD = 0x00040000, // no GCD for spell casts from charm/summon (vehicle spells is an example) CAST_FLAG_VISUAL_CHAIN = 0x00080000, CAST_FLAG_UNKNOWN_21 = 0x00100000, CAST_FLAG_RUNE_LIST = 0x00200000, diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 46f1413c311..be184525599 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3748,51 +3748,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) } break; } - case SPELLFAMILY_DEATHKNIGHT: - { - // Pestilence - if (m_spellInfo->SpellFamilyFlags[1]&0x10000) - { - // Get diseases on target of spell - if (m_targets.GetUnitTarget() && // Glyph of Disease - cast on unit target too to refresh aura - (m_targets.GetUnitTarget() != unitTarget || m_caster->HasAura(63334))) - { - // And spread them on target - // Blood Plague - if (m_targets.GetUnitTarget()->HasAura(55078)) - { - AuraEffect* aurEffOld = m_targets.GetUnitTarget()->GetAura(55078)->GetEffect(0); - float donePct = aurEffOld->GetDonePct(); - float critChance = aurEffOld->GetCritChance(); - - m_caster->CastSpell(unitTarget, 55078, true); - - if (unitTarget->HasAura(55078)) - if (AuraEffect* aurEffNew = unitTarget->GetAura(55078)->GetEffect(0)) - { - aurEffNew->SetCritChance(critChance); // Blood Plague can crit if caster has T9. - aurEffNew->SetDonePct(donePct); - aurEffNew->SetDamage(m_caster->SpellDamageBonusDone(unitTarget, aurEffNew->GetSpellInfo(), std::max(aurEffNew->GetAmount(), 0), DOT) * donePct); - } - } - // Frost Fever - if (m_targets.GetUnitTarget()->HasAura(55095)) - { - float donePct = m_targets.GetUnitTarget()->GetAura(55095)->GetEffect(0)->GetDonePct(); - - m_caster->CastSpell(unitTarget, 55095, true); - - if (unitTarget->HasAura(55095)) - if (AuraEffect* aurEffNew = unitTarget->GetAura(55095)->GetEffect(0)) - { - aurEffNew->SetDonePct(donePct); - aurEffNew->SetDamage(m_caster->SpellDamageBonusDone(unitTarget, aurEffNew->GetSpellInfo(), std::max(aurEffNew->GetAmount(), 0), DOT) * donePct); - } - } - } - } - break; - } } // normal DB scripted effect diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 277d2b701c4..09bb154106b 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -768,6 +768,15 @@ SpellGroupStackRule SpellMgr::CheckSpellGroupStackRules(SpellInfo const* spellIn return rule; } +SpellGroupStackRule SpellMgr::GetSpellGroupStackRule(SpellGroup group) const +{ + SpellGroupStackMap::const_iterator itr = mSpellGroupStack.find(group); + if (itr != mSpellGroupStack.end()) + return itr->second; + + return SPELL_GROUP_STACK_RULE_DEFAULT; +} + SpellProcEventEntry const* SpellMgr::GetSpellProcEvent(uint32 spellId) const { SpellProcEventMap::const_iterator itr = mSpellProcEventMap.find(spellId); diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 29bfd33f704..42259e58360 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -655,6 +655,7 @@ class SpellMgr // Spell Group Stack Rules table bool AddSameEffectStackRuleSpellGroups(SpellInfo const* spellInfo, int32 amount, std::map<SpellGroup, int32>& groups) const; SpellGroupStackRule CheckSpellGroupStackRules(SpellInfo const* spellInfo1, SpellInfo const* spellInfo2) const; + SpellGroupStackRule GetSpellGroupStackRule(SpellGroup groupid) const; // Spell proc event table SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const; diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index fb465aaca16..3b3eb303d58 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -43,6 +43,7 @@ struct EnumName #define CREATE_NAMED_ENUM(VALUE) { VALUE, STRINGIZE(VALUE) } #define NPCFLAG_COUNT 24 +#define FLAGS_EXTRA_COUNT 16 EnumName<NPCFlags, int32> const npcFlagTexts[NPCFLAG_COUNT] = { @@ -145,6 +146,26 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] = CREATE_NAMED_ENUM(UNIT_FLAG_UNK_31) }; +EnumName<CreatureFlagsExtra> const flagsExtra[FLAGS_EXTRA_COUNT] = +{ + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_INSTANCE_BIND), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_CIVILIAN), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_PARRY), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_BLOCK), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_CRUSH), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_XP_AT_KILL), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_TRIGGER), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_TAUNT), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_WORLDEVENT), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_GUARD), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_CRIT), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_SKILLGAIN), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_TAUNT_DIMINISH), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_ALL_DIMINISH), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_DUNGEON_BOSS) +}; + class npc_commandscript : public CommandScript { public: @@ -735,6 +756,10 @@ public: handler->PSendSysMessage(LANG_NPCINFO_ARMOR, target->GetArmor()); handler->PSendSysMessage(LANG_NPCINFO_POSITION, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()); handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName().c_str(), target->GetScriptName().c_str()); + handler->PSendSysMessage(LANG_NPCINFO_FLAGS_EXTRA, cInfo->flags_extra); + for (uint8 i = 0; i < FLAGS_EXTRA_COUNT; ++i) + if (cInfo->flags_extra & flagsExtra[i].Value) + handler->PSendSysMessage("%s (0x%X)", flagsExtra[i].Name, flagsExtra[i].Value); for (uint8 i = 0; i < NPCFLAG_COUNT; i++) if (npcflags & npcFlagTexts[i].Value) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp index 2a473754ce6..f9757997731 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_golemagg.cpp @@ -144,7 +144,7 @@ class npc_core_rager : public CreatureScript if (HealthAbovePct(50) || !instance) return; - if (Creature* pGolemagg = instance->instance->GetCreature(instance->GetData64(BOSS_GOLEMAGG_THE_INCINERATOR))) + if (Creature* pGolemagg = ObjectAccessor::GetCreature(*me, instance->GetData64(BOSS_GOLEMAGG_THE_INCINERATOR))) { if (pGolemagg->IsAlive()) { diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp index 4aa59e72556..bf7b4355ea6 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/instance_ahnkahet.cpp @@ -141,9 +141,9 @@ class instance_ahnkahet : public InstanceMapScript SwitchTrigger = data; break; case DATA_JEDOGA_RESET_INITIANDS: - for (std::set<uint64>::const_iterator itr = InitiandGUIDs.begin(); itr != InitiandGUIDs.end(); ++itr) + for (uint64 guid : InitiandGUIDs) { - if (Creature* creature = instance->GetCreature(*itr)) + if (Creature* creature = instance->GetCreature(guid)) { creature->Respawn(); if (!creature->IsInEvadeMode()) @@ -164,9 +164,9 @@ class instance_ahnkahet : public InstanceMapScript case DATA_SPHERE_2: return SpheresState[type - DATA_SPHERE_1]; case DATA_ALL_INITIAND_DEAD: - for (std::set<uint64>::const_iterator itr = InitiandGUIDs.begin(); itr != InitiandGUIDs.end(); ++itr) + for (uint64 guid : InitiandGUIDs) { - Creature* cr = instance->GetCreature(*itr); + Creature* cr = instance->GetCreature(guid); if (!cr || cr->IsAlive()) return 0; } @@ -214,11 +214,11 @@ class instance_ahnkahet : public InstanceMapScript { std::vector<uint64> vInitiands; vInitiands.clear(); - for (std::set<uint64>::const_iterator itr = InitiandGUIDs.begin(); itr != InitiandGUIDs.end(); ++itr) + for (uint64 guid : InitiandGUIDs) { - Creature* cr = instance->GetCreature(*itr); + Creature* cr = instance->GetCreature(guid); if (cr && cr->IsAlive()) - vInitiands.push_back(*itr); + vInitiands.push_back(guid); } if (vInitiands.empty()) return 0; @@ -245,9 +245,9 @@ class instance_ahnkahet : public InstanceMapScript case DATA_JEDOGA_SHADOWSEEKER: if (state == DONE) { - for (std::set<uint64>::const_iterator itr = InitiandGUIDs.begin(); itr != InitiandGUIDs.end(); ++itr) + for (uint64 guid : InitiandGUIDs) { - if (Creature* cr = instance->GetCreature(*itr)) + if (Creature* cr = instance->GetCreature(guid)) cr->DespawnOrUnsummon(); } } diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt index aff3c0a9528..8401ea4b9a5 100644 --- a/src/server/scripts/Northrend/CMakeLists.txt +++ b/src/server/scripts/Northrend/CMakeLists.txt @@ -20,7 +20,6 @@ set(scripts_STAT_SRCS Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp Northrend/Ulduar/HallsOfLightning/boss_loken.cpp Northrend/Ulduar/Ulduar/boss_general_vezax.cpp - Northrend/Ulduar/Ulduar/ulduar_teleporter.cpp Northrend/Ulduar/Ulduar/boss_thorim.cpp Northrend/Ulduar/Ulduar/boss_ignis.cpp Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp diff --git a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp index 63b1359a406..4e9462a447f 100644 --- a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp +++ b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp @@ -208,7 +208,7 @@ public: { for (uint8 i = 0; i < 4; i++) if (uint64 guid = instance->GetData64(summoners[i].data)) - if (Creature* crystalChannelTarget = instance->instance->GetCreature(guid)) + if (Creature* crystalChannelTarget = ObjectAccessor::GetCreature(*me, guid)) { if (active) crystalChannelTarget->AI()->SetData(summoners[i].spell, summoners[i].timer); @@ -221,7 +221,7 @@ public: { for (uint8 i = 0; i < 4; i++) if (uint64 guid = instance->GetData64(DATA_NOVOS_CRYSTAL_1 + i)) - if (GameObject* crystal = instance->instance->GetGameObject(guid)) + if (GameObject* crystal = ObjectAccessor::GetGameObject(*me, guid)) SetCrystalStatus(crystal, active); } @@ -241,7 +241,7 @@ public: { for (uint8 i = 0; i < 4; i++) if (uint64 guid = instance->GetData64(DATA_NOVOS_CRYSTAL_1 + i)) - if (GameObject* crystal = instance->instance->GetGameObject(guid)) + if (GameObject* crystal = ObjectAccessor::GetGameObject(*me, guid)) if (crystal->GetGoState() == GO_STATE_ACTIVE) { SetCrystalStatus(crystal, false); @@ -258,7 +258,7 @@ public: events.ScheduleEvent(EVENT_SUMMON_MINIONS, 15000); } else if (uint64 guid = instance->GetData64(DATA_NOVOS_SUMMONER_4)) - if (Creature* crystalChannelTarget = instance->instance->GetCreature(guid)) + if (Creature* crystalChannelTarget = ObjectAccessor::GetCreature(*me, guid)) crystalChannelTarget->AI()->SetData(SPELL_SUMMON_CRYSTAL_HANDLER, 15000); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index 16d1531e890..e1658e564ec 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -1227,9 +1227,9 @@ class spell_deathbringer_blood_nova_targeting : public SpellScriptLoader if (targets.empty()) return; - // select one random target, with preference of ranged targets + // select one random target, preferring ranged targets uint32 targetsAtRange = 0; - uint32 const minTargets = uint32(GetCaster()->GetMap()->GetSpawnMode() & 1 ? 10 : 4); + uint32 const minTargets = uint32(GetCaster()->GetMap()->Is25ManRaid() ? 10 : 4); targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster(), false)); // get target count at range @@ -1237,18 +1237,12 @@ class spell_deathbringer_blood_nova_targeting : public SpellScriptLoader if ((*itr)->GetDistance(GetCaster()) < 12.0f) break; - // set the upper cap + // If not enough ranged targets are present just select anyone if (targetsAtRange < minTargets) - targetsAtRange = std::min<uint32>(targets.size() - 1, minTargets); - - if (!targetsAtRange) - { - targets.clear(); - return; - } + targetsAtRange = uint32(targets.size()); std::list<WorldObject*>::const_iterator itr = targets.begin(); - std::advance(itr, urand(0, targetsAtRange)); + std::advance(itr, urand(0, targetsAtRange - 1)); target = *itr; targets.clear(); targets.push_back(target); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp index d3b4a285af6..d4f00414b7d 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp @@ -120,7 +120,6 @@ public: } void MoveInLineOfSight(Unit* who) override - { if (!hasTaunted && me->IsWithinDistInMap(who, 60.0f) && who->GetTypeId() == TYPEID_PLAYER) { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp index 02bafa8d10d..3d42827c0a8 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_faerlina.cpp @@ -84,7 +84,6 @@ class boss_faerlina : public CreatureScript } void MoveInLineOfSight(Unit* who) override - { if (!_introDone && who->GetTypeId() == TYPEID_PLAYER) { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp index 3a0e3ce7c73..381be8d5cd1 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp @@ -70,7 +70,6 @@ public: } void MoveInLineOfSight(Unit* who) override - { if (who->GetEntry() == NPC_ZOMBIE && me->IsWithinDistInMap(who, 7)) { diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index 1331c25de17..e8ed181da5a 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -368,9 +368,9 @@ class instance_naxxramas : public InstanceMapScript if (i == section) continue; - for (std::set<uint64>::const_iterator itr = HeiganEruptionGUID[i].begin(); itr != HeiganEruptionGUID[i].end(); ++itr) + for (uint64 guid : HeiganEruptionGUID[i]) { - if (GameObject* heiganEruption = instance->GetGameObject(*itr)) + if (GameObject* heiganEruption = instance->GetGameObject(guid)) { heiganEruption->SendCustomAnim(heiganEruption->GetGoAnimProgress()); heiganEruption->CastSpell(NULL, SPELL_ERUPTION); diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp index bf84a267a27..2b15ddf32c4 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp @@ -281,8 +281,8 @@ class instance_oculus : public InstanceMapScript void GreaterWhelps() { - for (std::list<uint64>::const_iterator itr = GreaterWhelpList.begin(); itr != GreaterWhelpList.end(); ++itr) - if (Creature* gwhelp = instance->GetCreature(*itr)) + for (uint64 guid : GreaterWhelpList) + if (Creature* gwhelp = instance->GetCreature(guid)) gwhelp->SetPhaseMask(1, true); } diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp index 24d145f097f..13ea815febc 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_bjarngrim.cpp @@ -164,11 +164,9 @@ public: for (uint8 i = 0; i < 2; ++i) { - if (Creature* pStormforgedLieutenant = (ObjectAccessor::GetCreature((*me), m_auiStormforgedLieutenantGUID[i]))) - { + if (Creature* pStormforgedLieutenant = ObjectAccessor::GetCreature(*me, m_auiStormforgedLieutenantGUID[i])) if (!pStormforgedLieutenant->IsAlive()) pStormforgedLieutenant->Respawn(); - } } if (m_uiStance != STANCE_DEFENSIVE) @@ -411,7 +409,7 @@ public: void EnterCombat(Unit* who) override { - if (Creature* pBjarngrim = instance->instance->GetCreature(instance->GetData64(DATA_BJARNGRIM))) + if (Creature* pBjarngrim = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_BJARNGRIM))) { if (pBjarngrim->IsAlive() && !pBjarngrim->GetVictim()) pBjarngrim->AI()->AttackStart(who); @@ -434,7 +432,7 @@ public: if (m_uiRenewSteel_Timer <= uiDiff) { - if (Creature* pBjarngrim = instance->instance->GetCreature(instance->GetData64(DATA_BJARNGRIM))) + if (Creature* pBjarngrim = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_BJARNGRIM))) { if (pBjarngrim->IsAlive()) DoCast(pBjarngrim, SPELL_RENEW_STEEL_N); diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp index aac315cda0d..83082b18d73 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp @@ -165,9 +165,9 @@ public: Position pos = me->GetPosition(); - for (std::list<uint64>::const_iterator itr = lSparkList.begin(); itr != lSparkList.end(); ++itr) + for (uint64 guid : lSparkList) { - if (Creature* pSpark = ObjectAccessor::GetCreature(*me, *itr)) + if (Creature* pSpark = ObjectAccessor::GetCreature(*me, guid)) { if (pSpark->IsAlive()) { diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp index d0b8f75e711..b424ce01b06 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp @@ -162,13 +162,11 @@ public: if (m_lGolemGUIDList.empty()) return; - for (std::list<uint64>::const_iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr) + for (uint64 guid : m_lGolemGUIDList) { - if (Creature* temp = ObjectAccessor::GetCreature(*me, *itr)) - { + if (Creature* temp = ObjectAccessor::GetCreature(*me, guid)) if (temp->IsAlive()) temp->DespawnOrUnsummon(); - } } m_lGolemGUIDList.clear(); @@ -179,9 +177,9 @@ public: if (m_lGolemGUIDList.empty()) return; - for (std::list<uint64>::const_iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr) + for (uint64 guid : m_lGolemGUIDList) { - if (Creature* temp = ObjectAccessor::GetCreature(*me, *itr)) + if (Creature* temp = ObjectAccessor::GetCreature(*me, guid)) { // Only shatter brittle golems if (temp->IsAlive() && temp->GetEntry() == NPC_BRITTLE_GOLEM) diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp index eec08c3c429..796299cc952 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfStone/boss_maiden_of_grief.cpp @@ -37,7 +37,7 @@ enum Spells enum Events { - EVENT_PARTING_SORROW = 1, + EVENT_PARTING_SORROW = 1, EVENT_STORM_OF_GRIEF, EVENT_SHOCK_OF_SORROW, EVENT_PILLAR_OF_WOE diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp index d728bc2a898..6437a76ee95 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/instance_ulduar.cpp @@ -18,9 +18,10 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "InstanceScript.h" -#include "ulduar.h" #include "Player.h" #include "WorldPacket.h" +#include "SpellScript.h" +#include "ulduar.h" static DoorData const doorData[] = { @@ -1094,7 +1095,43 @@ class instance_ulduar : public InstanceMapScript } }; +class spell_ulduar_teleporter : public SpellScriptLoader +{ + public: + spell_ulduar_teleporter() : SpellScriptLoader("spell_ulduar_teleporter") { } + + class spell_ulduar_teleporter_SpellScript : public SpellScript + { + PrepareSpellScript(spell_ulduar_teleporter_SpellScript); + + SpellCastResult CheckRequirement() + { + if (GetExplTargetUnit()->GetTypeId() != TYPEID_PLAYER) + return SPELL_FAILED_DONT_REPORT; + + if (GetExplTargetUnit()->IsInCombat()) + { + Spell::SendCastResult(GetExplTargetUnit()->ToPlayer(), GetSpellInfo(), 0, SPELL_FAILED_AFFECTING_COMBAT); + return SPELL_FAILED_AFFECTING_COMBAT; + } + + return SPELL_CAST_OK; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_ulduar_teleporter_SpellScript::CheckRequirement); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_ulduar_teleporter_SpellScript(); + } +}; + void AddSC_instance_ulduar() { new instance_ulduar(); + new spell_ulduar_teleporter(); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar_teleporter.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar_teleporter.cpp deleted file mode 100644 index 9fc0e4056fa..00000000000 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/ulduar_teleporter.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "ScriptMgr.h" -#include "ScriptedGossip.h" -#include "InstanceScript.h" -#include "Player.h" -#include "ulduar.h" - -/* -The teleporter appears to be active and stable. - -- Expedition Base Camp -- Formation Grounds -- Colossal Forge -- Scrapyard -- Antechamber of Ulduar -- Shattered Walkway -- Conservatory of Life -*/ - -enum UlduarTeleporter -{ - BASE_CAMP = 200, - GROUNDS = 201, - FORGE = 202, - SCRAPYARD = 203, - ANTECHAMBER = 204, - WALKWAY = 205, - CONSERVATORY = 206, -}; - -class ulduar_teleporter : public GameObjectScript -{ - public: - ulduar_teleporter() : GameObjectScript("ulduar_teleporter") { } - - bool OnGossipSelect(Player* player, GameObject* /*gameObject*/, uint32 sender, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (sender != GOSSIP_SENDER_MAIN) - return false; - if (!player->getAttackers().empty()) - return false; - - switch (action) - { - case BASE_CAMP: - player->TeleportTo(603, -706.122f, -92.6024f, 429.876f, 0.0f); - player->CLOSE_GOSSIP_MENU(); - break; - case GROUNDS: - player->TeleportTo(603, 131.248f, -35.3802f, 409.804f, 0.0f); - player->CLOSE_GOSSIP_MENU(); - break; - case FORGE: - player->TeleportTo(603, 553.233f, -12.3247f, 409.679f, 0.0f); - player->CLOSE_GOSSIP_MENU(); - break; - case SCRAPYARD: - player->TeleportTo(603, 926.292f, -11.4635f, 418.595f, 0.0f); - player->CLOSE_GOSSIP_MENU(); - break; - case ANTECHAMBER: - player->TeleportTo(603, 1498.09f, -24.246f, 420.967f, 0.0f); - player->CLOSE_GOSSIP_MENU(); - break; - case WALKWAY: - player->TeleportTo(603, 1859.45f, -24.1f, 448.9f, 0.0f); - player->CLOSE_GOSSIP_MENU(); - break; - case CONSERVATORY: - player->TeleportTo(603, 2086.27f, -24.3134f, 421.239f, 0.0f); - player->CLOSE_GOSSIP_MENU(); - break; - } - - return true; - } - - bool OnGossipHello(Player* player, GameObject* gameObject) override - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Expedition Base Camp", GOSSIP_SENDER_MAIN, BASE_CAMP); - if (InstanceScript* instance = gameObject->GetInstanceScript()) - { - if (instance->GetData(DATA_COLOSSUS) == 2) //count of 2 collossus death - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Formation Grounds", GOSSIP_SENDER_MAIN, GROUNDS); - if (instance->GetBossState(BOSS_LEVIATHAN) == DONE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Colossal Forge", GOSSIP_SENDER_MAIN, FORGE); - if (instance->GetBossState(BOSS_XT002) == DONE) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Scrapyard", GOSSIP_SENDER_MAIN, SCRAPYARD); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Antechamber of Ulduar", GOSSIP_SENDER_MAIN, ANTECHAMBER); - } - if (instance->GetBossState(BOSS_KOLOGARN) == DONE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Shattered Walkway", GOSSIP_SENDER_MAIN, WALKWAY); - if (instance->GetBossState(BOSS_AURIAYA) == DONE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "Teleport to the Conservatory of Life", GOSSIP_SENDER_MAIN, CONSERVATORY); - } - - player->SEND_GOSSIP_MENU(gameObject->GetGOInfo()->GetGossipMenuId(), gameObject->GetGUID()); - return true; - } -}; - -void AddSC_ulduar_teleporter() -{ - new ulduar_teleporter(); -} diff --git a/src/server/scripts/Northrend/zone_crystalsong_forest.cpp b/src/server/scripts/Northrend/zone_crystalsong_forest.cpp index e4bd9c469fb..7d680ecd071 100644 --- a/src/server/scripts/Northrend/zone_crystalsong_forest.cpp +++ b/src/server/scripts/Northrend/zone_crystalsong_forest.cpp @@ -76,20 +76,18 @@ public: GetCreatureListWithEntryInGrid(orbList, me, NPC_TRANSITUS_SHIELD_DUMMY, 32.0f); if (!orbList.empty()) { - for (std::list<Creature*>::const_iterator itr = orbList.begin(); itr != orbList.end(); ++itr) + for (Creature* orb : orbList) { - if (Creature* pOrb = *itr) + if (orb->GetPositionY() < 1000) { - if (pOrb->GetPositionY() < 1000) - { - targetGUID = pOrb->GetGUID(); - break; - } + targetGUID = orb->GetGUID(); + break; } } } } - }else + } + else { if (!targetGUID) if (Creature* pOrb = GetClosestCreatureWithEntry(me, NPC_TRANSITUS_SHIELD_DUMMY, 32.0f)) diff --git a/src/server/scripts/Northrend/zone_dalaran.cpp b/src/server/scripts/Northrend/zone_dalaran.cpp index 30e86fc3b63..2d5028bac05 100644 --- a/src/server/scripts/Northrend/zone_dalaran.cpp +++ b/src/server/scripts/Northrend/zone_dalaran.cpp @@ -73,7 +73,6 @@ public: void AttackStart(Unit* /*who*/) override { } void MoveInLineOfSight(Unit* who) override - { if (!who || !who->IsInWorld() || who->GetZoneId() != 4395) return; diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index bda6d953d9f..59c9b21a220 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -246,13 +246,10 @@ class npc_commander_eligor_dawnbringer : public CreatureScript { std::list<Creature*> creatureList; GetCreatureListWithEntryInGrid(creatureList, me, AudienceMobs[ii], 15.0f); - for (std::list<Creature*>::iterator itr = creatureList.begin(); itr != creatureList.end(); ++itr) + for (Creature* creature : creatureList) { - if (Creature* creatureList = *itr) - { - audienceList[creaturecount] = creatureList->GetGUID(); - ++creaturecount; - } + audienceList[creaturecount] = creature->GetGUID(); + ++creaturecount; } } diff --git a/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp index 0004df68016..f03caa37cb2 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_bloodboil.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp @@ -17,14 +17,14 @@ */ /* ScriptData -SDName: Boss_Bloodboil -SD%Complete: 80 -SDComment: Bloodboil not working correctly, missing enrage -SDCategory: Black Temple +Name: Boss_Bloodboil +Complete: 80 +Category: Black Temple EndScriptData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "black_temple.h" enum Bloodboil @@ -54,9 +54,6 @@ enum Bloodboil SPELL_BERSERK = 45078 }; - -//This is used to sort the players by distance in preparation for the Bloodboil cast. - class boss_gurtogg_bloodboil : public CreatureScript { public: @@ -137,51 +134,6 @@ public: Talk(SAY_DEATH); } - // Note: This seems like a very complicated fix. The fix needs to be handled by the core, as implementation of limited-target AoE spells are still not limited. - void CastBloodboil() - { - // Get the Threat List - std::list<HostileReference*> m_threatlist = me->getThreatManager().getThreatList(); - - if (m_threatlist.empty()) // He doesn't have anyone in his threatlist, useless to continue - return; - - std::list<Unit*> targets; - std::list<HostileReference*>::const_iterator itr = m_threatlist.begin(); - for (; itr!= m_threatlist.end(); ++itr) //store the threat list in a different container - { - Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - //only on alive players - if (target && target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER) - targets.push_back(target); - } - - //Sort the list of players - targets.sort(Trinity::ObjectDistanceOrderPred(me, false)); - //Resize so we only get top 5 - targets.resize(5); - - //Aura each player in the targets list with Bloodboil. Aura code copied+pasted from Aura command in Level3.cpp - /*SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_BLOODBOIL); - if (spellInfo) - { - for (std::list<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr) - { - Unit* target = *itr; - if (!target) return; - for (uint32 i = 0; i<3; ++i) - { - uint8 eff = spellInfo->Effect[i]; - if (eff >= TOTAL_SPELL_EFFECTS) - continue; - - Aura* Aur = new Aura(spellInfo, i, target, target, target); - target->AddAura(Aur); - } - } - }*/ - } - void RevertThreatOnTarget(uint64 guid) { if (Unit* unit = ObjectAccessor::GetUnit(*me, guid)) @@ -247,8 +199,7 @@ public: { if (BloodboilCount < 5) // Only cast it five times. { - //CastBloodboil(); // Causes issues on windows, so is commented out. - DoCastVictim(SPELL_BLOODBOIL); + DoCastAOE(SPELL_BLOODBOIL); ++BloodboilCount; BloodboilTimer = 10000*BloodboilCount; } @@ -274,7 +225,7 @@ public: { if (Phase1) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) { Phase1 = false; @@ -327,7 +278,41 @@ public: }; +// 42005 - Bloodboil +class spell_gurtogg_bloodboil_bloodboil : public SpellScriptLoader +{ + public: + spell_gurtogg_bloodboil_bloodboil() : SpellScriptLoader("spell_gurtogg_bloodboil_bloodboil") { } + + class spell_gurtogg_bloodboil_bloodboil_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gurtogg_bloodboil_bloodboil_SpellScript); + + void FilterTargets(std::list<WorldObject*>& targets) + { + if (targets.size() <= 5) + return; + + // Sort the list of players + targets.sort(Trinity::ObjectDistanceOrderPred(GetCaster(), false)); + // Resize so we only get top 5 + targets.resize(5); + } + + void Register() override + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gurtogg_bloodboil_bloodboil_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_gurtogg_bloodboil_bloodboil_SpellScript(); + } +}; + void AddSC_boss_gurtogg_bloodboil() { new boss_gurtogg_bloodboil(); + new spell_gurtogg_bloodboil_bloodboil(); } diff --git a/src/server/scripts/Outland/CMakeLists.txt b/src/server/scripts/Outland/CMakeLists.txt index 414a3bce14a..0c69a236ef8 100644 --- a/src/server/scripts/Outland/CMakeLists.txt +++ b/src/server/scripts/Outland/CMakeLists.txt @@ -112,7 +112,7 @@ set(scripts_STAT_SRCS Outland/BlackTemple/instance_black_temple.cpp Outland/BlackTemple/boss_reliquary_of_souls.cpp Outland/BlackTemple/boss_warlord_najentus.cpp - Outland/BlackTemple/boss_bloodboil.cpp + Outland/BlackTemple/boss_gurtogg_bloodboil.cpp Outland/BlackTemple/boss_illidan.cpp Outland/zone_shadowmoon_valley.cpp Outland/zone_blades_edge_mountains.cpp diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 9c6ed2ff120..a98734e8308 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -33,6 +33,7 @@ enum DeathKnightSpells SPELL_DK_BLACK_ICE_R1 = 49140, SPELL_DK_BLOOD_BOIL_TRIGGERED = 65658, SPELL_DK_BLOOD_GORGED_HEAL = 50454, + SPELL_DK_BLOOD_PLAGUE = 55078, SPELL_DK_BLOOD_PRESENCE = 48263, SPELL_DK_BLOOD_PRESENCE_TRIGGERED = 61261, SPELL_DK_BLOOD_SHIELD_MASTERY = 77513, @@ -43,9 +44,11 @@ enum DeathKnightSpells SPELL_DK_DEATH_COIL_HEAL = 47633, SPELL_DK_DEATH_STRIKE_HEAL = 45470, SPELL_DK_DEATH_STRIKE_ENABLER = 89832, + SPELL_DK_FROST_FEVER = 55095, SPELL_DK_FROST_PRESENCE = 48266, SPELL_DK_GHOUL_EXPLODE = 47496, SPELL_DK_GLYPH_OF_ICEBOUND_FORTITUDE = 58625, + SPELL_DK_GLYPH_OF_DISEASE = 63334, SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1 = 50365, SPELL_DK_IMPROVED_DEATH_STRIKE = 62905, SPELL_DK_IMPROVED_FROST_PRESENCE_R1 = 50384, @@ -936,6 +939,90 @@ class spell_dk_necrotic_strike : public SpellScriptLoader } }; +// ID - 50842 Pestilence +class spell_dk_pestilence : public SpellScriptLoader +{ + public: + spell_dk_pestilence() : SpellScriptLoader("spell_dk_pestilence") { } + + class spell_dk_pestilence_SpellScript : public SpellScript + { + PrepareSpellScript(spell_dk_pestilence_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_GLYPH_OF_DISEASE) + || !sSpellMgr->GetSpellInfo(SPELL_DK_BLOOD_PLAGUE) + || !sSpellMgr->GetSpellInfo(SPELL_DK_FROST_FEVER)) + return false; + return true; + } + + void OnHit(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Unit* hitUnit = GetHitUnit(); + Unit* victim = GetExplTargetUnit(); + + if (!victim) + return; + + if (victim != hitUnit || caster->HasAura(SPELL_DK_GLYPH_OF_DISEASE)) + { + if (Aura* aurOld = victim->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID())) // Check Blood Plague application on victim. + { + if (AuraEffect* aurEffOld = aurOld->GetEffect(EFFECT_0)) + { + float donePct = aurEffOld->GetDonePct(); + float critChance = aurEffOld->GetCritChance(); + + caster->CastSpell(hitUnit, SPELL_DK_BLOOD_PLAGUE, true); // Spread the disease to hitUnit. + + if (Aura* aurNew = hitUnit->GetAura(SPELL_DK_BLOOD_PLAGUE, caster->GetGUID())) // Check Blood Plague application on hitUnit. + { + if (AuraEffect* aurEffNew = aurNew->GetEffect(EFFECT_0)) + { + aurEffNew->SetCritChance(critChance); // Blood Plague can crit if caster has T9. + aurEffNew->SetDonePct(donePct); + aurEffNew->SetDamage(caster->SpellDamageBonusDone(hitUnit, aurEffNew->GetSpellInfo(), std::max(aurEffNew->GetAmount(), 0), DOT) * donePct); + } + } + } + } + + if (Aura* aurOld = victim->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID())) // Check Frost Fever application on victim. + { + if (AuraEffect* aurEffOld = aurOld->GetEffect(EFFECT_0)) + { + float donePct = aurEffOld->GetDonePct(); + + caster->CastSpell(hitUnit, SPELL_DK_FROST_FEVER, true); // Spread the disease to hitUnit. + + if (Aura* aurNew = hitUnit->GetAura(SPELL_DK_FROST_FEVER, caster->GetGUID())) // Check Frost Fever application on hitUnit. + { + if (AuraEffect* aurEffNew = aurNew->GetEffect(EFFECT_0)) + { + aurEffNew->SetDonePct(donePct); + aurEffNew->SetDamage(caster->SpellDamageBonusDone(hitUnit, aurEffNew->GetSpellInfo(), std::max(aurEffNew->GetAmount(), 0), DOT) * donePct); + } + } + } + } + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_dk_pestilence_SpellScript::OnHit, EFFECT_2, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_dk_pestilence_SpellScript(); + } +}; + // 48266 - Blood Presence // 48263 - Frost Presence // 48265 - Unholy Presence @@ -1415,6 +1502,7 @@ void AddSC_deathknight_spell_scripts() new spell_dk_improved_frost_presence(); new spell_dk_improved_unholy_presence(); new spell_dk_necrotic_strike(); + new spell_dk_pestilence(); new spell_dk_presence(); new spell_dk_raise_dead(); new spell_dk_rune_tap_party(); diff --git a/src/server/scripts/World/areatrigger_scripts.cpp b/src/server/scripts/World/areatrigger_scripts.cpp index fb438c38efb..4393f72eb1b 100644 --- a/src/server/scripts/World/areatrigger_scripts.cpp +++ b/src/server/scripts/World/areatrigger_scripts.cpp @@ -51,11 +51,7 @@ enum CoilfangGOs class AreaTrigger_at_coilfang_waterfall : public AreaTriggerScript { public: - - AreaTrigger_at_coilfang_waterfall() - : AreaTriggerScript("at_coilfang_waterfall") - { - } + AreaTrigger_at_coilfang_waterfall() : AreaTriggerScript("at_coilfang_waterfall") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override { @@ -83,11 +79,7 @@ enum LegionTeleporter class AreaTrigger_at_legion_teleporter : public AreaTriggerScript { public: - - AreaTrigger_at_legion_teleporter() - : AreaTriggerScript("at_legion_teleporter") - { - } + AreaTrigger_at_legion_teleporter() : AreaTriggerScript("at_legion_teleporter") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override { @@ -125,11 +117,7 @@ enum StormwrightShelf class AreaTrigger_at_stormwright_shelf : public AreaTriggerScript { public: - - AreaTrigger_at_stormwright_shelf() - : AreaTriggerScript("at_stormwright_shelf") - { - } + AreaTrigger_at_stormwright_shelf() : AreaTriggerScript("at_stormwright_shelf") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override { @@ -153,11 +141,7 @@ enum ScentLarkorwi class AreaTrigger_at_scent_larkorwi : public AreaTriggerScript { public: - - AreaTrigger_at_scent_larkorwi() - : AreaTriggerScript("at_scent_larkorwi") - { - } + AreaTrigger_at_scent_larkorwi() : AreaTriggerScript("at_scent_larkorwi") { } bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override { @@ -184,11 +168,7 @@ enum AtLastRites class AreaTrigger_at_last_rites : public AreaTriggerScript { public: - - AreaTrigger_at_last_rites() - : AreaTriggerScript("at_last_rites") - { - } + AreaTrigger_at_last_rites() : AreaTriggerScript("at_last_rites") { } bool OnTrigger(Player* player, AreaTriggerEntry const* trigger) override { @@ -246,7 +226,6 @@ enum Waygate class AreaTrigger_at_sholazar_waygate : public AreaTriggerScript { public: - AreaTrigger_at_sholazar_waygate() : AreaTriggerScript("at_sholazar_waygate") { } bool OnTrigger(Player* player, AreaTriggerEntry const* trigger) override diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 9dbcb91dc1a..f73d1c77da4 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -118,7 +118,8 @@ public: enum GildedBrazier { - NPC_STILLBLADE = 17716, + NPC_STILLBLADE = 17716, + QUEST_THE_FIRST_TRIAL = 9678 }; class go_gilded_brazier : public GameObjectScript @@ -130,7 +131,7 @@ public: { if (go->GetGoType() == GAMEOBJECT_TYPE_GOOBER) { - if (player->GetQuestStatus(9678) == QUEST_STATUS_INCOMPLETE) + if (player->GetQuestStatus(QUEST_THE_FIRST_TRIAL) == QUEST_STATUS_INCOMPLETE) { if (Creature* Stillblade = player->SummonCreature(NPC_STILLBLADE, 8106.11f, -7542.06f, 151.775f, 3.02598f, TEMPSUMMON_DEAD_DESPAWN, 60000)) Stillblade->AI()->AttackStart(player); diff --git a/src/server/scripts/World/guards.cpp b/src/server/scripts/World/guards.cpp index 21a81d37868..a156a41fcef 100644 --- a/src/server/scripts/World/guards.cpp +++ b/src/server/scripts/World/guards.cpp @@ -387,7 +387,7 @@ public: void AddSC_guards() { - new guard_generic; - new guard_shattrath_aldor; - new guard_shattrath_scryer; + new guard_generic(); + new guard_shattrath_aldor(); + new guard_shattrath_scryer(); } diff --git a/src/server/scripts/World/mob_generic_creature.cpp b/src/server/scripts/World/mob_generic_creature.cpp index 30666d5d2ea..2eb91b7b8fe 100644 --- a/src/server/scripts/World/mob_generic_creature.cpp +++ b/src/server/scripts/World/mob_generic_creature.cpp @@ -229,6 +229,6 @@ public: void AddSC_generic_creature() { //new generic_creature; - new trigger_periodic; + new trigger_periodic(); //new trigger_death; } diff --git a/src/server/scripts/World/npc_innkeeper.cpp b/src/server/scripts/World/npc_innkeeper.cpp index b647cccf8ea..be56e57cc9d 100644 --- a/src/server/scripts/World/npc_innkeeper.cpp +++ b/src/server/scripts/World/npc_innkeeper.cpp @@ -134,6 +134,6 @@ public: void AddSC_npc_innkeeper() { - new npc_innkeeper; + new npc_innkeeper(); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 321c3d67dc5..a0a48de1872 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -185,7 +185,6 @@ public: } void MoveInLineOfSight(Unit* who) override - { if (!SpawnAssoc) return; diff --git a/src/server/shared/Utilities/Util.cpp b/src/server/shared/Utilities/Util.cpp index c26b981dab9..4c54492f5ad 100644 --- a/src/server/shared/Utilities/Util.cpp +++ b/src/server/shared/Utilities/Util.cpp @@ -559,6 +559,15 @@ std::string ByteArrayToHexStr(uint8 const* bytes, uint32 arrayLen, bool reverse return ss.str(); } +uint32 EventMap::GetTimeUntilEvent(uint32 eventId) const +{ + for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr) + if (eventId == (itr->second & 0x0000FFFF)) + return itr->first - _time; + + return std::numeric_limits<uint32>::max(); +} + void HexStrToByteArray(std::string const& str, uint8* out, bool reverse /*= false*/) { // string must have even number of characters diff --git a/src/server/shared/Utilities/Util.h b/src/server/shared/Utilities/Util.h index bf70bdf406a..538264c9cfe 100644 --- a/src/server/shared/Utilities/Util.h +++ b/src/server/shared/Utilities/Util.h @@ -868,14 +868,7 @@ class EventMap * @param Id of the event. * @return Time of next event. */ - uint32 GetTimeUntilEvent(uint32 eventId) const - { - for (EventStore::const_iterator itr = _eventMap.begin(); itr != _eventMap.end(); ++itr) - if (eventId == (itr->second & 0x0000FFFF)) - return itr->first - _time; - - return std::numeric_limits<uint32>::max(); - } + uint32 GetTimeUntilEvent(uint32 eventId) const; private: /** |