Another pass on item modeling

This commit is contained in:
Joseph Ferano 2022-03-02 23:10:01 +07:00
parent 8a57d3305e
commit f01fc28a0c
7 changed files with 394 additions and 353 deletions

View File

@ -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<DiscordComponent>
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)

View File

@ -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<Item array>(file)
JsonConvert.DeserializeObject<ItemDetails array>(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<GBT> }
@ -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
$"**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}"

View File

@ -82,40 +82,6 @@ type PlayerEvent =
Cooldown : int<mins>
Timestamp : DateTime }
type HackItem = {
Id : int
Name : string
Price : int<GBT>
Power : int
Class : int
Cooldown : int<mins>
}
type ShieldItem = {
Id : int
Name : string
Price : int<GBT>
Class : int
Cooldown : int<mins>
}
type FoodItem = {
Id : int
Name : string
Price : int<GBT>
TargetStat : StatId
BoostAmount : int
}
type AccessoryItem = {
Id : int
Name : string
Price : int<GBT>
TargetStat : StatId
FloorBoost : int
CeilBoost : int
}
[<RequireQualifiedAccess>]
type ItemType =
| Hack
@ -123,20 +89,76 @@ type ItemType =
| Food
| Accessory
type ItemDetails =
| Hack of power : int * hackClass : int * cooldown : int<mins>
| Shield of shieldClass : int * cooldown : int<mins>
| Food of targetStat : StatId * boostAmount : int
| Accessory of targetStat : StatId * floorBoost : int * ceilBoost : int
and Item = {
type Item = {
Id : int
Name : string
Price : int<GBT>
// Type : ItemType
Details : ItemDetails
}
and Inventory = Item list
and PlayerData = {
type HackItem = {
Power : int
Class : int
Cooldown : int<mins>
Item : Item
}
type ShieldItem = {
Class : int
Cooldown : int<mins>
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

View File

@ -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<GBT> }
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
}
[<SlashCommand("invite", "Invite user to this discord and earn rewards")>]
member this.CreateInvite (ctx : InteractionContext) =
invite (DiscordInteractionContext ctx)
[<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
member this.Arsenal (ctx : InteractionContext) =
enforceChannels (DiscordInteractionContext ctx) (Trainer.handleArsenal) arsenal

View File

@ -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<GBT> 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))
[<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GoodBoyTokenz")>]
member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy Inventory.filterByShields)
member this.BuyShield (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Shield))
[<SlashCommand("buy-food", "Purchase a food item to help boost your stats")>]
member this.BuyFood (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy Inventory.filterByFood)
member this.BuyFood (ctx : InteractionContext) = enforceChannel (DiscordInteractionContext(ctx)) (buy (Inventory.getItemsByType ItemType.Food))
[<SlashCommand("sell-hack", "Sell a hack for GoodBoyTokenz")>]
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))
[<SlashCommand("sell-shield", "Sell a shield for GoodBoyTokenz")>]
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))
[<SlashCommand("consume", "Consume a food item")>]
member this.Consume (ctx : InteractionContext) =
enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" Inventory.filterByShields)
enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType.Food))
[<SlashCommand("inventory", "Check your inventory")>]
member this.Inventory (ctx : InteractionContext) =
enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" Inventory.filterByShields)
// [<SlashCommand("inventory", "Check your inventory")>]
// member this.Inventory (ctx : InteractionContext) =
// enforceChannel (DiscordInteractionContext(ctx)) (sell "Shields" (Inventory.getItemsByType ItemType))

View File

@ -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 lets **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

View File

