diff --git a/Bot/Bot.fsproj b/Bot/Bot.fsproj index cb720fe..6d23b5c 100644 --- a/Bot/Bot.fsproj +++ b/Bot/Bot.fsproj @@ -13,6 +13,7 @@ + diff --git a/Bot/Game.fs b/Bot/Game.fs index 3237399..0734140 100644 --- a/Bot/Game.fs +++ b/Bot/Game.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 diff --git a/Bot/Thief.fs b/Bot/Thief.fs index 89dcaa0..686c0ff 100644 --- a/Bot/Thief.fs +++ b/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 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 ; 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 ; 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() = } [] - member this.Steal (ctx : InteractionContext, [] target : DiscordUser) = - enforceChannel (DiscordInteractionContext ctx) (steal target) -// steal target (DiscordInteractionContext ctx) + member this.Steal (ctx : InteractionContext, + [] target : DiscordUser, + [] amount : double) = +// enforceChannel (DiscordInteractionContext ctx) (steal target amount) + steal target amount (DiscordInteractionContext ctx) diff --git a/Bot/XP.fs b/Bot/XP.fs new file mode 100644 index 0000000..0477d3a --- /dev/null +++ b/Bot/XP.fs @@ -0,0 +1,53 @@ +module Degenz.XP + +open DSharpPlus +open DSharpPlus.Entities + + +type RewardType = + | Currency of int + | RandomItem of itemType : ItemType * amount : int + | SpecialItem of id : int + +[] +let ToroRojo = 13 +[] +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 ; SpecialItem ToroRojo |] + [| Currency 150 ; SpecialItem AnabolicCycle |] + [| Currency 200 ; SpecialItem ToroRojo |] + [| Currency 100 ; SpecialItem AnabolicCycle |] +|] + +let getRewardsEmbed level (player : PlayerData) = + let buttons = + [ DiscordButtonComponent(ButtonStyle.Success, $"Reward-levelup-{level}", "Claim") ] + |> Seq.cast + 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) + diff --git a/DbService/DbService.fs b/DbService/DbService.fs index c32a3ba..b5be678 100644 --- a/DbService/DbService.fs +++ b/DbService/DbService.fs @@ -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 } +//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 +//} +// + let mongo = MongoClient(Environment.GetEnvironmentVariable("CONN_STRING")) let db = mongo.GetDatabase("degenz") diff --git a/Shared/Shared.fs b/Shared/Shared.fs index 010fb93..f263f14 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -13,6 +13,20 @@ module ResultHelpers = let (>>=) x f = Result.bind f x let () x f = Result.map f x +[] +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 + [] module Types =