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