1 /*
2 * Block.cxx
3 * Daniel Nelson - 8/21/0
4 *
5 * Copyright (C) 2000 Daniel Nelson
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * Daniel Nelson - aluminumangel.org
22 * 174 W. 18th Ave.
23 * Columbus, OH 43210
24 *
25 * The block object, of which each block is one.
26 */
27
28 #include "Game.h"
29 #include "Block.h"
30 #include "BlockManager.h"
31 #include "ComboTabulator.h"
32 #include "Garbage.h"
33 #include "Grid.h"
34 #include "Random.h"
35 #include "Swapper.h"
36 #include "SparkleManager.h"
37 #include "X.h"
38 #include "Sound.h"
39
40 using namespace std;
41
initializeStatic(int _x,int _y,int _flavor)42 void Block::initializeStatic ( int _x, int _y, int _flavor )
43 {
44 x = _x;
45 y = _y;
46 flavor = _flavor;
47 f_y = 0;
48
49 state = BS_STATIC;
50 alarm = 0;
51 // pop_alarm = 0;
52 current_combo = null;
53
54 // add ourselves to the grid
55 Grid::addBlock(x, y, this, GR_BLOCK);
56
57 // if we're wild, the extreme code needs to know
58 if (flavor == BF_WILD)
59 X::activateWild(*this);
60 else if (BlockManager::isSpecialColorFlavor(flavor))
61 X::activateSpecialColor(*this);
62 }
63
initializeAwaking(int _x,int _y,int _flavor,int pop_delay,int awake_delay,ComboTabulator * combo,int _pop_color)64 void Block::initializeAwaking ( int _x, int _y, int _flavor, int pop_delay,
65 int awake_delay, ComboTabulator *combo, int _pop_color )
66 {
67 x = _x;
68 y = _y;
69 flavor = _flavor;
70 f_y = 0;
71
72 state = BS_AWAKING;
73 alarm = Game::time_step + awake_delay;
74 pop_alarm = Game::time_step + pop_delay;
75 pop_direction = BlockManager::generatePopDirection();
76 pop_color = _pop_color;
77 current_combo = combo;
78
79 // let the combo know we're involved
80 current_combo->incrementInvolvement();
81
82 // change the game state
83 Game::awaking_count++;
84
85 // add ourselves to the grid
86 Grid::addBlock(x, y, this, GR_IMMUTABLE);
87 }
88
timeStep()89 void Block::timeStep ( )
90 {
91 if (state & BS_STATIC) {
92 // We may have to fall.
93
94 if (Grid::stateAt(x, y - 1) & GR_EMPTY)
95 startFalling();
96 else
97 return;
98
99 } else if (state & BS_AWAKING) {
100 // The alarm has been set to go off when we're done awaking. When the
101 // pop alarm goes off, we only switch our appearence.
102 if (pop_alarm == Game::time_step) {
103 #ifdef AUDIO_ENABLED
104 Sound::play( GC_SOUND_BLOCK_AWAKING, 5 );
105 #endif
106 pop_alarm = 0;
107 }
108
109 if (alarm == Game::time_step) {
110
111 // change the game state
112 Game::awaking_count--;
113
114 // change our state; startFalling() and eliminations check for
115 // BS_STATIC state
116 state = BS_STATIC;
117
118 // if we're going to fall
119 if (Grid::stateAt(x, y - 1) & GR_EMPTY)
120 startFalling(current_combo, true);
121
122 else {
123 // update the grid
124 Grid::changeState(x, y, this, GR_BLOCK);
125
126 // register for elimination checking
127 Grid::requestEliminationCheck(*this, current_combo);
128 }
129
130 } else
131 return;
132 }
133
134 // Deal with all other states.
135
136 if (state & BS_FALLING) {
137 // We are assured that the timeStep() of any blocks below us has already
138 // been called. Note that to start a fall, all we have to do is set our
139 // state to BS_FALLING. This code will deal with the rest.
140
141 if (alarm == Game::time_step)
142 // hang alarm goes off
143 alarm = 0;
144
145 // if the hang alarm has gone off
146 if (alarm == 0) {
147
148 // if we're at the bottom of a grid element
149 if (f_y == 0) {
150
151 // if we're still going to fall
152 if (Grid::stateAt(x, y - 1) & GR_EMPTY) {
153
154 // shift our grid position down to the next row
155 y--;
156 f_y = GC_STEPS_PER_GRID;
157
158 // update the grid
159 Grid::remove(x, y + 1, this);
160 Grid::addBlock(x, y, this, GR_FALLING);
161
162 // if we've landed
163 } else {
164
165 // change our state
166 state = BS_STATIC;
167 #ifdef AUDIO_ENABLED
168 Sound::play( GC_SOUND_BLOCK_FALLEN, 2 );
169 #endif
170
171 // update the grid
172 Grid::changeState(x, y, this, GR_BLOCK);
173
174 // register for elimination checking
175 Grid::requestEliminationCheck(*this, current_combo);
176
177 // if the block below us is swapping, we may have to switch it's combo
178 if (current_combo)
179 Swapper::notifyLanding(x, y, *this, current_combo);
180 }
181 }
182
183 // if we still are, fall
184 if (state & BS_FALLING)
185 f_y -= GC_FALL_VELOCITY;
186 }
187
188 } else if (state & BS_DYING) {
189 // The alarm has been set to go off when we're done dying.
190
191 if (--alarm == 0) {
192
193 // change the game state
194 Game::dying_count--;
195 Game::dying_count_2--;
196
197 // update the grid
198 Grid::remove(x, y, this);
199
200 // tell our upward neighbor to start a combo fall
201 if (y < GC_PLAY_HEIGHT - 1) {
202 if (Grid::stateAt(x, y + 1) & GR_BLOCK)
203 Grid::blockAt(x, y + 1).startFalling(current_combo);
204 else if (Grid::stateAt(x, y + 1) & GR_GARBAGE)
205 Grid::garbageAt(x, y + 1).startFalling(current_combo);
206 }
207
208 // let the combo know we're out
209 current_combo->decrementInvolvement();
210
211 // generate some sparkles; pop_alarm stores the number
212 if (flavor != BF_WILD)
213 SparkleManager::createBlockDeathSpark(x, y, flavor, pop_alarm);
214 else
215 SparkleManager::createBlockDeathSpark(x, y, X::wildFlavor(*this),
216 pop_alarm);
217
218 // if we're wild, the extreme code needs to know
219 if (flavor == BF_WILD)
220 X::deactivateWild(*this);
221 else if (BlockManager::isSpecialColorFlavor(flavor))
222 X::deactivateSpecialColor();
223
224 // delete ourselves
225 BlockManager::deleteBlock(this);
226
227 // if we just started dying
228 } else if (alarm == GC_DYING_DELAY - 1)
229 // grab the elimination magnitude from our combo
230 pop_alarm = current_combo->latest_magnitude;
231 }
232 }
233
startFalling(ComboTabulator * combo,bool no_hang)234 void Block::startFalling ( ComboTabulator *combo, bool no_hang )
235 /*
236 * Although blocks will fall on their own, this must be called to syncronize
237 * and connect that falling with a elimination combo.
238 */
239 {
240 if (!(state & BS_STATIC)) return;
241
242 // change our state
243 state = BS_FALLING;
244
245 // set the hang alarm and update the grid
246 if (no_hang) {
247 alarm = 0;
248 Grid::changeState(x, y, this, GR_FALLING);
249
250 } else {
251 alarm = Game::time_step + GC_HANG_DELAY;
252 Grid::changeState(x, y, this, GR_HANGING | GR_FALLING);
253 }
254
255 // let the combo know we're involved
256 if (combo)
257 beginComboInvolvement(combo);
258
259 // tell our upward neighbor to start a combo fall
260 if (y < GC_PLAY_HEIGHT - 1) {
261 if (Grid::stateAt(x, y + 1) & GR_BLOCK)
262 Grid::blockAt(x, y + 1).startFalling(current_combo, no_hang);
263 else if (Grid::stateAt(x, y + 1) & GR_GARBAGE)
264 Grid::garbageAt(x, y + 1).startFalling(current_combo, no_hang);
265 }
266 }
267
startDying(ComboTabulator * combo,int spark_number)268 void Block::startDying ( ComboTabulator *combo, int spark_number )
269 {
270 // change the game state
271 Game::dying_count++;
272 Game::dying_count_2++;
273 #ifdef AUDIO_ENABLED
274 Sound::play( GC_SOUND_BLOCK_DYING, spark_number / 3 );
275 #endif
276
277 // let the combo know we're in
278 beginComboInvolvement(combo);
279
280 // change our state
281 state = BS_DYING;
282
283 // set the alarm; the alarm works this way due to display needs
284 alarm = GC_DYING_DELAY;
285
286 // update the grid
287 Grid::changeState(x, y, this, GR_IMMUTABLE);
288
289 // generate a random rotation axis
290 Random::angle(axis_x, axis_y);
291 }
292
startSwapping(int direction)293 void Block::startSwapping ( int direction )
294 {
295 // change our state and swap direction
296 state = BS_SWAPPING | (direction & SA_RIGHT ? BS_SWAP_DIRECTION_MASK : 0);
297
298 // update the grid
299 Grid::changeState(x, y, this, GR_IMMUTABLE);
300 }
301
finishSwapping(int s_x)302 void Block::finishSwapping ( int s_x )
303 {
304 // change our state
305 state = BS_STATIC;
306 x = s_x;
307
308 // update the grid
309 Grid::addBlock(x, y, this, GR_BLOCK);
310 }
311
312