source: trunk/monsterz/board.cpp @ 284

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

New exploding sprites by Luc. Added to the data but not yet to the code.

  • Property svn:keywords set to Id
File size: 10.2 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 <cstdio>
16#include <cmath>
17#include <cstdlib>
18#include <ctime>
19
20#include "core.h"
21#include "board.h"
22#include "piece.h"
23#include "mash.h"
24#include "monsterz.h"
25
26/*
27 * Board implementation class
28 */
29
30class BoardData
31{
32    friend class Board;
33
34private:
35    Game *game;
36    int screen, board, tiles;
37    int click, whip;
38    Piece *pieces[8][8];
39    Piece *grabbed;
40    Mash *mashes;
41    Int2 src_cell, dst_cell;
42
43    Int2 mouse;
44    Int3 buttons;
45    float nextblink, whipdelay;
46
47    enum
48    {
49        IDLE,
50        BADCLICK,
51        GRAB,
52    }
53    state;
54};
55
56/*
57 * Public Board class
58 */
59
60Board::Board(Game *game)
61  : data(new BoardData())
62{
63    data->game = game;
64    Ticker::Ref(game);
65    data->screen = Tiler::Register(PNG_BACKGROUND, 640, 480, 1.0f);
66    data->board = Tiler::Register(PNG_BOARD, 384, 384, 1.0f);
67    data->tiles = Tiler::Register(PNG_TILES, 48, 48, 1.0f);
68    data->click = Sampler::Register(WAV_CLICK);
69    data->whip = Sampler::Register(WAV_WHIP);
70
71    srand(rand() ^ time(NULL));
72
73restart:
74    for (int j = 0; j < 8; j++)
75        for (int i = 0; i < 8; i++)
76        {
77            int id = 100 + 20 * (rand() % 7);
78            data->pieces[i][j] = new Piece(game, Int2(i, j), id);
79            if (j)
80                data->pieces[i][j]->SetBelow(data->pieces[i][j - 1]);
81            Ticker::Ref(data->pieces[i][j]);
82        }
83
84    int list[8][8];
85    if (ListMashes(list))
86    {
87        for (int j = 0; j < 8; j++)
88            for (int i = 0; i < 8; i++)
89            {
90                data->pieces[i][j]->SetBelow(NULL);
91                Ticker::Unref(data->pieces[i][j]);
92            }
93        goto restart;
94    }
95
96    data->mashes = NULL;
97    data->nextblink = 0.0f;
98    data->whipdelay = 0.0f;
99    data->state = BoardData::IDLE;
100}
101
102void Board::TickGame(float deltams)
103{
104    Entity::TickGame(deltams);
105
106    Int2 mouse = Input::GetMousePos();
107    Int3 buttons = Input::GetMouseButtons();
108
109    /* If possible, make a random monster blink */
110    if ((data->nextblink -= deltams) < 0.0f)
111    {
112        data->pieces[rand() % 8][rand() % 8]->Blink();
113        data->nextblink = (float)(200 + rand() % 500);
114    }
115
116    /* Do not whip too often, the sound may become annoying */
117    data->whipdelay -= deltams;
118
119    /* Get rid of finished mashes */
120    for (Mash **it = &data->mashes; *it; )
121    {
122        if ((*it)->IsDead())
123        {
124            Ticker::Unref(*it);
125            *it = (*it)->nextmash;
126        }
127        else
128            it = &(*it)->nextmash;
129    }
130
131    switch (data->state)
132    {
133    case BoardData::IDLE:
134        /* Should we start dragging something? */
135        if (buttons[0] && !data->buttons[0])
136        {
137            int x = mouse.x - 24;
138            int y = mouse.y - 72;
139            if (x >= 0 && x < 8 * 48 && y >= 0 && y < 8 * 48)
140            {
141                if (data->pieces[x / 48][y / 48]->Grab(Int2(0, 0)))
142                {
143                    Sampler::PlaySample(data->click);
144                    data->grabbed = data->pieces[x / 48][y / 48];
145                    data->src_cell = Int2(x / 48, y / 48);
146                    data->dst_cell = Int2(-1);
147                    data->state = BoardData::GRAB;
148                }
149                else
150                    data->state = BoardData::BADCLICK;
151            }
152            else
153                data->state = BoardData::BADCLICK;
154        }
155        break;
156    case BoardData::GRAB:
157        if (mouse.x >= 0 && mouse.y >= 0)
158        {
159            /* Mouse is still in the window, keep grabbing */
160            data->grabbed->Grab(mouse - data->mouse);
161            Int2 cur_pos = data->grabbed->GetPos();
162            Int2 cur_cell = (cur_pos + 24) / 48;
163            if (cur_cell.i < 0 || cur_cell.i >= 8
164                 || cur_cell.j < 0 || cur_cell.j >= 8
165                 || (cur_pos - cur_cell * 48).sqlen() > 24 * 24
166                 || (cur_cell - data->src_cell).sqlen() != 1)
167                cur_cell = Int2(-1);
168            /* If potential target changed, update our cache. */
169            if (cur_cell != data->dst_cell)
170            {
171                if (data->whipdelay < 0.0f)
172                {
173                    Sampler::PlaySample(data->whip);
174                    data->whipdelay = 300.0f;
175                }
176                if (data->dst_cell != Int2(-1))
177                    data->pieces[data->dst_cell.i]
178                                [data->dst_cell.j]->Ungrab(data->dst_cell * 48);
179                if (cur_cell != Int2(-1))
180                    data->pieces[cur_cell.i]
181                                [cur_cell.j]->Ungrab(data->src_cell * 48);
182                data->dst_cell = cur_cell;
183            }
184        }
185        if (!buttons[0] || mouse.x < 0 || mouse.y < 0
186             || (data->src_cell * 48
187                  - data->grabbed->GetPos()).sqlen() > 100 * 100)
188        {
189            /* Mouse released, or exited window, or dragged too far. */
190            data->grabbed->Ungrab(data->grabbed->GetCell() * 48);
191            if (data->dst_cell != Int2(-1))
192                Switch(data->src_cell, data->dst_cell);
193            data->state = BoardData::IDLE;
194        }
195        break;
196    case BoardData::BADCLICK:
197        if (!buttons[0])
198            data->state = BoardData::IDLE;
199        break;
200    }
201
202    data->mouse = mouse;
203    data->buttons = buttons;
204}
205
206void Board::TickDraw(float deltams)
207{
208    Entity::TickDraw(deltams);
209
210    data->game->GetScene()->AddTile((data->screen << 16) | 0, 0, 1050, 0, 0);
211    data->game->GetScene()->AddTile((data->board << 16) | 0, 24, 912, 1, 0);
212
213    if (data->mouse.x >= 0 && data->mouse.y >= 0)
214    {
215        int x = data->mouse.x - 2;
216        int y = data->mouse.y + 59;
217        data->game->GetScene()->AddTile((data->tiles << 16) | 22, x, y, 10, 0);
218    }
219}
220
221void Board::Switch(Int2 cell_a, Int2 cell_b)
222{
223    Piece *a = data->pieces[cell_a.i][cell_a.j];
224    Piece *b = data->pieces[cell_b.i][cell_b.j];
225    data->pieces[cell_a.i][cell_a.j] = b;
226    data->pieces[cell_b.i][cell_b.j] = a;
227
228    /* Check whether this is a valid move by testing all patterns.
229     * If the move is invalid, cancel the swap and bail out */
230    int list[8][8];
231
232    if (!ListMashes(list))
233    {
234        data->pieces[cell_a.i][cell_a.j] = a;
235        data->pieces[cell_b.i][cell_b.j] = b;
236        a->Ungrab(cell_a * 48);
237        b->Ungrab(cell_b * 48);
238        return;
239    }
240
241    /* Perform the swap */
242    a->SetCell(cell_b);
243    a->Ungrab(cell_b * 48);
244    b->SetCell(cell_a);
245    b->Ungrab(cell_a * 48);
246
247    /* Swap above and below cells */
248    if (cell_a.i == cell_b.i)
249    {
250        Piece *tmpa = a->GetAbove();
251        Piece *tmpb = b->GetAbove();
252        if (tmpb == a)
253        {
254            tmpb = b->GetBelow();
255            b->SetAbove(tmpa);
256            b->SetBelow(a);
257            a->SetBelow(tmpb);
258        }
259        else /* tmpa == b */
260        {
261            tmpa = a->GetBelow();
262            a->SetAbove(tmpb);
263            a->SetBelow(b);
264            b->SetBelow(tmpa);
265        }
266    }
267    else
268    {
269        Piece *tmpa = a->GetAbove();
270        Piece *tmpb = b->GetAbove();
271        a->SetAbove(tmpb);
272        b->SetAbove(tmpa);
273        tmpa = a->GetBelow();
274        tmpb = b->GetBelow();
275        a->SetBelow(tmpb);
276        b->SetBelow(tmpa);
277    }
278
279    /* Remove matching pieces and store them in Mash objects */
280    do
281    {
282        Mash *mash = new Mash(data->game);
283        Ticker::Ref(mash);
284
285        for (int j = 8; j--;) for (int i = 0; i < 8; i++)
286        {
287            if (!list[i][j])
288                continue;
289
290            /* The mash becomes the new owner of the disappearing piece */
291            mash->AddPiece(data->pieces[i][j]);
292
293            Piece *below = data->pieces[i][7];
294
295            /* Change coordinates for the whole column above */
296            for (int j2 = j + 1; j2 < 8; j2++)
297            {
298                data->pieces[i][j2 - 1] = data->pieces[i][j2];
299                data->pieces[i][j2 - 1]->SetCell(Int2(i, j2 - 1));
300                data->pieces[i][j2 - 1]->Move(Int2(i, j2 - 1) * 48);
301                list[i][j2 - 1] = list[i][j2];
302            }
303
304            /* Spawn a new piece above all the others and attach it to
305             * the board. */
306            Int2 newpos = Int2(i * 48, below->GetPos().y + 48);
307            int id = 100 + 20 * (rand() % 7);
308            data->pieces[i][7] = new Piece(data->game, Int2(i, 7), id);
309            data->pieces[i][7]->SetBelow(below);
310            data->pieces[i][7]->SetPos(newpos);
311            data->pieces[i][7]->Move(Int2(i, 7) * 48);
312            Ticker::Ref(data->pieces[i][7]);
313            list[i][7] = 0;
314        }
315
316        mash->nextmash = data->mashes;
317        data->mashes = mash;
318    }
319    while(ListMashes(list));
320}
321
322int Board::ListMashes(int list[8][8])
323{
324    int ret = 0;
325
326    for (int j = 0; j < 8; j++)
327        for (int i = 0; i < 8; i++)
328            list[i][j] = 0;
329
330    for (int j = 0; j < 8; j++)
331        for (int i = 0; i < 8; i++)
332        {
333            int id = data->pieces[i][j]->GetId();
334
335            if (i + 2 < 8 && data->pieces[i + 1][j]->GetId() == id
336                          && data->pieces[i + 2][j]->GetId() == id)
337            {
338                list[i][j] = list[i + 1][j] = list[i + 2][j] = 1;
339                ret = 1;
340            }
341
342            if (j + 2 < 8 && data->pieces[i][j + 1]->GetId() == id
343                          && data->pieces[i][j + 2]->GetId() == id)
344            {
345                list[i][j] = list[i][j + 1] = list[i][j + 2] = 1;
346                ret = 1;
347            }
348        }
349
350    return ret;
351}
352
353Board::~Board()
354{
355    Ticker::Unref(data->game);
356    for (int j = 0; j < 8; j++)
357        for (int i = 0; i < 8; i++)
358        {
359            data->pieces[i][j]->SetBelow(NULL);
360            Ticker::Unref(data->pieces[i][j]);
361        }
362    while (data->mashes)
363    {
364        Ticker::Unref(data->mashes);
365        data->mashes = data->mashes->nextmash;
366    }
367    Tiler::Deregister(data->board);
368    Tiler::Deregister(data->screen);
369    Tiler::Deregister(data->tiles);
370    Sampler::Deregister(data->click);
371    Sampler::Deregister(data->whip);
372    delete data;
373}
374
Note: See TracBrowser for help on using the repository browser.