Major refactor for new BattleItem/Action data models
This commit is contained in:
parent
d1cf329521
commit
7c8a460d5b
102
Bot/Embeds.fs
102
Bot/Embeds.fs
@ -5,23 +5,27 @@ open Degenz.Shared
|
||||
open DSharpPlus.Entities
|
||||
open AsciiTableFormatter
|
||||
|
||||
let hackGif = "https://s10.gifyu.com/images/Hacker-Degenz-V2.gif"
|
||||
let shieldGif = "https://s10.gifyu.com/images/Defense-Degenz-V2.gif"
|
||||
|
||||
let getHackGif = function
|
||||
| Hack.Virus -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| Hack.Ransom -> "https://s10.gifyu.com/images/Mind-Control-Degenz-V2.gif"
|
||||
| Hack.Worm -> "https://s10.gifyu.com/images/WormBugAttack_Degenz.gif"
|
||||
| Hack.DDos -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| Hack.Crack -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| Hack.Injection -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| _ -> "https://s10.gifyu.com/images/Hacker-Degenz-V2.gif"
|
||||
| HackId.Virus -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| HackId.Ransom -> "https://s10.gifyu.com/images/Mind-Control-Degenz-V2.gif"
|
||||
| HackId.Worm -> "https://s10.gifyu.com/images/WormBugAttack_Degenz.gif"
|
||||
| HackId.DDos -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| HackId.Crack -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| HackId.Injection -> "https://s10.gifyu.com/images/Attack-DegenZ.gif"
|
||||
| _ -> hackGif
|
||||
|
||||
let getShieldGif = function
|
||||
| Shield.Firewall -> "https://s10.gifyu.com/images/Defense-GIF-1-Degenz.gif"
|
||||
| Shield.PortScan -> "https://s10.gifyu.com/images/PortScanDefense_Degenz.gif"
|
||||
| Shield.Encryption -> "https://s10.gifyu.com/images/Anonymous-Degenz-V2.gif"
|
||||
| Shield.Hardening -> "https://s10.gifyu.com/images/Encryption-Degenz-V2.gif"
|
||||
| Shield.Sanitation -> "https://s10.gifyu.com/images/VPN-Degenz.gif"
|
||||
| Shield.Cypher -> "https://s10.gifyu.com/images/Matrix_Degenz.gif"
|
||||
| _ -> "https://s10.gifyu.com/images/Hacker-Degenz-V2.gif"
|
||||
| ShieldId.Firewall -> "https://s10.gifyu.com/images/Defense-GIF-1-Degenz.gif"
|
||||
| ShieldId.PortScan -> "https://s10.gifyu.com/images/PortScanDefense_Degenz.gif"
|
||||
| ShieldId.Encryption -> "https://s10.gifyu.com/images/Anonymous-Degenz-V2.gif"
|
||||
| ShieldId.Hardening -> "https://s10.gifyu.com/images/Encryption-Degenz-V2.gif"
|
||||
| ShieldId.Sanitation -> "https://s10.gifyu.com/images/VPN-Degenz.gif"
|
||||
| ShieldId.Cypher -> "https://s10.gifyu.com/images/Matrix_Degenz.gif"
|
||||
| _ -> shieldGif
|
||||
|
||||
|
||||
let constructEmbed message =
|
||||
let builder = DiscordEmbedBuilder()
|
||||
@ -36,14 +40,14 @@ let constructEmbed message =
|
||||
|
||||
let pickDefense actionId player =
|
||||
let buttons =
|
||||
constructButtons actionId (string player.DiscordId) player.Shields
|
||||
constructButtons actionId (string player.DiscordId) (Player.shields player)
|
||||
|> Seq.cast<DiscordComponent>
|
||||
|
||||
let embed =
|
||||
DiscordEmbedBuilder()
|
||||
.WithColor(DiscordColor.Blurple)
|
||||
.WithDescription("Pick a defense to mount for 10 hours")
|
||||
.WithImageUrl("https://s10.gifyu.com/images/Defense-Degenz-V2.gif")
|
||||
.WithImageUrl(shieldGif)
|
||||
|
||||
DiscordInteractionResponseBuilder()
|
||||
.AddComponents(buttons)
|
||||
@ -52,37 +56,61 @@ let pickDefense actionId player =
|
||||
|
||||
let pickHack actionId attacker defender =
|
||||
let buttons =
|
||||
constructButtons actionId $"{defender.DiscordId}-{defender.Name}" attacker.Weapons
|
||||
constructButtons actionId $"{defender.DiscordId}-{defender.Name}" (Player.hacks attacker)
|
||||
|> Seq.cast<DiscordComponent>
|
||||
|
||||
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")
|
||||
.WithImageUrl(hackGif)
|
||||
|
||||
DiscordInteractionResponseBuilder()
|
||||
.AddComponents(buttons)
|
||||
.AddEmbed(embed.Build())
|
||||
.AsEphemeral true
|
||||
|
||||
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")
|
||||
let responseSuccessfulHack defenderName hack prize =
|
||||
let embed = DiscordEmbedBuilder()
|
||||
embed.ImageUrl <- getHackGif hack
|
||||
|
||||
DiscordInteractionResponseBuilder()
|
||||
.WithContent($"Successfully hacked {defenderName} using {hack}! You just won {prize} GoodBoyTokenz!")
|
||||
.AddEmbed(embed.Build())
|
||||
.AsEphemeral(true)
|
||||
|
||||
let responseSuccessfulHackTrainer defenderName (hack : BattleItem) prize =
|
||||
let embed = DiscordEmbedBuilder()
|
||||
embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id))
|
||||
|
||||
DiscordFollowupMessageBuilder()
|
||||
.WithContent($"Successfully hacked {defenderName} using {hack}! You just won {prize} GoodBoyTokenz!")
|
||||
.AddEmbed(embed.Build())
|
||||
.AsEphemeral(true)
|
||||
|
||||
let responseCreatedShield shield =
|
||||
DiscordInteractionResponseBuilder()
|
||||
.AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif shield))
|
||||
.AsEphemeral(true)
|
||||
.WithContent($"Mounted a {shield} defense for 6 hours")
|
||||
|
||||
let responseCreatedShieldTrainer (shield : BattleItem) =
|
||||
DiscordFollowupMessageBuilder()
|
||||
.AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id))))
|
||||
.AsEphemeral(true)
|
||||
.WithContent($"Mounted a {shield.Name} defense for 6 hours")
|
||||
|
||||
let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) targetId prize =
|
||||
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")
|
||||
|
||||
// let embed =
|
||||
// DiscordEmbedBuilder()
|
||||
// .WithColor(DiscordColor.Blurple)
|
||||
// .WithDescription("Pick the hack that you want to use")
|
||||
// .WithImageUrl(hackGif)
|
||||
//
|
||||
DiscordMessageBuilder()
|
||||
.WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
|
||||
|
||||
@ -96,20 +124,10 @@ type Table = {
|
||||
let storeListing store =
|
||||
let embeds =
|
||||
store
|
||||
|> Array.groupBy (fun (bi : BattleItem) -> bi.Type)
|
||||
|> Array.map (fun ( itemType , items ) ->
|
||||
items
|
||||
|> Array.map (fun (item : Item) ->
|
||||
let itemClass =
|
||||
if itemType = ItemType.Hack
|
||||
then hackInventory
|
||||
|> Array.find (fun w -> item.Name = string w)
|
||||
|> int
|
||||
|> getClass
|
||||
else shieldInventory
|
||||
|> Array.find (fun w -> item.Name = string w)
|
||||
|> int
|
||||
|> getClass
|
||||
{ Name = item.Name ; Cost = string item.Cost ; Class = string itemClass })
|
||||
|> Array.map (fun item -> { Name = item.Name ; Cost = string item.Cost ; Class = string item.Class })
|
||||
|> Formatter.Format
|
||||
|> sprintf "**%As**\n``` %s ```" itemType
|
||||
|> constructEmbed)
|
||||
|
@ -25,22 +25,21 @@ let checkIfPlayerIsAttackingThemselves defender attacker =
|
||||
| false -> Ok attacker
|
||||
|
||||
let checkForExistingHack defenderId attacker =
|
||||
let updatedAttacks =
|
||||
attacker.Attacks
|
||||
|> removeExpiredActions (TimeSpan.FromHours(24)) (fun atk -> atk.Timestamp)
|
||||
updatedAttacks
|
||||
|> Array.tryFind (fun a -> a.Target.Id = defenderId)
|
||||
attacker.Actions
|
||||
|> removeExpiredActions
|
||||
|> getAttacksFlat
|
||||
|> Array.tryFind (fun (_,t,_) -> t.Id = defenderId)
|
||||
|> function
|
||||
| Some attack ->
|
||||
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(24)) attack.Timestamp
|
||||
Error $"You can only hack the same target once every 24 hours, wait {cooldown} to attempt another hack on {attack.Target.Name}."
|
||||
| Some ( atk , target , _ ) ->
|
||||
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(24)) atk.Timestamp
|
||||
Error $"You can only hack the same target once every 24 hours, wait {cooldown} to attempt another hack on {target.Name}."
|
||||
| None ->
|
||||
Ok attacker
|
||||
|
||||
let checkIfHackHasCooldown hack attacker =
|
||||
let checkIfHackHasCooldown hackId attacker =
|
||||
let mostRecentHackAttack =
|
||||
attacker.Attacks
|
||||
|> Array.tryFind (fun a -> a.HackType = hack)
|
||||
attacker.Actions
|
||||
|> Array.tryFind (fun a -> a.ActionId = hackId)
|
||||
|> function
|
||||
| Some a -> a.Timestamp
|
||||
| None -> DateTime.MinValue
|
||||
@ -48,42 +47,40 @@ let checkIfHackHasCooldown hack attacker =
|
||||
Ok attacker
|
||||
else
|
||||
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(5)) mostRecentHackAttack
|
||||
Error $"{hack} is currently on cooldown, wait {cooldown} to use it again."
|
||||
let item = armoury |> Array.find (fun i -> i.Id = hackId)
|
||||
Error $"{item.Name} is currently on cooldown, wait {cooldown} to use it again."
|
||||
|
||||
let checkIfInventoryIsEmpty attacker =
|
||||
match attacker.Weapons with
|
||||
match attacker.Arsenal with
|
||||
| [||] -> Error $"You currently do not have any Hacks to use against others. Please go to the store and purchase one."
|
||||
| _ -> Ok attacker
|
||||
|
||||
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 calculateDamage (hack : BattleItem) (shield : BattleItem) =
|
||||
if hack.Power > shield.Power
|
||||
then Strong
|
||||
else Weak
|
||||
|
||||
let runHackerBattle 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))
|
||||
Player.defenses defender
|
||||
|> removeExpiredActions
|
||||
|> Array.map (fun dfn -> armoury |> Array.find (fun w -> w.Id = dfn.ActionId))
|
||||
|> Array.map (calculateDamage (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 } }
|
||||
let updatePlayer amount attack p =
|
||||
{ p with Actions = Array.append [| attack |] p.Actions ; Bank = max (p.Bank + amount) 0<GBT> }
|
||||
let target = { Id = defender.DiscordId ; Name = defender.Name }
|
||||
let attack = { ActionId = int hack ; Type = Attack ( target , prize > 0<GBT> ) ; Timestamp = DateTime.UtcNow }
|
||||
|
||||
[ DbService.updatePlayer <| updatePlayer prize attack attacker
|
||||
DbService.updatePlayer <| modifyPlayerBank defender -prize ]
|
||||
|> Async.Parallel
|
||||
|> Async.Ignore
|
||||
[ 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
|
||||
let prize = 3<GBT>
|
||||
|
||||
do! updateCombatants attacker defender hack prize
|
||||
|
||||
@ -101,7 +98,7 @@ let successfulHack (event : ComponentInteractionCreateEventArgs) attacker defend
|
||||
let failedHack (event : ComponentInteractionCreateEventArgs) attacker defender hack =
|
||||
async {
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
let prize = 2
|
||||
let prize = 2<GBT>
|
||||
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.ChannelMessageWithSource, builder)
|
||||
@ -147,7 +144,7 @@ let attack (ctx : InteractionContext) (target : DiscordUser) =
|
||||
let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
||||
async {
|
||||
let split = event.Id.Split("-")
|
||||
let hack = Enum.Parse(typedefof<Hack>, split.[1]) :?> Hack
|
||||
let hack = enum<HackId>(int split.[1])
|
||||
let ( resultId , targetId ) = UInt64.TryParse split.[2]
|
||||
let! resultPlayer = DbService.tryFindPlayer event.User.Id
|
||||
let! resultTarget = DbService.tryFindPlayer targetId
|
||||
@ -155,10 +152,10 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
||||
match resultPlayer , resultTarget , true , resultId with
|
||||
| Some attacker , Some defender , true , true ->
|
||||
do! checkForExistingHack defender.DiscordId attacker
|
||||
|> Result.bind (checkIfHackHasCooldown hack)
|
||||
|> Result.bind (checkIfHackHasCooldown (int hack))
|
||||
|> function
|
||||
| Ok _ ->
|
||||
runHackerBattle defender hack
|
||||
runHackerBattle defender (getItemFromArmoury <| int hack)
|
||||
|> function
|
||||
| false -> successfulHack event attacker defender hack
|
||||
| true -> failedHack event attacker defender hack
|
||||
@ -182,13 +179,13 @@ let defend (ctx : InteractionContext) =
|
||||
let! player = DbService.tryFindPlayer ctx.Member.Id
|
||||
match player with
|
||||
| Some player ->
|
||||
if player.Shields.Length > 0 then
|
||||
if Player.defenses player |> Array.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.Content <- $"You currently do not have any Shields to protect your system. Please go to the armoury and purchase one."
|
||||
builder.AsEphemeral true |> ignore
|
||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||
|> Async.AwaitTask
|
||||
@ -199,20 +196,20 @@ let defend (ctx : InteractionContext) =
|
||||
let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
||||
async {
|
||||
let split = event.Id.Split("-")
|
||||
let ( shieldResult , shield ) = Shield.TryParse(split.[1])
|
||||
let shield = enum<ShieldId>(int split.[1])
|
||||
let! playerResult = DbService.tryFindPlayer event.User.Id
|
||||
match playerResult , shieldResult with
|
||||
| Some player , true ->
|
||||
let updatedDefenses = removeExpiredActions (TimeSpan.FromHours(6)) (fun (pro : Defense) -> pro.Timestamp) player.Defenses
|
||||
let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.DefenseType = shield)
|
||||
match playerResult with
|
||||
| Some player ->
|
||||
let updatedDefenses = Player.defenses player |> removeExpiredActions
|
||||
let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = int shield)
|
||||
|
||||
match alreadyUsedShield , updatedDefenses.Length < 2 with
|
||||
| false , true ->
|
||||
let embed = Embeds.responseCreatedShield shield
|
||||
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed)
|
||||
|> Async.AwaitTask
|
||||
let defense = { DefenseType = shield ; Timestamp = DateTime.UtcNow }
|
||||
do! DbService.updatePlayer <| { player with Defenses = Array.append [| defense |] player.Defenses }
|
||||
let defense = { ActionId = int shield ; Type = Defense ; Timestamp = DateTime.UtcNow }
|
||||
do! DbService.updatePlayer <| { player with Actions = Array.append [| defense |] player.Actions }
|
||||
let builder = DiscordMessageBuilder()
|
||||
builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
|
||||
let channel = event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle)
|
||||
@ -227,16 +224,16 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
||||
builder.Content <- $"You are only allowed two shields at a time. Wait {cooldown} minutes to add another shield"
|
||||
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||
|> Async.AwaitTask
|
||||
do! DbService.updatePlayer <| { player with Defenses = updatedDefenses }
|
||||
do! DbService.updatePlayer <| { player with Actions = updatedDefenses }
|
||||
| true , _ ->
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
builder.IsEphemeral <- true
|
||||
let timestamp = updatedDefenses |> Array.find (fun d -> d.DefenseType = shield) |> fun a -> a.Timestamp
|
||||
let timestamp = updatedDefenses |> Array.find (fun d -> d.ActionId = int shield) |> fun a -> a.Timestamp
|
||||
let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(6)) timestamp
|
||||
builder.Content <- $"{shield} shield is already in use. Wait {cooldown} minutes to use this shield again"
|
||||
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||
|> Async.AwaitTask
|
||||
do! DbService.updatePlayer <| { player with Defenses = updatedDefenses }
|
||||
do! DbService.updatePlayer <| { player with Actions = updatedDefenses }
|
||||
|
||||
| _ ->
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
|
168
Bot/Items.json
168
Bot/Items.json
@ -1,62 +1,158 @@
|
||||
[
|
||||
{
|
||||
"Name" : "Virus",
|
||||
"ItemType" : { "Case" : "Hack" },
|
||||
"Cost" : 5
|
||||
"Id": 0,
|
||||
"Name": "Virus",
|
||||
"Type": {
|
||||
"Case": "Hack"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Ransom",
|
||||
"ItemType" : { "Case" : "Hack" },
|
||||
"Cost" : 10
|
||||
"Id": 1,
|
||||
"Name": "Ransom",
|
||||
"Type": {
|
||||
"Case": "Hack"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Worm",
|
||||
"ItemType" : { "Case" : "Hack" },
|
||||
"Cost" : 5
|
||||
"Id": 2,
|
||||
"Name": "Worm",
|
||||
"Type": {
|
||||
"Case": "Hack"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "DDos",
|
||||
"ItemType" : { "Case" : "Hack" },
|
||||
"Cost" : 10
|
||||
"Id": 3,
|
||||
"Name": "DDos",
|
||||
"Type": {
|
||||
"Case": "Hack"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Crack",
|
||||
"ItemType" : { "Case" : "Hack" },
|
||||
"Cost" : 5
|
||||
"Id": 4,
|
||||
"Name": "Crack",
|
||||
"Type": {
|
||||
"Case": "Hack"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Injection",
|
||||
"ItemType" : { "Case" : "Hack" },
|
||||
"Cost" : 10
|
||||
"Id": 5,
|
||||
"Name": "Injection",
|
||||
"Type": {
|
||||
"Case": "Hack"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Firewall",
|
||||
"ItemType" : { "Case" : "Shield" },
|
||||
"Cost" : 5
|
||||
"Id": 6,
|
||||
"Name": "Firewall",
|
||||
"Type": {
|
||||
"Case": "Shield"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "PortScan",
|
||||
"ItemType" : { "Case" : "Shield" },
|
||||
"Cost" : 10
|
||||
"Id": 7,
|
||||
"Name": "PortScan",
|
||||
"Type": {
|
||||
"Case": "Shield"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Cypher",
|
||||
"ItemType" : { "Case" : "Shield" },
|
||||
"Cost" : 5
|
||||
"Id": 8,
|
||||
"Name": "Encryption",
|
||||
"Type": {
|
||||
"Case": "Shield"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Encryption",
|
||||
"ItemType" : { "Case" : "Shield" },
|
||||
"Cost" : 10
|
||||
"Id": 9,
|
||||
"Name": "Hardening",
|
||||
"Type": {
|
||||
"Case": "Shield"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Sanitation",
|
||||
"ItemType" : { "Case" : "Shield" },
|
||||
"Cost" : 5
|
||||
"Id": 10,
|
||||
"Name": "Sanitation",
|
||||
"Type": {
|
||||
"Case": "Shield"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
},
|
||||
{
|
||||
"Name" : "Hardening",
|
||||
"ItemType" : { "Case" : "Shield" },
|
||||
"Cost" : 10
|
||||
"Id": 11,
|
||||
"Name": "Cypher",
|
||||
"Type": {
|
||||
"Case": "Shield"
|
||||
},
|
||||
"Class": {
|
||||
"Case": "Network"
|
||||
},
|
||||
"Cost": 100,
|
||||
"Power": 50,
|
||||
"Cooldown": 260
|
||||
}
|
||||
]
|
||||
|
@ -10,7 +10,7 @@ open Degenz
|
||||
open Degenz.Shared
|
||||
|
||||
module Commands =
|
||||
let newPlayer nickname (membr : uint64) =
|
||||
// let newPlayer nickname (membr : uint64) =
|
||||
// let h1 = [| Weapon.Virus ; Weapon.Ransom |]
|
||||
// let h2 = [| Weapon.DDos ; Weapon.Worm |]
|
||||
// let h3 = [| Weapon.Crack ; Weapon.Injection |]
|
||||
@ -18,51 +18,49 @@ module Commands =
|
||||
// let d2 = [| Shield.Encryption ; Shield.Cypher |]
|
||||
// let d3 = [| Shield.Hardening ; Shield.Sanitation |]
|
||||
|
||||
let rand = System.Random(System.Guid.NewGuid().GetHashCode())
|
||||
let getRandom (actions : 'a array) = actions.[rand.Next(0, Math.Max(0, actions.Length - 1))]
|
||||
|
||||
let weapons = [| getRandom hackInventory |]
|
||||
let shields = [| getRandom shieldInventory |]
|
||||
|
||||
{ DiscordId = membr
|
||||
Name = nickname
|
||||
Weapons = weapons
|
||||
Shields = shields
|
||||
Attacks = [||]
|
||||
Defenses = [||]
|
||||
Bank = 15 }
|
||||
|
||||
let addHackerRole (ctx : InteractionContext) =
|
||||
async {
|
||||
let! player = DbService.tryFindPlayer ctx.Member.Id
|
||||
let! newPlayer =
|
||||
match player with
|
||||
| Some _ -> async.Return false
|
||||
| None ->
|
||||
async {
|
||||
do! newPlayer ctx.Member.DisplayName ctx.Member.Id
|
||||
|> DbService.insertNewPlayer
|
||||
// let rand = System.Random(System.Guid.NewGuid().GetHashCode())
|
||||
// let getRandom (actions : 'a array) = actions.[rand.Next(0, max 0 (actions.Length - 1))]
|
||||
//
|
||||
// let weapons = [| getRandom hackInventory |]
|
||||
// let shields = [| getRandom shieldInventory |]
|
||||
//
|
||||
// { DiscordId = membr
|
||||
// Name = nickname
|
||||
// Weapons = weapons
|
||||
// Shields = shields
|
||||
// Attacks = [||]
|
||||
// Defenses = [||]
|
||||
// Bank = 15 }
|
||||
|
||||
// let addHackerRole (ctx : InteractionContext) =
|
||||
// async {
|
||||
// let! player = DbService.tryFindPlayer ctx.Member.Id
|
||||
// let! newPlayer =
|
||||
// match player with
|
||||
// | Some _ -> async.Return false
|
||||
// | None ->
|
||||
// async {
|
||||
// do! newPlayer ctx.Member.DisplayName ctx.Member.Id
|
||||
// |> DbService.insertNewPlayer
|
||||
//
|
||||
// for role in ctx.Guild.Roles do
|
||||
// if role.Value.Name = "Hacker" then
|
||||
// do! ctx.Member.GrantRoleAsync(role.Value)
|
||||
// |> Async.AwaitTask
|
||||
// return true
|
||||
// }
|
||||
// if newPlayer then
|
||||
// do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
|
||||
// |> Async.AwaitTask
|
||||
// else
|
||||
// do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
|
||||
// |> Async.AwaitTask
|
||||
//
|
||||
// } |> Async.StartAsTask
|
||||
// :> Task
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if newPlayer then
|
||||
do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
|
||||
|> Async.AwaitTask
|
||||
else
|
||||
do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
|
||||
|> Async.AwaitTask
|
||||
|
||||
} |> Async.StartAsTask
|
||||
:> Task
|
||||
|
||||
let removeHackerRole (ctx : InteractionContext) =
|
||||
async {
|
||||
// let removeHackerRole (ctx : InteractionContext) =
|
||||
// async {
|
||||
// for role in ctx.Member.Roles do
|
||||
// if role.Name = "Hacker" then
|
||||
// do! ctx.Member.RevokeRoleAsync(role)
|
||||
@ -70,10 +68,10 @@ module Commands =
|
||||
|
||||
// do! DbService.removePlayer ctx.Member.Id
|
||||
|
||||
do! ctx.CreateResponseAsync("You are now lame", true)
|
||||
|> Async.AwaitTask
|
||||
} |> Async.StartAsTask
|
||||
:> Task
|
||||
// do! ctx.CreateResponseAsync("You are now lame", true)
|
||||
// |> Async.AwaitTask
|
||||
// } |> Async.StartAsTask
|
||||
// :> Task
|
||||
|
||||
[<CLIMutable>]
|
||||
type LeaderboardEntry = {
|
||||
@ -108,13 +106,13 @@ module Commands =
|
||||
let! player = DbService.tryFindPlayer ctx.Member.Id
|
||||
match player with
|
||||
| Some p ->
|
||||
let updatedAttacks = p.Attacks |> removeExpiredActions (TimeSpan.FromHours(24)) (fun (atk : Attack) -> atk.Timestamp)
|
||||
let updatedDefenses = p.Defenses |> removeExpiredActions (TimeSpan.FromHours(6)) (fun (p : Defense) -> p.Timestamp)
|
||||
let updatedPlayer = { p with Attacks = updatedAttacks ; Defenses = updatedDefenses }
|
||||
do! DbService.updatePlayer updatedPlayer
|
||||
// let updatedAttacks = p.Attacks |> removeExpiredActions (TimeSpan.FromHours(24)) (fun (atk : Attack) -> atk.Timestamp)
|
||||
// let updatedDefenses = p.Defenses |> removeExpiredActions (TimeSpan.FromHours(6)) (fun (p : Defense) -> p.Timestamp)
|
||||
// let updatedPlayer = { p with Attacks = updatedAttacks ; Defenses = updatedDefenses }
|
||||
// do! DbService.updatePlayer updatedPlayer
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
builder.IsEphemeral <- true
|
||||
builder.Content <- statusFormat updatedPlayer
|
||||
// builder.Content <- statusFormat updatedPlayer
|
||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||
|> Async.AwaitTask
|
||||
| None -> do! notYetAHackerMsg ctx
|
||||
@ -124,11 +122,11 @@ module Commands =
|
||||
type PlayerInteractions() =
|
||||
inherit ApplicationCommandModule ()
|
||||
|
||||
[<SlashCommand("redpill", "Take the redpill and become a hacker")>]
|
||||
member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx
|
||||
// [<SlashCommand("redpill", "Take the redpill and become a hacker")>]
|
||||
// member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx
|
||||
|
||||
[<SlashCommand("bluepill", "Take the bluepill and become lame")>]
|
||||
member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx
|
||||
// [<SlashCommand("bluepill", "Take the bluepill and become lame")>]
|
||||
// member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx
|
||||
|
||||
[<SlashCommand("status", "Get your current status like bank account, and active hacks and defenses")>]
|
||||
member this.Status (ctx : InteractionContext) = Commands.status ctx
|
||||
|
@ -26,9 +26,9 @@ type SlotMachine() =
|
||||
|| (results.[0] <> results.[1] && results.[1] <> results.[2] && results.[0] <> results.[2])
|
||||
|
||||
if winConditions then
|
||||
do! DbService.updatePlayer { player with Bank = player.Bank + 10 }
|
||||
do! DbService.updatePlayer { player with Bank = player.Bank + 10<GBT> }
|
||||
else
|
||||
do! DbService.updatePlayer { player with Bank = Math.Max(player.Bank - 1, 0) }
|
||||
do! DbService.updatePlayer { player with Bank = max (player.Bank - 1<GBT>) 0<GBT> }
|
||||
|
||||
|
||||
do! ctx.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource)
|
||||
|
120
Bot/Store.fs
120
Bot/Store.fs
@ -8,74 +8,32 @@ open DSharpPlus.SlashCommands
|
||||
open Degenz
|
||||
open Degenz.Embeds
|
||||
open Degenz.Shared
|
||||
open Newtonsoft.Json
|
||||
|
||||
let store =
|
||||
let file = System.IO.File.ReadAllText("Items.json")
|
||||
JsonConvert.DeserializeObject<Item array>(file)
|
||||
|> Array.groupBy (fun (i : Item) -> i.ItemType)
|
||||
|
||||
let viewStore (ctx : InteractionContext) =
|
||||
async {
|
||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing store)
|
||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing armoury)
|
||||
|> Async.AwaitTask
|
||||
} |> Async.StartAsTask
|
||||
:> Task
|
||||
|
||||
let getItems itemType = store |> Array.find (fun ( t , _ ) -> t = itemType) |> snd
|
||||
|
||||
let buyHack (ctx : InteractionContext) hackId =
|
||||
let buyItem (ctx : InteractionContext) itemId =
|
||||
async {
|
||||
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
|
||||
let weapons = getItems ItemType.Hack
|
||||
let weaponResult = weapons |> Array.tryFind (fun w -> w.Name = string hackId)
|
||||
return!
|
||||
match playerResult , weaponResult with
|
||||
| Some player , Some item ->
|
||||
async {
|
||||
let newBalance = player.Bank - item.Cost
|
||||
if newBalance >= 0 then
|
||||
let playerHasItem = player.Weapons |> Array.exists (fun w -> item.Name = string w)
|
||||
if not playerHasItem then
|
||||
let weapon = hackInventory |> Array.find (fun w -> item.Name = string w)
|
||||
let p = { player with Bank = newBalance ; Weapons = Array.append [| weapon |] player.Weapons }
|
||||
do! DbService.updatePlayer p
|
||||
do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx
|
||||
else
|
||||
do! createSimpleResponseAsync $"You already own this item!" ctx
|
||||
else
|
||||
do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx
|
||||
}
|
||||
| None , _ -> notYetAHackerMsg ctx
|
||||
| _ -> createSimpleResponseAsync "Something is wrong" ctx
|
||||
} |> Async.StartAsTask
|
||||
:> Task
|
||||
|
||||
let buyShield (ctx : InteractionContext) shieldId =
|
||||
async {
|
||||
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
|
||||
let shieldResult =
|
||||
getItems ItemType.Shield
|
||||
|> Array.tryFind (fun w -> w.Name = string shieldId)
|
||||
return!
|
||||
match playerResult , shieldResult with
|
||||
| Some player , Some item ->
|
||||
async {
|
||||
let newBalance = player.Bank - item.Cost
|
||||
if newBalance >= 0 then
|
||||
let playerHasItem = player.Shields |> Array.exists (fun w -> item.Name = string w)
|
||||
if not playerHasItem then
|
||||
let shield = shieldInventory |> Array.find (fun w -> item.Name = string w)
|
||||
let p = { player with Bank = newBalance ; Shields = Array.append [| shield |] player.Shields }
|
||||
do! DbService.updatePlayer p
|
||||
do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx
|
||||
else
|
||||
do! createSimpleResponseAsync $"You already own this item!" ctx
|
||||
else
|
||||
do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx
|
||||
}
|
||||
| None , _ -> notYetAHackerMsg ctx
|
||||
| _ -> createSimpleResponseAsync "Something is wrong" ctx
|
||||
let item = armoury |> Array.find (fun w -> w.Id = itemId)
|
||||
match playerResult with
|
||||
| Some player ->
|
||||
let newBalance = player.Bank - item.Cost
|
||||
if newBalance >= 0<GBT> then
|
||||
let playerHasItem = player.Arsenal |> Array.exists (fun w -> item.Id = w.Id)
|
||||
if not playerHasItem then
|
||||
let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
|
||||
do! DbService.updatePlayer p
|
||||
do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx
|
||||
else
|
||||
do! createSimpleResponseAsync $"You already own this item!" ctx
|
||||
else
|
||||
do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx
|
||||
| None -> do! notYetAHackerMsg ctx
|
||||
} |> Async.StartAsTask
|
||||
:> Task
|
||||
|
||||
@ -88,28 +46,19 @@ let sell (ctx : InteractionContext) =
|
||||
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
|
||||
match playerResult with
|
||||
| Some player ->
|
||||
let hasInventoryToSell = Array.length player.Weapons + Array.length player.Shields > 0
|
||||
let hasInventoryToSell = Array.length player.Arsenal > 0
|
||||
if hasInventoryToSell then
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
builder.AddEmbed (constructEmbed "Pick the item you wish to sell.") |> ignore
|
||||
|
||||
Array.chunkBySize 3 player.Weapons
|
||||
Array.chunkBySize 5 player.Arsenal
|
||||
|> Array.iter
|
||||
(fun wps ->
|
||||
wps
|
||||
|> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Primary, $"Hack-{w}", $"{w}"))
|
||||
|> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Primary, $"{w.Type}-{w.Id}", $"{w.Name}"))
|
||||
|> Seq.cast<DiscordComponent>
|
||||
|> builder.AddComponents
|
||||
|> ignore)
|
||||
Array.chunkBySize 3 player.Shields
|
||||
|> Array.iter
|
||||
(fun shs ->
|
||||
shs
|
||||
|> Array.map (fun s -> DiscordButtonComponent(ButtonStyle.Primary, $"Shield-{s}", $"{s}"))
|
||||
|> Seq.cast<DiscordComponent>
|
||||
|> builder.AddComponents
|
||||
|> ignore)
|
||||
|
||||
builder.AsEphemeral true |> ignore
|
||||
|
||||
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|
||||
@ -121,19 +70,16 @@ let sell (ctx : InteractionContext) =
|
||||
} |> Async.StartAsTask
|
||||
:> Task
|
||||
|
||||
let updateShields player salePrice updatedShields = { player with Bank = player.Bank + salePrice ; Shields = updatedShields }
|
||||
let updateHacks player salePrice updatedHacks = { player with Bank = player.Bank + salePrice ; Weapons = updatedHacks }
|
||||
let updateArsenal player salePrice updatedArsenal = { player with Bank = player.Bank + salePrice ; Arsenal = updatedArsenal }
|
||||
|
||||
let sellItem (event : ComponentInteractionCreateEventArgs) updateFn player inventory itemType itemName =
|
||||
let sellItem (event : ComponentInteractionCreateEventArgs) player itemId =
|
||||
async {
|
||||
let item = getItems itemType |> Array.find (fun i -> i.Name = itemName)
|
||||
let salePrice = item.Cost
|
||||
let updatedItems = inventory |> Array.filter (fun i -> string i <> itemName)
|
||||
let updatedPlayer = updateFn player salePrice updatedItems
|
||||
let item = armoury |> Array.find (fun i -> i.Id = itemId)
|
||||
let updatedPlayer = { player with Bank = player.Bank + item.Cost ; Arsenal = player.Arsenal |> Array.filter (fun w -> w.Id = itemId)}
|
||||
do! DbService.updatePlayer updatedPlayer
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
builder.IsEphemeral <- true
|
||||
builder.Content <- $"Sold {itemType.ToString().ToLower()} {itemName} for {salePrice}! Current Balance: {updatedPlayer.Bank}"
|
||||
builder.Content <- $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}"
|
||||
do! event.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder)
|
||||
|> Async.AwaitTask
|
||||
}
|
||||
@ -143,12 +89,8 @@ let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCrea
|
||||
let! playerResult = DbService.tryFindPlayer event.User.Id
|
||||
match playerResult with
|
||||
| Some player ->
|
||||
let split = event.Id.Split("-")
|
||||
let itemType = match split.[0] with "Hack" -> ItemType.Hack | _ -> ItemType.Shield
|
||||
let itemName = split.[1]
|
||||
match itemType with
|
||||
| ItemType.Hack -> do! sellItem event updateHacks player player.Weapons ItemType.Hack itemName
|
||||
| ItemType.Shield -> do! sellItem event updateShields player player.Shields ItemType.Shield itemName
|
||||
let itemId = int <| event.Id.Split("-").[1]
|
||||
do! sellItem event player itemId
|
||||
| None ->
|
||||
let builder = DiscordInteractionResponseBuilder()
|
||||
builder.IsEphemeral <- true
|
||||
@ -165,12 +107,12 @@ type Store() =
|
||||
member _.ViewStore (ctx : InteractionContext) = viewStore ctx
|
||||
|
||||
[<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
|
||||
member _.BuyHack (ctx : InteractionContext, [<Option("hack-id", "The ID of the hack you wish to purchase")>] hackId : Hack) =
|
||||
buyHack ctx hackId
|
||||
member _.BuyHack (ctx : InteractionContext, [<Option("hack-id", "The ID of the hack you wish to purchase")>] hackId : HackId) =
|
||||
buyItem ctx (int hackId)
|
||||
|
||||
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GoodBoyTokenz")>]
|
||||
member this.BuyShield (ctx : InteractionContext, [<Option("shield-id", "The ID of the shield you wish to purchase")>] shieldId : Shield) =
|
||||
buyShield ctx shieldId
|
||||
member this.BuyShield (ctx : InteractionContext, [<Option("shield-id", "The ID of the shield you wish to purchase")>] shieldId : ShieldId) =
|
||||
buyItem ctx (int shieldId)
|
||||
|
||||
[<SlashCommand("sell", "Sell an item in your inventory for GoodBoyTokenz")>]
|
||||
member this.SellItem (ctx : InteractionContext) = sell ctx
|
||||
|
@ -7,8 +7,8 @@ open DSharpPlus.EventArgs
|
||||
open DSharpPlus.SlashCommands
|
||||
open Degenz.Shared
|
||||
|
||||
let defaultHack = Hack.Virus
|
||||
let defaultShield = Shield.Firewall
|
||||
let defaultHack = armoury |> Array.find (fun i -> i.Id = int HackId.Virus)
|
||||
let defaultShield = armoury |> Array.find (fun i -> i.Id = int ShieldId.Firewall)
|
||||
|
||||
let sendInitialEmbed (client : DiscordClient) =
|
||||
async {
|
||||
@ -32,13 +32,17 @@ let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
|
||||
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|
||||
|> Async.AwaitTask
|
||||
match maybePlayer with
|
||||
| Some player when not <| Array.isEmpty player.Weapons && not <| Array.isEmpty player.Shields ->
|
||||
| Some player when player.Arsenal |> Array.exists (fun w -> w.Type = Hack)
|
||||
&& player.Arsenal |> Array.exists (fun w -> w.Type = Shield) ->
|
||||
let msg = "First time, eh? Beautopia is a dangerous place. I'm going to teach you how to protect yourself from other degenerates. "
|
||||
+ "And in the process, I'll also show you how to hack some sheeple, so you can earn some cash."
|
||||
do! Message.sendFollowUpMessageWithButton event "Trainer-2" msg
|
||||
| Some player ->
|
||||
let missingItem = match player.Weapons with [||] -> "Hacks" | _ -> "Shields"
|
||||
let msg = $"Looks like you don't own any {missingItem}. Go to the store and purchase some before proceeding."
|
||||
let missingItem =
|
||||
if player.Arsenal |> Array.exists (fun w -> w.Type = Hack)
|
||||
then "Shields"
|
||||
else "Hacks"
|
||||
let msg = $"Looks like you're missing {missingItem}. Go to the store and purchase some before proceeding."
|
||||
do! Message.sendFollowUpMessage event msg
|
||||
| None ->
|
||||
let msg = "An error occurred, please contact a moderator"
|
||||
@ -50,7 +54,7 @@ let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) =
|
||||
let! result = DbService.tryFindPlayer event.User.Id
|
||||
match result with
|
||||
| Some player ->
|
||||
let weaponName = player.Shields |> Array.tryHead |> Option.defaultValue defaultShield
|
||||
let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield
|
||||
do! Message.sendInteractionEvent event
|
||||
($"First things first, let's get your system protected. Let's enable a shield to protect you from potential hackers. "
|
||||
+ $"You currently have {weaponName} in your arsenal. To enable it and protect your system, you can use the `/defend` slash command to choose a shield."
|
||||
@ -65,8 +69,8 @@ let defend (ctx : InteractionContext) =
|
||||
match playerResult with
|
||||
| Some player ->
|
||||
let playerWithShields =
|
||||
match player.Shields with
|
||||
| [||] -> { player with Shields = [| defaultShield |] }
|
||||
match player.Arsenal with
|
||||
| [||] -> { player with Arsenal = [| defaultShield |] }
|
||||
| _ -> player
|
||||
let embed = Embeds.pickDefense "Trainer-3" playerWithShields
|
||||
|
||||
@ -92,7 +96,7 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
|
||||
match result with
|
||||
| Some player ->
|
||||
let prize = 0.223f
|
||||
let shield = player.Shields |> Array.tryHead |> Option.defaultValue defaultShield
|
||||
let shield = player.Arsenal |> Array.tryHead |> Option.defaultValue defaultShield
|
||||
let embed = Embeds.responseCreatedShieldTrainer shield
|
||||
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
|
||||
do! Async.Sleep 2000
|
||||
@ -112,7 +116,7 @@ let handleTrainerStep4 (event : ComponentInteractionCreateEventArgs) =
|
||||
let! result = DbService.tryFindPlayer event.User.Id
|
||||
match result with
|
||||
| Some player ->
|
||||
let weaponName = player.Weapons |> Array.tryHead |> Option.defaultValue defaultHack
|
||||
let weaponName = player.Arsenal |> Array.tryHead |> Option.defaultValue defaultHack
|
||||
do! Message.sendInteractionEvent event
|
||||
($"Next why don't you try hacking me. You currently have {weaponName} equipped. To hack me and get some money, "
|
||||
+ $" you can use the '/hack' slash command and select a user to hack, then choose the hack attack you wish to use."
|
||||
@ -160,7 +164,7 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
|
||||
| Some player ->
|
||||
let prize = 2
|
||||
do! Async.Sleep 1000
|
||||
let hack = player.Weapons |> Array.tryHead |> Option.defaultValue defaultHack
|
||||
let hack = player.Arsenal |> Array.tryHead |> Option.defaultValue defaultHack
|
||||
let embed = Embeds.responseSuccessfulHackTrainer $"<@{GuildEnvironment.botHackerBattle}>" hack prize
|
||||
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
|
||||
do! Async.Sleep 3000
|
||||
|
@ -20,11 +20,11 @@ let tryFindPlayer (id : uint64) =
|
||||
| p -> return p
|
||||
.GetValue("Player")
|
||||
.ToBsonDocument()
|
||||
|> BsonSerializer.Deserialize<Player>
|
||||
|> BsonSerializer.Deserialize<PlayerData>
|
||||
|> Some
|
||||
}
|
||||
|
||||
let insertNewPlayer (player : Player) =
|
||||
let insertNewPlayer (player : PlayerData) =
|
||||
async {
|
||||
let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ]
|
||||
do! BsonDocument(dict)
|
||||
@ -32,7 +32,7 @@ let insertNewPlayer (player : Player) =
|
||||
|> Async.AwaitTask
|
||||
}
|
||||
|
||||
let deletePlayer (player : Player) =
|
||||
let deletePlayer (player : PlayerData) =
|
||||
async {
|
||||
let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ]
|
||||
do! BsonDocument(dict)
|
||||
|
135
Shared/Shared.fs
135
Shared/Shared.fs
@ -5,17 +5,20 @@ open DSharpPlus
|
||||
open DSharpPlus.Entities
|
||||
open DSharpPlus.EventArgs
|
||||
open DSharpPlus.SlashCommands
|
||||
open Newtonsoft.Json
|
||||
|
||||
type ItemType =
|
||||
| Hack
|
||||
| Shield
|
||||
[<Measure>]
|
||||
type mins
|
||||
|
||||
type ActionClass =
|
||||
[<Measure>]
|
||||
type GBT
|
||||
|
||||
type BattleClass =
|
||||
| Network
|
||||
| Exploit
|
||||
| Penetration
|
||||
|
||||
type Hack =
|
||||
type HackId =
|
||||
| Virus = 0
|
||||
| Ransom = 1
|
||||
| Worm = 2
|
||||
@ -23,30 +26,28 @@ type Hack =
|
||||
| Crack = 4
|
||||
| Injection = 5
|
||||
|
||||
type Shield =
|
||||
| Firewall = 0
|
||||
| PortScan = 1
|
||||
| Encryption = 2
|
||||
| Hardening = 4
|
||||
| Sanitation = 5
|
||||
| Cypher = 3
|
||||
type ShieldId =
|
||||
| Firewall = 6
|
||||
| PortScan = 7
|
||||
| Encryption = 8
|
||||
| Hardening = 9
|
||||
| Sanitation = 10
|
||||
| Cypher = 11
|
||||
|
||||
[<CLIMutable>]
|
||||
type Item = {
|
||||
type ItemType =
|
||||
| Hack
|
||||
| Shield
|
||||
|
||||
type BattleItem = {
|
||||
Id : int
|
||||
Name : string
|
||||
ItemType : ItemType
|
||||
Cost : int
|
||||
Cost : int<GBT>
|
||||
Type : ItemType
|
||||
Class : BattleClass
|
||||
Power : int
|
||||
Cooldown : int<mins>
|
||||
}
|
||||
|
||||
let hackInventory = [| Hack.Virus ; Hack.Ransom ; Hack.DDos ; Hack.Worm ; Hack.Crack ; Hack.Injection |]
|
||||
let shieldInventory = [| Shield.Firewall ; Shield.PortScan ; Shield.Encryption ; Shield.Cypher ; Shield.Hardening ; Shield.Sanitation |]
|
||||
|
||||
let getClass =
|
||||
function
|
||||
| 0 | 1 -> Network
|
||||
| 2 | 3 -> Exploit
|
||||
| 4 | _ -> Penetration
|
||||
|
||||
type HackResult =
|
||||
| Strong
|
||||
| Weak
|
||||
@ -54,26 +55,37 @@ type HackResult =
|
||||
[<CLIMutable>]
|
||||
type DiscordPlayer = { Id: uint64; Name: string }
|
||||
|
||||
[<CLIMutable>]
|
||||
type Attack =
|
||||
{ HackType: Hack
|
||||
Target: DiscordPlayer
|
||||
Timestamp: DateTime }
|
||||
type Attack = {
|
||||
Result : bool
|
||||
Target : DiscordPlayer
|
||||
}
|
||||
|
||||
type ActionType =
|
||||
| Attack of target : DiscordPlayer * result : bool
|
||||
| Defense
|
||||
|
||||
type Action =
|
||||
{ ActionId : int
|
||||
Type : ActionType
|
||||
Timestamp : DateTime }
|
||||
|
||||
[<CLIMutable>]
|
||||
type Defense =
|
||||
{ DefenseType: Shield
|
||||
Timestamp: DateTime }
|
||||
type PlayerData =
|
||||
{ DiscordId : uint64
|
||||
Name : string
|
||||
Arsenal : BattleItem array
|
||||
Actions : Action array
|
||||
Bank : int<GBT> }
|
||||
|
||||
[<CLIMutable>]
|
||||
type Player =
|
||||
{ DiscordId: uint64
|
||||
Name: string
|
||||
Weapons: Hack array
|
||||
Shields: Shield array
|
||||
Attacks: Attack array
|
||||
Defenses: Defense array
|
||||
Bank: int }
|
||||
module Player =
|
||||
let hacks player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack)
|
||||
let shields player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack)
|
||||
let attacks player =
|
||||
player.Actions
|
||||
|> Array.choose (fun act -> match act.Type with Attack (t,r) -> Some (act,t,r) | Defense -> None)
|
||||
let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense _ -> true | _ -> false)
|
||||
|
||||
let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack (t,r) -> Some (act,t,r) | Defense -> None)
|
||||
|
||||
let createSimpleResponseAsync msg (ctx: InteractionContext) =
|
||||
async {
|
||||
@ -91,21 +103,30 @@ let notYetAHackerMsg =
|
||||
|
||||
let hackDescription = ""
|
||||
|
||||
let statusFormat player =
|
||||
$"Hack Inventory: {player.Weapons |> Array.toList}
|
||||
Shield Inventory: {player.Shields |> Array.toList}
|
||||
Active Hacks: {player.Attacks |> Array.toList}
|
||||
Active Defenses: {player.Defenses |> Array.toList}
|
||||
Bank: {player.Bank}"
|
||||
let statusFormat p =
|
||||
$"Hacks: {Player.hacks p |> Array.toList}
|
||||
Shields: {Player.defenses p |> Array.toList}
|
||||
Hack Attacks: {Player.attacks p |> Array.toList}
|
||||
Active Defenses: {Player.defenses p |> Array.toList}
|
||||
Bank: {p.Bank}"
|
||||
|
||||
let armoury =
|
||||
let file = System.IO.File.ReadAllText("Items.json")
|
||||
JsonConvert.DeserializeObject<BattleItem array>(file)
|
||||
|
||||
let getItemFromArmoury id = armoury |> Array.find (fun w -> w.Id = id)
|
||||
|
||||
let constructButtons (actionType: string) (playerInfo: string) (weapons: 'a array) =
|
||||
weapons
|
||||
|> Seq.map (fun hack -> DiscordButtonComponent(ButtonStyle.Primary, $"{actionType}-{hack}-{playerInfo}", $"{hack}"))
|
||||
|
||||
let removeExpiredActions timespan (timestamp: 'a -> DateTime) actions =
|
||||
actions |> Array.filter (fun act -> DateTime.UtcNow - (timestamp act) < timespan)
|
||||
let removeExpiredActions actions =
|
||||
actions
|
||||
|> Array.filter (fun (act : Action) ->
|
||||
let item = armoury |> Array.find (fun w -> w.Id = act.ActionId)
|
||||
DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown))
|
||||
|
||||
let modifyPlayerBank player amount = { player with Bank = Math.Max(player.Bank + amount, 0) }
|
||||
let modifyPlayerBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
|
||||
|
||||
module Message =
|
||||
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
|
||||
@ -118,6 +139,18 @@ module Message =
|
||||
|> Async.Ignore
|
||||
}
|
||||
|
||||
let sendFollowUpMessageWithEmbed (event : ComponentInteractionCreateEventArgs) (embed : DiscordEmbed) msg =
|
||||
async {
|
||||
let builder =
|
||||
DiscordFollowupMessageBuilder()
|
||||
.AsEphemeral(true)
|
||||
.WithContent(msg)
|
||||
.AddEmbed(embed)
|
||||
do! event.Interaction.CreateFollowupMessageAsync(builder)
|
||||
|> Async.AwaitTask
|
||||
|> Async.Ignore
|
||||
}
|
||||
|
||||
let sendFollowUpMessageWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg =
|
||||
async {
|
||||
let builder = DiscordFollowupMessageBuilder()
|
||||
|
Loading…
x
Reference in New Issue
Block a user