Fixing some trainer bugs, mapping value cause Mongo hates F#

This commit is contained in:
Joseph Ferano 2022-01-31 02:34:56 +07:00
parent 7c8a460d5b
commit 7b48ff0a9b
9 changed files with 161 additions and 125 deletions

View File

@ -26,7 +26,6 @@ for conf in configs do
conf.TokenType <- TokenType.Bot
conf.Intents <- DiscordIntents.All
let guild = GuildEnvironment.guildId
playerInteractionsConfig.Token <- GuildEnvironment.tokenPlayerInteractions

View File

@ -84,7 +84,7 @@ let responseSuccessfulHackTrainer defenderName (hack : BattleItem) prize =
embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id))
DiscordFollowupMessageBuilder()
.WithContent($"Successfully hacked {defenderName} using {hack}! You just won {prize} GoodBoyTokenz!")
.WithContent($"Successfully hacked {defenderName} using {hack.Name}! You just won {prize} GoodBoyTokenz!")
.AddEmbed(embed.Build())
.AsEphemeral(true)

View File

@ -8,6 +8,7 @@ open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz
open Degenz.Shared
open Degenz.Store
let getTimeTillCooldownFinishes (timespan : TimeSpan) timestamp =
let timeRemaining = timespan - (DateTime.UtcNow - timestamp)
@ -42,7 +43,7 @@ let checkIfHackHasCooldown hackId attacker =
|> Array.tryFind (fun a -> a.ActionId = hackId)
|> function
| Some a -> a.Timestamp
| None -> DateTime.MinValue
| None -> DateTime.MinValue;
if DateTime.UtcNow - mostRecentHackAttack > TimeSpan.FromMinutes(5) then
Ok attacker
else
@ -56,7 +57,7 @@ let checkIfInventoryIsEmpty attacker =
| _ -> Ok attacker
let calculateDamage (hack : BattleItem) (shield : BattleItem) =
if hack.Power > shield.Power
if hack.Class = shield.Class
then Strong
else Weak
@ -71,7 +72,7 @@ let updateCombatants attacker defender hack prize =
let updatePlayer amount attack p =
{ p with Actions = Array.append [| attack |] p.Actions ; Bank = max (p.Bank + amount) 0<GBT> }
let target = { Id = defender.DiscordId ; Name = defender.Name }
let attack = { ActionId = int hack ; Type = Attack ( target , prize > 0<GBT> ) ; Timestamp = DateTime.UtcNow }
let attack = { ActionId = int hack ; Type = Attack { Target = target ; Result = prize > 0<GBT> } ; Timestamp = DateTime.UtcNow }
[ DbService.updatePlayer <| updatePlayer prize attack attacker
DbService.updatePlayer <| modifyPlayerBank defender -prize ]
@ -179,7 +180,7 @@ let defend (ctx : InteractionContext) =
let! player = DbService.tryFindPlayer ctx.Member.Id
match player with
| Some player ->
if Player.defenses player |> Array.length > 0 then
if Player.shields player |> Array.length > 0 then
let embed = Embeds.pickDefense "Defend" player
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, embed)
|> Async.AwaitTask

View File

@ -32,7 +32,7 @@
"Case": "Hack"
},
"Class": {
"Case": "Network"
"Case": "Exploit"
},
"Cost": 100,
"Power": 50,
@ -45,7 +45,7 @@
"Case": "Hack"
},
"Class": {
"Case": "Network"
"Case": "Exploit"
},
"Cost": 100,
"Power": 50,
@ -58,7 +58,7 @@
"Case": "Hack"
},
"Class": {
"Case": "Network"
"Case": "Penetration"
},
"Cost": 100,
"Power": 50,
@ -71,7 +71,7 @@
"Case": "Hack"
},
"Class": {
"Case": "Network"
"Case": "Penetration"
},
"Cost": 100,
"Power": 50,
@ -110,7 +110,7 @@
"Case": "Shield"
},
"Class": {
"Case": "Network"
"Case": "Exploit"
},
"Cost": 100,
"Power": 50,
@ -123,7 +123,7 @@
"Case": "Shield"
},
"Class": {
"Case": "Network"
"Case": "Penetration"
},
"Cost": 100,
"Power": 50,
@ -136,7 +136,7 @@
"Case": "Shield"
},
"Class": {
"Case": "Network"
"Case": "Penetration"
},
"Cost": 100,
"Power": 50,
@ -149,7 +149,7 @@
"Case": "Shield"
},
"Class": {
"Case": "Network"
"Case": "Exploit"
},
"Cost": 100,
"Power": 50,

View File

