diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs index eb1759369..22580514e 100644 --- a/Source/Framework/Database/Databases/HotfixDatabase.cs +++ b/Source/Framework/Database/Databases/HotfixDatabase.cs @@ -177,6 +177,12 @@ namespace Framework.Database PrepareStatement(HotfixStatements.SEL_BARBER_SHOP_STYLE_LOCALE, "SELECT ID, DisplayName_lang, Description_lang FROM barber_shop_style_locale" + " WHERE (`VerifiedBuild` > 0) = ? AND locale = ?"); + // BattlePetAbility.db2 + PrepareStatement(HotfixStatements.SEL_BATTLE_PET_ABILITY, "SELECT ID, Name, Description, IconFileDataID, PetTypeEnum, Cooldown, BattlePetVisualID, Flags" + + " FROM battle_pet_ability WHERE (`VerifiedBuild` > 0) = ?"); + PrepareStatement(HotfixStatements.SEL_BATTLE_PET_ABILITY_LOCALE, "SELECT ID, Name_lang, Description_lang FROM battle_pet_ability_locale" + + " WHERE (`VerifiedBuild` > 0) = ? AND locale = ?"); + // BattlePetBreedQuality.db2 PrepareStatement(HotfixStatements.SEL_BATTLE_PET_BREED_QUALITY, "SELECT ID, MaxQualityRoll, StateMultiplier, QualityEnum FROM battle_pet_breed_quality" + " WHERE (`VerifiedBuild` > 0) = ?"); @@ -1026,6 +1032,12 @@ namespace Framework.Database // PathProperty.db2 PrepareStatement(HotfixStatements.SEL_PATH_PROPERTY, "SELECT ID, PathID, PropertyIndex, Value FROM path_property WHERE (`VerifiedBuild` > 0) = ?"); + // PerksActivity.db2 + PrepareStatement(HotfixStatements.SEL_PERKS_ACTIVITY, "SELECT ActivityName, Description, ID, CriteriaTreeID, ThresholdContributionAmount, Supersedes, " + + "Priority FROM perks_activity WHERE (`VerifiedBuild` > 0) = ?"); + PrepareStatement(HotfixStatements.SEL_PERKS_ACTIVITY_LOCALE, "SELECT ID, ActivityName_lang, Description_lang FROM perks_activity_locale" + + " WHERE (`VerifiedBuild` > 0) = ? AND locale = ?"); + // Phase.db2 PrepareStatement(HotfixStatements.SEL_PHASE, "SELECT ID, Flags FROM phase WHERE (`VerifiedBuild` > 0) = ?"); @@ -1769,6 +1781,9 @@ namespace Framework.Database SEL_BARBER_SHOP_STYLE, SEL_BARBER_SHOP_STYLE_LOCALE, + SEL_BATTLE_PET_ABILITY, + SEL_BATTLE_PET_ABILITY_LOCALE, + SEL_BATTLE_PET_BREED_QUALITY, SEL_BATTLE_PET_BREED_STATE, @@ -2169,6 +2184,9 @@ namespace Framework.Database SEL_PATH_PROPERTY, + SEL_PERKS_ACTIVITY, + SEL_PERKS_ACTIVITY_LOCALE, + SEL_PHASE, SEL_PHASE_X_PHASE_GROUP, diff --git a/Source/Game/Chat/CommandArgs.cs b/Source/Game/Chat/CommandArgs.cs index a74e9d10f..7679adfd7 100644 --- a/Source/Game/Chat/CommandArgs.cs +++ b/Source/Game/Chat/CommandArgs.cs @@ -26,13 +26,13 @@ namespace Game.Chat static ChatCommandResult TryConsumeTo(dynamic[] tuple, int offset, ParameterInfo[] parameterInfos, CommandHandler handler, string args) { - var optionalArgAttribute = parameterInfos[offset].GetCustomAttribute(true); - if (optionalArgAttribute != null || parameterInfos[offset].ParameterType.IsGenericType && parameterInfos[offset].ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>)) + if (parameterInfos[offset].ParameterType.IsGenericType && parameterInfos[offset].ParameterType.GetGenericTypeDefinition() == typeof(OptionalArg<>)) { // try with the argument - Type myArg = Nullable.GetUnderlyingType(parameterInfos[offset].ParameterType) ?? parameterInfos[offset].ParameterType; + Type myArg = parameterInfos[offset].ParameterType.GetGenericArguments()?[0]; - ChatCommandResult result1 = TryConsume(out tuple[offset], myArg, handler, args); + ChatCommandResult result1 = TryConsume(out dynamic tempValue, myArg, handler, args); + tuple[offset] = Activator.CreateInstance(typeof(OptionalArg<>).MakeGenericType(myArg), [tempValue]); if (result1.IsSuccessful()) if ((result1 = ConsumeFromOffset(tuple, offset + 1, parameterInfos, handler, result1)).IsSuccessful()) return result1; @@ -55,9 +55,9 @@ namespace Game.Chat { ChatCommandResult next; - var variantArgAttribute = parameterInfos[offset].GetCustomAttribute(true); - if (variantArgAttribute != null) - next = TryConsumeVariant(out tuple[offset], variantArgAttribute.Types, handler, args); + var genericTypes = parameterInfos[offset].ParameterType.GetGenericArguments(); + if (!genericTypes.Empty()) + next = TryConsumeVariant(out tuple[offset], parameterInfos[offset].ParameterType, genericTypes, handler, args); else next = TryConsume(out tuple[offset], parameterInfos[offset].ParameterType, handler, args); @@ -238,54 +238,123 @@ namespace Game.Chat return val.TryConsume(handler, args); case nameof(AchievementRecord): { - ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args); + ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(AchievementLinkData), handler, args); + if (result.IsSuccessful() && (val = tempVal.Achievement) != null) + return result; + + result = TryConsume(out tempVal, typeof(uint), handler, args); if (!result.IsSuccessful() || (val = CliDB.AchievementStorage.LookupByKey((uint)tempVal)) != null) return result; - return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserAchievementNoExist, tempVal)); + + if (val is uint) + return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserAchievementNoExist, tempVal)); + + return result; } case nameof(CurrencyTypesRecord): { - ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args); + ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(CurrencyLinkData), handler, args); + if (result.IsSuccessful() && (val = tempVal.Currency) != null) + return result; + + result = TryConsume(out tempVal, typeof(uint), handler, args); if (!result.IsSuccessful() || (val = CliDB.CurrencyTypesStorage.LookupByKey((uint)tempVal)) != null) return result; - return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserCurrencyNoExist, tempVal)); + + if (val is uint) + return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserCurrencyNoExist, tempVal)); + + return result; } case nameof(GameTele): { - ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args); - if (!result.IsSuccessful()) - result = TryConsume(out tempVal, typeof(string), handler, args); + ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(TeleLinkData), handler, args); + if (result.IsSuccessful() && (val = Global.ObjectMgr.GetGameTele(tempVal)) != null) + return result; + result = TryConsume(out tempVal, typeof(string), handler, args); if (!result.IsSuccessful() || (val = Global.ObjectMgr.GetGameTele(tempVal)) != null) return result; - if (tempVal is uint) + + if (tempVal is not string) return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserGameTeleIdNoExist, tempVal)); else return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserGameTeleNoExist, tempVal)); } case nameof(ItemTemplate): { - ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args); + ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(ItemLinkData), handler, args); + if (result.IsSuccessful() && (val = tempVal.Item) != null) + return result; + + result = TryConsume(out tempVal, typeof(uint), handler, args); if (!result.IsSuccessful() || (val = Global.ObjectMgr.GetItemTemplate(tempVal)) != null) return result; - return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserItemNoExist, tempVal)); + if (val is uint) + return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserItemNoExist, tempVal)); + + return result; } case nameof(SpellInfo): { - ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args); - if (!result.IsSuccessful() || (val = Global.SpellMgr.GetSpellInfo(tempVal, Difficulty.None)) != null) + SpellInfo getSpellInfo(uint spellId) + { + return Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None); + } + + ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(ArtifactPowerLinkData), handler, args); + if (result.IsSuccessful() && (val = getSpellInfo(tempVal.ArtifactPower.SpellID)) != null) + return result; + + result = TryConsume(out tempVal, typeof(ConduitLinkData), handler, args); + if (result.IsSuccessful() && (val = getSpellInfo(tempVal.SpellID)) != null) + return result; + + result = TryConsume(out tempVal, typeof(EnchantLinkData), handler, args); + if (result.IsSuccessful() && (val = tempVal) != null) + return result; + + result = TryConsume(out tempVal, typeof(MawPowerLinkData), handler, args); + if (result.IsSuccessful() && (val = getSpellInfo(tempVal.SpellID)) != null) + return result; + + result = TryConsume(out tempVal, typeof(MountLinkData), handler, args); + if (result.IsSuccessful() && (val = tempVal.Spell) != null) + return result; + + result = TryConsume(out tempVal, typeof(PvpTalLinkData), handler, args); + if (result.IsSuccessful() && (val = getSpellInfo(tempVal.SpellID)) != null) + return result; + + result = TryConsume(out tempVal, typeof(SpellLinkData), handler, args); + if (result.IsSuccessful() && (val = tempVal.Spell) != null) + return result; + + result = TryConsume(out tempVal, typeof(TalentBuildLinkData), handler, args); + if (result.IsSuccessful() && (val = getSpellInfo(tempVal.SpellID)) != null) + return result; + + result = TryConsume(out tempVal, typeof(TradeskillLinkData), handler, args); + if (!result.IsSuccessful() || (val = tempVal.Spell) != null) return result; return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserSpellNoExist, tempVal)); } case nameof(Quest): { - ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args); + ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(QuestLinkData), handler, args); + if (result.IsSuccessful() && (val = tempVal.Quest) != null) + return result; + + result = TryConsume(out tempVal, typeof(uint), handler, args); if (!result.IsSuccessful() || (val = Global.ObjectMgr.GetQuestTemplate(tempVal)) != null) return result; - - return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserQuestNoExist, tempVal)); + + if (val is uint) + return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserQuestNoExist, tempVal)); + + return result; } } break; @@ -295,11 +364,16 @@ namespace Game.Chat return default; } - public static ChatCommandResult TryConsumeVariant(out dynamic val, Type[] types, CommandHandler handler, string args) + public static ChatCommandResult TryConsumeVariant(out dynamic val, Type type, Type[] types, CommandHandler handler, string args) { - ChatCommandResult result = TryAtIndex(out val, types, 0, handler, args); + val = default; + + ChatCommandResult result = TryAtIndex(out dynamic tempVal, types, 0, handler, args); if (result.HasErrorMessage() && (result.GetErrorMessage().IndexOf('\n') != -1)) return ChatCommandResult.FromErrorMessage($"{handler.GetCypherString(CypherStrings.CmdparserEither)} {result.GetErrorMessage()}"); + + val = Activator.CreateInstance(type); + val.Set(tempVal); return result; } @@ -314,7 +388,7 @@ namespace Game.Chat return thisResult; else { - ChatCommandResult nestedResult = TryAtIndex(out val, types, index+1, handler, args); + ChatCommandResult nestedResult = TryAtIndex(out val, types, index + 1, handler, args); if (nestedResult.IsSuccessful() || !thisResult.HasErrorMessage()) return nestedResult; if (!nestedResult.HasErrorMessage()) @@ -349,7 +423,7 @@ namespace Game.Chat public string GetErrorMessage() { return errorMessage; } - public void SetErrorMessage(string _value) + public void SetErrorMessage(string _value) { result = false; errorMessage = _value; diff --git a/Source/Game/Chat/CommandAttribute.cs b/Source/Game/Chat/CommandAttribute.cs index 0b3fa4931..ef8487ac8 100644 --- a/Source/Game/Chat/CommandAttribute.cs +++ b/Source/Game/Chat/CommandAttribute.cs @@ -64,40 +64,67 @@ namespace Game.Chat public CommandNonGroupAttribute(string command, RBACPermissions rbac, bool allowConsole = false) : base(command, rbac, allowConsole) { } } - [AttributeUsage(AttributeTargets.Parameter)] - public class OptionalArgAttribute : Attribute { } - - public class VariantArgAttribute : Attribute + public struct OptionalArg { - public Type[] Types { get; set; } + public T Value { get; set { field = value; HasValue = true; } } + public bool HasValue { get; private set; } - public VariantArgAttribute(params Type[] types) + public OptionalArg(T value) { - Types = types; + Value = value; } + + public T GetValueOrDefault(T defaultValue) => HasValue ? Value : defaultValue; + + public static implicit operator T(OptionalArg optionalArg) => optionalArg.Value; + public static implicit operator OptionalArg(T value) => new OptionalArg(value); } - [AttributeUsage(AttributeTargets.Parameter)] - public class VariantArg : VariantArgAttribute + public class VariantArg { - public VariantArg() : base(typeof(T1)) { } + dynamic value; + + public dynamic GetValue() { return value; } + + public bool Set(dynamic val) + { + return CheckAndSet(val); + } + + public bool Is() { return value is T; } + + internal bool CheckAndSet(dynamic val) + { + if (val is not T) + return false; + + value = val; + return true; + } + + public static implicit operator T1(VariantArg variant) => variant.GetValue(); } - [AttributeUsage(AttributeTargets.Parameter)] - public class VariantArg : VariantArgAttribute + public class VariantArg : VariantArg { - public VariantArg() : base(typeof(T1), typeof(T2)) { } + public new bool Set(dynamic val) + { + return CheckAndSet(val) || CheckAndSet(val); + } + + public static implicit operator T1(VariantArg variant) => variant.GetValue(); + public static implicit operator T2(VariantArg variant) => variant.Is() ? variant.GetValue() : default; } - [AttributeUsage(AttributeTargets.Parameter)] - public class VariantArg : VariantArgAttribute + public class VariantArg : VariantArg { - public VariantArg() : base(typeof(T1), typeof(T2), typeof(T3)) { } - } + public new bool Set(dynamic val) + { + return CheckAndSet(val) || CheckAndSet(val) || CheckAndSet(val); + } - [AttributeUsage(AttributeTargets.Parameter)] - public class VariantArg : VariantArgAttribute - { - public VariantArg() : base(typeof(T1), typeof(T2), typeof(T3), typeof(T4)) { } + public static implicit operator T1(VariantArg variant) => variant.GetValue(); + public static implicit operator T2(VariantArg variant) => variant.GetValue(); + public static implicit operator T3(VariantArg variant) => variant.GetValue(); } } diff --git a/Source/Game/Chat/CommandManager.cs b/Source/Game/Chat/CommandManager.cs index 1864b6ffb..5043884a6 100644 --- a/Source/Game/Chat/CommandManager.cs +++ b/Source/Game/Chat/CommandManager.cs @@ -399,7 +399,7 @@ namespace Game.Chat public bool Invoke(CommandHandler handler, string args) { if (parameters.Any(p => p.ParameterType == typeof(StringArguments)))//Old system, can remove once all commands are changed. - return (bool)_methodInfo.Invoke(null, new object[] { handler, new StringArguments(args) }); + return (bool)_methodInfo.Invoke(null, [handler, new StringArguments(args)]); else { var parseArgs = new dynamic[parameters.Length]; diff --git a/Source/Game/Chat/Commands/AccountCommands.cs b/Source/Game/Chat/Commands/AccountCommands.cs index 0541d9a5e..83cb0f432 100644 --- a/Source/Game/Chat/Commands/AccountCommands.cs +++ b/Source/Game/Chat/Commands/AccountCommands.cs @@ -55,7 +55,7 @@ namespace Game.Chat } [Command("2fa remove", CypherStrings.CommandAcc2faRemoveHelp, RBACPermissions.CommandAccount2FaRemove)] - static bool HandleAccount2FARemoveCommand(CommandHandler handler, uint? token) + static bool HandleAccount2FARemoveCommand(CommandHandler handler, OptionalArg token) { /*var masterKey = Global.SecretMgr.GetSecret(Secrets.TOTPMasterKey); if (!masterKey.IsAvailable()) @@ -118,7 +118,7 @@ namespace Game.Chat } [Command("2fa setup", CypherStrings.CommandAcc2faSetupHelp, RBACPermissions.CommandAccount2FaSetup)] - static bool HandleAccount2FASetupCommand(CommandHandler handler, uint? token) + static bool HandleAccount2FASetupCommand(CommandHandler handler, OptionalArg token) { /*var masterKey = Global.SecretMgr.GetSecret(Secrets.TOTPMasterKey); if (!masterKey.IsAvailable()) @@ -197,7 +197,7 @@ namespace Game.Chat } [Command("create", CypherStrings.CommandAccCreateHelp, RBACPermissions.CommandAccountCreate, true)] - static bool HandleAccountCreateCommand(CommandHandler handler, string accountName, string password, [OptionalArg] string email) + static bool HandleAccountCreateCommand(CommandHandler handler, string accountName, string password, OptionalArg email) { if (accountName.Contains("@")) { @@ -205,7 +205,7 @@ namespace Game.Chat return false; } - AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email ?? ""); + AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email.GetValueOrDefault("")); switch (result) { case AccountOpResult.Ok: @@ -215,7 +215,7 @@ namespace Game.Chat Log.outInfo(LogFilter.Player, "Account: {0} (IP: {1}) Character:[{2}] (GUID: {3}) created Account {4} (Email: '{5}')", handler.GetSession().GetAccountId(), handler.GetSession().GetRemoteAddress(), handler.GetSession().GetPlayer().GetName(), handler.GetSession().GetPlayer().GetGUID().ToString(), - accountName, email ?? ""); + accountName, email.GetValueOrDefault("")); } break; case AccountOpResult.NameTooLong: @@ -337,7 +337,7 @@ namespace Game.Chat } [Command("password", CypherStrings.CommandAccPasswordHelp, RBACPermissions.CommandAccountPassword)] - static bool HandleAccountPasswordCommand(CommandHandler handler, string oldPassword, string newPassword, string confirmPassword, [OptionalArg] string confirmEmail) + static bool HandleAccountPasswordCommand(CommandHandler handler, string oldPassword, string newPassword, string confirmPassword, OptionalArg confirmEmail) { // First, we check config. What security type (sec type) is it ? Depending on it, the command branches out uint pwConfig = WorldConfig.GetUIntValue(WorldCfg.AccPasschangesec); // 0 - PW_NONE, 1 - PW_EMAIL, 2 - PW_RBAC @@ -466,34 +466,34 @@ namespace Game.Chat [Command("", CypherStrings.CommandAccOnlinelistHelp, RBACPermissions.CommandAccountOnlineList, true)] static bool HandleAccountOnlineListCommand(CommandHandler handler) { - return HandleAccountOnlineListCommandWithParameters(handler, null, null, null, null); + return HandleAccountOnlineListCommandWithParameters(handler, null, default, default, default); } [Command("ip", CypherStrings.CommandAccOnlinelistHelp, RBACPermissions.CommandAccountOnlineList, true)] static bool HandleAccountOnlineListWithIpFilterCommand(CommandHandler handler, string ipAddress) { - return HandleAccountOnlineListCommandWithParameters(handler, ipAddress, null, null, null); + return HandleAccountOnlineListCommandWithParameters(handler, ipAddress, default, default, default); } [Command("limit", CypherStrings.CommandAccOnlinelistHelp, RBACPermissions.CommandAccountOnlineList, true)] static bool HandleAccountOnlineListWithLimitCommand(CommandHandler handler, uint limit) { - return HandleAccountOnlineListCommandWithParameters(handler, null, limit, null, null); + return HandleAccountOnlineListCommandWithParameters(handler, null, limit, default, default); } [Command("map", CypherStrings.CommandAccOnlinelistHelp, RBACPermissions.CommandAccountOnlineList, true)] static bool HandleAccountOnlineListWithMapFilterCommand(CommandHandler handler, uint mapId) { - return HandleAccountOnlineListCommandWithParameters(handler, null, null, mapId, null); + return HandleAccountOnlineListCommandWithParameters(handler, null, default, mapId, default); } [Command("zone", CypherStrings.CommandAccOnlinelistHelp, RBACPermissions.CommandAccountOnlineList, true)] static bool HandleAccountOnlineListWithZoneFilterCommand(CommandHandler handler, uint zoneId) { - return HandleAccountOnlineListCommandWithParameters(handler, null, null, null, zoneId); + return HandleAccountOnlineListCommandWithParameters(handler, null, default, default, zoneId); } - static bool HandleAccountOnlineListCommandWithParameters(CommandHandler handler, string ipAddress, uint? limit, uint? mapId, uint? zoneId) + static bool HandleAccountOnlineListCommandWithParameters(CommandHandler handler, string ipAddress, OptionalArg limit, OptionalArg mapId, OptionalArg zoneId) { int sessionsMatchCount = 0; @@ -614,13 +614,13 @@ namespace Game.Chat } [Command("addon", CypherStrings.CommandAccSetAddonHelp, RBACPermissions.CommandAccountSetAddon, true)] - static bool HandleAccountSetAddonCommand(CommandHandler handler, [OptionalArg] string accountName, byte expansion) + static bool HandleAccountSetAddonCommand(CommandHandler handler, OptionalArg accountName, byte expansion) { uint accountId; - if (!accountName.IsEmpty()) + if (accountName.HasValue) { // Convert Account name to Upper Format - accountName = accountName.ToUpper(); + accountName = accountName.Value.ToUpper(); accountId = Global.AccountMgr.GetId(accountName); if (accountId == 0) @@ -636,7 +636,8 @@ namespace Game.Chat return false; accountId = player.GetSession().GetAccountId(); - Global.AccountMgr.GetName(accountId, out accountName); + Global.AccountMgr.GetName(accountId, out string tempAccountName); + accountName = tempAccountName; } // Let set addon state only for lesser (strong) security level @@ -702,10 +703,10 @@ namespace Game.Chat [Command("seclevel", CypherStrings.CommandAccSetSeclevelHelp, RBACPermissions.CommandAccountSetSecLevel, true)] [Command("gmlevel", CypherStrings.CommandAccSetSeclevelHelp, RBACPermissions.CommandAccountSetSecLevel, true)] - static bool HandleAccountSetSecLevelCommand(CommandHandler handler, [OptionalArg] string accountName, byte securityLevel, int? realmId) + static bool HandleAccountSetSecLevelCommand(CommandHandler handler, OptionalArg accountName, byte securityLevel, OptionalArg realmId) { uint accountId; - if (!accountName.IsEmpty()) + if (accountName.HasValue) { accountId = Global.AccountMgr.GetId(accountName); if (accountId == 0) @@ -721,7 +722,8 @@ namespace Game.Chat return false; accountId = player.GetSession().GetAccountId(); - Global.AccountMgr.GetName(accountId, out accountName); + Global.AccountMgr.GetName(accountId, out string tempAccountName); + accountName = tempAccountName; } if (securityLevel > (uint)AccountTypes.Console) diff --git a/Source/Game/Chat/Commands/BNetAccountCommands.cs b/Source/Game/Chat/Commands/BNetAccountCommands.cs index d85854d74..5852dc2c7 100644 --- a/Source/Game/Chat/Commands/BNetAccountCommands.cs +++ b/Source/Game/Chat/Commands/BNetAccountCommands.cs @@ -12,7 +12,7 @@ namespace Game.Chat.Commands class BNetAccountCommands { [Command("create", RBACPermissions.CommandBnetAccountCreate, true)] - static bool HandleAccountCreateCommand(CommandHandler handler, string accountName, string password, bool? createGameAccount) + static bool HandleAccountCreateCommand(CommandHandler handler, string accountName, string password, OptionalArg createGameAccount) { if (accountName.IsEmpty() || !accountName.Contains('@')) { diff --git a/Source/Game/Chat/Commands/BanCommands.cs b/Source/Game/Chat/Commands/BanCommands.cs index 57624c949..344936696 100644 --- a/Source/Game/Chat/Commands/BanCommands.cs +++ b/Source/Game/Chat/Commands/BanCommands.cs @@ -269,13 +269,13 @@ namespace Game.Chat.Commands class BanListCommands { [Command("account", RBACPermissions.CommandBanlistAccount, true)] - static bool HandleBanListAccountCommand(CommandHandler handler, [OptionalArg] string filter) + static bool HandleBanListAccountCommand(CommandHandler handler, OptionalArg filter) { PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.DEL_EXPIRED_IP_BANS); DB.Login.Execute(stmt); SQLResult result; - if (filter.IsEmpty()) + if (!filter.HasValue) { stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SEL_ACCOUNT_BANNED_ALL); result = DB.Login.Query(stmt); @@ -378,14 +378,14 @@ namespace Game.Chat.Commands } [Command("ip", RBACPermissions.CommandBanlistIp, true)] - static bool HandleBanListIPCommand(CommandHandler handler, [OptionalArg] string filter) + static bool HandleBanListIPCommand(CommandHandler handler, OptionalArg filter) { PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.DEL_EXPIRED_IP_BANS); DB.Login.Execute(stmt); SQLResult result; - if (filter.IsEmpty()) + if (!filter.HasValue) { stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SEL_IP_BANNED_ALL); result = DB.Login.Query(stmt); diff --git a/Source/Game/Chat/Commands/CastCommands.cs b/Source/Game/Chat/Commands/CastCommands.cs index a8319c738..a28f77612 100644 --- a/Source/Game/Chat/Commands/CastCommands.cs +++ b/Source/Game/Chat/Commands/CastCommands.cs @@ -13,7 +13,7 @@ namespace Game.Chat class CastCommands { [Command("", RBACPermissions.CommandCast)] - static bool HandleCastCommand(CommandHandler handler, uint spellId, [OptionalArg] string triggeredStr) + static bool HandleCastCommand(CommandHandler handler, uint spellId, OptionalArg triggeredStr) { Unit target = handler.GetSelectedUnit(); if (target == null) @@ -34,7 +34,7 @@ namespace Game.Chat } [Command("back", RBACPermissions.CommandCastBack)] - static bool HandleCastBackCommand(CommandHandler handler, uint spellId, [OptionalArg] string triggeredStr) + static bool HandleCastBackCommand(CommandHandler handler, uint spellId, OptionalArg triggeredStr) { Creature caster = handler.GetSelectedCreature(); if (caster == null) @@ -56,7 +56,7 @@ namespace Game.Chat } [Command("dist", RBACPermissions.CommandCastDist)] - static bool HandleCastDistCommand(CommandHandler handler, uint spellId, float dist, [OptionalArg] string triggeredStr) + static bool HandleCastDistCommand(CommandHandler handler, uint spellId, float dist, OptionalArg triggeredStr) { if (CheckSpellExistsAndIsValid(handler, spellId)) return false; @@ -74,7 +74,7 @@ namespace Game.Chat } [Command("self", RBACPermissions.CommandCastSelf)] - static bool HandleCastSelfCommand(CommandHandler handler, uint spellId, [OptionalArg] string triggeredStr) + static bool HandleCastSelfCommand(CommandHandler handler, uint spellId, OptionalArg triggeredStr) { Unit target = handler.GetSelectedUnit(); if (target == null) @@ -96,7 +96,7 @@ namespace Game.Chat } [Command("target", RBACPermissions.CommandCastTarget)] - static bool HandleCastTargetCommad(CommandHandler handler, uint spellId, [OptionalArg] string triggeredStr) + static bool HandleCastTargetCommad(CommandHandler handler, uint spellId, OptionalArg triggeredStr) { Creature caster = handler.GetSelectedCreature(); if (caster == null) @@ -124,7 +124,7 @@ namespace Game.Chat } [Command("dest", RBACPermissions.CommandCastDest)] - static bool HandleCastDestCommand(CommandHandler handler, uint spellId, float x, float y, float z, [OptionalArg] string triggeredStr) + static bool HandleCastDestCommand(CommandHandler handler, uint spellId, float x, float y, float z, OptionalArg triggeredStr) { Unit caster = handler.GetSelectedUnit(); if (caster == null) diff --git a/Source/Game/Chat/Commands/CharacterCommands.cs b/Source/Game/Chat/Commands/CharacterCommands.cs index 516d2382f..250319a41 100644 --- a/Source/Game/Chat/Commands/CharacterCommands.cs +++ b/Source/Game/Chat/Commands/CharacterCommands.cs @@ -63,9 +63,9 @@ namespace Game.Chat //rename characters [Command("rename", RBACPermissions.CommandCharacterRename, true)] - static bool HandleCharacterRenameCommand(CommandHandler handler, PlayerIdentifier player, [OptionalArg] string newName) + static bool HandleCharacterRenameCommand(CommandHandler handler, PlayerIdentifier player, OptionalArg newNameV) { - if (player == null && !newName.IsEmpty()) + if (player == null && !newNameV.HasValue) return false; if (player == null) @@ -77,8 +77,9 @@ namespace Game.Chat if (handler.HasLowerSecurity(null, player.GetGUID())) return false; - if (!newName.IsEmpty()) + if (newNameV.HasValue) { + string newName = newNameV; if (!ObjectManager.NormalizePlayerName(ref newName)) { handler.SendSysMessage(CypherStrings.BadValue); @@ -455,7 +456,7 @@ namespace Game.Chat } [Command("list", RBACPermissions.CommandCharacterDeletedList, true)] - static bool HandleCharacterDeletedListCommand(CommandHandler handler, [OptionalArg] string needle) + static bool HandleCharacterDeletedListCommand(CommandHandler handler, OptionalArg needle) { List foundList = new(); if (!GetDeletedCharacterInfoList(foundList, needle)) @@ -474,7 +475,7 @@ namespace Game.Chat } [Command("restore", RBACPermissions.CommandCharacterDeletedRestore, true)] - static bool HandleCharacterDeletedRestoreCommand(CommandHandler handler, string needle, [OptionalArg] string newCharName, AccountIdentifier newAccount) + static bool HandleCharacterDeletedRestoreCommand(CommandHandler handler, string needle, OptionalArg newCharName, AccountIdentifier newAccount) { List foundList = new(); if (!GetDeletedCharacterInfoList(foundList, needle)) @@ -489,7 +490,7 @@ namespace Game.Chat handler.SendSysMessage(CypherStrings.CharacterDeletedRestore); HandleCharacterDeletedListHelper(foundList, handler); - if (newCharName.IsEmpty()) + if (!newCharName.HasValue) { // Drop not existed account cases foreach (var info in foundList) @@ -521,7 +522,7 @@ namespace Game.Chat } [Command("old", RBACPermissions.CommandCharacterDeletedOld, true)] - static bool HandleCharacterDeletedOldCommand(CommandHandler handler, ushort? days) + static bool HandleCharacterDeletedOldCommand(CommandHandler handler, OptionalArg days) { int keepDays = WorldConfig.GetIntValue(WorldCfg.ChardeleteKeepDays); @@ -658,13 +659,15 @@ namespace Game.Chat } [CommandNonGroup("levelup", RBACPermissions.CommandLevelup)] - static bool HandleLevelUpCommand(CommandHandler handler, [OptionalArg]PlayerIdentifier player, short level) + static bool HandleLevelUpCommand(CommandHandler handler, OptionalArg playerIdentifier, short level) { - if (player == null) - player = PlayerIdentifier.FromTargetOrSelf(handler); - if (player == null) + if (!playerIdentifier.HasValue) + playerIdentifier = PlayerIdentifier.FromTargetOrSelf(handler); + if (playerIdentifier.Value == null) return false; + PlayerIdentifier player = playerIdentifier.Value; + int oldlevel = (int)(player.IsConnected() ? player.GetConnectedPlayer().GetLevel() : Global.CharacterCacheStorage.GetCharacterLevelByGuid(player.GetGUID())); int newlevel = oldlevel + level; @@ -711,7 +714,7 @@ namespace Game.Chat class PdumpCommand { [Command("copy", RBACPermissions.CommandPdumpCopy, true)] - static bool HandlePDumpCopyCommand(CommandHandler handler, PlayerIdentifier player, AccountIdentifier account, [OptionalArg] string characterName, ulong? characterGUID) + static bool HandlePDumpCopyCommand(CommandHandler handler, PlayerIdentifier player, AccountIdentifier account, OptionalArg characterName, OptionalArg characterGUID) { /* std::string name; @@ -757,7 +760,7 @@ namespace Game.Chat } [Command("load", RBACPermissions.CommandPdumpLoad, true)] - static bool HandlePDumpLoadCommand(CommandHandler handler, string fileName, AccountIdentifier account, [OptionalArg] string characterName, ulong? characterGuid) + static bool HandlePDumpLoadCommand(CommandHandler handler, string fileName, AccountIdentifier account, OptionalArg characterName, OptionalArg characterGuid) { /* if (!AccountMgr.normalizeString(accountName)) diff --git a/Source/Game/Chat/Commands/CheatCommands.cs b/Source/Game/Chat/Commands/CheatCommands.cs index 99182218b..c88b2a5d0 100644 --- a/Source/Game/Chat/Commands/CheatCommands.cs +++ b/Source/Game/Chat/Commands/CheatCommands.cs @@ -11,7 +11,7 @@ namespace Game.Chat.Commands class CheatCommands { [Command("casttime", RBACPermissions.CommandCheatCasttime)] - static bool HandleCasttimeCheatCommand(CommandHandler handler, bool? enableArg) + static bool HandleCasttimeCheatCommand(CommandHandler handler, OptionalArg enableArg) { bool enable = !handler.GetSession().GetPlayer().GetCommandStatus(PlayerCommandStates.Casttime); if (enableArg.HasValue) @@ -32,7 +32,7 @@ namespace Game.Chat.Commands } [Command("cooldown", RBACPermissions.CommandCheatCooldown)] - static bool HandleCoolDownCheatCommand(CommandHandler handler, bool? enableArg) + static bool HandleCoolDownCheatCommand(CommandHandler handler, OptionalArg enableArg) { bool enable = !handler.GetSession().GetPlayer().GetCommandStatus(PlayerCommandStates.Cooldown); if (enableArg.HasValue) @@ -87,7 +87,7 @@ namespace Game.Chat.Commands } [Command("god", RBACPermissions.CommandCheatGod)] - static bool HandleGodModeCheatCommand(CommandHandler handler, bool? enableArg) + static bool HandleGodModeCheatCommand(CommandHandler handler, OptionalArg enableArg) { bool enable = !handler.GetSession().GetPlayer().GetCommandStatus(PlayerCommandStates.God); if (enableArg.HasValue) @@ -108,7 +108,7 @@ namespace Game.Chat.Commands } [Command("power", RBACPermissions.CommandCheatPower)] - static bool HandlePowerCheatCommand(CommandHandler handler, bool? enableArg) + static bool HandlePowerCheatCommand(CommandHandler handler, OptionalArg enableArg) { bool enable = !handler.GetSession().GetPlayer().GetCommandStatus(PlayerCommandStates.Power); if (enableArg.HasValue) @@ -152,7 +152,7 @@ namespace Game.Chat.Commands } [Command("taxi", RBACPermissions.CommandCheatTaxi)] - static bool HandleTaxiCheatCommand(CommandHandler handler, bool? enableArg) + static bool HandleTaxiCheatCommand(CommandHandler handler, OptionalArg enableArg) { Player chr = handler.GetSelectedPlayer(); if (chr == null) @@ -183,7 +183,7 @@ namespace Game.Chat.Commands } [Command("waterwalk", RBACPermissions.CommandCheatWaterwalk)] - static bool HandleWaterWalkCheatCommand(CommandHandler handler, bool? enableArg) + static bool HandleWaterWalkCheatCommand(CommandHandler handler, OptionalArg enableArg) { bool enable = !handler.GetSession().GetPlayer().GetCommandStatus(PlayerCommandStates.Waterwalk); if (enableArg.HasValue) diff --git a/Source/Game/Chat/Commands/DebugCommands.cs b/Source/Game/Chat/Commands/DebugCommands.cs index ac7b8eac2..cb6680068 100644 --- a/Source/Game/Chat/Commands/DebugCommands.cs +++ b/Source/Game/Chat/Commands/DebugCommands.cs @@ -440,7 +440,7 @@ namespace Game.Chat } [Command("instancespawn", RBACPermissions.CommandDebug)] - static bool HandleDebugInstanceSpawns(CommandHandler handler, [VariantArg] object optArg) + static bool HandleDebugInstanceSpawns(CommandHandler handler, VariantArg optArg) { Player player = handler.GetPlayer(); if (player == null) @@ -448,10 +448,10 @@ namespace Game.Chat bool explain = false; uint groupID = 0; - if (optArg is string && (optArg as string).Equals("explain", StringComparison.OrdinalIgnoreCase)) + if (optArg.Is() && ((string)optArg).Equals("explain", StringComparison.OrdinalIgnoreCase)) explain = true; else - groupID = (uint)optArg; + groupID = optArg; if (groupID != 0 && Global.ObjectMgr.GetSpawnGroupData(groupID) == null) { @@ -560,7 +560,7 @@ namespace Game.Chat } [Command("loadcells", RBACPermissions.CommandDebug, true)] - static bool HandleDebugLoadCellsCommand(CommandHandler handler, uint? mapId, uint? tileX, uint? tileY) + static bool HandleDebugLoadCellsCommand(CommandHandler handler, OptionalArg mapId, OptionalArg tileX, OptionalArg tileY) { if (mapId.HasValue) { @@ -578,7 +578,7 @@ namespace Game.Chat return false; } - static bool HandleDebugLoadCellsCommandHelper(CommandHandler handler, Map map, uint? tileX, uint? tileY) + static bool HandleDebugLoadCellsCommandHelper(CommandHandler handler, Map map, OptionalArg tileX, OptionalArg tileY) { if (map == null) return false; @@ -640,7 +640,7 @@ namespace Game.Chat } [Command("moveflags", RBACPermissions.CommandDebug)] - static bool HandleDebugMoveflagsCommand(CommandHandler handler, uint? moveFlags, uint? moveFlagsExtra, uint? moveFlagsExtra2) + static bool HandleDebugMoveflagsCommand(CommandHandler handler, OptionalArg moveFlags, OptionalArg moveFlagsExtra, OptionalArg moveFlagsExtra2) { Unit target = handler.GetSelectedUnit(); if (target == null) @@ -653,10 +653,10 @@ namespace Game.Chat } else { - target.SetUnitMovementFlags((MovementFlag)moveFlags); + target.SetUnitMovementFlags((MovementFlag)moveFlags.Value); if (moveFlagsExtra.HasValue) - target.SetUnitMovementFlags2((MovementFlag2)moveFlagsExtra); + target.SetUnitMovementFlags2((MovementFlag2)moveFlagsExtra.Value); if (moveFlagsExtra2.HasValue) target.SetExtraUnitMovementFlags2((MovementFlags3)moveFlagsExtra2.Value); @@ -721,7 +721,7 @@ namespace Game.Chat } [Command("objectcount", RBACPermissions.CommandDebug, true)] - static bool HandleDebugObjectCountCommand(CommandHandler handler, uint? mapId) + static bool HandleDebugObjectCountCommand(CommandHandler handler, OptionalArg mapId) { void HandleDebugObjectCountMap(Map map) { @@ -792,7 +792,7 @@ namespace Game.Chat } [Command("pvp warmode", RBACPermissions.CommandDebug, true)] - static bool HandleDebugWarModeBalanceCommand(CommandHandler handler, string command, int? rewardValue) + static bool HandleDebugWarModeBalanceCommand(CommandHandler handler, string command, OptionalArg rewardValue) { // USAGE: .debug pvp fb [pct] // neutral Sets faction balance off. @@ -888,7 +888,7 @@ namespace Game.Chat } [Command("setaurastate", RBACPermissions.CommandDebug)] - static bool HandleDebugSetAuraStateCommand(CommandHandler handler, AuraStateType? state, bool apply) + static bool HandleDebugSetAuraStateCommand(CommandHandler handler, OptionalArg state, bool apply) { Unit unit = handler.GetSelectedUnit(); if (unit == null) @@ -1230,7 +1230,7 @@ namespace Game.Chat } [Command("objectsound", RBACPermissions.CommandDebug)] - static bool HandleDebugPlayObjectSoundCommand(CommandHandler handler, uint soundKitId, int? broadcastTextId) + static bool HandleDebugPlayObjectSoundCommand(CommandHandler handler, uint soundKitId, OptionalArg broadcastTextId) { if (!CliDB.SoundKitStorage.ContainsKey(soundKitId)) { @@ -1409,7 +1409,7 @@ namespace Game.Chat } [Command("spellfail", RBACPermissions.CommandDebug)] - static bool HandleDebugSendSpellFailCommand(CommandHandler handler, SpellCastResult result, int? failArg1, int? failArg2) + static bool HandleDebugSendSpellFailCommand(CommandHandler handler, SpellCastResult result, OptionalArg failArg1, OptionalArg failArg2) { CastFailed castFailed = new(); castFailed.CastID = ObjectGuid.Empty; diff --git a/Source/Game/Chat/Commands/DisableCommands.cs b/Source/Game/Chat/Commands/DisableCommands.cs index 99f701ec4..a30075534 100644 --- a/Source/Game/Chat/Commands/DisableCommands.cs +++ b/Source/Game/Chat/Commands/DisableCommands.cs @@ -14,7 +14,7 @@ namespace Game.Chat.Commands [CommandGroup("add")] class DisableAddCommands { - static bool HandleAddDisables(CommandHandler handler, DisableType disableType, uint entry, uint? flags, Tail disableComment) + static bool HandleAddDisables(CommandHandler handler, DisableType disableType, uint entry, OptionalArg flags, Tail disableComment) { switch (disableType) { @@ -125,19 +125,19 @@ namespace Game.Chat.Commands } [Command("spell", RBACPermissions.CommandDisableAddSpell, true)] - static bool HandleAddDisableSpellCommand(CommandHandler handler, uint entry, uint? flags, Tail disableComment) + static bool HandleAddDisableSpellCommand(CommandHandler handler, uint entry, OptionalArg flags, Tail disableComment) { return HandleAddDisables(handler, DisableType.Spell, entry, flags, disableComment); } [Command("quest", RBACPermissions.CommandDisableAddQuest, true)] - static bool HandleAddDisableQuestCommand(CommandHandler handler, uint entry, uint? flags, Tail disableComment) + static bool HandleAddDisableQuestCommand(CommandHandler handler, uint entry, OptionalArg flags, Tail disableComment) { return HandleAddDisables(handler, DisableType.Quest, entry, flags, disableComment); } [Command("map", RBACPermissions.CommandDisableAddMap, true)] - static bool HandleAddDisableMapCommand(CommandHandler handler, uint entry, uint? flags, Tail disableComment) + static bool HandleAddDisableMapCommand(CommandHandler handler, uint entry, OptionalArg flags, Tail disableComment) { return HandleAddDisables(handler, DisableType.Map, entry, flags, disableComment); } diff --git a/Source/Game/Chat/Commands/EventCommands.cs b/Source/Game/Chat/Commands/EventCommands.cs index b54b641c9..793d75491 100644 --- a/Source/Game/Chat/Commands/EventCommands.cs +++ b/Source/Game/Chat/Commands/EventCommands.cs @@ -10,7 +10,7 @@ namespace Game.Chat class EventCommands { [Command("info", RBACPermissions.CommandEventInfo, true)] - static bool HandleEventInfoCommand(CommandHandler handler, ushort eventId) + static bool HandleEventInfoCommand(CommandHandler handler, VariantArg eventId) { var events = Global.GameEventMgr.GetEventMap(); if (eventId >= events.Length) @@ -74,7 +74,7 @@ namespace Game.Chat } [Command("start", RBACPermissions.CommandEventStart, true)] - static bool HandleEventStartCommand(CommandHandler handler, ushort eventId) + static bool HandleEventStartCommand(CommandHandler handler, VariantArg eventId) { var events = Global.GameEventMgr.GetEventMap(); if (eventId < 1 || eventId >= events.Length) @@ -102,7 +102,7 @@ namespace Game.Chat } [Command("stop", RBACPermissions.CommandEventStop, true)] - static bool HandleEventStopCommand(CommandHandler handler, ushort eventId) + static bool HandleEventStopCommand(CommandHandler handler, VariantArg eventId) { var events = Global.GameEventMgr.GetEventMap(); if (eventId < 1 || eventId >= events.Length) diff --git a/Source/Game/Chat/Commands/GMCommands.cs b/Source/Game/Chat/Commands/GMCommands.cs index 2528c2af9..f85135be0 100644 --- a/Source/Game/Chat/Commands/GMCommands.cs +++ b/Source/Game/Chat/Commands/GMCommands.cs @@ -12,7 +12,7 @@ namespace Game.Chat class GMCommands { [Command("chat", RBACPermissions.CommandGmChat)] - static bool HandleGMChatCommand(CommandHandler handler, bool? enableArg) + static bool HandleGMChatCommand(CommandHandler handler, OptionalArg enableArg) { WorldSession session = handler.GetSession(); if (session != null) @@ -160,7 +160,7 @@ namespace Game.Chat } [Command("visible", RBACPermissions.CommandGmVisible)] - static bool HandleGMVisibleCommand(CommandHandler handler, bool? visibleArg) + static bool HandleGMVisibleCommand(CommandHandler handler, OptionalArg visibleArg) { Player _player = handler.GetSession().GetPlayer(); diff --git a/Source/Game/Chat/Commands/GameObjectCommands.cs b/Source/Game/Chat/Commands/GameObjectCommands.cs index 18db7a8dc..ae5c4d910 100644 --- a/Source/Game/Chat/Commands/GameObjectCommands.cs +++ b/Source/Game/Chat/Commands/GameObjectCommands.cs @@ -18,7 +18,7 @@ namespace Game.Chat class GameObjectCommands { [Command("activate", RBACPermissions.CommandGobjectActivate)] - static bool HandleGameObjectActivateCommand(CommandHandler handler, ulong guidLow) + static bool HandleGameObjectActivateCommand(CommandHandler handler, VariantArg guidLow) { GameObject obj = handler.GetObjectFromPlayerMapByDbGuid(guidLow); if (obj == null) @@ -38,7 +38,7 @@ namespace Game.Chat } [Command("delete", RBACPermissions.CommandGobjectDelete)] - static bool HandleGameObjectDeleteCommand(CommandHandler handler, ulong spawnId) + static bool HandleGameObjectDeleteCommand(CommandHandler handler, VariantArg spawnId) { GameObject obj = handler.GetObjectFromPlayerMapByDbGuid(spawnId); if (obj != null) @@ -101,14 +101,14 @@ namespace Game.Chat } [Command("info", RBACPermissions.CommandGobjectInfo)] - static bool HandleGameObjectInfoCommand(CommandHandler handler, [OptionalArg] string isGuid, ulong data) + static bool HandleGameObjectInfoCommand(CommandHandler handler, OptionalArg isGuid, VariantArg data) { GameObject thisGO = null; GameObjectData spawnData = null; uint entry; ulong spawnId = 0; - if (!isGuid.IsEmpty() && isGuid.Equals("guid", StringComparison.OrdinalIgnoreCase)) + if (!isGuid.HasValue && isGuid.Value.Equals("guid", StringComparison.OrdinalIgnoreCase)) { spawnId = data; spawnData = Global.ObjectMgr.GetGameObjectData(spawnId); @@ -186,7 +186,7 @@ namespace Game.Chat } [Command("move", RBACPermissions.CommandGobjectMove)] - static bool HandleGameObjectMoveCommand(CommandHandler handler, ulong guidLow, float[] xyz) + static bool HandleGameObjectMoveCommand(CommandHandler handler, VariantArg guidLow, OptionalArg xyz) { GameObject obj = handler.GetObjectFromPlayerMapByDbGuid(guidLow); if (obj == null) @@ -196,9 +196,9 @@ namespace Game.Chat } Position pos; - if (xyz != null) + if (xyz.HasValue) { - pos = new Position(xyz[0], xyz[1], xyz[2]); + pos = new Position(xyz.Value[0], xyz.Value[1], xyz.Value[2]); if (!GridDefines.IsValidMapCoord(obj.GetMapId(), pos)) { handler.SendSysMessage(CypherStrings.InvalidTargetCoord, pos.GetPositionX(), pos.GetPositionY(), obj.GetMapId()); @@ -236,7 +236,7 @@ namespace Game.Chat } [Command("near", RBACPermissions.CommandGobjectNear)] - static bool HandleGameObjectNearCommand(CommandHandler handler, float? dist) + static bool HandleGameObjectNearCommand(CommandHandler handler, OptionalArg dist) { float distance = dist.GetValueOrDefault(10f); uint count = 0; @@ -323,23 +323,24 @@ namespace Game.Chat } [Command("target", RBACPermissions.CommandGobjectTarget)] - static bool HandleGameObjectTargetCommand(CommandHandler handler, string objectIdStr) + static bool HandleGameObjectTargetCommand(CommandHandler handler, OptionalArg, string>> objectId) { Player player = handler.GetSession().GetPlayer(); SQLResult result; var activeEventsList = Global.GameEventMgr.GetActiveEventList(); - if (objectIdStr.IsEmpty()) + if (objectId.HasValue) { - if (uint.TryParse(objectIdStr, out uint objectId)) + if (objectId.Value.Is>()) result = DB.World.Query("SELECT guid, id, position_x, position_y, position_z, orientation, map, PhaseId, PhaseGroup, (POW(position_x - '{0}', 2) + POW(position_y - '{1}', 2) + POW(position_z - '{2}', 2)) AS order_ FROM gameobject WHERE map = '{3}' AND id = '{4}' ORDER BY order_ ASC LIMIT 1", player.GetPositionX(), player.GetPositionY(), player.GetPositionZ(), player.GetMapId(), objectId); else { + string name = objectId.Value.GetValue(); result = DB.World.Query( "SELECT guid, id, position_x, position_y, position_z, orientation, map, PhaseId, PhaseGroup, (POW(position_x - {0}, 2) + POW(position_y - {1}, 2) + POW(position_z - {2}, 2)) AS order_ " + "FROM gameobject LEFT JOIN gameobject_template ON gameobject_template.entry = gameobject.id WHERE map = {3} AND name LIKE CONCAT('%%', '{4}', '%%') ORDER BY order_ ASC LIMIT 1", - player.GetPositionX(), player.GetPositionY(), player.GetPositionZ(), player.GetMapId(), objectIdStr); + player.GetPositionX(), player.GetPositionY(), player.GetPositionZ(), player.GetMapId(), name); } } else @@ -433,7 +434,7 @@ namespace Game.Chat } [Command("turn", RBACPermissions.CommandGobjectTurn)] - static bool HandleGameObjectTurnCommand(CommandHandler handler, ulong guidLow, float? oz, float? oy, float? ox) + static bool HandleGameObjectTurnCommand(CommandHandler handler, VariantArg guidLow, OptionalArg oz, OptionalArg oy, OptionalArg ox) { GameObject obj = handler.GetObjectFromPlayerMapByDbGuid(guidLow); if (obj == null) @@ -470,7 +471,7 @@ namespace Game.Chat class AddCommands { [Command("", RBACPermissions.CommandGobjectAdd)] - static bool HandleGameObjectAddCommand(CommandHandler handler, uint objectId, int? spawnTimeSecs) + static bool HandleGameObjectAddCommand(CommandHandler handler, VariantArg objectId, OptionalArg spawnTimeSecs) { if (objectId == 0) return false; @@ -518,7 +519,7 @@ namespace Game.Chat } [Command("temp", RBACPermissions.CommandGobjectAddTemp)] - static bool HandleGameObjectAddTempCommand(CommandHandler handler, uint objectId, ulong? spawntime) + static bool HandleGameObjectAddTempCommand(CommandHandler handler, VariantArg objectId, OptionalArg spawntime) { Player player = handler.GetPlayer(); TimeSpan spawntm = TimeSpan.FromSeconds(spawntime.GetValueOrDefault(300)); @@ -540,7 +541,7 @@ namespace Game.Chat class SetCommands { [Command("phase", RBACPermissions.CommandGobjectSetPhase)] - static bool HandleGameObjectSetPhaseCommand(CommandHandler handler, ulong guidLow, uint phaseId) + static bool HandleGameObjectSetPhaseCommand(CommandHandler handler, VariantArg guidLow, uint phaseId) { if (guidLow == 0) return false; @@ -564,7 +565,7 @@ namespace Game.Chat } [Command("state", RBACPermissions.CommandGobjectSetState)] - static bool HandleGameObjectSetStateCommand(CommandHandler handler, ulong guidLow, int objectType, uint? objectState) + static bool HandleGameObjectSetStateCommand(CommandHandler handler, VariantArg guidLow, int objectType, OptionalArg objectState) { if (guidLow == 0) return false; @@ -585,16 +586,16 @@ namespace Game.Chat return true; } - if (objectState == 0) + if (!objectState.HasValue) return false; switch (objectType) { case 0: - obj.SetGoState((GameObjectState)objectState); + obj.SetGoState((GameObjectState)objectState.Value); break; case 1: - obj.SetGoType((GameObjectTypes)objectState); + obj.SetGoType((GameObjectTypes)objectState.Value); break; case 2: obj.SetGoArtKit(objectState.Value); @@ -606,10 +607,10 @@ namespace Game.Chat obj.SendCustomAnim(objectState.Value); break; case 5: - if (objectState < 0 || objectState > (uint)GameObjectDestructibleState.Rebuilding) + if (objectState.Value < 0 || objectState.Value > (uint)GameObjectDestructibleState.Rebuilding) return false; - obj.SetDestructibleState((GameObjectDestructibleState)objectState); + obj.SetDestructibleState((GameObjectDestructibleState)objectState.Value); break; default: break; diff --git a/Source/Game/Chat/Commands/GoCommands.cs b/Source/Game/Chat/Commands/GoCommands.cs index 9bb95bcea..8a0b4ebb9 100644 --- a/Source/Game/Chat/Commands/GoCommands.cs +++ b/Source/Game/Chat/Commands/GoCommands.cs @@ -17,7 +17,7 @@ namespace Game.Chat.Commands class GoCommands { [Command("areatrigger", RBACPermissions.CommandGo)] - static bool HandleGoAreaTriggerCommand(CommandHandler handler, uint areaTriggerId) + static bool HandleGoAreaTriggerCommand(CommandHandler handler, VariantArg areaTriggerId) { var at = CliDB.AreaTriggerStorage.LookupByKey(areaTriggerId); if (at == null) @@ -168,7 +168,7 @@ namespace Game.Chat.Commands } [Command("grid", RBACPermissions.CommandGo)] - static bool HandleGoGridCommand(CommandHandler handler, float gridX, float gridY, uint? mapIdArg) + static bool HandleGoGridCommand(CommandHandler handler, float gridX, float gridY, OptionalArg mapIdArg) { Player player = handler.GetSession().GetPlayer(); uint mapId = mapIdArg.GetValueOrDefault(player.GetMapId()); @@ -286,7 +286,7 @@ namespace Game.Chat.Commands } [Command("offset", RBACPermissions.CommandGo)] - static bool HandleGoOffsetCommand(CommandHandler handler, float dX, float? dY, float? dZ, float? dO) + static bool HandleGoOffsetCommand(CommandHandler handler, float dX, OptionalArg dY, OptionalArg dZ, OptionalArg dO) { Position loc = handler.GetSession().GetPlayer().GetPosition(); loc.RelocateOffset(new Position(dX, dY.GetValueOrDefault(0f), dZ.GetValueOrDefault(0f), dO.GetValueOrDefault(0f))); @@ -295,7 +295,7 @@ namespace Game.Chat.Commands } [Command("quest", RBACPermissions.CommandGo)] - static bool HandleGoQuestCommand(CommandHandler handler, uint questId) + static bool HandleGoQuestCommand(CommandHandler handler, VariantArg questId) { Player player = handler.GetSession().GetPlayer(); @@ -350,7 +350,7 @@ namespace Game.Chat.Commands } [Command("taxinode", RBACPermissions.CommandGo)] - static bool HandleGoTaxinodeCommand(CommandHandler handler, uint nodeId) + static bool HandleGoTaxinodeCommand(CommandHandler handler, VariantArg nodeId) { var node = CliDB.TaxiNodesStorage.LookupByKey(nodeId); if (node == null) @@ -363,7 +363,7 @@ namespace Game.Chat.Commands //teleport at coordinates, including Z and orientation [Command("xyz", RBACPermissions.CommandGo)] - static bool HandleGoXYZCommand(CommandHandler handler, float x, float y, float? z, uint? id, float? o) + static bool HandleGoXYZCommand(CommandHandler handler, float x, float y, OptionalArg z, OptionalArg id, OptionalArg o) { Player player = handler.GetSession().GetPlayer(); uint mapId = id.GetValueOrDefault(player.GetMapId()); @@ -386,12 +386,12 @@ namespace Game.Chat.Commands z = Math.Max(terrain.GetStaticHeight(PhasingHandler.EmptyPhaseShift, mapId, x, y, MapConst.MaxHeight), terrain.GetWaterLevel(PhasingHandler.EmptyPhaseShift, mapId, x, y)); } - return DoTeleport(handler, new Position(x, y, z.Value, o.GetValueOrDefault()), mapId); + return DoTeleport(handler, new Position(x, y, z.Value, o.GetValueOrDefault(0f)), mapId); } //teleport at coordinates [Command("zonexy", RBACPermissions.CommandGo)] - static bool HandleGoZoneXYCommand(CommandHandler handler, float x, float y, uint? areaIdArg) + static bool HandleGoZoneXYCommand(CommandHandler handler, float x, float y, OptionalArg> areaIdArg) { Player player = handler.GetSession().GetPlayer(); @@ -486,7 +486,7 @@ namespace Game.Chat.Commands class GoCommandCreature { [Command("", RBACPermissions.CommandGo)] - static bool HandleGoCreatureSpawnIdCommand(CommandHandler handler, ulong spawnId) + static bool HandleGoCreatureSpawnIdCommand(CommandHandler handler, VariantArg spawnId) { CreatureData spawnpoint = Global.ObjectMgr.GetCreatureData(spawnId); if (spawnpoint == null) @@ -499,7 +499,7 @@ namespace Game.Chat.Commands } [Command("id", RBACPermissions.CommandGo)] - static bool HandleGoCreatureCIdCommand(CommandHandler handler, uint id) + static bool HandleGoCreatureCIdCommand(CommandHandler handler, VariantArg id) { CreatureData spawnpoint = null; foreach (var pair in Global.ObjectMgr.GetAllCreatureData()) @@ -530,7 +530,7 @@ namespace Game.Chat.Commands class GoCommandGameobject { [Command("", RBACPermissions.CommandGo)] - static bool HandleGoGameObjectSpawnIdCommand(CommandHandler handler, ulong spawnId) + static bool HandleGoGameObjectSpawnIdCommand(CommandHandler handler, VariantArg spawnId) { GameObjectData spawnpoint = Global.ObjectMgr.GetGameObjectData(spawnId); if (spawnpoint == null) @@ -543,7 +543,7 @@ namespace Game.Chat.Commands } [Command("id", RBACPermissions.CommandGo)] - static bool HandleGoGameObjectGOIdCommand(CommandHandler handler, uint goId) + static bool HandleGoGameObjectGOIdCommand(CommandHandler handler, VariantArg goId) { GameObjectData spawnpoint = null; foreach (var pair in Global.ObjectMgr.GetAllGameObjectData()) diff --git a/Source/Game/Chat/Commands/GuildCommands.cs b/Source/Game/Chat/Commands/GuildCommands.cs index f95bd3940..7903f4998 100644 --- a/Source/Game/Chat/Commands/GuildCommands.cs +++ b/Source/Game/Chat/Commands/GuildCommands.cs @@ -167,16 +167,16 @@ namespace Game.Chat } [Command("info", RBACPermissions.CommandGuildInfo, true)] - static bool HandleGuildInfoCommand(CommandHandler handler, [OptionalArg][VariantArg] dynamic guildIdentifier) + static bool HandleGuildInfoCommand(CommandHandler handler, OptionalArg> guildIdentifier) { Guild guild = null; - if (guildIdentifier != null) + if (guildIdentifier.HasValue) { - if (guildIdentifier is ulong) - guild = Global.GuildMgr.GetGuildById(guildIdentifier); + if (guildIdentifier.Value.Is()) + guild = Global.GuildMgr.GetGuildById(guildIdentifier.Value); else - guild = Global.GuildMgr.GetGuildByName(guildIdentifier); + guild = Global.GuildMgr.GetGuildByName(guildIdentifier.Value); } else { diff --git a/Source/Game/Chat/Commands/InstanceCommands.cs b/Source/Game/Chat/Commands/InstanceCommands.cs index c2cc799c3..860d8bafc 100644 --- a/Source/Game/Chat/Commands/InstanceCommands.cs +++ b/Source/Game/Chat/Commands/InstanceCommands.cs @@ -141,7 +141,7 @@ namespace Game.Chat } [Command("unbind", RBACPermissions.CommandInstanceUnbind)] - static bool HandleInstanceUnbindCommand(CommandHandler handler, [VariantArg] object mapArg, uint? difficultyArg) + static bool HandleInstanceUnbindCommand(CommandHandler handler, VariantArg mapArg, OptionalArg difficultyArg) { Player player = handler.GetSelectedPlayer(); if (player == null) @@ -150,11 +150,11 @@ namespace Game.Chat uint? mapId = null; Difficulty? difficulty = null; - if (mapArg is uint) - mapId = (uint)mapArg; + if (mapArg.Is()) + mapId = mapArg; if (difficultyArg.HasValue && CliDB.DifficultyStorage.ContainsKey(difficultyArg.Value)) - difficulty = (Difficulty)difficultyArg; + difficulty = (Difficulty)difficultyArg.Value; List locksReset = new(); List locksNotReset = new(); diff --git a/Source/Game/Chat/Commands/LFGCommands.cs b/Source/Game/Chat/Commands/LFGCommands.cs index b55282e6c..dd7e7f4b9 100644 --- a/Source/Game/Chat/Commands/LFGCommands.cs +++ b/Source/Game/Chat/Commands/LFGCommands.cs @@ -74,7 +74,7 @@ namespace Game.Chat } [Command("options", RBACPermissions.CommandLfgOptions, true)] - static bool HandleLfgOptionsCommand(CommandHandler handler, uint? optionsArg) + static bool HandleLfgOptionsCommand(CommandHandler handler, OptionalArg optionsArg) { if (optionsArg.HasValue) { diff --git a/Source/Game/Chat/Commands/LearnCommands.cs b/Source/Game/Chat/Commands/LearnCommands.cs index fcac5f2c0..08ac77fa6 100644 --- a/Source/Game/Chat/Commands/LearnCommands.cs +++ b/Source/Game/Chat/Commands/LearnCommands.cs @@ -15,7 +15,7 @@ namespace Game.Chat.Commands class LearnCommands { [Command("", CypherStrings.CommandLearnHelp, RBACPermissions.CommandLearn)] - static bool HandleLearnCommand(CommandHandler handler, uint spellId, [OptionalArg] string allRanksStr) + static bool HandleLearnCommand(CommandHandler handler, uint spellId, OptionalArg allRanksStr) { Player targetPlayer = handler.GetSelectedPlayerOrSelf(); if (targetPlayer == null) @@ -30,7 +30,7 @@ namespace Game.Chat.Commands return false; } - bool allRanks = !allRanksStr.IsEmpty() && allRanksStr.Equals("all", StringComparison.OrdinalIgnoreCase); + bool allRanks = allRanksStr.HasValue && allRanksStr.Value.Equals("all", StringComparison.OrdinalIgnoreCase); if (!allRanks && targetPlayer.HasSpell(spellId)) { @@ -314,7 +314,7 @@ namespace Game.Chat.Commands } [CommandNonGroup("unlearn", CypherStrings.CommandUnlearnHelp, RBACPermissions.CommandUnlearn)] - static bool HandleUnLearnCommand(CommandHandler handler, uint spellId, [OptionalArg] string allRanksStr) + static bool HandleUnLearnCommand(CommandHandler handler, uint spellId, OptionalArg allRanksStr) { Player target = handler.GetSelectedPlayer(); if (target == null) @@ -323,7 +323,7 @@ namespace Game.Chat.Commands return false; } - bool allRanks = !allRanksStr.IsEmpty() && allRanksStr.Equals("all", StringComparison.OrdinalIgnoreCase); + bool allRanks = allRanksStr.HasValue && allRanksStr.Value.Equals("all", StringComparison.OrdinalIgnoreCase); if (allRanks) spellId = Global.SpellMgr.GetFirstSpellInChain(spellId); diff --git a/Source/Game/Chat/Commands/ListCommands.cs b/Source/Game/Chat/Commands/ListCommands.cs index 0109f5daa..3200af1c2 100644 --- a/Source/Game/Chat/Commands/ListCommands.cs +++ b/Source/Game/Chat/Commands/ListCommands.cs @@ -17,7 +17,7 @@ namespace Game.Chat.Commands class ListCommands { [Command("creature", RBACPermissions.CommandListCreature, true)] - static bool HandleListCreatureCommand(CommandHandler handler, uint creatureId, uint? countArg) + static bool HandleListCreatureCommand(CommandHandler handler, VariantArg creatureId, OptionalArg countArg) { CreatureTemplate cInfo = Global.ObjectMgr.GetCreatureTemplate(creatureId); if (cInfo == null) @@ -88,8 +88,9 @@ namespace Game.Chat.Commands } [Command("item", RBACPermissions.CommandListItem, true)] - static bool HandleListItemCommand(CommandHandler handler, uint itemId, uint? countArg) + static bool HandleListItemCommand(CommandHandler handler, VariantArg item, OptionalArg countArg) { + uint itemId = item.GetValue().Item.GetId(); uint count = countArg.GetValueOrDefault(10); if (count == 0) @@ -258,26 +259,26 @@ namespace Game.Chat.Commands } [Command("mail", RBACPermissions.CommandListMail, true)] - static bool HandleListMailCommand(CommandHandler handler, PlayerIdentifier player) + static bool HandleListMailCommand(CommandHandler handler, OptionalArg player) { - if (player == null) + if (!player.HasValue) player = PlayerIdentifier.FromTargetOrSelf(handler); - if (player == null) + if (player.Value == null) return false; PreparedStatement stmt = CharacterDatabase.GetPreparedStatement(CharStatements.SEL_MAIL_LIST_COUNT); - stmt.AddValue(0, player.GetGUID().GetCounter()); + stmt.AddValue(0, player.Value.GetGUID().GetCounter()); SQLResult result = DB.Characters.Query(stmt); if (!result.IsEmpty()) { uint countMail = result.Read(0); - string nameLink = handler.PlayerLink(player.GetName()); - handler.SendSysMessage(CypherStrings.ListMailHeader, countMail, nameLink, player.GetGUID().ToString()); + string nameLink = handler.PlayerLink(player.Value.GetName()); + handler.SendSysMessage(CypherStrings.ListMailHeader, countMail, nameLink, player.Value.GetGUID().ToString()); handler.SendSysMessage(CypherStrings.AccountListBar); stmt = CharacterDatabase.GetPreparedStatement(CharStatements.SEL_MAIL_LIST_INFO); - stmt.AddValue(0, player.GetGUID().GetCounter()); + stmt.AddValue(0, player.Value.GetGUID().GetCounter()); SQLResult result1 = DB.Characters.Query(stmt); if (!result1.IsEmpty()) @@ -354,7 +355,7 @@ namespace Game.Chat.Commands } [Command("object", RBACPermissions.CommandListObject, true)] - static bool HandleListObjectCommand(CommandHandler handler, uint gameObjectId, uint? countArg) + static bool HandleListObjectCommand(CommandHandler handler, VariantArg gameObjectId, OptionalArg countArg) { GameObjectTemplate gInfo = Global.ObjectMgr.GetGameObjectTemplate(gameObjectId); if (gInfo == null) @@ -426,7 +427,7 @@ namespace Game.Chat.Commands } [Command("respawns", RBACPermissions.CommandListRespawns)] - static bool HandleListRespawnsCommand(CommandHandler handler, uint? range) + static bool HandleListRespawnsCommand(CommandHandler handler, OptionalArg range) { Player player = handler.GetSession().GetPlayer(); Map map = player.GetMap(); @@ -552,7 +553,7 @@ namespace Game.Chat.Commands [Command("", RBACPermissions.CommandListAuras)] static bool HandleListAllAurasCommand(CommandHandler handler) { - return ListAurasCommand(handler, null, null); + return ListAurasCommand(handler, default, null); } [Command("id", RBACPermissions.CommandListAuras)] @@ -564,10 +565,10 @@ namespace Game.Chat.Commands [Command("name", RBACPermissions.CommandListAuras)] static bool HandleListAurasByNameCommand(CommandHandler handler, Tail namePart) { - return ListAurasCommand(handler, null, namePart); + return ListAurasCommand(handler, default, namePart); } - static bool ListAurasCommand(CommandHandler handler, uint? spellId, string namePart) + static bool ListAurasCommand(CommandHandler handler, OptionalArg spellId, string namePart) { Unit unit = handler.GetSelectedUnit(); if (unit == null) @@ -625,7 +626,7 @@ namespace Game.Chat.Commands return true; } - static bool ShouldListAura(SpellInfo spellInfo, uint? spellId, string namePart, Locale locale) + static bool ShouldListAura(SpellInfo spellInfo, OptionalArg spellId, string namePart, Locale locale) { if (spellId.HasValue) return spellInfo.Id == spellId.Value; diff --git a/Source/Game/Chat/Commands/MessageCommands.cs b/Source/Game/Chat/Commands/MessageCommands.cs index 85753c11f..f6f16edf0 100644 --- a/Source/Game/Chat/Commands/MessageCommands.cs +++ b/Source/Game/Chat/Commands/MessageCommands.cs @@ -92,7 +92,7 @@ namespace Game.Chat } [CommandNonGroup("whispers", RBACPermissions.CommandWhispers)] - static bool HandleWhispersCommand(CommandHandler handler, bool? operationArg, [OptionalArg] string playerNameArg) + static bool HandleWhispersCommand(CommandHandler handler, OptionalArg operationArg, OptionalArg playerNameArg) { if (!operationArg.HasValue) { diff --git a/Source/Game/Chat/Commands/MiscCommands.cs b/Source/Game/Chat/Commands/MiscCommands.cs index 82f65c097..fdba43a8e 100644 --- a/Source/Game/Chat/Commands/MiscCommands.cs +++ b/Source/Game/Chat/Commands/MiscCommands.cs @@ -213,7 +213,7 @@ namespace Game.Chat } [CommandNonGroup("damage", RBACPermissions.CommandDamage)] - static bool HandleDamageCommand(CommandHandler handler, uint damage, SpellSchools? school, [OptionalArg] SpellInfo spellInfo) + static bool HandleDamageCommand(CommandHandler handler, uint damage, OptionalArg school, OptionalArg spellInfo) { Unit target = handler.GetSelectedUnit(); if (target == null || handler.GetSession().GetPlayer().GetTarget().IsEmpty()) @@ -246,7 +246,7 @@ namespace Game.Chat damage = Unit.CalcArmorReducedDamage(handler.GetPlayer(), target, damage, null, WeaponAttackType.BaseAttack); // melee damage by specific school - if (spellInfo == null) + if (!spellInfo.HasValue) { DamageInfo dmgInfo = new(attacker, target, damage, null, schoolmask, DamageEffectType.SpellDirect, WeaponAttackType.BaseAttack); Unit.CalcAbsorbResist(dmgInfo); @@ -266,7 +266,7 @@ namespace Game.Chat // non-melee damage - SpellNonMeleeDamage damageInfo = new(attacker, target, spellInfo, new SpellCastVisual(spellInfo.GetSpellXSpellVisualId(attacker), 0), spellInfo.SchoolMask); + SpellNonMeleeDamage damageInfo = new(attacker, target, spellInfo, new SpellCastVisual(spellInfo.Value.GetSpellXSpellVisualId(attacker), 0), spellInfo.Value.SchoolMask); damageInfo.damage = damage; Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); target.DealSpellDamage(damageInfo, true); @@ -275,7 +275,7 @@ namespace Game.Chat } [CommandNonGroup("damage go", RBACPermissions.CommandDamage)] - static bool HandleDamageGoCommand(CommandHandler handler, ulong spawnId, int damage) + static bool HandleDamageGoCommand(CommandHandler handler, VariantArg spawnId, int damage) { GameObject go = handler.GetObjectFromPlayerMapByDbGuid(spawnId); if (go == null) @@ -296,7 +296,7 @@ namespace Game.Chat } [CommandNonGroup("dev", RBACPermissions.CommandDev)] - static bool HandleDevCommand(CommandHandler handler, bool? enableArg) + static bool HandleDevCommand(CommandHandler handler, OptionalArg enableArg) { Player player = handler.GetSession().GetPlayer(); @@ -755,14 +755,14 @@ namespace Game.Chat } [CommandNonGroup("linkgrave", RBACPermissions.CommandLinkgrave)] - static bool HandleLinkGraveCommand(CommandHandler handler, uint graveyardId, [OptionalArg] string teamArg) + static bool HandleLinkGraveCommand(CommandHandler handler, uint graveyardId, OptionalArg teamArg) { Team team; - if (teamArg.IsEmpty()) + if (!teamArg.HasValue) team = 0; - else if (teamArg.Equals("horde", StringComparison.OrdinalIgnoreCase)) + else if (teamArg.Value.Equals("horde", StringComparison.OrdinalIgnoreCase)) team = Team.Horde; - else if (teamArg.Equals("alliance", StringComparison.OrdinalIgnoreCase)) + else if (teamArg.Value.Equals("alliance", StringComparison.OrdinalIgnoreCase)) team = Team.Alliance; else return false; @@ -925,22 +925,22 @@ namespace Game.Chat // mute player for the specified duration [CommandNonGroup("mute", RBACPermissions.CommandMute, true)] - static bool HandleMuteCommand(CommandHandler handler, PlayerIdentifier player, uint muteTime, Tail muteReason) + static bool HandleMuteCommand(CommandHandler handler, OptionalArg player, uint muteTime, Tail muteReason) { string muteReasonStr = muteReason; if (muteReason.IsEmpty()) muteReasonStr = handler.GetCypherString(CypherStrings.NoReason); - if (player == null) + if (!player.HasValue) player = PlayerIdentifier.FromTarget(handler); - if (player == null) + if (player.Value == null) { handler.SendSysMessage(CypherStrings.PlayerNotFound); return false; } - Player target = player.GetConnectedPlayer(); - uint accountId = target != null ? target.GetSession().GetAccountId() : Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(player.GetGUID()); + Player target = player.Value.GetConnectedPlayer(); + uint accountId = target != null ? target.GetSession().GetAccountId() : Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(player.Value.GetGUID()); // find only player from same account if any if (target == null) @@ -951,7 +951,7 @@ namespace Game.Chat } // must have strong lesser security level - if (handler.HasLowerSecurity(target, player.GetGUID(), true)) + if (handler.HasLowerSecurity(target, player.Value.GetGUID(), true)) return false; PreparedStatement stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UPD_MUTE_TIME); @@ -986,7 +986,7 @@ namespace Game.Chat stmt.AddValue(3, muteReasonStr); DB.Login.Execute(stmt); - string nameLink = handler.PlayerLink(player.GetName()); + string nameLink = handler.PlayerLink(player.Value.GetName()); if (WorldConfig.GetBoolValue(WorldCfg.ShowMuteInWorld)) Global.WorldMgr.SendWorldText(CypherStrings.CommandMutemessageWorld, muteBy, nameLink, muteTime, muteReasonStr); @@ -1040,14 +1040,14 @@ namespace Game.Chat } [CommandNonGroup("neargrave", RBACPermissions.CommandNeargrave)] - static bool HandleNearGraveCommand(CommandHandler handler, [OptionalArg] string teamArg) + static bool HandleNearGraveCommand(CommandHandler handler, OptionalArg teamArg) { Team team; - if (teamArg.IsEmpty()) + if (!teamArg.HasValue) team = 0; - else if (teamArg.Equals("horde", StringComparison.OrdinalIgnoreCase)) + else if (teamArg.Value.Equals("horde", StringComparison.OrdinalIgnoreCase)) team = Team.Horde; - else if (teamArg.Equals("alliance", StringComparison.OrdinalIgnoreCase)) + else if (teamArg.Value.Equals("alliance", StringComparison.OrdinalIgnoreCase)) team = Team.Alliance; else return false; @@ -1097,18 +1097,17 @@ namespace Game.Chat } [CommandNonGroup("pinfo", RBACPermissions.CommandPinfo, true)] - static bool HandlePInfoCommand(CommandHandler handler, [OptionalArg] PlayerIdentifier arg) + static bool HandlePInfoCommand(CommandHandler handler, OptionalArg arg) { - if (arg == null) + if (!arg.HasValue) arg = PlayerIdentifier.FromTargetOrSelf(handler); - - if (arg == null) + if (arg.Value == null) return false; // Define ALL the player variables! - Player target = arg.GetConnectedPlayer(); - ObjectGuid targetGuid = arg.GetGUID(); - string targetName = arg.GetName(); + Player target = arg.Value.GetConnectedPlayer(); + ObjectGuid targetGuid = arg.Value.GetGUID(); + string targetName = arg.Value.GetName(); PreparedStatement stmt; /* The variables we extract for the command. They are @@ -1450,7 +1449,7 @@ namespace Game.Chat } [CommandNonGroup("playall", RBACPermissions.CommandPlayall)] - static bool HandlePlayAllCommand(CommandHandler handler, uint soundId, uint? broadcastTextId) + static bool HandlePlayAllCommand(CommandHandler handler, uint soundId, OptionalArg broadcastTextId) { if (!CliDB.SoundKitStorage.ContainsKey(soundId)) { @@ -1801,12 +1800,12 @@ namespace Game.Chat } [CommandNonGroup("unfreeze", RBACPermissions.CommandUnfreeze)] - static bool HandleUnFreezeCommand(CommandHandler handler, [OptionalArg] string targetNameArg) + static bool HandleUnFreezeCommand(CommandHandler handler, OptionalArg targetNameArg) { string name = ""; Player player; - if (!targetNameArg.IsEmpty()) + if (targetNameArg.HasValue) { name = targetNameArg; ObjectManager.NormalizePlayerName(ref name); @@ -1830,7 +1829,7 @@ namespace Game.Chat } else { - if (!targetNameArg.IsEmpty()) + if (!targetNameArg.HasValue) { // Check for offline players ObjectGuid guid = Global.CharacterCacheStorage.GetCharacterGuidByName(name); @@ -2022,83 +2021,71 @@ namespace Game.Chat [CommandGroup("additem")] class MiscAddItemCommands { - [Command("", RBACPermissions.CommandAdditem)] - static bool HandleAddItemCommand(CommandHandler handler, StringArguments args) + static bool HandleAddItemCommandHelper(CommandHandler handler, Player player, Player playerTarget, VariantArg itemArg, OptionalArg countArg, OptionalArg bonusListIdString, OptionalArg itemContextArg) { - if (args.Empty()) - return false; - uint itemId = 0; - - if (args[0] == '[') // [name] manual form + List bonusListIDs = new(); + ItemContext itemContext = ItemContext.None; + if (itemArg.Is()) { - string itemName = args.NextString("]").Substring(1); + ItemLinkData itemLinkData = itemArg.GetValue(); + itemId = itemLinkData.Item.GetId(); + bonusListIDs = itemLinkData.ItemBonusListIDs; + itemContext = (ItemContext)itemLinkData.Context; + } + else if (itemArg.Is()) + itemId = itemArg; + else if (itemArg.Is()) + { + string itemName = itemArg; + if (itemName.StartsWith('[')) + itemName.Remove(0, 1); + if (itemName.EndsWith(']')) + itemName.Remove(itemName.Length - 1, 1); - if (!string.IsNullOrEmpty(itemName)) + var record = CliDB.ItemSparseStorage.Values.FirstOrDefault(sparse => { - var record = CliDB.ItemSparseStorage.Values.FirstOrDefault(itemSparse => - { - for (Locale i = 0; i < Locale.Total; ++i) - if (itemName == itemSparse.Display[i]) - return true; - return false; - }); + for (Locale i = Locale.enUS; i < Locale.Total; i += 1) + if (itemName == sparse.Display[i]) + return true; + return false; + }); - if (record == null) - { - handler.SendSysMessage(CypherStrings.CommandCouldnotfind, itemName); - return false; - } - itemId = record.Id; + if (record == null) + { + handler.SendSysMessage(CypherStrings.CommandCouldnotfind, itemName); + return false; } - else - return false; - } - else // item_id or [name] Shift-click form |color|Hitem:item_id:0:0:0|h[name]|h|r - { - string idStr = handler.ExtractKeyFromLink(args, "Hitem"); - if (string.IsNullOrEmpty(idStr)) - return false; - if (!uint.TryParse(idStr, out itemId)) - return false; + itemId = record.Id; } - int count = args.NextInt32(); + int count = countArg.GetValueOrDefault(1); if (count == 0) count = 1; - List bonusListIDs = new(); - var bonuses = args.NextString(); - var context = args.NextString(); - // semicolon separated bonuslist ids (parse them after all arguments are extracted by strtok!) - if (!bonuses.IsEmpty()) + if (bonusListIdString.HasValue) { - var tokens = new StringArray(bonuses, ';'); - for (var i = 0; i < tokens.Length; ++i) + var tokens = new StringArray(bonusListIdString.Value, ';'); + foreach (string token in tokens) { - if (uint.TryParse(tokens[i], out uint id)) - bonusListIDs.Add(id); + if (uint.TryParse(token, out uint bonusListId)) + bonusListIDs.Add(bonusListId); } } - ItemContext itemContext = ItemContext.None; - if (!context.IsEmpty()) + if (itemContextArg.HasValue) { - itemContext = context.ToEnum(); + itemContext = (ItemContext)itemContextArg.Value; if (itemContext < ItemContext.Max) { var contextBonuses = ItemBonusMgr.GetBonusListsForItem(itemId, new(itemContext)); bonusListIDs.AddRange(contextBonuses); + bonusListIDs.Sort(); } } - Player player = handler.GetSession().GetPlayer(); - Player playerTarget = handler.GetSelectedPlayer(); - if (playerTarget == null) - playerTarget = player; - ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemId); if (itemTemplate == null) { @@ -2129,15 +2116,14 @@ namespace Game.Chat // failed to destroy items of the amount requested handler.SendSysMessage(CypherStrings.RemoveitemFailure, itemId, -count, handler.GetNameLink(playerTarget)); } + return true; } // Adding items - uint noSpaceForCount = 0; - // check space and find places List dest = new(); - InventoryResult msg = playerTarget.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, itemId, (uint)count, out noSpaceForCount); + InventoryResult msg = playerTarget.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, itemId, (uint)count, out uint noSpaceForCount); if (msg != InventoryResult.Ok) // convert to possible store amount count -= (int)noSpaceForCount; @@ -2147,14 +2133,15 @@ namespace Game.Chat return false; } - Item item = playerTarget.StoreNewItem(dest, itemId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(itemId), null, itemContext, bonusListIDs.Empty() ? null : bonusListIDs); + Item item = playerTarget.StoreNewItem(dest, itemId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(itemId), null, itemContext, + bonusListIDs.Empty() ? null : bonusListIDs); // remove binding (let GM give it to another player later) if (player == playerTarget) { - foreach (var posCount in dest) + foreach (var itemPosCount in dest) { - Item item1 = player.GetItemByPos(posCount.pos); + Item item1 = player.GetItemByPos(itemPosCount.pos); if (item1 != null) item1.SetBinding(false); } @@ -2172,10 +2159,21 @@ namespace Game.Chat handler.SendSysMessage(CypherStrings.ItemCannotCreate, itemId, noSpaceForCount); return true; + + + } + + [Command("", RBACPermissions.CommandAdditem, true)] + static bool HandleAddItemCommand(CommandHandler handler, VariantArg item, OptionalArg countArg, OptionalArg bonusListIdString, OptionalArg itemContextArg) + { + Player player = handler.GetSession().GetPlayer(); + Player playerTarget = handler.GetSelectedPlayerOrSelf(); + + return HandleAddItemCommandHelper(handler, player, playerTarget, item, countArg, bonusListIdString, itemContextArg); } [Command("set", RBACPermissions.CommandAdditemset)] - static bool HandleAddItemSetCommand(CommandHandler handler, uint itemSetId, [OptionalArg] string bonuses, byte? context) + static bool HandleAddItemSetCommand(CommandHandler handler, VariantArg itemSetId, OptionalArg bonuses, OptionalArg context) { // prevent generation all items with itemset field value '0' if (itemSetId == 0) @@ -2187,7 +2185,7 @@ namespace Game.Chat List bonusListIDs = new(); // semicolon separated bonuslist ids (parse them after all arguments are extracted by strtok!) - if (!bonuses.IsEmpty()) + if (bonuses.HasValue) { var tokens = new StringArray(bonuses, ';'); for (var i = 0; i < tokens.Length; ++i) @@ -2255,164 +2253,16 @@ namespace Game.Chat } [Command("to", RBACPermissions.CommandAdditemset)] - static bool HandleAddItemToCommand(CommandHandler handler, StringArguments args) + static bool HandleAddItemToCommand(CommandHandler handler, PlayerIdentifier target, VariantArg item, OptionalArg countArg, OptionalArg bonusListIdString, OptionalArg itemContextArg) { - if (args.Empty()) - return false; - Player player = handler.GetSession().GetPlayer(); - Player playerTarget = null; - if (!handler.ExtractPlayerTarget(args, out playerTarget)) - return false; - - StringArguments tailArgs = new StringArguments(args.NextString("")); - if (tailArgs.Empty()) - return false; - - uint itemId = 0; - - if (tailArgs[0] == '[') // [name] manual form + if (!target.IsConnected()) { - string itemNameStr = tailArgs.NextString("]"); - - if (!itemNameStr.IsEmpty()) - { - string itemName = itemNameStr.Substring(1); - var itr = CliDB.ItemSparseStorage.Values.FirstOrDefault(sparse => - { - for (Locale i = Locale.enUS; i < Locale.Total; ++i) - if (itemName == sparse.Display[i]) - return true; - return false; - }); - - if (itr == null) - { - handler.SendSysMessage(CypherStrings.CommandCouldnotfind, itemName); - return false; - } - - itemId = itr.Id; - } - else - return false; - } - else // item_id or [name] Shift-click form |color|Hitem:item_id:0:0:0|h[name]|h|r - { - string id = handler.ExtractKeyFromLink(tailArgs, "Hitem"); - if (id.IsEmpty()) - return false; - itemId = uint.Parse(id); - } - - string ccount = tailArgs.NextString(); - - int count = 1; - if (!ccount.IsEmpty()) - count = int.Parse(ccount); - - if (count == 0) - count = 1; - - List bonusListIDs = new(); - string bonuses = tailArgs.NextString(); - - string context = tailArgs.NextString(); - - ItemContext itemContext = ItemContext.None; - if (!context.IsEmpty()) - { - itemContext = context.ToEnum(); - if (itemContext < ItemContext.Max) - { - var contextBonuses = ItemBonusMgr.GetBonusListsForItem(itemId, new(itemContext)); - bonusListIDs.AddRange(contextBonuses); - } - } - - // semicolon separated bonuslist ids - if (!bonuses.IsEmpty()) - { - foreach (var token in bonuses.Split(';', StringSplitOptions.RemoveEmptyEntries)) - { - if (uint.TryParse(token, out uint bonusListId)) - bonusListIDs.Add(bonusListId); - } - } - - ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemId); - if (itemTemplate == null) - { - handler.SendSysMessage(CypherStrings.CommandItemidinvalid, itemId); + handler.SendSysMessage(CypherStrings.PlayerNotFound); return false; } - // Subtract - if (count < 0) - { - uint destroyedItemCount = playerTarget.DestroyItemCount(itemId, (uint)-count, true, false); - - if (destroyedItemCount > 0) - { - // output the amount of items successfully destroyed - handler.SendSysMessage(CypherStrings.Removeitem, itemId, destroyedItemCount, handler.GetNameLink(playerTarget)); - - // check to see if we were unable to destroy all of the amount requested. - uint unableToDestroyItemCount = (uint)(-count - destroyedItemCount); - if (unableToDestroyItemCount > 0) - { - // output message for the amount of items we couldn't destroy - handler.SendSysMessage(CypherStrings.RemoveitemFailure, itemId, unableToDestroyItemCount, handler.GetNameLink(playerTarget)); - } - } - else - { - // failed to destroy items of the amount requested - handler.SendSysMessage(CypherStrings.RemoveitemFailure, itemId, -count, handler.GetNameLink(playerTarget)); - } - - return true; - } - - // Adding items - uint noSpaceForCount = 0; - - // check space and find places - List dest = new(); - InventoryResult msg = playerTarget.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, itemId, (uint)count, out noSpaceForCount); - if (msg != InventoryResult.Ok) // convert to possible store amount - count -= (int)noSpaceForCount; - - if (count == 0 || dest.Empty()) // can't add any - { - handler.SendSysMessage(CypherStrings.ItemCannotCreate, itemId, noSpaceForCount); - return false; - } - - Item item = playerTarget.StoreNewItem(dest, itemId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(itemId), null, itemContext, bonusListIDs.Empty() ? null : bonusListIDs); - - // remove binding (let GM give it to another player later) - if (player == playerTarget) - { - foreach (var itemPostCount in dest) - { - Item item1 = player.GetItemByPos(itemPostCount.pos); - if (item1 != null) - item1.SetBinding(false); - } - } - - if (count > 0 && item != null) - { - player.SendNewItem(item, (uint)count, false, true); - if (player != playerTarget) - playerTarget.SendNewItem(item, (uint)count, true, false); - } - - if (noSpaceForCount > 0) - handler.SendSysMessage(CypherStrings.ItemCannotCreate, itemId, noSpaceForCount); - - return true; + return HandleAddItemCommandHelper(handler, player, target.GetConnectedPlayer(), item, countArg, bonusListIdString, itemContextArg); } } } \ No newline at end of file diff --git a/Source/Game/Chat/Commands/ModifyCommands.cs b/Source/Game/Chat/Commands/ModifyCommands.cs index aea3ed90c..771ae38d7 100644 --- a/Source/Game/Chat/Commands/ModifyCommands.cs +++ b/Source/Game/Chat/Commands/ModifyCommands.cs @@ -94,7 +94,7 @@ namespace Game.Chat } [Command("faction", RBACPermissions.CommandModifyFaction)] - static bool HandleModifyFactionCommand(CommandHandler handler, uint? factionid, uint? flag, ulong? npcflag, uint? dyflag) + static bool HandleModifyFactionCommand(CommandHandler handler, OptionalArg factionid, OptionalArg flag, OptionalArg npcflag, OptionalArg dyflag) { Creature target = handler.GetSelectedCreature(); if (target == null) @@ -104,13 +104,13 @@ namespace Game.Chat } if (!flag.HasValue) - flag = target.m_unitData.Flags; + flag = (uint)target.m_unitData.Flags; if (!npcflag.HasValue) npcflag = (ulong)target.m_unitData.NpcFlags2 << 32 | target.m_unitData.NpcFlags; if (!dyflag.HasValue) - dyflag = target.m_objectData.DynamicFlags; + dyflag = (uint)target.m_objectData.DynamicFlags; if (!factionid.HasValue) { @@ -127,10 +127,10 @@ namespace Game.Chat handler.SendSysMessage(CypherStrings.YouChangeFaction, target.GetGUID().ToString(), factionid.Value, flag.Value, npcflag.Value, dyflag.Value); target.SetFaction(factionid.Value); - target.ReplaceAllUnitFlags((UnitFlags)flag); + target.ReplaceAllUnitFlags((UnitFlags)flag.Value); target.ReplaceAllNpcFlags((NPCFlags)(npcflag & 0xFFFFFFFF)); target.ReplaceAllNpcFlags2((NPCFlags2)(npcflag >> 32)); - target.ReplaceAllDynamicFlags((UnitDynFlags)dyflag); + target.ReplaceAllDynamicFlags((UnitDynFlags)dyflag.Value); return true; } @@ -427,7 +427,7 @@ namespace Game.Chat } [Command("phase", RBACPermissions.CommandModifyPhase)] - static bool HandleModifyPhaseCommand(CommandHandler handler, uint phaseId, uint? visibleMapId) + static bool HandleModifyPhaseCommand(CommandHandler handler, uint phaseId, OptionalArg visibleMapId) { if (phaseId != 0 && !CliDB.PhaseStorage.ContainsKey(phaseId)) { diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index ddb09fa4c..b43717b1f 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -47,7 +47,7 @@ namespace Game.Chat } [Command("evade", RBACPermissions.CommandNpcEvade)] - static bool HandleNpcEvadeCommand(CommandHandler handler, EvadeReason? why, string force) + static bool HandleNpcEvadeCommand(CommandHandler handler, OptionalArg why, string force) { Creature creatureTarget = handler.GetSelectedCreature(); if (creatureTarget == null || creatureTarget.IsPet()) @@ -165,7 +165,7 @@ namespace Game.Chat } [Command("move", RBACPermissions.CommandNpcMove)] - static bool HandleNpcMoveCommand(CommandHandler handler, ulong? spawnId) + static bool HandleNpcMoveCommand(CommandHandler handler, OptionalArg spawnId) { Creature creature = handler.GetSelectedCreature(); Player player = handler.GetSession().GetPlayer(); @@ -214,7 +214,7 @@ namespace Game.Chat } [Command("near", RBACPermissions.CommandNpcNear)] - static bool HandleNpcNearCommand(CommandHandler handler, float? dist) + static bool HandleNpcNearCommand(CommandHandler handler, OptionalArg dist) { float distance = dist.GetValueOrDefault(10.0f); uint count = 0; @@ -699,7 +699,7 @@ namespace Game.Chat } [Command("item", RBACPermissions.CommandNpcAddItem)] - static bool HandleNpcAddVendorItemCommand(CommandHandler handler, uint itemId, uint? mc, uint? it, uint? ec, [OptionalArg] string bonusListIds) + static bool HandleNpcAddVendorItemCommand(CommandHandler handler, uint itemId, OptionalArg mc, OptionalArg it, OptionalArg ec, OptionalArg bonusListIds) { if (itemId == 0) { @@ -726,7 +726,7 @@ namespace Game.Chat vItem.ExtendedCost = extendedcost; vItem.Type = ItemVendorType.Item; - if (!bonusListIds.IsEmpty()) + if (bonusListIds.HasValue) { var bonusListIDsTok = new StringArray(bonusListIds, ';'); if (!bonusListIDsTok.IsEmpty()) @@ -773,7 +773,7 @@ namespace Game.Chat } [Command("formation", RBACPermissions.CommandNpcAddFormation)] - static bool HandleNpcAddFormationCommand(CommandHandler handler, ulong leaderGUID, bool? linkedAggro, bool? formationMovement) + static bool HandleNpcAddFormationCommand(CommandHandler handler, ulong leaderGUID, OptionalArg linkedAggro, OptionalArg formationMovement) { Creature creature = handler.GetSelectedCreature(); if (creature == null || creature.GetSpawnId() == 0) @@ -819,14 +819,14 @@ namespace Game.Chat } [Command("temp", RBACPermissions.CommandNpcAddTemp)] - static bool HandleNpcAddTempSpawnCommand(CommandHandler handler, [OptionalArg] string lootStr, uint id) + static bool HandleNpcAddTempSpawnCommand(CommandHandler handler, OptionalArg lootStr, uint id) { bool loot = false; - if (!lootStr.IsEmpty()) + if (lootStr.HasValue) { - if (lootStr.Equals("loot", StringComparison.OrdinalIgnoreCase)) + if (lootStr.Value.Equals("loot", StringComparison.OrdinalIgnoreCase)) loot = true; - else if (lootStr.Equals("noloot", StringComparison.OrdinalIgnoreCase)) + else if (lootStr.Value.Equals("noloot", StringComparison.OrdinalIgnoreCase)) loot = false; else return false; @@ -846,7 +846,7 @@ namespace Game.Chat class DeleteCommands { [Command("", RBACPermissions.CommandNpcDelete)] - static bool HandleNpcDeleteCommand(CommandHandler handler, ulong? spawnIdArg) + static bool HandleNpcDeleteCommand(CommandHandler handler, OptionalArg spawnIdArg) { ulong spawnId; if (spawnIdArg.HasValue) @@ -1151,7 +1151,7 @@ namespace Game.Chat } [Command("movetype", RBACPermissions.CommandNpcSetMovetype)] - static bool HandleNpcSetMoveTypeCommand(CommandHandler handler, ulong? lowGuid, string type, string nodel) + static bool HandleNpcSetMoveTypeCommand(CommandHandler handler, OptionalArg lowGuid, string type, string nodel) { // 3 arguments: // GUID (optional - you can also select the creature) diff --git a/Source/Game/Chat/Commands/PetCommands.cs b/Source/Game/Chat/Commands/PetCommands.cs index a5fe513d1..d308d2496 100644 --- a/Source/Game/Chat/Commands/PetCommands.cs +++ b/Source/Game/Chat/Commands/PetCommands.cs @@ -119,7 +119,7 @@ namespace Game.Chat } [Command("level", RBACPermissions.CommandPetLevel)] - static bool HandlePetLevelCommand(CommandHandler handler, int? level) + static bool HandlePetLevelCommand(CommandHandler handler, OptionalArg level) { Pet pet = GetSelectedPlayerPetOrOwn(handler); Player owner = pet != null ? pet.GetOwner() : null; diff --git a/Source/Game/Chat/Commands/RbacCommands.cs b/Source/Game/Chat/Commands/RbacCommands.cs index 5c7588c5d..c8ca4e917 100644 --- a/Source/Game/Chat/Commands/RbacCommands.cs +++ b/Source/Game/Chat/Commands/RbacCommands.cs @@ -13,7 +13,7 @@ namespace Game.Chat.Commands class RbacComands { [Command("list", RBACPermissions.CommandRbacList, true)] - static bool HandleRBACListPermissionsCommand(CommandHandler handler, uint? permId) + static bool HandleRBACListPermissionsCommand(CommandHandler handler, OptionalArg permId) { if (!permId.HasValue) { @@ -49,7 +49,7 @@ namespace Game.Chat.Commands class RbacAccountCommands { [Command("deny", RBACPermissions.CommandRbacAccPermDeny, true)] - static bool HandleRBACPermDenyCommand(CommandHandler handler, AccountIdentifier account, uint permId, int? realmId) + static bool HandleRBACPermDenyCommand(CommandHandler handler, AccountIdentifier account, uint permId, OptionalArg realmId) { if (account == null) account = AccountIdentifier.FromTarget(handler); @@ -92,7 +92,7 @@ namespace Game.Chat.Commands } [Command("grant", RBACPermissions.CommandRbacAccPermGrant, true)] - static bool HandleRBACPermGrantCommand(CommandHandler handler, AccountIdentifier account, uint permId, int? realmId) + static bool HandleRBACPermGrantCommand(CommandHandler handler, AccountIdentifier account, uint permId, OptionalArg realmId) { if (account == null) account = AccountIdentifier.FromTarget(handler); @@ -186,7 +186,7 @@ namespace Game.Chat.Commands } [Command("revoke", RBACPermissions.CommandRbacAccPermRevoke, true)] - static bool HandleRBACPermRevokeCommand(CommandHandler handler, AccountIdentifier account, uint permId, int? realmId) + static bool HandleRBACPermRevokeCommand(CommandHandler handler, AccountIdentifier account, uint permId, OptionalArg realmId) { if (account == null) account = AccountIdentifier.FromTarget(handler); diff --git a/Source/Game/Chat/Commands/SceneCommands.cs b/Source/Game/Chat/Commands/SceneCommands.cs index c8c7b52f0..f2bb01e3b 100644 --- a/Source/Game/Chat/Commands/SceneCommands.cs +++ b/Source/Game/Chat/Commands/SceneCommands.cs @@ -59,7 +59,7 @@ namespace Game.Chat } [Command("playpackage", RBACPermissions.CommandScenePlayPackage)] - static bool HandlePlayScenePackageCommand(CommandHandler handler, uint sceneScriptPackageId, SceneFlags? flags) + static bool HandlePlayScenePackageCommand(CommandHandler handler, uint sceneScriptPackageId, OptionalArg flags) { Player target = handler.GetSelectedPlayerOrSelf(); if (target == null) diff --git a/Source/Game/Chat/Commands/SpellCommands.cs b/Source/Game/Chat/Commands/SpellCommands.cs index 0a83860cb..2e8273219 100644 --- a/Source/Game/Chat/Commands/SpellCommands.cs +++ b/Source/Game/Chat/Commands/SpellCommands.cs @@ -13,7 +13,7 @@ namespace Game.Chat class SpellCommands { [CommandNonGroup("cooldown", RBACPermissions.CommandCooldown)] - static bool HandleCooldownCommand(CommandHandler handler, uint? spellIdArg) + static bool HandleCooldownCommand(CommandHandler handler, OptionalArg spellIdArg) { Unit target = handler.GetSelectedUnit(); if (target == null) @@ -38,16 +38,15 @@ namespace Game.Chat } else { - SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellIdArg.Value, target.GetMap().GetDifficultyID()); - if (spellInfo == null) + if (spellIdArg.Value == null) { handler.SendSysMessage(CypherStrings.UnknownSpell, owner == handler.GetSession().GetPlayer() ? handler.GetCypherString(CypherStrings.You) : nameLink); return false; } - target.GetSpellHistory().ResetCooldown(spellInfo.Id, true); - target.GetSpellHistory().ResetCharges(spellInfo.ChargeCategoryId); - handler.SendSysMessage(CypherStrings.RemoveallCooldown, spellInfo.Id, owner == handler.GetSession().GetPlayer() ? handler.GetCypherString(CypherStrings.You) : nameLink); + target.GetSpellHistory().ResetCooldown(spellIdArg.Value.Id, true); + target.GetSpellHistory().ResetCharges(spellIdArg.Value.ChargeCategoryId); + handler.SendSysMessage(CypherStrings.RemoveallCooldown, spellIdArg.Value.Id, owner == handler.GetSession().GetPlayer() ? handler.GetCypherString(CypherStrings.You) : nameLink); } return true; } @@ -104,7 +103,7 @@ namespace Game.Chat } [CommandNonGroup("setskill", RBACPermissions.CommandSetskill)] - static bool HandleSetSkillCommand(CommandHandler handler, uint skillId, uint level, uint? maxSkillArg) + static bool HandleSetSkillCommand(CommandHandler handler, VariantArg skillId, uint level, OptionalArg maxSkillArg) { Player target = handler.GetSelectedPlayerOrSelf(); if (target == null) @@ -120,11 +119,11 @@ namespace Game.Chat return false; } - bool targetHasSkill = target.GetSkillValue((SkillType)skillId) != 0; + bool targetHasSkill = target.GetSkillValue((SkillType)skillId.GetValue()) != 0; // If our target does not yet have the skill they are trying to add to them, the chosen level also becomes // the max level of the new profession. - ushort max = (ushort)maxSkillArg.GetValueOrDefault(targetHasSkill ? target.GetPureMaxSkillValue((SkillType)skillId) : level); + ushort max = (ushort)maxSkillArg.GetValueOrDefault(targetHasSkill ? target.GetPureMaxSkillValue((SkillType)skillId.GetValue()) : level); if (level == 0 || level > max) return false; @@ -132,7 +131,7 @@ namespace Game.Chat // If the player has the skill, we get the current skill step. If they don't have the skill, we // add the skill to the player's book with step 1 (which is the first rank, in most cases something // like 'Apprentice '. - target.SetSkill((SkillType)skillId, (uint)(targetHasSkill ? target.GetSkillStep((SkillType)skillId) : 1), level, max); + target.SetSkill((SkillType)skillId.GetValue(), (uint)(targetHasSkill ? target.GetSkillStep((SkillType)skillId.GetValue()) : 1), level, max); handler.SendSysMessage(CypherStrings.SetSkill, skillId, skillLine.DisplayName[handler.GetSessionDbcLocale()], handler.GetNameLink(target), level, max); return true; } diff --git a/Source/Game/Chat/Commands/TeleCommands.cs b/Source/Game/Chat/Commands/TeleCommands.cs index 24474aa5b..4494db743 100644 --- a/Source/Game/Chat/Commands/TeleCommands.cs +++ b/Source/Game/Chat/Commands/TeleCommands.cs @@ -223,23 +223,23 @@ namespace Game.Chat class TeleNameCommands { [Command("", RBACPermissions.CommandTeleName, true)] - static bool HandleTeleNameCommand(CommandHandler handler, [OptionalArg] PlayerIdentifier player, [VariantArg] object where) + static bool HandleTeleNameCommand(CommandHandler handler, OptionalArg player, VariantArg where) { - if (player == null) + if (!player.HasValue) player = PlayerIdentifier.FromTargetOrSelf(handler); - if (player == null) + if (player.Value == null) return false; - Player target = player.GetConnectedPlayer(); + Player target = player.Value.GetConnectedPlayer(); - if (where is string && where.Equals("$home")) // References target's homebind + if (where.Is() && ((string)where).Equals("$home")) // References target's homebind { if (target != null) target.TeleportTo(target.GetHomebind()); else { PreparedStatement stmt = CharacterDatabase.GetPreparedStatement(CharStatements.SEL_CHAR_HOMEBIND); - stmt.AddValue(0, player.GetGUID().GetCounter()); + stmt.AddValue(0, player.Value.GetGUID().GetCounter()); SQLResult result = DB.Characters.Query(stmt); if (!result.IsEmpty()) @@ -247,7 +247,7 @@ namespace Game.Chat WorldLocation loc = new(result.Read(0), result.Read(2), result.Read(3), result.Read(4), 0.0f); uint zoneId = result.Read(1); - Player.SavePositionInDB(loc, zoneId, player.GetGUID()); + Player.SavePositionInDB(loc, zoneId, player.Value.GetGUID()); } } @@ -255,7 +255,7 @@ namespace Game.Chat } // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r - GameTele tele = where as GameTele; + GameTele tele = where; return DoNameTeleport(handler, player, tele.mapId, new Position(tele.posX, tele.posY, tele.posZ, tele.orientation), tele.name); } diff --git a/Source/Game/Chat/Commands/WPCommands.cs b/Source/Game/Chat/Commands/WPCommands.cs index 1f034b827..85ac0f0a0 100644 --- a/Source/Game/Chat/Commands/WPCommands.cs +++ b/Source/Game/Chat/Commands/WPCommands.cs @@ -16,7 +16,7 @@ namespace Game.Chat.Commands class WPCommands { [Command("add", RBACPermissions.CommandWpAdd)] - static bool HandleWpAddCommand(CommandHandler handler, uint? pathId) + static bool HandleWpAddCommand(CommandHandler handler, OptionalArg pathId) { Creature target = handler.GetSelectedCreature(); @@ -91,7 +91,7 @@ namespace Game.Chat.Commands } [Command("load", RBACPermissions.CommandWpLoad)] - static bool HandleWpLoadCommand(CommandHandler handler, uint? pathId) + static bool HandleWpLoadCommand(CommandHandler handler, OptionalArg pathId) { Creature target = handler.GetSelectedCreature(); if (target == null) @@ -224,7 +224,7 @@ namespace Game.Chat.Commands } [Command("show", RBACPermissions.CommandWpShow)] - static bool HandleWpShowCommand(CommandHandler handler, string subCommand, uint? pathId) + static bool HandleWpShowCommand(CommandHandler handler, string subCommand, OptionalArg pathId) { // first arg: on, off, first, last if (subCommand.IsEmpty()) diff --git a/Source/Game/Chat/Hyperlink.cs b/Source/Game/Chat/Hyperlink.cs index 06064fe8b..c1170a23c 100644 --- a/Source/Game/Chat/Hyperlink.cs +++ b/Source/Game/Chat/Hyperlink.cs @@ -12,16 +12,26 @@ namespace Game.Chat { value = default; - HyperlinkInfo info = ParseHyperlink(arg); + HyperlinkInfo info = ParseSingleHyperlink(arg); // invalid hyperlinks cannot be consumed if (info == null) return default; + if (!typeof(IHyperlink).IsAssignableFrom(type)) + return default; + + IHyperlink hyperLinkValue = (IHyperlink)Activator.CreateInstance(type); + + // check if we got the right tag + if (info.Tag != hyperLinkValue.tag()) + return default; + // store value - HyperlinkDataTokenizer t = new(info.Data, true); - if (!t.TryConsumeTo(out value, type)) + if (!hyperLinkValue.Parse(info.Data)) return new ChatCommandResult(handler.GetCypherString(CypherStrings.CmdparserLinkdataInvalid)); + value = hyperLinkValue; + // finally, skip any potential delimiters var (token, next) = info.Tail.Tokenize(); if (token.IsEmpty()) /* empty token = first character is delimiter, skip past it */ @@ -61,7 +71,7 @@ namespace Game.Chat continue; } - HyperlinkInfo info = ParseHyperlink(str.Substring(pos)); + HyperlinkInfo info = ParseSingleHyperlink(str.Substring(pos)); if (info == null)// todo fix me || !ValidateLinkInfo(info)) return false; @@ -74,107 +84,126 @@ namespace Game.Chat return true; } - static byte toHex(char c) { return (byte)((c >= '0' && c <= '9') ? c - '0' + 0x10 : (c >= 'a' && c <= 'f') ? c - 'a' + 0x1a : 0x00); } - - public static HyperlinkInfo ParseHyperlink(string currentString) + public static HyperlinkInfo ParseSingleHyperlink(string str) { - if (currentString.IsEmpty()) + if (str.IsEmpty()) return null; + string color = ""; + string tag = ""; + string data = ""; + string text = ""; + int pos = 0; //color tag - if (currentString[pos++] != '|' || currentString[pos++] != 'c') + if (str[pos++] != '|' || str[pos++] != 'c') return null; - uint color = 0; - for (byte i = 0; i < 8; ++i) + if (str.Length < 8) + return null; + + if (str[pos] == 'n') { - byte hex = toHex(currentString[pos++]); - if (hex != 0) - color = (uint)((int)(color << 4) | (hex & 0xf)); + // numeric color id + pos++; + int endOfColor = str.IndexOf(":", pos); + if (endOfColor != -1) + { + color = str.Substring(pos, endOfColor - pos); + pos = endOfColor + 1; + } else return null; } - - // link data start tag - if (currentString[pos++] != '|' || currentString[pos++] != 'H') - return null; - - // link tag, find next : or | - int tagStart = pos; - int tagLength = 0; - while (pos < currentString.Length && currentString[pos] != '|' && currentString[pos++] != ':') // we only advance pointer to one past if the last thing is : (not for |), this is intentional! - ++tagLength; - - // ok, link data, skip to next | - int dataStart = pos; - int dataLength = 0; - while (pos < currentString.Length && currentString[pos++] != '|') - ++dataLength; - - // ok, next should be link data end tag... - if (currentString[pos++] != 'h') - return null; - - // then visible link text, starts with [ - if (currentString[pos++] != '[') - return null; - - // skip until we hit the next ], abort on unexpected | - int textStart = pos; - int textLength = 0; - while (pos < currentString.Length) + else { - if (currentString[pos] == '|') - return null; - - if (currentString[pos++] == ']') - break; - - ++textLength; + // hex color + color = str.Substring(pos, 8); + pos += 8; } - // link end tag - if (currentString[pos++] != '|' || currentString[pos++] != 'h' || currentString[pos++] != '|' || currentString[pos++] != 'r') + if (str[pos++] != '|' || str[pos++] != 'H') + return null; + + // tag+data part follows + int delimPos = str.IndexOf('|', pos); + if (delimPos != -1) + { + tag = str.Substring(pos, delimPos - pos); + pos = (delimPos + 1); + } + else + return null; + + // split tag if : is present (data separator) + int dataStart = tag.IndexOf(':'); + if (dataStart != -1) + { + data = tag.Substring(dataStart + 1); + tag = tag.Substring(0, dataStart); + } + + // ok, next should be link data end tag... + if (str[pos++] != 'h') + return null; + + // extract text, must be between [] + if (str[pos] != '[') + return null; + + int openBrackets = 0; + for (int nameItr = pos; nameItr < str.Length; ++nameItr) + { + switch (str[nameItr]) + { + case '[': + ++openBrackets; + break; + case ']': + --openBrackets; + break; + default: + break; + } + + if (openBrackets == 0) + { + text = str.Substring(pos + 1, (nameItr - 1) - pos); + pos = nameItr + 1; + break; + } + } + + // check end tag + if (str[pos++] != '|' || str[pos++] != 'h' || str[pos++] != '|' || str[pos++] != 'r') return null; // ok, valid hyperlink, return info - return new HyperlinkInfo(currentString.Substring(pos), color, currentString.Substring(tagStart, tagLength), currentString.Substring(dataStart, dataLength), currentString.Substring(textStart, textLength)); - } + return new(str.Substring(pos), color, tag, data, text); + } } class HyperlinkInfo { - public HyperlinkInfo(string t = null, uint c = 0, string tag = null, string data = null, string text = null) - { - Tail = t; - color = new(c); - Tag = tag; - Data = data; - Text = text; - } - public string Tail; public HyperlinkColor color; public string Tag; public string Data; public string Text; - } - struct HyperlinkColor - { - public byte r; - public byte g; - public byte b; - public byte a; - - public HyperlinkColor(uint c) + public HyperlinkInfo(string t, string c, string ta, string d, string te) { - r = (byte)(c >> 16); - g = (byte)(c >> 8); - b = (byte)c; - a = (byte)(c >> 24); + Tail = t; + color = new(c); + Tag = ta; + Data = d; + Text = te; } } -} + + struct HyperlinkColor(string c) + { + public string data = c; + } +} \ No newline at end of file diff --git a/Source/Game/Chat/HyperlinkTags.cs b/Source/Game/Chat/HyperlinkTags.cs index d34d2c537..dc8602899 100644 --- a/Source/Game/Chat/HyperlinkTags.cs +++ b/Source/Game/Chat/HyperlinkTags.cs @@ -1,6 +1,7 @@ // 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 Framework.Constants; using Framework.IO; using Game.DataStorage; using Game.Entities; @@ -18,85 +19,85 @@ namespace Game.Chat _allowEmptyTokens = allowEmptyTokens; } - public bool TryConsumeTo(out dynamic val, Type type) + public bool TryConsumeTo(out T val) { val = default; if (IsEmpty()) return _allowEmptyTokens; - switch (Type.GetTypeCode(type)) + switch (Type.GetTypeCode(typeof(T))) { case TypeCode.SByte: - val = _arg.NextByte(":"); + val = (dynamic)_arg.NextByte(":"); return true; case TypeCode.Int16: - val = _arg.NextUInt16(":"); + val = (dynamic)_arg.NextUInt16(":"); return true; case TypeCode.Int32: - val = _arg.NextUInt32(":"); + val = (dynamic)_arg.NextUInt32(":"); return true; case TypeCode.Int64: - val = _arg.NextUInt64(":"); + val = (dynamic)_arg.NextUInt64(":"); return true; case TypeCode.Byte: - val = _arg.NextByte(":"); + val = (dynamic)_arg.NextByte(":"); return true; case TypeCode.UInt16: - val = _arg.NextUInt16(":"); + val = (dynamic)_arg.NextUInt16(":"); return true; case TypeCode.UInt32: - val = _arg.NextUInt32(":"); + val = (dynamic)_arg.NextUInt32(":"); return true; case TypeCode.UInt64: - val = _arg.NextUInt64(":"); + val = (dynamic)_arg.NextUInt64(":"); return true; case TypeCode.Single: - val = _arg.NextSingle(":"); + val = (dynamic)_arg.NextSingle(":"); return true; case TypeCode.Double: - val = _arg.NextDouble(":"); + val = (dynamic)_arg.NextDouble(":"); return true; case TypeCode.String: - val = _arg.NextString(":"); + val = (dynamic)_arg.NextString(":"); return true; case TypeCode.Object: { - switch (type.Name) + switch (typeof(T).Name) { case nameof(AchievementRecord): - val = CliDB.AchievementStorage.LookupByKey(_arg.NextUInt32(":")); + val = (dynamic)CliDB.AchievementStorage.LookupByKey(_arg.NextUInt32(":")); if (val != null) return true; break; case nameof(CurrencyTypesRecord): - val = CliDB.CurrencyTypesStorage.LookupByKey(_arg.NextUInt32(":")); + val = (dynamic)CliDB.CurrencyTypesStorage.LookupByKey(_arg.NextUInt32(":")); if (val != null) return true; break; case nameof(GameTele): - val = Global.ObjectMgr.GetGameTele(_arg.NextUInt32(":")); + val = (dynamic)Global.ObjectMgr.GetGameTele(_arg.NextUInt32(":")); if (val != null) return true; break; case nameof(ItemTemplate): - val = Global.ObjectMgr.GetItemTemplate(_arg.NextUInt32(":")); + val = (dynamic)Global.ObjectMgr.GetItemTemplate(_arg.NextUInt32(":")); if (val != null) return true; break; case nameof(Quest): - val = Global.ObjectMgr.GetQuestTemplate(_arg.NextUInt32(":")); + val = (dynamic)Global.ObjectMgr.GetQuestTemplate(_arg.NextUInt32(":")); if (val != null) return true; break; case nameof(SpellInfo): - val = Global.SpellMgr.GetSpellInfo(_arg.NextUInt32(":"), Framework.Constants.Difficulty.None); + val = (dynamic)Global.SpellMgr.GetSpellInfo(_arg.NextUInt32(":"), Framework.Constants.Difficulty.None); if (val != null) return true; break; case nameof(ObjectGuid): - val = ObjectGuid.FromString(_arg.NextString(":")); - if (val != ObjectGuid.FromStringFailed) + val = (dynamic)ObjectGuid.FromString(_arg.NextString(":")); + if (val != (dynamic)ObjectGuid.FromStringFailed) return true; break; default: @@ -109,9 +110,1001 @@ namespace Game.Chat return false; } - public bool IsEmpty() { return _arg.Empty(); } + public bool IsEmpty() { return _arg.IsAtEnd(); } StringArguments _arg; bool _allowEmptyTokens; } + + public interface IHyperlink + { + public string tag(); + public bool Parse(string data); + } + + abstract class GenericHyperlink : IHyperlink + { + public T value; + + public abstract string tag(); + public abstract bool Parse(string data); + + public static implicit operator T(GenericHyperlink genericHyperlink) => genericHyperlink.value; + } + + public class AchievementLinkData : IHyperlink + { + public AchievementRecord Achievement; + public ObjectGuid CharacterId; + public bool IsFinished = false; + public int Year; + public int Month; + public int Day; + public uint[] Criteria = new uint[4]; + + public string tag() => "achievement"; + + public bool Parse(string data) + { + HyperlinkDataTokenizer t = new(data); + + uint achievementId; + if (!t.TryConsumeTo(out achievementId)) + return false; + Achievement = CliDB.AchievementStorage.LookupByKey(achievementId); + + if (!(Achievement != null && t.TryConsumeTo(out CharacterId) && t.TryConsumeTo(out IsFinished) && t.TryConsumeTo(out Month) && t.TryConsumeTo(out Day))) + return false; + if ((12 < Month) || (31 < Day)) + return false; + + int year; + if (!t.TryConsumeTo(out year)) + return false; + if (IsFinished) // if finished, year must be >= 0 + { + if (year < 0) + return false; + Year = year; + } + else + Year = 0; + + return (t.TryConsumeTo(out Criteria[0]) && t.TryConsumeTo(out Criteria[1]) && t.TryConsumeTo(out Criteria[2]) && t.TryConsumeTo(out Criteria[3]) && t.IsEmpty()); + } + } + + class ApiLinkData : IHyperlink + { + public string Type; + public string Name; + public string Parent; + + public string tag() => "api"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text, true); + if (!(t.TryConsumeTo(out Type) && t.TryConsumeTo(out Name) && t.TryConsumeTo(out Parent) && t.IsEmpty())) + return false; + return true; + } + } + + class AreaLinkData : GenericHyperlink + { + public override string tag() => "area"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out uint tempValue)) + return true; + + return false; + } + } + + class AreatriggerLinkData : GenericHyperlink + { + public override string tag() => "areatrigger"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out value)) + return true; + + return false; + } + } + + class ArtifactPowerLinkData : IHyperlink + { + public ArtifactPowerRankRecord ArtifactPower; + public byte PurchasedRank; + public byte CurrentRankWithBonus; + + public string tag() => "apower"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint artifactPowerId; + if (!(t.TryConsumeTo(out artifactPowerId) && t.TryConsumeTo(out PurchasedRank) && t.TryConsumeTo(out CurrentRankWithBonus) && t.IsEmpty())) + return false; + if (!CliDB.ArtifactPowerStorage.ContainsKey(artifactPowerId)) + return false; + ArtifactPower = Global.DB2Mgr.GetArtifactPowerRank(artifactPowerId, Math.Max(CurrentRankWithBonus, (byte)1)); + if (ArtifactPower != null) + return false; + return true; + } + } + + class AzeriteEssenceLinkData : IHyperlink + { + public AzeriteEssenceRecord Essence; + public byte Rank; + + public string tag() => "azessence"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint azeriteEssenceId; + if (!t.TryConsumeTo(out azeriteEssenceId)) + return false; + return (Essence = CliDB.AzeriteEssenceStorage.LookupByKey(azeriteEssenceId)) != null && t.TryConsumeTo(out Rank) + && Global.DB2Mgr.GetAzeriteEssencePower(azeriteEssenceId, Rank) != null && t.IsEmpty(); + } + } + + class BattlePetLinkData : IHyperlink + { + public BattlePetSpeciesRecord Species; + public byte Level; + public byte Quality; + public uint MaxHealth; + public uint Power; + public uint Speed; + public ObjectGuid PetGuid; + public uint DisplayId; + + public string tag() => "battlepet"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint battlePetSpeciesId; + if (!t.TryConsumeTo(out battlePetSpeciesId)) + return false; + return (Species = CliDB.BattlePetSpeciesStorage.LookupByKey(battlePetSpeciesId)) != null && t.TryConsumeTo(out Level) + && t.TryConsumeTo(out Quality) && Quality < (int)ItemQuality.Max + && t.TryConsumeTo(out MaxHealth) && t.TryConsumeTo(out Power) && t.TryConsumeTo(out Speed) + && t.TryConsumeTo(out PetGuid) && PetGuid.GetHigh() == HighGuid.BattlePet && t.TryConsumeTo(out DisplayId) + && t.IsEmpty(); + } + } + + class BattlePetAbilLinkData : IHyperlink + { + public BattlePetAbilityRecord Ability; + public uint MaxHealth; + public uint Power; + public uint Speed; + + public string tag() => "battlePetAbil"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint battlePetAbilityId; + if (!t.TryConsumeTo(out battlePetAbilityId)) + return false; + return (Ability = CliDB.BattlePetAbilityStorage.LookupByKey(battlePetAbilityId)) != null + && t.TryConsumeTo(out MaxHealth) && t.TryConsumeTo(out Power) && t.TryConsumeTo(out Speed) + && t.IsEmpty(); + } + } + + class ClubFinderLinkData : GenericHyperlink + { + public override string tag() => "clubFinder"; + + public override bool Parse(string data) + { + ObjectGuid parsed = ObjectGuid.FromString(data); + if (parsed != ObjectGuid.FromStringFailed) + { + value = parsed; + return true; + } + + return false; + } + } + + class ClubTicketLinkData : GenericHyperlink + { + public override string tag() => "clubTicket"; + + public override bool Parse(string data) + { + value = data; + return true; + } + } + + class ConduitLinkData : GenericHyperlink + { + public override string tag() => "conduit"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + int soulbindConduitId, rank; + if (!(t.TryConsumeTo(out soulbindConduitId) && t.TryConsumeTo(out rank) && t.IsEmpty())) + return false; + + return (value = Global.DB2Mgr.GetSoulbindConduitRank(soulbindConduitId, rank)) != null; + } + } + + class CreatureLinkData : GenericHyperlink + { + public override string tag() => "creature"; + + public override bool Parse(string data) + { + if (ulong.TryParse(data, out value)) + return true; + + return false; + } + } + + class CreatureEntryLinkData : GenericHyperlink + { + public override string tag() => "creature_entry"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out uint tempValue)) + { + value = tempValue; + return true; + } + + value = default; + return false; + } + } + + class CurioLinkData : GenericHyperlink + { + public override string tag() => "curio"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint spellId; + if (!(t.TryConsumeTo(out spellId) && t.IsEmpty())) + return false; + return (value = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None)) != null; + } + } + + class CurrencyLinkData : IHyperlink + { + public CurrencyTypesRecord Currency; + public int Quantity; + + public CurrencyContainerRecord Container; + + public string tag() => "currency"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint currencyId; + if (!t.TryConsumeTo(out currencyId)) + return false; + Currency = CliDB.CurrencyTypesStorage.LookupByKey(currencyId); + if (Currency == null || !t.TryConsumeTo(out Quantity) || !t.IsEmpty()) + return false; + Container = Global.DB2Mgr.GetCurrencyContainerForCurrencyQuantity(currencyId, Quantity); + return true; + } + } + + class DungeonScoreLinkData : IHyperlink + { + public uint Score; + public ObjectGuid Player; + public string PlayerName; + public byte PlayerClass; + public uint AvgItemLevel; + public byte PlayerLevel; + public uint RunsThisSeason; + public uint BestSeasonScore; + public uint BestSeasonNumber; + + public struct Dungeon + { + public uint MapChallengeModeID; + public bool CompletedInTime; + public uint KeystoneLevel; + } + + public List Dungeons = new(); + + public string tag() => "dungeonScore"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + if (!t.TryConsumeTo(out Score) || !t.TryConsumeTo(out Player) || !Player.IsPlayer() + || !t.TryConsumeTo(out PlayerName) || !t.TryConsumeTo(out PlayerClass) || !t.TryConsumeTo(out AvgItemLevel) + || !t.TryConsumeTo(out PlayerLevel) || !t.TryConsumeTo(out RunsThisSeason) + || !t.TryConsumeTo(out BestSeasonScore) || !t.TryConsumeTo(out BestSeasonNumber)) + return false; + + if (t.IsEmpty()) + return true; + + for (uint i = 0; i < 10; ++i) + { + DungeonScoreLinkData.Dungeon dungeon = new(); + if (!t.TryConsumeTo(out dungeon.MapChallengeModeID) || !CliDB.MapChallengeModeStorage.ContainsKey(dungeon.MapChallengeModeID)) + return false; + if (!t.TryConsumeTo(out dungeon.CompletedInTime) || !t.TryConsumeTo(out dungeon.KeystoneLevel)) + return false; + if (t.IsEmpty()) + return true; + + Dungeons.Add(dungeon); + } + + return false; + } + } + + class EnchantLinkData : GenericHyperlink + { + public override string tag() => "enchant"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint spellId; + if (!(t.TryConsumeTo(out spellId) && t.IsEmpty())) + return false; + return (value = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None)) != null && value.HasAttribute(SpellAttr0.IsTradeskill); + } + } + + class GameeventLinkData : GenericHyperlink + { + public override string tag() => "gameevent"; + + public override bool Parse(string data) + { + if (ushort.TryParse(data, out value)) + return true; + + return false; + } + } + + class GameobjectLinkData : GenericHyperlink + { + public override string tag() => "gameobject"; + + public override bool Parse(string data) + { + if (ulong.TryParse(data, out ulong value)) + return true; + + return false; + } + } + + class GameobjectEntryLinkData : GenericHyperlink + { + public override string tag() => "gameobject_entry"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out uint value)) + return true; + + return false; + } + } + + class GarrisonFollowerLinkData : IHyperlink + { + public GarrFollowerRecord Follower; + public uint Quality; + public uint Level; + public uint ItemLevel; + public uint[] Abilities = new uint[4]; + public uint[] Traits = new uint[4]; + public uint Specialization; + + public string tag() => "garrfollower"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint garrFollowerId; + if (!t.TryConsumeTo(out garrFollowerId)) + return false; + + Follower = CliDB.GarrFollowerStorage.LookupByKey(garrFollowerId); + if (Follower == null || !t.TryConsumeTo(out Quality) || Quality >= (int)ItemQuality.Max || !t.TryConsumeTo(out Level) || !t.TryConsumeTo(out ItemLevel) + || !t.TryConsumeTo(out Abilities[0]) || !t.TryConsumeTo(out Abilities[1]) || !t.TryConsumeTo(out Abilities[2]) || !t.TryConsumeTo(out Abilities[3]) + || !t.TryConsumeTo(out Traits[0]) || !t.TryConsumeTo(out Traits[1]) || !t.TryConsumeTo(out Traits[2]) || !t.TryConsumeTo(out Traits[3]) + || !t.TryConsumeTo(out Specialization) || !t.IsEmpty()) + return false; + + foreach (uint ability in Abilities) + if (ability != 0 && !CliDB.GarrAbilityStorage.ContainsKey(ability)) + return false; + + foreach (uint trait in Traits) + if (trait != 0 && !CliDB.GarrAbilityStorage.ContainsKey(trait)) + return false; + + if (Specialization != 0 && !CliDB.GarrAbilityStorage.ContainsKey(Specialization)) + return false; + + return true; + } + } + + class GarrFollowerAbilityLinkData : GenericHyperlink + { + public override string tag() => "garrfollowerability"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint garrAbilityId; + if (!t.TryConsumeTo(out garrAbilityId)) + return false; + return (value = CliDB.GarrAbilityStorage.LookupByKey(garrAbilityId)) != null && t.IsEmpty(); + } + } + + class GarrisonMissionLinkData : IHyperlink + { + public GarrMissionRecord Mission; + public ulong DbID; + + public string tag() => "garrmission"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint garrMissionId; + if (!t.TryConsumeTo(out garrMissionId)) + return false; + return (Mission = CliDB.GarrMissionStorage.LookupByKey(garrMissionId)) != null && t.TryConsumeTo(out DbID) && t.IsEmpty(); + } + } + + class InstanceLockLinkData : IHyperlink + { + public ObjectGuid Owner; + public MapRecord Map; + public uint Difficulty; + public uint CompletedEncountersMask; + + public string tag() => "instancelock"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + if (!t.TryConsumeTo(out Owner)) + return false; + uint mapId; + if (!t.TryConsumeTo(out mapId)) + return false; + return (Map = CliDB.MapStorage.LookupByKey(mapId)) != null + && t.TryConsumeTo(out Difficulty) && Global.DB2Mgr.GetMapDifficultyData(mapId, (Difficulty)Difficulty) != null + && t.TryConsumeTo(out CompletedEncountersMask) && t.IsEmpty(); + } + } + + class ItemLinkData : IHyperlink + { + public ItemTemplate Item; + public uint EnchantId; + public uint[] GemItemId = new uint[3]; + public byte RenderLevel; + public uint RenderSpecialization; + public byte Context; + public List ItemBonusListIDs = new(); + + public struct Modifier + { + public uint Type; + public int Value; + } + + public List Modifiers = new(); + public List[] GemItemBonusListIDs = new List[3]; + public ObjectGuid Creator; + public uint UseEnchantId; + + public uint Quality; + public ItemNameDescriptionRecord Suffix; + + public string tag() => "item"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text, true); + uint itemId, dummy, numBonusListIDs; + if (!t.TryConsumeTo(out itemId)) + return false; + Item = Global.ObjectMgr.GetItemTemplate(itemId); + if (!(Item != null && t.TryConsumeTo(out EnchantId) && t.TryConsumeTo(out GemItemId[0]) && t.TryConsumeTo(out GemItemId[1]) && + t.TryConsumeTo(out GemItemId[2]) && t.TryConsumeTo(out dummy) && t.TryConsumeTo(out dummy) && t.TryConsumeTo(out dummy) && + t.TryConsumeTo(out RenderLevel) && t.TryConsumeTo(out RenderSpecialization) && t.TryConsumeTo(out dummy) && + t.TryConsumeTo(out Context) && t.TryConsumeTo(out numBonusListIDs))) + return false; + + uint maxBonusListIDs = 16; + if (numBonusListIDs > maxBonusListIDs) + return false; + + BonusData evaluatedBonus = new(Item); + + for (var i = 0; i < numBonusListIDs; ++i) + { + if (!t.TryConsumeTo(out uint itemBonusListID)) + return false; + + ItemBonusListIDs.Add(itemBonusListID); + evaluatedBonus.AddBonusList(itemBonusListID); + } + + if (!ItemBonusListIDs.Empty() && ItemBonusListIDs[0] == 3524) // default uninitialized bonus + { + ItemBonusListIDs = ItemBonusMgr.GetBonusListsForItem(itemId, new((ItemContext)Context)); + + // reset bonuses + evaluatedBonus = new(Item); + foreach (uint itemBonusListID in ItemBonusListIDs) + evaluatedBonus.AddBonusList(itemBonusListID); + } + + Quality = (uint)evaluatedBonus.Quality; + Suffix = CliDB.ItemNameDescriptionStorage.LookupByKey(evaluatedBonus.Suffix); + if (evaluatedBonus.Suffix != 0 && Suffix == null) + return false; + + uint numModifiers; + if (!t.TryConsumeTo(out numModifiers)) + return false; + + if (numModifiers > (int)ItemModifier.Max) + return false; + + for (var i = 0; i < numModifiers; ++i) + { + Modifier modifier = new(); + if (!(t.TryConsumeTo(out modifier.Type) && modifier.Type < (int)ItemModifier.Max && t.TryConsumeTo(out modifier.Value))) + return false; + + Modifiers.Add(modifier); + } + + for (uint i = 0; i < ItemConst.MaxGemSockets; ++i) + { + if (!t.TryConsumeTo(out numBonusListIDs) || numBonusListIDs > maxBonusListIDs) + return false; + + for (var c = 0; c < numBonusListIDs; ++c) + { + if (!t.TryConsumeTo(out int itemBonusListID)) + return false; + + GemItemBonusListIDs[i].Add(itemBonusListID); + } + } + + return t.TryConsumeTo(out Creator) && t.TryConsumeTo(out UseEnchantId) && t.IsEmpty(); + } + } + + class ItemsetLinkData : GenericHyperlink + { + public override string tag() => "itemset"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out uint value)) + return true; + + return false; + } + } + + class JournalLinkData : IHyperlink + { + public enum Types + { + Instance = 0, + Encounter = 1, + EncounterSection = 2, + Tier = 3 + } + + public byte Type; + public LocalizedString ExpectedText; + public uint Difficulty; + + public string tag() => "journal"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint id; + if (!t.TryConsumeTo(out Type) || !t.TryConsumeTo(out id) || !t.TryConsumeTo(out Difficulty) || !t.IsEmpty()) + return false; + switch ((JournalLinkData.Types)Type) + { + case JournalLinkData.Types.Instance: + { + JournalInstanceRecord instance = CliDB.JournalInstanceStorage.LookupByKey(id); + if (instance == null) + return false; + ExpectedText = instance.Name; + break; + } + case JournalLinkData.Types.Encounter: + { + JournalEncounterRecord encounter = CliDB.JournalEncounterStorage.LookupByKey(id); + if (encounter == null) + return false; + ExpectedText = encounter.Name; + break; + } + case JournalLinkData.Types.EncounterSection: + { + JournalEncounterSectionRecord encounterSection = CliDB.JournalEncounterSectionStorage.LookupByKey(id); + if (encounterSection == null) + return false; + ExpectedText = encounterSection.Title; + break; + } + case JournalLinkData.Types.Tier: + { + JournalTierRecord tier = Global.DB2Mgr.GetJournalTier(id); + if (tier == null) + return false; + ExpectedText = tier.Name; + break; + } + default: + return false; + } + return true; + } + } + + class KeystoneLinkData : IHyperlink + { + public uint ItemId; + public MapChallengeModeRecord Map; + public uint Level; + public uint[] Affix = new uint[4]; + + public string tag() => "keystone"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint mapChallengeModeId; + if (!t.TryConsumeTo(out ItemId) || !t.TryConsumeTo(out mapChallengeModeId) || !t.TryConsumeTo(out Level) + || !t.TryConsumeTo(out Affix[0]) || !t.TryConsumeTo(out Affix[1]) || !t.TryConsumeTo(out Affix[2]) || !t.TryConsumeTo(out Affix[3]) + || !t.IsEmpty()) + return false; + Map = CliDB.MapChallengeModeStorage.LookupByKey(mapChallengeModeId); + if (Map == null) + return false; + ItemTemplate item = Global.ObjectMgr.GetItemTemplate(ItemId); + if (item == null || item.GetClass() != ItemClass.Reagent || item.GetSubClass() != (uint)ItemSubClassReagent.Keystone) + return false; + foreach (uint keystoneAffix in Affix) + if (keystoneAffix != 0 && !CliDB.KeystoneAffixStorage.ContainsKey(keystoneAffix)) + return false; + return true; + } + } + + class MawPowerLinkData : GenericHyperlink + { + public override string tag() => "mawpower"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint mawPowerId; + if (!t.TryConsumeTo(out mawPowerId)) + return false; + return (value = CliDB.MawPowerStorage.LookupByKey(mawPowerId)) != null && t.IsEmpty(); + } + } + + class MountLinkData : IHyperlink + { + public SpellInfo Spell; + public uint DisplayId; + public string Customizations; + + public string tag() => "mount"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint spellId; + if (!t.TryConsumeTo(out spellId) || (Spell = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None)) == null) + return false; + if (!t.TryConsumeTo(out DisplayId) || !CliDB.CreatureDisplayInfoStorage.ContainsKey(DisplayId)) + return false; + return t.TryConsumeTo(out Customizations) && t.IsEmpty(); + } + } + + class OutfitLinkData : GenericHyperlink + { + public override string tag() => "outfit"; + + public override bool Parse(string data) + { + value = data; + return true; + } + } + + class PerksActivityLinkData : GenericHyperlink + { + public override string tag() => "perksactivity"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint perksActivityId; + if (!t.TryConsumeTo(out perksActivityId)) + return false; + return (value = CliDB.PerksActivityStorage.LookupByKey(perksActivityId)) != null && t.IsEmpty(); + } + } + + class PlayerLinkData : GenericHyperlink + { + public override string tag() => "player"; + + public override bool Parse(string data) + { + value = data; + return true; + } + } + + class PvpTalLinkData : GenericHyperlink + { + public override string tag() => "pvptal"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint pvpTalentId; + if (!(t.TryConsumeTo(out pvpTalentId) && t.IsEmpty())) + return false; + if ((value = CliDB.PvpTalentStorage.LookupByKey(pvpTalentId)) == null) + return false; + return true; + } + } + + class QuestLinkData : IHyperlink + { + public Quest Quest; + public uint ContentTuningId; + + public string tag() => "quest"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint questId; + if (!t.TryConsumeTo(out questId)) + return false; + return (Quest = Global.ObjectMgr.GetQuestTemplate(questId)) != null && t.TryConsumeTo(out ContentTuningId) && t.IsEmpty(); + } + } + + class SkillLinkData : GenericHyperlink + { + public override string tag() => "skill"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out value)) + return true; + + return false; + } + } + + class SpellLinkData : IHyperlink + { + public SpellInfo Spell; + public GlyphPropertiesRecord Glyph; + + public string tag() => "spell"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint spellId, glyphPropertiesId; + if (!(t.TryConsumeTo(out spellId) && t.TryConsumeTo(out glyphPropertiesId) && t.IsEmpty())) + return false; + return (Spell = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None)) != null + && (glyphPropertiesId == 0 || (Glyph = CliDB.GlyphPropertiesStorage.LookupByKey(glyphPropertiesId)) != null); + } + } + + class TalentBuildLinkData : IHyperlink + { + public ChrSpecializationRecord Spec; + public uint Level; + public string ImportString; + + public string tag() => "talent"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint chrSpecializationId; + if (!t.TryConsumeTo(out chrSpecializationId)) + return false; + if ((Spec = CliDB.ChrSpecializationStorage.LookupByKey(chrSpecializationId)) == null) + return false; + if (!t.TryConsumeTo(out Level) || !t.TryConsumeTo(out ImportString)) + return false; + return true; + } + } + + class TaxiNodeLinkData : GenericHyperlink + { + public override string tag() => "taxinode"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out value)) + return true; + + return false; + } + } + + class TeleLinkData : GenericHyperlink + { + public override string tag() => "tele"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out value)) + return true; + + return false; + } + } + + class TitleLinkData : GenericHyperlink + { + public override string tag() => "title"; + + public override bool Parse(string data) + { + if (uint.TryParse(data, out value)) + return true; + + return false; + } + } + + class TradeskillLinkData : IHyperlink + { + public ObjectGuid Owner; + public SpellInfo Spell; + public SkillLineRecord Skill; + + public string tag() => "trade"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint spellId, skillId; + if (!t.TryConsumeTo(out Owner) || !t.TryConsumeTo(out spellId) || !t.TryConsumeTo(out skillId) || !t.IsEmpty()) + return false; + Spell = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None); + Skill = CliDB.SkillLineStorage.LookupByKey(skillId); + if (Spell == null || !Spell.HasEffect(SpellEffectName.TradeSkill) || Skill == null) + return false; + return true; + } + } + + class TransmogAppearanceLinkData : GenericHyperlink + { + public override string tag() => "transmogappearance"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + if (!t.TryConsumeTo(out uint itemModifiedAppearanceId)) + return false; + return (value = CliDB.ItemModifiedAppearanceStorage.LookupByKey(itemModifiedAppearanceId)) != null && t.IsEmpty(); + } + } + + class TransmogIllusionLinkData : GenericHyperlink + { + public override string tag() => "transmogillusion"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + if (!t.TryConsumeTo(out uint spellItemEnchantmentId)) + return false; + return (value = CliDB.SpellItemEnchantmentStorage.LookupByKey(spellItemEnchantmentId)) != null + && Global.DB2Mgr.GetTransmogIllusionForEnchantment(spellItemEnchantmentId) != null && t.IsEmpty(); + } + } + + class TransmogSetLinkData : GenericHyperlink + { + public override string tag() => "transmogset"; + + public override bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + if (!t.TryConsumeTo(out uint transmogSetId)) + return false; + return (value = CliDB.TransmogSetStorage.LookupByKey(transmogSetId)) != null && t.IsEmpty(); + } + } + + class WorldMapLinkData : IHyperlink + { + public UiMapRecord UiMap; + public uint X; + public uint Y; + public uint? Z; + + public string tag() => "worldmap"; + + public bool Parse(string text) + { + HyperlinkDataTokenizer t = new(text); + uint uiMapId; + if (!t.TryConsumeTo(out uiMapId)) + return false; + UiMap = CliDB.UiMapStorage.LookupByKey(uiMapId); + if (UiMap == null || !t.TryConsumeTo(out X) || !t.TryConsumeTo(out Y)) + return false; + if (t.IsEmpty()) + return true; + if (!t.TryConsumeTo(out Z)) + return false; + return t.IsEmpty(); + } + } } \ No newline at end of file diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index e431d5192..bbd7c76be 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -86,6 +86,7 @@ namespace Game.DataStorage BankTabStorage = ReadDB2("BankTab.db2", HotfixStatements.SEL_BANK_TAB); BannedAddOnsStorage = ReadDB2("BannedAddons.db2", HotfixStatements.SEL_BANNED_ADDONS); BarberShopStyleStorage = ReadDB2("BarberShopStyle.db2", HotfixStatements.SEL_BARBER_SHOP_STYLE, HotfixStatements.SEL_BARBER_SHOP_STYLE_LOCALE); + BattlePetAbilityStorage = ReadDB2("BattlePetAbility.db2", HotfixStatements.SEL_BATTLE_PET_ABILITY, HotfixStatements.SEL_BATTLE_PET_ABILITY_LOCALE); BattlePetBreedQualityStorage = ReadDB2("BattlePetBreedQuality.db2", HotfixStatements.SEL_BATTLE_PET_BREED_QUALITY); BattlePetBreedStateStorage = ReadDB2("BattlePetBreedState.db2", HotfixStatements.SEL_BATTLE_PET_BREED_STATE); BattlePetSpeciesStorage = ReadDB2("BattlePetSpecies.db2", HotfixStatements.SEL_BATTLE_PET_SPECIES, HotfixStatements.SEL_BATTLE_PET_SPECIES_LOCALE); @@ -261,6 +262,7 @@ namespace Game.DataStorage PathStorage = ReadDB2("Path.db2", HotfixStatements.SEL_PATH); PathNodeStorage = ReadDB2("PathNode.db2", HotfixStatements.SEL_PATH_NODE); PathPropertyStorage = ReadDB2("PathProperty.db2", HotfixStatements.SEL_PATH_PROPERTY); + PerksActivityStorage = ReadDB2("PerksActivity.db2", HotfixStatements.SEL_PERKS_ACTIVITY, HotfixStatements.SEL_PERKS_ACTIVITY_LOCALE); PhaseStorage = ReadDB2("Phase.db2", HotfixStatements.SEL_PHASE); PhaseXPhaseGroupStorage = ReadDB2("PhaseXPhaseGroup.db2", HotfixStatements.SEL_PHASE_X_PHASE_GROUP); PlayerConditionStorage = ReadDB2("PlayerCondition.db2", HotfixStatements.SEL_PLAYER_CONDITION, HotfixStatements.SEL_PLAYER_CONDITION_LOCALE); @@ -492,6 +494,7 @@ namespace Game.DataStorage public static DB6Storage BankTabStorage; public static DB6Storage BannedAddOnsStorage; public static DB6Storage BarberShopStyleStorage; + public static DB6Storage BattlePetAbilityStorage; public static DB6Storage BattlePetBreedQualityStorage; public static DB6Storage BattlePetBreedStateStorage; public static DB6Storage BattlePetSpeciesStorage; @@ -664,6 +667,7 @@ namespace Game.DataStorage public static DB6Storage NumTalentsAtLevelStorage; public static DB6Storage OverrideSpellDataStorage; public static DB6Storage ParagonReputationStorage; + public static DB6Storage PerksActivityStorage; public static DB6Storage PathStorage; public static DB6Storage PathNodeStorage; public static DB6Storage PathPropertyStorage; diff --git a/Source/Game/DataStorage/Structs/B_Records.cs b/Source/Game/DataStorage/Structs/B_Records.cs index d515c691c..3e7f70728 100644 --- a/Source/Game/DataStorage/Structs/B_Records.cs +++ b/Source/Game/DataStorage/Structs/B_Records.cs @@ -39,6 +39,18 @@ namespace Game.DataStorage public byte Data; // real ID to hair/facial hair } + public sealed class BattlePetAbilityRecord + { + public uint Id; + public LocalizedString Name; + public LocalizedString Description; + public int IconFileDataID; + public sbyte PetTypeEnum; + public uint Cooldown; + public ushort BattlePetVisualID; + public byte Flags; + } + public sealed class BattlePetBreedQualityRecord { public uint Id; diff --git a/Source/Game/DataStorage/Structs/P_Records.cs b/Source/Game/DataStorage/Structs/P_Records.cs index f1be33296..023f33248 100644 --- a/Source/Game/DataStorage/Structs/P_Records.cs +++ b/Source/Game/DataStorage/Structs/P_Records.cs @@ -43,6 +43,17 @@ namespace Game.DataStorage public PathPropertyIndex GetPropertyIndex() { return (PathPropertyIndex)PropertyIndex; } } + public sealed class PerksActivityRecord + { + public LocalizedString ActivityName; + public LocalizedString Description; + public uint Id; + public int CriteriaTreeID; + public int ThresholdContributionAmount; + public int Supersedes; + public int Priority; + } + public sealed class PhaseRecord { public uint Id; diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index c18ead6d2..e2053cbdd 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -2481,7 +2481,7 @@ namespace Game.Entities return null; } - bool AddSpell(uint spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, uint fromSkill = 0, bool favorite = false, PlayerSpellTrait? trait = null) + bool AddSpell(uint spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, uint fromSkill = 0, bool favorite = false, PlayerSpellTrait trait = null) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None); if (spellInfo == null)