From ef3d5c58f5b2387dc125b18c78cba714a5f96ba8 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Tue, 1 Feb 2022 19:54:52 +0700 Subject: [PATCH] Move some stuff around --- Bot/Bot.fs | 1 - Bot/Bot.fsproj | 2 +- Bot/Embeds.fs | 6 +- Bot/Game.fs | 15 ++++ Bot/GameConfig.fs | 1 - Bot/HackerBattle.fs | 61 ++++++------- Bot/PlayerInteractions.fs | 31 +++---- Bot/SlotMachine.fs | 94 +++++++++---------- Bot/Store.fs | 118 +++++++++++------------- Bot/Trainer.fs | 36 ++++---- DbService/DbService.fs | 6 +- Shared/Shared.fs | 185 ++++++++++++++++++++------------------ 12 files changed, 270 insertions(+), 286 deletions(-) create mode 100644 Bot/Game.fs delete mode 100644 Bot/GameConfig.fs diff --git a/Bot/Bot.fs b/Bot/Bot.fs index d328966..8efcaf0 100644 --- a/Bot/Bot.fs +++ b/Bot/Bot.fs @@ -1,6 +1,5 @@ module Degenz.Bot -open System open System.Threading.Tasks open DSharpPlus open DSharpPlus.SlashCommands diff --git a/Bot/Bot.fsproj b/Bot/Bot.fsproj index 81c8a2a..c3c069f 100644 --- a/Bot/Bot.fsproj +++ b/Bot/Bot.fsproj @@ -11,8 +11,8 @@ PreserveNewest - + diff --git a/Bot/Embeds.fs b/Bot/Embeds.fs index 2cf51a4..abbc6ee 100644 --- a/Bot/Embeds.fs +++ b/Bot/Embeds.fs @@ -1,7 +1,7 @@ module Degenz.Embeds open DSharpPlus.EventArgs -open Degenz.Shared +open Degenz.Types open DSharpPlus.Entities open AsciiTableFormatter @@ -34,7 +34,7 @@ let constructEmbed message = let pickDefense actionId player = let buttons = - constructButtons actionId (string player.DiscordId) (Player.shields player) + Messaging.constructButtons actionId (string player.DiscordId) (Player.shields player) |> Seq.cast let embed = @@ -50,7 +50,7 @@ let pickDefense actionId player = let pickHack actionId attacker defender = let buttons = - constructButtons actionId $"{defender.DiscordId}-{defender.Name}" (Player.hacks attacker) + Messaging.constructButtons actionId $"{defender.DiscordId}-{defender.Name}" (Player.hacks attacker) |> Seq.cast let embed = diff --git a/Bot/Game.fs b/Bot/Game.fs new file mode 100644 index 0000000..76ea0b9 --- /dev/null +++ b/Bot/Game.fs @@ -0,0 +1,15 @@ +module Degenz.Game + +open System.Threading.Tasks +open DSharpPlus.SlashCommands +open Degenz.DbService + +let executePlayerAction (ctx : InteractionContext) (dispatch : PlayerData -> Async) = + async { + let! playerResult = tryFindPlayer ctx.Member.Id + match playerResult with + | Some player -> do! dispatch player + | None -> do! Messaging.sendSimpleResponse ctx "You are currently not a hacker, first use the /redpill command to become one" + } |> Async.StartAsTask + :> Task + diff --git a/Bot/GameConfig.fs b/Bot/GameConfig.fs deleted file mode 100644 index a4d0894..0000000 --- a/Bot/GameConfig.fs +++ /dev/null @@ -1 +0,0 @@ -module Degenz.GameConfig diff --git a/Bot/HackerBattle.fs b/Bot/HackerBattle.fs index d4ae65d..0c27523 100644 --- a/Bot/HackerBattle.fs +++ b/Bot/HackerBattle.fs @@ -7,8 +7,7 @@ open DSharpPlus.Entities open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz -open Degenz.Shared -open Degenz.Store +open Degenz.Messaging let getTimeTillCooldownFinishes (timespan : TimeSpan) timestamp = let timeRemaining = timespan - (DateTime.UtcNow - timestamp) @@ -26,8 +25,8 @@ let checkIfPlayerIsAttackingThemselves defender attacker = let checkForExistingHack defenderId attacker = attacker.Actions - |> removeExpiredActions - |> getAttacksFlat + |> Player.removeExpiredActions + |> Player.getAttacksFlat |> Array.tryFind (fun (_,t,_) -> t.Id = defenderId) |> function | Some ( atk , target , _ ) -> @@ -47,7 +46,7 @@ let checkIfHackHasCooldown hackId attacker = Ok attacker else let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(5)) mostRecentHackAttack - let item = armoury |> Array.find (fun i -> i.Id = hackId) + let item = Armoury.battleItems |> Array.find (fun i -> i.Id = hackId) Error $"{item.Name} is currently on cooldown, wait {cooldown} to use it again." let checkIfInventoryIsEmpty attacker = @@ -62,8 +61,8 @@ let calculateDamage (hack : BattleItem) (shield : BattleItem) = let runHackerBattle defender hack = Player.defenses defender - |> removeExpiredActions - |> Array.map (fun dfn -> armoury |> Array.find (fun w -> w.Id = dfn.ActionId)) + |> Player.removeExpiredActions + |> Array.map (fun dfn -> Armoury.battleItems |> Array.find (fun w -> w.Id = dfn.ActionId)) |> Array.map (calculateDamage (hack)) |> Array.contains Weak @@ -74,7 +73,7 @@ let updateCombatants attacker defender hack prize = let attack = { ActionId = int hack ; Type = Attack { Target = target ; Result = prize > 0 } ; Timestamp = DateTime.UtcNow } [ DbService.updatePlayer <| updatePlayer prize attack attacker - DbService.updatePlayer <| modifyPlayerBank defender -prize ] + DbService.updatePlayer <| Player.modifyBank defender -prize ] |> Async.Parallel |> Async.Ignore @@ -115,11 +114,10 @@ let failedHack (event : ComponentInteractionCreateEventArgs) attacker defender h } let attack (ctx : InteractionContext) (target : DiscordUser) = - async { - let! attacker = DbService.tryFindPlayer ctx.Member.Id + Game.executePlayerAction ctx (fun attacker -> async { let! defender = DbService.tryFindPlayer target.Id - match attacker , defender with - | Some attacker , Some defender -> + match defender with + | Some defender -> let hackAttempt = checkForExistingHack defender.DiscordId attacker |> Result.bind checkIfInventoryIsEmpty @@ -136,10 +134,8 @@ let attack (ctx : InteractionContext) (target : DiscordUser) = .AsEphemeral(true) do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask - | None , _ -> do! notYetAHackerMsg ctx - | _ , None -> do! createSimpleResponseAsync "Your target is not connected to the network, they must join first by using the /redpill command" ctx - } |> Async.StartAsTask - :> Task + | None -> do! sendSimpleResponse ctx "Your target is not connected to the network, they must join first by using the /redpill command" + }) let handleAttack (event : ComponentInteractionCreateEventArgs) = async { @@ -155,7 +151,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = |> Result.bind (checkIfHackHasCooldown (int hack)) |> function | Ok _ -> - runHackerBattle defender (getItemFromArmoury <| int hack) + runHackerBattle defender (Armoury.getItem (int hack)) |> function | false -> successfulHack event attacker defender hack | true -> failedHack event attacker defender hack @@ -175,23 +171,18 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = } let defend (ctx : InteractionContext) = - async { - let! player = DbService.tryFindPlayer ctx.Member.Id - match player with - | Some player -> - if Player.shields player |> Array.length > 0 then - let embed = Embeds.pickDefense "Defend" player - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) - |> Async.AwaitTask - else - let builder = DiscordInteractionResponseBuilder() - builder.Content <- $"You currently do not have any Shields to protect your system. Please go to the armoury and purchase one." - builder.AsEphemeral true |> ignore - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - | None -> do! notYetAHackerMsg ctx - } |> Async.StartAsTask - :> Task + Game.executePlayerAction ctx (fun player -> async { + if Player.shields player |> Array.length > 0 then + let embed = Embeds.pickDefense "Defend" player + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) + |> Async.AwaitTask + else + let builder = DiscordInteractionResponseBuilder() + builder.Content <- $"You currently do not have any Shields to protect your system. Please go to the armoury and purchase one." + builder.AsEphemeral true |> ignore + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + }) let handleDefense (event : ComponentInteractionCreateEventArgs) = async { @@ -200,7 +191,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) = let! playerResult = DbService.tryFindPlayer event.User.Id match playerResult with | Some player -> - let updatedDefenses = Player.defenses player |> removeExpiredActions + let updatedDefenses = Player.defenses player |> Player.removeExpiredActions let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = int shield) match alreadyUsedShield , updatedDefenses.Length < 2 with diff --git a/Bot/PlayerInteractions.fs b/Bot/PlayerInteractions.fs index 548b121..6592805 100644 --- a/Bot/PlayerInteractions.fs +++ b/Bot/PlayerInteractions.fs @@ -5,15 +5,15 @@ open DSharpPlus.Entities open DSharpPlus open DSharpPlus.SlashCommands open Degenz.Store -open Degenz.Shared +open Degenz.Types module Commands = let newPlayer nickname (membr : uint64) = let rand = System.Random(System.Guid.NewGuid().GetHashCode()) let randHack = rand.Next(0, 3) let randShield = rand.Next(6, 9) - let hack = armoury |> Array.find (fun i -> i.Id = randHack) - let shield = armoury |> Array.find (fun i -> i.Id = randShield) + let hack = Armoury.battleItems |> Array.find (fun i -> i.Id = randHack) + let shield = Armoury.battleItems |> Array.find (fun i -> i.Id = randShield) { DiscordId = membr Name = nickname @@ -72,21 +72,16 @@ module Commands = // :> Task let status (ctx : InteractionContext) = - async { - let! maybePlayer = DbService.tryFindPlayer ctx.Member.Id - match maybePlayer with - | Some player -> - let updatedActions = removeExpiredActions player.Actions - let updatedPlayer = { player with Actions = updatedActions } - let builder = DiscordInteractionResponseBuilder() - builder.IsEphemeral <- true - builder.Content <- statusFormat updatedPlayer - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask - do! DbService.updatePlayer updatedPlayer - | None -> do! notYetAHackerMsg ctx - } |> Async.StartAsTask - :> Task + Game.executePlayerAction ctx (fun player -> async { + let updatedActions = Player.removeExpiredActions player.Actions + let updatedPlayer = { player with Actions = updatedActions } + let builder = DiscordInteractionResponseBuilder() + builder.IsEphemeral <- true + builder.Content <- Messaging.statusFormat updatedPlayer + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + do! DbService.updatePlayer updatedPlayer + }) type PlayerInteractions() = inherit ApplicationCommandModule () diff --git a/Bot/SlotMachine.fs b/Bot/SlotMachine.fs index 153bc14..a46b4de 100644 --- a/Bot/SlotMachine.fs +++ b/Bot/SlotMachine.fs @@ -5,7 +5,7 @@ open System.Threading.Tasks open DSharpPlus open DSharpPlus.Entities open DSharpPlus.SlashCommands -open Degenz.Shared +open Degenz.Types let slots = [| "https://i.ibb.co/pKqZdr7/cherry.png" ; "https://i.ibb.co/JnghQsL/lemon.jpg" ; "https://i.ibb.co/1JTFPSs/seven.png" |] @@ -13,68 +13,62 @@ type SlotMachine() = inherit ApplicationCommandModule () [] - member this.AttackCommand (ctx : InteractionContext) = - async { - let! playerResult = DbService.tryFindPlayer ctx.Member.Id - match playerResult with - | Some player -> - let sleepTime = 1000 - let random = Random(System.Guid.NewGuid().GetHashCode()) - let results = [ random.Next(0, 3) ; random.Next(0, 3) ; random.Next(0, 3)] + member this.Spin (ctx : InteractionContext) = + Game.executePlayerAction ctx (fun player -> async { + let sleepTime = 1000 + let random = Random(System.Guid.NewGuid().GetHashCode()) + let results = [ random.Next(0, 3) ; random.Next(0, 3) ; random.Next(0, 3)] - let winConditions = (results.[0] = results.[1] && results.[0] = results.[2]) - || (results.[0] <> results.[1] && results.[1] <> results.[2] && results.[0] <> results.[2]) + let winConditions = (results.[0] = results.[1] && results.[0] = results.[2]) + || (results.[0] <> results.[1] && results.[1] <> results.[2] && results.[0] <> results.[2]) - if winConditions then - do! DbService.updatePlayer { player with Bank = player.Bank + 10 } - else - do! DbService.updatePlayer { player with Bank = max (player.Bank - 1) 0 } + if winConditions then + do! DbService.updatePlayer { player with Bank = player.Bank + 10 } + else + do! DbService.updatePlayer { player with Bank = max (player.Bank - 1) 0 } - do! ctx.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource) - |> Async.AwaitTask + do! ctx.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource) + |> Async.AwaitTask + do! Async.Sleep sleepTime + let builder = DiscordFollowupMessageBuilder() + let embed = DiscordEmbedBuilder() + embed.ImageUrl <- slots.[results.[0]] + builder.AddEmbed(embed.Build()) |> ignore + do! ctx.Interaction.CreateFollowupMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + + do! Async.Sleep sleepTime + let builder = DiscordFollowupMessageBuilder() + embed.ImageUrl <- slots.[results.[1]] + builder.AddEmbed(embed.Build()) |> ignore + do! ctx.Interaction.CreateFollowupMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + + do! Async.Sleep sleepTime + let builder = DiscordFollowupMessageBuilder() + embed.ImageUrl <- slots.[results.[2]] + builder.AddEmbed(embed.Build()) |> ignore + do! ctx.Interaction.CreateFollowupMessageAsync(builder) + |> Async.AwaitTask + |> Async.Ignore + + if winConditions then do! Async.Sleep sleepTime let builder = DiscordFollowupMessageBuilder() - let embed = DiscordEmbedBuilder() - embed.ImageUrl <- slots.[results.[0]] - builder.AddEmbed(embed.Build()) |> ignore + builder.Content <- "You win 10 GBT!" do! ctx.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore - + else do! Async.Sleep sleepTime let builder = DiscordFollowupMessageBuilder() - embed.ImageUrl <- slots.[results.[1]] - builder.AddEmbed(embed.Build()) |> ignore + builder.Content <- "You lose 0.5 GBT! Try your luck again!" do! ctx.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore + }) - do! Async.Sleep sleepTime - let builder = DiscordFollowupMessageBuilder() - embed.ImageUrl <- slots.[results.[2]] - builder.AddEmbed(embed.Build()) |> ignore - do! ctx.Interaction.CreateFollowupMessageAsync(builder) - |> Async.AwaitTask - |> Async.Ignore - - if winConditions then - do! Async.Sleep sleepTime - let builder = DiscordFollowupMessageBuilder() - builder.Content <- "You win 10 GBT!" - do! ctx.Interaction.CreateFollowupMessageAsync(builder) - |> Async.AwaitTask - |> Async.Ignore - else - do! Async.Sleep sleepTime - let builder = DiscordFollowupMessageBuilder() - builder.Content <- "You lose 0.5 GBT! Try your luck again!" - do! ctx.Interaction.CreateFollowupMessageAsync(builder) - |> Async.AwaitTask - |> Async.Ignore - - | None -> do! notYetAHackerMsg ctx - - } |> Async.StartAsTask - :> Task diff --git a/Bot/Store.fs b/Bot/Store.fs index 2d7faae..21c2ccf 100644 --- a/Bot/Store.fs +++ b/Bot/Store.fs @@ -8,83 +8,51 @@ open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz open Degenz.Embeds -open Degenz.Shared -open Newtonsoft.Json - -let getItemFromArmoury id = armoury |> Array.find (fun w -> w.Id = id) - -let removeExpiredActions actions = - actions - |> Array.filter (fun (act : Action) -> - let item = armoury |> Array.find (fun w -> w.Id = act.ActionId) - DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown)) +open Degenz.Messaging let viewStore (ctx : InteractionContext) = async { - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing armoury) + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing Armoury.battleItems) |> Async.AwaitTask } |> Async.StartAsTask :> Task -let buyItem (ctx : InteractionContext) itemId = - async { - let! playerResult = DbService.tryFindPlayer ctx.Member.Id - let item = armoury |> Array.find (fun w -> w.Id = itemId) - match playerResult with - | Some player -> - let newBalance = player.Bank - item.Cost - if newBalance >= 0 then - let playerHasItem = player.Arsenal |> Array.exists (fun w -> item.Id = w.Id) - if not playerHasItem then - let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal } - 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 -> do! notYetAHackerMsg ctx - } |> Async.StartAsTask - :> Task +let buyItem (ctx : InteractionContext) itemType = + Game.executePlayerAction ctx (fun player -> async { + let embed = DiscordEmbedBuilder() -let constructItemButtons playerInfo itemType (items : 'a array) = - items - |> Seq.map (fun item -> DiscordButtonComponent(ButtonStyle.Primary, $"{playerInfo}-{itemType}-{item}", $"{item}")) +// embed.Fields + + do! ctx.CreateResponseAsync(embed, true) + |> Async.AwaitTask + }) let sell (ctx : InteractionContext) = - async { - let! playerResult = DbService.tryFindPlayer ctx.Member.Id - match playerResult with - | Some player -> - let hasInventoryToSell = Array.length player.Arsenal > 0 - if hasInventoryToSell then - let builder = DiscordInteractionResponseBuilder() - builder.AddEmbed (constructEmbed "Pick the item you wish to sell.") |> ignore + Game.executePlayerAction ctx (fun player -> async { + let hasInventoryToSell = Array.length player.Arsenal > 0 + if hasInventoryToSell then + let builder = DiscordInteractionResponseBuilder() + builder.AddEmbed (constructEmbed "Pick the item you wish to sell.") |> ignore - Array.chunkBySize 5 player.Arsenal - |> Array.iter - (fun wps -> - wps - |> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Primary, $"{w.Type}-{w.Id}", $"{w.Name}")) - |> Seq.cast - |> builder.AddComponents - |> ignore) - builder.AsEphemeral true |> ignore + Array.chunkBySize 5 player.Arsenal + |> Array.iter + (fun wps -> + wps + |> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Primary, $"{w.Type}-{w.Id}", $"{w.Name}")) + |> 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 updateArsenal player salePrice updatedArsenal = { player with Bank = player.Bank + salePrice ; Arsenal = updatedArsenal } + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + else + do! sendSimpleResponse ctx "You currently have no inventory to sell" + }) let sellItem (event : ComponentInteractionCreateEventArgs) player itemId = async { - let item = armoury |> Array.find (fun i -> i.Id = itemId) + let item = Armoury.battleItems |> Array.find (fun i -> i.Id = itemId) let updatedPlayer = { player with Bank = player.Bank + item.Cost ; Arsenal = player.Arsenal |> Array.filter (fun w -> w.Id <> itemId)} do! DbService.updatePlayer updatedPlayer let builder = DiscordInteractionResponseBuilder() @@ -94,6 +62,22 @@ let sellItem (event : ComponentInteractionCreateEventArgs) player itemId = |> Async.AwaitTask } +let handleBuyItem (ctx : InteractionContext) itemId = + Game.executePlayerAction ctx (fun player -> async { + let item = Armoury.battleItems |> Array.find (fun w -> w.Id = itemId) + let newBalance = player.Bank - item.Cost + if newBalance >= 0 then + let playerHasItem = player.Arsenal |> Array.exists (fun w -> item.Id = w.Id) + if not playerHasItem then + let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal } + do! DbService.updatePlayer p + do! sendSimpleResponse ctx $"Successfully purchased {item.Name}! You now have {newBalance} remaining" + else + do! sendSimpleResponse ctx $"You already own this item!" + else + do! sendSimpleResponse ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" + }) + let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) = async { let! playerResult = DbService.tryFindPlayer event.User.Id @@ -112,17 +96,15 @@ let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCrea type Store() = inherit ApplicationCommandModule () +// [] - [] - member _.ViewStore (ctx : InteractionContext) = viewStore ctx +// member _.ViewStore (ctx : InteractionContext) = viewStore ctx [] - member _.BuyHack (ctx : InteractionContext, [] hackId : HackId) = - buyItem ctx (int hackId) + member _.BuyHack (ctx : InteractionContext) = buyItem ctx Hack [] - member this.BuyShield (ctx : InteractionContext, [] shieldId : ShieldId) = - buyItem ctx (int shieldId) + member this.BuyShield (ctx : InteractionContext) = buyItem ctx Shield [] member this.SellItem (ctx : InteractionContext) = sell ctx diff --git a/Bot/Trainer.fs b/Bot/Trainer.fs index bd23b60..241e28f 100644 --- a/Bot/Trainer.fs +++ b/Bot/Trainer.fs @@ -5,11 +5,11 @@ open DSharpPlus open DSharpPlus.Entities open DSharpPlus.EventArgs open DSharpPlus.SlashCommands -open Degenz.Shared -open Degenz.Store +open Degenz.Types +open Degenz.Messaging -let defaultHack = armoury |> Array.find (fun i -> i.Id = int HackId.Virus) -let defaultShield = armoury |> Array.find (fun i -> i.Id = int ShieldId.Firewall) +let defaultHack = Armoury.battleItems |> Array.find (fun i -> i.Id = int HackId.Virus) +let defaultShield = Armoury.battleItems |> Array.find (fun i -> i.Id = int ShieldId.Firewall) let sendInitialEmbed (client : DiscordClient) = async { @@ -41,10 +41,10 @@ let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) = |> Async.AwaitTask match maybePlayer with | Some _ -> - do! Message.sendFollowUpMessageWithButton event step1Msg + do! sendFollowUpMessageWithButton event step1Msg | None -> let msg = "Looks like an error occurred, you're not a registered degenerate. Please contact a moderator." - do! Message.sendFollowUpMessage event msg + do! sendFollowUpMessage event msg } let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) = @@ -53,19 +53,19 @@ let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) = match result with | Some player -> let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name - do! Message.updateMessageWithGreyedOutButtons event step1Msg + do! updateMessageWithGreyedOutButtons event step1Msg let shieldMessage = if Player.shields player |> Array.isEmpty then $"You do not have any Shields in your arsenal, here's a {defaultShield.Name} you can use for now" else $"You currently have {weaponName} in your arsenal" - do! Message.sendFollowUpMessage event + do! sendFollowUpMessage event ($"First things first, let's get your system protected. Let's enable a shield to protect you from potential hackers. " + $"{shieldMessage}. To enable it and protect your system, you can use the `/defend` slash command to choose a shield." + $"\n\nRun the `/defend` command now and then select '{weaponName}'.") | None -> - do! Message.sendFollowUpMessage event $"Something went wrong, please contact a moderator" + do! sendFollowUpMessage event $"Something went wrong, please contact a moderator" } let defend (ctx : InteractionContext) = @@ -100,7 +100,7 @@ let handleDefenseMsg = { } let handleDefense (event : ComponentInteractionCreateEventArgs) = - let sendMessage' = Message.sendFollowUpMessage event + let sendMessage' = sendFollowUpMessage event async { do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate) |> Async.AwaitTask @@ -116,7 +116,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) = do! Async.Sleep 4000 do! sendMessage' $"Hacking attempt failed! {player.Name} defended hack from Degenz-Trainer and took {prize} from them! " do! Async.Sleep 3000 - do! Message.sendFollowUpMessageWithButton event handleDefenseMsg + do! sendFollowUpMessageWithButton event handleDefenseMsg | None -> do! sendMessage' $"Something went wrong, please contact a moderator" } @@ -127,19 +127,19 @@ let handleTrainerStep4 (event : ComponentInteractionCreateEventArgs) = match result with | Some player -> let weaponName = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name - do! Message.updateMessageWithGreyedOutButtons event handleDefenseMsg + do! updateMessageWithGreyedOutButtons event handleDefenseMsg let hackMessage = if Player.shields player |> Array.isEmpty then $"You do not have any Hacks in your arsenal, here's a {defaultHack.Name} you can use for now" else $"You currently have {weaponName} in your arsenal" - do! Message.sendFollowUpMessage event + do! sendFollowUpMessage event ($"Next why don't you try hacking me. {hackMessage}. To hack me and get some money, " + $" you can use the '/hack' slash command and select a user to hack, then choose the hack attack you wish to use." + $"\n\nRun the `/hack` command now and pick me as your target, then click on the '{weaponName}' button.") | None -> - do! Message.sendInteractionEvent event $"Something went wrong, please contact a moderator" + do! sendInteractionEvent event $"Something went wrong, please contact a moderator" } let attack (ctx : InteractionContext) (target : DiscordUser) = @@ -184,7 +184,7 @@ let handleAttackMsg = { } let handleAttack (event : ComponentInteractionCreateEventArgs) = - let sendMessage' = Message.sendFollowUpMessage event + let sendMessage' = sendFollowUpMessage event async { do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate) |> Async.AwaitTask @@ -200,7 +200,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = do! sendMessage' ("Look at that, you are now officially an elite haxor! By successfully hacking other people you can earn GoodBoyTokenz. " + "Hacks take time to recover so check back in later once you've used all your hacks.") do! Async.Sleep 7000 - do! Message.sendFollowUpMessageWithButton event handleAttackMsg + do! sendFollowUpMessageWithButton event handleAttackMsg | None -> do! sendMessage' $"Something went wrong, please contact a moderator" } @@ -214,7 +214,7 @@ let handleTrainerStep6 (event : ComponentInteractionCreateEventArgs) = let builder = DiscordFollowupMessageBuilder() builder.IsEphemeral <- true builder.Content <- "Get out of here!" - do! Message.updateMessageWithGreyedOutButtons event handleAttackMsg + do! updateMessageWithGreyedOutButtons event handleAttackMsg do! event.Interaction.CreateFollowupMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore @@ -230,6 +230,6 @@ let handleButtonEvent (event : ComponentInteractionCreateEventArgs) = | 4 -> do! handleTrainerStep4 event | 5 -> do! handleAttack event | 6 -> do! handleTrainerStep6 event - | _ -> do! Message.sendFollowUpMessage event "No action found" + | _ -> do! sendFollowUpMessage event "No action found" } diff --git a/DbService/DbService.fs b/DbService/DbService.fs index 1a57261..b04b81f 100644 --- a/DbService/DbService.fs +++ b/DbService/DbService.fs @@ -2,7 +2,8 @@ open System open System.Collections.Generic -open Degenz.Shared +open System.Threading.Tasks +open Degenz.Types open MongoDB.Bson open MongoDB.Bson.Serialization open MongoDB.Driver @@ -62,7 +63,7 @@ let private playerMap (player : PlayerData) = { let private mapBack (player : PlayerEntry) : PlayerData = { DiscordId = player.DiscordId Name = player.Name - Arsenal = player.Arsenal |> Array.map (fun w -> armoury |> Array.find (fun w' -> w = w'.Id)) + Arsenal = player.Arsenal |> Array.map (fun w -> Armoury.battleItems |> Array.find (fun w' -> w = w'.Id)) Actions = let atks = player.Attacks |> Array.map attackToAction let dfns = player.Defenses |> Array.map defenseToAction @@ -113,6 +114,7 @@ let updatePlayer (player : PlayerData) = return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore } + //let getTopPlayers amount = // async { // return! players.FindAsync() diff --git a/Shared/Shared.fs b/Shared/Shared.fs index d3174e0..6d8da3c 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -1,4 +1,4 @@ -module Degenz.Shared +namespace Degenz open System open DSharpPlus @@ -7,71 +7,82 @@ open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Newtonsoft.Json -[] -type mins +[] +module Types = -[] -type GBT + [] + type mins -type BattleClass = - | Network - | Exploit - | Penetration + [] + type GBT -type HackId = - | Virus = 0 - | RemoteAccess = 1 - | Worm = 2 + type BattleClass = + | Network + | Exploit + | Penetration -type ShieldId = - | Firewall = 6 - | Encryption = 7 - | Cypher = 8 + type HackId = + | Virus = 0 + | RemoteAccess = 1 + | Worm = 2 -type ItemType = - | Hack - | Shield + type ShieldId = + | Firewall = 6 + | Encryption = 7 + | Cypher = 8 -type BattleItem = { - Id : int - Name : string - Cost : int - Type : ItemType - Class : BattleClass - Power : int - Cooldown : int -} + type ItemType = + | Hack + | Shield -type HackResult = - | Strong - | Weak + type BattleItem = { + Id : int + Name : string + Cost : int + Type : ItemType + Class : BattleClass + Power : int + Cooldown : int + } -[] -type DiscordPlayer = { Id: uint64; Name: string } + type HackResult = + | Strong + | Weak -[] -type AttackResult = { - Result : bool - Target : DiscordPlayer -} + [] + type DiscordPlayer = { Id: uint64; Name: string } -type ActionType = - | Attack of AttackResult - | Defense + [] + type AttackResult = { + Result : bool + Target : DiscordPlayer + } -[] -type Action = - { ActionId : int - Type : ActionType - Timestamp : DateTime } + type ActionType = + | Attack of AttackResult + | Defense -[] -type PlayerData = - { DiscordId : uint64 - Name : string - Arsenal : BattleItem array - Actions : Action array - Bank : int } + [] + type Action = + { ActionId : int + Type : ActionType + Timestamp : DateTime } + + [] + type PlayerData = + { DiscordId : uint64 + Name : string + Arsenal : BattleItem array + Actions : Action array + Bank : int } + + +module Armoury = + let battleItems = + let file = System.IO.File.ReadAllText("Items.json") + JsonConvert.DeserializeObject(file) + + let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId) module Player = let hacks player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack) @@ -81,48 +92,44 @@ module Player = |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None) let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense _ -> true | _ -> false) -let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None) + let removeExpiredActions actions = + actions + |> Array.filter (fun (act : Action) -> + let item = Armoury.battleItems |> Array.find (fun w -> w.Id = act.ActionId) + DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown)) -let createSimpleResponseAsync msg (ctx: InteractionContext) = - async { - let builder = DiscordInteractionResponseBuilder() - builder.Content <- msg - builder.AsEphemeral true |> ignore + let modifyBank player amount = { player with Bank = max (player.Bank + amount) 0 } - do! - ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) - |> Async.AwaitTask + let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None) + + +module Messaging = + type InteractiveMessage = { + ButtonId : string + ButtonText : string + Message : string } -let notYetAHackerMsg = - createSimpleResponseAsync "You are currently not a hacker, first use the /redpill command to become one" + let statusFormat p = + $"Hacks: {Player.hacks p |> Array.toList} + Shields: {Player.shields p |> Array.toList} + Hack Attacks: {Player.attacks p |> Array.toList} + Active Defenses: {Player.defenses p |> Array.toList} + Bank: {p.Bank}" -let hackDescription = "" + let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) = + weapons + |> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Success, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}")) -let statusFormat p = - $"Hacks: {Player.hacks p |> Array.toList} -Shields: {Player.shields p |> Array.toList} -Hack Attacks: {Player.attacks p |> Array.toList} -Active Defenses: {Player.defenses p |> Array.toList} -Bank: {p.Bank}" + let sendSimpleResponse (ctx: InteractionContext) msg = + async { + let builder = DiscordInteractionResponseBuilder() + builder.Content <- msg + builder.AsEphemeral true |> ignore + do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + |> Async.AwaitTask + } -let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) = - weapons - |> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Success, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}")) - -let modifyPlayerBank player amount = { player with Bank = max (player.Bank + amount) 0 } - -let armoury = - let file = System.IO.File.ReadAllText("Items.json") - JsonConvert.DeserializeObject(file) - -type InteractiveMessage = { - ButtonId : string - ButtonText : string - Message : string -} - -module Message = let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg = async { let builder = DiscordFollowupMessageBuilder()