New store flow
This commit is contained in:
		
							parent
							
								
									36471195aa
								
							
						
					
					
						commit
						dcf0bdb174
					
				
							
								
								
									
										37
									
								
								Bot/Bot.fs
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								Bot/Bot.fs
									
									
									
									
									
								
							@ -4,7 +4,6 @@ open System.Threading.Tasks
 | 
				
			|||||||
open DSharpPlus
 | 
					open DSharpPlus
 | 
				
			||||||
open DSharpPlus.SlashCommands
 | 
					open DSharpPlus.SlashCommands
 | 
				
			||||||
open Degenz
 | 
					open Degenz
 | 
				
			||||||
open Degenz.PlayerInteractions
 | 
					 | 
				
			||||||
open Degenz.HackerBattle
 | 
					open Degenz.HackerBattle
 | 
				
			||||||
open Degenz.Store
 | 
					open Degenz.Store
 | 
				
			||||||
open Emzi0767.Utilities
 | 
					open Emzi0767.Utilities
 | 
				
			||||||
@ -12,46 +11,41 @@ open Emzi0767.Utilities
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
 | 
					type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let playerInteractionsConfig = DiscordConfiguration()
 | 
					let guild = GuildEnvironment.guildId
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let hackerBattleConfig = DiscordConfiguration()
 | 
					let hackerBattleConfig = DiscordConfiguration()
 | 
				
			||||||
let storeConfig = DiscordConfiguration()
 | 
					let storeConfig = DiscordConfiguration()
 | 
				
			||||||
//let slotMachineConfig = DiscordConfiguration()
 | 
					//let slotMachineConfig = DiscordConfiguration()
 | 
				
			||||||
//hackerBattleConfig.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
 | 
					//hackerBattleConfig.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//let configs = [| playerInteractionsConfig ; hackerBattleConfig ; storeConfig ; slotMachineConfig ; |]
 | 
					//let configs = [| hackerBattleConfig ; storeConfig ; slotMachineConfig ; |]
 | 
				
			||||||
let configs = [| playerInteractionsConfig ; hackerBattleConfig ; storeConfig |]
 | 
					let configs = [ hackerBattleConfig ; storeConfig ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
for conf in configs do
 | 
					for conf in configs do
 | 
				
			||||||
    conf.TokenType <- TokenType.Bot
 | 
					    conf.TokenType <- TokenType.Bot
 | 
				
			||||||
    conf.Intents <- DiscordIntents.All
 | 
					    conf.Intents <- DiscordIntents.All
 | 
				
			||||||
let guild = GuildEnvironment.guildId
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
playerInteractionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions
 | 
					 | 
				
			||||||
hackerBattleConfig.Token <- GuildEnvironment.tokenHackerBattle
 | 
					hackerBattleConfig.Token <- GuildEnvironment.tokenHackerBattle
 | 
				
			||||||
storeConfig.Token <- GuildEnvironment.tokenStore
 | 
					storeConfig.Token <- GuildEnvironment.tokenStore
 | 
				
			||||||
//slotMachineConfig.Token <- Environment.GetEnvironmentVariable("BOT_SLOT_MACHINE")
 | 
					//slotMachineConfig.Token <- Environment.GetEnvironmentVariable("BOT_SLOT_MACHINE")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//let playerInteractionsBot = new DiscordClient(playerInteractionsConfig)
 | 
					 | 
				
			||||||
let hackerBattleBot = new DiscordClient(hackerBattleConfig)
 | 
					let hackerBattleBot = new DiscordClient(hackerBattleConfig)
 | 
				
			||||||
let storeBot = new DiscordClient(storeConfig)
 | 
					let storeBot = new DiscordClient(storeConfig)
 | 
				
			||||||
//let slotMachineBot = new DiscordClient(slotMachineConfig)
 | 
					//let slotMachineBot = new DiscordClient(slotMachineConfig)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//let clients = [| storeBot  ; trainerBot ; hackerBattleBot ; playerInteractionsBot ; slotMachineBot |]
 | 
					//let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |]
 | 
				
			||||||
//let clients = [| hackerBattleBot ; storeBot ; playerInteractionsBot |]
 | 
					let clients = [ hackerBattleBot ; storeBot ]
 | 
				
			||||||
let clients = [| hackerBattleBot ; storeBot |]
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//let sc1 = playerInteractionsBot.UseSlashCommands()
 | 
					let sc1 = hackerBattleBot.UseSlashCommands()
 | 
				
			||||||
let sc3 = hackerBattleBot.UseSlashCommands()
 | 
					let sc2 = storeBot.UseSlashCommands()
 | 
				
			||||||
let sc4 = storeBot.UseSlashCommands()
 | 
					//let sc3 = slotMachineBot.UseSlashCommands()
 | 
				
			||||||
//let sc5 = slotMachineBot.UseSlashCommands()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
//sc1.RegisterCommands<PlayerInteractions>(guild);
 | 
					sc1.RegisterCommands<HackerGame>(guild);
 | 
				
			||||||
sc3.RegisterCommands<HackerGame>(guild);
 | 
					sc2.RegisterCommands<Store>(guild);
 | 
				
			||||||
sc4.RegisterCommands<Store>(guild);
 | 
					//sc3.RegisterCommands<SlotMachine>(guild);
 | 
				
			||||||
//sc5.RegisterCommands<SlotMachine>(guild);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent))
 | 
					hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent))
 | 
				
			||||||
