source: trunk/monsterz/title.cpp @ 339

Last change on this file since 339 was 337, checked in by sam, 10 years ago

Fix an off-by-one error in animation frame computations.

File size: 7.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#include <cstdio>
16#include <cmath>
17#include <cstdlib>
18#include <ctime>
19
20#include "core.h"
21#include "monsterz.h"
22#include "title.h"
23
24/*
25 * Title implementation class
26 */
27
28class TitleData
29{
30    friend class Title;
31
32private:
33    int title, logo;
34    int anim[6];
35    int event[7];
36
37    enum
38    {
39        IDLE,
40        ANIM,
41        EVENT,
42    }
43    state;
44
45    enum
46    {
47        DAY,
48        NIGHT,
49        STEALTH,
50        RADIO,
51    }
52    period, nextperiod;
53
54    float timer, length;
55    int nframes, animid;
56};
57
58static int2 const animsize[] =
59{
60    int2(384, 384),
61    int2(284, 81),
62    int2(38, 146),
63    int2(29, 137),
64    int2(284, 82),
65    int2(384, 384),
66};
67
68static int2 const animpos[] =
69{
70    int2(0, 0),
71    int2(65, 255),
72    int2(239, 168),
73    int2(248, 168),
74    int2(63, 255),
75    int2(0, 0),
76};
77
78static int2 const eventsize[] =
79{
80    int2(143, 16),
81    int2(68, 49),
82    int2(17, 29),
83    int2(50, 80),
84    int2(237, 238),
85    int2(59, 53),
86    int2(140, 15),
87};
88
89static int2 const eventpos[] =
90{
91    int2(0, 322),
92    int2(316, 286),
93    int2(246, 245),
94    int2(279, 173),
95    int2(42, 101),
96    int2(231, 154),
97    int2(0, 322),
98};
99
100/*
101 * Public Title class
102 */
103
104Title::Title()
105  : data(new TitleData())
106{
107    srand(rand() ^ time(NULL));
108
109    data->logo = Tiler::Register(PNG_LOGO, 380, 181, 1.0f);
110    data->title = Tiler::Register(PNG_TITLE, 384, 384, 1.0f);
111    for (int n = 0; n < 6; n++)
112        data->anim[n] = Tiler::Register(PNG_TITLEANIM[n],
113                                        animsize[n].x, animsize[n].y, 1.0f);
114    for (int n = 0; n < 7; n++)
115        data->event[n] = Tiler::Register(PNG_TITLEEVENT[n],
116                                         eventsize[n].x, eventsize[n].y, 1.0f);
117    data->state = TitleData::IDLE;
118    data->period = TitleData::DAY;
119    data->nextperiod = TitleData::DAY;
120    data->timer = RandF(2000.0f, 3000.0f);
121
122    position = int3(24, 72, 1);
123    bbox[0] = position;
124    bbox[1] = bbox[0] + int3(384, 384, 0);
125
126    Input::TrackMouse(this);
127}
128
129void Title::TickGame(float deltams)
130{
131    Entity::TickGame(deltams);
132
133    data->timer -= deltams;
134
135    /* Probability of playing an animation given the current period */
136    static float const p1[] = { 0.6f, 0.3f, 0.0f, 0.2f };
137    /* Probability of a period change given the current period */
138    static float const p2[] = { 0.1f, 0.2f, 0.5f, 0.5f };
139    /* Number of frames in transition anims and events */
140    static int const t1[] = { 31, 26, 7, 7, 32, 6, 31 };
141    static int const t2[] = { 2, 4, 11, 4, 6, 2 };
142
143    switch (data->state)
144    {
145    case TitleData::IDLE:
146        if (data->timer > 0.0f)
147            break;
148        if (RandF() < p1[data->period])
149        {
150            switch (data->period)
151            {
152            case TitleData::DAY:
153                data->animid = rand() % 4;
154                break;
155            case TitleData::NIGHT:
156                data->animid = (rand() % 2) ? 4 : 6;
157                break;
158            case TitleData::STEALTH:
159                /* XXX: we should not be here! */
160                break;
161            case TitleData::RADIO:
162                data->animid = 5;
163                break;
164            }
165            data->state = TitleData::EVENT;
166            data->nframes = t1[data->animid];
167            data->timer = data->length = data->nframes * 100.0f;
168        }
169        else if (RandF() < p2[data->period])
170        {
171            switch (data->period)
172            {
173            case TitleData::DAY:
174                data->nextperiod = TitleData::NIGHT;
175                data->animid = 0;
176                break;
177            case TitleData::NIGHT:
178                if (RandF() < 0.6f)
179                {
180                    data->nextperiod = TitleData::DAY;
181                    data->animid = 5;
182                }
183                else
184                {
185                    data->nextperiod = TitleData::STEALTH;
186                    data->animid = 1;
187                }
188                break;
189            case TitleData::STEALTH:
190                if (RandF() < 0.3f)
191                {
192                    data->nextperiod = TitleData::RADIO;
193                    data->animid = 2;
194                }
195                else
196                {
197                    data->nextperiod = TitleData::NIGHT;
198                    data->animid = 4;
199                }
200                break;
201            case TitleData::RADIO:
202                data->nextperiod = TitleData::STEALTH;
203                data->animid = 3;
204                break;
205            }
206            data->state = TitleData::ANIM;
207            data->nframes = t2[data->animid];
208            data->timer = data->length = data->nframes * 100.0f;
209        }
210        else
211        {
212            data->timer = RandF(500.0f, 1000.0f);
213        }
214        break;
215    case TitleData::ANIM:
216        if (data->timer < 0.0f)
217            data->period = data->nextperiod;
218            /* Fall through */
219    case TitleData::EVENT:
220        if (data->timer < 0.0f)
221        {
222            data->state = TitleData::IDLE;
223            data->timer = RandF(500.0f, 1000.0f);
224        }
225        break;
226    }
227}
228
229void Title::TickDraw(float deltams)
230{
231    Entity::TickDraw(deltams);
232
233    /* The background, always here. */
234    int backid = (int)data->period;
235    Scene::GetDefault()->AddTile((data->title << 16) | backid, 24, 72, 1, 0);
236
237    /* Maybe an animation? */
238    int tiler = 0;
239    int id = 0;
240    int2 pos;
241
242    switch (data->state)
243    {
244    case TitleData::IDLE:
245        break;
246    case TitleData::ANIM:
247        tiler = data->anim[data->animid];
248        pos = animpos[data->animid];
249        pos.y = 384 - animsize[data->animid].y - pos.y; // Hack
250        id = (data->length - data->timer) * data->nframes / data->length;
251        if (id < 0) id = 0;
252        if (id > data->nframes - 1) id = data->nframes - 1;
253        break;
254    case TitleData::EVENT:
255        tiler = data->event[data->animid];
256        pos = eventpos[data->animid];
257        pos.y = 384 - eventsize[data->animid].y - pos.y; // Hack
258        id = (data->length - data->timer) * data->nframes / data->length;
259        if (id < 0) id = 0;
260        if (id > data->nframes - 1) id = data->nframes - 1;
261        break;
262    }
263    if (tiler)
264        Scene::GetDefault()->AddTile((tiler << 16) | id, 24 + pos.x, 72 + pos.y, 1, 0);
265
266    /* The logo, on top of the rest. */
267    Scene::GetDefault()->AddTile((data->logo << 16) | 0, 26, 250, 2, 0);
268}
269
270int Title::IsClicked() const
271{
272    return clicked[0];
273}
274
275Title::~Title()
276{
277    Input::UntrackMouse(this);
278    Tiler::Deregister(data->logo);
279    Tiler::Deregister(data->title);
280    for (int n = 0; n < 6; n++)
281        Tiler::Deregister(data->anim[n]);
282    for (int n = 0; n < 7; n++)
283        Tiler::Deregister(data->event[n]);
284    delete data;
285}
286
Note: See TracBrowser for help on using the repository browser.