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