source: trunk/monsterz/board.cpp @ 274

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

Implement above/below pieces.

  • Property svn:keywords set to Id
File size: 8.8 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;
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;
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
70    srand(rand() ^ time(NULL));
71
72restart:
73    for (int j = 0; j < 8; j++)
74        for (int i = 0; i < 8; i++)
75        {
76            int id = rand() % 7;
77            data->pieces[i][j] = new Piece(game, Int2(i, j), 25 + 5 * id);
78            if (j)
79                data->pieces[i][j]->SetBelow(data->pieces[i][j - 1]);
80            Ticker::Ref(data->pieces[i][j]);
81        }
82
83    int list[8][8];
84    if (ListMashes(list))
85    {
86        for (int j = 0; j < 8; j++)
87            for (int i = 0; i < 8; i++)
88            {
89                data->pieces[i][j]->SetBelow(NULL);
90                Ticker::Unref(data->pieces[i][j]);
91            }
92        goto restart;
93    }
94
95    data->mashes = NULL;
96    data->nextblink = 0.0f;
97    data->state = BoardData::IDLE;
98}
99
100void Board::TickGame(float deltams)
101{
102    Entity::TickGame(deltams);
103
104    Int2 mouse = Input::GetMousePos();
105    Int3 buttons = Input::GetMouseButtons();
106
107    /* If possible, make a random monster blink */
108    if ((data->nextblink -= deltams) < 0.0f)
109    {
110        data->pieces[rand() % 8][rand() % 8]->Blink();
111        data->nextblink = (float)(200 + rand() % 500);
112    }
113
114    /* Get rid of finished mashes */
115    for (Mash **it = &data->mashes; *it; )
116    {
117        if ((*it)->IsDead())
118        {
119            Ticker::Unref(*it);
120            *it = (*it)->nextmash;
121        }
122        else
123            it = &(*it)->nextmash;
124    }
125
126    switch (data->state)
127    {
128    case BoardData::IDLE:
129        /* Should we start dragging something? */
130        if (buttons[0] && !data->buttons[0])
131        {
132            int x = mouse.x - 24;
133            int y = mouse.y - 72;
134            if (x >= 0 && x < 8 * 48 && y >= 0 && y < 8 * 48)
135            {
136                if (data->pieces[x / 48][y / 48]->Grab(Int2(0, 0)))
137                {
138                    Sampler::PlaySample(data->click);
139                    data->grabbed = data->pieces[x / 48][y / 48];
140                    data->src_cell = Int2(x / 48, y / 48);
141                    data->dst_cell = Int2(-1);
142                    data->state = BoardData::GRAB;
143                }
144                else
145                    data->state = BoardData::BADCLICK;
146            }
147            else
148                data->state = BoardData::BADCLICK;
149        }
150        break;
151    case BoardData::GRAB:
152        /* Should we stop dragging? */
153        if (mouse.x >= 0 && mouse.y >= 0)
154        {
155            data->grabbed->Grab(mouse - data->mouse);
156            Int2 cur_pos = data->grabbed->GetPos();
157            Int2 cur_cell = (cur_pos + 24) / 48;
158            if (cur_cell.i < 0 || cur_cell.i >= 8
159                 || cur_cell.j < 0 || cur_cell.j >= 8
160                 || (cur_pos - cur_cell * 48).sqlen() > 24 * 24
161                 || (cur_cell - data->src_cell).sqlen() != 1)
162                cur_cell = Int2(-1);
163            /* If potential target changed, update our cache. */
164            if (cur_cell != data->dst_cell)
165            {
166                if (data->dst_cell != Int2(-1))
167                    data->pieces[data->dst_cell.i]
168                                [data->dst_cell.j]->Goto(data->dst_cell * 48,
169                                                         0.3f);
170                if (cur_cell != Int2(-1))
171                    data->pieces[cur_cell.i]
172                                [cur_cell.j]->Goto(data->src_cell * 48, 0.3f);
173                data->dst_cell = cur_cell;
174            }
175        }
176        if (!buttons[0] || mouse.x < 0 || mouse.y < 0
177             || (data->src_cell * 48
178                  - data->grabbed->GetPos()).sqlen() > 100 * 100)
179        {
180            data->grabbed->Ungrab();
181            if (data->dst_cell != Int2(-1))
182                Switch(data->src_cell, data->dst_cell);
183            data->state = BoardData::IDLE;
184        }
185        break;
186    case BoardData::BADCLICK:
187        if (!buttons[0])
188            data->state = BoardData::IDLE;
189        break;
190    }
191
192    data->mouse = mouse;
193    data->buttons = buttons;
194}
195
196void Board::TickDraw(float deltams)
197{
198    Entity::TickDraw(deltams);
199
200    data->game->GetScene()->AddTile((data->screen << 16) | 0, 0, 1050, 0, 0);
201    data->game->GetScene()->AddTile((data->board << 16) | 0, 24, 912, 1, 0);
202
203    if (data->mouse.x >= 0 && data->mouse.y >= 0)
204    {
205        int x = data->mouse.x - 2;
206        int y = data->mouse.y + 59;
207        data->game->GetScene()->AddTile((data->tiles << 16) | 7, x, y, 10, 0);
208    }
209}
210
211void Board::Switch(Int2 cell_a, Int2 cell_b)
212{
213    Piece *a = data->pieces[cell_a.i][cell_a.j];
214    Piece *b = data->pieces[cell_b.i][cell_b.j];
215    data->pieces[cell_a.i][cell_a.j] = b;
216    data->pieces[cell_b.i][cell_b.j] = a;
217
218    /* Check whether this is a valid move by testing all patterns.
219     * If the move is invalid, cancel the swap and bail out */
220    int list[8][8];
221
222    if (!ListMashes(list))
223    {
224        data->pieces[cell_a.i][cell_a.j] = a;
225        data->pieces[cell_b.i][cell_b.j] = b;
226        a->Goto(cell_a * 48, 0.3f);
227        b->Goto(cell_b * 48, 0.3f);
228        return;
229    }
230
231    /* Perform the swap */
232    a->SetCell(cell_b);
233    a->Goto(cell_b * 48, 0.3f);
234    b->SetCell(cell_a);
235    b->Goto(cell_a * 48, 0.3f);
236
237    /* Remove matching pieces */
238    do
239    {
240        Mash *mash = new Mash(data->game);
241        Ticker::Ref(mash);
242
243        for (int j = 8; j--;) for (int i = 0; i < 8; i++)
244        {
245            if (!list[i][j])
246                continue;
247
248            /* The mash becomes the new owner of the disappearing piece */
249            mash->AddPiece(data->pieces[i][j]);
250
251            Piece *below = data->pieces[i][7];
252
253            for (int j2 = j + 1; j2 < 8; j2++)
254            {
255                data->pieces[i][j2 - 1] = data->pieces[i][j2];
256                data->pieces[i][j2 - 1]->SetCell(Int2(i, j2 - 1));
257                data->pieces[i][j2 - 1]->Goto(Int2(i, j2 - 1) * 48, 0.1f);
258                list[i][j2 - 1] = list[i][j2];
259            }
260            Int2 newpos = Int2(i * 48, below->GetPos().y + 48);
261            data->pieces[i][7] = new Piece(data->game, Int2(i, 7), 25 + 5 * (rand() % 7));
262            data->pieces[i][7]->SetBelow(below);
263            data->pieces[i][7]->SetPos(newpos);
264            data->pieces[i][7]->Goto(Int2(i, 7) * 48, 0.1f);
265            Ticker::Ref(data->pieces[i][7]);
266            list[i][7] = 0;
267        }
268
269        mash->nextmash = data->mashes;
270        data->mashes = mash;
271    }
272    while(ListMashes(list));
273}
274
275int Board::ListMashes(int list[8][8])
276{
277    int ret = 0;
278
279    for (int j = 0; j < 8; j++)
280        for (int i = 0; i < 8; i++)
281            list[i][j] = 0;
282
283    for (int j = 0; j < 8; j++)
284        for (int i = 0; i < 8; i++)
285        {
286            int id = data->pieces[i][j]->GetId();
287
288            if (i + 2 < 8 && data->pieces[i + 1][j]->GetId() == id
289                          && data->pieces[i + 2][j]->GetId() == id)
290            {
291                list[i][j] = list[i + 1][j] = list[i + 2][j] = 1;
292                ret = 1;
293            }
294
295            if (j + 2 < 8 && data->pieces[i][j + 1]->GetId() == id
296                          && data->pieces[i][j + 2]->GetId() == id)
297            {
298                list[i][j] = list[i][j + 1] = list[i][j + 2] = 1;
299                ret = 1;
300            }
301        }
302
303    return ret;
304}
305
306Board::~Board()
307{
308    Ticker::Unref(data->game);
309    for (int j = 0; j < 8; j++)
310        for (int i = 0; i < 8; i++)
311        {
312            data->pieces[i][j]->SetBelow(NULL);
313            Ticker::Unref(data->pieces[i][j]);
314        }
315    while (data->mashes)
316    {
317        Ticker::Unref(data->mashes);
318        data->mashes = data->mashes->nextmash;
319    }
320    Tiler::Deregister(data->board);
321    Tiler::Deregister(data->screen);
322    Tiler::Deregister(data->tiles);
323    Sampler::Deregister(data->click);
324    delete data;
325}
326
Note: See TracBrowser for help on using the repository browser.