source: trunk/monsterz/board.cpp @ 293

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

Add pseudorandom functions for floats.

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