UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cl_game_team.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.m
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 
24 */
25 
26 #include "../client.h"
27 #include "cl_game.h"
28 #include "cl_game_team.h"
29 #include "../cl_inventory.h"
30 #include "../../shared/parse.h"
31 #include "../cl_team.h"
32 #include "../ui/ui_main.h"
33 #include "../ui/ui_popup.h"
34 #include "../ui/node/ui_node_container.h"
35 #include "save_team.h"
36 #include "save_character.h"
37 #include "save_inventory.h"
38 
39 #define TEAM_SAVE_FILE_VERSION 4
40 
43 
44 typedef struct teamSaveFileHeader_s {
45  uint32_t version;
46  uint32_t soldiercount;
47  char name[32];
48  uint32_t xmlSize;
50 
51 static void GAME_UpdateActiveTeamList (void)
52 {
53  const int teamSize = LIST_Count(chrDisplayList);
55  for (int i = 0; i < teamSize; i++)
56  characterActive[i] = true;
57 }
58 
59 void GAME_AutoTeam (const char* equipmentDefinitionID, int teamMembers)
60 {
61  const equipDef_t* ed = INV_GetEquipmentDefinitionByID(equipmentDefinitionID);
62  const char* teamDefID = GAME_GetTeamDef();
64 
65  GAME_GenerateTeam(teamDefID, ed, teamMembers);
66 }
67 
68 void GAME_AutoTeam_f (void)
69 {
70  if (Cmd_Argc() != 2) {
71  Com_Printf("Usage: %s <equipment-definition>\n", Cmd_Argv(0));
72  return;
73  }
74 
77 }
78 
84 {
85  if (Cmd_Argc() != 3) {
86  Com_Printf("Usage: %s <num> <value>\n", Cmd_Argv(0));
87  return;
88  }
89 
90  const int ucn = atoi(Cmd_Argv(1));
91  const int value = atoi(Cmd_Argv(2));
92 
93  int i = 0;
94  bool found = false;
96  if (ucn == chrTmp->ucn) {
97  found = true;
98  break;
99  }
100  i++;
101  }
102  if (!found)
103  return;
104 
105  characterActive[i] = (value != 0);
106 }
107 
113 {
114  int i = 0;
116  if (!characterActive[i++])
118  }
119 }
120 
124 void GAME_TeamDelete_f (void)
125 {
126  if (Cmd_Argc() != 2) {
127  Com_Printf("Usage: %s <filename>\n", Cmd_Argv(0));
128  return;
129  }
130 
131  const char* team = Cmd_Argv(1);
132  char buf[MAX_OSPATH];
133  GAME_GetAbsoluteSavePath(buf, sizeof(buf));
134  Q_strcat(buf, sizeof(buf), "%s", team);
135  FS_RemoveFile(buf);
136 }
137 
142 {
143  UI_ExecuteConfunc("teamsaveslotsclear");
144 
145  char relSavePath[MAX_OSPATH];
146  GAME_GetRelativeSavePath(relSavePath, sizeof(relSavePath));
147  char pattern[MAX_OSPATH];
148  Q_strncpyz(pattern, relSavePath, sizeof(pattern));
149  Q_strcat(pattern, sizeof(pattern), "*.mpt");
150 
151  FS_BuildFileList(pattern);
152  int i = 0;
153  const char* filename;
154  while ((filename = FS_NextFileFromFileList(pattern)) != nullptr) {
155  ScopedFile f;
156  const char* savePath = va("%s/%s", relSavePath, filename);
157  FS_OpenFile(savePath, &f, FILE_READ);
158  if (!f) {
159  Com_Printf("Warning: Could not open '%s'\n", filename);
160  continue;
161  }
162  teamSaveFileHeader_t header;
163  const int clen = sizeof(header);
164  if (FS_Read(&header, clen, &f) != clen) {
165  Com_Printf("Warning: Could not read %i bytes from savefile\n", clen);
166  continue;
167  }
168  if (LittleLong(header.version) != TEAM_SAVE_FILE_VERSION) {
169  Com_Printf("Warning: Version mismatch in '%s'\n", filename);
170  continue;
171  }
172 
173  char absSavePath[MAX_OSPATH];
174  GAME_GetAbsoluteSavePath(absSavePath, sizeof(absSavePath));
175  const bool uploadable = FS_FileExists("%s/%s", absSavePath, filename);
176  UI_ExecuteConfunc("teamsaveslotadd %i \"%s\" \"%s\" %i %i", i++, filename, header.name, LittleLong(header.soldiercount), uploadable ? 1 : 0);
177  }
178  FS_NextFileFromFileList(nullptr);
179 }
180 
186 static void GAME_SaveTeamInfo (xmlNode_t* p)
187 {
190  GAME_SaveCharacter(n, chr);
191  }
192 }
193 
199 static void GAME_LoadTeamInfo (xmlNode_t* p)
200 {
201  const size_t size = GAME_GetCharacterArraySize();
202 
205 
206  /* header */
207  int i = 0;
208  for (xmlNode_t* n = XML_GetNode(p, SAVE_TEAM_CHARACTER); n && i < size; i++, n = XML_GetNextNode(n, p, SAVE_TEAM_CHARACTER)) {
209  character_t* chr = GAME_GetCharacter(i);
210  GAME_LoadCharacter(n, chr);
211  UI_ExecuteConfunc("team_memberadd %i \"%s\" \"%s\" %i", i, chr->name, chr->head, chr->headSkin);
212  LIST_AddPointer(&chrDisplayList, (void*)chr);
213  }
214 
216 }
217 
221 static bool GAME_SaveTeam (const char* filename, const char* name)
222 {
223  teamSaveFileHeader_t header;
224  char dummy[2];
226 
227  xmlNode_t* topNode = mxmlNewXML("1.0");
228  xmlNode_t* node = XML_AddNode(topNode, SAVE_TEAM_ROOTNODE);
229  OBJZERO(header);
232  Q_strncpyz(header.name, name, sizeof(header.name));
233 
234  xmlNode_t* snode = XML_AddNode(node, SAVE_TEAM_NODE);
235  GAME_SaveTeamInfo(snode);
236 
237  snode = XML_AddNode(node, SAVE_TEAM_EQUIPMENT);
238  for (int i = 0; i < csi.numODs; i++) {
239  const objDef_t* od = INVSH_GetItemByIDX(i);
240  if (ed->numItems[od->idx] || ed->numItemsLoose[od->idx]) {
241  xmlNode_t* ssnode = XML_AddNode(snode, SAVE_TEAM_ITEM);
242  XML_AddString(ssnode, SAVE_TEAM_ID, od->id);
243  XML_AddIntValue(ssnode, SAVE_TEAM_NUM, ed->numItems[od->idx]);
245  }
246  }
247  const int requiredBufferLength = mxmlSaveString(topNode, dummy, 2, MXML_NO_CALLBACK);
248  /* required for storing compressed */
249  header.xmlSize = LittleLong(requiredBufferLength);
250 
251  byte* const buf = Mem_PoolAllocTypeN(byte, requiredBufferLength + 1, cl_genericPool);
252  if (!buf) {
253  mxmlDelete(topNode);
254  Com_Printf("Error: Could not allocate enough memory to save this game\n");
255  return false;
256  }
257  mxmlSaveString(topNode, (char*)buf, requiredBufferLength + 1, MXML_NO_CALLBACK);
258  mxmlDelete(topNode);
259 
260  byte* const fbuf = Mem_PoolAllocTypeN(byte, requiredBufferLength + 1 + sizeof(header), cl_genericPool);
261  memcpy(fbuf, &header, sizeof(header));
262  memcpy(fbuf + sizeof(header), buf, requiredBufferLength + 1);
263  Mem_Free(buf);
264 
265  /* last step - write data */
266  FS_WriteFile(fbuf, requiredBufferLength + 1 + sizeof(header), filename);
267  Mem_Free(fbuf);
268 
269  return true;
270 }
271 
280 {
281  char buf[MAX_OSPATH];
282  GAME_GetRelativeSavePath(buf, sizeof(buf));
283 
284  for (int num = 0; num < 100; num++) {
285  Com_sprintf(filename, size, "%s/team%02i.mpt", buf, num);
286  if (FS_CheckFile("%s", filename) == -1) {
287  return true;
288  }
289  }
290  return false;
291 }
292 
296 void GAME_SaveTeam_f (void)
297 {
298  if (Cmd_Argc() != 2) {
299  Com_Printf("Usage: %s <name>\n", Cmd_Argv(0));
300  return;
301  }
302 
304  UI_Popup(_("Note"), _("Error saving team. Nothing to save yet."));
305  return;
306  }
307 
308  const char* name = Cmd_Argv(1);
309  if (Q_strnull(name))
310  name = _("New Team");
311 
312  char filename[MAX_OSPATH];
313  if (!GAME_TeamGetFreeFilename(filename, sizeof(filename))) {
314  UI_Popup(_("Note"), _("Error saving team. Too many teams!"));
315  return;
316  }
317 
318  if (!GAME_SaveTeam(filename, name))
319  UI_Popup(_("Note"), _("Error saving team. Check free disk space!"));
320 }
321 
325 static bool GAME_LoadTeam (const char* filename)
326 {
327  /* open file */
328  ScopedFile f;
329  FS_OpenFile(filename, &f, FILE_READ);
330  if (!f) {
331  Com_Printf("Couldn't open file '%s'\n", filename);
332  return false;
333  }
334 
335  const int clen = FS_FileLength(&f);
336  byte* const cbuf = Mem_PoolAllocTypeN(byte, clen, cl_genericPool);
337  if (FS_Read(cbuf, clen, &f) != clen) {
338  Com_Printf("Warning: Could not read %i bytes from savefile\n", clen);
339  Mem_Free(cbuf);
340  return false;
341  }
342 
343  teamSaveFileHeader_t header;
344  memcpy(&header, cbuf, sizeof(header));
345  /* swap all int values if needed */
346  header.version = LittleLong(header.version);
347  header.xmlSize = LittleLong(header.xmlSize);
348 
349  if (header.version != TEAM_SAVE_FILE_VERSION) {
350  Com_Printf("Invalid version number\n");
351  Mem_Free(cbuf);
352  return false;
353  }
354 
355  Com_Printf("Loading team (size %d / %i)\n", clen, header.xmlSize);
356 
357  xmlNode_t* topNode = XML_Parse((const char*)(cbuf + sizeof(header)));
358  Mem_Free(cbuf);
359  if (!topNode) {
360  Com_Printf("Error: Failure in loading the xml data!");
361  return false;
362  }
363 
364  xmlNode_t* node = XML_GetNode(topNode, SAVE_TEAM_ROOTNODE);
365  if (!node) {
366  mxmlDelete(topNode);
367  Com_Printf("Error: Failure in loading the xml data! (node '" SAVE_TEAM_ROOTNODE "' not found)\n");
368  return false;
369  }
370 
371  xmlNode_t* snode = XML_GetNode(node, SAVE_TEAM_NODE);
372  if (!snode) {
373  mxmlDelete(topNode);
374  Com_Printf("Error: Failure in loading the xml data! (node '" SAVE_TEAM_NODE "' not found)\n");
375  return false;
376  }
377  GAME_LoadTeamInfo(snode);
378 
379  snode = XML_GetNode(node, SAVE_TEAM_EQUIPMENT);
380  if (!snode) {
381  mxmlDelete(topNode);
382  Com_Printf("Error: Failure in loading the xml data! (node '" SAVE_TEAM_EQUIPMENT "' not found)\n");
383  return false;
384  }
385 
387  for (xmlNode_t* ssnode = XML_GetNode(snode, SAVE_TEAM_ITEM); ssnode; ssnode = XML_GetNextNode(ssnode, snode, SAVE_TEAM_ITEM)) {
388  const char* objID = XML_GetString(ssnode, SAVE_TEAM_ID);
389  const objDef_t* od = INVSH_GetItemByID(objID);
390 
391  if (od) {
392  ed->numItems[od->idx] = XML_GetInt(snode, SAVE_TEAM_NUM, 0);
393  ed->numItemsLoose[od->idx] = XML_GetInt(snode, SAVE_TEAM_NUMLOOSE, 0);
394  }
395  }
396 
397  Com_Printf("File '%s' loaded.\n", filename);
398 
399  mxmlDelete(topNode);
400 
401  return true;
402 }
403 
413 bool GAME_GetTeamFileName (unsigned int index, char* filename, size_t filenameLength)
414 {
415  const char* save;
416  /* we will loop the whole team save list, just because i don't want
417  * to specify the filename in the script api of this command. Otherwise
418  * one could upload everything with this command */
419  char buf[MAX_OSPATH];
420  GAME_GetRelativeSavePath(buf, sizeof(buf));
421  char pattern[MAX_OSPATH];
422  Com_sprintf(pattern, sizeof(pattern), "%s*.mpt", buf);
423  const int amount = FS_BuildFileList(pattern);
424  Com_DPrintf(DEBUG_CLIENT, "found %i entries for %s\n", amount, pattern);
425  while ((save = FS_NextFileFromFileList(pattern)) != nullptr) {
426  if (index == 0)
427  break;
428  index--;
429  }
430  FS_NextFileFromFileList(nullptr);
431  if (index > 0)
432  return false;
433  if (save == nullptr)
434  return false;
435  Com_sprintf(filename, filenameLength, "%s/%s", buf, save);
436  return true;
437 }
438 
439 bool GAME_LoadDefaultTeam (bool force)
440 {
442  if (!force)
443  return false;
445  }
446 
447  char filename[MAX_OSPATH];
448  if (!GAME_GetTeamFileName(cls.teamSaveSlotIndex, filename, sizeof(filename)))
449  return false;
450  if (GAME_LoadTeam(filename) && !GAME_IsTeamEmpty()) {
451  return true;
452  }
453 
455  return false;
456 }
457 
461 void GAME_LoadTeam_f (void)
462 {
463  if (Cmd_Argc() != 2) {
464  Com_Printf("Usage: %s <slotindex>\n", Cmd_Argv(0));
465  return;
466  }
467 
468  const int index = atoi(Cmd_Argv(1));
469 
470  char filename[MAX_OSPATH];
471  if (!GAME_GetTeamFileName(index, filename, sizeof(filename))) {
472  Com_Printf("Could not get the file for the index: %i\n", index);
473  return;
474  }
475  if (GAME_LoadTeam(filename) && !GAME_IsTeamEmpty()) {
477  } else {
479  }
480 }
481 
482 static void GAME_UpdateInventory (Inventory* inv, const equipDef_t* ed)
483 {
486  else
487  ui_inventory = nullptr;
488 
489  /* manage inventory */
491 }
492 
497 static void GAME_GetEquipment (void)
498 {
499  const char* equipmentName = Cvar_GetString("cl_equip");
500  /* search equipment definition */
501  const equipDef_t* edFromScript = INV_GetEquipmentDefinitionByID(equipmentName);
502 
504  *ed = *edFromScript;
505 
506  game_inventory.init();
507  GAME_UpdateInventory(&game_inventory, ed);
508 }
509 
515 {
516  /* reset description */
517  Cvar_Set("mn_itemname", "%s", "");
518  Cvar_Set("mn_item", "%s", "");
520 
521  int i = 0;
522  UI_ExecuteConfunc("team_soldierlist_clear");
524  UI_ExecuteConfunc("team_soldierlist_add %d %d \"%s\"", chr->ucn, characterActive[i], chr->name);
525  i++;
526  }
527 
529 }
530 
532 {
533  /* check syntax */
534  if (Cmd_Argc() < 2) {
535  Com_Printf("Usage: %s <num>\n", Cmd_Argv(0));
536  return;
537  }
538 
539  const int ucn = atoi(Cmd_Argv(1));
540  character_t* chr = nullptr;
542  if (ucn == chrTmp->ucn) {
543  chr = chrTmp;
544  break;
545  }
546  }
547  if (!chr)
548  return;
549 
550  /* update menu inventory */
551  if (ui_inventory && ui_inventory != &chr->inv) {
553  /* set 'old' CID_EQUIP to nullptr */
555  }
556  ui_inventory = &chr->inv;
557  /* set info cvars */
559 }
560 
569 static void GAME_SaveItem (xmlNode_t* p, const Item* item, containerIndex_t container, int x, int y)
570 {
571  assert(item->def() != nullptr);
572 
580  if (item->getAmmoLeft() > NONE_AMMO) {
583  }
584 }
585 
593 static void GAME_SaveInventory (xmlNode_t* p, const Inventory* inv)
594 {
595  const Container* cont = nullptr;
596  while ((cont = inv->getNextCont(cont, false))) {
597  Item* item = nullptr;
598  while ((item = cont->getNextItem(item))) {
600  GAME_SaveItem(s, item, cont->id, item->getX(), item->getY());
601  }
602  }
603 }
604 
614 static bool GAME_LoadItem (xmlNode_t* n, Item* item, containerIndex_t* container, int* x, int* y)
615 {
616  const char* itemID = XML_GetString(n, SAVE_INVENTORY_WEAPONID);
617  const char* contID = XML_GetString(n, SAVE_INVENTORY_CONTAINER);
618 
619  *x = XML_GetInt(n, SAVE_INVENTORY_X, 0);
620  *y = XML_GetInt(n, SAVE_INVENTORY_Y, 0);
621 
622  /* reset */
623  OBJZERO(*item);
624  int i;
625  for (i = 0; i < CID_MAX; i++) {
626  if (Q_streq(csi.ids[i].name, contID))
627  break;
628  }
629  *container = i;
630  if (i >= CID_MAX) {
631  Com_Printf("Invalid container id '%s'\n", contID);
632  return false;
633  }
634 
635  item->setDef(INVSH_GetItemByID(itemID));
636  if (item->def() == nullptr) {
637  return false;
638  }
642  if (item->getAmmoLeft() > NONE_AMMO) {
644  item->setAmmoDef(INVSH_GetItemByID(itemID));
645 
646  /* reset ammo count if ammunition (item) not found */
647  if (!item->ammoDef())
648  item->setAmmoLeft(NONE_AMMO);
649  /* no ammo needed - fire definitions are in the item */
650  } else if (!item->isReloadable()) {
651  item->setAmmoDef(item->def());
652  }
653 
654  return true;
655 }
656 
666 static void GAME_LoadInventory (xmlNode_t* p, Inventory* inv, int maxLoad)
667 {
669  Item item;
670  containerIndex_t container;
671  int x;
672  int y;
673 
674  GAME_LoadItem(s, &item, &container, &x, &y);
675  if (item.def() == nullptr)
676  continue;
677 
678  if (INVDEF(container)->temp)
679  Com_Error(ERR_DROP, "GAME_LoadInventory failed, tried to add '%s' to a temp container %i", item.def()->id, container);
680  /* ignore the overload for now */
681  if (!inv->canHoldItemWeight(CID_EQUIP, container, item, maxLoad))
682  Com_Printf("GAME_LoadInventory: Item %s exceeds weight capacity\n", item.def()->id);
683 
684  if (!cls.i.addToInventory(inv, &item, INVDEF(container), x, y, 1))
685  Com_Printf("Could not add item '%s' to inventory\n", item.def() ? item.def()->id : "nullptr");
686  }
687 }
688 
695 {
696  assert(chr);
698  /* Store the character data */
709  XML_AddInt(p, SAVE_CHARACTER_HP, chr->HP);
714 
715  const int implants = lengthof(chr->implants);
717  for (int i = 0; i < implants; i++) {
718  const implant_t& implant = chr->implants[i];
719  if (implant.def == nullptr)
720  continue;
721 
722  xmlNode_t* sImplant = XML_AddNode(sImplants, SAVE_CHARACTER_IMPLANT);
725  XML_AddString(sImplant, SAVE_CHARACTER_IMPLANT_IMPLANT, implant.def->id);
726  }
727 
729  /* Store wounds */
730  for (int k = 0; k < chr->teamDef->bodyTemplate->numBodyParts(); ++k) {
731  if (!chr->wounds.treatmentLevel[k])
732  continue;
733  xmlNode_t* sWound = XML_AddNode(sInjuries, SAVE_CHARACTER_WOUND);
734 
737  }
738 
739  const chrScoreGlobal_t* score = &chr->score;
740 
742  /* Store skills */
743  for (int k = 0; k <= SKILL_NUM_TYPES; k++) {
744  if (score->experience[k] || score->initialSkills[k]
745  || (k < SKILL_NUM_TYPES && score->skills[k])) {
746  xmlNode_t* sSkill = XML_AddNode(sScore, SAVE_CHARACTER_SKILLS);
747  const int initial = score->initialSkills[k];
748  const int experience = score->experience[k];
749 
751  XML_AddIntValue(sSkill, SAVE_CHARACTER_INITSKILL, initial);
752  XML_AddIntValue(sSkill, SAVE_CHARACTER_EXPERIENCE, experience);
753  if (k < SKILL_NUM_TYPES) {
754  const int skills = *(score->skills + k);
755  const int improve = skills - initial;
757  }
758  }
759  }
760  /* Store kills */
761  for (int k = 0; k < KILLED_NUM_TYPES; k++) {
762  if (score->kills[k] || score->stuns[k]) {
763  xmlNode_t* sKill = XML_AddNode(sScore, SAVE_CHARACTER_KILLS);
765  XML_AddIntValue(sKill, SAVE_CHARACTER_KILLED, score->kills[k]);
766  XML_AddIntValue(sKill, SAVE_CHARACTER_STUNNED, score->stuns[k]);
767  }
768  }
770  XML_AddInt(sScore, SAVE_CHARACTER_SCORE_RANK, score->rank);
771 
772  /* Store inventories */
774  GAME_SaveInventory(sInventory, &chr->inv);
775 
777  return true;
778 }
779 
786 {
787  bool success = true;
788 
789  /* Load the character data */
790  Q_strncpyz(chr->name, XML_GetString(p, SAVE_CHARACTER_NAME), sizeof(chr->name));
791  Q_strncpyz(chr->body, XML_GetString(p, SAVE_CHARACTER_BODY), sizeof(chr->body));
792  Q_strncpyz(chr->path, XML_GetString(p, SAVE_CHARACTER_PATH), sizeof(chr->path));
793  Q_strncpyz(chr->head, XML_GetString(p, SAVE_CHARACTER_HEAD), sizeof(chr->head));
794 
795  const int maxSkins = CL_GetActorSkinCount() - 1;
796  const int bodySkin = XML_GetInt(p, SAVE_CHARACTER_BDOY_SKIN, 0);
797  chr->bodySkin = std::min(maxSkins, bodySkin);
800  chr->ucn = XML_GetInt(p, SAVE_CHARACTER_UCN, 0);
801  chr->maxHP = XML_GetInt(p, SAVE_CHARACTER_MAXHP, 0);
802  chr->HP = XML_GetInt(p, SAVE_CHARACTER_HP, 0);
803  chr->STUN = XML_GetInt(p, SAVE_CHARACTER_STUN, 0);
806  chr->state = XML_GetInt(p, SAVE_CHARACTER_STATE, 0);
807 
808  /* Team-definition */
809  const char* s = XML_GetString(p, SAVE_CHARACTER_TEAMDEF);
811  if (!chr->teamDef)
812  return false;
813 
815  /* Load wounds */
816  for (xmlNode_t* sWound = XML_GetNode(sInjuries, SAVE_CHARACTER_WOUND); sWound; sWound = XML_GetNextNode(sWound, sInjuries, SAVE_CHARACTER_WOUND)) {
817  const char* bodyPartId = XML_GetString(sWound, SAVE_CHARACTER_WOUNDEDPART);
818  short bodyPart;
819 
820  if (bodyPartId[0] != '\0') {
821  for (bodyPart = 0; bodyPart < chr->teamDef->bodyTemplate->numBodyParts(); ++bodyPart)
822  if (Q_streq(chr->teamDef->bodyTemplate->id(bodyPart), bodyPartId))
823  break;
824  } else {
826  Com_Printf("GAME_LoadCharacter: Body part id not found while loading character wounds, must be an old save\n");
827  bodyPart = XML_GetInt(sWound, SAVE_CHARACTER_WOUNDTYPE, NONE);
828  }
829  if (bodyPart < 0 || bodyPart >= chr->teamDef->bodyTemplate->numBodyParts()) {
830  Com_Printf("GAME_LoadCharacter: Invalid body part id '%s' for %s (ucn: %i)\n", bodyPartId, chr->name, chr->ucn);
831  return false;
832  }
833  chr->wounds.treatmentLevel[bodyPart] = XML_GetInt(sWound, SAVE_CHARACTER_WOUNDSEVERITY, 0);
834  }
835 
837  /* Load implants */
838  int implantCnt = 0;
839  for (xmlNode_t* sImplant = XML_GetNode(sImplants, SAVE_CHARACTER_IMPLANT); sImplant; sImplant = XML_GetNextNode(sImplant, sImplants, SAVE_CHARACTER_IMPLANT)) {
840  implant_t& implant = chr->implants[implantCnt++];
843  const char* implantDefID = XML_GetString(sImplants, SAVE_CHARACTER_IMPLANT_IMPLANT);
844  implant.def = INVSH_GetImplantByID(implantDefID);
845  }
846 
848 
850  /* Load Skills */
851  for (xmlNode_t* sSkill = XML_GetNode(sScore, SAVE_CHARACTER_SKILLS); sSkill; sSkill = XML_GetNextNode(sSkill, sScore, SAVE_CHARACTER_SKILLS)) {
852  int idx;
853  const char* type = XML_GetString(sSkill, SAVE_CHARACTER_SKILLTYPE);
854 
856  Com_Printf("Invalid skill type '%s' for %s (ucn: %i)\n", type, chr->name, chr->ucn);
857  success = false;
858  break;
859  }
860 
861  chr->score.initialSkills[idx] = XML_GetInt(sSkill, SAVE_CHARACTER_INITSKILL, 0);
862  chr->score.experience[idx] = XML_GetInt(sSkill, SAVE_CHARACTER_EXPERIENCE, 0);
863  if (idx < SKILL_NUM_TYPES) {
864  chr->score.skills[idx] = chr->score.initialSkills[idx];
865  chr->score.skills[idx] += XML_GetInt(sSkill, SAVE_CHARACTER_SKILLIMPROVE, 0);
866  }
867  }
868 
869  if (!success) {
871  return false;
872  }
873 
874  /* Load kills */
875  for (xmlNode_t* sKill = XML_GetNode(sScore, SAVE_CHARACTER_KILLS); sKill; sKill = XML_GetNextNode(sKill, sScore, SAVE_CHARACTER_KILLS)) {
876  int idx;
877  const char* type = XML_GetString(sKill, SAVE_CHARACTER_KILLTYPE);
878 
880  Com_Printf("Invalid kill type '%s' for %s (ucn: %i)\n", type, chr->name, chr->ucn);
881  success = false;
882  break;
883  }
884  chr->score.kills[idx] = XML_GetInt(sKill, SAVE_CHARACTER_KILLED, 0);
885  chr->score.stuns[idx] = XML_GetInt(sKill, SAVE_CHARACTER_STUNNED, 0);
886  }
887 
888  if (!success) {
890  return false;
891  }
892 
894  chr->score.rank = XML_GetInt(sScore, SAVE_CHARACTER_SCORE_RANK, -1);
895 
896  cls.i.destroyInventory(&chr->inv);
898  GAME_LoadInventory(sInventory, &chr->inv, GAME_GetChrMaxLoad(chr));
899 
901 
902  const char* body = CHRSH_CharGetBody(chr);
903  const char* head = CHRSH_CharGetHead(chr);
904  if (!R_ModelExists(head) || !R_ModelExists(body)) {
905  if (!Com_GetCharacterModel(chr))
906  return false;
907  }
908 
909  return true;
910 }
#define SAVE_INVENTORY_ROTATED
equipDef_t * GAME_GetEquipmentDefinition(void)
Definition: cl_game.cpp:1614
bool Q_strnull(const char *string)
Definition: shared.h:138
bool R_ModelExists(const char *name)
Definition: r_model.cpp:177
const char * Cmd_Argv(int arg)
Returns a given argument.
Definition: cmd.cpp:516
void GAME_GenerateTeam(const char *teamDefID, const equipDef_t *ed, int teamMembers)
Definition: cl_game.cpp:276
void CL_UpdateCharacterValues(const character_t *chr)
Definition: cl_team.cpp:218
const char * FS_NextFileFromFileList(const char *files)
Returns the next file that is found in the virtual filesystem identified by the given file pattern...
Definition: files.cpp:1079
char path[MAX_VAR]
Definition: chr_shared.h:372
#define SAVE_INVENTORY_INVENTORY
#define SAVE_CHARACTER_WOUNDTYPE
void destroyInventory(Inventory *const inv)
Destroys inventory.
Definition: inventory.cpp:521
#define CID_EQUIP
Definition: inv_shared.h:56
void setAmmoLeft(int value)
Definition: inv_shared.h:441
bool Com_GetConstIntFromNamespace(const char *space, const char *variable, int *value)
Searches whether a given value was registered as a string to int mapping.
Definition: scripts.cpp:103
bool GAME_SaveCharacter(xmlNode_t *p, const character_t *chr)
saves a character to a given xml node
chrScoreGlobal_t score
Definition: chr_shared.h:387
int FS_CheckFile(const char *fmt,...)
Just returns the filelength and -1 if the file wasn't found.
Definition: files.cpp:298
#define SAVE_TEAM_ITEM
Definition: save_team.h:33
const objDef_t * INVSH_GetItemByID(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn't found.
Definition: inv_shared.cpp:282
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
char id[MAX_VAR]
Definition: chr_shared.h:298
int FS_OpenFile(const char *filename, qFILE *file, filemode_t mode)
Finds and opens the file in the search path.
Definition: files.cpp:162
InventoryInterface i
Definition: client.h:101
#define SAVE_CHARACTER_PATH
static void GAME_LoadTeamInfo(xmlNode_t *p)
Loads the wholeTeam from the xml file.
#define SAVE_CHARACTER_MORALE
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functi...
Definition: shared.cpp:410
int teamSaveSlotIndex
Definition: client.h:78
int getAmmoLeft() const
Definition: inv_shared.h:466
const teamDef_t * teamDef
Definition: chr_shared.h:394
int installedTime
Definition: chr_shared.h:363
void resetContainer(const containerIndex_t idx)
Definition: inv_shared.h:554
int experience[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:120
#define SAVE_TEAM_CHARACTER
Definition: save_team.h:30
void GAME_TeamDelete_f(void)
Removes a user created team.
const char * GAME_GetAbsoluteSavePath(char *buf, size_t bufSize)
Definition: cl_game.cpp:1608
#define _(String)
Definition: cl_shared.h:43
int XML_GetInt(xmlNode_t *parent, const char *name, const int defaultval)
retrieve an Int attribute from an XML Node
Definition: xml.cpp:308
#define SAVE_CHARACTER_KILLS
void * data
Definition: list.h:31
#define SAVE_CHARACTER_HEAD_SKIN
#define SAVE_CHARACTER_IMPLANTS
csi_t csi
Definition: common.cpp:39
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
int removedTime
Definition: chr_shared.h:364
bool FS_FileExists(const char *filename,...)
Checks whether a file exists (not in virtual filesystem)
Definition: files.cpp:1581
#define SAVE_CHARACTER_SKILLS
const char * filename
Definition: ioapi.h:41
#define SAVE_CHARACTER_IMPLANT_IMPLANT
#define SAVE_CHARACTER_TEAMDEF
Shared game type headers.
#define SAVE_CHARACTER_HP
const implantDef_t * def
Definition: chr_shared.h:362
#define SAVE_CHARACTER_MAXHP
const objDef_t * def(void) const
Definition: inv_shared.h:469
unsigned int CL_GetActorSkinCount(void)
Get number of registered actorskins.
Definition: cl_team.cpp:63
void FS_RemoveFile(const char *osPath)
Definition: files.cpp:1690
invDef_t ids[MAX_INVDEFS]
Definition: q_shared.h:524
static bool characterActive[MAX_ACTIVETEAM]
#define TEAM_SAVE_FILE_VERSION
bool Com_GetCharacterModel(character_t *chr)
Definition: scripts.cpp:2380
int stuns[KILLED_NUM_TYPES]
Definition: chr_shared.h:127
void UI_ContainerNodeUpdateEquipment(Inventory *inv, const equipDef_t *ed)
Fills the ground container of the ui_inventory with unused items from a given equipment definition...
#define MAX_OSPATH
Definition: filesys.h:44
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
int numODs
Definition: q_shared.h:518
const char * CHRSH_CharGetBody(const character_t *const chr)
Returns the body model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:296
void setDef(const objDef_t *objDef)
Definition: inv_shared.h:444
int FS_BuildFileList(const char *fileList)
Build a filelist.
Definition: files.cpp:960
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
char name[MAX_VAR]
Definition: inv_shared.h:372
void Com_RegisterConstList(const constListEntry_t constList[])
Registers a list of string aliases.
Definition: scripts.cpp:253
void GAME_TeamSlotComments_f(void)
Reads the comments from team files.
const equipDef_t * INV_GetEquipmentDefinitionByID(const char *name)
Gets equipment definition by id.
#define SAVE_CHARACTER_INITSKILL
#define SAVE_CHARACTER_UCN
#define SAVE_CHARACTER_STATE
#define SAVE_INVENTORY_Y
#define SAVE_TEAM_NODE
Definition: save_team.h:29
voidpf void * buf
Definition: ioapi.h:42
const teamDef_t * Com_GetTeamDefinitionByID(const char *team)
Returns the teamDef pointer for the searched team id - or nullptr if not found in the teamDef array...
Definition: scripts.cpp:2367
int GAME_GetChrMaxLoad(const character_t *chr)
Returns the max weight the given character can carry.
Definition: cl_game.cpp:1768
void GAME_AutoTeam_f(void)
bool GAME_GetTeamFileName(unsigned int index, char *filename, size_t filenameLength)
Get the filename for the xth team in the file system.
#define SAVE_TEAM_NUM
Definition: save_team.h:35
static void GAME_SaveInventory(xmlNode_t *p, const Inventory *inv)
Save callback for savegames in XML Format.
bool GAME_LoadCharacter(xmlNode_t *p, character_t *chr)
Loads a character from a given xml node.
int LIST_Count(const linkedList_t *list)
Definition: list.cpp:344
#define SAVE_CHARACTER_GENDER
memPool_t * cl_genericPool
Definition: cl_main.cpp:86
static void GAME_SaveItem(xmlNode_t *p, const Item *item, containerIndex_t container, int x, int y)
Save one item.
const char * XML_GetString(xmlNode_t *parent, const char *name)
retrieve a String attribute from an XML Node
Definition: xml.cpp:350
#define SAVE_INVENTORY_CONTAINER
#define SAVE_INVENTORY_AMOUNT
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
#define SAVE_CHARACTER_SCORE_ASSIGNEDMISSIONS
#define xmlNode_t
Definition: xml.h:24
xmlNode_t * XML_AddNode(xmlNode_t *parent, const char *name)
add a new node to the XML tree
Definition: xml.cpp:277
client_static_t cls
Definition: cl_main.cpp:83
item instance data, with linked list capability
Definition: inv_shared.h:402
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
inventory definition with all its containers
Definition: inv_shared.h:525
bool LIST_Remove(linkedList_t **list, const void *data)
Definition: list.cpp:213
void XML_AddInt(xmlNode_t *parent, const char *name, int value)
add an Int attribute to the XML Node
Definition: xml.cpp:183
#define SAVE_INVENTORY_MUNITIONID
void XML_AddString(xmlNode_t *parent, const char *name, const char *value)
add a String attribute to the XML Node
Definition: xml.cpp:36
#define ERR_DROP
Definition: common.h:211
void GAME_ActorSelect_f(void)
#define SAVE_CHARACTER_SKILLIMPROVE
int treatmentLevel[BODYPART_MAXTYPE]
Definition: chr_shared.h:352
#define SAVE_CHARACTER_WOUNDSEVERITY
#define DEBUG_CLIENT
Definition: defines.h:59
void UI_ResetData(int dataId)
Reset a shared data. Type became NONE and value became nullptr.
Definition: ui_data.cpp:212
GLsizei size
Definition: r_gl.h:152
#define OBJZERO(obj)
Definition: shared.h:178
XML tag constants for savegame.
static bool GAME_LoadItem(xmlNode_t *n, Item *item, containerIndex_t *container, int *x, int *y)
Load one item.
#define SAVE_CHARACTER_IMPLANT_INSTALLEDTIME
int kills[KILLED_NUM_TYPES]
Definition: chr_shared.h:126
void GAME_ResetCharacters(void)
Reset all characters in the static character array.
Definition: cl_game.cpp:243
static void GAME_SaveTeamInfo(xmlNode_t *p)
Stores the wholeTeam into a xml structure.
#define SAVE_INVENTORY_X
int Cmd_Argc(void)
Return the number of arguments of the current command. "command parameter" will result in a argc of 2...
Definition: cmd.cpp:505
struct teamSaveFileHeader_s teamSaveFileHeader_t
char head[MAX_VAR]
Definition: chr_shared.h:374
int initialSkills[SKILL_NUM_TYPES+1]
Definition: chr_shared.h:123
#define SAVE_TEAM_EQUIPMENT
Definition: save_team.h:32
cgame team management headers.
#define SAVE_INVENTORY_WEAPONID
#define SAVE_CHARACTER_KILLED
int FS_WriteFile(const void *buffer, size_t len, const char *filename)
Definition: files.cpp:1544
XML tag constants for team savegame.
static void GAME_GetEquipment(void)
Get the equipment definition (from script files) for the current selected team and updates the equipm...
const implantDef_t * INVSH_GetImplantByID(const char *id)
Returns the implant that belongs to the given id or nullptr if it wasn't found.
Definition: inv_shared.cpp:326
void Com_DPrintf(int level, const char *fmt,...)
A Com_Printf that only shows up if the "developer" cvar is set.
Definition: common.cpp:398
static Inventory game_inventory
int getAmount() const
Definition: inv_shared.h:463
#define INVDEF(containerID)
Definition: cl_shared.h:47
Item * getEquipContainer() const
Definition: inv_shared.cpp:980
implant_t implants[MAX_CHARACTER_IMPLANTS]
Definition: chr_shared.h:399
void GAME_LoadTeam_f(void)
Loads the selected teamslot.
Item * getNextItem(const Item *prev) const
Definition: inv_shared.cpp:671
#define SAVE_CHARACTER_IMPLANT_REMOVETIME
#define SAVE_TEAM_ROOTNODE
Definition: save_team.h:27
void init()
Definition: inv_shared.cpp:703
#define SAVE_TEAM_NUMLOOSE
Definition: save_team.h:36
linkedList_t * chrDisplayList
List of currently displayed or equipable characters.
Definition: cl_team.cpp:38
const char * CHRSH_CharGetHead(const character_t *const chr)
Returns the head model for the soldiers for armoured and non armoured soldiers.
Definition: chr_shared.cpp:318
bool GAME_LoadDefaultTeam(bool force)
QGL_EXTERN GLuint index
Definition: r_gl.h:110
#define SAVE_CHARACTER_SKILLTYPE_NAMESPACE
character_t * GAME_GetCharacter(int index)
Returns a character that can be used to store the game type specific character values.
Definition: cl_game.cpp:196
const char * id
Definition: inv_shared.h:102
bool canHoldItemWeight(containerIndex_t from, containerIndex_t to, const Item &item, int maxWeight) const
Check that adding an item to the inventory won't exceed the max permitted weight. ...
Definition: inv_shared.cpp:919
#define SAVE_CHARACTER_KILLTYPE
#define SAVE_CHARACTER_BDOY_SKIN
woundInfo_t wounds
Definition: chr_shared.h:383
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
int32_t containerIndex_t
Definition: inv_shared.h:46
#define SAVE_TEAM_ID
Definition: save_team.h:34
int FS_FileLength(qFILE *f)
Returns the size of a given file or -1 if no file is opened.
Definition: files.cpp:91
#define SAVE_CHARACTER_EXPERIENCE
Structure of all stats collected for an actor over time.
Definition: chr_shared.h:119
const char * Com_GetConstVariable(const char *space, int value)
Searches the mapping variable for a given integer value and a namespace.
Definition: scripts.cpp:122
#define Mem_PoolAllocTypeN(type, n, pool)
Definition: mem.h:42
Item * addToInventory(Inventory *const inv, const Item *const item, const invDef_t *container, int x, int y, int amount) __attribute__((warn_unused_result))
Add an item to a specified container in a given inventory.
Definition: inventory.cpp:91
void GAME_SaveTeam_f(void)
Stores a team in a specified teamslot.
void GAME_ToggleActorForTeam_f(void)
This will activate/deactivate the actor for the team.
#define SAVE_CHARACTER_IMPLANT
int FS_Read(void *buffer, int len, qFILE *f)
Definition: files.cpp:371
short numBodyParts(void) const
Definition: chr_shared.cpp:388
bool isReloadable() const
Definition: inv_shared.h:479
Inventory * ui_inventory
static bool GAME_SaveTeam(const char *filename, const char *name)
Saves a team in xml format.
#define SAVE_CHARACTER_STUNNED
bool GAME_TeamGetFreeFilename(char *filename, size_t size)
Searches a free team filename.
size_t GAME_GetCharacterArraySize(void)
Definition: cl_game.cpp:226
QGL_EXTERN GLint i
Definition: r_gl.h:113
#define SAVE_CHARACTER_WOUND
#define SAVE_INVENTORY_AMMO
void GAME_UpdateTeamMenuParameters_f(void)
Displays actor info and equipment and unused items in proper (filter) category.
const Container * getNextCont(const Container *prev, bool inclTemp=false) const
Definition: inv_shared.cpp:722
int numItems[MAX_OBJDEFS]
Definition: inv_shared.h:608
void GAME_AutoTeam(const char *equipmentDefinitionID, int teamMembers)
bool GAME_IsTeamEmpty(void)
Definition: cl_game.cpp:388
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
void setAmmoDef(const objDef_t *od)
Definition: inv_shared.h:435
const BodyData * bodyTemplate
Definition: chr_shared.h:339
#define CID_MAX
Definition: inv_shared.h:57
#define Mem_Free(ptr)
Definition: mem.h:35
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition: shared.cpp:475
#define MAX_ACTIVETEAM
Definition: defines.h:41
#define NONE_AMMO
Definition: defines.h:69
const objDef_t * ammoDef(void) const
Definition: inv_shared.h:460
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it's safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
#define SAVE_CHARACTER_STUN
xmlNode_t * XML_GetNode(xmlNode_t *parent, const char *name)
Get first Node of the XML tree by name.
Definition: xml.cpp:487
void setAmount(int value)
Definition: inv_shared.h:438
byte numItemsLoose[MAX_OBJDEFS]
Definition: inv_shared.h:609
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
Definition: inv_shared.cpp:266
Inventory inv
Definition: chr_shared.h:392
int getX() const
Definition: inv_shared.h:454
#define SAVE_INVENTORY_ITEM
char name[MAX_VAR]
Definition: chr_shared.h:371
static void GAME_UpdateInventory(Inventory *inv, const equipDef_t *ed)
#define SAVE_CHARACTER_SKILLTYPE
#define lengthof(x)
Definition: shared.h:105
#define SAVE_CHARACTER_HEAD
const char * id(void) const
Definition: chr_shared.cpp:358
int rotated
Definition: inv_shared.h:412
cvar_t * Cvar_Set(const char *varName, const char *value,...)
Sets a cvar value.
Definition: cvar.cpp:615
const char * Cvar_GetString(const char *varName)
Returns the value of cvar as string.
Definition: cvar.cpp:210
#define NONE
Definition: defines.h:68
#define Q_streq(a, b)
Definition: shared.h:136
#define SAVE_CHARACTER_FIELDSIZE
bool LIST_IsEmpty(const linkedList_t *list)
Checks whether the given list is empty.
Definition: list.cpp:335
static void GAME_LoadInventory(xmlNode_t *p, Inventory *inv, int maxLoad)
Load callback for savegames in XML Format.
void XML_AddIntValue(xmlNode_t *parent, const char *name, int value)
add a non-zero Int attribute to the XML Node
Definition: xml.cpp:195
#define SAVE_CHARACTER_SCORE_RANK
const char * GAME_GetRelativeSavePath(char *buf, size_t bufSize)
Definition: cl_game.cpp:1602
void UI_Popup(const char *title, const char *text)
Popup on geoscape.
Definition: ui_popup.cpp:47
uint8_t byte
Definition: ufotypes.h:34
void GAME_SaveTeamState_f(void)
Will remove those actors that should not be used in the team.
#define NO_TEAM_SLOT_LOADED
Definition: cl_game_team.h:30
actorSizeEnum_t fieldSize
Definition: chr_shared.h:390
const char * GAME_GetTeamDef(void)
Definition: cl_game.cpp:1442
int skills[SKILL_NUM_TYPES]
Definition: chr_shared.h:122
char body[MAX_VAR]
Definition: chr_shared.h:373
#define SAVE_CHARACTER_WOUNDEDPART
xmlNode_t * XML_GetNextNode(xmlNode_t *current, xmlNode_t *parent, const char *name)
Get next Node of the XML tree by name.
Definition: xml.cpp:499
#define SAVE_CHARACTER_KILLTYPE_NAMESPACE
void setContainer(const containerIndex_t idx, Item *cont)
Definition: inv_shared.h:550
static void GAME_UpdateActiveTeamList(void)
static const constListEntry_t saveCharacterConstants[]
XML tag constants for savegame.
const char * id
Definition: inv_shared.h:268
#define SAVE_CHARACTER_INJURIES
int getY() const
Definition: inv_shared.h:457
bool Com_UnregisterConstList(const constListEntry_t constList[])
Unregisters a list of string aliases.
Definition: scripts.cpp:237
#define SAVE_CHARACTER_BODY
static bool GAME_LoadTeam(const char *filename)
Load a team from an xml file.
void LIST_AddPointer(linkedList_t **listDest, void *data)
Adds just a pointer to a new or to an already existing linked list.
Definition: list.cpp:153
xmlNode_t * XML_Parse(const char *buffer)
Definition: xml.cpp:531
#define LittleLong(X)
Definition: byte.h:37
void UI_ExecuteConfunc(const char *fmt,...)
Executes confunc - just to identify those confuncs in the code - in this frame.
Definition: ui_main.cpp:110
#define SAVE_CHARACTER_NAME
Describes a character with all its attributes.
Definition: chr_shared.h:369
#define SAVE_CHARACTER_SCORES