Fixes to Trainer copy, Check if defender has money, bring back status (arsenal)

This commit is contained in:
Joseph Ferano 2022-02-02 00:07:08 +07:00
parent e772a6d6f3
commit ad613064a8
5 changed files with 92 additions and 64 deletions

View File

@ -9,14 +9,7 @@ open DSharpPlus.SlashCommands
open Degenz open Degenz
open Degenz.Messaging open Degenz.Messaging
let getTimeTillCooldownFinishes (timespan : TimeSpan) timestamp = let (>>=) x f = Result.bind f x
let timeRemaining = timespan - (DateTime.UtcNow - timestamp)
if timeRemaining.Hours > 0 then
$"{timeRemaining.Hours} hours"
elif timeRemaining.Minutes > 0 then
$"{timeRemaining.Minutes} minutes"
else
$"{timeRemaining.Seconds} seconds"
let checkIfPlayerIsAttackingThemselves defender attacker = let checkIfPlayerIsAttackingThemselves defender attacker =
match attacker.DiscordId = defender.DiscordId with match attacker.DiscordId = defender.DiscordId with
@ -30,10 +23,9 @@ let checkForExistingHack defenderId attacker =
|> Array.tryFind (fun (_,t,_) -> t.Id = defenderId) |> Array.tryFind (fun (_,t,_) -> t.Id = defenderId)
|> function |> function
| Some ( atk , target , _ ) -> | Some ( atk , target , _ ) ->
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(24)) atk.Timestamp let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(2)) atk.Timestamp
Error $"You can only hack the same target once every 24 hours, wait {cooldown} to attempt another hack on {target.Name}." Error $"You can only hack the same target once every 2 hours, wait {cooldown} to attempt another hack on {target.Name}."
| None -> | None -> Ok attacker
Ok attacker
let checkIfHackHasCooldown hackId attacker = let checkIfHackHasCooldown hackId attacker =
let mostRecentHackAttack = let mostRecentHackAttack =
@ -54,6 +46,11 @@ let checkIfInventoryIsEmpty attacker =
| [||] -> Error $"You currently do not have any Hacks to steal 💰$GBT from others. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one." | [||] -> Error $"You currently do not have any Hacks to steal 💰$GBT from others. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one."
| _ -> Ok attacker | _ -> Ok attacker
let checkIfTargetHasMoney (target : PlayerData) attacker =
if target.Bank < Game.HackPrize
then Error $"{target.Name} does not have enough 💰$GBT to steal from, the broke loser. Pick a different target."
else Ok attacker
let calculateDamage (hack : BattleItem) (shield : BattleItem) = let calculateDamage (hack : BattleItem) (shield : BattleItem) =
if hack.Class = shield.Class if hack.Class = shield.Class
then Strong then Strong
@ -115,22 +112,22 @@ let attack (ctx : InteractionContext) (target : DiscordUser) =
let! defender = DbService.tryFindPlayer target.Id let! defender = DbService.tryFindPlayer target.Id
match defender with match defender with
| Some defender -> | Some defender ->
let hackAttempt = do! checkForExistingHack defender.DiscordId attacker
checkForExistingHack defender.DiscordId attacker >>= checkIfInventoryIsEmpty
|> Result.bind checkIfInventoryIsEmpty >>= (checkIfTargetHasMoney defender)
|> Result.bind (checkIfPlayerIsAttackingThemselves defender) >>= (checkIfPlayerIsAttackingThemselves defender)
match hackAttempt with |> function
| Ok _ -> | Ok _ ->
let embed = Embeds.pickHack "Attack" attacker defender let embed = Embeds.pickHack "Attack" attacker defender
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed) ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed)
|> Async.AwaitTask |> Async.AwaitTask
| Error msg -> | Error msg ->
let builder = let builder =
DiscordInteractionResponseBuilder() DiscordInteractionResponseBuilder()
.WithContent(msg) .WithContent(msg)
.AsEphemeral(true) .AsEphemeral(true)
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask |> Async.AwaitTask
| None -> do! sendSimpleResponse ctx "Your target is not connected to the network, they must join first by using the /redpill command" | None -> do! sendSimpleResponse ctx "Your target is not connected to the network, they must join first by using the /redpill command"
}) })
@ -260,7 +257,7 @@ type HackerGame() =
else else
attack ctx target attack ctx target
[<SlashCommand("shield", "Create a passive defense that will last 24 hours")>] [<SlashCommand("shield", "Create a passive shield that will protect you for a certain time")>]
member this.ShieldCommand (ctx : InteractionContext) = member this.ShieldCommand (ctx : InteractionContext) =
if ctx.Channel.Id = GuildEnvironment.channelTraining then if ctx.Channel.Id = GuildEnvironment.channelTraining then
Trainer.defend ctx Trainer.defend ctx

