/* $Id: client.c,v 5.12 2002/01/17 19:51:16 bertg Exp $ * * XPilot, a multiplayer gravity war game. Copyright (C) 1991-2001 by * * Bjørn Stabell * Ken Ronny Schouten * Bert Gijsbers * Dick Balaska * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #ifndef _WINDOWS # include # ifndef __hpux # include # endif # include #endif #ifdef _WINDOWS # include "NT/winClient.h" #endif #include "version.h" #include "config.h" #include "const.h" #include "setup.h" #include "error.h" #include "rules.h" #include "bit.h" #include "netclient.h" #include "paint.h" #include "xinit.h" #include "protoclient.h" #include "portability.h" #include "talk.h" #include "commonproto.h" char client_version[] = VERSION; #define MAX_CHECKPOINT 26 char *talk_fast_msgs[TALK_FAST_NR_OF_MSGS]; /* talk macros */ int scoresChanged = 0; int RadarHeight = 0; int RadarWidth = 256; /* must always be 256! */ ipos pos; ipos vel; ipos world; ipos realWorld; short heading; short nextCheckPoint; u_byte numItems[NUM_ITEMS]; /* Count of currently owned items */ u_byte lastNumItems[NUM_ITEMS];/* Last item count shown */ int numItemsTime[NUM_ITEMS];/* Number of frames to show this item count */ DFLOAT showItemsTime; /* How long to show changed item count for */ short autopilotLight; int showScoreDecimals; short lock_id; /* Id of player locked onto */ short lock_dir; /* Direction of lock */ short lock_dist; /* Distance to player locked onto */ short selfVisible; /* Are we alive and playing? */ short damaged; /* Damaged by ECM */ short destruct; /* If self destructing */ short shutdown_delay; short shutdown_count; short thrusttime; short thrusttimemax; short shieldtime; short shieldtimemax; short phasingtime; short phasingtimemax; int roundDelay; /* != 0 means we're in a delay */ int roundDelayMax; /* (not yet) used for graph of time remaining in delay */ int map_point_distance; /* spacing of navigation points */ int map_point_size; /* size of navigation points */ int spark_size; /* size of debris and spark */ int shot_size; /* size of shot */ int teamshot_size; /* size of team shot */ bool showNastyShots = false; /* show original flavor shots or the new "nasty shots" */ long control_count; /* Display control for how long? */ u_byte spark_rand; /* Sparkling effect */ u_byte old_spark_rand; /* previous value of spark_rand */ long fuelSum; /* Sum of fuel in all tanks */ long fuelMax; /* How much fuel can you take? */ short fuelCurrent; /* Number of currently used tank */ short numTanks; /* Number of tanks */ long fuelCount; /* Display fuel for how long? */ int fuelLevel1; /* Fuel critical level */ int fuelLevel2; /* Fuel warning level */ int fuelLevel3; /* Fuel notify level */ char *shipShape; /* Shape of player's ship */ DFLOAT power; /* Force of thrust */ DFLOAT power_s; /* Saved power fiks */ DFLOAT turnspeed; /* How fast player acc-turns */ DFLOAT turnspeed_s; /* Saved turnspeed */ DFLOAT turnresistance; /* How much is lost in % */ DFLOAT turnresistance_s; /* Saved (see above) */ DFLOAT displayedPower; /* What the server is sending us */ DFLOAT displayedTurnspeed; /* What the server is sending us */ DFLOAT displayedTurnresistance;/* What the server is sending us */ DFLOAT spark_prob; /* Sparkling effect user configurable */ int charsPerSecond; /* Message output speed (configurable) */ DFLOAT hud_move_fact; /* scale the hud-movement (speed) */ DFLOAT ptr_move_fact; /* scale the speed pointer length */ long instruments; /* Instruments on screen (bitmask) */ char mods[MAX_CHARS]; /* Current modifiers in effect */ int packet_size; /* Current frame update packet size */ int packet_loss; /* lost packets per second */ int packet_drop; /* dropped packets per second */ int packet_lag; /* approximate lag in frames */ char *packet_measure; /* packet measurement in a second */ long packet_loop; /* start of measurement */ bool showRealName = false; /* Show realname instead of nick name */ char name[MAX_CHARS]; /* Nick-name of player */ char realname[MAX_CHARS]; /* Real name of player */ char servername[MAX_CHARS]; /* Name of server connecting to */ unsigned version; /* Version of the server */ int toggle_shield; /* Are shields toggled by a press? */ int shields = 1; /* When shields are considered up */ int auto_shield = 1; /* shield drops for fire */ int maxFPS; /* Client's own FPS */ int oldMaxFPS; int clientPortStart = 0; /* First UDP port for clients */ int clientPortEnd = 0; /* Last one (these are for firewalls) */ u_byte lose_item; /* index for dropping owned item */ int lose_item_active; /* one of the lose keys is pressed */ #ifdef WINDOWSCALING DFLOAT scaleFactor; DFLOAT scaleFactor_s; #endif #ifdef SOUND char sounds[MAX_CHARS]; /* audio mappings */ char audioServer[MAX_CHARS]; /* audio server */ int maxVolume; /* maximum volume (in percent) */ #endif /* SOUND */ static other_t *Others = 0; static int num_others = 0, max_others = 0; static DFLOAT teamscores[MAX_TEAMS]; static fuelstation_t *fuels = 0; static int num_fuels = 0; static homebase_t *bases = 0; static int num_bases = 0; static cannontime_t *cannons = 0; static int num_cannons = 0; static target_t *targets = 0; static int num_targets = 0; static checkpoint_t checks[MAX_CHECKPOINT]; score_object_t score_objects[MAX_SCORE_OBJECTS]; int score_object = 0; #ifndef _WINDOWS /* provide cut&paste and message history */ extern selection_t selection; static char *HistoryBlock = NULL; extern char *HistoryMsg[MAX_HIST_MSGS]; #endif bool selectionAndHistory = false; int maxLinesInHistory; static fuelstation_t *Fuelstation_by_pos(int x, int y) { int i, lo, hi, pos; lo = 0; hi = num_fuels - 1; pos = x * Setup->y + y; while (lo < hi) { i = (lo + hi) >> 1; if (pos > fuels[i].pos) { lo = i + 1; } else { hi = i; } } if (lo == hi && pos == fuels[lo].pos) { return &fuels[lo]; } errno = 0; error("No fuelstation at (%d,%d)", x, y); return NULL; } int Fuel_by_pos(int x, int y) { fuelstation_t *fuelp; if ((fuelp = Fuelstation_by_pos(x, y)) == NULL) { return 0; } return fuelp->fuel; } int Target_by_index(int ind, int *xp, int *yp, int *dead_time, int *damage) { if (ind < 0 || ind >= num_targets) { return -1; } *xp = targets[ind].pos / Setup->y; *yp = targets[ind].pos % Setup->y; *dead_time = targets[ind].dead_time; *damage = targets[ind].damage; return 0; } int Target_alive(int x, int y, int *damage) { int i, lo, hi, pos; lo = 0; hi = num_targets - 1; pos = x * Setup->y + y; while (lo < hi) { i = (lo + hi) >> 1; if (pos > targets[i].pos) { lo = i + 1; } else { hi = i; } } if (lo == hi && pos == targets[lo].pos) { *damage = targets[lo].damage; return targets[lo].dead_time; } errno = 0; error("No targets at (%d,%d)", x, y); return -1; } int Handle_fuel(int ind, int fuel) { if (ind < 0 || ind >= num_fuels) { errno = 0; error("Bad fuelstation index (%d)", ind); return -1; } fuels[ind].fuel = fuel; return 0; } static cannontime_t *Cannon_by_pos(int x, int y) { int i, lo, hi, pos; lo = 0; hi = num_cannons - 1; pos = x * Setup->y + y; while (lo < hi) { i = (lo + hi) >> 1; if (pos > cannons[i].pos) { lo = i + 1; } else { hi = i; } } if (lo == hi && pos == cannons[lo].pos) { return &cannons[lo]; } errno = 0; error("No cannon at (%d,%d)", x, y); return NULL; } int Cannon_dead_time_by_pos(int x, int y, int *dot) { cannontime_t *cannonp; if ((cannonp = Cannon_by_pos(x, y)) == NULL) { return -1; } *dot = cannonp->dot; return cannonp->dead_time; } int Handle_cannon(int ind, int dead_time) { if (ind < 0 || ind >= num_cannons) { errno = 0; error("Bad cannon index (%d)", ind); return 0; } cannons[ind].dead_time = dead_time; return 0; } int Handle_target(int num, int dead_time, int damage) { if (num < 0 || num >= num_targets) { errno = 0; error("Bad target index (%d)", num); return 0; } if (dead_time == 0 && (damage < 1 || damage > TARGET_DAMAGE)) { printf ("BUG target %d, dead %d, damage %d\n", num, dead_time, damage); } if (targets[num].dead_time > 0 && dead_time == 0) { int pos = targets[num].pos; Paint_radar_block(pos / Setup->y, pos % Setup->y, targetRadarColor); } else if (targets[num].dead_time == 0 && dead_time > 0) { int pos = targets[num].pos; Paint_radar_block(pos / Setup->y, pos % Setup->y, BLACK); } targets[num].dead_time = dead_time; targets[num].damage = damage; return 0; } static homebase_t *Homebase_by_pos(int x, int y) { int i, lo, hi, pos; lo = 0; hi = num_bases - 1; pos = x * Setup->y + y; while (lo < hi) { i = (lo + hi) >> 1; if (pos > bases[i].pos) { lo = i + 1; } else { hi = i; } } if (lo == hi && pos == bases[lo].pos) { return &bases[lo]; } errno = 0; error("No homebase at (%d,%d)", x, y); return NULL; } int Base_info_by_pos(int x, int y, int *idp, int *teamp) { homebase_t *basep; if ((basep = Homebase_by_pos(x, y)) == NULL) { return -1; } *idp = basep->id; *teamp = basep->team; return 0; } int Handle_base(int id, int ind) { int i; if (ind < 0 || ind >= num_bases) { errno = 0; error("Bad homebase index (%d)", ind); return -1; } for (i = 0; i < num_bases; i++) { if (bases[i].id == id) { bases[i].id = -1; } } bases[ind].id = id; return 0; } int Check_pos_by_index(int ind, int *xp, int *yp) { if (ind < 0 || ind >= MAX_CHECKPOINT) { errno = 0; error("Bad checkpoint index (%d)", ind); *xp = 0; *yp = 0; return -1; } *xp = checks[ind].pos / Setup->y; *yp = checks[ind].pos % Setup->y; return 0; } int Check_index_by_pos(int x, int y) { int i, pos; pos = x * Setup->y + y; for (i = 0; i < MAX_CHECKPOINT; i++) { if (pos == checks[i].pos) { return i; } } errno = 0; error("Can't find checkpoint (%d,%d)", x, y); return 0; } /* * Convert a `space' map block into a dot. */ static void Map_make_dot(unsigned char *data) { if (*data == SETUP_SPACE) { *data = SETUP_SPACE_DOT; } else if (*data == SETUP_DECOR_FILLED) { *data = SETUP_DECOR_DOT_FILLED; } else if (*data == SETUP_DECOR_RU) { *data = SETUP_DECOR_DOT_RU; } else if (*data == SETUP_DECOR_RD) { *data = SETUP_DECOR_DOT_RD; } else if (*data == SETUP_DECOR_LU) { *data = SETUP_DECOR_DOT_LU; } else if (*data == SETUP_DECOR_LD) { *data = SETUP_DECOR_DOT_LD; } } /* * Optimize the drawing of all blue space dots by converting * certain map objects into a specialised form of their type. */ void Map_dots(void) { int i, x, y, start; unsigned char dot[256]; /* * Lookup table to recognize dots. */ memset(dot, 0, sizeof dot); dot[SETUP_SPACE_DOT] = 1; dot[SETUP_DECOR_DOT_FILLED] = 1; dot[SETUP_DECOR_DOT_RU] = 1; dot[SETUP_DECOR_DOT_RD] = 1; dot[SETUP_DECOR_DOT_LU] = 1; dot[SETUP_DECOR_DOT_LD] = 1; /* * Restore the map to unoptimized form. */ for (i = Setup->x * Setup->y; i-- > 0; ) { if (dot[Setup->map_data[i]]) { if (Setup->map_data[i] == SETUP_SPACE_DOT) { Setup->map_data[i] = SETUP_SPACE; } else if (Setup->map_data[i] == SETUP_DECOR_DOT_FILLED) { Setup->map_data[i] = SETUP_DECOR_FILLED; } else if (Setup->map_data[i] == SETUP_DECOR_DOT_RU) { Setup->map_data[i] = SETUP_DECOR_RU; } else if (Setup->map_data[i] == SETUP_DECOR_DOT_RD) { Setup->map_data[i] = SETUP_DECOR_RD; } else if (Setup->map_data[i] == SETUP_DECOR_DOT_LU) { Setup->map_data[i] = SETUP_DECOR_LU; } else if (Setup->map_data[i] == SETUP_DECOR_DOT_LD) { Setup->map_data[i] = SETUP_DECOR_LD; } } } /* * Lookup table to test for map data which can be turned into a dot. */ memset(dot, 0, sizeof dot); dot[SETUP_SPACE] = 1; if (!BIT(instruments, SHOW_DECOR)) { dot[SETUP_DECOR_FILLED] = 1; dot[SETUP_DECOR_RU] = 1; dot[SETUP_DECOR_RD] = 1; dot[SETUP_DECOR_LU] = 1; dot[SETUP_DECOR_LD] = 1; } /* * Optimize. */ if (map_point_size > 0) { if (BIT(Setup->mode, WRAP_PLAY)) { for (x = 0; x < Setup->x; x++) { if (dot[Setup->map_data[x * Setup->y]]) { Map_make_dot(&Setup->map_data[x * Setup->y]); } } for (y = 0; y < Setup->y; y++) { if (dot[Setup->map_data[y]]) { Map_make_dot(&Setup->map_data[y]); } } start = map_point_distance; } else { start = 0; } if (map_point_distance > 0) { for (x = start; x < Setup->x; x += map_point_distance) { for (y = start; y < Setup->y; y += map_point_distance) { if (dot[Setup->map_data[x * Setup->y + y]]) { Map_make_dot(&Setup->map_data[x * Setup->y + y]); } } } } for (i = 0; i < num_cannons; i++) { x = cannons[i].pos / Setup->y; y = cannons[i].pos % Setup->y; if ((x == 0 || y == 0) && BIT(Setup->mode, WRAP_PLAY)) { cannons[i].dot = 1; } else if (map_point_distance > 0 && x % map_point_distance == 0 && y % map_point_distance == 0) { cannons[i].dot = 1; } else { cannons[i].dot = 0; } } } } /* * Optimize the drawing of all blue map objects by converting * their map type to a bitmask with bits for each blue segment. */ void Map_restore(int startx, int starty, int width, int height) { int i, j, x, y, map_index, type; /* * Restore an optimized map to its original unoptimized state. */ x = startx; for (i = 0; i < width; i++, x++) { if (x < 0) { x += Setup->x; } else if (x >= Setup->x) { x -= Setup->x; } y = starty; for (j = 0; j < height; j++, y++) { if (y < 0) { y += Setup->y; } else if (y >= Setup->y) { y -= Setup->y; } map_index = x * Setup->y + y; type = Setup->map_data[map_index]; if ((type & BLUE_BIT) == 0) { if (type == SETUP_FILLED_NO_DRAW) { Setup->map_data[map_index] = SETUP_FILLED; } } else if ((type & BLUE_FUEL) == BLUE_FUEL) { Setup->map_data[map_index] = SETUP_FUEL; } else if (type & BLUE_OPEN) { if (type & BLUE_BELOW) { Setup->map_data[map_index] = SETUP_REC_RD; } else { Setup->map_data[map_index] = SETUP_REC_LU; } } else if (type & BLUE_CLOSED) { if (type & BLUE_BELOW) { Setup->map_data[map_index] = SETUP_REC_LD; } else { Setup->map_data[map_index] = SETUP_REC_RU; } } else { Setup->map_data[map_index] = SETUP_FILLED; } } } } void Map_blue(int startx, int starty, int width, int height) { int i, j, x, y, map_index, type, newtype; unsigned char blue[256]; const long outline_mask = SHOW_OUTLINE_WORLD | SHOW_FILLED_WORLD | SHOW_TEXTURED_WALLS; /* * Optimize the map for blue. */ memset(blue, 0, sizeof blue); blue[SETUP_FILLED] = BLUE_LEFT | BLUE_UP | BLUE_RIGHT | BLUE_DOWN; blue[SETUP_FILLED_NO_DRAW] = blue[SETUP_FILLED]; blue[SETUP_FUEL] = blue[SETUP_FILLED]; blue[SETUP_REC_RU] = BLUE_RIGHT | BLUE_UP; blue[SETUP_REC_RD] = BLUE_RIGHT | BLUE_DOWN; blue[SETUP_REC_LU] = BLUE_LEFT | BLUE_UP; blue[SETUP_REC_LD] = BLUE_LEFT | BLUE_DOWN; blue[BLUE_BIT|BLUE_OPEN] = blue[BLUE_BIT|BLUE_OPEN|BLUE_LEFT] = blue[BLUE_BIT|BLUE_OPEN|BLUE_UP] = blue[BLUE_BIT|BLUE_OPEN|BLUE_LEFT|BLUE_UP] = blue[SETUP_REC_LU]; blue[BLUE_BIT|BLUE_OPEN|BLUE_BELOW] = blue[BLUE_BIT|BLUE_OPEN|BLUE_BELOW|BLUE_RIGHT] = blue[BLUE_BIT|BLUE_OPEN|BLUE_BELOW|BLUE_DOWN] = blue[BLUE_BIT|BLUE_OPEN|BLUE_BELOW|BLUE_RIGHT|BLUE_DOWN] = blue[SETUP_REC_RD]; blue[BLUE_BIT|BLUE_CLOSED] = blue[BLUE_BIT|BLUE_CLOSED|BLUE_RIGHT] = blue[BLUE_BIT|BLUE_CLOSED|BLUE_UP] = blue[BLUE_BIT|BLUE_CLOSED|BLUE_RIGHT|BLUE_UP] = blue[SETUP_REC_RU]; blue[BLUE_BIT|BLUE_CLOSED|BLUE_BELOW] = blue[BLUE_BIT|BLUE_CLOSED|BLUE_BELOW|BLUE_LEFT] = blue[BLUE_BIT|BLUE_CLOSED|BLUE_BELOW|BLUE_DOWN] = blue[BLUE_BIT|BLUE_CLOSED|BLUE_BELOW|BLUE_LEFT|BLUE_DOWN] = blue[SETUP_REC_LD]; for (i = BLUE_BIT; i < sizeof blue; i++) { if ((i & BLUE_FUEL) == BLUE_FUEL || (i & (BLUE_OPEN|BLUE_CLOSED)) == 0) { blue[i] = blue[SETUP_FILLED]; } } x = startx; for (i = 0; i < width; i++, x++) { if (x < 0) { x += Setup->x; } else if (x >= Setup->x) { x -= Setup->x; } y = starty; for (j = 0; j < height; j++, y++) { if (y < 0) { y += Setup->y; } else if (y >= Setup->y) { y -= Setup->y; } map_index = x * Setup->y + y; type = Setup->map_data[map_index]; newtype = 0; switch (type) { case SETUP_FILLED: case SETUP_FILLED_NO_DRAW: case SETUP_FUEL: newtype = BLUE_BIT; if (type == SETUP_FUEL) { newtype |= BLUE_FUEL; } if ((x == 0) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[(Setup->x - 1) * Setup->y + y]] & BLUE_RIGHT)) : !(blue[Setup->map_data[(x - 1) * Setup->y + y]] & BLUE_RIGHT)) newtype |= BLUE_LEFT; if ((y == 0) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[x * Setup->y + Setup->y - 1]] & BLUE_UP)) : !(blue[Setup->map_data[x * Setup->y + (y - 1)]] & BLUE_UP)) newtype |= BLUE_DOWN; if (!BIT(instruments, outline_mask) || ((x == Setup->x - 1) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[y]] & BLUE_LEFT)) : !(blue[Setup->map_data[(x + 1) * Setup->y + y]] & BLUE_LEFT))) newtype |= BLUE_RIGHT; if (!BIT(instruments, outline_mask) || ((y == Setup->y - 1) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[x * Setup->y]] & BLUE_DOWN)) : !(blue[Setup->map_data[x * Setup->y + (y + 1)]] & BLUE_DOWN))) newtype |= BLUE_UP; break; case SETUP_REC_LU: newtype = BLUE_BIT | BLUE_OPEN; if (x == 0 ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[(Setup->x - 1) * Setup->y + y]] & BLUE_RIGHT)) : !(blue[Setup->map_data[(x - 1) * Setup->y + y]] & BLUE_RIGHT)) newtype |= BLUE_LEFT; if (!BIT(instruments, outline_mask) || ((y == Setup->y - 1) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[x * Setup->y]] & BLUE_DOWN)) : !(blue[Setup->map_data[x * Setup->y + (y + 1)]] & BLUE_DOWN))) newtype |= BLUE_UP; break; case SETUP_REC_RU: newtype = BLUE_BIT | BLUE_CLOSED; if (!BIT(instruments, outline_mask) || ((x == Setup->x - 1) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[y]] & BLUE_LEFT)) : !(blue[Setup->map_data[(x + 1) * Setup->y + y]] & BLUE_LEFT))) newtype |= BLUE_RIGHT; if (!BIT(instruments, outline_mask) || ((y == Setup->y - 1) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[x * Setup->y]] & BLUE_DOWN)) : !(blue[Setup->map_data[x * Setup->y + (y + 1)]] & BLUE_DOWN))) newtype |= BLUE_UP; break; case SETUP_REC_LD: newtype = BLUE_BIT | BLUE_BELOW | BLUE_CLOSED; if ((x == 0) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[(Setup->x - 1) * Setup->y + y]] & BLUE_RIGHT)) : !(blue[Setup->map_data[(x - 1) * Setup->y + y]] & BLUE_RIGHT)) newtype |= BLUE_LEFT; if ((y == 0) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[x * Setup->y + Setup->y - 1]] & BLUE_UP)) : !(blue[Setup->map_data[x * Setup->y + (y - 1)]] & BLUE_UP)) newtype |= BLUE_DOWN; break; case SETUP_REC_RD: newtype = BLUE_BIT | BLUE_BELOW | BLUE_OPEN; if (!BIT(instruments, outline_mask) || ((x == Setup->x - 1) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[y]] & BLUE_LEFT)) : !(blue[Setup->map_data[(x + 1) * Setup->y + y]] & BLUE_LEFT))) newtype |= BLUE_RIGHT; if ((y == 0) ? (!BIT(Setup->mode, WRAP_PLAY) || !(blue[Setup->map_data[x * Setup->y + Setup->y - 1]] & BLUE_UP)) : !(blue[Setup->map_data[x * Setup->y + (y - 1)]] & BLUE_UP)) newtype |= BLUE_DOWN; break; default: continue; } if (newtype != 0) { if (newtype == BLUE_BIT) { newtype = SETUP_FILLED_NO_DRAW; } Setup->map_data[map_index] = newtype; } } } } static int Map_init(void) { int i, max, type; u_byte types[256]; num_fuels = 0; num_bases = 0; num_cannons = 0; num_targets = 0; fuels = NULL; bases = NULL; cannons = NULL; targets = NULL; memset(types, 0, sizeof types); types[SETUP_FUEL] = 1; types[SETUP_CANNON_UP] = 2; types[SETUP_CANNON_RIGHT] = 2; types[SETUP_CANNON_DOWN] = 2; types[SETUP_CANNON_LEFT] = 2; for (i = SETUP_TARGET; i < SETUP_TARGET + 10; i++) { types[i] = 3; } for (i = SETUP_BASE_LOWEST; i <= SETUP_BASE_HIGHEST; i++) { types[i] = 4; } max = Setup->x * Setup->y; for (i = 0; i < max; i++) { switch (types[Setup->map_data[i]]) { case 1: num_fuels++; break; case 2: num_cannons++; break; case 3: num_targets++; break; case 4: num_bases++; break; } } if (num_bases != 0) { bases = (homebase_t *) malloc(num_bases * sizeof(homebase_t)); if (bases == NULL) { error("No memory for Map bases (%d)", num_bases); return -1; } num_bases = 0; } if (num_fuels != 0) { fuels = (fuelstation_t *) malloc(num_fuels * sizeof(fuelstation_t)); if (fuels == NULL) { error("No memory for Map fuels (%d)", num_fuels); return -1; } num_fuels = 0; } if (num_targets != 0) { targets = (target_t *) malloc(num_targets * sizeof(target_t)); if (targets == NULL) { error("No memory for Map targets (%d)", num_targets); return -1; } num_targets = 0; } if (num_cannons != 0) { cannons = (cannontime_t *) malloc(num_cannons * sizeof(cannontime_t)); if (cannons == NULL) { error("No memory for Map cannons (%d)", num_cannons); return -1; } num_cannons = 0; } for (i = 0; i < MAX_CHECKPOINT; i++) { types[SETUP_CHECK + i] = 5; } for (i = 0; i < max; i++) { type = Setup->map_data[i]; switch (types[type]) { case 1: fuels[num_fuels].pos = i; fuels[num_fuels].fuel = MAX_STATION_FUEL; num_fuels++; break; case 2: cannons[num_cannons].pos = i; cannons[num_cannons].dead_time = 0; cannons[num_cannons].dot = 0; num_cannons++; break; case 3: targets[num_targets].pos = i; targets[num_targets].dead_time = 0; targets[num_targets].damage = TARGET_DAMAGE; num_targets++; break; case 4: bases[num_bases].pos = i; bases[num_bases].id = -1; bases[num_bases].team = type % 10; num_bases++; Setup->map_data[i] = type - (type % 10); break; case 5: checks[type - SETUP_CHECK].pos = i; Setup->map_data[i] = SETUP_CHECK; break; } } return 0; } static int Map_cleanup(void) { if (num_bases > 0) { if (bases != NULL) { free(bases); bases = NULL; } num_bases = 0; } if (num_fuels > 0) { if (fuels != NULL) { free(fuels); fuels = NULL; } num_fuels = 0; } if (num_targets > 0) { if (targets != NULL) { free(targets); targets = NULL; } num_targets = 0; } if (num_cannons > 0) { if (cannons != NULL) { free(cannons); cannons = NULL; } num_cannons = 0; } return 0; } other_t *Other_by_id(int id) { int i; if (id != -1) { for (i = 0; i < num_others; i++) { if (Others[i].id == id) { return &Others[i]; } } } return NULL; } shipobj *Ship_by_id(int id) { other_t *other; if ((other = Other_by_id(id)) == NULL) { return Parse_shape_str(NULL); } return other->ship; } int Handle_leave(int id) { other_t *other; int i; char msg[MSG_LEN]; if ((other = Other_by_id(id)) != NULL) { if (other == self) { errno = 0; error("Self left?!"); self = NULL; } Free_ship_shape(other->ship); other->ship = NULL; /* * Silent about tanks and robots. */ if (other->mychar != 'T' && other->mychar != 'R') { sprintf(msg, "%s left this world.", other->name); Add_message(msg); } num_others--; while (other < &Others[num_others]) { *other = other[1]; other++; } scoresChanged = 1; } for (i = 0; i < num_others; i++) { other = &Others[i]; if (other->war_id == id) { other->war_id = -1; scoresChanged = 1; } } return 0; } int Handle_player(int id, int player_team, int mychar, char *player_name, char *real_name, char *host_name, char *shape) { other_t *other; if ((other = Other_by_id(id)) == NULL) { if (num_others >= max_others) { max_others += 5; if (num_others == 0) { Others = (other_t *) malloc(max_others * sizeof(other_t)); } else { Others = (other_t *) realloc(Others, max_others * sizeof(other_t)); } if (Others == NULL) { error("Not enough memory for player info"); num_others = max_others = 0; self = NULL; return -1; } if (self != NULL) { /* * We've made `self' the first member of Others[]. */ self = &Others[0]; } } other = &Others[num_others++]; } if (self == NULL && strcmp(name, player_name) == 0) { if (other != &Others[0]) { /* * Make `self' the first member of Others[]. */ *other = Others[0]; other = &Others[0]; } self = other; team = player_team; } other->id = id; other->team = player_team; other->score = 0; other->round = 0; other->check = 0; other->timing = 0; other->life = 0; other->mychar = mychar; other->war_id = -1; other->name_width = 0; strlcpy(other->name, player_name, sizeof(other->name)); strlcpy(other->real, real_name, sizeof(other->real)); strlcpy(other->host, host_name, sizeof(other->host)); scoresChanged = 1; other->ship = Convert_shape_str(shape); Calculate_shield_radius(other->ship); return 0; } int Handle_war(int robot_id, int killer_id) { other_t *robot, *killer; char msg[MSG_LEN]; if ((robot = Other_by_id(robot_id)) == NULL) { errno = 0; IFNWINDOWS(error("Can't update war for non-existing player (%d,%d)", robot_id, killer_id);) return 0; } if (killer_id == -1) { /* * Robot is no longer in war mode. */ robot->war_id = -1; return 0; } if ((killer = Other_by_id(killer_id)) == NULL) { errno = 0; IFNWINDOWS(error("Can't update war against non-existing player (%d,%d)", robot_id, killer_id);) return 0; } robot->war_id = killer_id; sprintf(msg, "%s declares war on %s.", robot->name, killer->name); Add_message(msg); scoresChanged = 1; return 0; } int Handle_seek(int programmer_id, int robot_id, int sought_id) { other_t *programmer, *robot, *sought; char msg[MSG_LEN]; if ((programmer = Other_by_id(programmer_id)) == NULL || (robot = Other_by_id(robot_id)) == NULL || (sought = Other_by_id(sought_id)) == NULL) { errno = 0; error("Bad player seek (%d,%d,%d)", programmer_id, robot_id, sought_id); return 0; } robot->war_id = sought_id; sprintf(msg, "%s has programmed %s to seek %s.", programmer->name, robot->name, sought->name); Add_message(msg); scoresChanged = 1; return 0; } int Handle_score(int id, DFLOAT score, int life, int mychar, int alliance) { other_t *other; if ((other = Other_by_id(id)) == NULL) { #ifndef _WINDOWS errno = 0; error("Can't update score for non-existing player %d,%.2f,%d", id, score, life); #endif return 0; } else if (other->score != score || other->life != life || other->mychar != mychar || other->alliance != alliance) { other->score = score; other->life = life; other->mychar = mychar; other->alliance = alliance; scoresChanged = 1; } return 0; } int Handle_team_score(int team, DFLOAT score) { if (teamscores[team] != score) { teamscores[team] = score; scoresChanged = 1; } return 0; } int Handle_timing(int id, int check, int round) { other_t *other; if ((other = Other_by_id(id)) == NULL) { errno = 0; error("Can't update timing for non-existing player %d,%d,%d", id, check, round); return 0; } else if (other->check != check || other->round != round) { other->check = check; other->round = round; other->timing = round * MAX_CHECKS + check; other->timing_loops = last_loops; scoresChanged = 1; } return 0; } int Handle_score_object(DFLOAT score, int x, int y, char *msg) { score_object_t* sobj = &score_objects[score_object]; sobj->score = score; sobj->x = x; sobj->y = y; sobj->count = 1; /* Initialize sobj->hud_msg (is shown on the HUD) */ if (msg[0] != '\0') { if (showScoreDecimals > 0 && version >= 0x4500) { sprintf(sobj->hud_msg, "%s %.*f", msg, showScoreDecimals, score); } else { sprintf(sobj->hud_msg, "%s %d", msg, (int) rint(score)); } sobj->hud_msg_len = strlen(sobj->hud_msg); sobj->hud_msg_width = XTextWidth(gameFont, sobj->hud_msg, sobj->hud_msg_len); } else sobj->hud_msg_len = 0; /* Initialize sobj->msg data (is shown on game area) */ if (showScoreDecimals > 0 && version >= 0x4500) { sprintf(sobj->msg, "%.*f", showScoreDecimals, score); } else { sprintf(sobj->msg, "%d", (int) rint(score)); } sobj->msg_len = strlen(sobj->msg); sobj->msg_width = XTextWidth(gameFont, sobj->msg, sobj->msg_len); /* Update global index variable */ score_object = (score_object + 1) % MAX_SCORE_OBJECTS; return 0; } void Client_score_table(void) { struct team_score { DFLOAT score; int life; int playing; }; struct team_score team[MAX_TEAMS], *team_order[MAX_TEAMS]; other_t *other, **order; int i, j, k, best = -1; DFLOAT ratio, best_ratio = -1e7; if (scoresChanged == 0) { return; } if (players_exposed == false) { return; } if (num_others < 1) { Paint_score_start(); scoresChanged = 0; return; } if ((order = (other_t **)malloc(num_others * sizeof(other_t *))) == NULL) { error("No memory for score"); return; } if (BIT(Setup->mode, TEAM_PLAY|TIMING) == TEAM_PLAY) { memset(&team[0], 0, sizeof team); } for (i = 0; i < num_others; i++) { other = &Others[i]; if (BIT(Setup->mode, TIMING)) { /* * Sort the score table on position in race. * Put paused and waiting players last as well as tanks. */ if (strchr("PTW", other->mychar)) { j = i; } else { for (j = 0; j < i; j++) { if (order[j]->timing < other->timing) { break; } if (strchr("PTW", order[j]->mychar)) { break; } if (order[j]->timing == other->timing) { if (order[j]->timing_loops > other->timing_loops) { break; } } } } } else { if (BIT(Setup->mode, LIMITED_LIVES)) { ratio = other->score; } else { ratio = other->score / (other->life + 1); } if (best == -1 || ratio > best_ratio) { best_ratio = ratio; best = i; } for (j = 0; j < i; j++) { if (order[j]->score < other->score) { break; } } } for (k = i; k > j; k--) { order[k] = order[k - 1]; } order[j] = other; if (BIT(Setup->mode, TEAM_PLAY|TIMING) == TEAM_PLAY) { switch (other->mychar) { case 'P': case 'W': case 'T': break; case ' ': case 'R': if (BIT(Setup->mode, LIMITED_LIVES)) { team[other->team].life += other->life + 1; } else { team[other->team].life += other->life; } /*FALLTHROUGH*/ default: team[other->team].playing++; if (version < 0x4500) { team[other->team].score += other->score; } break; } if (version >= 0x4500) { team[other->team].score = teamscores[other->team]; } } } Paint_score_start(); if (BIT(Setup->mode, TIMING)) { best = order[0] - Others; } for (i = 0; i < num_others; i++) { other = order[i]; j = other - Others; Paint_score_entry(i, other, (j == best) ? true : false); } if (BIT(Setup->mode, TEAM_PLAY|TIMING) == TEAM_PLAY) { int pos = num_others + 1; int num_playing_teams = 0; for (i = 0; i < MAX_TEAMS; i++) { if (team[i].playing) { for (j = 0; j < num_playing_teams; j++) { if (team[i].score > team_order[j]->score || (team[i].score == team_order[j]->score && ((BIT(Setup->mode, LIMITED_LIVES)) ? (team[i].life > team_order[j]->life) : (team[i].life < team_order[j]->life)))) { for (k = i; k > j; k--) { team_order[k] = team_order[k - 1]; } break; } } team_order[j] = &team[i]; num_playing_teams++; } } for (i = 0; i < num_playing_teams; i++) { other_t tmp; tmp.id = -1; tmp.team = team_order[i] - &team[0]; tmp.war_id = -1; tmp.name_width = 0; tmp.ship = NULL; sprintf(tmp.name, "Team %d", tmp.team); strcpy(tmp.real, tmp.name); strcpy(tmp.host, ""); if (BIT(Setup->mode, LIMITED_LIVES) && team_order[i]->life == 0) { tmp.mychar = 'D'; } else { tmp.mychar = ' '; } tmp.score = team_order[i]->score; tmp.life = team_order[i]->life; Paint_score_entry(pos++, &tmp, false); } } free(order); IFWINDOWS( MarkPlayersForRedraw(); ) scoresChanged = 0; } #ifndef _WINDOWS static int Alloc_history(void) { char *hist_ptr; int i; /* maxLinesInHistory is a runtime constant */ if ((hist_ptr = (char *)malloc(maxLinesInHistory * MAX_CHARS)) == NULL) { error("No memory for history"); return -1; } HistoryBlock = hist_ptr; for (i = 0; i < maxLinesInHistory; i++) { HistoryMsg[i] = hist_ptr; hist_ptr[0] = '\0'; hist_ptr += MAX_CHARS; } return 0; } static void Free_selectionAndHistory(void) { if (HistoryBlock) { free(HistoryBlock); HistoryBlock = NULL; } if (selection.txt) { free(selection.txt); selection.txt = NULL; } } #else static int Alloc_history(void) { return 0; } static void Free_selectionAndHistory(void) { } #endif int Client_init(char *server, unsigned server_version) { version = server_version; Make_table(); #ifdef WINDOWSCALING Init_scale_array(); #endif if ( Init_wreckage() == -1 ) { return -1; } if (Init_asteroids() == -1) { return -1; } strlcpy(servername, server, sizeof(servername)); return 0; } int Client_setup(void) { if (Map_init() == -1) { return -1; } Map_dots(); Map_restore(0, 0, Setup->x, Setup->y); Map_blue(0, 0, Setup->x, Setup->y); RadarHeight = (RadarWidth * Setup->y) / Setup->x; if (Init_playing_windows() == -1) { return -1; } if (Alloc_msgs() == -1) { return -1; } if (Alloc_history() == -1) { return -1; } /* Old servers can't deal with 0.0 turnresistance, so swap to * the alternate bank, and hope there's something better there. */ /* HACK: Hanging Gardens runs an old server (version code 0x4101) * which happens to have the turnresistance patch. */ if (turnresistance == 0.0 && version < 0x4200 && version != 0x4101) { DFLOAT tmp; #define SWAP(a,b) (tmp = (a), (a) = (b), (b) = tmp) SWAP(power, power_s); SWAP(turnspeed, turnspeed_s); SWAP(turnresistance, turnresistance_s); #undef SWAP control_count = CONTROL_DELAY; Add_message("Old server can't handle turnResistance=0.0; " "swapping to alternate settings [*Client message*]"); } return 0; } int Client_fps_request(void) { LIMIT(maxFPS, FPS / 2, FPS); oldMaxFPS = maxFPS; return Send_fps_request(maxFPS); } int Client_power(void) { int i; if (Send_power(power) == -1 || Send_power_s(power_s) == -1 || Send_turnspeed(turnspeed) == -1 || Send_turnspeed_s(turnspeed_s) == -1 || Send_turnresistance(turnresistance) == -1 || Send_turnresistance_s(turnresistance_s) == -1 || Send_display() == -1 || Startup_server_motd() == -1) { return -1; } for (i = 0; i < NUM_MODBANKS; i++) { if (Send_modifier_bank(i) == -1) { return -1; } } return 0; } int Client_start(void) { Key_init(); return 0; } void Client_cleanup(void) { int i; Quit(); Free_selectionAndHistory(); if (max_others > 0) { for (i = 0; i < num_others; i++) { other_t* other = &Others[i]; Free_ship_shape(other->ship); } free(Others); num_others = 0; max_others = 0; } Map_cleanup(); } int Client_fd(void) { return ConnectionNumber(dpy); } int Client_input(int new_input) { #ifndef _WINDOWS return x_event(new_input); #else return 0; #endif } void Client_flush(void) { XFlush(dpy); } void Client_sync(void) { XSync(dpy, False); } int Client_wrap_mode(void) { return (BIT(Setup->mode, WRAP_PLAY) != 0); } int Check_client_fps(void) { if (oldMaxFPS != maxFPS) { LIMIT(maxFPS, FPS / 2, FPS); oldMaxFPS = maxFPS; return Send_fps_request(maxFPS); } return 0; }