1 | // |
---|
2 | // Monsterz |
---|
3 | // |
---|
4 | // Copyright: (c) 2005-2011 Sam Hocevar <sam@hocevar.net> |
---|
5 | // This program is free software; you can redistribute it and/or |
---|
6 | // modify it under the terms of the Do What The Fuck You Want To |
---|
7 | // Public License, Version 2, as published by Sam Hocevar. See |
---|
8 | // http://sam.zoy.org/projects/COPYING.WTFPL for more details. |
---|
9 | // |
---|
10 | |
---|
11 | #if defined HAVE_CONFIG_H |
---|
12 | # include "config.h" |
---|
13 | #endif |
---|
14 | |
---|
15 | #include <cstdio> |
---|
16 | #include <cmath> |
---|
17 | #include <cstdlib> |
---|
18 | #include <ctime> |
---|
19 | #include <cstring> |
---|
20 | |
---|
21 | #include "core.h" |
---|
22 | #include "board.h" |
---|
23 | #include "piece.h" |
---|
24 | #include "thumbs.h" |
---|
25 | #include "mash.h" |
---|
26 | #include "monsterz.h" |
---|
27 | |
---|
28 | /* |
---|
29 | * Board implementation class |
---|
30 | */ |
---|
31 | |
---|
32 | class BoardData |
---|
33 | { |
---|
34 | friend class Board; |
---|
35 | |
---|
36 | private: |
---|
37 | Board::game_t game; |
---|
38 | |
---|
39 | int2 dim; |
---|
40 | int minnpieces, npieces, maxnpieces; |
---|
41 | |
---|
42 | int board, tiles, icons; |
---|
43 | int click, whip; |
---|
44 | |
---|
45 | struct Pair |
---|
46 | { |
---|
47 | int id; |
---|
48 | Piece *piece; |
---|
49 | } |
---|
50 | pairs[MAX_WIDTH][MAX_HEIGHT]; |
---|
51 | |
---|
52 | /* Hunt */ |
---|
53 | struct Pair grabbed; |
---|
54 | int nmoves; |
---|
55 | int2 src_cell, dst_cell; |
---|
56 | |
---|
57 | /* Fusion */ |
---|
58 | struct Pair current[2]; |
---|
59 | int next[2], rotation; |
---|
60 | |
---|
61 | Text *scoretext; |
---|
62 | int score; |
---|
63 | |
---|
64 | Mash *mashes; |
---|
65 | Emitter *emitter; |
---|
66 | Thumbs *thumbs; |
---|
67 | |
---|
68 | int2 oldmouse; |
---|
69 | int3 oldbuttons; |
---|
70 | float nextblink, whipdelay; |
---|
71 | |
---|
72 | enum |
---|
73 | { |
---|
74 | HUNT_IDLE, |
---|
75 | HUNT_BADCLICK, |
---|
76 | HUNT_GRAB, |
---|
77 | FUSION_IDLE, |
---|
78 | } |
---|
79 | state; |
---|
80 | }; |
---|
81 | |
---|
82 | /* |
---|
83 | * Public Board class |
---|
84 | */ |
---|
85 | |
---|
86 | Board::Board(game_t game, int2 dim, int minnpieces, int maxnpieces) |
---|
87 | : data(new BoardData()) |
---|
88 | { |
---|
89 | data->game = game; |
---|
90 | |
---|
91 | data->dim = dim; |
---|
92 | data->minnpieces = minnpieces; |
---|
93 | data->npieces = minnpieces; |
---|
94 | data->maxnpieces = maxnpieces; |
---|
95 | data->board = Tiler::Register(PNG_BOARD, 384, 384, 1.0f); |
---|
96 | data->tiles = Tiler::Register(PNG_TILES, 48, 48, 1.0f); |
---|
97 | data->icons = Tiler::Register(PNG_ICONS, 24, 24, 1.0f); |
---|
98 | data->click = Sampler::Register(WAV_CLICK); |
---|
99 | data->whip = Sampler::Register(WAV_WHIP); |
---|
100 | |
---|
101 | data->emitter = new Emitter(data->tiles, float3(0, -0.0006f, 0)); |
---|
102 | Ticker::Ref(data->emitter); |
---|
103 | |
---|
104 | data->thumbs = new Thumbs(0); |
---|
105 | Ticker::Ref(data->thumbs); |
---|
106 | |
---|
107 | switch (data->game) |
---|
108 | { |
---|
109 | case GAME_HUNT: |
---|
110 | Fill(); |
---|
111 | data->thumbs->SetMax(data->npieces); |
---|
112 | data->state = BoardData::HUNT_IDLE; |
---|
113 | break; |
---|
114 | |
---|
115 | case GAME_FUSION: |
---|
116 | for (int j = 0; j < data->dim.j; j++) |
---|
117 | for (int i = 0; i < data->dim.i; i++) |
---|
118 | data->pairs[i][j].id = 0; |
---|
119 | |
---|
120 | data->current[0].id = GetRandomId(); |
---|
121 | data->current[1].id = GetRandomId(); |
---|
122 | data->current[0].piece = new Piece(data->emitter, int2(3, 8), |
---|
123 | 80 + 20 * data->current[0].id); |
---|
124 | data->current[1].piece = new Piece(data->emitter, int2(4, 8), |
---|
125 | 80 + 20 * data->current[1].id); |
---|
126 | Ticker::Ref(data->current[0].piece); |
---|
127 | Ticker::Ref(data->current[1].piece); |
---|
128 | data->current[0].piece->SetPos(int2(3, 7) * 48); |
---|
129 | data->current[1].piece->SetPos(int2(4, 7) * 48); |
---|
130 | |
---|
131 | data->next[0] = GetRandomId(); |
---|
132 | data->next[1] = GetRandomId(); |
---|
133 | data->rotation = 0; |
---|
134 | |
---|
135 | data->thumbs->SetMax(data->npieces + 1); |
---|
136 | data->state = BoardData::FUSION_IDLE; |
---|
137 | break; |
---|
138 | } |
---|
139 | |
---|
140 | data->mashes = NULL; |
---|
141 | data->nextblink = 0.0f; |
---|
142 | data->whipdelay = 0.0f; |
---|
143 | |
---|
144 | data->scoretext = new Text(NULL, "monsterz/gfx/font2.png"); |
---|
145 | data->scoretext->SetAlign(Text::ALIGN_RIGHT); |
---|
146 | data->scoretext->SetPos(int3(624, 432, 1)); |
---|
147 | Ticker::Ref(data->scoretext); |
---|
148 | data->score = 0; |
---|
149 | |
---|
150 | position = int3(24, 72, 1); |
---|
151 | bbox[0] = position; |
---|
152 | bbox[1] = bbox[0] + int3(384, 384, 0); |
---|
153 | |
---|
154 | Input::TrackMouse(this); |
---|
155 | } |
---|
156 | |
---|
157 | void Board::TickGame(float deltams) |
---|
158 | { |
---|
159 | Entity::TickGame(deltams); |
---|
160 | |
---|
161 | int3 buttons = Input::GetMouseButtons(); |
---|
162 | |
---|
163 | /* If possible, make a random monster blink */ |
---|
164 | if (data->game == Board::GAME_HUNT && (data->nextblink -= deltams) < 0.0f) |
---|
165 | { |
---|
166 | data->pairs[rand() % data->dim.i] |
---|
167 | [rand() % data->dim.j].piece->Blink(); |
---|
168 | data->nextblink = (float)(200 + rand() % 500); |
---|
169 | } |
---|
170 | |
---|
171 | /* Do not whip too often, the sound may become annoying */ |
---|
172 | data->whipdelay -= deltams; |
---|
173 | |
---|
174 | /* Get rid of finished mashes */ |
---|
175 | for (Mash **it = &data->mashes; *it; ) |
---|
176 | { |
---|
177 | if ((*it)->IsDead()) |
---|
178 | { |
---|
179 | Ticker::Unref(*it); |
---|
180 | *it = (*it)->nextmash; |
---|
181 | } |
---|
182 | else |
---|
183 | it = &(*it)->nextmash; |
---|
184 | } |
---|
185 | |
---|
186 | /* Update score */ |
---|
187 | data->scoretext->SetInt(data->score); |
---|
188 | |
---|
189 | switch (data->state) |
---|
190 | { |
---|
191 | case BoardData::HUNT_IDLE: |
---|
192 | /* Should we start dragging something? */ |
---|
193 | if (buttons[0] && !data->oldbuttons[0] && mousepos.x != -1) |
---|
194 | { |
---|
195 | int2 cell = mousepos / 48; |
---|
196 | if (data->pairs[cell.x][cell.y].piece->Grab(int2(0, 0))) |
---|
197 | { |
---|
198 | Sampler::PlaySample(data->click); |
---|
199 | data->grabbed = data->pairs[cell.x][cell.y]; |
---|
200 | data->src_cell = mousepos / 48; |
---|
201 | data->dst_cell = int2(-1); |
---|
202 | data->state = BoardData::HUNT_GRAB; |
---|
203 | } |
---|
204 | else |
---|
205 | data->state = BoardData::HUNT_BADCLICK; |
---|
206 | } |
---|
207 | break; |
---|
208 | |
---|
209 | case BoardData::HUNT_GRAB: |
---|
210 | if (mousepos.x != -1) |
---|
211 | { |
---|
212 | /* Mouse is still in the window, keep grabbing */ |
---|
213 | data->grabbed.piece->Grab(mousepos - data->oldmouse); |
---|
214 | int2 cur_pos = data->grabbed.piece->GetPos(); |
---|
215 | int2 cur_cell = (cur_pos + 24) / 48; |
---|
216 | if (cur_cell.i < 0 || cur_cell.i >= data->dim.i |
---|
217 | || cur_cell.j < 0 || cur_cell.j >= data->dim.j |
---|
218 | || (cur_pos - cur_cell * 48).sqlen() > 24 * 24 |
---|
219 | || (cur_cell - data->src_cell).sqlen() != 1) |
---|
220 | cur_cell = int2(-1); |
---|
221 | /* If potential target changed, update our cache. */ |
---|
222 | if (cur_cell != data->dst_cell) |
---|
223 | { |
---|
224 | if (data->whipdelay < 0.0f) |
---|
225 | { |
---|
226 | Sampler::PlaySample(data->whip); |
---|
227 | data->whipdelay = DELAY_WHIP; |
---|
228 | } |
---|
229 | if (data->dst_cell != int2(-1)) |
---|
230 | data->pairs[data->dst_cell.i] |
---|
231 | [data->dst_cell.j].piece->Ungrab(data->dst_cell * 48); |
---|
232 | if (cur_cell != int2(-1)) |
---|
233 | data->pairs[cur_cell.i] |
---|
234 | [cur_cell.j].piece->Ungrab(data->src_cell * 48); |
---|
235 | data->dst_cell = cur_cell; |
---|
236 | } |
---|
237 | } |
---|
238 | if (!buttons[0] || mousepos.x == -1 |
---|
239 | || (data->src_cell * 48 |
---|
240 | - data->grabbed.piece->GetPos()).sqlen() > 100 * 100) |
---|
241 | { |
---|
242 | /* Mouse released, or exited window, or dragged too far. */ |
---|
243 | data->grabbed.piece->Ungrab(data->grabbed.piece->GetCell() * 48); |
---|
244 | if (data->dst_cell != int2(-1)) |
---|
245 | Switch(data->src_cell, data->dst_cell); |
---|
246 | data->state = BoardData::HUNT_IDLE; |
---|
247 | } |
---|
248 | break; |
---|
249 | |
---|
250 | case BoardData::HUNT_BADCLICK: |
---|
251 | if (!buttons[0]) |
---|
252 | data->state = BoardData::HUNT_IDLE; |
---|
253 | break; |
---|
254 | |
---|
255 | case BoardData::FUSION_IDLE: |
---|
256 | { |
---|
257 | int column = -1; |
---|
258 | |
---|
259 | if (clicked[2]) |
---|
260 | { |
---|
261 | column = data->current[0].piece->GetCell().x; |
---|
262 | data->rotation = (data->rotation + 1) % 2; |
---|
263 | if (column - data->rotation > data->dim.i - 1) |
---|
264 | column = data->dim.i - 1 - data->rotation; |
---|
265 | if (!data->rotation) |
---|
266 | { |
---|
267 | BoardData::Pair tmp = data->current[0]; |
---|
268 | data->current[0] = data->current[1]; |
---|
269 | data->current[1] = tmp; |
---|
270 | if (column == data->dim.i - 1) |
---|
271 | column = 6; |
---|
272 | } |
---|
273 | } |
---|
274 | |
---|
275 | if (mousepos.x != -1 |
---|
276 | && mousepos.x / 48 != data->current[0].piece->GetCell().x) |
---|
277 | { |
---|
278 | column = mousepos.x / 48; |
---|
279 | column = column < 0 ? 0 : column > data->dim.i - 2 + data->rotation ? data->dim.i - 2 + data->rotation : column; |
---|
280 | } |
---|
281 | |
---|
282 | if (column != -1) |
---|
283 | { |
---|
284 | if (data->rotation) |
---|
285 | { |
---|
286 | data->current[0].piece->SetCell(int2(column, 6)); |
---|
287 | data->current[1].piece->SetCell(int2(column, 7)); |
---|
288 | } |
---|
289 | else |
---|
290 | { |
---|
291 | data->current[0].piece->SetCell(int2(column, 7)); |
---|
292 | data->current[1].piece->SetCell(int2(column + 1, 7)); |
---|
293 | } |
---|
294 | |
---|
295 | data->current[0].piece->Move(data->current[0].piece->GetCell() * 48); |
---|
296 | data->current[1].piece->Move(data->current[1].piece->GetCell() * 48); |
---|
297 | } |
---|
298 | |
---|
299 | if (clicked[0]) |
---|
300 | { |
---|
301 | for (int t = 0; t < 2; t++) |
---|
302 | { |
---|
303 | int i = data->current[t].piece->GetCell().i; |
---|
304 | for (int j = 0; j < 7; j++) |
---|
305 | if (data->pairs[i][j].id == 0) |
---|
306 | { |
---|
307 | data->current[t].piece->SetCell(int2(i, j)); |
---|
308 | data->current[t].piece->Move(int2(i, j) * 48); |
---|
309 | data->pairs[i][j] = data->current[t]; |
---|
310 | data->thumbs->AddCount(data->current[t].id, 1); |
---|
311 | break; |
---|
312 | } |
---|
313 | } |
---|
314 | |
---|
315 | data->current[0].id = data->next[0]; |
---|
316 | data->current[1].id = data->next[1]; |
---|
317 | data->current[0].piece = new Piece(data->emitter, int2(3, 7), |
---|
318 | 80 + 20 * data->current[0].id); |
---|
319 | data->current[1].piece = new Piece(data->emitter, int2(4, 7), |
---|
320 | 80 + 20 * data->current[1].id); |
---|
321 | Ticker::Ref(data->current[0].piece); |
---|
322 | Ticker::Ref(data->current[1].piece); |
---|
323 | data->current[0].piece->SetPos(int2(3, 8) * 48); |
---|
324 | data->current[1].piece->SetPos(int2(4, 8) * 48); |
---|
325 | data->current[0].piece->Move(data->current[0].piece->GetCell() * 48); |
---|
326 | data->current[1].piece->Move(data->current[1].piece->GetCell() * 48); |
---|
327 | data->next[0] = GetRandomId(); |
---|
328 | data->next[1] = GetRandomId(); |
---|
329 | data->rotation = 0; |
---|
330 | |
---|
331 | Resolve(); |
---|
332 | } |
---|
333 | break; |
---|
334 | } |
---|
335 | } |
---|
336 | |
---|
337 | data->oldmouse = mousepos; |
---|
338 | data->oldbuttons = buttons; |
---|
339 | } |
---|
340 | |
---|
341 | void Board::TickDraw(float deltams) |
---|
342 | { |
---|
343 | Entity::TickDraw(deltams); |
---|
344 | |
---|
345 | Scene::GetDefault()->AddTile((data->board << 16) | 0, |
---|
346 | position.x, position.y, 1, 0); |
---|
347 | |
---|
348 | switch (data->game) |
---|
349 | { |
---|
350 | case GAME_HUNT: |
---|
351 | break; |
---|
352 | case GAME_FUSION: |
---|
353 | Scene::GetDefault()->AddTile((data->icons << 16) | (data->next[0] - 1), |
---|
354 | 350, 400, 11, 0); |
---|
355 | Scene::GetDefault()->AddTile((data->icons << 16) | (data->next[1] - 1), |
---|
356 | 380, 400, 11, 0); |
---|
357 | break; |
---|
358 | } |
---|
359 | } |
---|
360 | |
---|
361 | /* Fill the board with an initial position. We ensure no pieces are |
---|
362 | * aligned and at least one move is possible. */ |
---|
363 | void Board::Fill() |
---|
364 | { |
---|
365 | srand(rand() ^ time(NULL)); |
---|
366 | |
---|
367 | int list[MAX_WIDTH][MAX_HEIGHT]; |
---|
368 | do |
---|
369 | { |
---|
370 | for (int j = 0; j < data->dim.j; j++) |
---|
371 | for (int i = 0; i < data->dim.i; i++) |
---|
372 | data->pairs[i][j].id = 1 + rand() % data->npieces; |
---|
373 | } while (ListMashes(list) || !(data->nmoves = ListMoves(list))); |
---|
374 | |
---|
375 | /* Spawn pieces */ |
---|
376 | for (int j = 0; j < data->dim.j; j++) |
---|
377 | for (int i = 0; i < data->dim.i; i++) |
---|
378 | { |
---|
379 | int2 newpos = int2(i, j + data->dim.j) * 48; |
---|
380 | int id = 80 + 20 * data->pairs[i][j].id; |
---|
381 | data->pairs[i][j].piece = new Piece(data->emitter, int2(i, j), id); |
---|
382 | data->pairs[i][j].piece->SetPos(newpos); |
---|
383 | data->pairs[i][j].piece->Move(int2(i, j) * 48); |
---|
384 | if (j) |
---|
385 | data->pairs[i][j].piece->SetBelow(data->pairs[i][j - 1].piece); |
---|
386 | Ticker::Ref(data->pairs[i][j].piece); |
---|
387 | } |
---|
388 | } |
---|
389 | |
---|
390 | void Board::Switch(int2 cell_a, int2 cell_b) |
---|
391 | { |
---|
392 | BoardData::Pair a = data->pairs[cell_a.i][cell_a.j]; |
---|
393 | BoardData::Pair b = data->pairs[cell_b.i][cell_b.j]; |
---|
394 | data->pairs[cell_a.i][cell_a.j] = b; |
---|
395 | data->pairs[cell_b.i][cell_b.j] = a; |
---|
396 | |
---|
397 | /* Check whether this is a valid move by testing all patterns. |
---|
398 | * If the move is invalid, cancel the swap and bail out */ |
---|
399 | int list[MAX_WIDTH][MAX_HEIGHT]; |
---|
400 | |
---|
401 | if (!ListMashes(list)) |
---|
402 | { |
---|
403 | data->pairs[cell_a.i][cell_a.j] = a; |
---|
404 | data->pairs[cell_b.i][cell_b.j] = b; |
---|
405 | a.piece->Ungrab(cell_a * 48); |
---|
406 | b.piece->Ungrab(cell_b * 48); |
---|
407 | return; |
---|
408 | } |
---|
409 | |
---|
410 | /* Perform the swap */ |
---|
411 | a.piece->SetCell(cell_b); |
---|
412 | a.piece->Ungrab(cell_b * 48); |
---|
413 | b.piece->SetCell(cell_a); |
---|
414 | b.piece->Ungrab(cell_a * 48); |
---|
415 | |
---|
416 | /* Swap above and below cells */ |
---|
417 | if (cell_a.i == cell_b.i) |
---|
418 | { |
---|
419 | Piece *tmpa = a.piece->GetAbove(); |
---|
420 | Piece *tmpb = b.piece->GetAbove(); |
---|
421 | if (tmpb == a.piece) |
---|
422 | { |
---|
423 | tmpb = b.piece->GetBelow(); |
---|
424 | b.piece->SetAbove(tmpa); |
---|
425 | b.piece->SetBelow(a.piece); |
---|
426 | a.piece->SetBelow(tmpb); |
---|
427 | } |
---|
428 | else /* tmpa == b.piece */ |
---|
429 | { |
---|
430 | tmpa = a.piece->GetBelow(); |
---|
431 | a.piece->SetAbove(tmpb); |
---|
432 | a.piece->SetBelow(b.piece); |
---|
433 | b.piece->SetBelow(tmpa); |
---|
434 | } |
---|
435 | } |
---|
436 | else |
---|
437 | { |
---|
438 | Piece *tmpa = a.piece->GetAbove(); |
---|
439 | Piece *tmpb = b.piece->GetAbove(); |
---|
440 | a.piece->SetAbove(tmpb); |
---|
441 | b.piece->SetAbove(tmpa); |
---|
442 | tmpa = a.piece->GetBelow(); |
---|
443 | tmpb = b.piece->GetBelow(); |
---|
444 | a.piece->SetBelow(tmpb); |
---|
445 | b.piece->SetBelow(tmpa); |
---|
446 | } |
---|
447 | |
---|
448 | /* Remove matching pieces and store them in Mash objects */ |
---|
449 | do |
---|
450 | { |
---|
451 | Mash *mash = new Mash(data->emitter); |
---|
452 | Ticker::Ref(mash); |
---|
453 | |
---|
454 | for (int j = data->dim.j; j--;) for (int i = 0; i < data->dim.i; i++) |
---|
455 | { |
---|
456 | if (!list[i][j]) |
---|
457 | continue; |
---|
458 | |
---|
459 | /* The mash becomes the new owner of the disappearing piece */ |
---|
460 | mash->AddPiece(data->pairs[i][j].piece); |
---|
461 | data->thumbs->AddCount(data->pairs[i][j].id, 1); |
---|
462 | |
---|
463 | #if 0 // Test for piece creation |
---|
464 | if (list[i][j] >= 2) |
---|
465 | { |
---|
466 | Piece *old = data->pairs[i][j].piece; |
---|
467 | int id = 1 + rand() % data->npieces; |
---|
468 | data->pairs[i][j].id = id; |
---|
469 | data->pairs[i][j].piece = new Piece(data->emitter, int2(i, j), 80 + 20 * id); |
---|
470 | data->pairs[i][j].piece->SetBelow(old->GetBelow()); |
---|
471 | data->pairs[i][j].piece->SetAbove(old->GetAbove()); |
---|
472 | Ticker::Ref(data->pieces[i][j].piece); |
---|
473 | list[i][j] = 0; |
---|
474 | } |
---|
475 | else |
---|
476 | #endif |
---|
477 | { |
---|
478 | Piece *below = data->pairs[i][data->dim.j - 1].piece; |
---|
479 | |
---|
480 | /* Change coordinates for the whole column above */ |
---|
481 | for (int j2 = j + 1; j2 < data->dim.j; j2++) |
---|
482 | { |
---|
483 | data->pairs[i][j2 - 1] = data->pairs[i][j2]; |
---|
484 | data->pairs[i][j2 - 1].piece->SetCell(int2(i, j2 - 1)); |
---|
485 | data->pairs[i][j2 - 1].piece->Move(int2(i, j2 - 1) * 48); |
---|
486 | list[i][j2 - 1] = list[i][j2]; |
---|
487 | } |
---|
488 | |
---|
489 | /* Spawn a new piece above all the others and attach it to |
---|
490 | * the board. */ |
---|
491 | int2 newpos = int2(i * 48, below->GetPos().y + 48); |
---|
492 | int2 newcell = int2(i, data->dim.j - 1); |
---|
493 | int id = 1 + rand() % data->npieces; |
---|
494 | Piece *tmp = new Piece(data->emitter, newcell, 80 + 20 * id); |
---|
495 | tmp->SetBelow(below); |
---|
496 | tmp->SetPos(newpos); |
---|
497 | tmp->Move(newcell * 48); |
---|
498 | Ticker::Ref(tmp); |
---|
499 | data->pairs[i][data->dim.j - 1].id = id; |
---|
500 | data->pairs[i][data->dim.j - 1].piece = tmp; |
---|
501 | list[i][data->dim.j - 1] = 0; |
---|
502 | } |
---|
503 | } |
---|
504 | |
---|
505 | mash->nextmash = data->mashes; |
---|
506 | data->mashes = mash; |
---|
507 | } |
---|
508 | while(ListMashes(list)); |
---|
509 | |
---|
510 | data->nmoves = ListMoves(list); |
---|
511 | |
---|
512 | if (data->nmoves == 0) |
---|
513 | { |
---|
514 | Mash *mash = new Mash(data->emitter); |
---|
515 | Ticker::Ref(mash); |
---|
516 | |
---|
517 | for (int j = data->dim.j; j--;) for (int i = 0; i < data->dim.i; i++) |
---|
518 | mash->AddPiece(data->pairs[i][j].piece); |
---|
519 | |
---|
520 | mash->nextmash = data->mashes; |
---|
521 | data->mashes = mash; |
---|
522 | |
---|
523 | Piece *below[MAX_WIDTH]; |
---|
524 | for (int i = 0; i < data->dim.i; i++) |
---|
525 | below[i] = data->pairs[i][data->dim.j - 1].piece; |
---|
526 | |
---|
527 | Fill(); |
---|
528 | |
---|
529 | for (int i = 0; i < data->dim.i; i++) |
---|
530 | data->pairs[i][0].piece->SetBelow(below[i]); |
---|
531 | } |
---|
532 | } |
---|
533 | |
---|
534 | /* Fill an array with the list of pieces that should disappear due to |
---|
535 | * 3-piece or more alignments. */ |
---|
536 | int Board::ListMashes(int list[MAX_WIDTH][MAX_HEIGHT]) |
---|
537 | { |
---|
538 | int ret = 0; |
---|
539 | |
---|
540 | for (int j = 0; j < data->dim.j; j++) |
---|
541 | for (int i = 0; i < data->dim.i; i++) |
---|
542 | list[i][j] = 0; |
---|
543 | |
---|
544 | for (int j = 0; j < data->dim.j; j++) |
---|
545 | for (int i = 0; i < data->dim.i; i++) |
---|
546 | { |
---|
547 | int id = data->pairs[i][j].id; |
---|
548 | |
---|
549 | if (i + 2 < data->dim.i && data->pairs[i + 1][j].id == id |
---|
550 | && data->pairs[i + 2][j].id == id) |
---|
551 | { |
---|
552 | list[i][j]++; |
---|
553 | list[i + 1][j] += 2; |
---|
554 | list[i + 2][j]++; |
---|
555 | ret = 1; |
---|
556 | } |
---|
557 | |
---|
558 | if (j + 2 < data->dim.j && data->pairs[i][j + 1].id == id |
---|
559 | && data->pairs[i][j + 2].id == id) |
---|
560 | { |
---|
561 | list[i][j]++; |
---|
562 | list[i][j + 1] += 2; |
---|
563 | list[i][j + 2]++; |
---|
564 | ret = 1; |
---|
565 | } |
---|
566 | } |
---|
567 | |
---|
568 | return ret; |
---|
569 | } |
---|
570 | |
---|
571 | /* Fill an array with the list of pieces that can be moved. A value of 1 |
---|
572 | * indicates the piece can be moved right. A value of 2 means it can be |
---|
573 | * moved up, and a value of 3 means both moves are possible. The number |
---|
574 | * of possible moves is returned. */ |
---|
575 | int Board::ListMoves(int moves[MAX_WIDTH][MAX_HEIGHT]) |
---|
576 | { |
---|
577 | int ret = 0; |
---|
578 | |
---|
579 | for (int j = 0; j < data->dim.j; j++) |
---|
580 | for (int i = 0; i < data->dim.i; i++) |
---|
581 | moves[i][j] = 0; |
---|
582 | |
---|
583 | for (int j = 0; j < data->dim.j; j++) |
---|
584 | for (int i = 0; i < data->dim.i; i++) |
---|
585 | { |
---|
586 | /* Copy neighbourhood to a local buffer */ |
---|
587 | int tmp[6][6]; |
---|
588 | |
---|
589 | for (int dj = -2; dj <= 3; dj++) |
---|
590 | for (int di = -2; di <= 3; di++) |
---|
591 | if (j + dj >= 0 && j + dj < data->dim.j |
---|
592 | && i + di >= 0 && i + di < data->dim.i) |
---|
593 | tmp[2 + di][2 + dj] = data->pairs[i + di][j + dj].id; |
---|
594 | else |
---|
595 | tmp[2 + di][2 + dj] = 0; |
---|
596 | |
---|
597 | /* +--+--+--+--+--+--+ |
---|
598 | * | | |25| | | | |
---|
599 | * +--+--+--+--+--+--+ |
---|
600 | * | | |24|34| | | |
---|
601 | * +--+--+--+--+--+--+ |
---|
602 | * |03|13|c |33|43| | |
---|
603 | * +--+--+--+--+--+--+ |
---|
604 | * |02|12|a |b |42|52| |
---|
605 | * +--+--+--+--+--+--+ |
---|
606 | * | |11|21|31| | | |
---|
607 | * +--+--+--+--+--+--+ |
---|
608 | * | | |20|30| | | |
---|
609 | * +--+--+--+--+--+--+ */ |
---|
610 | int a = tmp[2][2]; |
---|
611 | int b = tmp[3][2] ? tmp[3][2] : -1; |
---|
612 | int c = tmp[2][3] ? tmp[2][3] : -1; |
---|
613 | |
---|
614 | /* Try moving right */ |
---|
615 | if ((a == tmp[3][0] && a == tmp[3][1]) || |
---|
616 | (a == tmp[3][1] && a == tmp[3][3]) || |
---|
617 | (a == tmp[3][3] && a == tmp[3][4]) || |
---|
618 | (a == tmp[4][2] && a == tmp[5][2]) || |
---|
619 | (b == tmp[2][0] && b == tmp[2][1]) || |
---|
620 | (b == tmp[2][1] && b == tmp[2][3]) || |
---|
621 | (b == tmp[2][3] && b == tmp[2][4]) || |
---|
622 | (b == tmp[0][2] && b == tmp[1][2])) |
---|
623 | { |
---|
624 | moves[i][j] |= 1; |
---|
625 | ret++; |
---|
626 | } |
---|
627 | |
---|
628 | /* Try moving up */ |
---|
629 | if ((a == tmp[0][3] && a == tmp[1][3]) || |
---|
630 | (a == tmp[1][3] && a == tmp[3][3]) || |
---|
631 | (a == tmp[3][3] && a == tmp[4][3]) || |
---|
632 | (a == tmp[2][4] && a == tmp[2][5]) || |
---|
633 | (c == tmp[0][2] && c == tmp[1][2]) || |
---|
634 | (c == tmp[1][2] && c == tmp[3][2]) || |
---|
635 | (c == tmp[3][2] && c == tmp[4][2]) || |
---|
636 | (c == tmp[2][0] && c == tmp[2][1])) |
---|
637 | { |
---|
638 | moves[i][j] |= 2; |
---|
639 | ret++; |
---|
640 | } |
---|
641 | } |
---|
642 | |
---|
643 | return ret; |
---|
644 | } |
---|
645 | |
---|
646 | int Board::GetRandomId() const |
---|
647 | { |
---|
648 | int max = data->npieces; |
---|
649 | |
---|
650 | if (max > data->minnpieces) |
---|
651 | max--; |
---|
652 | |
---|
653 | return 1 + rand() % max; |
---|
654 | } |
---|
655 | |
---|
656 | void Board::Resolve() |
---|
657 | { |
---|
658 | int list[MAX_PIECES][MAX_PIECES]; |
---|
659 | int count[MAX_PIECES * MAX_PIECES]; |
---|
660 | |
---|
661 | for (int j = 0; j < data->dim.j; j++) |
---|
662 | for (int i = 0; i < data->dim.i; i++) |
---|
663 | list[i][j] = -1; |
---|
664 | memset(count, 0, sizeof(count)); |
---|
665 | |
---|
666 | int seq = 0, effect = 0; |
---|
667 | |
---|
668 | /* Count connected tiles */ |
---|
669 | for (int j = 0; j < data->dim.j; j++) for (int i = 0; i < data->dim.i; i++) |
---|
670 | { |
---|
671 | if (!data->pairs[i][j].id) |
---|
672 | continue; |
---|
673 | |
---|
674 | if (data->pairs[i][j].id >= data->maxnpieces) |
---|
675 | continue; |
---|
676 | |
---|
677 | if (list[i][j] != -1) |
---|
678 | continue; |
---|
679 | |
---|
680 | list[i][j] = seq; |
---|
681 | count[seq] = TagNeighbours(list, i, j); |
---|
682 | if (count[seq] >= 3) |
---|
683 | effect = 1; |
---|
684 | seq++; |
---|
685 | } |
---|
686 | |
---|
687 | /* Only continue if there is an effect */ |
---|
688 | if (!effect) |
---|
689 | return; |
---|
690 | |
---|
691 | /* Add tiles to a mash; add mash to our list */ |
---|
692 | Mash *mash = new Mash(data->emitter); |
---|
693 | Ticker::Ref(mash); |
---|
694 | |
---|
695 | for (int j = 0; j < data->dim.j; j++) for (int i = 0; i < data->dim.i; i++) |
---|
696 | { |
---|
697 | if (list[i][j] == -1) |
---|
698 | continue; |
---|
699 | if (count[list[i][j]] < 3) |
---|
700 | continue; |
---|
701 | |
---|
702 | mash->AddPiece(data->pairs[i][j].piece); |
---|
703 | data->pairs[i][j].piece = NULL; |
---|
704 | } |
---|
705 | |
---|
706 | mash->nextmash = data->mashes; |
---|
707 | data->mashes = mash; |
---|
708 | |
---|
709 | /* Create new pieces where necessary */ |
---|
710 | for (int j = 0; j < data->dim.j; j++) for (int i = 0; i < data->dim.i; i++) |
---|
711 | { |
---|
712 | if (list[i][j] == -1) |
---|
713 | continue; |
---|
714 | if (count[list[i][j]] < 3) |
---|
715 | { |
---|
716 | if (!data->pairs[i][j].piece) |
---|
717 | data->pairs[i][j].id = 0; |
---|
718 | continue; |
---|
719 | } |
---|
720 | |
---|
721 | data->pairs[i][j].id++; |
---|
722 | if (data->pairs[i][j].id > data->npieces |
---|
723 | && data->pairs[i][j].id <= data->maxnpieces) |
---|
724 | { |
---|
725 | data->npieces++; |
---|
726 | data->thumbs->SetMax(data->npieces); |
---|
727 | } |
---|
728 | data->pairs[i][j].piece = new Piece(data->emitter, int2(i, j), 80 + 20 * data->pairs[i][j].id); |
---|
729 | Ticker::Ref(data->pairs[i][j].piece); |
---|
730 | data->pairs[i][j].piece->SetPos(int2(i, j) * 48); |
---|
731 | data->thumbs->AddCount(data->pairs[i][j].id, 1); |
---|
732 | count[list[i][j]] = 0; |
---|
733 | list[i][j] = -1; |
---|
734 | } |
---|
735 | |
---|
736 | /* Move everything down */ |
---|
737 | for (int j = data->dim.j; j--;) for (int i = 0; i < data->dim.i; i++) |
---|
738 | { |
---|
739 | if (list[i][j] == -1 || data->pairs[i][j].piece) |
---|
740 | continue; |
---|
741 | |
---|
742 | for (int j2 = j + 1; j2 < data->dim.j; j2++) |
---|
743 | { |
---|
744 | data->pairs[i][j2 - 1] = data->pairs[i][j2]; |
---|
745 | if (data->pairs[i][j2 - 1].id) |
---|
746 | { |
---|
747 | data->pairs[i][j2 - 1].piece->SetCell(int2(i, j2 - 1)); |
---|
748 | data->pairs[i][j2 - 1].piece->Move(int2(i, j2 - 1) * 48); |
---|
749 | } |
---|
750 | list[i][j2 - 1] = list[i][j2]; |
---|
751 | } |
---|
752 | |
---|
753 | data->pairs[i][data->dim.j - 1].id = 0; |
---|
754 | list[i][data->dim.j - 1] = -1; |
---|
755 | } |
---|
756 | |
---|
757 | /* Start again (FIXME: make this a while() loop) */ |
---|
758 | Resolve(); |
---|
759 | } |
---|
760 | |
---|
761 | int Board::TagNeighbours(int list[MAX_PIECES][MAX_PIECES], int i, int j) |
---|
762 | { |
---|
763 | int2 const off[] = { int2(-1, 0), int2(1, 0), int2(0, -1), int2(0, 1) }; |
---|
764 | |
---|
765 | int count = 1; |
---|
766 | |
---|
767 | for (int n = 0; n < 4; n++) |
---|
768 | { |
---|
769 | int i2 = i + off[n].i; |
---|
770 | int j2 = j + off[n].j; |
---|
771 | |
---|
772 | if (i2 >= 0 && i2 < data->dim.i && j2 >= 0 && j2 < data->dim.j |
---|
773 | && data->pairs[i2][j2].id == data->pairs[i][j].id |
---|
774 | && list[i2][j2] == -1) |
---|
775 | { |
---|
776 | list[i2][j2] = list[i][j]; |
---|
777 | count += TagNeighbours(list, i2, j2); |
---|
778 | } |
---|
779 | } |
---|
780 | return count; |
---|
781 | } |
---|
782 | |
---|
783 | Board::~Board() |
---|
784 | { |
---|
785 | Input::UntrackMouse(this); |
---|
786 | |
---|
787 | switch (data->game) |
---|
788 | { |
---|
789 | case GAME_HUNT: |
---|
790 | for (int j = 0; j < data->dim.j; j++) |
---|
791 | for (int i = 0; i < data->dim.i; i++) |
---|
792 | { |
---|
793 | data->pairs[i][j].piece->SetBelow(NULL); |
---|
794 | Ticker::Unref(data->pairs[i][j].piece); |
---|
795 | } |
---|
796 | break; |
---|
797 | case GAME_FUSION: |
---|
798 | for (int j = 0; j < data->dim.j; j++) |
---|
799 | for (int i = 0; i < data->dim.i; i++) |
---|
800 | if (data->pairs[i][j].id) |
---|
801 | Ticker::Unref(data->pairs[i][j].piece); |
---|
802 | Ticker::Unref(data->current[0].piece); |
---|
803 | Ticker::Unref(data->current[1].piece); |
---|
804 | break; |
---|
805 | } |
---|
806 | Ticker::Unref(data->thumbs); |
---|
807 | Ticker::Unref(data->scoretext); |
---|
808 | while (data->mashes) |
---|
809 | { |
---|
810 | Ticker::Unref(data->mashes); |
---|
811 | data->mashes = data->mashes->nextmash; |
---|
812 | } |
---|
813 | /* FIXME: the emitter may be destroyed after the Tiler is removed, |
---|
814 | * because the last Tiler tick may be done just after the emitter |
---|
815 | * scheduled its sprites! */ |
---|
816 | Ticker::Unref(data->emitter); |
---|
817 | Tiler::Deregister(data->board); |
---|
818 | Tiler::Deregister(data->tiles); |
---|
819 | Tiler::Deregister(data->icons); |
---|
820 | Sampler::Deregister(data->click); |
---|
821 | Sampler::Deregister(data->whip); |
---|
822 | delete data; |
---|
823 | } |
---|
824 | |
---|