1 //
2 // Cross-platform free Puyo-Puyo clone.
3 // Copyright (C) 2006, 2007 Emma's Software
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 #if defined (HAVE_CONFIG_H)
20 #include <config.h>
21 #endif // HAVE_CONFIG_H
22 #include <algorithm>
23 #include <assert.h>
24 #include <cmath>
25 #include "ChainLabel.h"
26 #include "File.h"
27 #include "Grid.h"
28 #include "GridStatus.h"
29 #include "PairGenerator.h"
30 #include "System.h"
31
32 using namespace Amoebax;
33
34 // Constants.
35 static const float k_Cos[360] = {1.000000f, 0.999848f, 0.999391f, 0.998630f,
36 0.997564f, 0.996195f, 0.994522f, 0.992546f,
37 0.990268f, 0.987688f, 0.984808f, 0.981627f,
38 0.978148f, 0.974370f, 0.970296f, 0.965926f,
39 0.961262f, 0.956305f, 0.951057f, 0.945519f,
40 0.939693f, 0.933580f, 0.927184f, 0.920505f,
41 0.913545f, 0.906308f, 0.898794f, 0.891007f,
42 0.882948f, 0.874620f, 0.866025f, 0.857167f,
43 0.848048f, 0.838671f, 0.829038f, 0.819152f,
44 0.809017f, 0.798636f, 0.788011f, 0.777146f,
45 0.766044f, 0.754710f, 0.743145f, 0.731354f,
46 0.719340f, 0.707107f, 0.694658f, 0.681998f,
47 0.669131f, 0.656059f, 0.642788f, 0.629320f,
48 0.615661f, 0.601815f, 0.587785f, 0.573576f,
49 0.559193f, 0.544639f, 0.529919f, 0.515038f,
50 0.500000f, 0.484810f, 0.469472f, 0.453990f,
51 0.438371f, 0.422618f, 0.406737f, 0.390731f,
52 0.374607f, 0.358368f, 0.342020f, 0.325568f,
53 0.309017f, 0.292372f, 0.275637f, 0.258819f,
54 0.241922f, 0.224951f, 0.207912f, 0.190809f,
55 0.173648f, 0.156434f, 0.139173f, 0.121869f,
56 0.104528f, 0.087156f, 0.069756f, 0.052336f,
57 0.034899f, 0.017452f, 0.000000f, -0.017452f,
58 -0.034899f, -0.052336f, -0.069756f, -0.087156f,
59 -0.104528f, -0.121869f, -0.139173f, -0.156434f,
60 -0.173648f, -0.190809f, -0.207912f, -0.224951f,
61 -0.241922f, -0.258819f, -0.275637f, -0.292372f,
62 -0.309017f, -0.325568f, -0.342020f, -0.358368f,
63 -0.374607f, -0.390731f, -0.406737f, -0.422618f,
64 -0.438371f, -0.453990f, -0.469472f, -0.484810f,
65 -0.500000f, -0.515038f, -0.529919f, -0.544639f,
66 -0.559193f, -0.573576f, -0.587785f, -0.601815f,
67 -0.615661f, -0.629320f, -0.642788f, -0.656059f,
68 -0.669131f, -0.681998f, -0.694658f, -0.707107f,
69 -0.719340f, -0.731354f, -0.743145f, -0.754710f,
70 -0.766044f, -0.777146f, -0.788011f, -0.798636f,
71 -0.809017f, -0.819152f, -0.829038f, -0.838671f,
72 -0.848048f, -0.857167f, -0.866025f, -0.874620f,
73 -0.882948f, -0.891007f, -0.898794f, -0.906308f,
74 -0.913545f, -0.920505f, -0.927184f, -0.933580f,
75 -0.939693f, -0.945519f, -0.951057f, -0.956305f,
76 -0.961262f, -0.965926f, -0.970296f, -0.974370f,
77 -0.978148f, -0.981627f, -0.984808f, -0.987688f,
78 -0.990268f, -0.992546f, -0.994522f, -0.996195f,
79 -0.997564f, -0.998630f, -0.999391f, -0.999848f,
80 -1.000000f, -0.999848f, -0.999391f, -0.998630f,
81 -0.997564f, -0.996195f, -0.994522f, -0.992546f,
82 -0.990268f, -0.987688f, -0.984808f, -0.981627f,
83 -0.978148f, -0.974370f, -0.970296f, -0.965926f,
84 -0.961262f, -0.956305f, -0.951057f, -0.945519f,
85 -0.939693f, -0.933580f, -0.927184f, -0.920505f,
86 -0.913545f, -0.906308f, -0.898794f, -0.891007f,
87 -0.882948f, -0.874620f, -0.866025f, -0.857167f,
88 -0.848048f, -0.838671f, -0.829038f, -0.819152f,
89 -0.809017f, -0.798636f, -0.788011f, -0.777146f,
90 -0.766044f, -0.754710f, -0.743145f, -0.731354f,
91 -0.719340f, -0.707107f, -0.694658f, -0.681998f,
92 -0.669131f, -0.656059f, -0.642788f, -0.629320f,
93 -0.615661f, -0.601815f, -0.587785f, -0.573576f,
94 -0.559193f, -0.544639f, -0.529919f, -0.515038f,
95 -0.500000f, -0.484810f, -0.469472f, -0.453990f,
96 -0.438371f, -0.422618f, -0.406737f, -0.390731f,
97 -0.374607f, -0.358368f, -0.342020f, -0.325568f,
98 -0.309017f, -0.292372f, -0.275637f, -0.258819f,
99 -0.241922f, -0.224951f, -0.207912f, -0.190809f,
100 -0.173648f, -0.156434f, -0.139173f, -0.121869f,
101 -0.104528f, -0.087156f, -0.069756f, -0.052336f,
102 -0.034899f, -0.017452f, -0.000000f, 0.017452f,
103 0.034899f, 0.052336f, 0.069756f, 0.087156f,
104 0.104528f, 0.121869f, 0.139173f, 0.156434f,
105 0.173648f, 0.190809f, 0.207912f, 0.224951f,
106 0.241922f, 0.258819f, 0.275637f, 0.292372f,
107 0.309017f, 0.325568f, 0.342020f, 0.358368f,
108 0.374607f, 0.390731f, 0.406737f, 0.422618f,
109 0.438371f, 0.453990f, 0.469472f, 0.484810f,
110 0.500000f, 0.515038f, 0.529919f, 0.544639f,
111 0.559193f, 0.573576f, 0.587785f, 0.601815f,
112 0.615661f, 0.629320f, 0.642788f, 0.656059f,
113 0.669131f, 0.681998f, 0.694658f, 0.707107f,
114 0.719340f, 0.731354f, 0.743145f, 0.754710f,
115 0.766044f, 0.777146f, 0.788011f, 0.798636f,
116 0.809017f, 0.819152f, 0.829038f, 0.838671f,
117 0.848048f, 0.857167f, 0.866025f, 0.874620f,
118 0.882948f, 0.891007f, 0.898794f, 0.906308f,
119 0.913545f, 0.920505f, 0.927184f, 0.933580f,
120 0.939693f, 0.945519f, 0.951057f, 0.956305f,
121 0.961262f, 0.965926f, 0.970296f, 0.974370f,
122 0.978148f, 0.981627f, 0.984808f, 0.987688f,
123 0.990268f, 0.992546f, 0.994522f, 0.996195f,
124 0.997564f, 0.998630f, 0.999391f, 0.999848f};
125
126 static const float k_Sin[360] = {0.000000f, 0.017452f, 0.034899f, 0.052336f,
127 0.069756f, 0.087156f, 0.104528f, 0.121869f,
128 0.139173f, 0.156434f, 0.173648f, 0.190809f,
129 0.207912f, 0.224951f, 0.241922f, 0.258819f,
130 0.275637f, 0.292372f, 0.309017f, 0.325568f,
131 0.342020f, 0.358368f, 0.374607f, 0.390731f,
132 0.406737f, 0.422618f, 0.438371f, 0.453990f,
133 0.469472f, 0.484810f, 0.500000f, 0.515038f,
134 0.529919f, 0.544639f, 0.559193f, 0.573576f,
135 0.587785f, 0.601815f, 0.615661f, 0.629320f,
136 0.642788f, 0.656059f, 0.669131f, 0.681998f,
137 0.694658f, 0.707107f, 0.719340f, 0.731354f,
138 0.743145f, 0.754710f, 0.766044f, 0.777146f,
139 0.788011f, 0.798636f, 0.809017f, 0.819152f,
140 0.829038f, 0.838671f, 0.848048f, 0.857167f,
141 0.866025f, 0.874620f, 0.882948f, 0.891007f,
142 0.898794f, 0.906308f, 0.913545f, 0.920505f,
143 0.927184f, 0.933580f, 0.939693f, 0.945519f,
144 0.951057f, 0.956305f, 0.961262f, 0.965926f,
145 0.970296f, 0.974370f, 0.978148f, 0.981627f,
146 0.984808f, 0.987688f, 0.990268f, 0.992546f,
147 0.994522f, 0.996195f, 0.997564f, 0.998630f,
148 0.999391f, 0.999848f, 1.000000f, 0.999848f,
149 0.999391f, 0.998630f, 0.997564f, 0.996195f,
150 0.994522f, 0.992546f, 0.990268f, 0.987688f,
151 0.984808f, 0.981627f, 0.978148f, 0.974370f,
152 0.970296f, 0.965926f, 0.961262f, 0.956305f,
153 0.951057f, 0.945519f, 0.939693f, 0.933580f,
154 0.927184f, 0.920505f, 0.913545f, 0.906308f,
155 0.898794f, 0.891007f, 0.882948f, 0.874620f,
156 0.866025f, 0.857167f, 0.848048f, 0.838671f,
157 0.829038f, 0.819152f, 0.809017f, 0.798636f,
158 0.788011f, 0.777146f, 0.766044f, 0.754710f,
159 0.743145f, 0.731354f, 0.719340f, 0.707107f,
160 0.694658f, 0.681998f, 0.669131f, 0.656059f,
161 0.642788f, 0.629320f, 0.615661f, 0.601815f,
162 0.587785f, 0.573576f, 0.559193f, 0.544639f,
163 0.529919f, 0.515038f, 0.500000f, 0.484810f,
164 0.469472f, 0.453990f, 0.438371f, 0.422618f,
165 0.406737f, 0.390731f, 0.374607f, 0.358368f,
166 0.342020f, 0.325568f, 0.309017f, 0.292372f,
167 0.275637f, 0.258819f, 0.241922f, 0.224951f,
168 0.207912f, 0.190809f, 0.173648f, 0.156434f,
169 0.139173f, 0.121869f, 0.104528f, 0.087156f,
170 0.069756f, 0.052336f, 0.034899f, 0.017452f,
171 0.000000f, -0.017452f, -0.034899f, -0.052336f,
172 -0.069756f, -0.087156f, -0.104528f, -0.121869f,
173 -0.139173f, -0.156434f, -0.173648f, -0.190809f,
174 -0.207912f, -0.224951f, -0.241922f, -0.258819f,
175 -0.275637f, -0.292372f, -0.309017f, -0.325568f,
176 -0.342020f, -0.358368f, -0.374607f, -0.390731f,
177 -0.406737f, -0.422618f, -0.438371f, -0.453990f,
178 -0.469472f, -0.484810f, -0.500000f, -0.515038f,
179 -0.529919f, -0.544639f, -0.559193f, -0.573576f,
180 -0.587785f, -0.601815f, -0.615661f, -0.629320f,
181 -0.642788f, -0.656059f, -0.669131f, -0.681998f,
182 -0.694658f, -0.707107f, -0.719340f, -0.731354f,
183 -0.743145f, -0.754710f, -0.766044f, -0.777146f,
184 -0.788011f, -0.798636f, -0.809017f, -0.819152f,
185 -0.829038f, -0.838671f, -0.848048f, -0.857167f,
186 -0.866025f, -0.874620f, -0.882948f, -0.891007f,
187 -0.898794f, -0.906308f, -0.913545f, -0.920505f,
188 -0.927184f, -0.933580f, -0.939693f, -0.945519f,
189 -0.951057f, -0.956305f, -0.961262f, -0.965926f,
190 -0.970296f, -0.974370f, -0.978148f, -0.981627f,
191 -0.984808f, -0.987688f, -0.990268f, -0.992546f,
192 -0.994522f, -0.996195f, -0.997564f, -0.998630f,
193 -0.999391f, -0.999848f, -1.000000f, -0.999848f,
194 -0.999391f, -0.998630f, -0.997564f, -0.996195f,
195 -0.994522f, -0.992546f, -0.990268f, -0.987688f,
196 -0.984808f, -0.981627f, -0.978148f, -0.974370f,
197 -0.970296f, -0.965926f, -0.961262f, -0.956305f,
198 -0.951057f, -0.945519f, -0.939693f, -0.933580f,
199 -0.927184f, -0.920505f, -0.913545f, -0.906308f,
200 -0.898794f, -0.891007f, -0.882948f, -0.874620f,
201 -0.866025f, -0.857167f, -0.848048f, -0.838671f,
202 -0.829038f, -0.819152f, -0.809017f, -0.798636f,
203 -0.788011f, -0.777146f, -0.766044f, -0.754710f,
204 -0.743145f, -0.731354f, -0.719340f, -0.707107f,
205 -0.694658f, -0.681998f, -0.669131f, -0.656059f,
206 -0.642788f, -0.629320f, -0.615661f, -0.601815f,
207 -0.587785f, -0.573576f, -0.559193f, -0.544639f,
208 -0.529919f, -0.515038f, -0.500000f, -0.484810f,
209 -0.469472f, -0.453990f, -0.438371f, -0.422618f,
210 -0.406737f, -0.390731f, -0.374607f, -0.358368f,
211 -0.342020f, -0.325568f, -0.309017f, -0.292372f,
212 -0.275637f, -0.258819f, -0.241922f, -0.224951f,
213 -0.207912f, -0.190809f, -0.173648f, -0.156434f,
214 -0.139173f, -0.121869f, -0.104528f, -0.087156f,
215 -0.069756f, -0.052336f, -0.034899f, -0.017452f};
216
217 // The speed of the horizontal movement of the queue.
218 static const float k_QueueHorizontalSpeed = 0.10f;
219 // The speed of the vertical movement of the queue.
220 static const float k_QueueVerticalSpeed = 0.25f;
221 // The speed of the satellite rotation, in degress/ms.
222 static const float k_RotationSpeed = 0.5f;
223
224 ///
225 /// \brief Constructor.
226 ///
227 /// \param gridPositionX The X position of the grid's top-left corner.
228 /// \param gridPositionY The Y position of the grid's top-left corner.
229 /// \param queuePositionX The X position of queue's top-left corner,
230 /// where the amoebas wait to fall.
231 /// \param queuePositionY The Y position of queue's top-left corner,
232 /// where the amoebas wait to fall.
233 /// \param waitingGhostPositionX The X position of the top-left corner
234 /// where the ghost amoebas wait to fall.
235 /// \param waitingGhostPositionY The Y position of the top-left corner
236 /// where the ghost amoebas wait to fall.
237 /// \param amoebaSize The size of a single amoeba. The amoebas must be
238 /// squared, so the width and the height are the same.
239 /// \param queueSide The side where the queue is located at.
240 /// \param score The initial grid's score.
241 /// \param layout The grid's layout.
242 ///
Grid(uint16_t gridPositionX,uint16_t gridPositionY,uint16_t queuePositionX,uint16_t queuePositionY,uint16_t waitingGhostPositionX,uint16_t waitingGhostPositionY,uint16_t amoebaSize,QueueSide queueSide,uint32_t score,Layout layout)243 Grid::Grid (uint16_t gridPositionX, uint16_t gridPositionY,
244 uint16_t queuePositionX, uint16_t queuePositionY,
245 uint16_t waitingGhostPositionX, uint16_t waitingGhostPositionY,
246 uint16_t amoebaSize, QueueSide queueSide, uint32_t score,
247 Layout layout):
248 m_ActiveAmoebas (0),
249 m_AmoebaSize (amoebaSize),
250 m_BlinkTime (0),
251 m_ChainLabels (0),
252 m_CurrentStepChain (0),
253 m_CurrentRotationDegrees (0.0f),
254 m_DegreesUntilRotationDone (0.0f),
255 m_DyingAmoebas (0),
256 m_DyingTime (0),
257 m_FallingPair (),
258 m_Filled (false),
259 m_FirstFallingPair (true),
260 m_FloatingAmoebas (0),
261 m_FloatingAmoebasVerticalOffset (0),
262 m_Generator (0),
263 m_Grid (k_GridHeight * k_GridWidth, static_cast<Amoeba *>(0)),
264 m_GridPositionX (gridPositionX),
265 m_GridPositionY (gridPositionY),
266 m_HasNewFallingPair (false),
267 m_InactiveAmoebas (0),
268 m_Layout (layout),
269 m_MaxFallingSpeed (false),
270 m_OpponentGhostAmoebas (0),
271 m_Queue (0),
272 m_QueuedAmoebas (0),
273 m_QueueIsMoving (false),
274 m_QueueSide (queueSide),
275 m_QueuePositionX (queuePositionX),
276 m_QueuePositionY (queuePositionY),
277 m_RemainingQueueHorizontalOffset (0),
278 m_RemainingQueueVerticalOffset (0),
279 m_RotationDirection (1.0f),
280 m_Score (score),
281 m_SilhouetteFrame (0),
282 m_SilhouetteFrameDirection (-1),
283 m_SilhouetteTime (0),
284 m_WaitingInitialAmoebas (true),
285 m_WaitingGhostAmoebas (0),
286 m_WaitingGhostAmoebasNumber (0),
287 m_WaitingGhostPositionX (waitingGhostPositionX),
288 m_WaitingGhostPositionY (waitingGhostPositionY)
289 {
290 // Load sounds.
291 m_DieSound.reset (Sound::fromFile (File::getSoundFilePath ("die.wav")));
292
293 // Create the waiting ghost amoebas.
294 // There's only 6 waiting amoebas, but the "neighbour" state
295 // of each ghost amoebas tells how many "real" ghosts will fall from
296 // this amoeba (max. 5 for each.)
297 for ( uint8_t currentAmoeba = 0 ; currentAmoeba < 6 ; ++currentAmoeba )
298 {
299 m_WaitingGhostAmoebas.push_back (new Amoeba (Amoeba::ColourGhost));
300 m_WaitingGhostAmoebas[currentAmoeba]->setState (Amoeba::StateTopRightBottomLeft);
301 if ( Grid::LayoutVertical == getLayout () )
302 {
303 m_WaitingGhostAmoebas[currentAmoeba]->setY (m_WaitingGhostPositionY);
304 m_WaitingGhostAmoebas[currentAmoeba]->setX (m_WaitingGhostPositionX +
305 getAmoebaSize () *
306 currentAmoeba);
307 }
308 else
309 {
310 if ( Grid::QueueSideLeft == getQueueSide () )
311 {
312 m_WaitingGhostAmoebas[currentAmoeba]->setX (m_WaitingGhostPositionX -
313 getAmoebaSize ());
314 m_WaitingGhostAmoebas[currentAmoeba]->setY (m_WaitingGhostPositionY +
315 getAmoebaSize () *
316 currentAmoeba);
317
318 }
319 else
320 {
321 m_WaitingGhostAmoebas[currentAmoeba]->setX (m_WaitingGhostPositionX);
322 m_WaitingGhostAmoebas[currentAmoeba]->setY (m_WaitingGhostPositionY -
323 getAmoebaSize () *
324 (currentAmoeba + 1));
325 }
326 }
327 }
328 }
329
330 ///
331 /// \brief Destructor.
332 ///
~Grid(void)333 Grid::~Grid (void)
334 {
335 std::for_each (m_ActiveAmoebas.begin (), m_ActiveAmoebas.end (),
336 DeleteObject<Amoeba> ());
337 m_ActiveAmoebas.clear ();
338
339 std::for_each (m_ChainLabels.begin (), m_ChainLabels.end (),
340 DeleteObject<ChainLabel> ());
341 m_ChainLabels.clear ();
342
343 std::for_each (m_InactiveAmoebas.begin (), m_InactiveAmoebas.end (),
344 DeleteObject<Amoeba> ());
345 m_InactiveAmoebas.clear ();
346
347 std::for_each (m_QueuedAmoebas.begin (), m_QueuedAmoebas.end (),
348 DeleteObject<Amoeba> ());
349 m_QueuedAmoebas.clear ();
350
351 std::for_each (m_WaitingGhostAmoebas.begin (), m_WaitingGhostAmoebas.end (),
352 DeleteObject<Amoeba> ());
353 m_WaitingGhostAmoebas.clear ();
354 }
355
356 ///
357 /// \brief Adds a new step chain label.
358 ///
359 /// An step chain label is the text that appears on the grid
360 /// when a chain is made to let know the user which step the
361 /// chain was made in. The label will move a little to the top
362 /// and then will disappear.
363 ///
364 /// \param stepChain The step in which the chain was made in. It starts
365 /// from 1, not 0.
366 /// \param x The screen X position where the label should appear centered at.
367 /// \param y The screen Y position where the label should appear centered at.
368 ///
369 void
addChainLabel(uint8_t stepChain,int16_t x,int16_t y)370 Grid::addChainLabel (uint8_t stepChain, int16_t x, int16_t y)
371 {
372 m_ChainLabels.push_back (new ChainLabel (stepChain, x, y,
373 y - getAmoebaSize ()));
374 }
375
376 ///
377 /// \brief Adds a new amoebas pair.
378 ///
379 /// \param main The main amoeba of the pair.
380 /// \param satellite The satellite amoeba of the pair. This is the one that
381 /// will rotate around \a main.
382 ///
383 void
addNewPair(Amoeba * main,Amoeba * satellite)384 Grid::addNewPair (Amoeba *main, Amoeba *satellite)
385 {
386 // Just add the to the list of waiting amoebas.
387 m_InactiveAmoebas.push_back (main);
388 m_InactiveAmoebas.push_back (satellite);
389 // Set up the initial amoebas.
390 // We need to how 6 amoebas for the set up: The falling pair and
391 // the two waiting pairs.
392 if ( m_WaitingInitialAmoebas && 8 == m_InactiveAmoebas.size () )
393 {
394 m_WaitingInitialAmoebas = false;
395 getNextPair ();
396 getNextPair ();
397 setupFallingPair ();
398 }
399 }
400
401 ///
402 /// \brief Removes all dying amoebas.
403 ///
404 /// Once all dying amoebas are deleted checks for floating amoebas and if
405 /// no floating amoeba is found then sets up the falling pair.
406 ///
407 void
clearDyingAmoebas(void)408 Grid::clearDyingAmoebas (void)
409 {
410 m_DieSound->play ();
411
412 uint8_t amoebasErased = 0;
413 for (std::vector<FallingAmoeba>::iterator currentAmoeba =
414 m_DyingAmoebas.begin () ;
415 currentAmoeba != m_DyingAmoebas.end () ; ++currentAmoeba )
416 {
417 if ( Amoeba::ColourGhost != currentAmoeba->amoeba->getColour () )
418 {
419 ++amoebasErased;
420 }
421 setAmoebaAt (currentAmoeba->x, currentAmoeba->y, 0);
422 m_ActiveAmoebas.remove (currentAmoeba->amoeba);
423 delete currentAmoeba->amoeba;
424 }
425 m_DyingAmoebas.clear ();
426
427 // Activates all chain label.
428 std::for_each (getChainLabels ().begin (), getChainLabels ().end (),
429 std::mem_fun (&ChainLabel::activate));
430
431 // Increment the score based on the number of non-ghost amoebas erased
432 // and the current step chain (starting from 0.)
433 uint16_t score = ((2 * amoebasErased - 4) *
434 10) << getCurrentStepChain ();
435 incrementScore (score);
436 incrementCurrentStepChain ();
437 // Compute the number of ghost amoebas the score generates, subtracts
438 // the number of waiting amoebas and then set the remaining
439 // ghost amoebas as ghost amoebas for the opponent.
440 uint8_t newGhostAmoebas = static_cast<uint16_t>(std::ceil (score / 70.0f));
441 uint8_t remainingGhostAmoebas = removeGhostAmoebas (newGhostAmoebas);
442 setOpponentGhostAmoebas (remainingGhostAmoebas);
443
444 findFloatingAmoebas ();
445 // If there's no floating amoebas, set up the falling pair.
446 if ( m_FloatingAmoebas.empty () )
447 {
448 // If the positions above the grid of the 3rd column (from
449 // the right) is occuped by an amoeba, then the grid is
450 // considered filled. If any amobea is sitting over the grid in
451 // any other column, this is not considered filled.
452 if ( 0 != getAmoebaAt (3, k_FirstVisibleHeight - 1) )
453 {
454 markAsFilled ();
455 }
456 else
457 {
458 scheduleFallingPair ();
459 }
460 }
461 }
462
463 ///
464 /// \brief Finds floating amoebas.
465 ///
466 /// A floating amoeba is an amoeaba that just lost its supporting amoeba
467 /// and hence "is floating" on the air and must start to fall.
468 ///
469 /// Checks each and every amoeba, except for those in the last line, to
470 /// see if it's a floating amoeba and adds it to the m_FloatingAmoeba
471 /// list if so.
472 ///
473 void
findFloatingAmoebas(void)474 Grid::findFloatingAmoebas (void)
475 {
476 // Now check which amoebas are "floating" and make them fall.
477 m_FloatingAmoebasVerticalOffset = 0;
478 for ( int16_t row = k_GridHeight - 2 ; row >= 0 ; --row )
479 {
480 for ( int16_t column = 0 ; column < k_GridWidth ; ++column )
481 {
482 Amoeba *amoeba = getAmoebaAt (column, row);
483 if ( 0 != amoeba && 0 == getAmoebaAt (column, row + 1) )
484 {
485 // This amoeba no longer occupes the current position.
486 setAmoebaAt (column, row, 0);
487 // Now is a falling amoeba.
488 FallingAmoeba floatingAmoeba;
489 floatingAmoeba.amoeba = amoeba;
490 floatingAmoeba.x = column;
491 floatingAmoeba.y = row;
492 m_FloatingAmoebas.push_back (floatingAmoeba);
493 // Set the state of the current and neighbour amoebas.
494 amoeba->setState (Amoeba::StateNone);
495 setAmoebaStateAt (column, row, true);
496 }
497 }
498 }
499 }
500
501 ///
502 /// \brief Gets the amoeba at a given grid's position.
503 ///
504 /// \param x The X position of the grid to get the amoeba from.
505 /// \param y The Y position of the grid to get the amoeba from.
506 /// \return The amoeba at position \p x and \p y or 0 if there's
507 /// no amoeba at this position or the position is incorrect (i.e.,
508 /// out of grid's limits.)
509 ///
510 Amoeba *
getAmoebaAt(int16_t x,int16_t y) const511 Grid::getAmoebaAt (int16_t x, int16_t y) const
512 {
513 if ( 0 <= x && x < k_GridWidth &&
514 0 <= y && y < k_GridHeight )
515 {
516 return m_Grid[y * k_GridWidth + x];
517 }
518 return 0;
519 }
520
521 ///
522 /// \brief Gets the current satellite rotation degree.
523 ///
524 /// \return The current satellite rotation degree.
525 ///
526 inline int16_t
getCurrentRotationDegree(void) const527 Grid::getCurrentRotationDegree (void) const
528 {
529 return static_cast<int16_t> (m_CurrentRotationDegrees);
530 }
531
532 ///
533 /// \brief Gets the main amoeba of the falling pair.
534 ///
535 /// \return The main falling amoeba of the falling pair.
536 ///
537 Grid::FallingAmoeba
getFallingMainAmoeba(void) const538 Grid::getFallingMainAmoeba (void) const
539 {
540 return m_FallingPair.main;
541 }
542
543 ///
544 /// \brief Gets the satellite amoeba of the falling pair.
545 ///
546 /// \return The satellite falling amoeba of the falling pair.
547 ///
548 Grid::FallingAmoeba
getFallingSatelliteAmoeba(void) const549 Grid::getFallingSatelliteAmoeba (void) const
550 {
551 return m_FallingPair.satellite;
552 }
553
554 ///
555 /// \brief Gets the main amoeba that is going to fall after the next pair.
556 ///
557 /// Since this amoeba doesn't have a position, yet, we set it to
558 /// bet at the center of the grid at the top position.
559 ///
560 /// \return The main amoeba that is going to fall after the next pair.
561 ///
562 Grid::FallingAmoeba
getFollowingFallingMainAmoeba(void) const563 Grid::getFollowingFallingMainAmoeba (void) const
564 {
565 FallingAmoeba followingMain;
566
567 followingMain.x = k_GridWidth / 2;
568 followingMain.y = -1;
569 std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
570 ++pair;
571 assert (m_Queue.end () != pair &&
572 "The pairs queue doesn't have two pairs" );
573 followingMain.amoeba = pair->main.amoeba;
574
575 return followingMain;
576 }
577
578 ///
579 /// \brief Gets the satellite amoeba that is going to fall after the next pair.
580 ///
581 /// Since this amoeba doesn't have a position, yet, we set it to
582 /// bet at the center of the grid at the top position.
583 ///
584 /// \return The satellite amoeba that is going to fall after the next pair.
585 ///
586 Grid::FallingAmoeba
getFollowingFallingSatelliteAmoeba(void) const587 Grid::getFollowingFallingSatelliteAmoeba (void) const
588 {
589 FallingAmoeba followingSatellite;
590
591 followingSatellite.x = k_GridWidth / 2;
592 followingSatellite.y = -1;
593 std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
594 ++pair;
595 assert (m_Queue.end () != pair &&
596 "The pairs queue doesn't have two pairs" );
597 followingSatellite.amoeba = pair->satellite.amoeba;
598
599 return followingSatellite;
600 }
601
602 ///
603 /// \brief Gets the main amoeba that is going to fall next.
604 ///
605 /// Since this amoeba doesn't have a position, yet, we set it to
606 /// be at the center of the grid at the top position.
607 ///
608 /// \return The main amoeba that is going to fall next.
609 ///
610 Grid::FallingAmoeba
getNextFallingMainAmoeba(void) const611 Grid::getNextFallingMainAmoeba (void) const
612 {
613 FallingAmoeba nextMain;
614
615 nextMain.x = k_GridWidth / 2;
616 nextMain.y = -1;
617 std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
618 assert (m_Queue.end () != pair && "The pairs queue is empty" );
619 nextMain.amoeba = pair->main.amoeba;
620
621 return nextMain;
622 }
623
624 ///
625 /// \brief Gets the main amoeba that is going to fall next.
626 ///
627 /// Since this amoeba doesn't have a position, yet, we set it to
628 /// be at the center of the grid at the top position.
629 ///
630 /// \return The main amoeba that is going to fall next.
631 ///
632 Grid::FallingAmoeba
getNextFallingSatelliteAmoeba(void) const633 Grid::getNextFallingSatelliteAmoeba (void) const
634 {
635 FallingAmoeba nextSatellite;
636
637 nextSatellite.x = k_GridWidth / 2;
638 nextSatellite.y = -2;
639 std::list<FallingPair>::const_iterator pair = m_Queue.begin ();
640 assert (m_Queue.end () != pair && "The pairs queue is empty" );
641 nextSatellite.amoeba = pair->satellite.amoeba;
642
643 return nextSatellite;
644 }
645
646 ///
647 /// \brief Gets the next pair from the inactive list and sets it to the queue.
648 ///
649 /// The next pair will be the last element in the queue, so it should
650 /// be called when the first element of the queue becomes the falling pair.
651 ///
652 /// If the inactive amoebas list becomes empty, this function request
653 /// to the pair generator a new pair.
654 ///
655 void
getNextPair(void)656 Grid::getNextPair (void)
657 {
658 assert ( 0 != m_Generator &&
659 "Tried to get the next pair without a generator.");
660 assert ( 2 <= m_InactiveAmoebas.size () &&
661 "Tried to get the next pair, but the inactive list is empty.");
662
663 // Gets the next pair from the inactive list.
664 Amoeba *main = m_InactiveAmoebas.front ();
665 m_InactiveAmoebas.pop_front ();
666 Amoeba *satellite = m_InactiveAmoebas.front ();
667 m_InactiveAmoebas.pop_front ();
668 // And set them to queued.
669 m_QueuedAmoebas.push_back (main);
670 m_QueuedAmoebas.push_back (satellite);
671 // Sets they position to be the last pair on the queue.
672 // Take into account the side where the queue is located at
673 // to choose the offset to apply to this pair.
674 uint16_t lastPairOffsetX = getAmoebaSize () / 2;
675 if ( LayoutVertical == getLayout () )
676 {
677 if ( Grid::QueueSideLeft == getQueueSide () )
678 {
679 lastPairOffsetX = -lastPairOffsetX;
680 }
681 main->setX (getQueuePositionX () + lastPairOffsetX);
682 main->setY (getQueuePositionY () + getAmoebaSize () * 3);
683 satellite->setX (getQueuePositionX () + lastPairOffsetX);
684 satellite->setY (getQueuePositionY () + getAmoebaSize () * 2);
685 }
686 else
687 {
688 if (Grid::QueueSideLeft == getQueueSide () )
689 {
690 main->setX (getQueuePositionX () - getAmoebaSize () * 4);
691 main->setY (getQueuePositionY () + lastPairOffsetX);
692 satellite->setX (getQueuePositionX () - getAmoebaSize () * 3);
693 satellite->setY (getQueuePositionY () + lastPairOffsetX);
694 }
695 else
696 {
697 main->setX (getQueuePositionX () + getAmoebaSize () * 3);
698 main->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
699 satellite->setX (getQueuePositionX () + getAmoebaSize () * 2);
700 satellite->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
701 }
702 }
703 // And set up the falling pair structure for when they fall.
704 FallingPair fallingPair;
705 fallingPair.main.amoeba = main;
706 fallingPair.main.x = 3;
707 fallingPair.main.y = k_FirstVisibleHeight - 2;
708 fallingPair.satellite.amoeba = satellite;
709 fallingPair.satellite.x = 3;
710 fallingPair.satellite.y = k_FirstVisibleHeight - 3;
711 fallingPair.verticalOffset = 0;
712 fallingPair.fallingTime = k_DefaultFallingTime;
713 m_Queue.push_back (fallingPair);
714 // Request a new pair if we are out of inactive pairs.
715 if ( m_InactiveAmoebas.empty () )
716 {
717 m_Generator->generate ();
718 }
719 }
720
721 ///
722 /// \brief Gets the side of the queue.
723 ///
724 /// \return The side of the grid where the queue is located at.
725 ///
726 Grid::QueueSide
getQueueSide(void) const727 Grid::getQueueSide (void) const
728 {
729 return m_QueueSide;
730 }
731
732 ///
733 /// \brief Gets the queue's top-left corner X position.
734 ///
735 /// \return The X position of queue's top-left corner.
736 ///
737 uint16_t
getQueuePositionX(void) const738 Grid::getQueuePositionX (void) const
739 {
740 return m_QueuePositionX;
741 }
742
743 ///
744 /// \brief Gets the queue's top-left corner Y position.
745 ///
746 /// \return The Y position of queue's top-left corner.
747 ///
748 uint16_t
getQueuePositionY(void) const749 Grid::getQueuePositionY (void) const
750 {
751 return m_QueuePositionY;
752 }
753
754 ///
755 /// \brief Gets the current grid's state.
756 ///
757 /// \return The grid's state.
758 ///
759 GridStatus
getState(void) const760 Grid::getState (void) const
761 {
762 std::vector<Amoeba::Colour> state (k_GridWidth * k_GridHeight,
763 Amoeba::ColourNone);
764 assert (state.size () == m_Grid.size () &&
765 "The state's size and the grid's size doesn't match.");
766 for ( uint16_t currentAmoeba = 0 ; currentAmoeba < state.size () ;
767 ++currentAmoeba )
768 {
769 Amoeba *amoeba = m_Grid.at (currentAmoeba);
770 if ( 0 != amoeba )
771 {
772 state.at (currentAmoeba) = amoeba->getColour ();
773 }
774 }
775 return GridStatus (state);
776 }
777
778 ///
779 /// \brief Gets the waiting ghost's top-left corner X position.
780 ///
781 /// \return The X position of the waitings ghost's top-left corner.
782 ///
783 uint16_t
getWaitingGhostPositionX(void) const784 Grid::getWaitingGhostPositionX (void) const
785 {
786 return m_WaitingGhostPositionX;
787 }
788
789 ///
790 /// \brief Gets the waiting ghost's top-left corner Y position.
791 ///
792 /// \return The Y position of the waitings ghost's top-left corner.
793 ///
794 uint16_t
getWaitingGhostPositionY(void) const795 Grid::getWaitingGhostPositionY (void) const
796 {
797 return m_WaitingGhostPositionY;
798 }
799
800 ///
801 /// \brief Checks if an amoeba at a give position is the same colour of another.
802 ///
803 /// \param x The grid's X position of the amoeba to check its colour.
804 /// \param y The grid's Y position of the amoeba to check its colour.
805 /// \param amoeba The amoeba to check if its colour is the same as the
806 /// amoeba at \a x i \a y.
807 /// \return \a true if the amoeba at \a x and \a y if of the same colour as
808 /// \a amoeba.
809 ///
810 bool
isOfSameColour(int16_t x,int16_t y,Amoeba * amoeba) const811 Grid::isOfSameColour (int16_t x, int16_t y, Amoeba *amoeba) const
812 {
813 assert ( 0 != amoeba && "Tried to check the colour of a null amoeba.");
814
815 Amoeba *amoebaInGrid = getAmoebaAt (x, y);
816 if ( 0 != amoebaInGrid )
817 {
818 return amoebaInGrid->getColour () == amoeba->getColour ();
819 }
820 return false;
821 }
822
823 ///
824 /// \brief Tells if the queue is moving and needs to be updated.
825 ///
826 /// \return \a true if the amoebas in the queue are moving and needs to
827 /// be updated, \a false otherwise.
828 ///
829 inline bool
isQueueMoving(void) const830 Grid::isQueueMoving (void) const
831 {
832 return m_QueueIsMoving;
833 }
834
835 ///
836 /// \brief Tells if the satellite amoeba is rotating.
837 ///
838 /// \return \a true if the satellite amoeba is rotating.
839 /// \a false otherwise.
840 ///
841 inline bool
isSatelliteRotating(void) const842 Grid::isSatelliteRotating (void) const
843 {
844 return 0 != static_cast<int16_t> (m_DegreesUntilRotationDone);
845 }
846
847 ///
848 /// \brief Increments the number of ghosts amoebas.
849 ///
850 /// \param amount The amount to increment the number of ghost amoebas
851 /// that are waiting to fall into the grid.
852 ///
853 void
incrementNumberOfWaitingGhosts(uint8_t amount)854 Grid::incrementNumberOfWaitingGhosts (uint8_t amount)
855 {
856 setNumberOfWaitingGhosts (getNumberOfWaitingGhosts () + amount);
857 updateWaitingGhosts ();
858 }
859
860 ///
861 /// \brief Sets the waiting ghost as falling amoebas.
862 ///
863 void
makeGhostsFall(void)864 Grid::makeGhostsFall (void)
865 {
866 std::vector<uint8_t> emptyPositions (k_GridWidth);
867 uint8_t remainingGhosts = getNumberOfWaitingGhosts ();
868 int8_t remainingPositions = 0;
869 int8_t y = -1;
870
871 while ( remainingGhosts > 0 )
872 {
873 if ( 0 == remainingPositions )
874 {
875 for ( uint8_t currentPosition = 0 ; currentPosition < k_GridWidth ;
876 ++currentPosition )
877 {
878 emptyPositions.at (currentPosition) = currentPosition;
879 }
880 y -= 1;
881 remainingPositions = k_GridWidth;
882 }
883 uint8_t horizontalPositionIndex = rand () % remainingPositions;
884 int8_t x = emptyPositions.at (horizontalPositionIndex);
885 emptyPositions.at (horizontalPositionIndex) =
886 emptyPositions.at (remainingPositions - 1);
887 --remainingPositions;
888
889 FallingAmoeba ghostAmoeba;
890 ghostAmoeba.amoeba = new Amoeba (Amoeba::ColourGhost);
891 ghostAmoeba.y = y;
892 ghostAmoeba.x = x;
893 m_ActiveAmoebas.push_back (ghostAmoeba.amoeba);
894 m_FloatingAmoebas.push_back (ghostAmoeba);
895
896 --remainingGhosts;
897 }
898 setNumberOfWaitingGhosts (0);
899 updateWaitingGhosts ();
900 }
901
902 ///
903 /// \brief Makes a group of equal colour amoebas recursively.
904 ///
905 /// \param group The group vector to save the members found to.
906 /// \param ghosts The ghosts amoebas that are in contact with the group.
907 /// \param x The X position to look for a posible group candidate.
908 /// \param y The Y position to look for a possible group candidate.
909 /// \param visitedPositions The set of the already visited positions by the
910 /// algorithm. Initially should be empty.
911 /// \param colour The colour the amoeba should be to be part of the group.
912 /// \param initialAmoeba Tells if the amoeba \a x and \a y should be the
913 /// first amoeba of the group.
914 ///
915 void
makeGroup(std::vector<FallingAmoeba> & group,std::vector<FallingAmoeba> & ghosts,int16_t x,int16_t y,std::set<std::pair<int16_t,int16_t>> & visitedPositions,Amoeba::Colour colour,bool initialAmoeba)916 Grid::makeGroup (std::vector<FallingAmoeba> &group,
917 std::vector<FallingAmoeba> &ghosts, int16_t x, int16_t y,
918 std::set<std::pair<int16_t, int16_t> > &visitedPositions,
919 Amoeba::Colour colour, bool initialAmoeba)
920 {
921 // Only check this possition if we didn't visited it yet.
922 if ( visitedPositions.end () ==
923 visitedPositions.find (std::make_pair (x, y)) )
924 {
925 // If there's any amoeba at the given position, check if is a member
926 // of the group.
927 Amoeba *amoeba = getAmoebaAt (x, y);
928 if ( 0 != amoeba && !amoeba->isDying () )
929 {
930 // The initial amoeba is always a member of the group, so
931 // we get its colour and make it the group's colour.
932 if ( initialAmoeba )
933 {
934 colour = amoeba->getColour ();
935 }
936 // Can't make groups of ghost amoebas. Set it to the
937 // ghosts groups and don't look any further.
938 if ( amoeba->getColour () == Amoeba::ColourGhost )
939 {
940 FallingAmoeba ghost;
941 ghost.amoeba = amoeba;
942 ghost.x = x;
943 ghost.y = y;
944 ghosts.push_back (ghost);
945
946 // Add the current position to the already visited positions.
947 visitedPositions.insert (std::make_pair (x, y));
948 }
949 else if ( colour == amoeba->getColour () )
950 {
951 // Once a member is identified, add it to the group.
952 FallingAmoeba groupMember;
953 groupMember.amoeba = amoeba;
954 groupMember.x = x;
955 groupMember.y = y;
956 group.push_back (groupMember);
957
958 // Add the current position to the already visited positions.
959 visitedPositions.insert (std::make_pair (x, y));
960
961 // Check neighbours.
962 makeGroup (group, ghosts, x, y - 1, visitedPositions, colour, false);
963 makeGroup (group, ghosts, x + 1, y, visitedPositions, colour, false);
964 makeGroup (group, ghosts, x, y + 1, visitedPositions, colour, false);
965 makeGroup (group, ghosts, x - 1, y, visitedPositions, colour, false);
966 }
967 }
968 }
969 }
970
971 ///
972 /// \brief Checks all grid's position and tries to make chains.
973 ///
974 /// A chain is nothing more than 4 or more amoebas joined in a single
975 /// group.
976 ///
977 void
makeChain(void)978 Grid::makeChain (void)
979 {
980 // First delete all groups.
981 for ( int16_t row = 0 ; row < k_GridHeight ; ++row )
982 {
983 for ( int16_t column = 0 ; column < k_GridWidth ; ++column )
984 {
985 std::set<std::pair<int16_t, int16_t> > visitedPositions;
986 std::vector<FallingAmoeba> group;
987 std::vector<FallingAmoeba> ghosts;
988 makeGroup (group, ghosts, column, row, visitedPositions);
989 if ( 4 <= group.size () )
990 {
991 int16_t meanXPosition = 0;
992 int16_t meanYPosition = 0;
993
994 m_DyingTime = k_DefaultDyingTime;
995 m_BlinkTime = k_DefaultBlinkTime;
996 for (std::vector<FallingAmoeba>::iterator currentAmoeba =
997 group.begin () ;
998 currentAmoeba != group.end () ; ++currentAmoeba )
999 {
1000 meanXPosition += currentAmoeba->x;
1001 meanYPosition += currentAmoeba->y;
1002 currentAmoeba->amoeba->setDying (true);
1003 m_DyingAmoebas.push_back (*currentAmoeba);
1004 }
1005
1006 for (std::vector<FallingAmoeba>::iterator currentGhost =
1007 ghosts.begin () ;
1008 currentGhost != ghosts.end () ; ++currentGhost )
1009 {
1010 currentGhost->amoeba->setDying (true);
1011 m_DyingAmoebas.push_back (*currentGhost);
1012 }
1013
1014 meanXPosition /= group.size ();
1015 meanYPosition /= group.size ();
1016 addChainLabel (getCurrentStepChain () + 1,
1017 getGridPositionX () +
1018 meanXPosition * getAmoebaSize () +
1019 getAmoebaSize () / 2,
1020 getGridPositionY () +
1021 (meanYPosition - k_FirstVisibleHeight) *
1022 getAmoebaSize () + getAmoebaSize () / 2);
1023 }
1024 }
1025 }
1026
1027 findFloatingAmoebas ();
1028 // If there's no floating amoebas, set up the falling pair.
1029 if ( m_FloatingAmoebas.empty () && m_DyingAmoebas.empty () )
1030 {
1031 if ( getNumberOfWaitingGhosts () == 0 )
1032 {
1033 // If the positions above the grid of the 3rd column (from
1034 // the right) is occuped by an amoeba, then the grid is
1035 // considered filled. If any amobea is sitting over the grid in
1036 // any other column, this is not considered filled.
1037 if ( 0 != getAmoebaAt (3, k_FirstVisibleHeight - 1) )
1038 {
1039 markAsFilled ();
1040 }
1041 else
1042 {
1043 scheduleFallingPair ();
1044 }
1045 }
1046 else
1047 {
1048 makeGhostsFall ();
1049 }
1050 }
1051 }
1052
1053 ///
1054 /// \brief Marks the grid as filled.
1055 ///
1056 /// The grid must be marked as filled when the falling amoebas
1057 /// can't enter the grid.
1058 ///
1059 /// \see isFilled().
1060 ///
1061 void
markAsFilled(void)1062 Grid::markAsFilled (void)
1063 {
1064 m_Filled = true;
1065 }
1066
1067 ///
1068 /// \brief Moves the falling amoeba pair a position to the left.
1069 ///
1070 void
moveLeft(void)1071 Grid::moveLeft (void)
1072 {
1073 if ( 0 < std::min (m_FallingPair.main.x,
1074 m_FallingPair.satellite.x) &&
1075 0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) &&
1076 0 == getAmoebaAt (m_FallingPair.satellite.x - 1,
1077 m_FallingPair.satellite.y) )
1078 {
1079 --m_FallingPair.main.x;
1080 --m_FallingPair.satellite.x;
1081 }
1082 }
1083
1084 ///
1085 /// \brief Moves the falling amoeba pair a position to the right.
1086 ///
1087 void
moveRight(void)1088 Grid::moveRight (void)
1089 {
1090 if ( k_GridWidth - 1 > std::max (m_FallingPair.main.x,
1091 m_FallingPair.satellite.x) &&
1092 0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) &&
1093 0 == getAmoebaAt (m_FallingPair.satellite.x + 1,
1094 m_FallingPair.satellite.y) )
1095 {
1096 ++m_FallingPair.main.x;
1097 ++m_FallingPair.satellite.x;
1098 }
1099 }
1100
1101 ///
1102 /// \brief Removes some waiting ghost amoebas.
1103 ///
1104 /// \param newGhostAmoebas The number of waiting ghost amoebas to remove.
1105 /// \return The number of ghost amoebas remaining after removing
1106 /// \p newGhostAmoebas ghost amoebas.
1107 ///
1108 uint8_t
removeGhostAmoebas(uint8_t newGhostAmoebas)1109 Grid::removeGhostAmoebas (uint8_t newGhostAmoebas)
1110 {
1111 uint8_t remainingGhostAmoebas = 0;
1112 if ( getNumberOfWaitingGhosts () < newGhostAmoebas )
1113 {
1114 remainingGhostAmoebas = newGhostAmoebas - getNumberOfWaitingGhosts ();
1115 setNumberOfWaitingGhosts (0);
1116 }
1117 else
1118 {
1119 setNumberOfWaitingGhosts (getNumberOfWaitingGhosts () - newGhostAmoebas);
1120 }
1121 updateWaitingGhosts ();
1122 return remainingGhostAmoebas;
1123 }
1124
1125 ///
1126 /// \brief Rotates the falling pair clockwise.
1127 ///
1128 void
rotateClockwise(void)1129 Grid::rotateClockwise (void)
1130 {
1131 if ( !isSatelliteRotating () )
1132 {
1133 m_DegreesUntilRotationDone = 90.0f;
1134 m_RotationDirection = -1.0f;
1135
1136 if ( m_FallingPair.satellite.y < m_FallingPair.main.y )
1137 {
1138 if ( k_GridWidth - 1 > m_FallingPair.main.x &&
1139 0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1140 {
1141 m_FallingPair.satellite.x++;
1142 m_FallingPair.satellite.y++;
1143 }
1144 else if ( 1 < m_FallingPair.main.x &&
1145 0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1146 {
1147 m_FallingPair.satellite.y++;
1148 m_FallingPair.main.x--;
1149 }
1150 else
1151 {
1152 // Can't rotate
1153 m_DegreesUntilRotationDone = 0.0f;
1154 }
1155 }
1156 else if ( m_FallingPair.satellite.y > m_FallingPair.main.y )
1157 {
1158 if ( 0 < m_FallingPair.main.x &&
1159 0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1160 {
1161 m_FallingPair.satellite.x--;
1162 m_FallingPair.satellite.y--;
1163 }
1164 else if ( k_GridWidth - 1 > m_FallingPair.main.x &&
1165 0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1166 {
1167 m_FallingPair.satellite.y--;
1168 m_FallingPair.main.x++;
1169 }
1170 else
1171 {
1172 // Can't rotate
1173 m_DegreesUntilRotationDone = 0.0f;
1174 }
1175 }
1176 else if ( m_FallingPair.satellite.x > m_FallingPair.main.x )
1177 {
1178 if ( 0 == getAmoebaAt (m_FallingPair.main.x, m_FallingPair.main.y + 1) )
1179 {
1180 m_FallingPair.satellite.x--;
1181 m_FallingPair.satellite.y++;
1182 }
1183 else
1184 {
1185 // Can't rotate
1186 m_DegreesUntilRotationDone = 0.0f;
1187 }
1188 }
1189 else
1190 {
1191 m_FallingPair.satellite.x++;
1192 m_FallingPair.satellite.y--;
1193 }
1194 }
1195 }
1196
1197 ///
1198 /// \brief Rotates the falling pair counterclockwise.
1199 ///
1200 void
rotateCounterClockwise(void)1201 Grid::rotateCounterClockwise (void)
1202 {
1203 if ( !isSatelliteRotating () )
1204 {
1205 m_DegreesUntilRotationDone = 90.0f;
1206 m_RotationDirection = 1.0f;
1207 if ( m_FallingPair.satellite.y < m_FallingPair.main.y )
1208 {
1209 if ( 0 < m_FallingPair.main.x &&
1210 0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1211 {
1212 m_FallingPair.satellite.x--;
1213 m_FallingPair.satellite.y++;
1214 }
1215 else if ( k_GridWidth - 1 > m_FallingPair.main.x &&
1216 0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1217 {
1218 m_FallingPair.satellite.y++;
1219 m_FallingPair.main.x++;
1220 }
1221 else
1222 {
1223 // Can't rotate
1224 m_DegreesUntilRotationDone = 0.0f;
1225 }
1226 }
1227 else if ( m_FallingPair.satellite.y > m_FallingPair.main.y )
1228 {
1229 if ( k_GridWidth - 1 > m_FallingPair.main.x &&
1230 0 == getAmoebaAt (m_FallingPair.main.x + 1, m_FallingPair.main.y) )
1231 {
1232 m_FallingPair.satellite.x++;
1233 m_FallingPair.satellite.y--;
1234 }
1235 else if ( 1 < m_FallingPair.main.x &&
1236 0 == getAmoebaAt (m_FallingPair.main.x - 1, m_FallingPair.main.y) )
1237 {
1238 m_FallingPair.satellite.y--;
1239 m_FallingPair.main.x--;
1240 }
1241 else
1242 {
1243 // Can't rotate
1244 m_DegreesUntilRotationDone = 0.0f;
1245 }
1246 }
1247 else if ( m_FallingPair.satellite.x < m_FallingPair.main.x )
1248 {
1249 if ( 0 == getAmoebaAt (m_FallingPair.main.x, m_FallingPair.main.y + 1) )
1250 {
1251 m_FallingPair.satellite.x++;
1252 m_FallingPair.satellite.y++;
1253 }
1254 else
1255 {
1256 // Can't rotate
1257 m_DegreesUntilRotationDone = 0.0f;
1258 }
1259 }
1260 else
1261 {
1262 m_FallingPair.satellite.x--;
1263 m_FallingPair.satellite.y--;
1264 }
1265 }
1266 }
1267
1268 ///
1269 /// \brief Rotates the satellite amoeba.
1270 ///
1271 /// \param degreesToRotate The degrees to make rotate the satellite amoeba.
1272 ///
1273 void
rotateSatelliteAmoeba(float degreesToRotate)1274 Grid::rotateSatelliteAmoeba (float degreesToRotate)
1275 {
1276 if ( degreesToRotate > m_DegreesUntilRotationDone )
1277 {
1278 degreesToRotate = m_DegreesUntilRotationDone;
1279 }
1280
1281 m_DegreesUntilRotationDone -= degreesToRotate;
1282 degreesToRotate *= m_RotationDirection;
1283 m_CurrentRotationDegrees += degreesToRotate;
1284 if ( m_CurrentRotationDegrees > 360.0f )
1285 {
1286 m_CurrentRotationDegrees -= 360.0f;
1287 }
1288 else if ( m_CurrentRotationDegrees < 0 )
1289 {
1290 m_CurrentRotationDegrees += 360.0f;
1291 }
1292 setFallingAmoebaScreenPosition (m_FallingPair.satellite,
1293 m_FallingPair.verticalOffset,
1294 isSatelliteRotating ());
1295 }
1296
1297 ///
1298 /// \brief Sets the amoeba at a given grid's position.
1299 ///
1300 /// \param x The X position to set the amoeba to.
1301 /// \param y The Y position to set the amoeba to.
1302 /// \param amoeba The amoeba to set at grid's position \p x and \p y.
1303 ///
1304 void
setAmoebaAt(int16_t x,int16_t y,Amoeba * amoeba)1305 Grid::setAmoebaAt (int16_t x, int16_t y, Amoeba *amoeba)
1306 {
1307 if ( 0 <= x && x < k_GridWidth &&
1308 0 <= y && y < k_GridHeight )
1309 {
1310 m_Grid[y * k_GridWidth + x] = amoeba;
1311 }
1312 }
1313
1314 ///
1315 /// \brief Sets the status of an amoeba at a given position.
1316 ///
1317 /// This checks the neighbors of the amoeba at grid's position \a x and \a y
1318 /// to check how many amoebas of the same colour there are.
1319 ///
1320 /// \param x The grid's X position of the amoeba to set the state to.
1321 /// \param y The grid's Y position of the amoeba to set the state to.
1322 /// \param neighbors If set to \a true, this function sets the state of
1323 /// the neighbors amoebas.
1324 ///
1325 void
setAmoebaStateAt(int16_t x,int16_t y,bool neighbors)1326 Grid::setAmoebaStateAt (int16_t x, int16_t y, bool neighbors)
1327 {
1328 Amoeba *currentAmoeba = getAmoebaAt (x, y);
1329 if ( 0 != currentAmoeba &&
1330 Amoeba::ColourGhost != currentAmoeba->getColour () )
1331 {
1332 int state = Amoeba::StateNone;
1333 if ( isOfSameColour (x, y - 1, currentAmoeba) )
1334 {
1335 state |= Amoeba::StateTop;
1336 }
1337 if ( isOfSameColour (x + 1, y, currentAmoeba) )
1338 {
1339 state |= Amoeba::StateRight;
1340 }
1341 if ( isOfSameColour (x, y + 1, currentAmoeba) )
1342 {
1343 state |= Amoeba::StateBottom;
1344 }
1345 if ( isOfSameColour (x - 1, y, currentAmoeba) )
1346 {
1347 state |= Amoeba::StateLeft;
1348 }
1349 currentAmoeba->setState (static_cast<Amoeba::State>(state));
1350 }
1351
1352 // If we must check neighbors, set the state of the direct
1353 // neighbors (i.e., no diagonals).
1354 if ( neighbors )
1355 {
1356 setAmoebaStateAt (x, y - 1, false);
1357 setAmoebaStateAt (x + 1, y, false);
1358 setAmoebaStateAt (x, y + 1, false);
1359 setAmoebaStateAt (x - 1, y, false);
1360 }
1361 }
1362
1363 ///
1364 /// \brief Gets the next pair from the inactive list and sets it to the queue.
1365 ///
1366 /// The next pair will be the last element in the queue, so it should
1367 /// be called when the first element of the queue becomes the falling pair.
1368 ///
1369 /// If the inactive amoebas list becomes empty, this function request
1370 /// to the pair generator a new pair.
1371 ///
1372 /// This function is used when moving the queue step by step instead of
1373 /// just setting the queued amoeba behind the scene.
1374 ///
1375 void
scheduleFallingPair(void)1376 Grid::scheduleFallingPair (void)
1377 {
1378 assert ( 0 != m_Generator &&
1379 "Tried to get the next pair without a generator.");
1380 assert ( 2 <= m_InactiveAmoebas.size () &&
1381 "Tried to get the next pair, but the inactive list is empty.");
1382
1383 // Gets the next pair from the inactive list.
1384 Amoeba *main = m_InactiveAmoebas.front ();
1385 m_InactiveAmoebas.pop_front ();
1386 Amoeba *satellite = m_InactiveAmoebas.front ();
1387 m_InactiveAmoebas.pop_front ();
1388 // And set them to queued.
1389 m_QueuedAmoebas.push_back (main);
1390 m_QueuedAmoebas.push_back (satellite);
1391 // Sets they position to be the last pair on the queue.
1392 // Take into account the side where the queue is located at
1393 // to choose the offset to apply to this pair.
1394 uint16_t lastPairOffsetX = getAmoebaSize () / 2;
1395 if ( LayoutVertical == getLayout () )
1396 {
1397 if ( Grid::QueueSideLeft == getQueueSide () )
1398 {
1399 lastPairOffsetX = -lastPairOffsetX;
1400 }
1401 main->setX (getQueuePositionX () + lastPairOffsetX);
1402 main->setY (getQueuePositionY () + getAmoebaSize () * 5);
1403 satellite->setX (getQueuePositionX () + lastPairOffsetX);
1404 satellite->setY (getQueuePositionY () + getAmoebaSize () * 4);
1405 }
1406 else
1407 {
1408 if (Grid::QueueSideLeft == getQueueSide () )
1409 {
1410 main->setX (getQueuePositionX () - getAmoebaSize () * 6);
1411 main->setY (getQueuePositionY () + lastPairOffsetX);
1412 satellite->setX (getQueuePositionX () - getAmoebaSize () * 5);
1413 satellite->setY (getQueuePositionY () + lastPairOffsetX);
1414 }
1415 else
1416 {
1417 main->setX (getQueuePositionX () + getAmoebaSize () * 5);
1418 main->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
1419 satellite->setX (getQueuePositionX () + getAmoebaSize () * 4);
1420 satellite->setY (getQueuePositionY () - getAmoebaSize () - lastPairOffsetX);
1421 }
1422 }
1423 // And set up the falling pair structure for when they fall.
1424 FallingPair fallingPair;
1425 fallingPair.main.amoeba = main;
1426 fallingPair.main.x = 3;
1427 fallingPair.main.y = k_FirstVisibleHeight - 2;
1428 fallingPair.satellite.amoeba = satellite;
1429 fallingPair.satellite.x = 3;
1430 fallingPair.satellite.y = k_FirstVisibleHeight - 3;
1431 fallingPair.verticalOffset = 0;
1432 fallingPair.fallingTime = k_DefaultFallingTime;
1433 m_Queue.push_back (fallingPair);
1434 // Request a new pair if we are out of inactive pairs.
1435 if ( m_InactiveAmoebas.empty () )
1436 {
1437 m_Generator->generate ();
1438 }
1439
1440 // Tells that the queue is moving.
1441 m_RemainingQueueHorizontalOffset = getAmoebaSize () / 2;
1442 m_RemainingQueueVerticalOffset = 2 * getAmoebaSize ();
1443 m_QueueIsMoving = true;
1444 }
1445
1446 ///
1447 /// \brief Sets the global screen position to a falling amoeba.
1448 ///
1449 /// \param fallingAmoeba The falling amoeba to set its screen position.
1450 /// \param verticalOffset The offset to add to the amoeba's Y coordinate.
1451 /// \param rotating Tells if the amoeba is rotating and has to take into
1452 /// account the current rotation degrees. Used for the
1453 /// satellite.
1454 ///
1455 void
setFallingAmoebaScreenPosition(FallingAmoeba & fallingAmoeba,int16_t verticalOffset,bool rotating)1456 Grid::setFallingAmoebaScreenPosition (FallingAmoeba &fallingAmoeba,
1457 int16_t verticalOffset, bool rotating)
1458 {
1459 int16_t x = getGridPositionX ();
1460 int16_t y = getGridPositionY ();
1461 if (Grid::LayoutVertical == getLayout ())
1462 {
1463 y += verticalOffset - getAmoebaSize () * k_FirstVisibleHeight;
1464 if ( rotating )
1465 {
1466 float rotationX = k_Cos[getCurrentRotationDegree ()];
1467 float rotationY = k_Sin[getCurrentRotationDegree ()];
1468 int16_t directionX = 0;
1469 int16_t directionY = 0;
1470 if ( 0 <= getCurrentRotationDegree () &&
1471 getCurrentRotationDegree () < 90 )
1472 {
1473 if ( m_RotationDirection < 0.0f )
1474 {
1475 directionX = -1;
1476 directionY = 0;
1477 }
1478 else
1479 {
1480 directionX = 0;
1481 directionY = 1;
1482 }
1483 }
1484 else if ( 90 <= getCurrentRotationDegree () &&
1485 getCurrentRotationDegree () < 180 )
1486 {
1487 if ( m_RotationDirection < 0.0f )
1488 {
1489 directionX = 0;
1490 directionY = 1;
1491 }
1492 else
1493 {
1494 directionX = 1;
1495 directionY = 0;
1496 }
1497 }
1498 else if ( 180 <= getCurrentRotationDegree () &&
1499 getCurrentRotationDegree () < 270 )
1500 {
1501 if ( m_RotationDirection < 0.0f )
1502 {
1503 directionX = 1;
1504 directionY = 0;
1505 }
1506 else
1507 {
1508 directionX = 0;
1509 directionY = -1;
1510 }
1511 }
1512 else if ( getCurrentRotationDegree () >= 270 )
1513 {
1514 if ( m_RotationDirection < 0.0f )
1515 {
1516 directionX = 0;
1517 directionY = -1;
1518 }
1519 else
1520 {
1521 directionX = -1;
1522 directionY = 0;
1523 }
1524 }
1525
1526 x += static_cast<uint16_t> ((fallingAmoeba.x + directionX) *
1527 getAmoebaSize () +
1528 getAmoebaSize () * rotationX);
1529 y += static_cast<uint16_t> ((fallingAmoeba.y + directionY) *
1530 getAmoebaSize () -
1531 getAmoebaSize () * rotationY);
1532 }
1533 else
1534 {
1535 x += fallingAmoeba.x * getAmoebaSize ();
1536 y += fallingAmoeba.y * getAmoebaSize ();
1537 }
1538 }
1539 else
1540 {
1541 float rotationY = k_Cos[getCurrentRotationDegree ()];
1542 float rotationX = k_Sin[getCurrentRotationDegree ()];
1543 int16_t directionY = 0;
1544 int16_t directionX = 0;
1545 if ( rotating )
1546 {
1547 if ( 0 <= getCurrentRotationDegree () &&
1548 getCurrentRotationDegree () < 90 )
1549 {
1550 if ( m_RotationDirection < 0.0f )
1551 {
1552 directionY = -1;
1553 directionX = 0;
1554 }
1555 else
1556 {
1557 directionY = 0;
1558 directionX = 1;
1559 }
1560 }
1561 else if ( 90 <= getCurrentRotationDegree () &&
1562 getCurrentRotationDegree () < 180 )
1563 {
1564 if ( m_RotationDirection < 0.0f )
1565 {
1566 directionY = 0;
1567 directionX = 1;
1568 }
1569 else
1570 {
1571 directionY = 1;
1572 directionX = 0;
1573 }
1574 }
1575 else if ( 180 <= getCurrentRotationDegree () &&
1576 getCurrentRotationDegree () < 270 )
1577 {
1578 if ( m_RotationDirection < 0.0f )
1579 {
1580 directionY = 1;
1581 directionX = 0;
1582 }
1583 else
1584 {
1585 directionY = 0;
1586 directionX = -1;
1587 }
1588 }
1589 else if ( getCurrentRotationDegree () >= 270 )
1590 {
1591 if ( m_RotationDirection < 0.0f )
1592 {
1593 directionY = 0;
1594 directionX = -1;
1595 }
1596 else
1597 {
1598 directionY = -1;
1599 directionX = 0;
1600 }
1601 }
1602 }
1603
1604
1605 if (Grid::QueueSideLeft == getQueueSide ())
1606 {
1607 if (rotating)
1608 {
1609 x -= (fallingAmoeba.y + 1 + directionX) * getAmoebaSize () -
1610 static_cast<uint16_t> (getAmoebaSize () * rotationX);
1611 y += (fallingAmoeba.x + directionY) * getAmoebaSize () +
1612 static_cast<uint16_t> (getAmoebaSize () * rotationY);
1613 }
1614 else
1615 {
1616
1617 x -= (fallingAmoeba.y + 1) * getAmoebaSize ();
1618 y += fallingAmoeba.x * getAmoebaSize ();
1619 }
1620 x -= verticalOffset - getAmoebaSize () * k_FirstVisibleHeight;
1621 }
1622 else
1623 {
1624 if ( rotating )
1625 {
1626 x += (fallingAmoeba.y + directionX) * getAmoebaSize () -
1627 static_cast<uint16_t> (getAmoebaSize () * rotationX);
1628 y -= (fallingAmoeba.x + 1 + directionY) * getAmoebaSize () +
1629 static_cast<uint16_t> (getAmoebaSize () * rotationY);
1630 }
1631 else
1632 {
1633 x += fallingAmoeba.y * getAmoebaSize ();
1634 y -= (fallingAmoeba.x + 1) * getAmoebaSize ();
1635 }
1636 x += verticalOffset - getAmoebaSize () * k_FirstVisibleHeight;
1637 }
1638 }
1639
1640 fallingAmoeba.amoeba->setX (x);
1641 fallingAmoeba.amoeba->setY (y);
1642 }
1643
1644 ///
1645 /// \brief Sets the generator.
1646 ///
1647 /// When the list of waiting amoebas reaches zero, this generator
1648 /// will be the used to generate a new pair.
1649 ///
1650 /// \param generator The generator that the class is linked to and
1651 /// will use to request more pairs.
1652 ///
1653 void
setGenerator(PairGenerator * generator)1654 Grid::setGenerator (PairGenerator *generator)
1655 {
1656 m_Generator = generator;
1657 }
1658
1659 ///
1660 /// \brief Sets the falling speed to maximum.
1661 ///
1662 void
setMaxFallingSpeed(void)1663 Grid::setMaxFallingSpeed (void)
1664 {
1665 m_MaxFallingSpeed = true;
1666 }
1667
1668 ///
1669 /// \brief Sets the status of the new falling pair.
1670 ///
1671 /// \param hasNewFallingPair Set it to \a true when a new falling pair
1672 /// starts to fall. Otherwise set it to \a false.
1673 /// \see hasNewFallingPair().
1674 ///
1675 inline void
setHasNewFallingPair(bool hasNewFallingPair)1676 Grid::setHasNewFallingPair (bool hasNewFallingPair)
1677 {
1678 m_HasNewFallingPair = hasNewFallingPair;
1679 }
1680
1681 ///
1682 /// \brief Sets the next falling pair.
1683 ///
1684 /// Gets the next falling pair from the list of waiting pairs and
1685 /// moves the last element to be the next.
1686 ///
1687 void
setupFallingPair(void)1688 Grid::setupFallingPair (void)
1689 {
1690 // Get the two first queued amoebas and set it to be active.
1691 m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
1692 m_QueuedAmoebas.pop_front ();
1693 m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
1694 m_QueuedAmoebas.pop_front ();
1695
1696 m_FallingPair = m_Queue.front ();
1697 m_Queue.pop_front ();
1698 // Set the screen position without any vertical offset. The grid
1699 // position is set at getNextPair().
1700 setFallingAmoebaScreenPosition (m_FallingPair.main, 0, false);
1701 setFallingAmoebaScreenPosition (m_FallingPair.satellite, 0, false);
1702
1703 // Reset the current rotation.
1704 m_CurrentRotationDegrees = 90.0f;
1705 m_DegreesUntilRotationDone = 0.0f;
1706 m_RotationDirection = 1.0f;
1707
1708 // Now set the remainig pair to be the next to fall and
1709 // add a new pair.
1710 FallingPair remainingPair (m_Queue.front ());
1711 if ( Grid::LayoutVertical == getLayout () )
1712 {
1713 remainingPair.main.amoeba->setX (getQueuePositionX ());
1714 remainingPair.main.amoeba->setY (getQueuePositionY () + getAmoebaSize ());
1715 remainingPair.satellite.amoeba->setX (getQueuePositionX ());
1716 remainingPair.satellite.amoeba->setY (getQueuePositionY ());
1717 }
1718 else
1719 {
1720 if (Grid::QueueSideLeft == getQueueSide () )
1721 {
1722 remainingPair.main.amoeba->setX (getQueuePositionX () - getAmoebaSize () * 2);
1723 remainingPair.main.amoeba->setY (getQueuePositionY ());
1724 remainingPair.satellite.amoeba->setX (getQueuePositionX () - getAmoebaSize ());
1725 remainingPair.satellite.amoeba->setY (getQueuePositionY ());
1726 }
1727 else
1728 {
1729 remainingPair.main.amoeba->setX (getQueuePositionX () + getAmoebaSize ());
1730 remainingPair.main.amoeba->setY (getQueuePositionY () - getAmoebaSize ());
1731 remainingPair.satellite.amoeba->setX (getQueuePositionX ());
1732 remainingPair.satellite.amoeba->setY (getQueuePositionY () - getAmoebaSize ());
1733 }
1734 }
1735 getNextPair ();
1736 setCurrentStepChain (0);
1737 setHasNewFallingPair (true);
1738 }
1739
1740 ///
1741 /// \brief Sets the falling speed to normal.
1742 ///
1743 void
setNormalFallingSpeed(void)1744 Grid::setNormalFallingSpeed (void)
1745 {
1746 m_MaxFallingSpeed = false;
1747 }
1748
1749
1750 ///
1751 /// \brief Sets the current silhoutte frame.
1752 ///
1753 /// \param frame The silhouette frame to set as current.
1754 ///
1755 inline void
setSilhouetteFrame(int8_t frame)1756 Grid::setSilhouetteFrame (int8_t frame)
1757 {
1758 m_SilhouetteFrame = frame;
1759 }
1760
1761 ///
1762 /// \brief Updates the grid.
1763 ///
1764 /// Moves the amoebax according to its state and the time elapsed from the last
1765 /// time this function was called, as specified in \a elapsedTime.
1766 ///
1767 /// \param elapsedTime The elapsed time between the previous and current call,
1768 /// in milliseconds.
1769 ///
1770 void
update(uint32_t elapsedTime)1771 Grid::update (uint32_t elapsedTime)
1772 {
1773 // Only update if we are no waiting the initial amoebas
1774 // and the grid is not filled.
1775 if ( !m_WaitingInitialAmoebas && !isFilled () )
1776 {
1777 // By default, we don't have a new falling pair unless
1778 // we must set up a new pair to fall or is the first one.
1779 setHasNewFallingPair (false || m_FirstFallingPair);
1780 // The first pair is done!
1781 m_FirstFallingPair = false;
1782 // No ghost amoebas for our opponent.
1783 setOpponentGhostAmoebas (0);
1784
1785 // Remove old label chain labels.
1786 for ( std::list<ChainLabel *>::iterator currentLabel =
1787 m_ChainLabels.begin () ;
1788 currentLabel != m_ChainLabels.end () ; )
1789 {
1790 if ( !(*currentLabel)->isAlive () )
1791 {
1792 currentLabel = m_ChainLabels.erase (currentLabel);
1793 }
1794 else
1795 {
1796 ++currentLabel;
1797 }
1798 }
1799 // And update the remaining.
1800 for ( std::list<ChainLabel *>::iterator currentLabel =
1801 m_ChainLabels.begin () ;
1802 currentLabel != m_ChainLabels.end () ;
1803 ++currentLabel )
1804 {
1805 (*currentLabel)->update (elapsedTime);
1806 }
1807
1808 if ( m_FloatingAmoebas.empty () && m_DyingAmoebas.empty () &&
1809 !isQueueMoving () )
1810 {
1811 m_SilhouetteTime -= elapsedTime;
1812 if ( 0 > m_SilhouetteTime )
1813 {
1814 m_SilhouetteTime = k_SilhouetteTime;
1815 setSilhouetteFrame (getSilhouetteFrame () +
1816 m_SilhouetteFrameDirection);
1817 if ( 0 > getSilhouetteFrame () ||
1818 k_MaxSilhouetteFrames == getSilhouetteFrame () )
1819 {
1820 m_SilhouetteFrameDirection = -m_SilhouetteFrameDirection;
1821 setSilhouetteFrame (getSilhouetteFrame () +
1822 m_SilhouetteFrameDirection);
1823 }
1824 }
1825
1826 if ( isSatelliteRotating () )
1827 {
1828 float degreesToRotate = k_RotationSpeed * elapsedTime;
1829 rotateSatelliteAmoeba (degreesToRotate);
1830 }
1831
1832 m_FallingPair.fallingTime -= elapsedTime;
1833 if ( 0 >= m_FallingPair.fallingTime ||
1834 m_MaxFallingSpeed )
1835 {
1836 m_FallingPair.fallingTime = k_DefaultFallingTime;
1837 m_FallingPair.verticalOffset += getAmoebaSize () / 2;
1838 if ( m_MaxFallingSpeed )
1839 {
1840 m_FallingPair.verticalOffset += getAmoebaSize () / 2;
1841 incrementScore ();
1842 }
1843 }
1844
1845
1846 if ( m_FallingPair.verticalOffset >= getAmoebaSize () )
1847 {
1848 m_FallingPair.verticalOffset -= getAmoebaSize ();
1849 ++m_FallingPair.main.y;
1850 ++m_FallingPair.satellite.y;
1851 }
1852
1853 if ( k_GridHeight - 1 == m_FallingPair.main.y ||
1854 k_GridHeight - 1 == m_FallingPair.satellite.y ||
1855 0 != getAmoebaAt (m_FallingPair.main.x,
1856 m_FallingPair.main.y + 1) ||
1857 0 != getAmoebaAt (m_FallingPair.satellite.x,
1858 m_FallingPair.satellite.y + 1) )
1859 {
1860 // Set to no silhouette frame.
1861 setSilhouetteFrame (0);
1862 m_SilhouetteFrameDirection = 1;
1863 // Set the screen position without any vertical offset.
1864 setFallingAmoebaScreenPosition (m_FallingPair.main, 0, false);
1865 setFallingAmoebaScreenPosition (m_FallingPair.satellite, 0, false);
1866 // Set the amoeba's position to be occuped.
1867 setAmoebaAt (m_FallingPair.main.x, m_FallingPair.main.y,
1868 m_FallingPair.main.amoeba);
1869 setAmoebaAt (m_FallingPair.satellite.x,
1870 m_FallingPair.satellite.y,
1871 m_FallingPair.satellite.amoeba);
1872 setAmoebaStateAt (m_FallingPair.main.x,
1873 m_FallingPair.main.y);
1874 setAmoebaStateAt (m_FallingPair.satellite.x,
1875 m_FallingPair.satellite.y);
1876 // If either the satelite or the main amoeba is floating
1877 // make it to behaviour as it should.
1878 findFloatingAmoebas ();
1879 // Check if we have any chain that we can make if no
1880 // floating amoeba is around.
1881 if ( m_FloatingAmoebas.empty () )
1882 {
1883 makeChain ();
1884 }
1885 setNormalFallingSpeed ();
1886 }
1887 else
1888 {
1889 setFallingAmoebaScreenPosition (m_FallingPair.main,
1890 m_FallingPair.verticalOffset,
1891 false);
1892 setFallingAmoebaScreenPosition (m_FallingPair.satellite,
1893 m_FallingPair.verticalOffset,
1894 isSatelliteRotating ());
1895 }
1896 }
1897
1898 else if ( !m_FloatingAmoebas.empty () )
1899 {
1900 m_FloatingAmoebasVerticalOffset += getAmoebaSize () / 2;
1901 std::list<FallingAmoeba>::iterator floatingAmoeba =
1902 m_FloatingAmoebas.begin ();
1903 while ( floatingAmoeba != m_FloatingAmoebas.end () )
1904 {
1905 if ( m_FloatingAmoebasVerticalOffset >= getAmoebaSize () )
1906 {
1907 ++floatingAmoeba->y;
1908 if ( k_GridHeight - 1 == floatingAmoeba->y ||
1909 0 != getAmoebaAt (floatingAmoeba->x,
1910 floatingAmoeba->y + 1) )
1911 {
1912 if ( floatingAmoeba->y >= 0 )
1913 {
1914 setFallingAmoebaScreenPosition (*floatingAmoeba, 0,
1915 false);
1916 setAmoebaAt (floatingAmoeba->x, floatingAmoeba->y,
1917 floatingAmoeba->amoeba);
1918 setAmoebaStateAt (floatingAmoeba->x,
1919 floatingAmoeba->y);
1920 }
1921 else
1922 {
1923 m_ActiveAmoebas.remove (floatingAmoeba->amoeba);
1924 delete floatingAmoeba->amoeba;
1925 }
1926 floatingAmoeba =
1927 m_FloatingAmoebas.erase (floatingAmoeba);
1928 }
1929 else
1930 {
1931 ++floatingAmoeba;
1932 }
1933 }
1934 else
1935 {
1936 setFallingAmoebaScreenPosition (*floatingAmoeba,
1937 m_FloatingAmoebasVerticalOffset, false);
1938 ++floatingAmoeba;
1939 }
1940 }
1941
1942 if ( m_FloatingAmoebasVerticalOffset >= getAmoebaSize () )
1943 {
1944 m_FloatingAmoebasVerticalOffset -= getAmoebaSize ();
1945 }
1946
1947 if ( m_FloatingAmoebas.empty () )
1948 {
1949 makeChain ();
1950 }
1951 }
1952
1953 else if ( !m_DyingAmoebas.empty () )
1954 {
1955 m_DyingTime -= elapsedTime;
1956 if ( 0 >= m_DyingTime )
1957 {
1958 clearDyingAmoebas ();
1959 }
1960 else
1961 {
1962 m_BlinkTime -= elapsedTime;
1963 if ( 0 >= m_BlinkTime )
1964 {
1965 m_BlinkTime = k_DefaultBlinkTime;
1966 for (std::vector<FallingAmoeba>::iterator currentAmoeba =
1967 m_DyingAmoebas.begin () ;
1968 currentAmoeba != m_DyingAmoebas.end () ; ++currentAmoeba )
1969 {
1970 currentAmoeba->amoeba->setVisible (
1971 !currentAmoeba->amoeba->isVisible ());
1972 }
1973 }
1974 }
1975 }
1976
1977 else if ( isQueueMoving () )
1978 {
1979 updateQueue (elapsedTime);
1980 }
1981 }
1982 }
1983
1984 ///
1985 /// \brief Updates the moving queue.
1986 ///
1987 /// \param elapsedTime The time elapsed from the last call.
1988 ///
1989 void
updateQueue(uint32_t elapsedTime)1990 Grid::updateQueue (uint32_t elapsedTime)
1991 {
1992 uint16_t queueHorizontalOffset =
1993 static_cast<uint16_t> (k_QueueVerticalSpeed * elapsedTime);
1994 if ( queueHorizontalOffset > m_RemainingQueueHorizontalOffset )
1995 {
1996 queueHorizontalOffset = m_RemainingQueueHorizontalOffset;
1997 }
1998 m_RemainingQueueHorizontalOffset -= queueHorizontalOffset;
1999
2000 uint16_t queueVerticalOffset =
2001 static_cast<uint16_t> (k_QueueVerticalSpeed * elapsedTime);
2002 if ( queueVerticalOffset > m_RemainingQueueVerticalOffset )
2003 {
2004 queueVerticalOffset = m_RemainingQueueVerticalOffset;
2005 }
2006 m_RemainingQueueVerticalOffset -= queueVerticalOffset;
2007
2008 if ( Grid::LayoutHorizontal == getLayout () )
2009 {
2010 if ( Grid::QueueSideLeft == getQueueSide () )
2011 {
2012 queueVerticalOffset = -queueVerticalOffset;
2013 }
2014 else
2015 {
2016 queueHorizontalOffset = -queueHorizontalOffset;
2017 }
2018 }
2019 else
2020 {
2021 if ( Grid::QueueSideLeft == getQueueSide () )
2022 {
2023 queueHorizontalOffset = -queueHorizontalOffset;
2024 }
2025 }
2026
2027 std::list<Amoeba *>::iterator currentAmoeba = m_QueuedAmoebas.begin () ;
2028 for ( int amoebaNum = 0 ; currentAmoeba != m_QueuedAmoebas.end () ;
2029 ++currentAmoeba, ++amoebaNum )
2030 {
2031 if ( Grid::LayoutHorizontal == getLayout () )
2032 {
2033 (*currentAmoeba)->setX ((*currentAmoeba)->getX () -
2034 queueVerticalOffset);
2035 if ( 2 == amoebaNum || 3 == amoebaNum )
2036 {
2037 (*currentAmoeba)->setY ((*currentAmoeba)->getY () -
2038 queueHorizontalOffset);
2039 }
2040 }
2041 else
2042 {
2043 (*currentAmoeba)->setY ((*currentAmoeba)->getY () -
2044 queueVerticalOffset);
2045
2046 if ( 2 == amoebaNum || 3 == amoebaNum )
2047 {
2048 (*currentAmoeba)->setX ((*currentAmoeba)->getX () -
2049 queueHorizontalOffset);
2050 }
2051 }
2052 }
2053
2054
2055 if ( m_RemainingQueueHorizontalOffset == 0 &&
2056 m_RemainingQueueVerticalOffset == 0 )
2057 {
2058 m_QueueIsMoving = false;
2059
2060 // Get the two first queued amoebas and set it to be active.
2061 m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
2062 m_QueuedAmoebas.pop_front ();
2063 m_ActiveAmoebas.push_back (m_QueuedAmoebas.front ());
2064 m_QueuedAmoebas.pop_front ();
2065
2066 m_FallingPair = m_Queue.front ();
2067 m_Queue.pop_front ();
2068 // Set the screen position without any vertical offset. The grid
2069 // position is set at getNextPair().
2070 setFallingAmoebaScreenPosition (m_FallingPair.main, 0, false);
2071 setFallingAmoebaScreenPosition (m_FallingPair.satellite, 0, false);
2072
2073 // Reset the current rotation.
2074 m_CurrentRotationDegrees = 90.0f;
2075 m_DegreesUntilRotationDone = 0.0f;
2076 m_RotationDirection = 1.0f;
2077
2078 setCurrentStepChain (0);
2079 setHasNewFallingPair (true);
2080 }
2081 }
2082
2083 ///
2084 /// \brief Updates the state of the waiting ghosts amoebas.
2085 ///
2086 /// Updates the state of the amoebas that will be drawn as the
2087 /// waiting status. The "neighbour" state of those amoebas will
2088 /// decide the "weight" that each ghost amoeba has.
2089 ///
2090 void
updateWaitingGhosts(void)2091 Grid::updateWaitingGhosts (void)
2092 {
2093 int8_t remainingGhost = getNumberOfWaitingGhosts ();
2094 for ( uint8_t currentAmoeba = 0 ; currentAmoeba < k_GridWidth ; ++currentAmoeba )
2095 {
2096 uint8_t weight = 0;
2097 if ( remainingGhost > 0 )
2098 {
2099 if ( remainingGhost <= (k_GridWidth - currentAmoeba) )
2100 {
2101 weight = 1;
2102 }
2103 else
2104 {
2105 weight = static_cast<uint8_t>(std::ceil (remainingGhost /
2106 (static_cast<double>(k_GridWidth) - currentAmoeba)));
2107 }
2108 remainingGhost -= weight;
2109 }
2110 m_WaitingGhostAmoebas[currentAmoeba]->setState (
2111 static_cast<Amoeba::State> (Amoeba::StateTopRightBottomLeft - weight));
2112 }
2113 }
2114