mirror of
https://github.com/barelyprofessional/KfChatDotNet.git
synced 2026-05-03 21:12:03 -04:00
Planes
This commit is contained in:
@@ -6,6 +6,8 @@ using KfChatDotNetBot.Services;
|
|||||||
using KfChatDotNetBot.Settings;
|
using KfChatDotNetBot.Settings;
|
||||||
using KfChatDotNetWsClient.Models.Events;
|
using KfChatDotNetWsClient.Models.Events;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using RandN;
|
||||||
|
using RandN.Compat;
|
||||||
|
|
||||||
namespace KfChatDotNetBot.Commands;
|
namespace KfChatDotNetBot.Commands;
|
||||||
|
|
||||||
@@ -242,5 +244,281 @@ public class KenoCommand : ICommand
|
|||||||
|
|
||||||
return numbers;
|
return numbers;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[KasinoCommand]
|
||||||
|
[WagerCommand]
|
||||||
|
public class Planes : ICommand
|
||||||
|
{
|
||||||
|
public List<Regex> Patterns => [
|
||||||
|
new Regex(@"^planes (?<amount>\d+)$", RegexOptions.IgnoreCase),
|
||||||
|
new Regex(@"^planes (?<amount>\d+\.\d+)$", RegexOptions.IgnoreCase),
|
||||||
|
new Regex("^planes$")
|
||||||
|
];
|
||||||
|
public string? HelpText => "!planes <bet amount>";
|
||||||
|
public UserRight RequiredRight => UserRight.Loser;
|
||||||
|
public TimeSpan Timeout => TimeSpan.FromSeconds(60);
|
||||||
|
public RateLimitOptionsModel? RateLimitOptions => new()
|
||||||
|
{
|
||||||
|
MaxInvocations = 3,
|
||||||
|
Window = TimeSpan.FromSeconds(30)
|
||||||
|
};
|
||||||
|
|
||||||
|
private const string PlaneUp = "🛫";
|
||||||
|
private const string PlaneDown = "🛬";
|
||||||
|
private const string Bomb = "💣";
|
||||||
|
private const string Multi = "💎";
|
||||||
|
private const string Carrier = "⛴";
|
||||||
|
private const string Water = "🌊";
|
||||||
|
private const string Air = "🌫";
|
||||||
|
private const string BlankSpace = "⠀"; //need 35?
|
||||||
|
|
||||||
|
public async Task RunCommand(ChatBot botInstance, MessageModel message, UserDbModel user, GroupCollection arguments,
|
||||||
|
CancellationToken ctx)
|
||||||
|
{
|
||||||
|
if (!arguments.TryGetValue("amount", out var amount))
|
||||||
|
{
|
||||||
|
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, not enough arguments. !planes <wager>", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var wager = Convert.ToDecimal(amount.Value);
|
||||||
|
var gambler = await Money.GetGamblerEntityAsync(user.Id, ct: ctx);
|
||||||
|
if (gambler == null)
|
||||||
|
throw new InvalidOperationException($"Caught a null when retrieving gambler for {user.KfUsername}");
|
||||||
|
if (gambler.Balance < wager)
|
||||||
|
{
|
||||||
|
await botInstance.SendChatMessageAsync(
|
||||||
|
$"{user.FormatUsername()}, your balance of {await gambler.Balance.FormatKasinoCurrencyAsync()} isn't enough for this wager.",
|
||||||
|
true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var carrierCount = 6;
|
||||||
|
var planesBoard = CreatePlanesBoard(gambler);
|
||||||
|
var planesBoard2 = CreatePlanesBoard(gambler);
|
||||||
|
var plane = new Plane(gambler);
|
||||||
|
|
||||||
|
var counter = 0;
|
||||||
|
var frameLength = 1056.0;
|
||||||
|
var noseUp = true;
|
||||||
|
var planesDisplay = GetGameBoard(counter, planesBoard, plane, carrierCount, noseUp);
|
||||||
|
var msgId = await botInstance.SendChatMessageAsync(planesDisplay, true);
|
||||||
|
//place where planes used to stop working
|
||||||
|
/*
|
||||||
|
* new goal of basic planes game
|
||||||
|
* static board, plane moves through the board, 25 spaces long
|
||||||
|
* if it gets to the end, reset the board, remember plane height, and continue playing no smooth transition
|
||||||
|
*/
|
||||||
|
do
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromMilliseconds(frameLength / 3), ctx);
|
||||||
|
var neutral = false;
|
||||||
|
var frameCounter = 0;
|
||||||
|
while (counter % 28 < 3)
|
||||||
|
{
|
||||||
|
planesDisplay = GetGameBoard(counter, planesBoard, plane, carrierCount, noseUp);
|
||||||
|
await botInstance.KfClient.EditMessageAsync(msgId.ChatMessageId!.Value, planesDisplay);
|
||||||
|
await Task.Delay(TimeSpan.FromMilliseconds(frameLength), ctx);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
while (!neutral)
|
||||||
|
{
|
||||||
|
frameCounter++;
|
||||||
|
switch (planesBoard[plane.Height, counter%28 - 3])
|
||||||
|
{
|
||||||
|
case 0: //do nothing plane hit neutral space
|
||||||
|
plane.Gravity();
|
||||||
|
neutral = true;
|
||||||
|
break;
|
||||||
|
case 1: //hit rocket
|
||||||
|
plane.HitRocket();
|
||||||
|
noseUp = false;
|
||||||
|
planesBoard[plane.Height, counter%28 - 3] = 0; //plane consumes rocket
|
||||||
|
break;
|
||||||
|
case 2: //hit multi
|
||||||
|
plane.HitMulti();
|
||||||
|
noseUp = true;
|
||||||
|
planesBoard[plane.Height, counter%28 - 3] = 0; //plane consumes multi
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await botInstance.SendChatMessageAsync("Something went wrong, error code 1.", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (neutral) //this will be the last frame so use all the remaining frame time left
|
||||||
|
{
|
||||||
|
if (frameCounter == 1) await Task.Delay(TimeSpan.FromMilliseconds(frameLength * 2 / 3), ctx); //first frame used 1/3 of frame time so 2/3 is remaining
|
||||||
|
else await Task.Delay(TimeSpan.FromMilliseconds(frameLength / (3 * (frameCounter - 1))), ctx);
|
||||||
|
}
|
||||||
|
else await Task.Delay(TimeSpan.FromMilliseconds(frameLength / (3 * frameCounter)), ctx); //if not the last frame use a fraction of the remaining frame time
|
||||||
|
planesDisplay = GetGameBoard(counter, planesBoard, plane, carrierCount, noseUp);
|
||||||
|
planesDisplay += $"[br]Multi: {plane.MultiTracker}x";
|
||||||
|
for (var i = 0; i < 33; i++)
|
||||||
|
{
|
||||||
|
planesDisplay += BlankSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
var winnings = plane.MultiTracker * wager;
|
||||||
|
planesDisplay += $"Winnings: {await winnings.FormatKasinoCurrencyAsync()}";
|
||||||
|
await botInstance.KfClient.EditMessageAsync(msgId.ChatMessageId!.Value, planesDisplay);
|
||||||
|
if (plane.Height >= 6)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//maybe fuckery around here
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
if (counter % 28 != 0) continue;
|
||||||
|
planesBoard = planesBoard2;
|
||||||
|
planesBoard2 = CreatePlanesBoard(gambler);
|
||||||
|
} while (plane.Height < 6);
|
||||||
|
//now plane is too low so you have either won or lost depending on your position
|
||||||
|
var colors =
|
||||||
|
await SettingsProvider.GetMultipleValuesAsync([
|
||||||
|
BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor
|
||||||
|
]);
|
||||||
|
var newBalance = gambler.Balance - wager;
|
||||||
|
if (counter % 28 % carrierCount == 0) //if you landed on the carrier
|
||||||
|
{
|
||||||
|
var win = plane.MultiTracker * wager;
|
||||||
|
newBalance = gambler.Balance + win;
|
||||||
|
await botInstance.SendChatMessageAsync(
|
||||||
|
$"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]successfully landed with {await win.FormatKasinoCurrencyAsync()} from a total {plane.MultiTracker:N2}x multi![/color]. Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
plane.Crash();
|
||||||
|
await botInstance.SendChatMessageAsync($"{user.FormatUsername()}, you [color={colors[BuiltIn.Keys.KiwiFarmsGreenColor].Value}]crashed![/color] Your balance is now: {await newBalance.FormatKasinoCurrencyAsync()}", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetGameBoard(int counter, int[,] planesBoard, Plane plane, int carrierCount, bool noseUp)
|
||||||
|
{
|
||||||
|
var output = "";
|
||||||
|
for (var row = 0; row < 8; row++)
|
||||||
|
{
|
||||||
|
for (var column = -3; column < 25; column++) //plane starts out 3 space behind to give some space to the view,
|
||||||
|
{
|
||||||
|
if (row == plane.Height && column == counter && noseUp)
|
||||||
|
{
|
||||||
|
output += PlaneUp;
|
||||||
|
}
|
||||||
|
else if (row == plane.Height && column == counter && !noseUp)
|
||||||
|
{
|
||||||
|
output += PlaneDown;
|
||||||
|
}
|
||||||
|
else if (column < 0) //beginning columns have no multis or bombs or carriers just air and water
|
||||||
|
{
|
||||||
|
if (row != 7) output += Air;
|
||||||
|
else output += Water;
|
||||||
|
}
|
||||||
|
else if (row == 6)//row between the gameboard and where the carrier is displayed, should show the plane in this row on top of the boat on a win
|
||||||
|
{
|
||||||
|
output += Air;
|
||||||
|
}
|
||||||
|
else if (row == 7) //water/carrier row
|
||||||
|
{
|
||||||
|
if (column % carrierCount == 0) output += Carrier;
|
||||||
|
else output += Water;
|
||||||
|
}
|
||||||
|
else //this leaves rows 0-5 and columns 0-24, exactly what we need for the board
|
||||||
|
{
|
||||||
|
switch (planesBoard[row, column])
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
output += Air;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
output += Bomb;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
output += Multi;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output += "[br]";
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int[,] CreatePlanesBoard(GamblerDbModel gambler)
|
||||||
|
{
|
||||||
|
var board = new int [6, 25];
|
||||||
|
for (var row = 0; row < 6; row++)
|
||||||
|
{
|
||||||
|
for (var column = 0; column < 25; column++)
|
||||||
|
{
|
||||||
|
var randomNum = Money.GetRandomNumber(gambler, 1, 100);
|
||||||
|
if (randomNum < 35)
|
||||||
|
{
|
||||||
|
board[row, column] = 0; //neutral
|
||||||
|
}
|
||||||
|
else if (randomNum > 69)
|
||||||
|
{
|
||||||
|
board[row, column] = 1; //rocket
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
board[row, column] = 2; //multi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return board;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class Plane(GamblerDbModel gambler)
|
||||||
|
{
|
||||||
|
public int Height = 1;
|
||||||
|
public decimal MultiTracker = 1;
|
||||||
|
private int _justHitMulti = 1;
|
||||||
|
private readonly RandomShim<StandardRng> _random = RandomShim.Create(StandardRng.Create());
|
||||||
|
|
||||||
|
public void HitRocket()
|
||||||
|
{
|
||||||
|
Gravity();
|
||||||
|
MultiTracker /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Gravity()
|
||||||
|
{
|
||||||
|
if (_justHitMulti > 0)
|
||||||
|
{
|
||||||
|
_justHitMulti--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Height++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Crash()
|
||||||
|
{
|
||||||
|
MultiTracker = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HitMulti()
|
||||||
|
{
|
||||||
|
var randomNum = Money.GetRandomNumber(gambler, 0, 1);
|
||||||
|
var weightedRand = WeightedRandomNumber(1, 10);
|
||||||
|
if (randomNum == 0)
|
||||||
|
{
|
||||||
|
MultiTracker += weightedRand;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MultiTracker *= weightedRand;
|
||||||
|
}
|
||||||
|
|
||||||
|
Height--;
|
||||||
|
_justHitMulti++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int WeightedRandomNumber(int min, int max)
|
||||||
|
{
|
||||||
|
var range = max - min + 1;
|
||||||
|
var weight = 4.5 + Height;
|
||||||
|
var r = _random.NextDouble();
|
||||||
|
var exp = -Math.Log(1 - r) / weight;
|
||||||
|
var returnVal = min + (int)Math.Round(exp * range);
|
||||||
|
return Math.Clamp(returnVal, min, max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user