From 9f756540f1c23d2d99a2c122bd4fbe87e4bfb374 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Mon, 14 Feb 2022 12:01:04 +0700 Subject: [PATCH] Rock Paper Scissors --- Bot/Game.fs | 5 +- Bot/HackerBattle.fs | 2 +- Bot/RockPaperScissors.fs | 130 +++++++++++++++++++++++++++++++++------ Bot/Thief.fs | 2 +- Shared/Shared.fs | 3 + 5 files changed, 119 insertions(+), 23 deletions(-) diff --git a/Bot/Game.fs b/Bot/Game.fs index 86b9627..3237399 100644 --- a/Bot/Game.fs +++ b/Bot/Game.fs @@ -60,12 +60,13 @@ module Game = else do! Messaging.sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command" } |> Async.StartAsTask :> Task - let executePlayerActionWithTargetId (targetId : uint64) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async) = + let executePlayerActionWithTargetId defer (targetId : uint64) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async) = async { let builder = DiscordInteractionResponseBuilder() builder.IsEphemeral <- true builder.Content <- "Content" - do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask + if defer then + do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask let! players = [ tryFindPlayer (ctx.GetDiscordMember().Id) tryFindPlayer targetId ] diff --git a/Bot/HackerBattle.fs b/Bot/HackerBattle.fs index df47de6..9925973 100644 --- a/Bot/HackerBattle.fs +++ b/Bot/HackerBattle.fs @@ -211,7 +211,7 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve | id when id.StartsWith("Defend") -> handleDefense eventCtx | id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task | id when id.StartsWith("Steal") -> Thief.handleSteal eventCtx - | id when id.StartsWith("Steal") -> RockPaperScissors.handleRPS eventCtx + | id when id.StartsWith("RPS") -> RockPaperScissors.handleRPS eventCtx | _ -> task { let builder = DiscordInteractionResponseBuilder() diff --git a/Bot/RockPaperScissors.fs b/Bot/RockPaperScissors.fs index 06f9211..d39ed67 100644 --- a/Bot/RockPaperScissors.fs +++ b/Bot/RockPaperScissors.fs @@ -4,45 +4,137 @@ open System open System.Threading.Tasks open DSharpPlus open DSharpPlus.Entities +open DSharpPlus.EventArgs open DSharpPlus.SlashCommands open Degenz.Messaging type Move = | Rock | Paper - | Scissor + | Scissors type RoundResult = | P1Win | P2Win | Draw -let player1Won p1m p2m = +let moveFromString = function + | "rock" -> Rock + | "paper" -> Paper + | "scissors" -> Scissors + | _ -> failwith "Unknown RPS move" + +let getWinner p1m p2m = match p1m , p2m with | Rock , Paper -> P2Win - | Rock , Scissor -> P1Win + | Rock , Scissors -> P1Win | Paper , Rock -> P1Win - | Paper , Scissor -> P2Win - | Scissor , Rock -> P2Win - | Scissor , Paper -> P1Win + | Paper , Scissors -> P2Win + | Scissors , Rock -> P2Win + | Scissors , Paper -> P1Win | _ , _ -> Draw +let leftGif = function + | Rock -> "https://s10.gifyu.com/images/Sprite-0001614d8697f32165af.gif" + | Paper -> "https://s10.gifyu.com/images/Sprite-0001e7e8ac379d1c5112.gif" + | Scissors -> "https://s10.gifyu.com/images/Sprite-00012c83b50d768a2ed1.gif" + +let rightGif = function + | Rock -> "https://s10.gifyu.com/images/Sprite-0001b4002047d9699732.gif" + | Paper -> "https://s10.gifyu.com/images/Sprite-00014072202347b8271e.gif" + | Scissors -> "https://s10.gifyu.com/images/Sprite-0001c8532b0538ed8706.gif" + +let rpsEmbed disabled opponentMove opponent = + let buttonId move = + match opponentMove with + | Some m -> $"RPS-{move}-{opponent.DiscordId}-{opponent.Name}-True-{m}" + | None -> $"RPS-{move}-{opponent.DiscordId}-{opponent.Name}-False" + let buttons = + [ DiscordButtonComponent(ButtonStyle.Primary, buttonId "rock", "🪨 Rock", disabled) + DiscordButtonComponent(ButtonStyle.Primary, buttonId "paper", "📜 Paper", disabled) + DiscordButtonComponent(ButtonStyle.Primary, buttonId "scissors", "✂ Scissors", disabled) ] + |> Seq.cast + + let embed = + DiscordEmbedBuilder() + .WithTitle("Rock Paper Scissors") + .AddField("Opponent", opponent.Name) + .WithImageUrl("https://s10.gifyu.com/images/222e9b8f5459e5d3a7d.png") + ( buttons , embed ) + +let matchResultsEmbed winner move1 move2 player1 player2 = + let firstEmbed = + DiscordEmbedBuilder() + .WithTitle($"{player1.Name}'s Move") + .WithImageUrl(leftGif move1) + let secondEmbed = + DiscordEmbedBuilder() + .WithTitle($"{player2.Name}'s Move") + .WithImageUrl(rightGif move2) + + let description = + match winner with + | P1Win -> $"{player1.Name} wins with {move1}" + | P2Win -> $"{player2.Name} wins with {move2}" + | Draw -> $"Draw! Both players played {move1}" + let thirdEmbed = + DiscordEmbedBuilder() + .WithTitle($"Results") + .WithDescription(description) + + [ firstEmbed ; secondEmbed ; thirdEmbed ] + let playRPS target ctx = - Game.executePlayerActionWithTarget target ctx (fun attacker defender -> async { - let buttons = - [ DiscordButtonComponent(ButtonStyle.Primary, $"RPS-rock-{defender.DiscordId}-{defender.Name}", "🪨 Rock") - DiscordButtonComponent(ButtonStyle.Primary, $"RPS-paper", "📜 Paper") - DiscordButtonComponent(ButtonStyle.Primary, $"RPS-scissors", "✂ Scissors") ] - |> Seq.cast - - let builder = DiscordFollowupMessageBuilder() - builder.AddComponents(buttons) |> ignore - builder.IsEphemeral <- true - - do! ctx.FollowUp builder |> Async.AwaitTask + Game.executePlayerActionWithTarget target ctx (fun _ defender -> async { + let buttons , embed = rpsEmbed false None defender + let builder = + DiscordFollowupMessageBuilder() + .AddComponents(buttons) + .AddEmbed(embed) + .AsEphemeral(true) + do! ctx.FollowUp (builder) |> Async.AwaitTask }) -let handleRPS ctx = async.Zero() |> Async.StartAsTask +let handleRPS (ctx : IDiscordContext) = + let split = ctx.GetInteractionId().Split("-") + let move = split.[1] + let targetId = uint64 split.[2] + let isResponse = split.[4] = "True" + Game.executePlayerActionWithTargetId false targetId ctx (fun attacker defender -> async { + if isResponse then + let eventCtx = ctx.GetContext() :?> ComponentInteractionCreateEventArgs + let buttons , embed = rpsEmbed true None attacker + let builder = + DiscordInteractionResponseBuilder() + .AddComponents(buttons) + .AddEmbed(embed) + .AsEphemeral(true) + do! eventCtx.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask + let move1 = moveFromString move + let move2 = moveFromString split.[5] + let winner = getWinner move1 move2 + let embeds = matchResultsEmbed winner move1 move2 attacker defender |> Seq.map (fun e -> e.Build()) + let builder = DiscordMessageBuilder().AddEmbeds(embeds) + do! ctx.GetChannel().SendMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore + else + let eventCtx = ctx.GetContext() :?> ComponentInteractionCreateEventArgs + let buttons , embed = rpsEmbed true None attacker + let builder = + DiscordInteractionResponseBuilder() + .AddComponents(buttons) + .AddEmbed(embed) + .AsEphemeral(true) + do! eventCtx.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, builder) |> Async.AwaitTask +// do! sendFollowUpMessage ctx $"Sent challenge to {defender.Name}, wait till they respond" + let buttons , embed = rpsEmbed false (Some move) attacker + let builder = + DiscordMessageBuilder() + .WithContent($"<@{defender.DiscordId}>, you have been challenged to a match of Rock, Paper, Scissors. Pick your move!") + .AddComponents(buttons) + .AddEmbed(embed) + + do! ctx.GetChannel().SendMessageAsync(builder) |> Async.AwaitTask |> Async.Ignore + }) type RPSGame() = inherit ApplicationCommandModule () diff --git a/Bot/Thief.fs b/Bot/Thief.fs index 9e8a621..89dcaa0 100644 --- a/Bot/Thief.fs +++ b/Bot/Thief.fs @@ -171,7 +171,7 @@ let handleSteal (ctx : IDiscordContext) = } if answer = "yes" then let targetId = uint64 split.[2] - Game.executePlayerActionWithTargetId targetId ctx (fun attacker defender -> async { + Game.executePlayerActionWithTargetId true targetId ctx (fun attacker defender -> async { do! attacker |> Player.removeExpiredActions false |> checkVictimStealingCooldown defender diff --git a/Shared/Shared.fs b/Shared/Shared.fs index c917f81..010fb93 100644 --- a/Shared/Shared.fs +++ b/Shared/Shared.fs @@ -129,6 +129,7 @@ module Messaging = abstract member GetGuild : unit -> DiscordGuild abstract member GetInteractionId : unit -> string abstract member GetChannel : unit -> DiscordChannel + abstract member GetContext : unit -> obj type DiscordInteractionContext(ctx : InteractionContext) = interface IDiscordContext with @@ -144,6 +145,7 @@ module Messaging = member this.GetGuild() = ctx.Guild member this.GetInteractionId() = string ctx.InteractionId member this.GetChannel() = ctx.Channel + member this.GetContext() = ctx type DiscordEventContext(ctx : ComponentInteractionCreateEventArgs) = interface IDiscordContext with @@ -159,6 +161,7 @@ module Messaging = member this.GetGuild() = ctx.Guild member this.GetInteractionId() = ctx.Id member this.GetChannel() = ctx.Channel + member this.GetContext() = ctx let getTimeText isCooldown (timespan : TimeSpan) timestamp = let span =