Fix accidental switch/teleporter behavior changes
[neverball] / share / part.c
1 /*
2  * Copyright (C) 2003 Robert Kooima
3  *
4  * NEVERBALL is  free software; you can redistribute  it and/or modify
5  * it under the  terms of the GNU General  Public License as published
6  * by the Free  Software Foundation; either version 2  of the License,
7  * or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT  ANY  WARRANTY;  without   even  the  implied  warranty  of
11  * MERCHANTABILITY or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
12  * General Public License for more details.
13  */
14
15 #include <SDL.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <math.h>
19
20 #include "glext.h"
21 #include "part.h"
22 #include "vec3.h"
23 #include "image.h"
24
25 /*---------------------------------------------------------------------------*/
26
27 struct part
28 {
29     float t;
30     float a;
31     float w;
32     float c[3];
33     float p[3];
34     float v[3];
35 };
36
37 static struct part part_coin[PART_MAX_COIN];
38 static struct part part_goal[PART_MAX_GOAL];
39 static struct part part_jump[PART_MAX_JUMP];
40 static GLuint      part_text_star;
41 static GLuint      part_text_squiggle;
42 static GLuint      part_list;
43
44 static float goal_height;
45 static float jump_height;
46
47 /*---------------------------------------------------------------------------*/
48
49 #define PI 3.1415927f
50
51 static float rnd(float l, float h)
52 {
53     return l + (h - l) * rand() / RAND_MAX;
54 }
55
56 /*---------------------------------------------------------------------------*/
57
58 void part_reset(float zh, float jh)
59 {
60     int i;
61
62     goal_height = zh;
63     jump_height = jh;
64
65     for (i = 0; i < PART_MAX_COIN; i++)
66         part_coin[i].t = 0.0f;
67
68     for (i = 0; i < PART_MAX_GOAL; i++)
69     {
70         float t = rnd(+0.1f,      +1.0f);
71         float a = rnd(-1.0f * PI, +1.0f * PI);
72         float w = rnd(-2.0f * PI, +2.0f * PI);
73
74         part_goal[i].t = t;
75         part_goal[i].a = V_DEG(a);
76         part_goal[i].w = V_DEG(w);
77
78         part_goal[i].c[0] = 1.0f;
79         part_goal[i].c[1] = 1.0f;
80         part_goal[i].c[2] = 0.0f;
81
82         part_goal[i].p[0] = fsinf(a);
83         part_goal[i].p[1] = (1.f - t) * goal_height;
84         part_goal[i].p[2] = fcosf(a);
85
86         part_goal[i].v[0] = 0.f;
87         part_goal[i].v[1] = 0.f;
88         part_goal[i].v[2] = 0.f;
89     }
90
91     for (i = 0; i < PART_MAX_JUMP; i++)
92     {
93         float t = rnd(+0.1f,      +1.0f);
94         float a = rnd(-1.0f * PI, +1.0f * PI);
95         float w = rnd(+0.5f,      +2.5f);
96
97         float vy = rnd(+0.025f, +0.25f);
98
99         part_jump[i].t = t;
100         part_jump[i].a = V_DEG(a);
101         part_jump[i].w = w;
102
103         part_jump[i].c[0] = 1.0f;
104         part_jump[i].c[1] = 1.0f;
105         part_jump[i].c[2] = 1.0f;
106
107         part_jump[i].p[0] = fsinf(a);
108         part_jump[i].p[1] = (1.f - t) * jump_height;
109         part_jump[i].p[2] = fcosf(a);
110
111         part_jump[i].v[0] = 0.f;
112         part_jump[i].v[1] = vy;
113         part_jump[i].v[2] = 0.f;
114     }
115 }
116
117 void part_init(float zh, float jh)
118 {
119     memset(part_coin, 0, PART_MAX_COIN * sizeof (struct part));
120     memset(part_goal, 0, PART_MAX_GOAL * sizeof (struct part));
121     memset(part_jump, 0, PART_MAX_JUMP * sizeof (struct part));
122
123     part_text_star     = make_image_from_file(IMG_PART_STAR);
124     part_text_squiggle = make_image_from_file(IMG_PART_SQUIGGLE);
125
126     part_list = glGenLists(1);
127
128     glNewList(part_list, GL_COMPILE);
129     {
130         glBegin(GL_QUADS);
131         {
132             glTexCoord2f(0.f, 0.f);
133             glVertex2f(-PART_SIZE, -PART_SIZE);
134
135             glTexCoord2f(1.f, 0.f);
136             glVertex2f(+PART_SIZE, -PART_SIZE);
137
138             glTexCoord2f(1.f, 1.f);
139             glVertex2f(+PART_SIZE, +PART_SIZE);
140
141             glTexCoord2f(0.f, 1.f);
142             glVertex2f(-PART_SIZE, +PART_SIZE);
143         }
144         glEnd();
145     }
146     glEndList();
147
148     part_reset(zh, jh);
149 }
150
151 void part_free(void)
152 {
153     if (glIsList(part_list))
154         glDeleteLists(part_list, 1);
155
156     if (glIsTexture(part_text_star))
157         glDeleteTextures(1, &part_text_star);
158
159     if (glIsTexture(part_text_squiggle))
160         glDeleteTextures(1, &part_text_squiggle);
161 }
162
163 /*---------------------------------------------------------------------------*/
164
165 void part_burst(const float *p, const float *c)
166 {
167     int i, n = 0;
168
169     for (i = 0; n < 10 && i < PART_MAX_COIN; i++)
170         if (part_coin[i].t <= 0.f)
171         {
172             float a = rnd(-1.0f * PI, +1.0f * PI);
173             float b = rnd(+0.3f * PI, +0.5f * PI);
174             float w = rnd(-4.0f * PI, +4.0f * PI);
175
176             part_coin[i].p[0] = p[0];
177             part_coin[i].p[1] = p[1];
178             part_coin[i].p[2] = p[2];
179
180             part_coin[i].v[0] = 4.f * fcosf(a) * fcosf(b);
181             part_coin[i].v[1] = 4.f *            fsinf(b);
182             part_coin[i].v[2] = 4.f * fsinf(a) * fcosf(b);
183
184             part_coin[i].c[0] = c[0];
185             part_coin[i].c[1] = c[1];
186             part_coin[i].c[2] = c[2];
187
188             part_coin[i].t = 1.f;
189             part_coin[i].a = 0.f;
190             part_coin[i].w = V_DEG(w);
191
192             n++;
193         }
194 }
195
196 /*---------------------------------------------------------------------------*/
197
198 static void part_fall(struct part *part, int n, const float *g, float dt)
199 {
200     int i;
201
202     for (i = 0; i < n; i++)
203         if (part[i].t > 0.f)
204         {
205             part[i].t -= dt;
206
207             part[i].v[0] += (g[0] * dt);
208             part[i].v[1] += (g[1] * dt);
209             part[i].v[2] += (g[2] * dt);
210
211             part[i].p[0] += (part[i].v[0] * dt);
212             part[i].p[1] += (part[i].v[1] * dt);
213             part[i].p[2] += (part[i].v[2] * dt);
214         }
215 }
216
217 static void part_spin(struct part *part, int n, const float *g, float dt)
218 {
219     int i;
220
221     for (i = 0; i < n; i++)
222         if (part[i].t > 0.f)
223         {
224             part[i].a += 30.f * dt;
225
226             part[i].p[0] = fsinf(V_RAD(part[i].a));
227             part[i].p[2] = fcosf(V_RAD(part[i].a));
228         }
229 }
230
231 void part_step(const float *g, float dt)
232 {
233     int i;
234
235     part_fall(part_coin, PART_MAX_COIN, g, dt);
236
237     if (g[1] > 0.f)
238         part_fall(part_goal, PART_MAX_GOAL, g, dt);
239     else
240         part_spin(part_goal, PART_MAX_GOAL, g, dt);
241
242     for (i = 0; i < PART_MAX_JUMP; i++)
243     {
244         part_jump[i].p[1] += part_jump[i].v[1] * dt;
245
246         if (part_jump[i].p[1] > jump_height)
247             part_jump[i].p[1] = 0.0f;
248     }
249 }
250
251 /*---------------------------------------------------------------------------*/
252
253 static void part_draw(const float *M,
254                       const float *p, float r, float rz, float s)
255 {
256     glPushMatrix();
257     {
258         glTranslatef(r * p[0], p[1], r * p[2]);
259         glMultMatrixf(M);
260         glRotatef(rz, 0.f, 0.f, 1.f);
261         glScalef(s, s, 1.0f);
262
263         glCallList(part_list);
264     }
265     glPopMatrix();
266 }
267
268 void part_draw_coin(const float *M, float t)
269 {
270     int i;
271
272     glBindTexture(GL_TEXTURE_2D, part_text_star);
273
274     for (i = 0; i < PART_MAX_COIN; i++)
275         if (part_coin[i].t > 0.f)
276         {
277             glColor4f(part_coin[i].c[0],
278                       part_coin[i].c[1],
279                       part_coin[i].c[2],
280                       part_coin[i].t);
281
282             part_draw(M, part_coin[i].p, 1.0f, t * part_coin[i].w, 1.0f);
283         }
284 }
285
286 void part_draw_goal(const float *M, float radius, float a, float t)
287 {
288     int i;
289
290     glBindTexture(GL_TEXTURE_2D, part_text_star);
291
292     glColor4f(1.0f, 1.0f, 0.0f, a);
293
294     for (i = 0; i < PART_MAX_GOAL; i++)
295         if (part_goal[i].t > 0.0f)
296             part_draw(M, part_goal[i].p, radius - 0.05f,
297                       t * part_goal[i].w, 1.0f);
298 }
299
300 void part_draw_jump(const float *M, float radius, float a, float t)
301 {
302     int i;
303
304     glBindTexture(GL_TEXTURE_2D, part_text_squiggle);
305
306     for (i = 0; i < PART_MAX_JUMP; i++)
307     {
308         glColor4f(part_jump[i].c[0],
309                   part_jump[i].c[1],
310                   part_jump[i].c[2],
311                   1.0f - part_jump[i].p[1] / jump_height);
312
313         /*
314          * X is the current time since some Epoch, Y is the time it
315          * takes for a squiggle to grow to its full size and then
316          * shrink again.  F is the current scale of the squiggle in
317          * the interval [0.0, 1.0].
318          */
319
320 #define F(x, y) fabsf(fcosf(((x) / (y)) * PI))
321
322         part_draw(M, part_jump[i].p, radius - 0.05f,
323                   0.0f, F(t, part_jump[i].w));
324
325 #undef F
326     }
327 }
328
329 /*---------------------------------------------------------------------------*/