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.
33 #include "ComboManager.h"
34 #include "LevelLights.h"
36 #include "BlockManager.h"
39 GridElement
Grid::grid
[GC_PLAY_WIDTH
][GC_PLAY_HEIGHT
];
40 CheckRegistryElement
Grid::check_registry
[GC_GRID_SIZE
];
41 int Grid::check_count
;
42 int Grid::shatter_count
;
43 int Grid::shatter_top
;
44 int Grid::shatter_bottom
;
45 bool Grid::gray_shatter
;
46 int Grid::top_occupied_row
;
47 int Grid::top_effective_row
;
49 void Grid::gameStart ( )
55 for (int i
= GC_PLAY_WIDTH
; i
--; )
56 for (int j
= GC_PLAY_HEIGHT
; j
--; ) {
57 grid
[i
][j
].state
= GR_EMPTY
;
58 grid
[i
][j
].resident_type
= GR_EMPTY
;
59 grid
[i
][j
].resident
= null
;
62 // Generate the initial blocks.
64 int short_collumn
= Random::number(GC_PLAY_WIDTH
);
66 for (int x
= GC_PLAY_WIDTH
; x
--; ) {
68 // generate the height
69 int height
= (short_collumn
== x
? 2 : 7) + Random::number2(2);
70 if (height
- 1 > top_occupied_row
)
71 top_occupied_row
= height
- 1;
73 for (int y
= height
; --y
; ) {
75 // determine the flavor
78 flavor
= Random::number(BF_NUMBER_NORMAL
);
80 if (!(Grid::stateAt(x
, y
+ 1) & GR_EMPTY
)
81 && Grid::blockAt(x
, y
+ 1).flavor
== flavor
) continue;
83 if (x
== GC_PLAY_WIDTH
- 1) break;
85 if (!(Grid::stateAt(x
+ 1, y
) & GR_EMPTY
)
86 && Grid::blockAt(x
+ 1, y
).flavor
== flavor
) continue;
91 // set the block manager block creation state
93 BlockManager::second_to_last_row_c
[x
] = flavor
;
95 BlockManager::last_row_c
[x
] = flavor
;
98 BlockManager::newBlock(x
, y
, flavor
);
102 top_effective_row
= top_occupied_row
;
105 void Grid::timeStep ( )
107 // Process elimination check requests.
109 // loop through the request registry
110 for (int n
= 0; check_count
; n
++)
111 if (check_registry
[n
].mark
) {
112 check_registry
[n
].mark
= false;
115 Block
&block
= BlockManager::blockStore
[n
];
117 // insure that the block is still static
118 if (!(block
.state
& BS_STATIC
)) continue;
120 // use the block's combo, if it has one
121 handleEliminationCheckRequest(block
,
122 block
.current_combo
? block
.current_combo
: check_registry
[n
].combo
);
125 // Update top_occupied_row.
130 for (int x
= GC_PLAY_WIDTH
; x
--; )
131 if (!(stateAt(x
, top_occupied_row
) & GR_EMPTY
)) {
137 // Update top_effective_row.
138 int o
= top_effective_row
;
143 for (int x
= GC_PLAY_WIDTH
; x
--; )
144 if (!(residentTypeAt(x
, top_effective_row
) & GR_EMPTY
)
145 && !((residentTypeAt(x
, top_effective_row
) & GR_GARBAGE
)
146 && garbageAt(x
, top_effective_row
).initial_fall
)) {
151 if (top_effective_row
< o
)
152 LevelLights::levelLower(top_effective_row
);
155 void Grid::handleEliminationCheckRequest ( Block
&block
, ComboTabulator
*combo
)
160 // Look in four directions for matching lines.
164 if (!(stateAt(l
- 1, y
) & GR_BLOCK
)) break;
165 if (!matchAt(l
- 1, y
, block
)) break;
170 while (r
< GC_PLAY_WIDTH
) {
171 if (!(stateAt(r
, y
) & GR_BLOCK
)) break;
172 if (!matchAt(r
, y
, block
)) break;
178 if (!(stateAt(x
, b
- 1) & GR_BLOCK
)) break;
179 if (!matchAt(x
, b
- 1, block
)) break;
184 while (t
< GC_PLAY_HEIGHT
) {
185 if (!(stateAt(x
, t
) & GR_BLOCK
)) break;
186 if (!matchAt(x
, t
, block
)) break;
195 if (w
>= GC_MIN_PATTERN_LENGTH
) {
196 pattern
|= PT_HORIZONTAL
;
199 if (h
>= GC_MIN_PATTERN_LENGTH
) {
200 pattern
|= PT_VERTICAL
;
204 // if no pattern found
206 block
.endComboInvolvement(combo
);
210 // create a combo for the elimination
212 combo
= &ComboManager::newComboTabulator();
214 // if pattern matches both directions
215 if (pattern
== (PT_HORIZONTAL
| PT_VERTICAL
)) magnitude
--;
217 // Kill the pattern's blocks and look for touching garbage.
221 shatter_bottom
= GC_PLAY_HEIGHT
;
223 // we need to know if it's a gray shatter in order to know if black
224 // garbage should shatter
225 if (!BlockManager::isColorlessFlavor(block
.flavor
))
226 gray_shatter
= false;
230 ComboManager::specialBlockTally(*combo
, block
);
231 block
.startDying(combo
, magnitude
);
233 if (pattern
& PT_HORIZONTAL
) {
234 // kill the pattern's blocks
235 for (int k_x
= l
; k_x
< r
; k_x
++)
237 ComboManager::specialBlockTally(*combo
, blockAt(k_x
, y
));
238 blockAt(k_x
, y
).startDying(combo
, magnitude
);
241 // look for garbage to the left and below the pattern
243 shatterGarbage(l
- 1, y
);
245 for (int k_x
= l
; k_x
< r
; k_x
++)
246 shatterGarbage(k_x
, y
- 1);
248 // look for garbage to the right and above the pattern
249 if (r
< GC_PLAY_WIDTH
)
250 shatterGarbage(r
, y
);
251 if (y
< GC_PLAY_HEIGHT
- 1)
252 for (int k_x
= l
; k_x
< r
; k_x
++)
253 shatterGarbage(k_x
, y
+ 1);
256 if (pattern
& PT_VERTICAL
) {
257 // kill the pattern's blocks
258 for (int k_y
= b
; k_y
< t
; k_y
++)
260 ComboManager::specialBlockTally(*combo
, blockAt(x
, k_y
));
261 blockAt(x
, k_y
).startDying(combo
, magnitude
);
264 // look for garbage to the left and below the pattern
266 shatterGarbage(x
, b
- 1);
268 for (int k_y
= b
; k_y
< t
; k_y
++)
269 shatterGarbage(x
- 1, k_y
);
271 // look for garbage to the right and above the pattern
272 if (t
< GC_PLAY_HEIGHT
)
273 shatterGarbage(x
, t
);
274 if (x
< GC_PLAY_WIDTH
- 1)
275 for (int k_y
= b
; k_y
< t
; k_y
++)
276 shatterGarbage(x
+ 1, k_y
);
279 // The grid now contains shattered elements. We now traverse the grid and
280 // syncronize the shattering garbages' timers. The garbage itself deals with
281 // the creation of blocks and such, as it's behavior may varry.
283 // delay until the new blocks fall
284 int awaken_delay
= GC_INITIAL_POP_DELAY
+ GC_FINAL_POP_DELAY
285 + GC_INTERNAL_POP_DELAY
* (shatter_count
- 1);
287 // delay until the next block pops
288 int pop_delay
= GC_INITIAL_POP_DELAY
;
290 // traverse the shatter area; s_x and pop_delay will be advanced during
292 for (int s_y
= shatter_bottom
; s_y
< shatter_top
; s_y
++)
293 for (int s_x
= 0; s_x
< GC_PLAY_WIDTH
; )
294 if (stateAt(s_x
, s_y
) & GR_SHATTERING
)
295 garbageAt(s_x
, s_y
).startShattering(s_x
, s_y
, pop_delay
, awaken_delay
,
300 // notify the combo of the pattern match
301 combo
->reportElimination(magnitude
, block
);
304 void Grid::shatterGarbage_inline_split_ ( int x
, int y
, Garbage
*due_to
)
306 Garbage
&garbage
= garbageAt(x
, y
);
308 // ask the garbage about shattering; we have to do black ourselves
309 if (!garbage
.considerShattering(due_to
)) return;
310 if (garbage
.flavor
== GF_BLACK
&& !gray_shatter
) return;
312 // keep track of the bounds on the shattered area
313 if (y
+ garbage
.height
> shatter_top
)
314 shatter_top
= y
+ garbage
.height
;
315 if (y
- garbage
.height
< shatter_bottom
)
316 shatter_bottom
= y
- garbage
.height
;
318 shatter_count
+= garbage
.width
* garbage
.height
;
320 // mark the garbage grid locations as shattered
321 for (int h
= garbage
.height
; h
--; )
322 for (int w
= garbage
.width
; w
--; )
323 grid
[garbage
.x
+ w
][garbage
.y
+ h
].state
= GR_SHATTERING
;
325 // look for connecting garbage recursively
327 for (int h
= garbage
.height
; h
--; )
328 shatterGarbage(garbage
.x
- 1, garbage
.y
+ h
, &garbage
);
329 if (garbage
.x
+ garbage
.width
< GC_PLAY_WIDTH
)
330 for (int h
= garbage
.height
; h
--; )
331 shatterGarbage(garbage
.x
+ garbage
.width
, garbage
.y
+ h
, &garbage
);
333 for (int w
= garbage
.width
; w
--; )
334 shatterGarbage(garbage
.x
+ w
, garbage
.y
- 1, &garbage
);
335 if (garbage
.y
+ garbage
.height
< GC_PLAY_HEIGHT
)
336 for (int w
= garbage
.width
; w
--; )
337 shatterGarbage(garbage
.x
+ w
, garbage
.y
+ garbage
.height
, &garbage
);
340 bool Grid::shiftGridUp ( )
342 if (top_occupied_row
== GC_PLAY_HEIGHT
- 1) return false;
345 for (int y
= top_occupied_row
+ 1; y
--; )
346 for (int x
= GC_PLAY_WIDTH
; x
--; )
347 grid
[x
][y
+ 1] = grid
[x
][y
];
350 // otherwise the assert() will tag us
351 for (int x
= GC_PLAY_WIDTH
; x
--; )
352 grid
[x
][0].state
= GR_EMPTY
;
357 LevelLights::levelRaise(top_effective_row
);
359 // shift blocks and garbage up with the grid
360 BlockManager::shiftUp();
361 GarbageManager::shiftUp();