/* hud.c Heads-up display bar Copyright (C) 1996-1997 Id Software, Inc. Copyright (C) 2022 Bill Currie This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "QF/cvar.h" #include "QF/screen.h" #include "QF/render.h" #include "QF/plugin/vid_render.h" #include "QF/ui/view.h" #include "compat.h" #include "client/hud.h" static const component_t hud_components[hud_comp_count] = { [hud_href] = { .size = sizeof (hierref_t), .name = "href", }, [hud_update] = { .size = sizeof (hud_update_f), .name = "update", }, [hud_updateonce] = { .size = sizeof (hud_update_f), .name = "updateonce", }, [hud_tile] = { .size = sizeof (byte), .name = "pic", }, [hud_pic] = { .size = sizeof (qpic_t *), .name = "pic", }, [hud_subpic] = { .size = sizeof (hud_subpic_t), .name = "subpic", }, [hud_cachepic] = { .size = sizeof (const char *), .name = "cachepic", }, [hud_fill] = { .size = sizeof (byte), .name = "fill", }, [hud_charbuff] = { .size = sizeof (draw_charbuffer_t *), .name = "charbuffer", }, [hud_func] = { .size = sizeof (hud_func_f), .name = "func", }, [hud_outline] = { .size = sizeof (byte), .name = "outline", }, }; ecs_registry_t *hud_registry; int hud_sb_lines; int hud_sbar; static cvar_t hud_sbar_cvar = { .name = "hud_sbar", .description = "status bar mode: 0 = hud, 1 = oldstyle", .default_value = "0", .flags = CVAR_ARCHIVE, .value = { .type = &cexpr_int, .value = &hud_sbar }, }; grav_t hud_scoreboard_gravity; static cvar_t hud_scoreboard_gravity_cvar = { .name = "hud_scoreboard_gravity", .description = "control placement of scoreboard overlay: center, northwest, north, " "northeast, west, east, southwest, south, southeast", .default_value = "center", .flags = CVAR_ARCHIVE, .value = { .type = &grav_t_type, .value = &hud_scoreboard_gravity }, }; int hud_swap; static cvar_t hud_swap_cvar = { .name = "hud_swap", .description = "new HUD on left side?", .default_value = "0", .flags = CVAR_ARCHIVE, .value = { .type = &cexpr_int, .value = &hud_swap }, }; int hud_fps; static cvar_t hud_fps_cvar = { .name = "hud_fps", .description = "display realtime frames per second", .default_value = "0", .flags = CVAR_ARCHIVE, .value = { .type = &cexpr_int, .value = &hud_fps }, }; int hud_ping; static cvar_t hud_ping_cvar = { .name = "hud_ping", .description = "display current ping to server", .default_value = "0", .flags = CVAR_ARCHIVE, .value = { .type = &cexpr_int, .value = &hud_ping }, }; int hud_pl; static cvar_t hud_pl_cvar = { .name = "hud_pl", .description = "display current packet loss to server", .default_value = "0", .flags = CVAR_ARCHIVE, .value = { .type = &cexpr_int, .value = &hud_pl }, }; int hud_time; static cvar_t hud_time_cvar = { .name = "hud_time", .description = "display the current time", .default_value = "0", .flags = CVAR_ARCHIVE, .value = { .type = &cexpr_int, .value = &hud_time }, }; int hud_debug; static cvar_t hud_debug_cvar = { .name = "hud_debug", .description = "display hud view outlines for debugging", .default_value = "0", .flags = CVAR_NONE, .value = { .type = &cexpr_int, .value = &hud_debug }, }; view_t sbar_view; view_t sbar_inventory_view; view_t sbar_frags_view; view_t hud_view; view_t hud_overlay_view; view_t hud_stuff_view; view_t hud_time_view; view_t hud_fps_view; view_t hud_ping_view; view_t hud_pl_view; static void hud_add_outlines (view_t view) { byte color = 0x6f; Ent_SetComponent (view.id, hud_outline, view.reg, &color); uint32_t count = View_ChildCount (view); for (uint32_t i = 0; i < count; i++) { hud_add_outlines (View_GetChild (view, i)); } } static void hud_remove_outlines (view_t view) { Ent_RemoveComponent (view.id, hud_outline, view.reg); uint32_t count = View_ChildCount (view); for (uint32_t i = 0; i < count; i++) { hud_remove_outlines (View_GetChild (view, i)); } } static void hud_debug_f (void *data, const cvar_t *cvar) { if (!View_Valid (hud_view)) { return; } if (hud_debug) { hud_add_outlines (hud_view); hud_add_outlines (hud_overlay_view); } else { hud_remove_outlines (hud_view); hud_remove_outlines (hud_overlay_view); } } static void hud_sbar_f (void *data, const cvar_t *cvar) { HUD_Calc_sb_lines (*r_data->scr_viewsize); SCR_SetBottomMargin (hud_sbar ? hud_sb_lines : 0); #if 0//XXX if (hud_sbar) { view_remove (hud_main_view, hud_main_view->children[0]); view_insert (hud_main_view, sbar_view, 0); } else { view_remove (hud_main_view, hud_main_view->children[0]); view_insert (hud_main_view, hud_view, 0); } #endif } static void hud_swap_f (void *data, const cvar_t *cvar) { #if 0//XXX if (hud_swap) { //FIXME why is this needed for nq but not for qw? hud_armament_view->children[0]->gravity = grav_northwest; hud_armament_view->children[1]->gravity = grav_southeast; view_setgravity (hud_armament_view, grav_southwest); view_setgravity (hud_stuff_view, grav_southeast); } else { //FIXME why is this needed for nq but not for qw? hud_armament_view->children[0]->gravity = grav_northeast; hud_armament_view->children[1]->gravity = grav_southwest; view_setgravity (hud_armament_view, grav_southeast); view_setgravity (hud_stuff_view, grav_southwest); } view_move (hud_armament_view, hud_armament_view->xpos, hud_armament_view->ypos); view_move (hud_stuff_view, hud_stuff_view->xpos, hud_stuff_view->ypos); #endif } static void hud_scoreboard_gravity_f (void *data, const cvar_t *cvar) { if (View_Valid (hud_overlay_view)) { View_SetGravity (hud_overlay_view, hud_scoreboard_gravity); } } void HUD_Init (void) { hud_registry = ECS_NewRegistry (); ECS_RegisterComponents (hud_registry, hud_components, hud_comp_count); hud_registry->href_comp = hud_href; } void HUD_Init_Cvars (void) { Cvar_Register (&hud_fps_cvar, 0, 0); Cvar_MakeAlias ("show_fps", &hud_fps_cvar); Cvar_Register (&hud_ping_cvar, 0, 0); Cvar_Register (&hud_pl_cvar, 0, 0); Cvar_Register (&hud_time_cvar, 0, 0); Cvar_Register (&hud_debug_cvar, hud_debug_f, 0); Cvar_Register (&hud_sbar_cvar, hud_sbar_f, 0); Cvar_Register (&hud_swap_cvar, hud_swap_f, 0); Cvar_Register (&hud_scoreboard_gravity_cvar, hud_scoreboard_gravity_f, 0); } void HUD_Calc_sb_lines (int view_size) { #if 0//XXX int stuff_y; if (view_size >= 120) { hud_sb_lines = 0; stuff_y = 0; } else if (view_size >= 110) { hud_sb_lines = 24; sbar_inventory_view->visible = 0; hud_inventory_view->visible = 0; hud_armament_view->visible = 0; stuff_y = 32; } else { hud_sb_lines = 48; sbar_inventory_view->visible = 1; hud_inventory_view->visible = 1; hud_armament_view->visible = 1; stuff_y = 48; } if (hud_sb_lines) { sbar_view->visible = 1; hud_view->visible = 1; view_resize (sbar_view, sbar_view->xlen, hud_sb_lines); view_resize (hud_view, hud_view->xlen, hud_sb_lines); } else { sbar_view->visible = 0; hud_view->visible = 0; } view_move (hud_stuff_view, hud_stuff_view->xpos, stuff_y); #endif } static void draw_update (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; hud_update_f *func = pool->data; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; (*func++) (view); } } static void draw_updateonce (ecs_pool_t *pool) { draw_update (pool); pool->count = 0; } static void draw_tile_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; if (View_GetVisible (view)) { view_pos_t pos = View_GetAbs (view); view_pos_t len = View_GetLen (view); r_funcs->Draw_TileClear (pos.x, pos.y, len.x, len.y); } } } static void draw_pic_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; qpic_t **pic = pool->data; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; if (View_GetVisible (view)) { view_pos_t pos = View_GetAbs (view); r_funcs->Draw_Pic (pos.x, pos.y, *pic); } pic++; } } static void draw_subpic_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; hud_subpic_t *subpic = pool->data; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; if (View_GetVisible (view)) { view_pos_t pos = View_GetAbs (view); r_funcs->Draw_SubPic (pos.x, pos.y, subpic->pic, subpic->x, subpic->y, subpic->w, subpic->h); } subpic++; } } static void draw_cachepic_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; const char **name = pool->data; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; if (View_GetVisible (view)) { view_pos_t pos = View_GetAbs (view); qpic_t *pic = r_funcs->Draw_CachePic (*name, 1); r_funcs->Draw_Pic (pos.x, pos.y, pic); } name++; } } static void draw_fill_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; byte *fill = pool->data; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; if (View_GetVisible (view)) { view_pos_t pos = View_GetAbs (view); view_pos_t len = View_GetLen (view); r_funcs->Draw_Fill (pos.x, pos.y, len.x, len.y, *fill); } fill++; } } static void draw_charbuff_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; draw_charbuffer_t **charbuff = pool->data; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; if (View_GetVisible (view)) { view_pos_t pos = View_GetAbs (view); r_funcs->Draw_CharBuffer (pos.x, pos.y, *charbuff); } charbuff++; } } static void draw_func_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; hud_func_f *func = pool->data; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; if (View_GetVisible (view)) { view_pos_t pos = View_GetAbs (view); view_pos_t len = View_GetLen (view); (*func) (pos, len); } func++; } } static void draw_outline_views (ecs_pool_t *pool) { uint32_t count = pool->count; uint32_t *ent = pool->dense; byte *col = pool->data; __auto_type line = r_funcs->Draw_Line; while (count-- > 0) { view_t view = { .id = *ent++, .reg = hud_registry }; byte c = *col++; if (View_GetVisible (view)) { view_pos_t p = View_GetAbs (view); view_pos_t l = View_GetLen (view); view_pos_t q = { p.x + l.x - 1, p.y + l.y - 1 }; line (p.x, p.y, q.x, p.y, c); line (p.x, q.y, q.x, q.y, c); line (p.x, p.y, p.x, q.y, c); line (q.x, p.y, q.x, q.y, c); } } } void HUD_Draw_Views (void) { static void (*draw_func[hud_comp_count]) (ecs_pool_t *) = { [hud_update] = draw_update, [hud_updateonce] = draw_updateonce, [hud_tile] = draw_tile_views, [hud_pic] = draw_pic_views, [hud_subpic] = draw_subpic_views, [hud_cachepic] = draw_cachepic_views, [hud_fill] = draw_fill_views, [hud_charbuff] = draw_charbuff_views, [hud_func] = draw_func_views, [hud_outline] = draw_outline_views, }; for (int i = 0; i < hud_comp_count; i++) { if (draw_func[i]) { draw_func[i] (&hud_registry->comp_pools[i]); } } }