Implement new hacker battle mechanics
This commit is contained in:
		
							parent
							
								
									308409a1bc
								
							
						
					
					
						commit
						ed68f0b87d
					
				@ -7,7 +7,7 @@ open AsciiTableFormatter
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
let constructEmbed message =
 | 
					let constructEmbed message =
 | 
				
			||||||
    let builder = DiscordEmbedBuilder()
 | 
					    let builder = DiscordEmbedBuilder()
 | 
				
			||||||
    builder.Color <- Optional(DiscordColor.PhthaloGreen)
 | 
					    builder.Color <- Optional(DiscordColor.Blurple)
 | 
				
			||||||
    builder.Description <- message
 | 
					    builder.Description <- message
 | 
				
			||||||
    let author = DiscordEmbedBuilder.EmbedAuthor()
 | 
					    let author = DiscordEmbedBuilder.EmbedAuthor()
 | 
				
			||||||
    author.Name <- "Degenz Hacker Game"
 | 
					    author.Name <- "Degenz Hacker Game"
 | 
				
			||||||
@ -23,7 +23,7 @@ let pickDefense actionId player =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let embed =
 | 
					    let embed =
 | 
				
			||||||
        DiscordEmbedBuilder()
 | 
					        DiscordEmbedBuilder()
 | 
				
			||||||
            .WithColor(DiscordColor.PhthaloGreen)
 | 
					            .WithColor(DiscordColor.Blurple)
 | 
				
			||||||
            .WithDescription("Pick a defense to mount for 10 hours")
 | 
					            .WithDescription("Pick a defense to mount for 10 hours")
 | 
				
			||||||
            .WithImageUrl("https://s10.gifyu.com/images/Defense-Degenz-V2.gif")
 | 
					            .WithImageUrl("https://s10.gifyu.com/images/Defense-Degenz-V2.gif")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -39,7 +39,7 @@ let pickHack actionId attacker defender =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let embed =
 | 
					    let embed =
 | 
				
			||||||
        DiscordEmbedBuilder()
 | 
					        DiscordEmbedBuilder()
 | 
				
			||||||
            .WithColor(DiscordColor.PhthaloGreen)
 | 
					            .WithColor(DiscordColor.Blurple)
 | 
				
			||||||
            .WithDescription("Pick the hack that you want to use")
 | 
					            .WithDescription("Pick the hack that you want to use")
 | 
				
			||||||
            .WithImageUrl("https://s10.gifyu.com/images/Hacker-Degenz-V2.gif")
 | 
					            .WithImageUrl("https://s10.gifyu.com/images/Hacker-Degenz-V2.gif")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -49,6 +49,22 @@ let pickHack actionId attacker defender =
 | 
				
			|||||||
        .AsEphemeral true
 | 
					        .AsEphemeral true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) targetId prize =
 | 
					let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) targetId prize =
 | 
				
			||||||
 | 
					    let embed =
 | 
				
			||||||
 | 
					        DiscordEmbedBuilder()
 | 
				
			||||||
 | 
					            .WithColor(DiscordColor.Blurple)
 | 
				
			||||||
 | 
					            .WithDescription("Pick the hack that you want to use")
 | 
				
			||||||
 | 
					            .WithImageUrl("https://s10.gifyu.com/images/Hacker-Degenz-V2.gif")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DiscordMessageBuilder()
 | 
				
			||||||
 | 
					        .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let eventFailedHack (event : ComponentInteractionCreateEventArgs) targetId prize =
 | 
				
			||||||
 | 
					    let embed =
 | 
				
			||||||
 | 
					        DiscordEmbedBuilder()
 | 
				
			||||||
 | 
					            .WithColor(DiscordColor.Blurple)
 | 
				
			||||||
 | 
					            .WithDescription("Pick the hack that you want to use")
 | 
				
			||||||
 | 
					            .WithImageUrl("https://s10.gifyu.com/images/Hacker-Degenz-V2.gif")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DiscordMessageBuilder()
 | 
					    DiscordMessageBuilder()
 | 
				
			||||||
        .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
 | 
					        .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -77,7 +93,7 @@ let storeListing store =
 | 
				
			|||||||
                             |> getClass
 | 
					                             |> getClass
 | 
				
			||||||
                { Name = item.Name ; Cost = string item.Cost ; Class = string itemClass })
 | 
					                { Name = item.Name ; Cost = string item.Cost ; Class = string itemClass })
 | 
				
			||||||
            |> Formatter.Format
 | 
					            |> Formatter.Format
 | 
				
			||||||
            |> sprintf "**%A**\n``` %s ```" itemType
 | 
					            |> sprintf "**%As**\n``` %s ```" itemType
 | 
				
			||||||
            |> constructEmbed)
 | 
					            |> constructEmbed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DiscordInteractionResponseBuilder()
 | 
					    DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
				
			|||||||
