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