source: trunk/monsterz/piece.cpp @ 339

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

Fix an off-by-one error in animation frame computations.

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