Bug 102557 - save game wishlist bug
Summary: save game wishlist bug
Status: RESOLVED FIXED
Alias: None
Product: katomic
Classification: Applications
Component: general (show other bugs)
Version: 2.0
Platform: unspecified Linux
: NOR wishlist
Target Milestone: ---
Assignee: Stephan Kulow
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-03-26 21:14 UTC by Ritesh Raj Sarraf
Modified: 2006-11-07 17:54 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Ritesh Raj Sarraf 2005-03-26 21:14:26 UTC
Version:           2.0 (using KDE 3.3.2,  (3.1))
Compiler:          gcc version 3.3.5 (Debian 1:3.3.5-6)
OS:                Linux (i686) release 2.6.11mbl-bsplash

Please include a feature to save the katomic game so that we can play the same game for days-months.
Comment 1 Dmitry Suzdalev 2006-11-07 17:54:21 UTC
SVN commit 603044 by dimsuz:

Implement wish #102557 - saving/loading of games.

BUG: 102557


 M  +26 -1     gamewidget.cpp  
 M  +3 -0      gamewidget.h  
 M  +107 -38   playfield.cpp  
 M  +18 -6     playfield.h  
 M  +2 -0      toplevel.cpp  


--- trunk/KDE/kdegames/katomic/gamewidget.cpp #603043:603044
@@ -33,6 +33,7 @@
 #include <kstandarddirs.h>
 #include <ksimpleconfig.h>
 #include <kglobalsettings.h>
+#include <kfiledialog.h>
 
 
 #define MPOSX 480
@@ -42,6 +43,7 @@
 // #	class GameWidget    #
 // ##########################
 
+// FIXME dimsuz: get rid of it
 int level;
 
 void GameWidget::moveUp()
@@ -102,7 +104,6 @@
     {
         high.exec();
     }
-    updateLevel(level+1);
 }
 
 void GameWidget::updateMoves(int moves)
@@ -212,6 +213,30 @@
 {
 }
 
+void GameWidget::saveGame()
+{
+    QString fileName = KFileDialog::getSaveFileName( KUrl(), "*.katomic", this );
+    if(fileName.isEmpty())
+        return;
+    KSimpleConfig config(fileName);
+    config.setGroup("Savegame");
+    config.writeEntry( "Level", level );
+    m_playField->saveGame( config );
+}
+
+void GameWidget::loadGame()
+{
+    QString fileName = KFileDialog::getOpenFileName( KUrl(), "*.katomic", this );
+    if(fileName.isEmpty())
+        return;
+    KSimpleConfig config(fileName);
+    config.setGroup("Savegame");
+    int l = config.readEntry( "Level", 1 );
+    level = l;
+    updateLevel(level);
+    m_playField->loadGame( config );
+}
+
 void GameWidget::showHighscores ()
 {
     KScoreDialog high(KScoreDialog::Name | KScoreDialog::Score, this);
--- trunk/KDE/kdegames/katomic/gamewidget.h #603043:603044
@@ -46,6 +46,9 @@
     // bringt level auf neuesten stand
     void updateLevel (int);
 
+    void saveGame();
+    void loadGame();
+
     // restart current level
     void restartLevel();
 
--- trunk/KDE/kdegames/katomic/playfield.cpp #603043:603044
@@ -74,7 +74,7 @@
 // =============== Play Field ========================
 
 PlayField::PlayField( QObject* parent )
-    : QGraphicsScene(parent), m_mol(0), m_numMoves(0), m_elemSize(30), m_selAtom(0), m_animSpeed(120)
+    : QGraphicsScene(parent), m_mol(0), m_numMoves(0), m_elemSize(30), m_selIdx(-1), m_animSpeed(120)
 {
     m_renderer = new KAtomicRenderer( KStandardDirs::locate("appdata", "pics/default_theme.svgz"), this );
     m_renderer->setElementSize( m_elemSize );
@@ -137,7 +137,7 @@
         }
     }
 
-    m_selAtom = 0;
+    m_selIdx = -1;
     updateArrows(true); // this will hide them (no atom selected)
     updateFieldItems();
     nextAtom();
@@ -150,7 +150,7 @@
         item->setPixmap( m_renderer->renderAtom( m_mol->getAtom(item->atomNum()) ) );
 
         // this may be true if resize happens during animation
