RetroShare/plugins/qsolocards_plugin/DealAnimation.cpp
defnax 082d5732b0 added game plugin solocards
git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@2346 b45a01b8-16f6-495d-af2f-9b41ad6348cc
2010-02-17 01:14:52 +00:00

481 lines
13 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 "DealAnimation.h"
#include "CardAnimationLock.h"
#include <QtGui/QPixmap>
#include <QtGui/QGraphicsScene>
#include <QtCore/QTimeLine>
#include <QtCore/QTimer>
#include <iostream>
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealItem::DealItem(CardStack * pDst,CardStack * pSrc)
:m_pDst(pDst),
m_pSrc(pSrc),
m_flipList()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealItem::DealItem(const DealItem & rh)
:m_pDst(NULL),
m_pSrc(NULL),
m_flipList()
{
*this=rh;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealItem::~DealItem()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
bool DealItem::getNextCard()
{
bool rc=false;
if (!m_flipList.empty())
{
rc=m_flipList.front();
m_flipList.pop_front();
}
return rc;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealItem & DealItem::operator=(const DealItem & rh)
{
m_pSrc=rh.m_pSrc;
m_pDst=rh.m_pDst;
m_flipList=rh.m_flipList;
return *this;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealGraphicsItem::DealGraphicsItem(DealItem & dealItem,CardMoveRecord &moveRecord)
:QObject(),
QGraphicsPixmapItem(),
m_dealItem(dealItem),
m_flipCard(dealItem.getNextCard()),
m_card(PlayingCard::MaxSuit,PlayingCard::MaxCardIndex),
m_pGItemAni(new QGraphicsItemAnimation),
m_moveRecord(moveRecord),
m_timeLine(),
m_delayTimer(),
m_emitFinished(true)
{
this->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealGraphicsItem::~DealGraphicsItem()
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealGraphicsItem::slotAniFinished()
{
// add the card
m_dealItem.dst()->addCard(m_card,m_moveRecord);
// now update the destination
m_dealItem.dst()->updateStack();
// remove the item from the scene
m_dealItem.src()->scene()->removeItem(this);
// now if the card needs to be flipped. Flip it. If we are not
// emitting a finish signal. Then disable animation for the flip.
if (m_flipCard)
{
m_dealItem.dst()->flipCard(m_dealItem.dst()->getCardVector().size()-1,m_moveRecord,m_emitFinished);
}
delete m_pGItemAni;
m_pGItemAni=NULL;
if (m_emitFinished)
{
emit aniFinished(this);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealGraphicsItem::slotTimeToStart()
{
m_timeLine.start();
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealGraphicsItem::setupAnimation(unsigned int duration,unsigned int delay,unsigned int zValue)
{
m_timeLine.setDuration(duration);
this->connect(&m_timeLine,SIGNAL(finished()),
this,SLOT(slotAniFinished()));
QPointF startPt(m_dealItem.src()->getGlobalLastCardPt());
this->m_card=m_dealItem.src()->removeTopCard(m_moveRecord);
PlayingCardVector cardVector;
cardVector.push_back(this->m_card);
QPixmap * pPixmap=m_dealItem.src()->getStackPixmap(cardVector);
if (pPixmap)
{
this->setPixmap(*pPixmap);
delete pPixmap;
}
// set the z value passed in
this->setZValue(zValue);
// add the item to the scene and move it over the stack in the
// place of the cards we are going to move
m_dealItem.src()->scene()->addItem(this);
this->setPos(startPt);
// setup the animation
m_pGItemAni->setItem(this);
m_pGItemAni->setTimeLine(&m_timeLine);
m_pGItemAni->setPosAt (0, startPt);
m_pGItemAni->setPosAt (1, m_dealItem.dst()->getGlobalCardAddPt());
m_pGItemAni->setRotationAt (0, 0);
// change the rotation slightly depending on how far apart in the
// x direction that stacks are apart.
if (qAbs((int)(startPt.x()-m_dealItem.dst()->getGlobalCardAddPt().x()))<5)
{
}
else if (startPt.x()>=m_dealItem.dst()->getGlobalCardAddPt().x())
{
m_pGItemAni->setRotationAt (.5, 90);
}
else
{
m_pGItemAni->setRotationAt (.5, -90);
}
m_pGItemAni->setRotationAt (1, 0);
// redraw the source stack.
m_dealItem.src()->updateStack();
if (delay>0)
{
m_delayTimer.setSingleShot(true);
m_delayTimer.setInterval(delay);
this->connect(&m_delayTimer,SIGNAL(timeout()),
this,SLOT(slotTimeToStart()));
m_delayTimer.start();
}
else
{
this->slotTimeToStart();
}
}
void DealGraphicsItem::stopAni()
{
bool stopping=false;
if (QTimeLine::Running==m_timeLine.state())
{
m_timeLine.stop();
stopping=true;
}
if (m_delayTimer.isActive())
{
m_delayTimer.stop();
stopping=true;
}
if (stopping)
{
m_emitFinished=false;
this->slotAniFinished();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealAnimation::DealAnimation()
:m_moveRecord(),
m_dealItemVector(),
m_graphicsItemVector(),
m_aniRunning(false),
m_stopAni(false),
m_duration(DealAnimation::PerDealDuration),
m_createMoveRecord(true)
{
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
DealAnimation::~DealAnimation()
{
this->stopAni();
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
bool DealAnimation::dealCards(const DealItemVector & itemVector, bool createMoveRecord)
{
if (m_aniRunning)
{
return false;
}
m_createMoveRecord=createMoveRecord;
// clear any previous items in the move record.
m_moveRecord.clear();
// clear any previous items in the dealItemVector
// and set it equal to the new one.
m_dealItemVector.clear();
m_dealItemVector=itemVector;
if (!CardAnimationLock::getInst().animationsEnabled())
{
this->noAniStackUdpates();
}
else
{
this->buildAniStackUpdates();
CardAnimationLock::getInst().lock();
this->m_aniRunning=true;
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealAnimation::stopAni()
{
// ok we only want to do this if animation is running.
if (m_aniRunning)
{
m_aniRunning=false;
unsigned int i;
// different from the case when we are cleaning these up as we go we want a hard update, and then
// delete of the objects.
for (i=0;i<m_graphicsItemVector.size();i++)
{
m_graphicsItemVector[i]->stopAni();
delete m_graphicsItemVector[i];
}
m_graphicsItemVector.clear();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealAnimation::slotAniFinished(DealGraphicsItem * pDealGraphicsItem)
{
if (m_aniRunning)
{
// cleanup and finialize addition of items to new stacks.
this->cleanUpGraphicsItem(pDealGraphicsItem);
if (m_graphicsItemVector.empty())
{
m_aniRunning=false;
CardAnimationLock::getInst().unlock();
if (!m_createMoveRecord)
{
m_moveRecord.clear();
}
emit cardsMoved(m_moveRecord);
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
bool DealAnimation::cardsRemaining()
{
bool rc=false;
unsigned int i;
for (i=0;i<m_dealItemVector.size();i++)
{
if(!m_dealItemVector[i].isEmpty())
{
rc=true;
break;
}
}
return rc;
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealAnimation::cleanUpGraphicsItem(DealGraphicsItem * pDealGraphicsItem)
{
unsigned int i;
for (i=0;i<m_graphicsItemVector.size();i++)
{
if (m_graphicsItemVector[i]==pDealGraphicsItem)
{
m_graphicsItemVector.erase (m_graphicsItemVector.begin()+i);
pDealGraphicsItem->deleteLater();
break;
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealAnimation::cleanUpGraphicsItems()
{
unsigned int i;
for (i=0;i<m_graphicsItemVector.size();i++)
{
m_graphicsItemVector[i]->deleteLater();
}
m_graphicsItemVector.clear();
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealAnimation::noAniStackUdpates()
{
unsigned int i;
bool oneNotEmpty=true;
while (oneNotEmpty)
{
oneNotEmpty=false;
for (i=0;i<m_dealItemVector.size();i++)
{
if (!m_dealItemVector[i].isEmpty())
{
bool flip=m_dealItemVector[i].getNextCard();
PlayingCard card(m_dealItemVector[i].src()->removeTopCard(m_moveRecord));
m_dealItemVector[i].dst()->addCard(card,m_moveRecord);
if (flip)
{
m_dealItemVector[i].dst()->flipCard(-1,m_moveRecord);
}
m_dealItemVector[i].src()->updateStack();
m_dealItemVector[i].dst()->updateStack();
oneNotEmpty=true;
}
}
}
if (!m_createMoveRecord)
{
m_moveRecord.clear();
}
if (m_aniRunning)
{
emit cardsMoved(m_moveRecord);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////
void DealAnimation::buildAniStackUpdates()
{
unsigned int i;
unsigned int j=0;
bool oneNotEmpty=true;
while (oneNotEmpty)
{
oneNotEmpty=false;
for (i=0;i<m_dealItemVector.size();i++)
{
if (!m_dealItemVector[i].isEmpty())
{
DealGraphicsItem * pCurrGraphicsItem=new DealGraphicsItem(m_dealItemVector[i],m_moveRecord);
// we want these cards to be above that of any stack and the stacks
// have a zValue of 1. And we want the cards to be of different values.
// So, cards that are going
// to dealt first will be on top of the ones that are delayed. A separate var
// is used for the delay. Because we may not have cards for all stacks. And
// we want the delay between each card to be the same. And we don't want a delay
// before dealing the first card.
pCurrGraphicsItem->setupAnimation(m_duration,j*PerCardDelay,j+2);
this->connect(pCurrGraphicsItem,SIGNAL(aniFinished(DealGraphicsItem *)),
this,SLOT(slotAniFinished(DealGraphicsItem *)));
m_graphicsItemVector.push_back(pCurrGraphicsItem);
oneNotEmpty=true;
j++;
}
}
}
}