From d6919f8bc6909a29d4577cff001bbb454ee21615 Mon Sep 17 00:00:00 2001 From: Brendan Enrick <2243498+benrick@users.noreply.github.com> Date: Thu, 7 May 2020 16:23:29 -0400 Subject: [PATCH] Adding Basic Give/Remove GP Commands --- .../Diagnostics/Memory/GpAccessor.cs | 87 +++++++++++++++++++ .../Diagnostics/Memory/IGpAccessor.cs | 9 ++ .../Extensions/StringExtensions.cs | 6 ++ .../FinalFantasy/Addresses.cs | 1 + .../Settings/CommandSettings.cs | 31 ++++++- .../Settings/EquipmentSettings.cs | 1 + .../Settings/PlayerGpSettings.cs | 57 ++++++++++++ .../Commands/GivePlayerGpCommand.cs | 59 +++++++++++++ .../Commands/RemovePlayerGpCommand.cs | 68 +++++++++++++++ .../Startup/DependencyRegistrar.cs | 3 + 10 files changed, 320 insertions(+), 2 deletions(-) create mode 100644 src/InteractiveSeven.Core/Diagnostics/Memory/GpAccessor.cs create mode 100644 src/InteractiveSeven.Core/Diagnostics/Memory/IGpAccessor.cs create mode 100644 src/InteractiveSeven.Core/Settings/PlayerGpSettings.cs create mode 100644 src/InteractiveSeven.Twitch/Commands/GivePlayerGpCommand.cs create mode 100644 src/InteractiveSeven.Twitch/Commands/RemovePlayerGpCommand.cs diff --git a/src/InteractiveSeven.Core/Diagnostics/Memory/GpAccessor.cs b/src/InteractiveSeven.Core/Diagnostics/Memory/GpAccessor.cs new file mode 100644 index 00000000..2875f420 --- /dev/null +++ b/src/InteractiveSeven.Core/Diagnostics/Memory/GpAccessor.cs @@ -0,0 +1,87 @@ +using InteractiveSeven.Core.FinalFantasy; +using InteractiveSeven.Core.Settings; +using Microsoft.Extensions.Logging; +using System; + +namespace InteractiveSeven.Core.Diagnostics.Memory +{ + public class GpAccessor : IGpAccessor + { + private readonly IMemoryAccessor _memoryAccessor; + private readonly ILogger _logger; + private ApplicationSettings Settings => ApplicationSettings.Instance; + private static readonly object Padlock = new object(); + + public GpAccessor(IMemoryAccessor memoryAccessor, ILogger logger) + { + _memoryAccessor = memoryAccessor; + _logger = logger; + } + + public ushort GetGp() + { + try + { + lock (Padlock) + { + var buffer = new byte[Addresses.Gp.NumBytes]; + _memoryAccessor.ReadMem(Settings.ProcessName, Addresses.Gp.Address, buffer); + return BitConverter.ToUInt16(buffer); + } + } + catch (Exception e) + { + _logger.LogError(e, "Error setting player GP."); + } + + return 0; + } + + public void AddGp(in ushort amount) + { + try + { + lock (Padlock) + { + var buffer = new byte[Addresses.Gp.NumBytes]; + _memoryAccessor.ReadMem(Settings.ProcessName, Addresses.Gp.Address, buffer); + ushort currentBalance = BitConverter.ToUInt16(buffer); + currentBalance += amount; + _memoryAccessor.WriteMem(Settings.ProcessName, Addresses.Gp.Address, + BitConverter.GetBytes(currentBalance)); + } + } + catch (Exception e) + { + _logger.LogError(e, "Error adding GP to player."); + } + } + + public void RemoveGp(ushort amount) + { + try + { + lock (Padlock) + { + var buffer = new byte[Addresses.Gp.NumBytes]; + _memoryAccessor.ReadMem(Settings.ProcessName, Addresses.Gp.Address, buffer); + ushort currentBalance = BitConverter.ToUInt16(buffer); + if (currentBalance >= amount) + { + currentBalance -= amount; + } + else + { + currentBalance = 0; + } + _memoryAccessor.WriteMem(Settings.ProcessName, Addresses.Gp.Address, + BitConverter.GetBytes(currentBalance)); + } + } + catch (Exception e) + { + _logger.LogError(e, "Error adding GP to player."); + } + } + } +} \ No newline at end of file diff --git a/src/InteractiveSeven.Core/Diagnostics/Memory/IGpAccessor.cs b/src/InteractiveSeven.Core/Diagnostics/Memory/IGpAccessor.cs new file mode 100644 index 00000000..00a9c9b2 --- /dev/null +++ b/src/InteractiveSeven.Core/Diagnostics/Memory/IGpAccessor.cs @@ -0,0 +1,9 @@ +namespace InteractiveSeven.Core.Diagnostics.Memory +{ + public interface IGpAccessor + { + ushort GetGp(); + void AddGp(in ushort amount); + void RemoveGp(ushort amount); + } +} \ No newline at end of file diff --git a/src/InteractiveSeven.Core/Extensions/StringExtensions.cs b/src/InteractiveSeven.Core/Extensions/StringExtensions.cs index 7ed677fe..4aaac5ca 100644 --- a/src/InteractiveSeven.Core/Extensions/StringExtensions.cs +++ b/src/InteractiveSeven.Core/Extensions/StringExtensions.cs @@ -24,6 +24,12 @@ public static int SafeIntParse(this string text) return result; } + public static ushort SafeUshortParse(this string text) + { + ushort.TryParse(text, out ushort result); + return result; + } + public static string NoAt(this string text) => text.TrimStart('@'); } } \ No newline at end of file diff --git a/src/InteractiveSeven.Core/FinalFantasy/Addresses.cs b/src/InteractiveSeven.Core/FinalFantasy/Addresses.cs index f0411169..c9514aa5 100644 --- a/src/InteractiveSeven.Core/FinalFantasy/Addresses.cs +++ b/src/InteractiveSeven.Core/FinalFantasy/Addresses.cs @@ -17,6 +17,7 @@ public static class Addresses public static readonly MemLoc MenuBotRightSave = new MemLoc(0x91EFE1, 3); // order red, green, blue public static readonly MemLoc Gil = new MemLoc(0xDC08B4, 4); // 4 bytes + public static readonly MemLoc Gp = new MemLoc(0xDC0A26, 2); // Max of 10,000? public static readonly MemLoc GameMoment = new MemLoc(0xDC08DC, 2); public static readonly MemLoc MenuVisibility = new MemLoc(0xDC08F8, 2); diff --git a/src/InteractiveSeven.Core/Settings/CommandSettings.cs b/src/InteractiveSeven.Core/Settings/CommandSettings.cs index f91da270..af6676a9 100644 --- a/src/InteractiveSeven.Core/Settings/CommandSettings.cs +++ b/src/InteractiveSeven.Core/Settings/CommandSettings.cs @@ -10,6 +10,8 @@ public class CommandSettings : ObservableSettingsBase { private string[] _givePlayerGilCommandWords = { "FindGil", "GivePlayerGil" }; private string[] _removePlayerGilCommandWords = { "DropGil", "RemovePlayerGil" }; + private string[] _givePlayerGpCommandWords = { "FindGp", "GivePlayerGp" }; + private string[] _removePlayerGpCommandWords = { "DropGp", "RemovePlayerGp" }; private string[] _esunaCommandWords = { "Esuna", "Remedy" }; private string[] _healCommandWords = { "Heal", "Cure" }; private string[] _costsCommandWords = { "Costs", "Cost", "Price", "Prices" }; @@ -52,11 +54,16 @@ public CommandSettings() ("BrendanLock", () => new [] {"BrendanLock"}), ("BrendanUnlock", () => new [] {"BrendanUnlock"}), - (nameof(GiveGilCommandWords), - () => GiveGilCommandWords), + (nameof(GivePlayerGilCommandWords), + () => GivePlayerGilCommandWords), (nameof(RemovePlayerGilCommandWords), () => RemovePlayerGilCommandWords), + (nameof(GivePlayerGpCommandWords), + () => GivePlayerGpCommandWords), + (nameof(RemovePlayerGpCommandWords), + () => RemovePlayerGpCommandWords), + (nameof(EsunaCommandWords), () => EsunaCommandWords), (nameof(HealCommandWords), @@ -153,6 +160,26 @@ public string[] RemovePlayerGilCommandWords } } + public string[] GivePlayerGpCommandWords + { + get => _givePlayerGpCommandWords; + set + { + _givePlayerGpCommandWords = RemoveAllDuplicates(value); + OnPropertyChanged(); + } + } + + public string[] RemovePlayerGpCommandWords + { + get => _removePlayerGpCommandWords; + set + { + _removePlayerGpCommandWords = RemoveAllDuplicates(value); + OnPropertyChanged(); + } + } + public string[] EsunaCommandWords { get => _esunaCommandWords; diff --git a/src/InteractiveSeven.Core/Settings/EquipmentSettings.cs b/src/InteractiveSeven.Core/Settings/EquipmentSettings.cs index 7f6c33a7..fc5f7ff6 100644 --- a/src/InteractiveSeven.Core/Settings/EquipmentSettings.cs +++ b/src/InteractiveSeven.Core/Settings/EquipmentSettings.cs @@ -92,5 +92,6 @@ public List AllByName(string name, CharNames charName, Type public List AllArmlets { get; set; } public List AllAccessories { get; set; } public PlayerGilSettings PlayerGilSettings { get; set; } = new PlayerGilSettings(); + public PlayerGpSettings PlayerGpSettings { get; set; } = new PlayerGpSettings(); } } \ No newline at end of file diff --git a/src/InteractiveSeven.Core/Settings/PlayerGpSettings.cs b/src/InteractiveSeven.Core/Settings/PlayerGpSettings.cs new file mode 100644 index 00000000..902b25df --- /dev/null +++ b/src/InteractiveSeven.Core/Settings/PlayerGpSettings.cs @@ -0,0 +1,57 @@ +namespace InteractiveSeven.Core.Settings +{ + public class PlayerGpSettings : ObservableSettingsBase + { + private int _giveMultiplier = 100; + private int _removeMultiplier = 100; + private bool _allowModOverride = true; + private bool _giveGpEnabled = false; + private bool _removeGpEnabled = false; + + public bool GiveGpEnabled // TODO: Add to UI + { + get => _giveGpEnabled; + set + { + _giveGpEnabled = value; + OnPropertyChanged(); + } + } + public bool RemoveGpEnabled // TODO: Add to UI + { + get => _removeGpEnabled; + set + { + _removeGpEnabled = value; + OnPropertyChanged(); + } + } + public int GiveMultiplier // TODO: Add to UI + { + get => _giveMultiplier; + set + { + _giveMultiplier = value; + OnPropertyChanged(); + } + } + public int RemoveMultiplier // TODO: Add to UI + { + get => _removeMultiplier; + set + { + _removeMultiplier = value; + OnPropertyChanged(); + } + } + public bool AllowModOverride // TODO: Add to UI + { + get => _allowModOverride; + set + { + _allowModOverride = value; + OnPropertyChanged(); + } + } + } +} \ No newline at end of file diff --git a/src/InteractiveSeven.Twitch/Commands/GivePlayerGpCommand.cs b/src/InteractiveSeven.Twitch/Commands/GivePlayerGpCommand.cs new file mode 100644 index 00000000..09c739b2 --- /dev/null +++ b/src/InteractiveSeven.Twitch/Commands/GivePlayerGpCommand.cs @@ -0,0 +1,59 @@ +using InteractiveSeven.Core; +using InteractiveSeven.Core.Diagnostics.Memory; +using InteractiveSeven.Core.Emitters; +using InteractiveSeven.Core.Settings; +using InteractiveSeven.Twitch.Model; +using InteractiveSeven.Twitch.Payments; +using System.Linq; +using TwitchLib.Client.Interfaces; + +namespace InteractiveSeven.Twitch.Commands +{ + public class GivePlayerGpCommand : BaseCommand + { + private readonly PaymentProcessor _paymentProcessor; + private readonly ITwitchClient _twitchClient; + private readonly IGpAccessor _gpAccessor; + private readonly IStatusHubEmitter _statusHubEmitter; + private PlayerGpSettings GpSettings => Settings.EquipmentSettings.PlayerGpSettings; + + public GivePlayerGpCommand(PaymentProcessor paymentProcessor, ITwitchClient twitchClient, + IGpAccessor gpAccessor, IStatusHubEmitter statusHubEmitter) + : base(x => x.GivePlayerGpCommandWords, + x => x.EquipmentSettings.PlayerGpSettings.GiveGpEnabled) + { + _paymentProcessor = paymentProcessor; + _twitchClient = twitchClient; + _gpAccessor = gpAccessor; + _statusHubEmitter = statusHubEmitter; + } + + public override void Execute(in CommandData commandData) + { + ushort amount = commandData.Arguments.FirstOrDefault().SafeUshortParse(); + + if (amount <= 0) + { + _twitchClient.SendMessage(commandData.Channel, + $"How much gp do you want to give to the player, {commandData.User.Username}?"); + return; + } + + int gilCost = amount * GpSettings.GiveMultiplier; + GilTransaction gilTransaction = _paymentProcessor.ProcessPayment( + commandData, gilCost, GpSettings.AllowModOverride); + + if (!gilTransaction.Paid) + { + _twitchClient.SendMessage(commandData.Channel, + $"You don't have {gilCost} gil, {commandData.User.Username}."); + return; + } + + _gpAccessor.AddGp(amount); + string message = $"Added {amount} GP for player."; + _twitchClient.SendMessage(commandData.Channel, message); + _statusHubEmitter.ShowEvent(message); + } + } +} \ No newline at end of file diff --git a/src/InteractiveSeven.Twitch/Commands/RemovePlayerGpCommand.cs b/src/InteractiveSeven.Twitch/Commands/RemovePlayerGpCommand.cs new file mode 100644 index 00000000..d019c9f0 --- /dev/null +++ b/src/InteractiveSeven.Twitch/Commands/RemovePlayerGpCommand.cs @@ -0,0 +1,68 @@ +using InteractiveSeven.Core; +using InteractiveSeven.Core.Diagnostics.Memory; +using InteractiveSeven.Core.Emitters; +using InteractiveSeven.Core.Settings; +using InteractiveSeven.Twitch.Model; +using InteractiveSeven.Twitch.Payments; +using System.Linq; +using TwitchLib.Client.Interfaces; + +namespace InteractiveSeven.Twitch.Commands +{ + public class RemovePlayerGpCommand : BaseCommand + { + private readonly PaymentProcessor _paymentProcessor; + private readonly ITwitchClient _twitchClient; + private readonly IGpAccessor _gpAccessor; + private readonly IStatusHubEmitter _statusHubEmitter; + private PlayerGpSettings GpSettings => Settings.EquipmentSettings.PlayerGpSettings; + + public RemovePlayerGpCommand(PaymentProcessor paymentProcessor, ITwitchClient twitchClient, + IGpAccessor gpAccessor, IStatusHubEmitter statusHubEmitter) + : base(x => x.RemovePlayerGpCommandWords, + x => x.EquipmentSettings.PlayerGpSettings.RemoveGpEnabled) + { + _paymentProcessor = paymentProcessor; + _twitchClient = twitchClient; + _gpAccessor = gpAccessor; + _statusHubEmitter = statusHubEmitter; + } + + public override void Execute(in CommandData commandData) + { + ushort amount = commandData.Arguments.FirstOrDefault().SafeUshortParse(); + + if (amount <= 0) + { + _twitchClient.SendMessage(commandData.Channel, + $"How much gp do you want to take from the player, {commandData.User.Username}?"); + return; + } + + uint currentGp = _gpAccessor.GetGp(); + if (amount > currentGp) + { + // TODO: Adjust their request to remove all gp. + _twitchClient.SendMessage(commandData.Channel, + $"Player doesn't have {amount} gp."); + return; + } + + int gilCost = amount * GpSettings.RemoveMultiplier; + GilTransaction gilTransaction = _paymentProcessor.ProcessPayment( + commandData, gilCost, GpSettings.AllowModOverride); + + if (!gilTransaction.Paid) + { + _twitchClient.SendMessage(commandData.Channel, + $"You don't have {gilCost} gil, {commandData.User.Username}."); + return; + } + + _gpAccessor.RemoveGp(amount); + string message = $"Removed {amount} gp from player."; + _twitchClient.SendMessage(commandData.Channel, message); + _statusHubEmitter.ShowEvent(message); + } + } +} \ No newline at end of file diff --git a/src/InteractiveSeven/Startup/DependencyRegistrar.cs b/src/InteractiveSeven/Startup/DependencyRegistrar.cs index f9afd4d1..983414ae 100644 --- a/src/InteractiveSeven/Startup/DependencyRegistrar.cs +++ b/src/InteractiveSeven/Startup/DependencyRegistrar.cs @@ -58,6 +58,7 @@ public static void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -81,6 +82,8 @@ public static void ConfigureServices(IServiceCollection services) services.RegisterNonBattleCommand(); services.RegisterNonBattleCommand(); services.RegisterNonBattleCommand(); + services.RegisterNonBattleCommand(); + services.RegisterNonBattleCommand(); services.RegisterTwitchCommand(); services.RegisterTwitchCommand();