diff --git a/Source/Framework/Constants/MapConst.cs b/Source/Framework/Constants/MapConst.cs index ef802e910..093bbd6ca 100644 --- a/Source/Framework/Constants/MapConst.cs +++ b/Source/Framework/Constants/MapConst.cs @@ -107,11 +107,13 @@ namespace Framework.Constants GameObject = 0x08, Player = 0x10, AreaTrigger = 0x20, - Conversation = 0x40, - All = 0x7F, + SceneObject = 0x40, + Conversation = 0x80, - //GameObjects, Creatures(except pets), DynamicObject, Corpse(Bones), AreaTrigger - AllGrid = GameObject | Creature | DynamicObject | Corpse | AreaTrigger | Conversation, + All = 0xFF, + + //GameObjects, Creatures(except pets), DynamicObject, Corpse(Bones), AreaTrigger, SceneObject + AllGrid = GameObject | Creature | DynamicObject | Corpse | AreaTrigger | SceneObject | Conversation, //Player, Pets, Corpse(resurrectable), DynamicObject(farsight) AllWorld = Player | Creature | Corpse | DynamicObject diff --git a/Source/Framework/Constants/ObjectConst.cs b/Source/Framework/Constants/ObjectConst.cs index 267e0b7ed..01cf870f1 100644 --- a/Source/Framework/Constants/ObjectConst.cs +++ b/Source/Framework/Constants/ObjectConst.cs @@ -50,7 +50,7 @@ namespace Framework.Constants DynamicObject = 0x200, Corpse = 0x400, AreaTrigger = 0x800, - Sceneobject = 0x1000, + SceneObject = 0x1000, Conversation = 0x2000, Seer = Player | Unit | DynamicObject } diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 4994df179..963bf7417 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -2839,4 +2839,10 @@ namespace Framework.Constants FanfareNeeded = 0x80, DisplayOverridden = 0x100 } + + public enum SceneType + { + Normal = 0, + PetBattle = 1 + } } diff --git a/Source/Game/Entities/Object/Update/UpdateFields.cs b/Source/Game/Entities/Object/Update/UpdateFields.cs index 29bdcbcbc..77298487e 100644 --- a/Source/Game/Entities/Object/Update/UpdateFields.cs +++ b/Source/Game/Entities/Object/Update/UpdateFields.cs @@ -5566,7 +5566,7 @@ namespace Game.Entities WriteUpdate(data, _changesMask, false, owner, receiver); } - void WriteUpdate(WorldPacket data, UpdateMask changesMask, bool ignoreNestedChangesMask, WorldObject owner, Player receiver) + public void WriteUpdate(WorldPacket data, UpdateMask changesMask, bool ignoreNestedChangesMask, WorldObject owner, Player receiver) { data.WriteBits(_changesMask.GetBlock(0), 5); diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 875b38a45..7140d60af 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -20,14 +20,14 @@ using Framework.Dynamic; using Framework.GameMath; using Game.AI; using Game.BattleFields; +using Game.DataStorage; using Game.Maps; using Game.Networking; using Game.Networking.Packets; using Game.Scenarios; +using Game.Spells; using System; using System.Collections.Generic; -using Game.DataStorage; -using Game.Spells; namespace Game.Entities { @@ -155,6 +155,7 @@ namespace Game.Entities case HighGuid.Corpse: case HighGuid.DynamicObject: case HighGuid.AreaTrigger: + case HighGuid.SceneObject: case HighGuid.Conversation: updateType = UpdateType.CreateObject2; break; @@ -606,11 +607,11 @@ namespace Game.Entities // *data << ObjectGuid(ReplaceObject); //} - //if (flags.SceneObject) - //{ - // data.WriteBit(HasLocalScriptData); - // data.WriteBit(HasPetBattleFullUpdate); - // data.FlushBits(); + if (flags.SceneObject) + { + data.WriteBit(false); // HasLocalScriptData + data.WriteBit(false); // HasPetBattleFullUpdate + data.FlushBits(); // if (HasLocalScriptData) // { @@ -714,7 +715,7 @@ namespace Game.Entities // data.WriteBit(CanAwardXP); // data.FlushBits(); // } - //} + } if (flags.ActivePlayer) { diff --git a/Source/Game/Entities/SceneObject.cs b/Source/Game/Entities/SceneObject.cs new file mode 100644 index 000000000..5b27ac630 --- /dev/null +++ b/Source/Game/Entities/SceneObject.cs @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2012-2020 CypherCore + * + * 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 3 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 . + */ + +using Framework.Constants; +using Game.Maps; +using Game.Networking; +using Game.Spells; + +namespace Game.Entities +{ + public class SceneObject : WorldObject + { + SceneObjectData m_sceneObjectData; + + Position _stationaryPosition; + ObjectGuid _createdBySpellCast; + + public SceneObject() : base(false) + { + ObjectTypeMask |= TypeMask.SceneObject; + ObjectTypeId = TypeId.SceneObject; + + m_updateFlag.Stationary = true; + m_updateFlag.SceneObject = true; + } + + public override void AddToWorld() + { + if (!IsInWorld) + { + GetMap().GetObjectsStore().Add(GetGUID(), this); + base.AddToWorld(); + } + } + + public override void RemoveFromWorld() + { + if (IsInWorld) + { + base.RemoveFromWorld(); + GetMap().GetObjectsStore().Remove(GetGUID()); + } + } + + public override void Update(uint diff) + { + base.Update(diff); + + if (ShouldBeRemoved()) + Remove(); + } + + void Remove() + { + if (IsInWorld) + AddObjectToRemoveList(); + } + + bool ShouldBeRemoved() + { + Unit creator = Global.ObjAccessor.GetUnit(this, GetOwnerGUID()); + if (creator == null) + return true; + + if (!_createdBySpellCast.IsEmpty()) + { + // search for a dummy aura on creator + Aura linkedAura = creator.GetAura(_createdBySpellCast.GetEntry(), aura => aura.GetCastId() == _createdBySpellCast); + if (linkedAura == null) + return true; + } + + return false; + } + + public static SceneObject CreateSceneObject(uint sceneId, Unit creator, Position pos, ObjectGuid privateObjectOwner) + { + SceneTemplate sceneTemplate = Global.ObjectMgr.GetSceneTemplate(sceneId); + if (sceneTemplate == null) + return null; + + ulong lowGuid = creator.GetMap().GenerateLowGuid(HighGuid.SceneObject); + + SceneObject sceneObject = new SceneObject(); + if (!sceneObject.Create(lowGuid, SceneType.Normal, sceneId, sceneTemplate != null ? sceneTemplate.ScenePackageId : 0, creator.GetMap(), creator, pos, privateObjectOwner)) + { + sceneObject.Dispose(); + return null; + } + + return sceneObject; + } + + bool Create(ulong lowGuid, SceneType type, uint sceneId, uint scriptPackageId, Map map, Unit creator, Position pos, ObjectGuid privateObjectOwner) + { + SetMap(map); + Relocate(pos); + RelocateStationaryPosition(pos); + + SetPrivateObjectOwner(privateObjectOwner); + + _Create(ObjectGuid.Create(HighGuid.SceneObject, GetMapId(), sceneId, lowGuid)); + PhasingHandler.InheritPhaseShift(this, creator); + + SetEntry(scriptPackageId); + SetObjectScale(1.0f); + + SetUpdateFieldValue(m_values.ModifyValue(m_sceneObjectData).ModifyValue(m_sceneObjectData.ScriptPackageID), (int)scriptPackageId); + SetUpdateFieldValue(m_values.ModifyValue(m_sceneObjectData).ModifyValue(m_sceneObjectData.RndSeedVal), GameTime.GetGameTimeMS()); + SetUpdateFieldValue(m_values.ModifyValue(m_sceneObjectData).ModifyValue(m_sceneObjectData.CreatedBy), creator.GetGUID()); + SetUpdateFieldValue(m_values.ModifyValue(m_sceneObjectData).ModifyValue(m_sceneObjectData.SceneType), (uint)type); + + if (!GetMap().AddToMap(this)) + return false; + + return true; + } + + public override void BuildValuesCreate(WorldPacket data, Player target) + { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); + WorldPacket buffer = new(); + + m_objectData.WriteCreate(buffer, flags, this, target); + m_sceneObjectData.WriteCreate(buffer, flags, this, target); + + data.WriteUInt32(buffer.GetSize()); + data.WriteUInt8((byte)flags); + data.WriteBytes(buffer); + + + } + + public override void BuildValuesUpdate(WorldPacket data, Player target) + { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); + WorldPacket buffer = new(); + + buffer.WriteUInt32(m_values.GetChangedObjectTypeMask()); + if (m_values.HasChanged(TypeId.Object)) + m_objectData.WriteUpdate(buffer, flags, this, target); + + if (m_values.HasChanged(TypeId.SceneObject)) + m_sceneObjectData.WriteUpdate(buffer, flags, this, target); + + data.WriteUInt32(buffer.GetSize()); + data.WriteBytes(buffer); + } + + void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedSceneObjectMask, Player target) + { + UpdateMask valuesMask = new((int)TypeId.Max); + if (requestedObjectMask.IsAnySet()) + valuesMask.Set((int)TypeId.Object); + + if (requestedSceneObjectMask.IsAnySet()) + valuesMask.Set((int)TypeId.SceneObject); + + WorldPacket buffer = new(); + buffer.WriteUInt32(valuesMask.GetBlock(0)); + + if (valuesMask[(int)TypeId.Object]) + m_objectData.WriteUpdate(buffer, requestedObjectMask, true, this, target); + + if (valuesMask[(int)TypeId.SceneObject]) + m_sceneObjectData.WriteUpdate(buffer, requestedSceneObjectMask, true, this, target); + + WorldPacket buffer1 = new(); + buffer1.WriteUInt8((byte)UpdateType.Values); + buffer1.WritePackedGuid(GetGUID()); + buffer1.WriteUInt32(buffer.GetSize()); + buffer1.WriteBytes(buffer.GetData()); + + data.AddUpdateBlock(buffer1); + } + + public override void ClearUpdateMask(bool remove) + { + m_values.ClearChangesMask(m_sceneObjectData); + base.ClearUpdateMask(remove); + } + + public override ObjectGuid GetOwnerGUID() { return m_sceneObjectData.CreatedBy; } + public override uint GetFaction() { return 0; } + + public override float GetStationaryX() { return _stationaryPosition.GetPositionX(); } + public override float GetStationaryY() { return _stationaryPosition.GetPositionY(); } + public override float GetStationaryZ() { return _stationaryPosition.GetPositionZ(); } + public override float GetStationaryO() { return _stationaryPosition.GetOrientation(); } + void RelocateStationaryPosition(Position pos) { _stationaryPosition.Relocate(pos); } + + public void SetCreatedBySpellCast(ObjectGuid castId) { _createdBySpellCast = castId; } + } +} diff --git a/Source/Game/Globals/ObjectAccessor.cs b/Source/Game/Globals/ObjectAccessor.cs index 2edaaf643..1ae30618e 100644 --- a/Source/Game/Globals/ObjectAccessor.cs +++ b/Source/Game/Globals/ObjectAccessor.cs @@ -51,6 +51,8 @@ public class ObjectAccessor : Singleton return GetAreaTrigger(p, guid); case HighGuid.Corpse: return GetCorpse(p, guid); + case HighGuid.SceneObject: + return GetSceneObject(p, guid); case HighGuid.Conversation: return GetConversation(p, guid); default: @@ -92,6 +94,10 @@ public class ObjectAccessor : Singleton if (typemask.HasAnyFlag(TypeMask.AreaTrigger)) return GetAreaTrigger(p, guid); break; + case HighGuid.SceneObject: + if (typemask.HasAnyFlag(TypeMask.SceneObject)) + return GetSceneObject(p, guid); + break; case HighGuid.Conversation: if (typemask.HasAnyFlag(TypeMask.Conversation)) return GetConversation(p, guid); @@ -133,6 +139,11 @@ public class ObjectAccessor : Singleton return u.GetMap().GetAreaTrigger(guid); } + static SceneObject GetSceneObject(WorldObject u, ObjectGuid guid) + { + return u.GetMap().GetSceneObject(guid); + } + static Conversation GetConversation(WorldObject u, ObjectGuid guid) { return u.GetMap().GetConversation(guid); diff --git a/Source/Game/Maps/Grid.cs b/Source/Game/Maps/Grid.cs index f88c38753..4087c1f06 100644 --- a/Source/Game/Maps/Grid.cs +++ b/Source/Game/Maps/Grid.cs @@ -292,6 +292,7 @@ namespace Game.Maps visitor.Visit(_container.dynamicObjects); visitor.Visit(_container.corpses); visitor.Visit(_container.areaTriggers); + visitor.Visit(_container.sceneObjects); visitor.Visit(_container.conversations); visitor.Visit(_container.worldObjects); break; @@ -349,7 +350,7 @@ namespace Game.Maps MultiTypeContainer _objects; /// - /// Holds all Grid objects - GameObjects, Creatures(except pets), DynamicObject, Corpse(Bones), AreaTrigger, Conversation + /// Holds all Grid objects - GameObjects, Creatures(except pets), DynamicObject, Corpse(Bones), AreaTrigger, Conversation, SceneObject /// MultiTypeContainer _container; } @@ -379,6 +380,9 @@ namespace Game.Maps case TypeId.AreaTrigger: areaTriggers.Add((AreaTrigger)obj); break; + case TypeId.SceneObject: + sceneObjects.Add((SceneObject)obj); + break; case TypeId.Conversation: conversations.Add((Conversation)obj); break; @@ -408,6 +412,9 @@ namespace Game.Maps case TypeId.AreaTrigger: areaTriggers.Remove((AreaTrigger)obj); break; + case TypeId.SceneObject: + sceneObjects.Remove((SceneObject)obj); + break; case TypeId.Conversation: conversations.Remove((Conversation)obj); break; @@ -447,6 +454,7 @@ namespace Game.Maps public List corpses = new(); public List dynamicObjects = new(); public List areaTriggers = new(); + public List sceneObjects = new(); public List conversations = new(); public List gameObjects = new(); public List worldObjects = new(); diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index d383bd936..3c04a1599 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -31,6 +31,7 @@ namespace Game.Maps public virtual void Visit(IList objs) { } public virtual void Visit(IList objs) { } public virtual void Visit(IList objs) { } + public virtual void Visit(IList objs) { } public virtual void Visit(IList objs) { } public virtual void Visit(IList objs) { } public virtual void Visit(IList objs) { } @@ -66,6 +67,7 @@ namespace Game.Maps public void Visit(IList collection) { _notifier.Visit(collection); } public void Visit(IList creatures) { _notifier.Visit(creatures); } public void Visit(IList areatriggers) { _notifier.Visit(areatriggers); } + public void Visit(IList sceneObjects) { _notifier.Visit(sceneObjects); } public void Visit(IList conversations) { _notifier.Visit(conversations); } public void Visit(IList gameobjects) { _notifier.Visit(gameobjects); } public void Visit(IList dynamicobjects) { _notifier.Visit(dynamicobjects); } @@ -767,6 +769,19 @@ namespace Game.Maps } } + public override void Visit(IList objs) + { + if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.SceneObject)) + return; + + for (var i = 0; i < objs.Count; ++i) + { + SceneObject sceneObject = objs[i]; + if (sceneObject.IsInPhase(_searcher)) + i_do.Invoke(sceneObject); + } + } + public override void Visit(IList objs) { if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) @@ -1129,6 +1144,29 @@ namespace Game.Maps } } + public override void Visit(IList objs) + { + if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.SceneObject)) + return; + + // already found + if (i_object) + return; + + for (var i = 0; i < objs.Count; ++i) + { + SceneObject sceneObject = objs[i]; + if (!sceneObject.IsInPhase(_searcher)) + continue; + + if (i_check.Invoke(sceneObject)) + { + i_object = sceneObject; + return; + } + } + } + public override void Visit(IList objs) { if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) @@ -1264,6 +1302,22 @@ namespace Game.Maps } } + public override void Visit(IList objs) + { + if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.SceneObject)) + return; + + for (var i = 0; i < objs.Count; ++i) + { + SceneObject sceneObject = objs[i]; + if (!sceneObject.IsInPhase(_searcher)) + continue; + + if (i_check.Invoke(sceneObject)) + i_object = sceneObject; + } + } + public override void Visit(IList objs) { if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) @@ -1375,6 +1429,19 @@ namespace Game.Maps } } + public override void Visit(IList objs) + { + if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) + return; + + for (var i = 0; i < objs.Count; ++i) + { + SceneObject sceneObject = objs[i]; + if (i_check.Invoke(sceneObject)) + i_objects.Add(sceneObject); + } + } + public override void Visit(IList objs) { if (!i_mapTypeMask.HasAnyFlag(GridMapTypeMask.Conversation)) diff --git a/Source/Game/Maps/Map.cs b/Source/Game/Maps/Map.cs index 0201144e8..1a4dbbe13 100644 --- a/Source/Game/Maps/Map.cs +++ b/Source/Game/Maps/Map.cs @@ -21,7 +21,6 @@ using Framework.Dynamic; using Framework.GameMath; using Game.BattleGrounds; using Game.Collision; -using Game.Combat; using Game.DataStorage; using Game.Entities; using Game.Groups; @@ -3824,14 +3823,21 @@ namespace Game.Maps return (AreaTrigger)_objectsStore.LookupByKey(guid); } + public SceneObject GetSceneObject(ObjectGuid guid) + { + return _objectsStore.LookupByKey(guid) as SceneObject; + } + public Conversation GetConversation(ObjectGuid guid) { return (Conversation)_objectsStore.LookupByKey(guid); } + public Player GetPlayer(ObjectGuid guid) { return Global.ObjAccessor.GetPlayer(this, guid); } + public Corpse GetCorpse(ObjectGuid guid) { if (!guid.IsCorpse()) diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index f71a5715e..7d4ac74b6 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -16,7 +16,6 @@ */ using Framework.Constants; -using Framework.Dynamic; using Framework.GameMath; using Game.BattleGrounds; using Game.BattlePets; @@ -32,6 +31,7 @@ using Game.Movement; using Game.Networking.Packets; using System; using System.Collections.Generic; +using System.Linq; namespace Game.Spells { @@ -5589,6 +5589,54 @@ namespace Game.Spells m_caster.ToPlayer().GetSceneMgr().PlaySceneByPackageId((uint)effectInfo.MiscValue, SceneFlags.PlayerNonInteractablePhased, destTarget); } + bool IsUnitTargetSceneObjectAura(Spell spell, TargetInfo target) + { + if (target.TargetGUID != spell.GetCaster().GetGUID()) + return false; + + foreach (SpellEffectInfo spellEffectInfo in spell.GetSpellInfo().GetEffects()) + if ((target.EffectMask & (1 << (int)spellEffectInfo.EffectIndex)) != 0 && spellEffectInfo.IsUnitOwnedAuraEffect()) + return true; + + return false; + } + + [SpellEffectHandler(SpellEffectName.CreateSceneObject)] + void EffectCreateSceneObject() + { + if (effectHandleMode != SpellEffectHandleMode.Hit) + return; + + if (!unitCaster || !m_targets.HasDst()) + return; + + SceneObject sceneObject = SceneObject.CreateSceneObject((uint)effectInfo.MiscValue, unitCaster, destTarget.GetPosition(), ObjectGuid.Empty); + if (sceneObject != null) + { + bool hasAuraTargetingCaster = m_UniqueTargetInfo.Any(target => IsUnitTargetSceneObjectAura(this, target)); + if (hasAuraTargetingCaster) + sceneObject.SetCreatedBySpellCast(m_castId); + } + } + + [SpellEffectHandler(SpellEffectName.CreatePersonalSceneObject)] + void EffectCreatePrivateSceneObject() + { + if (effectHandleMode != SpellEffectHandleMode.Hit) + return; + + if (!unitCaster || !m_targets.HasDst()) + return; + + SceneObject sceneObject = SceneObject.CreateSceneObject((uint)effectInfo.MiscValue, unitCaster, destTarget.GetPosition(), unitCaster.GetGUID()); + if (sceneObject != null) + { + bool hasAuraTargetingCaster = m_UniqueTargetInfo.Any(target => IsUnitTargetSceneObjectAura(this, target)); + if (hasAuraTargetingCaster) + sceneObject.SetCreatedBySpellCast(m_castId); + } + } + [SpellEffectHandler(SpellEffectName.PlayScene)] void EffectPlayScene() {