Model for the new RPG mechanics

This commit is contained in:
Joseph Ferano 2022-02-17 10:17:55 +07:00
parent c7ee6bfa6a
commit 881fd12aa9
8 changed files with 63 additions and 135 deletions

View File

@ -77,7 +77,7 @@ let pickHack actionId attacker defender isTrainer =
.AddEmbed(embed.Build()) .AddEmbed(embed.Build())
.AsEphemeral true .AsEphemeral true
let responseSuccessfulHack earnedMoney (targetId : uint64) (hack : BattleItem) = let responseSuccessfulHack earnedMoney (targetId : uint64) (hack : Item) =
let embed = DiscordEmbedBuilder() let embed = DiscordEmbedBuilder()
embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id)) embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id))
embed.Title <- "Hack Attack" embed.Title <- "Hack Attack"
@ -88,7 +88,7 @@ let responseSuccessfulHack earnedMoney (targetId : uint64) (hack : BattleItem) =
.AddEmbed(embed.Build()) .AddEmbed(embed.Build())
.AsEphemeral(true) .AsEphemeral(true)
let responseCreatedShield (shield : BattleItem) = let responseCreatedShield (shield : Item) =
let embed = DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id))) let embed = DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id)))
embed.Title <- "Mounted Shield" embed.Title <- "Mounted Shield"
embed.Description <- $"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours" embed.Description <- $"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours"
@ -101,20 +101,20 @@ let eventSuccessfulHack (ctx : IDiscordContext) target prize =
DiscordMessageBuilder() DiscordMessageBuilder()
.WithContent($"{ctx.GetDiscordMember().Username} successfully hacked <@{target.DiscordId}> for a total of {prize} GoodBoyTokenz") .WithContent($"{ctx.GetDiscordMember().Username} successfully hacked <@{target.DiscordId}> for a total of {prize} GoodBoyTokenz")
let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : BattleItem array) = let getBuyItemsEmbed (player : PlayerData) (itemType : ItemType) (store : Item array) =
let embeds , buttons = let embeds , buttons =
store store
|> Array.filter (fun i -> i.Type = itemType) |> Array.filter (fun i -> i.Type = itemType)
|> Array.map (fun item -> |> Array.map (fun item ->
let embed = DiscordEmbedBuilder() let embed = DiscordEmbedBuilder()
match item.Type with match item.Type with
| Hack -> | ItemType.Hack ->
embed embed
.AddField($"Hacking Power |", string item.Power, true) .AddField($"Hacking Power |", string item.Power, true)
.AddField("Cooldown |", $"{TimeSpan.FromMinutes(int item.Cooldown).Minutes} minutes", true) .AddField("Cooldown |", $"{TimeSpan.FromMinutes(int item.Cooldown).Minutes} minutes", true)
.WithThumbnail(getHackIcon (enum<HackId>(item.Id))) .WithThumbnail(getHackIcon (enum<HackId>(item.Id)))
|> ignore |> ignore
| Shield -> | _ ->
embed embed
.AddField($"Defensive Strength |", string item.Power, true) .AddField($"Defensive Strength |", string item.Power, true)
.AddField("Active For |", $"{TimeSpan.FromMinutes(int item.Cooldown).Hours} hours", true) .AddField("Active For |", $"{TimeSpan.FromMinutes(int item.Cooldown).Hours} hours", true)
@ -143,8 +143,8 @@ let getSellItemsEmbed (itemType : ItemType) (player : PlayerData) =
|> Array.map (fun item -> |> Array.map (fun item ->
let embed = DiscordEmbedBuilder() let embed = DiscordEmbedBuilder()
match item.Type with match item.Type with
| Hack -> embed.WithThumbnail(getHackIcon (enum<HackId>(item.Id))) |> ignore | ItemType.Hack -> embed.WithThumbnail(getHackIcon (enum<HackId>(item.Id))) |> ignore
| Shield -> embed.WithThumbnail(getShieldIcon (enum<ShieldId>(item.Id))) |> ignore | _ -> embed.WithThumbnail(getShieldIcon (enum<ShieldId>(item.Id))) |> ignore
embed embed
.AddField("Sell For 💰", $"{item.Price} $GBT", true) .AddField("Sell For 💰", $"{item.Price} $GBT", true)
.WithTitle($"{item.Name}") .WithTitle($"{item.Name}")

View File

@ -98,7 +98,7 @@ module Player =
let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0<GBT> } let modifyBank (player : PlayerData) amount = { player with Bank = max (player.Bank + amount) 0<GBT> }
module Arsenal = module Arsenal =
let battleItemFormat (items : BattleItem array) = let battleItemFormat (items : Item array) =
match items with match items with
| [||] -> "None" | [||] -> "None"
| _ -> items |> Array.toList |> List.map (fun i -> i.Name) |> String.concat ", " | _ -> items |> Array.toList |> List.map (fun i -> i.Name) |> String.concat ", "

View File

@ -63,7 +63,8 @@ let checkPlayerHasShieldSlotsAvailable shield player =
Error $"You are only allowed two shields at a time. Wait {cooldown} to add another shield" Error $"You are only allowed two shields at a time. Wait {cooldown} to add another shield"
| false -> Ok updatedPlayer | false -> Ok updatedPlayer
let calculateDamage (hack : BattleItem) (shield : BattleItem) = // TODO H: Need to update the new hacker game mechanics
let calculateDamage (hack : Item) (shield : Item) =
if hack.Power < shield.Power if hack.Power < shield.Power
then Weak then Weak
else Strong else Strong

View File

@ -2,12 +2,7 @@
{ {
"Id": 0, "Id": 0,
"Name": "Virus", "Name": "Virus",
"Type": { "Type": 0,
"Case": "Hack"
},
"Class": {
"Case": "Network"
},
"Cost": 50, "Cost": 50,
"Power": 50, "Power": 50,
"Cooldown": 2 "Cooldown": 2
@ -15,12 +10,7 @@
{ {
"Id": 1, "Id": 1,
"Name": "RemoteAccess", "Name": "RemoteAccess",
"Type": { "Type": 0,
"Case": "Hack"
},
"Class": {
"Case": "Penetration"
},
"Cost": 50, "Cost": 50,
"Power": 50, "Power": 50,
"Cooldown": 2 "Cooldown": 2
@ -28,12 +18,7 @@
{ {
"Id": 2, "Id": 2,
"Name": "Worm", "Name": "Worm",
"Type": { "Type": 0,
"Case": "Hack"
},
"Class": {
"Case": "Exploit"
},
"Cost": 50, "Cost": 50,
"Power": 50, "Power": 50,
"Cooldown": 2 "Cooldown": 2
@ -41,12 +26,7 @@
{ {
"Id": 6, "Id": 6,
"Name": "Firewall", "Name": "Firewall",
"Type": { "Type": 1,
"Case": "Shield"
},
"Class": {
"Case": "Network"
},
"Cost": 50, "Cost": 50,
"Power": 50, "Power": 50,
"Cooldown": 600 "Cooldown": 600
@ -54,12 +34,7 @@
{ {
"Id": 8, "Id": 8,
"Name": "Cypher", "Name": "Cypher",
"Type": { "Type": 1,
"Case": "Shield"
},
"Class": {
"Case": "Penetration"
},
"Cost": 50, "Cost": 50,
"Power": 50, "Power": 50,
"Cooldown": 600 "Cooldown": 600
@ -67,12 +42,7 @@
{ {
"Id": 7, "Id": 7,
"Name": "Encryption", "Name": "Encryption",
"Type": { "Type": 1,
"Case": "Shield"
},
"Class": {
"Case": "Exploit"
},
"Cost": 50, "Cost": 50,
"Power": 50, "Power": 50,
"Cooldown": 600 "Cooldown": 600

View File

@ -8,17 +8,17 @@ open DSharpPlus.SlashCommands
open Degenz open Degenz
open Degenz.Messaging open Degenz.Messaging
let checkHasSufficientFunds (item : BattleItem) player = let checkHasSufficientFunds (item : Item) player =
if player.Bank - item.Price >= 0<GBT> if player.Bank - item.Price >= 0<GBT>
then Ok player then Ok player
else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT" else Error $"You do not have sufficient funds to buy this item! Current balance: {player.Bank} GBT"
let checkAlreadyOwnsItem (item : BattleItem) player = let checkAlreadyOwnsItem (item : Item) player =
if player.Inventory |> Array.exists (fun w -> item.Id = w.Id) if player.Inventory |> Array.exists (fun w -> item.Id = w.Id)
then Error $"You already own {item.Name}!" then Error $"You already own {item.Name}!"
else Ok player else Ok player
let checkSoldItemAlready (item : BattleItem) player = let checkSoldItemAlready (item : Item) player =
if player.Inventory |> Array.exists (fun w -> item.Id = w.Id) if player.Inventory |> Array.exists (fun w -> item.Id = w.Id)
then Ok player then Ok player
else Error $"{item.Name} not found in your arsenal! Looks like you sold it already." else Error $"{item.Name} not found in your arsenal! Looks like you sold it already."

View File

@ -168,7 +168,7 @@ let handleAttack (ctx : IDiscordContext) =
do! Async.Sleep 1000 do! Async.Sleep 1000
let updatedPlayer = { let updatedPlayer = {
player with player with
Bank = player.Bank + hackMoney + shieldMoney Bank = player.Bank + 100<GBT>
Events = [ Events = [
{ PlayerEvent.Timestamp = System.DateTime.UtcNow { PlayerEvent.Timestamp = System.DateTime.UtcNow
PlayerEvent.Adversary = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" } PlayerEvent.Adversary = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }

View File

@ -1,50 +1,46 @@
module Degenz.DbService module Degenz.DbService
open Degenz.Types
open System open System
open System.Collections.Generic
open MongoDB.Bson open MongoDB.Bson
open MongoDB.Bson.Serialization open MongoDB.Bson.Serialization
open MongoDB.Driver open MongoDB.Driver
open Degenz.Types
[<CLIMutable>] let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING"))
type PlayerEntry = let db = mongo.GetDatabase("degenz")
{ DiscordId : uint64 let players = db.GetCollection<BsonDocument>("players")
Name : string
XP : int
Bank : int }
let private playerMap (player : PlayerData) = {
DiscordId = player.DiscordId
Name = player.Name
XP = player.XP
Bank = int player.Bank
}
let tryWithDefault (bson : BsonDocument) field (defaultValue : 'a) (map : BsonValue -> 'a) = let tryWithDefault (bson : BsonDocument) field (defaultValue : 'a) (map : BsonValue -> 'a) =
let result , bval = bson.TryGetValue(field) try
if result then map bval else defaultValue let result , bval = bson.TryGetValue(field)
if result then map bval else defaultValue
with _ -> defaultValue
let private mapBack (bson : BsonDocument) : PlayerData = let mapBack (bson : BsonDocument) : PlayerData =
{ DiscordId = tryWithDefault bson "Player.DiscordId" 0uL (fun v -> v.AsInt64 |> uint64) { DiscordId = tryWithDefault bson "DiscordId" 0uL (fun v -> v.AsInt64 |> uint64)
Name = tryWithDefault bson "Player.Name" "Empty" (fun v -> v.AsString) Name = tryWithDefault bson "Name" "Empty" (fun v -> v.AsString)
Inventory = Inventory =
tryWithDefault bson "Inventory" [||] (fun v -> tryWithDefault bson "Inventory" [||] (fun v ->
v.AsBsonArray v.AsBsonArray
|> Seq.map (fun (bv : BsonValue) -> bv.AsInt32) |> Seq.map (fun (bv : BsonValue) -> bv.AsInt32)
|> Seq.map (fun w -> Armory.battleItems |> Array.find (fun w' -> w = w'.Id)) |> Seq.map (fun w -> Armory.battleItems |> Array.find (fun w' -> w = w'.Id))
|> Seq.toArray) |> Seq.toArray)
Events = tryWithDefault bson "Events" [||] (fun _ -> [||]) Events = tryWithDefault bson "Events" [||] (fun v ->
Traits = tryWithDefault bson "Traits" PlayerTraits.empty (fun _ -> PlayerTraits.empty) v.AsBsonArray
Achievements = tryWithDefault bson "Achievements" [||] (fun _ -> [||]) |> Seq.map (fun (bv : BsonValue) ->
XP = tryWithDefault bson "XP" 0 (fun _ -> 0) BsonSerializer.Deserialize<PlayerEvent>(bv.ToBsonDocument()))
Bank = tryWithDefault bson "Player.Bank" 0<GBT> (fun v -> v.AsInt32 * 1<GBT>) |> Seq.toArray)
Traits =
tryWithDefault bson "Traits" PlayerTraits.empty (fun v ->
BsonSerializer.Deserialize<PlayerTraits>(v.ToBsonDocument()))
Achievements =
tryWithDefault bson "Achievements" [||] (fun v ->
v.AsBsonArray |> Seq.map (fun (bv : BsonValue) -> bv.AsString) |> Seq.toArray)
XP = tryWithDefault bson "XP" 0 (fun v -> v.AsInt32)
Bank = tryWithDefault bson "Bank" 0<GBT> (fun v -> v.AsInt32 * 1<GBT>)
} }
let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING"))
let db = mongo.GetDatabase("degenz")
let players = db.GetCollection<BsonDocument>("players")
let tryFindPlayer (id : uint64) = let tryFindPlayer (id : uint64) =
async { async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id) let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id)
@ -58,31 +54,16 @@ let tryFindPlayer (id : uint64) =
|> Some |> Some
} }
let insertNewPlayer (player : PlayerData) =
async {
do! BsonDocument("Player", player.ToBsonDocument()) |> players.InsertOneAsync |> Async.AwaitTask
}
let updatePlayer (player : PlayerData) = let updatePlayer (player : PlayerData) =
async { async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", player.DiscordId) let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", player.DiscordId)
let update = Builders<BsonDocument>.Update let update = Builders<BsonDocument>.Update.Set("Player", player.ToBsonDocument())
.Set("Player", playerMap player) do! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
.AddToSet("Traits", player.Traits)
.AddToSet("Events", player.Events)
.AddToSet("Achievements", player.Achievements)
.AddToSet("Inventory", player.Inventory)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}
let getAchievements (id : uint64) =
async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id)
try
let! player = players.FindAsync<BsonDocument>(filter) |> Async.AwaitTask
match player.FirstOrDefault() with
| null -> return None
| p -> return p
.GetValue("achievements")
.AsBsonArray
|> Seq.map (fun (bv : BsonValue) -> bv.AsString)
|> Some
with ex -> return None
} }
let addAchievement (id : uint64) (achievement : string) = let addAchievement (id : uint64) (achievement : string) =
@ -90,31 +71,4 @@ let addAchievement (id : uint64) (achievement : string) =
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id) let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id)
let update = Builders<BsonDocument>.Update.Push("Achievements", achievement) let update = Builders<BsonDocument>.Update.Push("Achievements", achievement)
return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore return! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
} }
let insertNewPlayer (player : PlayerData) =
async {
let dict = [
KeyValuePair("Player" , (playerMap player).ToBsonDocument() :> Object)
KeyValuePair("Events" , [||] :> Object)
KeyValuePair("Inventory" , [||] :> Object)
KeyValuePair("Traits" , PlayerTraits.empty.ToBsonDocument() :> Object)
KeyValuePair("XP" , 0 :> 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 getTopPlayers amount =
// async {
// return! players.FindAsync()
// }

View File

@ -47,10 +47,12 @@ module Types =
| Cypher = 8 | Cypher = 8
type ItemType = type ItemType =
| Hack | Hack = 0
| Shield | Shield = 1
| Consumable = 1
type BattleItem = { [<CLIMutable>]
type Item = {
Id : int Id : int
Name : string Name : string
Price : int<GBT> Price : int<GBT>
@ -99,7 +101,7 @@ module Types =
type PlayerData = { type PlayerData = {
DiscordId : uint64 DiscordId : uint64
Name : string Name : string
Inventory : BattleItem array Inventory : Item array
Events : PlayerEvent array Events : PlayerEvent array
Traits : PlayerTraits Traits : PlayerTraits
Achievements : string array Achievements : string array
@ -111,10 +113,11 @@ module Types =
module Armory = module Armory =
let battleItems = let battleItems =
let file = System.IO.File.ReadAllText("Items.json") let file = System.IO.File.ReadAllText("Items.json")
JsonConvert.DeserializeObject<BattleItem array>(file) // let file = System.IO.File.ReadAllText("Bot/Items.json")
JsonConvert.DeserializeObject<Item array>(file)
let hacks = battleItems |> Array.filter (fun bi -> match bi.Type with Hack -> true | Shield -> false) let hacks = battleItems |> Array.filter (fun bi -> match bi.Type with ItemType.Hack -> true | _ -> false)
let shields = battleItems |> Array.filter (fun bi -> match bi.Type with Shield -> true | Hack -> false) let shields = battleItems |> Array.filter (fun bi -> match bi.Type with ItemType.Shield -> true | _ -> false)
let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId) let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId)