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