/** * @brief This removes an employee from the global list so that * he/she is no longer hireable. */ static void E_EmployeeDelete_f (void) { /* Check syntax. */ if (cgi->Cmd_Argc() < 2) { Com_Printf("Usage: %s <num>\n", cgi->Cmd_Argv(0)); return; } /* num - menu index (line in text) */ int num = employeeScrollPos + atoi(cgi->Cmd_Argv(1)); Employee* employee = E_GetEmployeeByMenuIndex(num); /* empty slot selected */ if (!employee) return; if (employee->isHired()) { if (!employee->unhire()) { cgi->UI_DisplayNotice(_("Could not fire employee"), 2000, "employees"); Com_DPrintf(DEBUG_CLIENT, "Couldn't fire employee\n"); return; } } E_DeleteEmployee(employee); cgi->Cbuf_AddText("employee_init %i\n", employeeCategory); num = std::max(0, num - 1); cgi->Cbuf_AddText("employee_select %i\n", num); cgi->Cbuf_AddText("hire_select %i\n", num); cgi->Cbuf_AddText("employee_update_count\n"); }
/** * @brief Removes employee until all employees fit in quarters capacity. * @param[in] base Pointer to the base where the number of employees should be updated. * @note employees are killed, and not just unhired (if base is destroyed, you can't recruit the same employees elsewhere) * if you want to unhire employees, you should do it before calling this function. * @note employees are not randomly chosen. Reason is that all Quarter will be destroyed at the same time, * so all employees are going to be killed anyway. */ void E_DeleteEmployeesExceedingCapacity (base_t *base) { int type; /* Check if there are too many employees */ if (base->capacities[CAP_EMPLOYEES].cur <= base->capacities[CAP_EMPLOYEES].max) return; /* do a reverse loop in order to finish by soldiers (the most important employees) */ for (type = MAX_EMPL - 1; type >= 0; type--) { employee_t *employee; /* UGV are not stored in Quarters */ if (type == EMPL_ROBOT) continue; E_Foreach(type, employee) { if (E_IsInBase(employee, base)) E_DeleteEmployee(employee); if (base->capacities[CAP_EMPLOYEES].cur <= base->capacities[CAP_EMPLOYEES].max) return; } } Com_Printf("E_DeleteEmployeesExceedingCapacity: Warning, removed all employees from base '%s', but capacity is still > 0\n", base->name); }
static void testEmployeeHandling (void) { employeeType_t type; ResetCampaignData(); for (type = 0; type < MAX_EMPL; type++) { if (type != EMPL_ROBOT) { int cnt; employee_t *e = E_CreateEmployee(type, NULL, NULL); CU_ASSERT_PTR_NOT_NULL(e); cnt = E_CountUnhired(type); CU_ASSERT_EQUAL(cnt, 1); E_DeleteEmployee(e); cnt = E_CountUnhired(type); CU_ASSERT_EQUAL(cnt, 0); } } { int i; const int amount = 3; for (i = 0; i < amount; i++) { employee_t *e = E_CreateEmployee(EMPL_SOLDIER, NULL, NULL); CU_ASSERT_PTR_NOT_NULL(e); } { employee_t *e; int cnt = 0; E_Foreach(EMPL_SOLDIER, e) { cnt++; } CU_ASSERT_EQUAL(cnt, amount); E_Foreach(EMPL_SOLDIER, e) { CU_ASSERT_TRUE(E_DeleteEmployee(e)); } cnt = E_CountUnhired(EMPL_SOLDIER); CU_ASSERT_EQUAL(cnt, 0) } }
TEST_F(CampaignTest, testEmployeeHandling) { int i; for (i = 0; i < MAX_EMPL; i++) { employeeType_t type = (employeeType_t)i; if (type != EMPL_ROBOT) { int cnt; Employee* e = E_CreateEmployee(type, nullptr, nullptr); ASSERT_TRUE(nullptr != e); cnt = E_CountUnhired(type); ASSERT_EQ(cnt, 1); E_DeleteEmployee(e); cnt = E_CountUnhired(type); ASSERT_EQ(cnt, 0); } } { const int amount = 3; for (i = 0; i < amount; i++) { Employee* e = E_CreateEmployee(EMPL_SOLDIER, nullptr, nullptr); ASSERT_TRUE(nullptr != e); } { int cnt = 0; E_Foreach(EMPL_SOLDIER, e) { (void)e; cnt++; } ASSERT_EQ(cnt, amount); E_Foreach(EMPL_SOLDIER, e) { ASSERT_TRUE(E_DeleteEmployee(e)); } cnt = E_CountUnhired(EMPL_SOLDIER); ASSERT_EQ(cnt, 0); }
/** * @brief Unloads transfer cargo when finishing the transfer or destroys it when no buildings/base. * @param[in,out] destination The destination base - might be nullptr in case the base * is already destroyed * @param[in] transfer Pointer to transfer in ccs.transfers. * @param[in] success True if the transfer reaches dest base, false if the base got destroyed. * @sa TR_TransferEnd */ static void TR_EmptyTransferCargo (base_t* destination, transfer_t* transfer, bool success) { assert(transfer); /* antimatter */ if (transfer->antimatter > 0 && success) { if (B_GetBuildingStatus(destination, B_ANTIMATTER)) { B_AddAntimatter(destination, transfer->antimatter); } else { Com_sprintf(cp_messageBuffer, sizeof(cp_messageBuffer), _("%s does not have Antimatter Storage, antimatter are removed!"), destination->name); MSO_CheckAddNewMessage(NT_TRANSFER_LOST, _("Transport mission"), cp_messageBuffer, MSG_TRANSFERFINISHED); } } /* items */ if (transfer->itemCargo != nullptr) { if (success) { linkedList_t* cargo = transfer->itemCargo->list(); LIST_Foreach(cargo, itemCargo_t, item) { if (item->amount <= 0) continue; if (!B_ItemIsStoredInBaseStorage(item->objDef)) continue; B_AddToStorage(destination, item->objDef, item->amount); } cgi->LIST_Delete(&cargo); } delete transfer->alienCargo; transfer->alienCargo = nullptr; } /* Employee */ if (transfer->hasEmployees && transfer->srcBase) { /* Employees. (cannot come from a mission) */ for (int i = EMPL_SOLDIER; i < MAX_EMPL; i++) { const employeeType_t type = (employeeType_t)i; TR_ForeachEmployee(employee, transfer, type) { employee->transfer = false; if (!success) { E_DeleteEmployee(employee); continue; } switch (type) { case EMPL_WORKER: PR_UpdateProductionCap(destination, 0); break; case EMPL_PILOT: AIR_AutoAddPilotToAircraft(destination, employee); break; default: break; } } } }
/** * @brief Remove items until everything fits in storage. * @note items will be randomly selected for removal. * @param[in] base Pointer to the base */ void CAP_RemoveItemsExceedingCapacity (base_t *base) { int i; int objIdx[MAX_OBJDEFS]; /**< Will contain idx of items that can be removed */ int num, cnt; if (CAP_GetFreeCapacity(base, CAP_ITEMS) >= 0) return; for (i = 0, num = 0; i < cgi->csi->numODs; i++) { const objDef_t *obj = INVSH_GetItemByIDX(i); if (!B_ItemIsStoredInBaseStorage(obj)) continue; /* Don't count item that we don't have in base */ if (B_ItemInBase(obj, base) <= 0) continue; objIdx[num++] = i; } cnt = E_CountHired(base, EMPL_ROBOT); /* UGV takes room in storage capacity: we store them with a value MAX_OBJDEFS that can't be used by objIdx */ for (i = 0; i < cnt; i++) { objIdx[num++] = MAX_OBJDEFS; } while (num && CAP_GetFreeCapacity(base, CAP_ITEMS) < 0) { /* Select the item to remove */ const int randNumber = rand() % num; if (objIdx[randNumber] >= MAX_OBJDEFS) { /* A UGV is destroyed: get first one */ Employee* employee = E_GetHiredRobot(base, 0); /* There should be at least a UGV */ assert(employee); E_DeleteEmployee(employee); } else { /* items are destroyed. We guess that all items of a given type are stored in the same location * => destroy all items of this type */ const int idx = objIdx[randNumber]; const objDef_t *od = INVSH_GetItemByIDX(idx); B_UpdateStorageAndCapacity(base, od, -B_ItemInBase(od, base), false); } REMOVE_ELEM(objIdx, randNumber, num); /* Make sure that we don't have an infinite loop */ if (num <= 0) break; } Com_DPrintf(DEBUG_CLIENT, "B_RemoveItemsExceedingCapacity: Remains %i in storage for a maximum of %i\n", CAP_GetCurrent(base, CAP_ITEMS), CAP_GetMax(base, CAP_ITEMS)); }
/** * @brief Removes all employees completely from the game (buildings + global list) from a given base. * @note Used if the base e.g is destroyed by the aliens. * @param[in] base Which base the employee should be fired from. */ void E_DeleteAllEmployees (base_t* base) { employeeType_t type; for (type = EMPL_SOLDIER; type < MAX_EMPL; type++) { employee_t *lastEmployee = NULL; employee_t *employee = NULL; E_Foreach(type, employee) { if ((base == NULL || E_IsInBase(employee, base)) && E_DeleteEmployee(employee)) employee = lastEmployee; else lastEmployee = employee; } } }
/** * @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; }