-        if(m_timeLine->state() == QTimeLine::Running && item == m_selAtom )
+        if( isAnimating() && m_selIdx != -1 && item == m_atoms.at(m_selIdx) )
             continue; // its position will be taken care of in animFrameChanged()
 
         item->setPos( toPixX( item->fieldX() ), toPixY( item->fieldY() ) );
@@ -180,7 +180,7 @@
     m_renderer->setBackgroundSize( QSize(width, height) );
 
     // if animation is running we need to rescale timeline
-    if( m_timeLine->state() == QTimeLine::Running )
+    if( isAnimating() )
     {
         kDebug() << "restarting animation" << endl;
         int curTime = m_timeLine->currentTime();
@@ -197,15 +197,15 @@
 
 void PlayField::nextAtom()
 {
-    if(!m_selAtom)
+    if(m_selIdx == -1)
     {
-        m_selAtom = m_atoms.at(0);
+        m_selIdx = 0;
         updateArrows();
         return;
     }
 
-    int xs = m_selAtom->fieldX();
-    int ys = m_selAtom->fieldY()+1;
+    int xs = m_atoms.at(m_selIdx)->fieldX();
+    int ys = m_atoms.at(m_selIdx)->fieldY()+1;
 
     int x = xs;
 
@@ -219,7 +219,7 @@
             item = qgraphicsitem_cast<FieldGraphicsItem*>( itemAt(px, py) );
             if( item != 0 && item->atomNum() != -1 )
             {
-                m_selAtom = item;
+                m_selIdx = m_atoms.indexOf(item);
                 updateArrows();
                 return;
             }
@@ -233,15 +233,15 @@
 
 void PlayField::previousAtom()
 {
-    if(!m_selAtom)
+    if(m_selIdx == -1)
     {
-        m_selAtom = m_atoms.at(0);
+        m_selIdx = 0;
         updateArrows();
         return;
     }
 
-    int xs = m_selAtom->fieldX();
-    int ys = m_selAtom->fieldY()-1;
+    int xs = m_atoms.at(m_selIdx)->fieldX();
+    int ys = m_atoms.at(m_selIdx)->fieldY()-1;
 
     int x = xs;
 
@@ -255,7 +255,7 @@
             item = qgraphicsitem_cast<FieldGraphicsItem*>( itemAt(px, py) );
             if( item != 0 && item->atomNum() != -1 )
             {
-                m_selAtom = item;
+                m_selIdx = m_atoms.indexOf(item);
                 updateArrows();
                 return;
             }
@@ -269,7 +269,7 @@
 
 void PlayField::undo()
 {
-    if(m_timeLine->state() == QTimeLine::Running || m_undoStack.isEmpty())
+    if( isAnimating() || m_undoStack.isEmpty())
         return;
 
     AtomMove am = m_undoStack.pop();
@@ -284,7 +284,7 @@
     m_numMoves--;
     emit updateMoves(m_numMoves);
 
-    m_selAtom = am.atom;
+    m_selIdx = am.atomIdx;
     switch( am.dir )
     {
         case Up:
@@ -304,7 +304,7 @@
 
 void PlayField::redo()
 {
-    if(m_timeLine->state() == QTimeLine::Running || m_redoStack.isEmpty())
+    if( isAnimating() || m_redoStack.isEmpty() )
         return;
 
     AtomMove am = m_redoStack.pop();
@@ -320,22 +320,23 @@
     m_numMoves++;
     emit updateMoves(m_numMoves);
 
-    m_selAtom = am.atom;
+    m_selIdx = am.atomIdx;
     moveSelectedAtom(am.dir, am.numCells);
 }
 
 void PlayField::mousePressEvent( QGraphicsSceneMouseEvent* ev )
 {
-    if( m_timeLine->state() == QTimeLine::Running )
+    if( isAnimating() )
         return;
 
     FieldGraphicsItem *clickedItem = qgraphicsitem_cast<FieldGraphicsItem*>(itemAt(ev->scenePos()));
     if(!clickedItem)
         return;
 
-    if( m_atoms.indexOf( clickedItem ) != -1 ) // that is: atom selected
+    int idx = m_atoms.indexOf( clickedItem );
+    if( idx != -1 ) // that is: atom selected
     {
-        m_selAtom = clickedItem;
+        m_selIdx = idx;
         updateArrows();
     }
     else if( clickedItem == m_upArrow )
@@ -358,7 +359,7 @@
 
 void PlayField::moveSelectedAtom( Direction dir, int numCells )
 {
-    if( m_timeLine->state() == QTimeLine::Running )
+    if( isAnimating() )
         return;
 
 
@@ -371,8 +372,8 @@
     {
         // helpers
         int x = 0, y = 0;
-        int selX = m_selAtom->fieldX();
-        int selY = m_selAtom->fieldY();
+        int selX = m_atoms.at(m_selIdx)->fieldX();
+        int selY = m_atoms.at(m_selIdx)->fieldY();
         switch( dir )
         {
             case Up:
@@ -417,7 +418,7 @@
     {
         if(m_undoStack.isEmpty())
             emit enableUndo(true);
-        m_undoStack.push( AtomMove(m_selAtom, m_dir, numEmptyCells) );
+        m_undoStack.push( AtomMove(m_selIdx, m_dir, numEmptyCells) );
     }
 
     m_timeLine->setCurrentTime(0); // reset
@@ -429,34 +430,34 @@
 
 void PlayField::animFrameChanged(int frame)
 {
-    int posx= toPixX(m_selAtom->fieldX());
-    int posy= toPixY(m_selAtom->fieldY());
+    FieldGraphicsItem *selAtom = m_atoms.at(m_selIdx);
+    int posx= toPixX(selAtom->fieldX());
+    int posy= toPixY(selAtom->fieldY());
 
     switch( m_dir )
     {
         case Up:
-            posy = toPixY(m_selAtom->fieldY()) - frame;
-            m_selAtom->setPos( posx, posy );
+            posy = toPixY(selAtom->fieldY()) - frame;
             break;
         case Down:
-            posy = toPixY(m_selAtom->fieldY()) + frame;
+            posy = toPixY(selAtom->fieldY()) + frame;
             break;
         case Left:
-            posx = toPixX(m_selAtom->fieldX()) - frame;
+            posx = toPixX(selAtom->fieldX()) - frame;
             break;
         case Right:
-            posx = toPixX(m_selAtom->fieldX()) + frame;
+            posx = toPixX(selAtom->fieldX()) + frame;
             break;
     }
 
-    m_selAtom->setPos(posx, posy);
+    selAtom->setPos(posx, posy);
 
     if(frame == m_timeLine->endFrame()) // that is: move finished
     {
         // FIXME dimsuz: consider moving this to separate function
         // to improve code readablility
-        m_selAtom->setFieldX( toFieldX((int)m_selAtom->pos().x()) );
-        m_selAtom->setFieldY( toFieldY((int)m_selAtom->pos().y()) );
+        selAtom->setFieldX( toFieldX((int)selAtom->pos().x()) );
+        selAtom->setFieldY( toFieldY((int)selAtom->pos().y()) );
         updateArrows();
 
         emit updateMoves(m_numMoves);
@@ -525,11 +526,11 @@
     m_leftArrow->hide();
     m_rightArrow->hide();
 
-    if(justHide || !m_selAtom)
+    if(justHide || m_selIdx == -1)
         return;
 
-    int selX = m_selAtom->fieldX();
-    int selY = m_selAtom->fieldY();
+    int selX = m_atoms.at(m_selIdx)->fieldX();
+    int selY = m_atoms.at(m_selIdx)->fieldY();
 
     if(cellIsEmpty(selX-1, selY))
     {
@@ -568,4 +569,72 @@
                 p->drawPixmap(toPixX(i), toPixY(j), aPix);
 }
 
+bool PlayField::isAnimating() const
+{
+    return (m_timeLine->state() == QTimeLine::Running);
+}
+
+void PlayField::saveGame( KSimpleConfig& config ) const
+{
+    // REMEMBER: while saving use atom indexes within m_atoms, not atom's atomNum()'s.
+    // atomNum()'s arent unique, there can be several atoms
+    // in molecule which represent same atomNum
+    
+    for(int idx=0; idx<m_atoms.count(); ++idx)
+    {
+        // we'll write pos through using QPoint
+        // I'd use QPair but it isn't supported by QVariant
+        QPoint pos(m_atoms.at(idx)->fieldX(), m_atoms.at(idx)->fieldY()); 
+        config.writeEntry( QString("Atom_%1").arg(idx), pos);
+    }
+
+    // save undo history
+    int moveCount = m_undoStack.count();
+    config.writeEntry( "MoveCount", moveCount );
+    AtomMove mv;
+    for(int i=0;i<moveCount;++i)
+    {
+        mv = m_undoStack.at(i);
+        // atomIdx, direction, numCells
+        QList<int> move;
+        move << mv.atomIdx << static_cast<int>(mv.dir) << mv.numCells;
+        config.writeEntry( QString("Move_%1").arg(i), move );
+    }
+    config.writeEntry("SelectedAtom", m_selIdx);
+}
+
+void PlayField::loadGame( const KSimpleConfig& config )
+{
+    // it is assumed that this method is called right after loadLevel() so
+    // level itself is already loaded at this point
+    
+    // read atom positions
+    for(int idx=0; idx<m_atoms.count(); ++idx)
+    {
+        QPoint pos = config.readEntry( QString("Atom_%1").arg(idx), QPoint() );
+        m_atoms.at(idx)->setFieldXY(pos.x(), pos.y());
+        m_atoms.at(idx)->setPos( toPixX(pos.x()), toPixY(pos.y()) );
+    }
+    // fill undo history
+    m_numMoves = config.readEntry("MoveCount", 0);
+
+    AtomMove mv;
+    for(int i=0;i<m_numMoves;++i)
+    {
+        QList<int> move = config.readEntry( QString("Move_%1").arg(i), QList<int>() );
+        mv.atomIdx = move.at(0);
+        mv.dir = static_cast<Direction>(move.at(1));
+        mv.numCells = move.at(2);
+        m_undoStack.push(mv);
+    }
+    if(m_numMoves)
+    {
+        emit enableUndo(true);
+        emit updateMoves(m_numMoves);
+    }
+
+    m_selIdx = config.readEntry("SelectedAtom", 0);
+    updateArrows();
+}
+
 #include "playfield.moc"
--- trunk/KDE/kdegames/katomic/playfield.h #603043:603044
@@ -43,7 +43,7 @@
 {
     Q_OBJECT
 public:
-    enum Direction { Up, Down, Left, Right };
+    enum Direction { Up=0, Down, Left, Right };
     /**
      *  Constructor
      */
@@ -85,6 +85,14 @@
      *  Redoes one movement
      */
     void redo();
+    /**
+     *  Saves the current game to config object
+     */
+    void saveGame(KSimpleConfig& config) const;
+    /**
+     *  Loads game from config object
+     */
+    void loadGame(const KSimpleConfig& config);
 private slots:
     void animFrameChanged(int frame);
 signals:
@@ -114,6 +122,10 @@
      *  Returns true if Field cell (x,y) is empty, i.e. it isn't a wall and has no atom
      */
     bool cellIsEmpty(int x, int y) const;
+    /**
+     *  Returns true if atom animation is running
+     */
+    bool isAnimating() const;
 
     inline int toPixX( int fieldX ) { return fieldX*m_elemSize; }
     inline int toPixY( int fieldY ) { return fieldY*m_elemSize; }
@@ -150,9 +162,9 @@
      */
     FieldGraphicsItem *m_upArrow, *m_leftArrow, *m_downArrow, *m_rightArrow;
     /**
-     *  Currently selected atom
+     *  Index of currently selected atom
      */
-    FieldGraphicsItem *m_selAtom;
+    int m_selIdx;
     /**
      *  Direction in which current atom animation moves
      */
@@ -168,11 +180,11 @@
 
     struct AtomMove
     {
-        FieldGraphicsItem* atom;
+        int atomIdx; // atom index in m_atoms
         Direction dir;
         int numCells;
-        AtomMove( FieldGraphicsItem* at=0, Direction d=Up, int nc=0 )
-            : atom(at), dir(d), numCells(nc) { }
+        AtomMove( int idx=-1, Direction d=Up, int nc=0 )
+            : atomIdx(idx), dir(d), numCells(nc) { }
     };
     QStack<AtomMove> m_undoStack;
     QStack<AtomMove> m_redoStack;
--- trunk/KDE/kdegames/katomic/toplevel.cpp #603043:603044
@@ -39,6 +39,8 @@
 {
     KAction *act = KStdGameAction::highscores(m_gameWid, SLOT(showHighscores()), actionCollection());
     act->setText(i18n("Show &Highscores"));
+    KStdGameAction::load( m_gameWid, SLOT(loadGame()), actionCollection() );
+    KStdGameAction::save( m_gameWid, SLOT(saveGame()), actionCollection() );
     KStdGameAction::quit(this, SLOT(close()), actionCollection());
     KStdGameAction::restart(m_gameWid, SLOT(restartLevel()), actionCollection());