diff --git a/Source/Framework/Constants/CreatureConst.cs b/Source/Framework/Constants/CreatureConst.cs index c562017cd..28fec6852 100644 --- a/Source/Framework/Constants/CreatureConst.cs +++ b/Source/Framework/Constants/CreatureConst.cs @@ -209,6 +209,8 @@ namespace Framework.Constants VaultKeeper = 0x20000000, // Void Storage WildBattlePet = 0x40000000, // Pet That Player Can Fight (Battle Pet) BlackMarket = 0x80000000, // Black Market + + VendorMask = Vendor | VendorAmmo | VendorFood | VendorPoison | VendorReagent } public enum NPCFlags2 @@ -728,4 +730,14 @@ namespace Framework.Constants None = 0, Empty = 1 } + + public enum VendorDataTypeFlags + { + Generic = 0x01, + Ammo = 0x02, + Food = 0x04, + Poison = 0x08, + Reagent = 0x10, + Petition = 0x20, + } } diff --git a/Source/Framework/Constants/ObjectConst.cs b/Source/Framework/Constants/ObjectConst.cs index 867aae926..106d17318 100644 --- a/Source/Framework/Constants/ObjectConst.cs +++ b/Source/Framework/Constants/ObjectConst.cs @@ -39,7 +39,7 @@ namespace Framework.Constants SceneObject = 0x1000, Conversation = 0x2000, - Seer = Player | Unit | DynamicObject, + Seer = Unit | Player | DynamicObject, WorldObject = Unit | GameObject | DynamicObject | Corpse | AreaTrigger | SceneObject | Conversation } diff --git a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs index 8230b56be..22661b89b 100644 --- a/Source/Game/Entities/AreaTrigger/AreaTrigger.cs +++ b/Source/Game/Entities/AreaTrigger/AreaTrigger.cs @@ -28,6 +28,8 @@ namespace Game.Entities m_updateFlag.Stationary = true; m_updateFlag.AreaTrigger = true; + m_entityFragments.Add(EntityFragment.Tag_AreaTrigger, false); + m_areaTriggerData = new AreaTriggerFieldData(); _spline = new(); @@ -1348,6 +1350,7 @@ namespace Game.Entities public void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedAreaTriggerMask, Player target) { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask = new((int)TypeId.Max); if (requestedObjectMask.IsAnySet()) valuesMask.Set((int)TypeId.Object); @@ -1356,6 +1359,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.AreaTrigger); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Conversation.cs b/Source/Game/Entities/Conversation.cs index 471199d06..700117fb3 100644 --- a/Source/Game/Entities/Conversation.cs +++ b/Source/Game/Entities/Conversation.cs @@ -23,6 +23,8 @@ namespace Game.Entities m_updateFlag.Stationary = true; m_updateFlag.Conversation = true; + m_entityFragments.Add(EntityFragment.Tag_Conversation, false); + m_conversationData = new ConversationData(); } @@ -305,6 +307,7 @@ namespace Game.Entities void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedConversationMask, Player target) { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask = new((int)TypeId.Max); if (requestedObjectMask.IsAnySet()) valuesMask.Set((int)TypeId.Object); @@ -313,6 +316,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.Conversation); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Corpse.cs b/Source/Game/Entities/Corpse.cs index 327d7aba4..d5238d690 100644 --- a/Source/Game/Entities/Corpse.cs +++ b/Source/Game/Entities/Corpse.cs @@ -24,6 +24,8 @@ namespace Game.Entities m_updateFlag.Stationary = true; + m_entityFragments.Add(EntityFragment.Tag_Corpse, false); + m_corpseData = new(); m_time = GameTime.GetGameTime(); @@ -235,6 +237,7 @@ namespace Game.Entities void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedCorpseMask, Player target) { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask = new((int)TypeId.Max); if (requestedObjectMask.IsAnySet()) valuesMask.Set((int)TypeId.Object); @@ -243,6 +246,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.Corpse); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Creature/Creature.Fields.cs b/Source/Game/Entities/Creature/Creature.Fields.cs index afd6cc728..20660ee13 100644 --- a/Source/Game/Entities/Creature/Creature.Fields.cs +++ b/Source/Game/Entities/Creature/Creature.Fields.cs @@ -82,6 +82,7 @@ namespace Game.Entities uint m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check // vendor items + VendorData m_vendorData; List m_vendorItemCounts = new(); internal Dictionary m_personalLoot = new(); diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 19df653c5..0cf7caa14 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -316,9 +316,17 @@ namespace Game.Entities if (cInfo.FlagsExtra.HasAnyFlag(CreatureFlagsExtra.Worldevent)) npcFlags |= Global.GameEventMgr.GetNPCFlag(this); + if (IsVendor() && !npcFlags.HasAnyFlag((ulong)NPCFlags.VendorMask)) + SetVendor(NPCFlags.VendorMask, false); + ReplaceAllNpcFlags((NPCFlags)(npcFlags & 0xFFFFFFFF)); ReplaceAllNpcFlags2((NPCFlags2)(npcFlags >> 32)); + if (npcFlags.HasAnyFlag((ulong)NPCFlags.VendorMask)) + SetVendor((NPCFlags)npcFlags & NPCFlags.VendorMask, true); + + SetPetitioner(npcFlags.HasAnyFlag((ulong)NPCFlags.Petitioner)); + // if unit is in combat, keep this flag unitFlags &= ~(uint)UnitFlags.InCombat; if (IsInCombat()) @@ -3081,6 +3089,62 @@ namespace Game.Entities return vCount.count; } + public void SetVendor(NPCFlags flags, bool apply) + { + flags &= NPCFlags.VendorMask; + VendorDataTypeFlags vendorFlags = (VendorDataTypeFlags)((uint)flags >> 7); + if (apply) + { + if (m_vendorData == null) + { + m_entityFragments.Add(EntityFragment.FVendor_C, IsInWorld); + m_vendorData = new(); + } + + SetNpcFlag(flags); + SetUpdateFieldFlagValue(m_values.ModifyValue(m_vendorData).ModifyValue(m_vendorData.Flags), (int)vendorFlags); + } + else if (m_vendorData != null) + { + RemoveNpcFlag(flags); + RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_vendorData).ModifyValue(m_vendorData.Flags), (int)vendorFlags); + if (m_vendorData.Flags == 0) + { + m_values.ModifyValue(m_vendorData); + AddToObjectUpdateIfNeeded(); + m_vendorData = null; + m_entityFragments.Remove(EntityFragment.FVendor_C); + } + } + } + + void SetPetitioner(bool apply) + { + if (apply) + { + if (m_vendorData == null) + { + m_entityFragments.Add(EntityFragment.FVendor_C, IsInWorld); + m_vendorData = new(); + } + + SetNpcFlag(NPCFlags.Petitioner); + SetUpdateFieldFlagValue(m_values.ModifyValue(m_vendorData).ModifyValue(m_vendorData.Flags), (int)VendorDataTypeFlags.Petition); + } + else if (m_vendorData != null) + { + RemoveNpcFlag(NPCFlags.Petitioner); + RemoveUpdateFieldFlagValue(m_values.ModifyValue(m_vendorData).ModifyValue(m_vendorData.Flags), (int)VendorDataTypeFlags.Petition); + if (m_vendorData.Flags == 0) + { + m_values.ModifyValue(m_vendorData); + AddToObjectUpdateIfNeeded(); + m_vendorData = null; + m_entityFragments.Remove(EntityFragment.FVendor_C); + } + } + } + public override string GetName(Locale locale = Locale.enUS) { if (locale != Locale.enUS) @@ -3695,17 +3759,26 @@ namespace Game.Entities { m_objectData.WriteCreate(data, flags, this, target); m_unitData.WriteCreate(data, flags, this, target); + + if (m_vendorData != null) + m_vendorData.WriteCreate(data, flags, this, target); } public override void BuildValuesUpdate(WorldPacket data, UpdateFieldFlag flags, Player target) { - data.WriteUInt32(m_values.GetChangedObjectTypeMask()); + if ((m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(EntityFragment.CGObject)) != 0) + { + data.WriteUInt32(m_values.GetChangedObjectTypeMask()); - if (m_values.HasChanged(TypeId.Object)) - m_objectData.WriteUpdate(data, flags, this, target); + if (m_values.HasChanged(TypeId.Object)) + m_objectData.WriteUpdate(data, flags, this, target); - if (m_values.HasChanged(TypeId.Unit)) - m_unitData.WriteUpdate(data, flags, this, target); + if (m_values.HasChanged(TypeId.Unit)) + m_unitData.WriteUpdate(data, flags, this, target); + } + + if (m_vendorData != null && (m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(EntityFragment.FVendor_C)) != 0) + m_vendorData.WriteUpdate(data, flags, this, target); } public override void BuildValuesUpdateWithFlag(WorldPacket data, UpdateFieldFlag flags, Player target) @@ -3733,6 +3806,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.Unit); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) @@ -3750,6 +3824,14 @@ namespace Game.Entities data.AddUpdateBlock(buffer1); } + public override void ClearUpdateMask(bool remove) + { + if (m_vendorData != null) + m_values.ClearChangesMask(m_vendorData); + + base.ClearUpdateMask(remove); + } + class ValuesUpdateForPlayerWithMaskSender : IDoWork { Creature Owner; diff --git a/Source/Game/Entities/DynamicObject.cs b/Source/Game/Entities/DynamicObject.cs index f6322fb01..faaa71a9d 100644 --- a/Source/Game/Entities/DynamicObject.cs +++ b/Source/Game/Entities/DynamicObject.cs @@ -18,6 +18,8 @@ namespace Game.Entities m_updateFlag.Stationary = true; + m_entityFragments.Add(EntityFragment.Tag_DynamicObject, false); + m_dynamicObjectData = new DynamicObjectData(); } @@ -258,6 +260,7 @@ namespace Game.Entities void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedDynamicObjectMask, Player target) { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask = new((int)TypeId.Max); if (requestedObjectMask.IsAnySet()) valuesMask.Set((int)TypeId.Object); @@ -266,6 +269,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.DynamicObject); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index 1b8e0ed20..598847de3 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -31,6 +31,8 @@ namespace Game.Entities m_updateFlag.Stationary = true; m_updateFlag.Rotation = true; + m_entityFragments.Add(EntityFragment.Tag_GameObject, false); + m_respawnDelayTime = 300; m_despawnDelay = 0; m_lootState = LootState.NotReady; @@ -298,6 +300,7 @@ namespace Game.Entities SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.ParentRotation), new Quaternion(goInfo.DestructibleBuilding.DestructibleModelRec, 0f, 0f, 0f)); break; case GameObjectTypes.Transport: + m_updateFlag.GameObject = true; m_goTypeImpl = new GameObjectType.Transport(this); if (goInfo.Transport.startOpen != 0) @@ -3289,6 +3292,7 @@ namespace Game.Entities public void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedGameObjectMask, Player target) { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask = new((int)TypeId.Max); if (requestedObjectMask.IsAnySet()) valuesMask.Set((int)TypeId.Object); @@ -3297,6 +3301,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.GameObject); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) @@ -3328,22 +3333,11 @@ namespace Game.Entities return null; } + public float? GetPathProgressForClient() { return m_transportPathProgress; } + public void SetPathProgressForClient(float progress) { - DoWithSuppressingObjectUpdates(() => - { - ObjectFieldData dynflagMask = new(); - dynflagMask.MarkChanged(m_objectData.DynamicFlags); - bool marked = (m_objectData.GetUpdateMask() & dynflagMask.GetUpdateMask()).IsAnySet(); - - uint dynamicFlags = (uint)GetDynamicFlags(); - dynamicFlags &= 0xFFFF; // remove high bits - dynamicFlags |= (uint)(progress * 65535.0f) << 16; - ReplaceAllDynamicFlags((GameObjectDynamicLowFlags)dynamicFlags); - - if (!marked) - m_objectData.ClearChanged(m_objectData.DynamicFlags); - }); + m_transportPathProgress = progress; } public void GetRespawnPosition(out float x, out float y, out float z, out float ori) @@ -3943,6 +3937,7 @@ namespace Game.Entities bool m_respawnCompatibilityMode; ushort _animKitId; uint _worldEffectID; + float? m_transportPathProgress; VignetteData m_vignette; diff --git a/Source/Game/Entities/Item/AzeriteEmpoweredItem.cs b/Source/Game/Entities/Item/AzeriteEmpoweredItem.cs index 9ff8e0aad..13860a719 100644 --- a/Source/Game/Entities/Item/AzeriteEmpoweredItem.cs +++ b/Source/Game/Entities/Item/AzeriteEmpoweredItem.cs @@ -22,6 +22,8 @@ namespace Game.Entities ObjectTypeMask |= TypeMask.AzeriteEmpoweredItem; ObjectTypeId = TypeId.AzeriteEmpoweredItem; + m_entityFragments.Add(EntityFragment.Tag_AzeriteEmpoweredItem, false); + m_azeriteEmpoweredItemData = new AzeriteEmpoweredItemData(); } @@ -181,6 +183,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.AzeriteEmpoweredItem); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Item/AzeriteItem.cs b/Source/Game/Entities/Item/AzeriteItem.cs index 4889194b5..c1db6e0e6 100644 --- a/Source/Game/Entities/Item/AzeriteItem.cs +++ b/Source/Game/Entities/Item/AzeriteItem.cs @@ -22,6 +22,8 @@ namespace Game.Entities ObjectTypeMask |= TypeMask.AzeriteItem; ObjectTypeId = TypeId.AzeriteItem; + m_entityFragments.Add(EntityFragment.Tag_AzeriteItem, false); + SetUpdateFieldValue(m_values.ModifyValue(m_azeriteItemData).ModifyValue(m_azeriteItemData.DEBUGknowledgeWeek), -1); } @@ -457,6 +459,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.AzeriteItem); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Item/Bag.cs b/Source/Game/Entities/Item/Bag.cs index 05006a3fd..fe2312ca3 100644 --- a/Source/Game/Entities/Item/Bag.cs +++ b/Source/Game/Entities/Item/Bag.cs @@ -16,6 +16,8 @@ namespace Game.Entities ObjectTypeMask |= TypeMask.Container; ObjectTypeId = TypeId.Container; + m_entityFragments.Add(EntityFragment.Tag_Container, false); + m_containerData = new ContainerData(); } @@ -195,6 +197,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.Container); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Item/Item.cs b/Source/Game/Entities/Item/Item.cs index dc9a9f8b3..5d3d8067e 100644 --- a/Source/Game/Entities/Item/Item.cs +++ b/Source/Game/Entities/Item/Item.cs @@ -24,6 +24,8 @@ namespace Game.Entities ObjectTypeMask |= TypeMask.Item; ObjectTypeId = TypeId.Item; + m_entityFragments.Add(EntityFragment.Tag_Item, false); + m_itemData = new ItemData(); uState = ItemUpdateState.New; @@ -1310,6 +1312,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.Item); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Object/Update/UpdateFields.cs b/Source/Game/Entities/Object/Update/UpdateFields.cs index 2c9e4e8d9..08d6cf989 100644 --- a/Source/Game/Entities/Object/Update/UpdateFields.cs +++ b/Source/Game/Entities/Object/Update/UpdateFields.cs @@ -19,7 +19,7 @@ namespace Game.Entities public UpdateField DynamicFlags = new(0, 2); public UpdateField Scale = new(0, 3); - public ObjectFieldData() : base(0, TypeId.Object, 4) { } + public ObjectFieldData() : base((int)EntityFragment.CGObject, TypeId.Object, 4) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, WorldObject owner, Player receiver) { @@ -109,7 +109,6 @@ namespace Game.Entities if (gameObject != null) { GameObjectDynamicLowFlags dynFlags = 0; - ushort pathProgress = 0xFFFF; switch (gameObject.GetGoType()) { case GameObjectTypes.Button: @@ -133,13 +132,6 @@ namespace Game.Entities if (gameObject.HasConditionalInteraction() && gameObject.CanActivateForPlayer(receiver)) dynFlags |= GameObjectDynamicLowFlags.Sparkle; break; - case GameObjectTypes.Transport: - case GameObjectTypes.MapObjTransport: - { - dynFlags = (GameObjectDynamicLowFlags)((int)unitDynFlags & 0xFFFF); - pathProgress = (ushort)((int)unitDynFlags >> 16); - break; - } case GameObjectTypes.CapturePoint: if (!gameObject.CanInteractWithCapturePoint(receiver)) dynFlags |= GameObjectDynamicLowFlags.NoInterract; @@ -169,7 +161,7 @@ namespace Game.Entities dynFlags |= GameObjectDynamicLowFlags.NoInterract; } - unitDynFlags = ((uint)pathProgress << 16) | (uint)dynFlags; + unitDynFlags = (uint)dynFlags; } } @@ -410,7 +402,7 @@ namespace Game.Entities public UpdateFieldArray SpellCharges = new(5, 21, 22); public UpdateFieldArray Enchantment = new(13, 27, 28); - public ItemData() : base(0, TypeId.Item, 41) { } + public ItemData() : base((int)EntityFragment.CGObject, TypeId.Item, 41) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Item owner, Player receiver) { @@ -662,7 +654,7 @@ namespace Game.Entities public UpdateField NumSlots = new(0, 1); public UpdateFieldArray Slots = new(98, 2, 3); - public ContainerData() : base(0, TypeId.Container, 101) { } + public ContainerData() : base((int)EntityFragment.CGObject, TypeId.Container, 101) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Bag owner, Player receiver) { @@ -717,7 +709,7 @@ namespace Game.Entities { public UpdateFieldArray Selections = new(5, 0, 1); - public AzeriteEmpoweredItemData() : base(0, TypeId.AzeriteEmpoweredItem, 6) { } + public AzeriteEmpoweredItemData() : base((int)EntityFragment.CGObject, TypeId.AzeriteEmpoweredItem, 6) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Item owner, Player receiver) { @@ -855,7 +847,7 @@ namespace Game.Entities public UpdateField KnowledgeLevel = new(0, 8); public UpdateField DEBUGknowledgeWeek = new(0, 9); - public AzeriteItemData() : base(10) { } + public AzeriteItemData() : base((int)EntityFragment.CGObject, TypeId.AzeriteItem, 10) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, AzeriteItem owner, Player receiver) { @@ -1256,8 +1248,8 @@ namespace Game.Entities public UpdateField GuildGUID = new(96, 122); public UpdateField FlightCapabilityID = new(96, 123); public UpdateField GlideEventSpeedDivisor = new(96, 124); // Movement speed gets divided by this value when evaluating what GlideEvents to use - public UpdateField MaxHealthModifierFlatNeg = new(96, 125); - public UpdateField MaxHealthModifierFlatPos = new(96, 126); + public UpdateField MaxHealthModifierFlatNeg = new(96, 125); + public UpdateField MaxHealthModifierFlatPos = new(96, 126); public UpdateField SilencedSchoolMask = new(96, 127); public UpdateField CurrentAreaID = new(128, 129); public UpdateField Field_31C = new(128, 130); @@ -1277,7 +1269,7 @@ namespace Game.Entities public UpdateFieldArray BonusResistanceMods = new(7, 198, 206); public UpdateFieldArray ManaCostModifier = new(7, 198, 213); - public UnitData() : base(0, TypeId.Unit, ChangeMaskLength) { } + public UnitData() : base((int)EntityFragment.CGObject, TypeId.Unit, ChangeMaskLength) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Unit owner, Player receiver) { @@ -1461,8 +1453,8 @@ namespace Game.Entities data.WriteInt32(ChannelObjects.Size()); data.WriteInt32(FlightCapabilityID); data.WriteFloat(GlideEventSpeedDivisor); - data.WriteUInt32(MaxHealthModifierFlatNeg); - data.WriteUInt32(MaxHealthModifierFlatPos); + data.WriteInt32(MaxHealthModifierFlatNeg); + data.WriteInt32(MaxHealthModifierFlatPos); data.WriteUInt32(SilencedSchoolMask); data.WriteUInt32(CurrentAreaID); data.WriteFloat(Field_31C); @@ -2062,11 +2054,11 @@ namespace Game.Entities } if (changesMask[125]) { - data.WriteUInt32(MaxHealthModifierFlatNeg); + data.WriteInt32(MaxHealthModifierFlatNeg); } if (changesMask[126]) { - data.WriteUInt32(MaxHealthModifierFlatPos); + data.WriteInt32(MaxHealthModifierFlatPos); } if (changesMask[127]) { @@ -2955,7 +2947,7 @@ namespace Game.Entities public UpdateFieldArray VisibleEquipableSpells = new(16, 285, 286); public UpdateFieldArray Field_3120 = new(19, 302, 303); - public PlayerData() : base(0, TypeId.Player, 322) { } + public PlayerData() : base((int)EntityFragment.CGObject, TypeId.Player, 322) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Player owner, Player receiver) { @@ -3600,22 +3592,17 @@ namespace Game.Entities } } - public class BitVectors : HasChangesMask + public class BitVector : HasChangesMask { - public UpdateFieldArray> Values = new(11, 0, -1); + public DynamicUpdateField Values = new(0, 1); - public BitVectors() : base(1) { } + public BitVector() : base(2) { } public void WriteCreate(WorldPacket data, Player owner, Player receiver) { - for (int i = 0; i < 11; ++i) - { - data.WriteInt32(Values[i].Size()); - for (int j = 0; j < Values[i].Size(); ++j) - { - data.WriteUInt64(Values[i][j]); - } - } + data.WriteInt32(Values.Size()); + for (int i = 0; i < Values.Size(); ++i) + data.WriteUInt64(Values[i]); } public void WriteUpdate(WorldPacket data, bool ignoreChangesMask, Player owner, Player receiver) @@ -3624,32 +3611,74 @@ namespace Game.Entities if (ignoreChangesMask) changesMask.SetAll(); - data.WriteBits(changesMask.GetBlock(0), 1); + data.WriteBits(changesMask.GetBlock(0), 2); if (changesMask[0]) { - for (int i = 0; i < 11; ++i) + if (changesMask[1]) { if (!ignoreChangesMask) - Values[i].WriteUpdateMask(data); + Values.WriteUpdateMask(data); else - WriteCompleteDynamicFieldUpdateMask(Values[i].Size(), data); + WriteCompleteDynamicFieldUpdateMask(Values.Size(), data); } } + data.FlushBits(); if (changesMask[0]) { - for (int i = 0; i < 11; ++i) + if (changesMask[1]) { - for (int j = 0; j < Values[i].Size(); ++j) + for (int i = 0; i < Values.Size(); ++i) { - if (Values[i].HasChanged(j) || ignoreChangesMask) + if (Values.HasChanged(i) || ignoreChangesMask) { - data.WriteUInt64(Values[i][j]); + data.WriteUInt64(Values[i]); } } } } + } + + public override void ClearChangesMask() + { + ClearChangesMask(Values); + _changesMask.ResetAll(); + } + } + + public class BitVectors : HasChangesMask + { + public UpdateFieldArray Values = new(13, 0, 1); + + public BitVectors() : base(1) { } + + public void WriteCreate(WorldPacket data, Player owner, Player receiver) + { + for (int i = 0; i < 13; ++i) + Values[i].WriteCreate(data, owner, receiver); + } + + public void WriteUpdate(WorldPacket data, bool ignoreChangesMask, Player owner, Player receiver) + { + UpdateMask changesMask = _changesMask; + if (ignoreChangesMask) + changesMask.SetAll(); + + data.WriteBits(changesMask.GetBlocksMask(0), 1); + if (changesMask.GetBlock(0) != 0) + data.WriteBits(changesMask.GetBlock(0), 32); + data.FlushBits(); + if (changesMask[0]) + { + for (int i = 0; i < 13; ++i) + { + if (changesMask[1 + i]) + { + Values[i].WriteUpdate(data, ignoreChangesMask, owner, receiver); + } + } + } } public override void ClearChangesMask() @@ -5460,7 +5489,7 @@ namespace Game.Entities { public List Owners = new(); public int Field_0; - public long Field_8; + public ulong Field_8; public int Field_10; public int SpellID; public uint Started; // Restricts rewards to players in m_owners if set to true. Intended to prevent rewarwding players that join in-progress delve? @@ -5468,7 +5497,7 @@ namespace Game.Entities public void WriteCreate(WorldPacket data, Player owner, Player receiver) { data.WriteInt32(Field_0); - data.WriteInt64(Field_8); + data.WriteUInt64(Field_8); data.WriteInt32(Field_10); data.WriteInt32(SpellID); data.WriteInt32(Owners.Count); @@ -5484,7 +5513,7 @@ namespace Game.Entities { data.FlushBits(); data.WriteInt32(Field_0); - data.WriteInt64(Field_8); + data.WriteUInt64(Field_8); data.WriteInt32(Field_10); data.WriteInt32(SpellID); data.WriteInt32(Owners.Count); @@ -5517,7 +5546,7 @@ namespace Game.Entities public static int QuestCompletedBitsSize; public static int QuestCompletedBitsPerBlock; - static int changeMaskLength = 1476; + static int changeMaskLength = 1516; public UpdateField BackpackAutoSortDisabled = new(0, 1); public UpdateField BackpackSellJunkDisabled = new(0, 2); @@ -5671,10 +5700,10 @@ namespace Game.Entities public UpdateFieldArray ProfessionSkillLine = new(2, 480, 481); public UpdateFieldArray BagSlotFlags = new(5, 483, 484); public UpdateFieldArray BankBagSlotFlags = new(7, 489, 490); - public UpdateFieldArray QuestCompleted = new(960, 497, 498); - public UpdateFieldArray ItemUpgradeHighWatermark = new(17, 1458, 1459); + public UpdateFieldArray QuestCompleted = new(1000, 497, 498); + public UpdateFieldArray ItemUpgradeHighWatermark = new(17, 1498, 1499); - public ActivePlayerData() : base(0, TypeId.ActivePlayer, changeMaskLength) + public ActivePlayerData() : base((int)EntityFragment.CGObject, TypeId.ActivePlayer, changeMaskLength) { QuestCompletedBitsSize = QuestCompleted.GetSize(); QuestCompletedBitsPerBlock = sizeof(ulong) * 8; @@ -5801,7 +5830,7 @@ namespace Game.Entities { data.WriteUInt32(BankBagSlotFlags[i]); } - for (int i = 0; i < 960; ++i) + for (int i = 0; i < 1000; ++i) { data.WriteUInt64(QuestCompleted[i]); } @@ -6045,8 +6074,8 @@ namespace Game.Entities { for (uint i = 0; i < 1; ++i) data.WriteUInt32(changesMask.GetBlocksMask(i)); - data.WriteBits(changesMask.GetBlocksMask(1), 15); - for (uint i = 0; i < 47; ++i) + data.WriteBits(changesMask.GetBlocksMask(1), 16); + for (uint i = 0; i < 48; ++i) if (changesMask.GetBlock(i) != 0) data.WriteBits(changesMask.GetBlock(i), 32); @@ -7272,7 +7301,7 @@ namespace Game.Entities } if (changesMask[497]) { - for (int i = 0; i < 960; ++i) + for (int i = 0; i < 1000; ++i) { if (changesMask[498 + i]) { @@ -7280,11 +7309,11 @@ namespace Game.Entities } } } - if (changesMask[1458]) + if (changesMask[1498]) { for (int i = 0; i < 17; ++i) { - if (changesMask[1459 + i]) + if (changesMask[1499 + i]) { data.WriteFloat(ItemUpgradeHighWatermark[i]); } @@ -7480,7 +7509,7 @@ namespace Game.Entities public UpdateField UiWidgetItemQuality = new(0, 23); public UpdateField UiWidgetItemUnknown1000 = new(0, 24); - public GameObjectFieldData() : base(0, TypeId.GameObject, 25) { } + public GameObjectFieldData() : base((int)EntityFragment.CGObject, TypeId.GameObject, 25) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, GameObject owner, Player receiver) { @@ -7731,7 +7760,7 @@ namespace Game.Entities public UpdateField Radius = new(0, 5); public UpdateField CastTime = new(0, 6); - public DynamicObjectData() : base(0, TypeId.DynamicObject, 7) { } + public DynamicObjectData() : base((int)EntityFragment.CGObject, TypeId.DynamicObject, 7) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, DynamicObject owner, Player receiver) { @@ -7810,7 +7839,7 @@ namespace Game.Entities public UpdateField StateSpellVisualKitID = new(0, 12); public UpdateFieldArray Items = new(19, 13, 14); - public CorpseData() : base(0, TypeId.Corpse, 33) { } + public CorpseData() : base((int)EntityFragment.CGObject, TypeId.Corpse, 33) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Corpse owner, Player receiver) { @@ -8109,7 +8138,7 @@ namespace Game.Entities public UpdateField PositionalSoundKitID = new(0, 24); public UpdateField VisualAnim = new(0, 25); - public AreaTriggerFieldData() : base(0, TypeId.AreaTrigger, 26) { } + public AreaTriggerFieldData() : base((int)EntityFragment.CGObject, TypeId.AreaTrigger, 26) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, AreaTrigger owner, Player receiver) { @@ -8300,7 +8329,7 @@ namespace Game.Entities public UpdateField CreatedBy = new(0, 3); public UpdateField SceneType = new(0, 4); - public SceneObjectData() : base(5) { } + public SceneObjectData() : base((int)EntityFragment.CGObject, TypeId.SceneObject, 5) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, WorldObject owner, Player receiver) { @@ -8437,7 +8466,7 @@ namespace Game.Entities public UpdateField Progress = new(0, 5); public UpdateField Flags = new(0, 6); - public ConversationData() : base(0, TypeId.Conversation, 7) { } + public ConversationData() : base((int)EntityFragment.CGObject, TypeId.Conversation, 7) { } public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Conversation owner, Player receiver) { @@ -8539,4 +8568,41 @@ namespace Game.Entities return (uint)conversation.GetLastLineEndTime(locale).TotalMilliseconds; } } + + public class VendorData : HasChangesMask + { + public UpdateField Flags = new(0, 1); + + public VendorData() : base(2) { } + + public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Creature owner, Player receiver) + { + data.WriteInt32(Flags); + } + + public void WriteUpdate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Creature owner, Player receiver) + { + WriteUpdate(data, _changesMask, false, owner, receiver); + } + + void WriteUpdate(WorldPacket data, UpdateMask changesMask, bool ignoreNestedChangesMask, Creature owner, Player receiver) + { + data.WriteBits(changesMask.GetBlock(0), 2); + + data.FlushBits(); + if (changesMask[0]) + { + if (changesMask[1]) + { + data.WriteInt32(Flags); + } + } + } + + public override void ClearChangesMask() + { + ClearChangesMask(Flags); + _changesMask.ResetAll(); + } + } } diff --git a/Source/Game/Entities/Object/Update/WowCSEntityDefinitions.cs b/Source/Game/Entities/Object/Update/WowCSEntityDefinitions.cs new file mode 100644 index 000000000..adf9228cb --- /dev/null +++ b/Source/Game/Entities/Object/Update/WowCSEntityDefinitions.cs @@ -0,0 +1,175 @@ +// Copyright (c) CypherCore All rights reserved. +// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. + +using System; +using System.Linq; + +namespace Game.Entities +{ + public enum EntityFragment + { + CGObject = 0, // UPDATEABLE, INDIRECT, + Tag_Item = 1, // TAG, + Tag_Container = 2, // TAG, + Tag_AzeriteEmpoweredItem = 3, // TAG, + Tag_AzeriteItem = 4, // TAG, + Tag_Unit = 5, // TAG, + Tag_Player = 6, // TAG, + Tag_GameObject = 7, // TAG, + Tag_DynamicObject = 8, // TAG, + Tag_Corpse = 9, // TAG, + Tag_AreaTrigger = 10, // TAG, + Tag_SceneObject = 11, // TAG, + Tag_Conversation = 12, // TAG, + Tag_AIGroup = 13, // TAG, + Tag_Scenario = 14, // TAG, + Tag_LootObject = 15, // TAG, + Tag_ActivePlayer = 16, // TAG, + Tag_ActiveClient_S = 17, // TAG, + Tag_ActiveObject_C = 18, // TAG, + Tag_VisibleObject_C = 19, // TAG, + Tag_UnitVehicle = 20, // TAG, + FEntityPosition = 112, + FEntityLocalMatrix = 113, + FEntityWorldMatrix = 114, + CActor = 115, // INDIRECT, + FVendor_C = 117, // UPDATEABLE, + FMirroredObject_C = 119, + End = 255, + } + + public enum EntityFragmentSerializationType + { + Full = 0, + Partial = 1 + } + + class EntityDefinitionsConst + { + // common case optimization, make use of the fact that fragment arrays are sorted + public const byte CGObjectActiveMask = 0x1; + public const byte CGObjectChangedMask = 0x2; + public const byte CGObjectUpdateMask = CGObjectActiveMask | CGObjectChangedMask; + + } + + public class EntityFragmentsHolder + { + EntityFragment[] Ids = + [ + EntityFragment.End, EntityFragment.End, EntityFragment.End, EntityFragment.End, + EntityFragment.End, EntityFragment.End, EntityFragment.End, EntityFragment.End + ]; + + public byte Count; + public bool IdsChanged; + + public EntityFragment[] UpdateableIds = [EntityFragment.End, EntityFragment.End]; + public byte[] UpdateableMasks = new byte[2]; + public byte UpdateableCount = 0; + public byte ContentsChangedMask = EntityDefinitionsConst.CGObjectActiveMask; + + public void Add(EntityFragment fragment, bool update) + { + Cypher.Assert(Count < Ids.Length); + + (int, bool) insertSorted(ref EntityFragment[] arr, ref byte count, EntityFragment f) + { + //auto where = std::ranges::lower_bound(arr.begin(), arr.begin() + count, f); + var where = Array.IndexOf(arr, f); + if (where != -1) + return (where, false); + + arr.SetValue(f, where); + arr = arr[^1..^0].Concat(arr[..^1]).ToArray(); + ++count; + return (where, true); + }; + + if (!insertSorted(ref Ids, ref Count, fragment).Item2) + return; + + if (IsUpdateableFragment(fragment)) + { + Cypher.Assert(UpdateableCount < UpdateableIds.Length); + + var index = insertSorted(ref UpdateableIds, ref UpdateableCount, fragment).Item1; + byte maskLowPart = (byte)(ContentsChangedMask & ((1 << index) - 1)); + byte maskHighPart = (byte)((ContentsChangedMask & ~((1 << index) - 1)) << (1 + (IsIndirectFragment(fragment) ? 1 : 0))); + ContentsChangedMask = (byte)(maskLowPart | maskHighPart); + for (byte i = 0, maskIndex = 0; i < UpdateableCount; ++i) + { + UpdateableMasks[i] = (byte)(1 << maskIndex++); + if (IsIndirectFragment(UpdateableIds[i])) + UpdateableMasks[i] |= (byte)(1 << maskIndex++); + } + } + + if (update) + IdsChanged = true; + } + + public void Remove(EntityFragment fragment) + { + (int, bool) removeSorted(ref EntityFragment[] arr, ref byte count, EntityFragment f) + { + var where = Array.IndexOf(arr, f); + if (where != -1) + { + arr.SetValue(EntityFragment.End, where); + arr = arr[^1..^0].Concat(arr[..^1]).ToArray(); + --count; + return (where, true); + } + return (where, false); + }; + + if (!removeSorted(ref Ids, ref Count, fragment).Item2) + return; + + if (IsUpdateableFragment(fragment)) + { + var (index, removed) = removeSorted(ref UpdateableIds, ref UpdateableCount, fragment); + if (removed) + { + byte maskLowPart = (byte)(ContentsChangedMask & ((1 << index) - 1)); + byte maskHighPart = (byte)((ContentsChangedMask & ~((1 << index) - 1)) >> (1 + (IsIndirectFragment(fragment) ? 1 : 0))); + ContentsChangedMask = (byte)(maskLowPart | maskHighPart); + for (byte i = 0, maskIndex = 0; i < UpdateableCount; ++i) + { + UpdateableMasks[i] = (byte)(1 << maskIndex++); + if (IsIndirectFragment(UpdateableIds[i])) + UpdateableMasks[i] |= (byte)(1 << maskIndex++); + } + } + } + + IdsChanged = true; + } + + public static bool IsUpdateableFragment(EntityFragment frag) + { + return frag == EntityFragment.CGObject || frag == EntityFragment.FVendor_C; + } + + public static bool IsIndirectFragment(EntityFragment frag) + { + return frag == EntityFragment.CGObject || frag == EntityFragment.CActor; + } + + public EntityFragment[] GetIds() { return Ids[..Count]; } + public EntityFragment[] GetUpdateableIds() { return UpdateableIds[..UpdateableCount]; } + + public byte GetUpdateMaskFor(EntityFragment fragment) + { + if (fragment == EntityFragment.CGObject) // common case optimization, make use of the fact that fragment arrays are sorted + return EntityDefinitionsConst.CGObjectChangedMask; + + for (byte i = 1; i < UpdateableCount; ++i) + if (UpdateableIds[i] == fragment) + return UpdateableMasks[i]; + + return 0; + } + } +} diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index 5183a0693..774ebc306 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -34,11 +34,13 @@ namespace Game.Entities ObjectTypeId = TypeId.Object; ObjectTypeMask = TypeMask.Object; - m_values = new UpdateFieldHolder(); + m_values = new UpdateFieldHolder(this); m_movementInfo = new MovementInfo(); m_updateFlag.Clear(); + m_entityFragments.Add((int)EntityFragment.CGObject, false); + m_objectData = new ObjectFieldData(); m_staticFloorZ = MapConst.VMAPInvalidHeightValue; @@ -173,6 +175,8 @@ namespace Game.Entities BuildValuesCreate(tempBuffer, fieldFlags, target); buffer.WriteUInt32(tempBuffer.GetSize()); buffer.WriteUInt8((byte)fieldFlags); + BuildEntityFragments(buffer, m_entityFragments.GetIds()); + buffer.WriteUInt8(1); // IndirectFragmentActive: CGObject buffer.WriteBytes(tempBuffer); data.AddUpdateBlock(buffer); @@ -204,6 +208,14 @@ namespace Game.Entities WorldPacket tempBuffer = new(); BuildValuesUpdate(tempBuffer, fieldFlags, target); buffer.WriteUInt32(tempBuffer.GetSize()); + buffer.WriteUInt8((byte)(fieldFlags.HasFlag(UpdateFieldFlag.Owner) ? 1 : 0)); + buffer.WriteUInt8((byte)(m_entityFragments.IdsChanged ? 1 : 0)); + if (m_entityFragments.IdsChanged) + { + buffer.WriteUInt8((byte)EntityFragmentSerializationType.Full); + BuildEntityFragments(buffer, m_entityFragments.GetIds()); + } + buffer.WriteUInt8(m_entityFragments.ContentsChangedMask); buffer.WriteBytes(tempBuffer); data.AddUpdateBlock(buffer); @@ -216,6 +228,7 @@ namespace Game.Entities buffer.WritePackedGuid(GetGUID()); WorldPacket tempBuffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(tempBuffer, flags); BuildValuesUpdateWithFlag(tempBuffer, flags, target); buffer.WriteUInt32(tempBuffer.GetSize()); buffer.WriteBytes(tempBuffer); @@ -223,6 +236,21 @@ namespace Game.Entities data.AddUpdateBlock(buffer); } + void BuildEntityFragments(WorldPacket data, EntityFragment[] fragments) + { + foreach (var frag in fragments) + data.WriteUInt8((byte)frag); + + data.WriteUInt8((byte)EntityFragment.End); + } + + public void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(WorldPacket data, UpdateFieldFlag flags) + { + data.WriteUInt8((byte)(flags.HasFlag(UpdateFieldFlag.Owner) ? 1 : 0)); + data.WriteUInt8(0); // m_entityFragments.IdsChanged + data.WriteUInt8(EntityDefinitionsConst.CGObjectUpdateMask); + } + public void BuildDestroyUpdateBlock(UpdateData data) { data.AddDestroyObject(GetGUID()); @@ -252,13 +280,14 @@ namespace Game.Entities target.SendPacket(packet); } - public void BuildMovementUpdate(WorldPacket data, CreateObjectBits flags, Player target) + public unsafe void BuildMovementUpdate(WorldPacket data, CreateObjectBits flags, Player target) { List PauseTimes = null; GameObject go = ToGameObject(); if (go != null) PauseTimes = go.GetPauseTimes(); + data.WriteBit(IsWorldObject()); // HasPositionFragment data.WriteBit(flags.NoBirthAnim); data.WriteBit(flags.EnablePortals); data.WriteBit(flags.PlayHoverAnim); @@ -456,6 +485,66 @@ namespace Game.Entities data.WriteVector3(areaTrigger.GetRollPitchYaw()); + switch (shape.TriggerType) + { + case AreaTriggerShapeType.Sphere: + data.WriteInt8(0); + data.WriteFloat(shape.SphereDatas.Radius); + data.WriteFloat(shape.SphereDatas.RadiusTarget); + break; + case AreaTriggerShapeType.Box: + data.WriteInt8(1); + data.WriteFloat(shape.BoxDatas.Extents[0]); + data.WriteFloat(shape.BoxDatas.Extents[1]); + data.WriteFloat(shape.BoxDatas.Extents[2]); + data.WriteFloat(shape.BoxDatas.ExtentsTarget[0]); + data.WriteFloat(shape.BoxDatas.ExtentsTarget[1]); + data.WriteFloat(shape.BoxDatas.ExtentsTarget[2]); + break; + case AreaTriggerShapeType.Polygon: + data.WriteInt8(3); + data.WriteInt32(shape.PolygonVertices.Count); + data.WriteInt32(shape.PolygonVerticesTarget.Count); + data.WriteFloat(shape.PolygonDatas.Height); + data.WriteFloat(shape.PolygonDatas.HeightTarget); + + foreach (var vertice in shape.PolygonVertices) + data.WriteVector2(vertice); + + foreach (var vertice in shape.PolygonVerticesTarget) + data.WriteVector2(vertice); + break; + case AreaTriggerShapeType.Cylinder: + data.WriteInt8(4); + data.WriteFloat(shape.CylinderDatas.Radius); + data.WriteFloat(shape.CylinderDatas.RadiusTarget); + data.WriteFloat(shape.CylinderDatas.Height); + data.WriteFloat(shape.CylinderDatas.HeightTarget); + data.WriteFloat(shape.CylinderDatas.LocationZOffset); + data.WriteFloat(shape.CylinderDatas.LocationZOffsetTarget); + break; + case AreaTriggerShapeType.Disk: + data.WriteInt8(7); + data.WriteFloat(shape.DiskDatas.InnerRadius); + data.WriteFloat(shape.DiskDatas.InnerRadiusTarget); + data.WriteFloat(shape.DiskDatas.OuterRadius); + data.WriteFloat(shape.DiskDatas.OuterRadiusTarget); + data.WriteFloat(shape.DiskDatas.Height); + data.WriteFloat(shape.DiskDatas.HeightTarget); + data.WriteFloat(shape.DiskDatas.LocationZOffset); + data.WriteFloat(shape.DiskDatas.LocationZOffsetTarget); + break; + case AreaTriggerShapeType.BoundedPlane: + data.WriteInt8(8); + data.WriteFloat(shape.BoundedPlaneDatas.Extents[0]); + data.WriteFloat(shape.BoundedPlaneDatas.Extents[1]); + data.WriteFloat(shape.BoundedPlaneDatas.ExtentsTarget[0]); + data.WriteFloat(shape.BoundedPlaneDatas.ExtentsTarget[1]); + break; + default: + break; + } + bool hasAbsoluteOrientation = createProperties != null && createProperties.Flags.HasFlag(AreaTriggerCreatePropertiesFlag.HasAbsoluteOrientation); bool hasDynamicShape = createProperties != null && createProperties.Flags.HasFlag(AreaTriggerCreatePropertiesFlag.HasDynamicShape); bool hasAttached = createProperties != null && createProperties.Flags.HasFlag(AreaTriggerCreatePropertiesFlag.HasAttached); @@ -468,12 +557,6 @@ namespace Game.Entities bool hasMorphCurveID = createProperties != null && createProperties.MorphCurveId != 0; bool hasFacingCurveID = createProperties != null && createProperties.FacingCurveId != 0; bool hasMoveCurveID = createProperties != null && createProperties.MoveCurveId != 0; - bool hasAreaTriggerSphere = shape.IsSphere(); - bool hasAreaTriggerBox = shape.IsBox(); - bool hasAreaTriggerPolygon = shape.IsPolygon(); - bool hasAreaTriggerCylinder = shape.IsCylinder(); - bool hasDisk = shape.IsDisk(); - bool hasBoundedPlane = shape.IsBoundedPlane(); bool hasAreaTriggerSpline = areaTrigger.HasSplines(); bool hasOrbit = areaTrigger.HasOrbit(); bool hasMovementScript = false; @@ -492,12 +575,6 @@ namespace Game.Entities data.WriteBit(hasFacingCurveID); data.WriteBit(hasMoveCurveID); data.WriteBit(hasPositionalSoundKitID); - data.WriteBit(hasAreaTriggerSphere); - data.WriteBit(hasAreaTriggerBox); - data.WriteBit(hasAreaTriggerPolygon); - data.WriteBit(hasAreaTriggerCylinder); - data.WriteBit(hasDisk); - data.WriteBit(hasBoundedPlane); data.WriteBit(hasAreaTriggerSpline); data.WriteBit(hasOrbit); data.WriteBit(hasMovementScript); @@ -530,73 +607,6 @@ namespace Game.Entities if (hasPositionalSoundKitID) data.WriteUInt32(0); - if (hasAreaTriggerSphere) - { - data.WriteFloat(shape.SphereDatas.Radius); - data.WriteFloat(shape.SphereDatas.RadiusTarget); - } - - if (hasAreaTriggerBox) - { - unsafe - { - data.WriteFloat(shape.BoxDatas.Extents[0]); - data.WriteFloat(shape.BoxDatas.Extents[1]); - data.WriteFloat(shape.BoxDatas.Extents[2]); - - data.WriteFloat(shape.BoxDatas.ExtentsTarget[0]); - data.WriteFloat(shape.BoxDatas.ExtentsTarget[1]); - data.WriteFloat(shape.BoxDatas.ExtentsTarget[2]); - } - } - - if (hasAreaTriggerPolygon) - { - data.WriteInt32(shape.PolygonVertices.Count); - data.WriteInt32(shape.PolygonVerticesTarget.Count); - data.WriteFloat(shape.PolygonDatas.Height); - data.WriteFloat(shape.PolygonDatas.HeightTarget); - - foreach (var vertice in shape.PolygonVertices) - data.WriteVector2(vertice); - - foreach (var vertice in shape.PolygonVerticesTarget) - data.WriteVector2(vertice); - } - - if (hasAreaTriggerCylinder) - { - data.WriteFloat(shape.CylinderDatas.Radius); - data.WriteFloat(shape.CylinderDatas.RadiusTarget); - data.WriteFloat(shape.CylinderDatas.Height); - data.WriteFloat(shape.CylinderDatas.HeightTarget); - data.WriteFloat(shape.CylinderDatas.LocationZOffset); - data.WriteFloat(shape.CylinderDatas.LocationZOffsetTarget); - } - - if (hasDisk) - { - data.WriteFloat(shape.DiskDatas.InnerRadius); - data.WriteFloat(shape.DiskDatas.InnerRadiusTarget); - data.WriteFloat(shape.DiskDatas.OuterRadius); - data.WriteFloat(shape.DiskDatas.OuterRadiusTarget); - data.WriteFloat(shape.DiskDatas.Height); - data.WriteFloat(shape.DiskDatas.HeightTarget); - data.WriteFloat(shape.DiskDatas.LocationZOffset); - data.WriteFloat(shape.DiskDatas.LocationZOffsetTarget); - } - - if (hasBoundedPlane) - { - unsafe - { - data.WriteFloat(shape.BoundedPlaneDatas.Extents[0]); - data.WriteFloat(shape.BoundedPlaneDatas.Extents[1]); - data.WriteFloat(shape.BoundedPlaneDatas.ExtentsTarget[0]); - data.WriteFloat(shape.BoundedPlaneDatas.ExtentsTarget[1]); - } - } - //if (hasMovementScript) // *data << *areaTrigger.GetMovementScript(); // AreaTriggerMovementScriptInfo @@ -606,17 +616,32 @@ namespace Game.Entities if (flags.GameObject) { - bool bit8 = false; - uint Int1 = 0; - GameObject gameObject = ToGameObject(); + Transport transport = gameObject.ToTransport(); + + bool bit8 = false; data.WriteUInt32(gameObject.GetWorldEffectID()); data.WriteBit(bit8); + data.WriteBit(transport != null); + data.WriteBit(gameObject.GetPathProgressForClient().HasValue); data.FlushBits(); + if (transport != null) + { + data.WriteUInt32(transport.GetTransportPeriod()); + data.WriteUInt32(transport.GetTimer()); + data.WriteBit(transport.IsStopRequested()); + data.WriteBit(transport.IsStopped()); + data.WriteBit(false); + data.FlushBits(); + } + if (bit8) - data.WriteUInt32(Int1); + data.WriteUInt32(0); + + if (gameObject.GetPathProgressForClient().HasValue) + data.WriteFloat(gameObject.GetPathProgressForClient().Value); } if (flags.SmoothPhasing) @@ -650,10 +675,10 @@ namespace Game.Entities // for (std::size_t i = 0; i < 2; ++i) // { // *data << ObjectGuid(Players[i].CharacterID); - // *data << int32(Players[i].TrapAbilityID); - // *data << int32(Players[i].TrapStatus); + // data.WriteInt32(Players[i].TrapAbilityID); + // data.WriteInt32(Players[i].TrapStatus); // *data << uint16(Players[i].RoundTimeSecs); - // *data << int8(Players[i].FrontPet); + // data.WriteInt8(Players[i].FrontPet); // *data << uint8(Players[i].InputFlags); // data.WriteBits(Players[i].Pets.size(), 2); @@ -661,45 +686,45 @@ namespace Game.Entities // for (std::size_t j = 0; j < Players[i].Pets.size(); ++j) // { // *data << ObjectGuid(Players[i].Pets[j].BattlePetGUID); - // *data << int32(Players[i].Pets[j].SpeciesID); - // *data << int32(Players[i].Pets[j].CreatureID); - // *data << int32(Players[i].Pets[j].DisplayID); + // data.WriteInt32(Players[i].Pets[j].SpeciesID); + // data.WriteInt32(Players[i].Pets[j].CreatureID); + // data.WriteInt32(Players[i].Pets[j].DisplayID); // *data << int16(Players[i].Pets[j].Level); // *data << int16(Players[i].Pets[j].Xp); - // *data << int32(Players[i].Pets[j].CurHealth); - // *data << int32(Players[i].Pets[j].MaxHealth); - // *data << int32(Players[i].Pets[j].Power); - // *data << int32(Players[i].Pets[j].Speed); - // *data << int32(Players[i].Pets[j].NpcTeamMemberID); + // data.WriteInt32(Players[i].Pets[j].CurHealth); + // data.WriteInt32(Players[i].Pets[j].MaxHealth); + // data.WriteInt32(Players[i].Pets[j].Power); + // data.WriteInt32(Players[i].Pets[j].Speed); + // data.WriteInt32(Players[i].Pets[j].NpcTeamMemberID); // *data << uint8(Players[i].Pets[j].BreedQuality); // *data << uint16(Players[i].Pets[j].StatusFlags); - // *data << int8(Players[i].Pets[j].Slot); + // data.WriteInt8(Players[i].Pets[j].Slot); // *data << uint(Players[i].Pets[j].Abilities.size()); // *data << uint(Players[i].Pets[j].Auras.size()); // *data << uint(Players[i].Pets[j].States.size()); // for (std::size_t k = 0; k < Players[i].Pets[j].Abilities.size(); ++k) // { - // *data << int32(Players[i].Pets[j].Abilities[k].AbilityID); + // data.WriteInt32(Players[i].Pets[j].Abilities[k].AbilityID); // *data << int16(Players[i].Pets[j].Abilities[k].CooldownRemaining); // *data << int16(Players[i].Pets[j].Abilities[k].LockdownRemaining); - // *data << int8(Players[i].Pets[j].Abilities[k].AbilityIndex); + // data.WriteInt8(Players[i].Pets[j].Abilities[k].AbilityIndex); // *data << uint8(Players[i].Pets[j].Abilities[k].Pboid); // } // for (std::size_t k = 0; k < Players[i].Pets[j].Auras.size(); ++k) // { - // *data << int32(Players[i].Pets[j].Auras[k].AbilityID); + // data.WriteInt32(Players[i].Pets[j].Auras[k].AbilityID); // *data << uint(Players[i].Pets[j].Auras[k].InstanceID); - // *data << int32(Players[i].Pets[j].Auras[k].RoundsRemaining); - // *data << int32(Players[i].Pets[j].Auras[k].CurrentRound); + // data.WriteInt32(Players[i].Pets[j].Auras[k].RoundsRemaining); + // data.WriteInt32(Players[i].Pets[j].Auras[k].CurrentRound); // *data << uint8(Players[i].Pets[j].Auras[k].CasterPBOID); // } // for (std::size_t k = 0; k < Players[i].Pets[j].States.size(); ++k) // { // *data << uint(Players[i].Pets[j].States[k].StateID); - // *data << int32(Players[i].Pets[j].States[k].StateValue); + // data.WriteInt32(Players[i].Pets[j].States[k].StateValue); // } // data.WriteBits(Players[i].Pets[j].CustomName.length(), 7); @@ -714,26 +739,26 @@ namespace Game.Entities // *data << uint(Enviros[j].States.size()); // for (std::size_t j = 0; j < Enviros[j].Auras.size(); ++j) // { - // *data << int32(Enviros[j].Auras[j].AbilityID); + // data.WriteInt32(Enviros[j].Auras[j].AbilityID); // *data << uint(Enviros[j].Auras[j].InstanceID); - // *data << int32(Enviros[j].Auras[j].RoundsRemaining); - // *data << int32(Enviros[j].Auras[j].CurrentRound); + // data.WriteInt32(Enviros[j].Auras[j].RoundsRemaining); + // data.WriteInt32(Enviros[j].Auras[j].CurrentRound); // *data << uint8(Enviros[j].Auras[j].CasterPBOID); // } // for (std::size_t j = 0; j < Enviros[j].States.size(); ++j) // { // *data << uint(Enviros[i].States[j].StateID); - // *data << int32(Enviros[i].States[j].StateValue); + // data.WriteInt32(Enviros[i].States[j].StateValue); // } // } // *data << uint16(WaitingForFrontPetsMaxSecs); // *data << uint16(PvpMaxRoundTime); - // *data << int32(CurRound); + // data.WriteInt32(CurRound); // *data << uint(NpcCreatureID); // *data << uint(NpcDisplayID); - // *data << int8(CurPetBattleState); + // data.WriteInt8(CurPetBattleState); // *data << uint8(ForfeitPenalty); // *data << ObjectGuid(InitialWildPetGUID); // data.WriteBit(IsPVP); @@ -812,6 +837,8 @@ namespace Game.Entities public virtual void ClearUpdateMask(bool remove) { m_values.ClearChangesMask(m_objectData); + m_entityFragments.IdsChanged = false; + m_entityFragments.ContentsChangedMask = EntityDefinitionsConst.CGObjectActiveMask; if (m_objectUpdated) { @@ -3852,6 +3879,7 @@ namespace Game.Entities public TypeMask ObjectTypeMask { get; set; } protected TypeId ObjectTypeId { get; set; } protected CreateObjectBits m_updateFlag; + public EntityFragmentsHolder m_entityFragments; ObjectGuid m_guid; bool _isNewObject; bool _isDestroyedObject; @@ -4204,6 +4232,12 @@ namespace Game.Entities public class UpdateFieldHolder { UpdateMask _changesMask = new((int)TypeId.Max); + WorldObject _owner; + + public UpdateFieldHolder(WorldObject owner) + { + _owner = owner; + } public HasChangesMask ModifyValue(HasChangesMask updateData) { @@ -4235,5 +4269,65 @@ namespace Game.Entities { return _changesMask[(int)index]; } + + //New shit + /*HasChangesMask ModifyValue(HasChangesMask updateData) + { + _owner.m_entityFragments.ContentsChangedMask |= _owner.m_entityFragments.GetUpdateMaskFor(WowCS::EntityFragment(BlockBit)); + if constexpr(WowCS::EntityFragment(BlockBit) == WowCS::EntityFragment::CGObject) + _changesMask |= UpdateMaskHelpers::GetBlockFlag(Bit); + + return { (static_cast(owner)->* field)._value }; + }*/ + + public UpdateField ModifyValue(UpdateField updateField) where U : new() + { + _owner.m_entityFragments.ContentsChangedMask |= _owner.m_entityFragments.GetUpdateMaskFor((EntityFragment)updateField.BlockBit); + if ((EntityFragment)updateField.BlockBit == EntityFragment.CGObject) + _changesMask.Set(updateField.Bit); + + return updateField; + } + + public OptionalUpdateField ModifyValue(OptionalUpdateField updateField) where U : new() + { + _owner.m_entityFragments.ContentsChangedMask |= _owner.m_entityFragments.GetUpdateMaskFor((EntityFragment)updateField.BlockBit); + if ((EntityFragment)updateField.BlockBit == EntityFragment.CGObject) + _changesMask.Set(updateField.Bit); + + return updateField; + } + + public OptionalUpdateField ModifyValue(OptionalUpdateField updateField, uint dummy) where U : new() + { + _owner.m_entityFragments.ContentsChangedMask |= _owner.m_entityFragments.GetUpdateMaskFor((EntityFragment)updateField.BlockBit); + if ((EntityFragment)updateField.BlockBit == EntityFragment.CGObject) + _changesMask.Set(updateField.Bit); + + if (!updateField.HasValue()) + updateField.SetValue(new U()); + + return updateField; + } + + void ClearChangesMask(UpdateField updateField) where U : new() + { + _owner.m_entityFragments.ContentsChangedMask &= (byte)~_owner.m_entityFragments.GetUpdateMaskFor((EntityFragment)updateField.BlockBit); + if ((EntityFragment)updateField.BlockBit == EntityFragment.CGObject) + _changesMask.Reset(updateField.Bit); + + if (typeof(IHasChangesMask).IsAssignableFrom(typeof(U))) + ((IHasChangesMask)updateField._value).ClearChangesMask(); + } + + void ClearChangesMask(OptionalUpdateField updateField) where U : new() + { + _owner.m_entityFragments.ContentsChangedMask &= (byte)~_owner.m_entityFragments.GetUpdateMaskFor((EntityFragment)updateField.BlockBit); + if ((EntityFragment)updateField.BlockBit == EntityFragment.CGObject) + _changesMask.Reset(updateField.Bit); + + if (typeof(IHasChangesMask).IsAssignableFrom(typeof(U))) + ((IHasChangesMask)updateField._value).ClearChangesMask(); + } } } \ No newline at end of file diff --git a/Source/Game/Entities/Player/Player.DB.cs b/Source/Game/Entities/Player/Player.DB.cs index 13a78d786..67552f91a 100644 --- a/Source/Game/Entities/Player/Player.DB.cs +++ b/Source/Game/Entities/Player/Player.DB.cs @@ -3802,8 +3802,8 @@ namespace Game.Entities stmt.AddValue(index++, GetLootSpecId()); ss.Clear(); - for (int i = 0; i < m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Size(); ++i) - ss.Append($"{(uint)(m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex][i] & 0xFFFFFFFF)} {(uint)((m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex][i] >> 32) & 0xFFFFFFFF)} "); + for (int i = 0; i < m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values.Size(); ++i) + ss.Append($"{(uint)(m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values[i] & 0xFFFFFFFF)} {(uint)((m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values[i] >> 32) & 0xFFFFFFFF)} "); stmt.AddValue(index++, ss.ToString()); @@ -3958,8 +3958,8 @@ namespace Game.Entities stmt.AddValue(index++, GetLootSpecId()); ss.Clear(); - for (int i = 0; i < m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Size(); ++i) - ss.Append($"{(uint)(m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex][i] & 0xFFFFFFFF)} {(uint)((m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex][i] >> 32) & 0xFFFFFFFF)} "); + for (int i = 0; i < m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values.Size(); ++i) + ss.Append($"{(uint)(m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values[i] & 0xFFFFFFFF)} {(uint)((m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values[i] >> 32) & 0xFFFFFFFF)} "); stmt.AddValue(index++, ss.ToString()); diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 571e533ba..6fb6e9e43 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -6485,8 +6485,8 @@ namespace Game.Entities int offset = (areaEntry.AreaBit / PlayerConst.ExploredZonesBits); ulong val = 1ul << (areaEntry.AreaBit % PlayerConst.ExploredZonesBits); - if (offset >= m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Size() - || (m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex][offset] & val) == 0) + if (offset >= m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values.Size() + || (m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values[offset] & val) == 0) { AddExploredZones(offset, val); @@ -6540,13 +6540,15 @@ namespace Game.Entities public void AddExploredZones(int pos, ulong mask) { BitVectors bitVectors = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.BitVectors); - SetUpdateFieldFlagValue(bitVectors.ModifyValue(bitVectors.Values, (int)PlayerDataFlag.ExploredZonesIndex, pos), mask); + BitVector bitVector = bitVectors.ModifyValue(bitVectors.Values, (int)PlayerDataFlag.ExploredZonesIndex); + SetUpdateFieldFlagValue(bitVector.ModifyValue(bitVector.Values, pos), mask); } public void RemoveExploredZones(int pos, ulong mask) { BitVectors bitVectors = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.BitVectors); - RemoveUpdateFieldFlagValue(bitVectors.ModifyValue(bitVectors.Values, (int)PlayerDataFlag.ExploredZonesIndex, pos), mask); + BitVector bitVector = bitVectors.ModifyValue(bitVectors.Values, (int)PlayerDataFlag.ExploredZonesIndex); + RemoveUpdateFieldFlagValue(bitVector.ModifyValue(bitVector.Values, pos), mask); } public bool HasExploredZone(uint areaId) @@ -6559,11 +6561,11 @@ namespace Game.Entities return false; int playerIndexOffset = area.AreaBit / PlayerConst.ExploredZonesBits; - if (playerIndexOffset >= m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Size()) + if (playerIndexOffset >= m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values.Size()) return false; ulong mask = 1ul << (area.AreaBit % PlayerConst.ExploredZonesBits); - return (m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex][playerIndexOffset] & mask) != 0; + return (m_activePlayerData.BitVectors.GetValue().Values[(int)PlayerDataFlag.ExploredZonesIndex].Values[playerIndexOffset] & mask) != 0; } void SendExplorationExperience(uint Area, uint Experience) @@ -7976,6 +7978,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.ActivePlayer); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/SceneObject.cs b/Source/Game/Entities/SceneObject.cs index 05f2da50a..40b5529fa 100644 --- a/Source/Game/Entities/SceneObject.cs +++ b/Source/Game/Entities/SceneObject.cs @@ -25,6 +25,8 @@ namespace Game.Entities m_updateFlag.Stationary = true; m_updateFlag.SceneObject = true; + m_entityFragments.Add(EntityFragment.Tag_SceneObject, false); + m_sceneObjectData = new(); _stationaryPosition = new(); } @@ -139,6 +141,7 @@ namespace Game.Entities void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedSceneObjectMask, Player target) { + UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); UpdateMask valuesMask = new((int)TypeId.Max); if (requestedObjectMask.IsAnySet()) valuesMask.Set((int)TypeId.Object); @@ -147,6 +150,7 @@ namespace Game.Entities valuesMask.Set((int)TypeId.SceneObject); WorldPacket buffer = new(); + BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags); buffer.WriteUInt32(valuesMask.GetBlock(0)); if (valuesMask[(int)TypeId.Object]) diff --git a/Source/Game/Entities/Transport.cs b/Source/Game/Entities/Transport.cs index 943da30c7..058daeeb4 100644 --- a/Source/Game/Entities/Transport.cs +++ b/Source/Game/Entities/Transport.cs @@ -114,6 +114,7 @@ namespace Game.Entities m_updateFlag.ServerTime = true; m_updateFlag.Stationary = true; m_updateFlag.Rotation = true; + m_updateFlag.GameObject = true; } public override void Dispose() @@ -164,7 +165,6 @@ namespace Game.Entities } _pathProgress = goinfo.MoTransport.allowstopping == 0 ? Time.GetMSTime() /*might be called before world update loop begins, don't use GameTime*/ % tInfo.TotalPathTime : 0; - SetPathProgressForClient((float)_pathProgress / (float)tInfo.TotalPathTime); SetObjectScale(goinfo.size); SetPeriod(tInfo.TotalPathTime); SetEntry(goinfo.entry); @@ -228,8 +228,6 @@ namespace Game.Entities _eventsToTrigger.SetAll(true); } - SetPathProgressForClient((float)_pathProgress / (float)GetTransportPeriod()); - uint timer = _pathProgress % GetTransportPeriod(); int eventToTriggerIndex = -1; @@ -760,7 +758,7 @@ namespace Game.Entities { return _transportInfo.PathLegs[_currentPathLeg].MapId; } - + public HashSet GetPassengers() { return _passengers; } public ObjectGuid GetTransportGUID() { return GetGUID(); } @@ -769,6 +767,11 @@ namespace Game.Entities public uint GetTransportPeriod() { return m_gameObjectData.Level; } public void SetPeriod(uint period) { SetLevel(period); } public uint GetTimer() { return _pathProgress; } + public bool IsStopRequested() { return _requestStopTimestamp.HasValue; } + public bool IsStopped() + { + return HasDynamicFlag(GameObjectDynamicLowFlags.Stopped); + } TransportTemplate _transportInfo; diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index ccfcf32b3..a5abffef0 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -34,6 +34,8 @@ namespace Game.Entities ObjectTypeMask |= TypeMask.Unit; m_updateFlag.MovementUpdate = true; + m_entityFragments.Add(EntityFragment.Tag_Unit, false); + m_modAttackSpeedPct = new float[] { 1.0f, 1.0f, 1.0f }; m_deathState = DeathState.Alive; diff --git a/Source/Game/Networking/Packets/UpdatePackets.cs b/Source/Game/Networking/Packets/UpdatePackets.cs index 4adac3387..24569467c 100644 --- a/Source/Game/Networking/Packets/UpdatePackets.cs +++ b/Source/Game/Networking/Packets/UpdatePackets.cs @@ -11,8 +11,9 @@ namespace Game.Networking.Packets public override void Write() { - _worldPacket.WriteUInt32(NumObjUpdates); _worldPacket.WriteUInt16(MapID); + _worldPacket.WriteUInt32(NumObjUpdates); + _worldPacket.WriteBit(true); // unk _worldPacket.WriteBytes(Data); } diff --git a/Source/Scripts/World/NpcsSpecial.cs b/Source/Scripts/World/NpcsSpecial.cs index 9c94bae5c..2d1cb94b6 100644 --- a/Source/Scripts/World/NpcsSpecial.cs +++ b/Source/Scripts/World/NpcsSpecial.cs @@ -1717,12 +1717,14 @@ namespace Scripts.World.NpcsSpecial if (owner.HasAchieved(ArgentSquireIds.AchievementPonyUp) && !me.HasAura(ArgentSquireIds.AuraTiredS) && !me.HasAura(ArgentSquireIds.AuraTiredG)) { - me.SetNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox | NPCFlags.Vendor); + me.SetVendor(NPCFlags.Vendor, true); + me.SetNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox); return; } } - me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox | NPCFlags.Vendor); + me.SetVendor(NPCFlags.VendorMask, false); + me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Mailbox); } public override bool OnGossipSelect(Player player, uint menuId, uint gossipListId) @@ -1731,7 +1733,8 @@ namespace Scripts.World.NpcsSpecial { case ArgentSquireIds.GossipOptionBank: { - me.RemoveNpcFlag(NPCFlags.Mailbox | NPCFlags.Vendor); + me.SetVendor(NPCFlags.VendorMask, false); + me.RemoveNpcFlag(NPCFlags.Mailbox); uint _bankAura = IsArgentSquire() ? ArgentSquireIds.AuraBankS : ArgentSquireIds.AuraBankG; if (!me.HasAura(_bankAura)) DoCastSelf(_bankAura); @@ -1753,7 +1756,8 @@ namespace Scripts.World.NpcsSpecial } case ArgentSquireIds.GossipOptionMail: { - me.RemoveNpcFlag(NPCFlags.Banker | NPCFlags.Vendor); + me.SetVendor(NPCFlags.VendorMask, false); + me.RemoveNpcFlag(NPCFlags.Banker); uint _mailAura = IsArgentSquire() ? ArgentSquireIds.AuraPostmanS : ArgentSquireIds.AuraPostmanG; if (!me.HasAura(_mailAura)) DoCastSelf(_mailAura);