1 /*
2  * Luola - 2D multiplayer cave-flying game
3  * Copyright (C) 2001-2006 Calle Laakkonen
4  *
5  * File        : critter.c
6  * Description : Critters that walk,swim or fly around the level. Most are passive, but some attack (soldiers and helicopters)
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 <math.h>
26 
27 #include "console.h"
28 #include "critter.h"
29 #include "decor.h"
30 #include "player.h"
31 #include "level.h"
32 #include "game.h"
33 #include "list.h"
34 #include "ship.h"
35 #include "fs.h"
36 
37 #include "audio.h"
38 
39 /* List of critters */
40 struct dllist *critter_list;
41 
42 /* Number of limited critters */
43 static int soldier_count[4];
44 static int helicopter_count[4];
45 
46 /* Critter images */
47 static SDL_Surface **cow_gfx;
48 static SDL_Surface **bird_gfx;
49 static SDL_Surface **fish_gfx;
50 static SDL_Surface **bat_gfx;
51 static SDL_Surface **soldier_gfx;
52 static SDL_Surface **helicopter_gfx;
53 static SDL_Surface *bat_attack;
54 static SDL_Surface *iceblock;
55 
56 /* Number of frames in critter animations */
57 static int cow_frames;
58 static int bird_frames;
59 static int fish_frames;
60 static int bat_frames;
61 static int soldier_frames;
62 static int helicopter_frames;
63 
64 /* Bat attack! */
65 struct BatAttack {
66     SDL_Rect src;
67     SDL_Rect targ;
68     int end;
69     struct Critter *me;
70 } crit_bat_attack[16];
71 
72 /* Load critter data */
init_critters(LDAT * datafile)73 void init_critters (LDAT *datafile) {
74     cow_gfx =
75         load_image_array (datafile, 0, T_ALPHA, "COW", &cow_frames);
76     fish_gfx =
77         load_image_array (datafile, 0, T_ALPHA, "FISH", &fish_frames);
78     bird_gfx =
79         load_image_array (datafile, 0, T_ALPHA, "BIRD", &bird_frames);
80     bat_gfx =
81         load_image_array (datafile, 0, T_ALPHA, "BAT", &bat_frames);
82     soldier_gfx =
83         load_image_array (datafile, 0, T_COLORKEY, "INFANTRY", &soldier_frames);
84     helicopter_gfx =
85         load_image_array (datafile, 0, T_COLORKEY, "HELICOPTER", &helicopter_frames);
86     bat_attack = load_image_ldat (datafile, 1, T_ALPHA, "BAT_ATTACK", 0);
87     iceblock = load_image_ldat (datafile, 1, T_ALPHA, "ICE", 0);
88 }
89 
90 /* Add random number of critters of specified species in random locations */
add_random_critters(ObjectType species,int max)91 static void add_random_critters(ObjectType species,int max) {
92     if(max>0) {
93         int r,count;
94         count = rand()%max;
95         for(r=0;r<count;r++) {
96             struct Critter *critter = make_critter(species,-1,-1,-1);
97             if(critter)
98                 add_critter(critter);
99         }
100     }
101 }
102 
103 /* Add random and manually placed critters */
prepare_critters(struct LevelSettings * settings)104 void prepare_critters (struct LevelSettings * settings)
105 {
106     struct Critter *crit = NULL;
107     struct dllist *objects = NULL;
108     int r;
109 
110     /* Clear old critters */
111     dllist_free(critter_list,free);
112     critter_list=NULL;
113 
114     /* Stop here if critters are disabled */
115     if (level_settings.critters == 0)
116         return;
117 
118     /* Add random critters (if any) */
119     add_random_critters(OBJ_BIRD, level_settings.birds);
120     add_random_critters(OBJ_COW, level_settings.cows);
121     add_random_critters(OBJ_FISH, level_settings.fish);
122     add_random_critters(OBJ_BAT, level_settings.bats);
123 
124     /* Reset critter counters */
125     for (r = 0; r < 4; r++) {
126         soldier_count[r] = 0;
127         helicopter_count[r] = 0;
128     }
129     for (r = 0; r < sizeof(crit_bat_attack)/sizeof(struct BatAttack); r++) {
130         crit_bat_attack[r].src.y = 0;
131         crit_bat_attack[r].src.w = screen->w/2;
132         crit_bat_attack[r].src.h = screen->h/2;
133         crit_bat_attack[r].end = 0;
134     }
135 
136     /* Add manually placed critters */
137     if (settings)
138         objects = settings->objects;
139     while (objects) {
140         struct LSB_Object *object = objects->data;
141         if (object->type >= FIRST_CRITTER && object->type <= LAST_CRITTER) {
142             crit = make_critter (object->type, object->x, object->y, -1);
143             if(crit) {
144 #if 0
145                 if (object->ceiling_attach)
146                     critter->physics.y -= critter->gfx_rect.h;
147 #endif
148                 add_critter (crit);
149             }
150         }
151         objects = objects->next;
152     }
153 }
154 
155 /* Splash of blood */
splatter(struct Critter * critter)156 static void splatter(struct Critter *critter) {
157     add_splash(critter->physics.x, critter->physics.y,5.0,16,
158             critter->physics.vel,make_blood);
159 }
160 
161 /* Splash of blood and ice */
shatter(struct Critter * critter)162 static void shatter(struct Critter *critter) {
163     add_splash(critter->physics.x, critter->physics.y,5.0,8,
164             critter->physics.vel,make_blood);
165     add_splash(critter->physics.x, critter->physics.y,5.0,8,
166             critter->physics.vel,make_snowflake);
167 }
168 
169 /* Timer function: change ground critter walking direction */
gc_dosomething(struct Critter * critter)170 static void gc_dosomething(struct Critter *critter) {
171     /* Decisions, 0 stay still, 1 walk left, 2 walk right */
172     int decision = rand()%3;
173     switch(decision) {
174         case 0: critter->walker.walking = 0; break;
175         case 1:
176                 if(critter->walker.walking>0)
177                     critter->cornered+=1;
178                 critter->walker.walking = -1;
179                 break;
180         case 2:
181                 if(critter->walker.walking<0)
182                     critter->cornered+=1;
183                 critter->walker.walking = 1;
184                 break;
185     }
186     /* Time until next decision */
187     critter->timer = 1+rand()%60;
188 }
189 
190 /* Ground critter timer function: flee from any nearby enemy player */
gc_flee(struct Critter * critter)191 static void gc_flee(struct Critter *critter) {
192     if(critter->ff>0) {
193         double distance;
194         int enemy = find_nearest_enemy(critter->walker.physics.x,
195                 critter->walker.physics.y, critter->owner, &distance);
196         if(distance<200.0) {
197             critter->walker.walkspeed = 3;
198             if(players[enemy].ship->physics.x < critter->walker.physics.x) {
199                 if(critter->walker.walking<0)
200                     critter->cornered+=1;
201                 critter->walker.walking = 1;
202             } else {
203                 if(critter->walker.walking>0)
204                     critter->cornered+=1;
205                 critter->walker.walking = -1;
206             }
207         } else {
208             critter->walker.walkspeed = 1;
209         }
210         critter->ff--;
211         critter->timer = 1;
212     } else {
213         critter->walker.walkspeed = 1;
214         critter->timerfunc = gc_dosomething;
215         critter->timer = 2;
216     }
217 
218 }
219 
220 /* A bird dies in a splash of blood and feathers */
bird_die(struct Critter * critter)221 static void bird_die(struct Critter *critter) {
222     add_splash(critter->physics.x, critter->physics.y,5.0, 16,
223             critter->physics.vel,make_blood);
224     add_splash(critter->physics.x, critter->physics.y,3.0, 24,
225             critter->physics.vel,make_feather);
226     playwave_3d (WAV_CRITTER2, critter->physics.x, critter->physics.y);
227 }
228 
229 /* Helicopters explode */
helicopter_die(struct Critter * critter)230 static void helicopter_die(struct Critter *critter) {
231     spawn_clusters (critter->physics.x + critter->gfx_rect.w / 2,
232                         critter->physics.y + critter->gfx_rect.h / 2,
233                         5.6, 6, make_bullet);
234 }
235 
236 /* Ground critter dies and alerts others nearby */
gc_die(struct Critter * critter)237 static void gc_die(struct Critter *critter) {
238     struct dllist *lst=critter_list;
239     splatter(critter);
240     while(lst) {
241         struct Critter *c=lst->data;
242         if(c!=critter && c->type==GROUNDCRITTER &&
243                 fabs(c->physics.x-critter->physics.x)<250 &&
244                 fabs(c->physics.y-critter->physics.y)<250)
245         {
246             if(c->physics.x<critter->physics.x)
247                 c->walker.walking = -1;
248             else
249                 c->walker.walking = 1;
250             c->ff = 15 + rand()%30;
251             c->timerfunc = gc_flee;
252             c->timer = 0;
253         }
254         lst=lst->next;
255     }
256 }
257 
258 /* Air/water critter timer function: search a new target to go to */
ac_searchtarget(struct Critter * critter)259 static void ac_searchtarget(struct Critter *critter) {
260     unsigned int loops=0;
261     int newx,newy,tmp;
262     int oldx = Round(critter->physics.x);
263     int oldy = Round(critter->physics.y);
264 
265     /* Search for a new target that doesn't go thru solid terrain */
266     do {
267         newx = oldx + 200-rand()%400;
268         newy = oldy + 200-rand()%400;
269     } while(((critter->type==AIRCRITTER?is_water(newx,newy):is_free(newx,newy))
270             || hit_solid_line(oldx,oldy,newx,newy,&tmp,&tmp)
271             != (critter->type==AIRCRITTER?TER_FREE:TER_WATER))
272             && loops++<100);
273 
274     critter->flyer.targx = newx;
275     critter->flyer.targy = newy;
276 
277     critter->timer = 2*GAME_SPEED + rand()%(5*GAME_SPEED);
278 }
279 
280 /* Shoot */
critter_shoot(struct Critter * critter,double angle)281 static int critter_shoot(struct Critter *critter, double angle) {
282     double x = critter->physics.x + cos(angle)* critter->physics.radius;
283     double y = critter->physics.y + sin(angle)* critter->physics.radius;
284     if(is_solid(Round(x),Round(y))) return 0;
285     Vector mvel = {cos(angle) * 10, sin(angle)*10};
286     add_projectile(make_bullet(x,y,addVectors(critter->physics.vel,mvel)));
287     return 1;
288 };
289 
290 /* Find the nearest enemy critter */
291 /* Critters are identified by their graphics */
find_enemy_critter(float x,float y,int owner,double * distance,SDL_Surface ** gfx)292 static struct Critter *find_enemy_critter(float x,float y,int owner, double *distance,SDL_Surface **gfx) {
293     struct dllist *ptr = critter_list;
294     struct Critter *nearest = NULL;
295     double dist = 999999;
296     while(ptr) {
297         struct Critter *e = ptr->data;
298         if(e->gfx == gfx && same_team(e->owner,owner)==0) {
299             double d=hypot(e->physics.x-x,e->physics.y-y);
300             if(d<dist) {
301                 dist=d;
302                 nearest=e;
303             }
304         }
305         ptr=ptr->next;
306     }
307     if(distance)
308         *distance = dist;
309     return nearest;
310 }
311 
312 /* Search for a target to shoot at */
313 /* Set airborne to zero to limit targets to those that can be easily */
314 /* shot at from ground */
find_target(float x,float y,int owner,float * targx,float * targy,double * dist,int airborne)315 static int find_target(float x, float y, int owner, float *targx, float *targy,
316         double *dist, int airborne) {
317     double distance;
318     int eplr;
319     struct Critter *ec;
320     /* First priority, enemy ships */
321     eplr = find_nearest_enemy(x,y, owner, &distance);
322     if(distance < 120.0) {
323         *targx = players[eplr].ship->physics.x;
324         *targy = players[eplr].ship->physics.y;
325         if(dist) *dist = distance;
326         return 1;
327     }
328     /* Second priority, enemy helicopters */
329     ec = find_enemy_critter(x,y, owner ,&distance,helicopter_gfx);
330     if(distance < 120.0) {
331         *targx = ec->physics.x;
332         *targy = ec->physics.y;
333         if(dist) *dist = distance;
334         return 1;
335     }
336     if(airborne) {
337         /* Third priority, airborne only, enemy soldiers */
338         ec = find_enemy_critter(x,y, owner,&distance,soldier_gfx);
339         if(distance < 120.0) {
340             *targx = ec->physics.x;
341             *targy = ec->physics.y;
342             if(dist) *dist = distance;
343             return 1;
344         }
345         /* Fourth priority, airborne only, enemy pilots */
346         eplr = find_nearest_pilot(x,y,owner,&distance);
347         if(distance < 120.0) {
348             *targx = players[eplr].pilot.walker.physics.x;
349             *targy = players[eplr].pilot.walker.physics.y;
350             if(dist) *dist = distance;
351             return 1;
352         }
353     }
354     return 0;
355 }
356 
357 /* Search for enemies and shoot */
soldier_animate(struct Critter * soldier)358 static void soldier_animate(struct Critter *soldier) {
359     if(soldier->cooloff>0)
360         soldier->cooloff--;
361     else if(soldier->ff==0 || soldier->cornered>60.0) {
362         float targx,targy;
363         if(find_target(soldier->physics.x, soldier->physics.y,
364                     soldier->owner, &targx, &targy, NULL, 0))
365         {
366             if(critter_shoot(soldier,atan2(targy-soldier->physics.y,targx-soldier->physics.x))) {
367                 soldier->walker.walking = 0;
368                 if(soldier->cornered>70.0) /* Shoot like mad when cornered */
369                     soldier->cooloff = 0.15*GAME_SPEED;
370                 else
371                     soldier->cooloff = 0.7*GAME_SPEED;
372             }
373         }
374     }
375 }
376 
377 /* Search for enemies and shoot */
helicopter_animate(struct Critter * hc)378 static void helicopter_animate(struct Critter *hc) {
379     if(hc->cooloff>0)
380         hc->cooloff--;
381     else if(hc->ff==0) {
382         double distance;
383         float targx,targy;
384         if(find_target(hc->physics.x,hc->physics.y, hc->owner, &targx, &targy,
385                     &distance, 1))
386         {
387             if(distance>60.0) {
388                 hc->flyer.targx = targx;
389                 hc->flyer.targy = targy;
390             }
391             if(distance<150.0) {
392                 if(critter_shoot(hc,atan2(targy-hc->physics.y,targx-hc->physics.x))) {
393                     hc->cooloff = 0.7*GAME_SPEED;
394                 }
395             }
396         }
397     }
398 }
399 
400 /* Bats seek out a place to perch */
bat_seekground(struct Critter * bat)401 static void bat_seekground(struct Critter *bat) {
402     double a = (rand()%3141)/1000.0;
403     int targx = Round(bat->physics.x) + cos(a)*150;
404     int targy = Round(bat->physics.y) - sin(a)*150;
405 
406     hit_solid_line(Round(bat->physics.x),Round(bat->physics.y),
407             targx,targy,&targx,&targy);
408     bat->flyer.targx = targx;
409     bat->flyer.targy = targy;
410 }
411 
412 /* Bat attack. Set position on player viewport where bat image */
413 /* will be drawn in draw_bat_attack() */
bat_harass_player(struct Critter * bat,int plr)414 static void bat_harass_player(struct Critter *bat, int plr) {
415     int b=0;
416     for(;b<sizeof(crit_bat_attack)/sizeof(struct BatAttack);b++) {
417         if(crit_bat_attack[b].me == bat && crit_bat_attack[b].end)
418             break;
419         if (crit_bat_attack[b].end == 0) {
420             int dx, dy;
421             dx = cam_rects[plr].w/2 - rand () % cam_rects[plr].w;
422             dy = cam_rects[plr].h/2 - rand () % cam_rects[plr].h;
423             crit_bat_attack[b].targ.x = viewport_rects[plr].x + dx;
424             crit_bat_attack[b].targ.y = viewport_rects[plr].y + dy;
425             crit_bat_attack[b].src.x = 0;
426             crit_bat_attack[b].src.y = 0;
427             crit_bat_attack[b].src.w = bat_attack->w;
428             crit_bat_attack[b].src.h = bat_attack->h;
429             if (dx < 0) {
430                 crit_bat_attack[b].src.x -= dx;
431                 crit_bat_attack[b].src.w = bat_attack->w + dx;
432                 crit_bat_attack[b].targ.x -= dx;
433             } else if(dx+bat_attack->w>cam_rects[plr].w) {
434                 crit_bat_attack[b].src.w -= dx + bat_attack->w - cam_rects[plr].w;
435             }
436             if (dy < 0) {
437                 crit_bat_attack[b].src.y = -dy;
438                 crit_bat_attack[b].src.h = bat_attack->h + dy;
439                 crit_bat_attack[b].targ.y -= dy;
440             } else if(dy+bat_attack->h>cam_rects[plr].h) {
441                 crit_bat_attack[b].src.h -= dy + bat_attack->h - cam_rects[plr].h;
442             }
443             crit_bat_attack[b].me = bat;
444             crit_bat_attack[b].end = 10;
445             break;
446         }
447     }
448 }
449 
450 /* Bats stay mainly still unless disturbed */
bat_animate(struct Critter * bat)451 static void bat_animate(struct Critter *bat) {
452     if(is_breathable(Round(bat->physics.x),Round(bat->physics.y)-1)) {
453         /* Seek out nearby ships or ground */
454         double d;
455         struct Ship *targ = find_nearest_ship(bat->physics.x,bat->physics.y,NULL,&d);
456         if(d<200.0) {
457             bat->flyer.targx = targ->physics.x;
458             bat->flyer.targy = targ->physics.y;
459             if(d<20.0) {
460                 int p=0;
461                 for(;p<4;p++) {
462                     if(players[p].ship == targ) {
463                         bat_harass_player(bat,p);
464                         break;
465                     }
466                 }
467             }
468         } else if(bat->timer<0) {
469             bat->timer = 2*GAME_SPEED + rand()%(2*GAME_SPEED);
470         }
471     }
472 }
473 
474 /* Make a basic critter skeleton */
make_base(SDL_Surface ** gfx)475 static struct Critter *make_base(SDL_Surface **gfx) {
476     struct Critter *c = malloc(sizeof(struct Critter));
477     if(!c) {
478         perror(__func__);
479         return NULL;
480     }
481 
482     c->gfx = gfx;
483     c->gfx_rect.x = 0;
484     c->gfx_rect.y = 0;
485     c->gfx_rect.w = gfx[0]->w;
486     c->gfx_rect.h = gfx[0]->h;
487     c->frame = 0;
488     c->bidir = 1;
489 
490     c->health = 0.01;
491     c->owner = -1;
492     c->ship = 1;
493     c->frozen = 0;
494 
495     c->timer=-1;
496     c->ff = 0;
497     c->cooloff = 0;
498     c->cornered = 0;
499 
500     c->animate = NULL;
501 
502     return c;
503 }
504 
505 /* Create a basic ground critter */
make_base_gc(SDL_Surface ** gfx,float x,float y)506 static struct Critter *make_base_gc(SDL_Surface **gfx,float x,float y) {
507     struct Critter *c = make_base(gfx);
508     c->type = GROUNDCRITTER;
509     init_walker(&c->walker);
510     c->physics.x = x;
511     c->physics.y = y;
512     c->physics.radius = 6;
513     c->physics.mass = 12;
514     c->walker.walking = rand()%2?-1:1;
515     c->walker.walkspeed = 1;
516     c->walker.slope = 5;
517     c->ship = 0;
518 
519     c->timer = 1;
520     c->timerfunc = gc_dosomething;
521     c->die = gc_die;
522     return c;
523 }
524 
525 /* Make a cow */
make_cow(float x,float y)526 static struct Critter *make_cow(float x,float y) {
527     struct Critter *cow = make_base_gc(cow_gfx,x,y);
528     cow->frames = cow_frames;
529     return cow;
530 }
531 
532 /* Make a soldier */
make_soldier(float x,float y,int owner)533 static struct Critter *make_soldier(float x,float y,int owner) {
534     struct Critter *soldier = make_base_gc(soldier_gfx,x,y);
535     soldier->frames = soldier_frames;
536     soldier->walker.slope=10;
537     soldier->gfx_rect.w/=4;
538     soldier->gfx_rect.x = soldier->gfx_rect.w*owner;
539     soldier->animate = soldier_animate;
540     soldier->owner=owner;
541 
542     return soldier;
543 }
544 
545 /* Create a basic air critter */
make_base_ac(SDL_Surface ** gfx,float x,float y)546 static struct Critter *make_base_ac(SDL_Surface **gfx,float x,float y) {
547     struct Critter *c = make_base(gfx);
548     init_flyer(&c->flyer,AIRBORNE);
549     c->type = AIRCRITTER;
550     c->physics.x = x;
551     c->physics.y = y;
552 
553     c->timer = 0;
554     c->timerfunc = ac_searchtarget;
555     return c;
556 }
557 
558 /* Create a bird */
make_bird(float x,float y)559 static struct Critter *make_bird(float x,float y) {
560     struct Critter *bird = make_base_ac(bird_gfx,x,y);
561     bird->frames = bird_frames;
562     bird->bidir = 0;
563     bird->die = bird_die;
564     return bird;
565 }
566 
567 /* Create a bat */
make_bat(float x,float y)568 static struct Critter *make_bat(float x,float y) {
569     struct Critter *bat = make_base_ac(bat_gfx,x,y);
570     bat->health = 0.05;
571     bat->frames = bat_frames;
572     bat->bidir = 0;
573     bat->flyer.bat = 1;
574     bat->ship = 0;
575     bat->timerfunc = bat_seekground;
576     bat->animate = bat_animate;
577     bat->die = splatter;
578     return bat;
579 }
580 
581 /* Create a helicopter */
make_helicopter(float x,float y,int owner)582 static struct Critter *make_helicopter(float x,float y,int owner) {
583     struct Critter *hc = make_base_ac(helicopter_gfx,x,y);
584     hc->frames = helicopter_frames;
585     hc->health = 0.2;
586     hc->bidir = 1;
587     hc->gfx_rect.w/=4;
588     hc->gfx_rect.x = hc->gfx_rect.w*owner;
589     hc->animate = helicopter_animate;
590     hc->owner = owner;
591     hc->ship = 0;
592     hc->die = helicopter_die;
593     return hc;
594 }
595 
596 /* Create a basic water critter */
make_base_wc(SDL_Surface ** gfx,float x,float y)597 static struct Critter *make_base_wc(SDL_Surface **gfx,float x,float y) {
598     struct Critter *c = make_base(gfx);
599     init_flyer(&c->flyer,UNDERWATER);
600     c->flyer.speed = 0.5;
601     c->type = WATERCRITTER;
602     c->physics.x = x;
603     c->physics.y = y;
604 
605     c->timer = 0;
606     c->timerfunc = ac_searchtarget;
607     return c;
608 }
609 
610 /* Create a fish */
make_fish(float x,float y)611 static struct Critter *make_fish(float x,float y) {
612     struct Critter *fish = make_base_wc(fish_gfx,x,y);
613     fish->frames = fish_frames;
614     fish->bidir = 1;
615     fish->die = splatter;
616     return fish;
617 }
618 
619 /* Find a random starting position. If ground=-+1, coordinates
620  * will be at the interface of walkable terrain and the medium.
621  * x and y will be set to the discovered coordinates.
622  * Returns nonzero if no suitable coordinates were found.
623  */
random_coords(Uint8 medium,int ground,float * xcoord,float * ycoord)624 static int random_coords(Uint8 medium, int ground,float *xcoord,float *ycoord) {
625     unsigned int loops=0;
626     while(loops++<1000) {
627         int x = rand()%lev_level.width;
628         int y = rand()%lev_level.height;
629         if(lev_level.solid[x][y]==medium) {
630             if(ground) {
631                 int r=0;
632                 for(r=0;r<200;r++,y+=ground) {
633                     if(y<0 || y>=lev_level.height ||
634                             lev_level.solid[x][y]!=medium) break;
635                 }
636                 if(is_walkable(x,y)==0) continue;
637             }
638             *xcoord = x;
639             *ycoord = y;
640             return 0;
641         }
642     }
643     return 1;
644 }
645 
646 /* Make a critter */
make_critter(ObjectType species,float x,float y,int owner)647 struct Critter *make_critter (ObjectType species, float x, float y, int owner)
648 {
649     struct Critter *newcritter = NULL;
650     switch(species) {
651         case OBJ_COW: newcritter = make_cow(x,y); break;
652         case OBJ_SOLDIER: newcritter = make_soldier(x,y,owner); break;
653         case OBJ_BIRD: newcritter = make_bird(x,y); break;
654         case OBJ_BAT: newcritter = make_bat(x,y); break;
655         case OBJ_HELICOPTER: newcritter = make_helicopter(x,y,owner); break;
656         case OBJ_FISH: newcritter = make_fish(x,y); break;
657         default:
658            fprintf(stderr,"make_critter(%d,%.1f,%.1f,%d): Unhandled object type.\n",species, x, y, owner);
659     }
660     if(newcritter==NULL) return NULL;
661 
662 
663     /* Find a place for the critter if not specified explicitly */
664     if (x < 0 || y < 0) {
665         int medium,ground;
666         if(newcritter->type==WATERCRITTER) {
667             medium = TER_WATER;
668             ground = 0;
669         } else {
670             medium = TER_FREE;
671             if(newcritter->type==GROUNDCRITTER)
672                 ground=1;
673             else if(species==OBJ_BAT)
674                 ground=-1;
675             else
676                 ground=0;
677         }
678         if(random_coords(medium,ground,&newcritter->physics.x,
679                     &newcritter->physics.y)) {
680             fprintf(stderr,"Couldn't find a place for a %s.\n", obj2str(species));
681             free (newcritter);
682             return NULL;
683         }
684     }
685     return newcritter;
686 }
687 
688 /* Kill a critter */
kill_critter(struct dllist * list)689 static struct dllist *kill_critter (struct dllist *list) {
690     struct dllist *next=list->next;
691     struct Critter *c = list->data;
692 
693     c->die(c);
694 
695     if(c->gfx == soldier_gfx && c->owner>=0)
696         soldier_count[c->owner]--;
697     else if(c->gfx == helicopter_gfx && c->owner>=0)
698         helicopter_count[c->owner]--;
699 
700     free (list->data);
701 
702     if(list==critter_list)
703         critter_list=dllist_remove(list);
704     else
705         dllist_remove(list);
706     return next;
707 }
708 
709 /* Add a critter to the list */
add_critter(struct Critter * newcritter)710 void add_critter (struct Critter * newcritter) {
711     int kill=0;
712     /* Enforce hostile critter limits */
713     if(newcritter->gfx == soldier_gfx && game_settings.soldiers>0 &&
714             newcritter->owner>=0)
715     {
716         if(++soldier_count[newcritter->owner] >= game_settings.soldiers)
717             kill=1;
718     } else if(newcritter->gfx == helicopter_gfx && game_settings.helicopters>0
719             && newcritter->owner>=0)
720     {
721         if(++helicopter_count[newcritter->owner] >= game_settings.helicopters)
722             kill=1;
723     }
724     if(kill) {
725         struct dllist *ptr = critter_list;
726         while(ptr) {
727             struct Critter *c = ptr->data;
728             if(c->gfx == newcritter->gfx && c->owner == newcritter->owner) {
729                 kill_critter(ptr);
730                 break;
731             }
732             ptr=ptr->next;
733         }
734     }
735 
736     /* Add critter to the list */
737     if (critter_list)
738         dllist_append(critter_list,newcritter);
739     else
740         critter_list=dllist_append(critter_list,newcritter);
741 }
742 
743 /* Projectile hits a critter */
hit_critter(struct Critter * critter,struct Projectile * p)744 void hit_critter(struct Critter *critter, struct Projectile *p) {
745     critter->health -= p->damage;
746     /* special case, snowball freezes critters */
747     if(p->color == col_snow && p->damage==0 && p->critical==0) {
748         critter->timer=-1;
749         critter->animate = NULL;
750         critter->timerfunc = NULL;
751         critter->die = shatter;
752         critter->type = INERTCRITTER;
753         critter->frozen = 1;
754     }
755 }
756 
757 /* Draw a critter on all active viewports */
draw_critter(struct Critter * critter)758 static void draw_critter (struct Critter * critter)
759 {
760     int p;
761     for (p = 0; p < 4; p++) {
762         if (players[p].state==ALIVE || players[p].state==DEAD) {
763             SDL_Rect rect, rect2;
764             rect.x = critter->physics.x - critter->gfx_rect.w/2;
765             rect.y = critter->physics.y;
766             if(critter->type==GROUNDCRITTER)
767                 rect.y -= critter->gfx_rect.h;
768             else
769                 rect.y -= critter->gfx_rect.h/2;
770 
771             rect.x = rect.x - cam_rects[p].x + viewport_rects[p].x;
772             rect.y = rect.y - cam_rects[p].y + viewport_rects[p].y;
773             if ((rect.x > viewport_rects[p].x - critter->gfx_rect.w
774                  && rect.x < viewport_rects[p].x + cam_rects[p].w)
775                 && (rect.y > viewport_rects[p].y - critter->gfx_rect.h
776                     && rect.y < viewport_rects[p].y + cam_rects[p].h)) {
777                 rect2 =
778                     cliprect (rect.x, rect.y, critter->gfx_rect.w,
779                             critter->gfx_rect.h, viewport_rects[p].x,
780                             viewport_rects[p].y,
781                             viewport_rects[p].x + cam_rects[p].w,
782                             viewport_rects[p].y + cam_rects[p].h);
783                 rect2.x += critter->gfx_rect.x;
784                 rect2.y += critter->gfx_rect.y;
785                 if (rect.x < viewport_rects[p].x)
786                     rect.x = viewport_rects[p].x;
787                 if (rect.y < viewport_rects[p].y)
788                     rect.y = viewport_rects[p].y;
789                 SDL_BlitSurface (critter->gfx[critter->frame],
790                         &rect2, screen, &rect);
791                 if(critter->frozen && iceblock) {
792                     rect.x = rect.x + critter->gfx_rect.w/2 - iceblock->w/2;
793                     rect.y = rect.y + critter->gfx_rect.h/2 - iceblock->h/2;
794                     SDL_BlitSurface(iceblock, NULL, screen, &rect);
795                 }
796 #if 0
797                 /* Debugging aid: display critter target */
798                 if(critter->type!=GROUNDCRITTER) {
799                     int x = critter->flyer.targx-cam_rects[p].x;
800                     int y = critter->flyer.targy-cam_rects[p].y;
801                     if(x>0 && x<cam_rects[p].w && y>0 && y<cam_rects[p].h)
802                         putpixel(screen,x+viewport_rects[p].x,y+viewport_rects[p].y,col_red);
803                         putpixel(screen,x+2+viewport_rects[p].x,y+viewport_rects[p].y,col_red);
804                         putpixel(screen,x-2+viewport_rects[p].x,y+viewport_rects[p].y,col_red);
805                         putpixel(screen,x+viewport_rects[p].x,y+2+viewport_rects[p].y,col_red);
806                         putpixel(screen,x+viewport_rects[p].x,y-2+viewport_rects[p].y,col_red);
807                 }
808 #endif
809             }
810         }
811     }
812 }
813 
814 /* Some generic ground critter animation */
animate_groundcritter(struct Critter * critter)815 static void animate_groundcritter(struct Critter *critter) {
816     animate_walker(&critter->walker,critter->ship?1:0,&ship_list);
817     if(critter->walker.walking) {
818         int x = Round(critter->walker.physics.x);
819         int y = Round(critter->walker.physics.y);
820         x += critter->walker.walkspeed * critter->walker.walking;
821         if(find_foothold(x,y, critter->walker.slope)<0) {
822             /* Turn around if can't find foothold */
823             critter->walker.walking *= -1;
824             critter->cornered += 1;
825         }
826         /* Update animation frame */
827         critter->frame++;
828         if(critter->walker.walking>0) {
829             if(critter->frame>=critter->frames/2)
830                 critter->frame=0;
831         } else {
832             if(critter->frame>=critter->frames)
833                 critter->frame=critter->frames/2;
834         }
835     } else {
836         /* Reset to neutral pose */
837         if(critter->frame>=critter->frames/2)
838             critter->frame=critter->frames/2;
839         else
840             critter->frame=0;
841     }
842 }
843 
844 /* Some generic air critter animation */
animate_aircritter(struct Critter * critter)845 static void animate_aircritter(struct Critter *critter) {
846     animate_flyer(&critter->flyer,critter->ship?1:0,&ship_list);
847     if(critter->physics.hitground && is_walkable(Round(critter->physics.x),Round(critter->physics.y) + (critter->flyer.bat?1:-1))==0) {
848         /* Flying critter is perched */
849         critter->frame = critter->frames-1;
850     } else {
851         critter->frame++;
852         if(critter->bidir) {
853             if(critter->physics.vel.x>0) {
854                 if(critter->frame>=(critter->frames-1)/2)
855                     critter->frame=0;
856             } else {
857                 if(critter->frame>=critter->frames-1)
858                     critter->frame=(critter->frames-1)/2;
859             }
860         } else {
861             if(critter->frame>=critter->frames-1)
862                 critter->frame=0;
863         }
864     }
865 }
866 
867 /* Some generic water critter animation */
animate_watercritter(struct Critter * critter)868 static void animate_watercritter(struct Critter *critter) {
869     animate_flyer(&critter->flyer,critter->ship?1:0,&ship_list);
870     critter->frame++;
871     if(critter->bidir) {
872         if(critter->physics.vel.x>0) {
873             if(critter->frame>=critter->frames/2)
874                 critter->frame=0;
875         } else {
876             if(critter->frame>=critter->frames)
877                 critter->frame=critter->frames/2;
878         }
879     } else {
880         if(critter->frame>=critter->frames)
881             critter->frame=0;
882     }
883 }
884 
885 /* Animate critters */
animate_critters(void)886 void animate_critters (void) {
887     struct dllist *list = critter_list;
888     while (list) {
889         struct Critter *critter=list->data;
890         switch(critter->type) {
891             case INERTCRITTER: animate_object(&critter->physics,1,&ship_list); break;
892             case GROUNDCRITTER: animate_groundcritter(critter); break;
893             case AIRCRITTER: animate_aircritter(critter); break;
894             case WATERCRITTER: animate_watercritter(critter); break;
895         }
896 
897         /* Timer function */
898         if(critter->timer>0) critter->timer--;
899         else if(critter->timer==0) {
900             critter->timer=-1;
901             critter->timerfunc(critter);
902         }
903 
904         /* Decrease feeling of claustrophobia */
905         if(critter->cornered>0)
906             critter->cornered -= 1.0/GAME_SPEED;
907 
908         /* Special animation if any */
909         if(critter->animate)
910             critter->animate(critter);
911 
912         /* Check if critter has been thrown against ground too hard */
913         if(critter->physics.hitground && hypot(critter->physics.hitvel.x,
914                     critter->physics.hitvel.y)> 5.0)
915         {
916             critter->health -= 0.1;
917         }
918 
919         /* Kill critter if health is below 0 and/or move on to the next one */
920         if(critter->health<=0) {
921             list = kill_critter(list);
922         } else {
923             draw_critter (critter);
924             list = list->next;
925         }
926     }
927 }
928 
929 /* Draw the bat attack */
draw_bat_attack()930 void draw_bat_attack ()
931 {
932     int b;
933     if (!bat_attack)
934         return;
935     for (b = 0; b < sizeof(crit_bat_attack)/sizeof(struct BatAttack); b++) {
936         if (crit_bat_attack[b].end) {
937             crit_bat_attack[b].end--;
938             SDL_BlitSurface (bat_attack, &crit_bat_attack[b].src, screen,
939                              &crit_bat_attack[b].targ);
940         }
941     }
942 }
943 
944