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