267 lines
12 KiB
Forth
267 lines
12 KiB
Forth
module Degenz.Trainer
|
||
|
||
open System.Text
|
||
open System.Threading.Tasks
|
||
open DSharpPlus
|
||
open DSharpPlus.Entities
|
||
open Degenz.Types
|
||
open Degenz.Messaging
|
||
|
||
let TrainerAchievement = "FINISHED_TRAINER"
|
||
let Sensei = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
|
||
let defaultHack = Armory.weapons |> Inventory.findHackById (int ItemId.Virus)
|
||
let defaultShield = Armory.weapons |> Inventory.findShieldById (int ItemId.Firewall)
|
||
let CurrencyGift = 250<GBT>
|
||
let BeginnerProtectionHours = 24
|
||
|
||
let HackEvent () = {
|
||
Timestamp = System.DateTime.UtcNow
|
||
Cooldown = 1<mins>
|
||
Type = Hacking {
|
||
Adversary = Sensei
|
||
Success = true
|
||
IsInstigator = true
|
||
HackId = defaultHack.Item.Id
|
||
}
|
||
}
|
||
let ShieldEvents () = [
|
||
{ Timestamp = System.DateTime.UtcNow
|
||
Cooldown = BeginnerProtectionHours * 60<mins>
|
||
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 (ctx : IDiscordContext) =
|
||
async {
|
||
try
|
||
let channel = ctx.GetGuild().GetChannel(GuildEnvironment.channelTraining)
|
||
let builder = DiscordMessageBuilder()
|
||
let embed = DiscordEmbedBuilder()
|
||
embed.ImageUrl <- "https://s10.gifyu.com/images/MasterTraining_Degenz.gif"
|
||
builder.AddEmbed embed |> ignore
|
||
builder.Content <- "Welcome Degen… To the Hacker Training Program.\n"
|
||
+ "Here you will learn how to defend yourself, and hack other Degenz to take their 💰$GBT.\n"
|
||
+ "Are you ready?"
|
||
let button = DiscordButtonComponent(ButtonStyle.Success, $"Trainer-1", $"LFG") :> DiscordComponent
|
||
builder.AddComponents [| button |] |> ignore
|
||
do! GuildEnvironment.botClientHacker.Value.SendMessageAsync(channel, builder)
|
||
|> Async.AwaitTask
|
||
|> Async.Ignore
|
||
with e ->
|
||
printfn $"Error trying to get channel {GuildEnvironment.channelTraining}\n\n{e.Message}"
|
||
} |> Async.RunSynchronously
|
||
|
||
let handleTrainerStep1 (ctx : IDiscordContext) =
|
||
async {
|
||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
|
||
do! ctx.GetDiscordMember().GrantRoleAsync(role)
|
||
|> 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.Item.Name}`\n"
|
||
let builder =
|
||
DiscordInteractionResponseBuilder()
|
||
.WithContent(msg)
|
||
.AsEphemeral(true)
|
||
|
||
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
||
do! Analytics.trainingDojoStep "LFG" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
} |> Async.StartAsTask :> Task
|
||
|
||
let defend (ctx : IDiscordContext) =
|
||
async {
|
||
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 = [ Shield defaultShield ] ; Name = name } true
|
||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||
do! Analytics.trainingDojoStep "DefendCommand" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
} |> Async.StartAsTask :> Task
|
||
|
||
let handleDefenseMsg hackId = {
|
||
ButtonId = "Trainer-3"
|
||
ButtonText = "Next"
|
||
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) =
|
||
async {
|
||
do! Messaging.defer ctx
|
||
|
||
let sendMessage' = sendFollowUpMessage ctx
|
||
let tokens = ctx.GetInteractionId().Split("-")
|
||
let shieldId = enum<ItemId>(int tokens.[2])
|
||
let playerName = tokens.[4]
|
||
let embed = Embeds.responseCreatedShield defaultShield
|
||
do! ctx.FollowUp embed |> Async.AwaitTask
|
||
do! Async.Sleep 1000
|
||
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 3000
|
||
do! sendMessage' $"❌ HACKING FAILED!\n\n{playerName} defended hack from <@{Sensei.Id}>!"
|
||
do! Async.Sleep 1500
|
||
do! sendFollowUpMessageWithButton ctx (handleDefenseMsg defaultHack.Item.Name)
|
||
do! Analytics.trainingDojoStep "ShieldActivated" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
} |> Async.StartAsTask :> Task
|
||
|
||
let handleTrainerStep3 (ctx : IDiscordContext) =
|
||
async {
|
||
let builder =
|
||
DiscordInteractionResponseBuilder()
|
||
.AsEphemeral(true)
|
||
.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.Item.Name}`")
|
||
|
||
do! ctx.Respond(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
|
||
do! Analytics.trainingDojoStep "LetsHack" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
} |> Async.StartAsTask :> Task
|
||
|
||
let hack (target : DiscordUser) (ctx : IDiscordContext) =
|
||
async {
|
||
do! Messaging.defer ctx
|
||
|
||
let isRightTarget = target.Id = Sensei.Id
|
||
match isRightTarget with
|
||
| true ->
|
||
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
|
||
|
||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||
| false ->
|
||
let builder =
|
||
DiscordFollowupMessageBuilder()
|
||
.WithContent("You picked the wrong target, you dufus. Try again, this time pick me!")
|
||
.AsEphemeral(true)
|
||
|
||
do! ctx.FollowUp(builder) |> Async.AwaitTask
|
||
do! Analytics.trainingDojoStep "HackCommand" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
} |> Async.StartAsTask :> Task
|
||
|
||
let handleHack (ctx : IDiscordContext) =
|
||
PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||
let sendMessage' = sendFollowUpMessage ctx
|
||
do! Async.Sleep 1000
|
||
let embed = Embeds.responseSuccessfulHack false Sensei.Id defaultHack.Power defaultHack
|
||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||
do! Async.Sleep 1000
|
||
do! sendMessage'
|
||
("🎉 **Congratulations**\n\n"
|
||
+ "You successfully **HACKED** me, and are now an **Elite Haxor!**\n\n"
|
||
+ "When you **HACK** other Degenz, you **TAKE** their 💰$GBT.\n"
|
||
+ "But remember, hacks take time to recover, so use them wisely.")
|
||
|
||
do! Analytics.trainingDojoStep "HackActivated" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
do! Async.Sleep 1000
|
||
|
||
let message =
|
||
$"""
|
||
**🎁 FREE GIFTS:**
|
||
Here, I'm going to gift you:
|
||
|
||
1. A hack - `{defaultHack.Item.Name}`,
|
||
2. A shield - `{defaultShield.Item.Name}`,
|
||
3. `{CurrencyGift} 💰 $GBT`,
|
||
|
||
I'm also going to give you some **TEMPORARY SHIELDS** until you buy your own.
|
||
They will **ONLY** protect you from hacks for **{BeginnerProtectionHours} hrs**...
|
||
|
||
To finish your training and collect all your **GIFTS**,
|
||
type the `/arsenal` command NOW"""
|
||
|
||
let! completed = DbService.checkHasAchievement player.DiscordId TrainerAchievement
|
||
if not completed then
|
||
do! sendFollowUpMessage ctx message
|
||
else
|
||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
|
||
do! ctx.GetDiscordMember().RevokeRoleAsync(role) |> Async.AwaitTask
|
||
|
||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleHacker)
|
||
do! ctx.GetDiscordMember().GrantRoleAsync(role) |> Async.AwaitTask
|
||
|
||
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! Analytics.trainingDojoStep "Completed" (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
})
|
||
|
||
let handleArsenal (ctx : IDiscordContext) = PlayerInteractions.executePlayerAction ctx (fun player -> async {
|
||
let! completed = DbService.checkHasAchievement player.DiscordId TrainerAchievement
|
||
if not completed then
|
||
do! DbService.addAchievement player.DiscordId TrainerAchievement |> Async.Ignore
|
||
let addIfDoesntExist (weapon : ItemDetails) (inventory : Inventory) =
|
||
if inventory |> List.exists (fun i -> i.Id = weapon.Id)
|
||
then inventory
|
||
else weapon::inventory
|
||
let updatedPlayer = {
|
||
player with
|
||
Events =
|
||
player
|
||
|> Player.removeExpiredActions
|
||
|> fun p -> p.Events
|
||
|> List.filter (fun e -> match e.Type with Shielding _ -> false | _ -> true)
|
||
|> List.append (ShieldEvents())
|
||
|> List.consTo (HackEvent())
|
||
Inventory =
|
||
player.Inventory
|
||
|> addIfDoesntExist (Hack defaultHack)
|
||
|> addIfDoesntExist (Shield defaultShield)
|
||
Bank = player.Bank + CurrencyGift
|
||
}
|
||
do! DbService.updatePlayer updatedPlayer |> Async.Ignore
|
||
try
|
||
do! ShieldEvents ()
|
||
|> List.map (DbService.addPlayerEvent player.DiscordId)
|
||
|> Async.Parallel
|
||
|> Async.Ignore
|
||
with ex ->
|
||
printfn "%s" ex.Message
|
||
()
|
||
|
||
let embed = Embeds.getArsenalEmbed updatedPlayer
|
||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||
|
||
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 gifts." TrainerAchievement
|
||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||
|
||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
|
||
do! ctx.GetDiscordMember().RevokeRoleAsync(role) |> Async.AwaitTask
|
||
|
||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleHacker)
|
||
do! ctx.GetDiscordMember().GrantRoleAsync(role) |> Async.AwaitTask
|
||
|
||
do! InviteTracker.acceptInvite ctx player |> Async.AwaitTask
|
||
|
||
do! Async.Sleep 1000
|
||
|
||
do! sendFollowUpMessage ctx $"Now get out of there and go hack other Degenz in the <#{GuildEnvironment.channelBattle}> channel!"
|
||
do! Analytics.trainingDojoCompleted false (ctx.GetDiscordMember().Id) (ctx.GetDiscordMember().Username)
|
||
else
|
||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleTrainee)
|
||
do! ctx.GetDiscordMember().RevokeRoleAsync(role) |> Async.AwaitTask
|
||
|
||
let role = ctx.GetGuild().GetRole(GuildEnvironment.roleHacker)
|
||
do! ctx.GetDiscordMember().GrantRoleAsync(role) |> Async.AwaitTask
|
||
|
||
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) =
|
||
async {
|
||
let tokens = ctx.GetInteractionId().Split("-")
|
||
match int tokens.[1] with
|
||
| 1 -> do! handleTrainerStep1 ctx |> Async.AwaitTask
|
||
| 2 -> do! handleDefense ctx |> Async.AwaitTask
|
||
| 3 -> do! handleTrainerStep3 ctx |> Async.AwaitTask
|
||
| 4 -> do! handleHack ctx |> Async.AwaitTask
|
||
| _ -> do! sendFollowUpMessage ctx "No action found"
|
||
}
|
||
|