diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | c90c389a8a8d9d8661e9772ec4144c5cf2039f23 (patch) | |
tree | 6d8391395bce9eaea4ad78958617edb20c6a7573 /kpoker/kpoker.cpp | |
download | tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.tar.gz tdegames-c90c389a8a8d9d8661e9772ec4144c5cf2039f23.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kpoker/kpoker.cpp')
-rw-r--r-- | kpoker/kpoker.cpp | 1484 |
1 files changed, 1484 insertions, 0 deletions
diff --git a/kpoker/kpoker.cpp b/kpoker/kpoker.cpp new file mode 100644 index 00000000..2956b42c --- /dev/null +++ b/kpoker/kpoker.cpp @@ -0,0 +1,1484 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <stdio.h> +#include <stdlib.h> + +// QT includes +#include <qlabel.h> +#include <qtimer.h> +#include <qpainter.h> +#include <qtooltip.h> +#include <qlayout.h> + +// KDE includes +#include <kmessagebox.h> +#include <kapplication.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kconfig.h> +#include <klocale.h> +#include <kaudioplayer.h> +#include <kcarddialog.h> + +// own includes +#include "betbox.h" +#include "kpaint.h" +#include "optionsdlg.h" +#include "newgamedlg.h" +#include "player.h" +#include "playerbox.h" +#include "version.h" +#include "kpoker.h" +#include "defines.h" + + +// ================================================================ +// Class PokerGame + + +PokerGame::PokerGame(KRandomSequence *random) + : m_deck(random) +{ + // We don't need to save if we just started. + m_isDirty = false; + + // Some defaults. + m_type = SinglePlayer; +} + + +PokerGame::~PokerGame() +{ + m_activePlayers.clear(); + m_removedPlayers.clear(); +} + + +void +PokerGame::dealCards(PokerPlayer *player, bool skip[]) +{ + CardValue card; + + for (int i = 0; i < PokerHandSize; i++) { + if (skip[i]) + continue; + + card = m_deck.getTopCard(); + + player->takeCard(i, card); + } +} + + +void +PokerGame::activatePlayer(PokerPlayer *player) +{ + if (!m_activePlayers.contains(player)) { + m_activePlayers.append(player); + m_removedPlayers.remove(player); + } +} + + +void +PokerGame::inactivatePlayer(PokerPlayer *player) +{ + if (m_activePlayers.contains(player)) { + m_activePlayers.remove(player); + m_removedPlayers.append(player); + } +} + + +void +PokerGame::newGame(PokerGameType type, + int numPlayers, PokerPlayer *players, + int minBet, int maxBet) +{ + m_type = type; + + // Store the players. These are never changed in the current implementation. + m_numPlayers = numPlayers; + m_players = players; + + setBettingLimits(minBet, maxBet); + + setState(StateStartRound); + + // Initiate player arrays. Make all players active from the start. + m_activePlayers.clear(); + m_removedPlayers.clear(); + for (unsigned int i = 0; i < m_numPlayers; i++) + m_activePlayers.append(&m_players[i]); + + // Inform players how much they may bet. + for (unsigned int i = 0; i < m_activePlayers.count(); i++) + m_activePlayers.at(i)->setBetDefaults(m_minBet, m_maxBet); +} + + +void +PokerGame::newRound() +{ + // Reset the pot to zero. + m_pot = 0; + + // Collect the cards and shuffle them. + m_deck.reset(); + m_deck.shuffle(); + + // Clear the list of active and removed players. + m_activePlayers.clear(); + m_removedPlayers.clear(); +} + + +// ================================================================ +// Global variables + + +CardImages *cardImages; + + +// ================================================================ +// Class kpok + + +kpok::kpok(QWidget *parent, const char *name) + : QWidget(parent, name), + m_game(&m_random) +{ + QString version; + + m_random.setSeed(0); + + // This class owns the players. Create them here. + // Currently we always allocate two - one human and one computer. + m_numPlayers = 2; // FIXME: Hard coded!! + m_players = new PokerPlayer[m_numPlayers]; + + // Initialize the first one to human... + m_players[0].setHuman(); + m_players[0].setName(i18n("You")); + + // ...and the rest to computer players. + for (int unsigned i = 1; i < m_numPlayers; i++) + m_players[i].setName(QString("Computer %1").arg(i-1)); + + lastHandText = ""; + + version = kapp->caption() + " " + KPOKER_VERSION; + setCaption( version ); + + mOptions = 0; + + playerBox = 0; + adjust = false; + + initWindow(); // FIXME: Change this name!!! + initSound(); + + if (!readEntriesAndInitPoker()) + exit(0); +} + + +kpok::~kpok() +{ + if (mOptions != 0) { + if (m_numPlayers > 1) + m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet()); + + drawDelay = mOptions->getDrawDelay(); + // cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable + } + + KConfig* conf = kapp->config(); + conf->setGroup("General"); + conf->writeEntry("MinBet", m_game.getMinBet()); + conf->writeEntry("MaxBet", m_game.getMaxBet()); + // conf->writeEntry("CashPerRound", cashPerRound); + conf->writeEntry("DrawDelay", drawDelay); + + + delete[] m_players; + + for (unsigned int i = 0; i < m_numPlayers; i++) + delete playerBox[i]; + delete[] playerBox; + + delete mOptions; +} + + +// Init the main window and load the configuration and card images. +// +// Only called by kpok::kpok() once. + +void kpok::initWindow() +{ + m_blinking = true; + m_blinkStat = 0; + m_blinkingBox = 0; + + // General font stuff. Define myFixedFont and wonFont. + QFont myFixedFont; + myFixedFont.setPointSize(12); + QFont wonFont; + wonFont.setPointSize(14); + wonFont.setBold(true); + + topLayout = new QVBoxLayout(this, BORDER); + QVBoxLayout* topInputLayout = new QVBoxLayout; + topLayout->addLayout(topInputLayout); + + QHBoxLayout* betLayout = new QHBoxLayout; + inputLayout = new QHBoxLayout; + inputLayout->addLayout(betLayout); + topInputLayout->addLayout(inputLayout); + + // The draw button + drawButton = new QPushButton(this); + drawButton->setText(i18n("&Deal")); + connect(drawButton, SIGNAL(clicked()), this, SLOT(drawClick())); + inputLayout->addWidget(drawButton); + inputLayout->addStretch(1); + + // The waving text + QFont waveFont; + waveFont.setPointSize(16); + waveFont.setBold(true); + QFontMetrics tmp(waveFont); + + // The widget where the winner is announced. + mWonWidget = new QWidget(this); + inputLayout->addWidget(mWonWidget, 2); + mWonWidget->setMinimumHeight(50); //FIXME hardcoded value for the wave + mWonWidget->setMinimumWidth(tmp.width(i18n("You won %1").arg(KGlobal::locale()->formatMoney(100))) + 20); // workaround for width problem in wave + QHBoxLayout* wonLayout = new QHBoxLayout(mWonWidget); + wonLayout->setAutoAdd(true); + + wonLabel = new QLabel(mWonWidget); + wonLabel->setFont(wonFont); + wonLabel->setAlignment(AlignCenter); + wonLabel->hide(); + inputLayout->addStretch(1); + + // The pot view + potLabel = new QLabel(this); + potLabel->setFont(myFixedFont); + potLabel->setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + inputLayout->addWidget(potLabel, 0, AlignCenter); + + // Label widget in the lower left. + clickToHold = new QLabel(this); + clickToHold->hide(); + + // Timers + blinkTimer = new QTimer(this); + connect( blinkTimer, SIGNAL(timeout()), SLOT(bTimerEvent()) ); + + waveTimer = new QTimer(this); + connect( waveTimer, SIGNAL(timeout()), SLOT(waveTimerEvent()) ); + + drawTimer = new QTimer(this); + connect (drawTimer, SIGNAL(timeout()), SLOT(drawCardsEvent()) ); + + // and now the betUp/Down Buttons + betBox = new BetBox(this, 0); + betLayout->addWidget(betBox); + connect(betBox, SIGNAL(betChanged(int)), this, SLOT(betChange(int))); + connect(betBox, SIGNAL(betAdjusted()), this, SLOT(adjustBet())); + connect(betBox, SIGNAL(fold()), this, SLOT(out())); + + // some tips + QToolTip::add(drawButton, i18n("Continue the round")); + QToolTip::add(potLabel, i18n("The current pot")); + + // Load all cards into pixmaps first -> in the constructor. + cardImages = new CardImages(this, 0); + + // The configuration + KConfig* conf = kapp->config(); + conf->setGroup("General"); + + // Load the card deck. + if (conf->readBoolEntry("RandomDeck", true)) { + cardImages->loadDeck(KCardDialog::getRandomDeck()); + } else { + cardImages->loadDeck(conf->readPathEntry("DeckPath", KCardDialog::getDefaultDeck())); + } + if (conf->readBoolEntry("RandomCardDir", true)) { + cardImages->loadCards(KCardDialog::getRandomCardDir()); + } else { + cardImages->loadCards(conf->readPathEntry("CardPath", + KCardDialog::getDefaultCardDir())); + } +} + + +// Start a new complete game (not a round). + +void kpok::newGame() +{ + NewGameDlg* newGameDlg; + + // Get a "New Game" dialog. + lastHandText = ""; + newGameDlg = new NewGameDlg(this); + newGameDlg->hideReadingFromConfig(); + + if (newGameDlg->exec()) { + stopBlinking(); + stopDrawing(); + stopWave(); + + // Delete the old values. + for (unsigned int i = 0; i < m_numPlayers; i++) + delete playerBox[i]; + delete[] playerBox;// necessary? + + int numPlayers = newGameDlg->getPlayers(); + + // Most things will be done in initPoker. + initPoker(numPlayers); + + // Set/show the name and money of all players. + for (unsigned int i = 0; i < m_numPlayers; i++) { + m_players[i].setName(newGameDlg->name(i)); + playerBox[i]->showName(); + m_players[i].setCash((newGameDlg->money() >= m_game.getMinBet()) + ? newGameDlg->money() : START_MONEY); + m_game.setDirty(); + } + + // Show the money for all players and the pot. + paintCash(); + + // Activate the Draw button. + drawButton->setText(i18n("&Deal")); + drawButton->setEnabled(true); + } + + delete newGameDlg; +} + + +void kpok::newRound() +{ + bool onePlayerGame = false; + + m_game.newRound(); + + playerBox[0]->setHeldEnabled(false); + + if (m_numPlayers == 1) + onePlayerGame = true; + + readOptions(); // maybe some options have changed so check em + + if (m_players[0].getCash() < m_game.getMinBet()) + noMoney(); + else { + for (unsigned int i = 0; i < m_numPlayers; i++) { + if (m_players[i].getCash() >= m_game.getMinBet()) + m_game.activatePlayer(&m_players[i]); + else + removePlayerFromRound(&m_players[i]); + } + } + + if (m_game.getNumActivePlayers() == 1 && m_game.getType() != SinglePlayer) + switchToOnePlayerRules(); + + m_blinkingBox = 0; + wonLabel->hide(); + stopBlinking(); + stopWave(); + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + m_game.getActivePlayer(i)->newRound(); + + // We are beginning a new round so every card is available. + drawAllDecks(); + playerBox[0]->showHelds(false); + + // Deal first cards of the round + bool skip[PokerHandSize]; + for (int i = 0; i < PokerHandSize; i++) + skip[i] = false; + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + drawCards(m_game.getActivePlayer(i), skip); + + if (m_game.getNumActivePlayers() > 1) { + findHumanPlayer()->changeBet(m_game.getMinBet()); + m_game.bet(m_game.getMinBet()); + betBox->show(); + } + else { + m_game.getActivePlayer(0)->changeBet(cashPerRound); + betBox->hide(); + } + + paintCash(); + + drawTimer->start(drawDelay, TRUE); +} + + +void kpok::bet() +{ + // The players will bet, now. Player 1 (the human one ;-)) has + // already bet using the betBox ALL players (except player 1 who has + // already had) will get a chance to raise the value if nobody + // raises further that player will not get another chance, else he + // will. + + bool raised = false; + int oldMustBet = 0; + if (m_game.getState() == StateBet2) + oldMustBet = currentMustBet; + + if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) { + // The betting has just begun. This can be either the first or + // the second betting phase (state == StateBet1 or StateBet2). + // FIXME: These state names must be changed! + + // Find out how much the other players must bet. + currentMustBet = findHumanPlayer()->getCurrentBet(); + + // First bet as usual. + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + PokerPlayer *player = m_game.getActivePlayer(i); + int playerBet = 0; + + if (player->getHuman()) + continue; + + if (m_game.getState() == StateBet1) // first bet phase + playerBet = player->bet(currentMustBet); + else if (m_game.getState() == StateBet2) // 2nd bet phase + playerBet = player->raise(currentMustBet); + + if (playerBet < currentMustBet) { + removePlayerFromRound(player); + // FIXME: This should be bet from the beginning! + if (m_game.getState() == StateBet1) + m_game.bet(m_game.getMinBet()); + i--; + } + else { + // The player is seeing or has raised. + + if (playerBet > currentMustBet) + raised = true; + + currentMustBet = playerBet; + m_game.bet(currentMustBet - oldMustBet); + } + } + } + else if (m_game.getState() == StateRaise1 + || m_game.getState() == StateRaise2) { + + // The bet has been raised. + paintCash(); + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + PokerPlayer *player = m_game.getActivePlayer(i); + + // human player + if (player->getCurrentBet() < currentMustBet && player->getHuman()) { + removePlayerFromRound(player); + i--; + } + } + } + + paintCash(); + + + oldBet_raise = findHumanPlayer()->getCurrentBet(); // used by out() only + if (m_game.getState() == StateBet1 || m_game.getState() == StateBet2) { + if (raised) { + if (m_game.getState() == StateBet1) + m_game.setState(StateRaise1); + else + m_game.setState(StateRaise2); + + if (adjust) + adjustBet(); + else + out(); // not necessary + betBox->setEnabled(true); + betBox->beginRaise(); + if (adjust) + emit statusBarMessage(i18n("Clicking on draw means you adjust your bet")); + else + emit statusBarMessage(i18n("Clicking on draw means you are out")); + } + else { + if (m_game.getState() == StateBet1) + m_game.setState(StateExchangeCards); + else + m_game.setState(StateSee); + } + } + else if (m_game.getState() == StateRaise1 && !raised) { + emit clearStatusBar(); + // weWillAdjustLabel->hide(); + betBox->stopRaise(); + m_game.setState(StateExchangeCards); + } + else if (m_game.getState() == StateRaise2 && !raised) { + emit clearStatusBar(); + // weWillAdjustLabel->hide(); + betBox->stopRaise(); + m_game.setState(StateSee); + } + + + // Check if player 1 is out -> for players > 2 + // TODO: maybe if (!m_activePlayers.contains(humanPlayer)) + // {exchangeCards(); bet(); displayerWinner_computer();return;} + + + // don't continue game if player 1 is alone + // TODO: port to players > 2 + // this is ONLY working for players <= 2 + if (m_game.getNumInactivePlayers() >= 1 + && m_game.getNumActivePlayers() == 1) + displayWinner_Computer(m_game.getActivePlayer(0), true); +} + + +void kpok::out() +{ + // weWillAdjustLabel->hide(); + emit clearStatusBar(); + + m_game.bet(oldBet_raise - findHumanPlayer()->getCurrentBet()); + findHumanPlayer()->changeBet(oldBet_raise + - findHumanPlayer()->getCurrentBet()); + paintCash(); + + // Go to next state immediately. (Previously we told the user to + // click the action button.) + drawClick(); + //drawButton->setText(i18n("&End Round")); +} + + +void kpok::adjustBet() +{ + emit clearStatusBar(); + + betChange(currentMustBet - findHumanPlayer()->getCurrentBet()); + paintCash(); +} + + +// Initiate for a completely new game (not a round). +// +// This is called from newGame(), readEntriesAndInitPoker(), loadGame(). +// + +void kpok::initPoker(unsigned int numPlayers) +{ + m_numPlayers = numPlayers; + + // Tell the game about the players also. + PokerGameType gametype = (numPlayers == 1) ? SinglePlayer : MultiPlayer; + + // Read some defaults. + kapp->config()->setGroup("General"); + int minBet = kapp->config()->readNumEntry("MinBet", MIN_BET); + int maxBet = kapp->config()->readNumEntry("MaxBet", MAX_BET); + + // Start a new poker game using the data found out above. + m_game.newGame(gametype, m_numPlayers, m_players, minBet, maxBet); + + // Not (yet) configurable + cashPerRound = CASH_PER_ROUND; + drawDelay = kapp->config()->readNumEntry("DrawDelay", DRAWDELAY); + + m_blinkingBox = 0; + currentMustBet = m_game.getMinBet(); + + // --- Graphics --- + + // Make all labels / boxes / cardwidgets for every player. + playerBox = new PlayerBox *[numPlayers]; + + for (int unsigned i = 0; i < numPlayers; i++) { + playerBox[i] = new PlayerBox(i == 0, this); + playerBox[i]->setPlayer(&m_players[i]); + if (i == 0) + topLayout->insertWidget(0, playerBox[i]); + else + topLayout->addWidget(playerBox[i]); + playerBox[i]->showName(); + + // If it has been deleted and created again it hasn't be shown + // correctly - hiding and re-showing solves the problem. + playerBox[i]->hide(); + playerBox[i]->show(); + } + + // Connects for player 1 + // + // FIXME: Make CardWidget::toggleHeld() work. + playerBox[0]->activateToggleHeld(); + connect(playerBox[0], SIGNAL(toggleHeld()), this, SLOT(toggleHeld())); + + // hide some things + playerBox[0]->showHelds(false); + wonLabel->hide(); + emit showClickToHold(false); + + // Different meaning of the status for single and multi player games. + if (m_game.getNumActivePlayers() > 1) { + setHand(i18n("Nobody"), false); + betBox->show(); + betBox->setEnabled(false); + potLabel->show(); + } + else { + setHand(i18n("Nothing")); + betBox->hide(); + potLabel->hide(); + playerBox[0]->singlePlayerGame(cashPerRound); + } + + // Some final inits. + drawStat = 0; + waveActive = 0; + fCount = 0; + + // Finally clear the pot and show the decks/cash - in one word: begin :-) + m_game.clearPot(); + drawAllDecks(); + for (unsigned int i = 0; i < m_numPlayers; i++) { + playerBox[i]->repaintCard(); + } + paintCash(); + playerBox[0]->setHeldEnabled(false); +} + + +void kpok::paintCash() +{ + for (unsigned int i = 0; i < m_numPlayers; i++) { + playerBox[i]->showCash(); + } + potLabel->setText(i18n("Pot: %1").arg(KGlobal::locale()->formatMoney(m_game.getPot()))); +} + + +void kpok::updateLHLabel() +{ + if (!lastHandText.isEmpty()) + setHand(lastHandText); + else if (m_game.getNumActivePlayers() > 1) + setHand(i18n("Nobody"), false); + else + setHand(i18n("Nothing")); +} + + +void kpok::setHand(const QString& newHand, bool lastHand) +{ + emit changeLastHand(newHand, lastHand); + + lastHandText = newHand; +} + + +void kpok::toggleHeld() +{ + if (m_game.getState() == StateBet1 || m_game.getState() == StateRaise1) + playSound("hold.wav"); +} + + +void kpok::drawClick() +{ + if (!drawButton->isEnabled()) + return; + + // If this is the start of a new round, then deal new cards. + if (m_game.getState() == StateStartRound) { + drawButton->setEnabled(false); + betBox->setEnabled(false); + newRound(); + } + else if (m_game.getState() == StateBet1) { // bet + emit showClickToHold(false); + + bet(); + if (m_game.getState() == StateExchangeCards) {// should be set in bet() + drawClick(); + } + } + else if (m_game.getState() == StateRaise1) { // continue bet + bet(); + if (m_game.getState() == StateExchangeCards) {// should be set in bet() + drawClick(); + } + } + else if (m_game.getState() == StateExchangeCards) { // exchange cards + drawButton->setEnabled(false); + playerBox[0]->setHeldEnabled(false); + betBox->setEnabled(false); + bool skip[PokerHandSize]; + for (int i = 0; i < PokerHandSize; i++) + skip[i] = false; + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (!m_game.getActivePlayer(i)->getHuman()) + m_game.getActivePlayer(i)->exchangeCards(skip); + else { + for (int i = 0; i < PokerHandSize; i++) { + skip[i] = playerBox[0]->getHeld(i); + if (!skip[i]) + playerBox[0]->paintDeck(i); + } + } + drawCards(m_game.getActivePlayer(i), skip); + } + + if (playerBox[0]->getHeld(0)) + drawTimer->start(0, TRUE); + else + drawTimer->start(drawDelay, TRUE); + } + else if (m_game.getState() == StateBet2) { // raise + setBetButtonEnabled(false); + bet(); + + if (m_game.getState() == StateSee)//should be set in bet()->if no one has raised + drawClick(); + } + else if (m_game.getState() == StateRaise2) { + bet(); + if (m_game.getState() == StateSee) + drawClick(); + } + else if (m_game.getState() == StateSee) + winner(); +} + + +void kpok::drawCards(PokerPlayer* p, bool skip[]) +{ + m_game.dealCards(p, skip); +} + + +void kpok::displayWinner_Computer(PokerPlayer* winner, bool othersPassed) +{ + // Determine the box that contains the winner. + for (unsigned int i = 0; i < m_numPlayers; i++) { + if (&m_players[i] == winner) + m_blinkingBox = i; + } + + // Give the pot to the winner. + winner->setCash(winner->getCash() + m_game.getPot()); + m_game.setDirty(); + + // Generate a string with winner info and show it. + QString label; + if (winner->getHuman()) + label = i18n("You won %1").arg(KGlobal::locale()->formatMoney(m_game.getPot())); + else + label = i18n("%1 won %2").arg(winner->getName()).arg(KGlobal::locale()->formatMoney(m_game.getPot())); + wonLabel->setText(label); + + // Start the waving motion of the text. + QFont waveFont; + waveFont.setBold(true); + waveFont.setPointSize(16); + QFontMetrics tmp(waveFont); + mWonWidget->setMinimumWidth(tmp.width(label) + 20); + + // Play a suitable sound. + if (winner->getHuman()) { + playSound("win.wav"); + wonLabel->hide(); + startWave(); + } + else { + playSound("lose.wav"); + wonLabel->show(); + } + + m_game.clearPot(); + m_game.setState(StateStartRound); + drawButton->setEnabled(true); + setHand(winner->getName(), false); + paintCash(); + + // Only start blinking if player 1 is still in. + if (m_game.isActivePlayer(&m_players[0]) && !othersPassed) + startBlinking(); + + drawButton->setText(i18n("&Deal New Round")); +} + + +void kpok::showComputerCards() +{ + // Don't show cards of 'out' players. + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (!m_game.getActivePlayer(i)->getHuman()){ + playerBox[i]->paintCard(drawStat); //TODO change: + + if (i == 1) //TODO : CHANGE! + playSound("cardflip.wav");//perhaps in playerbox or even in cardwidget + } + } + + if (drawStat == 4) { // just did last card + drawButton->setEnabled(true); + drawStat = 0; + } else { // only inc drawStat if not done with displaying + drawStat++; + showComputerCards(); + } +} + + +void kpok::setBetButtonEnabled(bool enabled) +{ + betBox->setEnabled(enabled); +} + + +void kpok::drawCardsEvent() +{ + if (!playerBox[0]->getHeld(drawStat)) { + playerBox[0]->paintCard(drawStat); + + playSound("cardflip.wav");//maybe in playerbox or even in cardwidget + } + + if (drawStat == 4) { // just did last card + drawButton->setEnabled(true); + betBox->setEnabled(true); + drawStat = 0; + + if (m_game.getState() == StateExchangeCards) { + if (m_game.getType() == SinglePlayer) + result(); + else { + // Now give players the chance to raise. + drawButton->setText(i18n("&See!")); + m_game.setState(StateBet2); + } + } else if (m_game.getState() == StateStartRound) { + playerBox[0]->setHeldEnabled(true); + + emit showClickToHold(true); + //clickToHold->show(); + //TODO: + m_game.setState(StateBet1); + drawButton->setText(i18n("&Draw New Cards")); + } + + } else { // only inc drawStat if not done with displaying + drawStat++; + // look at next card and if it is held instantly call drawCardEvent again + if (playerBox[0]->getHeld(drawStat)) + drawTimer->start(0,TRUE); + else + drawTimer->start(drawDelay,TRUE); + } +} + + +// Called to display the result in a single player game. + +void kpok::result() +{ + int testResult = m_game.getActivePlayer(0)->testHand(); + switch (testResult) { + case Pair: + if (m_game.getActivePlayer(0)->getHand().get_firstRank() < JACK) { + displayWin(i18n("Nothing"), 0); + break; + } + + displayWin(i18n("One Pair"), 5); + break; + + case TwoPairs: + displayWin(i18n("Two Pairs"), 10); + break; + + case ThreeOfAKind: + displayWin(i18n("3 of a Kind"), 15); + break; + + case Straight: + displayWin(i18n("Straight"), 20); + break; + + case Flush: + displayWin(i18n("Flush"), 25); + break; + + case FullHouse: + displayWin(i18n("Full House"), 40); + break; + + case FourOfAKind: + displayWin(i18n("4 of a Kind"), 125); + break; + + case StraightFlush: + displayWin(i18n("Straight Flush"), 250); + break; + + case RoyalFlush: + displayWin(i18n("Royal Flush"), 2000); + break; + + default: + displayWin(i18n("Nothing"), 0); + break; + } + + startBlinking(); + m_game.setState(StateStartRound); + + if (m_game.getActivePlayer(0)->getCash() < cashPerRound) + noMoney(); +} + + +// Display winner and give money and so on. + +void kpok::winner() +{ + PokerPlayer *winner; + + showComputerCards(); + + winner = m_game.getActivePlayer(0); + for (int i = 1; i < m_game.getNumActivePlayers(); i++) { + if (winner->getHand() < m_game.getActivePlayer(i)->getHand()) { + //kdDebug() << "Hand 2 is better." << endl; + winner = m_game.getActivePlayer(i); + } + else { + //kdDebug() << "Hand 1 is better." << endl; + } + } + + displayWinner_Computer(winner, false); +} + + +void kpok::noMoney() +{ + KMessageBox::sorry(0, i18n("You Lost"), i18n("Oops, you went bankrupt.\n" + "Starting a new game.\n")); + newGame(); +} + + +void kpok::startBlinking() +{ + blinkTimer->start(650); +} + + +void kpok::stopBlinking() +{ + blinkTimer->stop(); + m_blinkStat = 1; + m_blinkingBox = 0; +} + + +void kpok::startWave() +{ + waveTimer->start(40); + waveActive = true; +} + + +void kpok::stopWave() +{ + waveTimer->stop(); + fCount = -1; /* clear image */ + repaint ( FALSE ); + waveActive = false; +} + + +void kpok::stopDrawing() +{ + drawTimer->stop(); +} + + +void kpok::waveTimerEvent() +{ + fCount = (fCount + 1) & 15; + repaint( FALSE ); +} + + +void kpok::bTimerEvent() +{ + if (m_blinking) { + if (m_blinkStat != 0) { + playerBox[m_blinkingBox]->blinkOn(); + m_blinkStat = 0; + } else { + playerBox[m_blinkingBox]->blinkOff(); + m_blinkStat = 1; + } + } +} + + +void kpok::displayWin(const QString& hand, int cashWon) +{ + QString buf; + + setHand(hand); + m_game.getActivePlayer(0)->setCash(m_game.getActivePlayer(0)->getCash() + + cashWon); + m_game.setDirty(); + paintCash(); + + if (cashWon) { + playSound("win.wav"); + buf = i18n("You won %1!").arg(KGlobal::locale()->formatMoney(cashWon)); + } else { + playSound("lose.wav"); + buf = i18n("Game Over"); // locale + } + wonLabel->setText(buf); + + if (!cashWon) + wonLabel->show(); + else { + wonLabel->hide(); + startWave(); + } + + drawButton->setText(i18n("&Deal New Round")); +} + + +void kpok::paintEvent( QPaintEvent *) +{ + /* NOTE: This was shamelessy stolen from the "hello world" example + * coming with Qt Thanks to the Qt-Guys for doing such a cool + * example 8-) + */ + + if (!waveActive) { + return; + } + + QString txt = wonLabel->text(); + wonLabel->hide(); + + static int sin_tbl[16] = { + 0, 38, 71, 92, 100, 92, 71, 38, 0, -38, -71, -92, -100, -92, -71, -38}; + + if ( txt.isEmpty() ) { + return; + } + + QFont wonFont; + wonFont.setPointSize(18); + wonFont.setBold(true); + + QFontMetrics fm = QFontMetrics(wonFont); + + int w = fm.width(txt) + 20; + int h = fm.height() * 2; + while (w > mWonWidget->width() && wonFont.pointSize() > 6) {// > 6 for emergency abort... + wonFont.setPointSize(wonFont.pointSize() - 1); + fm = QFontMetrics(wonFont); + w = fm.width(txt) + 20; + h = fm.height() * 2; + } + + int pmx = mWonWidget->width() / 2 - w / 2; + int pmy = 0; + // int pmy = (playerBox[0]->x() + playerBox[0]->height() + 10) - h / 4; + + QPixmap pm( w, h ); + pm.fill( mWonWidget, pmx, pmy ); + + if (fCount == -1) { /* clear area */ + bitBlt( mWonWidget, pmx, pmy, &pm ); + return; + } + + QPainter p; + int x = 10; + int y = h/2 + fm.descent(); + unsigned int i = 0; + p.begin( &pm ); + p.setFont( wonFont ); + p.setPen( QColor(0,0,0) ); + + while ( i < txt.length() ) { + int i16 = (fCount+i) & 15; + + p.drawText( x, y-sin_tbl[i16]*h/800, QString(txt[i]), 1 ); + x += fm.width( txt[i] ); + i++; + } + p.end(); + + // 4: Copy the pixmap to the Hello widget + bitBlt( mWonWidget, pmx, pmy, &pm ); +} + + +void kpok::drawAllDecks() +{ + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + for (int i2 = 0; i2 < PokerHandSize; i2++) { + m_game.getActivePlayer(i)->takeCard(i2, 0); + playerBox[i]->paintCard(i2); + } + } +} + + +void kpok::removePlayerFromRound(PokerPlayer* removePlayer) +{ + removePlayer->setOut(true); + m_game.inactivatePlayer(removePlayer); + + for (int i = 0; i < m_game.getNumPlayers(); i++) + playerBox[i]->showCash(); +} + + +void kpok::switchToOnePlayerRules() +{ + KMessageBox::information(0, i18n("You are the only player with money!\n" + "Switching to one player rules..."), + i18n("You Won")); + + // Hide all computer players. + for (unsigned int i = 1; i < m_numPlayers; i++) { + playerBox[i]->hide(); + } + + m_game.setType(SinglePlayer); + + betBox->hide(); + potLabel->hide(); + playerBox[0]->singlePlayerGame(cashPerRound); + setHand(i18n("Nothing")); +} + + +PokerPlayer* kpok::findHumanPlayer() +{ + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + if (m_game.getActivePlayer(i)->getHuman()) + return m_game.getActivePlayer(i); + } + + return m_game.getActivePlayer(0);//error +} + + +bool kpok::readEntriesAndInitPoker() +{ + NewGameDlg *newGameDlg = NULL; + KConfig *conf = kapp->config(); + int numPlayers = DEFAULT_PLAYERS; + + conf->setGroup("NewGameDlg"); + bool showNewGameDlg = conf->readBoolEntry("showNewGameDlgOnStartup", false); + bool aborted = false; + bool oldGame = false; + + if (showNewGameDlg) { + newGameDlg = new NewGameDlg(this); + if (!newGameDlg->exec()) + return false; // exit game + } + + if (!aborted && (!showNewGameDlg || showNewGameDlg && + newGameDlg->readFromConfigFile())) { + // Try starting an old game. + //kdDebug() << "Trying to load old game" << endl; + oldGame = loadGame(); + if (oldGame) + return true; + } + + if (!aborted && showNewGameDlg && !oldGame) { + // Don't use config file - just take the values from the dialog. + // (This is also config - the dialog defaults are from config.) + numPlayers = newGameDlg->getPlayers(); + if (numPlayers <= 0) + aborted = true; + + for (int i = 0; i < numPlayers; i++) { + m_players[i].setName(newGameDlg->name(i)); + m_players[i].setCash(newGameDlg->money()); + m_game.setDirty(); + } + } + initPoker(numPlayers); + + if (newGameDlg != 0) { + delete newGameDlg; + } + + return true; +} + + +void kpok::betChange(int betChange) +{ + PokerPlayer* p = findHumanPlayer(); + + switch(m_game.getState()){ + case StateBet1: + if (p->getCurrentBet() + betChange >= m_game.getMinBet() && + p->getCurrentBet() + betChange <= m_game.getMaxBet()) { + // Bet at least getMinBet() but not more than getMaxBet(). + if (p->changeBet(betChange)) + m_game.bet(betChange); + } + break; + case StateBet2: + case StateRaise2: + case StateRaise1: + if (p->getCurrentBet() + betChange >= currentMustBet + && p->getCurrentBet() + betChange <= currentMustBet + m_game.getMaxBet()) { + if (p->changeBet(betChange)) + m_game.bet(betChange); + } + break; + default: + break; + } + + paintCash(); +} + + +void kpok::saveGame() +{ + kapp->config()->setGroup("Save"); + saveGame(kapp->config()); +} + + +void kpok::saveGame(KConfig* conf) +{ + // kdWarning() << "save game" << endl; + int players = m_game.getNumPlayers(); + + conf->writeEntry("players", players); + conf->writeEntry("lastHandText", lastHandText); + + for (int i = 0; i < players; i++) { + conf->writeEntry(QString("Name_%1").arg(i), m_players[i].getName()); + conf->writeEntry(QString("Human_%1").arg(i), m_players[i].getHuman()); + conf->writeEntry(QString("Cash_%1").arg(i), m_players[i].getCash()); + } + + m_game.clearDirty(); +} + + +void kpok::toggleSound() { setSound(!sound); } +void kpok::toggleBlinking() { setBlinking(!m_blinking); } +void kpok::toggleAdjust() { setAdjust(!adjust); } + + +void kpok::slotPreferences() +{ + if ( mOptions==0 ) + mOptions = new OptionsDlg(this, 0, m_numPlayers); + + if (m_numPlayers > 1) + mOptions->init(drawDelay, m_game.getMaxBet(), m_game.getMinBet()); + else + mOptions->init(drawDelay, cashPerRound); + + if (!mOptions->exec()) { + delete mOptions; + mOptions = 0; + } +} + + +void kpok::setAdjust(bool ad) +{ + adjust = ad; + + //update guessed money statusbar if we currently are in need of adjust + if (m_game.getState() == StateRaise1 + || m_game.getState() == StateRaise2) { + if (adjust) + adjustBet(); + else + out(); + } +} + + +bool kpok::initSound() +{ + sound = true; + return true; +} + + +void kpok::playSound(const QString &soundname) +{ + if (sound) + KAudioPlayer::play(locate("data", QString("kpoker/sounds/")+soundname)); +} + + +void kpok::setSound(bool s) +{ + sound = s; +} + + +void kpok::readOptions() +{ + if (mOptions != 0) { + if (m_numPlayers > 1) { + m_game.setBettingLimits(mOptions->getMinBet(), mOptions->getMaxBet()); + for (int i = 0; i < m_game.getNumActivePlayers(); i++) + m_game.getActivePlayer(i)->setBetDefaults(m_game.getMinBet(), + m_game.getMaxBet()); // inform players how much they may bet + } + drawDelay = mOptions->getDrawDelay(); + // kdDebug() << cashPerRound << endl; + // cashPerRound = mOptions->getCashPerRound(); // NOT(!) configurable + delete mOptions; + mOptions = 0; + } +} + + +bool kpok::loadGame() +{ + kapp->config()->setGroup("Save"); + return loadGame(kapp->config()); +} + + +bool kpok::loadGame(KConfig* conf) +{ + int numPlayers = DEFAULT_PLAYERS; + + // conf->setGroup("Save"); + numPlayers = conf->readNumEntry("players", -1); + + if (numPlayers > 0) { + for (int i = 0; i < numPlayers; i++) { + QString buf = conf->readEntry(QString("Name_%1").arg(i), + "Player"); + m_players[i].setName(buf); + bool human = conf->readBoolEntry(QString("Human_%1").arg(i), + false); + if (human) + m_players[i].setHuman(); // i == 0 + int cash = conf->readNumEntry(QString("Cash_%1").arg(i), + START_MONEY); + m_players[i].setCash(cash); + m_game.setDirty(); + } + initPoker(numPlayers); + + // after initPoker because initPoker does a default initialization + // of lastHandText + conf->setGroup("Save"); + lastHandText = conf->readEntry("lastHandText", ""); + + return true; + } + + return false; +} + + +// These slots are called from keyboard shortcuts ('1'..'5') + +void kpok::exchangeCard1() { playerBox[0]->cardClicked(1); } +void kpok::exchangeCard2() { playerBox[0]->cardClicked(2); } +void kpok::exchangeCard3() { playerBox[0]->cardClicked(3); } +void kpok::exchangeCard4() { playerBox[0]->cardClicked(4); } +void kpok::exchangeCard5() { playerBox[0]->cardClicked(5); } + + +// This slot is called when the user wants to change some aspect of +// the card deck (front or back). +// +// FIXME: Maybe the slot should be moved to top.cpp instead and simply +// call a setDeck() or setBack() method in kpok. + +void kpok::slotCardDeck() +{ + kapp->config()->setGroup("General"); + QString deckPath = kapp->config()->readPathEntry("DeckPath", 0); + QString cardPath = kapp->config()->readPathEntry("CardPath", 0); + bool randomDeck, randomCardDir; + + // Show the "Select Card Deck" dialog and load the images for the + // selected deck, if any. + if (KCardDialog::getCardDeck(deckPath, cardPath, this, KCardDialog::Both, + &randomDeck, &randomCardDir) + == QDialog::Accepted) { + + // Load backside and front images. + if (playerBox && m_blinking && (m_blinkStat == 0)) + bTimerEvent(); + + cardImages->loadDeck(deckPath); + cardImages->loadCards(cardPath); + + for (int i = 0; i < m_game.getNumActivePlayers(); i++) { + playerBox[i]->repaintCard(); + } + + // Save selected stuff in the configuration. + kapp->config()->writePathEntry("DeckPath", deckPath); + kapp->config()->writeEntry("RandomDeck", randomDeck); + kapp->config()->writePathEntry("CardPath", cardPath); + kapp->config()->writeEntry("RandomCardDir", randomCardDir); + } +} + + +#include "kpoker.moc" |