diff --git a/HackerBattle/Commands.fs b/HackerBattle/Commands.fs index b85bfee..61f5c6b 100644 --- a/HackerBattle/Commands.fs +++ b/HackerBattle/Commands.fs @@ -23,7 +23,7 @@ let attack (ctx : InteractionContext) (target : DiscordUser) = | Some attacker , Some defender -> let updatedAttacks = attacker.Attacks - |> removeExpiredActions (TimeSpan.FromMinutes(5)) (fun (atk : Attack) -> atk.Timestamp) + |> removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp) do! DbService.updateAttacks attacker.DiscordId updatedAttacks if updatedAttacks.Length < 2 then let builder = DiscordInteractionResponseBuilder() @@ -61,11 +61,11 @@ let defend (ctx : InteractionContext) = let! player = DbService.tryFindPlayer ctx.Member.Id match player with | Some player -> - let updatedDefenses = removeExpiredActions (TimeSpan.FromMinutes(60)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses + let updatedDefenses = removeExpiredActions (TimeSpan.FromHours(24)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses do! DbService.updatePlayer <| { player with Defenses = updatedDefenses } - if updatedDefenses.Length < 2 then + if updatedDefenses.Length < 3 then let builder = DiscordInteractionResponseBuilder() - builder.AddEmbed (constructEmbed "Pick a defense to mount for a duration of time") |> ignore + builder.AddEmbed (constructEmbed "Pick a defense to mount for 24 hours") |> ignore constructButtons "Defend" (string player.DiscordId) player.Shields |> Seq.cast @@ -80,7 +80,8 @@ let defend (ctx : InteractionContext) = let builder = DiscordInteractionResponseBuilder() let timestamp = updatedDefenses |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - timestamp) - builder.Content <- $"Cannot add new defense, please wait {timeRemaining.Minutes} minutes and {timeRemaining.Seconds} seconds to add another defense" + // TODO: Make this handle hours and minutes + builder.Content <- $"Cannot add new defense, please wait {timeRemaining.Hours} hours and {timeRemaining.Minutes} minutes to add another defense" builder.AsEphemeral true |> ignore @@ -101,43 +102,46 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) = let! resultTarget = DbService.tryFindPlayer targetId match resultPlayer , resultTarget , resultHack , resultId with | Some player , Some target , true , true -> + let updatedDefenses = removeExpiredActions (TimeSpan.FromHours(24)) (fun (p : Defense) -> p.Timestamp) target.Defenses + do! DbService.updatePlayer <| { player with Defenses = updatedDefenses } let wasSuccessfulHack = - target.Defenses + updatedDefenses |> Seq.toArray |> Array.map (fun dfn -> int dfn.DefenseType) |> Array.map (calculateDamage weapon) |> Array.contains Weak match wasSuccessfulHack with | false -> - let prize = 0.1726f + let prize = 1.337f // LEET let attack = { HackType = enum(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } } do! DbService.updatePlayer <| updatePlayer prize attack player + do! DbService.updatePlayer { target with Bank = MathF.Max(target.Bank - prize, 0f)} let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true - builder.Content <- $"Successfully hacked {split.[3]} using {weapon}! You just won {prize} genz!" + builder.Content <- $"Successfully hacked {split.[3]} using {weapon}! You just won {prize} GoodBoyTokenz!" do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask let builder = DiscordMessageBuilder() - builder.WithContent($"{event.User.Username} successfully hacked <@{targetId}>!") |> ignore + builder.WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz") |> ignore let channel = (event.Guild.GetChannel(battleChannel)) do! channel.SendMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore | true -> let builder = DiscordInteractionResponseBuilder() - let loss = -0.0623f + let prize = 0.0623f builder.IsEphemeral <- true - builder.Content <- $"Hack failed! {split.[3]} was able to mount a successful defense! You lost {loss} genz!" + builder.Content <- $"Hack failed! {split.[3]} was able to mount a successful defense! You lost {prize} GoodBoyTokenz!" do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask let attack = { HackType = enum(weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } } - do! DbService.updatePlayer <| updatePlayer loss attack player + do! DbService.updatePlayer <| updatePlayer -prize attack player let builder = DiscordMessageBuilder() - builder.WithContent($"{event.User.Username} failed to hack <@{targetId}>!") |> ignore + builder.WithContent($"Hacking attempt failed! <@{targetId}> defended hack from {event.User} and took {prize} from them! ") |> ignore let channel = (event.Guild.GetChannel(battleChannel)) do! channel.SendMessageAsync(builder) |> Async.AwaitTask diff --git a/PlayerInteractions/Program.fs b/PlayerInteractions/Program.fs index 3336644..06546c5 100644 --- a/PlayerInteractions/Program.fs +++ b/PlayerInteractions/Program.fs @@ -94,6 +94,9 @@ module Commands = let! player = DbService.tryFindPlayer ctx.Member.Id match player with | Some p -> + let updatedAttacks = p.Attacks |> removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp) + let updatedDefenses = p.Defenses |> removeExpiredActions (TimeSpan.FromHours(24)) (fun (p : Defense) -> p.Timestamp) + do! DbService.updatePlayer <| { p with Attacks = updatedAttacks ; Defenses = updatedDefenses } let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- statusFormat p diff --git a/Shared/Shared.fs b/Shared/Shared.fs index 974a772..f6657e2 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -26,55 +26,56 @@ type Shield = | Sanitation = 5 | Cypher = 3 -let getClass = function - | 0 | 1 -> Network - | 2 | 3 -> Exploit - | 4 | _ -> Penetration +let getClass = + function + | 0 + | 1 -> Network + | 2 + | 3 -> Exploit + | 4 + | _ -> Penetration type HackResult = | Strong | Weak [] -type DiscordPlayer = { - Id : uint64 - Name : string -} +type DiscordPlayer = { Id: uint64; Name: string } [] -type Attack = { - HackType : Weapon - Target : DiscordPlayer - Timestamp : DateTime -} +type Attack = + { HackType: Weapon + Target: DiscordPlayer + Timestamp: DateTime } [] -type Defense = { - DefenseType : Shield - Timestamp : DateTime -} +type Defense = + { DefenseType: Shield + Timestamp: DateTime } [] -type Player = { - DiscordId : uint64 - Name : string - Weapons : Weapon array - Shields : Shield array - Attacks : Attack array - Defenses : Defense array - Bank : single -} +type Player = + { DiscordId: uint64 + Name: string + Weapons: Weapon array + Shields: Shield array + Attacks: Attack array + Defenses: Defense array + Bank: single } -let createSimpleResponseAsync msg (ctx : InteractionContext) = +let createSimpleResponseAsync msg (ctx: InteractionContext) = async { let builder = DiscordInteractionResponseBuilder() builder.Content <- msg builder.AsEphemeral true |> ignore - do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) + + do! + ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } -let notYetAHackerMsg = createSimpleResponseAsync "You are not currently a hacker, first use the /redpill command to become one" +let notYetAHackerMsg = + createSimpleResponseAsync "You are not currently a hacker, first use the /redpill command to become one" let hackDescription = "" @@ -85,20 +86,12 @@ Active Hacks: {player.Attacks |> Array.toList} Active Defenses: {player.Defenses |> Array.toList} Bank: {player.Bank}" -let constructButtons (actionType : string) (playerInfo : string) (weapons : 'a array) = +let constructButtons (actionType: string) (playerInfo: string) (weapons: 'a array) = weapons - |> Seq.map (fun hack -> - DiscordButtonComponent( - ButtonStyle.Primary, - $"{actionType}-{hack}-{playerInfo}", - $"{hack}")) + |> Seq.map (fun hack -> DiscordButtonComponent(ButtonStyle.Primary, $"{actionType}-{hack}-{playerInfo}", $"{hack}")) -let removeExpiredActions timespan (timestamp : 'a -> DateTime) actions = - actions - |> Array.filter (fun act -> - if DateTime.UtcNow - (timestamp act) < timespan - then true - else false) +let removeExpiredActions timespan (timestamp: 'a -> DateTime) actions = + actions |> Array.filter (fun act -> DateTime.UtcNow - (timestamp act) < timespan) let constructEmbed message = let builder = DiscordEmbedBuilder() @@ -111,10 +104,10 @@ let constructEmbed message = builder.Author <- author builder.Build() -let calculateDamage (hack : int) (shield : int) = +let calculateDamage (hack: int) (shield: int) = let hackClass = getClass hack let protectionClass = getClass shield - match hackClass , protectionClass with - | h , p when h = p -> Weak - | _ -> Strong + match hackClass, protectionClass with + | h, p when h = p -> Weak + | _ -> Strong