storeBot.add_ComponentInteractionCreated(AsyncEventHandler(Store.handleSellButtonEvents))
 | 
					storeBot.add_ComponentInteractionCreated(AsyncEventHandler(Store.handleStoreEvents))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) =
 | 
					let asdf (_ : DiscordClient) (event : DSharpPlus.EventArgs.InteractionCreateEventArgs) =
 | 
				
			||||||
    async {
 | 
					    async {
 | 
				
			||||||
@ -74,11 +68,10 @@ let run (client : DiscordClient) =
 | 
				
			|||||||
        do! client.ConnectAsync () |> Async.AwaitTask
 | 
					        do! client.ConnectAsync () |> Async.AwaitTask
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Trainer.sendInitialEmbed hackerBattleBot
 | 
					//Trainer.sendInitialEmbed hackerBattleBot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clients
 | 
					clients
 | 
				
			||||||
|> Array.map run
 | 
					|> List.map run
 | 
				
			||||||
|> Array.toSeq
 | 
					 | 
				
			||||||
|> Async.Sequential
 | 
					|> Async.Sequential
 | 
				
			||||||
|> Async.RunSynchronously
 | 
					|> Async.RunSynchronously
 | 
				
			||||||
|> ignore
 | 
					|> ignore
 | 
				
			||||||
 | 
				
			|||||||
@ -1,28 +1,28 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
					<Project Sdk="Microsoft.NET.Sdk">
 | 
				
			||||||
  <PropertyGroup>
 | 
					    <PropertyGroup>
 | 
				
			||||||
    <OutputType>Exe</OutputType>
 | 
					        <OutputType>Exe</OutputType>
 | 
				
			||||||
    <TargetFramework>net6.0</TargetFramework>
 | 
					        <TargetFramework>net6.0</TargetFramework>
 | 
				
			||||||
    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
 | 
					        <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
 | 
				
			||||||
    <RootNamespace>Degenz</RootNamespace>
 | 
					        <RootNamespace>Degenz</RootNamespace>
 | 
				
			||||||
  </PropertyGroup>
 | 
					    </PropertyGroup>
 | 
				
			||||||
  <ItemGroup>
 | 
					    <ItemGroup>
 | 
				
			||||||
    <Content Include="Items.json">
 | 
					        <Content Include="Items.json">
 | 
				
			||||||
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
					            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 | 
				
			||||||
    </Content>
 | 
					        </Content>
 | 
				
			||||||
    <Content Include="paket.references" />
 | 
					        <Content Include="paket.references"/>
 | 
				
			||||||
    <Compile Include="GuildEnvironment.fs" />
 | 
					        <Compile Include="GuildEnvironment.fs"/>
 | 
				
			||||||
    <Compile Include="Game.fs" />
 | 
					        <Compile Include="Game.fs"/>
 | 
				
			||||||
    <Compile Include="Embeds.fs" />
 | 
					        <Compile Include="PlayerInteractions.fs"/>
 | 
				
			||||||
    <Compile Include="Store.fs" />
 | 
					        <Compile Include="Embeds.fs"/>
 | 
				
			||||||
    <Compile Include="Trainer.fs" />
 | 
					        <Compile Include="Store.fs"/>
 | 
				
			||||||
    <Compile Include="HackerBattle.fs" />
 | 
					        <Compile Include="Trainer.fs"/>
 | 
				
			||||||
    <Compile Include="SlotMachine.fs" />
 | 
					        <Compile Include="HackerBattle.fs"/>
 | 
				
			||||||
    <Compile Include="PlayerInteractions.fs" />
 | 
					        <Compile Include="SlotMachine.fs"/>
 | 
				
			||||||
    <Compile Include="Bot.fs" />
 | 
					        <Compile Include="Bot.fs"/>
 | 
				
			||||||
  </ItemGroup>
 | 
					    </ItemGroup>
 | 
				
			||||||
  <ItemGroup>
 | 
					    <ItemGroup>
 | 
				
			||||||
    <ProjectReference Include="..\DbService\DbService.fsproj" />
 | 
					        <ProjectReference Include="..\DbService\DbService.fsproj"/>
 | 
				
			||||||
  </ItemGroup>
 | 
					    </ItemGroup>
 | 
				
			||||||
  <Import Project="..\.paket\Paket.Restore.targets" />
 | 
					    <Import Project="..\.paket\Paket.Restore.targets"/>
 | 
				
			||||||
</Project>
 | 
					</Project>
 | 
				
			||||||
							
								
								
									
										107
									
								
								Bot/Embeds.fs
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								Bot/Embeds.fs
									
									
									
									
									
								
							@ -4,35 +4,22 @@ open System
 | 
				
			|||||||
open DSharpPlus.EventArgs
 | 
					open DSharpPlus.EventArgs
 | 
				
			||||||
open Degenz.Types
 | 
					open Degenz.Types
 | 
				
			||||||
open DSharpPlus.Entities
 | 
					open DSharpPlus.Entities
 | 
				
			||||||
open AsciiTableFormatter
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let hackGif = "https://s10.gifyu.com/images/Hacker-Degenz-V20ce8eb832734aa62-min.gif"
 | 
					let hackGif = "https://s10.gifyu.com/images/Hacker-Degenz-V20ce8eb832734aa62-min.gif"
 | 
				
			||||||
let shieldGif = "https://s10.gifyu.com/images/Defense-Degenz-V2-min.gif"
 | 
					let shieldGif = "https://s10.gifyu.com/images/Defense-Degenz-V2-min.gif"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let getHackGif = function
 | 
					let getHackGif = function
 | 
				
			||||||
    | HackId.Virus -> "https://s10.gifyu.com/images/Attack-DegenZ-1.gif"
 | 
					    | HackId.Virus -> "https://s10.gifyu.com/images/Virus-icon.jpg"
 | 
				
			||||||
    | HackId.RemoteAccess -> "https://s10.gifyu.com/images/Mind-Control-Degenz-V2-min.gif"
 | 
					    | HackId.RemoteAccess -> "https://s10.gifyu.com/images/Mind-Control-Degenz-V2-min.jpg"
 | 
				
			||||||
    | HackId.Worm -> "https://s10.gifyu.com/images/WormBugAttack_Degenz-min.gif"
 | 
					    | HackId.Worm -> "https://s10.gifyu.com/images/WormBugAttack_Degenz-min.jpg"
 | 
				
			||||||
    | _ -> hackGif
 | 
					    | _ -> hackGif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let getShieldGif = function
 | 
					let getShieldGif = function
 | 
				
			||||||
    | ShieldId.Firewall -> "https://s10.gifyu.com/images/Defense-GIF-1-Degenz-min.gif"
 | 
					    | ShieldId.Firewall -> "https://s10.gifyu.com/images/Defense-GIF-1-Degenz-1.jpg"
 | 
				
			||||||
    | ShieldId.Encryption -> "https://s10.gifyu.com/images/Encryption-Degenz-V2-1-min.gif"
 | 
					    | ShieldId.Encryption -> "https://s10.gifyu.com/images/Encryption-Degenz-V2-1-min.jpg"
 | 
				
			||||||
    | ShieldId.Cypher -> "https://s10.gifyu.com/images/Cypher-Smaller.gif"
 | 
					    | ShieldId.Cypher -> "https://s10.gifyu.com/images/Cypher-Smaller.jpg"
 | 
				
			||||||
    | _ -> shieldGif
 | 
					    | _ -> shieldGif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
let constructEmbed message =
 | 
					 | 
				
			||||||
    let builder = DiscordEmbedBuilder()
 | 
					 | 
				
			||||||
    builder.Color <- Optional(DiscordColor.Blurple)
 | 
					 | 
				
			||||||
    builder.Description <- message
 | 
					 | 
				
			||||||
    let author = DiscordEmbedBuilder.EmbedAuthor()
 | 
					 | 
				
			||||||
    author.Name <- "Degenz Hacker Game"
 | 
					 | 
				
			||||||
    author.Url <- "https://twitter.com/degenzgame"
 | 
					 | 
				
			||||||
    author.IconUrl <- "https://pbs.twimg.com/profile_images/1473192843359309825/cqjm0VQ4_400x400.jpg"
 | 
					 | 
				
			||||||
    builder.Author <- author
 | 
					 | 
				
			||||||
    builder.Build()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let pickDefense actionId player =
 | 
					let pickDefense actionId player =
 | 
				
			||||||
    let buttons =
 | 
					    let buttons =
 | 
				
			||||||
        Messaging.constructButtons actionId (string player.DiscordId) (Player.shields player)
 | 
					        Messaging.constructButtons actionId (string player.DiscordId) (Player.shields player)
 | 
				
			||||||
@ -85,43 +72,73 @@ let responseCreatedShieldTrainer (shield : BattleItem) =
 | 
				
			|||||||
    DiscordFollowupMessageBuilder()
 | 
					    DiscordFollowupMessageBuilder()
 | 
				
			||||||
        .AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id))))
 | 
					        .AddEmbed(DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id))))
 | 
				
			||||||
        .AsEphemeral(true)
 | 
					        .AsEphemeral(true)
 | 
				
			||||||
        .WithContent($"Mounted a {shield.Name} defense for 6 hours")
 | 
					        .WithContent($"Mounted a {shield.Name} defense for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) targetId prize =
 | 
					let eventSuccessfulHack (event : ComponentInteractionCreateEventArgs) targetId prize =
 | 
				
			||||||
    DiscordMessageBuilder()
 | 
					    DiscordMessageBuilder()
 | 
				
			||||||
        .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
 | 
					        .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let eventFailedHack (event : ComponentInteractionCreateEventArgs) targetId prize =
 | 
					let eventFailedHack (event : ComponentInteractionCreateEventArgs) targetId prize =
 | 
				
			||||||
//    let embed =
 | 
					 | 
				
			||||||
//        DiscordEmbedBuilder()
 | 
					 | 
				
			||||||
//            .WithColor(DiscordColor.Blurple)
 | 
					 | 
				
			||||||
//            .WithDescription("Pick the hack that you want to use")
 | 
					 | 
				
			||||||
//            .WithImageUrl(hackGif)
 | 
					 | 
				
			||||||
//
 | 
					 | 
				
			||||||
    DiscordMessageBuilder()
 | 
					    DiscordMessageBuilder()
 | 
				
			||||||
        .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
 | 
					        .WithContent($"{event.User.Username} successfully hacked <@{targetId}> for a total of {prize} GoodBoyTokenz")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[<CLIMutable>]
 | 
					let getGoodAgainst = function
 | 
				
			||||||
type Table = {
 | 
					    | BattleClass.Network     -> ( ShieldId.Firewall   , HackId.Virus        )
 | 
				
			||||||
    Name : string
 | 
					    | BattleClass.Penetration -> ( ShieldId.Cypher     , HackId.RemoteAccess )
 | 
				
			||||||
    Cost : string
 | 
					    | BattleClass.Exploit     -> ( ShieldId.Encryption , HackId.Worm         )
 | 
				
			||||||
    Class : string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let storeListing store =
 | 
					let getBuyItemsEmbed (itemType : ItemType) (store : BattleItem array) =
 | 
				
			||||||
    let embeds =
 | 
					    let embeds , buttons =
 | 
				
			||||||
        store
 | 
					        store
 | 
				
			||||||
        |> Array.groupBy (fun (bi : BattleItem) -> bi.Type)
 | 
					        |> Array.filter (fun i -> i.Type = itemType)
 | 
				
			||||||
        |> Array.map (fun ( itemType , items ) ->
 | 
					        |> Array.map (fun item ->
 | 
				
			||||||
            let msg =
 | 
					            let embed = DiscordEmbedBuilder()
 | 
				
			||||||
                items
 | 
					            match item.Type with
 | 
				
			||||||
                |> Array.map (fun item -> { Name = item.Name ; Cost = string item.Cost ; Class = string item.Class })
 | 
					            | Hack ->
 | 
				
			||||||
                |> Formatter.Format
 | 
					                embed
 | 
				
			||||||
                |> sprintf "**%As**\n``` %s ```" itemType
 | 
					                    .AddField($"Weak Against  |", getGoodAgainst item.Class |> fst |> string , true)
 | 
				
			||||||
            DiscordEmbedBuilder()
 | 
					                    .AddField("Cooldown  |", $"{TimeSpan.FromMinutes(int item.Cooldown).Minutes} minutes", true)
 | 
				
			||||||
                .WithDescription(msg)
 | 
					                    .WithThumbnail(getHackGif (enum<HackId>(item.Id)))
 | 
				
			||||||
                .Build())
 | 
					                    |> ignore
 | 
				
			||||||
 | 
					            | Shield ->
 | 
				
			||||||
 | 
					                embed
 | 
				
			||||||
 | 
					                    .AddField($"Strong Against  |", getGoodAgainst item.Class |> snd |> string , true)
 | 
				
			||||||
 | 
					                    .AddField("Active For  |", $"{TimeSpan.FromMinutes(int item.Cooldown).Hours} hours", true)
 | 
				
			||||||
 | 
					                    .WithThumbnail(getShieldGif (enum<ShieldId>(item.Id)))
 | 
				
			||||||
 | 
					                    |> ignore
 | 
				
			||||||
 | 
					            embed
 | 
				
			||||||
 | 
					              .AddField("Cost 💰", $"{item.Cost} $GBT", true)
 | 
				
			||||||
 | 
					              .WithColor(Game.getClassEmbedColor item.Class)
 | 
				
			||||||
 | 
					              .WithTitle($"{item.Name}")
 | 
				
			||||||
 | 
					              |> ignore
 | 
				
			||||||
 | 
					            let button = DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Buy-{item.Id}", $"Buy {item.Name}")
 | 
				
			||||||
 | 
					            embed.Build() , button :> DiscordComponent)
 | 
				
			||||||
 | 
					        |> Array.unzip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    DiscordInteractionResponseBuilder()
 | 
					    DiscordFollowupMessageBuilder()
 | 
				
			||||||
        .AddEmbeds(embeds)
 | 
					        .AddEmbeds(embeds)
 | 
				
			||||||
 | 
					        .AddComponents(buttons)
 | 
				
			||||||
 | 
					        .AsEphemeral(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let getSellItemsEmbed (itemType : ItemType) (player : PlayerData) =
 | 
				
			||||||
 | 
					    let embeds , buttons =
 | 
				
			||||||
 | 
					        player.Arsenal
 | 
				
			||||||
 | 
					        |> Array.filter (fun i -> i.Type = itemType)
 | 
				
			||||||
 | 
					        |> Array.map (fun item ->
 | 
				
			||||||
 | 
					            let embed = DiscordEmbedBuilder()
 | 
				
			||||||
 | 
					            match item.Type with
 | 
				
			||||||
 | 
					            | Hack -> embed.WithThumbnail(getHackGif (enum<HackId>(item.Id))) |> ignore
 | 
				
			||||||
 | 
					            | Shield -> embed.WithThumbnail(getShieldGif (enum<ShieldId>(item.Id))) |> ignore
 | 
				
			||||||
 | 
					            embed
 | 
				
			||||||
 | 
					              .AddField("Sell For 💰", $"{item.Cost} $GBT", true)
 | 
				
			||||||
 | 
					              .WithColor(Game.getClassEmbedColor item.Class)
 | 
				
			||||||
 | 
					              .WithTitle($"{item.Name}")
 | 
				
			||||||
 | 
					              |> ignore
 | 
				
			||||||
 | 
					            let button = DiscordButtonComponent(Game.getClassButtonColor item.Class, $"Sell-{item.Id}", $"Sell {item.Name}")
 | 
				
			||||||
 | 
					            embed.Build() , button :> DiscordComponent)
 | 
				
			||||||
 | 
					        |> Array.unzip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    DiscordFollowupMessageBuilder()
 | 
				
			||||||
 | 
					        .AddEmbeds(embeds)
 | 
				
			||||||
 | 
					        .AddComponents(buttons)
 | 
				
			||||||
        .AsEphemeral(true)
 | 
					        .AsEphemeral(true)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										99
									
								
								Bot/Game.fs
									
									
									
									
									
								
							
							
						
						
									
										99
									
								
								Bot/Game.fs
									
									
									
									
									
								
							@ -1,43 +1,78 @@
 | 
				
			|||||||
module Degenz.Game
 | 
					namespace Degenz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
open System
 | 
					 | 
				
			||||||
open System.Threading.Tasks
 | 
					open System.Threading.Tasks
 | 
				
			||||||
open DSharpPlus
 | 
					open DSharpPlus
 | 
				
			||||||
open DSharpPlus.Entities
 | 
					open DSharpPlus.Entities
 | 
				
			||||||
open DSharpPlus.EventArgs
 | 
					open DSharpPlus.EventArgs
 | 
				
			||||||
open DSharpPlus.SlashCommands
 | 
					open DSharpPlus.SlashCommands
 | 
				
			||||||
open Degenz.DbService
 | 
					open Degenz.DbService
 | 
				
			||||||
open Microsoft.VisualBasic
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let HackPrize = 10<GBT>
 | 
					module Game =
 | 
				
			||||||
let ShieldPrize = 5<GBT>
 | 
					    let HackPrize = 10<GBT>
 | 
				
			||||||
 | 
					    let ShieldPrize = 5<GBT>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let executePlayerInteraction (ctx : InteractionContext) (dispatch : PlayerData -> Async<unit>) =
 | 
					    let SameTargetAttackCooldown = System.TimeSpan.FromHours(6)
 | 
				
			||||||
    async {
 | 
					 | 
				
			||||||
        let builder = DiscordInteractionResponseBuilder()
 | 
					 | 
				
			||||||
        builder.IsEphemeral <- true
 | 
					 | 
				
			||||||
        builder.Content <- "Content"
 | 
					 | 
				
			||||||
        do! ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder)
 | 
					 | 
				
			||||||
            |> Async.AwaitTask
 | 
					 | 
				
			||||||
        let! playerResult = tryFindPlayer ctx.Member.Id
 | 
					 | 
				
			||||||
        match playerResult with
 | 
					 | 
				
			||||||
        | Some player -> do! dispatch player
 | 
					 | 
				
			||||||
        | None -> do! Messaging.sendSimpleResponse ctx "You are currently not a hacker, first use the /redpill command to become one"
 | 
					 | 
				
			||||||
    } |> Async.StartAsTask
 | 
					 | 
				
			||||||
      :> Task
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: Create an abstraction for these two helper functions
 | 
					    let getClassButtonColor = function
 | 
				
			||||||
let executePlayerEvent (event : ComponentInteractionCreateEventArgs) (dispatch : PlayerData -> Async<unit>) =
 | 
					        | Network -> ButtonStyle.Danger
 | 
				
			||||||
    async {
 | 
					        | Exploit -> ButtonStyle.Success
 | 
				
			||||||
        let builder = DiscordInteractionResponseBuilder()
 | 
					        | Penetration -> ButtonStyle.Primary
 | 
				
			||||||
        builder.IsEphemeral <- true
 | 
					
 | 
				
			||||||
        builder.Content <- "Content"
 | 
					    let getClassEmbedColor = function
 | 
				
			||||||
        do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder)
 | 
					        | Network -> DiscordColor.Red
 | 
				
			||||||
            |> Async.AwaitTask
 | 
					        | Penetration -> DiscordColor.Blurple
 | 
				
			||||||
        let! playerResult = tryFindPlayer event.User.Id
 | 
					        | Exploit -> DiscordColor.Green
 | 
				
			||||||
        match playerResult with
 | 
					
 | 
				
			||||||
        | Some player -> do! dispatch player
 | 
					    let executePlayerInteraction (ctx : InteractionContext) (dispatch : PlayerData -> Async<unit>) =
 | 
				
			||||||
        | None -> do! Messaging.sendInteractionEvent event "You are currently not a hacker, first use the /redpill command to become one"
 | 
					        async {
 | 
				
			||||||
    } |> Async.StartAsTask
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
      :> Task
 | 
					            builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					            builder.Content <- "Content"
 | 
				
			||||||
 | 
					            do! ctx.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder)
 | 
				
			||||||
 | 
					                |> Async.AwaitTask
 | 
				
			||||||
 | 
					            let! playerResult = tryFindPlayer ctx.Member.Id
 | 
				
			||||||
 | 
					            match playerResult with
 | 
				
			||||||
 | 
					            | Some player -> do! dispatch player
 | 
				
			||||||
 | 
					            | None -> do! Messaging.sendSimpleResponse ctx "You are currently not a hacker, first use the /redpill command to become one"
 | 
				
			||||||
 | 
					        } |> Async.StartAsTask
 | 
				
			||||||
 | 
					          :> Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: Create an abstraction for these two helper functions
 | 
				
			||||||
 | 
					    let executePlayerEvent (event : ComponentInteractionCreateEventArgs) (dispatch : PlayerData -> Async<unit>) =
 | 
				
			||||||
 | 
					        async {
 | 
				
			||||||
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					            builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					            builder.Content <- "Content"
 | 
				
			||||||
 | 
					            do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredChannelMessageWithSource, builder)
 | 
				
			||||||
 | 
					                |> Async.AwaitTask
 | 
				
			||||||
 | 
					            let! playerResult = tryFindPlayer event.User.Id
 | 
				
			||||||
 | 
					            match playerResult with
 | 
				
			||||||
 | 
					            | Some player -> do! dispatch player
 | 
				
			||||||
 | 
					            | None -> do! Messaging.sendInteractionEvent event "You are currently not a hacker, first use the /redpill command to become one"
 | 
				
			||||||
 | 
					        } |> Async.StartAsTask
 | 
				
			||||||
 | 
					          :> Task
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module Player =
 | 
				
			||||||
 | 
					    let hacks (player : PlayerData) = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack)
 | 
				
			||||||
 | 
					    let shields (player : PlayerData) = player.Arsenal |> Array.filter (fun bi -> bi.Type = Shield)
 | 
				
			||||||
 | 
					    let attacks player =
 | 
				
			||||||
 | 
					        player.Actions
 | 
				
			||||||
 | 
					        |> Array.filter (fun act -> match act.Type with Attack _ -> true | _ -> false)
 | 
				
			||||||
 | 
					    let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let removeExpiredActions player =
 | 
				
			||||||
 | 
					        let actions =
 | 
				
			||||||
 | 
					            player.Actions
 | 
				
			||||||
 | 
					            |> Array.filter (fun (act : Action) ->
 | 
				
			||||||
 | 
					                match act.Type with
 | 
				
			||||||
 | 
					                // So the player doesnt attack the same player so many times in a row
 | 
				
			||||||
 | 
					                | Attack _ -> System.DateTime.UtcNow - act.Timestamp < Game.SameTargetAttackCooldown
 | 
				
			||||||
 | 
					                | Defense ->
 | 
				
			||||||
 | 
					                    let item = Armory.getItem act.ActionId
 | 
				
			||||||
 | 
					                    System.DateTime.UtcNow - act.Timestamp < System.TimeSpan.FromMinutes(int item.Cooldown))
 | 
				
			||||||
 | 
					        { player with Actions = actions }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -9,24 +9,22 @@ open DSharpPlus.SlashCommands
 | 
				
			|||||||
