mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-10-01 02:35:48 -04:00
082d5732b0
git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@2346 b45a01b8-16f6-495d-af2f-9b41ad6348cc
435 lines
12 KiB
C++
435 lines
12 KiB
C++
/*
|
|
QSoloCards is a collection of Solitaire card games written using Qt
|
|
Copyright (C) 2009 Steve Moore
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "GameBoard.h"
|
|
#include <QtGui/QImage>
|
|
#include "CardPixmaps.h"
|
|
#include <QtGui/QResizeEvent>
|
|
#include <QtGui/QCursor>
|
|
#include <QtGui/QMessageBox>
|
|
|
|
#include "CardAnimationLock.h"
|
|
|
|
#include <iostream>
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////
|
|
GameBoard::GameBoard(QWidget * pParent,
|
|
const QString & gameName,
|
|
const QString & gameSettingsId)
|
|
:QGraphicsView(pParent),
|
|
m_scene(),
|
|
m_sToSAniMove(),
|
|
m_dealAni(),
|
|
m_gameName(gameName),
|
|
m_gameSettingsId(gameSettingsId),
|
|
m_gamePixmap(":/images/sol32x32.png"),
|
|
m_undoStack(),
|
|
m_redoStack(),
|
|
m_helpFile(""),
|
|
m_numColsOrRows(1),
|
|
m_resizeType(ResizeByWidth),
|
|
m_demoRunning(false),
|
|
m_pDemoSrcPrev(NULL),
|
|
m_pDemoDstPrev(NULL),
|
|
m_demoCardsPrev(),
|
|
m_demoCardAniTime(GameBoard::DemoNormalCardAniTime),
|
|
m_stacksCreated(false)
|
|
{
|
|
// set the background image
|
|
this->setBackgroundBrush(QImage(":/images/greenfelt.png"));
|
|
this->setCacheMode(QGraphicsView::CacheBackground);
|
|
|
|
// add the scene to the view.
|
|
// and set it to rescale.
|
|
this->setScene(&m_scene);
|
|
|
|
// turnoff the scroll bars on the view
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
|
|
// hook up the animation signal for stack to stack moves. This will enable us
|
|
// to update things just like it was a drag and drop move
|
|
this->connect(&m_sToSAniMove,SIGNAL(cardsMoved(CardMoveRecord)),
|
|
this,SLOT(slotCardsMoved(CardMoveRecord)));
|
|
|
|
this->connect(&m_dealAni,SIGNAL(cardsMoved(CardMoveRecord)),
|
|
this,SLOT(slotCardsMoved(CardMoveRecord)));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////
|
|
GameBoard::~GameBoard()
|
|
{
|
|
CardAnimationLock::getInst().setDemoMode(false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::setCardResizeAlg(unsigned int colsOrRows,CardResizeType resizeType)
|
|
{
|
|
if (colsOrRows<1)
|
|
{
|
|
colsOrRows=1;
|
|
}
|
|
|
|
m_numColsOrRows=colsOrRows;
|
|
|
|
m_resizeType=resizeType;
|
|
|
|
this->updateCardSize(this->size());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::updateCardSize(const QSize & newSize)
|
|
{
|
|
if (GameBoard::ResizeByHeight==m_resizeType)
|
|
{
|
|
CardPixmaps::getInst().setCardHeight((newSize.height()-(LayoutSpacing*(m_numColsOrRows+1)))/m_numColsOrRows);
|
|
}
|
|
else
|
|
{
|
|
CardPixmaps::getInst().setCardWidth((newSize.width()-(LayoutSpacing*(m_numColsOrRows+1)))/m_numColsOrRows);
|
|
}
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// ok go through and restore the last state of all stacks
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::undoMove()
|
|
{
|
|
if (!this->m_undoStack.empty())
|
|
{
|
|
CardMoveRecord moveRecord(m_undoStack.top());
|
|
|
|
CardStack::processCardMoveRecord(CardStack::UndoMove,m_undoStack.top());
|
|
m_undoStack.pop();
|
|
|
|
// if we no longer have any undo info let users know.
|
|
if (!this->canUndoMove())
|
|
{
|
|
emit undoAvail(false);
|
|
}
|
|
|
|
bool canRedo=this->canRedoMove();
|
|
|
|
m_redoStack.push(moveRecord);
|
|
|
|
// if we didn't have a redo previously and now have one
|
|
// emit a signal to tell listeners about it.
|
|
if (!canRedo)
|
|
{
|
|
emit redoAvail(true);
|
|
}
|
|
|
|
// calc the score with the changes
|
|
this->calcScore();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// ok go through and redo the last state of all stacks
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::redoMove()
|
|
{
|
|
if (!this->m_redoStack.empty())
|
|
{
|
|
CardMoveRecord moveRecord(m_redoStack.top());
|
|
|
|
CardStack::processCardMoveRecord(CardStack::RedoMove,m_redoStack.top());
|
|
m_redoStack.pop();
|
|
|
|
// emit a signal that we are out of redo info
|
|
if (!this->canRedoMove())
|
|
{
|
|
emit redoAvail(false);
|
|
}
|
|
|
|
bool canUndo=this->canUndoMove();
|
|
m_undoStack.push(moveRecord);
|
|
|
|
// emit a signal that we now have undo info
|
|
if (!canUndo)
|
|
{
|
|
emit undoAvail(true);
|
|
}
|
|
|
|
// calc the score with the changes
|
|
this->calcScore();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool GameBoard::canUndoMove() const
|
|
{
|
|
return !m_undoStack.empty();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool GameBoard::canRedoMove() const
|
|
{
|
|
return !m_redoStack.empty();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// adding some to the undo stack also means that the redo stack is cleared.
|
|
// since
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::addUndoMove(const CardMoveRecord &moveRecord)
|
|
{
|
|
bool canUndo=this->canUndoMove();
|
|
|
|
m_undoStack.push(moveRecord);
|
|
|
|
// send a signal that we now have undo info
|
|
if (!canUndo)
|
|
{
|
|
emit undoAvail(true);
|
|
}
|
|
|
|
// clear the redo stack because we have made a move
|
|
while(!m_redoStack.empty())
|
|
{
|
|
m_redoStack.pop();
|
|
}
|
|
|
|
// since we have made a move the redo stack is now gone.
|
|
emit redoAvail(false);
|
|
|
|
// calc the score with the changes
|
|
this->calcScore();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::clearUndoRedoStacks()
|
|
{
|
|
while (!this->m_undoStack.empty())
|
|
{
|
|
m_undoStack.pop();
|
|
}
|
|
|
|
while (!this->m_redoStack.empty())
|
|
{
|
|
m_redoStack.pop();
|
|
}
|
|
|
|
emit undoAvail(false);
|
|
emit redoAvail(false);
|
|
|
|
// calc the score with the changes
|
|
this->calcScore();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::restartGame()
|
|
{
|
|
this->stopDemo();
|
|
while (!this->m_undoStack.empty())
|
|
{
|
|
CardMoveRecord moveRecord(m_undoStack.top());
|
|
|
|
CardStack::processCardMoveRecord(CardStack::UndoMove,m_undoStack.top());
|
|
m_undoStack.pop();
|
|
}
|
|
|
|
this->clearUndoRedoStacks();
|
|
|
|
// update the screen for the new layout
|
|
this->update();
|
|
|
|
// calc the score with the changes
|
|
this->calcScore();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::showHint()
|
|
{
|
|
unsigned int index;
|
|
CardStack * pFromStack=NULL;
|
|
CardStack * pToStack=NULL;
|
|
|
|
// show hint if we found one.
|
|
if (this->getHint(pFromStack,index,pToStack) && pToStack && pFromStack)
|
|
{
|
|
CardStack::showHint(pFromStack,index,pToStack);
|
|
}
|
|
else
|
|
{
|
|
QMessageBox::critical(this,this->gameName(),
|
|
tr("No move found involving full sets of movable cards. "
|
|
"It might be possible to make a move not using the full set of movable cards.").trimmed());
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::startDemo(GameBoard::DemoCardAniTime demoCardAniTime)
|
|
{
|
|
if (!m_demoRunning)
|
|
{
|
|
m_pDemoSrcPrev=NULL;
|
|
m_pDemoDstPrev=NULL;
|
|
m_demoCardsPrev.clear();
|
|
|
|
m_demoCardAniTime=demoCardAniTime;
|
|
|
|
m_demoRunning=true;
|
|
CardAnimationLock::getInst().setDemoMode(true);
|
|
emit demoStarted();
|
|
runDemo();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::stopDemo()
|
|
{
|
|
if (m_demoRunning)
|
|
{
|
|
m_demoRunning=false;
|
|
CardAnimationLock::getInst().setDemoMode(false);
|
|
emit demoStopped();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::newGame()
|
|
{
|
|
m_dealAni.stopAni();
|
|
m_sToSAniMove.stopAni();
|
|
|
|
stopDemo();
|
|
|
|
CardStack::clearAllStacks();
|
|
CardStack::updateAllStacks();
|
|
|
|
// clear the undo and redo info
|
|
this->clearUndoRedoStacks();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::slotCardsMoved(const CardMoveRecord & moveRecord)
|
|
{
|
|
// calc the score with the changes
|
|
this->calcScore();
|
|
|
|
// if the move record is not empty add it.
|
|
if (!moveRecord.empty())
|
|
{
|
|
this->addUndoMove(moveRecord);
|
|
}
|
|
|
|
if (this->hasDemo()&& this->isDemoRunning())
|
|
{
|
|
runDemo();
|
|
}
|
|
// if all suits are sent home the game is over. Show a dialog stating that
|
|
// and start the game over.
|
|
if (this->isGameWon())
|
|
{
|
|
QMessageBox::information(this,this->gameName(),tr("Congratulations you won!").trimmed());
|
|
this->newGame();
|
|
}
|
|
else if (CardAnimationLock::getInst().animationsEnabled() &&
|
|
this->hasDemo() && !this->isDemoRunning() &&
|
|
this->isGameWonNotComplete())
|
|
{
|
|
this->startDemo(DemoEndGameCardAniTime);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
void GameBoard::resizeEvent (QResizeEvent * event)
|
|
{
|
|
QGraphicsView::resizeEvent(event);
|
|
|
|
this->updateCardSize(event->size());
|
|
this->m_scene.setSceneRect(QRectF(QPointF(0,0),event->size()));
|
|
|
|
if (!m_stacksCreated)
|
|
{
|
|
m_stacksCreated=true;
|
|
this->createStacks();
|
|
}
|
|
|
|
CardStack::updateAllStacks();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
bool GameBoard::runDemo(bool stopWhenNoMore)
|
|
{
|
|
bool rc=false;
|
|
unsigned int index;
|
|
CardStack * pFromStack=NULL;
|
|
CardStack * pToStack=NULL;
|
|
PlayingCardVector moveCards;
|
|
CardMoveRecord moveRecord;
|
|
|
|
// here we are looking to stop moving cards back and forth over and over
|
|
// between two stacks
|
|
if (this->getHint(pFromStack,index,pToStack) && pToStack && pFromStack)
|
|
{
|
|
rc=true;
|
|
if (pFromStack->removeCardsStartingAt(index,moveCards,moveRecord,true))
|
|
{
|
|
if (m_pDemoSrcPrev==pToStack &&
|
|
m_pDemoDstPrev==pFromStack &&
|
|
m_demoCardsPrev==moveCards)
|
|
{
|
|
rc=false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if there is a hint perform the move
|
|
if (rc)
|
|
{
|
|
pToStack->addCards(moveCards,moveRecord,true);
|
|
|
|
m_pDemoSrcPrev=pFromStack;
|
|
m_pDemoDstPrev=pToStack;
|
|
m_demoCardsPrev=moveCards;
|
|
|
|
this->m_sToSAniMove.moveCards(moveRecord,m_demoCardAniTime);
|
|
}
|
|
else if (stopWhenNoMore)
|
|
{
|
|
stopDemo();
|
|
rc=false;
|
|
}
|
|
else
|
|
{
|
|
rc=false;
|
|
}
|
|
|
|
return rc;
|
|
}
|