# include "DiabloUI/hero/selhero.h"
# include <algorithm>
# include <chrono>
# include <cstdint>
# include <random>
# include <fmt/format.h>
# include "DiabloUI/diabloui.h"
# include "DiabloUI/dialogs.h"
# include "DiabloUI/multi/selgame.h"
# include "DiabloUI/scrollbar.h"
# include "DiabloUI/selok.h"
# include "DiabloUI/selyesno.h"
# include "control.h"
# include "controls/plrctrls.h"
# include "menu.h"
# include "options.h"
# include "pfile.h"
# include "utils/language.h"
# include "utils/str_cat.hpp"
# include "utils/utf8.hpp"
namespace devilution {
bool selhero_endMenu ;
bool selhero_isMultiPlayer ;
bool ( * gfnHeroInfo ) ( bool ( * fninfofunc ) ( _uiheroinfo * ) ) ;
bool ( * gfnHeroCreate ) ( _uiheroinfo * ) ;
void ( * gfnHeroStats ) ( unsigned int , _uidefaultstats * ) ;
namespace {
std : : size_t selhero_SaveCount = 0 ;
_uiheroinfo selhero_heros [ MAX_CHARACTERS ] ;
_uiheroinfo selhero_heroInfo ;
char textStats [ 6 ] [ 4 ] ;
const char * title = " " ;
_selhero_selections selhero_result ;
bool selhero_navigateYesNo ;
bool selhero_isSavegame ;
std : : vector < std : : unique_ptr < UiItemBase > > vecSelHeroDialog ;
std : : vector < std : : unique_ptr < UiListItem > > vecSelHeroDlgItems ;
std : : vector < std : : unique_ptr < UiItemBase > > vecSelDlgItems ;
UiImageClx * SELHERO_DIALOG_HERO_IMG ;
void SelheroListFocus ( int value ) ;
void SelheroListSelect ( int value ) ;
void SelheroListEsc ( ) ;
void SelheroLoadFocus ( int value ) ;
void SelheroLoadSelect ( int value ) ;
void SelheroNameSelect ( int value ) ;
void SelheroNameEsc ( ) ;
void SelheroClassSelectorFocus ( int value ) ;
void SelheroClassSelectorSelect ( int value ) ;
void SelheroClassSelectorEsc ( ) ;
const char * SelheroGenerateName ( HeroClass heroClass ) ;
void SelheroUiFocusNavigationYesNo ( )
{
if ( selhero_isSavegame )
UiFocusNavigationYesNo ( ) ;
}
void SelheroFree ( )
{
ArtBackground = std : : nullopt ;
vecSelHeroDialog . clear ( ) ;
vecSelDlgItems . clear ( ) ;
vecSelHeroDlgItems . clear ( ) ;
UnloadScrollBar ( ) ;
}
void SelheroSetStats ( )
{
SELHERO_DIALOG_HERO_IMG - > setSprite ( UiGetHeroDialogSprite ( static_cast < size_t > ( selhero_heroInfo . heroclass ) ) ) ;
CopyUtf8 ( textStats [ 0 ] , StrCat ( selhero_heroInfo . level ) , sizeof ( textStats [ 0 ] ) ) ;
CopyUtf8 ( textStats [ 1 ] , StrCat ( selhero_heroInfo . strength ) , sizeof ( textStats [ 1 ] ) ) ;
CopyUtf8 ( textStats [ 2 ] , StrCat ( selhero_heroInfo . magic ) , sizeof ( textStats [ 2 ] ) ) ;
CopyUtf8 ( textStats [ 3 ] , StrCat ( selhero_heroInfo . dexterity ) , sizeof ( textStats [ 3 ] ) ) ;
CopyUtf8 ( textStats [ 4 ] , StrCat ( selhero_heroInfo . vitality ) , sizeof ( textStats [ 4 ] ) ) ;
CopyUtf8 ( textStats [ 5 ] , StrCat ( selhero_heroInfo . saveNumber ) , sizeof ( textStats [ 5 ] ) ) ;
}
void RenderDifficultyIndicators ( )
{
if ( ! selhero_isSavegame )
return ;
const uint16_t width = ( * DifficultyIndicator ) [ 0 ] . width ( ) ;
const uint16_t height = ( * DifficultyIndicator ) [ 0 ] . height ( ) ;
SDL_Rect rect = MakeSdlRect (
SELHERO_DIALOG_HERO_IMG - > m_rect . x + 1 ,
SELHERO_DIALOG_HERO_IMG - > m_rect . y + SELHERO_DIALOG_HERO_IMG - > m_rect . h - height - 1 ,
width ,
height ) ;
for ( int i = 0 ; i < = DIFF_LAST ; i + + ) {
if ( i > = selhero_heroInfo . herorank )
break ;
UiRenderItem ( UiImageClx ( ( * DifficultyIndicator ) [ 0 ] , rect , UiFlags : : None ) ) ;
rect . x + = width ;
}
}
UiArtTextButton * SELLIST_DIALOG_DELETE_BUTTON ;
bool SelHeroGetHeroInfo ( _uiheroinfo * pInfo )
{
selhero_heros [ selhero_SaveCount ] = * pInfo ;
selhero_SaveCount + + ;
return true ;
}
void SelheroListFocus ( int value )
{
const auto index = static_cast < std : : size_t > ( value ) ;
UiFlags baseFlags = UiFlags : : AlignCenter | UiFlags : : FontSize30 ;
if ( selhero_SaveCount ! = 0 & & index < selhero_SaveCount ) {
memcpy ( & selhero_heroInfo , & selhero_heros [ index ] , sizeof ( selhero_heroInfo ) ) ;
SelheroSetStats ( ) ;
SELLIST_DIALOG_DELETE_BUTTON - > SetFlags ( baseFlags | UiFlags : : ColorUiGold ) ;
selhero_isSavegame = true ;
return ;
}
SELHERO_DIALOG_HERO_IMG - > setSprite ( UiGetHeroDialogSprite ( enum_size < HeroClass > : : value ) ) ;
for ( char * textStat : textStats )
strcpy ( textStat , " -- " ) ;
SELLIST_DIALOG_DELETE_BUTTON - > SetFlags ( baseFlags | UiFlags : : ColorUiSilver | UiFlags : : ElementDisabled ) ;
selhero_isSavegame = false ;
}
bool SelheroListDeleteYesNo ( )
{
selhero_navigateYesNo = selhero_isSavegame ;
return selhero_navigateYesNo ;
}
void SelheroListSelect ( int value )
{
const Point uiPosition = GetUIRectangle ( ) . position ;
if ( static_cast < std : : size_t > ( value ) = = selhero_SaveCount ) {
vecSelDlgItems . clear ( ) ;
SDL_Rect rect1 = { ( Sint16 ) ( uiPosition . x + 264 ) , ( Sint16 ) ( uiPosition . y + 211 ) , 320 , 33 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtText > ( _ ( " Choose Class " ) . data ( ) , rect1 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiSilver , 3 ) ) ;
vecSelHeroDlgItems . clear ( ) ;
int itemH = 33 ;
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " Warrior " ) , static_cast < int > ( HeroClass : : Warrior ) ) ) ;
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " Rogue " ) , static_cast < int > ( HeroClass : : Rogue ) ) ) ;
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " Sorcerer " ) , static_cast < int > ( HeroClass : : Sorcerer ) ) ) ;
if ( gbIsHellfire ) {
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " Monk " ) , static_cast < int > ( HeroClass : : Monk ) ) ) ;
}
if ( gbBard | | * sgOptions . Gameplay . testBard ) {
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " Bard " ) , static_cast < int > ( HeroClass : : Bard ) ) ) ;
}
if ( gbBarbarian | | * sgOptions . Gameplay . testBarbarian ) {
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " Barbarian " ) , static_cast < int > ( HeroClass : : Barbarian ) ) ) ;
}
if ( vecSelHeroDlgItems . size ( ) > 4 )
itemH = 26 ;
int itemY = 246 + ( 176 - vecSelHeroDlgItems . size ( ) * itemH ) / 2 ;
vecSelDlgItems . push_back ( std : : make_unique < UiList > ( vecSelHeroDlgItems , vecSelHeroDlgItems . size ( ) , uiPosition . x + 264 , ( uiPosition . y + itemY ) , 320 , itemH , UiFlags : : AlignCenter | UiFlags : : FontSize24 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect2 = { ( Sint16 ) ( uiPosition . x + 279 ) , ( Sint16 ) ( uiPosition . y + 429 ) , 140 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " OK " ) , & UiFocusNavigationSelect , rect2 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect3 = { ( Sint16 ) ( uiPosition . x + 429 ) , ( Sint16 ) ( uiPosition . y + 429 ) , 140 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " Cancel " ) , & UiFocusNavigationEsc , rect3 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
UiInitList ( SelheroClassSelectorFocus , SelheroClassSelectorSelect , SelheroClassSelectorEsc , vecSelDlgItems , true ) ;
memset ( & selhero_heroInfo . name , 0 , sizeof ( selhero_heroInfo . name ) ) ;
selhero_heroInfo . saveNumber = pfile_ui_get_first_unused_save_num ( ) ;
SelheroSetStats ( ) ;
title = selhero_isMultiPlayer ? _ ( " New Multi Player Hero " ) . data ( ) : _ ( " New Single Player Hero " ) . data ( ) ;
selhero_isSavegame = false ;
return ;
}
if ( selhero_heroInfo . hassaved ) {
vecSelDlgItems . clear ( ) ;
SDL_Rect rect1 = { ( Sint16 ) ( uiPosition . x + 264 ) , ( Sint16 ) ( uiPosition . y + 211 ) , 320 , 33 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtText > ( _ ( " Save File Exists " ) . data ( ) , rect1 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiSilver , 3 ) ) ;
vecSelHeroDlgItems . clear ( ) ;
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " Load Game " ) , 0 ) ) ;
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " New Game " ) , 1 ) ) ;
vecSelDlgItems . push_back ( std : : make_unique < UiList > ( vecSelHeroDlgItems , vecSelHeroDlgItems . size ( ) , uiPosition . x + 265 , ( uiPosition . y + 285 ) , 320 , 33 , UiFlags : : AlignCenter | UiFlags : : FontSize24 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect2 = { ( Sint16 ) ( uiPosition . x + 279 ) , ( Sint16 ) ( uiPosition . y + 427 ) , 140 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " OK " ) , & UiFocusNavigationSelect , rect2 , UiFlags : : AlignCenter | UiFlags : : VerticalCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect3 = { ( Sint16 ) ( uiPosition . x + 429 ) , ( Sint16 ) ( uiPosition . y + 427 ) , 140 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " Cancel " ) , & UiFocusNavigationEsc , rect3 , UiFlags : : AlignCenter | UiFlags : : VerticalCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
UiInitList ( SelheroLoadFocus , SelheroLoadSelect , selhero_List_Init , vecSelDlgItems , true ) ;
title = _ ( " Single Player Characters " ) . data ( ) ;
return ;
}
SelheroLoadSelect ( 1 ) ;
}
void SelheroListEsc ( )
{
UiInitList_clear ( ) ;
selhero_endMenu = true ;
selhero_result = SELHERO_PREVIOUS ;
}
void SelheroClassSelectorFocus ( int value )
{
const auto heroClass = static_cast < HeroClass > ( vecSelHeroDlgItems [ value ] - > m_value ) ;
_uidefaultstats defaults ;
gfnHeroStats ( static_cast < unsigned int > ( heroClass ) , & defaults ) ;
selhero_heroInfo . level = 1 ;
selhero_heroInfo . heroclass = heroClass ;
selhero_heroInfo . strength = defaults . strength ;
selhero_heroInfo . magic = defaults . magic ;
selhero_heroInfo . dexterity = defaults . dexterity ;
selhero_heroInfo . vitality = defaults . vitality ;
SelheroSetStats ( ) ;
}
bool ShouldPrefillHeroName ( )
{
# if defined(PREFILL_PLAYER_NAME)
return true ;
# else
return ControlMode ! = ControlTypes : : KeyboardAndMouse ;
# endif
}
void RemoveSelHeroBackground ( )
{
vecSelHeroDialog . erase ( vecSelHeroDialog . begin ( ) ) ;
ArtBackground = std : : nullopt ;
}
void AddSelHeroBackground ( )
{
LoadBackgroundArt ( " ui_art \\ selhero " ) ;
vecSelHeroDialog . insert ( vecSelHeroDialog . begin ( ) ,
std : : make_unique < UiImageClx > ( ( * ArtBackground ) [ 0 ] , MakeSdlRect ( 0 , GetUIRectangle ( ) . position . y , 0 , 0 ) , UiFlags : : AlignCenter ) ) ;
}
void SelheroClassSelectorSelect ( int value )
{
auto hClass = static_cast < HeroClass > ( vecSelHeroDlgItems [ value ] - > m_value ) ;
if ( gbIsSpawn & & ( hClass = = HeroClass : : Rogue | | hClass = = HeroClass : : Sorcerer | | ( hClass = = HeroClass : : Bard & & ! gbBard ) ) ) {
RemoveSelHeroBackground ( ) ;
UiSelOkDialog ( nullptr , _ ( " The Rogue and Sorcerer are only available in the full retail version of Diablo. Visit https://www.gog.com/game/diablo to purchase. " ) . data ( ) , false ) ;
AddSelHeroBackground ( ) ;
SelheroListSelect ( selhero_SaveCount ) ;
return ;
}
const Point uiPosition = GetUIRectangle ( ) . position ;
title = selhero_isMultiPlayer ? _ ( " New Multi Player Hero " ) . data ( ) : _ ( " New Single Player Hero " ) . data ( ) ;
memset ( selhero_heroInfo . name , ' \0 ' , sizeof ( selhero_heroInfo . name ) ) ;
if ( ShouldPrefillHeroName ( ) )
strcpy ( selhero_heroInfo . name , SelheroGenerateName ( selhero_heroInfo . heroclass ) ) ;
vecSelDlgItems . clear ( ) ;
SDL_Rect rect1 = { ( Sint16 ) ( uiPosition . x + 264 ) , ( Sint16 ) ( uiPosition . y + 211 ) , 320 , 33 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtText > ( _ ( " Enter Name " ) . data ( ) , rect1 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiSilver , 3 ) ) ;
SDL_Rect rect2 = { ( Sint16 ) ( uiPosition . x + 265 ) , ( Sint16 ) ( uiPosition . y + 317 ) , 320 , 33 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiEdit > ( _ ( " Enter Name " ) , selhero_heroInfo . name , 15 , false , rect2 , UiFlags : : FontSize24 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect3 = { ( Sint16 ) ( uiPosition . x + 279 ) , ( Sint16 ) ( uiPosition . y + 429 ) , 140 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " OK " ) , & UiFocusNavigationSelect , rect3 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect4 = { ( Sint16 ) ( uiPosition . x + 429 ) , ( Sint16 ) ( uiPosition . y + 429 ) , 140 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " Cancel " ) , & UiFocusNavigationEsc , rect4 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
UiInitList ( nullptr , SelheroNameSelect , SelheroNameEsc , vecSelDlgItems ) ;
}
void SelheroClassSelectorEsc ( )
{
vecSelDlgItems . clear ( ) ;
vecSelHeroDlgItems . clear ( ) ;
if ( selhero_SaveCount ! = 0 ) {
selhero_List_Init ( ) ;
return ;
}
SelheroListEsc ( ) ;
}
void SelheroNameSelect ( int /*value*/ )
{
// only check names in multiplayer, we don't care about them in single
if ( selhero_isMultiPlayer & & ! UiValidPlayerName ( selhero_heroInfo . name ) ) {
RemoveSelHeroBackground ( ) ;
UiSelOkDialog ( title , _ ( " Invalid name. A name cannot contain spaces, reserved characters, or reserved words. \n " ) . data ( ) , false ) ;
AddSelHeroBackground ( ) ;
} else {
if ( gfnHeroCreate ( & selhero_heroInfo ) ) {
SelheroLoadSelect ( 1 ) ;
return ;
}
UiErrorOkDialog ( _ ( /* TRANSLATORS: Error Message */ " Unable to create character. " ) , vecSelHeroDialog ) ;
}
memset ( selhero_heroInfo . name , ' \0 ' , sizeof ( selhero_heroInfo . name ) ) ;
SelheroClassSelectorSelect ( 0 ) ;
}
void SelheroNameEsc ( )
{
SelheroListSelect ( selhero_SaveCount ) ;
}
void SelheroLoadFocus ( int value )
{
}
void SelheroLoadSelect ( int value )
{
UiInitList_clear ( ) ;
selhero_endMenu = true ;
if ( vecSelHeroDlgItems [ value ] - > m_value = = 0 ) {
selhero_result = SELHERO_CONTINUE ;
return ;
}
if ( ! selhero_isMultiPlayer ) {
// This is part of a dangerous hack to enable difficulty selection in single-player.
// FIXME: Dialogs should not refer to each other's variables.
// We disable `selhero_endMenu` and replace the background and art
// and the item list with the difficulty selection ones.
//
// This means selhero's render loop will render selgame's items,
// which happens to work because the render loops are similar.
selhero_endMenu = false ;
// Set this to false so that we do not attempt to render difficulty indicators.
selhero_isSavegame = false ;
SelheroFree ( ) ;
LoadBackgroundArt ( " ui_art \\ selgame " ) ;
selgame_GameSelection_Select ( 0 ) ;
}
selhero_result = SELHERO_NEW_DUNGEON ;
}
const char * SelheroGenerateName ( HeroClass heroClass )
{
static const char * const Names [ 6 ] [ 10 ] = {
{
// Warrior
" Aidan " ,
" Qarak " ,
" Born " ,
" Cathan " ,
" Halbu " ,
" Lenalas " ,
" Maximus " ,
" Vane " ,
" Myrdgar " ,
" Rothat " ,
} ,
{
// Rogue
" Moreina " ,
" Akara " ,
" Kashya " ,
" Flavie " ,
" Divo " ,
" Oriana " ,
" Iantha " ,
" Shikha " ,
" Basanti " ,
" Elexa " ,
} ,
{
// Sorcerer
" Jazreth " ,
" Drognan " ,
" Armin " ,
" Fauztin " ,
" Jere " ,
" Kazzulk " ,
" Ranslor " ,
" Sarnakyle " ,
" Valthek " ,
" Horazon " ,
} ,
{
// Monk
" Akyev " ,
" Dvorak " ,
" Kekegi " ,
" Kharazim " ,
" Mikulov " ,
" Shenlong " ,
" Vedenin " ,
" Vhalit " ,
" Vylnas " ,
" Zhota " ,
} ,
{
// Bard (uses Rogue names)
" Moreina " ,
" Akara " ,
" Kashya " ,
" Flavie " ,
" Divo " ,
" Oriana " ,
" Iantha " ,
" Shikha " ,
" Basanti " ,
" Elexa " ,
} ,
{
// Barbarian
" Alaric " ,
" Barloc " ,
" Egtheow " ,
" Guthlaf " ,
" Heorogar " ,
" Hrothgar " ,
" Oslaf " ,
" Qual-Kehk " ,
" Ragnar " ,
" Ulf " ,
} ,
} ;
int iRand = rand ( ) % 10 ;
return Names [ static_cast < std : : size_t > ( heroClass ) % 6 ] [ iRand ] ;
}
} // namespace
void selhero_Init ( )
{
AddSelHeroBackground ( ) ;
UiAddLogo ( & vecSelHeroDialog ) ;
LoadScrollBar ( ) ;
selhero_SaveCount = 0 ;
gfnHeroInfo ( SelHeroGetHeroInfo ) ;
std : : reverse ( selhero_heros , selhero_heros + selhero_SaveCount ) ;
const Point uiPosition = GetUIRectangle ( ) . position ;
vecSelDlgItems . clear ( ) ;
SDL_Rect rect = MakeSdlRect ( uiPosition . x + 24 , uiPosition . y + 161 , 590 , 35 ) ;
vecSelHeroDialog . push_back ( std : : make_unique < UiArtText > ( & title , rect , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiSilver , 3 ) ) ;
rect = MakeSdlRect ( uiPosition . x + 30 , uiPosition . y + 211 , 180 , 76 ) ;
auto heroImg = std : : make_unique < UiImageClx > ( UiGetHeroDialogSprite ( 0 ) , rect , UiFlags : : None ) ;
SELHERO_DIALOG_HERO_IMG = heroImg . get ( ) ;
vecSelHeroDialog . push_back ( std : : move ( heroImg ) ) ;
const UiFlags labelFlags = UiFlags : : FontSize12 | UiFlags : : ColorUiSilverDark | UiFlags : : AlignRight ;
const UiFlags valueFlags = UiFlags : : FontSize12 | UiFlags : : ColorUiSilverDark | UiFlags : : AlignCenter ;
const int labelX = uiPosition . x + 39 ;
const int valueX = uiPosition . x + 159 ;
const int labelWidth = 110 ;
const int valueWidth = 40 ;
const int statHeight = 21 ;
vecSelHeroDialog . push_back ( std : : make_unique < UiArtText > ( _ ( " Level: " ) . data ( ) , MakeSdlRect ( labelX , uiPosition . y + 323 , labelWidth , statHeight ) , labelFlags ) ) ;
vecSelHeroDialog . push_back ( std : : make_unique < UiArtText > ( textStats [ 0 ] , MakeSdlRect ( valueX , uiPosition . y + 323 , valueWidth , statHeight ) , valueFlags ) ) ;
const char * statLabels [ ] {
_ ( " Strength: " ) . data ( ) , _ ( " Magic: " ) . data ( ) , _ ( " Dexterity: " ) . data ( ) , _ ( " Vitality: " ) . data ( ) ,
# ifdef _DEBUG
_ ( " Savegame: " ) . data ( )
# endif
} ;
int statY = uiPosition . y + 358 ;
for ( size_t i = 0 ; i < sizeof ( statLabels ) / sizeof ( statLabels [ 0 ] ) ; + + i ) {
vecSelHeroDialog . push_back ( std : : make_unique < UiArtText > ( statLabels [ i ] , MakeSdlRect ( labelX , statY , labelWidth , statHeight ) , labelFlags ) ) ;
vecSelHeroDialog . push_back ( std : : make_unique < UiArtText > ( textStats [ i + 1 ] , MakeSdlRect ( valueX , statY , valueWidth , statHeight ) , valueFlags ) ) ;
statY + = statHeight ;
}
}
void selhero_List_Init ( )
{
const Point uiPosition = GetUIRectangle ( ) . position ;
size_t selectedItem = 0 ;
vecSelDlgItems . clear ( ) ;
SDL_Rect rect1 = { ( Sint16 ) ( uiPosition . x + 264 ) , ( Sint16 ) ( uiPosition . y + 211 ) , 320 , 33 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtText > ( _ ( " Select Hero " ) . data ( ) , rect1 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiSilver , 3 ) ) ;
vecSelHeroDlgItems . clear ( ) ;
for ( std : : size_t i = 0 ; i < selhero_SaveCount ; i + + ) {
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( selhero_heros [ i ] . name , static_cast < int > ( i ) ) ) ;
if ( selhero_heros [ i ] . saveNumber = = selhero_heroInfo . saveNumber )
selectedItem = i ;
}
vecSelHeroDlgItems . push_back ( std : : make_unique < UiListItem > ( _ ( " New Hero " ) . data ( ) , static_cast < int > ( selhero_SaveCount ) ) ) ;
vecSelDlgItems . push_back ( std : : make_unique < UiList > ( vecSelHeroDlgItems , 6 , uiPosition . x + 265 , ( uiPosition . y + 256 ) , 320 , 26 , UiFlags : : AlignCenter | UiFlags : : FontSize24 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect2 = { ( Sint16 ) ( uiPosition . x + 585 ) , ( Sint16 ) ( uiPosition . y + 244 ) , 25 , 178 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiScrollbar > ( ( * ArtScrollBarBackground ) [ 0 ] , ( * ArtScrollBarThumb ) [ 0 ] , * ArtScrollBarArrow , rect2 ) ) ;
SDL_Rect rect3 = { ( Sint16 ) ( uiPosition . x + 239 ) , ( Sint16 ) ( uiPosition . y + 429 ) , 120 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " OK " ) , & UiFocusNavigationSelect , rect3 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
SDL_Rect rect4 = { ( Sint16 ) ( uiPosition . x + 364 ) , ( Sint16 ) ( uiPosition . y + 429 ) , 120 , 35 } ;
auto setlistDialogDeleteButton = std : : make_unique < UiArtTextButton > ( _ ( " Delete " ) , & SelheroUiFocusNavigationYesNo , rect4 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiSilver | UiFlags : : ElementDisabled ) ;
SELLIST_DIALOG_DELETE_BUTTON = setlistDialogDeleteButton . get ( ) ;
vecSelDlgItems . push_back ( std : : move ( setlistDialogDeleteButton ) ) ;
SDL_Rect rect5 = { ( Sint16 ) ( uiPosition . x + 489 ) , ( Sint16 ) ( uiPosition . y + 429 ) , 120 , 35 } ;
vecSelDlgItems . push_back ( std : : make_unique < UiArtTextButton > ( _ ( " Cancel " ) , & UiFocusNavigationEsc , rect5 , UiFlags : : AlignCenter | UiFlags : : FontSize30 | UiFlags : : ColorUiGold ) ) ;
UiInitList ( SelheroListFocus , SelheroListSelect , SelheroListEsc , vecSelDlgItems , false , nullptr , SelheroListDeleteYesNo , selectedItem ) ;
if ( selhero_isMultiPlayer ) {
title = _ ( " Multi Player Characters " ) . data ( ) ;
} else {
title = _ ( " Single Player Characters " ) . data ( ) ;
}
}
static void UiSelHeroDialog (
bool ( * fninfo ) ( bool ( * fninfofunc ) ( _uiheroinfo * ) ) ,
bool ( * fncreate ) ( _uiheroinfo * ) ,
void ( * fnstats ) ( unsigned int , _uidefaultstats * ) ,
bool ( * fnremove ) ( _uiheroinfo * ) ,
_selhero_selections * dlgresult ,
uint32_t * saveNumber )
{
do {
gfnHeroInfo = fninfo ;
gfnHeroCreate = fncreate ;
gfnHeroStats = fnstats ;
selhero_result = * dlgresult ;
selhero_navigateYesNo = false ;
selhero_Init ( ) ;
if ( selhero_SaveCount ! = 0 ) {
selhero_heroInfo = { } ;
// Search last used save and remember it as selected item
for ( size_t i = 0 ; i < selhero_SaveCount ; i + + ) {
if ( selhero_heros [ i ] . saveNumber = = * saveNumber ) {
memcpy ( & selhero_heroInfo , & selhero_heros [ i ] , sizeof ( selhero_heroInfo ) ) ;
break ;
}
}
selhero_List_Init ( ) ;
} else {
SelheroListSelect ( selhero_SaveCount ) ;
}
selhero_endMenu = false ;
while ( ! selhero_endMenu & & ! selhero_navigateYesNo ) {
UiClearScreen ( ) ;
UiRenderItems ( vecSelHeroDialog ) ;
RenderDifficultyIndicators ( ) ;
UiPollAndRender ( ) ;
}
SelheroFree ( ) ;
if ( selhero_navigateYesNo ) {
char dialogTitle [ 128 ] ;
char dialogText [ 256 ] ;
if ( selhero_isMultiPlayer ) {
CopyUtf8 ( dialogTitle , _ ( " Delete Multi Player Hero " ) , sizeof ( dialogTitle ) ) ;
} else {
CopyUtf8 ( dialogTitle , _ ( " Delete Single Player Hero " ) , sizeof ( dialogTitle ) ) ;
}
strcpy ( dialogText , fmt : : format ( fmt : : runtime ( _ ( " Are you sure you want to delete the character \" {:s} \" ? " ) ) , selhero_heroInfo . name ) . c_str ( ) ) ;
if ( UiSelHeroYesNoDialog ( dialogTitle , dialogText ) )
fnremove ( & selhero_heroInfo ) ;
}
} while ( selhero_navigateYesNo ) ;
* dlgresult = selhero_result ;
* saveNumber = selhero_heroInfo . saveNumber ;
}
void UiSelHeroSingDialog (
bool ( * fninfo ) ( bool ( * fninfofunc ) ( _uiheroinfo * ) ) ,
bool ( * fncreate ) ( _uiheroinfo * ) ,
bool ( * fnremove ) ( _uiheroinfo * ) ,
void ( * fnstats ) ( unsigned int , _uidefaultstats * ) ,
_selhero_selections * dlgresult ,
uint32_t * saveNumber ,
_difficulty * difficulty )
{
selhero_isMultiPlayer = false ;
UiSelHeroDialog ( fninfo , fncreate , fnstats , fnremove , dlgresult , saveNumber ) ;
* difficulty = nDifficulty ;
}
void UiSelHeroMultDialog (
bool ( * fninfo ) ( bool ( * fninfofunc ) ( _uiheroinfo * ) ) ,
bool ( * fncreate ) ( _uiheroinfo * ) ,
bool ( * fnremove ) ( _uiheroinfo * ) ,
void ( * fnstats ) ( unsigned int , _uidefaultstats * ) ,
_selhero_selections * dlgresult ,
uint32_t * saveNumber )
{
selhero_isMultiPlayer = true ;
UiSelHeroDialog ( fninfo , fncreate , fnstats , fnremove , dlgresult , saveNumber ) ;
}
} // namespace devilution