diff --git a/PROBot/BattleAI.cs b/PROBot/BattleAI.cs index b45f63b..a525d70 100644 --- a/PROBot/BattleAI.cs +++ b/PROBot/BattleAI.cs @@ -47,6 +47,7 @@ public int UsablePokemonsCount return usablePokemons; } } + public bool UseMandatoryAction() { return RepeatAttack(); @@ -152,10 +153,10 @@ public bool UseMove(string moveName) return false; } - public bool UseItem(int itemId, int pokemonUid = 0) + public bool UseItem(int itemId, int pokemonUid = 0, int moveIndex = 0) { if (ActivePokemon.CurrentHealth == 0) return false; - _client.UseItem(itemId, pokemonUid); + _client.UseItem(itemId, pokemonUid, moveIndex); return true; } @@ -237,6 +238,12 @@ private bool UseAttack(bool useBestAttack) } } + if (move.Id == DragonRage) + { + if (opponentType1 == PokemonType.Fairy || opponentType2 == PokemonType.Fairy) + power = 0; + } + if (power < 0.01) continue; if (bestMove == null || power > bestPower) diff --git a/PROBot/BotClient.cs b/PROBot/BotClient.cs index 193d250..e2e39c6 100644 --- a/PROBot/BotClient.cs +++ b/PROBot/BotClient.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; namespace PROBot { @@ -46,6 +47,7 @@ public enum State public Dictionary TextOptions { get; set; } private bool _loginRequested; + private bool _authenticationRequired; private Timeout _actionTimeout = new Timeout(); @@ -161,6 +163,8 @@ private void LoginUpdate() { client = new GameClient(new GameConnection(server), new MapConnection()); } + + Encryption.Reset(); SetClient(client); client.Open(); } @@ -176,6 +180,19 @@ public void Logout(bool allowAutoReconnect) public void Update() { + if (_authenticationRequired) + { + if (Encryption.StateReady) + { + // TODO: Add an option to select the OS we want, it could be useful. + Game.SendAuthentication(Account.Name, + Account.Password, + Account.DeviceId ?? Hardware.GenerateRandomHash(), + Hardware.GenerateRandomOsInfo()); + _authenticationRequired = false; + } + return; + } AutoReconnector.Update(); if (_loginRequested) @@ -198,7 +215,7 @@ public void Update() if (Game.IsCreatingNewCharacter) { LogMessage("Creating a new character with a random skin..."); - Game.CreateCharacter(Rand.Next(14), Rand.Next(28), Rand.Next(8), Rand.Next(6), Rand.Next(5)); + Game.CreateCharacter(Rand.Next(14), Rand.Next(28), Rand.Next(4), 695 + Rand.Next(6), Rand.Next(5)); return; } @@ -261,17 +278,13 @@ public void LoadScript(string filename) { string input = File.ReadAllText(filename); - List libs = new List(); + var libs = new List(); if (Directory.Exists("Libs")) { - string[] files = Directory.GetFiles("Libs"); - foreach (string file in files) - { - if (file.ToUpperInvariant().EndsWith(".LUA")) - { - libs.Add(File.ReadAllText(file)); - } - } + libs = Directory.GetFiles("Libs") + .Where(f => f.EndsWith(".lua", StringComparison.InvariantCultureIgnoreCase)) + .Select(File.ReadAllText) + .ToList(); } BaseScript script = new LuaScript(this, Path.GetFullPath(filename), input, libs); @@ -283,10 +296,10 @@ public void LoadScript(string filename) Script.ScriptMessage += Script_ScriptMessage; Script.Initialize(); } - catch (Exception ex) + catch (Exception) { Script = null; - throw ex; + throw; } } @@ -402,7 +415,7 @@ private void ExecuteNextAction() private void Client_ConnectionOpened() { ConnectionOpened?.Invoke(); - Game.SendAuthentication(Account.Name, Account.Password, Account.DeviceId ?? HardwareHash.GenerateRandom()); + _authenticationRequired = true; } private void Client_ConnectionClosed(Exception ex) @@ -441,7 +454,7 @@ private void Client_ConnectionFailed(Exception ex) SetClient(null); } - private void Client_DialogOpened(string message) + private void Client_DialogOpened(string message, string[] options) { if (Running == State.Started) { diff --git a/PROBot/Modules/AutoReconnector.cs b/PROBot/Modules/AutoReconnector.cs index 21449e7..f031623 100644 --- a/PROBot/Modules/AutoReconnector.cs +++ b/PROBot/Modules/AutoReconnector.cs @@ -29,6 +29,7 @@ public bool IsEnabled private bool _relogging; private double _reloggingDelay; private DateTime _autoReconnectTimeout; + private DateTime _botScriptDelay = DateTime.MaxValue; public AutoReconnector(BotClient bot) { @@ -58,6 +59,12 @@ public void Update() _autoReconnectTimeout = DateTime.UtcNow.AddSeconds(_bot.Rand.Next(MinDelay, MaxDelay + 1)); } } + + if (_botScriptDelay < DateTime.UtcNow) + { + _bot.Start(); + _botScriptDelay = DateTime.MaxValue; + } } public void Relog(double delay) @@ -84,16 +91,18 @@ private void Client_LoggedIn() { _reconnecting = false; _relogging = false; - _bot.Start(); + _botScriptDelay = DateTime.UtcNow.AddSeconds(10); } } - private void Client_AuthenticationFailed(AuthenticationResult result) + private void Client_AuthenticationFailed(string message) { _relogging = false; - if (result == AuthenticationResult.InvalidUser || result == AuthenticationResult.InvalidPassword + message = message.ToLowerInvariant(); + if (/*result == AuthenticationResult.InvalidUser || result == AuthenticationResult.InvalidPassword || result == AuthenticationResult.InvalidVersion || result == AuthenticationResult.Banned - || result == AuthenticationResult.EmailNotActivated) + || result == AuthenticationResult.EmailNotActivated*/ + message.Contains("invalid") || message.Contains("ban") || message.Contains("email")) { IsEnabled = false; _reconnecting = false; diff --git a/PROBot/Modules/MoveTeacher.cs b/PROBot/Modules/MoveTeacher.cs index fbe40f0..b187078 100644 --- a/PROBot/Modules/MoveTeacher.cs +++ b/PROBot/Modules/MoveTeacher.cs @@ -5,7 +5,7 @@ namespace PROBot.Modules public class MoveTeacher { public bool IsLearning { get; private set; } - public int PokemonUid { get; private set; } + public int PokemonDBid { get; private set; } public int MoveToForget { get; set; } private readonly BotClient _bot; @@ -23,7 +23,7 @@ public bool Update() if (_learningTimeout.IsActive && !_learningTimeout.Update()) { IsLearning = false; - _bot.Game.LearnMove(PokemonUid, MoveToForget); + _bot.Game.LearnMove(PokemonDBid, MoveToForget); return true; } return _learningTimeout.IsActive; @@ -37,16 +37,15 @@ private void Bot_ClientChanged() } } - private void Game_LearningMove(int moveId, string moveName, int pokemonUid) + private void Game_LearningMove(int moveId, string moveName, int pokemonDBid) { if (_bot.Game == null || _bot.Script == null) return; IsLearning = true; - PokemonUid = pokemonUid; + PokemonDBid = pokemonDBid; MoveToForget = 0; _learningTimeout.Set(_bot.Rand.Next(1000, 3000)); - - _bot.Script.OnLearningMove(moveName, pokemonUid); + _bot.Script.OnLearningMove(moveName, _bot.Game.GetPokemonFromDBId(pokemonDBid).Uid); } } } diff --git a/PROBot/Modules/MovementResynchronizer.cs b/PROBot/Modules/MovementResynchronizer.cs index 3f16429..d9845b2 100644 --- a/PROBot/Modules/MovementResynchronizer.cs +++ b/PROBot/Modules/MovementResynchronizer.cs @@ -38,7 +38,7 @@ private void Game_BattleEnded() Reset(); } - private void Game_DialogOpened(string message) + private void Game_DialogOpened(string message, string[] options) { Reset(); } diff --git a/PROBot/Modules/PokemonEvolver.cs b/PROBot/Modules/PokemonEvolver.cs index e12e0ad..a0fd11a 100644 --- a/PROBot/Modules/PokemonEvolver.cs +++ b/PROBot/Modules/PokemonEvolver.cs @@ -24,7 +24,7 @@ public bool IsEnabled private readonly BotClient _bot; private Timeout _evolutionTimeout = new Timeout(); - public int _evolvingPokemonUid; + public int _evolvingPokemonDBid; public int _evolvingItem; public PokemonEvolver(BotClient bot) @@ -39,11 +39,11 @@ public bool Update() { if (IsEnabled) { - _bot.Game.SendAcceptEvolution(_evolvingPokemonUid, _evolvingItem); + _bot.Game.SendAcceptEvolution(_evolvingPokemonDBid); } else { - _bot.Game.SendCancelEvolution(_evolvingPokemonUid, _evolvingItem); + _bot.Game.SendCancelEvolution(_evolvingPokemonDBid); } return true; } @@ -58,9 +58,9 @@ private void Bot_ClientChanged() } } - private void Game_Evolving(int evolvingPokemonUid, int evolvingItem) + private void Game_Evolving(int evolvingPokemonDBid, int evolvingItem) { - _evolvingPokemonUid = evolvingPokemonUid; + _evolvingPokemonDBid = evolvingPokemonDBid; _evolvingItem = evolvingItem; _evolutionTimeout.Set(_bot.Rand.Next(2000, 3000)); } diff --git a/PROBot/PROBot.csproj b/PROBot/PROBot.csproj index 603ef6e..a2e31ed 100644 --- a/PROBot/PROBot.csproj +++ b/PROBot/PROBot.csproj @@ -34,8 +34,8 @@ ..\packages\MoonSharp.2.0.0.0\lib\net40-client\MoonSharp.Interpreter.dll True - - ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll diff --git a/PROBot/Scripting/LuaScript.cs b/PROBot/Scripting/LuaScript.cs index 7f33953..cabe950 100644 --- a/PROBot/Scripting/LuaScript.cs +++ b/PROBot/Scripting/LuaScript.cs @@ -1,2921 +1,3056 @@ -using MoonSharp.Interpreter; -using PROBot.Utils; -using PROProtocol; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Media; -using System.Text; - -namespace PROBot.Scripting -{ - public class LuaScript : BaseScript - { - public BotClient Bot { get; private set; } - -#if DEBUG - public int TimeoutDelay = 60000; -#else - public int TimeoutDelay = 3000; -#endif - - private Script _lua; - private string _path; - private string _content; - private IList _libsContent; - private IDictionary> _hookedFunctions; - - private bool _actionExecuted; - - public LuaScript(BotClient bot, string path, string content, IList libsContent) - { - Bot = bot; - _path = Path.GetDirectoryName(path); - _content = content; - _libsContent = libsContent; - } - - public override void Initialize() - { - CreateLuaInstance(); - - Name = _lua.Globals.Get("name").CastToString(); - Author = _lua.Globals.Get("author").CastToString(); - Description = _lua.Globals.Get("description").CastToString(); - } - - public override void Start() - { - CallFunctionSafe("onStart"); - } - - public override void Stop() - { - CallFunctionSafe("onStop"); - } - - public override void Pause() - { - CallFunctionSafe("onPause"); - } - - public override void Resume() - { - CallFunctionSafe("onResume"); - } - - public override void OnDialogMessage(string message) - { - CallFunctionSafe("onDialogMessage", message); - } - - public override void OnBattleMessage(string message) - { - CallFunctionSafe("onBattleMessage", message); - } - - public override void OnSystemMessage(string message) - { - CallFunctionSafe("onSystemMessage", message); - } - - public override void OnWarningMessage(bool differentMap, int distance = -1) - { - CallFunctionSafe("onWarningMessage", differentMap, distance); - } - - public override void OnLearningMove(string moveName, int pokemonIndex) - { - CallFunctionSafe("onLearningMove", moveName, pokemonIndex); - } - - public override bool ExecuteNextAction() - { - string functionName = Bot.Game.IsInBattle ? "onBattleAction" : "onPathAction"; - - _actionExecuted = false; - try - { - CallFunction(functionName, true); - } - catch (ScriptRuntimeException ex) - { - throw new Exception(ex.DecoratedMessage, ex); - } - return _actionExecuted; - } - - private void CreateLuaInstance() - { - _hookedFunctions = new Dictionary>(); - - _lua = new Script(CoreModules.Preset_SoftSandbox | CoreModules.LoadMethods); - _lua.Options.ScriptLoader = new CustomScriptLoader(_path) { ModulePaths = new[] { "?.lua" } }; - _lua.Options.CheckThreadAccess = false; - _lua.Globals["log"] = new Action(Log); - _lua.Globals["fatal"] = new Action(Fatal); - _lua.Globals["logout"] = new Action(Logout); - _lua.Globals["relog"] = new Action(Relog); - _lua.Globals["stringContains"] = new Func(StringContains); - _lua.Globals["playSound"] = new Action(PlaySound); - _lua.Globals["registerHook"] = new Action(RegisterHook); - - // General conditions - _lua.Globals["getPlayerX"] = new Func(GetPlayerX); - _lua.Globals["getPlayerY"] = new Func(GetPlayerY); - _lua.Globals["getAccountName"] = new Func(GetAccountName); - _lua.Globals["getMapName"] = new Func(GetMapName); - _lua.Globals["getPokedexOwned"] = new Func(GetPokedexOwned); - _lua.Globals["getPokedexSeen"] = new Func(GetPokedexSeen); - _lua.Globals["getPokedexEvolved"] = new Func(GetPokedexEvolved); - _lua.Globals["getTeamSize"] = new Func(GetTeamSize); - _lua.Globals["isAccountMember"] = new Func(IsAccountMember); - - _lua.Globals["getPokemonId"] = new Func(GetPokemonId); - _lua.Globals["getPokemonName"] = new Func(GetPokemonName); - _lua.Globals["getPokemonHealth"] = new Func(GetPokemonHealth); - _lua.Globals["getPokemonHealthPercent"] = new Func(GetPokemonHealthPercent); - _lua.Globals["getPokemonMaxHealth"] = new Func(GetPokemonMaxHealth); - _lua.Globals["getPokemonLevel"] = new Func(GetPokemonLevel); - _lua.Globals["getPokemonTotalExperience"] = new Func(GetPokemonTotalExperience); - _lua.Globals["getPokemonRemainingExperience"] = new Func(GetPokemonRemainingExperience); - _lua.Globals["getPokemonStatus"] = new Func(GetPokemonStatus); - _lua.Globals["getPokemonForm"] = new Func(GetPokemonForm); - _lua.Globals["getPokemonHeldItem"] = new Func(GetPokemonHeldItem); - _lua.Globals["getPokemonUniqueId"] = new Func(GetPokemonUniqueId); - _lua.Globals["getRemainingPowerPoints"] = new Func(GetRemainingPowerPoints); - _lua.Globals["getPokemonMaxPowerPoints"] = new Func(GetPokemonMaxPowerPoints); - _lua.Globals["isPokemonShiny"] = new Func(IsPokemonShiny); - _lua.Globals["getPokemonMoveName"] = new Func(GetPokemonMoveName); - _lua.Globals["getPokemonMoveAccuracy"] = new Func(GetPokemonMoveAccuracy); - _lua.Globals["getPokemonMovePower"] = new Func(GetPokemonMovePower); - _lua.Globals["getPokemonMoveType"] = new Func(GetPokemonMoveType); - _lua.Globals["getPokemonMoveDamageType"] = new Func(GetPokemonMoveDamageType); - _lua.Globals["getPokemonMoveStatus"] = new Func(GetPokemonMoveStatus); - _lua.Globals["getPokemonNature"] = new Func(GetPokemonNature); - _lua.Globals["getPokemonAbility"] = new Func(GetPokemonAbility); - _lua.Globals["getPokemonStat"] = new Func(GetPokemonStat); - _lua.Globals["getPokemonEffortValue"] = new Func(GetPokemonEffortValue); - _lua.Globals["getPokemonIndividualValue"] = new Func(GetPokemonIndividualValue); - _lua.Globals["getPokemonHappiness"] = new Func(GetPokemonHappiness); - _lua.Globals["getPokemonRegion"] = new Func(GetPokemonRegion); - _lua.Globals["getPokemonOriginalTrainer"] = new Func(GetPokemonOriginalTrainer); - _lua.Globals["getPokemonGender"] = new Func(GetPokemonGender); - _lua.Globals["getPokemonType"] = new Func(GetPokemonType); - _lua.Globals["getDamageMultiplier"] = new Func(GetDamageMultiplier); - _lua.Globals["isPokemonUsable"] = new Func(IsPokemonUsable); - _lua.Globals["getUsablePokemonCount"] = new Func(GetUsablePokemonCount); - _lua.Globals["hasMove"] = new Func(HasMove); - _lua.Globals["getActiveBattlers"] = new Func>>(GetActiveBattlers); - _lua.Globals["getActiveDigSpots"] = new Func>>(GetActiveDigSpots); - _lua.Globals["getActiveHeadbuttTrees"] = new Func>>(GetActiveHeadbuttTrees); - _lua.Globals["getActiveBerryTrees"] = new Func>>(GetActiveBerryTrees); - _lua.Globals["getDiscoverableItems"] = new Func>>(GetDiscoverableItems); - _lua.Globals["getNpcData"] = new Func>>(GetNpcData); - - _lua.Globals["hasItem"] = new Func(HasItem); - _lua.Globals["getItemQuantity"] = new Func(GetItemQuantity); - _lua.Globals["hasItemId"] = new Func(HasItemId); - _lua.Globals["getItemQuantityId"] = new Func(GetItemQuantityID); - - _lua.Globals["hasPokemonInTeam"] = new Func(HasPokemonInTeam); - _lua.Globals["isTeamSortedByLevelAscending"] = new Func(IsTeamSortedByLevelAscending); - _lua.Globals["isTeamSortedByLevelDescending"] = new Func(IsTeamSortedByLevelDescending); - _lua.Globals["isTeamRangeSortedByLevelAscending"] = new Func(IsTeamRangeSortedByLevelAscending); - _lua.Globals["isTeamRangeSortedByLevelDescending"] = new Func(IsTeamRangeSortedByLevelDescending); - _lua.Globals["isNpcVisible"] = new Func(IsNpcVisible); - _lua.Globals["isNpcOnCell"] = new Func(IsNpcOnCell); - _lua.Globals["isShopOpen"] = new Func(IsShopOpen); - _lua.Globals["isRelearningMoves"] = new Func(IsRelearningMoves); - _lua.Globals["getMoney"] = new Func(GetMoney); - _lua.Globals["isMounted"] = new Func(IsMounted); - _lua.Globals["isSurfing"] = new Func(IsSurfing); - _lua.Globals["isPrivateMessageEnabled"] = new Func(IsPrivateMessageEnabled); - _lua.Globals["getTime"] = new GetTimeDelegate(GetTime); - _lua.Globals["isMorning"] = new Func(IsMorning); - _lua.Globals["isNoon"] = new Func(IsNoon); - _lua.Globals["isNight"] = new Func(IsNight); - _lua.Globals["isOutside"] = new Func(IsOutside); - _lua.Globals["isAutoEvolve"] = new Func(IsAutoEvolve); - _lua.Globals["setMount"] = new Func(SetMount); - _lua.Globals["setWaterMount"] = new Func(SetWaterMount); - - _lua.Globals["isCurrentPCBoxRefreshed"] = new Func(IsCurrentPCBoxRefreshed); - _lua.Globals["getCurrentPCBoxId"] = new Func(GetCurrentPCBoxId); - _lua.Globals["isPCOpen"] = new Func(IsPCOpen); - _lua.Globals["getCurrentPCBoxId"] = new Func(GetCurrentPCBoxId); - _lua.Globals["getCurrentPCBoxSize"] = new Func(GetCurrentPCBoxSize); - _lua.Globals["getPCBoxCount"] = new Func(GetPCBoxCount); - _lua.Globals["getPCPokemonCount"] = new Func(GetPCPokemonCount); - - _lua.Globals["getPokemonIdFromPC"] = new Func(GetPokemonIdFromPC); - _lua.Globals["getPokemonNameFromPC"] = new Func(GetPokemonNameFromPC); - _lua.Globals["getPokemonHealthFromPC"] = new Func(GetPokemonHealthFromPC); - _lua.Globals["getPokemonHealthPercentFromPC"] = new Func(GetPokemonHealthPercentFromPC); - _lua.Globals["getPokemonMaxHealthFromPC"] = new Func(GetPokemonMaxHealthFromPC); - _lua.Globals["getPokemonLevelFromPC"] = new Func(GetPokemonLevelFromPC); - _lua.Globals["getPokemonTotalExperienceFromPC"] = new Func(GetPokemonTotalExperienceFromPC); - _lua.Globals["getPokemonRemainingExperienceFromPC"] = new Func(GetPokemonRemainingExperienceFromPC); - _lua.Globals["getPokemonStatusFromPC"] = new Func(GetPokemonStatusFromPC); - _lua.Globals["getPokemonTypeFromPC"] = new Func(GetPokemonTypeFromPC); - _lua.Globals["getPokemonHeldItemFromPC"] = new Func(GetPokemonHeldItemFromPC); - _lua.Globals["getPokemonUniqueIdFromPC"] = new Func(GetPokemonUniqueIdFromPC); - _lua.Globals["getPokemonRemainingPowerPointsFromPC"] = new Func(GetPokemonRemainingPowerPointsFromPC); - _lua.Globals["getPokemonMaxPowerPointsFromPC"] = new Func(GetPokemonMaxPowerPointsFromPC); - _lua.Globals["isPokemonFromPCShiny"] = new Func(IsPokemonFromPCShiny); - _lua.Globals["getPokemonMoveNameFromPC"] = new Func(GetPokemonMoveNameFromPC); - _lua.Globals["getPokemonMoveAccuracyFromPC"] = new Func(GetPokemonMoveAccuracyFromPC); - _lua.Globals["getPokemonMovePowerFromPC"] = new Func(GetPokemonMovePowerFromPC); - _lua.Globals["getPokemonMoveTypeFromPC"] = new Func(GetPokemonMoveTypeFromPC); - _lua.Globals["getPokemonMoveDamageTypeFromPC"] = new Func(GetPokemonMoveDamageTypeFromPC); - _lua.Globals["getPokemonMoveStatusFromPC"] = new Func(GetPokemonMoveStatusFromPC); - _lua.Globals["getPokemonNatureFromPC"] = new Func(GetPokemonNatureFromPC); - _lua.Globals["getPokemonAbilityFromPC"] = new Func(GetPokemonAbilityFromPC); - _lua.Globals["getPokemonStatFromPC"] = new Func(GetPokemonStatFromPC); - _lua.Globals["getPokemonEffortValueFromPC"] = new Func(GetPokemonEffortValueFromPC); - _lua.Globals["getPokemonIndividualValueFromPC"] = new Func(GetPokemonIndividualValueFromPC); - _lua.Globals["getPokemonHappinessFromPC"] = new Func(GetPokemonHappinessFromPC); - _lua.Globals["getPokemonRegionFromPC"] = new Func(GetPokemonRegionFromPC); - _lua.Globals["getPokemonOriginalTrainerFromPC"] = new Func(GetPokemonOriginalTrainerFromPC); - _lua.Globals["getPokemonGenderFromPC"] = new Func(GetPokemonGenderFromPC); - _lua.Globals["getPokemonFormFromPC"] = new Func(GetPokemonFormFromPC); - - _lua.Globals["getServer"] = new Func(GetServer); - - // Battle conditions - _lua.Globals["isOpponentShiny"] = new Func(IsOpponentShiny); - _lua.Globals["isAlreadyCaught"] = new Func(IsAlreadyCaught); - _lua.Globals["isWildBattle"] = new Func(IsWildBattle); - _lua.Globals["getActivePokemonNumber"] = new Func(GetActivePokemonNumber); - _lua.Globals["getOpponentId"] = new Func(GetOpponentId); - _lua.Globals["getOpponentName"] = new Func(GetOpponentName); - _lua.Globals["getOpponentHealth"] = new Func(GetOpponentHealth); - _lua.Globals["getOpponentMaxHealth"] = new Func(GetOpponentMaxHealth); - _lua.Globals["getOpponentHealthPercent"] = new Func(GetOpponentHealthPercent); - _lua.Globals["getOpponentLevel"] = new Func(GetOpponentLevel); - _lua.Globals["getOpponentStatus"] = new Func(GetOpponentStatus); - _lua.Globals["getOpponentForm"] = new Func(GetOpponentForm); - _lua.Globals["isOpponentEffortValue"] = new Func(IsOpponentEffortValue); - _lua.Globals["getOpponentEffortValue"] = new Func(GetOpponentEffortValue); - _lua.Globals["getOpponentType"] = new Func(GetOpponentType); - - // Path actions - _lua.Globals["moveToCell"] = new Func(MoveToCell); - _lua.Globals["moveToMap"] = new Func(MoveToMap); - _lua.Globals["moveToRectangle"] = new Func(MoveToRectangle); - - _lua.Globals["moveToNormalGround"] = new Func(MoveToNormalGround); - _lua.Globals["moveToGrass"] = new Func(MoveToGrass); - _lua.Globals["moveToWater"] = new Func(MoveToWater); - _lua.Globals["moveNearExit"] = new Func(MoveNearExit); - _lua.Globals["talkToNpc"] = new Func(TalkToNpc); - _lua.Globals["talkToNpcOnCell"] = new Func(TalkToNpcOnCell); - _lua.Globals["usePokecenter"] = new Func(UsePokecenter); - _lua.Globals["swapPokemon"] = new Func(SwapPokemon); - _lua.Globals["swapPokemonWithLeader"] = new Func(SwapPokemonWithLeader); - _lua.Globals["sortTeamByLevelAscending"] = new Func(SortTeamByLevelAscending); - _lua.Globals["sortTeamByLevelDescending"] = new Func(SortTeamByLevelDescending); - _lua.Globals["sortTeamRangeByLevelAscending"] = new Func(SortTeamRangeByLevelAscending); - _lua.Globals["sortTeamRangeByLevelDescending"] = new Func(SortTeamRangeByLevelDescending); - _lua.Globals["buyItem"] = new Func(BuyItem); - _lua.Globals["relearnMove"] = new Func(RelearnMove); - _lua.Globals["usePC"] = new Func(UsePC); - _lua.Globals["openPCBox"] = new Func(OpenPCBox); - _lua.Globals["depositPokemonToPC"] = new Func(DepositPokemonToPC); - _lua.Globals["withdrawPokemonFromPC"] = new Func(WithdrawPokemonFromPC); - _lua.Globals["swapPokemonFromPC"] = new Func(SwapPokemonFromPC); - _lua.Globals["giveItemToPokemon"] = new Func(GiveItemToPokemon); - _lua.Globals["takeItemFromPokemon"] = new Func(TakeItemFromPokemon); - _lua.Globals["releasePokemonFromTeam"] = new Func(ReleasePokemonFromTeam); - _lua.Globals["releasePokemonFromPC"] = new Func(ReleasePokemonFromPC); - _lua.Globals["enablePrivateMessage"] = new Func(EnablePrivateMessage); - _lua.Globals["disablePrivateMessage"] = new Func(DisablePrivateMessage); - _lua.Globals["enableAutoEvolve"] = new Func(EnableAutoEvolve); - _lua.Globals["disableAutoEvolve"] = new Func(DisableAutoEvolve); - - // Path functions - _lua.Globals["pushDialogAnswer"] = new Action(PushDialogAnswer); - - // General actions - _lua.Globals["useItem"] = new Func(UseItem); - _lua.Globals["useItemOnPokemon"] = new Func(UseItemOnPokemon); - - // Battle actions - _lua.Globals["attack"] = new Func(Attack); - _lua.Globals["weakAttack"] = new Func(WeakAttack); - _lua.Globals["run"] = new Func(Run); - _lua.Globals["sendUsablePokemon"] = new Func(SendUsablePokemon); - _lua.Globals["sendAnyPokemon"] = new Func(SendAnyPokemon); - _lua.Globals["sendPokemon"] = new Func(SendPokemon); - _lua.Globals["useMove"] = new Func(UseMove); - _lua.Globals["useAnyMove"] = new Func(UseAnyMove); - - // Move learning actions - _lua.Globals["forgetMove"] = new Func(ForgetMove); - _lua.Globals["forgetAnyMoveExcept"] = new Func(ForgetAnyMoveExcept); - - // Custom option slider functions - _lua.Globals["setOption"] = new Action(SetOption); - _lua.Globals["getOption"] = new Func(GetOption); - _lua.Globals["setOptionName"] = new Action(SetOptionName); - _lua.Globals["setOptionDescription"] = new Action(SetOptionDescription); - _lua.Globals["removeOption"] = new Action(RemoveOption); - - // Custom text option functions - _lua.Globals["setTextOption"] = new Action(SetTextOption); - _lua.Globals["getTextOption"] = new Func(GetTextOption); - _lua.Globals["setTextOptionName"] = new Action(SetTextOptionName); - _lua.Globals["setTextOptionDescription"] = new Action(SetTextOptionDescription); - _lua.Globals["removeTextOption"] = new Action(RemoveTextOption); - - // File editing actions - _lua.Globals["logToFile"] = new Action(LogToFile); - _lua.Globals["readLinesFromFile"] = new Func(ReadLinesFromFile); - - foreach (string content in _libsContent) - { - CallContent(content); - } - CallContent(_content); - } - - private void CallFunctionSafe(string functionName, params object[] args) - { - try - { - try - { - CallFunction(functionName, false, args); - } - catch (ScriptRuntimeException ex) - { - throw new Exception(ex.DecoratedMessage, ex); - } - } - catch (Exception ex) - { -#if DEBUG - Fatal("Error during the execution of '" + functionName + "': " + ex); -#else - Fatal("Error during the execution of '" + functionName + "': " + ex.Message); -#endif - } - } - - private void CallContent(string content) - { - try - { - TaskUtils.CallActionWithTimeout(() => _lua.DoString(content), delegate - { - throw new Exception("The execution of the script timed out."); - }, TimeoutDelay); - } - catch (SyntaxErrorException ex) - { - throw new Exception(ex.DecoratedMessage, ex); - } - } - - private void CallFunction(string functionName, bool isPathAction, params object[] args) - { - if (_hookedFunctions.ContainsKey(functionName)) - { - foreach (DynValue function in _hookedFunctions[functionName]) - { - CallDynValueFunction(function, "hook:" + functionName, args); - if (isPathAction && _actionExecuted) return; - } - } - CallDynValueFunction(_lua.Globals.Get(functionName), functionName, args); - } - - private void CallDynValueFunction(DynValue function, string functionName, params object[] args) - { - if (function.Type != DataType.Function) return; - TaskUtils.CallActionWithTimeout(() => _lua.Call(function, args), delegate - { - Fatal("The execution of the script timed out (" + functionName + ")."); - }, TimeoutDelay); - } - - private bool ValidateAction(string source, bool inBattle) - { - if (_actionExecuted) - { - Fatal("error: " + source + ": the script can only execute one action per frame."); - return false; - } - if (Bot.Game.IsInBattle != inBattle) - { - if (inBattle) - { - Fatal("error: " + source + " you cannot execute a battle action while not in a battle."); - } - else +using MoonSharp.Interpreter; +using PROBot.Utils; +using PROProtocol; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Media; +using System.Text; + +namespace PROBot.Scripting +{ + public class LuaScript : BaseScript + { + public BotClient Bot { get; private set; } + +#if DEBUG + public int TimeoutDelay = 60000; +#else + public int TimeoutDelay = 3000; +#endif + + private Script _lua; + private string _path; + private string _content; + private IList _libsContent; + private IDictionary> _hookedFunctions; + + private bool _actionExecuted; + + public LuaScript(BotClient bot, string path, string content, IList libsContent) + { + Bot = bot; + _path = Path.GetDirectoryName(path); + _content = content; + _libsContent = libsContent; + } + + public override void Initialize() + { + CreateLuaInstance(); + + Name = _lua.Globals.Get("name").CastToString(); + Author = _lua.Globals.Get("author").CastToString(); + Description = _lua.Globals.Get("description").CastToString(); + } + + public override void Start() + { + CallFunctionSafe("onStart"); + } + + public override void Stop() + { + CallFunctionSafe("onStop"); + } + + public override void Pause() + { + CallFunctionSafe("onPause"); + } + + public override void Resume() + { + CallFunctionSafe("onResume"); + } + + public override void OnDialogMessage(string message) + { + CallFunctionSafe("onDialogMessage", message); + } + + public override void OnBattleMessage(string message) + { + CallFunctionSafe("onBattleMessage", message); + } + + public override void OnSystemMessage(string message) + { + CallFunctionSafe("onSystemMessage", message); + } + + public override void OnWarningMessage(bool differentMap, int distance = -1) + { + CallFunctionSafe("onWarningMessage", differentMap, distance); + } + + public override void OnLearningMove(string moveName, int pokemonIndex) + { + CallFunctionSafe("onLearningMove", moveName, pokemonIndex); + } + + public override bool ExecuteNextAction() + { + string functionName = Bot.Game.IsInBattle ? "onBattleAction" : "onPathAction"; + + _actionExecuted = false; + try + { + CallFunction(functionName, true); + } + catch (ScriptRuntimeException ex) + { + throw new Exception(ex.DecoratedMessage, ex); + } + return _actionExecuted; + } + + private void CreateLuaInstance() + { + _hookedFunctions = new Dictionary>(); + + _lua = new Script(CoreModules.Preset_SoftSandbox | CoreModules.LoadMethods); + _lua.Options.ScriptLoader = new CustomScriptLoader(_path) { ModulePaths = new[] { "?.lua", Directory.GetCurrentDirectory() + "/Libs/?.lua" } }; + _lua.Options.CheckThreadAccess = false; + _lua.Globals["log"] = new Action(Log); + _lua.Globals["fatal"] = new Action(Fatal); + _lua.Globals["logout"] = new Action(Logout); + _lua.Globals["relog"] = new Action(Relog); + _lua.Globals["stringContains"] = new Func(StringContains); + _lua.Globals["playSound"] = new Action(PlaySound); + _lua.Globals["registerHook"] = new Action(RegisterHook); + + // General conditions + _lua.Globals["getPlayerX"] = new Func(GetPlayerX); + _lua.Globals["getPlayerY"] = new Func(GetPlayerY); + _lua.Globals["getAccountName"] = new Func(GetAccountName); + _lua.Globals["getMapName"] = new Func(GetMapName); + _lua.Globals["getPokedexOwned"] = new Func(GetPokedexOwned); + _lua.Globals["getPokedexSeen"] = new Func(GetPokedexSeen); + _lua.Globals["getPokedexEvolved"] = new Func(GetPokedexEvolved); + _lua.Globals["getTeamSize"] = new Func(GetTeamSize); + _lua.Globals["isAccountMember"] = new Func(IsAccountMember); + + _lua.Globals["getPokemonId"] = new Func(GetPokemonId); + _lua.Globals["getPokemonDatabaseId"] = new Func(GetPokemonDatabaseId); + _lua.Globals["getPokemonName"] = new Func(GetPokemonName); + _lua.Globals["getPokemonHealth"] = new Func(GetPokemonHealth); + _lua.Globals["getPokemonHealthPercent"] = new Func(GetPokemonHealthPercent); + _lua.Globals["getPokemonMaxHealth"] = new Func(GetPokemonMaxHealth); + _lua.Globals["getPokemonLevel"] = new Func(GetPokemonLevel); + _lua.Globals["getPokemonTotalExperience"] = new Func(GetPokemonTotalExperience); + _lua.Globals["getPokemonRemainingExperience"] = new Func(GetPokemonRemainingExperience); + _lua.Globals["getPokemonStatus"] = new Func(GetPokemonStatus); + _lua.Globals["getPokemonForm"] = new Func(GetPokemonForm); + _lua.Globals["getPokemonHeldItem"] = new Func(GetPokemonHeldItem); + _lua.Globals["getPokemonUniqueId"] = new Func(GetPokemonUniqueId); + _lua.Globals["getRemainingPowerPoints"] = new Func(GetRemainingPowerPoints); + _lua.Globals["getPokemonMaxPowerPoints"] = new Func(GetPokemonMaxPowerPoints); + _lua.Globals["isPokemonShiny"] = new Func(IsPokemonShiny); + _lua.Globals["getPokemonMoveName"] = new Func(GetPokemonMoveName); + _lua.Globals["getPokemonMoveAccuracy"] = new Func(GetPokemonMoveAccuracy); + _lua.Globals["getPokemonMovePower"] = new Func(GetPokemonMovePower); + _lua.Globals["getPokemonMoveType"] = new Func(GetPokemonMoveType); + _lua.Globals["getPokemonMoveDamageType"] = new Func(GetPokemonMoveDamageType); + _lua.Globals["getPokemonMoveStatus"] = new Func(GetPokemonMoveStatus); + _lua.Globals["getPokemonNature"] = new Func(GetPokemonNature); + _lua.Globals["getPokemonAbility"] = new Func(GetPokemonAbility); + _lua.Globals["getPokemonStat"] = new Func(GetPokemonStat); + _lua.Globals["getPokemonEffortValue"] = new Func(GetPokemonEffortValue); + _lua.Globals["getPokemonIndividualValue"] = new Func(GetPokemonIndividualValue); + _lua.Globals["getPokemonHappiness"] = new Func(GetPokemonHappiness); + _lua.Globals["getPokemonRegion"] = new Func(GetPokemonRegion); + _lua.Globals["getPokemonOriginalTrainer"] = new Func(GetPokemonOriginalTrainer); + _lua.Globals["getPokemonGender"] = new Func(GetPokemonGender); + _lua.Globals["getPokemonType"] = new Func(GetPokemonType); + _lua.Globals["getDamageMultiplier"] = new Func(GetDamageMultiplier); + _lua.Globals["isPokemonUsable"] = new Func(IsPokemonUsable); + _lua.Globals["getUsablePokemonCount"] = new Func(GetUsablePokemonCount); + _lua.Globals["hasMove"] = new Func(HasMove); + _lua.Globals["getActiveBattlers"] = new Func>>(GetActiveBattlers); + _lua.Globals["getActiveDigSpots"] = new Func>>(GetActiveDigSpots); + _lua.Globals["getActiveHeadbuttTrees"] = new Func>>(GetActiveHeadbuttTrees); + _lua.Globals["getActiveBerryTrees"] = new Func>>(GetActiveBerryTrees); + _lua.Globals["getDiscoverableItems"] = new Func>>(GetDiscoverableItems); + _lua.Globals["getNpcData"] = new Func>>(GetNpcData); + _lua.Globals["getMapLinks"] = new Func>>(GetMapLinks); + _lua.Globals["getMapWidth"] = new Func(GetMapWidth); + _lua.Globals["getMapHeight"] = new Func(GetMapHeight); + _lua.Globals["getCellType"] = new Func(GetCellType); + + _lua.Globals["hasItem"] = new Func(HasItem); + _lua.Globals["getItemQuantity"] = new Func(GetItemQuantity); + _lua.Globals["hasItemId"] = new Func(HasItemId); + _lua.Globals["getItemQuantityId"] = new Func(GetItemQuantityID); + + _lua.Globals["hasPokemonInTeam"] = new Func(HasPokemonInTeam); + _lua.Globals["isTeamSortedByLevelAscending"] = new Func(IsTeamSortedByLevelAscending); + _lua.Globals["isTeamSortedByLevelDescending"] = new Func(IsTeamSortedByLevelDescending); + _lua.Globals["isTeamRangeSortedByLevelAscending"] = new Func(IsTeamRangeSortedByLevelAscending); + _lua.Globals["isTeamRangeSortedByLevelDescending"] = new Func(IsTeamRangeSortedByLevelDescending); + _lua.Globals["isNpcVisible"] = new Func(IsNpcVisible); + _lua.Globals["isNpcOnCell"] = new Func(IsNpcOnCell); + _lua.Globals["isShopOpen"] = new Func(IsShopOpen); + _lua.Globals["isRelearningMoves"] = new Func(IsRelearningMoves); + _lua.Globals["getMoney"] = new Func(GetMoney); + _lua.Globals["isMounted"] = new Func(IsMounted); + _lua.Globals["isSurfing"] = new Func(IsSurfing); + _lua.Globals["isPrivateMessageEnabled"] = new Func(IsPrivateMessageEnabled); + _lua.Globals["isNpcInteractionsEnabled"] = new Func(IsNpcInteractionsEnabled); + _lua.Globals["getTime"] = new GetTimeDelegate(GetTime); + _lua.Globals["isMorning"] = new Func(IsMorning); + _lua.Globals["isNoon"] = new Func(IsNoon); + _lua.Globals["isNight"] = new Func(IsNight); + _lua.Globals["isOutside"] = new Func(IsOutside); + _lua.Globals["isAutoEvolve"] = new Func(IsAutoEvolve); + _lua.Globals["setMount"] = new Func(SetMount); + _lua.Globals["setWaterMount"] = new Func(SetWaterMount); + + _lua.Globals["isCurrentPCBoxRefreshed"] = new Func(IsCurrentPCBoxRefreshed); + _lua.Globals["getCurrentPCBoxId"] = new Func(GetCurrentPCBoxId); + _lua.Globals["isPCOpen"] = new Func(IsPCOpen); + _lua.Globals["getCurrentPCBoxId"] = new Func(GetCurrentPCBoxId); + _lua.Globals["getCurrentPCBoxSize"] = new Func(GetCurrentPCBoxSize); + _lua.Globals["getPCBoxCount"] = new Func(GetPCBoxCount); + //_lua.Globals["getPCPokemonCount"] = new Func(GetPCPokemonCount); + + _lua.Globals["getPokemonIdFromPC"] = new Func(GetPokemonIdFromPC); + _lua.Globals["getPokemonDatabaseIdFromPC"] = new Func(GetPokemonDatabaseIdFromPC); + _lua.Globals["getPokemonNameFromPC"] = new Func(GetPokemonNameFromPC); + _lua.Globals["getPokemonHealthFromPC"] = new Func(GetPokemonHealthFromPC); + _lua.Globals["getPokemonHealthPercentFromPC"] = new Func(GetPokemonHealthPercentFromPC); + _lua.Globals["getPokemonMaxHealthFromPC"] = new Func(GetPokemonMaxHealthFromPC); + _lua.Globals["getPokemonLevelFromPC"] = new Func(GetPokemonLevelFromPC); + _lua.Globals["getPokemonTotalExperienceFromPC"] = new Func(GetPokemonTotalExperienceFromPC); + _lua.Globals["getPokemonRemainingExperienceFromPC"] = new Func(GetPokemonRemainingExperienceFromPC); + _lua.Globals["getPokemonStatusFromPC"] = new Func(GetPokemonStatusFromPC); + _lua.Globals["getPokemonTypeFromPC"] = new Func(GetPokemonTypeFromPC); + _lua.Globals["getPokemonHeldItemFromPC"] = new Func(GetPokemonHeldItemFromPC); + _lua.Globals["getPokemonUniqueIdFromPC"] = new Func(GetPokemonUniqueIdFromPC); + _lua.Globals["getPokemonRemainingPowerPointsFromPC"] = new Func(GetPokemonRemainingPowerPointsFromPC); + _lua.Globals["getPokemonMaxPowerPointsFromPC"] = new Func(GetPokemonMaxPowerPointsFromPC); + _lua.Globals["isPokemonFromPCShiny"] = new Func(IsPokemonFromPCShiny); + _lua.Globals["getPokemonMoveNameFromPC"] = new Func(GetPokemonMoveNameFromPC); + _lua.Globals["getPokemonMoveAccuracyFromPC"] = new Func(GetPokemonMoveAccuracyFromPC); + _lua.Globals["getPokemonMovePowerFromPC"] = new Func(GetPokemonMovePowerFromPC); + _lua.Globals["getPokemonMoveTypeFromPC"] = new Func(GetPokemonMoveTypeFromPC); + _lua.Globals["getPokemonMoveDamageTypeFromPC"] = new Func(GetPokemonMoveDamageTypeFromPC); + _lua.Globals["getPokemonMoveStatusFromPC"] = new Func(GetPokemonMoveStatusFromPC); + _lua.Globals["getPokemonNatureFromPC"] = new Func(GetPokemonNatureFromPC); + _lua.Globals["getPokemonAbilityFromPC"] = new Func(GetPokemonAbilityFromPC); + _lua.Globals["getPokemonStatFromPC"] = new Func(GetPokemonStatFromPC); + _lua.Globals["getPokemonEffortValueFromPC"] = new Func(GetPokemonEffortValueFromPC); + _lua.Globals["getPokemonIndividualValueFromPC"] = new Func(GetPokemonIndividualValueFromPC); + _lua.Globals["getPokemonHappinessFromPC"] = new Func(GetPokemonHappinessFromPC); + _lua.Globals["getPokemonRegionFromPC"] = new Func(GetPokemonRegionFromPC); + _lua.Globals["getPokemonOriginalTrainerFromPC"] = new Func(GetPokemonOriginalTrainerFromPC); + _lua.Globals["getPokemonGenderFromPC"] = new Func(GetPokemonGenderFromPC); + _lua.Globals["getPokemonFormFromPC"] = new Func(GetPokemonFormFromPC); + + _lua.Globals["getServer"] = new Func(GetServer); + + // Battle conditions + _lua.Globals["isOpponentShiny"] = new Func(IsOpponentShiny); + _lua.Globals["isAlreadyCaught"] = new Func(IsAlreadyCaught); + _lua.Globals["isWildBattle"] = new Func(IsWildBattle); + _lua.Globals["getActivePokemonNumber"] = new Func(GetActivePokemonNumber); + _lua.Globals["getOpponentId"] = new Func(GetOpponentId); + _lua.Globals["getOpponentName"] = new Func(GetOpponentName); + _lua.Globals["getOpponentHealth"] = new Func(GetOpponentHealth); + _lua.Globals["getOpponentMaxHealth"] = new Func(GetOpponentMaxHealth); + _lua.Globals["getOpponentHealthPercent"] = new Func(GetOpponentHealthPercent); + _lua.Globals["getOpponentLevel"] = new Func(GetOpponentLevel); + _lua.Globals["getOpponentStatus"] = new Func(GetOpponentStatus); + _lua.Globals["getOpponentForm"] = new Func(GetOpponentForm); + _lua.Globals["isOpponentEffortValue"] = new Func(IsOpponentEffortValue); + _lua.Globals["getOpponentEffortValue"] = new Func(GetOpponentEffortValue); + _lua.Globals["getOpponentType"] = new Func(GetOpponentType); + + // Path actions + _lua.Globals["moveToCell"] = new Func(MoveToCell); + _lua.Globals["moveToMap"] = new Func(MoveToMap); + _lua.Globals["moveToRectangle"] = new Func(MoveToRectangle); + + _lua.Globals["moveToNormalGround"] = new Func(MoveToNormalGround); + _lua.Globals["moveToGrass"] = new Func(MoveToGrass); + _lua.Globals["moveToWater"] = new Func(MoveToWater); + _lua.Globals["moveNearExit"] = new Func(MoveNearExit); + _lua.Globals["talkToNpc"] = new Func(TalkToNpc); + _lua.Globals["talkToNpcOnCell"] = new Func(TalkToNpcOnCell); + _lua.Globals["usePokecenter"] = new Func(UsePokecenter); + _lua.Globals["swapPokemon"] = new Func(SwapPokemon); + _lua.Globals["swapPokemonWithLeader"] = new Func(SwapPokemonWithLeader); + _lua.Globals["sortTeamByLevelAscending"] = new Func(SortTeamByLevelAscending); + _lua.Globals["sortTeamByLevelDescending"] = new Func(SortTeamByLevelDescending); + _lua.Globals["sortTeamRangeByLevelAscending"] = new Func(SortTeamRangeByLevelAscending); + _lua.Globals["sortTeamRangeByLevelDescending"] = new Func(SortTeamRangeByLevelDescending); + _lua.Globals["buyItem"] = new Func(BuyItem); + _lua.Globals["relearnMove"] = new Func(RelearnMove); + _lua.Globals["usePC"] = new Func(UsePC); + _lua.Globals["openPCBox"] = new Func(OpenPCBox); + _lua.Globals["depositPokemonToPC"] = new Func(DepositPokemonToPC); + _lua.Globals["withdrawPokemonFromPC"] = new Func(WithdrawPokemonFromPC); + _lua.Globals["swapPokemonFromPC"] = new Func(SwapPokemonFromPC); + _lua.Globals["giveItemToPokemon"] = new Func(GiveItemToPokemon); + _lua.Globals["takeItemFromPokemon"] = new Func(TakeItemFromPokemon); + _lua.Globals["releasePokemonFromTeam"] = new Func(ReleasePokemonFromTeam); + _lua.Globals["releasePokemonFromPC"] = new Func(ReleasePokemonFromPC); + _lua.Globals["enablePrivateMessage"] = new Func(EnablePrivateMessage); + _lua.Globals["disablePrivateMessage"] = new Func(DisablePrivateMessage); + _lua.Globals["enableAutoEvolve"] = new Func(EnableAutoEvolve); + _lua.Globals["disableAutoEvolve"] = new Func(DisableAutoEvolve); + _lua.Globals["enableNpcInteractions"] = new Func(EnableNpcInteractions); + _lua.Globals["disableNpcInteractions"] = new Func(DisableNpcInteractions); + + // Path functions + _lua.Globals["pushDialogAnswer"] = new Action(PushDialogAnswer); + + // General actions + _lua.Globals["useItem"] = new Func(UseItem); + _lua.Globals["useItemOnPokemon"] = new Func(UseItemOnPokemon); + _lua.Globals["useItemOnPokemonMove"] = new Func(UseItemOnPokemonMove); + + // Battle actions + _lua.Globals["attack"] = new Func(Attack); + _lua.Globals["weakAttack"] = new Func(WeakAttack); + _lua.Globals["run"] = new Func(Run); + _lua.Globals["sendUsablePokemon"] = new Func(SendUsablePokemon); + _lua.Globals["sendAnyPokemon"] = new Func(SendAnyPokemon); + _lua.Globals["sendPokemon"] = new Func(SendPokemon); + _lua.Globals["useMove"] = new Func(UseMove); + _lua.Globals["useAnyMove"] = new Func(UseAnyMove); + + // Move learning actions + _lua.Globals["forgetMove"] = new Func(ForgetMove); + _lua.Globals["forgetAnyMoveExcept"] = new Func(ForgetAnyMoveExcept); + + // Custom option slider functions + _lua.Globals["setOption"] = new Action(SetOption); + _lua.Globals["getOption"] = new Func(GetOption); + _lua.Globals["setOptionName"] = new Action(SetOptionName); + _lua.Globals["setOptionDescription"] = new Action(SetOptionDescription); + _lua.Globals["removeOption"] = new Action(RemoveOption); + + // Custom text option functions + _lua.Globals["setTextOption"] = new Action(SetTextOption); + _lua.Globals["getTextOption"] = new Func(GetTextOption); + _lua.Globals["setTextOptionName"] = new Action(SetTextOptionName); + _lua.Globals["setTextOptionDescription"] = new Action(SetTextOptionDescription); + _lua.Globals["removeTextOption"] = new Action(RemoveTextOption); + + // File editing actions + _lua.Globals["logToFile"] = new Action(LogToFile); + _lua.Globals["readLinesFromFile"] = new Func(ReadLinesFromFile); + + foreach (string content in _libsContent) + { + CallContent(content); + } + CallContent(_content); + } + + private void CallFunctionSafe(string functionName, params object[] args) + { + try + { + try + { + CallFunction(functionName, false, args); + } + catch (ScriptRuntimeException ex) + { + throw new Exception(ex.DecoratedMessage, ex); + } + } + catch (Exception ex) + { +#if DEBUG + Fatal("Error during the execution of '" + functionName + "': " + ex); +#else + Fatal("Error during the execution of '" + functionName + "': " + ex.Message); +#endif + } + } + + private void CallContent(string content) + { + try + { + TaskUtils.CallActionWithTimeout(() => _lua.DoString(content), delegate + { + throw new Exception("The execution of the script timed out."); + }, TimeoutDelay); + } + catch (SyntaxErrorException ex) + { + throw new Exception(ex.DecoratedMessage, ex); + } + } + + private void CallFunction(string functionName, bool isPathAction, params object[] args) + { + if (_hookedFunctions.ContainsKey(functionName)) + { + foreach (DynValue function in _hookedFunctions[functionName]) + { + CallDynValueFunction(function, "hook:" + functionName, args); + if (isPathAction && _actionExecuted) return; + } + } + Console.WriteLine(functionName); + CallDynValueFunction(_lua.Globals.Get(functionName), functionName, args); + } + + private void CallDynValueFunction(DynValue function, string functionName, params object[] args) + { + if (function.Type != DataType.Function) return; + TaskUtils.CallActionWithTimeout(() => _lua.Call(function, args), delegate + { + Fatal("The execution of the script timed out (" + functionName + ")."); + }, TimeoutDelay); + } + + private bool ValidateAction(string source, bool inBattle) + { + if (_actionExecuted) + { + Fatal("error: " + source + ": the script can only execute one action per frame."); + return false; + } + if (Bot.Game.IsInBattle != inBattle) + { + if (inBattle) + { + Fatal("error: " + source + " you cannot execute a battle action while not in a battle."); + } + else + { + Fatal("error: " + source + " you cannot execute a path action while in a battle."); + } + return false; + } + return true; + } + + private bool ExecuteAction(bool result) + { + if (result) + { + _actionExecuted = true; + } + return result; + } + + // API: Displays the specified message to the message log. + private void Log(string message) + { + LogMessage(message); + } + + // API: Displays the specified message to the message log and stop the bot. + private void Fatal(string message) + { + LogMessage(message); + Bot.Stop(); + } + + // API: Displays the specified message to the message log and logs out. + private void Logout(string message) + { + LogMessage(message); + Bot.Stop(); + Bot.Logout(false); + } + + // API: Logs out and logs back in after the specified number of seconds. + private void Relog(double delay, string message) + { + LogMessage(message); + Bot.Relog(delay); + } + + // API return an array of all NPCs that can be challenged on the current map. format : {"npcName" = {"x" = x, "y" = y}} + private Dictionary> GetActiveBattlers() + { + var activeBattlers = new Dictionary>(); + foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.CanBattle)) + { + var npcData = new Dictionary(); + npcData["x"] = npc.PositionX; + npcData["y"] = npc.PositionY; + activeBattlers[npc.Name] = npcData; + } + return activeBattlers; + } + + // API return an array of all usable Dig Spots on the currrent map. format : {index = {"x" = x, "y" = y}} + private List> GetActiveDigSpots() + { + var digSpots = new List>(); + foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.Type == 70 || npc.Type == 71)) + { + var npcData = new Dictionary(); + npcData["x"] = npc.PositionX; + npcData["y"] = npc.PositionY; + digSpots.Add(npcData); + } + return digSpots; + } + + // API return an array of all usable Headbutt trees on the currrent map. format : {index = {"x" = x, "y" = y}} + private List> GetActiveHeadbuttTrees() + { + var trees = new List>(); + foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.Type == 101)) + { + var npcData = new Dictionary(); + npcData["x"] = npc.PositionX; + npcData["y"] = npc.PositionY; + trees.Add(npcData); + } + return trees; + } + + // API return an array of all harvestable berry trees on the currrent map. format : {index = {"x" = x, "y" = y}} + private List> GetActiveBerryTrees() + { + int[] berries = { 42, 43, 44, 45, 49, 50, 51, 52, 55, 56, 57, 58, 59, 60, 61, 62 }; + var trees = new List>(); + foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => Array.Exists(berries, e => e == npc.Type))) + { + var npcData = new Dictionary(); + npcData["x"] = npc.PositionX; + npcData["y"] = npc.PositionY; + trees.Add(npcData); + } + return trees; + } + + // API return an array of all discoverable items on the currrent map. format : {index = {"x" = x, "y" = y}} + private List> GetDiscoverableItems() + { + var items = new List>(); + foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.Type == 11 && npc.LosLength < 100)) + { + var npcData = new Dictionary(); + npcData["x"] = npc.PositionX; + npcData["y"] = npc.PositionY; + items.Add(npcData); + } + return items; + } + + // API: Returns npc data on current map, format : { { "x" = x , "y" = y, "type" = type }, {...}, ... } + private List> GetNpcData() + { + var lNpc = new List>(); + foreach (Npc npc in Bot.Game.Map.Npcs) + { + var npcData = new Dictionary + { + ["x"] = DynValue.NewNumber(npc.PositionX), + ["y"] = DynValue.NewNumber(npc.PositionY), + ["type"] = DynValue.NewNumber(npc.Type), + ["name"] = DynValue.NewString(npc.Name), + ["isBattler"] = DynValue.NewBoolean(npc.IsBattler), + ["id"] = DynValue.NewNumber(npc.Id), + ["los"] = DynValue.NewNumber(npc.LosLength) + }; + lNpc.Add(npcData); + } + return lNpc; + } + + // API: Returns an array of all map links on the current map + public List> GetMapLinks() + { + var links = new List>(); + + for (int y = 0; y < Bot.Game.Map.DimensionY; y++) + { + for (int x = 0; x < Bot.Game.Map.DimensionX; x++) + { + if (Bot.Game.Map.Links[x, y]) + { + links.Add(new Dictionary + { + ["x"] = x, + ["y"] = y, + }); + } + } + } + + return links; + } + + // API: The number of cells on the current map in the x direction. + private int GetMapWidth() + { + return Bot.Game.Map.Width; + } + + // API: The number of cells on the current map in the y direction. + private int GetMapHeight() + { + return Bot.Game.Map.Height; + } + + // API: Returns the cell type of the specified cell on the current map. + private string GetCellType(int x, int y) + { + Map map = Bot.Game.Map; + + if (map.HasLink(x, y)) return "Link"; + if (map.IsGrass(x, y)) return "Grass"; + if (map.IsWater(x, y)) return "Water"; + if (map.IsNormalGround(x, y)) return "Normal Ground"; + if (map.IsIce(x, y)) return "Ice"; + if (map.IsPC(x, y)) return "PC"; + + int collider = map.GetCollider(x, y); + if (collider == 2) return "Ledge South"; + if (collider == 3) return "Ledge East"; + if (collider == 4) return "Ledge West"; + if (collider == 11) return "Tree"; + if (collider == 13) return "Rock"; + + return "Collider"; + } + + // API: Returns true if the string contains the specified part, ignoring the case. + private bool StringContains(string haystack, string needle) + { + return haystack.ToUpperInvariant().Contains(needle.ToUpperInvariant()); + } + + // API: Returns playing a custom sound. + private void PlaySound(string file) + { + if (File.Exists(file)) + { + using (SoundPlayer player = new SoundPlayer(file)) + { + player.Play(); + } + }; + } + + // API: Calls the specified function when the specified event occurs. + private void RegisterHook(string eventName, DynValue callback) + { + if (callback.Type != DataType.Function) + { + Fatal("error: registerHook: the callback must be a function."); + return; + } + if (!_hookedFunctions.ContainsKey(eventName)) + { + _hookedFunctions.Add(eventName, new List()); + } + _hookedFunctions[eventName].Add(callback); + } + + // API: Returns the X-coordinate of the current cell. + private int GetPlayerX() + { + return Bot.Game.PlayerX; + } + + // API: Returns the Y-coordinate of the current cell. + private int GetPlayerY() + { + return Bot.Game.PlayerY; + } + + // API: Returns current account name. + private string GetAccountName() + { + return Bot.Account.Name; + } + + // API: Returns the name of the current map. + private string GetMapName() + { + return Bot.Game.MapName; + } + + // API: Returns Owned Entry of the pokedex + private int GetPokedexOwned() + { + return Bot.Game.PokedexOwned; + } + + // API: Returns Seen Entry of the pokedex + private int GetPokedexSeen() + { + return Bot.Game.PokedexSeen; + } + + // API: Returns Evolved Entry of the pokedex + private int GetPokedexEvolved() + { + return Bot.Game.PokedexEvolved; + } + + // API: Returns the amount of pokémon in the team. + private int GetTeamSize() + { + return Bot.Game.Team.Count; + } + + // API: Returns current account's membership status. + private bool IsAccountMember() + { + return Bot.Game.IsMember; + } + + // API: Returns the ID of the specified pokémon in the team. + private int GetPokemonId(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonId: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + return Bot.Game.Team[index - 1].Id; + } + + // API: Returns the Database ID of the specified pokémon in the team. + private int GetPokemonDatabaseId(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonDatabaseId: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + return Bot.Game.Team[index - 1].DatabaseId; + } + + // API: Returns the name of the specified pokémon in the team. + private string GetPokemonName(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonName: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + return Bot.Game.Team[index - 1].Name; + } + + // API: PROShine unique ID of the pokemon of the current box matching the ID. + private int GetPokemonUniqueId(int pokemonUid) + { + if (pokemonUid < 1 || pokemonUid > Bot.Game.Team.Count) + { + Fatal("error: getPokemonUniqueId: tried to retrieve the non-existing pokemon " + pokemonUid + "."); + return -1; + } + PokemonStats iv = Bot.Game.Team[pokemonUid - 1].IV; + + // Converting a base 31 to 10 + // The odds of having twice the same pokemon unique ID being + // 1 against 887,503,680 + int uniqueId = (iv.Attack - 1); + uniqueId += (iv.Defence - 1) * (int)Math.Pow(31, 1); + uniqueId += (iv.Speed - 1) * (int)Math.Pow(31, 2); + uniqueId += (iv.SpAttack - 1) * (int)Math.Pow(31, 3); + uniqueId += (iv.SpDefence - 1) * (int)Math.Pow(31, 4); + uniqueId += (iv.Health - 1) * (int)Math.Pow(31, 5); + return uniqueId; + } + + // API: Returns the current health of the specified pokémon in the team. + private int GetPokemonHealth(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonHealth: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + return Bot.Game.Team[index - 1].CurrentHealth; + } + + // API: Returns the percentage of remaining health of the specified pokémon in the team. + private int GetPokemonHealthPercent(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonHealthPercent: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.CurrentHealth * 100 / pokemon.MaxHealth; + } + + // API: Returns the maximum health of the specified pokémon in the team. + private int GetPokemonMaxHealth(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMaxHealth: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.MaxHealth; + } + + // API: Returns the shyniness of the specified pokémon in the team. + private bool IsPokemonShiny(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: isPokemonShiny: tried to retrieve the non-existing pokemon " + index + "."); + return false; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.IsShiny; + } + + // API: Returns the move of the specified pokémon in the team at the specified index. + private string GetPokemonMoveName(int index, int moveId) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMove: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMove: tried to access an impossible move #" + moveId + "."); + return null; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Moves[moveId - 1].Name; + } + + // API: Returns the move accuracy of the specified pokémon in the team at the specified index. + private int GetPokemonMoveAccuracy(int index, int moveId) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMoveAccuracy: tried to retrieve the non-existing pokemon " + index + "."); + return -1; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveAccuracy: tried to access an impossible move #" + moveId + "."); + return -1; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Moves[moveId - 1].Data.Accuracy; + } + + // API: Returns the move power of the specified pokémon in the team at the specified index. + private int GetPokemonMovePower(int index, int moveId) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMovePower: tried to retrieve the non-existing pokemon " + index + "."); + return -1; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMovePower: tried to access an impossible move #" + moveId + "."); + return -1; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Moves[moveId - 1].Data.Power; + } + + // API: Returns the move type of the specified pokémon in the team at the specified index. + private string GetPokemonMoveType(int index, int moveId) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMoveType: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveType: tried to access an impossible move #" + moveId + "."); + return null; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Moves[moveId - 1].Data.Type.ToString(); + } + + // API: Returns the move damage type of the specified pokémon in the team at the specified index. + private string GetPokemonMoveDamageType(int index, int moveId) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMoveDamageType: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveDamageType: tried to access an impossible move #" + moveId + "."); + return null; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Moves[moveId - 1].Data.DamageType.ToString(); + } + + // API: Returns true if the move of the specified pokémon in the team at the specified index can apply a status . + private bool GetPokemonMoveStatus(int index, int moveId) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMoveStatus: tried to retrieve the non-existing pokemon " + index + "."); + return false; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveStatus: tried to access an impossible move #" + moveId + "."); + return false; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Moves[moveId - 1].Data.Status; + } + + // API: Max move PP of the pokemon of the current box matching the ID. + private int GetPokemonMaxPowerPoints(int index, int moveId) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonMove: tried to retrieve the non-existing pokemon " + index + "."); + return -1; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMove: tried to access an impossible move #" + moveId + "."); + return -1; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Moves[moveId - 1].MaxPoints; + } + + // API: Nature of the pokemon of the current box matching the ID. + private string GetPokemonNature(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonNature: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Nature.Name; + } + + // API: Ability of the pokemon of the current box matching the ID. + private string GetPokemonAbility(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonAbility: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Ability.Name; + } + + // API: Returns the experience total of a pokemon level. + private int GetPokemonTotalExperience(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonTotalXP: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Experience.TotalLevelExperience; + } + + // API: Returns the remaining experience of a pokemon before next level. + private int GetPokemonRemainingExperience(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonRemainingXP: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + Pokemon pokemon = Bot.Game.Team[index - 1]; + return pokemon.Experience.RemainingExperience; + } + + // API: Returns the level of the specified pokémon in the team. + private int GetPokemonLevel(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonLevel: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + return Bot.Game.Team[index - 1].Level; + } + + // API: Returns the happiness of the specified pokémon in the team. + private int GetPokemonHappiness(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonHappiness: tried to retrieve the non-existing pokemon " + index + "."); + return -1; + } + return Bot.Game.Team[index - 1].Happiness; + } + + // API: Returns the region of capture of the specified pokémon in the team. + private string GetPokemonRegion(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonRegion: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + return Bot.Game.Team[index - 1].Region.ToString(); + } + + // API: Returns the original trainer of the specified pokémon in the team. + private string GetPokemonOriginalTrainer(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonOriginalTrainer: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + return Bot.Game.Team[index - 1].OriginalTrainer; + } + + // API: Returns the gender of the specified pokémon in the team. + private string GetPokemonGender(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonGender: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + return Bot.Game.Team[index - 1].Gender; + } + + // API: Returns the type of the specified pokémon in the team as an array of length 2. + private string[] GetPokemonType(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonType: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + + int id = Bot.Game.Team[index - 1].Id; + + if (!TypesManager.Instance.Type1.ContainsKey(id)) + { + return new string[] { "Unknown", "Unknown" }; + } + + return new string[] { TypesManager.Instance.Type1[id].ToString(), TypesManager.Instance.Type2[id].ToString() }; + } + + private string[] _types = { "", "NORMAL", "FIGHTING", "FLYING", "POISON", "GROUND", "ROCK", "BUG", "GHOST", "STEEL", "FIRE", "WATER", "GRASS", "ELECTRIC", "PSYCHIC", "ICE", "DRAGON", "DARK", "FAIRY" }; + + // API: Returns the multiplier of the damage type between an attacking type and one or two defending types. + private double GetDamageMultiplier(string attacker, params DynValue[] defender) + { + if (defender[0].Type == DataType.Table) + { + if (defender[0].Table.Length == 1) + { + defender = new DynValue[] { defender[0].Table.Values.ToArray()[0], DynValue.NewString("") }; + } + else + { + defender = defender[0].Table.Values.ToArray(); + } + } + else if (defender.Length == 1) + { + defender = new DynValue[] { defender[0], DynValue.NewString("") }; + } + + if (attacker.ToUpperInvariant() == "NONE") + attacker = ""; + + if (defender[0].CastToString().ToUpperInvariant() == "NONE") + defender[0] = DynValue.NewString(""); + + if (defender[1].CastToString().ToUpperInvariant() == "NONE") + defender[1] = DynValue.NewString(""); + + if (!Array.Exists(_types, e => e == attacker.ToUpperInvariant())) + { + Fatal("error: getDamageMultiplier: the damage type '" + attacker + "' does not exist."); + return -1; + } + + if (!Array.Exists(_types, e => e == defender[0].CastToString().ToUpperInvariant())) + { + Fatal("error: getDamageMultiplier: the damage type '" + defender[0].CastToString() + "' does not exist."); + return -1; + } + + if (!Array.Exists(_types, e => e == defender[1].CastToString().ToUpperInvariant())) + { + Fatal("error: getDamageMultiplier: the damage type '" + defender[1].CastToString() + "' does not exist."); + return -1; + } + + double power = 1; + + power *= TypesManager.Instance.GetMultiplier(PokemonTypeExtensions.FromName(attacker), PokemonTypeExtensions.FromName(defender[0].CastToString())); + power *= TypesManager.Instance.GetMultiplier(PokemonTypeExtensions.FromName(attacker), PokemonTypeExtensions.FromName(defender[1].CastToString())); + + return power; + } + + // API: Returns the status of the specified pokémon in the team. + private string GetPokemonStatus(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonStatus: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + return Bot.Game.Team[index - 1].Status; + } + + // API: Returns the form of the specified pokémon in the team (0 if no form). + private int GetPokemonForm(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonForm: tried to retrieve the non-existing pokemon " + index + "."); + return 0; + } + return Bot.Game.Team[index - 1].Form; + } + + // API: Returns the item held by the specified pokemon in the team, null if empty. + private string GetPokemonHeldItem(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: getPokemonHeldItem: tried to retrieve the non-existing pokemon " + index + "."); + return null; + } + string itemHeld = Bot.Game.Team[index - 1].ItemHeld; + return itemHeld == string.Empty ? null : itemHeld; + } + + // API: Returns true if the specified pokémon has is alive and has an offensive attack available. + private bool IsPokemonUsable(int index) + { + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: isPokemonUsable: tried to retrieve the non-existing pokemon " + index + "."); + return false; + } + return Bot.AI.IsPokemonUsable(Bot.Game.Team[index - 1]); + } + + // API: Returns the amount of usable pokémon in the team. + private int GetUsablePokemonCount() + { + return Bot.AI.UsablePokemonsCount; + } + + // API: Returns true if the specified pokémon has a move with the specified name. + private bool HasMove(int pokemonIndex, string moveName) + { + if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) + { + Fatal("error: hasMove: tried to retrieve the non-existing pokemon " + pokemonIndex + "."); + return false; + } + + return Bot.Game.PokemonUidHasMove(pokemonIndex, moveName.ToUpperInvariant()); + } + + // API: Returns the remaining power points of the specified move of the specified pokémon in the team. + private int GetRemainingPowerPoints(int pokemonIndex, string moveName) + { + if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) + { + Fatal("error: getRemainingPowerPoints: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); + return 0; + } + + moveName = moveName.ToUpperInvariant(); + PokemonMove move = Bot.Game.Team[pokemonIndex - 1].Moves.FirstOrDefault(m => MovesManager.Instance.GetMoveData(m.Id)?.Name.ToUpperInvariant() == moveName); + if (move == null) + { + Fatal("error: getRemainingPowerPoints: the pokémon " + pokemonIndex + " does not have a move called '" + moveName + "'."); + return 0; + } + + return move.CurrentPoints; + } + + // API: Returns the value for the specified stat of the specified pokémon in the team. + private int GetPokemonStat(int pokemonIndex, string statType) + { + if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) + { + Fatal("error: getPokemonStat: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); + return 0; + } + + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: getPokemonStat: the stat '" + statType + "' does not exist."); + return 0; + } + + return Bot.Game.Team[pokemonIndex - 1].Stats.GetStat(_stats[statType.ToUpperInvariant()]); + } + + // API: Returns the effort value for the specified stat of the specified pokémon in the team. + private int GetPokemonEffortValue(int pokemonIndex, string statType) + { + if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) + { + Fatal("error: getPokemonEffortValue: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); + return 0; + } + + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: getPokemonEffortValue: the stat '" + statType + "' does not exist."); + return 0; + } + + return Bot.Game.Team[pokemonIndex - 1].EV.GetStat(_stats[statType.ToUpperInvariant()]); + } + + // API: Returns the individual value for the specified stat of the specified pokémon in the team. + private int GetPokemonIndividualValue(int pokemonIndex, string statType) + { + if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) + { + Fatal("error: getPokemonIndividualValue: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); + return 0; + } + + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: getPokemonIndividualValue: the stat '" + statType + "' does not exist."); + return 0; + } + + return Bot.Game.Team[pokemonIndex - 1].IV.GetStat(_stats[statType.ToUpperInvariant()]); + } + + // API: Returns true if the specified item is in the inventory. + private bool HasItem(string itemName) + { + return Bot.Game.HasItemName(itemName.ToUpperInvariant()); + } + + // API: Returns the quantity of the specified item in the inventory. + private int GetItemQuantity(string itemName) + { + return Bot.Game.GetItemFromName(itemName.ToUpperInvariant())?.Quantity ?? 0; + } + + // API: Returns true if the specified item is in the inventory. + private bool HasItemId(int itemid) + { + return Bot.Game.HasItemId(itemid); + } + // API: Returns the quantity of the specified item in the inventory. + private int GetItemQuantityID(int itemid) + { + return Bot.Game.GetItemFromId(itemid)?.Quantity ?? 0; + } + + // API: Returns true if the specified pokémon is present in the team. + private bool HasPokemonInTeam(string pokemonName) + { + return Bot.Game.HasPokemonInTeam(pokemonName.ToUpperInvariant()); + } + + // API: Returns true if the team is sorted by level in ascending order. + private bool IsTeamSortedByLevelAscending() + { + return IsTeamSortedByLevel(true, 1, 6); + } + + // API: Returns true if the team is sorted by level in descending order. + private bool IsTeamSortedByLevelDescending() + { + return IsTeamSortedByLevel(false, 1, 6); + } + + // API: Returns true if the specified part of the team is sorted by level in ascending order. + private bool IsTeamRangeSortedByLevelAscending(int fromIndex, int toIndex) + { + return IsTeamSortedByLevel(true, fromIndex, toIndex); + } + + // API: Returns true if the specified part of the team the team is sorted by level in descending order. + private bool IsTeamRangeSortedByLevelDescending(int fromIndex, int toIndex) + { + return IsTeamSortedByLevel(false, fromIndex, toIndex); + } + + private bool IsTeamSortedByLevel(bool ascending, int from, int to) + { + from = Math.Max(from, 1); + to = Math.Min(to, Bot.Game.Team.Count); + + int level = ascending ? 0 : int.MaxValue; + for (int i = from - 1; i < to; ++i) + { + Pokemon pokemon = Bot.Game.Team[i]; + if (ascending && pokemon.Level < level) return false; + if (!ascending && pokemon.Level > level) return false; + level = pokemon.Level; + } + return true; + } + + // API: Returns true if there is a visible NPC with the specified name on the map. + private bool IsNpcVisible(string npcName) + { + npcName = npcName.ToUpperInvariant(); + return Bot.Game.Map.Npcs.Any(npc => npc.Name.ToUpperInvariant() == npcName); + } + + // API: Returns true if there is a visible NPC the specified coordinates. + private bool IsNpcOnCell(int cellX, int cellY) + { + return Bot.Game.Map.Npcs.Any(npc => npc.PositionX == cellX && npc.PositionY == cellY); + } + + // API: Returns true if there is a shop opened. + private bool IsShopOpen() + { + return Bot.Game.OpenedShop != null; + } + + // API: Returns true if the player is relearning the move of a Pokemon from an NPC. + private bool IsRelearningMoves() + { + return Bot.Game.MoveRelearner != null; + } + + // API: Returns the amount of money in the inventory. + private int GetMoney() + { + return Bot.Game.Money; + } + + // API: Returns true if the player is riding a mount or the bicycle. + private bool IsMounted() + { + return Bot.Game.IsBiking; + } + + // API: Returns true if the player is surfing + private bool IsSurfing() + { + return Bot.Game.IsSurfing; + } + + // API: Returns true if the opponent pokémon is shiny. + private bool IsOpponentShiny() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: isOpponentShiny is only usable in battle."); + return false; + } + return Bot.Game.ActiveBattle.IsShiny; + } + + // API: Returns true if the opponent pokémon has already been caught and has a pokédex entry. + private bool IsAlreadyCaught() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: isAlreadyCaught is only usable in battle."); + return false; + } + return Bot.Game.ActiveBattle.AlreadyCaught; + } + + // API: Returns true if the current battle is against a wild pokémon. + private bool IsWildBattle() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: isWildBattle is only usable in battle."); + return false; + } + return Bot.Game.ActiveBattle.IsWild; + } + + // API: Returns the index of the active team pokémon in the current battle. + private int GetActivePokemonNumber() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getActivePokemonNumber is only usable in battle."); + return 0; + } + return Bot.Game.ActiveBattle.SelectedPokemonIndex + 1; + } + + // API: Returns the id of the opponent pokémon in the current battle. + private int GetOpponentId() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentId can only be used in battle."); + return 0; + } + return Bot.Game.ActiveBattle.OpponentId; + } + + // API: Returns the name of the opponent pokémon in the current battle. + private string GetOpponentName() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentName can only be used in battle."); + return null; + } + return PokemonNamesManager.Instance.Names[Bot.Game.ActiveBattle.OpponentId]; + } + + // API: Returns the current health of the opponent pokémon in the current battle. + private int GetOpponentHealth() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentHealth can only be used in battle."); + return 0; + } + return Bot.Game.ActiveBattle.CurrentHealth; + } + + // API: Returns the maximum health of the opponent pokémon in the current battle. + private int GetOpponentMaxHealth() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentMaxHealth can only be used in battle."); + return 0; + } + return Bot.Game.ActiveBattle.OpponentHealth; + } + + // API: Returns the percentage of remaining health of the opponent pokémon in the current battle. + private int GetOpponentHealthPercent() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentHealthPercent can only be used in battle."); + return 0; + } + return Bot.Game.ActiveBattle.CurrentHealth * 100 / Bot.Game.ActiveBattle.OpponentHealth; + } + + // API: Returns the level of the opponent pokémon in the current battle. + private int GetOpponentLevel() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentLevel can only be used in battle."); + return 0; + } + return Bot.Game.ActiveBattle.OpponentLevel; + } + + // API: Returns the status of the opponent pokémon in the current battle. + private string GetOpponentStatus() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentStatus can only be used in battle."); + return null; + } + return Bot.Game.ActiveBattle.OpponentStatus; + } + + // API: Returns the form of the opponent pokémon in the current battle (0 if no form). + private int GetOpponentForm() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentForm can only be used in battle."); + return 0; + } + return Bot.Game.ActiveBattle.AlternateForm; + } + + private static Dictionary _stats = new Dictionary() + { + { "HP", StatType.Health }, + { "HEALTH", StatType.Health }, + { "ATK", StatType.Attack }, + { "ATTACK", StatType.Attack }, + { "DEF", StatType.Defence }, + { "DEFENCE", StatType.Defence }, + { "DEFENSE", StatType.Defence }, + { "SPATK", StatType.SpAttack }, + { "SPATTACK", StatType.SpAttack }, + { "SPDEF", StatType.SpDefence }, + { "SPDEFENCE", StatType.SpDefence }, + { "SPDEFENSE", StatType.SpDefence }, + { "SPD", StatType.Speed }, + { "SPEED", StatType.Speed } + }; + + // API: Returns true if the opponent is only giving the specified effort value. + private bool IsOpponentEffortValue(string statType) + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: isOpponentEffortValue can only be used in battle."); + return false; + } + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: isOpponentEffortValue: the stat '" + statType + "' does not exist."); + return false; + } + if (!EffortValuesManager.Instance.BattleValues.ContainsKey(Bot.Game.ActiveBattle.OpponentId)) + { + return false; + } + + PokemonStats stats = EffortValuesManager.Instance.BattleValues[Bot.Game.ActiveBattle.OpponentId]; + return stats.HasOnly(_stats[statType.ToUpperInvariant()]); + } + + // API: Returns the amount of a particular EV given by the opponent. + private int GetOpponentEffortValue(string statType) + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentEffortValue can only be used in battle."); + return -1; + } + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: getOpponentEffortValue: the stat '" + statType + "' does not exist."); + return -1; + } + if (!EffortValuesManager.Instance.BattleValues.ContainsKey(Bot.Game.ActiveBattle.OpponentId)) + { + return -1; + } + + PokemonStats stats = EffortValuesManager.Instance.BattleValues[Bot.Game.ActiveBattle.OpponentId]; + return stats.GetStat(_stats[statType.ToUpperInvariant()]); + } + + // API: Returns the type of the opponent pokémon in the current battle as an array of length 2. + private string[] GetOpponentType() + { + if (!Bot.Game.IsInBattle) + { + Fatal("error: getOpponentType can only be used in battle."); + return null; + } + + int id = Bot.Game.ActiveBattle.OpponentId; + + if (!TypesManager.Instance.Type1.ContainsKey(id)) + { + return new string[] { "Unknown", "Unknown" }; + } + + return new string[] { TypesManager.Instance.Type1[id].ToString(), TypesManager.Instance.Type2[id].ToString() }; + } + + // API: Moves to the specified coordinates. + private bool MoveToCell(int x, int y) + { + if (!ValidateAction("moveToCell", false)) return false; + + return ExecuteAction(Bot.MoveToCell(x, y)); + } + + // API: Moves to the nearest cell teleporting to the specified map. + private bool MoveToMap(string mapName) + { + Fatal("error: moveToMap: this function is no longer available, please use moveToCell instead."); + return false; + } + + // API: Moves to a random accessible cell of the specified rectangle. + private bool MoveToRectangle(params DynValue[] values) + { + if (values.Length != 1 && values.Length != 4 || + (values.Length == 1 && values[0].Type != DataType.Table) || + (values.Length == 4 + && (values[0].Type != DataType.Number || values[1].Type != DataType.Number + || values[2].Type != DataType.Number || values[3].Type != DataType.Number))) + { + Fatal("error: moveToRectangle: must receive either a table or four numbers."); + return false; + } + if (values.Length == 1) + { + values = values[0].Table.Values.ToArray(); + } + return MoveToRectangle((int)values[0].Number, (int)values[1].Number, (int)values[2].Number, (int)values[3].Number); + } + + // API: Moves to a random accessible cell of the specified rectangle. + private bool MoveToRectangle(int minX, int minY, int maxX, int maxY) + { + if (!ValidateAction("moveToRectangle", false)) return false; + + if (minX > maxX || minY > maxY) + { + Fatal("error: moveToRectangle: the maximum cell cannot be less than the minimum cell."); + return false; + } + + int x; + int y; + int tries = 0; + do + { + if (++tries > 100) return false; + x = Bot.Game.Rand.Next(minX, maxX + 1); + y = Bot.Game.Rand.Next(minY, maxY + 1); + } while (x == Bot.Game.PlayerX && y == Bot.Game.PlayerY); + + return ExecuteAction(Bot.MoveToCell(x, y)); + } + + // API: Move randomly avoiding water and links. + private bool MoveToNormalGround() + { + if (!ValidateAction("moveToNormalGround", false)) return false; + + return ExecuteAction(MoveToCellType((x, y) => Bot.Game.Map.IsNormalGround(x, y))); + } + + // API: Moves to the nearest grass patch then move randomly inside it. + private bool MoveToGrass() + { + if (!ValidateAction("moveToGrass", false)) return false; + + return ExecuteAction(MoveToCellType((x, y) => Bot.Game.Map.IsGrass(x, y))); + } + + // API: Moves to the nearest water area then move randomly inside it. + private bool MoveToWater() + { + if (!ValidateAction("moveToWater", false)) return false; + + return ExecuteAction(MoveToCellType((x, y) => Bot.Game.Map.IsWater(x, y))); + } + + private bool MoveToCellType(Func cellTypePredicate) + { + bool alreadyInCell = cellTypePredicate(Bot.Game.PlayerX, Bot.Game.PlayerY); + + List> cells = new List>(); + + for (int x = 0; x < Bot.Game.Map.Width; ++x) + { + for (int y = 0; y < Bot.Game.Map.Height; ++y) + { + if (cellTypePredicate(x, y) && (x != Bot.Game.PlayerX || y != Bot.Game.PlayerY)) + { + int distance = Bot.Game.DistanceTo(x, y); + cells.Add(new Tuple(x, y, distance)); + } + } + } + + List> trash = new List>(); + if (alreadyInCell) + { + foreach (var cell in cells) + { + if (cell.Item3 >= 10) + { + trash.Add(cell); + } + } + } + else + { + int minDistance = -1; + foreach (var cell in cells) + { + if (minDistance == -1 || cell.Item3 < minDistance) + { + minDistance = cell.Item3; + } + } + foreach (var cell in cells) + { + if (cell.Item3 > minDistance + 5) + { + trash.Add(cell); + } + } + } + while (trash.Count > 0) + { + cells.Remove(trash[0]); + trash.RemoveAt(0); + } + + if (cells.Count > 0) + { + var randomCell = cells[Bot.Game.Rand.Next(cells.Count)]; + return Bot.MoveToCell(randomCell.Item1, randomCell.Item2); + } + return false; + } + + // API: Moves near the cell teleporting to the specified map. + private bool MoveNearExit(string mapName) + { + Fatal("error: moveNearExit: this function is no longer available, please use moveToNormalGround instead."); + return false; + } + + // API: Moves then talk to NPC specified by its name. + private bool TalkToNpc(string npcName) + { + if (!ValidateAction("talkToNpc", false)) return false; + + npcName = npcName.ToUpperInvariant(); + Npc target = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.Name.ToUpperInvariant() == npcName); + if (target == null) + { + Fatal("error: talkToNpc: could not find the NPC '" + npcName + "'."); + return false; + } + + return ExecuteAction(Bot.TalkToNpc(target)); + } + + // API: Moves then talk to NPC located on the specified cell. + private bool TalkToNpcOnCell(int cellX, int cellY) + { + if (!ValidateAction("talkToNpcOnCell", false)) return false; + + Npc target = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.PositionX == cellX && npc.PositionY == cellY); + if (target == null) + { + Fatal("error: talkToNpcOnCell: could not find any NPC on the cell [" + cellX + "," + cellY + "]."); + return false; + } + + return ExecuteAction(Bot.TalkToNpc(target)); + } + + // API: Moves to the Nurse Joy then talk to the cell below her. + private bool UsePokecenter() + { + if (!ValidateAction("usePokecenter", false)) return false; + + Npc nurse = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.Name.StartsWith("Nurse")); + if (nurse == null) + { + Fatal("error: usePokecenter: could not find the Nurse Joy."); + return false; + } + Npc target = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.PositionX == nurse.PositionX && npc.PositionY == nurse.PositionY + 1); + if (target == null) + { + Fatal("error: usePokecenter: could not find the entity below the Nurse Joy."); + return false; + } + + return ExecuteAction(Bot.TalkToNpc(target)); + } + + // API: Swaps the two pokémon specified by their position in the team. + private bool SwapPokemon(int index1, int index2) + { + if (!ValidateAction("swapPokemon", false)) return false; + + return ExecuteAction(Bot.Game.SwapPokemon(index1, index2)); + } + + // API: Swaps the first pokémon with the specified name with the leader of the team. + private bool SwapPokemonWithLeader(string pokemonName) + { + if (!ValidateAction("swapPokemonWithLeader", false)) return false; + + Pokemon pokemon = Bot.Game.FindFirstPokemonInTeam(pokemonName.ToUpperInvariant()); + if (pokemon == null) + { + Fatal("error: swapPokemonWithLeader: there is no pokémon '" + pokemonName + "' in the team."); + return false; + } + if (pokemon.Uid == 1) + { + Fatal("error: swapPokemonWithLeader: '" + pokemonName + "' is already the leader of the team."); + return false; + } + + return ExecuteAction(Bot.Game.SwapPokemon(1, pokemon.Uid)); + } + + // API: Sorts the pokémon in the team by level in ascending order, one pokémon at a time. + private bool SortTeamByLevelAscending() + { + if (!ValidateAction("sortTeamByLevelAscending", false)) return false; + + return ExecuteAction(SortTeamByLevel(true, 1, 6)); + } + + // API: Sorts the pokémon in the team by level in descending order, one pokémon at a time. + private bool SortTeamByLevelDescending() + { + if (!ValidateAction("sortTeamByLevelDescending", false)) return false; + + return ExecuteAction(SortTeamByLevel(false, 1, 6)); + } + + // API: Sorts the specified part of the team by level in ascending order, one pokémon at a time. + private bool SortTeamRangeByLevelAscending(int fromIndex, int toIndex) + { + if (!ValidateAction("sortTeamRangeByLevelAscending", false)) return false; + + return ExecuteAction(SortTeamByLevel(true, fromIndex, toIndex)); + } + + // API: Sorts the specified part of the team by level in descending order, one pokémon at a time. + private bool SortTeamRangeByLevelDescending(int fromIndex, int toIndex) + { + if (!ValidateAction("sortTeamRangeByLevelDescending", false)) return false; + + return ExecuteAction(SortTeamByLevel(false, fromIndex, toIndex)); + } + + private bool SortTeamByLevel(bool ascending, int from, int to) + { + from = Math.Max(from, 1); + to = Math.Min(to, Bot.Game.Team.Count); + + for (int i = from - 1; i < to - 1; ++i) + { + int currentIndex = i; + int currentLevel = Bot.Game.Team[i].Level; + for (int j = i + 1; j < to; ++j) + { + if ((ascending && Bot.Game.Team[j].Level < currentLevel) || + (!ascending && Bot.Game.Team[j].Level > currentLevel)) + { + currentIndex = j; + currentLevel = Bot.Game.Team[j].Level; + } + } + + if (currentIndex != i) + { + Bot.Game.SwapPokemon(i + 1, currentIndex + 1); + return true; + } + } + return false; + } + + // API: Return the state Auto Evolve + private bool IsAutoEvolve() + { + return Bot.PokemonEvolver.IsEnabled; + } + + // API: Enable auto evolve on PrO Shine client. + private bool EnableAutoEvolve() + { + Bot.PokemonEvolver.IsEnabled = true; + return Bot.PokemonEvolver.IsEnabled; + } + + // API: Disable auto evolve on PrO Shine client. + private bool DisableAutoEvolve() + { + Bot.PokemonEvolver.IsEnabled = false; + return !Bot.PokemonEvolver.IsEnabled; + } + + // API: Check if the private message from normal users are blocked. + private bool IsPrivateMessageEnabled() + { + return Bot.Game.IsPrivateMessageOn; + } + + // API: Enable private messages from users. + private bool EnablePrivateMessage() + { + return ExecuteAction(Bot.Game.PrivateMessageOn()); + } + + // API: Disable private messages from users. + private bool DisablePrivateMessage() + { + return ExecuteAction(Bot.Game.PrivateMessageOff()); + } + + // API: Returns true if the bot is checking for npc interactions. + private bool IsNpcInteractionsEnabled() + { + return Bot.Game.DisableMovingNpcInteractions; + } + + // API: Enables npc interactions. + private bool EnableNpcInteractions() + { + return Bot.Game.DisableMovingNpcInteractions = true; + } + + // API: Disables npc interactions. + private bool DisableNpcInteractions() + { + return !(Bot.Game.DisableMovingNpcInteractions = false); + } + + private delegate int GetTimeDelegate(out int minute); + + // API: Return the current in game hour and minute. + private int GetTime(out int minute) + { + DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); + minute = dt.Minute; + return dt.Hour; + } + + // API: Return true if morning time. + private bool IsMorning() + { + DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); + if (dt.Hour >= 4 && dt.Hour < 10) + { + return true; + } + return false; + } + + // API: Return true if noon time. + private bool IsNoon() + { + DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); + if (dt.Hour >= 10 && dt.Hour < 20) + { + return true; + } + return false; + } + + // API: Return true if night time. + private bool IsNight() + { + DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); + if (dt.Hour >= 20 || dt.Hour < 4) + { + return true; + } + return false; + } + + // API: Return true if the character is outside. + private bool IsOutside() + { + return Bot.Game.Map.IsOutside; + } + + // API: Check if the PC is open. Moving close the PC, usePC() opens it. + private bool IsPCOpen() + { + return Bot.Game.IsPCOpen; + } + + // API: Move to the PC and opens it, refreshing the first box. + private bool UsePC() + { + if (!ValidateAction("usePc", false)) return false; + + return ExecuteAction(Bot.OpenPC()); + } + + // API: Open box from the PC. + private bool OpenPCBox(int boxId) + { + if (!ValidateAction("openPCBox", false)) return false; + + if (!Bot.Game.IsPCOpen) + { + Fatal("error: openPCBox: tried to open box #" + boxId + " while the PC is closed."); + } + return ExecuteAction(Bot.Game.RefreshPCBox(boxId)); + } + + // API: Withdraw a pokemon from a known box. + private bool WithdrawPokemonFromPC(int boxId, int boxPokemonId) + { + if (!ValidateAction("withdrawPokemonFromPC", false)) return false; + + if (!IsPCAccessValid("withdrawPokemonFromPC", boxId, boxPokemonId)) + { + return false; + } + + if (Bot.Game.WithdrawPokemonFromPC(boxId, boxPokemonId)) + { + return ExecuteAction(Bot.Game.RefreshPCBox(boxId)); + } + return false; + } + + // API: Deposit a pokemon to the pc. + private bool DepositPokemonToPC(int pokemonUid) + { + if (!ValidateAction("depositPokemonToPC", false)) return false; + + if (Bot.Game.DepositPokemonToPC(pokemonUid)) + { + return ExecuteAction(Bot.Game.RefreshCurrentPCBox()); + } + return false; + } + + // API: Swap a pokemon from the team with a pokemon from the pc. + private bool SwapPokemonFromPC(int boxId, int boxPokemonId, int pokemonUid) + { + if (!ValidateAction("swapPokemonFromPC", false)) return false; + + if (!IsPCAccessValid("swapPokemonFromPC", boxId, boxPokemonId)) + { + return false; + } + + if (Bot.Game.SwapPokemonFromPC(boxId, boxPokemonId, pokemonUid)) + { + return ExecuteAction(Bot.Game.RefreshCurrentPCBox()); + } + return false; + } + + // API: Get the active PC Box. + private int GetCurrentPCBoxId() + { + if (!Bot.Game.IsPCOpen) + { + return -1; + } + return Bot.Game.CurrentPCBoxId; + } + + // API: Return the number of boxes in the PC + private int GetPCBoxCount() + { + if (!Bot.Game.IsPCOpen || Bot.Game.IsPCBoxRefreshing) + { + return -1; + } + return 67; // hardcoded in the client + } + + // API: Return the number of pokemon in the PC + /*private int GetPCPokemonCount() + { + // The PCGreatestUid is only known after the first box refresh + if (!Bot.Game.IsPCOpen || Bot.Game.PCGreatestUid == -1 || Bot.Game.IsPCBoxRefreshing) + { + return -1; + } + return Bot.Game.PCGreatestUid - 7; + }*/ + + // API: Is the currentPcBox refreshed yet? + private bool IsCurrentPCBoxRefreshed() + { + if (!Bot.Game.IsPCOpen || Bot.Game.IsPCBoxRefreshing) + { + return false; + } + return true; + } + + // API: Current box size. + private int GetCurrentPCBoxSize() + { + if (!Bot.Game.IsPCOpen || Bot.Game.IsPCBoxRefreshing) + { + return -1; + } + return Bot.Game.CurrentPCBox.Count; + } + + private bool IsPCAccessValid(string functionName, int boxId, int boxPokemonId) + { + if (!Bot.Game.IsPCOpen) + { + Fatal("error: " + functionName + ": tried to access box #" + boxId + " while the PC is closed."); + return false; + } + if (Bot.Game.IsPCBoxRefreshing) + { + Fatal("error: " + functionName + ": tried to access box #" + boxId + " while the box is refreshing."); + return false; + } + if (boxId != Bot.Game.CurrentPCBoxId) + { + Fatal("error: " + functionName + ": tried to access box #" + boxId + " different from the currently loaded box."); + return false; + } + if (boxPokemonId < 1 || boxPokemonId > Bot.Game.CurrentPCBox.Count) + { + Fatal("error: " + functionName + ": tried to access the unknown pokemon #" + boxPokemonId + " of the box #" + boxId + "."); + return false; + } + return true; + } + + // API: Name of the pokemon of the current box matching the ID. + private string GetPokemonNameFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonNameFromPC", boxId, boxPokemonId)) + { + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Name; + } + + // API: Pokedex ID of the pokemon of the current box matching the ID. + private int GetPokemonIdFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonIdFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Id; + } + + // API: Database ID of the pokemon of the current box matching the ID. + private int GetPokemonDatabaseIdFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonDatabaseIdFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].DatabaseId; + } + + // API: PROShine custom unique ID of the pokemon of the current box matching the ID. + private int GetPokemonUniqueIdFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonUniqueIdFromPC", boxId, boxPokemonId)) + { + return -1; + } + PokemonStats iv = Bot.Game.CurrentPCBox[boxPokemonId - 1].IV; + + // Converting a base 31 to 10 + // The odds of having twice the same pokemon unique ID being + // 1 against 887,503,680 + int uniqueId = (iv.Attack - 1); + uniqueId += (iv.Defence - 1) * (int)Math.Pow(31, 1); + uniqueId += (iv.Speed - 1) * (int)Math.Pow(31, 2); + uniqueId += (iv.SpAttack - 1) * (int)Math.Pow(31, 3); + uniqueId += (iv.SpDefence - 1) * (int)Math.Pow(31, 4); + uniqueId += (iv.Health - 1) * (int)Math.Pow(31, 5); + return uniqueId; + } + + // API: Current HP of the pokemon of the current box matching the ID. + private int GetPokemonHealthFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonCurrentHealthFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].CurrentHealth; + } + + // API: Returns the percentage of remaining health of the specified pokémon in the team. + private int GetPokemonHealthPercentFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonCurrentHealthPercentFromPC", boxId, boxPokemonId)) + { + return -1; + } + Pokemon pokemon = Bot.Game.CurrentPCBox[boxPokemonId - 1]; + return pokemon.CurrentHealth * 100 / pokemon.MaxHealth; + } + + // API: Max HP of the pokemon of the current box matching the ID. + private int GetPokemonMaxHealthFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonMaxHealthFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].MaxHealth; + } + + // API: Level of the pokemon of the current box matching the ID. + private int GetPokemonLevelFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonMaxHealthFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Level; + } + + // API: Total of experience cost of a level for the pokemon of the current box matching the ID. + private int GetPokemonTotalExperienceFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonTotalXPFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Experience.TotalLevelExperience; + } + + // API: Remaining experience before the next level of the pokemon of the current box matching the ID. + private int GetPokemonRemainingExperienceFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonRemainingXPFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Experience.RemainingExperience; + } + + // API: Shyniness of the pokemon of the current box matching the ID. + private bool IsPokemonFromPCShiny(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("isPokemonFromPCShiny", boxId, boxPokemonId)) + { + return false; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].IsShiny; + } + + // API: Move of the pokemon of the current box matching the ID. + private string GetPokemonMoveNameFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMoveNameFromPC", boxId, boxPokemonId)) + { + return null; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveNameFromPC: tried to access an impossible move #" + moveId + "."); + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Name; + } + + // API: Returns the move accuracy of the specified pokémon in the box at the specified index. + private int GetPokemonMoveAccuracyFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMoveAccuracyFromPC", boxId, boxPokemonId)) + { + return -1; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveAccuracyFromPC: tried to access an impossible move #" + moveId + "."); + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Accuracy; + } + + // API: Returns the move power of the specified pokémon in the box at the specified index. + private int GetPokemonMovePowerFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMovePowerFromPC", boxId, boxPokemonId)) + { + return -1; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMovePowerFromPC: tried to access an impossible move #" + moveId + "."); + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Power; + } + + // API: Returns the move type of the specified pokémon in the box at the specified index. + private string GetPokemonMoveTypeFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMoveTypeFromPC", boxId, boxPokemonId)) + { + return null; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveTypeFromPC: tried to access an impossible move #" + moveId + "."); + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Type.ToString(); + } + + // API: Returns the move damage type of the specified pokémon in the box at the specified index. + private string GetPokemonMoveDamageTypeFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMoveDamageTypeFromPC", boxId, boxPokemonId)) + { + return null; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveDamageTypeFromPC: tried to access an impossible move #" + moveId + "."); + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.DamageType.ToString(); + } + + // API: Returns true if the move of the specified pokémon in the box at the specified index can apply a status . + private bool GetPokemonMoveStatusFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMoveStatusTypeFromPC", boxId, boxPokemonId)) + { + return false; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveStatusTypeFromPC: tried to access an impossible move #" + moveId + "."); + return false; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Status; + } + + // API: Current move PP of the pokemon of the current box matching the ID. + private int GetPokemonRemainingPowerPointsFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMoveCurrentPPFromPC", boxId, boxPokemonId)) + { + return -1; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveCurrentPPFromPC: tried to access an impossible move #" + moveId + "."); + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].CurrentPoints; + } + + // API: Max move PP of the pokemon of the current box matching the ID. + private int GetPokemonMaxPowerPointsFromPC(int boxId, int boxPokemonId, int moveId) + { + if (!IsPCAccessValid("getPokemonMoveMaxPPFromPC", boxId, boxPokemonId)) + { + return -1; + } + if (moveId < 1 || moveId > 4) + { + Fatal("error: getPokemonMoveMaxPPFromPC: tried to access an impossible move #" + moveId + "."); + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].MaxPoints; + } + + // API: Nature of the pokemon of the current box matching the ID. + private string GetPokemonNatureFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonNatureFromPC", boxId, boxPokemonId)) + { + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Nature.Name; + } + + // API: Ability of the pokemon of the current box matching the ID. + private string GetPokemonAbilityFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonAbilityFromPC", boxId, boxPokemonId)) + { + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Ability.Name; + } + + // API: Returns the value for the specified stat of the specified pokémon in the PC. + private int GetPokemonStatFromPC(int boxId, int boxPokemonId, string statType) + { + if (!IsPCAccessValid("getPokemonStatFromPC", boxId, boxPokemonId)) + { + return -1; + } + + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: getPokemonStatFromPC: the stat '" + statType + "' does not exist."); + return 0; + } + + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Stats.GetStat(_stats[statType.ToUpperInvariant()]); + } + + // API: Returns the effort value for the specified stat of the specified pokémon in the PC. + private int GetPokemonEffortValueFromPC(int boxId, int boxPokemonId, string statType) + { + if (!IsPCAccessValid("getPokemonEffortValueFromPC", boxId, boxPokemonId)) + { + return -1; + } + + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: getPokemonEffortValueFromPC: the stat '" + statType + "' does not exist."); + return 0; + } + + return Bot.Game.CurrentPCBox[boxPokemonId - 1].EV.GetStat(_stats[statType.ToUpperInvariant()]); + } + + // API: Returns the individual value for the specified stat of the specified pokémon in the PC. + private int GetPokemonIndividualValueFromPC(int boxId, int boxPokemonId, string statType) + { + if (!IsPCAccessValid("getPokemonIndividualValueFromPC", boxId, boxPokemonId)) + { + return -1; + } + + if (!_stats.ContainsKey(statType.ToUpperInvariant())) + { + Fatal("error: getPokemonIndividualValueFromPC: the stat '" + statType + "' does not exist."); + return 0; + } + + return Bot.Game.CurrentPCBox[boxPokemonId - 1].IV.GetStat(_stats[statType.ToUpperInvariant()]); + } + + // API: Happiness of the pokemon of the current box matching the ID. + private int GetPokemonHappinessFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonHappinessFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Happiness; + } + + // API: Region of capture of the pokemon of the current box matching the ID. + private string GetPokemonRegionFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonRegionFromPC", boxId, boxPokemonId)) + { + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Region.ToString(); + } + + // API: Original trainer of the pokemon of the current box matching the ID. + private string GetPokemonOriginalTrainerFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonOriginalTrainerFromPC", boxId, boxPokemonId)) + { + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].OriginalTrainer; + } + + // API: Gender of the pokemon of the current box matching the ID. + private string GetPokemonGenderFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonHappinessFromPC", boxId, boxPokemonId)) + { + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Gender; + } + + // API: Form of the pokémon in the current box matching the ID. (0 if no form) + private int GetPokemonFormFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonFormFromPC", boxId, boxPokemonId)) + { + return -1; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Form; + } + + // API: Status of the pokemon of the current box matching the ID. + private string GetPokemonStatusFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonStatusFromPC", boxId, boxPokemonId)) + { + return null; + } + return Bot.Game.CurrentPCBox[boxPokemonId - 1].Status; + } + + // API: Type of the pokemon of the current box matching the ID as an array of length 2. + private string[] GetPokemonTypeFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonTypeFromPC", boxId, boxPokemonId)) + { + return null; + } + + int id = Bot.Game.CurrentPCBox[boxPokemonId - 1].Id; + + if (id <= 0 || id >= TypesManager.Instance.Type1.Count()) + { + return new string[] { "Unknown", "Unknown" }; + } + + return new string[] { TypesManager.Instance.Type1[id].ToString(), TypesManager.Instance.Type2[id].ToString() }; + } + + // API: Returns the item held by the specified pokemon in the PC, null if empty. + private string GetPokemonHeldItemFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("getPokemonHeldItemFromPC", boxId, boxPokemonId)) + { + return null; + } + string itemHeld = Bot.Game.CurrentPCBox[boxPokemonId - 1].ItemHeld; + return itemHeld == string.Empty ? null : itemHeld; + } + + // API: Releases the specified pokemon in the team. + private bool ReleasePokemonFromTeam(int pokemonUid) + { + if (pokemonUid < 1 || pokemonUid > 6 || pokemonUid > Bot.Game.Team.Count) + { + Fatal("error: releasePokemonFromTeam: pokemonUid is out of range: " + pokemonUid + + " (team size: " + Bot.Game.Team.Count.ToString() + ")."); + return false; + } + if (!Bot.Game.IsPCOpen) + { + Fatal("error: releasePokemonFromTeam: cannot release a pokemon while the PC is closed: #" + pokemonUid + " (" + Bot.Game.Team[pokemonUid].Name + ")."); + return false; + } + if (Bot.Game.IsPCBoxRefreshing) + { + Fatal("error: releasePokemonFromTeam: cannot release a pokemon while the PC box is refreshing: #" + pokemonUid + " (" + Bot.Game.Team[pokemonUid].Name + ")."); + return false; + } + return ExecuteAction(Bot.Game.ReleasePokemonFromTeam(pokemonUid)); + } + + // API: Releases the specified pokemon in the PC. + private bool ReleasePokemonFromPC(int boxId, int boxPokemonId) + { + if (!IsPCAccessValid("releasePokemonFromPC", boxId, boxPokemonId)) + { + return false; + } + return ExecuteAction(Bot.Game.ReleasePokemonFromPC(boxId, boxPokemonId)); + } + + // API: Buys the specified item from the opened shop. + private bool BuyItem(string itemName, int quantity) + { + if (!ValidateAction("buyItem", false)) return false; + + if (Bot.Game.OpenedShop == null) + { + Fatal("error: buyItem can only be used when a shop is open."); + return false; + } + + ShopItem item = Bot.Game.OpenedShop.Items.FirstOrDefault(i => i.Name.Equals(itemName, StringComparison.InvariantCultureIgnoreCase)); + + if (item == null) + { + Fatal("error: buyItem: the item '" + itemName + "' does not exist in the opened shop."); + return false; + } + + return ExecuteAction(Bot.Game.BuyItem(item.Id, quantity)); + } + + // API: Relearn a move from the move relearner NPC. + private bool RelearnMove(string moveName) + { + if (!ValidateAction("relearnMove", false)) return false; + if (GetMoney() < 2000) return false; + + if (Bot.Game.MoveRelearner is null) + { + Fatal("error: relearnMove can only be used when you have talked with the move relearner npc."); + return false; + } + + MovesManager.MoveData move = Bot.Game.MoveRelearner.Moves.FirstOrDefault(i => i.Name.Equals(moveName, StringComparison.InvariantCultureIgnoreCase)); + + if (move == null) + { + Fatal($"error: relearnMove: the move '{ moveName }' cannot be learn by the current Pokemon or already learnt."); + return false; + } + + int moveId = MovesManager.Instance.GetMoveId(moveName); + if (moveId == -1) + { + Fatal($"error: relearnMove: the move '{ moveName }' cannot be learn by the current Pokemon or already learnt."); + return false; + } + return ExecuteAction(Bot.Game.PurchaseMove(moveId)); + } + + // API: Give the specified item on the specified pokemon. + private bool GiveItemToPokemon(string itemName, int pokemonIndex) + { + if (!ValidateAction("giveItemToPokemon", false)) return false; + + if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) + { + Fatal("error: giveItemToPokemon: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); + return false; + } + + InventoryItem item = Bot.Game.GetItemFromName(itemName); + if (item == null || item.Quantity == 0) + { + Fatal("error: giveItemToPokemon: tried to give the non-existing item '" + itemName + "'."); + return false; + } + + return ExecuteAction(Bot.Game.GiveItemToPokemon(pokemonIndex, item.Id)); + } + + // API: Take the held item from the specified pokemon. + private bool TakeItemFromPokemon(int index) + { + if (!ValidateAction("takeItemFromPokemon", false)) return false; + + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: takeItemFromPokemon: tried to retrieve the non-existing pokemon " + index + "."); + return false; + } + + if (Bot.Game.Team[index - 1].ItemHeld == string.Empty) + { + Fatal("error: takeItemFromPokemon: tried to take the non-existing held item from pokémon '" + index + "'."); + return false; + } + + return ExecuteAction(Bot.Game.TakeItemFromPokemon(index)); + } + + // API: Adds the specified answer to the answer queue. It will be used in the next dialog. + private void PushDialogAnswer(DynValue answerValue) + { + if (answerValue.Type == DataType.String) + { + Bot.Game.PushDialogAnswer(answerValue.CastToString()); + } + else if (answerValue.Type == DataType.Number) + { + Bot.Game.PushDialogAnswer((int)answerValue.CastToNumber()); + } + else + { + Fatal("error: pushDialogAnswer: the argument must be a number (index) or a string (search text)."); + } + } + + // API: Uses the specified item. + private bool UseItem(string itemName) + { + InventoryItem item = Bot.Game.GetItemFromName(itemName.ToUpperInvariant()); + if (item != null && item.Quantity > 0) + { + if (Bot.Game.IsInBattle && item.CanBeUsedInBattle) + { + if (!ValidateAction("useItem", true)) return false; + return ExecuteAction(Bot.AI.UseItem(item.Id)); + } + else if (!Bot.Game.IsInBattle && item.CanBeUsedOutsideOfBattle) + { + if (!ValidateAction("useItem", false)) return false; + Bot.Game.UseItem(item.Id); + return ExecuteAction(true); + } + } + return false; + } + + // API: Uses the specified item on the specified pokémon. + private bool UseItemOnPokemon(string itemName, int pokemonIndex) + { + itemName = itemName.ToUpperInvariant(); + InventoryItem item = Bot.Game.GetItemFromName(itemName.ToUpperInvariant()); + + if (item != null && item.Quantity > 0) + { + if (item.Scope == 17) { - Fatal("error: " + source + " you cannot execute a path action while in a battle."); - } - return false; - } - return true; - } - - private bool ExecuteAction(bool result) - { - if (result) - { - _actionExecuted = true; - } - return result; - } - - // API: Displays the specified message to the message log. - private void Log(string message) - { - LogMessage(message); - } - - // API: Displays the specified message to the message log and stop the bot. - private void Fatal(string message) - { - LogMessage(message); - Bot.Stop(); - } - - // API: Displays the specified message to the message log and logs out. - private void Logout(string message) - { - LogMessage(message); - Bot.Stop(); - Bot.Logout(false); - } - - // API: Logs out and logs back in after the specified number of seconds. - private void Relog(double delay, string message) - { - LogMessage(message); - Bot.Relog(delay); - } - - // API return an array of all NPCs that can be challenged on the current map. format : {"npcName" = {"x" = x, "y" = y}} - private Dictionary> GetActiveBattlers() - { - var activeBattlers = new Dictionary>(); - foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.CanBattle)) - { - var npcData = new Dictionary(); - npcData["x"] = npc.PositionX; - npcData["y"] = npc.PositionY; - activeBattlers[npc.Name] = npcData; - } - return activeBattlers; - } - - // API return an array of all usable Dig Spots on the currrent map. format : {index = {"x" = x, "y" = y}} - private List> GetActiveDigSpots() - { - var digSpots = new List>(); - foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.Type == 70 || npc.Type == 71)) - { - var npcData = new Dictionary(); - npcData["x"] = npc.PositionX; - npcData["y"] = npc.PositionY; - digSpots.Add(npcData); - } - return digSpots; - } - - // API return an array of all usable Headbutt trees on the currrent map. format : {index = {"x" = x, "y" = y}} - private List> GetActiveHeadbuttTrees() - { - var trees = new List>(); - foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.Type == 101)) - { - var npcData = new Dictionary(); - npcData["x"] = npc.PositionX; - npcData["y"] = npc.PositionY; - trees.Add(npcData); - } - return trees; - } - - // API return an array of all harvestable berry trees on the currrent map. format : {index = {"x" = x, "y" = y}} - private List> GetActiveBerryTrees() - { - int[] berries = { 42, 43, 44, 45, 49, 50, 51, 52, 55, 56, 57, 58, 59, 60, 61, 62 }; - var trees = new List>(); - foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => Array.Exists(berries, e => e == npc.Type))) - { - var npcData = new Dictionary(); - npcData["x"] = npc.PositionX; - npcData["y"] = npc.PositionY; - trees.Add(npcData); - } - return trees; - } - - // API return an array of all discoverable items on the currrent map. format : {index = {"x" = x, "y" = y}} - private List> GetDiscoverableItems() - { - var items = new List>(); - foreach (Npc npc in Bot.Game.Map.Npcs.Where(npc => npc.Type == 11 && npc.LosLength < 100)) - { - var npcData = new Dictionary(); - npcData["x"] = npc.PositionX; - npcData["y"] = npc.PositionY; - items.Add(npcData); - } - return items; - } - - // API return npc data on current map, format : { { "x" = x , "y" = y, "type" = type }, {...}, ... } - private List> GetNpcData() - { - var lNpc = new List>(); - foreach (Npc npc in Bot.Game.Map.Npcs) - { - var npcData = new Dictionary(); - npcData["x"] = npc.PositionX.ToString(); - npcData["y"] = npc.PositionY.ToString(); - npcData["type"] = npc.Type.ToString(); - npcData["name"] = npc.Name; - npcData["isBattler"] = npc.IsBattler.ToString(); - npcData["id"] = npc.Id.ToString(); - npcData["los"] = npc.LosLength.ToString(); - lNpc.Add(npcData); - } - return lNpc; - } - - // API: Returns true if the string contains the specified part, ignoring the case. - private bool StringContains(string haystack, string needle) - { - return haystack.ToUpperInvariant().Contains(needle.ToUpperInvariant()); - } - - // API: Returns playing a custom sound. - private void PlaySound(string file) - { - if (File.Exists(file)) - { - using (SoundPlayer player = new SoundPlayer(file)) - { - player.Play(); - } - }; - } - - // API: Calls the specified function when the specified event occurs. - private void RegisterHook(string eventName, DynValue callback) - { - if (callback.Type != DataType.Function) - { - Fatal("error: registerHook: the callback must be a function."); - return; - } - if (!_hookedFunctions.ContainsKey(eventName)) - { - _hookedFunctions.Add(eventName, new List()); - } - _hookedFunctions[eventName].Add(callback); - } - - // API: Returns the X-coordinate of the current cell. - private int GetPlayerX() - { - return Bot.Game.PlayerX; - } - - // API: Returns the Y-coordinate of the current cell. - private int GetPlayerY() - { - return Bot.Game.PlayerY; - } - - // API: Returns current account name. - private string GetAccountName() - { - return Bot.Account.Name; - } - - // API: Returns the name of the current map. - private string GetMapName() - { - return Bot.Game.MapName; - } - - // API: Returns Owned Entry of the pokedex - private int GetPokedexOwned() - { - return Bot.Game.PokedexOwned; - } - - // API: Returns Seen Entry of the pokedex - private int GetPokedexSeen() - { - return Bot.Game.PokedexSeen; - } - - // API: Returns Evolved Entry of the pokedex - private int GetPokedexEvolved() - { - return Bot.Game.PokedexEvolved; - } - - // API: Returns the amount of pokémon in the team. - private int GetTeamSize() - { - return Bot.Game.Team.Count; - } - - // API: Returns current account's membership status. - private bool IsAccountMember() - { - return Bot.Game.IsMember; - } - - // API: Returns the ID of the specified pokémon in the team. - private int GetPokemonId(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonId: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - return Bot.Game.Team[index - 1].Id; - } - - // API: Returns the name of the specified pokémon in the team. - private string GetPokemonName(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonName: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - return Bot.Game.Team[index - 1].Name; - } - - // API: PROShine unique ID of the pokemon of the current box matching the ID. - private int GetPokemonUniqueId(int pokemonUid) - { - if (pokemonUid < 1 || pokemonUid > Bot.Game.Team.Count) - { - Fatal("error: getPokemonUniqueId: tried to retrieve the non-existing pokemon " + pokemonUid + "."); - return -1; - } - PokemonStats iv = Bot.Game.Team[pokemonUid - 1].IV; - - // Converting a base 31 to 10 - // The odds of having twice the same pokemon unique ID being - // 1 against 887,503,680 - int uniqueId = (iv.Attack - 1); - uniqueId += (iv.Defence - 1) * (int)Math.Pow(31, 1); - uniqueId += (iv.Speed - 1) * (int)Math.Pow(31, 2); - uniqueId += (iv.SpAttack - 1) * (int)Math.Pow(31, 3); - uniqueId += (iv.SpDefence - 1) * (int)Math.Pow(31, 4); - uniqueId += (iv.Health - 1) * (int)Math.Pow(31, 5); - return uniqueId; - } - - // API: Returns the current health of the specified pokémon in the team. - private int GetPokemonHealth(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonHealth: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - return Bot.Game.Team[index - 1].CurrentHealth; - } - - // API: Returns the percentage of remaining health of the specified pokémon in the team. - private int GetPokemonHealthPercent(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonHealthPercent: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.CurrentHealth * 100 / pokemon.MaxHealth; - } - - // API: Returns the maximum health of the specified pokémon in the team. - private int GetPokemonMaxHealth(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMaxHealth: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.MaxHealth; - } - - // API: Returns the shyniness of the specified pokémon in the team. - private bool IsPokemonShiny(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: isPokemonShiny: tried to retrieve the non-existing pokemon " + index + "."); - return false; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.IsShiny; - } - - // API: Returns the move of the specified pokémon in the team at the specified index. - private string GetPokemonMoveName(int index, int moveId) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMove: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMove: tried to access an impossible move #" + moveId + "."); - return null; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Moves[moveId - 1].Name; - } - - // API: Returns the move accuracy of the specified pokémon in the team at the specified index. - private int GetPokemonMoveAccuracy(int index, int moveId) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMoveAccuracy: tried to retrieve the non-existing pokemon " + index + "."); - return -1; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveAccuracy: tried to access an impossible move #" + moveId + "."); - return -1; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Moves[moveId - 1].Data.Accuracy; - } - - // API: Returns the move power of the specified pokémon in the team at the specified index. - private int GetPokemonMovePower(int index, int moveId) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMovePower: tried to retrieve the non-existing pokemon " + index + "."); - return -1; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMovePower: tried to access an impossible move #" + moveId + "."); - return -1; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Moves[moveId - 1].Data.Power; - } - - // API: Returns the move type of the specified pokémon in the team at the specified index. - private string GetPokemonMoveType(int index, int moveId) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMoveType: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveType: tried to access an impossible move #" + moveId + "."); - return null; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Moves[moveId - 1].Data.Type.ToString(); - } - - // API: Returns the move damage type of the specified pokémon in the team at the specified index. - private string GetPokemonMoveDamageType(int index, int moveId) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMoveDamageType: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveDamageType: tried to access an impossible move #" + moveId + "."); - return null; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Moves[moveId - 1].Data.DamageType.ToString(); - } - - // API: Returns true if the move of the specified pokémon in the team at the specified index can apply a status . - private bool GetPokemonMoveStatus(int index, int moveId) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMoveStatus: tried to retrieve the non-existing pokemon " + index + "."); - return false; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveStatus: tried to access an impossible move #" + moveId + "."); - return false; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Moves[moveId - 1].Data.Status; - } - - // API: Max move PP of the pokemon of the current box matching the ID. - private int GetPokemonMaxPowerPoints(int index, int moveId) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonMove: tried to retrieve the non-existing pokemon " + index + "."); - return -1; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMove: tried to access an impossible move #" + moveId + "."); - return -1; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Moves[moveId - 1].MaxPoints; - } - - // API: Nature of the pokemon of the current box matching the ID. - private string GetPokemonNature(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonNature: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Nature.Name; - } - - // API: Ability of the pokemon of the current box matching the ID. - private string GetPokemonAbility(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonAbility: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Ability.Name; - } - - // API: Returns the experience total of a pokemon level. - private int GetPokemonTotalExperience(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonTotalXP: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Experience.TotalLevelExperience; - } - - // API: Returns the remaining experience of a pokemon before next level. - private int GetPokemonRemainingExperience(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonRemainingXP: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - Pokemon pokemon = Bot.Game.Team[index - 1]; - return pokemon.Experience.RemainingExperience; - } - - // API: Returns the level of the specified pokémon in the team. - private int GetPokemonLevel(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonLevel: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - return Bot.Game.Team[index - 1].Level; - } - - // API: Returns the happiness of the specified pokémon in the team. - private int GetPokemonHappiness(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonHappiness: tried to retrieve the non-existing pokemon " + index + "."); - return -1; - } - return Bot.Game.Team[index - 1].Happiness; - } - - // API: Returns the region of capture of the specified pokémon in the team. - private string GetPokemonRegion(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonRegion: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - return Bot.Game.Team[index - 1].Region.ToString(); - } - - // API: Returns the original trainer of the specified pokémon in the team. - private string GetPokemonOriginalTrainer(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonOriginalTrainer: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - return Bot.Game.Team[index - 1].OriginalTrainer; - } - - // API: Returns the gender of the specified pokémon in the team. - private string GetPokemonGender(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonGender: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - return Bot.Game.Team[index - 1].Gender; - } - - // API: Returns the type of the specified pokémon in the team as an array of length 2. - private string[] GetPokemonType(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonType: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - - int id = Bot.Game.Team[index - 1].Id; - - if (id <= 0 || id >= TypesManager.Instance.Type1.Count()) - { - return new string[] { "Unknown", "Unknown" }; - } - - return new string[] { TypesManager.Instance.Type1[id].ToString(), TypesManager.Instance.Type2[id].ToString() }; - } - - private string[] _types = { "", "NORMAL", "FIGHTING", "FLYING", "POISON", "GROUND", "ROCK", "BUG", "GHOST", "STEEL", "FIRE", "WATER", "GRASS", "ELECTRIC", "PSYCHIC", "ICE", "DRAGON", "DARK", "FAIRY" }; - - // API: Returns the multiplier of the damage type between an attacking type and one or two defending types. - private double GetDamageMultiplier(string attacker, params DynValue[] defender) - { - if (defender[0].Type == DataType.Table) - { - if (defender[0].Table.Length == 1) - { - defender = new DynValue[] { defender[0].Table.Values.ToArray()[0], DynValue.NewString("") }; - } - else - { - defender = defender[0].Table.Values.ToArray(); - } - } - else if (defender.Length == 1) - { - defender = new DynValue[] { defender[0], DynValue.NewString("") }; - } - - if (attacker.ToUpperInvariant() == "NONE") - attacker = ""; - - if (defender[0].CastToString().ToUpperInvariant() == "NONE") - defender[0] = DynValue.NewString(""); - - if (defender[1].CastToString().ToUpperInvariant() == "NONE") - defender[1] = DynValue.NewString(""); - - if (!Array.Exists(_types, e => e == attacker.ToUpperInvariant())) - { - Fatal("error: getDamageMultiplier: the damage type '" + attacker + "' does not exist."); - return -1; - } - - if (!Array.Exists(_types, e => e == defender[0].CastToString().ToUpperInvariant())) - { - Fatal("error: getDamageMultiplier: the damage type '" + defender[0].CastToString() + "' does not exist."); - return -1; - } - - if (!Array.Exists(_types, e => e == defender[1].CastToString().ToUpperInvariant())) - { - Fatal("error: getDamageMultiplier: the damage type '" + defender[1].CastToString() + "' does not exist."); - return -1; - } - - double power = 1; - - power *= TypesManager.Instance.GetMultiplier(PokemonTypeExtensions.FromName(attacker), PokemonTypeExtensions.FromName(defender[0].CastToString())); - power *= TypesManager.Instance.GetMultiplier(PokemonTypeExtensions.FromName(attacker), PokemonTypeExtensions.FromName(defender[1].CastToString())); - - return power; - } - - // API: Returns the status of the specified pokémon in the team. - private string GetPokemonStatus(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonStatus: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - return Bot.Game.Team[index - 1].Status; - } - - // API: Returns the form of the specified pokémon in the team (0 if no form). - private int GetPokemonForm(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonForm: tried to retrieve the non-existing pokemon " + index + "."); - return 0; - } - return Bot.Game.Team[index - 1].Form; - } - - // API: Returns the item held by the specified pokemon in the team, null if empty. - private string GetPokemonHeldItem(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: getPokemonHeldItem: tried to retrieve the non-existing pokemon " + index + "."); - return null; - } - string itemHeld = Bot.Game.Team[index - 1].ItemHeld; - return itemHeld == string.Empty ? null : itemHeld; - } - - // API: Returns true if the specified pokémon has is alive and has an offensive attack available. - private bool IsPokemonUsable(int index) - { - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: isPokemonUsable: tried to retrieve the non-existing pokemon " + index + "."); - return false; - } - return Bot.AI.IsPokemonUsable(Bot.Game.Team[index - 1]); - } - - // API: Returns the amount of usable pokémon in the team. - private int GetUsablePokemonCount() - { - return Bot.AI.UsablePokemonsCount; - } - - // API: Returns true if the specified pokémon has a move with the specified name. - private bool HasMove(int pokemonIndex, string moveName) - { - if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) - { - Fatal("error: hasMove: tried to retrieve the non-existing pokemon " + pokemonIndex + "."); - return false; - } - - return Bot.Game.PokemonUidHasMove(pokemonIndex, moveName.ToUpperInvariant()); - } - - // API: Returns the remaining power points of the specified move of the specified pokémon in the team. - private int GetRemainingPowerPoints(int pokemonIndex, string moveName) - { - if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) - { - Fatal("error: getRemainingPowerPoints: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); - return 0; - } - - moveName = moveName.ToUpperInvariant(); - PokemonMove move = Bot.Game.Team[pokemonIndex - 1].Moves.FirstOrDefault(m => MovesManager.Instance.GetMoveData(m.Id)?.Name.ToUpperInvariant() == moveName); - if (move == null) - { - Fatal("error: getRemainingPowerPoints: the pokémon " + pokemonIndex + " does not have a move called '" + moveName + "'."); - return 0; - } - - return move.CurrentPoints; - } - - // API: Returns the value for the specified stat of the specified pokémon in the team. - private int GetPokemonStat(int pokemonIndex, string statType) - { - if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) - { - Fatal("error: getPokemonStat: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); - return 0; - } - - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: getPokemonStat: the stat '" + statType + "' does not exist."); - return 0; - } - - return Bot.Game.Team[pokemonIndex - 1].Stats.GetStat(_stats[statType.ToUpperInvariant()]); - } - - // API: Returns the effort value for the specified stat of the specified pokémon in the team. - private int GetPokemonEffortValue(int pokemonIndex, string statType) - { - if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) - { - Fatal("error: getPokemonEffortValue: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); - return 0; - } - - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: getPokemonEffortValue: the stat '" + statType + "' does not exist."); - return 0; - } - - return Bot.Game.Team[pokemonIndex - 1].EV.GetStat(_stats[statType.ToUpperInvariant()]); - } - - // API: Returns the individual value for the specified stat of the specified pokémon in the team. - private int GetPokemonIndividualValue(int pokemonIndex, string statType) - { - if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) - { - Fatal("error: getPokemonIndividualValue: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); - return 0; - } - - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: getPokemonIndividualValue: the stat '" + statType + "' does not exist."); - return 0; - } - - return Bot.Game.Team[pokemonIndex - 1].IV.GetStat(_stats[statType.ToUpperInvariant()]); - } - - // API: Returns true if the specified item is in the inventory. - private bool HasItem(string itemName) - { - return Bot.Game.HasItemName(itemName.ToUpperInvariant()); - } - - // API: Returns the quantity of the specified item in the inventory. - private int GetItemQuantity(string itemName) - { - return Bot.Game.GetItemFromName(itemName.ToUpperInvariant())?.Quantity ?? 0; - } - - // API: Returns true if the specified item is in the inventory. - private bool HasItemId(int itemid) - { - return Bot.Game.HasItemId(itemid); - } - // API: Returns the quantity of the specified item in the inventory. - private int GetItemQuantityID(int itemid) - { - return Bot.Game.GetItemFromId(itemid)?.Quantity ?? 0; - } - - // API: Returns true if the specified pokémon is present in the team. - private bool HasPokemonInTeam(string pokemonName) - { - return Bot.Game.HasPokemonInTeam(pokemonName.ToUpperInvariant()); - } - - // API: Returns true if the team is sorted by level in ascending order. - private bool IsTeamSortedByLevelAscending() - { - return IsTeamSortedByLevel(true, 1, 6); - } - - // API: Returns true if the team is sorted by level in descending order. - private bool IsTeamSortedByLevelDescending() - { - return IsTeamSortedByLevel(false, 1, 6); - } - - // API: Returns true if the specified part of the team is sorted by level in ascending order. - private bool IsTeamRangeSortedByLevelAscending(int fromIndex, int toIndex) - { - return IsTeamSortedByLevel(true, fromIndex, toIndex); - } - - // API: Returns true if the specified part of the team the team is sorted by level in descending order. - private bool IsTeamRangeSortedByLevelDescending(int fromIndex, int toIndex) - { - return IsTeamSortedByLevel(false, fromIndex, toIndex); - } - - private bool IsTeamSortedByLevel(bool ascending, int from, int to) - { - from = Math.Max(from, 1); - to = Math.Min(to, Bot.Game.Team.Count); - - int level = ascending ? 0 : int.MaxValue; - for (int i = from - 1; i < to; ++i) - { - Pokemon pokemon = Bot.Game.Team[i]; - if (ascending && pokemon.Level < level) return false; - if (!ascending && pokemon.Level > level) return false; - level = pokemon.Level; - } - return true; - } - - // API: Returns true if there is a visible NPC with the specified name on the map. - private bool IsNpcVisible(string npcName) - { - npcName = npcName.ToUpperInvariant(); - return Bot.Game.Map.Npcs.Any(npc => npc.Name.ToUpperInvariant() == npcName); - } - - // API: Returns true if there is a visible NPC the specified coordinates. - private bool IsNpcOnCell(int cellX, int cellY) - { - return Bot.Game.Map.Npcs.Any(npc => npc.PositionX == cellX && npc.PositionY == cellY); - } - - // API: Returns true if there is a shop opened. - private bool IsShopOpen() - { - return Bot.Game.OpenedShop != null; - } - - // API: Returns true if the player is relearning the move of a Pokemon from an NPC. - private bool IsRelearningMoves() - { - return Bot.Game.MoveRelearner != null; - } - - // API: Returns the amount of money in the inventory. - private int GetMoney() - { - return Bot.Game.Money; - } - - // API: Returns true if the player is riding a mount or the bicycle. - private bool IsMounted() - { - return Bot.Game.IsBiking; - } - - // API: Returns true if the player is surfing - private bool IsSurfing() - { - return Bot.Game.IsSurfing; - } - - // API: Returns true if the opponent pokémon is shiny. - private bool IsOpponentShiny() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: isOpponentShiny is only usable in battle."); - return false; - } - return Bot.Game.ActiveBattle.IsShiny; - } - - // API: Returns true if the opponent pokémon has already been caught and has a pokédex entry. - private bool IsAlreadyCaught() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: isAlreadyCaught is only usable in battle."); - return false; - } - return Bot.Game.ActiveBattle.AlreadyCaught; - } - - // API: Returns true if the current battle is against a wild pokémon. - private bool IsWildBattle() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: isWildBattle is only usable in battle."); - return false; - } - return Bot.Game.ActiveBattle.IsWild; - } - - // API: Returns the index of the active team pokémon in the current battle. - private int GetActivePokemonNumber() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getActivePokemonNumber is only usable in battle."); - return 0; - } - return Bot.Game.ActiveBattle.SelectedPokemonIndex + 1; - } - - // API: Returns the id of the opponent pokémon in the current battle. - private int GetOpponentId() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentId can only be used in battle."); - return 0; - } - return Bot.Game.ActiveBattle.OpponentId; - } - - // API: Returns the name of the opponent pokémon in the current battle. - private string GetOpponentName() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentName can only be used in battle."); - return null; - } - return PokemonNamesManager.Instance.Names[Bot.Game.ActiveBattle.OpponentId]; - } - - // API: Returns the current health of the opponent pokémon in the current battle. - private int GetOpponentHealth() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentHealth can only be used in battle."); - return 0; - } - return Bot.Game.ActiveBattle.CurrentHealth; - } - - // API: Returns the maximum health of the opponent pokémon in the current battle. - private int GetOpponentMaxHealth() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentMaxHealth can only be used in battle."); - return 0; - } - return Bot.Game.ActiveBattle.OpponentHealth; - } - - // API: Returns the percentage of remaining health of the opponent pokémon in the current battle. - private int GetOpponentHealthPercent() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentHealthPercent can only be used in battle."); - return 0; - } - return Bot.Game.ActiveBattle.CurrentHealth * 100 / Bot.Game.ActiveBattle.OpponentHealth; - } - - // API: Returns the level of the opponent pokémon in the current battle. - private int GetOpponentLevel() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentLevel can only be used in battle."); - return 0; - } - return Bot.Game.ActiveBattle.OpponentLevel; - } - - // API: Returns the status of the opponent pokémon in the current battle. - private string GetOpponentStatus() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentStatus can only be used in battle."); - return null; - } - return Bot.Game.ActiveBattle.OpponentStatus; - } - - // API: Returns the form of the opponent pokémon in the current battle (0 if no form). - private int GetOpponentForm() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentForm can only be used in battle."); - return 0; - } - return Bot.Game.ActiveBattle.AlternateForm; - } - - private static Dictionary _stats = new Dictionary() - { - { "HP", StatType.Health }, - { "HEALTH", StatType.Health }, - { "ATK", StatType.Attack }, - { "ATTACK", StatType.Attack }, - { "DEF", StatType.Defence }, - { "DEFENCE", StatType.Defence }, - { "DEFENSE", StatType.Defence }, - { "SPATK", StatType.SpAttack }, - { "SPATTACK", StatType.SpAttack }, - { "SPDEF", StatType.SpDefence }, - { "SPDEFENCE", StatType.SpDefence }, - { "SPDEFENSE", StatType.SpDefence }, - { "SPD", StatType.Speed }, - { "SPEED", StatType.Speed } - }; - - // API: Returns true if the opponent is only giving the specified effort value. - private bool IsOpponentEffortValue(string statType) - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: isOpponentEffortValue can only be used in battle."); - return false; - } - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: isOpponentEffortValue: the stat '" + statType + "' does not exist."); - return false; - } - if (!EffortValuesManager.Instance.BattleValues.ContainsKey(Bot.Game.ActiveBattle.OpponentId)) - { - return false; - } - - PokemonStats stats = EffortValuesManager.Instance.BattleValues[Bot.Game.ActiveBattle.OpponentId]; - return stats.HasOnly(_stats[statType.ToUpperInvariant()]); - } - - // API: Returns the amount of a particular EV given by the opponent. - private int GetOpponentEffortValue(string statType) - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentEffortValue can only be used in battle."); - return -1; - } - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: getOpponentEffortValue: the stat '" + statType + "' does not exist."); - return -1; - } - if (!EffortValuesManager.Instance.BattleValues.ContainsKey(Bot.Game.ActiveBattle.OpponentId)) - { - return -1; - } - - PokemonStats stats = EffortValuesManager.Instance.BattleValues[Bot.Game.ActiveBattle.OpponentId]; - return stats.GetStat(_stats[statType.ToUpperInvariant()]); - } - - // API: Returns the type of the opponent pokémon in the current battle as an array of length 2. - private string[] GetOpponentType() - { - if (!Bot.Game.IsInBattle) - { - Fatal("error: getOpponentType can only be used in battle."); - return null; - } - - int id = Bot.Game.ActiveBattle.OpponentId; - - if (id <= 0 || id >= TypesManager.Instance.Type1.Count()) - { - return new string[] { "Unknown", "Unknown" }; - } - - return new string[] { TypesManager.Instance.Type1[id].ToString(), TypesManager.Instance.Type2[id].ToString() }; - } - - // API: Moves to the specified coordinates. - private bool MoveToCell(int x, int y) - { - if (!ValidateAction("moveToCell", false)) return false; - - return ExecuteAction(Bot.MoveToCell(x, y)); - } - - // API: Moves to the nearest cell teleporting to the specified map. - private bool MoveToMap(string mapName) - { - Fatal("error: moveToMap: this function is no longer available, please use moveToCell or moveToRectangle instead."); - return false; - } - - // API: Moves to a random accessible cell of the specified rectangle. - private bool MoveToRectangle(params DynValue[] values) - { - if (values.Length != 1 && values.Length != 4 || - (values.Length == 1 && values[0].Type != DataType.Table) || - (values.Length == 4 - && (values[0].Type != DataType.Number || values[1].Type != DataType.Number - || values[2].Type != DataType.Number || values[3].Type != DataType.Number))) - { - Fatal("error: moveToRectangle: must receive either a table or four numbers."); - return false; - } - if (values.Length == 1) - { - values = values[0].Table.Values.ToArray(); - } - return MoveToRectangle((int)values[0].Number, (int)values[1].Number, (int)values[2].Number, (int)values[3].Number); - } - - // API: Moves to a random accessible cell of the specified rectangle. - private bool MoveToRectangle(int minX, int minY, int maxX, int maxY) - { - if (!ValidateAction("moveToRectangle", false)) return false; - - if (minX > maxX || minY > maxY) - { - Fatal("error: moveToRectangle: the maximum cell cannot be less than the minimum cell."); - return false; - } - - int x; - int y; - int tries = 0; - do - { - if (++tries > 100) return false; - x = Bot.Game.Rand.Next(minX, maxX + 1); - y = Bot.Game.Rand.Next(minY, maxY + 1); - } while (x == Bot.Game.PlayerX && y == Bot.Game.PlayerY); - - return ExecuteAction(Bot.MoveToCell(x, y)); - } - - // API: Move randomly avoiding water and links. - private bool MoveToNormalGround() - { - if (!ValidateAction("moveToNormalGround", false)) return false; - - return ExecuteAction(MoveToCellType((x, y) => Bot.Game.Map.IsNormalGround(x, y))); - } - - // API: Moves to the nearest grass patch then move randomly inside it. - private bool MoveToGrass() - { - if (!ValidateAction("moveToGrass", false)) return false; - - return ExecuteAction(MoveToCellType((x, y) => Bot.Game.Map.IsGrass(x, y))); - } - - // API: Moves to the nearest water area then move randomly inside it. - private bool MoveToWater() - { - if (!ValidateAction("moveToWater", false)) return false; - - return ExecuteAction(MoveToCellType((x, y) => Bot.Game.Map.IsWater(x, y))); - } - - private bool MoveToCellType(Func cellTypePredicate) - { - bool alreadyInCell = cellTypePredicate(Bot.Game.PlayerX, Bot.Game.PlayerY); - - List> cells = new List>(); - - for (int x = 0; x < Bot.Game.Map.Width; ++x) - { - for (int y = 0; y < Bot.Game.Map.Height; ++y) - { - if (cellTypePredicate(x, y) && (x != Bot.Game.PlayerX || y != Bot.Game.PlayerY)) - { - int distance = Bot.Game.DistanceTo(x, y); - cells.Add(new Tuple(x, y, distance)); - } - } - } - - List> trash = new List>(); - if (alreadyInCell) - { - foreach (var cell in cells) - { - if (cell.Item3 >= 10) - { - trash.Add(cell); - } - } - } - else - { - int minDistance = -1; - foreach (var cell in cells) - { - if (minDistance == -1 || cell.Item3 < minDistance) - { - minDistance = cell.Item3; - } - } - foreach (var cell in cells) - { - if (cell.Item3 > minDistance + 5) - { - trash.Add(cell); - } - } - } - while (trash.Count > 0) - { - cells.Remove(trash[0]); - trash.RemoveAt(0); - } - - if (cells.Count > 0) - { - var randomCell = cells[Bot.Game.Rand.Next(cells.Count)]; - return Bot.MoveToCell(randomCell.Item1, randomCell.Item2); - } - return false; - } - - // API: Moves near the cell teleporting to the specified map. - private bool MoveNearExit(string mapName) - { - if (!ValidateAction("moveNearExit", false)) return false; - - Tuple nearest = Bot.Game.Map.GetNearestLinks(mapName.ToUpperInvariant(), Bot.Game.PlayerX, Bot.Game.PlayerY).First(); - if (nearest == null) - { - Fatal("error: moveNearExit: could not find the exit '" + mapName + "'."); - return false; - } - - int x, y; - int tries = 0; - do - { - x = Bot.Game.Rand.Next(-10, 10) + nearest.Item1; - y = Bot.Game.Rand.Next(-10, 10) + nearest.Item2; - if (++tries > 100) return false; - } while (x <= 0 || y <= 0 || !Bot.Game.Map.IsNormalGround(x, y) || (x == Bot.Game.PlayerX && y == Bot.Game.PlayerY)); - - return ExecuteAction(Bot.MoveToCell(x, y)); - } - - // API: Moves then talk to NPC specified by its name. - private bool TalkToNpc(string npcName) - { - if (!ValidateAction("talkToNpc", false)) return false; - - npcName = npcName.ToUpperInvariant(); - Npc target = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.Name.ToUpperInvariant() == npcName); - if (target == null) - { - Fatal("error: talkToNpc: could not find the NPC '" + npcName + "'."); - return false; - } - - return ExecuteAction(Bot.TalkToNpc(target)); - } - - // API: Moves then talk to NPC located on the specified cell. - private bool TalkToNpcOnCell(int cellX, int cellY) - { - if (!ValidateAction("talkToNpcOnCell", false)) return false; - - Npc target = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.PositionX == cellX && npc.PositionY == cellY); - if (target == null) - { - Fatal("error: talkToNpcOnCell: could not find any NPC on the cell [" + cellX + "," + cellY + "]."); - return false; - } - - return ExecuteAction(Bot.TalkToNpc(target)); - } - - // API: Moves to the Nurse Joy then talk to the cell below her. - private bool UsePokecenter() - { - if (!ValidateAction("usePokecenter", false)) return false; - - Npc nurse = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.Name.StartsWith("Nurse")); - if (nurse == null) - { - Fatal("error: usePokecenter: could not find the Nurse Joy."); - return false; - } - Npc target = Bot.Game.Map.Npcs.FirstOrDefault(npc => npc.PositionX == nurse.PositionX && npc.PositionY == nurse.PositionY + 1); - if (target == null) - { - Fatal("error: usePokecenter: could not find the entity below the Nurse Joy."); - return false; - } - - return ExecuteAction(Bot.TalkToNpc(target)); - } - - // API: Swaps the two pokémon specified by their position in the team. - private bool SwapPokemon(int index1, int index2) - { - if (!ValidateAction("swapPokemon", false)) return false; - - return ExecuteAction(Bot.Game.SwapPokemon(index1, index2)); - } - - // API: Swaps the first pokémon with the specified name with the leader of the team. - private bool SwapPokemonWithLeader(string pokemonName) - { - if (!ValidateAction("swapPokemonWithLeader", false)) return false; - - Pokemon pokemon = Bot.Game.FindFirstPokemonInTeam(pokemonName.ToUpperInvariant()); - if (pokemon == null) - { - Fatal("error: swapPokemonWithLeader: there is no pokémon '" + pokemonName + "' in the team."); - return false; - } - if (pokemon.Uid == 1) - { - Fatal("error: swapPokemonWithLeader: '" + pokemonName + "' is already the leader of the team."); - return false; - } - - return ExecuteAction(Bot.Game.SwapPokemon(1, pokemon.Uid)); - } - - // API: Sorts the pokémon in the team by level in ascending order, one pokémon at a time. - private bool SortTeamByLevelAscending() - { - if (!ValidateAction("sortTeamByLevelAscending", false)) return false; - - return ExecuteAction(SortTeamByLevel(true, 1, 6)); - } - - // API: Sorts the pokémon in the team by level in descending order, one pokémon at a time. - private bool SortTeamByLevelDescending() - { - if (!ValidateAction("sortTeamByLevelDescending", false)) return false; - - return ExecuteAction(SortTeamByLevel(false, 1, 6)); - } - - // API: Sorts the specified part of the team by level in ascending order, one pokémon at a time. - private bool SortTeamRangeByLevelAscending(int fromIndex, int toIndex) - { - if (!ValidateAction("sortTeamRangeByLevelAscending", false)) return false; - - return ExecuteAction(SortTeamByLevel(true, fromIndex, toIndex)); - } - - // API: Sorts the specified part of the team by level in descending order, one pokémon at a time. - private bool SortTeamRangeByLevelDescending(int fromIndex, int toIndex) - { - if (!ValidateAction("sortTeamRangeByLevelDescending", false)) return false; - - return ExecuteAction(SortTeamByLevel(false, fromIndex, toIndex)); - } - - private bool SortTeamByLevel(bool ascending, int from, int to) - { - from = Math.Max(from, 1); - to = Math.Min(to, Bot.Game.Team.Count); - - for (int i = from - 1; i < to - 1; ++i) - { - int currentIndex = i; - int currentLevel = Bot.Game.Team[i].Level; - for (int j = i + 1; j < to; ++j) - { - if ((ascending && Bot.Game.Team[j].Level < currentLevel) || - (!ascending && Bot.Game.Team[j].Level > currentLevel)) - { - currentIndex = j; - currentLevel = Bot.Game.Team[j].Level; - } - } - - if (currentIndex != i) - { - Bot.Game.SwapPokemon(i + 1, currentIndex + 1); - return true; - } - } - return false; - } - - // API: Return the state Auto Evolve - private bool IsAutoEvolve() - { - return Bot.PokemonEvolver.IsEnabled; - } - - // API: Enable auto evolve on PrO Shine client. - private bool EnableAutoEvolve() - { - Bot.PokemonEvolver.IsEnabled = true; - return Bot.PokemonEvolver.IsEnabled; - } - - // API: Disable auto evolve on PrO Shine client. - private bool DisableAutoEvolve() - { - Bot.PokemonEvolver.IsEnabled = false; - return !Bot.PokemonEvolver.IsEnabled; - } - - // API: Check if the private message from normal users are blocked. - private bool IsPrivateMessageEnabled() - { - return Bot.Game.IsPrivateMessageOn; - } - - // API: Enable private messages from users. - private bool EnablePrivateMessage() - { - return ExecuteAction(Bot.Game.PrivateMessageOn()); - } - - // API: Disable private messages from users. - private bool DisablePrivateMessage() - { - return ExecuteAction(Bot.Game.PrivateMessageOff()); - } - - private delegate int GetTimeDelegate(out int minute); - - // API: Return the current in game hour and minute. - private int GetTime(out int minute) - { - DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); - minute = dt.Minute; - return dt.Hour; - } - - // API: Return true if morning time. - private bool IsMorning() - { - DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); - if (dt.Hour >= 4 && dt.Hour < 10) - { - return true; - } - return false; - } - - // API: Return true if noon time. - private bool IsNoon() - { - DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); - if (dt.Hour >= 10 && dt.Hour < 20) - { - return true; - } - return false; - } - - // API: Return true if night time. - private bool IsNight() - { - DateTime dt = Convert.ToDateTime(Bot.Game.PokemonTime); - if (dt.Hour >= 20 || dt.Hour < 4) - { - return true; - } - return false; - } - - // API: Return true if the character is outside. - private bool IsOutside() - { - return Bot.Game.Map.IsOutside; - } - - // API: Check if the PC is open. Moving close the PC, usePC() opens it. - private bool IsPCOpen() - { - return Bot.Game.IsPCOpen; - } - - // API: Move to the PC and opens it, refreshing the first box. - private bool UsePC() - { - if (!ValidateAction("usePc", false)) return false; - - return ExecuteAction(Bot.OpenPC()); - } - - // API: Open box from the PC. - private bool OpenPCBox(int boxId) - { - if (!ValidateAction("openPCBox", false)) return false; - - if (!Bot.Game.IsPCOpen) - { - Fatal("error: openPCBox: tried to open box #" + boxId + " while the PC is closed."); - } - return ExecuteAction(Bot.Game.RefreshPCBox(boxId)); - } - - // API: Withdraw a pokemon from a known box. - private bool WithdrawPokemonFromPC(int boxId, int boxPokemonId) - { - if (!ValidateAction("withdrawPokemonFromPC", false)) return false; - - if (!IsPCAccessValid("withdrawPokemonFromPC", boxId, boxPokemonId)) - { - return false; - } - - if (Bot.Game.WithdrawPokemonFromPC(boxId, boxPokemonId)) - { - return ExecuteAction(Bot.Game.RefreshPCBox(boxId)); - } - return false; - } - - // API: Deposit a pokemon to the pc. - private bool DepositPokemonToPC(int pokemonUid) - { - if (!ValidateAction("depositPokemonToPC", false)) return false; - - if (Bot.Game.DepositPokemonToPC(pokemonUid)) - { - return ExecuteAction(Bot.Game.RefreshCurrentPCBox()); - } - return false; - } - - // API: Swap a pokemon from the team with a pokemon from the pc. - private bool SwapPokemonFromPC(int boxId, int boxPokemonId, int pokemonUid) - { - if (!ValidateAction("swapPokemonFromPC", false)) return false; - - if (!IsPCAccessValid("swapPokemonFromPC", boxId, boxPokemonId)) - { - return false; - } - - if (Bot.Game.SwapPokemonFromPC(boxId, boxPokemonId, pokemonUid)) - { - return ExecuteAction(Bot.Game.RefreshCurrentPCBox()); - } - return false; - } - - // API: Get the active PC Box. - private int GetCurrentPCBoxId() - { - if (!Bot.Game.IsPCOpen) - { - return -1; - } - return Bot.Game.CurrentPCBoxId; - } - - // API: Return the number of non-empty boxes in the PC - private int GetPCBoxCount() - { - // The PCGreatestUid is only known after the first box refresh - if (!Bot.Game.IsPCOpen || Bot.Game.PCGreatestUid == -1 || Bot.Game.IsPCBoxRefreshing) - { - return -1; - } - return Bot.Game.GetBoxIdFromPokemonUid(Bot.Game.PCGreatestUid); - } - - // API: Return the number of pokemon in the PC - private int GetPCPokemonCount() - { - // The PCGreatestUid is only known after the first box refresh - if (!Bot.Game.IsPCOpen || Bot.Game.PCGreatestUid == -1 || Bot.Game.IsPCBoxRefreshing) - { - return -1; - } - return Bot.Game.PCGreatestUid - 7; - } - - // API: Is the currentPcBox refreshed yet? - private bool IsCurrentPCBoxRefreshed() - { - if (!Bot.Game.IsPCOpen || Bot.Game.IsPCBoxRefreshing) - { - return false; - } - return true; - } - - // API: Current box size. - private int GetCurrentPCBoxSize() - { - if (!Bot.Game.IsPCOpen || Bot.Game.IsPCBoxRefreshing) - { - return -1; - } - return Bot.Game.CurrentPCBox.Count; - } - - private bool IsPCAccessValid(string functionName, int boxId, int boxPokemonId) - { - if (!Bot.Game.IsPCOpen) - { - Fatal("error: " + functionName + ": tried to access box #" + boxId + " while the PC is closed."); - return false; - } - if (Bot.Game.IsPCBoxRefreshing) - { - Fatal("error: " + functionName + ": tried to access box #" + boxId + " while the box is refreshing."); - return false; - } - if (boxId != Bot.Game.CurrentPCBoxId) - { - Fatal("error: " + functionName + ": tried to access box #" + boxId + " different from the currently loaded box."); - return false; - } - if (boxPokemonId < 1 || boxPokemonId > Bot.Game.CurrentPCBox.Count) - { - Fatal("error: " + functionName + ": tried to access the unknown pokemon #" + boxPokemonId + " of the box #" + boxId + "."); - return false; - } - return true; - } - - // API: Name of the pokemon of the current box matching the ID. - private string GetPokemonNameFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonNameFromPC", boxId, boxPokemonId)) - { - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Name; - } - - // API: Pokedex ID of the pokemon of the current box matching the ID. - private int GetPokemonIdFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonNationalIdFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Id; - } - - // API: PROShine custom unique ID of the pokemon of the current box matching the ID. - private int GetPokemonUniqueIdFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonUniqueIdFromPC", boxId, boxPokemonId)) - { - return -1; - } - PokemonStats iv = Bot.Game.CurrentPCBox[boxPokemonId - 1].IV; - - // Converting a base 31 to 10 - // The odds of having twice the same pokemon unique ID being - // 1 against 887,503,680 - int uniqueId = (iv.Attack - 1); - uniqueId += (iv.Defence - 1) * (int)Math.Pow(31, 1); - uniqueId += (iv.Speed - 1) * (int)Math.Pow(31, 2); - uniqueId += (iv.SpAttack - 1) * (int)Math.Pow(31, 3); - uniqueId += (iv.SpDefence - 1) * (int)Math.Pow(31, 4); - uniqueId += (iv.Health - 1) * (int)Math.Pow(31, 5); - return uniqueId; - } - - // API: Current HP of the pokemon of the current box matching the ID. - private int GetPokemonHealthFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonCurrentHealthFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].CurrentHealth; - } - - // API: Returns the percentage of remaining health of the specified pokémon in the team. - private int GetPokemonHealthPercentFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonCurrentHealthPercentFromPC", boxId, boxPokemonId)) - { - return -1; - } - Pokemon pokemon = Bot.Game.CurrentPCBox[boxPokemonId - 1]; - return pokemon.CurrentHealth * 100 / pokemon.MaxHealth; - } - - // API: Max HP of the pokemon of the current box matching the ID. - private int GetPokemonMaxHealthFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonMaxHealthFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].MaxHealth; - } - - // API: Level of the pokemon of the current box matching the ID. - private int GetPokemonLevelFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonMaxHealthFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Level; - } - - // API: Total of experience cost of a level for the pokemon of the current box matching the ID. - private int GetPokemonTotalExperienceFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonTotalXPFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Experience.TotalLevelExperience; - } - - // API: Remaining experience before the next level of the pokemon of the current box matching the ID. - private int GetPokemonRemainingExperienceFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonRemainingXPFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Experience.RemainingExperience; - } - - // API: Shyniness of the pokemon of the current box matching the ID. - private bool IsPokemonFromPCShiny(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("isPokemonFromPCShiny", boxId, boxPokemonId)) - { - return false; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].IsShiny; - } - - // API: Move of the pokemon of the current box matching the ID. - private string GetPokemonMoveNameFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMoveNameFromPC", boxId, boxPokemonId)) - { - return null; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveNameFromPC: tried to access an impossible move #" + moveId + "."); - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Name; - } - - // API: Returns the move accuracy of the specified pokémon in the box at the specified index. - private int GetPokemonMoveAccuracyFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMoveAccuracyFromPC", boxId, boxPokemonId)) - { - return -1; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveAccuracyFromPC: tried to access an impossible move #" + moveId + "."); - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Accuracy; - } - - // API: Returns the move power of the specified pokémon in the box at the specified index. - private int GetPokemonMovePowerFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMovePowerFromPC", boxId, boxPokemonId)) - { - return -1; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMovePowerFromPC: tried to access an impossible move #" + moveId + "."); - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Power; - } - - // API: Returns the move type of the specified pokémon in the box at the specified index. - private string GetPokemonMoveTypeFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMoveTypeFromPC", boxId, boxPokemonId)) - { - return null; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveTypeFromPC: tried to access an impossible move #" + moveId + "."); - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Type.ToString(); - } - - // API: Returns the move damage type of the specified pokémon in the box at the specified index. - private string GetPokemonMoveDamageTypeFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMoveDamageTypeFromPC", boxId, boxPokemonId)) - { - return null; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveDamageTypeFromPC: tried to access an impossible move #" + moveId + "."); - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.DamageType.ToString(); - } - - // API: Returns true if the move of the specified pokémon in the box at the specified index can apply a status . - private bool GetPokemonMoveStatusFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMoveStatusTypeFromPC", boxId, boxPokemonId)) - { - return false; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveStatusTypeFromPC: tried to access an impossible move #" + moveId + "."); - return false; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].Data.Status; - } - - // API: Current move PP of the pokemon of the current box matching the ID. - private int GetPokemonRemainingPowerPointsFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMoveCurrentPPFromPC", boxId, boxPokemonId)) - { - return -1; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveCurrentPPFromPC: tried to access an impossible move #" + moveId + "."); - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].CurrentPoints; - } - - // API: Max move PP of the pokemon of the current box matching the ID. - private int GetPokemonMaxPowerPointsFromPC(int boxId, int boxPokemonId, int moveId) - { - if (!IsPCAccessValid("getPokemonMoveMaxPPFromPC", boxId, boxPokemonId)) - { - return -1; - } - if (moveId < 1 || moveId > 4) - { - Fatal("error: getPokemonMoveMaxPPFromPC: tried to access an impossible move #" + moveId + "."); - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Moves[moveId - 1].MaxPoints; - } - - // API: Nature of the pokemon of the current box matching the ID. - private string GetPokemonNatureFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonNatureFromPC", boxId, boxPokemonId)) - { - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Nature.Name; - } - - // API: Ability of the pokemon of the current box matching the ID. - private string GetPokemonAbilityFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonAbilityFromPC", boxId, boxPokemonId)) - { - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Ability.Name; - } - - // API: Returns the value for the specified stat of the specified pokémon in the PC. - private int GetPokemonStatFromPC(int boxId, int boxPokemonId, string statType) - { - if (!IsPCAccessValid("getPokemonStatFromPC", boxId, boxPokemonId)) - { - return -1; - } - - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: getPokemonStatFromPC: the stat '" + statType + "' does not exist."); - return 0; - } - - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Stats.GetStat(_stats[statType.ToUpperInvariant()]); - } - - // API: Returns the effort value for the specified stat of the specified pokémon in the PC. - private int GetPokemonEffortValueFromPC(int boxId, int boxPokemonId, string statType) - { - if (!IsPCAccessValid("getPokemonEffortValueFromPC", boxId, boxPokemonId)) - { - return -1; - } - - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: getPokemonEffortValueFromPC: the stat '" + statType + "' does not exist."); - return 0; - } - - return Bot.Game.CurrentPCBox[boxPokemonId - 1].EV.GetStat(_stats[statType.ToUpperInvariant()]); - } - - // API: Returns the individual value for the specified stat of the specified pokémon in the PC. - private int GetPokemonIndividualValueFromPC(int boxId, int boxPokemonId, string statType) - { - if (!IsPCAccessValid("getPokemonIndividualValueFromPC", boxId, boxPokemonId)) - { - return -1; - } - - if (!_stats.ContainsKey(statType.ToUpperInvariant())) - { - Fatal("error: getPokemonIndividualValueFromPC: the stat '" + statType + "' does not exist."); - return 0; - } - - return Bot.Game.CurrentPCBox[boxPokemonId - 1].IV.GetStat(_stats[statType.ToUpperInvariant()]); - } - - // API: Happiness of the pokemon of the current box matching the ID. - private int GetPokemonHappinessFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonHappinessFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Happiness; - } - - // API: Region of capture of the pokemon of the current box matching the ID. - private string GetPokemonRegionFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonRegionFromPC", boxId, boxPokemonId)) - { - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Region.ToString(); - } - - // API: Original trainer of the pokemon of the current box matching the ID. - private string GetPokemonOriginalTrainerFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonOriginalTrainerFromPC", boxId, boxPokemonId)) - { - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].OriginalTrainer; - } - - // API: Gender of the pokemon of the current box matching the ID. - private string GetPokemonGenderFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonHappinessFromPC", boxId, boxPokemonId)) - { - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Gender; - } - - // API: Form of the pokémon in the current box matching the ID. (0 if no form) - private int GetPokemonFormFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonFormFromPC", boxId, boxPokemonId)) - { - return -1; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Form; - } - - // API: Status of the pokemon of the current box matching the ID. - private string GetPokemonStatusFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonStatusFromPC", boxId, boxPokemonId)) - { - return null; - } - return Bot.Game.CurrentPCBox[boxPokemonId - 1].Status; - } - - // API: Type of the pokemon of the current box matching the ID as an array of length 2. - private string[] GetPokemonTypeFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonTypeFromPC", boxId, boxPokemonId)) - { - return null; - } - - int id = Bot.Game.CurrentPCBox[boxPokemonId - 1].Id; - - if (id <= 0 || id >= TypesManager.Instance.Type1.Count()) - { - return new string[] { "Unknown", "Unknown" }; - } - - return new string[] { TypesManager.Instance.Type1[id].ToString(), TypesManager.Instance.Type2[id].ToString() }; - } - - // API: Returns the item held by the specified pokemon in the PC, null if empty. - private string GetPokemonHeldItemFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("getPokemonHeldItemFromPC", boxId, boxPokemonId)) - { - return null; - } - string itemHeld = Bot.Game.CurrentPCBox[boxPokemonId - 1].ItemHeld; - return itemHeld == string.Empty ? null : itemHeld; - } - - // API: Releases the specified pokemon in the team. - private bool ReleasePokemonFromTeam(int pokemonUid) - { - if (pokemonUid < 1 || pokemonUid > 6 || pokemonUid > Bot.Game.Team.Count) - { - Fatal("error: releasePokemonFromTeam: pokemonUid is out of range: " + pokemonUid - + " (team size: " + Bot.Game.Team.Count.ToString() + ")."); - return false; - } - if (!Bot.Game.IsPCOpen) - { - Fatal("error: releasePokemonFromTeam: cannot release a pokemon while the PC is closed: #" + pokemonUid + " (" + Bot.Game.Team[pokemonUid].Name + ")."); - return false; - } - if (Bot.Game.IsPCBoxRefreshing) - { - Fatal("error: releasePokemonFromTeam: cannot release a pokemon while the PC box is refreshing: #" + pokemonUid + " (" + Bot.Game.Team[pokemonUid].Name + ")."); - return false; - } - return ExecuteAction(Bot.Game.ReleasePokemonFromTeam(pokemonUid)); - } - - // API: Releases the specified pokemon in the PC. - private bool ReleasePokemonFromPC(int boxId, int boxPokemonId) - { - if (!IsPCAccessValid("releasePokemonFromPC", boxId, boxPokemonId)) - { - return false; - } - return ExecuteAction(Bot.Game.ReleasePokemonFromPC(boxId, boxPokemonId)); - } - - // API: Buys the specified item from the opened shop. - private bool BuyItem(string itemName, int quantity) - { - if (!ValidateAction("buyItem", false)) return false; - - if (Bot.Game.OpenedShop == null) - { - Fatal("error: buyItem can only be used when a shop is open."); - return false; - } - - ShopItem item = Bot.Game.OpenedShop.Items.FirstOrDefault(i => i.Name.Equals(itemName, StringComparison.InvariantCultureIgnoreCase)); - - if (item == null) - { - Fatal("error: buyItem: the item '" + itemName + "' does not exist in the opened shop."); - return false; - } - - return ExecuteAction(Bot.Game.BuyItem(item.Id, quantity)); - } - - // API: Relearn a move from the move relearner NPC. - private bool RelearnMove(string moveName) - { - if (!ValidateAction("relearnMove", false)) return false; - if (GetMoney() < 2000) return false; - - if (Bot.Game.MoveRelearner is null) - { - Fatal("error: relearnMove can only be used when you have talked with the move relearner npc."); - return false; - } - - MovesManager.MoveData move = Bot.Game.MoveRelearner.Moves.FirstOrDefault(i => i.Name.Equals(moveName.ToLowerInvariant(), StringComparison.InvariantCultureIgnoreCase)); - - if (move == null) - { - Fatal($"error: relearnMove: the move '{ moveName }' cannot be learn by the current Pokemon or already learnt."); - return false; - } - return ExecuteAction(Bot.Game.PurchaseMove(moveName)); - } - - // API: Give the specified item on the specified pokemon. - private bool GiveItemToPokemon(string itemName, int pokemonIndex) - { - if (!ValidateAction("giveItemToPokemon", false)) return false; - - if (pokemonIndex < 1 || pokemonIndex > Bot.Game.Team.Count) - { - Fatal("error: giveItemToPokemon: tried to retrieve the non-existing pokémon " + pokemonIndex + "."); - return false; - } - - InventoryItem item = Bot.Game.GetItemFromName(itemName); - if (item == null || item.Quantity == 0) - { - Fatal("error: giveItemToPokemon: tried to give the non-existing item '" + itemName + "'."); - return false; - } - - return ExecuteAction(Bot.Game.GiveItemToPokemon(pokemonIndex, item.Id)); - } - - // API: Take the held item from the specified pokemon. - private bool TakeItemFromPokemon(int index) - { - if (!ValidateAction("takeItemFromPokemon", false)) return false; - - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: takeItemFromPokemon: tried to retrieve the non-existing pokemon " + index + "."); - return false; - } - - if (Bot.Game.Team[index - 1].ItemHeld == string.Empty) - { - Fatal("error: takeItemFromPokemon: tried to take the non-existing held item from pokémon '" + index + "'."); - return false; - } - - return ExecuteAction(Bot.Game.TakeItemFromPokemon(index)); - } - - // API: Adds the specified answer to the answer queue. It will be used in the next dialog. - private void PushDialogAnswer(DynValue answerValue) - { - if (answerValue.Type == DataType.String) - { - Bot.Game.PushDialogAnswer(answerValue.CastToString()); - } - else if (answerValue.Type == DataType.Number) - { - Bot.Game.PushDialogAnswer((int)answerValue.CastToNumber()); - } - else - { - Fatal("error: pushDialogAnswer: the argument must be a number (index) or a string (search text)."); - } - } - - // API: Uses the specified item. - private bool UseItem(string itemName) - { - InventoryItem item = Bot.Game.GetItemFromName(itemName.ToUpperInvariant()); - if (item != null && item.Quantity > 0) - { - if (Bot.Game.IsInBattle && item.CanBeUsedInBattle) - { - if (!ValidateAction("useItem", true)) return false; - return ExecuteAction(Bot.AI.UseItem(item.Id)); - } - else if (!Bot.Game.IsInBattle && item.CanBeUsedOutsideOfBattle) - { - if (!ValidateAction("useItem", false)) return false; - Bot.Game.UseItem(item.Id); - return ExecuteAction(true); - } - } - return false; - } - - // API: Uses the specified item on the specified pokémon. - private bool UseItemOnPokemon(string itemName, int pokemonIndex) - { - itemName = itemName.ToUpperInvariant(); + Fatal("error: 'useItemOnPokemon' can only be used for items which don't affect any moves of the specified pokemon."); + return false; + } + if (Bot.Game.IsInBattle && item.CanBeUsedOnPokemonInBattle) + { + if (!ValidateAction("useItemOnPokemon", true)) return false; + return ExecuteAction(Bot.AI.UseItem(item.Id, pokemonIndex)); + } + else if (!Bot.Game.IsInBattle && item.CanBeUsedOnPokemonOutsideOfBattle) + { + if (!ValidateAction("useItemOnPokemon", false)) return false; + Bot.Game.UseItem(item.Id, pokemonIndex); + return ExecuteAction(true); + } + } + return false; + } + + // API: Uses the specified item on the specified pokémon's specified move. + private bool UseItemOnPokemonMove(string itemName, int pokemonIndex, DynValue move) + { + itemName = itemName.ToUpperInvariant(); InventoryItem item = Bot.Game.GetItemFromName(itemName.ToUpperInvariant()); - if (item != null && item.Quantity > 0) - { - if (Bot.Game.IsInBattle && item.CanBeUsedOnPokemonInBattle) - { - if (!ValidateAction("useItemOnPokemon", true)) return false; - return ExecuteAction(Bot.AI.UseItem(item.Id, pokemonIndex)); - } - else if (!Bot.Game.IsInBattle && item.CanBeUsedOnPokemonOutsideOfBattle) - { - if (!ValidateAction("useItemOnPokemon", false)) return false; - Bot.Game.UseItem(item.Id, pokemonIndex); - return ExecuteAction(true); - } - } - return false; - } - - // API: Uses the most effective offensive move available. - private bool Attack() - { - if (!ValidateAction("attack", true)) return false; - - return ExecuteAction(Bot.AI.Attack()); - } - - // API: Uses the least effective offensive move available. - private bool WeakAttack() - { - if (!ValidateAction("weakAttack", true)) return false; - - return ExecuteAction(Bot.AI.WeakAttack()); - } - - // API: Tries to escape from the current wild battle. - private bool Run() - { - if (!ValidateAction("run", true)) return false; - - return ExecuteAction(Bot.AI.Run()); - } - - // API: Sends the first usable pokemon different from the active one. - private bool SendUsablePokemon() - { - if (!ValidateAction("sendUsablePokemon", true)) return false; - - return ExecuteAction(Bot.AI.SendUsablePokemon()); - } - - // API: Sends the first available pokemon different from the active one. - private bool SendAnyPokemon() - { - if (!ValidateAction("sendAnyPokemon", true)) return false; - - return ExecuteAction(Bot.AI.SendAnyPokemon()); - } - - // API: Sends the specified pokemon to battle. - private bool SendPokemon(int index) - { - if (!ValidateAction("sendPokemon", true)) return false; - - if (index < 1 || index > Bot.Game.Team.Count) - { - Fatal("error: sendPokemon: tried to send the non-existing pokemon " + index + "."); - return false; - } - - return ExecuteAction(Bot.AI.SendPokemon(index)); - } - - // API: Uses the specified move in the current battle if available. - private bool UseMove(string moveName) - { - if (!ValidateAction("useMove", true)) return false; - - return ExecuteAction(Bot.AI.UseMove(moveName)); - } - - // API: Uses the first available move or struggle if out of PP. - private bool UseAnyMove() - { - if (!ValidateAction("useAnyMove", true)) return false; - - return ExecuteAction(Bot.AI.UseAnyMove()); - } - - // API: Forgets the specified move, if existing, in order to learn a new one. - private bool ForgetMove(string moveName) - { - if (!Bot.MoveTeacher.IsLearning) - { - Fatal("error: ‘forgetMove’ can only be used when a pokémon is learning a new move."); - return false; - } - - moveName = moveName.ToUpperInvariant(); - Pokemon pokemon = Bot.Game.Team[Bot.MoveTeacher.PokemonUid - 1]; - PokemonMove move = pokemon.Moves.FirstOrDefault(m => MovesManager.Instance.GetMoveData(m.Id)?.Name.ToUpperInvariant() == moveName); - - if (move != null) - { - Bot.MoveTeacher.MoveToForget = move.Position; - return true; - } - return false; - } - - // API: Forgets the first move that is not one of the specified moves. - private bool ForgetAnyMoveExcept(DynValue[] moveNames) - { - if (!Bot.MoveTeacher.IsLearning) - { - Fatal("error: ‘forgetAnyMoveExcept’ can only be used when a pokémon is learning a new move."); - return false; - } - - HashSet movesInvariantNames = new HashSet(); - foreach (DynValue value in moveNames) - { - movesInvariantNames.Add(value.CastToString().ToUpperInvariant()); - } - - Pokemon pokemon = Bot.Game.Team[Bot.MoveTeacher.PokemonUid - 1]; - PokemonMove move = pokemon.Moves.FirstOrDefault(m => !movesInvariantNames.Contains(MovesManager.Instance.GetMoveData(m.Id)?.Name.ToUpperInvariant())); - - if (move != null) - { - Bot.MoveTeacher.MoveToForget = move.Position; - return true; - } - return false; - } - - // API: Writes a string, a number, or a table of strings and/or numbers to file - // overwrite is an optional parameter, and will append the line(s) if absent - private void LogToFile(string file, DynValue text, bool overwrite = false) - { - DirectoryInfo directory = new DirectoryInfo("Logs/"); - FileInfo info = new FileInfo("Logs/" + file); - - // Restricting access to Logs folder - if (!info.FullName.StartsWith(directory.FullName)) - { - Fatal("Error: Invalid file write access"); - return; - } - - // Creating all necessary folders - Directory.CreateDirectory(Path.GetDirectoryName(info.FullName)); - - StringBuilder sb = new StringBuilder(); - - if (text.Type == DataType.Table) - { - DynValue[] lines = text.Table.Values.ToArray(); - for (int i = 0; i < lines.Length; i++) - { - sb.AppendLine(lines[i].CastToString()); - } - } - else - { - sb.AppendLine(text.CastToString()); - } - - if (overwrite) - File.WriteAllText(info.FullName, sb.ToString()); - else - File.AppendAllText(info.FullName, sb.ToString()); - } - - // API: Returns a table of every line in file - private string[] ReadLinesFromFile(string file) - { - DirectoryInfo directory = new DirectoryInfo("Logs/"); - FileInfo info = new FileInfo("Logs/" + file); - - if (!info.FullName.StartsWith(directory.FullName)) - { - Fatal("Error: Invalid File read access"); - return new string[] { }; - } - - file = info.FullName; - - if (!File.Exists(file)) return new string[] { }; - return File.ReadAllLines(file); - } - - // API: Returns the connected server - private string GetServer() - { - return Bot.Game != null ? Bot.Game.Server.ToString() : "None"; - } - - // API: Sets the option at a particular index, or creates it if it doesn't exist - private void SetOption(int index, bool value) - { - if (!Bot.SliderOptions.ContainsKey(index)) - { - Bot.CreateSlider(index, value); - return; - } - - Bot.SliderOptions[index].IsEnabled = value; - } - - // API: Gets the option at a particular index, or creates it if it doesn't exist - private bool GetOption(int index) - { - if (!Bot.SliderOptions.ContainsKey(index)) - return false; - - return Bot.SliderOptions[index].IsEnabled; - } - - // API: Sets the name of the option at a particular index, or creates it if it doesn't exist - private void SetOptionName(int index, string content) - { - if (!Bot.SliderOptions.ContainsKey(index)) - { - Bot.CreateSlider(index, content + ": ", true); - return; - } - - Bot.SliderOptions[index].Name = content + ": "; - } - - // API: Sets the tooltip description of the option at a particular index, or creates it if it doesn't exist - private void SetOptionDescription(int index, string content) - { - if (!Bot.SliderOptions.ContainsKey(index)) - { - Bot.CreateSlider(index, content, false); - return; - } - - Bot.SliderOptions[index].Description = content; - } - - // API: Removes the slider option at the specified index - private void RemoveOption(int index) - { - if (Bot.SliderOptions.ContainsKey(index)) - Bot.RemoveSlider(index); - } - - // API: Sets the text of the TextOption at a particular index, or creates it if it doesn't exist - private void SetTextOption(int index, string content) - { - if (!Bot.TextOptions.ContainsKey(index)) - { - Bot.CreateText(index, content); - return; - } - - Bot.TextOptions[index].Content = content; - } - - // API: Returns the text content of the TextOption at a particular index, or an empty string if it doesn't exist - private string GetTextOption(int index) - { - if (!Bot.TextOptions.ContainsKey(index)) - return ""; - - return Bot.TextOptions[index].Content; - } - - // API: Sets the name of the TextOption at a particular index, or creates it if it doesn't exist - private void SetTextOptionName(int index, string content) - { - if (!Bot.TextOptions.ContainsKey(index)) - { - Bot.CreateText(index, content + ": ", true); - return; - } - - Bot.TextOptions[index].Name = content + ": "; - } - - // API: Sets the tooltip description of the TextOption at a particular index, or creates it if it doesn't exist - private void SetTextOptionDescription(int index, string content) - { - if (!Bot.TextOptions.ContainsKey(index)) - { - Bot.CreateText(index, content, false); - return; - } - - Bot.TextOptions[index].Description = content; - } - - // API: Removes the text option at the specified index - private void RemoveTextOption(int index) - { - if (Bot.TextOptions.ContainsKey(index)) - Bot.RemoveText(index); - } - - // API: Sets the item that will be used to mount the player - private bool SetMount(string mount) - { - if (string.IsNullOrEmpty(mount)) - { - Bot.Game.GroundMount = null; - return true; - } - - InventoryItem item = Bot.Game.GetItemFromName(mount); - - if (item == null) - return false; - - Bot.Game.GroundMount = item; - return true; - } - - // API: Sets the item that will be used when the player begins surfing - private bool SetWaterMount(string mount) - { - if (string.IsNullOrEmpty(mount)) - { - Bot.Game.WaterMount = null; - return true; - } - - InventoryItem item = Bot.Game.GetItemFromName(mount); - - if (item == null) - return false; - - Bot.Game.WaterMount = item; - return true; - } - } -} + if (pokemonIndex < 1 || pokemonIndex > 6 || (move.Type != DataType.String && move.Type != DataType.Number)) return false; + + int moveIndex = 0; + if (move.Type == DataType.String) + moveIndex = Bot.Game.Team[pokemonIndex - 1].Moves. + FirstOrDefault(m => MovesManager.Instance.GetMoveData(m.Id)?.Name.ToUpperInvariant() == move.String.ToUpperInvariant()).Position; + if (move.Type == DataType.Number) + moveIndex = (int)move.Number; + + if (item != null && item.Quantity > 0 && moveIndex > 0) + { + if (Bot.Game.IsInBattle && item.CanBeUsedOnPokemonInBattle) + { + if (!ValidateAction("useItemOnPokemonMove", true)) return false; + return ExecuteAction(Bot.AI.UseItem(item.Id, pokemonIndex, moveIndex)); + } + else if (!Bot.Game.IsInBattle && item.CanBeUsedOnPokemonOutsideOfBattle) + { + if (!ValidateAction("useItemOnPokemonMove", false)) return false; + Bot.Game.UseItem(item.Id, pokemonIndex, moveIndex); + return ExecuteAction(true); + } + } + return false; + } + + // API: Uses the most effective offensive move available. + private bool Attack() + { + if (!ValidateAction("attack", true)) return false; + + return ExecuteAction(Bot.AI.Attack()); + } + + // API: Uses the least effective offensive move available. + private bool WeakAttack() + { + if (!ValidateAction("weakAttack", true)) return false; + + return ExecuteAction(Bot.AI.WeakAttack()); + } + + // API: Tries to escape from the current wild battle. + private bool Run() + { + if (!ValidateAction("run", true)) return false; + + return ExecuteAction(Bot.AI.Run()); + } + + // API: Sends the first usable pokemon different from the active one. + private bool SendUsablePokemon() + { + if (!ValidateAction("sendUsablePokemon", true)) return false; + + return ExecuteAction(Bot.AI.SendUsablePokemon()); + } + + // API: Sends the first available pokemon different from the active one. + private bool SendAnyPokemon() + { + if (!ValidateAction("sendAnyPokemon", true)) return false; + + return ExecuteAction(Bot.AI.SendAnyPokemon()); + } + + // API: Sends the specified pokemon to battle. + private bool SendPokemon(int index) + { + if (!ValidateAction("sendPokemon", true)) return false; + + if (index < 1 || index > Bot.Game.Team.Count) + { + Fatal("error: sendPokemon: tried to send the non-existing pokemon " + index + "."); + return false; + } + + return ExecuteAction(Bot.AI.SendPokemon(index)); + } + + // API: Uses the specified move in the current battle if available. + private bool UseMove(string moveName) + { + if (!ValidateAction("useMove", true)) return false; + + return ExecuteAction(Bot.AI.UseMove(moveName)); + } + + // API: Uses the first available move or struggle if out of PP. + private bool UseAnyMove() + { + if (!ValidateAction("useAnyMove", true)) return false; + + return ExecuteAction(Bot.AI.UseAnyMove()); + } + + // API: Forgets the specified move, if existing, in order to learn a new one. + private bool ForgetMove(string moveName) + { + if (!Bot.MoveTeacher.IsLearning) + { + Fatal("error: ‘forgetMove’ can only be used when a pokémon is learning a new move."); + return false; + } + + moveName = moveName.ToUpperInvariant(); + Pokemon pokemon = Bot.Game.GetPokemonFromDBId(Bot.MoveTeacher.PokemonDBid); + PokemonMove move = pokemon.Moves.FirstOrDefault(m => MovesManager.Instance.GetMoveData(m.Id)?.Name.ToUpperInvariant() == moveName); + + if (move != null) + { + Bot.MoveTeacher.MoveToForget = move.Position; + return true; + } + return false; + } + + // API: Forgets the first move that is not one of the specified moves. + private bool ForgetAnyMoveExcept(DynValue[] moveNames) + { + if (!Bot.MoveTeacher.IsLearning) + { + Fatal("error: ‘forgetAnyMoveExcept’ can only be used when a pokémon is learning a new move."); + return false; + } + + HashSet movesInvariantNames = new HashSet(); + foreach (DynValue value in moveNames) + { + movesInvariantNames.Add(value.CastToString().ToUpperInvariant()); + } + + Pokemon pokemon = Bot.Game.GetPokemonFromDBId(Bot.MoveTeacher.PokemonDBid); + PokemonMove move = pokemon.Moves.FirstOrDefault(m => !movesInvariantNames.Contains(MovesManager.Instance.GetMoveData(m.Id)?.Name.ToUpperInvariant())); + + if (move != null) + { + Bot.MoveTeacher.MoveToForget = move.Position; + return true; + } + return false; + } + + // API: Writes a string, a number, or a table of strings and/or numbers to file + // overwrite is an optional parameter, and will append the line(s) if absent + private void LogToFile(string file, DynValue text, bool overwrite = false) + { + DirectoryInfo directory = new DirectoryInfo("Logs/"); + FileInfo info = new FileInfo("Logs/" + file); + + // Restricting access to Logs folder + if (!info.FullName.StartsWith(directory.FullName)) + { + Fatal("Error: Invalid file write access"); + return; + } + + // Creating all necessary folders + Directory.CreateDirectory(Path.GetDirectoryName(info.FullName)); + + StringBuilder sb = new StringBuilder(); + + if (text.Type == DataType.Table) + { + DynValue[] lines = text.Table.Values.ToArray(); + for (int i = 0; i < lines.Length; i++) + { + sb.AppendLine(lines[i].CastToString()); + } + } + else + { + sb.AppendLine(text.CastToString()); + } + + if (overwrite) + File.WriteAllText(info.FullName, sb.ToString()); + else + File.AppendAllText(info.FullName, sb.ToString()); + } + + // API: Returns a table of every line in file + private string[] ReadLinesFromFile(string file) + { + DirectoryInfo directory = new DirectoryInfo("Logs/"); + FileInfo info = new FileInfo("Logs/" + file); + + if (!info.FullName.StartsWith(directory.FullName)) + { + Fatal("Error: Invalid File read access"); + return new string[] { }; + } + + file = info.FullName; + + if (!File.Exists(file)) return new string[] { }; + return File.ReadAllLines(file); + } + + // API: Returns the connected server + private string GetServer() + { + return Bot.Game != null ? Bot.Game.Server.ToString() : "None"; + } + + // API: Sets the option at a particular index, or creates it if it doesn't exist + private void SetOption(int index, bool value) + { + if (!Bot.SliderOptions.ContainsKey(index)) + { + Bot.CreateSlider(index, value); + return; + } + + Bot.SliderOptions[index].IsEnabled = value; + } + + // API: Gets the option at a particular index, or creates it if it doesn't exist + private bool GetOption(int index) + { + if (!Bot.SliderOptions.ContainsKey(index)) + return false; + + return Bot.SliderOptions[index].IsEnabled; + } + + // API: Sets the name of the option at a particular index, or creates it if it doesn't exist + private void SetOptionName(int index, string content) + { + if (!Bot.SliderOptions.ContainsKey(index)) + { + Bot.CreateSlider(index, content + ": ", true); + return; + } + + Bot.SliderOptions[index].Name = content + ": "; + } + + // API: Sets the tooltip description of the option at a particular index, or creates it if it doesn't exist + private void SetOptionDescription(int index, string content) + { + if (!Bot.SliderOptions.ContainsKey(index)) + { + Bot.CreateSlider(index, content, false); + return; + } + + Bot.SliderOptions[index].Description = content; + } + + // API: Removes the slider option at the specified index + private void RemoveOption(int index) + { + if (Bot.SliderOptions.ContainsKey(index)) + Bot.RemoveSlider(index); + } + + // API: Sets the text of the TextOption at a particular index, or creates it if it doesn't exist + private void SetTextOption(int index, string content) + { + if (!Bot.TextOptions.ContainsKey(index)) + { + Bot.CreateText(index, content); + return; + } + + Bot.TextOptions[index].Content = content; + } + + // API: Returns the text content of the TextOption at a particular index, or an empty string if it doesn't exist + private string GetTextOption(int index) + { + if (!Bot.TextOptions.ContainsKey(index)) + return ""; + + return Bot.TextOptions[index].Content; + } + + // API: Sets the name of the TextOption at a particular index, or creates it if it doesn't exist + private void SetTextOptionName(int index, string content) + { + if (!Bot.TextOptions.ContainsKey(index)) + { + Bot.CreateText(index, content + ": ", true); + return; + } + + Bot.TextOptions[index].Name = content + ": "; + } + + // API: Sets the tooltip description of the TextOption at a particular index, or creates it if it doesn't exist + private void SetTextOptionDescription(int index, string content) + { + if (!Bot.TextOptions.ContainsKey(index)) + { + Bot.CreateText(index, content, false); + return; + } + + Bot.TextOptions[index].Description = content; + } + + // API: Removes the text option at the specified index + private void RemoveTextOption(int index) + { + if (Bot.TextOptions.ContainsKey(index)) + Bot.RemoveText(index); + } + + // API: Sets the item that will be used to mount the player + private bool SetMount(string mount) + { + if (string.IsNullOrEmpty(mount)) + { + Bot.Game.GroundMount = null; + return true; + } + + InventoryItem item = Bot.Game.GetItemFromName(mount); + + if (item == null) + return false; + + Bot.Game.GroundMount = item; + return true; + } + + // API: Sets the item that will be used when the player begins surfing + private bool SetWaterMount(string mount) + { + if (string.IsNullOrEmpty(mount)) + { + Bot.Game.WaterMount = null; + return true; + } + + InventoryItem item = Bot.Game.GetItemFromName(mount); + + if (item == null) + return false; + + Bot.Game.WaterMount = item; + return true; + } + } +} diff --git a/PROBot/packages.config b/PROBot/packages.config index 2e8219b..747f455 100644 --- a/PROBot/packages.config +++ b/PROBot/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/PROProtocol/AbilitiesManager.cs b/PROProtocol/AbilitiesManager.cs new file mode 100644 index 0000000..86be676 --- /dev/null +++ b/PROProtocol/AbilitiesManager.cs @@ -0,0 +1,33 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; + +namespace PROProtocol +{ + public class AbilityData + { + public string Name { get; set; } + public string Desc { get; set; } + } + + public class AbilitiesManager + { + private static AbilitiesManager _instance; + + public static AbilitiesManager Instance + { + get + { + return _instance ?? (_instance = new AbilitiesManager()); + } + } + + public Dictionary Abilities { get; } + + private AbilitiesManager() + { + Abilities = ResourcesUtil.GetResource>("Abilities.json"); + } + } +} diff --git a/PROProtocol/Battle.cs b/PROProtocol/Battle.cs index 6cfe007..410bee3 100644 --- a/PROProtocol/Battle.cs +++ b/PROProtocol/Battle.cs @@ -42,21 +42,22 @@ public Battle(string playerName, string[] data) OpponentHealth = Convert.ToInt32(data[1]); CurrentHealth = OpponentHealth; OpponentLevel = Convert.ToInt32(data[2]); - SelectedPokemonIndex = Convert.ToInt32(data[3]) - 1; - BattleText = data[4]; - IsShiny = (data[5] != "0"); - IsWild = data[6] == string.Empty && data[8] == string.Empty; - TrainerName = data[6]; - IsPvp = data[7] == string.Empty; + // data[3] = mega form? or something + SelectedPokemonIndex = Convert.ToInt32(data[4]) - 1; + BattleText = data[5]; + IsShiny = (data[6] != "0"); + IsWild = data[7] == string.Empty && data[9] == string.Empty; + TrainerName = data[7]; + IsPvp = data[8] == string.Empty; PokemonCount = 1; - if (data[8] != string.Empty) + if (data[9] != string.Empty) { PokemonCount = Convert.ToInt32(data[8]); } - OpponentGender = data[9]; - OpponentStatus = data[10]; - AlreadyCaught = (data[11] == "1"); - AlternateForm = int.Parse(data[12]); + OpponentGender = data[10]; + OpponentStatus = data[11]; + AlreadyCaught = data[12] == "1"; + AlternateForm = int.Parse(data[13]); } public bool ProcessMessage(List team, string message) @@ -126,14 +127,15 @@ public bool ProcessMessage(List team, string message) int pokemonId = Convert.ToInt32(data[2]); int level = Convert.ToInt32(data[3]); - bool isShiny = data[4] == "1"; - int maxHealth = Convert.ToInt32(data[5]); - int currentHealth = Convert.ToInt32(data[6]); - int index = Convert.ToInt32(data[7]) - 1; - string status = data[8]; - string gender = data[9]; - bool alreadyCaught = (data[10] == "1"); - string alternateForm = data[11]; + // data[4] = mega form? or something + bool isShiny = data[5] == "1"; + int maxHealth = Convert.ToInt32(data[6]); + int currentHealth = Convert.ToInt32(data[7]); + int index = Convert.ToInt32(data[8]) - 1; + string status = data[9]; + string gender = data[10]; + bool alreadyCaught = (data[11] == "1"); + string alternateForm = data[12]; if (data[1] == _playerName) { @@ -191,10 +193,17 @@ public bool ProcessMessage(List team, string message) int stickyWeb = Convert.ToInt32(data[5]); return true; } - - if (message.StartsWith("CP") && message.Length == 3) + + if (message.StartsWith("T:") || message.StartsWith("U:") || message.StartsWith("EXP:")) + { + // exp animation + // may be blackout animation? + return true; + } + + if (message.StartsWith("W:")) { - // Capture animation (2 = fail, 3 = success). + // Capture animation (ball shake number):(1 = success, 0 = fail). return true; } diff --git a/PROProtocol/EffortValuesManager.cs b/PROProtocol/EffortValuesManager.cs index dd03709..a7b6222 100644 --- a/PROProtocol/EffortValuesManager.cs +++ b/PROProtocol/EffortValuesManager.cs @@ -20,826 +20,10 @@ private EffortValuesManager() { BattleValues = new Dictionary(); - Add(1, 0, 0, 0, 1, 0, 0); - Add(2, 0, 0, 0, 1, 1, 0); - Add(3, 0, 0, 0, 2, 1, 0); - Add(4, 0, 0, 0, 0, 0, 1); - Add(5, 0, 0, 0, 1, 0, 1); - Add(6, 0, 0, 0, 3, 0, 0); - Add(7, 0, 0, 1, 0, 0, 0); - Add(8, 0, 0, 1, 0, 1, 0); - Add(9, 0, 0, 0, 0, 3, 0); - Add(10, 1, 0, 0, 0, 0, 0); - Add(11, 0, 0, 2, 0, 0, 0); - Add(12, 0, 0, 0, 2, 1, 0); - Add(13, 0, 0, 0, 0, 0, 1); - Add(14, 0, 0, 2, 0, 0, 0); - Add(15, 0, 2, 0, 0, 1, 0); - Add(16, 0, 0, 0, 0, 0, 1); - Add(17, 0, 0, 0, 0, 0, 2); - Add(18, 0, 0, 0, 0, 0, 3); - Add(19, 0, 0, 0, 0, 0, 1); - Add(20, 0, 0, 0, 0, 0, 2); - Add(21, 0, 0, 0, 0, 0, 1); - Add(22, 0, 0, 0, 0, 0, 2); - Add(23, 0, 1, 0, 0, 0, 0); - Add(24, 0, 2, 0, 0, 0, 0); - Add(25, 0, 0, 0, 0, 0, 2); - Add(26, 0, 0, 0, 0, 0, 3); - Add(27, 0, 0, 1, 0, 0, 0); - Add(28, 0, 0, 2, 0, 0, 0); - Add(29, 1, 0, 0, 0, 0, 0); - Add(30, 2, 0, 0, 0, 0, 0); - Add(31, 3, 0, 0, 0, 0, 0); - Add(32, 0, 1, 0, 0, 0, 0); - Add(33, 0, 2, 0, 0, 0, 0); - Add(34, 0, 3, 0, 0, 0, 0); - Add(35, 2, 0, 0, 0, 0, 0); - Add(36, 3, 0, 0, 0, 0, 0); - Add(37, 0, 0, 0, 0, 0, 1); - Add(38, 0, 0, 0, 0, 1, 1); - Add(39, 2, 0, 0, 0, 0, 0); - Add(40, 3, 0, 0, 0, 0, 0); - Add(41, 0, 0, 0, 0, 0, 1); - Add(42, 0, 0, 0, 0, 0, 2); - Add(43, 0, 0, 0, 1, 0, 0); - Add(44, 0, 0, 0, 2, 0, 0); - Add(45, 0, 0, 0, 3, 0, 0); - Add(46, 0, 1, 0, 0, 0, 0); - Add(47, 0, 2, 1, 0, 0, 0); - Add(48, 0, 0, 0, 0, 1, 0); - Add(49, 0, 0, 0, 1, 0, 1); - Add(50, 0, 0, 0, 0, 0, 1); - Add(51, 0, 0, 0, 0, 0, 2); - Add(52, 0, 0, 0, 0, 0, 1); - Add(53, 0, 0, 0, 0, 0, 2); - Add(54, 0, 0, 0, 1, 0, 0); - Add(55, 0, 0, 0, 2, 0, 0); - Add(56, 0, 1, 0, 0, 0, 0); - Add(57, 0, 2, 0, 0, 0, 0); - Add(58, 0, 1, 0, 0, 0, 0); - Add(59, 0, 2, 0, 0, 0, 0); - Add(60, 0, 0, 0, 0, 0, 1); - Add(61, 0, 0, 0, 0, 0, 2); - Add(62, 0, 0, 3, 0, 0, 0); - Add(63, 0, 0, 0, 1, 0, 0); - Add(64, 0, 0, 0, 2, 0, 0); - Add(65, 0, 0, 0, 3, 0, 0); - Add(66, 0, 1, 0, 0, 0, 0); - Add(67, 0, 2, 0, 0, 0, 0); - Add(68, 0, 3, 0, 0, 0, 0); - Add(69, 0, 1, 0, 0, 0, 0); - Add(70, 0, 2, 0, 0, 0, 0); - Add(71, 0, 3, 0, 0, 0, 0); - Add(72, 0, 0, 0, 0, 1, 0); - Add(73, 0, 0, 0, 0, 2, 0); - Add(74, 0, 0, 1, 0, 0, 0); - Add(75, 0, 0, 2, 0, 0, 0); - Add(76, 0, 0, 3, 0, 0, 0); - Add(77, 0, 0, 0, 0, 0, 1); - Add(78, 0, 0, 0, 0, 0, 2); - Add(79, 1, 0, 0, 0, 0, 0); - Add(80, 0, 0, 2, 0, 0, 0); - Add(81, 0, 0, 0, 1, 0, 0); - Add(82, 0, 0, 0, 2, 0, 0); - Add(83, 0, 1, 0, 0, 0, 0); - Add(84, 0, 1, 0, 0, 0, 0); - Add(85, 0, 2, 0, 0, 0, 0); - Add(86, 0, 0, 0, 0, 1, 0); - Add(87, 0, 0, 0, 0, 2, 0); - Add(88, 1, 0, 0, 0, 0, 0); - Add(89, 1, 1, 0, 0, 0, 0); - Add(90, 0, 0, 1, 0, 0, 0); - Add(91, 0, 0, 2, 0, 0, 0); - Add(92, 0, 0, 0, 1, 0, 0); - Add(93, 0, 0, 0, 2, 0, 0); - Add(94, 0, 0, 0, 3, 0, 0); - Add(95, 0, 0, 1, 0, 0, 0); - Add(96, 0, 0, 0, 0, 1, 0); - Add(97, 0, 0, 0, 0, 2, 0); - Add(98, 0, 1, 0, 0, 0, 0); - Add(99, 0, 2, 0, 0, 0, 0); - Add(100, 0, 0, 0, 0, 0, 1); - Add(101, 0, 0, 0, 0, 0, 2); - Add(102, 0, 0, 1, 0, 0, 0); - Add(103, 0, 0, 0, 2, 0, 0); - Add(104, 0, 0, 1, 0, 0, 0); - Add(105, 0, 0, 2, 0, 0, 0); - Add(106, 0, 2, 0, 0, 0, 0); - Add(107, 0, 0, 0, 0, 2, 0); - Add(108, 2, 0, 0, 0, 0, 0); - Add(109, 0, 0, 1, 0, 0, 0); - Add(110, 0, 0, 2, 0, 0, 0); - Add(111, 0, 0, 1, 0, 0, 0); - Add(112, 0, 2, 0, 0, 0, 0); - Add(113, 2, 0, 0, 0, 0, 0); - Add(114, 0, 0, 1, 0, 0, 0); - Add(115, 2, 0, 0, 0, 0, 0); - Add(116, 0, 0, 0, 1, 0, 0); - Add(117, 0, 0, 1, 1, 0, 0); - Add(118, 0, 1, 0, 0, 0, 0); - Add(119, 0, 2, 0, 0, 0, 0); - Add(120, 0, 0, 0, 0, 0, 1); - Add(121, 0, 0, 0, 0, 0, 2); - Add(122, 0, 0, 0, 0, 2, 0); - Add(123, 0, 1, 0, 0, 0, 0); - Add(124, 0, 0, 0, 2, 0, 0); - Add(125, 0, 0, 0, 0, 0, 2); - Add(126, 0, 0, 0, 2, 0, 0); - Add(127, 0, 2, 0, 0, 0, 0); - Add(128, 0, 1, 0, 0, 0, 1); - Add(129, 0, 0, 0, 0, 0, 1); - Add(130, 0, 2, 0, 0, 0, 0); - Add(131, 2, 0, 0, 0, 0, 0); - Add(132, 1, 0, 0, 0, 0, 0); - Add(133, 0, 0, 0, 0, 1, 0); - Add(134, 2, 0, 0, 0, 0, 0); - Add(135, 0, 0, 0, 0, 0, 2); - Add(136, 0, 2, 0, 0, 0, 0); - Add(137, 0, 0, 0, 1, 0, 0); - Add(138, 0, 0, 1, 0, 0, 0); - Add(139, 0, 0, 2, 0, 0, 0); - Add(140, 0, 0, 1, 0, 0, 0); - Add(141, 0, 2, 0, 0, 0, 0); - Add(142, 0, 0, 0, 0, 0, 2); - Add(143, 2, 0, 0, 0, 0, 0); - Add(144, 0, 0, 0, 0, 3, 0); - Add(145, 0, 0, 0, 3, 0, 0); - Add(146, 0, 0, 0, 3, 0, 0); - Add(147, 0, 1, 0, 0, 0, 0); - Add(148, 0, 2, 0, 0, 0, 0); - Add(149, 0, 3, 0, 0, 0, 0); - Add(150, 0, 0, 0, 3, 0, 0); - Add(151, 3, 0, 0, 0, 0, 0); - Add(152, 0, 0, 0, 0, 1, 0); - Add(153, 0, 0, 1, 0, 1, 0); - Add(154, 0, 0, 1, 0, 2, 0); - Add(155, 0, 0, 0, 0, 0, 1); - Add(156, 0, 0, 0, 1, 0, 1); - Add(157, 0, 0, 0, 3, 0, 0); - Add(158, 0, 1, 0, 0, 0, 0); - Add(159, 0, 1, 1, 0, 0, 0); - Add(160, 0, 2, 1, 0, 0, 0); - Add(161, 0, 1, 0, 0, 0, 0); - Add(162, 0, 0, 0, 0, 0, 2); - Add(163, 1, 0, 0, 0, 0, 0); - Add(164, 2, 0, 0, 0, 0, 0); - Add(165, 0, 0, 0, 0, 1, 0); - Add(166, 0, 0, 0, 0, 2, 0); - Add(167, 0, 1, 0, 0, 0, 0); - Add(168, 0, 2, 0, 0, 0, 0); - Add(169, 0, 0, 0, 0, 0, 3); - Add(170, 1, 0, 0, 0, 0, 0); - Add(171, 2, 0, 0, 0, 0, 0); - Add(172, 0, 0, 0, 0, 0, 1); - Add(173, 0, 0, 0, 0, 1, 0); - Add(174, 1, 0, 0, 0, 0, 0); - Add(175, 0, 0, 0, 0, 1, 0); - Add(176, 0, 0, 0, 0, 2, 0); - Add(177, 0, 0, 0, 1, 0, 0); - Add(178, 0, 0, 0, 1, 0, 1); - Add(179, 0, 0, 0, 1, 0, 0); - Add(180, 0, 0, 0, 2, 0, 0); - Add(181, 0, 0, 0, 3, 0, 0); - Add(182, 0, 0, 0, 0, 3, 0); - Add(183, 2, 0, 0, 0, 0, 0); - Add(184, 3, 0, 0, 0, 0, 0); - Add(185, 0, 0, 2, 0, 0, 0); - Add(186, 0, 0, 0, 0, 3, 0); - Add(187, 0, 0, 0, 0, 1, 0); - Add(188, 0, 0, 0, 0, 0, 2); - Add(189, 0, 0, 0, 0, 0, 3); - Add(190, 0, 0, 0, 0, 0, 1); - Add(191, 0, 0, 0, 1, 0, 0); - Add(192, 0, 0, 0, 2, 0, 0); - Add(193, 0, 0, 0, 0, 0, 1); - Add(194, 1, 0, 0, 0, 0, 0); - Add(195, 2, 0, 0, 0, 0, 0); - Add(196, 0, 0, 0, 2, 0, 0); - Add(197, 0, 0, 0, 0, 2, 0); - Add(198, 0, 0, 0, 0, 0, 1); - Add(199, 0, 0, 0, 0, 3, 0); - Add(200, 0, 0, 0, 0, 1, 0); - Add(201, 0, 1, 0, 1, 0, 0); - Add(202, 2, 0, 0, 0, 0, 0); - Add(203, 0, 0, 0, 2, 0, 0); - Add(204, 0, 0, 1, 0, 0, 0); - Add(205, 0, 0, 2, 0, 0, 0); - Add(206, 1, 0, 0, 0, 0, 0); - Add(207, 0, 0, 1, 0, 0, 0); - Add(208, 0, 0, 2, 0, 0, 0); - Add(209, 0, 1, 0, 0, 0, 0); - Add(210, 0, 2, 0, 0, 0, 0); - Add(211, 0, 1, 0, 0, 0, 0); - Add(212, 0, 2, 0, 0, 0, 0); - Add(213, 0, 0, 1, 0, 1, 0); - Add(214, 0, 2, 0, 0, 0, 0); - Add(215, 0, 0, 0, 0, 0, 1); - Add(216, 0, 1, 0, 0, 0, 0); - Add(217, 0, 2, 0, 0, 0, 0); - Add(218, 0, 0, 0, 1, 0, 0); - Add(219, 0, 0, 2, 0, 0, 0); - Add(220, 0, 1, 0, 0, 0, 0); - Add(221, 1, 1, 0, 0, 0, 0); - Add(222, 0, 0, 1, 0, 1, 0); - Add(223, 0, 0, 0, 1, 0, 0); - Add(224, 0, 1, 0, 1, 0, 0); - Add(225, 0, 0, 0, 0, 0, 1); - Add(226, 0, 0, 0, 0, 2, 0); - Add(227, 0, 0, 2, 0, 0, 0); - Add(228, 0, 0, 0, 1, 0, 0); - Add(229, 0, 0, 0, 2, 0, 0); - Add(230, 0, 1, 0, 1, 1, 0); - Add(231, 1, 0, 0, 0, 0, 0); - Add(232, 0, 1, 1, 0, 0, 0); - Add(233, 0, 0, 0, 2, 0, 0); - Add(234, 0, 1, 0, 0, 0, 0); - Add(235, 0, 0, 0, 0, 0, 1); - Add(236, 0, 1, 0, 0, 0, 0); - Add(237, 0, 0, 0, 0, 2, 0); - Add(238, 0, 0, 0, 1, 0, 0); - Add(239, 0, 0, 0, 0, 0, 1); - Add(240, 0, 0, 0, 0, 0, 1); - Add(241, 0, 0, 2, 0, 0, 0); - Add(242, 3, 0, 0, 0, 0, 0); - Add(243, 0, 0, 0, 1, 0, 2); - Add(244, 1, 2, 0, 0, 0, 0); - Add(245, 0, 0, 1, 0, 2, 0); - Add(246, 0, 1, 0, 0, 0, 0); - Add(247, 0, 2, 0, 0, 0, 0); - Add(248, 0, 3, 0, 0, 0, 0); - Add(249, 0, 0, 0, 0, 3, 0); - Add(250, 0, 0, 0, 0, 3, 0); - Add(251, 3, 0, 0, 0, 0, 0); - Add(252, 0, 0, 0, 0, 0, 1); - Add(253, 0, 0, 0, 0, 0, 2); - Add(254, 0, 0, 0, 0, 0, 3); - Add(255, 0, 0, 0, 1, 0, 0); - Add(256, 0, 1, 0, 1, 0, 0); - Add(257, 0, 3, 0, 0, 0, 0); - Add(258, 0, 1, 0, 0, 0, 0); - Add(259, 0, 2, 0, 0, 0, 0); - Add(260, 0, 3, 0, 0, 0, 0); - Add(261, 0, 1, 0, 0, 0, 0); - Add(262, 0, 2, 0, 0, 0, 0); - Add(263, 0, 0, 0, 0, 0, 1); - Add(264, 0, 0, 0, 0, 0, 2); - Add(265, 1, 0, 0, 0, 0, 0); - Add(266, 0, 0, 2, 0, 0, 0); - Add(267, 0, 0, 0, 3, 0, 0); - Add(268, 0, 0, 2, 0, 0, 0); - Add(269, 0, 0, 0, 0, 3, 0); - Add(270, 0, 0, 0, 0, 1, 0); - Add(271, 0, 0, 0, 0, 2, 0); - Add(272, 0, 0, 0, 0, 3, 0); - Add(273, 0, 0, 1, 0, 0, 0); - Add(274, 0, 2, 0, 0, 0, 0); - Add(275, 0, 3, 0, 0, 0, 0); - Add(276, 0, 0, 0, 0, 0, 1); - Add(277, 0, 0, 0, 0, 0, 2); - Add(278, 0, 0, 0, 0, 0, 1); - Add(279, 0, 0, 2, 0, 0, 0); - Add(280, 0, 0, 0, 1, 0, 0); - Add(281, 0, 0, 0, 2, 0, 0); - Add(282, 0, 0, 0, 3, 0, 0); - Add(283, 0, 0, 0, 0, 0, 1); - Add(284, 0, 0, 0, 1, 1, 0); - Add(285, 1, 0, 0, 0, 0, 0); - Add(286, 0, 2, 0, 0, 0, 0); - Add(287, 1, 0, 0, 0, 0, 0); - Add(288, 0, 0, 0, 0, 0, 2); - Add(289, 3, 0, 0, 0, 0, 0); - Add(290, 0, 0, 1, 0, 0, 0); - Add(291, 0, 0, 0, 0, 0, 2); - Add(292, 2, 0, 0, 0, 0, 0); - Add(293, 1, 0, 0, 0, 0, 0); - Add(294, 2, 0, 0, 0, 0, 0); - Add(295, 3, 0, 0, 0, 0, 0); - Add(296, 1, 0, 0, 0, 0, 0); - Add(297, 2, 0, 0, 0, 0, 0); - Add(298, 1, 0, 0, 0, 0, 0); - Add(299, 0, 0, 1, 0, 0, 0); - Add(300, 0, 0, 0, 0, 0, 1); - Add(301, 1, 0, 0, 0, 0, 1); - Add(302, 0, 1, 1, 0, 0, 0); - Add(303, 0, 1, 1, 0, 0, 0); - Add(304, 0, 0, 1, 0, 0, 0); - Add(305, 0, 0, 2, 0, 0, 0); - Add(306, 0, 0, 3, 0, 0, 0); - Add(307, 0, 0, 0, 0, 0, 1); - Add(308, 0, 0, 0, 0, 0, 2); - Add(309, 0, 0, 0, 0, 0, 1); - Add(310, 0, 0, 0, 0, 0, 2); - Add(311, 0, 0, 0, 0, 0, 1); - Add(312, 0, 0, 0, 0, 0, 1); - Add(313, 0, 0, 0, 0, 0, 1); - Add(314, 0, 0, 0, 0, 0, 1); - Add(315, 0, 0, 0, 2, 0, 0); - Add(316, 1, 0, 0, 0, 0, 0); - Add(317, 2, 0, 0, 0, 0, 0); - Add(318, 0, 1, 0, 0, 0, 0); - Add(319, 0, 2, 0, 0, 0, 0); - Add(320, 1, 0, 0, 0, 0, 0); - Add(321, 2, 0, 0, 0, 0, 0); - Add(322, 0, 0, 0, 1, 0, 0); - Add(323, 0, 1, 0, 1, 0, 0); - Add(324, 0, 0, 2, 0, 0, 0); - Add(325, 0, 0, 0, 0, 1, 0); - Add(326, 0, 0, 0, 0, 2, 0); - Add(327, 0, 0, 0, 1, 0, 0); - Add(328, 0, 1, 0, 0, 0, 0); - Add(329, 0, 1, 0, 0, 0, 1); - Add(330, 0, 1, 0, 0, 0, 2); - Add(331, 0, 0, 0, 1, 0, 0); - Add(332, 0, 1, 0, 1, 0, 0); - Add(333, 0, 0, 0, 0, 1, 0); - Add(334, 0, 0, 0, 0, 2, 0); - Add(335, 0, 2, 0, 0, 0, 0); - Add(336, 0, 1, 0, 1, 0, 0); - Add(337, 0, 0, 0, 2, 0, 0); - Add(338, 0, 2, 0, 0, 0, 0); - Add(339, 1, 0, 0, 0, 0, 0); - Add(340, 2, 0, 0, 0, 0, 0); - Add(341, 0, 1, 0, 0, 0, 0); - Add(342, 0, 2, 0, 0, 0, 0); - Add(343, 0, 0, 0, 0, 1, 0); - Add(344, 0, 0, 0, 0, 2, 0); - Add(345, 0, 0, 0, 0, 1, 0); - Add(346, 0, 0, 0, 0, 2, 0); - Add(347, 0, 1, 0, 0, 0, 0); - Add(348, 0, 2, 0, 0, 0, 0); - Add(349, 0, 0, 0, 0, 0, 1); - Add(350, 0, 0, 0, 0, 2, 0); - Add(351, 1, 0, 0, 0, 0, 0); - Add(352, 0, 0, 0, 0, 1, 0); - Add(353, 0, 1, 0, 0, 0, 0); - Add(354, 0, 2, 0, 0, 0, 0); - Add(355, 0, 0, 0, 0, 1, 0); - Add(356, 0, 0, 1, 0, 1, 0); - Add(357, 2, 0, 0, 0, 0, 0); - Add(358, 0, 0, 0, 1, 1, 0); - Add(359, 0, 2, 0, 0, 0, 0); - Add(360, 1, 0, 0, 0, 0, 0); - Add(361, 1, 0, 0, 0, 0, 0); - Add(362, 2, 0, 0, 0, 0, 0); - Add(363, 1, 0, 0, 0, 0, 0); - Add(364, 2, 0, 0, 0, 0, 0); - Add(365, 3, 0, 0, 0, 0, 0); - Add(366, 0, 0, 1, 0, 0, 0); - Add(367, 0, 1, 1, 0, 0, 0); - Add(368, 0, 0, 0, 2, 0, 0); - Add(369, 1, 0, 1, 0, 0, 0); - Add(370, 0, 0, 0, 0, 0, 1); - Add(371, 0, 1, 0, 0, 0, 0); - Add(372, 0, 0, 2, 0, 0, 0); - Add(373, 0, 3, 0, 0, 0, 0); - Add(374, 0, 0, 1, 0, 0, 0); - Add(375, 0, 0, 2, 0, 0, 0); - Add(376, 0, 0, 3, 0, 0, 0); - Add(377, 0, 0, 3, 0, 0, 0); - Add(378, 0, 0, 0, 0, 3, 0); - Add(379, 0, 0, 2, 0, 1, 0); - Add(380, 0, 0, 0, 0, 3, 0); - Add(381, 0, 0, 0, 3, 0, 0); - Add(382, 0, 0, 0, 3, 0, 0); - Add(383, 0, 3, 0, 0, 0, 0); - Add(384, 0, 2, 0, 1, 0, 0); - Add(385, 3, 0, 0, 0, 0, 0); - Add(386, 0, 2, 0, 1, 0, 0); - Add(386, 0, 1, 0, 0, 1, 1); - Add(386, 0, 0, 2, 0, 1, 0); - Add(386, 0, 0, 0, 0, 0, 3); - Add(387, 0, 1, 0, 0, 0, 0); - Add(388, 0, 1, 1, 0, 0, 0); - Add(389, 0, 2, 1, 0, 0, 0); - Add(390, 0, 0, 0, 0, 0, 1); - Add(391, 0, 0, 0, 1, 0, 1); - Add(392, 0, 1, 0, 1, 0, 1); - Add(393, 0, 0, 0, 1, 0, 0); - Add(394, 0, 0, 0, 2, 0, 0); - Add(395, 0, 0, 0, 3, 0, 0); - Add(396, 0, 0, 0, 0, 0, 1); - Add(397, 0, 0, 0, 0, 0, 2); - Add(398, 0, 3, 0, 0, 0, 0); - Add(399, 1, 0, 0, 0, 0, 0); - Add(400, 0, 2, 0, 0, 0, 0); - Add(401, 0, 0, 1, 0, 0, 0); - Add(402, 0, 2, 0, 0, 0, 0); - Add(403, 0, 1, 0, 0, 0, 0); - Add(404, 0, 2, 0, 0, 0, 0); - Add(405, 0, 3, 0, 0, 0, 0); - Add(406, 0, 0, 0, 1, 0, 0); - Add(407, 0, 0, 0, 3, 0, 0); - Add(408, 0, 1, 0, 0, 0, 0); - Add(409, 0, 2, 0, 0, 0, 0); - Add(410, 0, 0, 1, 0, 0, 0); - Add(411, 0, 0, 2, 0, 0, 0); - Add(412, 0, 0, 0, 0, 1, 0); - Add(413, 0, 0, 0, 0, 2, 0); - Add(413, 0, 0, 2, 0, 0, 0); - Add(413, 0, 0, 1, 0, 1, 0); - Add(414, 0, 1, 0, 1, 0, 0); - Add(415, 0, 0, 0, 0, 0, 1); - Add(416, 0, 0, 1, 0, 1, 0); - Add(417, 0, 0, 0, 0, 0, 1); - Add(418, 0, 0, 0, 0, 0, 1); - Add(419, 0, 0, 0, 0, 0, 2); - Add(420, 0, 0, 0, 1, 0, 0); - Add(421, 0, 0, 0, 2, 0, 0); - Add(422, 1, 0, 0, 0, 0, 0); - Add(423, 2, 0, 0, 0, 0, 0); - Add(424, 0, 0, 0, 0, 0, 2); - Add(425, 1, 0, 0, 0, 0, 0); - Add(426, 2, 0, 0, 0, 0, 0); - Add(427, 0, 0, 0, 0, 0, 1); - Add(428, 0, 0, 0, 0, 0, 2); - Add(429, 0, 0, 0, 1, 1, 0); - Add(430, 0, 2, 0, 0, 0, 0); - Add(431, 0, 0, 0, 0, 0, 1); - Add(432, 0, 0, 0, 0, 0, 2); - Add(433, 0, 0, 0, 1, 0, 0); - Add(434, 0, 0, 0, 0, 0, 1); - Add(435, 2, 0, 0, 0, 0, 0); - Add(436, 0, 0, 1, 0, 0, 0); - Add(437, 0, 0, 1, 0, 1, 0); - Add(438, 0, 0, 1, 0, 0, 0); - Add(439, 0, 0, 0, 0, 1, 0); - Add(440, 1, 0, 0, 0, 0, 0); - Add(441, 0, 1, 0, 0, 0, 0); - Add(442, 0, 0, 1, 0, 1, 0); - Add(443, 0, 1, 0, 0, 0, 0); - Add(444, 0, 2, 0, 0, 0, 0); - Add(445, 0, 3, 0, 0, 0, 0); - Add(446, 1, 0, 0, 0, 0, 0); - Add(447, 0, 1, 0, 0, 0, 0); - Add(448, 0, 1, 0, 1, 0, 0); - Add(449, 0, 0, 1, 0, 0, 0); - Add(450, 0, 0, 2, 0, 0, 0); - Add(451, 0, 0, 1, 0, 0, 0); - Add(452, 0, 0, 2, 0, 0, 0); - Add(453, 0, 1, 0, 0, 0, 0); - Add(454, 0, 2, 0, 0, 0, 0); - Add(455, 0, 2, 0, 0, 0, 0); - Add(456, 0, 0, 0, 0, 0, 1); - Add(457, 0, 0, 0, 0, 0, 2); - Add(458, 0, 0, 0, 0, 1, 0); - Add(459, 0, 1, 0, 0, 0, 0); - Add(460, 0, 1, 0, 1, 0, 0); - Add(461, 0, 1, 0, 0, 0, 1); - Add(462, 0, 0, 0, 3, 0, 0); - Add(463, 3, 0, 0, 0, 0, 0); - Add(464, 0, 3, 0, 0, 0, 0); - Add(465, 0, 0, 2, 0, 0, 0); - Add(466, 0, 3, 0, 0, 0, 0); - Add(467, 0, 0, 0, 3, 0, 0); - Add(468, 0, 0, 0, 2, 1, 0); - Add(469, 0, 2, 0, 0, 0, 0); - Add(470, 0, 0, 2, 0, 0, 0); - Add(471, 0, 0, 0, 2, 0, 0); - Add(472, 0, 0, 2, 0, 0, 0); - Add(473, 0, 3, 0, 0, 0, 0); - Add(474, 0, 0, 0, 3, 0, 0); - Add(475, 0, 3, 0, 0, 0, 0); - Add(476, 0, 0, 1, 0, 2, 0); - Add(477, 0, 0, 1, 0, 2, 0); - Add(478, 0, 0, 0, 0, 0, 2); - Add(479, 0, 0, 0, 1, 0, 1); - Add(480, 0, 0, 2, 0, 1, 0); - Add(481, 0, 1, 0, 1, 1, 0); - Add(482, 0, 2, 0, 1, 0, 0); - Add(483, 0, 0, 0, 3, 0, 0); - Add(484, 0, 0, 0, 3, 0, 0); - Add(485, 0, 0, 0, 3, 0, 0); - Add(486, 0, 3, 0, 0, 0, 0); - Add(487, 3, 0, 0, 0, 0, 0); - Add(488, 0, 0, 0, 0, 3, 0); - Add(489, 1, 0, 0, 0, 0, 0); - Add(490, 3, 0, 0, 0, 0, 0); - Add(491, 0, 0, 0, 2, 0, 1); - Add(492, 3, 0, 0, 0, 0, 0); - Add(492, 0, 0, 0, 0, 0, 3); - Add(493, 3, 0, 0, 0, 0, 0); - Add(494, 3, 0, 0, 0, 0, 0); - Add(495, 0, 0, 0, 0, 0, 1); - Add(496, 0, 0, 0, 0, 0, 2); - Add(497, 0, 0, 0, 0, 0, 3); - Add(498, 1, 0, 0, 0, 0, 0); - Add(499, 0, 2, 0, 0, 0, 0); - Add(500, 0, 3, 0, 0, 0, 0); - Add(501, 0, 0, 0, 1, 0, 0); - Add(502, 0, 0, 0, 2, 0, 0); - Add(503, 0, 0, 0, 3, 0, 0); - Add(504, 0, 1, 0, 0, 0, 0); - Add(505, 0, 1, 0, 0, 0, 0); - Add(506, 0, 1, 0, 0, 0, 0); - Add(507, 0, 2, 0, 0, 0, 0); - Add(508, 0, 3, 0, 0, 0, 0); - Add(509, 0, 0, 0, 0, 0, 1); - Add(510, 0, 0, 0, 0, 0, 2); - Add(511, 0, 0, 0, 0, 0, 1); - Add(512, 0, 0, 0, 0, 0, 2); - Add(513, 0, 0, 0, 0, 0, 1); - Add(514, 0, 0, 0, 0, 0, 2); - Add(515, 0, 0, 0, 0, 0, 1); - Add(516, 0, 0, 0, 0, 0, 2); - Add(517, 1, 0, 0, 0, 0, 0); - Add(518, 2, 0, 0, 0, 0, 0); - Add(519, 0, 1, 0, 0, 0, 0); - Add(520, 0, 2, 0, 0, 0, 0); - Add(521, 0, 3, 0, 0, 0, 0); - Add(522, 0, 0, 0, 0, 0, 1); - Add(523, 0, 0, 0, 0, 0, 2); - Add(524, 0, 0, 1, 0, 0, 0); - Add(525, 0, 1, 1, 0, 0, 0); - Add(526, 0, 3, 0, 0, 0, 0); - Add(527, 0, 0, 0, 0, 0, 1); - Add(528, 0, 0, 0, 0, 0, 2); - Add(529, 0, 1, 0, 0, 0, 0); - Add(530, 0, 2, 0, 0, 0, 0); - Add(531, 2, 0, 0, 0, 0, 0); - Add(532, 0, 1, 0, 0, 0, 0); - Add(533, 0, 2, 0, 0, 0, 0); - Add(534, 0, 3, 0, 0, 0, 0); - Add(535, 0, 0, 0, 0, 0, 1); - Add(536, 2, 0, 0, 0, 0, 0); - Add(537, 3, 0, 0, 0, 0, 0); - Add(538, 2, 0, 0, 0, 0, 0); - Add(539, 0, 2, 0, 0, 0, 0); - Add(540, 0, 0, 1, 0, 0, 0); - Add(541, 0, 0, 2, 0, 0, 0); - Add(542, 0, 3, 0, 0, 0, 0); - Add(543, 0, 0, 1, 0, 0, 0); - Add(544, 0, 0, 2, 0, 0, 0); - Add(545, 0, 0, 0, 0, 0, 3); - Add(546, 0, 0, 0, 0, 0, 1); - Add(547, 0, 0, 0, 0, 0, 2); - Add(548, 0, 0, 0, 1, 0, 0); - Add(549, 0, 0, 0, 2, 0, 0); - Add(550, 0, 0, 0, 0, 0, 2); - Add(551, 0, 1, 0, 0, 0, 0); - Add(552, 0, 2, 0, 0, 0, 0); - Add(553, 0, 3, 0, 0, 0, 0); - Add(554, 0, 1, 0, 0, 0, 0); - Add(555, 0, 2, 0, 0, 0, 0); - Add(555, 0, 0, 0, 2, 0, 0); - Add(556, 0, 0, 0, 2, 0, 0); - Add(557, 0, 0, 1, 0, 0, 0); - Add(558, 0, 0, 2, 0, 0, 0); - Add(559, 0, 1, 0, 0, 0, 0); - Add(560, 0, 0, 1, 0, 1, 0); - Add(561, 0, 0, 0, 2, 0, 0); - Add(562, 0, 0, 1, 0, 0, 0); - Add(563, 0, 0, 2, 0, 0, 0); - Add(564, 0, 0, 1, 0, 0, 0); - Add(565, 0, 0, 2, 0, 0, 0); - Add(566, 0, 1, 0, 0, 0, 0); - Add(567, 0, 2, 0, 0, 0, 0); - Add(568, 0, 0, 0, 0, 0, 1); - Add(569, 0, 2, 0, 0, 0, 0); - Add(570, 0, 0, 0, 1, 0, 0); - Add(571, 0, 0, 0, 2, 0, 0); - Add(572, 0, 0, 0, 0, 0, 1); - Add(573, 0, 0, 0, 0, 0, 2); - Add(574, 0, 0, 0, 0, 1, 0); - Add(575, 0, 0, 0, 0, 2, 0); - Add(576, 0, 0, 0, 0, 3, 0); - Add(577, 0, 0, 0, 1, 0, 0); - Add(578, 0, 0, 0, 2, 0, 0); - Add(579, 0, 0, 0, 3, 0, 0); - Add(580, 1, 0, 0, 0, 0, 0); - Add(581, 0, 0, 0, 0, 0, 2); - Add(582, 0, 0, 0, 1, 0, 0); - Add(583, 0, 0, 0, 2, 0, 0); - Add(584, 0, 0, 0, 3, 0, 0); - Add(585, 0, 0, 0, 0, 0, 1); - Add(586, 0, 2, 0, 0, 0, 0); - Add(587, 0, 0, 0, 0, 0, 2); - Add(588, 0, 1, 0, 0, 0, 0); - Add(589, 0, 2, 0, 0, 0, 0); - Add(590, 1, 0, 0, 0, 0, 0); - Add(591, 2, 0, 0, 0, 0, 0); - Add(592, 0, 0, 0, 0, 1, 0); - Add(593, 0, 0, 0, 0, 2, 0); - Add(594, 2, 0, 0, 0, 0, 0); - Add(595, 0, 0, 0, 0, 0, 1); - Add(596, 0, 0, 0, 0, 0, 2); - Add(597, 0, 0, 1, 0, 0, 0); - Add(598, 0, 0, 2, 0, 0, 0); - Add(599, 0, 0, 1, 0, 0, 0); - Add(600, 0, 0, 2, 0, 0, 0); - Add(601, 0, 0, 3, 0, 0, 0); - Add(602, 0, 0, 0, 0, 0, 1); - Add(603, 0, 2, 0, 0, 0, 0); - Add(604, 0, 3, 0, 0, 0, 0); - Add(605, 0, 0, 0, 1, 0, 0); - Add(606, 0, 0, 0, 2, 0, 0); - Add(607, 0, 0, 0, 1, 0, 0); - Add(608, 0, 0, 0, 2, 0, 0); - Add(609, 0, 0, 0, 3, 0, 0); - Add(610, 0, 1, 0, 0, 0, 0); - Add(611, 0, 2, 0, 0, 0, 0); - Add(612, 0, 3, 0, 0, 0, 0); - Add(613, 0, 1, 0, 0, 0, 0); - Add(614, 0, 2, 0, 0, 0, 0); - Add(615, 0, 0, 0, 0, 2, 0); - Add(616, 0, 0, 1, 0, 0, 0); - Add(617, 0, 0, 0, 0, 0, 2); - Add(618, 2, 0, 0, 0, 0, 0); - Add(619, 0, 1, 0, 0, 0, 0); - Add(620, 0, 2, 0, 0, 0, 0); - Add(621, 0, 2, 0, 0, 0, 0); - Add(622, 0, 1, 0, 0, 0, 0); - Add(623, 0, 2, 0, 0, 0, 0); - Add(624, 0, 1, 0, 0, 0, 0); - Add(625, 0, 2, 0, 0, 0, 0); - Add(626, 0, 2, 0, 0, 0, 0); - Add(627, 0, 1, 0, 0, 0, 0); - Add(628, 0, 2, 0, 0, 0, 0); - Add(629, 0, 0, 1, 0, 0, 0); - Add(630, 0, 0, 0, 2, 0, 0); - Add(631, 0, 0, 0, 2, 0, 0); - Add(632, 0, 0, 2, 0, 0, 0); - Add(633, 0, 1, 0, 0, 0, 0); - Add(634, 0, 2, 0, 0, 0, 0); - Add(635, 0, 0, 0, 3, 0, 0); - Add(636, 0, 1, 0, 0, 0, 0); - Add(637, 0, 0, 0, 3, 0, 0); - Add(638, 0, 0, 3, 0, 0, 0); - Add(639, 0, 3, 0, 0, 0, 0); - Add(640, 0, 0, 0, 0, 3, 0); - Add(641, 0, 3, 0, 0, 0, 0); - Add(641, 0, 0, 0, 0, 0, 3); - Add(642, 0, 3, 0, 0, 0, 0); - Add(642, 0, 0, 0, 3, 0, 0); - Add(643, 0, 0, 0, 3, 0, 0); - Add(644, 0, 3, 0, 0, 0, 0); - Add(645, 0, 3, 0, 0, 0, 0); - Add(645, 0, 0, 0, 3, 0, 0); - Add(646, 0, 3, 0, 0, 0, 0); - Add(646, 1, 1, 0, 1, 0, 0); - Add(646, 0, 0, 0, 3, 0, 0); - Add(647, 0, 0, 0, 1, 1, 1); - Add(648, 0, 1, 1, 0, 0, 1); - Add(648, 0, 0, 0, 1, 1, 1); - Add(649, 0, 1, 0, 1, 0, 1); - Add(650, 0, 0, 1, 0, 0, 0); - Add(651, 0, 0, 2, 0, 0, 0); - Add(652, 0, 0, 3, 0, 0, 0); - Add(653, 0, 0, 0, 1, 0, 0); - Add(654, 0, 0, 0, 2, 0, 0); - Add(655, 0, 0, 0, 3, 0, 0); - Add(656, 0, 0, 0, 0, 0, 1); - Add(657, 0, 0, 0, 0, 0, 2); - Add(658, 0, 0, 0, 0, 0, 3); - Add(659, 0, 0, 0, 0, 0, 1); - Add(660, 2, 0, 0, 0, 0, 0); - Add(661, 0, 0, 0, 0, 0, 1); - Add(662, 0, 0, 0, 0, 0, 2); - Add(663, 0, 0, 0, 0, 0, 3); - Add(664, 0, 0, 1, 0, 0, 0); - Add(665, 0, 0, 2, 0, 0, 0); - Add(666, 1, 0, 0, 1, 0, 1); - Add(667, 0, 0, 0, 1, 0, 0); - Add(668, 0, 0, 0, 2, 0, 0); - Add(669, 0, 0, 0, 0, 1, 0); - Add(670, 0, 0, 0, 0, 2, 0); - Add(671, 0, 0, 0, 0, 3, 0); - Add(672, 1, 0, 0, 0, 0, 0); - Add(673, 2, 0, 0, 0, 0, 0); - Add(674, 0, 1, 0, 0, 0, 0); - Add(675, 0, 2, 0, 0, 0, 0); - Add(676, 0, 0, 0, 0, 0, 1); - Add(677, 0, 0, 0, 0, 0, 1); - Add(678, 0, 0, 0, 0, 0, 2); - Add(679, 0, 0, 1, 0, 0, 0); - Add(680, 0, 0, 2, 0, 0, 0); - Add(681, 0, 2, 0, 1, 0, 0); - Add(681, 0, 0, 2, 0, 1, 0); - Add(682, 1, 0, 0, 0, 0, 0); - Add(683, 2, 0, 0, 0, 0, 0); - Add(684, 0, 0, 1, 0, 0, 0); - Add(685, 0, 0, 2, 0, 0, 0); - Add(686, 0, 1, 0, 0, 0, 0); - Add(687, 0, 2, 0, 0, 0, 0); - Add(688, 0, 1, 0, 0, 0, 0); - Add(689, 0, 2, 0, 0, 0, 0); - Add(690, 0, 0, 0, 0, 1, 0); - Add(691, 0, 0, 0, 0, 2, 0); - Add(692, 0, 0, 0, 1, 0, 0); - Add(693, 0, 0, 0, 2, 0, 0); - Add(694, 0, 0, 0, 0, 0, 1); - Add(695, 0, 0, 0, 0, 0, 2); - Add(696, 0, 1, 0, 0, 0, 0); - Add(697, 0, 2, 0, 0, 0, 0); - Add(698, 1, 0, 0, 0, 0, 0); - Add(699, 2, 0, 0, 0, 0, 0); - Add(700, 0, 0, 0, 0, 2, 0); - Add(701, 0, 2, 0, 0, 0, 0); - Add(702, 0, 0, 0, 0, 0, 2); - Add(703, 0, 0, 1, 0, 1, 0); - Add(704, 0, 0, 0, 0, 1, 0); - Add(705, 0, 0, 0, 0, 2, 0); - Add(706, 0, 0, 0, 0, 3, 0); - Add(707, 0, 0, 1, 0, 0, 0); - Add(708, 0, 1, 0, 0, 0, 0); - Add(709, 0, 2, 0, 0, 0, 0); - Add(710, 0, 0, 1, 0, 0, 0); - Add(711, 0, 0, 2, 0, 0, 0); - Add(712, 0, 0, 1, 0, 0, 0); - Add(713, 0, 0, 2, 0, 0, 0); - Add(714, 0, 0, 0, 0, 0, 1); - Add(715, 0, 0, 0, 0, 0, 2); - Add(716, 3, 0, 0, 0, 0, 0); - Add(717, 3, 0, 0, 0, 0, 0); - Add(718, 3, 0, 0, 0, 0, 0); - Add(719, 3, 0, 0, 0, 0, 0); - Add(720, 3, 0, 0, 0, 0, 0); - Add(721, 0, 0, 3, 0, 0, 0); - Add(722, 1, 0, 0, 0, 0, 0); - Add(723, 2, 0, 0, 0, 0, 0); - Add(724, 0, 3, 0, 0, 0, 0); - Add(725, 0, 0, 0, 0, 0, 1); - Add(726, 0, 0, 0, 0, 0, 2); - Add(727, 0, 3, 0, 0, 0, 0); - Add(728, 0, 0, 0, 1, 0, 0); - Add(729, 0, 0, 0, 2, 0, 0); - Add(730, 0, 0, 0, 3, 0, 0); - Add(731, 0, 1, 0, 0, 0, 0); - Add(732, 0, 2, 0, 0, 0, 0); - Add(733, 0, 3, 0, 0, 0, 0); - Add(734, 0, 1, 0, 0, 0, 0); - Add(735, 0, 2, 0, 0, 0, 0); - Add(736, 0, 1, 0, 0, 0, 0); - Add(737, 0, 0, 2, 0, 0, 0); - Add(738, 0, 0, 0, 3, 0, 0); - Add(739, 0, 1, 0, 0, 0, 0); - Add(740, 0, 2, 0, 0, 0, 0); - Add(741, 0, 0, 0, 2, 0, 0); - Add(742, 0, 0, 0, 0, 0, 1); - Add(743, 0, 0, 0, 0, 0, 2); - Add(744, 0, 1, 0, 0, 0, 0); - Add(745, 1, 0, 0, 0, 0, 0); - Add(746, 1, 0, 0, 0, 0, 0); - Add(747, 0, 0, 1, 0, 0, 0); - Add(748, 0, 0, 2, 0, 0, 0); - Add(749, 0, 1, 0, 0, 0, 0); - Add(750, 0, 2, 0, 0, 0, 0); - Add(751, 0, 0, 0, 0, 1, 0); - Add(752, 0, 0, 0, 0, 2, 0); - Add(753, 0, 1, 0, 0, 0, 0); - Add(754, 0, 2, 0, 0, 0, 0); - Add(755, 0, 0, 0, 0, 1, 0); - Add(756, 0, 0, 0, 0, 2, 0); - Add(757, 0, 0, 0, 0, 0, 0); - Add(758, 0, 0, 0, 0, 0, 0); - Add(759, 0, 1, 0, 0, 0, 0); - Add(760, 0, 2, 0, 0, 0, 0); - Add(762, 1, 0, 0, 0, 0, 0); - Add(763, 0, 3, 0, 0, 0, 0); - Add(764, 0, 0, 0, 0, 2, 0); - Add(765, 0, 0, 0, 0, 2, 0); - Add(766, 0, 2, 0, 0, 0, 0); - Add(767, 0, 0, 0, 0, 0, 1); - Add(768, 0, 0, 2, 0, 0, 0); - Add(769, 0, 0, 1, 0, 0, 0); - Add(770, 0, 0, 2, 0, 0, 0); - Add(771, 0, 0, 0, 0, 2, 0); - Add(772, 2, 0, 0, 0, 0, 0); - Add(773, 3, 0, 0, 0, 0, 0); - Add(774, 0, 0, 1, 0, 1, 0); - Add(775, 0, 2, 0, 0, 0, 0); - Add(776, 0, 0, 2, 0, 0, 0); - Add(777, 0, 2, 0, 0, 0, 0); - Add(778, 0, 0, 0, 0, 2, 0); - Add(779, 0, 2, 0, 0, 0, 0); - Add(780, 0, 0, 0, 2, 0, 0); - Add(781, 0, 2, 0, 0, 0, 0); - Add(782, 0, 0, 1, 0, 0, 0); - Add(783, 0, 0, 2, 0, 0, 0); - Add(784, 0, 0, 3, 0, 0, 0); - Add(785, 0, 0, 0, 0, 0, 3); - Add(786, 0, 0, 0, 3, 0, 0); - Add(787, 0, 3, 0, 0, 0, 0); - Add(788, 0, 0, 0, 0, 3, 0); - Add(789, 1, 0, 0, 0, 0, 0); - Add(790, 0, 0, 1, 0, 0, 0); - Add(791, 0, 3, 0, 0, 0, 0); - Add(792, 0, 0, 0, 3, 0, 0); - Add(793, 0, 0, 0, 0, 3, 0); - Add(794, 0, 1, 2, 0, 0, 0); - Add(795, 0, 0, 0, 0, 0, 3); - Add(796, 0, 0, 0, 3, 0, 0); - Add(797, 0, 1, 1, 1, 0, 0); - Add(798, 0, 3, 0, 0, 0, 0); - Add(799, 3, 0, 0, 0, 0, 0); - Add(800, 1, 0, 2, 0, 0, 0); - Add(801, 0, 0, 0, 3, 0, 0); - Add(802, 0, 2, 0, 0, 0, 0); - Add(803, 0, 0, 0, 0, 0, 1); - Add(804, 0, 0, 0, 3, 0, 0); - Add(805, 0, 0, 3, 0, 0, 0); - Add(806, 0, 0, 0, 3, 0, 0); - Add(807, 0, 0, 0, 0, 0, 3); + foreach (var entry in Pokedex.Instance.Entries) + { + Add(entry.ID, entry.EVHP, entry.EVATK, entry.EVDEF, entry.EVSPATK, entry.EVSPDEF, entry.EVSPD); + } } private void Add(int id, int hp, int atk, int def, int spatk, int spdef, int speed) diff --git a/PROProtocol/Encryption.cs b/PROProtocol/Encryption.cs new file mode 100644 index 0000000..1e8803f --- /dev/null +++ b/PROProtocol/Encryption.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PROProtocol +{ + public static class Encryption + { + private static readonly byte[] DEFAULT_SEND_KEY = { 0x72, 0xEA, 0xB1, 0xC0, 0xD5, 0xFC, 0x17, 0x85, 0x7E, 0x29, 0x18, 0x81, 0x3C, 0x21, 0xCC, 0xAE, 0xD9, 0x4B, 0x31, 0x0E, 0x27, 0xA4, 0x20, 0x6E, 0x4E, 0xB3, 0xA8, 0xB5, 0x6A, 0xDB, 0x1B, 0x70, 0x12, 0x41, 0x90, 0xA7, 0xCE, 0xBD, 0xB8, 0x61, 0x26, 0x6D, 0x7F, 0xAF, 0x3F, 0xCA, 0xBF, 0x05, 0xDA, 0x9B, 0x4F, 0xEC, 0xAB, 0x52, 0xB7, 0x75, 0xB0, 0x97, 0xBB, 0x19, 0xA5, 0xB9, 0xDF, 0x1A, 0x5F, 0xF4, 0xE1, 0xB4, 0x49, 0xEB, 0x2D, 0xF2, 0xE6, 0x9E, 0xD4, 0x07, 0x5E, 0x3D, 0x04, 0x7A, 0xD3, 0x45, 0x36, 0x11, 0x42, 0xD0, 0x94, 0x82, 0x83, 0xEE, 0xFF, 0x22, 0xA9, 0x5C, 0x6B, 0x84, 0xDD, 0x68, 0xF7, 0xC8, 0x65, 0x35, 0x60, 0x53, 0x71, 0x8D, 0xBC, 0xB2, 0x9D, 0xFD, 0x77, 0xEF, 0xFB, 0x9F, 0x1D, 0x23, 0xE8, 0x30, 0x91, 0x67, 0xF8, 0x87, 0x1F, 0xAD, 0x13, 0xF3, 0x7D, 0x01, 0x7C, 0x33, 0x8B, 0x0C, 0x63, 0x89, 0xE5, 0x3A, 0xF5, 0x8C, 0xBA, 0x00, 0xC4, 0x0F, 0xFA, 0x47, 0x74, 0x92, 0x3B, 0x1E, 0xC9, 0x32, 0x88, 0x6C, 0x06, 0x0D, 0xA3, 0xD8, 0x08, 0xDC, 0x44, 0x78, 0x09, 0xC7, 0xF1, 0x51, 0xC2, 0x76, 0x2A, 0xD2, 0x56, 0x73, 0xDE, 0x99, 0xA1, 0xB6, 0x0A, 0x5D, 0x79, 0x2C, 0xE0, 0x7B, 0xE2, 0x02, 0x43, 0x80, 0x2E, 0xD1, 0x48, 0x8A, 0x4A, 0xE4, 0x86, 0xA6, 0x62, 0x28, 0x03, 0x69, 0x6F, 0x39, 0xAA, 0x0B, 0x96, 0x9C, 0xED, 0x57, 0x50, 0xF6, 0x3E, 0x64, 0xA0, 0x9A, 0xC6, 0xBE, 0xE9, 0x10, 0x16, 0x15, 0x93, 0xF9, 0x37, 0x2B, 0xA2, 0x98, 0x58, 0x5B, 0x55, 0xE7, 0x8F, 0x66, 0xD6, 0xC5, 0x2F, 0xCD, 0xC3, 0x54, 0x4C, 0xAC, 0x4D, 0xFE, 0x46, 0x59, 0xCB, 0xCF, 0x95, 0x34, 0x5A, 0x25, 0x14, 0x40, 0xE3, 0xF0, 0xD7, 0x1C, 0x38, 0x8E, 0xC1, 0x24 }; + + private static readonly byte[] DEFAULT_RECV_KEY = { 0x29, 0xB4, 0x15, 0xD1, 0x04, 0xA0, 0xF3, 0x0A, 0xF9, 0x99, 0x4B, 0x28, 0xC3, 0x64, 0x7A, 0x01, 0xB7, 0xAE, 0x65, 0x7F, 0xB5, 0x72, 0x87, 0x9B, 0x8C, 0x61, 0xD4, 0x3D, 0xE1, 0x91, 0xF1, 0xC3, 0x48, 0xAF, 0x38, 0x74, 0x36, 0xC2, 0xA9, 0x76, 0xF7, 0xC9, 0x51, 0xDA, 0x5B, 0x0B, 0x14, 0x82, 0x71, 0x5A, 0x7F, 0xC5, 0x7B, 0xDA, 0xD9, 0xF0, 0x17, 0x5E, 0x6F, 0x54, 0xF7, 0x7B, 0x54, 0xB0, 0xC2, 0x8B, 0xCA, 0xB6, 0xB2, 0x05, 0xE9, 0x75, 0x30, 0xB1, 0xE7, 0xCE, 0x59, 0x8F, 0x93, 0x16, 0xA5, 0xC8, 0x87, 0x74, 0x01, 0x22, 0xA5, 0x0B, 0xA5, 0x44, 0x9D, 0x79, 0x8A, 0x39, 0x00, 0x14, 0x29, 0x76, 0xD8, 0x59, 0xDD, 0x5D, 0x59, 0xBF, 0x6C, 0x1A, 0x51, 0xE6, 0xC1, 0x9E, 0xBC, 0x31, 0xB9, 0x8D, 0xBE, 0xC9, 0x53, 0xE5, 0x52, 0x28, 0x39, 0xA6, 0x2F, 0xAF, 0x20, 0x67, 0x9E, 0x4F, 0xD6, 0x6C, 0x70, 0xC5, 0x28, 0x0D, 0xE6, 0x4B, 0x10, 0x02, 0x28, 0xA7, 0xE1, 0x73, 0x2B, 0xA8, 0x8E, 0x0E, 0xC4, 0xE9, 0x0D, 0xB6, 0xA4, 0x30, 0xB1, 0x5A, 0xAC, 0xDB, 0xEC, 0x4A, 0x83, 0x64, 0x92, 0xCD, 0x5D, 0xBE, 0xA3, 0xC2, 0xBD, 0xC2, 0xCC, 0xE6, 0x1D, 0x5F, 0x1F, 0x70, 0x9D, 0xA3, 0x4C, 0x9E, 0x47, 0xE2, 0xAC, 0x19, 0x3B, 0xA3, 0x9D, 0x71, 0x46, 0x43, 0xD4, 0x24, 0x37, 0xFF, 0x1F, 0x70, 0xBF, 0x73, 0x19, 0x3D, 0xB6, 0x06, 0xF1, 0xD3, 0x0B, 0xB9, 0xB5, 0x8F, 0x4E, 0xBD, 0x6E, 0x9B, 0xE8, 0xA5, 0xE4, 0x56, 0xD6, 0xFD, 0xE2, 0x20, 0x51, 0xEF, 0x5A, 0x15, 0xC1, 0xFA, 0xFD, 0x13, 0x93, 0xC3, 0xE3, 0x32, 0xD9, 0x8C, 0xF3, 0x29, 0xC3, 0x9C, 0x9A, 0xAB, 0xD9, 0xDA, 0x61, 0xBB, 0xA6, 0xBD, 0xB2, 0x60, 0x59, 0x28, 0x77, 0x23, 0x5E, 0x73, 0x72, 0xD1, 0x5F, 0x4C }; + + private class State + { + public State(byte[] box) + { + _box = new byte[box.Length]; + Array.Copy(box, _box, box.Length); + + _i = 0; + _j = 0; + } + + public byte[] Crypt(IReadOnlyList bytes, int len) + { + byte[] new_bytes = new byte[len]; + var data = bytes.Take(len).ToArray(); + + for (int i = 0; i < len; i++) + { + _i = (_i + 1) % 256; + _j = (_j + _box[_i]) % 256; + + byte temp_i = _box[_i]; + + _box[_i] = _box[_j]; + _box[_j] = temp_i; + + new_bytes[i] = (byte)(data[i] ^ _box[(_box[_i] + _box[_j]) % 256]); + } + + return new_bytes; + } + + private readonly byte[] _box; + + private int _i; + private int _j; + } + + private static State recvState; + private static State sendState; + + public static bool StateReady = false; + + public static byte[] Encrypt(byte[] input, int len = -1) + { + return sendState.Crypt(input, len > 0 ? len : input.Length); + } + + public static byte[] Decrypt(byte[] input, int len = -1) + { + return recvState.Crypt(input, len > 0 ? len : input.Length); + } + + public static void Reset() + { + sendState = new State(DEFAULT_SEND_KEY); + recvState = new State(DEFAULT_RECV_KEY); + StateReady = false; + } + + public static string ReplaceSumByte(string id) + { + id = id.Substring(0, 34); + int sum = 0; + for (int i = 0; i < id.Length; i++) + { + sum += id[i]; + } + id += (sum & 0xff).ToString("x2"); // need the last byte + return id; + } + } +} diff --git a/PROProtocol/GameClient.cs b/PROProtocol/GameClient.cs index ea57f19..b8a488d 100644 --- a/PROProtocol/GameClient.cs +++ b/PROProtocol/GameClient.cs @@ -1,2041 +1,2140 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace PROProtocol -{ - public class GameClient - { - public InventoryItem GroundMount; - public InventoryItem WaterMount; - - public Random Rand { get; private set; } - public Language I18n { get; private set; } - - public bool IsConnected { get; private set; } - public bool IsAuthenticated { get; private set; } - public string PlayerName { get; private set; } - - public int PlayerX { get; private set; } - public int PlayerY { get; private set; } - public string MapName { get; private set; } - public Map Map { get; private set; } - - public int PokedexOwned { get; private set; } - public int PokedexSeen { get; private set; } - public int PokedexEvolved { get; private set; } - - public bool IsInBattle { get; private set; } - public bool IsSurfing { get; private set; } - public bool IsBiking { get; private set; } - public bool IsOnGround { get; private set; } - public bool IsPCOpen { get; private set; } - public bool CanUseCut { get; private set; } - public bool CanUseSmashRock { get; private set; } - public bool IsPrivateMessageOn { get; private set; } - - public int Money { get; private set; } - public int Coins { get; private set; } - public bool IsMember { get; private set; } - public List Team { get; private set; } - public List CurrentPCBox { get; private set; } - public List Items { get; private set; } - public string PokemonTime { get; private set; } - public string Weather { get; private set; } - public int PCGreatestUid { get; private set; } - - public bool IsScriptActive { get; private set; } - public string ScriptId { get; private set; } - public int ScriptStatus { get; private set; } - public string[] DialogContent { get; private set; } - - public Battle ActiveBattle { get; private set; } - public Shop OpenedShop { get; private set; } - public MoveRelearner MoveRelearner { get; private set; } - - public List Channels { get; } - public List Conversations { get; } - public Dictionary Players { get; } - private DateTime _updatePlayers; - private DateTime _refreshBoxTimeout; - public bool IsPCBoxRefreshing { get; private set; } - public int CurrentPCBoxId { get; private set; } - - public bool IsCreatingNewCharacter { get; private set; } - - public event Action ConnectionOpened; - public event Action ConnectionFailed; - public event Action ConnectionClosed; - public event Action LoggedIn; - public event Action AuthenticationFailed; - public event Action QueueUpdated; - - public event Action PositionUpdated; - public event Action TeleportationOccuring; - public event Action MapLoaded; - public event Action> NpcReceived; - public event Action PokemonsUpdated; - public event Action InventoryUpdated; - public event Action BattleStarted; - public event Action BattleMessage; - public event Action BattleEnded; - public event Action BattleUpdated; - public event Action DialogOpened; - public event Action EmoteMessage; - public event Action ChatMessage; - public event Action RefreshChannelList; - public event Action ChannelMessage; - public event Action ChannelSystemMessage; - public event Action ChannelPrivateMessage; - public event Action SystemMessage; - public event Action PrivateMessage; - public event Action LeavePrivateMessage; - public event Action PlayerUpdated; - public event Action PlayerAdded; - public event Action PlayerRemoved; - public event Action InvalidPacket; - public event Action LearningMove; - public event Action Evolving; - public event Action PokeTimeUpdated; - public event Action ShopOpened; - public event Action MoveRelearnerOpened; - public event Action> PCBoxUpdated; - public event Action LogMessage; - public event Action ActivePokemonChanged; - public event Action OpponentChanged; - - private const string Version = "Dolphin"; - - private GameConnection _connection; - private DateTime _lastMovement; - private List _movements = new List(); - private Direction? _slidingDirection; - private bool _surfAfterMovement; - private Queue _dialogResponses = new Queue(); - - private Timeout _movementTimeout = new Timeout(); - private Timeout _battleTimeout = new Timeout(); - private Timeout _loadingTimeout = new Timeout(); - private Timeout _mountingTimeout = new Timeout(); - private Timeout _teleportationTimeout = new Timeout(); - private Timeout _dialogTimeout = new Timeout(); - private Timeout _swapTimeout = new Timeout(); - private Timeout _itemUseTimeout = new Timeout(); - private Timeout _fishingTimeout = new Timeout(); - private Timeout _refreshingPCBox = new Timeout(); - private Timeout _moveRelearnerTimeout = new Timeout(); - - private Timeout _npcBattleTimeout = new Timeout(); - private Npc _npcBattler; - - private MapClient _mapClient; - - public void ClearPath() - { - _movements.Clear(); - } - - public bool IsInactive => - _movements.Count == 0 - && !_movementTimeout.IsActive - && !_battleTimeout.IsActive - && !_loadingTimeout.IsActive - && !_mountingTimeout.IsActive - && !_teleportationTimeout.IsActive - && !_dialogTimeout.IsActive - && !_swapTimeout.IsActive - && !_itemUseTimeout.IsActive - && !_fishingTimeout.IsActive - && !_refreshingPCBox.IsActive - && !_npcBattleTimeout.IsActive - && !_moveRelearnerTimeout.IsActive - && !IsCreatingNewCharacter; - - public bool IsTeleporting => _teleportationTimeout.IsActive; - - public GameServer Server => _connection.Server; - - public bool IsMapLoaded => Map != null; - public bool AreNpcReceived { get; private set; } - - public GameClient(GameConnection connection, MapConnection mapConnection) - { - _mapClient = new MapClient(mapConnection); - _mapClient.ConnectionOpened += MapClient_ConnectionOpened; - _mapClient.ConnectionClosed += MapClient_ConnectionClosed; - _mapClient.ConnectionFailed += MapClient_ConnectionFailed; - _mapClient.MapLoaded += MapClient_MapLoaded; - - _connection = connection; - _connection.PacketReceived += OnPacketReceived; - _connection.Connected += OnConnectionOpened; - _connection.Disconnected += OnConnectionClosed; - - Rand = new Random(); - I18n = new Language(); - Team = new List(); - CurrentPCBox = new List(); - Items = new List(); - Channels = new List(); - Conversations = new List(); - Players = new Dictionary(); - PCGreatestUid = -1; - IsPrivateMessageOn = true; - } - - public void Open() - { - _connection.Connect(); - } - - public void Close(Exception error = null) - { - _connection.Close(error); - } - - public void Update() - { - _mapClient.Update(); - _connection.Update(); - if (!IsAuthenticated) - return; - - _movementTimeout.Update(); - _battleTimeout.Update(); - _loadingTimeout.Update(); - _mountingTimeout.Update(); - _teleportationTimeout.Update(); - _dialogTimeout.Update(); - _swapTimeout.Update(); - _itemUseTimeout.Update(); - _fishingTimeout.Update(); - _refreshingPCBox.Update(); - _moveRelearnerTimeout.Update(); - - SendRegularPing(); - UpdateMovement(); - UpdateScript(); - UpdatePlayers(); - UpdatePCBox(); - UpdateNpcBattle(); - } - - public void CloseChannel(string channelName) - { - if (Channels.Any(e => e.Name == channelName)) - { - SendMessage("/cgleave " + channelName); - } - } - - public void CloseConversation(string pmName) - { - if (Conversations.Contains(pmName)) - { - SendMessage("/pm rem " + pmName + "-=-" + PlayerName + '|' + PlayerName); - Conversations.Remove(pmName); - } - } - - private int _pingCurrentStep = 0; - private bool _isPingSwapped = false; - - private void SendRegularPing() - { - if ((DateTime.UtcNow - _lastMovement).TotalSeconds >= 6) - { - _lastMovement = DateTime.UtcNow; - // DSSock.Update - int packetType; - if (_pingCurrentStep == 5) - { - packetType = _isPingSwapped ? 1 : 2; - _pingCurrentStep = 0; - } - else - { - packetType = Rand.Next(2) + 1; - } - if (packetType == 1) - { - _isPingSwapped = !_isPingSwapped; - } - _pingCurrentStep++; - SendPacket(packetType.ToString()); - } - } - - private void UpdateMovement() - { - if (!IsMapLoaded) return; - - if (!_movementTimeout.IsActive && _movements.Count > 0) - { - if (GroundMount != null && !_itemUseTimeout.IsActive && !IsBiking && !IsSurfing && Map.IsOutside) - { - LogMessage?.Invoke($"Mounting [{GroundMount.Name}]"); - UseItem(GroundMount.Id); - return; - } - - Direction direction = _movements[0]; - _movements.RemoveAt(0); - - if (ApplyMovement(direction)) - { - SendMovement(direction.AsChar()); - _movementTimeout.Set(IsBiking ? 125 : 250); - if (Map.HasLink(PlayerX, PlayerY)) - { - _teleportationTimeout.Set(); - } - else - { - Npc battler = Map.Npcs.FirstOrDefault(npc => npc.CanBattle && npc.IsInLineOfSight(PlayerX, PlayerY)); - if (battler != null) - { - battler.CanBattle = false; - LogMessage?.Invoke("The NPC " + (battler.Name ?? battler.Id.ToString()) + " saw us, interacting..."); - _npcBattler = battler; - int distanceFromBattler = DistanceBetween(PlayerX, PlayerY, battler.PositionX, battler.PositionY); - _npcBattleTimeout.Set(Rand.Next(1000, 2000) + distanceFromBattler * 250); - ClearPath(); - } - } - } - - if (_movements.Count == 0 && _surfAfterMovement) - { - _movementTimeout.Set(Rand.Next(750, 2000)); - } - } - if (!_movementTimeout.IsActive && _movements.Count == 0 && _surfAfterMovement) - { - _surfAfterMovement = false; - UseSurf(); - } - } - - private void UpdatePlayers() - { - if (_updatePlayers < DateTime.UtcNow) - { - foreach (string playerName in Players.Keys.ToArray()) - { - if (Players[playerName].IsExpired()) - { - PlayerRemoved?.Invoke(Players[playerName]); - Players.Remove(playerName); - } - } - _updatePlayers = DateTime.UtcNow.AddSeconds(5); - } - } - - private void UpdatePCBox() - { - // if we did not receive an answer, then the box is empty - if (IsPCBoxRefreshing && _refreshBoxTimeout > DateTime.UtcNow) - { - IsPCBoxRefreshing = false; - if (Map.IsPC(PlayerX, PlayerY - 1)) - { - IsPCOpen = true; - } - CurrentPCBox = new List(); - PCBoxUpdated?.Invoke(CurrentPCBox); - } - } - - private void UpdateNpcBattle() - { - if (_npcBattler == null) return; - - _npcBattleTimeout.Update(); - if (_npcBattleTimeout.IsActive) return; - - TalkToNpc(_npcBattler); - _npcBattler = null; - } - - private bool ApplyMovement(Direction direction) - { - int destinationX = PlayerX; - int destinationY = PlayerY; - bool isOnGround = IsOnGround; - bool isSurfing = IsSurfing; - - direction.ApplyToCoordinates(ref destinationX, ref destinationY); - - Map.MoveResult result = Map.CanMove(direction, destinationX, destinationY, isOnGround, isSurfing, CanUseCut, CanUseSmashRock); - if (Map.ApplyMovement(direction, result, ref destinationX, ref destinationY, ref isOnGround, ref isSurfing)) - { - PlayerX = destinationX; - PlayerY = destinationY; - IsOnGround = isOnGround; - IsSurfing = isSurfing; - PositionUpdated?.Invoke(MapName, PlayerX, PlayerY); - - if (result == Map.MoveResult.Icing) - { - _movements.Insert(0, direction); - } - - if (result == Map.MoveResult.Sliding) - { - int slider = Map.GetSlider(destinationX, destinationY); - if (slider != -1) - { - _slidingDirection = Map.SliderToDirection(slider); - } - } - - if (_slidingDirection != null) - { - _movements.Insert(0, _slidingDirection.Value); - } - - return true; - } - - _slidingDirection = null; - return false; - } - - private void UpdateScript() - { - if (IsScriptActive && !_dialogTimeout.IsActive) - { - if (ScriptStatus == 0) - { - _dialogResponses.Clear(); - IsScriptActive = false; - } - else if (ScriptStatus == 1) - { - SendDialogResponse(0); - _dialogTimeout.Set(); - } - else if (ScriptStatus == 3 || ScriptStatus == 4) - { - SendDialogResponse(GetNextDialogResponse()); - _dialogTimeout.Set(); - } - } - } - - private int GetNextDialogResponse() - { - if (_dialogResponses.Count > 0) - { - object response = _dialogResponses.Dequeue(); - if (response is int) - { - return (int)response; - } - else if (response is string) - { - string text = ((string)response).ToUpperInvariant(); - for (int i = 1; i < DialogContent.Length; ++i) - { - if (DialogContent[i].ToUpperInvariant().Equals(text)) - { - return i; - } - } - } - } - return 1; - } - - public int DistanceTo(int cellX, int cellY) - { - return Math.Abs(PlayerX - cellX) + Math.Abs(PlayerY - cellY); - } - - public static int DistanceBetween(int fromX, int fromY, int toX, int toY) - { - return Math.Abs(fromX - toX) + Math.Abs(fromY - toY); - } - - public void SendPacket(string packet) - { -#if DEBUG - Console.WriteLine("[>] " + packet); -#endif - _connection.Send(packet); - } - - public void SendMessage(string text) - { - // DSSock.sendMSG - SendPacket("{|.|" + text); - } - - public void SendPrivateMessage(string nickname, string text) - { - // DSSock.sendMSG - string pmHeader = "/pm " + PlayerName + "-=-" + nickname; - SendPacket("{|.|" + pmHeader + '|' + text); - } - - public void SendStartPrivateMessage(string nickname) - { - SendMessage("/pm " + PlayerName + "-=-" + nickname); - } - - public void SendFriendToggle(string nickname) - { - SendMessage("/friend " + nickname); - } - - public void SendIgnoreToggle(string nickname) - { - SendMessage("/ignore " + nickname); - } - - public void CreateCharacter(int hair, int colour, int tone, int clothe, int eyes) - { - if (!IsCreatingNewCharacter) return; - IsCreatingNewCharacter = false; - SendCreateCharacter(hair, colour, tone, clothe, eyes); - _dialogTimeout.Set(); - } - - private void SendCreateCharacter(int hair, int colour, int tone, int clothe, int eyes) - { - SendMessage("/setchar " + hair + "," + colour + "," + tone + "," + clothe + "," + eyes); - } - - public void SendAuthentication(string username, string password, Guid deviceId) - { - // DSSock.AttemptLogin - SendPacket("+|.|" + username + "|.|" + password + "|.|" + Version + "|.|" + deviceId + "|.|" + "Windows 10 (10.0.0) 64bit"); - // TODO: Add an option to select the OS we want, it could be useful. - // I use Windows 10 here because the version is the same for everyone. This is not the case on Windows 7 or Mac. - } - - public void SendUseItem(int id, int pokemon = 0) - { - string toSend = "*|.|" + id; - if (pokemon != 0) - { - toSend += "|.|" + pokemon; - } - SendPacket(toSend); - } - - public void SendGiveItem(int pokemonUid, int itemId) - { - SendMessage("/giveitem " + pokemonUid + "," + itemId); - } - - public void SendTakeItem(int pokemonUid) - { - SendMessage("/takeitem " + pokemonUid); - } - - public void LearnMove(int pokemonUid, int moveToForgetUid) - { - _swapTimeout.Set(); - SendLearnMove(pokemonUid, moveToForgetUid); - } - - private void SendLearnMove(int pokemonUid, int moveToForgetUid) - { - SendPacket("^|.|" + pokemonUid + "|.|" + moveToForgetUid); - } - - private void SendMovePokemonToPC(int pokemonUid) - { - SendPacket("?|.|" + pokemonUid + "|.|-1"); - } - - // if there is a pokemon in teamSlot, it will be swapped - private void SendMovePokemonFromPC(int pokemonUid, int teamSlot) - { - SendPacket("?|.|" + pokemonUid + "|.|" + teamSlot); - } - - private void SendRefreshPCBox(int box, string search) - { - SendPacket("M|.|" + box + "|.|" + search); - } - - private void SendReleasePokemon(int pokemonUid) - { - SendMessage("/release " + pokemonUid); - } - - private void SendPrivateMessageOn() - { - SendMessage("/pmon"); - } - - private void SendPrivateMessageOff() - { - SendMessage("/pmoff"); - } - - private void SendPrivateMessageAway() - { - SendMessage("/pmaway"); - } - - public bool PrivateMessageOn() - { - IsPrivateMessageOn = true; - SendPrivateMessageOn(); - return true; - } - - public bool PrivateMessageOff() - { - IsPrivateMessageOn = false; - SendPrivateMessageOff(); - return true; - } - - // /pmaway does not seem to do anything - public bool PrivateMessageAway() - { - SendPrivateMessageAway(); - return true; - } - - public bool ReleasePokemonFromPC(int boxId, int boxPokemonId) - { - if (!IsPCOpen || IsPCBoxRefreshing || boxId < 1 || boxId > 67 - || boxPokemonId < 1 || boxPokemonId > 15 || boxPokemonId > CurrentPCBox.Count) - { - return false; - } - int pokemonUid = GetPokemonPCUid(boxId, boxPokemonId); - if (pokemonUid == -1 || pokemonUid != CurrentPCBox[boxPokemonId - 1].Uid) - { - return false; - } - _refreshingPCBox.Set(Rand.Next(1500, 2000)); - SendReleasePokemon(pokemonUid); - return true; - } - - public bool ReleasePokemonFromTeam(int pokemonUid) - { - if (!IsPCOpen || IsPCBoxRefreshing - || pokemonUid < 1 || pokemonUid > 6 || pokemonUid > Team.Count) - { - return false; - } - _refreshingPCBox.Set(Rand.Next(1500, 2000)); - SendReleasePokemon(pokemonUid); - return true; - } - - public bool RefreshPCBox(int boxId) - { - if (!IsPCOpen || boxId < 1 || boxId > 67 || _refreshingPCBox.IsActive || IsPCBoxRefreshing) - { - return false; - } - _refreshingPCBox.Set(Rand.Next(1500, 2000)); // this is the amount of time we wait for an answer - CurrentPCBoxId = boxId; - IsPCBoxRefreshing = true; - CurrentPCBox = null; - _refreshBoxTimeout = DateTime.UtcNow.AddSeconds(5); // this is to avoid a flood of the function - SendRefreshPCBox(boxId - 1, "ID"); - return true; - } - - public bool RefreshCurrentPCBox() - { - return RefreshPCBox(CurrentPCBoxId); - } - - private int GetPokemonPCUid(int box, int id) - { - if (box < 1 || box > 67 || id < 1 || id > 15) - { - return -1; - } - int result = (box - 1) * 15 + 6 + id; - // ensures we cannot access a pokemon we do not have or know - if (result > PCGreatestUid || CurrentPCBox == null || box != CurrentPCBoxId) - { - return -1; - } - return result; - } - - public bool DepositPokemonToPC(int pokemonUid) - { - if (!IsPCOpen || pokemonUid < 1 || pokemonUid > 6 || Team.Count < pokemonUid) - { - return false; - } - SendMovePokemonToPC(pokemonUid); - return true; - } - - public bool WithdrawPokemonFromPC(int boxId, int boxPokemonId) - { - int pcPokemonUid = GetPokemonPCUid(boxId, boxPokemonId); - if (pcPokemonUid == -1) - { - return false; - } - if (!IsPCOpen || pcPokemonUid < 7 || pcPokemonUid > PCGreatestUid || Team.Count >= 6) - { - return false; - } - SendMovePokemonFromPC(pcPokemonUid, Team.Count + 1); - return true; - } - - public bool SwapPokemonFromPC(int boxId, int boxPokemonId, int teamPokemonUid) - { - int pcPokemonUid = GetPokemonPCUid(boxId, boxPokemonId); - if (pcPokemonUid == -1) - { - return false; - } - if (!IsPCOpen || pcPokemonUid < 7 || pcPokemonUid > PCGreatestUid - || teamPokemonUid < 1 || teamPokemonUid > 6 || Team.Count < teamPokemonUid) - { - return false; - } - SendMovePokemonFromPC(pcPokemonUid, teamPokemonUid); - return true; - } - - public bool SwapPokemon(int pokemon1, int pokemon2) - { - if (IsInBattle || pokemon1 < 1 || pokemon2 < 1 || Team.Count < pokemon1 || Team.Count < pokemon2 || pokemon1 == pokemon2) - { - return false; - } - if (!_swapTimeout.IsActive) - { - SendSwapPokemons(pokemon1, pokemon2); - _swapTimeout.Set(); - return true; - } - return false; - } - - public void Move(Direction direction) - { - _movements.Add(direction); - } - - public void RequestResync() - { - SendMessage("/syn"); - _teleportationTimeout.Set(); - } - - public void UseAttack(int number) - { - SendAttack(number.ToString()); - _battleTimeout.Set(); - } - - public void UseItem(int id, int pokemonUid = 0) - { - if (!(pokemonUid >= 0 && pokemonUid <= 6) || !HasItemId(id)) - { - return; - } - InventoryItem item = GetItemFromId(id); - if (item == null || item.Quantity == 0) - { - return; - } - if (pokemonUid == 0) // simple use - { - if (!_itemUseTimeout.IsActive && !IsInBattle && (item.Scope == 8 || item.Scope == 10 || item.Scope == 15)) - { - SendUseItem(id); - _itemUseTimeout.Set(); - } - else if (!_battleTimeout.IsActive && IsInBattle && item.Scope == 5) - { - SendAttack("item" + id); - _battleTimeout.Set(); - } - } - else // use item on pokemon - { - if (!_itemUseTimeout.IsActive && !IsInBattle - && (item.Scope == 2 || item.Scope == 3 || item.Scope == 9 - || item.Scope == 13 || item.Scope == 14)) - { - SendUseItem(id, pokemonUid); - _itemUseTimeout.Set(); - } - else if (!_battleTimeout.IsActive && IsInBattle && item.Scope == 2) - { - SendAttack("item" + id + ":" + pokemonUid); - _battleTimeout.Set(); - } - } - } - - public bool GiveItemToPokemon(int pokemonUid, int itemId) - { - if (!(pokemonUid >= 1 && pokemonUid <= Team.Count)) - { - return false; - } - InventoryItem item = GetItemFromId(itemId); - if (item == null || item.Quantity == 0) - { - return false; - } - if (!_itemUseTimeout.IsActive && !IsInBattle - && (item.Scope == 2 || item.Scope == 3 || item.Scope == 9 || item.Scope == 13 - || item.Scope == 14 || item.Scope == 5 || item.Scope == 12 || item.Scope == 6)) - { - SendGiveItem(pokemonUid, itemId); - _itemUseTimeout.Set(); - return true; - } - return false; - } - - public bool TakeItemFromPokemon(int pokemonUid) - { - if (!(pokemonUid >= 1 && pokemonUid <= Team.Count)) - { - return false; - } - if (!_itemUseTimeout.IsActive && Team[pokemonUid - 1].ItemHeld != "") - { - SendTakeItem(pokemonUid); - _itemUseTimeout.Set(); - return true; - } - return false; - } - - public bool HasSurfAbility() - { - return (HasMove("Surf") || WaterMount != null) && - (Map.Region == "1" && HasItemName("Soul Badge") || - Map.Region == "2" && HasItemName("Fog Badge") || - Map.Region == "3" && HasItemName("Balance Badge") || - Map.Region == "4" && HasItemName("Relic Badge")); - } - - public bool HasCutAbility() - { - return (HasMove("Cut") || HasTreeaxe()) && - (Map.Region == "1" && HasItemName("Cascade Badge") || - Map.Region == "2" && HasItemName("Hive Badge") || - Map.Region == "3" && HasItemName("Stone Badge") || - Map.Region == "4" && HasItemName("Forest Badge")); - } - - public bool HasRockSmashAbility() - { - return HasMove("Rock Smash") || HasPickaxe(); - } - - public bool HasTreeaxe() - { - return HasItemId(838) && HasItemId(317); - } - - public bool HasPickaxe() - { - return HasItemId(839); - } - - public bool PokemonUidHasMove(int pokemonUid, string moveName) - { - return Team.FirstOrDefault(p => p.Uid == pokemonUid)?.Moves.Any(m => m.Name?.Equals(moveName, StringComparison.InvariantCultureIgnoreCase) ?? false) ?? false; - } - - public bool HasMove(string moveName) - { - return Team.Any(p => p.Moves.Any(m => m.Name?.Equals(moveName, StringComparison.InvariantCultureIgnoreCase) ?? false)); - } - - public int GetMovePosition(int pokemonUid, string moveName) - { - return Team[pokemonUid].Moves.FirstOrDefault(m => m.Name?.Equals(moveName, StringComparison.InvariantCultureIgnoreCase) ?? false)?.Position ?? -1; - } - - public InventoryItem GetItemFromId(int id) - { - return Items.FirstOrDefault(i => i.Id == id && i.Quantity > 0); - } - - public bool HasItemId(int id) - { - return GetItemFromId(id) != null; - } - - public InventoryItem GetItemFromName(string itemName) - { - return Items.FirstOrDefault(i => i.Name.Equals(itemName, StringComparison.InvariantCultureIgnoreCase) && i.Quantity > 0); - } - - public bool HasItemName(string itemName) - { - return GetItemFromName(itemName) != null; - } - - public bool HasPokemonInTeam(string pokemonName) - { - return FindFirstPokemonInTeam(pokemonName) != null; - } - - public Pokemon FindFirstPokemonInTeam(string pokemonName) - { - return Team.FirstOrDefault(p => p.Name.Equals(pokemonName, StringComparison.InvariantCultureIgnoreCase)); - } - - public void UseSurf() - { - if (WaterMount == null) - { - SendMessage("/surf"); - } - else - { - LogMessage?.Invoke($"Mounting [{WaterMount.Name}]"); - UseItem(WaterMount.Id); - } - - _mountingTimeout.Set(); - } - - public void UseSurfAfterMovement() - { - _surfAfterMovement = true; - } - - public void RunFromBattle() - { - UseAttack(5); - } - - public void ChangePokemon(int number) - { - UseAttack(number + 5); - } - - public void TalkToNpc(Npc npc) - { - npc.CanBattle = false; - - SendTalkToNpc(npc.Id); - _dialogTimeout.Set(); - } - - public bool OpenPC() - { - if (!Map.IsPC(PlayerX, PlayerY - 1)) - { - return false; - } - IsPCOpen = true; - return RefreshPCBox(1); - } - - public void PushDialogAnswer(int index) - { - _dialogResponses.Enqueue(index); - } - - public void PushDialogAnswer(string text) - { - _dialogResponses.Enqueue(text); - } - - public bool BuyItem(int itemId, int quantity) - { - if (OpenedShop != null && OpenedShop.Items.Any(item => item.Id == itemId)) - { - _itemUseTimeout.Set(); - SendShopPokemart(OpenedShop.Id, itemId, quantity); - return true; - } - return false; - } - - public bool PurchaseMove(string moveName) - { - if (MoveRelearner != null && MoveRelearner.Moves.Any(move => move.Name == moveName.ToLowerInvariant())) - { - _moveRelearnerTimeout.Set(); - SendPurchaseMove(MoveRelearner.SelectedPokemonUid, moveName); - return true; - } - return false; - } - - private void SendPurchaseMove(int pokemonUid, string moveName) - { - // DSSock.cs handles Move Relearn as below. - - if (MoveRelearner != null) - { - if (!MoveRelearner.IsEgg) - { - SendPacket("z|.|" + pokemonUid + "|.|" + moveName); - } - else - { - int moveId = MovesManager.Instance.GetMoveId(moveName); - if (moveId != -1) - { - SendPacket("b|.|" + pokemonUid + "|.|" + moveId); - } - } - } - } - - private void MapClient_ConnectionOpened() - { -#if DEBUG - Console.WriteLine("[+++] Connecting to the game server"); -#endif - if (MapName != null && Map == null) - { - _mapClient.DownloadMap(MapName); - } - } - - private void MapClient_ConnectionFailed(Exception ex) - { - ConnectionFailed?.Invoke(ex); - } - - private void MapClient_ConnectionClosed(Exception ex) - { - Close(ex); - } - - private void MapClient_MapLoaded(string mapName, Map map) - { - if (mapName == MapName) - { - Players.Clear(); - - Map = map; - // DSSock.loadMap - SendPacket("-"); - SendPacket("k|.|" + MapName.ToLowerInvariant()); - - CanUseCut = HasCutAbility(); - CanUseSmashRock = HasRockSmashAbility(); - - MapLoaded?.Invoke(MapName); - } - else - { - InvalidPacket?.Invoke(mapName, "Received a map that is not the current map"); - } - } - - private void OnPacketReceived(string packet) - { - ProcessPacket(packet); - } - - private void OnConnectionOpened() - { - IsConnected = true; -#if DEBUG - Console.WriteLine("[+++] Connection opened"); -#endif - ConnectionOpened?.Invoke(); - } - - private void OnConnectionClosed(Exception ex) - { - _mapClient.Close(); - if (!IsConnected) - { -#if DEBUG - Console.WriteLine("[---] Connection failed"); -#endif - ConnectionFailed?.Invoke(ex); - } - else - { - IsConnected = false; -#if DEBUG - Console.WriteLine("[---] Connection closed"); -#endif - ConnectionClosed?.Invoke(ex); - } - } - - private void SendMovement(string direction) - { - _lastMovement = DateTime.UtcNow; - // Consider the pokemart closed after the first movement. - OpenedShop = null; - MoveRelearner = null; - IsPCOpen = false; - // DSSock.sendMove - SendPacket("#|.|" + direction); - } - - private void SendAttack(string number) - { - // DSSock.sendAttack - // DSSock.RunButton - SendPacket("(|.|" + number); - } - - private void SendTalkToNpc(int npcId) - { - // DSSock.Interact - SendPacket("N|.|" + npcId); - } - - private void SendDialogResponse(int number) - { - // DSSock.ClickButton - SendPacket("R|.|" + ScriptId + "|.|" + number); - } - - public void SendAcceptEvolution(int evolvingPokemonUid, int evolvingItem) - { - // DSSock.AcceptEvo - SendPacket("h|.|" + evolvingPokemonUid + "|.|" + evolvingItem); - } - - public void SendCancelEvolution(int evolvingPokemonUid, int evolvingItem) - { - // DSSock.CancelEvo - SendPacket("j|.|" + evolvingPokemonUid + "|.|" + evolvingItem); - } - - private void SendSwapPokemons(int pokemon1, int pokemon2) - { - SendPacket("?|.|" + pokemon2 + "|.|" + pokemon1); - } - - private void SendShopPokemart(int shopId, int itemId, int quantity) - { - SendPacket("c|.|" + shopId + "|.|" + itemId + "|.|" + quantity); - } - - private void ProcessPacket(string packet) - { -#if DEBUG - Console.WriteLine(packet); -#endif - - if (packet.Substring(0, 1) == "U") - { - packet = "U|.|" + packet.Substring(1); - } - - string[] data = packet.Split(new [] { "|.|" }, StringSplitOptions.None); - string type = data[0].ToLowerInvariant(); - switch (type) - { - case "5": - OnLoggedIn(data); - break; - case "6": - OnAuthenticationResult(data); - break; - case "l": - //Move relearn content - OnMoveRelearn(data); - break; - case ")": - OnQueueUpdated(data); - break; - case "q": - OnPlayerPosition(data); - break; - case "s": - OnPlayerSync(data); - break; - case "i": - OnPlayerInfos(data); - break; - case "(": - // CDs ? - break; - case "e": - OnUpdateTime(data); - break; - case "@": - OnNpcBattlers(data); - break; - case "*": - OnNpcDestroy(data); - break; - case "#": - OnTeamUpdate(data); - break; - case "d": - OnInventoryUpdate(data); - break; - case "&": - OnItemsUpdate(data); - break; - case "!": - OnBattleJoin(packet); - break; - case "a": - OnBattleMessage(data); - break; - case "r": - OnScript(data); - break; - case "$": - OnBikingUpdate(data); - break; - case "%": - OnSurfingUpdate(data); - break; - case "^": - OnLearningMove(data); - break; - case "h": - OnEvolving(data); - break; - case "=": - OnUpdatePlayer(data); - break; - case "c": - OnChannels(data); - break; - case "w": - OnChatMessage(data); - break; - case "o": - // Shop content - break; - case "pm": - OnPrivateMessage(data); - break; - case ".": - // DSSock.ProcessCommands - SendPacket("_"); - break; - case "'": - // DSSock.ProcessCommands - SendPacket("'"); - break; - case "m": - OnPCBox(data); - break; - default: -#if DEBUG - Console.WriteLine(" ^ unhandled /!\\"); -#endif - break; - } - } - - private void OnLoggedIn(string[] data) - { - Console.WriteLine("[Login] Authenticated successfully, connecting to map server"); - - IsCreatingNewCharacter = data[1] == "1"; - - string[] mapServerHost = data[2].Split(':'); - _mapClient.Open(mapServerHost[0], int.Parse(mapServerHost[1])); - - // DSSock.ProcessCommands - SendMessage("/in1"); - // TODO: Add a setting to disable the party inspection (send /in0 instead). - SendPacket(")"); - SendPacket("_"); - SendPacket("g"); - IsAuthenticated = true; - - LoggedIn?.Invoke(); - } - - private void OnAuthenticationResult(string[] data) - { - AuthenticationResult result = (AuthenticationResult)Convert.ToInt32(data[1]); - - if (result != AuthenticationResult.ServerFull) - { - AuthenticationFailed?.Invoke(result); - Close(); - } - } - - private void OnQueueUpdated(string[] data) - { - string[] queueData = data[1].Split('|'); - - int position = Convert.ToInt32(queueData[0]); - QueueUpdated?.Invoke(position); - } - - private void OnPlayerPosition(string[] data) - { - string[] mapData = data[1].Split(new[] { "|" }, StringSplitOptions.None); - string map = mapData[0]; - int playerX = Convert.ToInt32(mapData[1]); - int playerY = Convert.ToInt32(mapData[2]); - if (playerX != PlayerX || playerY != PlayerY || map != MapName) - { - TeleportationOccuring?.Invoke(map, playerX, playerY); - - PlayerX = playerX; - PlayerY = playerY; - LoadMap(map); - IsOnGround = (mapData[3] == "1"); - if (Convert.ToInt32(mapData[4]) == 1) - { - IsSurfing = true; - IsBiking = false; - } - // DSSock.sendSync - SendPacket("S"); - } - - PositionUpdated?.Invoke(MapName, PlayerX, playerY); - - _teleportationTimeout.Cancel(); - } - - private void OnPlayerSync(string[] data) - { - string[] mapData = data[1].Split(new[] { "|" }, StringSplitOptions.None); - - if (mapData.Length < 2) - return; - - string map = mapData[0]; - int playerX = Convert.ToInt32(mapData[1]); - int playerY = Convert.ToInt32(mapData[2]); - if (map.Length > 1) - { - PlayerX = playerX; - PlayerY = playerY; - LoadMap(map); - } - IsOnGround = (mapData[3] == "1"); - - PositionUpdated?.Invoke(MapName, PlayerX, playerY); - } - - private void OnPlayerInfos(string[] data) - { - string[] playerData = data[1].Split('|'); - PlayerName = playerData[0]; - PokedexOwned = Convert.ToInt32(playerData[4]); - PokedexSeen = Convert.ToInt32(playerData[5]); - PokedexEvolved = Convert.ToInt32(playerData[6]); - IsMember = playerData[10] == "1"; - } - - private void OnUpdateTime(string[] data) - { - string[] timeData = data[1].Split('|'); - - PokemonTime = timeData[0]; - - Weather = timeData[1]; - - PokeTimeUpdated?.Invoke(PokemonTime, Weather); - } - - private void OnNpcBattlers(string[] data) - { - if (!IsMapLoaded) return; - - List defeatedBattlers = data[1].Split(new [] { "|" }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToList(); - - Map.Npcs.Clear(); - foreach (Npc npc in Map.OriginalNpcs) - { - Npc clone = npc.Clone(); - if (defeatedBattlers.Contains(npc.Id)) - { - clone.CanBattle = false; - } - Map.Npcs.Add(clone); - } - } - - private void OnNpcDestroy(string[] data) - { - if (!IsMapLoaded) return; - - string[] npcData = data[1].Split('|'); - - foreach (string npcText in npcData) - { - int npcId = int.Parse(npcText); - foreach (Npc npc in Map.Npcs) - { - if (npc.Id == npcId) - { - Map.Npcs.Remove(npc); - break; - } - } - } - - AreNpcReceived = true; - NpcReceived?.Invoke(Map.Npcs); - } - - private void OnTeamUpdate(string[] data) - { - string[] teamData = data[1].Split(new[] { "\r\n" }, StringSplitOptions.None); - - Team.Clear(); - foreach (string pokemon in teamData) - { - if (pokemon == string.Empty) - continue; - - string[] pokemonData = pokemon.Split('|'); - - Team.Add(new Pokemon(pokemonData)); - } - - if (IsMapLoaded) - { - CanUseCut = HasCutAbility(); - CanUseSmashRock = HasRockSmashAbility(); - } - - if (_swapTimeout.IsActive) - { - _swapTimeout.Set(Rand.Next(500, 1000)); - } - PokemonsUpdated?.Invoke(); - } - - private void OnInventoryUpdate(string[] data) - { - Money = Convert.ToInt32(data[1]); - Coins = Convert.ToInt32(data[2]); - UpdateItems(data[3]); - } - - private void OnItemsUpdate(string[] data) - { - UpdateItems(data[1]); - } - - private void UpdateItems(string content) - { - Items.Clear(); - - string[] itemsData = content.Split(new[] { "\r\n" }, StringSplitOptions.None); - foreach (string item in itemsData) - { - if (item == string.Empty) - continue; - string[] itemData = item.Split(new[] { "|" }, StringSplitOptions.None); - Items.Add(new InventoryItem(Convert.ToInt32(itemData[0]), Convert.ToInt32(itemData[1]), Convert.ToInt32(itemData[2]))); - } - - if (_itemUseTimeout.IsActive) - { - _itemUseTimeout.Set(Rand.Next(500, 1000)); - } - InventoryUpdated?.Invoke(); - } - - private void OnBattleJoin(string packet) - { - string[] data = packet.Substring(4).Split('|'); - - IsScriptActive = false; - - IsInBattle = true; - ActiveBattle = new Battle(PlayerName, data); - ActiveBattle.ActivePokemonChanged += ActivePokemonChanged; - ActiveBattle.OpponentChanged += OpponentChanged; - - _movements.Clear(); - _slidingDirection = null; - - _battleTimeout.Set(Rand.Next(4000, 6000)); - _fishingTimeout.Cancel(); - - BattleStarted?.Invoke(); - - string[] battleMessages = ActiveBattle.BattleText.Split(new[] { "\r\n" }, StringSplitOptions.None); - - foreach (string message in battleMessages) - { - if (!ActiveBattle.ProcessMessage(Team, message)) - { - BattleMessage?.Invoke(I18n.Replace(message)); - } - } - - BattleUpdated?.Invoke(); - } - - private void OnBattleMessage(string[] data) - { - if (!IsInBattle) - { - return; - } - - string[] battleData = data[1].Split(new string[] { "|" }, StringSplitOptions.None); - string[] battleMessages = battleData[4].Split(new string[] { "\r\n" }, StringSplitOptions.None); - - foreach (string message in battleMessages) - { - if (!ActiveBattle.ProcessMessage(Team, message)) - { - BattleMessage?.Invoke(I18n.Replace(message)); - } - } - - PokemonsUpdated?.Invoke(); - BattleUpdated?.Invoke(); - - if (ActiveBattle.IsFinished) - { - _battleTimeout.Set(Rand.Next(1500, 5000)); - } - else - { - _battleTimeout.Set(Rand.Next(2000, 4000)); - } - - if (ActiveBattle.IsFinished) - { - IsInBattle = false; - ActiveBattle = null; - BattleEnded?.Invoke(); - } - } - - private void OnScript(string[] data) - { - string id = data[2]; - int status = Convert.ToInt32(data[1]); - string script = data[3]; - - DialogContent = script.Split(new string[] { "-#-" }, StringSplitOptions.None); - if (script.Contains("-#-") && status > 1) - { - script = DialogContent[0]; - } - string[] messages = script.Split(new string[] { "-=-" }, StringSplitOptions.RemoveEmptyEntries); - foreach (string message in messages) - { - if (message.StartsWith("emote") || message.StartsWith("playsound") || message.StartsWith("playmusic") || message.StartsWith("playcry")) - continue; - if (message.StartsWith("shop")) - { - OpenedShop = new Shop(message.Substring(4)); - ShopOpened?.Invoke(OpenedShop); - continue; - } - if (message.StartsWith("moverelearner")) - { - int pokemonUid = Convert.ToInt32(message.Substring(13)); - MoveRelearner = new MoveRelearner(pokemonUid, false); - - SendPacket("a|.|" + pokemonUid); - continue; - } - if (message.StartsWith("eggsrelearner")) - { - int pokemonUid = Convert.ToInt32(message.Substring(13)); - MoveRelearner = new MoveRelearner(pokemonUid, true); - - SendPacket(".|.|" + message.Substring(13)); - continue; - } - DialogOpened?.Invoke(message); - } - - IsScriptActive = true; - _dialogTimeout.Set(Rand.Next(1500, 4000)); - ScriptId = id; - ScriptStatus = status; - } - - private void OnMoveRelearn(string[] data) - { - if (MoveRelearner != null) - { - MoveRelearner.ProcessMessage(data[1]); - MoveRelearnerOpened?.Invoke(MoveRelearner); - } - } - - private void OnBikingUpdate(string[] data) - { - if (data[1] == "1") - { - IsBiking = true; - IsSurfing = false; - } - else - { - IsBiking = false; - } - _mountingTimeout.Set(Rand.Next(500, 1000)); - _itemUseTimeout.Cancel(); - } - - private void OnSurfingUpdate(string[] data) - { - if (data[1] == "1") - { - IsSurfing = true; - IsBiking = false; - } - else - { - IsSurfing = false; - } - _mountingTimeout.Set(Rand.Next(500, 1000)); - _itemUseTimeout.Cancel(); - } - - private void OnLearningMove(string[] data) - { - int moveId = Convert.ToInt32(data[1]); - string moveName = Convert.ToString(data[2]); - int pokemonUid = Convert.ToInt32(data[3]); - int movePp = Convert.ToInt32(data[4]); - LearningMove?.Invoke(moveId, moveName, pokemonUid); - MoveRelearner = null; - _itemUseTimeout.Cancel(); - _moveRelearnerTimeout.Cancel(); - // ^|.|348|.|Cut|.|3|.|30|.\ - } - - private void OnEvolving(string[] data) - { - int evolvingPokemonUid = Convert.ToInt32(data[1]); - int evolvingItem = Convert.ToInt32(data[3]); - - - Evolving.Invoke(evolvingPokemonUid, evolvingItem); - } - - private void OnUpdatePlayer(string[] data) - { - string[] updateData = data[1].Split('|'); - - bool isNewPlayer = false; - PlayerInfos player; - DateTime expiration = DateTime.UtcNow.AddSeconds(20); - if (Players.ContainsKey(updateData[0])) - { - player = Players[updateData[0]]; - player.Expiration = expiration; - } - else - { - isNewPlayer = true; - player = new PlayerInfos(expiration); - player.Name = updateData[0]; - } - - player.Updated = DateTime.UtcNow; - player.PosX = Convert.ToInt32(updateData[1]); - player.PosY = Convert.ToInt32(updateData[2]); - player.Direction = updateData[3][0]; - player.Skin = updateData[3].Substring(1); - player.IsAfk = updateData[4][0] != '0'; - player.IsInBattle = updateData[4][1] != '0'; - player.PokemonPetId = Convert.ToInt32(updateData[4].Substring(2)); - player.IsPokemonPetShiny = updateData[5][0] != '0'; - player.IsMember = updateData[5][1] != '0'; - player.IsOnground = updateData[5][2] != '0'; - player.GuildId = Convert.ToInt32(updateData[5].Substring(3)); - player.PetForm = Convert.ToInt32(updateData[6]); // ??? - - Players[player.Name] = player; - - if (isNewPlayer) - { - PlayerAdded?.Invoke(player); - } - else - { - PlayerUpdated?.Invoke(player); - } - } - - private void OnChannels(string[] data) - { - Channels.Clear(); - string[] channelsData = data[1].Split('|'); - for (int i = 1; i < channelsData.Length; i += 2) - { - string channelId = channelsData[i]; - string channelName = channelsData[i + 1]; - Channels.Add(new ChatChannel(channelId, channelName)); - } - RefreshChannelList?.Invoke(); - } - - private void OnChatMessage(string[] data) - { - string fullMessage = data[1]; - string[] chatData = fullMessage.Split(':'); - - if (fullMessage[0] == '*' && fullMessage[2] == '*') - { - fullMessage = fullMessage.Substring(3); - } - - string message; - if (chatData.Length <= 1) // we are not really sure what this stands for - { - string channelName; - - int start = fullMessage.IndexOf('(') + 1; - int end = fullMessage.IndexOf(')'); - if (fullMessage.Length <= end || start == 0 || end == -1) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Channel System Message with invalid channel"); - channelName = ""; - } - else - { - channelName = fullMessage.Substring(start, end - start); - } - - if (fullMessage.Length <= end + 2 || start == 0 || end == -1) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Channel System Message with invalid message"); - message = ""; - } - else - { - message = fullMessage.Substring(end + 2); - } - - ChannelSystemMessage?.Invoke(channelName, message); - return; - } - if (chatData[0] != "*G*System") - { - string channelName = null; - string mode = null; - string author; - - int start = (fullMessage[0] == '(' ? 1 : 0); - int end; - if (start != 0) - { - end = fullMessage.IndexOf(')'); - if (end != -1 && end - start > 0) - { - channelName = fullMessage.Substring(start, end - start); - } - else - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Channel Message with invalid channel name"); - channelName = ""; - } - } - start = fullMessage.IndexOf('[') + 1; - if (start != 0 && fullMessage[start] != 'n') - { - end = fullMessage.IndexOf(']'); - if (end == -1) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Message with invalid mode"); - message = ""; - } - mode = fullMessage.Substring(start, end - start); - } - string conversation = null; - if (channelName == "PM") - { - end = fullMessage.IndexOf(':'); - string header = ""; - if (end == -1) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Channel Private Message with invalid author"); - conversation = ""; - } - else - { - header = fullMessage.Substring(0, end); - start = header.LastIndexOf(' ') + 1; - if (end == -1) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Channel Private Message with invalid author"); - conversation = ""; - } - else - { - conversation = header.Substring(start); - } - } - if (header.Contains(" to ")) - { - author = PlayerName; - } - else - { - author = conversation; - } - } - else - { - start = fullMessage.IndexOf("[n=") + 3; - end = fullMessage.IndexOf("][/n]:"); - if (end == -1) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Message with invalid author"); - author = ""; - } - else - { - author = fullMessage.Substring(start, end - start); - } - } - start = fullMessage.IndexOf(':') + 2; - if (end == -1) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Channel Private Message with invalid message"); - message = ""; - } - else - { - message = fullMessage.Substring(start == 1 ? 0 : start); - } - if (channelName != null) - { - if (channelName == "PM") - { - ChannelPrivateMessage?.Invoke(conversation, mode, author, message); - } - else - { - ChannelMessage?.Invoke(channelName, mode, author, message); - } - } - else - { - if (message.IndexOf("em(") == 0) - { - end = message.IndexOf(")"); - int emoteId; - if (end != -1 && end - 3 > 0) - { - string emoteIdString = message.Substring(3, end - 3); - if (int.TryParse(emoteIdString, out emoteId) && emoteId > 0) - { - EmoteMessage?.Invoke(mode, author, emoteId); - return; - } - } - } - ChatMessage?.Invoke(mode, author, message); - } - return; - } - - int offset = fullMessage.IndexOf(':') + 2; - if (offset == -1 + 2) // for clarity... I prefectly know it's -3 - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "Channel Private Message with invalid author"); - message = ""; - } - else - { - message = fullMessage.Substring(offset == 1 ? 0 : offset); - } - - if (message.Contains("$YouUse the ") && message.Contains("Rod!")) - { - _itemUseTimeout.Cancel(); - _fishingTimeout.Set(2500 + Rand.Next(500, 1500)); - } - - SystemMessage?.Invoke(I18n.Replace(message)); - } - - private void OnPrivateMessage(string[] data) - { - if (data.Length < 2) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "PM with no parameter"); - } - string[] nicknames = data[1].Split(new[] { "-=-" }, StringSplitOptions.None); - if (nicknames.Length < 2) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "PM with invalid header"); - return; - } - - string conversation; - if (nicknames[0] != PlayerName) - { - conversation = nicknames[0]; - } - else - { - conversation = nicknames[1]; - } - - if (data.Length < 3) - { - string packet = string.Join("|.|", data); - InvalidPacket?.Invoke(packet, "PM without a message"); - /* - * the PM is sent since the packet is still understandable - * however, PRO client does not allow it - */ - PrivateMessage?.Invoke(conversation, null, conversation + " (deduced)", ""); - return; - } - - string mode = null; - int offset = data[2].IndexOf('[') + 1; - int end = 0; - if (offset != 0 && offset < data[2].IndexOf(':')) - { - end = data[2].IndexOf(']'); - mode = data[2].Substring(offset, end - offset); - } - - if (data[2].Substring(0, 4) == "rem:") - { - LeavePrivateMessage?.Invoke(conversation, mode, data[2].Substring(4 + end)); - return; - } - else if (!Conversations.Contains(conversation)) - { - Conversations.Add(conversation); - } - - string modeRemoved = data[2]; - if (end != 0) - { - modeRemoved = data[2].Substring(end + 2); - } - offset = modeRemoved.IndexOf(' '); - string speaker = modeRemoved.Substring(0, offset); - - offset = data[2].IndexOf(':') + 2; - string message = data[2].Substring(offset); - - PrivateMessage?.Invoke(conversation, mode, speaker, message); - } - - public int GetBoxIdFromPokemonUid(int lastUid) - { - return (lastUid - 7) / 15 + 1; - } - - private void OnPCBox(string[] data) - { - _refreshingPCBox.Cancel(); - IsPCBoxRefreshing = false; - if (Map.IsPC(PlayerX, PlayerY - 1)) - { - IsPCOpen = true; - } - string[] body = data[1].Split('='); - if (body.Length < 3) - { - InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received an invalid PC Box packet"); - return; - } - PCGreatestUid = Convert.ToInt32(body[0]); - - int pokemonCount = Convert.ToInt32(body[1]); - if (pokemonCount <= 0 || pokemonCount > 15) - { - InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received an invalid PC Box size"); - return; - } - string[] pokemonListDatas = body[2].Split(new[] { "\r\n" }, StringSplitOptions.None); - if (body.Length < 1) - { - InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received an empty box"); - return; - } - List pokemonBox = new List(); - foreach (var pokemonDatas in pokemonListDatas) - { - if (pokemonDatas == string.Empty) continue; - string[] pokemonDatasArray = pokemonDatas.Split('|'); - Pokemon pokemon = new Pokemon(pokemonDatasArray); - if (CurrentPCBoxId != GetBoxIdFromPokemonUid(pokemon.Uid)) - { - InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received a box packet for an unexpected box: expected #" - + CurrentPCBox + ", received #" + GetBoxIdFromPokemonUid(pokemon.Uid)); - return; - } - pokemonBox.Add(pokemon); - } - if (pokemonBox.Count != pokemonCount) - { - InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received a PC Box size that does not match the content"); - return; - } - CurrentPCBox = pokemonBox; - PCBoxUpdated?.Invoke(CurrentPCBox); - } - - private void LoadMap(string mapName) - { - mapName = MapClient.RemoveExtension(mapName); - - _loadingTimeout.Set(Rand.Next(1500, 4000)); - - OpenedShop = null; - MoveRelearner = null; - _movements.Clear(); - _surfAfterMovement = false; - _slidingDirection = null; - _dialogResponses.Clear(); - _movementTimeout.Cancel(); - _mountingTimeout.Cancel(); - _itemUseTimeout.Cancel(); - - if (Map == null || MapName != mapName) - { - DownloadMap(mapName); - } - } - - private void DownloadMap(string mapName) - { - Console.WriteLine("[Map] Requesting: " + MapName); - - Map = null; - AreNpcReceived = false; - MapName = mapName; - Players.Clear(); - - if (_mapClient.IsConnected) - { - _mapClient.DownloadMap(MapName); - } - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PROProtocol +{ + public class GameClient + { + public InventoryItem GroundMount; + public InventoryItem WaterMount; + + public Random Rand { get; private set; } = new Random(); + public Language I18n { get; private set; } = new Language(); + + public bool IsConnected { get; private set; } + public bool IsAuthenticated { get; private set; } + public string PlayerName { get; private set; } + public int GuildId { get; private set; } = -1; + + public int PlayerX { get; private set; } + public int PlayerY { get; private set; } + public string MapName { get; private set; } + public Map Map { get; private set; } + + public int PokedexOwned { get; private set; } + public int PokedexSeen { get; private set; } + public int PokedexEvolved { get; private set; } + + public bool IsInBattle { get; private set; } + public bool IsSurfing { get; private set; } + public bool IsBiking { get; private set; } + public bool IsOnGround { get; private set; } + public bool IsPCOpen { get; private set; } + public bool CanUseCut { get; private set; } + public bool CanUseSmashRock { get; private set; } + public bool IsPrivateMessageOn { get; private set; } = true; + public bool DisableMovingNpcInteractions = true; + + public int Money { get; private set; } + public int Coins { get; private set; } + public bool IsMember { get; private set; } + public List Team { get; private set; } = new List(); + public List CurrentPCBox { get; private set; } = new List(); + public List Items { get; private set; } = new List(); + public string PokemonTime { get; private set; } + public string Weather { get; private set; } + //public int PCGreatestUid { get; private set; } = -1; + public int MountId { get; private set; } = 0; + + public bool IsScriptActive { get; private set; } + public string ScriptId { get; private set; } + public int ScriptStatus { get; private set; } + public string[] DialogContent { get; private set; } + + public Battle ActiveBattle { get; private set; } + public Shop OpenedShop { get; private set; } + public MoveRelearner MoveRelearner { get; private set; } + + public List Channels { get; } = new List(); + public List Conversations { get; } = new List(); + public Dictionary Players { get; } = new Dictionary(); + + private DateTime _updatePlayers; + private DateTime _refreshBoxTimeout; + + public bool IsPCBoxRefreshing { get; private set; } + public int CurrentPCBoxId { get; private set; } + public bool IsCreatingNewCharacter { get; private set; } + + public event Action ConnectionOpened; + public event Action ConnectionFailed; + public event Action ConnectionClosed; + public event Action LoggedIn; + public event Action AuthenticationFailed; + public event Action QueueUpdated; + + public event Action PositionUpdated; + public event Action TeleportationOccuring; + public event Action MapLoaded; + public event Action> NpcReceived; + public event Action PokemonsUpdated; + public event Action InventoryUpdated; + public event Action BattleStarted; + public event Action BattleMessage; + public event Action BattleEnded; + public event Action BattleUpdated; + public event Action DialogOpened; + public event Action EmoteMessage; + public event Action ChatMessage; + public event Action RefreshChannelList; + public event Action ChannelMessage; + public event Action ChannelSystemMessage; + public event Action ChannelPrivateMessage; + public event Action SystemMessage; + public event Action PrivateMessage; + public event Action LeavePrivateMessage; + public event Action PlayerUpdated; + public event Action PlayerAdded; + public event Action PlayerRemoved; + public event Action InvalidPacket; + public event Action LearningMove; + public event Action Evolving; + public event Action PokeTimeUpdated; + public event Action ShopOpened; + public event Action MoveRelearnerOpened; + public event Action> PCBoxUpdated; + public event Action LogMessage; + public event Action ActivePokemonChanged; + public event Action OpponentChanged; + + private const string Version = "Christmas_21"; + + private GameConnection _connection; + private DateTime _lastMovement; + private DateTime _lastVisPacket; + private List _movements = new List(); + private Direction? _slidingDirection; + private int _surfAfterMovement; // 0 = no action needed, 1 = have to send surf packet, 2 = sent surf packet waiting for server response + private Queue _dialogResponses = new Queue(); + private bool _askTeamAfterBattle = false; + + private Timeout _movementTimeout = new Timeout(); + private Timeout _battleTimeout = new Timeout(); + private Timeout _loadingTimeout = new Timeout(); + private Timeout _mountingTimeout = new Timeout(); + private Timeout _teleportationTimeout = new Timeout(); + private Timeout _dialogTimeout = new Timeout(); + private Timeout _swapTimeout = new Timeout(); + private Timeout _itemUseTimeout = new Timeout(); + private Timeout _fishingTimeout = new Timeout(); + private Timeout _refreshingPCBox = new Timeout(); + private Timeout _moveRelearnerTimeout = new Timeout(); + + private Timeout _npcBattleTimeout = new Timeout(); + private Npc _npcBattler; + + private MapClient _mapClient; + private List _requestedGuildData = new List(); + + public void ClearPath() + { + _movements.Clear(); + } + + public bool IsInactive => + _movements.Count == 0 + && !_movementTimeout.IsActive + && !_battleTimeout.IsActive + && !_loadingTimeout.IsActive + && !_mountingTimeout.IsActive + && !_teleportationTimeout.IsActive + && !_dialogTimeout.IsActive + && !_swapTimeout.IsActive + && !_itemUseTimeout.IsActive + && !_fishingTimeout.IsActive + && !_refreshingPCBox.IsActive + && !_npcBattleTimeout.IsActive + && !_moveRelearnerTimeout.IsActive + && !IsCreatingNewCharacter; + + public bool IsTeleporting => _teleportationTimeout.IsActive; + + public GameServer Server => _connection.Server; + + public bool IsMapLoaded => Map != null; + public bool AreNpcReceived { get; private set; } + + public GameClient(GameConnection connection, MapConnection mapConnection) + { + _mapClient = new MapClient(mapConnection); + _mapClient.ConnectionOpened += MapClient_ConnectionOpened; + _mapClient.ConnectionClosed += MapClient_ConnectionClosed; + _mapClient.ConnectionFailed += MapClient_ConnectionFailed; + _mapClient.MapLoaded += MapClient_MapLoaded; + + _connection = connection; + _connection.PacketReceived += OnPacketReceived; + _connection.Connected += OnConnectionOpened; + _connection.Disconnected += OnConnectionClosed; + } + + public void Open() + { + _connection.Connect(); + } + + public void Close(Exception error = null) + { + _connection.Close(error); + } + + public void Update() + { + _mapClient.Update(); + _connection.Update(); + if (!IsAuthenticated) + return; + + _movementTimeout.Update(); + if (!_battleTimeout.Update() && _askTeamAfterBattle) + { + RequestTeam(); + _askTeamAfterBattle = false; + } + + _loadingTimeout.Update(); + _mountingTimeout.Update(); + _teleportationTimeout.Update(); + _dialogTimeout.Update(); + _swapTimeout.Update(); + _itemUseTimeout.Update(); + _fishingTimeout.Update(); + _refreshingPCBox.Update(); + _moveRelearnerTimeout.Update(); + + SendRegularPing(); + UpdateMovement(); + UpdateScript(); + UpdatePlayers(); + UpdatePCBox(); + UpdateNpcBattle(); + } + + public void CloseChannel(string channelName) + { + if (Channels.Any(e => e.Name == channelName)) + { + SendMessage("/cgleave " + channelName); + } + } + + public void CloseConversation(string pmName) + { + if (Conversations.Contains(pmName)) + { + SendMessage("/pm rem " + pmName + "-=-" + PlayerName + '|' + PlayerName); + Conversations.Remove(pmName); + } + } + + private short _legitCounter = 0; + private uint _legitFlag = 1; + + private void SendRegularPing() + { + if ((DateTime.UtcNow - _lastMovement).TotalSeconds > 6.0 + || (DateTime.UtcNow - _lastVisPacket).TotalSeconds > 60.0) + { + _lastMovement = DateTime.UtcNow; + _lastVisPacket = DateTime.UtcNow; + + // DSSock.Update + int packetType; + + bool isPingSwapped; + if (_legitCounter == 4) + { + isPingSwapped = _legitFlag != 0; + _legitCounter = -1; + } + else + { + isPingSwapped = Rand.NextDouble() > 0.5; + } + + packetType = isPingSwapped ? 1 : 2; + + SendPacket(packetType.ToString()); + if (isPingSwapped) + _legitFlag ^= 1u; + _legitCounter++; + } + } + + private void UpdateMovement() + { + if (!IsMapLoaded) return; + + if (!_movementTimeout.IsActive && _movements.Count > 0) + { + if (GroundMount != null && !_itemUseTimeout.IsActive && !IsBiking && !IsSurfing && Map.IsOutside) + { + LogMessage?.Invoke($"Mounting [{GroundMount.Name}]"); + UseItem(GroundMount.Id); + return; + } + + Direction direction = _movements[0]; + _movements.RemoveAt(0); + + if (ApplyMovement(direction)) + { + SendMovement(direction.AsChar()); + _movementTimeout.Set(IsBiking ? 125 : 250); + if (Map.HasLink(PlayerX, PlayerY)) + { + _teleportationTimeout.Set(); + } + else + { + Npc battler = Map.Npcs.FirstOrDefault(npc => npc.CanBattle && npc.IsInLineOfSight(PlayerX, PlayerY, DisableMovingNpcInteractions)); + if (battler != null) + { + battler.CanBattle = false; + LogMessage?.Invoke("The NPC " + (battler.Name ?? battler.Id.ToString()) + " saw us, interacting..."); + _npcBattler = battler; + int distanceFromBattler = DistanceBetween(PlayerX, PlayerY, battler.PositionX, battler.PositionY); + _npcBattleTimeout.Set(Rand.Next(1000, 2000) + distanceFromBattler * 250); + ClearPath(); + } + } + } + + if (_movements.Count == 0 && _surfAfterMovement == 1) + { + _movementTimeout.Set(Rand.Next(750, 2000)); + } + } + if (!_movementTimeout.IsActive && _movements.Count == 0 && _surfAfterMovement == 1) + { + UseSurf(); + _surfAfterMovement = 2; + } + } + + private void UpdatePlayers() + { + if (_updatePlayers < DateTime.UtcNow) + { + foreach (string playerName in Players.Keys.ToArray()) + { + if (Players[playerName].IsExpired()) + { + PlayerRemoved?.Invoke(Players[playerName]); + Players.Remove(playerName); + } + } + _updatePlayers = DateTime.UtcNow.AddSeconds(5); + } + } + + private void UpdatePCBox() + { + // if we did not receive an answer, then the box is empty + if (IsPCBoxRefreshing && _refreshBoxTimeout > DateTime.UtcNow) + { + IsPCBoxRefreshing = false; + if (Map.IsPC(PlayerX, PlayerY - 1)) + { + IsPCOpen = true; + } + CurrentPCBox = new List(); + PCBoxUpdated?.Invoke(CurrentPCBox); + } + } + + private void UpdateNpcBattle() + { + if (_npcBattler == null) return; + + _npcBattleTimeout.Update(); + if (_npcBattleTimeout.IsActive) return; + + TalkToNpc(_npcBattler); + _npcBattler = null; + } + + private bool ApplyMovement(Direction direction) + { + int destinationX = PlayerX; + int destinationY = PlayerY; + bool isOnGround = IsOnGround; + bool isSurfing = IsSurfing; + + direction.ApplyToCoordinates(ref destinationX, ref destinationY); + + Map.MoveResult result = Map.CanMove(direction, destinationX, destinationY, isOnGround, isSurfing, CanUseCut, CanUseSmashRock); + if (Map.ApplyMovement(direction, result, ref destinationX, ref destinationY, ref isOnGround, ref isSurfing)) + { + PlayerX = destinationX; + PlayerY = destinationY; + IsOnGround = isOnGround; + IsSurfing = isSurfing; + PositionUpdated?.Invoke(MapName, PlayerX, PlayerY); + + if (result == Map.MoveResult.Icing) + { + _movements.Insert(0, direction); + } + + if (result == Map.MoveResult.Sliding) + { + int slider = Map.GetSlider(destinationX, destinationY); + if (slider != -1) + { + _slidingDirection = Map.SliderToDirection(slider); + } + } + + if (_slidingDirection != null) + { + _movements.Insert(0, _slidingDirection.Value); + } + + return true; + } + + _slidingDirection = null; + return false; + } + + private void UpdateScript() + { + if (IsScriptActive && !_dialogTimeout.IsActive) + { + if (ScriptStatus == 0) + { + _dialogResponses.Clear(); + IsScriptActive = false; + } + else if (ScriptStatus == 1) + { + SendDialogResponse(0); + _dialogTimeout.Set(); + } + else if (ScriptStatus == 3 || ScriptStatus == 4) + { + var response = GetNextDialogResponse(); + if (ScriptStatus == 4 && response >= 1 && response <= 6) + { + // Asking for a pokemon database id + SendDialogResponse(Team[response - 1].DatabaseId); + } + else + { + SendDialogResponse(response); + } + _dialogTimeout.Set(); + } + } + } + + private int GetNextDialogResponse() + { + if (_dialogResponses.Count > 0) + { + object response = _dialogResponses.Dequeue(); + if (response is int) + { + return (int)response; + } + else if (response is string) + { + string text = ((string)response).ToUpperInvariant(); + for (int i = 1; i < DialogContent.Length; ++i) + { + if (DialogContent[i].ToUpperInvariant().Equals(text)) + { + return i; + } + } + } + } + return 1; + } + + public int DistanceTo(int cellX, int cellY) + { + return Math.Abs(PlayerX - cellX) + Math.Abs(PlayerY - cellY); + } + + public static int DistanceBetween(int fromX, int fromY, int toX, int toY) + { + return Math.Abs(fromX - toX) + Math.Abs(fromY - toY); + } + + public void SendPacket(string packet) + { +#if DEBUG + Console.WriteLine("[>] " + packet); +#endif + _connection.Send(packet); + } + + public void SendMessage(string text) + { + // DSSock.sendMSG + SendPacket("{|.|" + text); + } + + public void SendPrivateMessage(string nickname, string text) + { + // DSSock.sendMSG + string pmHeader = "/pm " + PlayerName + "-=-" + nickname; + SendPacket("{|.|" + pmHeader + '|' + text); + } + + public void SendStartPrivateMessage(string nickname) + { + SendMessage("/pm " + PlayerName + "-=-" + nickname); + } + + public void SendFriendToggle(string nickname) + { + SendMessage("/friend " + nickname); + } + + public void SendIgnoreToggle(string nickname) + { + SendMessage("/ignore " + nickname); + } + + public void CreateCharacter(int hair, int colour, int tone, int clothe, int eyes) + { + if (!IsCreatingNewCharacter) return; + IsCreatingNewCharacter = false; + SendCreateCharacter(hair, colour, tone, clothe, eyes); + _dialogTimeout.Set(); + } + + private void SendCreateCharacter(int hair, int colour, int tone, int clothe, int eyes) + { + SendMessage("/setchar " + hair + "," + colour + "," + tone + "," + clothe + "," + eyes); + } + + public void SendAuthentication(string username, string password, Guid deviceId, string osInfo) + { + // DSSock.AttemptLogin + SendPacket("+|.|" + username + + "|.|" + password + + "|.|" + Version + + "|.|" + + Encryption.ReplaceSumByte(deviceId.ToString()) + + "|.|" + osInfo); + } + + public void SendUseItem(int id, int pokemonDBId = 0, int moveIndex = 0) + { + string toSend = "*|.|" + id; + if (pokemonDBId != 0) + { + toSend += "|.|" + pokemonDBId; + } + if (moveIndex != 0) + { + toSend += "|.|" + moveIndex; + } + SendPacket(toSend); + } + + public void SendGiveItem(int pokemonDBid, int itemId) + { + //<|.|32683455|441 + SendPacket("<|.|" + pokemonDBid + "|" + itemId); + } + + public void SendTakeItem(int pokemonDBid) + { + //>|.|32683455 + SendPacket(">|.|" + pokemonDBid); + } + + public void LearnMove(int pokemonDBid, int moveToForgetUid) + { + _swapTimeout.Set(); + SendLearnMove(pokemonDBid, moveToForgetUid); + } + + private void SendLearnMove(int pokemonDBid, int moveToForgetUid) + { + SendPacket("^|.|" + pokemonDBid + "|.|" + moveToForgetUid); + } + + private void SendMovePokemonToPC(int pokemonUid) + { + SendPacket("?|.|" + pokemonUid + "|.|-1"); + } + + // if there is a pokemon in teamSlot, it will be swapped + private void SendMovePokemonFromPC(int pokemonUid, int teamSlot) + { + SendPacket("?|.|" + pokemonUid + "|.|" + teamSlot); + } + + private void SendRefreshPCBox(int box, string search) + { + SendPacket("M|.|" + box + "|.|" + search); + } + + private void SendReleasePokemon(int pokemonDBid) + { + SendPacket("e|.|" + pokemonDBid); + } + + private void SendPrivateMessageOn() + { + SendMessage("/pmon"); + } + + private void SendPrivateMessageOff() + { + SendMessage("/pmoff"); + } + + private void SendPrivateMessageAway() + { + SendMessage("/pmaway"); + } + + private void SendRequestGuildData(int id) + { + SendPacket(":|.|" + id); + } + + public bool PrivateMessageOn() + { + IsPrivateMessageOn = true; + SendPrivateMessageOn(); + return true; + } + + public bool PrivateMessageOff() + { + IsPrivateMessageOn = false; + SendPrivateMessageOff(); + return true; + } + + // /pmaway does not seem to do anything + public bool PrivateMessageAway() + { + SendPrivateMessageAway(); + return true; + } + + public bool ReleasePokemonFromPC(int boxId, int boxPokemonId) + { + if (!IsPCOpen || IsPCBoxRefreshing || boxId < 1 || boxId > 67 + || boxPokemonId < 1 || boxPokemonId > 15 || boxPokemonId > CurrentPCBox.Count) + { + return false; + } + /*int pokemonUid = GetPokemonPCUid(boxId, boxPokemonId); + if (pokemonUid == -1 || pokemonUid != CurrentPCBox[boxPokemonId - 1].Uid) + { + return false; + }*/ + _refreshingPCBox.Set(Rand.Next(1500, 2000)); + SendReleasePokemon(CurrentPCBox[boxPokemonId - 1].DatabaseId); + return true; + } + + public bool ReleasePokemonFromTeam(int pokemonUid) + { + if (!IsPCOpen || IsPCBoxRefreshing + || pokemonUid < 1 || pokemonUid > 6 || pokemonUid > Team.Count) + { + return false; + } + _refreshingPCBox.Set(Rand.Next(1500, 2000)); + SendReleasePokemon(Team[pokemonUid - 1].DatabaseId); + return true; + } + + public bool RefreshPCBox(int boxId) + { + if (!IsPCOpen || boxId < 1 || boxId > 67 || _refreshingPCBox.IsActive || IsPCBoxRefreshing) + { + return false; + } + _refreshingPCBox.Set(Rand.Next(1500, 2000)); // this is the amount of time we wait for an answer + CurrentPCBoxId = boxId; + IsPCBoxRefreshing = true; + CurrentPCBox = null; + _refreshBoxTimeout = DateTime.UtcNow.AddSeconds(5); // this is to avoid a flood of the function + SendRefreshPCBox(boxId - 1, "ID"); + return true; + } + + public bool RefreshCurrentPCBox() + { + return RefreshPCBox(CurrentPCBoxId); + } + + /*private int GetPokemonPCUid(int box, int id) + { + if (box < 1 || box > 67 || id < 1 || id > 15) + { + return -1; + } + int result = (box - 1) * 15 + 6 + id; + // ensures we cannot access a pokemon we do not have or know + if (CurrentPCBox == null || CurrentPCBox[id - 1].Uid != result || box != CurrentPCBoxId) + { + return -1; + } + return result; + }*/ + + public bool DepositPokemonToPC(int pokemonUid) + { + if (!IsPCOpen || pokemonUid < 1 || pokemonUid > 6 || Team.Count < pokemonUid) + { + return false; + } + SendMovePokemonToPC(pokemonUid); + return true; + } + + public bool WithdrawPokemonFromPC(int boxId, int boxPokemonId) + { + if (boxId != CurrentPCBoxId) + { + return false; + } + int pcPokemonUid = CurrentPCBox[boxPokemonId - 1].Uid; + if (!IsPCOpen || pcPokemonUid < 7 || Team.Count >= 6) + { + return false; + } + SendMovePokemonFromPC(pcPokemonUid, Team.Count + 1); + return true; + } + + public bool SwapPokemonFromPC(int boxId, int boxPokemonId, int teamPokemonUid) + { + if (boxId != CurrentPCBoxId) + { + return false; + } + int pcPokemonUid = CurrentPCBox[boxPokemonId - 1].Uid; + if (!IsPCOpen || pcPokemonUid < 7 || teamPokemonUid < 1 + || teamPokemonUid > 6 || Team.Count < teamPokemonUid) + { + return false; + } + SendMovePokemonFromPC(pcPokemonUid, teamPokemonUid); + return true; + } + + public bool SwapPokemon(int pokemon1, int pokemon2) + { + if (IsInBattle || pokemon1 < 1 || pokemon2 < 1 || Team.Count < pokemon1 || Team.Count < pokemon2 || pokemon1 == pokemon2) + { + return false; + } + if (!_swapTimeout.IsActive) + { + SendSwapPokemons(pokemon1, pokemon2); + _swapTimeout.Set(); + return true; + } + return false; + } + + public void Move(Direction direction) + { + _movements.Add(direction); + } + + public void RequestResync() + { + SendMessage("/syn"); + _teleportationTimeout.Set(); + } + + public void RequestTeam() + { + SendPacket("_"); + } + + public void RequestSpawnList() + { + SendPacket("k|.|" + MapName.ToLowerInvariant()); + } + + public void UseAttack(int number) + { + SendAttack(number.ToString()); + _battleTimeout.Set(); + } + + public void UseItem(int id, int pokemonUid = 0, int moveIndex = 0) + { + if (!(pokemonUid >= 0 && pokemonUid <= 6) || !HasItemId(id)) + { + return; + } + InventoryItem item = GetItemFromId(id); + if (item == null || item.Quantity == 0 || (item.Scope == 17 && moveIndex <= 0)) + { + return; + } + if (pokemonUid == 0) // simple use + { + if (!_itemUseTimeout.IsActive && !IsInBattle && item.CanBeUsedOutsideOfBattle) + { + SendUseItem(id); + _itemUseTimeout.Set(); + } + else if (!_battleTimeout.IsActive && IsInBattle && item.CanBeUsedInBattle) + { + SendAttack("item" + id); + _battleTimeout.Set(); + } + } + else // use item on pokemon + { + if (!_itemUseTimeout.IsActive && !IsInBattle && item.CanBeUsedOnPokemonOutsideOfBattle) + { + SendUseItem(id, Team[pokemonUid - 1].DatabaseId, moveIndex); + _itemUseTimeout.Set(); + } + else if (!_battleTimeout.IsActive && IsInBattle && item.CanBeUsedOnPokemonInBattle) + { + //(|.|item528:28118282:3||.\ + string bItemCmd = "item" + id + ":" + Team[pokemonUid - 1].DatabaseId; + if (moveIndex != 0) + bItemCmd += ":" + moveIndex; + SendAttack(bItemCmd); + _battleTimeout.Set(); + } + } + } + + public bool GiveItemToPokemon(int pokemonUid, int itemId) + { + if (!(pokemonUid >= 1 && pokemonUid <= Team.Count)) + { + return false; + } + InventoryItem item = GetItemFromId(itemId); + if (item == null || item.Quantity == 0) + { + return false; + } + if (!_itemUseTimeout.IsActive && !IsInBattle && item.CanBeHeld) + { + SendGiveItem(Team[pokemonUid - 1].DatabaseId, itemId); + _itemUseTimeout.Set(); + return true; + } + return false; + } + + public bool TakeItemFromPokemon(int pokemonUid) + { + if (!(pokemonUid >= 1 && pokemonUid <= Team.Count)) + { + return false; + } + if (!_itemUseTimeout.IsActive && Team[pokemonUid - 1].ItemHeld != "") + { + SendTakeItem(Team[pokemonUid - 1].DatabaseId); + _itemUseTimeout.Set(); + return true; + } + return false; + } + + public bool HasSurfAbility() + { + return (HasMove("Surf") || WaterMount != null) && + (Map.Region == "1" && HasItemName("Soul Badge") || + Map.Region == "2" && HasItemName("Fog Badge") || + Map.Region == "3" && HasItemName("Balance Badge") || + Map.Region == "4" && HasItemName("Relic Badge")); + } + + public bool HasCutAbility() + { + return (HasMove("Cut") || HasTreeaxe()) && + (Map.Region == "1" && HasItemName("Cascade Badge") || + Map.Region == "2" && HasItemName("Hive Badge") || + Map.Region == "3" && HasItemName("Stone Badge") || + Map.Region == "4" && HasItemName("Forest Badge")); + } + + public bool HasRockSmashAbility() + { + return HasMove("Rock Smash") || HasPickaxe(); + } + + public bool HasTreeaxe() + { + return HasItemId(838) && HasItemId(317); + } + + public bool HasPickaxe() + { + return HasItemId(839); + } + + public bool PokemonUidHasMove(int pokemonUid, string moveName) + { + return Team.FirstOrDefault(p => p.Uid == pokemonUid)?.Moves.Any(m => m.Name?.Equals(moveName, StringComparison.InvariantCultureIgnoreCase) ?? false) ?? false; + } + + public Pokemon GetPokemonFromDBId(int pokemonDBId) => Team.Find(pokemon => pokemon.DatabaseId == pokemonDBId); + + public bool HasMove(string moveName) + { + return Team.Any(p => p.Moves.Any(m => m.Name?.Equals(moveName, StringComparison.InvariantCultureIgnoreCase) ?? false)); + } + + public int GetMovePosition(int pokemonUid, string moveName) + { + return Team[pokemonUid].Moves.FirstOrDefault(m => m.Name?.Equals(moveName, StringComparison.InvariantCultureIgnoreCase) ?? false)?.Position ?? -1; + } + + public InventoryItem GetItemFromId(int id) + { + return Items.Find(i => i.Id == id && i.Quantity > 0); + } + + public bool HasItemId(int id) + { + return GetItemFromId(id) != null; + } + + public InventoryItem GetItemFromName(string itemName) + { + return Items.Find(i => i.Name?.Equals(itemName, StringComparison.InvariantCultureIgnoreCase) == true + && i.Quantity > 0); + } + + public bool HasItemName(string itemName) + { + return GetItemFromName(itemName) != null; + } + + public bool HasPokemonInTeam(string pokemonName) + { + return FindFirstPokemonInTeam(pokemonName) != null; + } + + public Pokemon FindFirstPokemonInTeam(string pokemonName) + { + return Team.Find(p => p.Name.Equals(pokemonName, StringComparison.InvariantCultureIgnoreCase)); + } + + public void UseSurf() + { + if (WaterMount == null) + { + SendPacket("w|.|/surf"); + } + else + { + LogMessage?.Invoke($"Mounting [{WaterMount.Name}]"); + UseItem(WaterMount.Id); + } + + _mountingTimeout.Set(); + } + + public void UseSurfAfterMovement() + { + _surfAfterMovement = 1; + } + + public void RunFromBattle() + { + UseAttack(5); + } + + public void ChangePokemon(int number) + { + UseAttack(number + 5); + } + + public void TalkToNpc(Npc npc) + { + npc.CanBattle = false; + + SendTalkToNpc(npc.Id); + _dialogTimeout.Set(); + } + + public bool OpenPC() + { + if (!Map.IsPC(PlayerX, PlayerY - 1)) + { + return false; + } + IsPCOpen = true; + return RefreshPCBox(1); + } + + public void PushDialogAnswer(int index) + { + _dialogResponses.Enqueue(index); + } + + public void PushDialogAnswer(string text) + { + _dialogResponses.Enqueue(text); + } + + public bool BuyItem(int itemId, int quantity) + { + if (OpenedShop != null && OpenedShop.Items.Any(item => item.Id == itemId)) + { + _itemUseTimeout.Set(); + SendShopPokemart(OpenedShop.Id, itemId, quantity); + return true; + } + return false; + } + + public bool PurchaseMove(int moveId) + { + if (MoveRelearner != null && + MoveRelearner.Moves.Any(m => MovesManager.Instance.GetMoveId(m.Name) == moveId)) + { + _moveRelearnerTimeout.Set(); + SendPurchaseMove(MoveRelearner.SelectedPokemonUid, moveId); + return true; + } + return false; + } + + private void SendPurchaseMove(int pokemonUid, int moveId) + { + // DSSock.cs handles Move Relearn as below. + if (MoveRelearner != null) + { + if (MoveRelearner.IsEggMoves) + { + SendPacket("b|.|" + pokemonUid + "|.|" + moveId); + } + else + { + SendPacket("z|.|" + pokemonUid + "|.|" + moveId); + } + } + } + + private void MapClient_ConnectionOpened() + { +#if DEBUG + Console.WriteLine("[+++] Connecting to the game server"); +#endif + if (MapName != null && Map == null) + { + _mapClient.DownloadMap(MapName); + } + } + + private void MapClient_ConnectionFailed(Exception ex) + { + ConnectionFailed?.Invoke(ex); + } + + private void MapClient_ConnectionClosed(Exception ex) + { + Close(ex); + } + + private void MapClient_MapLoaded(string mapName, Map map) + { + if (mapName == MapName) + { + Players.Clear(); + + Map = map; + // DSSock.loadMap + SendPacket("-"); + RequestSpawnList(); + + CanUseCut = HasCutAbility(); + CanUseSmashRock = HasRockSmashAbility(); + + MapLoaded?.Invoke(MapName); + } + else + { + InvalidPacket?.Invoke(mapName, "Received a map that is not the current map"); + } + } + + private void OnPacketReceived(string packet) + { + ProcessPacket(packet); + } + + private void OnConnectionOpened() + { + IsConnected = true; +#if DEBUG + Console.WriteLine("[+++] Connection opened"); +#endif + ConnectionOpened?.Invoke(); + } + + private void OnConnectionClosed(Exception ex) + { + _mapClient.Close(); + if (!IsConnected) + { +#if DEBUG + Console.WriteLine("[---] Connection failed"); +#endif + ConnectionFailed?.Invoke(ex); + } + else + { + IsConnected = false; +#if DEBUG + Console.WriteLine("[---] Connection closed"); +#endif + ConnectionClosed?.Invoke(ex); + } + } + + private void SendMovement(string direction) + { + _lastMovement = DateTime.UtcNow; + // Consider the pokemart closed after the first movement. + OpenedShop = null; + MoveRelearner = null; + IsPCOpen = false; + // DSSock.sendMove + SendPacket("#|.|" + direction); + } + + private void SendAttack(string number) + { + // DSSock.sendAttack + // DSSock.RunButton + SendPacket("(|.|" + number); + } + + private void SendTalkToNpc(int npcId) + { + // DSSock.Interact + SendPacket("N|.|" + npcId); + } + + private void SendDialogResponse(int number) + { + // DSSock.ClickButton + SendPacket("R|.|" + number); + } + + public void SendAcceptEvolution(int evolvingPokemonDBid) + { + // DSSock.AcceptEvo + SendPacket("h|.|" + evolvingPokemonDBid); + } + + public void SendCancelEvolution(int evolvingPokemonDBid) + { + // DSSock.CancelEvo + SendPacket("j|.|" + evolvingPokemonDBid); + } + + private void SendSwapPokemons(int pokemon1, int pokemon2) + { + SendPacket("?|.|" + pokemon2 + "|.|" + pokemon1); + } + + private void SendShopPokemart(int shopId, int itemId, int quantity) + { + SendPacket("c|.|" + shopId + "|.|" + itemId + "|.|" + quantity); + } + + private void ProcessPacket(string packet) + { +#if DEBUG + Console.WriteLine(packet); +#endif + + if (packet.Substring(0, 1) == "U") + { + packet = "U|.|" + packet.Substring(1); + } + + string[] data = packet.Split(new [] { "|.|" }, StringSplitOptions.None); + string type = data[0].ToLowerInvariant(); + try + { + switch (type) + { + case "5": + OnLoggedIn(data); + break; + case "6": + OnAuthenticationResult(data); + break; + case "l": + //Move relearn content + OnMoveRelearn(data); + break; + case ")": + OnQueueUpdated(data); + break; + case "q": + OnPlayerPosition(data); + break; + case "s": + OnPlayerSync(data); + break; + case "i": + OnPlayerInfos(data); + break; + case "(": + // CDs ? + break; + case "e": + OnUpdateTime(data); + break; + case "@": + OnNpcBattlers(data); + break; + case "#": + OnTeamUpdate(data); + break; + case "d": + OnInventoryUpdate(data); + break; + case "&": + OnItemsUpdate(data); + break; + case "!": + OnBattleJoin(packet); + break; + case "a": + OnBattleMessage(data); + break; + case "r": + OnScript(data); + break; + case "$": + OnMountUpdate(data); + break; + case "%": + OnSurfingUpdate(data); + break; + case "^": + OnLearningMove(data); + break; + case "h": + OnEvolving(data); + break; + case "=": + OnUpdatePlayer(data); + break; + case "c": + OnChannels(data); + break; + case "w": + OnChatMessage(data); + break; + case "o": + // Shop content + break; + case "pm": + OnPrivateMessage(data); + break; + case ".": + // DSSock.ProcessCommands + RequestTeam(); + break; + case "'": + // DSSock.ProcessCommands + SendPacket("'"); + break; + case "m": + OnPCBox(data); + break; + case "z": + OnPlayerMovement(data); + break; + case "y": + OnGuildData(data); + break; + default: +#if DEBUG + Console.WriteLine(" ^ unhandled /!\\"); +#endif + break; + } + } + catch(System.FormatException) + { + LogMessage?.Invoke("Format error occurred(Probably server issue): " + packet); + } + } + + + private void OnLoggedIn(string[] data) + { + Console.WriteLine("[Login] Authenticated successfully, connecting to map server"); + + IsCreatingNewCharacter = data[1] == "1"; + + string[] mapServerHost = data[2].Split(':'); + + _mapClient.Open(Server.GetMapAddress(), int.Parse(mapServerHost[1])); + + // DSSock.ProcessCommands + SendMessage("/in1"); + // TODO: Add a setting to disable the party inspection (send /in0 instead). + SendPacket(")"); + RequestTeam(); + SendPacket("g"); + SendPacket("p|.|l|0"); + SendRegularPing(); + IsAuthenticated = true; + + LoggedIn?.Invoke(); + } + + private void OnAuthenticationResult(string[] data) + { + //AuthenticationResult result = (AuthenticationResult)Convert.ToInt32(data[1]); + + if (/*result != AuthenticationResult.ServerFull*/ + !data[2].ToLowerInvariant().Contains("full")) + { + AuthenticationFailed?.Invoke(data[2]); + Close(); + } + } + + private void OnQueueUpdated(string[] data) + { + string[] queueData = data[1].Split('|'); + + int position = Convert.ToInt32(queueData[0]); + QueueUpdated?.Invoke(position); + } + + private void OnPlayerPosition(string[] data) + { + string[] mapData = data[1].Split(new[] { "|" }, StringSplitOptions.None); + string map = mapData[0]; + int playerX = Convert.ToInt32(mapData[1]); + int playerY = Convert.ToInt32(mapData[2]); + if (playerX != PlayerX || playerY != PlayerY || map != MapName) + { + TeleportationOccuring?.Invoke(map, playerX, playerY); + + PlayerX = playerX; + PlayerY = playerY; + LoadMap(map); + IsOnGround = (mapData[3] == "1"); + if (Convert.ToInt32(mapData[4]) == 1) + { + IsSurfing = true; + IsBiking = false; + } + // DSSock.sendSync + SendPacket("S"); + } + + PositionUpdated?.Invoke(MapName, PlayerX, playerY); + + _teleportationTimeout.Cancel(); + } + + // Server sends some movement data to move the character. + private void OnPlayerMovement(string[] data) + { + _dialogTimeout.Set(); + _movements.Clear(); + string[] movements = data[1].Split(new[] { "|" }, StringSplitOptions.None); + foreach(var movement in movements) + { + Move(DirectionExtensions.FromChar(movement[0])); + } + + _teleportationTimeout.Cancel(); + } + + private void OnPlayerSync(string[] data) + { + string[] mapData = data[1].Split(new[] { "|" }, StringSplitOptions.None); + + if (mapData.Length < 2) + return; + + string map = mapData[0]; + int playerX = Convert.ToInt32(mapData[1]); + int playerY = Convert.ToInt32(mapData[2]); + if (map.Length > 1) + { + PlayerX = playerX; + PlayerY = playerY; + LoadMap(map); + } + IsOnGround = (mapData[3] == "1"); + + PositionUpdated?.Invoke(MapName, PlayerX, playerY); + } + + private void OnPlayerInfos(string[] data) + { + string[] playerData = data[1].Split('|'); + PlayerName = playerData[0]; + PokedexOwned = Convert.ToInt32(playerData[4]); + PokedexSeen = Convert.ToInt32(playerData[5]); + PokedexEvolved = Convert.ToInt32(playerData[6]); + IsMember = playerData[10] == "1"; + if (GuildId != -1 && !_requestedGuildData.Contains(GuildId)) + { + SendRequestGuildData(GuildId); + _requestedGuildData.Add(GuildId); + } + } + + private void OnGuildData(string[] data) + { + //y|.|Guild name|999(id)|guild description|total members format: (total/max)|Leader Name|.\ + data = data[1].Split('|'); + if (data.Length > 1) + { + GuildId = int.Parse(data[1]); + } + } + + private void OnUpdateTime(string[] data) + { + string[] timeData = data[1].Split('|'); + + PokemonTime = timeData[0]; + + Weather = timeData[1]; + + PokeTimeUpdated?.Invoke(PokemonTime, Weather); + } + + private void OnNpcBattlers(string[] data) + { + if (!IsMapLoaded) return; + + var npcData = data[1].Split('*'); + var defeatedNpcs = npcData[0].Split(new[] { "|" }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); + var destroyedNpcs = npcData[1].Split(new[] { "|" }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse); + + Map.Npcs.Clear(); + foreach (Npc npc in Map.OriginalNpcs) + { + if (!destroyedNpcs.Contains(npc.Id)) + { + Npc clone = npc.Clone(); + if (defeatedNpcs.Contains(npc.Id)) + clone.CanBattle = false; + + Map.Npcs.Add(clone); + } + } + + AreNpcReceived = true; + NpcReceived?.Invoke(Map.Npcs); + } + + private void OnTeamUpdate(string[] data) + { + string[] teamData = data[1].Split(new[] { "\r\n" }, StringSplitOptions.None); + + Team.Clear(); + foreach (string pokemon in teamData) + { + if (pokemon == string.Empty) + continue; + + string[] pokemonData = pokemon.Split('|'); + + Team.Add(new Pokemon(pokemonData)); + } + + if (IsMapLoaded) + { + CanUseCut = HasCutAbility(); + CanUseSmashRock = HasRockSmashAbility(); + } + + if (_swapTimeout.IsActive) + { + _swapTimeout.Set(Rand.Next(500, 1000)); + } + PokemonsUpdated?.Invoke(); + } + + private void OnInventoryUpdate(string[] data) + { + Money = Convert.ToInt32(data[1]); + Coins = Convert.ToInt32(data[2]); + UpdateItems(data[3]); + } + + private void OnItemsUpdate(string[] data) + { + UpdateItems(data[1]); + } + + private void UpdateItems(string content) + { + Items.Clear(); + + string[] itemsData = content.Split(new[] { "\r\n" }, StringSplitOptions.None); + foreach (string item in itemsData) + { + if (item == string.Empty) + continue; + string[] itemData = item.Split(new[] { "|" }, StringSplitOptions.None); + Items.Add(new InventoryItem(Convert.ToInt32(itemData[0]), Convert.ToInt32(itemData[1]), Convert.ToInt32(itemData[2]))); + } + + if (_itemUseTimeout.IsActive) + { + _itemUseTimeout.Set(Rand.Next(500, 1000)); + } + InventoryUpdated?.Invoke(); + } + + private void OnBattleJoin(string packet) + { + string[] data = packet.Substring(4).Split('|'); + + IsScriptActive = false; + + IsInBattle = true; + ActiveBattle = new Battle(PlayerName, data); + ActiveBattle.ActivePokemonChanged += ActivePokemonChanged; + ActiveBattle.OpponentChanged += OpponentChanged; + + _movements.Clear(); + _slidingDirection = null; + + _battleTimeout.Set(Rand.Next(4000, 6000)); + _fishingTimeout.Cancel(); + + BattleStarted?.Invoke(); + + string[] battleMessages = ActiveBattle.BattleText.Split(new[] { "\r\n" }, StringSplitOptions.None); + + foreach (string message in battleMessages) + { + if (!ActiveBattle.ProcessMessage(Team, message)) + { + BattleMessage?.Invoke(I18n.Replace(message)); + } + } + + if (!ActiveBattle.AlreadyCaught) + { + RequestSpawnList(); + } + + BattleUpdated?.Invoke(); + } + + private void OnBattleMessage(string[] data) + { + if (!IsInBattle) + { + return; + } + + string[] battleData = data[1].Split(new string[] { "|" }, StringSplitOptions.None); + string[] battleMessages = battleData[4].Split(new string[] { "\r\n" }, StringSplitOptions.None); + + foreach (string message in battleMessages) + { + if (!ActiveBattle.ProcessMessage(Team, message)) + { + BattleMessage?.Invoke(I18n.Replace(message)); + } + } + + PokemonsUpdated?.Invoke(); + BattleUpdated?.Invoke(); + + if (ActiveBattle.IsFinished) + { + _battleTimeout.Set(Rand.Next(2500, 5000)); + } + else + { + _battleTimeout.Set(Rand.Next(2000, 4000)); + } + + if (ActiveBattle.IsFinished) + { + IsInBattle = false; + ActiveBattle = null; + BattleEnded?.Invoke(); + _askTeamAfterBattle = true; + } + } + + private void OnScript(string[] data) + { + string id = data[2]; + int status = Convert.ToInt32(data[1]); + string script = data[3]; + + DialogContent = script.Split(new string[] { "-#-" }, StringSplitOptions.None); + bool isPrompt = script.Contains("-#-") && status > 1; + if (isPrompt) + { + script = DialogContent[0]; + } + string[] messages = script.Split(new string[] { "-=-" }, StringSplitOptions.RemoveEmptyEntries); + for (int i = 0; i < messages.Length; i++) + { + string message = messages[i]; + if (message.StartsWith("emote") || message.StartsWith("playsound") || message.StartsWith("playmusic") || message.StartsWith("playcry")) + continue; + if (message.StartsWith("shop")) + { + OpenedShop = new Shop(message.Substring(4)); + ShopOpened?.Invoke(OpenedShop); + continue; + } + if (message.StartsWith("moverelearner")) + { + int pokemonUid = Convert.ToInt32(message.Substring(13)); + MoveRelearner = new MoveRelearner(pokemonUid, false); + + SendPacket("a|.|" + pokemonUid); + continue; + } + if (message.StartsWith("eggsrelearner")) + { + int pokemonUid = Convert.ToInt32(message.Substring(13)); + MoveRelearner = new MoveRelearner(pokemonUid, true); + + SendPacket(".|.|" + message.Substring(13)); + continue; + } + + bool lastMessage = (i == messages.Length - 1); + if (lastMessage && isPrompt) + { + var dialogOptions = new string[DialogContent.Length - 1]; + Array.Copy(DialogContent, 1, dialogOptions, 0, dialogOptions.Length); + DialogOpened?.Invoke(message, dialogOptions); + } + else + { + DialogOpened?.Invoke(message, new string[0]); + } + } + + IsScriptActive = true; + _dialogTimeout.Set(Rand.Next(1500, 4000)); + ScriptId = id; + ScriptStatus = status; + } + + private void OnMoveRelearn(string[] data) + { + if (MoveRelearner != null) + { + MoveRelearner.ProcessMessage(data[1]); + MoveRelearnerOpened?.Invoke(MoveRelearner); + } + } + + private void OnMountUpdate(string[] data) + { + MountId = int.Parse(data[1]); + if (MountId != 0) + { + IsBiking = !(_surfAfterMovement == 2); + IsSurfing = _surfAfterMovement == 2; + } + else + { + IsBiking = false; + IsSurfing = false; + } + if (IsSurfing) + { + Move(Map.GetWaterDirectionFrom(PlayerX, PlayerY)); + } + _surfAfterMovement = 0; + _mountingTimeout.Set(Rand.Next(500, 1000)); + _itemUseTimeout.Cancel(); + } + + private void OnSurfingUpdate(string[] data) + { + if (data[1] == "1") + { + IsSurfing = true; + IsBiking = false; + } + else + { + IsSurfing = false; + } + _mountingTimeout.Set(Rand.Next(500, 1000)); + _itemUseTimeout.Cancel(); + } + + private void OnLearningMove(string[] data) + { + int moveId = Convert.ToInt32(data[1]); + string moveName = Convert.ToString(data[2]); + int pokemonDBid = Convert.ToInt32(data[3]); + int movePp = Convert.ToInt32(data[4]); + LearningMove?.Invoke(moveId, moveName, pokemonDBid); + MoveRelearner = null; + _itemUseTimeout.Cancel(); + _moveRelearnerTimeout.Cancel(); + // ^|.|348|.|Cut|.|26703356|.|30 + } + + private void OnEvolving(string[] data) + { + int evlovingPokemonDBid = Convert.ToInt32(data[1]); + int evolvingItem = Convert.ToInt32(data[2]); + + // h|.|41258652|.|178 + // ^^ Data base id + Evolving.Invoke(evlovingPokemonDBid, evolvingItem); + } + + private void OnUpdatePlayer(string[] data) + { + string[] updateData = data[1].Split('|'); + + bool isNewPlayer = false; + PlayerInfos player; + DateTime expiration = DateTime.UtcNow.AddSeconds(20); + if (Players.ContainsKey(updateData[0])) + { + player = Players[updateData[0]]; + player.Expiration = expiration; + } + else + { + isNewPlayer = true; + player = new PlayerInfos(expiration); + player.Name = updateData[0]; + } + + player.Updated = DateTime.UtcNow; + player.PosX = Convert.ToInt32(updateData[1]); + player.PosY = Convert.ToInt32(updateData[2]); + player.Direction = updateData[3][0]; + player.Skin = updateData[3].Substring(1); + player.IsAfk = updateData[4][0] != '0'; + player.IsInBattle = updateData[4][1] != '0'; + player.PokemonPetId = Convert.ToInt32(updateData[4].Substring(2)); + player.IsPokemonPetShiny = updateData[5][0] != '0'; + player.IsMember = updateData[5][1] != '0'; + player.IsOnground = updateData[5][2] != '0'; + player.GuildId = Convert.ToInt32(updateData[5].Substring(3)); + player.PetForm = Convert.ToInt32(updateData[6]); // ??? + + Players[player.Name] = player; + + if (isNewPlayer) + { + PlayerAdded?.Invoke(player); + if (!_requestedGuildData.Contains(player.GuildId) && player.GuildId != 0) + { + SendRequestGuildData(player.GuildId); + _requestedGuildData.Add(player.GuildId); + } + } + else + { + PlayerUpdated?.Invoke(player); + } + } + + private void OnChannels(string[] data) + { + Channels.Clear(); + string[] channelsData = data[1].Split('|'); + for (int i = 1; i < channelsData.Length; i += 2) + { + string channelId = channelsData[i]; + string channelName = channelsData[i + 1]; + Channels.Add(new ChatChannel(channelId, channelName)); + } + RefreshChannelList?.Invoke(); + } + + private void OnChatMessage(string[] data) + { + string fullMessage = data[1]; + string[] chatData = fullMessage.Split(':'); + + if (fullMessage[0] == '*' && fullMessage[2] == '*') + { + fullMessage = fullMessage.Substring(3); + } + + string message; + if (chatData.Length <= 1) // we are not really sure what this stands for + { + string channelName; + + int start = fullMessage.IndexOf('(') + 1; + int end = fullMessage.IndexOf(')'); + if (fullMessage.Length <= end || start == 0 || end == -1) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Channel System Message with invalid channel"); + channelName = ""; + } + else + { + channelName = fullMessage.Substring(start, end - start); + } + + if (fullMessage.Length <= end + 2 || start == 0 || end == -1) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Channel System Message with invalid message"); + message = ""; + } + else + { + message = fullMessage.Substring(end + 2); + } + + ChannelSystemMessage?.Invoke(channelName, message); + return; + } + if (chatData[0] != "*G*System") + { + string channelName = null; + string mode = null; + string author; + + int start = (fullMessage[0] == '(' ? 1 : 0); + int end; + if (start != 0) + { + end = fullMessage.IndexOf(')'); + if (end != -1 && end - start > 0) + { + channelName = fullMessage.Substring(start, end - start); + } + else + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Channel Message with invalid channel name"); + channelName = ""; + } + } + start = fullMessage.IndexOf('[') + 1; + if (start != 0 && fullMessage[start] != 'n') + { + end = fullMessage.IndexOf(']'); + if (end == -1) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Message with invalid mode"); + message = ""; + } + mode = fullMessage.Substring(start, end - start); + } + string conversation = null; + if (channelName == "PM") + { + end = fullMessage.IndexOf(':'); + string header = ""; + if (end == -1) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Channel Private Message with invalid author"); + conversation = ""; + } + else + { + header = fullMessage.Substring(0, end); + start = header.LastIndexOf(' ') + 1; + if (end == -1) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Channel Private Message with invalid author"); + conversation = ""; + } + else + { + conversation = header.Substring(start); + } + } + if (header.Contains(" to ")) + { + author = PlayerName; + } + else + { + author = conversation; + } + } + else + { + start = fullMessage.IndexOf("[n=") + 3; + end = fullMessage.IndexOf("][/n]:"); + if (end == -1) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Message with invalid author"); + author = ""; + } + else + { + author = fullMessage.Substring(start, end - start); + } + } + start = fullMessage.IndexOf(':') + 2; + if (end == -1) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Channel Private Message with invalid message"); + message = ""; + } + else + { + message = fullMessage.Substring(start == 1 ? 0 : start); + } + if (channelName != null) + { + if (channelName == "PM") + { + ChannelPrivateMessage?.Invoke(conversation, mode, author, message); + } + else + { + ChannelMessage?.Invoke(channelName, mode, author, message); + } + } + else + { + if (message.IndexOf("em(") == 0) + { + end = message.IndexOf(")"); + int emoteId; + if (end != -1 && end - 3 > 0) + { + string emoteIdString = message.Substring(3, end - 3); + if (int.TryParse(emoteIdString, out emoteId) && emoteId > 0) + { + EmoteMessage?.Invoke(mode, author, emoteId); + return; + } + } + } + ChatMessage?.Invoke(mode, author, message); + } + return; + } + + int offset = fullMessage.IndexOf(':') + 2; + if (offset == -1 + 2) // for clarity... I prefectly know it's -3 + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "Channel Private Message with invalid author"); + message = ""; + } + else + { + message = fullMessage.Substring(offset == 1 ? 0 : offset); + } + + if (message.Contains("$YouUse the ") && message.Contains("Rod!")) + { + _itemUseTimeout.Cancel(); + _fishingTimeout.Set(2500 + Rand.Next(500, 1500)); + } + + SystemMessage?.Invoke(I18n.Replace(message)); + } + + private void OnPrivateMessage(string[] data) + { + if (data.Length < 2) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "PM with no parameter"); + } + string[] nicknames = data[1].Split(new[] { "-=-" }, StringSplitOptions.None); + if (nicknames.Length < 2) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "PM with invalid header"); + return; + } + + string conversation; + if (nicknames[0] != PlayerName) + { + conversation = nicknames[0]; + } + else + { + conversation = nicknames[1]; + } + + if (data.Length < 3) + { + string packet = string.Join("|.|", data); + InvalidPacket?.Invoke(packet, "PM without a message"); + /* + * the PM is sent since the packet is still understandable + * however, PRO client does not allow it + */ + PrivateMessage?.Invoke(conversation, null, conversation + " (deduced)", ""); + return; + } + + string mode = null; + int offset = data[2].IndexOf('[') + 1; + int end = 0; + if (offset != 0 && offset < data[2].IndexOf(':')) + { + end = data[2].IndexOf(']'); + mode = data[2].Substring(offset, end - offset); + } + + if (data[2].Substring(0, 4) == "rem:") + { + LeavePrivateMessage?.Invoke(conversation, mode, data[2].Substring(4 + end)); + return; + } + else if (!Conversations.Contains(conversation)) + { + Conversations.Add(conversation); + } + + string modeRemoved = data[2]; + if (end != 0) + { + modeRemoved = data[2].Substring(end + 2); + } + offset = modeRemoved.IndexOf(' '); + string speaker = modeRemoved.Substring(0, offset); + + offset = data[2].IndexOf(':') + 2; + string message = data[2].Substring(offset); + + PrivateMessage?.Invoke(conversation, mode, speaker, message); + } + + public int GetBoxIdFromPokemonUid(int lastUid) + { + return (lastUid - 7) / 15 + 1; + } + + private void OnPCBox(string[] data) + { + _refreshingPCBox.Cancel(); + IsPCBoxRefreshing = false; + if (Map.IsPC(PlayerX, PlayerY - 1)) + { + IsPCOpen = true; + } + /*string[] body = data[1].Split('='); + if (body.Length < 3) + { + InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received an invalid PC Box packet"); + return; + } + PCGreatestUid = Convert.ToInt32(body[0]); + + int pokemonCount = Convert.ToInt32(body[1]); + if (pokemonCount <= 0 || pokemonCount > 15) + { + InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received an invalid PC Box size"); + return; + } + if (body.Length < 1) + { + InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received an empty box"); + return; + }*/ + string[] pokemonListDatas = data[1].Split(new[] { "\r\n" }, StringSplitOptions.None); + List pokemonBox = new List(); + foreach (var pokemonDatas in pokemonListDatas) + { + if (pokemonDatas == string.Empty) continue; + string[] pokemonDatasArray = pokemonDatas.Split('|'); + Pokemon pokemon = new Pokemon(pokemonDatasArray); + if (CurrentPCBoxId != GetBoxIdFromPokemonUid(pokemon.Uid)) + { + InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received a box packet for an unexpected box: expected #" + + CurrentPCBox + ", received #" + GetBoxIdFromPokemonUid(pokemon.Uid)); + return; + } + pokemonBox.Add(pokemon); + } + /*if (pokemonBox.Count != pokemonCount) + { + InvalidPacket?.Invoke(data[0] + "|.|" + data[1], "Received a PC Box size that does not match the content"); + return; + }*/ + CurrentPCBox = pokemonBox; + PCBoxUpdated?.Invoke(CurrentPCBox); + } + + private void LoadMap(string mapName) + { + mapName = MapClient.RemoveExtension(mapName); + + _loadingTimeout.Set(Rand.Next(1500, 4000)); + + OpenedShop = null; + MoveRelearner = null; + _movements.Clear(); + _surfAfterMovement = 0; + _slidingDirection = null; + _dialogResponses.Clear(); + _movementTimeout.Cancel(); + _mountingTimeout.Cancel(); + _itemUseTimeout.Cancel(); + + if (Map == null || MapName != mapName) + { + DownloadMap(mapName); + } + } + + private void DownloadMap(string mapName) + { + Console.WriteLine("[Map] Requesting: " + MapName); + + Map = null; + AreNpcReceived = false; + MapName = mapName; + Players.Clear(); + + if (_mapClient.IsConnected) + { + _mapClient.DownloadMap(MapName); + } + } + } +} diff --git a/PROProtocol/GameConnection.cs b/PROProtocol/GameConnection.cs index 464babe..e345187 100644 --- a/PROProtocol/GameConnection.cs +++ b/PROProtocol/GameConnection.cs @@ -1,6 +1,6 @@ using BrightNetwork; using System; -using System.Net; +using System.Linq; using System.Net.Sockets; using System.Text; @@ -9,7 +9,7 @@ namespace PROProtocol public class GameConnection : SimpleTextClient { public GameServer Server; - + private bool _useSocks; private int _socksVersion; private string _socksHost; @@ -21,7 +21,7 @@ public GameConnection(GameServer server) : base(new BrightClient()) { PacketDelimiter = "|.\\\r\n"; - TextEncoding = Encoding.GetEncoding(1252); + TextEncoding = Encoding.GetEncoding("ISO-8859-1"); Server = server; } @@ -61,12 +61,22 @@ public async void Connect() protected override string ProcessDataBeforeSending(string data) { - return XorEncryption.Encrypt(data); + var input_bytes = TextEncoding.GetBytes(data); + var output_bytes = Encryption.Encrypt(input_bytes); + return TextEncoding.GetString(output_bytes); } protected override string ProcessDataBeforeReceiving(string data) { - return XorEncryption.Encrypt(data); + var data_bytes = TextEncoding.GetBytes(data); + if (!Encryption.StateReady) + { + Encryption.Decrypt(data_bytes, 16); + Encryption.Encrypt(data_bytes.Skip(16).ToArray(), 16); + Encryption.StateReady = true; + return null; + } + return TextEncoding.GetString(Encryption.Decrypt(data_bytes)); } } } diff --git a/PROProtocol/GameServer.cs b/PROProtocol/GameServer.cs index 894c270..424d4e3 100644 --- a/PROProtocol/GameServer.cs +++ b/PROProtocol/GameServer.cs @@ -1,38 +1,58 @@ -using System; -using System.Net; - -namespace PROProtocol -{ - public enum GameServer - { - Silver, - Gold - } - - public static class GameServerExtensions - { - public static IPEndPoint GetAddress(this GameServer server) - { - switch (server) - { - case GameServer.Silver: - return new IPEndPoint(IPAddress.Parse("185.70.107.113"), 800); - case GameServer.Gold: - return new IPEndPoint(IPAddress.Parse("185.70.107.113"), 801); - } - return null; - } - - public static GameServer FromName(string name) - { - switch (name.ToUpperInvariant()) - { - case "SILVER": - return GameServer.Silver; - case "GOLD": - return GameServer.Gold; - } - throw new Exception("The server " + name + " does not exist"); - } - } -} +using System; +using System.Collections.Generic; +using System.Net; + +namespace PROProtocol +{ + public enum GameServer + { + Silver, + Gold + } + + public static class GameServerExtensions + { + private static Dictionary _cachedIpAddresses = new Dictionary(); + + public static IPEndPoint GetAddress(this GameServer server) + { + if (!_cachedIpAddresses.ContainsKey(server)) + _cachedIpAddresses.Add(server, GetAddressFromDns(server + ".pokemonrevolution.net")); + else if (_cachedIpAddresses[server] is null) + _cachedIpAddresses[server] = GetAddressFromDns(server + ".pokemonrevolution.net"); + + switch (server) + { + case GameServer.Silver: + return new IPEndPoint(_cachedIpAddresses[server], 800); + case GameServer.Gold: + return new IPEndPoint(_cachedIpAddresses[server], 801); + } + return null; + } + + public static GameServer FromName(string name) + { + switch (name.ToUpperInvariant()) + { + case "SILVER": + return GameServer.Silver; + case "GOLD": + return GameServer.Gold; + } + throw new Exception("The server " + name + " does not exist"); + } + + public static IPAddress GetMapAddress(this GameServer server) + { + return _cachedIpAddresses[server]; + } + + private static Random Random = new Random(); + private static IPAddress GetAddressFromDns(string dns_host) + { + var addresses = Dns.GetHostAddresses(dns_host); + return addresses[Random.Next(0, addresses.Length - 1)]; + } + } +} diff --git a/PROProtocol/Hardware.cs b/PROProtocol/Hardware.cs new file mode 100644 index 0000000..7779b74 --- /dev/null +++ b/PROProtocol/Hardware.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace PROProtocol +{ + public static class Hardware + { + private static readonly Random Random = new Random(); + public static Guid GenerateRandomHash() + { + return Guid.NewGuid(); + } + + public static Guid RetrieveRealHash() + { + // TODO: find a way to retrieve the real Device ID from Unity. + throw new NotImplementedException(); + } + + public static string GenerateRandomOsInfo() + { + string[] osVer = {"Windows 10 (10.0.18363) 64bit", + "Windows 10 (10.0.10240) 64bit", + "Windows 10 (10.0.10586) 64bit", + "Windows 10 (10.0.14393) 64bit", + "Windows 10 (10.0.15063) 64bit", + "Windows 10 (10.0.16299) 64bit", + "Windows 10 (10.0.17134) 64bit", + "Windows 10 (10.0.17763) 64bit", + "Windows 10 (10.0.18362) 64bit", + "Windows 10 (10.0.19041) 64bit", + "Windows 10 (10.0.19042) 64bit", + "Windows 10 (10.0.19043) 64bit", + "Windows 8.1 (6.3.9600) 64 bit", + "Windows 7 (6.1.7601) 64bit", + //"Mac OS X 10.10.4", + //"iPhone OS 8.4", + //"Android OS API-22", + //"Android OS API-23", + //"Android OS API-24", + //"Android OS API-25", + //"Android OS API-26", + //"Android OS API-27", + //"Android OS API-28", + //"Android OS API-29", + }; + int vRandom = Random.Next(osVer.Length); + + return osVer[vRandom]; + } + + public static string RetrieveRealOsInfo() + { + // TODO: find a way to retrieve the real Os Info from Unity. + throw new NotImplementedException(); + } + } +} diff --git a/PROProtocol/HardwareHash.cs b/PROProtocol/HardwareHash.cs deleted file mode 100644 index 1438cfe..0000000 --- a/PROProtocol/HardwareHash.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace PROProtocol -{ - public static class HardwareHash - { - public static Guid GenerateRandom() - { - return Guid.NewGuid(); - } - - public static Guid RetrieveReal() - { - // TODO: find a way to retrieve the real Device ID from Unity. - throw new NotImplementedException(); - } - } -} diff --git a/PROProtocol/InventoryItem.cs b/PROProtocol/InventoryItem.cs index d5f9439..49c6274 100644 --- a/PROProtocol/InventoryItem.cs +++ b/PROProtocol/InventoryItem.cs @@ -19,10 +19,10 @@ public InventoryItem(int id, int quantity, int scope) public bool CanBeUsedOutsideOfBattle => Scope == 8 || Scope == 10 || Scope == 15; - public bool CanBeUsedOnPokemonOutsideOfBattle => Scope == 2 || Scope == 3 || Scope == 9 || Scope == 13 || Scope == 14; + public bool CanBeUsedOnPokemonOutsideOfBattle => Scope == 2 || Scope == 3 || Scope == 9 || Scope == 13 || Scope == 14 || Scope == 17; public bool CanBeUsedInBattle => Scope == 5; - public bool CanBeUsedOnPokemonInBattle => Scope == 2; + public bool CanBeUsedOnPokemonInBattle => Scope == 2 || Scope == 17; } } diff --git a/PROProtocol/ItemsDatabase.cs b/PROProtocol/ItemsDatabase.cs index f574d12..a323405 100644 --- a/PROProtocol/ItemsDatabase.cs +++ b/PROProtocol/ItemsDatabase.cs @@ -20,18 +20,7 @@ public class ItemInfo private ItemsDatabase() { - try - { - if (File.Exists("Resources/Items.json")) - { - string json = File.ReadAllText("Resources/Items.json"); - _items = JsonConvert.DeserializeObject>(json); - } - } - catch (Exception ex) - { - Console.Error.WriteLine("Could not read the items: " + ex.Message); - } + _items = ResourcesUtil.GetResource>("Items.json"); } public ItemInfo Get(int itemId) diff --git a/PROProtocol/Map.cs b/PROProtocol/Map.cs index 3dd053c..8cc2881 100644 --- a/PROProtocol/Map.cs +++ b/PROProtocol/Map.cs @@ -19,7 +19,7 @@ public enum MoveResult Icing } - public int[,] Colliders { get; } + public byte[,] Colliders { get; } public bool[,] Links { get; } public int[,] Tiles1 { get; } public int[,] Tiles2 { get; } @@ -53,16 +53,17 @@ public Map(byte[] content) { using (BinaryReader reader = new BinaryReader(stream)) { - Colliders = ReadTiles(reader); - DimensionX = Colliders.GetUpperBound(0) + 1; - DimensionY = Colliders.GetUpperBound(1) + 1; + DimensionY = reader.ReadInt32(); + DimensionX = reader.ReadInt32(); Width = DimensionX - 1; Height = DimensionY - 1; - Tiles1 = ReadTiles(reader); - Tiles2 = ReadTiles(reader); - Tiles3 = ReadTiles(reader); - Tiles4 = ReadTiles(reader); + Colliders = ReadColliders(reader, DimensionX, DimensionY); + + Tiles1 = ReadTiles(reader, DimensionX, DimensionY); + Tiles2 = ReadTiles(reader, DimensionX, DimensionY); + Tiles3 = ReadTiles(reader, DimensionX, DimensionY); + Tiles4 = ReadTiles(reader, DimensionX, DimensionY); MapWeather = ReadString(reader); reader.ReadInt16(); @@ -98,9 +99,7 @@ public Map(byte[] content) bool isBattler = reader.ReadInt16() != 0; - int npcId = reader.ReadInt16(); - - reader.ReadInt16(); + int npcId = reader.ReadInt32(); OriginalNpcs.Add(new Npc(npcId, npcName, isBattler, type, x, y, DirectionExtensions.FromNumber(direction), losLength, path)); } @@ -113,16 +112,27 @@ public static bool Exists(string name) return File.Exists("Resources/" + name + ".dat"); } - private int[,] ReadTiles(BinaryReader reader) + private static byte[,] ReadColliders(BinaryReader reader, int dimensionX, int dimensionY) { - int height = reader.ReadInt32(); - int width = reader.ReadInt32(); - int[,] tiles = new int[width, height]; - for (int y = 0; y < height; ++y) + byte[,] tiles = new byte[dimensionX, dimensionY]; + for (int y = 0; y < dimensionY; ++y) { - for (int x = 0; x < width; ++x) + for (int x = 0; x < dimensionX; ++x) { - tiles[x, y] = reader.ReadUInt16(); + tiles[x, y] = reader.ReadByte(); + } + } + return tiles; + } + + private int[,] ReadTiles(BinaryReader reader, int dimensionX, int dimensionY) + { + int[,] tiles = new int[dimensionX, dimensionY]; + for (int y = 0; y < dimensionY; ++y) + { + for (int x = 0; x < dimensionX; ++x) + { + tiles[x, y] = reader.ReadInt32(); } } return tiles; @@ -182,6 +192,35 @@ public bool CanSurf(int positionX, int positionY, bool isOnGround) return false; } + public Direction GetWaterDirectionFrom(int positionX, int positionY) + { + int collider = GetCollider(positionX, positionY - 1); + if (collider == 5 || collider == 12) + { + return Direction.Up; + } + + collider = GetCollider(positionX, positionY + 1); + if (collider == 5 || collider == 12) + { + return Direction.Down; + } + + collider = GetCollider(positionX - 1, positionY); + if (collider == 5 || collider == 12) + { + return Direction.Left; + } + + collider = GetCollider(positionX + 1, positionY); + if (collider == 5 || collider == 12) + { + return Direction.Right; + } + + return Direction.Down; + } + public MoveResult CanMove(Direction direction, int destinationX, int destinationY, bool isOnGround, bool isSurfing, bool canUseCut, bool canUseSmashRock) { if (destinationX < 0 || destinationX >= DimensionX @@ -367,7 +406,7 @@ public IEnumerable> GetNearestLinks(string linkName, int x, int private bool IsMovementValid(Direction direction, int collider, bool isOnGround, bool isSurfing, bool canUseCut, bool canUseSmashRock) { - if (collider == -1) + if (collider == 1) { return false; } diff --git a/PROProtocol/MapClient.cs b/PROProtocol/MapClient.cs index 5d3ab4f..210b88d 100644 --- a/PROProtocol/MapClient.cs +++ b/PROProtocol/MapClient.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Net; using System.Text; namespace PROProtocol @@ -29,12 +30,12 @@ public MapClient(MapConnection connection) _connection.Disconnected += OnDisconnected; } - public void Open(string host, int port) + public void Open(IPAddress ip, int port) { #if DEBUG Console.WriteLine("[+++] Connecting to the map server"); #endif - _connection.Connect(host, port); + _connection.Connect(ip, port); } public void Update() diff --git a/PROProtocol/MoveRelearner.cs b/PROProtocol/MoveRelearner.cs index 0f51145..452dc84 100644 --- a/PROProtocol/MoveRelearner.cs +++ b/PROProtocol/MoveRelearner.cs @@ -10,28 +10,23 @@ public class MoveRelearner private List _moveNames = new List(); public int SelectedPokemonUid { get; } + public bool IsEggMoves { get; } - public bool IsEgg { get; } - - public MoveRelearner(int pokemonUid, bool isEgg) + public MoveRelearner(int pokemonUid, bool isEggMoves) { SelectedPokemonUid = pokemonUid; - IsEgg = isEgg; + IsEggMoves = isEggMoves; } - public void ProcessMessage(string msg) + public void ProcessMessage(string data) { - string[] array = msg.Split(new string[] - { - "|" - }, StringSplitOptions.None); + string[] moveNames = data.Split(new string[] { "|" }, StringSplitOptions.None); - for (int i = 0; i < array.Length; i++) + for (int i = 0; i < moveNames.Length; i++) { - string moveName = array[i]; - if (moveName.Length > 2) + if (moveNames[i].Length > 2) { - MovesManager.MoveData move = MovesManager.Instance.GetMoveData(moveName); + MovesManager.MoveData move = MovesManager.Instance.GetMoveData(moveNames[i]); _moveNames.Add(move); } } diff --git a/PROProtocol/MovesManager.cs b/PROProtocol/MovesManager.cs index 63f59d8..2703deb 100644 --- a/PROProtocol/MovesManager.cs +++ b/PROProtocol/MovesManager.cs @@ -1,4 +1,8 @@ +using Newtonsoft.Json; +using System; using System.Collections.Generic; +using System.IO; +using System.Linq; namespace PROProtocol { @@ -10,14 +14,59 @@ public class MoveData public int Power; public int Accuracy; public string Type; + [JsonConverter(typeof(MoveStatusConverter))] public bool Status; public DamageType DamageType; + public string Desc; + + class MoveStatusConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(string); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + // value can be either "y" or "n" + return value != "n"; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException(); + } } + [JsonConverter(typeof(DamageTypeConverter))] public enum DamageType { Physical, - Special + Special, + Z, + } + + class DamageTypeConverter : JsonConverter + { + public override bool CanConvert(Type t) => t == typeof(string); + + public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) return null; + var value = serializer.Deserialize(reader); + switch (value) + { + case "p": + return DamageType.Physical; + case "s": + return DamageType.Special; + case "z": + return DamageType.Z; + default: + Console.Error.WriteLine($"Can't unmarshal DamageType '{value}'"); + // better than crashing... + return DamageType.Physical; + } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException(); } private static MovesManager _instance; @@ -30,26 +79,28 @@ public static MovesManager Instance } } - public const int MovesCount = 661; - - public MoveData[] Moves = new MoveData[MovesCount]; - public string[] MoveNames = new string[MovesCount]; - private Dictionary _namesToMoves = new Dictionary(); - private Dictionary _namesToIds = new Dictionary(); - private MoveData[] _idsToMoves = new MoveData[MovesCount]; + public MoveData[] Moves; + public string[] MoveNames; + private Dictionary _namesToMoves; + private Dictionary _namesToIds; + private MoveData[] _idsToMoves; private MovesManager() { LoadMoves(); - for (int i = 0; i < MovesCount; i++) + _namesToMoves = new Dictionary(); + for (int i = 0; i < Moves.Length; i++) { - if (Moves[i].Name != null && !_namesToMoves.ContainsKey(Moves[i].Name)) + if (Moves[i].Name != null && !_namesToMoves.ContainsKey(Moves[i].Name.ToLowerInvariant())) { - _namesToMoves.Add(Moves[i].Name, Moves[i]); + _namesToMoves.Add(Moves[i].Name.ToLowerInvariant(), Moves[i]); } } - for (int i = 0; i < MovesCount; i++) + + _idsToMoves = new MoveData[Moves.Length]; + _namesToIds = new Dictionary(); + for (int i = 0; i < Moves.Length; i++) { string lowerName = MoveNames[i].ToLowerInvariant(); if (_namesToMoves.ContainsKey(lowerName)) @@ -85,7 +136,7 @@ public MoveData GetMoveData(string moveName) public MoveData GetMoveData(int moveId) { - if (moveId > 0 && moveId < MovesCount) + if (moveId > 0 && moveId < Moves.Length) { return _idsToMoves[moveId]; } @@ -104,4636 +155,16 @@ public string GetTrueName(string lowerName) private void LoadMoves() { + var moves = ResourcesUtil.GetResource>("Moves.json"); + + Moves = moves.Values.ToArray(); + LoadMoveNames(); - for (int i = 0; i < MovesCount; i++) - { - Moves[i] = new MoveData(); - } - Moves[0].Name = "acid"; - Moves[0].Power = 40; - Moves[0].Accuracy = 100; - Moves[0].Type = "poison"; - Moves[0].Status = false; - Moves[0].DamageType = DamageType.Special; - Moves[1].Name = "acid armor"; - Moves[1].Power = -1; - Moves[1].Accuracy = -1; - Moves[1].Type = "poison"; - Moves[1].Status = true; - Moves[1].DamageType = DamageType.Physical; - Moves[2].Name = "aerial ace"; - Moves[2].Power = 60; - Moves[2].Accuracy = -1; - Moves[2].Type = "flying"; - Moves[2].Status = false; - Moves[2].DamageType = DamageType.Physical; - Moves[3].Name = "aeroblast"; - Moves[3].Power = 100; - Moves[3].Accuracy = 95; - Moves[3].Type = "flying"; - Moves[3].Status = false; - Moves[3].DamageType = DamageType.Special; - Moves[4].Name = "agility"; - Moves[4].Power = -1; - Moves[4].Accuracy = -1; - Moves[4].Type = "psychic"; - Moves[4].Status = true; - Moves[4].DamageType = DamageType.Physical; - Moves[5].Name = "air cutter"; - Moves[5].Power = 60; - Moves[5].Accuracy = 95; - Moves[5].Type = "flying"; - Moves[5].Status = false; - Moves[5].DamageType = DamageType.Special; - Moves[6].Name = "amnesia"; - Moves[6].Power = -1; - Moves[6].Accuracy = -1; - Moves[6].Type = "psychic"; - Moves[6].Status = true; - Moves[6].DamageType = DamageType.Physical; - Moves[7].Name = "ancient power"; - Moves[7].Power = 60; - Moves[7].Accuracy = 100; - Moves[7].Type = "rock"; - Moves[7].Status = false; - Moves[7].DamageType = DamageType.Special; - Moves[8].Name = "arm thrust"; - Moves[8].Power = 15; - Moves[8].Accuracy = 100; - Moves[8].Type = "fighting"; - Moves[8].Status = false; - Moves[8].DamageType = DamageType.Physical; - Moves[9].Name = "aromatherapy"; - Moves[9].Power = -1; - Moves[9].Accuracy = -1; - Moves[9].Type = "grass"; - Moves[9].Status = true; - Moves[9].DamageType = DamageType.Physical; - Moves[10].Name = "assist"; - Moves[10].Power = -1; - Moves[10].Accuracy = -1; - Moves[10].Type = "normal"; - Moves[10].Status = true; - Moves[10].DamageType = DamageType.Physical; - Moves[11].Name = "astonish"; - Moves[11].Power = 30; - Moves[11].Accuracy = 100; - Moves[11].Type = "ghost"; - Moves[11].Status = false; - Moves[11].DamageType = DamageType.Physical; - Moves[12].Name = "attract"; - Moves[12].Power = -1; - Moves[12].Accuracy = 100; - Moves[12].Type = "normal"; - Moves[12].Status = true; - Moves[12].DamageType = DamageType.Physical; - Moves[13].Name = "aurora beam"; - Moves[13].Power = 65; - Moves[13].Accuracy = 100; - Moves[13].Type = "ice"; - Moves[13].Status = false; - Moves[13].DamageType = DamageType.Special; - Moves[14].Name = "barrage"; - Moves[14].Power = 15; - Moves[14].Accuracy = 85; - Moves[14].Type = "normal"; - Moves[14].Status = false; - Moves[14].DamageType = DamageType.Physical; - Moves[15].Name = "barrier"; - Moves[15].Power = -1; - Moves[15].Accuracy = -1; - Moves[15].Type = "psychic"; - Moves[15].Status = true; - Moves[15].DamageType = DamageType.Physical; - Moves[16].Name = "baton pass"; - Moves[16].Power = -1; - Moves[16].Accuracy = -1; - Moves[16].Type = "normal"; - Moves[16].Status = true; - Moves[16].DamageType = DamageType.Physical; - Moves[17].Name = "beat up"; - Moves[17].Power = -1; - Moves[17].Accuracy = 100; - Moves[17].Type = "dark"; - Moves[17].Status = false; - Moves[17].DamageType = DamageType.Physical; - Moves[18].Name = "belly drum"; - Moves[18].Power = -1; - Moves[18].Accuracy = -1; - Moves[18].Type = "normal"; - Moves[18].Status = true; - Moves[18].DamageType = DamageType.Physical; - Moves[19].Name = "bide"; - Moves[19].Power = -1; - Moves[19].Accuracy = 100; - Moves[19].Type = "normal"; - Moves[19].Status = false; - Moves[19].DamageType = DamageType.Physical; - Moves[20].Name = "bind"; - Moves[20].Power = 15; - Moves[20].Accuracy = 85; - Moves[20].Type = "normal"; - Moves[20].Status = false; - Moves[20].DamageType = DamageType.Physical; - Moves[21].Name = "bite"; - Moves[21].Power = 60; - Moves[21].Accuracy = 100; - Moves[21].Type = "dark"; - Moves[21].Status = false; - Moves[21].DamageType = DamageType.Physical; - Moves[22].Name = "blaze kick"; - Moves[22].Power = 85; - Moves[22].Accuracy = 90; - Moves[22].Type = "fire"; - Moves[22].Status = false; - Moves[22].DamageType = DamageType.Physical; - Moves[23].Name = "blizzard"; - Moves[23].Power = 110; - Moves[23].Accuracy = 70; - Moves[23].Type = "ice"; - Moves[23].Status = false; - Moves[23].DamageType = DamageType.Special; - Moves[24].Name = "ignore"; - Moves[24].Power = -1; - Moves[24].Accuracy = 100; - Moves[24].Type = "normal"; - Moves[24].Status = true; - Moves[24].DamageType = DamageType.Physical; - Moves[25].Name = "block"; - Moves[25].Power = -1; - Moves[25].Accuracy = 100; - Moves[25].Type = "normal"; - Moves[25].Status = true; - Moves[25].DamageType = DamageType.Physical; - Moves[26].Name = "body slam"; - Moves[26].Power = 85; - Moves[26].Accuracy = 100; - Moves[26].Type = "normal"; - Moves[26].Status = false; - Moves[26].DamageType = DamageType.Physical; - Moves[27].Name = "bone club"; - Moves[27].Power = 65; - Moves[27].Accuracy = 85; - Moves[27].Type = "ground"; - Moves[27].Status = false; - Moves[27].DamageType = DamageType.Physical; - Moves[28].Name = "bone rush"; - Moves[28].Power = 25; - Moves[28].Accuracy = 90; - Moves[28].Type = "ground"; - Moves[28].Status = false; - Moves[28].DamageType = DamageType.Physical; - Moves[29].Name = "bonemerang"; - Moves[29].Power = 50; - Moves[29].Accuracy = 90; - Moves[29].Type = "ground"; - Moves[29].Status = false; - Moves[29].DamageType = DamageType.Physical; - Moves[30].Name = "bounce"; - Moves[30].Power = 85; - Moves[30].Accuracy = 85; - Moves[30].Type = "flying"; - Moves[30].Status = false; - Moves[30].DamageType = DamageType.Physical; - Moves[31].Name = "brick break"; - Moves[31].Power = 75; - Moves[31].Accuracy = 100; - Moves[31].Type = "fighting"; - Moves[31].Status = false; - Moves[31].DamageType = DamageType.Physical; - Moves[32].Name = "bubble"; - Moves[32].Power = 40; - Moves[32].Accuracy = 100; - Moves[32].Type = "water"; - Moves[32].Status = false; - Moves[32].DamageType = DamageType.Special; - Moves[33].Name = "bubblebeam"; - Moves[33].Power = 65; - Moves[33].Accuracy = 100; - Moves[33].Type = "water"; - Moves[33].Status = false; - Moves[33].DamageType = DamageType.Special; - Moves[34].Name = "bulk up"; - Moves[34].Power = -1; - Moves[34].Accuracy = -1; - Moves[34].Type = "fighting"; - Moves[34].Status = true; - Moves[34].DamageType = DamageType.Physical; - Moves[35].Name = "bullet seed"; - Moves[35].Power = 25; - Moves[35].Accuracy = 100; - Moves[35].Type = "grass"; - Moves[35].Status = false; - Moves[35].DamageType = DamageType.Physical; - Moves[36].Name = "calm mind"; - Moves[36].Power = -1; - Moves[36].Accuracy = -1; - Moves[36].Type = "psychic"; - Moves[36].Status = true; - Moves[36].DamageType = DamageType.Physical; - Moves[37].Name = "camouflage"; - Moves[37].Power = -1; - Moves[37].Accuracy = 100; - Moves[37].Type = "normal"; - Moves[37].Status = true; - Moves[37].DamageType = DamageType.Physical; - Moves[38].Name = "charge"; - Moves[38].Power = -1; - Moves[38].Accuracy = -1; - Moves[38].Type = "electric"; - Moves[38].Status = true; - Moves[38].DamageType = DamageType.Physical; - Moves[39].Name = "charm"; - Moves[39].Power = -1; - Moves[39].Accuracy = 100; - Moves[39].Type = "fairy"; - Moves[39].Status = true; - Moves[39].DamageType = DamageType.Physical; - Moves[40].Name = "clamp"; - Moves[40].Power = 35; - Moves[40].Accuracy = 85; - Moves[40].Type = "water"; - Moves[40].Status = false; - Moves[40].DamageType = DamageType.Physical; - Moves[41].Name = "comet punch"; - Moves[41].Power = 18; - Moves[41].Accuracy = 85; - Moves[41].Type = "normal"; - Moves[41].Status = false; - Moves[41].DamageType = DamageType.Physical; - Moves[42].Name = "confuse ray"; - Moves[42].Power = -1; - Moves[42].Accuracy = 100; - Moves[42].Type = "ghost"; - Moves[42].Status = true; - Moves[42].DamageType = DamageType.Physical; - Moves[43].Name = "confusion"; - Moves[43].Power = 50; - Moves[43].Accuracy = 100; - Moves[43].Type = "psychic"; - Moves[43].Status = false; - Moves[43].DamageType = DamageType.Special; - Moves[44].Name = "constrict"; - Moves[44].Power = 10; - Moves[44].Accuracy = 100; - Moves[44].Type = "normal"; - Moves[44].Status = false; - Moves[44].DamageType = DamageType.Physical; - Moves[45].Name = "ignore 2"; - Moves[45].Power = 50; - Moves[45].Accuracy = 100; - Moves[45].Type = "fighting"; - Moves[45].Status = false; - Moves[45].DamageType = DamageType.Physical; - Moves[46].Name = "conversion"; - Moves[46].Power = -1; - Moves[46].Accuracy = -1; - Moves[46].Type = "normal"; - Moves[46].Status = true; - Moves[46].DamageType = DamageType.Physical; - Moves[47].Name = "conversion 2"; - Moves[47].Power = -1; - Moves[47].Accuracy = -1; - Moves[47].Type = "normal"; - Moves[47].Status = true; - Moves[47].DamageType = DamageType.Physical; - Moves[48].Name = "cosmic power"; - Moves[48].Power = -1; - Moves[48].Accuracy = 100; - Moves[48].Type = "psychic"; - Moves[48].Status = true; - Moves[48].DamageType = DamageType.Physical; - Moves[49].Name = "cotton spore"; - Moves[49].Power = -1; - Moves[49].Accuracy = 100; - Moves[49].Type = "grass"; - Moves[49].Status = true; - Moves[49].DamageType = DamageType.Physical; - Moves[50].Name = "counter"; - Moves[50].Power = -1; - Moves[50].Accuracy = 100; - Moves[50].Type = "fighting"; - Moves[50].Status = false; - Moves[50].DamageType = DamageType.Physical; - Moves[51].Name = "covet"; - Moves[51].Power = 60; - Moves[51].Accuracy = 100; - Moves[51].Type = "normal"; - Moves[51].Status = false; - Moves[51].DamageType = DamageType.Physical; - Moves[52].Name = "crabhammer"; - Moves[52].Power = 100; - Moves[52].Accuracy = 90; - Moves[52].Type = "water"; - Moves[52].Status = false; - Moves[52].DamageType = DamageType.Physical; - Moves[53].Name = "cross chop"; - Moves[53].Power = 100; - Moves[53].Accuracy = 80; - Moves[53].Type = "fighting"; - Moves[53].Status = false; - Moves[53].DamageType = DamageType.Physical; - Moves[54].Name = "crunch"; - Moves[54].Power = 80; - Moves[54].Accuracy = 100; - Moves[54].Type = "dark"; - Moves[54].Status = false; - Moves[54].DamageType = DamageType.Physical; - Moves[55].Name = "crush claw"; - Moves[55].Power = 75; - Moves[55].Accuracy = 95; - Moves[55].Type = "normal"; - Moves[55].Status = false; - Moves[55].DamageType = DamageType.Physical; - Moves[56].Name = "curse"; - Moves[56].Power = -1; - Moves[56].Accuracy = -1; - Moves[56].Type = "ghost"; - Moves[56].Status = true; - Moves[56].DamageType = DamageType.Physical; - Moves[57].Name = "defense curl"; - Moves[57].Power = -1; - Moves[57].Accuracy = -1; - Moves[57].Type = "normal"; - Moves[57].Status = true; - Moves[57].DamageType = DamageType.Physical; - Moves[58].Name = "destiny bond"; - Moves[58].Power = -1; - Moves[58].Accuracy = -1; - Moves[58].Type = "ghost"; - Moves[58].Status = true; - Moves[58].DamageType = DamageType.Physical; - Moves[59].Name = "detect"; - Moves[59].Power = -1; - Moves[59].Accuracy = -1; - Moves[59].Type = "fighting"; - Moves[59].Status = true; - Moves[59].DamageType = DamageType.Physical; - Moves[60].Name = "dig"; - Moves[60].Power = 80; - Moves[60].Accuracy = 100; - Moves[60].Type = "ground"; - Moves[60].Status = false; - Moves[60].DamageType = DamageType.Physical; - Moves[61].Name = "disable"; - Moves[61].Power = -1; - Moves[61].Accuracy = 100; - Moves[61].Type = "normal"; - Moves[61].Status = true; - Moves[61].DamageType = DamageType.Physical; - Moves[62].Name = "dizzy punch"; - Moves[62].Power = 70; - Moves[62].Accuracy = 100; - Moves[62].Type = "normal"; - Moves[62].Status = false; - Moves[62].DamageType = DamageType.Physical; - Moves[63].Name = "doom desire"; - Moves[63].Power = 140; - Moves[63].Accuracy = 100; - Moves[63].Type = "steel"; - Moves[63].Status = false; - Moves[63].DamageType = DamageType.Physical; - Moves[64].Name = "gangsta rap"; - Moves[64].Power = 675; - Moves[64].Accuracy = 100; - Moves[64].Type = "dark"; - Moves[64].Status = false; - Moves[64].DamageType = DamageType.Physical; - Moves[65].Name = "double kick"; - Moves[65].Power = 30; - Moves[65].Accuracy = 100; - Moves[65].Type = "fighting"; - Moves[65].Status = false; - Moves[65].DamageType = DamageType.Physical; - Moves[66].Name = "ignore 3"; - Moves[66].Power = -1; - Moves[66].Accuracy = 100; - Moves[66].Type = "normal"; - Moves[66].Status = false; - Moves[66].DamageType = DamageType.Physical; - Moves[67].Name = "double team"; - Moves[67].Power = -1; - Moves[67].Accuracy = -1; - Moves[67].Type = "normal"; - Moves[67].Status = true; - Moves[67].DamageType = DamageType.Physical; - Moves[68].Name = "double-edge"; - Moves[68].Power = 120; - Moves[68].Accuracy = 100; - Moves[68].Type = "normal"; - Moves[68].Status = false; - Moves[68].DamageType = DamageType.Physical; - Moves[69].Name = "double slap"; - Moves[69].Power = 15; - Moves[69].Accuracy = 85; - Moves[69].Type = "normal"; - Moves[69].Status = false; - Moves[69].DamageType = DamageType.Physical; - Moves[70].Name = "dragon claw"; - Moves[70].Power = 80; - Moves[70].Accuracy = 100; - Moves[70].Type = "dragon"; - Moves[70].Status = false; - Moves[70].DamageType = DamageType.Physical; - Moves[71].Name = "dragon dance"; - Moves[71].Power = -1; - Moves[71].Accuracy = -1; - Moves[71].Type = "dragon"; - Moves[71].Status = true; - Moves[71].DamageType = DamageType.Physical; - Moves[72].Name = "dragon rage"; - Moves[72].Power = -1; - Moves[72].Accuracy = 100; - Moves[72].Type = "dragon"; - Moves[72].Status = false; - Moves[72].DamageType = DamageType.Special; - Moves[73].Name = "dragonbreath"; - Moves[73].Power = 60; - Moves[73].Accuracy = 100; - Moves[73].Type = "dragon"; - Moves[73].Status = false; - Moves[73].DamageType = DamageType.Special; - Moves[74].Name = "dream eater"; - Moves[74].Power = 100; - Moves[74].Accuracy = 100; - Moves[74].Type = "psychic"; - Moves[74].Status = false; - Moves[74].DamageType = DamageType.Special; - Moves[75].Name = "drill peck"; - Moves[75].Power = 80; - Moves[75].Accuracy = 100; - Moves[75].Type = "flying"; - Moves[75].Status = false; - Moves[75].DamageType = DamageType.Physical; - Moves[76].Name = "ignore 4"; - Moves[76].Power = -1; - Moves[76].Accuracy = 100; - Moves[76].Type = "fighting"; - Moves[76].Status = false; - Moves[76].DamageType = DamageType.Physical; - Moves[77].Name = "dynamic punch"; - Moves[77].Power = 100; - Moves[77].Accuracy = 50; - Moves[77].Type = "fighting"; - Moves[77].Status = false; - Moves[77].DamageType = DamageType.Physical; - Moves[78].Name = "earthquake"; - Moves[78].Power = 100; - Moves[78].Accuracy = 100; - Moves[78].Type = "ground"; - Moves[78].Status = false; - Moves[78].DamageType = DamageType.Physical; - Moves[79].Name = "egg bomb"; - Moves[79].Power = 100; - Moves[79].Accuracy = 75; - Moves[79].Type = "normal"; - Moves[79].Status = false; - Moves[79].DamageType = DamageType.Physical; - Moves[80].Name = "ember"; - Moves[80].Power = 40; - Moves[80].Accuracy = 100; - Moves[80].Type = "fire"; - Moves[80].Status = false; - Moves[80].DamageType = DamageType.Special; - Moves[81].Name = "encore"; - Moves[81].Power = -1; - Moves[81].Accuracy = 100; - Moves[81].Type = "normal"; - Moves[81].Status = true; - Moves[81].DamageType = DamageType.Physical; - Moves[82].Name = "endeavor"; - Moves[82].Power = -1; - Moves[82].Accuracy = 100; - Moves[82].Type = "normal"; - Moves[82].Status = false; - Moves[82].DamageType = DamageType.Physical; - Moves[83].Name = "endure"; - Moves[83].Power = -1; - Moves[83].Accuracy = -1; - Moves[83].Type = "normal"; - Moves[83].Status = true; - Moves[83].DamageType = DamageType.Physical; - Moves[84].Name = "eruption"; - Moves[84].Power = -1; - Moves[84].Accuracy = 100; - Moves[84].Type = "fire"; - Moves[84].Status = false; - Moves[84].DamageType = DamageType.Special; - Moves[85].Name = "explosion"; - Moves[85].Power = 250; - Moves[85].Accuracy = 100; - Moves[85].Type = "normal"; - Moves[85].Status = false; - Moves[85].DamageType = DamageType.Physical; - Moves[86].Name = "extrasensory"; - Moves[86].Power = 80; - Moves[86].Accuracy = 100; - Moves[86].Type = "psychic"; - Moves[86].Status = false; - Moves[86].DamageType = DamageType.Special; - Moves[87].Name = "extreme speed"; - Moves[87].Power = 80; - Moves[87].Accuracy = 100; - Moves[87].Type = "normal"; - Moves[87].Status = false; - Moves[87].DamageType = DamageType.Physical; - Moves[88].Name = "feint attack"; - Moves[88].Power = 60; - Moves[88].Accuracy = -1; - Moves[88].Type = "dark"; - Moves[88].Status = false; - Moves[88].DamageType = DamageType.Physical; - Moves[89].Name = "fake out"; - Moves[89].Power = 40; - Moves[89].Accuracy = 100; - Moves[89].Type = "normal"; - Moves[89].Status = false; - Moves[89].DamageType = DamageType.Physical; - Moves[90].Name = "fake tears"; - Moves[90].Power = -1; - Moves[90].Accuracy = 100; - Moves[90].Type = "dark"; - Moves[90].Status = true; - Moves[90].DamageType = DamageType.Physical; - Moves[91].Name = "false swipe"; - Moves[91].Power = 40; - Moves[91].Accuracy = 100; - Moves[91].Type = "normal"; - Moves[91].Status = false; - Moves[91].DamageType = DamageType.Physical; - Moves[92].Name = "feather dance"; - Moves[92].Power = -1; - Moves[92].Accuracy = 100; - Moves[92].Type = "flying"; - Moves[92].Status = true; - Moves[92].DamageType = DamageType.Physical; - Moves[93].Name = "fire blast"; - Moves[93].Power = 110; - Moves[93].Accuracy = 85; - Moves[93].Type = "fire"; - Moves[93].Status = false; - Moves[93].DamageType = DamageType.Special; - Moves[94].Name = "fire punch"; - Moves[94].Power = 75; - Moves[94].Accuracy = 100; - Moves[94].Type = "fire"; - Moves[94].Status = false; - Moves[94].DamageType = DamageType.Physical; - Moves[95].Name = "fire spin"; - Moves[95].Power = 35; - Moves[95].Accuracy = 85; - Moves[95].Type = "fire"; - Moves[95].Status = false; - Moves[95].DamageType = DamageType.Special; - Moves[96].Name = "fissure"; - Moves[96].Power = -1; - Moves[96].Accuracy = -1; - Moves[96].Type = "ground"; - Moves[96].Status = false; - Moves[96].DamageType = DamageType.Physical; - Moves[97].Name = "flail"; - Moves[97].Power = -1; - Moves[97].Accuracy = 100; - Moves[97].Type = "normal"; - Moves[97].Status = false; - Moves[97].DamageType = DamageType.Physical; - Moves[98].Name = "flame wheel"; - Moves[98].Power = 60; - Moves[98].Accuracy = 100; - Moves[98].Type = "fire"; - Moves[98].Status = false; - Moves[98].DamageType = DamageType.Physical; - Moves[99].Name = "flamethrower"; - Moves[99].Power = 90; - Moves[99].Accuracy = 100; - Moves[99].Type = "fire"; - Moves[99].Status = false; - Moves[99].DamageType = DamageType.Special; - Moves[100].Name = "flatter"; - Moves[100].Power = -1; - Moves[100].Accuracy = 100; - Moves[100].Type = "dark"; - Moves[100].Status = true; - Moves[100].DamageType = DamageType.Physical; - Moves[101].Name = "fly"; - Moves[101].Power = 90; - Moves[101].Accuracy = 95; - Moves[101].Type = "flying"; - Moves[101].Status = false; - Moves[101].DamageType = DamageType.Physical; - Moves[102].Name = "focus energy"; - Moves[102].Power = -1; - Moves[102].Accuracy = -1; - Moves[102].Type = "normal"; - Moves[102].Status = true; - Moves[102].DamageType = DamageType.Physical; - Moves[103].Name = "focus punch"; - Moves[103].Power = 150; - Moves[103].Accuracy = 100; - Moves[103].Type = "fighting"; - Moves[103].Status = false; - Moves[103].DamageType = DamageType.Physical; - Moves[104].Name = "follow me"; - Moves[104].Power = -1; - Moves[104].Accuracy = 100; - Moves[104].Type = "normal"; - Moves[104].Status = true; - Moves[104].DamageType = DamageType.Physical; - Moves[105].Name = "foresight"; - Moves[105].Power = -1; - Moves[105].Accuracy = 100; - Moves[105].Type = "normal"; - Moves[105].Status = true; - Moves[105].DamageType = DamageType.Physical; - Moves[106].Name = "fury attack"; - Moves[106].Power = 15; - Moves[106].Accuracy = 85; - Moves[106].Type = "normal"; - Moves[106].Status = false; - Moves[106].DamageType = DamageType.Physical; - Moves[107].Name = "fury cutter"; - Moves[107].Power = 40; - Moves[107].Accuracy = 95; - Moves[107].Type = "bug"; - Moves[107].Status = false; - Moves[107].DamageType = DamageType.Physical; - Moves[108].Name = "fury swipes"; - Moves[108].Power = 18; - Moves[108].Accuracy = 80; - Moves[108].Type = "normal"; - Moves[108].Status = false; - Moves[108].DamageType = DamageType.Physical; - Moves[109].Name = "future sight"; - Moves[109].Power = 120; - Moves[109].Accuracy = 100; - Moves[109].Type = "psychic"; - Moves[109].Status = false; - Moves[109].DamageType = DamageType.Special; - Moves[110].Name = "giga drain"; - Moves[110].Power = 75; - Moves[110].Accuracy = 100; - Moves[110].Type = "grass"; - Moves[110].Status = false; - Moves[110].DamageType = DamageType.Special; - Moves[111].Name = "glare"; - Moves[111].Power = -1; - Moves[111].Accuracy = 100; - Moves[111].Type = "normal"; - Moves[111].Status = true; - Moves[111].DamageType = DamageType.Physical; - Moves[112].Name = "grass whistle"; - Moves[112].Power = -1; - Moves[112].Accuracy = 55; - Moves[112].Type = "grass"; - Moves[112].Status = true; - Moves[112].DamageType = DamageType.Physical; - Moves[113].Name = "growl"; - Moves[113].Power = -1; - Moves[113].Accuracy = 100; - Moves[113].Type = "normal"; - Moves[113].Status = true; - Moves[113].DamageType = DamageType.Physical; - Moves[114].Name = "growth"; - Moves[114].Power = -1; - Moves[114].Accuracy = -1; - Moves[114].Type = "normal"; - Moves[114].Status = true; - Moves[114].DamageType = DamageType.Physical; - Moves[115].Name = "grudge"; - Moves[115].Power = -1; - Moves[115].Accuracy = 100; - Moves[115].Type = "ghost"; - Moves[115].Status = true; - Moves[115].DamageType = DamageType.Physical; - Moves[116].Name = "guillotine"; - Moves[116].Power = -1; - Moves[116].Accuracy = -1; - Moves[116].Type = "normal"; - Moves[116].Status = true; - Moves[116].DamageType = DamageType.Physical; - Moves[117].Name = "gust"; - Moves[117].Power = 40; - Moves[117].Accuracy = 100; - Moves[117].Type = "flying"; - Moves[117].Status = false; - Moves[117].DamageType = DamageType.Special; - Moves[118].Name = "hail"; - Moves[118].Power = -1; - Moves[118].Accuracy = -1; - Moves[118].Type = "ice"; - Moves[118].Status = true; - Moves[118].DamageType = DamageType.Physical; - Moves[119].Name = "harden"; - Moves[119].Power = -1; - Moves[119].Accuracy = -1; - Moves[119].Type = "normal"; - Moves[119].Status = true; - Moves[119].DamageType = DamageType.Physical; - Moves[120].Name = "haze"; - Moves[120].Power = -1; - Moves[120].Accuracy = -1; - Moves[120].Type = "ice"; - Moves[120].Status = true; - Moves[120].DamageType = DamageType.Physical; - Moves[121].Name = "headbutt"; - Moves[121].Power = 70; - Moves[121].Accuracy = 100; - Moves[121].Type = "normal"; - Moves[121].Status = false; - Moves[121].DamageType = DamageType.Physical; - Moves[122].Name = "heal bell"; - Moves[122].Power = -1; - Moves[122].Accuracy = -1; - Moves[122].Type = "normal"; - Moves[122].Status = true; - Moves[122].DamageType = DamageType.Physical; - Moves[123].Name = "heat wave"; - Moves[123].Power = 95; - Moves[123].Accuracy = 90; - Moves[123].Type = "fire"; - Moves[123].Status = false; - Moves[123].DamageType = DamageType.Special; - Moves[124].Name = "helping hand"; - Moves[124].Power = -1; - Moves[124].Accuracy = -1; - Moves[124].Type = "normal"; - Moves[124].Status = true; - Moves[124].DamageType = DamageType.Physical; - Moves[125].Name = "high jump kick"; - Moves[125].Power = 130; - Moves[125].Accuracy = 90; - Moves[125].Type = "fighting"; - Moves[125].Status = false; - Moves[125].DamageType = DamageType.Physical; - Moves[126].Name = "hidden power"; - Moves[126].Power = 60; - Moves[126].Accuracy = 100; - Moves[126].Type = "normal"; - Moves[126].Status = false; - Moves[126].DamageType = DamageType.Special; - Moves[127].Name = "horn attack"; - Moves[127].Power = 65; - Moves[127].Accuracy = 100; - Moves[127].Type = "normal"; - Moves[127].Status = false; - Moves[127].DamageType = DamageType.Physical; - Moves[128].Name = "horn drill"; - Moves[128].Power = -1; - Moves[128].Accuracy = -1; - Moves[128].Type = "normal"; - Moves[128].Status = true; - Moves[128].DamageType = DamageType.Physical; - Moves[129].Name = "howl"; - Moves[129].Power = -1; - Moves[129].Accuracy = -1; - Moves[129].Type = "normal"; - Moves[129].Status = true; - Moves[129].DamageType = DamageType.Physical; - Moves[130].Name = "hydro pump"; - Moves[130].Power = 110; - Moves[130].Accuracy = 80; - Moves[130].Type = "water"; - Moves[130].Status = false; - Moves[130].DamageType = DamageType.Special; - Moves[131].Name = "hyper beam"; - Moves[131].Power = 150; - Moves[131].Accuracy = 90; - Moves[131].Type = "normal"; - Moves[131].Status = false; - Moves[131].DamageType = DamageType.Special; - Moves[132].Name = "hyper fang"; - Moves[132].Power = 80; - Moves[132].Accuracy = 90; - Moves[132].Type = "normal"; - Moves[132].Status = false; - Moves[132].DamageType = DamageType.Physical; - Moves[133].Name = "hyper voice"; - Moves[133].Power = 90; - Moves[133].Accuracy = 100; - Moves[133].Type = "normal"; - Moves[133].Status = false; - Moves[133].DamageType = DamageType.Special; - Moves[134].Name = "hypnosis"; - Moves[134].Power = -1; - Moves[134].Accuracy = 60; - Moves[134].Type = "psychic"; - Moves[134].Status = true; - Moves[134].DamageType = DamageType.Physical; - Moves[135].Name = "ice ball"; - Moves[135].Power = 30; - Moves[135].Accuracy = 90; - Moves[135].Type = "ice"; - Moves[135].Status = false; - Moves[135].DamageType = DamageType.Physical; - Moves[136].Name = "ice beam"; - Moves[136].Power = 90; - Moves[136].Accuracy = 100; - Moves[136].Type = "ice"; - Moves[136].Status = false; - Moves[136].DamageType = DamageType.Special; - Moves[137].Name = "ice punch"; - Moves[137].Power = 75; - Moves[137].Accuracy = 100; - Moves[137].Type = "ice"; - Moves[137].Status = false; - Moves[137].DamageType = DamageType.Physical; - Moves[138].Name = "icicle spear"; - Moves[138].Power = 25; - Moves[138].Accuracy = 100; - Moves[138].Type = "ice"; - Moves[138].Status = false; - Moves[138].DamageType = DamageType.Physical; - Moves[139].Name = "icy wind"; - Moves[139].Power = 55; - Moves[139].Accuracy = 95; - Moves[139].Type = "ice"; - Moves[139].Status = false; - Moves[139].DamageType = DamageType.Special; - Moves[140].Name = "imprison"; - Moves[140].Power = -1; - Moves[140].Accuracy = 100; - Moves[140].Type = "psychic"; - Moves[140].Status = true; - Moves[140].DamageType = DamageType.Physical; - Moves[141].Name = "ingrain"; - Moves[141].Power = -1; - Moves[141].Accuracy = -1; - Moves[141].Type = "grass"; - Moves[141].Status = true; - Moves[141].DamageType = DamageType.Physical; - Moves[142].Name = "iron defense"; - Moves[142].Power = -1; - Moves[142].Accuracy = -1; - Moves[142].Type = "steel"; - Moves[142].Status = true; - Moves[142].DamageType = DamageType.Physical; - Moves[143].Name = "iron tail"; - Moves[143].Power = 100; - Moves[143].Accuracy = 75; - Moves[143].Type = "steel"; - Moves[143].Status = false; - Moves[143].DamageType = DamageType.Physical; - Moves[144].Name = "jump kick"; - Moves[144].Power = 100; - Moves[144].Accuracy = 95; - Moves[144].Type = "fighting"; - Moves[144].Status = false; - Moves[144].DamageType = DamageType.Physical; - Moves[145].Name = "karate chop"; - Moves[145].Power = 50; - Moves[145].Accuracy = 100; - Moves[145].Type = "fighting"; - Moves[145].Status = false; - Moves[145].DamageType = DamageType.Physical; - Moves[146].Name = "kinesis"; - Moves[146].Power = -1; - Moves[146].Accuracy = 80; - Moves[146].Type = "psychic"; - Moves[146].Status = true; - Moves[146].DamageType = DamageType.Physical; - Moves[147].Name = "knock off"; - Moves[147].Power = 65; - Moves[147].Accuracy = 100; - Moves[147].Type = "dark"; - Moves[147].Status = false; - Moves[147].DamageType = DamageType.Physical; - Moves[148].Name = "leaf blade"; - Moves[148].Power = 90; - Moves[148].Accuracy = 100; - Moves[148].Type = "grass"; - Moves[148].Status = false; - Moves[148].DamageType = DamageType.Physical; - Moves[149].Name = "leech life"; - Moves[149].Power = 20; - Moves[149].Accuracy = 100; - Moves[149].Type = "bug"; - Moves[149].Status = false; - Moves[149].DamageType = DamageType.Physical; - Moves[150].Name = "leech seed"; - Moves[150].Power = -1; - Moves[150].Accuracy = 90; - Moves[150].Type = "grass"; - Moves[150].Status = true; - Moves[150].DamageType = DamageType.Physical; - Moves[151].Name = "leer"; - Moves[151].Power = -1; - Moves[151].Accuracy = 100; - Moves[151].Type = "normal"; - Moves[151].Status = true; - Moves[151].DamageType = DamageType.Physical; - Moves[152].Name = "lick"; - Moves[152].Power = 30; - Moves[152].Accuracy = 100; - Moves[152].Type = "ghost"; - Moves[152].Status = false; - Moves[152].DamageType = DamageType.Physical; - Moves[153].Name = "light screen"; - Moves[153].Power = -1; - Moves[153].Accuracy = -1; - Moves[153].Type = "psychic"; - Moves[153].Status = true; - Moves[153].DamageType = DamageType.Physical; - Moves[154].Name = "ignore 5"; - Moves[154].Power = -1; - Moves[154].Accuracy = 100; - Moves[154].Type = "normal"; - Moves[154].Status = false; - Moves[154].DamageType = DamageType.Physical; - Moves[155].Name = "lock-on"; - Moves[155].Power = -1; - Moves[155].Accuracy = 100; - Moves[155].Type = "normal"; - Moves[155].Status = true; - Moves[155].DamageType = DamageType.Physical; - Moves[156].Name = "lovely kiss"; - Moves[156].Power = -1; - Moves[156].Accuracy = 75; - Moves[156].Type = "normal"; - Moves[156].Status = true; - Moves[156].DamageType = DamageType.Physical; - Moves[157].Name = "low kick"; - Moves[157].Power = 50; - Moves[157].Accuracy = 100; - Moves[157].Type = "fighting"; - Moves[157].Status = false; - Moves[157].DamageType = DamageType.Physical; - Moves[158].Name = "luster purge"; - Moves[158].Power = 70; - Moves[158].Accuracy = 100; - Moves[158].Type = "psychic"; - Moves[158].Status = false; - Moves[158].DamageType = DamageType.Special; - Moves[159].Name = "mach punch"; - Moves[159].Power = 40; - Moves[159].Accuracy = 100; - Moves[159].Type = "fighting"; - Moves[159].Status = false; - Moves[159].DamageType = DamageType.Physical; - Moves[160].Name = "magic coat"; - Moves[160].Power = -1; - Moves[160].Accuracy = -1; - Moves[160].Type = "psychic"; - Moves[160].Status = true; - Moves[160].DamageType = DamageType.Physical; - Moves[161].Name = "magical leaf"; - Moves[161].Power = 60; - Moves[161].Accuracy = -1; - Moves[161].Type = "grass"; - Moves[161].Status = false; - Moves[161].DamageType = DamageType.Special; - Moves[162].Name = "magnitude"; - Moves[162].Power = -1; - Moves[162].Accuracy = 100; - Moves[162].Type = "ground"; - Moves[162].Status = false; - Moves[162].DamageType = DamageType.Physical; - Moves[163].Name = "mean look"; - Moves[163].Power = -1; - Moves[163].Accuracy = 100; - Moves[163].Type = "normal"; - Moves[163].Status = true; - Moves[163].DamageType = DamageType.Physical; - Moves[164].Name = "meditate"; - Moves[164].Power = -1; - Moves[164].Accuracy = -1; - Moves[164].Type = "psychic"; - Moves[164].Status = true; - Moves[164].DamageType = DamageType.Physical; - Moves[165].Name = "mega drain"; - Moves[165].Power = 40; - Moves[165].Accuracy = 100; - Moves[165].Type = "grass"; - Moves[165].Status = false; - Moves[165].DamageType = DamageType.Special; - Moves[166].Name = "mega kick"; - Moves[166].Power = 120; - Moves[166].Accuracy = 75; - Moves[166].Type = "normal"; - Moves[166].Status = false; - Moves[166].DamageType = DamageType.Physical; - Moves[167].Name = "mega punch"; - Moves[167].Power = 80; - Moves[167].Accuracy = 85; - Moves[167].Type = "normal"; - Moves[167].Status = false; - Moves[167].DamageType = DamageType.Physical; - Moves[168].Name = "megahorn"; - Moves[168].Power = 120; - Moves[168].Accuracy = 85; - Moves[168].Type = "bug"; - Moves[168].Status = false; - Moves[168].DamageType = DamageType.Physical; - Moves[169].Name = "memento"; - Moves[169].Power = -1; - Moves[169].Accuracy = 100; - Moves[169].Type = "dark"; - Moves[169].Status = true; - Moves[169].DamageType = DamageType.Physical; - Moves[170].Name = "metal claw"; - Moves[170].Power = 50; - Moves[170].Accuracy = 95; - Moves[170].Type = "steel"; - Moves[170].Status = false; - Moves[170].DamageType = DamageType.Physical; - Moves[171].Name = "metal sound"; - Moves[171].Power = -1; - Moves[171].Accuracy = 85; - Moves[171].Type = "steel"; - Moves[171].Status = true; - Moves[171].DamageType = DamageType.Physical; - Moves[172].Name = "meteor mash"; - Moves[172].Power = 90; - Moves[172].Accuracy = 90; - Moves[172].Type = "steel"; - Moves[172].Status = false; - Moves[172].DamageType = DamageType.Physical; - Moves[173].Name = "metronome"; - Moves[173].Power = -1; - Moves[173].Accuracy = -1; - Moves[173].Type = "normal"; - Moves[173].Status = true; - Moves[173].DamageType = DamageType.Physical; - Moves[174].Name = "milk drink"; - Moves[174].Power = -1; - Moves[174].Accuracy = -1; - Moves[174].Type = "normal"; - Moves[174].Status = true; - Moves[174].DamageType = DamageType.Physical; - Moves[175].Name = "mimic"; - Moves[175].Power = -1; - Moves[175].Accuracy = 100; - Moves[175].Type = "normal"; - Moves[175].Status = true; - Moves[175].DamageType = DamageType.Physical; - Moves[176].Name = "mind reader"; - Moves[176].Power = -1; - Moves[176].Accuracy = 100; - Moves[176].Type = "normal"; - Moves[176].Status = true; - Moves[176].DamageType = DamageType.Physical; - Moves[177].Name = "minimize"; - Moves[177].Power = -1; - Moves[177].Accuracy = -1; - Moves[177].Type = "normal"; - Moves[177].Status = true; - Moves[177].DamageType = DamageType.Physical; - Moves[178].Name = "mirror coat"; - Moves[178].Power = -1; - Moves[178].Accuracy = 100; - Moves[178].Type = "psychic"; - Moves[178].Status = true; - Moves[178].DamageType = DamageType.Special; - Moves[179].Name = "mirror move"; - Moves[179].Power = -1; - Moves[179].Accuracy = -1; - Moves[179].Type = "flying"; - Moves[179].Status = true; - Moves[179].DamageType = DamageType.Physical; - Moves[180].Name = "mist"; - Moves[180].Power = -1; - Moves[180].Accuracy = -1; - Moves[180].Type = "ice"; - Moves[180].Status = true; - Moves[180].DamageType = DamageType.Physical; - Moves[181].Name = "mist ball"; - Moves[181].Power = 70; - Moves[181].Accuracy = 100; - Moves[181].Type = "psychic"; - Moves[181].Status = false; - Moves[181].DamageType = DamageType.Special; - Moves[182].Name = "moonlight"; - Moves[182].Power = -1; - Moves[182].Accuracy = -1; - Moves[182].Type = "fairy"; - Moves[182].Status = true; - Moves[182].DamageType = DamageType.Physical; - Moves[183].Name = "morning sun"; - Moves[183].Power = -1; - Moves[183].Accuracy = -1; - Moves[183].Type = "normal"; - Moves[183].Status = true; - Moves[183].DamageType = DamageType.Physical; - Moves[184].Name = "mud shot"; - Moves[184].Power = 55; - Moves[184].Accuracy = 95; - Moves[184].Type = "ground"; - Moves[184].Status = false; - Moves[184].DamageType = DamageType.Special; - Moves[185].Name = "mud-slap"; - Moves[185].Power = 20; - Moves[185].Accuracy = 100; - Moves[185].Type = "ground"; - Moves[185].Status = false; - Moves[185].DamageType = DamageType.Special; - Moves[186].Name = "mud sport"; - Moves[186].Power = -1; - Moves[186].Accuracy = 100; - Moves[186].Type = "ground"; - Moves[186].Status = true; - Moves[186].DamageType = DamageType.Physical; - Moves[187].Name = "muddy water"; - Moves[187].Power = 90; - Moves[187].Accuracy = 85; - Moves[187].Type = "water"; - Moves[187].Status = false; - Moves[187].DamageType = DamageType.Special; - Moves[188].Name = "nature power"; - Moves[188].Power = -1; - Moves[188].Accuracy = -1; - Moves[188].Type = "normal"; - Moves[188].Status = true; - Moves[188].DamageType = DamageType.Physical; - Moves[189].Name = "needle arm"; - Moves[189].Power = 60; - Moves[189].Accuracy = 100; - Moves[189].Type = "grass"; - Moves[189].Status = false; - Moves[189].DamageType = DamageType.Physical; - Moves[190].Name = "night shade"; - Moves[190].Power = -1; - Moves[190].Accuracy = 100; - Moves[190].Type = "ghost"; - Moves[190].Status = false; - Moves[190].DamageType = DamageType.Special; - Moves[191].Name = "nightmare"; - Moves[191].Power = -1; - Moves[191].Accuracy = 100; - Moves[191].Type = "ghost"; - Moves[191].Status = true; - Moves[191].DamageType = DamageType.Physical; - Moves[192].Name = "octazooka"; - Moves[192].Power = 65; - Moves[192].Accuracy = 85; - Moves[192].Type = "water"; - Moves[192].Status = false; - Moves[192].DamageType = DamageType.Special; - Moves[193].Name = "odor sleuth"; - Moves[193].Power = -1; - Moves[193].Accuracy = 100; - Moves[193].Type = "normal"; - Moves[193].Status = true; - Moves[193].DamageType = DamageType.Physical; - Moves[194].Name = "outrage"; - Moves[194].Power = 120; - Moves[194].Accuracy = 100; - Moves[194].Type = "dragon"; - Moves[194].Status = false; - Moves[194].DamageType = DamageType.Physical; - Moves[195].Name = "pain split"; - Moves[195].Power = -1; - Moves[195].Accuracy = 100; - Moves[195].Type = "normal"; - Moves[195].Status = true; - Moves[195].DamageType = DamageType.Physical; - Moves[196].Name = "pay day"; - Moves[196].Power = 40; - Moves[196].Accuracy = 100; - Moves[196].Type = "normal"; - Moves[196].Status = false; - Moves[196].DamageType = DamageType.Physical; - Moves[197].Name = "peck"; - Moves[197].Power = 35; - Moves[197].Accuracy = 100; - Moves[197].Type = "flying"; - Moves[197].Status = false; - Moves[197].DamageType = DamageType.Physical; - Moves[198].Name = "perish song"; - Moves[198].Power = -1; - Moves[198].Accuracy = -1; - Moves[198].Type = "normal"; - Moves[198].Status = true; - Moves[198].DamageType = DamageType.Physical; - Moves[199].Name = "petal dance"; - Moves[199].Power = 120; - Moves[199].Accuracy = 100; - Moves[199].Type = "grass"; - Moves[199].Status = false; - Moves[199].DamageType = DamageType.Special; - Moves[200].Name = "pin missile"; - Moves[200].Power = 25; - Moves[200].Accuracy = 95; - Moves[200].Type = "bug"; - Moves[200].Status = false; - Moves[200].DamageType = DamageType.Physical; - Moves[201].Name = "poison fang"; - Moves[201].Power = 50; - Moves[201].Accuracy = 100; - Moves[201].Type = "poison"; - Moves[201].Status = false; - Moves[201].DamageType = DamageType.Physical; - Moves[202].Name = "poison gas"; - Moves[202].Power = -1; - Moves[202].Accuracy = 90; - Moves[202].Type = "poison"; - Moves[202].Status = true; - Moves[202].DamageType = DamageType.Physical; - Moves[203].Name = "poison sting"; - Moves[203].Power = 15; - Moves[203].Accuracy = 100; - Moves[203].Type = "poison"; - Moves[203].Status = false; - Moves[203].DamageType = DamageType.Physical; - Moves[204].Name = "poison tail"; - Moves[204].Power = 50; - Moves[204].Accuracy = 100; - Moves[204].Type = "poison"; - Moves[204].Status = false; - Moves[204].DamageType = DamageType.Physical; - Moves[205].Name = "poison powder"; - Moves[205].Power = -1; - Moves[205].Accuracy = 75; - Moves[205].Type = "poison"; - Moves[205].Status = true; - Moves[205].DamageType = DamageType.Physical; - Moves[206].Name = "pound"; - Moves[206].Power = 40; - Moves[206].Accuracy = 100; - Moves[206].Type = "normal"; - Moves[206].Status = false; - Moves[206].DamageType = DamageType.Physical; - Moves[207].Name = "powder snow"; - Moves[207].Power = 40; - Moves[207].Accuracy = 100; - Moves[207].Type = "ice"; - Moves[207].Status = false; - Moves[207].DamageType = DamageType.Special; - Moves[208].Name = "present"; - Moves[208].Power = -1; - Moves[208].Accuracy = 90; - Moves[208].Type = "normal"; - Moves[208].Status = false; - Moves[208].DamageType = DamageType.Physical; - Moves[209].Name = "protect"; - Moves[209].Power = -1; - Moves[209].Accuracy = -1; - Moves[209].Type = "normal"; - Moves[209].Status = true; - Moves[209].DamageType = DamageType.Physical; - Moves[210].Name = "psybeam"; - Moves[210].Power = 65; - Moves[210].Accuracy = 100; - Moves[210].Type = "psychic"; - Moves[210].Status = false; - Moves[210].DamageType = DamageType.Special; - Moves[211].Name = "psych up"; - Moves[211].Power = -1; - Moves[211].Accuracy = -1; - Moves[211].Type = "normal"; - Moves[211].Status = true; - Moves[211].DamageType = DamageType.Physical; - Moves[212].Name = "psychic"; - Moves[212].Power = 90; - Moves[212].Accuracy = 100; - Moves[212].Type = "psychic"; - Moves[212].Status = false; - Moves[212].DamageType = DamageType.Special; - Moves[213].Name = "psycho boost"; - Moves[213].Power = 140; - Moves[213].Accuracy = 90; - Moves[213].Type = "psychic"; - Moves[213].Status = false; - Moves[213].DamageType = DamageType.Special; - Moves[214].Name = "psywave"; - Moves[214].Power = -1; - Moves[214].Accuracy = 100; - Moves[214].Type = "psychic"; - Moves[214].Status = false; - Moves[214].DamageType = DamageType.Special; - Moves[215].Name = "pursuit"; - Moves[215].Power = 40; - Moves[215].Accuracy = 100; - Moves[215].Type = "dark"; - Moves[215].Status = false; - Moves[215].DamageType = DamageType.Physical; - Moves[216].Name = "quick attack"; - Moves[216].Power = 40; - Moves[216].Accuracy = 100; - Moves[216].Type = "normal"; - Moves[216].Status = false; - Moves[216].DamageType = DamageType.Physical; - Moves[217].Name = "rage"; - Moves[217].Power = 20; - Moves[217].Accuracy = 100; - Moves[217].Type = "normal"; - Moves[217].Status = false; - Moves[217].DamageType = DamageType.Physical; - Moves[218].Name = "rain dance"; - Moves[218].Power = -1; - Moves[218].Accuracy = -1; - Moves[218].Type = "water"; - Moves[218].Status = true; - Moves[218].DamageType = DamageType.Physical; - Moves[219].Name = "rapid spin"; - Moves[219].Power = 20; - Moves[219].Accuracy = 100; - Moves[219].Type = "normal"; - Moves[219].Status = false; - Moves[219].DamageType = DamageType.Physical; - Moves[220].Name = "razor leaf"; - Moves[220].Power = 55; - Moves[220].Accuracy = 95; - Moves[220].Type = "grass"; - Moves[220].Status = false; - Moves[220].DamageType = DamageType.Physical; - Moves[221].Name = "razor wind"; - Moves[221].Power = 80; - Moves[221].Accuracy = 100; - Moves[221].Type = "normal"; - Moves[221].Status = false; - Moves[221].DamageType = DamageType.Special; - Moves[222].Name = "recover"; - Moves[222].Power = -1; - Moves[222].Accuracy = -1; - Moves[222].Type = "normal"; - Moves[222].Status = true; - Moves[222].DamageType = DamageType.Physical; - Moves[223].Name = "recycle"; - Moves[223].Power = -1; - Moves[223].Accuracy = 100; - Moves[223].Type = "normal"; - Moves[223].Status = true; - Moves[223].DamageType = DamageType.Physical; - Moves[224].Name = "reflect"; - Moves[224].Power = -1; - Moves[224].Accuracy = -1; - Moves[224].Type = "psychic"; - Moves[224].Status = true; - Moves[224].DamageType = DamageType.Physical; - Moves[225].Name = "refresh"; - Moves[225].Power = -1; - Moves[225].Accuracy = 100; - Moves[225].Type = "normal"; - Moves[225].Status = true; - Moves[225].DamageType = DamageType.Physical; - Moves[226].Name = "rest"; - Moves[226].Power = -1; - Moves[226].Accuracy = -1; - Moves[226].Type = "psychic"; - Moves[226].Status = true; - Moves[226].DamageType = DamageType.Physical; - Moves[227].Name = "revenge"; - Moves[227].Power = 60; - Moves[227].Accuracy = 100; - Moves[227].Type = "fighting"; - Moves[227].Status = false; - Moves[227].DamageType = DamageType.Physical; - Moves[228].Name = "reversal"; - Moves[228].Power = -1; - Moves[228].Accuracy = 100; - Moves[228].Type = "fighting"; - Moves[228].Status = false; - Moves[228].DamageType = DamageType.Physical; - Moves[229].Name = "roar"; - Moves[229].Power = -1; - Moves[229].Accuracy = -1; - Moves[229].Type = "normal"; - Moves[229].Status = true; - Moves[229].DamageType = DamageType.Physical; - Moves[230].Name = "rock blast"; - Moves[230].Power = 25; - Moves[230].Accuracy = 90; - Moves[230].Type = "rock"; - Moves[230].Status = false; - Moves[230].DamageType = DamageType.Physical; - Moves[231].Name = "rock slide"; - Moves[231].Power = 75; - Moves[231].Accuracy = 90; - Moves[231].Type = "rock"; - Moves[231].Status = false; - Moves[231].DamageType = DamageType.Physical; - Moves[232].Name = "rock throw"; - Moves[232].Power = 50; - Moves[232].Accuracy = 90; - Moves[232].Type = "rock"; - Moves[232].Status = false; - Moves[232].DamageType = DamageType.Physical; - Moves[233].Name = "rock tomb"; - Moves[233].Power = 60; - Moves[233].Accuracy = 95; - Moves[233].Type = "rock"; - Moves[233].Status = false; - Moves[233].DamageType = DamageType.Physical; - Moves[234].Name = "role play"; - Moves[234].Power = -1; - Moves[234].Accuracy = -1; - Moves[234].Type = "psychic"; - Moves[234].Status = true; - Moves[234].DamageType = DamageType.Physical; - Moves[235].Name = "rolling kick"; - Moves[235].Power = 60; - Moves[235].Accuracy = 85; - Moves[235].Type = "fighting"; - Moves[235].Status = false; - Moves[235].DamageType = DamageType.Physical; - Moves[236].Name = "rollout"; - Moves[236].Power = 30; - Moves[236].Accuracy = 90; - Moves[236].Type = "rock"; - Moves[236].Status = false; - Moves[236].DamageType = DamageType.Physical; - Moves[237].Name = "sacred fire"; - Moves[237].Power = 100; - Moves[237].Accuracy = 95; - Moves[237].Type = "fire"; - Moves[237].Status = false; - Moves[237].DamageType = DamageType.Physical; - Moves[238].Name = "safeguard"; - Moves[238].Power = -1; - Moves[238].Accuracy = -1; - Moves[238].Type = "normal"; - Moves[238].Status = true; - Moves[238].DamageType = DamageType.Physical; - Moves[239].Name = "sand tomb"; - Moves[239].Power = 35; - Moves[239].Accuracy = 85; - Moves[239].Type = "ground"; - Moves[239].Status = false; - Moves[239].DamageType = DamageType.Physical; - Moves[240].Name = "sand-attack"; - Moves[240].Power = -1; - Moves[240].Accuracy = 100; - Moves[240].Type = "ground"; - Moves[240].Status = true; - Moves[240].DamageType = DamageType.Physical; - Moves[241].Name = "sandstorm"; - Moves[241].Power = -1; - Moves[241].Accuracy = -1; - Moves[241].Type = "rock"; - Moves[241].Status = true; - Moves[241].DamageType = DamageType.Physical; - Moves[242].Name = "scratch (broken)"; - Moves[242].Power = -1; - Moves[242].Accuracy = 100; - Moves[242].Type = "normal"; - Moves[242].Status = false; - Moves[242].DamageType = DamageType.Physical; - Moves[243].Name = "scary face"; - Moves[243].Power = -1; - Moves[243].Accuracy = 100; - Moves[243].Type = "normal"; - Moves[243].Status = true; - Moves[243].DamageType = DamageType.Physical; - Moves[244].Name = "scratch"; - Moves[244].Power = 40; - Moves[244].Accuracy = 100; - Moves[244].Type = "normal"; - Moves[244].Status = false; - Moves[244].DamageType = DamageType.Physical; - Moves[245].Name = "screech"; - Moves[245].Power = -1; - Moves[245].Accuracy = 85; - Moves[245].Type = "normal"; - Moves[245].Status = true; - Moves[245].DamageType = DamageType.Physical; - Moves[246].Name = "seismic toss"; - Moves[246].Power = -1; - Moves[246].Accuracy = 100; - Moves[246].Type = "fighting"; - Moves[246].Status = false; - Moves[246].DamageType = DamageType.Physical; - Moves[247].Name = "selfdestruct"; - Moves[247].Power = 200; - Moves[247].Accuracy = 100; - Moves[247].Type = "normal"; - Moves[247].Status = false; - Moves[247].DamageType = DamageType.Physical; - Moves[248].Name = "shadow ball"; - Moves[248].Power = 80; - Moves[248].Accuracy = 100; - Moves[248].Type = "ghost"; - Moves[248].Status = false; - Moves[248].DamageType = DamageType.Special; - Moves[249].Name = "shadow punch"; - Moves[249].Power = 60; - Moves[249].Accuracy = -1; - Moves[249].Type = "ghost"; - Moves[249].Status = false; - Moves[249].DamageType = DamageType.Physical; - Moves[250].Name = "sharpen"; - Moves[250].Power = -1; - Moves[250].Accuracy = -1; - Moves[250].Type = "normal"; - Moves[250].Status = true; - Moves[250].DamageType = DamageType.Physical; - Moves[251].Name = "sheer cold"; - Moves[251].Power = -1; - Moves[251].Accuracy = -1; - Moves[251].Type = "ice"; - Moves[251].Status = true; - Moves[251].DamageType = DamageType.Special; - Moves[252].Name = "signal beam"; - Moves[252].Power = 75; - Moves[252].Accuracy = 100; - Moves[252].Type = "bug"; - Moves[252].Status = false; - Moves[252].DamageType = DamageType.Special; - Moves[253].Name = "silver wind"; - Moves[253].Power = 60; - Moves[253].Accuracy = 100; - Moves[253].Type = "bug"; - Moves[253].Status = false; - Moves[253].DamageType = DamageType.Special; - Moves[254].Name = "sing"; - Moves[254].Power = -1; - Moves[254].Accuracy = 55; - Moves[254].Type = "normal"; - Moves[254].Status = true; - Moves[254].DamageType = DamageType.Physical; - Moves[255].Name = "sketch"; - Moves[255].Power = -1; - Moves[255].Accuracy = -1; - Moves[255].Type = "normal"; - Moves[255].Status = true; - Moves[255].DamageType = DamageType.Physical; - Moves[256].Name = "skull bash"; - Moves[256].Power = 130; - Moves[256].Accuracy = 100; - Moves[256].Type = "normal"; - Moves[256].Status = false; - Moves[256].DamageType = DamageType.Physical; - Moves[257].Name = "sky attack"; - Moves[257].Power = 140; - Moves[257].Accuracy = 90; - Moves[257].Type = "flying"; - Moves[257].Status = false; - Moves[257].DamageType = DamageType.Physical; - Moves[258].Name = "sky uppercut"; - Moves[258].Power = 85; - Moves[258].Accuracy = 90; - Moves[258].Type = "fighting"; - Moves[258].Status = false; - Moves[258].DamageType = DamageType.Physical; - Moves[259].Name = "slack off"; - Moves[259].Power = -1; - Moves[259].Accuracy = 100; - Moves[259].Type = "normal"; - Moves[259].Status = true; - Moves[259].DamageType = DamageType.Physical; - Moves[260].Name = "slam"; - Moves[260].Power = 80; - Moves[260].Accuracy = 75; - Moves[260].Type = "normal"; - Moves[260].Status = false; - Moves[260].DamageType = DamageType.Physical; - Moves[261].Name = "slash"; - Moves[261].Power = 70; - Moves[261].Accuracy = 100; - Moves[261].Type = "normal"; - Moves[261].Status = false; - Moves[261].DamageType = DamageType.Physical; - Moves[262].Name = "sleep powder"; - Moves[262].Power = -1; - Moves[262].Accuracy = 75; - Moves[262].Type = "grass"; - Moves[262].Status = true; - Moves[262].DamageType = DamageType.Physical; - Moves[263].Name = "sleep talk"; - Moves[263].Power = -1; - Moves[263].Accuracy = -1; - Moves[263].Type = "normal"; - Moves[263].Status = true; - Moves[263].DamageType = DamageType.Physical; - Moves[264].Name = "sludge"; - Moves[264].Power = 65; - Moves[264].Accuracy = 100; - Moves[264].Type = "poison"; - Moves[264].Status = false; - Moves[264].DamageType = DamageType.Special; - Moves[265].Name = "sludge bomb"; - Moves[265].Power = 90; - Moves[265].Accuracy = 100; - Moves[265].Type = "poison"; - Moves[265].Status = false; - Moves[265].DamageType = DamageType.Special; - Moves[266].Name = "smelling salts"; - Moves[266].Power = 70; - Moves[266].Accuracy = 100; - Moves[266].Type = "normal"; - Moves[266].Status = false; - Moves[266].DamageType = DamageType.Physical; - Moves[267].Name = "smog"; - Moves[267].Power = 30; - Moves[267].Accuracy = 70; - Moves[267].Type = "poison"; - Moves[267].Status = false; - Moves[267].DamageType = DamageType.Special; - Moves[268].Name = "smokescreen"; - Moves[268].Power = -1; - Moves[268].Accuracy = 100; - Moves[268].Type = "normal"; - Moves[268].Status = true; - Moves[268].DamageType = DamageType.Physical; - Moves[269].Name = "broken"; - Moves[269].Power = 10; - Moves[269].Accuracy = 100; - Moves[269].Type = "normal"; - Moves[269].Status = false; - Moves[269].DamageType = DamageType.Physical; - Moves[270].Name = "snatch"; - Moves[270].Power = -1; - Moves[270].Accuracy = 100; - Moves[270].Type = "dark"; - Moves[270].Status = true; - Moves[270].DamageType = DamageType.Physical; - Moves[271].Name = "snore"; - Moves[271].Power = 50; - Moves[271].Accuracy = 100; - Moves[271].Type = "normal"; - Moves[271].Status = false; - Moves[271].DamageType = DamageType.Special; - Moves[272].Name = "softboiled"; - Moves[272].Power = -1; - Moves[272].Accuracy = -1; - Moves[272].Type = "normal"; - Moves[272].Status = true; - Moves[272].DamageType = DamageType.Physical; - Moves[273].Name = "solar beam"; - Moves[273].Power = 120; - Moves[273].Accuracy = 100; - Moves[273].Type = "grass"; - Moves[273].Status = false; - Moves[273].DamageType = DamageType.Special; - Moves[274].Name = "sonicboom"; - Moves[274].Power = -1; - Moves[274].Accuracy = 90; - Moves[274].Type = "normal"; - Moves[274].Status = false; - Moves[274].DamageType = DamageType.Special; - Moves[275].Name = "spark"; - Moves[275].Power = 65; - Moves[275].Accuracy = 100; - Moves[275].Type = "electric"; - Moves[275].Status = false; - Moves[275].DamageType = DamageType.Physical; - Moves[276].Name = "spider web"; - Moves[276].Power = -1; - Moves[276].Accuracy = 100; - Moves[276].Type = "bug"; - Moves[276].Status = true; - Moves[276].DamageType = DamageType.Physical; - Moves[277].Name = "spike cannon"; - Moves[277].Power = 20; - Moves[277].Accuracy = 100; - Moves[277].Type = "normal"; - Moves[277].Status = false; - Moves[277].DamageType = DamageType.Physical; - Moves[278].Name = "spikes"; - Moves[278].Power = -1; - Moves[278].Accuracy = -1; - Moves[278].Type = "ground"; - Moves[278].Status = true; - Moves[278].DamageType = DamageType.Physical; - Moves[279].Name = "spit up"; - Moves[279].Power = -1; - Moves[279].Accuracy = 100; - Moves[279].Type = "normal"; - Moves[279].Status = false; - Moves[279].DamageType = DamageType.Special; - Moves[280].Name = "spite"; - Moves[280].Power = -1; - Moves[280].Accuracy = 100; - Moves[280].Type = "ghost"; - Moves[280].Status = true; - Moves[280].DamageType = DamageType.Physical; - Moves[281].Name = "splash"; - Moves[281].Power = -1; - Moves[281].Accuracy = -1; - Moves[281].Type = "normal"; - Moves[281].Status = true; - Moves[281].DamageType = DamageType.Physical; - Moves[282].Name = "spore"; - Moves[282].Power = -1; - Moves[282].Accuracy = 100; - Moves[282].Type = "grass"; - Moves[282].Status = true; - Moves[282].DamageType = DamageType.Physical; - Moves[283].Name = "steel wing"; - Moves[283].Power = 70; - Moves[283].Accuracy = 90; - Moves[283].Type = "steel"; - Moves[283].Status = false; - Moves[283].DamageType = DamageType.Physical; - Moves[284].Name = "stockpile"; - Moves[284].Power = -1; - Moves[284].Accuracy = -1; - Moves[284].Type = "normal"; - Moves[284].Status = true; - Moves[284].DamageType = DamageType.Physical; - Moves[285].Name = "stomp"; - Moves[285].Power = 65; - Moves[285].Accuracy = 100; - Moves[285].Type = "normal"; - Moves[285].Status = false; - Moves[285].DamageType = DamageType.Physical; - Moves[286].Name = "string shot"; - Moves[286].Power = -1; - Moves[286].Accuracy = 95; - Moves[286].Type = "bug"; - Moves[286].Status = true; - Moves[286].DamageType = DamageType.Physical; - Moves[287].Name = "stun spore"; - Moves[287].Power = -1; - Moves[287].Accuracy = 75; - Moves[287].Type = "grass"; - Moves[287].Status = true; - Moves[287].DamageType = DamageType.Physical; - Moves[288].Name = "submission"; - Moves[288].Power = 80; - Moves[288].Accuracy = 80; - Moves[288].Type = "fighting"; - Moves[288].Status = false; - Moves[288].DamageType = DamageType.Physical; - Moves[289].Name = "substitute"; - Moves[289].Power = -1; - Moves[289].Accuracy = -1; - Moves[289].Type = "normal"; - Moves[289].Status = true; - Moves[289].DamageType = DamageType.Physical; - Moves[290].Name = "sunny day"; - Moves[290].Power = -1; - Moves[290].Accuracy = -1; - Moves[290].Type = "fire"; - Moves[290].Status = true; - Moves[290].DamageType = DamageType.Physical; - Moves[291].Name = "super fang"; - Moves[291].Power = -1; - Moves[291].Accuracy = 90; - Moves[291].Type = "normal"; - Moves[291].Status = false; - Moves[291].DamageType = DamageType.Physical; - Moves[292].Name = "superpower"; - Moves[292].Power = 120; - Moves[292].Accuracy = 100; - Moves[292].Type = "fighting"; - Moves[292].Status = false; - Moves[292].DamageType = DamageType.Physical; - Moves[293].Name = "supersonic"; - Moves[293].Power = -1; - Moves[293].Accuracy = 55; - Moves[293].Type = "normal"; - Moves[293].Status = true; - Moves[293].DamageType = DamageType.Physical; - Moves[294].Name = "swagger"; - Moves[294].Power = -1; - Moves[294].Accuracy = 90; - Moves[294].Type = "normal"; - Moves[294].Status = true; - Moves[294].DamageType = DamageType.Physical; - Moves[295].Name = "swallow"; - Moves[295].Power = -1; - Moves[295].Accuracy = -1; - Moves[295].Type = "normal"; - Moves[295].Status = true; - Moves[295].DamageType = DamageType.Physical; - Moves[296].Name = "sweet kiss"; - Moves[296].Power = -1; - Moves[296].Accuracy = 75; - Moves[296].Type = "fairy"; - Moves[296].Status = true; - Moves[296].DamageType = DamageType.Physical; - Moves[297].Name = "sweet scent"; - Moves[297].Power = -1; - Moves[297].Accuracy = 100; - Moves[297].Type = "normal"; - Moves[297].Status = true; - Moves[297].DamageType = DamageType.Physical; - Moves[298].Name = "swift"; - Moves[298].Power = 60; - Moves[298].Accuracy = -1; - Moves[298].Type = "normal"; - Moves[298].Status = false; - Moves[298].DamageType = DamageType.Special; - Moves[299].Name = "swords dance"; - Moves[299].Power = -1; - Moves[299].Accuracy = -1; - Moves[299].Type = "normal"; - Moves[299].Status = true; - Moves[299].DamageType = DamageType.Physical; - Moves[300].Name = "synthesis"; - Moves[300].Power = -1; - Moves[300].Accuracy = -1; - Moves[300].Type = "grass"; - Moves[300].Status = true; - Moves[300].DamageType = DamageType.Physical; - Moves[301].Name = "tackle"; - Moves[301].Power = 50; - Moves[301].Accuracy = 100; - Moves[301].Type = "normal"; - Moves[301].Status = false; - Moves[301].DamageType = DamageType.Physical; - Moves[302].Name = "tail glow"; - Moves[302].Power = -1; - Moves[302].Accuracy = 100; - Moves[302].Type = "bug"; - Moves[302].Status = true; - Moves[302].DamageType = DamageType.Physical; - Moves[303].Name = "tail whip"; - Moves[303].Power = -1; - Moves[303].Accuracy = 100; - Moves[303].Type = "normal"; - Moves[303].Status = true; - Moves[303].DamageType = DamageType.Physical; - Moves[304].Name = "take down"; - Moves[304].Power = 90; - Moves[304].Accuracy = 85; - Moves[304].Type = "normal"; - Moves[304].Status = false; - Moves[304].DamageType = DamageType.Physical; - Moves[305].Name = "taunt"; - Moves[305].Power = -1; - Moves[305].Accuracy = 100; - Moves[305].Type = "dark"; - Moves[305].Status = true; - Moves[305].DamageType = DamageType.Physical; - Moves[306].Name = "teeter dance"; - Moves[306].Power = -1; - Moves[306].Accuracy = 100; - Moves[306].Type = "normal"; - Moves[306].Status = true; - Moves[306].DamageType = DamageType.Physical; - Moves[307].Name = "teleport"; - Moves[307].Power = -1; - Moves[307].Accuracy = -1; - Moves[307].Type = "psychic"; - Moves[307].Status = true; - Moves[307].DamageType = DamageType.Physical; - Moves[308].Name = "thief"; - Moves[308].Power = 60; - Moves[308].Accuracy = 100; - Moves[308].Type = "dark"; - Moves[308].Status = false; - Moves[308].DamageType = DamageType.Physical; - Moves[309].Name = "thrash"; - Moves[309].Power = 120; - Moves[309].Accuracy = 100; - Moves[309].Type = "normal"; - Moves[309].Status = false; - Moves[309].DamageType = DamageType.Physical; - Moves[310].Name = "thunder"; - Moves[310].Power = 110; - Moves[310].Accuracy = 70; - Moves[310].Type = "electric"; - Moves[310].Status = false; - Moves[310].DamageType = DamageType.Special; - Moves[311].Name = "thunder wave"; - Moves[311].Power = -1; - Moves[311].Accuracy = 100; - Moves[311].Type = "electric"; - Moves[311].Status = true; - Moves[311].DamageType = DamageType.Physical; - Moves[312].Name = "thunderbolt"; - Moves[312].Power = 90; - Moves[312].Accuracy = 100; - Moves[312].Type = "electric"; - Moves[312].Status = false; - Moves[312].DamageType = DamageType.Special; - Moves[313].Name = "thunder punch"; - Moves[313].Power = 75; - Moves[313].Accuracy = 100; - Moves[313].Type = "electric"; - Moves[313].Status = false; - Moves[313].DamageType = DamageType.Physical; - Moves[314].Name = "thundershock"; - Moves[314].Power = 40; - Moves[314].Accuracy = 100; - Moves[314].Type = "electric"; - Moves[314].Status = false; - Moves[314].DamageType = DamageType.Special; - Moves[315].Name = "tickle"; - Moves[315].Power = -1; - Moves[315].Accuracy = 100; - Moves[315].Type = "normal"; - Moves[315].Status = true; - Moves[315].DamageType = DamageType.Physical; - Moves[316].Name = "torment"; - Moves[316].Power = -1; - Moves[316].Accuracy = 100; - Moves[316].Type = "dark"; - Moves[316].Status = true; - Moves[316].DamageType = DamageType.Physical; - Moves[317].Name = "toxic"; - Moves[317].Power = -1; - Moves[317].Accuracy = 90; - Moves[317].Type = "poison"; - Moves[317].Status = true; - Moves[317].DamageType = DamageType.Physical; - Moves[318].Name = "transform"; - Moves[318].Power = -1; - Moves[318].Accuracy = -1; - Moves[318].Type = "normal"; - Moves[318].Status = true; - Moves[318].DamageType = DamageType.Physical; - Moves[319].Name = "tri attack"; - Moves[319].Power = 80; - Moves[319].Accuracy = 100; - Moves[319].Type = "normal"; - Moves[319].Status = false; - Moves[319].DamageType = DamageType.Special; - Moves[320].Name = "trick"; - Moves[320].Power = -1; - Moves[320].Accuracy = 100; - Moves[320].Type = "psychic"; - Moves[320].Status = false; - Moves[320].DamageType = DamageType.Physical; - Moves[321].Name = "triple kick"; - Moves[321].Power = 10; - Moves[321].Accuracy = 90; - Moves[321].Type = "fighting"; - Moves[321].Status = false; - Moves[321].DamageType = DamageType.Physical; - Moves[322].Name = "twineedle"; - Moves[322].Power = 25; - Moves[322].Accuracy = 100; - Moves[322].Type = "bug"; - Moves[322].Status = false; - Moves[322].DamageType = DamageType.Physical; - Moves[323].Name = "twister"; - Moves[323].Power = 40; - Moves[323].Accuracy = 100; - Moves[323].Type = "dragon"; - Moves[323].Status = false; - Moves[323].DamageType = DamageType.Special; - Moves[324].Name = "uproar"; - Moves[324].Power = 90; - Moves[324].Accuracy = 100; - Moves[324].Type = "normal"; - Moves[324].Status = false; - Moves[324].DamageType = DamageType.Special; - Moves[325].Name = "blank"; - Moves[325].Power = -1; - Moves[325].Accuracy = 100; - Moves[325].Type = "normal"; - Moves[325].Status = false; - Moves[325].DamageType = DamageType.Physical; - Moves[326].Name = "vicegrip"; - Moves[326].Power = 55; - Moves[326].Accuracy = 100; - Moves[326].Type = "normal"; - Moves[326].Status = false; - Moves[326].DamageType = DamageType.Physical; - Moves[327].Name = "vine whip"; - Moves[327].Power = 45; - Moves[327].Accuracy = 100; - Moves[327].Type = "grass"; - Moves[327].Status = false; - Moves[327].DamageType = DamageType.Physical; - Moves[328].Name = "vital throw"; - Moves[328].Power = 70; - Moves[328].Accuracy = -1; - Moves[328].Type = "fighting"; - Moves[328].Status = false; - Moves[328].DamageType = DamageType.Physical; - Moves[329].Name = "water gun"; - Moves[329].Power = 40; - Moves[329].Accuracy = 100; - Moves[329].Type = "water"; - Moves[329].Status = false; - Moves[329].DamageType = DamageType.Special; - Moves[330].Name = "water pulse"; - Moves[330].Power = 60; - Moves[330].Accuracy = 100; - Moves[330].Type = "water"; - Moves[330].Status = false; - Moves[330].DamageType = DamageType.Special; - Moves[331].Name = "water sport"; - Moves[331].Power = -1; - Moves[331].Accuracy = 100; - Moves[331].Type = "water"; - Moves[331].Status = false; - Moves[331].DamageType = DamageType.Physical; - Moves[332].Name = "water spout"; - Moves[332].Power = -1; - Moves[332].Accuracy = 100; - Moves[332].Type = "water"; - Moves[332].Status = false; - Moves[332].DamageType = DamageType.Special; - Moves[333].Name = "waterfall"; - Moves[333].Power = 80; - Moves[333].Accuracy = 100; - Moves[333].Type = "water"; - Moves[333].Status = false; - Moves[333].DamageType = DamageType.Physical; - Moves[334].Name = "weather ball"; - Moves[334].Power = 50; - Moves[334].Accuracy = 100; - Moves[334].Type = "normal"; - Moves[334].Status = false; - Moves[334].DamageType = DamageType.Special; - Moves[335].Name = "whirlpool"; - Moves[335].Power = 35; - Moves[335].Accuracy = 85; - Moves[335].Type = "water"; - Moves[335].Status = false; - Moves[335].DamageType = DamageType.Special; - Moves[336].Name = "whirlwind"; - Moves[336].Power = -1; - Moves[336].Accuracy = -1; - Moves[336].Type = "normal"; - Moves[336].Status = true; - Moves[336].DamageType = DamageType.Physical; - Moves[337].Name = "will-o-wisp"; - Moves[337].Power = -1; - Moves[337].Accuracy = 85; - Moves[337].Type = "fire"; - Moves[337].Status = true; - Moves[337].DamageType = DamageType.Physical; - Moves[338].Name = "wing attack"; - Moves[338].Power = 60; - Moves[338].Accuracy = 100; - Moves[338].Type = "flying"; - Moves[338].Status = false; - Moves[338].DamageType = DamageType.Physical; - Moves[339].Name = "wish"; - Moves[339].Power = -1; - Moves[339].Accuracy = -1; - Moves[339].Type = "normal"; - Moves[339].Status = true; - Moves[339].DamageType = DamageType.Physical; - Moves[340].Name = "withdraw"; - Moves[340].Power = -1; - Moves[340].Accuracy = -1; - Moves[340].Type = "water"; - Moves[340].Status = true; - Moves[340].DamageType = DamageType.Physical; - Moves[341].Name = "wrap"; - Moves[341].Power = 15; - Moves[341].Accuracy = 90; - Moves[341].Type = "normal"; - Moves[341].Status = false; - Moves[341].DamageType = DamageType.Physical; - Moves[342].Name = "yawn"; - Moves[342].Power = -1; - Moves[342].Accuracy = 100; - Moves[342].Type = "normal"; - Moves[342].Status = true; - Moves[342].DamageType = DamageType.Physical; - Moves[343].Name = "zap cannon"; - Moves[343].Power = 120; - Moves[343].Accuracy = 50; - Moves[343].Type = "electric"; - Moves[343].Status = false; - Moves[343].DamageType = DamageType.Special; - Moves[344].Name = "cut"; - Moves[344].Power = 50; - Moves[344].Accuracy = 95; - Moves[344].Type = "normal"; - Moves[344].Status = false; - Moves[344].DamageType = DamageType.Physical; - Moves[345].Name = "broken"; - Moves[345].Power = -1; - Moves[345].Accuracy = 100; - Moves[345].Type = "bug"; - Moves[345].Status = true; - Moves[345].DamageType = DamageType.Physical; - Moves[346].Name = "surf"; - Moves[346].Power = 90; - Moves[346].Accuracy = 100; - Moves[346].Type = "water"; - Moves[346].Status = false; - Moves[346].DamageType = DamageType.Special; - Moves[347].Name = "strength"; - Moves[347].Power = 80; - Moves[347].Accuracy = 100; - Moves[347].Type = "normal"; - Moves[347].Status = false; - Moves[347].DamageType = DamageType.Physical; - Moves[348].Name = "flash"; - Moves[348].Power = -1; - Moves[348].Accuracy = 100; - Moves[348].Type = "normal"; - Moves[348].Status = true; - Moves[348].DamageType = DamageType.Physical; - Moves[349].Name = "struggle"; - Moves[349].Power = 50; - Moves[349].Accuracy = 100; - Moves[349].Type = "normal"; - Moves[349].Status = false; - Moves[349].DamageType = DamageType.Physical; - Moves[350].Name = "broken"; - Moves[350].Power = -1; - Moves[350].Accuracy = 100; - Moves[350].Type = "normal"; - Moves[350].Status = false; - Moves[350].DamageType = DamageType.Physical; - Moves[351].Name = "broken"; - Moves[351].Power = 20; - Moves[351].Accuracy = 100; - Moves[351].Type = "ground"; - Moves[351].Status = false; - Moves[351].DamageType = DamageType.Physical; - Moves[352].Name = "return"; - Moves[352].Power = -1; - Moves[352].Accuracy = 100; - Moves[352].Type = "normal"; - Moves[352].Status = false; - Moves[352].DamageType = DamageType.Physical; - Moves[353].Name = "frustration"; - Moves[353].Power = -1; - Moves[353].Accuracy = 100; - Moves[353].Type = "normal"; - Moves[353].Status = false; - Moves[353].DamageType = DamageType.Physical; - Moves[354].Name = "rock smash"; - Moves[354].Power = 40; - Moves[354].Accuracy = 100; - Moves[354].Type = "fighting"; - Moves[354].Status = false; - Moves[354].DamageType = DamageType.Physical; - Moves[355].Name = "facade"; - Moves[355].Power = 70; - Moves[355].Accuracy = 100; - Moves[355].Type = "normal"; - Moves[355].Status = false; - Moves[355].DamageType = DamageType.Physical; - Moves[356].Name = "skill swap"; - Moves[356].Power = -1; - Moves[356].Accuracy = 100; - Moves[356].Type = "psychic"; - Moves[356].Status = true; - Moves[356].DamageType = DamageType.Physical; - Moves[357].Name = "secret power"; - Moves[357].Power = 70; - Moves[357].Accuracy = 100; - Moves[357].Type = "normal"; - Moves[357].Status = false; - Moves[357].DamageType = DamageType.Physical; - Moves[358].Name = "dive"; - Moves[358].Power = 80; - Moves[358].Accuracy = 100; - Moves[358].Type = "water"; - Moves[358].Status = false; - Moves[358].DamageType = DamageType.Physical; - Moves[359].Name = "blast burn"; - Moves[359].Power = 150; - Moves[359].Accuracy = 90; - Moves[359].Type = "fire"; - Moves[359].Status = false; - Moves[359].DamageType = DamageType.Special; - Moves[360].Name = "hydro cannon"; - Moves[360].Power = 150; - Moves[360].Accuracy = 90; - Moves[360].Type = "water"; - Moves[360].Status = false; - Moves[360].DamageType = DamageType.Special; - Moves[361].Name = "overheat"; - Moves[361].Power = 130; - Moves[361].Accuracy = 90; - Moves[361].Type = "fire"; - Moves[361].Status = false; - Moves[361].DamageType = DamageType.Special; - Moves[362].Name = "frenzy plant"; - Moves[362].Power = 150; - Moves[362].Accuracy = 90; - Moves[362].Type = "grass"; - Moves[362].Status = false; - Moves[362].DamageType = DamageType.Special; - Moves[363].Name = "volt tackle"; - Moves[363].Power = 120; - Moves[363].Accuracy = 100; - Moves[363].Type = "electric"; - Moves[363].Status = false; - Moves[363].DamageType = DamageType.Physical; - Moves[364].Name = "shock wave"; - Moves[364].Power = 60; - Moves[364].Accuracy = -1; - Moves[364].Type = "electric"; - Moves[364].Status = false; - Moves[364].DamageType = DamageType.Special; - Moves[365].Name = "roost"; - Moves[365].Power = -1; - Moves[365].Accuracy = -1; - Moves[365].Type = "flying"; - Moves[365].Status = true; - Moves[365].DamageType = DamageType.Physical; - Moves[366].Name = "gravity"; - Moves[366].Power = -1; - Moves[366].Accuracy = -1; - Moves[366].Type = "psychic"; - Moves[366].Status = true; - Moves[366].DamageType = DamageType.Physical; - Moves[367].Name = "miracle eye"; - Moves[367].Power = -1; - Moves[367].Accuracy = -1; - Moves[367].Type = "psychic"; - Moves[367].Status = true; - Moves[367].DamageType = DamageType.Physical; - Moves[368].Name = "wake-up slap"; - Moves[368].Power = 70; - Moves[368].Accuracy = 100; - Moves[368].Type = "fighting"; - Moves[368].Status = false; - Moves[368].DamageType = DamageType.Physical; - Moves[369].Name = "hammer arm"; - Moves[369].Power = 100; - Moves[369].Accuracy = 90; - Moves[369].Type = "fighting"; - Moves[369].Status = false; - Moves[369].DamageType = DamageType.Physical; - Moves[370].Name = "gyro ball"; - Moves[370].Power = -1; - Moves[370].Accuracy = 100; - Moves[370].Type = "steel"; - Moves[370].Status = false; - Moves[370].DamageType = DamageType.Physical; - Moves[371].Name = "healing wish"; - Moves[371].Power = -1; - Moves[371].Accuracy = -1; - Moves[371].Type = "psychic"; - Moves[371].Status = true; - Moves[371].DamageType = DamageType.Physical; - Moves[372].Name = "brine"; - Moves[372].Power = 65; - Moves[372].Accuracy = 100; - Moves[372].Type = "water"; - Moves[372].Status = false; - Moves[372].DamageType = DamageType.Special; - Moves[373].Name = "natural gift"; - Moves[373].Power = -1; - Moves[373].Accuracy = 100; - Moves[373].Type = " "; - Moves[373].Status = false; - Moves[373].DamageType = DamageType.Physical; - Moves[374].Name = "feint"; - Moves[374].Power = 30; - Moves[374].Accuracy = 100; - Moves[374].Type = "normal"; - Moves[374].Status = false; - Moves[374].DamageType = DamageType.Physical; - Moves[375].Name = "pluck"; - Moves[375].Power = 60; - Moves[375].Accuracy = 100; - Moves[375].Type = "flying"; - Moves[375].Status = false; - Moves[375].DamageType = DamageType.Physical; - Moves[376].Name = "tailwind"; - Moves[376].Power = -1; - Moves[376].Accuracy = -1; - Moves[376].Type = "flying"; - Moves[376].Status = true; - Moves[376].DamageType = DamageType.Physical; - Moves[377].Name = "acupressure"; - Moves[377].Power = -1; - Moves[377].Accuracy = -1; - Moves[377].Type = "normal"; - Moves[377].Status = true; - Moves[377].DamageType = DamageType.Physical; - Moves[378].Name = "metal burst"; - Moves[378].Power = -1; - Moves[378].Accuracy = 100; - Moves[378].Type = "steel"; - Moves[378].Status = false; - Moves[378].DamageType = DamageType.Physical; - Moves[379].Name = "u-turn"; - Moves[379].Power = 70; - Moves[379].Accuracy = 100; - Moves[379].Type = "bug"; - Moves[379].Status = false; - Moves[379].DamageType = DamageType.Physical; - Moves[380].Name = "close combat"; - Moves[380].Power = 120; - Moves[380].Accuracy = 100; - Moves[380].Type = "fighting"; - Moves[380].Status = false; - Moves[380].DamageType = DamageType.Physical; - Moves[381].Name = "payback"; - Moves[381].Power = 50; - Moves[381].Accuracy = 100; - Moves[381].Type = "dark"; - Moves[381].Status = false; - Moves[381].DamageType = DamageType.Physical; - Moves[382].Name = "assurance"; - Moves[382].Power = 60; - Moves[382].Accuracy = 100; - Moves[382].Type = "dark"; - Moves[382].Status = false; - Moves[382].DamageType = DamageType.Physical; - Moves[383].Name = "embargo"; - Moves[383].Power = -1; - Moves[383].Accuracy = 100; - Moves[383].Type = "dark"; - Moves[383].Status = true; - Moves[383].DamageType = DamageType.Physical; - Moves[384].Name = "fling"; - Moves[384].Power = -1; - Moves[384].Accuracy = 100; - Moves[384].Type = "dark"; - Moves[384].Status = false; - Moves[384].DamageType = DamageType.Physical; - Moves[385].Name = "psycho shift"; - Moves[385].Power = -1; - Moves[385].Accuracy = 100; - Moves[385].Type = "psychic"; - Moves[385].Status = true; - Moves[385].DamageType = DamageType.Physical; - Moves[386].Name = "trump card"; - Moves[386].Power = -1; - Moves[386].Accuracy = -1; - Moves[386].Type = "normal"; - Moves[386].Status = false; - Moves[386].DamageType = DamageType.Special; - Moves[387].Name = "heal block"; - Moves[387].Power = -1; - Moves[387].Accuracy = 100; - Moves[387].Type = "psychic"; - Moves[387].Status = true; - Moves[387].DamageType = DamageType.Physical; - Moves[388].Name = "wring out"; - Moves[388].Power = -1; - Moves[388].Accuracy = 100; - Moves[388].Type = "normal"; - Moves[388].Status = false; - Moves[388].DamageType = DamageType.Special; - Moves[389].Name = "power trick"; - Moves[389].Power = -1; - Moves[389].Accuracy = -1; - Moves[389].Type = "psychic"; - Moves[389].Status = true; - Moves[389].DamageType = DamageType.Physical; - Moves[390].Name = "gastro acid"; - Moves[390].Power = -1; - Moves[390].Accuracy = 100; - Moves[390].Type = "poison"; - Moves[390].Status = true; - Moves[390].DamageType = DamageType.Physical; - Moves[391].Name = "lucky chant"; - Moves[391].Power = -1; - Moves[391].Accuracy = -1; - Moves[391].Type = "normal"; - Moves[391].Status = true; - Moves[391].DamageType = DamageType.Physical; - Moves[392].Name = "me first"; - Moves[392].Power = -1; - Moves[392].Accuracy = -1; - Moves[392].Type = "normal"; - Moves[392].Status = true; - Moves[392].DamageType = DamageType.Physical; - Moves[393].Name = "copycat"; - Moves[393].Power = -1; - Moves[393].Accuracy = -1; - Moves[393].Type = "normal"; - Moves[393].Status = true; - Moves[393].DamageType = DamageType.Physical; - Moves[394].Name = "power swap"; - Moves[394].Power = -1; - Moves[394].Accuracy = -1; - Moves[394].Type = "psychic"; - Moves[394].Status = true; - Moves[394].DamageType = DamageType.Physical; - Moves[395].Name = "guard swap"; - Moves[395].Power = -1; - Moves[395].Accuracy = -1; - Moves[395].Type = "psychic"; - Moves[395].Status = false; - Moves[395].DamageType = DamageType.Physical; - Moves[396].Name = "punishment"; - Moves[396].Power = -1; - Moves[396].Accuracy = 100; - Moves[396].Type = "dark"; - Moves[396].Status = false; - Moves[396].DamageType = DamageType.Physical; - Moves[397].Name = "last resort"; - Moves[397].Power = 140; - Moves[397].Accuracy = 100; - Moves[397].Type = "normal"; - Moves[397].Status = false; - Moves[397].DamageType = DamageType.Physical; - Moves[398].Name = "worry seed"; - Moves[398].Power = -1; - Moves[398].Accuracy = 100; - Moves[398].Type = "grass"; - Moves[398].Status = true; - Moves[398].DamageType = DamageType.Physical; - Moves[399].Name = "sucker punch"; - Moves[399].Power = 80; - Moves[399].Accuracy = 100; - Moves[399].Type = "dark"; - Moves[399].Status = false; - Moves[399].DamageType = DamageType.Physical; - Moves[400].Name = "toxic spikes"; - Moves[400].Power = -1; - Moves[400].Accuracy = -1; - Moves[400].Type = "poison"; - Moves[400].Status = true; - Moves[400].DamageType = DamageType.Physical; - Moves[401].Name = "heart swap"; - Moves[401].Power = -1; - Moves[401].Accuracy = -1; - Moves[401].Type = "psychic"; - Moves[401].Status = true; - Moves[401].DamageType = DamageType.Physical; - Moves[402].Name = "aqua ring"; - Moves[402].Power = -1; - Moves[402].Accuracy = -1; - Moves[402].Type = "water"; - Moves[402].Status = true; - Moves[402].DamageType = DamageType.Physical; - Moves[403].Name = "magnet rise"; - Moves[403].Power = -1; - Moves[403].Accuracy = -1; - Moves[403].Type = "electric"; - Moves[403].Status = true; - Moves[403].DamageType = DamageType.Physical; - Moves[404].Name = "flare blitz"; - Moves[404].Power = 120; - Moves[404].Accuracy = 100; - Moves[404].Type = "fire"; - Moves[404].Status = false; - Moves[404].DamageType = DamageType.Physical; - Moves[405].Name = "force palm"; - Moves[405].Power = 60; - Moves[405].Accuracy = 100; - Moves[405].Type = "fighting"; - Moves[405].Status = false; - Moves[405].DamageType = DamageType.Physical; - Moves[406].Name = "aura sphere"; - Moves[406].Power = 80; - Moves[406].Accuracy = -1; - Moves[406].Type = "fighting"; - Moves[406].Status = false; - Moves[406].DamageType = DamageType.Special; - Moves[407].Name = "rock polish"; - Moves[407].Power = -1; - Moves[407].Accuracy = -1; - Moves[407].Type = "rock"; - Moves[407].Status = true; - Moves[407].DamageType = DamageType.Physical; - Moves[408].Name = "poison jab"; - Moves[408].Power = 80; - Moves[408].Accuracy = 100; - Moves[408].Type = "poison"; - Moves[408].Status = false; - Moves[408].DamageType = DamageType.Physical; - Moves[409].Name = "dark pulse"; - Moves[409].Power = 80; - Moves[409].Accuracy = 100; - Moves[409].Type = "dark"; - Moves[409].Status = false; - Moves[409].DamageType = DamageType.Special; - Moves[410].Name = "night slash"; - Moves[410].Power = 70; - Moves[410].Accuracy = 100; - Moves[410].Type = "dark"; - Moves[410].Status = false; - Moves[410].DamageType = DamageType.Physical; - Moves[411].Name = "aqua tail"; - Moves[411].Power = 90; - Moves[411].Accuracy = 90; - Moves[411].Type = "water"; - Moves[411].Status = false; - Moves[411].DamageType = DamageType.Physical; - Moves[412].Name = "seed bomb"; - Moves[412].Power = 80; - Moves[412].Accuracy = 100; - Moves[412].Type = "grass"; - Moves[412].Status = false; - Moves[412].DamageType = DamageType.Physical; - Moves[413].Name = "air slash"; - Moves[413].Power = 75; - Moves[413].Accuracy = 95; - Moves[413].Type = "flying"; - Moves[413].Status = false; - Moves[413].DamageType = DamageType.Special; - Moves[414].Name = "x-scissor"; - Moves[414].Power = 80; - Moves[414].Accuracy = 100; - Moves[414].Type = "bug"; - Moves[414].Status = false; - Moves[414].DamageType = DamageType.Physical; - Moves[415].Name = "bug buzz"; - Moves[415].Power = 90; - Moves[415].Accuracy = 100; - Moves[415].Type = "bug"; - Moves[415].Status = false; - Moves[415].DamageType = DamageType.Special; - Moves[416].Name = "dragon pulse"; - Moves[416].Power = 85; - Moves[416].Accuracy = 100; - Moves[416].Type = "dragon"; - Moves[416].Status = false; - Moves[416].DamageType = DamageType.Special; - Moves[417].Name = "dragon rush"; - Moves[417].Power = 100; - Moves[417].Accuracy = 75; - Moves[417].Type = "dragon"; - Moves[417].Status = false; - Moves[417].DamageType = DamageType.Physical; - Moves[418].Name = "power gem"; - Moves[418].Power = 80; - Moves[418].Accuracy = 100; - Moves[418].Type = "rock"; - Moves[418].Status = false; - Moves[418].DamageType = DamageType.Special; - Moves[419].Name = "drain punch"; - Moves[419].Power = 75; - Moves[419].Accuracy = 100; - Moves[419].Type = "fighting"; - Moves[419].Status = false; - Moves[419].DamageType = DamageType.Physical; - Moves[420].Name = "vacuum wave"; - Moves[420].Power = 40; - Moves[420].Accuracy = 100; - Moves[420].Type = "fighting"; - Moves[420].Status = false; - Moves[420].DamageType = DamageType.Special; - Moves[421].Name = "focus blast"; - Moves[421].Power = 120; - Moves[421].Accuracy = 70; - Moves[421].Type = "fighting"; - Moves[421].Status = false; - Moves[421].DamageType = DamageType.Special; - Moves[422].Name = "energy ball"; - Moves[422].Power = 90; - Moves[422].Accuracy = 100; - Moves[422].Type = "grass"; - Moves[422].Status = false; - Moves[422].DamageType = DamageType.Special; - Moves[423].Name = "brave bird"; - Moves[423].Power = 120; - Moves[423].Accuracy = 100; - Moves[423].Type = "flying"; - Moves[423].Status = false; - Moves[423].DamageType = DamageType.Physical; - Moves[424].Name = "earth power"; - Moves[424].Power = 90; - Moves[424].Accuracy = 100; - Moves[424].Type = "ground"; - Moves[424].Status = false; - Moves[424].DamageType = DamageType.Special; - Moves[425].Name = "switcheroo"; - Moves[425].Power = -1; - Moves[425].Accuracy = 100; - Moves[425].Type = "dark"; - Moves[425].Status = true; - Moves[425].DamageType = DamageType.Physical; - Moves[426].Name = "giga impact"; - Moves[426].Power = 150; - Moves[426].Accuracy = 90; - Moves[426].Type = "normal"; - Moves[426].Status = false; - Moves[426].DamageType = DamageType.Physical; - Moves[427].Name = "nasty plot"; - Moves[427].Power = -1; - Moves[427].Accuracy = -1; - Moves[427].Type = "dark"; - Moves[427].Status = true; - Moves[427].DamageType = DamageType.Physical; - Moves[428].Name = "bullet punch"; - Moves[428].Power = 40; - Moves[428].Accuracy = 100; - Moves[428].Type = "steel"; - Moves[428].Status = false; - Moves[428].DamageType = DamageType.Physical; - Moves[429].Name = "avalanche"; - Moves[429].Power = 60; - Moves[429].Accuracy = 100; - Moves[429].Type = "ice"; - Moves[429].Status = false; - Moves[429].DamageType = DamageType.Physical; - Moves[430].Name = "ice shard"; - Moves[430].Power = 40; - Moves[430].Accuracy = 100; - Moves[430].Type = "ice"; - Moves[430].Status = false; - Moves[430].DamageType = DamageType.Physical; - Moves[431].Name = "shadow claw"; - Moves[431].Power = 70; - Moves[431].Accuracy = 100; - Moves[431].Type = "ghost"; - Moves[431].Status = false; - Moves[431].DamageType = DamageType.Physical; - Moves[432].Name = "thunder fang"; - Moves[432].Power = 65; - Moves[432].Accuracy = 95; - Moves[432].Type = "electric"; - Moves[432].Status = false; - Moves[432].DamageType = DamageType.Physical; - Moves[433].Name = "ice fang"; - Moves[433].Power = 65; - Moves[433].Accuracy = 95; - Moves[433].Type = "ice"; - Moves[433].Status = false; - Moves[433].DamageType = DamageType.Physical; - Moves[434].Name = "fire fang"; - Moves[434].Power = 65; - Moves[434].Accuracy = 95; - Moves[434].Type = "fire"; - Moves[434].Status = false; - Moves[434].DamageType = DamageType.Physical; - Moves[435].Name = "shadow sneak"; - Moves[435].Power = 40; - Moves[435].Accuracy = 100; - Moves[435].Type = "ghost"; - Moves[435].Status = false; - Moves[435].DamageType = DamageType.Physical; - Moves[436].Name = "mud bomb"; - Moves[436].Power = 65; - Moves[436].Accuracy = 85; - Moves[436].Type = "ground"; - Moves[436].Status = false; - Moves[436].DamageType = DamageType.Special; - Moves[437].Name = "psycho cut"; - Moves[437].Power = 70; - Moves[437].Accuracy = 100; - Moves[437].Type = "psychic"; - Moves[437].Status = false; - Moves[437].DamageType = DamageType.Physical; - Moves[438].Name = "zen headbutt"; - Moves[438].Power = 80; - Moves[438].Accuracy = 90; - Moves[438].Type = "psychic"; - Moves[438].Status = false; - Moves[438].DamageType = DamageType.Physical; - Moves[439].Name = "mirror shot"; - Moves[439].Power = 65; - Moves[439].Accuracy = 85; - Moves[439].Type = "steel"; - Moves[439].Status = false; - Moves[439].DamageType = DamageType.Special; - Moves[440].Name = "flash cannon"; - Moves[440].Power = 80; - Moves[440].Accuracy = 100; - Moves[440].Type = "steel"; - Moves[440].Status = false; - Moves[440].DamageType = DamageType.Special; - Moves[441].Name = "rock climb"; - Moves[441].Power = 90; - Moves[441].Accuracy = 85; - Moves[441].Type = "normal"; - Moves[441].Status = false; - Moves[441].DamageType = DamageType.Physical; - Moves[442].Name = "defog"; - Moves[442].Power = -1; - Moves[442].Accuracy = -1; - Moves[442].Type = "flying"; - Moves[442].Status = false; - Moves[442].DamageType = DamageType.Physical; - Moves[443].Name = "trick room"; - Moves[443].Power = -1; - Moves[443].Accuracy = -1; - Moves[443].Type = "psychic"; - Moves[443].Status = true; - Moves[443].DamageType = DamageType.Physical; - Moves[444].Name = "draco meteor"; - Moves[444].Power = 130; - Moves[444].Accuracy = 90; - Moves[444].Type = "dragon"; - Moves[444].Status = false; - Moves[444].DamageType = DamageType.Special; - Moves[445].Name = "discharge"; - Moves[445].Power = 80; - Moves[445].Accuracy = 100; - Moves[445].Type = "electric"; - Moves[445].Status = false; - Moves[445].DamageType = DamageType.Special; - Moves[446].Name = "lava plume"; - Moves[446].Power = 80; - Moves[446].Accuracy = 100; - Moves[446].Type = "fire"; - Moves[446].Status = false; - Moves[446].DamageType = DamageType.Special; - Moves[447].Name = "leaf storm"; - Moves[447].Power = 130; - Moves[447].Accuracy = 90; - Moves[447].Type = "grass"; - Moves[447].Status = false; - Moves[447].DamageType = DamageType.Special; - Moves[448].Name = "power whip"; - Moves[448].Power = 120; - Moves[448].Accuracy = 85; - Moves[448].Type = "grass"; - Moves[448].Status = false; - Moves[448].DamageType = DamageType.Physical; - Moves[449].Name = "rock wrecker"; - Moves[449].Power = 150; - Moves[449].Accuracy = 90; - Moves[449].Type = "rock"; - Moves[449].Status = false; - Moves[449].DamageType = DamageType.Physical; - Moves[450].Name = "cross poison"; - Moves[450].Power = 70; - Moves[450].Accuracy = 100; - Moves[450].Type = "poison"; - Moves[450].Status = false; - Moves[450].DamageType = DamageType.Physical; - Moves[451].Name = "gunk shot"; - Moves[451].Power = 120; - Moves[451].Accuracy = 80; - Moves[451].Type = "poison"; - Moves[451].Status = false; - Moves[451].DamageType = DamageType.Physical; - Moves[452].Name = "iron head"; - Moves[452].Power = 80; - Moves[452].Accuracy = 100; - Moves[452].Type = "steel"; - Moves[452].Status = false; - Moves[452].DamageType = DamageType.Physical; - Moves[453].Name = "magnet bomb"; - Moves[453].Power = 60; - Moves[453].Accuracy = -1; - Moves[453].Type = "steel"; - Moves[453].Status = false; - Moves[453].DamageType = DamageType.Physical; - Moves[454].Name = "stone edge"; - Moves[454].Power = 100; - Moves[454].Accuracy = 80; - Moves[454].Type = "rock"; - Moves[454].Status = false; - Moves[454].DamageType = DamageType.Physical; - Moves[455].Name = "captivate"; - Moves[455].Power = -1; - Moves[455].Accuracy = 100; - Moves[455].Type = "normal"; - Moves[455].Status = true; - Moves[455].DamageType = DamageType.Physical; - Moves[456].Name = "stealth rock"; - Moves[456].Power = -1; - Moves[456].Accuracy = -1; - Moves[456].Type = "rock"; - Moves[456].Status = true; - Moves[456].DamageType = DamageType.Physical; - Moves[457].Name = "grass knot"; - Moves[457].Power = -1; - Moves[457].Accuracy = 100; - Moves[457].Type = "grass"; - Moves[457].Status = false; - Moves[457].DamageType = DamageType.Special; - Moves[458].Name = "chatter"; - Moves[458].Power = 65; - Moves[458].Accuracy = 100; - Moves[458].Type = "flying"; - Moves[458].Status = false; - Moves[458].DamageType = DamageType.Special; - Moves[459].Name = "judgment"; - Moves[459].Power = 100; - Moves[459].Accuracy = 100; - Moves[459].Type = "normal"; - Moves[459].Status = false; - Moves[459].DamageType = DamageType.Special; - Moves[460].Name = "bug bite"; - Moves[460].Power = 60; - Moves[460].Accuracy = 100; - Moves[460].Type = "bug"; - Moves[460].Status = false; - Moves[460].DamageType = DamageType.Physical; - Moves[461].Name = "charge beam"; - Moves[461].Power = 50; - Moves[461].Accuracy = 90; - Moves[461].Type = "electric"; - Moves[461].Status = false; - Moves[461].DamageType = DamageType.Special; - Moves[462].Name = "wood hammer"; - Moves[462].Power = 120; - Moves[462].Accuracy = 100; - Moves[462].Type = "grass"; - Moves[462].Status = false; - Moves[462].DamageType = DamageType.Physical; - Moves[463].Name = "aqua jet"; - Moves[463].Power = 40; - Moves[463].Accuracy = 100; - Moves[463].Type = "water"; - Moves[463].Status = false; - Moves[463].DamageType = DamageType.Physical; - Moves[464].Name = "attack order"; - Moves[464].Power = 90; - Moves[464].Accuracy = 100; - Moves[464].Type = "bug"; - Moves[464].Status = false; - Moves[464].DamageType = DamageType.Physical; - Moves[465].Name = "defend order"; - Moves[465].Power = -1; - Moves[465].Accuracy = -1; - Moves[465].Type = "bug"; - Moves[465].Status = true; - Moves[465].DamageType = DamageType.Physical; - Moves[466].Name = "heal order"; - Moves[466].Power = -1; - Moves[466].Accuracy = -1; - Moves[466].Type = "bug"; - Moves[466].Status = true; - Moves[466].DamageType = DamageType.Physical; - Moves[467].Name = "head smash"; - Moves[467].Power = 150; - Moves[467].Accuracy = 80; - Moves[467].Type = "rock"; - Moves[467].Status = false; - Moves[467].DamageType = DamageType.Physical; - Moves[468].Name = "double hit"; - Moves[468].Power = 35; - Moves[468].Accuracy = 90; - Moves[468].Type = "normal"; - Moves[468].Status = false; - Moves[468].DamageType = DamageType.Physical; - Moves[469].Name = "roar of time"; - Moves[469].Power = 150; - Moves[469].Accuracy = 90; - Moves[469].Type = "dragon"; - Moves[469].Status = false; - Moves[469].DamageType = DamageType.Special; - Moves[470].Name = "spacial rend"; - Moves[470].Power = 100; - Moves[470].Accuracy = 95; - Moves[470].Type = "dragon"; - Moves[470].Status = false; - Moves[470].DamageType = DamageType.Special; - Moves[471].Name = "lunar dance"; - Moves[471].Power = -1; - Moves[471].Accuracy = -1; - Moves[471].Type = "psychic"; - Moves[471].Status = true; - Moves[471].DamageType = DamageType.Physical; - Moves[472].Name = "crush grip"; - Moves[472].Power = -1; - Moves[472].Accuracy = 100; - Moves[472].Type = "normal"; - Moves[472].Status = false; - Moves[472].DamageType = DamageType.Physical; - Moves[473].Name = "magma storm"; - Moves[473].Power = 100; - Moves[473].Accuracy = 75; - Moves[473].Type = "fire"; - Moves[473].Status = false; - Moves[473].DamageType = DamageType.Special; - Moves[474].Name = "dark void"; - Moves[474].Power = -1; - Moves[474].Accuracy = 80; - Moves[474].Type = "dark"; - Moves[474].Status = true; - Moves[474].DamageType = DamageType.Physical; - Moves[475].Name = "seed flare"; - Moves[475].Power = 120; - Moves[475].Accuracy = 85; - Moves[475].Type = "grass"; - Moves[475].Status = false; - Moves[475].DamageType = DamageType.Special; - Moves[476].Name = "ominous wind"; - Moves[476].Power = 60; - Moves[476].Accuracy = 100; - Moves[476].Type = "ghost"; - Moves[476].Status = false; - Moves[476].DamageType = DamageType.Special; - Moves[477].Name = "shadow force"; - Moves[477].Power = 120; - Moves[477].Accuracy = 100; - Moves[477].Type = "ghost"; - Moves[477].Status = false; - Moves[477].DamageType = DamageType.Physical; - Moves[478].Name = "inferno"; - Moves[478].Power = 100; - Moves[478].Accuracy = 50; - Moves[478].Type = "fire"; - Moves[478].Status = false; - Moves[478].DamageType = DamageType.Special; - Moves[479].Name = "flame burst"; - Moves[479].Power = 70; - Moves[479].Accuracy = 100; - Moves[479].Type = "fire"; - Moves[479].Status = false; - Moves[479].DamageType = DamageType.Special; - Moves[480].Name = "rage powder"; - Moves[480].Power = -1; - Moves[480].Accuracy = -1; - Moves[480].Type = "bug"; - Moves[480].Status = true; - Moves[480].DamageType = DamageType.Physical; - Moves[481].Name = "quiver dance"; - Moves[481].Power = -1; - Moves[481].Accuracy = -1; - Moves[481].Type = "bug"; - Moves[481].Status = true; - Moves[481].DamageType = DamageType.Physical; - Moves[482].Name = "hurricane"; - Moves[482].Power = 110; - Moves[482].Accuracy = 70; - Moves[482].Type = "flying"; - Moves[482].Status = false; - Moves[482].DamageType = DamageType.Special; - Moves[483].Name = "drill run"; - Moves[483].Power = 80; - Moves[483].Accuracy = 95; - Moves[483].Type = "ground"; - Moves[483].Status = false; - Moves[483].DamageType = DamageType.Physical; - Moves[484].Name = "acid spray"; - Moves[484].Power = 40; - Moves[484].Accuracy = 100; - Moves[484].Type = "poison"; - Moves[484].Status = false; - Moves[484].DamageType = DamageType.Special; - Moves[485].Name = "coil"; - Moves[485].Power = -1; - Moves[485].Accuracy = -1; - Moves[485].Type = "poison"; - Moves[485].Status = true; - Moves[485].DamageType = DamageType.Physical; - Moves[486].Name = "flame charge"; - Moves[486].Power = 50; - Moves[486].Accuracy = 100; - Moves[486].Type = "fire"; - Moves[486].Status = false; - Moves[486].DamageType = DamageType.Physical; - Moves[487].Name = "chip away"; - Moves[487].Power = 70; - Moves[487].Accuracy = 100; - Moves[487].Type = "normal"; - Moves[487].Status = false; - Moves[487].DamageType = DamageType.Physical; - Moves[488].Name = "echoed voice"; - Moves[488].Power = 40; - Moves[488].Accuracy = 100; - Moves[488].Type = "normal"; - Moves[488].Status = false; - Moves[488].DamageType = DamageType.Special; - Moves[489].Name = "synchronoise"; - Moves[489].Power = 120; - Moves[489].Accuracy = 100; - Moves[489].Type = "psychic"; - Moves[489].Status = false; - Moves[489].DamageType = DamageType.Special; - Moves[490].Name = "acrobatics"; - Moves[490].Power = 55; - Moves[490].Accuracy = 100; - Moves[490].Type = "flying"; - Moves[490].Status = false; - Moves[490].DamageType = DamageType.Physical; - Moves[491].Name = "electro ball"; - Moves[491].Power = -1; - Moves[491].Accuracy = 100; - Moves[491].Type = "electric"; - Moves[491].Status = false; - Moves[491].DamageType = DamageType.Special; - Moves[492].Name = "bestow"; - Moves[492].Power = 1; - Moves[492].Accuracy = -1; - Moves[492].Type = "normal"; - Moves[492].Status = true; - Moves[492].DamageType = DamageType.Physical; - Moves[493].Name = "stored power"; - Moves[493].Power = 20; - Moves[493].Accuracy = 100; - Moves[493].Type = "psychic"; - Moves[493].Status = false; - Moves[493].DamageType = DamageType.Special; - Moves[494].Name = "after you"; - Moves[494].Power = 1; - Moves[494].Accuracy = -1; - Moves[494].Type = "normal"; - Moves[494].Status = true; - Moves[494].DamageType = DamageType.Physical; - Moves[495].Name = "round"; - Moves[495].Power = 60; - Moves[495].Accuracy = 100; - Moves[495].Type = "normal"; - Moves[495].Status = false; - Moves[495].DamageType = DamageType.Special; - Moves[496].Name = "dev insta kill"; - Moves[496].Power = 999; - Moves[496].Accuracy = 100; - Moves[496].Type = "dragon"; - Moves[496].Status = false; - Moves[496].DamageType = DamageType.Physical; - Moves[497].Name = "soul crush"; - Moves[497].Power = 794; - Moves[497].Accuracy = 100; - Moves[497].Type = "ghost"; - Moves[497].Status = false; - Moves[497].DamageType = DamageType.Physical; - Moves[498].Name = "heavy slam"; - Moves[498].Power = -1; - Moves[498].Accuracy = 100; - Moves[498].Type = "steel"; - Moves[498].Status = false; - Moves[498].DamageType = DamageType.Physical; - Moves[499].Name = "work up"; - Moves[499].Power = -1; - Moves[499].Accuracy = -1; - Moves[499].Type = "normal"; - Moves[499].Status = true; - Moves[499].DamageType = DamageType.Physical; - Moves[500].Name = "psyshock"; - Moves[500].Power = 80; - Moves[500].Accuracy = 100; - Moves[500].Type = "psychic"; - Moves[500].Status = false; - Moves[500].DamageType = DamageType.Special; - Moves[501].Name = "wide guard"; - Moves[501].Power = -1; - Moves[501].Accuracy = -1; - Moves[501].Type = "rock"; - Moves[501].Status = true; - Moves[501].DamageType = DamageType.Physical; - Moves[502].Name = "quick guard"; - Moves[502].Power = -1; - Moves[502].Accuracy = -1; - Moves[502].Type = "fighting"; - Moves[502].Status = true; - Moves[502].DamageType = DamageType.Physical; - Moves[503].Name = "retaliate"; - Moves[503].Power = 70; - Moves[503].Accuracy = 100; - Moves[503].Type = "normal"; - Moves[503].Status = false; - Moves[503].DamageType = DamageType.Physical; - Moves[504].Name = "sludge wave"; - Moves[504].Power = 95; - Moves[504].Accuracy = 100; - Moves[504].Type = "poison"; - Moves[504].Status = false; - Moves[504].DamageType = DamageType.Special; - Moves[505].Name = "soak"; - Moves[505].Power = -1; - Moves[505].Accuracy = 100; - Moves[505].Type = "water"; - Moves[505].Status = true; - Moves[505].DamageType = DamageType.Physical; - Moves[506].Name = "bulldoze"; - Moves[506].Power = 60; - Moves[506].Accuracy = 100; - Moves[506].Type = "ground"; - Moves[506].Status = false; - Moves[506].DamageType = DamageType.Physical; - Moves[507].Name = "wonder room"; - Moves[507].Power = -1; - Moves[507].Accuracy = -1; - Moves[507].Type = "psychic"; - Moves[507].Status = true; - Moves[507].DamageType = DamageType.Physical; - Moves[508].Name = "final gambit"; - Moves[508].Power = -1; - Moves[508].Accuracy = 100; - Moves[508].Type = "fighting"; - Moves[508].Status = true; - Moves[508].DamageType = DamageType.Special; - Moves[509].Name = "smack down"; - Moves[509].Power = 50; - Moves[509].Accuracy = 100; - Moves[509].Type = "rock"; - Moves[509].Status = false; - Moves[509].DamageType = DamageType.Physical; - Moves[510].Name = "steamroller"; - Moves[510].Power = 65; - Moves[510].Accuracy = 100; - Moves[510].Type = "bug"; - Moves[510].Status = false; - Moves[510].DamageType = DamageType.Physical; - Moves[511].Name = "autotomize"; - Moves[511].Power = -1; - Moves[511].Accuracy = -1; - Moves[511].Type = "steel"; - Moves[511].Status = true; - Moves[511].DamageType = DamageType.Physical; - Moves[512].Name = "absorb"; - Moves[512].Power = 20; - Moves[512].Accuracy = 100; - Moves[512].Type = "grass"; - Moves[512].Status = false; - Moves[512].DamageType = DamageType.Special; - Moves[513].Name = "petal blizzard"; - Moves[513].Power = 90; - Moves[513].Accuracy = 100; - Moves[513].Type = "grass"; - Moves[513].Status = false; - Moves[513].DamageType = DamageType.Physical; - Moves[514].Name = "fell stinger"; - Moves[514].Power = 30; - Moves[514].Accuracy = 100; - Moves[514].Type = "bug"; - Moves[514].Status = false; - Moves[514].DamageType = DamageType.Physical; - Moves[515].Name = "belch"; - Moves[515].Power = 120; - Moves[515].Accuracy = 90; - Moves[515].Type = "poison"; - Moves[515].Status = false; - Moves[515].DamageType = DamageType.Special; - Moves[516].Name = "play nice"; - Moves[516].Power = -1; - Moves[516].Accuracy = -1; - Moves[516].Type = "normal"; - Moves[516].Status = true; - Moves[516].DamageType = DamageType.Special; - Moves[517].Name = "wild charge"; - Moves[517].Power = 90; - Moves[517].Accuracy = 100; - Moves[517].Type = "electric"; - Moves[517].Status = false; - Moves[517].DamageType = DamageType.Physical; - Moves[518].Name = "moonblast"; - Moves[518].Power = 95; - Moves[518].Accuracy = 100; - Moves[518].Type = "fairy"; - Moves[518].Status = false; - Moves[518].DamageType = DamageType.Special; - Moves[519].Name = "venoshock"; - Moves[519].Power = 65; - Moves[519].Accuracy = 100; - Moves[519].Type = "poison"; - Moves[519].Status = false; - Moves[519].DamageType = DamageType.Special; - Moves[520].Name = "play rough"; - Moves[520].Power = 90; - Moves[520].Accuracy = 90; - Moves[520].Type = "fairy"; - Moves[520].Status = false; - Moves[520].DamageType = DamageType.Physical; - Moves[521].Name = "nuzzle"; - Moves[521].Power = 20; - Moves[521].Accuracy = 20; - Moves[521].Type = "electric"; - Moves[521].Status = false; - Moves[521].DamageType = DamageType.Physical; - Moves[522].Name = "baby-doll eyes"; - Moves[522].Power = -1; - Moves[522].Accuracy = 100; - Moves[522].Type = "fairy"; - Moves[522].Status = true; - Moves[522].DamageType = DamageType.Special; - Moves[523].Name = "disarming voice"; - Moves[523].Power = 40; - Moves[523].Accuracy = 100; - Moves[523].Type = "fairy"; - Moves[523].Status = false; - Moves[523].DamageType = DamageType.Special; - Moves[524].Name = "hex"; - Moves[524].Power = 65; - Moves[524].Accuracy = 100; - Moves[524].Type = "ghost"; - Moves[524].Status = false; - Moves[524].DamageType = DamageType.Special; - Moves[525].Name = "shell smash"; - Moves[525].Power = -1; - Moves[525].Accuracy = 100; - Moves[525].Type = "normal"; - Moves[525].Status = true; - Moves[525].DamageType = DamageType.Special; - Moves[526].Name = "razor shell"; - Moves[526].Power = 75; - Moves[526].Accuracy = 95; - Moves[526].Type = "water"; - Moves[526].Status = false; - Moves[526].DamageType = DamageType.Physical; - Moves[527].Name = "eerie impulse"; - Moves[527].Power = -1; - Moves[527].Accuracy = 100; - Moves[527].Type = "electric"; - Moves[527].Status = true; - Moves[527].DamageType = DamageType.Special; - Moves[528].Name = "magic room"; - Moves[528].Power = -1; - Moves[528].Accuracy = 100; - Moves[528].Type = "psychic"; - Moves[528].Status = true; - Moves[528].DamageType = DamageType.Special; - Moves[529].Name = "dragon tail"; - Moves[529].Power = 60; - Moves[529].Accuracy = 90; - Moves[529].Type = "dragon"; - Moves[529].Status = false; - Moves[529].DamageType = DamageType.Physical; - Moves[530].Name = "circle throw"; - Moves[530].Power = 60; - Moves[530].Accuracy = 90; - Moves[530].Type = "fighting"; - Moves[530].Status = false; - Moves[530].DamageType = DamageType.Physical; - Moves[531].Name = "volt switch"; - Moves[531].Power = 70; - Moves[531].Accuracy = 100; - Moves[531].Type = "electric"; - Moves[531].Status = false; - Moves[531].DamageType = DamageType.Special; - Moves[532].Name = "sticky web"; - Moves[532].Power = -1; - Moves[532].Accuracy = 100; - Moves[532].Type = "bug"; - Moves[532].Status = true; - Moves[532].DamageType = DamageType.Special; - Moves[533].Name = "horn leech"; - Moves[533].Power = 75; - Moves[533].Accuracy = 100; - Moves[533].Type = "grass"; - Moves[533].Status = false; - Moves[533].DamageType = DamageType.Physical; - Moves[534].Name = "scald"; - Moves[534].Power = 80; - Moves[534].Accuracy = 100; - Moves[534].Type = "water"; - Moves[534].Status = false; - Moves[534].DamageType = DamageType.Special; - Moves[535].Name = "dazzling gleam"; - Moves[535].Power = 80; - Moves[535].Accuracy = 100; - Moves[535].Type = "fairy"; - Moves[535].Status = false; - Moves[535].DamageType = DamageType.Special; - Moves[536].Name = "foul play"; - Moves[536].Power = 95; - Moves[536].Accuracy = 100; - Moves[536].Type = "dark"; - Moves[536].DamageType = DamageType.Physical; - Moves[536].Status = false; - Moves[537].Name = "accelerock"; - Moves[537].Power = 40; - Moves[537].Accuracy = 100; - Moves[537].Type = "rock"; - Moves[537].DamageType = DamageType.Physical; - Moves[537].Status = false; - Moves[538].Name = "anchor shot"; - Moves[538].Power = 80; - Moves[538].Accuracy = 100; - Moves[538].Type = "steel"; - Moves[538].DamageType = DamageType.Physical; - Moves[538].Status = false; - Moves[539].Name = "aurora veil"; - Moves[539].Power = 0; - Moves[539].Accuracy = 100; - Moves[539].Type = "ice"; - Moves[539].DamageType = DamageType.Physical; - Moves[539].Status = false; - Moves[540].Name = "beak blast"; - Moves[540].Power = 100; - Moves[540].Accuracy = 100; - Moves[540].Type = "flying"; - Moves[540].DamageType = DamageType.Physical; - Moves[540].Status = false; - Moves[541].Name = "blue flare"; - Moves[541].Power = 130; - Moves[541].Accuracy = 85; - Moves[541].Type = "fire"; - Moves[541].DamageType = DamageType.Special; - Moves[541].Status = false; - Moves[542].Name = "bolt strike"; - Moves[542].Power = 130; - Moves[542].Accuracy = 85; - Moves[542].Type = "electric"; - Moves[542].DamageType = DamageType.Physical; - Moves[542].Status = false; - Moves[543].Name = "boomburst"; - Moves[543].Power = 140; - Moves[543].Accuracy = 100; - Moves[543].Type = "normal"; - Moves[543].DamageType = DamageType.Special; - Moves[543].Status = false; - Moves[544].Name = "brutal swing"; - Moves[544].Power = 60; - Moves[544].Accuracy = 100; - Moves[544].Type = "dark"; - Moves[544].DamageType = DamageType.Physical; - Moves[544].Status = false; - Moves[545].Name = "burn up"; - Moves[545].Power = 130; - Moves[545].Accuracy = 100; - Moves[545].Type = "fire"; - Moves[545].DamageType = DamageType.Special; - Moves[545].Status = false; - Moves[546].Name = "clanging scales"; - Moves[546].Power = 110; - Moves[546].Accuracy = 100; - Moves[546].Type = "dragon"; - Moves[546].DamageType = DamageType.Special; - Moves[546].Status = false; - Moves[547].Name = "clear smog"; - Moves[547].Power = 50; - Moves[547].Accuracy = 100; - Moves[547].Type = "poison"; - Moves[547].DamageType = DamageType.Special; - Moves[547].Status = false; - Moves[548].Name = "core enforcer"; - Moves[548].Power = 100; - Moves[548].Accuracy = 100; - Moves[548].Type = "dragon"; - Moves[548].DamageType = DamageType.Special; - Moves[548].Status = false; - Moves[549].Name = "crafty shield"; - Moves[549].Power = 0; - Moves[549].Accuracy = 100; - Moves[549].Type = "fairy"; - Moves[549].DamageType = DamageType.Physical; - Moves[549].Status = false; - Moves[550].Name = "darkest lariat"; - Moves[550].Power = 85; - Moves[550].Accuracy = 100; - Moves[550].Type = "dark"; - Moves[550].DamageType = DamageType.Physical; - Moves[550].Status = false; - Moves[551].Name = "dragon ascent"; - Moves[551].Power = 120; - Moves[551].Accuracy = 100; - Moves[551].Type = "flying"; - Moves[551].DamageType = DamageType.Physical; - Moves[551].Status = false; - Moves[552].Name = "dragon hammer"; - Moves[552].Power = 90; - Moves[552].Accuracy = 100; - Moves[552].Type = "dragon"; - Moves[552].DamageType = DamageType.Physical; - Moves[552].Status = false; - Moves[553].Name = "draining kiss"; - Moves[553].Power = 50; - Moves[553].Accuracy = 100; - Moves[553].Type = "fairy"; - Moves[553].DamageType = DamageType.Special; - Moves[553].Status = false; - Moves[554].Name = "dual chop"; - Moves[554].Power = 40; - Moves[554].Accuracy = 90; - Moves[554].Type = "dragon"; - Moves[554].DamageType = DamageType.Physical; - Moves[554].Status = false; - Moves[555].Name = "electric terrain"; - Moves[555].Power = 0; - Moves[555].Accuracy = 100; - Moves[555].Type = "electric"; - Moves[555].DamageType = DamageType.Physical; - Moves[555].Status = true; - Moves[556].Name = "electrify"; - Moves[556].Power = 0; - Moves[556].Accuracy = 100; - Moves[556].Type = "electric"; - Moves[556].DamageType = DamageType.Physical; - Moves[556].Status = false; - Moves[557].Name = "entrainment"; - Moves[557].Power = 0; - Moves[557].Accuracy = 100; - Moves[557].Type = "normal"; - Moves[557].DamageType = DamageType.Physical; - Moves[557].Status = false; - Moves[558].Name = "fairy lock"; - Moves[558].Power = 0; - Moves[558].Accuracy = 100; - Moves[558].Type = "fairy"; - Moves[558].DamageType = DamageType.Physical; - Moves[558].Status = true; - Moves[559].Name = "fairy wind"; - Moves[559].Power = 40; - Moves[559].Accuracy = 100; - Moves[559].Type = "fairy"; - Moves[559].DamageType = DamageType.Special; - Moves[559].Status = false; - Moves[560].Name = "fire lash"; - Moves[560].Power = 80; - Moves[560].Accuracy = 100; - Moves[560].Type = "fire"; - Moves[560].DamageType = DamageType.Physical; - Moves[560].Status = false; - Moves[561].Name = "fire pledge"; - Moves[561].Power = 80; - Moves[561].Accuracy = 100; - Moves[561].Type = "fire"; - Moves[561].DamageType = DamageType.Special; - Moves[561].Status = false; - Moves[562].Name = "first impression"; - Moves[562].Power = 90; - Moves[562].Accuracy = 100; - Moves[562].Type = "bug"; - Moves[562].DamageType = DamageType.Physical; - Moves[562].Status = false; - Moves[563].Name = "fleur cannon"; - Moves[563].Power = 130; - Moves[563].Accuracy = 90; - Moves[563].Type = "fairy"; - Moves[563].DamageType = DamageType.Special; - Moves[563].Status = false; - Moves[564].Name = "floral healing"; - Moves[564].Power = 0; - Moves[564].Accuracy = 100; - Moves[564].Type = "fairy"; - Moves[564].DamageType = DamageType.Physical; - Moves[564].Status = true; - Moves[565].Name = "flying press"; - Moves[565].Power = 100; - Moves[565].Accuracy = 95; - Moves[565].Type = "fighting"; - Moves[565].DamageType = DamageType.Physical; - Moves[565].Status = false; - Moves[566].Name = "forest's curse"; - Moves[566].Power = 0; - Moves[566].Accuracy = 100; - Moves[566].Type = "grass"; - Moves[566].DamageType = DamageType.Physical; - Moves[566].Status = false; - Moves[567].Name = "freeze shock"; - Moves[567].Power = 140; - Moves[567].Accuracy = 90; - Moves[567].Type = "ice"; - Moves[567].DamageType = DamageType.Physical; - Moves[567].Status = false; - Moves[568].Name = "freeze-dry"; - Moves[568].Power = 70; - Moves[568].Accuracy = 100; - Moves[568].Type = "ice"; - Moves[568].DamageType = DamageType.Special; - Moves[568].Status = false; - Moves[569].Name = "frost breath"; - Moves[569].Power = 60; - Moves[569].Accuracy = 90; - Moves[569].Type = "ice"; - Moves[569].DamageType = DamageType.Special; - Moves[569].Status = false; - Moves[570].Name = "fusion bolt"; - Moves[570].Power = 100; - Moves[570].Accuracy = 100; - Moves[570].Type = "electric"; - Moves[570].DamageType = DamageType.Physical; - Moves[570].Status = false; - Moves[571].Name = "fusion flare"; - Moves[571].Power = 100; - Moves[571].Accuracy = 100; - Moves[571].Type = "fire"; - Moves[571].DamageType = DamageType.Special; - Moves[571].Status = false; - Moves[572].Name = "gear grind"; - Moves[572].Power = 50; - Moves[572].Accuracy = 85; - Moves[572].Type = "steel"; - Moves[572].DamageType = DamageType.Physical; - Moves[572].Status = false; - Moves[573].Name = "gear up"; - Moves[573].Power = 0; - Moves[573].Accuracy = 100; - Moves[573].Type = "steel"; - Moves[573].DamageType = DamageType.Physical; - Moves[573].Status = true; - Moves[574].Name = "grass pledge"; - Moves[574].Power = 80; - Moves[574].Accuracy = 100; - Moves[574].Type = "grass"; - Moves[574].DamageType = DamageType.Special; - Moves[574].Status = false; - Moves[575].Name = "grassy terrain"; - Moves[575].Power = 0; - Moves[575].Accuracy = 100; - Moves[575].Type = "grass"; - Moves[575].DamageType = DamageType.Physical; - Moves[575].Status = true; - Moves[576].Name = "guard split"; - Moves[576].Power = 0; - Moves[576].Accuracy = 100; - Moves[576].Type = "psychic"; - Moves[576].DamageType = DamageType.Physical; - Moves[576].Status = false; - Moves[577].Name = "happy hour"; - Moves[577].Power = 0; - Moves[577].Accuracy = 100; - Moves[577].Type = "normal"; - Moves[577].DamageType = DamageType.Physical; - Moves[577].Status = false; - Moves[578].Name = "head charge"; - Moves[578].Power = 120; - Moves[578].Accuracy = 100; - Moves[578].Type = "normal"; - Moves[578].DamageType = DamageType.Physical; - Moves[578].Status = false; - Moves[579].Name = "heal pulse"; - Moves[579].Power = 0; - Moves[579].Accuracy = 100; - Moves[579].Type = "psychic"; - Moves[579].DamageType = DamageType.Physical; - Moves[579].Status = true; - Moves[580].Name = "heart stamp"; - Moves[580].Power = 60; - Moves[580].Accuracy = 100; - Moves[580].Type = "psychic"; - Moves[580].DamageType = DamageType.Physical; - Moves[580].Status = false; - Moves[581].Name = "heat crash"; - Moves[581].Power = 0; - Moves[581].Accuracy = 100; - Moves[581].Type = "fire"; - Moves[581].DamageType = DamageType.Physical; - Moves[581].Status = false; - Moves[582].Name = "high horsepower"; - Moves[582].Power = 95; - Moves[582].Accuracy = 95; - Moves[582].Type = "ground"; - Moves[582].DamageType = DamageType.Physical; - Moves[582].Status = false; - Moves[583].Name = "hold back"; - Moves[583].Power = 40; - Moves[583].Accuracy = 100; - Moves[583].Type = "normal"; - Moves[583].DamageType = DamageType.Physical; - Moves[583].Status = false; - Moves[584].Name = "hold hands"; - Moves[584].Power = 0; - Moves[584].Accuracy = 100; - Moves[584].Type = "normal"; - Moves[584].DamageType = DamageType.Physical; - Moves[584].Status = false; - Moves[585].Name = "hyperspace fury"; - Moves[585].Power = 100; - Moves[585].Accuracy = 100; - Moves[585].Type = "dark"; - Moves[585].DamageType = DamageType.Physical; - Moves[585].Status = false; - Moves[586].Name = "hyperspace hole"; - Moves[586].Power = 80; - Moves[586].Accuracy = 100; - Moves[586].Type = "psychic"; - Moves[586].DamageType = DamageType.Special; - Moves[586].Status = false; - Moves[587].Name = "ice burn"; - Moves[587].Power = 140; - Moves[587].Accuracy = 90; - Moves[587].Type = "ice"; - Moves[587].DamageType = DamageType.Special; - Moves[587].Status = false; - Moves[588].Name = "ice hammer"; - Moves[588].Power = 100; - Moves[588].Accuracy = 90; - Moves[588].Type = "ice"; - Moves[588].DamageType = DamageType.Physical; - Moves[588].Status = false; - Moves[589].Name = "icicle crash"; - Moves[589].Power = 85; - Moves[589].Accuracy = 90; - Moves[589].Type = "ice"; - Moves[589].DamageType = DamageType.Physical; - Moves[589].Status = false; - Moves[590].Name = "incinerate"; - Moves[590].Power = 60; - Moves[590].Accuracy = 100; - Moves[590].Type = "fire"; - Moves[590].DamageType = DamageType.Special; - Moves[590].Status = false; - Moves[591].Name = "infestation"; - Moves[591].Power = 20; - Moves[591].Accuracy = 100; - Moves[591].Type = "bug"; - Moves[591].DamageType = DamageType.Special; - Moves[591].Status = false; - Moves[592].Name = "instruct"; - Moves[592].Power = 0; - Moves[592].Accuracy = 100; - Moves[592].Type = "psychic"; - Moves[592].DamageType = DamageType.Physical; - Moves[592].Status = false; - Moves[593].Name = "ion deluge"; - Moves[593].Power = 0; - Moves[593].Accuracy = 100; - Moves[593].Type = "electric"; - Moves[593].DamageType = DamageType.Physical; - Moves[593].Status = true; - Moves[594].Name = "land's wrath"; - Moves[594].Power = 90; - Moves[594].Accuracy = 100; - Moves[594].Type = "ground"; - Moves[594].DamageType = DamageType.Physical; - Moves[594].Status = false; - Moves[595].Name = "leaf tornado"; - Moves[595].Power = 65; - Moves[595].Accuracy = 90; - Moves[595].Type = "grass"; - Moves[595].DamageType = DamageType.Special; - Moves[595].Status = false; - Moves[596].Name = "leafage"; - Moves[596].Power = 40; - Moves[596].Accuracy = 100; - Moves[596].Type = "grass"; - Moves[596].DamageType = DamageType.Physical; - Moves[596].Status = false; - Moves[597].Name = "light of ruin"; - Moves[597].Power = 140; - Moves[597].Accuracy = 90; - Moves[597].Type = "fairy"; - Moves[597].DamageType = DamageType.Special; - Moves[597].Status = false; - Moves[598].Name = "liquidation"; - Moves[598].Power = 85; - Moves[598].Accuracy = 100; - Moves[598].Type = "water"; - Moves[598].DamageType = DamageType.Physical; - Moves[598].Status = false; - Moves[599].Name = "lunge"; - Moves[599].Power = 80; - Moves[599].Accuracy = 100; - Moves[599].Type = "bug"; - Moves[599].DamageType = DamageType.Physical; - Moves[599].Status = false; - Moves[600].Name = "mat block"; - Moves[600].Power = 0; - Moves[600].Accuracy = 100; - Moves[600].Type = "fighting"; - Moves[600].DamageType = DamageType.Physical; - Moves[600].Status = false; - Moves[601].Name = "mind blown"; - Moves[601].Power = 150; - Moves[601].Accuracy = 100; - Moves[601].Type = "fire"; - Moves[601].DamageType = DamageType.Special; - Moves[601].Status = false; - Moves[602].Name = "misty terrain"; - Moves[602].Power = 0; - Moves[602].Accuracy = 100; - Moves[602].Type = "fairy"; - Moves[602].DamageType = DamageType.Physical; - Moves[602].Status = true; - Moves[603].Name = "moongeist beam"; - Moves[603].Power = 100; - Moves[603].Accuracy = 100; - Moves[603].Type = "ghost"; - Moves[603].DamageType = DamageType.Special; - Moves[603].Status = false; - Moves[604].Name = "multi-attack"; - Moves[604].Power = 90; - Moves[604].Accuracy = 100; - Moves[604].Type = "normal"; - Moves[604].DamageType = DamageType.Physical; - Moves[604].Status = false; - Moves[605].Name = "nature's madness"; - Moves[605].Power = 0; - Moves[605].Accuracy = 90; - Moves[605].Type = "fairy"; - Moves[605].DamageType = DamageType.Special; - Moves[605].Status = false; - Moves[606].Name = "night daze"; - Moves[606].Power = 85; - Moves[606].Accuracy = 95; - Moves[606].Type = "dark"; - Moves[606].DamageType = DamageType.Special; - Moves[606].Status = false; - Moves[607].Name = "oblivion wing"; - Moves[607].Power = 80; - Moves[607].Accuracy = 100; - Moves[607].Type = "flying"; - Moves[607].DamageType = DamageType.Special; - Moves[607].Status = false; - Moves[608].Name = "origin pulse"; - Moves[608].Power = 110; - Moves[608].Accuracy = 85; - Moves[608].Type = "water"; - Moves[608].DamageType = DamageType.Special; - Moves[608].Status = false; - Moves[609].Name = "parabolic charge"; - Moves[609].Power = 65; - Moves[609].Accuracy = 100; - Moves[609].Type = "electric"; - Moves[609].DamageType = DamageType.Special; - Moves[609].Status = false; - Moves[610].Name = "phantom force"; - Moves[610].Power = 90; - Moves[610].Accuracy = 100; - Moves[610].Type = "ghost"; - Moves[610].DamageType = DamageType.Physical; - Moves[610].Status = false; - Moves[611].Name = "photon geyser"; - Moves[611].Power = 100; - Moves[611].Accuracy = 100; - Moves[611].Type = "psychic"; - Moves[611].DamageType = DamageType.Special; - Moves[611].Status = false; - Moves[612].Name = "plasma fists"; - Moves[612].Power = 100; - Moves[612].Accuracy = 100; - Moves[612].Type = "electric"; - Moves[612].DamageType = DamageType.Physical; - Moves[612].Status = false; - Moves[613].Name = "pollen puff"; - Moves[613].Power = 90; - Moves[613].Accuracy = 100; - Moves[613].Type = "bug"; - Moves[613].DamageType = DamageType.Special; - Moves[613].Status = false; - Moves[614].Name = "powder"; - Moves[614].Power = 0; - Moves[614].Accuracy = 100; - Moves[614].Type = "bug"; - Moves[614].DamageType = DamageType.Physical; - Moves[614].Status = false; - Moves[615].Name = "power split"; - Moves[615].Power = 0; - Moves[615].Accuracy = 100; - Moves[615].Type = "psychic"; - Moves[615].DamageType = DamageType.Physical; - Moves[615].Status = false; - Moves[616].Name = "power trip"; - Moves[616].Power = 20; - Moves[616].Accuracy = 100; - Moves[616].Type = "dark"; - Moves[616].DamageType = DamageType.Physical; - Moves[616].Status = false; - Moves[617].Name = "precipice blades"; - Moves[617].Power = 120; - Moves[617].Accuracy = 85; - Moves[617].Type = "ground"; - Moves[617].DamageType = DamageType.Physical; - Moves[617].Status = false; - Moves[618].Name = "prismatic laser"; - Moves[618].Power = 160; - Moves[618].Accuracy = 100; - Moves[618].Type = "psychic"; - Moves[618].DamageType = DamageType.Special; - Moves[618].Status = false; - Moves[619].Name = "psychic fangs"; - Moves[619].Power = 85; - Moves[619].Accuracy = 100; - Moves[619].Type = "psychic"; - Moves[619].DamageType = DamageType.Physical; - Moves[619].Status = false; - Moves[620].Name = "psychic terrain"; - Moves[620].Power = 0; - Moves[620].Accuracy = 100; - Moves[620].Type = "psychic"; - Moves[620].DamageType = DamageType.Physical; - Moves[620].Status = true; - Moves[621].Name = "psystrike"; - Moves[621].Power = 100; - Moves[621].Accuracy = 100; - Moves[621].Type = "psychic"; - Moves[621].DamageType = DamageType.Special; - Moves[621].Status = false; - Moves[622].Name = "purify"; - Moves[622].Power = 0; - Moves[622].Accuracy = 100; - Moves[622].Type = "poison"; - Moves[622].DamageType = DamageType.Physical; - Moves[622].Status = false; - Moves[623].Name = "quash"; - Moves[623].Power = 0; - Moves[623].Accuracy = 100; - Moves[623].Type = "dark"; - Moves[623].DamageType = DamageType.Physical; - Moves[623].Status = false; - Moves[624].Name = "reflect type"; - Moves[624].Power = 0; - Moves[624].Accuracy = 100; - Moves[624].Type = "normal"; - Moves[624].DamageType = DamageType.Physical; - Moves[624].Status = false; - Moves[625].Name = "relic song"; - Moves[625].Power = 75; - Moves[625].Accuracy = 100; - Moves[625].Type = "normal"; - Moves[625].DamageType = DamageType.Special; - Moves[625].Status = false; - Moves[626].Name = "revelation dance"; - Moves[626].Power = 90; - Moves[626].Accuracy = 100; - Moves[626].Type = "normal"; - Moves[626].DamageType = DamageType.Special; - Moves[626].Status = false; - Moves[627].Name = "sacred sword"; - Moves[627].Power = 90; - Moves[627].Accuracy = 100; - Moves[627].Type = "fighting"; - Moves[627].DamageType = DamageType.Physical; - Moves[627].Status = false; - Moves[628].Name = "searing shot"; - Moves[628].Power = 100; - Moves[628].Accuracy = 100; - Moves[628].Type = "fire"; - Moves[628].DamageType = DamageType.Special; - Moves[628].Status = false; - Moves[629].Name = "secret sword"; - Moves[629].Power = 85; - Moves[629].Accuracy = 100; - Moves[629].Type = "fighting"; - Moves[629].DamageType = DamageType.Special; - Moves[629].Status = false; - Moves[630].Name = "shadow bone"; - Moves[630].Power = 85; - Moves[630].Accuracy = 100; - Moves[630].Type = "ghost"; - Moves[630].DamageType = DamageType.Physical; - Moves[630].Status = false; - Moves[631].Name = "shell trap"; - Moves[631].Power = 150; - Moves[631].Accuracy = 100; - Moves[631].Type = "fire"; - Moves[631].DamageType = DamageType.Special; - Moves[631].Status = false; - Moves[632].Name = "simple beam"; - Moves[632].Power = 0; - Moves[632].Accuracy = 100; - Moves[632].Type = "normal"; - Moves[632].DamageType = DamageType.Physical; - Moves[632].Status = false; - Moves[633].Name = "sky drop"; - Moves[633].Power = 60; - Moves[633].Accuracy = 100; - Moves[633].Type = "flying"; - Moves[633].DamageType = DamageType.Physical; - Moves[633].Status = false; - Moves[634].Name = "smart strike"; - Moves[634].Power = 70; - Moves[634].Accuracy = 100; - Moves[634].Type = "steel"; - Moves[634].DamageType = DamageType.Physical; - Moves[634].Status = false; - Moves[635].Name = "solar blade"; - Moves[635].Power = 125; - Moves[635].Accuracy = 100; - Moves[635].Type = "grass"; - Moves[635].DamageType = DamageType.Physical; - Moves[635].Status = false; - Moves[636].Name = "sparkling aria"; - Moves[636].Power = 90; - Moves[636].Accuracy = 100; - Moves[636].Type = "water"; - Moves[636].DamageType = DamageType.Special; - Moves[636].Status = false; - Moves[637].Name = "spectral thief"; - Moves[637].Power = 90; - Moves[637].Accuracy = 100; - Moves[637].Type = "ghost"; - Moves[637].DamageType = DamageType.Physical; - Moves[637].Status = false; - Moves[638].Name = "speed swap"; - Moves[638].Power = 0; - Moves[638].Accuracy = 100; - Moves[638].Type = "psychic"; - Moves[638].DamageType = DamageType.Physical; - Moves[638].Status = false; - Moves[639].Name = "spirit shackle"; - Moves[639].Power = 80; - Moves[639].Accuracy = 100; - Moves[639].Type = "ghost"; - Moves[639].DamageType = DamageType.Physical; - Moves[639].Status = false; - Moves[640].Name = "spotlight"; - Moves[640].Power = 0; - Moves[640].Accuracy = 100; - Moves[640].Type = "normal"; - Moves[640].DamageType = DamageType.Physical; - Moves[640].Status = false; - Moves[641].Name = "steam eruption"; - Moves[641].Power = 110; - Moves[641].Accuracy = 95; - Moves[641].Type = "water"; - Moves[641].DamageType = DamageType.Special; - Moves[641].Status = false; - Moves[642].Name = "stomping tantrum"; - Moves[642].Power = 75; - Moves[642].Accuracy = 100; - Moves[642].Type = "ground"; - Moves[642].DamageType = DamageType.Physical; - Moves[642].Status = false; - Moves[643].Name = "storm throw"; - Moves[643].Power = 60; - Moves[643].Accuracy = 100; - Moves[643].Type = "fighting"; - Moves[643].DamageType = DamageType.Physical; - Moves[643].Status = false; - Moves[644].Name = "strength sap"; - Moves[644].Power = 0; - Moves[644].Accuracy = 100; - Moves[644].Type = "grass"; - Moves[644].DamageType = DamageType.Physical; - Moves[644].Status = false; - Moves[645].Name = "sunsteel strike"; - Moves[645].Power = 100; - Moves[645].Accuracy = 100; - Moves[645].Type = "steel"; - Moves[645].DamageType = DamageType.Physical; - Moves[645].Status = false; - Moves[646].Name = "tail slap"; - Moves[646].Power = 25; - Moves[646].Accuracy = 85; - Moves[646].Type = "normal"; - Moves[646].DamageType = DamageType.Physical; - Moves[646].Status = false; - Moves[647].Name = "tearful look"; - Moves[647].Power = 0; - Moves[647].Accuracy = 100; - Moves[647].Type = "normal"; - Moves[647].DamageType = DamageType.Physical; - Moves[647].Status = true; - Moves[648].Name = "techno blast"; - Moves[648].Power = 120; - Moves[648].Accuracy = 100; - Moves[648].Type = "normal"; - Moves[648].DamageType = DamageType.Special; - Moves[648].Status = false; - Moves[649].Name = "telekinesis"; - Moves[649].Power = 0; - Moves[649].Accuracy = 100; - Moves[649].Type = "psychic"; - Moves[649].DamageType = DamageType.Physical; - Moves[649].Status = true; - Moves[650].Name = "thousand arrows"; - Moves[650].Power = 90; - Moves[650].Accuracy = 100; - Moves[650].Type = "ground"; - Moves[650].DamageType = DamageType.Physical; - Moves[650].Status = false; - Moves[651].Name = "thousand waves"; - Moves[651].Power = 90; - Moves[651].Accuracy = 100; - Moves[651].Type = "ground"; - Moves[651].DamageType = DamageType.Physical; - Moves[651].Status = false; - Moves[652].Name = "throat chop"; - Moves[652].Power = 80; - Moves[652].Accuracy = 100; - Moves[652].Type = "dark"; - Moves[652].DamageType = DamageType.Physical; - Moves[652].Status = false; - Moves[653].Name = "topsy-turvy"; - Moves[653].Power = 0; - Moves[653].Accuracy = 100; - Moves[653].Type = "dark"; - Moves[653].DamageType = DamageType.Physical; - Moves[653].Status = false; - Moves[654].Name = "toxic thread"; - Moves[654].Power = 0; - Moves[654].Accuracy = 100; - Moves[654].Type = "poison"; - Moves[654].DamageType = DamageType.Physical; - Moves[654].Status = false; - Moves[655].Name = "trick-or-treat"; - Moves[655].Power = 0; - Moves[655].Accuracy = 100; - Moves[655].Type = "ghost"; - Moves[655].DamageType = DamageType.Physical; - Moves[655].Status = false; - Moves[656].Name = "trop kick"; - Moves[656].Power = 70; - Moves[656].Accuracy = 100; - Moves[656].Type = "grass"; - Moves[656].DamageType = DamageType.Physical; - Moves[656].Status = false; - Moves[657].Name = "water pledge"; - Moves[657].Power = 80; - Moves[657].Accuracy = 100; - Moves[657].Type = "water"; - Moves[657].DamageType = DamageType.Special; - Moves[657].Status = false; - Moves[658].Name = "water shuriken"; - Moves[658].Power = 15; - Moves[658].Accuracy = 100; - Moves[658].Type = "water"; - Moves[658].DamageType = DamageType.Special; - Moves[658].Status = false; - Moves[659].Name = "zing zap"; - Moves[659].Power = 80; - Moves[659].Accuracy = 100; - Moves[659].Type = "electric"; - Moves[659].DamageType = DamageType.Physical; - Moves[659].Status = false; } private void LoadMoveNames() { - MoveNames[0] = string.Empty; - MoveNames[1] = "Absorb"; - MoveNames[2] = "Acid"; - MoveNames[3] = "Acid Armor"; - MoveNames[4] = "Aerial Ace"; - MoveNames[5] = "Aeroblast"; - MoveNames[6] = "Agility"; - MoveNames[7] = "Air Cutter"; - MoveNames[8] = "Amnesia"; - MoveNames[9] = "Ancient Power"; - MoveNames[10] = "Arm Thrust"; - MoveNames[11] = "Aromatherapy"; - MoveNames[12] = "Assist"; - MoveNames[13] = "Astonish"; - MoveNames[14] = "Attract"; - MoveNames[15] = "Aurora Beam"; - MoveNames[16] = "Barrage"; - MoveNames[17] = "Barrier"; - MoveNames[18] = "Baton Pass"; - MoveNames[19] = "Beat Up"; - MoveNames[20] = "Belly Drum"; - MoveNames[21] = "Bide"; - MoveNames[22] = "Bind"; - MoveNames[23] = "Bite"; - MoveNames[24] = "Blaze Kick"; - MoveNames[25] = "Blizzard"; - MoveNames[26] = "IGNORE"; - MoveNames[27] = "Block"; - MoveNames[28] = "Body Slam"; - MoveNames[29] = "Bone Club"; - MoveNames[30] = "Bone Rush"; - MoveNames[31] = "Bonemerang"; - MoveNames[32] = "Bounce"; - MoveNames[33] = "Brick Break"; - MoveNames[34] = "Bubble"; - MoveNames[35] = "Bubblebeam"; - MoveNames[36] = "Bulk Up"; - MoveNames[37] = "Bullet Seed"; - MoveNames[38] = "Calm Mind"; - MoveNames[39] = "Camouflage"; - MoveNames[40] = "Charge"; - MoveNames[41] = "Charm"; - MoveNames[42] = "Clamp"; - MoveNames[43] = "Comet Punch"; - MoveNames[44] = "Confuse Ray"; - MoveNames[45] = "Confusion"; - MoveNames[46] = "Constrict"; - MoveNames[47] = "IGNORE 2"; - MoveNames[48] = "Conversion"; - MoveNames[49] = "Conversion 2"; - MoveNames[50] = "Cosmic Power"; - MoveNames[51] = "Cotton Spore"; - MoveNames[52] = "Counter"; - MoveNames[53] = "Covet"; - MoveNames[54] = "Crabhammer"; - MoveNames[55] = "Cross Chop"; - MoveNames[56] = "Crunch"; - MoveNames[57] = "Crush Claw"; - MoveNames[58] = "Curse"; - MoveNames[59] = "Defense Curl"; - MoveNames[60] = "Destiny Bond"; - MoveNames[61] = "Detect"; - MoveNames[62] = "Dig"; - MoveNames[63] = "Disable"; - MoveNames[64] = "Dizzy Punch"; - MoveNames[65] = "Doom Desire"; - MoveNames[66] = "Gangsta Rap"; - MoveNames[67] = "Double Kick"; - MoveNames[68] = "IGNORE 3"; - MoveNames[69] = "Double Team"; - MoveNames[70] = "Double-Edge"; - MoveNames[71] = "Double Slap"; - MoveNames[72] = "Dragon Claw"; - MoveNames[73] = "Dragon Dance"; - MoveNames[74] = "Dragon Rage"; - MoveNames[75] = "Dragonbreath"; - MoveNames[76] = "Dream Eater"; - MoveNames[77] = "Drill Peck"; - MoveNames[78] = "IGNORE 4"; - MoveNames[79] = "Dynamic Punch"; - MoveNames[80] = "Earthquake"; - MoveNames[81] = "Egg Bomb"; - MoveNames[82] = "Ember"; - MoveNames[83] = "Encore"; - MoveNames[84] = "Endeavor"; - MoveNames[85] = "Endure"; - MoveNames[86] = "Eruption"; - MoveNames[87] = "Explosion"; - MoveNames[88] = "Extrasensory"; - MoveNames[89] = "Extreme Speed"; - MoveNames[90] = "Feint Attack"; - MoveNames[91] = "Fake Out"; - MoveNames[92] = "Fake Tears"; - MoveNames[93] = "False Swipe"; - MoveNames[94] = "Feather Dance"; - MoveNames[95] = "Fire Blast"; - MoveNames[96] = "Fire Punch"; - MoveNames[97] = "Fire Spin"; - MoveNames[98] = "Fissure"; - MoveNames[99] = "Flail"; - MoveNames[100] = "Flame Wheel"; - MoveNames[101] = "Flamethrower"; - MoveNames[102] = "Flatter"; - MoveNames[103] = "Fly"; - MoveNames[104] = "Focus Energy"; - MoveNames[105] = "Focus Punch"; - MoveNames[106] = "Follow Me"; - MoveNames[107] = "Foresight"; - MoveNames[108] = "Fury Attack"; - MoveNames[109] = "Fury Cutter"; - MoveNames[110] = "Fury Swipes"; - MoveNames[111] = "Future Sight"; - MoveNames[112] = "Giga Drain"; - MoveNames[113] = "Glare"; - MoveNames[114] = "Grass Whistle"; - MoveNames[115] = "Growl"; - MoveNames[116] = "Growth"; - MoveNames[117] = "Grudge"; - MoveNames[118] = "Guillotine"; - MoveNames[119] = "Gust"; - MoveNames[120] = "Hail"; - MoveNames[121] = "Harden"; - MoveNames[122] = "Haze"; - MoveNames[124] = "Headbutt"; - MoveNames[125] = "Heal Bell"; - MoveNames[126] = "Heat Wave"; - MoveNames[127] = "Helping Hand"; - MoveNames[128] = "High Jump Kick"; - MoveNames[129] = "Hidden Power"; - MoveNames[130] = "Horn Attack"; - MoveNames[131] = "Horn Drill"; - MoveNames[132] = "Howl"; - MoveNames[133] = "Hydro Pump"; - MoveNames[134] = "Hyper Beam"; - MoveNames[135] = "Hyper Fang"; - MoveNames[136] = "Hyper Voice"; - MoveNames[137] = "Hypnosis"; - MoveNames[138] = "Ice Ball"; - MoveNames[139] = "Ice Beam"; - MoveNames[140] = "Ice Punch"; - MoveNames[141] = "Icicle Spear"; - MoveNames[142] = "Icy Wind"; - MoveNames[143] = "Imprison"; - MoveNames[144] = "Ingrain"; - MoveNames[145] = "Iron Defense"; - MoveNames[146] = "Iron Tail"; - MoveNames[147] = "Jump Kick"; - MoveNames[148] = "Karate Chop"; - MoveNames[149] = "Kinesis"; - MoveNames[150] = "Knock Off"; - MoveNames[151] = "Leaf Blade"; - MoveNames[152] = "Leech Life"; - MoveNames[153] = "Leech Seed"; - MoveNames[154] = "Leer"; - MoveNames[155] = "Lick"; - MoveNames[156] = "Light Screen"; - MoveNames[157] = "IGNORE 5"; - MoveNames[158] = "Lock-On"; - MoveNames[159] = "Lovely Kiss"; - MoveNames[160] = "Low Kick"; - MoveNames[161] = "Luster Purge"; - MoveNames[162] = "Mach Punch"; - MoveNames[163] = "Magic Coat"; - MoveNames[164] = "Magical Leaf"; - MoveNames[165] = "Magnitude"; - MoveNames[166] = "Mean Look"; - MoveNames[167] = "Meditate"; - MoveNames[168] = "Mega Drain"; - MoveNames[169] = "Mega Kick"; - MoveNames[170] = "Mega Punch"; - MoveNames[171] = "Megahorn"; - MoveNames[172] = "Memento"; - MoveNames[173] = "Metal Claw"; - MoveNames[174] = "Metal Sound"; - MoveNames[175] = "Meteor Mash"; - MoveNames[176] = "Metronome"; - MoveNames[177] = "Milk Drink"; - MoveNames[178] = "Mimic"; - MoveNames[179] = "Mind Reader"; - MoveNames[180] = "Minimize"; - MoveNames[181] = "Mirror Coat"; - MoveNames[182] = "Mirror Move"; - MoveNames[183] = "Mist"; - MoveNames[184] = "Mist Ball"; - MoveNames[185] = "Moonlight"; - MoveNames[186] = "Morning Sun"; - MoveNames[187] = "Mud Shot"; - MoveNames[188] = "Mud-Slap"; - MoveNames[189] = "Mud Sport"; - MoveNames[190] = "Muddy Water"; - MoveNames[191] = "Nature Power"; - MoveNames[192] = "Needle Arm"; - MoveNames[193] = "Night Shade"; - MoveNames[194] = "Nightmare"; - MoveNames[195] = "Octazooka"; - MoveNames[196] = "Odor Sleuth"; - MoveNames[197] = "Outrage"; - MoveNames[198] = "Pain Split"; - MoveNames[199] = "Pay Day"; - MoveNames[200] = "Peck"; - MoveNames[201] = "Perish Song"; - MoveNames[202] = "Petal Dance"; - MoveNames[203] = "Pin Missile"; - MoveNames[204] = "Poison Fang"; - MoveNames[205] = "Poison Gas"; - MoveNames[206] = "Poison Sting"; - MoveNames[207] = "Poison Tail"; - MoveNames[208] = "Poison Powder"; - MoveNames[209] = "Pound"; - MoveNames[210] = "Powder Snow"; - MoveNames[211] = "Present"; - MoveNames[212] = "Protect"; - MoveNames[213] = "Psybeam"; - MoveNames[214] = "Psych Up"; - MoveNames[215] = "Psychic"; - MoveNames[216] = "Psycho Boost"; - MoveNames[217] = "Psywave"; - MoveNames[218] = "Pursuit"; - MoveNames[219] = "Quick Attack"; - MoveNames[220] = "Rage"; - MoveNames[221] = "Rain Dance"; - MoveNames[222] = "Rapid Spin"; - MoveNames[223] = "Razor Leaf"; - MoveNames[224] = "Razor Wind"; - MoveNames[225] = "Recover"; - MoveNames[226] = "Recycle"; - MoveNames[227] = "Reflect"; - MoveNames[228] = "Refresh"; - MoveNames[229] = "Rest"; - MoveNames[230] = "Revenge"; - MoveNames[231] = "Reversal"; - MoveNames[232] = "Roar"; - MoveNames[233] = "Rock Blast"; - MoveNames[234] = "Rock Slide"; - MoveNames[235] = "Rock Throw"; - MoveNames[236] = "Rock Tomb"; - MoveNames[237] = "Role Play"; - MoveNames[238] = "Rolling Kick"; - MoveNames[239] = "Rollout"; - MoveNames[240] = "Sacred Fire"; - MoveNames[241] = "Safeguard"; - MoveNames[242] = "Sand Tomb"; - MoveNames[243] = "Sand-Attack"; - MoveNames[244] = "Sandstorm"; - MoveNames[245] = "Scratch (broken)"; - MoveNames[246] = "Scary Face"; - MoveNames[247] = "Scratch"; - MoveNames[248] = "Screech"; - MoveNames[249] = "Seismic Toss"; - MoveNames[250] = "Selfdestruct"; - MoveNames[251] = "Shadow Ball"; - MoveNames[252] = "Shadow Punch"; - MoveNames[253] = "Sharpen"; - MoveNames[254] = "Sheer Cold"; - MoveNames[255] = "Signal Beam"; - MoveNames[256] = "Silver Wind"; - MoveNames[257] = "Sing"; - MoveNames[258] = "Sketch"; - MoveNames[259] = "Skull Bash"; - MoveNames[260] = "Sky Attack"; - MoveNames[261] = "Sky Uppercut"; - MoveNames[262] = "Slack Off"; - MoveNames[263] = "Slam"; - MoveNames[264] = "Slash"; - MoveNames[265] = "Sleep Powder"; - MoveNames[266] = "Sleep Talk"; - MoveNames[267] = "Sludge"; - MoveNames[268] = "Sludge Bomb"; - MoveNames[269] = "Smelling Salts"; - MoveNames[270] = "Smog"; - MoveNames[271] = "SmokeScreen"; - MoveNames[272] = "BROKEN"; - MoveNames[273] = "Snatch"; - MoveNames[274] = "Snore"; - MoveNames[275] = "Softboiled"; - MoveNames[276] = "Solar Beam"; - MoveNames[277] = "SonicBoom"; - MoveNames[278] = "Spark"; - MoveNames[279] = "Spider Web"; - MoveNames[280] = "Spike Cannon"; - MoveNames[281] = "Spikes"; - MoveNames[282] = "Spit Up"; - MoveNames[283] = "Spite"; - MoveNames[284] = "Splash"; - MoveNames[285] = "Spore"; - MoveNames[286] = "Steel Wing"; - MoveNames[287] = "Stockpile"; - MoveNames[288] = "Stomp"; - MoveNames[289] = "String Shot"; - MoveNames[290] = "Stun Spore"; - MoveNames[291] = "Submission"; - MoveNames[292] = "Substitute"; - MoveNames[293] = "Sunny Day"; - MoveNames[294] = "Super Fang"; - MoveNames[295] = "Superpower"; - MoveNames[296] = "Supersonic"; - MoveNames[297] = "Swagger"; - MoveNames[298] = "Swallow"; - MoveNames[299] = "Sweet Kiss"; - MoveNames[300] = "Sweet Scent"; - MoveNames[301] = "Swift"; - MoveNames[302] = "Swords Dance"; - MoveNames[303] = "Synthesis"; - MoveNames[304] = "Tackle"; - MoveNames[305] = "Tail Glow"; - MoveNames[306] = "Tail Whip"; - MoveNames[307] = "Take Down"; - MoveNames[308] = "Taunt"; - MoveNames[309] = "Teeter Dance"; - MoveNames[310] = "Teleport"; - MoveNames[311] = "Thief"; - MoveNames[312] = "Thrash"; - MoveNames[313] = "Thunder"; - MoveNames[314] = "Thunder Wave"; - MoveNames[315] = "Thunderbolt"; - MoveNames[316] = "Thunder Punch"; - MoveNames[317] = "ThunderShock"; - MoveNames[319] = "Tickle"; - MoveNames[320] = "Torment"; - MoveNames[321] = "Toxic"; - MoveNames[322] = "Transform"; - MoveNames[323] = "Tri Attack"; - MoveNames[324] = "Trick"; - MoveNames[325] = "Triple Kick"; - MoveNames[326] = "Twineedle"; - MoveNames[327] = "Twister"; - MoveNames[328] = "Uproar"; - MoveNames[329] = "BLANK"; - MoveNames[330] = "ViceGrip"; - MoveNames[331] = "Vine Whip"; - MoveNames[332] = "Vital Throw"; - MoveNames[333] = "Water Gun"; - MoveNames[334] = "Water Pulse"; - MoveNames[335] = "Water Sport"; - MoveNames[336] = "Water Spout"; - MoveNames[337] = "Waterfall"; - MoveNames[338] = "Weather Ball"; - MoveNames[339] = "Whirlpool"; - MoveNames[340] = "Whirlwind"; - MoveNames[341] = "Will-o-Wisp"; - MoveNames[342] = "Wing Attack"; - MoveNames[343] = "Wish"; - MoveNames[344] = "Withdraw"; - MoveNames[345] = "Wrap"; - MoveNames[346] = "Yawn"; - MoveNames[347] = "Zap Cannon"; - MoveNames[348] = "Cut"; - MoveNames[349] = "BROKEN"; - MoveNames[350] = "Surf"; - MoveNames[351] = "Strength"; - MoveNames[352] = "Flash"; - MoveNames[353] = "Struggle"; - MoveNames[354] = "BROKEN"; - MoveNames[355] = "BROKEN"; - MoveNames[356] = "Return"; - MoveNames[357] = "Frustration"; - MoveNames[358] = "Rock Smash"; - MoveNames[359] = "Facade"; - MoveNames[360] = "Skill Swap"; - MoveNames[361] = "Secret Power"; - MoveNames[362] = "Dive"; - MoveNames[363] = "Blast Burn"; - MoveNames[364] = "Hydro Cannon"; - MoveNames[365] = "Overheat"; - MoveNames[366] = "Frenzy Plant"; - MoveNames[367] = "Volt Tackle"; - MoveNames[368] = "Shock Wave"; - MoveNames[369] = "Roost"; - MoveNames[370] = "Gravity"; - MoveNames[371] = "Miracle Eye"; - MoveNames[372] = "Wake-Up Slap"; - MoveNames[373] = "Hammer Arm"; - MoveNames[374] = "Gyro Ball"; - MoveNames[375] = "Healing Wish"; - MoveNames[376] = "Brine"; - MoveNames[377] = "Natural Gift"; - MoveNames[378] = "Feint"; - MoveNames[379] = "Pluck"; - MoveNames[380] = "Tailwind"; - MoveNames[381] = "Acupressure"; - MoveNames[382] = "Metal Burst"; - MoveNames[383] = "U-Turn"; - MoveNames[384] = "Close Combat"; - MoveNames[385] = "Payback"; - MoveNames[386] = "Assurance"; - MoveNames[387] = "Embargo"; - MoveNames[388] = "Fling"; - MoveNames[389] = "Psycho Shift"; - MoveNames[390] = "Trump Card"; - MoveNames[391] = "Heal Block"; - MoveNames[392] = "Wring Out"; - MoveNames[393] = "Power Trick"; - MoveNames[394] = "Gastro Acid"; - MoveNames[395] = "Lucky Chant"; - MoveNames[396] = "Me First"; - MoveNames[397] = "Copycat"; - MoveNames[398] = "Power Swap"; - MoveNames[399] = "Guard Swap"; - MoveNames[400] = "Punishment"; - MoveNames[401] = "Last Resort"; - MoveNames[402] = "Worry Seed"; - MoveNames[403] = "Sucker Punch"; - MoveNames[404] = "Toxic Spikes"; - MoveNames[405] = "Heart Swap"; - MoveNames[406] = "Aqua Ring"; - MoveNames[407] = "Magnet Rise"; - MoveNames[408] = "Flare Blitz"; - MoveNames[409] = "Force Palm"; - MoveNames[410] = "Aura Sphere"; - MoveNames[411] = "Rock Polish"; - MoveNames[412] = "Poison Jab"; - MoveNames[413] = "Dark Pulse"; - MoveNames[414] = "Night Slash"; - MoveNames[415] = "Aqua Tail"; - MoveNames[416] = "Seed Bomb"; - MoveNames[417] = "Air Slash"; - MoveNames[418] = "X-Scissor"; - MoveNames[419] = "Bug Buzz"; - MoveNames[420] = "Dragon Pulse"; - MoveNames[421] = "Dragon Rush"; - MoveNames[422] = "Power Gem"; - MoveNames[423] = "Drain Punch"; - MoveNames[424] = "Vacuum Wave"; - MoveNames[425] = "Focus Blast"; - MoveNames[426] = "Energy Ball"; - MoveNames[427] = "Brave Bird"; - MoveNames[428] = "Earth Power"; - MoveNames[429] = "Switcheroo"; - MoveNames[430] = "Giga Impact"; - MoveNames[431] = "Nasty Plot"; - MoveNames[432] = "Bullet Punch"; - MoveNames[433] = "Avalanche"; - MoveNames[434] = "Ice Shard"; - MoveNames[435] = "Shadow Claw"; - MoveNames[436] = "Thunder Fang"; - MoveNames[437] = "Ice Fang"; - MoveNames[438] = "Fire Fang"; - MoveNames[439] = "Shadow Sneak"; - MoveNames[440] = "Mud Bomb"; - MoveNames[441] = "Psycho Cut"; - MoveNames[442] = "Zen Headbutt"; - MoveNames[443] = "Mirror Shot"; - MoveNames[444] = "Flash Cannon"; - MoveNames[445] = "Rock Climb"; - MoveNames[446] = "Defog"; - MoveNames[447] = "Trick Room"; - MoveNames[448] = "Draco Meteor"; - MoveNames[449] = "Discharge"; - MoveNames[450] = "Lava Plume"; - MoveNames[451] = "Leaf Storm"; - MoveNames[452] = "Power Whip"; - MoveNames[453] = "Rock Wrecker"; - MoveNames[454] = "Cross Poison"; - MoveNames[455] = "Gunk Shot"; - MoveNames[456] = "Iron Head"; - MoveNames[457] = "Magnet Bomb"; - MoveNames[458] = "Stone Edge"; - MoveNames[459] = "Captivate"; - MoveNames[460] = "Stealth Rock"; - MoveNames[461] = "Grass Knot"; - MoveNames[462] = "Chatter"; - MoveNames[463] = "Judgment"; - MoveNames[464] = "Bug Bite"; - MoveNames[465] = "Charge Beam"; - MoveNames[466] = "Wood Hammer"; - MoveNames[467] = "Aqua Jet"; - MoveNames[468] = "Attack Order"; - MoveNames[469] = "Defend Order"; - MoveNames[470] = "Heal Order"; - MoveNames[471] = "Head Smash"; - MoveNames[472] = "Double Hit"; - MoveNames[473] = "Roar Of Time"; - MoveNames[474] = "Spacial Rend"; - MoveNames[475] = "Lunar Dance"; - MoveNames[476] = "Crush Grip"; - MoveNames[477] = "Magma Storm"; - MoveNames[478] = "Dark Void"; - MoveNames[479] = "Seed Flare"; - MoveNames[480] = "Ominous Wind"; - MoveNames[481] = "Shadow Force"; - MoveNames[482] = "Inferno"; - MoveNames[483] = "Flame Burst"; - MoveNames[484] = "Rage Powder"; - MoveNames[485] = "Quiver Dance"; - MoveNames[486] = "Hurricane"; - MoveNames[487] = "Drill Run"; - MoveNames[488] = "Acid Spray"; - MoveNames[489] = "Coil"; - MoveNames[490] = "Flame Charge"; - MoveNames[491] = "Chip Away"; - MoveNames[493] = "Echoed Voice"; - MoveNames[492] = "Synchronoise"; - MoveNames[123] = "Acrobatics"; - MoveNames[494] = "Electro Ball"; - MoveNames[495] = "Bestow"; - MoveNames[496] = "Stored Power"; - MoveNames[497] = "After You"; - MoveNames[498] = "Round"; - MoveNames[318] = "Soul Crush"; - MoveNames[499] = "Heavy Slam"; - MoveNames[500] = "Work Up"; - MoveNames[501] = "Psyshock"; - MoveNames[502] = "Wide Guard"; - MoveNames[503] = "Quick Guard"; - MoveNames[504] = "Retaliate"; - MoveNames[505] = "Sludge Wave"; - MoveNames[506] = "Soak"; - MoveNames[507] = "Bulldoze"; - MoveNames[508] = "Wonder Room"; - MoveNames[509] = "Final Gambit"; - MoveNames[510] = "Smack Down"; - MoveNames[511] = "Steamroller"; - MoveNames[512] = "Autotomize"; - MoveNames[513] = "Petal Blizzard"; - MoveNames[514] = "Fell Stinger"; - MoveNames[515] = "Belch"; - MoveNames[516] = "Play Nice"; - MoveNames[517] = "Wild Charge"; - MoveNames[518] = "Moonblast"; - MoveNames[519] = "Venoshock"; - MoveNames[520] = "Play Rough"; - MoveNames[521] = "Nuzzle"; - MoveNames[522] = "Baby-Doll Eyes"; - MoveNames[523] = "Disarming Voice"; - MoveNames[524] = "Hex"; - MoveNames[525] = "Megavolt"; - MoveNames[526] = "Shell Smash"; - MoveNames[527] = "Razor Shell"; - MoveNames[528] = "Eerie Impulse"; - MoveNames[529] = "Magic Room"; - MoveNames[530] = "Dragon Tail"; - MoveNames[531] = "Circle Throw"; - MoveNames[532] = "Volt Switch"; - MoveNames[533] = "Sticky Web"; - MoveNames[534] = "Horn Leech"; - MoveNames[535] = "Scald"; - MoveNames[536] = "Dazzling Gleam"; - MoveNames[537] = "Foul Play"; - MoveNames[538] = "Accelerock"; - MoveNames[539] = "Anchor Shot"; - MoveNames[540] = "Aurora Veil"; - MoveNames[541] = "Beak Blast"; - MoveNames[542] = "Blue Flare"; - MoveNames[543] = "Bolt Strike"; - MoveNames[544] = "Boomburst"; - MoveNames[545] = "Brutal Swing"; - MoveNames[546] = "Burn Up"; - MoveNames[547] = "Clanging Scales"; - MoveNames[548] = "Clear Smog"; - MoveNames[549] = "Core Enforcer"; - MoveNames[550] = "Crafty Shield"; - MoveNames[551] = "Darkest Lariat"; - MoveNames[552] = "Dragon Ascent"; - MoveNames[553] = "Dragon Hammer"; - MoveNames[554] = "Draining Kiss"; - MoveNames[555] = "Dual Chop"; - MoveNames[556] = "Electric Terrain"; - MoveNames[557] = "Electrify"; - MoveNames[558] = "Entrainment"; - MoveNames[559] = "Fairy Lock"; - MoveNames[560] = "Fairy Wind"; - MoveNames[561] = "Fire Lash"; - MoveNames[562] = "Fire Pledge"; - MoveNames[563] = "First Impression"; - MoveNames[564] = "Fleur Cannon"; - MoveNames[565] = "Floral Healing"; - MoveNames[566] = "Flying Press"; - MoveNames[567] = "Forest's Curse"; - MoveNames[568] = "Freeze Shock"; - MoveNames[569] = "Freeze-Dry"; - MoveNames[570] = "Frost Breath"; - MoveNames[571] = "Fusion Bolt"; - MoveNames[572] = "Fusion Flare"; - MoveNames[573] = "Gear Grind"; - MoveNames[574] = "Gear Up"; - MoveNames[575] = "Grass Pledge"; - MoveNames[576] = "Grassy Terrain"; - MoveNames[577] = "Guard Split"; - MoveNames[578] = "Happy Hour"; - MoveNames[579] = "Head Charge"; - MoveNames[580] = "Heal Pulse"; - MoveNames[581] = "Heart Stamp"; - MoveNames[582] = "Heat Crash"; - MoveNames[583] = "High Horsepower"; - MoveNames[584] = "Hold Back"; - MoveNames[585] = "Hold Hands"; - MoveNames[586] = "Hyperspace Fury"; - MoveNames[587] = "Hyperspace Hole"; - MoveNames[588] = "Ice Burn"; - MoveNames[589] = "Ice Hammer"; - MoveNames[590] = "Icicle Crash"; - MoveNames[591] = "Incinerate"; - MoveNames[592] = "Infestation"; - MoveNames[593] = "Instruct"; - MoveNames[594] = "Ion Deluge"; - MoveNames[595] = "Land's Wrath"; - MoveNames[596] = "Leaf Tornado"; - MoveNames[597] = "Leafage"; - MoveNames[598] = "Light of Ruin"; - MoveNames[599] = "Liquidation"; - MoveNames[600] = "Lunge"; - MoveNames[601] = "Mat Block"; - MoveNames[602] = "Mind Blown"; - MoveNames[603] = "Misty Terrain"; - MoveNames[604] = "Moongeist Beam"; - MoveNames[605] = "Multi-Attack"; - MoveNames[606] = "Nature's Madness"; - MoveNames[607] = "Night Daze"; - MoveNames[608] = "Oblivion Wing"; - MoveNames[609] = "Origin Pulse"; - MoveNames[610] = "Parabolic Charge"; - MoveNames[611] = "Phantom Force"; - MoveNames[612] = "Photon Geyser"; - MoveNames[613] = "Plasma Fists"; - MoveNames[614] = "Pollen Puff"; - MoveNames[615] = "Powder"; - MoveNames[616] = "Power Split"; - MoveNames[617] = "Power Trip"; - MoveNames[618] = "Precipice Blades"; - MoveNames[619] = "Prismatic Laser"; - MoveNames[620] = "Psychic Fangs"; - MoveNames[621] = "Psychic Terrain"; - MoveNames[622] = "Psystrike"; - MoveNames[623] = "Purify"; - MoveNames[624] = "Quash"; - MoveNames[625] = "Reflect Type"; - MoveNames[626] = "Relic Song"; - MoveNames[627] = "Revelation Dance"; - MoveNames[628] = "Sacred Sword"; - MoveNames[629] = "Searing Shot"; - MoveNames[630] = "Secret Sword"; - MoveNames[631] = "Shadow Bone"; - MoveNames[632] = "Shell Trap"; - MoveNames[633] = "Simple Beam"; - MoveNames[634] = "Sky Drop"; - MoveNames[635] = "Smart Strike"; - MoveNames[636] = "Solar Blade"; - MoveNames[637] = "Sparkling Aria"; - MoveNames[638] = "Spectral Thief"; - MoveNames[639] = "Speed Swap"; - MoveNames[640] = "Spirit Shackle"; - MoveNames[641] = "Spotlight"; - MoveNames[642] = "Steam Eruption"; - MoveNames[643] = "Stomping Tantrum"; - MoveNames[644] = "Storm Throw"; - MoveNames[645] = "Strength Sap"; - MoveNames[646] = "Sunsteel Strike"; - MoveNames[647] = "Tail Slap"; - MoveNames[648] = "Tearful Look"; - MoveNames[649] = "Techno Blast"; - MoveNames[650] = "Telekinesis"; - MoveNames[651] = "Thousand Arrows"; - MoveNames[652] = "Thousand Waves"; - MoveNames[653] = "Throat Chop"; - MoveNames[654] = "Topsy-Turvy"; - MoveNames[655] = "Toxic Thread"; - MoveNames[656] = "Trick-or-Treat"; - MoveNames[657] = "Trop Kick"; - MoveNames[658] = "Water Pledge"; - MoveNames[659] = "Water Shuriken"; - MoveNames[660] = "Zing Zap"; + MoveNames = Moves.Select(m => m.Name ?? "").ToArray(); } } } diff --git a/PROProtocol/Npc.cs b/PROProtocol/Npc.cs index 26ae536..3abbc92 100644 --- a/PROProtocol/Npc.cs +++ b/PROProtocol/Npc.cs @@ -46,6 +46,7 @@ public class Npc { 74, "chuck" }, { 101, "headbuttable tree" }, { 111, "bill" }, + { 113, "interactive environment"}, { 119, "pokestop"}, }; @@ -65,7 +66,7 @@ public class Npc public bool IsMoving => Path.Length > 0 && Path.IndexOfAny(Movements) >= 0; - public bool CanBlockPlayer => Type != 10; + public bool CanBlockPlayer => Type != 10 && Type != 113; public Npc(int id, string name, bool isBattler, int type, int x, int y, Direction direction, int losLength, string path) { @@ -86,11 +87,11 @@ public Npc Clone() return new Npc(Id, Name, IsBattler, Type, PositionX, PositionY, Direction, LosLength, Path); } - public bool IsInLineOfSight(int x, int y) + public bool IsInLineOfSight(int x, int y, bool skipMovingNpc = false) { if (x != PositionX && y != PositionY) return false; int distance = GameClient.DistanceBetween(PositionX, PositionY, x, y); - if (distance > LosLength) return false; + if (distance > LosLength || (skipMovingNpc && IsMoving)) return false; switch (Direction) { case Direction.Up: diff --git a/PROProtocol/PROProtocol.csproj b/PROProtocol/PROProtocol.csproj index 52d1cb9..68f70b5 100644 --- a/PROProtocol/PROProtocol.csproj +++ b/PROProtocol/PROProtocol.csproj @@ -30,10 +30,11 @@ 4 - - ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + @@ -53,13 +54,14 @@ - + + - + @@ -67,12 +69,13 @@ + - + @@ -83,14 +86,25 @@ - PreserveNewest + Always + + + + + Always + + + Always + + + Always + + + Always - - PreserveNewest -