3 * Daniel Nelson - 8/23/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 * Holds a map of the play area and also deals with elimination checks.
32 #include "ComboManager.h"
33 #include "LevelLights.h"
35 #include "BlockManager.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 ( )
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
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;
90 // set the block manager block creation state
92 BlockManager::second_to_last_row_c
[x
] = flavor
;
94 BlockManager::last_row_c
[x
] = flavor
;
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;
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.
129 for (int x
= GC_PLAY_WIDTH
; x
--; )
130 if (!(stateAt(x
, top_occupied_row
) & GR_EMPTY
)) {
136 // Update top_effective_row.
137 int o
= 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
)) {
150 if (top_effective_row
< o
)
151 LevelLights::levelLower(top_effective_row
);
154 void Grid::handleEliminationCheckRequest ( Block
&block
, ComboTabulator
*combo
)
159 // Look in four directions for matching lines.
163 if (!(stateAt(l
- 1, y
) & GR_BLOCK
)) break;
164 if (!matchAt(l
- 1, y
, block
)) break;
169 while (r
< GC_PLAY_WIDTH
) {
170 if (!(stateAt(r
, y
) & GR_BLOCK
)) break;
171 if (!matchAt(r
, y
, block
)) break;
177 if (!(stateAt(x
, b
- 1) & GR_BLOCK
)) break;
178 if (!matchAt(x
, b
- 1, block
)) break;
183 while (t
< GC_PLAY_HEIGHT
) {
184 if (!(stateAt(x
, t
) & GR_BLOCK
)) break;
185 if (!matchAt(x
, t
, block
)) break;
194 if (w
>= GC_MIN_PATTERN_LENGTH
) {
195 pattern
|= PT_HORIZONTAL
;
198 if (h
>= GC_MIN_PATTERN_LENGTH
) {
199 pattern
|= PT_VERTICAL
;
203 // if no pattern found
205 block
.endComboInvolvement(combo
);
209 // create a combo for the elimination
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.
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;
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
++)
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
242 shatterGarbage(l
- 1, y
);
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
++)
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
265 shatterGarbage(x
, b
- 1);
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
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
,
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
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
);
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;
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
];
349 // otherwise the assert() will tag us
350 for (int x
= GC_PLAY_WIDTH
; x
--; )
351 grid
[x
][0].state
= GR_EMPTY
;
356 LevelLights::levelRaise(top_effective_row
);
358 // shift blocks and garbage up with the grid
359 BlockManager::shiftUp();
360 GarbageManager::shiftUp();