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