module Degenz.Store open System.Threading.Tasks open DSharpPlus.Entities open DSharpPlus open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz open Degenz.Embeds open Degenz.Messaging let handleResultWithResponse ctx fn (player : Result) = match player with | Ok p -> fn p | Error e -> async { do! sendFollowUpMessageFromCtx ctx e } let handleResultWithResponseFromEvent event fn (player : Result) = match player with | Ok p -> fn p | Error e -> async { do! sendFollowUpMessage event e } let checkHasSufficientFunds (item : BattleItem) player = if player.Bank - item.Cost >= 0 then Ok player else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" let checkAlreadyOwnsItem (item : BattleItem) player = if player.Arsenal |> Array.exists (fun w -> item.Id = w.Id) then Error $"You already own {item.Name}!" else Ok player let checkSoldItemAlready (item : BattleItem) player = if player.Arsenal |> Array.exists (fun w -> item.Id = w.Id) then Ok player else Error $"{item.Name} not found in your arsenal! Looks like you sold it already." let checkHasItemsInArsenal itemType player = if player.Arsenal |> Array.filter (fun i -> i.Type = itemType ) |> Array.length > 0 then Ok player else Error $"You currently have no {itemType}s in your arsenal to sell!" let statusFormat p = $"**Hacks:** {Player.hacks p |> battleItemFormat}\n **Shields:** {Player.shields p |> battleItemFormat}\n **Hack Attacks:**\n{Player.attacks p |> actionFormat}\n **Active Shields:**\n{Player.defenses p |> actionFormat}" let arsenal (ctx : InteractionContext) = Game.executePlayerInteraction ctx (fun player -> async { let updatedPlayer = Player.removeExpiredActions player let builder = DiscordFollowupMessageBuilder() let embed = DiscordEmbedBuilder() embed.AddField("Arsenal", statusFormat updatedPlayer) |> ignore builder.AddEmbed(embed) |> ignore builder.IsEphemeral <- true do! ctx.FollowUpAsync(builder) |> Async.AwaitTask |> Async.Ignore do! DbService.updatePlayer updatedPlayer }) let buy itemType (ctx : InteractionContext) = Game.executePlayerInteraction ctx (fun player -> async { let itemStore = Embeds.getBuyItemsEmbed player itemType Armory.battleItems do! ctx.Interaction.CreateFollowupMessageAsync(itemStore) |> Async.AwaitTask |> Async.Ignore }) let sell itemType (ctx : InteractionContext) = Game.executePlayerInteraction ctx (fun player -> async { match checkHasItemsInArsenal itemType player with | Ok _ -> let itemStore = Embeds.getSellItemsEmbed itemType player do! ctx.FollowUpAsync(itemStore) |> Async.AwaitTask |> Async.Ignore | Error e -> do! sendFollowUpMessageFromCtx ctx e }) // TODO: When you buy a shield, prompt the user to activate it let handleBuyItem (event : ComponentInteractionCreateEventArgs) itemId = Game.executePlayerEvent event (fun player -> async { let item = Armory.battleItems |> Array.find (fun w -> w.Id = itemId) do! player |> checkHasSufficientFunds item >>= checkAlreadyOwnsItem item |> handleResultWithResponseFromEvent event (fun player -> async { let newBalance = player.Bank - item.Cost let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal } do! DbService.updatePlayer p do! sendFollowUpMessage event $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining" }) }) let handleSell (event : ComponentInteractionCreateEventArgs) itemId = Game.executePlayerEvent event (fun player -> async { let item = Armory.getItem itemId do! player |> checkSoldItemAlready item |> handleResultWithResponseFromEvent event (fun player -> async { let updatedPlayer = { player with Bank = player.Bank + item.Cost Arsenal = player.Arsenal |> Array.filter (fun i -> i.Id <> itemId) Actions = if item.Type = ItemType.Shield then player.Actions |> Array.filter (fun a -> a.ActionId <> itemId) else player.Actions } do! DbService.updatePlayer updatedPlayer do! sendFollowUpMessage event $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}" }) }) let handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = let itemId = int <| event.Id.Split("-").[1] match event.Id with | id when id.StartsWith("Buy") -> handleBuyItem event itemId | id when id.StartsWith("Sell") -> handleSell event itemId | _ -> task { let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- $"Incorrect Action identifier {event.Id}" do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } type Store() = inherit ApplicationCommandModule () let enforceChannel (ctx : InteractionContext) (storeFn : InteractionContext -> Task) = match ctx.Channel.Id with | id when id = GuildEnvironment.channelArmory -> storeFn ctx | _ -> task { let msg = $"You must go to <#{GuildEnvironment.channelArmory}> channel to buy/sell or check your arsenal" do! Messaging.sendSimpleResponse ctx msg } [] member this.Arsenal (ctx : InteractionContext) = arsenal ctx [] member _.BuyHack (ctx : InteractionContext) = enforceChannel ctx (buy ItemType.Hack) [] member this.BuyShield (ctx : InteractionContext) = enforceChannel ctx (buy ItemType.Shield) [] member this.SellHack (ctx : InteractionContext) = enforceChannel ctx (sell ItemType.Hack) [] member this.SellShield (ctx : InteractionContext) = enforceChannel ctx (sell ItemType.Shield)