Changes and fixes to attributes

This commit is contained in:
Joseph Ferano 2022-02-17 16:12:25 +07:00
parent 881fd12aa9
commit e51223df88
8 changed files with 158 additions and 143 deletions

View File

@ -121,7 +121,7 @@ let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : Item a
.WithThumbnail(getShieldIcon (enum<ShieldId>(item.Id)))
|> ignore
embed
.AddField("Cost 💰", $"{item.Price} $GBT", true)
.AddField("Price 💰", $"{item.Price} $GBT", true)
.WithTitle($"{item.Name}")
|> ignore
let button =

View File

@ -68,10 +68,10 @@ module Player =
let getShields (player : PlayerData) = getItems ItemType.Shield player
let getHackEvents player =
player.Events
|> Array.filter (fun act -> match act.Type with PlayerEventType.Hacking -> true | _ -> false || act.ItemId < 12)
|> Array.filter (fun act -> match act.Type with PlayerEventType.Hacking -> true | _ -> false && act.ItemId < 12)
let getShieldEvents player =
player.Events
|> Array.filter (fun act -> match act.Type with PlayerEventType.Shielding -> true | _ -> false || act.ItemId < 12)
|> Array.filter (fun act -> match act.Type with PlayerEventType.Shielding -> 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
@ -106,28 +106,22 @@ module Arsenal =
let actionFormat (actions : PlayerEvent array) =
match actions with
| [||] -> "None"
| _ ->
let hacks , defenses =
actions
|> Array.filter (fun act -> act.ItemId < 12)
|> Array.partition (fun act -> match act.Type with PlayerEventType.Hacking -> true | _ -> false)
let hacks = hacks |> Array.take (min hacks.Length 10)
hacks
|> Array.append defenses
| _ -> actions
|> Array.map (fun act ->
let item = Armory.getItem act.ItemId
match act.Type with
| PlayerEventType.Hacking ->
let cooldown = Messaging.getTimeText false Game.SameTargetAttackCooldown act.Timestamp
$"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}")
let item = Armory.getItem act.ItemId
match act.Type with
| PlayerEventType.Hacking ->
let cooldown = Messaging.getTimeText false Game.SameTargetAttackCooldown act.Timestamp
$"Hacked {act.Adversary.Name} with {item.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"
let statusFormat p =
let hacks = Player.getHackEvents p
$"**Hacks:** {Player.getHacks p |> battleItemFormat}\n
**Shields:** {Player.getShields p |> battleItemFormat}\n
**Hack Attacks:**\n{Player.getHackEvents p |> actionFormat}\n
**Hack Attacks:**\n{ hacks |> Array.take (min hacks.Length 10) |> actionFormat}\n
**Active Shields:**\n{Player.getShieldEvents p |> actionFormat}"

View File

@ -264,7 +264,7 @@ type HackerGame() =
[<SlashCommand("hack", "Send a hack attack to another player")>]
member this.AttackCommand (ctx : InteractionContext, [<Option("target", "The player you want to hack")>] target : DiscordUser) =
enforceChannels (DiscordInteractionContext ctx) (Trainer.attack target) (attack target)
enforceChannels (DiscordInteractionContext ctx) (Trainer.hack target) (attack target)
[<SlashCommand("shield", "Create a passive shield that will protect you for a certain time")>]
member this.ShieldCommand (ctx : InteractionContext) =

View File

@ -3,48 +3,84 @@
"Id": 0,
"Name": "Virus",
"Type": 0,
"Cost": 50,
"Power": 50,
"Cooldown": 2
"Price": 0,
"Power": 10,
"Cooldown": 2,
"Attributes": {
"Sell": false,
"Buy": false,
"Consume": false,
"Drop": false
}
},
{
"Id": 1,
"Name": "RemoteAccess",
"Type": 0,
"Cost": 50,
"Price": 500,
"Power": 50,
"Cooldown": 2
"Cooldown": 2,
"Attributes": {
"Sell": true,
"Buy": true,
"Consume": false,
"Drop": true
}
},
{
"Id": 2,
"Name": "Worm",
"Type": 0,
"Cost": 50,
"Power": 50,
"Cooldown": 2
"Price": 5000,
"Power": 80,
"Cooldown": 2,
"Attributes": {
"Sell": true,
"Buy": true,
"Consume": false,
"Drop": true
}
},
{
"Id": 6,
"Name": "Firewall",
"Type": 1,
"Cost": 50,
"Power": 50,
"Cooldown": 600
"Price": 0,
"Power": 10,
"Cooldown": 600,
"Attributes": {
"Sell": false,
"Buy": false,
"Consume": false,
"Drop": false
}
},
{
"Id": 8,
"Name": "Cypher",
"Type": 1,
"Cost": 50,
"Price": 500,
"Power": 50,
"Cooldown": 600
"Cooldown": 600,
"Attributes": {
"Sell": true,
"Buy": true,
"Consume": false,
"Drop": true
}
},
{
"Id": 7,
"Name": "Encryption",
"Type": 1,
"Cost": 50,
"Power": 50,
"Cooldown": 600
"Price": 5000,
"Power": 80,
"Cooldown": 600,
"Attributes": {
"Sell": true,
"Buy": true,
"Consume": false,
"Drop": true
}
}
]