@ -9,124 +9,141 @@ open DSharpPlus.SlashCommands
 | 
				
			|||||||
open Degenz
 | 
					open Degenz
 | 
				
			||||||
open Degenz.Shared
 | 
					open Degenz.Shared
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let checkForExistingHack attacker defenderId =
 | 
				
			||||||
 | 
					    let updatedAttacks =
 | 
				
			||||||
 | 
					        attacker.Attacks
 | 
				
			||||||
 | 
					        |> removeExpiredActions (TimeSpan.FromHours(24)) (fun atk -> atk.Timestamp)
 | 
				
			||||||
 | 
					    updatedAttacks
 | 
				
			||||||
 | 
					    |> Array.tryFind (fun a -> a.Target.Id = defenderId)
 | 
				
			||||||
 | 
					    |> function
 | 
				
			||||||
 | 
					       | Some attack ->
 | 
				
			||||||
 | 
					           let timeRemaining = TimeSpan.FromHours(24) - (DateTime.UtcNow - attack.Timestamp)
 | 
				
			||||||
 | 
					           Error $"You can only hack the same target once every 24 hours, wait {timeRemaining.Seconds} seconds to attempt another hack on {attack.Target.Name}."
 | 
				
			||||||
 | 
					       | None ->
 | 
				
			||||||
 | 
					           Ok updatedAttacks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let checkIfHackHasCooldown hack updatedAttacks =
 | 
				
			||||||
 | 
					    let mostRecentHackAttack =
 | 
				
			||||||
 | 
					        updatedAttacks
 | 
				
			||||||
 | 
					        |> Array.tryFind (fun a -> a.HackType = hack)
 | 
				
			||||||
 | 
					        |> function
 | 
				
			||||||
 | 
					           | Some a -> a.Timestamp
 | 
				
			||||||
 | 
					           | None -> DateTime.UtcNow
 | 
				
			||||||
 | 
					    if DateTime.UtcNow - mostRecentHackAttack <= TimeSpan.FromMinutes(5) then
 | 
				
			||||||
 | 
					        Ok updatedAttacks
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					       let timeRemaining = TimeSpan.FromMinutes(5) - (DateTime.UtcNow - mostRecentHackAttack)
 | 
				
			||||||
 | 
					       Error $"You can only attack once a minute, wait {timeRemaining.Seconds} seconds to attack again."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let runHackerBattle attacker defender hack =
 | 
				
			||||||
 | 
					    defender.Defenses
 | 
				
			||||||
 | 
					    |> removeExpiredActions (TimeSpan.FromHours(6)) (fun (pro : Defense) -> pro.Timestamp)
 | 
				
			||||||
 | 
					    |> Seq.toArray
 | 
				
			||||||
 | 
					    |> Array.map (fun dfn -> int dfn.DefenseType)
 | 
				
			||||||
 | 
					    |> Array.map (calculateDamage (int hack))
 | 
				
			||||||
 | 
					    |> Array.contains Weak
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let updateCombatants attacker defender hack prize =
 | 
				
			||||||
 | 
					   let updatePlayer amount attack p =
 | 
				
			||||||
 | 
					       { p with Attacks = Array.append [| attack |] p.Attacks ; Bank = Math.Max(p.Bank + amount, 0) }
 | 
				
			||||||
 | 
					   let attack = { HackType = enum<Hack>(int hack) ; Timestamp = DateTime.UtcNow ; Target = { Id = defender.DiscordId ; Name = defender.Name } }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   [ DbService.updatePlayer <| updatePlayer prize attack attacker
 | 
				
			||||||
 | 
					     DbService.updatePlayer <| modifyPlayerBank defender -prize ]
 | 
				
			||||||
 | 
					   |> Async.Parallel
 | 
				
			||||||
 | 
					   |> Async.Ignore
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let successfulHack (event : ComponentInteractionCreateEventArgs) attacker defender hack =
 | 
				
			||||||
 | 
					    async {
 | 
				
			||||||
 | 
					        let prize = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do! updateCombatants attacker defender hack prize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					        builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					        builder.Content <- $"Successfully hacked {defender.Name} using {hack}! You just won {prize} GoodBoyTokenz!"
 | 
				
			||||||
 | 
					        // TODO: Don't make this an Update
 | 
				
			||||||
 | 
					        do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
 | 
				
			||||||
 | 
					            |> Async.AwaitTask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let builder = Embeds.eventSuccessfulHack event defender.DiscordId prize
 | 
				
			||||||
 | 
					        let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)
 | 
				
			||||||
 | 
					        do! channel.SendMessageAsync(builder)
 | 
				
			||||||
 | 
					            |> Async.AwaitTask
 | 
				
			||||||
 | 
					            |> Async.Ignore
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let failedHack (event : ComponentInteractionCreateEventArgs) attacker defender hack =
 | 
				
			||||||
 | 
					    async {
 | 
				
			||||||
 | 
					        let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					        let prize = 2
 | 
				
			||||||
 | 
					        builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					        builder.Content <- $"Hack failed! {defender.Name} was able to mount a successful defense! You lost {prize} GoodBoyTokenz!"
 | 
				
			||||||
 | 
					        do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
 | 
				
			||||||
 | 
					            |> Async.AwaitTask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        do! updateCombatants attacker defender hack -prize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let builder = DiscordMessageBuilder()
 | 
				
			||||||
 | 
					        builder.WithContent($"Hacking attempt failed! <@{defender.DiscordId}> defended hack from {event.User.Username} and took {prize} from them! ") |> ignore
 | 
				
			||||||
 | 
					        let channel = (event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle))
 | 
				
			||||||
 | 
					        do! channel.SendMessageAsync(builder)
 | 
				
			||||||
 | 
					            |> Async.AwaitTask
 | 
				
			||||||
 | 
					            |> Async.Ignore
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let attack (ctx : InteractionContext) (target : DiscordUser) =
 | 
					let attack (ctx : InteractionContext) (target : DiscordUser) =
 | 
				
			||||||
    async {
 | 
					    async {
 | 
				
			||||||
        let! attacker = DbService.tryFindPlayer ctx.Member.Id
 | 
					        let! attacker = DbService.tryFindPlayer ctx.Member.Id
 | 
				
			||||||
        let! defender = DbService.tryFindPlayer target.Id
 | 
					        let! defender = DbService.tryFindPlayer target.Id
 | 
				
			||||||
        match attacker , defender with
 | 
					        match attacker , defender with
 | 
				
			||||||
        | Some attacker , Some defender ->
 | 
					        | Some attacker , Some defender ->
 | 
				
			||||||
            let updatedAttacks =
 | 
					            let existingHack = checkForExistingHack attacker defender.DiscordId
 | 
				
			||||||
                attacker.Attacks
 | 
					            match existingHack with
 | 
				
			||||||
                |> removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp)
 | 
					            | Ok _ ->
 | 
				
			||||||
            do! DbService.updatePlayer <| { attacker with Attacks = updatedAttacks }
 | 
					 | 
				
			||||||
            if updatedAttacks.Length < 2 then
 | 
					 | 
				
			||||||
                 let embed = Embeds.pickHack "Attack" attacker defender
 | 
					                 let embed = Embeds.pickHack "Attack" attacker defender
 | 
				
			||||||
 | 
					 | 
				
			||||||
                 do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed)
 | 
					                 do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed)
 | 
				
			||||||
                     |> Async.AwaitTask
 | 
					                     |> Async.AwaitTask
 | 
				
			||||||
 | 
					            | Error msg ->
 | 
				
			||||||
            else
 | 
					                 let builder = DiscordInteractionResponseBuilder().WithContent(msg).AsEphemeral(true)
 | 
				
			||||||
                let builder = DiscordInteractionResponseBuilder()
 | 
					 | 
				
			||||||
                let timestamp = updatedAttacks |> 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 <- $"No more hacks available, please wait {timeRemaining.Minutes} minutes and {timeRemaining.Seconds} seconds to attempt another hack"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                builder.AsEphemeral true |> ignore
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                 do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
					                 do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
				
			||||||
                     |> Async.AwaitTask
 | 
					                     |> Async.AwaitTask
 | 
				
			||||||
 | 
					 | 
				
			||||||
        | None , _ -> do! notYetAHackerMsg ctx
 | 
					        | 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
 | 
					        | _ , None -> do! createSimpleResponseAsync "Your target is not connected to the network, they must join first by using the /redpill command" ctx
 | 
				
			||||||
    } |> Async.StartAsTask
 | 
					    } |> Async.StartAsTask
 | 
				
			||||||
    :> Task
 | 
					    :> Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let defend (ctx : InteractionContext) =
 | 
					 | 
				
			||||||
    async {
 | 
					 | 
				
			||||||
        let! player = DbService.tryFindPlayer ctx.Member.Id
 | 
					 | 
				
			||||||
        match player with
 | 
					 | 
				
			||||||
        | Some player ->
 | 
					 | 
				
			||||||
            let updatedDefenses = removeExpiredActions (TimeSpan.FromHours(24)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses
 | 
					 | 
				
			||||||
            do! DbService.updatePlayer <| { player with Defenses = updatedDefenses }
 | 
					 | 
				
			||||||
            if updatedDefenses.Length < 3 then
 | 
					 | 
				
			||||||
                let embed = Embeds.pickDefense "Defend" player
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed)
 | 
					 | 
				
			||||||
                    |> Async.AwaitTask
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                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)
 | 
					 | 
				
			||||||
                // 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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
					 | 
				
			||||||
                    |> Async.AwaitTask
 | 
					 | 
				
			||||||
        | None -> do! notYetAHackerMsg ctx
 | 
					 | 
				
			||||||
    } |> Async.StartAsTask
 | 
					 | 
				
			||||||
    :> Task
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let handleAttack (event : ComponentInteractionCreateEventArgs) =
 | 
					let handleAttack (event : ComponentInteractionCreateEventArgs) =
 | 
				
			||||||
    let updatePlayer amount attack p =
 | 
					 | 
				
			||||||
        { p with Attacks = Array.append [| attack |] p.Attacks ; Bank = Math.Max(p.Bank + amount, 0) }
 | 
					 | 
				
			||||||
    async {
 | 
					    async {
 | 
				
			||||||
        let split = event.Id.Split("-")
 | 
					        let split = event.Id.Split("-")
 | 
				
			||||||
        let weapon = Enum.Parse(typedefof<Hack>, split.[1]) :?> Hack
 | 
					        let hack = Enum.Parse(typedefof<Hack>, split.[1]) :?> Hack
 | 
				
			||||||
        let ( resultId , targetId ) = UInt64.TryParse split.[2]
 | 
					        let ( resultId , targetId ) = UInt64.TryParse split.[2]
 | 
				
			||||||
        let! resultPlayer = DbService.tryFindPlayer event.User.Id
 | 
					        let! resultPlayer = DbService.tryFindPlayer event.User.Id
 | 
				
			||||||
        let! resultTarget = DbService.tryFindPlayer targetId
 | 
					        let! resultTarget = DbService.tryFindPlayer targetId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // TODO: Do not let player hack themselves
 | 
				
			||||||
        match resultPlayer , resultTarget , true , resultId with
 | 
					        match resultPlayer , resultTarget , true , resultId with
 | 
				
			||||||
        | Some player , Some target , true , true ->
 | 
					        | Some attacker , Some defender , true , true ->
 | 
				
			||||||
           let updatedDefenses = removeExpiredActions (TimeSpan.FromHours(24)) (fun (p : Defense) -> p.Timestamp) target.Defenses
 | 
					            do! checkForExistingHack attacker defender.DiscordId
 | 
				
			||||||
           do! DbService.updatePlayer <| { player with Defenses = updatedDefenses }
 | 
					                |> Result.bind (checkIfHackHasCooldown hack)
 | 
				
			||||||
           let wasSuccessfulHack =
 | 
					                |> function
 | 
				
			||||||
                updatedDefenses
 | 
					                   | Ok _ ->
 | 
				
			||||||
                |> Seq.toArray
 | 
					                       runHackerBattle attacker defender hack
 | 
				
			||||||
                |> Array.map (fun dfn -> int dfn.DefenseType)
 | 
					                       |> function
 | 
				
			||||||
                |> Array.map (calculateDamage (int weapon))
 | 
					                          | false -> successfulHack event attacker defender hack
 | 
				
			||||||
                |> Array.contains Weak
 | 
					                          | true -> failedHack event attacker defender hack
 | 
				
			||||||
           match wasSuccessfulHack with
 | 
					                   | Error msg ->
 | 
				
			||||||
           | false ->
 | 
					 | 
				
			||||||
//                let prize = 1.337f // LEET
 | 
					 | 
				
			||||||
                let prize = 13
 | 
					 | 
				
			||||||
                let attack = { HackType = enum<Hack>(int weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let! _ =
 | 
					 | 
				
			||||||
                  [ DbService.updatePlayer <| updatePlayer prize attack player
 | 
					 | 
				
			||||||
                    DbService.updatePlayer { target with Bank = Math.Max(target.Bank - prize, 0)} ]
 | 
					 | 
				
			||||||
                    |> Async.Parallel
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                       let builder = DiscordInteractionResponseBuilder()
 | 
					                       let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
                builder.IsEphemeral <- true
 | 
					                       event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder.WithContent(msg))
 | 
				
			||||||
                builder.Content <- $"Successfully hacked {split.[3]} using {weapon}! You just won {prize} GoodBoyTokenz!"
 | 
					 | 
				
			||||||
                do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
 | 
					 | 
				
			||||||
                       |> Async.AwaitTask
 | 
					                       |> Async.AwaitTask
 | 
				
			||||||
 | 
					 | 
				
			||||||
                let builder = Embeds.eventSuccessfulHack event targetId prize
 | 
					 | 
				
			||||||
                let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)
 | 
					 | 
				
			||||||
                do! channel.SendMessageAsync(builder)
 | 
					 | 
				
			||||||
                    |> Async.AwaitTask
 | 
					 | 
				
			||||||
                    |> Async.Ignore
 | 
					 | 
				
			||||||
           | true ->
 | 
					 | 
				
			||||||
                let builder = DiscordInteractionResponseBuilder()
 | 
					 | 
				
			||||||
                let prize = 2
 | 
					 | 
				
			||||||
                builder.IsEphemeral <- true
 | 
					 | 
				
			||||||
                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<Hack>(int weapon) ; Timestamp = DateTime.UtcNow ; Target = { Id = targetId ; Name = split.[3] } }
 | 
					 | 
				
			||||||
                do! DbService.updatePlayer <| updatePlayer -prize attack player
 | 
					 | 
				
			||||||
                do! DbService.updatePlayer { target with Bank = target.Bank + prize }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let builder = DiscordMessageBuilder()
 | 
					 | 
				
			||||||
                builder.WithContent($"Hacking attempt failed! <@{targetId}> defended hack from {event.User.Username} and took {prize} from them! ") |> ignore
 | 
					 | 
				
			||||||
                let channel = (event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle))
 | 
					 | 
				
			||||||
                do! channel.SendMessageAsync(builder)
 | 
					 | 
				
			||||||
                    |> Async.AwaitTask
 | 
					 | 
				
			||||||
                    |> Async.Ignore
 | 
					 | 
				
			||||||
        | _ ->
 | 
					        | _ ->
 | 
				
			||||||
            let builder = DiscordInteractionResponseBuilder()
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
            builder.IsEphemeral <- true
 | 
					            builder.IsEphemeral <- true
 | 
				
			||||||
