134 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Forth
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Forth
		
	
	
	
	
	
| 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<GBT>
 | |
|     let ShieldPrize = 5<GBT>
 | |
| 
 | |
|     let SameTargetAttackCooldown = System.TimeSpan.FromHours(6)
 | |
| 
 | |
|     let executePlayerAction (ctx : IDiscordContext) (dispatch : PlayerData -> Async<unit>) =
 | |
|         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.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
 | |
|         } |> Async.StartAsTask :> Task
 | |
| 
 | |
|     let executePlayerActionWithTarget (targetPlayer : DiscordUser) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
 | |
|         async {
 | |
|             let builder = DiscordInteractionResponseBuilder()
 | |
|             builder.IsEphemeral <- true
 | |
|             builder.Content <- "Content"
 | |
|             do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
 | |
|             let! players =
 | |
|                 [ tryFindPlayer (ctx.GetDiscordMember().Id)
 | |
|                   tryFindPlayer targetPlayer.Id ]
 | |
|                 |> Async.Parallel
 | |
|             match players.[0] , players.[1] with
 | |
|             | Some player , Some target -> do! dispatch player target
 | |
|             | None , _ -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
 | |
|             | _ , None ->
 | |
|                 if targetPlayer.IsBot
 | |
|                     then do! Messaging.sendFollowUpMessage ctx $"{targetPlayer.Username} is a bot, pick a real human to hack"
 | |
|                     else do! Messaging.sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command"
 | |
|         } |> Async.StartAsTask :> Task
 | |
| 
 | |
|     let executePlayerActionWithTargetId defer (targetId : uint64) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
 | |
|         async {
 | |
|             let builder = DiscordInteractionResponseBuilder()
 | |
|             builder.IsEphemeral <- true
 | |
|             builder.Content <- "Content"
 | |
|             if defer then
 | |
|                 do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
 | |
|             let! players =
 | |
|                 [ tryFindPlayer (ctx.GetDiscordMember().Id)
 | |
|                   tryFindPlayer targetId ]
 | |
|                 |> Async.Parallel
 | |
|             match players.[0] , players.[1] with
 | |
|             | Some player , Some target -> do! dispatch player target
 | |
|             | None , _ -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
 | |
|             | _ , None -> do! Messaging.sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command"
 | |
|         } |> Async.StartAsTask :> Task
 | |
| 
 | |
| module Player =
 | |
|     let getItems itemType (player : PlayerData) = player.Inventory |> Array.filter (fun i -> i.Type = itemType)
 | |
|     let getHacks (player : PlayerData) = getItems ItemType.Hack player
 | |
|     let getShields (player : PlayerData) = getItems ItemType.Shield player
 | |
|     let getHackEvents player =
 | |
|         player.Events
 | |
|         |> Array.filter (fun act -> match act.Type with PlayerEventType.Hacking -> true | _ -> false || act.ItemId < 12)
 | |
|     let getShieldEvents player =
 | |
|         player.Events
 | |
|         |> Array.filter (fun act -> match act.Type with PlayerEventType.Shielding -> true | _ -> false || act.ItemId < 12)
 | |
| 
 | |
|     // TODO: This parameter is a result of putting the cooldown on the attack side. Put the cooldown on the defender
 | |
|     // side and only check if it's the same target, we need to refactor Actions
 | |
|     let removeExpiredActions filterByAttackCooldown player =
 | |
|         let actions =
 | |
|             player.Events
 | |
|             |> Array.filter (fun (act : PlayerEvent) ->
 | |
|                 let itemCooldown =
 | |
|                     if act.ItemId < 12 then
 | |
|                         (Armory.getItem act.ItemId).Cooldown
 | |
|                     else
 | |
|                         match act.Type with
 | |
|                         | PlayerEventType.Steal -> 1<mins>
 | |
|                         | _ -> 720<mins>
 | |
|                     |> int
 | |
| 
 | |
|                 match act.Type , filterByAttackCooldown with
 | |
|                 | PlayerEventType.Hacking    , true  -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(itemCooldown)
 | |
|                 | PlayerEventType.Hacking    , false -> System.DateTime.UtcNow - act.Timestamp < Game.SameTargetAttackCooldown
 | |
|                 | PlayerEventType.Shielding  , _     -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(itemCooldown)
 | |
|                 | _ -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(itemCooldown))
 | |
|         { player with Events = actions }
 | |
| 
 | |
|     let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
 | |
| 
 | |
| module Arsenal =
 | |
|     let battleItemFormat (items : Item array) =
 | |
|         match items with
 | |
|         | [||] -> "None"
 | |
|         | _ -> items |> Array.toList |> List.map (fun i -> i.Name) |> String.concat ", "
 | |
| 
 | |
|     let actionFormat (actions : PlayerEvent array) =
 | |
|         match actions with
 | |
|         | [||] -> "None"
 | |
|         | _ ->
 | |
|             let hacks , defenses =
 | |
|                 actions
 | |
|                 |> Array.filter (fun act -> act.ItemId < 12)
 | |
|                 |> Array.partition (fun act -> match act.Type with PlayerEventType.Hacking -> true | _ -> false)
 | |
|             let hacks = hacks |> Array.take (min hacks.Length 10)
 | |
|             hacks
 | |
|             |> Array.append defenses
 | |
|             |> Array.map (fun act ->
 | |
|                 let item = Armory.getItem act.ItemId
 | |
|                 match act.Type with
 | |
|                 | PlayerEventType.Hacking ->
 | |
|                     let cooldown = Messaging.getTimeText false Game.SameTargetAttackCooldown act.Timestamp
 | |
|                     $"Hacked {act.Adversary.Name} {cooldown} ago"
 | |
|                 | _ ->
 | |
|                     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.getHackEvents p |> actionFormat}\n
 | |
|     **Active Shields:**\n{Player.getShieldEvents p |> actionFormat}"
 | |
| 
 |