source: trunk/monsterz/title.cpp @ 900

Last change on this file since 900 was 866, checked in by sam, 10 years ago

core: more vec?i -> ?veci renames.

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