@ -135,6 +152,25 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
 | 
				
			|||||||
                    |> Async.AwaitTask
 | 
					                    |> Async.AwaitTask
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let defend (ctx : InteractionContext) =
 | 
				
			||||||
 | 
					    async {
 | 
				
			||||||
 | 
					        let! player = DbService.tryFindPlayer ctx.Member.Id
 | 
				
			||||||
 | 
					        match player with
 | 
				
			||||||
 | 
					        | Some player ->
 | 
				
			||||||
 | 
					            if player.Shields.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 store and purchase one."
 | 
				
			||||||
 | 
					                builder.AsEphemeral true |> ignore
 | 
				
			||||||
 | 
					                do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
				
			||||||
 | 
					                    |> Async.AwaitTask
 | 
				
			||||||
 | 
					        | None -> do! notYetAHackerMsg ctx
 | 
				
			||||||
 | 
					    } |> Async.StartAsTask
 | 
				
			||||||
 | 
					    :> Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let handleDefense (event : ComponentInteractionCreateEventArgs) =
 | 
					let handleDefense (event : ComponentInteractionCreateEventArgs) =
 | 
				
			||||||
    async {
 | 
					    async {
 | 
				
			||||||
        let split = event.Id.Split("-")
 | 
					        let split = event.Id.Split("-")
 | 
				
			||||||
@ -142,21 +178,46 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
 | 
				
			|||||||
        let! playerResult = DbService.tryFindPlayer event.User.Id
 | 
					        let! playerResult = DbService.tryFindPlayer event.User.Id
 | 
				
			||||||
        match playerResult , shieldResult with
 | 
					        match playerResult , shieldResult with
 | 
				
			||||||
        | Some player , true ->
 | 
					        | Some player , true ->
 | 
				
			||||||
 | 
					            // TODO: All of this is wrong
 | 
				
			||||||
 | 
					            let updatedDefenses = removeExpiredActions (TimeSpan.FromHours(6)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses
 | 
				
			||||||
 | 
					            let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.DefenseType = shield)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            match alreadyUsedShield , updatedDefenses.Length < 2 with
 | 
				
			||||||
 | 
					            | false , true ->
 | 
				
			||||||
                let builder = DiscordInteractionResponseBuilder()
 | 
					                let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
                builder.IsEphemeral <- true
 | 
					                builder.IsEphemeral <- true
 | 
				
			||||||
            builder.Content <- $"Mounted a {shield} defense for 24 hours"
 | 
					                builder.Content <- $"Mounted a {shield} defense for 6 hours"
 | 
				
			||||||
            do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
 | 
					                do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
				
			||||||
                    |> Async.AwaitTask
 | 
					                    |> Async.AwaitTask
 | 
				
			||||||
 | 
					 | 
				
			||||||
                let defense = { DefenseType = shield ; Timestamp = DateTime.UtcNow }
 | 
					                let defense = { DefenseType = shield ; Timestamp = DateTime.UtcNow }
 | 
				
			||||||
                do! DbService.updatePlayer <| { player with Defenses = Array.append [| defense |] player.Defenses  }
 | 
					                do! DbService.updatePlayer <| { player with Defenses = Array.append [| defense |] player.Defenses  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
                let builder = DiscordMessageBuilder()
 | 
					                let builder = DiscordMessageBuilder()
 | 
				
			||||||
                builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
 | 
					                builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
 | 
				
			||||||
            let channel = event.Guild.Channels.Values |> Seq.find (fun c -> c.Name = "battle-1")
 | 
					                let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)
 | 
				
			||||||
                do! channel.SendMessageAsync(builder)
 | 
					                do! channel.SendMessageAsync(builder)
 | 
				
			||||||
                    |> Async.AwaitTask
 | 
					                    |> Async.AwaitTask
 | 
				
			||||||
                    |> Async.Ignore
 | 
					                    |> Async.Ignore
 | 
				
			||||||
 | 
					            | _ , false ->
 | 
				
			||||||
 | 
					                let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					                builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					                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)
 | 
				
			||||||
 | 
					                let hours = if timeRemaining.Hours > 0 then $"{timeRemaining.Hours} hours and " else ""
 | 
				
			||||||
 | 
					                builder.Content <- $"You are only allowed two shields at a time. Wait {hours}{timeRemaining.Minutes} minutes to add another shield"
 | 
				
			||||||
 | 
					                do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
 | 
				
			||||||
 | 
					                    |> Async.AwaitTask
 | 
				
			||||||
 | 
					                do! DbService.updatePlayer <| { player with Defenses = updatedDefenses }
 | 
				
			||||||
 | 
					            | true , _ ->
 | 
				
			||||||
 | 
					                let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					                builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					                let timestamp = updatedDefenses |> Array.find (fun d -> d.DefenseType = shield) |> fun a -> a.Timestamp
 | 
				
			||||||
 | 
					                let timeRemaining = TimeSpan.FromMinutes(15) - (DateTime.UtcNow - timestamp)
 | 
				
			||||||
 | 
					                let hours = if timeRemaining.Hours > 0 then $"{timeRemaining.Hours} hours and " else ""
 | 
				
			||||||
 | 
					                builder.Content <- $"{shield} shield is already in use. Wait {hours}{timeRemaining.Minutes} minutes to use this shield again"
 | 
				
			||||||
 | 
					                do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
				
			||||||
 | 
					                    |> Async.AwaitTask
 | 
				
			||||||
 | 
					                do! DbService.updatePlayer <| { player with Defenses = updatedDefenses }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        | _ ->
 | 
					        | _ ->
 | 
				
			||||||
            let builder = DiscordInteractionResponseBuilder()
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
            builder.IsEphemeral <- true
 | 
					            builder.IsEphemeral <- true
 | 
				
			||||||
 | 
				
			|||||||
@ -106,13 +106,13 @@ module Commands =
 | 
				
			|||||||
            let! player = DbService.tryFindPlayer ctx.Member.Id
 | 
					            let! player = DbService.tryFindPlayer ctx.Member.Id
 | 
				
			||||||
            match player with
 | 
					            match player with
 | 
				
			||||||
            | Some p ->
 | 
					            | Some p ->
 | 
				
			||||||
                // TODO: Is this working?
 | 
					                let updatedAttacks = p.Attacks |> removeExpiredActions (TimeSpan.FromHours(24)) (fun (atk : Attack) -> atk.Timestamp)
 | 
				
			||||||
                let updatedAttacks = p.Attacks |> removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp)
 | 
					                let updatedDefenses = p.Defenses |> removeExpiredActions (TimeSpan.FromHours(6)) (fun (p : Defense) -> p.Timestamp)
 | 
				
			||||||
                let updatedDefenses = p.Defenses |> removeExpiredActions (TimeSpan.FromHours(24)) (fun (p : Defense) -> p.Timestamp)
 | 
					                let updatedPlayer = { p with Attacks = updatedAttacks ; Defenses = updatedDefenses }
 | 
				
			||||||
                do! DbService.updatePlayer <| { p with Attacks = updatedAttacks ; Defenses = updatedDefenses }
 | 
					                do! DbService.updatePlayer updatedPlayer
 | 
				
			||||||
                let builder = DiscordInteractionResponseBuilder()
 | 
					                let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
                builder.IsEphemeral <- true
 | 
					                builder.IsEphemeral <- true
 | 
				
			||||||
                builder.Content <- statusFormat p
 | 
					                builder.Content <- statusFormat updatedPlayer
 | 
				
			||||||
                do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
					                do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
				
			||||||
                    |> Async.AwaitTask
 | 
					                    |> Async.AwaitTask
 | 
				
			||||||
            | None -> do! notYetAHackerMsg ctx
 | 
					            | None -> do! notYetAHackerMsg ctx
 | 
				
			||||||
 | 
				
			|||||||
@ -106,13 +106,7 @@ let constructButtons (actionType: string) (playerInfo: string) (weapons: 'a arra
 | 
				
			|||||||
let removeExpiredActions timespan (timestamp: 'a -> DateTime) actions =
 | 
					let removeExpiredActions timespan (timestamp: 'a -> DateTime) actions =
 | 
				
			||||||
    actions |> Array.filter (fun act -> DateTime.UtcNow - (timestamp act) < timespan)
 | 
					    actions |> Array.filter (fun act -> DateTime.UtcNow - (timestamp act) < timespan)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let calculateDamage (hack: int) (shield: int) =
 | 
					let modifyPlayerBank player amount = { player with Bank = Math.Max(player.Bank + amount, 0) }
 | 
				
			||||||
    let hackClass = getClass hack
 | 
					 | 
				
			||||||
    let protectionClass = getClass shield
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    match hackClass, protectionClass with
 | 
					 | 
				
			||||||
    | h, p when h = p -> Weak
 | 
					 | 
				
			||||||
    | _ -> Strong
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Message =
 | 
					module Message =
 | 
				
			||||||
    let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
 | 
					    let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user