/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * 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 . */ #include "DynamicObject.h" #include "Common.h" #include "GameTime.h" #include "Log.h" #include "Map.h" #include "ObjectAccessor.h" #include "PhasingHandler.h" #include "Player.h" #include "ScriptMgr.h" #include "SpellAuras.h" #include "SpellInfo.h" #include "SpellMgr.h" #include "Transport.h" #include "Unit.h" #include "UpdateData.h" DynamicObject::DynamicObject(bool isWorldObject) : WorldObject(isWorldObject), _aura(nullptr), _removedAura(nullptr), _caster(nullptr), _duration(0), _isViewpoint(false) { m_objectType |= TYPEMASK_DYNAMICOBJECT; m_objectTypeId = TYPEID_DYNAMICOBJECT; m_updateFlag.Stationary = true; m_entityFragments.Add(WowCS::EntityFragment::Tag_DynamicObject, false); } DynamicObject::~DynamicObject() { // make sure all references were properly removed ASSERT(!_aura); ASSERT(!_caster); ASSERT(!_isViewpoint); delete _removedAura; } void DynamicObject::AddToWorld() { ///- Register the dynamicObject for guid lookup and for caster if (!IsInWorld()) { GetMap()->GetObjectsStore().Insert(this); WorldObject::AddToWorld(); BindToCaster(); } } void DynamicObject::RemoveFromWorld() { ///- Remove the dynamicObject from the accessor and from all lists of objects in world if (IsInWorld()) { if (_isViewpoint) RemoveCasterViewpoint(); if (_aura) RemoveAura(); // dynobj could get removed in Aura::RemoveAura if (!IsInWorld()) return; UnbindFromCaster(); WorldObject::RemoveFromWorld(); GetMap()->GetObjectsStore().Remove(this); } } bool DynamicObject::CreateDynamicObject(ObjectGuid::LowType guidlow, Unit* caster, SpellInfo const* spell, Position const& pos, float radius, DynamicObjectType type, SpellCastVisual spellVisual) { SetMap(caster->GetMap()); Relocate(pos); if (!IsPositionValid()) { TC_LOG_ERROR("misc", "DynamicObject (spell {}) not created. Suggested coordinates isn't valid (X: {} Y: {})", spell->Id, GetPositionX(), GetPositionY()); return false; } WorldObject::_Create(ObjectGuid::Create(GetMapId(), spell->Id, guidlow)); PhasingHandler::InheritPhaseShift(this, caster); UpdatePositionData(); SetZoneScript(); SetEntry(spell->Id); SetObjectScale(1.0f); auto dynamicObjectData = m_values.ModifyValue(&DynamicObject::m_dynamicObjectData); SetUpdateFieldValue(dynamicObjectData.ModifyValue(&UF::DynamicObjectData::Caster), caster->GetGUID()); SetUpdateFieldValue(dynamicObjectData.ModifyValue(&UF::DynamicObjectData::Type), type); SetUpdateFieldValue(dynamicObjectData.ModifyValue(&UF::DynamicObjectData::SpellVisual).ModifyValue(&UF::SpellCastVisual::SpellXSpellVisualID), spellVisual.SpellXSpellVisualID); SetUpdateFieldValue(dynamicObjectData.ModifyValue(&UF::DynamicObjectData::SpellVisual).ModifyValue(&UF::SpellCastVisual::ScriptVisualID), spellVisual.ScriptVisualID); SetUpdateFieldValue(dynamicObjectData.ModifyValue(&UF::DynamicObjectData::SpellID), spell->Id); SetUpdateFieldValue(dynamicObjectData.ModifyValue(&UF::DynamicObjectData::Radius), radius); SetUpdateFieldValue(dynamicObjectData.ModifyValue(&UF::DynamicObjectData::CastTime), GameTime::GetGameTimeMS()); if (IsStoredInWorldObjectGridContainer()) setActive(true); //must before add to map to be put in world container TransportBase* transport = caster->GetTransport(); if (transport) { 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); // This object must be added to transport before adding to map for the client to properly display it transport->AddPassenger(this); } if (!GetMap()->AddToMap(this)) { // Returning false will cause the object to be deleted - remove from transport if (transport) transport->RemovePassenger(this); return false; } return true; } void DynamicObject::Update(uint32 p_time) { // caster has to be always available and in the same map ASSERT(_caster); ASSERT(_caster->GetMap() == GetMap()); bool expired = false; if (_aura) { if (!_aura->IsRemoved()) _aura->UpdateOwner(p_time, this); // _aura may be set to null in Aura::UpdateOwner call if (_aura && (_aura->IsRemoved() || _aura->IsExpired())) expired = true; } else { if (GetDuration() > int32(p_time)) _duration -= p_time; else expired = true; } if (expired) Remove(); else sScriptMgr->OnDynamicObjectUpdate(this, p_time); } void DynamicObject::Remove() { if (IsInWorld()) AddObjectToRemoveList(); } int32 DynamicObject::GetDuration() const { if (!_aura) return _duration; else return _aura->GetDuration(); } void DynamicObject::SetDuration(int32 newDuration) { if (!_aura) _duration = newDuration; else _aura->SetDuration(newDuration); } void DynamicObject::Delay(int32 delaytime) { SetDuration(GetDuration() - delaytime); } void DynamicObject::SetAura(Aura* aura) { ASSERT(!_aura && aura); _aura = aura; } void DynamicObject::RemoveAura() { ASSERT(_aura && !_removedAura); _removedAura = _aura; _aura = nullptr; if (!_removedAura->IsRemoved()) _removedAura->_Remove(AURA_REMOVE_BY_DEFAULT); } void DynamicObject::SetCasterViewpoint() { if (Player* caster = _caster->ToPlayer()) { caster->SetViewpoint(this, true); _isViewpoint = true; } } void DynamicObject::RemoveCasterViewpoint() { if (Player* caster = _caster->ToPlayer()) { caster->SetViewpoint(this, false); _isViewpoint = false; } } uint32 DynamicObject::GetFaction() const { ASSERT(_caster); return _caster->GetFaction(); } void DynamicObject::BindToCaster() { ASSERT(!_caster); _caster = ObjectAccessor::GetUnit(*this, GetCasterGUID()); ASSERT(_caster); ASSERT(_caster->GetMap() == GetMap()); _caster->_RegisterDynObject(this); } void DynamicObject::UnbindFromCaster() { ASSERT(_caster); _caster->_UnregisterDynObject(this); _caster = nullptr; } SpellInfo const* DynamicObject::GetSpellInfo() const { return sSpellMgr->GetSpellInfo(GetSpellId(), GetMap()->GetDifficultyID()); } void DynamicObject::BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { m_objectData->WriteCreate(*data, flags, this, target); m_dynamicObjectData->WriteCreate(*data, flags, this, target); } void DynamicObject::BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const { *data << uint32(m_values.GetChangedObjectTypeMask()); if (m_values.HasChanged(TYPEID_OBJECT)) m_objectData->WriteUpdate(*data, flags, this, target); if (m_values.HasChanged(TYPEID_DYNAMICOBJECT)) m_dynamicObjectData->WriteUpdate(*data, flags, this, target); } void DynamicObject::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::DynamicObjectData::Mask const& requestedDynamicObjectMask, Player const* target) const { UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); if (requestedDynamicObjectMask.IsAnySet()) valuesMask.Set(TYPEID_DYNAMICOBJECT); ByteBuffer& buffer = PrepareValuesUpdateBuffer(data); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); BuildEntityFragmentsForValuesUpdateForPlayerWithMask(&buffer, flags); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) m_objectData->WriteUpdate(buffer, requestedObjectMask, true, this, target); if (valuesMask[TYPEID_DYNAMICOBJECT]) m_dynamicObjectData->WriteUpdate(buffer, requestedDynamicObjectMask, true, this, target); buffer.put(sizePos, buffer.wpos() - sizePos - 4); data->AddUpdateBlock(); } void DynamicObject::ValuesUpdateForPlayerWithMaskSender::operator()(Player const* player) const { UpdateData udata(Owner->GetMapId()); WorldPacket packet; Owner->BuildValuesUpdateForPlayerWithMask(&udata, ObjectMask.GetChangesMask(), DynamicObjectMask.GetChangesMask(), player); udata.BuildPacket(&packet); player->SendDirectMessage(&packet); } void DynamicObject::ClearUpdateMask(bool remove) { m_values.ClearChangesMask(&DynamicObject::m_dynamicObjectData); Object::ClearUpdateMask(remove); }