241 lines
11 KiB
Forth
241 lines
11 KiB
Forth
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<GBT> 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-{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<GBT>
|
|
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
|
|
}
|
|
|
|
// [<SlashCommand("buy-item", "Purchase an item")>]
|
|
// member _.BuyItem (ctx : InteractionContext) = checkChannel (DiscordInteractionContext(ctx))
|
|
//
|
|
[<SlashCommand("buy-hack", "Purchase a hack so you can take money from other Degenz")>]
|
|
member _.BuyHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Hack))
|
|
|
|
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GBT")>]
|
|
member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Shield))
|
|
|
|
// [<SlashCommand("buy-food", "Purchase a food item to help boost your stats")>]
|
|
// member this.BuyFood (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Food))
|
|
//
|
|
[<SlashCommand("sell-hack", "Sell a hack for GoodBoyTokenz")>]
|
|
member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" (Inventory.getItemsByType ItemType.Hack))
|
|
|
|
[<SlashCommand("sell-shield", "Sell a shield for GoodBoyTokenz")>]
|
|
member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Shield))
|
|
|
|
[<SlashCommand("consume", "Consume a food item")>]
|
|
member this.Consume (ctx : InteractionContext) =
|
|
enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Food))
|
|
|
|
// [<SlashCommand("inventory", "Check your inventory")>]
|
|
// member this.Inventory (ctx : InteractionContext) =
|
|
// enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType))
|
|
|