source: trunk/monsterz/piece.cpp @ 313

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

Rework tile handling. This possibly breaks deushax pretty rudely.

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