From d017206ce125be0183fa224774ff2f48f05fbb26 Mon Sep 17 00:00:00 2001 From: Zyntex Date: Fri, 15 Dec 2023 23:45:00 +0100 Subject: [PATCH] rework hud alignment system --- src/core/huds/huds.cpp | 147 ++++++++++++++++++++++++------------ src/core/menu/framework.cpp | 2 +- src/core/photon.cpp | 22 +++--- 3 files changed, 112 insertions(+), 59 deletions(-) diff --git a/src/core/huds/huds.cpp b/src/core/huds/huds.cpp index 4f51ef4..4288bd2 100644 --- a/src/core/huds/huds.cpp +++ b/src/core/huds/huds.cpp @@ -2,6 +2,7 @@ #include "photon-sdk/photon.h" +#include #include static sdk::vec2_t get_abs_pos( photon_api::hud_t *hud ) { @@ -12,9 +13,16 @@ static sdk::vec2_t get_abs_pos( photon_api::hud_t *hud ) { } static void set_abs_pos( photon_api::hud_t *hud, sdk::vec2_t pos ) { + const auto screen_size = photon->render->get_screen_size( ); + const auto anchor = hud->anchor * hud->bounds; - hud->pos = photon->render->normalize( pos + anchor ); + auto new_pos = pos + anchor; + + new_pos.x = std::clamp( new_pos.x, ( float ) huds::safezone_x, screen_size.x - huds::safezone_x ); + new_pos.y = std::clamp( new_pos.y, ( float ) huds::safezone_y, screen_size.y - huds::safezone_y ); + + hud->pos = photon->render->normalize( new_pos ); } static void set_hud_anchor( photon_api::hud_t *hud ) { @@ -37,46 +45,103 @@ static void set_hud_anchor( photon_api::hud_t *hud ) { hud->anchor.y = 0.5f; } -static void align_hud_element( photon_api::hud_t *hud, photon_api::hud_t *other_hud ) { - const auto screen_size = photon->render->get_screen_size( ); +struct hud_bounds_t { + sdk::vec2_t mins; + sdk::vec2_t maxs; + sdk::vec2_t center; +}; + +static hud_bounds_t get_hud_bounds( photon_api::hud_t *hud ) { + const auto hud_pos = get_abs_pos( hud ); + hud_bounds_t bounds; + bounds.mins = sdk::vec2_t( hud_pos.x, hud_pos.y ); + bounds.maxs = sdk::vec2_t( hud_pos.x + hud->bounds.x, hud_pos.y + hud->bounds.y ); + bounds.center = sdk::vec2_t( hud_pos.x + hud->bounds.x / 2, hud_pos.y + hud->bounds.y / 2 ); + + return bounds; +}; + +/* + * get all distances to all hud element's edges, sort them, align to closest + * this whole thing might be overcomplicated, but this was my best idea + */ +static void align_hud_element( photon_api::hud_t *hud ) { const auto clr = sdk::color_t( 255, 0, 255, 255 ); - const auto hud_pos = get_abs_pos( hud ); - const auto other_hud_pos = get_abs_pos( other_hud ); - - int hud_rect[ 6 ] = { - hud_pos.x, - hud_pos.y, - hud_pos.x + hud->bounds.x, - hud_pos.y + hud->bounds.y, - hud_pos.x + hud->bounds.x / 2, - hud_pos.y + hud->bounds.y / 2 }; - int other_hud_rect[ 6 ] = { - other_hud_pos.x, - other_hud_pos.y, - other_hud_pos.x + other_hud->bounds.x, - other_hud_pos.y + other_hud->bounds.y, - other_hud_pos.x + other_hud->bounds.x / 2, - other_hud_pos.y + other_hud->bounds.y / 2 }; - - // TODO: sort everything by the distance and then use the closest one - for ( int i = 0; i < 6; ++i ) { - for ( int j = 0; j < 6; ++j ) { - if ( i % 2 != j % 2 ) - continue; - - if ( abs( hud_rect[ i ] - other_hud_rect[ j ] ) < 8 ) { - if ( j % 2 == 0 ) { - hud->pos.x = ( other_hud_rect[ j ] - ( hud_rect[ i ] - hud_rect[ 0 ] ) + hud->anchor.x * hud->bounds.x ) / screen_size.x; - photon->render->draw_line( other_hud_rect[ j ], 0, 0, screen_size.y, clr ); - } else { - hud->pos.y = ( other_hud_rect[ j ] - ( hud_rect[ i ] - hud_rect[ 1 ] ) + hud->anchor.y * hud->bounds.y ) / screen_size.y; - photon->render->draw_line( 0, other_hud_rect[ j ], screen_size.x, 0, clr ); + const auto screen_size = photon->render->get_screen_size( ); + const auto hud_rect = get_hud_bounds( hud ); + + struct point_distance_t { + sdk::vec2_t pt1; // one of the current hud's points + sdk::vec2_t pt2; // one of the other hud's points + float dist; // squared distance + }; + std::vector distances; + + for ( const auto &other_hud : huds::huds ) { + if ( hud == other_hud ) + continue; + + const auto other_hud_rect = get_hud_bounds( other_hud ); + + // loop over all edges + for ( int i = 0; i < 3; ++i ) { + for ( int j = 0; j < 3; ++j ) { + for ( int axis = 0; axis < 2; ++axis ) { + /* + * NOTE: this whole section is using pointer indexing to access struct members + */ + + auto pt1 = ( ( sdk::vec2_t * ) &hud_rect )[ i ]; + auto pt2 = ( ( sdk::vec2_t * ) &other_hud_rect )[ j ]; + + // since we align huds on axis, not actually by points, set -1 on the axis we dont use + ( ( float * ) &pt1 )[ 1 - axis /* other axis */ ] = -1; + ( ( float * ) &pt2 )[ 1 - axis /* other axis */ ] = -1; + + // delta pos, again only on one axis + float delta_pos = ( ( float * ) &pt1 )[ axis ] - ( ( float * ) &pt2 )[ axis ]; + + point_distance_t dist; + dist.pt1 = pt1; + dist.pt2 = pt2; + dist.dist = delta_pos * delta_pos; // squared distance + + distances.push_back( dist ); } } } } + + // sort points by distance + std::sort( distances.begin( ), distances.end( ), []( const point_distance_t &lhs, const point_distance_t &rhs ) { + return lhs.dist < rhs.dist; + } ); + + // align hud element x, y but only once per axis + bool was_vertical = false; + for ( int i = 0; i < 2; ++i ) { + const auto &pt_dist = distances[ i ]; + const auto vertical = pt_dist.pt2.x == -1; // axis check + const auto hud_pos = get_abs_pos( hud ); + + // only align once per axis + if ( i == 1 && vertical == was_vertical ) + return; + + was_vertical = vertical; + + if ( pt_dist.dist < 100 ) { + if ( !vertical ) { + hud->pos.x = ( pt_dist.pt2.x - ( pt_dist.pt1.x - hud_pos.x ) ) / screen_size.x; + photon->render->draw_line( pt_dist.pt2.x, 0, 0, screen_size.y, clr ); + } else { + hud->pos.y = ( pt_dist.pt2.y - ( pt_dist.pt1.y - hud_pos.y ) ) / screen_size.y; + photon->render->draw_line( 0, pt_dist.pt2.y, screen_size.x, 0, clr ); + } + } + } } void huds::paint( ) { @@ -135,19 +200,7 @@ void huds::paint_ui( ) { set_abs_pos( hud, photon->input->get_cursor_position( ) - grab_pos ); - // dummy hud element for safezone - auto safezone = photon_api::hud_t( ); - const auto safezone_vec = sdk::vec2_t( safezone_x, safezone_y ); - safezone.pos = photon->render->normalize( safezone_vec ); - safezone.bounds = screen_size - sdk::vec2_t( safezone_vec ) * 2; - align_hud_element( hud, &safezone ); - - for ( const auto &other_hud : huds ) { - if ( hud == other_hud ) - continue; - - align_hud_element( hud, other_hud ); - } + align_hud_element( hud ); } if ( photon->input->get_key_release( sdk::mouse_left ) ) { diff --git a/src/core/menu/framework.cpp b/src/core/menu/framework.cpp index e9021db..4e7d020 100644 --- a/src/core/menu/framework.cpp +++ b/src/core/menu/framework.cpp @@ -69,7 +69,7 @@ bool gui::framework::tab( int &selected, sdk::vec2_t pos, sdk::vec2_t size, std: photon->portal2->surface->set_clip_rect( 0, 0, photon->render->get_screen_size( ).x, photon->render->get_screen_size( ).y ); photon->render->draw_outlined_rect( pos.x, pos.y, size.x, size.y, active ? colors::white : hover ? colors::dark - : colors::darker ); + : colors::darker ); photon->render->draw_filled_rect( pos.x + 1, pos.y + 1, size.x - 2, size.y - 2, colors::bg ); const auto text_size = photon->render->get_text_size( fonts::title, title.c_str( ) ); diff --git a/src/core/photon.cpp b/src/core/photon.cpp index fbb4d3f..7288de7 100644 --- a/src/core/photon.cpp +++ b/src/core/photon.cpp @@ -109,25 +109,25 @@ void c_photon::un_pause( ) {} const char *c_photon::get_plugin_description( ) { return photon_plugin_sig; } -void c_photon::level_init( char const *p_map_name ) {} -void c_photon::server_activate( void *p_edict_list, int edict_count, int client_max ) {} +void c_photon::level_init( char const *map_name ) {} +void c_photon::server_activate( void *edict_list, int edict_count, int client_max ) {} void c_photon::game_frame( bool simulating ) {} void c_photon::level_shutdown( ) {} -void c_photon::client_fully_connect( void *p_edict ) {} -void c_photon::client_active( void *p_entity ) {} -void c_photon::client_disconnect( void *p_entity ) {} -void c_photon::client_put_in_server( void *p_entity, char const *playername ) {} +void c_photon::client_fully_connect( void *edict ) {} +void c_photon::client_active( void *entity ) {} +void c_photon::client_disconnect( void *entity ) {} +void c_photon::client_put_in_server( void *entity, char const *playername ) {} void c_photon::set_command_client( int index ) {} -void c_photon::client_settings_changed( void *p_edict ) {} -int c_photon::client_connect( bool *b_allow_connect, void *p_entity, const char *psz_name, const char *psz_address, char *reject, int maxrejectlen ) { +void c_photon::client_settings_changed( void *edict ) {} +int c_photon::client_connect( bool *allow_connect, void *entity, const char *name, const char *address, char *reject, int maxrejectlen ) { return 0; } -int c_photon::client_command( void *p_entity, const void *&args ) { +int c_photon::client_command( void *entity, const void *&args ) { return 0; } -int c_photon::network_id_validated( const char *psz_user_name, const char *psz_network_id ) { +int c_photon::network_id_validated( const char *user_name, const char *network_id ) { return 0; } -void c_photon::on_query_cvar_value_finished( int i_cookie, void *p_player_entity, int e_status, const char *p_cvar_name, const char *p_cvar_value ) {} +void c_photon::on_query_cvar_value_finished( int cookie, void *player_entity, int status, const char *cvar_name, const char *cvar_value ) {} void c_photon::on_edict_allocated( void *edict ) {} void c_photon::on_edict_freed( const void *edict ) {}