/** * @brief Checks the parsed buildings for errors * @return false if there are errors - true otherwise */ bool B_BuildingScriptSanityCheck (void) { int i, error = 0; building_t* b; for (i = 0, b = ccs.buildingTemplates; i < ccs.numBuildingTemplates; i++, b++) { if (!b->name) { error++; Com_Printf("...... no name for building '%s' given\n", b->id); } if (!b->image) { error++; Com_Printf("...... no image for building '%s' given\n", b->id); } if (!b->pedia) { error++; Com_Printf("...... no pedia link for building '%s' given\n", b->id); } else if (!RS_GetTechByID(b->pedia)) { error++; Com_Printf("...... could not get pedia entry tech (%s) for building '%s'\n", b->pedia, b->id); } } return !error; }
/** * @brief Removes all scientists from the selected research-list entry. */ static void RS_Stop_f (void) { const base_t* base = B_GetCurrentSelectedBase(); if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <tech_id>\n", cgi->Cmd_Argv(0)); return; } technology_t* tech = RS_GetTechByID(cgi->Cmd_Argv(1)); if (!tech) { Com_Printf("RS_Stop_f: Invalid tech '%s'\n", cgi->Cmd_Argv(1)); return; } if (!tech->base) { return; } if (tech->base != base) { Com_Printf("RS_Stop_f: Tech '%s' is not researched in this base\n", cgi->Cmd_Argv(1)); return; } RS_StopResearch(tech); cgi->UI_ExecuteConfunc("ui_research_update_topic %s %d", tech->id, tech->scientists); cgi->UI_ExecuteConfunc("ui_research_update_caps %d %d %d %d", E_CountUnassigned(base, EMPL_SCIENTIST), E_CountHired(base, EMPL_SCIENTIST), CAP_GetFreeCapacity(base, CAP_LABSPACE), CAP_GetMax(base, CAP_LABSPACE)); }
/** * @brief Script function to add and remove a scientist to the technology entry in the research-list. */ static void RS_Change_f (void) { base_t* base = B_GetCurrentSelectedBase(); if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <tech_id>\n", cgi->Cmd_Argv(0)); return; } technology_t* tech = RS_GetTechByID(cgi->Cmd_Argv(1)); if (!tech) { Com_Printf("RS_ChangeScientist_f: Invalid tech '%s'\n", cgi->Cmd_Argv(1)); return; } if (tech->base && tech->base != base) { Com_Printf("RS_ChangeScientist_f: Tech '%s' is not researched in this base\n", cgi->Cmd_Argv(1)); return; } const int diff = atoi(cgi->Cmd_Argv(2)); if (diff == 0) return; if (diff > 0) { RS_AssignScientist(tech, base); } else if (tech->base) { RS_RemoveScientist(tech, nullptr); } cgi->UI_ExecuteConfunc("ui_research_update_topic %s %d", tech->id, tech->scientists); cgi->UI_ExecuteConfunc("ui_research_update_caps %d %d %d %d", E_CountUnassigned(base, EMPL_SCIENTIST), E_CountHired(base, EMPL_SCIENTIST), CAP_GetFreeCapacity(base, CAP_LABSPACE), CAP_GetMax(base, CAP_LABSPACE)); }
/** * @brief Search and open the UFOpaedia with given id */ static void UP_FindEntry_f (void) { const char* id; technology_t* tech; if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <id>\n", cgi->Cmd_Argv(0)); return; } /* what are we searching for? */ id = cgi->Cmd_Argv(1); /* maybe we get a call like 'ufopedia ""' */ if (id[0] == '\0') { Com_Printf("UP_FindEntry_f: No UFOpaedia entry given as parameter\n"); return; } tech = RS_GetTechByID(id); if (!tech) { Com_DPrintf(DEBUG_CLIENT, "UP_FindEntry_f: No UFOpaedia entry found for %s\n", id); return; } if (tech->redirect) tech = tech->redirect; UP_Article(tech, nullptr); }
/** * @note Mission trigger function * @sa CP_MissionTriggerFunctions * @sa CP_ExecuteMissionTrigger */ static void CP_AddTechAsResearchable_f (void) { if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <tech>\n", cgi->Cmd_Argv(0)); return; } const char* techID = cgi->Cmd_Argv(1); technology_t* tech = RS_GetTechByID(techID); RS_MarkOneResearchable(tech); }
/** * @brief Shows research image/model and title on the research screen */ static void RS_GetDetails_f (void) { if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <tech_id>\n", cgi->Cmd_Argv(0)); return; } const technology_t* tech = RS_GetTechByID(cgi->Cmd_Argv(1)); if (!tech) { Com_Printf("RS_GetDetails_f: Invalid tech '%s'\n", cgi->Cmd_Argv(1)); return; } cgi->UI_ExecuteConfunc("ui_research_details \"%s\" \"%s\" \"%s\"", _(tech->name), tech->image ? tech->image : "", tech->mdl ? tech->mdl : ""); }
/** * @brief Read the data for campaigns * @sa SAV_GameLoad * @sa CP_ResetCampaignData */ void CP_ParseCampaignData (void) { const char *type, *name, *text; int i; campaign_t *campaign; /* pre-stage parsing */ FS_BuildFileList("ufos/*.ufo"); FS_NextScriptHeader(NULL, NULL, NULL); text = NULL; while ((type = FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != NULL) CP_ParseScriptFirst(type, name, &text); /* fill in IDXs for required research techs */ RS_RequiredLinksAssign(); /* stage two parsing */ FS_NextScriptHeader(NULL, NULL, NULL); text = NULL; Com_DPrintf(DEBUG_CLIENT, "Second stage parsing started...\n"); while ((type = FS_NextScriptHeader("ufos/*.ufo", &name, &text)) != NULL) CP_ParseScriptSecond(type, name, &text); INS_LinkTechnologies(); for (i = 0; i < cgi->csi->numTeamDefs; i++) { const teamDef_t *teamDef = &cgi->csi->teamDef[i]; if (!CHRSH_IsTeamDefAlien(teamDef)) continue; ccs.teamDefTechs[teamDef->idx] = RS_GetTechByID(teamDef->tech); if (ccs.teamDefTechs[teamDef->idx] == NULL) cgi->Com_Error(ERR_DROP, "Could not find a tech for teamdef %s", teamDef->id); } for (i = 0, campaign = ccs.campaigns; i < ccs.numCampaigns; i++, campaign++) { /* find the relevant markets */ campaign->marketDef = INV_GetEquipmentDefinitionByID(campaign->market); campaign->asymptoticMarketDef = INV_GetEquipmentDefinitionByID(campaign->asymptoticMarket); } Com_Printf("Campaign data loaded - size " UFO_SIZE_T " bytes\n", sizeof(ccs)); Com_Printf("...techs: %i\n", ccs.numTechnologies); Com_Printf("...buildings: %i\n", ccs.numBuildingTemplates); Com_Printf("...ranks: %i\n", ccs.numRanks); Com_Printf("...nations: %i\n", ccs.numNations); Com_Printf("...cities: %i\n", ccs.numCities); Com_Printf("\n"); }
/** * Will return the campaign related events * @note Also performs some sanity check * @param name The events id */ const campaignEvents_t *CP_GetEventsByID (const char *name) { int i; for (i = 0; i < ccs.numCampaignEventDefinitions; i++) { const campaignEvents_t *events = &ccs.campaignEvents[i]; if (Q_streq(events->id, name)) { int j; for (j = 0; j < events->numCampaignEvents; j++) { const campaignEvent_t *event = &events->campaignEvents[j]; if (!RS_GetTechByID(event->tech)) Sys_Error("Illegal tech '%s' given in events '%s'", event->tech, events->id); } return events; } } return NULL; }
/** * @brief Returns if storing a specific life form is supported by the containment * @param[in] team Pointer to the alien Team Definition */ bool AlienContainment::isLifeSupported(const teamDef_t* team) { /* No team - not supported */ if (!team) return false; /* humans supported */ if (!CHRSH_IsTeamDefAlien(team)) return true; /* Robots are supported */ if (CHRSH_IsTeamDefRobot(team)) return true; /* Organic aliens need breathing apparatus known */ /** @todo find a way that doesn't need a tech ID hardcoded */ const technology_t* tech = RS_GetTechByID(BREATHINGAPPARATUS_TECH); if (!tech) return false; return RS_IsResearched_ptr(tech); }
/** * @brief Assign as many scientists to the research project as possible. */ static void RS_Max_f (void) { /* The base the tech is researched in. */ base_t* base = B_GetCurrentSelectedBase(); if (!base) return; if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <tech_id>\n", cgi->Cmd_Argv(0)); return; } /* The technology you want to max out. */ technology_t* tech = RS_GetTechByID(cgi->Cmd_Argv(1)); if (!tech) { Com_Printf("RS_Max_f: Invalid tech '%s'\n", cgi->Cmd_Argv(1)); return; } if (tech->base && tech->base != base) { Com_Printf("RS_Max_f: Tech '%s' is not researched in this base\n", cgi->Cmd_Argv(1)); return; } /* Add as many scientists as possible to this tech. */ while (CAP_GetFreeCapacity(base, CAP_LABSPACE) > 0) { Employee* employee = E_GetUnassignedEmployee(base, EMPL_SCIENTIST); if (!employee) break; RS_AssignScientist(tech, base, employee); if (!employee->isAssigned()) break; } cgi->UI_ExecuteConfunc("ui_research_update_topic %s %d", tech->id, tech->scientists); cgi->UI_ExecuteConfunc("ui_research_update_caps %d %d %d %d", E_CountUnassigned(base, EMPL_SCIENTIST), E_CountHired(base, EMPL_SCIENTIST), CAP_GetFreeCapacity(base, CAP_LABSPACE), CAP_GetMax(base, CAP_LABSPACE)); }
/** * @brief Fills the battery list, descriptions, and weapons in slots * of the basedefence equip menu */ static void BDEF_BaseDefenceMenuUpdate_f (void) { char type[MAX_VAR]; base_t* base = B_GetCurrentSelectedBase(); installation_t* installation = INS_GetCurrentSelectedInstallation(); aircraftItemType_t bdefType; linkedList_t* slotList = nullptr; const bool missileResearched = RS_IsResearched_ptr(RS_GetTechByID("rs_building_missile")); const bool laserResearched = RS_IsResearched_ptr(RS_GetTechByID("rs_building_laser")); if (cgi->Cmd_Argc() != 2) type[0] = '\0'; else Q_strncpyz(type, cgi->Cmd_Argv(1), sizeof(type)); /* don't let old links appear on this menu */ cgi->UI_ResetData(TEXT_BASEDEFENCE_LIST); cgi->UI_ResetData(TEXT_LIST); cgi->UI_ResetData(TEXT_ITEMDESCRIPTION); /* base or installation should not be nullptr because we are in the menu of this base or installation */ if (!base && !installation) return; /* base and installation should not both be set. This function requires one or the other set. */ if (base && installation) { Sys_Error("BDEF_BaseDefenceMenuUpdate_f: Both the base and installation are set"); return; } cgi->Cvar_Set("mn_target", _("None")); cgi->UI_ExecuteConfunc("setautofire disable"); if (installation) { /* Every slot aims the same target */ if (installation->numBatteries) { cgi->UI_ExecuteConfunc("setautofire %i", installation->batteries[0].autofire); if (installation->batteries[0].target) cgi->Cvar_Set("mn_target", "%s", UFO_GetName(installation->batteries[0].target)); } } else if (base) { bool autofire = false; /* Every slot aims the same target */ if (base->numBatteries) { autofire |= base->batteries[0].autofire; if (base->batteries[0].target) cgi->Cvar_Set("mn_target", "%s", UFO_GetName(base->batteries[0].target)); } if (base->numLasers) { autofire |= base->lasers[0].autofire; if (base->lasers[0].target && !base->batteries[0].target) cgi->Cvar_Set("mn_target", "%s", UFO_GetName(base->lasers[0].target)); } if (base->numBatteries || base->numLasers) cgi->UI_ExecuteConfunc("setautofire %i", autofire); } /* Check if we can change to laser or missile */ if (base) { cgi->UI_ExecuteConfunc("set_defencetypes %s %s", (!missileResearched) ? "na" : (base && base->numBatteries > 0) ? "enable" : "disable", (!laserResearched) ? "na" : (base && base->numLasers > 0) ? "enable" : "disable"); } else if (installation) { cgi->UI_ExecuteConfunc("set_defencetypes %s %s", (!missileResearched) ? "na" : (installation && installation->installationStatus == INSTALLATION_WORKING && installation->numBatteries > 0) ? "enable" : "disable", "na"); } if (Q_streq(type, "missile")) bdefType = AC_ITEM_BASE_MISSILE; else if (Q_streq(type, "laser")) bdefType = AC_ITEM_BASE_LASER; else /* info page */ return; /* Check that the base or installation has at least 1 battery */ if (base) { if (base->numBatteries + base->numLasers < 1) { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: there is no defence battery in this base: you shouldn't be in this function.\n"); return; } } else if (installation) { if (installation->installationStatus != INSTALLATION_WORKING) { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: installation isn't working: you shouldn't be in this function.\n"); return; } else if (installation->installationTemplate->maxBatteries < 1) { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: there is no defence battery in this installation: you shouldn't be in this function.\n"); return; } } if (installation) { /* we are in the installation defence menu */ if (installation->installationTemplate->maxBatteries == 0) { cgi->LIST_AddString(&slotList, _("No defence of this type in this installation")); } else { BDEF_FillSlotList(installation->batteries, installation->installationTemplate->maxBatteries, &slotList); } } else if (bdefType == AC_ITEM_BASE_MISSILE) { /* we are in the base defence menu for missile */ if (base->numBatteries == 0) { cgi->LIST_AddString(&slotList, _("No defence of this type in this base")); } else { BDEF_FillSlotList(base->batteries, base->numActiveBatteries, &slotList); } } else if (bdefType == AC_ITEM_BASE_LASER) { /* we are in the base defence menu for laser */ if (base->numLasers == 0) { cgi->LIST_AddString(&slotList, _("No defence of this type in this base")); } else { BDEF_FillSlotList(base->lasers, base->numActiveLasers, &slotList); } } else { Com_Printf("BDEF_BaseDefenceMenuUpdate_f: unknown bdefType.\n"); return; } cgi->UI_RegisterLinkedListText(TEXT_BASEDEFENCE_LIST, slotList); }
/** * @brief Puts alien cargo into Alien Containment. * @param[in] aircraft Aircraft transporting cargo to homebase. * @sa B_AircraftReturnedToHomeBase * @sa AL_FillInContainment * @note an event mail about missing breathing tech will be triggered if necessary. */ void AL_AddAliens (aircraft_t *aircraft) { base_t *toBase; const aliensTmp_t *cargo; int alienCargoTypes; int i; int j; qboolean limit = qfalse; qboolean messageAlreadySet = qfalse; technology_t *breathingTech; qboolean alienBreathing = qfalse; const objDef_t *alienBreathingObjDef; assert(aircraft); toBase = aircraft->homebase; assert(toBase); cargo = AL_GetAircraftAlienCargo(aircraft); alienCargoTypes = AL_GetAircraftAlienCargoTypes(aircraft); if (alienCargoTypes == 0) return; if (!B_GetBuildingStatus(toBase, B_ALIEN_CONTAINMENT)) { MS_AddNewMessage(_("Notice"), _("You cannot process aliens yet. Alien Containment not ready in this base."), qfalse, MSG_STANDARD, NULL); return; } breathingTech = RS_GetTechByID(BREATHINGAPPARATUS_TECH); if (!breathingTech) Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus tech definition"); alienBreathing = RS_IsResearched_ptr(breathingTech); alienBreathingObjDef = INVSH_GetItemByID(breathingTech->provides); if (!alienBreathingObjDef) Com_Error(ERR_DROP, "AL_AddAliens: Could not get breathing apparatus item definition"); for (i = 0; i < alienCargoTypes; i++) { for (j = 0; j < ccs.numAliensTD; j++) { assert(toBase->alienscont[j].teamDef); assert(cargo[i].teamDef); if (toBase->alienscont[j].teamDef == cargo[i].teamDef) { toBase->alienscont[j].amountDead += cargo[i].amountDead; /* Add breathing apparatuses to aircraft cargo so that they are processed with other collected items */ AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountDead); if (cargo[i].amountAlive <= 0) continue; if (!alienBreathing && !CHRSH_IsTeamDefRobot(cargo[i].teamDef)) { /* We can not store living (i.e. no robots or dead bodies) aliens without rs_alien_breathing tech */ toBase->alienscont[j].amountDead += cargo[i].amountAlive; /* Add breathing apparatuses as well */ AII_CollectItem(aircraft, alienBreathingObjDef, cargo[i].amountAlive); /* only once */ if (!messageAlreadySet) { MS_AddNewMessage(_("Notice"), _("You can't hold live aliens yet. Aliens died."), qfalse, MSG_DEATH, NULL); messageAlreadySet = qtrue; } if (!ccs.breathingMailSent) { Cmd_ExecuteString("addeventmail alienbreathing"); ccs.breathingMailSent = qtrue; } } else { int k; for (k = 0; k < cargo[i].amountAlive; k++) { /* Check base capacity. */ if (AL_CheckAliveFreeSpace(toBase, NULL, 1)) { AL_ChangeAliveAlienNumber(toBase, &(toBase->alienscont[j]), 1); } else { /* Every exceeding alien is killed * Display a message only when first one is killed */ if (!limit) { toBase->capacities[CAP_ALIENS].cur = toBase->capacities[CAP_ALIENS].max; MS_AddNewMessage(_("Notice"), _("You don't have enough space in Alien Containment. Some aliens got killed."), qfalse, MSG_STANDARD, NULL); limit = qtrue; } /* Just kill aliens which don't fit the limit. */ toBase->alienscont[j].amountDead++; AII_CollectItem(aircraft, alienBreathingObjDef, 1); } } /* only once */ if (!messageAlreadySet) { MS_AddNewMessage(_("Notice"), _("You've captured new aliens."), qfalse, MSG_STANDARD, NULL); messageAlreadySet = qtrue; } } break; } } } for (i = 0; i < ccs.numAliensTD; i++) { aliensCont_t *ac = &toBase->alienscont[i]; technology_t *tech = ac->tech; #ifdef DEBUG if (!tech) Sys_Error("AL_AddAliens: Failed to initialize the tech for '%s'\n", ac->teamDef->name); #endif /* we need this to let RS_Collected_ return true */ if (ac->amountAlive + ac->amountDead > 0) RS_MarkCollected(tech); #ifdef DEBUG /* print all of them */ if (ac->amountAlive > 0) Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens alive: %s amount: %i\n", ac->teamDef->name, ac->amountAlive); if (ac->amountDead > 0) Com_DPrintf(DEBUG_CLIENT, "AL_AddAliens bodies: %s amount: %i\n", ac->teamDef->name, ac->amountDead); #endif } /* we shouldn't have any more aliens on the aircraft after this */ AL_SetAircraftAlienCargoTypes(aircraft, 0); }
/** * @brief Load callback for messages * @param[in] p XML Node structure, where we get the information from * @sa MS_SaveXML * @sa UI_AddNewMessageSound */ bool MS_LoadXML (xmlNode_t* p) { int i; xmlNode_t* n, *sn; n = cgi->XML_GetNode(p, SAVE_MESSAGES_MESSAGES); if (!n) return false; /* we have to set this a little bit higher here, otherwise the samples that are played when adding * a message to the stack would all played a few milliseconds after each other - that doesn't sound * nice */ cgi->S_SetSampleRepeatRate(500); cgi->Com_RegisterConstList(saveMessageConstants); for (sn = cgi->XML_GetNode(n, SAVE_MESSAGES_MESSAGE), i = 0; sn; sn = cgi->XML_GetNextNode(sn, n, SAVE_MESSAGES_MESSAGE), i++) { eventMail_t* mail; const char* type = cgi->XML_GetString(sn, SAVE_MESSAGES_TYPE); int mtype; char title[MAX_VAR]; char text[MAX_MESSAGE_TEXT]; char id[MAX_VAR]; technology_t* tech = nullptr; uiMessageListNodeMessage_t* mess; if (!cgi->Com_GetConstIntFromNamespace(SAVE_MESSAGETYPE_NAMESPACE, type, (int*) &mtype)) { cgi->Com_Printf("Invalid message type '%s'\n", type); continue; } /* can contain high bits due to utf8 */ Q_strncpyz(title, cgi->XML_GetString(sn, SAVE_MESSAGES_TITLE), sizeof(title)); Q_strncpyz(text, cgi->XML_GetString(sn, SAVE_MESSAGES_TEXT), sizeof(text)); if (mtype == MSG_EVENT) { mail = CL_GetEventMail(cgi->XML_GetString(sn, SAVE_MESSAGES_EVENTMAILID)); if (mail) mail->read = cgi->XML_GetBool(sn, SAVE_MESSAGES_EVENTMAILREAD, false); } else mail = nullptr; /* event and not mail means, dynamic mail - we don't save or load them */ if (mtype == MSG_EVENT && !mail) continue; if (mtype == MSG_DEBUG && cgi->Cvar_GetInteger("developer") == 0) continue; Q_strncpyz(id, cgi->XML_GetString(sn, SAVE_MESSAGES_PEDIAID), sizeof(id)); if (id[0] != '\0') tech = RS_GetTechByID(id); if (!tech && (mtype == MSG_RESEARCH_PROPOSAL || mtype == MSG_RESEARCH_FINISHED)) { /** No tech found drop message. */ continue; } mess = MS_AddNewMessage(title, text, (messageType_t)mtype, tech, false, false); mess->eventMail = mail; cgi->XML_GetDate(sn, SAVE_MESSAGES_DATE, &mess->date.day, &mess->date.sec); /* redo timestamp text after setting date */ MS_TimestampedText(mess->timestamp, mess, sizeof(mess->timestamp)); if (mail) { dateLong_t date; char dateBuf[MAX_VAR] = ""; CP_DateConvertLong(&mess->date, &date); Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day); mail->date = cgi->PoolStrDup(dateBuf, cp_campaignPool, 0); } } cgi->Com_UnregisterConstList(saveMessageConstants); /* reset the sample repeat rate */ cgi->S_SetSampleRepeatRate(0); return true; }
void CP_XVIInit (void) { rsAlienXVI = RS_GetTechByID(XVI_EVENT_NAME); if (!rsAlienXVI) cgi->Com_Error(ERR_DROP, "CP_XVIInit: Could not find tech definition for " XVI_EVENT_NAME); }