open Degenz
 | 
					open Degenz
 | 
				
			||||||
open Degenz.Messaging
 | 
					open Degenz.Messaging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let (>>=) x f = Result.bind f x
 | 
					let checkPlayerIsAttackingThemselves defender attacker =
 | 
				
			||||||
 | 
					 | 
				
			||||||
let checkIfPlayerIsAttackingThemselves defender attacker  =
 | 
					 | 
				
			||||||
    match attacker.DiscordId = defender.DiscordId with
 | 
					    match attacker.DiscordId = defender.DiscordId with
 | 
				
			||||||
    | true -> Error "You think you're clever? You can't hack yourself, pal."
 | 
					    | true -> Error "You think you're clever? You can't hack yourself, pal."
 | 
				
			||||||
    | false -> Ok attacker
 | 
					    | false -> Ok attacker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let checkForExistingTarget defenderId attacker =
 | 
					let checkAlreadyHackedTarget defenderId attacker =
 | 
				
			||||||
    attacker.Actions
 | 
					    attacker.Actions
 | 
				
			||||||
    |> Player.getAttacksFlat
 | 
					    |> Player.getAttacksFlat
 | 
				
			||||||
    |> Array.tryFind (fun (_,t,_) -> t.Id = defenderId)
 | 
					    |> Array.tryFind (fun (_,t,_) -> t.Id = defenderId)
 | 
				
			||||||
    |> function
 | 
					    |> function
 | 
				
			||||||
       | Some ( atk , target , _ ) ->
 | 
					       | Some ( atk , target , _ ) ->
 | 
				
			||||||
           let cooldown = getTimeTillCooldownFinishes Player.SameTargetAttackCooldown atk.Timestamp
 | 
					           let cooldown = getTimeTillCooldownFinishes Game.SameTargetAttackCooldown atk.Timestamp
 | 
				
			||||||
           Error $"You can only hack the same target once every {Player.SameTargetAttackCooldown.Hours} hours, wait {cooldown} to attempt another hack on {target.Name}."
 | 
					           Error $"You can only hack the same target once every {Game.SameTargetAttackCooldown.Hours} hours, wait {cooldown} to attempt another hack on {target.Name}."
 | 
				
			||||||
       | None -> Ok attacker
 | 
					       | None -> Ok attacker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let checkIfHackHasCooldown hackId attacker =
 | 
					let checkHackHasCooldown hackId attacker =
 | 
				
			||||||
    let mostRecentHackAttack =
 | 
					    let mostRecentHackAttack =
 | 
				
			||||||
        attacker.Actions
 | 
					        attacker.Actions
 | 
				
			||||||
        |> Array.tryFind (fun a -> a.ActionId = hackId)
 | 
					        |> Array.tryFind (fun a -> a.ActionId = hackId)
 | 
				
			||||||
