New stuff (#99)

* rain control

* blackjack double payout fix. minor display fixes
This commit is contained in:
CrackmaticSoftware
2026-03-14 21:59:34 +01:00
committed by GitHub
parent 75addfb185
commit af7b5e027b
2 changed files with 82 additions and 68 deletions

View File

@@ -157,12 +157,13 @@ public class BlackjackCommand : ICommand
deck.RemoveRange(0, 4); deck.RemoveRange(0, 4);
// Create game state // Create game state
// HasDoubledDown is a single bool — doubles are not permitted after a split
var newGameState = new BlackjackGameMetaModel var newGameState = new BlackjackGameMetaModel
{ {
PlayerHands = new List<List<Card>> { playerHand }, PlayerHands = new List<List<Card>> { playerHand },
DealerHand = dealerHand, DealerHand = dealerHand,
Deck = deck, Deck = deck,
HasDoubledDown = new List<bool> { false }, HasDoubledDown = false,
CurrentHandIndex = 0, CurrentHandIndex = 0,
OriginalWagerAmount = wager OriginalWagerAmount = wager
}; };
@@ -302,58 +303,49 @@ public class BlackjackCommand : ICommand
var playerValue = BlackjackHelper.CalculateHandValue(currentHand); var playerValue = BlackjackHelper.CalculateHandValue(currentHand);
if (playerValue > 21) // Whether this hit ends the current hand (bust, 21, or post-double auto-stand)
{ bool handEnded = playerValue > 21 || playerValue == 21 || gameState.HasDoubledDown;
// Bust - player loses
var colors = await SettingsProvider.GetMultipleValuesAsync([
BuiltIn.Keys.KiwiFarmsGreenColor, BuiltIn.Keys.KiwiFarmsRedColor
]);
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}{handLabel} hit and drew {card}[br]" +
$"[B]Your hand:[/B] {BlackjackHelper.FormatHand(currentHand)} = {playerValue}[br]" +
$"[B][COLOR={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]BUST![/COLOR][/B]",
true, autoDeleteAfter: cleanupDelay);
// Move to next hand or resolve game
await MoveToNextHandOrResolve(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
return;
}
// Auto-stand on 21
if (playerValue == 21)
{
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}{handLabel} hit and drew {card}[br]" +
$"[B]Your hand:[/B] {BlackjackHelper.FormatHand(currentHand)} = {playerValue}[br]" +
$"[B]Standing on 21[/B]",
true, autoDeleteAfter: cleanupDelay);
await MoveToNextHandOrResolve(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
return;
}
if (gameState.HasDoubledDown[gameState.CurrentHandIndex])
{
// Auto-stand after double down hit
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}{handLabel} hit and drew {card}[br]" +
$"[B]Your hand:[/B] {BlackjackHelper.FormatHand(currentHand)} = {playerValue}",
true, autoDeleteAfter: cleanupDelay);
await MoveToNextHandOrResolve(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
return;
}
// Continue game // Whether this is the last hand — if so, skip the intermediate message and let ResolveGame
wager.GameMeta = JsonSerializer.Serialize(gameState); // produce the single consolidated output, avoiding a duplicate bust/result message.
await _dbContext.SaveChangesAsync(ctx); bool isLastHand = gameState.CurrentHandIndex >= gameState.PlayerHands.Count - 1;
if (handEnded && !isLastHand)
{
// Transitioning between split hands: show what happened on this hand before moving on
string transitionalResult;
if (playerValue > 21)
{
var colors = await SettingsProvider.GetMultipleValuesAsync([BuiltIn.Keys.KiwiFarmsRedColor]);
transitionalResult = $"[B][COLOR={colors[BuiltIn.Keys.KiwiFarmsRedColor].Value}]BUST![/COLOR][/B]";
}
else
{
transitionalResult = $"[B]Standing on {playerValue}[/B]";
}
await botInstance.SendChatMessageAsync( await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}{handLabel} hit and drew {card}[br]" + $"{user.FormatUsername()}{handLabel} hit and drew {card}[br]" +
$"[B]Your hand:[/B] {BlackjackHelper.FormatHand(currentHand)} = {playerValue}[br]" + $"[B]Your hand:[/B] {BlackjackHelper.FormatHand(currentHand)} = {playerValue}[br]" +
$"Use [B]!bj hit[/B] or [B]!bj stand[/B] to continue", transitionalResult,
true, autoDeleteAfter: cleanupDelay); true, autoDeleteAfter: cleanupDelay);
}
else if (!handEnded)
{
// Hand is still in progress — show current state and prompt for next action
wager.GameMeta = JsonSerializer.Serialize(gameState);
await _dbContext.SaveChangesAsync(ctx);
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}{handLabel} hit and drew {card}[br]" +
$"[B]Your hand:[/B] {BlackjackHelper.FormatHand(currentHand)} = {playerValue}[br]" +
$"Use [B]!bj hit[/B] or [B]!bj stand[/B] to continue",
true, autoDeleteAfter: cleanupDelay);
return;
}
// If handEnded && isLastHand: fall through silently — ResolveGame will show everything
await MoveToNextHandOrResolve(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
} }
private async Task HandleStand(ChatBot botInstance, UserDbModel user, GamblerDbModel gambler, private async Task HandleStand(ChatBot botInstance, UserDbModel user, GamblerDbModel gambler,
@@ -363,9 +355,15 @@ public class BlackjackCommand : ICommand
var currentHand = gameState.PlayerHands[gameState.CurrentHandIndex]; var currentHand = gameState.PlayerHands[gameState.CurrentHandIndex];
var playerValue = BlackjackHelper.CalculateHandValue(currentHand); var playerValue = BlackjackHelper.CalculateHandValue(currentHand);
await botInstance.SendChatMessageAsync( // Only send an intermediate stand message when transitioning between split hands.
$"{user.FormatUsername()}{handLabel} stands with {BlackjackHelper.FormatHand(currentHand)} = {playerValue}", // For the final/only hand, ResolveGame produces the sole consolidated message.
true, autoDeleteAfter: cleanupDelay); bool isLastHand = gameState.CurrentHandIndex >= gameState.PlayerHands.Count - 1;
if (!isLastHand)
{
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}{handLabel} stands with {BlackjackHelper.FormatHand(currentHand)} = {playerValue}",
true, autoDeleteAfter: cleanupDelay);
}
await MoveToNextHandOrResolve(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx); await MoveToNextHandOrResolve(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
} }
@@ -384,6 +382,15 @@ public class BlackjackCommand : ICommand
return; return;
} }
// Doubling after a split is not permitted
if (gameState.PlayerHands.Count > 1)
{
await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}, you cannot double down after splitting.",
true, autoDeleteAfter: cleanupDelay);
return;
}
// Check if player has enough balance for double // Check if player has enough balance for double
if (gambler.Balance < gameState.OriginalWagerAmount) if (gambler.Balance < gameState.OriginalWagerAmount)
{ {
@@ -393,22 +400,21 @@ public class BlackjackCommand : ICommand
return; return;
} }
// Double the wager // Double the wager: charge the additional amount and record it
var additionalWager = wager.WagerAmount; var additionalWager = gameState.OriginalWagerAmount;
await Money.ModifyBalanceAsync(gambler.Id, -additionalWager, TransactionSourceEventType.Gambling, await Money.ModifyBalanceAsync(gambler.Id, -additionalWager, TransactionSourceEventType.Gambling,
$"Double down for {wager.Id}", ct: ctx); $"Double down for {wager.Id}", ct: ctx);
wager.WagerAmount *= 2; wager.WagerAmount += additionalWager; // Total wager is now OriginalWagerAmount * 2
wager.WagerEffect -= additionalWager; // Subtract the additional wager wager.WagerEffect -= additionalWager; // Outstanding loss reflects the extra stake
gameState.HasDoubledDown[gameState.CurrentHandIndex] = true; gameState.HasDoubledDown = true;
await _dbContext.SaveChangesAsync(ctx); await _dbContext.SaveChangesAsync(ctx);
var handLabel = gameState.PlayerHands.Count > 1 ? $" (Hand {gameState.CurrentHandIndex + 1})" : "";
await botInstance.SendChatMessageAsync( await botInstance.SendChatMessageAsync(
$"{user.FormatUsername()}{handLabel} doubled down! Wager is now {await wager.WagerAmount.FormatKasinoCurrencyAsync()}", $"{user.FormatUsername()} doubled down! Wager is now {await wager.WagerAmount.FormatKasinoCurrencyAsync()}",
true, autoDeleteAfter: cleanupDelay); true, autoDeleteAfter: cleanupDelay);
// Draw one card and auto-stand // Draw exactly one card then auto-stand (handled inside HandleHit via HasDoubledDown flag)
await HandleHit(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx); await HandleHit(botInstance, user, gambler, wager, gameState, cleanupDelay, ctx);
} }
@@ -466,7 +472,7 @@ public class BlackjackCommand : ICommand
gameState.Deck.RemoveRange(0, 2); gameState.Deck.RemoveRange(0, 2);
gameState.PlayerHands = new List<List<Card>> { hand1, hand2 }; gameState.PlayerHands = new List<List<Card>> { hand1, hand2 };
gameState.HasDoubledDown = new List<bool> { false, false }; gameState.HasDoubledDown = false; // Single bool — doubles are blocked after splitting
gameState.CurrentHandIndex = 0; gameState.CurrentHandIndex = 0;
// Charge for the split // Charge for the split
@@ -497,7 +503,7 @@ public class BlackjackCommand : ICommand
if (gameState.CurrentHandIndex < gameState.PlayerHands.Count) if (gameState.CurrentHandIndex < gameState.PlayerHands.Count)
{ {
// Move to next hand // Move to next split hand
wager.GameMeta = JsonSerializer.Serialize(gameState); wager.GameMeta = JsonSerializer.Serialize(gameState);
await _dbContext.SaveChangesAsync(ctx); await _dbContext.SaveChangesAsync(ctx);
@@ -563,15 +569,23 @@ public class BlackjackCommand : ICommand
var message = $"🃏 {user.FormatUsername()}'s blackjack game:[br]"; var message = $"🃏 {user.FormatUsername()}'s blackjack game:[br]";
// For a split game each hand pays out at the original wager per hand.
// For a single hand (with optional double down) the full wager.WagerAmount is at stake,
// which already reflects the doubled stake when the player doubled down.
bool isSplitGame = gameState.PlayerHands.Count > 1;
// Process each hand // Process each hand
for (int i = 0; i < gameState.PlayerHands.Count; i++) for (int i = 0; i < gameState.PlayerHands.Count; i++)
{ {
var hand = gameState.PlayerHands[i]; var hand = gameState.PlayerHands[i];
var playerValue = BlackjackHelper.CalculateHandValue(hand); var playerValue = BlackjackHelper.CalculateHandValue(hand);
var playerBlackjack = BlackjackHelper.IsBlackjack(hand); var playerBlackjack = BlackjackHelper.IsBlackjack(hand);
var handWager = gameState.OriginalWagerAmount;
var handLabel = gameState.PlayerHands.Count > 1 ? $" {i + 1}" : ""; // Split hands each pay at the original wager amount.
// A single hand pays at the full (possibly doubled) wager amount.
var handWager = isSplitGame ? gameState.OriginalWagerAmount : wager.WagerAmount;
var handLabel = isSplitGame ? $" {i + 1}" : "";
message += $"[B]Your hand{handLabel}:[/B] {BlackjackHelper.FormatHand(hand)} = {playerValue}[br]"; message += $"[B]Your hand{handLabel}:[/B] {BlackjackHelper.FormatHand(hand)} = {playerValue}[br]";
decimal handEffect; decimal handEffect;
@@ -651,4 +665,4 @@ public class BlackjackCommand : ICommand
$"{user.FormatUsername()}, your blackjack game timed out and you forfeited {await wager.WagerAmount.FormatKasinoCurrencyAsync()}", $"{user.FormatUsername()}, your blackjack game timed out and you forfeited {await wager.WagerAmount.FormatKasinoCurrencyAsync()}",
true, autoDeleteAfter: cleanupDelay); true, autoDeleteAfter: cleanupDelay);
} }
} }

View File

@@ -23,7 +23,7 @@ public class BlackjackGameMetaModel
/// <summary> /// <summary>
/// Whether each hand has doubled down (can only hit once more) /// Whether each hand has doubled down (can only hit once more)
/// </summary> /// </summary>
public required List<bool> HasDoubledDown { get; set; } public required bool HasDoubledDown { get; set; }
/// <summary> /// <summary>
/// Current hand being played (for split hands) /// Current hand being played (for split hands)