@ -1,177 +1,205 @@
[
{
"Case": "Hack",
"Fields": [
{
"Power": 25,
"Class": 0,
"Cooldown": 1,
"Item": {
"Id": 0,
"Name": "Virus",
"Price": 0,
"Type": {
"Case": "Hack",
"Fields": [
25,
0,
1
]
"Price": 0
}
}
]
},
{
"Case": "Hack",
"Fields": [
{
"Power": 75,
"Class": 1,
"Cooldown": 3,
"Item": {
"Id": 1,
"Name": "Remote Access",
"Price": 500,
"Type": {
"Case": "Hack",
"Fields": [
75,
1,
3
]
"Price": 500
}
}
]
},
{
"Case": "Hack",
"Fields": [
{
"Power": 150,
"Class": 2,
"Cooldown": 5,
"Item": {
"Id": 2,
"Name": "Worm",
"Price": 5000,
"Type": {
"Case": "Hack",
"Fields": [
150,
2,
5
]
"Price": 5000
}
}
]
},
{
"Case": "Shield",
"Fields": [
{
"Class": 0,
"Cooldown": 120,
"Item": {
"Id": 6,
"Name": "Firewall",
"Price": 0,
"Type": {
"Case": "Shield",
"Fields": [
0,
120
]
"Price": 0
}
}
]
},
{
"Case": "Shield",
"Fields": [
{
"Class": 1,
"Cooldown": 240,
"Item": {
"Id": 7,
"Name": "Encryption",
"Price": 500,
"Type": {
"Case": "Shield",
"Fields": [
1,
240
]
"Price": 500
}
}
]
},
{
"Case": "Shield",
"Fields": [
{
"Class": 2,
"Cooldown": 380,
"Item": {
"Id": 8,
"Name": "Cypher",
"Price": 5000,
"Type": {
"Case": "Shield",
"Fields": [
2,
380
]
"Price": 5000
}
}
]
},
{
"Case": "Food",
"Fields": [
{
"TargetStat" : 0,
"BoostAmount" : 30,
"Item": {
"Id": 12,
"Name": "Protein Powder",
"Price": 50,
"Type": {
"Case": "Food",
"Fields": [
0,
30
]
"Price": 50
}
}
]
},
{
"Case": "Food",
"Fields": [
{
"TargetStat" : 1,
"BoostAmount" : 30,
"Item": {
"Id": 13,
"Name": "Toro Loco",
"Price": 50,
"Type": {
"Case": "Food",
"Fields": [
1,
30
]
"Price": 50
}
}
]
},
{
"Case": "Food",
"Fields": [
{
"TargetStat" : 2,
"BoostAmount" : 30,
"Item": {
"Id": 14,
"Name": "Cigarettes",
"Price": 50,
"Type": {
"Case": "Food",
"Fields": [
2,
30
]
"Name": "Oldports Cigs",
"Price": 50
}
}
]
},
{
"Case": "Food",
"Fields": [
{
"TargetStat" : 3,
"BoostAmount" : 30,
"Item": {
"Id": 15,
"Name": "Moon Pie",
"Price": 50,
"Type": {
"Case": "Food",
"Fields": [
3,
30
]
"Price": 50
}
}
]
},
{
"Id": 20,
"Name": "Kettle Bell",
"Price": 250,
"Type": {
"Case": "Accessory",
"Fields": [
0,
25,
0
]
{
"TargetStat" : 0,
"FloorBoost" : 25,
"CeilBoost" : 0,
"Item": {
"Id": 20,
"Name": "Kettlebell",
"Price": 250
}
}
]
},
{
"Case": "Accessory",
"Fields": [
{
"TargetStat" : 1,
"FloorBoost" : 25,
"CeilBoost" : 0,
"Item": {
"Id": 21,
"Name": "Headphones",
"Price": 250,
"Type": {
"Case": "Accessory",
"Fields": [
1,
25,
0
]
"Price": 250
}
}
]
},
{
"Case": "Accessory",
"Fields": [
{
"TargetStat" : 2,
"FloorBoost" : 0,
"CeilBoost" : 25,
"Item": {
"Id": 22,
"Name": "Silk Shirt",
"Price": 250,
"Type": {
"Case": "Accessory",
"Fields": [
2,
0,
25
]
"Name": "Rolox Watch",
"Price": 250
}
}
]
},
{
"Case": "Accessory",
"Fields": [
{
"TargetStat" : 3,
"FloorBoost" : 0,
"CeilBoost" : 25,
"Item": {
"Id": 23,
"Name": "Buddha Keychain",
"Price": 250,
"Type": {
"Case": "Accessory",
"Fields": [
3,
0,
25
]
"Price": 250
}
}
]
}
]