3 * Daniel Nelson - 8/21/0
5 * Copyright (C) 2000 Daniel Nelson
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.
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.
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.
21 * Daniel Nelson - aluminumangel.org
25 * The block object, of which each block is one.
31 #include "BlockManager.h"
32 #include "ComboTabulator.h"
37 #include "SparkleManager.h"
42 void Block::initializeStatic ( int _x
, int _y
, int _flavor
)
54 // add ourselves to the grid
55 Grid::addBlock(x
, y
, this, GR_BLOCK
);
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);
64 void Block::initializeAwaking ( int _x
, int _y
, int _flavor
, int pop_delay
,
65 int awake_delay
, ComboTabulator
*combo
, int _pop_color
)
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
;
79 // let the combo know we're involved
80 current_combo
->incrementInvolvement();
82 // change the game state
83 Game::awaking_count
++;
85 // add ourselves to the grid
86 Grid::addBlock(x
, y
, this, GR_IMMUTABLE
);
89 void Block::timeStep ( )
91 if (state
& BS_STATIC
) {
92 // We may have to fall.
94 if (Grid::stateAt(x
, y
- 1) & GR_EMPTY
)
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
) {
104 Sound::play( GC_SOUND_BLOCK_AWAKING
, 5 );
109 if (alarm
== Game::time_step
) {
111 // change the game state
112 Game::awaking_count
--;
114 // change our state; startFalling() and eliminations check for
118 // if we're going to fall
119 if (Grid::stateAt(x
, y
- 1) & GR_EMPTY
)
120 startFalling(current_combo
, true);
124 Grid::changeState(x
, y
, this, GR_BLOCK
);
126 // register for elimination checking
127 Grid::requestEliminationCheck(*this, current_combo
);
134 // Deal with all other states.
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.
141 if (alarm
== Game::time_step
)
142 // hang alarm goes off
145 // if the hang alarm has gone off
148 // if we're at the bottom of a grid element
151 // if we're still going to fall
152 if (Grid::stateAt(x
, y
- 1) & GR_EMPTY
) {
154 // shift our grid position down to the next row
156 f_y
= GC_STEPS_PER_GRID
;
159 Grid::remove(x
, y
+ 1, this);
160 Grid::addBlock(x
, y
, this, GR_FALLING
);
168 Sound::play( GC_SOUND_BLOCK_FALLEN
, 2 );
172 Grid::changeState(x
, y
, this, GR_BLOCK
);
174 // register for elimination checking
175 Grid::requestEliminationCheck(*this, current_combo
);
177 // if the block below us is swapping, we may have to switch it's combo
179 Swapper::notifyLanding(x
, y
, *this, current_combo
);
183 // if we still are, fall
184 if (state
& BS_FALLING
)
185 f_y
-= GC_FALL_VELOCITY
;
188 } else if (state
& BS_DYING
) {
189 // The alarm has been set to go off when we're done dying.
193 // change the game state
195 Game::dying_count_2
--;
198 Grid::remove(x
, y
, this);
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
);
208 // let the combo know we're out
209 current_combo
->decrementInvolvement();
211 // generate some sparkles; pop_alarm stores the number
212 if (flavor
!= BF_WILD
)
213 SparkleManager::createBlockDeathSpark(x
, y
, flavor
, pop_alarm
);
215 SparkleManager::createBlockDeathSpark(x
, y
, X::wildFlavor(*this),
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();
225 BlockManager::deleteBlock(this);
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
;
234 void Block::startFalling ( ComboTabulator
*combo
, bool no_hang
)
236 * Although blocks will fall on their own, this must be called to syncronize
237 * and connect that falling with a elimination combo.
240 if (!(state
& BS_STATIC
)) return;
245 // set the hang alarm and update the grid
248 Grid::changeState(x
, y
, this, GR_FALLING
);
251 alarm
= Game::time_step
+ GC_HANG_DELAY
;
252 Grid::changeState(x
, y
, this, GR_HANGING
| GR_FALLING
);
255 // let the combo know we're involved
257 beginComboInvolvement(combo
);
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
);
268 void Block::startDying ( ComboTabulator
*combo
, int spark_number
)
270 // change the game state
272 Game::dying_count_2
++;
274 Sound::play( GC_SOUND_BLOCK_DYING
, spark_number
/ 3 );
277 // let the combo know we're in
278 beginComboInvolvement(combo
);
283 // set the alarm; the alarm works this way due to display needs
284 alarm
= GC_DYING_DELAY
;
287 Grid::changeState(x
, y
, this, GR_IMMUTABLE
);
289 // generate a random rotation axis
290 Random::angle(axis_x
, axis_y
);
293 void Block::startSwapping ( int direction
)
295 // change our state and swap direction
296 state
= BS_SWAPPING
| (direction
& SA_RIGHT
? BS_SWAP_DIRECTION_MASK
: 0);
299 Grid::changeState(x
, y
, this, GR_IMMUTABLE
);
302 void Block::finishSwapping ( int s_x
)
309 Grid::addBlock(x
, y
, this, GR_BLOCK
);