source: trunk/monsterz/piece.cpp @ 1310

Last change on this file since 1310 was 1310, checked in by sam, 8 years ago

core: tick methods now use seconds, like any sane system.

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