// Pinkfish
// Started Wed May 30 21:37:15 PDT 2001
inherit "/std/room/furniture/games/card_base";
inherit "/std/room/furniture/games/multiplayer_base";
inherit "/std/room/furniture/commercial";
#include <money.h>
#include <room/card_base.h>
// The hand types.
#define HAND_TYPE_HIGH_CARD 1
#define HAND_TYPE_PAIR 2
#define HAND_TYPE_TWO_PAIR 3
#define HAND_TYPE_THREE 4
#define HAND_TYPE_STRAIGHT 5
#define HAND_TYPE_FLUSH 6
#define HAND_TYPE_FULL_HOUSE 7
#define HAND_TYPE_FOUR 8
#define HAND_TYPE_STRAIGHT_FLUSH 9
#define POKER_STATE_ANTE 0
#define POKER_STATE_PAID_ANTE 1
#define POKER_STATE_DEALT 2
#define POKER_STATE_BET 3
#define POKER_STATE_DISCARD 4
#define POKER_STATE_AFTER_DISCARD 5
#define POKER_STATE_FINAL_BET 6
#define POKER_STATE_FOLDED 7
#define POKER_STATE_END 8
#define TIE -1
class hand_type {
int hand_type;
class playing_card* kickers;
class playing_card duplicate_1;
class playing_card duplicate_2;
class playing_card high_card;
}
class player_data {
class playing_card* hand;
class hand_type hand_type;
int bet;
int state;
}
#define BOARD_TAG "poker"
//
// This gives an estimated return on the payment. For example 50%
// return would mean you get back approximately 50% of your money on
// average
//
private int _return;
private int _ante_amount;
private int _ante_house;
private int _house_cut;
private int _min_bet;
private int _max_bet;
private int _current_bet;
private int _pot;
private int _finished;
private int _poker_phase;
private int _turn_timeout;
private int _draw_round;
private int _max_draw_rounds;
private class playing_card* _deck;
private class playing_card* _discard;
private mapping _player_stats;
int query_hand_value(class playing_card* cards);
class hand_type query_hand_type(class playing_card* hand);
void create() {
multiplayer_base::create();
set_minimum_needed(2);
add_help_file("poker");
_ante_house = 400;
_ante_amount = 400;
_min_bet = 400;
_max_bet = 1200;
_max_draw_rounds = 1;
_player_stats = ([ ]);
set_shop_use_types(({ "poker" }));
commercial::create();
set_commercial_type("gambling");
set_commercial_name("poker");
} /* create() */
string query_hand_type_string(class hand_type bing) {
string ret;
switch (bing->hand_type) {
case HAND_TYPE_FULL_HOUSE :
ret = "Full house";
break;
case HAND_TYPE_THREE :
ret = "Three of a kind";
break;
case HAND_TYPE_FLUSH :
ret = "Flush";
break;
case HAND_TYPE_STRAIGHT :
ret = "Straight";
break;
case HAND_TYPE_STRAIGHT_FLUSH :
ret = "Straight flush";
break;
case HAND_TYPE_PAIR :
ret = "Pair";
break;
case HAND_TYPE_FOUR :
ret = "Four of a kind";
break;
case HAND_TYPE_TWO_PAIR :
ret = "Two pairs";
break;
case HAND_TYPE_HIGH_CARD :
ret = "High card " + query_card_string(bing->high_card);
break;
default :
ret = "Nothing";
break;
}
return ret;
} /* query_hand_type_string() */
/**
* This method shows the current status of the cards.
* @param id the id to show the status for
* @return the status of the cards
*/
string query_card_status(string id) {
string id_bing;
string ret;
string* not_playing;
class player_data data;
string* womble;
string place;
int left;
ret = "";
not_playing = ({ });
womble = query_player_ids();
place = query_money_place();
foreach (id_bing in womble) {
if (is_person_playing(id_bing)) {
ret += capitalize(id_bing) + " (" +
query_player_cap_name(id_bing) + ")";
data = query_player_data(id_bing);
if (data) {
if (data->state == POKER_STATE_FOLDED) {
ret += " Folded!\n";
} else {
if (data->state == POKER_STATE_PAID_ANTE) {
ret += " (paid ante) ";
}
if (data->state == POKER_STATE_AFTER_DISCARD) {
ret += " (discarded) ";
}
if (data->state == POKER_STATE_BET ||
data->state == POKER_STATE_FINAL_BET) {
if (id_bing == query_current_player()) {
ret += " <-- Their bet ";
}
}
if (data->bet) {
ret += " Current bet " +
MONEY_HAND->money_value_string(data->bet, place);
ret += "\n";
} else {
ret += " No bet yet.\n";
}
}
} else {
ret += "\n";
}
} else {
not_playing += ({ id_bing });
}
}
if (sizeof(not_playing) > 1) {
ret += query_multiple_short(map(not_playing, (: capitalize($1) :))) +
" are not playing.\n";
} else if (sizeof(not_playing) == 1) {
ret += query_multiple_short(map(not_playing, (: capitalize($1) :))) +
" is not playing.\n";
}
ret += "The pot is " +
MONEY_HAND->money_value_string(_pot, place) + ".\n";
if (is_game_started()) {
if (_draw_round < _max_draw_rounds) {
left = _max_draw_rounds - _draw_round;
ret += left + " draw round" + (left>1?"s":"") + " left.\n";
}
}
ret += "\n";
if (id) {
if (!_finished) {
data = query_player_data(id);
if (data && sizeof(data->hand)) {
ret += "Your hand (" +
query_hand_type_string(data->hand_type) +
(data->state == POKER_STATE_FOLDED?" -- Folded":"") +
"):\n";
if (data->state != POKER_STATE_FOLDED) {
ret += query_hand_string(data->hand,
CARD_HAND_THREE|CARD_HAND_LETTERS,
this_player()->query_cols());
}
}
if (_finished) {
tell_all_players(query_player_cap_name(id) + " peeks at their hand on " +
the_short() + ".\n", ({ id }));
}
} else {
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if (data && sizeof(data->hand)) {
ret += query_player_cap_name(id);
if (data->state == POKER_STATE_FOLDED) {
ret += " folded, so the cards are hidden.\n";
} else {
ret += " hand (" +
query_hand_type_string(data->hand_type) + ":\n";
ret += query_hand_string(data->hand, CARD_HAND_SINGLE,
this_player()->query_cols());
}
}
}
}
}
return ret;
}
/** @ignore yes */
string long(string str, int dark) {
if (dark) {
return ::long() +
"It is too dark to make out the pieces on the board.\n";
}
return ::long() + query_card_status(find_player_id_of_person(this_player()));
} /* long() */
/**
* See if the round has completed.
*/
int check_end_round() {
int bet;
string id;
class player_data data;
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if (data->state != POKER_STATE_FOLDED) {
if (!data->bet) {
return 0;
} else if (!bet && data->bet) {
bet = data->bet;
} else if (bet != data->bet) {
return 0;
}
}
}
if (bet) {
call_out("complete_round", 2);
_poker_phase = POKER_STATE_END;
return 1;
}
} /* check_end_round() */
/**
* This method bounces to the next person to bid.
*/
void next_person_turn() {
class player_data data;
string start_id;
start_id = query_current_player();
do {
increment_current_player();
data = query_player_data(query_current_player());
} while (data->state == POKER_STATE_FOLDED &&
query_current_player() != start_id);
// Only one person left...
if (query_current_player() == start_id) {
printf("Force end of game.\n");
call_out("complete_round", 2, 1);
_poker_phase = POKER_STATE_END;
} else {
call_out("tell_current_player", 0, "%^BOLD%^Your turn!%^RESET%^\n");
}
} /* next_person_turn() */
/**
* This deals cards to everyone.
*/
void deal_cards() {
string id;
class player_data data;
_deck = make_deck(1, 0);
_deck = shuffle_deck(_deck);
foreach (id in query_currently_playing_ids()) {
data = query_player_data(id);
data->hand = sort_cards(_deck[0..4], 3);
data->bet = 0;
if (data->state != POKER_STATE_FOLDED) {
if (!_max_draw_rounds) {
data->state = POKER_STATE_FINAL_BET;
} else {
data->state = POKER_STATE_BET;
}
}
data->hand_type = query_hand_type(data->hand);
_deck = _deck[5..];
if (query_player_object(id)) {
tell_player(id, "Your hand:\n" +
query_hand_string(data->hand, CARD_HAND_THREE|CARD_HAND_LETTERS,
query_player_object(id)->query_cols()) +
query_hand_type_string(data->hand_type) + ".\n");
}
}
if (!_max_draw_rounds) {
_poker_phase = POKER_STATE_FINAL_BET;
} else {
_poker_phase = POKER_STATE_BET;
}
next_person_turn();
tell_all_players(query_player_cap_name(query_current_player()) +
" goes first.\n");
//call_out("tell_current_player", 0, "%^BOLD%^Your turn!%^RESET%^\n");
//tell_current_player("%^BOLD%^Your turn!%^RESET%^\n");
_current_bet = 0;
} /* deal_cards() */
/** @ignore yes */
int start_game() {
class player_data data;
string id;
randomise_player_numbers();
if (!::start_game()) {
return 0;
}
foreach (id in query_player_ids()) {
data = new(class player_data);
data->bet = 0;
data->hand = ({ });
data->state = POKER_STATE_ANTE;
set_player_data(id, data);
}
_poker_phase = POKER_STATE_ANTE;
_pot = 0;
_finished = 0;
_draw_round = 0;
_discard = ({ });
tell_all_players("%^BOLD%^Place your ante to start playing.%^RESET%^\n");
return 1;
} /* start_game() */
/**
* Checks to see if all the people playing have put in their first
* bets.
*/
void check_for_finish_ante() {
string id;
class player_data data;
object ob;
foreach (id in query_currently_playing_ids()) {
data = query_player_data(id);
ob = query_player_object(id);
if (ob && !interactive(ob)) {
// Set them to folded.
data->state = POKER_STATE_FOLDED;
}
if (ob && interactive(ob) && data->state == POKER_STATE_ANTE) {
return ;
}
}
_poker_phase = POKER_STATE_BET;
// Move to the next state!
call_out("deal_cards", 2);
} /* check_for_finish_ante() */
/**
* This figures out if the card is higher than the other one or not.
* @param card_new the new card o checxl
* @param card_old the old card to check againt
* @return 1 if the new card is higher, 0 if the old card is higher
*/
int is_card_higher(class playing_card card_new, class playing_card card_old) {
if (card_new->number == card_old->number) {
// Then it is based on suits.
return 0;
}
if (card_new->number == CARD_NUMBER_ACE) {
return 1;
}
if (card_old->number == CARD_NUMBER_ACE) {
return 0;
}
if (card_new->number > card_old->number) {
return 1;
}
return 0;
} /* is_card_higher() */
/**
* This method returns the basic type of the hand.
*/
class hand_type query_hand_type(class playing_card* hand) {
int high_num;
int num;
int id;
int i;
int j;
class playing_card* tmp_hand;
class hand_type ret_type;
class playing_card tmp_card;
ret_type = new(class hand_type);
high_num = 0;
for (i = 1; i < sizeof(hand); i++) {
if (is_card_higher(hand[i], hand[high_num])) {
high_num = i;
}
}
ret_type->hand_type = HAND_TYPE_HIGH_CARD;
ret_type->high_card = hand[high_num];
//
// First check for x of a kind.
//
high_num = 1;
for (i = 0; i < sizeof(hand) - 1; i++) {
num = 1;
for (j = 0; j < sizeof(hand) - i - 1; j++) {
if (hand[i]->number == hand[i + j + 1]->number) {
num++;
}
}
if (num > high_num) {
high_num = num;
id = hand[i]->number;
}
}
tmp_hand = hand;
if (high_num > 1) {
// Remove the found cards from the list.
for (i = 0; i < sizeof(tmp_hand); i++) {
if (tmp_hand[i]->number == id) {
ret_type->duplicate_1 = tmp_hand[i];
tmp_hand = tmp_hand[0..i-1] + tmp_hand[i+1..];
i--;
}
}
}
//
// If you have more than one of a certain numbered card you cannot
// have any of the other bits.
//
if (high_num == 2) {
//
// Could be two pair.
//
high_num = 1;
for (i = 0; i < sizeof(tmp_hand) - 1; i++) {
num = 1;
for (j = 0; j < sizeof(tmp_hand) - i - 1; j++) {
if (tmp_hand[i]->number == tmp_hand[i + j + 1]->number) {
num++;
}
}
if (num > high_num) {
high_num = num;
id = tmp_hand[i]->number;
}
}
if (high_num == 2) {
ret_type->hand_type = HAND_TYPE_TWO_PAIR;
// Remove the found cards from the list.
for (i = 0; i < sizeof(tmp_hand); i++) {
if (tmp_hand[i]->number == id) {
ret_type->duplicate_2 = tmp_hand[i];
tmp_hand = tmp_hand[0..i-1] + tmp_hand[i+1..];
}
}
if (is_card_higher(ret_type->duplicate_2, ret_type->duplicate_1)) {
tmp_card = ret_type->duplicate_1;
ret_type->duplicate_1 = ret_type->duplicate_2;
ret_type->duplicate_2 = tmp_card;
}
ret_type->kickers = sort_cards(tmp_hand, 3);
} else {
ret_type->hand_type = HAND_TYPE_PAIR;
high_num = 0;
for (i = 1; i < sizeof(tmp_hand); i++) {
if (is_card_higher(tmp_hand[i], tmp_hand[high_num])) {
high_num = i;
}
}
ret_type->kickers = sort_cards(tmp_hand, 3);
}
return ret_type;
} else if (high_num == 3) {
//
// Could be a full house.
//
if (tmp_hand[0]->number == tmp_hand[1]->number) {
ret_type->hand_type = HAND_TYPE_FULL_HOUSE;
ret_type->duplicate_2 = tmp_hand[0];
ret_type->kickers = ({ });
} else {
ret_type->hand_type = HAND_TYPE_THREE;
ret_type->kickers = sort_cards(tmp_hand, 3);
}
return ret_type;
} else if (high_num == 4) {
ret_type->hand_type = HAND_TYPE_FOUR;
ret_type->high_card = tmp_hand[0];
return ret_type;
}
//
// Check for a flush.
//
for (i = 0; i < sizeof(hand) - 1; i++) {
if (hand[i + 1]->suit != hand[0]->suit) {
break;
}
}
if (i == sizeof(hand) - 1) {
ret_type->hand_type = HAND_TYPE_FLUSH;
ret_type->kickers = sort_cards(hand, 3);
// Find the high card.
high_num = 0;
for (i = 1; i < sizeof(hand); i++) {
if (is_card_higher(hand[i], hand[high_num])) {
high_num = i;
}
}
ret_type->high_card = hand[high_num];
}
//
// Check for a straight.
//
tmp_hand = sort_array(hand, (: ((class playing_card)$1)->number -
((class playing_card)$2)->number :));
if (tmp_hand[0]->number == CARD_NUMBER_ACE) {
if (tmp_hand[1]->number == 2) {
// Skip the ace at the start.
j = 1;
} else {
// Move the ace to the end.
j = 0;
tmp_hand = tmp_hand[1..] + ({ tmp_hand[0] });
}
} else {
j = 0;
}
for (i = j; i < sizeof(tmp_hand) - 1; i++) {
if (tmp_hand[i]->number + 1 != tmp_hand[i + 1]->number) {
if (tmp_hand[i + 1]->number != CARD_NUMBER_ACE ||
tmp_hand[i]->number != CARD_NUMBER_KING) {
break;
}
}
}
if (i == sizeof(tmp_hand) - 1) {
if (ret_type->hand_type == HAND_TYPE_FLUSH) {
ret_type->hand_type = HAND_TYPE_STRAIGHT_FLUSH;
ret_type->high_card = tmp_hand[<1];
} else {
ret_type->hand_type = HAND_TYPE_STRAIGHT;
ret_type->high_card = tmp_hand[<1];
}
}
if (ret_type->hand_type == HAND_TYPE_HIGH_CARD) {
ret_type->kickers = sort_cards(hand, 3);
}
return ret_type;
} /* query_hand_type() */
/**
* This figures out which hand is greator than the other.
*/
int is_greator_hand(class hand_type new_hand,
class hand_type old_hand) {
int i;
if (new_hand->hand_type > old_hand->hand_type) {
return 1;
}
if (new_hand->hand_type < old_hand->hand_type) {
return 0;
}
if (new_hand->hand_type == old_hand->hand_type) {
// Try and work out ties...
switch (new_hand->hand_type) {
case HAND_TYPE_HIGH_CARD :
if (is_card_higher(new_hand->high_card,
old_hand->high_card)) {
return 1;
}
if (is_card_higher(old_hand->high_card,
new_hand->high_card)) {
return 0;
}
for (i = 1; i <= sizeof(new_hand->kickers); i++) {
if (is_card_higher(new_hand->kickers[<i],
old_hand->kickers[<i])) {
return 1;
} else if (is_card_higher(old_hand->kickers[<i],
new_hand->kickers[<i])) {
return 0;
}
}
break;
case HAND_TYPE_FLUSH :
for (i = 1; i <= sizeof(new_hand->kickers); i++) {
if (is_card_higher(new_hand->kickers[<i],
old_hand->kickers[<i])) {
return 1;
} else if (is_card_higher(old_hand->kickers[<i],
new_hand->kickers[<i])) {
return 0;
}
}
break;
case HAND_TYPE_PAIR :
if (is_card_higher(new_hand->duplicate_1,
old_hand->duplicate_1)) {
return 1;
}
if (is_card_higher(old_hand->duplicate_1,
new_hand->duplicate_1)) {
return 0;
}
for (i = 1; i <= sizeof(new_hand->kickers); i++) {
if (is_card_higher(new_hand->kickers[<i],
old_hand->kickers[<i])) {
return 1;
} else if (is_card_higher(old_hand->kickers[<i],
new_hand->kickers[<i])) {
return 0;
}
}
break;
case HAND_TYPE_TWO_PAIR :
if (is_card_higher(new_hand->duplicate_1,
old_hand->duplicate_1)) {
return 1;
}
if (is_card_higher(old_hand->duplicate_1,
new_hand->duplicate_1)) {
return 0;
}
if (is_card_higher(new_hand->duplicate_2,
old_hand->duplicate_2)) {
return 1;
}
if (is_card_higher(old_hand->duplicate_2,
new_hand->duplicate_2)) {
return 0;
}
if (is_card_higher(new_hand->kickers[0],
old_hand->kickers[0])) {
return 1;
}
if (is_card_higher(old_hand->kickers[0],
new_hand->kickers[0])) {
return 0;
}
break;
case HAND_TYPE_THREE :
if (is_card_higher(new_hand->duplicate_1,
old_hand->duplicate_1)) {
return 1;
}
if (is_card_higher(old_hand->duplicate_1,
new_hand->duplicate_1)) {
return 0;
}
for (i = 1; i <= sizeof(new_hand->kickers); i++) {
if (is_card_higher(new_hand->kickers[<i],
old_hand->kickers[<i])) {
return 1;
} else if (is_card_higher(old_hand->kickers[<i],
new_hand->kickers[<i])) {
return 0;
}
}
break;
case HAND_TYPE_FOUR :
if (is_card_higher(new_hand->duplicate_1,
old_hand->duplicate_1)) {
return 1;
}
if (is_card_higher(old_hand->duplicate_1,
new_hand->duplicate_1)) {
return 0;
}
if (is_card_higher(new_hand->kickers[0],
old_hand->kickers[0])) {
return 1;
}
if (is_card_higher(old_hand->kickers[0],
new_hand->kickers[0])) {
return 0;
}
break;
case HAND_TYPE_FULL_HOUSE :
if (is_card_higher(new_hand->duplicate_1,
old_hand->duplicate_1)) {
return 1;
}
if (is_card_higher(old_hand->duplicate_1,
new_hand->duplicate_1)) {
return 0;
}
if (is_card_higher(new_hand->duplicate_2,
old_hand->duplicate_2)) {
return 1;
}
if (is_card_higher(old_hand->duplicate_2,
new_hand->duplicate_2)) {
return 0;
}
break;
case HAND_TYPE_STRAIGHT :
case HAND_TYPE_STRAIGHT_FLUSH :
// Use the high card as the kicker.
if (is_card_higher(new_hand->high_card,
old_hand->high_card)) {
return 1;
}
if (is_card_higher(new_hand->high_card,
old_hand->high_card)) {
return 0;
}
break;
}
}
return TIE;
} /* is_greator_hand() */
void test_hand_type() {
class playing_card* other_tmp_hand;
class playing_card* tmp_hand;
other_tmp_hand = ({ new(class playing_card, suit: CARD_SUIT_HEARTS, number : 11),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 11),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 12),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 13),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 13) });
tmp_hand = ({ new(class playing_card, suit: CARD_SUIT_HEARTS, number : 2),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 3),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 4),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 1),
new(class playing_card, suit: CARD_SUIT_HEARTS, number : 5) });
write(query_hand_type_string(query_hand_type(tmp_hand)) + " --\n" +
query_hand_type_string(query_hand_type(other_tmp_hand)));
printf("%O\n", sizeof(query_hand_type(tmp_hand)->kickers));
printf("%O\n", sizeof(query_hand_type(other_tmp_hand)->kickers));
write("Result: " + is_greator_hand(query_hand_type(tmp_hand),
query_hand_type(other_tmp_hand)) + "\n");
}
/**
* This method completes the round.
*/
void complete_round(int force_end) {
string stuff;
object ob;
class player_data data;
string id;
string place;
string* winner;
class hand_type winning_hand_type;
class playing_card* winning_hand;
int paid;
int discard;
int result;
int num;
if (_finished) {
return 0;
}
place = query_money_place();
stuff = "";
winner = ({ });
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if ((data->state == POKER_STATE_BET || discard) && !force_end) {
if (data->state != POKER_STATE_FOLDED) {
data->state = POKER_STATE_DISCARD;
data->bet = 0;
}
discard = 1;
} else if (data->state != POKER_STATE_FOLDED) {
num++;
if (!winning_hand) {
winning_hand = data->hand;
winning_hand_type = data->hand_type;
winner = ({ id });
} else {
data->hand_type = query_hand_type(data->hand);
result = is_greator_hand(data->hand_type, winning_hand_type);
if (result == TIE) {
winner += ({ id });
} else if (result) {
winning_hand = data->hand;
winning_hand_type = data->hand_type;
winner = ({ id });
}
}
stuff += query_player_cap_name(id) + "'s hand (" +
query_hand_type_string(data->hand_type) + "):\n" +
query_hand_string(data->hand, CARD_HAND_SINGLE,
this_player()->query_cols()) + "\n";
}
}
if (num == 1) {
stuff = "All but one person folded so the cards are not revealed.\n";
}
if (discard) {
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if (data->state == POKER_STATE_FOLDED) {
tell_player(id, "%^BOLD%^Now into the discard phase.%^RESET%^\n"
"You have folded.\n");
} else {
data->bet = 0;
tell_player(id, "%^BOLD%^Now into the discard phase.%^RESET%^\n"
"Your hand:\n" +
query_hand_string(data->hand, CARD_HAND_THREE | CARD_HAND_LETTERS,
80));
}
}
} else {
if (_house_cut) {
stuff += "House takes " +
MONEY_HAND->money_value_string((_pot * _house_cut) / 100, place) +
".\n";
adjust_float((_pot * _house_cut) / 100);
//_revenue += (_pot * _house_cut) / 100;
_pot -= (_pot * _house_cut) / 100;
}
if (sizeof(winner) > 1) {
stuff += "Tie for winning between " +
query_multiple_short(map(winner, (: query_player_cap_name($1) :))) +
", they each win " +
MONEY_HAND->money_value_string(_pot / sizeof(winner), place) +
".\n";
paid = _pot / sizeof(winner);
foreach (id in winner) {
ob = query_player_object(id);
if (ob) {
ob->adjust_money(MONEY_HAND->create_money_array(paid, place), place);
_player_stats[ob->query_name()] += paid;
} else {
stuff += "Unable to find " + id + " to pay them, money "
"going to the house.\n";
adjust_float(paid);
//_revenue += paid;
}
}
} else {
stuff += "Winner is " +
query_multiple_short(map(winner, (: query_player_cap_name($1) :))) +
", and they win " +
MONEY_HAND->money_value_string(_pot, place) +
".\n";
ob = query_player_object(winner[0]);
ob->adjust_money(MONEY_HAND->create_money_array(_pot, place), place);
_player_stats[ob->query_name()] += _pot;
}
// Do the dealer.
tell_all_players("The players reveal their cards as:\n" +
stuff);
tell_room(environment(), "The game ends with " + query_multiple_short(winner) +
" as the winner.\n");
finish_game(query_multiple_short(winner));
_finished = 1;
}
} /* complete_round() */
/**
* This method checks to see if everyone has discarded and ready for the
* next round.
*/
void finish_discard() {
string id;
class player_data data;
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if (data->state != POKER_STATE_AFTER_DISCARD &&
data->state != POKER_STATE_FOLDED) {
return 0;
}
}
_draw_round++;
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if (data->state == POKER_STATE_AFTER_DISCARD) {
if (_draw_round >= _max_draw_rounds) {
data->state = POKER_STATE_FINAL_BET;
} else {
data->state = POKER_STATE_BET;
}
}
}
_current_bet = 0;
_poker_phase = POKER_STATE_FINAL_BET;
call_out("tell_all_players", 0,
"Discard round completed. Now onto the " +
(_draw_round >= _max_draw_rounds?"final ":"") + "betting round.\n");
next_person_turn();
//call_out("tell_current_player", 0, "%^BOLD%^Your turn!%^RESET%^\n");
} /* finish_discard() */
/**
* Places your bet.
*/
int do_ante() {
string place;
string id;
int amount;
class player_data data;
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
id = find_player_id_of_person(this_player());
if (!id) {
add_failed_mess("You are not playing.\n");
return 0;
}
data = query_player_data(id);
if (data->state != POKER_STATE_ANTE) {
add_failed_mess("You have already put in your ante.\n");
return 0;
}
place = query_money_place();
amount = _ante_house + _ante_amount;
if (this_player()->query_value_in(place) < amount) {
add_failed_mess("You do not have the needed " +
MONEY_HAND->money_value_string(amount, place) +
" to meet the ante.\n");
return 0;
}
_player_stats[this_player()->query_name()] -= amount;
this_player()->pay_money(MONEY_HAND->create_money_array(amount, place),
place);
adjust_float(_ante_house);
//_revenue += _ante_house;
_pot += _ante_amount;
data->state = POKER_STATE_PAID_ANTE;
environment()->event_save(this_object());
// This is for the starting bet.
check_for_finish_ante();
add_succeeded_mess("$N $V " +
MONEY_HAND->money_value_string(amount, place) +
" on $D.\n");
return 1;
} /* do_ante() */
/**
* This hits you for another card.
*/
int do_discard(string throw_away) {
int i;
string id;
class player_data data;
string* bits;
int* new_bits;
throw_away = lower_case(throw_away);
// Get another card.
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
id = find_player_id_of_person(this_player());
if (!id) {
add_failed_mess("You are not playing.\n");
return 0;
}
data = query_player_data(id);
if (!sizeof(data->hand)) {
add_failed_mess("You cannot discard before you have been dealt "
"cards.\n");
return 0;
}
if (data->state != POKER_STATE_DISCARD) {
add_failed_mess("You are not in the discard phase.\n");
return 0;
}
if (throw_away && throw_away != "none") {
throw_away = lower_case(replace_string(throw_away, " ", ""));
bits = explode(throw_away, ",");
if (sizeof(filter(bits, (: strlen($1) > 1 :))) > 0) {
add_failed_mess("Some of the card references " + throw_away +
" are invalid. Use a command seperated list of "
"card ids.\n");
return 0;
}
// Figure out the cards to throw.
new_bits = map(bits, (: $1[0] - 'a' :));
if (sizeof(filter(new_bits, (: $1 < 0 || $1 >= 5 :))) > 0) {
add_failed_mess("Some of the card references " + throw_away +
" are invalid. Use a command seperated list of "
"card ids.\n");
return 0;
}
for (i = 0; i < sizeof(new_bits); i++) {
if (member_array(new_bits[i],
new_bits[0..i-1] + new_bits[i+1..]) != -1) {
add_failed_mess("You have referenced the card " +
sprintf("%c", 'A' + new_bits[i]) + " twice.\n");
return 0;
}
}
new_bits = sort_array(new_bits, (: $2 - $1 :));
for (i = 0; i < sizeof(new_bits); i++) {
_discard += ({ data->hand[new_bits[i]] });
data->hand = data->hand[0..new_bits[i]-1] +
data->hand[new_bits[i]+1..];
}
i = 5 - sizeof(data->hand) - 1;
if (sizeof(_deck) < i) {
_deck += _discard;
_deck = shuffle_array(_deck);
_discard = ({ });
}
data->hand += _deck[0..i];
_deck = _deck[i + 1..];
data->hand = sort_cards(data->hand, 3);
data->hand_type = query_hand_type(data->hand);
add_succeeded_mess(({ "", "$N draw$s " + query_num(sizeof(new_bits)) +
" new card" +
(sizeof(new_bits) > 1?"s":"") + " on $D.\n" }));
write("Your new hand (" +
query_hand_type_string(data->hand_type) +
"):\n" +
query_hand_string(data->hand, CARD_HAND_THREE,
this_player()->query_cols()));
data->state = POKER_STATE_AFTER_DISCARD;
} else {
new_bits = ({ });
data->state = POKER_STATE_AFTER_DISCARD;
add_succeeded_mess("$N do$es not discard any cards on $D.\n");
}
finish_discard();
return 1;
} /* do_discard() */
int do_check() {
// Get another card.
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
if (_poker_phase != POKER_STATE_BET &&
_poker_phase != POKER_STATE_FINAL_BET) {
add_failed_mess("Not a betting phase.\n");
return 0;
}
if (!is_current_player(this_player())) {
add_failed_mess("Not your turn to bet.\n");
return 0;
}
if (_current_bet) {
add_failed_mess("You cannot check since betting has started.\n");
return 0;
}
add_succeeded_mess("$N check$s on $D.\n");
next_person_turn();
return 1;
} /* do_stand() */
int do_call() {
class player_data data;
int amt;
string place;
// Get another card.
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
if (_poker_phase != POKER_STATE_BET &&
_poker_phase != POKER_STATE_FINAL_BET) {
add_failed_mess("Not a betting phase.\n");
return 0;
}
if (!is_current_player(this_player())) {
add_failed_mess("Not your turn to bet.\n");
return 0;
}
if (!_current_bet) {
add_failed_mess("No one has bet anything for you to call.\n");
return 0;
}
place = query_money_place();
data = query_player_data(query_current_player());
if (_current_bet && data->bet == _current_bet) {
// They are called.
call_out("complete_round", 2);
if (_poker_phase == POKER_STATE_ANTE) {
_poker_phase = POKER_STATE_DISCARD;
} else {
_poker_phase = POKER_STATE_ANTE;
}
} else {
amt = _current_bet - data->bet;
if (this_player()->query_value_in(place) < amt) {
add_failed_mess("You do not have the necessary " +
MONEY_HAND->money_value_string(amt, place) +
" to call the bet.\n");
return 0;
}
_player_stats[this_player()->query_name()] -= amt;
this_player()->pay_money(MONEY_HAND->create_money_array(amt, place),
place);
_pot += amt;
data->bet = _current_bet;
if (!check_end_round()) {
next_person_turn();
}
}
add_succeeded_mess("$N call$s the bet by adding " +
MONEY_HAND->money_value_string(amt, place) +
" to the pot for a total bet of " +
MONEY_HAND->money_value_string(_current_bet, place) +
" on $D.\n");
return 1;
} /* do_call() */
int do_raise(string amount) {
class player_data data;
string place;
int amt;
int raise_amt;
// Get another card.
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
if (_poker_phase != POKER_STATE_BET &&
_poker_phase != POKER_STATE_FINAL_BET) {
add_failed_mess("Not a betting phase.\n");
return 0;
}
if (!is_current_player(this_player())) {
add_failed_mess("Not your turn to bet.\n");
return 0;
}
place = query_money_place();
amt = MONEY_HAND->value_from_string(amount, place);
raise_amt = amt;
if (!amt) {
add_failed_mess("The value " + amount + " is not a valid "
"money amount.\n");
return 0;
}
if (amt < _min_bet) {
add_failed_mess("The minimum bet is " +
MONEY_HAND->money_value_string(_min_bet, place) +
".\n");
return 0;
}
if (amt > _max_bet) {
add_failed_mess("The maximum bet is " +
MONEY_HAND->money_value_string(_max_bet, place) +
".\n");
return 0;
}
data = query_player_data(query_current_player());
amt = _current_bet + amt - data->bet;
if (this_player()->query_value_in(place) < amt) {
add_failed_mess("You do not have the nessessary " +
MONEY_HAND->money_value_string(amt, place) +
" to raise the bet.\n");
return 0;
}
_player_stats[this_player()->query_name()] -= amt;
this_player()->pay_money(MONEY_HAND->create_money_array(amt, place),
place);
_current_bet = data->bet + amt;
data->bet = _current_bet;
_pot += amt;
add_succeeded_mess("$N raise$s the bet by " +
MONEY_HAND->money_value_string(raise_amt, place) +
" to " +
MONEY_HAND->money_value_string(_current_bet, place) +
" on $D.\n");
next_person_turn();
return 1;
} /* do_raise() */
int do_bet(string amount) {
class player_data data;
string place;
int amt;
int raise_amt;
// Get another card.
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
if (_poker_phase != POKER_STATE_BET &&
_poker_phase != POKER_STATE_FINAL_BET) {
add_failed_mess("Not a betting phase.\n");
return 0;
}
if (!is_current_player(this_player())) {
add_failed_mess("Not your turn to bet.\n");
return 0;
}
place = query_money_place();
amt = MONEY_HAND->value_from_string(amount, place);
if (!amt) {
add_failed_mess("The value " + amount + " is not a valid "
"money amount.\n");
return 0;
}
if (amt == _current_bet) {
return do_call();
}
if (amt < _current_bet) {
add_failed_mess("You have to bet higher than the current bet of " +
MONEY_HAND->money_value_string(_current_bet, place) +
" money amount.\n");
return 0;
}
amt = amt - _current_bet;
raise_amt = amt;
if (amt < _min_bet) {
add_failed_mess("The minimum bet is " +
MONEY_HAND->money_value_string(_min_bet, place) +
".\n");
return 0;
}
if (amt > _max_bet) {
add_failed_mess("The maximum bet is " +
MONEY_HAND->money_value_string(_max_bet, place) +
".\n");
return 0;
}
data = query_player_data(query_current_player());
amt = _current_bet + amt - data->bet;
if (this_player()->query_value_in(place) < amt) {
add_failed_mess("You do not have the nessessary " +
MONEY_HAND->money_value_string(amt, place) +
" to raise the bet.\n");
return 0;
}
_player_stats[this_player()->query_name()] -= amt;
this_player()->pay_money(MONEY_HAND->create_money_array(amt, place),
place);
_current_bet = data->bet + amt;
data->bet = _current_bet;
_pot += amt;
add_succeeded_mess("$N raise$s the bet by " +
MONEY_HAND->money_value_string(raise_amt, place) +
" to " +
MONEY_HAND->money_value_string(_current_bet, place) +
" on $D.\n");
next_person_turn();
return 1;
} /* do_raise() */
int do_fold() {
class player_data data;
string id;
int not_folded;
// Get another card.
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
if (!is_current_player(this_player())) {
add_failed_mess("Not your turn to bet.\n");
return 0;
}
data = query_player_data(query_current_player());
data->state = POKER_STATE_FOLDED;
add_succeeded_mess("$N fold$s on $D.\n");
// Figure out if there is only one non-folded player left.
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if (data->state != POKER_STATE_FOLDED) {
not_folded++;
}
}
if (not_folded == 1) {
call_out("complete_round", 0, 1);
_poker_phase = POKER_STATE_END;
} else if (!check_end_round()) {
next_person_turn();
}
return 1;
} /* do_fold() */
/**
* Starts a nice furry game.
*/
int do_start() {
int old_pot;
if (!is_open_for("poker", this_player()->query_name())) {
add_failed_mess("The poker table is not open.\n");
return 0;
}
//
// There must eb enough money in the float for everyone to bid the max amo
// amount and win with a poker.
//
if (!is_playing(this_player())) {
add_failed_mess("You must be playing the game to start it.\n");
return 0;
}
old_pot = _pot;
if (!start_game()) {
add_failed_mess("You need at least two people to play poker.\n");
return 0;
}
adjust_float(old_pot);
//_revenue += old_pot;
add_succeeded_mess("$N $V a game on $D.\n");
return 1;
} /* do_start() */
/**
* If it is finished early... Oh dear.
*/
int do_finish() {
string person;
if (!is_game_started()) {
add_failed_mess("The game has not started.\n");
return 0;
}
person = find_player_id_of_person(this_player());
if (!person) {
add_failed_mess("You must actually be playing to finish the game.\n");
return 0;
}
finish_game(0);
reset_game();
return 1;
} /* do_finish() */
string query_main_status(int hint) {
string place;
string ret;
string name;
int amt;
place = query_money_place();
ret = "$I$0=Poker table:\n"
"$I$6= Maximum bet: " +
MONEY_HAND->money_value_string(_max_bet, place) +
"\n$I$6= Minimum bet: " +
MONEY_HAND->money_value_string(_min_bet, place) +
"\n$I$6= Ante : " +
MONEY_HAND->money_value_string(_ante_amount, place) +
"\n$I$6= House Ante : " +
MONEY_HAND->money_value_string(_ante_house, place) +
"\n$I$6= Draw Rounds: " + _max_draw_rounds +
"\n$I$6= Cut : " + _house_cut + "%"
"\n$I$6= Revenue : " +
MONEY_HAND->money_value_string(query_revenue(), place) +
"\n$I$0=";
foreach (name, amt in _player_stats) {
ret += sprintf(" %-13s: %s\n" , name,
MONEY_HAND->money_value_string(amt, place));
}
return ret + "\n";
} /* query_main_status() */
/**
* This method sets the ante amounts.
* @param str the amount string
* @param max_bet if it a max or min bet to set
*/
int do_set_ante(string str, int ante_game) {
string place;
int value;
if (!is_allowed(this_player()->query_name())) {
add_failed_mess("You are not allowed to change the paramaters of "
"$D.\n");
return 0;
}
if (lower_case(str) == "none" && !ante_game) {
value = 0;
} else {
place = query_money_place();
value = MONEY_HAND->value_from_string(str, place);
if (!value) {
add_failed_mess("Unable to parse the string " + str + ".\n");
return 0;
}
}
if (ante_game) {
_ante_amount = value;
add_succeeded_mess("$N set$s the ante amount to play to " +
MONEY_HAND->money_value_string(value, place) + " on $D.\n");
} else {
_ante_house = value;
add_succeeded_mess("$N set$s the ante amount for the house to " +
MONEY_HAND->money_value_string(value, place) + " on $D.\n");
}
return 1;
} /* do_set_ante() */
/**
* This method sets the ante amounts.
* @param str the amount string
* @param max_bet if it a max or min bet to set
*/
int do_set_bet(string str, int max_bet) {
string place;
int value;
if (!is_allowed(this_player()->query_name())) {
add_failed_mess("You are not allowed to change the paramaters of "
"$D.\n");
return 0;
}
place = query_money_place();
value = MONEY_HAND->value_from_string(str, place);
if (!value) {
add_failed_mess("Unable to parse the string " + str + ".\n");
return 0;
}
if (max_bet) {
_max_bet = value;
add_succeeded_mess("$N set$s the maximum bet to " +
MONEY_HAND->money_value_string(value, place) + " on $D.\n");
} else {
_min_bet = value;
add_succeeded_mess("$N set$s the minimum bet to " +
MONEY_HAND->money_value_string(value, place) + " on $D.\n");
}
return 1;
} /* do_set_bet() */
/**
* This method sets the cut
* @param percent the cut percentage
*/
int do_set_cut(int percent) {
if (!is_allowed(this_player()->query_name())) {
add_failed_mess("You are not allowed to change the paramaters of "
"$D.\n");
return 0;
}
if (percent < 0) {
add_failed_mess("You cannot set the cut less than 0.\n");
return 0;
}
if (percent > 100) {
add_failed_mess("You cannot set the cut to greator than 100%.\n");
return 0;
}
_house_cut = percent;
add_succeeded_mess("$N set$s the cut to " + percent + "% on $D.\n");
return 1;
} /* do_set_cut() */
/**
* This method sets the maximum number of draw rounds.
* @param percent the cut percentage
* @param draw the maximum number of draw rounds
*/
int do_set_draw(int draw) {
if (!is_allowed(this_player()->query_name())) {
add_failed_mess("You are not allowed to change the paramaters of "
"$D.\n");
return 0;
}
if (draw < 0) {
add_failed_mess("You cannot set the number of draws less than 0.\n");
return 0;
}
if (draw > 5) {
add_failed_mess("You cannot set the draw to greator than 5.\n");
return 0;
}
_max_draw_rounds = draw;
add_succeeded_mess("$N set$s the number of draw rounds to " + draw + " on $D.\n");
return 1;
} /* do_set_draw() */
/**
* This method shows the current set of house rules. This will contain
* any modifiable bits.
*/
int do_rules() {
string ret;
string place;
place = query_money_place();
ret = "The rules for this table are:\n";
ret += "$I$6= Maximum amount that can be raised " +
MONEY_HAND->money_value_string(_max_bet, place) +
"\n$I$6= Minimum amount that can be raised " +
MONEY_HAND->money_value_string(_min_bet, place) +
"\n$I$6= Total ante " +
MONEY_HAND->money_value_string(_ante_amount + _ante_house, place) +
"\n$I$6= Cut of the pool : " + _house_cut +
"\n$I$6= Draw Rounds : " + _max_draw_rounds +
"\n$I$6= House Ante : " +
MONEY_HAND->money_value_string(_ante_house, place);
write("$P$Rules$P$" + ret);
} /* do_rules() */
int do_reset() {
if (::do_reset()) {
_player_stats = ([ ]);
add_succeeded_mess("$N clear$s the player stats.\n");
return 1;
}
} /* do_reset() */
void init() {
multiplayer_base::init();
commercial::init();
add_command("discard", "<string'card'> on <direct:object>",
(: do_discard($4[0]) :));
add_command("ante", "on <direct:object>",
(: do_ante() :));
add_command("check", "on <direct:object>",
(: do_check() :));
add_command("call", "on <direct:object>",
(: do_call() :));
add_command("bet", "<string'total bet'> on <direct:object>",
(: do_bet($4[0]) :));
add_command("raise", "<string'raise'> on <direct:object>",
(: do_raise($4[0]) :));
add_command("fold", "on <direct:object>",
(: do_fold() :));
add_command("finish", "game on <direct:object>",
(: do_finish() :));
add_command("start", "game on <direct:object>",
(: do_start() :));
add_command("rules", "<direct:object>",
(: do_rules() :));
add_command("rules", "{for|on|of} <direct:object>",
(: do_rules() :));
if (environment()->is_allowed(this_player()->query_name())) {
add_command("set", "draw <number'rounds'> on <direct:object>",
(: do_set_draw($4[0]) :));
add_command("set", "cut <number'percentage'> on <direct:object>",
(: do_set_cut($4[0]) :));
add_command("set", "ante house <string'amount|none'> on <direct:object>",
(: do_set_ante($4[0], 0) :));
add_command("set", "ante game <string'amount'> on <direct:object>",
(: do_set_ante($4[0], 1) :));
add_command("set", "minimum bet <string'amount'> on <direct:object>",
(: do_set_bet($4[0], 0) :));
add_command("set", "maximum bet <string'amount'> on <direct:object>",
(: do_set_bet($4[0], 1) :));
}
} /* init() */
void resign_person(string id) {
class player_data data;
int not_folded;
data = query_player_data(id);
if (data && data->state != POKER_STATE_FOLDED) {
// Make them fold.
tell_all_players(query_player_cap_name(id) +
" resigns and folds.\n");
data->state = POKER_STATE_FOLDED;
foreach (id in query_started_player_ids()) {
data = query_player_data(id);
if (data->state != POKER_STATE_FOLDED) {
not_folded++;
}
}
if (not_folded == 1) {
call_out("complete_round", 0, 1);
_poker_phase = POKER_STATE_END;
}
} else {
// Check and see if the round finished.
switch (_poker_phase) {
case POKER_STATE_ANTE :
check_for_finish_ante();
break;
case POKER_STATE_BET :
check_end_round();
break;
case POKER_STATE_DISCARD :
finish_discard();
break;
}
}
} /* resign_person() */
void event_exit(object ob, string mess, object to) {
string id;
if (userp(ob) && to != environment()) {
id = find_player_id_of_person(ob);
if (id) {
resign_person(id);
}
}
} /* event_exit() */
void multiplayer_someone_resigns(string id, string name) {
resign_person(id);
} /* multiplayer_someone_resigns() */
/** @ignore yes */
mapping query_dynamic_auto_load() {
mapping map;
map = commercial::query_dynamic_auto_load();
multiplayer_base::query_dynamic_auto_load(map);
add_auto_load_value(map, BOARD_TAG, "return", _return);
//add_auto_load_value(map, BOARD_TAG, "revenue", _revenue);
add_auto_load_value(map, BOARD_TAG, "ante amount", _ante_amount);
add_auto_load_value(map, BOARD_TAG, "house cut", _house_cut);
add_auto_load_value(map, BOARD_TAG, "ante house", _ante_house);
add_auto_load_value(map, BOARD_TAG, "min bet", _min_bet);
add_auto_load_value(map, BOARD_TAG, "max bet", _max_bet);
add_auto_load_value(map, BOARD_TAG, "current bet", _current_bet);
add_auto_load_value(map, BOARD_TAG, "pot", _pot);
add_auto_load_value(map, BOARD_TAG, "deck", _deck);
add_auto_load_value(map, BOARD_TAG, "poker phase", _poker_phase);
add_auto_load_value(map, BOARD_TAG, "draw round", _draw_round);
add_auto_load_value(map, BOARD_TAG, "max draw rounds", _max_draw_rounds);
add_auto_load_value(map, BOARD_TAG, "discard", _discard);
add_auto_load_value(map, BOARD_TAG, "player stats", _player_stats);
return map;
} /* query_dynamic_auto_load() */
/** @ignore yes */
void init_dynamic_arg(mapping map, object player) {
commercial::init_dynamic_arg(map, player);
multiplayer_base::init_dynamic_arg(map, player);
_return = query_auto_load_value(map, BOARD_TAG, "return");
//_revenue = query_auto_load_value(map, BOARD_TAG, "revenue");
_ante_amount = query_auto_load_value(map, BOARD_TAG, "ante amount");
_ante_house = query_auto_load_value(map, BOARD_TAG, "ante house");
_house_cut = query_auto_load_value(map, BOARD_TAG, "house cut");
_deck = query_auto_load_value(map, BOARD_TAG, "deck");
_current_bet = query_auto_load_value(map, BOARD_TAG, "current bet");
_pot = query_auto_load_value(map, BOARD_TAG, "pot");
_min_bet = query_auto_load_value(map, BOARD_TAG, "min bet");
_max_bet = query_auto_load_value(map, BOARD_TAG, "max bet");
_poker_phase = query_auto_load_value(map, BOARD_TAG, "poker phase");
_draw_round = query_auto_load_value(map, BOARD_TAG, "draw round");
_max_draw_rounds = query_auto_load_value(map, BOARD_TAG, "max draw rounds");
_discard = query_auto_load_value(map, BOARD_TAG, "discard");
_player_stats = query_auto_load_value(map, BOARD_TAG, "player stats");
if (!_player_stats) {
_player_stats = ([ ]);
}
} /* init_dynamic_arg() */