1 /*
2 * Luola - 2D multiplayer cave-flying game
3 * Copyright (C) 2001-2005 Calle Laakkonen
4 *
5 * File : level.c
6 * Description : Level handling and animation
7 * Author(s) : Calle Laakkonen
8 *
9 * Luola is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * Luola is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <math.h>
27 #include <string.h>
28 #include "console.h"
29 #include "player.h"
30 #include "fs.h"
31 #include "level.h"
32 #include "levelfile.h"
33 #include "particle.h"
34 #include "decor.h"
35 #include "animation.h"
36 #include "ship.h" /* for bump_ship() */
37
38 #define BASE_REGEN_SPEED 9 /* Delay between each regenerated pixel */
39
40 /* Level effects */
41 typedef struct {
42 int x, y;
43 int brake;
44 unsigned char value;
45 char icicle;
46 LevelFXType type;
47 } LevelFX;
48
49 /* Linked lists for level effects */
50 struct LevelEffects {
51 LevelFX *fx;
52 struct LevelEffects *next;
53 struct LevelEffects *prev;
54 } *level_effects;
55
56 static struct LevelEffects *lev_lastfx;
57
58 /* Stars */
59 typedef struct {
60 int x, y;
61 } Star;
62
63 /* Internally used globals */
64 static Star lev_stars[15];
65
66 /* Exported globals */
67 Uint32 burncolor[FIRE_FRAMES];
68 Uint32 lev_watercol;
69 SDL_Rect cam_rects[4];
70 SDL_Rect viewport_rects[4]; /* Use only x and y. w and h get overwritten by SDL_BlitSurface */
71 Level lev_level;
72
73 /* Bullet hole bitmap */
74 #define HOLE_W 9
75 #define HOLE_H 9
76 static const Uint8 hole_bm[HOLE_H][HOLE_W] = {
77 {1, 1, 1, 1, 0, 1, 1, 1, 1},
78 {1, 1, 1, 0, 0, 0, 1, 1, 1},
79 {1, 0, 0, 0, 0, 0, 0, 0, 1},
80 {1, 0, 0, 0, 0, 0, 0, 0, 1},
81 {0, 0, 0, 0, 0, 0, 0, 0, 0},
82 {1, 0, 0, 0, 0, 0, 0, 0, 1},
83 {1, 0, 0, 0, 0, 0, 0, 0, 1},
84 {1, 1, 1, 0, 0, 0, 1, 1, 1},
85 {1, 1, 1, 1, 0, 1, 1, 1, 1}
86 };
87
touch_wall(int x,int y)88 static int touch_wall (int x, int y)
89 {
90 int x1, y1;
91 int solid;
92 for (x1 = x - 3; x1 < x + 4; x1++) {
93 if (x1 >= lev_level.width)
94 return 0;
95 for (y1 = y - 3; y1 < y + 4; y1++) {
96 if (y1 >= lev_level.height)
97 return 0;
98 solid = lev_level.solid[x1][y1];
99 if (solid == TER_GROUND || solid == TER_INDESTRUCT
100 || solid == TER_BASE || solid == TER_COMBUSTABLE
101 || solid == TER_EXPLOSIVE || solid == TER_WALKWAY)
102 return 1;
103 }
104 }
105
106 return 0;
107 }
108
draw_stars(SDL_Rect * cam,SDL_Rect * targ)109 static void draw_stars (SDL_Rect * cam, SDL_Rect * targ)
110 {
111 int r, x, y;
112 Uint8 *col;
113 for (r = 0; r < sizeof(lev_stars)/sizeof(Star); r++) {
114 x = cam->x + lev_stars[r].x;
115 y = cam->y + lev_stars[r].y;
116 col =
117 (Uint8 *) lev_level.terrain->pixels +
118 y * lev_level.terrain->pitch +
119 x * lev_level.terrain->format->BytesPerPixel;
120 if (x < lev_level.width && y < lev_level.height)
121 if (lev_level.solid[x][y] == TER_FREE && col[0] < 5 && col[1] < 5 && col[2] < 5)
122 putpixel (screen, targ->x + lev_stars[r].x,
123 targ->y + lev_stars[r].y, col_white);
124 }
125 }
126
127 /* Draw the level for all players */
draw_level(void)128 static inline void draw_level (void)
129 {
130 int p;
131 for (p = 0; p < 4; p++) {
132 if (players[p].state==ALIVE || players[p].state==DEAD) {
133 SDL_BlitSurface (lev_level.terrain, &cam_rects[p], screen,
134 &viewport_rects[p]);
135 if(level_settings.stars)
136 draw_stars (&cam_rects[p], &viewport_rects[p]);
137 }
138 }
139 }
140
141 /* Initialize level subsystem */
init_level(void)142 void init_level (void)
143 {
144 int r,red, green, blue;
145 lev_watercol = map_rgba(0x64,0x64,0xff,0xff);
146 level_effects = NULL;
147 lev_lastfx = NULL;
148 for (r = 0; r < FIRE_FRAMES; r++) {
149 if (r == 0)
150 red = 0;
151 else
152 red = 128 + (255.0 / FIRE_FRAMES) * r;
153 green = (255.0 / FIRE_FRAMES) * r;
154 blue = 0;
155 if (red > 255)
156 red = 255;
157 burncolor[r] = map_rgba(red,green,blue,0xff);
158 }
159 }
160
161 /* Calculate star positions. This must be done when player screen */
162 /* geometry changes. */
reinit_stars(void)163 void reinit_stars(void) {
164 SDL_Rect size = get_viewport_size();
165 int r;
166 for (r = 0; r < sizeof(lev_stars)/sizeof(Star); r++) {
167 lev_stars[r].x = rand () % size.w;
168 lev_stars[r].y = rand () % size.h;
169 }
170 }
171
172 /* Find the first occurance of a color in the level palette */
find_color(Uint8 color,Uint8 * palette,SDL_Palette * source,SDL_Color ** target)173 static void find_color(Uint8 color,Uint8 *palette,SDL_Palette *source,SDL_Color **target) {
174 int c=0;
175 while (c < source->ncolors) {
176 if (palette[c]==color) {
177 *target=source->colors+c;
178 break;
179 }
180 c++;
181 }
182 }
183
184 /* Sort base regeneration array */
sort_regen(const void * ptr1,const void * ptr2)185 static int sort_regen(const void *ptr1,const void *ptr2) {
186 const RegenCoord *c1=ptr1,*c2=ptr2;
187 if(c1->y<c2->y) return 1;
188 else if(c1->y==c2->y) return 0;
189 else return -1;
190 }
191
192 /* Load level and prepare for new game */
load_level(struct LevelFile * lev)193 void load_level (struct LevelFile *lev) {
194 SDL_Surface *collmap;
195 int x, y, p, r;
196 int freepix, otherpix;
197 SDL_Color *tmpcol, defaultwater;
198 Uint8 *bits;
199 int basebufsize;
200
201 defaultwater.r = 0;
202 defaultwater.g = 0;
203 defaultwater.b = 255;
204
205 /* Load level artwork */
206 lev_level.terrain = load_level_art (lev);
207 lev_level.width = lev_level.terrain->w;
208 lev_level.height = lev_level.terrain->h;
209 /* Load level collisionmap */
210 collmap = load_level_coll (lev);
211 if (!collmap) {
212 printf ("An error occured while loading collisionmap\n");
213 exit (1);
214 }
215 if (collmap->w != lev_level.width || collmap->h != lev_level.height) {
216 printf
217 ("Error Collision map image \"%s\" has incorrect size (%dx%d), should be %dx%d!\n",
218 lev->settings->mainblock.collmap, collmap->w, collmap->h,
219 lev_level.width, lev_level.height);
220 exit (1);
221 }
222 /* Get palette entries */
223 /* Get water colour */
224 tmpcol = &defaultwater;
225 find_color(TER_WATER,lev->settings->palette.entries,
226 collmap->format->palette,&tmpcol);
227 lev_watercol = map_rgba(tmpcol->r, tmpcol->g, tmpcol->b,0xff);
228 /* Calculate underwater clay colour */
229 col_clay_uw = map_rgba((tmpcol->r+255)/2, (tmpcol->r+200)/2, (tmpcol->r+128)/2, 0xff);
230 /* Get snow colour */
231 tmpcol = NULL;
232 find_color(TER_SNOW,lev->settings->palette.entries,
233 collmap->format->palette,&tmpcol);
234 if (tmpcol) {
235 col_snow = map_rgba(tmpcol->r, tmpcol->g, tmpcol->b,0xff);
236 }
237 /* Prepare base regeneration array */
238 if(game_settings.base_regen) {
239 basebufsize=512; /* Some arbitary size */
240 lev_level.base = malloc(sizeof(RegenCoord)*basebufsize);
241 } else {
242 basebufsize=0;
243 lev_level.base = NULL;
244 }
245 /* Load data map */
246 lev_level.base_area = 0;
247 lev_level.regen_area = 0;
248 lev_level.solid = malloc (sizeof (char *) * lev_level.width);
249 freepix = 0;
250 otherpix = 0;
251 for (x = 0; x < lev_level.width; x++) {
252 bits = ((Uint8 *) collmap->pixels)+x;
253 lev_level.solid[x] = malloc (lev_level.height);
254 for (y = 0; y < lev_level.height; y++,bits+=collmap->pitch) {
255 lev_level.solid[x][y] = lev->settings->palette.entries[*bits];
256 if (lev_level.solid[x][y] == TER_FREE
257 || lev_level.solid[x][y] == TER_WATER)
258 freepix++;
259 else {
260 otherpix++;
261 if (lev_level.solid[x][y] == TER_BASE) {
262 if(lev_level.base) {
263 Uint8 r,g,b;
264 lev_level.base[lev_level.base_area].x=x;
265 lev_level.base[lev_level.base_area].y=y;
266 SDL_GetRGB(getpixel(lev_level.terrain,x,y),
267 screen->format,
268 &r,
269 &g,
270 &b);
271 lev_level.base[lev_level.base_area].c=
272 map_rgba(r, g, b, 0xff);
273
274 if(lev_level.base_area==basebufsize-1) {
275 basebufsize+=512;
276 lev_level.base=realloc(lev_level.base,
277 sizeof(RegenCoord)*basebufsize);
278 }
279 }
280 lev_level.base_area++;
281 }
282 }
283 }
284 }
285 SDL_FreeSurface (collmap);
286 /* Finalize the base regeneration buffer */
287 if(lev_level.base) {
288 lev_level.regen_timer = 0;
289 lev_level.regen_area = lev_level.base_area;
290 if(lev_level.base_area<basebufsize) {
291 basebufsize=lev_level.base_area;
292 lev_level.base=realloc(lev_level.base,
293 sizeof(RegenCoord)*basebufsize);
294 }
295 qsort(lev_level.base,lev_level.base_area,sizeof(RegenCoord),sort_regen);
296 }
297 /* Position players */
298 if (game_settings.playmode == OutsideShip
299 || game_settings.playmode == OutsideShip1)
300 y = 2;
301 else
302 y = 1;
303 for (p = 0; p < 4; p++)
304 if (players[p].state != INACTIVE) {
305 for (x = 0; x < y; x++) {
306 r = 0;
307 do {
308 lev_level.player_def_x[x][p] = rand () % lev_level.width;
309 lev_level.player_def_y[x][p] = rand () % lev_level.height;
310 r++;
311 if (r > 100000) {
312 fprintf(stderr,
313 "Warning: while searching for player %d startup position, loop counter reached 100000!\n",
314 p);
315 fprintf(stderr,
316 "Number of free space (including water) pixels: %d, number of other pixels: %d\n",
317 freepix, otherpix);
318 fprintf (stderr,
319 "%0.3f%% of the surface area is available\n",
320 ((double) freepix / (double) otherpix) * 100);
321 exit (0);
322 }
323 } while (lev_level.
324 solid[lev_level.player_def_x[x][p]][lev_level.
325 player_def_y[x]
326 [p]] !=
327 TER_FREE);
328 }
329 }
330 }
331
332 /* Release level from memory */
unload_level(void)333 void unload_level (void)
334 {
335 int x;
336 struct LevelEffects *next;
337 SDL_FreeSurface (lev_level.terrain);
338 for (x = 0; x < lev_level.width; x++)
339 free (lev_level.solid[x]);
340 free (lev_level.solid);
341 if(lev_level.base)
342 free(lev_level.base);
343 while (level_effects) {
344 next = level_effects->next;
345 free (level_effects->fx);
346 free (level_effects);
347 level_effects = next;
348 }
349 lev_lastfx = NULL;
350 }
351
352 /* Pixel perfect collision detection. */
hit_solid_line(int startx,int starty,int endx,int endy,int * newx,int * newy)353 int hit_solid_line (int startx, int starty, int endx, int endy, int *newx,
354 int *newy)
355 {
356 int dx, dy, ax, ay, sx, sy, x, y, d;
357 if (startx<0 || endx < 0 || startx >= lev_level.width || endx >= lev_level.width) {
358 *newx = endx<0?0:endx>=lev_level.width?lev_level.width-1:endx;
359 *newy = endy<0?0:endy>=lev_level.height?lev_level.height-1:endy;
360 return TER_INDESTRUCT;
361 }
362 if (startx<0 || endy < 0 || startx >= lev_level.width || endy >= lev_level.height) {
363 *newx = endx<0?0:endx>=lev_level.width?lev_level.width-1:endx;
364 *newy = endy<0?0:endy>=lev_level.height?lev_level.height-1:endy;
365 return TER_INDESTRUCT;
366 }
367 dx = endx - startx;
368 dy = endy - starty;
369 ax = abs (dx) << 1;
370 ay = abs (dy) << 1;
371 sx = (dx >= 0) ? 1 : -1;
372 sy = (dy >= 0) ? 1 : -1;
373 x = startx;
374 y = starty;
375 if (ax > ay) {
376 d = ay - (ax >> 1);
377 while (x != endx) {
378 if (is_solid (x, y)) {
379 *newx = x;
380 *newy = y;
381 return lev_level.solid[x][y];
382 }
383 if (d > 0 || (d == 0 && sx == 1)) {
384 y += sy;
385 d -= ax;
386 }
387 x += sx;
388 d += ay;
389 }
390 } else {
391 d = ax - (ay >> 1);
392 while (y != endy) {
393 if (is_solid (x, y)) {
394 *newx = x;
395 *newy = y;
396 return lev_level.solid[x][y];
397 }
398 if (d > 0 || (d == 0 && sy == 1)) {
399 x += sx;
400 d -= ay;
401 }
402 y += sy;
403 d += ax;
404 }
405 }
406 *newx = endx;
407 *newy = endy;
408 return lev_level.solid[endx][endy];
409 }
410
find_rainy(int x)411 int find_rainy (int x)
412 {
413 int y, loops = 0, r, c;
414 do {
415 y = 15 + rand () % (screen->w/4);
416 loops++;
417 if (loops > 100)
418 return -1;
419 c = 0;
420 for (r = y; r < y + 25; r++)
421 c += lev_level.solid[x][r] != TER_FREE;
422 } while (c > 17);
423 return y;
424 }
425
start_burning(int x,int y)426 void start_burning (int x, int y)
427 {
428 struct LevelEffects *newentry;
429 LevelFX *fx;
430 if (x <= 0 || y <= 0 || x >= lev_level.width || y >= lev_level.height)
431 return;
432 if (is_water(x,y) || is_indestructable(x,y))
433 return;
434 newentry = malloc (sizeof (struct LevelEffects));
435 newentry->next = NULL;
436 newentry->prev = lev_lastfx;
437 fx = malloc (sizeof (LevelFX));
438 fx->x = x;
439 fx->y = y;
440 fx->type = Fire;
441 fx->value = FIRE_FRAMES;
442 if (lev_level.solid[x][y] == TER_COMBUSTABL2)
443 lev_level.solid[x][y] = TER_GROUND;
444 else
445 lev_level.solid[x][y] = TER_FREE;
446 newentry->fx = fx;
447 if (lev_lastfx == NULL)
448 level_effects = newentry;
449 else
450 lev_lastfx->next = newentry;
451 lev_lastfx = newentry;
452 }
453
start_melting(int x,int y,unsigned int recurse)454 void start_melting (int x, int y, unsigned int recurse)
455 {
456 struct LevelEffects *newentry;
457 LevelFX *fx;
458 if (x <= 0 || y <= 0 || x >= lev_level.width || y >= lev_level.height
459 || recurse == 0)
460 return;
461 if (is_water(x,y) || is_indestructable(x,y))
462 return;
463 newentry = malloc (sizeof (struct LevelEffects));
464 newentry->next = NULL;
465 newentry->prev = lev_lastfx;
466 fx = malloc (sizeof (LevelFX));
467 fx->x = x;
468 fx->y = y;
469 fx->type = Melt;
470 fx->value = 4;
471 fx->brake = recurse;
472 if(game_settings.base_regen && lev_level.solid[x][y]==TER_BASE)
473 lev_level.base_area--;
474 lev_level.solid[x][y] = TER_FREE;
475 putpixel (lev_level.terrain, x, y, col_green);
476 newentry->fx = fx;
477 if (lev_lastfx == NULL)
478 level_effects = newentry;
479 else
480 lev_lastfx->next = newentry;
481 lev_lastfx = newentry;
482 }
483
alter_level(int x,int y,int recurse,LevelFXType type)484 void alter_level (int x, int y, int recurse, LevelFXType type)
485 {
486 struct LevelEffects *newentry;
487 LevelFX *fx;
488 if (recurse == 0 || x<0 || y<0 || x>=lev_level.width || y>=lev_level.height)
489 return;
490 newentry = malloc (sizeof (struct LevelEffects));
491 newentry->next = NULL;
492 newentry->prev = lev_lastfx;
493 fx = malloc (sizeof (LevelFX));
494 fx->x = x;
495 fx->y = y;
496 fx->value = 3;
497 fx->type = type;
498 fx->brake = recurse;
499 fx->icicle = 0;
500 if (type == Explosive) {
501 lev_level.solid[x][y] = TER_EXPLOSIVE;
502 } else if (type == Ice) {
503 if (lev_level.solid[x][y] == TER_WATER)
504 lev_level.solid[x][y] = TER_ICE;
505 else if (lev_level.solid[x][y] == TER_FREE
506 || lev_level.solid[x][y] == TER_TUNNEL)
507 lev_level.solid[x][y] = TER_SNOW;
508 if (rand () % 15 == 0) {
509 fx->icicle = 1;
510 fx->value = 6;
511 }
512 } else {
513 if (lev_level.solid[x][y] == TER_FREE
514 || lev_level.solid[x][y] == TER_TUNNEL)
515 lev_level.solid[x][y] = TER_GROUND;
516 else if (lev_level.solid[x][y] == TER_WATER)
517 lev_level.solid[x][y] = TER_UNDERWATER;
518 }
519 newentry->fx = fx;
520 if (lev_lastfx == NULL)
521 level_effects = newentry;
522 else
523 lev_lastfx->next = newentry;
524 lev_lastfx = newentry;
525 }
526
527 /* Make a bullet hole in the ground */
make_hole(int x,int y)528 void make_hole(int x,int y) {
529 if (lev_level.solid[x][y] != TER_INDESTRUCT
530 && !(level_settings.indstr_base
531 && (lev_level.solid[x][y] == TER_BASE
532 || lev_level.solid[x][y] == TER_BASEMAT))) {
533 int fx,fy;
534 for (fx = 0; fx < HOLE_W; fx++) {
535 for (fy = 0; fy < HOLE_H; fy++) {
536 int terrain;
537 int rx,ry;
538 if (hole_bm[fx][fy])
539 continue;
540 rx = fx - HOLE_W/2 + x;
541 ry = fy - HOLE_H/2 + y;
542 if (rx < 0 || ry < 0 || rx >= lev_level.width
543 || ry >= lev_level.height)
544 continue;
545 terrain = lev_level.solid[rx][ry];
546 if((ter_semisolid(terrain) || ter_solid(terrain))
547 && ter_indestructable(terrain)==0)
548 {
549 if(terrain == TER_BASE)
550 lev_level.base_area--;
551 if (terrain == TER_UNDERWATER || terrain == TER_ICE) {
552 lev_level.solid[rx][ry] = TER_WATER;
553 putpixel (lev_level.terrain, rx, ry, lev_watercol);
554 } else {
555 lev_level.solid[rx][ry] = TER_FREE;
556 putpixel (lev_level.terrain, rx, ry, col_black);
557 }
558 }
559 }
560 }
561 }
562 }
563
564 /* Burn a patch of ground */
burn_hole(int x,int y)565 void burn_hole(int x,int y) {
566 int fx,fy;
567 if(is_burnable(x,y))
568 start_burning(x,y);
569 for (fx = 0; fx < HOLE_W; fx++)
570 for (fy = 0; fy < HOLE_H; fy++) {
571 int rx,ry,terrain;
572 if (hole_bm[fx][fy])
573 continue;
574 rx = fx - HOLE_W/2 + x;
575 ry = fy - HOLE_H/2 + y;
576 if (rx < 0 || ry < 0 || rx >= lev_level.width
577 || ry >= lev_level.height)
578 continue;
579 terrain = lev_level.solid[rx][ry];
580 if((ter_semisolid(terrain) || ter_solid(terrain))
581 && ter_indestructable(terrain)==0)
582 {
583 putpixel (lev_level.terrain, rx, ry, col_gray);
584 }
585 }
586 }
587
animate_level(void)588 void animate_level (void)
589 {
590 struct LevelEffects *list = level_effects;
591 struct LevelEffects *next = NULL;
592 double f;
593 int v, tx, ty, nx, ny;
594 char solid;
595 struct Particle *part;
596 /* Base regeneration */
597 if(lev_level.base && lev_level.base_area<lev_level.regen_area) {
598 if(lev_level.regen_timer>BASE_REGEN_SPEED) {
599 int r,x,y;
600 lev_level.regen_timer=0;
601 for(r=0;r<lev_level.regen_area;r++) {
602 x=lev_level.base[r].x;
603 y=lev_level.base[r].y;
604 if(lev_level.solid[x][y]==TER_FREE) {
605 bump_ship(x,y);
606 lev_level.solid[x][y]=TER_BASE;
607 putpixel (lev_level.terrain, x, y, lev_level.base[r].c);
608 putpixel (lev_level.terrain, x, y, lev_level.base[r].c);
609 lev_level.base_area++;
610 break;
611 }
612 }
613 } else {
614 lev_level.regen_timer++;
615 }
616 }
617 /* Level effects */
618 while (list) {
619 next = list->next;
620 if (list->fx->type == Fire) { /* Fire */
621 list->fx->value--;
622 if (list->fx->value > 0)
623 v = list->fx->value + rand () % FIRE_RANDOM;
624 else
625 v = list->fx->value;
626 if (list->fx->x < 3 || list->fx->y < 3
627 || list->fx->x > lev_level.width - 3
628 || list->fx->y > lev_level.height - 3) {
629 list->fx->value = 0;
630 } else {
631 solid = lev_level.solid[list->fx->x][list->fx->y];
632 if (solid != TER_INDESTRUCT && solid != TER_BASE
633 && solid != TER_BASEMAT)
634 solid = 1;
635 else
636 solid = 0;
637 if (solid
638 && lev_level.solid[list->fx->x][list->fx->y + 1] ==
639 TER_FREE && list->fx->value < FIRE_SPREAD) {
640 if (lev_level.solid[list->fx->x][list->fx->y] == TER_GROUND) /* Combustable2 turns into ground, remember ? */
641 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
642 col_gray);
643 else
644 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
645 burncolor[0]);
646 list->fx->y -= rand () % 3;
647 }
648 solid = lev_level.solid[list->fx->x][list->fx->y];
649 if (v < FIRE_FRAMES && solid != TER_INDESTRUCT
650 && solid != TER_BASE && solid != TER_BASEMAT) {
651 if (v == 0
652 && lev_level.solid[list->fx->x][list->fx->y] ==
653 TER_GROUND)
654 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
655 col_gray);
656 else
657 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
658 burncolor[v]);
659 }
660 if (list->fx->value == FIRE_SPREAD) {
661 for (f = -M_PI; f < M_PI; f += M_PI / 4.0) {
662 nx = list->fx->x + Round(sin (f) * 3.0);
663 ny = list->fx->y + Round(cos (f) * 3.0);
664 if (nx >= lev_level.width || ny >= lev_level.height
665 || nx <= 0 || ny <= 0)
666 continue;
667 solid = lev_level.solid[nx][ny];
668 if (solid == TER_COMBUSTABLE
669 || solid == TER_COMBUSTABL2)
670 start_burning (nx, ny);
671 else if (solid == TER_EXPLOSIVE)
672 spawn_clusters (nx, ny,5.6, 6, make_bullet);
673 else if (solid == TER_EXPLOSIVE2)
674 spawn_clusters (nx, ny,5.6, 3, make_grenade);
675 else if (game_settings.enable_smoke
676 && solid == TER_FREE && cos (f) < 0
677 && rand () % 3 == 1) {
678 part = make_particle (nx, ny, 9);
679 part->vector.y = -2.5;
680 part->vector.x = -weather_wind_vector;
681 part->color[0] = 255;
682 part->color[1] = 178;
683 part->color[2] = 0;
684 #if HAVE_LIBSDL_GFX
685 part->rd = 0;
686 part->gd = 0;
687 part->bd = 11;
688 part->ad = -15;
689 #else
690 part->rd = -17;
691 part->gd = -8;
692 part->bd = 11;
693 #endif
694 } else if (solid == TER_ICE) {
695 lev_level.solid[nx][ny] = TER_WATER;
696 putpixel (lev_level.terrain, nx, ny,
697 lev_watercol);
698 } else if (solid == TER_SNOW) {
699 lev_level.solid[nx][ny] = TER_FREE;
700 putpixel (lev_level.terrain, nx, ny,
701 burncolor[0]);
702 } else if (solid == TER_WALKWAY)
703 putpixel (lev_level.terrain, nx, ny, col_gray);
704 }
705 }
706 }
707 } else if (list->fx->type == Melt) { /* Acid */
708 list->fx->value--;
709 if (list->fx->value == 1) {
710 if (list->fx->brake)
711 list->fx->brake--;
712 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
713 col_black);
714 for (f = -M_PI; f < M_PI; f += M_PI / 4.0) {
715 nx = list->fx->x + Round(sin (f) * 3.0);
716 ny = list->fx->y + Round(cos (f) * 3.0);
717 if (nx >= lev_level.width || ny >= lev_level.height
718 || nx <= 0 || ny <= 0)
719 continue;
720 solid = lev_level.solid[nx][ny];
721 if (solid == TER_GROUND || solid == TER_COMBUSTABLE
722 || solid == TER_COMBUSTABL2 || solid == TER_SNOW
723 || solid == TER_TUNNEL || solid == TER_WALKWAY)
724 start_melting (nx, ny, list->fx->brake);
725 else if ((solid == TER_BASE || solid == TER_BASEMAT)
726 && !level_settings.indstr_base)
727 start_melting (nx, ny, list->fx->brake);
728 else if (game_settings.enable_smoke && solid == TER_FREE
729 && cos (f) < 0 && rand () % 3 == 1) {
730 part = make_particle (nx, ny, 9);
731 part->vector.y = -2.5;
732 part->vector.x = -weather_wind_vector;
733 part->color[0] = 0;
734 part->color[1] = 255;
735 part->color[2] = 0;
736 part->rd = -22;
737 part->gd = -28;
738 part->bd = 22;
739 }
740 }
741 }
742 } else { /* Ice, Earth or Explosive */
743 solid = lev_level.solid[list->fx->x][list->fx->y];
744 if (list->fx->value > 0)
745 list->fx->value--;
746 if (list->fx->icicle
747 && lev_level.solid[list->fx->x][list->fx->y + 1] ==
748 TER_FREE) {
749 list->fx->y++;
750 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
751 col_snow);
752 }
753 if (list->fx->value == 1) {
754 if (list->fx->type == Earth) {
755 if (solid == TER_UNDERWATER)
756 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
757 col_clay_uw);
758 else
759 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
760 col_clay);
761 } else if (list->fx->type == Ice)
762 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
763 col_snow);
764 else
765 putpixel (lev_level.terrain, list->fx->x, list->fx->y,
766 col_gray);
767 if (list->fx->brake > 0)
768 list->fx->brake--;
769 if (list->fx->x > 5 && list->fx->y > 5
770 && list->fx->x < lev_level.width - 5
771 && list->fx->y < lev_level.height - 5) {
772 if (list->fx->type == Ice
773 && (solid == TER_GROUND || solid == TER_FREE
774 || solid == TER_SNOW || solid == TER_INDESTRUCT
775 || solid == TER_COMBUSTABLE
776 || solid == TER_COMBUSTABL2
777 || solid == TER_WALKWAY)) {
778 for (tx = list->fx->x - 3; tx < list->fx->x + 4; tx++)
779 for (ty = list->fx->y - 3; ty < list->fx->y + 4;
780 ty++) {
781 if (lev_level.solid[tx][ty] == TER_WATER)
782 alter_level (tx, ty, list->fx->brake,
783 Ice);
784 else if ((lev_level.solid[tx][ty] == TER_FREE
785 || lev_level.solid[tx][ty] ==
786 TER_TUNNEL) && touch_wall (tx, ty))
787 alter_level (tx, ty, list->fx->brake,
788 list->fx->type);
789 }
790 } else {
791 for (f = -M_PI; f < M_PI; f += M_PI / 4.0) {
792 solid =
793 lev_level.
794 solid[(int) (list->fx->x + sin (f) * 4.0)][(int) (list->fx->y + cos (f) * 4.0)];
795 if (list->fx->type == Ice) {
796 if (solid == TER_WATER && list->fx->brake)
797 alter_level ((int)
798 (list->fx->x +
799 sin (f) * 4.0),
800 (int) (list->fx->y +
801 cos (f) * 4.0),
802 list->fx->brake,
803 list->fx->type);
804 } else if (list->fx->type == Earth) {
805 if ((solid == TER_FREE || solid == TER_WATER
806 || solid == TER_TUNNEL)
807 && list->fx->brake)
808 alter_level ((int)
809 (list->fx->x +
810 sin (f) * 4.0),
811 (int) (list->fx->y +
812 cos (f) * 4.0),
813 list->fx->brake,
814 list->fx->type);
815 } else {
816 if ((solid == TER_GROUND
817 || solid == TER_COMBUSTABLE
818 || solid == TER_COMBUSTABL2)
819 && list->fx->brake)
820 alter_level ((int)
821 (list->fx->x +
822 sin (f) * 4.0),
823 (int) (list->fx->y +
824 cos (f) * 4.0),
825 list->fx->brake,
826 list->fx->type);
827 }
828 }
829 }
830 }
831 }
832 }
833 if (list->fx->value == 0) {
834 free (list->fx);
835 if (list->prev)
836 list->prev->next = list->next;
837 else
838 level_effects = list->next;
839 if (list->next)
840 list->next->prev = list->prev;
841 else
842 lev_lastfx = list->prev;
843 free (list);
844 }
845 list = next;
846 }
847 draw_level ();
848 }
849
850