/** * @brief Debug command to list all hired employee */ static void E_ListHired_f (void) { employeeType_t emplType; for (emplType = 0; emplType < MAX_EMPL; emplType++) { employee_t *employee; E_Foreach(emplType, employee) { Com_Printf("Employee: %s (ucn: %i) %s at %s\n", E_GetEmployeeString(employee->type), employee->chr.ucn, employee->chr.name, employee->baseHired->name); if (employee->type != emplType) Com_Printf("Warning: EmployeeType mismatch: %i != %i\n", emplType, employee->type); } }
/** * @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 Base Summary menu init function. * @note Should be called whenever the Base Summary menu gets active. */ static void BaseSummary_Init (const base_t *base) { static char textStatsBuffer[1024]; static char textInfoBuffer[256]; const aliensCont_t *containment = base->alienscont; int i; baseCapacities_t cap; const production_queue_t *queue; const technology_t *tech; int tmp; /* wipe away old buffers */ textStatsBuffer[0] = textInfoBuffer[0] = 0; Q_strcat(textInfoBuffer, _("^BAircraft\n"), sizeof(textInfoBuffer)); for (i = 0; i <= MAX_HUMAN_AIRCRAFT_TYPE; i++) { const aircraftType_t airType = (aircraftType_t)i; const int count = AIR_CountTypeInBase(base, airType); if (count == 0) continue; Q_strcat(textInfoBuffer, va("\t%s:\t\t\t\t%i\n", AIR_GetAircraftString(airType), count), sizeof(textInfoBuffer)); } Q_strcat(textInfoBuffer, "\n", sizeof(textInfoBuffer)); Q_strcat(textInfoBuffer, _("^BEmployees\n"), sizeof(textInfoBuffer)); for (i = 0; i < MAX_EMPL; i++) { const employeeType_t emplType = (employeeType_t)i; tmp = E_CountHired(base, emplType); if (tmp == 0) continue; Q_strcat(textInfoBuffer, va("\t%s:\t\t\t\t%i\n", E_GetEmployeeString(emplType, tmp), tmp), sizeof(textInfoBuffer)); } Q_strcat(textInfoBuffer, "\n", sizeof(textInfoBuffer)); Q_strcat(textInfoBuffer, _("^BAliens\n"), sizeof(textInfoBuffer)); for (i = 0; i < ccs.numAliensTD; i++) { if (!containment[i].amountAlive && !containment[i].amountDead) continue; Q_strcat(textInfoBuffer, va("\t%s:\t\t\t\t%i/%i\n", _(containment[i].teamDef->name), containment[i].amountAlive, containment[i].amountDead), sizeof(textInfoBuffer)); } /* link into the menu */ cgi->UI_RegisterText(TEXT_STANDARD, textInfoBuffer); Q_strcat(textStatsBuffer, _("^BBuildings\t\t\t\t\t\tCapacity\t\t\t\tAmount\n"), sizeof(textStatsBuffer)); for (i = 0; i < ccs.numBuildingTemplates; i++) { const building_t* b = &ccs.buildingTemplates[i]; /* only show already researched buildings */ if (!RS_IsResearched_ptr(b->tech)) continue; cap = B_GetCapacityFromBuildingType(b->buildingType); if (cap == MAX_CAP) continue; if (!B_GetNumberOfBuildingsInBaseByBuildingType(base, b->buildingType)) continue; /* Check if building is functional (see comments in B_UpdateBaseCapacities) */ if (B_GetBuildingStatus(base, b->buildingType)) { Q_strcat(textStatsBuffer, va("%s:\t\t\t\t\t\t%i/%i", _(b->name), CAP_GetCurrent(base, cap), CAP_GetMax(base, cap)), sizeof(textStatsBuffer)); } else { if (b->buildingStatus == B_STATUS_UNDER_CONSTRUCTION) { const float remaining = B_GetConstructionTimeRemain(b); const float timeLeft = std::max(0.0f, remaining); Q_strcat(textStatsBuffer, va("%s:\t\t\t\t\t\t%3.1f %s", _(b->name), timeLeft, ngettext("day", "days", timeLeft)), sizeof(textStatsBuffer)); } else { Q_strcat(textStatsBuffer, va("%s:\t\t\t\t\t\t%i/%i", _(b->name), CAP_GetCurrent(base, cap), 0), sizeof(textStatsBuffer)); } } Q_strcat(textStatsBuffer, va("\t\t\t\t%i\n", B_GetNumberOfBuildingsInBaseByBuildingType(base, b->buildingType)), sizeof(textStatsBuffer)); } Q_strcat(textStatsBuffer, "\n", sizeof(textStatsBuffer)); Q_strcat(textStatsBuffer, _("^BProduction\t\t\t\t\t\tQuantity\t\t\t\tPercent\n"), sizeof(textStatsBuffer)); queue = PR_GetProductionForBase(base); if (queue->numItems > 0) { for (i = 0; i < queue->numItems; i++) { const production_t *production = &queue->items[i]; const char *name = PR_GetName(&production->data); /** @todo use the same method as we do in PR_ProductionInfo */ Q_strcat(textStatsBuffer, va(_("%s\t\t\t\t\t\t%d\t\t\t\t%.2f%%\n"), name, production->amount, PR_GetProgress(production) * 100), sizeof(textStatsBuffer)); } } else { Q_strcat(textStatsBuffer, _("Nothing\n"), sizeof(textStatsBuffer)); } Q_strcat(textStatsBuffer, "\n", sizeof(textStatsBuffer)); Q_strcat(textStatsBuffer, _("^BResearch\t\t\t\t\t\tScientists\t\t\t\tPercent\n"), sizeof(textStatsBuffer)); tmp = 0; for (i = 0; i < ccs.numTechnologies; i++) { tech = RS_GetTechByIDX(i); if (tech->base == base && (tech->statusResearch == RS_RUNNING || tech->statusResearch == RS_PAUSED)) { Q_strcat(textStatsBuffer, va(_("%s\t\t\t\t\t\t%d\t\t\t\t%1.2f%%\n"), _(tech->name), tech->scientists, (1 - tech->time / tech->overallTime) * 100), sizeof(textStatsBuffer)); tmp++; } } if (!tmp) Q_strcat(textStatsBuffer, _("Nothing\n"), sizeof(textStatsBuffer)); /* link into the menu */ cgi->UI_RegisterText(TEXT_STATS_BASESUMMARY, textStatsBuffer); }
/** * @brief Checks why a button in base menu is disabled, and create a popup to inform player */ static void B_CheckBuildingStatusForMenu_f (void) { int num; const char *buildingID; const building_t *building; const base_t *base = B_GetCurrentSelectedBase(); if (cgi->Cmd_Argc() != 2) { Com_Printf("Usage: %s <buildingID>\n", cgi->Cmd_Argv(0)); return; } buildingID = cgi->Cmd_Argv(1); building = B_GetBuildingTemplate(buildingID); if (!building || !base) return; /* Maybe base is under attack ? */ if (B_IsUnderAttack(base)) { CP_Popup(_("Notice"), _("Base is under attack, you can't access this building !")); return; } if (building->buildingType == B_HANGAR) { /* this is an exception because you must have a small or large hangar to enter aircraft menu */ CP_Popup(_("Notice"), _("You need at least one Hangar (and its dependencies) to use aircraft.")); return; } num = B_GetNumberOfBuildingsInBaseByBuildingType(base, building->buildingType); if (num > 0) { int numUnderConstruction; /* maybe all buildings of this type are under construction ? */ B_CheckBuildingTypeStatus(base, building->buildingType, B_STATUS_UNDER_CONSTRUCTION, &numUnderConstruction); if (numUnderConstruction == num) { int minDay = 99999; building_t *b = NULL; while ((b = B_GetNextBuildingByType(base, b, building->buildingType))) { if (b->buildingStatus == B_STATUS_UNDER_CONSTRUCTION) { const float remaining = B_GetConstructionTimeRemain(b); minDay = std::min(minDay, (int)std::max(0.0f, remaining)); } } CP_Popup(_("Notice"), ngettext("Construction of building will be over in %i day.\nPlease wait to enter.", "Construction of building will be over in %i days.\nPlease wait to enter.", minDay), minDay); return; } if (!B_CheckBuildingDependencesStatus(building)) { const building_t *dependenceBuilding = building->dependsBuilding; assert(building->dependsBuilding); if (B_GetNumberOfBuildingsInBaseByBuildingType(base, dependenceBuilding->buildingType) <= 0) { /* the dependence of the building is not built */ CP_Popup(_("Notice"), _("You need a building %s to make building %s functional."), _(dependenceBuilding->name), _(building->name)); return; } else { /* maybe the dependence of the building is under construction * note that we can't use B_STATUS_UNDER_CONSTRUCTION here, because this value * is not use for every building (for exemple Command Centre) */ building_t *b = NULL; while ((b = B_GetNextBuildingByType(base, b, dependenceBuilding->buildingType))) { if (!B_IsBuildingBuiltUp(b)) { CP_Popup(_("Notice"), _("Building %s is not finished yet, and is needed to use building %s."), _(dependenceBuilding->name), _(building->name)); return; } } /* the dependence is built but doesn't work - must be because of their dependendes */ CP_Popup(_("Notice"), _("Make sure that the dependencies of building %s (%s) are operational, so that building %s may be used."), _(dependenceBuilding->name), _(dependenceBuilding->dependsBuilding->name), _(building->name)); return; } } /* all buildings are OK: employees must be missing */ if (building->buildingType == B_WORKSHOP && E_CountHired(base, EMPL_WORKER) <= 0) { CP_Popup(_("Notice"), _("You need to recruit %s to use building %s."), E_GetEmployeeString(EMPL_WORKER, 2), _(building->name)); return; } else if (building->buildingType == B_LAB && E_CountHired(base, EMPL_SCIENTIST) <= 0) { CP_Popup(_("Notice"), _("You need to recruit %s to use building %s."), E_GetEmployeeString(EMPL_SCIENTIST, 2), _(building->name)); return; } } else { CP_Popup(_("Notice"), _("Build a %s first."), _(building->name)); return; } }
/** * @brief Updates the hospital menu. * @sa HOS_Init_f */ static void HOS_UpdateMenu (void) { char name[128]; char rank[128]; int j, type; int entry; base_t *base = B_GetCurrentSelectedBase(); if (!base) return; /* Reset list. */ cgi->UI_ExecuteConfunc("hospital_clear"); for (type = 0, j = 0, entry = 0; type < MAX_EMPL; type++) { E_Foreach(type, employee) { float injuryLevel = HOS_InjuryLevel(&employee->chr); if (!E_IsInBase(employee, base)) continue; /* Don't show soldiers who are gone in mission */ if (E_IsAwayFromBase(employee)) continue; /* Don't show healthy employees */ if (employee->chr.HP >= employee->chr.maxHP && injuryLevel <= 0) continue; if (j >= hospitalFirstEntry && entry < HOS_MENU_MAX_ENTRIES) { /* Print name. */ Q_strncpyz(name, employee->chr.name, sizeof(name)); /* Print rank for soldiers or type for other personel. */ if (type == EMPL_SOLDIER) { const rank_t *rankPtr = CL_GetRankByIdx(employee->chr.score.rank); Q_strncpyz(rank, _(rankPtr->name), sizeof(rank)); } else Q_strncpyz(rank, E_GetEmployeeString(employee->type, 1), sizeof(rank)); Com_DPrintf(DEBUG_CLIENT, "%s ucn: %i entry: %i\n", name, employee->chr.ucn, entry); /* If the employee is seriously wounded (HP <= 50% maxHP), make him red. */ if (employee->chr.HP <= (int) (employee->chr.maxHP * 0.5) || injuryLevel >= 0.5) cgi->UI_ExecuteConfunc("hospitalserious %i", entry); /* If the employee is semi-seriously wounded (HP <= 85% maxHP), make him yellow. */ else if (employee->chr.HP <= (int) (employee->chr.maxHP * 0.85) || injuryLevel >= 0.15) cgi->UI_ExecuteConfunc("hospitalmedium %i", entry); else cgi->UI_ExecuteConfunc("hospitallight %i", entry); /* Display name in the correct list-entry. */ cgi->Cvar_Set(va("mn_hos_item%i", entry), name); /* Display rank in the correct list-entry. */ cgi->Cvar_Set(va("mn_hos_rank%i", entry), rank); /* Prepare the health bar */ cgi->Cvar_Set(va("mn_hos_hp%i", entry), va("%i", employee->chr.HP)); cgi->Cvar_Set(va("mn_hos_hpmax%i", entry), va("%i", employee->chr.maxHP)); /* Send wound info */ HOS_EntryWoundData(&employee->chr, entry); /* Increase the counter of list entries. */ entry++; } j++; } }