source: trunk/monsterz/board.cpp @ 752

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

monsterz: do not display the interface in the title screen.

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