1 /**
2  * @file
3  * @brief Single player employee stuff.
4  * @note Employee related functions prefix: E_
5  */
6 
7 /*
8 Copyright (C) 2002-2013 UFO: Alien Invasion.
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 
19 See the GNU General Public License for more details.
20 
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24 */
25 
26 #include "../../cl_shared.h"
27 #include "../cl_game.h" /* GAME_GetTeamDef */
28 #include "../cl_game_team.h" /* character xml loading */
29 #include "cp_campaign.h"
30 #include "cp_employee_callbacks.h"
31 #include "cp_rank.h"
32 #include "cp_popup.h"
33 #include "save/save_employee.h"
34 
35 /**
36  * @brief Returns number of employees of a type
37  * @param[in] type Employeetype to check
38  */
E_CountByType(employeeType_t type)39 int E_CountByType (employeeType_t type)
40 {
41 	return cgi->LIST_Count(ccs.employees[type]);
42 }
43 
44 /**
45  * @brief Iterates through unhired employees
46  * @param[in] type Employee type to look for
47  * @sa employeeType_t
48  */
E_GetUnhired(employeeType_t type)49 Employee* E_GetUnhired (employeeType_t type)
50 {
51 	E_Foreach(type, employee) {
52 		if (!employee->isHired())
53 			return employee;
54 	}
55 
56 	return nullptr;
57 }
58 
59 /**
60  * @brief Tells you if a employee is away from his home base (gone in mission).
61  * @return bool true if the employee is away in mission, false if he is not or he is unhired.
62  */
isAwayFromBase() const63 bool Employee::isAwayFromBase () const
64 {
65 	/* Check that employee is hired */
66 	if (!isHired())
67 		return false;
68 
69 	/* Check if employee is currently transferred. */
70 	if (transfer)
71 		return true;
72 
73 	/* for now only soldiers, ugvs and pilots can be assigned to an aircraft */
74 	if (!isSoldier() && !isRobot() && !isPilot())
75 		return false;
76 
77 	/* Crashed aircraft no longer belongs to any base but poor pilot/soldiers assigned
78 	 * to it are definitely away from the base so we need to iterate through all aircraft */
79 	AIR_Foreach(aircraft) {
80 		if (aircraft->homebase != baseHired)
81 			continue;
82 		if (!AIR_IsAircraftInBase(aircraft) && AIR_IsEmployeeInAircraft(this, aircraft))
83 			return true;
84 	}
85 	return false;
86 }
87 
88 /**
89  * @brief  Hires some employees of appropriate type for a building
90  * @param[in] base Which base the employee should be hired in.
91  * @param[in] building in which building
92  * @param[in] num how many employees, if -1, hire building->maxEmployees
93  * @sa B_SetUpBase
94  */
E_HireForBuilding(base_t * base,building_t * building,int num)95 void E_HireForBuilding (base_t* base, building_t* building, int num)
96 {
97 	if (num < 0)
98 		num = building->maxEmployees;
99 
100 	if (num) {
101 		employeeType_t employeeType;
102 		switch (building->buildingType) {
103 		case B_WORKSHOP:
104 			employeeType = EMPL_WORKER;
105 			break;
106 		case B_LAB:
107 			employeeType = EMPL_SCIENTIST;
108 			break;
109 		case B_HANGAR: /* the Dropship Hangar */
110 			employeeType = EMPL_SOLDIER;
111 			break;
112 		case B_MISC:
113 			Com_DPrintf(DEBUG_CLIENT, "E_HireForBuilding: Misc building type: %i with employees: %i.\n", building->buildingType, num);
114 			return;
115 		default:
116 			Com_DPrintf(DEBUG_CLIENT, "E_HireForBuilding: Unknown building type: %i.\n", building->buildingType);
117 			return;
118 		}
119 		/* don't try to hire more that available - see E_CreateEmployee */
120 		num = std::min(num, E_CountByType(employeeType));
121 		for (;num--;) {
122 			assert(base);
123 			if (!E_HireEmployeeByType(base, employeeType)) {
124 				Com_DPrintf(DEBUG_CLIENT, "E_HireForBuilding: Hiring %i employee(s) of type %i failed.\n", num, employeeType);
125 				return;
126 			}
127 		}
128 	}
129 }
130 
131 /**
132  * Will change the base where the employee is located in and will also update the
133  * capacity in the affected bases.
134  * @note Doesn't make any capacity checks and the employee must be hired already.
135  * @param employee The employee to change the base for
136  * @param newBase The base where the employee should be located at
137  * @return @c false if @c employee was a @c nullptr pointer
138  */
E_MoveIntoNewBase(Employee * employee,base_t * newBase)139 bool E_MoveIntoNewBase (Employee* employee, base_t* newBase)
140 {
141 	if (employee) {
142 		base_t* oldBase = employee->baseHired;
143 		assert(oldBase);
144 		employee->baseHired = newBase;
145 		/* Remove employee from corresponding capacity */
146 		switch (employee->getType()) {
147 		case EMPL_PILOT:
148 		case EMPL_WORKER:
149 		case EMPL_SCIENTIST:
150 		case EMPL_SOLDIER:
151 			CAP_AddCurrent(oldBase, CAP_EMPLOYEES, -1);
152 			CAP_AddCurrent(newBase, CAP_EMPLOYEES, 1);
153 			break;
154 		case EMPL_ROBOT:
155 			CAP_AddCurrent(oldBase, CAP_ITEMS, -UGV_SIZE);
156 			CAP_AddCurrent(newBase, CAP_ITEMS, UGV_SIZE);
157 			break;
158 		case MAX_EMPL:
159 			break;
160 		}
161 		return true;
162 	}
163 
164 	return false;
165 }
166 
167 /**
168  * @brief Convert employeeType_t to translated string
169  * @param type employeeType_t value
170  * @param n number of persons of that kind (for plural detection)
171  * @return translated employee string
172  */
E_GetEmployeeString(employeeType_t type,int n)173 const char* E_GetEmployeeString (employeeType_t type, int n)
174 {
175 	switch (type) {
176 	case EMPL_SOLDIER:
177 		return ngettext("Soldier", "Soldiers", n);
178 	case EMPL_SCIENTIST:
179 		return ngettext("Scientist", "Scientists", n);
180 	case EMPL_WORKER:
181 		return ngettext("Worker", "Workers", n);
182 	case EMPL_PILOT:
183 		return ngettext("Pilot", "Pilots", n);
184 	case EMPL_ROBOT:
185 		return ngettext("UGV", "UGVs", n);
186 	default:
187 		cgi->Com_Error(ERR_DROP, "Unknown employee type '%i'\n", type);
188 	}
189 }
190 
191 /**
192  * @brief Convert string to employeeType_t
193  * @param type Pointer to employee type string
194  * @return employeeType_t
195  * @todo use cgi->Com_ConstInt*
196  */
E_GetEmployeeType(const char * type)197 employeeType_t E_GetEmployeeType (const char* type)
198 {
199 	if (!type)
200 		return MAX_EMPL;
201 
202 	/* employee types as string */
203 	if (Q_streq(type, "EMPL_SCIENTIST"))
204 		return EMPL_SCIENTIST;
205 	else if (Q_streq(type, "EMPL_SOLDIER"))
206 		return EMPL_SOLDIER;
207 	else if (Q_streq(type, "EMPL_WORKER"))
208 		return EMPL_WORKER;
209 	else if (Q_streq(type, "EMPL_PILOT"))
210 		return EMPL_PILOT;
211 	else if (Q_streq(type, "EMPL_ROBOT"))
212 		return EMPL_ROBOT;
213 
214 	/* human readable employee type strings */
215 	if (Q_streq(type, "scientist"))
216 		return EMPL_SCIENTIST;
217 	else if (Q_streq(type, "soldier"))
218 		return EMPL_SOLDIER;
219 	else if (Q_streq(type, "worker"))
220 		return EMPL_WORKER;
221 	else if (Q_streq(type, "pilot"))
222 		return EMPL_PILOT;
223 	else if (Q_streq(type, "robot"))
224 		return EMPL_ROBOT;
225 	else if (Q_streq(type, "ugv"))
226 		return EMPL_ROBOT;
227 
228 	return MAX_EMPL;
229 }
230 
231 /**
232  * @brief Return a "not hired" ugv-employee pointer of a given ugv-type.
233  * @param[in] ugvType What type of robot we want.
234  * @return Employee pointer on success or nullptr on error.
235  * @sa E_GetHiredRobot
236  */
E_GetUnhiredRobot(const ugv_t * ugvType)237 Employee* E_GetUnhiredRobot (const ugv_t* ugvType)
238 {
239 	E_Foreach(EMPL_ROBOT, employee) {
240 		if (!employee->isHired()) {
241 			/* If no type was given we return the first ugv we find. */
242 			if (!ugvType || employee->getUGV() == ugvType)
243 				return employee;
244 		}
245 	}
246 
247 	return nullptr;
248 }
249 
250 /**
251  * @brief Return a list of hired employees in the given base of a given type
252  * @param[in] base Which base the employee should be searched in. If nullptr is given employees in all bases will be listed.
253  * @param[in] type Which employee type to search for.
254  * @param[out] hiredEmployees Linked list of hired employees in the base.
255  * @return Number of hired employees in the base that are currently not on a transfer. Or @c -1 in case of an error.
256  */
E_GetHiredEmployees(const base_t * const base,employeeType_t type,linkedList_t ** hiredEmployees)257 int E_GetHiredEmployees (const base_t* const base, employeeType_t type, linkedList_t** hiredEmployees)
258 {
259 	if (type >= MAX_EMPL) {
260 		Com_Printf("E_GetHiredEmployees: Unknown EmployeeType: %i\n", type);
261 		*hiredEmployees = nullptr;
262 		return -1;
263 	}
264 
265 	cgi->LIST_Delete(hiredEmployees);
266 
267 	E_Foreach(type, employee) {
268 		if (!employee->isHired())
269 			continue;
270 		if (!employee->transfer && (!base || employee->isHiredInBase(base))) {
271 			cgi->LIST_AddPointer(hiredEmployees, employee);
272 		}
273 	}
274 
275 	return cgi->LIST_Count(*hiredEmployees);
276 }
277 
278 /**
279  * @brief Return a "hired" ugv-employee pointer of a given ugv-type in a given base.
280  * @param[in] base Which base the ugv should be searched in.c
281  * @param[in] ugvType What type of robot we want.
282  * @return Employee pointer on success or nullptr on error.
283  * @sa E_GetUnhiredRobot
284  */
E_GetHiredRobot(const base_t * const base,const ugv_t * ugvType)285 Employee* E_GetHiredRobot (const base_t* const base, const ugv_t* ugvType)
286 {
287 	linkedList_t* hiredEmployees = nullptr;
288 	Employee* employee;
289 
290 	E_GetHiredEmployees(base, EMPL_ROBOT, &hiredEmployees);
291 
292 	employee = nullptr;
293 	LIST_Foreach(hiredEmployees, Employee, e) {
294 		if ((e->getUGV() == ugvType || !ugvType)	/* If no type was given we return the first ugv we find. */
295 		 && e->isHiredInBase(base)) {		/* It has to be in the defined base. */
296 			assert(e->isHired());
297 			employee = e;
298 			break;
299 		}
300 	}
301 
302 	cgi->LIST_Delete(&hiredEmployees);
303 
304 	if (!employee)
305 		Com_DPrintf(DEBUG_CLIENT, "Could not get unhired ugv/robot.\n");
306 
307 	return employee;
308 }
309 
310 /**
311  * @brief Gets an unassigned employee of a given type from the given base.
312  * @param[in] base Which base the employee should be hired in.
313  * @param[in] type The type of employee to search.
314  * @return Employee
315  * @sa E_EmployeeIsUnassigned
316  * @note assigned is not hired - they are already hired in a base, in a quarter _and_ working in another building.
317  */
E_GetAssignedEmployee(const base_t * const base,const employeeType_t type)318 Employee* E_GetAssignedEmployee (const base_t* const base, const employeeType_t type)
319 {
320 	E_Foreach(type, employee) {
321 		if (!employee->isHiredInBase(base))
322 			continue;
323 		if (employee->isAssigned())
324 			return employee;
325 	}
326 	return nullptr;
327 }
328 
329 /**
330  * @brief Gets an assigned employee of a given type from the given base.
331  * @param[in] base Which base the employee should be hired in.
332  * @param[in] type The type of employee to search.
333  * @return Employee
334  * @sa E_EmployeeIsUnassigned
335  * @note unassigned is not unhired - they are already hired in a base but are at quarters
336  */
E_GetUnassignedEmployee(const base_t * const base,const employeeType_t type)337 Employee* E_GetUnassignedEmployee (const base_t* const base, const employeeType_t type)
338 {
339 	E_Foreach(type, employee) {
340 		if (!employee->isHiredInBase(base))
341 			continue;
342 		if (!employee->isAssigned())
343 			return employee;
344 	}
345 	return nullptr;
346 }
347 
348 /**
349  * @brief Hires the employee in a base.
350  * @param[in] base Which base the employee should be hired in
351  * @param[in] employee Which employee to hire
352  * @sa E_HireEmployeeByType
353  * @sa E_UnhireEmployee
354  * @todo handle EMPL_ROBOT capacities here?
355  */
E_HireEmployee(base_t * base,Employee * employee)356 bool E_HireEmployee (base_t* base, Employee* employee)
357 {
358 	if (CAP_GetFreeCapacity(base, CAP_EMPLOYEES) <= 0) {
359 		CP_Popup(_("Not enough quarters"), _("You don't have enough quarters for your employees.\nBuild more quarters."));
360 		return false;
361 	}
362 
363 	if (employee) {
364 		/* Now uses quarter space. */
365 		employee->baseHired = base;
366 		/* Update other capacities */
367 		switch (employee->getType()) {
368 		case EMPL_WORKER:
369 			CAP_AddCurrent(base, CAP_EMPLOYEES, 1);
370 			PR_UpdateProductionCap(base);
371 			break;
372 		case EMPL_PILOT:
373 			AIR_AutoAddPilotToAircraft(base, employee);
374 			/* fall through */
375 		case EMPL_SCIENTIST:
376 		case EMPL_SOLDIER:
377 			CAP_AddCurrent(base, CAP_EMPLOYEES, 1);
378 			break;
379 		case EMPL_ROBOT:
380 			CAP_AddCurrent(base, CAP_ITEMS, UGV_SIZE);
381 			break;
382 		case MAX_EMPL:
383 			break;
384 		}
385 		return true;
386 	}
387 	return false;
388 }
389 
390 /**
391  * @brief Hires the first free employee of that type.
392  * @param[in] base Which base the employee should be hired in
393  * @param[in] type Which employee type do we search
394  * @sa E_HireEmployee
395  * @sa E_UnhireEmployee
396  */
E_HireEmployeeByType(base_t * base,employeeType_t type)397 bool E_HireEmployeeByType (base_t* base, employeeType_t type)
398 {
399 	Employee* employee = E_GetUnhired(type);
400 	return employee ? E_HireEmployee(base, employee) : false;
401 }
402 
403 /**
404  * @brief Hires the first free employee of that type.
405  * @param[in] base  Which base the ugv/robot should be hired in.
406  * @param[in] ugvType What type of ugv/robot should be hired.
407  * @return true if everything went ok (the ugv was added), otherwise false.
408  */
E_HireRobot(base_t * base,const ugv_t * ugvType)409 bool E_HireRobot (base_t* base, const ugv_t* ugvType)
410 {
411 	Employee* employee = E_GetUnhiredRobot(ugvType);
412 	return employee ? E_HireEmployee(base, employee) : false;
413 }
414 
415 /**
416  * @brief Removes the inventory of the employee and also removes him from buildings
417  */
unassign()418 base_t* Employee::unassign ()
419 {
420 	/* get the base where the employee is hired in */
421 	base_t* base = baseHired;
422 	if (!base)
423 		return base;
424 
425 	/* Remove employee from building/tech/production/aircraft). */
426 	switch (_type) {
427 	case EMPL_SCIENTIST:
428 		if (isAssigned())
429 			RS_RemoveFiredScientist(base, this);
430 		break;
431 	case EMPL_ROBOT:
432 	case EMPL_SOLDIER:
433 		/* Remove soldier from aircraft/team if he was assigned to one. */
434 		if (AIR_IsEmployeeInAircraft(this, nullptr))
435 			AIR_RemoveEmployee(this, nullptr);
436 		break;
437 	case EMPL_PILOT:
438 		AIR_RemovePilotFromAssignedAircraft(base, this);
439 		break;
440 	case EMPL_WORKER:
441 		/* Update current capacity and production times if worker is being counted there. */
442 		PR_UpdateProductionCap(base, -1);
443 		break;
444 	default:
445 		break;
446 	}
447 
448 	/* Destroy the inventory of the employee (carried items will remain in base->storage) */
449 	cgi->INV_DestroyInventory(&chr.inv);
450 
451 	/* Set all employee-tags to 'unhired'. */
452 	baseHired = nullptr;
453 
454 	return base;
455 }
456 
457 /**
458  * @brief Fires an employee.
459  * @note also remove him from the aircraft
460  * @sa E_HireEmployee
461  * @sa E_HireEmployeeByType
462  * @sa CL_RemoveSoldierFromAircraft
463  * @todo handle EMPL_ROBOT capacities here?
464  */
unhire()465 bool Employee::unhire ()
466 {
467 	if (!isHired() || transfer) {
468 		Com_DPrintf(DEBUG_CLIENT, "Could not fire employee\n");
469 		return false;
470 	}
471 
472 	base_t* base = unassign();
473 
474 	/* Remove employee from corresponding capacity */
475 	switch (_type) {
476 	case EMPL_PILOT:
477 	case EMPL_WORKER:
478 	case EMPL_SCIENTIST:
479 	case EMPL_SOLDIER:
480 		CAP_AddCurrent(base, CAP_EMPLOYEES, -1);
481 		break;
482 	case EMPL_ROBOT:
483 		CAP_AddCurrent(base, CAP_ITEMS, -UGV_SIZE);
484 		break;
485 	case MAX_EMPL:
486 		break;
487 	}
488 
489 	return true;
490 }
491 
492 /**
493  * @brief Reset the hired flag for all employees of a given type in a given base
494  * @param[in] base Which base the employee should be fired from.
495  * @param[in] type Which employee type do we search.
496  */
E_UnhireAllEmployees(base_t * base,employeeType_t type)497 void E_UnhireAllEmployees (base_t* base, employeeType_t type)
498 {
499 	if (!base)
500 		return;
501 
502 	assert(type < MAX_EMPL);
503 
504 	E_Foreach(type, employee) {
505 		if (!employee->isHiredInBase(base))
506 			continue;
507 		employee->unhire();
508 	}
509 }
510 
511 /**
512  * @brief Creates an entry of a new employee in the global list and assignes it to no building/base.
513  * @param[in] type What type of employee to create.
514  * @param[in] nation What nation the employee (mainly used for soldiers in singleplayer) comes from.
515  * @param[in] ugvType What type of ugv this employee is.
516  * @return Pointer to the newly created employee in the global list. nullptr if something goes wrong.
517  * @sa E_DeleteEmployee
518  */
E_CreateEmployee(employeeType_t type,const nation_t * nation,const ugv_t * ugvType)519 Employee* E_CreateEmployee (employeeType_t type, const nation_t* nation, const ugv_t* ugvType)
520 {
521 	const char* teamID;
522 	char teamDefName[MAX_VAR];
523 	const char* rank;
524 
525 	if (type >= MAX_EMPL)
526 		return nullptr;
527 
528 	Employee employee(type, nation, ugvType);
529 
530 	teamID = GAME_GetTeamDef();
531 
532 	/* Generate character stats, models & names. */
533 	switch (type) {
534 	case EMPL_SOLDIER:
535 		rank = "rifleman";
536 		Q_strncpyz(teamDefName, teamID, sizeof(teamDefName));
537 		break;
538 	case EMPL_SCIENTIST:
539 		rank = "scientist";
540 		Com_sprintf(teamDefName, sizeof(teamDefName), "%s_scientist", teamID);
541 		break;
542 	case EMPL_PILOT:
543 		rank = "pilot";
544 		Com_sprintf(teamDefName, sizeof(teamDefName), "%s_pilot", teamID);
545 		break;
546 	case EMPL_WORKER:
547 		rank = "worker";
548 		Com_sprintf(teamDefName, sizeof(teamDefName), "%s_worker", teamID);
549 		break;
550 	case EMPL_ROBOT:
551 		if (ugvType == nullptr)
552 			cgi->Com_Error(ERR_DROP, "E_CreateEmployee: no type given for generation of EMPL_ROBOT employee.");
553 
554 		rank = "ugv";
555 
556 		Com_sprintf(teamDefName, sizeof(teamDefName), "%s%s", teamID, ugvType->actors);
557 		break;
558 	default:
559 		cgi->Com_Error(ERR_DROP, "E_CreateEmployee: Unknown employee type\n");
560 	}
561 
562 	cgi->CL_GenerateCharacter(&employee.chr, teamDefName);
563 	employee.chr.score.rank = CL_GetRankIdx(rank);
564 
565 	Com_DPrintf(DEBUG_CLIENT, "Generate character for type: %i\n", type);
566 
567 	return &LIST_Add(&ccs.employees[type], employee);
568 }
569 
570 /**
571  * @brief Removes the employee completely from the game (buildings + global list).
572  * @param[in] employee The pointer to the employee you want to remove.
573  * @return True if the employee was removed successfully, otherwise false.
574  * @sa E_CreateEmployee
575  * @sa E_UnhireEmployee
576  * @note This function has the side effect, that the global employee number for
577  * the given employee type is reduced by one, also the ccs.employees pointers are
578  * moved to fill the gap of the removed employee. Thus pointers like acTeam in
579  * the aircraft can point to wrong employees now. This has to be taken into
580  * account
581  */
E_DeleteEmployee(Employee * employee)582 bool E_DeleteEmployee (Employee* employee)
583 {
584 	employeeType_t type;
585 
586 	if (!employee)
587 		return false;
588 
589 	type = employee->getType();
590 
591 	/* Fire the employee. This will also:
592 	 * 1) remove him from buildings&work
593 	 * 2) remove his inventory */
594 
595 	if (employee->baseHired) {
596 		/* make sure that this employee is really unhired */
597 		employee->transfer = false;
598 		employee->unhire();
599 	}
600 
601 	/* Remove the employee from the global list. */
602 	return cgi->LIST_Remove(&ccs.employees[type], (void*) employee);
603 }
604 
605 /**
606  * @brief Removes all employees completely from the game (buildings + global list) from a given base.
607  * @note Used if the base e.g is destroyed by the aliens.
608  * @param[in] base Which base the employee should be fired from.
609  */
E_DeleteAllEmployees(base_t * base)610 void E_DeleteAllEmployees (base_t* base)
611 {
612 	int i;
613 
614 	for (i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
615 		const employeeType_t type = (employeeType_t)i;
616 		E_Foreach(type, employee) {
617 			if (base == nullptr || employee->isHiredInBase(base))
618 				E_DeleteEmployee(employee);
619 		}
620 	}
621 }
622 
623 
624 /**
625  * @brief Removes employee until all employees fit in quarters capacity.
626  * @param[in] base Pointer to the base where the number of employees should be updated.
627  * @note employees are killed, and not just unhired (if base is destroyed, you can't recruit the same employees elsewhere)
628  *	if you want to unhire employees, you should do it before calling this function.
629  * @note employees are not randomly chosen. Reason is that all Quarter will be destroyed at the same time,
630  *	so all employees are going to be killed anyway.
631  */
E_DeleteEmployeesExceedingCapacity(base_t * base)632 void E_DeleteEmployeesExceedingCapacity (base_t* base)
633 {
634 	int i;
635 
636 	/* Check if there are too many employees */
637 	if (CAP_GetFreeCapacity(base, CAP_EMPLOYEES) >= 0)
638 		return;
639 
640 	/* do a reverse loop in order to finish by soldiers (the most important employees) */
641 	for (i = MAX_EMPL - 1; i >= 0; i--) {
642 		const employeeType_t type = (employeeType_t)i;
643 		/* UGV are not stored in Quarters */
644 		if (type == EMPL_ROBOT)
645 			continue;
646 
647 		E_Foreach(type, employee) {
648 			if (employee->isHiredInBase(base))
649 				E_DeleteEmployee(employee);
650 
651 			if (CAP_GetFreeCapacity(base, CAP_EMPLOYEES) >= 0)
652 				return;
653 		}
654 	}
655 
656 	Com_Printf("E_DeleteEmployeesExceedingCapacity: Warning, removed all employees from base '%s', but capacity is still > 0\n", base->name);
657 }
658 
659 /**
660  * @brief Recreates all the employees for a particular employee type in the global list.
661  * But it does not overwrite any employees already hired.
662  * @param[in] type The type of the employee list to process.
663  * @param[in] excludeUnhappyNations True if a nation is unhappy then they wont
664  * send any pilots, false if happiness of nations in not considered.
665  * @sa CP_NationHandleBudget
666  */
E_RefreshUnhiredEmployeeGlobalList(const employeeType_t type,const bool excludeUnhappyNations)667 int E_RefreshUnhiredEmployeeGlobalList (const employeeType_t type, const bool excludeUnhappyNations)
668 {
669 	const nation_t* happyNations[MAX_NATIONS];
670 	int numHappyNations = 0;
671 	int idx, nationIdx, cnt;
672 
673 	happyNations[0] = nullptr;
674 	/* get a list of nations,  if excludeHappyNations is true then also exclude
675 	 * unhappy nations (unhappy nation: happiness <= 0) from the list */
676 	for (idx = 0; idx < ccs.numNations; idx++) {
677 		const nation_t* nation = NAT_GetNationByIDX(idx);
678 		const nationInfo_t* stats = NAT_GetCurrentMonthInfo(nation);
679 		if (stats->happiness > 0 || !excludeUnhappyNations) {
680 			happyNations[numHappyNations] = nation;
681 			numHappyNations++;
682 		}
683 	}
684 
685 	if (!numHappyNations)
686 		return 0;
687 
688 	idx = 0;
689 	/* Fill the global data employee list with employees, evenly distributed
690 	 * between nations in the happyNations list */
691 	E_Foreach(type, employee) {
692 		/* we don't want to overwrite employees that have already been hired */
693 		if (!employee->isHired()) {
694 			E_DeleteEmployee(employee);
695 			idx++;
696 		}
697 	}
698 
699 	nationIdx = 0;
700 	cnt = 0;
701 	while (idx-- > 0) {
702 		if (E_CreateEmployee(type, happyNations[nationIdx], nullptr) != nullptr)
703 			cnt++;
704 		nationIdx = (nationIdx + 1) % numHappyNations;
705 	}
706 
707 	return cnt;
708 }
709 
710 /**
711  * @brief Counts hired employees of a given type in a given base
712  * @param[in] base The base where we count (@c nullptr to count all).
713  * @param[in] type The type of employee to search.
714  * @return count of hired employees of a given type in a given base
715  */
E_CountHired(const base_t * const base,employeeType_t type)716 int E_CountHired (const base_t* const base, employeeType_t type)
717 {
718 	int count = 0;
719 
720 	E_Foreach(type, employee) {
721 		if (!employee->isHired())
722 			continue;
723 		if (!base || employee->isHiredInBase(base))
724 			count++;
725 	}
726 	return count;
727 }
728 
729 /**
730  * @brief Counts 'hired' (i.e. bought or produced UGVs and other robots of a given ugv-type in a given base.
731  * @param[in] base The base where we count (@c nullptr to count all).
732  * @param[in] ugvType What type of robot/ugv we are looking for.
733  * @return Count of Robots/UGVs.
734  */
E_CountHiredRobotByType(const base_t * const base,const ugv_t * ugvType)735 int E_CountHiredRobotByType (const base_t* const base, const ugv_t* ugvType)
736 {
737 	int count = 0;
738 
739 	E_Foreach(EMPL_ROBOT, employee) {
740 		if (!employee->isHired())
741 			continue;
742 		if (employee->getUGV() == ugvType && (!base || employee->isHiredInBase(base)))
743 			count++;
744 	}
745 	return count;
746 }
747 
748 
749 /**
750  * @brief Counts all hired employees of a given base
751  * @param[in] base The base where we count
752  * @return count of hired employees of a given type in a given base
753  * @note must not return 0 if hasBuilding[B_QUARTER] is false: used to update capacity
754  * @todo What about EMPL_ROBOT?
755  */
E_CountAllHired(const base_t * const base)756 int E_CountAllHired (const base_t* const base)
757 {
758 	if (!base)
759 		return 0;
760 
761 	int count = 0;
762 	for (int i = 0; i < MAX_EMPL; i++) {
763 		const employeeType_t type = (employeeType_t)i;
764 		count += E_CountHired(base, type);
765 	}
766 
767 	return count;
768 }
769 
770 /**
771  * @brief Counts unhired employees of a given type in a given base
772  * @param[in] type The type of employee to search.
773  * @return count of hired employees of a given type in a given base
774  */
E_CountUnhired(employeeType_t type)775 int E_CountUnhired (employeeType_t type)
776 {
777 	int count = 0;
778 
779 	E_Foreach(type, employee) {
780 		if (!employee->isHired())
781 			count++;
782 	}
783 	return count;
784 }
785 
786 /**
787  * @brief Counts all available Robots/UGVs that are for sale.
788  * @param[in] ugvType What type of robot/ugv we are looking for.
789  * @return count of available robots/ugvs.
790  */
E_CountUnhiredRobotsByType(const ugv_t * ugvType)791 int E_CountUnhiredRobotsByType (const ugv_t* ugvType)
792 {
793 	int count = 0;
794 
795 	E_Foreach(EMPL_ROBOT, employee) {
796 		if (!employee->isHired() && employee->getUGV() == ugvType)
797 			count++;
798 	}
799 	return count;
800 }
801 
802 /**
803  * @brief Counts unassigned employees of a given type in a given base
804  * @param[in] type The type of employee to search.
805  * @param[in] base The base where we count
806  */
E_CountUnassigned(const base_t * const base,employeeType_t type)807 int E_CountUnassigned (const base_t* const base, employeeType_t type)
808 {
809 	if (!base)
810 		return 0;
811 
812 	int count = 0;
813 	E_Foreach(type, employee) {
814 		if (!employee->isHiredInBase(base))
815 			continue;
816 		if (!employee->isAssigned())
817 			count++;
818 	}
819 	return count;
820 }
821 
822 /**
823  * @brief Hack to get a random nation for the initial
824  */
E_RandomNation(void)825 static inline const nation_t* E_RandomNation (void)
826 {
827 	const int nationIndex = rand() % ccs.numNations;
828 	return NAT_GetNationByIDX(nationIndex);
829 }
830 
831 /**
832  * @brief Create initial hireable employees
833  */
E_InitialEmployees(const campaign_t * campaign)834 void E_InitialEmployees (const campaign_t* campaign)
835 {
836 	int i;
837 
838 	/* setup initial employee count */
839 	for (i = 0; i < campaign->soldiers; i++)
840 		E_CreateEmployee(EMPL_SOLDIER, E_RandomNation(), nullptr);
841 	for (i = 0; i < campaign->scientists; i++)
842 		E_CreateEmployee(EMPL_SCIENTIST, E_RandomNation(), nullptr);
843 	for (i = 0; i < campaign->workers; i++)
844 		E_CreateEmployee(EMPL_WORKER, E_RandomNation(), nullptr);
845 	for (i = 0; i < campaign->pilots; i++)
846 		E_CreateEmployee(EMPL_PILOT, E_RandomNation(), nullptr);
847 }
848 
849 #ifdef DEBUG
850 /**
851  * @brief Debug command to list all hired employee
852  */
E_ListHired_f(void)853 static void E_ListHired_f (void)
854 {
855 	int i;
856 
857 	for (i = 0; i < MAX_EMPL; i++) {
858 		const employeeType_t emplType = (employeeType_t)i;
859 		E_Foreach(emplType, employee) {
860 			Com_Printf("Employee: %s (ucn: %i) %s at %s\n", E_GetEmployeeString(employee->getType(), 1), employee->chr.ucn,
861 					employee->chr.name, employee->baseHired->name);
862 			if (employee->getType() != emplType)
863 				Com_Printf("Warning: EmployeeType mismatch: %i != %i\n", emplType, employee->getType());
864 		}
865 	}
866 }
867 
868 /**
869  * @brief Debug function to add 5 new unhired employees of each type
870  * @note called with debug_addemployees
871  */
CL_DebugNewEmployees_f(void)872 static void CL_DebugNewEmployees_f (void)
873 {
874 	int j;
875 	nation_t* nation = &ccs.nations[0];	/**< This is just a debugging function, nation does not matter */
876 
877 	for (j = 0; j < 5; j++)
878 		/* Create a scientist */
879 		E_CreateEmployee(EMPL_SCIENTIST, nation, nullptr);
880 
881 	for (j = 0; j < 5; j++)
882 		/* Create a pilot. */
883 		E_CreateEmployee(EMPL_PILOT, nation, nullptr);
884 
885 	for (j = 0; j < 5; j++)
886 		/* Create a soldier. */
887 		E_CreateEmployee(EMPL_SOLDIER, nation, nullptr);
888 
889 	for (j = 0; j < 5; j++)
890 		/* Create a worker. */
891 		E_CreateEmployee(EMPL_WORKER, nation, nullptr);
892 }
893 #endif
894 
895 /**
896  * @brief Searches employee from a type for the ucn (character id)
897  * @param[in] type employee type
898  * @param[in] uniqueCharacterNumber unique character number (UCN)
899  */
E_GetEmployeeByTypeFromChrUCN(employeeType_t type,int uniqueCharacterNumber)900 Employee* E_GetEmployeeByTypeFromChrUCN (employeeType_t type, int uniqueCharacterNumber)
901 {
902 	E_Foreach(type, employee) {
903 		if (employee->chr.ucn == uniqueCharacterNumber)
904 			return employee;
905 	}
906 
907 	return nullptr;
908 }
909 
910 /**
911  * @brief Searches all employee for the ucn (character id)
912  * @param[in] uniqueCharacterNumber unique character number (UCN)
913  */
E_GetEmployeeFromChrUCN(int uniqueCharacterNumber)914 Employee* E_GetEmployeeFromChrUCN (int uniqueCharacterNumber)
915 {
916 	int i;
917 
918 	for (i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
919 		const employeeType_t emplType = (employeeType_t)i;
920 		Employee* employee = E_GetEmployeeByTypeFromChrUCN(emplType, uniqueCharacterNumber);
921 		if (employee)
922 			return employee;
923 	}
924 
925 	return nullptr;
926 }
927 
928 
929 /**
930  * @brief Save callback for savegames in XML Format
931  * @param[out] p XML Node structure, where we write the information to
932  * @sa E_LoadXML
933  * @sa SAV_GameSaveXML
934  * @sa G_SendCharacterData
935  * @sa CP_ParseCharacterData
936  * @sa GAME_SendCurrentTeamSpawningInfo
937  */
E_SaveXML(xmlNode_t * p)938 bool E_SaveXML (xmlNode_t* p)
939 {
940 	int i;
941 
942 	cgi->Com_RegisterConstList(saveEmployeeConstants);
943 	for (i = 0; i < MAX_EMPL; i++) {
944 		const employeeType_t emplType = (employeeType_t)i;
945 		xmlNode_t* snode = cgi->XML_AddNode(p, SAVE_EMPLOYEE_EMPLOYEES);
946 
947 		cgi->XML_AddString(snode, SAVE_EMPLOYEE_TYPE, cgi->Com_GetConstVariable(SAVE_EMPLOYEETYPE_NAMESPACE, emplType));
948 		E_Foreach(emplType, employee) {
949 			xmlNode_t* chrNode;
950 			xmlNode_t* ssnode = cgi->XML_AddNode(snode, SAVE_EMPLOYEE_EMPLOYEE);
951 
952 			/** @note e->transfer is not saved here because it'll be restored via TR_Load. */
953 			if (employee->baseHired)
954 				cgi->XML_AddInt(ssnode, SAVE_EMPLOYEE_BASEHIRED, employee->baseHired->idx);
955 			if (employee->isAssigned())
956 				cgi->XML_AddBool(ssnode, SAVE_EMPLOYEE_ASSIGNED, employee->isAssigned());
957 			/* Store the nations identifier string. */
958 			assert(employee->getNation());
959 			cgi->XML_AddString(ssnode, SAVE_EMPLOYEE_NATION, employee->getNation()->id);
960 			/* Store the ugv-type identifier string. (Only exists for EMPL_ROBOT). */
961 			if (employee->getUGV())
962 				cgi->XML_AddString(ssnode, SAVE_EMPLOYEE_UGV, employee->getUGV()->id);
963 			/* Character Data */
964 			chrNode = cgi->XML_AddNode(ssnode, SAVE_EMPLOYEE_CHR);
965 			GAME_SaveCharacter(chrNode, &employee->chr);
966 		}
967 	}
968 	cgi->Com_UnregisterConstList(saveEmployeeConstants);
969 
970 	return true;
971 }
972 
973 /**
974  * @brief Load callback for savegames in XML Format
975  * @param[in] p XML Node structure, where we get the information from
976  */
E_LoadXML(xmlNode_t * p)977 bool E_LoadXML (xmlNode_t* p)
978 {
979 	xmlNode_t* snode;
980 	bool success = true;
981 
982 	cgi->Com_RegisterConstList(saveEmployeeConstants);
983 	for (snode = cgi->XML_GetNode(p, SAVE_EMPLOYEE_EMPLOYEES); snode;
984 			snode = cgi->XML_GetNextNode(snode, p, SAVE_EMPLOYEE_EMPLOYEES)) {
985 		xmlNode_t* ssnode;
986 		employeeType_t emplType;
987 		const char* type = cgi->XML_GetString(snode, SAVE_EMPLOYEE_TYPE);
988 
989 		if (!cgi->Com_GetConstIntFromNamespace(SAVE_EMPLOYEETYPE_NAMESPACE, type, (int*) &emplType)) {
990 			Com_Printf("Invalid employee type '%s'\n", type);
991 			success = false;
992 			break;
993 		}
994 
995 		for (ssnode = cgi->XML_GetNode(snode, SAVE_EMPLOYEE_EMPLOYEE); ssnode;
996 				ssnode = cgi->XML_GetNextNode(ssnode, snode, SAVE_EMPLOYEE_EMPLOYEE)) {
997 			int baseIDX;
998 			xmlNode_t* chrNode;
999 
1000 			/* nation */
1001 			const nation_t* nation = NAT_GetNationByID(cgi->XML_GetString(ssnode, SAVE_EMPLOYEE_NATION));
1002 			if (!nation) {
1003 				Com_Printf("No nation defined for employee\n");
1004 				success = false;
1005 				break;
1006 			}
1007 			/* UGV-Type */
1008 			const ugv_t* ugv = cgi->Com_GetUGVByIDSilent(cgi->XML_GetString(ssnode, SAVE_EMPLOYEE_UGV));
1009 			Employee e(emplType, nation, ugv);
1010 			/** @note e->transfer is restored in cl_transfer.c:TR_Load */
1011 			/* base */
1012 			assert(B_AtLeastOneExists());	/* Just in case the order is ever changed. */
1013 			baseIDX = cgi->XML_GetInt(ssnode, SAVE_EMPLOYEE_BASEHIRED, -1);
1014 			e.baseHired = B_GetBaseByIDX(baseIDX);
1015 			/* assigned to a building? */
1016 			e.setAssigned(cgi->XML_GetBool(ssnode, SAVE_EMPLOYEE_ASSIGNED, false));
1017 			/* Character Data */
1018 			chrNode = cgi->XML_GetNode(ssnode, SAVE_EMPLOYEE_CHR);
1019 			if (!chrNode) {
1020 				Com_Printf("No character definition found for employee\n");
1021 				success = false;
1022 				break;
1023 			}
1024 			if (!GAME_LoadCharacter(chrNode, &e.chr)) {
1025 				Com_Printf("Error loading character definition for employee\n");
1026 				success = false;
1027 				break;
1028 			}
1029 			LIST_Add(&ccs.employees[emplType], e);
1030 		}
1031 		if (!success)
1032 			break;
1033 	}
1034 	cgi->Com_UnregisterConstList(saveEmployeeConstants);
1035 
1036 	return success;
1037 }
1038 
1039 /**
1040  * @brief Returns true if the current base is able to handle employees
1041  * @sa B_BaseInit_f
1042  */
E_HireAllowed(const base_t * base)1043 bool E_HireAllowed (const base_t* base)
1044 {
1045 	if (!B_IsUnderAttack(base) && B_GetBuildingStatus(base, B_QUARTERS))
1046 		return true;
1047 	return false;
1048 }
1049 
1050 /**
1051  * @brief Removes the items of an employee (soldier) from the base storage (s)he is hired at
1052  * @param[in] employee Pointer to the soldier whose items should be removed
1053  */
E_RemoveInventoryFromStorage(Employee * employee)1054 void E_RemoveInventoryFromStorage (Employee* employee)
1055 {
1056 	const character_t* chr = &employee->chr;
1057 
1058 	assert(employee->baseHired);
1059 
1060 	const Container* cont = nullptr;
1061 	while ((cont = chr->inv.getNextCont(cont))) {
1062 		Item* item = nullptr;
1063 		while ((item = cont->getNextItem(item))) {
1064 			/* Remove ammo */
1065 			if (item->ammoDef() && item->ammoDef() != item->def())
1066 				B_AddToStorage(employee->baseHired, item->ammoDef(), -1);
1067 			/* Remove Item */
1068 			if (item->def())
1069 				B_AddToStorage(employee->baseHired, item->def(), -1);
1070 		}
1071 	}
1072 }
1073 
1074 /**
1075  * @brief This is more or less the initial
1076  * Bind some of the functions in this file to console-commands that you can call ingame.
1077  */
E_InitStartup(void)1078 void E_InitStartup (void)
1079 {
1080 	E_InitCallbacks();
1081 #ifdef DEBUG
1082 	cgi->Cmd_AddCommand("debug_listhired", E_ListHired_f, "Debug command to list all hired employee");
1083 	cgi->Cmd_AddCommand("debug_addemployees", CL_DebugNewEmployees_f, "Debug function to add 5 new unhired employees of each type");
1084 #endif
1085 }
1086 
1087 /**
1088  * @brief Closing actions for employee-subsystem
1089  */
E_Shutdown(void)1090 void E_Shutdown (void)
1091 {
1092 	int i;
1093 
1094 	for (i = EMPL_SOLDIER; i < MAX_EMPL; i++) {
1095 		const employeeType_t emplType = (employeeType_t)i;
1096 		cgi->LIST_Delete(&ccs.employees[emplType]);
1097 	}
1098 
1099 	E_ShutdownCallbacks();
1100 #ifdef DEBUG
1101 	cgi->Cmd_RemoveCommand("debug_listhired");
1102 	cgi->Cmd_RemoveCommand("debug_addemployees");
1103 #endif
1104 }
1105