@ -41,12 +39,12 @@ let checkIfHackHasCooldown hackId attacker =
 | 
				
			|||||||
       let item = Armory.battleItems |> Array.find (fun i -> i.Id = hackId)
 | 
					       let item = Armory.battleItems |> Array.find (fun i -> i.Id = hackId)
 | 
				
			||||||
       Error $"{item.Name} is currently on cooldown, wait {cooldown} to use it again."
 | 
					       Error $"{item.Name} is currently on cooldown, wait {cooldown} to use it again."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let checkIfHasEmptyHacks attacker =
 | 
					let checkHasEmptyHacks attacker =
 | 
				
			||||||
    match Player.hacks attacker with
 | 
					    match Player.hacks attacker with
 | 
				
			||||||
    | [||] -> Error $"You currently do not have any Hacks to steal 💰$GBT from others. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one."
 | 
					    | [||] -> Error $"You currently do not have any Hacks to steal 💰$GBT from others. Please go to the <#{GuildEnvironment.channelArmory}> and purchase one."
 | 
				
			||||||
    | _ -> Ok attacker
 | 
					    | _ -> Ok attacker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let checkIfTargetHasMoney (target : PlayerData) attacker =
 | 
					let checkTargetHasMoney (target : PlayerData) attacker =
 | 
				
			||||||
    if target.Bank < Game.HackPrize
 | 
					    if target.Bank < Game.HackPrize
 | 
				
			||||||
        then Error $"{target.Name} does not have enough 💰$GBT to steal from, the broke loser. Pick a different target."
 | 
					        then Error $"{target.Name} does not have enough 💰$GBT to steal from, the broke loser. Pick a different target."
 | 
				
			||||||
        else Ok attacker
 | 
					        else Ok attacker
 | 
				
			||||||
