/* 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 "Options.hpp" #include "globals.hpp" #include "support.hpp" #include "callbacks.hpp" #include "HumanGuiPlayer.hpp" HumanGuiPlayer::HumanGuiPlayer(Common::PlayerPosition myPos) : Player(myPos), GuiPlayer(myPos), itsState(INIT), itsPartnerLoner(0) { char* formatStr = "pcard%d"; char pcardStr[100]; for (int i = 0; i < (Common::CARDS_PER_HAND+1); i++) { sprintf(pcardStr, formatStr, i); itsCards[i] = lookup_widget(mainwin, pcardStr); GList* children = gtk_container_children(GTK_CONTAINER(itsCards[i])); itsCardBacks[i] = GTK_WIDGET(children->data); g_list_free(children); itsCardStates[i] = NO_CARD_STATE; } for (int i = 0; i <= Card::Spades; i++) { sprintf(pcardStr, "bid_%d", i); itsBidButtons[i] = lookup_widget(mainwin, pcardStr); } itsLonerBidButton = lookup_widget(mainwin, "bid_loner"); itsPassBidButton = lookup_widget(mainwin, "bid_pass"); } HumanGuiPlayer::~HumanGuiPlayer() { LOG("in HumanGuiPlayer::~HumanGuiPlayer" << endl); for (int i = 0; i < (Common::CARDS_PER_HAND+1); i++) { GList* children = gtk_container_children(GTK_CONTAINER(itsCards[i])); if (GTK_WIDGET(children->data) != itsCardBacks[i]) { gtk_container_remove(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(children->data)); gtk_container_add(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(itsCardBacks[i])); } g_list_free(children); } } void HumanGuiPlayer::setHand(const Hand& theHand) { LOG("enter hgp setHand" << endl); /* sort theHand and stick it into itsHand */ Player::setHand(theHand); sortHand(); for (int i = 0; i < Common::CARDS_PER_HAND; i++) { int num = itsHand.getCard(i).getNumber(); int suit = itsHand.getCard(i).getSuit(); gtk_container_remove(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(itsCardBacks[i])); gtk_container_add(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(cardPixmaps[num][suit])); } LOG("exit hgp setHand" << endl); } void HumanGuiPlayer::assignBid(Common::Bid bid, Card::Suit trump) { LOG("enter hgp setBid with " << bid << endl); itsBid = bid; itsCalledTrump = trump; itsState = INAUCTION2; LOG("exit hgp setBid" << endl); } int HumanGuiPlayer::isInteractive() const { return 1; } void HumanGuiPlayer::setTopCard(const Card& upCard) { setCardPixmap(cardPixmaps[upCard.getNumber()][upCard.getSuit()]); } void HumanGuiPlayer::unsetTopCard() { setCardPixmap(itsOldPixmap); } Common::Bid HumanGuiPlayer::auction1(const Card& upCard, Common::PlayerPosition dealer) { LOG("enter hgp auction 1" << endl); if (itsState != INAUCTION2) { itsState = INAUCTION1; itsBid = Common::NOBID; for (int i = 0; i < (Card::Spades+1); i++) { if (upCard.getSuit() == i) { gtk_widget_set_sensitive(itsBidButtons[i], TRUE); } else { gtk_widget_set_sensitive(itsBidButtons[i], FALSE); } } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(itsLonerBidButton), FALSE); gtk_widget_set_sensitive(itsLonerBidButton, TRUE); gtk_widget_set_sensitive(itsPassBidButton, TRUE); } else { for (int i = 0; i < (Card::Spades+1); i++) { gtk_widget_set_sensitive(itsBidButtons[i], FALSE); } gtk_widget_set_sensitive(itsLonerBidButton, FALSE); gtk_widget_set_sensitive(itsPassBidButton, FALSE); } if (itsBid == Common::PICKITUP) { setBidSuitPixmap(bidSuitPixmaps[upCard.getSuit()]); setBidCallPixmap(bidCallPixmaps[0]); } else if (itsBid == Common::LONER) { setBidSuitPixmap(bidSuitPixmaps[upCard.getSuit()]); setBidCallPixmap(bidCallPixmaps[1]); } else if (itsBid == Common::PASS) { setMarker(passMarkers[itsPos]); } LOG("exit hgp auction1 with " << itsBid << endl); return itsBid; } void HumanGuiPlayer::auction1End(const Card& upCard) { setCardPixmap(itsOldPixmap); itsState = INAUCTION1; setMarker(NULL); } Common::Bid HumanGuiPlayer::auction2(Card& yourTrump, const Card& upCard, bool stuck) { LOG("enter hgp auction2" << endl); if (itsState != INAUCTION2) { itsState = INAUCTION1; itsBid = Common::NOBID; for (int i = 0; i < (Card::Spades+1); i++) { if (upCard.getSuit() == i) { gtk_widget_set_sensitive(itsBidButtons[i], FALSE); } else { gtk_widget_set_sensitive(itsBidButtons[i], TRUE); } } gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(itsLonerBidButton), FALSE); gtk_widget_set_sensitive(itsLonerBidButton, TRUE); /* if we are playing stick the dealer and we are the dealer then we cannot pass. */ if (stuck) { gtk_widget_set_sensitive(itsPassBidButton, FALSE); } else { gtk_widget_set_sensitive(itsPassBidButton, TRUE); } } else { for (int i = 0; i < (Card::Spades+1); i++) { gtk_widget_set_sensitive(itsBidButtons[i], FALSE); } gtk_widget_set_sensitive(itsLonerBidButton, FALSE); gtk_widget_set_sensitive(itsPassBidButton, FALSE); yourTrump.setSuit(itsCalledTrump); } if (itsBid == Common::PICKITUP) { setBidSuitPixmap(bidSuitPixmaps[yourTrump.getSuit()]); setBidCallPixmap(bidCallPixmaps[0]); } else if (itsBid == Common::LONER) { setBidSuitPixmap(bidSuitPixmaps[yourTrump.getSuit()]); setBidCallPixmap(bidCallPixmaps[1]); } else if (itsBid == Common::PASS) { setMarker(passMarkers[itsPos]); } LOG("exit hgp auction2 with " << itsBid << " and suit " << yourTrump.getSuit() << endl); return itsBid; } Card HumanGuiPlayer::discard(Card& newCard) { LOG("enter hgp discard" << endl); /* if we aren't playing then just skip this */ if (itsPartnerLoner == 1) { return newCard; } Card ret; int num = newCard.getNumber(); int suit = newCard.getSuit(); if (itsState == DISCARD2) { LOG("hgp discard with " << itsSelectedCard << " and " << newCard << endl); itsState = INROUND; /* hide the 6th card */ gtk_container_remove(GTK_CONTAINER(itsCards[Common::CARDS_PER_HAND]), GTK_WIDGET(cardPixmaps[num][suit])); gtk_container_add(GTK_CONTAINER(itsCards[Common::CARDS_PER_HAND]), GTK_WIDGET(itsCardBacks[Common::CARDS_PER_HAND])); /* I guess you can discard the up card ?? */ if (itsSelectedCard != Common::CARDS_PER_HAND) { ret = itsHand.getCard(itsSelectedCard); itsHand.setCard(itsSelectedCard, newCard); /* replace the selected card */ gtk_container_remove(GTK_CONTAINER(itsCards[itsSelectedCard]), GTK_WIDGET(cardPixmaps[ret.getNumber()][ret.getSuit()])); gtk_container_add(GTK_CONTAINER(itsCards[itsSelectedCard]), GTK_WIDGET(cardPixmaps[num][suit])); } else { ret = newCard; } /* deactivate all the buttons */ for (int i = 0; i < Common::CARDS_PER_HAND; i++) { itsCardStates[i] = NO_CARD_STATE; gtk_signal_disconnect_by_func(GTK_OBJECT(itsCards[i]), GTK_SIGNAL_FUNC(on_pcard_clicked), (void*) i); } /* don't let the signal propogate any further. After we return from here the first trick will start and we will reattach the signal for the pcards in getCard() below. As this function is called as a result of a signal being received, if we don't stop the propogation the function registered in getCard() will be invoked for the discard button click. Not a good thing. */ gtk_signal_emit_stop_by_name(GTK_OBJECT(itsCards[itsSelectedCard]), "clicked"); /* resort and show the hand again */ resortAndShowHand(); } else { itsState = DISCARD1; /* show the 5th card */ gtk_container_remove(GTK_CONTAINER(itsCards[Common::CARDS_PER_HAND]), GTK_WIDGET(itsCardBacks[Common::CARDS_PER_HAND])); gtk_container_add(GTK_CONTAINER(itsCards[Common::CARDS_PER_HAND]), GTK_WIDGET(cardPixmaps[num][suit])); /* activate all the buttons */ for (int i = 0; i < Common::CARDS_PER_HAND; i++) { if (itsCardStates[i] == VALID_CARD) { LOG("no attach to " << i << endl); continue; } if (itsCardStates[i] == INVALID_CARD) { gtk_signal_disconnect_by_func(GTK_OBJECT(itsCards[i]), GTK_SIGNAL_FUNC(on_bad_pcard_clicked), (void*) i); } itsCardStates[i] = VALID_CARD; gtk_signal_connect (GTK_OBJECT(itsCards[i]), "clicked", GTK_SIGNAL_FUNC(on_pcard_clicked), (void*) i); } } LOG("exit hgp discard with " << ret << endl); return ret; } void HumanGuiPlayer::setBid(Common::PlayerPosition who, Common::Bid bid) { LOG("enter hgp setBid" << endl); if (bid == Common::LONER && who == Common::getPartner(itsPos)) { itsPartnerLoner = 1; for (int i = 0; i < Common::CARDS_PER_HAND; i++) { int num = itsHand.getCard(i).getNumber(); int suit = itsHand.getCard(i).getSuit(); gtk_container_remove(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(cardPixmaps[num][suit])); gtk_container_add(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(itsCardBacks[i])); } } else { itsPartnerLoner = 0; } setMarker(NULL); LOG("exit hgp setBid" << endl); } void HumanGuiPlayer::setTrump(Card::Suit trump) { LOG("enter hgp setTrump with " << trump << endl); setCardPixmap(itsOldPixmap); itsState = INROUND; if (itsPartnerLoner == 0) { resortAndShowHand(); } LOG("exit hgp setTrump" << endl); } Card HumanGuiPlayer::getCard(const Round& theRound, Common::PlayerPosition whoStarted) { LOG("enter hgp getCard" << endl); Card ret; if (Options::get()->getAutoPlay() == 1) { int t = getAutoPlayCard(theRound, whoStarted); if (t >= 0) { /* if we find a card to play then make it look like the user clicked on it */ itsState = GETCARD2; itsSelectedCard = t; } } if (itsState == GETCARD2) { itsState = INROUND; ret = itsHand.removeCard(itsSelectedCard); gtk_container_remove(GTK_CONTAINER(itsCards[itsSelectedCard]), cardPixmaps[ret.getNumber()][ret.getSuit()]); gtk_container_add(GTK_CONTAINER(itsCards[itsSelectedCard]), GTK_WIDGET(itsCardBacks[itsSelectedCard])); setCardPixmap(cardPixmaps[ret.getNumber()][ret.getSuit()]); /* disconnect all signal handlers on the buttons */ for (int i = 0; i < Common::CARDS_PER_HAND; i++) { if (itsCardStates[i] == VALID_CARD) { gtk_signal_disconnect_by_func(GTK_OBJECT(itsCards[i]), GTK_SIGNAL_FUNC(on_pcard_clicked), (void*) i); } else if (itsCardStates[i] == INVALID_CARD) { gtk_signal_disconnect_by_func(GTK_OBJECT(itsCards[i]), GTK_SIGNAL_FUNC(on_bad_pcard_clicked), (void*) i); } itsCardStates[i] = NO_CARD_STATE; } } else if (itsState == INROUND) { itsState = GETCARD1; /* if we have the suit led then we have to follow suit */ Card::Suit suitLed = theRound.getSuit(whoStarted); if (suitLed != Card::NoSuit && itsHand.contains(suitLed)) { for (int i = 0; i < Common::CARDS_PER_HAND; i++) { if (itsHand.getCard(i).isSuit(suitLed)) { LOG(" " << itsPos << " " << i << " good card " << itsHand.getCard(i) << endl); itsCardStates[i] = VALID_CARD; gtk_signal_connect (GTK_OBJECT(itsCards[i]), "clicked", GTK_SIGNAL_FUNC(on_pcard_clicked), (void*) i); } else { LOG(" " << itsPos << " " << i << " bad card " << itsHand.getCard(i) << endl); itsCardStates[i] = INVALID_CARD; gtk_signal_connect(GTK_OBJECT(itsCards[i]), "clicked", GTK_SIGNAL_FUNC(on_bad_pcard_clicked), (void*) i); } } } else { /* otherwise all cards are available to be played */ for (int i = 0; i < Common::CARDS_PER_HAND; i++) { if (itsHand.getCard(i).getSuit() != Card::NoSuit) { LOG(" " << itsPos << " " << i << " good card " << itsHand.getCard(i) << endl); itsCardStates[i] = VALID_CARD; gtk_signal_connect(GTK_OBJECT(itsCards[i]), "clicked", GTK_SIGNAL_FUNC(on_pcard_clicked), (void*) i); } else { LOG(" " << itsPos << " " << i << " bad card " << itsHand.getCard(i) << endl); itsCardStates[i] = INVALID_CARD; gtk_signal_connect(GTK_OBJECT(itsCards[i]), "clicked", GTK_SIGNAL_FUNC(on_bad_pcard_clicked), (void*) i); } } } } else { LOG("error hdp getCard called with bad state " << itsState << endl); } LOG("exit hgp get Card with state " << itsState << " and card " << ret << endl); return ret; } void HumanGuiPlayer::finishRound(const Round& theRound, Common::PlayerPosition whoStarted) { LOG("enter hgp finishRound" << endl); if (itsPartnerLoner == 0) { setCardPixmap(itsOldPixmap); itsState = INROUND; } LOG("exit hgp finishRound" << endl); } void HumanGuiPlayer::finishDeal(int NSPoints, int EWPoints) { /* make sure all pixmaps are set back to the original card back */ for (int i = 0; i < (Common::CARDS_PER_HAND+1); i++) { GList* children = gtk_container_children(GTK_CONTAINER(itsCards[i])); if (GTK_WIDGET(children->data) != itsCardBacks[i]) { gtk_container_remove(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(children->data)); gtk_container_add(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(itsCardBacks[i])); } g_list_free(children); } /* erase bid pixmap if there was one */ setBidSuitPixmap(NULL); setBidCallPixmap(NULL); } int HumanGuiPlayer::setSelectedCard(int index) { int ret = 1; if (itsState == GETCARD1) { itsSelectedCard = index; itsState = GETCARD2; } else if (itsState == DISCARD1) { itsSelectedCard = index; itsState = DISCARD2; } else { LOG("in hgp setSelectedCard with bad state " << itsState << endl); ret = 0; } return ret; } void HumanGuiPlayer::allPass() { /* reset all cards to their background */ for (int i = 0; i < Common::CARDS_PER_HAND; i++) { int num = itsHand.getCard(i).getNumber(); int suit = itsHand.getCard(i).getSuit(); gtk_container_remove(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(cardPixmaps[num][suit])); gtk_container_add(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(itsCardBacks[i])); } /* reset our bid pixmap and bid marker as well */ setCardPixmap(itsOldPixmap); setMarker(NULL); itsState = ENDROUND; } void HumanGuiPlayer::resortAndShowHand() { /* take away the old card pixmaps */ for (int i = 0; i < Common::CARDS_PER_HAND; i++) { int num = itsHand.getCard(i).getNumber(); int suit = itsHand.getCard(i).getSuit(); gtk_container_remove(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(cardPixmaps[num][suit])); } /* resort the hand based on the new trump values */ sortHand(); /* and the resorted card pixmaps */ for (int i = 0; i < Common::CARDS_PER_HAND; i++) { int num = itsHand.getCard(i).getNumber(); int suit = itsHand.getCard(i).getSuit(); gtk_container_add(GTK_CONTAINER(itsCards[i]), GTK_WIDGET(cardPixmaps[num][suit])); } } void HumanGuiPlayer::showBidWindow(GtkWidget* window) { GdkWindow* win; gint x, y, width, height, depth = 0; win = GTK_WIDGET(mainwin)->window; gdk_window_get_geometry(win, &x, &y, &width, &height, &depth); gdk_window_get_root_origin(win, &x, &y); gtk_widget_set_uposition(window, (x + 110), (y + (height -10))); gtk_widget_show(window); } int HumanGuiPlayer::getAutoPlayCard(const Round& theRound, Common::PlayerPosition whoStarted) { Card::Suit suitLed = theRound.getSuit(whoStarted); /* if there is only one card left then return that */ if (itsHand.cardsLeft() == 1) { return itsHand.getBestCardIndex(); } /* if we aren't leading and we only have one of the led suit then return that */ if (whoStarted != itsPos && itsHand.count(suitLed) == 1) { return itsHand.getBestCardIndex(suitLed); } return -1; }