Gift GBT when completing training, don't let new players be hacked

This commit is contained in:
Joseph Ferano 2022-03-10 14:36:12 +07:00
parent e0fea6dd64
commit ed512bd394
5 changed files with 202 additions and 86 deletions

View File

@ -89,10 +89,19 @@ type ItemType =
| Food | Food
| Accessory | Accessory
type ItemAttributes = {
CanBuy : bool
CanSell : bool
CanConsume : bool
CanTrade : bool
CanDrop : bool
}
type Item = { type Item = {
Id : int Id : int
Name : string Name : string
Price : int<GBT> Price : int<GBT>
Attributes : ItemAttributes
} }
type HackItem = { type HackItem = {

View File

@ -41,12 +41,12 @@ let checkWeaponHasCooldown (weapon : Item) attacker =
|> function |> function
| Some event -> | Some event ->
let cooldown = getTimeText true (TimeSpan.FromMinutes(int event.Cooldown)) event.Timestamp let cooldown = getTimeText true (TimeSpan.FromMinutes(int event.Cooldown)) event.Timestamp
Error $"{weapon.Name} is still active, it will expire in {cooldown}." Error $"{weapon.Name} was recently used, wait another {cooldown} to use it again."
| None -> Ok attacker | None -> Ok attacker
let checkHasEmptyHacks attacker = let checkHasEmptyHacks (attacker : PlayerData) =
match Inventory.getHacks 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." | [] -> Error $"You currently do not have any Hacks to take 💰$GBT from others. Please go to the <#{GuildEnvironment.channelTraining}> and complete the training."
| _ -> Ok attacker | _ -> Ok attacker
let checkPlayerOwnsWeapon (item : Item) player = let checkPlayerOwnsWeapon (item : Item) player =
@ -69,16 +69,21 @@ let checkTargetHasFunds target player =
| true -> Error $"Looks like the poor bastard has no $GBT... pick a different victim." | true -> Error $"Looks like the poor bastard has no $GBT... pick a different victim."
| false -> Ok player | false -> Ok player
let checkTargetCompletedTraining target (player : PlayerData) = async {
let! targetCompletedTraining = DbService.checkHasAchievement target.DiscordId Trainer.TrainerAchievement
if targetCompletedTraining || not target.Inventory.IsEmpty
then return Ok player
else return Error $"Looks like they haven't completed training with Sensei yet, you can't hack noobs..."
}
let runHackerBattle defender (hack : HackItem) = let runHackerBattle defender (hack : HackItem) =
defender defender
|> Player.removeExpiredActions |> Player.removeExpiredActions
|> fun p -> p.Events |> fun p -> p.Events
|> List.choose (fun event -> |> List.exists (fun event ->
match event.Type with match event.Type with
| Shielding id -> defender.Inventory |> Inventory.getShields |> List.tryFind (fun item -> item.Item.Id = id) | Shielding id -> hack.Class = (Inventory.findShieldById id Armory.weapons).Class
| _ -> None) | _ -> false)
|> List.map (fun shield -> if hack.Class = shield.Class then Weak else Strong)
|> List.contains Weak
let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerData) (hack : HackItem) prize = let updateCombatants successfulHack (attacker : PlayerData) (defender : PlayerData) (hack : HackItem) prize =
let updatePlayer amount attack p = let updatePlayer amount attack p =
@ -134,9 +139,11 @@ let failedHack (ctx : IDiscordContext) attacker defender (hack : HackItem) =
let hack (target : DiscordUser) (ctx : IDiscordContext) = let hack (target : DiscordUser) (ctx : IDiscordContext) =
executePlayerActionWithTarget target ctx (fun attacker defender -> async { executePlayerActionWithTarget target ctx (fun attacker defender -> async {
let! result = checkTargetCompletedTraining defender attacker
do! attacker do! attacker
|> Player.removeExpiredActions |> Player.removeExpiredActions
|> checkAlreadyHackedTarget defender |> checkAlreadyHackedTarget defender
>>= fun p -> match result with Ok _ -> Ok p | Error e -> Error e
>>= checkTargetHasFunds defender >>= checkTargetHasFunds defender
>>= checkHasEmptyHacks >>= checkHasEmptyHacks
>>= checkPlayerIsAttackingThemselves defender >>= checkPlayerIsAttackingThemselves defender
@ -180,7 +187,7 @@ let defend (ctx : IDiscordContext) =
let embed = Embeds.pickDefense "Defend" p false let embed = Embeds.pickDefense "Defend" p false
do! ctx.FollowUp embed |> Async.AwaitTask do! ctx.FollowUp embed |> Async.AwaitTask
else else
let msg = $"You currently do not have any Shields to protect yourself from hacks. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one." let msg = $"You currently do not have any Shields to protect yourself from hacks. Please go to the <#{GuildEnvironment.channelTraining}> and purchase one."
do! Messaging.sendFollowUpMessage ctx msg do! Messaging.sendFollowUpMessage ctx msg
}) })

View File

@ -7,10 +7,12 @@ open DSharpPlus.Entities
open Degenz.Types open Degenz.Types
open Degenz.Messaging open Degenz.Messaging
let trainerAchievement = "FINISHED_TRAINER" let TrainerAchievement = "FINISHED_TRAINER"
let Sensei = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" } let Sensei = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
let defaultHack = Armory.weapons |> Inventory.findHackById (int ItemId.Virus) let defaultHack = Armory.weapons |> Inventory.findHackById (int ItemId.Virus)
let defaultShield = Armory.weapons |> Inventory.findShieldById (int ItemId.Firewall) let defaultShield = Armory.weapons |> Inventory.findShieldById (int ItemId.Firewall)
let CurrencyGift = 250<GBT>
let BeginnerProtectionHours = 24
let HackEvent () = { let HackEvent () = {
Timestamp = System.DateTime.UtcNow Timestamp = System.DateTime.UtcNow
@ -22,11 +24,17 @@ let HackEvent () = {
HackId = defaultHack.Item.Id HackId = defaultHack.Item.Id
} }
} }
let ShieldEvent () = { let ShieldEvents () = [
Timestamp = System.DateTime.UtcNow { Timestamp = System.DateTime.UtcNow
Cooldown = defaultShield.Cooldown Cooldown = BeginnerProtectionHours * 60<mins>
Type = Shielding defaultShield.Item.Id Type = Shielding (int ItemId.Firewall) }
} { Timestamp = System.DateTime.UtcNow
Cooldown = BeginnerProtectionHours * 60<mins>
Type = Shielding (int ItemId.Encryption) }
{ Timestamp = System.DateTime.UtcNow
Cooldown = BeginnerProtectionHours * 60<mins>
Type = Shielding (int ItemId.Cypher) }
]
let sendInitialEmbed (client : DiscordClient) = let sendInitialEmbed (client : DiscordClient) =
async { async {
@ -149,76 +157,69 @@ let handleHack (ctx : IDiscordContext) =
let sb = StringBuilder("Here, ") let sb = StringBuilder("Here, ")
let! completed = DbService.checkHasAchievement player.DiscordId trainerAchievement let! completed = DbService.checkHasAchievement player.DiscordId TrainerAchievement
if not completed then if not completed then
do! DbService.addAchievement player.DiscordId trainerAchievement sb.Append($"I'm going to gift you a hack,`{defaultHack.Item.Name}`, a shield, `{defaultShield.Item.Name}`, and `{CurrencyGift} 💰$GBT`") |> ignore
|> Async.Ignore sb.Append(", you'll need this to survive\n\n") |> ignore
sb.Append($"I'm also going to give you some **TEMPORARY SHIELDS** to protect you for the next **{BeginnerProtectionHours} hours**\n\n") |> ignore
sb.Append($"I'm going to gift you a hack,`{defaultHack.Item.Name}` and a shield, `{defaultShield.Item.Name}`") |> ignore sb.AppendLine("To finish your training and collect your prizes, type the `/arsenal` command **NOW**") |> 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 do! Async.Sleep 1000
do! sendFollowUpMessage ctx (sb.ToString()) do! sendFollowUpMessage ctx (sb.ToString())
else else
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!") 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
}) })
let handleArsenal (ctx : IDiscordContext) = let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async {
PlayerInteractions.executePlayerAction ctx (fun player -> async { let! completed = DbService.checkHasAchievement player.DiscordId TrainerAchievement
let hack = if not completed then
if player.Inventory |> List.exists (fun i -> i.Id = defaultHack.Item.Id) do! DbService.addAchievement player.DiscordId TrainerAchievement |> Async.Ignore
then [] let addIfDoesntExist (weapon : ItemDetails) (inventory : Inventory) =
else [ Hack defaultHack ] if inventory |> List.exists (fun i -> i.Id = weapon.Id)
let shield = then inventory
if player.Inventory |> List.exists (fun i -> i.Id = defaultShield.Item.Id) else weapon::inventory
then [] let updatedPlayer = {
else [ Shield defaultShield ] player with
let shieldEvent = Events =
let hasShield =
player player
|> Player.removeExpiredActions |> Player.removeExpiredActions
|> fun p -> p.Events |> fun p -> p.Events
|> List.exists (fun e -> match e.Type with Shielding shieldId -> shieldId = defaultShield.Item.Id | _ -> false) |> List.filter (fun e -> match e.Type with Shielding _ -> false | _ -> true)
if hasShield |> List.append (ShieldEvents())
then [] |> List.consTo (HackEvent())
else [ ShieldEvent() ] Inventory =
let updatedPlayer = { player.Inventory
Player.removeExpiredActions player with |> addIfDoesntExist (Hack defaultHack)
Events = shieldEvent @ player.Events |> addIfDoesntExist (Shield defaultShield)
Inventory = hack @ shield @ player.Inventory Bank = player.Bank + CurrencyGift
} }
if not (List.isEmpty hack) || not (List.isEmpty shield) then
do! DbService.updatePlayer updatedPlayer |> Async.Ignore do! DbService.updatePlayer updatedPlayer |> Async.Ignore
if not (List.isEmpty shieldEvent) then
try try
do! DbService.addPlayerEvent player.DiscordId (List.head shieldEvent) |> Async.Ignore do! ShieldEvents ()
|> List.map (DbService.addPlayerEvent player.DiscordId)
|> Async.Parallel
|> Async.Ignore
with ex -> with ex ->
printfn "%s" ex.Message printfn "%s" ex.Message
() ()
let playerForEmbed = { let embed = Embeds.getArsenalEmbed updatedPlayer
player with
Events = [ HackEvent() ; ShieldEvent() ]
Inventory = hack @ shield @ player.Inventory
}
let embed = Embeds.getArsenalEmbed playerForEmbed
do! ctx.FollowUp(embed) |> Async.AwaitTask do! ctx.FollowUp(embed) |> Async.AwaitTask
let! completed = DbService.checkHasAchievement player.DiscordId trainerAchievement
if not completed then
do! Async.Sleep 3000
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 do! Async.Sleep 2000
let rewards = [ $"{defaultHack.Item.Name} Hack" ; $"{defaultShield.Item.Name} Shield" ; $"{CurrencyGift} 💰$GBT"]
let embed = Embeds.getAchievementEmbed rewards "You completed the Training Dojo and collected some prizes." TrainerAchievement
do! ctx.FollowUp(embed) |> Async.AwaitTask
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee) let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
do! ctx.GetDiscordMember().RevokeRoleAsync(role) |> Async.AwaitTask do! ctx.GetDiscordMember().RevokeRoleAsync(role)
|> Async.AwaitTask
do! Async.Sleep 2000
do! sendFollowUpMessage ctx $"Now get out of there and go hack other Degenz in the <#{GuildEnvironment.channelBattle}> channel!" do! sendFollowUpMessage ctx $"Now get out of there and go hack other Degenz in the <#{GuildEnvironment.channelBattle}> channel!"
else
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 handleButtonEvent (ctx : IDiscordContext) = let handleButtonEvent (ctx : IDiscordContext) =

View File

@ -9,7 +9,14 @@
"Item": { "Item": {
"Id": 0, "Id": 0,
"Name": "Virus", "Name": "Virus",
"Price": 0 "Price": 0,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -20,11 +27,18 @@
{ {
"Power": 50, "Power": 50,
"Class": 1, "Class": 1,
"Cooldown": 3, "Cooldown": 5,
"Item": { "Item": {
"Id": 1, "Id": 1,
"Name": "Remote Access", "Name": "Remote Access",
"Price": 500 "Price": 500,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -35,11 +49,18 @@
{ {
"Power": 100, "Power": 100,
"Class": 2, "Class": 2,
"Cooldown": 5, "Cooldown": 10,
"Item": { "Item": {
"Id": 2, "Id": 2,
"Name": "Worm", "Name": "Worm",
"Price": 5000 "Price": 5000,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -53,7 +74,14 @@
"Item": { "Item": {
"Id": 6, "Id": 6,
"Name": "Firewall", "Name": "Firewall",
"Price": 0 "Price": 0,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -67,7 +95,14 @@
"Item": { "Item": {
"Id": 7, "Id": 7,
"Name": "Encryption", "Name": "Encryption",
"Price": 500 "Price": 500,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -81,7 +116,14 @@
"Item": { "Item": {
"Id": 8, "Id": 8,
"Name": "Cypher", "Name": "Cypher",
"Price": 5000 "Price": 5000,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -95,7 +137,14 @@
"Item": { "Item": {
"Id": 12, "Id": 12,
"Name": "Protein Powder", "Name": "Protein Powder",
"Price": 50 "Price": 50,
"Attributes": {
"CanBuy" : true,
"CanSell" : false,
"CanConsume" : true,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -109,7 +158,14 @@
"Item": { "Item": {
"Id": 13, "Id": 13,
"Name": "Toro Loco", "Name": "Toro Loco",
"Price": 50 "Price": 50,
"Attributes": {
"CanBuy" : true,
"CanSell" : false,
"CanConsume" : true,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -123,7 +179,14 @@
"Item": { "Item": {
"Id": 14, "Id": 14,
"Name": "Oldports Cigs", "Name": "Oldports Cigs",
"Price": 50 "Price": 50,
"Attributes": {
"CanBuy" : true,
"CanSell" : false,
"CanConsume" : true,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -137,7 +200,14 @@
"Item": { "Item": {
"Id": 15, "Id": 15,
"Name": "Moon Pie", "Name": "Moon Pie",
"Price": 50 "Price": 50,
"Attributes": {
"CanBuy" : true,
"CanSell" : false,
"CanConsume" : true,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -152,7 +222,14 @@
"Item": { "Item": {
"Id": 20, "Id": 20,
"Name": "Kettlebell", "Name": "Kettlebell",
"Price": 250 "Price": 250,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -167,7 +244,14 @@
"Item": { "Item": {
"Id": 21, "Id": 21,
"Name": "Headphones", "Name": "Headphones",
"Price": 250 "Price": 250,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -182,7 +266,14 @@
"Item": { "Item": {
"Id": 22, "Id": 22,
"Name": "Rolox Watch", "Name": "Rolox Watch",
"Price": 250 "Price": 250,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
@ -197,9 +288,17 @@
"Item": { "Item": {
"Id": 23, "Id": 23,
"Name": "Buddha Keychain", "Name": "Buddha Keychain",
"Price": 250 "Price": 250,
"Attributes": {
"CanBuy" : true,
"CanSell" : true,
"CanConsume" : false,
"CanTrade" : false,
"CanDrop" : false
}
} }
} }
] ]
} }
] ]

View File

@ -5,8 +5,8 @@ module ResultHelpers =
let (>>=) x f = Result.bind f x let (>>=) x f = Result.bind f x
let (<!>) x f = Result.map f x let (<!>) x f = Result.map f x
[<Microsoft.FSharp.Core.AutoOpen>]
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
[<Microsoft.FSharp.Core.AutoOpen>]
module List = module List =
let cons xs x = x :: xs let cons xs x = x :: xs
let consTo x xs = x :: xs let consTo x xs = x :: xs