00001
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "../cl_shared.h"
00027 #include "../cl_game.h"
00028 #include "../cl_inventory.h"
00029 #include "../cl_team.h"
00030 #include "../ui/ui_main.h"
00031 #include "../ui/ui_popup.h"
00032 #include "../ui/node/ui_node_container.h"
00033 #include "mp_team.h"
00034 #include "save_multiplayer.h"
00035
00036 #define MPTEAM_SAVE_FILE_VERSION 4
00037
00038 static inventory_t mp_inventory;
00039
00040 static qboolean characterActive[MAX_ACTIVETEAM];
00041
00042 typedef struct mpSaveFileHeader_s {
00043 uint32_t version;
00044 uint32_t soldiercount;
00045 char name[32];
00046 uint32_t xmlSize;
00047 } mpSaveFileHeader_t;
00048
00049 static void MP_UpdateActiveTeamList (void)
00050 {
00051 int i;
00052
00053 memset(characterActive, 0, sizeof(characterActive));
00054 for (i = 0; i < chrDisplayList.num; i++)
00055 characterActive[i] = qtrue;
00056
00057 UI_ExecuteConfunc("mp_checkboxes_update %i", chrDisplayList.num);
00058 }
00059
00060 void MP_AutoTeam_f (void)
00061 {
00062 GAME_MP_AutoTeam();
00063
00064 MP_UpdateActiveTeamList();
00065 }
00066
00071 void MP_ToggleActorForTeam_f (void)
00072 {
00073 int num;
00074 int value;
00075
00076 if (Cmd_Argc() != 3) {
00077 Com_Printf("Usage: %s <num> <value>\n", Cmd_Argv(0));
00078 return;
00079 }
00080
00081 num = atoi(Cmd_Argv(1));
00082 value = atoi(Cmd_Argv(2));
00083 if (num < 0 || num >= chrDisplayList.num)
00084 value = 0;
00085
00086 characterActive[num] = (value != 0);
00087 }
00088
00093 void MP_SaveTeamState_f (void)
00094 {
00095 int i, num;
00096
00097 num = 0;
00098 for (i = 0; i < chrDisplayList.num; i++) {
00099 chrDisplayList.chr[num]->ucn -= (i -num);
00100 if (characterActive[i]) {
00101 assert(chrDisplayList.chr[i] != NULL);
00102 chrDisplayList.chr[num++] = chrDisplayList.chr[i];
00103 }
00104 }
00105 chrDisplayList.num = num;
00106 }
00107
00111 void MP_MultiplayerTeamSlotComments_f (void)
00112 {
00113 int i;
00114
00115 for (i = 0; i < 8; i++) {
00116
00117 qFILE f;
00118
00119 FS_OpenFile(va("save/team%i.mpt", i), &f, FILE_READ);
00120 if (f.f || f.z) {
00121 mpSaveFileHeader_t header;
00122 const int clen = sizeof(header);
00123 if (FS_Read(&header, clen, &f) != clen) {
00124 Com_Printf("Warning: Could not read %i bytes from savefile\n", clen);
00125 FS_CloseFile(&f);
00126 Cvar_Set(va("mn_slot%i", i), "");
00127 continue;
00128 }
00129 FS_CloseFile(&f);
00130 if (LittleLong(header.version) == MPTEAM_SAVE_FILE_VERSION) {
00131 UI_ExecuteConfunc("set_slotname %i %i \"%s\"", i, LittleLong(header.soldiercount), header.name);
00132 } else {
00133 Cvar_Set(va("mn_slot%i", i), "");
00134 }
00135 } else {
00136 Cvar_Set(va("mn_slot%i", i), "");
00137 }
00138 }
00139 }
00140
00146 static void MP_SaveTeamMultiplayerInfo (mxml_node_t *p)
00147 {
00148 int i;
00149
00150
00151 Com_DPrintf(DEBUG_CLIENT, "Saving %i teammembers\n", chrDisplayList.num);
00152 for (i = 0; i < chrDisplayList.num; i++) {
00153 const character_t *chr = chrDisplayList.chr[i];
00154 mxml_node_t *n = mxml_AddNode(p, SAVE_MULTIPLAYER_CHARACTER);
00155 CL_SaveCharacterXML(n, chr);
00156 }
00157 }
00158
00164 static void MP_LoadTeamMultiplayerInfo (mxml_node_t *p)
00165 {
00166 int i;
00167 mxml_node_t *n;
00168 const size_t size = GAME_GetCharacterArraySize();
00169
00170 GAME_ResetCharacters();
00171 memset(characterActive, 0, sizeof(characterActive));
00172
00173
00174 for (i = 0, n = mxml_GetNode(p, SAVE_MULTIPLAYER_CHARACTER); n && i < size; i++, n = mxml_GetNextNode(n, p, SAVE_MULTIPLAYER_CHARACTER)) {
00175 character_t *chr = GAME_GetCharacter(i);
00176 CL_LoadCharacterXML(n, chr);
00177 assert(i < lengthof(chrDisplayList.chr));
00178 chrDisplayList.chr[i] = chr;
00179 }
00180 chrDisplayList.num = i;
00181
00182 MP_UpdateActiveTeamList();
00183
00184 Com_DPrintf(DEBUG_CLIENT, "Loaded %i teammembers\n", chrDisplayList.num);
00185 }
00186
00192 static qboolean MP_SaveTeamMultiplayer (const char *filename, const char *name)
00193 {
00194 int requiredBufferLength;
00195 byte *buf, *fbuf;
00196 mpSaveFileHeader_t header;
00197 char dummy[2];
00198 int i;
00199 mxml_node_t *topNode, *node, *snode;
00200 equipDef_t *ed = GAME_GetEquipmentDefinition();
00201
00202 topNode = mxmlNewXML("1.0");
00203 node = mxml_AddNode(topNode, SAVE_MULTIPLAYER_ROOTNODE);
00204 memset(&header, 0, sizeof(header));
00205 header.version = LittleLong(MPTEAM_SAVE_FILE_VERSION);
00206 header.soldiercount = LittleLong(chrDisplayList.num);
00207 Q_strncpyz(header.name, name, sizeof(header.name));
00208
00209 snode = mxml_AddNode(node, SAVE_MULTIPLAYER_TEAM);
00210 MP_SaveTeamMultiplayerInfo(snode);
00211
00212 snode = mxml_AddNode(node, SAVE_MULTIPLAYER_EQUIPMENT);
00213 for (i = 0; i < csi.numODs; i++) {
00214 const objDef_t *od = INVSH_GetItemByIDX(i);
00215 if (ed->numItems[od->idx] || ed->numItemsLoose[od->idx]) {
00216 mxml_node_t *ssnode = mxml_AddNode(snode, SAVE_MULTIPLAYER_ITEM);
00217 mxml_AddString(ssnode, SAVE_MULTIPLAYER_ID, od->id);
00218 mxml_AddIntValue(ssnode, SAVE_MULTIPLAYER_NUM, ed->numItems[od->idx]);
00219 mxml_AddIntValue(ssnode, SAVE_MULTIPLAYER_NUMLOOSE, ed->numItemsLoose[od->idx]);
00220 }
00221 }
00222 requiredBufferLength = mxmlSaveString(topNode, dummy, 2, MXML_NO_CALLBACK);
00223
00224 header.xmlSize = LittleLong(requiredBufferLength);
00225
00226 buf = (byte *) Mem_PoolAlloc(sizeof(byte) * requiredBufferLength + 1, cl_genericPool, 0);
00227 if (!buf) {
00228 mxmlDelete(topNode);
00229 Com_Printf("Error: Could not allocate enough memory to save this game\n");
00230 return qfalse;
00231 }
00232 mxmlSaveString(topNode, (char*)buf, requiredBufferLength + 1, MXML_NO_CALLBACK);
00233 mxmlDelete(topNode);
00234
00235 fbuf = (byte *) Mem_PoolAlloc(requiredBufferLength + 1 + sizeof(header), cl_genericPool, 0);
00236 memcpy(fbuf, &header, sizeof(header));
00237 memcpy(fbuf + sizeof(header), buf, requiredBufferLength + 1);
00238 Mem_Free(buf);
00239
00240
00241 FS_WriteFile(fbuf, requiredBufferLength + 1 + sizeof(header), filename);
00242 Mem_Free(fbuf);
00243
00244 return qtrue;
00245 }
00246
00250 void MP_SaveTeamMultiplayer_f (void)
00251 {
00252 if (!chrDisplayList.num) {
00253 UI_Popup(_("Note"), _("Error saving team. Nothing to save yet."));
00254 return;
00255 } else {
00256 char filename[MAX_OSPATH];
00257 int index;
00258 const char *name;
00259
00260 if (Cmd_Argc() != 2) {
00261 Com_Printf("Usage: %s <slotindex>\n", Cmd_Argv(0));
00262 return;
00263 }
00264
00265 index = atoi(Cmd_Argv(1));
00266
00267 name = Cvar_GetString(va("mn_slot%i", index));
00268 if (name[0] == '\0')
00269 name = _("NewTeam");
00270
00271
00272 Com_sprintf(filename, sizeof(filename), "save/team%i.mpt", index);
00273 if (!MP_SaveTeamMultiplayer(filename, name))
00274 UI_Popup(_("Note"), _("Error saving team. Check free disk space!"));
00275 }
00276 }
00277
00283 static qboolean MP_LoadTeamMultiplayer (const char *filename)
00284 {
00285 uLongf len;
00286 qFILE f;
00287 byte *cbuf;
00288 int clen;
00289 mxml_node_t *topNode, *node, *snode, *ssnode;
00290 mpSaveFileHeader_t header;
00291 equipDef_t *ed;
00292
00293
00294 FS_OpenFile(filename, &f, FILE_READ);
00295 if (!f.f && !f.z) {
00296 Com_Printf("Couldn't open file '%s'\n", filename);
00297 return qfalse;
00298 }
00299
00300 clen = FS_FileLength(&f);
00301 cbuf = (byte *) Mem_PoolAlloc(sizeof(byte) * clen, cl_genericPool, 0);
00302 if (FS_Read(cbuf, clen, &f) != clen) {
00303 Com_Printf("Warning: Could not read %i bytes from savefile\n", clen);
00304 FS_CloseFile(&f);
00305 Mem_Free(cbuf);
00306 return qfalse;
00307 }
00308 FS_CloseFile(&f);
00309
00310 memcpy(&header, cbuf, sizeof(header));
00311
00312 header.version = LittleLong(header.version);
00313 header.xmlSize = LittleLong(header.xmlSize);
00314 len = header.xmlSize + 1 + sizeof(header);
00315
00316 if (header.version != MPTEAM_SAVE_FILE_VERSION) {
00317 Com_Printf("Invalid version number\n");
00318 return qfalse;
00319 }
00320
00321 Com_Printf("Loading multiplayer team (size %d / %i)\n", clen, header.xmlSize);
00322
00323 topNode = mxmlLoadString(NULL, (char*)(cbuf + sizeof(header)) , mxml_ufo_type_cb);
00324 Mem_Free(cbuf);
00325 if (!topNode) {
00326 Com_Printf("Error: Failure in loading the xml data!");
00327 return qfalse;
00328 }
00329
00330 node = mxml_GetNode(topNode, SAVE_MULTIPLAYER_ROOTNODE);
00331 if (!node) {
00332 mxmlDelete(topNode);
00333 Com_Printf("Error: Failure in loading the xml data! (node 'multiplayer' not found)\n");
00334 return qfalse;
00335 }
00336 Cvar_Set("mn_teamname", header.name);
00337
00338 snode = mxml_GetNode(node, SAVE_MULTIPLAYER_TEAM);
00339 if (!snode) {
00340 mxmlDelete(topNode);
00341 Mem_Free(cbuf);
00342 Com_Printf("Error: Failure in loading the xml data! (node 'team' not found)\n");
00343 return qfalse;
00344 }
00345 MP_LoadTeamMultiplayerInfo(snode);
00346
00347 snode = mxml_GetNode(node, SAVE_MULTIPLAYER_EQUIPMENT);
00348 if (!snode) {
00349 mxmlDelete(topNode);
00350 Mem_Free(cbuf);
00351 Com_Printf("Error: Failure in loading the xml data! (node 'equipment' not found)\n");
00352 return qfalse;
00353 }
00354
00355 ed = GAME_GetEquipmentDefinition();
00356 for (ssnode = mxml_GetNode(snode, SAVE_MULTIPLAYER_ITEM); ssnode; ssnode = mxml_GetNextNode(ssnode, snode, SAVE_MULTIPLAYER_ITEM)) {
00357 const char *objID = mxml_GetString(ssnode, SAVE_MULTIPLAYER_ID);
00358 const objDef_t *od = INVSH_GetItemByID(objID);
00359
00360 if (od) {
00361 ed->numItems[od->idx] = mxml_GetInt(snode, SAVE_MULTIPLAYER_NUM, 0);
00362 ed->numItemsLoose[od->idx] = mxml_GetInt(snode, SAVE_MULTIPLAYER_NUMLOOSE, 0);
00363 }
00364 }
00365
00366 Com_Printf("File '%s' loaded.\n", filename);
00367
00368 mxmlDelete(topNode);
00369
00370 return qtrue;
00371 }
00372
00373 qboolean MP_LoadDefaultTeamMultiplayer (void)
00374 {
00375 return MP_LoadTeamMultiplayer("save/team0.mpt");
00376 }
00377
00381 void MP_LoadTeamMultiplayer_f (void)
00382 {
00383 char filename[MAX_OSPATH];
00384 int index;
00385
00386 if (Cmd_Argc() != 2) {
00387 Com_Printf("Usage: %s <slotindex>\n", Cmd_Argv(0));
00388 return;
00389 }
00390
00391 index = atoi(Cmd_Argv(1));
00392
00393
00394 Com_sprintf(filename, sizeof(filename), "save/team%i.mpt", index);
00395 MP_LoadTeamMultiplayer(filename);
00396 }
00397
00402 static void MP_GetEquipment (void)
00403 {
00404 const equipDef_t *edFromScript;
00405 const char *teamID = Com_ValueToStr(&cl_team->integer, V_TEAM, 0);
00406 char equipmentName[MAX_VAR];
00407 equipDef_t unused;
00408 equipDef_t *ed;
00409
00410 Com_sprintf(equipmentName, sizeof(equipmentName), "multiplayer_%s", teamID);
00411
00412
00413 edFromScript = INV_GetEquipmentDefinitionByID(equipmentName);
00414
00415 if (chrDisplayList.num > 0)
00416 ui_inventory = &chrDisplayList.chr[0]->i;
00417 else
00418 ui_inventory = NULL;
00419
00420 ed = GAME_GetEquipmentDefinition();
00421 *ed = *edFromScript;
00422
00423
00424 unused = *edFromScript;
00425
00426 UI_ContainerNodeUpdateEquipment(&mp_inventory, &unused);
00427 }
00428
00433 void MP_UpdateMenuParameters_f (void)
00434 {
00435 int i;
00436 const size_t size = lengthof(chrDisplayList.chr);
00437
00438
00439 Cvar_Set("mn_itemname", "");
00440 Cvar_Set("mn_item", "");
00441 UI_ResetData(TEXT_STANDARD);
00442
00443 for (i = 0; i < size; i++) {
00444 const char *name;
00445 if (i < chrDisplayList.num)
00446 name = chrDisplayList.chr[i]->name;
00447 else
00448 name = "";
00449 Cvar_Set(va("mn_name%i", i), name);
00450 }
00451
00452 MP_GetEquipment();
00453 }
00454
00455 void MP_TeamSelect_f (void)
00456 {
00457 const int old = cl_selected->integer;
00458 int num;
00459
00460
00461 if (Cmd_Argc() < 2) {
00462 Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
00463 return;
00464 }
00465
00466 num = atoi(Cmd_Argv(1));
00467 if (num < 0 || num >= chrDisplayList.num)
00468 return;
00469
00470 UI_ExecuteConfunc("mp_soldier_select %i", num);
00471 UI_ExecuteConfunc("mp_soldier_unselect %i", old);
00472 Cmd_ExecuteString(va("actor_select_equip %i", num));
00473 }