diff --git a/PROBot/BotClient.cs b/PROBot/BotClient.cs index 7808872..5be2f60 100644 --- a/PROBot/BotClient.cs +++ b/PROBot/BotClient.cs @@ -59,6 +59,30 @@ public BotClient() TextOptions = new Dictionary(); } + public void CancelInvokes() + { + if (Script != null) + foreach (Invoker invoker in Script.Invokes) + invoker.Called = true; + } + + public void CallInvokes() + { + if (Script != null) + { + for (int i = Script.Invokes.Count - 1; i >= 0; i--) + { + if (Script.Invokes[i].Time < DateTime.UtcNow) + { + if (Script.Invokes[i].Called) + Script.Invokes.RemoveAt(i); + else + Script.Invokes[i].Call(); + } + } + } + } + public void CreateText(int index, string content) { TextOptions[index] = new TextOption("Text " + index + ": ", "Custom text option " + index + " for use in scripts.", content); @@ -152,6 +176,8 @@ public void Logout(bool allowAutoReconnect) public void Update() { + CallInvokes(); + AutoReconnector.Update(); if (_loginRequested) diff --git a/PROBot/Scripting/BaseScript.cs b/PROBot/Scripting/BaseScript.cs index 6be4742..2eb18d0 100644 --- a/PROBot/Scripting/BaseScript.cs +++ b/PROBot/Scripting/BaseScript.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; namespace PROBot.Scripting { @@ -24,6 +25,8 @@ public virtual void OnLearningMove(string moveName, int pokemonIndex) { } public abstract bool ExecuteNextAction(); + public List Invokes = new List(); + protected void LogMessage(string message) { ScriptMessage?.Invoke(message); diff --git a/PROBot/Scripting/LuaScript.cs b/PROBot/Scripting/LuaScript.cs index c51f693..747aa53 100644 --- a/PROBot/Scripting/LuaScript.cs +++ b/PROBot/Scripting/LuaScript.cs @@ -320,6 +320,12 @@ private void CreateLuaInstance() // File editing actions _lua.Globals["logToFile"] = new Action(LogToFile); _lua.Globals["readLinesFromFile"] = new Func(ReadLinesFromFile); + + _lua.Globals["login"] = new Action(Login); + _lua.Globals["relog"] = new Action(Relog); + _lua.Globals["startScript"] = new Func(StartScript); + _lua.Globals["invoke"] = new Action(Invoke); + _lua.Globals["cancelInvokes"] = new Action(CancelInvokes); foreach (string content in _libsContent) { @@ -430,6 +436,7 @@ private void Fatal(string message) { LogMessage(message); Bot.Stop(); + Bot.CancelInvokes(); } // API: Displays the specified message to the message log and logs out. @@ -2766,5 +2773,123 @@ private void SetTextOptionDescription(int index, string content) Bot.TextOptions[index].Description = content; } + + // API: Logs in using specified credentials + private void Login(string accountName, string password, string server, int socks = 0, string host = "", int port = 0, string socksUser = "", string socksPass = "") + { + server = server.ToUpperInvariant(); + + if (Bot.Game != null) + { + Fatal("error: login: tried to login while already logged in"); + return; + } + + if (server != "BLUE" && server != "RED" && server != "YELLOW") + { + Fatal("error: login: tried to connect to an invalid server: \"" + server + "\""); + return; + } + + LogMessage("Connecting to the server..."); + Account account = new Account(accountName); + account.Password = password; + account.Server = server; + + if (socks == 4 || socks == 5) + { + account.Socks.Version = (SocksVersion)socks; + account.Socks.Host = host; + account.Socks.Port = port; + account.Socks.Username = socksUser; + account.Socks.Password = socksPass; + } + + Bot.Login(account); + } + + // API: Logs out and logs back in after the specified number of seconds, then starts the script shortly after + private void Relog(float seconds, string message) + { + DynValue name = DynValue.NewString(Bot.Account.Name); + DynValue password = DynValue.NewString(Bot.Account.Password); + DynValue server = DynValue.NewString(Bot.Account.Server); + + if (Bot.Account.Socks.Version != SocksVersion.None) + { + DynValue socks = DynValue.NewNumber((int)Bot.Account.Socks.Version); + DynValue host = DynValue.NewString(Bot.Account.Socks.Host); + DynValue port = DynValue.NewNumber(Bot.Account.Socks.Port); + DynValue socksUser = DynValue.NewString(Bot.Account.Socks.Username); + DynValue socksPass = DynValue.NewString(Bot.Account.Socks.Password); + Invoke(_lua.Globals.Get("login"), seconds, name, password, server, socks, host, port, socksUser, socksPass); + } + else + { + Invoke(_lua.Globals.Get("login"), seconds, name, password, server); + } + + Invoke(_lua.Globals.Get("startScript"), seconds + 10); + Logout(message); + } + + // API: Starts the loaded script (usable in the outer scope or with invoke) + private bool StartScript() + { + if (Bot.Game != null && (Bot.Running == BotClient.State.Stopped || Bot.Running == BotClient.State.Paused)) + { + Bot.Start(); + return true; + } + + return false; + } + + // API: Calls the specified function after the specified number of seconds + public void Invoke(DynValue function, float seconds, params DynValue[] args) + { + if (function.Type != DataType.Function && function.Type != DataType.ClrFunction) + { + Fatal("error: invoke: tried to call an invalid function"); + return; + } + + if (seconds == 0) + { + _lua.Call(function, args); + return; + } + + Invoker invoker = new Invoker() + { + Function = function, + Time = DateTime.UtcNow.AddSeconds(seconds), + Script = this, + Args = args + }; + + Invokes.Add(invoker); + } + + // API: Cancels all queued Invokes + private void CancelInvokes() + { + Bot.CancelInvokes(); + } + } + + public class Invoker + { + public DynValue Function; + public DateTime Time; + public LuaScript Script; + public DynValue[] Args; + public bool Called = false; + + public void Call() + { + Called = true; + Script.Invoke(Function, 0, Args); + } } } diff --git a/PROShine/Views/MainWindow.xaml.cs b/PROShine/Views/MainWindow.xaml.cs index 2e18ffe..b2761c2 100644 --- a/PROShine/Views/MainWindow.xaml.cs +++ b/PROShine/Views/MainWindow.xaml.cs @@ -352,7 +352,7 @@ private void BotStopMenuItem_Click(object sender, RoutedEventArgs e) { lock (Bot) { - Bot.Stop(); + Bot.Stop(); } } @@ -403,6 +403,7 @@ private void Bot_ConnectionOpened() SetTitle(Bot.Account.Name + " - " + Bot.Game.Server); UpdateBotMenu(); LogoutMenuItem.IsEnabled = true; + LoginMenuItem.IsEnabled = false; LoginButton.IsEnabled = true; LoginButtonIcon.Icon = FontAwesome.WPF.FontAwesomeIcon.SignOut; LogMessage("Connected, authenticating..."); @@ -823,6 +824,7 @@ private void StopScriptButton_Click(object sender, RoutedEventArgs e) lock (Bot) { Bot.Stop(); + Bot.CancelInvokes(); } } diff --git a/PROShine/Views/TeamView.xaml b/PROShine/Views/TeamView.xaml index f997b3a..7418bde 100644 --- a/PROShine/Views/TeamView.xaml +++ b/PROShine/Views/TeamView.xaml @@ -17,6 +17,7 @@