File: | client/battlescape/cl_hud.cpp |
Location: | line 1040, column 3 |
Description: | Value stored to 'iconOffsetY' is never read |
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; |
Value stored to 'iconOffsetY' is never read | |
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 | } |