UFO: Alien Invasion
Doxygen documentation generating
scripts.cpp
Go to the documentation of this file.
1 
7 /*
8 Copyright (C) 2002-2023 UFO: Alien Invasion.
9 
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
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 "scripts.h"
27 #include "../shared/parse.h"
28 #include "../shared/keyvaluepair.h"
29 #include "../game/inventory.h"
30 #include "../client/cl_screen.h"
31 
32 #define CONSTNAMEINT_HASH_SIZE 32
33 
34 #define MAX_CONSTNAMEINT_NAME 32
35 
39 typedef struct com_constNameInt_s {
41  char* fullname;
42  int value;
43  struct com_constNameInt_s* hash_next;
44  struct com_constNameInt_s* next;
46 
51 
57 static const char* Com_ConstIntGetVariable (const char* name)
58 {
59  const char* space = strstr(name, "::");
60  if (space)
61  return space + 2;
62  return name;
63 }
64 
74 bool Com_GetConstInt (const char* name, int* value)
75 {
76  const char* variable = Com_ConstIntGetVariable(name);
77 
78  /* if the alias already exists */
79  const unsigned int hash = Com_HashKey(variable, CONSTNAMEINT_HASH_SIZE);
81  if (Q_streq(variable, a->name)) {
82  if (!a->fullname || variable == name || Q_streq(a->fullname, name)) {
83  *value = a->value;
84  return true;
85  }
86  }
87  }
88 
89  return false;
90 }
91 
103 bool Com_GetConstIntFromNamespace (const char* space, const char* variable, int* value)
104 {
105  if (Q_strnull(variable))
106  return false;
107 
108  if (Q_strnull(space))
109  return Com_GetConstInt(variable, value);
110 
111  return Com_GetConstInt(va("%s::%s", space, variable), value);
112 }
113 
122 const char* Com_GetConstVariable (const char* space, int value)
123 {
124  const size_t namespaceLength = strlen(space);
125 
127  while (a) {
128  if (a->value == value && a->fullname) {
129  if (!strncmp(a->fullname, space, namespaceLength))
130  return a->name;
131  }
132  a = a->next;
133  }
134 
135  return nullptr;
136 }
137 
148 {
149  com_constNameInt_t* prev = nullptr;
150 
152  while (a) {
153  if (!a->fullname || !Q_streq(a->fullname, name)) {
154  prev = a;
155  a = a->next;
156  continue;
157  }
158 
159  if (prev)
160  prev->next = a->next;
161  else
162  com_constNameInt = a->next;
163 
164  prev = nullptr;
165 
166  const char* variable = Com_ConstIntGetVariable(name);
167  const unsigned int hash = Com_HashKey(variable, CONSTNAMEINT_HASH_SIZE);
168  for (com_constNameInt_t* b = com_constNameInt_hash[hash]; b; prev = b, b = b->hash_next) {
169  if (!b->fullname)
170  continue;
171  if (!Q_streq(name, b->fullname))
172  continue;
173 
174  if (prev)
175  prev->hash_next = b->hash_next;
176  else
178  break;
179  }
180  Mem_Free(a->fullname);
181  Mem_Free(a);
182  return true;
183  }
184  return false;
185 }
186 
198 void Com_RegisterConstInt (const char* name, int value)
199 {
201  const char* variable = Com_ConstIntGetVariable(name);
202 
203  /* if the alias already exists, reuse it */
204  const unsigned int hash = Com_HashKey(variable, CONSTNAMEINT_HASH_SIZE);
205  for (a = com_constNameInt_hash[hash]; a; a = a->hash_next) {
206  if (a->fullname) {
207  if (Q_streq(a->fullname, name))
208  break;
209  } else if (!strncmp(variable, a->name, sizeof(a->name))) {
210  break;
211  }
212  }
213 
214  if (a) {
215  Com_Printf("Com_RegisterConstInt: Const string already defined. '%s = %d' is not set.\n", name, value);
216  return;
217  }
218 
220  Q_strncpyz(a->name, variable, sizeof(a->name));
221  if (!Q_streq(variable, name))
222  a->fullname = Mem_StrDup(name);
223  a->next = com_constNameInt;
224  /* com_constNameInt_hash should be null on the first run */
227  com_constNameInt = a;
228  a->value = value;
229 }
230 
238 {
239  bool state = true;
240 
241  for (int i = 0; constList[i].name != nullptr; i++)
242  state &= Com_UnregisterConstVariable(constList[i].name);
243 
244  return state;
245 }
246 
253 void Com_RegisterConstList (const constListEntry_t constList[])
254 {
255  for (int i = 0; constList[i].name != nullptr; i++)
256  Com_RegisterConstInt(constList[i].name, constList[i].value);
257 }
258 
263 static int Com_FindNameType (const char* nameType)
264 {
265  for (int i = 0; i < NAME_NUM_TYPES; i++) {
266  if (Q_streq(nameType, name_strings[i])) {
267  return i;
268  }
269  }
270  return -1;
271 }
272 
277 const char* Com_EParse (const char** text, const char* errhead, const char* errinfo, char* target, size_t size)
278 {
279  const char* token = Com_Parse(text, target, size);
280  if (!*text) {
281  if (errinfo)
282  Com_Printf("%s \"%s\")\n", errhead, errinfo);
283  else
284  Com_Printf("%s\n", errhead);
285 
286  return nullptr;
287  }
288 
289  return token;
290 }
291 
292 static bool versionParsed;
293 
294 static void Com_ParseVersion (const char* version)
295 {
296  if (!versionParsed) {
297  if (!Q_streq(version, UFO_VERSION))
298  Sys_Error("You are mixing versions of the binary (" UFO_VERSION ") and the script (%s) files.", version);
299  } else {
300  Sys_Error("More than one version string found in the script files.");
301  }
302 
303  versionParsed = true;
304 }
305 
310 const char* const vt_names[] = {
311  "",
312  "bool",
313  "char",
314  "int",
315  "int2",
316  "float",
317  "pos",
318  "vector",
319  "color",
320  "string",
321  "translation_string",
322  "longstring",
323  "align",
324  "blend",
325  "style",
326  "fade",
327  "shapes",
328  "shapeb",
329  "damage",
330  "relabs",
331  "hunk_string",
332  "team",
333  "ufo",
334  "ufocrashed",
335  "aircrafttype",
336  "list"
337 };
339 
340 const char* const align_names[] = {
341  "ul", "uc", "ur", "cl", "cc", "cr", "ll", "lc", "lr", "ul_rsl", "uc_rsl", "ur_rsl", "cl_rsl", "cc_rsl", "cr_rsl", "ll_rsl", "lc_rsl", "lr_rsl"
342 };
344 
345 const char* const blend_names[] = {
346  "replace", "one", "blend", "add", "filter", "invfilter"
347 };
349 
350 const char* const style_names[] = {
351  "facing", "rotated", "beam", "line", "axis", "circle"
352 };
354 
355 const char* const fade_names[] = {
356  "none", "in", "out", "sin", "saw"
357 };
359 
361 static const size_t vt_sizes[] = {
362  0, /* V_NULL */
363  sizeof(bool), /* V_BOOL */
364  sizeof(char), /* V_CHAR */
365  sizeof(int), /* V_INT */
366  2 * sizeof(int), /* V_INT2 */
367  sizeof(float), /* V_FLOAT */
368  sizeof(vec2_t), /* V_POS */
369  sizeof(vec3_t), /* V_VECTOR */
370  sizeof(vec4_t), /* V_COLOR */
371  0, /* V_STRING */
372  0, /* V_TRANSLATION_STRING */
373  0, /* V_LONGSTRING */
374  sizeof(align_t), /* V_ALIGN */
375  sizeof(blend_t), /* V_BLEND */
376  sizeof(style_t), /* V_STYLE */
377  sizeof(fade_t), /* V_FADE */
378  sizeof(int), /* V_SHAPE_SMALL */
379  0, /* V_SHAPE_BIG */
380  sizeof(byte), /* V_DAMAGE */
381  sizeof(float), /* V_RELABS */
382  0, /* V_HUNK_STRING */
383  sizeof(int), /* V_TEAM */
384  sizeof(ufoType_t), /* V_UFO */
385  sizeof(ufoType_t), /* V_UFOCRASHED */
386  sizeof(humanAircraftType_t), /* V_AIRCRAFTTYPE */
387  0 /* V_LIST */
388 };
390 
392 static const size_t vt_aligns[] = {
393  0, /* V_NULL */
394  sizeof(bool), /* V_BOOL */
395  sizeof(char), /* V_CHAR */
396  sizeof(int), /* V_INT */
397  sizeof(int), /* V_INT2 */
398  sizeof(float), /* V_FLOAT */
399  sizeof(vec_t), /* V_POS */
400  sizeof(vec_t), /* V_VECTOR */
401  sizeof(vec_t), /* V_COLOR */
402  sizeof(char), /* V_STRING */
403  sizeof(char), /* V_TRANSLATION_STRING */
404  sizeof(char), /* V_LONGSTRING */
405  sizeof(align_t), /* V_ALIGN */
406  sizeof(blend_t), /* V_BLEND */
407  sizeof(style_t), /* V_STYLE */
408  sizeof(fade_t), /* V_FADE */
409  sizeof(int), /* V_SHAPE_SMALL */
410  sizeof(uint32_t), /* V_SHAPE_BIG */
411  sizeof(byte), /* V_DAMAGE */
412  sizeof(float), /* V_RELABS */
413  sizeof(char), /* V_HUNK_STRING */
414  sizeof(int), /* V_TEAM */
415  sizeof(ufoType_t), /* V_UFO */
416  sizeof(ufoType_t), /* V_UFOCRASHED */
417  sizeof(humanAircraftType_t), /* V_AIRCRAFTTYPE */
418  sizeof(void*)
419 };
421 
422 static char parseErrorMessage[256];
423 
428 const char* Com_GetLastParseError (void)
429 {
430  return parseErrorMessage;
431 }
432 
437 void* Com_AlignPtr (const void* memory, valueTypes_t type)
438 {
439  const size_t align = vt_aligns[type];
440  assert(memory != nullptr);
441  if (align == V_NULL)
442  Sys_Error("Com_AlignPtr: can't align V_NULL");
443  if (type >= V_NUM_TYPES)
444  Sys_Error("Com_AlignPtr: type hit V_NUM_TYPES");
445  return ALIGN_PTR(memory, align);
446 }
447 
448 typedef enum {
454 
455 static const char* const craftTypeIds[CRAFT_MAX * 2] = {
456  "drop",
457  "inter",
458  "ufo",
459  /* For crashed aircraft names */
460  "crash_drop",
461  "crash_inter",
462  "crash"
463 };
464 
469 static const char* ufoIdsTable[UFO_MAX];
470 static const char* dropIdsTable[DROPSHIP_MAX];
471 static const char* interIdsTable[INTERCEPTOR_MAX];
472 static const char** const aircraftIdsTable[CRAFT_MAX] = {
473  dropIdsTable,
476 };
477 static short aircraftIdsNum[CRAFT_MAX];
478 
479 static const char* Com_GetAircraftDef (aircraftType_t type, short idNum)
480 {
481  if (idNum >= 0 && idNum < aircraftIdsNum[type]) {
482  return aircraftIdsTable[type][idNum];
483  }
484  return nullptr;
485 }
486 
487 static short Com_GetAircraftIdNum (aircraftType_t type, const char* idString)
488 {
489  const char* const id = Q_strstart(idString, va("craft_%s_", craftTypeIds[type]));
490  if (!Q_strnull(id)) {
491  for (int i = 0; i < aircraftIdsNum[type]; i++)
492  if (Q_streq(id, aircraftIdsTable[type][i]))
493  return i;
494  }
495 
496  return AIRCRAFT_NONE;
497 }
498 
499 static void Com_GetAircraftIdStr (aircraftType_t type, short idNum, char* outStr, const size_t size)
500 {
501  const char* uDef = Com_GetAircraftDef(type, idNum);
502  if (uDef)
503  Com_sprintf(outStr, size, "craft_%s_%s", craftTypeIds[type], uDef);
504  else
505  outStr[0] = 0;
506 }
507 
508 static short Com_GetCrashedAircraftIdNum (aircraftType_t type, const char* idString)
509 {
510  const char* const id = Q_strstart(idString, va("craft_%s_",craftTypeIds[type + CRAFT_MAX]));
511  if (!Q_strnull(id)) {
512  for (int i = 0; i < aircraftIdsNum[type]; i++)
513  if (Q_streq(id, aircraftIdsTable[type][i]))
514  return i;
515  }
516 
517  return AIRCRAFT_NONE;
518 }
519 
520 static void Com_GetCrashedAircraftIdStr (aircraftType_t type, short idNum, char* outStr, const size_t size)
521 {
522  const char* uDef = Com_GetAircraftDef(type, idNum);
523  if (uDef)
524  Com_sprintf(outStr, size, "craft_%s_%s", craftTypeIds[type + CRAFT_MAX], uDef);
525  else
526  outStr[0] = 0;
527 }
528 
529 static ufoType_t Com_GetUfoIdNum (const char* idString)
530 {
531  return Com_GetAircraftIdNum(CRAFT_UFO, idString);
532 }
533 
534 static ufoType_t Com_GetCrashedUfoIdNum (const char* idString)
535 {
536  return Com_GetCrashedAircraftIdNum(CRAFT_UFO, idString);
537 }
538 
539 static void Com_GetUfoIdStr (ufoType_t idNum, char* outStr, const size_t size)
540 {
541  Com_GetAircraftIdStr(CRAFT_UFO, idNum, outStr, size);
542 }
543 
544 static void Com_GetCrashedUfoIdStr (ufoType_t idNum, char* outStr, const size_t size)
545 {
546  Com_GetCrashedAircraftIdStr(CRAFT_UFO, idNum, outStr, size);
547 }
548 
549 static short Com_GetHumanCraftIdNum (const char* idString)
550 {
551  short idNum = Com_GetAircraftIdNum(CRAFT_DROP, idString);
552  if (idNum != AIRCRAFT_NONE)
553  return idNum;
554 
555  idNum = Com_GetAircraftIdNum(CRAFT_INTER, idString);
556  if (idNum != AIRCRAFT_NONE)
557  return idNum + aircraftIdsNum[CRAFT_DROP];
558 
559  return AIRCRAFT_NONE;
560 }
561 
562 static void Com_GetHumanCraftIdStr (short idNum, char* outStr, const size_t size)
563 {
564  if (idNum < aircraftIdsNum[CRAFT_DROP])
565  Com_GetAircraftIdStr(CRAFT_DROP, idNum, outStr, size);
566  else
568 }
569 
570 /* @todo Get rid of these somehow */
571 short Com_GetUfoIdsNum (void)
572 {
573  return aircraftIdsNum[CRAFT_UFO];
574 }
575 
577 {
578  return aircraftIdsNum[CRAFT_DROP];
579 }
580 
582 {
584 }
585 
589 static void Com_ParseAircraftNames (const char* const name, const char** text)
590 {
591  aircraftType_t craftType = CRAFT_MAX;
592  for (int i = 0; i < CRAFT_MAX; ++i)
593  if (Q_streq(name, va("%sids", craftTypeIds[i]))) {
594  craftType = static_cast<aircraftType_t>(i);
595  break;
596  }
597 
598  if (craftType == CRAFT_MAX) {
599  Com_Printf("Com_ParseAircraftNames: Unknown aircraft name type '%s' ignored\n", name);
600  return;
601  }
602 
603  const char* token = Com_Parse(text);
604  if (!*text || *token != '{') {
605  Com_Printf("Com_ParseAircraftNames: names def \"%s\" without body ignored\n", name);
606  return;
607  }
608 
609  short maxIds = 0;
610  switch (craftType) {
611  case CRAFT_DROP:
612  maxIds = DROPSHIP_MAX;
613  break;
614  case CRAFT_INTER:
615  maxIds = INTERCEPTOR_MAX;
616  break;
617  case CRAFT_UFO:
618  maxIds = UFO_MAX;
619  break;
620  default:
621  Com_Printf("Com_ParseAircraftNames: Unknown aircraft type '%s' without max ids ignored\n", name);
622  return;
623  }
624  const char* const errhead = "Com_ParseAircraftNames: Unexpected end of file (name type ";
625  do {
626  /* get the name type */
627  token = Com_EParse(text, errhead, name);
628  if (!*text)
629  break;
630  if (*token == '}')
631  break;
632  if (aircraftIdsNum[craftType] >= maxIds) {
633  Com_Printf("Com_ParseAircraftNames: Too many aircraft ids for type '%s', '%s' ignored!\n", name, token);
634  continue;
635  }
636  if (Com_GetAircraftIdNum(craftType, va("craft_%s_%s", craftTypeIds[craftType], token)) != AIRCRAFT_NONE) {
637  Com_Printf("Com_ParseAircraftNames: Aircraft with same name found '%s', second ignored\n", token);
638  continue;
639  }
640  aircraftIdsTable[craftType][aircraftIdsNum[craftType]++] = Mem_StrDup(token);
641  } while (*text);
642 }
643 
656 resultStatus_t Com_ParseValue (void* base, const char* token, valueTypes_t type, int ofs, size_t size, size_t* writtenBytes)
657 {
658  int x, y, w, h;
659  byte num;
660  resultStatus_t status = RESULT_OK;
661  byte* b = (byte*) base + ofs;
662  *writtenBytes = 0;
663  ufoType_t ufoType = UFO_NONE;
665 
666 #ifdef DEBUG
667  if (b != Com_AlignPtr(b, type))
668  Com_Printf("Wrong alignment: %p %p type:%d size:" UFO_SIZE_T "\n", b, Com_AlignPtr(b, type), type, vt_aligns[type]);
669 #endif
670 
671  if (size) {
672 #ifdef DEBUG
673  if (size > vt_sizes[type]) {
674  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Size mismatch: given size: " UFO_SIZE_T ", should be: " UFO_SIZE_T " (type: %i). ", size, vt_sizes[type], type);
675  status = RESULT_WARNING;
676  }
677 #endif
678  if (size < vt_sizes[type]) {
679  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Size mismatch: given size: " UFO_SIZE_T ", should be: " UFO_SIZE_T ". (type: %i)", size, vt_sizes[type], type);
680  return RESULT_ERROR;
681  }
682  }
683 
684  switch (type) {
685  case V_HUNK_STRING:
686  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "V_HUNK_STRING is not parsed here");
687  return RESULT_ERROR;
688 
689  case V_NULL:
690  *writtenBytes = 0;
691  break;
692 
693  case V_BOOL:
694  if (Q_streq(token, "true") || *token == '1')
695  *(bool*)b = true;
696  else if (Q_streq(token, "false") || *token == '0')
697  *(bool*)b = false;
698  else {
699  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal bool statement '%s'", token);
700  return RESULT_ERROR;
701  }
702  *writtenBytes = sizeof(bool);
703  break;
704 
705  case V_CHAR:
706  if (token[0] == '\0') {
707  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Char expected, but end of string found");
708  return RESULT_ERROR;
709  }
710  if (token[1] != '\0') {
711  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal end of string. '\\0' explected but 0x%x found", token[1]);
712  return RESULT_ERROR;
713  }
714  *(char*) b = token[0];
715  *writtenBytes = sizeof(char);
716  break;
717 
718  case V_TEAM:
719  if (Q_streq(token, "civilian"))
720  *(int*) b = TEAM_CIVILIAN;
721  else if (Q_streq(token, "phalanx"))
722  *(int*) b = TEAM_PHALANX;
723  else if (Q_streq(token, "alien"))
724  *(int*) b = TEAM_ALIEN;
725  else
726  Sys_Error("Unknown team string: '%s' found in script files", token);
727  *writtenBytes = sizeof(int);
728  break;
729 
730  case V_AIRCRAFTTYPE:
731  craftType = Com_GetHumanCraftIdNum(token);
732  if (craftType != AIRCRAFT_NONE)
733  *(humanAircraftType_t*) b = craftType;
734  else
735  Sys_Error("Unknown aircraft type: '%s'", token);
736  *writtenBytes = sizeof(humanAircraftType_t);
737  break;
738 
739  case V_UFO:
740  ufoType = Com_GetUfoIdNum(token);
741  if (ufoType != AIRCRAFT_NONE)
742  *(ufoType_t*) b = ufoType;
743  else
744  Sys_Error("Unknown ufo type: '%s'", token);
745  *writtenBytes = sizeof(ufoType_t);
746  break;
747 
748  case V_UFOCRASHED:
749  ufoType = Com_GetCrashedUfoIdNum(token);
750  if (ufoType != AIRCRAFT_NONE)
751  *(ufoType_t*) b = ufoType;
752  else
753  Sys_Error("Unknown ufo type: '%s'", token);
754  *writtenBytes = sizeof(ufoType_t);
755  break;
756 
757  case V_INT:
758  if (sscanf(token, "%i", &((int*) b)[0]) != 1) {
759  if (!Com_GetConstInt(token, &((int*) b)[0])) {
760  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal int statement '%s'", token);
761  return RESULT_ERROR;
762  }
763  }
764  *writtenBytes = sizeof(int);
765  break;
766 
767  case V_INT2:
768  if (sscanf(token, "%i %i", &((int*) b)[0], &((int*) b)[1]) != 2) {
769  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal int2 statement '%s'", token);
770  return RESULT_ERROR;
771  }
772  *writtenBytes = 2 * sizeof(int);
773  break;
774 
775  case V_FLOAT:
776  if (sscanf(token, "%f", &((float*) b)[0]) != 1) {
777  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal float statement '%s'", token);
778  return RESULT_ERROR;
779  }
780  *writtenBytes = sizeof(float);
781  break;
782 
783  case V_POS:
784  if (sscanf(token, "%f %f", &((float*) b)[0], &((float*) b)[1]) != 2) {
785  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal pos statement '%s'", token);
786  return RESULT_ERROR;
787  }
788  *writtenBytes = 2 * sizeof(float);
789  break;
790 
791  case V_VECTOR:
792  if (sscanf(token, "%f %f %f", &((float*) b)[0], &((float*) b)[1], &((float*) b)[2]) != 3) {
793  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal vector statement '%s'", token);
794  return RESULT_ERROR;
795  }
796  *writtenBytes = 3 * sizeof(float);
797  break;
798 
799  case V_COLOR:
800  {
801  float* f = (float*) b;
802  if (sscanf(token, "%f %f %f %f", &f[0], &f[1], &f[2], &f[3]) != 4) {
803  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal color statement '%s'", token);
804  return RESULT_ERROR;
805  }
806  *writtenBytes = 4 * sizeof(float);
807  }
808  break;
809 
810  case V_STRING:
811  Q_strncpyz((char*) b, token, MAX_VAR);
812  w = (int)strlen(token) + 1;
813  *writtenBytes = w;
814  break;
815 
816  /* just remove the _ but don't translate */
818  if (*token == '_')
819  token++;
820 
821  Q_strncpyz((char*) b, token, MAX_VAR);
822  w = (int)strlen((char*) b) + 1;
823  *writtenBytes = w;
824  break;
825 
826  case V_LONGSTRING:
827  strcpy((char*) b, token);
828  w = (int)strlen(token) + 1;
829  *writtenBytes = w;
830  break;
831 
832  case V_ALIGN:
833  for (num = 0; num < ALIGN_LAST; num++)
834  if (Q_streq(token, align_names[num]))
835  break;
836  if (num == ALIGN_LAST) {
837  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal align token '%s'", token);
838  return RESULT_ERROR;
839  }
840  *(align_t*)b = (align_t)num;
841  *writtenBytes = sizeof(align_t);
842  break;
843 
844  case V_BLEND:
845  for (num = 0; num < BLEND_LAST; num++)
846  if (Q_streq(token, blend_names[num]))
847  break;
848  if (num == BLEND_LAST) {
849  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal blend token '%s'", token);
850  return RESULT_ERROR;
851  }
852  *(blend_t*)b = (blend_t)num;
853  *writtenBytes = sizeof(blend_t);
854  break;
855 
856  case V_STYLE:
857  for (num = 0; num < STYLE_LAST; num++)
858  if (Q_streq(token, style_names[num]))
859  break;
860  if (num == STYLE_LAST) {
861  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal style token '%s'", token);
862  return RESULT_ERROR;
863  }
864  *(style_t*)b = (style_t)num;
865  *writtenBytes = sizeof(style_t);
866  break;
867 
868  case V_FADE:
869  for (num = 0; num < FADE_LAST; num++)
870  if (Q_streq(token, fade_names[num]))
871  break;
872  if (num == FADE_LAST) {
873  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal fade token '%s'", token);
874  return RESULT_ERROR;
875  }
876  *(fade_t*)b = (fade_t)num;
877  *writtenBytes = sizeof(fade_t);
878  break;
879 
880  case V_SHAPE_SMALL:
881  if (sscanf(token, "%i %i %i %i", &x, &y, &w, &h) != 4) {
882  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal shape small statement '%s'", token);
883  return RESULT_ERROR;
884  }
885 
887  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "illegal shape small statement - max h value is %i (y: %i, h: %i)", SHAPE_SMALL_MAX_HEIGHT, y, h);
888  return RESULT_ERROR;
889  }
891  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "illegal shape small statement - max x and w values are %i", SHAPE_SMALL_MAX_WIDTH);
892  return RESULT_ERROR;
893  }
894  for (h += y; y < h; y++)
895  *(uint32_t*) b |= ((1 << w) - 1) << x << (y * SHAPE_SMALL_MAX_WIDTH);
896  *writtenBytes = SHAPE_SMALL_MAX_HEIGHT;
897  break;
898 
899  case V_SHAPE_BIG:
900  if (sscanf(token, "%i %i %i %i", &x, &y, &w, &h) != 4) {
901  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal shape big statement '%s'", token);
902  return RESULT_ERROR;
903  }
905  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "Illegal shape big statement, max height is %i", SHAPE_BIG_MAX_HEIGHT);
906  return RESULT_ERROR;
907  }
908  if (x + w > SHAPE_BIG_MAX_WIDTH || x >= SHAPE_BIG_MAX_WIDTH || w > SHAPE_BIG_MAX_WIDTH) {
909  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "illegal shape big statement - max x and w values are %i ('%s')", SHAPE_BIG_MAX_WIDTH, token);
910  return RESULT_ERROR;
911  }
912  w = ((1 << w) - 1) << x;
913  for (h += y; y < h; y++)
914  ((uint32_t*) b)[y] |= w;
916  break;
917 
918  case V_DAMAGE:
919  for (num = 0; num < csi.numDTs; num++)
920  if (Q_streq(token, csi.dts[num].id))
921  break;
922  if (num == csi.numDTs)
923  *b = 0;
924  else
925  *b = num;
926  *writtenBytes = sizeof(byte);
927  break;
928 
929  case V_RELABS:
930  if (token[0] == '-' || token[0] == '+') {
931  if (fabs(atof(token + 1)) <= 2.0f) {
932  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "a V_RELABS (absolute) value should always be bigger than +/-2.0");
933  status = RESULT_WARNING;
934  }
935  if (token[0] == '-')
936  *(float*) b = atof(token + 1) * (-1);
937  else
938  *(float*) b = atof(token + 1);
939  } else {
940  if (fabs(atof(token)) > 2.0f) {
941  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "a V_RELABS (relative) value should only be between 0.00..1 and 2.0");
942  status = RESULT_WARNING;
943  }
944  *(float*) b = atof(token);
945  }
946  *writtenBytes = sizeof(float);
947  break;
948 
949  default:
950  snprintf(parseErrorMessage, sizeof(parseErrorMessage), "unknown value type '%s'", token);
951  return RESULT_ERROR;
952  }
953  return status;
954 }
955 
964 int Com_EParseValue (void* base, const char* token, valueTypes_t type, int ofs, size_t size)
965 {
966  size_t writtenBytes;
967  const resultStatus_t result = Com_ParseValue(base, token, type, ofs, size, &writtenBytes);
968  switch (result) {
969  case RESULT_ERROR:
970  Sys_Error("Com_EParseValue: %s\n", parseErrorMessage);
971  break;
972  case RESULT_WARNING:
973  Com_Printf("Com_EParseValue: %s\n", parseErrorMessage);
974  break;
975  case RESULT_OK:
976  break;
977  }
978  return writtenBytes;
979 }
980 
986 bool Com_ParseBoolean (const char* token)
987 {
988  bool b;
989  size_t writtenBytes;
990  if (Com_ParseValue(&b, token, V_BOOL, 0, sizeof(b), &writtenBytes) != RESULT_ERROR) {
991  assert(writtenBytes == sizeof(b));
992  return b;
993  }
994  return false;
995 }
996 
1006 #ifdef DEBUG
1007 int Com_SetValueDebug (void* base, const void* set, valueTypes_t type, int ofs, size_t size, const char* file, int line)
1008 #else
1009 int Com_SetValue (void* base, const void* set, valueTypes_t type, int ofs, size_t size)
1010 #endif
1011 {
1012  int len;
1013  ufoType_t ufoType = UFO_NONE;
1014  humanAircraftType_t craftType = AIRCRAFT_NONE;
1015 
1016  byte* b = (byte*) base + ofs;
1017 
1018  if (size) {
1019 #ifdef DEBUG
1020  if (size > vt_sizes[type])
1021  Com_Printf("Warning: Size mismatch: given size: " UFO_SIZE_T ", should be: " UFO_SIZE_T ". File: '%s', line: %i (type: %i)\n", size, vt_sizes[type], file, line, type);
1022 
1023  if (size < vt_sizes[type])
1024  Sys_Error("Size mismatch: given size: " UFO_SIZE_T ", should be: " UFO_SIZE_T ". File: '%s', line: %i (type: %i)", size, vt_sizes[type], file, line, type);
1025 #else
1026  if (size < vt_sizes[type])
1027  Sys_Error("Size mismatch: given size: " UFO_SIZE_T ", should be: " UFO_SIZE_T ". (type: %i)", size, vt_sizes[type], type);
1028 #endif
1029  }
1030 
1031 #ifdef DEBUG
1032  if (b != Com_AlignPtr(b, type)) {
1033  Com_Printf("Wrong alignment: %p %p type:%d size:" UFO_SIZE_T " - this code will CRASH on ARM CPU\n", b, Com_AlignPtr(b, type), type, vt_aligns[type]);
1034  Sys_Backtrace();
1035  }
1036 #endif
1037 
1038  switch (type) {
1039  case V_NULL:
1040  return 0;
1041 
1042  case V_BOOL:
1043  if (*(const bool*) set)
1044  *(bool*)b = true;
1045  else
1046  *(bool*)b = false;
1047  return sizeof(bool);
1048 
1049  case V_CHAR:
1050  *(char*) b = *(const char*) set;
1051  return sizeof(char);
1052 
1053  case V_TEAM:
1054  if (Q_streq((const char*)set, "civilian"))
1055  *(int*) b = TEAM_CIVILIAN;
1056  else if (Q_streq((const char*)set, "phalanx"))
1057  *(int*) b = TEAM_PHALANX;
1058  else if (Q_streq((const char*)set, "alien"))
1059  *(int*) b = TEAM_ALIEN;
1060  else
1061  Sys_Error("Unknown team given: '%s'", (const char*)set);
1062  return sizeof(int);
1063 
1064  case V_AIRCRAFTTYPE:
1065  craftType = Com_GetHumanCraftIdNum((const char*)set);
1066  if (craftType != AIRCRAFT_NONE)
1067  *(humanAircraftType_t*) b = craftType;
1068  else
1069  Sys_Error("Unknown aircraft type: '%s'", (const char*)set);
1070  return sizeof(humanAircraftType_t);
1071 
1072  case V_UFO:
1073  ufoType = Com_GetUfoIdNum((const char*)set);
1074  if (ufoType != AIRCRAFT_NONE)
1075  *(ufoType_t*) b = ufoType;
1076  else
1077  Sys_Error("Unknown ufo type: '%s'", (const char*)set);
1078  return sizeof(ufoType_t);
1079 
1080  case V_UFOCRASHED:
1081  ufoType = Com_GetCrashedUfoIdNum((const char*)set);
1082  if (ufoType != AIRCRAFT_NONE)
1083  *(ufoType_t*) b = ufoType;
1084  else
1085  Sys_Error("Unknown ufo type: '%s'", (const char*)set);
1086  return sizeof(ufoType_t);
1087 
1088  case V_INT:
1089  *(int*) b = *(const int*) set;
1090  return sizeof(int);
1091 
1092  case V_INT2:
1093  ((int*) b)[0] = ((const int*) set)[0];
1094  ((int*) b)[1] = ((const int*) set)[1];
1095  return 2 * sizeof(int);
1096 
1097  case V_FLOAT:
1098  *(float*) b = *(const float*) set;
1099  return sizeof(float);
1100 
1101  case V_POS:
1102  ((float*) b)[0] = ((const float*) set)[0];
1103  ((float*) b)[1] = ((const float*) set)[1];
1104  return 2 * sizeof(float);
1105 
1106  case V_VECTOR:
1107  ((float*) b)[0] = ((const float*) set)[0];
1108  ((float*) b)[1] = ((const float*) set)[1];
1109  ((float*) b)[2] = ((const float*) set)[2];
1110  return 3 * sizeof(float);
1111 
1112  case V_COLOR:
1113  ((float*) b)[0] = ((const float*) set)[0];
1114  ((float*) b)[1] = ((const float*) set)[1];
1115  ((float*) b)[2] = ((const float*) set)[2];
1116  ((float*) b)[3] = ((const float*) set)[3];
1117  return 4 * sizeof(float);
1118 
1119  case V_STRING:
1120  Q_strncpyz((char*) b, (const char*) set, MAX_VAR);
1121  len = (int)strlen((const char*) set) + 1;
1122  if (len > MAX_VAR)
1123  len = MAX_VAR;
1124  return len;
1125 
1126  case V_LONGSTRING:
1127  strcpy((char*) b, (const char*) set);
1128  len = (int)strlen((const char*) set) + 1;
1129  return len;
1130 
1131  case V_ALIGN:
1132  *(align_t*)b = *(const align_t*) set;
1133  return sizeof(align_t);
1134 
1135  case V_BLEND:
1136  *(blend_t*)b = *(const blend_t*) set;
1137  return sizeof(blend_t);
1138 
1139  case V_STYLE:
1140  *(style_t*)b = *(const style_t*) set;
1141  return sizeof(style_t);
1142 
1143  case V_FADE:
1144  *(fade_t*)b = *(const fade_t*) set;
1145  return sizeof(fade_t);
1146 
1147  case V_SHAPE_SMALL:
1148  *(int*) b = *(const int*) set;
1149  return SHAPE_SMALL_MAX_HEIGHT;
1150 
1151  case V_SHAPE_BIG:
1152  memcpy(b, set, 64);
1153  return SHAPE_BIG_MAX_HEIGHT * 4;
1154 
1155  case V_DAMAGE:
1156  *b = *(const byte*) set;
1157  return 1;
1158 
1159  default:
1160  Sys_Error("Com_SetValue: unknown value type\n");
1161  }
1162 }
1163 
1171 const char* Com_ValueToStr (const void* base, const valueTypes_t type, const int ofs)
1172 {
1173  static char valuestr[MAX_VAR];
1174  const byte* b = (const byte*) base + ofs;
1175 
1176 #ifdef DEBUG
1177  if (b != Com_AlignPtr(b, type)) {
1178  Com_Printf("Wrong alignment: %p %p type:%d size:" UFO_SIZE_T " - this code will CRASH on ARM CPU\n", b, Com_AlignPtr(b, type), type, vt_aligns[type]);
1179  Sys_Backtrace();
1180  }
1181 #endif
1182 
1183  switch (type) {
1184  case V_NULL:
1185  return 0;
1186 
1187  case V_HUNK_STRING:
1188  if (b == nullptr)
1189  return "(null)";
1190  else
1191  return (const char*)b;
1192 
1193  case V_BOOL:
1194  if (*(const bool*)b)
1195  return "true";
1196  else
1197  return "false";
1198 
1199  case V_CHAR:
1200  return (const char*) b;
1201  break;
1202 
1203  case V_TEAM:
1204  switch (*(const int*) b) {
1205  case TEAM_CIVILIAN:
1206  return "civilian";
1207  case TEAM_PHALANX:
1208  return "phalanx";
1209  case TEAM_ALIEN:
1210  return "alien";
1211  default:
1212  Sys_Error("Unknown team id '%i'", *(const int*) b);
1213  }
1214 
1215  case V_AIRCRAFTTYPE:
1216  Com_GetHumanCraftIdStr(*(const humanAircraftType_t*) b, valuestr, sizeof(valuestr));
1217  if (valuestr[0])
1218  return valuestr;
1219  else
1220  Sys_Error("Unknown aircraft type: '%i'", *(const humanAircraftType_t*) b);
1221 
1222  case V_UFO:
1223  Com_GetUfoIdStr(*(const ufoType_t*) b, valuestr, sizeof(valuestr));
1224  if (valuestr[0])
1225  return valuestr;
1226  else
1227  Sys_Error("Unknown ufo type: '%i'", *(const ufoType_t*) b);
1228 
1229  case V_UFOCRASHED:
1230  Com_GetCrashedUfoIdStr(*(const ufoType_t*) b, valuestr, sizeof(valuestr));
1231  if (valuestr[0])
1232  return valuestr;
1233  else
1234  Sys_Error("Unknown crashed ufo type: '%i'", *(const ufoType_t*) b);
1235 
1236  case V_INT:
1237  Com_sprintf(valuestr, sizeof(valuestr), "%i", *(const int*) b);
1238  return valuestr;
1239 
1240  case V_INT2:
1241  Com_sprintf(valuestr, sizeof(valuestr), "%i %i", ((const int*) b)[0], ((const int*) b)[1]);
1242  return valuestr;
1243 
1244  case V_FLOAT:
1245  Com_sprintf(valuestr, sizeof(valuestr), "%.2f", *(const float*) b);
1246  return valuestr;
1247 
1248  case V_POS:
1249  Com_sprintf(valuestr, sizeof(valuestr), "%.2f %.2f", ((const float*) b)[0], ((const float*) b)[1]);
1250  return valuestr;
1251 
1252  case V_VECTOR:
1253  Com_sprintf(valuestr, sizeof(valuestr), "%.2f %.2f %.2f", ((const float*) b)[0], ((const float*) b)[1], ((const float*) b)[2]);
1254  return valuestr;
1255 
1256  case V_COLOR:
1257  Com_sprintf(valuestr, sizeof(valuestr), "%.2f %.2f %.2f %.2f", ((const float*) b)[0], ((const float*) b)[1], ((const float*) b)[2], ((const float*) b)[3]);
1258  return valuestr;
1259 
1260  case V_TRANSLATION_STRING:
1261  case V_STRING:
1262  case V_LONGSTRING:
1263  assert(b); /* this should never happen. let's see */
1264  return (const char*) b;
1265 
1266  case V_ALIGN:
1267  assert(*(const align_t*)b < ALIGN_LAST);
1268  Q_strncpyz(valuestr, align_names[*(const align_t*)b], sizeof(valuestr));
1269  return valuestr;
1270 
1271  case V_BLEND:
1272  assert(*(const blend_t*)b < BLEND_LAST);
1273  Q_strncpyz(valuestr, blend_names[*(const blend_t*)b], sizeof(valuestr));
1274  return valuestr;
1275 
1276  case V_STYLE:
1277  assert(*(const style_t*)b < STYLE_LAST);
1278  Q_strncpyz(valuestr, style_names[*(const style_t*)b], sizeof(valuestr));
1279  return valuestr;
1280 
1281  case V_FADE:
1282  assert(*(const fade_t*)b < FADE_LAST);
1283  Q_strncpyz(valuestr, fade_names[*(const fade_t*)b], sizeof(valuestr));
1284  return valuestr;
1285 
1286  case V_SHAPE_SMALL:
1287  case V_SHAPE_BIG:
1288  return "";
1289 
1290  case V_DAMAGE:
1291  assert(*(const byte*)b < MAX_DAMAGETYPES);
1292  return csi.dts[*(const byte*)b].id;
1293 
1294  case V_RELABS:
1295  /* absolute value */
1296  if (*(const float*) b > 2.0)
1297  Com_sprintf(valuestr, sizeof(valuestr), "+%.2f", *(const float*) b);
1298  /* absolute value */
1299  else if (*(const float*) b < 2.0)
1300  Com_sprintf(valuestr, sizeof(valuestr), "-%.2f", *(const float*) b);
1301  /* relative value */
1302  else
1303  Com_sprintf(valuestr, sizeof(valuestr), "%.2f", *(const float*) b);
1304  return valuestr;
1305 
1306  default:
1307  Sys_Error("Com_ValueToStr: unknown value type %i\n", type);
1308  }
1309 }
1310 
1311 bool Com_ParseBlockToken (const char* name, const char** text, void* base, const value_t* values, memPool_t* mempool, const char* token)
1312 {
1313  const value_t* v;
1314  const char* errhead = "Com_ParseBlockToken: unexpected end of file (";
1315 
1316  for (v = values; v->string; v++)
1317  if (Q_streq(token, v->string)) {
1318  /* found a definition */
1319  token = Com_EParse(text, errhead, name);
1320  if (!*text)
1321  return false;
1322 
1323  switch (v->type) {
1324  case V_TRANSLATION_STRING:
1325  if (mempool == nullptr) {
1326  if (Com_EParseValue(base, token, v->type, v->ofs, v->size) == -1)
1327  Com_Printf("Com_ParseBlockToken: Wrong size for value %s\n", v->string);
1328  break;
1329  }
1330  if (*token == '_')
1331  token++;
1332  /* fall through */
1333  case V_HUNK_STRING:
1334  Mem_PoolStrDupTo(token, &Com_GetValue<char*>(base, v), mempool, 0);
1335  break;
1336  case V_LIST: {
1337  linkedList_t*& list = Com_GetValue<linkedList_t*>(base, v);
1338  assert(!list);
1340  if (!Com_ParseList(text, &list)) {
1341  return false;
1342  }
1343  break;
1344  }
1345  default:
1346  if (Com_EParseValue(base, token, v->type, v->ofs, v->size) == -1)
1347  Com_Printf("Com_ParseBlockToken: Wrong size for value %s\n", v->string);
1348  break;
1349  }
1350  break;
1351  }
1352 
1353  return v->string != nullptr;
1354 }
1355 
1363 bool Com_ParseList (const char** text, linkedList_t** list)
1364 {
1365  *list = nullptr;
1366 
1367  if (Com_NextToken(text) != TT_BEGIN_LIST) {
1368  Com_Printf("Com_ParseList: expected '(' but \"%s\" found\n", Com_GetToken(text));
1369  return false;
1370  }
1371 
1372  while (true) {
1374  if (type == TT_END_LIST)
1375  break;
1376  if (type == TT_EOF) {
1377  Com_Printf("Com_ParseList: expected list content but end of file found\n");
1378  LIST_Delete(list);
1379  return false;
1380  }
1381  if (type < TT_CONTENT) {
1382  Com_Printf("Com_ParseList: expected list content but \"%s\" found\n", Com_GetToken(text));
1383  LIST_Delete(list);
1384  return false;
1385  }
1386  // read content
1387  LIST_AddString(list, Com_GetToken(text));
1388  }
1389 
1390  return true;
1391 }
1392 
1393 bool Com_ParseBlock (const char* name, const char** text, void* base, const value_t* values, memPool_t* mempool)
1394 {
1395  const char* errhead = "Com_ParseBlock: unexpected end of file (";
1396 
1397  /* get name/id */
1398  const char* token = Com_Parse(text);
1399 
1400  if (!*text || *token != '{') {
1401  Com_Printf("Com_ParseBlock: block \"%s\" without body ignored\n", name);
1402  return false;
1403  }
1404 
1405  do {
1406  /* get the name type */
1407  token = Com_EParse(text, errhead, name);
1408  if (!*text)
1409  break;
1410  if (*token == '}')
1411  break;
1412  if (!Com_ParseBlockToken(name, text, base, values, mempool, token))
1413  Com_Printf("Com_ParseBlock: unknown token '%s' ignored (%s)\n", token, name);
1414  } while (*text);
1415 
1416  return true;
1417 }
1418 
1419 /*
1420 ==============================================================================
1421 OBJECT DEFINITION INTERPRETER
1422 ==============================================================================
1423 */
1424 
1425 static const char* const skillNames[SKILL_NUM_TYPES + 1] = {
1426  "strength",
1427  "speed",
1428  "accuracy",
1429  "mind",
1430  "close",
1431  "heavy",
1432  "assault",
1433  "sniper",
1434  "explosive",
1435  "piloting",
1436  "targeting",
1437  "evading",
1438  "health"
1439 };
1440 
1442 enum {
1446 };
1447 
1448 static const value_t od_vals[] = {
1449  {"name", V_TRANSLATION_STRING, offsetof(objDef_t, name), 0},
1450  {"armourpath", V_HUNK_STRING, offsetof(objDef_t, armourPath), 0},
1451  {"model", V_HUNK_STRING, offsetof(objDef_t, model), 0},
1452  {"image", V_HUNK_STRING, offsetof(objDef_t, image), 0},
1453  {"type", V_HUNK_STRING, offsetof(objDef_t, type), 0},
1454  {"reloadsound", V_HUNK_STRING, offsetof(objDef_t, reloadSound), 0},
1455  {"animationindex", V_CHAR, offsetof(objDef_t, animationIndex), MEMBER_SIZEOF(objDef_t, animationIndex)},
1456  {"shape", V_SHAPE_SMALL, offsetof(objDef_t, shape), MEMBER_SIZEOF(objDef_t, shape)},
1457  {"scale", V_FLOAT, offsetof(objDef_t, scale), MEMBER_SIZEOF(objDef_t, scale)},
1458  {"center", V_VECTOR, offsetof(objDef_t, center), MEMBER_SIZEOF(objDef_t, center)},
1459  {"weapon", V_BOOL, offsetof(objDef_t, weapon), MEMBER_SIZEOF(objDef_t, weapon)},
1460  {"holdtwohanded", V_BOOL, offsetof(objDef_t, holdTwoHanded), MEMBER_SIZEOF(objDef_t, holdTwoHanded)},
1461  {"firetwohanded", V_BOOL, offsetof(objDef_t, fireTwoHanded), MEMBER_SIZEOF(objDef_t, fireTwoHanded)},
1462  {"implant", V_BOOL, offsetof(objDef_t, implant), MEMBER_SIZEOF(objDef_t, implant)},
1463  {"headgear", V_BOOL, offsetof(objDef_t, headgear), MEMBER_SIZEOF(objDef_t, headgear)},
1464  {"thrown", V_BOOL, offsetof(objDef_t, thrown), MEMBER_SIZEOF(objDef_t, thrown)},
1465  {"ammo", V_INT, offsetof(objDef_t, ammo), MEMBER_SIZEOF(objDef_t, ammo)},
1466  {"oneshot", V_BOOL, offsetof(objDef_t, oneshot), MEMBER_SIZEOF(objDef_t, oneshot)},
1467  {"deplete", V_BOOL, offsetof(objDef_t, deplete), MEMBER_SIZEOF(objDef_t, deplete)},
1468  {"reload", V_INT, offsetof(objDef_t, _reload), MEMBER_SIZEOF(objDef_t, _reload)},
1469  {"reloadattenuation", V_FLOAT, offsetof(objDef_t, reloadAttenuation), MEMBER_SIZEOF(objDef_t, reloadAttenuation)},
1470  {"size", V_INT, offsetof(objDef_t, size), MEMBER_SIZEOF(objDef_t, size)},
1471  {"weight", V_INT, offsetof(objDef_t, weight), MEMBER_SIZEOF(objDef_t, weight)},
1472  {"price", V_INT, offsetof(objDef_t, price), MEMBER_SIZEOF(objDef_t, price)},
1473  {"productioncost", V_INT, offsetof(objDef_t, productionCost), MEMBER_SIZEOF(objDef_t, productionCost)},
1474  {"useable", V_TEAM, offsetof(objDef_t, useable), MEMBER_SIZEOF(objDef_t, useable)},
1475  {"notonmarket", V_BOOL, offsetof(objDef_t, notOnMarket), MEMBER_SIZEOF(objDef_t, notOnMarket)},
1476 
1477  {"installationTime", V_INT, offsetof(objDef_t, craftitem.installationTime), MEMBER_SIZEOF(objDef_t, craftitem.installationTime)},
1478  {"bullets", V_BOOL, offsetof(objDef_t, craftitem.bullets), MEMBER_SIZEOF(objDef_t, craftitem.bullets)},
1479  {"beam", V_BOOL, offsetof(objDef_t, craftitem.beam), MEMBER_SIZEOF(objDef_t, craftitem.beam)},
1480  {"beamcolor", V_COLOR, offsetof(objDef_t, craftitem.beamColor), MEMBER_SIZEOF(objDef_t, craftitem.beamColor)},
1481  {"wdamage", V_FLOAT, offsetof(objDef_t, craftitem.weaponDamage), MEMBER_SIZEOF(objDef_t, craftitem.weaponDamage)},
1482  {"wspeed", V_FLOAT, offsetof(objDef_t, craftitem.weaponSpeed), MEMBER_SIZEOF(objDef_t, craftitem.weaponSpeed)},
1483  {"delay", V_FLOAT, offsetof(objDef_t, craftitem.weaponDelay), MEMBER_SIZEOF(objDef_t, craftitem.weaponDelay)},
1484  {"shield", V_FLOAT, offsetof(objDef_t, craftitem.stats[AIR_STATS_SHIELD]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_SHIELD])},
1485  {"wrange", V_FLOAT, offsetof(objDef_t, craftitem.stats[AIR_STATS_WRANGE]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_WRANGE])},
1486  {"damage", V_RELABS, offsetof(objDef_t, craftitem.stats[AIR_STATS_DAMAGE]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_DAMAGE])},
1487  {"accuracy", V_RELABS, offsetof(objDef_t, craftitem.stats[AIR_STATS_ACCURACY]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_ACCURACY])},
1488  {"ecm", V_RELABS, offsetof(objDef_t, craftitem.stats[AIR_STATS_ECM]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_ECM])},
1489  {"speed", V_RELABS, offsetof(objDef_t, craftitem.stats[AIR_STATS_SPEED]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_SPEED])},
1490  {"maxspeed", V_RELABS, offsetof(objDef_t, craftitem.stats[AIR_STATS_MAXSPEED]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_SPEED])},
1491  {"fuelsize", V_RELABS, offsetof(objDef_t, craftitem.stats[AIR_STATS_FUELSIZE]), MEMBER_SIZEOF(objDef_t, craftitem.stats[AIR_STATS_FUELSIZE])},
1492  {"dmgtype", V_DAMAGE, offsetof(objDef_t, dmgtype), MEMBER_SIZEOF(objDef_t, dmgtype)},
1493 
1494  {"is_primary", V_BOOL, offsetof(objDef_t, isPrimary), MEMBER_SIZEOF(objDef_t, isPrimary)},
1495  {"is_secondary", V_BOOL, offsetof(objDef_t, isSecondary), MEMBER_SIZEOF(objDef_t, isSecondary)},
1496  {"is_heavy", V_BOOL, offsetof(objDef_t, isHeavy), MEMBER_SIZEOF(objDef_t, isHeavy)},
1497  {"is_misc", V_BOOL, offsetof(objDef_t, isMisc), MEMBER_SIZEOF(objDef_t, isMisc)},
1498  {"is_ugvitem", V_BOOL, offsetof(objDef_t, isUGVitem), MEMBER_SIZEOF(objDef_t, isUGVitem)},
1499  {"is_dummy", V_BOOL, offsetof(objDef_t, isDummy), MEMBER_SIZEOF(objDef_t, isDummy)},
1500  {"virtual", V_BOOL, offsetof(objDef_t, isVirtual), MEMBER_SIZEOF(objDef_t, isVirtual)},
1501 
1502  {nullptr, V_NULL, 0, 0}
1503 };
1504 
1505 static const value_t effect_vals[] = {
1506  {"period", V_INT, offsetof(itemEffect_t, period), MEMBER_SIZEOF(itemEffect_t, period)},
1507  {"duration", V_INT, offsetof(itemEffect_t, duration), MEMBER_SIZEOF(itemEffect_t, duration)},
1508  {"permanent", V_BOOL, offsetof(itemEffect_t, isPermanent), MEMBER_SIZEOF(itemEffect_t, isPermanent)},
1509 
1510  {"accuracy", V_FLOAT, offsetof(itemEffect_t, accuracy), MEMBER_SIZEOF(itemEffect_t, accuracy)},
1511  {"tu", V_FLOAT, offsetof(itemEffect_t, TUs), MEMBER_SIZEOF(itemEffect_t, TUs)},
1512  {"power", V_FLOAT, offsetof(itemEffect_t, power), MEMBER_SIZEOF(itemEffect_t, power)},
1513  {"mind", V_FLOAT, offsetof(itemEffect_t, mind), MEMBER_SIZEOF(itemEffect_t, mind)},
1514  {"morale", V_FLOAT, offsetof(itemEffect_t, morale), MEMBER_SIZEOF(itemEffect_t, morale)},
1515 
1516  {nullptr, V_NULL, 0, 0}
1517 };
1518 
1519 /* =========================================================== */
1520 
1521 static const value_t fdps[] = {
1522  {"name", V_TRANSLATION_STRING, offsetof(fireDef_t, name), 0},
1523  {"shotorg", V_POS, offsetof(fireDef_t, shotOrg), MEMBER_SIZEOF(fireDef_t, shotOrg)},
1524  {"projtl", V_HUNK_STRING, offsetof(fireDef_t, projectile), 0},
1525  {"impact", V_HUNK_STRING, offsetof(fireDef_t, impact), 0},
1526  {"hitbody", V_HUNK_STRING, offsetof(fireDef_t, hitBody), 0},
1527  {"firesnd", V_HUNK_STRING, offsetof(fireDef_t, fireSound), 0},
1528  {"impsnd", V_HUNK_STRING, offsetof(fireDef_t, impactSound), 0},
1529  {"bodysnd", V_HUNK_STRING, offsetof(fireDef_t, hitBodySound), 0},
1530  {"bncsnd", V_HUNK_STRING, offsetof(fireDef_t, bounceSound), 0},
1531  {"fireattenuation", V_FLOAT, offsetof(fireDef_t, fireAttenuation), MEMBER_SIZEOF(fireDef_t, fireAttenuation)},
1532  {"impactattenuation", V_FLOAT, offsetof(fireDef_t, impactAttenuation), MEMBER_SIZEOF(fireDef_t, impactAttenuation)},
1533  {"throughwall", V_INT, offsetof(fireDef_t, throughWall), MEMBER_SIZEOF(fireDef_t, throughWall)},
1534  {"sndonce", V_BOOL, offsetof(fireDef_t, soundOnce), MEMBER_SIZEOF(fireDef_t, soundOnce)},
1535  {"gravity", V_BOOL, offsetof(fireDef_t, gravity), MEMBER_SIZEOF(fireDef_t, gravity)},
1536  {"launched", V_BOOL, offsetof(fireDef_t, launched), MEMBER_SIZEOF(fireDef_t, launched)},
1537  {"rolled", V_BOOL, offsetof(fireDef_t, rolled), MEMBER_SIZEOF(fireDef_t, rolled)},
1538  {"reaction", V_BOOL, offsetof(fireDef_t, reaction), MEMBER_SIZEOF(fireDef_t, reaction)},
1539  {"delay", V_INT, offsetof(fireDef_t, delay), MEMBER_SIZEOF(fireDef_t, delay)},
1540  {"bounce", V_INT, offsetof(fireDef_t, bounce), MEMBER_SIZEOF(fireDef_t, bounce)},
1541  {"bncfac", V_FLOAT, offsetof(fireDef_t, bounceFac), MEMBER_SIZEOF(fireDef_t, bounceFac)},
1542  {"speed", V_FLOAT, offsetof(fireDef_t, speed), MEMBER_SIZEOF(fireDef_t, speed)},
1543  {"spread", V_POS, offsetof(fireDef_t, spread), MEMBER_SIZEOF(fireDef_t, spread)},
1544  {"crouch", V_FLOAT, offsetof(fireDef_t, crouch), MEMBER_SIZEOF(fireDef_t, crouch)},
1545  {"shots", V_INT, offsetof(fireDef_t, shots), MEMBER_SIZEOF(fireDef_t, shots)},
1546  {"ammo", V_INT, offsetof(fireDef_t, ammo), MEMBER_SIZEOF(fireDef_t, ammo)},
1547  {"delaybetweenshots", V_FLOAT, offsetof(fireDef_t, delayBetweenShots), MEMBER_SIZEOF(fireDef_t, delayBetweenShots)},
1548  {"time", V_INT, offsetof(fireDef_t, time), MEMBER_SIZEOF(fireDef_t, time)},
1549  {"damage", V_POS, offsetof(fireDef_t, damage), MEMBER_SIZEOF(fireDef_t, damage)},
1550  {"spldmg", V_POS, offsetof(fireDef_t, spldmg), MEMBER_SIZEOF(fireDef_t, spldmg)},
1551  {"dmgweight", V_DAMAGE, offsetof(fireDef_t, dmgweight), MEMBER_SIZEOF(fireDef_t, dmgweight)},
1552  {"irgoggles", V_BOOL, offsetof(fireDef_t, irgoggles), MEMBER_SIZEOF(fireDef_t, irgoggles)},
1553  {"rounds", V_INT, offsetof(fireDef_t, rounds), MEMBER_SIZEOF(fireDef_t, rounds)},
1554  {nullptr, V_NULL, 0, 0}
1555 };
1556 
1563 static effectStages_t Com_ParseItemEffect (itemEffect_t* e, const char* name, const char** text)
1564 {
1565  effectStages_t stage = EFFECT_MAX;
1566 
1567  const char* token = Com_Parse(text);
1568  if (!*text) {
1569  Com_Printf("Com_ParseItemEffect: syntax error for item '%s'\n", name);
1570  return stage;
1571  }
1572 
1573  if (Q_streq(token, "active")) {
1574  stage = EFFECT_ACTIVE;
1575  } else if (Q_streq(token, "inactive")) {
1576  stage = EFFECT_INACTIVE;
1577  } else if (Q_streq(token, "overdose")) {
1578  stage = EFFECT_OVERDOSE;
1579  } else if (Q_streq(token, "strengthen")) {
1580  stage = EFFECT_STRENGTHEN;
1581  } else {
1582  token = Com_Parse(text);
1583  if (!*text || *token != '{') {
1584  Com_Printf("Com_ParseItemEffect: syntax error for item '%s'\n", name);
1585  return stage;
1586  }
1587  Com_SkipBlock(text);
1588  Com_Printf("Com_ParseItemEffect: item effect of \"%s\" has invalid effect stage: '%s'\n", name, token);
1589  return stage;
1590  }
1591 
1592  token = Com_Parse(text);
1593  if (token[0] != '{') {
1594  Com_Printf("Com_ParseItemEffect: syntax error for item '%s'\n", name);
1595  return stage;
1596  }
1597 
1598  do {
1599  token = Com_Parse(text);
1600  if (!*text)
1601  break;
1602  if (*token == '}')
1603  break;
1604 
1605  if (!Com_ParseBlockToken(name, text, e, effect_vals, com_genericPool, token)) {
1606  Com_Printf("Com_ParseItemEffect: item effect of \"%s\" contains invalid values\n", name);
1607  return stage;
1608  }
1609  } while (*text); /* dummy condition */
1610 
1611  return stage;
1612 }
1613 
1620 static void Com_ParseFireEffect (fireDef_t* fd, const char* name, const char** text)
1621 {
1623  const effectStages_t stage = Com_ParseItemEffect(e, name, text);
1624  if (stage == EFFECT_MAX) {
1625  Mem_Free(e);
1626  return;
1627  }
1628 
1629  itemEffect_t** stagePtr = nullptr;
1630  switch (stage) {
1631  case EFFECT_ACTIVE:
1632  stagePtr = &fd->activeEffect;
1633  break;
1634  case EFFECT_INACTIVE:
1635  stagePtr = &fd->deactiveEffect;
1636  break;
1637  case EFFECT_OVERDOSE:
1638  stagePtr = &fd->overdoseEffect;
1639  break;
1640  case EFFECT_STRENGTHEN:
1641  /* strengthen effect isn't available here */
1642  case EFFECT_MAX:
1643  stagePtr = nullptr;
1644  break;
1645  }
1646 
1647  if (stagePtr == nullptr) {
1648  Mem_Free(e);
1649  Com_Printf("Com_ParseFireEffect: invalid effect stage for '%s'\n", name);
1650  return;
1651  }
1652 
1653  if (*stagePtr != nullptr) {
1654  Mem_Free(e);
1655  Com_Printf("Com_ParseFireEffect: item effect of \"%s\" already has an effect assigned\n", name);
1656  return;
1657  }
1658 
1659  *stagePtr = e;
1660 }
1661 
1668 static bool Com_ParseFire (const char* name, const char** text, fireDef_t* fd)
1669 {
1670  const char* errhead = "Com_ParseFire: unexpected end of file";
1671 
1672  /* get its body */
1673  const char* token = Com_Parse(text);
1674 
1675  if (!*text || *token != '{') {
1676  Com_Printf("Com_ParseFire: fire definition \"%s\" without body ignored\n", name);
1677  return false;
1678  }
1679 
1680  do {
1681  token = Com_EParse(text, errhead, name);
1682  if (!*text)
1683  return true;
1684  if (*token == '}')
1685  return true;
1686 
1687  if (!Com_ParseBlockToken(name, text, fd, fdps, com_genericPool, token)) {
1688  if (Q_streq(token, "skill")) {
1689  int skill;
1690 
1691  token = Com_EParse(text, errhead, name);
1692  if (!*text)
1693  return false;
1694 
1695  for (skill = ABILITY_NUM_TYPES; skill < SKILL_NUM_TYPES; skill++)
1696  if (Q_streq(skillNames[skill], token)) {
1697  fd->weaponSkill = skill;
1698  break;
1699  }
1700  if (skill >= SKILL_NUM_TYPES)
1701  Com_Printf("Com_ParseFire: unknown weapon skill \"%s\" ignored (weapon %s)\n", token, name);
1702  } else if (Q_streq(token, "effect")) {
1703  Com_ParseFireEffect(fd, name, text);
1704  } else if (Q_streq(token, "range")) {
1705  token = Com_EParse(text, errhead, name);
1706  if (!*text)
1707  return false;
1708  fd->range = atof(token) * UNIT_SIZE;
1709  } else if (Q_streq(token, "splrad")) {
1710  token = Com_EParse(text, errhead, name);
1711  if (!*text)
1712  return false;
1713  fd->splrad = atof(token) * UNIT_SIZE;
1714  } else
1715  Com_Printf("Com_ParseFire: unknown token \"%s\" ignored (weapon %s)\n", token, name);
1716  }
1717  } while (*text);
1718 
1720  Com_Printf("Com_ParseFire: firedef for weapon \"%s\" has an invalid impact sound attenuation value set\n", name);
1721 
1723  Com_Printf("Com_ParseFire: firedef for weapon \"%s\" has an invalid fire sound attenuation value set\n", name);
1724 
1725  if (fd->weaponSkill < ABILITY_NUM_TYPES)
1726  Com_Printf("Com_ParseFire: firedef for weapon \"%s\" doesn't have a skill set\n", name);
1727 
1728  if (fd->shots == 1 && fd->delayBetweenShots > 0.0f) {
1729  Com_Printf("Com_ParseFire: firedef for weapon \"%s\" has delayBetweenShots set but is only a one-shot-firedef\n", name);
1730  fd->delayBetweenShots = 0.0f;
1731  }
1732 
1733  if (fd->name == nullptr) {
1734  Com_Printf("firedef without name\n");
1735  return false;
1736  }
1737 
1738  return true;
1739 }
1740 
1747 static void Com_ParseArmourOrResistance (const char* name, const char** text, short* ad, bool rating)
1748 {
1749  const char* errhead = "Com_ParseArmourOrResistance: unexpected end of file";
1750 
1751  /* get its body */
1752  const char* token = Com_Parse(text);
1753 
1754  if (!*text || *token != '{') {
1755  Com_Printf("Com_ParseArmourOrResistance: armour definition \"%s\" without body ignored\n", name);
1756  return;
1757  }
1758 
1759  do {
1760  int i;
1761  token = Com_EParse(text, errhead, name);
1762  if (!*text)
1763  return;
1764  if (*token == '}')
1765  return;
1766 
1767  for (i = 0; i < csi.numDTs; i++) {
1768  const damageType_t& dt = csi.dts[i];
1769  if (Q_streq(token, dt.id)) {
1770  token = Com_EParse(text, errhead, name);
1771  if (!*text)
1772  return;
1773  if (rating && !dt.showInMenu)
1774  Sys_Error("Com_ParseArmourOrResistance: You try to set a rating value for a none menu displayed damage type '%s'",
1775  dt.id);
1776  /* protection or rating values */
1777  ad[i] = atoi(token);
1778  break;
1779  }
1780  }
1781 
1782  if (i >= csi.numDTs)
1783  Com_Printf("Com_ParseArmourOrResistance: unknown damage type \"%s\" ignored (in %s)\n", token, name);
1784  } while (*text);
1785 }
1786 
1793 
1798 static linkedList_t* parseItemWeapons = nullptr;
1799 
1803  char* token;
1804 };
1805 
1806 static void Com_ParseFireDefinition (objDef_t* od, const char* name, const char** text)
1807 {
1809  Sys_Error("max weapons per objdef exceeded");
1810 
1811  /* get it's body */
1812  const char* token = Com_Parse(text);
1813  if (!*text || *token != '{') {
1814  Com_Printf("Com_ParseFireDefinition: weapon_mod \"%s\" without body ignored\n", name);
1815  return;
1816  }
1817 
1818  /* get weapon property */
1819  token = Com_Parse(text);
1820  if (!*text || !Q_streq(token, "weapon")) {
1821  Com_Printf("Com_ParseFireDefinition: weapon_mod \"%s\" weapon as first element expected.\n", name);
1822  return;
1823  }
1824 
1825  /* Save the weapon id. */
1826  token = Com_Parse(text);
1827  /* Store the current item-pointer and the weapon id for later linking of the "weapon" pointers */
1828  parseItemWeapon_t parse;
1829  parse.od = od;
1830  parse.numWeapons = od->numWeapons;
1831  parse.token = Mem_StrDup(token);
1832  LIST_Add(&parseItemWeapons, parse);
1833 
1834  /* For each firedef entry for this weapon. */
1835  do {
1836  const char* errhead = "Com_ParseFireDefinition: unexpected end of file (weapon_mod ";
1837  token = Com_EParse(text, errhead, name);
1838  if (!*text)
1839  return;
1840  if (*token == '}')
1841  break;
1842 
1843  if (Q_streq(token, "firedef")) {
1844  const weaponFireDefIndex_t weapFdsIdx = od->numWeapons;
1845  if (od->numFiredefs[weapFdsIdx] < MAX_FIREDEFS_PER_WEAPON) {
1846  const fireDefIndex_t fdIdx = od->numFiredefs[weapFdsIdx];
1847  fireDef_t* fd = &od->fd[weapFdsIdx][fdIdx];
1850  /* Parse firemode into fd[IDXweapon][IDXfiremode] */
1851  Com_ParseFire(name, text, fd);
1852  /* Self-link fd */
1853  fd->fdIdx = fdIdx;
1854  /* Self-link weapon_mod */
1855  fd->weapFdsIdx = weapFdsIdx;
1856  od->numFiredefs[od->numWeapons]++;
1857  } else {
1858  Com_Printf("Com_ParseFireDefinition: Too many firedefs at \"%s\". Max is %i\n", name, MAX_FIREDEFS_PER_WEAPON);
1859  }
1860  } else {
1861  Com_Printf("Unknown token '%s' - expected firedef\n", token);
1862  }
1863  } while (*text);
1864  od->numWeapons++;
1865 }
1866 
1867 static void Com_ParseObjDefEffect (objDef_t* od, const char* name, const char** text)
1868 {
1870  const effectStages_t stage = Com_ParseItemEffect(e, name, text);
1871  if (stage != EFFECT_STRENGTHEN) {
1872  Com_Printf("Com_ParseObjDefEffect: ignore invalid item effect stage for item: '%s'\n", name);
1873  Mem_Free(e);
1874  return;
1875  }
1876  if (od->strengthenEffect != nullptr) {
1877  Com_Printf("Com_ParseObjDefEffect: there is already a strengthen effect assigned to: '%s'\n", name);
1878  Mem_Free(e);
1879  return;
1880  }
1881  od->strengthenEffect = e;
1882 }
1883 
1888 static void Com_ParseItem (const char* name, const char** text)
1889 {
1890  /* search for items with same name */
1891  if (INVSH_GetItemByIDSilent(name) != nullptr) {
1892  Com_Printf("Com_ParseItem: weapon def \"%s\" with same name found, second ignored\n", name);
1893  return;
1894  }
1895 
1896  if (csi.numODs >= MAX_OBJDEFS)
1897  Sys_Error("Com_ParseItem: MAX_OBJDEFS exceeded\n");
1898 
1899  Com_DPrintf(DEBUG_SHARED, "...found item: '%s' (%i)\n", name, csi.numODs);
1900 
1901  /* initialize the object definition */
1902  objDef_t* od = &csi.ods[csi.numODs++];
1903  OBJZERO(*od);
1904 
1905  /* default is no craftitem */
1906  od->craftitem.type = MAX_ACITEMS;
1908  od->reloadSound = "weapons/reload-pistol";
1909  od->armourPath = od->image = od->type = od->model = od->name = "";
1910 
1911  od->id = Mem_StrDup(name);
1912  if (Q_strnull(od->id))
1913  Sys_Error("Com_ParseItem: no id given\n");
1914 
1915  od->idx = csi.numODs - 1;
1916 
1917  /* get it's body */
1918  const char* token = Com_Parse(text);
1919 
1920  if (!*text || *token != '{') {
1921  Com_Printf("Com_ParseItem: weapon def \"%s\" without body ignored\n", name);
1922  csi.numODs--;
1923  return;
1924  }
1925 
1926  const char* errhead = "Com_ParseItem: unexpected end of file (weapon ";
1927  int i;
1928 
1929  do {
1930  token = Com_EParse(text, errhead, name);
1931  if (!*text)
1932  break;
1933  if (*token == '}')
1934  break;
1935 
1936  if (!Com_ParseBlockToken(name, text, od, od_vals, com_genericPool, token)) {
1937  if (Q_streq(token, "craftweapon")) {
1938  /* parse a value */
1939  token = Com_EParse(text, errhead, name);
1940  if (od->numWeapons < MAX_WEAPONS_PER_OBJDEF) {
1941  parseItemWeapon_t parse;
1942  parse.od = od;
1943  parse.numWeapons = od->numWeapons;
1944  parse.token = Mem_StrDup(token);
1945  /* Store the current item-pointer and the weapon id for later linking of the "weapon" pointers */
1946  LIST_Add(&parseItemWeapons, parse);
1947  od->numWeapons++;
1948  } else {
1949  Com_Printf("Com_ParseItem: Too many weapon_mod definitions at \"%s\". Max is %i\n", name, MAX_WEAPONS_PER_OBJDEF);
1950  }
1951  } else if (Q_streq(token, "effect")) {
1952  Com_ParseObjDefEffect(od, name, text);
1953  } else if (Q_streq(token, "crafttype")) {
1954  /* Craftitem type definition. */
1955  token = Com_EParse(text, errhead, name);
1956  if (!*text)
1957  return;
1958 
1959  /* Check which type it is and store the correct one.*/
1960  for (i = 0; i < MAX_ACITEMS; i++) {
1961  if (Q_streq(token, air_slot_type_strings[i])) {
1963  break;
1964  }
1965  }
1966  if (i == MAX_ACITEMS)
1967  Com_Printf("AII_ParseAircraftItem: \"%s\" unknown craftitem type: \"%s\" - ignored.\n", name, token);
1968  } else if (Q_streq(token, "protection")) {
1969  Com_ParseArmourOrResistance(name, text, od->protection, false);
1970  } else if (Q_streq(token, "rating")) {
1971  Com_ParseArmourOrResistance(name, text, od->ratings, true);
1972  } else if (Q_streq(token, "weapon_mod")) {
1973  Com_ParseFireDefinition(od, name, text);
1974  } else {
1975  Com_Printf("Com_ParseItem: unknown token \"%s\" ignored (weapon %s)\n", token, name);
1976  }
1977  }
1978  } while (*text);
1979  if (od->productionCost == 0)
1980  od->productionCost = od->price;
1981 
1982  /* get size */
1983  for (i = SHAPE_SMALL_MAX_WIDTH - 1; i >= 0; i--)
1984  if (od->shape & (0x01010101 << i))
1985  break;
1986  od->sx = i + 1;
1987 
1988  for (i = SHAPE_SMALL_MAX_HEIGHT - 1; i >= 0; i--)
1989  if (od->shape & (0xFF << (i * SHAPE_SMALL_MAX_WIDTH)))
1990  break;
1991  od->sy = i + 1;
1992 
1993  if ((od->weapon || od->isAmmo() || od->isArmour() || od->implant) && !od->isVirtual && od->shape == 0) {
1994  Sys_Error("Item %s has no shape\n", od->id);
1995  }
1996 
1997  if (od->thrown && od->deplete && od->oneshot && od->ammo) {
1998  Sys_Error("Item %s has invalid parameters\n", od->id);
1999  }
2000 
2002  Com_Printf("Com_ParseItem: weapon \"%s\" has an invalid reload sound attenuation value set\n", od->id);
2003 }
2004 
2005 /*
2006 ==============================================================================
2007 IMPLANT DEFINITION INTERPRETER
2008 ==============================================================================
2009 */
2010 
2011 static const value_t implant_vals[] = {
2012  {"installationtime", V_INT, offsetof(implantDef_t, installationTime), 0},
2013  {"removetime", V_INT, offsetof(implantDef_t, removeTime), 0},
2014 
2015  {nullptr, V_NULL, 0, 0}
2016 };
2017 
2018 static void Com_ParseImplant (const char* name, const char** text)
2019 {
2020  /* search for implants with same name */
2021  if (INVSH_GetItemByIDSilent(name) != nullptr) {
2022  Com_Printf("Com_ParseImplant: implant def \"%s\" with same name found, second ignored\n", name);
2023  return;
2024  }
2025 
2026  if (csi.numImplants >= MAX_IMPLANTS)
2027  Sys_Error("Com_ParseImplant: MAX_IMPLANTS exceeded\n");
2028 
2029  Com_DPrintf(DEBUG_SHARED, "...found implant: '%s' (%i)\n", name, csi.numImplants);
2030 
2031  /* initialize the implant definition */
2032  implantDef_t* implant = &csi.implants[csi.numImplants++];
2033  OBJZERO(*implant);
2034  implant->id = Mem_StrDup(name);
2035  if (Q_strnull(implant->id))
2036  Sys_Error("Com_ParseImplant: no id given\n");
2037 
2038  implant->idx = csi.numImplants - 1;
2039 
2040  /* get it's body */
2041  const char* token = Com_Parse(text);
2042 
2043  if (!*text || *token != '{') {
2044  Com_Printf("Com_ParseImplant: implant def \"%s\" without body ignored\n", name);
2045  csi.numImplants--;
2046  return;
2047  }
2048 
2049  const char* errhead = "Com_ParseImplant: unexpected end of file (implant ";
2050  do {
2051  token = Com_EParse(text, errhead, name);
2052  if (!*text)
2053  break;
2054  if (*token == '}')
2055  break;
2056 
2057  if (!Com_ParseBlockToken(name, text, implant, implant_vals, com_genericPool, token)) {
2058  if (Q_streq(token, "item")) {
2059  token = Com_EParse(text, errhead, name);
2060  if (!*text) {
2061  Com_Printf("Com_ParseImplant: syntax error (implant %s)\n", name);
2062  break;
2063  }
2064  implant->item = INVSH_GetItemByID(token);
2065  } else {
2066  Com_Printf("Com_ParseImplant: unknown token \"%s\" ignored (implant %s)\n", token, name);
2067  }
2068  }
2069  } while (*text);
2070 
2071  if (implant->item == nullptr) {
2072  Sys_Error("implant %s without item found", name);
2073  }
2074 }
2075 
2076 /*
2077 ==============================================================================
2078 INVENTORY DEFINITION INTERPRETER
2079 ==============================================================================
2080 */
2081 
2082 static const value_t idps[] = {
2083  {"shape", V_SHAPE_BIG, offsetof(invDef_t, shape), 0},
2084  /* only a single item */
2085  {"single", V_BOOL, offsetof(invDef_t, single), MEMBER_SIZEOF(invDef_t, single)},
2086  /* Scrollable container */
2087  {"scroll", V_BOOL, offsetof(invDef_t, scroll), MEMBER_SIZEOF(invDef_t, scroll)},
2088  /* this is the implant container */
2089  {"implant", V_BOOL, offsetof(invDef_t, implant), MEMBER_SIZEOF(invDef_t, implant)},
2090  /* this is the armour container */
2091  {"armour", V_BOOL, offsetof(invDef_t, armour), MEMBER_SIZEOF(invDef_t, armour)},
2092  /* this is the headgear container */
2093  {"headgear", V_BOOL, offsetof(invDef_t, headgear), MEMBER_SIZEOF(invDef_t, headgear)},
2094  /* allow everything to be stored in this container (e.g armour and weapons) */
2095  {"all", V_BOOL, offsetof(invDef_t, all), MEMBER_SIZEOF(invDef_t, all)},
2096  /* Does not allow to put the same item more than once into the container */
2097  {"unique", V_BOOL, offsetof(invDef_t, unique), MEMBER_SIZEOF(invDef_t, unique)},
2098  {"temp", V_BOOL, offsetof(invDef_t, temp), MEMBER_SIZEOF(invDef_t, temp)},
2099  /* time units for moving something in */
2100  {"in", V_INT, offsetof(invDef_t, in), MEMBER_SIZEOF(invDef_t, in)},
2101  /* time units for moving something out */
2102  {"out", V_INT, offsetof(invDef_t, out), MEMBER_SIZEOF(invDef_t, out)},
2103 
2104  {nullptr, V_NULL, 0, 0}
2105 };
2106 
2107 static void Com_ParseInventory (const char* name, const char** text)
2108 {
2109  containerIndex_t cid;
2110 
2111  /* Special IDs for container. These are also used elsewhere, so be careful. */
2112  if (Q_streq(name, "right")) {
2113  cid = CID_RIGHT;
2114  } else if (Q_streq(name, "left")) {
2115  cid = CID_LEFT;
2116  } else if (Q_streq(name, "implant")) {
2117  cid = CID_IMPLANT;
2118  } else if (Q_streq(name, "belt")) {
2119  cid = CID_BELT;
2120  } else if (Q_streq(name, "holster")) {
2121  cid = CID_HOLSTER;
2122  } else if (Q_streq(name, "backpack")) {
2123  cid = CID_BACKPACK;
2124  } else if (Q_streq(name, "armour")) {
2125  cid = CID_ARMOUR;
2126  } else if (Q_streq(name, "floor")) {
2127  cid = CID_FLOOR;
2128  } else if (Q_streq(name, "equip")) {
2129  cid = CID_EQUIP;
2130  } else if (Q_streq(name, "headgear")) {
2131  cid = CID_HEADGEAR;
2132  } else {
2133  Sys_Error("Unknown inventory definition \"%s\". Aborting.\n", name);
2134  return; /* never reached */
2135  }
2136 
2137  /* search for containers with same name */
2138  if (!strncmp(name, csi.ids[cid].name, sizeof(csi.ids[cid].name))) {
2139  Com_Printf("Com_ParseInventory: inventory def \"%s\" with same name found, second ignored\n", name);
2140  return;
2141  }
2142 
2143  /* initialize the inventory definition */
2144  invDef_t* id = &csi.ids[cid];
2145  OBJZERO(*id);
2146 
2147  if (!Com_ParseBlock(name, text, id, idps, nullptr))
2148  return;
2149 
2150  Q_strncpyz(id->name, name, sizeof(id->name));
2151  id->id = cid;
2152 
2153  csi.numIDs++;
2154 }
2155 
2156 /*
2157 ==============================================================================
2158 EQUIPMENT DEFINITION INTERPRETER
2159 ==============================================================================
2160 */
2161 
2162 const char* const name_strings[NAME_NUM_TYPES] = {
2163  "neutral",
2164  "female",
2165  "male",
2166  "lastname",
2167  "female_lastname",
2168  "male_lastname"
2169 };
2170 
2173  {"mininterest", V_INT, offsetof(equipDef_t, minInterest), MEMBER_SIZEOF(equipDef_t, minInterest)},
2174  {"maxinterest", V_INT, offsetof(equipDef_t, maxInterest), MEMBER_SIZEOF(equipDef_t, maxInterest)},
2175  {"name", V_TRANSLATION_STRING, offsetof(equipDef_t, name), 0},
2176 
2177  {nullptr, V_NULL, 0, 0}
2178 };
2179 
2180 static void Com_ParseEquipment (const char* name, const char** text)
2181 {
2182  const char* errhead = "Com_ParseEquipment: unexpected end of file (equipment ";
2183  int i;
2184 
2185  /* search for equipments with same name */
2186  for (i = 0; i < csi.numEDs; i++)
2187  if (Q_streq(name, csi.eds[i].id))
2188  break;
2189 
2190  if (i < csi.numEDs) {
2191  Com_Printf("Com_ParseEquipment: equipment def \"%s\" with same name found, second ignored\n", name);
2192  return;
2193  }
2194 
2195  if (i >= MAX_EQUIPDEFS)
2196  Sys_Error("Com_ParseEquipment: MAX_EQUIPDEFS exceeded\n");
2197 
2198  /* initialize the equipment definition */
2199  equipDef_t* ed = &csi.eds[csi.numEDs++];
2200  OBJZERO(*ed);
2201 
2202  Q_strncpyz(ed->id, name, sizeof(ed->id));
2203  ed->name = ed->id;
2204 
2205  /* get it's body */
2206  const char* token = Com_Parse(text);
2207 
2208  if (!*text || *token != '{') {
2209  Com_Printf("Com_ParseEquipment: equipment def \"%s\" without body ignored\n", name);
2210  csi.numEDs--;
2211  return;
2212  }
2213 
2214  do {
2215  token = Com_EParse(text, errhead, name);
2216  if (!*text || *token == '}')
2217  return;
2218 
2220  if (Q_streq(token, "item")) {
2221  linkedList_t* list;
2222  if (!Com_ParseList(text, &list)) {
2223  Com_Error(ERR_DROP, "Com_ParseEquipment: error while reading equipment item tuple");
2224  }
2225  if (LIST_Count(list) != 2) {
2226  Com_Error(ERR_DROP, "Com_ParseEquipment: equipment item tuple must contains 2 elements (id amount)");
2227  }
2228  const char* itemToken = (char*)list->data;
2229  const char* amountToken = (char*)list->next->data;
2230 
2231  const objDef_t* od;
2232  od = INVSH_GetItemByID(itemToken);
2233  if (od) {
2234  const int n = atoi(amountToken);
2235  if (ed->numItems[od->idx])
2236  Com_Printf("Com_ParseEquipment: item '%s' is used several times in def '%s'. Only last entry will be taken into account.\n",
2237  od->id, name);
2238  if (n)
2239  ed->numItems[od->idx] = n;
2240  } else {
2241  Com_Printf("Com_ParseEquipment: unknown token \"%s\" ignored (equipment %s)\n", itemToken, name);
2242  }
2243  LIST_Delete(&list);
2244  } else if (Q_streq(token, "aircraft")) {
2245  linkedList_t* list;
2246  if (!Com_ParseList(text, &list)) {
2247  Com_Error(ERR_DROP, "Com_ParseEquipment: error while reading equipment aircraft tuple");
2248  }
2249  if (LIST_Count(list) != 2) {
2250  Com_Error(ERR_DROP, "Com_ParseEquipment: equipment aircraft tuple must contains 2 elements (id amount)");
2251  }
2252  const char* aircraftToken = (char*)list->data;
2253  const char* amountToken = (char*)list->next->data;
2254 
2256  type = Com_DropShipShortNameToID(aircraftToken);
2257  const int n = atoi(amountToken);
2258  if (ed->numAircraft[type])
2259  Com_Printf("Com_ParseEquipment: aircraft type '%i' is used several times in def '%s'. Only last entry will be taken into account.\n",
2260  type, name);
2261  if (n)
2262  ed->numAircraft[type] = n;
2263  LIST_Delete(&list);
2264  } else {
2265  Sys_Error("unknown token in equipment in definition %s: '%s'", ed->id, token);
2266  }
2267  }
2268  } while (*text);
2269 }
2270 
2271 
2272 /*
2273 ==============================================================================
2274 NAME AND TEAM DEFINITION INTERPRETER
2275 ==============================================================================
2276 */
2277 
2283 static const char* Com_GiveName (int gender, const teamDef_t* td)
2284 {
2285  int name = 0;
2286 
2287 #ifdef DEBUG
2288  for (int j = 0; j < NAME_NUM_TYPES; j++)
2289  name += td->numNames[j];
2290  if (!name)
2291  Sys_Error("Could not find any valid name definitions for category '%s'\n", td->id);
2292 #endif
2293  /* found category */
2294  if (!td->numNames[gender]) {
2295 #ifdef DEBUG
2296  Com_DPrintf(DEBUG_ENGINE, "No valid name definitions for gender %i in category '%s'\n", gender, td->id);
2297 #endif
2298  return nullptr;
2299  }
2300  name = rand() % td->numNames[gender];
2301 
2302  /* skip names */
2303  linkedList_t* list = td->names[gender];
2304  for (int j = 0; j < name; j++) {
2305  assert(list);
2306  list = list->next;
2307  }
2308 
2309  /* store the name */
2310  return (const char*)list->data;
2311 }
2312 
2318 static teamDef_t::model_t const* Com_GiveModel (int gender, const teamDef_t* td)
2319 {
2320  /* found category */
2321  if (!td->numModels[gender]) {
2322  Com_Printf("Com_GiveModel: no models defined for gender %i and category '%s'\n", gender, td->id);
2323  return nullptr;
2324  }
2325 
2326  /* search one of the model definitions */
2327  size_t n = rand() % td->numModels[gender];
2328 
2329  /* skip models and unwanted info */
2330  const linkedList_t* list = td->models[gender];
2331  while (n-- != 0) {
2332  assert(list);
2333  list = list->next;
2334  }
2335 
2336  /* return the value */
2337  return static_cast<teamDef_t::model_t const*>(list->data);
2338 }
2339 
2345 const teamDef_t* Com_GetTeamDefinitionByID (const char* team)
2346 {
2347  /* get team definition */
2348  for (int i = 0; i < csi.numTeamDefs; i++) {
2349  const teamDef_t* t = &csi.teamDef[i];
2350  if (Q_streq(team, t->id))
2351  return t;
2352  }
2353 
2354  Com_Printf("Com_GetTeamDefinitionByID: could not find team definition for '%s' in team definitions\n", team);
2355  return nullptr;
2356 }
2357 
2359 {
2360  if (!chr->teamDef)
2361  return false;
2362 
2363  /* get model */
2364  teamDef_t::model_t const* const model = Com_GiveModel(chr->gender, chr->teamDef);
2365  if (!model)
2366  return false;
2367 
2368  Q_strncpyz(chr->path, model->path, sizeof(chr->path));
2369  Q_strncpyz(chr->body, model->body, sizeof(chr->body));
2370  Q_strncpyz(chr->head, model->head, sizeof(chr->head));
2371  chr->bodySkin = model->bodySkin;
2372  chr->headSkin = model->headSkin;
2373 
2374  return true;
2375 }
2376 
2382 static int Com_GetGender (const teamDef_t* teamDef)
2383 {
2384  int gender;
2385  int numModels = 0;
2386  for (gender = 0; gender < NAME_LAST; ++gender)
2387  if (teamDef->numNames[gender] > 0 && teamDef->numNames[gender + NAME_LAST] > 0)
2388  numModels += teamDef->numModels[gender];
2389  if (numModels == 0)
2390  Com_Error(ERR_DROP, "Could not set character values for team '%s'", teamDef->name);
2391  int roll = rand() % numModels;
2392  for (gender = 0; gender < NAME_LAST; ++gender)
2393  if (teamDef->numNames[gender] > 0 && teamDef->numNames[gender + NAME_LAST] > 0) {
2394  if (roll < teamDef->numModels[gender])
2395  break;
2396  roll -= teamDef->numModels[gender];
2397  }
2398  return gender;
2399 }
2400 
2408 void Com_GetCharacterValues (const char* teamDefition, character_t* chr)
2409 {
2410  int retry = 1000;
2411 
2412  assert(chr);
2413 
2414  chr->teamDef = Com_GetTeamDefinitionByID(teamDefition);
2415  if (chr->teamDef == nullptr)
2416  Com_Error(ERR_DROP, "Com_GetCharacterValues: could not find team '%s' in team definitions", teamDefition);
2417 
2418  if (chr->teamDef->size != ACTOR_SIZE_INVALID)
2419  chr->fieldSize = chr->teamDef->size;
2420  else
2422 
2423  /* get the models */
2424  while (retry--) {
2425  const int gender = Com_GetGender(chr->teamDef);
2426 
2427  chr->gender = gender;
2428 
2429  /* get name */
2430  const char* str = Com_GiveName(gender, chr->teamDef);
2431  if (!str)
2432  continue;
2433  Q_strncpyz(chr->name, str, sizeof(chr->name));
2434  Q_strcat(chr->name, sizeof(chr->name), " ");
2435  str = Com_GiveName(gender + NAME_LAST, chr->teamDef);
2436  if (!str)
2437  continue;
2438  Q_strcat(chr->name, sizeof(chr->name), "%s", str);
2439 
2440  if (!Com_GetCharacterModel(chr))
2441  continue;
2442  return;
2443  }
2444  Com_Error(ERR_DROP, "Could not set character values for team '%s'\n", teamDefition);
2445 }
2446 
2452 static void Com_ParseActorNames (const char* name, const char** text)
2453 {
2454  const char* errhead = "Com_ParseNames: unexpected end of file (names ";
2455  teamNames_t nameList;
2456 
2458  if (Q_streq(name, names->id)) {
2459  Com_Printf("Com_ParseActorNames: Name list with same name found, second ignored '%s'\n", name);
2460  return;
2461  }
2462  }
2463 
2464  OBJZERO(nameList);
2465  Q_strncpyz(nameList.id, name, sizeof(nameList.id));
2466 
2467  /* get name list body */
2468  const char* token = Com_Parse(text);
2469  if (!*text || *token != '{') {
2470  Com_Printf("Com_ParseActorNames: names def \"%s\" without body ignored\n", name);
2471  return;
2472  }
2473 
2474  do {
2475  /* get the name type */
2476  token = Com_EParse(text, errhead, name);
2477  if (!*text)
2478  break;
2479  if (*token == '}')
2480  break;
2481 
2482  const int nameType = Com_FindNameType(token);
2483  if (nameType == -1) {
2484  Com_Error(ERR_DROP, "Com_ParseActorNames: name type \"%s\" unknown", token);
2485  }
2486 
2487  linkedList_t* list;
2488  if (!Com_ParseList(text, &list)) {
2489  Com_Error(ERR_DROP, "Com_ParseActorNames: error while reading names (%s)", name);
2490  }
2491 
2492  for (linkedList_t* element = list; element != nullptr; element = element->next) {
2493  /* some names can be translatable */
2494  const char* n = (char*)element->data;
2495  if (*n == '_')
2496  token++;
2497  LIST_AddString(&nameList.names[nameType], n);
2498  nameList.numNames[nameType]++;
2499  }
2500  LIST_Delete(&list);
2501 
2502  /* lastname is different */
2503  /* fill female and male lastnames from neutral lastnames */
2504  if (nameType == NAME_LAST) {
2505  for (int i = NAME_NUM_TYPES - 1; i > NAME_LAST; i--) {
2506  nameList.names[i] = nameList.names[NAME_LAST];
2507  nameList.numNames[i] = nameList.numNames[NAME_LAST];
2508  }
2509  }
2510 
2511  } while (*text);
2512 
2513  if (nameList.numNames[NAME_FEMALE] && !nameList.numNames[NAME_FEMALE_LAST])
2514  Sys_Error("Com_ParseNames: '%s' has no female lastname category\n", nameList.id);
2515  if (nameList.numNames[NAME_MALE] && !nameList.numNames[NAME_MALE_LAST])
2516  Sys_Error("Com_ParseNames: '%s' has no male lastname category\n", nameList.id);
2517  if (nameList.numNames[NAME_NEUTRAL] && !nameList.numNames[NAME_LAST])
2518  Sys_Error("Com_ParseNames: '%s' has no neutral lastname category\n", nameList.id);
2519 
2520  LIST_Add(&csi.actorNames, nameList);
2521 }
2522 
2528 static void Com_ParseActorModels (const char* name, const char** text, teamDef_t* td)
2529 {
2530  const char* errhead = "Com_ParseActorModels: unexpected end of file (actors ";
2531 
2532  /* get name list body body */
2533  const char* token = Com_Parse(text);
2534 
2535  if (!*text || *token != '{') {
2536  Com_Printf("Com_ParseActorModels: actor def \"%s\" without body ignored\n", td->id);
2537  return;
2538  }
2539 
2540  do {
2541  /* get the name type */
2542  token = Com_EParse(text, errhead, name);
2543  if (!*text)
2544  break;
2545  if (*token == '}')
2546  break;
2547 
2548  const int nameType = Com_FindNameType(token);
2549  if (nameType == -1) {
2550  Com_Error(ERR_DROP, "Com_ParseActorModels: name type \"%s\" unknown", token);
2551  }
2552 
2553  linkedList_t* list;
2554  if (!Com_ParseList(text, &list)) {
2555  Com_Error(ERR_DROP, "Com_ParseActorModels: error while reading model tuple (%s)", name);
2556  }
2557  if (LIST_Count(list) != 5) {
2558  LIST_Delete(&list);
2559  Com_Error(ERR_DROP, "Com_ParseActorModels: model tuple must contains 5 elements");
2560  }
2561 
2562  linkedList_t* element = list;
2563  const char* pathToken = (const char*)element->data;
2564  element = element->next;
2565  const char* bodyToken = (const char*)element->data;
2566  element = element->next;
2567  const char* headToken = (const char*)element->data;
2568  element = element->next;
2569  const char* bodySkinToken = (const char*)element->data;
2570  element = element->next;
2571  const char* headSkinToken = (const char*)element->data;
2572 
2573  teamDef_t::model_t model;
2574  model.path = Mem_StrDup(pathToken);
2575  model.body = Mem_StrDup(bodyToken);
2576  model.head = Mem_StrDup(headToken);
2577  model.bodySkin = atoi(bodySkinToken);
2578  model.headSkin = atoi(headSkinToken);
2579 
2580  LIST_Add(&td->models[nameType], model);
2581  td->numModels[nameType]++;
2582  LIST_Delete(&list);
2583 
2584  } while (*text);
2585 }
2586 
2592 static void Com_ParseActorSounds (const char* name, const char** text, teamDef_t* td)
2593 {
2594  const char* const errhead = "Com_ParseActorSounds: unexpected end of file (actorsounds ";
2595  int i;
2596 
2597  /* get name list body body */
2598  const char* token = Com_Parse(text);
2599 
2600  if (!*text || *token != '{') {
2601  Com_Printf("Com_ParseActorSounds: actorsounds def \"%s\" without body ignored\n", name);
2602  return;
2603  }
2604 
2605  do {
2606  /* get the name type */
2607  token = Com_EParse(text, errhead, name);
2608  if (!*text)
2609  break;
2610  if (*token == '}')
2611  break;
2612 
2613  for (i = 0; i < NAME_LAST; i++)
2614  if (Q_streq(token, name_strings[i])) {
2615  token = Com_EParse(text, errhead, name);
2616  if (!*text)
2617  break;
2618  if (*token != '{')
2619  break;
2620 
2621  do {
2622  /* get the sounds */
2623  token = Com_EParse(text, errhead, name);
2624  if (!*text)
2625  break;
2626  if (*token == '}')
2627  break;
2628  if (Q_streq(token, "hurtsound")) {
2629  token = Com_EParse(text, errhead, name);
2630  if (!*text)
2631  break;
2632  LIST_AddString(&td->sounds[SND_HURT][i], token);
2633  td->numSounds[SND_HURT][i]++;
2634  } else if (Q_streq(token, "deathsound")) {
2635  token = Com_EParse(text, errhead, name);
2636  if (!*text)
2637  break;
2638  LIST_AddString(&td->sounds[SND_DEATH][i], token);
2639  td->numSounds[SND_DEATH][i]++;
2640  } else {
2641  Com_Printf("Com_ParseActorSounds: unknown token \"%s\" ignored (actorsounds %s)\n", token, name);
2642  }
2643  } while (*text);
2644  break; /* next gender sound definition */
2645  }
2646 
2647  if (i == NAME_NUM_TYPES)
2648  Com_Printf("Com_ParseActorSounds: unknown token \"%s\" ignored (actorsounds %s)\n", token, name);
2649 
2650  } while (*text);
2651 }
2652 
2653 static const BodyData* Com_GetBodyTemplateByID (const char* id)
2654 {
2656  if (Q_streq(id, bd->id()))
2657  return bd;
2658  Com_Printf("Com_GetBodyTemplateByID: could not find template: '%s'\n", id);
2659  return nullptr;
2660 }
2661 
2662 static const teamNames_t* Com_GetNameListByID (const char* id)
2663 {
2665  if (Q_streq(id, names->id))
2666  return names;
2667  Com_Printf("Com_GetNameListByID: could not find name list: '%s'\n", id);
2668  return nullptr;
2669 }
2670 
2672 static const value_t teamDefValues[] = {
2673  {"tech", V_STRING, offsetof(teamDef_t, tech), 0},
2674  {"footstepsound", V_STRING, offsetof(teamDef_t, footstepSound), 0},
2675  {"name", V_TRANSLATION_STRING, offsetof(teamDef_t, name), 0},
2676  {"armour", V_BOOL, offsetof(teamDef_t, armour), MEMBER_SIZEOF(teamDef_t, armour)},
2677  {"weapons", V_BOOL, offsetof(teamDef_t, weapons), MEMBER_SIZEOF(teamDef_t, weapons)},
2678  {"size", V_INT, offsetof(teamDef_t, size), MEMBER_SIZEOF(teamDef_t, size)},
2679  {"hit_particle", V_STRING, offsetof(teamDef_t, hitParticle), 0},
2680  {"death_texture", V_STRING, offsetof(teamDef_t, deathTextureName), 0},
2681  {"team", V_TEAM, offsetof(teamDef_t, team), MEMBER_SIZEOF(teamDef_t, team)},
2682  {"robot", V_BOOL, offsetof(teamDef_t, robot), MEMBER_SIZEOF(teamDef_t, robot)},
2683 
2684  {nullptr, V_NULL, 0, 0}
2685 };
2686 
2687 static void Com_ParseTeam (const char* name, const char** text)
2688 {
2689  teamDef_t* td;
2690  const char* errhead = "Com_ParseTeam: unexpected end of file (team ";
2691  int i;
2692 
2693  /* check for additions to existing name categories */
2694  for (i = 0, td = csi.teamDef; i < csi.numTeamDefs; i++, td++)
2695  if (Q_streq(td->id, name))
2696  break;
2697 
2698  /* reset new category */
2699  if (i == csi.numTeamDefs) {
2700  if (csi.numTeamDefs < MAX_TEAMDEFS) {
2701  OBJZERO(*td);
2702  /* index backlink */
2703  td->idx = csi.numTeamDefs;
2704  csi.numTeamDefs++;
2705  } else {
2706  Com_Printf("CL_ParseTeam: Too many team definitions, '%s' ignored.\n", name);
2707  return;
2708  }
2709  } else {
2710  Com_Printf("CL_ParseTeam: Team with same name found, second ignored '%s'\n", name);
2711  Com_SkipBlock(text);
2712  return;
2713  }
2714 
2715  Q_strncpyz(td->id, name, sizeof(td->id));
2716  td->armour = td->weapons = true; /* default values */
2717  td->onlyWeapon = nullptr;
2718 
2719  /* get name list body body */
2720  const char* token = Com_Parse(text);
2721 
2722  if (!*text || *token != '{') {
2723  Com_Printf("Com_ParseTeam: team def \"%s\" without body ignored\n", name);
2724  if (csi.numTeamDefs - 1 == td - csi.teamDef)
2725  csi.numTeamDefs--;
2726  return;
2727  }
2728 
2729  do {
2730  /* get the name type */
2731  token = Com_EParse(text, errhead, name);
2732  if (!*text)
2733  break;
2734  if (*token == '}')
2735  break;
2736 
2737  if (!Com_ParseBlockToken(name, text, td, teamDefValues, nullptr, token)) {
2738  if (Q_streq(token, "onlyWeapon")) {
2739  token = Com_EParse(text, errhead, name);
2740  if (!*text)
2741  return;
2742  const objDef_t* od = INVSH_GetItemByID(token);
2743 
2744  if (od)
2745  td->onlyWeapon = od;
2746  else
2747  Sys_Error("Com_ParseTeam: Could not get item definition for '%s'", token);
2748  } else if (Q_streq(token, "templates")) {
2749  linkedList_t* list;
2750  if (!Com_ParseList(text, &list)) {
2751  Com_Error(ERR_DROP, "Com_ParseTeam: error while reading templates (team \"%s\")", name);
2752  }
2753 
2754  for (linkedList_t* element = list; element != nullptr; element = element->next) {
2755  for (i = 0; i < td->numTemplates; i++) {
2756  if (Q_streq(token, td->characterTemplates[i]->id)) {
2757  Com_Printf("Com_ParseTeam: template %s used more than once in team def %s second ignored", (char*)element->data, name);
2758  break;
2759  }
2760  }
2761  if (i >= td->numTemplates) {
2762  const chrTemplate_t* ct = Com_GetCharacterTemplateByID((char*)element->data);
2763  if (ct)
2764  td->characterTemplates[td->numTemplates++] = ct;
2765  else
2766  Sys_Error("Com_ParseTeam: Could not get character template for '%s' in %s", (char*)element->data, name);
2767  } else
2768  break;
2769  }
2770  LIST_Delete(&list);
2771  } else if (Q_streq(token, "bodytype")) {
2772  token = Com_EParse(text, errhead, name);
2773  const BodyData* bd = Com_GetBodyTemplateByID(token);
2774  if (bd == nullptr)
2775  Sys_Error("Com_ParseTeam: Could not find body type %s in team def %s\n", token, name);
2776  td->bodyTemplate = bd;
2777  } else if (Q_streq(token, "names")) {
2778  token = Com_EParse(text, errhead, name);
2779  const teamNames_t* nameList = Com_GetNameListByID(token);
2780  if (nameList == nullptr)
2781  Sys_Error("Com_ParseTeam: Could not find name list %s in team def %s\n", token, name);
2782  td->names = nameList->names;
2783  td->numNames = nameList->numNames;
2784  } else if (Q_streq(token, "models"))
2785  Com_ParseActorModels(name, text, td);
2786  else if (Q_streq(token, "actorsounds"))
2787  Com_ParseActorSounds(name, text, td);
2788  else if (Q_streq(token, "resistance"))
2789  Com_ParseArmourOrResistance(name, text, td->resistance, false);
2790  else
2791  Com_Printf("Com_ParseTeam: unknown token \"%s\" ignored (team %s)\n", token, name);
2792  }
2793  } while (*text);
2794 
2795  if (td->deathTextureName[0] == '\0') {
2796  const int i = rand() % MAX_DEATH;
2797  Q_strncpyz(td->deathTextureName, va("pics/sfx/blood_%i", i), sizeof(td->deathTextureName));
2798  Com_DPrintf(DEBUG_CLIENT, "Using random blood for teamdef: '%s' (%i)\n", td->id, i);
2799  }
2800  if (td->bodyTemplate == nullptr)
2801  Sys_Error("Teamdef without body data: %s\n", td->id);
2802 }
2803 
2809 const chrTemplate_t* Com_GetCharacterTemplateByID (const char* chrTemplate)
2810 {
2811  if (Q_strnull(chrTemplate))
2812  return nullptr;
2813 
2814  /* get character template */
2815  for (int i = 0; i < csi.numChrTemplates; i++)
2816  if (Q_streq(chrTemplate, csi.chrTemplates[i].id))
2817  return &csi.chrTemplates[i];
2818 
2819  Com_Printf("Com_GetCharacterTemplateByID: could not find character template: '%s'\n", chrTemplate);
2820  return nullptr;
2821 }
2822 
2823 static const value_t ugvValues[] = {
2824  {"tu", V_INT, offsetof(ugv_t, tu), MEMBER_SIZEOF(ugv_t, tu)},
2825  {"weapon", V_STRING, offsetof(ugv_t, weapon), 0},
2826  {"armour", V_STRING, offsetof(ugv_t, armour), 0},
2827  {"actors", V_STRING, offsetof(ugv_t, actors), 0},
2828  {"price", V_INT, offsetof(ugv_t, price), 0},
2829 
2830  {nullptr, V_NULL, 0, 0}
2831 };
2832 
2837 static void Com_ParseUGVs (const char* name, const char** text)
2838 {
2839  for (int i = 0; i < csi.numUGV; i++) {
2840  if (Q_streq(name, csi.ugvs[i].id)) {
2841  Com_Printf("Com_ParseUGVs: ugv \"%s\" with same name already loaded\n", name);
2842  return;
2843  }
2844  }
2845 
2846  if (csi.numUGV >= MAX_UGV) {
2847  Com_Printf("Com_ParseUGVs: Too many UGV descriptions, '%s' ignored.\n", name);
2848  return;
2849  }
2850 
2851  /* parse ugv */
2852  ugv_t* ugv = &csi.ugvs[csi.numUGV];
2853  OBJZERO(*ugv);
2854 
2855  if (Com_ParseBlock(name, text, ugv, ugvValues, nullptr)) {
2856  ugv->id = Mem_PoolStrDup(name, com_genericPool, 0);
2857  ugv->idx = csi.numUGV;
2858  csi.numUGV++;
2859  }
2860 }
2861 
2865 static void Com_ParseCharacterTemplate (const char* name, const char** text)
2866 {
2867  const char* errhead = "Com_ParseCharacterTemplate: unexpected end of file";
2868  int i;
2869 
2870  for (i = 0; i < csi.numChrTemplates; i++)
2871  if (Q_streq(name, csi.chrTemplates[i].id)) {
2872  Com_Printf("Com_ParseCharacterTemplate: Template with same name found, second ignored '%s'\n", name);
2873  return;
2874  }
2875 
2876  if (i >= MAX_CHARACTER_TEMPLATES)
2877  Sys_Error("Com_ParseCharacterTemplate: too many character templates");
2878 
2879  /* initialize the character template */
2881  OBJZERO(*ct);
2882 
2883  Q_strncpyz(ct->id, name, sizeof(ct->id));
2884 
2885  const char* token = Com_Parse(text);
2886 
2887  if (!*text || *token != '{') {
2888  Com_Printf("Com_ParseCharacterTemplate: character template \"%s\" without body ignored\n", name);
2889  csi.numChrTemplates--;
2890  return;
2891  }
2892 
2893  do {
2894  token = Com_EParse(text, errhead, name);
2895  if (!*text || *token == '}')
2896  return;
2897 
2898  for (i = 0; i < SKILL_NUM_TYPES + 1; i++)
2899  if (Q_streq(token, skillNames[i])) {
2900  /* found a definition */
2901  token = Com_EParse(text, errhead, name);
2902  if (!*text)
2903  return;
2904 
2905  Com_EParseValue(ct->skills[i], token, V_INT2, 0, sizeof(ct->skills[i]));
2906  break;
2907  }
2908  if (i >= SKILL_NUM_TYPES + 1) {
2909  if (Q_streq(token, "rate")) {
2910  token = Com_EParse(text, errhead, name);
2911  if (!*text)
2912  return;
2913  ct->rate = atof(token);
2914  } else
2915  Com_Printf("Com_ParseCharacterTemplate: unknown token \"%s\" ignored (template %s)\n", token, name);
2916  }
2917  } while (*text);
2918 }
2919 
2920 static const value_t bodyPartValues[] = {
2921  {"name", V_TRANSLATION_STRING, offsetof(BodyPartData, name), 0},
2922  {"hit_area", V_COLOR, offsetof(BodyPartData, shape), MEMBER_SIZEOF(BodyPartData, shape)},
2923  {"bleeding_rate", V_INT, offsetof(BodyPartData, bleedingFactor), MEMBER_SIZEOF(BodyPartData, bleedingFactor)},
2924  {"wound_threshold", V_INT, offsetof(BodyPartData, woundThreshold), MEMBER_SIZEOF(BodyPartData, woundThreshold)},
2925 
2926  {nullptr, V_NULL, 0, 0}
2927 };
2928 
2929 static const char* const penaltyNames[MODIFIER_MAX] = {
2930  "accuracy", "shooting_tu", "movement_tu", "detection", "reaction_time", "max_tu"
2931 };
2932 
2933 static void Com_ParseBodyPart (const char* name, const char** text, BodyData* bd)
2934 {
2935  const char* errhead = "Com_ParseBodyPart: unexpected end of file";
2936  int i;
2937 
2938  for (i = 0; i < bd->numBodyParts(); i++) {
2939  if (Q_streq(name, bd->id(i))) {
2940  Com_Printf("Com_ParseBodyPart: BodyPart with same name found, second ignored '%s'\n", name);
2941  return;
2942  }
2943  }
2944 
2945  if (i > BODYPART_MAXTYPE) {
2946  Com_Printf("Com_ParseBodyPart: too many BodyParts '%s' ignored ('%s')\n", name, bd->id());
2947  }
2948 
2949  BodyPartData bp;
2950  OBJZERO(bp);
2951  Q_strncpyz(bp.id, name, sizeof(bp.id));
2952 
2953  const char* token = Com_Parse(text);
2954 
2955  if (!*text || *token != '{') {
2956  Com_Printf("Com_ParseBodyPart: BodyPart '%s' without body ignored\n", name);
2957  return;
2958  }
2959 
2960  do {
2961  token = Com_EParse(text, errhead, name);
2962  if (!*text || *token == '}')
2963  break;
2964 
2965  if (!Com_ParseBlockToken(name, text, &bp, bodyPartValues, nullptr, token)) {
2966  if (Q_streq(token, "penalty")) {
2967  linkedList_t* list;
2968  if (!Com_ParseList(text, &list)) {
2969  Com_Error(ERR_DROP, "Com_ParseBodyPart: error while reading penalties ('%s')", name);
2970  }
2971 
2972  if (LIST_Count(list) != 2) {
2973  LIST_Delete(&list);
2974  Com_Error(ERR_DROP, "Com_ParseBodyPart: penalty tuple must contain 2 elements ('%s')", name);
2975  }
2976 
2977  linkedList_t* element = list;
2978  for (i = 0; i < MODIFIER_MAX; i++) {
2979  if (Q_streq(static_cast<const char*>(element->data), penaltyNames[i])) {
2980  /* Found a definition */
2981  element = element->next;
2982  Com_EParseValue(&bp.penalties[i], static_cast<const char*>(element->data), V_INT, 0, sizeof(bp.penalties[i]));
2983  break;
2984  }
2985  }
2986  if (i >= MODIFIER_MAX)
2987  Com_Printf("Com_ParseBodyPart: Unknown penalty '%s' ignored ('%s')\n", static_cast<const char*>(element->data), name);
2988 
2989  LIST_Delete(&list);
2990  } else {
2991  Com_Printf("Com_ParseBodyPart: Unknown token '%s' ignored ('%s')\n", token, name);
2992  }
2993  }
2994  } while (*text);
2995 
2996  bd->addBodyPart(bp);
2997 }
2998 
2999 static void Com_ParseBodyTemplate (const char* name, const char** text)
3000 {
3001  const char* errhead = "Com_ParseBodyTemplate: unexpected end of file";
3002  BodyData bd;
3003 
3005  if (Q_streq(name, bt->id())) {
3006  Com_Printf("Com_ParseBodyTemplate: BodyTemplate with same name found, second ignored '%s'\n", name);
3007  return;
3008  }
3009  }
3010 
3011  const char* token = Com_Parse(text);
3012 
3013  if (!*text || *token != '{') {
3014  Com_Printf("Com_ParseBodyTemplate: body template '%s' without body ignored\n", name);
3015  return;
3016  }
3017 
3018  bd.setId(name);
3019 
3020  do {
3021  token = Com_EParse(text, errhead, name);
3022  if (!*text || *token == '}')
3023  break;
3024 
3025  if (Q_streq(token, "bodypart")) {
3026  token = Com_EParse(text, errhead, name);
3027  if (!*text)
3028  break;
3029 
3030  Com_ParseBodyPart (token, text, &bd);
3031  } else {
3032  Com_Printf("Com_ParseBodyTemplate: unknown token '%s' ignored ('%s')\n", token, name);
3033  }
3034  } while (*text);
3035 
3036  if (bd.numBodyParts() < 1) {
3037  Com_Printf("Body template without bodyparts %s ignored!\n", name);
3038  return;
3039  }
3040 
3041  LIST_Add(&csi.bodyTemplates, bd);
3042 }
3043 
3044 /*
3045 ==============================================================================
3046 TERRAIN PARSERS
3047 ==============================================================================
3048 */
3049 
3050 #define TERRAIN_HASH_SIZE 64
3052 
3053 static const value_t terrainTypeValues[] = {
3054  {"footstepsound", V_HUNK_STRING, offsetof(terrainType_t, footstepSound), 0},
3055  {"particle", V_HUNK_STRING, offsetof(terrainType_t, particle), 0},
3056  {"footstepvolume", V_FLOAT, offsetof(terrainType_t, footstepVolume), 0},
3057  {"bouncefraction", V_FLOAT, offsetof(terrainType_t, bounceFraction), 0},
3058 
3059  {nullptr, V_NULL, 0, 0}
3060 };
3061 
3067 const terrainType_t* Com_GetTerrainType (const char* textureName)
3068 {
3069  assert(textureName);
3070  const unsigned hash = Com_HashKey(textureName, TERRAIN_HASH_SIZE);
3071  for (const terrainType_t* t = terrainTypesHash[hash]; t; t = t->hash_next) {
3072  if (Q_streq(textureName, t->texture))
3073  return t;
3074  }
3075 
3076  return nullptr;
3077 }
3078 
3084 static void Com_ParseTerrain (const char* name, const char** text)
3085 {
3086  /* check for additions to existing name categories */
3087  if (Com_GetTerrainType(name) != nullptr) {
3088  Com_Printf("Terrain definition with same name already parsed: '%s'\n", name);
3089  return;
3090  }
3091 
3094  t->bounceFraction = 1.0f;
3095 
3097  const unsigned hash = Com_HashKey(name, TERRAIN_HASH_SIZE);
3099  /* link in terrainTypesHash[hash] should be nullptr on the first run */
3101  terrainTypesHash[hash] = t;
3102  } else {
3103  Mem_Free(t);
3104  }
3105 }
3106 
3107 /*
3108 ==============================================================================
3109 GAMETYPE INTERPRETER
3110 ==============================================================================
3111 */
3112 
3114 static const value_t gameTypeValues[] = {
3115  {"name", V_TRANSLATION_STRING, offsetof(gametype_t, name), 0},
3116  {nullptr, V_NULL, 0, 0}
3117 };
3118 
3119 static void Com_ParseGameTypes (const char* name, const char** text)
3120 {
3121  const char* errhead = "Com_ParseGameTypes: unexpected end of file (gametype ";
3122  int i;
3123 
3124  /* get it's body */
3125  const char* token = Com_Parse(text);
3126  if (!*text || *token != '{') {
3127  Com_Printf("Com_ParseGameTypes: gametype \"%s\" without body ignored\n", name);
3128  return;
3129  }
3130 
3131  /* search for game types with same name */
3132  for (i = 0; i < csi.numGTs; i++)
3133  if (!strncmp(token, csi.gts[i].id, MAX_VAR))
3134  break;
3135 
3136  if (i == csi.numGTs) {
3137  if (i >= MAX_GAMETYPES)
3138  Sys_Error("Com_ParseGameTypes: MAX_GAMETYPES exceeded");
3139  gametype_t* gt = &csi.gts[csi.numGTs++];
3140  OBJZERO(*gt);
3141  Q_strncpyz(gt->id, name, sizeof(gt->id));
3142  if (csi.numGTs >= MAX_GAMETYPES)
3143  Sys_Error("Com_ParseGameTypes: Too many gametypes.");
3144 
3145  do {
3146  token = Com_EParse(text, errhead, name);
3147  if (!*text)
3148  break;
3149  if (*token == '}')
3150  break;
3151 
3152  if (!Com_ParseBlockToken(name, text, gt, gameTypeValues, nullptr, token)) {
3153  if (!Q_streq(token, "cvarlist"))
3154  Sys_Error("Com_ParseGameTypes: gametype \"%s\" without cvarlist", name);
3155 
3156  token = Com_EParse(text, errhead, name);
3157  if (!*text)
3158  break;
3159  if (*token != '{')
3160  Sys_Error("Com_ParseGameTypes: gametype \"%s\" without cvarlist", name);
3161 
3162  do {
3163  token = Com_EParse(text, errhead, name);
3164  if (!*text || *token == '}') {
3165  if (!gt->num_cvars)
3166  Sys_Error("Com_ParseGameTypes: gametype \"%s\" with empty cvarlist", name);
3167  else
3168  break;
3169  }
3170  /* initial pointer */
3171  cvarlist_t* cvarlist = &gt->cvars[gt->num_cvars++];
3172  if (gt->num_cvars >= MAX_CVARLISTINGAMETYPE)
3173  Sys_Error("Com_ParseGameTypes: gametype \"%s\" max cvarlist hit", name);
3174  Q_strncpyz(cvarlist->name, token, sizeof(cvarlist->name));
3175  token = Com_EParse(text, errhead, name);
3176  if (!*text || *token == '}')
3177  Sys_Error("Com_ParseGameTypes: gametype \"%s\" cvar \"%s\" with no value", name, cvarlist->name);
3178  Q_strncpyz(cvarlist->value, token, sizeof(cvarlist->value));
3179  } while (*text && *token != '}');
3180  }
3181  } while (*text);
3182  } else {
3183  Com_Printf("Com_ParseGameTypes: gametype \"%s\" with same already exists - ignore the second one\n", name);
3184  Com_SkipBlock(text);
3185  }
3186 }
3187 
3188 /*
3189 ==============================================================================
3190 DAMAGE TYPES INTERPRETER
3191 ==============================================================================
3192 */
3193 
3194 static void Com_ParseDamageTypes (const char* name, const char** text)
3195 {
3196  const char* errhead = "Com_ParseDamageTypes: unexpected end of file (damagetype ";
3197  int i;
3198 
3199  /* get it's body */
3200  const char* token = Com_Parse(text);
3201 
3202  if (!*text || *token != '{') {
3203  Com_Printf("Com_ParseDamageTypes: damage type list \"%s\" without body ignored\n", name);
3204  return;
3205  }
3206 
3207  do {
3208  token = Com_EParse(text, errhead, name);
3209  if (!*text)
3210  break;
3211  if (*token == '}')
3212  break;
3213 
3214  /* Gettext marker (also indicates that it is a dmgtype value - additional to being a dmgweight value) */
3215  if (*token == '_') {
3216  token++;
3217  csi.dts[csi.numDTs].showInMenu = true;
3218  }
3219 
3220  /* search for damage types with same name */
3221  for (i = 0; i < csi.numDTs; i++)
3222  if (Q_streq(token, csi.dts[i].id))
3223  break;
3224 
3225  /* Not found in the for loop. */
3226  if (i == csi.numDTs) {
3227  Q_strncpyz(csi.dts[csi.numDTs].id, token, sizeof(csi.dts[csi.numDTs].id));
3228 
3229  /* Special IDs */
3230  if (Q_streq(token, "normal"))
3231  csi.damNormal = csi.numDTs;
3232  else if (Q_streq(token, "blast"))
3233  csi.damBlast = csi.numDTs;
3234  else if (Q_streq(token, "fire"))
3235  csi.damFire = csi.numDTs;
3236  else if (Q_streq(token, "shock"))
3237  csi.damShock = csi.numDTs;
3238  else if (Q_streq(token, "laser"))
3239  csi.damLaser = csi.numDTs;
3240  else if (Q_streq(token, "plasma"))
3241  csi.damPlasma = csi.numDTs;
3242  else if (Q_streq(token, "particlebeam"))
3244  else if (Q_streq(token, "stun_electro"))
3246  else if (Q_streq(token, "stun_gas"))
3248  else if (Q_streq(token, "smoke"))
3249  csi.damSmoke = csi.numDTs;
3250  else if (Q_streq(token, "incendiary"))
3252 
3253  csi.numDTs++;
3254  if (csi.numDTs >= MAX_DAMAGETYPES)
3255  Sys_Error("Com_ParseDamageTypes: Too many damage types.");
3256  } else {
3257  Com_Printf("Com_ParseDamageTypes: damage type \"%s\" in list \"%s\" with same already exists - ignore the second one (#%i)\n", token, name, csi.numDTs);
3258  }
3259  } while (*text);
3260 }
3261 
3262 
3263 /*
3264 ==============================================================================
3265 MAIN SCRIPT PARSING FUNCTION
3266 ==============================================================================
3267 */
3268 
3277 const char* Com_GetRandomMapAssemblyNameForCraft (const char* craftID)
3278 {
3279  return va("+%s", craftID);
3280 }
3281 
3285 const char* Com_GetRandomMapAssemblyNameForCrashedCraft (const char* craftID)
3286 {
3287  if (Q_streq(craftID, "craft_drop_firebird"))
3288  return "+craft_crash_drop_firebird";
3289  else if (Q_streq(craftID, "craft_drop_raptor"))
3290  return "+craft_crash_drop_raptor";
3291  else if (Q_streq(craftID, "craft_inter_dragon"))
3292  return "+craft_crash_inter_dragon";
3293  else if (Q_streq(craftID, "craft_inter_saracen"))
3294  return "+craft_crash_inter_saracen";
3295  else if (Q_streq(craftID, "craft_inter_starchaser"))
3296  return "+craft_crash_inter_starchaser";
3297 
3298  return "";
3299 }
3300 
3308 {
3309  humanAircraftType_t aircraftType;
3310  size_t dummy;
3311  Com_ParseValue(&aircraftType, token, V_AIRCRAFTTYPE, 0, sizeof(aircraftType), &dummy);
3312  return aircraftType;
3313 }
3314 
3320 {
3321  return Com_ValueToStr(&type, V_AIRCRAFTTYPE, 0);
3322 }
3323 
3329 ufoType_t Com_UFOShortNameToID (const char* token)
3330 {
3331  ufoType_t ufoType;
3332  size_t dummy;
3333  Com_ParseValue(&ufoType, token, V_UFO, 0, sizeof(ufoType), &dummy);
3334  return ufoType;
3335 }
3336 
3343 {
3344  return Com_ValueToStr(&type, V_UFO, 0);
3345 }
3346 
3352 {
3353  return Com_ValueToStr(&type, V_UFOCRASHED, 0);
3354 }
3355 
3362 const ugv_t* Com_GetUGVByIDSilent (const char* ugvID)
3363 {
3364  if (!ugvID)
3365  return nullptr;
3366 
3367  for (int i = 0; i < csi.numUGV; i++) {
3368  const ugv_t* ugv = &csi.ugvs[i];
3369  if (Q_streq(ugv->id, ugvID)) {
3370  return ugv;
3371  }
3372  }
3373  return nullptr;
3374 }
3375 
3381 const ugv_t* Com_GetUGVByID (const char* ugvID)
3382 {
3383  const ugv_t* ugv = Com_GetUGVByIDSilent(ugvID);
3384 
3385  if (!ugvID)
3386  Com_Printf("Com_GetUGVByID Called with nullptr ugvID!\n");
3387  else if (!ugv)
3388  Com_Printf("Com_GetUGVByID: No ugv_t entry found for id '%s' in %i entries.\n", ugvID, csi.numUGV);
3389  return ugv;
3390 }
3391 
3395 static void Com_AddObjectLinks (void)
3396 {
3397  /* Add links to weapons. */
3399  const int weaponsIdx = parse->numWeapons;
3400  const char* id = parse->token;
3401 
3402  /* Link the weapon pointers for this item. */
3403  parse->od->weapons[weaponsIdx] = INVSH_GetItemByID(id);
3404  if (!parse->od->weapons[weaponsIdx]) {
3405  Sys_Error("Com_AddObjectLinks: Could not get item '%s' for linking into item '%s'\n",
3406  id , parse->od->id);
3407  }
3408 
3409  /* Back-link the obj-idx inside the fds */
3410  for (int k = 0; k < parse->od->numFiredefs[weaponsIdx]; k++) {
3411  parse->od->fd[weaponsIdx][k].obj = parse->od;
3412  }
3413 
3414  Mem_Free(parse->token);
3415  }
3416 
3417  /* Clear the temporary list. */
3419 
3420  /* Add links to ammos */
3421  objDef_t* od;
3422  int i;
3423  for (i = 0, od = csi.ods; i < csi.numODs; i++, od++) {
3424  od->numAmmos = 0; /* Default value */
3425  if (od->weapon || od->craftitem.type <= AC_ITEM_WEAPON) {
3426  /* this is a weapon, an aircraft weapon, or a base defence system */
3427  for (int n = 0; n < csi.numODs; n++) {
3428  const objDef_t* weapon = INVSH_GetItemByIDX(n);
3429  for (int m = 0; m < weapon->numWeapons; m++) {
3430  if (weapon->weapons[m] == od) {
3431  assert(od->numAmmos < MAX_AMMOS_PER_OBJDEF);
3432  od->ammos[od->numAmmos++] = weapon;
3433  Com_DPrintf(DEBUG_SHARED, "link ammo %s to weapon: %s\n", weapon->id, od->id);
3434  }
3435  }
3436  }
3437  }
3438  }
3439 }
3440 
3442 static const value_t mapdef_vals[] = {
3443  {"description", V_TRANSLATION_STRING, offsetof(mapDef_t, description), 0},
3444  {"victorycondition", V_TRANSLATION_STRING, offsetof(mapDef_t, victoryCondition), 0},
3445  {"victorybonusperalien", V_FLOAT, offsetof(mapDef_t, victoryBonusPerAlien), MEMBER_SIZEOF(mapDef_t, victoryBonusPerAlien)},
3446  {"missionbriefing", V_TRANSLATION_STRING, offsetof(mapDef_t, missionBriefing), 0},
3447  {"map", V_HUNK_STRING, offsetof(mapDef_t, mapTheme), 0},
3448  {"size", V_HUNK_STRING, offsetof(mapDef_t, size), 0},
3449  {"civilianteam", V_HUNK_STRING, offsetof(mapDef_t, civTeam), 0},
3450 
3451  {"maxaliens", V_INT, offsetof(mapDef_t, maxAliens), MEMBER_SIZEOF(mapDef_t, maxAliens)},
3452  {"hwclass", V_INT, offsetof(mapDef_t, hwclass), MEMBER_SIZEOF(mapDef_t, hwclass)},
3453  {"storyrelated", V_BOOL, offsetof(mapDef_t, storyRelated), MEMBER_SIZEOF(mapDef_t, storyRelated)},
3454 
3455  {"teams", V_INT, offsetof(mapDef_t, teams), MEMBER_SIZEOF(mapDef_t, teams)},
3456  {"multiplayer", V_BOOL, offsetof(mapDef_t, multiplayer), MEMBER_SIZEOF(mapDef_t, multiplayer)},
3457  {"singleplayer", V_BOOL, offsetof(mapDef_t, singleplayer), MEMBER_SIZEOF(mapDef_t, singleplayer)},
3458  {"campaign", V_BOOL, offsetof(mapDef_t, campaign), MEMBER_SIZEOF(mapDef_t, campaign)},
3459 
3460  {"onwin", V_HUNK_STRING, offsetof(mapDef_t, onwin), 0},
3461  {"onlose", V_HUNK_STRING, offsetof(mapDef_t, onlose), 0},
3462 
3463  {"ufos", V_LIST, offsetof(mapDef_t, ufos), 0},
3464  {"aircraft", V_LIST, offsetof(mapDef_t, aircraft), 0},
3465  {"terrains", V_LIST, offsetof(mapDef_t, terrains), 0},
3466  {"populations", V_LIST, offsetof(mapDef_t, populations), 0},
3467  {"cultures", V_LIST, offsetof(mapDef_t, cultures), 0},
3468  {"gametypes", V_LIST, offsetof(mapDef_t, gameTypes), 0},
3469 
3470  {nullptr, V_NULL, 0, 0}
3471 };
3472 
3473 static void Com_ParseMapDefinition (const char* name, const char** text)
3474 {
3475  const char* errhead = "Com_ParseMapDefinition: unexpected end of file (mapdef ";
3476 
3477  /* get it's body */
3478  const char* token = Com_Parse(text);
3479 
3480  if (!*text || *token != '{') {
3481  Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" without body ignored\n", name);
3482  return;
3483  }
3484 
3486  csi.numMDs++;
3487  if (csi.numMDs >= lengthof(csi.mds))
3488  Sys_Error("Com_ParseMapDefinition: Max mapdef hit");
3489 
3490  OBJZERO(*md);
3492  md->singleplayer = true;
3493  md->campaign = true;
3494  md->multiplayer = false;
3495 
3496  do {
3497  token = Com_EParse(text, errhead, name);
3498  if (!*text)
3499  break;
3500  if (*token == '}')
3501  break;
3502 
3503  if (!Com_ParseBlockToken(name, text, md, mapdef_vals, com_genericPool, token)) {
3504  if (Q_streq(token, "params")) {
3505  Com_ParseList(text, &md->params);
3506  } else {
3507  Com_Printf("Com_ParseMapDefinition: unknown token \"%s\" ignored (mapdef %s)\n", token, name);
3508  continue;
3509  }
3510  }
3511  } while (*text);
3512 
3513  if (!md->mapTheme) {
3514  Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" with no map\n", name);
3515  csi.numMDs--;
3516  }
3517 
3518  if (!md->description) {
3519  Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" with no description\n", name);
3520  csi.numMDs--;
3521  }
3522 
3523  if (md->maxAliens <= 0) {
3524  Com_Printf("Com_ParseMapDefinition: mapdef \"%s\" with invalid maxAlien value\n", name);
3525  csi.numMDs--;
3526  }
3527 
3528  /* Skip if the hardware can't handle this map. */
3529  if (hwclass->integer < md->hwclass) {
3530  Com_DPrintf(DEBUG_SHARED, "Com_ParseMapDefinition: mapdef \"%s\" is skipped because hwclass doesn't match\n", name);
3531  csi.numMDs--;
3532  }
3533 }
3534 
3535 static void Com_ParseTerrainDefinition (const char* name, const char** text)
3536 {
3537  const char* errhead = "Com_ParseTerrainDefinition: unexpected end of file (terraindef ";
3538  TerrainDef* tDef = new TerrainDef;
3539 
3540  Q_strncpyz(tDef->terrainName, name, sizeof(tDef->terrainName));
3541 
3542  /* get it's body */
3543  const char* token = Com_Parse(text);
3544 
3545  if (!*text || *token != '{') {
3546  Com_Printf("Com_ParseTerrainDefinition: mapdef \"%s\" without body ignored\n", name);
3547  delete tDef;
3548  return;
3549  }
3550 
3551  do {
3552  token = Com_EParse(text, errhead, name);
3553  if (!*text)
3554  break;
3555  if (*token == '}')
3556  break;
3557 
3558  char key[MAX_VAR];
3559  Q_strncpyz(key, token, sizeof(key));
3560  token = Com_EParse(text, errhead, name);
3561  KeyValuePair kvp(key, token);
3562 
3563  if (kvp.isKey("rgbred"))
3564  tDef->rgbRed = kvp.asInt();
3565  else if (kvp.isKey("rgbgreen"))
3566  tDef->rgbGreen = kvp.asInt();
3567  else if (kvp.isKey("rgbblue"))
3568  tDef->rgbBlue = kvp.asInt();
3569  else if (kvp.isKey("survivalchance"))
3570  tDef->survivalChance = kvp.asFloat();
3571  else if (kvp.isKey("rainchance"))
3572  tDef->rainChance = kvp.asFloat();
3573  else if (kvp.isKey("snowchance"))
3574  tDef->snowChance = kvp.asFloat();
3575  else {
3576  Com_Printf("Com_ParseTerrainDefinition: unknown token \"%s\" ignored (terraindef %s)\n", token, name);
3577  }
3578  } while (*text);
3579 
3580  if (!tDef->terrainName[0]) {
3581  Com_Printf("Com_ParseTerrainDefinition: terraindef \"%s\" with no name\n", name);
3582  }
3583  /* Now add the stuff we just parsed to the table. */
3584  if (!csi.terrainDefs.add(tDef))
3585  Com_Printf("Com_ParseTerrainDefinition: could not add terraindef \"%s\". Duplicate colors or name.\n", name);
3586 }
3587 
3589 {
3590  return csi.numMDs;
3591 }
3592 
3594 {
3595  return &csi.mds[index];
3596 }
3597 
3598 mapDef_t* Com_GetMapDefinitionByID (const char* mapDefID)
3599 {
3600  mapDef_t* md;
3601 
3602  assert(mapDefID);
3603 
3604  MapDef_Foreach(md) {
3605  if (Q_streq(md->id, mapDefID))
3606  return md;
3607  }
3608 
3609  Com_DPrintf(DEBUG_SHARED, "Com_GetMapDefinition: Could not find mapdef with id: '%s'\n", mapDefID);
3610  return nullptr;
3611 }
3612 
3619 void Com_ParseScripts (bool onlyServer)
3620 {
3621  const char* type, *name;
3622 
3623  Com_Printf("\n----------- parse scripts ----------\n");
3624 
3625  /* reset csi basic info */
3626  INVSH_InitCSI(&csi);
3629 
3630  /* Reset aircraft ids */
3635 
3636  /* pre-stage parsing */
3637  Com_Printf("%i script files\n", FS_BuildFileList("ufos/*.ufo"));
3638  const char* text = nullptr;
3639 
3640  FS_NextScriptHeader(nullptr, nullptr, nullptr);
3641 
3642  while ((type = FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr)
3643  if (Q_streq(type, "damagetypes"))
3644  Com_ParseDamageTypes(name, &text);
3645  else if (Q_streq(type, "gametype"))
3646  Com_ParseGameTypes(name, &text);
3647  else if (Q_streq(type, "version"))
3649 
3650  /* stage one parsing */
3651  FS_NextScriptHeader(nullptr, nullptr, nullptr);
3652  text = nullptr;
3653 
3654  while ((type = FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr) {
3655  /* server/client scripts */
3656  if (Q_streq(type, "item") || Q_streq(type, "craftitem"))
3657  Com_ParseItem(name, &text);
3658  else if (Q_streq(type, "inventory"))
3659  Com_ParseInventory(name, &text);
3660  else if (Q_streq(type, "terrain"))
3661  Com_ParseTerrain(name, &text);
3662  else if (Q_streq(type, "ugv"))
3663  Com_ParseUGVs(name, &text);
3664  else if (Q_streq(type, "chrtemplate"))
3666  else if (Q_streq(type, "terraindef"))
3668  else if (Q_streq(type, "mapdef"))
3669  Com_ParseMapDefinition(name, &text);
3670  else if (Q_streq(type, "bodydef"))
3671  Com_ParseBodyTemplate(name, &text);
3672  else if (Q_streq(type, "names"))
3673  Com_ParseActorNames(name, &text);
3674  else if (Q_streq(type, "aircraftnames"))
3675  Com_ParseAircraftNames(name, &text);
3676  else if (!onlyServer)
3677  CL_ParseClientData(type, name, &text);
3678  }
3679 
3680  if (!versionParsed)
3681  Sys_Error("Could not find version string for script files");
3682 
3683  /* Stage two parsing (weapon/inventory dependant stuff). */
3684  FS_NextScriptHeader(nullptr, nullptr, nullptr);
3685  text = nullptr;
3686 
3687  while ((type = FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr) {
3688  /* server/client scripts */
3689  if (Q_streq(type, "equipment"))
3690  Com_ParseEquipment(name, &text);
3691  else if (Q_streq(type, "team"))
3692  Com_ParseTeam(name, &text);
3693  else if (Q_streq(type, "implant"))
3694  Com_ParseImplant(name, &text);
3695  }
3696 
3697  Com_AddObjectLinks(); /* Add ammo<->weapon links to items.*/
3698 
3699  /* parse ui node script */
3700  if (!onlyServer) {
3701  Com_Printf("%i ui script files\n", FS_BuildFileList("ufos/ui/*.ufo"));
3702  FS_NextScriptHeader(nullptr, nullptr, nullptr);
3703  text = nullptr;
3704  while ((type = FS_NextScriptHeader("ufos/ui/*.ufo", &name, &text)) != nullptr) {
3705  CL_ParseClientData(type, name, &text);
3706  }
3707  }
3708 
3709  Com_Printf("Shared Client/Server Info loaded\n");
3710  Com_Printf("...%3i items parsed\n", csi.numODs);
3711  Com_Printf("...%3i damage types parsed\n", csi.numDTs);
3712  Com_Printf("...%3i equipment definitions parsed\n", csi.numEDs);
3713  Com_Printf("...%3i inventory definitions parsed\n", csi.numIDs);
3714  Com_Printf("...%3i team definitions parsed\n", csi.numTeamDefs);
3715 }
3716 
3718 {
3719  static int checksum = 0;
3720  const char* buf;
3721 
3722  if (checksum != 0)
3723  return checksum;
3724 
3725  while ((buf = FS_GetFileData("ufos/*.ufo")) != nullptr)
3726  checksum += LittleLong(Com_BlockChecksum(buf, strlen(buf)));
3727  FS_GetFileData(nullptr);
3728 
3729  return checksum;
3730 }
3731 
3732 void Com_Shutdown (void)
3733 {
3736  com_constNameInt = nullptr;
3737  versionParsed = false;
3738 }
char deathTextureName[MAX_VAR]
Definition: chr_shared.h:343
static void Com_ParseActorModels(const char *name, const char **text, teamDef_t *td)
Parses "actors" definition from team_* ufo script files.
Definition: scripts.cpp:2528
bool Q_strnull(const char *string)
Definition: shared.h:138
char id[MAX_VAR]
Definition: chr_shared.h:57
bool CL_ParseClientData(const char *type, const char *name, const char **text)
Called at client startup.
Definition: cl_main.cpp:770
bool Com_ParseBlock(const char *name, const char **text, void *base, const value_t *values, memPool_t *mempool)
Definition: scripts.cpp:1393
int damPlasma
Definition: q_shared.h:532
#define SHAPE_BIG_MAX_HEIGHT
defines the max height of an inventory container
Definition: inv_shared.h:188
static void Com_ParseObjDefEffect(objDef_t *od, const char *name, const char **text)
Definition: scripts.cpp:1867
int numModels[NAME_LAST]
Definition: chr_shared.h:326
const objDef_t * INVSH_GetItemByIDSilent(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn&#39;t found.
Definition: inv_shared.cpp:249
int damLaser
Definition: q_shared.h:532
const char * Com_GetLastParseError(void)
Definition: scripts.cpp:428
static void Com_GetCrashedUfoIdStr(ufoType_t idNum, char *outStr, const size_t size)
Definition: scripts.cpp:544
bool armour
Definition: chr_shared.h:334
#define BODYPART_MAXTYPE
Definition: chr_shared.h:266
int numTeamDefs
Definition: q_shared.h:549
int hwclass
Definition: q_shared.h:482
linkedList_t *const * names
Definition: chr_shared.h:314
#define MAX_CHARACTER_TEMPLATES
Definition: chr_shared.h:229
Definition: scripts.h:51
bool Com_UnregisterConstVariable(const char *name)
Removes a registered constant from the script mapping hash table.
Definition: scripts.cpp:147
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
const int * numNames
Definition: chr_shared.h:315
#define CID_EQUIP
Definition: inv_shared.h:56
static const char * dropIdsTable[DROPSHIP_MAX]
Definition: scripts.cpp:470
Valid ufo types.
Definition: scripts.h:71
actorSizeEnum_t size
Definition: chr_shared.h:341
static const char * interIdsTable[INTERCEPTOR_MAX]
Definition: scripts.cpp:471
int penalties[MODIFIER_MAX]
Definition: chr_shared.h:272
#define MAX_CVARLISTINGAMETYPE
Definition: q_shared.h:335
short Com_GetDropShipIdsNum(void)
Definition: scripts.cpp:576
static void Com_ParseBodyPart(const char *name, const char **text, BodyData *bd)
Definition: scripts.cpp:2933
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
int numDTs
Definition: q_shared.h:545
A pair of strings representing a key and a value The value string can be trimmed and rendered in the ...
Definition: keyvaluepair.h:38
#define MAX_UGV
Definition: chr_shared.h:227
Definition: scripts.h:70
const char * Com_GetRandomMapAssemblyNameForCrashedCraft(const char *craftID)
Definition: scripts.cpp:3285
const objDef_t * INVSH_GetItemByID(const char *id)
Returns the item that belongs to the given id or nullptr if it wasn&#39;t found.
Definition: inv_shared.cpp:282
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
objDef_t * od
Definition: scripts.cpp:1801
static void Com_ParseArmourOrResistance(const char *name, const char **text, short *ad, bool rating)
Parses the armour definitions or the team resistance values from script files. The protection and rat...
Definition: scripts.cpp:1747
#define TEAM_PHALANX
Definition: q_shared.h:62
linkedList_t * bodyTemplates
Definition: q_shared.h:560
valueTypes_t
possible values for parsing functions
Definition: scripts.h:48
char * mapTheme
Definition: q_shared.h:464
const chrTemplate_t * Com_GetCharacterTemplateByID(const char *chrTemplate)
Returns the chrTemplate pointer for the given id - or nullptr if not found in the chrTemplates array...
Definition: scripts.cpp:2809
void * data
Definition: list.h:31
float rainChance
Definition: q_shared.h:363
#define TEAM_ALIEN
Definition: q_shared.h:63
static void Com_ParseItem(const char *name, const char **text)
Parses weapon, equipment, craft items and armour.
Definition: scripts.cpp:1888
static short aircraftIdsNum[CRAFT_MAX]
Definition: scripts.cpp:477
static const char *const craftTypeIds[CRAFT_MAX *2]
Definition: scripts.cpp:455
const char * va(const char *format,...)
does a varargs printf into a temp buffer, so I don&#39;t need to have varargs versions of all text functi...
Definition: shared.cpp:410
int damStunGas
Definition: q_shared.h:533
int Com_GetMapDefNumber(void)
Definition: scripts.cpp:3588
Definition: parse.h:34
struct com_constNameInt_s * next
Definition: scripts.cpp:44
short humanAircraftType_t
Definition: inv_shared.h:28
const terrainType_t * Com_GetTerrainType(const char *textureName)
Searches the terrain definition if given.
Definition: scripts.cpp:3067
static const vec3_t scale
char value[MAX_VAR]
Definition: q_shared.h:338
const char * id(void) const
Definition: chr_shared.cpp:359
#define MAX_CONSTNAMEINT_NAME
Definition: scripts.cpp:34
void addBodyPart(const BodyPartData &bodyPart)
Definition: chr_shared.cpp:399
byte rgbGreen
Definition: q_shared.h:359
bool showInMenu
Definition: inv_shared.h:622
bool isKey(const char *name) const
Definition: keyvaluepair.h:51
float survivalChance
Definition: q_shared.h:362
linkedList_t * actorNames
Definition: q_shared.h:562
char body[MAX_VAR]
Definition: chr_shared.h:392
int numTemplates
Definition: chr_shared.h:348
static const value_t ugvValues[]
Definition: scripts.cpp:2823
int numAircraft[AIRCRAFTTYPE_MAX]
Definition: inv_shared.h:610
#define CONSTNAMEINT_HASH_SIZE
Definition: scripts.cpp:32
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
static linkedList_t * parseItemWeapons
Temporary list of weapon ids as parsed from the ufo file "weapon_mod <id>" in Com_ParseItem and use...
Definition: scripts.cpp:1798
static int Com_GetGender(const teamDef_t *teamDef)
Return a random (weighted by number of models) gender for this teamDef.
Definition: scripts.cpp:2382
static void Com_ParseTerrainDefinition(const char *name, const char **text)
Definition: scripts.cpp:3535
effectStages_t
Definition: inv_shared.h:80
memPool_t * com_aliasSysPool
Definition: common.cpp:68
#define SOUND_ATTN_NONE
Definition: common.h:185
#define MAX_TEAMDEFS
Definition: chr_shared.h:228
static void Com_ParseEquipment(const char *name, const char **text)
Definition: scripts.cpp:2180
const char * armourPath
Definition: inv_shared.h:272
static int Com_FindNameType(const char *nameType)
Definition: scripts.cpp:263
float range
Definition: inv_shared.h:152
float vec_t
Definition: ufotypes.h:37
short ufoType_t
Definition: scripts.h:145
const char *const fade_names[]
Definition: scripts.cpp:355
Com_TokenType_t
Definition: parse.h:33
static const char * ufoIdsTable[UFO_MAX]
Ufoai uses two types of ids for aircraft: the string is used for references in the scripts...
Definition: scripts.cpp:469
Describes a character with all its attributes.
Definition: chr_shared.h:388
static short Com_GetCrashedAircraftIdNum(aircraftType_t type, const char *idString)
Definition: scripts.cpp:508
int numChrTemplates
Definition: q_shared.h:559
int damIncendiary
Definition: q_shared.h:537
char id[MAX_VAR]
Definition: inv_shared.h:621
int integer
Definition: cvar.h:81
voidpf void uLong size
Definition: ioapi.h:42
#define MAX_GAMETYPES
Definition: q_shared.h:333
uint32_t shape
Definition: inv_shared.h:273
CASSERT(lengthof(vt_names)==V_NUM_TYPES)
#define CID_ARMOUR
Definition: inv_shared.h:54
void Com_ParseScripts(bool onlyServer)
Definition: scripts.cpp:3619
unsigned Com_BlockChecksum(const void *buffer, int length)
Definition: md4.cpp:202
const BodyData * bodyTemplate
Definition: chr_shared.h:350
bool Com_GetCharacterModel(character_t *chr)
Definition: scripts.cpp:2358
static void Com_GetHumanCraftIdStr(short idNum, char *outStr, const size_t size)
Definition: scripts.cpp:562
char id[MAX_TEXPATH]
Definition: chr_shared.h:270
#define TEAM_CIVILIAN
Definition: q_shared.h:61
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
static const char **const aircraftIdsTable[CRAFT_MAX]
Definition: scripts.cpp:472
#define ABILITY_NUM_TYPES
Definition: chr_shared.h:54
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
char terrainName[20]
Definition: q_shared.h:361
const char * image
Definition: inv_shared.h:270
short ratings[MAX_DAMAGETYPES]
Definition: inv_shared.h:323
const teamDef_t * teamDef
Definition: chr_shared.h:413
const char * Com_DropShipTypeToShortName(humanAircraftType_t type)
Translate DropShip type to short name.
Definition: scripts.cpp:3319
char id[MAX_VAR]
Definition: chr_shared.h:302
QGL_EXTERN GLuint * id
Definition: r_gl.h:86
static const char *const penaltyNames[MODIFIER_MAX]
Definition: scripts.cpp:2929
itemEffect_t * strengthenEffect
Definition: inv_shared.h:283
#define Mem_StrDup(in)
Definition: mem.h:48
static void Com_ParseMapDefinition(const char *name, const char **text)
Definition: scripts.cpp:3473
int FS_BuildFileList(const char *fileList)
Build a filelist.
Definition: files.cpp:962
inventory definition for our menus
Definition: inv_shared.h:371
int numMDs
Definition: q_shared.h:572
byte sy
Definition: inv_shared.h:326
static short Com_GetHumanCraftIdNum(const char *idString)
Definition: scripts.cpp:549
int num_cvars
Definition: q_shared.h:345
void LIST_Delete(linkedList_t **list)
Definition: list.cpp:195
static const BodyData * Com_GetBodyTemplateByID(const char *id)
Definition: scripts.cpp:2653
void Com_RegisterConstList(const constListEntry_t constList[])
Registers a list of string aliases.
Definition: scripts.cpp:253
static void Com_ParseBodyTemplate(const char *name, const char **text)
Definition: scripts.cpp:2999
gametype_t gts[MAX_GAMETYPES]
Definition: q_shared.h:567
const char * Com_GetRandomMapAssemblyNameForCraft(const char *craftID)
Returns the name of an aircraft or an ufo that is used in the ump files for the random map assembly...
Definition: scripts.cpp:3277
static const value_t fdps[]
Definition: scripts.cpp:1521
#define MAX_IMPLANTS
Definition: inv_shared.h:38
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:2345
#define CID_BELT
Definition: inv_shared.h:52
short resistance[MAX_DAMAGETYPES]
Definition: chr_shared.h:345
int productionCost
Definition: inv_shared.h:333
const ugv_t * Com_GetUGVByIDSilent(const char *ugvID)
Searches an UGV definition by a given script id and returns the pointer to the global data...
Definition: scripts.cpp:3362
int LIST_Count(const linkedList_t *list)
Definition: list.cpp:344
#define Mem_PoolStrDupTo(in, out, pool, tagNum)
Definition: mem.h:49
const char *const align_names[]
Definition: scripts.cpp:340
#define INTERCEPTOR_MAX
Definition: inv_shared.h:33
#define AIRCRAFT_NONE
Definition: scripts.h:147
static const char * Com_ConstIntGetVariable(const char *name)
Will extract the variable from a string<=>int mapping string which contain a namespace.
Definition: scripts.cpp:57
void Sys_Backtrace(void)
On platforms supporting it, print a backtrace.
#define SOUND_ATTN_IDLE
Definition: common.h:187
int numImplants
Definition: q_shared.h:521
void Com_GetCharacterValues(const char *teamDefition, character_t *chr)
Assign character values, 3D models and names to a character.
Definition: scripts.cpp:2408
int Com_GetScriptChecksum(void)
Definition: scripts.cpp:3717
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
char head[MAX_VAR]
Definition: chr_shared.h:393
itemEffect_t * activeEffect
Definition: inv_shared.h:131
int numWeapons
Definition: inv_shared.h:317
mapDef_t mds[MAX_MAPDEFS]
Definition: q_shared.h:571
#define CID_LEFT
Definition: inv_shared.h:48
int damStunElectro
Definition: q_shared.h:535
bool weapons
Definition: chr_shared.h:335
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
Header for script parsing functions.
static const value_t equipment_definition_vals[]
Valid equipment definition values from script files.
Definition: scripts.cpp:2172
float impactAttenuation
Definition: inv_shared.h:121
float snowChance
Definition: q_shared.h:364
char id[MAX_VAR]
Definition: q_shared.h:342
unsigned int key
Definition: cl_input.cpp:68
const objDef_t * onlyWeapon
Definition: chr_shared.h:336
int numUGV
Definition: q_shared.h:565
#define CID_HEADGEAR
Definition: inv_shared.h:50
align_t
We need this here for checking the boundaries from script values.
Definition: scripts.h:89
#define UFO_VERSION
Definition: common.h:36
static com_constNameInt_t * com_constNameInt_hash[CONSTNAMEINT_HASH_SIZE]
Hash of all the registeres mappings.
Definition: scripts.cpp:50
const struct objDef_s * ammos[MAX_AMMOS_PER_OBJDEF]
Definition: inv_shared.h:307
#define ERR_DROP
Definition: common.h:211
static const value_t terrainTypeValues[]
Definition: scripts.cpp:3053
itemEffect_t * overdoseEffect
Definition: inv_shared.h:133
#define MAX_DAMAGETYPES
Definition: inv_shared.h:258
#define DEBUG_CLIENT
Definition: defines.h:59
#define SND_VOLUME_FOOTSTEPS
Definition: scripts.h:212
#define DEBUG_ENGINE
Definition: defines.h:56
int numNames[NAME_NUM_TYPES]
Definition: chr_shared.h:304
#define CID_RIGHT
Definition: inv_shared.h:47
float fireAttenuation
Definition: inv_shared.h:120
const char * name
Definition: scripts.h:232
int skills[SKILL_NUM_TYPES+1][2]
Definition: chr_shared.h:59
void LIST_AddString(linkedList_t **listDest, const char *data)
Adds an string to a new or to an already existing linked list. The string is copied here...
Definition: list.cpp:139
char id[MAX_VAR]
Definition: chr_shared.h:309
#define SHAPE_BIG_MAX_WIDTH
32 bit mask
Definition: inv_shared.h:190
#define OBJZERO(obj)
Definition: shared.h:178
struct terrainType_s * hash_next
Definition: scripts.h:221
Definition: scripts.h:78
cvar_t * hwclass
Definition: common.cpp:62
#define SOUND_ATTN_MAX
Definition: common.h:189
#define MAX_VAR
Definition: shared.h:36
static terrainType_t * terrainTypesHash[TERRAIN_HASH_SIZE]
Definition: scripts.cpp:3051
#define MAX_OBJDEFS
Definition: inv_shared.h:37
equipDef_t eds[MAX_EQUIPDEFS]
Definition: q_shared.h:540
const char *const vt_names[]
possible values for parsing functions
Definition: scripts.cpp:310
bool multiplayer
Definition: q_shared.h:474
static const value_t od_vals[]
Definition: scripts.cpp:1448
char name[MAX_CONSTNAMEINT_NAME]
Definition: scripts.cpp:40
static wrapCache_t * hash[MAX_WRAP_HASH]
Definition: r_font.cpp:86
short Com_GetUfoIdsNum(void)
Definition: scripts.cpp:571
int Com_SetValue(void *base, const void *set, valueTypes_t type, int ofs, size_t size)
Definition: scripts.cpp:1009
static const char *const skillNames[SKILL_NUM_TYPES+1]
Definition: scripts.cpp:1425
#define MAX_WEAPONS_PER_OBJDEF
Definition: inv_shared.h:40
int32_t fireDefIndex_t
Definition: inv_shared.h:78
cvarlist_t cvars[MAX_CVARLISTINGAMETYPE]
Definition: q_shared.h:344
static const value_t effect_vals[]
Definition: scripts.cpp:1505
#define MAX_FIREDEFS_PER_WEAPON
Definition: inv_shared.h:42
float bounceFraction
Definition: scripts.h:219
memPool_t * com_genericPool
Definition: common.cpp:73
#define CID_IMPLANT
Definition: inv_shared.h:49
const char * Com_ValueToStr(const void *base, const valueTypes_t type, const int ofs)
Definition: scripts.cpp:1171
#define UFO_MAX
Definition: scripts.h:146
const char * Com_EParse(const char **text, const char *errhead, const char *errinfo, char *target, size_t size)
Parsing function that prints an error message when there is no text in the buffer.
Definition: scripts.cpp:277
int weaponSkill
Definition: inv_shared.h:162
char const * Q_strstart(char const *str, char const *start)
Matches the start of a string.
Definition: shared.cpp:587
#define SHAPE_SMALL_MAX_WIDTH
The max width and height of an item-shape.
Definition: inv_shared.h:176
#define CID_FLOOR
Definition: inv_shared.h:55
blend_t
Definition: scripts.h:113
linkedList_t * next
Definition: list.h:32
const char * Com_UFOTypeToShortName(ufoType_t type)
Translate UFO type to short name.
Definition: scripts.cpp:3342
static const value_t bodyPartValues[]
Definition: scripts.cpp:2920
Structure to map (script) strings and integer (enum) values.
Definition: scripts.cpp:39
int numODs
Definition: q_shared.h:518
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
Definition: scripts.h:64
int numItems[MAX_OBJDEFS]
Definition: inv_shared.h:608
implantDef_t implants[MAX_IMPLANTS]
Definition: q_shared.h:520
int price
Definition: inv_shared.h:332
ufoType_t Com_UFOShortNameToID(const char *token)
Translate short name to UFO type.
Definition: scripts.cpp:3329
static void Com_GetAircraftIdStr(aircraftType_t type, short idNum, char *outStr, const size_t size)
Definition: scripts.cpp:499
craftItem craftitem
Definition: inv_shared.h:331
#define TERRAIN_HASH_SIZE
Definition: scripts.cpp:3050
char name[MAX_VAR]
Definition: inv_shared.h:372
static bool Com_ParseFire(const char *name, const char **text, fireDef_t *fd)
Parses the firemode.
Definition: scripts.cpp:1668
int numIDs
Definition: q_shared.h:525
Different terrain definitions for footsteps and particles.
Definition: scripts.h:215
invDef_t ids[MAX_INVDEFS]
Definition: q_shared.h:524
Definition: scripts.h:53
static const size_t vt_aligns[]
natural align for each targets
Definition: scripts.cpp:392
TerrainDefs terrainDefs
Definition: q_shared.h:574
bool weapon
Definition: inv_shared.h:277
const char * id
Definition: inv_shared.h:102
char path[MAX_VAR]
Definition: chr_shared.h:391
mapDef_t * Com_GetMapDefByIDX(int index)
Definition: scripts.cpp:3593
const struct objDef_s * weapons[MAX_WEAPONS_PER_OBJDEF]
Definition: inv_shared.h:311
void Com_RegisterConstInt(const char *name, int value)
Register mappings between script strings and enum values for values of the type V_INT.
Definition: scripts.cpp:198
resultStatus_t Com_ParseValue(void *base, const char *token, valueTypes_t type, int ofs, size_t size, size_t *writtenBytes)
Parse a value from a string.
Definition: scripts.cpp:656
static const size_t vt_sizes[]
target sizes for buffer
Definition: scripts.cpp:361
static ufoType_t Com_GetCrashedUfoIdNum(const char *idString)
Definition: scripts.cpp:534
#define CID_HOLSTER
Definition: inv_shared.h:53
bool isArmour() const
Definition: inv_shared.h:346
bool add(const TerrainDef *tdef)
Translate color value to terrain type to random weather code.
Definition: q_shared.cpp:53
#define CID_BACKPACK
Definition: inv_shared.h:51
const char * texture
Definition: scripts.h:216
QGL_EXTERN GLuint index
Definition: r_gl.h:110
bool deplete
Definition: inv_shared.h:301
const char * name
Definition: inv_shared.h:111
objDef_t ods[MAX_OBJDEFS]
Definition: q_shared.h:517
int damShock
Definition: q_shared.h:529
#define UNIT_SIZE
Definition: defines.h:121
int32_t weaponFireDefIndex_t
Definition: inv_shared.h:77
float footstepVolume
Definition: scripts.h:220
static const teamNames_t * Com_GetNameListByID(const char *id)
Definition: scripts.cpp:2662
actorSizeEnum_t fieldSize
Definition: chr_shared.h:409
static bool versionParsed
Definition: scripts.cpp:292
static void Com_ParseUGVs(const char *name, const char **text)
Parse 2x2 units (e.g. UGVs)
Definition: scripts.cpp:2837
const struct objDef_s * item
Definition: inv_shared.h:104
CGAME_HARD_LINKED_FUNCTIONS linkedList_t * LIST_Add(linkedList_t **listDest, void const *data, size_t length)
int ammo
Definition: inv_shared.h:293
QGL_EXTERN GLfloat f
Definition: r_gl.h:114
resultStatus_t
Definition: scripts.h:184
int32_t containerIndex_t
Definition: inv_shared.h:46
fireDefIndex_t fdIdx
Definition: inv_shared.h:130
const char *const air_slot_type_strings[]
List of valid strings for slot types.
Definition: scripts.cpp:1791
const chrTemplate_t * characterTemplates[MAX_TEMPLATES_PER_TEAM]
Definition: chr_shared.h:347
linkedList_t * names[NAME_NUM_TYPES]
Definition: chr_shared.h:303
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
int asInt() const
Definition: keyvaluepair.h:57
int Com_EParseValue(void *base, const char *token, valueTypes_t type, int ofs, size_t size)
Definition: scripts.cpp:964
fade_t
Definition: scripts.h:135
bool isVirtual
Definition: inv_shared.h:284
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition: parse.cpp:107
float reloadAttenuation
Definition: inv_shared.h:298
bool campaign
Definition: q_shared.h:480
static void Com_ParseCharacterTemplate(const char *name, const char **text)
Parses character templates from scripts.
Definition: scripts.cpp:2865
Definition: scripts.h:49
static void Com_GetCrashedAircraftIdStr(aircraftType_t type, short idNum, char *outStr, const size_t size)
Definition: scripts.cpp:520
int idx
Definition: chr_shared.h:247
#define MAX_DEATH
Definition: q_shared.h:257
const char *const blend_names[]
Definition: scripts.cpp:345
const char * reloadSound
Definition: inv_shared.h:297
static void Com_ParseTeam(const char *name, const char **text)
Definition: scripts.cpp:2687
bool isAmmo() const
Definition: inv_shared.h:343
char id[MAX_VAR]
Definition: inv_shared.h:606
linkedList_t * sounds[SND_MAX][NAME_LAST]
Definition: chr_shared.h:328
int damParticle
Definition: q_shared.h:532
const char * id
Definition: inv_shared.h:268
#define AIR_SLOT_TYPE_STRINGS
Definition: scripts.h:154
bool Com_ParseList(const char **text, linkedList_t **list)
Definition: scripts.cpp:1363
static const value_t teamDefValues[]
possible teamdesc values (ufo-scriptfiles)
Definition: scripts.cpp:2672
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
mapDef_t * Com_GetMapDefinitionByID(const char *mapDefID)
Definition: scripts.cpp:3598
Definition: scripts.h:50
damageType_t dts[MAX_DAMAGETYPES]
Definition: q_shared.h:544
#define MAX_AMMOS_PER_OBJDEF
Definition: inv_shared.h:41
bool implant
Definition: inv_shared.h:282
#define DROPSHIP_MAX
Definition: inv_shared.h:32
static teamDef_t::model_t const * Com_GiveModel(int gender, const teamDef_t *td)
Definition: scripts.cpp:2318
static short Com_GetAircraftIdNum(aircraftType_t type, const char *idString)
Definition: scripts.cpp:487
const char * FS_GetFileData(const char *files)
Returns the buffer of a file.
Definition: files.cpp:1137
weaponFireDefIndex_t weapFdsIdx
Definition: inv_shared.h:126
char * FS_NextScriptHeader(const char *files, const char **name, const char **text)
Definition: files.cpp:1196
aircraftItemType_t
All different types of craft items.
Definition: inv_shared.h:197
ugv_t ugvs[MAX_UGV]
Definition: q_shared.h:564
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
static void Com_AddObjectLinks(void)
Creates links to other items (i.e. ammo<->weapons)
Definition: scripts.cpp:3395
linkedList_t * params
Definition: q_shared.h:465
bool thrown
Definition: inv_shared.h:281
const char * name
Definition: inv_shared.h:267
fireDefIndex_t numFiredefs[MAX_WEAPONS_PER_OBJDEF]
Definition: inv_shared.h:315
static const char * Com_GetAircraftDef(aircraftType_t type, short idNum)
Definition: scripts.cpp:479
#define Mem_Free(ptr)
Definition: mem.h:35
static const value_t idps[]
Definition: scripts.cpp:2082
unsigned int Com_HashKey(const char *name, int hashsize)
returns hash key for a string
Definition: shared.cpp:336
int damFire
Definition: q_shared.h:528
void Q_strcat(char *dest, size_t destsize, const char *format,...)
Safely (without overflowing the destination buffer) concatenates two strings.
Definition: shared.cpp:475
static void Com_ParseTerrain(const char *name, const char **text)
Parses "terrain" definition from script files.
Definition: scripts.cpp:3084
static void Com_ParseVersion(const char *version)
Definition: scripts.cpp:294
static char parseErrorMessage[256]
Definition: scripts.cpp:422
int numEDs
Definition: q_shared.h:541
struct com_constNameInt_s * hash_next
Definition: scripts.cpp:43
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition: scripts.h:34
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
#define UFO_SIZE_T
Definition: ufotypes.h:89
byte rgbRed
Definition: q_shared.h:358
static void Com_ParseGameTypes(const char *name, const char **text)
Definition: scripts.cpp:3119
itemEffect_t * deactiveEffect
Definition: inv_shared.h:132
void Com_Shutdown(void)
Definition: scripts.cpp:3732
#define LIST_Foreach(list, type, var)
Iterates over a linked list, it&#39;s safe to delete the returned entry from the list while looping over ...
Definition: list.h:41
style_t
Definition: scripts.h:124
vec_t vec3_t[3]
Definition: ufotypes.h:39
vec_t vec2_t[2]
Definition: ufotypes.h:38
#define SOUND_ATTN_NORM
Definition: common.h:186
static effectStages_t Com_ParseItemEffect(itemEffect_t *e, const char *name, const char **text)
Parses the item effect.
Definition: scripts.cpp:1563
this is a fire definition for our weapons/ammo
Definition: inv_shared.h:110
Definition: scripts.h:52
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
static const value_t implant_vals[]
Definition: scripts.cpp:2011
aircraftItemType_t type
Definition: inv_shared.h:247
Terrain property table entry Terrain is defined by the file map_earth_terrain.png in pics/geoscape...
Definition: q_shared.h:355
chrTemplate_t chrTemplates[MAX_CHARACTER_TEMPLATES]
Definition: q_shared.h:558
byte rgbBlue
Definition: q_shared.h:360
void * Com_AlignPtr(const void *memory, valueTypes_t type)
Align a memory to use a natural address for the data type we will write.
Definition: scripts.cpp:437
#define Mem_AllocType(type)
Definition: mem.h:39
char name[MAX_VAR]
Definition: chr_shared.h:390
#define MapDef_Foreach(var)
Definition: q_shared.h:505
float splrad
Definition: inv_shared.h:161
const char *const style_names[]
Definition: scripts.cpp:350
void Com_UnParseLastToken(void)
Put back the last token into the parser The next call of Com_Parse will return the same token again...
Definition: parse.cpp:42
static const char * Com_GiveName(int gender, const teamDef_t *td)
Definition: scripts.cpp:2283
const char * model
Definition: inv_shared.h:269
void setId(const char *id)
Definition: chr_shared.cpp:394
short protection[MAX_DAMAGETYPES]
Definition: inv_shared.h:322
#define lengthof(x)
Definition: shared.h:105
char * id
Definition: q_shared.h:463
fireDef_t fd[MAX_WEAPONS_PER_OBJDEF][MAX_FIREDEFS_PER_WEAPON]
Definition: inv_shared.h:314
const char * name
Definition: inv_shared.h:607
#define NONE
Definition: defines.h:68
#define Q_streq(a, b)
Definition: shared.h:136
#define Mem_PoolStrDup(in, pool, tagNum)
Definition: mem.h:50
void INVSH_InitCSI(const csi_t *import)
Initializes client server shared data pointer. This works because the client and the server are both ...
Definition: inv_shared.cpp:39
char name[MAX_VAR]
Definition: q_shared.h:337
Com_TokenType_t Com_NextToken(const char **data_p)
Compute the next token.
Definition: parse.cpp:69
linkedList_t * models[NAME_LAST]
Definition: chr_shared.h:325
char * id
Definition: chr_shared.h:246
const char * type
Definition: inv_shared.h:271
list of script aliases to register
Definition: scripts.h:231
#define MAX_EQUIPDEFS
Definition: inv_shared.h:603
#define Mem_PoolAllocType(type, pool)
Definition: mem.h:43
#define DEBUG_SHARED
Definition: defines.h:55
const char *const name_strings[NAME_NUM_TYPES]
Definition: scripts.cpp:2162
int numSounds[SND_MAX][NAME_LAST]
Definition: chr_shared.h:329
#define ACTOR_SIZE_NORMAL
Definition: defines.h:302
int damSmoke
Definition: q_shared.h:536
static const value_t mapdef_vals[]
valid mapdef descriptors
Definition: scripts.cpp:3442
const char * Com_GetToken(const char **data_p)
Get the current token value.
Definition: parse.cpp:51
humanAircraftType_t Com_DropShipShortNameToID(const char *token)
Translate DropShip type to short name.
Definition: scripts.cpp:3307
teamDef_t teamDef[MAX_TEAMDEFS]
Definition: q_shared.h:548
uint8_t byte
Definition: ufotypes.h:34
QGL_EXTERN int GLboolean GLfloat * v
Definition: r_gl.h:120
float delayBetweenShots
Definition: inv_shared.h:155
#define SHAPE_SMALL_MAX_HEIGHT
Definition: inv_shared.h:177
static const value_t gameTypeValues[]
possible gametype values for the gameserver (ufo-scriptfiles)
Definition: scripts.cpp:3114
#define ACTOR_SIZE_INVALID
Definition: defines.h:301
char name[MAX_VAR]
Definition: chr_shared.h:310
static ufoType_t Com_GetUfoIdNum(const char *idString)
Definition: scripts.cpp:529
byte sx
Definition: inv_shared.h:326
static void Com_ParseInventory(const char *name, const char **text)
Definition: scripts.cpp:2107
static struct mdfour * m
Definition: md4.cpp:35
short numBodyParts(void) const
Definition: chr_shared.cpp:389
bool oneshot
Definition: inv_shared.h:299
char * description
Definition: q_shared.h:466
static void Com_ParseFireEffect(fireDef_t *fd, const char *name, const char **text)
Parses the effect that is bound to a fire definitions.
Definition: scripts.cpp:1620
Definition: scripts.h:55
static void Com_ParseFireDefinition(objDef_t *od, const char *name, const char **text)
Definition: scripts.cpp:1806
const char * Com_UFOCrashedTypeToShortName(ufoType_t type)
Translate UFO type to short name when UFO is crashed.
Definition: scripts.cpp:3351
int numGTs
Definition: q_shared.h:568
int numAmmos
Definition: inv_shared.h:308
aircraftType_t
Definition: scripts.cpp:448
bool singleplayer
Definition: q_shared.h:481
static void Com_ParseAircraftNames(const char *const name, const char **text)
Parse the aircraft names from the scripts.
Definition: scripts.cpp:589
bool Com_ParseBlockToken(const char *name, const char **text, void *base, const value_t *values, memPool_t *mempool, const char *token)
Definition: scripts.cpp:1311
Defines a type of UGV/Robot.
Definition: chr_shared.h:245
bool Com_GetConstInt(const char *name, int *value)
Searches whether a given value was registered as a string to int mapping.
Definition: scripts.cpp:74
bool Com_UnregisterConstList(const constListEntry_t constList[])
Unregisters a list of string aliases.
Definition: scripts.cpp:237
int damBlast
Definition: q_shared.h:528
float asFloat() const
Definition: keyvaluepair.h:54
#define UFO_NONE
Definition: scripts.h:148
static void Com_ParseActorSounds(const char *name, const char **text, teamDef_t *td)
Parses "actorsounds" definition from team_* ufo script files.
Definition: scripts.cpp:2592
static com_constNameInt_t * com_constNameInt
Linked list of all the registeres mappings.
Definition: scripts.cpp:48
static void Com_ParseActorNames(const char *name, const char **text)
Parses "name" definition from team_* ufo script files.
Definition: scripts.cpp:2452
int damNormal
Definition: q_shared.h:528
int maxAliens
Definition: q_shared.h:483
const ugv_t * Com_GetUGVByID(const char *ugvID)
Searches an UGV definition by a given script id and returns the pointer to the global data...
Definition: scripts.cpp:3381
void Com_SkipBlock(const char **text)
Skips a block of {} in our script files.
Definition: parse.cpp:253
short Com_GetHumanAircraftIdsNum(void)
Definition: scripts.cpp:581
#define LittleLong(X)
Definition: byte.h:37
static void Com_ParseImplant(const char *name, const char **text)
Definition: scripts.cpp:2018
static void Com_ParseDamageTypes(const char *name, const char **text)
Definition: scripts.cpp:3194
bool Com_ParseBoolean(const char *token)
Parses a boolean from a string.
Definition: scripts.cpp:986
vec_t vec4_t[4]
Definition: ufotypes.h:40
static void Com_GetUfoIdStr(ufoType_t idNum, char *outStr, const size_t size)
Definition: scripts.cpp:539