source: trunk/monsterz/board.cpp @ 735

Last change on this file since 735 was 735, checked in by sam, 11 years ago

core: get rid of now useless <cstdio> includes.

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