diff --git a/Source/Framework/Constants/GameObjectConst.cs b/Source/Framework/Constants/GameObjectConst.cs index cc4bd6409..b69800985 100644 --- a/Source/Framework/Constants/GameObjectConst.cs +++ b/Source/Framework/Constants/GameObjectConst.cs @@ -13,7 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - */ + */ namespace Framework.Constants { @@ -149,4 +149,54 @@ namespace Framework.Constants TimedOrCorpseDespawn = 0, // despawns after a specified time OR when the summoner dies TimedDespawn = 1 // despawns after a specified time } + + public enum GameObjectActions + { + // Name from client executable // Comments + None = 0, // -NONE- + AnimateCustom0 = 1, // Animate Custom0 + AnimateCustom1 = 2, // Animate Custom1 + AnimateCustom2 = 3, // Animate Custom2 + AnimateCustom3 = 4, // Animate Custom3 + Disturb = 5, // Disturb // Triggers trap + Unlock = 6, // Unlock // Resets GO_FLAG_LOCKED + Lock = 7, // Lock // Sets GO_FLAG_LOCKED + Open = 8, // Open // Sets GO_STATE_ACTIVE + OpenAndUnlock = 9, // Open + Unlock // Sets GO_STATE_ACTIVE and resets GO_FLAG_LOCKED + Close = 10, // Close // Sets GO_STATE_READY + ToggleOpen = 11, // Toggle Open + Destroy = 12, // Destroy // Sets GO_STATE_DESTROYED + Rebuild = 13, // Rebuild // Resets from GO_STATE_DESTROYED + Creation = 14, // Creation + Despawn = 15, // Despawn + MakeInert = 16, // Make Inert // Disables interactions + MakeActive = 17, // Make Active // Enables interactions + CloseAndLock = 18, // Close + Lock // Sets GO_STATE_READY and sets GO_FLAG_LOCKED + UseArtKit0 = 19, // Use ArtKit0 // 46904: 121 + UseArtKit1 = 20, // Use ArtKit1 // 36639: 81, 46903: 122 + UseArtKit2 = 21, // Use ArtKit2 + UseArtKit3 = 22, // Use ArtKit3 + SetTapList = 23, // Set Tap List + GoTo1stFloor = 24, // Go to 1st floor + GoTo2ndFloor = 25, // Go to 2nd floor + GoTo3rdFloor = 26, // Go to 3rd floor + GoTo4thFloor = 27, // Go to 4th floor + GoTo5thFloor = 28, // Go to 5th floor + GoTo6thFloor = 29, // Go to 6th floor + GoTo7thFloor = 30, // Go to 7th floor + GoTo8thFloor = 31, // Go to 8th floor + GoTo9thFloor = 32, // Go to 9th floor + GoTo10thFloor = 33, // Go to 10th floor + UseArtKit4 = 34, // Use ArtKit4 + PlayAnimKit = 35, // Play Anim Kit "{AnimKit}" + OpenAndPlayAnimKit = 36, // Open + Play Anim Kit "{AnimKit}" + CloseAndPlayAnimKit = 37, // Close + Play Anim Kit "{AnimKit}" + PlayOneShotAnimKit = 38, // Play One-shot Anim Kit "{AnimKit}" + StopAnimKit = 39, // Stop Anim Kit + OpenAndStopAnimKit = 40, // Open + Stop Anim Kit + CloseAndStopAnimKit = 41, // Close + Stop Anim Kit + PlaySpellVisual = 42, // Play Spell Visual "{SpellVisual}" + StopSpellVisual = 43, // Stop Spell Visual + SetTappedToChallengePlayers = 44, // Set Tapped to Challenge Players + } } diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs index 47be63cc4..cb1cc2b51 100644 --- a/Source/Framework/Database/Databases/HotfixDatabase.cs +++ b/Source/Framework/Database/Databases/HotfixDatabase.cs @@ -422,6 +422,8 @@ namespace Framework.Database PrepareStatement(HotfixStatements.SEL_FRIENDSHIP_REPUTATION, "SELECT Description, StandingModified, StandingChanged, ID, FactionID, TextureFileID, Flags FROM friendship_reputation"); PrepareStatement(HotfixStatements.SEL_FRIENDSHIP_REPUTATION_LOCALE, "SELECT ID, Description_lang, StandingModified_lang, StandingChanged_lang FROM friendship_reputation_locale WHERE locale = ?"); + // GameobjectArtKit.db2 + PrepareStatement(HotfixStatements.SEL_GAMEOBJECT_ART_KIT, "SELECT ID, AttachModelFileID, TextureVariationFileID1, TextureVariationFileID2, TextureVariationFileID3 FROM gameobject_art_kit"); // GameobjectDisplayInfo.db2 PrepareStatement(HotfixStatements.SEL_GAMEOBJECT_DISPLAY_INFO, "SELECT ID, GeoBoxMinX, GeoBoxMinY, GeoBoxMinZ, GeoBoxMaxX, GeoBoxMaxY, GeoBoxMaxZ, " + @@ -1495,6 +1497,8 @@ namespace Framework.Database SEL_FRIENDSHIP_REPUTATION, SEL_FRIENDSHIP_REPUTATION_LOCALE, + SEL_GAMEOBJECT_ART_KIT, + SEL_GAMEOBJECT_DISPLAY_INFO, SEL_GAMEOBJECTS, diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index 531e5e6e9..8d7616934 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -143,6 +143,7 @@ namespace Game.DataStorage FactionTemplateStorage = ReadDB2("FactionTemplate.db2", HotfixStatements.SEL_FACTION_TEMPLATE); FriendshipRepReactionStorage = ReadDB2("FriendshipRepReaction.db2", HotfixStatements.SEL_FRIENDSHIP_REP_REACTION, HotfixStatements.SEL_FRIENDSHIP_REP_REACTION_LOCALE); FriendshipReputationStorage = ReadDB2("FriendshipReputation.db2", HotfixStatements.SEL_FRIENDSHIP_REPUTATION, HotfixStatements.SEL_FRIENDSHIP_REPUTATION_LOCALE); + GameObjectArtKitStorage = ReadDB2("GameObjectArtKit.db2", HotfixStatements.SEL_GAMEOBJECT_ART_KIT); GameObjectDisplayInfoStorage = ReadDB2("GameObjectDisplayInfo.db2", HotfixStatements.SEL_GAMEOBJECT_DISPLAY_INFO); GameObjectsStorage = ReadDB2("GameObjects.db2", HotfixStatements.SEL_GAMEOBJECTS, HotfixStatements.SEL_GAMEOBJECTS_LOCALE); GarrAbilityStorage = ReadDB2("GarrAbility.db2", HotfixStatements.SEL_GARR_ABILITY, HotfixStatements.SEL_GARR_ABILITY_LOCALE); @@ -533,8 +534,9 @@ namespace Game.DataStorage public static DB6Storage FactionTemplateStorage; public static DB6Storage FriendshipRepReactionStorage; public static DB6Storage FriendshipReputationStorage; - public static DB6Storage GameObjectsStorage; + public static DB6Storage GameObjectArtKitStorage; public static DB6Storage GameObjectDisplayInfoStorage; + public static DB6Storage GameObjectsStorage; public static DB6Storage GarrAbilityStorage; public static DB6Storage GarrBuildingStorage; public static DB6Storage GarrBuildingPlotInstStorage; diff --git a/Source/Game/DataStorage/Structs/G_Records.cs b/Source/Game/DataStorage/Structs/G_Records.cs index 28655096a..f2353091e 100644 --- a/Source/Game/DataStorage/Structs/G_Records.cs +++ b/Source/Game/DataStorage/Structs/G_Records.cs @@ -20,6 +20,13 @@ using System.Numerics; namespace Game.DataStorage { + public sealed class GameObjectArtKitRecord + { + public uint Id; + public int AttachModelFileID; + public int[] TextureVariationFileID = new int[3]; + } + public sealed class GameObjectDisplayInfoRecord { public uint Id; diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index 9ceaff7a2..a44149385 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -2880,7 +2880,7 @@ namespace Game.Entities SendMessageToSet(activateAnimKit, true); } - void SetSpellVisualId(uint spellVisualId, ObjectGuid activatorGuid = default) + public void SetSpellVisualId(uint spellVisualId, ObjectGuid activatorGuid = default) { SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.SpellVisualID), spellVisualId); diff --git a/Source/Game/Entities/GameObject/GameObjectData.cs b/Source/Game/Entities/GameObject/GameObjectData.cs index 30550f846..6dcdd5896 100644 --- a/Source/Game/Entities/GameObject/GameObjectData.cs +++ b/Source/Game/Entities/GameObject/GameObjectData.cs @@ -1410,6 +1410,7 @@ namespace Game.Entities { public uint Mingold; public uint Maxgold; + public uint[] ArtKits = new uint[5]; public uint WorldEffectID; public uint AIAnimKitID; } diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index ee952ee03..0090f67bc 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -4072,8 +4072,8 @@ namespace Game { uint oldMSTime = Time.GetMSTime(); - // 0 1 2 3 4 5 6 - SQLResult result = DB.World.Query("SELECT entry, faction, flags, mingold, maxgold, WorldEffectID, AIAnimKitID FROM gameobject_template_addon"); + // 0 1 2 3 4 5 6 7 8 9 10 11 + SQLResult result = DB.World.Query("SELECT entry, faction, flags, mingold, maxgold, artkit0, artkit1, artkit2, artkit3, artkit4, WorldEffectID, AIAnimKitID FROM gameobject_template_addon"); if (result.IsEmpty()) { Log.outInfo(LogFilter.ServerLoading, "Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty."); @@ -4097,8 +4097,23 @@ namespace Game gameObjectAddon.Flags = (GameObjectFlags)result.Read(2); gameObjectAddon.Mingold = result.Read(3); gameObjectAddon.Maxgold = result.Read(4); - gameObjectAddon.WorldEffectID = result.Read(5); - gameObjectAddon.AIAnimKitID = result.Read(6); + gameObjectAddon.WorldEffectID = result.Read(10); + gameObjectAddon.AIAnimKitID = result.Read(11); + + for (int i = 0; i < gameObjectAddon.ArtKits.Length; ++i) + { + uint artKitID = result.Read(5 + i); + if (artKitID == 0) + continue; + + if (!CliDB.GameObjectArtKitStorage.ContainsKey(artKitID)) + { + Log.outError(LogFilter.Sql, $"GameObject (Entry: {entry}) has invalid `artkit{i}` ({artKitID}) defined, set to zero instead."); + continue; + } + + gameObjectAddon.ArtKits[i] = artKitID; + } // checks if (gameObjectAddon.Faction != 0 && !CliDB.FactionTemplateStorage.ContainsKey(gameObjectAddon.Faction)) diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 330f087a4..0fef413f7 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -3020,12 +3020,144 @@ namespace Game.Spells if (gameObjTarget == null) return; - ScriptInfo activateCommand = new(); - activateCommand.command = ScriptCommands.ActivateObject; + GameObjectActions action = (GameObjectActions)effectInfo.MiscValue; - // int unk = effectInfo.MiscValue; // This is set for EffectActivateObject spells; needs research + switch (action) + { + case GameObjectActions.AnimateCustom0: + case GameObjectActions.AnimateCustom1: + case GameObjectActions.AnimateCustom2: + case GameObjectActions.AnimateCustom3: + gameObjTarget.SendCustomAnim((uint)(action - GameObjectActions.AnimateCustom0)); + break; + case GameObjectActions.Disturb: // What's the difference with Open? + case GameObjectActions.Open: + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + gameObjTarget.Use(unitCaster); + break; + } + case GameObjectActions.OpenAndUnlock: + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + gameObjTarget.UseDoorOrButton(0, false, unitCaster); - gameObjTarget.GetMap().ScriptCommandStart(activateCommand, 0, m_caster, gameObjTarget); + gameObjTarget.RemoveFlag(GameObjectFlags.Locked); + break; + } + case GameObjectActions.Unlock: + gameObjTarget.RemoveFlag(GameObjectFlags.Locked); + break; + case GameObjectActions.Lock: + gameObjTarget.AddFlag(GameObjectFlags.Locked); + break; + case GameObjectActions.Close: + case GameObjectActions.Rebuild: + gameObjTarget.ResetDoorOrButton(); + break; + case GameObjectActions.Despawn: + gameObjTarget.DespawnOrUnsummon(); + break; + case GameObjectActions.MakeInert: + gameObjTarget.AddFlag(GameObjectFlags.NotSelectable); + break; + case GameObjectActions.MakeActive: + gameObjTarget.RemoveFlag(GameObjectFlags.NotSelectable); + break; + case GameObjectActions.CloseAndLock: + gameObjTarget.ResetDoorOrButton(); + gameObjTarget.AddFlag(GameObjectFlags.Locked); + break; + case GameObjectActions.Destroy: + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + gameObjTarget.UseDoorOrButton(0, true, unitCaster); + break; + } + case GameObjectActions.UseArtKit0: + case GameObjectActions.UseArtKit1: + case GameObjectActions.UseArtKit2: + case GameObjectActions.UseArtKit3: + case GameObjectActions.UseArtKit4: + { + GameObjectTemplateAddon templateAddon = gameObjTarget.GetTemplateAddon(); + + uint artKitIndex = action != GameObjectActions.UseArtKit4 ? (uint)(action - GameObjectActions.UseArtKit0) : 4; + uint artKitValue = 0; + if (templateAddon != null) + artKitValue = templateAddon.ArtKits[artKitIndex]; + + if (artKitValue == 0) + Log.outError(LogFilter.Sql, $"GameObject {gameObjTarget.GetEntry()} hit by spell {m_spellInfo.Id} needs `artkit{artKitIndex}` in `gameobject_template_addon`"); + else + gameObjTarget.SetGoArtKit((byte)artKitValue); + + break; + } + case GameObjectActions.GoTo1stFloor: + case GameObjectActions.GoTo2ndFloor: + case GameObjectActions.GoTo3rdFloor: + case GameObjectActions.GoTo4thFloor: + case GameObjectActions.GoTo5thFloor: + case GameObjectActions.GoTo6thFloor: + case GameObjectActions.GoTo7thFloor: + case GameObjectActions.GoTo8thFloor: + case GameObjectActions.GoTo9thFloor: + case GameObjectActions.GoTo10thFloor: + if (gameObjTarget.GetGoType() == GameObjectTypes.Transport) + gameObjTarget.SetTransportState(GameObjectState.TransportStopped, (uint)(action - GameObjectActions.GoTo1stFloor)); + else + Log.outError(LogFilter.Spells, $"Spell {m_spellInfo.Id} targeted non-transport gameobject for transport only action \"Go to Floor\" {action} in effect {effectInfo.EffectIndex}"); + break; + case GameObjectActions.PlayAnimKit: + gameObjTarget.SetAnimKitId((ushort)effectInfo.MiscValueB, false); + break; + case GameObjectActions.OpenAndPlayAnimKit: + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + gameObjTarget.UseDoorOrButton(0, false, unitCaster); + gameObjTarget.SetAnimKitId((ushort)effectInfo.MiscValueB, false); + break; + } + case GameObjectActions.CloseAndPlayAnimKit: + gameObjTarget.ResetDoorOrButton(); + gameObjTarget.SetAnimKitId((ushort)effectInfo.MiscValueB, false); + break; + case GameObjectActions.PlayOneShotAnimKit: + gameObjTarget.SetAnimKitId((ushort)effectInfo.MiscValueB, true); + break; + case GameObjectActions.StopAnimKit: + gameObjTarget.SetAnimKitId(0, false); + break; + case GameObjectActions.OpenAndStopAnimKit: + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster != null) + gameObjTarget.UseDoorOrButton(0, false, unitCaster); + gameObjTarget.SetAnimKitId(0, false); + break; + } + case GameObjectActions.CloseAndStopAnimKit: + gameObjTarget.ResetDoorOrButton(); + gameObjTarget.SetAnimKitId(0, false); + break; + case GameObjectActions.PlaySpellVisual: + gameObjTarget.SetSpellVisualId((ushort)effectInfo.MiscValueB, m_originalCasterGUID); + break; + case GameObjectActions.StopSpellVisual: + gameObjTarget.SetSpellVisualId(0); + break; + case GameObjectActions.None: + Log.outFatal(LogFilter.Spells, $"Spell {m_spellInfo.Id} has action type NONE in effect {effectInfo.EffectIndex}"); + break; + default: + Log.outFatal(LogFilter.Spells, $"Spell {m_spellInfo.Id} has unhandled action {action} in effect {effectInfo.EffectIndex}"); + break; + } } [SpellEffectHandler(SpellEffectName.ApplyGlyph)]