You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
22 KiB
625 lines
22 KiB
|
3 years ago
|
#include "DiabloUI/hero/selhero.h"
|
||
|
7 years ago
|
|
||
|
7 years ago
|
#include <algorithm>
|
||
|
6 years ago
|
#include <chrono>
|
||
|
|
#include <random>
|
||
|
7 years ago
|
|
||
|
5 years ago
|
#include <fmt/format.h>
|
||
|
|
|
||
|
7 years ago
|
#include "DiabloUI/diabloui.h"
|
||
|
6 years ago
|
#include "DiabloUI/dialogs.h"
|
||
|
3 years ago
|
#include "DiabloUI/multi/selgame.h"
|
||
|
6 years ago
|
#include "DiabloUI/scrollbar.h"
|
||
|
5 years ago
|
#include "DiabloUI/selok.h"
|
||
|
|
#include "DiabloUI/selyesno.h"
|
||
|
5 years ago
|
#include "control.h"
|
||
|
4 years ago
|
#include "controls/plrctrls.h"
|
||
|
4 years ago
|
#include "menu.h"
|
||
|
5 years ago
|
#include "options.h"
|
||
|
5 years ago
|
#include "pfile.h"
|
||
|
5 years ago
|
#include "utils/language.h"
|
||
|
4 years ago
|
#include "utils/str_cat.hpp"
|
||
|
4 years ago
|
#include "utils/utf8.hpp"
|
||
|
7 years ago
|
|
||
|
5 years ago
|
namespace devilution {
|
||
|
7 years ago
|
|
||
|
5 years ago
|
bool selhero_endMenu;
|
||
|
|
bool selhero_isMultiPlayer;
|
||
|
|
|
||
|
|
bool (*gfnHeroInfo)(bool (*fninfofunc)(_uiheroinfo *));
|
||
|
|
bool (*gfnHeroCreate)(_uiheroinfo *);
|
||
|
|
void (*gfnHeroStats)(unsigned int, _uidefaultstats *);
|
||
|
|
|
||
|
|
namespace {
|
||
|
6 years ago
|
|
||
|
7 years ago
|
std::size_t selhero_SaveCount = 0;
|
||
|
7 years ago
|
_uiheroinfo selhero_heros[MAX_CHARACTERS];
|
||
|
|
_uiheroinfo selhero_heroInfo;
|
||
|
5 years ago
|
char textStats[6][4];
|
||
|
5 years ago
|
const char *title = "";
|
||
|
5 years ago
|
_selhero_selections selhero_result;
|
||
|
7 years ago
|
bool selhero_navigateYesNo;
|
||
|
4 years ago
|
bool selhero_isSavegame;
|
||
|
7 years ago
|
|
||
|
5 years ago
|
std::vector<std::unique_ptr<UiItemBase>> vecSelHeroDialog;
|
||
|
|
std::vector<std::unique_ptr<UiListItem>> vecSelHeroDlgItems;
|
||
|
|
std::vector<std::unique_ptr<UiItemBase>> vecSelDlgItems;
|
||
|
7 years ago
|
|
||
|
4 years ago
|
UiImageClx *SELHERO_DIALOG_HERO_IMG;
|
||
|
7 years ago
|
|
||
|
5 years ago
|
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()
|
||
|
7 years ago
|
{
|
||
|
4 years ago
|
if (selhero_isSavegame)
|
||
|
7 years ago
|
UiFocusNavigationYesNo();
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroFree()
|
||
|
7 years ago
|
{
|
||
|
4 years ago
|
ArtBackground = std::nullopt;
|
||
|
6 years ago
|
|
||
|
|
vecSelHeroDialog.clear();
|
||
|
|
|
||
|
5 years ago
|
vecSelDlgItems.clear();
|
||
|
|
vecSelHeroDlgItems.clear();
|
||
|
5 years ago
|
UnloadScrollBar();
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroSetStats()
|
||
|
7 years ago
|
{
|
||
|
4 years ago
|
SELHERO_DIALOG_HERO_IMG->setSprite(UiGetHeroDialogSprite(static_cast<size_t>(selhero_heroInfo.heroclass)));
|
||
|
4 years ago
|
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]));
|
||
|
4 years ago
|
}
|
||
|
4 years ago
|
|
||
|
4 years ago
|
void RenderDifficultyIndicators()
|
||
|
|
{
|
||
|
|
if (!selhero_isSavegame)
|
||
|
|
return;
|
||
|
|
const uint16_t width = (*DifficultyIndicator[0])[0].width();
|
||
|
|
const uint16_t height = (*DifficultyIndicator[0])[0].height();
|
||
|
|
SDL_Rect rect = MakeSdlRect(SELHERO_DIALOG_HERO_IMG->m_rect.x, SELHERO_DIALOG_HERO_IMG->m_rect.y - height - 2, width, height);
|
||
|
4 years ago
|
for (int i = 0; i <= DIFF_LAST; i++) {
|
||
|
4 years ago
|
UiRenderItem(UiImageClx((*DifficultyIndicator[i < selhero_heroInfo.herorank ? 0 : 1])[0], rect, UiFlags::None));
|
||
|
|
rect.x += width;
|
||
|
4 years ago
|
}
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
6 years ago
|
UiArtTextButton *SELLIST_DIALOG_DELETE_BUTTON;
|
||
|
|
|
||
|
5 years ago
|
bool SelHeroGetHeroInfo(_uiheroinfo *pInfo)
|
||
|
5 years ago
|
{
|
||
|
|
selhero_heros[selhero_SaveCount] = *pInfo;
|
||
|
5 years ago
|
|
||
|
5 years ago
|
selhero_SaveCount++;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroListFocus(int value)
|
||
|
7 years ago
|
{
|
||
|
5 years ago
|
const auto index = static_cast<std::size_t>(value);
|
||
|
5 years ago
|
UiFlags baseFlags = UiFlags::AlignCenter | UiFlags::FontSize30;
|
||
|
5 years ago
|
if (selhero_SaveCount != 0 && index < selhero_SaveCount) {
|
||
|
7 years ago
|
memcpy(&selhero_heroInfo, &selhero_heros[index], sizeof(selhero_heroInfo));
|
||
|
5 years ago
|
SelheroSetStats();
|
||
|
4 years ago
|
SELLIST_DIALOG_DELETE_BUTTON->SetFlags(baseFlags | UiFlags::ColorUiGold);
|
||
|
4 years ago
|
selhero_isSavegame = true;
|
||
|
7 years ago
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
4 years ago
|
SELHERO_DIALOG_HERO_IMG->setSprite(UiGetHeroDialogSprite(enum_size<HeroClass>::value));
|
||
|
4 years ago
|
for (char *textStat : textStats)
|
||
|
|
strcpy(textStat, "--");
|
||
|
4 years ago
|
SELLIST_DIALOG_DELETE_BUTTON->SetFlags(baseFlags | UiFlags::ColorUiSilver | UiFlags::ElementDisabled);
|
||
|
4 years ago
|
selhero_isSavegame = false;
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
bool SelheroListDeleteYesNo()
|
||
|
7 years ago
|
{
|
||
|
4 years ago
|
selhero_navigateYesNo = selhero_isSavegame;
|
||
|
7 years ago
|
|
||
|
|
return selhero_navigateYesNo;
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroListSelect(int value)
|
||
|
7 years ago
|
{
|
||
|
4 years ago
|
const Point uiPosition = GetUIRectangle().position;
|
||
|
|
|
||
|
7 years ago
|
if (static_cast<std::size_t>(value) == selhero_SaveCount) {
|
||
|
5 years ago
|
vecSelDlgItems.clear();
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Choose Class").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
|
||
|
6 years ago
|
|
||
|
5 years ago
|
vecSelHeroDlgItems.clear();
|
||
|
6 years ago
|
int itemH = 33;
|
||
|
5 years ago
|
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)));
|
||
|
5 years ago
|
if (gbIsHellfire) {
|
||
|
5 years ago
|
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Monk"), static_cast<int>(HeroClass::Monk)));
|
||
|
5 years ago
|
}
|
||
|
4 years ago
|
if (gbBard || *sgOptions.Gameplay.testBard) {
|
||
|
5 years ago
|
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Bard"), static_cast<int>(HeroClass::Bard)));
|
||
|
6 years ago
|
}
|
||
|
4 years ago
|
if (gbBarbarian || *sgOptions.Gameplay.testBarbarian) {
|
||
|
5 years ago
|
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Barbarian"), static_cast<int>(HeroClass::Barbarian)));
|
||
|
6 years ago
|
}
|
||
|
|
if (vecSelHeroDlgItems.size() > 4)
|
||
|
|
itemH = 26;
|
||
|
|
int itemY = 246 + (176 - vecSelHeroDlgItems.size() * itemH) / 2;
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiList>(vecSelHeroDlgItems, vecSelHeroDlgItems.size(), uiPosition.x + 264, (uiPosition.y + itemY), 320, itemH, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 279), (Sint16)(uiPosition.y + 429), 140, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect2, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 429), (Sint16)(uiPosition.y + 429), 140, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("Cancel"), &UiFocusNavigationEsc, rect3, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
UiInitList(SelheroClassSelectorFocus, SelheroClassSelectorSelect, SelheroClassSelectorEsc, vecSelDlgItems, true);
|
||
|
7 years ago
|
memset(&selhero_heroInfo.name, 0, sizeof(selhero_heroInfo.name));
|
||
|
5 years ago
|
selhero_heroInfo.saveNumber = pfile_ui_get_first_unused_save_num();
|
||
|
|
SelheroSetStats();
|
||
|
4 years ago
|
title = selhero_isMultiPlayer ? _("New Multi Player Hero").data() : _("New Single Player Hero").data();
|
||
|
7 years ago
|
return;
|
||
|
6 years ago
|
}
|
||
|
|
|
||
|
|
if (selhero_heroInfo.hassaved) {
|
||
|
5 years ago
|
vecSelDlgItems.clear();
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Save File Exists").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
|
||
|
6 years ago
|
|
||
|
5 years ago
|
vecSelHeroDlgItems.clear();
|
||
|
|
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("Load Game"), 0));
|
||
|
|
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("New Game"), 1));
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiList>(vecSelHeroDlgItems, vecSelHeroDlgItems.size(), uiPosition.x + 265, (uiPosition.y + 285), 320, 33, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 279), (Sint16)(uiPosition.y + 427), 140, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect2, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 429), (Sint16)(uiPosition.y + 427), 140, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("Cancel"), &UiFocusNavigationEsc, rect3, UiFlags::AlignCenter | UiFlags::VerticalCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
UiInitList(SelheroLoadFocus, SelheroLoadSelect, selhero_List_Init, vecSelDlgItems, true);
|
||
|
4 years ago
|
title = _("Single Player Characters").data();
|
||
|
7 years ago
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
SelheroLoadSelect(1);
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroListEsc()
|
||
|
7 years ago
|
{
|
||
|
6 years ago
|
UiInitList_clear();
|
||
|
|
|
||
|
7 years ago
|
selhero_endMenu = true;
|
||
|
|
selhero_result = SELHERO_PREVIOUS;
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroClassSelectorFocus(int value)
|
||
|
7 years ago
|
{
|
||
|
5 years ago
|
const auto heroClass = static_cast<HeroClass>(vecSelHeroDlgItems[value]->m_value);
|
||
|
5 years ago
|
|
||
|
7 years ago
|
_uidefaultstats defaults;
|
||
|
5 years ago
|
gfnHeroStats(static_cast<unsigned int>(heroClass), &defaults);
|
||
|
7 years ago
|
|
||
|
|
selhero_heroInfo.level = 1;
|
||
|
5 years ago
|
selhero_heroInfo.heroclass = heroClass;
|
||
|
7 years ago
|
selhero_heroInfo.strength = defaults.strength;
|
||
|
|
selhero_heroInfo.magic = defaults.magic;
|
||
|
|
selhero_heroInfo.dexterity = defaults.dexterity;
|
||
|
|
selhero_heroInfo.vitality = defaults.vitality;
|
||
|
|
|
||
|
5 years ago
|
SelheroSetStats();
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
bool ShouldPrefillHeroName()
|
||
|
5 years ago
|
{
|
||
|
5 years ago
|
#if defined(PREFILL_PLAYER_NAME)
|
||
|
5 years ago
|
return true;
|
||
|
|
#else
|
||
|
4 years ago
|
return ControlMode != ControlTypes::KeyboardAndMouse;
|
||
|
5 years ago
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
4 years ago
|
void RemoveSelHeroBackground()
|
||
|
|
{
|
||
|
|
vecSelHeroDialog.erase(vecSelHeroDialog.begin());
|
||
|
|
ArtBackground = std::nullopt;
|
||
|
|
}
|
||
|
|
|
||
|
|
void AddSelHeroBackground()
|
||
|
|
{
|
||
|
4 years ago
|
LoadBackgroundArt("ui_art\\selhero");
|
||
|
4 years ago
|
vecSelHeroDialog.insert(vecSelHeroDialog.begin(),
|
||
|
4 years ago
|
std::make_unique<UiImageClx>((*ArtBackground)[0], MakeSdlRect(0, GetUIRectangle().position.y, 0, 0), UiFlags::AlignCenter));
|
||
|
4 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroClassSelectorSelect(int value)
|
||
|
7 years ago
|
{
|
||
|
5 years ago
|
auto hClass = static_cast<HeroClass>(vecSelHeroDlgItems[value]->m_value);
|
||
|
4 years ago
|
if (gbIsSpawn && (hClass == HeroClass::Rogue || hClass == HeroClass::Sorcerer || (hClass == HeroClass::Bard && !gbBard))) {
|
||
|
4 years ago
|
RemoveSelHeroBackground();
|
||
|
4 years ago
|
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);
|
||
|
4 years ago
|
AddSelHeroBackground();
|
||
|
5 years ago
|
SelheroListSelect(selhero_SaveCount);
|
||
|
6 years ago
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
4 years ago
|
const Point uiPosition = GetUIRectangle().position;
|
||
|
|
|
||
|
4 years ago
|
title = selhero_isMultiPlayer ? _("New Multi Player Hero").data() : _("New Single Player Hero").data();
|
||
|
7 years ago
|
memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name));
|
||
|
5 years ago
|
if (ShouldPrefillHeroName())
|
||
|
4 years ago
|
strcpy(selhero_heroInfo.name, SelheroGenerateName(selhero_heroInfo.heroclass));
|
||
|
5 years ago
|
vecSelDlgItems.clear();
|
||
|
4 years ago
|
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Enter Name").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 265), (Sint16)(uiPosition.y + 317), 320, 33 };
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiEdit>(_("Enter Name"), selhero_heroInfo.name, 15, false, rect2, UiFlags::FontSize24 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 279), (Sint16)(uiPosition.y + 429), 140, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect3, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 429), (Sint16)(uiPosition.y + 429), 140, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("Cancel"), &UiFocusNavigationEsc, rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
6 years ago
|
|
||
|
4 years ago
|
UiInitList(nullptr, SelheroNameSelect, SelheroNameEsc, vecSelDlgItems);
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroClassSelectorEsc()
|
||
|
7 years ago
|
{
|
||
|
5 years ago
|
vecSelDlgItems.clear();
|
||
|
|
vecSelHeroDlgItems.clear();
|
||
|
6 years ago
|
|
||
|
5 years ago
|
if (selhero_SaveCount != 0) {
|
||
|
7 years ago
|
selhero_List_Init();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
SelheroListEsc();
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroNameSelect(int /*value*/)
|
||
|
7 years ago
|
{
|
||
|
5 years ago
|
// only check names in multiplayer, we don't care about them in single
|
||
|
5 years ago
|
if (selhero_isMultiPlayer && !UiValidPlayerName(selhero_heroInfo.name)) {
|
||
|
4 years ago
|
RemoveSelHeroBackground();
|
||
|
4 years ago
|
UiSelOkDialog(title, _("Invalid name. A name cannot contain spaces, reserved characters, or reserved words.\n").data(), false);
|
||
|
4 years ago
|
AddSelHeroBackground();
|
||
|
5 years ago
|
} else {
|
||
|
5 years ago
|
if (gfnHeroCreate(&selhero_heroInfo)) {
|
||
|
|
SelheroLoadSelect(1);
|
||
|
|
return;
|
||
|
|
}
|
||
|
3 years ago
|
UiErrorOkDialog(_(/* TRANSLATORS: Error Message */ "Unable to create character."), vecSelHeroDialog);
|
||
|
5 years ago
|
}
|
||
|
6 years ago
|
|
||
|
6 years ago
|
memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name));
|
||
|
5 years ago
|
SelheroClassSelectorSelect(0);
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroNameEsc()
|
||
|
7 years ago
|
{
|
||
|
5 years ago
|
SelheroListSelect(selhero_SaveCount);
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroLoadFocus(int value)
|
||
|
7 years ago
|
{
|
||
|
|
}
|
||
|
|
|
||
|
5 years ago
|
void SelheroLoadSelect(int value)
|
||
|
7 years ago
|
{
|
||
|
6 years ago
|
UiInitList_clear();
|
||
|
7 years ago
|
selhero_endMenu = true;
|
||
|
6 years ago
|
if (vecSelHeroDlgItems[value]->m_value == 0) {
|
||
|
7 years ago
|
selhero_result = SELHERO_CONTINUE;
|
||
|
|
return;
|
||
|
5 years ago
|
}
|
||
|
|
|
||
|
|
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.
|
||
|
6 years ago
|
selhero_endMenu = false;
|
||
|
4 years ago
|
|
||
|
|
// Set this to false so that we do not attempt to render difficulty indicators.
|
||
|
|
selhero_isSavegame = false;
|
||
|
|
|
||
|
5 years ago
|
SelheroFree();
|
||
|
4 years ago
|
LoadBackgroundArt("ui_art\\selgame");
|
||
|
6 years ago
|
selgame_GameSelection_Select(0);
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
selhero_result = SELHERO_NEW_DUNGEON;
|
||
|
7 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
const char *SelheroGenerateName(HeroClass heroClass)
|
||
|
6 years ago
|
{
|
||
|
5 years ago
|
static const char *const Names[6][10] = {
|
||
|
6 years ago
|
{
|
||
|
5 years ago
|
// Warrior
|
||
|
6 years ago
|
"Aidan",
|
||
|
|
"Qarak",
|
||
|
|
"Born",
|
||
|
|
"Cathan",
|
||
|
|
"Halbu",
|
||
|
|
"Lenalas",
|
||
|
|
"Maximus",
|
||
|
|
"Vane",
|
||
|
|
"Myrdgar",
|
||
|
|
"Rothat",
|
||
|
|
},
|
||
|
|
{
|
||
|
5 years ago
|
// Rogue
|
||
|
6 years ago
|
"Moreina",
|
||
|
|
"Akara",
|
||
|
|
"Kashya",
|
||
|
|
"Flavie",
|
||
|
|
"Divo",
|
||
|
|
"Oriana",
|
||
|
|
"Iantha",
|
||
|
|
"Shikha",
|
||
|
|
"Basanti",
|
||
|
|
"Elexa",
|
||
|
|
},
|
||
|
|
{
|
||
|
5 years ago
|
// Sorcerer
|
||
|
6 years ago
|
"Jazreth",
|
||
|
|
"Drognan",
|
||
|
|
"Armin",
|
||
|
|
"Fauztin",
|
||
|
|
"Jere",
|
||
|
|
"Kazzulk",
|
||
|
|
"Ranslor",
|
||
|
|
"Sarnakyle",
|
||
|
|
"Valthek",
|
||
|
|
"Horazon",
|
||
|
5 years ago
|
},
|
||
|
|
{
|
||
|
|
// Monk
|
||
|
|
"Akyev",
|
||
|
|
"Dvorak",
|
||
|
|
"Kekegi",
|
||
|
|
"Kharazim",
|
||
|
|
"Mikulov",
|
||
|
|
"Shenlong",
|
||
|
|
"Vedenin",
|
||
|
|
"Vhalit",
|
||
|
|
"Vylnas",
|
||
|
|
"Zhota",
|
||
|
5 years ago
|
},
|
||
|
|
{
|
||
|
|
// 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",
|
||
|
|
},
|
||
|
6 years ago
|
};
|
||
|
6 years ago
|
|
||
|
5 years ago
|
int iRand = rand() % 10;
|
||
|
6 years ago
|
|
||
|
5 years ago
|
return Names[static_cast<std::size_t>(heroClass) % 6][iRand];
|
||
|
7 years ago
|
}
|
||
|
6 years ago
|
|
||
|
5 years ago
|
} // namespace
|
||
|
|
|
||
|
|
void selhero_Init()
|
||
|
|
{
|
||
|
4 years ago
|
AddSelHeroBackground();
|
||
|
5 years ago
|
UiAddLogo(&vecSelHeroDialog);
|
||
|
|
LoadScrollBar();
|
||
|
|
|
||
|
|
selhero_SaveCount = 0;
|
||
|
5 years ago
|
gfnHeroInfo(SelHeroGetHeroInfo);
|
||
|
5 years ago
|
std::reverse(selhero_heros, selhero_heros + selhero_SaveCount);
|
||
|
|
|
||
|
4 years ago
|
const Point uiPosition = GetUIRectangle().position;
|
||
|
|
|
||
|
5 years ago
|
vecSelDlgItems.clear();
|
||
|
4 years ago
|
SDL_Rect rect = MakeSdlRect(uiPosition.x + 24, uiPosition.y + 161, 590, 35);
|
||
|
4 years ago
|
vecSelHeroDialog.push_back(std::make_unique<UiArtText>(&title, rect, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
rect = MakeSdlRect(uiPosition.x + 30, uiPosition.y + 211, 180, 76);
|
||
|
4 years ago
|
auto heroImg = std::make_unique<UiImageClx>(UiGetHeroDialogSprite(0), rect, UiFlags::None);
|
||
|
5 years ago
|
SELHERO_DIALOG_HERO_IMG = heroImg.get();
|
||
|
|
vecSelHeroDialog.push_back(std::move(heroImg));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
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;
|
||
|
5 years ago
|
|
||
|
4 years ago
|
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));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
const char *statLabels[] {
|
||
|
|
_("Strength:").data(), _("Magic:").data(), _("Dexterity:").data(), _("Vitality:").data(),
|
||
|
4 years ago
|
#ifdef _DEBUG
|
||
|
4 years ago
|
_("Savegame:").data()
|
||
|
5 years ago
|
#endif
|
||
|
4 years ago
|
};
|
||
|
|
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;
|
||
|
|
}
|
||
|
5 years ago
|
}
|
||
|
|
|
||
|
|
void selhero_List_Init()
|
||
|
|
{
|
||
|
4 years ago
|
const Point uiPosition = GetUIRectangle().position;
|
||
|
|
|
||
|
5 years ago
|
size_t selectedItem = 0;
|
||
|
5 years ago
|
vecSelDlgItems.clear();
|
||
|
5 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect1 = { (Sint16)(uiPosition.x + 264), (Sint16)(uiPosition.y + 211), 320, 33 };
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtText>(_("Select Hero").data(), rect1, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver, 3));
|
||
|
5 years ago
|
|
||
|
5 years ago
|
vecSelHeroDlgItems.clear();
|
||
|
4 years ago
|
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)
|
||
|
5 years ago
|
selectedItem = i;
|
||
|
|
}
|
||
|
4 years ago
|
vecSelHeroDlgItems.push_back(std::make_unique<UiListItem>(_("New Hero").data(), static_cast<int>(selhero_SaveCount)));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiList>(vecSelHeroDlgItems, 6, uiPosition.x + 265, (uiPosition.y + 256), 320, 26, UiFlags::AlignCenter | UiFlags::FontSize24 | UiFlags::ColorUiGold));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect2 = { (Sint16)(uiPosition.x + 585), (Sint16)(uiPosition.y + 244), 25, 178 };
|
||
|
4 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiScrollbar>((*ArtScrollBarBackground)[0], (*ArtScrollBarThumb)[0], *ArtScrollBarArrow, rect2));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect3 = { (Sint16)(uiPosition.x + 239), (Sint16)(uiPosition.y + 429), 120, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("OK"), &UiFocusNavigationSelect, rect3, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect4 = { (Sint16)(uiPosition.x + 364), (Sint16)(uiPosition.y + 429), 120, 35 };
|
||
|
5 years ago
|
auto setlistDialogDeleteButton = std::make_unique<UiArtTextButton>(_("Delete"), &SelheroUiFocusNavigationYesNo, rect4, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiSilver | UiFlags::ElementDisabled);
|
||
|
5 years ago
|
SELLIST_DIALOG_DELETE_BUTTON = setlistDialogDeleteButton.get();
|
||
|
|
vecSelDlgItems.push_back(std::move(setlistDialogDeleteButton));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
SDL_Rect rect5 = { (Sint16)(uiPosition.x + 489), (Sint16)(uiPosition.y + 429), 120, 35 };
|
||
|
5 years ago
|
vecSelDlgItems.push_back(std::make_unique<UiArtTextButton>(_("Cancel"), &UiFocusNavigationEsc, rect5, UiFlags::AlignCenter | UiFlags::FontSize30 | UiFlags::ColorUiGold));
|
||
|
5 years ago
|
|
||
|
4 years ago
|
UiInitList(SelheroListFocus, SelheroListSelect, SelheroListEsc, vecSelDlgItems, false, nullptr, SelheroListDeleteYesNo, selectedItem);
|
||
|
5 years ago
|
if (selhero_isMultiPlayer) {
|
||
|
4 years ago
|
title = _("Multi Player Characters").data();
|
||
|
5 years ago
|
} else {
|
||
|
4 years ago
|
title = _("Single Player Characters").data();
|
||
|
5 years ago
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static void UiSelHeroDialog(
|
||
|
|
bool (*fninfo)(bool (*fninfofunc)(_uiheroinfo *)),
|
||
|
|
bool (*fncreate)(_uiheroinfo *),
|
||
|
|
void (*fnstats)(unsigned int, _uidefaultstats *),
|
||
|
|
bool (*fnremove)(_uiheroinfo *),
|
||
|
|
_selhero_selections *dlgresult,
|
||
|
5 years ago
|
uint32_t *saveNumber)
|
||
|
5 years ago
|
{
|
||
|
|
do {
|
||
|
|
gfnHeroInfo = fninfo;
|
||
|
|
gfnHeroCreate = fncreate;
|
||
|
|
gfnHeroStats = fnstats;
|
||
|
|
selhero_result = *dlgresult;
|
||
|
|
|
||
|
|
selhero_navigateYesNo = false;
|
||
|
|
|
||
|
|
selhero_Init();
|
||
|
|
|
||
|
5 years ago
|
if (selhero_SaveCount != 0) {
|
||
|
5 years ago
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
5 years ago
|
selhero_List_Init();
|
||
|
|
} else {
|
||
|
5 years ago
|
SelheroListSelect(selhero_SaveCount);
|
||
|
5 years ago
|
}
|
||
|
|
|
||
|
|
selhero_endMenu = false;
|
||
|
|
while (!selhero_endMenu && !selhero_navigateYesNo) {
|
||
|
|
UiClearScreen();
|
||
|
|
UiRenderItems(vecSelHeroDialog);
|
||
|
4 years ago
|
RenderDifficultyIndicators();
|
||
|
5 years ago
|
UiPollAndRender();
|
||
|
|
}
|
||
|
5 years ago
|
SelheroFree();
|
||
|
5 years ago
|
|
||
|
|
if (selhero_navigateYesNo) {
|
||
|
4 years ago
|
char dialogTitle[128];
|
||
|
5 years ago
|
char dialogText[256];
|
||
|
|
if (selhero_isMultiPlayer) {
|
||
|
4 years ago
|
CopyUtf8(dialogTitle, _("Delete Multi Player Hero"), sizeof(dialogTitle));
|
||
|
5 years ago
|
} else {
|
||
|
4 years ago
|
CopyUtf8(dialogTitle, _("Delete Single Player Hero"), sizeof(dialogTitle));
|
||
|
5 years ago
|
}
|
||
|
4 years ago
|
strcpy(dialogText, fmt::format(fmt::runtime(_("Are you sure you want to delete the character \"{:s}\"?")), selhero_heroInfo.name).c_str());
|
||
|
5 years ago
|
|
||
|
|
if (UiSelHeroYesNoDialog(dialogTitle, dialogText))
|
||
|
|
fnremove(&selhero_heroInfo);
|
||
|
|
}
|
||
|
|
} while (selhero_navigateYesNo);
|
||
|
|
|
||
|
|
*dlgresult = selhero_result;
|
||
|
5 years ago
|
*saveNumber = selhero_heroInfo.saveNumber;
|
||
|
5 years ago
|
}
|
||
|
|
|
||
|
|
void UiSelHeroSingDialog(
|
||
|
|
bool (*fninfo)(bool (*fninfofunc)(_uiheroinfo *)),
|
||
|
|
bool (*fncreate)(_uiheroinfo *),
|
||
|
|
bool (*fnremove)(_uiheroinfo *),
|
||
|
|
void (*fnstats)(unsigned int, _uidefaultstats *),
|
||
|
|
_selhero_selections *dlgresult,
|
||
|
5 years ago
|
uint32_t *saveNumber,
|
||
|
5 years ago
|
_difficulty *difficulty)
|
||
|
|
{
|
||
|
|
selhero_isMultiPlayer = false;
|
||
|
5 years ago
|
UiSelHeroDialog(fninfo, fncreate, fnstats, fnremove, dlgresult, saveNumber);
|
||
|
5 years ago
|
*difficulty = nDifficulty;
|
||
|
|
}
|
||
|
|
|
||
|
|
void UiSelHeroMultDialog(
|
||
|
|
bool (*fninfo)(bool (*fninfofunc)(_uiheroinfo *)),
|
||
|
|
bool (*fncreate)(_uiheroinfo *),
|
||
|
|
bool (*fnremove)(_uiheroinfo *),
|
||
|
|
void (*fnstats)(unsigned int, _uidefaultstats *),
|
||
|
|
_selhero_selections *dlgresult,
|
||
|
5 years ago
|
uint32_t *saveNumber)
|
||
|
5 years ago
|
{
|
||
|
|
selhero_isMultiPlayer = true;
|
||
|
5 years ago
|
UiSelHeroDialog(fninfo, fncreate, fnstats, fnremove, dlgresult, saveNumber);
|
||
|
5 years ago
|
}
|
||
|
|
|
||
|
5 years ago
|
} // namespace devilution
|