namespace Degenz open System.Threading.Tasks open DSharpPlus open DSharpPlus.Entities open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz.DbService open Degenz.Messaging module Game = let HackPrize = 10 let ShieldPrize = 5 let SameTargetAttackCooldown = System.TimeSpan.FromHours(6) let getClassButtonColor = function | Network -> ButtonStyle.Danger | Exploit -> ButtonStyle.Success | Penetration -> ButtonStyle.Primary let getClassEmbedColor = function | Network -> DiscordColor.Red | Penetration -> DiscordColor.Blurple | Exploit -> DiscordColor.Green let getGoodAgainst = function | BattleClass.Network -> ( ShieldId.Firewall , HackId.Virus ) | BattleClass.Penetration -> ( ShieldId.Cypher , HackId.RemoteAccess ) | BattleClass.Exploit -> ( ShieldId.Encryption , HackId.Worm ) let executePlayerAction (ctx : IDiscordContext) (dispatch : PlayerData -> Async) = async { let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- "Content" do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask let! playerResult = tryFindPlayer (ctx.GetDiscordMember().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 module Player = let getItems itemType (player : PlayerData) = player.Arsenal |> Array.filter (fun i -> i.Type = itemType) let getHacks (player : PlayerData) = getItems ItemType.Hack player let getShields (player : PlayerData) = getItems ItemType.Shield player let getAttacks player = player.Actions |> Array.filter (fun act -> match act.Type with Attack _ -> true | _ -> false) let getDefenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false) let removeExpiredActions filterByAttackCooldown player = let actions = player.Actions |> Array.filter (fun (act : Action) -> let item = Armory.getItem act.ActionId match act.Type , filterByAttackCooldown with | Attack _ , true -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(int item.Cooldown) | Attack _ , false -> System.DateTime.UtcNow - act.Timestamp < Game.SameTargetAttackCooldown | Defense , _ -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(int item.Cooldown)) { player with Actions = actions } let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0 } let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None) module Arsenal = let battleItemFormat (items : BattleItem array) = match items with | [||] -> "None" | _ -> items |> Array.toList |> List.map (fun i -> i.Name) |> String.concat ", " let actionFormat (actions : Action array) = match actions with | [||] -> "None" | _ -> let hacks , defenses = actions |> Array.partition (fun act -> match act.Type with Attack _ -> true | Defense -> false) let hacks = hacks |> Array.take (min hacks.Length 10) hacks |> Array.append defenses |> Array.map (fun act -> let item = Armory.getItem act.ActionId match act.Type with | Attack atk -> let cooldown = Messaging.getTimeText false Game.SameTargetAttackCooldown act.Timestamp $"Hacked {atk.Target.Name} {cooldown} ago" | Defense -> let cooldown = Messaging.getTimeText true (System.TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp $"{item.Name} Shield active for {cooldown}") |> String.concat "\n" let statusFormat p = $"**Hacks:** {Player.getHacks p |> battleItemFormat}\n **Shields:** {Player.getShields p |> battleItemFormat}\n **Hack Attacks:**\n{Player.getAttacks p |> actionFormat}\n **Active Shields:**\n{Player.getDefenses p |> actionFormat}"