1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 1991-2001 by
5  *
6  *      Bj�rn Stabell        <bjoern@xpilot.org>
7  *      Ken Ronny Schouten   <ken@xpilot.org>
8  *      Bert Gijsbers        <bert@xpilot.org>
9  *      Dick Balaska         <dick@xpilot.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25 
26 #include "xpserver.h"
27 
28 
29 bool		updateScores = true;
30 
31 int		playerArrayNumber;
32 player_t	**PlayersArray;
33 static int	GetIndArray[NUM_IDS + MAX_SPECTATORS + 1];
34 
35 /*
36  * Get index in Players array for player with id 'id'.
37  */
GetInd(int id)38 int GetInd(int id)
39 {
40     if (id == NO_ID)
41 	return NO_IND;
42 
43     /*
44      * kps - in some places where we look at the id we don't
45      * bother about spectators.
46      * This should be cleaned up in general.
47      */
48     if (id < 0 || id >= NELEM(GetIndArray)) {
49 	/*warn("GetInd: id = %d, array size = %d\n",
50 	  id, NUM_IDS + MAX_SPECTATORS + 1);*/
51 	return NO_IND;
52     }
53     return GetIndArray[id];
54 }
55 
56 /********* **********
57  * Functions on player array.
58  */
59 
Pick_startpos(player_t * pl)60 void Pick_startpos(player_t *pl)
61 {
62     int ind = GetInd(pl->id), i, num_free, pick = 0, seen = 0, order, min_order = INT_MAX;
63     static int prev_num_bases = 0;
64     static char	*free_bases = NULL;
65 
66     if (Player_is_tank(pl)) {
67 	pl->home_base = Base_by_index(0);
68 	return;
69     }
70 
71     if (prev_num_bases != Num_bases()) {
72 	prev_num_bases = Num_bases();
73 	XFREE(free_bases);
74 	free_bases = XMALLOC(char, Num_bases());
75 	if (free_bases == NULL) {
76 	    error("Can't allocate memory for free_bases");
77 	    End_game();
78 	}
79     }
80 
81     for (i = 0; i < Num_bases(); i++) {
82 	if (Base_by_index(i)->team == pl->team) {
83 	    free_bases[i] = 1;
84 	} else {
85 	    free_bases[i] = 0;	/* other team */
86 	}
87     }
88 
89     for (i = 0; i < NumPlayers; i++) {
90 	player_t *pl_i = Player_by_index(i);
91 
92 	if (pl_i->id != pl->id
93 	    && !Player_is_tank(pl_i)
94 	    && pl_i->home_base
95 	    && free_bases[pl_i->home_base->ind]) {
96 	    free_bases[pl_i->home_base->ind] = 0;	/* occupado */
97 	}
98     }
99 
100     /* find out the lowest order of all free bases */
101     for (i = 0; i < Num_bases(); i++) {
102 	if (free_bases[i] != 0) {
103 	    order = Base_by_index(i)->order;
104 	    if (order < min_order) {
105 	        min_order = order;
106 	    }
107 	}
108     }
109 
110     /* mark all bases with higher order as occupied */
111     num_free = 0;
112     for (i = 0; i < Num_bases(); i++) {
113 	if (free_bases[i] != 0) {
114 	    if (Base_by_index(i)->order <= min_order) {
115 		num_free++;
116 	    } else {
117 		free_bases[i] = 0;
118 	    }
119 	}
120     }
121 
122     /* pick a random base of all bases marked free */
123     pick = (int)(rfrac() * num_free);
124     seen = 0;
125     for (i = 0; i < Num_bases(); i++) {
126 	if (free_bases[i] != 0) {
127 	    if (seen < pick)
128 		seen++;
129 	    else
130 		break;
131 	}
132     }
133 
134     if (i == Num_bases()) {
135 	error("Can't pick startpos (ind=%d,num=%d,free=%d,pick=%d,seen=%d)",
136 	      ind, Num_bases(), num_free, pick, seen);
137 	End_game();
138     } else {
139 	pl->home_base = Base_by_index(i);
140 	if (ind < NumPlayers) {
141 	    for (i = 0; i < spectatorStart + NumSpectators; i++) {
142 		player_t *pl_i;
143 
144 		if (i == NumPlayers) {
145 		    i = spectatorStart - 1;
146 		    continue;
147 		}
148 		pl_i = Player_by_index(i);
149 		if (pl_i->conn != NULL)
150 		    Send_base(pl_i->conn, pl->id, pl->home_base->ind);
151 	    }
152 	    if (Player_is_paused(pl)
153 		|| Player_is_waiting(pl)
154 		|| Player_is_dead(pl))
155 		Go_home(pl);
156 	}
157     }
158 }
159 
Go_home(player_t * pl)160 void Go_home(player_t *pl)
161 {
162     int ind = GetInd(pl->id), i, dir, check;
163     double vx, vy, velo;
164     clpos_t pos, initpos;
165 
166     if (Player_is_tank(pl)) {
167 	/*NOTREACHED*/
168 	/* Tanks have no homebase. */
169 	warn("BUG: gohome tank");
170 	return;
171     }
172 
173     if (BIT(world->rules->mode, TIMING)
174 	&& pl->round
175 	&& !(Player_is_waiting(pl)
176 	     || Player_is_dead(pl))) {
177 	if (pl->check)
178 	    check = pl->check - 1;
179 	else
180 	    check = world->NumChecks - 1;
181 	pos = Check_by_index(check)->pos;
182 	vx = (rfrac() - 0.5) * 0.1;
183 	vy = (rfrac() - 0.5) * 0.1;
184 	velo = LENGTH(vx, vy);
185 	dir = pl->last_check_dir;
186 	dir = MOD2(dir + (int)((rfrac() - 0.5) * (RES / 8)), RES);
187     } else if (pl->home_base != NULL) {
188 	pos = pl->home_base->pos;
189 	dir = pl->home_base->dir;
190 	vx = vy = velo = 0;
191     } else {
192 	pos.cx = pos.cy = dir = 0;
193 	vx = vy = velo = 0.0;
194     }
195 
196     pl->dir = dir;
197     Player_set_float_dir(pl, (double)dir);
198     pl->wanted_float_dir = pl->float_dir;/*TURNQUEUE*/
199     initpos.cx = (click_t)(pos.cx + CLICK * vx);
200     initpos.cy = (click_t)(pos.cy + CLICK * vy);
201     Object_position_init_clpos(OBJ_PTR(pl), initpos);
202     pl->vel.x = vx;
203     pl->vel.y = vy;
204     pl->velocity = velo;
205     pl->acc.x = pl->acc.y = 0.0;
206     pl->turnacc = pl->turnvel = 0.0;
207     memset(pl->last_keyv, 0, sizeof(pl->last_keyv));
208     memset(pl->prev_keyv, 0, sizeof(pl->prev_keyv));
209     Emergency_shield(pl, false);
210     Player_used_kill(pl);
211     /*Player_init_items(pl);*/
212 
213     if (options.playerStartsShielded) {
214 	SET_BIT(pl->used, HAS_SHIELD);
215 	if (!options.allowShields) {
216 	    pl->shield_time = SHIELD_TIME;
217 	    SET_BIT(pl->have, HAS_SHIELD);
218 	}
219 	if (Player_has_deflector(pl))
220 	    Deflector(pl, true);
221     }
222     Thrust(pl, false);
223     pl->updateVisibility = true;
224     for (i = 0; i < NumPlayers; i++) {
225 	pl->visibility[i].lastChange = 0;
226 	Player_by_index(i)->visibility[ind].lastChange = 0;
227     }
228 
229     if (Player_is_robot(pl))
230 	Robot_go_home(pl);
231 }
232 
Base_set_option(base_t * base,const char * name,const char * value)233 void Base_set_option(base_t *base, const char *name, const char *value)
234 {
235     Item_t item;
236 
237     item = Item_by_option_name(name);
238     if (item != NO_ITEM) {
239 	base->initial_items[item] = atoi(value);
240 	return;
241     }
242 
243     warn("This server doesn't support option %s for bases.", name);
244 }
245 
246 /*
247  * Compute the current sensor range for player 'pl'.  This is based on the
248  * amount of fuel, the number of sensor items (each one adds 25%), and the
249  * minimum and maximum visibility limits in effect.
250  */
Compute_sensor_range(player_t * pl)251 void Compute_sensor_range(player_t *pl)
252 {
253     static int init = 0;
254     static double EnergyRangeFactor;
255 
256     if (!init) {
257 	if (options.minVisibilityDistance <= 0.0)
258 	    options.minVisibilityDistance = VISIBILITY_DISTANCE;
259 	else
260 	    options.minVisibilityDistance *= BLOCK_SZ;
261 	if (options.maxVisibilityDistance <= 0.0)
262 	    options.maxVisibilityDistance = world->hypotenuse;
263 	else
264 	    options.maxVisibilityDistance *= BLOCK_SZ;
265 
266 	if (world->items[ITEM_FUEL].initial > 0.0) {
267 	    EnergyRangeFactor = options.minVisibilityDistance /
268 		(world->items[ITEM_FUEL].initial
269 		 * (1.0 + ((double)world->items[ITEM_SENSOR].initial * 0.25)));
270 	} else
271 	    EnergyRangeFactor = ENERGY_RANGE_FACTOR;
272 	init = 1;
273     }
274 
275     pl->sensor_range = pl->fuel.sum * EnergyRangeFactor;
276     pl->sensor_range *= (1.0 + ((double)pl->item[ITEM_SENSOR] * 0.25));
277     LIMIT(pl->sensor_range,
278 	  options.minVisibilityDistance, options.maxVisibilityDistance);
279 }
280 
281 /*
282  * Give ship one more tank, if possible.
283  */
Player_add_tank(player_t * pl,double tank_fuel)284 void Player_add_tank(player_t *pl, double tank_fuel)
285 {
286     double tank_cap, add_fuel;
287 
288     if (pl->fuel.num_tanks < MAX_TANKS) {
289 	pl->fuel.num_tanks++;
290 	tank_cap = TANK_CAP(pl->fuel.num_tanks);
291 	add_fuel = tank_fuel;
292 	LIMIT(add_fuel, 0.0, tank_cap);
293 	pl->fuel.sum += add_fuel;
294 	pl->fuel.max += tank_cap;
295 	pl->fuel.tank[pl->fuel.num_tanks] = add_fuel;
296 	pl->emptymass += TANK_MASS;
297 	pl->item[ITEM_TANK] = pl->fuel.num_tanks;
298     }
299 }
300 
301 /*
302  * Remove a tank from a ship, if possible.
303  */
Player_remove_tank(player_t * pl,int which_tank)304 void Player_remove_tank(player_t *pl, int which_tank)
305 {
306     int i, tank_ind;
307     double tank_fuel, tank_cap;
308 
309     if (pl->fuel.num_tanks > 0) {
310 	tank_ind = which_tank;
311 	LIMIT(tank_ind, 1, pl->fuel.num_tanks);
312 	pl->emptymass -= TANK_MASS;
313 	tank_fuel = pl->fuel.tank[tank_ind];
314 	tank_cap = TANK_CAP(tank_ind);
315 	pl->fuel.max -= tank_cap;
316 	pl->fuel.sum -= tank_fuel;
317 	pl->fuel.num_tanks--;
318 	if (pl->fuel.current > pl->fuel.num_tanks)
319 	    pl->fuel.current = 0;
320 	else {
321 	    for (i = tank_ind; i <= pl->fuel.num_tanks; i++)
322 		pl->fuel.tank[i] = pl->fuel.tank[i + 1];
323 	}
324 	pl->item[ITEM_TANK] = pl->fuel.num_tanks;
325     }
326 }
327 
Player_hit_armor(player_t * pl)328 void Player_hit_armor(player_t *pl)
329 {
330     if (--pl->item[ITEM_ARMOR] <= 0)
331 	CLR_BIT(pl->have, HAS_ARMOR);
332 }
333 
334 /*
335  * Clear used bits.
336  */
Player_used_kill(player_t * pl)337 void Player_used_kill(player_t *pl)
338 {
339     pl->used &= ~USED_KILL;
340     if (!BIT(DEF_HAVE, HAS_SHIELD))
341 	CLR_BIT(pl->have, HAS_SHIELD);
342     pl->used |= DEF_USED;
343 }
344 
345 /*
346  * Calculate the mass of a player.
347  */
Player_set_mass(player_t * pl)348 void Player_set_mass(player_t *pl)
349 {
350     double sum_item_mass = 0.0, item_mass;
351     int item;
352 
353     for (item = 0; item < NUM_ITEMS; item++) {
354 	switch (item) {
355 	case ITEM_FUEL:
356 	case ITEM_TANK:
357 	    item_mass = 0.0;
358 	    break;
359 	case ITEM_ARMOR:
360 	    item_mass = pl->item[ITEM_ARMOR] * ARMOR_MASS;
361 	    break;
362 	default:
363 	    item_mass = pl->item[item] * options.minItemMass;
364 	    break;
365 	}
366 	sum_item_mass += item_mass;
367     }
368 
369     pl->mass = pl->emptymass
370 	+ FUEL_MASS(pl->fuel.sum)
371 	+ sum_item_mass;
372 }
373 
374 /*
375  * Give player the initial number of tanks and amount of fuel.
376  * Upto the maximum allowed.
377  */
Player_init_fuel(player_t * pl,double total_fuel)378 static void Player_init_fuel(player_t *pl, double total_fuel)
379 {
380     double fuel = total_fuel;
381     int i;
382 
383     pl->fuel.num_tanks  = 0;
384     pl->fuel.current    = 0;
385     pl->fuel.max	= TANK_CAP(0);
386     pl->fuel.sum	= MIN(fuel, pl->fuel.max);
387     pl->fuel.tank[0]	= pl->fuel.sum;
388     pl->emptymass	= options.shipMass;
389     pl->item[ITEM_TANK]	= pl->fuel.num_tanks;
390 
391     fuel -= pl->fuel.sum;
392 
393     for (i = 1; i <= world->items[ITEM_TANK].initial; i++) {
394 	Player_add_tank(pl, fuel);
395 	fuel -= pl->fuel.tank[i];
396     }
397 }
398 
399 #if 0
400 /*
401  * Set initial items for a player.
402  * Number of initial items can depend on which base the player starts from.
403  */
404 void Player_init_items(player_t *pl)
405 {
406     int i, num_tanks;
407     double total_fuel;
408     base_t *base = pl->home_base;
409 
410     for (i = 0; i < NUM_ITEMS; i++) {
411 	if (i == ITEM_FUEL || i == ITEM_TANK))
412 	    continue;
413 
414 	if (base && base->initial_items[i] >= 0)
415 	    pl->item[i] = base->initial_items[i];
416 	else
417 	    pl->item[i] = world->items[i].initial;
418     }
419 
420     if (base && base->initial_items[ITEM_TANK] >= 0)
421 	num_tanks = base->initial_items[ITEM_TANK];
422     else
423 	num_tanks = world->items[ITEM_TANK].initial;
424 
425     if (base && base->initial_items[ITEM_FUEL] >= 0)
426 	total_fuel = (double)base->initial_items[ITEM_FUEL];
427     else
428 	total_fuel = (double)world->items[ITEM_FUEL].initial;
429 
430     Player_init_fuel(pl, num_tanks, total_fuel);
431 }
432 #endif
433 
Player_init_items(player_t * pl)434 void Player_init_items(player_t *pl)
435 {
436     int i;
437 
438     /*
439      * Give player an initial set of items.
440      */
441     for (i = 0; i < NUM_ITEMS; i++) {
442 	if (i == ITEM_FUEL || i == ITEM_TANK)
443 	    pl->item[i] = 0;
444 	else
445 	    pl->item[i] = world->items[i].initial;
446     }
447 
448     Player_init_fuel(pl, (double)world->items[ITEM_FUEL].initial);
449 
450     /*
451      * Remember the amount of initial items. This way we can
452      * later figure out what items the player has picked up.
453      */
454     for (i = 0; i < NUM_ITEMS; i++)
455 	pl->initial_item[i] = pl->item[i];
456 }
457 
Init_player(int ind,shipshape_t * ship,int type)458 int Init_player(int ind, shipshape_t *ship, int type)
459 {
460     player_t *pl = Player_by_index(ind);
461     visibility_t *v = pl->visibility;
462     int i;
463 
464     memset(pl, 0, sizeof(player_t));
465     pl->visibility = v;
466 
467     /*
468      * Make sure floats, doubles and pointers are correctly zeroed.
469      */
470     assert(pl->wall_time == 0);
471     assert(pl->turnspeed == 0);
472     assert(pl->conn  == NULL);
473 
474     pl->dir = DIR_UP;
475     Player_set_float_dir(pl, (double)pl->dir);
476 
477     pl->mass = options.shipMass;
478     pl->emptymass = options.shipMass;
479 
480     Player_init_items(pl);
481 
482     if (options.allowShipShapes && ship)
483 	pl->ship = ship;
484     else {
485 	shipshape_t *tryship = Parse_shape_str(options.defaultShipShape);
486 
487 	if (tryship)
488 	    pl->ship = tryship;
489 	else
490 	    pl->ship = Default_ship();
491     }
492 
493     pl->power = pl->power_s = MAX_PLAYER_POWER;
494     pl->turnspeed = pl->turnspeed_s = MIN_PLAYER_TURNSPEED;
495     pl->type = OBJ_PLAYER;
496     pl->pl_type = type;
497     if (type == PL_TYPE_HUMAN)
498 	pl->pl_type_mychar = ' ';
499     else if (type == PL_TYPE_ROBOT)
500 	pl->pl_type_mychar = 'R';
501     else if (type == PL_TYPE_TANK)
502 	pl->pl_type_mychar = 'T';
503 
504     /*Player_init_items(pl);*/
505     Compute_sensor_range(pl);
506 
507     pl->obj_status = GRAVITY;
508     assert(pl->pl_status == 0);
509     assert(pl->pl_state == PL_STATE_UNDEFINED);
510     Player_set_state(pl, PL_STATE_ALIVE);
511     pl->have = DEF_HAVE;
512     pl->used = DEF_USED;
513 
514     if (pl->item[ITEM_CLOAK] > 0)
515 	SET_BIT(pl->have, HAS_CLOAKING_DEVICE);
516 
517     Mods_clear(&pl->mods);
518     for (i = 0; i < NUM_MODBANKS; i++)
519 	Mods_clear(&pl->modbank[i]);
520 
521     for (i = 0; i < LOCKBANK_MAX; i++)
522 	pl->lockbank[i] = NO_ID;
523 
524     {
525 	static unsigned short	pseudo_team_no = 0;
526 
527 	pl->pseudo_team = pseudo_team_no++;
528     }
529     pl->survival_time=0;
530     Player_set_life(pl, world->rules->lives);
531 
532     pl->player_fps = 50; /* Client should send a value after startup */
533     pl->maxturnsps = MAX_SERVER_FPS;
534 
535     pl->kills = 0;
536     pl->deaths = 0;
537 
538     /*
539      * If limited lives you will have to wait 'til everyone gets GAME OVER.
540      *
541      * Indeed you have to! (Mara)
542      *
543      * At least don't make the player wait for a new round if he's the
544      * only one on the server. Mara's change (always too_late) meant
545      * there was a round reset when the first player joined. -uau
546      *
547      * In individual games, make the new players appear after a small delay.
548      */
549     if (NumPlayers > 0
550 	&& !Player_is_tank(pl)) {
551 	if (BIT(world->rules->mode, LIMITED_LIVES))
552 	    Player_set_state(pl, PL_STATE_WAITING);
553 	else
554 	    Player_set_state(pl, PL_STATE_APPEARING);
555     }
556 
557     pl->team		= TEAM_NOT_SET;
558 
559     pl->alliance	= ALLIANCE_NOT_SET;
560     pl->invite		= NO_ID;
561 
562     pl->lock.tagged	= LOCK_NONE;
563     pl->lock.pl_id	= 0; /* kps - ??? */
564 
565     pl->id		= peek_ID();
566     GetIndArray[pl->id]	= ind;
567     if (!Is_player_id(pl->id))
568 	warn("Init_player: Not a player id: %d", pl->id);
569 
570     for (i = 0; i < MAX_RECORDED_SHOVES; i++)
571 	pl->shove_record[i].pusher_id = NO_ID;
572 
573     pl->update_score = true;
574 
575     return pl->id;
576 }
577 
578 
579 static player_t *playerArray;
580 static visibility_t *visibilityArray;
581 
Alloc_players(int number)582 void Alloc_players(int number)
583 {
584     player_t *p;
585     visibility_t *t;
586     size_t n = number;
587     int i;
588 
589     /* Allocate space for pointers */
590     PlayersArray = XCALLOC(player_t *, n);
591 
592     /* Allocate space for all entries, all player structs */
593     p = playerArray = XCALLOC(player_t, n);
594 
595     /* Allocate space for all visibility arrays, n arrays of n entries */
596     t = visibilityArray = XCALLOC(visibility_t, n * n);
597 
598     if (!PlayersArray || !playerArray || !visibilityArray) {
599 	error("Not enough memory for Players.");
600 	exit(1);
601     }
602 
603     for (i = 0; i < number; i++) {
604 	PlayersArray[i] = p++;
605 	PlayersArray[i]->visibility = t;
606 	/* Advance to next block/array */
607 	t += number;
608     }
609 
610     playerArrayNumber = number;
611 
612     /* Initialize player id to index lookup table */
613     for (i = 0; i < NELEM(GetIndArray); i++)
614 	GetIndArray[i] = NO_IND;
615 }
616 
617 
618 
Free_players(void)619 void Free_players(void)
620 {
621     XFREE(PlayersArray);
622     XFREE(playerArray);
623     XFREE(visibilityArray);
624 }
625 
626 
627 
Update_score_table(void)628 void Update_score_table(void)
629 {
630     int i, j, check;
631     player_t *pl;
632 
633     for (j = 0; j < NumPlayers; j++) {
634 	pl = Player_by_index(j);
635 	if (pl->update_score) {
636 	    pl->update_score = false;
637 	    for (i = 0; i < NumPlayers; i++) {
638 		player_t *pl_i = Player_by_index(i);
639 
640 		if (pl_i->conn != NULL)
641 		    Send_score(pl_i->conn, pl->id,  Get_Score(pl), pl->pl_life,
642 			       pl->mychar, pl->alliance);
643 	    }
644 	    for (i = 0; i < NumSpectators; i++)
645 		Send_score(Player_by_index(i + spectatorStart)->conn, pl->id,
646 			    Get_Score(pl), pl->pl_life, pl->mychar, pl->alliance);
647 	}
648 	if (BIT(world->rules->mode, TIMING)) {
649 	    if (pl->check != pl->prev_check
650 		|| pl->round != pl->prev_round) {
651 		pl->prev_check = pl->check;
652 		pl->prev_round = pl->round;
653 		check = (pl->round == 0)
654 			    ? 0
655 			    : (pl->check == 0)
656 				? (world->NumChecks - 1)
657 				: (pl->check - 1);
658 		for (i = 0; i < NumPlayers; i++) {
659 		    player_t *pl_i = Player_by_index(i);
660 
661 		    if (pl_i->conn != NULL)
662 			Send_timing(pl_i->conn, pl->id, check, pl->round);
663 		}
664 	    }
665 	}
666     }
667     updateScores = false;
668 }
669 
670 
Reset_all_players(void)671 void Reset_all_players(void)
672 {
673     player_t *pl;
674     int i, j;
675 
676     updateScores = true;
677 
678     for (i = 0; i < NumPlayers; i++) {
679 	pl = Player_by_index(i);
680 
681 	if (options.endOfRoundReset) {
682 	    if (Player_is_paused(pl))
683 		Player_death_reset(pl, false);
684 	    else {
685 		Kill_player(pl, false);
686 		if (pl != Player_by_index(i)) {
687 		    i--;
688 		    continue;
689 		}
690 	    }
691 	}
692 
693 	pl->kills = 0;
694 	pl->deaths = 0;
695 
696 	if (!Player_is_paused(pl)
697 	    && !Player_is_waiting(pl))
698 	    Rank_add_round(pl);
699 
700 	CLR_BIT(pl->have, HAS_BALL);
701 	Player_reset_timing(pl);
702 
703 	if (!Player_is_paused(pl)) {
704 	    Player_set_state(pl, PL_STATE_APPEARING);
705 	    Player_set_life(pl, world->rules->lives);
706 	}
707     }
708 
709     if (BIT(world->rules->mode, TEAM_PLAY)) {
710 	/* Detach any balls and kill ball */
711 	/* We are starting all over again */
712 	for (j = NumObjs - 1; j >= 0 ; j--) {
713 	    if (Obj[j]->type == OBJ_BALL) {
714 		ballobject_t *ball = BALL_IND(j);
715 
716 		ball->id = NO_ID;
717 		ball->life = 0;
718 		/*
719 		 * why not -1 ???
720 		 * naive question, obviously yet another dirty hack
721 		 */
722 		ball->ball_owner = 0;
723 		CLR_BIT(ball->obj_status, RECREATE);
724 		Delete_shot(j);
725 	    }
726 	}
727 
728 	/* Reset the treasures */
729 	for (i = 0; i < Num_treasures(); i++) {
730 	    treasure_t *treasure = Treasure_by_index(i);
731 
732 	    treasure->destroyed = 0;
733 	    treasure->have = false;
734 	    Make_treasure_ball(treasure);
735 	}
736 
737 	/* Reset the teams */
738 	for (i = 0; i < MAX_TEAMS; i++) {
739 	    team_t *teamp = Team_by_index(i);
740 
741 	    teamp->TreasuresDestroyed = 0;
742 	    teamp->TreasuresLeft
743 		= teamp->NumTreasures - teamp->NumEmptyTreasures;
744 	}
745 
746 	if (options.endOfRoundReset) {
747 	    /* Reset the targets */
748 	    for (i = 0; i < Num_targets(); i++) {
749 		target_t *targ = Target_by_index(i);
750 
751 		if (targ->damage != TARGET_DAMAGE || targ->dead_ticks > 0)
752 		    World_restore_target(targ);
753 	    }
754 	}
755     }
756 
757     if (options.endOfRoundReset) {
758 	for (i = 0; i < NumObjs; i++) {
759 	    object_t *obj = Obj[i];
760 
761 	    if (BIT(OBJ_TYPEBIT(obj->type),
762 		    OBJ_SHOT_BIT|OBJ_MINE_BIT|OBJ_DEBRIS_BIT|OBJ_SPARK_BIT
763 		    |OBJ_CANNON_SHOT_BIT|OBJ_TORPEDO_BIT|OBJ_SMART_SHOT_BIT
764 		    |OBJ_HEAT_SHOT_BIT|OBJ_PULSE_BIT|OBJ_ITEM_BIT)) {
765 		obj->life = 0;
766 		if (BIT(OBJ_TYPEBIT(obj->type),
767 			OBJ_TORPEDO_BIT|OBJ_SMART_SHOT_BIT|OBJ_HEAT_SHOT_BIT
768 			|OBJ_CANNON_SHOT_BIT|OBJ_MINE_BIT))
769 		    /* Take care that no new explosions are made. */
770 		    obj->mass = 0;
771 	    }
772 	}
773     }
774 
775     roundtime = options.maxRoundTime * FPS;
776 
777     Update_score_table();
778 }
779 
780 
Check_team_members(int team)781 void Check_team_members(int team)
782 {
783     player_t *pl;
784     team_t *teamp;
785     int members, i;
786 
787     if (!BIT(world->rules->mode, TEAM_PLAY))
788 	return;
789 
790     for (members = i = 0; i < NumPlayers; i++) {
791 	pl = Player_by_index(i);
792 	if (!Player_is_tank(pl)
793 	    && pl->team == team
794 	    && pl->home_base != NULL)
795 	    members++;
796     }
797     teamp = Team_by_index(team);
798     if (teamp->NumMembers != members) {
799 	warn("Server has reset team %d members from %d to %d",
800 	     team, teamp->NumMembers, members);
801 	for (i = 0; i < NumPlayers; i++) {
802 	    pl = Player_by_index(i);
803 	    if (!Player_is_tank(pl)
804 		&& pl->team == team
805 		&& pl->home_base != NULL)
806 		warn("Team %d currently has player %d: \"%s\"",
807 		     team, i+1, pl->name);
808 	}
809 	teamp->NumMembers = members;
810     }
811 }
812 
813 
Compute_end_of_round_values(double * average_score,int * num_best_players,double * best_ratio,int best_players[])814 static void Compute_end_of_round_values(double *average_score,
815 					int *num_best_players,
816 					double *best_ratio,
817 					int best_players[])
818 {
819     int i, n = 0;
820     double ratio;
821 
822     /* Initialize everything */
823     *average_score = 0;
824     *num_best_players = 0;
825     *best_ratio = -1.0;
826 
827     /* Figure out what the average score is and who has the best kill/death */
828     /* ratio for this round */
829     for (i = 0; i < NumPlayers; i++) {
830 	player_t *pl = Player_by_index(i);
831 
832 	if (Player_is_tank(pl)
833 	    || (Player_is_paused(pl) && pl->pause_count <= 0)
834 	    || Player_is_waiting(pl))
835 	    continue;
836 
837 	n++;
838 	*average_score +=  Get_Score(pl);
839 	ratio = (double) pl->kills / (pl->deaths + 1);
840 	if (ratio > *best_ratio) {
841 	    *best_ratio = ratio;
842 	    best_players[0] = i;
843 	    *num_best_players = 1;
844 	} else if (ratio == *best_ratio)
845 	    best_players[(*num_best_players)++] = i;
846     }
847     if (n != 0)  /* Can this be 0? */
848 	*average_score /= n;
849 }
850 
851 
Give_best_player_bonus(double average_score,int num_best_players,double best_ratio,int best_players[])852 static void Give_best_player_bonus(double average_score,
853 				   int num_best_players,
854 				   double best_ratio,
855 				   int best_players[])
856 {
857     int i;
858     double points;
859     char msg[MSG_LEN];
860 
861     if (num_best_players == 0 || best_ratio == 0)
862 	sprintf(msg, "There is no Deadly Player.");
863     else if (num_best_players == 1) {
864 	player_t *bp = Player_by_index(best_players[0]);
865 
866 	sprintf(msg,
867 		"%s is the Deadliest Player with a kill ratio of %d/%d.",
868 		bp->name,
869 		bp->kills, bp->deaths);
870 	points = best_ratio * Rate(Get_Score(bp), average_score);
871 	if (!options.zeroSumScoring) Score(bp, points, bp->pos, "[Deadliest]");
872     	Rank_add_deadliest(bp);
873 	/*if (options.zeroSumScoring);*//* TODO */
874     } else {
875 	msg[0] = '\0';
876 	for (i = 0; i < num_best_players; i++) {
877 	    player_t	*bp = Player_by_index(best_players[i]);
878 	    double	ratio = Rate(Get_Score(bp), average_score);
879 	    double	score = (ratio + num_best_players) / num_best_players;
880 
881 	    if (msg[0]) {
882 		if (i == num_best_players - 1)
883 		    strcat(msg, " and ");
884 		else
885 		    strcat(msg, ", ");
886 	    }
887 	    if (strlen(msg) + 8 + strlen(bp->name) >= sizeof(msg)) {
888 		Set_message(msg);
889 		msg[0] = '\0';
890 	    }
891 	    strcat(msg, bp->name);
892 	    points = best_ratio * score;
893 	    if (!options.zeroSumScoring) Score(bp, points, bp->pos, "[Deadly]");
894 	    Rank_add_deadliest(bp);
895 	    /*if (options.zeroSumScoring);*//* TODO */
896 	}
897 	if (strlen(msg) + 64 >= sizeof(msg)) {
898 	    Set_message(msg);
899 	    msg[0] = '\0';
900 	}
901 	sprintf(msg + strlen(msg),
902 		" are the Deadly Players with kill ratios of %d/%d.",
903 		Player_by_index(best_players[0])->kills,
904 		Player_by_index(best_players[0])->deaths);
905     }
906     Set_message(msg);
907 }
908 
Give_individual_bonus(player_t * pl,double average_score)909 static void Give_individual_bonus(player_t *pl, double average_score)
910 {
911     double ratio, points;
912 
913     ratio = (double) pl->kills / (pl->deaths + 1);
914     points = ratio * Rate( Get_Score(pl), average_score);
915     if (!options.zeroSumScoring) Score(pl, points, pl->pos, "[Winner]");
916     /*if (options.zeroSumScoring);*//* TODO */
917 }
918 
Count_rounds(void)919 void Count_rounds(void)
920 {
921     if (!options.roundsToPlay)
922 	return;
923 
924     ++roundsPlayed;
925 
926     Set_message_f(" < Round %d out of %d completed. >",
927 		  roundsPlayed, options.roundsToPlay);
928     /* only do the game over once */
929     if (roundsPlayed == options.roundsToPlay)
930 	Game_Over();
931 }
932 
933 
Team_game_over(int winning_team,const char * reason)934 void Team_game_over(int winning_team, const char *reason)
935 {
936     int i, j, num_best_players, *best_players;
937     double average_score, best_ratio;
938 
939     if (!(best_players = XMALLOC(int, NumPlayers))) {
940 	warn("no mem");
941 	End_game();
942     }
943 
944     /* Figure out the average score and who has the best kill/death ratio */
945     /* ratio for this round */
946     Compute_end_of_round_values(&average_score,
947 				&num_best_players,
948 				&best_ratio,
949 				best_players);
950 
951     /* Print out the results of the round */
952     if (winning_team != -1) {
953 	Set_message_f(" < Team %d has won the round%s! >",
954 		      winning_team, reason);
955 	sound_play_all(TEAM_WIN_SOUND);
956     } else {
957 	Set_message_f(" < We have a draw%s! >", reason);
958 	sound_play_all(TEAM_DRAW_SOUND);
959     }
960 
961     /* Give bonus to the best player */
962     Give_best_player_bonus(average_score,
963 			   num_best_players,
964 			   best_ratio,
965 			   best_players);
966 
967     /* Give bonuses to the winning team */
968     if (winning_team != -1) {
969 	for (i = 0; i < NumPlayers; i++) {
970 	    player_t *pl_i = Player_by_index(i);
971 
972 	    if (pl_i->team != winning_team)
973 		continue;
974 
975 	    if (Player_is_tank(pl_i)
976 		|| (Player_is_paused(pl_i) && pl_i->pause_count <= 0)
977 		|| Player_is_waiting(pl_i))
978 		continue;
979 
980 	    for (j = 0; j < num_best_players; j++) {
981 		if (i == best_players[j])
982 		    break;
983 	    }
984 	    if (j == num_best_players)
985 		Give_individual_bonus(pl_i, average_score);
986 	}
987     }
988 
989     teamcup_round_end(winning_team);
990 
991     Reset_all_players();
992 
993     Count_rounds();
994 
995     free(best_players);
996 
997     teamcup_round_start();
998 
999     /* Ranking */
1000     Rank_write_webpage();
1001     Rank_write_rankfile();
1002     Rank_show_ranks();
1003 }
1004 
Individual_game_over(int winner)1005 void Individual_game_over(int winner)
1006 {
1007     int i, j, num_best_players, *best_players;
1008     double average_score, best_ratio;
1009 
1010     if (!(best_players = XMALLOC(int, NumPlayers))) {
1011 	warn("no mem");
1012 	End_game();
1013     }
1014 
1015     /* Figure out what the average score is and who has the best kill/death */
1016     /* ratio for this round */
1017     Compute_end_of_round_values(&average_score, &num_best_players,
1018 				&best_ratio, best_players);
1019 
1020     /* Print out the results of the round */
1021     if (winner == -1) {
1022 	Set_message(" < We have a draw! >");
1023 	sound_play_all(PLAYER_DRAW_SOUND);
1024     }
1025     else if (winner == -2) {
1026 	Set_message(" < The robots have won the round! >");
1027 	/* Perhaps this should be a different sound? */
1028 	sound_play_all(PLAYER_WIN_SOUND);
1029     } else {
1030 	Set_message_f(" < %s has won the round! >",
1031 		      Player_by_index(winner)->name);
1032 	sound_play_all(PLAYER_WIN_SOUND);
1033     }
1034 
1035     /* Give bonus to the best player */
1036     Give_best_player_bonus(average_score,
1037 			   num_best_players,
1038 			   best_ratio,
1039 			   best_players);
1040 
1041     /* Give bonus to the winning player */
1042     if (winner >= 0) {
1043 	for (i = 0; i < num_best_players; i++) {
1044 	    if (winner == best_players[i])
1045 		break;
1046 	}
1047 	if (i == num_best_players)
1048 	    Give_individual_bonus(Player_by_index(winner), average_score);
1049     }
1050     else if (winner == -2) {
1051 	for (j = 0; j < NumPlayers; j++) {
1052 	    player_t *pl_j = Player_by_index(j);
1053 
1054 	    if (Player_is_robot(pl_j)) {
1055 		for (i = 0; i < num_best_players; i++) {
1056 		    if (j == best_players[i])
1057 			break;
1058 		}
1059 		if (i == num_best_players)
1060 		    Give_individual_bonus(pl_j, average_score);
1061 	    }
1062 	}
1063     }
1064 
1065     Reset_all_players();
1066 
1067     Count_rounds();
1068 
1069     free(best_players);
1070 }
1071 
Compute_game_status(void)1072 void Compute_game_status(void)
1073 {
1074     int i;
1075     char msg[MSG_LEN];
1076 
1077     if (roundtime > 0)
1078 	roundtime--;
1079 
1080     if (BIT(world->rules->mode, TIMING))
1081 	Race_compute_game_status();
1082     else if (BIT(world->rules->mode, TEAM_PLAY)) {
1083 
1084 	/* Do we have a winning team ? */
1085 
1086 	enum TeamState {
1087 	    TeamEmpty,
1088 	    TeamDead,
1089 	    TeamAlive
1090 	} team_state[MAX_TEAMS];
1091 	int num_dead_teams = 0;
1092 	int num_alive_teams = 0;
1093 	int winning_team = -1;
1094 
1095 	for (i = 0; i < MAX_TEAMS; i++)
1096 	    team_state[i] = TeamEmpty;
1097 
1098 	for (i = 0; i < NumPlayers; i++) {
1099 	    player_t *pl_i = Player_by_index(i);
1100 
1101 	    if (Player_is_tank(pl_i))
1102 		/* Ignore tanks. */
1103 		continue;
1104 	    else if (Player_is_paused(pl_i))
1105 		/* Ignore paused players. */
1106 		continue;
1107 #if 0
1108 	    /* not all teammode maps have treasures. */
1109 	    else if (world->teams[pl_i->team].NumTreasures == 0)
1110 		/* Ignore players with no treasure troves */
1111 		continue;
1112 #endif
1113 	    else if (Player_is_waiting(pl_i)
1114 		     || Player_is_dead(pl_i)) {
1115 		if (team_state[pl_i->team] == TeamEmpty) {
1116 		    /* Assume all teammembers are dead. */
1117 		    num_dead_teams++;
1118 		    team_state[pl_i->team] = TeamDead;
1119 		}
1120 	    }
1121 	    /*
1122 	     * If the player is not paused and he is not in the
1123 	     * game over mode and his team owns treasures then he is
1124 	     * considered alive.
1125 	     * But he may not be playing though if the rest of the team
1126 	     * was genocided very quickly after game reset, while this
1127 	     * player was still being transported back to his homebase.
1128 	     */
1129 	    else if (team_state[pl_i->team] != TeamAlive) {
1130 		if (team_state[pl_i->team] == TeamDead)
1131 		    /* Oops!  Not all teammembers are dead yet. */
1132 		    num_dead_teams--;
1133 		team_state[pl_i->team] = TeamAlive;
1134 		++num_alive_teams;
1135 		/* Remember a team which was alive. */
1136 		winning_team = pl_i->team;
1137 	    }
1138 	}
1139 
1140 	if (num_alive_teams > 1) {
1141 	    char *bp;
1142 	    int teams_with_treasure = 0, team_win[MAX_TEAMS];
1143 	    double team_score[MAX_TEAMS], max_score = 0;
1144 	    int winners, max_destroyed = 0, max_left = 0;
1145 	    team_t *team_ptr, *specialballteam_ptr;
1146 	    bool no_special_balls_present = false;
1147 	    /*
1148 	     * Game is not over if more than one team which have treasures
1149 	     * still have one remaining in play.  Note that it is possible
1150 	     * for max_destroyed to be zero, in the case where a team
1151 	     * destroys some treasures and then all quit, and the remaining
1152 	     * teams did not destroy any.
1153 	     */
1154 	    for (i = 0; i < MAX_TEAMS; i++) {
1155 		team_score[i] = 0;
1156 		if ((team_state[i] != TeamAlive) && (i != options.specialBallTeam)) {
1157 		    team_win[i] = 0;
1158 		    continue;
1159 		}
1160 
1161 		team_win[i] = 1;
1162 		team_ptr = &(world->teams[i]);
1163 		specialballteam_ptr = Team_by_index(options.specialBallTeam);
1164 
1165 		if (options.specialBallTeam < 0 || options.specialBallTeam >=MAX_TEAMS ||
1166 		    specialballteam_ptr->NumTreasures == 0)
1167 		  no_special_balls_present = true;
1168 
1169 		if (team_ptr->TreasuresDestroyed > max_destroyed)
1170 		  max_destroyed = team_ptr->TreasuresDestroyed;
1171 		if ((team_ptr->TreasuresLeft > 0) ||
1172 		    ((team_ptr->NumTreasures == team_ptr->NumEmptyTreasures) &&
1173 		     no_special_balls_present))
1174 		  teams_with_treasure++;
1175 	    }
1176 
1177 	    /*
1178 	     * Game is not over if more than one team has treasure.
1179 	     */
1180 	    if ((teams_with_treasure > 1 || !max_destroyed)
1181 		&& (roundtime != 0 || options.maxRoundTime <= 0))
1182 		return;
1183 
1184 	    if (options.maxRoundTime > 0 && roundtime == 0)
1185 		Set_message("Timer expired. Round ends now.");
1186 
1187 	    /*
1188 	     * Find the winning team;
1189 	     *	Team destroying most number of treasures;
1190 	     *	If drawn; the one with most saved treasures,
1191 	     *	If drawn; the team with the most points,
1192 	     *	If drawn; an overall draw.
1193 	     */
1194 	    for (winners = i = 0; i < MAX_TEAMS; i++) {
1195 		if (!team_win[i])
1196 		    continue;
1197 		if (world->teams[i].TreasuresDestroyed == max_destroyed) {
1198 		    if (world->teams[i].TreasuresLeft > max_left)
1199 			max_left = world->teams[i].TreasuresLeft;
1200 		    winning_team = i;
1201 		    winners++;
1202 		} else
1203 		    team_win[i] = 0;
1204 	    }
1205 	    if (winners == 1) {
1206 		sprintf(msg, " by destroying %d treasures", max_destroyed);
1207 		Team_game_over(winning_team, msg);
1208 		return;
1209 	    }
1210 
1211 	    for (i = 0; i < NumPlayers; i++) {
1212 		player_t *pl_i = Player_by_index(i);
1213 
1214 		if (Player_is_paused(pl_i) || Player_is_tank(pl_i))
1215 		    continue;
1216 		team_score[pl_i->team] += Get_Score(pl_i);
1217 	    }
1218 
1219 	    for (winners = i = 0; i < MAX_TEAMS; i++) {
1220 		if (!team_win[i])
1221 		    continue;
1222 		if (world->teams[i].TreasuresLeft == max_left) {
1223 		    if (team_score[i] > max_score)
1224 			max_score = team_score[i];
1225 		    winning_team = i;
1226 		    winners++;
1227 		} else
1228 		    team_win[i] = 0;
1229 	    }
1230 	    if (winners == 1) {
1231 		sprintf(msg,
1232 			" by destroying %d treasures"
1233 			" and successfully defending %d",
1234 			max_destroyed, max_left);
1235 		Team_game_over(winning_team, msg);
1236 		return;
1237 	    }
1238 
1239 	    for (winners = i = 0; i < MAX_TEAMS; i++) {
1240 		if (!team_win[i])
1241 		    continue;
1242 		if (team_score[i] == max_score) {
1243 		    winning_team = i;
1244 		    winners++;
1245 		} else
1246 		    team_win[i] = 0;
1247 	    }
1248 	    if (winners == 1) {
1249 		sprintf(msg, " by destroying %d treasures, saving %d, and "
1250 			"scoring %.2f points",
1251 			max_destroyed, max_left, max_score);
1252 		Team_game_over(winning_team, msg);
1253 		return;
1254 	    }
1255 
1256 	    /* Highly unlikely */
1257 
1258 	    sprintf(msg, " between teams ");
1259 	    bp = msg + strlen(msg);
1260 	    for (i = 0; i < MAX_TEAMS; i++) {
1261 		if (!team_win[i])
1262 		    continue;
1263 		*bp++ = "0123456789"[i]; *bp++ = ','; *bp++ = ' ';
1264 	    }
1265 	    bp -= 2;
1266 	    *bp = '\0';
1267 	    Team_game_over(-1, msg);
1268 
1269 	}
1270 	else if (num_dead_teams > 0) {
1271 	    if (num_alive_teams == 1)
1272 		Team_game_over(winning_team, " by staying alive");
1273 	    else
1274 		Team_game_over(-1, " as everyone died");
1275 	}
1276 	else {
1277 	    /*
1278 	     * num_alive_teams <= 1 && num_dead_teams == 0
1279 	     *
1280 	     * There is a possibility that the game has ended because players
1281 	     * quit, the game over state is needed to reset treasures.  We
1282 	     * must count how many treasures are missing, if there are any
1283 	     * the playing team (if any) wins.
1284 	     */
1285 	    int	j, treasures_destroyed;
1286 
1287 	    for (treasures_destroyed = j = 0; j < MAX_TEAMS; j++)
1288 		treasures_destroyed += (world->teams[j].NumTreasures
1289 					- world->teams[j].NumEmptyTreasures
1290 					- world->teams[j].TreasuresLeft);
1291 	    if (treasures_destroyed)
1292 		Team_game_over(winning_team, " by staying in the game");
1293 	}
1294 
1295     } else {
1296 
1297 	/* Do we have a winner ? (No team play) */
1298 	int num_alive_players = 0;
1299 	int num_active_players = 0;
1300 	int num_alive_robots = 0;
1301 	int num_active_humans = 0;
1302 	int winner = -1;
1303 
1304 	for (i = 0; i < NumPlayers; i++)  {
1305 	    player_t *pl_i = Player_by_index(i);
1306 
1307 	    if (Player_is_paused(pl_i) || Player_is_tank(pl_i))
1308 		continue;
1309 	    if (!(Player_is_waiting(pl_i)
1310 		  || Player_is_dead(pl_i))) {
1311 		num_alive_players++;
1312 		if (Player_is_robot(pl_i))
1313 		    num_alive_robots++;
1314 		winner = i; 	/* Tag player that's alive */
1315 	    }
1316 	    else if (Player_is_human(pl_i))
1317 		num_active_humans++;
1318 	    num_active_players++;
1319 	}
1320 
1321 	if (num_alive_players == 1 && num_active_players > 1)
1322 	    Individual_game_over(winner);
1323 	else if (num_alive_players == 0 && num_active_players >= 1)
1324 	    Individual_game_over(-1);
1325 	else if (num_alive_robots > 1
1326 		    && num_alive_players == num_alive_robots
1327 		    && num_active_humans > 0)
1328 	    Individual_game_over(-2);
1329 	else if (options.maxRoundTime > 0 && roundtime == 0) {
1330 	    Set_message("Timer expired. Round ends now.");
1331 	    Individual_game_over(-1);
1332 	}
1333     }
1334 }
1335 
Delete_player(player_t * pl)1336 void Delete_player(player_t *pl)
1337 {
1338     int ind = GetInd(pl->id), i, j, id = pl->id;
1339     object_t *obj;
1340     team_t *teamp = Team_by_index(pl->team);
1341 
1342     /* call before important player structures are destroyed */
1343     Leave_alliance(pl);
1344 
1345     if (options.tagGame && tagItPlayerId == pl->id)
1346 	tagItPlayerId = NO_ID;
1347 
1348     if (Player_is_robot(pl))
1349 	Robot_destroy(pl);
1350 
1351     if (pl->isoperator) {
1352 	if (!--NumOperators && game_lock) {
1353 	    game_lock = false;
1354 	    Set_message(" < The game has been unlocked as "
1355 			"the last operator left! >");
1356 	}
1357     }
1358 
1359     /* Won't be swapping anywhere */
1360     for (i = MAX_TEAMS - 1; i >= 0; i--)
1361 	if (world->teams[i].SwapperId == id)
1362 	    world->teams[i].SwapperId = -1;
1363 
1364     /* Delete remaining shots */
1365     for (i = NumObjs - 1; i >= 0; i--) {
1366 	obj = Obj[i];
1367 	if (obj->id == id) {
1368 	    if (obj->type == OBJ_BALL) {
1369 		Delete_shot(i);
1370 		BALL_PTR(obj)->ball_owner = NO_ID;
1371 	    }
1372 	    else if (obj->type == OBJ_DEBRIS
1373 		     || obj->type == OBJ_SPARK)
1374 		/* Okay, so you want robot explosions to exist,
1375 		 * even if the robot left the game. */
1376 		obj->id = NO_ID;
1377 	    else {
1378 		if (!options.keepShots) {
1379 		    obj->life = 0;
1380 		    if (BIT(OBJ_TYPEBIT(obj->type),
1381 			    OBJ_CANNON_SHOT_BIT|OBJ_MINE_BIT|OBJ_SMART_SHOT_BIT
1382 			    |OBJ_HEAT_SHOT_BIT|OBJ_TORPEDO_BIT))
1383 			obj->mass = 0;
1384 		}
1385 	        obj->id = NO_ID;
1386 		if (obj->type == OBJ_MINE)
1387 		    MINE_PTR(obj)->mine_owner = NO_ID;
1388 	    }
1389 	}
1390 	else {
1391 	    if (obj->type == OBJ_MINE) {
1392 		mineobject_t *mine = MINE_PTR(obj);
1393 
1394 		if (mine->mine_owner == id) {
1395 		    mine->mine_owner = NO_ID;
1396 		    if (!options.keepShots) {
1397 			obj->life = 0;
1398 			obj->mass = 0;
1399 		    }
1400 		}
1401 	    }
1402 	    else if (obj->type == OBJ_BALL) {
1403 		ballobject_t *ball = BALL_PTR(obj);
1404 
1405 		if (ball->ball_owner == id)
1406 		    ball->ball_owner = NO_ID;
1407 	    }
1408 	}
1409     }
1410 
1411     Free_ship_shape(pl->ship);
1412 
1413     sound_close(pl);
1414 
1415     NumPlayers--;
1416     if (Player_is_tank(pl))
1417 	NumPseudoPlayers--;
1418 
1419     if (pl->rank) {
1420 	Rank_save_score(pl);
1421    	if (NumPlayers == NumRobots + NumPseudoPlayers) {
1422 	    Rank_write_webpage();
1423 	    Rank_write_rankfile();
1424    	}
1425     }
1426 
1427     if (teamp && !Player_is_tank(pl) && pl->home_base) {
1428 	teamp->NumMembers--;
1429 	if (Player_is_robot(pl))
1430 	    teamp->NumRobots--;
1431     }
1432 
1433     if (Player_is_robot(pl))
1434 	NumRobots--;
1435 
1436     /*
1437      * Swap entry no 'ind' with the last one.
1438      *
1439      * Change the PlayersArray[] pointer array to have
1440      * Player_by_index(ind) point to a valid player and move our leaving
1441      * player to PlayersArray[NumPlayers].
1442      */
1443     /* Swap pointers... */
1444     pl				= Player_by_index(NumPlayers);
1445     PlayersArray[NumPlayers]	= Player_by_index(ind);
1446     PlayersArray[ind]		= pl;
1447     /* Restore pointer. */
1448     pl				= Player_by_index(NumPlayers);
1449 
1450     GetIndArray[Player_by_index(ind)->id] = ind;
1451     GetIndArray[Player_by_index(NumPlayers)->id] = NumPlayers;
1452 
1453     Check_team_members(pl->team);
1454 
1455     for (i = NumPlayers - 1; i >= 0; i--) {
1456 	player_t *pl_i = Player_by_index(i);
1457 
1458 	if (Player_is_tank(pl_i)
1459 	    && pl_i->lock.pl_id == id) {
1460 	    /* remove tanks which were released by this player. */
1461 	    if (options.keepShots)
1462 		pl_i->lock.pl_id = NO_ID;
1463 	    else
1464 		Delete_player(pl_i);
1465 	    continue;
1466 	}
1467 	if (BIT(pl_i->lock.tagged, LOCK_PLAYER|LOCK_VISIBLE)
1468 	    && (pl_i->lock.pl_id == id || NumPlayers <= 1)) {
1469 	    CLR_BIT(pl_i->lock.tagged, LOCK_PLAYER|LOCK_VISIBLE);
1470 	    CLR_BIT(pl_i->used, USES_TRACTOR_BEAM);
1471 	}
1472 	if (Player_is_robot(pl_i)
1473 	    && Robot_war_on_player(pl_i) == id)
1474 	    Robot_reset_war(pl_i);
1475 
1476 	for (j = 0; j < LOCKBANK_MAX; j++) {
1477 	    if (pl_i->lockbank[j] == id)
1478 		pl_i->lockbank[j] = NO_ID;
1479 	}
1480 	for (j = 0; j < MAX_RECORDED_SHOVES; j++) {
1481 	    if (pl_i->shove_record[j].pusher_id == id)
1482 		pl_i->shove_record[j].pusher_id = NO_ID;
1483 	}
1484     }
1485 
1486     for (i = NumPlayers - 1; i >= 0; i--) {
1487 	player_t *pl_i = Player_by_index(i);
1488 
1489 	if (pl_i->conn != NULL)
1490 	    Send_leave(pl_i->conn, id);
1491 	else if (Player_is_tank(pl_i)) {
1492 	    if (pl_i->lock.pl_id == id)
1493 		Delete_player(pl_i);
1494 	}
1495     }
1496 
1497     for (i = NumSpectators - 1; i >= 0; i--)
1498 	Send_leave(Player_by_index(i + spectatorStart)->conn, id);
1499 
1500     GetIndArray[id] = NO_IND;
1501     release_ID(id);
1502 }
1503 
Add_spectator(player_t * pl)1504 void Add_spectator(player_t *pl)
1505 {
1506     pl->home_base = NULL;
1507     pl->team = 0;
1508     GetIndArray[pl->id] = spectatorStart + NumSpectators;
1509     Player_set_score(pl,-6666);
1510     Player_set_mychar(pl,'S');
1511     NumSpectators++;
1512 }
1513 
Delete_spectator(player_t * pl)1514 void Delete_spectator(player_t *pl)
1515 {
1516     int i, ind = GetInd(pl->id);
1517 
1518     NumSpectators--;
1519     /* Swap leaver last */
1520     pl = Player_by_index(spectatorStart + NumSpectators);
1521     PlayersArray[spectatorStart + NumSpectators] = Player_by_index(ind);
1522     PlayersArray[ind] = pl;
1523     pl = Player_by_index(spectatorStart + NumSpectators);
1524 
1525     GetIndArray[Player_by_index(ind)->id] = ind;
1526     GetIndArray[pl->id] = spectatorStart + NumSpectators;
1527 
1528     Free_ship_shape(pl->ship);
1529     for (i = NumSpectators - 1; i >= 0; i--)
1530 	Send_leave(Player_by_index(i + spectatorStart)->conn, pl->id);
1531 }
1532 
Detach_ball(player_t * pl,ballobject_t * ball)1533 void Detach_ball(player_t *pl, ballobject_t *ball)
1534 {
1535     int i, cnt;
1536 
1537     if (ball == NULL || ball == pl->ball) {
1538 	pl->ball = NULL;
1539 	CLR_BIT(pl->used, USES_CONNECTOR);
1540     }
1541 
1542     if (BIT(pl->have, HAS_BALL)) {
1543 	for (cnt = i = 0; i < NumObjs; i++) {
1544 	    object_t *obj = Obj[i];
1545 
1546 	    if (obj->type == OBJ_BALL && obj->id == pl->id) {
1547 		if (ball == NULL || ball == BALL_PTR(obj))
1548 		    obj->id = NO_ID;
1549 		    /* Don't reset owner so you can throw balls */
1550 		else
1551 		    cnt++;
1552 	    }
1553 	}
1554 	if (cnt == 0)
1555 	    CLR_BIT(pl->have, HAS_BALL);
1556 	else
1557 	    sound_play_sensors(pl->pos, DROP_BALL_SOUND);
1558     }
1559 }
1560 
Kill_player(player_t * pl,bool add_rank_death)1561 void Kill_player(player_t *pl, bool add_rank_death)
1562 {
1563     if (Player_is_killed(pl))
1564 	Explode_fighter(pl);
1565     Player_death_reset(pl, add_rank_death);
1566 }
1567 
Player_death_reset(player_t * pl,bool add_rank_death)1568 void Player_death_reset(player_t *pl, bool add_rank_death)
1569 {
1570     if (Player_is_tank(pl)) {
1571 	Delete_player(pl);
1572 	return;
1573     }
1574 
1575     if (Player_is_paused(pl))
1576 	return;
1577 
1578     Detach_ball(pl, NULL);
1579     if (Player_uses_autopilot(pl)
1580 	|| Player_is_hoverpaused(pl)) {
1581 	CLR_BIT(pl->pl_status, HOVERPAUSE);
1582 	Autopilot(pl, false);
1583     }
1584 
1585     pl->vel.x		= pl->vel.y	= 0.0;
1586     pl->acc.x		= pl->acc.y	= 0.0;
1587     pl->emptymass	= pl->mass	= options.shipMass;
1588     pl->obj_status	&= ~(KILL_OBJ_BITS);
1589 
1590     if (BIT(world->rules->mode, LIMITED_LIVES)) {
1591 	bool waiting = Player_is_waiting(pl);
1592 
1593 	Player_set_life(pl, pl->pl_life - 1);
1594 	Player_set_state(pl, PL_STATE_APPEARING);
1595 
1596 	if (pl->pl_life == -1) {
1597 	    Player_set_life(pl, 0);
1598 	    if (waiting)
1599 		Player_set_state(pl, PL_STATE_WAITING);
1600 	    else
1601 		Player_set_state(pl, PL_STATE_DEAD);
1602 	    Player_lock_closest(pl, false);
1603 	}
1604     }
1605     else {
1606 	Player_set_life(pl, pl->pl_life + 1);
1607 	Player_set_state(pl, PL_STATE_APPEARING);
1608     }
1609 
1610     Player_init_items(pl);
1611 
1612     pl->forceVisible	= 0;
1613     assert(pl->recovery_count == RECOVERY_DELAY);
1614     pl->ecmcount	= 0;
1615     pl->emergency_thrust_left = 0;
1616     pl->emergency_shield_left = 0;
1617     pl->phasing_left	= 0;
1618     pl->self_destruct_count = 0;
1619     pl->damaged 	= 0;
1620     pl->stunned		= 0;
1621     pl->lock.distance	= 0;
1622 
1623     if (add_rank_death) {
1624 	Rank_add_death(pl);
1625 	pl->pl_deaths_since_join++;
1626     }
1627 
1628     pl->have	= DEF_HAVE;
1629     pl->used	&= ~(USED_KILL);
1630     pl->used	|= DEF_USED;
1631     pl->used	&= pl->have;
1632 }
1633 
1634 /* determines if two players are immune to eachother */
Team_immune(int id1,int id2)1635 bool Team_immune(int id1, int id2)
1636 {
1637     player_t *pl1, *pl2;
1638 
1639     /* owned stuff is never team immune */
1640     if (id1 == id2)
1641 	return false;
1642 
1643     if (!options.teamImmunity)
1644 	return false;
1645 
1646     if (id1 == NO_ID || id2 == NO_ID)
1647 	/* can't find owner for cannon stuff */
1648 	return false;
1649 
1650     pl1 = Player_by_id(id1);
1651     pl2 = Player_by_id(id2);
1652 
1653     if (Players_are_teammates(pl1, pl2))
1654 	return true;
1655 
1656     if (Players_are_allies(pl1, pl2))
1657 	return true;
1658 
1659     return false;
1660 }
1661 
old_status2str(int old_status)1662 static char *old_status2str(int old_status)
1663 {
1664     static char buf[256];
1665 
1666     buf[0] = '\0';
1667 
1668     if (old_status & OLD_PLAYING)
1669 	strlcat(buf, "OLD_PLAYING ", sizeof(buf));
1670     if (old_status & OLD_PAUSE)
1671 	strlcat(buf, "OLD_PAUSE ", sizeof(buf));
1672     if (old_status & OLD_GAME_OVER)
1673 	strlcat(buf, "OLD_GAME_OVER ", sizeof(buf));
1674 
1675     return buf;
1676 }
1677 
state2str(int state)1678 static char *state2str(int state)
1679 {
1680     static char buf[256];
1681 
1682     buf[0] = '\0';
1683 
1684     if (state == PL_STATE_UNDEFINED)
1685 	strlcat(buf, "PL_STATE_UNDEFINED", sizeof(buf));
1686     if (state == PL_STATE_WAITING)
1687 	strlcat(buf, "PL_STATE_WAITING", sizeof(buf));
1688     if (state == PL_STATE_APPEARING)
1689 	strlcat(buf, "PL_STATE_APPEARING", sizeof(buf));
1690     if (state == PL_STATE_ALIVE)
1691 	strlcat(buf, "PL_STATE_ALIVE", sizeof(buf));
1692     if (state == PL_STATE_KILLED)
1693 	strlcat(buf, "PL_STATE_KILLED", sizeof(buf));
1694     if (state == PL_STATE_DEAD)
1695 	strlcat(buf, "PL_STATE_DEAD", sizeof(buf));
1696     if (state == PL_STATE_PAUSED)
1697 	strlcat(buf, "PL_STATE_PAUSED", sizeof(buf));
1698 
1699     return buf;
1700 }
1701 
Player_print_state(player_t * pl,const char * funcname)1702 void Player_print_state(player_t *pl, const char *funcname)
1703 {
1704     warn("%-20s: %-16s (%c): %-20s %s ", funcname, pl->name, pl->mychar,
1705 	 state2str(pl->pl_state), old_status2str(pl->pl_old_status));
1706 }
1707 
Player_set_state(player_t * pl,int state)1708 void Player_set_state(player_t *pl, int state)
1709 {
1710     pl->pl_state = state;
1711 
1712     switch (state) {
1713     case PL_STATE_WAITING:
1714 	Player_set_mychar(pl, 'W');
1715 	Player_set_life(pl, 0);
1716 	pl->pl_old_status = OLD_GAME_OVER;
1717 	break;
1718     case PL_STATE_APPEARING:
1719 	Player_set_mychar(pl, pl->pl_type_mychar);
1720 	/*Player_set_mychar(pl, 'A');*/
1721 	pl->pl_old_status = 0;
1722 	pl->recovery_count = RECOVERY_DELAY;
1723 	break;
1724     case PL_STATE_ALIVE:
1725 	Player_set_mychar(pl, pl->pl_type_mychar);
1726 	pl->pl_old_status = OLD_PLAYING;
1727 	break;
1728     case PL_STATE_KILLED:
1729 	break;
1730     case PL_STATE_DEAD:
1731 	Player_set_mychar(pl, 'D');
1732 	pl->pl_old_status = OLD_GAME_OVER;
1733 	break;
1734     case PL_STATE_PAUSED:
1735 	Player_set_mychar(pl, 'P');
1736 	Player_set_life(pl, 0);
1737 	pl->pl_old_status = OLD_PAUSE;
1738 	break;
1739     default:
1740 	break;
1741     }
1742 }
1743