New trainer bot flow that gifts weapons or money, plus achievement

This commit is contained in:
Joseph Ferano 2022-02-11 15:51:45 +07:00
parent 213f9be2ee
commit 785660558d
10 changed files with 272 additions and 138 deletions

View File

@ -66,30 +66,38 @@ let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEven
if guild <> 922419263275425832uL then
Trainer.sendInitialEmbed hackerBattleBot
let run (client : DiscordClient) =
async {
do! client.ConnectAsync () |> Async.AwaitTask
}
hackerBattleBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
GuildEnvironment.botUserHackerBattle <- Some hackerBattleBot.CurrentUser
storeBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
GuildEnvironment.botUserArmory <- Some storeBot.CurrentUser
let clients =
if guild = 922419263275425832uL then
let interactionsConfig = DiscordConfiguration()
interactionsConfig.TokenType <- TokenType.Bot
interactionsConfig.Intents <- DiscordIntents.All
interactionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions
let interactionsBot = new DiscordClient(interactionsConfig)
let commands = interactionsBot.UseSlashCommands()
commands.RegisterCommands<PlayerInteractions.PlayerInteractions>(guild)
//async {
// let! user = hackerBattleBot.GetUserAsync(GuildEnvironment.botIdHackerBattle) |> Async.AwaitTask
// if user <> null then
// GuildEnvironment.botUserHackerBattle <- Some user
// return ()
//} |> Async.RunSynchronously
[ hackerBattleBot ; storeBot ; interactionsBot ]
else
[ hackerBattleBot ; storeBot ]
//async {
// let! user = storeBot.GetUserAsync(GuildEnvironment.botIdHackerBattle) |> Async.AwaitTask
// if user <> null then
// GuildEnvironment.botUserHackerBattle <- Some user
// return ()
//} |> Async.RunSynchronously
if guild = 922419263275425832uL then
let interactionsConfig = DiscordConfiguration()
interactionsConfig.TokenType <- TokenType.Bot
interactionsConfig.Intents <- DiscordIntents.All
interactionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions
let interactionsBot = new DiscordClient(interactionsConfig)
let commands = interactionsBot.UseSlashCommands()
commands.RegisterCommands<PlayerInteractions.PlayerInteractions>(guild)
interactionsBot.ConnectAsync() |> Async.AwaitTask |> Async.RunSynchronously
clients
|> List.map run
|> Async.Sequential
|> Async.RunSynchronously
|> ignore
Task.Delay(-1)
|> Async.AwaitTask

View File

@ -10,19 +10,19 @@
<Content Include="Items.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="paket.references"/>
<Compile Include="GuildEnvironment.fs"/>
<Compile Include="Game.fs"/>
<Compile Include="PlayerInteractions.fs"/>
<Compile Include="Embeds.fs"/>
<Compile Include="Store.fs"/>
<Compile Include="Trainer.fs"/>
<Compile Include="HackerBattle.fs"/>
<Compile Include="SlotMachine.fs"/>
<Compile Include="Bot.fs"/>
<Content Include="paket.references" />
<Compile Include="GuildEnvironment.fs" />
<Compile Include="Game.fs" />
<Compile Include="PlayerInteractions.fs" />
<Compile Include="Embeds.fs" />
<Compile Include="Store.fs" />
<Compile Include="Trainer.fs" />
<Compile Include="HackerBattle.fs" />
<Compile Include="SlotMachine.fs" />
<Compile Include="Bot.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DbService\DbService.fsproj"/>
<ProjectReference Include="..\DbService\DbService.fsproj" />
</ItemGroup>
<Import Project="..\.paket\Paket.Restore.targets"/>
<Import Project="..\.paket\Paket.Restore.targets" />
</Project>

View File

