File indexing completed on 2024-05-12 08:00:34
0001 /* 0002 * Copyright (C) 1995 Paul Olav Tvete <paul@troll.no> 0003 * Copyright (C) 2000-2009 Stephan Kulow <coolo@kde.org> 0004 * Copyright (C) 2010 Parker Coates <coates@kde.org> 0005 * 0006 * License of original code: 0007 * ------------------------------------------------------------------------- 0008 * Permission to use, copy, modify, and distribute this software and its 0009 * documentation for any purpose and without fee is hereby granted, 0010 * provided that the above copyright notice appear in all copies and that 0011 * both that copyright notice and this permission notice appear in 0012 * supporting documentation. 0013 * 0014 * This file is provided AS IS with no warranties of any kind. The author 0015 * shall have no liability with respect to the infringement of copyrights, 0016 * trade secrets or any patents by this file or any part thereof. In no 0017 * event will the author be liable for any lost revenue or profits or 0018 * other special, indirect and consequential damages. 0019 * ------------------------------------------------------------------------- 0020 * 0021 * License of modifications/additions made after 2009-01-01: 0022 * ------------------------------------------------------------------------- 0023 * This program is free software; you can redistribute it and/or 0024 * modify it under the terms of the GNU General Public License as 0025 * published by the Free Software Foundation; either version 2 of 0026 * the License, or (at your option) any later version. 0027 * 0028 * This program is distributed in the hope that it will be useful, 0029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0031 * GNU General Public License for more details. 0032 * 0033 * You should have received a copy of the GNU General Public License 0034 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0035 * ------------------------------------------------------------------------- 0036 */ 0037 0038 #include "klondike.h" 0039 0040 // own 0041 #include "dealerinfo.h" 0042 #include "patsolve/klondikesolver.h" 0043 #include "pileutils.h" 0044 #include "settings.h" 0045 #include "speeds.h" 0046 // KF 0047 #include <KLocalizedString> 0048 #include <KSelectAction> 0049 0050 KlondikePile::KlondikePile(DealerScene *scene, int index, const QString &objectName) 0051 : PatPile(scene, index, objectName) 0052 , m_cardsToShow(1) 0053 { 0054 } 0055 0056 void KlondikePile::setCardsToShow(int draw) 0057 { 0058 m_cardsToShow = draw; 0059 } 0060 0061 int KlondikePile::cardsToShow() const 0062 { 0063 return m_cardsToShow; 0064 } 0065 0066 QList<QPointF> KlondikePile::cardPositions() const 0067 { 0068 QList<QPointF> positions; 0069 QPointF currentPos(0, 0); 0070 for (int i = 0; i < count(); ++i) { 0071 positions << currentPos; 0072 if (i >= count() - m_cardsToShow) 0073 currentPos += spread(); 0074 } 0075 return positions; 0076 } 0077 0078 Klondike::Klondike(const DealerInfo *di) 0079 : DealerScene(di) 0080 { 0081 } 0082 0083 void Klondike::initialize() 0084 { 0085 // The units of the follwoing constants are pixels 0086 const qreal hspacing = 1.0 / 6 + 0.02; // horizontal spacing between card piles 0087 const qreal vspacing = 1.0 / 4; // vertical spacing between card piles 0088 0089 setDeckContents(); 0090 0091 easyRules = Settings::klondikeIsDrawOne(); 0092 0093 talon = new PatPile(this, 0, QStringLiteral("talon")); 0094 talon->setPileRole(PatPile::Stock); 0095 talon->setLayoutPos(0, 0); 0096 // Give the talon a low Z value to keep it out of the way during there 0097 // deal animation. 0098 talon->setZValue(-52); 0099 talon->setKeyboardSelectHint(KCardPile::NeverFocus); 0100 talon->setKeyboardDropHint(KCardPile::NeverFocus); 0101 connect(talon, &KCardPile::clicked, this, &DealerScene::drawDealRowOrRedeal); 0102 0103 pile = new KlondikePile(this, 13, QStringLiteral("pile")); 0104 pile->setCardsToShow(easyRules ? 1 : 3); 0105 pile->setPileRole(PatPile::Waste); 0106 pile->setRightPadding(1.1); 0107 pile->setLayoutPos(1 + hspacing, 0); 0108 pile->setSpread(0.33, 0); 0109 pile->setKeyboardSelectHint(KCardPile::ForceFocusTop); 0110 pile->setKeyboardDropHint(KCardPile::NeverFocus); 0111 0112 for (int i = 0; i < 7; ++i) { 0113 play[i] = new PatPile(this, i + 5, QStringLiteral("play%1").arg(i)); 0114 play[i]->setPileRole(PatPile::Tableau); 0115 play[i]->setLayoutPos((1.0 + hspacing) * i, 1.0 + vspacing); 0116 play[i]->setAutoTurnTop(true); 0117 play[i]->setBottomPadding(play[i]->spread().y() * 5); 0118 play[i]->setHeightPolicy(KCardPile::GrowDown); 0119 play[i]->setKeyboardSelectHint(KCardPile::AutoFocusDeepestFaceUp); 0120 play[i]->setKeyboardDropHint(KCardPile::AutoFocusTop); 0121 } 0122 0123 for (int i = 0; i < 4; ++i) { 0124 target[i] = new PatPile(this, i + 1, QStringLiteral("target%1").arg(i)); 0125 target[i]->setPileRole(PatPile::Foundation); 0126 target[i]->setLayoutPos((3 + i) * (1.0 + hspacing), 0); 0127 target[i]->setKeyboardSelectHint(KCardPile::ForceFocusTop); 0128 target[i]->setKeyboardDropHint(KCardPile::ForceFocusTop); 0129 } 0130 0131 setActions(DealerScene::Hint | DealerScene::Demo | DealerScene::Draw); 0132 setSolver(new KlondikeSolver(this, pile->cardsToShow())); 0133 0134 options = new KSelectAction(i18n("Klondike &Options"), this); 0135 options->addAction(i18n("Draw 1")); 0136 options->addAction(i18n("Draw 3")); 0137 options->setCurrentItem(easyRules ? 0 : 1); 0138 connect(options, &KSelectAction::indexTriggered, this, &Klondike::gameTypeChanged); 0139 } 0140 0141 bool Klondike::checkAdd(const PatPile *pile, const QList<KCard *> &oldCards, const QList<KCard *> &newCards) const 0142 { 0143 switch (pile->pileRole()) { 0144 case PatPile::Tableau: 0145 return checkAddAlternateColorDescendingFromKing(oldCards, newCards); 0146 case PatPile::Foundation: 0147 return checkAddSameSuitAscendingFromAce(oldCards, newCards); 0148 case PatPile::Waste: 0149 case PatPile::Stock: 0150 default: 0151 return false; 0152 } 0153 } 0154 0155 bool Klondike::checkRemove(const PatPile *pile, const QList<KCard *> &cards) const 0156 { 0157 switch (pile->pileRole()) { 0158 case PatPile::Tableau: 0159 return isAlternateColorDescending(cards); 0160 case PatPile::Foundation: 0161 return easyRules && cards.first() == pile->topCard(); 0162 case PatPile::Waste: 0163 return cards.first() == pile->topCard(); 0164 case PatPile::Stock: 0165 default: 0166 return false; 0167 } 0168 } 0169 0170 void Klondike::cardsMoved(const QList<KCard *> &cards, KCardPile *oldPile, KCardPile *newPile) 0171 { 0172 DealerScene::cardsMoved(cards, oldPile, newPile); 0173 0174 Q_EMIT newCardsPossible(!talon->isEmpty() || pile->count() > 1); 0175 } 0176 0177 QList<QAction *> Klondike::configActions() const 0178 { 0179 return QList<QAction *>() << options; 0180 } 0181 0182 bool Klondike::newCards() 0183 { 0184 if (talon->isEmpty() && pile->count() <= 1) 0185 return false; 0186 0187 if (talon->isEmpty()) { 0188 // Move the cards from the pile back to the deck 0189 flipCardsToPile(pile->cards(), talon, DURATION_MOVE); 0190 0191 } else { 0192 QList<KCard *> cards = talon->topCards(pile->cardsToShow()); 0193 flipCardsToPile(cards, pile, DURATION_MOVE); 0194 setKeyboardFocus(pile->topCard()); 0195 } 0196 0197 if (talon->isEmpty() && pile->count() <= 1) 0198 Q_EMIT newCardsPossible(false); 0199 0200 // we need to look that many steps in the future to see if we can lose 0201 setNeededFutureMoves(talon->count() + pile->count()); 0202 0203 return true; 0204 } 0205 0206 void Klondike::restart(const QList<KCard *> &cards) 0207 { 0208 QList<KCard *> cardList = cards; 0209 0210 for (int round = 0; round < 7; ++round) 0211 for (int i = round; i < 7; ++i) 0212 addCardForDeal(play[i], cardList.takeLast(), (i == round), talon->pos()); 0213 0214 while (!cardList.isEmpty()) { 0215 KCard *c = cardList.takeFirst(); 0216 c->setPos(talon->pos()); 0217 c->setFaceUp(false); 0218 talon->add(c); 0219 } 0220 0221 startDealAnimation(); 0222 } 0223 0224 void Klondike::gameTypeChanged() 0225 { 0226 stopDemo(); 0227 0228 if (allowedToStartNewGame()) { 0229 setEasy(options->currentItem() == 0); 0230 startNew(gameNumber()); 0231 } else { 0232 // If we're not allowed, reset the option to 0233 // the current number of suits. 0234 options->setCurrentItem(easyRules ? 0 : 1); 0235 } 0236 } 0237 0238 void Klondike::setGameState(const QString &state) 0239 { 0240 Q_UNUSED(state); 0241 0242 Q_EMIT newCardsPossible(!talon->isEmpty() || pile->count() > 1); 0243 } 0244 0245 QString Klondike::getGameOptions() const 0246 { 0247 return QString::number(pile->cardsToShow()); 0248 } 0249 0250 void Klondike::setGameOptions(const QString &options) 0251 { 0252 setEasy(options.toInt() == 1); 0253 } 0254 0255 void Klondike::setEasy(bool _EasyRules) 0256 { 0257 if (_EasyRules != easyRules) { 0258 easyRules = _EasyRules; 0259 options->setCurrentItem(easyRules ? 0 : 1); 0260 0261 int drawNumber = easyRules ? 1 : 3; 0262 pile->setCardsToShow(drawNumber); 0263 0264 KCardPile::KeyboardFocusHint hint = easyRules ? KCardPile::ForceFocusTop : KCardPile::NeverFocus; 0265 for (int i = 0; i < 4; ++i) 0266 target[i]->setKeyboardSelectHint(hint); 0267 0268 setSolver(new KlondikeSolver(this, drawNumber)); 0269 0270 Settings::setKlondikeIsDrawOne(easyRules); 0271 } 0272 } 0273 0274 bool Klondike::drop() 0275 { 0276 bool pileempty = pile->isEmpty(); 0277 if (!DealerScene::drop()) 0278 return false; 0279 if (pile->isEmpty() && !pileempty) 0280 newCards(); 0281 return true; 0282 } 0283 0284 void Klondike::mapOldId(int id) 0285 { 0286 switch (id) { 0287 case DealerInfo::KlondikeDrawOneId: 0288 setEasy(true); 0289 break; 0290 case DealerInfo::KlondikeDrawThreeId: 0291 setEasy(false); 0292 break; 0293 case DealerInfo::KlondikeGeneralId: 0294 default: 0295 // Do nothing. 0296 break; 0297 } 0298 } 0299 0300 int Klondike::oldId() const 0301 { 0302 if (easyRules) 0303 return DealerInfo::KlondikeDrawOneId; 0304 else 0305 return DealerInfo::KlondikeDrawThreeId; 0306 } 0307 0308 static class KlondikeDealerInfo : public DealerInfo 0309 { 0310 public: 0311 KlondikeDealerInfo() 0312 : DealerInfo(kli18n("Klondike"), DealerInfo::KlondikeGeneralId) 0313 { 0314 addSubtype(KlondikeDrawOneId, kli18n("Klondike (Draw 1)")); 0315 addSubtype(KlondikeDrawThreeId, kli18n("Klondike (Draw 3)")); 0316 } 0317 0318 DealerScene *createGame() const override 0319 { 0320 return new Klondike(this); 0321 } 0322 } klondikeDealerInfo; 0323 0324 #include "moc_klondike.cpp"