Anti spam cooldown for player and a 12 hour global cooldown for victim

This commit is contained in:
Joseph Ferano 2022-02-13 20:12:04 +07:00
parent 7e2b46a96b
commit 04add71c6e
7 changed files with 137 additions and 74 deletions

View File

@ -41,15 +41,17 @@ module Game =
| None -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
} |> Async.StartAsTask :> Task
let executePlayerWithTargetAction (targetPlayer : DiscordUser) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
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! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id)
let! targetResult = tryFindPlayer targetPlayer.Id
match playerResult , targetResult with
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 ->
@ -58,6 +60,22 @@ module Game =
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 (targetId : uint64) (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 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.Arsenal |> Array.filter (fun i -> i.Type = itemType)
let getHacks (player : PlayerData) = getItems ItemType.Hack player

View File

@ -9,8 +9,6 @@ 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."
@ -120,7 +118,7 @@ let failedHack (ctx : IDiscordContext) attacker defender hack =
}
let attack (target : DiscordUser) (ctx : IDiscordContext) =
Game.executePlayerWithTargetAction target ctx (fun attacker defender -> async {
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async {
do! attacker
|> checkAlreadyHackedTarget defender.DiscordId
<!> (Player.removeExpiredActions true)
@ -180,7 +178,7 @@ let handleDefense (ctx : IDiscordContext) =
|> checkPlayerOwnsWeapon shieldId
>>= checkPlayerHasShieldSlotsAvailable shield
>>= checkItemHasCooldown shieldId
|> handleResultWithResponseFromEvent ctx (fun p -> async {
|> handleResultWithResponse ctx (fun p -> async {
let embed = Embeds.responseCreatedShield (Armory.getItem shieldId)
do! ctx.FollowUp embed |> Async.AwaitTask
let defense = { ActionId = shieldId ; Type = Defense ; Timestamp = DateTime.UtcNow }

View File

@ -28,7 +28,7 @@ let player1Won p1m p2m =
| _ , _ -> Draw
let playRPS target ctx =
Game.executePlayerWithTargetAction target ctx (fun attacker defender -> async {
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async {
return ()
})

View File

@ -49,7 +49,7 @@ let handleBuyItem (ctx : IDiscordContext) itemId =
do! player
|> checkHasSufficientFunds item
>>= checkAlreadyOwnsItem item
|> handleResultWithResponseFromEvent ctx (fun player -> async {
|> handleResultWithResponse ctx (fun player -> async {
let newBalance = player.Bank - item.Cost
let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
do! DbService.updatePlayer p
@ -62,7 +62,7 @@ let handleSell (ctx : IDiscordContext) itemId =
let item = Armory.getItem itemId
do! player
|> checkSoldItemAlready item
|> handleResultWithResponseFromEvent ctx (fun player -> async {
|> handleResultWithResponse ctx (fun player -> async {
let updatedPlayer = {
player with
Bank = player.Bank + item.Cost

View File

@ -7,6 +7,14 @@ open DSharpPlus.Entities
open DSharpPlus.SlashCommands
open Degenz.Messaging
[<Literal>]
let StealActionId = 12
[<Literal>]
let VictimDefenseActionId = 12
let ThiefCooldown = TimeSpan.FromMinutes(1)
let VictimRecovery = TimeSpan.FromHours(12)
type StealResult =
| Success
| WentToPrison
@ -78,49 +86,92 @@ let getResultEmbed targetName result =
.WithImageUrl(img)
.WithTitle($"Robbery Results")
let steal target (ctx : IDiscordContext) =
Game.executePlayerWithTargetAction target ctx (fun attacker defender -> async {
let ``base`` = 0.5
let winPercentage = double (attacker.Stats.Strength - defender.Stats.Strength) * 0.45 + ``base``
let prize = payout (float defender.Bank) winPercentage
let embed = getStealEmbed winPercentage prize defender
let checkVictimStealingCooldown defender attacker =
defender
|> Player.getDefenses
|> Array.tryFind (fun act -> act.ActionId = VictimDefenseActionId)
|> function
| Some act ->
let cooldown = VictimRecovery - (DateTime.UtcNow - act.Timestamp)
let hours = if cooldown.Hours = 0 then "hour" else $"{cooldown.Hours} hours"
Error $"{defender.Name} was robbed recently so they won't be going out for at least another {hours}."
| None -> Ok attacker
do! ctx.FollowUp(embed) |> Async.AwaitTask
// TODO: Look for ways to generalize checking for action cooldowns
let checkThiefCooldown attacker =
attacker
|> Player.getAttacks
|> Array.tryFind (fun act -> act.ActionId = StealActionId)
|> function
| Some act ->
if ThiefCooldown > (DateTime.UtcNow - act.Timestamp) then
let cooldown = ThiefCooldown - (DateTime.UtcNow - act.Timestamp)
let minutes = if cooldown.Minutes = 0 then "minute" else $"{cooldown.Minutes} minutes"
Error $"Whoa there you clepto, wait at least another {minutes} before you try stealing again."
else
Ok attacker
| None -> Ok attacker
let steal target (ctx : IDiscordContext) =
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async {
do! attacker
|> checkVictimStealingCooldown defender
>>= checkThiefCooldown
|> handleResultWithResponse ctx (fun player -> async {
let ``base`` = 0.5
let winPercentage = double (attacker.Stats.Strength - defender.Stats.Strength) * 0.45 + ``base``
let prize = payout (float defender.Bank) winPercentage
let embed = getStealEmbed winPercentage prize defender
do! ctx.FollowUp(embed) |> Async.AwaitTask
})
})
let handleSteal (ctx : IDiscordContext) =
let split = ctx.GetInteractionId().Split("-")
let answer = split.[1]
let handleYes player = async {
let targetId = uint64 split.[2]
let targetName = split.[3]
let chance = double split.[4]
let prize = int split.[5] * 1<GBT>
let rand = Random(Guid.NewGuid().GetHashCode())
let result = chance >= rand.NextDouble() , rand.Next(0,3) = 0
match result with
| true , _ ->
let xp = 15
let embed = getResultEmbed targetName Success
embed.AddField("$GBT Stolen", string prize) |> ignore
embed.AddField("XP Gained", $"{xp}+") |> ignore
do! Messaging.sendFollowUpEmbed ctx (embed.Build())
match! DbService.tryFindPlayer targetId with
| Some t ->
let action = { ActionId = VictimDefenseActionId ; Type = Defense ; Timestamp = DateTime.UtcNow }
do! DbService.updatePlayer { t with Bank = max (t.Bank - prize) 0<GBT> ; Actions = Array.append [| action |] t.Actions }
| None -> ()
let dp = { DiscordPlayer.Id = targetId ; DiscordPlayer.Name = targetName }
let action = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = true ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow }
do! DbService.updatePlayer { player with Bank = player.Bank + prize ; XP = player.XP + xp ; Actions = Array.append [| action |] player.Actions }
| false , false ->
let embed = getResultEmbed targetName TargetRanAway
do! Messaging.sendFollowUpEmbed ctx (embed.Build())
| false , true ->
let embed = getResultEmbed targetName WentToPrison
do! Messaging.sendFollowUpEmbed ctx (embed.Build())
do! Async.Sleep 5000
let role = ctx.GetGuild().GetRole(GuildEnvironment.rolePrisoner)
do! ctx.GetDiscordMember().GrantRoleAsync(role) |> Async.AwaitTask
}
if answer = "yes" then
Game.executePlayerAction ctx (fun player -> async {
let targetId = uint64 split.[2]
let targetName = split.[3]
let chance = double split.[4]
let prize = int split.[5] * 1<GBT>
let rand = Random(Guid.NewGuid().GetHashCode())
let result = chance >= rand.NextDouble() , rand.Next(0,3) = 0
match result with
| true , _ ->
let xp = 15
let embed = getResultEmbed targetName Success
embed.AddField("$GBT Stolen", string prize) |> ignore
embed.AddField("XP Gained", $"{xp}+") |> ignore
do! Messaging.sendFollowUpEmbed ctx (embed.Build())
match! DbService.tryFindPlayer targetId with
| Some t -> do! DbService.updatePlayer { t with Bank = max (t.Bank - prize) 0<GBT> }
| None -> ()
do! DbService.updatePlayer { player with Bank = player.Bank + prize ; XP = player.XP + xp }
| false , false ->
let embed = getResultEmbed targetName TargetRanAway
do! Messaging.sendFollowUpEmbed ctx (embed.Build())
| false , true ->
let embed = getResultEmbed targetName WentToPrison
do! Messaging.sendFollowUpEmbed ctx (embed.Build())
do! Async.Sleep 5000
let role = ctx.GetGuild().GetRole(GuildEnvironment.rolePrisoner)
do! ctx.GetDiscordMember().GrantRoleAsync(role) |> Async.AwaitTask
let targetId = uint64 split.[2]
Game.executePlayerActionWithTargetId targetId ctx (fun attacker defender -> async {
do! attacker
|> checkVictimStealingCooldown defender
>>= checkThiefCooldown
|> handleResultWithResponse ctx handleYes
})
else
async {

View File

@ -169,28 +169,29 @@ let handleAttack (ctx : IDiscordContext) =
sb.AppendLine("To finish your training and collect the loot, type the `/arsenal` command **NOW**") |> ignore
do! Async.Sleep 1000
let updatedPlayer = {
player with Bank = player.Bank + hackMoney + shieldMoney
Actions = [
{ Action.Timestamp = System.DateTime.UtcNow
Action.Type =
Attack {
Result = true
Target = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
}
ActionId = defaultHack.Id
}
if not hasShields && Array.exists (fun act -> act.ActionId = freeShield.Id) player.Actions |> not then {
Action.Timestamp = System.DateTime.UtcNow
Action.Type = Defense
Action.ActionId = freeShield.Id
}
] |> Seq.toArray
|> Array.append player.Actions
Arsenal = [
if not hasHacks then freeHack
if not hasShields then freeShield
] |> Seq.toArray
|> Array.append player.Arsenal
player with
Bank = player.Bank + hackMoney + shieldMoney
Actions = [
{ Action.Timestamp = System.DateTime.UtcNow
Action.Type =
Attack {
Result = true
Target = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
}
ActionId = defaultHack.Id
}
if not hasShields && Array.exists (fun act -> act.ActionId = freeShield.Id) player.Actions |> not then {
Action.Timestamp = System.DateTime.UtcNow
Action.Type = Defense
Action.ActionId = freeShield.Id
}
] |> Seq.toArray
|> Array.append player.Actions
Arsenal = [
if not hasHacks then freeHack
if not hasShields then freeShield
] |> Seq.toArray
|> Array.append player.Arsenal
}
do! DbService.updatePlayer updatedPlayer
do! sendFollowUpMessage ctx (sb.ToString())

View File

@ -228,8 +228,3 @@ module Messaging =
| Ok p -> fn p
| Error e -> async { do! sendFollowUpMessage ctx e }
let handleResultWithResponseFromEvent event fn (player : Result<PlayerData, string>) =
match player with
| Ok p -> fn p
| Error e -> async { do! sendFollowUpMessage event e }