From 9867d065121cf6f7c2734174a709fd22322fd305 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sun, 27 Feb 2022 22:33:15 +0700 Subject: [PATCH] Introduce denormalized records + mapping functions --- Bot/Embeds.fs | 78 +++++---------------------- Bot/GameHelpers.fs | 111 +++++++++++++++++++++++++------------- Bot/GameTypes.fs | 23 ++++++-- Bot/Games/HackerBattle.fs | 48 ++++++++--------- Bot/Games/Store.fs | 63 ++++++++++++++++++++-- Bot/Games/Trainer.fs | 38 +++++++------ 6 files changed, 206 insertions(+), 155 deletions(-) diff --git a/Bot/Embeds.fs b/Bot/Embeds.fs index 9fd59a4..c2a88f2 100644 --- a/Bot/Embeds.fs +++ b/Bot/Embeds.fs @@ -48,7 +48,7 @@ let constructButtons (actionId: string) (buttonInfo : string) (player: PlayerDat |> Seq.cast let pickDefense actionId player isTrainer = - let shieldItems = player |> Player.getShieldItems + let shieldItems = player.Inventory |> Inventory.filterByShields let buttons = constructButtons actionId (string player.DiscordId) player shieldItems isTrainer let embed = @@ -56,10 +56,10 @@ let pickDefense actionId player isTrainer = .WithTitle("Shield Defense") .WithDescription("Pick a shield to protect yourself from hacks") - for (item,sClass,cooldown) in Player.getShields player do - let hours = TimeSpan.FromMinutes(int cooldown).TotalHours - let against = WeaponClass.getGoodAgainst(sClass) |> snd - embed.AddField(item.Name, $"Active {hours} hours\nDefeats {against}", true) |> ignore + for shield in Inventory.getShieldItems player.Inventory do + let hours = TimeSpan.FromMinutes(int shield.Cooldown).TotalHours + let against = WeaponClass.getGoodAgainst(shield.Class) |> snd + embed.AddField(shield.Name, $"Active {hours} hours\nDefeats {against}", true) |> ignore DiscordFollowupMessageBuilder() .AddComponents(buttons) @@ -67,7 +67,7 @@ let pickDefense actionId player isTrainer = .AsEphemeral(true) let pickHack actionId attacker defender isTrainer = - let hackItems = attacker |> Player.getHackItems + let hackItems = attacker.Inventory |> Inventory.filterByHacks let buttons = constructButtons actionId $"{defender.DiscordId}-{defender.Name}" attacker hackItems isTrainer let stealMsg = if not isTrainer then $"{defender.Name} has **{defender.Bank} $GBT** we can take from them. " else "" @@ -77,16 +77,16 @@ let pickHack actionId attacker defender isTrainer = .WithDescription($"{stealMsg}Pick the hack you want to use.") if not isTrainer then - for (item,power,hClass,cooldown) in Player.getHacks attacker do - let amount = if power > int defender.Bank then int defender.Bank else power - embed.AddField(item.Name, $"Cooldown {cooldown} mins\nExtract {amount} $GBT", true) |> ignore + for hack in Inventory.getHackItems attacker.Inventory do + let amount = if hack.Power > int defender.Bank then int defender.Bank else hack.Power + embed.AddField(hack.Name, $"Cooldown {hack.Cooldown} mins\nExtract {amount} $GBT", true) |> ignore DiscordFollowupMessageBuilder() .AddComponents(buttons) .AddEmbeds([ DiscordEmbedBuilder().WithImageUrl(hackGif).Build() ; embed.Build() ]) .AsEphemeral true -let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : Item) = +let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : HackItem) = let embed = DiscordEmbedBuilder() .WithImageUrl(getItemGif hack.Id) @@ -98,10 +98,10 @@ let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : I .AddEmbed(embed.Build()) .AsEphemeral(true) -let responseCreatedShield ((item,_,cooldown) : ShieldItem) = - let embed = DiscordEmbedBuilder().WithImageUrl(getItemGif item.Id) +let responseCreatedShield (shield : ShieldItem) = + let embed = DiscordEmbedBuilder().WithImageUrl(getItemGif shield.Id) embed.Title <- "Mounted Shield" - embed.Description <- $"Mounted {item.Name} shield for {TimeSpan.FromMinutes(int cooldown).Hours} hours" + embed.Description <- $"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours" DiscordFollowupMessageBuilder() .AddEmbed(embed) @@ -111,58 +111,6 @@ let eventSuccessfulHack (ctx : IDiscordContext) target prize = DiscordMessageBuilder() .WithContent($"{ctx.GetDiscordMember().Username} successfully hacked <@{target.DiscordId}> and took {prize} GoodBoyTokenz") -let getBuyItemsEmbed (playerInventory : Inventory) (storeInventory : Inventory) = - let embeds , buttons = - storeInventory - |> List.map (fun item -> - let embed = DiscordEmbedBuilder() - match item.Type with - | Hack(power,_,cooldown) -> - embed.AddField($"$GBT Reward |", string power, true) - .AddField("Cooldown |", $"{TimeSpan.FromMinutes(int cooldown).Minutes} minutes", true) - .WithThumbnail(getItemIcon item.Id) - |> ignore - | Shield(shieldClass,cooldown) -> - embed.AddField($"Strong against |", WeaponClass.getGoodAgainst shieldClass |> snd |> string, true) -// .AddField($"Defensive Strength |", string item.Power, true) - .AddField("Active For |", $"{TimeSpan.FromMinutes(int cooldown).Hours} hours", true) - .WithThumbnail(getItemIcon item.Id) - |> ignore - | _ -> () - embed - .AddField("Price 💰", (if item.Price = 0 then "Free" else $"{item.Price} $GBT"), true) - .WithColor(WeaponClass.getClassEmbedColor item) - .WithTitle($"{item.Name}") - |> ignore - let button = - if playerInventory |> List.exists (fun i -> i.Id = item.Id) - then DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Buy-{item.Id}", $"Own {item.Name}", true) - else DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Buy-{item.Id}", $"Buy {item.Name}") - ( embed.Build() , button :> DiscordComponent )) - |> List.unzip - - DiscordFollowupMessageBuilder() - .AddEmbeds(embeds) - .AddComponents(buttons) - .AsEphemeral(true) - -let getSellEmbed (items : Item list) = - let embeds , buttons = - items - |> List.map (fun item -> - DiscordEmbedBuilder() - .AddField("Sell For 💰", $"{item.Price} $GBT", true) - .WithTitle($"{item.Name}") - .WithColor(WeaponClass.getClassEmbedColor item) - .Build() - , DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Sell-{id}", $"Sell {item.Name}") :> DiscordComponent) - |> List.unzip - - DiscordFollowupMessageBuilder() - .AddEmbeds(embeds) - .AddComponents(buttons) - .AsEphemeral(true) - let getArsenalEmbed (player : PlayerData) = DiscordFollowupMessageBuilder() .AsEphemeral(true) diff --git a/Bot/GameHelpers.fs b/Bot/GameHelpers.fs index abe5637..2218b58 100644 --- a/Bot/GameHelpers.fs +++ b/Bot/GameHelpers.fs @@ -24,20 +24,60 @@ module Armory = inventory |> List.filter (fun item -> match item.Type with Shield _ -> true | _ -> false) |> List.sortBy (fun item -> item.Id) - let getHacks : HackItem list = - battleItems - |> List.choose (fun item -> - match item.Type with - | Hack(power, hackClass, cooldown) -> Some (item , power, hackClass, cooldown) | _ -> None) - |> List.sortBy (fun (item,_,_,_) -> item.Id) - let getShields : ShieldItem list = - battleItems - |> List.choose (fun item -> - match item.Type with - | Shield(hackClass, cooldown) -> Some (item,hackClass,cooldown) | _ -> None) - |> List.sortBy (fun (item,_,_) -> item.Id) - let getHackById id = getHacks |> List.find (fun (item,_,_,_) -> item.Id = id) - let getShieldById id = getShields |> List.find (fun (item,_,_) -> item.Id = id) + let getHackById id inventory = inventory |> getHackItems |> List.find (fun item -> item.Id = id) + let getShieldById id inventory = inventory |> getShieldItems |> List.find (fun item -> item.Id = id) + +module Inventory = + let itemToHack item power hackClass cooldown = { + Id = item.Id + Name = item.Name + Price = item.Price + Power = power + Class = hackClass + Cooldown = cooldown + } + + let itemToShield item hackClass cooldown = { + Id = item.Id + Name = item.Name + Price = item.Price + Class = hackClass + Cooldown = cooldown + } + let hackToItem (hack : HackItem) = { + Id = hack.Id + Name = hack.Name + Price = hack.Price + Type = Hack (hack.Power, hack.Class, hack.Cooldown) + } + let shieldToItem (shield : ShieldItem) = { + Id = shield.Id + Name = shield.Name + Price = shield.Price + Type = Shield (shield.Class, shield.Cooldown) + } + + let filterByHacks inventory = + inventory |> List.filter (fun item -> match item.Type with Hack _ -> true | _ -> false) + let filterByShields inventory = + inventory |> List.filter (fun item -> match item.Type with Shield _ -> true | _ -> false) + + let getHackItems inventory = + inventory + |> List.choose (fun item -> match item.Type with Hack (p,cl,co) -> Some (itemToHack item p cl co) | _ -> None) + |> List.sortBy (fun item -> item.Id) + let getShieldItems inventory = + inventory + |> List.choose (fun item -> match item.Type with Shield (cl,co) -> Some (itemToShield item cl co) | _ -> None) + |> List.sortBy (fun item -> item.Id) + let findHackById id inventory = + inventory |> getHackItems |> List.pick (fun item -> if item.Id = id then Some item else None) + let findShieldById id inventory = + inventory |> getShieldItems |> List.pick (fun item -> if item.Id = id then Some item else None) + let tryFindHackById id inventory = + inventory |> getHackItems |> List.tryFind (fun item -> item.Id = id) + let tryFindShieldById id inventory = + inventory |> getShieldItems |> List.tryFind (fun item -> item.Id = id) module WeaponClass = // TODO: Find a different place to put this @@ -63,26 +103,6 @@ module WeaponClass = | _ -> ( ItemId.Cypher , ItemId.Worm ) module Player = - let getHackItems player = - player.Inventory - |> List.filter (fun item -> match item.Type with Hack _ -> true | _ -> false) - |> List.sortBy (fun item -> item.Id) - let getShieldItems player = - player.Inventory - |> List.filter (fun item -> match item.Type with Shield _ -> true | _ -> false) - |> List.sortBy (fun item -> item.Id) - let getHacks player : HackItem list = - player.Inventory - |> List.choose (fun item -> - match item.Type with - | Hack(power, hackClass, cooldown) -> Some (item , power, hackClass, cooldown) | _ -> None) - |> List.sortBy (fun (item,_,_,_) -> item.Id) - let getShields player : ShieldItem list = - player.Inventory - |> List.choose (fun item -> - match item.Type with - | Shield(hackClass, cooldown) -> Some (item , hackClass, cooldown) | _ -> None) - |> List.sortBy (fun (item,_,_) -> item.Id) let getHackEvents player = player.Events |> Array.filter (fun act -> match act.Type with PlayerEventType.Hacking h -> h.IsInstigator | _ -> false) @@ -133,8 +153,27 @@ module Arsenal = let statusFormat p = let hacks = Player.getHackEvents p - $"**Hacks:** {Player.getHackItems p |> battleItemFormat}\n - **Shields:** {Player.getShieldItems p |> battleItemFormat}\n + $"**Hacks:** {Inventory.filterByHacks p.Inventory |> battleItemFormat}\n + **Shields:** {Inventory.filterByShields p.Inventory |> battleItemFormat}\n **Hack Attacks:**\n{ hacks |> Array.take (min hacks.Length 10) |> actionFormat}\n **Active Shields:**\n{Player.getShieldEvents p |> actionFormat}" +module Items = + let mapHack fn inventory = + inventory + |> List.choose (fun item -> + match item.Type with + | Hack(power, hackClass, cooldown) -> Some <| fn item power hackClass cooldown + | _ -> None) + let mapShield fn inventory = + inventory + |> List.choose (fun item -> + match item.Type with + | Shield(hackClass, cooldown) -> Some <| fn item hackClass cooldown + | _ -> None) + let doShields fn inventory = + inventory + |> List.iter (fun item -> + match item.Type with + | Shield(hackClass, cooldown) -> fn item hackClass cooldown + | _ -> ()) diff --git a/Bot/GameTypes.fs b/Bot/GameTypes.fs index 4f15f61..b9e9454 100644 --- a/Bot/GameTypes.fs +++ b/Bot/GameTypes.fs @@ -73,11 +73,24 @@ type PlayerEvent = Cooldown : int Timestamp : DateTime } -type HackItem = Item * int * int * int -and ShieldItem = Item * int * int -and FoodItem = Item * (Item -> PlayerData -> PlayerData) -and AccessoryItem = Item * (Item -> PlayerData -> PlayerData) -and ItemType = +type HackItem = { + Id : int + Name : string + Price : int + Power : int + Class : int + Cooldown : int +} + +type ShieldItem = { + Id : int + Name : string + Price : int + Class : int + Cooldown : int +} + +type ItemType = | Hack of power : int * hackClass : int * cooldown : int | Shield of shieldClass : int * cooldown : int | Food of effect : (Item -> PlayerData -> PlayerData) diff --git a/Bot/Games/HackerBattle.fs b/Bot/Games/HackerBattle.fs index f6c3fae..10c5b94 100644 --- a/Bot/Games/HackerBattle.fs +++ b/Bot/Games/HackerBattle.fs @@ -44,7 +44,7 @@ let checkWeaponHasCooldown (weapon : Item) attacker = | None -> Ok attacker let checkHasEmptyHacks attacker = - match Player.getHacks attacker with + match Inventory.getHackItems attacker.Inventory with | [] -> Error $"You currently do not have any Hacks to take 💰$GBT from others. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one." | _ -> Ok attacker @@ -68,36 +68,30 @@ let checkTargetHasFunds target player = | true -> Error $"Looks like the poor bastard has no $GBT... pick a different victim." | false -> Ok player -let calculateDamage ((_,_,hackClass,_) : HackItem) ((_,shieldClass,_) : ShieldItem) = - if hackClass = shieldClass - then Weak - else Strong - let runHackerBattle defender (hack : HackItem) = defender |> Player.removeExpiredActions |> fun p -> p.Events |> Array.choose (fun event -> match event.Type with - | Shielding id -> Armory.getShields |> List.find (fun (item,_,_) -> item.Id = id) |> Some + | Shielding id -> defender.Inventory |> Inventory.getShieldItems |> List.find (fun item -> item.Id = id) |> Some | _ -> None) - |> Array.map (calculateDamage hack) + |> Array.map (fun shield -> if hack.Class = shield.Class then Weak else Strong) |> Array.contains Weak let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerData) (hack : HackItem) prize = - let (item, power, hackClass, cooldown) = hack let updatePlayer amount attack p = { p with Events = Array.append [| attack |] p.Events ; Bank = max (p.Bank + amount) 0 } let event isDefenderEvent = let hackEvent = { - HackId = item.Id + HackId = hack.Id Adversary = if isDefenderEvent then attacker.basicPlayer else defender.basicPlayer IsInstigator = not isDefenderEvent Success = successfulHack } { Type = Hacking hackEvent Timestamp = DateTime.UtcNow - Cooldown = if isDefenderEvent then int WeaponClass.SameTargetAttackCooldown.TotalMinutes * 1 else cooldown } + Cooldown = if isDefenderEvent then int WeaponClass.SameTargetAttackCooldown.TotalMinutes * 1 else hack.Cooldown } [ DbService.updatePlayer GuildEnvironment.pgDb <| updatePlayer prize (event false) attacker DbService.updatePlayer GuildEnvironment.pgDb <| updatePlayer -prize (event true) defender @@ -108,11 +102,10 @@ let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerDa let successfulHack (ctx : IDiscordContext) attacker defender (hack : HackItem) = async { - let (item,power,hackClass,cooldown) = hack - let prizeAmount = if power < int defender.Bank then power else int defender.Bank + let prizeAmount = if hack.Power < int defender.Bank then hack.Power else int defender.Bank do! updateCombatants true attacker defender hack (prizeAmount * 1) - let embed = Embeds.responseSuccessfulHack true defender.DiscordId prizeAmount item + let embed = Embeds.responseSuccessfulHack true defender.DiscordId prizeAmount hack do! ctx.FollowUp embed |> Async.AwaitTask let builder = Embeds.eventSuccessfulHack ctx defender prizeAmount @@ -123,9 +116,8 @@ let successfulHack (ctx : IDiscordContext) attacker defender (hack : HackItem) = } let failedHack (ctx : IDiscordContext) attacker defender (hack : HackItem) = - let (item, power, hackClass, cooldown) = hack async { - let lostAmount = if power < int attacker.Bank then power else int attacker.Bank + let lostAmount = if hack.Power < int attacker.Bank then hack.Power else int attacker.Bank let msg = $"Hack failed! {defender.Name} was able to mount a successful defense! You lost {lostAmount} $GBT!" do! sendFollowUpMessage ctx msg @@ -158,7 +150,8 @@ let handleAttack (ctx : IDiscordContext) = executePlayerAction ctx (fun attacker -> async { let tokens = ctx.GetInteractionId().Split("-") let hackId = int tokens.[1] - let item,_,_,_ as hackItem = Armory.getHackById hackId + let hack = Armory.battleItems |> Inventory.findHackById hackId + let hackAsItem = Inventory.hackToItem hack let resultId , targetId = UInt64.TryParse tokens.[2] let! resultTarget = DbService.tryFindPlayer GuildEnvironment.pgDb targetId @@ -167,21 +160,21 @@ let handleAttack (ctx : IDiscordContext) = do! attacker |> Player.removeExpiredActions |> checkAlreadyHackedTarget defender - >>= checkPlayerOwnsWeapon item - >>= checkWeaponHasCooldown item + >>= checkPlayerOwnsWeapon hackAsItem + >>= checkWeaponHasCooldown hackAsItem |> function | Ok atkr -> - runHackerBattle defender hackItem + runHackerBattle defender hack |> function - | false -> successfulHack ctx atkr defender hackItem - | true -> failedHack ctx attacker defender hackItem + | false -> successfulHack ctx atkr defender hack + | true -> failedHack ctx attacker defender hack | Error msg -> Messaging.sendFollowUpMessage ctx msg | _ -> do! Messaging.sendFollowUpMessage ctx "Error occurred processing attack" }) let defend (ctx : IDiscordContext) = executePlayerAction ctx (fun player -> async { - if Player.getShields player |> List.length > 0 then + if player.Inventory |> Inventory.filterByShields |> List.length > 0 then let p = Player.removeExpiredActions player let embed = Embeds.pickDefense "Defend" p false do! ctx.FollowUp embed |> Async.AwaitTask @@ -194,18 +187,19 @@ let handleDefense (ctx : IDiscordContext) = executePlayerAction ctx (fun player -> async { let tokens = ctx.GetInteractionId().Split("-") let shieldId = int tokens.[1] - let item, shieldClass, cooldown as shield = Armory.getShields |> List.find (fun (item,_,_) -> item.Id = shieldId) + let shield = Armory.battleItems |> Inventory.findShieldById shieldId + let shieldAsItem = Inventory.shieldToItem shield do! player - |> checkPlayerOwnsWeapon item + |> checkPlayerOwnsWeapon shieldAsItem >>= checkPlayerHasShieldSlotsAvailable - >>= checkWeaponHasCooldown item + >>= checkWeaponHasCooldown shieldAsItem |> handleResultWithResponse ctx (fun p -> async { let embed = Embeds.responseCreatedShield shield do! ctx.FollowUp embed |> Async.AwaitTask let defense = { Type = Shielding shieldId - Cooldown = cooldown + Cooldown = shield.Cooldown Timestamp = DateTime.UtcNow } do! DbService.updatePlayer GuildEnvironment.pgDb p diff --git a/Bot/Games/Store.fs b/Bot/Games/Store.fs index 26caf93..ce5dde8 100644 --- a/Bot/Games/Store.fs +++ b/Bot/Games/Store.fs @@ -1,5 +1,6 @@ module Degenz.Store +open System open System.Threading.Tasks open DSharpPlus.Entities open DSharpPlus @@ -9,6 +10,58 @@ open Degenz open Degenz.Messaging open Degenz.PlayerInteractions +let getBuyItemsEmbed (playerInventory : Inventory) (storeInventory : Inventory) = + let embeds , buttons = + storeInventory + |> List.map (fun item -> + let embed = DiscordEmbedBuilder() + match item.Type with + | Hack(power,_,cooldown) -> + embed.AddField($"$GBT Reward |", string power, true) + .AddField("Cooldown |", $"{TimeSpan.FromMinutes(int cooldown).Minutes} minutes", true) + .WithThumbnail(Embeds.getItemIcon item.Id) + |> ignore + | Shield(shieldClass,cooldown) -> + embed.AddField($"Strong against |", WeaponClass.getGoodAgainst shieldClass |> snd |> string, true) +// .AddField($"Defensive Strength |", string item.Power, true) + .AddField("Active For |", $"{TimeSpan.FromMinutes(int cooldown).Hours} hours", true) + .WithThumbnail(Embeds.getItemIcon item.Id) + |> ignore + | _ -> () + embed + .AddField("Price 💰", (if item.Price = 0 then "Free" else $"{item.Price} $GBT"), true) + .WithColor(WeaponClass.getClassEmbedColor item) + .WithTitle($"{item.Name}") + |> ignore + let button = + if playerInventory |> List.exists (fun i -> i.Id = item.Id) + then DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Buy-{item.Id}", $"Own {item.Name}", true) + else DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Buy-{item.Id}", $"Buy {item.Name}") + ( embed.Build() , button :> DiscordComponent )) + |> List.unzip + + DiscordFollowupMessageBuilder() + .AddEmbeds(embeds) + .AddComponents(buttons) + .AsEphemeral(true) + +let getSellEmbed (items : Item list) = + let embeds , buttons = + items + |> List.map (fun item -> + DiscordEmbedBuilder() + .AddField("Sell For 💰", $"{item.Price} $GBT", true) + .WithTitle($"{item.Name}") + .WithColor(WeaponClass.getClassEmbedColor item) + .Build() + , DiscordButtonComponent(WeaponClass.getClassButtonColor item, $"Sell-{id}", $"Sell {item.Name}") :> DiscordComponent) + |> List.unzip + + DiscordFollowupMessageBuilder() + .AddEmbeds(embeds) + .AddComponents(buttons) + .AsEphemeral(true) + let checkHasSufficientFunds (item : Item) player = if player.Bank - item.Price >= 0 then Ok player @@ -31,15 +84,15 @@ let checkHasItemsInArsenal itemType items player = let buy getItems (ctx : IDiscordContext) = executePlayerAction ctx (fun player -> async { - let itemStore = Embeds.getBuyItemsEmbed (getItems player.Inventory) (getItems Armory.battleItems) + let itemStore = getBuyItemsEmbed (getItems player.Inventory) (getItems Armory.battleItems) do! ctx.FollowUp itemStore |> Async.AwaitTask }) let sell itemType getItems (ctx : IDiscordContext) = executePlayerAction ctx (fun player -> async { - let items = getItems player + let items = getItems player.Inventory match checkHasItemsInArsenal itemType items player with - | Ok _ -> let itemStore = Embeds.getSellEmbed items + | Ok _ -> let itemStore = getSellEmbed items do! ctx.FollowUp(itemStore) |> Async.AwaitTask | Error e -> do! sendFollowUpMessage ctx e }) @@ -114,8 +167,8 @@ type Store() = member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy Armory.getShieldItems) [] - member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" Player.getHackItems) + member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" Inventory.filterByHacks) [] - member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" Player.getShieldItems) + member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" Inventory.filterByShields) diff --git a/Bot/Games/Trainer.fs b/Bot/Games/Trainer.fs index a0aa6c1..e9873d7 100644 --- a/Bot/Games/Trainer.fs +++ b/Bot/Games/Trainer.fs @@ -9,20 +9,20 @@ open Degenz.Messaging let trainerAchievement = "FINISHED_TRAINER" let Sensei = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" } -let defaultHackItem, hackPower, hackClass, hackCooldown as defaultHack = Armory.getHackById (int ItemId.Virus) -let defaultShieldItem, shieldClass, shieldCooldown as defaultShield = Armory.getShieldById (int ItemId.Firewall) +let defaultHack = Armory.battleItems |> Inventory.findHackById (int ItemId.Virus) +let defaultShield = Armory.battleItems |> Inventory.findShieldById (int ItemId.Firewall) let TrainerEvents = [| { Timestamp = System.DateTime.UtcNow - Cooldown = 2 + Cooldown = defaultHack.Cooldown Type = Hacking { Adversary = Sensei Success = true IsInstigator = true - HackId = defaultHackItem.Id } } + HackId = defaultHack.Id } } { Timestamp = System.DateTime.UtcNow - Cooldown = shieldCooldown - Type = Shielding defaultShieldItem.Id } + Cooldown = defaultShield.Cooldown + Type = Shielding defaultShield.Id } |] let sendInitialEmbed (client : DiscordClient) = @@ -52,7 +52,7 @@ let handleTrainerStep1 (ctx : IDiscordContext) = |> Async.AwaitTask let msg = "Beautopia© is a dangerous place... quick, put up a SHIELD 🛡 before another Degen hacks you, and takes your 💰$GBT.\n\n" + "To enable it, you need to run the `/shield` slash command.\n\n" - + $"Type the `/shield` command now, then select - `{defaultShieldItem.Name}`\n" + + $"Type the `/shield` command now, then select - `{defaultShield.Name}`\n" let builder = DiscordInteractionResponseBuilder() .WithContent(msg) @@ -66,7 +66,7 @@ let defend (ctx : IDiscordContext) = do! Messaging.defer ctx let m = ctx.GetDiscordMember() let name = if System.String.IsNullOrEmpty m.Nickname then m.DisplayName else m.Nickname - let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [ defaultShieldItem ] ; Name = name } true + let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [ Inventory.shieldToItem defaultShield ] ; Name = name } true do! ctx.FollowUp(embed) |> Async.AwaitTask } |> Async.StartAsTask :> Task @@ -88,11 +88,11 @@ let handleDefense (ctx : IDiscordContext) = let embed = Embeds.responseCreatedShield defaultShield do! ctx.FollowUp embed |> Async.AwaitTask do! Async.Sleep 4000 - do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{defaultHackItem.Name}**" + do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{defaultHack.Name}**" do! Async.Sleep 5000 do! sendMessage' $"❌ HACKING FAILED!\n\n{playerName} defended hack from <@{Sensei.Id}>!" do! Async.Sleep 4000 - do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHackItem.Name) + do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Name) } |> Async.StartAsTask :> Task let handleTrainerStep3 (ctx : IDiscordContext) = @@ -103,7 +103,7 @@ let handleTrainerStep3 (ctx : IDiscordContext) = .WithContent ( "Now let’s **HACK** 💻... I want you to **HACK ME**!\n\n" + "To **hack**, you need to run the `/hack` slash command.\n" - + $"Type the `/hack` command now, then choose me - <@{Sensei.Id}> as your target, and select `{defaultHackItem.Name}`") + + $"Type the `/hack` command now, then choose me - <@{Sensei.Id}> as your target, and select `{defaultHack.Name}`") do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } |> Async.StartAsTask :> Task @@ -115,8 +115,9 @@ let hack (target : DiscordUser) (ctx : IDiscordContext) = let isRightTarget = target.Id = Sensei.Id match isRightTarget with | true -> + let player = { PlayerData.empty with Inventory = [ Inventory.hackToItem defaultHack ] } let bot = { PlayerData.empty with DiscordId = Sensei.Id ; Name = Sensei.Name } - let embed = Embeds.pickHack "Trainer-4" { PlayerData.empty with Inventory = [ defaultHackItem ] } bot true + let embed = Embeds.pickHack "Trainer-4" player bot true do! ctx.FollowUp(embed) |> Async.AwaitTask | false -> @@ -132,7 +133,7 @@ let handleHack (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async { let sendMessage' = sendFollowUpMessage ctx do! Async.Sleep 1000 - let embed = Embeds.responseSuccessfulHack false Sensei.Id hackPower defaultHackItem + let embed = Embeds.responseSuccessfulHack false Sensei.Id defaultHack.Power defaultHack do! ctx.FollowUp(embed) |> Async.AwaitTask do! Async.Sleep 4000 do! sendMessage' @@ -150,7 +151,7 @@ let handleHack (ctx : IDiscordContext) = do! DbService.addAchievement GuildEnvironment.pgDb player.DiscordId trainerAchievement |> Async.Ignore - sb.Append($"I'm going to gift you a hack,`{defaultHackItem.Name}` and a shield, `{defaultShieldItem.Name}`") |> ignore + sb.Append($"I'm going to gift you a hack,`{defaultHack.Name}` and a shield, `{defaultShield.Name}`") |> ignore sb.Append(", you'll need em to survive\n\n") |> ignore sb.AppendLine("To finish your training and collect the loot, type the `/arsenal` command **NOW**") |> ignore do! Async.Sleep 1000 @@ -165,12 +166,15 @@ let handleHack (ctx : IDiscordContext) = let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async { - let hasStockWeapons = Player.getHackItems player |> List.exists (fun item -> item.Id = defaultHackItem.Id) + let hasStockWeapons = + player.Inventory + |> List.choose (fun item -> if item.Id = defaultHack.Id || item.Id = defaultShield.Id then Some item else None) + |> List.length > 0 let updatedPlayer = if not hasStockWeapons then { Player.removeExpiredActions player with Events = TrainerEvents |> Array.append player.Events - Inventory = defaultHackItem::defaultShieldItem::player.Inventory + Inventory = Inventory.hackToItem defaultHack::Inventory.shieldToItem defaultShield::player.Inventory } else Player.removeExpiredActions player @@ -187,7 +191,7 @@ let handleArsenal (ctx : IDiscordContext) = let! completed = DbService.checkHasAchievement GuildEnvironment.pgDb player.DiscordId trainerAchievement if not completed then do! Async.Sleep 3000 - let rewards = [ $"{defaultHackItem.Name} Hack" ; $"{defaultShieldItem.Name} Shield" ] + let rewards = [ $"{defaultHack.Name} Hack" ; $"{defaultShield.Name} Shield" ] let embed = Embeds.getAchievementEmbed rewards "You completed the Training Dojo and collected loot." trainerAchievement do! ctx.FollowUp(embed) |> Async.AwaitTask do! Async.Sleep 2000