From 07246dcdb20cde13f7e2e17cd56c921a5ee94fe0 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sun, 16 Jan 2022 22:33:28 +0700 Subject: [PATCH] Store: Buy Weapons and Shields, and selling --- DbService/DbService.fs | 14 +- HackerBattle/Commands.fs | 2 +- HackerBattle/{Program.fs => HackerBattle.fs} | 2 +- HackerBattle/HackerBattle.fsproj | 2 +- .../{Program.fs => PlayerInteractions.fs} | 0 PlayerInteractions/PlayerInteractions.fsproj | 2 +- Shared/Shared.fs | 7 +- Store/Program.fs | 112 ---------- Store/Store.fs | 198 ++++++++++++++++++ Store/Store.fsproj | 2 +- 10 files changed, 211 insertions(+), 130 deletions(-) rename HackerBattle/{Program.fs => HackerBattle.fs} (97%) rename PlayerInteractions/{Program.fs => PlayerInteractions.fs} (100%) delete mode 100644 Store/Program.fs create mode 100644 Store/Store.fs diff --git a/DbService/DbService.fs b/DbService/DbService.fs index 0f8fcc0..4ce1b28 100644 --- a/DbService/DbService.fs +++ b/DbService/DbService.fs @@ -10,8 +10,11 @@ type PlayerEntry = { Player : Player } -let mongo = MongoClient("mongodb://localhost:27017") -let db = mongo.GetDatabase("degenz-game") +//let connString = "mongodb://localhost:27017" +let connString = "mongodb+srv://joe:mtmu3XXe3crUfXdO@stage.2hyxh.mongodb.net/degenz?retryWrites=true&w=majority" + +let mongo = MongoClient(connString) +let db = mongo.GetDatabase("degenz") let players = db.GetCollection("players") let tryFindPlayer (id : uint64) : Async = @@ -38,13 +41,6 @@ let removePlayer (memberId : uint64) = |> Async.Ignore } -let updateAttacks (playerId : uint64) (attacks : Attack array) = - async { - let filter = Builders.Filter.Eq((fun e -> e.Player.DiscordId), playerId) - let update = Builders.Update.Set((fun e -> e.Player.Attacks), attacks) - return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore - } - let updatePlayer player = async { let filter = Builders.Filter.Eq((fun e -> e.Player.DiscordId), player.DiscordId) diff --git a/HackerBattle/Commands.fs b/HackerBattle/Commands.fs index ab6d2be..45bfc86 100644 --- a/HackerBattle/Commands.fs +++ b/HackerBattle/Commands.fs @@ -24,7 +24,7 @@ let attack (ctx : InteractionContext) (target : DiscordUser) = let updatedAttacks = attacker.Attacks |> removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp) - do! DbService.updateAttacks attacker.DiscordId updatedAttacks + do! DbService.updatePlayer <| { attacker with Attacks = updatedAttacks } if updatedAttacks.Length < 2 then let builder = DiscordInteractionResponseBuilder() builder.AddEmbed (constructEmbed "Pick the hack you wish to use.") |> ignore diff --git a/HackerBattle/Program.fs b/HackerBattle/HackerBattle.fs similarity index 97% rename from HackerBattle/Program.fs rename to HackerBattle/HackerBattle.fs index a994180..d74d836 100644 --- a/HackerBattle/Program.fs +++ b/HackerBattle/HackerBattle.fs @@ -15,7 +15,7 @@ type HackerGame() = member this.AttackCommand (ctx : InteractionContext, [] target : DiscordUser) = Commands.attack ctx target - [] + [] member this.DefendCommand (ctx : InteractionContext) = Commands.defend ctx let config = DiscordConfiguration() diff --git a/HackerBattle/HackerBattle.fsproj b/HackerBattle/HackerBattle.fsproj index 3390018..ef3cec8 100644 --- a/HackerBattle/HackerBattle.fsproj +++ b/HackerBattle/HackerBattle.fsproj @@ -15,7 +15,7 @@ - + diff --git a/PlayerInteractions/Program.fs b/PlayerInteractions/PlayerInteractions.fs similarity index 100% rename from PlayerInteractions/Program.fs rename to PlayerInteractions/PlayerInteractions.fs diff --git a/PlayerInteractions/PlayerInteractions.fsproj b/PlayerInteractions/PlayerInteractions.fsproj index 9db4136..32cf28f 100644 --- a/PlayerInteractions/PlayerInteractions.fsproj +++ b/PlayerInteractions/PlayerInteractions.fsproj @@ -7,7 +7,7 @@ PlayerRegistration - + diff --git a/Shared/Shared.fs b/Shared/Shared.fs index 3314d11..2b85a20 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -6,8 +6,8 @@ open DSharpPlus.Entities open DSharpPlus.SlashCommands type ItemType = - | Weapon = 0 - | Shield = 1 + | Weapon + | Shield type ActionClass = | Network @@ -40,7 +40,6 @@ type Item = { let weaponInventory = [| Weapon.Virus ; Weapon.Ransom ; Weapon.DDos ; Weapon.Worm ; Weapon.Crack ; Weapon.Injection |] let shieldInventory = [| Shield.Firewall ; Shield.PortScan ; Shield.Encryption ; Shield.Cypher ; Shield.Hardening ; Shield.Sanitation |] - let getClass = function | 0 @@ -91,7 +90,7 @@ let createSimpleResponseAsync msg (ctx: InteractionContext) = } let notYetAHackerMsg = - createSimpleResponseAsync "You are not currently a hacker, first use the /redpill command to become one" + createSimpleResponseAsync "You are currently not a hacker, first use the /redpill command to become one" let hackDescription = "" diff --git a/Store/Program.fs b/Store/Program.fs deleted file mode 100644 index 557ae5c..0000000 --- a/Store/Program.fs +++ /dev/null @@ -1,112 +0,0 @@ -open System -open System.Threading.Tasks -open DSharpPlus.Entities -open DSharpPlus -open DSharpPlus.EventArgs -open DSharpPlus.SlashCommands -open DegenzGame.Shared -open Emzi0767.Utilities -open Newtonsoft.Json - -module Commands = - let constructItemButtons playerInfo (items : 'a array) = - items - |> Seq.map (fun item -> DiscordButtonComponent(ButtonStyle.Primary, $"{playerInfo}-{item}", $"{item}")) - - let viewStore (ctx : InteractionContext) = - async { - let builder = DiscordInteractionResponseBuilder() - - try - let file = System.IO.File.ReadAllText("Items.json") - JsonConvert.DeserializeObject(file) - |> Array.groupBy (fun (i : Item) -> i.ItemType) - |> Array.iter (fun ( itemType , items ) -> - let itemList = items |> Array.map (fun i -> $"{i.Name} - {i.Cost} GBT") |> String.concat "\n" - builder.AddEmbed (constructEmbed $"{itemType}:\n{itemList}") |> ignore) - with _ -> builder.Content <- "System error preparing inventory for viewing" - - builder.AsEphemeral true |> ignore - - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - } |> Async.StartAsTask - :> Task - - let buyHack (ctx : InteractionContext) hackId = - async { - return () - } |> Async.StartAsTask - :> Task - - let buyShield (ctx : InteractionContext) shieldId = - async { - return () - } |> Async.StartAsTask - :> Task - - let sellItem (ctx : InteractionContext) = - async { - return () - } |> Async.StartAsTask - :> Task - -type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule () - -type Store() = - inherit ApplicationCommandModule () - - [] - member _.ViewStore (ctx : InteractionContext) = Commands.viewStore ctx - - [] - member _.BuyHack (ctx : InteractionContext, [] hackId : Weapon) = - Commands.buyHack ctx hackId - - [] - member this.BuyShield (ctx : InteractionContext, [] shieldId : Shield) = - Commands.buyShield ctx shieldId - - [] - member this.SellItem (ctx : InteractionContext) = - Commands.sellItem ctx - -let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = - async { - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - builder.Content <- $"" - do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - } |> Async.StartAsTask - :> Task - -let config = DiscordConfiguration() -config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk" -config.TokenType <- TokenType.Bot -config.Intents <- DiscordIntents.All -//config.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace - -let client = new DiscordClient(config) - -client.add_ComponentInteractionCreated(AsyncEventHandler(handleButtonEvent)) - -let slash = client.UseSlashCommands() - -// My server -slash.RegisterCommands(922419263275425832uL); -// Degenz -//slash.RegisterCommands(922414052708327494uL); - -client.ConnectAsync () -|> Async.AwaitTask -|> Async.RunSynchronously - -Task.Delay(-1) -|> Async.AwaitTask -|> Async.RunSynchronously - -client.DisconnectAsync () -|> Async.AwaitTask -|> Async.RunSynchronously - diff --git a/Store/Store.fs b/Store/Store.fs new file mode 100644 index 0000000..45c7dda --- /dev/null +++ b/Store/Store.fs @@ -0,0 +1,198 @@ +open System +open System.Threading.Tasks +open DSharpPlus.Entities +open DSharpPlus +open DSharpPlus.EventArgs +open DSharpPlus.SlashCommands +open DegenzGame +open DegenzGame.Shared +open Emzi0767.Utilities +open Newtonsoft.Json + +let store = + try + let file = System.IO.File.ReadAllText("Items.json") + JsonConvert.DeserializeObject(file) + |> Array.groupBy (fun (i : Item) -> i.ItemType) + with _ -> [||] + +let storeListing = + store + |> Array.map (fun ( itemType , items ) -> + let itemList = items |> Array.map (fun i -> $"{i.Name} - {i.Cost} GBT") |> String.concat "\n" + (constructEmbed $"{itemType}:\n{itemList}")) + +module Commands = + let viewStore (ctx : InteractionContext) = + async { + let builder = DiscordInteractionResponseBuilder() + + builder.AddEmbeds(storeListing) + .AsEphemeral(true) + |> ignore + + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + } |> Async.StartAsTask + :> Task + + let getItems itemType = store |> Array.find (fun ( t , _ ) -> t = itemType) |> snd + + let buyHack (ctx : InteractionContext) hackId = + async { + let! playerResult = DbService.tryFindPlayer ctx.Member.Id + let weapons = getItems ItemType.Weapon + let weaponResult = weapons |> Array.tryFind (fun w -> w.Name = string hackId) + return! + match playerResult , weaponResult with + | Some player , Some item -> + async { + let newBalance = player.Bank - item.Cost + if newBalance >= 0.0f then + let playerHasItem = player.Weapons |> Array.exists (fun w -> item.Name = string w) + if not playerHasItem then + let weapon = weaponInventory |> Array.find (fun w -> item.Name = string w) + let p = { player with Bank = newBalance ; Weapons = Array.append [| weapon |] player.Weapons } + do! DbService.updatePlayer p + do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx + else + do! createSimpleResponseAsync $"You already own this item!" ctx + else + do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx + } + | None , _ -> notYetAHackerMsg ctx + | _ -> createSimpleResponseAsync "Something is wrong" ctx + } |> Async.StartAsTask + :> Task + + let buyShield (ctx : InteractionContext) shieldId = + async { + let! playerResult = DbService.tryFindPlayer ctx.Member.Id + let shieldResult = + getItems ItemType.Shield + |> Array.tryFind (fun w -> w.Name = string shieldId) + return! + match playerResult , shieldResult with + | Some player , Some item -> + async { + let newBalance = player.Bank - item.Cost + if newBalance >= 0.0f then + let playerHasItem = player.Shields |> Array.exists (fun w -> item.Name = string w) + if not playerHasItem then + let shield = shieldInventory |> Array.find (fun w -> item.Name = string w) + let p = { player with Bank = newBalance ; Shields = Array.append [| shield |] player.Shields } + do! DbService.updatePlayer p + do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx + else + do! createSimpleResponseAsync $"You already own this item!" ctx + else + do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx + } + | None , _ -> notYetAHackerMsg ctx + | _ -> createSimpleResponseAsync "Something is wrong" ctx + } |> Async.StartAsTask + :> Task + + let constructItemButtons playerInfo itemType (items : 'a array) = + items + |> Seq.map (fun item -> DiscordButtonComponent(ButtonStyle.Primary, $"{playerInfo}-{itemType}-{item}", $"{item}")) + + let sellItem (ctx : InteractionContext) = + async { + let! playerResult = DbService.tryFindPlayer ctx.Member.Id + match playerResult with + | Some player -> + let hasInventoryToSell = Array.length player.Weapons + Array.length player.Shields > 0 + if hasInventoryToSell then + let builder = DiscordInteractionResponseBuilder() + builder.AddEmbed (constructEmbed "Pick the item you wish to sell.") |> ignore + + Array.chunkBySize 3 player.Weapons + |> Array.iter + (fun wps -> + wps + |> Array.map (fun w -> + DiscordButtonComponent(ButtonStyle.Primary, $"{player.DiscordId}-Weapon-{w}", $"{w}")) + |> Seq.cast + |> builder.AddComponents + |> ignore) + Array.chunkBySize 3 player.Shields + |> Array.iter + (fun shs -> + shs + |> Array.map (fun s -> + DiscordButtonComponent(ButtonStyle.Primary, $"{player.DiscordId}-Shield-{s}", $"{s}")) + |> Seq.cast + |> builder.AddComponents + |> ignore) + + builder.AsEphemeral true |> ignore + + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + else + do! createSimpleResponseAsync "You currently have no inventory to sell" ctx + | None -> do! notYetAHackerMsg ctx + return () + } |> Async.StartAsTask + :> Task + +let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = + async { + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- $"{event.Id}" + do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + } |> Async.StartAsTask + :> Task + +type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule () + +type Store() = + inherit ApplicationCommandModule () + + [] + member _.ViewStore (ctx : InteractionContext) = Commands.viewStore ctx + + [] + member _.BuyHack (ctx : InteractionContext, [] hackId : Weapon) = + Commands.buyHack ctx hackId + + [] + member this.BuyShield (ctx : InteractionContext, [] shieldId : Shield) = + Commands.buyShield ctx shieldId + + [] + member this.SellItem (ctx : InteractionContext) = + Commands.sellItem ctx + +let config = DiscordConfiguration() +config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk" +config.TokenType <- TokenType.Bot +config.Intents <- DiscordIntents.All +//config.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace + +let client = new DiscordClient(config) + +client.add_ComponentInteractionCreated(AsyncEventHandler(handleSellButtonEvents)) + +let slash = client.UseSlashCommands() + +// My server +slash.RegisterCommands(922419263275425832uL); +// Degenz +//slash.RegisterCommands(922414052708327494uL); + +client.ConnectAsync () +|> Async.AwaitTask +|> Async.RunSynchronously + +Task.Delay(-1) +|> Async.AwaitTask +|> Async.RunSynchronously + +client.DisconnectAsync () +|> Async.AwaitTask +|> Async.RunSynchronously + diff --git a/Store/Store.fsproj b/Store/Store.fsproj index f5f12f6..16412e7 100644 --- a/Store/Store.fsproj +++ b/Store/Store.fsproj @@ -6,7 +6,7 @@ Linux - +