1 /*
2     This file is part of the KDE project "KAtomic"
3 
4     SPDX-FileCopyrightText: 2006-2007 Dmitry Suzdalev <dimsuz@gmail.com>
5     SPDX-FileCopyrightText: 2010 Brian Croom <brian.s.croom@gmail.com>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #ifndef PLAYFIELD_H
11 #define PLAYFIELD_H
12 
13 #include "commondefs.h"
14 
15 #include <KGameRenderer>
16 
17 #include <QGraphicsScene>
18 #include <QList>
19 #include <QStack>
20 
21 #define MIN_ELEM_SIZE 30
22 
23 class KConfigGroup;
24 class AtomFieldItem;
25 class ArrowFieldItem;
26 class MoleculePreviewItem;
27 class QTimeLine;
28 class KGamePopupItem;
29 class LevelData;
30 
31 /**
32  *  KAtomic level playfield
33  */
34 class PlayField : public QGraphicsScene
35 {
36     Q_OBJECT
37 public:
38     enum Direction { Up=0, Down, Left, Right };
39     explicit PlayField( QObject *parent );
40     ~PlayField() override;
41     /**
42      *  Resizes playfield to width,height
43      */
44     void resize( int width, int height );
45     /**
46      *  Loads level
47      */
48     void setLevelData(const LevelData* level);
49     /**
50      *  Sets animation speed (0-slow, 1-normal, 2-fast)
51      */
52     void setAnimationSpeed(int speed);
53     /**
54      *  Animates currently selected atom movement in direction dir
55      *  @param numCells used on undos/redos
56      */
57     void moveSelectedAtom( Direction dir, int numCells=0 );
58     /**
59      *  Saves the current game to config object
60      */
61     void saveGame(KConfigGroup& config) const;
62     /**
63      *  Loads game from config object
64      */
65     void loadGame(const KConfigGroup& config);
66     /**
67      *  Returns whether level is finished already
68      */
isLevelFinished()69     bool isLevelFinished() const { return m_levelFinished; }
70     /**
71      * Displays a passive popup message at the bottom of the scene
72      */
73     void showMessage( const QString& message );
74     /**
75      * Name of the current molecule
76      */
77     QString moleculeName() const;
78     /**
79      * The caching SVG pixmap renderer
80      */
renderer()81     KGameRenderer* renderer() { return &m_renderer; }
82 
83 public Q_SLOTS:
84     /**
85      *  Selects next atom
86      */
87     void nextAtom();
88     /**
89      *  Selects previous atom
90      */
91     void previousAtom();
92     /**
93      *  Undoes one movement
94      */
95     void undo();
96     /**
97      *  Redoes one movement
98      */
99     void redo();
100     /**
101      *  Undoes all movements
102      */
103     void undoAll();
104     /**
105      *  Redoes all movements
106      */
107     void redoAll();
108 Q_SIGNALS:
109     void gameOver(int numMoves);
110     void updateMoves(int);
111     void enableUndo(bool);
112     void enableRedo(bool);
113 private Q_SLOTS:
114     void atomAnimFrameChanged(int frame);
115 private:
116     void drawForeground( QPainter*, const QRectF& ) override;
117     void mousePressEvent( QGraphicsSceneMouseEvent* ev ) override;
118 
119     /**
120      *  Checks if molecule is finished
121      */
122     bool checkDone() const;
123     /**
124      *  Re-renders atoms&arrows Pixmaps, updates their positions
125      */
126     void updateFieldItems();
127     /**
128      *  Updates arrows around selected atom
129      */
130     void updateArrows(bool justHide=false);
131     /**
132      * Set the background brush to a properly sized pixmap
133      */
134     void updateBackground();
135     /**
136      *  Returns true if Field cell (x,y) is empty, i.e. it isn't a wall and has no atom
137      */
138     bool cellIsEmpty(int x, int y) const;
139     /**
140      *  Returns true if atom animation is running
141      */
142     bool isAnimating() const;
143 
toPixX(int fieldX)144     inline int toPixX( int fieldX ) const { return fieldX*m_elemSize; }
toPixY(int fieldY)145     inline int toPixY( int fieldY ) const { return fieldY*m_elemSize; }
toFieldX(int pixX)146     inline int toFieldX( int pixX ) const { return pixX/m_elemSize; }
toFieldY(int pixY)147     inline int toFieldY( int pixY ) const { return pixY/m_elemSize; }
fieldCenterX()148     inline int fieldCenterX() const { return toPixX(0) + m_elemSize*FIELD_SIZE/2; }
fieldCenterY()149     inline int fieldCenterY() const { return toPixY(0) + m_elemSize*FIELD_SIZE/2; }
150 
151     /**
152      * Renderer object
153      */
154     KGameRenderer m_renderer;
155     /**
156      *  Number of moves made for current level
157      */
158     int m_numMoves;
159     /**
160      * Level Data
161      */
162     const LevelData* m_levelData;
163     /**
164      *  Element (i.e. atom, wall, arrow) size
165      */
166     int m_elemSize;
167     /**
168      *  List of atom QGraphicsItems
169      */
170     QList<AtomFieldItem*> m_atoms;
171     /**
172      *  Arrow items
173      */
174     ArrowFieldItem *m_upArrow, *m_leftArrow, *m_downArrow, *m_rightArrow;
175     /**
176      * Item used to show messages to user
177      */
178     KGamePopupItem *m_messageItem;
179     /**
180      *  Index of currently selected atom
181      */
182     int m_selIdx;
183     /**
184      *  Direction in which current atom animation moves
185      */
186     Direction m_dir;
187     /**
188      *  Animation speed. Atom will move at 1cell in m_animSpeed msec speed
189      */
190     int m_animSpeed;
191     /**
192      *  Timeline object to control atom movement animation
193      */
194     QTimeLine *m_atomTimeLine;
195     /**
196      *  True if current level is finished and thus all player input should be disabled
197      */
198     bool m_levelFinished;
199 
200     struct AtomMove
201     {
202         int atomIdx; // atom index in m_atoms
203         Direction dir;
204         int numCells;
205         AtomMove( int idx=-1, Direction d=Up, int nc=0 )
atomIdxAtomMove206             : atomIdx(idx), dir(d), numCells(nc) { }
207     };
208     QStack<AtomMove> m_undoStack;
209     QStack<AtomMove> m_redoStack;
210 
211     MoleculePreviewItem *m_previewItem;
212 };
213 
214 #endif
215