source: trunk/monsterz/title.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: 12.0 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#if defined _XBOX
16#   define _USE_MATH_DEFINES /* for M_PI */
17#   include <xtl.h>
18#elif defined _WIN32
19#   define _USE_MATH_DEFINES /* for M_PI */
20#   define WIN32_LEAN_AND_MEAN
21#   include <windows.h>
22#endif
23
24#include <cmath>
25#include <cstdlib>
26#include <ctime>
27
28#include "core.h"
29
30using namespace std;
31using namespace lol;
32
33#include "monsterz.h"
34#include "title.h"
35
36/*
37 * Title implementation class
38 */
39
40class TitleData
41{
42    friend class Title;
43
44private:
45    TileSet *title, *stars, *clouds, *logo, *ground, *rocks;
46    TileSet *anim[6];
47    TileSet *event[7];
48
49    Sprite *logo_sprite;
50
51    enum
52    {
53        IDLE,
54        ANIM,
55        EVENT,
56    }
57    state;
58
59    enum
60    {
61        DAY,
62        NIGHT,
63        STEALTH,
64        RADIO,
65    }
66    period, nextperiod;
67
68    vec2 cloudpos[MAX_CLOUDS];
69    vec2 cloudspeed[MAX_CLOUDS];
70
71    ivec2 ground_pos, rocks_pos;
72
73    struct
74    {
75        TileSet *tiles;
76        int y;
77        float duration, timer;
78    }
79    eagle;
80
81    float timer, length;
82    int nframes, animid;
83
84    /* Sky shader test */
85    Gradient *gradient;
86};
87
88static ivec2 const animsize[] =
89{
90    ivec2(384, 384),
91    ivec2(284, 81),
92    ivec2(38, 146),
93    ivec2(29, 137),
94    ivec2(284, 82),
95    ivec2(384, 384),
96};
97
98static ivec2 const animpos[] =
99{
100    ivec2(0, 0),
101    ivec2(65, 255),
102    ivec2(239, 168),
103    ivec2(248, 168),
104    ivec2(63, 255),
105    ivec2(0, 0),
106};
107
108static ivec2 const eventsize[] =
109{
110    ivec2(143, 16),
111    ivec2(68, 49),
112    ivec2(17, 29),
113    ivec2(50, 80),
114    ivec2(237, 238),
115    ivec2(59, 53),
116    ivec2(140, 15),
117};
118
119static ivec2 const eventpos[] =
120{
121    ivec2(0, 322),
122    ivec2(316, 286),
123    ivec2(246, 245),
124    ivec2(279, 173),
125    ivec2(42, 101),
126    ivec2(231, 154),
127    ivec2(0, 322),
128};
129
130/*
131 * Public Title class
132 */
133
134Title::Title()
135  : data(new TitleData())
136{
137    /* FIXME: this should not be hardcoded */
138    m_position = ivec3(0, 0, 1);
139    m_bbox[0] = m_position;
140    m_bbox[1] = m_bbox[0] + vec3(640, 480, 0);
141
142    srand(rand() ^ time(NULL));
143
144    data->logo = Tiler::Register(PNG_TITLE_LOGO, ivec2(380, 181), ivec2(0));
145    data->logo_sprite = new Sprite(data->logo, 0);
146    data->logo_sprite->m_position = vec3(640 / 2 - 380 / 2, 250, 10);
147    Ticker::Ref(data->logo_sprite);
148
149    data->ground = Tiler::Register(PNG_TITLE_GROUND, ivec2(384, 80), ivec2(0));
150    data->ground_pos = ivec2((m_bbox[1] - m_bbox[0]).xy / vec2(2, 4))
151                     - ivec2(192, 80);
152
153    data->rocks = Tiler::Register(PNG_TITLE_ROCKS, ivec2(640, 155), ivec2(0));
154    data->rocks_pos = ivec2((m_bbox[1] - m_bbox[0]).xy / vec2(2, 2))
155                    - ivec2(320, 240);
156
157    data->title = Tiler::Register(PNG_TITLE, ivec2(384), ivec2(0));
158    data->stars = Tiler::Register(PNG_STARS, ivec2(384, 144), ivec2(0));
159    data->clouds = Tiler::Register(PNG_CLOUDS, ivec2(160, 32), ivec2(0));
160    data->eagle.tiles = Tiler::Register(PNG_EAGLE, ivec2(16), ivec2(0));
161    for (int n = 0; n < 6; n++)
162        data->anim[n] = Tiler::Register(PNG_TITLEANIM[n],
163                                        animsize[n], ivec2(0));
164    for (int n = 0; n < 7; n++)
165        data->event[n] = Tiler::Register(PNG_TITLEEVENT[n],
166                                         eventsize[n], ivec2(0));
167    data->state = TitleData::IDLE;
168    data->period = TitleData::DAY;
169    data->nextperiod = TitleData::DAY;
170    data->timer = RandF(2.0f, 3.0f);
171    /* Time needed for the eagle to cross the screen */
172    data->eagle.duration = (m_bbox[1].x - m_bbox[0].x + 16) * STEP_EAGLE;
173    data->eagle.timer = RandF(data->eagle.duration, 4 * data->eagle.duration);
174
175    for (int n = 0; n < MAX_CLOUDS; n++)
176    {
177        data->cloudpos[n] = vec2(RandF((m_bbox[1] - m_bbox[0]).x),
178                                 RandF(80) + (m_bbox[1] - m_bbox[0]).y - 300);
179        data->cloudspeed[n] = vec2(RandF(-10.f, 10.f));
180    }
181
182    data->eagle.y = 140 + rand() % 40;
183
184    data->gradient = new Gradient(vec3(0.0f, 0.0f, 0.0f),
185                                  vec3(640.0f, 480.0f, 0.0f));
186    Ticker::Ref(data->gradient);
187
188    Input::TrackMouse(this);
189}
190
191void Title::TickGame(float seconds)
192{
193    Entity::TickGame(seconds);
194
195    data->timer -= seconds;
196    data->eagle.timer -= seconds;
197
198    /* Probability of playing an animation given the current period */
199    static float const p1[] = { 0.6f, 0.3f, 0.0f, 0.2f };
200    /* Probability of a period change given the current period */
201    static float const p2[] = { 0.1f, 0.2f, 0.5f, 0.5f };
202    /* Number of frames in transition anims and events */
203    static int const t1[] = { 31, 26, 7, 7, 32, 6, 31 };
204    static int const t2[] = { 2, 4, 11, 4, 6, 2 };
205
206    for (int n = 0; n < MAX_CLOUDS; n++)
207    {
208        float wrap = m_bbox[1].x - m_bbox[0].x;
209        data->cloudpos[n].x += seconds * data->cloudspeed[n].x;
210        if (data->cloudpos[n].x > wrap)
211            data->cloudpos[n].x -= wrap;
212        else if (data->cloudpos[n].x < 0.0f)
213            data->cloudpos[n].x += wrap;
214    }
215
216    switch (data->state)
217    {
218    case TitleData::IDLE:
219        if (data->eagle.timer < 0.0f)
220        {
221            data->eagle.timer = RandF(data->eagle.duration, 4 * data->eagle.duration);
222            data->eagle.y = 140 + rand() % 40;
223        }
224        if (data->timer > 0.0f)
225            break;
226        if (RandF() < p1[data->period])
227        {
228            switch (data->period)
229            {
230            case TitleData::DAY:
231                data->animid = rand() % 4;
232                break;
233            case TitleData::NIGHT:
234                data->animid = (rand() % 2) ? 4 : 6;
235                break;
236            case TitleData::STEALTH:
237                /* XXX: we should not be here! */
238                break;
239            case TitleData::RADIO:
240                data->animid = 5;
241                break;
242            }
243            data->state = TitleData::EVENT;
244            data->nframes = t1[data->animid];
245            data->timer = data->length = data->nframes * .1f;
246        }
247        else if (RandF() < p2[data->period])
248        {
249            switch (data->period)
250            {
251            case TitleData::DAY:
252                data->nextperiod = TitleData::NIGHT;
253                data->animid = 0;
254                break;
255            case TitleData::NIGHT:
256                if (RandF() < 0.6f)
257                {
258                    data->nextperiod = TitleData::DAY;
259                    data->animid = 5;
260                }
261                else
262                {
263                    data->nextperiod = TitleData::STEALTH;
264                    data->animid = 1;
265                }
266                break;
267            case TitleData::STEALTH:
268                if (RandF() < 0.3f)
269                {
270                    data->nextperiod = TitleData::RADIO;
271                    data->animid = 2;
272                }
273                else
274                {
275                    data->nextperiod = TitleData::NIGHT;
276                    data->animid = 4;
277                }
278                break;
279            case TitleData::RADIO:
280                data->nextperiod = TitleData::STEALTH;
281                data->animid = 3;
282                break;
283            }
284            data->state = TitleData::ANIM;
285            data->nframes = t2[data->animid];
286            data->timer = data->length = data->nframes * .1f;
287        }
288        else
289        {
290            data->timer = RandF(500.0f, 1000.0f);
291        }
292        break;
293    case TitleData::ANIM:
294        if (data->timer < 0.0f)
295            data->period = data->nextperiod;
296            /* Fall through */
297    case TitleData::EVENT:
298        if (data->timer < 0.0f)
299        {
300            data->state = TitleData::IDLE;
301            data->timer = RandF(500.0f, 1000.0f);
302        }
303        break;
304    }
305}
306
307void Title::TickDraw(float seconds)
308{
309    Entity::TickDraw(seconds);
310
311    /* The background, always here. */
312    int backid = (int)data->period;
313    Scene::GetDefault()->AddTile(data->title, backid,
314              ivec3(data->ground_pos.x, data->ground_pos.y, 1), 0, vec2(1.0f));
315
316    /* The stars */
317    if (data->period != TitleData::DAY)
318    {
319        Scene::GetDefault()->AddTile(data->stars, 0,
320                                     ivec3(24 + 0, 72 + 240, 2), 0, vec2(1.0f));
321    }
322
323    /* The clouds. FIXME: tune color grading later */
324    int cloudoff = data->period == TitleData::DAY ? 0 : 3;
325
326    for (int n = 0; n < MAX_CLOUDS; n++)
327    {
328        /* There are only 5 cloud sprites */
329        int cloudid = (n % 5) * 4 + cloudoff;
330        Scene::GetDefault()->AddTile(data->clouds, cloudid,
331                                     ivec3(data->cloudpos[n].x,
332                                           data->cloudpos[n].y, 2), 0,
333                                     vec2(1.0f));
334        Scene::GetDefault()->AddTile(data->clouds, cloudid,
335                                     ivec3(data->cloudpos[n].x - m_bbox[1].x
336                                                               + m_bbox[0].x,
337                                           data->cloudpos[n].y, 2), 0,
338                                     vec2(1.0f));
339    }
340
341    /* Maybe an eagle? */
342    if (data->eagle.timer >= 0.0f)
343    {
344        int eagleid = (int)(data->eagle.timer * 10.f) % 6;
345        if (data->period != TitleData::DAY)
346            eagleid += 6;
347        float phase = sinf(data->eagle.timer * (2.0f * M_PI * 10.f / 6));
348        int x = (data->eagle.duration - data->eagle.timer) / STEP_EAGLE;
349        int y = data->eagle.y + 5.0f * sinf(phase);
350        Scene::GetDefault()->AddTile(data->eagle.tiles, eagleid,
351                                     ivec3(24 + x, 72 + y, 3), 0, vec2(1.0f));
352    }
353
354    /* The ground. */
355    for (int x = (data->ground_pos.x - 1) % 384 - 384;
356         x < Video::GetSize().x; x += 384)
357    {
358        Scene::GetDefault()->AddTile(data->ground, 0,
359                                     ivec3(x, data->ground_pos.y, 0), 0,
360                                     vec2(1.0f));
361    }
362
363    /* The rocks */
364    Scene::GetDefault()->AddTile(data->rocks, 0,
365                                 ivec3(data->rocks_pos, 10), 0,
366                                 vec2(1.0f));
367
368    /* Maybe an animation? */
369    TileSet *tiler = NULL;
370    int tileid = 0;
371    ivec2 pos = ivec2(0);
372
373    switch (data->state)
374    {
375    case TitleData::IDLE:
376        break;
377    case TitleData::ANIM:
378        tiler = data->anim[data->animid];
379        pos = animpos[data->animid];
380        pos.y = 384 - animsize[data->animid].y - pos.y; // Hack
381        tileid = (data->length - data->timer) * data->nframes / data->length;
382        if (tileid < 0) tileid = 0;
383        if (tileid > data->nframes - 1) tileid = data->nframes - 1;
384        break;
385    case TitleData::EVENT:
386        tiler = data->event[data->animid];
387        pos = eventpos[data->animid];
388        pos.y = 384 - eventsize[data->animid].y - pos.y; // Hack
389        tileid = (data->length - data->timer) * data->nframes / data->length;
390        if (tileid < 0) tileid = 0;
391        if (tileid > data->nframes - 1) tileid = data->nframes - 1;
392        break;
393    }
394    if (tiler)
395        Scene::GetDefault()->AddTile(tiler, tileid,
396                                     ivec3(data->ground_pos.x + pos.x,
397                                           data->ground_pos.y + pos.y, 1), 0,
398                                     vec2(1.0f));
399}
400
401int Title::IsClicked() const
402{
403    return m_clicked[0];
404}
405
406Title::~Title()
407{
408    Input::UntrackMouse(this);
409    Ticker::Unref(data->logo_sprite);
410    Tiler::Deregister(data->logo);
411    Tiler::Deregister(data->ground);
412    Tiler::Deregister(data->rocks);
413    Tiler::Deregister(data->title);
414    Tiler::Deregister(data->stars);
415    Tiler::Deregister(data->clouds);
416    Tiler::Deregister(data->eagle.tiles);
417    for (int n = 0; n < 6; n++)
418        Tiler::Deregister(data->anim[n]);
419    for (int n = 0; n < 7; n++)
420        Tiler::Deregister(data->event[n]);
421    Ticker::Unref(data->gradient);
422    delete data;
423}
424
Note: See TracBrowser for help on using the repository browser.