module Degenz.Store open System open System.Threading.Tasks open DSharpPlus.Entities open DSharpPlus open DSharpPlus.EventArgs open DSharpPlus.SlashCommands 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 with | Hack hack -> embed.AddField($"$GBT Reward |", string hack.Power, true) .AddField("Cooldown |", $"{TimeSpan.FromMinutes(int hack.Cooldown).Minutes} minutes", true) .WithThumbnail(Embeds.getItemIcon item.Id) |> ignore | Shield shield -> embed.AddField($"Strong against |", WeaponClass.getGoodAgainst shield.Class |> snd |> string, true) // .AddField($"Defensive Strength |", string item.Power, true) .AddField("Active For |", $"{TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours", true) .WithThumbnail(Embeds.getItemIcon item.Id) |> ignore | Food food -> embed.AddField($"Stat |", $"{food.TargetStat}", true) .AddField($"Amount |", $"+{food.BoostAmount}", true) |> ignore | Accessory accessory -> embed.AddField($"Stat |", $"{accessory.TargetStat}", true) |> ignore if accessory.FloorBoost > 0 then embed.AddField($"Min Boost |", $"+{accessory.FloorBoost}", true) |> ignore if accessory.CeilBoost > 0 then embed.AddField($"Max Boost |", $"+{accessory.CeilBoost}", true) |> 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 : ItemDetails 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-{item.Id}", $"Sell {item.Name}") :> DiscordComponent) |> List.unzip DiscordFollowupMessageBuilder() .AddEmbeds(embeds) .AddComponents(buttons) .AsEphemeral(true) let getConsumeEmbed (items : ItemDetails list) = let embeds , buttons = items |> List.groupBy (fun item -> item.Id) |> List.map (fun (itemId , items ) -> let item = List.head items let foodItem = Inventory.findFoodById itemId items DiscordEmbedBuilder() .AddField($"{foodItem.Item.Name}", $"Total {items.Length}\nBoosts {foodItem.TargetStat} +{foodItem.BoostAmount}", true) .WithTitle($"Food Items") .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 else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" let checkAlreadyOwnsItem (item : Item) player = if player.Inventory |> List.exists (fun w -> item.Id = w.Id) then Error $"You already own {item.Name}!" else Ok player let checkSoldItemAlready item player = if player.Inventory |> List.exists (fun i -> item.Id = i.Id) then Ok player else Error $"{item.Name} not found in your inventory! Looks like you sold it already." let checkHasItemsInArsenal itemType items player = if List.isEmpty items |> not then Ok player else Error $"You currently have no {itemType} in your arsenal to sell!" let buy getItems (ctx : IDiscordContext) = executePlayerAction ctx (fun player -> async { let itemStore = getBuyItemsEmbed (getItems player.Inventory) (getItems Armory.weapons) do! ctx.FollowUp itemStore |> Async.AwaitTask }) let sell itemType getItems (ctx : IDiscordContext) = executePlayerAction ctx (fun player -> async { let items = getItems player.Inventory match checkHasItemsInArsenal itemType items player with | Ok _ -> let itemStore = getSellEmbed items do! ctx.FollowUp(itemStore) |> Async.AwaitTask | Error e -> do! sendFollowUpMessage ctx e }) // TODO: When you buy a shield, prompt the user to activate it let handleBuyItem (ctx : IDiscordContext) itemId = executePlayerAction ctx (fun player -> async { let item = Armory.weapons |> Inventory.findItemById itemId do! player |> checkHasSufficientFunds item.getItem >>= checkAlreadyOwnsItem item.getItem |> handleResultWithResponse ctx (fun player -> async { let newBalance = player.Bank - item.Price let p = { player with Bank = newBalance ; Inventory = item::player.Inventory } do! DbService.updatePlayer p |> Async.Ignore do! sendFollowUpMessage ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining" }) }) let handleSell (ctx : IDiscordContext) itemId = executePlayerAction ctx (fun player -> async { let item = Armory.weapons |> Inventory.findItemById itemId do! player |> checkSoldItemAlready item.getItem |> handleResultWithResponse ctx (fun player -> async { let updatedPlayer = { player with Bank = player.Bank + item.Price Inventory = player.Inventory |> List.filter (fun i -> i.Id <> itemId) } do! [ DbService.updatePlayer updatedPlayer |> Async.Ignore DbService.removeShieldEvent updatedPlayer.DiscordId itemId |> Async.Ignore sendFollowUpMessage ctx $"Sold {item.Name} for {item.Price}! Current Balance: {updatedPlayer.Bank}" ] |> Async.Parallel |> Async.Ignore }) }) //let inventory (ctx : IDiscordContext) = // executePlayerAction ctx (fun player -> async { // player.Inventory // |> List.groupBy (fun item -> item.Details) // }) // // //let consume (ctx : IDiscordContext) = // executePlayerAction ctx (fun player -> async { // // }) let handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = let ctx = DiscordEventContext event :> IDiscordContext let id = ctx.GetInteractionId() let itemId = int <| id.Split("-").[1] match id with | id when id.StartsWith("Buy") -> handleBuyItem ctx itemId | id when id.StartsWith("Sell") -> handleSell ctx itemId | _ -> task { let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- $"Incorrect Action identifier {id}" do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } type Store() = inherit ApplicationCommandModule () let enforceChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) = match ctx.GetChannel().Id with | id when id = GuildEnvironment.channelArmory -> storeFn ctx | _ -> task { let msg = $"You must go to <#{GuildEnvironment.channelArmory}> channel to buy or sell weapons" do! Messaging.sendSimpleResponse ctx msg } let checkChannel (ctx : IDiscordContext) = match ctx.GetChannel().Id with // | id when id = GuildEnvironment.channelBackAlley -> buy (Inventory.getItemsByType ItemType.Hack) ctx | id when id = GuildEnvironment.channelArmory -> buy (Inventory.getItemsByType ItemType.Shield) ctx // | id when id = GuildEnvironment.channelMarket -> buy (Inventory.getItemsByType ItemType.Food) ctx // | id when id = GuildEnvironment.channelAccessoryShop -> buy (Inventory.getItemsByType ItemType.Accessory) ctx | _ -> task { let msg = $"This channel doesn't have any items to sell" do! Messaging.sendSimpleResponse ctx msg } // [] // member _.BuyItem (ctx : InteractionContext) = checkChannel (DiscordInteractionContext(ctx)) // [] member _.BuyHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Hack)) [] member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Shield)) // [] // member this.BuyFood (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Food)) // [] member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" (Inventory.getItemsByType ItemType.Hack)) [] member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Shield)) [] member this.Consume (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Food)) // [] // member this.Inventory (ctx : InteractionContext) = // enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType))