From 7900bba3c36028173a537b13fd12af395c983172 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Wed, 16 Feb 2022 17:40:17 +0700 Subject: [PATCH] Refactor for new inventory, events, and traits --- Bot/Embeds.fs | 31 +++++++++-------- Bot/Game.fs | 70 +++++++++++++++------------------------ Bot/HackerBattle.fs | 48 +++++++++++++++++---------- Bot/PlayerInteractions.fs | 4 +-- Bot/Store.fs | 24 +++++++------- Bot/Thief.fs | 33 ++++++++---------- Bot/Trainer.fs | 49 +++++++++++++-------------- DbService/DbService.fs | 28 +++------------- Shared/Shared.fs | 60 +++++++++++++++------------------ 9 files changed, 154 insertions(+), 193 deletions(-) diff --git a/Bot/Embeds.fs b/Bot/Embeds.fs index 4a7b5c2..15cc99e 100644 --- a/Bot/Embeds.fs +++ b/Bot/Embeds.fs @@ -1,6 +1,7 @@ module Degenz.Embeds open System +open DSharpPlus open Degenz.Messaging open Degenz.Types open DSharpPlus.Entities @@ -37,15 +38,15 @@ let constructButtons (actionId: string) (buttonInfo : string) (player: PlayerDat |> Player.getItems itemType |> Array.map (fun item -> let action = - player.Actions - |> Array.tryFind (fun i -> i.ActionId = item.Id) + player.Events + |> Array.tryFind (fun i -> i.ItemId = item.Id) match action , isTrainer with | None , _ | Some _ , true -> - DiscordButtonComponent(Game.getClassButtonColor item.Class, $"{actionId}-{item.Id}-{buttonInfo}", $"{item.Name}") + DiscordButtonComponent(ButtonStyle.Primary, $"{actionId}-{item.Id}-{buttonInfo}", $"{item.Name}") | Some act , false -> - let c = ((Armory.getItem act.ActionId).Cooldown) + let c = ((Armory.getItem act.ItemId).Cooldown) let time = Messaging.getShortTimeText (TimeSpan.FromMinutes(int c)) act.Timestamp - DiscordButtonComponent(Game.getClassButtonColor item.Class, $"{actionId}-{item.Id}", $"{item.Name} ({time} left)", true)) + DiscordButtonComponent(ButtonStyle.Primary, $"{actionId}-{item.Id}", $"{item.Name} ({time} left)", true)) |> Seq.cast let pickDefense actionId player isTrainer = @@ -109,25 +110,24 @@ let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : Battle match item.Type with | Hack -> embed - .AddField($"Weak Against |", Game.getGoodAgainst item.Class |> fst |> string , true) + .AddField($"Hacking Power |", string item.Power, true) .AddField("Cooldown |", $"{TimeSpan.FromMinutes(int item.Cooldown).Minutes} minutes", true) .WithThumbnail(getHackIcon (enum(item.Id))) |> ignore | Shield -> embed - .AddField($"Strong Against |", Game.getGoodAgainst item.Class |> snd |> string , true) + .AddField($"Defensive Strength |", string item.Power, true) .AddField("Active For |", $"{TimeSpan.FromMinutes(int item.Cooldown).Hours} hours", true) .WithThumbnail(getShieldIcon (enum(item.Id))) |> ignore embed - .AddField("Cost 💰", $"{item.Cost} $GBT", true) - .WithColor(Game.getClassEmbedColor item.Class) + .AddField("Cost 💰", $"{item.Price} $GBT", true) .WithTitle($"{item.Name}") |> ignore let button = - if player.Arsenal |> Array.exists (fun i -> i.Id = item.Id) - then DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Buy-{item.Id}", $"Own {item.Name}", true) - else DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Buy-{item.Id}", $"Buy {item.Name}") + if player.Inventory |> Array.exists (fun i -> i.Id = item.Id) + then DiscordButtonComponent(ButtonStyle.Primary, $"Buy-{item.Id}", $"Own {item.Name}", true) + else DiscordButtonComponent(ButtonStyle.Primary, $"Buy-{item.Id}", $"Buy {item.Name}") embed.Build() , button :> DiscordComponent) |> Array.unzip @@ -138,7 +138,7 @@ let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : Battle let getSellItemsEmbed (itemType : ItemType) (player : PlayerData) = let embeds , buttons = - player.Arsenal + player.Inventory |> Array.filter (fun i -> i.Type = itemType) |> Array.map (fun item -> let embed = DiscordEmbedBuilder() @@ -146,11 +146,10 @@ let getSellItemsEmbed (itemType : ItemType) (player : PlayerData) = | Hack -> embed.WithThumbnail(getHackIcon (enum(item.Id))) |> ignore | Shield -> embed.WithThumbnail(getShieldIcon (enum(item.Id))) |> ignore embed - .AddField("Sell For 💰", $"{item.Cost} $GBT", true) - .WithColor(Game.getClassEmbedColor item.Class) + .AddField("Sell For 💰", $"{item.Price} $GBT", true) .WithTitle($"{item.Name}") |> ignore - let button = DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Sell-{item.Id}", $"Sell {item.Name}") + let button = DiscordButtonComponent(ButtonStyle.Primary, $"Sell-{item.Id}", $"Sell {item.Name}") embed.Build() , button :> DiscordComponent) |> Array.unzip diff --git a/Bot/Game.fs b/Bot/Game.fs index 0734140..e235d89 100644 --- a/Bot/Game.fs +++ b/Bot/Game.fs @@ -14,21 +14,6 @@ module Game = let SameTargetAttackCooldown = System.TimeSpan.FromHours(6) - let getClassButtonColor = function - | Network -> ButtonStyle.Danger - | Exploit -> ButtonStyle.Success - | Penetration -> ButtonStyle.Primary - - let getClassEmbedColor = function - | Network -> DiscordColor.Red - | Penetration -> DiscordColor.Blurple - | Exploit -> DiscordColor.Green - - let getGoodAgainst = function - | BattleClass.Network -> ( ShieldId.Firewall , HackId.Virus ) - | BattleClass.Penetration -> ( ShieldId.Cypher , HackId.RemoteAccess ) - | BattleClass.Exploit -> ( ShieldId.Encryption , HackId.Worm ) - let executePlayerAction (ctx : IDiscordContext) (dispatch : PlayerData -> Async) = async { let builder = DiscordInteractionResponseBuilder() @@ -78,65 +63,64 @@ module Game = } |> Async.StartAsTask :> Task module Player = - let getItems itemType (player : PlayerData) = player.Arsenal |> Array.filter (fun i -> i.Type = itemType) + 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 getAttacks player = - player.Actions - |> Array.filter (fun act -> match act.Type with Attack _ -> true | _ -> false || act.ActionId < 12) - let getDefenses player = - player.Actions - |> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false || act.ActionId < 12) + let getHackEvents player = + player.Events + |> Array.filter (fun act -> match act.Type with PlayerEventType.Hack -> true | _ -> false || act.ItemId < 12) + let getShieldEvents player = + player.Events + |> Array.filter (fun act -> match act.Type with PlayerEventType.Shield -> 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.Actions - |> Array.filter (fun (act : Action) -> + player.Events + |> Array.filter (fun (act : PlayerEvent) -> let itemCooldown = - if act.ActionId < 12 then - (Armory.getItem act.ActionId).Cooldown + if act.ItemId < 12 then + (Armory.getItem act.ItemId).Cooldown else match act.Type with - | Attack _ -> 1 - | Defense -> 720 + | PlayerEventType.Steal -> 1 + | _ -> 720 |> int match act.Type , filterByAttackCooldown with - | Attack _ , true -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(itemCooldown) - | Attack _ , false -> System.DateTime.UtcNow - act.Timestamp < Game.SameTargetAttackCooldown - | Defense , _ -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(itemCooldown)) - { player with Actions = actions } + | PlayerEventType.Hack , true -> System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(itemCooldown) + | PlayerEventType.Hack , false -> System.DateTime.UtcNow - act.Timestamp < Game.SameTargetAttackCooldown + | PlayerEventType.Shield , _ -> 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 } - let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None) - module Arsenal = 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) = + let actionFormat (actions : PlayerEvent array) = match actions with | [||] -> "None" | _ -> let hacks , defenses = actions - |> Array.filter (fun act -> act.ActionId < 12) - |> Array.partition (fun act -> match act.Type with Attack _ -> true | Defense -> false) + |> Array.filter (fun act -> act.ItemId < 12) + |> Array.partition (fun act -> match act.Type with PlayerEventType.Hack -> true | _ -> false) let hacks = hacks |> Array.take (min hacks.Length 10) hacks |> Array.append defenses |> Array.map (fun act -> - let item = Armory.getItem act.ActionId + let item = Armory.getItem act.ItemId match act.Type with - | Attack atk -> + | PlayerEventType.Hack -> let cooldown = Messaging.getTimeText false Game.SameTargetAttackCooldown act.Timestamp - $"Hacked {atk.Target.Name} {cooldown} ago" - | Defense -> + $"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" @@ -144,6 +128,6 @@ module Arsenal = let statusFormat p = $"**Hacks:** {Player.getHacks p |> battleItemFormat}\n **Shields:** {Player.getShields p |> battleItemFormat}\n - **Hack Attacks:**\n{Player.getAttacks p |> actionFormat}\n - **Active Shields:**\n{Player.getDefenses p |> actionFormat}" + **Hack Attacks:**\n{Player.getHackEvents p |> actionFormat}\n + **Active Shields:**\n{Player.getShieldEvents p |> actionFormat}" diff --git a/Bot/HackerBattle.fs b/Bot/HackerBattle.fs index 9925973..6681cd6 100644 --- a/Bot/HackerBattle.fs +++ b/Bot/HackerBattle.fs @@ -15,19 +15,18 @@ let checkPlayerIsAttackingThemselves defender attacker = | false -> Ok attacker let checkAlreadyHackedTarget defenderId attacker = - attacker.Actions - |> Player.getAttacksFlat - |> Array.tryFind (fun (_,t,_) -> t.Id = defenderId) + attacker.Events + |> Array.tryFind (fun pe -> pe.Adversary.Id = defenderId) |> function - | Some ( atk , target , _ ) -> - let cooldown = getTimeText true Game.SameTargetAttackCooldown atk.Timestamp - Error $"You can only hack the same target once every {Game.SameTargetAttackCooldown.Hours} hours, wait {cooldown} to attempt another hack on {target.Name}." + | Some event -> + let cooldown = getTimeText true Game.SameTargetAttackCooldown event.Timestamp + Error $"You can only hack the same target once every {Game.SameTargetAttackCooldown.Hours} hours, wait {cooldown} to attempt another hack on {event.Adversary.Name}." | None -> Ok attacker let checkItemHasCooldown itemId attacker = let cooldown = - attacker.Actions - |> Array.tryFind (fun a -> a.ActionId = itemId) + attacker.Events + |> Array.tryFind (fun a -> a.ItemId = itemId) |> function | Some a -> a.Timestamp | None -> DateTime.MinValue @@ -45,7 +44,7 @@ let checkHasEmptyHacks attacker = | _ -> Ok attacker let checkPlayerOwnsWeapon itemId player = - match player.Arsenal |> Array.exists (fun i -> i.Id = itemId) with + match player.Inventory |> Array.exists (fun i -> i.Id = itemId) with | true -> Ok player | false -> Error $"You sold your weapon already, you cheeky bastard..." @@ -56,7 +55,7 @@ let checkTargetHasMoney (target : PlayerData) attacker = let checkPlayerHasShieldSlotsAvailable shield player = let updatedPlayer = player |> Player.removeExpiredActions false - let defenses = Player.getDefenses updatedPlayer + let defenses = Player.getShieldEvents updatedPlayer match defenses |> Array.length >= 2 with | true -> let timestamp = defenses |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp @@ -65,24 +64,33 @@ let checkPlayerHasShieldSlotsAvailable shield player = | false -> Ok updatedPlayer let calculateDamage (hack : BattleItem) (shield : BattleItem) = - if hack.Class = shield.Class + if hack.Power < shield.Power then Weak else Strong let runHackerBattle defender hack = defender |> Player.removeExpiredActions false - |> Player.getDefenses - |> Array.map (fun dfn -> Armory.battleItems |> Array.find (fun w -> w.Id = dfn.ActionId)) + |> Player.getShieldEvents + |> Array.map (fun dfn -> Armory.battleItems |> Array.find (fun w -> w.Id = dfn.ItemId)) |> Array.map (calculateDamage (hack)) |> Array.contains Weak let updateCombatants attacker defender hack prize = let updatePlayer amount attack p = - { p with Actions = Array.append [| attack |] p.Actions ; Bank = max (p.Bank + amount) 0 } + { p with Events = Array.append [| attack |] p.Events ; Bank = max (p.Bank + amount) 0 } let target = { Id = defender.DiscordId ; Name = defender.Name } - let attack = { ActionId = int hack ; Type = Attack { Target = target ; Result = prize > 0 } ; Timestamp = DateTime.UtcNow } + let attack = { + ItemId = int hack + Type = PlayerEventType.Hack + Adversary = target + Result = if prize > 0 then PlayerEventResult.Successful else PlayerEventResult.Failed + Timestamp = DateTime.UtcNow + } + // TODO: This is what I was talking about, this isn't a "Shield" event, this is a hack event but there's an adversary + // who loses, so the event itself is to just "hack", so there's no "mugged" event, there's just a failed steal defense + // or something like that. [ DbService.updatePlayer <| updatePlayer prize attack attacker DbService.updatePlayer <| Player.modifyBank defender -prize ] |> Async.Parallel @@ -181,8 +189,14 @@ let handleDefense (ctx : IDiscordContext) = |> handleResultWithResponse ctx (fun _ -> async { // Don't use this player, it removes player cooldowns let embed = Embeds.responseCreatedShield (Armory.getItem shieldId) do! ctx.FollowUp embed |> Async.AwaitTask - let defense = { ActionId = shieldId ; Type = Defense ; Timestamp = DateTime.UtcNow } - do! DbService.updatePlayer <| { player with Actions = Array.append [| defense |] player.Actions } + let defense = { + ItemId = shieldId + Type = PlayerEventType.Shield + Result = PlayerEventResult.Successful + Timestamp = DateTime.UtcNow + Adversary = DiscordPlayer.empty + } + do! DbService.updatePlayer <| { player with Events = Array.append [| defense |] player.Events } let builder = DiscordMessageBuilder() builder.WithContent($"{ctx.GetDiscordMember().Username} has protected their system!") |> ignore let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelEventsHackerBattle) diff --git a/Bot/PlayerInteractions.fs b/Bot/PlayerInteractions.fs index 85a2987..a7a7444 100644 --- a/Bot/PlayerInteractions.fs +++ b/Bot/PlayerInteractions.fs @@ -14,8 +14,8 @@ module Commands = { DiscordId = membr Name = nickname - Arsenal = [| hack ; shield |] - Actions = [||] + Inventory = [| hack ; shield |] + Events = [||] XP = 0 Achievements = [||] Traits = PlayerTraits.empty diff --git a/Bot/Store.fs b/Bot/Store.fs index 951a0db..6e0a30c 100644 --- a/Bot/Store.fs +++ b/Bot/Store.fs @@ -9,22 +9,22 @@ open Degenz open Degenz.Messaging let checkHasSufficientFunds (item : BattleItem) player = - if player.Bank - item.Cost >= 0 + if player.Bank - item.Price >= 0 then Ok player else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" let checkAlreadyOwnsItem (item : BattleItem) player = - if player.Arsenal |> Array.exists (fun w -> item.Id = w.Id) + if player.Inventory |> Array.exists (fun w -> item.Id = w.Id) then Error $"You already own {item.Name}!" else Ok player let checkSoldItemAlready (item : BattleItem) player = - if player.Arsenal |> Array.exists (fun w -> item.Id = w.Id) + if player.Inventory |> Array.exists (fun w -> item.Id = w.Id) then Ok player else Error $"{item.Name} not found in your arsenal! Looks like you sold it already." let checkHasItemsInArsenal itemType player = - if player.Arsenal |> Array.filter (fun i -> i.Type = itemType ) |> Array.length > 0 + if player.Inventory |> Array.filter (fun i -> i.Type = itemType ) |> Array.length > 0 then Ok player else Error $"You currently have no {itemType}s in your arsenal to sell!" @@ -50,8 +50,8 @@ let handleBuyItem (ctx : IDiscordContext) itemId = |> checkHasSufficientFunds item >>= checkAlreadyOwnsItem item |> handleResultWithResponse ctx (fun player -> async { - let newBalance = player.Bank - item.Cost - let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal } + let newBalance = player.Bank - item.Price + let p = { player with Bank = newBalance ; Inventory = Array.append [| item |] player.Inventory } do! DbService.updatePlayer p do! sendFollowUpMessage ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining" }) @@ -65,15 +65,15 @@ let handleSell (ctx : IDiscordContext) itemId = |> handleResultWithResponse ctx (fun player -> async { let updatedPlayer = { player with - Bank = player.Bank + item.Cost - Arsenal = player.Arsenal |> Array.filter (fun i -> i.Id <> itemId) - Actions = + Bank = player.Bank + item.Price + Inventory = player.Inventory |> Array.filter (fun i -> i.Id <> itemId) + Events = if item.Type = ItemType.Shield - then player.Actions |> Array.filter (fun a -> a.ActionId <> itemId) - else player.Actions + then player.Events |> Array.filter (fun a -> a.ItemId <> itemId) + else player.Events } do! DbService.updatePlayer updatedPlayer - do! sendFollowUpMessage ctx $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}" + do! sendFollowUpMessage ctx $"Sold {item.Type} {item.Name} for {item.Price}! Current Balance: {updatedPlayer.Bank}" }) }) diff --git a/Bot/Thief.fs b/Bot/Thief.fs index 9090245..1600d91 100644 --- a/Bot/Thief.fs +++ b/Bot/Thief.fs @@ -7,11 +7,6 @@ open DSharpPlus.Entities open DSharpPlus.SlashCommands open Degenz.Messaging -[] -let StealActionId = 12 -[] -let VictimDefenseActionId = 12 - let ThiefCooldown = TimeSpan.FromMinutes(1) let VictimRecovery = TimeSpan.FromHours(6) @@ -89,8 +84,8 @@ let getResultEmbed targetName result = let checkVictimStealingCooldown defender attacker = defender |> Player.removeExpiredActions false - |> Player.getDefenses - |> Array.tryFind (fun act -> act.ActionId = VictimDefenseActionId) + |> Player.getShieldEvents + |> Array.tryFind (fun pe -> pe.Type = PlayerEventType.Mugged) |> function | Some act -> let cooldown = VictimRecovery - (DateTime.UtcNow - act.Timestamp) @@ -101,8 +96,8 @@ let checkVictimStealingCooldown defender attacker = // TODO: Look for ways to generalize checking for action cooldowns let checkThiefCooldown attacker = attacker - |> Player.getAttacks - |> Array.tryFind (fun act -> act.ActionId = StealActionId) + |> Player.getHackEvents + |> Array.tryFind (fun pe -> pe.Type = PlayerEventType.Steal) |> function | Some act -> if ThiefCooldown > (DateTime.UtcNow - act.Timestamp) then @@ -144,7 +139,7 @@ let handleSteal (ctx : IDiscordContext) = let split = ctx.GetInteractionId().Split("-") let answer = split.[1] - let handleYes player = async { + let handleYes (player : PlayerData) = async { let targetId = uint64 split.[2] let targetName = split.[3] let chance = double split.[4] @@ -154,7 +149,7 @@ let handleSteal (ctx : IDiscordContext) = let result = chance >= rand.NextDouble() , rand.Next(0,3) = 0 let dp = { DiscordPlayer.Id = targetId ; DiscordPlayer.Name = targetName } - let stealAction = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = false ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow } + let stealAction result = { ItemId = -1 ; Type = PlayerEventType.Steal ; Result = result ; Adversary = dp ; Timestamp = DateTime.UtcNow } // TODO: Send event to the hall of privacy // TODO: We need to check if the player is on cooldown match result with @@ -166,24 +161,24 @@ let handleSteal (ctx : IDiscordContext) = do! Messaging.sendFollowUpEmbed ctx (embed.Build()) match! DbService.tryFindPlayer targetId with | Some t -> - let action = { ActionId = VictimDefenseActionId ; Type = Defense ; Timestamp = DateTime.UtcNow } - let actions = t |> Player.removeExpiredActions false |> fun p -> Array.append [| action |] p.Actions - do! DbService.updatePlayer { t with Bank = max (t.Bank - prize) 0 ; Actions = actions } + let mugged = { ItemId = -1 ; Type = PlayerEventType.Mugged ; Result = PlayerEventResult.Failed ; Adversary = player.basicPlayer ; Timestamp = DateTime.UtcNow } + let actions = t |> Player.removeExpiredActions false |> fun p -> Array.append [| mugged |] p.Events + do! DbService.updatePlayer { t with Bank = max (t.Bank - prize) 0 ; Events = actions } | None -> () - let action = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = true ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow } - let actions = player |> Player.removeExpiredActions false |> fun p -> Array.append [| action |] p.Actions - do! DbService.updatePlayer { player with Bank = player.Bank + prize ; XP = player.XP + xp ; Actions = actions } + let stole = { ItemId = -1 ; Type = PlayerEventType.Steal ; Result = PlayerEventResult.Successful ; Adversary = dp ; Timestamp = DateTime.UtcNow } + let actions = player |> Player.removeExpiredActions false |> fun p -> Array.append [| stole |] p.Events + do! DbService.updatePlayer { player with Bank = player.Bank + prize ; XP = player.XP + xp ; Events = actions } let newLevel = XP.getLevel (player.XP + xp) // if XP.getLevel player.XP < newLevel then do! Async.Sleep 2000 do! ctx.FollowUp (XP.getRewardsEmbed 1 player) |> Async.AwaitTask | false , false -> let embed = getResultEmbed targetName VictimRanAway - do! DbService.updatePlayer { player with Actions = Array.append [| stealAction |] player.Actions } + do! DbService.updatePlayer { player with Events = Array.append [| stealAction PlayerEventResult.Neutral |] player.Events } do! Messaging.sendFollowUpEmbed ctx (embed.Build()) | false , true -> let embed = getResultEmbed targetName WentToPrison - do! DbService.updatePlayer { player with Actions = Array.append [| stealAction |] player.Actions } + do! DbService.updatePlayer { player with Events = Array.append [| stealAction PlayerEventResult.Neutral |] player.Events } do! Messaging.sendFollowUpEmbed ctx (embed.Build()) do! Async.Sleep 2000 let role = ctx.GetGuild().GetRole(GuildEnvironment.rolePrisoner) diff --git a/Bot/Trainer.fs b/Bot/Trainer.fs index 0dccd0c..2039043 100644 --- a/Bot/Trainer.fs +++ b/Bot/Trainer.fs @@ -53,7 +53,7 @@ let defend (ctx : IDiscordContext) = Game.executePlayerAction ctx (fun player -> async { let playerWithShields = match Player.getShields player with - | [||] -> { player with Arsenal = [| defaultShield |] } + | [||] -> { player with Inventory = [| defaultShield |] } | _ -> player let embed = Embeds.pickDefense "Trainer-2" playerWithShields true @@ -78,12 +78,11 @@ let handleDefense (ctx : IDiscordContext) = let embed = Embeds.responseCreatedShield shield do! ctx.FollowUp embed |> Async.AwaitTask do! Async.Sleep 4000 - let weakHack = Game.getGoodAgainst shield.Class - do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{snd weakHack}**" + do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **VIRUS**" do! Async.Sleep 5000 do! sendMessage' $"❌ HACKING FAILED!\n\n{player.Name} defended hack from <@{GuildEnvironment.botIdHackerBattle}>!" do! Async.Sleep 4000 - do! sendFollowUpMessageWithButton ctx (handleDefenseMsg shieldId (snd weakHack)) + do! sendFollowUpMessageWithButton ctx (handleDefenseMsg shieldId "VIRUS") }) let handleTrainerStep3 (ctx : IDiscordContext) = @@ -110,7 +109,7 @@ let attack (target : DiscordUser) (ctx : IDiscordContext) = | true -> let playerWithAttacks = match Player.getHacks player with - | [||] -> { player with Arsenal = [| defaultHack |] } + | [||] -> { player with Inventory = [| defaultHack |] } | _ -> player let bot = { player with DiscordId = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" } let embed = Embeds.pickHack "Trainer-4" playerWithAttacks bot true @@ -154,14 +153,14 @@ let handleAttack (ctx : IDiscordContext) = let rand = System.Random(System.Guid.NewGuid().GetHashCode()) let freeHack = Armory.hacks.[rand.Next(0, 3)] let freeShield = Armory.shields.[rand.Next(0, 3)] - let hackMoney = if hasHacks then defaultHack.Cost else 0 - let shieldMoney = if hasShields then defaultShield.Cost else 0 + let hackMoney = if hasHacks then defaultHack.Price else 0 + let shieldMoney = if hasShields then defaultShield.Price else 0 let giftMsg = match hasHacks , hasShields with | true , true -> $"I'm going to give you these {hackMoney + shieldMoney} 💰$GBT" - | false , true -> $"I'm going to gift you a hack, `{freeHack.Name}` and {defaultHack.Cost} 💰$GBT" - | true , false -> $"I'm going to gift you a shield, `{freeShield.Name}` and {defaultHack.Cost} 💰$GBT" + | false , true -> $"I'm going to gift you a hack, `{freeHack.Name}` and {defaultHack.Price} 💰$GBT" + | true , false -> $"I'm going to gift you a shield, `{freeShield.Name}` and {defaultHack.Price} 💰$GBT" | false , false -> $"I'm going to gift you a hack,`{freeHack.Name}` and a shield, `{freeShield.Name}`" sb.Append(giftMsg) |> ignore @@ -171,27 +170,25 @@ let handleAttack (ctx : IDiscordContext) = 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 - } + Events = [ + { PlayerEvent.Timestamp = System.DateTime.UtcNow + PlayerEvent.Adversary = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" } + PlayerEvent.Type = PlayerEventType.Shield + PlayerEvent.Result = PlayerEventResult.Successful + PlayerEvent.ItemId = defaultHack.Id } + if not hasShields && Array.exists (fun act -> act.ItemId = freeShield.Id) player.Events |> not then { + PlayerEvent.Timestamp = System.DateTime.UtcNow + PlayerEvent.Adversary = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" } + PlayerEvent.Type = PlayerEventType.Shield + PlayerEvent.Result = PlayerEventResult.Successful + PlayerEvent.ItemId = defaultHack.Id } ] |> Seq.toArray - |> Array.append player.Actions - Arsenal = [ + |> Array.append player.Events + Inventory = [ if not hasHacks then freeHack if not hasShields then freeShield ] |> Seq.toArray - |> Array.append player.Arsenal + |> Array.append player.Inventory } do! DbService.updatePlayer updatedPlayer do! sendFollowUpMessage ctx (sb.ToString()) diff --git a/DbService/DbService.fs b/DbService/DbService.fs index 5efacc2..c8dae60 100644 --- a/DbService/DbService.fs +++ b/DbService/DbService.fs @@ -26,26 +26,6 @@ type PlayerEntry = XP : int Bank : int } -let private actionToAttack (action : Action) (hack : AttackResult) = - { ActionId = action.ActionId - Result = hack.Result - Target = hack.Target - Timestamp = action.Timestamp } - -let private actionToDefense (action : Action) = - { ActionId = action.ActionId - Timestamp = action.Timestamp } - -let private attackToAction (attack : AttackAction) = - { ActionId = attack.ActionId - Type = Attack { Target = attack.Target ; Result = attack.Result } - Timestamp = attack.Timestamp } - -let private defenseToAction (action : DefenseAction) = - { ActionId = action.ActionId - Type = Defense - Timestamp = action.Timestamp } - let private playerMap (player : PlayerData) = { DiscordId = player.DiscordId Name = player.Name @@ -60,13 +40,13 @@ let tryWithDefault (bson : BsonDocument) field (defaultValue : 'a) (map : BsonVa let private mapBack (bson : BsonDocument) : PlayerData = { DiscordId = tryWithDefault bson "Player.DiscordId" 0uL (fun v -> v.AsInt64 |> uint64) Name = tryWithDefault bson "Player.Name" "Empty" (fun v -> v.AsString) - Arsenal = + Inventory = tryWithDefault bson "Inventory" [||] (fun v -> v.AsBsonArray |> Seq.map (fun (bv : BsonValue) -> bv.AsInt32) |> Seq.map (fun w -> Armory.battleItems |> Array.find (fun w' -> w = w'.Id)) |> Seq.toArray) - Actions = tryWithDefault bson "Events" [||] (fun _ -> [||]) + Events = tryWithDefault bson "Events" [||] (fun _ -> [||]) Traits = tryWithDefault bson "Traits" PlayerTraits.empty (fun _ -> PlayerTraits.empty) Achievements = tryWithDefault bson "Achievements" [||] (fun _ -> [||]) XP = tryWithDefault bson "XP" 0 (fun _ -> 0) @@ -96,9 +76,9 @@ let updatePlayer (player : PlayerData) = let update = Builders.Update .Set("Player", playerMap player) .AddToSet("Traits", player.Traits) - .AddToSet("Events", player.Actions) + .AddToSet("Events", player.Events) .AddToSet("Achievements", player.Achievements) - .AddToSet("Inventory", player.Arsenal) + .AddToSet("Inventory", player.Inventory) return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore } diff --git a/Shared/Shared.fs b/Shared/Shared.fs index c69ebd3..c5071c1 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -36,11 +36,6 @@ module Types = [] type GBT - type BattleClass = - | Network - | Exploit - | Penetration - type HackId = | Virus = 0 | RemoteAccess = 1 @@ -58,9 +53,8 @@ module Types = type BattleItem = { Id : int Name : string - Cost : int + Price : int Type : ItemType - Class : BattleClass Power : int Cooldown : int } @@ -71,51 +65,49 @@ module Types = [] type DiscordPlayer = { Id: uint64; Name: string } + with static member empty = { Id = 0uL ; Name = "None" } + + type PlayerEventResult = + | Successful = 0 + | Neutral = 1 + | Failed = 2 + + type PlayerEventType = + | Hack = 0 + | Shield = 1 + | Steal = 2 + | Mugged = 3 + | Imprison = 3 + | Imprisoned = 3 [] - type AttackResult = { - Result : bool - Target : DiscordPlayer - } - - type ActionType = - | Attack of AttackResult - | Defense - - [] - type Action = - { ActionId : int - Type : ActionType + type PlayerEvent = + { Type : PlayerEventType + Result : PlayerEventResult + Adversary : DiscordPlayer + ItemId : int Timestamp : DateTime } - type StatAmount = int - type XPAmount = int - - type AttributeId = - | Strength = 0 - | Cunning = 1 - type PlayerTraits = { Strength : int Focus : int + Luck : int + Charisma : int } - with static member empty = { Strength = 0 ; Focus = 0 } - - type PlayerXP = { - Amount : XPAmount - } + with static member empty = { Strength = 0 ; Focus = 0 ; Luck = 0 ; Charisma = 0 } [] type PlayerData = { DiscordId : uint64 Name : string - Arsenal : BattleItem array - Actions : Action array + Inventory : BattleItem array + Events : PlayerEvent array Traits : PlayerTraits Achievements : string array XP : int Bank : int } + with member this.basicPlayer = { Id = this.DiscordId ; Name = this.Name } module Armory = let battleItems =