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 garbage blocks is what this details.
31 #include "BlockManager.h"
32 #include "GarbageManager.h"
33 #include "Displayer.h"
40 void Garbage::initializeStatic ( int _x
, int _y
, int _height
, int _width
,
57 // add ourselves to the grid
58 for (int h
= height
; h
--; )
59 for (int w
= width
; w
--; )
60 Grid::addGarbage(x
+ w
, y
+ h
, this, GR_GARBAGE
);
63 void Garbage::initializeFalling ( int _x
, int _y
, int _height
, int _width
,
66 //MESSAGE("Entering initialize Falling _x " << _x << " _y " << _y << " _height " << _height << " _width " << _width);
74 //MESSAGE("Second block start");
76 alarm
= Game::time_step
+ GC_HANG_DELAY
;
82 //MESSAGE("Add us to the grid height: " << height << " width " << width);
83 // add ourselves to the grid
84 for (int h
= height
; h
--; )
85 for (int w
= width
; w
--; )
86 Grid::addGarbage(x
+ w
, y
+ h
, this, GR_FALLING
);
87 //MESSAGE("Out and alive!");
90 void Garbage::initializeAwaking ( int _x
, int _y
, int _height
, int pop_delay
,
91 int awake_delay
, ComboTabulator
*combo
, int _pop_color
)
96 width
= GC_PLAY_WIDTH
;
101 alarm
= Game::time_step
+ awake_delay
;
102 pop_alarm
= Game::time_step
+ pop_delay
;
104 pop_direction
= BlockManager::generatePopDirection(height
* width
);
105 pop_color
= _pop_color
;
106 initial_fall
= false;
108 // Although garbage does not participate in combos as such, this is needed
109 // so that the garbage can pass the combo on to blocks above it if it falls
110 // when it awakes. Note that this never happens because garbage currently
111 // only awakes as the second row of a three row or taller shattering
112 // garbage. Thus, all blocks above it will be BS_AWAKING when the garbage
113 // calls their startFalling().
114 awaking_combo
= combo
;
116 // change the game state
117 Game::awaking_count
++;
119 // add ourselves to the grid
120 for (int h
= height
; h
--; )
121 for (int w
= width
; w
--; )
122 Grid::addGarbage(x
+ w
, y
+ h
, this, GR_IMMUTABLE
);
125 void Garbage::timeStep ( int &l_x
, int &l_y
)
127 // We must advance l_x and l_y based on our size.
129 // normal garbage dimensions
130 if (height
== 1 || width
== GC_PLAY_WIDTH
) {
134 // special garbage dimensions
137 // if it's not our top row, don't time step
138 if (l_y
!= y
+ height
- 1) return;
141 // First, the states that may change do to falling.
143 if (state
& GS_STATIC
) {
144 // We may have to fall.
147 for (int w
= width
; w
--; )
148 if (!(Grid::stateAt(x
+ w
, y
- 1) & GR_EMPTY
)) {
155 } else if (state
& GS_AWAKING
) {
156 // The alarm has been set to go off when we're done awaking. When the pop
157 // alarm goes off, we have to pop one more of our sections. If that's the
158 // last section, we don't reset the pop timer. In about a million places,
159 // we assume that awaking garbage is as wide as the grid.
161 if (sections_popped
< width
* height
)
162 if (pop_alarm
== Game::time_step
) {
164 if (sections_popped
< width
* height
) {
165 if (pop_direction
& (1 << 3))
166 pop_direction
= (1 << 0);
169 pop_alarm
= Game::time_step
+ GC_INTERNAL_POP_DELAY
;
173 if (alarm
== Game::time_step
) {
175 // change the game state
176 Game::awaking_count
--;
178 // if we're going to fall
180 for (int w
= width
; w
--; )
181 if (!(Grid::stateAt(x
+ w
, y
- 1) & GR_EMPTY
)) {
187 startFalling(awaking_combo
, true, true);
195 for (int h
= height
; h
--; )
196 for (int w
= width
; w
--; )
197 Grid::changeState(x
+ w
, y
+ h
, this, GR_GARBAGE
);
202 // Deal with all other states.
204 if (state
& GS_FALLING
) {
205 // We are assured that the timeStep() of any blocks below us has already
206 // been called. Note that to start a fall, all we have to do is set our
207 // state to GS_FALLING. This code will deal with the rest.
209 if (alarm
== Game::time_step
)
210 // hang alarm goes off
213 // if the hang alarm has gone off
216 // if we're at the bottom of a grid element
219 // if we're still going to fall
221 for (int w
= width
; w
--; )
222 if (!(Grid::stateAt(x
+ w
, y
- 1) & GR_EMPTY
)) {
228 // shift our grid position down to the next row
230 f_y
= GC_STEPS_PER_GRID
;
233 for (int h
= height
; h
--; )
234 for (int w
= width
; w
--; )
235 Grid::remove(x
+ w
, y
+ h
+ 1, this);
236 for (int h
= height
; h
--; )
237 for (int w
= width
; w
--; )
238 Grid::addGarbage(x
+ w
, y
+ h
, this, GR_FALLING
);
246 // if this is the end of our initial fall
248 initial_fall
= false;
249 if (!(MetaState::mode
& CM_REALLY_LOW_GRAPHICS
))
250 Spring::notifyImpact(height
, width
);
251 Grid::notifyImpact(y
, height
);
253 X::notifyImpact(*this);
257 for (int h
= height
; h
--; )
258 for (int w
= width
; w
--; )
259 Grid::changeState(x
+ w
, y
+ h
, this, GR_GARBAGE
);
263 // if we still are, fall
264 if (state
& GS_FALLING
)
265 f_y
-= GC_FALL_VELOCITY
;
270 void Garbage::startFalling ( ComboTabulator
*combo
, bool no_hang
,
273 * While garbage doesn't have a current combo and doesn't have to deal with
274 * such things, it does need to pass combo falls along to it's upward neighbors.
277 // if we're calling our own startFalling() this has already been checked
280 if (!(state
& BS_STATIC
)) return;
282 // if we're not going to fall
283 for (int w
= width
; w
--; )
284 if (!(Grid::stateAt(x
+ w
, y
- 1) & (GR_EMPTY
| GR_FALLING
)))
291 // set the hang alarm and update the grid
294 for (int h
= height
; h
--; )
295 for (int w
= width
; w
--; )
296 Grid::changeState(x
+ w
, y
+ h
, this, GR_FALLING
);
299 alarm
= Game::time_step
+ GC_HANG_DELAY
;
300 for (int h
= height
; h
--; )
301 for (int w
= width
; w
--; )
302 Grid::changeState(x
+ w
, y
+ h
, this, GR_HANGING
| GR_FALLING
);
305 // tell our upward neighbors to start a combo fall
306 if (y
+ height
< GC_PLAY_HEIGHT
) {
307 for (int w
= width
; w
--; ) {
308 if (Grid::stateAt(x
+ w
, y
+ height
) & GR_BLOCK
)
309 Grid::blockAt(x
+ w
, y
+ height
).startFalling(combo
, no_hang
);
310 else if (Grid::stateAt(x
+ w
, y
+ height
) & GR_GARBAGE
)
311 Grid::garbageAt(x
+ w
, y
+ height
).startFalling(combo
, no_hang
);
316 void Garbage::startShattering ( int &s_x
, int s_y
, int &pop_delay
,
317 int awake_delay
, ComboTabulator
*combo
)
319 * This is called for each row we occupy, with s_x equal to our left most
320 * position and s_y indicating which row the call is for. We must convert
321 * ourselves to blocks or new garbage along that row. Additionally, we must
322 * advance s_x to our right side as well as pop_delay and delete ourselves if
323 * this is our top row.
325 * Note that we may want to change the block/garbage conversion behavior.
329 // otherwise assert will bite us
330 for (int w
= 0; w
< width
; w
++)
331 Grid::remove(s_x
+ w
, s_y
, this);
334 // if it's an even row, perhaps shatter into new garbage
335 if ((width
== GC_PLAY_WIDTH
&& ((s_y
- y
) & (1 << 0))
336 && Random::chanceIn(GC_GARBAGE_TO_GARBAGE_SHATTER
))
337 || flavor
== GF_SHATTER_TO_NORMAL_GARBAGE
) {
338 GarbageManager::newAwakingGarbage(s_x
, s_y
, 1, pop_delay
, awake_delay
,
340 s_x
+= GC_PLAY_WIDTH
;
341 pop_delay
+= GC_PLAY_WIDTH
* GC_INTERNAL_POP_DELAY
;
343 // otherwise, shatter into blocks
345 for (int w
= 0; w
< width
; w
++) {
346 BlockManager::newAwakingBlock(s_x
, s_y
, pop_delay
, awake_delay
, combo
,
349 pop_delay
+= GC_INTERNAL_POP_DELAY
;
352 // If it's our top row, enter shatter state; we are no longer on the grid
353 // but we stay around a bit to animate our shattering; since we're not in the
354 // grid, our time step will never be called; that's OK! We'll be deleted by
355 // the display code, sloppy but fastest.
356 if (s_y
+ 1 == y
+ height
) {
359 state
= GS_SHATTERING
;
361 // set the deletion alarm
362 alarm
= Game::time_step
+ DC_SHATTER_TIME
;
364 // notify extreme effects of our demise
365 X::notifyShatter(*this);