diff --git a/Bot/Embeds.fs b/Bot/Embeds.fs index a27dbfd..b4d8e28 100644 --- a/Bot/Embeds.fs +++ b/Bot/Embeds.fs @@ -28,13 +28,13 @@ let getItemGif id = | ItemId.Cypher -> "https://s10.gifyu.com/images/Cypher-Smaller.gif" | _ -> hackGif -let constructButtons (actionId: string) (buttonInfo : string) (player: PlayerData) items ignoreCooldown = +let constructButtons (actionId: string) (buttonInfo : string) (player: PlayerData) (items : Inventory) ignoreCooldown = items |> List.map (fun item -> let action = player.Events - |> List.tryFind (fun i -> - match i.Type with + |> List.tryFind (fun event -> + match event.Type with | Hacking h -> h.HackId = item.Id && h.IsInstigator | Shielding id -> id = item.Id | _ -> false) @@ -48,7 +48,7 @@ let constructButtons (actionId: string) (buttonInfo : string) (player: PlayerDat |> Seq.cast let pickDefense actionId player isTrainer = - let shieldItems = player.Inventory |> Inventory.filterByShields + let shieldItems = player.Inventory |> Inventory.getItemsByType ItemType.Shield let buttons = constructButtons actionId (string player.DiscordId) player shieldItems isTrainer let embed = @@ -56,10 +56,10 @@ let pickDefense actionId player isTrainer = .WithTitle("Shield Defense") .WithDescription("Pick a shield to protect yourself from hacks") - for shield in Inventory.getShieldItems player.Inventory do + for shield in Inventory.getShields player.Inventory do let hours = TimeSpan.FromMinutes(int shield.Cooldown).TotalHours let against = WeaponClass.getGoodAgainst(shield.Class) |> snd - embed.AddField(shield.Name, $"Active {hours} hours\nDefeats {against}", true) |> ignore + embed.AddField(shield.Item.Name, $"Active {hours} hours\nDefeats {against}", true) |> ignore DiscordFollowupMessageBuilder() .AddComponents(buttons) @@ -67,7 +67,7 @@ let pickDefense actionId player isTrainer = .AsEphemeral(true) let pickHack actionId attacker defender isTrainer = - let hackItems = attacker.Inventory |> Inventory.filterByHacks + let hackItems = attacker.Inventory |> Inventory.getItemsByType ItemType.Hack let buttons = constructButtons actionId $"{defender.DiscordId}-{defender.Name}" attacker hackItems isTrainer let stealMsg = if not isTrainer then $"{defender.Name} has **{defender.Bank} $GBT** we can take from them. " else "" @@ -77,9 +77,9 @@ let pickHack actionId attacker defender isTrainer = .WithDescription($"{stealMsg}Pick the hack you want to use.") if not isTrainer then - for hack in Inventory.getHackItems attacker.Inventory do + for hack in Inventory.getHacks attacker.Inventory do let amount = if hack.Power > int defender.Bank then int defender.Bank else hack.Power - embed.AddField(hack.Name, $"Cooldown {hack.Cooldown} mins\nExtract {amount} $GBT", true) |> ignore + embed.AddField(hack.Item.Name, $"Cooldown {hack.Cooldown} mins\nExtract {amount} $GBT", true) |> ignore DiscordFollowupMessageBuilder() .AddComponents(buttons) @@ -89,9 +89,9 @@ let pickHack actionId attacker defender isTrainer = let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : HackItem) = let embed = DiscordEmbedBuilder() - .WithImageUrl(getItemGif hack.Id) + .WithImageUrl(getItemGif hack.Item.Id) .WithTitle("Hack Attack") - .WithDescription($"You successfully hacked <@{targetId}> using {hack.Name}" + .WithDescription($"You successfully hacked <@{targetId}> using {hack.Item.Name}" + (if earnedMoney then $", and took {amountTaken} 💰$GBT from them!" else "!")) DiscordFollowupMessageBuilder() @@ -99,9 +99,9 @@ let responseSuccessfulHack earnedMoney (targetId : uint64) amountTaken (hack : H .AsEphemeral(true) let responseCreatedShield (shield : ShieldItem) = - let embed = DiscordEmbedBuilder().WithImageUrl(getItemGif shield.Id) + let embed = DiscordEmbedBuilder().WithImageUrl(getItemGif shield.Item.Id) embed.Title <- "Mounted Shield" - embed.Description <- $"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).TotalHours} hours" + embed.Description <- $"Mounted {shield.Item.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).TotalHours} hours" DiscordFollowupMessageBuilder() .AddEmbed(embed) diff --git a/Bot/GameHelpers.fs b/Bot/GameHelpers.fs index a80bdea..5a287fa 100644 --- a/Bot/GameHelpers.fs +++ b/Bot/GameHelpers.fs @@ -3,102 +3,60 @@ namespace Degenz open System open DSharpPlus open DSharpPlus.Entities +open Degenz open Newtonsoft.Json module Armory = - let weapons = +// let weapons : ItemDetails list= [] + let weapons : ItemDetails list = let file = System.IO.File.ReadAllText("Items.json") // let file = System.IO.File.ReadAllText("Bot/Items.json") - JsonConvert.DeserializeObject(file) + JsonConvert.DeserializeObject(file) |> Array.toList module Inventory = - let itemToHack item power hackClass cooldown = { - Id = item.Id - Name = item.Name - Price = item.Price - Power = power - Class = hackClass - Cooldown = cooldown - } - let itemToShield item hackClass cooldown = { - Id = item.Id - Name = item.Name - Price = item.Price - Class = hackClass - Cooldown = cooldown - } - let itemToFood item targetStat boostAmount = { - Id = item.Id - Name = item.Name - Price = item.Price - TargetStat = targetStat - BoostAmount = boostAmount - } - let hackToItem (hack : HackItem) = { - Id = hack.Id - Name = hack.Name - Price = hack.Price - Details = Hack (hack.Power, hack.Class, hack.Cooldown) - } - let shieldToItem (shield : ShieldItem) = { - Id = shield.Id - Name = shield.Name - Price = shield.Price - Details = Shield (shield.Class, shield.Cooldown) - } + let getItemsByType itemType inventory = + match itemType with + | ItemType.Hack -> inventory |> List.filter (fun item -> match item with Hack _ -> true | _ -> false) + | ItemType.Shield -> inventory |> List.filter (fun item -> match item with Shield _ -> true | _ -> false) + | ItemType.Food -> inventory |> List.filter (fun item -> match item with Food _ -> true | _ -> false) + | ItemType.Accessory -> inventory |> List.filter (fun item -> match item with Accessory _ -> true | _ -> false) - let filterByHacks inventory = - inventory |> List.filter (fun item -> match item.Details with Hack _ -> true | _ -> false) - let filterByShields inventory = - inventory |> List.filter (fun item -> match item.Details with Shield _ -> true | _ -> false) - let filterByWeapons inventory = - inventory |> List.filter (fun item -> match item.Details with Hack _ | Shield _ -> true | _ -> false) - let filterByFood inventory = - inventory |> List.filter (fun item -> match item.Details with Food _ -> true | _ -> false) - let filterByAccessories inventory = - inventory |> List.filter (fun item -> match item.Details with Accessory _ -> true | _ -> false) + let findItemById id (inventory : Inventory) = inventory |> List.find (fun item -> item.Id = id) - let getHackItems inventory = - inventory - |> List.choose (fun item -> match item.Details with Hack (p,cl,co) -> Some (itemToHack item p cl co) | _ -> None) - |> List.sortBy (fun item -> item.Id) - let getShieldItems inventory = - inventory - |> List.choose (fun item -> match item.Details with Shield (cl,co) -> Some (itemToShield item cl co) | _ -> None) - |> List.sortBy (fun item -> item.Id) - let getFoodItems inventory = - inventory - |> List.choose (fun item -> match item.Details with Food(t,b) -> Some (itemToFood item t b) | _ -> None) - |> List.sortBy (fun item -> item.Id) - let findItemById id inventory = inventory |> List.find (fun item -> item.Id = id) - let tryFindItemById id inventory = inventory |> List.tryFind (fun item -> item.Id = id) let findHackById id inventory = - inventory |> getHackItems |> List.pick (fun item -> if item.Id = id then Some item else None) + inventory |> List.pick (fun item -> match item with | Hack h -> (if h.Item.Id = id then Some h else None) | _ -> None) let findShieldById id inventory = - inventory |> getShieldItems |> List.pick (fun item -> if item.Id = id then Some item else None) + inventory |> List.pick (fun item -> match item with | Shield s -> (if s.Item.Id = id then Some s else None) | _ -> None) let findFoodById id inventory = - inventory |> getFoodItems |> List.pick (fun item -> if item.Id = id then Some item else None) - let tryFindHackById id inventory = - inventory |> getHackItems |> List.tryFind (fun item -> item.Id = id) - let tryFindShieldById id inventory = - inventory |> getShieldItems |> List.tryFind (fun item -> item.Id = id) + inventory |> List.pick (fun item -> match item with | Food f -> (if f.Item.Id = id then Some f else None) | _ -> None) + let findAccessoryById id inventory = + inventory |> List.pick (fun item -> match item with | Accessory a -> (if a.Item.Id = id then Some a else None) | _ -> None) + + let getHacks inventory = + inventory |> List.choose (fun item -> match item with | Hack h -> Some h | _ -> None) + let getShields inventory = + inventory |> List.choose (fun item -> match item with | Shield s -> Some s | _ -> None) + let getFoods inventory = + inventory |> List.choose (fun item -> match item with | Food f -> Some f | _ -> None) + let getAccessories inventory = + inventory |> List.choose (fun item -> match item with | Accessory a -> Some a | _ -> None) module WeaponClass = - let SameTargetAttackCooldown = System.TimeSpan.FromHours(1) + let SameTargetAttackCooldown = TimeSpan.FromHours(1) let getClassButtonColor item = - match item.Details with - | Hack (_,0,_) | Shield (0,_) -> ButtonStyle.Danger - | Hack (_,1,_) | Shield (1,_) -> ButtonStyle.Primary - | Hack (_,2,_) | Shield (2,_) -> ButtonStyle.Success + match ItemDetails.getClass item with + | 0 -> ButtonStyle.Danger + | 1 -> ButtonStyle.Primary + | 2 -> ButtonStyle.Success | _ -> ButtonStyle.Primary let getClassEmbedColor item = - match item.Details with - | Hack (_,0,_) | Shield (0,_) -> DiscordColor.Red - | Hack (_,1,_) | Shield (1,_) -> DiscordColor.Blurple - | Hack (_,2,_) | Shield (2,_) -> DiscordColor.Green + match ItemDetails.getClass item with + | 0 -> DiscordColor.Red + | 1 -> DiscordColor.Blurple + | 2 -> DiscordColor.Green | _ -> DiscordColor.Blurple let getGoodAgainst = function @@ -118,8 +76,8 @@ module Player = let actions = player.Events |> List.filter (fun (act : PlayerEvent) -> - let cooldown = System.TimeSpan.FromMinutes(int act.Cooldown) - System.DateTime.UtcNow - act.Timestamp < cooldown) + let cooldown = TimeSpan.FromMinutes(int act.Cooldown) + DateTime.UtcNow - act.Timestamp < cooldown) { player with Events = actions } let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0 } @@ -138,14 +96,14 @@ module PlayerStats = // let hoursElapsed = (DateTime.UtcNow - lastRead).Hours // let totalDecay = float hoursElapsed * statConfig.BaseDecayRate let modMinMax = - let min = items |> List.sumBy (fun item -> match item.Details with | Accessory(_,floorBoost,_) -> floorBoost | _ -> 0) - let max = items |> List.sumBy (fun item -> match item.Details with | Accessory(_,_,ceilBoost) -> ceilBoost | _ -> 0) + let min = items |> List.sumBy (fun item -> match item with | Accessory a -> a.FloorBoost | _ -> 0) + let max = items |> List.sumBy (fun item -> match item with | Accessory a -> a.CeilBoost | _ -> 0) Range.create (statConfig.BaseRange.Min + min) (statConfig.BaseRange.Max + max) let amountAfterDecay = modMinMax |> Range.constrain amount { Id = statId ; Amount = amountAfterDecay ; ModRange = modMinMax ; LastRead = DateTime.UtcNow } module Arsenal = - let battleItemFormat (items : Item list) = + let battleItemFormat (items : ItemDetails list) = match items with | [] -> "None" | _ -> items |> List.map (fun item -> item.Name) |> String.concat ", " @@ -155,23 +113,23 @@ module Arsenal = | [] -> "None" | acts -> acts - |> List.map (fun act -> - match act.Type with + |> List.map (fun event -> + match event.Type with | Hacking h -> let item = Armory.weapons |> Inventory.findHackById h.HackId - let cooldown = Messaging.getTimeText false WeaponClass.SameTargetAttackCooldown act.Timestamp - $"Hacked {h.Adversary.Name} with {item.Name} {cooldown} ago" + let cooldown = Messaging.getTimeText false WeaponClass.SameTargetAttackCooldown event.Timestamp + $"Hacked {h.Adversary.Name} with {item.Item.Name} {cooldown} ago" | Shielding id -> - let item = Armory.weapons |> Inventory.findHackById id - let cooldown = Messaging.getTimeText true (System.TimeSpan.FromMinutes(int act.Cooldown)) act.Timestamp - $"{item.Name} Shield active for {cooldown}" + let item = Armory.weapons |> Inventory.findShieldById id + let cooldown = Messaging.getTimeText true (TimeSpan.FromMinutes(int event.Cooldown)) event.Timestamp + $"{item.Item.Name} Shield active for {cooldown}" | _ -> "") - |> List.filter (System.String.IsNullOrWhiteSpace >> not) + |> List.filter (String.IsNullOrWhiteSpace >> not) |> String.concat "\n" let statusFormat p = let hacks = Player.getHackEvents p - $"**Hacks:** {Inventory.filterByHacks p.Inventory |> battleItemFormat}\n - **Shields:** {Inventory.filterByShields p.Inventory |> battleItemFormat}\n - **Hack Attacks:**\n{ hacks |> List.take (min hacks.Length 10) |> actionFormat}\n + $"**Hacks:** {Inventory.getItemsByType ItemType.Hack p.Inventory |> battleItemFormat}\n + **Shields:** {Inventory.getItemsByType ItemType.Hack p.Inventory |> battleItemFormat}\n + **Hack Attacks:**\n{hacks |> List.take (min hacks.Length 10) |> actionFormat}\n **Active Shields:**\n{Player.getShieldEvents p |> actionFormat}" diff --git a/Bot/GameTypes.fs b/Bot/GameTypes.fs index 8794e15..90fe63a 100644 --- a/Bot/GameTypes.fs +++ b/Bot/GameTypes.fs @@ -82,40 +82,6 @@ type PlayerEvent = Cooldown : int Timestamp : DateTime } -type HackItem = { - Id : int - Name : string - Price : int - Power : int - Class : int - Cooldown : int -} - -type ShieldItem = { - Id : int - Name : string - Price : int - Class : int - Cooldown : int -} - -type FoodItem = { - Id : int - Name : string - Price : int - TargetStat : StatId - BoostAmount : int -} - -type AccessoryItem = { - Id : int - Name : string - Price : int - TargetStat : StatId - FloorBoost : int - CeilBoost : int -} - [] type ItemType = | Hack @@ -123,20 +89,76 @@ type ItemType = | Food | Accessory -type ItemDetails = - | Hack of power : int * hackClass : int * cooldown : int - | Shield of shieldClass : int * cooldown : int - | Food of targetStat : StatId * boostAmount : int - | Accessory of targetStat : StatId * floorBoost : int * ceilBoost : int -and Item = { +type Item = { Id : int Name : string Price : int -// Type : ItemType - Details : ItemDetails } -and Inventory = Item list -and PlayerData = { + +type HackItem = { + Power : int + Class : int + Cooldown : int + Item : Item +} + +type ShieldItem = { + Class : int + Cooldown : int + Item : Item +} + +type FoodItem = { + TargetStat : StatId + BoostAmount : int + Item : Item +} + +type AccessoryItem = { + TargetStat : StatId + FloorBoost : int + CeilBoost : int + Item : Item +} + +type ItemDetails = + | Hack of HackItem + | Shield of ShieldItem + | Food of FoodItem + | Accessory of AccessoryItem + member this.Id = + match this with + | Hack i -> i.Item.Id + | Shield i -> i.Item.Id + | Food i -> i.Item.Id + | Accessory i -> i.Item.Id + member this.Name = + match this with + | Hack i -> i.Item.Name + | Shield i -> i.Item.Name + | Food i -> i.Item.Name + | Accessory i -> i.Item.Name + member this.Price = + match this with + | Hack i -> i.Item.Price + | Shield i -> i.Item.Price + | Food i -> i.Item.Price + | Accessory i -> i.Item.Price + member this.getItem = + match this with + | Hack i -> i.Item + | Shield i -> i.Item + | Food i -> i.Item + | Accessory i -> i.Item + static member getClass = function + | Hack i -> i.Class + | Shield i -> i.Class + | Food _ -> -1 + | Accessory _ -> -1 + +type Inventory = ItemDetails list + +type PlayerData = { DiscordId : uint64 Name : string Inventory : Inventory diff --git a/Bot/Games/HackerBattle.fs b/Bot/Games/HackerBattle.fs index 62e983e..e5d0826 100644 --- a/Bot/Games/HackerBattle.fs +++ b/Bot/Games/HackerBattle.fs @@ -44,7 +44,7 @@ let checkWeaponHasCooldown (weapon : Item) attacker = | None -> Ok attacker let checkHasEmptyHacks attacker = - match Inventory.getHackItems attacker.Inventory with + match Inventory.getHacks attacker.Inventory with | [] -> Error $"You currently do not have any Hacks to take 💰$GBT from others. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one." | _ -> Ok attacker @@ -74,7 +74,7 @@ let runHackerBattle defender (hack : HackItem) = |> fun p -> p.Events |> List.choose (fun event -> match event.Type with - | Shielding id -> defender.Inventory |> Inventory.getShieldItems |> List.find (fun item -> item.Id = id) |> Some + | Shielding id -> defender.Inventory |> Inventory.getShields |> List.find (fun item -> item.Item.Id = id) |> Some | _ -> None) |> List.map (fun shield -> if hack.Class = shield.Class then Weak else Strong) |> List.contains Weak @@ -84,7 +84,7 @@ let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerDa { p with Events = attack::p.Events ; Bank = max (p.Bank + amount) 0 } let event isDefenderEvent = let hackEvent = { - HackId = hack.Id + HackId = hack.Item.Id Adversary = if isDefenderEvent then attacker.toDiscordPlayer else defender.toDiscordPlayer IsInstigator = not isDefenderEvent Success = successfulHack @@ -151,7 +151,6 @@ let handleAttack (ctx : IDiscordContext) = let tokens = ctx.GetInteractionId().Split("-") let hackId = int tokens.[1] let hack = Armory.weapons |> Inventory.findHackById hackId - let hackAsItem = Inventory.hackToItem hack let resultId , targetId = UInt64.TryParse tokens.[2] let! resultTarget = DbService.tryFindPlayer targetId @@ -160,8 +159,8 @@ let handleAttack (ctx : IDiscordContext) = do! attacker |> Player.removeExpiredActions |> checkAlreadyHackedTarget defender - >>= checkPlayerOwnsWeapon hackAsItem - >>= checkWeaponHasCooldown hackAsItem + >>= checkPlayerOwnsWeapon hack.Item + >>= checkWeaponHasCooldown hack.Item |> function | Ok atkr -> runHackerBattle defender hack @@ -174,7 +173,7 @@ let handleAttack (ctx : IDiscordContext) = let defend (ctx : IDiscordContext) = executePlayerAction ctx (fun player -> async { - if player.Inventory |> Inventory.filterByShields |> List.length > 0 then + if player.Inventory |> Inventory.getShields |> List.length > 0 then let p = Player.removeExpiredActions player let embed = Embeds.pickDefense "Defend" p false do! ctx.FollowUp embed |> Async.AwaitTask @@ -188,12 +187,11 @@ let handleDefense (ctx : IDiscordContext) = let tokens = ctx.GetInteractionId().Split("-") let shieldId = int tokens.[1] let shield = Armory.weapons |> Inventory.findShieldById shieldId - let shieldAsItem = Inventory.shieldToItem shield do! player - |> checkPlayerOwnsWeapon shieldAsItem + |> checkPlayerOwnsWeapon shield.Item >>= checkPlayerHasShieldSlotsAvailable - >>= checkWeaponHasCooldown shieldAsItem + >>= checkWeaponHasCooldown shield.Item |> handleResultWithResponse ctx (fun p -> async { let embed = Embeds.responseCreatedShield shield do! ctx.FollowUp embed |> Async.AwaitTask @@ -244,6 +242,37 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve do! eventCtx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } +let invite (ctx : IDiscordContext) = + task { + let channel = ctx.GetGuild().GetChannel(927449884204867664uL) + let invite = channel.CreateInviteAsync(reason = "I MEAN WHY NOT") |> Async.AwaitTask |> Async.RunSynchronously + + printfn "The invite code is %s" invite.Code + + do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, DiscordInteractionResponseBuilder().AsEphemeral(true).WithContent($"https://discord.gg/{invite.Code}")) |> Async.AwaitTask + } + +//let invite (ctx : IDiscordContext) = +// task { +// let code = Guid.NewGuid().ToString().Substring(0, 7) +// +//// let embed1 = +//// DiscordEmbedBuilder() +//// .WithImageUrl("https://pbs.twimg.com/profile_banners/1449270642340089856/1640071520/1500x500") +// let embed2 = +// DiscordEmbedBuilder() +// .WithDescription($"Send this invite to your friend, when they join, type the `/enter-code` slash command\n\n```{code}```") +// .WithImageUrl("https://pbs.twimg.com/profile_banners/1449270642340089856/1640071520/1500x500") +// .WithTitle("Invite Code") +// +// let msg = +// DiscordInteractionResponseBuilder() +// .AsEphemeral(true) +//// .AddEmbed(embed1) +// .AddEmbed(embed2) +// +// do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, msg) +// } type HackerGame() = inherit ApplicationCommandModule () @@ -267,6 +296,10 @@ type HackerGame() = do! Messaging.sendSimpleResponse ctx msg } + [] + member this.CreateInvite (ctx : InteractionContext) = + invite (DiscordInteractionContext ctx) + [] member this.Arsenal (ctx : InteractionContext) = enforceChannels (DiscordInteractionContext ctx) (Trainer.handleArsenal) arsenal diff --git a/Bot/Games/Store.fs b/Bot/Games/Store.fs index a80b070..77b0f13 100644 --- a/Bot/Games/Store.fs +++ b/Bot/Games/Store.fs @@ -15,27 +15,27 @@ let getBuyItemsEmbed (playerInventory : Inventory) (storeInventory : Inventory) storeInventory |> List.map (fun item -> let embed = DiscordEmbedBuilder() - match item.Details with - | Hack(power,_,cooldown) -> - embed.AddField($"$GBT Reward |", string power, true) - .AddField("Cooldown |", $"{TimeSpan.FromMinutes(int cooldown).Minutes} minutes", true) + match item with + | Hack hack -> + embed.AddField($"$GBT Reward |", string hack.Power, true) + .AddField("Cooldown |", $"{TimeSpan.FromMinutes(int hack.Cooldown).Minutes} minutes", true) .WithThumbnail(Embeds.getItemIcon item.Id) |> ignore - | Shield(shieldClass,cooldown) -> - embed.AddField($"Strong against |", WeaponClass.getGoodAgainst shieldClass |> snd |> string, true) + | Shield shield -> + embed.AddField($"Strong against |", WeaponClass.getGoodAgainst shield.Class |> snd |> string, true) // .AddField($"Defensive Strength |", string item.Power, true) - .AddField("Active For |", $"{TimeSpan.FromMinutes(int cooldown).Hours} hours", true) + .AddField("Active For |", $"{TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours", true) .WithThumbnail(Embeds.getItemIcon item.Id) |> ignore - | Food(targetStat, boostAmount) -> - embed.AddField($"Stat |", $"{targetStat}", true) - .AddField($"Amount |", $"+{boostAmount}", true) |> ignore - | Accessory(targetStat, floorBoost, ceilBoost) -> - embed.AddField($"Stat |", $"{targetStat}", true) |> ignore - if floorBoost > 0 then - embed.AddField($"Min Boost |", $"+{floorBoost}", true) |> ignore - if ceilBoost > 0 then - embed.AddField($"Max Boost |", $"+{ceilBoost}", true) |> ignore + | Food food -> + embed.AddField($"Stat |", $"{food.TargetStat}", true) + .AddField($"Amount |", $"+{food.BoostAmount}", true) |> ignore + | Accessory accessory -> + embed.AddField($"Stat |", $"{accessory.TargetStat}", true) |> ignore + if accessory.FloorBoost > 0 then + embed.AddField($"Min Boost |", $"+{accessory.FloorBoost}", true) |> ignore + if accessory.CeilBoost > 0 then + embed.AddField($"Max Boost |", $"+{accessory.CeilBoost}", true) |> ignore embed .AddField("Price 💰", (if item.Price = 0 then "Free" else $"{item.Price} $GBT"), true) .WithColor(WeaponClass.getClassEmbedColor item) @@ -53,7 +53,7 @@ let getBuyItemsEmbed (playerInventory : Inventory) (storeInventory : Inventory) .AddComponents(buttons) .AsEphemeral(true) -let getSellEmbed (items : Item list) = +let getSellEmbed (items : ItemDetails list) = let embeds , buttons = items |> List.map (fun item -> @@ -70,7 +70,7 @@ let getSellEmbed (items : Item list) = .AddComponents(buttons) .AsEphemeral(true) -let getConsumeEmbed (items : Item list) = +let getConsumeEmbed (items : ItemDetails list) = let embeds , buttons = items |> List.groupBy (fun item -> item.Id) @@ -78,7 +78,7 @@ let getConsumeEmbed (items : Item list) = let item = List.head items let foodItem = Inventory.findFoodById itemId items DiscordEmbedBuilder() - .AddField($"{foodItem.Name}", $"Total {items.Length}\nBoosts {foodItem.TargetStat} +{foodItem.BoostAmount}", true) + .AddField($"{foodItem.Item.Name}", $"Total {items.Length}\nBoosts {foodItem.TargetStat} +{foodItem.BoostAmount}", true) .WithTitle($"Food Items") .WithColor(WeaponClass.getClassEmbedColor item) .Build() @@ -130,8 +130,8 @@ let handleBuyItem (ctx : IDiscordContext) itemId = executePlayerAction ctx (fun player -> async { let item = Armory.weapons |> Inventory.findItemById itemId do! player - |> checkHasSufficientFunds item - >>= checkAlreadyOwnsItem item + |> checkHasSufficientFunds item.getItem + >>= checkAlreadyOwnsItem item.getItem |> handleResultWithResponse ctx (fun player -> async { let newBalance = player.Bank - item.Price let p = { player with Bank = newBalance ; Inventory = item::player.Inventory } @@ -145,7 +145,7 @@ let handleSell (ctx : IDiscordContext) itemId = let item = Armory.weapons |> Inventory.findItemById itemId do! player - |> checkSoldItemAlready item + |> checkSoldItemAlready item.getItem |> handleResultWithResponse ctx (fun player -> async { let updatedPlayer = { player with @@ -155,7 +155,7 @@ let handleSell (ctx : IDiscordContext) itemId = do! [ DbService.updatePlayer updatedPlayer |> Async.Ignore DbService.removeShieldEvent updatedPlayer.DiscordId itemId |> Async.Ignore - sendFollowUpMessage ctx $"Sold {item.Details} {item.Name} for {item.Price}! Current Balance: {updatedPlayer.Bank}" ] + sendFollowUpMessage ctx $"Sold {item.Name} for {item.Price}! Current Balance: {updatedPlayer.Bank}" ] |> Async.Parallel |> Async.Ignore }) @@ -202,10 +202,10 @@ type Store() = let checkChannel (ctx : IDiscordContext) = match ctx.GetChannel().Id with - | id when id = GuildEnvironment.channelBackAlley -> buy Inventory.filterByHacks ctx - | id when id = GuildEnvironment.channelArmory -> buy Inventory.filterByShields ctx - | id when id = GuildEnvironment.channelMarket -> buy Inventory.filterByFood ctx - | id when id = GuildEnvironment.channelAccessoryShop -> buy Inventory.filterByAccessories ctx + | id when id = GuildEnvironment.channelBackAlley -> buy (Inventory.getItemsByType ItemType.Hack) ctx + | id when id = GuildEnvironment.channelArmory -> buy (Inventory.getItemsByType ItemType.Shield) ctx + | id when id = GuildEnvironment.channelMarket -> buy (Inventory.getItemsByType ItemType.Food) ctx + | id when id = GuildEnvironment.channelAccessoryShop -> buy (Inventory.getItemsByType ItemType.Accessory) ctx | _ -> task { let msg = $"This channel doesn't have any items to sell" @@ -216,22 +216,22 @@ type Store() = member _.BuyItem (ctx : InteractionContext) = checkChannel (DiscordInteractionContext(ctx)) [] - member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy Inventory.filterByShields) + member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Shield)) [] - member this.BuyFood (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy Inventory.filterByFood) + member this.BuyFood (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Food)) [] - member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" Inventory.filterByHacks) + member this.SellHack (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Hacks" (Inventory.getItemsByType ItemType.Hack)) [] - member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" Inventory.filterByShields) + member this.SellShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Shield)) [] member this.Consume (ctx : InteractionContext) = - enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" Inventory.filterByShields) + enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Food)) - [] - member this.Inventory (ctx : InteractionContext) = - enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" Inventory.filterByShields) +// [] +// member this.Inventory (ctx : InteractionContext) = +// enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType)) diff --git a/Bot/Games/Trainer.fs b/Bot/Games/Trainer.fs index fe5d53f..a8f33b9 100644 --- a/Bot/Games/Trainer.fs +++ b/Bot/Games/Trainer.fs @@ -19,10 +19,10 @@ let TrainerEvents = [ Adversary = Sensei Success = true IsInstigator = true - HackId = defaultHack.Id } } + HackId = defaultHack.Item.Id } } { Timestamp = System.DateTime.UtcNow Cooldown = defaultShield.Cooldown - Type = Shielding defaultShield.Id } + Type = Shielding defaultShield.Item.Id } ] let sendInitialEmbed (client : DiscordClient) = @@ -52,7 +52,7 @@ let handleTrainerStep1 (ctx : IDiscordContext) = |> Async.AwaitTask let msg = "Beautopia© is a dangerous place... quick, put up a SHIELD 🛡 before another Degen hacks you, and takes your 💰$GBT.\n\n" + "To enable it, you need to run the `/shield` slash command.\n\n" - + $"Type the `/shield` command now, then select - `{defaultShield.Name}`\n" + + $"Type the `/shield` command now, then select - `{defaultShield.Item.Name}`\n" let builder = DiscordInteractionResponseBuilder() .WithContent(msg) @@ -66,7 +66,7 @@ let defend (ctx : IDiscordContext) = do! Messaging.defer ctx let m = ctx.GetDiscordMember() let name = if System.String.IsNullOrEmpty m.Nickname then m.DisplayName else m.Nickname - let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [ Inventory.shieldToItem defaultShield ] ; Name = name } true + let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [ Shield defaultShield ] ; Name = name } true do! ctx.FollowUp(embed) |> Async.AwaitTask } |> Async.StartAsTask :> Task @@ -88,11 +88,11 @@ let handleDefense (ctx : IDiscordContext) = let embed = Embeds.responseCreatedShield defaultShield do! ctx.FollowUp embed |> Async.AwaitTask do! Async.Sleep 4000 - do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{defaultHack.Name}**" + do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{defaultHack.Item.Name}**" do! Async.Sleep 5000 do! sendMessage' $"❌ HACKING FAILED!\n\n{playerName} defended hack from <@{Sensei.Id}>!" do! Async.Sleep 4000 - do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Name) + do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Item.Name) } |> Async.StartAsTask :> Task let handleTrainerStep3 (ctx : IDiscordContext) = @@ -103,7 +103,7 @@ let handleTrainerStep3 (ctx : IDiscordContext) = .WithContent ( "Now let’s **HACK** 💻... I want you to **HACK ME**!\n\n" + "To **hack**, you need to run the `/hack` slash command.\n" - + $"Type the `/hack` command now, then choose me - <@{Sensei.Id}> as your target, and select `{defaultHack.Name}`") + + $"Type the `/hack` command now, then choose me - <@{Sensei.Id}> as your target, and select `{defaultHack.Item.Name}`") do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask } |> Async.StartAsTask :> Task @@ -115,7 +115,7 @@ let hack (target : DiscordUser) (ctx : IDiscordContext) = let isRightTarget = target.Id = Sensei.Id match isRightTarget with | true -> - let player = { PlayerData.empty with Inventory = [ Inventory.hackToItem defaultHack ] } + let player = { PlayerData.empty with Inventory = [ Hack defaultHack ] } let bot = { PlayerData.empty with DiscordId = Sensei.Id ; Name = Sensei.Name } let embed = Embeds.pickHack "Trainer-4" player bot true @@ -151,7 +151,7 @@ let handleHack (ctx : IDiscordContext) = do! DbService.addAchievement player.DiscordId trainerAchievement |> Async.Ignore - sb.Append($"I'm going to gift you a hack,`{defaultHack.Name}` and a shield, `{defaultShield.Name}`") |> ignore + sb.Append($"I'm going to gift you a hack,`{defaultHack.Item.Name}` and a shield, `{defaultShield.Item.Name}`") |> ignore sb.Append(", you'll need em to survive\n\n") |> ignore sb.AppendLine("To finish your training and collect the loot, type the `/arsenal` command **NOW**") |> ignore do! Async.Sleep 1000 @@ -168,13 +168,13 @@ let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async { let hasStockWeapons = player.Inventory - |> List.choose (fun item -> if item.Id = defaultHack.Id || item.Id = defaultShield.Id then Some item else None) + |> List.choose (fun item -> if item.Id = defaultHack.Item.Id || item.Id = defaultShield.Item.Id then Some item else None) |> List.length > 0 let updatedPlayer = if not hasStockWeapons then { Player.removeExpiredActions player with Events = TrainerEvents @ player.Events - Inventory = Inventory.hackToItem defaultHack::Inventory.shieldToItem defaultShield::player.Inventory + Inventory = Hack defaultHack::Shield defaultShield::player.Inventory } else Player.removeExpiredActions player @@ -191,7 +191,7 @@ let handleArsenal (ctx : IDiscordContext) = let! completed = DbService.checkHasAchievement player.DiscordId trainerAchievement if not completed then do! Async.Sleep 3000 - let rewards = [ $"{defaultHack.Name} Hack" ; $"{defaultShield.Name} Shield" ] + let rewards = [ $"{defaultHack.Item.Name} Hack" ; $"{defaultShield.Item.Name} Shield" ] let embed = Embeds.getAchievementEmbed rewards "You completed the Training Dojo and collected loot." trainerAchievement do! ctx.FollowUp(embed) |> Async.AwaitTask do! Async.Sleep 2000 diff --git a/Bot/Items.json b/Bot/Items.json index c99fd23..ff95a07 100644 --- a/Bot/Items.json +++ b/Bot/Items.json @@ -1,177 +1,205 @@ [ { - "Id": 0, - "Name": "Virus", - "Price": 0, - "Type": { - "Case": "Hack", - "Fields": [ - 25, - 0, - 1 - ] - } + "Case": "Hack", + "Fields": [ + { + "Power": 25, + "Class": 0, + "Cooldown": 1, + "Item": { + "Id": 0, + "Name": "Virus", + "Price": 0 + } + } + ] }, { - "Id": 1, - "Name": "Remote Access", - "Price": 500, - "Type": { - "Case": "Hack", - "Fields": [ - 75, - 1, - 3 - ] - } + "Case": "Hack", + "Fields": [ + { + "Power": 75, + "Class": 1, + "Cooldown": 3, + "Item": { + "Id": 1, + "Name": "Remote Access", + "Price": 500 + } + } + ] }, { - "Id": 2, - "Name": "Worm", - "Price": 5000, - "Type": { - "Case": "Hack", - "Fields": [ - 150, - 2, - 5 - ] - } + "Case": "Hack", + "Fields": [ + { + "Power": 150, + "Class": 2, + "Cooldown": 5, + "Item": { + "Id": 2, + "Name": "Worm", + "Price": 5000 + } + } + ] }, { - "Id": 6, - "Name": "Firewall", - "Price": 0, - "Type": { - "Case": "Shield", - "Fields": [ - 0, - 120 - ] - } + "Case": "Shield", + "Fields": [ + { + "Class": 0, + "Cooldown": 120, + "Item": { + "Id": 6, + "Name": "Firewall", + "Price": 0 + } + } + ] }, { - "Id": 7, - "Name": "Encryption", - "Price": 500, - "Type": { - "Case": "Shield", - "Fields": [ - 1, - 240 - ] - } + "Case": "Shield", + "Fields": [ + { + "Class": 1, + "Cooldown": 240, + "Item": { + "Id": 7, + "Name": "Encryption", + "Price": 500 + } + } + ] }, { - "Id": 8, - "Name": "Cypher", - "Price": 5000, - "Type": { - "Case": "Shield", - "Fields": [ - 2, - 380 - ] - } + "Case": "Shield", + "Fields": [ + { + "Class": 2, + "Cooldown": 380, + "Item": { + "Id": 8, + "Name": "Cypher", + "Price": 5000 + } + } + ] }, { - "Id": 12, - "Name": "Protein Powder", - "Price": 50, - "Type": { - "Case": "Food", - "Fields": [ - 0, - 30 - ] - } + "Case": "Food", + "Fields": [ + { + "TargetStat" : 0, + "BoostAmount" : 30, + "Item": { + "Id": 12, + "Name": "Protein Powder", + "Price": 50 + } + } + ] }, { - "Id": 13, - "Name": "Toro Loco", - "Price": 50, - "Type": { - "Case": "Food", - "Fields": [ - 1, - 30 - ] - } + "Case": "Food", + "Fields": [ + { + "TargetStat" : 1, + "BoostAmount" : 30, + "Item": { + "Id": 13, + "Name": "Toro Loco", + "Price": 50 + } + } + ] }, { - "Id": 14, - "Name": "Cigarettes", - "Price": 50, - "Type": { - "Case": "Food", - "Fields": [ - 2, - 30 - ] - } + "Case": "Food", + "Fields": [ + { + "TargetStat" : 2, + "BoostAmount" : 30, + "Item": { + "Id": 14, + "Name": "Oldports Cigs", + "Price": 50 + } + } + ] }, { - "Id": 15, - "Name": "Moon Pie", - "Price": 50, - "Type": { - "Case": "Food", - "Fields": [ - 3, - 30 - ] - } + "Case": "Food", + "Fields": [ + { + "TargetStat" : 3, + "BoostAmount" : 30, + "Item": { + "Id": 15, + "Name": "Moon Pie", + "Price": 50 + } + } + ] }, { - "Id": 20, - "Name": "Kettle Bell", - "Price": 250, - "Type": { - "Case": "Accessory", - "Fields": [ - 0, - 25, - 0 - ] - } + "Case": "Accessory", + "Fields": [ + { + "TargetStat" : 0, + "FloorBoost" : 25, + "CeilBoost" : 0, + "Item": { + "Id": 20, + "Name": "Kettlebell", + "Price": 250 + } + } + ] }, { - "Id": 21, - "Name": "Headphones", - "Price": 250, - "Type": { - "Case": "Accessory", - "Fields": [ - 1, - 25, - 0 - ] - } + "Case": "Accessory", + "Fields": [ + { + "TargetStat" : 1, + "FloorBoost" : 25, + "CeilBoost" : 0, + "Item": { + "Id": 21, + "Name": "Headphones", + "Price": 250 + } + } + ] }, { - "Id": 22, - "Name": "Silk Shirt", - "Price": 250, - "Type": { - "Case": "Accessory", - "Fields": [ - 2, - 0, - 25 - ] - } + "Case": "Accessory", + "Fields": [ + { + "TargetStat" : 2, + "FloorBoost" : 0, + "CeilBoost" : 25, + "Item": { + "Id": 22, + "Name": "Rolox Watch", + "Price": 250 + } + } + ] }, { - "Id": 23, - "Name": "Buddha Keychain", - "Price": 250, - "Type": { - "Case": "Accessory", - "Fields": [ - 3, - 0, - 25 - ] - } + "Case": "Accessory", + "Fields": [ + { + "TargetStat" : 3, + "FloorBoost" : 0, + "CeilBoost" : 25, + "Item": { + "Id": 23, + "Name": "Buddha Keychain", + "Price": 250 + } + } + ] } ]