1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <string.h>
5 
6 #include "xtux.h"
7 #include "server.h"
8 #include "entity.h"
9 #include "clients.h"
10 #include "sv_map.h"
11 #include "weapon.h"
12 #include "world.h"
13 #include "sv_net.h"
14 #include "ai.h"
15 #include "item.h"
16 #include "game.h"
17 
18 extern server_t server;
19 extern game_t game;
20 extern byte num_weapon_types;
21 
22 /* Start and end of entity list, initially NULL */
23 entity *root;
24 entity *tail;
25 
26 int num_entities;
27 byte num_entity_types;
28 
29 /* Keeps track of how entities of each class there are */
30 int class_population[NUM_ENT_CLASSES];
31 
32 static int id_num = 1;
33 
34 static void entity_update_movement(float secs);
35 static void entity_check_collisions(void);
36 static void entity_animate(void);
37 static void entity_die(entity *ent);
38 
39 
40 /* Called once at program initialisation */
entity_init(void)41 void entity_init(void)
42 {
43     num_entity_types = entity_type_init();
44 }
45 
46 
entity_drip(entity * ent,byte color1,byte color2)47 void entity_drip(entity *ent, byte color1, byte color2)
48 {
49     netmsg msg;
50 
51     msg.type = NETMSG_PARTICLES;
52     msg.particles.effect = P_DRIP;
53     msg.particles.dir = ent->dir;
54     msg.particles.length = 1;
55     msg.particles.color1 = color1;
56     msg.particles.color2 = color2;
57     /* Draw with offsets to make it look good on the client */
58     msg.particles.x = ent->x + ent->type_info->width/2;
59     msg.particles.y = ent->y + ent->type_info->height/2;
60     sv_net_send_to_all(msg);
61 
62 }
63 
64 
entity_spawn_effect(entity * ent)65 void entity_spawn_effect(entity *ent)
66 {
67     netmsg msg;
68 
69     /* Send respawning effect to clients */
70     msg.type = NETMSG_PARTICLES;
71     msg.particles.effect = P_SPAWN;
72     msg.particles.color1 = COL_WHITE;
73     msg.particles.color2 = COL_GRAY;
74     msg.particles.length = ent->type_info->width / 2;
75     msg.particles.x = ent->x + ent->type_info->width/2;
76     msg.particles.y = ent->y + ent->type_info->height/2;
77     sv_net_send_to_all(msg);
78 
79 }
80 
81 /* How often the entity drips while invisible */
82 #define INVISIBLE_DRIP_TIME 500
83 
entity_update(float secs)84 void entity_update(float secs)
85 {
86     ent_type_t *et;
87     entity *ent, *next;
88     msec_t now, time;
89     int i;
90 
91     now = server.now;
92 
93     for( ent = root ; ent != NULL ; ent = ent->next ) {
94 	et = ent->type_info;
95 
96 
97 
98 	if( ent->dripping ) {
99 	    time = (ent->dripping==1)? et->drip_time : INVISIBLE_DRIP_TIME;
100 	    if( now - ent->last_drip >= time ) {
101 		entity_drip(ent, ent->drip1, ent->drip2);
102 		ent->last_drip = now;
103 	    }
104 	}
105 
106 	if( ent->lookatdir > 0 ) {
107 	    ent->speed = 0;
108 	    /* make the entity slowly turn towards the lookatpoint */
109 	    if( ent->dir > ent->lookatdir )
110 		ent->dir -= MAX( 1, (ent->dir - ent->lookatdir)/10);
111 	    else if( ent->dir < ent->lookatdir )
112 		ent->dir += MAX( 1, (ent->lookatdir - ent->dir)/10);
113 	    else
114 		ent->lookatdir = -1;
115 	} else if( ent->mode == ALIVE )
116 	    ent->speed = et->speed;
117 	else if( ent->mode == LIMBO ) { 	/* Respawn items */
118 	    if( ent->respawn_time && (now >= ent->respawn_time) ) {
119 		ent->mode = ALIVE;
120 		ent->respawn_time = 0;
121 		entity_spawn_effect(ent);
122 	    }
123 	}
124 
125 	if( ent->powerup ) {
126 	    /* Entity is wounded */
127 	    if( ent->powerup & (1<<PU_WOUND) )
128 		if( now - ent->last_wound >= M_SEC ) {
129 		    entity_drip(ent, COL_RED, COL_RED);	/* Drip blood */
130 		    ent->health -= 2;
131 		    ent->last_wound = now;
132 		}
133 
134 	    if( ent->powerup & (1<<PU_INVISIBILITY) ) {
135 		ent->visible = 0;
136 		if( ent->x_v || ent->y_v ) { /* Drip if moving */
137 		    ent->dripping = 2;
138 		    ent->drip1 = ent->color1;
139 		    ent->drip2 = ent->color2;
140 		} else {
141 		    ent->dripping = et->dripping;
142 		    ent->drip1 = et->drip1;
143 		    ent->drip2 = et->drip2;
144 		}
145 	    }
146 
147 	    for( i=0 ; i<NUM_POWERUPS ; i++ ) {
148 		if( ent->powerup & (1<<i) && now > ent->powerup_expire[i] ) {
149 		    ent->powerup &= ~(1<<i);
150 		    ent->powerup_expire[i] = 0;
151 
152 		    switch( i ) {
153 		    case PU_INVISIBILITY:
154 			ent->dripping = et->dripping;
155 			ent->drip1 = et->drip1;
156 			ent->drip2 = et->drip2;
157 			ent->visible = 1;
158 			break;
159 		    case PU_FROZEN:
160 			if( ent->mode == FROZEN )
161 			    ent->mode = ALIVE;
162 			break;
163 		    case PU_E:
164 			/* Worn off, entity is now on a come down... */
165 			if( ent->mode == ENLIGHTENED ) {
166 			    ent->mode = COMEDOWN;
167 			    ent->speed = et->speed * 0.50;
168 			    ent->powerup |= (1<<PU_E); /* Restore bit */
169 			    ent->powerup_expire[PU_E] = now + M_SEC * 10;
170 			} else if( ent->mode == COMEDOWN ) {
171 			    ent->mode = ALIVE;
172 			}
173 			break;
174 		    }
175 		}
176 	    }
177 	}
178     }
179 
180     entity_update_movement(secs);
181     entity_check_collisions();
182     entity_animate();
183 
184     for( ent = root ; ent != NULL ; ent = next ) {
185 	next = ent->next;
186 	if( ent->mode >= FROZEN && ent->health <= 0 )
187 	    entity_die(ent); /* Set to either dead or dying */
188 
189 	if( ent->mode == DEAD ) {
190 	    if( ent->controller == CTRL_CLIENT )
191 		spawn(ent, 1); /* Respawn */
192 	    else
193 		entity_delete(ent);
194 	}
195     }
196 
197 }
198 
199 
entity_update_movement(float secs)200 static void entity_update_movement(float secs)
201 {
202     entity  *ent;
203 
204     for( ent = root ;  ent != NULL ; ent = ent->next ) {
205 	if( ent->mode >= ALIVE && ent->ai && ent->controller == CTRL_AI ) {
206 	    switch( ent->ai ) {
207 	    case AI_NONE:
208 		break;
209 	    case AI_FIGHT:
210 		if( ent->weapon ) {
211 		    ai_shooter_think(ent);
212 		} else
213 		    ai_fight_think(ent);
214 		break;
215 	    case AI_FLEE:
216 		ai_flee_think(ent);
217 		break;
218 	    case AI_SEEK:
219 		ai_seek_think(ent);
220 		break;
221 	    case AI_ROTATE:
222 		ai_rotate_think(ent);
223 		break;
224 	    default:
225 		break;
226 	    }
227 	    ai_move(ent);
228 	}
229 
230 	if( ent->class != PROJECTILE )
231 	    world_friction(ent, secs);
232 
233 	if( ent->mode >= ALIVE ) {
234 	    if( ent->x_v > 0 ) {
235 		if( ent->x_v > ent->speed )
236 		    ent->x_v = ent->speed;
237 	    } else if( ent->x_v < 0 ) {
238 		if( ent->x_v < -ent->speed )
239 		    ent->x_v = -ent->speed;
240 	    }
241 
242 	    if( ent->y_v > 0 ) {
243 		if( ent->y_v > ent->speed )
244 		    ent->y_v = ent->speed;
245 	    } else if( ent->y_v < 0 ) {
246 		if( ent->y_v < -ent->speed )
247 		    ent->y_v = -ent->speed;
248 	    }
249 	}
250 
251 	world_check_entity_move(ent, secs);
252 
253     }
254 
255 }
256 
257 
258 /*
259 
260     Here's the order (from ../common/entity_type.h). You only need
261     to handle collision between first and the second->class's that
262     come after first's->class.
263 
264     GOODIE,
265     BADDIE,
266     NEUTRAL,
267     ITEM,
268     PROJECTILE,
269     NUM_ENT_CLASSES
270 */
271 
272 
col_goodie(entity * goodie,entity * second)273 static void col_goodie(entity *goodie, entity *second)
274 {
275     entity *shooter;
276     point pt;
277     int damage;
278 
279     switch( second->class ) {
280     case GOODIE:
281 	/* Nothing.... */
282 	break;
283     case BADDIE:
284 	/* Touch damage is delt over a second */
285 	if( (damage = second->type_info->touchdamage / server.fps ) < 1 )
286 	    damage = 1; /* Correct for rounding down */
287 	goodie->health -= damage;
288 	if( goodie->health <= 0 )
289 	    entity_killed(second, goodie, pt, 0);
290 	/* Slow the baddie down while he does damage */
291 	second->x_v *= 0.25;
292 	second->y_v *= 0.25;
293 	break;
294     case NEUTRAL:
295 	/* Nothing... */
296 	break;
297     case ITEM:
298 	item_collision(second, goodie);
299 	break;
300     case PROJECTILE:
301 	shooter = findent(second->pid);
302 	pt.x = second->x;
303 	pt.y = second->y;
304 	weapon_hit( shooter, goodie, pt, second->weapon );
305 	second->health = 0; /* Remove projectile */
306 	break;
307     default:
308 	printf("NOT HANDLED!\n");
309     }
310 
311 }
312 
313 
col_baddie(entity * baddie,entity * second)314 static void col_baddie(entity *baddie, entity *second)
315 {
316     entity *shooter;
317     point pt;
318     int damage;
319 
320     switch( second->class ) {
321     case BADDIE:
322 	/* Do damage if baddies are fighting */
323 	if( baddie->target == second ) {
324 	    if( (damage = baddie->type_info->touchdamage / server.fps) < 1 )
325 		damage = 1; /* Correct for rounding down */
326 	    second->health -= damage;
327 	    second->target = baddie;
328 	} else if( second->target == baddie ) {
329 	    if( (damage = second->type_info->touchdamage / server.fps) < 1 )
330 		damage = 1; /* Correct for rounding down */
331 	    baddie->health -= damage;
332 	    baddie->target = second;
333 	}
334 	break;
335     case NEUTRAL:
336 	/* Nothing.... */
337 	break;
338     case ITEM:
339 	/* item_collision(second, baddie); */
340 	break;
341     case PROJECTILE:
342 	shooter = findent(second->pid);
343 	pt.x = second->x;
344 	pt.y = second->y;
345 	weapon_hit( shooter, baddie, pt, second->weapon);
346 	second->health = 0; /* Remove projectile */
347 	break;
348     default:
349 	printf("NOT HANDLED!\n");
350     }
351 
352 }
353 
354 
col_neutral(entity * neutral,entity * second)355 static void col_neutral(entity *neutral, entity *second)
356 {
357     entity *shooter;
358     point pt;
359 
360     switch( second->class ) {
361     case NEUTRAL:
362 	break;
363     case ITEM:
364 	/* Nothing.... */
365 	break;
366     case PROJECTILE:
367 	shooter = findent(second->pid);
368 	pt.x = second->x;
369 	pt.y = second->y;
370 	weapon_hit( shooter, neutral, pt, second->weapon);
371 	second->health = 0; /* Remove projectile */
372 	break;
373     default:
374 	printf("NOT HANDLED!\n");
375     }
376 
377 }
378 
379 
col_item(entity * item,entity * second)380 static void col_item(entity *item, entity *second)
381 {
382 
383     switch( second->class ) {
384     case ITEM:
385 	/* Nothing.... */
386 	break;
387     case PROJECTILE:
388 	/* Nothing.... */
389 	break;
390     default:
391 	printf("NOT HANDLED!\n");
392     }
393 
394 }
395 
396 
397 /* In theory this should only be called with both first and second
398    being projectiles */
col_projectile(entity * proj,entity * second)399 static void col_projectile(entity *proj, entity *second)
400 {
401     entity *shooter;
402     point pt;
403 
404     switch( second->class ) {
405     case PROJECTILE:
406 	/* Projectile hits second */
407 	if( (shooter = findent(proj->pid)) != NULL ) {
408 	    pt.x = proj->x;
409 	    pt.y = proj->y;
410 	    weapon_hit( shooter, second, pt, proj->weapon);
411 	}
412 
413 	/* Second hits projectile */
414 	if( (shooter = findent(second->pid)) != NULL ) {
415 	    pt.x = second->x;
416 	    pt.y = second->y;
417 	    weapon_hit( shooter, proj, pt, second->weapon);
418 	}
419 
420 	break;
421     default:
422 	printf("NOT HANDLED!\n");
423     }
424 
425 }
426 
427 
428 static void (*handle_collision[NUM_ENT_CLASSES])(entity *,  entity *) = {
429     col_goodie,
430     col_baddie,
431     col_neutral,
432     col_item,
433     col_projectile
434 };
435 
436 
437 /* FIXME: Optimise and make correct (use intersection tests). */
entity_check_collisions(void)438 void entity_check_collisions(void)
439 {
440     entity *ent1, *ent2;
441     entity *first, *second;
442     int d1, d2; /* Diameters for entities 1, 2 */
443     int max_dist; /* Maximum distance 1 & 2 can be apart and still clip */
444 
445     for( ent1 = root ; ent1 != NULL ; ent1 = ent1->next ) {
446 	if( ent1->mode < FROZEN || ent1->health <= 0 )
447 	    continue; /* Skip LIMBO, DEAD & DYING entities */
448 	d1 = MAX( ent1->type_info->width, ent1->type_info->height );
449 	for( ent2 = ent1->next ; ent2 != NULL ; ent2 = ent2->next ) {
450 	    if( ent1->class == NEUTRAL && ent2->class == NEUTRAL )
451 		continue;
452 	    if( ent2->mode < FROZEN || ent1->health <= 0 )
453 		continue; /* Skip LIMBO, DEAD & DYING entities */
454 	    if( ent1->pid == ent2->id || ent2->pid == ent1->id )
455 		continue; /* Don't clip on parent */
456 	    d2 = MAX( ent2->type_info->width, ent2->type_info->height );
457 	    max_dist = (d1 + d2) / 2;
458 	    if( entity_dist(ent1, ent2) < max_dist ) {
459 		/* Call the function for the "lowest" class (in the
460 		   entity-class enumeration) with args (lowest, highest) */
461 		if( ent1->class < ent2->class ) {
462 		    first = ent1;
463 		    second = ent2;
464 		} else {
465 		    first = ent2;
466 		    second = ent1;
467 		}
468 		handle_collision[first->class]( first, second );
469 	    }
470 	}
471     }
472 
473 }
474 
475 
476 /* Creates a new entity on the end of the list of type "type" and returns
477    a pointer to the newly created entity */
entity_new(byte type,float x,float y,float x_v,float y_v)478 entity *entity_new(byte type, float x, float y, float x_v, float y_v)
479 {
480     ent_type_t *et;
481     entity *ent;
482 
483     if( (et = entity_type(type)) == NULL ) {
484 	printf("Error, no entity of type %d\n", (int)type);
485 	return NULL;
486     }
487 
488     /* Create entity */
489     if( (ent = entity_alloc()) == NULL)
490 	ERR_QUIT("Error creating new entity", EXIT_FAILURE);
491 
492     memset( ent, 0, sizeof(entity) );
493 
494     /* Add it to the list */
495     if( root == NULL )
496 	root = ent; /* The new root entity */
497 
498     if( tail != NULL )
499 	tail->next = ent;
500 
501     ent->prev = tail;
502     tail = ent;
503     tail->next = NULL;
504 
505     num_entities++;
506     id_num++;
507 
508     ent->id = id_num;
509     ent->x = x;
510     ent->y = y;
511     ent->x_v = x_v;
512     ent->y_v = y_v;
513 
514     /* Set values from default type for this particular entity */
515     ent->type_info = et;
516     ent->type = type;
517     ent->class = et->class;
518     ent->mode = ALIVE;
519     ent->speed = et->speed;
520     ent->health = et->health;
521     ent->dripping = et->dripping;
522     ent->drip1 = et->drip1;
523     ent->drip2 = et->drip2;
524     ent->cliplevel = et->cliplevel;
525     ent->color1 = COL_WHITE;
526     ent->color2 = COL_BLUE;
527     ent->last_fired = server.now;
528     ent->controller = CTRL_AI;
529     ent->visible = 1;
530     ent->lookatdir = -1;
531 
532     if( num_weapon_types > 0 ) {
533 	if( (ent->has_weapon = (byte *)malloc(num_weapon_types)) == NULL ) {
534 	    perror("Malloc");
535 	    ERR_QUIT("Error malloc'ing has_weapon array", -1);
536 	}
537 	weapon_reset(ent);
538     } else {
539 	ERR_QUIT("num_weapon_types out of range!\n", num_weapon_types);
540     }
541 
542     memset(ent->powerup_expire, 0, sizeof(ent->powerup_expire));
543 
544     if( et->ai ) {
545 	ent->cliplevel = AICLIP;
546 	ent->ai = et->ai;
547     } else { /* Default AI's for each class */
548 	switch( et->class ) {
549 	case BADDIE:
550 	    ent->cliplevel = AICLIP;
551 	case GOODIE:
552 	    ent->ai = AI_FIGHT;
553 	    break;
554 	case NEUTRAL:
555 	    ent->ai = AI_FLEE;
556 	    break;
557 	case PROJECTILE:
558 	    ent->ai = AI_NONE;
559 	    break;
560 	case ITEM:
561 	    if( et->item_action == GIVEWEAPON )
562 		ent->ai = AI_ROTATE;
563 	    break;
564 	}
565     }
566 
567     class_population[ ent->class ]++;
568 
569     /*
570       printf("created new %s (type %d, class %d) %d entities\n",
571       et->name, type, ent->class, num_entities);
572     */
573 
574     return ent;
575 
576 }
577 
578 
entity_delete(entity * ent)579 void entity_delete(entity *ent)
580 {
581     entity *seeker;
582     weap_type_t *wt;
583 
584     /* If something has current entity as target, remove it */
585     for(  seeker =  root ; seeker != NULL ; seeker = seeker->next ) {
586 	if( seeker->target == ent )
587 	    seeker->target = NULL;
588     }
589 
590     /* Does entity explode when it dies? */
591     if( ent->class == PROJECTILE ) {
592 	wt = weapon_type(ent->weapon);
593 	if( wt->explosion )
594 	    weapon_explode(ent, wt->explosion, wt->splashdamage);
595     }
596 
597     if( num_weapon_types > 0 ) {
598 	free( ent->has_weapon );
599     }
600 
601     num_entities--;
602     class_population[ ent->class ]--;
603 
604     /* Make sure that root & Tail will still point at the
605        right spot after the entity is deleted */
606     if( root == ent )
607 	root = ent->next;
608     if( tail == ent )
609 	tail = ent->prev;
610 
611     if( ent->prev )
612 	ent->prev->next = ent->next;
613 
614     if( ent->next )
615 	ent->next->prev = ent->prev;
616 
617     free(ent);
618 
619 }
620 
621 
622 /* Deletes all entities, returns the number of entities deleted */
entity_remove_all_non_clients(void)623 int entity_remove_all_non_clients(void)
624 {
625     entity *ent, *next;
626     int num = 0;
627 
628     /* printf("Removing all non client entities\n"); */
629 
630     ent = root;
631     while( ent != NULL ) {
632 	num++;
633 	next = ent->next;
634 	if( ent->cl ) /* Don't delete clients... */
635 	    ent->mode = LIMBO; /* Limbo until they send the "ready" message */
636 	else
637 	    entity_delete(ent);
638 	ent = next;
639     }
640 
641     return num;
642 
643 }
644 
645 
entity_to_netmsg(entity * ent)646 netmsg entity_to_netmsg(entity *ent)
647 {
648     netmsg msg;
649 
650     msg.entity.entity_type = ent->type;
651     msg.entity.dir = ent->dir;
652     msg.entity.mode = ent->mode;
653     msg.entity.x = ent->x + 0.5; /* Round floats to integers */
654     msg.entity.y = ent->y + 0.5;
655     msg.entity.img = ent->img;
656     msg.entity.weapon = ent->weapon;
657 
658     return msg;
659 
660 }
661 
662 
entity_killed(entity * shooter,entity * victim,point P,byte weaptype)663 void entity_killed(entity *shooter, entity *victim, point P, byte weaptype)
664 {
665     char buf[TEXTMESSAGE_STRLEN], *killerboy;
666     int change = 0;
667     weap_type_t *wt;
668 
669     victim->health = 0; /* Just to make sure */
670     victim->frame = 0;
671 
672     /* No frags for killing items or projectiles */
673     if( victim->class == PROJECTILE || victim->class == ITEM ) {
674 	return;
675     }
676 
677     if( victim == shooter ) {    /* Suicide */
678 	victim->frags--;
679 	if( victim->name ) {
680 	    snprintf(buf, TEXTMESSAGE_STRLEN, "%s couldn't cope",victim->name);
681 	    sv_net_send_text_to_all(buf);
682 	}
683 	return;
684     } else {
685 	/* Who was the victim? */
686 	switch( game.mode ) {
687 	case SAVETHEWORLD:
688 	    if( victim->class != GOODIE )
689 		change++;
690 	    break;
691 	case HOLYWAR:
692 	    if( victim->class == NEUTRAL )
693 		change--;
694 	    else {
695 		change++;
696 		/* OBITUARY IF CLIENT?? */
697 	    }
698 	    break;
699 	default:
700 	    ERR_QUIT("Game mode out of range!", game.mode );
701 	}
702     }
703 
704     if( shooter )	/* Update frag count */
705 	shooter->frags += change;
706 
707     if( victim->name ) {
708 	if( shooter ) {
709 	    if( shooter->name )
710 		killerboy = shooter->name;
711 	    else
712 		killerboy = shooter->type_info->name;
713 
714 	    if( (wt = weapon_type(weaptype)) && wt->obituary )
715 		snprintf(buf, TEXTMESSAGE_STRLEN, wt->obituary,
716 			 victim->name, killerboy);
717 	    else
718 		snprintf(buf, TEXTMESSAGE_STRLEN,
719 			 "%s got de-mapped by %s%s", victim->name,
720 			 shooter->name? "" : "a ", killerboy);
721 	    sv_net_send_text_to_all(buf);
722 	}
723     }
724 
725 }
726 
727 
entity_animate(void)728 void entity_animate(void)
729 {
730     animation_t *ani;
731     ent_type_t *et;
732     entity *ent;
733     msec_t framelen;
734     int animate, frames;
735 
736     for( ent = root ;  ent != NULL ; ent = ent->next ) {
737 	ani = entity_type_animation(ent->type_info, ent->mode);
738 	framelen = ani->framelen;
739 
740 	animate = 0; /* Don't animate by default */
741 	if( ani->images > 1 ) {
742 	    if( ani->stationary_ani ) /* Animates when stationary */
743 		animate = 1;
744 	    else if( (ent->x_v || ent->y_v) || ent->mode == ENLIGHTENED ) {
745 		animate = 1;
746 		framelen /= 2;
747 	    }
748 	}
749 
750 	if( animate ) {
751 	    if( framelen )
752 		frames = (server.now - ent->last_frame) / framelen;
753 	    else
754 		frames = 0;
755 	    if( frames > 0 ) {
756 		ent->last_frame = server.now;
757 		ent->frame += frames;
758 		if( ent->frame >= ani->frames ) {
759 		    /* End of this loop of animation. If the entity is
760 		       in it's dying animation, make it dead. */
761 		    if( ent->mode == DYING )
762 			ent->mode = DEAD;
763 		    else
764 			ent->frame = 0;
765 		}
766 	    }
767 
768 	    ent->img = ani->order[ent->frame];
769 
770 	} else {
771 
772 	    if( ent->mode == DYING ) {
773 		if( (et = entity_type(ent->type)) != NULL )
774 		    printf("Killed off %s\n", et->name);
775 		ent->mode = DEAD;
776 	    } else
777 		ent->img = 0;
778 	}
779     }
780 
781 }
782 
783 
784 
785 /* returns pixels between the CENTER of ent0 & ent1 */
entity_dist(entity * ent0,entity * ent1)786 float entity_dist(entity *ent0, entity *ent1)
787 {
788     float x_dist, y_dist;
789 
790     x_dist = (ent0->x + ent0->type_info->width/2)
791 	     - (ent1->x + ent1->type_info->width/2);
792     y_dist = (ent0->y + ent0->type_info->height/2)
793 	     - (ent1->y + ent1->type_info->height/2);
794     return sqrt( x_dist * x_dist + y_dist * y_dist );
795 
796 }
797 
798 
799 /* Find entity that has the entity-id of id */
findent(int id)800 entity *findent(int id)
801 {
802     entity *ent;
803 
804     for( ent=root ; ent != NULL ; ent = ent->next ) {
805 	if( ent->id == id )
806 	    return ent;
807     }
808 
809     return NULL;
810 
811 }
812 
813 
814 /* Called when victim's health <= 0 */
entity_die(entity * ent)815 static void entity_die(entity *ent)
816 {
817 
818     if( ent->type_info->animation[ DYING ] ) {
819 	ent->mode = DYING;
820 	ent->last_frame = server.now;
821 	ent->frame = 0;
822     } else
823 	ent->mode = DEAD;
824 
825     if( ent->type_info->explosive ) {
826 	weapon_explode(ent, 50, ent->type_info->explosive);
827     }
828 
829 
830 }
831 
832 
833 /* Returns a pointer to newly created entity, returns NULL on error */
entity_alloc(void)834 entity *entity_alloc(void)
835 {
836     entity *ent;
837 
838     if( (ent = (entity *)malloc( sizeof(entity) )) == NULL)
839 	perror("Malloc");
840 
841     return ent;
842 
843 }
844 
845 
846 
847 
848 
849 
850