1 /**
2  * @file
3  * @brief Team management for the campaign gametype
4  */
5 
6 /*
7 Copyright (C) 2002-2013 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.m
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 */
25 
26 #include "../../cl_shared.h"
27 #include "../../cl_team.h"
28 #include "cp_campaign.h"
29 #include "cp_team.h"
30 
31 /**
32  * @brief Updates status of weapon (sets pointers, reloads, etc).
33  * @param[in] ed Pointer to equipment definition.
34  * @param[in] item An item to update.
35  * @return Updated item in any case, even if there was no update.
36  * @sa CP_CleanupAircraftCrew
37  */
CP_AddWeaponAmmo(equipDef_t * ed,Item * item)38 void CP_AddWeaponAmmo (equipDef_t* ed, Item* item)
39 {
40 	const objDef_t* type = item->def();
41 
42 	assert(ed->numItems[type->idx] > 0);
43 	--ed->numItems[type->idx];
44 	if (type->isArmour())
45 		return;
46 
47 	if (type->weapons[0]) {
48 		/* The given item is ammo or self-contained weapon (i.e. It has firedefinitions. */
49 		if (!type->isAmmo()) {
50 			/* "Recharge" the oneshot weapon. */
51 			item->setAmmoLeft(type->ammo);
52 			item->setAmmoDef(item->def()); /* Just in case this hasn't been done yet. */
53 			Com_DPrintf(DEBUG_CLIENT, "CL_AddWeaponAmmo: oneshot weapon '%s'.\n", type->id);
54 			return;
55 		} else {
56 			/* No change, nothing needs to be done to this item. */
57 			return;
58 		}
59 	} else if (item->getAmmoLeft()) {
60 		assert(item->ammoDef());
61 		/* The item is a weapon and it was reloaded one time. */
62 		if (item->getAmmoLeft() == type->ammo) {
63 			/* Fully loaded, no need to reload, but mark the ammo as used. */
64 			if (ed->numItems[item->ammoDef()->idx] > 0) {
65 				--ed->numItems[item->ammoDef()->idx];
66 			} else {
67 				/* Your clip has been sold; give it back. */
68 				item->setAmmoLeft(NONE_AMMO);
69 			}
70 			return;
71 		}
72 	}
73 
74 	/* Check for complete clips of the same kind */
75 	if (item->ammoDef() && ed->numItems[item->ammoDef()->idx] > 0) {
76 		--ed->numItems[item->ammoDef()->idx];
77 		item->setAmmoLeft(type->ammo);
78 		return;
79 	}
80 
81 	/* Search for any complete clips. */
82 	/** @todo We may want to change this to use the type->ammo[] info. */
83 	for (int i = 0; i < cgi->csi->numODs; i++) {
84 		const objDef_t* od = INVSH_GetItemByIDX(i);
85 		if (od->isLoadableInWeapon(type)) {
86 			if (ed->numItems[i] > 0) {
87 				--ed->numItems[i];
88 				item->setAmmoLeft(type->ammo);
89 				item->setAmmoDef(od);
90 				return;
91 			}
92 		}
93 	}
94 
95 	/** @todo on return from a mission with no clips left
96 	 * and one weapon half-loaded wielded by soldier
97 	 * and one empty in equip, on the first opening of equip,
98 	 * the empty weapon will be in soldier hands, the half-full in equip;
99 	 * this should be the other way around. */
100 
101 	/* Failed to find a complete clip - see if there's any loose ammo
102 	 * of the same kind; if so, gather it all in this weapon. */
103 	if (item->ammoDef() && ed->numItemsLoose[item->ammoDef()->idx] > 0) {
104 		item->setAmmoLeft(ed->numItemsLoose[item->ammoDef()->idx]);
105 		ed->numItemsLoose[item->ammoDef()->idx] = 0;
106 		return;
107 	}
108 
109 	/* See if there's any loose ammo */
110 	/** @todo We may want to change this to use the type->ammo[] info. */
111 	item->setAmmoLeft(NONE_AMMO);
112 	for (int i = 0; i < cgi->csi->numODs; i++) {
113 		const objDef_t* od = INVSH_GetItemByIDX(i);
114 		if (od->isLoadableInWeapon(type) && ed->numItemsLoose[i] > item->getAmmoLeft()) {
115 			if (item->getAmmoLeft() > 0) {
116 				/* We previously found some ammo, but we've now found other
117 				 * loose ammo of a different (but appropriate) type with
118 				 * more bullets.  Put the previously found ammo back, so
119 				 * we'll take the new type. */
120 				assert(item->ammoDef());
121 				ed->numItemsLoose[item->ammoDef()->idx] = item->getAmmoLeft();
122 				/* We don't have to accumulate loose ammo into clips
123 				 * because we did it previously and we create no new ammo */
124 			}
125 			/* Found some loose ammo to load the weapon with */
126 			item->setAmmoLeft(ed->numItemsLoose[i]);
127 			ed->numItemsLoose[i] = 0;
128 			item->setAmmoDef(od);
129 		}
130 	}
131 }
132 
133 /**
134  * @brief Set up equip (floor) container for soldiers
135  * @param[in,out] chr Pointer to soldiers character structure
136  */
CP_SetEquipContainer(character_t * chr)137 void CP_SetEquipContainer (character_t* chr)
138 {
139 	/* get the inventory the UI uses for all the work */
140 	Inventory* uiInv = *cgi->ui_inventory;
141 	/* if it was not already set to our character's inventory ... */
142 	if (uiInv && uiInv != &chr->inv) {
143 		/* ...use the UI equip cont for our character's equipment container */
144 		chr->inv.setContainer(CID_EQUIP, uiInv->getContainer2(CID_EQUIP));
145 		/* set 'old' CID_EQUIP to nullptr */
146 		uiInv->resetContainer(CID_EQUIP);
147 	}
148 	/* set the UI inventory to our char's (including the equip container we preserved.) */
149 	*cgi->ui_inventory = &chr->inv;
150 }
151 
152 /**
153  * @brief Reloads weapons, removes not assigned and resets defaults
154  * @param[in] base Pointer to a base for given team.
155  * @param[in] ed equipDef_t pointer to equipment
156  * @sa CL_AddWeaponAmmo
157  * @note Iterate through in container order (right hand, left hand, belt,
158  * holster, backpack) at the top level, i.e. each squad member reloads
159  * the right hand, then each reloads the left hand, etc. The effect
160  * of this is that when things are tight, everyone has the opportunity
161  * to get their preferred weapon(s) loaded before anyone is allowed
162  * to keep her spares in the backpack or on the floor. We don't want
163  * the first person in the squad filling their backpack with spare ammo
164  * leaving others with unloaded guns in their hands...
165  */
CP_CleanupTeam(base_t * base,equipDef_t * ed)166 void CP_CleanupTeam (base_t* base, equipDef_t* ed)
167 {
168 	containerIndex_t container;
169 
170 	assert(base);
171 
172 	/* Auto-assign weapons to UGVs/Robots if they have no weapon yet. */
173 	E_Foreach(EMPL_ROBOT, employee) {
174 		if (!employee->isHiredInBase(base))
175 			continue;
176 		if (employee->transfer)
177 			continue;
178 
179 		character_t* chr = &employee->chr;
180 
181 		/* This is an UGV */
182 		if (employee->getUGV()) {
183 			/* Check if there is a weapon and add it if there isn't. */
184 			Item* rightH = chr->inv.getRightHandContainer();
185 			if (!rightH || !rightH->def())
186 				cgi->INV_EquipActorRobot(&chr->inv, INVSH_GetItemByID(employee->getUGV()->weapon));
187 		}
188 	}
189 
190 	for (container = 0; container < CID_MAX; container++) {
191 		E_Foreach(EMPL_SOLDIER, employee) {
192 			if (!employee->isHiredInBase(base))
193 				continue;
194 			if (employee->transfer)
195 				continue;
196 
197 			Item* ic, *next;
198 			character_t* chr = &employee->chr;
199 #if 0
200 			/* ignore items linked from any temp container */
201 			if (INVDEF(container)->temp)
202 				continue;
203 #endif
204 			for (ic = chr->inv.getContainer2(container); ic; ic = next) {
205 				next = ic->getNext();
206 				if (ed->numItems[ic->def()->idx] > 0) {
207 					CP_AddWeaponAmmo(ed, ic);
208 				} else {
209 					/* Drop ammo used for reloading and sold carried weapons. */
210 					if (!cgi->INV_RemoveFromInventory(&chr->inv, INVDEF(container), ic))
211 						cgi->Com_Error(ERR_DROP, "Could not remove item from inventory");
212 				}
213 			}
214 		}
215 	}
216 }
217 
218 /**
219  * @brief Reloads weapons, removes not assigned and resets defaults
220  * @param[in] aircraft Pointer to an aircraft for given team.
221  * @param[in] ed equipDef_t pointer to equipment
222  * @sa CL_AddWeaponAmmo
223  * @note Iterate through in container order (right hand, left hand, belt,
224  * holster, backpack) at the top level, i.e. each squad member reloads
225  * the right hand, then each reloads the left hand, etc. The effect
226  * of this is that when things are tight, everyone has the opportunity
227  * to get their preferred weapon(s) loaded before anyone is allowed
228  * to keep her spares in the backpack or on the floor. We don't want
229  * the first person in the squad filling their backpack with spare ammo
230  * leaving others with unloaded guns in their hands...
231  */
CP_CleanupAircraftTeam(aircraft_t * aircraft,equipDef_t * ed)232 void CP_CleanupAircraftTeam (aircraft_t* aircraft, equipDef_t* ed)
233 {
234 	containerIndex_t container;
235 
236 	assert(aircraft);
237 
238 	for (container = 0; container < CID_MAX; container++) {
239 		LIST_Foreach(aircraft->acTeam, Employee, employee) {
240 			Item* ic, *next;
241 			character_t* chr = &employee->chr;
242 
243 			/* Auto-assign weapons to UGVs/Robots if they have no weapon yet. */
244 			if (employee->getUGV()) {
245 				/* Check if there is a weapon and add it if there isn't. */
246 				Item* rightH = chr->inv.getRightHandContainer();
247 				if (!rightH || !rightH->def())
248 					cgi->INV_EquipActorRobot(&chr->inv, INVSH_GetItemByID(employee->getUGV()->weapon));
249 				continue;
250 			}
251 
252 #if 0
253 			/* ignore items linked from any temp container */
254 			if (INVDEF(container)->temp)
255 				continue;
256 #endif
257 			for (ic = chr->inv.getContainer2(container); ic; ic = next) {
258 				next = ic->getNext();
259 				if (ed->numItems[ic->def()->idx] > 0) {
260 					CP_AddWeaponAmmo(ed, ic);
261 				} else {
262 					/* Drop ammo used for reloading and sold carried weapons. */
263 					if (!cgi->INV_RemoveFromInventory(&chr->inv, INVDEF(container), ic))
264 						cgi->Com_Error(ERR_DROP, "Could not remove item from inventory");
265 				}
266 			}
267 		}
268 	}
269 }
270 
271 /**
272  * @brief Clears all containers that are temp containers (see script definition).
273  * @sa GAME_SaveTeamInfo
274  * @sa GAME_SendCurrentTeamSpawningInfo
275  */
CP_CleanTempInventory(base_t * base)276 void CP_CleanTempInventory (base_t* base)
277 {
278 	E_Foreach(EMPL_SOLDIER, employee) {
279 		employee->chr.inv.resetTempContainers();
280 	}
281 
282 	E_Foreach(EMPL_ROBOT, employee) {
283 		employee->chr.inv.resetTempContainers();
284 	}
285 
286 	if (!base)
287 		return;
288 
289 	cgi->INV_DestroyInventory(&base->bEquipment);
290 }
291 
292 /**
293  * @brief Updates data about teams in aircraft.
294  * @param[in] aircraft Pointer to an aircraft for a desired team.
295  * @param[in] employeeType Type of employee for which data is being updated.
296  * @returns the number of employees that are in the aircraft and were added to
297  * the character list
298  */
CP_UpdateActorAircraftVar(aircraft_t * aircraft,employeeType_t employeeType)299 void CP_UpdateActorAircraftVar (aircraft_t* aircraft, employeeType_t employeeType)
300 {
301 	int numOnAircraft;
302 	const Employee* pilot = AIR_GetPilot(aircraft);
303 
304 	assert(aircraft);
305 
306 	numOnAircraft = AIR_GetTeamSize(aircraft);
307 	cgi->Cvar_Set("mn_hired", _("%i of %i"), numOnAircraft, aircraft->maxTeamSize);
308 	cgi->Cvar_Set("mn_hirable_count", "%i", aircraft->maxTeamSize - numOnAircraft);
309 	cgi->Cvar_Set("mn_hired_count", "%i", numOnAircraft);
310 
311 	if (pilot) {
312 		cgi->Cvar_Set("mn_pilotassigned", "1");
313 		cgi->Cvar_Set("mn_pilot_name", "%s", pilot->chr.name);
314 		cgi->Cvar_Set("mn_pilot_body", "%s", CHRSH_CharGetBody(&pilot->chr));
315 		cgi->Cvar_Set("mn_pilot_head", "%s", CHRSH_CharGetHead(&pilot->chr));
316 		cgi->Cvar_Set("mn_pilot_body_skin", "%i", pilot->chr.bodySkin);
317 		cgi->Cvar_Set("mn_pilot_head_skin", "%i", pilot->chr.headSkin);
318 	} else {
319 		cgi->Cvar_Set("mn_pilotassigned", "0");
320 		cgi->Cvar_Set("mn_pilot_name", "");
321 		cgi->Cvar_Set("mn_pilot_body", "");
322 		cgi->Cvar_Set("mn_pilot_head", "");
323 		cgi->Cvar_Set("mn_pilot_body_skin", "");
324 		cgi->Cvar_Set("mn_pilot_head_skin", "");
325 	}
326 }
327