JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
refactoring towards sprites, updated todo
[vor.git] / rocks.c
1 #include <SDL_image.h>
2 #include <math.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "common.h"
7 #include "config.h"
8 #include "file.h"
9 #include "globals.h"
10 #include "mt.h"
11 #include "rocks.h"
12 #include "shape.h"
13
14 SDL_Surface *load_image(char *filename);
15
16 struct rock rocks[MAXROCKS], *free_rocks;
17
18 struct rock **rock_buckets[2];
19 int n_buckets;
20 // we have two sets of buckets -- this variable tells which we are using.
21 int p;
22 int bw, bh;
23 int grid_size;
24
25 SDL_Surface *surf_rock[NROCKS];
26 struct shape rock_shapes[NROCKS];
27
28 // timers for rock generation.
29 float rtimers[4];
30
31 uint32_t nrocks;
32 float nrocks_timer;
33 float nrocks_inc_ticks = 2*60*20/(F_ROCKS-I_ROCKS);
34
35 // constants for rock generation.
36 #define KH (32*20)  // 32 s for a speed=1 rock to cross the screen horizontally.
37 #define KV (24*20)  // 24 s for a speed=1 rock to cross the screen vertically.
38 #define RDX 2.5  // range for rock dx values (+/-)
39 #define RDY 2.5  // range for rock dy values (+/-)
40
41 static inline struct rock **
42 bucket(int x, int y, int p)
43 {
44         int b = (x+grid_size)/grid_size + bw*((y+grid_size)/grid_size);
45         return &rock_buckets[p][b];
46 }
47
48 void
49 init_buckets(void)
50 {
51         int scr_grid_w = (XSIZE+2*grid_size-1) / grid_size;
52         int scr_grid_h = (YSIZE+2*grid_size-1) / grid_size;
53         bw = 1 + scr_grid_w + 1;
54         bh = 1 + scr_grid_h + 1;
55         n_buckets = bw * bh;
56         
57         rock_buckets[0] = malloc(n_buckets * sizeof(struct rock *));
58         rock_buckets[1] = malloc(n_buckets * sizeof(struct rock *));
59         if(!rock_buckets[0] || !rock_buckets[1]) {
60                 fprintf(stderr, "Can't allocate rock buckets.\n");
61                 exit(1);
62         }
63         p = 0;
64 }
65
66 void
67 transfer_rock(struct rock *r, struct rock **from, struct rock **to)
68 {
69         *from = &r->next->rock;
70         r->next = SPRITE(*to);
71         *to = r;
72 }
73
74 void
75 reset_rocks(void)
76 {
77         int i;
78
79         for(i=0; i<MAXROCKS; i++) rocks[i].image = NULL;
80         rocks[0].next = NULL; free_rocks = &rocks[MAXROCKS-1];
81         for(i = 1; i<MAXROCKS; i++) rocks[i].next = SPRITE(&rocks[i-1]);
82         for(i = 0; i<n_buckets; i++) {
83                 rock_buckets[0][i] = NULL;
84                 rock_buckets[1][i] = NULL;
85         }
86
87         nrocks = I_ROCKS;
88         nrocks_timer = 0;
89 }
90
91 #define ROCK_LEN sizeof("sprites/rockXX.png")
92
93 int
94 init_rocks(void)
95 {
96         int i;
97         char a[ROCK_LEN];
98         int maxw=0, maxh=0;
99
100         for(i = 0; i<NROCKS; i++) {
101                 snprintf(a, ROCK_LEN, "sprites/rock%02d.png", i);
102                 NULLERROR(surf_rock[i] = load_image(a));
103                 get_shape(surf_rock[i], &rock_shapes[i]);
104                 maxw = max(maxw, rock_shapes[i].w);
105                 maxh = max(maxh, rock_shapes[i].h);
106         }
107         grid_size = max(maxw, maxh) * 3 / 2;
108         init_buckets();
109         reset_rocks();
110         return 0;
111 }
112
113 enum { LEFT, RIGHT, TOP, BOTTOM };
114
115
116 // compute the number of rocks/tick that should be coming from each side,
117 // and the speed ranges of rocks coming from each side
118 void
119 rock_sides(float *ti, float *speed_min, float *speed_max)
120 {
121         float dx0,dx1, dy0,dy1;
122         float hfactor, vfactor;
123         int i;
124
125         for(i=0; i<4; i++) ti[i] = 0;
126         for(i=0; i<4; i++) speed_min[i] = 0;
127         for(i=0; i<4; i++) speed_max[i] = 0;
128         hfactor = (float)nrocks/KH; vfactor = (float)nrocks/KV;
129
130         dx0 = -RDX - screendx; dx1 = RDX - screendx;
131         dy0 = -RDY - screendy; dy1 = RDY - screendy;
132
133         if(dx0 < 0) {
134                 speed_max[RIGHT] = -dx0;
135                 if(dx1 < 0) {
136                         // Rocks moving left only. So the RIGHT side of the screen
137                         speed_min[RIGHT] = -dx1;
138                         ti[RIGHT] = -(dx0+dx1)/2;
139                 } else {
140                         // Rocks moving left and right
141                         speed_max[LEFT] = dx1;
142                         ti[RIGHT] = -dx0/2;
143                         ti[LEFT] = dx1/2;
144                 }
145         } else {
146                 // Rocks moving right only. So the LEFT side of the screen
147                 speed_min[LEFT] = dx0;
148                 speed_max[LEFT] = dx1;
149                 ti[LEFT] = (dx0+dx1)/2;
150         }
151         ti[LEFT] *= hfactor;
152         ti[RIGHT] *= hfactor;
153
154         if(dy0 < 0) {
155                 speed_max[BOTTOM] = -dy0;
156                 if(dy1 < 0) {
157                         // Rocks moving up only. So the BOTTOM of the screen
158                         speed_min[BOTTOM] = -dy1;
159                         ti[BOTTOM] = -(dy0+dy1)/2;
160                 } else {
161                         // Rocks moving up and down
162                         speed_max[TOP] = dy1;
163                         ti[BOTTOM] = -dy0/2;
164                         ti[TOP] = dy1/2;
165                 }
166         } else {
167                 // Rocks moving down only. so the TOP of the screen
168                 speed_min[TOP] = dy0;
169                 speed_max[TOP] = dy1;
170                 ti[TOP] = (dy0+dy1)/2;
171         }
172         ti[TOP] *= vfactor;
173         ti[BOTTOM] *= vfactor;
174 }
175
176 float
177 weighted_rnd_range(float min, float max) {
178         return sqrt(min * min + frnd() * (max * max - min * min));
179 }
180
181 void
182 new_rocks(void)
183 {
184         int i;
185         struct rock *r;
186         float ti[4];
187         float rmin[4];
188         float rmax[4];
189
190         if(nrocks < F_ROCKS) {
191                 nrocks_timer += t_frame;
192                 if(nrocks_timer >= nrocks_inc_ticks) {
193                         nrocks_timer -= nrocks_inc_ticks;
194                         nrocks++;
195                 }
196         }
197
198         rock_sides(ti, rmin, rmax);
199
200         // increment timers
201         for(i=0; i<4; i++) rtimers[i] += ti[i]*t_frame;
202
203         // generate rocks
204         for(i=0; i<4; i++) {
205                 while(rtimers[i] >= 1) {
206                         rtimers[i] -= 1;
207                         if(!free_rocks) return;  // sorry, we ran out of rocks!
208                         r = free_rocks;
209                         r->type = urnd() % NROCKS;
210                         r->image = surf_rock[r->type];
211                         r->shape = &rock_shapes[r->type];
212                         switch(i) {
213                                 case RIGHT:
214                                         r->x = XSIZE;
215                                         r->y = frnd()*(YSIZE + r->image->h);
216
217                                         r->dx = -weighted_rnd_range(rmin[i], rmax[i]) + screendx;
218                                         r->dy = RDY*crnd();
219                                         break;
220                                 case LEFT:
221                                         r->x = -r->image->w;
222                                         r->y = frnd()*(YSIZE + r->image->h);
223
224                                         r->dx = weighted_rnd_range(rmin[i], rmax[i]) + screendx;
225                                         r->dy = RDY*crnd();
226                                         break;
227                                 case BOTTOM:
228                                         r->x = frnd()*(XSIZE + r->image->w);
229                                         r->y = YSIZE;
230
231                                         r->dx = RDX*crnd();
232                                         r->dy = -weighted_rnd_range(rmin[i], rmax[i]) + screendy;
233                                         break;
234                                 case TOP:
235                                         r->x = frnd()*(XSIZE + r->image->w);
236                                         r->y = -r->image->h;
237
238                                         r->dx = RDX*crnd();
239                                         r->dy = weighted_rnd_range(rmin[i], rmax[i]) + screendy;
240                                         break;
241                         }
242                         transfer_rock(r, &free_rocks, bucket(r->x, r->y, p));
243                 }
244         }
245 }
246
247 void
248 move_rocks(void)
249 {
250         int b;
251         struct rock **head;
252         struct rock *r;
253
254         // Move all the rocks
255         for(b=0; b<n_buckets; b++) {
256                 head=&rock_buckets[p][b]; r=*head;
257                 while(*head) {
258                         r=*head;
259
260                         // move
261                         r->x += (r->dx - screendx)*t_frame;
262                         r->y += (r->dy - screendy)*t_frame;
263
264                         // clip it, or sort it into the other bucket set
265                         // (either way we move it out of this list).
266                         if(r->x + r->image->w < 0 || r->x >= XSIZE
267                                         || r->y + r->image->h < 0 || r->y >= YSIZE) {
268                                 transfer_rock(r, head, &free_rocks);
269                                 r->image = NULL;
270                         } else transfer_rock(r, head, bucket(r->x, r->y, 1-p));
271                 }
272         }
273         p = 1-p;  // switch current set of buckets.
274 }
275
276 void
277 draw_rocks(void)
278 {
279         int i;
280         SDL_Rect dest;
281
282         for(i=0; i<MAXROCKS; i++) {
283                 if(!rocks[i].image) continue;
284                 dest.x = rocks[i].x; dest.y = rocks[i].y;
285                 SDL_BlitSurface(rocks[i].image,NULL,surf_screen,&dest);
286         }
287 }
288
289 int
290 hit_in_bucket(struct rock *r, float x, float y, struct shape *shape)
291 {
292         for(; r; r=&r->next->rock) {
293                 if(collide(x - r->x, y - r->y, r->shape, shape)) return 1;
294         }
295         return 0;
296 }
297
298 int
299 hit_rocks(float x, float y, struct shape *shape)
300 {
301         int ix, iy;
302         int l, r, t, b;
303         struct rock **bucket;
304
305         ix = x + grid_size; iy = y + grid_size;
306         l = ix / grid_size; r = (ix+shape->w)/grid_size;
307         t = iy / grid_size; b = (iy+shape->h)/grid_size;
308         bucket = &rock_buckets[p][l + t*bw];
309
310         if(hit_in_bucket(*bucket, x, y, shape)) return true;
311         if(l > 0 && hit_in_bucket(*(bucket-1), x, y, shape)) return true;
312         if(t > 0 && hit_in_bucket(*(bucket-bw), x, y, shape)) return true;
313         if(l > 0 && t > 0 && hit_in_bucket(*(bucket-1-bw), x, y, shape)) return true;
314
315         if(r > l) {
316                 if(hit_in_bucket(*(bucket+1), x, y, shape)) return true;
317                 if(t > 0 && hit_in_bucket(*(bucket+1-bw), x, y, shape)) return true;
318         }
319         if(b > t) {
320                 if(hit_in_bucket(*(bucket+bw), x, y, shape)) return true;
321                 if(l > 0 && hit_in_bucket(*(bucket-1+bw), x, y, shape)) return true;
322         }
323         if(r > l && b > t && hit_in_bucket(*(bucket+1+bw), x, y, shape)) return true;
324         return false;
325 }
326
327 int
328 pixel_hit_in_bucket(struct rock *r, float x, float y)
329 {
330         for(; r; r=&r->next->rock) {
331                 if(x < r->x || y < r->y) continue;
332                 if(pixel_collide(x - r->x, y - r->y, r->shape)) return 1;
333         }
334         return 0;
335 }
336
337 int
338 pixel_hit_rocks(float x, float y)
339 {
340         int ix, iy;
341         int l, t;
342         struct rock **bucket;
343
344         ix = x + grid_size; iy = y + grid_size;
345         l = ix / grid_size; t = iy / grid_size;
346         bucket = &rock_buckets[p][l + t*bw];
347         if(pixel_hit_in_bucket(*bucket, x, y)) return true;
348         if(l > 0 && pixel_hit_in_bucket(*(bucket-1), x, y)) return true;
349         if(t > 0 && pixel_hit_in_bucket(*(bucket-bw), x, y)) return true;
350         if(l > 0 && t > 0 && pixel_hit_in_bucket(*(bucket-1-bw), x, y)) return true;
351         return false;
352 }
353
354 void
355 blast_rocks(float x, float y, float radius, int onlyslow)
356 {
357         int b;
358         struct rock *r;
359         float dx, dy, n;
360
361         if(onlyslow) return;
362
363         for(b=0; b<n_buckets; b++) {
364                 for(r=rock_buckets[p][b]; r; r=&r->next->rock) {
365                         if(r->x <= 0) continue;
366
367                         // This makes it so your explosion from dying magically doesn't leave
368                         // any rocks that aren't moving much on the x axis. If onlyslow is set,
369                         // only rocks that are barely moving will be pushed.
370                         if(onlyslow && (r->dx - screendx < -4 || r->dx - screendx > 3)) continue;
371
372                         dx = r->x - x;
373                         dy = r->y - y;
374
375                         n = sqrt(dx*dx + dy*dy);
376                         if(n < radius) {
377                                 n *= 15;
378                                 r->dx += 54.0*dx/n;
379                                 r->dy += 54.0*dy/n;
380                         }
381                 }
382         }
383 }