source: trunk/monsterz/piece.cpp @ 384

Last change on this file since 384 was 384, checked in by sam, 10 years ago

Make the Board class more generic by allowing non-48x48 tiles.

File size: 8.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
18#include "core.h"
19#include "piece.h"
20#include "monsterz.h"
21
22/*
23 * Piece implementation class
24 */
25
26class PieceData
27{
28    friend class Piece;
29
30private:
31    Piece::piece_t piece;
32
33    int tiler;
34    Emitter *emitter;
35    Piece *above, *below;
36    int2 size, cell, pos, src, dst;
37    int id;
38    float speed, timer, blink, idle;
39
40    enum
41    {
42        IDLE,
43        BLINK,
44        GRAB,
45        UNGRAB,
46        MOVE,
47        POP,
48        DEAD,
49    }
50    state;
51};
52
53/*
54 * Public Piece class
55 */
56
57Piece::Piece(piece_t piece, Emitter *emitter, int2 cell, int id)
58  : data(new PieceData())
59{
60    data->piece = piece;
61
62    data->emitter = emitter;
63    Ticker::Ref(emitter);
64    data->above = NULL;
65    data->below = NULL;
66    data->cell = cell;
67    data->state = PieceData::IDLE;
68    data->timer = RandF(DELAY_IDLE);
69    data->blink = RandF(DELAY_BLINK);
70    data->idle = RandF(DELAY_IDLE, 2 * DELAY_IDLE);
71
72    switch (data->piece)
73    {
74    case PIECE_HUNT:
75        data->size = 48;
76        data->tiler = Tiler::Register(PNG_TILES,
77                                      data->size.x, data->size.y, 1.0f);
78        data->id = 80 + 20 * id;
79        break;
80    case PIECE_FUSION:
81        data->size = 40;
82        data->tiler = Tiler::Register(PNG_ELEMENTS,
83                                      data->size.x, data->size.y, 1.0f);
84        data->id = id - 1;
85        break;
86    }
87
88    data->pos = data->cell * data->size;
89}
90
91void Piece::SetCell(int2 cell)
92{
93    data->cell = cell;
94}
95
96int2 Piece::GetCell() const
97{
98    return data->cell;
99}
100
101void Piece::SetPos(int2 pos)
102{
103    data->pos = pos;
104}
105
106int2 Piece::GetPos() const
107{
108    return data->pos;
109}
110
111int2 Piece::GetSize() const
112{
113    return data->size;
114}
115
116int2 Piece::GetShift() const
117{
118    return data->pos - data->cell * data->size;
119}
120
121void Piece::SetAbove(Piece *above)
122{
123    Piece *old = data->above;
124
125    if (above == old)
126        return;
127
128    if (old)
129        Ticker::Unref(old);
130    data->above = above;
131    if (above)
132        Ticker::Ref(above);
133
134    if (old)
135        old->SetBelow(NULL);
136    if (above)
137        above->SetBelow(this);
138}
139
140Piece *Piece::GetAbove() const
141{
142    return data->above;
143}
144
145void Piece::SetBelow(Piece *below)
146{
147    Piece *old = data->below;
148
149    if (below == old)
150        return;
151
152    if (old)
153        Ticker::Unref(old);
154    data->below = below;
155    if (below)
156        Ticker::Ref(below);
157
158    if (old)
159        old->SetAbove(NULL);
160    if (below)
161        below->SetAbove(this);
162}
163
164Piece *Piece::GetBelow() const
165{
166    return data->below;
167}
168
169int Piece::IsDead() const
170{
171    return data->state == PieceData::DEAD;
172}
173
174int Piece::Pop()
175{
176    switch (data->state)
177    {
178    case PieceData::IDLE:
179    case PieceData::BLINK:
180    case PieceData::GRAB:
181    case PieceData::UNGRAB:
182    case PieceData::MOVE:
183    case PieceData::POP:
184        data->state = PieceData::POP;
185        data->timer = DELAY_DUH + DELAY_POP;
186        return 1;
187    default:
188        return 0;
189    }
190}
191
192int Piece::Grab(int2 dir)
193{
194    switch (data->state)
195    {
196    case PieceData::UNGRAB:
197    case PieceData::MOVE:
198        /* Only allow swaps when piece is close enough to its target cell. */
199        if ((data->cell * data->size - data->pos).sqlen()
200              > data->size.sqlen() / 8)
201            return 0;
202        /* Fall through */
203    case PieceData::IDLE:
204    case PieceData::BLINK:
205    case PieceData::GRAB:
206        data->state = PieceData::GRAB;
207        data->pos = data->pos + dir;
208        return 1;
209    default:
210        return 0;
211    }
212}
213
214int Piece::Ungrab(int2 pos)
215{
216    switch (data->state)
217    {
218    case PieceData::IDLE:
219    case PieceData::BLINK:
220    case PieceData::GRAB:
221    case PieceData::UNGRAB:
222    case PieceData::MOVE:
223        data->state = PieceData::UNGRAB;
224        data->speed = 0.4f;
225        data->timer = 0.0f;
226        data->src = data->pos;
227        data->dst = pos;
228        return 1;
229    default:
230        return 0;
231    }
232}
233
234int Piece::Move(int2 pos)
235{
236    switch (data->state)
237    {
238    case PieceData::IDLE:
239    case PieceData::BLINK:
240    case PieceData::UNGRAB:
241    case PieceData::MOVE:
242        data->state = PieceData::MOVE;
243        data->speed = 0.3f;
244        data->timer = 0.0f;
245        data->src = data->pos;
246        data->dst = pos;
247        return 1;
248    case PieceData::GRAB:
249    default:
250        return 0;
251    }
252}
253
254void Piece::TickGame(float deltams)
255{
256    switch (data->state)
257    {
258    case PieceData::IDLE:
259        data->timer -= deltams;
260        if (data->timer < 0.0f)
261            data->timer = data->idle = RandF(DELAY_IDLE, 2 * DELAY_IDLE);
262        data->blink -= deltams;
263        if (data->blink < 0.0f)
264        {
265            data->state = PieceData::BLINK;
266            data->timer = DURATION_BLINK;
267        }
268        break;
269    case PieceData::BLINK:
270        data->timer -= deltams;
271        if (data->timer < 0.0f)
272        {
273            data->state = PieceData::IDLE;
274            data->timer = data->idle = RandF(DELAY_IDLE, 2 * DELAY_IDLE);
275            data->blink = RandF(DELAY_BLINK, 2 * DELAY_BLINK);
276        }
277        break;
278    case PieceData::GRAB:
279        break;
280    case PieceData::UNGRAB:
281    case PieceData::MOVE:
282    {
283        data->timer += deltams;
284        int2 trip = data->dst - data->src;
285        float moving_time = trip.len() / data->speed;
286        float t = moving_time ? data->timer / moving_time : 1.0f;
287        if (t >= 1.0f)
288            t = 1.0f;
289        data->pos = data->src + (t * trip + 0.5f);
290        /* If the piece below is blocking us, clamp our position and
291         * start falling again. */
292        if (data->state == PieceData::MOVE && data->below
293             && data->pos.y < data->below->data->pos.y + data->size.y)
294        {
295            data->pos.y = data->below->data->pos.y + data->size.y;
296            data->src = data->pos;
297            data->timer = 0.0f;
298        }
299        if (data->timer > moving_time + 200.0f)
300        {
301            data->state = PieceData::IDLE;
302            data->timer = data->idle = RandF(DELAY_IDLE, 2 * DELAY_IDLE);
303        }
304        break;
305    }
306    case PieceData::POP:
307        data->timer -= deltams;
308        if (data->timer < 0.0f)
309        {
310            float3 pos(data->pos.x + 24, data->pos.y + 72, 5);
311            int start = data->id + 12;
312            int stop = data->id + 15;
313            for (int id = start; id < stop; id++)
314            {
315                float angle = RandF(-1.2f, 1.2f);
316                float speed = RandF(0.3f, 0.5f);
317                float3 vel(speed * sinf(angle), speed * cosf(angle), 0.0f);
318                data->emitter->AddParticle(id, pos, vel);
319            }
320
321            data->state = PieceData::DEAD;
322        }
323        break;
324    case PieceData::DEAD:
325        break;
326    }
327    Entity::TickGame(deltams);
328}
329
330void Piece::TickDraw(float deltams)
331{
332    Entity::TickDraw(deltams);
333
334    int id = data->id, off = 0;
335    int x = data->pos.x + 24;
336    int y = data->pos.y + 72;
337    int z = 2;
338
339    /* Optional modifiers */
340    switch (data->state)
341    {
342    case PieceData::IDLE:
343    {
344        off = data->timer * 3 / data->idle;
345        if (off < 0) off = 0;
346        if (off > 2) off = 2;
347        break;
348    }
349    case PieceData::BLINK:
350        off = 3;
351        break;
352    case PieceData::GRAB:
353        off = 3;
354        z = 4;
355        break;
356    case PieceData::UNGRAB:
357        if ((data->cell * data->size - data->src).sqlen()
358              > data->size.sqlen() / 8)
359            off = 7;
360        z = 4;
361        break;
362    case PieceData::MOVE:
363        z = 3;
364        break;
365    case PieceData::POP:
366    {
367        off = 4 - (int)(data->timer * 5 / DELAY_POP);
368        if (off < 0) off = 0;
369        if (off > 4) off = 4;
370        off += 7;
371        break;
372    }
373    case PieceData::DEAD:
374        break;
375    }
376
377    /* Only change image if the required frame is available */
378    switch (data->piece)
379    {
380    case PIECE_HUNT:
381        id += off;
382        break;
383    case PIECE_FUSION:
384        break;
385    }
386
387    if (data->state != PieceData::DEAD)
388        Scene::GetDefault()->AddTile((data->tiler << 16) | id, x, y, z, 0);
389
390    if (data->state == PieceData::GRAB)
391        Scene::GetDefault()->AddTile((data->tiler << 16) | 0, x, y, 5, 0);
392}
393
394Piece::~Piece()
395{
396    if (data->above)
397        Ticker::Unref(data->above);
398    if (data->below)
399        Ticker::Unref(data->below);
400    Ticker::Unref(data->emitter);
401    Tiler::Deregister(data->tiler);
402    delete data;
403}
404
Note: See TracBrowser for help on using the repository browser.