New trainer bot flow that gifts weapons or money, plus achievement
This commit is contained in:
parent
213f9be2ee
commit
785660558d
50
Bot/Bot.fs
50
Bot/Bot.fs
@ -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
|
||||
|
@ -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>
|
@ -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)
|
40
Bot/Game.fs
40
Bot/Game.fs
@ -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}"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
48
Bot/Store.fs
48
Bot/Store.fs
@ -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)
|
||||
|
||||
|
132
Bot/Trainer.fs
132
Bot/Trainer.fs
@ -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 let’s **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, you’re 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) =
|
||||
|
@ -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()
|
||||
|
@ -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 =
|
||||
|
Loading…
x
Reference in New Issue
Block a user