@ -76,36 +76,30 @@ let pickHack actionId attacker defender isTrainer =
.AddEmbed(embed.Build())
.AsEphemeral true
let responseSuccessfulHack (targetName : string) (hack : BattleItem) =
let responseSuccessfulHack earnedMoney (targetId : uint64) (hack : BattleItem) =
let embed = DiscordEmbedBuilder()
embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id))
embed.Title <- "Hack Attack"
embed.Description <- $"You successfully hacked <@{targetId}> using {hack.Name}"
+ (if earnedMoney then ", and stole 💰$GBT {Game.HackPrize} from them!" else "!")
DiscordFollowupMessageBuilder()
.WithContent($"You successfully hacked {targetName} using {hack.Name}, and stole 💰$GBT {Game.HackPrize} from them!")
.AddEmbed(embed.Build())
.AsEphemeral(true)
let responseCreatedShield (shield : BattleItem) =
DiscordFollowupMessageBuilder()
.AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id))))
.AsEphemeral(true)
.WithContent($"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours")
let embed = DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id)))
embed.Title <- "Mounted Shield"
embed.Description <- $"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours"
let responseCreatedShieldTrainer (shield : BattleItem) =
DiscordFollowupMessageBuilder()
.AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id))))
.AddEmbed(embed)
.AsEphemeral(true)
.WithContent($"Mounted a {shield.Name} defense for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours")
let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) target prize =
DiscordMessageBuilder()
.WithContent($"{event.User.Username} successfully hacked <@{target.DiscordId}> for a total of {prize} GoodBoyTokenz")
let getGoodAgainst = function
| BattleClass.Network -> ( ShieldId.Firewall , HackId.Virus )
| BattleClass.Penetration -> ( ShieldId.Cypher , HackId.RemoteAccess )
| BattleClass.Exploit -> ( ShieldId.Encryption , HackId.Worm )
let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : BattleItem array) =
let embeds , buttons =
store
@ -115,13 +109,13 @@ let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : Battle
match item.Type with
| Hack ->
embed
.AddField($"Weak Against |", getGoodAgainst item.Class |> fst |> string , true)
.AddField($"Weak Against |", Game.getGoodAgainst item.Class |> fst |> string , true)
.AddField("Cooldown |", $"{TimeSpan.FromMinutes(int item.Cooldown).Minutes} minutes", true)
.WithThumbnail(getHackIcon (enum<HackId>(item.Id)))
|> ignore
| Shield ->
embed
.AddField($"Strong Against |", getGoodAgainst item.Class |> snd |> string , true)
.AddField($"Strong Against |", Game.getGoodAgainst item.Class |> snd |> string , true)
.AddField("Active For |", $"{TimeSpan.FromMinutes(int item.Cooldown).Hours} hours", true)
.WithThumbnail(getShieldIcon (enum<ShieldId>(item.Id)))
|> ignore
@ -164,3 +158,30 @@ let getSellItemsEmbed (itemType : ItemType) (player : PlayerData) =
.AddEmbeds(embeds)
.AddComponents(buttons)
.AsEphemeral(true)
let getArsenalEmbed (player : PlayerData) =
DiscordFollowupMessageBuilder()
.AsEphemeral(true)
.AddEmbed(
DiscordEmbedBuilder()
.AddField( "Arsenal", Arsenal.statusFormat player ))
let getAchievementEmbed (player : PlayerData) description achievement =
let embed = DiscordEmbedBuilder()
GuildEnvironment.botUserHackerBattle
|> Option.iter (fun bot ->
embed.Author <- DiscordEmbedBuilder.EmbedAuthor()
embed.Author.Name <- bot.Username
embed.Author.IconUrl <- bot.AvatarUrl)
DiscordFollowupMessageBuilder()
.AddEmbed(
// TODO: We can add a Reward field but we'd need to keep track of what the player was awarded
embed.WithTitle("Achievement Unlocked!")
.WithDescription(description)
.WithColor(DiscordColor.Gold)
.AddField("Achievement", achievement)
// TODO: Once we add another achievement, fix this
.WithImageUrl("https://s10.gifyu.com/images/MasterTraining_Degenz.gif"))
.AsEphemeral(true)

View File

