First pass on thief game

This commit is contained in:
Joseph Ferano 2022-02-13 03:12:55 +07:00
parent dd33492418
commit bb9c04fe6e
8 changed files with 173 additions and 26 deletions

View File

@ -6,6 +6,7 @@ open DSharpPlus.SlashCommands
open Degenz open Degenz
open Degenz.HackerBattle open Degenz.HackerBattle
open Degenz.Store open Degenz.Store
open Degenz.Thief
open Emzi0767.Utilities open Emzi0767.Utilities
//open Degenz.SlotMachine //open Degenz.SlotMachine
@ -36,12 +37,13 @@ let storeBot = new DiscordClient(storeConfig)
//let slotMachineBot = new DiscordClient(slotMachineConfig) //let slotMachineBot = new DiscordClient(slotMachineConfig)
//let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |] //let clients = [| hackerBattleBot ; storeBot ; slotMachineBot |]
let sc1 = hackerBattleBot.UseSlashCommands() let hackerCommands = hackerBattleBot.UseSlashCommands()
let sc2 = storeBot.UseSlashCommands() let storeCommands = storeBot.UseSlashCommands()
//let sc3 = slotMachineBot.UseSlashCommands() //let sc3 = slotMachineBot.UseSlashCommands()
sc1.RegisterCommands<HackerGame>(guild); hackerCommands.RegisterCommands<HackerGame>(guild);
sc2.RegisterCommands<Store>(guild); hackerCommands.RegisterCommands<StealGame>(guild);
storeCommands.RegisterCommands<Store>(guild);
//sc3.RegisterCommands<SlotMachine>(guild); //sc3.RegisterCommands<SlotMachine>(guild);
hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent)) hackerBattleBot.add_ComponentInteractionCreated(AsyncEventHandler(HackerBattle.handleButtonEvent))

View File

@ -16,9 +16,10 @@
<Compile Include="PlayerInteractions.fs" /> <Compile Include="PlayerInteractions.fs" />
<Compile Include="Embeds.fs" /> <Compile Include="Embeds.fs" />
<Compile Include="Store.fs" /> <Compile Include="Store.fs" />
<Compile Include="Thief.fs" />
<Compile Include="SlotMachine.fs" />
<Compile Include="Trainer.fs" /> <Compile Include="Trainer.fs" />
<Compile Include="HackerBattle.fs" /> <Compile Include="HackerBattle.fs" />
<Compile Include="SlotMachine.fs" />
<Compile Include="Bot.fs" /> <Compile Include="Bot.fs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -38,7 +38,24 @@ module Game =
let! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id) let! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id)
match playerResult with match playerResult with
| Some player -> do! dispatch player | Some player -> do! dispatch player
| None -> do! Messaging.sendSimpleResponse ctx "You are currently not a hacker, first use the /redpill command to become one" | None -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
} |> Async.StartAsTask :> Task
let executePlayerWithTargetAction (targetPlayer : DiscordUser) (ctx : IDiscordContext) (dispatch : PlayerData -> PlayerData -> Async<unit>) =
async {
let builder = DiscordInteractionResponseBuilder()
builder.IsEphemeral <- true
builder.Content <- "Content"
do! ctx.Respond InteractionResponseType.DeferredChannelMessageWithSource builder |> Async.AwaitTask
let! playerResult = tryFindPlayer (ctx.GetDiscordMember().Id)
let! targetResult = tryFindPlayer targetPlayer.Id
match playerResult , targetResult with
| Some player , Some target -> do! dispatch player target
| None , _ -> do! Messaging.sendFollowUpMessage ctx "You are currently not a hacker, first use the /redpill command to become one"
| _ , None ->
if targetPlayer.IsBot
then do! Messaging.sendFollowUpMessage ctx $"{targetPlayer.Username} is a bot, pick a real human to hack"
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 } |> Async.StartAsTask :> Task
module Player = module Player =

View File

