1 /*
2  * XPilot NG, a multiplayer space war game.
3  *
4  * Copyright (C) 2000-2004 by
5  *
6  *      Uoti Urpala          <uau@users.sourceforge.net>
7  *      Kristian S�derblom   <kps@users.sourceforge.net>
8  *
9  * Copyright (C) 1991-2001 by
10  *
11  *      Bj�rn Stabell        <bjoern@xpilot.org>
12  *      Ken Ronny Schouten   <ken@xpilot.org>
13  *      Bert Gijsbers        <bert@xpilot.org>
14  *      Dick Balaska         <dick@xpilot.org>
15  *
16  * This program is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * This program is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, write to the Free Software
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29  */
30 
31 #include "xpserver.h"
32 
33 
34 #define MAX_SHUFFLE_INDEX	65535
35 
36 
37 typedef unsigned short shuffle_t;
38 
39 /*
40  * Structure for calculating if a click position is visible by a player.
41  * Used for map state info updating.
42  * The following always holds:
43  *	(world.cx >= realWorld.cx && world.cy >= realWorld.cy)
44  */
45 typedef struct {
46     clpos_t	unrealWorld;		/* Lower left hand corner is this */
47 					/* world coordinate */
48     clpos_t	realWorld;		/* If the player is on the edge of
49 					   the screen, these are the world
50 					   coordinates before adjustment... */
51 } click_visibility_t;
52 
53 typedef struct {
54     unsigned char	x, y;
55 } debris_t;
56 
57 typedef struct {
58     short		x, y, size;
59 } radar_t;
60 
61 
62 long			frame_loops = 1;
63 long			frame_loops_slow = 1;
64 double			frame_time = 0;
65 static long		last_frame_shuffle;
66 static shuffle_t	*object_shuffle_ptr;
67 static int		num_object_shuffle;
68 static int		max_object_shuffle;
69 static shuffle_t	*player_shuffle_ptr;
70 static int		num_player_shuffle;
71 static int		max_player_shuffle;
72 static radar_t		*radar_ptr;
73 static int		num_radar, max_radar;
74 
75 static click_visibility_t cv;
76 static int		view_width,
77 			view_height,
78 			view_cwidth,
79 			view_cheight,
80 			debris_x_areas,
81 			debris_y_areas,
82 			debris_areas,
83 			debris_colors,
84 			spark_rand;
85 static debris_t		*debris_ptr[DEBRIS_TYPES];
86 static unsigned		debris_num[DEBRIS_TYPES],
87 			debris_max[DEBRIS_TYPES];
88 static debris_t		*fastshot_ptr[DEBRIS_TYPES * 2];
89 static unsigned		fastshot_num[DEBRIS_TYPES * 2],
90 			fastshot_max[DEBRIS_TYPES * 2];
91 
92 /*
93  * Macro to make room in a given dynamic array for new elements.
94  * P is the pointer to the array memory.
95  * N is the current number of elements in the array.
96  * M is the current size of the array.
97  * T is the type of the elements.
98  * E is the number of new elements to store in the array.
99  * The goal is to keep the number of malloc/realloc calls low
100  * while not wasting too much memory because of over-allocation.
101  */
102 #define EXPAND(P,N,M,T,E)						\
103     if ((N) + (E) > (M)) {						\
104 	if ((M) <= 0) {							\
105 	    M = (E) + 2;						\
106 	    P = (T *) malloc((M) * sizeof(T));				\
107 	    N = 0;							\
108 	} else {							\
109 	    M = ((M) << 1) + (E);					\
110 	    P = (T *) realloc(P, (M) * sizeof(T));			\
111 	}								\
112 	if (P == NULL) {						\
113 	    error("No memory");						\
114 	    N = M = 0;							\
115 	    return;	/* ! */						\
116 	}								\
117     }
118 
119 
120 /*
121  * Note - I've changed the block_inview calls to clpos_inview calls,
122  * which means that the center of a block has to be visible to be
123  * in view.
124  */
clpos_inview(click_visibility_t * v,clpos_t pos)125 static inline bool clpos_inview(click_visibility_t *v, clpos_t pos)
126 {
127     clpos_t wpos = v->unrealWorld, rwpos = v->realWorld;
128 
129     if (!((pos.cx > wpos.cx && pos.cx < wpos.cx + view_cwidth)
130 	  || (pos.cx > rwpos.cx && pos.cx < rwpos.cx + view_cwidth)))
131 	return false;
132     if (!((pos.cy > wpos.cy && pos.cy < wpos.cy + view_cheight)
133 	  || (pos.cy > rwpos.cy && pos.cy < rwpos.cy + view_cheight)))
134 	return false;
135     return true;
136 }
137 
138 #define DEBRIS_STORE(xd,yd,color,offset) \
139     int			i;						  \
140     if (xd < 0)								  \
141 	xd += world->width;						  \
142     if (yd < 0)								  \
143 	yd += world->height;						  \
144     if ((unsigned) xd >= (unsigned)view_width || (unsigned) yd >= (unsigned)view_height) {	  \
145 	/*								  \
146 	 * There's some rounding error or so somewhere.			  \
147 	 * Should be possible to resolve it.				  \
148 	 */								  \
149 	return;								  \
150     }									  \
151 									  \
152     i = offset + color * debris_areas					  \
153 	+ (((yd >> 8) % debris_y_areas) * debris_x_areas)		  \
154 	+ ((xd >> 8) % debris_x_areas);					  \
155 									  \
156     if (num_ >= 255)							  \
157 	return;								  \
158     if (num_ >= max_) {							  \
159 	if (num_ == 0)							  \
160 	    ptr_ = (debris_t *) malloc((max_ = 16) * sizeof(*ptr_));	  \
161 	else								  \
162 	    ptr_ = (debris_t *) realloc(ptr_, (max_ += max_) * sizeof(*ptr_)); \
163 	if (ptr_ == NULL) {						  \
164 	    error("No memory for debris");				  \
165 	    num_ = 0;							  \
166 	    return;							  \
167 	}								  \
168     }									  \
169     ptr_[num_].x = (unsigned char) xd;					  \
170     ptr_[num_].y = (unsigned char) yd;					  \
171     num_++;
172 
fastshot_store(int cx,int cy,int color,int offset)173 static void fastshot_store(int cx, int cy, int color, int offset)
174 {
175     int xf = CLICK_TO_PIXEL(cx),
176 	yf = CLICK_TO_PIXEL(cy);
177 #define ptr_		(fastshot_ptr[i])
178 #define num_		(fastshot_num[i])
179 #define max_		(fastshot_max[i])
180     DEBRIS_STORE(xf, yf, color, offset);
181 #undef ptr_
182 #undef num_
183 #undef max_
184 }
185 
debris_store(int cx,int cy,int color)186 static void debris_store(int cx, int cy, int color)
187 {
188     int xf = CLICK_TO_PIXEL(cx),
189 	yf = CLICK_TO_PIXEL(cy);
190 #define ptr_		(debris_ptr[i])
191 #define num_		(debris_num[i])
192 #define max_		(debris_max[i])
193     DEBRIS_STORE(xf, yf, color, 0);
194 #undef ptr_
195 #undef num_
196 #undef max_
197 }
198 
fastshot_end(connection_t * conn)199 static void fastshot_end(connection_t *conn)
200 {
201     int i;
202 
203     for (i = 0; i < DEBRIS_TYPES * 2; i++) {
204 	if (fastshot_num[i] != 0) {
205 	    Send_fastshot(conn, i,
206 			  (unsigned char *) fastshot_ptr[i],
207 			  fastshot_num[i]);
208 	    fastshot_num[i] = 0;
209 	}
210     }
211 }
212 
debris_end(connection_t * conn)213 static void debris_end(connection_t *conn)
214 {
215     int			i;
216 
217     for (i = 0; i < DEBRIS_TYPES; i++) {
218 	if (debris_num[i] != 0) {
219 	    Send_debris(conn, i,
220 			(unsigned char *) debris_ptr[i],
221 			debris_num[i]);
222 	    debris_num[i] = 0;
223 	}
224     }
225 }
226 
Frame_radar_buffer_reset(void)227 static void Frame_radar_buffer_reset(void)
228 {
229     num_radar = 0;
230 }
231 
Frame_radar_buffer_add(clpos_t pos,int s)232 static void Frame_radar_buffer_add(clpos_t pos, int s)
233 {
234     radar_t *p;
235 
236     EXPAND(radar_ptr, num_radar, max_radar, radar_t, 1);
237     p = &radar_ptr[num_radar++];
238     p->x = CLICK_TO_PIXEL(pos.cx);
239     p->y = CLICK_TO_PIXEL(pos.cy);
240     p->size = s;
241 }
242 
Frame_radar_buffer_send(connection_t * conn,player_t * pl)243 static void Frame_radar_buffer_send(connection_t *conn, player_t *pl)
244 {
245     int i, dest, tmp;
246     radar_t *p;
247     const int radar_width = 256;
248     int radar_height, radar_x, radar_y, send_x, send_y;
249     shuffle_t *radar_shuffle;
250 
251     radar_height = (radar_width * world->height) / world->width;
252 
253     if (num_radar > MIN(256, MAX_SHUFFLE_INDEX))
254 	num_radar = MIN(256, MAX_SHUFFLE_INDEX);
255     radar_shuffle = XMALLOC(shuffle_t, num_radar);
256     if (radar_shuffle == NULL)
257 	return;
258     for (i = 0; i < num_radar; i++)
259 	radar_shuffle[i] = i;
260 
261     if (conn->rectype != 2) {
262 	/* permute. */
263 	for (i = 0; i < num_radar; i++) {
264 	    dest = (int)(rfrac() * (num_radar - i)) + i;
265 	    tmp = radar_shuffle[i];
266 	    radar_shuffle[i] = radar_shuffle[dest];
267 	    radar_shuffle[dest] = tmp;
268 	}
269     }
270 
271     if (!FEATURE(conn, F_FASTRADAR)) {
272 	for (i = 0; i < num_radar; i++) {
273 	    p = &radar_ptr[radar_shuffle[i]];
274 	    radar_x = (radar_width * p->x) / world->width;
275 	    radar_y = (radar_height * p->y) / world->height;
276 	    send_x = (world->width * radar_x) / radar_width;
277 	    send_y = (world->height * radar_y) / radar_height;
278 	    Send_radar(conn, send_x, send_y, p->size);
279 	}
280     }
281     else {
282 	unsigned char buf[3*256];
283 	int buf_index = 0;
284 	unsigned fast_count = 0;
285 
286 	if (num_radar > 256)
287 	    num_radar = 256;
288 	for (i = 0; i < num_radar; i++) {
289 	    p = &radar_ptr[radar_shuffle[i]];
290 	    radar_x = (radar_width * p->x) / world->width;
291 	    radar_y = (radar_height * p->y) / world->height;
292 	    if (radar_y >= 1024)
293 		continue;
294 	    buf[buf_index++] = (unsigned char)(radar_x);
295 	    buf[buf_index++] = (unsigned char)(radar_y & 0xFF);
296 	    buf[buf_index] = (unsigned char)((radar_y >> 2) & 0xC0);
297 	    if (p->size & 0x80)
298 		buf[buf_index] |= (unsigned char)(0x20);
299 	    buf[buf_index] |= (unsigned char)(p->size & 0x07);
300 	    buf_index++;
301 	    fast_count++;
302 	}
303 	if (fast_count > 0)
304 	    Send_fastradar(conn, buf, fast_count);
305     }
306 
307     free(radar_shuffle);
308 }
309 
Frame_radar_buffer_free(void)310 static void Frame_radar_buffer_free(void)
311 {
312     XFREE(radar_ptr);
313     num_radar = 0;
314     max_radar = 0;
315 }
316 
Frame_status(connection_t * conn,player_t * pl)317 static int Frame_status(connection_t *conn, player_t *pl)
318 {
319     static char modsstr[MAX_CHARS];
320     int n, lock_ind, lock_id = NO_ID, lock_dist = 0, lock_dir = 0;
321     int showautopilot;
322 
323     /*
324      * Don't make lock visible during this frame if;
325      * 0) we are not player locked or compass is not on.
326      * 1) we have limited visibility and the player is out of range.
327      * 2) the player is invisible and he's not in our team.
328      * 3) he's not actively playing.
329      * 4) we have blind mode and he's not on the visible screen.
330      * 5) his distance is zero.
331      */
332 
333     CLR_BIT(pl->lock.tagged, LOCK_VISIBLE);
334     if (BIT(pl->lock.tagged, LOCK_PLAYER)
335 	&& Player_uses_compass(pl)) {
336 	player_t *lock_pl = Player_by_id(pl->lock.pl_id);
337 
338 	lock_id = pl->lock.pl_id;
339 	lock_ind = GetInd(lock_id);
340 
341 	if ((!BIT(world->rules->mode, LIMITED_VISIBILITY)
342 	     || pl->lock.distance <= pl->sensor_range)
343 	    && (pl->visibility[lock_ind].canSee
344 		|| Player_owns_tank(pl, lock_pl)
345 		|| Players_are_teammates(pl, lock_pl)
346 		|| Players_are_allies(pl, lock_pl))
347 	    && Player_is_alive(lock_pl)
348 	    && (options.playersOnRadar
349 		|| clpos_inview(&cv, lock_pl->pos))
350 	    && pl->lock.distance != 0) {
351 	    double a;
352 
353 	    SET_BIT(pl->lock.tagged, LOCK_VISIBLE);
354 	    a = Wrap_cfindDir(lock_pl->pos.cx - pl->pos.cx,
355 			      lock_pl->pos.cy - pl->pos.cy);
356 	    lock_dir = (int) a;
357 	    lock_dist = (int)pl->lock.distance;
358 	}
359     }
360 
361     if (Player_is_hoverpaused(pl))
362 	showautopilot = (pl->pause_count <= 0 || (frame_loops_slow % 8) < 4);
363     else if (Player_uses_autopilot(pl))
364 	showautopilot = (frame_loops_slow % 8) < 4;
365     else
366 	showautopilot = 0;
367 
368     /*
369      * Don't forget to modify Receive_modifier_bank() in netserver.c
370      */
371     Mods_to_string(pl->mods, modsstr, sizeof(modsstr));
372     n = Send_self(conn,
373 		  pl,
374 		  lock_id,
375 		  lock_dist,
376 		  lock_dir,
377 		  showautopilot,
378 		  Player_by_id(Get_player_id(conn))->pl_old_status,
379 		  modsstr);
380     if (n <= 0)
381 	return 0;
382 
383     if (Player_uses_emergency_thrust(pl))
384 	Send_thrusttime(conn,
385 			(int) pl->emergency_thrust_left,
386 			EMERGENCY_THRUST_TIME);
387     if (BIT(pl->used, HAS_EMERGENCY_SHIELD))
388 	Send_shieldtime(conn,
389 			(int) pl->emergency_shield_left,
390 			EMERGENCY_SHIELD_TIME);
391     if (Player_is_self_destructing(pl))
392 	Send_destruct(conn, (int) pl->self_destruct_count);
393     if (Player_is_phasing(pl))
394 	Send_phasingtime(conn,
395 			 (int) pl->phasing_left,
396 			 PHASING_TIME);
397     if (ShutdownServer != -1)
398 	Send_shutdown(conn, ShutdownServer, ShutdownDelay);
399 
400     return 1;
401 }
402 
403 
Frame_map(connection_t * conn,player_t * pl)404 static void Frame_map(connection_t *conn, player_t *pl)
405 {
406     int i, k, conn_bit = (1 << conn->ind);
407     const int fuel_packet_size = 5;
408     const int cannon_packet_size = 5;
409     const int target_packet_size = 7;
410     const int polystyle_packet_size = 5;
411     int bytes_left = 2000, max_packet, packet_count;
412 
413     packet_count = 0;
414     max_packet = MAX(5, bytes_left / target_packet_size);
415     i = MAX(0, pl->last_target_update);
416     for (k = 0; k < Num_targets(); k++) {
417 	target_t *targ;
418 
419 	if (++i >= Num_targets())
420 	    i = 0;
421 	targ = Target_by_index(i);
422 	if (BIT(targ->update_mask, conn_bit)
423 	    || (BIT(targ->conn_mask, conn_bit) == 0
424 		&& clpos_inview(&cv, targ->pos))) {
425 	    Send_target(conn, i, (int)targ->dead_ticks, targ->damage);
426 	    pl->last_target_update = i;
427 	    bytes_left -= target_packet_size;
428 	    if (++packet_count >= max_packet)
429 		break;
430 	}
431     }
432 
433     packet_count = 0;
434     max_packet = MAX(5, bytes_left / cannon_packet_size);
435     i = MAX(0, pl->last_cannon_update);
436     for (k = 0; k < Num_cannons(); k++) {
437 	cannon_t *cannon;
438 
439 	if (++i >= Num_cannons())
440 	    i = 0;
441 	cannon = Cannon_by_index(i);
442 	if (clpos_inview(&cv, cannon->pos)) {
443 	    if (BIT(cannon->conn_mask, conn_bit) == 0) {
444 		Send_cannon(conn, i, (int)cannon->dead_ticks);
445 		pl->last_cannon_update = i;
446 		bytes_left -= max_packet * cannon_packet_size;
447 		if (++packet_count >= max_packet)
448 		    break;
449 	    }
450 	}
451     }
452 
453     packet_count = 0;
454     max_packet = MAX(5, bytes_left / fuel_packet_size);
455     i = MAX(0, pl->last_fuel_update);
456     for (k = 0; k < Num_fuels(); k++) {
457 	fuel_t *fs;
458 
459 	if (++i >= Num_fuels())
460 	    i = 0;
461 
462 	fs = Fuel_by_index(i);
463 	if (BIT(fs->conn_mask, conn_bit) == 0) {
464 	    if ((CENTER_XCLICK(fs->pos.cx - pl->pos.cx) <
465 		 (view_width << CLICK_SHIFT) + BLOCK_CLICKS) &&
466 		(CENTER_YCLICK(fs->pos.cy - pl->pos.cy) <
467 		 (view_height << CLICK_SHIFT) + BLOCK_CLICKS)) {
468 		Send_fuel(conn, i, fs->fuel);
469 		pl->last_fuel_update = i;
470 		bytes_left -= max_packet * fuel_packet_size;
471 		if (++packet_count >= max_packet)
472 		    break;
473 	    }
474 	}
475     }
476 
477     packet_count = 0;
478     max_packet = MAX(5, bytes_left / polystyle_packet_size);
479     i = MAX(0, pl->last_polystyle_update);
480     for (k = 0; k < num_polys; k++) {
481 	poly_t *poly;
482 
483 	if (++i >= num_polys)
484 	    i = 0;
485 
486 	poly = &pdata[i];
487 	if (BIT(poly->update_mask, conn_bit)) {
488 	    Send_polystyle(conn, i, poly->current_style);
489 	    pl->last_polystyle_update = i;
490 	    bytes_left -= max_packet * polystyle_packet_size;
491 	    if (++packet_count >= max_packet)
492 		break;
493 	}
494     }
495 }
496 
497 
Frame_shuffle_objects(void)498 static void Frame_shuffle_objects(void)
499 {
500     int i;
501 
502     num_object_shuffle = MIN(NumObjs, options.maxVisibleObject);
503 
504     if (max_object_shuffle < num_object_shuffle) {
505 	XFREE(object_shuffle_ptr);
506 	max_object_shuffle = num_object_shuffle;
507 	object_shuffle_ptr = XMALLOC(shuffle_t, max_object_shuffle);
508 	if (object_shuffle_ptr == NULL)
509 	    max_object_shuffle = 0;
510     }
511 
512     if (max_object_shuffle < num_object_shuffle)
513 	num_object_shuffle = max_object_shuffle;
514 
515     for (i = 0; i < num_object_shuffle; i++)
516 	object_shuffle_ptr[i] = i;
517 
518     /* permute. Not perfect distribution but probably doesn't matter here */
519     for (i = num_object_shuffle - 1; i >= 0; --i) {
520 	if (object_shuffle_ptr[i] == i) {
521 	    int j = (int)(rfrac() * i);
522 	    shuffle_t tmp = object_shuffle_ptr[j];
523 	    object_shuffle_ptr[j] = object_shuffle_ptr[i];
524 	    object_shuffle_ptr[i] = tmp;
525 	}
526     }
527 }
528 
Frame_shuffle_players(void)529 static void Frame_shuffle_players(void)
530 {
531     int				i;
532 
533     num_player_shuffle = MIN(NumPlayers, MAX_SHUFFLE_INDEX);
534 
535     if (max_player_shuffle < num_player_shuffle) {
536 	XFREE(player_shuffle_ptr);
537 	max_player_shuffle = num_player_shuffle;
538 	player_shuffle_ptr = XMALLOC(shuffle_t, max_player_shuffle);
539 	if (player_shuffle_ptr == NULL)
540 	    max_player_shuffle = 0;
541     }
542 
543     if (max_player_shuffle < num_player_shuffle)
544 	num_player_shuffle = max_player_shuffle;
545 
546     for (i = 0; i < num_player_shuffle; i++)
547 	player_shuffle_ptr[i] = i;
548 
549     /* permute. */
550     for (i = 0; i < num_player_shuffle; i++) {
551 	int j = (int)(rfrac() * (num_player_shuffle - i) + i);
552 	shuffle_t tmp = player_shuffle_ptr[j];
553 	player_shuffle_ptr[j] = player_shuffle_ptr[i];
554 	player_shuffle_ptr[i] = tmp;
555     }
556 }
557 
558 
Frame_shuffle(void)559 static void Frame_shuffle(void)
560 {
561     if (last_frame_shuffle != frame_loops) {
562 	last_frame_shuffle = frame_loops;
563 	Frame_shuffle_objects();
564 	Frame_shuffle_players();
565     }
566 }
567 
Frame_shots(connection_t * conn,player_t * pl)568 static void Frame_shots(connection_t *conn, player_t *pl)
569 {
570     clpos_t pos;
571     int ldir = 0, i, k, color, fuzz = 0, teamshot, len, obj_count;
572     object_t *shot, **obj_list;
573     int hori_blocks, vert_blocks;
574 
575     hori_blocks = (view_width + (BLOCK_SZ - 1)) / (2 * BLOCK_SZ);
576     vert_blocks = (view_height + (BLOCK_SZ - 1)) / (2 * BLOCK_SZ);
577     if (NumObjs >= options.cellGetObjectsThreshold)
578 	Cell_get_objects(pl->pos, MAX(hori_blocks, vert_blocks),
579 			 num_object_shuffle, &obj_list, &obj_count);
580     else {
581 	obj_list = Obj;
582 	obj_count = NumObjs;
583     }
584 
585     for (k = 0; k < num_object_shuffle; k++) {
586 	i = object_shuffle_ptr[k];
587 	if (i >= obj_count)
588 	    continue;
589 	shot = obj_list[i];
590 	pos = shot->pos;
591 
592 	if (shot->type != OBJ_PULSE) {
593 	    if (!clpos_inview(&cv, shot->pos))
594 		continue;
595 	} else {
596 	    pulseobject_t *pulse = PULSE_PTR(shot);
597 
598 	    /* check if either end of laser pulse is in view */
599 	    if (clpos_inview(&cv, pos))
600 		ldir = MOD2(pulse->pulse_dir + RES/2, RES);
601 	    else {
602 		pos.cx = (click_t)(pos.cx
603 			  - tcos(pulse->pulse_dir) * pulse->pulse_len * CLICK);
604 		pos.cy = (click_t)(pos.cy
605 			  - tsin(pulse->pulse_dir) * pulse->pulse_len * CLICK);
606 		pos = World_wrap_clpos(pos);
607 		ldir = pulse->pulse_dir;
608 		if (!clpos_inview(&cv, pos))
609 		    continue;
610 	    }
611 	}
612 	if ((color = shot->color) == BLACK) {
613 	    xpprintf("black %d,%d\n", shot->type, shot->id);
614 	    color = WHITE;
615 	}
616 	switch (shot->type) {
617 	case OBJ_SPARK:
618 	case OBJ_DEBRIS:
619 	    if ((fuzz >>= 7) < 0x40) {
620 		if (conn->rectype != 2)
621 		    fuzz = randomMT();
622 		else
623 		    fuzz = 0;
624 	    }
625 	    if ((fuzz & 0x7F) >= spark_rand) {
626 		/*
627 		 * produce a sparkling effect by not displaying
628 		 * particles every frame.
629 		 */
630 		break;
631 	    }
632 	    /*
633 	     * The number of colors which the client
634 	     * uses for displaying debris is bigger than 2
635 	     * then the color used denotes the temperature
636 	     * of the debris particles.
637 	     * Higher color number means hotter debris.
638 	     */
639 	    if (debris_colors >= 3) {
640 		if (debris_colors > 4) {
641 		    if (color == BLUE)
642 			color = (int)shot->life / 2;
643 		    else
644 			color = (int)shot->life / 4;
645 		} else {
646 		    if (color == BLUE)
647 			color = (int)shot->life / 4;
648 		    else
649 			color = (int)shot->life / 8;
650 		}
651 		if (color >= debris_colors)
652 		    color = debris_colors - 1;
653 	    }
654 
655 	    debris_store(shot->pos.cx - cv.unrealWorld.cx,
656 			 shot->pos.cy - cv.unrealWorld.cy,
657 			 color);
658 	    break;
659 
660 	case OBJ_WRECKAGE:
661 	    if (spark_rand != 0 || options.wreckageCollisionMayKill) {
662 		wireobject_t *wreck = WIRE_PTR(shot);
663 		Send_wreckage(conn, pos, wreck->wire_type,
664 			      wreck->wire_size, wreck->wire_rotation);
665 	    }
666 	    break;
667 
668 	case OBJ_ASTEROID: {
669 		wireobject_t *ast = WIRE_PTR(shot);
670 		Send_asteroid(conn, pos, ast->wire_type,
671 			      ast->wire_size, ast->wire_rotation);
672 	    }
673 	    break;
674 
675 	case OBJ_SHOT:
676 	case OBJ_CANNON_SHOT:
677 	    if (Team_immune(shot->id, pl->id)
678 		|| (shot->id != NO_ID
679 		    && Player_is_paused(Player_by_id(shot->id)))
680 		|| (shot->id == NO_ID
681 		    && BIT(world->rules->mode, TEAM_PLAY)
682 		    && shot->team == pl->team)) {
683 		color = BLUE;
684 		teamshot = DEBRIS_TYPES;
685 	    } else if (shot->id == pl->id
686 		&& options.selfImmunity) {
687 		color = BLUE;
688 		teamshot = DEBRIS_TYPES;
689 	    } else if (Mods_get(shot->mods, ModsNuclear)
690 		       && (frame_loops_slow & 2)) {
691 		color = RED;
692 		teamshot = DEBRIS_TYPES;
693 	    } else
694 		teamshot = 0;
695 
696 	    fastshot_store(shot->pos.cx - cv.unrealWorld.cx,
697 			   shot->pos.cy - cv.unrealWorld.cy,
698 			   color, teamshot);
699 	    break;
700 
701 	case OBJ_TORPEDO:
702 	    len = options.distinguishMissiles ? TORPEDO_LEN : MISSILE_LEN;
703 	    Send_missile(conn, pos, len, MISSILE_PTR(shot)->missile_dir);
704 	    break;
705 	case OBJ_SMART_SHOT:
706 	    len = options.distinguishMissiles ? SMART_SHOT_LEN : MISSILE_LEN;
707 	    Send_missile(conn, pos, len, MISSILE_PTR(shot)->missile_dir);
708 	    break;
709 	case OBJ_HEAT_SHOT:
710 	    len = options.distinguishMissiles ? HEAT_SHOT_LEN : MISSILE_LEN;
711 	    Send_missile(conn, pos, len, MISSILE_PTR(shot)->missile_dir);
712 	    break;
713 	case OBJ_BALL:
714 	    {
715 		ballobject_t *ball = BALL_PTR(shot);
716 
717 		Send_ball(conn, pos, ball->id,
718 			  options.ballStyles ? ball->ball_style : 0xff);
719 		break;
720 	    }
721 	    break;
722 	case OBJ_MINE:
723 	    {
724 		int id = 0;
725 		int laid_by_team = 0;
726 		int confused = 0;
727 		mineobject_t *mine = MINE_PTR(shot);
728 
729 		/* calculate whether ownership of mine can be determined */
730 		if (options.identifyMines
731 		    && (Wrap_length(pl->pos.cx - mine->pos.cx,
732 				    pl->pos.cy - mine->pos.cy) / CLICK
733 			< (SHIP_SZ + MINE_SENSE_BASE_RANGE
734 			   + pl->item[ITEM_SENSOR] * MINE_SENSE_RANGE_FACTOR))) {
735 		    id = mine->id;
736 		    if (id == NO_ID)
737 			id = EXPIRED_MINE_ID;
738 		    if (BIT(mine->obj_status, CONFUSED))
739 			confused = 1;
740 		}
741 		if (mine->id != NO_ID
742 		    && Player_is_paused(Player_by_id(mine->id))) {
743 		    laid_by_team = 1;
744 		} else {
745 		    laid_by_team = (Team_immune(mine->id, pl->id)
746 				    || (BIT(mine->obj_status, OWNERIMMUNE)
747 					&& mine->mine_owner == pl->id));
748 		    if (confused) {
749 			id = 0;
750 			laid_by_team = (rfrac() < 0.5);
751 		    }
752 		}
753 		Send_mine(conn, pos, laid_by_team, id);
754 	    }
755 	    break;
756 
757 	case OBJ_ITEM:
758 	    {
759 		itemobject_t *item = ITEM_PTR(shot);
760 		int item_type = item->item_type;
761 
762 		if (BIT(item->obj_status, RANDOM_ITEM))
763 		    item_type = Choose_random_item();
764 
765 		Send_item(conn, pos, item_type);
766 	    }
767 	    break;
768 
769 	case OBJ_PULSE:
770 	    {
771 		pulseobject_t *pulse = PULSE_PTR(shot);
772 
773 		if (Team_immune(pulse->id, pl->id))
774 		    color = BLUE;
775 		else if (pulse->id == pl->id && options.selfImmunity)
776 		    color = BLUE;
777 		else
778 		    color = RED;
779 		Send_laser(conn, color, pos, (int)pulse->pulse_len, ldir);
780 	    }
781 	break;
782 	default:
783 	    warn("Frame_shots: Shot type %d not defined.", shot->type);
784 	    break;
785 	}
786     }
787 }
788 
Frame_ships(connection_t * conn,player_t * pl)789 static void Frame_ships(connection_t *conn, player_t *pl)
790 {
791     int i, k;
792 
793     for (i = 0; i < Num_ecms(); i++) {
794 	ecm_t *ecm = Ecm_by_index(i);
795 
796 	if (clpos_inview(&cv, ecm->pos))
797 	    Send_ecm(conn, ecm->pos, (int)ecm->size);
798     }
799 
800     for (i = 0; i < Num_transporters(); i++) {
801 	transporter_t *trans = Transporter_by_index(i);
802 	player_t *victim = Player_by_id(trans->victim_id);
803 	player_t *tpl = Player_by_id(trans->id);
804 	clpos_t	pos = (tpl ? tpl->pos : trans->pos);
805 
806 	/* Player_by_id() can return NULL if the player quit the game. */
807 	if (victim == NULL || tpl == NULL)
808 	    continue;
809 
810 	if (clpos_inview(&cv, victim->pos)
811 	    || clpos_inview(&cv, pos))
812 	    Send_trans(conn, victim->pos, pos);
813     }
814 
815     for (i = 0; i < Num_cannons(); i++) {
816 	cannon_t *cannon = Cannon_by_index(i);
817 
818 	if (cannon->tractor_count > 0) {
819 	    player_t *t = Player_by_id(cannon->tractor_target_id);
820 
821 	    /* Player_by_id() can return NULL if the player quit the game. */
822 	    if (t == NULL)
823 		continue;
824 
825 	    if (clpos_inview(&cv, t->pos)) {
826 		int j;
827 
828 		for (j = 0; j < 3; j++) {
829 		    clpos_t pts, pos;
830 
831 		    pts = Ship_get_point_clpos(t->ship, j, t->dir);
832 		    pos.cx = t->pos.cx + pts.cx;
833 		    pos.cy = t->pos.cy + pts.cy;
834 		    Send_connector(conn, pos, cannon->pos, 1);
835 		}
836 	    }
837 	}
838     }
839 
840     for (k = 0; k < num_player_shuffle; k++) {
841 	player_t *pl_i;
842 
843 	i = player_shuffle_ptr[k];
844 	pl_i = Player_by_index(i);
845 
846 	if (Player_is_waiting(pl_i)
847 	    || Player_is_dead(pl_i))
848 	    continue;
849 
850 	if (Player_is_paused(pl_i)
851 	    || Player_is_appearing(pl_i)) {
852 	    if (pl_i->home_base == NULL)
853 		continue;
854 	    if (!clpos_inview(&cv, pl_i->home_base->pos))
855 		continue;
856 	    if (Player_is_paused(pl_i))
857 		Send_paused(conn, pl_i->home_base->pos,
858 			    (int)pl_i->pause_count);
859 	    else
860 		Send_appearing(conn, pl_i->home_base->pos, pl_i->id,
861 			       (int)(pl_i->recovery_count * 10));
862 	    continue;
863 	}
864 
865 	if (!clpos_inview(&cv, pl_i->pos))
866 	    continue;
867 
868 	/* Don't transmit information if fighter is invisible */
869 	if (pl->visibility[i].canSee
870 	    || pl_i->id == pl->id
871 	    || Players_are_teammates(pl_i, pl)
872 	    || Players_are_allies(pl_i, pl)) {
873 	    /*
874 	     * Transmit ship information
875 	     */
876 	    Send_ship(conn,
877 		      pl_i->pos,
878 		      pl_i->id,
879 		      pl_i->dir,
880 		      BIT(pl_i->used, HAS_SHIELD) != 0,
881 		      Player_is_cloaked(pl_i) ? 1 : 0,
882 		      BIT(pl_i->used, HAS_EMERGENCY_SHIELD) != 0,
883 		      Player_is_phasing(pl_i) ? 1 : 0,
884 		      BIT(pl_i->used, USES_DEFLECTOR) != 0
885 	    );
886 	}
887 
888 	if (Player_is_refueling(pl_i)) {
889 	    fuel_t *fs = Fuel_by_index(pl_i->fs);
890 
891 	    if (clpos_inview(&cv, fs->pos))
892 		Send_refuel(conn, fs->pos, pl_i->pos);
893 	}
894 
895 	if (Player_is_repairing(pl_i)) {
896 	    target_t *targ = Target_by_index(pl_i->repair_target);
897 
898 	    if (clpos_inview(&cv, targ->pos))
899 		/* same packet as refuel */
900 		Send_refuel(conn, pl_i->pos, targ->pos);
901 	}
902 
903 	if (Player_uses_tractor_beam(pl_i)) {
904 	    player_t *t = Player_by_id(pl_i->lock.pl_id);
905 
906 	    if (clpos_inview(&cv, t->pos)) {
907 		int j;
908 
909 		for (j = 0; j < 3; j++) {
910 		    clpos_t pts, pos;
911 
912 		    pts = Ship_get_point_clpos(t->ship, j, t->dir);
913 		    pos.cx = t->pos.cx + pts.cx;
914 		    pos.cy = t->pos.cy + pts.cy;
915 		    Send_connector(conn, pos, pl_i->pos, 1);
916 		}
917 	    }
918 	}
919 
920 	if (pl_i->ball != NULL
921 	    && clpos_inview(&cv, pl_i->ball->pos))
922 	    Send_connector(conn, pl_i->ball->pos, pl_i->pos, 0);
923     }
924 }
925 
Frame_radar(connection_t * conn,player_t * pl)926 static void Frame_radar(connection_t *conn, player_t *pl)
927 {
928     int i, k, mask, shownuke, size;
929     object_t *shot;
930     clpos_t pos;
931 
932     Frame_radar_buffer_reset();
933 
934     if (options.nukesOnRadar)
935 	mask = OBJ_SMART_SHOT_BIT|OBJ_TORPEDO_BIT|OBJ_HEAT_SHOT_BIT
936 	    |OBJ_MINE_BIT;
937     else {
938 	mask = (options.missilesOnRadar ?
939 		(OBJ_SMART_SHOT_BIT|OBJ_TORPEDO_BIT|OBJ_HEAT_SHOT_BIT) : 0);
940 	mask |= (options.minesOnRadar) ? OBJ_MINE_BIT : 0;
941     }
942     if (options.treasuresOnRadar)
943 	mask |= OBJ_BALL_BIT;
944     if (options.asteroidsOnRadar)
945 	mask |= OBJ_ASTEROID_BIT;
946 
947     if (mask) {
948 	for (i = 0; i < NumObjs; i++) {
949 	    shot = Obj[i];
950 	    if (!BIT(OBJ_TYPEBIT(shot->type), mask))
951 		continue;
952 
953 	    shownuke = (options.nukesOnRadar
954 			&& Mods_get(shot->mods, ModsNuclear));
955 	    if (shownuke && (frame_loops_slow & 2))
956 		size = 3;
957 	    else
958 		size = 0;
959 
960 	    if (shot->type == OBJ_MINE) {
961 		if (!options.minesOnRadar && !shownuke)
962 		    continue;
963 		if (frame_loops_slow % 8 >= 6)
964 		    continue;
965 	    } else if (shot->type == OBJ_BALL) {
966 		size = 2;
967 	    } else if (shot->type == OBJ_ASTEROID) {
968 		size = WIRE_PTR(shot)->wire_size + 1;
969 		size |= 0x80;
970 	    } else {
971 		if (!options.missilesOnRadar && !shownuke)
972 		    continue;
973 		if (frame_loops_slow & 1)
974 		    continue;
975 	    }
976 
977 	    pos = shot->pos;
978 	    if (Wrap_length(pl->pos.cx - pos.cx,
979 			    pl->pos.cy - pos.cy) <= pl->sensor_range * CLICK)
980 		Frame_radar_buffer_add(pos, size);
981 	}
982     }
983 
984     if (options.playersOnRadar
985 	|| BIT(world->rules->mode, TEAM_PLAY)
986 	|| NumPseudoPlayers > 0
987 	|| NumAlliances > 0) {
988 	for (k = 0; k < num_player_shuffle; k++) {
989 	    player_t *pl_i;
990 
991 	    i = player_shuffle_ptr[k];
992 	    pl_i = Player_by_index(i);
993 	    /*
994 	     * Don't show on the radar:
995 	     *		Ourselves (not necessarily same as who we watch).
996 	     *		People who are not playing.
997 	     *		People in other teams or alliances if;
998 	     *			no options.playersOnRadar or if not visible
999 	     */
1000 	    if (pl_i->conn == conn
1001 		|| !Player_is_active(pl_i) /* kps - active / playing ??? */
1002 		|| (!Players_are_teammates(pl_i, pl)
1003 		    && !Players_are_allies(pl, pl_i)
1004 		    && !Player_owns_tank(pl, pl_i)
1005 		    && (!options.playersOnRadar
1006 			|| !pl->visibility[i].canSee)))
1007 		continue;
1008 	    pos = pl_i->pos;
1009 	    if (BIT(world->rules->mode, LIMITED_VISIBILITY)
1010 		&& Wrap_length(pl->pos.cx - pos.cx,
1011 			       pl->pos.cy - pos.cy) > pl->sensor_range * CLICK)
1012 		continue;
1013 	    if (Player_uses_compass(pl)
1014 		&& BIT(pl->lock.tagged, LOCK_PLAYER)
1015 		&& GetInd(pl->lock.pl_id) == i
1016 		&& frame_loops_slow % 5 >= 3)
1017 		continue;
1018 	    size = 3;
1019 	    if (Players_are_teammates(pl_i, pl)
1020 		|| Players_are_allies(pl, pl_i)
1021 		|| Player_owns_tank(pl, pl_i))
1022 		size |= 0x80;
1023 	    Frame_radar_buffer_add(pos, size);
1024 	}
1025     }
1026 
1027     Frame_radar_buffer_send(conn, pl);
1028 }
1029 
Frame_lose_item_state(player_t * pl)1030 static void Frame_lose_item_state(player_t *pl)
1031 {
1032     if (pl->lose_item_state != 0) {
1033 	Send_loseitem(pl->conn, pl->lose_item);
1034 	if (pl->lose_item_state == 1)
1035 	    pl->lose_item_state = -5;
1036 	if (pl->lose_item_state < 0)
1037 	    pl->lose_item_state++;
1038     }
1039 }
1040 
Frame_parameters(connection_t * conn,player_t * pl)1041 static void Frame_parameters(connection_t *conn, player_t *pl)
1042 {
1043     Get_display_parameters(conn, &view_width, &view_height,
1044 			   &debris_colors, &spark_rand);
1045     debris_x_areas = (view_width + 255) >> 8;
1046     debris_y_areas = (view_height + 255) >> 8;
1047     debris_areas = debris_x_areas * debris_y_areas;
1048 
1049     view_cwidth = view_width * CLICK;
1050     view_cheight = view_height * CLICK;
1051     cv.unrealWorld.cx = pl->pos.cx - view_cwidth / 2;	/* Scroll */
1052     cv.unrealWorld.cy = pl->pos.cy - view_cheight / 2;
1053     cv.realWorld = cv.unrealWorld;
1054     if (BIT (world->rules->mode, WRAP_PLAY)) {
1055 	if (cv.unrealWorld.cx < 0
1056 	    && cv.unrealWorld.cx + view_cwidth < world->cwidth)
1057 	    cv.unrealWorld.cx += world->cwidth;
1058 	else if (cv.unrealWorld.cx > 0
1059 		 && cv.unrealWorld.cx + view_cwidth >= world->cwidth)
1060 	    cv.realWorld.cx -= world->cwidth;
1061 	if (cv.unrealWorld.cy < 0
1062 	    && cv.unrealWorld.cy + view_cheight < world->cheight)
1063 	    cv.unrealWorld.cy += world->cheight;
1064 	else if (cv.unrealWorld.cy > 0
1065 		 && cv.unrealWorld.cy + view_cheight >=world->cheight)
1066 	    cv.realWorld.cy -= world->cheight;
1067     }
1068 }
1069 
Frame_update(void)1070 void Frame_update(void)
1071 {
1072     int i, ind, player_fps;
1073     connection_t *conn;
1074     player_t *pl, *pl2;
1075     time_t newTimeLeft = 0;
1076     static time_t oldTimeLeft;
1077     static bool game_over_called = false;
1078     static double frame_time2 = 0.0;
1079 
1080     frame_loops++;
1081     frame_time += timeStep;
1082     frame_time2 += timeStep;
1083     if (frame_time2 >= 1.0) {
1084 	frame_time2 -= 1.0;
1085 	frame_loops_slow++;
1086     }
1087 
1088     Frame_shuffle();
1089 
1090     if (options.gameDuration > 0.0
1091 	&& game_over_called == false
1092 	&& oldTimeLeft != (newTimeLeft = gameOverTime - time(NULL))) {
1093 	/*
1094 	 * Do this once a second.
1095 	 */
1096 	if (newTimeLeft <= 0) {
1097 	    Game_Over();
1098 	    ShutdownServer = 30 * FPS;	/* Shutdown in 30 seconds */
1099 	    game_over_called = true;
1100 	}
1101     }
1102 
1103     for (i = 0; i < spectatorStart + NumSpectators; i++) {
1104 	if (i >= num_player_shuffle && i < spectatorStart)
1105 	    continue;
1106 	pl = Player_by_index(i);
1107 	conn = pl->conn;
1108 	if (conn == NULL)
1109 	    continue;
1110 	playback = (pl->rectype == 1);
1111 	player_fps = FPS;
1112 	if ((Player_is_paused(pl)
1113 	     || Player_is_waiting(pl)
1114 	     || Player_is_dead(pl))
1115 	    && pl->rectype != 2) {
1116 	    /*
1117 	     * Lower the frame rate for some players
1118 	     * to reduce network load.
1119 	     */
1120 	    if (Player_is_paused(pl) && options.pausedFPS)
1121 		player_fps = options.pausedFPS;
1122 	    else if (Player_is_waiting(pl) && options.waitingFPS)
1123 		player_fps = options.waitingFPS;
1124 	    else if (Player_is_dead(pl) && options.deadFPS)
1125 		player_fps = options.deadFPS;
1126 	}
1127 	player_fps = MIN(player_fps, pl->player_fps);
1128 
1129 	/*
1130 	 * Reduce frame rate to player's own rate.
1131 	 */
1132 	if (player_fps < FPS && !options.ignoreMaxFPS) {
1133 	    int divisor = (FPS - 1) / player_fps + 1;
1134 	    if (frame_loops % divisor)
1135  		continue;
1136 	}
1137 
1138 	if (Send_start_of_frame(conn) == -1)
1139 	    continue;
1140 	if (newTimeLeft != oldTimeLeft)
1141 	    Send_time_left(conn, newTimeLeft);
1142 	else if (options.maxRoundTime > 0 && roundtime >= 0)
1143 	    Send_time_left(conn, (roundtime + FPS - 1) / FPS);
1144 
1145 	/*
1146 	 * If player is waiting, dead or paused, the user may look through the
1147 	 * other players 'eyes'. Option lockOtherTeam determines whether
1148 	 * you can watch opponents while your own team is still alive
1149 	 * (potentially giving information to your team). Note that
1150 	 * if a player got killed on last life, he will not be allowed
1151 	 * to change view before the view has returned to his base.
1152 	 *
1153 	 * This is done by using two indexes, one
1154 	 * determining which data should be used (ind, set below) and
1155 	 * one determining which connection to send it to (conn).
1156 	 */
1157 	if (BIT(pl->lock.tagged, LOCK_PLAYER)
1158 	    && (Player_is_waiting(pl)
1159 		|| (Player_is_dead(pl) && pl->recovery_count == 0.0)
1160 		|| Player_is_paused(pl))) {
1161 	    ind = GetInd(pl->lock.pl_id);
1162 	} else
1163 	    ind = i;
1164 	if (pl->rectype == 2) {
1165 	    if (BIT(pl->lock.tagged, LOCK_PLAYER))
1166 		ind = GetInd(pl->lock.pl_id);
1167 	    else
1168 		ind = 0;
1169 	}
1170 
1171 	pl2 = Player_by_index(ind);
1172 	if (pl2->damaged > 0)
1173 	    Send_damaged(conn, (int)pl2->damaged);
1174 	else {
1175 	    Frame_parameters(conn, pl2);
1176 	    if (Frame_status(conn, pl2) <= 0)
1177 		continue;
1178 	    Frame_map(conn, pl2);
1179 	    Frame_shots(conn, pl2);
1180 	    Frame_ships(conn, pl2);
1181 	    Frame_radar(conn, pl2);
1182 	    Frame_lose_item_state(pl);
1183 	    debris_end(conn);
1184 	    fastshot_end(conn);
1185 	}
1186 	sound_play_queued(pl2);
1187 	Send_end_of_frame(conn);
1188     }
1189     playback = rplayback;
1190     oldTimeLeft = newTimeLeft;
1191 
1192     Frame_radar_buffer_free();
1193 }
1194 
Set_message(const char * message)1195 void Set_message(const char *message)
1196 {
1197     player_t *pl;
1198     int i;
1199     const char *msg;
1200     char tmp[MSG_LEN];
1201 
1202     if ((i = strlen(message)) >= MSG_LEN) {
1203 	warn("Max message len exceed (%d,%s)", i, message);
1204 	strlcpy(tmp, message, MSG_LEN);
1205 	msg = tmp;
1206     } else
1207 	msg = message;
1208 
1209     teamcup_log("    %s\n", message);
1210 
1211     if (!rplayback || playback)
1212 	for (i = 0; i < NumPlayers; i++) {
1213 	    pl = Player_by_index(i);
1214 	    if (pl->conn != NULL)
1215 		Send_message(pl->conn, msg);
1216 	}
1217     for (i = 0; i < NumSpectators; i++) {
1218 	pl = Player_by_index(i + spectatorStart);
1219 	Send_message(pl->conn, msg);
1220     }
1221 }
1222 
Set_player_message(player_t * pl,const char * message)1223 void Set_player_message(player_t *pl, const char *message)
1224 {
1225     int i;
1226     const char *msg;
1227     char tmp[MSG_LEN];
1228 
1229     if (rplayback && !playback && pl->rectype != 2)
1230 	return;
1231 
1232     if ((i = strlen(message)) >= MSG_LEN) {
1233 	warn("Max message len exceed (%d,%s)", i, message);
1234 	strlcpy(tmp, message, MSG_LEN);
1235 	msg = tmp;
1236     } else
1237 	msg = message;
1238     if (pl->conn != NULL)
1239 	Send_message(pl->conn, msg);
1240     else if (Player_is_robot(pl))
1241 	Robot_message(pl, msg);
1242 }
1243 
Set_message_f(const char * fmt,...)1244 void Set_message_f(const char *fmt, ...)
1245 {
1246     player_t *pl;
1247     int i;
1248     size_t len;
1249     static char msg[2 * MSG_LEN];
1250     va_list ap;
1251 
1252     va_start(ap, fmt);
1253     vsnprintf(msg, sizeof(msg), fmt, ap);
1254     va_end(ap);
1255 
1256     if ((len = strlen(msg)) >= MSG_LEN) {
1257 	warn("Set_message_f: Max len exceeded (%d,\"%s\")", len, msg);
1258 	msg[MSG_LEN - 1] = '\0';
1259 	assert(strlen(msg) < MSG_LEN);
1260     }
1261 
1262     teamcup_log("    %s\n", msg);
1263 
1264     if (!rplayback || playback)
1265 	for (i = 0; i < NumPlayers; i++) {
1266 	    pl = Player_by_index(i);
1267 	    if (pl->conn != NULL)
1268 		Send_message(pl->conn, msg);
1269 	}
1270     for (i = 0; i < NumSpectators; i++) {
1271 	pl = Player_by_index(i + spectatorStart);
1272 	Send_message(pl->conn, msg);
1273     }
1274 }
1275 
Set_player_message_f(player_t * pl,const char * fmt,...)1276 void Set_player_message_f(player_t *pl, const char *fmt, ...)
1277 {
1278     size_t len;
1279     static char msg[2 * MSG_LEN];
1280     va_list ap;
1281 
1282     if (rplayback && !playback && pl->rectype != 2)
1283 	return;
1284 
1285     va_start(ap, fmt);
1286     vsnprintf(msg, sizeof(msg), fmt, ap);
1287     va_end(ap);
1288 
1289     if ((len = strlen(msg)) >= MSG_LEN) {
1290 	warn("Set_player_message_f: Max len exceeded (%d,\"%s\")",
1291 	     len, msg);
1292 	msg[MSG_LEN - 1] = '\0';
1293 	assert(strlen(msg) < MSG_LEN);
1294     }
1295 
1296     if (pl->conn != NULL)
1297 	Send_message(pl->conn, msg);
1298     else if (Player_is_robot(pl))
1299 	Robot_message(pl, msg);
1300 }
1301