source: trunk/monsterz/title.cpp @ 791

Last change on this file since 791 was 791, checked in by sam, 9 years ago

monsterz: eagles and clouds now span across the whole width of the screen
instead of the initial 384×384 square.

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