cl_main.c

Go to the documentation of this file.
00001 
00006 /*
00007 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 Original file from Quake 2 v3.21: quake2-2.31/client/cl_main.c
00010 Copyright (C) 1997-2001 Id Software, Inc.
00011 
00012 This program is free software; you can redistribute it and/or
00013 modify it under the terms of the GNU General Public License
00014 as published by the Free Software Foundation; either version 2
00015 of the License, or (at your option) any later version.
00016 
00017 This program is distributed in the hope that it will be useful,
00018 but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00020 
00021 See the GNU General Public License for more details.
00022 
00023 You should have received a copy of the GNU General Public License
00024 along with this program; if not, write to the Free Software
00025 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00026 
00027 */
00028 
00029 #include "client.h"
00030 #include "battlescape/cl_localentity.h"
00031 #include "battlescape/events/e_server.h"
00032 #include "battlescape/cl_particle.h"
00033 #include "battlescape/cl_actor.h"
00034 #include "battlescape/cl_hud.h"
00035 #include "battlescape/cl_parse.h"
00036 #include "battlescape/events/e_parse.h"
00037 #include "battlescape/cl_view.h"
00038 #include "cl_console.h"
00039 #include "cl_screen.h"
00040 #include "cl_game.h"
00041 #include "cl_tutorials.h"
00042 #include "cl_tip.h"
00043 #include "cl_team.h"
00044 #include "cl_language.h"
00045 #include "cl_irc.h"
00046 #include "cl_sequence.h"
00047 #include "cl_inventory.h"
00048 #include "cl_menu.h"
00049 #include "cl_http.h"
00050 #include "input/cl_joystick.h"
00051 #include "cinematic/cl_cinematic.h"
00052 #include "sound/s_music.h"
00053 #include "sound/s_sample.h"
00054 #include "renderer/r_main.h"
00055 #include "renderer/r_particle.h"
00056 #include "ui/ui_main.h"
00057 #include "ui/ui_popup.h"
00058 #include "ui/ui_main.h"
00059 #include "ui/ui_font.h"
00060 #include "ui/ui_nodes.h"
00061 #include "ui/ui_parse.h"
00062 #include "multiplayer/mp_callbacks.h"
00063 #include "multiplayer/mp_serverlist.h"
00064 #include "multiplayer/mp_team.h"
00065 #include "../shared/infostring.h"
00066 #include "../shared/parse.h"
00067 #include "../ports/system.h"
00068 
00069 cvar_t *cl_fps;
00070 cvar_t *cl_leshowinvis;
00071 cvar_t *cl_selected;
00072 
00073 cvar_t *cl_lastsave;
00074 
00075 static cvar_t *cl_connecttimeout; /* multiplayer connection timeout value (ms) */
00076 
00077 static cvar_t *cl_introshown;
00078 
00079 /* userinfo */
00080 static cvar_t *cl_name;
00081 static cvar_t *cl_msg;
00082 static cvar_t *cl_ready;
00083 cvar_t *cl_teamnum;
00084 cvar_t *cl_team;
00085 
00086 client_static_t cls;
00087 
00088 struct memPool_s *cl_genericPool;   
00089 struct memPool_s *cl_ircSysPool;    
00090 struct memPool_s *cl_soundSysPool;
00091 struct memPool_s *vid_genericPool;  
00092 struct memPool_s *vid_imagePool;
00093 struct memPool_s *vid_lightPool;    
00094 struct memPool_s *vid_modelPool;    
00095 /*====================================================================== */
00096 
00102 void Cmd_ForwardToServer (void)
00103 {
00104     const char *cmd = Cmd_Argv(0);
00105     struct dbuffer *msg;
00106 
00107     if (cls.state <= ca_connected || cmd[0] == '-' || cmd[0] == '+') {
00108         Com_Printf("Unknown command \"%s\" - wasn't sent to server\n", cmd);
00109         return;
00110     }
00111 
00112     msg = new_dbuffer();
00113     NET_WriteByte(msg, clc_stringcmd);
00114     dbuffer_add(msg, cmd, strlen(cmd));
00115     if (Cmd_Argc() > 1) {
00116         dbuffer_add(msg, " ", 1);
00117         dbuffer_add(msg, Cmd_Args(), strlen(Cmd_Args()));
00118     }
00119     dbuffer_add(msg, "", 1);
00120     NET_WriteMsg(cls.netStream, msg);
00121 }
00122 
00127 static void CL_Env_f (void)
00128 {
00129     const int argc = Cmd_Argc();
00130 
00131     if (argc == 3) {
00132         Sys_Setenv(Cmd_Argv(1), Cmd_Argv(2));
00133     } else if (argc == 2) {
00134         const char *env = SDL_getenv(Cmd_Argv(1));
00135         if (env)
00136             Com_Printf("%s=%s\n", Cmd_Argv(1), env);
00137         else
00138             Com_Printf("%s undefined\n", Cmd_Argv(1));
00139     }
00140 }
00141 
00142 
00143 static void CL_ForwardToServer_f (void)
00144 {
00145     if (cls.state != ca_connected && cls.state != ca_active) {
00146         Com_Printf("Can't \"%s\", not connected\n", Cmd_Argv(0));
00147         return;
00148     }
00149 
00150     /* don't forward the first argument */
00151     if (Cmd_Argc() > 1) {
00152         struct dbuffer *msg;
00153         msg = new_dbuffer();
00154         NET_WriteByte(msg, clc_stringcmd);
00155         dbuffer_add(msg, Cmd_Args(), strlen(Cmd_Args()) + 1);
00156         NET_WriteMsg(cls.netStream, msg);
00157     }
00158 }
00159 
00160 static void CL_Quit_f (void)
00161 {
00162     CL_Disconnect();
00163     Com_Quit();
00164 }
00165 
00172 void CL_Drop (void)
00173 {
00174     CL_Disconnect();
00175 
00176     /* drop loading plaque */
00177     SCR_EndLoadingPlaque();
00178 
00179     GAME_Drop();
00180 }
00181 
00187 static void CL_Connect (void)
00188 {
00189     Com_SetUserinfoModified(qfalse);
00190 
00191     NET_DatagramSocketClose(cls.netDatagramSocket);
00192     cls.netDatagramSocket = NULL;
00193 
00194     assert(!cls.netStream);
00195 
00196     if (cls.servername[0] != '\0') {
00197         assert(cls.serverport[0] != '\0');
00198         Com_Printf("Connecting to %s %s...\n", cls.servername, cls.serverport);
00199         cls.netStream = NET_Connect(cls.servername, cls.serverport);
00200     } else {
00201         Com_Printf("Connecting to localhost...\n");
00202         cls.netStream = NET_ConnectToLoopBack();
00203     }
00204 
00205     if (cls.netStream) {
00206         NET_OOB_Printf(cls.netStream, "connect %i \"%s\"\n", PROTOCOL_VERSION, Cvar_Userinfo());
00207         cls.connectTime = CL_Milliseconds();
00208     } else {
00209         if (cls.servername[0]) {
00210             assert(cls.serverport[0]);
00211             Com_Printf("Could not connect to %s %s\n", cls.servername, cls.serverport);
00212         } else {
00213             Com_Printf("Could not connect to localhost\n");
00214         }
00215     }
00216 }
00217 
00224 static void CL_ClearState (void)
00225 {
00226     LE_Cleanup();
00227 
00228     /* wipe the entire cl structure */
00229     memset(&cl, 0, sizeof(cl));
00230     cl.cam.zoom = 1.0;
00231     CL_ViewCalcFieldOfViewX();
00232 
00233     /* wipe the particles with every new map */
00234     r_numParticles = 0;
00235     /* reset ir goggle state with every new map */
00236     refdef.rendererFlags &= ~RDF_IRGOGGLES;
00237 }
00238 
00247 void CL_Disconnect (void)
00248 {
00249     struct dbuffer *msg;
00250 
00251     if (cls.state < ca_connecting)
00252         return;
00253 
00254     /* send a disconnect message to the server */
00255     if (!Com_ServerState()) {
00256         msg = new_dbuffer();
00257         NET_WriteByte(msg, clc_stringcmd);
00258         NET_WriteString(msg, "disconnect\n");
00259         NET_WriteMsg(cls.netStream, msg);
00260         /* make sure, that this is send */
00261         NET_Wait(0);
00262     }
00263 
00264     NET_StreamFinished(cls.netStream);
00265     cls.netStream = NULL;
00266 
00267     CL_ClearState();
00268 
00269     S_Stop();
00270 
00271     R_ShutdownModels(qfalse);
00272 
00273     CL_SetClientState(ca_disconnected);
00274     CL_ClearBattlescapeEvents();
00275     GAME_EndBattlescape();
00276 }
00277 
00278 /* it's dangerous to activate this */
00279 /*#define ACTIVATE_PACKET_COMMAND*/
00280 #ifdef ACTIVATE_PACKET_COMMAND
00281 
00288 static void CL_Packet_f (void)
00289 {
00290     int i, l;
00291     const char *in;
00292     char *out;
00293     struct net_stream *s;
00294 
00295     if (Cmd_Argc() != 4) {
00296         Com_Printf("Usage: %s <destination> <port> <contents>\n", Cmd_Argv(0));
00297         return;
00298     }
00299 
00300     s = NET_Connect(Cmd_Argv(1), Cmd_Argv(2));
00301     if (!s) {
00302         Com_Printf("Could not connect to %s at port %s\n", Cmd_Argv(1), Cmd_Argv(2));
00303         return;
00304     }
00305 
00306     in = Cmd_Argv(3);
00307 
00308     l = strlen(in);
00309     for (i = 0; i < l; i++) {
00310         if (in[i] == '\\' && in[i + 1] == 'n') {
00311             *out++ = '\n';
00312             i++;
00313         } else
00314             *out++ = in[i];
00315     }
00316     *out = 0;
00317 
00318     NET_OOB_Printf(s, "%s %i", out, PROTOCOL_VERSION);
00319     NET_StreamFinished(s);
00320 }
00321 #endif
00322 
00330 static void CL_ConnectionlessPacket (struct dbuffer *msg)
00331 {
00332     char s[1024];
00333     const char *c;
00334 
00335     NET_ReadStringLine(msg, s, sizeof(s));
00336 
00337     Cmd_TokenizeString(s, qfalse);
00338 
00339     c = Cmd_Argv(0);
00340 
00341     Com_DPrintf(DEBUG_CLIENT, "server OOB: %s\n", Cmd_Args());
00342 
00343     /* server connection */
00344     if (!strncmp(c, "client_connect", 13)) {
00345         int i;
00346         for (i = 1; i < Cmd_Argc(); i++) {
00347             const char *p = Cmd_Argv(i);
00348             if (!strncmp(p, "dlserver=", 9)) {
00349                 p += 9;
00350                 Com_sprintf(cls.downloadReferer, sizeof(cls.downloadReferer), "ufo://%s", cls.servername);
00351                 CL_SetHTTPServer(p);
00352                 if (cls.downloadServer[0])
00353                     Com_Printf("HTTP downloading enabled, URL: %s\n", cls.downloadServer);
00354             }
00355         }
00356         if (cls.state == ca_connected) {
00357             Com_Printf("Dup connect received. Ignored.\n");
00358             return;
00359         }
00360         msg = new_dbuffer();
00361         NET_WriteByte(msg, clc_stringcmd);
00362         NET_WriteString(msg, "new\n");
00363         NET_WriteMsg(cls.netStream, msg);
00364         return;
00365     }
00366 
00367     /* remote command from gui front end */
00368     if (!strncmp(c, "cmd", 3)) {
00369         if (!NET_StreamIsLoopback(cls.netStream)) {
00370             Com_Printf("Command packet from remote host. Ignored.\n");
00371             return;
00372         } else {
00373             char str[1024];
00374             NET_ReadString(msg, str, sizeof(str));
00375             Cbuf_AddText(str);
00376             Cbuf_AddText("\n");
00377         }
00378         return;
00379     }
00380 
00381     /* teaminfo command */
00382     if (!strncmp(c, "teaminfo", 8)) {
00383         CL_ParseTeamInfoMessage(msg);
00384         return;
00385     }
00386 
00387     /* ping from server */
00388     if (!strncmp(c, "ping", 4)) {
00389         NET_OOB_Printf(cls.netStream, "ack");
00390         return;
00391     }
00392 
00393     /* echo request from server */
00394     if (!strncmp(c, "echo", 4)) {
00395         NET_OOB_Printf(cls.netStream, "%s", Cmd_Argv(1));
00396         return;
00397     }
00398 
00399     /* print */
00400     if (!strncmp(c, "print", 5)) {
00401         char str[1024];
00402         NET_ReadString(msg, str, sizeof(str));
00403         /* special reject messages needs proper handling */
00404         if (strstr(s, REJ_PASSWORD_REQUIRED_OR_INCORRECT))
00405             UI_PushWindow("serverpassword", NULL);
00406         UI_Popup(_("Notice"), _(str));
00407         return;
00408     }
00409 
00410     Com_Printf("Unknown command received \"%s\"\n", c);
00411 }
00412 
00420 static void CL_ReadPackets (void)
00421 {
00422     struct dbuffer *msg;
00423     while ((msg = NET_ReadMsg(cls.netStream))) {
00424         const svc_ops_t cmd = NET_ReadByte(msg);
00425         if (cmd == clc_oob)
00426             CL_ConnectionlessPacket(msg);
00427         else
00428             CL_ParseServerMessage(cmd, msg);
00429         free_dbuffer(msg);
00430     }
00431 }
00432 
00437 static void CL_UserInfo_f (void)
00438 {
00439     Com_Printf("User info settings:\n");
00440     Info_Print(Cvar_Userinfo());
00441 }
00442 
00447 static void CL_SpawnSoldiers_f (void)
00448 {
00449     if (!CL_OnBattlescape())
00450         return;
00451 
00452     if (cl.spawned)
00453         return;
00454 
00455     cl.spawned = qtrue;
00456     GAME_SpawnSoldiers();
00457 }
00458 
00459 static qboolean CL_DownloadUMPMap (const char *tiles)
00460 {
00461     char name[MAX_VAR];
00462     char base[MAX_QPATH];
00463     qboolean startedDownload = qfalse;
00464 
00465     /* load tiles */
00466     while (tiles) {
00467         /* get tile name */
00468         const char *token = Com_Parse(&tiles);
00469         if (!tiles)
00470             return startedDownload;
00471 
00472         /* get base path */
00473         if (token[0] == '-') {
00474             Q_strncpyz(base, token + 1, sizeof(base));
00475             continue;
00476         }
00477 
00478         /* get tile name */
00479         if (token[0] == '+')
00480             Com_sprintf(name, sizeof(name), "%s%s", base, token + 1);
00481         else
00482             Q_strncpyz(name, token, sizeof(name));
00483 
00484         startedDownload |= !CL_CheckOrDownloadFile(va("maps/%s.bsp", name));
00485     }
00486 
00487     return startedDownload;
00488 }
00489 
00490 static qboolean CL_DownloadMap (const char *map)
00491 {
00492     qboolean startedDownload;
00493     if (map[0] != '+') {
00494         startedDownload = !CL_CheckOrDownloadFile(va("maps/%s.bsp", map));
00495     } else {
00496         startedDownload = !CL_CheckOrDownloadFile(va("maps/%s.ump", map + 1));
00497         if (!startedDownload) {
00498             const char *tiles = CL_GetConfigString(CS_TILES);
00499             startedDownload = CL_DownloadUMPMap(tiles);
00500         }
00501     }
00502 
00503     return startedDownload;
00504 }
00505 
00511 static qboolean CL_CanMultiplayerStart (void)
00512 {
00513     const int day = CL_GetConfigStringInteger(CS_LIGHTMAP);
00514     const char *serverVersion = CL_GetConfigString(CS_VERSION);
00515 
00516     /* checksum doesn't match with the one the server gave us via configstring */
00517     if (strcmp(UFO_VERSION, serverVersion)) {
00518         Com_sprintf(popupText, sizeof(popupText), _("Local game version (%s) differs from the server version (%s)"), UFO_VERSION, serverVersion);
00519         UI_Popup(_("Error"), popupText);
00520         Com_Error(ERR_DISCONNECT, "Local game version (%s) differs from the server version (%s)", UFO_VERSION, serverVersion);
00521     /* amount of objects from script files doesn't match */
00522     } else if (csi.numODs != CL_GetConfigStringInteger(CS_OBJECTAMOUNT)) {
00523         UI_Popup(_("Error"), _("Script files are not the same"));
00524         Com_Error(ERR_DISCONNECT, "Script files are not the same");
00525     }
00526 
00527     /* activate the map loading screen for multiplayer, too */
00528     SCR_BeginLoadingPlaque();
00529 
00530     /* check download */
00531     if (cls.downloadMaps) { /* confirm map */
00532         if (CL_DownloadMap(CL_GetConfigString(CS_NAME)))
00533             return qfalse;
00534         cls.downloadMaps = qfalse;
00535     }
00536 
00537     /* map might still be downloading? */
00538     if (CL_PendingHTTPDownloads())
00539         return qfalse;
00540 
00541     if (Com_GetScriptChecksum() != CL_GetConfigStringInteger(CS_UFOCHECKSUM))
00542         Com_Printf("You are using modified ufo script files - might produce problems\n");
00543 
00544     CM_LoadMap(CL_GetConfigString(CS_TILES), day, CL_GetConfigString(CS_POSITIONS), cl.mapData, cl.mapTiles);
00545 
00546     if (cl.mapData->mapChecksum != CL_GetConfigStringInteger(CS_MAPCHECKSUM)) {
00547         UI_Popup(_("Error"), _("Local map version differs from server"));
00548         Com_Error(ERR_DISCONNECT, "Local map version differs from server: %u != '%i'",
00549             cl.mapData->mapChecksum, CL_GetConfigStringInteger(CS_MAPCHECKSUM));
00550     }
00551 
00552     return qtrue;
00553 }
00554 
00561 void CL_RequestNextDownload (void)
00562 {
00563     if (cls.state != ca_connected) {
00564         Com_Printf("CL_RequestNextDownload: Not connected (%i)\n", cls.state);
00565         return;
00566     }
00567 
00568     /* Use the map data from the server */
00569     cl.mapTiles = SV_GetMapTiles();
00570     cl.mapData = SV_GetMapData();
00571 
00572     /* as a multiplayer client we have to load the map here and
00573      * check the compatibility with the server */
00574     if (!Com_ServerState() && !CL_CanMultiplayerStart())
00575         return;
00576 
00577     CL_ViewLoadMedia();
00578 
00579     {
00580         struct dbuffer *msg = new_dbuffer();
00581         /* send begin */
00582         /* this will activate the render process (see client state ca_active) */
00583         NET_WriteByte(msg, clc_stringcmd);
00584         /* see CL_StartGame */
00585         NET_WriteString(msg, "begin\n");
00586         NET_WriteMsg(cls.netStream, msg);
00587     }
00588 
00589     cls.waitingForStart = CL_Milliseconds();
00590 }
00591 
00592 
00598 static void CL_Precache_f (void)
00599 {
00600     cls.downloadMaps = qtrue;
00601 
00602     CL_RequestNextDownload();
00603 }
00604 
00605 static void CL_SetRatioFilter_f (void)
00606 {
00607     uiNode_t* firstOption = UI_GetOption(OPTION_VIDEO_RESOLUTIONS);
00608     uiNode_t* option = firstOption;
00609     float requestedRation = atof(Cmd_Argv(1));
00610     qboolean all = qfalse;
00611     qboolean custom = qfalse;
00612     const float delta = 0.01;
00613 
00614     if (Cmd_Argc() != 2) {
00615         Com_Printf("Usage: %s <all|floatration>\n", Cmd_Argv(0));
00616         return;
00617     }
00618 
00619     if (!strcmp(Cmd_Argv(1), "all"))
00620         all = qtrue;
00621     else if (!strcmp(Cmd_Argv(1), "custom"))
00622         custom = qtrue;
00623     else
00624         requestedRation = atof(Cmd_Argv(1));
00625 
00626     while (option) {
00627         int width;
00628         int height;
00629         float ratio;
00630         qboolean visible = qfalse;
00631         int result = sscanf(OPTIONEXTRADATA(option).label, "%i x %i", &width, &height);
00632         if (result != 2)
00633             Com_Error(ERR_FATAL, "CL_SetRatioFilter_f: Impossible to decode resolution label.\n");
00634         ratio = (float)width / (float)height;
00635 
00636         if (all)
00637             visible = qtrue;
00638         else if (custom)
00640             visible = ratio > 2 || (ratio > 1.7 && ratio < 1.76);
00641         else
00642             visible = ratio - delta < requestedRation && ratio + delta > requestedRation;
00643 
00644         option->invis = !visible;
00645         option = option->next;
00646     }
00647 
00648     /* the content change */
00649     UI_RegisterOption(OPTION_VIDEO_RESOLUTIONS, firstOption);
00650 }
00651 
00652 static void CL_VideoInitMenu (void)
00653 {
00654     uiNode_t* option = UI_GetOption(OPTION_VIDEO_RESOLUTIONS);
00655     if (option == NULL) {
00656         int i;
00657         for (i = 0; i < VID_GetModeNums(); i++) {
00658             vidmode_t vidmode;
00659             if (VID_GetModeInfo(i, &vidmode))
00660                 UI_AddOption(&option, va("r%ix%i", vidmode.width, vidmode.height), va("%i x %i", vidmode.width, vidmode.height), va("%i", i));
00661         }
00662         UI_RegisterOption(OPTION_VIDEO_RESOLUTIONS, option);
00663     }
00664 }
00665 
00666 static void CL_TeamDefInitMenu (void)
00667 {
00668     uiNode_t* option = UI_GetOption(OPTION_TEAMDEFS);
00669     if (option == NULL) {
00670         int i;
00671         for (i = 0; i < csi.numTeamDefs; i++) {
00672             teamDef_t *td = &csi.teamDef[i];
00673             if (td->race != RACE_CIVILIAN)
00674                 UI_AddOption(&option, td->id, _(td->name), td->id);
00675         }
00676         UI_RegisterOption(OPTION_TEAMDEFS, option);
00677     }
00678 }
00679 
00681 static const value_t customskin_vals[] = {
00682     {"name", V_STRING, offsetof(customSkin_t, name), 0},
00683     {"singleplayer", V_BOOL, offsetof(customSkin_t, singleplayer), MEMBER_SIZEOF(customSkin_t, singleplayer)},
00684     {"multiplayer", V_BOOL, offsetof(customSkin_t, multiplayer), MEMBER_SIZEOF(customSkin_t, multiplayer)},
00685 
00686     {NULL, 0, 0, 0}
00687 };
00688 
00689 
00690 static void CL_ParseCustomSkin (const char *name, const char **text)
00691 {
00692     const char *errhead = "CL_ParseCustomSkin: unexpected end of file (customskin ";
00693     customSkin_t *skin;
00694     const value_t *vp;
00695     const char *token;
00696 
00697     /* get it's body */
00698     token = Com_Parse(text);
00699 
00700     if (!*text || *token != '{') {
00701         Com_Printf("CL_ParseCustomSkin: customskin \"%s\" without body ignored\n", name);
00702         return;
00703     }
00704 
00705     skin = Com_AllocateCustomSkin();
00706 
00707     skin->id = Mem_PoolStrDup(name, com_genericPool, 0);
00708 
00709     do {
00710         token = Com_EParse(text, errhead, name);
00711         if (!*text)
00712             break;
00713         if (*token == '}')
00714             break;
00715 
00716         for (vp = customskin_vals; vp->string; vp++)
00717             if (!strcmp(token, vp->string)) {
00718                 /* found a definition */
00719                 token = Com_EParse(text, errhead, name);
00720                 if (!*text)
00721                     return;
00722 
00723                 switch (vp->type) {
00724                 default:
00725                     Com_EParseValue(skin, token, vp->type, vp->ofs, vp->size);
00726                     break;
00727                 }
00728                 break;
00729             }
00730 /*
00731         if (!vp->string) {
00732             linkedList_t **list;
00733             if (!strcmp(token, "ufos")) {
00734                 list = &md->ufos;
00735             } else if (!strcmp(token, "aircraft")) {
00736                 list = &md->aircraft;
00737             } else if (!strcmp(token, "terrains")) {
00738                 list = &md->terrains;
00739             } else if (!strcmp(token, "populations")) {
00740                 list = &md->populations;
00741             } else if (!strcmp(token, "cultures")) {
00742                 list = &md->cultures;
00743             } else if (!strcmp(token, "gametypes")) {
00744                 list = &md->gameTypes;
00745             } else {
00746                 Com_Printf("Com_ParseMapDefinition: unknown token \"%s\" ignored (mapdef %s)\n", token, name);
00747                 continue;
00748             }
00749             token = Com_EParse(text, errhead, name);
00750             if (!*text || *token != '{') {
00751                 Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" has ufos, gametypes, terrains, populations or cultures block with no opening brace\n", name);
00752                 break;
00753             }
00754             do {
00755                 token = Com_EParse(text, errhead, name);
00756                 if (!*text || *token == '}')
00757                     break;
00758                 LIST_AddString(list, token);
00759             } while (*text);
00760         }*/
00761     } while (*text);
00762 
00763 }
00764 
00766 static const value_t mapdef_vals[] = {
00767     {"description", V_TRANSLATION_STRING, offsetof(mapDef_t, description), 0},
00768     {"map", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, map), 0},
00769     {"param", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, param), 0},
00770     {"size", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, size), 0},
00771 
00772     {"maxaliens", V_INT, offsetof(mapDef_t, maxAliens), MEMBER_SIZEOF(mapDef_t, maxAliens)},
00773     {"storyrelated", V_BOOL, offsetof(mapDef_t, storyRelated), MEMBER_SIZEOF(mapDef_t, storyRelated)},
00774     {"hurtaliens", V_BOOL, offsetof(mapDef_t, hurtAliens), MEMBER_SIZEOF(mapDef_t, hurtAliens)},
00775 
00776     {"teams", V_INT, offsetof(mapDef_t, teams), MEMBER_SIZEOF(mapDef_t, teams)},
00777     {"multiplayer", V_BOOL, offsetof(mapDef_t, multiplayer), MEMBER_SIZEOF(mapDef_t, multiplayer)},
00778 
00779     {"onwin", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, onwin), 0},
00780     {"onlose", V_CLIENT_HUNK_STRING, offsetof(mapDef_t, onlose), 0},
00781 
00782     {NULL, 0, 0, 0}
00783 };
00784 
00785 static void CL_ParseMapDefinition (const char *name, const char **text)
00786 {
00787     const char *errhead = "Com_ParseMapDefinition: unexpected end of file (mapdef ";
00788     mapDef_t *md;
00789     const value_t *vp;
00790     const char *token;
00791 
00792     /* get it's body */
00793     token = Com_Parse(text);
00794 
00795     if (!*text || *token != '{') {
00796         Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" without body ignored\n", name);
00797         return;
00798     }
00799 
00800     md = Com_GetMapDefByIDX(cls.numMDs);
00801     cls.numMDs++;
00802     if (cls.numMDs >= lengthof(cls.mds))
00803         Sys_Error("Com_ParseMapDefinition: Max mapdef hit");
00804 
00805     memset(md, 0, sizeof(*md));
00806     md->id = Mem_PoolStrDup(name, com_genericPool, 0);
00807 
00808     do {
00809         token = Com_EParse(text, errhead, name);
00810         if (!*text)
00811             break;
00812         if (*token == '}')
00813             break;
00814 
00815         for (vp = mapdef_vals; vp->string; vp++)
00816             if (!strcmp(token, vp->string)) {
00817                 /* found a definition */
00818                 token = Com_EParse(text, errhead, name);
00819                 if (!*text)
00820                     return;
00821 
00822                 switch (vp->type) {
00823                 default:
00824                     Com_EParseValue(md, token, vp->type, vp->ofs, vp->size);
00825                     break;
00826                 case V_TRANSLATION_STRING:
00827                     if (*token == '_')
00828                         token++;
00829                 /* fall through */
00830                 case V_CLIENT_HUNK_STRING:
00831                     Mem_PoolStrDupTo(token, (char**) ((char*)md + (int)vp->ofs), com_genericPool, 0);
00832                     break;
00833                 }
00834                 break;
00835             }
00836 
00837         if (!vp->string) {
00838             linkedList_t **list;
00839             if (!strcmp(token, "ufos")) {
00840                 list = &md->ufos;
00841             } else if (!strcmp(token, "aircraft")) {
00842                 list = &md->aircraft;
00843             } else if (!strcmp(token, "terrains")) {
00844                 list = &md->terrains;
00845             } else if (!strcmp(token, "populations")) {
00846                 list = &md->populations;
00847             } else if (!strcmp(token, "cultures")) {
00848                 list = &md->cultures;
00849             } else if (!strcmp(token, "gametypes")) {
00850                 list = &md->gameTypes;
00851             } else {
00852                 Com_Printf("Com_ParseMapDefinition: unknown token \"%s\" ignored (mapdef %s)\n", token, name);
00853                 continue;
00854             }
00855             token = Com_EParse(text, errhead, name);
00856             if (!*text || *token != '{') {
00857                 Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" has ufos, gametypes, terrains, populations or cultures block with no opening brace\n", name);
00858                 break;
00859             }
00860             do {
00861                 token = Com_EParse(text, errhead, name);
00862                 if (!*text || *token == '}')
00863                     break;
00864                 LIST_AddString(list, token);
00865             } while (*text);
00866         }
00867     } while (*text);
00868 
00869     if (!md->map) {
00870         Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" with no map\n", name);
00871         cls.numMDs--;
00872     }
00873 
00874     if (!md->description) {
00875         Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" with no description\n", name);
00876         cls.numMDs--;
00877     }
00878 }
00879 
00883 static int Com_MapDefSort (const void *mapDef1, const void *mapDef2)
00884 {
00885     const char *map1 = ((const mapDef_t *)mapDef1)->map;
00886     const char *map2 = ((const mapDef_t *)mapDef2)->map;
00887 
00888     /* skip special map chars for rma and base attack */
00889     if (map1[0] == '+' || map1[0] == '.')
00890         map1++;
00891     if (map2[0] == '+' || map2[0] == '.')
00892         map2++;
00893 
00894     return Q_StringSort(map1, map2);
00895 }
00896 
00901 void CL_InitAfter (void)
00902 {
00903     if (sv_dedicated->integer)
00904         return;
00905 
00906     /* start the music track already while precaching data */
00907     S_Frame();
00908     S_LoadSamples();
00909 
00910     /* preload all models for faster access */
00911     CL_ViewPrecacheModels(); /* 95% */
00912 
00913     CL_TeamDefInitMenu();
00914     CL_VideoInitMenu();
00915     IN_JoystickInitMenu();
00916 
00917     CL_LanguageInit();
00918 
00919     /* sort the mapdef array */
00920     qsort(cls.mds, cls.numMDs, sizeof(mapDef_t), Com_MapDefSort);
00921 }
00922 
00936 void CL_ParseClientData (const char *type, const char *name, const char **text)
00937 {
00938     if (!strcmp(type, "font"))
00939         UI_ParseFont(name, text);
00940     else if (!strcmp(type, "tutorial"))
00941         TUT_ParseTutorials(name, text);
00942     else if (!strcmp(type, "menu_model"))
00943         UI_ParseUIModel(name, text);
00944     else if (!strcmp(type, "icon"))
00945         UI_ParseIcon(name, text);
00946     else if (!strcmp(type, "particle"))
00947         CL_ParseParticle(name, text);
00948     else if (!strcmp(type, "sequence"))
00949         CL_ParseSequence(name, text);
00950     else if (!strcmp(type, "music"))
00951         M_ParseMusic(name, text);
00952     else if (!strcmp(type, "tips"))
00953         CL_ParseTipsOfTheDay(name, text);
00954     else if (!strcmp(type, "language"))
00955         CL_ParseLanguages(name, text);
00956     else if (!strcmp(type, "window"))
00957         UI_ParseWindow(type, name, text);
00958     else if (!strcmp(type, "component"))
00959         UI_ParseComponent(type, text);
00960     else if (!strcmp(type, "mapdef"))
00961         CL_ParseMapDefinition(name, text);
00962     else if (!strcmp(type, "customskin"))
00963         CL_ParseCustomSkin(name, text);
00964 }
00965 
00967 static cvarList_t checkcvar[] = {
00968     {"cl_name", NULL, NULL},
00969     {"s_language", NULL, NULL},
00970 
00971     {NULL, NULL, NULL}
00972 };
00976 static void CL_CheckCvars_f (void)
00977 {
00978     int i = 0;
00979 
00980     while (checkcvar[i].name) {
00981         if (!checkcvar[i].var)
00982             checkcvar[i].var = Cvar_Get(checkcvar[i].name, "", 0, NULL);
00983         if (checkcvar[i].var->string[0] == '\0') {
00984             Com_Printf("%s has no value\n", checkcvar[i].var->name);
00985             UI_PushWindow("checkcvars", NULL);
00986             break;
00987         }
00988         i++;
00989     }
00990 }
00991 
00996 static void CL_ShowConfigstrings_f (void)
00997 {
00998     int i;
00999 
01000     for (i = 0; i < MAX_CONFIGSTRINGS; i++) {
01001         const char *configString = CL_GetConfigString(i);
01002         if (configString[0] == '\0')
01003             continue;
01004         Com_Printf("configstring[%3i]: %s\n", i, configString);
01005     }
01006 }
01007 
01013 static void CL_InitLocal (void)
01014 {
01015     CL_SetClientState(ca_disconnected);
01016     cls.realtime = Sys_Milliseconds();
01017 
01018     /* register our variables */
01019     cl_introshown = Cvar_Get("cl_introshown", "0", CVAR_ARCHIVE, "Only show the intro once at the initial start");
01020     cl_fps = Cvar_Get("cl_fps", "0", CVAR_ARCHIVE, "Show frames per second");
01021     cl_log_battlescape_events = Cvar_Get("cl_log_battlescape_events", "1", 0, "Log all battlescape events to events.log");
01022     cl_selected = Cvar_Get("cl_selected", "0", CVAR_NOSET, "Current selected soldier");
01023     cl_connecttimeout = Cvar_Get("cl_connecttimeout", "25000", CVAR_ARCHIVE, "Connection timeout for multiplayer connects");
01024     cl_lastsave = Cvar_Get("cl_lastsave", "", CVAR_ARCHIVE, "Last saved slot - use for the continue-campaign function");
01025     /* userinfo */
01026     cl_name = Cvar_Get("cl_name", Sys_GetCurrentUser(), CVAR_USERINFO | CVAR_ARCHIVE, "Playername");
01027     cl_team = Cvar_Get("cl_team", "1", CVAR_USERINFO, "Humans (0) or aliens (7)");
01028     cl_teamnum = Cvar_Get("cl_teamnum", "1", CVAR_USERINFO | CVAR_ARCHIVE, "Teamnum for multiplayer teamplay games");
01029     cl_ready = Cvar_Get("cl_ready", "0", CVAR_USERINFO, "Ready indicator in the userinfo for tactical missions");
01030     cl_msg = Cvar_Get("cl_msg", "2", CVAR_USERINFO | CVAR_ARCHIVE, "Sets the message level for server messages the client receives");
01031     sv_maxclients = Cvar_Get("sv_maxclients", "1", CVAR_SERVERINFO, "If sv_maxclients is 1 we are in singleplayer - otherwise we are multiplayer mode (see sv_teamplay)");
01032 
01033     masterserver_url = Cvar_Get("masterserver_url", MASTER_SERVER, CVAR_ARCHIVE, "URL of UFO:AI masterserver");
01034 
01035     cl_map_debug = Cvar_Get("debug_map", "0", 0, "Activate realtime map debugging options - bitmask. Valid values are 0, 1, 3 and 7");
01036     cl_le_debug = Cvar_Get("debug_le", "0", 0, "Activates some local entity debug rendering");
01037     cl_leshowinvis = Cvar_Get("cl_leshowinvis", "0", CVAR_ARCHIVE, "Show invisible local entities as null models");
01038 
01039     /* register our commands */
01040     Cmd_AddCommand("check_cvars", CL_CheckCvars_f, "Check cvars like playername and so on");
01041     Cmd_AddCommand("targetalign", CL_ActorTargetAlign_f, _("Target your shot to the ground"));
01042 
01043     Cmd_AddCommand("cl_setratiofilter", CL_SetRatioFilter_f, "Filter the resolution screen list with a ration");
01044 
01045     Cmd_AddCommand("cmd", CL_ForwardToServer_f, "Forward to server");
01046     Cmd_AddCommand("cl_userinfo", CL_UserInfo_f, "Prints your userinfo string");
01047 #ifdef ACTIVATE_PACKET_COMMAND
01048     /* this is dangerous to leave in */
01049     Cmd_AddCommand("packet", CL_Packet_f, "Dangerous debug function for network testing");
01050 #endif
01051     Cmd_AddCommand("quit", CL_Quit_f, "Quits the game");
01052     Cmd_AddCommand("env", CL_Env_f, NULL);
01053 
01054     Cmd_AddCommand("precache", CL_Precache_f, "Function that is called at mapload to precache map data");
01055     Cmd_AddCommand("spawnsoldiers", CL_SpawnSoldiers_f, "Spawns the soldiers for the selected teamnum");
01056     Cmd_AddCommand("cl_configstrings", CL_ShowConfigstrings_f, "Print client configstrings to game console");
01057 
01058     /* forward to server commands
01059      * the only thing this does is allow command completion
01060      * to work -- all unknown commands are automatically
01061      * forwarded to the server */
01062     Cmd_AddCommand("say", NULL, "Chat command");
01063     Cmd_AddCommand("say_team", NULL, "Team chat command");
01064     Cmd_AddCommand("players", NULL, "List of team and player name");
01065 #ifdef DEBUG
01066     Cmd_AddCommand("debug_cgrid", Grid_DumpWholeClientMap_f, "Shows the whole client side pathfinding grid of the current loaded map");
01067     Cmd_AddCommand("debug_croute", Grid_DumpClientRoutes_f, "Shows the whole client side pathfinding grid of the current loaded map");
01068     Cmd_AddCommand("debug_listle", LE_List_f, "Shows a list of current know local entities with type and status");
01069     Cmd_AddCommand("debug_listlm", LM_List_f, "Shows a list of current know local models");
01070     /* forward commands again */
01071     Cmd_AddCommand("debug_edictdestroy", NULL, "Call the 'destroy' function of a given edict");
01072     Cmd_AddCommand("debug_edictuse", NULL, "Call the 'use' function of a given edict");
01073     Cmd_AddCommand("debug_edicttouch", NULL, "Call the 'touch' function of a given edict");
01074     Cmd_AddCommand("debug_killteam", NULL, "Kills a given team");
01075     Cmd_AddCommand("debug_stunteam", NULL, "Stuns a given team");
01076     Cmd_AddCommand("debug_listscore", NULL, "Shows mission-score entries of all team members");
01077 #endif
01078 
01079     IN_Init();
01080     CL_ServerEventsInit();
01081     CL_CameraInit();
01082 
01083     CLMN_InitStartup();
01084     TUT_InitStartup();
01085     PTL_InitStartup();
01086     GAME_InitStartup();
01087     SEQ_InitStartup();
01088     ACTOR_InitStartup();
01089     TEAM_InitStartup();
01090     TOTD_InitStartup();
01091     HUD_InitStartup();
01092     INV_InitStartup();
01093     HTTP_InitStartup();
01094 }
01095 
01101 static void CL_SendChangedUserinfos (void)
01102 {
01103     /* send a userinfo update if needed */
01104     if (cls.state >= ca_connected) {
01105         if (Com_IsUserinfoModified()) {
01106             struct dbuffer *msg = new_dbuffer();
01107             NET_WriteByte(msg, clc_userinfo);
01108             NET_WriteString(msg, Cvar_Userinfo());
01109             NET_WriteMsg(cls.netStream, msg);
01110             Com_SetUserinfoModified(qfalse);
01111         }
01112     }
01113 }
01114 
01118 static void CL_SendCommand (void)
01119 {
01120     /* get new key events */
01121     IN_SendKeyEvents();
01122 
01123     /* process console commands */
01124     Cbuf_Execute();
01125 
01126     /* send intentions now */
01127     CL_SendChangedUserinfos();
01128 
01129     /* fix any cheating cvars */
01130     Cvar_FixCheatVars();
01131 
01132     switch (cls.state) {
01133     case ca_disconnected:
01134         /* if the local server is running and we aren't connected then connect */
01135         if (Com_ServerState()) {
01136             cls.servername[0] = '\0';
01137             cls.serverport[0] = '\0';
01138             CL_SetClientState(ca_connecting);
01139             return;
01140         }
01141         break;
01142     case ca_connecting:
01143         if (CL_Milliseconds() - cls.connectTime > cl_connecttimeout->integer) {
01144             if (GAME_IsMultiplayer())
01145                 Com_Error(ERR_DROP, "Server is not reachable");
01146         }
01147         break;
01148     case ca_connected:
01149         if (cls.waitingForStart) {
01150             if (CL_Milliseconds() - cls.waitingForStart > cl_connecttimeout->integer) {
01151                 Com_Error(ERR_DROP, "Server aborted connection - the server didn't response in %is. You can try to increase the cvar cl_connecttimeout",
01152                         cl_connecttimeout->integer / 1000);
01153             } else {
01154                 Com_sprintf(cls.loadingMessages, sizeof(cls.loadingMessages),
01155                     "%s (%i)", _("Awaiting game start"), (CL_Milliseconds() - cls.waitingForStart) / 1000);
01156                 SCR_UpdateScreen();
01157             }
01158         }
01159         break;
01160     default:
01161         break;
01162     }
01163 }
01164 
01168 void CL_SetClientState (int state)
01169 {
01170     Com_DPrintf(DEBUG_CLIENT, "CL_SetClientState: Set new state to %i (old was: %i)\n", state, cls.state);
01171     cls.state = state;
01172 
01173     switch (cls.state) {
01174     case ca_uninitialized:
01175         Com_Error(ERR_FATAL, "CL_SetClientState: Don't set state ca_uninitialized\n");
01176         break;
01177     case ca_sequence:
01178         refdef.ready = qtrue;
01179         break;
01180     case ca_active:
01181         cls.waitingForStart = 0;
01182         break;
01183     case ca_connecting:
01184         CL_Connect();
01185         break;
01186     case ca_disconnected:
01187         cls.waitingForStart = 0;
01188         break;
01189     case ca_connected:
01190         /* wipe the client_state_t struct */
01191         CL_ClearState();
01192         break;
01193     default:
01194         break;
01195     }
01196 }
01197 
01201 void CL_Frame (int now, void *data)
01202 {
01203     static int lastFrame = 0;
01204     int delta;
01205 
01206     if (sys_priority->modified || sys_affinity->modified)
01207         Sys_SetAffinityAndPriority();
01208 
01209     /* decide the simulation time */
01210     delta = now - lastFrame;
01211     if (lastFrame)
01212         cls.frametime = delta / 1000.0;
01213     else
01214         cls.frametime = 0;
01215     cls.realtime = Sys_Milliseconds();
01216     cl.time = now;
01217     lastFrame = now;
01218 
01219     /* frame rate calculation */
01220     if (delta)
01221         cls.framerate = 1000.0 / delta;
01222 
01223     if (cls.state == ca_connected) {
01224         /* we run full speed when connecting */
01225         CL_RunHTTPDownloads();
01226     }
01227 
01228     /* fetch results from server */
01229     CL_ReadPackets();
01230 
01231     CL_SendCommand();
01232 
01233     IN_Frame();
01234 
01235     GAME_Frame();
01236 
01237     /* update camera position */
01238     CL_CameraMove();
01239 
01240     /* end the rounds when no soldier is alive */
01241     CL_RunMapParticles();
01242     CL_ParticleRun();
01243 
01244     /* update the screen */
01245     SCR_UpdateScreen();
01246 
01247     /* advance local effects for next frame */
01248     SCR_RunConsole();
01249 
01250     /* LE updates */
01251     LE_Think();
01252 
01253     /* sound frame */
01254     S_Frame();
01255 
01256     /* send a new command message to the server */
01257     CL_SendCommand();
01258 }
01259 
01263 void CL_SlowFrame (int now, void *data)
01264 {
01265     /* language */
01266     if (s_language->modified)
01267         CL_LanguageTryToSet(s_language->string);
01268 
01269     Irc_Logic_Frame();
01270 
01271     HUD_Update();
01272 }
01273 
01274 static void CL_InitMemPools (void)
01275 {
01276     cl_genericPool = Mem_CreatePool("Client: Generic");
01277     cl_soundSysPool = Mem_CreatePool("Client: Sound system");
01278     cl_ircSysPool = Mem_CreatePool("Client: IRC system");
01279 }
01280 
01285 void CL_Init (void)
01286 {
01287     /* i18n through gettext */
01288     char languagePath[MAX_OSPATH];
01289     cvar_t *fs_i18ndir;
01290 
01291     if (sv_dedicated->integer)
01292         return;                 /* nothing running on the client */
01293 
01294     memset(&cls, 0, sizeof(cls));
01295 
01296     fs_i18ndir = Cvar_Get("fs_i18ndir", "", 0, "System path to language files");
01297     /* i18n through gettext */
01298     setlocale(LC_ALL, "C");
01299     setlocale(LC_MESSAGES, "");
01300     /* use system locale dir if we can't find in gamedir */
01301     if (fs_i18ndir->string[0] != '\0')
01302         Q_strncpyz(languagePath, fs_i18ndir->string, sizeof(languagePath));
01303     else
01304         Com_sprintf(languagePath, sizeof(languagePath), "%s/"BASEDIRNAME"/i18n/", FS_GetCwd());
01305     Com_DPrintf(DEBUG_CLIENT, "...using mo files from %s\n", languagePath);
01306     bindtextdomain(TEXT_DOMAIN, languagePath);
01307     bind_textdomain_codeset(TEXT_DOMAIN, "UTF-8");
01308     /* load language file */
01309     textdomain(TEXT_DOMAIN);
01310 
01311     CL_InitMemPools();
01312 
01313     /* all archived variables will now be loaded */
01314     Con_Init();
01315 
01316     CIN_Init();
01317 
01318     VID_Init();
01319     S_Init();
01320     SCR_Init();
01321 
01322     CL_InitLocal();
01323 
01324     Irc_Init();
01325     CL_ViewInit();
01326 
01327     CL_InitParticles();
01328 
01329     CL_ClearState();
01330 
01331     /* Touch memory */
01332     Mem_TouchGlobal();
01333 }
01334 
01335 int CL_Milliseconds (void)
01336 {
01337     return cls.realtime;
01338 }
01339 
01347 void CL_Shutdown (void)
01348 {
01349     static qboolean isdown = qfalse;
01350 
01351     if (isdown) {
01352         printf("recursive shutdown\n");
01353         return;
01354     }
01355     isdown = qtrue;
01356 
01357     GAME_SetMode(GAME_NONE);
01358     CL_HTTP_Cleanup();
01359     Irc_Shutdown();
01360     Con_SaveConsoleHistory();
01361     Key_WriteBindings("keys.cfg");
01362     S_Shutdown();
01363     R_Shutdown();
01364     UI_Shutdown();
01365     CIN_Shutdown();
01366 }

Generated by  doxygen 1.6.2