#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "game.h" #include "deck.h" #include "hand.h" #include "score.h" #include "merc.h" void ShowValidCommands( CHAR_DATA *ch ) { static char buf[MAX_STRING_LENGTH]; static char commands[MAX_STRING_LENGTH]; commands[0] = '\0'; if ( ch->deck != NULL && ch->hand != NULL ) { if ( GameViewHand(ch->deck->Game, ch->deck->Turn) ) strcat( commands, " hand" ); if ( GameCanBet(ch->deck->Game, ch->deck->Turn) ) strcat( commands, " bet" ); if ( GameCanFold(ch->deck->Game, ch->deck->Turn) ) strcat( commands, " fold" ); if ( GameCanDiscard(ch->deck->Game, ch->deck->Turn) ) strcat( commands, " discard" ); if ( GameCanDraw(ch->deck->Game, ch->deck->Turn) ) strcat( commands, " draw" ); if ( GameCanStick(ch->deck->Game, ch->deck->Turn) ) strcat( commands, " stick" ); if ( GameCanDouble(ch->deck->Game, ch->deck->Turn) ) strcat( commands, " double" ); } if ( commands[0] == '\0' ) strcpy( commands, " none" ); sprintf( buf, "Card commands for turn %d:", ch->deck->Turn ); strcat( buf, commands ); strcat( buf, "\n\r" ); send_to_char( buf, ch ); } void FlushPlayers( CHAR_DATA *ch ) { if ( ch->deck != NULL && ch->hand != NULL ) { CHAR_DATA *current, *next = NULL; for ( current = ch; current != NULL; current = next ) { CHAR_DATA *prev = current->prev_card_player; next = current->next_card_player; /* Dispose of their hand and deck */ if ( --current->deck->Instances <= 0 ) free( current->deck ); current->deck = NULL; free( current->hand ); current->hand = NULL; /* Strip the character from the player list */ if ( prev != NULL ) { prev->next_card_player = next; current->prev_card_player = NULL; } if ( next != NULL ) { next->prev_card_player = prev; current->next_card_player = NULL; } } } } void RevealScore( CHAR_DATA *ch ) { if ( ch->deck != NULL && ch->hand != NULL ) { char buf[MAX_STRING_LENGTH]; CHAR_DATA *current, *winner = ch; combination_t HighCombination = eCombinationHighCard; int TopScore = -1; int WinnerCount = 0; char HighScore[16] = {'\0'}; int gold = ch->deck->Pot; for ( current = ch; current != NULL; current = current->next_card_player ) { /* Calculate score */ sprintf( buf, "You reveal your hand: %s\n\r", HandShow(current->hand, eFalse) ); send_to_char( buf, current ); sprintf( buf, "$n reveals $s hand: %s", HandShow(current->hand, eFalse) ); act( buf, current, NULL, NULL, TO_ROOM ); if ( GameScoring(ch->deck->Game) == eScorePoker ) { combination_t Combination = eCombinationMax; strncpy( buf, ScorePoker(current->hand, &Combination), 15 ); if ( Combination > HighCombination ) { /* We've a new high score */ HighCombination = Combination; strcpy( HighScore, buf ); winner = current; } else if ( Combination == HighCombination ) { int i; /* Loop counter */ for ( i = 0; HighScore[i] != '\0' && buf[i] != '\0'; ++i ) { if ( HighScore[i] > buf[i] ) { /* We've a new high score */ HighCombination = Combination; strcpy( HighScore, buf ); winner = current; } } } } else if ( GameScoring(ch->deck->Game) == eScoreBlackjack ) { int Score = ScoreBlackjack(current->hand); if ( Score > TopScore ) { /* We've a new high score */ TopScore = Score; winner = current; WinnerCount = 1; } else if ( Score == TopScore ) { /* We've more than one winner */ WinnerCount++; } } else /* Add support for your other scoring systems here */ { bug( "RevealScore: Function called with unsupported scoring system.\n", 0 ); } } if ( GameSoloBet(ch->deck->Game) ) { /* Find the dealer */ CHAR_DATA *dealer = NULL; for ( current = ch; current != NULL; current = current->next_card_player ) { if ( current->hand->bDealer ) dealer = current; } if ( dealer != NULL ) { int DealerScore = ScoreBlackjack(dealer->hand); for ( current = ch; current != NULL; current = current->next_card_player ) { if ( current != dealer ) { int Score = ScoreBlackjack(current->hand); if ( Score == 21 && current->hand->Size == 2 ) { int Winnings = (current->hand->Bet*5)/2; /* 3:2 payout */ sprintf( buf, "\n\rBlackjack! You collect %d gold.\n\r", Winnings ); send_to_char( buf, current ); sprintf( buf, "\n\rBLACKJACK! $n collects %d gold.", Winnings ); act( buf, current, NULL, NULL, TO_ROOM ); /* You get twice your bet back */ current->gold += Winnings; current->deck->Pot -= Winnings; } else if ( Score > 0 && (Score > DealerScore || Score == 21) ) { int Winnings = current->hand->Bet*2; /* 1:1 payout */ sprintf( buf, "\n\rYou win! You collect %d gold.\n\r", Winnings ); send_to_char( buf, current ); sprintf( buf, "\n\rWINNER! $n collects %d gold.", Winnings ); act( buf, current, NULL, NULL, TO_ROOM ); /* You get twice your bet back */ current->gold += Winnings; current->deck->Pot -= Winnings; } else if ( Score == DealerScore ) { sprintf( buf, "\n\rYou match the house. You recover your %d gold bet.\n\r", current->hand->Bet ); send_to_char( buf, current ); sprintf( buf, "\n\r$n matches the house, and recovers $s %d gold bet.", current->hand->Bet ); act( buf, current, NULL, NULL, TO_ROOM ); /* You get twice your bet back */ current->gold += current->hand->Bet; current->deck->Pot -= current->hand->Bet; } else /* The player loses */ { send_to_char( "\n\rYou lose to the house.\n\r", current ); } } } /* The dealer empties the pot */ if ( dealer->deck->Pot > 0 ) { dealer->gold += dealer->deck->Pot; sprintf( buf, "\n\rYou empty the remaining %d gold from the pot.\n\r", dealer->deck->Pot ); send_to_char( buf, dealer ); sprintf( buf, "\n\r$n collects the remaining %d gold from the pot.\n\r", dealer->deck->Pot ); act( buf, dealer, NULL, NULL, TO_ROOM ); } else if ( dealer->deck->Pot < 0 ) { dealer->deck->Pot = abs(dealer->deck->Pot); dealer->gold -= dealer->deck->Pot; sprintf( buf, "\n\rYou had to add an extra %d gold to the pot.\n\r", dealer->deck->Pot ); send_to_char( buf, dealer ); } } } else if ( WinnerCount > 1 ) { for ( current = ch; current != NULL; current = current->next_card_player ) { int Score = ScoreBlackjack(current->hand); if ( Score > 0 && Score == TopScore ) { sprintf( buf, "\n\rYou're one of %d winners! You collect %d gold from the pot.\n\r", WinnerCount, gold/WinnerCount ); send_to_char( buf, current ); sprintf( buf, "\n\rJOINT WIN! $n collects %d gold from the pot.\n\r", gold/WinnerCount ); act( buf, current, NULL, NULL, TO_ROOM ); winner->gold += (gold/WinnerCount); } } } else /* There was a clear winner */ { sprintf( buf, "\n\rYou win! You collect %d gold from the pot.\n\r", gold ); send_to_char( buf, winner ); sprintf( buf, "\n\rWINNER! $n collects %d gold from the pot.\n\r", gold ); act( buf, winner, NULL, NULL, TO_ROOM ); winner->gold += gold; } /* Flush the players out of the game */ FlushPlayers(ch); } } void ShareCard( CHAR_DATA *ch, card_t card, boolean_t show ) { if ( ch->deck != NULL && ch->hand != NULL ) { CHAR_DATA *next; for ( next = ch; next != NULL; next = next->next_card_player ) { /* Give each player a copy of the card */ (void)HandAddCard(next->hand, card); /* Show the player their new hand, if required */ if ( show ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "Including shared cards, you now have the following hand: %s\n\r\n\r", HandShow(next->hand, eFalse) ); send_to_char( buf, next ); send_to_char( HandShow(next->hand, eTrue), next ); } } } } void NextPlayer( CHAR_DATA *ch, bool bCheckWin ) { char buf[MAX_STRING_LENGTH]; if ( ch->deck != NULL && ch->hand != NULL ) { /* You've drawn one or more cards, so your turn is over */ ch->play_turn = FALSE; if ( GameSoloBet(ch->deck->Game) ) { sprintf( buf, "It's now your turn (current bet: %dgp).\n\r\n\r", ch->hand->Bet ); } else /* Its a shared bet */ { sprintf( buf, "It's now your turn (pot: %dgp, current bet: %dgp).\n\r\n\r", ch->deck->Pot, ch->deck->Bet ); } if ( ch->next_card_player != NULL ) { /* It's now the next players turn */ CHAR_DATA *next = ch->next_card_player; next->play_turn = TRUE; /* If there are two players and one just lost, the other wins */ if ( bCheckWin && next->deck->Instances == 2 ) { /* Reveal the scores */ RevealScore( next ); } else /* There are still other players */ { send_to_char( buf, next ); ShowValidCommands( next ); act( "It is now $n's turn.", next, NULL, NULL, TO_ROOM ); } } else /* We've reached the last player, so go back to the first one */ { CHAR_DATA *prev = ch->prev_card_player; if ( prev == NULL ) { /* There's only one player, so make them win */ ch->play_turn = TRUE; ch->deck->Turn = GameLastTurn(ch->deck->Game); RevealScore( ch ); } else /* Keep going back until we reach the start */ { while ( prev->prev_card_player != NULL ) prev = prev->prev_card_player; /* The first player has a turn again */ prev->play_turn = TRUE; /* If there are two players and one just lost, the other wins */ if ( bCheckWin && prev->deck->Instances == 2 ) { /* Reveal the scores */ RevealScore( prev ); } else if ( ++prev->deck->Turn > GameLastTurn(prev->deck->Game) ) { /* Reveal the scores */ RevealScore( prev ); } else /* The game is not yet finished */ { int BurnedCards = GameBurnCards(prev->deck->Game, prev->deck->Turn); int SharedCards = GameSharedDraw(prev->deck->Game, prev->deck->Turn); send_to_char( buf, prev ); ShowValidCommands( prev ); act( "It is now $n's turn.", prev, NULL, NULL, TO_ROOM ); /* Any card burning should be done now */ if ( BurnedCards > 0 ) { sprintf( buf, "You burn %d card%s from the top of the deck.\n\r", BurnedCards, BurnedCards == 1 ? "" : "s" ); send_to_char( buf, prev ); sprintf( buf, "$n burns %d card%s from the top of the deck.", BurnedCards, BurnedCards == 1 ? "" : "s" ); act( buf, prev, NULL, NULL, TO_ROOM ); for ( ; BurnedCards > 0; --BurnedCards ) { /* Draw a card and cast it into the void */ (void)DeckDrawCard(prev->deck); } } /* If there are any shared cards to be placed, do so now */ if ( SharedCards > 0 ) { sprintf( buf, "You draw %d new card%s from the deck and place %s face-up on the table.\n\r", SharedCards, SharedCards == 1 ? "" : "s", SharedCards == 1 ? "it" : "them" ); send_to_char( buf, prev ); sprintf( buf, "$n draws %d new card%s from the deck and places %s face-up on the table.", SharedCards, SharedCards == 1 ? "" : "s", SharedCards == 1 ? "it" : "them" ); act( buf, prev, NULL, NULL, TO_ROOM ); for ( ; SharedCards > 0; --SharedCards ) { /* Draw a card and give each player a copy */ card_t card = DeckDrawCard(prev->deck); ShareCard( prev, card, SharedCards == 1 ); } } } } } } } void do_bet( CHAR_DATA *ch, char *argument ) { char buf[MAX_STRING_LENGTH]; if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to start a game before you can bet.\n\r", ch ); } else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) ) { send_to_char( "The game is already over.\n\r", ch ); } else if ( !GameCanBet(ch->deck->Game, 0) ) { sprintf( buf, "You cannot place bets in %s.\n\r", GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( !GameCanBet(ch->deck->Game, ch->deck->Turn) ) { sprintf( buf, "You cannot place bets in turn %d of %s.\n\r", ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( ch->deck->Turn > 0 && ch->deck->Playing == eFalse ) { send_to_char( "You cannot bet until the first hand has been dealt.\n\r", ch ); } else if ( ch->play_turn == FALSE ) { send_to_char( "You can only bet on your turn.\n\r", ch ); } else if ( argument[0] == '\0' ) { if ( ch->deck->Turn == 0 ) { send_to_char( "How much do you want to set the minimum bet to?\n\r", ch ); } else if ( GameSoloBet(ch->deck->Game) ) { sprintf( buf, "How much do you wish to bet? Your current bet is %d gold.\n\r", ch->hand->Bet ); send_to_char( buf, ch ); } else /* Its a shared bet */ { sprintf( buf, "How much do you wish to bet? The current bet is %d gold.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); } } else if ( !strcmp( argument, "call" ) ) { if ( ch->deck->Turn == 0 ) { send_to_char( "You cannot 'bet call' for the minimum bet.\n\r", ch ); } else if ( ch->gold < ch->deck->Bet ) { send_to_char( "You don't have enough gold to call the current bet. You'll have to fold.\n\r", ch ); } else /* They can call the current bet */ { if ( ch->deck->Bet == 0 ) { send_to_char( "You defer to the next player.\n\r", ch ); act( "$n defers, chosing not to place a bet.", ch, NULL, NULL, TO_ROOM ); } else /* It's a proper bet */ { /* Update the pot and the characters gold */ ch->hand->Bet = ch->deck->Bet; ch->deck->Pot += ch->deck->Bet; ch->gold -= ch->deck->Bet; sprintf( buf, "Your call the current bet of %d gold.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); sprintf( buf, "$n calls the current bet of %d gold.", ch->deck->Bet ); act( buf, ch, NULL, NULL, TO_ROOM ); } /* Turn over - next player */ NextPlayer(ch, FALSE); } } else if ( !is_number(argument) ) { if ( GameSoloBet(ch->deck->Game) ) { sprintf( buf, "Please specify a number of gold pieces to bet. The current bet is %d gold.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); } else /* Its a shared bet */ { sprintf( buf, "Please specify a number of gold pieces to bet. Your current bet is %d gold.\n\r", ch->hand->Bet ); send_to_char( buf, ch ); } } else if ( ch->deck->Turn == 0 ) { int gold = atoi(argument); if ( ch->deck->Bet > 0 ) { sprintf( buf, "You've already set a minimum bet of %d gold. You should now 'deal'.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); } else if ( gold <= 0 ) { send_to_char( "You must set a minimum bet of at least 1 gold.\n\r", ch ); } else if ( gold > ch->gold ) { send_to_char( "You don't have enough gold to bet that much.\n\r", ch ); } else /* It's a valid bet */ { /* Set the bet - but don't remove gold at this point */ ch->hand->Bet = gold; ch->deck->Bet = gold; if ( GameMinimumBet(ch->deck->Game) ) { sprintf( buf, "Minimum bet is set to %d gold. Type 'deal' to start the game.\n\r", gold ); send_to_char( buf, ch ); sprintf( buf, "$n begins a game of %s with a minimum bet of %d gold. Type 'play' to join.", GameName(ch->deck->Game), gold ); act( buf, ch, NULL, NULL, TO_ROOM ); } else /* A minimum bet wasn't required */ { sprintf( buf, "You set the minimum bet to %d gold. You should now 'deal'.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); sprintf( buf, "$n sets the minimum bet to %d gold.", ch->deck->Bet ); act( buf, ch, NULL, NULL, TO_ROOM ); } } } else if ( GameSoloBet(ch->deck->Game) ) { int gold = atoi(argument); if ( gold < ch->deck->Bet ) { sprintf( buf, "You must at least match the minimum bet of %d gold.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); } else if ( gold <= ch->hand->Bet ) { sprintf( buf, "You must exceed your current bet of %d gold.\n\r", ch->hand->Bet ); send_to_char( buf, ch ); } else if ( gold > ch->gold ) { send_to_char( "You don't have enough gold to bet that much.\n\r", ch ); } else /* It's a valid bet */ { /* Remove their old bet, so that it can be replaced */ ch->deck->Pot -= ch->hand->Bet; ch->gold += ch->hand->Bet; /* Apply the new bet, update the pot, and the characters gold */ ch->hand->Bet = gold; ch->deck->Bet = gold; ch->deck->Pot += gold; ch->gold -= gold; sprintf( buf, "Your raise your bet to %d gold.\n\r", ch->hand->Bet ); send_to_char( buf, ch ); sprintf( buf, "$n raises $s bet to %d gold.", ch->hand->Bet ); act( buf, ch, NULL, NULL, TO_ROOM ); /* Turn over - next player */ NextPlayer(ch, FALSE); } } else /* We've got a numeric value of gold */ { int gold = atoi(argument); if ( gold <= ch->deck->Bet ) { sprintf( buf, "You must exceed the current bet of %d gold, or 'bet call' to match it.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); } else if ( gold > ch->gold ) { send_to_char( "You don't have enough gold to bet that much.\n\r", ch ); } else /* It's a valid bet */ { /* Remove their old bet, so that it can be replaced */ ch->deck->Pot -= ch->hand->Bet; ch->gold += ch->hand->Bet; /* Update the bet, the pot, and the characters gold */ ch->hand->Bet = gold; ch->deck->Bet = gold; ch->deck->Pot += gold; ch->gold -= gold; sprintf( buf, "Your raise the bet to %d gold.\n\r", ch->deck->Bet ); send_to_char( buf, ch ); sprintf( buf, "$n raises the bet to %d gold.", ch->deck->Bet ); act( buf, ch, NULL, NULL, TO_ROOM ); /* Turn over - next player */ NextPlayer(ch, FALSE); } } } void do_deal( CHAR_DATA *ch, char *argument ) { if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to start a game before you can deal.\n\r", ch ); } else if ( ch->deck->Playing == eTrue ) { send_to_char( "You can only deal at the start of a game.\n\r", ch ); } else if ( ch->play_turn == FALSE ) { send_to_char( "You're not the dealer.\n\r", ch ); } else if ( GameMinimumBet(ch->deck->Game) && ch->deck->Bet <= 0 ) { send_to_char( "You must first set the minimum bet (eg 'bet 10').\n\r", ch ); } else if ( ch->deck->Instances < 2 ) { send_to_char( "You need at least 2 players for a game of cards.\n\r", ch ); } else /* Go ahead and deal the cards */ { int Deal = GameInitialDraw(ch->deck->Game); /* Indicate that the game has begun */ ch->deck->Playing = eTrue; while ( HandCountCards(ch->hand) < Deal ) { CHAR_DATA *prev; for ( prev = ch; prev != NULL; prev = prev->prev_card_player ) { /* Draw a card from the deck and put it in their hand */ card_t card = DeckDrawCard(prev->deck); (void)HandAddCard(prev->hand, card); if ( HandCountCards(prev->hand) == Deal ) { char buf[MAX_STRING_LENGTH]; /* Check explicitly for turn 1 (we're in turn 0) */ if ( GameViewHand(ch->deck->Game, 1) ) { if ( ch == prev ) { sprintf( buf, "You deal yourself %d cards: %s\n\r\n\r", Deal, HandShow(prev->hand, eFalse) ); send_to_char( buf, ch ); } else /* Dealt to someone else */ { sprintf( buf, "$n deals you %d cards: %s\n\r", Deal, HandShow(prev->hand, eFalse) ); act( buf, ch, NULL, prev, TO_VICT ); } send_to_char( HandShow(prev->hand, eTrue), prev ); } else /* We're not allowed to show them the cards */ { if ( ch == prev ) { sprintf( buf, "You deal yourself %d cards face-down on the table.\n\r", Deal ); send_to_char( buf, ch ); if ( ch->deck->Game == eGameBlackjack ) { sprintf( buf, "You flip one of the cards over to reveal the %s.\n\r", CardName(card) ); send_to_char( buf, ch ); sprintf( buf, "$n deals $mself %d cards, one of which he reveals as the %s.", Deal, CardName(card) ); act( buf, ch, NULL, NULL, TO_ROOM ); } } else /* Dealt to someone else */ { sprintf( buf, "$n deals you %d cards face-down on the table.", Deal ); act( buf, ch, NULL, prev, TO_VICT ); } } } } } /* Now we begin turn 1 */ NextPlayer(ch, FALSE); } } void do_discard( CHAR_DATA *ch, char *argument ) { char buf[MAX_STRING_LENGTH]; card_t card; suit_t suit; rank_t rank; if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to start playing before you can discard a card.\n\r", ch ); return; } /* The game is over */ if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) ) { send_to_char( "The game is already over.\n\r", ch ); return; } if ( !GameCanDiscard(ch->deck->Game, 0) ) { sprintf( buf, "You cannot discard cards in %s.\n\r", GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); return; } /* You can only discard on your turn */ if ( ch->play_turn == FALSE ) { send_to_char( "You can only discard on your own turn.\n\r", ch ); return; } /* You can only discard on once the game has started */ if ( ch->deck->Playing == eFalse ) { send_to_char( "You cannot discard any cards until the dealer has dealt the first hand.\n\r", ch ); return; } if ( !GameCanDiscard(ch->deck->Game, ch->deck->Turn) ) { sprintf( buf, "You cannot discard cards in turn %d of %s.\n\r", ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); return; } /* Can you discard that many cards? */ if ( HandCountCards(ch->hand) <= GameInitialDraw(ch->deck->Game) - GameCanDiscard(ch->deck->Game, ch->deck->Turn) ) { sprintf( buf, "You may only discard up to %d cards in turn %d of %s.\n\r", GameCanDiscard(ch->deck->Game, ch->deck->Turn), ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); return; } if ( argument[0] == '\0' || strlen(argument) != 2 ) { send_to_char( "Please specify which card you wish to discard (eg 'discard H5').\n\r", ch ); return; } /* Find the suit and rank of the card they're trying to discard */ suit = CardSuit( UPPER(argument[0]) ); rank = CardRank( UPPER(argument[1]) ); if ( suit == eSuitMax || rank == eRankMax ) { /* Swap them around, in case the player did them the wrong way */ suit = CardSuit( UPPER(argument[1]) ); rank = CardRank( UPPER(argument[0]) ); if ( suit == eSuitMax || rank == eRankMax ) { sprintf( buf, "There is no such card as '%s'.\n\r", argument ); send_to_char( buf, ch ); return; } } /* Find the card type, based on its suit and rank */ card = CardType( suit, rank ); /* Try to remove the card from their hand */ if ( HandRemoveCard(ch->hand, card) == eFalse ) { sprintf( buf, "You're not even holding the %s (%s)!\n\r", CardName(card), CardValue(card) ); send_to_char( buf, ch ); return; } /* Try to put the card back onto the bottom of the deck */ if ( DeckReturnCard(ch->deck, card) == eFalse ) { sprintf( buf, "do_discard: Tried to return '%s' to deck, but it was already there!", CardValue(card) ); bug( buf, 0 ); } sprintf( buf, "You discard the %s (%s) from your hand.\n\r", CardName(card), CardValue(card) ); send_to_char( buf, ch ); act( "$n discards a card from $s hand.", ch, NULL, NULL, TO_ROOM ); } void do_draw( CHAR_DATA *ch, char *argument ) { char buf[MAX_STRING_LENGTH]; if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to start playing before you can draw a card.\n\r", ch ); } else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) ) { send_to_char( "The game is already over.\n\r", ch ); } else if ( !GameCanDraw(ch->deck->Game, 0) ) { sprintf( buf, "You cannot draw cards in %s.\n\r", GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( !GameCanDraw(ch->deck->Game, ch->deck->Turn) ) { sprintf( buf, "You cannot draw cards in turn %d of %s.\n\r", ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( ch->play_turn == FALSE ) { send_to_char( "You can only draw a card on your own turn.\n\r", ch ); } else if ( ch->deck->Playing == eFalse ) { send_to_char( "You cannot draw any cards until the dealer has dealt the first hand.\n\r", ch ); } else if ( GameCanDraw(ch->deck->Game, ch->deck->Turn) == 1 ) { /* Draw a card from the deck and put it in their hand */ card_t card = DeckDrawCard(ch->deck); if ( card == eCardMax ) { bug( "do_draw: Managed to end up with an empty deck.", 0 ); send_to_char( "The deck is empty!\n\r", ch ); } else if ( HandAddCard(ch->hand, card) == eFalse ) { bug( "do_draw: Duplicate card found in hand.", 0 ); send_to_char( "You were unable to draw the card.\n\r", ch ); } else /* They drew the card fine */ { int Score = ScoreBlackjack(ch->hand); if ( Score > 0 && Score <= 21 ) { sprintf( buf, "You draw the %s from the deck.\n\r", CardName(card) ); send_to_char( buf, ch ); sprintf( buf, "$n draws a new card from the deck." ); act( buf, ch, NULL, NULL, TO_ROOM ); } else /* They've bone bust */ { CHAR_DATA *prev = ch->prev_card_player; CHAR_DATA *next = ch->next_card_player; sprintf( buf, "You draw the %s from the deck, going bust!\n\r", CardName(card) ); send_to_char( buf, ch ); sprintf( buf, "$n draws a new card from the deck, and goes bust!" ); act( buf, ch, NULL, NULL, TO_ROOM ); /* Turn over - next player */ NextPlayer(ch, TRUE); /* Destroy the hand (no need to remove the cards beforehand) */ if ( ch->hand != NULL ) { free(ch->hand); ch->hand = NULL; } /* Strip the character from the player list */ if ( prev != NULL ) { prev->next_card_player = next; ch->prev_card_player = NULL; } if ( next != NULL ) { next->prev_card_player = prev; ch->next_card_player = NULL; } /* If they're the last person using that deck, destroy it */ if ( ch->deck != NULL ) { if ( --ch->deck->Instances == 0 ) free(ch->deck); ch->deck = NULL; } } } } else if ( HandCountCards(ch->hand) == GameFullHand(ch->deck->Game) ) { sprintf( buf, "You've already got a full hand of %d cards.\n\r", GameFullHand(ch->deck->Game) ); send_to_char( buf, ch ); } else /* They're playing, so draw a card */ { int CardsDrawn = 0; int FullHand = GameFullHand(ch->deck->Game); while ( HandCountCards(ch->hand) < FullHand ) { /* Draw a card from the deck and put it in their hand */ card_t card = DeckDrawCard(ch->deck); if ( card == eCardMax ) { bug( "do_draw: Managed to end up with an empty deck.", 0 ); send_to_char( "The deck is empty!\n\r", ch ); break; } else if ( HandAddCard(ch->hand, card) == eTrue ) { CardsDrawn++; } else /* They were unable to add the card to their hand */ { bug( "do_draw: Duplicate card found in hand.", 0 ); send_to_char( "You were unable to draw the card.\n\r", ch ); break; } } sprintf( buf, "You draw %d new card%s from the deck.\n\r", CardsDrawn, CardsDrawn == 1 ? "" : "s" ); send_to_char( buf, ch ); sprintf( buf, "$n draws %d new card%s from the deck.", CardsDrawn, CardsDrawn == 1 ? "" : "s" ); act( buf, ch, NULL, NULL, TO_ROOM ); /* Turn over - next player */ NextPlayer(ch, FALSE); } } void do_fold( CHAR_DATA *ch, char *argument ) { if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to be playing before you can fold.\n\r", ch ); } else if ( !GameCanFold(ch->deck->Game, 0) ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "You cannot fold in %s.\n\r", GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( !GameCanFold(ch->deck->Game, ch->deck->Turn) ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "You cannot fold in turn %d of %s.\n\r", ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( ch->play_turn == FALSE ) { send_to_char( "You can only fold on your own turn.\n\r", ch ); } else if ( ch->deck->Playing == eFalse ) { send_to_char( "You cannot fold until the first hand has been dealt.\n\r", ch ); } else /* Show the hand (if any) and then discard it */ { CHAR_DATA *prev = ch->prev_card_player; CHAR_DATA *next = ch->next_card_player; if ( HandCountCards(ch->hand) == 0 ) { send_to_char( "You fold.\n\r", ch ); act( "$n folds ($s hand is already empty).", ch, NULL, NULL, TO_ROOM ); } else /* Show their hand, then put the cards back into the deck */ { char buf[MAX_STRING_LENGTH]; int i; /* Loop counter */ const char *pCards = HandShow(ch->hand, eFalse); int Max = strlen(pCards); sprintf( buf, "You fold, throwing down your hand: %s\n\r", pCards ); send_to_char( buf, ch ); sprintf( buf, "Placing $s cards down, $n folds, revealing: %s", pCards ); act( buf, ch, NULL, NULL, TO_ROOM ); for ( i = 0; i < Max; i += 3 ) { /* Put all the cards in their hand back into the deck */ card_t card = CardType(CardSuit(pCards[i]), CardRank(pCards[i+1])); if ( card != eCardMax && HandRemoveCard(ch->hand, card) ) { /* Make sure the card isn't already in the deck */ if ( DeckReturnCard(ch->deck, card) == eFalse ) { sprintf( buf, "do_discard: Tried to return '%s' to deck, but it was already there!\n\r", CardValue(card) ); bug( buf, 0 ); } } } } /* Turn over - next player */ NextPlayer(ch, TRUE); /* Destroy the hand (no need to remove the cards beforehand) */ if ( ch->hand != NULL ) { free(ch->hand); ch->hand = NULL; } /* Strip the character from the player list */ if ( prev != NULL ) { prev->next_card_player = next; ch->prev_card_player = NULL; } if ( next != NULL ) { next->prev_card_player = prev; ch->next_card_player = NULL; } /* If they're the last person using that deck, destroy it */ if ( ch->deck != NULL ) { if ( --ch->deck->Instances == 0 ) free(ch->deck); ch->deck = NULL; } } } void do_hand( CHAR_DATA *ch, char *argument ) { if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to start playing in order to get a hand of cards.\n\r", ch ); } else if ( HandCountCards(ch->hand) == 0 ) { send_to_char( "Your hand is currently empty - you need to draw a card.\n\r", ch ); } else if ( !GameViewHand(ch->deck->Game, 0) ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "You cannot view your hand in %s.\n\r", GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( !GameViewHand(ch->deck->Game, ch->deck->Turn) ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "You cannot view your hand in turn %d of %s.\n\r", ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else /* Just show the hand to its owner */ { char buf[MAX_STRING_LENGTH]; sprintf( buf, "You glance at your hand: %s\n\r\n\r", HandShow(ch->hand, eFalse) ); send_to_char( buf, ch ); if ( !strcmp(argument, "table") ) send_to_char( ScorePoker(ch->hand, NULL), ch ); else /* Show the graphical layout */ send_to_char( HandShow(ch->hand, eTrue), ch ); send_to_char( "\n\r", ch ); ShowValidCommands( ch ); } } void do_stick( CHAR_DATA *ch, char *argument ) { if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to start a game before you can stick with your hand.\n\r", ch ); } else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) ) { send_to_char( "The game is already over.\n\r", ch ); } else if ( !GameCanStick(ch->deck->Game, 0) ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "You cannot stick in %s.\n\r", GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( !GameCanStick(ch->deck->Game, ch->deck->Turn) ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "You cannot stick in turn %d of %s.\n\r", ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( ch->play_turn == FALSE ) { send_to_char( "You can only stick on your own turn.\n\r", ch ); } else /* They choose to do nothing this turn */ { send_to_char( "You choose to stick with your current hand.\n\r", ch ); act( "$n chooses to stick with $s current hand.", ch, NULL, NULL, TO_ROOM ); /* Turn over - next player */ NextPlayer(ch, FALSE); } } void do_start( CHAR_DATA *ch, char *argument ) { char buf[MAX_STRING_LENGTH]; game_t Game = eGameNone; /* Make sure the character is not already playing a game */ if ( ch->deck != NULL ) { send_to_char( "You're already playing a game.\n\r", ch ); return; } if ( argument[0] != '0' && is_number(argument) ) Game = atoi(argument)-1; else /* They specified a string, not a number, so look it up */ Game = GameIndex(argument); if ( Game <= eGameNone || Game >= eGameMax ) { int i; /* Loop counter */ send_to_char( "Please specify the game to play from the following:\n\r", ch ); for ( i = eGameNone+1; i < eGameMax; ++i ) { sprintf( buf, "%d) %s\n\r", i+1, GameName(i) ); send_to_char( buf, ch ); } return; } else if ( GameMinimumBet(Game) ) { sprintf( buf, "You begin a game of %s. You must now set a minimum bet (eg 'bet 10').\n\r", GameName(Game) ); send_to_char( buf, ch ); } else /* It's a valid game */ { sprintf( buf, "You begin a game of %s (type 'deal' when ready).\n\r", GameName(Game) ); send_to_char( buf, ch ); sprintf( buf, "$n begins a game of %s (use the 'play' command to join).", GameName(Game) ); act( buf, ch, NULL, NULL, TO_ROOM ); } /* Create a new deck of cards for solo play */ ch->deck = DeckCreate(); ch->deck->Game = Game; ch->deck->Instances = 1; /* Shuffle the deck */ DeckShuffle(ch->deck); /* Create an empty hand for the character */ ch->hand = HandCreate(); ch->hand->bDealer = eTrue; /* Indicate that it's this characters turn */ ch->play_turn = TRUE; /* Shouldn't be necessary, but better safe than sorry */ ch->next_card_player = NULL; ch->prev_card_player = NULL; } void do_play( CHAR_DATA *ch, char *argument ) { CHAR_DATA *victim; if ( argument[0] == '\0' ) { send_to_char( "Who do you wish to play with?\n\r", ch ); return; } /* Make sure the target exists, and is in the same room as ch */ if ( ( victim = get_char_world( ch, argument ) ) == NULL || ( victim->in_room != ch->in_room ) ) { send_to_char( "They aren't here.\n\r", ch ); return; } /* Make sure the character is not already playing a game */ if ( ch->deck != NULL ) { send_to_char( "You're already playing a game.\n\r", ch ); return; } /* Make sure the game isn't already full or under way */ if ( victim->deck != NULL ) { int MaxPlayers = GameMaxPlayers(victim->deck->Game); if ( victim->deck->Instances >= MaxPlayers ) { char buf[MAX_STRING_LENGTH]; sprintf( buf, "That game is already full (it has %d player%s).\n\r", victim->deck->Instances, victim->deck->Instances == 1 ? "" : "s" ); send_to_char( buf, ch ); return; } else if ( victim->deck->Playing == eTrue ) { send_to_char( "You cannot join a game which is already under way.\n\r", ch ); return; } else if ( GameMinimumBet(victim->deck->Game) && victim->deck->Bet <= 0 ) { send_to_char( "You must first wait until the minimum bet has been set.\n\r", ch ); return; } } else /* victim->deck == NULL */ { act( "$N is not currently playing any card game.", ch, NULL, victim, TO_CHAR ); return; } if ( ch == victim ) { send_to_char( "Use the 'start' game to begin a new card game.\n\r", ch ); } else /* The other person is already playing, so join their game */ { /* Set ch to use the same deck as victim */ ch->deck = victim->deck; ch->deck->Instances++; /* Create an empty hand for ch */ ch->hand = HandCreate(); /* Store the minimum bet, if any */ ch->hand->Bet = ch->deck->Bet; /* Indicate that it's not this characters turn */ ch->play_turn = FALSE; /* Add yourself to the front of the player list */ if ( victim->prev_card_player == NULL ) { victim->prev_card_player = ch; ch->next_card_player = victim; ch->prev_card_player = NULL; } else /* There are already other players, so join at the front */ { CHAR_DATA *prev = victim; while ( prev->prev_card_player != NULL ) prev = prev->prev_card_player; prev->prev_card_player = ch; ch->next_card_player = prev; ch->prev_card_player = NULL; } act( "You join $N's game of cards.", ch, NULL, victim, TO_CHAR ); act( "$n joins your game of cards.", ch, NULL, victim, TO_VICT ); act( "$n joins $N's game of cards.", ch, NULL, victim, TO_NOTVICT ); } } void do_shuffle( CHAR_DATA *ch, char *argument ) { if ( ch->deck == NULL ) { send_to_char( "You need to start playing before you can shuffle the deck.\n\r", ch ); } else if ( DeckCountCards(ch->deck) == 0 ) { send_to_char( "You can't shuffle the deck, there's no cards left in it!.\n\r", ch ); } else /* Shuffle the deck */ { DeckShuffle(ch->deck); send_to_char( "You shuffle the deck.\n\r", ch ); act( "$n shuffles the deck.", ch, NULL, NULL, TO_ROOM ); } } void do_double( CHAR_DATA *ch, char *argument ) { char buf[MAX_STRING_LENGTH]; if ( ch->deck == NULL || ch->hand == NULL ) { send_to_char( "You need to start a game before you can double down.\n\r", ch ); } else if ( ch->deck->Turn > GameLastTurn(ch->deck->Game) ) { send_to_char( "The game is already over.\n\r", ch ); } else if ( !GameCanDouble(ch->deck->Game, 0) ) { sprintf( buf, "You cannot double down in %s.\n\r", GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( !GameCanDouble(ch->deck->Game, ch->deck->Turn) ) { sprintf( buf, "You cannot double down in turn %d of %s.\n\r", ch->deck->Turn, GameName(ch->deck->Game) ); send_to_char( buf, ch ); ShowValidCommands( ch ); } else if ( ch->deck->Turn > 0 && ch->deck->Playing == eFalse ) { send_to_char( "You cannot double down until the first hand has been dealt.\n\r", ch ); } else if ( ch->play_turn == FALSE ) { send_to_char( "You can only double down on your turn.\n\r", ch ); } else /* Double their current bet and draw a card */ { if ( ch->gold < ch->hand->Bet*2 ) { send_to_char( "You don't have enough gold to double down.\n\r", ch ); } else /* It's a valid bet */ { /* Draw a card from the deck and put it in their hand */ card_t card = DeckDrawCard(ch->deck); if ( card == eCardMax ) { bug( "do_draw: Managed to end up with an empty deck.", 0 ); send_to_char( "The deck is empty!\n\r", ch ); } else if ( HandAddCard(ch->hand, card) == eFalse ) { bug( "do_draw: Duplicate card found in hand.", 0 ); send_to_char( "You were unable to draw the card.\n\r", ch ); } else /* They drew the card fine */ { /* Apply the new bet, pot and gold */ ch->gold -= ch->hand->Bet; ch->deck->Pot += ch->hand->Bet; ch->hand->Bet *= 2; ch->deck->Bet = ch->hand->Bet; int Score = ScoreBlackjack(ch->hand); if ( Score > 0 && Score <= 21 ) { sprintf( buf, "Your double your bet to %d gold and draw the %s.\n\r", ch->hand->Bet, CardName(card) ); send_to_char( buf, ch ); sprintf( buf, "$n doubles $s bet to %d gold and draws a card.", ch->hand->Bet ); act( buf, ch, NULL, NULL, TO_ROOM ); /* Turn over - next player */ NextPlayer(ch, FALSE); } else /* They've bone bust */ { CHAR_DATA *prev = ch->prev_card_player; CHAR_DATA *next = ch->next_card_player; sprintf( buf, "Your double your bet to %d gold and draw the %s, going bust!\n\r", ch->hand->Bet, CardName(card) ); send_to_char( buf, ch ); sprintf( buf, "$n doubles $s bet to %d gold, draws a card, and goes bust!", ch->hand->Bet ); act( buf, ch, NULL, NULL, TO_ROOM ); /* Turn over - next player */ NextPlayer(ch, TRUE); /* Destroy the hand (no need to remove the cards beforehand) */ if ( ch->hand != NULL ) { free(ch->hand); ch->hand = NULL; } /* Strip the character from the player list */ if ( prev != NULL ) { prev->next_card_player = next; ch->prev_card_player = NULL; } if ( next != NULL ) { next->prev_card_player = prev; ch->next_card_player = NULL; } /* If they're the last person using that deck, destroy it */ if ( ch->deck != NULL ) { if ( --ch->deck->Instances == 0 ) free(ch->deck); ch->deck = NULL; } } } } } }