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())
.AsEphemeral true
let responseSuccessfulHack earnedMoney (targetId : uint64) (hack : BattleItem) =
let responseSuccessfulHack earnedMoney (targetId : uint64) (hack : Item) =
let embed = DiscordEmbedBuilder()
embed.ImageUrl <- getHackGif (enum<HackId>(hack.Id))
embed.Title <- "Hack Attack"
@ -88,7 +88,7 @@ let responseSuccessfulHack earnedMoney (targetId : uint64) (hack : BattleItem) =
.AddEmbed(embed.Build())
.AsEphemeral(true)
let responseCreatedShield (shield : BattleItem) =
let responseCreatedShield (shield : Item) =
let embed = DiscordEmbedBuilder().WithImageUrl(getShieldGif (enum<ShieldId>(shield.Id)))
embed.Title <- "Mounted Shield"
embed.Description <- $"Mounted {shield.Name} shield for {TimeSpan.FromMinutes(int shield.Cooldown).Hours} hours"
@ -101,20 +101,20 @@ let eventSuccessfulHack (ctx : IDiscordContext) target prize =
DiscordMessageBuilder()
.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 =
store
|> Array.filter (fun i -> i.Type = itemType)
|> Array.map (fun item ->
let embed = DiscordEmbedBuilder()
match item.Type with
| Hack ->
| ItemType.Hack ->
embed
.AddField($"Hacking Power |", string item.Power, true)
.AddField("Cooldown |", $"{TimeSpan.FromMinutes(int item.Cooldown).Minutes} minutes", true)
.WithThumbnail(getHackIcon (enum<HackId>(item.Id)))
|> ignore
| Shield ->
| _ ->
embed
.AddField($"Defensive Strength |", string item.Power, 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 ->
let embed = DiscordEmbedBuilder()
match item.Type with
| Hack -> embed.WithThumbnail(getHackIcon (enum<HackId>(item.Id))) |> ignore
| Shield -> embed.WithThumbnail(getShieldIcon (enum<ShieldId>(item.Id))) |> ignore
| ItemType.Hack -> embed.WithThumbnail(getHackIcon (enum<HackId>(item.Id))) |> ignore
| _ -> embed.WithThumbnail(getShieldIcon (enum<ShieldId>(item.Id))) |> ignore
embed
.AddField("Sell For 💰", $"{item.Price} $GBT", true)
.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> }
module Arsenal =
let battleItemFormat (items : BattleItem array) =
let battleItemFormat (items : Item array) =
match items with
| [||] -> "None"
| _ -> 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"
| 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
then Weak
else Strong

View File

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

View File

@ -8,17 +8,17 @@ open DSharpPlus.SlashCommands
open Degenz
open Degenz.Messaging
let checkHasSufficientFunds (item : BattleItem) player =
let checkHasSufficientFunds (item : Item) player =
if player.Bank - item.Price >= 0<GBT>
then Ok player
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)
then Error $"You already own {item.Name}!"
else Ok player
let checkSoldItemAlready (item : BattleItem) player =
let checkSoldItemAlready (item : Item) player =
if player.Inventory |> Array.exists (fun w -> item.Id = w.Id)
then Ok player
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
let updatedPlayer = {
player with
Bank = player.Bank + hackMoney + shieldMoney
Bank = player.Bank + 100<GBT>
Events = [
{ PlayerEvent.Timestamp = System.DateTime.UtcNow
PlayerEvent.Adversary = { Id = GuildEnvironment.botIdHackerBattle ; Name = "Sensei" }

View File

@ -1,50 +1,46 @@
module Degenz.DbService
open Degenz.Types
open System
open System.Collections.Generic
open MongoDB.Bson
open MongoDB.Bson.Serialization
open MongoDB.Driver
open Degenz.Types
[<CLIMutable>]
type PlayerEntry =
{ DiscordId : uint64
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 mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING"))
let db = mongo.GetDatabase("degenz")
let players = db.GetCollection<BsonDocument>("players")
let tryWithDefault (bson : BsonDocument) field (defaultValue : 'a) (map : BsonValue -> 'a) =
let result , bval = bson.TryGetValue(field)
if result then map bval else defaultValue
try
let result , bval = bson.TryGetValue(field)
if result then map bval else defaultValue
with _ -> defaultValue
let private mapBack (bson : BsonDocument) : PlayerData =
{ DiscordId = tryWithDefault bson "Player.DiscordId" 0uL (fun v -> v.AsInt64 |> uint64)
Name = tryWithDefault bson "Player.Name" "Empty" (fun v -> v.AsString)
let mapBack (bson : BsonDocument) : PlayerData =
{ DiscordId = tryWithDefault bson "DiscordId" 0uL (fun v -> v.AsInt64 |> uint64)
Name = tryWithDefault bson "Name" "Empty" (fun v -> v.AsString)
Inventory =
tryWithDefault bson "Inventory" [||] (fun v ->
v.AsBsonArray
|> Seq.map (fun (bv : BsonValue) -> bv.AsInt32)
|> Seq.map (fun w -> Armory.battleItems |> Array.find (fun w' -> w = w'.Id))
|> Seq.toArray)
Events = tryWithDefault bson "Events" [||] (fun _ -> [||])
Traits = tryWithDefault bson "Traits" PlayerTraits.empty (fun _ -> PlayerTraits.empty)
Achievements = tryWithDefault bson "Achievements" [||] (fun _ -> [||])
XP = tryWithDefault bson "XP" 0 (fun _ -> 0)
Bank = tryWithDefault bson "Player.Bank" 0<GBT> (fun v -> v.AsInt32 * 1<GBT>)
Events = tryWithDefault bson "Events" [||] (fun v ->
v.AsBsonArray
|> Seq.map (fun (bv : BsonValue) ->
BsonSerializer.Deserialize<PlayerEvent>(bv.ToBsonDocument()))
|> 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) =
async {
let filter = Builders<BsonDocument>.Filter.Eq("Player.DiscordId", id)
@ -58,31 +54,16 @@ let tryFindPlayer (id : uint64) =
|> Some
}
let insertNewPlayer (player : PlayerData) =
async {
do! BsonDocument("Player", player.ToBsonDocument()) |> 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", playerMap player)
.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 update = Builders<BsonDocument>.Update.Set("Player", player.ToBsonDocument())
do! players.UpdateOneAsync(filter, update) |> Async.AwaitTask |> Async.Ignore
}
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 update = Builders<BsonDocument>.Update.Push("Achievements", achievement)
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
type ItemType =
| Hack
| Shield
| Hack = 0
| Shield = 1
| Consumable = 1
type BattleItem = {
[<CLIMutable>]
type Item = {
Id : int
Name : string
Price : int<GBT>
@ -99,7 +101,7 @@ module Types =
type PlayerData = {
DiscordId : uint64
Name : string
Inventory : BattleItem array
Inventory : Item array
Events : PlayerEvent array
Traits : PlayerTraits
Achievements : string array
@ -111,10 +113,11 @@ module Types =
module Armory =
let battleItems =
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 shields = battleItems |> Array.filter (fun bi -> match bi.Type with Shield -> true | Hack -> 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 ItemType.Shield -> true | _ -> false)
let getItem itemId = battleItems |> Array.find (fun w -> w.Id = itemId)