@ -120,26 +120,18 @@ let failedHack (ctx : IDiscordContext) attacker defender hack =
} }
let attack (target : DiscordUser) (ctx : IDiscordContext) = let attack (target : DiscordUser) (ctx : IDiscordContext) =
Game.executePlayerAction ctx (fun attacker -> async { Game.executePlayerWithTargetAction target ctx (fun attacker defender -> async {
let! defender = DbService.tryFindPlayer target.Id do! attacker
match defender with |> checkAlreadyHackedTarget defender.DiscordId
| Some defender -> <!> (Player.removeExpiredActions true)
do! attacker >>= checkHasEmptyHacks
|> checkAlreadyHackedTarget defender.DiscordId >>= checkTargetHasMoney defender
<!> (Player.removeExpiredActions true) >>= checkPlayerIsAttackingThemselves defender
>>= checkHasEmptyHacks |> function
>>= checkTargetHasMoney defender | Ok atkr ->
>>= checkPlayerIsAttackingThemselves defender let embed = Embeds.pickHack "Attack" atkr defender false
|> function ctx.FollowUp(embed) |> Async.AwaitTask
| Ok atkr -> | Error msg -> sendFollowUpMessage ctx msg
let embed = Embeds.pickHack "Attack" atkr defender false
ctx.FollowUp(embed) |> Async.AwaitTask
| Error msg -> sendFollowUpMessage ctx msg
| None ->
if target.IsBot
then do! sendFollowUpMessage ctx $"{target.Username} is a bot, pick a real human to hack"
else do! sendFollowUpMessage ctx "Your target is not connected to the network, they must join first by using the /redpill command"
}) })
let handleAttack (ctx : IDiscordContext) = let handleAttack (ctx : IDiscordContext) =
@ -208,6 +200,7 @@ let handleButtonEvent (_ : DiscordClient) (event : ComponentInteractionCreateEve
| id when id.StartsWith("Attack") -> handleAttack eventCtx | id when id.StartsWith("Attack") -> handleAttack eventCtx
| id when id.StartsWith("Defend") -> handleDefense eventCtx | id when id.StartsWith("Defend") -> handleDefense eventCtx
| id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task | id when id.StartsWith("Trainer") -> Trainer.handleButtonEvent eventCtx |> Async.StartAsTask :> Task
| id when id.StartsWith("Steal") -> Thief.handleSteal eventCtx
| _ -> | _ ->
task { task {
let builder = DiscordInteractionResponseBuilder() let builder = DiscordInteractionResponseBuilder()

View File

@ -16,6 +16,8 @@ module Commands =
Name = nickname Name = nickname
Arsenal = [| hack ; shield |] Arsenal = [| hack ; shield |]
Actions = [||] Actions = [||]
XP = 0
Stats = PlayerStats.empty
Bank = 100<GBT> } Bank = 100<GBT> }
let addHackerRole (ctx : InteractionContext) = let addHackerRole (ctx : InteractionContext) =

102
Bot/Thief.fs Normal file
View File

@ -0,0 +1,102 @@
module Degenz.Thief
open System
open System.Threading.Tasks
open DSharpPlus
open DSharpPlus.Entities
open DSharpPlus.SlashCommands
open Degenz.Messaging
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 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 payoutChance targetBank chance = targetBank * 0.1 * (1.0 - chance)
let getStealEmbed chance (target : PlayerData) (player : PlayerData) =
let buttons =
let yes , no = getRandomStealBtnLabels ()
[ DiscordButtonComponent(ButtonStyle.Success, $"Steal-yes-{target.DiscordId}-{target.Name}-{chance}", yes)
DiscordButtonComponent(ButtonStyle.Danger, $"Steal-no", no) ]
|> Seq.cast<DiscordComponent>
let embed =
DiscordEmbedBuilder()
.AddField("Chance of Success", $"{chanceOfSuccessMsg chance}", 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")
DiscordFollowupMessageBuilder()
.AddEmbed(embed)
.AddComponents(buttons)
.AsEphemeral(true)
let steal target (ctx : IDiscordContext) =
Game.executePlayerWithTargetAction target ctx (fun attacker defender -> async {
let winPercentage = if attacker.Stats.Strength > defender.Stats.Strength then 1.0 else 0.0
let embed = getStealEmbed winPercentage defender attacker
do! ctx.FollowUp(embed) |> Async.AwaitTask
})
let handleSteal (ctx : IDiscordContext) =
let split = ctx.GetInteractionId().Split("-")
let answer = split.[1]
if answer = "yes" then
Game.executePlayerAction ctx (fun player -> async {
let targetId = uint64 split.[2]
let targetName = split.[3]
let chance = double split.[4]
let rand = Random(Guid.NewGuid().GetHashCode())
let result = rand.NextDouble()
if chance >= result then
let! target = DbService.tryFindPlayer targetId
do! Messaging.sendFollowUpMessage ctx $"You stole {targetName} money"
else
do! Messaging.sendFollowUpMessage ctx "You failed miserably"
return ()
})
else
async {
let builder = DiscordInteractionResponseBuilder()
builder.Content <- "I thought better of it"
do! ctx.Respond InteractionResponseType.UpdateMessage builder |> Async.AwaitTask
} |> Async.StartAsTask :> Task
type StealGame() =
inherit ApplicationCommandModule ()
let enforceChannel (ctx : IDiscordContext) (storeFn : IDiscordContext -> Task) =
match ctx.GetChannel().Id with
| id when id = GuildEnvironment.channelArmory -> storeFn ctx
| _ ->
task {
let msg = $"You must go to <#{GuildEnvironment.channelArmory}> channel to buy or sell weapons"
do! Messaging.sendSimpleResponse ctx msg
}
[<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)

View File

@ -67,6 +67,8 @@ let private mapBack (player : PlayerEntry) : PlayerData = {
let atks = player.Attacks |> Array.map attackToAction let atks = player.Attacks |> Array.map attackToAction
let dfns = player.Defenses |> Array.map defenseToAction let dfns = player.Defenses |> Array.map defenseToAction
Array.append atks dfns Array.append atks dfns
Stats = PlayerStats.empty
XP = 0
Bank = player.Bank * 1<GBT> Bank = player.Bank * 1<GBT>
} }

View File

@ -74,12 +74,31 @@ module Types =
Type : ActionType Type : ActionType
Timestamp : DateTime } Timestamp : DateTime }
type StatAmount = int
type XPAmount = int
type AttributeId =
| Strength = 0
| Cunning = 1
type PlayerStats = {
Strength : int
Focus : int
}
with static member empty = { Strength = 0 ; Focus = 0 }
type PlayerXP = {
Amount : XPAmount
}
[<CLIMutable>] [<CLIMutable>]
type PlayerData = type PlayerData =
{ DiscordId : uint64 { DiscordId : uint64
Name : string Name : string
Arsenal : BattleItem array Arsenal : BattleItem array
Actions : Action array Actions : Action array
Stats : PlayerStats
XP : int
Bank : int<GBT> } Bank : int<GBT> }
module Armory = module Armory =
@ -175,6 +194,15 @@ module Messaging =
do! ctx.FollowUp(builder) |> Async.AwaitTask do! ctx.FollowUp(builder) |> Async.AwaitTask
} }
let sendFollowUpEmbed (ctx : IDiscordContext) embed =
async {
let builder =
DiscordFollowupMessageBuilder()
.AsEphemeral(true)
.AddEmbed(embed)
do! ctx.FollowUp(builder) |> Async.AwaitTask
}
let sendFollowUpMessageWithButton (ctx : IDiscordContext) interactiveMessage = let sendFollowUpMessageWithButton (ctx : IDiscordContext) interactiveMessage =
async { async {
let builder = DiscordFollowupMessageBuilder() let builder = DiscordFollowupMessageBuilder()