View File

@ -71,27 +71,12 @@ module Commands =
// } |> Async.StartAsTask // } |> Async.StartAsTask
// :> Task // :> Task
let status (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let updatedActions = Player.removeExpiredActions player.Actions
let updatedPlayer = { player with Actions = updatedActions }
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- Messaging.statusFormat updatedPlayer
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
do! DbService.updatePlayer updatedPlayer
})
type PlayerInteractions() = type PlayerInteractions() =
inherit ApplicationCommandModule () inherit ApplicationCommandModule ()
[<SlashCommand("redpill", "Take the redpill and become a hacker")>] [<SlashCommand("redpill", "Take the redpill and become a hacker")>]
member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx
[<SlashCommand("status", "Get your current status like bank account, and active hacks and defenses")>]
member this.Status (ctx : InteractionContext) = Commands.status ctx
// [<SlashCommand("leaderboard", "View the current list of players ranked by highest earnings")>] // [<SlashCommand("leaderboard", "View the current list of players ranked by highest earnings")>]
// member this.Leaderboard (ctx : InteractionContext) = Commands.leaderboard ctx // member this.Leaderboard (ctx : InteractionContext) = Commands.leaderboard ctx

View File

@ -94,12 +94,29 @@ let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCrea
} |> Async.StartAsTask } |> Async.StartAsTask
:> Task :> Task
let status (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let updatedActions = Player.removeExpiredActions player.Actions
let updatedPlayer = { player with Actions = updatedActions }
let builder = DiscordInteractionResponseBuilder()
let embed = DiscordEmbedBuilder()
embed.AddField("Arsenal", Messaging.statusFormat updatedPlayer) |> ignore
builder.AddEmbed(embed) |> ignore
builder.IsEphemeral <- true
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
do! DbService.updatePlayer updatedPlayer
})
type Store() = type Store() =
inherit ApplicationCommandModule () inherit ApplicationCommandModule ()
// [<SlashCommand("view-store", "View items available for purchase")>] // [<SlashCommand("view-store", "View items available for purchase")>]
// member _.ViewStore (ctx : InteractionContext) = viewStore ctx // member _.ViewStore (ctx : InteractionContext) = viewStore ctx
[<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
member this.Arsenal (ctx : InteractionContext) = status ctx
[<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>] [<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
member _.BuyHack (ctx : InteractionContext) = buyItem ctx Hack member _.BuyHack (ctx : InteractionContext) = buyItem ctx Hack

View File

@ -1,7 +1,7 @@
module Degenz.Trainer module Degenz.Trainer
open System.Threading.Tasks
open DSharpPlus open DSharpPlus
open System.Threading.Tasks
open DSharpPlus.Entities open DSharpPlus.Entities
open DSharpPlus.EventArgs open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands open DSharpPlus.SlashCommands
@ -39,7 +39,7 @@ let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
do! sendInteractionEvent event do! sendInteractionEvent event
("Beautopia© is a dangerous place...\n" ("Beautopia© is a dangerous place...\n"
+ "Quick, put up a DEFENSE 🛡 before another Degen hacks you, and steals your 💰$GBT.\n" + "Quick, put up a SHIELD 🛡 before another Degen hacks you, and steals your 💰$GBT.\n"
+ shieldMessage + shieldMessage
+ "To enable it, you need to run the `/shield` slash command.\n\n" + "To enable it, you need to run the `/shield` slash command.\n\n"
+ $"Type the `/shield` command now, then select - `{weaponName}`\n") + $"Type the `/shield` command now, then select - `{weaponName}`\n")
@ -60,7 +60,7 @@ let defend (ctx : InteractionContext) =
let handleDefenseMsg = { let handleDefenseMsg = {
ButtonId = "Trainer-3" ButtonId = "Trainer-3"
ButtonText = "Got it" ButtonText = "Got it"
Message = "🎉 Congratulations 🎉\n\n" Message = "🎉 Congratulations\n\n"
+ "You successfully defended my hack!\n\n" + "You successfully defended my hack!\n\n"
+ "Because I tried hacking you when you had your defense up, you stole money from me...\n" + "Because I tried hacking you when you had your defense up, you stole money from me...\n"
+ "Defenses only protect you for a LIMITED TIME, so remember to keep at least 1 defense up at all times, or risk getting hacked!" + "Defenses only protect you for a LIMITED TIME, so remember to keep at least 1 defense up at all times, or risk getting hacked!"
@ -77,7 +77,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
do! Async.Sleep 2000 do! Async.Sleep 2000
do! sendMessage' "Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now" do! sendMessage' "Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now"
do! Async.Sleep 4000 do! Async.Sleep 4000
do! sendMessage' $"❌ HACKING FAILED! \n\n{player.Name} defended the hack from <@{GuildEnvironment.botHackerBattle}>, and stole 💰$GBT {Game.ShieldPrize} from them!" do! sendMessage' $"❌ HACKING FAILED!\n\n{player.Name} defended the hack from <@{GuildEnvironment.botHackerBattle}>, and stole 💰$GBT {Game.ShieldPrize} from them!"
do! Async.Sleep 3000 do! Async.Sleep 3000
do! sendFollowUpMessageWithButton event handleDefenseMsg do! sendFollowUpMessageWithButton event handleDefenseMsg
}) })
@ -133,15 +133,17 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
let embed = Embeds.responseSuccessfulHackTrainer hack let embed = Embeds.responseSuccessfulHackTrainer hack
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
do! Async.Sleep 3000 do! Async.Sleep 3000
do! sendMessage' ("🎉 **Congratulations** 🎉\n\n" do! sendMessage'
+ "You successfully **HACKED** me, and are now an **Elite Haxor!**\n\n" ("🎉 **Congratulations**\n\n"
+ "When you **HACK** other Degenz, you **STEAL** their 💰**$GBT.**\n" + "You successfully **HACKED** me, and are now an **Elite Haxor!**\n\n"
+ "But remember, hacks take time to recover, so use them wisely.") + "When you **HACK** other Degenz, you **STEAL** their 💰$GBT.\n"
+ "But remember, hacks take time to recover, so use them wisely.")
do! Async.Sleep 7000 do! Async.Sleep 7000
do! sendFollowUpMessage event ("Your training is **complete!**\n\n" do! sendFollowUpMessage event
+ "But, youre going to need more **HACKS & DEFENSES** 🛡 to survive in **Beautopia©...**\n" ("Your training is **complete!**\n\n"
+ $"You can purchase them from <@{GuildEnvironment.botArmory}>, at <#{GuildEnvironment.channelArmory}> anytime you want...\n\n" + "But, youre going to need more **HACKS & SHIELDS** 🛡 to survive in **Beautopia©...**\n"
+ "Go there **NOW** to buy **HACKS** & **SHIELDS** before you get hacked! 😱") + $"You can purchase them from <@{GuildEnvironment.botArmory}> anytime you want...\n\n"
+ $"Go to the <#{GuildEnvironment.channelArmory}> **NOW** and type the `/buy-shield` slash command! 😱")
}) })
let handleButtonEvent (event : ComponentInteractionCreateEventArgs) = let handleButtonEvent (event : ComponentInteractionCreateEventArgs) =

