source: trunk/monsterz/board.cpp @ 746

Last change on this file since 746 was 746, checked in by sam, 10 years ago

monsterz: perform move as soon as the grabbed piece is close to its
destination cell.

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