File: | client/battlescape/cl_hud.cpp |
Location: | line 914, column 3 |
Description: | Function call argument is an uninitialized value |
1 | /** | ||
2 | * @file | ||
3 | * @brief HUD related routines. | ||
4 | */ | ||
5 | |||
6 | /* | ||
7 | Copyright (C) 2002-2012 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. | ||
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 "../client.h" | ||
27 | #include "cl_localentity.h" | ||
28 | #include "cl_actor.h" | ||
29 | #include "cl_hud.h" | ||
30 | #include "cl_hud_callbacks.h" | ||
31 | #include "cl_view.h" | ||
32 | #include "../cgame/cl_game.h" | ||
33 | #include "../ui/ui_main.h" | ||
34 | #include "../ui/ui_popup.h" | ||
35 | #include "../ui/ui_nodes.h" | ||
36 | #include "../ui/ui_draw.h" | ||
37 | #include "../ui/ui_render.h" | ||
38 | #include "../ui/ui_tooltip.h" | ||
39 | #include "../renderer/r_mesh_anim.h" | ||
40 | #include "../renderer/r_draw.h" | ||
41 | #include "../../common/grid.h" | ||
42 | |||
43 | static cvar_t *cl_hud_message_timeout; | ||
44 | static cvar_t *cl_show_cursor_tooltips; | ||
45 | cvar_t *cl_worldlevel; | ||
46 | cvar_t *cl_hud; | ||
47 | |||
48 | enum { | ||
49 | REMAINING_TU_RELOAD_RIGHT, | ||
50 | REMAINING_TU_RELOAD_LEFT, | ||
51 | REMAINING_TU_CROUCH, | ||
52 | |||
53 | REMAINING_TU_MAX | ||
54 | }; | ||
55 | static bool displayRemainingTus[REMAINING_TU_MAX]; | ||
56 | |||
57 | typedef enum { | ||
58 | BT_RIGHT_FIRE, | ||
59 | BT_REACTION, | ||
60 | BT_LEFT_FIRE, | ||
61 | BT_RIGHT_RELOAD, | ||
62 | BT_LEFT_RELOAD, | ||
63 | BT_STAND, | ||
64 | BT_CROUCH, | ||
65 | BT_HEADGEAR, | ||
66 | |||
67 | BT_NUM_TYPES | ||
68 | } buttonTypes_t; | ||
69 | |||
70 | /** @brief a cbuf string for each button_types_t */ | ||
71 | static char const* const shootTypeStrings[] = { | ||
72 | "primaryright", | ||
73 | "reaction", | ||
74 | "primaryleft", | ||
75 | "reloadright", | ||
76 | "reloadleft", | ||
77 | "stand", | ||
78 | "crouch", | ||
79 | "headgear" | ||
80 | }; | ||
81 | CASSERT(lengthof(shootTypeStrings) == BT_NUM_TYPES)extern int ASSERT_COMPILE[(((sizeof(shootTypeStrings) / sizeof (*(shootTypeStrings))) == BT_NUM_TYPES) != 0) * 2 - 1]; | ||
82 | |||
83 | /** | ||
84 | * @brief Defines the various states of a button. | ||
85 | * @note Not all buttons do have all of these states (e.g. "unusable" is not very common). | ||
86 | */ | ||
87 | typedef enum { | ||
88 | BT_STATE_DISABLE, /**< 'Disabled' display (grey) */ | ||
89 | BT_STATE_DESELECT /**< Normal display (blue) */ | ||
90 | } weaponButtonState_t; | ||
91 | |||
92 | /** @note Order of elements here must correspond to order of elements in walkType_t. */ | ||
93 | static char const* const moveModeDescriptions[] = { | ||
94 | N_("Crouch walk")"Crouch walk", | ||
95 | N_("Autostand")"Autostand", | ||
96 | N_("Walk")"Walk", | ||
97 | N_("Crouch walk")"Crouch walk" | ||
98 | }; | ||
99 | CASSERT(lengthof(moveModeDescriptions) == WALKTYPE_MAX)extern int ASSERT_COMPILE[(((sizeof(moveModeDescriptions) / sizeof (*(moveModeDescriptions))) == WALKTYPE_MAX) != 0) * 2 - 1]; | ||
100 | |||
101 | typedef struct reserveShot_s { | ||
102 | actorHands_t hand; | ||
103 | int fireModeIndex; | ||
104 | int weaponIndex; | ||
105 | int TUs; | ||
106 | } reserveShot_t; | ||
107 | |||
108 | /** | ||
109 | * @brief Displays a message on the hud. | ||
110 | * @sa UI_DisplayNotice | ||
111 | * @param[in] text text is already translated here | ||
112 | */ | ||
113 | void HUD_DisplayMessage (const char *text) | ||
114 | { | ||
115 | assert(text)(__builtin_expect(!(text), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 115, "text") : (void)0); | ||
116 | UI_DisplayNotice(text, cl_hud_message_timeout->integer, cl_hud->string); | ||
117 | } | ||
118 | |||
119 | /** | ||
120 | * @brief Updates the global character cvars for battlescape. | ||
121 | * @note This is only called when we are in battlescape rendering mode | ||
122 | * It's assumed that every living actor - @c le_t - has a character assigned, too | ||
123 | */ | ||
124 | static void HUD_UpdateAllActors (void) | ||
125 | { | ||
126 | int i; | ||
127 | const size_t size = lengthof(cl.teamList)(sizeof(cl.teamList) / sizeof(*(cl.teamList))); | ||
128 | |||
129 | Cvar_SetValue("mn_numaliensspotted", cl.numEnemiesSpotted); | ||
130 | for (i = 0; i < size; i++) { | ||
131 | const le_t *le = cl.teamList[i]; | ||
132 | if (le && !LE_IsDead(le)((le)->state & 0x0003)) { | ||
133 | const invList_t *invList; | ||
134 | const char* tooltip; | ||
135 | const character_t *chr = CL_ActorGetChr(le); | ||
136 | assert(chr)(__builtin_expect(!(chr), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 136, "chr") : (void)0); | ||
137 | |||
138 | invList = RIGHT(le)(((le)->i.c[(csi.idRight)])); | ||
139 | if ((!invList || !invList->item.t || !invList->item.t->holdTwoHanded) && LEFT(le)(((le)->i.c[(csi.idLeft)]))) | ||
140 | invList = LEFT(le)(((le)->i.c[(csi.idLeft)])); | ||
141 | |||
142 | tooltip = va(_("%s\nHP: %i/%i TU: %i\n%s")gettext("%s\nHP: %i/%i TU: %i\n%s"), | ||
143 | chr->name, le->HP, le->maxHP, le->TU, (invList && invList->item.t) ? _(invList->item.t->name)gettext(invList->item.t->name) : ""); | ||
144 | |||
145 | UI_ExecuteConfunc("updateactorvalues %i \"%s\" \"%i\" \"%i\" \"%i\" \"%i\" \"%i\" \"%i\" \"%i\" \"%s\"", | ||
146 | i, le->model2->name, le->HP, le->maxHP, le->TU, le->maxTU, le->morale, le->maxMorale, le->STUN, tooltip); | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * @brief Sets the display for a single weapon/reload HUD button. | ||
153 | * @todo This should be a confunc which also sets the tooltips | ||
154 | */ | ||
155 | static void HUD_SetWeaponButton (buttonTypes_t button, weaponButtonState_t state) | ||
156 | { | ||
157 | const char *prefix; | ||
158 | |||
159 | switch (state) { | ||
160 | case BT_STATE_DESELECT: | ||
161 | prefix = "deselect_"; | ||
162 | break; | ||
163 | case BT_STATE_DISABLE: | ||
164 | prefix = "disable_"; | ||
165 | break; | ||
166 | default: | ||
167 | prefix = ""; | ||
168 | break; | ||
169 | } | ||
170 | |||
171 | /* Connect confunc strings to the ones as defined in "menu hud_nohud". */ | ||
172 | UI_ExecuteConfunc("%s%s", prefix, shootTypeStrings[button]); | ||
173 | } | ||
174 | |||
175 | /** | ||
176 | * @brief Returns the amount of usable "reaction fire" TUs for this actor (depends on active/inactive RF) | ||
177 | * @param[in] le The actor to check. | ||
178 | * @return The remaining/usable TUs for this actor | ||
179 | * @return -1 on error (this includes bad [very large] numbers stored in the struct). | ||
180 | * @todo Maybe only return "reaction" value if reaction-state is active? The value _should_ be 0, but one never knows :) | ||
181 | */ | ||
182 | static int HUD_UsableReactionTUs (const le_t * le) | ||
183 | { | ||
184 | /* Get the amount of usable TUs depending on the state (i.e. is RF on or off?) */ | ||
185 | if (le->state & STATE_REACTION0x0300) | ||
186 | /* CL_ActorUsableTUs DOES NOT return the stored value for "reaction" here. */ | ||
187 | return CL_ActorUsableTUs(le) + CL_ActorReservedTUs(le, RES_REACTION); | ||
188 | else | ||
189 | /* CL_ActorUsableTUs DOES return the stored value for "reaction" here. */ | ||
190 | return CL_ActorUsableTUs(le); | ||
191 | } | ||
192 | |||
193 | /** | ||
194 | * @brief Check if at least one firemode is available for reservation. | ||
195 | * @return true if there is at least one firemode - false otherwise. | ||
196 | * @sa HUD_RefreshButtons | ||
197 | * @sa HUD_PopupFiremodeReservation_f | ||
198 | */ | ||
199 | static bool HUD_CheckFiremodeReservation (void) | ||
200 | { | ||
201 | actorHands_t hand = ACTOR_HAND_RIGHT; | ||
202 | |||
203 | if (!selActor) | ||
204 | return false; | ||
205 | |||
206 | do { /* Loop for the 2 hands (l/r) to avoid unnecessary code-duplication and abstraction. */ | ||
207 | const fireDef_t *fireDef; | ||
208 | |||
209 | /* Get weapon (and its ammo) from the hand. */ | ||
210 | fireDef = HUD_GetFireDefinitionForHand(selActor, hand); | ||
211 | if (fireDef) { | ||
212 | int i; | ||
213 | const objDef_t *ammo = fireDef->obj; | ||
214 | for (i = 0; i < ammo->numFiredefs[fireDef->weapFdsIdx]; i++) { | ||
215 | /* Check if at least one firemode is available for reservation. */ | ||
216 | if (CL_ActorUsableTUs(selActor) + CL_ActorReservedTUs(selActor, RES_SHOT) >= ammo->fd[fireDef->weapFdsIdx][i].time) | ||
217 | return true; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | /* Prepare for next run or for end of loop. */ | ||
222 | if (hand == ACTOR_HAND_RIGHT) | ||
223 | hand = ACTOR_HAND_LEFT; | ||
224 | else | ||
225 | break; | ||
226 | } while (true); | ||
227 | |||
228 | /* No reservation possible */ | ||
229 | return false; | ||
230 | } | ||
231 | |||
232 | |||
233 | /** | ||
234 | * @brief Sets TU-reservation and firemode | ||
235 | * @param[in] le The local entity of the actor to change the tu reservation for. | ||
236 | * @param[in] tus How many TUs to set. | ||
237 | * @param[in] hand Store the given hand. | ||
238 | * @param[in] fireModeIndex Store the given firemode for this hand. | ||
239 | * @param[in] weapon Pointer to weapon in the hand. | ||
240 | */ | ||
241 | static void HUD_SetShootReservation (const le_t* le, const int tus, const actorHands_t hand, const int fireModeIndex, const objDef_t *weapon) | ||
242 | { | ||
243 | character_t* chr = CL_ActorGetChr(le); | ||
244 | assert(chr)(__builtin_expect(!(chr), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 244, "chr") : (void)0); | ||
245 | |||
246 | CL_ActorReserveTUs(le, RES_SHOT, tus); | ||
247 | CL_ActorSetShotSettings(chr, hand, fireModeIndex, weapon); | ||
248 | } | ||
249 | |||
250 | static linkedList_t* popupListData; | ||
251 | static uiNode_t* popupListNode; | ||
252 | |||
253 | /** | ||
254 | * @brief Creates a (text) list of all firemodes of the currently selected actor. | ||
255 | * @param[in] le The actor local entity | ||
256 | * @param[in] popupReload Prevent firemode reservation popup from being closed if | ||
257 | * no firemode is available because of insufficient TUs. | ||
258 | * @sa HUD_PopupFiremodeReservation_f | ||
259 | * @sa HUD_CheckFiremodeReservation | ||
260 | * @todo use components and confuncs here | ||
261 | */ | ||
262 | static void HUD_PopupFiremodeReservation (const le_t *le, bool popupReload) | ||
263 | { | ||
264 | actorHands_t hand = ACTOR_HAND_RIGHT; | ||
265 | int i; | ||
266 | static char text[MAX_VAR64]; | ||
267 | int selectedEntry; | ||
268 | linkedList_t* popupListText = NULL__null; | ||
269 | reserveShot_t reserveShotData; | ||
270 | |||
271 | /* reset the list */ | ||
272 | UI_ResetData(TEXT_LIST); | ||
273 | |||
274 | LIST_Delete(&popupListData); | ||
275 | |||
276 | /* Add list-entry for deactivation of the reservation. */ | ||
277 | LIST_AddPointer(&popupListText, _("[0 TU] No reservation")gettext("[0 TU] No reservation")); | ||
278 | reserveShotData.hand = ACTOR_HAND_NOT_SET; | ||
279 | reserveShotData.fireModeIndex = -1; | ||
280 | reserveShotData.weaponIndex = NONE-1; | ||
281 | reserveShotData.TUs = -1; | ||
282 | LIST_Add(&popupListData, reserveShotData); | ||
283 | selectedEntry = 0; | ||
284 | |||
285 | do { /* Loop for the 2 hands (l/r) to avoid unnecessary code-duplication and abstraction. */ | ||
286 | const fireDef_t *fd = HUD_GetFireDefinitionForHand(le, hand); | ||
287 | character_t* chr = CL_ActorGetChr(le); | ||
288 | assert(chr)(__builtin_expect(!(chr), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 288, "chr") : (void)0); | ||
289 | |||
290 | if (fd) { | ||
291 | const objDef_t *ammo = fd->obj; | ||
292 | |||
293 | for (i = 0; i < ammo->numFiredefs[fd->weapFdsIdx]; i++) { | ||
294 | const fireDef_t* ammoFD = &ammo->fd[fd->weapFdsIdx][i]; | ||
295 | if (CL_ActorUsableTUs(le) + CL_ActorReservedTUs(le, RES_SHOT) >= ammoFD->time) { | ||
296 | /* Get firemode name and TUs. */ | ||
297 | Com_sprintf(text, lengthof(text)(sizeof(text) / sizeof(*(text))), _("[%i TU] %s")gettext("[%i TU] %s"), ammoFD->time, _(ammoFD->name)gettext(ammoFD->name)); | ||
298 | |||
299 | /* Store text for popup */ | ||
300 | LIST_AddString(&popupListText, text); | ||
301 | |||
302 | /* Store Data for popup-callback. */ | ||
303 | reserveShotData.hand = hand; | ||
304 | reserveShotData.fireModeIndex = i; | ||
305 | reserveShotData.weaponIndex = ammo->weapons[fd->weapFdsIdx]->idx; | ||
306 | reserveShotData.TUs = ammoFD->time; | ||
307 | LIST_Add(&popupListData, reserveShotData); | ||
308 | |||
309 | /* Remember the line that is currently selected (if any). */ | ||
310 | if (chr->reservedTus.shotSettings.hand == hand | ||
311 | && chr->reservedTus.shotSettings.fmIdx == i | ||
312 | && chr->reservedTus.shotSettings.weapon == ammo->weapons[fd->weapFdsIdx]) | ||
313 | selectedEntry = LIST_Count(popupListData) - 1; | ||
314 | } | ||
315 | } | ||
316 | } | ||
317 | |||
318 | /* Prepare for next run or for end of loop. */ | ||
319 | if (hand == ACTOR_HAND_RIGHT) | ||
320 | /* First run. Set hand for second run of the loop (other hand) */ | ||
321 | hand = ACTOR_HAND_LEFT; | ||
322 | else | ||
323 | break; | ||
324 | } while (true); | ||
325 | |||
326 | if (LIST_Count(popupListData) > 1 || popupReload) { | ||
327 | /* We have more entries than the "0 TUs" one | ||
328 | * or we want to simply refresh/display the popup content (no matter how many TUs are left). */ | ||
329 | popupListNode = UI_PopupList(_("Shot Reservation")gettext("Shot Reservation"), _("Reserve TUs for firing/using.")gettext("Reserve TUs for firing/using."), popupListText, "hud_shotreserve <lineselected>"); | ||
330 | /* Set color for selected entry. */ | ||
331 | VectorSet(popupListNode->selectedColor, 0.0, 0.78, 0.0)((popupListNode->selectedColor)[0]=(0.0), (popupListNode-> selectedColor)[1]=(0.78), (popupListNode->selectedColor)[2 ]=(0.0)); | ||
332 | popupListNode->selectedColor[3] = 1.0; | ||
333 | UI_TextNodeSelectLine(popupListNode, selectedEntry); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | /** | ||
338 | * @brief Creates a (text) list of all firemodes of the currently selected actor. | ||
339 | * @sa HUD_PopupFiremodeReservation | ||
340 | */ | ||
341 | static void HUD_PopupFiremodeReservation_f (void) | ||
342 | { | ||
343 | if (!selActor) | ||
344 | return; | ||
345 | |||
346 | /* A second parameter (the value itself will be ignored) was given. | ||
347 | * This is used to reset the shot-reservation.*/ | ||
348 | if (Cmd_Argc() == 2) { | ||
349 | HUD_SetShootReservation(selActor, 0, ACTOR_HAND_NOT_SET, -1, NULL__null); | ||
350 | } else { | ||
351 | HUD_PopupFiremodeReservation(selActor, false); | ||
352 | } | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * @brief Get selected firemode in the list of the currently selected actor. | ||
357 | * @sa HUD_PopupFiremodeReservation_f | ||
358 | */ | ||
359 | static void HUD_ShotReserve_f (void) | ||
360 | { | ||
361 | int selectedPopupIndex; | ||
362 | const reserveShot_t* reserveShotData; | ||
363 | |||
364 | if (Cmd_Argc() < 2) { | ||
365 | Com_Printf("Usage: %s <popupindex>\n", Cmd_Argv(0)); | ||
366 | return; | ||
367 | } | ||
368 | |||
369 | if (!selActor) | ||
370 | return; | ||
371 | |||
372 | /* read and range check */ | ||
373 | selectedPopupIndex = atoi(Cmd_Argv(1)); | ||
374 | if (selectedPopupIndex < 0 || selectedPopupIndex >= LIST_Count(popupListData)) | ||
375 | return; | ||
376 | |||
377 | reserveShotData = (const reserveShot_t *)LIST_GetByIdx(popupListData, selectedPopupIndex); | ||
378 | if (!reserveShotData) | ||
379 | return; | ||
380 | |||
381 | if (reserveShotData->weaponIndex == NONE-1) { | ||
382 | HUD_SetShootReservation(selActor, 0, ACTOR_HAND_NOT_SET, -1, NULL__null); | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | /** @todo do this on the server */ | ||
387 | /* Check if we have enough TUs (again) */ | ||
388 | if (CL_ActorUsableTUs(selActor) + CL_ActorReservedTUs(selActor, RES_SHOT) >= reserveShotData->TUs) { | ||
389 | const objDef_t *od = INVSH_GetItemByIDX(reserveShotData->weaponIndex); | ||
390 | if (GAME_ItemIsUseable(od)) { | ||
391 | HUD_SetShootReservation(selActor, std::max(0, reserveShotData->TUs), reserveShotData->hand, reserveShotData->fireModeIndex, od); | ||
392 | if (popupListNode) | ||
393 | UI_TextNodeSelectLine(popupListNode, selectedPopupIndex); | ||
394 | } | ||
395 | } | ||
396 | } | ||
397 | |||
398 | /** | ||
399 | * @brief Sets the display for a single weapon/reload HUD button. | ||
400 | * @param[in] hand What list to display | ||
401 | */ | ||
402 | static void HUD_DisplayFiremodeEntry (const char* callback, const le_t* actor, const objDef_t* ammo, const weaponFireDefIndex_t weapFdsIdx, const actorHands_t hand, int index) | ||
403 | { | ||
404 | int usableTusForRF; | ||
405 | char tuString[MAX_VAR64]; | ||
406 | bool status; | ||
407 | const fireDef_t *fd; | ||
408 | const char *tooltip; | ||
409 | char id[32]; | ||
410 | |||
411 | if (index < ammo->numFiredefs[weapFdsIdx]) { | ||
412 | /* We have a defined fd ... */ | ||
413 | fd = &ammo->fd[weapFdsIdx][index]; | ||
414 | } else { | ||
415 | return; | ||
416 | } | ||
417 | |||
418 | assert(actor)(__builtin_expect(!(actor), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 418, "actor") : (void)0); | ||
419 | assert(hand == ACTOR_HAND_RIGHT || hand == ACTOR_HAND_LEFT)(__builtin_expect(!(hand == ACTOR_HAND_RIGHT || hand == ACTOR_HAND_LEFT ), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 419, "hand == ACTOR_HAND_RIGHT || hand == ACTOR_HAND_LEFT") : (void)0); | ||
420 | |||
421 | status = fd->time <= CL_ActorUsableTUs(actor); | ||
422 | usableTusForRF = HUD_UsableReactionTUs(actor); | ||
423 | |||
424 | if (usableTusForRF > fd->time) { | ||
425 | Com_sprintf(tuString, sizeof(tuString), _("Remaining TUs: %i")gettext("Remaining TUs: %i"), usableTusForRF - fd->time); | ||
426 | tooltip = tuString; | ||
427 | } else | ||
428 | tooltip = _("No remaining TUs left after shot.")gettext("No remaining TUs left after shot."); | ||
429 | |||
430 | /* unique identifier of the action */ | ||
431 | /* @todo use this id as action callback instead of hand and index (we can extend it with any other soldier action we need (open door, reload...)) */ | ||
432 | Com_sprintf(id, sizeof(id), "fire_hand%c_i%i", ACTOR_GET_HAND_CHAR(hand)((hand) == ACTOR_HAND_LEFT ? (char)'l' : (char)'r'), index); | ||
433 | |||
434 | UI_ExecuteConfunc("%s firemode %s %c %i %i %i \"%s\" \"%i\" \"%i\" \"%s\"", callback, id, ACTOR_GET_HAND_CHAR(hand)((hand) == ACTOR_HAND_LEFT ? (char)'l' : (char)'r'), | ||
435 | fd->fdIdx, fd->reaction, status, _(fd->name)gettext(fd->name), fd->time, fd->ammo, tooltip); | ||
436 | |||
437 | /* Display checkbox for reaction firemode */ | ||
438 | if (fd->reaction) { | ||
439 | character_t* chr = CL_ActorGetChr(actor); | ||
440 | const bool active = THIS_FIREMODE(&chr->RFmode, hand, fd->fdIdx)((&chr->RFmode)->hand == (hand) && (&chr ->RFmode)->fmIdx == (fd->fdIdx)); | ||
441 | /* Change the state of the checkbox. */ | ||
442 | UI_ExecuteConfunc("%s reaction %s %c %i", callback, id, ACTOR_GET_HAND_CHAR(hand)((hand) == ACTOR_HAND_LEFT ? (char)'l' : (char)'r'), active); | ||
443 | } | ||
444 | } | ||
445 | |||
446 | /** | ||
447 | * List actions from a soldier to a callback confunc | ||
448 | * @param callback confunc callback | ||
449 | * @param actor actor who can do the actions | ||
450 | * @param right if true, list right firemode | ||
451 | * @param left if true, list left firemode | ||
452 | * @param reloadRight if true, list right weapon reload actions | ||
453 | * @param reloadLeft if true, list left weapon reload actions | ||
454 | * @todo we can extend it with short cut equip action, more reload, action on the map (like open doors)... | ||
455 | */ | ||
456 | static void HUD_DisplayActions (const char* callback, const le_t* actor, bool right, bool left, bool reloadRight, bool reloadLeft) | ||
457 | { | ||
458 | const objDef_t *ammo; | ||
459 | const fireDef_t *fd; | ||
460 | int i; | ||
461 | |||
462 | if (!actor) | ||
463 | return; | ||
464 | |||
465 | if (cls.team != cl.actTeam) { /**< Not our turn */ | ||
466 | return; | ||
467 | } | ||
468 | |||
469 | UI_ExecuteConfunc("%s begin", callback); | ||
470 | |||
471 | if (right) { | ||
472 | const actorHands_t hand = ACTOR_HAND_RIGHT; | ||
473 | fd = HUD_GetFireDefinitionForHand(actor, hand); | ||
474 | if (fd == NULL__null) | ||
475 | return; | ||
476 | |||
477 | ammo = fd->obj; | ||
478 | if (!ammo) { | ||
479 | Com_DPrintf(DEBUG_CLIENT0x20, "HUD_DisplayFiremodes: no weapon or ammo found.\n"); | ||
480 | return; | ||
481 | } | ||
482 | |||
483 | for (i = 0; i < MAX_FIREDEFS_PER_WEAPON8; i++) { | ||
484 | /* Display the firemode information (image + text). */ | ||
485 | HUD_DisplayFiremodeEntry(callback, actor, ammo, fd->weapFdsIdx, hand, i); | ||
486 | } | ||
487 | } | ||
488 | |||
489 | if (reloadRight) { | ||
490 | invList_t* weapon = RIGHT(actor)(((actor)->i.c[(csi.idRight)])); | ||
491 | |||
492 | /* Reloeadable item in hand. */ | ||
493 | if (weapon && weapon->item.t && weapon->item.t->reload) { | ||
494 | int tus; | ||
495 | containerIndex_t container = csi.idRight; | ||
496 | bool noAmmo; | ||
497 | bool noTU; | ||
498 | const char *actionId = "reload_handr"; | ||
499 | |||
500 | tus = HUD_CalcReloadTime(actor, weapon->item.t, container); | ||
501 | noAmmo = tus == -1; | ||
502 | noTU = actor->TU < tus; | ||
503 | UI_ExecuteConfunc("%s reload %s %c %i %i %i", callback, actionId, 'r', tus, !noAmmo, !noTU); | ||
504 | } | ||
505 | } | ||
506 | |||
507 | if (left) { | ||
508 | const actorHands_t hand = ACTOR_HAND_LEFT; | ||
509 | fd = HUD_GetFireDefinitionForHand(actor, hand); | ||
510 | if (fd == NULL__null) | ||
511 | return; | ||
512 | |||
513 | ammo = fd->obj; | ||
514 | if (!ammo) { | ||
515 | Com_DPrintf(DEBUG_CLIENT0x20, "HUD_DisplayFiremodes: no weapon or ammo found.\n"); | ||
516 | return; | ||
517 | } | ||
518 | |||
519 | for (i = 0; i < MAX_FIREDEFS_PER_WEAPON8; i++) { | ||
520 | /* Display the firemode information (image + text). */ | ||
521 | HUD_DisplayFiremodeEntry(callback, actor, ammo, fd->weapFdsIdx, hand, i); | ||
522 | } | ||
523 | } | ||
524 | |||
525 | |||
526 | if (reloadLeft) { | ||
527 | invList_t* weapon = LEFT(actor)(((actor)->i.c[(csi.idLeft)])); | ||
528 | |||
529 | /* Reloeadable item in hand. */ | ||
530 | if (weapon && weapon->item.t && weapon->item.t->reload) { | ||
531 | int tus; | ||
532 | containerIndex_t container = csi.idLeft; | ||
533 | bool noAmmo; | ||
534 | bool noTU; | ||
535 | const char *actionId = "reload_handl"; | ||
536 | |||
537 | tus = HUD_CalcReloadTime(actor, weapon->item.t, container); | ||
538 | noAmmo = tus == -1; | ||
539 | noTU = actor->TU < tus; | ||
540 | UI_ExecuteConfunc("%s reload %s %c %i %i %i", callback, actionId, 'l', tus, !noAmmo, !noTU); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | UI_ExecuteConfunc("%s end", callback); | ||
545 | } | ||
546 | |||
547 | /** | ||
548 | * @brief Displays the firemodes for the given hand. | ||
549 | */ | ||
550 | static void HUD_DisplayActions_f (void) | ||
551 | { | ||
552 | char callback[32]; | ||
553 | bool right; | ||
554 | bool left; | ||
555 | bool rightReload; | ||
556 | bool leftReload; | ||
557 | |||
558 | if (!selActor) | ||
559 | return; | ||
560 | |||
561 | right = strchr(Cmd_Argv(2), 'r') != NULL__null; | ||
562 | left = strchr(Cmd_Argv(2), 'l') != NULL__null; | ||
563 | rightReload = strchr(Cmd_Argv(2), 'R') != NULL__null; | ||
564 | leftReload = strchr(Cmd_Argv(2), 'L') != NULL__null; | ||
565 | |||
566 | Q_strncpyz(callback, Cmd_Argv(1), sizeof(callback))Q_strncpyzDebug( callback, Cmd_Argv(1), sizeof(callback), "src/client/battlescape/cl_hud.cpp" , 566 ); | ||
567 | HUD_DisplayActions(callback, selActor, right, left, rightReload, leftReload); | ||
568 | } | ||
569 | |||
570 | /** | ||
571 | * @brief Displays the firemodes for the given hand. | ||
572 | */ | ||
573 | static void HUD_DisplayFiremodes_f (void) | ||
574 | { | ||
575 | actorHands_t hand; | ||
576 | char callback[32]; | ||
577 | |||
578 | if (!selActor) | ||
579 | return; | ||
580 | |||
581 | if (Cmd_Argc() < 3) | ||
582 | /* no argument given */ | ||
583 | hand = ACTOR_HAND_RIGHT; | ||
584 | else | ||
585 | hand = ACTOR_GET_HAND_INDEX(Cmd_Argv(2)[0])((Cmd_Argv(2)[0]) == (char)'l' ? ACTOR_HAND_LEFT : ACTOR_HAND_RIGHT ); | ||
586 | |||
587 | Q_strncpyz(callback, Cmd_Argv(1), sizeof(callback))Q_strncpyzDebug( callback, Cmd_Argv(1), sizeof(callback), "src/client/battlescape/cl_hud.cpp" , 587 ); | ||
588 | HUD_DisplayActions(callback, selActor, hand == ACTOR_HAND_RIGHT, hand == ACTOR_HAND_LEFT, false, false); | ||
589 | } | ||
590 | |||
591 | /** | ||
592 | * @brief Changes the display of the firemode-list to a given hand, but only if the list is visible already. | ||
593 | * @todo Delete that function: Should be done from within the scripts | ||
594 | */ | ||
595 | static void HUD_SwitchFiremodeList_f (void) | ||
596 | { | ||
597 | /* no argument given */ | ||
598 | if (Cmd_Argc() < 2) { | ||
599 | Com_Printf("Usage: %s callback [l|r]\n", Cmd_Argv(0)); | ||
600 | return; | ||
601 | } | ||
602 | |||
603 | { | ||
604 | char callback[32]; | ||
605 | actorHands_t hand; | ||
606 | hand = ACTOR_GET_HAND_INDEX(Cmd_Argv(2)[0])((Cmd_Argv(2)[0]) == (char)'l' ? ACTOR_HAND_LEFT : ACTOR_HAND_RIGHT ); | ||
607 | Q_strncpyz(callback, Cmd_Argv(1), sizeof(callback))Q_strncpyzDebug( callback, Cmd_Argv(1), sizeof(callback), "src/client/battlescape/cl_hud.cpp" , 607 ); | ||
608 | HUD_DisplayActions(callback, selActor, hand == ACTOR_HAND_RIGHT, hand == ACTOR_HAND_LEFT, false, false); | ||
609 | } | ||
610 | } | ||
611 | |||
612 | /** | ||
613 | * @brief Updates the information in RFmode for the selected actor with the given data from the parameters. | ||
614 | * @param[in] actor The actor we want to update the reaction-fire firemode for. | ||
615 | * @param[in] hand Which weapon(-hand) to use. | ||
616 | * @param[in] firemodeActive Set this to the firemode index you want to activate or set it to -1 if the default one (currently the first one found) should be used. | ||
617 | */ | ||
618 | static void HUD_UpdateReactionFiremodes (const le_t * actor, const actorHands_t hand, fireDefIndex_t firemodeActive) | ||
619 | { | ||
620 | const fireDef_t *fd; | ||
621 | const objDef_t *ammo, *od; | ||
622 | |||
623 | assert(actor)(__builtin_expect(!(actor), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 623, "actor") : (void)0); | ||
624 | |||
625 | fd = HUD_GetFireDefinitionForHand(actor, hand); | ||
626 | if (fd == NULL__null) | ||
627 | return; | ||
628 | |||
629 | ammo = fd->obj; | ||
630 | od = ammo->weapons[fd->weapFdsIdx]; | ||
631 | |||
632 | if (!GAME_ItemIsUseable(od)) | ||
633 | return; | ||
634 | |||
635 | MSG_Write_PA(PA_REACT_SELECT, actor->entnum, hand, firemodeActive, od ? od->idx : NONE-1); | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * @brief Checks if the selected firemode checkbox is ok as a reaction firemode and updates data+display. | ||
640 | */ | ||
641 | static void HUD_SelectReactionFiremode_f (void) | ||
642 | { | ||
643 | actorHands_t hand; | ||
644 | fireDefIndex_t firemode; | ||
645 | |||
646 | if (Cmd_Argc() < 3) { /* no argument given */ | ||
647 | Com_Printf("Usage: %s [l|r] <num> num=firemode number\n", Cmd_Argv(0)); | ||
648 | return; | ||
649 | } | ||
650 | |||
651 | if (!selActor) | ||
652 | return; | ||
653 | |||
654 | hand = ACTOR_GET_HAND_INDEX(Cmd_Argv(1)[0])((Cmd_Argv(1)[0]) == (char)'l' ? ACTOR_HAND_LEFT : ACTOR_HAND_RIGHT ); | ||
655 | firemode = atoi(Cmd_Argv(2)); | ||
656 | |||
657 | if (firemode >= MAX_FIREDEFS_PER_WEAPON8 || firemode < 0) { | ||
658 | Com_Printf("HUD_SelectReactionFiremode_f: Firemode out of bounds (%i).\n", firemode); | ||
659 | return; | ||
660 | } | ||
661 | |||
662 | HUD_UpdateReactionFiremodes(selActor, hand, firemode); | ||
663 | } | ||
664 | |||
665 | /** | ||
666 | * @brief Remember if we hover over a button that would cost some TUs when pressed. | ||
667 | * @note this is used in HUD_Update to update the "remaining TUs" bar correctly. | ||
668 | */ | ||
669 | static void HUD_RemainingTUs_f (void) | ||
670 | { | ||
671 | bool state; | ||
672 | const char *type; | ||
673 | |||
674 | if (Cmd_Argc() < 3) { | ||
675 | Com_Printf("Usage: %s <type> <popupindex>\n", Cmd_Argv(0)); | ||
676 | return; | ||
677 | } | ||
678 | |||
679 | type = Cmd_Argv(1); | ||
680 | state = Com_ParseBoolean(Cmd_Argv(2)); | ||
681 | |||
682 | OBJZERO(displayRemainingTus)(memset(&((displayRemainingTus)), (0), sizeof((displayRemainingTus )))); | ||
683 | |||
684 | if (Q_streq(type, "reload_r")(strcmp(type, "reload_r") == 0)) { | ||
685 | displayRemainingTus[REMAINING_TU_RELOAD_RIGHT] = state; | ||
686 | } else if (Q_streq(type, "reload_l")(strcmp(type, "reload_l") == 0)) { | ||
687 | displayRemainingTus[REMAINING_TU_RELOAD_LEFT] = state; | ||
688 | } else if (Q_streq(type, "crouch")(strcmp(type, "crouch") == 0)) { | ||
689 | displayRemainingTus[REMAINING_TU_CROUCH] = state; | ||
690 | } | ||
691 | } | ||
692 | |||
693 | /** | ||
694 | * @return The minimum time needed to fire the weapons in the given @c invList | ||
695 | */ | ||
696 | static int HUD_GetMinimumTUsForUsage (const invList_t *invList) | ||
697 | { | ||
698 | const fireDef_t *fdArray; | ||
699 | int time = 100; | ||
700 | int i; | ||
701 | |||
702 | assert(invList->item.t)(__builtin_expect(!(invList->item.t), 0) ? __assert_rtn(__func__ , "src/client/battlescape/cl_hud.cpp", 702, "invList->item.t" ) : (void)0); | ||
703 | |||
704 | fdArray = FIRESH_FiredefForWeapon(&invList->item); | ||
705 | if (fdArray == NULL__null) | ||
706 | return time; | ||
707 | |||
708 | /* Search for the smallest TU needed to shoot. */ | ||
709 | for (i = 0; i < MAX_FIREDEFS_PER_WEAPON8; i++) { | ||
710 | if (!fdArray[i].time) | ||
711 | continue; | ||
712 | if (fdArray[i].time < time) | ||
713 | time = fdArray[i].time; | ||
714 | } | ||
715 | |||
716 | return time; | ||
717 | } | ||
718 | |||
719 | /** | ||
720 | * @brief Checks every case for reload buttons on the HUD. | ||
721 | * @param[in] le Pointer of local entity being an actor. | ||
722 | * @param[in] containerID of the container to reload the weapon in. Used to get the movement TUs for moving something into the container. | ||
723 | * @param[out] reason The reason why the reload didn't work - only set if @c -1 is the return value | ||
724 | * @return TU units needed for reloading or -1 if weapon cannot be reloaded. | ||
725 | */ | ||
726 | static int HUD_WeaponCanBeReloaded (const le_t *le, containerIndex_t containerID, const char **reason) | ||
727 | { | ||
728 | const int tu = CL_ActorUsableTUs(le); | ||
729 | const invList_t *invList = CONTAINER(le, containerID)((le)->i.c[(containerID)]); | ||
730 | const objDef_t *weapon; | ||
731 | |||
732 | assert(le)(__builtin_expect(!(le), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 732, "le") : (void)0); | ||
733 | |||
734 | /* No weapon in hand. */ | ||
735 | if (!invList) { | ||
736 | *reason = _("No weapon.")gettext("No weapon."); | ||
737 | return -1; | ||
738 | } | ||
739 | |||
740 | weapon = invList->item.t; | ||
741 | assert(weapon)(__builtin_expect(!(weapon), 0) ? __assert_rtn(__func__, "src/client/battlescape/cl_hud.cpp" , 741, "weapon") : (void)0); | ||
742 | |||
743 | /* This weapon cannot be reloaded. */ | ||
744 | if (!weapon->reload) { | ||
745 | *reason = _("Weapon cannot be reloaded.")gettext("Weapon cannot be reloaded."); | ||
746 | return -1; | ||
747 | } | ||
748 | |||
749 | /* Weapon is fully loaded. */ | ||
750 | if (invList->item.m && weapon->ammo == invList->item.a) { | ||
751 | *reason = _("No reload possible, already fully loaded.")gettext("No reload possible, already fully loaded."); | ||
752 | return -1; | ||
753 | } | ||
754 | |||
755 | /* Weapon is empty or not fully loaded, find ammo of any type loadable to this weapon. */ | ||
756 | if (!invList->item.m || weapon->ammo > invList->item.a) { | ||
757 | const int tuCosts = HUD_CalcReloadTime(le, weapon, containerID); | ||
758 | if (tuCosts >= 0) { | ||
759 | if (tu >= tuCosts) | ||
760 | return tuCosts; | ||
761 | *reason = _("Not enough TUs for reloading weapon.")gettext("Not enough TUs for reloading weapon."); | ||
762 | } else { | ||
763 | /* Found no ammo which could be used for this weapon. */ | ||
764 | *reason = _("No reload possible, you don't have backup ammo.")gettext("No reload possible, you don't have backup ammo."); | ||
765 | } | ||
766 | } | ||
767 | |||
768 | return -1; | ||
769 | } | ||
770 | |||
771 | /** | ||
772 | * @brief Checks if there is a weapon in the hand that can be used for reaction fire. | ||
773 | * @param[in] actor What actor to check. | ||
774 | */ | ||
775 | static bool HUD_WeaponWithReaction (const le_t * actor) | ||
776 | { | ||
777 | const objDef_t *weapon = INVSH_HasReactionFireEnabledWeapon(RIGHT(actor)(((actor)->i.c[(csi.idRight)]))); | ||
778 | if (weapon) | ||
779 | return true; | ||
780 | return INVSH_HasReactionFireEnabledWeapon(LEFT(actor)(((actor)->i.c[(csi.idLeft)]))) != NULL__null; | ||
781 | } | ||
782 | |||
783 | /** | ||
784 | * @brief Display 'impossible" (red) reaction buttons. | ||
785 | * @param[in] actor the actor to check for his reaction state. | ||
786 | * @return true if nothing changed message was sent otherwise false. | ||
787 | */ | ||
788 | static bool HUD_DisplayImpossibleReaction (const le_t * actor) | ||
789 | { | ||
790 | if (!actor) | ||
791 | return false; | ||
792 | |||
793 | /* Given actor does not equal the currently selected actor. */ | ||
794 | if (!actor->selected) | ||
795 | return false; | ||
796 | |||
797 | /* Display 'impossible" (red) reaction buttons */ | ||
798 | if (actor->state & STATE_REACTION0x0300) { | ||
799 | UI_ExecuteConfunc("startreaction_impos"); | ||
800 | return false; | ||
801 | } | ||
802 | |||
803 | return true; | ||
804 | } | ||
805 | |||
806 | /** | ||
807 | * @brief Display 'usable" (blue) reaction buttons. | ||
808 | * @param[in] actor the actor to check for his reaction state. | ||
809 | */ | ||
810 | static void HUD_DisplayPossibleReaction (const le_t * actor) | ||
811 | { | ||
812 | if (!actor) | ||
813 | return; | ||
814 | |||
815 | /* Given actor does not equal the currently selected actor. This normally only happens on game-start. */ | ||
816 | if (!actor->selected) | ||
817 | return; | ||
818 | |||
819 | /* Display 'usable" (blue) reaction buttons */ | ||
820 | if (actor->state & STATE_REACTION0x0300) | ||
821 | UI_ExecuteConfunc("startreaction"); | ||
822 | } | ||
823 | |||
824 | /** | ||
825 | * @brief Refreshes the weapon/reload buttons on the HUD. | ||
826 | * @param[in] le Pointer to local entity for which we refresh HUD buttons. | ||
827 | */ | ||
828 | static void HUD_RefreshButtons (const le_t *le) | ||
829 | { | ||
830 | invList_t *weaponr; | ||
831 | invList_t *weaponl; | ||
832 | invList_t *headgear; | ||
833 | int rightCanBeReloaded, leftCanBeReloaded; | ||
834 | const int time = CL_ActorUsableTUs(le); | ||
835 | const char *reason; | ||
836 | |||
837 | if (!le) | ||
| |||
838 | return; | ||
839 | |||
840 | weaponr = RIGHT(le)(((le)->i.c[(csi.idRight)])); | ||
841 | headgear = HEADGEAR(le)(((le)->i.c[(csi.idHeadgear)])); | ||
842 | |||
843 | /* check for two-handed weapon - if not, also define weaponl */ | ||
844 | if (!weaponr || !weaponr->item.t->holdTwoHanded) | ||
| |||
845 | weaponl = LEFT(le)(((le)->i.c[(csi.idLeft)])); | ||
846 | else | ||
847 | weaponl = NULL__null; | ||
848 | |||
849 | /* Crouch/stand button. */ | ||
850 | if (LE_IsCrouched(le)((le)->state & 0x0004)) { | ||
| |||
851 | if (time + CL_ActorReservedTUs(le, RES_CROUCH) < TU_CROUCH3) { | ||
852 | Cvar_Set("mn_crouchstand_tt", _("Not enough TUs for standing up.")gettext("Not enough TUs for standing up.")); | ||
853 | HUD_SetWeaponButton(BT_CROUCH, BT_STATE_DISABLE); | ||
854 | } else { | ||
855 | Cvar_Set("mn_crouchstand_tt", va(_("Stand up (%i TU)")gettext("Stand up (%i TU)"), TU_CROUCH3)); | ||
856 | HUD_SetWeaponButton(BT_CROUCH, BT_STATE_DESELECT); | ||
857 | } | ||
858 | } else { | ||
859 | if (time + CL_ActorReservedTUs(le, RES_CROUCH) < TU_CROUCH3) { | ||
| |||
860 | Cvar_Set("mn_crouchstand_tt", _("Not enough TUs for crouching.")gettext("Not enough TUs for crouching.")); | ||
861 | HUD_SetWeaponButton(BT_STAND, BT_STATE_DISABLE); | ||
862 | } else { | ||
863 | Cvar_Set("mn_crouchstand_tt", va(_("Crouch (%i TU)")gettext("Crouch (%i TU)"), TU_CROUCH3)); | ||
864 | HUD_SetWeaponButton(BT_STAND, BT_STATE_DESELECT); | ||
865 | } | ||
866 | } | ||
867 | |||
868 | /* Crouch/stand reservation checkbox. */ | ||
869 | if (CL_ActorReservedTUs(le, RES_CROUCH) >= TU_CROUCH3) { | ||
| |||
870 | UI_ExecuteConfunc("crouch_checkbox_check"); | ||
871 | Cvar_Set("mn_crouch_reservation_tt", va(_("%i TUs reserved for crouching/standing up.\nClick to clear.")gettext("%i TUs reserved for crouching/standing up.\nClick to clear." ), | ||
872 | CL_ActorReservedTUs(le, RES_CROUCH))); | ||
873 | } else if (time >= TU_CROUCH3) { | ||
874 | UI_ExecuteConfunc("crouch_checkbox_clear"); | ||
875 | Cvar_Set("mn_crouch_reservation_tt", va(_("Reserve %i TUs for crouching/standing up.")gettext("Reserve %i TUs for crouching/standing up."), TU_CROUCH3)); | ||
876 | } else { | ||
877 | UI_ExecuteConfunc("crouch_checkbox_disable"); | ||
878 | Cvar_Set("mn_crouch_reservation_tt", _("Not enough TUs left to reserve for crouching/standing up.")gettext("Not enough TUs left to reserve for crouching/standing up." )); | ||
879 | } | ||
880 | |||
881 | /* Shot reservation button. mn_shot_reservation_tt is the tooltip text */ | ||
882 | if (CL_ActorReservedTUs(le, RES_SHOT)) { | ||
| |||
883 | UI_ExecuteConfunc("reserve_shot_check"); | ||
884 | Cvar_Set("mn_shot_reservation_tt", va(_("%i TUs reserved for shooting.\nClick to change.\nRight-Click to clear.")gettext("%i TUs reserved for shooting.\nClick to change.\nRight-Click to clear." ), | ||
885 | CL_ActorReservedTUs(le, RES_SHOT))); | ||
886 | } else if (HUD_CheckFiremodeReservation()) { | ||
| |||
887 | UI_ExecuteConfunc("reserve_shot_clear"); | ||
888 | Cvar_Set("mn_shot_reservation_tt", _("Reserve TUs for shooting.")gettext("Reserve TUs for shooting.")); | ||
889 | } else { | ||
890 | UI_ExecuteConfunc("reserve_shot_disable"); | ||
891 | Cvar_Set("mn_shot_reservation_tt", _("Reserving TUs for shooting not possible.")gettext("Reserving TUs for shooting not possible.")); | ||
892 | } | ||
893 | |||
894 | /* reaction-fire button */ | ||
895 | if (!(le->state & STATE_REACTION0x0300)) { | ||
| |||
896 | if (time >= CL_ActorReservedTUs(le, RES_REACTION) && HUD_WeaponWithReaction(le)) | ||
| |||
897 | HUD_SetWeaponButton(BT_REACTION, BT_STATE_DESELECT); | ||
898 | else | ||
899 | HUD_SetWeaponButton(BT_REACTION, BT_STATE_DISABLE); | ||
900 | } else { | ||
901 | if (HUD_WeaponWithReaction(le)) { | ||
902 | HUD_DisplayPossibleReaction(le); | ||
903 | } else { | ||
904 | HUD_DisplayImpossibleReaction(le); | ||
905 | } | ||
906 | } | ||
907 | |||
908 | /* Reload buttons */ | ||
909 | rightCanBeReloaded = HUD_WeaponCanBeReloaded(le, csi.idRight, &reason); | ||
910 | if (rightCanBeReloaded != -1) { | ||
| |||
911 | HUD_SetWeaponButton(BT_RIGHT_RELOAD, BT_STATE_DESELECT); | ||
912 | Cvar_Set("mn_reloadright_tt", va(_("Reload weapon (%i TU).")gettext("Reload weapon (%i TU)."), rightCanBeReloaded)); | ||
913 | } else { | ||
914 | Cvar_Set("mn_reloadright_tt", reason); | ||
| |||
915 | HUD_SetWeaponButton(BT_RIGHT_RELOAD, BT_STATE_DISABLE); | ||
916 | } | ||
917 | |||
918 | leftCanBeReloaded = HUD_WeaponCanBeReloaded(le, csi.idLeft, &reason); | ||
919 | if (leftCanBeReloaded != -1) { | ||
920 | HUD_SetWeaponButton(BT_LEFT_RELOAD, BT_STATE_DESELECT); | ||
921 | Cvar_Set("mn_reloadleft_tt", va(_("Reload weapon (%i TU).")gettext("Reload weapon (%i TU)."), leftCanBeReloaded)); | ||
922 | } else { | ||
923 | Cvar_Set("mn_reloadleft_tt", reason); | ||
924 | HUD_SetWeaponButton(BT_LEFT_RELOAD, BT_STATE_DISABLE); | ||
925 | } | ||
926 | |||
927 | /* Headgear button */ | ||
928 | if (headgear) { | ||
929 | const int minheadgeartime = HUD_GetMinimumTUsForUsage(headgear); | ||
930 | if (time < minheadgeartime) | ||
931 | HUD_SetWeaponButton(BT_HEADGEAR, BT_STATE_DISABLE); | ||
932 | else | ||
933 | HUD_SetWeaponButton(BT_HEADGEAR, BT_STATE_DESELECT); | ||
934 | } else { | ||
935 | HUD_SetWeaponButton(BT_HEADGEAR, BT_STATE_DISABLE); | ||
936 | } | ||
937 | |||
938 | /* Weapon firing buttons. */ | ||
939 | if (weaponr) { | ||
940 | const int minweaponrtime = HUD_GetMinimumTUsForUsage(weaponr); | ||
941 | if (time < minweaponrtime) | ||
942 | HUD_SetWeaponButton(BT_RIGHT_FIRE, BT_STATE_DISABLE); | ||
943 | else | ||
944 | HUD_SetWeaponButton(BT_RIGHT_FIRE, BT_STATE_DESELECT); | ||
945 | } else { | ||
946 | HUD_SetWeaponButton(BT_RIGHT_FIRE, BT_STATE_DISABLE); | ||
947 | } | ||
948 | |||
949 | if (weaponl) { | ||
950 | const int minweaponltime = HUD_GetMinimumTUsForUsage(weaponl); | ||
951 | if (time < minweaponltime) | ||
952 | HUD_SetWeaponButton(BT_LEFT_FIRE, BT_STATE_DISABLE); | ||
953 | else | ||
954 | HUD_SetWeaponButton(BT_LEFT_FIRE, BT_STATE_DESELECT); | ||
955 | } else { | ||
956 | HUD_SetWeaponButton(BT_LEFT_FIRE, BT_STATE_DISABLE); | ||
957 | } | ||
958 | |||
959 | /* Check if the firemode reservation popup is shown and refresh its content. (i.e. close&open it) */ | ||
960 | { | ||
961 | const char* menuName = UI_GetActiveWindowName(); | ||
962 | if (menuName[0] != '\0' && strstr(UI_GetActiveWindowName(), POPUPLIST_NODE_NAME"popup_list")) { | ||
963 | /* Update firemode reservation popup. */ | ||
964 | /** @todo this is called every frames... is this really needed? */ | ||
965 | HUD_PopupFiremodeReservation(le, true); | ||
966 | } | ||
967 | } | ||
968 | } | ||
969 | |||
970 | /** | ||
971 | * @brief Draw the mouse cursor tooltips in battlescape | ||
972 | * @param xOffset | ||
973 | * @param yOffset | ||
974 | * @param textId The text id to get the tooltip string from. | ||
975 | */ | ||
976 | static void HUD_DrawMouseCursorText (int xOffset, int yOffset, int textId) | ||
977 | { | ||
978 | const char *string = UI_GetText(textId); | ||
979 | if (string && cl_show_cursor_tooltips->integer) | ||
980 | UI_DrawTooltip(string, mousePosX + xOffset, mousePosY - yOffset, viddef.virtualWidth - mousePosX); | ||
981 | } | ||
982 | |||
983 | /** | ||
984 | * @brief Updates the cursor texts when in battlescape | ||
985 | */ | ||
986 | void HUD_UpdateCursor (void) | ||
987 | { | ||
988 | /* Offset of the first icon on the x-axis. */ | ||
989 | int iconOffsetX = 16; | ||
990 | /* Offset of the first icon on the y-axis. */ | ||
991 | /* the space between different icons. */ | ||
992 | const int iconSpacing = 2; | ||
993 | le_t *le = selActor; | ||
994 | if (le) { | ||
995 | int iconOffsetY = 16; | ||
996 | image_t *image; | ||
997 | /* icon width */ | ||
998 | int iconW = 16; | ||
999 | /* icon height. */ | ||
1000 | int iconH = 16; | ||
1001 | int width = 0; | ||
1002 | int bgX = mousePosX + iconOffsetX / 2 - 2; | ||
1003 | |||
1004 | /* checks if icons should be drawn */ | ||
1005 | if (!(LE_IsCrouched(le)((le)->state & 0x0004) || (le->state & STATE_REACTION0x0300))) | ||
1006 | /* make place holder for icons */ | ||
1007 | bgX += iconW + 4; | ||
1008 | |||
1009 | /* if exists gets width of player name */ | ||
1010 | if (UI_GetText(TEXT_MOUSECURSOR_PLAYERNAMES)) | ||
1011 | R_FontTextSize("f_verysmall", UI_GetText(TEXT_MOUSECURSOR_PLAYERNAMES), viddef.virtualWidth - bgX, LONGLINES_WRAP, &width, NULL__null, NULL__null, NULL__null); | ||
1012 | |||
1013 | /* gets width of background */ | ||
1014 | if (width == 0 && UI_GetText(TEXT_MOUSECURSOR_RIGHT)) { | ||
1015 | R_FontTextSize("f_verysmall", UI_GetText(TEXT_MOUSECURSOR_RIGHT), viddef.virtualWidth - bgX, LONGLINES_WRAP, &width, NULL__null, NULL__null, NULL__null); | ||
1016 | } | ||
1017 | |||
1018 | /* Display 'crouch' icon if actor is crouched. */ | ||
1019 | if (LE_IsCrouched(le)((le)->state & 0x0004)) { | ||
1020 | image = R_FindImage("pics/cursors/ducked", it_pic); | ||
1021 | if (image) | ||
1022 | R_DrawImage(mousePosX - image->width / 2 + iconOffsetX, mousePosY - image->height / 2 + iconOffsetY, image); | ||
1023 | } | ||
1024 | |||
1025 | /* Height of 'crouched' icon. */ | ||
1026 | iconOffsetY += 16; | ||
1027 | iconOffsetY += iconSpacing; | ||
1028 | |||
1029 | /* Display 'Reaction shot' icon if actor has it activated. */ | ||
1030 | if (le->state & STATE_REACTION0x0300) | ||
1031 | image = R_FindImage("pics/cursors/reactionfire", it_pic); | ||
1032 | else | ||
1033 | image = NULL__null; | ||
1034 | |||
1035 | if (image) | ||
1036 | R_DrawImage(mousePosX - image->width / 2 + iconOffsetX, mousePosY - image->height / 2 + iconOffsetY, image); | ||
1037 | |||
1038 | /* Height of 'reaction fire' icon. ... just in case we add further icons below.*/ | ||
1039 | iconOffsetY += iconH; | ||
1040 | iconOffsetY += iconSpacing; | ||
1041 | |||
1042 | /* Display weaponmode (text) heR_ */ | ||
1043 | HUD_DrawMouseCursorText(iconOffsetX + iconW, -10, TEXT_MOUSECURSOR_RIGHT); | ||
1044 | } | ||
1045 | |||
1046 | /* playernames */ | ||
1047 | HUD_DrawMouseCursorText(iconOffsetX + 16, -26, TEXT_MOUSECURSOR_PLAYERNAMES); | ||
1048 | UI_ResetData(TEXT_MOUSECURSOR_PLAYERNAMES); | ||
1049 | |||
1050 | if (cl_map_debug->integer & MAPDEBUG_TEXT(1<<1)) { | ||
1051 | /* Display ceiling text */ | ||
1052 | HUD_DrawMouseCursorText(0, -64, TEXT_MOUSECURSOR_TOP); | ||
1053 | /* Display floor text */ | ||
1054 | HUD_DrawMouseCursorText(0, 64, TEXT_MOUSECURSOR_BOTTOM); | ||
1055 | /* Display left text */ | ||
1056 | HUD_DrawMouseCursorText(-64, 0, TEXT_MOUSECURSOR_LEFT); | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1060 | /** | ||
1061 | * @brief Shows map pathfinding debugging parameters (if activated) | ||
1062 | * @param[in] le The current selected actors entity | ||
1063 | */ | ||
1064 | static void HUD_MapDebugCursor (const le_t *le) | ||
1065 | { | ||
1066 | if (cl_map_debug->integer & MAPDEBUG_TEXT(1<<1)) { | ||
1067 | int dvec; | ||
1068 | |||
1069 | static char topText[UI_MAX_SMALLTEXTLEN1024]; | ||
1070 | static char bottomText[UI_MAX_SMALLTEXTLEN1024]; | ||
1071 | static char leftText[UI_MAX_SMALLTEXTLEN1024]; | ||
1072 | |||
1073 | /* Display the floor and ceiling values for the current cell. */ | ||
1074 | Com_sprintf(topText, lengthof(topText)(sizeof(topText) / sizeof(*(topText))), "%u-(%i,%i,%i)\n", | ||
1075 | Grid_Ceiling(cl.mapData->map, ACTOR_GET_FIELDSIZE(le)((le != __null) ? (le)->fieldSize : 1), truePos), truePos[0], truePos[1], truePos[2]); | ||
1076 | /* Save the text for later display next to the cursor. */ | ||
1077 | UI_RegisterText(TEXT_MOUSECURSOR_TOP, topText); | ||
1078 | |||
1079 | /* Display the floor and ceiling values for the current cell. */ | ||
1080 | Com_sprintf(bottomText, lengthof(bottomText)(sizeof(bottomText) / sizeof(*(bottomText))), "%i-(%i,%i,%i)\n", | ||
1081 | Grid_Floor(cl.mapData->map, ACTOR_GET_FIELDSIZE(le)((le != __null) ? (le)->fieldSize : 1), truePos), mousePos[0], mousePos[1], mousePos[2]); | ||
1082 | /* Save the text for later display next to the cursor. */ | ||
1083 | UI_RegisterText(TEXT_MOUSECURSOR_BOTTOM, bottomText); | ||
1084 | |||
1085 | /* Display the floor and ceiling values for the current cell. */ | ||
1086 | dvec = Grid_MoveNext(&cl.pathMap, mousePos, 0); | ||
1087 | Com_sprintf(leftText, lengthof(leftText)(sizeof(leftText) / sizeof(*(leftText))), "%i-%i\n", getDVdir(dvec)((dvec) >> 8), getDVz(dvec)((dvec) & 0x0007)); | ||
1088 | /* Save the text for later display next to the cursor. */ | ||
1089 | UI_RegisterText(TEXT_MOUSECURSOR_LEFT, leftText); | ||
1090 | } | ||
1091 | } | ||
1092 | |||
1093 | /** | ||
1094 | * @param actor The actor to update the hud for | ||
1095 | * @return The amount of TUs needed for the current pending action | ||
1096 | */ | ||
1097 | static int HUD_UpdateActorFireMode (le_t *actor) | ||
1098 | { | ||
1099 | const invList_t *selWeapon; | ||
1100 | int time = 0; | ||
1101 | |||
1102 | /* get weapon */ | ||
1103 | if (IS_MODE_FIRE_HEADGEAR(actor->actorMode)((actor->actorMode) == M_FIRE_HEADGEAR)) { | ||
1104 | selWeapon = HEADGEAR(actor)(((actor)->i.c[(csi.idHeadgear)])); | ||
1105 | } else if (IS_MODE_FIRE_LEFT(actor->actorMode)((actor->actorMode) == M_FIRE_L || (actor->actorMode) == M_PEND_FIRE_L)) { | ||
1106 | selWeapon = HUD_GetLeftHandWeapon(actor, NULL__null); | ||
1107 | } else { | ||
1108 | selWeapon = RIGHT(actor)(((actor)->i.c[(csi.idRight)])); | ||
1109 | } | ||
1110 | |||
1111 | UI_ResetData(TEXT_MOUSECURSOR_RIGHT); | ||
1112 | |||
1113 | if (selWeapon) { | ||
1114 | static char infoText[UI_MAX_SMALLTEXTLEN1024]; | ||
1115 | |||
1116 | if (!selWeapon->item.t) { | ||
1117 | /* No valid weapon in the hand. */ | ||
1118 | CL_ActorSetFireDef(actor, NULL__null); | ||
1119 | } else { | ||
1120 | /* Check whether this item uses/has ammo. */ | ||
1121 | if (!selWeapon->item.m) { | ||
1122 | CL_ActorSetFireDef(actor, NULL__null); | ||
1123 | /* This item does not use ammo, check for existing firedefs in this item. */ | ||
1124 | /* This is supposed to be a weapon or other usable item. */ | ||
1125 | if (selWeapon->item.t->numWeapons > 0) { | ||
1126 | if (selWeapon->item.t->weapon || selWeapon->item.t->weapons[0] == selWeapon->item.t) { | ||
1127 | const fireDef_t *fdArray = FIRESH_FiredefForWeapon(&selWeapon->item); | ||
1128 | if (fdArray != NULL__null) { | ||
1129 | /* Get firedef from the weapon (or other usable item) entry instead. */ | ||
1130 | const fireDef_t *old = FIRESH_GetFiredef(selWeapon->item.t, fdArray->weapFdsIdx, actor->currentSelectedFiremode); | ||
1131 | CL_ActorSetFireDef(actor, old); | ||
1132 | } | ||
1133 | } | ||
1134 | } | ||
1135 | } else { | ||
1136 | const fireDef_t *fdArray = FIRESH_FiredefForWeapon(&selWeapon->item); | ||
1137 | if (fdArray != NULL__null) { | ||
1138 | const fireDef_t *old = FIRESH_GetFiredef(selWeapon->item.m, fdArray->weapFdsIdx, actor->currentSelectedFiremode); | ||
1139 | /* reset the align if we switched the firemode */ | ||
1140 | CL_ActorSetFireDef(actor, old); | ||
1141 | } | ||
1142 | } | ||
1143 | } | ||
1144 | |||
1145 | if (!GAME_ItemIsUseable(selWeapon->item.t)) { | ||
1146 | HUD_DisplayMessage(_("You cannot use this unknown item.\nYou need to research it first.")gettext("You cannot use this unknown item.\nYou need to research it first." )); | ||
1147 | CL_ActorSetMode(actor, M_MOVE); | ||
1148 | } else if (actor->fd) { | ||
1149 | const int hitProbability = CL_GetHitProbability(actor); | ||
1150 | static char mouseText[UI_MAX_SMALLTEXTLEN1024]; | ||
1151 | |||
1152 | Com_sprintf(infoText, lengthof(infoText)(sizeof(infoText) / sizeof(*(infoText))), | ||
1153 | "%s\n%s (%i) [%i%%] %i\n", _(selWeapon->item.t->name)gettext(selWeapon->item.t->name), _(actor->fd->name)gettext(actor->fd->name), | ||
1154 | actor->fd->ammo, hitProbability, actor->fd->time); | ||
1155 | |||
1156 | /* Save the text for later display next to the cursor. */ | ||
1157 | Q_strncpyz(mouseText, infoText, lengthof(mouseText))Q_strncpyzDebug( mouseText, infoText, (sizeof(mouseText) / sizeof (*(mouseText))), "src/client/battlescape/cl_hud.cpp", 1157 ); | ||
1158 | UI_RegisterText(TEXT_MOUSECURSOR_RIGHT, mouseText); | ||
1159 | |||
1160 | time = actor->fd->time; | ||
1161 | /* if no TUs left for this firing action | ||
1162 | * or if the weapon is reloadable and out of ammo, | ||
1163 | * then change to move mode */ | ||
1164 | if ((selWeapon->item.t->reload && selWeapon->item.a <= 0) || CL_ActorUsableTUs(actor) < time) | ||
1165 | CL_ActorSetMode(actor, M_MOVE); | ||
1166 | } else if (selWeapon) { | ||
1167 | Com_sprintf(infoText, lengthof(infoText)(sizeof(infoText) / sizeof(*(infoText))), _("%s\n(empty)\n")gettext("%s\n(empty)\n"), _(selWeapon->item.t->name)gettext(selWeapon->item.t->name)); | ||
1168 | } | ||
1169 | |||
1170 | UI_RegisterText(TEXT_STANDARD, infoText); | ||
1171 | } else { | ||
1172 | CL_ActorSetMode(actor, M_MOVE); | ||
1173 | } | ||
1174 | |||
1175 | return time; | ||
1176 | } | ||
1177 | |||
1178 | /** | ||
1179 | * @param[in] actor The actor to update the hud for | ||
1180 | * @return The amount of TUs needed for the current pending action | ||
1181 | */ | ||
1182 | static int HUD_UpdateActorMove (const le_t *actor) | ||
1183 | { | ||
1184 | const int reservedTUs = CL_ActorReservedTUs(actor, RES_ALL_ACTIVE); | ||
1185 | static char infoText[UI_MAX_SMALLTEXTLEN1024]; | ||
1186 | if (actor->actorMoveLength == ROUTING_NOT_REACHABLE0xFF) { | ||
1187 | UI_ResetData(TEXT_MOUSECURSOR_RIGHT); | ||
1188 | if (reservedTUs > 0) | ||
1189 | Com_sprintf(infoText, lengthof(infoText)(sizeof(infoText) / sizeof(*(infoText))), _("Morale %i | Reserved TUs: %i\n")gettext("Morale %i | Reserved TUs: %i\n"), actor->morale, reservedTUs); | ||
1190 | else | ||
1191 | Com_sprintf(infoText, lengthof(infoText)(sizeof(infoText) / sizeof(*(infoText))), _("Morale %i")gettext("Morale %i"), actor->morale); | ||
1192 | } else { | ||
1193 | static char mouseText[UI_MAX_SMALLTEXTLEN1024]; | ||
1194 | const int moveMode = CL_ActorMoveMode(actor, actor->actorMoveLength); | ||
1195 | if (reservedTUs > 0) | ||
1196 | Com_sprintf(infoText, lengthof(infoText)(sizeof(infoText) / sizeof(*(infoText))), _("Morale %i | Reserved TUs: %i\n%s %i (%i|%i TUs left)\n")gettext("Morale %i | Reserved TUs: %i\n%s %i (%i|%i TUs left)\n" ), | ||
1197 | actor->morale, reservedTUs, _(moveModeDescriptions[moveMode])gettext(moveModeDescriptions[moveMode]), actor->actorMoveLength, | ||
1198 | actor->TU - actor->actorMoveLength, actor->TU - reservedTUs - actor->actorMoveLength); | ||
1199 | else | ||
1200 | Com_sprintf(infoText, lengthof(infoText)(sizeof(infoText) / sizeof(*(infoText))), _("Morale %i\n%s %i (%i TUs left)\n")gettext("Morale %i\n%s %i (%i TUs left)\n"), actor->morale, | ||
1201 | _(moveModeDescriptions[moveMode])gettext(moveModeDescriptions[moveMode]), actor->actorMoveLength, actor->TU - actor->actorMoveLength); | ||
1202 | |||
1203 | if (actor->actorMoveLength <= CL_ActorUsableTUs(actor)) | ||
1204 | Com_sprintf(mouseText, lengthof(mouseText)(sizeof(mouseText) / sizeof(*(mouseText))), "%i (%i)\n", actor->actorMoveLength, CL_ActorUsableTUs(actor)); | ||
1205 | else | ||
1206 | Com_sprintf(mouseText, lengthof(mouseText)(sizeof(mouseText) / sizeof(*(mouseText))), "- (-)\n"); | ||
1207 | |||
1208 | UI_RegisterText(TEXT_MOUSECURSOR_RIGHT, mouseText); | ||
1209 | } | ||
1210 | |||
1211 | UI_RegisterText(TEXT_STANDARD, infoText); | ||
1212 | |||
1213 | return actor->actorMoveLength; | ||
1214 | } | ||
1215 | |||
1216 | static void HUD_UpdateActorCvar (const le_t *actor, const char *cvarPrefix) | ||
1217 | { | ||
1218 | const invList_t* invList; | ||
1219 | const char *animName; | ||
1220 | static char tuTooltipText[UI_MAX_SMALLTEXTLEN1024]; | ||
1221 | |||
1222 | Cvar_SetValue(va("%s%s", cvarPrefix, "hp"), actor->HP); | ||
1223 | Cvar_SetValue(va("%s%s", cvarPrefix, "hpmax"), actor->maxHP); | ||
1224 | Cvar_SetValue(va("%s%s", cvarPrefix, "tu"), actor->TU); | ||
1225 | Cvar_SetValue(va("%s%s", cvarPrefix, "tumax"), actor->maxTU); | ||
1226 | Cvar_SetValue(va("%s%s", cvarPrefix, "tureserved"), CL_ActorReservedTUs(actor, RES_ALL_ACTIVE)); | ||
1227 | Cvar_SetValue(va("%s%s", cvarPrefix, "morale"), actor->morale); | ||
1228 | Cvar_SetValue(va("%s%s", cvarPrefix, "moralemax"), actor->maxMorale); | ||
1229 | Cvar_SetValue(va("%s%s", cvarPrefix, "stun"), actor->STUN); | ||
1230 | |||
1231 | Com_sprintf(tuTooltipText, lengthof(tuTooltipText)(sizeof(tuTooltipText) / sizeof(*(tuTooltipText))), | ||
1232 | _("Time Units\n- Available: %i (of %i)\n- Reserved: %i\n- Remaining: %i\n")gettext("Time Units\n- Available: %i (of %i)\n- Reserved: %i\n- Remaining: %i\n" ), | ||
1233 | actor->TU, actor->maxTU, CL_ActorReservedTUs(actor, RES_ALL_ACTIVE), CL_ActorUsableTUs(actor)); | ||
1234 | Cvar_Set(va("%s%s", cvarPrefix, "tu_tooltips"), tuTooltipText); | ||
1235 | |||
1236 | /* animation and weapons */ | ||
1237 | animName = R_AnimGetName(&actor->as, actor->model1); | ||
1238 | if (animName) | ||
1239 | Cvar_Set(va("%s%s", cvarPrefix, "anim"), animName); | ||
1240 | if (RIGHT(actor)(((actor)->i.c[(csi.idRight)]))) { | ||
1241 | const invList_t *i = RIGHT(actor)(((actor)->i.c[(csi.idRight)])); | ||
1242 | Cvar_Set(va("%s%s", cvarPrefix, "rweapon"), i->item.t->model); | ||
1243 | Cvar_Set(va("%s%s", cvarPrefix, "rweapon_item"), i->item.t->id); | ||
1244 | } else { | ||
1245 | Cvar_Set(va("%s%s", cvarPrefix, "rweapon"), ""); | ||
1246 | Cvar_Set(va("%s%s", cvarPrefix, "rweapon_item"), ""); | ||
1247 | } | ||
1248 | if (LEFT(actor)(((actor)->i.c[(csi.idLeft)]))) { | ||
1249 | const invList_t *i = LEFT(actor)(((actor)->i.c[(csi.idLeft)])); | ||
1250 | Cvar_Set(va("%s%s", cvarPrefix, "lweapon"), i->item.t->model); | ||
1251 | Cvar_Set(va("%s%s", cvarPrefix, "lweapon_item"), i->item.t->id); | ||
1252 | } else { | ||
1253 | Cvar_Set(va("%s%s", cvarPrefix, "lweapon"), ""); | ||
1254 | Cvar_Set(va("%s%s", cvarPrefix, "lweapon_item"), ""); | ||
1255 | } | ||
1256 | |||
1257 | /* print ammo */ | ||
1258 | invList = RIGHT(actor)(((actor)->i.c[(csi.idRight)])); | ||
1259 | if (invList) | ||
1260 | Cvar_SetValue(va("%s%s", cvarPrefix, "ammoright"), invList->item.a); | ||
1261 | else | ||
1262 | Cvar_Set(va("%s%s", cvarPrefix, "ammoright"), ""); | ||
1263 | |||
1264 | invList = HUD_GetLeftHandWeapon(actor, NULL__null); | ||
1265 | if (invList) | ||
1266 | Cvar_SetValue(va("%s%s", cvarPrefix, "ammoleft"), invList->item.a); | ||
1267 | else | ||
1268 | Cvar_Set(va("%s%s", cvarPrefix, "ammoleft"), ""); | ||
1269 | } | ||
1270 | |||
1271 | /** | ||
1272 | * @brief Update cvars according to a soldier from a list while we are on battlescape | ||
1273 | */ | ||
1274 | static void HUD_ActorGetCvarData_f (void) | ||
1275 | { | ||
1276 | if (Cmd_Argc() < 3) { | ||
1277 | Com_Printf("Usage: %s <soldiernum> <cvarprefix>\n", Cmd_Argv(0)); | ||
1278 | return; | ||
1279 | } | ||
1280 | |||
1281 | /* check whether we are connected (tactical mission) */ | ||
1282 | if (CL_BattlescapeRunning()) { | ||
1283 | const int num = atoi(Cmd_Argv(1)); | ||
1284 | const char *cvarPrefix = Cmd_Argv(2); | ||
1285 | le_t *le; | ||
1286 | character_t *chr; | ||
1287 | |||
1288 | /* check if actor exists */ | ||
1289 | if (num >= cl.numTeamList || num < 0) | ||
1290 | return; | ||
1291 | |||
1292 | /* select actor */ | ||
1293 | le = cl.teamList[num]; | ||
1294 | if (!le) | ||
1295 | return; | ||
1296 | |||
1297 | chr = CL_ActorGetChr(le); | ||
1298 | if (!chr) { | ||
1299 | Com_Error(ERR_DROP1, "No character given for local entity"); | ||
1300 | return; | ||
1301 | } | ||
1302 | |||
1303 | CL_UpdateCharacterValues(chr, cvarPrefix); | ||
1304 | |||
1305 | /* override some cvar with HUD data */ | ||
1306 | HUD_UpdateActorCvar(le, cvarPrefix); | ||
1307 | |||
1308 | return; | ||
1309 | } | ||
1310 | } | ||
1311 | |||
1312 | /** | ||
1313 | * @brief Updates the hud for one actor | ||
1314 | * @param actor The actor to update the hud values for | ||
1315 | */ | ||
1316 | static void HUD_UpdateActor (le_t *actor) | ||
1317 | { | ||
1318 | int time; | ||
1319 | |||
1320 | HUD_UpdateActorCvar(actor, "mn_"); | ||
1321 | |||
1322 | /* write info */ | ||
1323 | time = 0; | ||
1324 | |||
1325 | /* handle actor in a panic */ | ||
1326 | if (LE_IsPaniced(actor)((actor)->state & 0x0008)) { | ||
1327 | UI_RegisterText(TEXT_STANDARD, _("Currently panics!\n")gettext("Currently panics!\n")); | ||
1328 | } else if (displayRemainingTus[REMAINING_TU_CROUCH]) { | ||
1329 | if (CL_ActorUsableTUs(actor) >= TU_CROUCH3) | ||
1330 | time = TU_CROUCH3; | ||
1331 | } else if (displayRemainingTus[REMAINING_TU_RELOAD_RIGHT] | ||
1332 | || displayRemainingTus[REMAINING_TU_RELOAD_LEFT]) { | ||
1333 | const invList_t *invList; | ||
1334 | containerIndex_t container; | ||
1335 | |||
1336 | if (displayRemainingTus[REMAINING_TU_RELOAD_RIGHT] && RIGHT(actor)(((actor)->i.c[(csi.idRight)]))) { | ||
1337 | container = csi.idRight; | ||
1338 | invList = RIGHT(actor)(((actor)->i.c[(csi.idRight)])); | ||
1339 | } else if (displayRemainingTus[REMAINING_TU_RELOAD_LEFT] && LEFT(actor)(((actor)->i.c[(csi.idLeft)]))) { | ||
1340 | container = NONE-1; | ||
1341 | invList = HUD_GetLeftHandWeapon(actor, &container); | ||
1342 | } else { | ||
1343 | container = NONE-1; | ||
1344 | invList = NULL__null; | ||
1345 | } | ||
1346 | |||
1347 | if (invList && invList->item.t && invList->item.m && invList->item.t->reload) { | ||
1348 | const int reloadtime = HUD_CalcReloadTime(actor, invList->item.t, container); | ||
1349 | if (reloadtime != -1 && reloadtime <= CL_ActorUsableTUs(actor)) | ||
1350 | time = reloadtime; | ||
1351 | } | ||
1352 | } else if (CL_ActorFireModeActivated(actor->actorMode)) { | ||
1353 | time = HUD_UpdateActorFireMode(actor); | ||
1354 | } else { | ||
1355 | /* If the mouse is outside the world, and we haven't placed the cursor in pend | ||
1356 | * mode already */ | ||
1357 | if (IN_GetMouseSpace()mouseSpace != MS_WORLD && actor->actorMode < M_PEND_MOVE) | ||
1358 | actor->actorMoveLength = ROUTING_NOT_REACHABLE0xFF; | ||
1359 | time = HUD_UpdateActorMove(actor); | ||
1360 | } | ||
1361 | |||
1362 | /* Calculate remaining TUs. */ | ||
1363 | /* We use the full count of TUs since the "reserved" bar is overlaid over this one. */ | ||
1364 | time = std::max(0, actor->TU - time); | ||
1365 | Cvar_Set("mn_turemain", va("%i", time)); | ||
1366 | |||
1367 | HUD_MapDebugCursor(actor); | ||
1368 | } | ||
1369 | |||
1370 | /** | ||
1371 | * @brief Updates console vars for an actor. | ||
1372 | * | ||
1373 | * This function updates the cvars for the hud (battlefield) | ||
1374 | * unlike CL_ActorCvars and CL_UGVCvars which updates them for | ||
1375 | * displaying the data in the menu system | ||
1376 | * | ||
1377 | * @sa CL_ActorCvars | ||
1378 | * @sa CL_UGVCvars | ||
1379 | */ | ||
1380 | void HUD_Update (void) | ||
1381 | { | ||
1382 | if (cls.state != ca_active) | ||
1383 | return; | ||
1384 | |||
1385 | /* worldlevel */ | ||
1386 | if (cl_worldlevel->modified) { | ||
1387 | int i; | ||
1388 | for (i = 0; i < PATHFINDING_HEIGHT8; i++) { | ||
1389 | int status = 0; | ||
1390 | if (i == cl_worldlevel->integer) | ||
1391 | status = 2; | ||
1392 | else if (i < cl.mapMaxLevel) | ||
1393 | status = 1; | ||
1394 | UI_ExecuteConfunc("updateLevelStatus %i %i", i, status); | ||
1395 | } | ||
1396 | cl_worldlevel->modified = false; | ||
1397 | } | ||
1398 | |||
1399 | /* set Cvars for all actors */ | ||
1400 | HUD_UpdateAllActors(); | ||
1401 | |||
1402 | /* force them empty first */ | ||
1403 | Cvar_Set("mn_anim", "stand0"); | ||
1404 | Cvar_Set("mn_rweapon", ""); | ||
1405 | Cvar_Set("mn_lweapon", ""); | ||
1406 | |||
1407 | if (selActor) { | ||
1408 | HUD_UpdateActor(selActor); | ||
1409 | } else if (!cl.numTeamList) { | ||
1410 | /* This will stop the drawing of the bars over the whole screen when we test maps. */ | ||
1411 | Cvar_SetValue("mn_hp", 0); | ||
1412 | Cvar_SetValue("mn_hpmax", 100); | ||
1413 | Cvar_SetValue("mn_tu", 0); | ||
1414 | Cvar_SetValue("mn_tumax", 100); | ||
1415 | Cvar_SetValue("mn_tureserved", 0); | ||
1416 | Cvar_SetValue("mn_morale", 0); | ||
1417 | Cvar_SetValue("mn_moralemax", 100); | ||
1418 | Cvar_SetValue("mn_stun", 0); | ||
1419 | } | ||
1420 | } | ||
1421 | |||
1422 | /** | ||
1423 | * @brief Callback that is called when the cl_selected cvar was changed | ||
1424 | * @param cvarName The cvar name (cl_selected) | ||
1425 | * @param oldValue The old value of the cvar (a sane actor idx) | ||
1426 | * @param newValue The new value of the cvar (a sane actor idx) | ||
1427 | */ | ||
1428 | static void HUD_ActorSelectionChangeListener (const char *cvarName, const char *oldValue, const char *newValue, void *data) | ||
1429 | { | ||
1430 | if (!CL_OnBattlescape()) | ||
1431 | return; | ||
1432 | |||
1433 | if (newValue[0] != '\0') { | ||
1434 | const int actorIdx = atoi(newValue); | ||
1435 | const size_t size = lengthof(cl.teamList)(sizeof(cl.teamList) / sizeof(*(cl.teamList))); | ||
1436 | if (actorIdx >= 0 && actorIdx < size) | ||
1437 | UI_ExecuteConfunc("hudselect %s", newValue); | ||
1438 | } | ||
1439 | } | ||
1440 | |||
1441 | /** | ||
1442 | * @brief Callback that is called when the right hand weapon of the current selected actor changed | ||
1443 | * @param cvarName The cvar name | ||
1444 | * @param oldValue The old value of the cvar | ||
1445 | * @param newValue The new value of the cvar | ||
1446 | */ | ||
1447 | static void HUD_RightHandChangeListener (const char *cvarName, const char *oldValue, const char *newValue, void *data) | ||
1448 | { | ||
1449 | if (!CL_OnBattlescape()) | ||
| |||
1450 | return; | ||
1451 | |||
1452 | HUD_RefreshButtons(selActor); | ||
| |||
1453 | } | ||
1454 | |||
1455 | /** | ||
1456 | * @brief Callback that is called when the left hand weapon of the current selected actor changed | ||
1457 | * @param cvarName The cvar name | ||
1458 | * @param oldValue The old value of the cvar | ||
1459 | * @param newValue The new value of the cvar | ||
1460 | */ | ||
1461 | static void HUD_LeftHandChangeListener (const char *cvarName, const char *oldValue, const char *newValue, void *data) | ||
1462 | { | ||
1463 | if (!CL_OnBattlescape()) | ||
1464 | return; | ||
1465 | |||
1466 | HUD_RefreshButtons(selActor); | ||
1467 | } | ||
1468 | |||
1469 | /** | ||
1470 | * @brief Callback that is called when the remaining TUs for the current selected actor changed | ||
1471 | * @param cvarName The cvar name | ||
1472 | * @param oldValue The old value of the cvar | ||
1473 | * @param newValue The new value of the cvar | ||
1474 | */ | ||
1475 | static void HUD_TUChangeListener (const char *cvarName, const char *oldValue, const char *newValue, void *data) | ||
1476 | { | ||
1477 | if (!CL_OnBattlescape()) | ||
1478 | return; | ||
1479 | |||
1480 | HUD_RefreshButtons(selActor); | ||
1481 | } | ||
1482 | |||
1483 | static bool CL_CvarWorldLevel (cvar_t *cvar) | ||
1484 | { | ||
1485 | const int maxLevel = cl.mapMaxLevel ? cl.mapMaxLevel - 1 : PATHFINDING_HEIGHT8 - 1; | ||
1486 | return Cvar_AssertValue(cvar, 0, maxLevel, true); | ||
1487 | } | ||
1488 | |||
1489 | /** | ||
1490 | * @brief Checks that the given cvar is a valid hud cvar | ||
1491 | * @param cvar The cvar to check | ||
1492 | * @return @c true if cvar is valid, @c false otherwise | ||
1493 | */ | ||
1494 | static bool HUD_CheckCLHud (cvar_t *cvar) | ||
1495 | { | ||
1496 | uiNode_t *window = UI_GetWindow(cvar->string); | ||
1497 | if (window == NULL__null) { | ||
1498 | return false; | ||
1499 | } | ||
1500 | |||
1501 | if (window->super == NULL__null) { | ||
1502 | return false; | ||
1503 | } | ||
1504 | |||
1505 | /** | ||
1506 | * @todo check for multiple base classes | ||
1507 | */ | ||
1508 | return Q_streq(window->super->name, "hud")(strcmp(window->super->name, "hud") == 0); | ||
1509 | } | ||
1510 | |||
1511 | /** | ||
1512 | * @brief Display the user interface | ||
1513 | * @param optionWindowName Name of the window used to display options, else NULL if nothing | ||
1514 | * @param popAll If true | ||
1515 | * @todo Remove popAll when it is possible. It should always be true | ||
1516 | */ | ||
1517 | void HUD_InitUI (const char *optionWindowName, bool popAll) | ||
1518 | { | ||
1519 | if (!HUD_CheckCLHud(cl_hud)) { | ||
1520 | Cvar_Set("cl_hud", "hud_default"); | ||
1521 | } | ||
1522 | UI_InitStack(cl_hud->string, optionWindowName, popAll, true); | ||
1523 | |||
1524 | UI_ExecuteConfunc("hudinit"); | ||
1525 | } | ||
1526 | |||
1527 | /** | ||
1528 | * @brief Checks that the given cvar is a valid hud cvar | ||
1529 | * @param cvar The cvar to check and to modify if the value is invalid | ||
1530 | * @return @c true if the valid is invalid, @c false otherwise | ||
1531 | */ | ||
1532 | static bool HUD_CvarCheckMNHud (cvar_t *cvar) | ||
1533 | { | ||
1534 | if (!HUD_CheckCLHud(cl_hud)) { | ||
1535 | Cvar_Reset(cvar); | ||
1536 | return true; | ||
1537 | } | ||
1538 | return false; | ||
1539 | } | ||
1540 | |||
1541 | void HUD_InitStartup (void) | ||
1542 | { | ||
1543 | HUD_InitCallbacks(); | ||
1544 | |||
1545 | Cmd_AddCommand("hud_remainingtus", HUD_RemainingTUs_f, "Define if remaining TUs should be displayed in the TU-bar for some hovered-over button."); | ||
1546 | Cmd_AddCommand("hud_shotreserve", HUD_ShotReserve_f, "Reserve TUs for the selected entry in the popup."); | ||
1547 | Cmd_AddCommand("hud_shotreservationpopup", HUD_PopupFiremodeReservation_f, "Pop up a list of possible firemodes for reservation in the current turn."); | ||
1548 | Cmd_AddCommand("hud_switchfiremodelist", HUD_SwitchFiremodeList_f, "Switch firemode-list to one for the given hand, but only if the list is visible already."); | ||
1549 | Cmd_AddCommand("hud_selectreactionfiremode", HUD_SelectReactionFiremode_f, "Change/Select firemode used for reaction fire."); | ||
1550 | Cmd_AddCommand("hud_listfiremodes", HUD_DisplayFiremodes_f, "Display a list of firemodes for a weapon+ammo."); | ||
1551 | Cmd_AddCommand("hud_listactions", HUD_DisplayActions_f, "Display a list of action from the selected soldier."); | ||
1552 | Cmd_AddCommand("hud_getactorcvar", HUD_ActorGetCvarData_f, "Update cvars from actor from list."); | ||
1553 | |||
1554 | /** @note We can't check the value at startup cause scripts are not yet loaded */ | ||
1555 | cl_hud = Cvar_Get("cl_hud", "hud_default", CVAR_ARCHIVE1 | CVAR_LATCH16, "Current selected HUD."); | ||
1556 | Cvar_SetCheckFunction("cl_hud", HUD_CvarCheckMNHud); | ||
1557 | |||
1558 | cl_worldlevel = Cvar_Get("cl_worldlevel", "0", 0, "Current worldlevel in tactical mode."); | ||
1559 | Cvar_SetCheckFunction("cl_worldlevel", CL_CvarWorldLevel); | ||
1560 | cl_worldlevel->modified = false; | ||
1561 | |||
1562 | Cvar_Get("mn_ammoleft", "", 0, "The remaining amount of ammunition in the left hand weapon."); | ||
1563 | Cvar_Get("mn_lweapon", "", 0, "The left hand weapon model of the current selected actor - empty if no weapon."); | ||
1564 | Cvar_RegisterChangeListener("mn_ammoleft", HUD_LeftHandChangeListener); | ||
1565 | Cvar_RegisterChangeListener("mn_lweapon", HUD_LeftHandChangeListener); | ||
1566 | |||
1567 | Cvar_Get("mn_ammoright", "", 0, "The remaining amount of ammunition in the right hand weapon."); | ||
1568 | Cvar_Get("mn_rweapon", "", 0, "The right hand weapon model of the current selected actor - empty if no weapon."); | ||
1569 | Cvar_RegisterChangeListener("mn_ammoright", HUD_RightHandChangeListener); | ||
1570 | Cvar_RegisterChangeListener("mn_rweapon", HUD_RightHandChangeListener); | ||
1571 | |||
1572 | Cvar_Get("mn_turemain", "", 0, "Remaining TUs for the current selected actor."); | ||
1573 | Cvar_RegisterChangeListener("mn_turemain", HUD_TUChangeListener); | ||
1574 | |||
1575 | Cvar_RegisterChangeListener("cl_selected", HUD_ActorSelectionChangeListener); | ||
1576 | |||
1577 | cl_hud_message_timeout = Cvar_Get("cl_hud_message_timeout", "2000", CVAR_ARCHIVE1, "Timeout for HUD messages (milliseconds)."); | ||
1578 | cl_show_cursor_tooltips = Cvar_Get("cl_show_cursor_tooltips", "1", CVAR_ARCHIVE1, "Show cursor tooltips in tactical game mode."); | ||
1579 | } |