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