source: trunk/monsterz/title.cpp @ 1046

Last change on this file since 1046 was 1046, checked in by sam, 12 years ago

core: split vector operations into linear and non-linear so that we can
reuse the linear operations in quaternions. Also mark some constructors
explicit to better spot coding errors.

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