source: trunk/monsterz/piece.cpp @ 633

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

TileSets can now be initialised either using the tile size, or the
known number of tiles in a row and a column. Necessary for ticket #24.

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