From 8f8aebd6acc09d5e054fe092655c11ca94980e17 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Tue, 8 Feb 2022 23:33:30 +0700 Subject: [PATCH] Several improvements and enhacements --- Bot/Bot.fs | 27 +++++++++++++++++++++------ Bot/Embeds.fs | 31 +++++++++++++++++++------------ Bot/Game.fs | 4 ++-- Bot/HackerBattle.fs | 14 ++++++++++---- Bot/Store.fs | 5 ++--- Bot/Trainer.fs | 5 ++++- Shared/Shared.fs | 11 +++++------ 7 files changed, 63 insertions(+), 34 deletions(-) diff --git a/Bot/Bot.fs b/Bot/Bot.fs index 67bfb65..d46fc20 100644 --- a/Bot/Bot.fs +++ b/Bot/Bot.fs @@ -21,9 +21,11 @@ let storeConfig = DiscordConfiguration() //let configs = [| hackerBattleConfig ; storeConfig ; slotMachineConfig ; |] let configs = [ hackerBattleConfig ; storeConfig ] -for conf in configs do - conf.TokenType <- TokenType.Bot - conf.Intents <- DiscordIntents.All +hackerBattleConfig.TokenType <- TokenType.Bot +hackerBattleConfig.Intents <- DiscordIntents.All + +storeConfig.TokenType <- TokenType.Bot +storeConfig.Intents <- DiscordIntents.All hackerBattleConfig.Token <- GuildEnvironment.tokenHackerBattle storeConfig.Token <- GuildEnvironment.tokenStore @@ -34,8 +36,6 @@ let storeBot = new DiscordClient(storeConfig) //let slotMachineBot = new DiscordClient(slotMachineConfig) //let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |] -let clients = [ hackerBattleBot ; storeBot ] - let sc1 = hackerBattleBot.UseSlashCommands() let sc2 = storeBot.UseSlashCommands() //let sc3 = slotMachineBot.UseSlashCommands() @@ -63,12 +63,27 @@ let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEven :> Task //hackerBattleBot.add_InteractionCreated(AsyncEventHandler(asdf)) +if guild <> 922419263275425832uL then + Trainer.sendInitialEmbed hackerBattleBot + let run (client : DiscordClient) = async { do! client.ConnectAsync () |> Async.AwaitTask } -Trainer.sendInitialEmbed hackerBattleBot +let clients = + if guild = 922419263275425832uL then + let interactionsConfig = DiscordConfiguration() + interactionsConfig.TokenType <- TokenType.Bot + interactionsConfig.Intents <- DiscordIntents.All + interactionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions + let interactionsBot = new DiscordClient(interactionsConfig) + let commands = interactionsBot.UseSlashCommands() + commands.RegisterCommands(guild) + + [ hackerBattleBot ; storeBot ; interactionsBot ] + else + [ hackerBattleBot ; storeBot ] clients |> List.map run diff --git a/Bot/Embeds.fs b/Bot/Embeds.fs index 5363e62..3197537 100644 --- a/Bot/Embeds.fs +++ b/Bot/Embeds.fs @@ -1,6 +1,7 @@ module Degenz.Embeds open System +open DSharpPlus open DSharpPlus.EventArgs open Degenz.Types open DSharpPlus.Entities @@ -32,13 +33,16 @@ let getShieldGif = function | ShieldId.Cypher -> "https://s10.gifyu.com/images/Cypher-Smaller.gif" | _ -> shieldGif -let constructButtons (actionType: string) (playerInfo: string) (items: BattleItem array) = +let constructButtons (actionType: string) (player: PlayerData) (buttonId : string) (items: BattleItem array) = items - |> Array.map (fun item -> DiscordButtonComponent(Game.getClassButtonColor item.Class, $"{actionType}-{item.Id}-{playerInfo}", $"{item.Name}")) + |> Array.map (fun item -> + match player.Actions |> Array.exists (fun i -> i.ActionId = item.Id) with + | true -> DiscordButtonComponent(Game.getClassButtonColor item.Class, $"{actionType}-{item.Id}", $"{item.Name} (on cooldown)", true) + | false -> DiscordButtonComponent(Game.getClassButtonColor item.Class, $"{actionType}-{item.Id}-{buttonId}", $"{item.Name}")) let pickDefense actionId player = let buttons = - constructButtons actionId (string player.DiscordId) (Player.shields player) + constructButtons actionId player (string player.DiscordId) (Player.shields player) |> Seq.cast let embed = @@ -55,7 +59,7 @@ let pickDefense actionId player = let pickHack actionId attacker defender = let buttons = let hacks = Player.hacks attacker - constructButtons actionId $"{defender.DiscordId}-{defender.Name}" hacks + constructButtons actionId attacker $"{defender.DiscordId}-{defender.Name}" hacks |> Seq.cast let embed = @@ -69,12 +73,12 @@ let pickHack actionId attacker defender = .AddEmbed(embed.Build()) .AsEphemeral true -let responseSuccessfulHack (targetId : uint64) (hack : BattleItem) = +let responseSuccessfulHack (targetName : string) (hack : BattleItem) = let embed = DiscordEmbedBuilder() embed.ImageUrl <- getHackGif (enum(hack.Id)) DiscordFollowupMessageBuilder() - .WithContent($"You successfully hacked <@{targetId}> using {hack.Name}, and stole 💰$GBT {Game.HackPrize} from them!") + .WithContent($"You successfully hacked {targetName} using {hack.Name}, and stole 💰$GBT {Game.HackPrize} from them!") .AddEmbed(embed.Build()) .AsEphemeral(true) @@ -90,20 +94,20 @@ let responseCreatedShieldTrainer (shield : BattleItem) = .AsEphemeral(true) .WithContent($"Mounted a {shield.Name} defense for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours") -let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) targetId prize = +let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) target prize = DiscordMessageBuilder() - .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz") + .WithContent($"{event.User.Username} successfully hacked {target.Name} for a total of {prize} GoodBoyTokenz") -let eventFailedHack (event : ComponentInteractionCreateEventArgs) targetId prize = +let eventFailedHack (event : ComponentInteractionCreateEventArgs) target prize = DiscordMessageBuilder() - .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz") + .WithContent($"{event.User.Username} successfully hacked {target.Name} for a total of {prize} GoodBoyTokenz") let getGoodAgainst = function | BattleClass.Network -> ( ShieldId.Firewall , HackId.Virus ) | BattleClass.Penetration -> ( ShieldId.Cypher , HackId.RemoteAccess ) | BattleClass.Exploit -> ( ShieldId.Encryption , HackId.Worm ) -let getBuyItemsEmbed (itemType : ItemType) (store : BattleItem array) = +let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : BattleItem array) = let embeds , buttons = store |> Array.filter (fun i -> i.Type = itemType) @@ -127,7 +131,10 @@ let getBuyItemsEmbed (itemType : ItemType) (store : BattleItem array) = .WithColor(Game.getClassEmbedColor item.Class) .WithTitle($"{item.Name}") |> ignore - let button = DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Buy-{item.Id}", $"Buy {item.Name}") + let button = + if player.Arsenal |> Array.exists (fun i -> i.Id = item.Id) + then DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Buy-{item.Id}", $"Own {item.Name}", true) + else DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Buy-{item.Id}", $"Buy {item.Name}") embed.Build() , button :> DiscordComponent) |> Array.unzip diff --git a/Bot/Game.fs b/Bot/Game.fs index 9c71e44..bebb0a9 100644 --- a/Bot/Game.fs +++ b/Bot/Game.fs @@ -33,11 +33,11 @@ module Game = 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" + | None -> do! Messaging.sendFollowUpMessageFromCtx ctx "You are currently not a hacker, first use the /redpill command to become one" } |> Async.StartAsTask :> Task - // TODO: Create an abstraction for these two helper functions + // TODO J: Create an abstraction for these two helper functions let executePlayerEvent (event : ComponentInteractionCreateEventArgs) (dispatch : PlayerData -> Async) = async { let builder = DiscordInteractionResponseBuilder() diff --git a/Bot/HackerBattle.fs b/Bot/HackerBattle.fs index 7b8f11c..207477d 100644 --- a/Bot/HackerBattle.fs +++ b/Bot/HackerBattle.fs @@ -9,6 +9,8 @@ open DSharpPlus.SlashCommands open Degenz open Degenz.Messaging +// TODO: Do not allow any attacks until the user has completed training +// TODO: Introduce second round of weapons, more expensive and with better stats let checkPlayerIsAttackingThemselves defender attacker = match attacker.DiscordId = defender.DiscordId with | true -> Error "You think you're clever? You can't hack yourself, pal." @@ -77,12 +79,12 @@ let successfulHack (event : ComponentInteractionCreateEventArgs) attacker defend async { do! updateCombatants attacker defender hack Game.HackPrize - let embed = Embeds.responseSuccessfulHack (defender.DiscordId) (Armory.getItem (int hack)) + let embed = Embeds.responseSuccessfulHack (defender.Name) (Armory.getItem (int hack)) do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore - let builder = Embeds.eventSuccessfulHack event defender.DiscordId Game.HackPrize + let builder = Embeds.eventSuccessfulHack event defender Game.HackPrize let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle) do! channel.SendMessageAsync(builder) |> Async.AwaitTask @@ -97,7 +99,7 @@ let failedHack (event : ComponentInteractionCreateEventArgs) attacker defender h do! updateCombatants attacker defender hack -Game.ShieldPrize let builder = DiscordMessageBuilder() - builder.WithContent($"Hacking attempt failed! <@{defender.DiscordId}> defended hack from {event.User.Username} and stole {Game.ShieldPrize} $GBT from them! ") |> ignore + builder.WithContent($"Hacking attempt failed! {defender.Name} defended hack from {event.User.Username} and stole {Game.ShieldPrize} $GBT from them! ") |> ignore let channel = (event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)) do! channel.SendMessageAsync(builder) |> Async.AwaitTask @@ -122,7 +124,11 @@ let attack (target : DiscordUser) (ctx : InteractionContext) = |> Async.AwaitTask |> Async.Ignore | Error msg -> sendFollowUpMessageFromCtx ctx msg - | None -> do! sendFollowUpMessageFromCtx ctx "Your target is not connected to the network, they must join first by using the /redpill command" + | None -> + if target.IsBot + then do! sendFollowUpMessageFromCtx ctx $"{target.Username} is a bot, pick a real human to hack" + else do! sendFollowUpMessageFromCtx ctx "Your target is not connected to the network, they must join first by using the /redpill command" + }) let handleAttack (event : ComponentInteractionCreateEventArgs) = diff --git a/Bot/Store.fs b/Bot/Store.fs index c82544f..c345c1f 100644 --- a/Bot/Store.fs +++ b/Bot/Store.fs @@ -61,7 +61,7 @@ let arsenal (ctx : InteractionContext) = let buy itemType (ctx : InteractionContext) = Game.executePlayerInteraction ctx (fun player -> async { - let itemStore = Embeds.getBuyItemsEmbed itemType Armory.battleItems + let itemStore = Embeds.getBuyItemsEmbed player itemType Armory.battleItems do! ctx.Interaction.CreateFollowupMessageAsync(itemStore) |> Async.AwaitTask |> Async.Ignore @@ -133,8 +133,7 @@ type Store() = let enforceChannel (ctx : InteractionContext) (storeFn : InteractionContext -> Task) = match ctx.Channel.Id with - | id when id = GuildEnvironment.channelArmory -> - storeFn ctx + | id when id = GuildEnvironment.channelArmory -> storeFn ctx | _ -> task { let msg = $"You must go to <#{GuildEnvironment.channelArmory}> channel to buy/sell or check your arsenal" diff --git a/Bot/Trainer.fs b/Bot/Trainer.fs index dcbd1e5..f933568 100644 --- a/Bot/Trainer.fs +++ b/Bot/Trainer.fs @@ -8,6 +8,9 @@ open DSharpPlus.SlashCommands open Degenz.Types open Degenz.Messaging +// TODO: We should either gift the weapons to the player during training, or have them buy it +// TODO: We should tell the user to type out /arsenal during the training +// TODO: How do we handle the money being generated here? It's fake but fake is confusing let defaultHack = Armory.battleItems |> Array.find (fun i -> i.Id = int HackId.Virus) let defaultShield = Armory.battleItems |> Array.find (fun i -> i.Id = int ShieldId.Encryption) @@ -136,7 +139,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = let sendMessage' = sendFollowUpMessage event do! Async.Sleep 1000 let hack = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultHack - let embed = Embeds.responseSuccessfulHack GuildEnvironment.botHackerBattle hack + let embed = Embeds.responseSuccessfulHack "Sensei" hack do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore do! Async.Sleep 5000 do! sendMessage' diff --git a/Shared/Shared.fs b/Shared/Shared.fs index 719e089..2711341 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -99,7 +99,7 @@ module Messaging = let plural amount = if amount = 1 then "" else "s" let ``and`` = if remaining.Hours > 0 then "and " else "" let hours = if remaining.Hours > 0 then $"{remaining.Hours} hour{plural remaining.Hours} {``and``}" else String.Empty - let totalMins = remaining.Minutes + 1 + let totalMins = remaining.Minutes let minutes = if totalMins > 0 then $"{totalMins} minute{plural totalMins}" else "1 minute" $"{hours}{minutes}" @@ -114,12 +114,11 @@ module Messaging = | _ -> actions |> Array.map (fun act -> + let item = Armory.getItem act.ActionId + let cooldown = getTimeTillCooldownFinishes (System.TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp match act.Type with - | Attack atk -> $"Hacked {atk.Target.Name} at {act.Timestamp.ToShortTimeString()}" - | Defense -> - let item = Armory.getItem act.ActionId - let cooldown = getTimeTillCooldownFinishes (System.TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp - $"{item.Name} active for {cooldown}") + | Attack atk -> $"Hacked {atk.Target.Name} {cooldown} ago" + | Defense -> $"{item.Name} Shield active for {cooldown}") |> String.concat "\n" let sendSimpleResponse (ctx: InteractionContext) msg =