/* * Copyright (C) 1999 Peter Amstutz * * 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., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include "tcpcore.h" #include "relay.h" #include "terrain.h" #include "gfx.h" #include "sky.h" #include "game.h" #include "player.h" #include "packets.h" #include "ballistics.h" #include "text.h" #include "kclient.h" #include "log.h" #include "clihandlers.h" ScrollWindow_txt *ch_scrlwin; ScrollWindow_txt *ch_ingmmsg; ScrollWindow_txt *ch_postgmsg; InputBox_txt *ch_inpbox; InputBox_txt *ch_postinpbox; InputBox_txt *ch_ingameinpbox; char ch_weaponbuylock = 0; char ch_gotallterrain = 0; /* sets game mode from the server */ void chSetGameMode(Relay_rl * rl, int id, char *pkt, int pktlen) { struct SetGameMode_pkt sgm; pktUnpackSetGameMode(&sgm, pkt); gm_gamemode = sgm.gamemode; } /* sets game type from the server */ void chSetGameType(Relay_rl* rl, int id, char *pkt, int pktlen) { struct SetGameMode_pkt sgm; pktUnpackSetGameMode(&sgm, pkt); gm_gametype=sgm.gamemode; } /* gets this clients game mode from the server */ void chGetMyID(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt pid; pktUnpackPlayerID(&pid, pkt); gm_myid = pid.id; } /* adds a new player that has just logged on to the local player list */ void chNewPlayer(Relay_rl * rl, int id, char *pkt, int pktlen) { struct NewPlayer_pkt nppkt; Player_pl *pl; pktUnpackNewPlayer(&nppkt, pkt); if(plLookupPlayer(nppkt.id)) /* yes, this can happen */ return; pl = plCreatePlayer(); pl->itemstock->activate = clFireWeapon; pl->id = nppkt.id; pl->ready = nppkt.ready; pl->name = strdup(nppkt.name); pl->tankcolor = nppkt.color; pl->score = 0; pl->roundScore = 0; if(gm_gamemode == PREGAME) gfxDrawPlayerList(); } /* removes a player from the local list that has left */ void chRemovePlayer(Relay_rl * rl, int id, char *pkt, int pktlen) { Player_pl *pcur; struct PlayerID_pkt pid; pktUnpackPlayerID(&pid, pkt); pcur = plLookupPlayer(pid.id); if(!pcur) /* not logged in yet */ return; if(gm_gamemode == INGAME && pcur->ready == READY) { gfxEraseTank(pcur, 0); gfxUpdate(); if(!gm_stuff_happening) { register struct Projectilelist_bal *prj; for(prj = bal_Projectiles; prj; prj = prj->next) { if(prj->prjpos.id == pid.id) { prj->stat = FREEING; } } } } if(pl_begin == pcur) pl_begin = pcur->next; if(pl_end == pcur) pl_end = pcur->prev; if(pcur->prev) pcur->prev->next = pcur->next; if(pcur->next) pcur->next->prev = pcur->prev; free(pcur); if(gm_gamemode == PREGAME) gfxDrawPlayerList(); } /* reads in new terrain data from the server */ void chUpdateTerrain(Relay_rl * rl, int id, char *pkt, int pktlen) { TerrainSpans_ter *tmp = NULL; unsigned int i, n; struct UpdateTerrain_pkt inpkt; int sx, sy, sw, sh; pktUnpackUpdateTerrain(&inpkt, pkt); for(i = 0, n = inpkt.startpos; i < inpkt.length; i += 2) { if(inpkt.ter[i] == 0) tmp = &ter_data[n++]; terAddSpan(tmp, inpkt.ter[i], inpkt.ter[i + 1]); } sx = gfxTerrainToScreenXCoord(inpkt.startpos); sy = 0; sw = gfxScaleTerrainToScreenXDimen(n - inpkt.startpos); sh = gfx_ysize; gfxDrawTerrain(sx, sy, sw, sh, inpkt.startpos, 0, (n - inpkt.startpos), ter_sizey, ter_data); gfxDrawWalls(sx, sy, sw, sh); gfxUpdate(); if(n == ter_sizex) ch_gotallterrain = 1; } /* server has told the clients to request new terrain data. client acks by formally requesting the new terrain data (handled above) */ void chNewTerrain(Relay_rl * rl, int id, char *pkt, int pktlen) { unsigned i; struct TerrainInfo_pkt ti; pktUnpackTerrainInfo(&ti, pkt); ter_sizex = ti.sizex; ter_sizey = ti.sizey; bal_lerp_tweak = ((double) ti.lerp_tweak) / ((double) 0xFFFF); bal_grav = ((double) ti.grav) / ((double) 0xFFFF); skyGen(0); skyDraw(gfx_xoff, gfx_yoff, gfx_xsize, gfx_ysize); gfxDrawWalls(0, 0, gfx_xmax, gfx_ymax); gfxUpdate(); for(i = 0; i < ter_sizex; i++) terFreeCol(ter_data[i].nexthigher); memset(ter_data, 0, sizeof(ter_data)); rlSend(rl, id, "GT", 2); } /* general purpose system messages / chatting */ void chMessage(Relay_rl * rl, int id, char *pkt, int pktlen) { struct Message_pkt mspkt; struct ColoredMessage_pkt mcpkt; if(pkt[1] == 'S') { pktUnpackMessage(&mspkt, pkt); mcpkt.color = server_color; /* blank */ strcpy(mcpkt.message, mspkt.message); } else if(pkt[1] == 'C') pktUnpackColoredMessage(&mcpkt, pkt); logPrintf(INTERESTING, "%s\n", mcpkt.message); if(gm_gamemode == PREGAME) { ggiSetGCForeground(gfx_vis, gfx_tankcolor[mcpkt.color]); txtScrollWindowPrintf(ch_scrlwin,"%s", mcpkt.message); } else if(gm_gamemode == INGAME) { if(gm_chatlines == 0) ch_ingmmsg->currow = 0; gfxDrawArea(gfxScreenToTerrainXCoord(ch_ingmmsg->x), gfxScreenToTerrainYCoord(ch_ingmmsg->y) - gfxScaleScreenToTerrainYDimen(ch_ingmmsg->h), gfxScaleScreenToTerrainXDimen(ch_ingmmsg->w), gfxScaleScreenToTerrainYDimen(ch_ingmmsg->h)); ggiSetGCForeground(gfx_vis, gfx_tankcolor[mcpkt.color]); txtScrollWindowPrintf(ch_ingmmsg, "%s", mcpkt.message); } else if(gm_gamemode == POSTGAME) { ggiSetGCForeground(gfx_vis, gfx_tankcolor[mcpkt.color]); txtScrollWindowPrintf(ch_postgmsg, "%s", mcpkt.message); } gfxUpdate(); } /* Sets some player's name. */ void chSetName(Relay_rl * rl, int id, char *pkt, int pktlen) { struct SetName_pkt snpkt; Player_pl *pcur; pktUnpackSetName(&snpkt, pkt); pcur = plLookupPlayer(snpkt.id); if(!pcur) /* We have not logged in yet */ return; if(pcur->name) free(pcur->name); pcur->name = strdup(snpkt.name); if(gm_gamemode == PREGAME) gfxDrawPlayerList(); } /* client lost connection to the server */ void chQuit(Relay_rl * rl, int id) { logPrintf(CRITICAL, "Server disconnected\n"); gm_quit = 1; } /* sets some tank's position */ void chSetTank(Relay_rl * rl, int id, char *pkt, int pktlen) { Player_pl *pcur; struct SetTank_pkt stpkt; pktUnpackSetTank(&stpkt, pkt); pcur = plLookupPlayer(stpkt.id); assert(pcur); if(stpkt.type[1] == 'D') { logPrintf(DEBUG, "--- %s\n", pcur->name); if(pcur->x != stpkt.x) { logPrintf(DEBUG, "x skew detected\n"); logPrintf(DEBUG, "was %i, server says %i\n", pcur->x, stpkt.x); } if(pcur->y != stpkt.y) { logPrintf(DEBUG, "y skew detected\n"); logPrintf(DEBUG, "was %i, server says %i\n", pcur->y, stpkt.y); } if(pcur->armor != stpkt.armor) { logPrintf(DEBUG, "armor skew detected\n"); logPrintf(DEBUG, "was %i, server says %i\n", pcur->armor, stpkt.armor); } } pcur->vy = 0.0; pcur->ox = pcur->x; pcur->oy = pcur->y; pcur->x = stpkt.x; pcur->y = stpkt.y; if(stpkt.type[1] == 'T') { pcur->fire_angle = stpkt.a; pcur->fire_velocity = stpkt.v; pcur->ox = pcur->x; pcur->oy = pcur->y; } pcur->barreloff_x = pcur->fire_angle < 90 ? pcur->barreloff_right : pcur->barreloff_left; pcur->armor = stpkt.armor; gfxDrawTank(pcur); gfxUpdate(); if(stpkt.id == gm_myid) gm_tank_damaged = 1; } /* a shot has been fired; add this shot to the list of projectiles in the air (balNewShot()) */ void chShotFired(Relay_rl * rl, int id, char *pkt, int pktlen) { Player_pl *pl; struct FireCmd_pkt sht; Weapon_wep *wp; pktUnpackFireCmd(&sht, pkt); wp = wepLookupWeapon(sht.shottype); if(wp) { pl = plLookupPlayer(sht.id); if(!pl) /* not logged in yet */ return; pl->fire_angle = sht.a; pl->barreloff_x = pl->fire_angle < 90 ? pl->barreloff_right : pl->barreloff_left; pl->fire_velocity = sht.v; pl->fired = 1; clDrawStatus(SU_PLAYER); gfxDrawTank(pl); logPrintf(DEBUG, "Calling balNewShotAV: pl->x=%i; pl->barreloff_x=%i; pl->y=%i; pl->barreloff_y=%i, pl_barrelen=%i\n", pl->x, pl->barreloff_x, pl->y, pl->barreloff_y, pl_barrelen); balNewShotAV(sht.id, sht.gen, pl->x + pl->barreloff_x + pl_barrelen * cos((pl->fire_angle / 180.0) * M_PI), pl->y + pl->barreloff_y + pl_barrelen * sin((pl->fire_angle / 180.0) * M_PI), pl->fire_angle, pl->fire_velocity, wp); if(sht.id == gm_myid) { if(plUseWeaponInStock(plLookupPlayer(gm_myid), wp, 1) <= 1) { for(gm_curitem = gm_curitem->next; gm_curitem->count <= 0; gm_curitem = gm_curitem->next) ; } } clDrawStatus(SU_WEAPON); } } /* changes the readiness of some tank in the local player list */ void chSetReadiness(Relay_rl * rl, int id, char *pkt, int pktlen) { struct ChangeReady_pkt chpkt; Player_pl *pl; pktUnpackChangeReady(&chpkt, pkt); pl = plLookupPlayer(chpkt.id); if(!pl) /* We have not logged in yet */ return; pl->ready = chpkt.r; if(gm_gamemode == PREGAME) { gfxDrawPlayerList(); } } void chActivateShots(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt gen; pktUnpackPlayerID(&gen, pkt); gm_AS_queue[gm_AS_pos++] = gen.id; } void chBuyWeapon(Relay_rl * rl, int id, char *pkt, int pktlen) { struct BuyWeapon_pkt bw; pktUnpackBuyWeapon(&bw, pkt); if(bw.count > 0) plBuyWeapon(gm_myid, bw.weapontype, bw.count, clFireWeapon); ch_weaponbuylock = 0; } void chSellWeapon(Relay_rl * rl, int id, char *pkt, int pktlen) { struct BuyWeapon_pkt bw; pktUnpackBuyWeapon(&bw, pkt); if(bw.count > 0) plSellWeapon(gm_myid, bw.weapontype, bw.count); ch_weaponbuylock = 0; } void chSetMoney(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt pid; Player_pl *pl; pktUnpackPlayerID(&pid, pkt); pl = plLookupPlayer(gm_myid); pl->money = pid.id; if(gm_gamemode == PREGAME) { ggiSetGCForeground(gfx_vis, 0); ggiDrawBox(gfx_vis, 5, 40, 240, 20); ggiSetGCForeground(gfx_vis, gfx_white); txtPrintf(5, 40, "$%i : ROUND %d of %d", pl->money, gm_currentRound, gm_totalRounds); gfxUpdate(); } } void chCheckProtocolVersion(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt pid; pktUnpackPlayerID(&pid, pkt); if(pid.id != PROTOCOL_VERSION) { logPrintf(CRITICAL, "Error! Server is using version %i protocol\n", pid.id); logPrintf(CRITICAL, "and client speaks version %i protocol.\n", PROTOCOL_VERSION); logPrintf(CRITICAL, "Cannot connect to this server.\n"); exit(1); } } void chUpdateScore(Relay_rl * rl, int id, char *pkt, int pktlen) { struct Score_pkt scpkt; Player_pl *pl; pktUnpackScore(&scpkt, pkt); pl = plLookupPlayer(scpkt.id); if(!pl) return; pl->score = scpkt.score; pl->roundScore = scpkt.roundScore; } /* sets round number from the server */ void chUpdateRound(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt nrpkt; pktUnpackPlayerID(&nrpkt, pkt); gm_currentRound = nrpkt.id; } /* sets total rounds from the server */ void chUpdateTotalRounds(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt nrpkt; pktUnpackPlayerID(&nrpkt, pkt); gm_totalRounds = nrpkt.id; } void chSetWindSpeed(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt ws; pktUnpackWindSpeed(&ws, pkt); logPrintf(SPAM, "Received wind packet = %i\n", ws.id); gm_WS_queue[gm_WS_pos++] = ws.id; } void chSetWallType(Relay_rl * rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt ws; pktUnpackWallType(&ws, pkt); bal_wall = (WallTypes_bal) ws.id; gfxDrawWalls(0, 0, gfx_xmax, gfx_ymax); } /* ** update fire info... sent by server when: ** simultaneous mode - everyone has fired ** turn mode - after each player fires */ void chUpdateFireInfo(Relay_rl* rl, int id, char *pkt, int pktlen) { struct PlayerID_pkt nrpkt; Player_pl *pcur; pktUnpackPlayerID(&nrpkt, pkt); curShooterId=nrpkt.id; /* if in simultaneous mode */ if(gm_gametype == SIMULTANEOUS) { /* clear every player's fired data */ for(pcur=pl_begin; pcur; pcur=pcur->next) { pcur->fired=0; } } } /* Change somes tank color */ void chTankColor(Relay_rl* rl, int id, char *pkt, int pktlen) { struct TankColor_pkt tc; Player_pl *pcur; pktUnpackTankColor(&tc, pkt); pcur = plLookupPlayer(tc.id); if(!pcur) return; pcur->tankcolor = tc.color; if(gm_gamemode == PREGAME) gfxDrawPlayerList(); }