source: trunk/monsterz/board.cpp @ 737

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

monsterz: some refactoring in the Piece and Board classes.

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