UFO: Alien Invasion
Doxygen documentation generating
cp_parse.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2023 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
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 #include "../../DateTime.h"
26 #include "../../cl_shared.h"
27 #include "../../../shared/parse.h"
28 #include "cp_campaign.h"
29 #include "cp_rank.h"
30 #include "cp_parse.h"
31 #include "../../cl_inventory.h" /* INV_GetEquipmentDefinitionByID */
32 #include "cp_component.h"
33 
39 {
40  if (Q_streq(type, "recon"))
42  else if (Q_streq(type, "terror"))
44  else if (Q_streq(type, "baseattack"))
46  else if (Q_streq(type, "building"))
48  else if (Q_streq(type, "supply"))
50  else if (Q_streq(type, "xvi"))
51  return INTERESTCATEGORY_XVI;
52  else if (Q_streq(type, "intercept"))
54  else if (Q_streq(type, "harvest"))
56  else if (Q_streq(type, "alienbase"))
58  else if (Q_streq(type, "ufocarrier"))
60  else if (Q_streq(type, "rescue"))
62  else {
63  cgi->Com_Printf("CP_GetAlienMissionTypeByID: unknown alien mission category '%s'\n", type);
64  return INTERESTCATEGORY_NONE;
65  }
66 }
67 
68 static const value_t alien_group_vals[] = {
69  {"mininterest", V_INT, offsetof(alienTeamGroup_t, minInterest), 0},
70  {"maxinterest", V_INT, offsetof(alienTeamGroup_t, maxInterest), 0},
71  {"minaliencount", V_INT, offsetof(alienTeamGroup_t, minAlienCount), 0},
72  {"maxaliencount", V_INT, offsetof(alienTeamGroup_t, maxAlienCount), 0},
73  {nullptr, V_NULL, 0, 0}
74 };
75 
79 static void CP_ParseAlienTeam (const char* name, const char** text)
80 {
81  const char* errhead = "CP_ParseAlienTeam: unexpected end of file (alienteam ";
82  const char* token;
83  int i;
84  alienTeamCategory_t* alienCategory;
85 
86  /* get it's body */
87  token = Com_Parse(text);
88 
89  if (!*text || *token != '{') {
90  cgi->Com_Printf("CP_ParseAlienTeam: alien team category \"%s\" without body ignored\n", name);
91  return;
92  }
93 
95  cgi->Com_Printf("CP_ParseAlienTeam: maximum number of alien team category reached (%i)\n", ALIENCATEGORY_MAX);
96  return;
97  }
98 
99  /* search for category with same name */
100  for (i = 0; i < ccs.numAlienCategories; i++)
102  break;
103  if (i < ccs.numAlienCategories) {
104  cgi->Com_Printf("CP_ParseAlienTeam: alien category def \"%s\" with same name found, second ignored\n", name);
105  return;
106  }
107 
108  alienCategory = &ccs.alienCategories[ccs.numAlienCategories++];
109  Q_strncpyz(alienCategory->id, name, sizeof(alienCategory->id));
110 
111  do {
112  token = cgi->Com_EParse(text, errhead, name);
113  if (!*text)
114  break;
115  if (*token == '}')
116  break;
117 
118  if (Q_streq(token, "equipment")) {
119  linkedList_t** list = &alienCategory->equipment;
120  if (!cgi->Com_ParseList(text, list)) {
121  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: \"%s\" Error while parsing equipment list", name);
122  }
123  } else if (Q_streq(token, "category")) {
124  linkedList_t* list;
125  if (!cgi->Com_ParseList(text, &list)) {
126  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: \"%s\" Error while parsing category list", name);
127  }
128  for (linkedList_t* element = list; element != nullptr; element = element->next) {
129  alienCategory->missionCategories[alienCategory->numMissionCategories] = CP_GetAlienMissionTypeByID((const char*)element->data);
130  if (alienCategory->missionCategories[alienCategory->numMissionCategories] == INTERESTCATEGORY_NONE)
131  cgi->Com_Printf("CP_ParseAlienTeam: alien team category \"%s\" is used with no mission category. It won't be used in game.\n", name);
132  alienCategory->numMissionCategories++;
133  }
134  cgi->LIST_Delete(&list);
135  } else if (Q_streq(token, "teaminterest")) {
136  alienTeamGroup_t* group;
137 
138  token = cgi->Com_EParse(text, errhead, name);
139  if (!*text || *token != '{') {
140  cgi->Com_Printf("CP_ParseAlienTeam: alien team \"%s\" has team with no opening brace\n", name);
141  break;
142  }
143 
144  if (alienCategory->numAlienTeamGroups >= MAX_ALIEN_GROUP_PER_CATEGORY) {
145  cgi->Com_Printf("CP_ParseAlienTeam: maximum number of alien team reached (%i) in category \"%s\"\n", MAX_ALIEN_GROUP_PER_CATEGORY, name);
146  break;
147  }
148 
149  group = &alienCategory->alienTeamGroups[alienCategory->numAlienTeamGroups];
150  group->idx = alienCategory->numAlienTeamGroups;
151  group->categoryIdx = alienCategory - ccs.alienCategories;
152  alienCategory->numAlienTeamGroups++;
153 
154  do {
155  token = cgi->Com_EParse(text, errhead, name);
156 
157  if (!cgi->Com_ParseBlockToken(name, text, group, alien_group_vals, cp_campaignPool, token)) {
158  if (!*text || *token == '}')
159  break;
160 
161  if (Q_streq(token, "team")) {
162  linkedList_t* list;
163  if (!cgi->Com_ParseList(text, &list)) {
164  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: \"%s\" Error while parsing team list", name);
165  }
166  for (linkedList_t* element = list; element != nullptr; element = element->next) {
167  if (group->numAlienTeams >= MAX_TEAMS_PER_MISSION)
168  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: MAX_TEAMS_PER_MISSION hit");
169  const teamDef_t* teamDef = cgi->Com_GetTeamDefinitionByID(strtok((char*)element->data, "/"));
170  if (teamDef) {
171  group->alienTeams[group->numAlienTeams] = teamDef;
172  const chrTemplate_t* chrTemplate = CHRSH_GetTemplateByID(teamDef, strtok(nullptr, ""));
173  group->alienChrTemplates[group->numAlienTeams] = chrTemplate;
174  ++group->numAlienTeams;
175  }
176  }
177  cgi->LIST_Delete(&list);
178  } else {
179  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: Unknown token \"%s\"\n", token);
180  }
181  }
182  } while (*text);
183 
184  if (group->minAlienCount > group->maxAlienCount) {
185  cgi->Com_Printf("CP_ParseAlienTeam: Minimum number of aliens is greater than maximum value! Swapped.\n");
186  const int swap = group->minAlienCount;
187  group->minAlienCount = group->maxAlienCount;
188  group->maxAlienCount = swap;
189  }
190  } else {
191  cgi->Com_Printf("CP_ParseAlienTeam: unknown token \"%s\" ignored (category %s)\n", token, name);
192  continue;
193  }
194  } while (*text);
195 
196  if (cgi->LIST_IsEmpty(alienCategory->equipment))
197  Sys_Error("alien category equipment list is empty");
198 }
199 
203 static void CP_ParseResearchedCampaignItems (const campaign_t* campaign, const char* name, const char** text)
204 {
205  const char* errhead = "CP_ParseResearchedCampaignItems: unexpected end of file (equipment ";
206  const char* token;
207  int i;
208 
209  /* Don't parse if it is not definition for current type of campaign. */
210  if (!Q_streq(campaign->researched, name))
211  return;
212 
213  /* get it's body */
214  token = Com_Parse(text);
215 
216  if (!*text || *token != '{') {
217  cgi->Com_Printf("CP_ParseResearchedCampaignItems: equipment def \"%s\" without body ignored (%s)\n",
218  name, token);
219  return;
220  }
221 
222  cgi->Com_DPrintf(DEBUG_CLIENT, "..campaign research list '%s'\n", name);
223  do {
224  token = cgi->Com_EParse(text, errhead, name);
225  if (!*text || *token == '}')
226  return;
227 
228  for (i = 0; i < ccs.numTechnologies; i++) {
229  technology_t* tech = RS_GetTechByIDX(i);
230  assert(tech);
231  if (Q_streq(token, tech->id)) {
232  tech->mailSent = MAILSENT_FINISHED;
236  cgi->Com_DPrintf(DEBUG_CLIENT, "...tech %s\n", tech->id);
237  break;
238  }
239  }
240 
241  if (i == ccs.numTechnologies)
242  cgi->Com_Printf("CP_ParseResearchedCampaignItems: unknown token \"%s\" ignored (tech %s)\n", token, name);
243 
244  } while (*text);
245 }
246 
255 static void CP_ParseResearchableCampaignStates (const campaign_t* campaign, const char* name, const char** text, bool researchable)
256 {
257  const char* errhead = "CP_ParseResearchableCampaignStates: unexpected end of file (equipment ";
258  const char* token;
259  int i;
260 
261  /* get it's body */
262  token = Com_Parse(text);
263 
264  if (!*text || *token != '{') {
265  cgi->Com_Printf("CP_ParseResearchableCampaignStates: equipment def \"%s\" without body ignored\n", name);
266  return;
267  }
268 
269  if (!Q_streq(campaign->researched, name)) {
270  cgi->Com_DPrintf(DEBUG_CLIENT, "..don't use '%s' as researchable list\n", name);
271  return;
272  }
273 
274  cgi->Com_DPrintf(DEBUG_CLIENT, "..campaign researchable list '%s'\n", name);
275  do {
276  token = cgi->Com_EParse(text, errhead, name);
277  if (!*text || *token == '}')
278  return;
279 
280  for (i = 0; i < ccs.numTechnologies; i++) {
281  technology_t* tech = RS_GetTechByIDX(i);
282  if (Q_streq(token, tech->id)) {
283  if (researchable) {
284  tech->mailSent = MAILSENT_PROPOSAL;
286  } else {
288  }
289  cgi->Com_DPrintf(DEBUG_CLIENT, "...tech %s\n", tech->id);
290  break;
291  }
292  }
293 
294  if (i == ccs.numTechnologies)
295  cgi->Com_Printf("CP_ParseResearchableCampaignStates: unknown token \"%s\" ignored (tech %s)\n", token, name);
296 
297  } while (*text);
298 }
299 
300 /* =========================================================== */
301 
302 static const value_t salary_vals[] = {
303  {"soldier_base", V_INT, offsetof(salary_t, base[EMPL_SOLDIER]), MEMBER_SIZEOF(salary_t, base[EMPL_SOLDIER])},
304  {"soldier_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_SOLDIER]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_SOLDIER])},
305  {"worker_base", V_INT, offsetof(salary_t, base[EMPL_WORKER]), MEMBER_SIZEOF(salary_t, base[EMPL_WORKER])},
306  {"worker_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_WORKER]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_WORKER])},
307  {"scientist_base", V_INT, offsetof(salary_t, base[EMPL_SCIENTIST]), MEMBER_SIZEOF(salary_t, base[EMPL_SCIENTIST])},
308  {"scientist_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_SCIENTIST]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_SCIENTIST])},
309  {"pilot_base", V_INT, offsetof(salary_t, base[EMPL_PILOT]), MEMBER_SIZEOF(salary_t, base[EMPL_PILOT])},
310  {"pilot_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_PILOT]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_PILOT])},
311  {"robot_base", V_INT, offsetof(salary_t, base[EMPL_ROBOT]), MEMBER_SIZEOF(salary_t, base[EMPL_ROBOT])},
312  {"robot_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_ROBOT]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_ROBOT])},
313  {"aircraft_factor", V_INT, offsetof(salary_t, aircraftFactor), MEMBER_SIZEOF(salary_t, aircraftFactor)},
314  {"aircraft_divisor", V_INT, offsetof(salary_t, aircraftDivisor), MEMBER_SIZEOF(salary_t, aircraftDivisor)},
315  {"base_upkeep", V_INT, offsetof(salary_t, baseUpkeep), MEMBER_SIZEOF(salary_t, baseUpkeep)},
316  {"debt_interest", V_FLOAT, offsetof(salary_t, debtInterest), MEMBER_SIZEOF(salary_t, debtInterest)},
317  {nullptr, V_NULL, 0, 0}
318 };
319 
330 static void CP_ParseSalary (const char* name, const char** text, salary_t* s)
331 {
332  cgi->Com_ParseBlock(name, text, s, salary_vals, cp_campaignPool);
333 }
334 
335 /* =========================================================== */
336 
337 static const value_t campaign_vals[] = {
338  {"default", V_BOOL, offsetof(campaign_t, defaultCampaign), MEMBER_SIZEOF(campaign_t, defaultCampaign)},
339  {"team", V_TEAM, offsetof(campaign_t, team), MEMBER_SIZEOF(campaign_t, team)},
340  {"soldiers", V_INT, offsetof(campaign_t, soldiers), MEMBER_SIZEOF(campaign_t, soldiers)},
341  {"workers", V_INT, offsetof(campaign_t, workers), MEMBER_SIZEOF(campaign_t, workers)},
342  {"xvirate", V_INT, offsetof(campaign_t, maxAllowedXVIRateUntilLost), MEMBER_SIZEOF(campaign_t, maxAllowedXVIRateUntilLost)},
343  {"maxdebts", V_INT, offsetof(campaign_t, negativeCreditsUntilLost), MEMBER_SIZEOF(campaign_t, negativeCreditsUntilLost)},
344  {"minhappiness", V_FLOAT, offsetof(campaign_t, minhappiness), MEMBER_SIZEOF(campaign_t, minhappiness)},
345  {"scientists", V_INT, offsetof(campaign_t, scientists), MEMBER_SIZEOF(campaign_t, scientists)},
346  {"pilots", V_INT, offsetof(campaign_t, pilots), MEMBER_SIZEOF(campaign_t, pilots)},
347  {"ugvs", V_INT, offsetof(campaign_t, ugvs), MEMBER_SIZEOF(campaign_t, ugvs)},
348  {"equipment", V_STRING, offsetof(campaign_t, equipment), 0},
349  {"soldierequipment", V_STRING, offsetof(campaign_t, soldierEquipment), 0},
350  {"market", V_STRING, offsetof(campaign_t, market), 0},
351  {"asymptotic_market", V_STRING, offsetof(campaign_t, asymptoticMarket), 0},
352  {"researched", V_STRING, offsetof(campaign_t, researched), 0},
353  {"difficulty", V_INT, offsetof(campaign_t, difficulty), MEMBER_SIZEOF(campaign_t, difficulty)},
354  {"map", V_STRING, offsetof(campaign_t, map), 0},
355  {"credits", V_INT, offsetof(campaign_t, credits), MEMBER_SIZEOF(campaign_t, credits)},
356  {"visible", V_BOOL, offsetof(campaign_t, visible), MEMBER_SIZEOF(campaign_t, visible)},
357  {"text", V_TRANSLATION_STRING, offsetof(campaign_t, text), 0}, /* just a gettext placeholder */
358  {"name", V_TRANSLATION_STRING, offsetof(campaign_t, name), 0},
359  {"basecost", V_INT, offsetof(campaign_t, basecost), MEMBER_SIZEOF(campaign_t, basecost)},
360  {"firstbase", V_STRING, offsetof(campaign_t, firstBaseTemplate), 0},
361  {"researchrate", V_FLOAT, offsetof(campaign_t, researchRate), MEMBER_SIZEOF(campaign_t, researchRate)},
362  {"producerate", V_FLOAT, offsetof(campaign_t, produceRate), MEMBER_SIZEOF(campaign_t, produceRate)},
363  {"healingrate", V_FLOAT, offsetof(campaign_t, healingRate), MEMBER_SIZEOF(campaign_t, healingRate)},
364  {"liquidationrate", V_FLOAT, offsetof(campaign_t, liquidationRate), MEMBER_SIZEOF(campaign_t, liquidationRate)},
365  {"componentrate", V_FLOAT, offsetof(campaign_t, componentRate), MEMBER_SIZEOF(campaign_t, componentRate)},
366  {"minmissions", V_INT, offsetof(campaign_t, minMissions), MEMBER_SIZEOF(campaign_t, minMissions)},
367  {"maxmissions", V_INT, offsetof(campaign_t, maxMissions), MEMBER_SIZEOF(campaign_t, maxMissions)},
368  {"uforeductionrate", V_FLOAT, offsetof(campaign_t, ufoReductionRate), MEMBER_SIZEOF(campaign_t, ufoReductionRate)},
369  {"initialinterest", V_INT, offsetof(campaign_t, initialInterest), MEMBER_SIZEOF(campaign_t, initialInterest)},
370  {"employeerate", V_FLOAT, offsetof(campaign_t, employeeRate), MEMBER_SIZEOF(campaign_t, employeeRate)},
371  {"alienbaseinterest", V_INT, offsetof(campaign_t, alienBaseInterest), MEMBER_SIZEOF(campaign_t, alienBaseInterest)},
372  {nullptr, V_NULL, 0, 0}
373 };
374 
378 static void CP_ParseCampaign (const char* name, const char** text)
379 {
380  const char* errhead = "CP_ParseCampaign: unexpected end of file (campaign ";
381  campaign_t* cp;
382  const char* token;
383  int i;
384  salary_t* s;
385  bool drop = false;
386 
387  /* search for campaigns with same name */
388  if (CP_GetCampaign(name) != nullptr) {
389  cgi->Com_Printf("CP_ParseCampaign: campaign def \"%s\" with same name found, second ignored\n", name);
390  return;
391  }
392 
393  if (ccs.numCampaigns >= MAX_CAMPAIGNS) {
394  cgi->Com_Printf("CP_ParseCampaign: Max campaigns reached (%i)\n", MAX_CAMPAIGNS);
395  return;
396  }
397 
398  /* initialize the campaign */
399  cp = &ccs.campaigns[ccs.numCampaigns++];
400  OBJZERO(*cp);
401  cp->idx = ccs.numCampaigns - 1;
402  Q_strncpyz(cp->id, name, sizeof(cp->id));
403  cp->team = TEAM_PHALANX;
404  Q_strncpyz(cp->researched, "researched_human", sizeof(cp->researched));
405  cp->researchRate = 0.8f;
406  cp->produceRate = 1.0f;
407  cp->healingRate = 1.0f;
408  cp->liquidationRate = 0.0f;
409  cp->componentRate = 1.0f;
410  cp->maxMissions = 17;
411  cp->minMissions = 5;
414  cp->employeeRate = 1.0f;
415  cp->alienBaseInterest = 200;
416 
417  /* get it's body */
418  token = Com_Parse(text);
419 
420  if (!*text || *token != '{') {
421  cgi->Com_Printf("CP_ParseCampaign: campaign def \"%s\" without body ignored\n", name);
422  ccs.numCampaigns--;
423  return;
424  }
425 
426  /* set undefined markers */
427  s = &cp->salaries;
428  for (i = 0; i < MAX_EMPL; i++) {
429  s->base[i] = -1;
430  s->rankBonus[i] = -1;
431  }
432  s->aircraftFactor = -1;
433  s->aircraftDivisor = -1;
434  s->baseUpkeep = -1;
435  s->debtInterest = -1;
436 
437  do {
438  token = cgi->Com_EParse(text, errhead, name);
439  if (!*text)
440  break;
441  if (*token == '}')
442  break;
443 
444  /* check for some standard values */
445  if (cgi->Com_ParseBlockToken(name, text, cp, campaign_vals, nullptr, token)) {
446  continue;
447  } else if (Q_streq(token, "salary")) {
448  CP_ParseSalary(token, text, s);
449  } else if (Q_streq(token, "events")) {
450  token = cgi->Com_EParse(text, errhead, name);
451  if (!*text)
452  return;
453  cp->events = CP_GetEventsByID(token);
454  } else if (Q_streq(token, "aircraft")) {
455  cgi->Com_ParseList(text, &cp->initialCraft);
456  } else if (Q_streq(token, "date")) {
457  token = cgi->Com_EParse(text, errhead, name);
458  if (!*text)
459  return;
460  int year;
461  int day;
462  int hour;
463  if (sscanf(token, "%i %i %i", &year, &day, &hour) != 3) {
464  Com_Error(ERR_DROP, "Illegal campaign start date for campaign %s", cp->id);
465  }
467  } else {
468  cgi->Com_Printf("CP_ParseCampaign: unknown token \"%s\" ignored (campaign %s)\n", token, name);
469  cgi->Com_EParse(text, errhead, name);
470  }
471  } while (*text);
472 
473  if (cp->difficulty < -4)
474  cp->difficulty = -4;
475  else if (cp->difficulty > 4)
476  cp->difficulty = 4;
477 
478  /* checking for undefined values */
479  for (i = 0; i < MAX_EMPL; i++) {
480  if (s->base[i] == -1 || s->rankBonus[i] == -1) {
481  drop = true;
482  break;
483  }
484  }
485  if (drop || s->aircraftFactor == -1 || s->aircraftDivisor == -1 || s->baseUpkeep == -1
486  || s->debtInterest == -1) {
487  cgi->Com_Printf("CP_ParseCampaign: check salary definition. Campaign def \"%s\" ignored\n", name);
488  ccs.numCampaigns--;
489  return;
490  }
491 }
492 
501 static void CP_ParseScriptFirst (const char* type, const char* name, const char** text)
502 {
503  /* check for client interpretable scripts */
504  if (Q_streq(type, "up_chapter"))
505  UP_ParseChapter(name, text);
506  else if (Q_streq(type, "building"))
507  B_ParseBuildings(name, text, false);
508  else if (Q_streq(type, "installation"))
510  else if (Q_streq(type, "tech"))
511  RS_ParseTechnologies(name, text);
512  else if (Q_streq(type, "nation"))
513  CL_ParseNations(name, text);
514  else if (Q_streq(type, "city"))
515  CITY_Parse(name, text);
516  else if (Q_streq(type, "rank"))
517  CL_ParseRanks(name, text);
518  else if (Q_streq(type, "aircraft"))
519  AIR_ParseAircraft(name, text, false);
520  else if (Q_streq(type, "mail"))
521  CL_ParseEventMails(name, text);
522  else if (Q_streq(type, "events"))
524  else if (Q_streq(type, "event"))
525  CP_ParseEventTrigger(name, text);
526  else if (Q_streq(type, "components"))
527  COMP_ParseComponents(name, text);
528  else if (Q_streq(type, "alienteam"))
529  CP_ParseAlienTeam(name, text);
530  else if (Q_streq(type, "msgoptions"))
532 }
533 
545 static void CP_ParseScriptSecond (const char* type, const char* name, const char** text)
546 {
547  /* check for client interpretable scripts */
548  if (Q_streq(type, "building"))
549  B_ParseBuildings(name, text, true);
550  else if (Q_streq(type, "aircraft"))
551  AIR_ParseAircraft(name, text, true);
552  else if (Q_streq(type, "basetemplate"))
553  B_ParseBaseTemplate(name, text);
554  else if (Q_streq(type, "campaign"))
555  CP_ParseCampaign(name, text);
556 }
557 
561 static void CP_ParseScriptCampaignRelated (const campaign_t* campaign, const char* type, const char* name, const char** text)
562 {
563  if (Q_streq(type, "researched"))
564  CP_ParseResearchedCampaignItems(campaign, name, text);
565  else if (Q_streq(type, "researchable"))
566  CP_ParseResearchableCampaignStates(campaign, name, text, true);
567  else if (Q_streq(type, "notresearchable"))
568  CP_ParseResearchableCampaignStates(campaign, name, text, false);
569 }
570 
574 static bool CP_ItemsSanityCheck (void)
575 {
576  bool result = true;
577 
578  for (int i = 0; i < cgi->csi->numODs; i++) {
579  const objDef_t* item = INVSH_GetItemByIDX(i);
580 
581  /* Warn if item has no size set. */
582  if (item->size <= 0 && B_ItemIsStoredInBaseStorage(item)) {
583  result = false;
584  cgi->Com_Printf("CP_ItemsSanityCheck: Item %s has zero size set.\n", item->id);
585  }
586 
587  /* Warn if no price is set. */
588  if (item->price <= 0 && BS_IsOnMarket(item)) {
589  result = false;
590  cgi->Com_Printf("CP_ItemsSanityCheck: Item %s has zero price set.\n", item->id);
591  }
592 
593  if (item->price > 0 && !BS_IsOnMarket(item) && !PR_ItemIsProduceable(item)) {
594  result = false;
595  cgi->Com_Printf("CP_ItemsSanityCheck: Item %s has a price set though it is neither available on the market and production.\n", item->id);
596  }
597  }
598 
599  return result;
600 }
601 
603 typedef struct {
604  bool (*check)(void);
605  const char* name;
607 
610  {B_BuildingScriptSanityCheck, "buildings"},
611  {RS_ScriptSanityCheck, "tech"},
612  {AIR_ScriptSanityCheck, "aircraft"},
613  {CP_ItemsSanityCheck, "items"},
614  {NAT_ScriptSanityCheck, "nations"},
615 
616  {nullptr, nullptr}
617 };
618 
624 {
625  const sanity_functions_t* s;
626 
627  cgi->Com_Printf("Sanity check for script data\n");
628  s = sanity_functions;
629  while (s->check) {
630  bool status = s->check();
631  cgi->Com_Printf("...%s %s\n", s->name, (status ? "ok" : "failed"));
632  s++;
633  }
634 }
635 
642 {
643  const char* type, *name, *text;
644  int i;
645  campaign_t* campaign;
646 
647  /* pre-stage parsing */
648  cgi->FS_BuildFileList("ufos/*.ufo");
649  cgi->FS_NextScriptHeader(nullptr, nullptr, nullptr);
650  text = nullptr;
651 
652  while ((type = cgi->FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr)
653  CP_ParseScriptFirst(type, name, &text);
654 
655  /* fill in IDXs for required research techs */
657 
658  /* stage two parsing */
659  cgi->FS_NextScriptHeader(nullptr, nullptr, nullptr);
660  text = nullptr;
661 
662  cgi->Com_DPrintf(DEBUG_CLIENT, "Second stage parsing started...\n");
663  while ((type = cgi->FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr)
664  CP_ParseScriptSecond(type, name, &text);
666 
667  for (i = 0; i < cgi->csi->numTeamDefs; i++) {
668  const teamDef_t* teamDef = &cgi->csi->teamDef[i];
669  if (!CHRSH_IsTeamDefAlien(teamDef))
670  continue;
671 
672  ccs.teamDefTechs[teamDef->idx] = RS_GetTechByID(teamDef->tech);
673  if (ccs.teamDefTechs[teamDef->idx] == nullptr)
674  cgi->Com_Error(ERR_DROP, "Could not find a tech for teamdef %s", teamDef->id);
675  }
676 
677  for (i = 0, campaign = ccs.campaigns; i < ccs.numCampaigns; i++, campaign++) {
678  /* find the relevant markets */
679  campaign->marketDef = cgi->INV_GetEquipmentDefinitionByID(campaign->market);
681  }
682 
683  cgi->Com_Printf("Campaign data loaded - size " UFO_SIZE_T " bytes\n", sizeof(ccs));
684  cgi->Com_Printf("...techs: %i\n", ccs.numTechnologies);
685  cgi->Com_Printf("...buildings: %i\n", ccs.numBuildingTemplates);
686  cgi->Com_Printf("...ranks: %i\n", ccs.numRanks);
687  cgi->Com_Printf("...nations: %i\n", ccs.numNations);
688  cgi->Com_Printf("...cities: %i\n", ccs.numCities);
689  cgi->Com_Printf("\n");
690 }
691 
692 void CP_ReadCampaignData (const campaign_t* campaign)
693 {
694  const char* type, *name, *text;
695 
696  /* stage two parsing */
697  cgi->FS_NextScriptHeader(nullptr, nullptr, nullptr);
698  text = nullptr;
699 
700  cgi->Com_DPrintf(DEBUG_CLIENT, "Second stage parsing started...\n");
701  while ((type = cgi->FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr)
702  CP_ParseScriptCampaignRelated(campaign, type, name, &text);
703 
704  ccs.date = campaign->date;
705 }
static void CP_ParseResearchedCampaignItems(const campaign_t *campaign, const char *name, const char **text)
This function parses a list of items that should be set to researched = true after campaign start...
Definition: cp_parse.cpp:203
interestCategory_t missionCategories[INTERESTCATEGORY_MAX]
Definition: cp_campaign.h:127
Class describing a point of time.
Definition: DateTime.h:30
void INS_LinkTechnologies(void)
int numTeamDefs
Definition: q_shared.h:549
static void CP_ParseScriptCampaignRelated(const campaign_t *campaign, const char *type, const char *name, const char **text)
Parses the campaign specific data - this data can only be parsed once the campaign started...
Definition: cp_parse.cpp:561
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
int minMissions
Definition: cp_campaign.h:202
alien team group definition.
Definition: cp_campaign.h:106
char asymptoticMarket[MAX_VAR]
Definition: cp_campaign.h:174
csi_t * csi
Definition: cgame.h:100
void UP_ParseChapter(const char *name, const char **text)
Parse the UFOpaedia chapters from scripts.
technology_t * RS_GetTechByIDX(int techIdx)
Returns the technology pointer for a tech index. You can use this instead of "&ccs.technologies[techIdx]" to avoid having to check valid indices.
Header file for Aircraft and item components.
static void CP_ParseResearchableCampaignStates(const campaign_t *campaign, const char *name, const char **text, bool researchable)
This function parses a list of items that should be set to researchable = true after campaign start...
Definition: cp_parse.cpp:255
static const value_t alien_group_vals[]
Definition: cp_parse.cpp:68
#define MAX_CAMPAIGNS
Definition: cp_campaign.h:31
Definition: scripts.h:70
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
float ufoReductionRate
Definition: cp_campaign.h:204
#define TEAM_PHALANX
Definition: q_shared.h:62
interestCategory_t
int numAlienCategories
Definition: cp_campaign.h:319
static void CP_ParseScriptSecond(const char *type, const char *name, const char **text)
Parsing only for singleplayer.
Definition: cp_parse.cpp:545
static void CP_ParseAlienTeam(const char *name, const char **text)
Definition: cp_parse.cpp:79
void RS_MarkOneResearchable(technology_t *tech)
Marks one tech as researchable.
int numNations
Definition: cp_campaign.h:297
bool(* check)(void)
Definition: cp_parse.cpp:604
static void CP_ParseCampaign(const char *name, const char **text)
Definition: cp_parse.cpp:378
float componentRate
Definition: cp_campaign.h:201
const equipDef_t * marketDef
Definition: cp_campaign.h:175
bool BS_IsOnMarket(const objDef_t *item)
Check if an item is on market.
Definition: cp_market.cpp:42
int alienBaseInterest
Definition: cp_campaign.h:209
int numTechnologies
Definition: cp_campaign.h:278
static const value_t campaign_vals[]
Definition: cp_parse.cpp:337
linkedList_t * initialCraft
Definition: cp_campaign.h:210
void B_ParseBuildings(const char *name, const char **text, bool link)
Copies an entry from the building description file into the list of building types.
bool CHRSH_IsTeamDefAlien(const teamDef_t *const td)
Check if a team definition is alien.
Definition: chr_shared.cpp:83
technology_t * teamDefTechs[MAX_TEAMDEFS]
Definition: cp_campaign.h:373
int maxMissions
Definition: cp_campaign.h:203
memPool_t * cp_campaignPool
Definition: cp_campaign.cpp:62
mailSentType_t mailSent
Definition: cp_research.h:180
char tech[MAX_VAR]
Definition: chr_shared.h:311
char *IMPORT * PoolStrDup(const char *in, memPool_t *pool, const int tagNum)
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
void CL_ParseCampaignEvents(const char *name, const char **text)
Definition: cp_event.cpp:448
markResearched_t markResearched
Definition: cp_research.h:189
void B_ParseBaseTemplate(const char *name, const char **text)
Reads a base layout template.
Definition: cp_base.cpp:1435
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
void RS_ParseTechnologies(const char *name, const char **text)
Parses one "tech" entry in the research.ufo file and writes it into the next free entry in technologi...
float healingRate
Definition: cp_campaign.h:199
#define ERR_DROP
Definition: common.h:211
#define DEBUG_CLIENT
Definition: defines.h:59
int numBuildingTemplates
Definition: cp_campaign.h:340
bool AIR_ScriptSanityCheck(void)
Checks the parsed aircraft for errors.
class DateTime date
Definition: cp_campaign.h:191
int numCities
Definition: cp_campaign.h:301
char id[MAX_VAR]
Definition: chr_shared.h:309
#define OBJZERO(obj)
Definition: shared.h:178
void CP_ParseEventTrigger(const char *name, const char **text)
Definition: cp_event.cpp:359
float produceRate
Definition: cp_campaign.h:197
campaign_t campaigns[MAX_CAMPAIGNS]
Definition: cp_campaign.h:381
static const int DAYS_PER_YEAR
Definition: DateTime.h:37
int aircraftFactor
Definition: cp_campaign.h:157
void INS_ParseInstallations(const char *name, const char **text)
Copies an entry from the installation description file into the list of installation templates...
void CP_ScriptSanityCheck(void)
Check the parsed script values for errors after parsing every script file.
Definition: cp_parse.cpp:623
bool RS_ScriptSanityCheck(void)
Checks the parsed tech data for errors.
void AIR_ParseAircraft(const char *name, const char **text, bool assignAircraftItems)
Parses all aircraft that are defined in our UFO-scripts.
const cgame_import_t * cgi
int initialInterest
Definition: cp_campaign.h:208
Campaign parsing header.
int base[MAX_EMPL]
Definition: cp_campaign.h:155
static bool CP_ItemsSanityCheck(void)
Make sure values of items after parsing are proper.
Definition: cp_parse.cpp:574
struct that holds the sanity check data
Definition: cp_parse.cpp:603
const chrTemplate_t * CHRSH_GetTemplateByID(const teamDef_t *teamDef, const char *templateId)
Definition: chr_shared.cpp:108
static const sanity_functions_t sanity_functions[]
Data for sanity check of parsed script data.
Definition: cp_parse.cpp:609
char id[MAX_VAR]
Definition: cp_campaign.h:126
campaign_t * CP_GetCampaign(const char *name)
Returns the campaign pointer from global campaign array.
ccs_t ccs
Definition: cp_campaign.cpp:63
const equipDef_t *IMPORT * INV_GetEquipmentDefinitionByID(const char *name)
linkedList_t * next
Definition: list.h:32
alienTeamGroup_t alienTeamGroups[MAX_ALIEN_GROUP_PER_CATEGORY]
Definition: cp_campaign.h:133
salary_t salaries
Definition: cp_campaign.h:196
int numODs
Definition: q_shared.h:518
const campaignEvents_t * CP_GetEventsByID(const char *name)
Definition: cp_event.cpp:139
char * campaign[MAX_CAMPAIGNS]
Definition: cp_research.h:94
int price
Definition: inv_shared.h:332
const teamDef_t *IMPORT * Com_GetTeamDefinitionByID(const char *team)
char id[MAX_VAR]
Definition: cp_campaign.h:166
int aircraftDivisor
Definition: cp_campaign.h:158
technology_t * RS_GetTechByID(const char *id)
return a pointer to the technology identified by given id string
const teamDef_t * alienTeams[MAX_TEAMS_PER_MISSION]
Definition: cp_campaign.h:114
alienTeamCategory_t alienCategories[ALIENCATEGORY_MAX]
Definition: cp_campaign.h:318
QGL_EXTERN void(APIENTRY *qglActiveTexture)(GLenum texture)
static void CP_ParseSalary(const char *name, const char **text, salary_t *s)
Parse the salaries from campaign definition.
Definition: cp_parse.cpp:330
float liquidationRate
Definition: cp_campaign.h:200
const chrTemplate_t * alienChrTemplates[MAX_TEAMS_PER_MISSION]
Definition: cp_campaign.h:116
static interestCategory_t CP_GetAlienMissionTypeByID(const char *type)
Definition: cp_parse.cpp:38
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
char market[MAX_VAR]
Definition: cp_campaign.h:173
Definition: scripts.h:49
bool B_BuildingScriptSanityCheck(void)
Checks the parsed buildings for errors.
const char * id
Definition: inv_shared.h:268
QGL_EXTERN GLint i
Definition: r_gl.h:113
void MSO_ParseMessageSettings(const char *name, const char **text)
parses message options settings from file.
float employeeRate
Definition: cp_campaign.h:207
Definition: scripts.h:50
This is the technology parsed from research.ufo.
Definition: cp_research.h:139
void RS_RequiredLinksAssign(void)
Assign Link pointers to all required techs/items/etc...
#define INITIAL_OVERALL_INTEREST
Determines the interest interval for a single campaign.
Definition: cp_campaign.h:72
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
class DateTime date
Definition: cp_campaign.h:246
#define MAX_TEAMS_PER_MISSION
Definition: inv_shared.h:618
int numRanks
Definition: cp_campaign.h:370
char *IMPORT * FS_NextScriptHeader(const char *files, const char **name, const char **text)
int baseUpkeep
Definition: cp_campaign.h:159
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition: scripts.h:34
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
#define UFO_SIZE_T
Definition: ufotypes.h:89
Header file for single player campaign control.
void CP_ParseCampaignData(void)
Read the data for campaigns.
Definition: cp_parse.cpp:641
int numCampaigns
Definition: cp_campaign.h:382
linkedList_t * equipment
Definition: cp_campaign.h:131
Definition: scripts.h:52
signed int difficulty
Definition: cp_campaign.h:186
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
Definition: inv_shared.cpp:266
static const short SECONDS_PER_HOUR
Definition: DateTime.h:42
alien team category definition
Definition: cp_campaign.h:125
const campaignEvents_t * events
Definition: cp_campaign.h:195
float debtInterest
Definition: cp_campaign.h:160
char researched[MAX_VAR]
Definition: cp_campaign.h:170
const equipDef_t * asymptoticMarketDef
Definition: cp_campaign.h:176
static void CP_ParseScriptFirst(const char *type, const char *name, const char **text)
Parsing campaign data.
Definition: cp_parse.cpp:501
bool PR_ItemIsProduceable(const objDef_t *item)
check if an item is producable.
Definition: cp_produce.cpp:640
#define Q_streq(a, b)
Definition: shared.h:136
void CL_ParseRanks(const char *name, const char **text)
Parse medals and ranks defined in the medals.ufo file.
Definition: cp_rank.cpp:74
void COMP_ParseComponents(const char *name, const char **text)
Parses one "components" entry in a .ufo file and writes it into the next free entry in xxxxxxxx (comp...
#define ALIENCATEGORY_MAX
Definition: cp_campaign.h:61
int size
Definition: inv_shared.h:334
void CL_ParseNations(const char *name, const char **text)
Parse the nation data from script file.
Definition: cp_nation.cpp:383
void CL_ParseEventMails(const char *name, const char **text)
Definition: cp_event.cpp:93
const char *IMPORT * Com_EParse(const char **text, const char *errhead, const char *errinfo)
teamDef_t teamDef[MAX_TEAMDEFS]
Definition: q_shared.h:548
int rankBonus[MAX_EMPL]
Definition: cp_campaign.h:156
#define NON_OCCURRENCE_PROBABILITY
The probability that any new alien mission will be a non-occurrence mission.
Definition: cp_campaign.h:89
const char * name
Definition: cp_parse.cpp:605
float researchRate
Definition: cp_campaign.h:198
bool NAT_ScriptSanityCheck(void)
Checks the parsed nations and cities for errors.
Definition: cp_nation.cpp:470
static const value_t salary_vals[]
Definition: cp_parse.cpp:302
void CITY_Parse(const char *name, const char **text)
Parse the city data from script file.
Definition: cp_nation.cpp:446
void CP_ReadCampaignData(const campaign_t *campaign)
Definition: cp_parse.cpp:692
bool markOnly[MAX_CAMPAIGNS]
Definition: cp_research.h:93
bool B_ItemIsStoredInBaseStorage(const objDef_t *obj)
Check if an item is stored in storage.
Definition: cp_base.cpp:2560
#define MAX_ALIEN_GROUP_PER_CATEGORY
Definition: cp_campaign.h:59