/** * @brief Choose nation if needed for given mission. * @param[in] mission Pointer to the mission we are creating. * @param[out] nationList linkedList that will contain the name of the nation where the mission should take place. * @note nationList should be empty if no nation should be favoured. * @return True if nationList has been filled, false else. */ static bool CP_ChooseNation (const mission_t* mission, linkedList_t** nationList) { int randomNumber, max = 0; /* Increase this factor to make probability to select non-infected nation higher * Used to make sure that non-infected nation can still be attacked */ const int OFFSET = 1; int i; if (mission->ufo) return false; /* favour mission with higher XVI level */ for (i = 0; i < ccs.numNations; i++) { const nation_t* nation = NAT_GetNationByIDX(i); const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation); max += OFFSET + stats->xviInfection; } randomNumber = (int) (frand() * (float) max); /* Select the corresponding nation */ for (i = 0; i < ccs.numNations; i++) { const nation_t* nation = NAT_GetNationByIDX(i); const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation); randomNumber -= OFFSET + stats->xviInfection; if (randomNumber < 0) { cgi->LIST_AddString(nationList, nation->id); return true; } } return false; }
/** * @brief Function to initialize list to sell recovered UFO to desired nation. * @note Command to call this: cp_uforecovery_sell_init. */ static void UR_DialogInitSell_f (void) { int i; /* Do nothing if recovery process is finished. */ if (ufoRecovery.recoveryDone) return; /* Do nothing without a ufoTemplate set */ if (!ufoRecovery.ufoTemplate) return; for (i = 0; i < ccs.numNations; i++) { const nation_t *nation = NAT_GetNationByIDX(i); const nationInfo_t *stats = NAT_GetCurrentMonthInfo(nation); int price; price = (int) (ufoRecovery.ufoTemplate->price * (.85f + frand() * .3f)); /* Nation will pay less if corrupted */ price = (int) (price * exp(-stats->xviInfection / 20.0f)); ufoRecovery.ufoNations[i].nation = nation; ufoRecovery.ufoNations[i].price = price; } UR_SortNations(UR_GetSortFunctionByColumn(ufoRecovery.sortedColumn), ufoRecovery.sortDescending); UR_DialogFillNations(); cgi->UI_ExecuteConfunc("btnatsel disable"); }
/** * @brief Return the average XVI rate * @note XVI = eXtraterrestial Viral Infection */ int CP_GetAverageXVIRate (void) { assert(ccs.numNations); /* check for XVI infection rate */ int xviRate = 0; for (int i = 0; i < ccs.numNations; i++) { const nation_t* nation = NAT_GetNationByIDX(i); const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation); xviRate += stats->xviInfection; } xviRate /= ccs.numNations; return xviRate; }
/** * @brief Function to start UFO selling process. * @note Command to call this: cp_uforecovery_sell_start. */ static void UR_DialogStartSell_f (void) { int price = -1; const nation_t *nation; int i; if (!ufoRecovery.nation) return; nation = ufoRecovery.nation; i = UR_DialogGetCurrentNationIndex(); price = ufoRecovery.ufoNations[i].price; assert(price >= 0); #if 0 if (ufoRecovery.selectedStorage) { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Sold previously recovered %s from %s to nation %s, gained %i credits."), UFO_TypeToName( ufoRecovery.selectedStorage->ufoTemplate->ufotype), ufoRecovery.selectedStorage->base->name, _(nation->name), price); } else #endif { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("Recovered %s from the battlefield. UFO sold to nation %s, gained %i credits."), UFO_GetName(ufoRecovery.ufoTemplate), _(nation->name), price); } MS_AddNewMessage(_("UFO Recovery"), cp_messageBuffer); CP_UpdateCredits(ccs.credits + price); /* update nation happiness */ for (i = 0; i < ccs.numNations; i++) { nation_t *nat = NAT_GetNationByIDX(i); float ufoHappiness; assert(nat); if (nat == nation) /* nation is happy because it got the UFO */ ufoHappiness = HAPPINESS_UFO_SALE_GAIN; else /* nation is unhappy because it wanted the UFO */ ufoHappiness = HAPPINESS_UFO_SALE_LOSS; NAT_SetHappiness(ccs.curCampaign->minhappiness, nat, nat->stats[0].happiness + ufoHappiness); } /* UFO recovery process is done, disable buttons. */ UR_DialogRecoveryDone(); }
/** * @brief Return a nation-pointer by the nations id (nation_t->id) text. * @param[in] nationID nation id as defined in (nation_t->id) * @return nation_t pointer or NULL if nothing found (=error). */ nation_t *NAT_GetNationByID (const char *nationID) { int i; if (!nationID) { Com_Printf("NAT_GetNationByID: NULL nationID\n"); return NULL; } for (i = 0; i < ccs.numNations; i++) { nation_t *nation = NAT_GetNationByIDX(i); if (Q_streq(nation->id, nationID)) return nation; } Com_Printf("NAT_GetNationByID: Could not find nation '%s'\n", nationID); /* No matching nation found - ERROR */ return NULL; }
/** * @brief Recreates all the employees for a particular employee type in the global list. * But it does not overwrite any employees already hired. * @param[in] type The type of the employee list to process. * @param[in] excludeUnhappyNations True if a nation is unhappy then they wont * send any pilots, false if happiness of nations in not considered. * @sa CP_NationHandleBudget */ int E_RefreshUnhiredEmployeeGlobalList (const employeeType_t type, const qboolean excludeUnhappyNations) { const nation_t *happyNations[MAX_NATIONS]; int numHappyNations = 0; int idx, nationIdx, cnt; employee_t *employee; happyNations[0] = NULL; /* get a list of nations, if excludeHappyNations is qtrue then also exclude * unhappy nations (unhappy nation: happiness <= 0) from the list */ for (idx = 0; idx < ccs.numNations; idx++) { const nation_t *nation = NAT_GetNationByIDX(idx); const nationInfo_t *stats = NAT_GetCurrentMonthInfo(nation); if (stats->happiness > 0 || !excludeUnhappyNations) { happyNations[numHappyNations] = nation; numHappyNations++; } } if (!numHappyNations) return 0; idx = 0; /* Fill the global data employee list with employees, evenly distributed * between nations in the happyNations list */ E_Foreach(type, employee) { /* we don't want to overwrite employees that have already been hired */ if (!E_IsHired(employee)) { E_DeleteEmployee(employee); idx++; } } nationIdx = 0; cnt = 0; while (idx-- > 0) { if (E_CreateEmployee(type, happyNations[nationIdx], NULL) != NULL) cnt++; nationIdx = (nationIdx + 1) % numHappyNations; } return cnt; }
static int CP_CheckTriggerEvent (const char *expression, const void* userdata) { const char *type; /* check that a particular installation type is built already */ type = Q_strstart(expression, "installation"); if (type != 0) { if (strlen(type) <= 1) return -1; char value[MAX_VAR]; Q_strncpyz(value, type + 1, sizeof(value)); value[strlen(value) - 1] = '\0'; const installationType_t insType = INS_GetType(value); if (INS_HasType(insType, INSTALLATION_NOT_USED)) return 1; return 0; } /* check whether a particular ufo was detected */ type = Q_strstart(expression, "ufo"); if (type != 0) { if (strlen(type) <= 1) return -1; char value[MAX_VAR]; Q_strncpyz(value, type + 1, sizeof(value)); value[strlen(value) - 1] = '\0'; const char* detectedUFO = static_cast<const char*>(userdata); if (Q_strnull(detectedUFO)) return -1; return Q_streq(detectedUFO, value); } /* check that the given xvi level is reached in any nation */ type = Q_strstart(expression, "xvi"); if (type != 0) { int xvi; if (sscanf(type, "[%i]", &xvi) != 1) return -1; int i; /* check for XVI infection rate */ for (i = 0; i < ccs.numNations; i++) { const nation_t *nation = NAT_GetNationByIDX(i); const nationInfo_t *stats = NAT_GetCurrentMonthInfo(nation); if (stats->xviInfection >= xvi) return 1; } return 0; } /* check for nation happiness - also see the lost conditions in the campaign */ type = Q_strstart(expression, "nationhappiness"); if (type != 0) { int nationAmount; if (sscanf(type, "[%i]", &nationAmount) != 1) return -1; int j, nationBelowLimit = 0; for (j = 0; j < ccs.numNations; j++) { const nation_t *nation = NAT_GetNationByIDX(j); const nationInfo_t *stats = NAT_GetCurrentMonthInfo(nation); if (stats->happiness < ccs.curCampaign->minhappiness) { nationBelowLimit++; if (nationBelowLimit >= nationAmount) return 1; } } return 0; } /* check that the given average xvi level is reached */ type = Q_strstart(expression, "averagexvi"); if (type != 0) { int xvipercent; if (sscanf(type, "[%i]", &xvipercent) != 1) return -1; if (xvipercent < 0 || xvipercent > 100) return -1; const int xvi = CP_GetAverageXVIRate(); if (xvi > ccs.curCampaign->maxAllowedXVIRateUntilLost * xvipercent / 100) return 1; return 0; } type = Q_strstart(expression, "difficulty"); if (type != 0) { int difficulty; if (sscanf(type, "[%i]", &difficulty) != 1) return -1; return ccs.curCampaign->difficulty == difficulty; } /* check that these days have passed in the campaign */ type = Q_strstart(expression, "days"); if (type != 0) { int days; if (sscanf(type, "[%i]", &days) != 1) return -1; date_t d = ccs.curCampaign->date; d.day += days; if (Date_IsDue(&d)) return 1; return 0; } type = Q_strstart(expression, "alienscaptured"); if (type != 0) { if (ccs.campaignStats.capturedAliens > 0) return 1; return 0; } type = Q_strstart(expression, "samsitearmed"); if (type != 0) { if (!INS_HasType(INSTALLATION_DEFENCE)) return 1; INS_ForeachOfType(installation, INSTALLATION_DEFENCE) { if (installation->installationStatus == INSTALLATION_WORKING) { for (int i = 0; i < installation->installationTemplate->maxBatteries; i++) { const aircraftSlot_t *slot = &installation->batteries[i].slot; if (slot->ammoLeft > 0) return 1; } } } return 0; } return -1; }
/** * @brief Shows the current stats from stats_t stats * @todo This is very redundant with NAT_HandleBudget Ivestigate and clean up. */ void CP_StatsUpdate_f (void) { char *pos; static char statsBuffer[MAX_STATS_BUFFER]; int hired[MAX_EMPL]; int i, costs = 0, sum = 0, totalfunds = 0; base_t *base; const campaign_t *campaign = ccs.curCampaign; const salary_t *salary = &campaign->salaries; const rank_t *rank; /* delete buffer */ OBJZERO(statsBuffer); OBJZERO(hired); pos = statsBuffer; /* missions */ cgi->UI_RegisterText(TEXT_STATS_MISSION, pos); Com_sprintf(pos, MAX_STATS_BUFFER, _("Won:\t%i\nLost:\t%i\n\n"), ccs.campaignStats.missionsWon, ccs.campaignStats.missionsLost); /* bases */ pos += (strlen(pos) + 1); cgi->UI_RegisterText(TEXT_STATS_BASES, pos); Com_sprintf(pos, (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos), _("Built:\t%i\nActive:\t%i\nAttacked:\t%i\n"), ccs.campaignStats.basesBuilt, B_GetCount(), ccs.campaignStats.basesAttacked), /* installations */ pos += (strlen(pos) + 1); cgi->UI_RegisterText(TEXT_STATS_INSTALLATIONS, pos); INS_Foreach(inst) { Q_strcat(pos, va("%s\n", inst->name), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); } /* nations */ pos += (strlen(pos) + 1); cgi->UI_RegisterText(TEXT_STATS_NATIONS, pos); for (i = 0; i < ccs.numNations; i++) { const nation_t *nation = NAT_GetNationByIDX(i); Q_strcat(pos, va(_("%s\t%s\n"), _(nation->name), NAT_GetHappinessString(nation)), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); totalfunds += NAT_GetFunding(nation, 0); } Q_strcat(pos, va(_("\nFunding this month:\t%d"), totalfunds), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); /* costs */ for (i = 0; i < MAX_EMPL; i++) { E_Foreach(i, employee) { const employeeType_t type = (employeeType_t)i; if (!E_IsHired(employee)) continue; rank = CL_GetRankByIdx(employee->chr.score.rank); costs += CP_GetSalaryBaseEmployee(salary, type) + rank->level * CP_GetSalaryRankBonusEmployee(salary, type); hired[employee->type]++; } } /* employees - this is between the two costs parts to count the hired employees */ pos += (strlen(pos) + 1); cgi->UI_RegisterText(TEXT_STATS_EMPLOYEES, pos); for (i = 0; i < MAX_EMPL; i++) { const employeeType_t type = (employeeType_t)i; Q_strcat(pos, va(_("%s\t%i\n"), E_GetEmployeeString(type, hired[i]), hired[i]), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); } /* costs - second part */ pos += (strlen(pos) + 1); cgi->UI_RegisterText(TEXT_STATS_COSTS, pos); Q_strcat(pos, va(_("Employees:\t%i c\n"), costs), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); sum += costs; costs = 0; AIR_Foreach(aircraft) { if (aircraft->status == AIR_CRASHED) continue; costs += aircraft->price * salary->aircraftFactor / salary->aircraftDivisor; } Q_strcat(pos, va(_("Aircraft:\t%i c\n"), costs), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); sum += costs; base = NULL; while ((base = B_GetNext(base)) != NULL) { costs = CP_GetSalaryUpKeepBase(salary, base); Q_strcat(pos, va(_("Base (%s):\t%i c\n"), base->name, costs), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); sum += costs; } costs = CP_GetSalaryAdministrative(salary); Q_strcat(pos, va(_("Administrative costs:\t%i c\n"), costs), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); sum += costs; if (ccs.credits < 0) { const float interest = ccs.credits * campaign->salaries.debtInterest; costs = (int)ceil(interest); Q_strcat(pos, va(_("Debt:\t%i c\n"), costs), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); sum += costs; } Q_strcat(pos, va(_("\n\t-------\nSum:\t%i c\n"), sum), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); /* campaign */ pos += (strlen(pos) + 1); cgi->UI_RegisterText(TEXT_GENERIC, pos); Q_strcat(pos, va(_("Max. allowed debts: %ic\n"), campaign->negativeCreditsUntilLost), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); /* only show the xvi spread data when it's available */ if (CP_IsXVIVisible()) { Q_strcat(pos, va(_("Max. allowed eXtraterrestial Viral Infection: %i%%\n" "Current eXtraterrestial Viral Infection: %i%%"), campaign->maxAllowedXVIRateUntilLost, CP_GetAverageXVIRate()), (ptrdiff_t)(&statsBuffer[MAX_STATS_BUFFER] - pos)); } }
/** * @brief Hack to get a random nation for the initial */ static inline const nation_t *E_RandomNation (void) { const int nationIndex = rand() % ccs.numNations; return NAT_GetNationByIDX(nationIndex); }
/** * @brief Update xviInfection value for each nation, using the XVI overlay. * @note should be executed after all daily event that could change XVI overlay */ void CP_UpdateNationXVIInfection (void) { /* No need to update XVI levels if the overlay didn't change */ if (!xviNationInfectionNeedsUpdate) return; /* width in pixel of the XVI overlay */ int width; /* height in pixel of the XVI overlay */ int height; CP_GetXVIMapDimensions(&width, &height); const float heightPerDegree = height / 180.0f; const float widthPerDegree = width / 360.0f; /* parameter used to normalize nation XVI level. * decrease this factor to increase XVI level per nation */ const float AREA_FACTOR = 650.0f; /* area used to normalized XVI infection level for each nation. * depend on overlay size so that if we change resolution of * overlay it doesn't impact nation XIInfection */ const float normalizingArea = width * height / AREA_FACTOR; /* temporary array to store the XVI levels */ float xviInfection[MAX_NATIONS]; /* Initialize array */ OBJZERO(xviInfection); for (int y = 0; y < height; y++) { int sum[MAX_NATIONS]; const byte* previousNationColor; const nation_t* nation; /* current position (in latitude / longitude) */ vec2_t currentPos; OBJZERO(sum); Vector2Set(currentPos, 180.0f, 90.0f - y / heightPerDegree); previousNationColor = GEO_GetColor(currentPos, MAPTYPE_NATIONS, nullptr); nation = GEO_GetNation(currentPos); for (int x = 0; x < width; x++) { const byte* nationColor; currentPos[0] = 180.0f - x / widthPerDegree; nationColor = GEO_GetColor(currentPos, MAPTYPE_NATIONS, nullptr); if (!VectorCompare(nationColor, previousNationColor)) { previousNationColor = nationColor; nation = GEO_GetNation(currentPos); } if (nation) { const int xviLevel = CP_GetXVILevel(x, y); if (xviLevel > 0) sum[nation->idx] += xviLevel; } } /* divide the total XVI infection by the area of a pixel * because pixel are smaller as you go closer from the pole */ for (int nationIdx = 0; nationIdx < ccs.numNations; nationIdx++) xviInfection[nationIdx] += ((float) sum[nationIdx]) / (cos(torad * currentPos[1]) * normalizingArea); } /* copy the new values of XVI infection level into nation array */ for (int nationIdx = 0; nationIdx < ccs.numNations; nationIdx++) { nation_t* nation = NAT_GetNationByIDX(nationIdx); nation->stats[0].xviInfection = ceil(xviInfection[nation->idx]); } xviNationInfectionNeedsUpdate = false; }