1 /**
2  * @file
3  */
4 
5 /*
6 Copyright (C) 2002-2013 UFO: Alien Invasion.
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12 
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 See the GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 
23 */
24 
25 #include "../client.h"
26 #include "cl_hud_callbacks.h"
27 #include "cl_hud.h"
28 #include "cl_actor.h"
29 
30 /**
31  * @brief returns the weapon the actor's left hand is touching. In case the actor
32  * holds a two handed weapon in his right hand, this weapon is returned here.
33  * This function only returns @c nullptr if no two handed weapon is in the right hand
34  * and the left hand is empty.
35  */
HUD_GetLeftHandWeapon(const le_t * actor,containerIndex_t * container)36 Item* HUD_GetLeftHandWeapon (const le_t* actor, containerIndex_t* container)
37 {
38 	Item* item = actor->getLeftHandItem();
39 
40 	if (!item) {
41 		item = actor->getRightHandItem();
42 		if (item == nullptr || !item->isHeldTwoHanded())
43 			item = nullptr;
44 		else if (container != nullptr)
45 			*container = CID_RIGHT;
46 	}
47 
48 	return item;
49 }
50 
51 /**
52  * @brief Returns the fire definition of the item the actor has in the given hand.
53  * @param[in] actor The pointer to the actor we want to get the data from.
54  * @param[in] hand Which hand to use
55  * @return the used @c fireDef_t
56  */
HUD_GetFireDefinitionForHand(const le_t * actor,const actorHands_t hand)57 const fireDef_t* HUD_GetFireDefinitionForHand (const le_t* actor, const actorHands_t hand)
58 {
59 	if (!actor)
60 		return nullptr;
61 
62 	const Item* weapon = actor->getHandItem(hand);
63 	if (!weapon || !weapon->def())
64 		return nullptr;
65 
66 	return weapon->getFiredefs();
67 }
68 
69 /**
70  * @brief Check if shooting is possible.
71  * i.e. The weapon has ammo and can be fired with the 'available' hands.
72  * TUs (i.e. "time") are are _not_ checked here, this needs to be done
73  * elsewhere for the correct firemode.
74  * @sa HUD_FireWeapon_f
75  * @return false when action is not possible, otherwise true
76  */
HUD_CheckShooting(const le_t * le,Item * weapon)77 static bool HUD_CheckShooting (const le_t* le, Item* weapon)
78 {
79 	if (!le)
80 		return false;
81 
82 	/* No item in hand. */
83 	if (!weapon || !weapon->def()) {
84 		HUD_DisplayMessage(_("Can't perform action - no item in hand!"));
85 		return false;
86 	}
87 	/* Cannot shoot because of lack of ammo. */
88 	if (weapon->mustReload()) {
89 		HUD_DisplayMessage(_("Can't perform action - no ammo!"));
90 		return false;
91 	}
92 	/* Cannot shoot because weapon is fireTwoHanded, yet both hands handle items. */
93 	if (weapon->def()->fireTwoHanded && le->getLeftHandItem()) {
94 		HUD_DisplayMessage(_("Can't perform action - weapon cannot be fired one handed!"));
95 		return false;
96 	}
97 
98 	return true;
99 }
100 
101 /**
102  * @brief Starts aiming/target mode for selected left/right firemode.
103  * @note Previously know as a combination of CL_FireRightPrimary, CL_FireRightSecondary,
104  * @note CL_FireLeftPrimary and CL_FireLeftSecondary.
105  */
HUD_FireWeapon_f(void)106 static void HUD_FireWeapon_f (void)
107 {
108 	le_t* actor = selActor;
109 
110 	if (Cmd_Argc() < 3) { /* no argument given */
111 		Com_Printf("Usage: %s <l|r> <firemode number>\n", Cmd_Argv(0));
112 		return;
113 	}
114 
115 	if (actor == nullptr)
116 		return;
117 
118 	const actorHands_t hand = ACTOR_GET_HAND_INDEX(Cmd_Argv(1)[0]);
119 	const fireDefIndex_t firemode = atoi(Cmd_Argv(2));
120 	if (firemode >= MAX_FIREDEFS_PER_WEAPON || firemode < 0)
121 		return;
122 
123 	const fireDef_t* fd = HUD_GetFireDefinitionForHand(actor, hand);
124 	if (fd == nullptr)
125 		return;
126 
127 	const objDef_t* ammo = fd->obj;
128 
129 	/* Let's check if shooting is possible. */
130 	if (!HUD_CheckShooting(actor, actor->getHandItem(hand)))
131 		return;
132 
133 	if (CL_ActorTimeForFireDef(actor, &ammo->fd[fd->weapFdsIdx][firemode]) <= CL_ActorUsableTUs(actor)) {
134 		/* Actually start aiming. This is done by changing the current mode of display. */
135 		if (hand == ACTOR_HAND_RIGHT)
136 			CL_ActorSetMode(actor, M_FIRE_R);
137 		else
138 			CL_ActorSetMode(actor, M_FIRE_L);
139 		/* Store firemode. */
140 		actor->currentSelectedFiremode = firemode;
141 	} else {
142 		/* Can not shoot because of not enough TUs - every other
143 		 * case should be checked previously in this function. */
144 		HUD_DisplayMessage(_("Can't perform action - not enough TUs!"));
145 	}
146 }
147 
HUD_SetMoveMode_f(void)148 static void HUD_SetMoveMode_f (void)
149 {
150 	le_t* actor = selActor;
151 
152 	if (actor == nullptr)
153 		return;
154 
155 	CL_ActorSetMode(actor, M_MOVE);
156 }
157 
158 /**
159  * @brief Toggles if the current actor reserves TUs for crouching.
160  */
HUD_ToggleCrouchReservation_f(void)161 static void HUD_ToggleCrouchReservation_f (void)
162 {
163 	le_t* actor = selActor;
164 
165 	if (!CL_ActorCheckAction(actor))
166 		return;
167 
168 	if (CL_ActorReservedTUs(actor, RES_CROUCH) >= TU_CROUCH) {
169 		/* Reset reserved TUs to 0 */
170 		CL_ActorReserveTUs(actor, RES_CROUCH, 0);
171 		HUD_DisplayMessage(_("Disabled automatic crouching/standing up."));
172 	} else {
173 		/* Reserve the exact amount for crouching/standing up (if we have enough to do so). */
174 		CL_ActorReserveTUs(actor, RES_CROUCH, TU_CROUCH);
175 		HUD_DisplayMessage(va(_("Reserved %i TUs for crouching/standing up."), TU_CROUCH));
176 	}
177 }
178 
179 /**
180  * @brief Toggles reaction fire states for the current selected actor
181  */
HUD_ToggleReaction_f(void)182 static void HUD_ToggleReaction_f (void)
183 {
184 	int state = 0;
185 	le_t* actor = selActor;
186 
187 	if (!CL_ActorCheckAction(actor))
188 		return;
189 
190 	if (!(actor->state & STATE_REACTION)) {
191 		if (actor->inv.holdsReactionFireWeapon() && CL_ActorUsableTUs(actor) >= HUD_ReactionFireGetTUs(actor)) {
192 			state = STATE_REACTION;
193 			HUD_DisplayMessage(_("Reaction fire enabled."));
194 		} else {
195 			return;
196 		}
197 	} else {
198 		state = ~STATE_REACTION;
199 		HUD_DisplayMessage(_("Reaction fire disabled."));
200 	}
201 
202 	/* Send request to update actor's reaction state to the server. */
203 	MSG_Write_PA(PA_STATE, actor->entnum, state);
204 }
205 
206 /**
207  * @brief Calculate total reload time for selected actor.
208  * @param[in] le Pointer to local entity handling the weapon.
209  * @param[in] weapon Item in (currently only right) hand.
210  * @param[in] toContainer The container index to get the ammo from (used
211  * to calculate the TUs that are needed to move the item out of this container)
212  * @return Time needed to reload or @c -1 if no suitable ammo found.
213  * @note This routine assumes the time to reload a weapon
214  * @note in the right hand is the same as the left hand.
215  * @sa HUD_UpdateButtons
216  * @sa HUD_CheckReload
217  */
HUD_CalcReloadTime(const le_t * le,const objDef_t * weapon,containerIndex_t toContainer)218 int HUD_CalcReloadTime (const le_t* le, const objDef_t* weapon, containerIndex_t toContainer)
219 {
220 	if (toContainer == NONE)
221 		return -1;
222 
223 	assert(le);
224 	assert(weapon);
225 
226 	Item* ic;
227 	const containerIndex_t container = CL_ActorGetContainerForReload(&ic, &le->inv, weapon);
228 	if (container == NONE)
229 		return -1;
230 
231 	/* total TU cost is the sum of 3 numbers:
232 	 * TU for weapon reload + TU to get ammo out + TU to put ammo in hands */
233 	return INVDEF(container)->out + INVDEF(toContainer)->in + weapon->getReloadTime();
234 }
235 
236 /**
237  * @brief Check if reload is possible.
238  * @param[in] le Pointer to local entity for which we handle an action on hud menu.
239  * @param[in] weapon An item in hands.
240  * @param[in] container The container to get the ammo from
241  * @return false when action is not possible, otherwise true
242  * @sa HUD_ReloadLeft_f
243  * @sa HUD_ReloadRight_f
244  */
HUD_CheckReload(const le_t * le,const Item * weapon,containerIndex_t container)245 static bool HUD_CheckReload (const le_t* le, const Item* weapon, containerIndex_t container)
246 {
247 	if (!le)
248 		return false;
249 
250 	/* No item in hand. */
251 	if (!weapon || !weapon->def()) {
252 		HUD_DisplayMessage(_("Can't perform action - no item in hand!"));
253 		return false;
254 	}
255 
256 	/* Cannot reload because this item is not reloadable. */
257 	if (!weapon->isReloadable()) {
258 		HUD_DisplayMessage(_("Can't perform action - this item is not reloadable!"));
259 		return false;
260 	}
261 
262 	const int tus = HUD_CalcReloadTime(le, weapon->def(), container);
263 	/* Cannot reload because of no ammo in inventory. */
264 	if (tus == -1) {
265 		HUD_DisplayMessage(_("Can't perform action - no ammo!"));
266 		return false;
267 	}
268 	/* Cannot reload because of not enough TUs. */
269 	if (le->TU < tus) {
270 		HUD_DisplayMessage(_("Can't perform action - not enough TUs!"));
271 		return false;
272 	}
273 
274 	return true;
275 }
276 
277 /**
278  * @brief Reload left weapon.
279  */
HUD_ReloadLeft_f(void)280 static void HUD_ReloadLeft_f (void)
281 {
282 	le_t* actor = selActor;
283 
284 	if (actor == nullptr)
285 		return;
286 
287 	containerIndex_t container = CID_LEFT;
288 	if (!HUD_CheckReload(actor, HUD_GetLeftHandWeapon(actor, &container), container))
289 		return;
290 	CL_ActorReload(actor, container);
291 	HUD_DisplayMessage(_("Left hand weapon reloaded."));
292 }
293 
294 /**
295  * @brief Reload right weapon.
296  */
HUD_ReloadRight_f(void)297 static void HUD_ReloadRight_f (void)
298 {
299 	le_t* actor = selActor;
300 
301 	if (!actor || !HUD_CheckReload(actor, actor->getRightHandItem(), CID_RIGHT))
302 		return;
303 	CL_ActorReload(actor, CID_RIGHT);
304 	HUD_DisplayMessage(_("Right hand weapon reloaded."));
305 }
306 
307 /**
308  * Ask the current selected soldier to execute an action
309  * @todo extend it to open doors or things like that
310  */
HUD_ExecuteAction_f(void)311 static void HUD_ExecuteAction_f (void)
312 {
313 	if (!selActor)
314 		return;
315 
316 	if (Cmd_Argc() < 2) {
317 		Com_Printf("Usage: %s <actionid>\n", Cmd_Argv(0));
318 		return;
319 	}
320 
321 	if (Q_streq(Cmd_Argv(1), "reload_handl")) {
322 		HUD_ReloadLeft_f();
323 		return;
324 	}
325 
326 	if (Q_streq(Cmd_Argv(1), "reload_handr")) {
327 		HUD_ReloadRight_f();
328 		return;
329 	}
330 
331 	if (char const* const rest = Q_strstart(Cmd_Argv(1), "fire_hand")) {
332 		if (strlen(rest) >= 4) {
333 			char const hand  = rest[0];
334 			int  const index = atoi(rest + 3);
335 			Cmd_ExecuteString("hud_fireweapon %c %i", hand, index);
336 			return;
337 		}
338 	}
339 
340 	Com_Printf("HUD_ExecuteAction_f: Action \"%s\" unknown.\n", Cmd_Argv(1));
341 }
342 
HUD_InitCallbacks(void)343 void HUD_InitCallbacks (void)
344 {
345 	Cmd_AddCommand("hud_movemode", HUD_SetMoveMode_f, N_("Set selected actor to move mode (it cancels the fire mode)."));
346 	Cmd_AddCommand("hud_reloadleft", HUD_ReloadLeft_f, N_("Reload the weapon in the soldiers left hand."));
347 	Cmd_AddCommand("hud_reloadright", HUD_ReloadRight_f, N_("Reload the weapon in the soldiers right hand."));
348 	Cmd_AddCommand("hud_togglecrouchreserve", HUD_ToggleCrouchReservation_f, N_("Toggle reservation for crouching."));
349 	Cmd_AddCommand("hud_togglereaction", HUD_ToggleReaction_f, N_("Toggle reaction fire."));
350 	Cmd_AddCommand("hud_fireweapon", HUD_FireWeapon_f, N_("Start aiming the weapon."));
351 	Cmd_AddCommand("hud_executeaction", HUD_ExecuteAction_f, N_("Execute an action."));
352 }
353