Holy god the windows build better fucking work
[crack-attack.git] / src / Garbage.cxx
blob25eedca5622238e2e9e19cb8e18a47a336579ca6
1 /*
2 * Garbage.cxx
3 * Daniel Nelson - 8/21/0
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.
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
22 * 174 W. 18th Ave.
23 * Columbus, OH 43210
25 * The garbage blocks is what this details.
28 #include "Game.h"
29 #include "Grid.h"
30 #include "Garbage.h"
31 #include "BlockManager.h"
32 #include "GarbageManager.h"
33 #include "Displayer.h"
34 #include "Spring.h"
35 #include "Random.h"
36 #include "X.h"
38 #include <cassert>
40 using namespace std;
42 void Garbage::initializeStatic ( int _x, int _y, int _height, int _width,
43 int _flavor )
45 x = _x;
46 y = _y;
47 height = _height;
48 width = _width;
49 flavor = _flavor;
50 f_y = 0;
52 state = GS_STATIC;
53 alarm = 0;
54 pop_alarm = 0;
55 sections_popped = 0;
56 initial_fall = false;
57 awaking_combo = null;
59 // add ourselves to the grid
60 for (int h = height; h--; )
61 for (int w = width; w--; )
62 Grid::addGarbage(x + w, y + h, this, GR_GARBAGE);
65 void Garbage::initializeFalling ( int _x, int _y, int _height, int _width,
66 int _flavor )
68 //MESSAGE("Entering initialize Falling _x " << _x << " _y " << _y << " _height " << _height << " _width " << _width);
69 x = _x;
70 y = _y;
71 height = _height;
72 width = _width;
73 flavor = _flavor;
74 f_y = 0;
76 //MESSAGE("Second block start");
77 state = GS_FALLING;
78 alarm = Game::time_step + GC_HANG_DELAY;
79 pop_alarm = 0;
80 sections_popped = 0;
81 initial_fall = true;
82 awaking_combo = NULL;
84 //MESSAGE("Add us to the grid height: " << height << " width " << width);
85 // add ourselves to the grid
86 for (int h = height; h--; )
87 for (int w = width; w--; )
88 Grid::addGarbage(x + w, y + h, this, GR_FALLING);
89 //MESSAGE("Out and alive!");
92 void Garbage::initializeAwaking ( int _x, int _y, int _height, int pop_delay,
93 int awake_delay, ComboTabulator *combo, int _pop_color )
95 x = _x;
96 y = _y;
97 height = _height;
98 width = GC_PLAY_WIDTH;
99 flavor = GF_NORMAL;
100 f_y = 0;
102 state = GS_AWAKING;
103 alarm = Game::time_step + awake_delay;
104 pop_alarm = Game::time_step + pop_delay;
105 sections_popped = 0;
106 pop_direction = BlockManager::generatePopDirection(height * width);
107 pop_color = _pop_color;
108 initial_fall = false;
110 // Although garbage does not participate in combos as such, this is needed
111 // so that the garbage can pass the combo on to blocks above it if it falls
112 // when it awakes. Note that this never happens because garbage currently
113 // only awakes as the second row of a three row or taller shattering
114 // garbage. Thus, all blocks above it will be BS_AWAKING when the garbage
115 // calls their startFalling().
116 awaking_combo = combo;
118 // change the game state
119 Game::awaking_count++;
121 // add ourselves to the grid
122 for (int h = height; h--; )
123 for (int w = width; w--; )
124 Grid::addGarbage(x + w, y + h, this, GR_IMMUTABLE);
127 void Garbage::timeStep ( int &l_x, int &l_y )
129 // We must advance l_x and l_y based on our size.
131 // normal garbage dimensions
132 if (height == 1 || width == GC_PLAY_WIDTH) {
133 l_y += height - 1;
134 l_x += width - 1;
136 // special garbage dimensions
137 } else {
138 l_x += width - 1;
139 // if it's not our top row, don't time step
140 if (l_y != y + height - 1) return;
143 // First, the states that may change do to falling.
145 if (state & GS_STATIC) {
146 // We may have to fall.
148 bool flag = true;
149 for (int w = width; w--; )
150 if (!(Grid::stateAt(x + w, y - 1) & GR_EMPTY)) {
151 flag = false;
152 break;
154 if (flag)
155 startFalling();
157 } else if (state & GS_AWAKING) {
158 // The alarm has been set to go off when we're done awaking. When the pop
159 // alarm goes off, we have to pop one more of our sections. If that's the
160 // last section, we don't reset the pop timer. In about a million places,
161 // we assume that awaking garbage is as wide as the grid.
163 if (sections_popped < width * height)
164 if (pop_alarm == Game::time_step) {
165 sections_popped++;
166 if (sections_popped < width * height) {
167 if (pop_direction & (1 << 3))
168 pop_direction = (1 << 0);
169 else
170 pop_direction <<= 1;
171 pop_alarm = Game::time_step + GC_INTERNAL_POP_DELAY;
175 if (alarm == Game::time_step) {
177 // change the game state
178 Game::awaking_count--;
180 // if we're going to fall
181 bool flag = true;
182 for (int w = width; w--; )
183 if (!(Grid::stateAt(x + w, y - 1) & GR_EMPTY)) {
184 flag = false;
185 break;
187 if (flag)
189 startFalling(awaking_combo, true, true);
191 else {
193 // change our state
194 state = GS_STATIC;
196 // update the grid
197 for (int h = height; h--; )
198 for (int w = width; w--; )
199 Grid::changeState(x + w, y + h, this, GR_GARBAGE);
204 // Deal with all other states.
206 if (state & GS_FALLING) {
207 // We are assured that the timeStep() of any blocks below us has already
208 // been called. Note that to start a fall, all we have to do is set our
209 // state to GS_FALLING. This code will deal with the rest.
211 if (alarm == Game::time_step)
212 // hang alarm goes off
213 alarm = 0;
215 // if the hang alarm has gone off
216 if (alarm == 0) {
218 // if we're at the bottom of a grid element
219 if (f_y == 0) {
221 // if we're still going to fall
222 bool flag = true;
223 for (int w = width; w--; )
224 if (!(Grid::stateAt(x + w, y - 1) & GR_EMPTY)) {
225 flag = false;
226 break;
228 if (flag) {
230 // shift our grid position down to the next row
231 y--;
232 f_y = GC_STEPS_PER_GRID;
234 // update the grid
235 for (int h = height; h--; )
236 for (int w = width; w--; )
237 Grid::remove(x + w, y + h + 1, this);
238 for (int h = height; h--; )
239 for (int w = width; w--; )
240 Grid::addGarbage(x + w, y + h, this, GR_FALLING);
242 // if we've landed
243 } else {
245 // change our state
246 state = BS_STATIC;
248 // if this is the end of our initial fall
249 if (initial_fall) {
250 initial_fall = false;
251 if (!(MetaState::mode & CM_REALLY_LOW_GRAPHICS))
252 Spring::notifyImpact(height, width);
253 Grid::notifyImpact(y, height);
255 X::notifyImpact(*this);
258 // update the grid
259 for (int h = height; h--; )
260 for (int w = width; w--; )
261 Grid::changeState(x + w, y + h, this, GR_GARBAGE);
265 // if we still are, fall
266 if (state & GS_FALLING)
267 f_y -= GC_FALL_VELOCITY;
272 void Garbage::startFalling ( ComboTabulator *combo, bool no_hang,
273 bool self_call )
275 * While garbage doesn't have a current combo and doesn't have to deal with
276 * such things, it does need to pass combo falls along to it's upward neighbors.
279 // if we're calling our own startFalling() this has already been checked
280 if (!self_call) {
282 if (!(state & BS_STATIC)) return;
284 // if we're not going to fall
285 for (int w = width; w--; )
286 if (!(Grid::stateAt(x + w, y - 1) & (GR_EMPTY | GR_FALLING)))
287 return;
290 // change our state
291 state = GS_FALLING;
293 // set the hang alarm and update the grid
294 if (no_hang) {
295 alarm = 0;
296 for (int h = height; h--; )
297 for (int w = width; w--; )
298 Grid::changeState(x + w, y + h, this, GR_FALLING);
300 } else {
301 alarm = Game::time_step + GC_HANG_DELAY;
302 for (int h = height; h--; )
303 for (int w = width; w--; )
304 Grid::changeState(x + w, y + h, this, GR_HANGING | GR_FALLING);
307 // tell our upward neighbors to start a combo fall
308 if (y + height < GC_PLAY_HEIGHT) {
309 for (int w = width; w--; ) {
310 if (Grid::stateAt(x + w, y + height) & GR_BLOCK)
311 Grid::blockAt(x + w, y + height).startFalling(combo, no_hang);
312 else if (Grid::stateAt(x + w, y + height) & GR_GARBAGE)
313 Grid::garbageAt(x + w, y + height).startFalling(combo, no_hang);
318 void Garbage::startShattering ( int &s_x, int s_y, int &pop_delay,
319 int awake_delay, ComboTabulator *combo )
321 * This is called for each row we occupy, with s_x equal to our left most
322 * position and s_y indicating which row the call is for. We must convert
323 * ourselves to blocks or new garbage along that row. Additionally, we must
324 * advance s_x to our right side as well as pop_delay and delete ourselves if
325 * this is our top row.
327 * Note that we may want to change the block/garbage conversion behavior.
330 #ifndef NDEBUG
331 // otherwise assert will bite us
332 for (int w = 0; w < width; w++)
333 Grid::remove(s_x + w, s_y, this);
334 #endif
336 // if it's an even row, perhaps shatter into new garbage
337 if ((width == GC_PLAY_WIDTH && ((s_y - y) & (1 << 0))
338 && Random::chanceIn(GC_GARBAGE_TO_GARBAGE_SHATTER))
339 || flavor == GF_SHATTER_TO_NORMAL_GARBAGE) {
340 GarbageManager::newAwakingGarbage(s_x, s_y, 1, pop_delay, awake_delay,
341 combo, flavor);
342 s_x += GC_PLAY_WIDTH;
343 pop_delay += GC_PLAY_WIDTH * GC_INTERNAL_POP_DELAY;
345 // otherwise, shatter into blocks
346 } else
347 for (int w = 0; w < width; w++) {
348 BlockManager::newAwakingBlock(s_x, s_y, pop_delay, awake_delay, combo,
349 flavor);
350 s_x++;
351 pop_delay += GC_INTERNAL_POP_DELAY;
354 // If it's our top row, enter shatter state; we are no longer on the grid
355 // but we stay around a bit to animate our shattering; since we're not in the
356 // grid, our time step will never be called; that's OK! We'll be deleted by
357 // the display code, sloppy but fastest.
358 if (s_y + 1 == y + height) {
360 // change our state
361 state = GS_SHATTERING;
363 // set the deletion alarm
364 alarm = Game::time_step + DC_SHATTER_TIME;
366 // notify extreme effects of our demise
367 X::notifyShatter(*this);