Simplify stealing mechanic and finish RPS
This commit is contained in:
parent
9f756540f1
commit
9f7b1956b4
@ -13,6 +13,7 @@
|
||||
<Content Include="paket.references" />
|
||||
<Compile Include="GuildEnvironment.fs" />
|
||||
<Compile Include="Game.fs" />
|
||||
<Compile Include="XP.fs" />
|
||||
<Compile Include="PlayerInteractions.fs" />
|
||||
<Compile Include="Embeds.fs" />
|
||||
<Compile Include="SlotMachine.fs" />
|
||||
|
@ -88,6 +88,8 @@ module Player =
|
||||
player.Actions
|
||||
|> 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 actions =
|
||||
player.Actions
|
||||
|
110
Bot/Thief.fs
110
Bot/Thief.fs
@ -13,30 +13,30 @@ let StealActionId = 12
|
||||
let VictimDefenseActionId = 12
|
||||
|
||||
let ThiefCooldown = TimeSpan.FromMinutes(1)
|
||||
let VictimRecovery = TimeSpan.FromHours(12)
|
||||
let VictimRecovery = TimeSpan.FromHours(6)
|
||||
|
||||
type StealResult =
|
||||
| Success
|
||||
| WentToPrison
|
||||
| VictimRanAway
|
||||
|
||||
let getRandomStealBtnLabels () =
|
||||
let rand = Random(Guid.NewGuid().GetHashCode())
|
||||
let affirmative = [| "LFG" ; "YOLO" ; "IDGAF" |]
|
||||
let negative = [| "NOPE" ; "IM OUT" ; "BAIL" |]
|
||||
( affirmative.[rand.Next(0, 3)] , negative.[rand.Next(0, 3)] )
|
||||
//let getRandomStealBtnLabels () =
|
||||
// let rand = Random(Guid.NewGuid().GetHashCode())
|
||||
// let affirmative = [| "LFG" ; "YOLO" ; "IDGAF" |]
|
||||
// let negative = [| "NOPE" ; "IM OUT" ; "BAIL" |]
|
||||
// ( affirmative.[rand.Next(0, 3)] , negative.[rand.Next(0, 3)] )
|
||||
|
||||
let chanceOfSuccessMsg = function
|
||||
| amt when amt < 0.20 -> "Looking pretty bad"
|
||||
| amt when amt < 0.50 -> "I mean, maybe"
|
||||
| amt when amt < 0.80 -> "I think you got this"
|
||||
| _ -> "Totally worth it"
|
||||
//let chanceOfSuccessMsg = function
|
||||
// | amt when amt < 0.20 -> "Looking bad"
|
||||
// | amt when amt < 0.50 -> "I mean, maybe"
|
||||
// | amt when amt < 0.80 -> "I think you got this"
|
||||
// | _ -> "Totally worth it"
|
||||
|
||||
let targetEvaluationMsg = function
|
||||
| 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.80 -> "and they're looking a little nervous"
|
||||
| _ -> "and they look weak af man"
|
||||
//let targetEvaluationMsg = function
|
||||
// | 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.80 -> "and they're looking a little nervous"
|
||||
// | _ -> "and they look weak af man"
|
||||
|
||||
let payout defenderBank chance =
|
||||
let rand = Random(Guid.NewGuid().GetHashCode())
|
||||
@ -45,19 +45,19 @@ let payout defenderBank chance =
|
||||
let randomAmount = baseAmount + randomBonus |> int
|
||||
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 yes , no = getRandomStealBtnLabels ()
|
||||
[ DiscordButtonComponent(ButtonStyle.Success, $"Steal-yes-{target.DiscordId}-{target.Name}-{chance}-{prize}", yes)
|
||||
DiscordButtonComponent(ButtonStyle.Danger, $"Steal-no", no) ]
|
||||
// let yes , no = getRandomStealBtnLabels ()
|
||||
let btnId = $"Steal-yes-{target.DiscordId}-{target.Name}-{chance}-{prize}"
|
||||
[ DiscordButtonComponent(ButtonStyle.Success, btnId, "Do it") ]
|
||||
// DiscordButtonComponent(ButtonStyle.Danger, $"Steal-no", no) ]
|
||||
|> Seq.cast<DiscordComponent>
|
||||
let embed =
|
||||
DiscordEmbedBuilder()
|
||||
.AddField("Chance of Success", $"{chanceOfSuccessMsg chance}", true)
|
||||
.AddField("Chance of Success", $"{chance}%%", true)
|
||||
.AddField("Payout", $"{prize}", true)
|
||||
.WithDescription($"{target.Name} is coming towards you in a dark alley, {targetEvaluationMsg chance}")
|
||||
.WithImageUrl("https://cdnb.artstation.com/p/assets/images/images/017/553/457/large/maarten-hof-backalley-mainshot.jpg")
|
||||
.WithTitle($"Steal Money")
|
||||
.WithTitle($"Steal $GBT from {target.Name}")
|
||||
|
||||
DiscordFollowupMessageBuilder()
|
||||
.AddEmbed(embed)
|
||||
@ -81,13 +81,14 @@ let getResultEmbed targetName result =
|
||||
, "https://i.imgur.com/NLHMvVK.jpg"
|
||||
|
||||
DiscordEmbedBuilder()
|
||||
.AddField("Result" , resultMsg)
|
||||
// .AddField("Result" , resultMsg)
|
||||
.WithDescription(msg)
|
||||
.WithImageUrl(img)
|
||||
// .WithImageUrl(img)
|
||||
.WithTitle($"Robbery Results")
|
||||
|
||||
let checkVictimStealingCooldown defender attacker =
|
||||
defender
|
||||
|> Player.removeExpiredActions false
|
||||
|> Player.getDefenses
|
||||
|> Array.tryFind (fun act -> act.ActionId = VictimDefenseActionId)
|
||||
|> function
|
||||
@ -112,19 +113,31 @@ let checkThiefCooldown attacker =
|
||||
Ok attacker
|
||||
| None -> Ok attacker
|
||||
|
||||
let steal target (ctx : IDiscordContext) =
|
||||
Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async {
|
||||
do! attacker
|
||||
|> checkVictimStealingCooldown defender
|
||||
>>= checkThiefCooldown
|
||||
|> handleResultWithResponse ctx (fun player -> async {
|
||||
let ``base`` = 0.5
|
||||
let winPercentage = double (attacker.Stats.Strength - defender.Stats.Strength) * 0.45 + ``base``
|
||||
let prize = payout (float defender.Bank) winPercentage
|
||||
let embed = getStealEmbed winPercentage prize defender
|
||||
|
||||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||||
})
|
||||
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 {
|
||||
do!
|
||||
attacker
|
||||
|> checkVictimStealingCooldown defender
|
||||
>>= checkThiefCooldown
|
||||
|> handleResultWithResponse ctx (fun _ -> async {
|
||||
let cappedPrize , winPercentage = calculateWinPercentage amount (int defender.Bank) attacker.Stats.Strength defender.Stats.Strength
|
||||
let embed = getStealEmbed winPercentage cappedPrize defender
|
||||
|
||||
do! ctx.FollowUp(embed) |> Async.AwaitTask
|
||||
})
|
||||
})
|
||||
|
||||
let handleSteal (ctx : IDiscordContext) =
|
||||
@ -143,9 +156,10 @@ let handleSteal (ctx : IDiscordContext) =
|
||||
let dp = { DiscordPlayer.Id = targetId ; DiscordPlayer.Name = targetName }
|
||||
let stealAction = { ActionId = StealActionId ; Type = Attack { AttackResult.Result = false ; AttackResult.Target = dp } ; Timestamp = DateTime.UtcNow }
|
||||
// TODO: Send event to the hall of privacy
|
||||
// TODO: We need to check if the player is on cooldown
|
||||
match result with
|
||||
| true , _ ->
|
||||
let xp = 15
|
||||
let xp = 25
|
||||
let embed = getResultEmbed targetName Success
|
||||
embed.AddField("$GBT Stolen", string prize) |> ignore
|
||||
embed.AddField("XP Gained", $"{xp}") |> ignore
|
||||
@ -153,10 +167,16 @@ let handleSteal (ctx : IDiscordContext) =
|
||||
match! DbService.tryFindPlayer targetId with
|
||||
| Some t ->
|
||||
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 -> ()
|
||||
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 ->
|
||||
let embed = getResultEmbed targetName VictimRanAway
|
||||
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")>]
|
||||
member this.Steal (ctx : InteractionContext, [<Option("target", "Who do you want to steal from?")>] target : DiscordUser) =
|
||||
enforceChannel (DiscordInteractionContext ctx) (steal target)
|
||||
// steal target (DiscordInteractionContext ctx)
|
||||
member this.Steal (ctx : InteractionContext,
|
||||
[<Option("target", "Who do you want to steal from?")>] target : DiscordUser,
|
||||
[<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
|
||||
Attacks : AttackAction array
|
||||
Defenses : DefenseAction array
|
||||
// XP : int
|
||||
Bank : int }
|
||||
|
||||
let private actionToAttack (action : Action) (hack : AttackResult) =
|
||||
@ -72,6 +73,24 @@ let private mapBack (player : PlayerEntry) : PlayerData = {
|
||||
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 db = mongo.GetDatabase("degenz")
|
||||
|
@ -13,6 +13,20 @@ module ResultHelpers =
|
||||
let (>>=) x f = Result.bind 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>]
|
||||
module Types =
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user