@ -116,24 +114,17 @@ let attack (target : DiscordUser) (ctx : InteractionContext) =
 | 
				
			|||||||
        | Some defender ->
 | 
					        | Some defender ->
 | 
				
			||||||
          do! attacker
 | 
					          do! attacker
 | 
				
			||||||
              |> Player.removeExpiredActions
 | 
					              |> Player.removeExpiredActions
 | 
				
			||||||
              |> checkForExistingTarget defender.DiscordId
 | 
					              |> checkAlreadyHackedTarget defender.DiscordId
 | 
				
			||||||
              >>= checkIfHasEmptyHacks
 | 
					              >>= checkHasEmptyHacks
 | 
				
			||||||
              >>= checkIfTargetHasMoney defender
 | 
					              >>= checkTargetHasMoney defender
 | 
				
			||||||
              >>= checkIfPlayerIsAttackingThemselves defender
 | 
					              >>= checkPlayerIsAttackingThemselves defender
 | 
				
			||||||
              |> function
 | 
					              |> function
 | 
				
			||||||
                 | Ok _ ->
 | 
					                 | Ok _ ->
 | 
				
			||||||
                     let embed = Embeds.pickHack "Attack" attacker defender
 | 
					                     let embed = Embeds.pickHack "Attack" attacker defender
 | 
				
			||||||
                     ctx.FollowUpAsync(embed)
 | 
					                     ctx.FollowUpAsync(embed)
 | 
				
			||||||
                     |> Async.AwaitTask
 | 
					                     |> Async.AwaitTask
 | 
				
			||||||
                     |> Async.Ignore
 | 
					                     |> Async.Ignore
 | 
				
			||||||
                 | Error msg ->
 | 
					                 | Error msg -> sendFollowUpMessageFromCtx ctx msg
 | 
				
			||||||
                     let builder =
 | 
					 | 
				
			||||||
                         DiscordFollowupMessageBuilder()
 | 
					 | 
				
			||||||
                             .WithContent(msg)
 | 
					 | 
				
			||||||
                             .AsEphemeral(true)
 | 
					 | 
				
			||||||
                     ctx.FollowUpAsync(builder)
 | 
					 | 
				
			||||||
                     |> Async.AwaitTask
 | 
					 | 
				
			||||||
                     |> Async.Ignore
 | 
					 | 
				
			||||||
        | None -> do! sendFollowUpMessageFromCtx ctx "Your target is not connected to the network, they must join first by using the /redpill command"
 | 
					        | None -> do! sendFollowUpMessageFromCtx ctx "Your target is not connected to the network, they must join first by using the /redpill command"
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -148,8 +139,8 @@ let handleAttack (event : ComponentInteractionCreateEventArgs) =
 | 
				
			|||||||
        | Some defender , true , true ->
 | 
					        | Some defender , true , true ->
 | 
				
			||||||
            do! attacker
 | 
					            do! attacker
 | 
				
			||||||
                |> Player.removeExpiredActions
 | 
					                |> Player.removeExpiredActions
 | 
				
			||||||
                |> checkForExistingTarget defender.DiscordId
 | 
					                |> checkAlreadyHackedTarget defender.DiscordId
 | 
				
			||||||
                >>= (checkIfHackHasCooldown (int hack))
 | 
					                >>= (checkHackHasCooldown (int hack))
 | 
				
			||||||
                |> function
 | 
					                |> function
 | 
				
			||||||
                   | Ok _ ->
 | 
					                   | Ok _ ->
 | 
				
			||||||
                       runHackerBattle defender (Armory.getItem (int hack))
 | 
					                       runHackerBattle defender (Armory.getItem (int hack))
 | 
				
			||||||
@ -175,17 +166,18 @@ let defend (ctx : InteractionContext) =
 | 
				
			|||||||
