Simplify stealing mechanic and finish RPS
This commit is contained in:
parent
9f756540f1
commit
9f7b1956b4
@ -13,6 +13,7 @@
|
|||||||
<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="XP.fs" />
|
||||||
<Compile Include="PlayerInteractions.fs" />
|
<Compile Include="PlayerInteractions.fs" />
|
||||||
<Compile Include="Embeds.fs" />
|
<Compile Include="Embeds.fs" />
|
||||||
<Compile Include="SlotMachine.fs" />
|
<Compile Include="SlotMachine.fs" />
|
||||||
|
@ -88,6 +88,8 @@ module Player =
|
|||||||
player.Actions
|
player.Actions
|
||||||
|> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false || act.ActionId < 12)
|
|> Array.filter (fun act -> match act.Type with Defense -> true | _ -> false || act.ActionId < 12)
|
||||||
|
|
||||||
|
// TODO: This parameter is a result of putting the cooldown on the attack side. Put the cooldown on the defender
|
||||||
|
// side and only check if it's the same target, we need to refactor Actions
|
||||||
let removeExpiredActions filterByAttackCooldown player =
|
let removeExpiredActions filterByAttackCooldown player =
|
||||||
let actions =
|
let actions =
|
||||||
player.Actions
|
player.Actions
|
||||||
|
100
Bot/Thief.fs
100
Bot/Thief.fs
@ -13,30 +13,30 @@ let StealActionId = 12
|
|||||||
let VictimDefenseActionId = 12
|
let VictimDefenseActionId = 12
|
||||||
|
|
||||||
let ThiefCooldown = TimeSpan.FromMinutes(1)
|
let ThiefCooldown = TimeSpan.FromMinutes(1)
|
||||||
let VictimRecovery = TimeSpan.FromHours(12)
|
let VictimRecovery = TimeSpan.FromHours(6)
|
||||||
|
|
||||||
type StealResult =
|
type StealResult =
|
||||||
| Success
|
| Success
|
||||||
| WentToPrison
|
| WentToPrison
|
||||||
| VictimRanAway
|
| VictimRanAway
|
||||||
|
|
||||||
let getRandomStealBtnLabels () =
|
//let getRandomStealBtnLabels () =
|
||||||
let rand = Random(Guid.NewGuid().GetHashCode())
|
// let rand = Random(Guid.NewGuid().GetHashCode())
|
||||||
let affirmative = [| "LFG" ; "YOLO" ; "IDGAF" |]
|
// let affirmative = [| "LFG" ; "YOLO" ; "IDGAF" |]
|
||||||
let negative = [| "NOPE" ; "IM OUT" ; "BAIL" |]
|
// let negative = [| "NOPE" ; "IM OUT" ; "BAIL" |]
|
||||||
( affirmative.[rand.Next(0, 3)] , negative.[rand.Next(0, 3)] )
|
// ( affirmative.[rand.Next(0, 3)] , negative.[rand.Next(0, 3)] )
|
||||||
|
|
||||||
let chanceOfSuccessMsg = function
|
//let chanceOfSuccessMsg = function
|
||||||
| amt when amt < 0.20 -> "Looking pretty bad"
|
// | amt when amt < 0.20 -> "Looking bad"
|
||||||
| amt when amt < 0.50 -> "I mean, maybe"
|
// | amt when amt < 0.50 -> "I mean, maybe"
|
||||||
| amt when amt < 0.80 -> "I think you got this"
|
// | amt when amt < 0.80 -> "I think you got this"
|
||||||
| _ -> "Totally worth it"
|
// | _ -> "Totally worth it"
|
||||||
|
|
||||||
let targetEvaluationMsg = function
|
//let targetEvaluationMsg = function
|
||||||
| amt when amt < 0.20 -> "but man, they look swole"
|
// | amt when amt < 0.20 -> "but man, they look swole"
|
||||||
| amt when amt < 0.50 -> "but they look a little confident"
|
// | amt when amt < 0.50 -> "but they look a little confident"
|
||||||
| amt when amt < 0.80 -> "and they're looking a little nervous"
|
// | amt when amt < 0.80 -> "and they're looking a little nervous"
|
||||||
| _ -> "and they look weak af man"
|
// | _ -> "and they look weak af man"
|
||||||
|
|
||||||
let payout defenderBank chance =
|
let payout defenderBank chance =
|
||||||
let rand = Random(Guid.NewGuid().GetHashCode())
|
let rand = Random(Guid.NewGuid().GetHashCode())
|
||||||
@ -45,19 +45,19 @@ let payout defenderBank chance =
|
|||||||
let randomAmount = baseAmount + randomBonus |> int
|
let randomAmount = baseAmount + randomBonus |> int
|
||||||
max 1 randomAmount
|
max 1 randomAmount
|
||||||
|
|
||||||
let getStealEmbed chance prize (target : PlayerData) =
|
let getStealEmbed (chance : double) prize (target : PlayerData) =
|
||||||
|
let chance = int (chance * 100.0)
|
||||||
let buttons =
|
let buttons =
|
||||||
let yes , no = getRandomStealBtnLabels ()
|
// let yes , no = getRandomStealBtnLabels ()
|
||||||
[ DiscordButtonComponent(ButtonStyle.Success, $"Steal-yes-{target.DiscordId}-{target.Name}-{chance}-{prize}", yes)
|
let btnId = $"Steal-yes-{target.DiscordId}-{target.Name}-{chance}-{prize}"
|
||||||
DiscordButtonComponent(ButtonStyle.Danger, $"Steal-no", no) ]
|
[ DiscordButtonComponent(ButtonStyle.Success, btnId, "Do it") ]
|
||||||
|
// DiscordButtonComponent(ButtonStyle.Danger, $"Steal-no", no) ]
|
||||||
|> Seq.cast<DiscordComponent>
|
|> Seq.cast<DiscordComponent>
|
||||||
let embed =
|
let embed =
|
||||||
DiscordEmbedBuilder()
|
DiscordEmbedBuilder()
|
||||||
.AddField("Chance of Success", $"{chanceOfSuccessMsg chance}", true)
|
.AddField("Chance of Success", $"{chance}%%", true)
|
||||||
.AddField("Payout", $"{prize}", true)
|
.AddField("Payout", $"{prize}", true)
|
||||||
.WithDescription($"{target.Name} is coming towards you in a dark alley, {targetEvaluationMsg chance}")
|
.WithTitle($"Steal $GBT from {target.Name}")
|
||||||
.WithImageUrl("https://cdnb.artstation.com/p/assets/images/images/017/553/457/large/maarten-hof-backalley-mainshot.jpg")
|
|
||||||
.WithTitle($"Steal Money")
|
|
||||||
|
|
||||||
DiscordFollowupMessageBuilder()
|
DiscordFollowupMessageBuilder()
|
||||||
.AddEmbed(embed)
|
.AddEmbed(embed)
|
||||||
@ -81,13 +81,14 @@ let getResultEmbed targetName result =
|
|||||||
, "https://i.imgur.com/NLHMvVK.jpg"
|
, "https://i.imgur.com/NLHMvVK.jpg"
|
||||||
|
|
||||||
DiscordEmbedBuilder()
|
DiscordEmbedBuilder()
|
||||||
.AddField("Result" , resultMsg)
|
// .AddField("Result" , resultMsg)
|
||||||
.WithDescription(msg)
|
.WithDescription(msg)
|
||||||
.WithImageUrl(img)
|
// .WithImageUrl(img)
|
||||||
.WithTitle($"Robbery Results")
|
.WithTitle($"Robbery Results")
|
||||||
|
|
||||||
let checkVictimStealingCooldown defender attacker =
|
let checkVictimStealingCooldown defender attacker =
|
||||||
defender
|
defender
|
||||||
|
|> Player.removeExpiredActions false
|
||||||
|> Player.getDefenses
|
|> Player.getDefenses
|
||||||
|> Array.tryFind (fun act -> act.ActionId = VictimDefenseActionId)
|
|> Array.tryFind (fun act -> act.ActionId = VictimDefenseActionId)
|
||||||
|> function
|
|> function
|
||||||
@ -112,16 +113,28 @@ let checkThiefCooldown attacker =
|
|||||||
Ok attacker
|
Ok attacker
|
||||||
| None -> Ok attacker
|
| None -> Ok attacker
|
||||||
|
|
||||||
let steal target (ctx : IDiscordContext) =
|
|
||||||
|
let calculateWinPercentage amountRequested bank attackerStrength defenderStrength =
|
||||||
|
let powerPercentage = float (attackerStrength - defenderStrength) * 0.005 + 0.25
|
||||||
|
printfn $"{(attackerStrength - defenderStrength)}"
|
||||||
|
printfn $"{powerPercentage}"
|
||||||
|
let cappedAmount = float bank * 0.5
|
||||||
|
let cappedRequest = min amountRequested (cappedAmount |> ceil)
|
||||||
|
let wagerPercentage = 1.0 - (cappedRequest / cappedAmount)
|
||||||
|
// Max chance of success is 97.5%
|
||||||
|
( cappedRequest , max 0.0 (wagerPercentage * 0.7 + powerPercentage * 1.3 ) / 2.05 )
|
||||||
|
|
||||||
|
//calculateWinPercentage 500 1000 100 50
|
||||||
|
|
||||||
|
let steal target amount (ctx : IDiscordContext) =
|
||||||
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async {
|
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async {
|
||||||
do! attacker
|
do!
|
||||||
|
attacker
|
||||||
|> checkVictimStealingCooldown defender
|
|> checkVictimStealingCooldown defender
|
||||||
>>= checkThiefCooldown
|
>>= checkThiefCooldown
|
||||||
|> handleResultWithResponse ctx (fun player -> async {
|
|> handleResultWithResponse ctx (fun _ -> async {
|
||||||
let ``base`` = 0.5
|
let cappedPrize , winPercentage = calculateWinPercentage amount (int defender.Bank) attacker.Stats.Strength defender.Stats.Strength
|
||||||
let winPercentage = double (attacker.Stats.Strength - defender.Stats.Strength) * 0.45 + ``base``
|
let embed = getStealEmbed winPercentage cappedPrize defender
|
||||||
let prize = payout (float defender.Bank) winPercentage
|
|
||||||
let embed = getStealEmbed winPercentage prize defender
|
|
||||||
|
|
||||||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||||||
})
|
})
|
||||||
@ -143,9 +156,10 @@ let handleSteal (ctx : IDiscordContext) =
|
|||||||
let dp = { DiscordPlayer.Id = targetId ; DiscordPlayer.Name = targetName }
|
let dp = { DiscordPlayer.Id = targetId ; DiscordPlayer.Name = targetName }
|
||||||
let stealAction = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = false ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow }
|
let stealAction = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = false ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow }
|
||||||
// TODO: Send event to the hall of privacy
|
// TODO: Send event to the hall of privacy
|
||||||
|
// TODO: We need to check if the player is on cooldown
|
||||||
match result with
|
match result with
|
||||||
| true , _ ->
|
| true , _ ->
|
||||||
let xp = 15
|
let xp = 25
|
||||||
let embed = getResultEmbed targetName Success
|
let embed = getResultEmbed targetName Success
|
||||||
embed.AddField("$GBT Stolen", string prize) |> ignore
|
embed.AddField("$GBT Stolen", string prize) |> ignore
|
||||||
embed.AddField("XP Gained", $"{xp}") |> ignore
|
embed.AddField("XP Gained", $"{xp}") |> ignore
|
||||||
@ -153,10 +167,16 @@ let handleSteal (ctx : IDiscordContext) =
|
|||||||
match! DbService.tryFindPlayer targetId with
|
match! DbService.tryFindPlayer targetId with
|
||||||
| Some t ->
|
| Some t ->
|
||||||
let action = { ActionId = VictimDefenseActionId ; Type = Defense ; Timestamp = DateTime.UtcNow }
|
let action = { ActionId = VictimDefenseActionId ; Type = Defense ; Timestamp = DateTime.UtcNow }
|
||||||
do! DbService.updatePlayer { t with Bank = max (t.Bank - prize) 0<GBT> ; Actions = Array.append [| action |] t.Actions }
|
let actions = t |> Player.removeExpiredActions false |> fun p -> Array.append [| action |] p.Actions
|
||||||
|
do! DbService.updatePlayer { t with Bank = max (t.Bank - prize) 0<GBT> ; Actions = actions }
|
||||||
| None -> ()
|
| None -> ()
|
||||||
let action = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = true ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow }
|
let action = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = true ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow }
|
||||||
do! DbService.updatePlayer { player with Bank = player.Bank + prize ; XP = player.XP + xp ; Actions = Array.append [| action |] player.Actions }
|
let actions = player |> Player.removeExpiredActions false |> fun p -> Array.append [| action |] p.Actions
|
||||||
|
do! DbService.updatePlayer { player with Bank = player.Bank + prize ; XP = player.XP + xp ; Actions = actions }
|
||||||
|
let newLevel = XP.getLevel (player.XP + xp)
|
||||||
|
// if XP.getLevel player.XP < newLevel then
|
||||||
|
do! Async.Sleep 2000
|
||||||
|
do! ctx.FollowUp (XP.getRewardsEmbed 1 player) |> Async.AwaitTask
|
||||||
| false , false ->
|
| false , false ->
|
||||||
let embed = getResultEmbed targetName VictimRanAway
|
let embed = getResultEmbed targetName VictimRanAway
|
||||||
do! DbService.updatePlayer { player with Actions = Array.append [| stealAction |] player.Actions }
|
do! DbService.updatePlayer { player with Actions = Array.append [| stealAction |] player.Actions }
|
||||||
@ -198,8 +218,10 @@ type StealGame() =
|
|||||||
}
|
}
|
||||||
|
|
||||||
[<SlashCommand("steal", "Steal some money from another player, but you might go to prison if caught")>]
|
[<SlashCommand("steal", "Steal some money from another player, but you might go to prison if caught")>]
|
||||||
member this.Steal (ctx : InteractionContext, [<Option("target", "Who do you want to steal from?")>] target : DiscordUser) =
|
member this.Steal (ctx : InteractionContext,
|
||||||
enforceChannel (DiscordInteractionContext ctx) (steal target)
|
[<Option("target", "Who do you want to steal from?")>] target : DiscordUser,
|
||||||
// steal target (DiscordInteractionContext ctx)
|
[<Option("amount", "How much you would like to steal")>] amount : double) =
|
||||||
|
// enforceChannel (DiscordInteractionContext ctx) (steal target amount)
|
||||||
|
steal target amount (DiscordInteractionContext ctx)
|
||||||
|
|
||||||
|
|
||||||
|
53
Bot/XP.fs
Normal file
53
Bot/XP.fs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
module Degenz.XP
|
||||||
|
|
||||||
|
open DSharpPlus
|
||||||
|
open DSharpPlus.Entities
|
||||||
|
|
||||||
|
|
||||||
|
type RewardType =
|
||||||
|
| Currency of int<GBT>
|
||||||
|
| RandomItem of itemType : ItemType * amount : int
|
||||||
|
| SpecialItem of id : int
|
||||||
|
|
||||||
|
[<Literal>]
|
||||||
|
let ToroRojo = 13
|
||||||
|
[<Literal>]
|
||||||
|
let AnabolicCycle = 14
|
||||||
|
|
||||||
|
let levels = [|
|
||||||
|
50
|
||||||
|
100
|
||||||
|
160
|
||||||
|
250
|
||||||
|
|]
|
||||||
|
|
||||||
|
let getLevel totalXp = levels |> Array.toList |> List.foldi (fun i acc elem -> if totalXp < elem then acc else i + 1) 0
|
||||||
|
|
||||||
|
let RewardTable = [|
|
||||||
|
[| Currency 100<GBT> ; SpecialItem ToroRojo |]
|
||||||
|
[| Currency 150<GBT> ; SpecialItem AnabolicCycle |]
|
||||||
|
[| Currency 200<GBT> ; SpecialItem ToroRojo |]
|
||||||
|
[| Currency 100<GBT> ; SpecialItem AnabolicCycle |]
|
||||||
|
|]
|
||||||
|
|
||||||
|
let getRewardsEmbed level (player : PlayerData) =
|
||||||
|
let buttons =
|
||||||
|
[ DiscordButtonComponent(ButtonStyle.Success, $"Reward-levelup-{level}", "Claim") ]
|
||||||
|
|> Seq.cast<DiscordComponent>
|
||||||
|
let header =
|
||||||
|
DiscordEmbedBuilder()
|
||||||
|
.WithTitle($"Level Up")
|
||||||
|
.WithDescription($"You reached Level {level}")
|
||||||
|
.WithImageUrl("https://thumbs.dreamstime.com/z/pixel-art-design-outdoor-landscape-background-colorful-arcade-screen-game-banner-button-level-up-concept-retro-style-180858223.jpg")
|
||||||
|
|
||||||
|
let footer =
|
||||||
|
DiscordEmbedBuilder()
|
||||||
|
.WithTitle("Rewards")
|
||||||
|
.AddField("$GBT", $"{RewardTable.[level].[0]}", true)
|
||||||
|
.AddField("Items", $"{RewardTable.[level].[1]}", true)
|
||||||
|
|
||||||
|
DiscordFollowupMessageBuilder()
|
||||||
|
.AddEmbeds([ header ; footer ] |> List.map (fun e -> e.Build()))
|
||||||
|
.AddComponents(buttons)
|
||||||
|
.AsEphemeral(true)
|
||||||
|
|
@ -26,6 +26,7 @@ type PlayerEntry =
|
|||||||
Arsenal : int array
|
Arsenal : int array
|
||||||
Attacks : AttackAction array
|
Attacks : AttackAction array
|
||||||
Defenses : DefenseAction array
|
Defenses : DefenseAction array
|
||||||
|
// XP : int
|
||||||
Bank : int }
|
Bank : int }
|
||||||
|
|
||||||
let private actionToAttack (action : Action) (hack : AttackResult) =
|
let private actionToAttack (action : Action) (hack : AttackResult) =
|
||||||
@ -72,6 +73,24 @@ let private mapBack (player : PlayerEntry) : PlayerData = {
|
|||||||
Bank = player.Bank * 1<GBT>
|
Bank = player.Bank * 1<GBT>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//let private mapBack (bson : BsonDocument) : PlayerData = {
|
||||||
|
// DiscordId = bson.GetValue("Player.DiscordId").AsInt64 |> uint64
|
||||||
|
// Name = bson.GetValue("Player.Name").AsString
|
||||||
|
// Arsenal =
|
||||||
|
// bson.GetValue("Inventory").AsBsonArray
|
||||||
|
// |> Seq.map (fun (bv : BsonValue) -> bv.AsInt32)
|
||||||
|
// |> Seq.map (fun w -> Armory.battleItems |> Array.find (fun w' -> w = w'.Id))
|
||||||
|
// |> Seq.toArray
|
||||||
|
// Actions =
|
||||||
|
// let atks = player.Attacks |> Array.map attackToAction
|
||||||
|
// let dfns = player.Defenses |> Array.map defenseToAction
|
||||||
|
// Array.append atks dfns
|
||||||
|
// Stats = PlayerStats.empty
|
||||||
|
// XP = 0
|
||||||
|
// Bank = player.Bank * 1<GBT>
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING"))
|
let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING"))
|
||||||
let db = mongo.GetDatabase("degenz")
|
let db = mongo.GetDatabase("degenz")
|
||||||
|
@ -13,6 +13,20 @@ module ResultHelpers =
|
|||||||
let (>>=) x f = Result.bind f x
|
let (>>=) x f = Result.bind f x
|
||||||
let (<!>) x f = Result.map f x
|
let (<!>) x f = Result.map f x
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module List =
|
||||||
|
let cons xs x = x :: xs
|
||||||
|
let consTo x xs = x :: xs
|
||||||
|
|
||||||
|
let rec foldk f (acc:'TState) xs =
|
||||||
|
match xs with
|
||||||
|
| [] -> acc
|
||||||
|
| x::xs -> f acc x (fun lacc -> foldk f lacc xs)
|
||||||
|
|
||||||
|
let foldi (f : int -> 'Acc -> 'elem -> 'Acc) (acc : 'Acc) xs =
|
||||||
|
let f' ( i , st ) acc = ( i + 1 , f i st acc )
|
||||||
|
List.fold f' ( 0 , acc ) xs |> snd
|
||||||
|
|
||||||
[<Microsoft.FSharp.Core.AutoOpen>]
|
[<Microsoft.FSharp.Core.AutoOpen>]
|
||||||
module Types =
|
module Types =
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user