source: trunk/monsterz/board.cpp @ 289

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

Change the way the Scene object works.

  • 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 = DELAY_WHIP;
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    Scene::GetDefault()->AddTile((data->screen << 16) | 0, 0, 1050, 0, 0);
211    Scene::GetDefault()->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        Scene::GetDefault()->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.