@ -1,77 +1,48 @@
module Degenz.PlayerInteractions
open System
open System.Threading.Tasks
open AsciiTableFormatter
open DSharpPlus.Entities
open DSharpPlus
open DSharpPlus.SlashCommands
open Degenz
open Degenz.Store
open Degenz.Shared
module Commands =
// let newPlayer nickname (membr : uint64) =
// let h1 = [| Weapon.Virus ; Weapon.Ransom |]
// let h2 = [| Weapon.DDos ; Weapon.Worm |]
// let h3 = [| Weapon.Crack ; Weapon.Injection |]
// let d1 = [| Shield.Firewall ; Shield.PortScan |]
// let d2 = [| Shield.Encryption ; Shield.Cypher |]
// let d3 = [| Shield.Hardening ; Shield.Sanitation |]
let newPlayer nickname (membr : uint64) =
// let rand = System.Random(System.Guid.NewGuid().GetHashCode())
// let getRandom (actions : 'a array) = actions.[rand.Next(0, max 0 (actions.Length - 1))]
//
// let weapons = [| getRandom hackInventory |]
// let shields = [| getRandom shieldInventory |]
//
// { DiscordId = membr
// Name = nickname
// Weapons = weapons
// Shields = shields
// Attacks = [||]
// Defenses = [||]
// Bank = 15 }
let rand = System.Random(System.Guid.NewGuid().GetHashCode())
let randHack = rand.Next(0, 3)
let randShield = rand.Next(3, 6)
let hack = armoury |> Array.find (fun i -> i.Id = randHack)
let shield = armoury |> Array.find (fun i -> i.Id = randShield)
// let addHackerRole (ctx : InteractionContext) =
// async {
// let! player = DbService.tryFindPlayer ctx.Member.Id
// let! newPlayer =
// match player with
// | Some _ -> async.Return false
// | None ->
// async {
// do! newPlayer ctx.Member.DisplayName ctx.Member.Id
// |> DbService.insertNewPlayer
//
// for role in ctx.Guild.Roles do
// if role.Value.Name = "Hacker" then
// do! ctx.Member.GrantRoleAsync(role.Value)
// |> Async.AwaitTask
// return true
// }
// if newPlayer then
// do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
// |> Async.AwaitTask
// else
// do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
// |> Async.AwaitTask
//
// } |> Async.StartAsTask
// :> Task
{ DiscordId = membr
Name = nickname
Arsenal = [| hack ; shield |]
Actions = [||]
Bank = 100<GBT> }
// let removeHackerRole (ctx : InteractionContext) =
// async {
// for role in ctx.Member.Roles do
// if role.Name = "Hacker" then
// do! ctx.Member.RevokeRoleAsync(role)
// |> Async.AwaitTask
let addHackerRole (ctx : InteractionContext) =
async {
let! player = DbService.tryFindPlayer ctx.Member.Id
let! newPlayer =
match player with
| Some _ -> async.Return false
| None ->
async {
do! newPlayer ctx.Member.DisplayName ctx.Member.Id
|> DbService.insertNewPlayer
return true
}
if newPlayer then
do! ctx.CreateResponseAsync("You are now an elite haxxor", true)
|> Async.AwaitTask
else
do! ctx.CreateResponseAsync("Already registered as an elite haxxor", true)
|> Async.AwaitTask
// do! DbService.removePlayer ctx.Member.Id
// do! ctx.CreateResponseAsync("You are now lame", true)
// |> Async.AwaitTask
// } |> Async.StartAsTask
// :> Task
} |> Async.StartAsTask
:> Task
[<CLIMutable>]
type LeaderboardEntry = {
@ -103,18 +74,17 @@ module Commands =
let status (ctx : InteractionContext) =
async {
let! player = DbService.tryFindPlayer ctx.Member.Id
match player with
| Some p ->
// let updatedAttacks = p.Attacks |> removeExpiredActions (TimeSpan.FromHours(24)) (fun (atk : Attack) -> atk.Timestamp)
// let updatedDefenses = p.Defenses |> removeExpiredActions (TimeSpan.FromHours(6)) (fun (p : Defense) -> p.Timestamp)
// let updatedPlayer = { p with Attacks = updatedAttacks ; Defenses = updatedDefenses }
// do! DbService.updatePlayer updatedPlayer
let! maybePlayer = DbService.tryFindPlayer ctx.Member.Id
match maybePlayer with
| Some player ->
let updatedActions = removeExpiredActions player.Actions
let updatedPlayer = { player with Actions = updatedActions }
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
// builder.Content <- statusFormat updatedPlayer
builder.Content <- statusFormat updatedPlayer
do! ctx.CreateResponseAsync(InteractionResponseType.ChannelMessageWithSource, builder)
|> Async.AwaitTask
do! DbService.updatePlayer updatedPlayer
| None -> do! notYetAHackerMsg ctx
} |> Async.StartAsTask
:> Task
@ -122,11 +92,8 @@ module Commands =
type PlayerInteractions() =
inherit ApplicationCommandModule ()
// [<SlashCommand("redpill", "Take the redpill and become a hacker")>]
// member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx
// [<SlashCommand("bluepill", "Take the bluepill and become lame")>]
// member _.RemoveHackerRole (ctx : InteractionContext) = Commands.removeHackerRole ctx
[<SlashCommand("redpill", "Take the redpill and become a hacker")>]
member _.AddHackerRole (ctx : InteractionContext) = Commands.addHackerRole ctx
[<SlashCommand("status", "Get your current status like bank account, and active hacks and defenses")>]
member this.Status (ctx : InteractionContext) = Commands.status ctx

View File

@ -1,5 +1,6 @@
module Degenz.Store
open System
open System.Threading.Tasks
open DSharpPlus.Entities
open DSharpPlus
@ -8,6 +9,15 @@ open DSharpPlus.SlashCommands
open Degenz
open Degenz.Embeds
open Degenz.Shared
open Newtonsoft.Json
let getItemFromArmoury id = armoury |> Array.find (fun w -> w.Id = id)
let removeExpiredActions actions =
actions
|> Array.filter (fun (act : Action) ->
let item = armoury |> Array.find (fun w -> w.Id = act.ActionId)
DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown))
let viewStore (ctx : InteractionContext) =
async {

View File

@ -6,6 +6,7 @@ open DSharpPlus.Entities
open DSharpPlus.EventArgs
open DSharpPlus.SlashCommands
open Degenz.Shared
open Degenz.Store
let defaultHack = armoury |> Array.find (fun i -> i.Id = int HackId.Virus)
let defaultShield = armoury |> Array.find (fun i -> i.Id = int ShieldId.Firewall)
@ -54,7 +55,7 @@ let handleTrainerStep2 (event : ComponentInteractionCreateEventArgs) =
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield
let weaponName = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
do! Message.sendInteractionEvent event
($"First things first, let's get your system protected. Let's enable a shield to protect you from potential hackers. "
+ $"You currently have {weaponName} in your arsenal. To enable it and protect your system, you can use the `/defend` slash command to choose a shield."
@ -92,11 +93,11 @@ let handleDefense (event : ComponentInteractionCreateEventArgs) =
async {
do! event.Interaction.CreateResponseAsync(InteractionResponseType.DeferredMessageUpdate)
|> Async.AwaitTask
let! result = DbService.tryFindPlayer event.User.Id
match result with
let! maybePlayer = DbService.tryFindPlayer event.User.Id
match maybePlayer with
| Some player ->
let prize = 0.223f
let shield = player.Arsenal |> Array.tryHead |> Option.defaultValue defaultShield
let shield = Player.shields player |> Array.tryHead |> Option.defaultValue defaultShield
let embed = Embeds.responseCreatedShieldTrainer shield
do! event.Interaction.CreateFollowupMessageAsync(embed) |> Async.AwaitTask |> Async.Ignore
do! Async.Sleep 2000
@ -116,7 +117,7 @@ let handleTrainerStep4 (event : ComponentInteractionCreateEventArgs) =
let! result = DbService.tryFindPlayer event.User.Id
match result with
| Some player ->
let weaponName = player.Arsenal |> Array.tryHead |> Option.defaultValue defaultHack
let weaponName = Player.hacks player |> Array.tryHead |> Option.defaultValue defaultShield |> fun w -> w.Name
do! Message.sendInteractionEvent event
($"Next why don't you try hacking me. You currently have {weaponName} equipped. To hack me and get some money, "
+ $" you can use the '/hack' slash command and select a user to hack, then choose the hack attack you wish to use."

View File

@ -7,6 +7,68 @@ open MongoDB.Bson
open MongoDB.Bson.Serialization
open MongoDB.Driver
[<CLIMutable>]
type AttackAction =
{ ActionId : int
Result : bool
Target : DiscordPlayer
Timestamp : DateTime }
[<CLIMutable>]
type DefenseAction =
{ ActionId : int
Timestamp : DateTime }
[<CLIMutable>]
type PlayerEntry =
{ DiscordId : uint64
Name : string
Arsenal : int array
Attacks : AttackAction array
Defenses : DefenseAction array
Bank : int }
let private actionToAttack (action : Action) (hack : AttackResult) =
{ ActionId = action.ActionId
Result = hack.Result
Target = hack.Target
Timestamp = action.Timestamp }
let private actionToDefense (action : Action) =
{ ActionId = action.ActionId
Timestamp = action.Timestamp }
let private attackToAction (attack : AttackAction) =
{ ActionId = attack.ActionId
Type = Attack { Target = attack.Target ; Result = attack.Result }
Timestamp = attack.Timestamp }
let private defenseToAction (action : DefenseAction) =
{ ActionId = action.ActionId
Type = Defense
Timestamp = action.Timestamp }
let private playerMap (player : PlayerData) = {
DiscordId = player.DiscordId
Name = player.Name
Arsenal = player.Arsenal |> Array.map (fun w -> w.Id)
Attacks = [||]
Defenses = [||]
Bank = int player.Bank
}
let private mapBack (player : PlayerEntry) : PlayerData = {
DiscordId = player.DiscordId
Name = player.Name
Arsenal = player.Arsenal |> Array.map (fun w -> armoury |> Array.find (fun w' -> w = w'.Id))
Actions =
let atks = player.Attacks |> Array.map attackToAction
let dfns = player.Defenses |> Array.map defenseToAction
Array.append atks dfns
Bank = player.Bank * 1<GBT>
}
let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING"))
let db = mongo.GetDatabase("degenz")
let players = db.GetCollection<BsonDocument>("players")
@ -20,30 +82,32 @@ let tryFindPlayer (id : uint64) =
| p -> return p
.GetValue("Player")
.ToBsonDocument()
|> BsonSerializer.Deserialize<PlayerData>
|> BsonSerializer.Deserialize<PlayerEntry>
|> mapBack
|> Some
}
let insertNewPlayer (player : PlayerData) =
async {
let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ]
let p = playerMap player
let dict = [ KeyValuePair("Player" , p.ToBsonDocument() :> Object) ]
do! BsonDocument(dict)
|> players.InsertOneAsync
|> Async.AwaitTask
}
let deletePlayer (player : PlayerData) =
async {
let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ]
do! BsonDocument(dict)
|> players.InsertOneAsync
|> Async.AwaitTask
}
let updatePlayer player =
//let deletePlayer (player : PlayerData) =
// async {
// let dict = [ KeyValuePair("Player" , player.ToBsonDocument() :> Object) ]
// do! BsonDocument(dict)
// |> players.InsertOneAsync
// |> Async.AwaitTask
// }
//
let updatePlayer (player : PlayerData) =
async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", player.DiscordId)
let update = Builders<BsonDocument>.Update.Set("Player", player)
let update = Builders<BsonDocument>.Update.Set("Player", playerMap player)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}

