source: trunk/monsterz/piece.cpp @ 750

Last change on this file since 750 was 750, checked in by sam, 11 years ago

monsterz: do not display mouse pointer or grab handle on touch devices.

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