Store: Buy Weapons and Shields, and selling

This commit is contained in:
Joseph Ferano 2022-01-16 22:33:28 +07:00
parent 5ef3243c41
commit 07246dcdb2
10 changed files with 211 additions and 130 deletions

View File

@ -10,8 +10,11 @@ type PlayerEntry = {
Player : Player
}
let mongo = MongoClient("mongodb://localhost:27017")
let db = mongo.GetDatabase("degenz-game")
//let connString = "mongodb://localhost:27017"
let connString = "mongodb+srv://joe:mtmu3XXe3crUfXdO@stage.2hyxh.mongodb.net/degenz?retryWrites=true&w=majority"
let mongo = MongoClient(connString)
let db = mongo.GetDatabase("degenz")
let players = db.GetCollection<PlayerEntry>("players")
let tryFindPlayer (id : uint64) : Async<Player option> =
@ -38,13 +41,6 @@ let removePlayer (memberId : uint64) =
|> Async.Ignore
}
let updateAttacks (playerId : uint64) (attacks : Attack array) =
async {
let filter = Builders<PlayerEntry>.Filter.Eq((fun e -> e.Player.DiscordId), playerId)
let update = Builders<PlayerEntry>.Update.Set((fun e -> e.Player.Attacks), attacks)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}
let updatePlayer player =
async {
let filter = Builders<PlayerEntry>.Filter.Eq((fun e -> e.Player.DiscordId), player.DiscordId)

View File

@ -24,7 +24,7 @@ let attack (ctx : InteractionContext) (target : DiscordUser) =
let updatedAttacks =
attacker.Attacks
|> removeExpiredActions (TimeSpan.FromMinutes(15)) (fun (atk : Attack) -> atk.Timestamp)
do! DbService.updateAttacks attacker.DiscordId updatedAttacks
do! DbService.updatePlayer <| { attacker with Attacks = updatedAttacks }
if updatedAttacks.Length < 2 then
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick the hack you wish to use.") |> ignore

View File

@ -15,7 +15,7 @@ type HackerGame() =
member this.AttackCommand (ctx : InteractionContext, [<Option("target", "The player you want to hack")>] target : DiscordUser) =
Commands.attack ctx target
[<SlashCommand("defend", "Create a passive defense that will last a certain amount of time")>]
[<SlashCommand("defend", "Create a passive defense that will last 24 hours")>]
member this.DefendCommand (ctx : InteractionContext) = Commands.defend ctx
let config = DiscordConfiguration()

View File

@ -15,7 +15,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Commands.fs" />
<Compile Include="Program.fs" />
<Compile Include="HackerBattle.fs" />
<Content Include="paket.references" />
</ItemGroup>
<ItemGroup>

View File

@ -7,7 +7,7 @@
<RootNamespace>PlayerRegistration</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
<Compile Include="PlayerInteractions.fs" />
</ItemGroup>
<ItemGroup>
<Content Include=".dockerignore" />

View File

@ -6,8 +6,8 @@ open DSharpPlus.Entities
open DSharpPlus.SlashCommands
type ItemType =
| Weapon = 0
| Shield = 1
| Weapon
| Shield
type ActionClass =
| Network
@ -40,7 +40,6 @@ type Item = {
let weaponInventory = [| Weapon.Virus ; Weapon.Ransom ; Weapon.DDos ; Weapon.Worm ; Weapon.Crack ; Weapon.Injection |]
let shieldInventory = [| Shield.Firewall ; Shield.PortScan ; Shield.Encryption ; Shield.Cypher ; Shield.Hardening ; Shield.Sanitation |]
let getClass =
function
| 0
@ -91,7 +90,7 @@ let createSimpleResponseAsync msg (ctx: InteractionContext) =
}
let notYetAHackerMsg =
createSimpleResponseAsync "You are not currently a hacker, first use the /redpill command to become one"
createSimpleResponseAsync "You are currently not a hacker, first use the /redpill command to become one"
let hackDescription = ""

View File

@ -1,112 +0,0 @@
open System
open System.Threading.Tasks
open DSharpPlus.Entities
open DSharpPlus
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open DegenzGame.Shared
open Emzi0767.Utilities
open Newtonsoft.Json
module Commands =
let constructItemButtons playerInfo (items : 'a array) =
items
|> Seq.map (fun item -> DiscordButtonComponent(ButtonStyle.Primary, $"{playerInfo}-{item}", $"{item}"))
let viewStore (ctx : InteractionContext) =
async {
let builder = DiscordInteractionResponseBuilder()
try
let file = System.IO.File.ReadAllText("Items.json")
JsonConvert.DeserializeObject<Item array>(file)
|> Array.groupBy (fun (i : Item) -> i.ItemType)
|> Array.iter (fun ( itemType , items ) ->
let itemList = items |> Array.map (fun i -> $"{i.Name} - {i.Cost} GBT") |> String.concat "\n"
builder.AddEmbed (constructEmbed $"{itemType}:\n{itemList}") |> ignore)
with _ -> builder.Content <- "System error preparing inventory for viewing"
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let buyHack (ctx : InteractionContext) hackId =
async {
return ()
} |> Async.StartAsTask
:> Task
let buyShield (ctx : InteractionContext) shieldId =
async {
return ()
} |> Async.StartAsTask
:> Task
let sellItem (ctx : InteractionContext) =
async {
return ()
} |> Async.StartAsTask
:> Task
type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
type Store() =
inherit ApplicationCommandModule ()
[<SlashCommand("store", "View items available for purchase")>]
member _.ViewStore (ctx : InteractionContext) = Commands.viewStore ctx
[<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 : Weapon) =
Commands.buyHack ctx hackId
[<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 : Shield) =
Commands.buyShield ctx shieldId
[<SlashCommand("sell", "Sell an item in your inventory for GoodBoyTokenz")>]
member this.SellItem (ctx : InteractionContext) =
Commands.sellItem ctx
let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $""
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let config = DiscordConfiguration()
config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk"
config.TokenType <- TokenType.Bot
config.Intents <- DiscordIntents.All
//config.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
let client = new DiscordClient(config)
client.add_ComponentInteractionCreated(AsyncEventHandler(handleButtonEvent))
let slash = client.UseSlashCommands()
// My server
slash.RegisterCommands<Store>(922419263275425832uL);
// Degenz
//slash.RegisterCommands<HackerGame>(922414052708327494uL);
client.ConnectAsync ()
|> Async.AwaitTask
|> Async.RunSynchronously
Task.Delay(-1)
|> Async.AwaitTask
|> Async.RunSynchronously
client.DisconnectAsync ()
|> Async.AwaitTask
|> Async.RunSynchronously

198
Store/Store.fs Normal file
View File

@ -0,0 +1,198 @@
open System
open System.Threading.Tasks
open DSharpPlus.Entities
open DSharpPlus
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open DegenzGame
open DegenzGame.Shared
open Emzi0767.Utilities
open Newtonsoft.Json
let store =
try
let file = System.IO.File.ReadAllText("Items.json")
JsonConvert.DeserializeObject<Item array>(file)
|> Array.groupBy (fun (i : Item) -> i.ItemType)
with _ -> [||]
let storeListing =
store
|> Array.map (fun ( itemType , items ) ->
let itemList = items |> Array.map (fun i -> $"{i.Name} - {i.Cost} GBT") |> String.concat "\n"
(constructEmbed $"{itemType}:\n{itemList}"))
module Commands =
let viewStore (ctx : InteractionContext) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbeds(storeListing)
.AsEphemeral(true)
|> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
let getItems itemType = store |> Array.find (fun ( t , _ ) -> t = itemType) |> snd
let buyHack (ctx : InteractionContext) hackId =
async {
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
let weapons = getItems ItemType.Weapon
let weaponResult = weapons |> Array.tryFind (fun w -> w.Name = string hackId)
return!
match playerResult , weaponResult with
| Some player , Some item ->
async {
let newBalance = player.Bank - item.Cost
if newBalance >= 0.0f then
let playerHasItem = player.Weapons |> Array.exists (fun w -> item.Name = string w)
if not playerHasItem then
let weapon = weaponInventory |> Array.find (fun w -> item.Name = string w)
let p = { player with Bank = newBalance ; Weapons = Array.append [| weapon |] player.Weapons }
do! DbService.updatePlayer p
do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx
else
do! createSimpleResponseAsync $"You already own this item!" ctx
else
do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx
}
| None , _ -> notYetAHackerMsg ctx
| _ -> createSimpleResponseAsync "Something is wrong" ctx
} |> Async.StartAsTask
:> Task
let buyShield (ctx : InteractionContext) shieldId =
async {
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
let shieldResult =
getItems ItemType.Shield
|> Array.tryFind (fun w -> w.Name = string shieldId)
return!
match playerResult , shieldResult with
| Some player , Some item ->
async {
let newBalance = player.Bank - item.Cost
if newBalance >= 0.0f then
let playerHasItem = player.Shields |> Array.exists (fun w -> item.Name = string w)
if not playerHasItem then
let shield = shieldInventory |> Array.find (fun w -> item.Name = string w)
let p = { player with Bank = newBalance ; Shields = Array.append [| shield |] player.Shields }
do! DbService.updatePlayer p
do! createSimpleResponseAsync $"Successfully purchased {item.Name}! You now have {newBalance} remaining" ctx
else
do! createSimpleResponseAsync $"You already own this item!" ctx
else
do! createSimpleResponseAsync $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" ctx
}
| None , _ -> notYetAHackerMsg ctx
| _ -> createSimpleResponseAsync "Something is wrong" ctx
} |> Async.StartAsTask
:> Task
let constructItemButtons playerInfo itemType (items : 'a array) =
items
|> Seq.map (fun item -> DiscordButtonComponent(ButtonStyle.Primary, $"{playerInfo}-{itemType}-{item}", $"{item}"))
let sellItem (ctx : InteractionContext) =
async {
let! playerResult = DbService.tryFindPlayer ctx.Member.Id
match playerResult with
| Some player ->
let hasInventoryToSell = Array.length player.Weapons + Array.length player.Shields > 0
if hasInventoryToSell then
let builder = DiscordInteractionResponseBuilder()
builder.AddEmbed (constructEmbed "Pick the item you wish to sell.") |> ignore
Array.chunkBySize 3 player.Weapons
|> Array.iter
(fun wps ->
wps
|> Array.map (fun w ->
DiscordButtonComponent(ButtonStyle.Primary, $"{player.DiscordId}-Weapon-{w}", $"{w}"))
|> Seq.cast<DiscordComponent>
|> builder.AddComponents
|> ignore)
Array.chunkBySize 3 player.Shields
|> Array.iter
(fun shs ->
shs
|> Array.map (fun s ->
DiscordButtonComponent(ButtonStyle.Primary, $"{player.DiscordId}-Shield-{s}", $"{s}"))
|> Seq.cast<DiscordComponent>
|> builder.AddComponents
|> ignore)
builder.AsEphemeral true |> ignore
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
else
do! createSimpleResponseAsync "You currently have no inventory to sell" ctx
| None -> do! notYetAHackerMsg ctx
return ()
} |> Async.StartAsTask
:> Task
let handleSellButtonEvents (_ : DiscordClient) (event : ComponentInteractionCreateEventArgs) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- $"{event.Id}"
do! event.Interaction.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
} |> Async.StartAsTask
:> Task
type EmptyGlobalCommandToAvoidFamousDuplicateSlashCommandsBug() = inherit ApplicationCommandModule ()
type Store() =
inherit ApplicationCommandModule ()
[<SlashCommand("store", "View items available for purchase")>]
member _.ViewStore (ctx : InteractionContext) = Commands.viewStore ctx
[<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 : Weapon) =
Commands.buyHack ctx hackId
[<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 : Shield) =
Commands.buyShield ctx shieldId
[<SlashCommand("sell", "Sell an item in your inventory for GoodBoyTokenz")>]
member this.SellItem (ctx : InteractionContext) =
Commands.sellItem ctx
let config = DiscordConfiguration()
config.Token <- "OTIyNDIyMDIyMTI1MDEwOTU1.YcBOcw.JxfW1CSIwEO7j6RbRFCnPZ-HoTk"
config.TokenType <- TokenType.Bot
config.Intents <- DiscordIntents.All
//config.MinimumLogLevel <- Microsoft.Extensions.Logging.LogLevel.Trace
let client = new DiscordClient(config)
client.add_ComponentInteractionCreated(AsyncEventHandler(handleSellButtonEvents))
let slash = client.UseSlashCommands()
// My server
slash.RegisterCommands<Store>(922419263275425832uL);
// Degenz
//slash.RegisterCommands<HackerGame>(922414052708327494uL);
client.ConnectAsync ()
|> Async.AwaitTask
|> Async.RunSynchronously
Task.Delay(-1)
|> Async.AwaitTask
|> Async.RunSynchronously
client.DisconnectAsync ()
|> Async.AwaitTask
|> Async.RunSynchronously

View File

@ -6,7 +6,7 @@
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
<Compile Include="Store.fs" />
</ItemGroup>
<ItemGroup>
<Content Include=".dockerignore" />