/* Euchre - a free as in freedom and as in beer version of the euchre card game Copyright 2002 C Nathan Buckles (nbuckles@bigfoot.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include "globals.hpp" #include "support.hpp" #include "ComputerPlayerEasy.hpp" #include "ComputerPlayerMedium.hpp" #include "ComputerPlayerHard.hpp" #include "ComputerGuiPlayer.hpp" #include "HumanGuiPlayer.hpp" #include "GuiOptions.hpp" #include "GuiGame.hpp" // predeclare timeout function static gint gui_game_gtk_timeout(GuiGame* gg); GuiGame::GuiGame() { for (int i = 0; i < NUMLABELS; i++) { itsLabelText[i] = new char[100]; } itsLabels[LEADER] = lookup_widget(mainwin, "Leader"); itsLabels[SUITLED] = lookup_widget(mainwin, "SuitLed"); itsLabels[WINNER] = lookup_widget(mainwin, "Winner"); itsLabels[NSTRICKS] = lookup_widget(mainwin, "NSTricks"); itsLabels[EWTRICKS] = lookup_widget(mainwin, "EWTricks"); itsLabels[TRUMP] = lookup_widget(mainwin, "Trump"); itsLabels[CALLER] = lookup_widget(mainwin, "Caller"); itsLabels[DEALER] = lookup_widget(mainwin, "Dealer"); itsLabels[NSPOINTS] = lookup_widget(mainwin, "NSPoints"); itsLabels[EWPOINTS] = lookup_widget(mainwin, "EWPoints"); itsLabelVars[LEADER] = NULL; itsLabelVars[SUITLED] = NULL; itsLabelVars[WINNER] = NULL; itsLabelVars[NSTRICKS] = &itsNSTricks; itsLabelVars[EWTRICKS] = &itsEWTricks; itsLabelVars[TRUMP] = NULL; itsLabelVars[CALLER] = NULL; itsLabelVars[DEALER] = NULL; itsLabelVars[NSPOINTS] = &itsNSPoints; itsLabelVars[EWPOINTS] = &itsEWPoints; itsLabelTables[0] = lookup_widget(mainwin, "trick_stat_table"); itsLabelTables[1] = lookup_widget(mainwin, "round_stat_table"); itsLabelTables[2] = lookup_widget(mainwin, "game_stat_table"); itsStatus = lookup_widget(mainwin, "status"); strcpy(itsStatusText, "Click File->New to start a game"); updateStatus(); itsMainwinNames[0] = lookup_widget(mainwin, "main_north_label"); itsMainwinNames[1] = lookup_widget(mainwin, "main_east_label"); itsMainwinNames[2] = lookup_widget(mainwin, "main_south_label"); itsMainwinNames[3] = lookup_widget(mainwin, "main_west_label"); itsOrigLabelStyle = gtk_widget_get_style(itsMainwinNames[0]); itsBlueLabelStyle = gtk_style_copy(itsOrigLabelStyle); itsRedLabelStyle = gtk_style_copy(itsOrigLabelStyle); itsPurpleLabelStyle = gtk_style_copy(itsOrigLabelStyle); GdkColormap* colormap = gdk_colormap_get_system(); GdkColor blue_color = { 0, 0, 0, 0xbbbb }; GdkColor red_color = { 0, 0xbbbb, 0, 0 }; GdkColor purple_color = { 0, 0xbbbb, 0, 0xbbbb }; gdk_colormap_alloc_color(colormap, &blue_color, FALSE, TRUE ); gdk_colormap_alloc_color(colormap, &red_color, FALSE, TRUE ); gdk_colormap_alloc_color(colormap, &purple_color, FALSE, TRUE ); itsBlueLabelStyle->fg[0] = blue_color; itsBlueLabelStyle->fg[1] = blue_color; itsBlueLabelStyle->fg[2] = blue_color; itsBlueLabelStyle->fg[3] = blue_color; itsBlueLabelStyle->fg[4] = blue_color; itsRedLabelStyle->fg[0] = red_color; itsRedLabelStyle->fg[1] = red_color; itsRedLabelStyle->fg[2] = red_color; itsRedLabelStyle->fg[3] = red_color; itsRedLabelStyle->fg[4] = red_color; itsPurpleLabelStyle->fg[0] = purple_color; itsPurpleLabelStyle->fg[1] = purple_color; itsPurpleLabelStyle->fg[2] = purple_color; itsPurpleLabelStyle->fg[3] = purple_color; itsPurpleLabelStyle->fg[4] = purple_color; itsPrevTrickTable = lookup_widget(prevdiag, "prev_trick_table"); itsPrevTrickPixmapsOrig[0] = lookup_widget(prevdiag, "nprevtrick"); itsPrevTrickPixmapsOrig[1] = lookup_widget(prevdiag, "eprevtrick"); itsPrevTrickPixmapsOrig[2] = lookup_widget(prevdiag, "sprevtrick"); itsPrevTrickPixmapsOrig[3] = lookup_widget(prevdiag, "wprevtrick"); itsPrevTrickPixmapsCur[0] = itsPrevTrickPixmapsOrig[0]; itsPrevTrickPixmapsCur[1] = itsPrevTrickPixmapsOrig[1]; itsPrevTrickPixmapsCur[2] = itsPrevTrickPixmapsOrig[2]; itsPrevTrickPixmapsCur[3] = itsPrevTrickPixmapsOrig[3]; itsPrevTrickButtons[0] = lookup_widget(prevdiag, "prev_prev_trick"); itsPrevTrickButtons[1] = lookup_widget(prevdiag, "next_prev_trick"); itsPrevTrickNumber = lookup_widget(prevdiag, "prev_trick_number"); itsPrevTrickLed = lookup_widget(prevdiag, "prev_trick_ledby"); itsPrevTrickWinner = lookup_widget(prevdiag, "prev_trick_winner"); itsPrevTrickCoords[0][0] = 1; itsPrevTrickCoords[0][1] = 3; itsPrevTrickCoords[0][2] = 0; itsPrevTrickCoords[0][3] = 1; itsPrevTrickCoords[1][0] = 3; itsPrevTrickCoords[1][1] = 4; itsPrevTrickCoords[1][2] = 1; itsPrevTrickCoords[1][3] = 2; itsPrevTrickCoords[2][0] = 1; itsPrevTrickCoords[2][1] = 3; itsPrevTrickCoords[2][2] = 2; itsPrevTrickCoords[2][3] = 3; itsPrevTrickCoords[3][0] = 0; itsPrevTrickCoords[3][1] = 1; itsPrevTrickCoords[3][2] = 1; itsPrevTrickCoords[3][3] = 2; itsHasTimeout = 0; } GuiGame::~GuiGame() { for (int i = 0; i < 6; i++) { delete [] itsLabelText[i]; } } void GuiGame::resetForGame() { Game::resetForGame(); /* be sure this is cleaned up in case we are reset in the middle of a game */ hideTopCard(); /* clean up the prev trick diag */ clearPrevTrickDiag(); } void GuiGame::resetForDeal() { char label_name[256]; GtkWidget* label_widget; Game::resetForDeal(); updateLabels(); updateStatus(""); setMainLabelColors(); clearPrevTrickDiag(); } void GuiGame::processEvent(Event ev) { LOG("Enter GuiGame::processEvent with ev = " << ev << endl); if (ev == PAUSE_END) { unpause(); itsHasTimeout = 0; /* just in case we set a timeout but came in here because of a root window click */ gtk_timeout_remove(itsTimeoutTag); switch (itsState) { case INAUCTION1: case INAUCTION2: addEvent(AUCTION_CONT); break; case AFTERAUCTION1: hideTopCard(); /* fall through */ case AFTERAUCTION2: Game::auctionEnd(); updateLabels(); break; case INDISCARD: Game::discardStart(); break; case INROUND: addEvent(ROUND_CONT); break; case AFTERROUND: { for (int i = 0; i < Common::PLAYERS_PER_GAME; i++) { itsPlayers[i]->finishRound(itsRounds[itsRoundCount], itsOldLeader); } // this updates itsRoundCount so we can now update the // previous tricks window. int over = isDealOver(); // show the last trick played if (itsPrevTrickVisible) { itsPrevTrickCounter = (itsRoundCount - 1); configPrevTrickDiag(); } if (over) { addEvent(DEAL_END); } else { addEvent(ROUND_START); } } break; case AFTERDEAL: Game::gameEnd(); updateLabels(); break; default: LOG("error GuiGame processEvent called with bad state " << itsState << endl); break; } } else { Game::processEvent(ev); } } void GuiGame::getPlayers() { for (int p = 0; p < Common::PLAYERS_PER_GAME; p++) { if (itsPlayers[p] != NULL) { delete itsPlayers[p]; } } itsPlayers[Common::SOUTH] = new HumanGuiPlayer(Common::SOUTH); itsPlayers[Common::NORTH] = getComputerPlayer(Common::NORTH); itsPlayers[Common::EAST] = getComputerPlayer(Common::EAST); itsPlayers[Common::WEST] = getComputerPlayer(Common::WEST); } void GuiGame::auctionCont() { Common::PlayerPosition pre, post; // set the label colors. definetly overkill to set them all but we // need to clean up from last time through... setAuctionLabelColors(); pre = itsTurnToPlay; Game::auctionCont(); post = itsTurnToPlay; if (pre == post) { if (itsState == INAUCTION1) { strcpy(itsStatusText, "Enter your bid"); } else { if (itsDealer == pre && GuiOptions::get()->getStickTheDealer()) { strcpy(itsStatusText, "You must bid! (Stick the dealer is on!)"); } else { strcpy(itsStatusText, "Pick your suit and/or enter your bid"); } } updateStatus(); } } void GuiGame::auctionEnd() { LOG("!!!!! enter GuiGame::auctionEnd() !!!!" << endl); if (itsState == AFTERAUCTION1) { sprintf(itsStatusText, "Auction 1 is over. %s", getPauseText()); } else { sprintf(itsStatusText, "Auction 2 is over. %s", getPauseText()); } updateStatus(); LOG("!!! auctionEnd calling pause" << endl); pause(); } void GuiGame::discardStart() { Game::discardStart(); if (itsPlayers[itsDealer]->isInteractive()) { strcpy(itsStatusText, "Click a card to discard"); updateStatus(); itsPaused = 1; } } void GuiGame::roundStart() { setMainLabelColors(); Game::roundStart(); } void GuiGame::roundCont() { Common::PlayerPosition pre, post; setMainLabelColors(); pre = itsTurnToPlay; Game::roundCont(); post = itsTurnToPlay; if (itsState == INROUND) { /* if we stayed with the same interactive player then prompt then to play a card */ if (pre == post && itsPlayers[pre]->isInteractive()) { strcpy(itsStatusText, "Click a card to play"); updateStatus(); itsPaused = 1; } } } void GuiGame::roundEnd() { updateLabels(); itsState = AFTERROUND; itsOldLeader = itsLeader; itsLeader = itsRounds[itsRoundCount].getWinner(itsLeader, Common::getTrump()); itsRoundWinner[itsRoundCount] = itsLeader; updateTrickCounts(itsLeader); LOG("!!! roundEnd calling pause" << endl); pause(); } void GuiGame::gameEnd() { LOG("!!! gameEnd calling pause" << endl); pause(); } void GuiGame::gameEndForReal() { sprintf(itsStatusText, "%s win the game. Click File->New to start a game", ((itsNSPoints >= GuiOptions::get()->getPointsForGame()) ? "NS" : "EW")); updateStatus(); } void GuiGame::updateTrickCounts(Common::PlayerPosition theWinner) { Game::updateTrickCounts(theWinner); sprintf(itsStatusText, "%s wins the trick. %s", Common::getPlayerPositionStr(theWinner), getPauseText()); updateStatus(); updateLabels(); } void GuiGame::updateLabels() { int start_index = 0; /* dont update trick stats when not INROUND */ if (itsState != INROUND) { /* but dont erase if AFTERROUND */ if (itsState != AFTERROUND) { for (int i = LEADER; i <= WINNER; i++) { strcpy(itsLabelText[i], ""); gtk_label_set(GTK_LABEL(itsLabels[i]), itsLabelText[i]); } } start_index = (WINNER+1); } #if 0 /* dont update round stats when not in a round */ if (itsState < INROUND || itsState > AFTERDEAL) { for (int i = NSTRICKS; i <= DEALER; i++) { strcpy(itsLabelText[i], ""); gtk_label_set(GTK_LABEL(itsLabels[i]), itsLabelText[i]); } start_index = (DEALER+1); } #endif for (int i = start_index; i < NUMLABELS; i++) { if (i == TRUMP) { if (Common::getTrump() == Card::NoSuit) { strcpy(itsLabelText[i], ""); } else { strcpy(itsLabelText[i], Card::getSuitStr(Common::getTrump())); } } else if (i == CALLER) { if (itsCaller == Common::NOPOS) { strcpy(itsLabelText[i], ""); } else { strcpy(itsLabelText[i], Common::getPlayerPositionStr(itsCaller)); } } else if (i == DEALER) { strcpy(itsLabelText[i], Common::getPlayerPositionStr(itsDealer)); } else if (i == LEADER) { strcpy(itsLabelText[i], Common::getPlayerPositionStr(itsLeader)); } else if (i == SUITLED) { /* adjust for little jack lead */ Card::Suit suit = itsRounds[itsRoundCount].getSuit(itsLeader); strcpy(itsLabelText[i], Card::getSuitStr(suit)); } else if (i == WINNER) { Common::PlayerPosition pos = itsRounds[itsRoundCount].getWinner(itsLeader, Common::getTrump()); strcpy(itsLabelText[i], Common::getPlayerPositionStr(pos)); } else { sprintf(itsLabelText[i], "%d", *itsLabelVars[i]); } gtk_label_set(GTK_LABEL(itsLabels[i]), itsLabelText[i]); } } void GuiGame::updateStatus(char* statusStr) { strcpy(itsStatusText, statusStr); updateStatus(); } void GuiGame::updateStatus() { gtk_label_set(GTK_LABEL(itsStatus), itsStatusText); } void GuiGame::setTopCard() { Game::setTopCard(); itsPlayers[itsDealer]->setTopCard(itsTopCard); } void GuiGame::hideTopCard() { itsPlayers[itsDealer]->unsetTopCard(); } void GuiGame::delayBetweenPlays() { setTimeout(1000); } void GuiGame::pause() { itsPaused = 1; if (strcmp(itsStatusText, "") == 0) { strcpy(itsStatusText, getPauseText()); updateStatus(); } switch (GuiOptions::get()->getDelayMode()) { case Options::NODELAY: addEvent(PAUSE_END); return; case Options::SDELAY: setTimeout(2000); break; case Options::MDELAY: setTimeout(5000); break; case Options::LDELAY: setTimeout(10000); break; case Options::PAUSE: return; } } void GuiGame::unpause() { strcpy(itsStatusText, ""); updateStatus(); Game::unpause(); } void GuiGame::updateLonerPoints() { Game::updateLonerPoints(); sprintf(itsStatusText, "%s get 4 points for loner take all! %s", ((itsCaller % 2) ? "EW" : "NS"), getPauseText()); updateStatus(); } void GuiGame::updateTakeAllPoints() { Game::updateTakeAllPoints(); sprintf(itsStatusText, "%s get 2 points for taking them all! %s", ((itsCaller % 2) ? "EW" : "NS"), getPauseText()); updateStatus(); } void GuiGame::updateTakeEnufPoints() { Game::updateTakeEnufPoints(); sprintf(itsStatusText, "%s get 1 point for making game! %s", ((itsCaller % 2) ? "EW" : "NS"), getPauseText()); updateStatus(); updateLabels(); } void GuiGame::updateSetPoints() { Game::updateSetPoints(); sprintf(itsStatusText, "%s get 2 points for setting! %s", ((itsCaller % 2) ? "NS" : "EW"), getPauseText()); updateStatus(); updateLabels(); } void GuiGame::allPass() { Game::allPass(); sprintf(itsStatusText, "Everyone passed! %s", getPauseText()); updateStatus(); updateLabels(); // don't need to pause as Game::allPass invokes the GAME_END event // which pauses for us (without updating status) } void GuiGame::bidMade(Common::PlayerPosition who, Common::Bid theBid) { int index = who; if (who < itsLeader) { index += Common::PLAYERS_PER_GAME; } if (itsState == INAUCTION2) { index += Common::PLAYERS_PER_GAME; } Game::bidMade(who, theBid); } void GuiGame::cardPlayed(Common::PlayerPosition who, const Card& theCard) { Game::cardPlayed(who, theCard); updateLabels(); } void GuiGame::setOptions() { typedef int (GuiOptions::* GUIOPTFUNCS)(); GUIOPTFUNCS guiOptFuncs[3]; guiOptFuncs[0] = &GuiOptions::getShowTrickStats; guiOptFuncs[1] = &GuiOptions::getShowRoundStats; guiOptFuncs[2] = &GuiOptions::getShowGameStats; for (int i = 0; i < 3; i++) { GUIOPTFUNCS t = guiOptFuncs[i]; if (((GuiOptions::get())->*t)()) { gtk_widget_show(itsLabelTables[i]); } else { gtk_widget_hide(itsLabelTables[i]); } } setCardBack(GuiOptions::get()->getCardBack()); } char* GuiGame::getPauseText() { if (GuiOptions::get()->getDelayMode() == Options::PAUSE) { return "Click to continue."; } return ""; } Player* GuiGame::getComputerPlayer(Common::PlayerPosition pos) { Player* p = NULL; switch (GuiOptions::get()->getAILevel(pos)) { case Options::EASY: p = new ComputerPlayerEasy(pos); break; case Options::MEDIUM: p = new ComputerPlayerMedium(pos); break; case Options::HARD: p = new ComputerPlayerHard(pos); break; default: LOG("ERROR: bad AI level for " << pos << endl); } return new ComputerGuiPlayer(pos, p); } void GuiGame::setMainLabelColors() { Common::PlayerPosition specialPosition; if (itsState < INDISCARD) { specialPosition = itsDealer; } else { specialPosition = itsCaller; } for (int i = 0; i < Common::PLAYERS_PER_GAME; i++) { if (i == specialPosition) { if (i == itsTurnToPlay) { gtk_widget_set_style(itsMainwinNames[i], itsPurpleLabelStyle); } else { gtk_widget_set_style(itsMainwinNames[i], itsBlueLabelStyle); } } else { if (i == itsTurnToPlay) { gtk_widget_set_style(itsMainwinNames[i], itsRedLabelStyle); } else { gtk_widget_set_style(itsMainwinNames[i], itsOrigLabelStyle); } } } } void GuiGame::setAuctionLabelColors() { for (int i = 0; i < Common::PLAYERS_PER_GAME; i++) { if (i == itsDealer) { if (i == itsTurnToPlay) { gtk_widget_set_style(itsMainwinNames[i], itsPurpleLabelStyle); } else { gtk_widget_set_style(itsMainwinNames[i], itsBlueLabelStyle); } } else { if (i == itsTurnToPlay) { gtk_widget_set_style(itsMainwinNames[i], itsRedLabelStyle); } else { gtk_widget_set_style(itsMainwinNames[i], itsOrigLabelStyle); } } } } void GuiGame::setTimeout(unsigned int milliseconds) { if (itsHasTimeout) { return; } itsTimeoutTag = gtk_timeout_add(milliseconds, (GtkFunction) gui_game_gtk_timeout, (gpointer) this); itsHasTimeout = 1; } void GuiGame::setCardBack(int which) { GdkColormap* gdk_c; GtkWidget* gtk_w; const int num_names = NUM_CARD_BACK_MAPS; const int num_in_mainwin = 10; char* names[num_names] = { "nplayedcard", "eplayedcard", "splayedcard", "wplayedcard", "pcard0_pix", "pcard1_pix", "pcard2_pix", "pcard3_pix", "pcard4_pix", "pcard5_pix", "nprevtrick", "eprevtrick", "sprevtrick", "wprevtrick" }; for (int i = 0; i < num_names; i++) { if (i < num_in_mainwin) { gtk_w = lookup_widget(mainwin, names[i]); } else { gtk_w = lookup_widget(prevdiag, names[i]); } gdk_c = gtk_widget_get_colormap(gtk_w); if (cardbackPixmaps[i][which] == NULL) { cardbackPixmaps[i][which] = gdk_pixmap_colormap_create_from_xpm_d(NULL, gdk_c, &(cardbackBitmaps[i][which]), NULL, card_back_pixmap[which]); } gtk_pixmap_set(GTK_PIXMAP(gtk_w), cardbackPixmaps[i][which], cardbackBitmaps[i][which]); } } void GuiGame::showPrevTrickDiag() { itsPrevTrickCounter = 0; itsPrevTrickVisible = 1; configPrevTrickDiag(); gtk_widget_show(prevdiag); } void GuiGame::hidePrevTrickDiag() { itsPrevTrickVisible = 0; gtk_widget_hide(prevdiag); } void GuiGame::incrPrevTrickDiag(int incrVal) { itsPrevTrickCounter += incrVal; configPrevTrickDiag(); } void GuiGame::configPrevTrickDiag() { if (itsPrevTrickCounter < 0) { itsPrevTrickCounter = 0; return; } else if (itsPrevTrickCounter >= itsRoundCount) { itsPrevTrickCounter = (itsRoundCount - 1); return; } for (int i = 0; i < Common::PLAYERS_PER_GAME; i++) { Card c = itsRounds[itsPrevTrickCounter].getCard((Common::PlayerPosition) i); // this will be the case for a player whose partner is going // loner. if (c.getSuit() == Card::NoSuit) { continue; } gtk_container_remove(GTK_CONTAINER(itsPrevTrickTable), itsPrevTrickPixmapsCur[i]); gtk_table_attach(GTK_TABLE(itsPrevTrickTable), cardPixmaps[c.getNumber()][c.getSuit()], itsPrevTrickCoords[i][0], itsPrevTrickCoords[i][1], itsPrevTrickCoords[i][2], itsPrevTrickCoords[i][3], (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 2, 2); itsPrevTrickPixmapsCur[i] = cardPixmaps[c.getNumber()][c.getSuit()]; } char string[256]; // adjust by one so we see 1 of 1 instead of 0 of 0 sprintf(string, "%d of %d", (itsPrevTrickCounter+1), itsRoundCount); gtk_label_set(GTK_LABEL(itsPrevTrickNumber), string); strcpy(string, Common::getPlayerPositionStr(itsRoundLeader[itsPrevTrickCounter])); gtk_label_set(GTK_LABEL(itsPrevTrickLed), string); strcpy(string, Common::getPlayerPositionStr(itsRoundWinner[itsPrevTrickCounter])); gtk_label_set(GTK_LABEL(itsPrevTrickWinner), string); } void GuiGame::clearPrevTrickDiag() { LOG("Enter clearPrevTrickDiag" << endl); for (int i = 0; i < Common::PLAYERS_PER_GAME; i++) { if (itsPrevTrickPixmapsCur[i] != itsPrevTrickPixmapsOrig[i]) { LOG("Enter clearPrevTrickDiag if block" << endl); gtk_container_remove(GTK_CONTAINER(itsPrevTrickTable), itsPrevTrickPixmapsCur[i]); gtk_table_attach(GTK_TABLE(itsPrevTrickTable), itsPrevTrickPixmapsOrig[i], itsPrevTrickCoords[i][0], itsPrevTrickCoords[i][1], itsPrevTrickCoords[i][2], itsPrevTrickCoords[i][3], (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 2, 2); itsPrevTrickPixmapsCur[i] = itsPrevTrickPixmapsOrig[i]; } } gtk_label_set(GTK_LABEL(itsPrevTrickNumber), ""); gtk_label_set(GTK_LABEL(itsPrevTrickLed), ""); gtk_label_set(GTK_LABEL(itsPrevTrickWinner), ""); } gint gui_game_gtk_timeout(GuiGame* gg) { gg->addEvent(GuiGame::PAUSE_END); gg->run(); return FALSE; }