UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
cp_parse.cpp
Go to the documentation of this file.
1 
6 /*
7 Copyright (C) 2002-2020 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
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 "../../cl_shared.h"
26 #include "../../../shared/parse.h"
27 #include "cp_campaign.h"
28 #include "cp_rank.h"
29 #include "cp_parse.h"
30 #include "../../cl_inventory.h" /* INV_GetEquipmentDefinitionByID */
31 #include "cp_component.h"
32 
38 {
39  if (Q_streq(type, "recon"))
41  else if (Q_streq(type, "terror"))
43  else if (Q_streq(type, "baseattack"))
45  else if (Q_streq(type, "building"))
47  else if (Q_streq(type, "supply"))
49  else if (Q_streq(type, "xvi"))
50  return INTERESTCATEGORY_XVI;
51  else if (Q_streq(type, "intercept"))
53  else if (Q_streq(type, "harvest"))
55  else if (Q_streq(type, "alienbase"))
57  else if (Q_streq(type, "ufocarrier"))
59  else if (Q_streq(type, "rescue"))
61  else {
62  cgi->Com_Printf("CP_GetAlienMissionTypeByID: unknown alien mission category '%s'\n", type);
63  return INTERESTCATEGORY_NONE;
64  }
65 }
66 
67 static const value_t alien_group_vals[] = {
68  {"mininterest", V_INT, offsetof(alienTeamGroup_t, minInterest), 0},
69  {"maxinterest", V_INT, offsetof(alienTeamGroup_t, maxInterest), 0},
70  {"minaliencount", V_INT, offsetof(alienTeamGroup_t, minAlienCount), 0},
71  {"maxaliencount", V_INT, offsetof(alienTeamGroup_t, maxAlienCount), 0},
72  {nullptr, V_NULL, 0, 0}
73 };
74 
78 static void CP_ParseAlienTeam (const char* name, const char** text)
79 {
80  const char* errhead = "CP_ParseAlienTeam: unexpected end of file (alienteam ";
81  const char* token;
82  int i;
83  alienTeamCategory_t* alienCategory;
84 
85  /* get it's body */
86  token = Com_Parse(text);
87 
88  if (!*text || *token != '{') {
89  cgi->Com_Printf("CP_ParseAlienTeam: alien team category \"%s\" without body ignored\n", name);
90  return;
91  }
92 
94  cgi->Com_Printf("CP_ParseAlienTeam: maximum number of alien team category reached (%i)\n", ALIENCATEGORY_MAX);
95  return;
96  }
97 
98  /* search for category with same name */
99  for (i = 0; i < ccs.numAlienCategories; i++)
100  if (Q_streq(name, ccs.alienCategories[i].id))
101  break;
102  if (i < ccs.numAlienCategories) {
103  cgi->Com_Printf("CP_ParseAlienTeam: alien category def \"%s\" with same name found, second ignored\n", name);
104  return;
105  }
106 
107  alienCategory = &ccs.alienCategories[ccs.numAlienCategories++];
108  Q_strncpyz(alienCategory->id, name, sizeof(alienCategory->id));
109 
110  do {
111  token = cgi->Com_EParse(text, errhead, name);
112  if (!*text)
113  break;
114  if (*token == '}')
115  break;
116 
117  if (Q_streq(token, "equipment")) {
118  linkedList_t** list = &alienCategory->equipment;
119  if (!cgi->Com_ParseList(text, list)) {
120  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: \"%s\" Error while parsing equipment list", name);
121  }
122  } else if (Q_streq(token, "category")) {
123  linkedList_t* list;
124  if (!cgi->Com_ParseList(text, &list)) {
125  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: \"%s\" Error while parsing category list", name);
126  }
127  for (linkedList_t* element = list; element != nullptr; element = element->next) {
128  alienCategory->missionCategories[alienCategory->numMissionCategories] = CP_GetAlienMissionTypeByID((const char*)element->data);
129  if (alienCategory->missionCategories[alienCategory->numMissionCategories] == INTERESTCATEGORY_NONE)
130  cgi->Com_Printf("CP_ParseAlienTeam: alien team category \"%s\" is used with no mission category. It won't be used in game.\n", name);
131  alienCategory->numMissionCategories++;
132  }
133  cgi->LIST_Delete(&list);
134  } else if (Q_streq(token, "teaminterest")) {
135  alienTeamGroup_t* group;
136 
137  token = cgi->Com_EParse(text, errhead, name);
138  if (!*text || *token != '{') {
139  cgi->Com_Printf("CP_ParseAlienTeam: alien team \"%s\" has team with no opening brace\n", name);
140  break;
141  }
142 
143  if (alienCategory->numAlienTeamGroups >= MAX_ALIEN_GROUP_PER_CATEGORY) {
144  cgi->Com_Printf("CP_ParseAlienTeam: maximum number of alien team reached (%i) in category \"%s\"\n", MAX_ALIEN_GROUP_PER_CATEGORY, name);
145  break;
146  }
147 
148  group = &alienCategory->alienTeamGroups[alienCategory->numAlienTeamGroups];
149  group->idx = alienCategory->numAlienTeamGroups;
150  group->categoryIdx = alienCategory - ccs.alienCategories;
151  alienCategory->numAlienTeamGroups++;
152 
153  do {
154  token = cgi->Com_EParse(text, errhead, name);
155 
156  if (!cgi->Com_ParseBlockToken(name, text, group, alien_group_vals, cp_campaignPool, token)) {
157  if (!*text || *token == '}')
158  break;
159 
160  if (Q_streq(token, "team")) {
161  linkedList_t* list;
162  if (!cgi->Com_ParseList(text, &list)) {
163  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: \"%s\" Error while parsing team list", name);
164  }
165  for (linkedList_t* element = list; element != nullptr; element = element->next) {
166  if (group->numAlienTeams >= MAX_TEAMS_PER_MISSION)
167  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: MAX_TEAMS_PER_MISSION hit");
168  const teamDef_t* teamDef = cgi->Com_GetTeamDefinitionByID(strtok((char*)element->data, "/"));
169  if (teamDef) {
170  group->alienTeams[group->numAlienTeams] = teamDef;
171  const chrTemplate_t* chrTemplate = CHRSH_GetTemplateByID(teamDef, strtok(nullptr, ""));
172  group->alienChrTemplates[group->numAlienTeams] = chrTemplate;
173  ++group->numAlienTeams;
174  }
175  }
176  cgi->LIST_Delete(&list);
177  } else {
178  cgi->Com_Error(ERR_DROP, "CL_ParseAlienTeam: Unknown token \"%s\"\n", token);
179  }
180  }
181  } while (*text);
182 
183  if (group->minAlienCount > group->maxAlienCount) {
184  cgi->Com_Printf("CP_ParseAlienTeam: Minimum number of aliens is greater than maximum value! Swapped.\n");
185  const int swap = group->minAlienCount;
186  group->minAlienCount = group->maxAlienCount;
187  group->maxAlienCount = swap;
188  }
189  } else {
190  cgi->Com_Printf("CP_ParseAlienTeam: unknown token \"%s\" ignored (category %s)\n", token, name);
191  continue;
192  }
193  } while (*text);
194 
195  if (cgi->LIST_IsEmpty(alienCategory->equipment))
196  Sys_Error("alien category equipment list is empty");
197 }
198 
202 static void CP_ParseResearchedCampaignItems (const campaign_t* campaign, const char* name, const char** text)
203 {
204  const char* errhead = "CP_ParseResearchedCampaignItems: unexpected end of file (equipment ";
205  const char* token;
206  int i;
207 
208  /* Don't parse if it is not definition for current type of campaign. */
209  if (!Q_streq(campaign->researched, name))
210  return;
211 
212  /* get it's body */
213  token = Com_Parse(text);
214 
215  if (!*text || *token != '{') {
216  cgi->Com_Printf("CP_ParseResearchedCampaignItems: equipment def \"%s\" without body ignored (%s)\n",
217  name, token);
218  return;
219  }
220 
221  cgi->Com_DPrintf(DEBUG_CLIENT, "..campaign research list '%s'\n", name);
222  do {
223  token = cgi->Com_EParse(text, errhead, name);
224  if (!*text || *token == '}')
225  return;
226 
227  for (i = 0; i < ccs.numTechnologies; i++) {
228  technology_t* tech = RS_GetTechByIDX(i);
229  assert(tech);
230  if (Q_streq(token, tech->id)) {
231  tech->mailSent = MAILSENT_FINISHED;
235  cgi->Com_DPrintf(DEBUG_CLIENT, "...tech %s\n", tech->id);
236  break;
237  }
238  }
239 
240  if (i == ccs.numTechnologies)
241  cgi->Com_Printf("CP_ParseResearchedCampaignItems: unknown token \"%s\" ignored (tech %s)\n", token, name);
242 
243  } while (*text);
244 }
245 
254 static void CP_ParseResearchableCampaignStates (const campaign_t* campaign, const char* name, const char** text, bool researchable)
255 {
256  const char* errhead = "CP_ParseResearchableCampaignStates: unexpected end of file (equipment ";
257  const char* token;
258  int i;
259 
260  /* get it's body */
261  token = Com_Parse(text);
262 
263  if (!*text || *token != '{') {
264  cgi->Com_Printf("CP_ParseResearchableCampaignStates: equipment def \"%s\" without body ignored\n", name);
265  return;
266  }
267 
268  if (!Q_streq(campaign->researched, name)) {
269  cgi->Com_DPrintf(DEBUG_CLIENT, "..don't use '%s' as researchable list\n", name);
270  return;
271  }
272 
273  cgi->Com_DPrintf(DEBUG_CLIENT, "..campaign researchable list '%s'\n", name);
274  do {
275  token = cgi->Com_EParse(text, errhead, name);
276  if (!*text || *token == '}')
277  return;
278 
279  for (i = 0; i < ccs.numTechnologies; i++) {
280  technology_t* tech = RS_GetTechByIDX(i);
281  if (Q_streq(token, tech->id)) {
282  if (researchable) {
283  tech->mailSent = MAILSENT_PROPOSAL;
285  } else {
287  }
288  cgi->Com_DPrintf(DEBUG_CLIENT, "...tech %s\n", tech->id);
289  break;
290  }
291  }
292 
293  if (i == ccs.numTechnologies)
294  cgi->Com_Printf("CP_ParseResearchableCampaignStates: unknown token \"%s\" ignored (tech %s)\n", token, name);
295 
296  } while (*text);
297 }
298 
299 /* =========================================================== */
300 
301 static const value_t salary_vals[] = {
302  {"soldier_base", V_INT, offsetof(salary_t, base[EMPL_SOLDIER]), MEMBER_SIZEOF(salary_t, base[EMPL_SOLDIER])},
303  {"soldier_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_SOLDIER]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_SOLDIER])},
304  {"worker_base", V_INT, offsetof(salary_t, base[EMPL_WORKER]), MEMBER_SIZEOF(salary_t, base[EMPL_WORKER])},
305  {"worker_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_WORKER]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_WORKER])},
306  {"scientist_base", V_INT, offsetof(salary_t, base[EMPL_SCIENTIST]), MEMBER_SIZEOF(salary_t, base[EMPL_SCIENTIST])},
307  {"scientist_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_SCIENTIST]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_SCIENTIST])},
308  {"pilot_base", V_INT, offsetof(salary_t, base[EMPL_PILOT]), MEMBER_SIZEOF(salary_t, base[EMPL_PILOT])},
309  {"pilot_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_PILOT]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_PILOT])},
310  {"robot_base", V_INT, offsetof(salary_t, base[EMPL_ROBOT]), MEMBER_SIZEOF(salary_t, base[EMPL_ROBOT])},
311  {"robot_rankbonus", V_INT, offsetof(salary_t, rankBonus[EMPL_ROBOT]), MEMBER_SIZEOF(salary_t, rankBonus[EMPL_ROBOT])},
312  {"aircraft_factor", V_INT, offsetof(salary_t, aircraftFactor), MEMBER_SIZEOF(salary_t, aircraftFactor)},
313  {"aircraft_divisor", V_INT, offsetof(salary_t, aircraftDivisor), MEMBER_SIZEOF(salary_t, aircraftDivisor)},
314  {"base_upkeep", V_INT, offsetof(salary_t, baseUpkeep), MEMBER_SIZEOF(salary_t, baseUpkeep)},
315  {"debt_interest", V_FLOAT, offsetof(salary_t, debtInterest), MEMBER_SIZEOF(salary_t, debtInterest)},
316  {nullptr, V_NULL, 0, 0}
317 };
318 
329 static void CP_ParseSalary (const char* name, const char** text, salary_t* s)
330 {
331  cgi->Com_ParseBlock(name, text, s, salary_vals, cp_campaignPool);
332 }
333 
334 /* =========================================================== */
335 
336 static const value_t campaign_vals[] = {
337  {"default", V_BOOL, offsetof(campaign_t, defaultCampaign), MEMBER_SIZEOF(campaign_t, defaultCampaign)},
338  {"team", V_TEAM, offsetof(campaign_t, team), MEMBER_SIZEOF(campaign_t, team)},
339  {"soldiers", V_INT, offsetof(campaign_t, soldiers), MEMBER_SIZEOF(campaign_t, soldiers)},
340  {"workers", V_INT, offsetof(campaign_t, workers), MEMBER_SIZEOF(campaign_t, workers)},
341  {"xvirate", V_INT, offsetof(campaign_t, maxAllowedXVIRateUntilLost), MEMBER_SIZEOF(campaign_t, maxAllowedXVIRateUntilLost)},
342  {"maxdebts", V_INT, offsetof(campaign_t, negativeCreditsUntilLost), MEMBER_SIZEOF(campaign_t, negativeCreditsUntilLost)},
343  {"minhappiness", V_FLOAT, offsetof(campaign_t, minhappiness), MEMBER_SIZEOF(campaign_t, minhappiness)},
344  {"scientists", V_INT, offsetof(campaign_t, scientists), MEMBER_SIZEOF(campaign_t, scientists)},
345  {"pilots", V_INT, offsetof(campaign_t, pilots), MEMBER_SIZEOF(campaign_t, pilots)},
346  {"ugvs", V_INT, offsetof(campaign_t, ugvs), MEMBER_SIZEOF(campaign_t, ugvs)},
347  {"equipment", V_STRING, offsetof(campaign_t, equipment), 0},
348  {"soldierequipment", V_STRING, offsetof(campaign_t, soldierEquipment), 0},
349  {"market", V_STRING, offsetof(campaign_t, market), 0},
350  {"asymptotic_market", V_STRING, offsetof(campaign_t, asymptoticMarket), 0},
351  {"researched", V_STRING, offsetof(campaign_t, researched), 0},
352  {"difficulty", V_INT, offsetof(campaign_t, difficulty), MEMBER_SIZEOF(campaign_t, difficulty)},
353  {"map", V_STRING, offsetof(campaign_t, map), 0},
354  {"credits", V_INT, offsetof(campaign_t, credits), MEMBER_SIZEOF(campaign_t, credits)},
355  {"visible", V_BOOL, offsetof(campaign_t, visible), MEMBER_SIZEOF(campaign_t, visible)},
356  {"text", V_TRANSLATION_STRING, offsetof(campaign_t, text), 0}, /* just a gettext placeholder */
357  {"name", V_TRANSLATION_STRING, offsetof(campaign_t, name), 0},
358  {"date", V_DATE, offsetof(campaign_t, date), 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 {
457  cgi->Com_Printf("CP_ParseCampaign: unknown token \"%s\" ignored (campaign %s)\n", token, name);
458  cgi->Com_EParse(text, errhead, name);
459  }
460  } while (*text);
461 
462  if (cp->difficulty < -4)
463  cp->difficulty = -4;
464  else if (cp->difficulty > 4)
465  cp->difficulty = 4;
466 
467  /* checking for undefined values */
468  for (i = 0; i < MAX_EMPL; i++) {
469  if (s->base[i] == -1 || s->rankBonus[i] == -1) {
470  drop = true;
471  break;
472  }
473  }
474  if (drop || s->aircraftFactor == -1 || s->aircraftDivisor == -1 || s->baseUpkeep == -1
475  || s->debtInterest == -1) {
476  cgi->Com_Printf("CP_ParseCampaign: check salary definition. Campaign def \"%s\" ignored\n", name);
477  ccs.numCampaigns--;
478  return;
479  }
480 }
481 
490 static void CP_ParseScriptFirst (const char* type, const char* name, const char** text)
491 {
492  /* check for client interpretable scripts */
493  if (Q_streq(type, "up_chapter"))
494  UP_ParseChapter(name, text);
495  else if (Q_streq(type, "building"))
496  B_ParseBuildings(name, text, false);
497  else if (Q_streq(type, "installation"))
498  INS_ParseInstallations(name, text);
499  else if (Q_streq(type, "tech"))
500  RS_ParseTechnologies(name, text);
501  else if (Q_streq(type, "nation"))
502  CL_ParseNations(name, text);
503  else if (Q_streq(type, "city"))
504  CITY_Parse(name, text);
505  else if (Q_streq(type, "rank"))
506  CL_ParseRanks(name, text);
507  else if (Q_streq(type, "aircraft"))
508  AIR_ParseAircraft(name, text, false);
509  else if (Q_streq(type, "mail"))
510  CL_ParseEventMails(name, text);
511  else if (Q_streq(type, "events"))
512  CL_ParseCampaignEvents(name, text);
513  else if (Q_streq(type, "event"))
514  CP_ParseEventTrigger(name, text);
515  else if (Q_streq(type, "components"))
516  COMP_ParseComponents(name, text);
517  else if (Q_streq(type, "alienteam"))
518  CP_ParseAlienTeam(name, text);
519  else if (Q_streq(type, "msgoptions"))
520  MSO_ParseMessageSettings(name, text);
521 }
522 
534 static void CP_ParseScriptSecond (const char* type, const char* name, const char** text)
535 {
536  /* check for client interpretable scripts */
537  if (Q_streq(type, "building"))
538  B_ParseBuildings(name, text, true);
539  else if (Q_streq(type, "aircraft"))
540  AIR_ParseAircraft(name, text, true);
541  else if (Q_streq(type, "basetemplate"))
542  B_ParseBaseTemplate(name, text);
543  else if (Q_streq(type, "campaign"))
544  CP_ParseCampaign(name, text);
545 }
546 
550 static void CP_ParseScriptCampaignRelated (const campaign_t* campaign, const char* type, const char* name, const char** text)
551 {
552  if (Q_streq(type, "researched"))
553  CP_ParseResearchedCampaignItems(campaign, name, text);
554  else if (Q_streq(type, "researchable"))
555  CP_ParseResearchableCampaignStates(campaign, name, text, true);
556  else if (Q_streq(type, "notresearchable"))
557  CP_ParseResearchableCampaignStates(campaign, name, text, false);
558 }
559 
563 static bool CP_ItemsSanityCheck (void)
564 {
565  bool result = true;
566 
567  for (int i = 0; i < cgi->csi->numODs; i++) {
568  const objDef_t* item = INVSH_GetItemByIDX(i);
569 
570  /* Warn if item has no size set. */
571  if (item->size <= 0 && B_ItemIsStoredInBaseStorage(item)) {
572  result = false;
573  cgi->Com_Printf("CP_ItemsSanityCheck: Item %s has zero size set.\n", item->id);
574  }
575 
576  /* Warn if no price is set. */
577  if (item->price <= 0 && BS_IsOnMarket(item)) {
578  result = false;
579  cgi->Com_Printf("CP_ItemsSanityCheck: Item %s has zero price set.\n", item->id);
580  }
581 
582  if (item->price > 0 && !BS_IsOnMarket(item) && !PR_ItemIsProduceable(item)) {
583  result = false;
584  cgi->Com_Printf("CP_ItemsSanityCheck: Item %s has a price set though it is neither available on the market and production.\n", item->id);
585  }
586  }
587 
588  return result;
589 }
590 
592 typedef struct {
593  bool (*check)(void);
594  const char* name;
596 
599  {B_BuildingScriptSanityCheck, "buildings"},
600  {RS_ScriptSanityCheck, "tech"},
601  {AIR_ScriptSanityCheck, "aircraft"},
602  {CP_ItemsSanityCheck, "items"},
603  {NAT_ScriptSanityCheck, "nations"},
604 
605  {nullptr, nullptr}
606 };
607 
613 {
614  const sanity_functions_t* s;
615 
616  cgi->Com_Printf("Sanity check for script data\n");
617  s = sanity_functions;
618  while (s->check) {
619  bool status = s->check();
620  cgi->Com_Printf("...%s %s\n", s->name, (status ? "ok" : "failed"));
621  s++;
622  }
623 }
624 
631 {
632  const char* type, *name, *text;
633  int i;
634  campaign_t* campaign;
635 
636  /* pre-stage parsing */
637  cgi->FS_BuildFileList("ufos/*.ufo");
638  cgi->FS_NextScriptHeader(nullptr, nullptr, nullptr);
639  text = nullptr;
640 
641  while ((type = cgi->FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr)
642  CP_ParseScriptFirst(type, name, &text);
643 
644  /* fill in IDXs for required research techs */
646 
647  /* stage two parsing */
648  cgi->FS_NextScriptHeader(nullptr, nullptr, nullptr);
649  text = nullptr;
650 
651  cgi->Com_DPrintf(DEBUG_CLIENT, "Second stage parsing started...\n");
652  while ((type = cgi->FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr)
653  CP_ParseScriptSecond(type, name, &text);
655 
656  for (i = 0; i < cgi->csi->numTeamDefs; i++) {
657  const teamDef_t* teamDef = &cgi->csi->teamDef[i];
658  if (!CHRSH_IsTeamDefAlien(teamDef))
659  continue;
660 
661  ccs.teamDefTechs[teamDef->idx] = RS_GetTechByID(teamDef->tech);
662  if (ccs.teamDefTechs[teamDef->idx] == nullptr)
663  cgi->Com_Error(ERR_DROP, "Could not find a tech for teamdef %s", teamDef->id);
664  }
665 
666  for (i = 0, campaign = ccs.campaigns; i < ccs.numCampaigns; i++, campaign++) {
667  /* find the relevant markets */
668  campaign->marketDef = cgi->INV_GetEquipmentDefinitionByID(campaign->market);
670  }
671 
672  cgi->Com_Printf("Campaign data loaded - size " UFO_SIZE_T " bytes\n", sizeof(ccs));
673  cgi->Com_Printf("...techs: %i\n", ccs.numTechnologies);
674  cgi->Com_Printf("...buildings: %i\n", ccs.numBuildingTemplates);
675  cgi->Com_Printf("...ranks: %i\n", ccs.numRanks);
676  cgi->Com_Printf("...nations: %i\n", ccs.numNations);
677  cgi->Com_Printf("...cities: %i\n", ccs.numCities);
678  cgi->Com_Printf("\n");
679 }
680 
681 void CP_ReadCampaignData (const campaign_t* campaign)
682 {
683  const char* type, *name, *text;
684 
685  /* stage two parsing */
686  cgi->FS_NextScriptHeader(nullptr, nullptr, nullptr);
687  text = nullptr;
688 
689  cgi->Com_DPrintf(DEBUG_CLIENT, "Second stage parsing started...\n");
690  while ((type = cgi->FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != nullptr)
691  CP_ParseScriptCampaignRelated(campaign, type, name, &text);
692 
693  /* initialise date */
694  ccs.date = campaign->date;
695  /* get day */
696  while (ccs.date.sec > SECONDS_PER_DAY) {
698  ccs.date.day++;
699  }
700 }
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:202
int base[MAX_EMPL]
Definition: cp_campaign.h:154
alienTeamCategory_t alienCategories[ALIENCATEGORY_MAX]
Definition: cp_campaign.h:317
int price
Definition: inv_shared.h:332
void INS_LinkTechnologies(void)
const equipDef_t * marketDef
Definition: cp_campaign.h:174
const equipDef_t * asymptoticMarketDef
Definition: cp_campaign.h:175
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:550
void Sys_Error(const char *error,...)
Definition: g_main.cpp:421
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:254
static const value_t alien_group_vals[]
Definition: cp_parse.cpp:67
#define MAX_CAMPAIGNS
Definition: cp_campaign.h:31
Definition: scripts.h:71
alien team category definition
Definition: cp_campaign.h:124
QGL_EXTERN GLint GLenum type
Definition: r_gl.h:94
char id[MAX_VAR]
Definition: chr_shared.h:298
#define TEAM_PHALANX
Definition: q_shared.h:62
int numAlienCategories
Definition: cp_campaign.h:318
float debtInterest
Definition: cp_campaign.h:159
float liquidationRate
Definition: cp_campaign.h:199
static void CP_ParseScriptSecond(const char *type, const char *name, const char **text)
Parsing only for singleplayer.
Definition: cp_parse.cpp:534
static void CP_ParseAlienTeam(const char *name, const char **text)
Definition: cp_parse.cpp:78
void RS_MarkOneResearchable(technology_t *tech)
Marks one tech as researchable.
float researchRate
Definition: cp_campaign.h:197
static void CP_ParseCampaign(const char *name, const char **text)
Definition: cp_parse.cpp:378
char market[MAX_VAR]
Definition: cp_campaign.h:172
char tech[MAX_VAR]
Definition: chr_shared.h:300
int numBuildingTemplates
Definition: cp_campaign.h:339
int baseUpkeep
Definition: cp_campaign.h:158
char *IMPORT * FS_NextScriptHeader(const char *files, const char **name, const char **text)
int initialInterest
Definition: cp_campaign.h:207
char id[MAX_VAR]
Definition: cp_campaign.h:165
const teamDef_t * alienTeams[MAX_TEAMS_PER_MISSION]
Definition: cp_campaign.h:113
bool BS_IsOnMarket(const objDef_t *item)
Check if an item is on market.
Definition: cp_market.cpp:41
int numNations
Definition: cp_campaign.h:296
int numRanks
Definition: cp_campaign.h:369
csi_t * csi
Definition: cgame.h:100
bool(* check)(void)
Definition: cp_parse.cpp:593
int day
Definition: common.h:291
date_t date
Definition: cp_campaign.h:245
static const value_t campaign_vals[]
Definition: cp_parse.cpp:336
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:82
int numODs
Definition: q_shared.h:518
bool markOnly[MAX_CAMPAIGNS]
Definition: cp_research.h:91
linkedList_t * equipment
Definition: cp_campaign.h:130
Defines all attributes of objects used in the inventory.
Definition: inv_shared.h:264
alienTeamGroup_t alienTeamGroups[MAX_ALIEN_GROUP_PER_CATEGORY]
Definition: cp_campaign.h:132
linkedList_t * initialCraft
Definition: cp_campaign.h:209
memPool_t * cp_campaignPool
Definition: cp_campaign.cpp:61
int alienBaseInterest
Definition: cp_campaign.h:208
technology_t * teamDefTechs[MAX_TEAMDEFS]
Definition: cp_campaign.h:372
void CL_ParseCampaignEvents(const char *name, const char **text)
Definition: cp_event.cpp:449
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...
const chrTemplate_t * alienChrTemplates[MAX_TEAMS_PER_MISSION]
Definition: cp_campaign.h:115
char researched[MAX_VAR]
Definition: cp_campaign.h:169
#define ERR_DROP
Definition: common.h:211
#define DEBUG_CLIENT
Definition: defines.h:59
bool AIR_ScriptSanityCheck(void)
Checks the parsed aircraft for errors.
char * campaign[MAX_CAMPAIGNS]
Definition: cp_research.h:92
#define OBJZERO(obj)
Definition: shared.h:178
const equipDef_t *IMPORT * INV_GetEquipmentDefinitionByID(const char *name)
void CP_ParseEventTrigger(const char *name, const char **text)
Definition: cp_event.cpp:360
mailSentType_t mailSent
Definition: cp_research.h:178
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:612
bool RS_ScriptSanityCheck(void)
Checks the parsed tech data for errors.
markResearched_t markResearched
Definition: cp_research.h:187
void AIR_ParseAircraft(const char *name, const char **text, bool assignAircraftItems)
Parses all aircraft that are defined in our UFO-scripts.
int numCities
Definition: cp_campaign.h:300
const cgame_import_t * cgi
char asymptoticMarket[MAX_VAR]
Definition: cp_campaign.h:173
Campaign parsing header.
static bool CP_ItemsSanityCheck(void)
Make sure values of items after parsing are proper.
Definition: cp_parse.cpp:563
struct that holds the sanity check data
Definition: cp_parse.cpp:592
const chrTemplate_t * CHRSH_GetTemplateByID(const teamDef_t *teamDef, const char *templateId)
Definition: chr_shared.cpp:107
static const sanity_functions_t sanity_functions[]
Data for sanity check of parsed script data.
Definition: cp_parse.cpp:598
This is the technology parsed from research.ufo.
Definition: cp_research.h:137
campaign_t * CP_GetCampaign(const char *name)
Returns the campaign pointer from global campaign array.
int numTechnologies
Definition: cp_campaign.h:277
ccs_t ccs
Definition: cp_campaign.cpp:62
int aircraftDivisor
Definition: cp_campaign.h:157
const campaignEvents_t * CP_GetEventsByID(const char *name)
Definition: cp_event.cpp:138
teamDef_t teamDef[MAX_TEAMDEFS]
Definition: q_shared.h:548
Definition: scripts.h:68
alien team group definition.
Definition: cp_campaign.h:105
technology_t * RS_GetTechByID(const char *id)
return a pointer to the technology identified by given id string
int sec
Definition: common.h:292
int numCampaigns
Definition: cp_campaign.h:381
const GLuint *typedef void(APIENTRY *GenRenderbuffersEXT_t)(GLsizei
Definition: r_gl.h:189
date_t date
Definition: cp_campaign.h:190
static void CP_ParseSalary(const char *name, const char **text, salary_t *s)
Parse the salaries from campaign definition.
Definition: cp_parse.cpp:329
static interestCategory_t CP_GetAlienMissionTypeByID(const char *type)
Definition: cp_parse.cpp:37
float healingRate
Definition: cp_campaign.h:198
int rankBonus[MAX_EMPL]
Definition: cp_campaign.h:155
const char * Com_Parse(const char *data_p[], char *target, size_t size, bool replaceWhitespaces)
Parse a token out of a string.
Definition: parse.cpp:107
float ufoReductionRate
Definition: cp_campaign.h:203
Definition: scripts.h:49
int numTeamDefs
Definition: q_shared.h:549
enum interestCategory_s interestCategory_t
bool B_BuildingScriptSanityCheck(void)
Checks the parsed buildings for errors.
QGL_EXTERN GLint i
Definition: r_gl.h:113
void MSO_ParseMessageSettings(const char *name, const char **text)
parses message options settings from file.
Definition: scripts.h:50
signed int difficulty
Definition: cp_campaign.h:185
void RS_RequiredLinksAssign(void)
Assign Link pointers to all required techs/items/etc...
int minMissions
Definition: cp_campaign.h:201
#define INITIAL_OVERALL_INTEREST
Determines the interest interval for a single campaign.
Definition: cp_campaign.h:71
QGL_EXTERN GLuint GLsizei GLsizei GLint GLenum GLchar * name
Definition: r_gl.h:110
int size
Definition: inv_shared.h:334
int aircraftFactor
Definition: cp_campaign.h:156
int maxMissions
Definition: cp_campaign.h:202
#define MAX_TEAMS_PER_MISSION
Definition: inv_shared.h:618
#define SECONDS_PER_DAY
Definition: common.h:301
#define MEMBER_SIZEOF(TYPE, MEMBER)
Definition: scripts.h:34
#define UFO_SIZE_T
Definition: ufotypes.h:89
linkedList_t * next
Definition: list.h:32
Header file for single player campaign control.
void CP_ParseCampaignData(void)
Read the data for campaigns.
Definition: cp_parse.cpp:630
float employeeRate
Definition: cp_campaign.h:206
Definition: scripts.h:52
const objDef_t * INVSH_GetItemByIDX(int index)
Returns the item that belongs to the given index or nullptr if the index is invalid.
Definition: inv_shared.cpp:266
const teamDef_t *IMPORT * Com_GetTeamDefinitionByID(const char *team)
const char *IMPORT * Com_EParse(const char **text, const char *errhead, const char *errinfo)
static void CP_ParseScriptFirst(const char *type, const char *name, const char **text)
Parsing campaign data.
Definition: cp_parse.cpp:490
bool PR_ItemIsProduceable(const objDef_t *item)
check if an item is producable.
Definition: cp_produce.cpp:639
#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...
float produceRate
Definition: cp_campaign.h:196
char *IMPORT * PoolStrDup(const char *in, memPool_t *pool, const int tagNum)
const campaignEvents_t * events
Definition: cp_campaign.h:194
#define ALIENCATEGORY_MAX
Definition: cp_campaign.h:60
float componentRate
Definition: cp_campaign.h:200
void CL_ParseNations(const char *name, const char **text)
Parse the nation data from script file.
Definition: cp_nation.cpp:382
void CL_ParseEventMails(const char *name, const char **text)
Definition: cp_event.cpp:92
#define NON_OCCURRENCE_PROBABILITY
The probability that any new alien mission will be a non-occurrence mission.
Definition: cp_campaign.h:88
const char * name
Definition: cp_parse.cpp:594
char id[MAX_VAR]
Definition: cp_campaign.h:125
campaign_t campaigns[MAX_CAMPAIGNS]
Definition: cp_campaign.h:380
interestCategory_t missionCategories[INTERESTCATEGORY_MAX]
Definition: cp_campaign.h:126
const char * id
Definition: inv_shared.h:268
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:301
void CITY_Parse(const char *name, const char **text)
Parse the city data from script file.
Definition: cp_nation.cpp:445
salary_t salaries
Definition: cp_campaign.h:195
void CP_ReadCampaignData(const campaign_t *campaign)
Definition: cp_parse.cpp:681
bool B_ItemIsStoredInBaseStorage(const objDef_t *obj)
Check if an item is stored in storage.
Definition: cp_base.cpp:2558
#define MAX_ALIEN_GROUP_PER_CATEGORY
Definition: cp_campaign.h:58