let handleDefense (event : ComponentInteractionCreateEventArgs) =
 | 
					let handleDefense (event : ComponentInteractionCreateEventArgs) =
 | 
				
			||||||
    Game.executePlayerEvent event (fun player -> async {
 | 
					    Game.executePlayerEvent event (fun player -> async {
 | 
				
			||||||
        let split = event.Id.Split("-")
 | 
					        let split = event.Id.Split("-")
 | 
				
			||||||
        let shieldId = enum<ShieldId>(int split.[1])
 | 
					        let shieldId = int split.[1]
 | 
				
			||||||
 | 
					        let shield = Armory.getItem shieldId
 | 
				
			||||||
        let updatedDefenses = player |> Player.removeExpiredActions |> Player.defenses
 | 
					        let updatedDefenses = player |> Player.removeExpiredActions |> Player.defenses
 | 
				
			||||||
        let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = int shieldId)
 | 
					        let alreadyUsedShield = updatedDefenses |> Array.exists (fun d -> d.ActionId = shieldId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        match alreadyUsedShield , updatedDefenses.Length < 2 with
 | 
					        match alreadyUsedShield , updatedDefenses.Length < 2 with
 | 
				
			||||||
        | false , true ->
 | 
					        | false , true ->
 | 
				
			||||||
            let embed = Embeds.responseCreatedShield (Armory.getItem (int shieldId))
 | 
					            let embed = Embeds.responseCreatedShield (Armory.getItem shieldId)
 | 
				
			||||||
            do! event.Interaction.CreateFollowupMessageAsync(embed)
 | 
					            do! event.Interaction.CreateFollowupMessageAsync(embed)
 | 
				
			||||||
                |> Async.AwaitTask
 | 
					                |> Async.AwaitTask
 | 
				
			||||||
                |> Async.Ignore
 | 
					                |> Async.Ignore
 | 
				
			||||||
            let defense = { ActionId = int shieldId ; Type = Defense ; Timestamp = DateTime.UtcNow }
 | 
					            let defense = { ActionId = shieldId ; Type = Defense ; Timestamp = DateTime.UtcNow }
 | 
				
			||||||
            do! DbService.updatePlayer <| { player with Actions = Array.append [| defense |] player.Actions  }
 | 
					            do! DbService.updatePlayer <| { player with Actions = Array.append [| defense |] player.Actions  }
 | 
				
			||||||
            let builder = DiscordMessageBuilder()
 | 
					            let builder = DiscordMessageBuilder()
 | 
				
			||||||
            builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
 | 
					            builder.WithContent($"{event.User.Username} has protected their system!") |> ignore
 | 
				
			||||||
@ -194,24 +186,14 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
 | 
				
			|||||||
                |> Async.AwaitTask
 | 
					                |> Async.AwaitTask
 | 
				
			||||||
                |> Async.Ignore
 | 
					                |> Async.Ignore
 | 
				
			||||||
        | _ , false ->
 | 
					        | _ , false ->
 | 
				
			||||||
            let builder = DiscordFollowupMessageBuilder()
 | 
					 | 
				
			||||||
            builder.IsEphemeral <- true
 | 
					 | 
				
			||||||
            let timestamp = updatedDefenses |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp
 | 
					            let timestamp = updatedDefenses |> Array.rev |> Array.head |> fun a -> a.Timestamp // This should be the next expiring timestamp
 | 
				
			||||||
            let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(6)) timestamp
 | 
					            let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(int shield.Cooldown)) timestamp
 | 
				
			||||||
            builder.Content <- $"You are only allowed two shields at a time. Wait {cooldown} minutes to add another shield"
 | 
					            do! sendFollowUpMessage event $"You are only allowed two shields at a time. Wait {cooldown} minutes to add another shield"
 | 
				
			||||||
            do! event.Interaction.CreateFollowupMessageAsync(builder)
 | 
					 | 
				
			||||||
                |> Async.AwaitTask
 | 
					 | 
				
			||||||
                |> Async.Ignore
 | 
					 | 
				
			||||||
            do! DbService.updatePlayer <| { player with Actions = updatedDefenses }
 | 
					            do! DbService.updatePlayer <| { player with Actions = updatedDefenses }
 | 
				
			||||||
        | true , _ ->
 | 
					        | true , _ ->
 | 
				
			||||||
            let builder = DiscordFollowupMessageBuilder()
 | 
					 | 
				
			||||||
            builder.IsEphemeral <- true
 | 
					 | 
				
			||||||
            let timestamp = updatedDefenses |> Array.find (fun d -> d.ActionId = int shieldId) |> fun a -> a.Timestamp
 | 
					            let timestamp = updatedDefenses |> Array.find (fun d -> d.ActionId = int shieldId) |> fun a -> a.Timestamp
 | 
				
			||||||
            let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromHours(6)) timestamp
 | 
					            let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(int shield.Cooldown)) timestamp
 | 
				
			||||||
            builder.Content <- $"{shieldId} shield is already in use. Wait {cooldown} minutes to use this shield again"
 | 
					            do! sendFollowUpMessage event $"{shieldId} shield is already in use. Wait {cooldown} minutes to use this shield again"
 | 
				
			||||||
            do! event.Interaction.CreateFollowupMessageAsync(builder)
 | 
					 | 
				
			||||||
                |> Async.AwaitTask
 | 
					 | 
				
			||||||
                |> Async.Ignore
 | 
					 | 
				
			||||||
            do! DbService.updatePlayer <| { player with Actions = updatedDefenses }
 | 
					            do! DbService.updatePlayer <| { player with Actions = updatedDefenses }
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@
 | 
				
			|||||||
    "Class": {
 | 
					    "Class": {
 | 
				
			||||||
      "Case": "Network"
 | 
					      "Case": "Network"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Cost": 100,
 | 
					    "Cost": 50,
 | 
				
			||||||
    "Power": 50,
 | 
					    "Power": 50,
 | 
				
			||||||
    "Cooldown": 2
 | 
					    "Cooldown": 2
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -21,7 +21,7 @@
 | 
				
			|||||||
    "Class": {
 | 
					    "Class": {
 | 
				
			||||||
      "Case": "Penetration"
 | 
					      "Case": "Penetration"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Cost": 100,
 | 
					    "Cost": 50,
 | 
				
			||||||
    "Power": 50,
 | 
					    "Power": 50,
 | 
				
			||||||
    "Cooldown": 2
 | 
					    "Cooldown": 2
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -34,7 +34,7 @@
 | 
				
			|||||||
    "Class": {
 | 
					    "Class": {
 | 
				
			||||||
      "Case": "Exploit"
 | 
					      "Case": "Exploit"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Cost": 100,
 | 
					    "Cost": 50,
 | 
				
			||||||
    "Power": 50,
 | 
					    "Power": 50,
 | 
				
			||||||
    "Cooldown": 2
 | 
					    "Cooldown": 2
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -47,20 +47,7 @@
 | 
				
			|||||||
    "Class": {
 | 
					    "Class": {
 | 
				
			||||||
      "Case": "Network"
 | 
					      "Case": "Network"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Cost": 100,
 | 
					    "Cost": 50,
 | 
				
			||||||
    "Power": 50,
 | 
					 | 
				
			||||||
    "Cooldown": 600
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  {
 | 
					 | 
				
			||||||
    "Id": 7,
 | 
					 | 
				
			||||||
    "Name": "Encryption",
 | 
					 | 
				
			||||||
    "Type": {
 | 
					 | 
				
			||||||
      "Case": "Shield"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "Class": {
 | 
					 | 
				
			||||||
      "Case": "Exploit"
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "Cost": 100,
 | 
					 | 
				
			||||||
    "Power": 50,
 | 
					    "Power": 50,
 | 
				
			||||||
    "Cooldown": 600
 | 
					    "Cooldown": 600
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -73,7 +60,20 @@
 | 
				
			|||||||
    "Class": {
 | 
					    "Class": {
 | 
				
			||||||
      "Case": "Penetration"
 | 
					      "Case": "Penetration"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "Cost": 100,
 | 
					    "Cost": 50,
 | 
				
			||||||
 | 
					    "Power": 50,
 | 
				
			||||||
 | 
					    "Cooldown": 600
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    "Id": 7,
 | 
				
			||||||
 | 
					    "Name": "Encryption",
 | 
				
			||||||
 | 
					    "Type": {
 | 
				
			||||||
 | 
					      "Case": "Shield"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Class": {
 | 
				
			||||||
 | 
					      "Case": "Exploit"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    "Cost": 50,
 | 
				
			||||||
    "Power": 50,
 | 
					    "Power": 50,
 | 
				
			||||||
    "Cooldown": 600
 | 
					    "Cooldown": 600
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										184
									
								
								Bot/Store.fs
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								Bot/Store.fs
									
									
									
									
									
								
							@ -9,90 +9,48 @@ open Degenz
 | 
				
			|||||||
open Degenz.Embeds
 | 
					open Degenz.Embeds
 | 
				
			||||||
open Degenz.Messaging
 | 
					open Degenz.Messaging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let viewStore (ctx : InteractionContext) =
 | 
					let handleResultWithResponse ctx fn (player : Result<PlayerData, string>) =
 | 
				
			||||||
    async {
 | 
					   match player with
 | 
				
			||||||
        do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, Embeds.storeListing Armory.battleItems)
 | 
					   | Ok p -> fn p
 | 
				
			||||||
            |> Async.AwaitTask
 | 
					   | Error e -> async { do! sendFollowUpMessageFromCtx ctx e }
 | 
				
			||||||
    } |> Async.StartAsTask
 | 
					 | 
				
			||||||
      :> Task
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let buyItem (ctx : InteractionContext) itemId =
 | 
					let handleResultWithResponseFromEvent event fn (player : Result<PlayerData, string>) =
 | 
				
			||||||
    Game.executePlayerInteraction ctx (fun player -> async {
 | 
					   match player with
 | 
				
			||||||
        let item = Armory.getItem itemId
 | 
					   | Ok p -> fn p
 | 
				
			||||||
        let newBalance = player.Bank - item.Cost
 | 
					   | Error e -> async { do! sendFollowUpMessage event e }
 | 
				
			||||||
        if newBalance >= 0<GBT> then
 | 
					 | 
				
			||||||
            let playerHasItem = player.Arsenal |> Array.exists (fun w -> item.Id = w.Id)
 | 
					 | 
				
			||||||
            if not playerHasItem then
 | 
					 | 
				
			||||||
                let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
 | 
					 | 
				
			||||||
                do! DbService.updatePlayer p
 | 
					 | 
				
			||||||
                do! sendFollowUpMessageFromCtx ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining"
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                do! sendFollowUpMessageFromCtx ctx $"You already own this item!"
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
                do! sendFollowUpMessageFromCtx ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT"
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let checkHasSufficientFunds (item : BattleItem) player =
 | 
				
			||||||
 | 
					    if player.Bank - item.Cost >= 0<GBT>
 | 
				
			||||||
 | 
					       then Ok player
 | 
				
			||||||
 | 
					       else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
let sell (ctx : InteractionContext) =
 | 
					let checkAlreadyOwnsItem (item : BattleItem) player =
 | 
				
			||||||
    Game.executePlayerInteraction ctx (fun player -> async {
 | 
					    if player.Arsenal |> Array.exists (fun w -> item.Id = w.Id)
 | 
				
			||||||
        let hasInventoryToSell = Array.length player.Arsenal > 0
 | 
					        then Error $"You already own {item.Name}!"
 | 
				
			||||||
        if hasInventoryToSell then
 | 
					        else Ok player
 | 
				
			||||||
            let builder = DiscordFollowupMessageBuilder()
 | 
					 | 
				
			||||||
            builder.AddEmbed (constructEmbed "Pick the item you wish to sell.") |> ignore
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Array.chunkBySize 5 player.Arsenal
 | 
					let checkSoldItemAlready (item : BattleItem) player =
 | 
				
			||||||
            |> Array.iter
 | 
					    if player.Arsenal |> Array.exists (fun w -> item.Id = w.Id)
 | 
				
			||||||
                (fun wps ->
 | 
					        then Ok player
 | 
				
			||||||
                    wps
 | 
					        else Error $"{item.Name} not found in your arsenal! Looks like you sold it already."
 | 
				
			||||||
                    |> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Primary, $"{w.Type}-{w.Id}", $"{w.Name}"))
 | 
					 | 
				
			||||||
                    |> Seq.cast<DiscordComponent>
 | 
					 | 
				
			||||||
                    |> builder.AddComponents
 | 
					 | 
				
			||||||
                    |> ignore)
 | 
					 | 
				
			||||||
            builder.AsEphemeral true |> ignore
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            do! ctx.FollowUpAsync(builder)
 | 
					let checkHasItemsInArsenal itemType player =
 | 
				
			||||||
                |> Async.AwaitTask
 | 
					    if player.Arsenal |> Array.filter (fun i -> i.Type = itemType ) |> Array.length > 0
 | 
				
			||||||
                |> Async.Ignore
 | 
					        then Ok player
 | 
				
			||||||
        else
 | 
					        else Error $"You currently have no {itemType}s in your arsenal to sell!"
 | 
				
			||||||
            do! sendFollowUpMessageFromCtx ctx "You currently have no inventory to sell"
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let handleBuyItem (ctx : InteractionContext) itemId =
 | 
					let statusFormat p =
 | 
				
			||||||
    Game.executePlayerInteraction ctx (fun player -> async {
 | 
					    $"**Hacks:** {Player.hacks p |> battleItemFormat}\n
 | 
				
			||||||
        let item = Armory.battleItems |> Array.find (fun w -> w.Id = itemId)
 | 
					**Shields:** {Player.shields p |> battleItemFormat}\n
 | 
				
			||||||
        let newBalance = player.Bank - item.Cost
 | 
					**Hack Attacks:**\n{Player.attacks p |> actionFormat}\n
 | 
				
			||||||
        if newBalance >= 0<GBT> then
 | 
					**Active Shields:**\n{Player.defenses p |> actionFormat}"
 | 
				
			||||||
            let playerHasItem = player.Arsenal |> Array.exists (fun w -> item.Id = w.Id)
 | 
					 | 
				
			||||||
            if not playerHasItem then
 | 
					 | 
				
			||||||
                let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
 | 
					 | 
				
			||||||
                do! DbService.updatePlayer p
 | 
					 | 
				
			||||||
                do! sendFollowUpMessageFromCtx ctx $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining"
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
                do! sendFollowUpMessageFromCtx ctx $"You already own this item!"
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
            do! sendFollowUpMessageFromCtx ctx $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT"
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
 | 
					let arsenal (ctx : InteractionContext) =
 | 
				
			||||||
    Game.executePlayerEvent event (fun player -> async {
 | 
					 | 
				
			||||||
        let itemId = int <| event.Id.Split("-").[1]
 | 
					 | 
				
			||||||
        let item = Armory.getItem itemId
 | 
					 | 
				
			||||||
        let updatedPlayer = { player with Bank = player.Bank + item.Cost ; Arsenal = player.Arsenal |> Array.filter (fun w -> w.Id <> itemId)}
 | 
					 | 
				
			||||||
        do! DbService.updatePlayer updatedPlayer
 | 
					 | 
				
			||||||
        let builder = DiscordFollowupMessageBuilder()
 | 
					 | 
				
			||||||
        builder.IsEphemeral <- true
 | 
					 | 
				
			||||||
        builder.Content <- $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}"
 | 
					 | 
				
			||||||
        do! event.Interaction.CreateFollowupMessageAsync(builder)
 | 
					 | 
				
			||||||
            |> Async.AwaitTask
 | 
					 | 
				
			||||||
            |> Async.Ignore
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let status (ctx : InteractionContext) =
 | 
					 | 
				
			||||||
    Game.executePlayerInteraction ctx (fun player -> async {
 | 
					    Game.executePlayerInteraction ctx (fun player -> async {
 | 
				
			||||||
        let updatedPlayer = Player.removeExpiredActions player
 | 
					        let updatedPlayer = Player.removeExpiredActions player
 | 
				
			||||||
        let builder = DiscordFollowupMessageBuilder()
 | 
					        let builder = DiscordFollowupMessageBuilder()
 | 
				
			||||||
        let embed = DiscordEmbedBuilder()
 | 
					        let embed = DiscordEmbedBuilder()
 | 
				
			||||||
        embed.AddField("Arsenal", Messaging.statusFormat updatedPlayer) |> ignore
 | 
					        embed.AddField("Arsenal", statusFormat updatedPlayer) |> ignore
 | 
				
			||||||
        builder.AddEmbed(embed) |> ignore
 | 
					        builder.AddEmbed(embed) |> ignore
 | 
				
			||||||
        builder.IsEphemeral <- true
 | 
					        builder.IsEphemeral <- true
 | 
				
			||||||
        do! ctx.FollowUpAsync(builder)
 | 
					        do! ctx.FollowUpAsync(builder)
 | 
				
			||||||
@ -101,23 +59,83 @@ let status (ctx : InteractionContext) =
 | 
				
			|||||||
        do! DbService.updatePlayer updatedPlayer
 | 
					        do! DbService.updatePlayer updatedPlayer
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let buy (ctx : InteractionContext) itemType =
 | 
				
			||||||
 | 
					    Game.executePlayerInteraction ctx (fun player -> async {
 | 
				
			||||||
 | 
					        let itemStore = Embeds.getBuyItemsEmbed itemType Armory.battleItems
 | 
				
			||||||
 | 
					        do! ctx.Interaction.CreateFollowupMessageAsync(itemStore)
 | 
				
			||||||
 | 
					            |> Async.AwaitTask
 | 
				
			||||||
 | 
					            |> Async.Ignore
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: Remove active shield when selling
 | 
				
			||||||
 | 
					let sell (ctx : InteractionContext) itemType =
 | 
				
			||||||
 | 
					    Game.executePlayerInteraction ctx (fun player -> async {
 | 
				
			||||||
 | 
					        match checkHasItemsInArsenal itemType player with
 | 
				
			||||||
 | 
					        | Ok _ ->
 | 
				
			||||||
 | 
					            let itemStore = Embeds.getSellItemsEmbed itemType player
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            do! ctx.FollowUpAsync(itemStore)
 | 
				
			||||||
 | 
					                |> Async.AwaitTask
 | 
				
			||||||
 | 
					                |> Async.Ignore
 | 
				
			||||||
 | 
					        | Error e -> do! sendFollowUpMessageFromCtx ctx e
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO: When you buy a shield, prompt the user to activate it
 | 
				
			||||||
 | 
					let handleBuyItem (event : ComponentInteractionCreateEventArgs) itemId =
 | 
				
			||||||
 | 
					    Game.executePlayerEvent event (fun player -> async {
 | 
				
			||||||
 | 
					        let item = Armory.battleItems |> Array.find (fun w -> w.Id = itemId)
 | 
				
			||||||
 | 
					        do! player
 | 
				
			||||||
 | 
					            |> checkHasSufficientFunds item
 | 
				
			||||||
 | 
					            >>= checkAlreadyOwnsItem item
 | 
				
			||||||
 | 
					            |> handleResultWithResponseFromEvent event (fun player -> async {
 | 
				
			||||||
 | 
					                let newBalance = player.Bank - item.Cost
 | 
				
			||||||
 | 
					                let p = { player with Bank = newBalance ; Arsenal = Array.append [| item |] player.Arsenal }
 | 
				
			||||||
 | 
					                do! DbService.updatePlayer p
 | 
				
			||||||
 | 
					                do! sendFollowUpMessage event $"Successfully purchased {item.Name}! You now have {newBalance} 💰$GBT remaining"
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let handleSell (event : ComponentInteractionCreateEventArgs) itemId =
 | 
				
			||||||
 | 
					    Game.executePlayerEvent event (fun player -> async {
 | 
				
			||||||
 | 
					        let item = Armory.getItem itemId
 | 
				
			||||||
 | 
					        do! player
 | 
				
			||||||
 | 
					            |> checkSoldItemAlready item
 | 
				
			||||||
 | 
					            |> handleResultWithResponseFromEvent event (fun player -> async {
 | 
				
			||||||
 | 
					                let updatedPlayer = { player with Bank = player.Bank + item.Cost ; Arsenal = player.Arsenal |> Array.filter (fun w -> w.Id <> itemId) }
 | 
				
			||||||
 | 
					                do! DbService.updatePlayer updatedPlayer
 | 
				
			||||||
 | 
					                do! sendFollowUpMessage event $"Sold {item.Type} {item.Name} for {item.Cost}! Current Balance: {updatedPlayer.Bank}"
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let handleStoreEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
 | 
				
			||||||
 | 
					    let itemId = int <| event.Id.Split("-").[1]
 | 
				
			||||||
 | 
					    match event.Id with
 | 
				
			||||||
 | 
					    | id when id.StartsWith("Buy") -> handleBuyItem event itemId
 | 
				
			||||||
 | 
					    | id when id.StartsWith("Sell") -> handleSell event itemId
 | 
				
			||||||
 | 
					    | _ ->
 | 
				
			||||||
 | 
					        task {
 | 
				
			||||||
 | 
					            let builder = DiscordInteractionResponseBuilder()
 | 
				
			||||||
 | 
					            builder.IsEphemeral <- true
 | 
				
			||||||
 | 
					            builder.Content <- $"Incorrect Action identifier {event.Id}"
 | 
				
			||||||
 | 
					            do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
 | 
				
			||||||
 | 
					                |> Async.AwaitTask
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Store() =
 | 
					type Store() =
 | 
				
			||||||
    inherit ApplicationCommandModule ()
 | 
					    inherit ApplicationCommandModule ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [<SlashCommand("armory", "View Hacks & Shields available for purchase")>]
 | 
					 | 
				
			||||||
    member _.Armory (ctx : InteractionContext) = viewStore ctx
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    [<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
 | 
					    [<SlashCommand("arsenal", "Get the Hacks and Shields you own, and which ones are active")>]
 | 
				
			||||||
    member this.Arsenal (ctx : InteractionContext) = status ctx
 | 
					    member this.Arsenal (ctx : InteractionContext) = arsenal ctx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
 | 
					    [<SlashCommand("buy-hack", "Purchase a hack attack you can use to earn GoodBoyTokenz")>]
 | 
				
			||||||
    member _.BuyHack (ctx : InteractionContext, [<Option("hack-id", "The ID of the hack you wish to purchase")>] hackId : HackId) =
 | 
					    member _.BuyHack (ctx : InteractionContext) = buy ctx ItemType.Hack
 | 
				
			||||||
        buyItem ctx (int hackId)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GoodBoyTokenz")>]
 | 
					    [<SlashCommand("buy-shield", "Purchase a hack shield so you can protect your GoodBoyTokenz")>]
 | 
				
			||||||
    member this.BuyShield (ctx : InteractionContext, [<Option("shield-id", "The ID of the shield you wish to purchase")>] shieldId : ShieldId) =
 | 
					    member this.BuyShield (ctx : InteractionContext) = buy ctx ItemType.Shield
 | 
				
			||||||
        buyItem ctx (int shieldId)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    [<SlashCommand("sell", "Sell an item in your inventory for GoodBoyTokenz")>]
 | 
					    [<SlashCommand("sell-hack", "Sell a hack for GoodBoyTokenz")>]
 | 
				
			||||||
    member this.SellItem (ctx : InteractionContext) = sell ctx
 | 
					    member this.SellHack (ctx : InteractionContext) = sell ctx ItemType.Hack
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    [<SlashCommand("sell-shield", "Sell a shield for GoodBoyTokenz")>]
 | 
				
			||||||
 | 
					    member this.SellShield (ctx : InteractionContext) = sell ctx ItemType.Shield
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,10 @@ open DSharpPlus.EventArgs
 | 
				
			|||||||
open DSharpPlus.SlashCommands
 | 
					open DSharpPlus.SlashCommands
 | 
				
			||||||
open Newtonsoft.Json
 | 
					open Newtonsoft.Json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[<Microsoft.FSharp.Core.AutoOpen>]
 | 
				
			||||||
 | 
					module ResultHelpers =
 | 
				
			||||||
 | 
					    let (>>=) x f = Result.bind f x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[<Microsoft.FSharp.Core.AutoOpen>]
 | 
					[<Microsoft.FSharp.Core.AutoOpen>]
 | 
				
			||||||
module Types =
 | 
					module Types =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -83,32 +87,6 @@ module Armory =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId)
 | 
					    let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Player =
 | 
					 | 
				
			||||||
    let SameTargetAttackCooldown = TimeSpan.FromHours(2)
 | 
					 | 
				
			||||||
    let hacks player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack)
 | 
					 | 
				
			||||||
    let shields player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Shield)
 | 
					 | 
				
			||||||
    let attacks player =
 | 
					 | 
				
			||||||
        player.Actions
 | 
					 | 
				
			||||||
        |> Array.filter (fun act -> match act.Type with Attack _ -> true | _ -> false)
 | 
					 | 
				
			||||||
    let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let removeExpiredActions player =
 | 
					 | 
				
			||||||
        let actions =
 | 
					 | 
				
			||||||
            player.Actions
 | 
					 | 
				
			||||||
            |> Array.filter (fun (act : Action) ->
 | 
					 | 
				
			||||||
                match act.Type with
 | 
					 | 
				
			||||||
                // So the player doesnt attack the same player so many times in a row
 | 
					 | 
				
			||||||
                | Attack _ -> DateTime.UtcNow - act.Timestamp < SameTargetAttackCooldown
 | 
					 | 
				
			||||||
                | Defense ->
 | 
					 | 
				
			||||||
                    let item = Armory.getItem act.ActionId
 | 
					 | 
				
			||||||
                    DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown))
 | 
					 | 
				
			||||||
        { player with Actions = actions }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let modifyBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module Messaging =
 | 
					module Messaging =
 | 
				
			||||||
    type InteractiveMessage = {
 | 
					    type InteractiveMessage = {
 | 
				
			||||||
        ButtonId : string
 | 
					        ButtonId : string
 | 
				
			||||||
@ -140,16 +118,10 @@ module Messaging =
 | 
				
			|||||||
                | Attack atk -> $"Hacked {atk.Target.Name} at {act.Timestamp.ToShortTimeString()}"
 | 
					                | Attack atk -> $"Hacked {atk.Target.Name} at {act.Timestamp.ToShortTimeString()}"
 | 
				
			||||||
                | Defense ->
 | 
					                | Defense ->
 | 
				
			||||||
                    let item = Armory.getItem act.ActionId
 | 
					                    let item = Armory.getItem act.ActionId
 | 
				
			||||||
                    let cooldown = getTimeTillCooldownFinishes (TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp
 | 
					                    let cooldown = getTimeTillCooldownFinishes (System.TimeSpan.FromMinutes(int item.Cooldown)) act.Timestamp
 | 
				
			||||||
                    $"{item.Name} active for {cooldown}")
 | 
					                    $"{item.Name} active for {cooldown}")
 | 
				
			||||||
            |> String.concat "\n"
 | 
					            |> String.concat "\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let statusFormat p =
 | 
					 | 
				
			||||||
        $"**Hacks:** {Player.hacks p |> battleItemFormat}\n
 | 
					 | 
				
			||||||
**Shields:** {Player.shields p |> battleItemFormat}\n
 | 
					 | 
				
			||||||
**Hack Attacks:**\n{Player.attacks p |> actionFormat}\n
 | 
					 | 
				
			||||||
**Active Shields:** {Player.defenses p |> actionFormat}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) =
 | 
					    let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) =
 | 
				
			||||||
        weapons
 | 
					        weapons
 | 
				
			||||||
        |> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Success, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}"))
 | 
					        |> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Success, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}"))
 | 
				
			||||||
@ -226,13 +198,3 @@ module Messaging =
 | 
				
			|||||||
            do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
 | 
					            do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let sendInteractionEventWithButton (event : ComponentInteractionCreateEventArgs) buttonId msg =
 | 
					 | 
				
			||||||
        async {
 | 
					 | 
				
			||||||
            let builder = DiscordInteractionResponseBuilder()
 | 
					 | 
				
			||||||
            let button = DiscordButtonComponent(ButtonStyle.Success, buttonId, "Got it") :> DiscordComponent
 | 
					 | 
				
			||||||
            builder.AddComponents [| button |] |> ignore
 | 
					 | 
				
			||||||
            builder.IsEphemeral <- true
 | 
					 | 
				
			||||||
            builder.Content <- msg
 | 
					 | 
				
			||||||
            do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder) |> Async.AwaitTask
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user