Fixes for datatype size on amd64.
[crack-attack.git] / src / Grid.cxx
blobf75592e5ece981e1ac4842bcdc2edb679c2ae51d
1 /*
2 * Grid.cxx
3 * Daniel Nelson - 8/23/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 * Holds a map of the play area and also deals with elimination checks.
28 #include "Grid.h"
30 #include "Game.h"
31 #include "Garbage.h"
32 #include "ComboManager.h"
33 #include "LevelLights.h"
34 #include "Swapper.h"
35 #include "BlockManager.h"
36 #include "Random.h"
38 GridElement Grid::grid[GC_PLAY_WIDTH][GC_PLAY_HEIGHT];
39 CheckRegistryElement Grid::check_registry[GC_GRID_SIZE];
40 int Grid::check_count;
41 int Grid::shatter_count;
42 int Grid::shatter_top;
43 int Grid::shatter_bottom;
44 bool Grid::gray_shatter;
45 int Grid::top_occupied_row;
46 int Grid::top_effective_row;
48 void Grid::gameStart ( )
50 check_count = 0;
52 top_occupied_row = 0;
54 for (int i = GC_PLAY_WIDTH; i--; )
55 for (int j = GC_PLAY_HEIGHT; j--; ) {
56 grid[i][j].state = GR_EMPTY;
57 grid[i][j].resident_type = GR_EMPTY;
58 grid[i][j].resident = null;
61 // Generate the initial blocks.
63 int short_collumn = Random::number(GC_PLAY_WIDTH);
65 for (int x = GC_PLAY_WIDTH; x--; ) {
67 // generate the height
68 int height = (short_collumn == x ? 2 : 7) + Random::number2(2);
69 if (height - 1 > top_occupied_row)
70 top_occupied_row = height - 1;
72 for (int y = height; --y; ) {
74 // determine the flavor
75 int flavor;
76 do {
77 flavor = Random::number(BF_NUMBER_NORMAL);
79 if (!(Grid::stateAt(x, y + 1) & GR_EMPTY)
80 && Grid::blockAt(x, y + 1).flavor == flavor) continue;
82 if (x == GC_PLAY_WIDTH - 1) break;
84 if (!(Grid::stateAt(x + 1, y) & GR_EMPTY)
85 && Grid::blockAt(x + 1, y).flavor == flavor) continue;
87 break;
88 } while (true);
90 // set the block manager block creation state
91 if (y == 2)
92 BlockManager::second_to_last_row_c[x] = flavor;
93 else if (y == 1)
94 BlockManager::last_row_c[x] = flavor;
96 // create the block
97 BlockManager::newBlock(x, y, flavor);
101 top_effective_row = top_occupied_row;
104 void Grid::timeStep ( )
106 // Process elimination check requests.
108 // loop through the request registry
109 for (int n = 0; check_count; n++)
110 if (check_registry[n].mark) {
111 check_registry[n].mark = false;
112 check_count--;
114 Block &block = BlockManager::blockStore[n];
116 // insure that the block is still static
117 if (!(block.state & BS_STATIC)) continue;
119 // use the block's combo, if it has one
120 handleEliminationCheckRequest(block,
121 block.current_combo ? block.current_combo : check_registry[n].combo);
124 // Update top_occupied_row.
125 top_occupied_row++;
126 bool flag = true;
127 do {
128 top_occupied_row--;
129 for (int x = GC_PLAY_WIDTH; x--; )
130 if (!(stateAt(x, top_occupied_row) & GR_EMPTY)) {
131 flag = false;
132 break;
134 } while (flag);
136 // Update top_effective_row.
137 int o = top_effective_row;
138 top_effective_row++;
139 flag = true;
140 do {
141 top_effective_row--;
142 for (int x = GC_PLAY_WIDTH; x--; )
143 if (!(residentTypeAt(x, top_effective_row) & GR_EMPTY)
144 && !((residentTypeAt(x, top_effective_row) & GR_GARBAGE)
145 && garbageAt(x, top_effective_row).initial_fall)) {
146 flag = false;
147 break;
149 } while (flag);
150 if (top_effective_row < o)
151 LevelLights::levelLower(top_effective_row);
154 void Grid::handleEliminationCheckRequest ( Block &block, ComboTabulator *combo )
156 int x = block.x;
157 int y = block.y;
159 // Look in four directions for matching lines.
161 int l = x;
162 while (l > 0) {
163 if (!(stateAt(l - 1, y) & GR_BLOCK)) break;
164 if (!matchAt(l - 1, y, block)) break;
165 l--;
168 int r = x + 1;
169 while (r < GC_PLAY_WIDTH) {
170 if (!(stateAt(r, y) & GR_BLOCK)) break;
171 if (!matchAt(r, y, block)) break;
172 r++;
175 int b = y;
176 while (b > 1) {
177 if (!(stateAt(x, b - 1) & GR_BLOCK)) break;
178 if (!matchAt(x, b - 1, block)) break;
179 b--;
182 int t = y + 1;
183 while (t < GC_PLAY_HEIGHT) {
184 if (!(stateAt(x, t) & GR_BLOCK)) break;
185 if (!matchAt(x, t, block)) break;
186 t++;
189 int w = r - l;
190 int h = t - b;
192 int magnitude = 0;
193 int pattern = 0;
194 if (w >= GC_MIN_PATTERN_LENGTH) {
195 pattern |= PT_HORIZONTAL;
196 magnitude += w;
198 if (h >= GC_MIN_PATTERN_LENGTH) {
199 pattern |= PT_VERTICAL;
200 magnitude += h;
203 // if no pattern found
204 if (pattern == 0) {
205 block.endComboInvolvement(combo);
206 return;
209 // create a combo for the elimination
210 if (!combo)
211 combo = &ComboManager::newComboTabulator();
213 // if pattern matches both directions
214 if (pattern == (PT_HORIZONTAL | PT_VERTICAL)) magnitude--;
216 // Kill the pattern's blocks and look for touching garbage.
218 shatter_count = 0;
219 shatter_top = 0;
220 shatter_bottom = GC_PLAY_HEIGHT;
222 // we need to know if it's a gray shatter in order to know if black
223 // garbage should shatter
224 if (!BlockManager::isColorlessFlavor(block.flavor))
225 gray_shatter = false;
226 else
227 gray_shatter = true;
229 ComboManager::specialBlockTally(*combo, block);
230 block.startDying(combo, magnitude);
232 if (pattern & PT_HORIZONTAL) {
233 // kill the pattern's blocks
234 for (int k_x = l; k_x < r; k_x++)
235 if (k_x != x) {
236 ComboManager::specialBlockTally(*combo, blockAt(k_x, y));
237 blockAt(k_x, y).startDying(combo, magnitude);
240 // look for garbage to the left and below the pattern
241 if (l > 0)
242 shatterGarbage(l - 1, y);
243 if (y > 1)
244 for (int k_x = l; k_x < r; k_x++)
245 shatterGarbage(k_x, y - 1);
247 // look for garbage to the right and above the pattern
248 if (r < GC_PLAY_WIDTH)
249 shatterGarbage(r, y);
250 if (y < GC_PLAY_HEIGHT - 1)
251 for (int k_x = l; k_x < r; k_x++)
252 shatterGarbage(k_x, y + 1);
255 if (pattern & PT_VERTICAL) {
256 // kill the pattern's blocks
257 for (int k_y = b; k_y < t; k_y++)
258 if (k_y != y) {
259 ComboManager::specialBlockTally(*combo, blockAt(x, k_y));
260 blockAt(x, k_y).startDying(combo, magnitude);
263 // look for garbage to the left and below the pattern
264 if (b > 1)
265 shatterGarbage(x, b - 1);
266 if (x > 0)
267 for (int k_y = b; k_y < t; k_y++)
268 shatterGarbage(x - 1, k_y);
270 // look for garbage to the right and above the pattern
271 if (t < GC_PLAY_HEIGHT)
272 shatterGarbage(x, t);
273 if (x < GC_PLAY_WIDTH - 1)
274 for (int k_y = b; k_y < t; k_y++)
275 shatterGarbage(x + 1, k_y);
278 // The grid now contains shattered elements. We now traverse the grid and
279 // syncronize the shattering garbages' timers. The garbage itself deals with
280 // the creation of blocks and such, as it's behavior may varry.
282 // delay until the new blocks fall
283 int awaken_delay = GC_INITIAL_POP_DELAY + GC_FINAL_POP_DELAY
284 + GC_INTERNAL_POP_DELAY * (shatter_count - 1);
286 // delay until the next block pops
287 int pop_delay = GC_INITIAL_POP_DELAY;
289 // traverse the shatter area; s_x and pop_delay will be advanced during
290 // startShattering()
291 for (int s_y = shatter_bottom; s_y < shatter_top; s_y++)
292 for (int s_x = 0; s_x < GC_PLAY_WIDTH; )
293 if (stateAt(s_x, s_y) & GR_SHATTERING)
294 garbageAt(s_x, s_y).startShattering(s_x, s_y, pop_delay, awaken_delay,
295 combo);
296 else
297 s_x++;
299 // notify the combo of the pattern match
300 combo->reportElimination(magnitude, block);
303 void Grid::shatterGarbage_inline_split_ ( int x, int y, Garbage *due_to )
305 Garbage &garbage = garbageAt(x, y);
307 // ask the garbage about shattering; we have to do black ourselves
308 if (!garbage.considerShattering(due_to)) return;
309 if (garbage.flavor == GF_BLACK && !gray_shatter) return;
311 // keep track of the bounds on the shattered area
312 if (y + garbage.height > shatter_top)
313 shatter_top = y + garbage.height;
314 if (y - garbage.height < shatter_bottom)
315 shatter_bottom = y - garbage.height;
317 shatter_count += garbage.width * garbage.height;
319 // mark the garbage grid locations as shattered
320 for (int h = garbage.height; h--; )
321 for (int w = garbage.width; w--; )
322 grid[garbage.x + w][garbage.y + h].state = GR_SHATTERING;
324 // look for connecting garbage recursively
325 if (garbage.x > 0)
326 for (int h = garbage.height; h--; )
327 shatterGarbage(garbage.x - 1, garbage.y + h, &garbage);
328 if (garbage.x + garbage.width < GC_PLAY_WIDTH)
329 for (int h = garbage.height; h--; )
330 shatterGarbage(garbage.x + garbage.width, garbage.y + h, &garbage);
331 if (garbage.y > 1)
332 for (int w = garbage.width; w--; )
333 shatterGarbage(garbage.x + w, garbage.y - 1, &garbage);
334 if (garbage.y + garbage.height < GC_PLAY_HEIGHT)
335 for (int w = garbage.width; w--; )
336 shatterGarbage(garbage.x + w, garbage.y + garbage.height, &garbage);
339 bool Grid::shiftGridUp ( )
341 if (top_occupied_row == GC_PLAY_HEIGHT - 1) return false;
343 // shift the grid
344 for (int y = top_occupied_row + 1; y--; )
345 for (int x = GC_PLAY_WIDTH; x--; )
346 grid[x][y + 1] = grid[x][y];
348 #ifndef NDEBUG
349 // otherwise the assert() will tag us
350 for (int x = GC_PLAY_WIDTH; x--; )
351 grid[x][0].state = GR_EMPTY;
352 #endif
354 top_occupied_row++;
355 top_effective_row++;
356 LevelLights::levelRaise(top_effective_row);
358 // shift blocks and garbage up with the grid
359 BlockManager::shiftUp();
360 GarbageManager::shiftUp();
362 // shift swapper up
363 Swapper::shiftUp();
365 return true;