From 3f8b1a231f60903c9c7f71b70ab0b44cc92c55bf Mon Sep 17 00:00:00 2001 From: JerryALT Date: Sun, 7 Apr 2024 15:46:48 +0300 Subject: [PATCH] [PlayerStats]: Added new module. --- .../localizedstrings/iw3sp_mod_loc.str | 27 ++ .../french/localizedstrings/iw3sp_mod_loc.str | 27 ++ .../german/localizedstrings/iw3sp_mod_loc.str | 27 ++ .../localizedstrings/iw3sp_mod_loc.str | 27 ++ .../localizedstrings/iw3sp_mod_loc.str | 27 ++ .../localizedstrings/iw3sp_mod_loc.str | 27 ++ .../slovak/localizedstrings/iw3sp_mod_loc.str | 27 ++ .../localizedstrings/iw3sp_mod_loc.str | 27 ++ iw3sp_mod_ff_src/raw/ui/main.menu | 4 +- iw3sp_mod_ff_src/raw/ui/player_info.inc | 126 +++++-- iw3sp_mod_ff_src/raw/ui/stats.menu | 6 +- src/Components/Loader.cpp | 1 + src/Components/Loader.hpp | 1 + src/Components/Modules/PlayerStats.cpp | 316 ++++++++++++++++++ src/Components/Modules/PlayerStats.hpp | 80 +++++ src/Utils/String.cpp | 14 + src/Utils/String.hpp | 1 + 17 files changed, 724 insertions(+), 41 deletions(-) create mode 100644 src/Components/Modules/PlayerStats.cpp create mode 100644 src/Components/Modules/PlayerStats.hpp diff --git a/iw3sp_mod_ff_src/raw/english/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/english/localizedstrings/iw3sp_mod_loc.str index 2554b7f..39eaa39 100644 --- a/iw3sp_mod_ff_src/raw/english/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/english/localizedstrings/iw3sp_mod_loc.str @@ -124,6 +124,33 @@ LANG_ENGLISH "Kills" REFERENCE MENU_CAREER_PLAYTIMESP LANG_ENGLISH "Total Play Time" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_ENGLISH "Melee Kills" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_ENGLISH "Vehicle Killed" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_ENGLISH "Explosives Kills" + +REFERENCE MENU_CAREER_ACCURACY +LANG_ENGLISH "Accuracy" + +REFERENCE MENU_CAREER_SP_STATS +LANG_ENGLISH "Singleplayer Stats" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_ENGLISH "Campaign Completed" + +REFERENCE MENU_CAREER_INTEL +LANG_ENGLISH "Intel" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_ENGLISH "Achievements" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_ENGLISH "Campaign Progress" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_ENGLISH "D H M S" diff --git a/iw3sp_mod_ff_src/raw/french/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/french/localizedstrings/iw3sp_mod_loc.str index 2936c94..a989664 100644 --- a/iw3sp_mod_ff_src/raw/french/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/french/localizedstrings/iw3sp_mod_loc.str @@ -124,6 +124,33 @@ LANG_FRENCH "Tu REFERENCE MENU_CAREER_PLAYTIMESP LANG_FRENCH "Temps total de jeu" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_FRENCH "Éliminations au corps à corps" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_FRENCH "Véhicules détruits" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_FRENCH "Éliminations aux explosifs" + +REFERENCE MENU_CAREER_ACCURACY +LANG_FRENCH "Précision" + +REFERENCE MENU_CAREER_SP_STATS +LANG_FRENCH "Statistiques solo" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_FRENCH "Campagne terminée" + +REFERENCE MENU_CAREER_INTEL +LANG_FRENCH "Renseignements" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_FRENCH "Succès débloqués" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_FRENCH "Avancement de la campagne" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_FRENCH "J H M S" diff --git a/iw3sp_mod_ff_src/raw/german/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/german/localizedstrings/iw3sp_mod_loc.str index e72d9e9..5d6f2d4 100644 --- a/iw3sp_mod_ff_src/raw/german/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/german/localizedstrings/iw3sp_mod_loc.str @@ -130,6 +130,33 @@ LANG_GERMAN "Absch" REFERENCE MENU_CAREER_PLAYTIMESP LANG_GERMAN "Gesamte Spielzeit" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_GERMAN "Nahkampf-Tötungen" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_GERMAN "Fahrzeuge zerstört" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_GERMAN "Explosiv-Tötungen" + +REFERENCE MENU_CAREER_ACCURACY +LANG_GERMAN "Genauigkeit" + +REFERENCE MENU_CAREER_SP_STATS +LANG_GERMAN "Einzelspieler-Statistiken" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_GERMAN "Kampagne abgeschlossen" + +REFERENCE MENU_CAREER_INTEL +LANG_GERMAN "Informationen" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_GERMAN "Errungenschaften" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_GERMAN "Kampagnenfortschritt" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_GERMAN "T S M S" diff --git a/iw3sp_mod_ff_src/raw/italian/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/italian/localizedstrings/iw3sp_mod_loc.str index dea11d3..b8fc946 100644 --- a/iw3sp_mod_ff_src/raw/italian/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/italian/localizedstrings/iw3sp_mod_loc.str @@ -124,6 +124,33 @@ LANG_ITALIAN "Uccisioni" REFERENCE MENU_CAREER_PLAYTIMESP LANG_ITALIAN "Tempo di gioco totale" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_ITALIAN "Uccisioni con corpo a corpo" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_ITALIAN "Veicoli distrutti" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_ITALIAN "Uccisioni con esplosivi" + +REFERENCE MENU_CAREER_ACCURACY +LANG_ITALIAN "Precisione" + +REFERENCE MENU_CAREER_SP_STATS +LANG_ITALIAN "Statistiche giocatore singolo" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_ITALIAN "Campagna completata" + +REFERENCE MENU_CAREER_INTEL +LANG_ITALIAN "Informazioni" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_ITALIAN "Obiettivi sbloccati" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_ITALIAN "Progresso campagna" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_ITALIAN "G O M S" diff --git a/iw3sp_mod_ff_src/raw/portuguese/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/portuguese/localizedstrings/iw3sp_mod_loc.str index 4ef49ee..3b3c031 100644 --- a/iw3sp_mod_ff_src/raw/portuguese/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/portuguese/localizedstrings/iw3sp_mod_loc.str @@ -124,6 +124,33 @@ LANG_PORTUGUESE "Kills" REFERENCE MENU_CAREER_PLAYTIMESP LANG_PORTUGUESE "Total Play Time" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_PORTUGUESE "Melee Kills" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_PORTUGUESE "Vehicle Killed" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_PORTUGUESE "Explosives Kills" + +REFERENCE MENU_CAREER_ACCURACY +LANG_PORTUGUESE "Accuracy" + +REFERENCE MENU_CAREER_SP_STATS +LANG_PORTUGUESE "Singleplayer Stats" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_PORTUGUESE "Campaign Completed" + +REFERENCE MENU_CAREER_INTEL +LANG_PORTUGUESE "Intel" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_PORTUGUESE "Achievements" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_PORTUGUESE "Campaign Progress" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_PORTUGUESE "D H M S" diff --git a/iw3sp_mod_ff_src/raw/russian/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/russian/localizedstrings/iw3sp_mod_loc.str index c2b0d41..725d00a 100644 --- a/iw3sp_mod_ff_src/raw/russian/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/russian/localizedstrings/iw3sp_mod_loc.str @@ -121,6 +121,33 @@ LANG_RUSSIAN " REFERENCE MENU_CAREER_PLAYTIMESP LANG_RUSSIAN "Èãðîâîå âðåìÿ" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_RUSSIAN "Óáèéñòâà â áëèæíåì áîþ" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_RUSSIAN "Óíè÷òîæåííûå òðàíñïîðòíûå ñðåäñòâà" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_RUSSIAN "Óáèéñòâà âçðûâ÷àòêîé" + +REFERENCE MENU_CAREER_ACCURACY +LANG_RUSSIAN "Òî÷íîñòü" + +REFERENCE MENU_CAREER_SP_STATS +LANG_RUSSIAN "Ñòàòèñòèêà îäèíî÷íîé èãðû" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_RUSSIAN "Êàìïàíèèÿ çàâåðøåíà" + +REFERENCE MENU_CAREER_INTEL +LANG_RUSSIAN "Ñâåäåíèÿ" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_RUSSIAN "Äîñòèæåíèÿ" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_RUSSIAN "Ïðîãðåññ êàìïàíèè" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_RUSSIAN "Ä × Ì Ñ" diff --git a/iw3sp_mod_ff_src/raw/slovak/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/slovak/localizedstrings/iw3sp_mod_loc.str index 13c8db1..b7f5f75 100644 --- a/iw3sp_mod_ff_src/raw/slovak/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/slovak/localizedstrings/iw3sp_mod_loc.str @@ -114,6 +114,33 @@ LANG_SLOVAK "Kills" REFERENCE MENU_CAREER_PLAYTIMESP LANG_SLOVAK "Total Play Time" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_SLOVAK "Melee Kills" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_SLOVAK "Vehicle Killed" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_SLOVAK "Explosives Kills" + +REFERENCE MENU_CAREER_ACCURACY +LANG_SLOVAK "Accuracy" + +REFERENCE MENU_CAREER_SP_STATS +LANG_SLOVAK "Singleplayer Stats" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_SLOVAK "Campaign Completed" + +REFERENCE MENU_CAREER_INTEL +LANG_SLOVAK "Intel" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_SLOVAK "Achievements" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_SLOVAK "Campaign Progress" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_SLOVAK "D H M S" diff --git a/iw3sp_mod_ff_src/raw/spanish/localizedstrings/iw3sp_mod_loc.str b/iw3sp_mod_ff_src/raw/spanish/localizedstrings/iw3sp_mod_loc.str index 621c07a..3966a5e 100644 --- a/iw3sp_mod_ff_src/raw/spanish/localizedstrings/iw3sp_mod_loc.str +++ b/iw3sp_mod_ff_src/raw/spanish/localizedstrings/iw3sp_mod_loc.str @@ -125,6 +125,33 @@ LANG_SPANISH "Asesinatos" REFERENCE MENU_CAREER_PLAYTIMESP LANG_SPANISH "Tiempo total de juego" +REFERENCE MENU_CAREER_KILLS_MELEE +LANG_SPANISH "Muertes cuerpo a cuerpo" + +REFERENCE MENU_CAREER_KILLS_VEHICLES +LANG_SPANISH "Vehículos destruidos" + +REFERENCE MENU_CAREER_KILLS_EXPLOSIVES +LANG_SPANISH "Muertes por explosivos" + +REFERENCE MENU_CAREER_ACCURACY +LANG_SPANISH "Precisión" + +REFERENCE MENU_CAREER_SP_STATS +LANG_SPANISH "Estadísticas de un jugador" + +REFERENCE MENU_CAREER_SP_COMPLETED +LANG_SPANISH "Campaña completada" + +REFERENCE MENU_CAREER_INTEL +LANG_SPANISH "Información" + +REFERENCE MENU_CAREER_ACHIEVEMENT_UNLOCKED +LANG_SPANISH "Logros desbloqueados" + +REFERENCE MENU_CAREER_SP_PROGRESS +LANG_SPANISH "Progreso de campaña" + REFERENCE MENU_SP_STAT_TIME_FORMAT LANG_SPANISH "D H M S" diff --git a/iw3sp_mod_ff_src/raw/ui/main.menu b/iw3sp_mod_ff_src/raw/ui/main.menu index 0411459..8aa95ed 100644 --- a/iw3sp_mod_ff_src/raw/ui/main.menu +++ b/iw3sp_mod_ff_src/raw/ui/main.menu @@ -279,8 +279,8 @@ CHOICE_DBUTTON_VIS( 5, "@MENU_ARCADEMODE", when( dvarInt( mis_01 ) < 20 && !localvarBool( ui_hideBack ) ); ) // CHOICE_BUTTON_VIS( 5, "@MENU_ARCADEMODE", open popmenu_arcade; LOCAL_ARCADE_RESET, when( dvarInt( mis_01 ) >= 20 && !localvarBool( ui_hideBack ) ); ) // CHOICE_DBUTTON_VIS( 5, "@MENU_ARCADEMODE", when( dvarInt( mis_01 ) < 20 && !localvarBool( ui_hideBack ) ); ) - CHOICE_BUTTON_VIS( 6, "@IW3SP_MOD_LOC_MENU_ACHIEVEMENTS", uiScript achievement_progressbar; open achievements; LOCAL_ARCADE_RESET, when( !localvarBool( ui_hideBack ) ); ) - //CHOICE_BUTTON_VIS( 6, "@IW3SP_MOD_LOC_MENU_STATS", open stats; LOCAL_ARCADE_RESET, when( !localvarBool( ui_hideBack ) ); ) + //CHOICE_BUTTON_VIS( 6, "@IW3SP_MOD_LOC_MENU_ACHIEVEMENTS", uiScript achievement_progressbar; open achievements; LOCAL_ARCADE_RESET, when( !localvarBool( ui_hideBack ) ); ) + CHOICE_BUTTON_VIS( 6, "@IW3SP_MOD_LOC_MENU_STATS", uiScript get_career_info; open stats; LOCAL_ARCADE_RESET, when( !localvarBool( ui_hideBack ) ); ) CHOICE_SEPARATOR_VIS( CHOICE_SEP_1, when( !localvarBool( ui_hideBack ) ); ) CHOICE_BUTTON_VIS( 7, "@MENU_CONTROLS", open iw3sp_mod_controls_pc_main /*open options_look*/;, when( !localvarBool( ui_hideBack ) ); ) CHOICE_BUTTON_VIS( 8, "@MENU_OPTIONS", open options_graphics;, when( !localvarBool( ui_hideBack ) ); ) diff --git a/iw3sp_mod_ff_src/raw/ui/player_info.inc b/iw3sp_mod_ff_src/raw/ui/player_info.inc index 3dc39fe..dc1a64c 100644 --- a/iw3sp_mod_ff_src/raw/ui/player_info.inc +++ b/iw3sp_mod_ff_src/raw/ui/player_info.inc @@ -77,14 +77,15 @@ itemDef { \ visible visArg \ decoration } -#define PREPROC_VALUE_TEXT( ptext, itemNumber, px_offset, pcolor ) \ - PREPROC_VALUE_TEXT_VIS( ptext, itemNumber, px_offset, pcolor, 1, ITEM_ALIGN_MIDDLE_RIGHT ) +#define PREPROC_VALUE_TEXT( pname, ptext, itemNumber, px_offset, pcolor ) \ + PREPROC_VALUE_TEXT_VIS( pname, ptext, itemNumber, px_offset, pcolor, 1, ITEM_ALIGN_MIDDLE_RIGHT ) -#define PREPROC_VALUE_TEXT_LEFT_ALIGNED( ptext, itemNumber, px_offset, pcolor ) \ - PREPROC_VALUE_TEXT_VIS( ptext, itemNumber, px_offset, pcolor, 1, ITEM_ALIGN_MIDDLE_LEFT ) +#define PREPROC_VALUE_TEXT_LEFT_ALIGNED( pname, ptext, itemNumber, px_offset, pcolor ) \ + PREPROC_VALUE_TEXT_VIS( pname, ptext, itemNumber, px_offset, pcolor, 1, ITEM_ALIGN_MIDDLE_LEFT ) -#define PREPROC_VALUE_TEXT_VIS( ptext, itemNumber, px_offset, pcolor, visArg, alignment ) \ +#define PREPROC_VALUE_TEXT_VIS( pname, ptext, itemNumber, px_offset, pcolor, visArg, alignment ) \ itemDef { \ + name pname \ text ptext; \ type ITEM_TYPE_TEXT \ rect ORIGIN_LABEL_ITEM( itemNumber ) (PLAYERSTATS_WIDTH) LABEL_ITEM_SPACING RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP \ @@ -98,6 +99,7 @@ itemDef { \ visible visArg \ decoration } + /* itemDef { type ITEM_TYPE_TEXT @@ -112,6 +114,7 @@ itemDef { \ visible 1 decoration } + */ // UI art PREPROC_SHADER_DRAW_ALIGNED( -4 12 (PLAYERSTATS_WIDTH+8) 326 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", FRAME_BG_COLOR, 0, 0, 0 0 0 0 ) @@ -150,24 +153,39 @@ itemDef { \ //kills PREPROC_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_KILLS", 1) - PREPROC_VALUE_TEXT( 228, 1, -24, LABEL_TITLECOLOR ) + PREPROC_VALUE_TEXT( "kills", "", 1, -24, LABEL_TITLECOLOR ) //headshots PREPROC_SUB_LABEL( "@IW3SP_MOD_LOC_MENU_CAREER_HEADSHOTS", 2 ) - PREPROC_VALUE_TEXT( 228, 2, -24, LABEL_TEXTCOLOR ) + PREPROC_VALUE_TEXT( "headshots", "", 2, -24, LABEL_TEXTCOLOR ) + + //melee kills + PREPROC_SUB_LABEL( "@IW3SP_MOD_LOC_MENU_CAREER_KILLS_MELEE", 3 ) + PREPROC_VALUE_TEXT( "kills_melee", "", 3, -24, LABEL_TEXTCOLOR ) + + //vehicle kills + // PREPROC_SUB_LABEL( "@IW3SP_MOD_LOC_MENU_CAREER_KILLS_VEHICLES", 4 ) + // PREPROC_VALUE_TEXT( "kills_vehicle", "", 4, -24, LABEL_TEXTCOLOR ) + + //explosives kills + PREPROC_SUB_LABEL( "@IW3SP_MOD_LOC_MENU_CAREER_KILLS_EXPLOSIVES", 4 ) + PREPROC_VALUE_TEXT( "kills_explosives", "", 4, -24, LABEL_TEXTCOLOR ) //deaths - PREPROC_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_DEATHS", 3) - PREPROC_VALUE_TEXT( 228, 3, -24, LABEL_TITLECOLOR ) + PREPROC_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_DEATHS", 5) + PREPROC_VALUE_TEXT( "deaths", "", 5, -24, LABEL_TITLECOLOR ) - PREPROC_DIVIDER( 4 ) + //accuracy + PREPROC_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_ACCURACY", 6) + PREPROC_VALUE_TEXT( "accuracy", "", 6, -24, LABEL_TITLECOLOR ) + + PREPROC_DIVIDER( 7 ) //play time (add as the last element in player info) - PREPROC_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_PLAYTIMESP", 5) - PREPROC_VALUE_TEXT( "0:00:00:00", 5, -24, LABEL_TITLECOLOR ) - PREPROC_LABEL_TIME("@IW3SP_MOD_LOC_MENU_SP_STAT_TIME_FORMAT", 4.4) + PREPROC_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_PLAYTIMESP", 8) + PREPROC_VALUE_TEXT( "playedtime", "", 8, -24, LABEL_TITLECOLOR ) - PREPROC_DIVIDER( 6 ) + PREPROC_DIVIDER( 9 ) //for sliders //145(18) - 185 (40) - 8 @@ -181,33 +199,69 @@ itemDef { \ //289(18) - 439 (40) - 16 //307(18) - 479 (40) - 17 - PREPROC_LABEL("Campaign Progress", 7) + PREPROC_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_SP_STATS", 10) - PREPROC_SUB_LABEL( "Recruit Progress:", 8 ) - PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 145 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) - PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+147), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + PREPROC_SUB_LABEL( "@IW3SP_MOD_LOC_MENU_CAREER_SP_COMPLETED", 11 ) + PREPROC_VALUE_TEXT( "difficulty", "", 11, -24, LABEL_TEXTCOLOR ) - PREPROC_SUB_LABEL( "Regular Progress:", 9 ) - PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 163 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) - PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+165), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + PREPROC_SUB_LABEL( "@IW3SP_MOD_LOC_MENU_CAREER_INTEL", 12 ) + PREPROC_VALUE_TEXT( "intels", "", 12, -24, LABEL_TEXTCOLOR ) - PREPROC_SUB_LABEL( "Hardened Progress:", 10 ) - PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 181 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) - PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+183), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + PREPROC_SUB_LABEL("@IW3SP_MOD_LOC_MENU_CAREER_ACHIEVEMENT_UNLOCKED", 13) + PREPROC_VALUE_TEXT( "achievements", "", 13, -24, LABEL_TEXTCOLOR ) - PREPROC_SUB_LABEL( "Veteran Progress:", 11 ) - PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 199 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) - PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+201), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + PREPROC_DIVIDER( 14 ) - PREPROC_DIVIDER( 12 ) + PREPROC_LABEL( "@IW3SP_MOD_LOC_MENU_CAREER_SP_PROGRESS", 15 ) + PREPROC_VALUE_TEXT( "campaign_progress", "", 15, -24, LABEL_TITLECOLOR ) + //PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 271 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) + //PREPROC_SHADER_DRAW_ADV_VIS( "campaign_progressbar", (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+273), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) - PREPROC_LABEL("Intel Items", 13) - PREPROC_VALUE_TEXT( 17, 13, -24, LABEL_TITLECOLOR ) - PREPROC_LABEL("Achievement Unlocked", 14) - PREPROC_VALUE_TEXT( 35, 14, -24, LABEL_TITLECOLOR ) + //#define PREPROC_SHADER_DRAW_ADV_VIS( pname, px, py, pw, ph, pshader, pcolor, pborder, pbordersize, pbordercolor, visArg ) - PREPROC_DIVIDER( 15 ) + //itemDef + //{ + // name "campaign_progressbar" + // style WINDOW_STYLE_SHADER + // rect 0 0 0 12 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP + // exp rect X( PLAYERSTATS_X+202 ) + // exp rect Y( PLAYERSTATS_Y+273 ) + // forecolor 1 0.9 0.5 0.6 + // exp material( "gradient_fadein" ); + // border 0 + // bordersize 1 + // bordercolor 1 1 1 1 + // visible visArg + // decoration + //} - PREPROC_SUB_LABEL( "Campaign Progress:", 16 ) - PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 289 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) - PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+291), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + + // OLD!!!! + // PREPROC_SUB_LABEL( "Recruit Progress:", 8 ) + // PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 145 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) + // PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+147), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + + // PREPROC_SUB_LABEL( "Regular Progress:", 9 ) + // PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 163 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) + // PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+165), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + + // PREPROC_SUB_LABEL( "Hardened Progress:", 10 ) + // PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 181 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) + // PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+183), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + + // PREPROC_SUB_LABEL( "Veteran Progress:", 11 ) + // PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 199 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) + // PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+201), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) + + //PREPROC_DIVIDER( 12 ) + + // PREPROC_LABEL("Intel Items", 13) + // PREPROC_VALUE_TEXT( "intels", 17, 13, -24, LABEL_TITLECOLOR ) + // PREPROC_LABEL("Achievement Unlocked", 14) + // PREPROC_VALUE_TEXT( "achievements", 35, 14, -24, LABEL_TITLECOLOR ) + + //PREPROC_DIVIDER( 15 ) + + // PREPROC_SUB_LABEL( "Campaign Progress:", 16 ) + // PREPROC_SHADER_DRAW_ALIGNED_VIS( 200 289 60 14 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, ORIGIN_PLAYERSTATS, "white", 0.1 0.1 0.1 0.35, 1, 1, 1 1 1 0.2, 1 ) + // PREPROC_SHADER_DRAW_ADV_VIS( (PLAYERSTATS_X+202), /*167*/(PLAYERSTATS_Y+291), 50, 12, "gradient_fadein", 1 0.9 0.5 0.6, 0, 1, 1 1 1 1 rect 0 0 0 0 RIGHTITEM_ALIGN VERTICAL_ALIGN_TOP, 1 ) diff --git a/iw3sp_mod_ff_src/raw/ui/stats.menu b/iw3sp_mod_ff_src/raw/ui/stats.menu index e0e3b8b..e3a69a3 100644 --- a/iw3sp_mod_ff_src/raw/ui/stats.menu +++ b/iw3sp_mod_ff_src/raw/ui/stats.menu @@ -26,9 +26,9 @@ style WINDOW_STYLE_FILLED border 0 soundloop "music_mainmenu_mp" - onOpen { /*uiScript get_career_info*/ } - onEsc { close stats;} - onClose {open main;} + onOpen { focusfirst; /*uiScript get_career_info*/ } + onEsc { close stats; } + onClose { open main; } #include "ui/blurredbg.inc" #include "ui_mp/navcontrols.inc" diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 3780f37..8f5ef53 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -65,6 +65,7 @@ namespace Components Register(new Markdown()); Register(new Changelog()); Register(new Paintball()); + Register(new PlayerStats()); Pregame = false; // Make sure preDestroy is called when the game shuts down diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 56fcf3b..22e8918 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -83,3 +83,4 @@ namespace Components #include "Modules/GUI/Markdown.hpp" #include "Modules/Changelog.hpp" #include "Modules/Paintball.hpp" +#include "Modules/PlayerStats.hpp" diff --git a/src/Components/Modules/PlayerStats.cpp b/src/Components/Modules/PlayerStats.cpp new file mode 100644 index 0000000..f355f6c --- /dev/null +++ b/src/Components/Modules/PlayerStats.cpp @@ -0,0 +1,316 @@ +#include "STDInc.hpp" + +namespace Components +{ + void PlayerStats::ChangeItemDefText(const char* menuName, const char* itemDefName, const char* value) + { + Game::menuDef_t* menu = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_MENU, menuName).menu; + if (!menu) return; + + for (auto i = 0; i < menu->itemCount; i++) + { + if (menu->items[i] && menu->items[i]->window.name) + { + if (!_stricmp(menu->items[i]->window.name, itemDefName)) + { + menu->items[i]->text = value; + } + } + } + } + + const char* PlayerStats::getHighestCompletedDifficulty(const float stat_easy, const float stat_regular, const float stat_hardened, const float stat_veteran) + { + if (stat_easy == 0.0f || stat_regular == 0.0f || stat_hardened == 0.0f || stat_veteran == 0.0f) + return "MENU_NOT_STARTED"; + if (stat_veteran == 100.0f) + return "MENU_VETERAN"; + else if (stat_hardened == 100.0f) + return "MENU_HARDENED"; + else if (stat_regular == 100.0f) + return "MENU_REGULAR"; + else if (stat_easy == 100.0f) + return "MENU_RECRUIT"; + else + return "MENU_IN_PROGRESS"; + } + + double PlayerStats::CalculateAccuracy(uint32_t bullets_fired, uint32_t bullets_hit) + { + if (bullets_fired != 0) + return static_cast(bullets_hit) / bullets_fired * 100.0; + else + return 0.0; + } + + float PlayerStats::getStatProgression(const std::string& difficulty_string, int difficulty) + { + int levels = 0; + size_t max_missions = std::min(difficulty_string.size(), static_cast(21)); + + for (size_t i = 0; i < max_missions; ++i) { + if (static_cast(difficulty_string[i] - '0') >= difficulty) { + levels++; + } + } + + float completion = static_cast(levels) / max_missions * 100; + return completion; + } + + float PlayerStats::getStatEasy(const std::string& difficulty_string) + { + return getStatProgression(difficulty_string, 1); + } + + float PlayerStats::getStatRegular(const std::string& difficulty_string) + { + return getStatProgression(difficulty_string, 2); + } + + float PlayerStats::getStatHardened(const std::string& difficulty_string) + { + return getStatProgression(difficulty_string, 3); + } + + float PlayerStats::getStatVeteran(const std::string& difficulty_string) + { + return getStatProgression(difficulty_string, 4); + } + + float PlayerStats::getStatIntel(int intelCount, int totalIntelItems) + { + return static_cast(intelCount) / totalIntelItems * 100; + } + + int PlayerStats::getTotalPercentCompleteSP() + { + float stat_easy = getStatEasy(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + float stat_regular = getStatRegular(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + float stat_hardened = getStatHardened(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + float stat_veteran = getStatVeteran(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + float stat_intel = getStatIntel(GetStruct("career", "intel"), 30); + Achievements::achievement_file_t file{}; + Achievements::GetAchievementsData(&file); + int achievements_earned = Achievements::GetEarnedAchievementCount(&file); + + // Calculate completion progress based on various factors + float total_progress = 0.0; + + // Completion progress based on difficulty completion + total_progress += (0.1 / 1) * stat_easy; + total_progress += (0.1 / 1) * stat_regular; + total_progress += (0.1 / 1) * stat_hardened; + total_progress += (0.5 / 1) * stat_veteran; + + // Intel completion progress + total_progress += (0.15 / 1) * stat_intel; + + // Achievement completion progress + total_progress += (0.15 / 38) * achievements_earned; + + // Clamp total progress to 100% + total_progress = std::min(total_progress, 100.0f); + + // Convert total progress to integer + return static_cast(total_progress); + } + + uint32_t PlayerStats::CalculateDays(uint32_t totalMilliseconds) { + return totalMilliseconds / (1000 * 60 * 60 * 24); + } + + uint32_t PlayerStats::CalculateHours(uint32_t totalMilliseconds) { + totalMilliseconds %= (1000 * 60 * 60 * 24); + return totalMilliseconds / (1000 * 60 * 60); + } + + uint32_t PlayerStats::CalculateMinutes(uint32_t totalMilliseconds) { + totalMilliseconds %= (1000 * 60 * 60); + return totalMilliseconds / (1000 * 60); + } + + uint32_t PlayerStats::CalculateSeconds(uint32_t totalMilliseconds) { + totalMilliseconds %= (1000 * 60); + return totalMilliseconds / 1000; + } + + void PlayerStats::DefaultStats() + { + const auto path = GetPlayerStatsPath(); + Utils::IO::RemoveFile(path); + } + + std::string PlayerStats::GetPlayerStatsPath() + { + auto fileBuffer = Utils::IO::ReadFile("players/profiles/active.txt"); + if (fileBuffer.empty()) + return ""; + + std::string game_folder = std::format("{}\\players\\profiles\\{}\\stats.json", Dvars::Functions::Dvar_FindVar("fs_basepath")->current.string, fileBuffer.c_str()); + return game_folder; + } + + nlohmann::json PlayerStats::ReadPlayerStats() + { + const auto path = GetPlayerStatsPath(); + if (!Utils::IO::FileExists(path)) + { + return {}; + } + + try + { + const auto data = Utils::IO::ReadFile(path); + return nlohmann::json::parse(data); + } + catch (const std::exception& e) + { + Game::Com_Printf(0, "Failed to parse config file: %s\n", e.what()); + Utils::IO::WriteFile(path, "{}", false); + } + + return {}; + } + + void PlayerStats::ResetData() + { + PlayerStats::DefaultStats(); + } + + void PlayerStats::WriteData(const nlohmann::json& json) + { + try + { + const auto path = GetPlayerStatsPath(); + const auto str = json.dump(4); + Utils::IO::WriteFile(path, str, false); + } + catch (const std::exception& e) + { + Game::Com_Printf(0, "Failed to write config file: %s\n", e.what()); + } + } + + void PlayerStats::PlayTimeStart() + { + Scheduler::Loop([] + { + static uint32_t lastUpdateTime = timeGetTime(); + + uint32_t currentTime = timeGetTime(); + uint32_t elapsedTime = currentTime - lastUpdateTime; + + uint32_t playTimeSP = GetStruct("career", "playTimeSP"); + playTimeSP += elapsedTime; + SetStruct("career", "playTimeSP", playTimeSP); + + lastUpdateTime = currentTime; + }, Scheduler::Pipeline::MAIN, 1000ms); + } + + void __declspec(naked) PlayerStats::Com_Init_Try_Block_Function_Stub() + { + const static uint32_t retn_addr = 0x535220; + __asm + { + mov dword ptr ds:[0xF8E004], ebx; + call PlayTimeStart; + jmp retn_addr; + } + } + + PlayerStats::PlayerStats() + { + Command::Add("reset_stats_json", [](Command::Params*) + { + ResetData(); + }); + + GSC::AddFunction("StatsGetFromStruct", [] + { + const auto json_struct = Game::Scr_GetString(0); + const auto field = Game::Scr_GetString(1); + const auto value = GetStruct(json_struct, field); + + return Game::Scr_AddInt(value); + }, false); + + GSC::AddFunction("StatsSetFromStruct", [] + { + const auto json_struct = Game::Scr_GetString(0); + const auto field = Game::Scr_GetString(1); + const auto value = Game::Scr_GetInt(2); + SetStruct(json_struct, field, value); + }, false); + + UIScript::Add("get_career_info", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info) + { + //stats vars + uint32_t var_bullets_fired = GetStruct("career", "bullets_fired"); + uint32_t var_bullets_hit = GetStruct("career", "bullets_hit"); + uint32_t var_deaths = GetStruct("career", "deaths"); + uint32_t var_headshots = GetStruct("career", "headshots"); + uint32_t var_kills = GetStruct("career", "kills"); + uint32_t var_kills_melee = GetStruct("career", "kills_melee"); + uint32_t var_kills_explosives = GetStruct("career", "kills_explosives"); + uint32_t var_playTimeSP = GetStruct("career", "playTimeSP"); + uint32_t var_intel = GetStruct("career", "intel"); + uint32_t var_days = CalculateDays(var_playTimeSP); + uint32_t var_hours = CalculateHours(var_playTimeSP); + uint32_t var_minutes = CalculateMinutes(var_playTimeSP); + uint32_t var_seconds = CalculateSeconds(var_playTimeSP); + + float stat_easy = getStatEasy(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + float stat_regular = getStatRegular(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + float stat_hardened = getStatHardened(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + float stat_veteran = getStatVeteran(Dvars::Functions::Dvar_FindVar("mis_difficulty")->current.string); + + Achievements::achievement_file_t file{}; + Achievements::GetAchievementsData(&file); + int var_achievements_earned = Achievements::GetEarnedAchievementCount(&file); + + auto var_accuracy = CalculateAccuracy(var_bullets_fired, var_bullets_hit); + + int total_progress = getTotalPercentCompleteSP(); + + // For menu showcase + const char* kills = Utils::String::VA_NEW("%d", var_kills); + const char* kills_melee = Utils::String::VA_NEW("%d", var_kills_melee); + const char* kills_explosives = Utils::String::VA_NEW("%d", var_kills_explosives); + const char* headshots = Utils::String::VA_NEW("%d", var_headshots); + const char* deaths = Utils::String::VA_NEW("%d", var_deaths); + const char* bullets_fired = Utils::String::VA_NEW("%d", var_bullets_fired); + const char* bullets_hit = Utils::String::VA_NEW("%d", var_bullets_hit); + const char* intel = Utils::String::VA_NEW("%d/%d", var_intel, 30); + const char* timestamp = Utils::String::VA_NEW("%u:%02u:%02u:%02u", var_days, var_hours, var_minutes, var_seconds); + const char* achievements_earned = Utils::String::VA_NEW("%d/%d", var_achievements_earned, Achievements::ACHIEVEMENT_TOTAL_COUNT); + const char* accuracy = Utils::String::VA_NEW("%.0f%%", var_accuracy); + const char* difficulty = getHighestCompletedDifficulty(stat_easy, stat_regular, stat_hardened, stat_veteran); + const char* total_percent_sp = Utils::String::VA_NEW("%d%%", total_progress); + + ChangeItemDefText("stats", "kills", kills); + ChangeItemDefText("stats", "headshots", headshots); + ChangeItemDefText("stats", "kills_melee", kills_melee); + ChangeItemDefText("stats", "kills_explosives", kills_explosives); + ChangeItemDefText("stats", "deaths", deaths); + ChangeItemDefText("stats", "accuracy", accuracy); + //----------------------------------------------------------- + ChangeItemDefText("stats", "playedtime", timestamp); + //----------------------------------------------------------- + ChangeItemDefText("stats", "difficulty", Game::UI_SafeTranslateString(difficulty)); + ChangeItemDefText("stats", "intels", intel); + ChangeItemDefText("stats", "achievements", achievements_earned); + //----------------------------------------------------------- + ChangeItemDefText("stats", "campaign_progress", total_percent_sp); + //----------------------------------------------------------- + }); + + Utils::Hook::Nop(0x53521A, 6); + Utils::Hook(0x53521A, Com_Init_Try_Block_Function_Stub, HOOK_JUMP).install()->quick(); + } + + PlayerStats::~PlayerStats() + { + } +} \ No newline at end of file diff --git a/src/Components/Modules/PlayerStats.hpp b/src/Components/Modules/PlayerStats.hpp new file mode 100644 index 0000000..5805bdb --- /dev/null +++ b/src/Components/Modules/PlayerStats.hpp @@ -0,0 +1,80 @@ +#pragma once + +namespace Components +{ + class PlayerStats : public Component + { + public: + static std::string JsonToString(const nlohmann::json& j) + { + return j.dump(); + } + + static nlohmann::json Get(const std::string& key) + { + const auto cfg = ReadPlayerStats(); + if (!cfg.is_object() || !cfg.contains(key) || !cfg[key].is_null()) + { + Set(key, defaultValue); + return cfg[key]; + } + + return cfg[key]; + } + + static nlohmann::json GetStruct(const std::string& name, const std::string& field) + { + const auto cfg = ReadPlayerStats(); + if (!cfg.is_object() || !cfg.contains(name) || !cfg[name].is_object() || !cfg[name].contains(field) || cfg[name][field].is_null()) + { + SetStruct(name, field, defaultValue); // You may want to handle default value differently + return defaultValue; + } + + return cfg[name][field]; + } + + static void Set(const std::string& key, const nlohmann::json& value) + { + auto cfg = ReadPlayerStats(); + cfg[key] = value; + WriteData(cfg); + } + + static void SetStruct(const std::string& name, const std::string& field, const nlohmann::json& value) + { + auto cfg = ReadPlayerStats(); + cfg[name][field] = value; + WriteData(cfg); + } + + PlayerStats(); + ~PlayerStats(); + private: + inline static int defaultValue = 0; + + static uint32_t CalculateSeconds(uint32_t totalMilliseconds); + static uint32_t CalculateMinutes(uint32_t totalMilliseconds); + static uint32_t CalculateHours(uint32_t totalMilliseconds); + static uint32_t CalculateDays(uint32_t totalMilliseconds); + + static std::string GetPlayerStatsPath(); + static nlohmann::json ReadPlayerStats(); + static void WriteData(const nlohmann::json& json); + static void ResetData(); + static void DefaultStats(); + static void PlayTimeStart(); + static void ChangeItemDefText(const char* menuName, const char* itemDefName, const char* value); + static const char* getHighestCompletedDifficulty(const float stat_easy, const float stat_regular, const float stat_hardened, const float stat_veteran); + static double CalculateAccuracy(uint32_t bullets_fired, uint32_t bullets_hit); + static float getStatProgression(const std::string& difficulty_string, int difficulty); + static float getStatEasy(const std::string& difficulty_string); + static float getStatRegular(const std::string& difficulty_string); + static float getStatHardened(const std::string& difficulty_string); + static float getStatVeteran(const std::string& difficulty_string); + static float getStatIntel(int intelCount, int totalIntelItems); + static int getTotalPercentCompleteSP(); + + static void Com_Init_Try_Block_Function_Stub(); + }; +} \ No newline at end of file diff --git a/src/Utils/String.cpp b/src/Utils/String.cpp index 9e77d15..a79bea0 100644 --- a/src/Utils/String.cpp +++ b/src/Utils/String.cpp @@ -7,6 +7,20 @@ namespace Utils { namespace String { + const char* VA_NEW(const char* fmt, ...) + { + static char g_vaBuffer[64][65536]; + static int g_vaNextBufferIndex = 0; + + va_list ap; + va_start(ap, fmt); + char* dest = g_vaBuffer[g_vaNextBufferIndex]; + vsnprintf(g_vaBuffer[g_vaNextBufferIndex], 65536, fmt, ap); + g_vaNextBufferIndex = (g_vaNextBufferIndex + 1) % 64; + va_end(ap); + return dest; + } + const char* VA(const char* fmt, ...) { static VAProvider<4, 100> globalProvider; diff --git a/src/Utils/String.hpp b/src/Utils/String.hpp index d3a3cbb..1a9687b 100644 --- a/src/Utils/String.hpp +++ b/src/Utils/String.hpp @@ -73,6 +73,7 @@ namespace Utils }; const char* VA(const char* fmt, ...); + const char* VA_NEW(const char* fmt, ...); template // This should display a nice "nullptr" instead of a number static void SanitizeFormatArgs(Arg& arg)