source: trunk/monsterz/title.cpp @ 860

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

render: add a Gradient class that will be used for dithering later.

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