View File

@ -168,8 +168,6 @@ let handleSteal (ctx : IDiscordContext) =
let stole = { ItemId = -1 ; Type = PlayerEventType.Steal ; Result = PlayerEventResult.Positive ; 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 ->

View File

@ -1,16 +1,29 @@
module Degenz.Trainer
open System.Text
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
open Degenz.Types
open Degenz.Messaging
let trainerAchievement = "FINISHED_TRAINER"
let Sensei = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
let defaultHack = Armory.battleItems |> Array.find (fun i -> i.Id = int HackId.Virus)
let defaultShield = Armory.battleItems |> Array.find (fun i -> i.Id = int ShieldId.Encryption)
let defaultShield = Armory.battleItems |> Array.find (fun i -> i.Id = int ShieldId.Firewall)
let TrainerEvents = [|
{ PlayerEvent.Timestamp = System.DateTime.UtcNow
PlayerEvent.Adversary = Sensei
PlayerEvent.Type = PlayerEventType.Hacking
PlayerEvent.Result = PlayerEventResult.Positive
PlayerEvent.ItemId = defaultHack.Id }
{ PlayerEvent.Timestamp = System.DateTime.UtcNow
PlayerEvent.Adversary = DiscordPlayer.empty
PlayerEvent.Type = PlayerEventType.Shielding
PlayerEvent.Result = PlayerEventResult.Positive
PlayerEvent.ItemId = defaultShield.Id }
|]
let sendInitialEmbed (client : DiscordClient) =
async {
@ -31,42 +44,31 @@ let sendInitialEmbed (client : DiscordClient) =
let handleTrainerStep1 (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let shieldMessage , weaponName =
if Player.getShields player |> Array.isEmpty
then $"You do not have any Shields in your arsenal, take the `{defaultShield.Name}` shield, you can use it for now.\n\n" , defaultShield.Name
else
let name = Player.getShields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
$"Looks like you have `{name}` in your arsenal… 👀\n\n" , name
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
do! ctx.GetDiscordMember().GrantRoleAsync(role)
|> Async.AwaitTask
do! sendFollowUpMessage ctx
("Beautopia© is a dangerous place... quick, put up a SHIELD 🛡 before another Degen hacks you, and steals your 💰$GBT.\n\n"
+ shieldMessage
+ "To enable it, you need to run the `/shield` slash command.\n\n"
+ $"Type the `/shield` command now, then select - `{weaponName}`\n")
+ $"Type the `/shield` command now, then select - `{defaultShield.Name}`\n")
})
let defend (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let playerWithShields =
match Player.getShields player with
| [||] -> { player with Inventory = [| defaultShield |] }
| _ -> player
let embed = Embeds.pickDefense "Trainer-2" playerWithShields true
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Content"
do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
let embed = Embeds.pickDefense "Trainer-2" { PlayerData.empty with Inventory = [| defaultShield |] } true
do! ctx.FollowUp(embed) |> Async.AwaitTask
})
} |> Async.StartAsTask :> Task
let handleDefenseMsg shieldId hackId = {
let handleDefenseMsg hackId = {
ButtonId = "Trainer-3"
ButtonText = "Got it"
Message = "🎉 Congratulations you successfully defended my hack!\n\n"
+ $"The `{shieldId}` shield is strong against the `{hackId}` hack, so make sure to have multiple shields up to protect from multiple attacks..."
+ "Shields only protect you for a LIMITED TIME, so remember to keep them mounted at all times, or risk getting hacked!"
Message = $"🎉 Congratulations you successfully defended my {hackId} hack!\n\n"
+ "Shields only protect you for a LIMITED TIME, so remember to keep them mounted at all times, or risk getting hacked!"
}
let handleDefense (ctx : IDiscordContext) =
@ -78,41 +80,32 @@ let handleDefense (ctx : IDiscordContext) =
let embed = Embeds.responseCreatedShield shield
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 **VIRUS**"
do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{defaultHack.Name}**"
do! Async.Sleep 5000
do! sendMessage' $"❌ HACKING FAILED!\n\n{player.Name} defended hack from <@{GuildEnvironment.botIdHackerBattle}>!"
do! sendMessage' $"❌ HACKING FAILED!\n\n{player.Name} defended hack from <@{Sensei.Id}>!"
do! Async.Sleep 4000
do! sendFollowUpMessageWithButton ctx (handleDefenseMsg shieldId "VIRUS")
do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Name)
})
let handleTrainerStep3 (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let hackMessage , weaponName =
if Player.getHacks player |> Array.isEmpty
then $"You do not have any Hacks in your arsenal, take this `{defaultHack.Name}`, you can use it for now.\n\n" , defaultHack.Name
else
let name = Player.getHacks player |> Array.tryHead |> Option.defaultValue defaultHack |> fun w -> w.Name
$"Looks like you have `{name}` in your arsenal...\n\n" , name
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Content"
do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
do! sendFollowUpMessage ctx
("Now lets **HACK!** 💻\n\n"
+ "I want you to **HACK ME**...\n\n"
+ hackMessage
+ "To deploy it, you need to run the `/hack` slash command.\n"
+ $"Type the `/hack` command now, then choose me - <@{GuildEnvironment.botIdHackerBattle}> as your target, and select `{weaponName}`")
})
( "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}`")
} |> Async.StartAsTask :> Task
let attack (target : DiscordUser) (ctx : IDiscordContext) =
let hack (target : DiscordUser) (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let isRightTarget = target.Id = GuildEnvironment.botIdHackerBattle
let isRightTarget = target.Id = Sensei.Id
match isRightTarget with
| true ->
let playerWithAttacks =
match Player.getHacks player with
| [||] -> { player with Inventory = [| defaultHack |] }
| _ -> player
let bot = { player with DiscordId = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
let embed = Embeds.pickHack "Trainer-4" playerWithAttacks bot true
let bot = { PlayerData.empty with DiscordId = Sensei.Id ; Name = Sensei.Name }
let embed = Embeds.pickHack "Trainer-4" { player with Inventory = [| defaultHack |] } bot true
do! ctx.FollowUp(embed) |> Async.AwaitTask
| false ->
@ -124,12 +117,11 @@ let attack (target : DiscordUser) (ctx : IDiscordContext) =
do! ctx.FollowUp(builder) |> Async.AwaitTask
})
let handleAttack (ctx : IDiscordContext) =
let handleHack (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let sendMessage' = sendFollowUpMessage ctx
do! Async.Sleep 1000
let hack = Player.getHacks player |> Array.tryHead |> Option.defaultValue defaultHack
let embed = Embeds.responseSuccessfulHack false GuildEnvironment.botIdHackerBattle hack
let embed = Embeds.responseSuccessfulHack false Sensei.Id defaultHack
do! ctx.FollowUp(embed) |> Async.AwaitTask
do! Async.Sleep 5000
do! sendMessage'
@ -146,53 +138,14 @@ let handleAttack (ctx : IDiscordContext) =
if isFirstTrainer then
do! DbService.addAchievement player.DiscordId trainerAchievement
let hasHacks = Player.getHacks player |> Array.isEmpty |> not
let hasShields = Player.getShields player |> Array.isEmpty |> not
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.Price else 0<GBT>
let shieldMoney = if hasShields then defaultShield.Price else 0<GBT>
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.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
sb.Append($"I'm going to gift you a hack,`{defaultHack.Name}` and a shield, `{defaultShield.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
let updatedPlayer = {
player with
Bank = player.Bank + 100<GBT>
Events = [
{ PlayerEvent.Timestamp = System.DateTime.UtcNow
PlayerEvent.Adversary = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
PlayerEvent.Type = PlayerEventType.Shielding
PlayerEvent.Result = PlayerEventResult.Positive
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.Shielding
PlayerEvent.Result = PlayerEventResult.Positive
PlayerEvent.ItemId = defaultHack.Id }
] |> Seq.toArray
|> Array.append player.Events
Inventory = [
if not hasHacks then freeHack
if not hasShields then freeShield
] |> Seq.toArray
|> Array.append player.Inventory
}
do! DbService.updatePlayer updatedPlayer
do! sendFollowUpMessage ctx (sb.ToString())
else
do! sendFollowUpMessage ctx ($"Your training is now complete. If you want to buy more **HACKS & SHIELDS**, go to the <#{GuildEnvironment.channelArmory}> **NOW** and type the `/buy-hack` and `/buy-shield` commands! 😱")
do! sendFollowUpMessage ctx ($"Your training is now complete. If you want to buy more **HACKS & SHIELDS**, go to the <#{GuildEnvironment.channelArmory}> and type the `/buy-hack` and `/buy-shield` commands!")
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
do! ctx.GetDiscordMember().RevokeRoleAsync(role)
|> Async.AwaitTask
@ -200,12 +153,28 @@ let handleAttack (ctx : IDiscordContext) =
let handleArsenal (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun player -> async {
let hasStockWeapons = Player.getHacks player |> Array.exists (fun item -> item.Id = defaultHack.Id)
let updatedPlayer = Player.removeExpiredActions false player
let embed = Embeds.getArsenalEmbed updatedPlayer
do! ctx.FollowUp(embed) |> Async.AwaitTask
do! Async.Sleep 3000
let embed = Embeds.getAchievementEmbed "You completed the Training Dojo and collected loot." trainerAchievement
let newPlayer =
if not hasStockWeapons then {
updatedPlayer with
Events = if not (player.Events |> Array.exists (fun act -> act.Adversary = Sensei))
then TrainerEvents
else [||]
|> Array.append player.Events
Inventory = if not hasStockWeapons then [| defaultHack ; defaultShield |] else Array.empty
|> Array.append player.Inventory
}
else
updatedPlayer
if not hasStockWeapons then
do! DbService.updatePlayer newPlayer
let embed = Embeds.getArsenalEmbed newPlayer
do! ctx.FollowUp(embed) |> Async.AwaitTask
if not (player.Achievements |> Array.contains trainerAchievement) then
do! Async.Sleep 3000
let embed = Embeds.getAchievementEmbed "You completed the Training Dojo and collected loot." trainerAchievement
do! ctx.FollowUp(embed) |> Async.AwaitTask
do! Async.Sleep 2000
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
do! ctx.GetDiscordMember().RevokeRoleAsync(role) |> Async.AwaitTask
@ -220,7 +189,7 @@ let handleButtonEvent (ctx : IDiscordContext) =
| 1 -> do! handleTrainerStep1 ctx |> Async.AwaitTask
| 2 -> do! handleDefense ctx |> Async.AwaitTask
| 3 -> do! handleTrainerStep3 ctx |> Async.AwaitTask
| 4 -> do! handleAttack ctx |> Async.AwaitTask
| 4 -> do! handleHack ctx |> Async.AwaitTask
| _ -> do! sendFollowUpMessage ctx "No action found"
}

View File

@ -69,6 +69,6 @@ let updatePlayer (player : PlayerData) =
let addAchievement (id : uint64) (achievement : string) =
async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id)
let update = Builders<BsonDocument>.Update.Push("Achievements", achievement)
let update = Builders<BsonDocument>.Update.Push("Player.Achievements", achievement)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}

View File

@ -49,9 +49,17 @@ module Types =
type ItemType =
| Hack = 0
| Shield = 1
| Consumable = 1
| Food = 1
type ItemAttributes = {
Sell : bool
Buy : bool
Consume : bool
Drop : bool
}
with static member empty = { Sell = false ; Buy = false ; Consume = false ; Drop = false }
[<CLIMutable>]
type Item = {
Id : int
Name : string
@ -59,6 +67,7 @@ module Types =
Type : ItemType
Power : int
Cooldown : int<mins>
Attributes : ItemAttributes
}
type HackResult =
@ -109,6 +118,15 @@ module Types =
Bank : int<GBT>
}
with member this.basicPlayer = { Id = this.DiscordId ; Name = this.Name }
static member empty =
{ DiscordId = 0uL
Name = "None"
Inventory = [||]
Events = [||]
Traits = PlayerTraits.empty
Achievements = [||]
XP = 0
Bank = 0<GBT> }
module Armory =
let battleItems =