source: trunk/monsterz/board.cpp @ 258

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

Implement valid move checks and early piece disappearance.

  • Property svn:keywords set to Id
File size: 7.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 <cstdio>
16#include <cmath>
17#include <cstdlib>
18
19#include "core.h"
20#include "board.h"
21#include "piece.h"
22#include "monsterz.h"
23
24/*
25 * Board implementation class
26 */
27
28class BoardData
29{
30    friend class Board;
31
32private:
33    Game *game;
34    int screen, board, tiles;
35    int click;
36    Piece *pieces[8][8];
37    Piece *grabbed;
38    Int2 src_cell, dst_cell;
39
40    Int2 mouse;
41    Int3 buttons;
42    float nextblink;
43
44    enum
45    {
46        IDLE,
47        BADCLICK,
48        GRAB,
49    }
50    state;
51};
52
53/*
54 * Public Board class
55 */
56
57Board::Board(Game *game)
58{
59    data = new BoardData();
60    data->game = game;
61    Ticker::Ref(game);
62    data->screen = Tiler::Register(PNG_BACKGROUND, 640, 480, 1.0f);
63    data->board = Tiler::Register(PNG_BOARD, 384, 384, 1.0f);
64    data->tiles = Tiler::Register(PNG_TILES, 48, 48, 1.0f);
65    data->click = Sampler::Register(WAV_CLICK);
66
67    for (int j = 0; j < 8; j++)
68        for (int i = 0; i < 8; i++)
69        {
70            int id = (35 + i + (i ^ (j + 13)) * (2 * j + 711)) % 9;
71            data->pieces[i][j] = new Piece(game, Int2(i, j), 25 + 5 * id);
72            Ticker::Ref(data->pieces[i][j]);
73        }
74
75    data->nextblink = 0.0f;
76    data->state = BoardData::IDLE;
77}
78
79void Board::TickGame(float deltams)
80{
81    Entity::TickGame(deltams);
82
83    Int2 mouse = Input::GetMousePos();
84    Int3 buttons = Input::GetMouseButtons();
85
86    /* If possible, make a random monster blink */
87    if ((data->nextblink -= deltams) < 0.0f)
88    {
89        data->pieces[rand() % 8][rand() % 8]->Blink();
90        data->nextblink = (float)(200 + rand() % 500);
91    }
92
93    /* Should we start dragging something? */
94    switch (data->state)
95    {
96    case BoardData::IDLE:
97        if (buttons[0] && !data->buttons[0])
98        {
99            int x = mouse.x - 24;
100            int y = mouse.y - 72;
101            if (x >= 0 && x < 8 * 48 && y >= 0 && y < 8 * 48)
102            {
103                if (data->pieces[x / 48][y / 48]->Grab(Int2(0, 0)))
104                {
105                    Sampler::PlaySample(data->click);
106                    data->grabbed = data->pieces[x / 48][y / 48];
107                    data->src_cell = Int2(x / 48, y / 48);
108                    data->dst_cell = Int2(-1);
109                    data->state = BoardData::GRAB;
110                }
111                else
112                    data->state = BoardData::BADCLICK;
113            }
114            else
115                data->state = BoardData::BADCLICK;
116        }
117        break;
118    case BoardData::GRAB:
119        if (mouse.x >= 0 && mouse.y >= 0)
120        {
121            data->grabbed->Grab(mouse - data->mouse);
122            Int2 cur_pos = data->grabbed->GetPos();
123            Int2 cur_cell = (cur_pos + 24) / 48;
124            if (cur_cell.i < 0 || cur_cell.i >= 8
125                 || cur_cell.j < 0 || cur_cell.j >= 8
126                 || (cur_pos - cur_cell * 48).sqlen() > 24 * 24
127                 || (cur_cell - data->src_cell).sqlen() != 1)
128                cur_cell = Int2(-1);
129            /* If potential target changed, update our cache. */
130            if (cur_cell != data->dst_cell)
131            {
132                if (data->dst_cell != Int2(-1))
133                    data->pieces[data->dst_cell.i]
134                                [data->dst_cell.j]->Goto(data->dst_cell * 48);
135                if (cur_cell != Int2(-1))
136                    data->pieces[cur_cell.i]
137                                [cur_cell.j]->Goto(data->src_cell * 48);
138                data->dst_cell = cur_cell;
139            }
140        }
141        if (!buttons[0] || mouse.x < 0 || mouse.y < 0
142             || (data->src_cell * 48
143                  - data->grabbed->GetPos()).sqlen() > 100 * 100)
144        {
145            data->grabbed->Ungrab();
146            if (data->dst_cell != Int2(-1))
147                Switch(data->src_cell, data->dst_cell);
148            data->state = BoardData::IDLE;
149        }
150        break;
151    case BoardData::BADCLICK:
152        if (!buttons[0])
153            data->state = BoardData::IDLE;
154        break;
155    }
156
157    data->mouse = mouse;
158    data->buttons = buttons;
159}
160
161void Board::TickDraw(float deltams)
162{
163    Entity::TickDraw(deltams);
164
165    data->game->GetScene()->AddTile((data->screen << 16) | 0, 0, 1050, 0, 0);
166    data->game->GetScene()->AddTile((data->board << 16) | 0, 24, 912, 1, 0);
167
168    if (data->mouse.x >= 0 && data->mouse.y >= 0)
169    {
170        int x = data->mouse.x - 2;
171        int y = data->mouse.y + 59;
172        data->game->GetScene()->AddTile((data->tiles << 16) | 7, x, y, 10, 0);
173    }
174}
175
176void Board::Switch(Int2 cell_a, Int2 cell_b)
177{
178    Piece *a = data->pieces[cell_a.i][cell_a.j];
179    Piece *b = data->pieces[cell_b.i][cell_b.j];
180    data->pieces[cell_a.i][cell_a.j] = b;
181    data->pieces[cell_b.i][cell_b.j] = a;
182
183    /* Check whether this is a valid move by testing all patterns.
184     * If the move is invalid, cancel the swap and bail out */
185    int list[8][8];
186
187    if (!ListMashes(list))
188    {
189        data->pieces[cell_a.i][cell_a.j] = a;
190        data->pieces[cell_b.i][cell_b.j] = b;
191        a->Goto(cell_a * 48);
192        b->Goto(cell_b * 48);
193        return;
194    }
195
196    /* Perform the swap */
197    a->SetCell(cell_b);
198    a->Goto(cell_b * 48);
199    b->SetCell(cell_a);
200    b->Goto(cell_a * 48);
201
202    /* Remove matching tiles */
203    do for (int j = 8; j--;) for (int i = 0; i < 8; i++)
204    {
205        if (list[i][j])
206        {
207            Ticker::Unref(data->pieces[i][j]);
208            for (int j2 = j + 1; j2 < 8; j2++)
209            {
210                data->pieces[i][j2 - 1] = data->pieces[i][j2];
211                data->pieces[i][j2 - 1]->SetCell(Int2(i, j2 - 1));
212                data->pieces[i][j2 - 1]->Goto(Int2(i, j2 - 1) * 48);
213                list[i][j2 - 1] = list[i][j2];
214            }
215            data->pieces[i][7] = new Piece(data->game, Int2(i, 7), 25 + 5 * (rand() % 8));
216            Ticker::Ref(data->pieces[i][7]);
217            list[i][7] = 0;
218        }
219    }
220    while(ListMashes(list));
221}
222
223int Board::ListMashes(int list[8][8])
224{
225    int ret = 0;
226
227    for (int j = 0; j < 8; j++)
228        for (int i = 0; i < 8; i++)
229            list[i][j] = 0;
230
231    for (int j = 0; j < 8; j++)
232        for (int i = 0; i < 8; i++)
233        {
234            int id = data->pieces[i][j]->GetId();
235
236            if (i + 2 < 8 && data->pieces[i + 1][j]->GetId() == id
237                          && data->pieces[i + 2][j]->GetId() == id)
238            {
239                list[i][j] = list[i + 1][j] = list[i + 2][j] = 1;
240                ret = 1;
241            }
242
243            if (j + 2 < 8 && data->pieces[i][j + 1]->GetId() == id
244                          && data->pieces[i][j + 2]->GetId() == id)
245            {
246                list[i][j] = list[i][j + 1] = list[i][j + 2] = 1;
247                ret = 1;
248            }
249        }
250
251    return ret;
252}
253
254Board::~Board()
255{
256    Ticker::Unref(data->game);
257    for (int j = 0; j < 8; j++)
258        for (int i = 0; i < 8; i++)
259            Ticker::Unref(data->pieces[i][j]);
260    Tiler::Deregister(data->board);
261    Tiler::Deregister(data->screen);
262    Tiler::Deregister(data->tiles);
263    Sampler::Deregister(data->click);
264    delete data;
265}
266
Note: See TracBrowser for help on using the repository browser.