View File

@ -55,15 +55,17 @@ type HackResult =
[<CLIMutable>]
type DiscordPlayer = { Id: uint64; Name: string }
type Attack = {
[<CLIMutable>]
type AttackResult = {
Result : bool
Target : DiscordPlayer
}
type ActionType =
| Attack of target : DiscordPlayer * result : bool
| Attack of AttackResult
| Defense
[<CLIMutable>]
type Action =
{ ActionId : int
Type : ActionType
@ -79,13 +81,13 @@ type PlayerData =
module Player =
let hacks player = player.Arsenal |> Array.filter (fun bi -> bi.Type = Hack)
let shields 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.choose (fun act -> match act.Type with Attack (t,r) -> Some (act,t,r) | Defense -> None)
|> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None)
let defenses player = player.Actions |> Array.filter (fun act -> match act.Type with Defense _ -> true | _ -> false)
let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack (t,r) -> Some (act,t,r) | Defense -> None)
let getAttacksFlat actions = actions |> Array.choose (fun act -> match act.Type with Attack ar -> Some (act,ar.Target,ar.Result) | Defense -> None)
let createSimpleResponseAsync msg (ctx: InteractionContext) =
async {
@ -105,29 +107,21 @@ let hackDescription = ""
let statusFormat p =
$"Hacks: {Player.hacks p |> Array.toList}
Shields: {Player.defenses p |> Array.toList}
Shields: {Player.shields p |> Array.toList}
Hack Attacks: {Player.attacks p |> Array.toList}
Active Defenses: {Player.defenses p |> Array.toList}
Bank: {p.Bank}"
let constructButtons (actionType: string) (playerInfo: string) (weapons: BattleItem array) =
weapons
|> Array.map (fun w -> DiscordButtonComponent(ButtonStyle.Primary, $"{actionType}-{w.Id}-{playerInfo}", $"{w.Name}"))
let modifyPlayerBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
let armoury =
let file = System.IO.File.ReadAllText("Items.json")
JsonConvert.DeserializeObject<BattleItem array>(file)
let getItemFromArmoury id = armoury |> Array.find (fun w -> w.Id = id)
let constructButtons (actionType: string) (playerInfo: string) (weapons: 'a array) =
weapons
|> Seq.map (fun hack -> DiscordButtonComponent(ButtonStyle.Primary, $"{actionType}-{hack}-{playerInfo}", $"{hack}"))
let removeExpiredActions actions =
actions
|> Array.filter (fun (act : Action) ->
let item = armoury |> Array.find (fun w -> w.Id = act.ActionId)
DateTime.UtcNow - act.Timestamp < TimeSpan.FromMinutes(int item.Cooldown))
let modifyPlayerBank player amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
module Message =
let sendFollowUpMessage (event : ComponentInteractionCreateEventArgs) msg =
async {