@ -23,6 +23,11 @@ module Game =
| Penetration -> DiscordColor.Blurple
| Exploit -> DiscordColor.Green
let getGoodAgainst = function
| BattleClass.Network -> ( ShieldId.Firewall , HackId.Virus )
| BattleClass.Penetration -> ( ShieldId.Cypher , HackId.RemoteAccess )
| BattleClass.Exploit -> ( ShieldId.Encryption , HackId.Worm )
let executePlayerInteraction (ctx : InteractionContext) (dispatch : PlayerData -> Async<unit>) =
async {
let builder = DiscordInteractionResponseBuilder()
@ -54,9 +59,9 @@ module Game =
module Player =
let getItems itemType (player : PlayerData) = player.Arsenal |> Array.filter (fun i -> i.Type = itemType)
let hacks (player : PlayerData) = getItems ItemType.Hack player
let getHacks (player : PlayerData) = getItems ItemType.Hack player
let getShields (player : PlayerData) = getItems ItemType.Shield player
let attacks player =
let getAttacks player =
player.Actions
|> Array.filter (fun act -> match act.Type with Attack _ -> true | _ -> false)
let getDefenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false)
@ -76,3 +81,34 @@ module Player =
let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None)
module Arsenal =
let battleItemFormat (items : BattleItem array) =
match items with
| [||] -> "None"
| _ -> items |> Array.toList |> List.map (fun i -> i.Name) |> String.concat ", "
let actionFormat (actions : Action array) =
match actions with
| [||] -> "None"
| _ ->
let hacks , defenses = actions |> Array.partition (fun act -> match act.Type with Attack _ -> true | Defense -> false)
let hacks = hacks |> Array.take (min hacks.Length 10)
hacks
|> Array.append defenses
|> Array.map (fun act ->
let item = Armory.getItem act.ActionId
match act.Type with
| Attack atk ->
let cooldown = Messaging.getTimeText false Game.SameTargetAttackCooldown act.Timestamp
$"Hacked {atk.Target.Name} {cooldown} ago"
| Defense ->
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 =
$"**Hacks:** {Player.getHacks p |> battleItemFormat}\n
**Shields:** {Player.getShields p |> battleItemFormat}\n
**Hack Attacks:**\n{Player.getAttacks p |> actionFormat}\n
**Active Shields:**\n{Player.getDefenses p |> actionFormat}"

View File

@ -2,6 +2,7 @@
module Degenz.GuildEnvironment
open System
open DSharpPlus.Entities
open dotenv.net
DotEnv.Load(DotEnvOptions(envFilePaths = [ "../../../../stag.env" ], overwriteExistingVars = false))
@ -18,6 +19,9 @@ let channelEventsHackerBattle = getId "CHANNEL_EVENTS_HACKER_BATTLE"
let channelTraining = getId "CHANNEL_TRAINING"
let channelArmory = getId "CHANNEL_ARMORY"
let channelBattle = getId "CHANNEL_BATTLE"
let botHackerBattle = getId "BOT_HACKER_BATTLE"
let botArmory = getId "BOT_ARMORY"
let botIdHackerBattle = getId "BOT_HACKER_BATTLE"
let botIdArmory = getId "BOT_ARMORY"
let roleTrainee = getId "ROLE_TRAINEE"
let mutable botUserHackerBattle : DiscordUser option = None
let mutable botUserArmory : DiscordUser option = None

View File

@ -42,7 +42,7 @@ let checkItemHasCooldown itemId attacker =
Error $"{item.Name} is currently on cooldown, wait {cooldown} to use it again."
let checkHasEmptyHacks attacker =
match Player.hacks attacker with
match Player.getHacks attacker with
| [||] -> Error $"You currently do not have any Hacks to steal 💰$GBT from others. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one."
| _ -> Ok attacker
@ -94,7 +94,7 @@ let successfulHack (event : ComponentInteractionCreateEventArgs) attacker defend
async {
do! updateCombatants attacker defender hack Game.HackPrize
let embed = Embeds.responseSuccessfulHack (defender.Name) (Armory.getItem hack)
let embed = Embeds.responseSuccessfulHack true defender.DiscordId (Armory.getItem hack)
do! event.Interaction.CreateFollowupMessageAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
@ -114,7 +114,7 @@ let failedHack (event : ComponentInteractionCreateEventArgs) attacker defender h
do! updateCombatants attacker defender hack -Game.ShieldPrize
let builder = DiscordMessageBuilder()
builder.WithContent($"Hacking attempt failed! {defender.Name} defended hack from {event.User.Username} and stole {Game.ShieldPrize} $GBT from them! ") |> ignore
builder.WithContent($"Hacking attempt failed! <@{defender.DiscordId}> defended hack from {event.User.Username} and stole {Game.ShieldPrize} $GBT from them! ") |> ignore
let channel = (event.Guild.GetChannel(GuildEnvironment.channelEventsHackerBattle))
do! channel.SendMessageAsync(builder)
|> Async.AwaitTask
@ -224,6 +224,20 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve
|> Async.AwaitTask
}
let arsenal (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let updatedPlayer = Player.removeExpiredActions false player
let builder = DiscordFollowupMessageBuilder()
let embed = DiscordEmbedBuilder()
embed.AddField("Arsenal", Arsenal.statusFormat updatedPlayer) |> ignore
builder.AddEmbed(embed) |> ignore
builder.IsEphemeral <- true
do! ctx.FollowUpAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! DbService.updatePlayer updatedPlayer
})
type HackerGame() =
inherit ApplicationCommandModule ()
@ -247,6 +261,10 @@ type HackerGame() =
do! Messaging.sendSimpleResponse ctx msg
}
[<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
member this.Arsenal (ctx : InteractionContext) =
enforceChannels ctx (Trainer.handleArsenal) arsenal
[<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 ctx (Trainer.attack target) (attack target)

View File

@ -29,51 +29,6 @@ let checkHasItemsInArsenal itemType player =
then Ok player
else Error $"You currently have no {itemType}s in your arsenal to sell!"
let battleItemFormat (items : BattleItem array) =
match items with
| [||] -> "None"
| _ -> items |> Array.toList |> List.map (fun i -> i.Name) |> String.concat ", "
let actionFormat (actions : Action array) =
match actions with
| [||] -> "None"
| _ ->
let hacks , defenses = actions |> Array.partition (fun act -> match act.Type with Attack _ -> true | Defense -> false)
let hacks = hacks |> Array.take (min hacks.Length 10)
hacks
|> Array.append defenses
|> Array.map (fun act ->
let item = Armory.getItem act.ActionId
match act.Type with
| Attack atk ->
let cooldown = getTimeText false Game.SameTargetAttackCooldown act.Timestamp
$"Hacked {atk.Target.Name} {cooldown} ago"
| Defense ->
let cooldown = getTimeText true (System.TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp
$"{item.Name} Shield active for {cooldown}")
|> String.concat "\n"
let statusFormat p =
$"**Hacks:** {Player.hacks p |> battleItemFormat}\n
**Shields:** {Player.getShields p |> battleItemFormat}\n
**Hack Attacks:**\n{Player.attacks p |> actionFormat}\n
**Active Shields:**\n{Player.getDefenses p |> actionFormat}"
// TODO: There's a 1000 character limit for embeds, so you need to filter by N last actions
let arsenal (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let updatedPlayer = Player.removeExpiredActions false player
let builder = DiscordFollowupMessageBuilder()
let embed = DiscordEmbedBuilder()
embed.AddField("Arsenal", statusFormat updatedPlayer) |> ignore
builder.AddEmbed(embed) |> ignore
builder.IsEphemeral <- true
do! ctx.FollowUpAsync(builder)
|> Async.AwaitTask
|> Async.Ignore
do! DbService.updatePlayer updatedPlayer
})
let buy itemType (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let itemStore = Embeds.getBuyItemsEmbed player itemType Armory.battleItems
@ -155,9 +110,6 @@ type Store() =
do! Messaging.sendSimpleResponse ctx msg
}
[<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
member this.Arsenal (ctx : InteractionContext) = arsenal ctx
[<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
member _.BuyHack (ctx : InteractionContext) = enforceChannel ctx (buy ItemType.Hack)

View File

@ -1,5 +1,6 @@
module Degenz.Trainer
open System.Text
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.EventArgs
@ -7,6 +8,8 @@ open DSharpPlus.SlashCommands
open Degenz.Types
open Degenz.Messaging
let trainerAchievement = "FINISHED_TRAINER"
// TODO: We should either gift the weapons to the player during training, or have them buy it
// TODO: We should tell the user to type out /arsenal during the training
// TODO: How do we handle the money being generated here? It's fake but fake is confusing
@ -34,7 +37,7 @@ let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let shieldMessage , weaponName =
if Player.getShields player |> Array.isEmpty
then $"You do not have any Shields in your arsenal, take this {defaultShield.Name}, you can use it for now.\n\n" , defaultShield.Name
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
@ -45,8 +48,7 @@ let handleTrainerStep1 (event : ComponentInteractionCreateEventArgs) =
|> Async.AwaitTask
do! sendFollowUpMessage event
("Beautopia© is a dangerous place...\n"
+ "Quick, put up a SHIELD 🛡 before another Degen hacks you, and steals your 💰$GBT.\n\n"
("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")
@ -66,13 +68,12 @@ let defend (ctx : InteractionContext) =
|> Async.Ignore
})
let handleDefenseMsg = {
let handleDefenseMsg shieldId hackId = {
ButtonId = "Trainer-3"
ButtonText = "Got it"
Message = "🎉 Congratulations\n\n"
+ "You successfully defended my hack!\n\n"
+ "Because I tried hacking you when you had your defense up, you stole money from me...\n"
+ "Defenses only protect you for a LIMITED TIME, so remember to keep at least 1 defense up at all times, or risk getting hacked!"
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!"
}
let handleDefense (event : ComponentInteractionCreateEventArgs) =
@ -81,43 +82,45 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
let split = event.Id.Split("-")
let shieldId = enum<ShieldId>(int split.[2])
let shield = Armory.getItem (int shieldId)
let embed = Embeds.responseCreatedShieldTrainer shield
let embed = Embeds.responseCreatedShield (shield)
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
do! Async.Sleep 4000
do! sendMessage' "Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now"
let weakHack = Game.getGoodAgainst shield.Class
do! sendMessage' $"Ok, good, let me make sure that worked.\n\nI'll try to **hack** you now with **{snd weakHack}**"
do! Async.Sleep 5000
do! sendMessage' $"❌ HACKING FAILED!\n\n{player.Name} defended the hack from <@{GuildEnvironment.botHackerBattle}>, and stole 💰$GBT {Game.ShieldPrize} from them!"
do! sendMessage' $"❌ HACKING FAILED!\n\n{player.Name} defended hack from <@{GuildEnvironment.botIdHackerBattle}>!"
do! Async.Sleep 4000
do! sendFollowUpMessageWithButton event handleDefenseMsg
})
do! sendFollowUpMessageWithButton event (handleDefenseMsg shieldId (snd weakHack))
})
let handleTrainerStep3 (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let hackMessage , weaponName =
if Player.hacks player |> Array.isEmpty
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.hacks player |> Array.tryHead |> Option.defaultValue defaultHack |> fun w -> w.Name
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
do! sendFollowUpMessage event
("Now lets **HACK!** 💻\n\n"
+ "I want you to **HACK ME**, and try to steal my 💰$GBT...\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.botHackerBattle}> as your target, and select `{weaponName}`")
+ $"Type the `/hack` command now, then choose me - <@{GuildEnvironment.botIdHackerBattle}> as your target, and select `{weaponName}`")
})
let attack (target : DiscordUser) (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let isRightTarget = target.Id = GuildEnvironment.botHackerBattle
let isRightTarget = target.Id = GuildEnvironment.botIdHackerBattle
match isRightTarget with
| true ->
let playerWithAttacks =
match Player.hacks player with
match Player.getHacks player with
| [||] -> { player with Arsenal = [| defaultHack |] }
| _ -> player
let embed = Embeds.pickHack "Trainer-4" playerWithAttacks player true
let bot = { player with DiscordId = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
let embed = Embeds.pickHack "Trainer-4" playerWithAttacks bot true
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
@ -137,8 +140,8 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
Game.executePlayerEvent event (fun player -> async {
let sendMessage' = sendFollowUpMessage event
do! Async.Sleep 1000
let hack = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultHack
let embed = Embeds.responseSuccessfulHack "Sensei" hack
let hack = Player.getHacks player |> Array.tryHead |> Option.defaultValue defaultHack
let embed = Embeds.responseSuccessfulHack false GuildEnvironment.botIdHackerBattle hack
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
do! Async.Sleep 5000
do! sendMessage'
@ -149,16 +152,85 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
do! Async.Sleep 6000
let membr = event.User :?> DiscordMember
let role = event.Guild.GetRole(GuildEnvironment.roleTrainee)
do! membr.RevokeRoleAsync(role)
let sb = StringBuilder("Here, ")
let! achievements = DbService.getAchievements player.DiscordId
let isFirstTrainer = achievements |> Option.map (Seq.contains trainerAchievement >> not) |> Option.defaultValue true
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.Cost else 0<GBT>
let shieldMoney = if hasShields then defaultShield.Cost 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.Cost} 💰$GBT"
| true , false -> $"I'm going to gift you a shield, `{freeShield.Name}` and {defaultHack.Cost} 💰$GBT"
| false , false -> $"I'm going to gift you a hack,`{freeHack.Name}` and a shield, `{freeShield.Name}`"
sb.Append(giftMsg) |> 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 + hackMoney + shieldMoney
Actions = [
{ Action.Timestamp = System.DateTime.UtcNow
Action.Type =
Attack {
Result = true
Target = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }
}
ActionId = defaultHack.Id
}
if not hasShields then {
Action.Timestamp = System.DateTime.UtcNow
Action.Type = Defense
Action.ActionId = freeShield.Id
}
] |> Seq.toArray
|> Array.append player.Actions
Arsenal = [
if not hasHacks then freeHack
if not hasShields then freeShield
] |> Seq.toArray
|> Array.append player.Arsenal
}
do! DbService.updatePlayer updatedPlayer
do! sendFollowUpMessage event (sb.ToString())
else
do! sendFollowUpMessage event ($"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! 😱")
let role = event.Guild.GetRole(GuildEnvironment.roleTrainee)
let ``member`` = event.User :?> DiscordMember
do! ``member``.RevokeRoleAsync(role)
|> Async.AwaitTask
})
let handleArsenal (ctx : InteractionContext) =
Game.executePlayerInteraction ctx (fun player -> async {
let updatedPlayer = Player.removeExpiredActions false player
let embed = Embeds.getArsenalEmbed updatedPlayer
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! Async.Sleep 3000
let embed = Embeds.getAchievementEmbed player "You completed the Training Dojo and collected loot." trainerAchievement
do! ctx.FollowUpAsync(embed)
|> Async.AwaitTask
|> Async.Ignore
do! Async.Sleep 2000
let role = ctx.Guild.GetRole(GuildEnvironment.roleTrainee)
do! ctx.Member.RevokeRoleAsync(role)
|> Async.AwaitTask
do! sendFollowUpMessage event
("Your training is **complete!**\n\n"
+ "But, youre going to need more **HACKS & SHIELDS** 🛡 to survive in **Beautopia©...**\n"
+ $"You can purchase them from <@{GuildEnvironment.botArmory}> anytime you want...\n\n"
+ $"Go to the <#{GuildEnvironment.channelArmory}> **NOW** and type the `/buy-hack` slash command! 😱")
do! sendFollowUpMessageFromCtx ctx "Now get out of there and go hack other Degenz!"
})
let handleButtonEvent (event : ComponentInteractionCreateEventArgs) =