View File

@ -76,7 +76,6 @@ module Types =
Actions : Action array Actions : Action array
Bank : int<GBT> } Bank : int<GBT> }
module Armory = module Armory =
let battleItems = let battleItems =
let file = System.IO.File.ReadAllText("Items.json") let file = System.IO.File.ReadAllText("Items.json")
@ -89,9 +88,10 @@ module Player =
let shields player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Shield) let shields player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Shield)
let attacks player = let attacks player =
player.Actions player.Actions
|> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None) |> Array.filter (fun act -> match act.Type with Attack _ -> true | _ -> false)
let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense _ -> true | _ -> false) let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false)
// TODO: I have to fix this, this will remove hacks before the single target cooldown expires
let removeExpiredActions actions = let removeExpiredActions actions =
actions actions
|> Array.filter (fun (act : Action) -> |> Array.filter (fun (act : Action) ->
@ -110,12 +110,39 @@ module Messaging =
Message : string Message : string
} }
let getTimeTillCooldownFinishes (timespan : TimeSpan) timestamp =
let timeRemaining = timespan - (DateTime.UtcNow - timestamp)
if timeRemaining.Hours > 0 then
$"{timeRemaining.Hours} hours"
elif timeRemaining.Minutes > 0 then
$"{timeRemaining.Minutes} minutes"
else
$"{timeRemaining.Seconds} seconds"
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"
| _ ->
actions
|> Array.map (fun act ->
match act.Type with
| Attack atk -> $"Hacked {atk.Target} at {act.Timestamp.ToLongDateString()}"
| Defense ->
let item = Armory.getItem act.ActionId
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(int item.Cooldown))
$"{item.Name} active for {cooldown}")
|> String.concat "\n"
let statusFormat p = let statusFormat p =
$"Hacks: {Player.hacks p |> Array.toList} $"**Hacks:** {Player.hacks p |> battleItemFormat}\n
Shields: {Player.shields p |> Array.toList} **Shields:** {Player.shields p |> battleItemFormat}\n
Hack Attacks: {Player.attacks p |> Array.toList} **Hack Attacks:** {Player.attacks p |> actionFormat}\n
Active Defenses: {Player.defenses p |> Array.toList} **Active Shields:** {Player.defenses p |> actionFormat}"
Bank: {p.Bank}"
let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) = let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) =
weapons weapons