View File

@ -2,11 +2,10 @@
open System
open System.Collections.Generic
open System.Threading.Tasks
open Degenz.Types
open MongoDB.Bson
open MongoDB.Bson.Serialization
open MongoDB.Driver
open Degenz.Types
[<CLIMutable>]
type AttackAction =
@ -90,6 +89,28 @@ let tryFindPlayer (id : uint64) =
|> Some
}
let getAchievements (id : uint64) =
async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id)
try
let! player = players.FindAsync<BsonDocument>(filter) |> Async.AwaitTask
match player.FirstOrDefault() with
| null -> return None
| p -> return p
.GetValue("achievements")
.AsBsonArray
|> Seq.map (fun (bv : BsonValue) -> bv.AsString)
|> Some
with ex -> return None
}
let addAchievement (id : uint64) (achievement : string) =
async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id)
let update = Builders<BsonDocument>.Update.Push("achievements", achievement)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}
let insertNewPlayer (player : PlayerData) =
async {
let p = playerMap player
@ -114,7 +135,6 @@ let updatePlayer (player : PlayerData) =
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}
//let getTopPlayers amount =
// async {
// return! players.FindAsync()

View File

@ -71,7 +71,7 @@ module Types =
type Action =
{ ActionId : int
Type : ActionType
Timestamp : DateTime }
Timestamp : System.DateTime }
[<CLIMutable>]
type PlayerData =
@ -86,6 +86,9 @@ module Armory =
let file = System.IO.File.ReadAllText("Items.json")
JsonConvert.DeserializeObject<BattleItem array>(file)
let hacks = battleItems |> Array.filter (fun bi -> match bi.Type with Hack -> true | Shield -> false)
let shields = battleItems |> Array.filter (fun bi -> match bi.Type with Shield -> true | Hack -> false)
let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId)
module Messaging =