#include "selhero.h" #include #include "scrollbar.h" #include "selyesno.h" #include "DiabloUI/diabloui.h" #include "DiabloUI/dialogs.h" #include "devilution.h" namespace dvl { std::size_t selhero_SaveCount = 0; _uiheroinfo selhero_heros[MAX_CHARACTERS]; _uiheroinfo selhero_heroInfo; std::size_t listOffset = 0; constexpr std::size_t kMaxViewportItems = 6; char textStats[5][4]; char title[32]; char selhero_Lable[32]; char selhero_Description[256]; int selhero_result; bool selhero_endMenu; bool selhero_isMultiPlayer; bool selhero_navigateYesNo; bool selhero_deleteEnabled; BOOL(*gfnHeroStats) (unsigned int, _uidefaultstats *); BOOL(*gfnHeroCreate) (_uiheroinfo *); BOOL(*gfnHeroDelete) (_uiheroinfo *); namespace { UiItem SELHERO_DIALOG[] = { MAINMENU_BACKGROUND, MAINMENU_LOGO, UiArtText(title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), UiImage(&ArtHero, UI_NUM_CLASSES, { 30, 211, 180, 76 }), UiArtText("Level:", { 39, 323, 110, 21 }, UIS_RIGHT), UiArtText(textStats[0], { 159, 323, 40, 21 }, UIS_CENTER), UiArtText("Strength:", { 39, 358, 110, 21 }, UIS_RIGHT), UiArtText(textStats[1], { 159, 358, 40, 21 }, UIS_CENTER), UiArtText("Magic:", { 39, 380, 110, 21 }, UIS_RIGHT), UiArtText(textStats[2], { 159, 380, 40, 21 }, UIS_CENTER), UiArtText("Dexterity:", { 39, 401, 110, 21 }, UIS_RIGHT), UiArtText(textStats[3], { 159, 401, 40, 21 }, UIS_CENTER), UiArtText("Vitality:", { 39, 422, 110, 21 }, UIS_RIGHT), UiArtText(textStats[4], { 159, 422, 40, 21 }, UIS_CENTER), }; UiImage *SELHERO_DIALOG_HERO_IMG = &SELHERO_DIALOG[3].image; UiListItem SELLIST_DIALOG_ITEMS[kMaxViewportItems]; UiItem SELLIST_DIALOG[] = { UiArtText("Select Hero", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), UiList(SELLIST_DIALOG_ITEMS, 265, 256, 320, 26, UIS_CENTER | UIS_MED | UIS_GOLD), MakeScrollBar({585, 244, 25, 178}), UiArtTextButton("OK", &UiFocusNavigationSelect, { 239, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD), UiArtTextButton("Delete", &selhero_UiFocusNavigationYesNo, { 364, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_DISABLED), UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 489, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD) }; UiList *SELLIST_DIALOG_LIST = &SELLIST_DIALOG[1].list; UiScrollBar *SELLIST_SCROLLBAR = &SELLIST_DIALOG[2].scrollbar; UiArtTextButton *SELLIST_DIALOG_DELETE_BUTTON = &SELLIST_DIALOG[4].art_text_button; UiListItem SELCLAS_DIALOG_ITEMS[] = { { "Warrior", UI_WARRIOR }, { "Rogue", UI_ROGUE }, { "Sorcerer", UI_SORCERER } }; UiItem SELCLASS_DIALOG[] = { UiArtText("Choose Class", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), UiList(SELCLAS_DIALOG_ITEMS, 264, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD), UiArtTextButton("OK", &UiFocusNavigationSelect, { 279, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD), UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD) }; UiItem ENTERNAME_DIALOG[] = { UiArtText("Enter Name", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), UiEdit(selhero_heroInfo.name, 15, { 265, 317, 320, 33 }, UIS_MED | UIS_GOLD), UiArtTextButton("OK", &UiFocusNavigationSelect, { 279, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD), UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD) }; UiListItem SELLOAD_DIALOG_ITEMS[] = { { "Load Game", 0 }, { "New Game", 1 } }; UiItem SELLOAD_DIALOG[] = { UiArtText("Save File Exists", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), UiList(SELLOAD_DIALOG_ITEMS, 265, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD), UiArtTextButton("OK", &UiFocusNavigationSelect, { 279, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD), UiArtTextButton("Cancel", &UiFocusNavigationEsc, { 429, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD) }; } // namespace void selhero_UiFocusNavigationYesNo() { if (selhero_deleteEnabled) UiFocusNavigationYesNo(); } void selhero_Free() { ArtBackground.Unload(); } void selhero_SetStats() { SELHERO_DIALOG_HERO_IMG->frame = selhero_heroInfo.heroclass; sprintf(textStats[0], "%d", selhero_heroInfo.level); sprintf(textStats[1], "%d", selhero_heroInfo.strength); sprintf(textStats[2], "%d", selhero_heroInfo.magic); sprintf(textStats[3], "%d", selhero_heroInfo.dexterity); sprintf(textStats[4], "%d", selhero_heroInfo.vitality); } namespace { void selhero_UpdateViewportItems() { const std::size_t num_viewport_heroes = std::min(selhero_SaveCount - listOffset, kMaxViewportItems); SELLIST_DIALOG_LIST->length = num_viewport_heroes; for (std::size_t i = 0; i < num_viewport_heroes; i++) { const std::size_t index = i + listOffset; SELLIST_DIALOG_ITEMS[i] = { selhero_heros[index].name, static_cast(index) }; } if (num_viewport_heroes < kMaxViewportItems) { SELLIST_DIALOG_ITEMS[num_viewport_heroes] = { "New Hero", static_cast(selhero_SaveCount) }; ++SELLIST_DIALOG_LIST->length; } } void selhero_ScrollIntoView(std::size_t index) { std::size_t new_offset = listOffset; if (index >= listOffset + kMaxViewportItems) new_offset = index - (kMaxViewportItems - 1); if (index < listOffset) new_offset = index; if (new_offset != listOffset) { listOffset = new_offset; selhero_UpdateViewportItems(); } } } // namespace void selhero_List_Init() { listOffset = 0; selhero_UpdateViewportItems(); UiInitList(0, selhero_SaveCount, selhero_List_Focus, selhero_List_Select, selhero_List_Esc, SELLIST_DIALOG, size(SELLIST_DIALOG), false, selhero_List_DeleteYesNo); UiInitScrollBar(SELLIST_SCROLLBAR, kMaxViewportItems, &listOffset); if (selhero_isMultiPlayer) { strcpy(title, "Multi Player Characters"); } else { strcpy(title, "Single Player Characters"); } } void selhero_List_Focus(int value) { const std::size_t index = static_cast(value); selhero_ScrollIntoView(index); int baseFlags = UIS_CENTER | UIS_BIG; if (selhero_SaveCount && index < selhero_SaveCount) { memcpy(&selhero_heroInfo, &selhero_heros[index], sizeof(selhero_heroInfo)); selhero_SetStats(); SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_GOLD; selhero_deleteEnabled = true; return; } SELHERO_DIALOG_HERO_IMG->frame = UI_NUM_CLASSES; strcpy(textStats[0], "--"); strcpy(textStats[1], "--"); strcpy(textStats[2], "--"); strcpy(textStats[3], "--"); strcpy(textStats[4], "--"); SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_DISABLED; selhero_deleteEnabled = false; } bool selhero_List_DeleteYesNo() { selhero_navigateYesNo = selhero_deleteEnabled; return selhero_navigateYesNo; } void selhero_List_Select(int value) { if (static_cast(value) == selhero_SaveCount) { UiInitList(0, 2, selhero_ClassSelector_Focus, selhero_ClassSelector_Select, selhero_ClassSelector_Esc, SELCLASS_DIALOG, size(SELCLASS_DIALOG)); memset(&selhero_heroInfo.name, 0, sizeof(selhero_heroInfo.name)); strcpy(title, "New Single Player Hero"); if (selhero_isMultiPlayer) { strcpy(title, "New Multi Player Hero"); } return; } else if (selhero_heroInfo.hassaved) { UiInitList(0, 1, selhero_Load_Focus, selhero_Load_Select, selhero_List_Init, SELLOAD_DIALOG, size(SELLOAD_DIALOG), true); strcpy(title, "Single Player Characters"); return; } UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); selhero_endMenu = true; } void selhero_List_Esc() { UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); selhero_endMenu = true; selhero_result = SELHERO_PREVIOUS; } void selhero_ClassSelector_Focus(int value) { _uidefaultstats defaults; gfnHeroStats(value, &defaults); selhero_heroInfo.level = 1; selhero_heroInfo.heroclass = value; selhero_heroInfo.strength = defaults.strength; selhero_heroInfo.magic = defaults.magic; selhero_heroInfo.dexterity = defaults.dexterity; selhero_heroInfo.vitality = defaults.vitality; selhero_SetStats(); } void selhero_ClassSelector_Select(int value) { strcpy(title, "New Single Player Hero"); if (selhero_isMultiPlayer) { strcpy(title, "New Multi Player Hero"); } memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name)); UiInitList(0, 0, NULL, selhero_Name_Select, selhero_Name_Esc, ENTERNAME_DIALOG, size(ENTERNAME_DIALOG)); } void selhero_ClassSelector_Esc() { if (selhero_SaveCount) { selhero_List_Init(); return; } selhero_List_Esc(); } void selhero_Name_Select(int value) { if (gfnHeroCreate(&selhero_heroInfo)) { UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); selhero_endMenu = true; } else { UiErrorOkDialog("Unable to create character.", SELHERO_DIALOG, size(SELHERO_DIALOG)); memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name)); selhero_ClassSelector_Select(selhero_heroInfo.heroclass); } } void selhero_Name_Esc() { selhero_List_Select(selhero_SaveCount); } void selhero_Load_Focus(int value) { } void selhero_Load_Select(int value) { UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); selhero_endMenu = true; if (value == 0) { selhero_result = SELHERO_CONTINUE; return; } selhero_result = 0; } BOOL SelHero_GetHeroInfo(_uiheroinfo *pInfo) { selhero_heros[selhero_SaveCount] = *pInfo; selhero_SaveCount++; return true; } BOOL UiSelHeroDialog( BOOL (*fninfo)(BOOL (*fninfofunc)(_uiheroinfo *)), BOOL (*fncreate)(_uiheroinfo *), BOOL (*fnstats)(unsigned int, _uidefaultstats *), BOOL (*fnremove)(_uiheroinfo *), int *dlgresult, char *name) { do { LoadBackgroundArt("ui_art\\selhero.pcx"); LoadScrollBar(); selhero_result = *dlgresult; gfnHeroStats = fnstats; gfnHeroCreate = fncreate; gfnHeroDelete = fnremove; selhero_navigateYesNo = false; selhero_SaveCount = 0; fninfo(SelHero_GetHeroInfo); std::reverse(selhero_heros, selhero_heros + selhero_SaveCount); if (selhero_SaveCount) { selhero_List_Init(); } else { selhero_List_Select(selhero_SaveCount); } selhero_endMenu = false; while (!selhero_endMenu && !selhero_navigateYesNo) { UiRenderItems(SELHERO_DIALOG, size(SELHERO_DIALOG)); UiPollAndRender(); } BlackPalette(); selhero_Free(); if (selhero_navigateYesNo) { if (!UiSelHeroDelYesNoDialog(gfnHeroDelete, &selhero_heroInfo, selhero_isMultiPlayer)) app_fatal("Unable to load Yes/No dialog"); } } while (selhero_navigateYesNo); *dlgresult = selhero_result; strcpy(name, selhero_heroInfo.name); UnloadScrollBar(); return true; } BOOL UiSelHeroSingDialog( BOOL (*fninfo)(BOOL (*fninfofunc)(_uiheroinfo *)), BOOL (*fncreate)(_uiheroinfo *), BOOL (*fnremove)(_uiheroinfo *), BOOL (*fnstats)(unsigned int, _uidefaultstats *), int *dlgresult, char *name, int *difficulty) { selhero_isMultiPlayer = false; return UiSelHeroDialog(fninfo, fncreate, fnstats, fnremove, dlgresult, name); } BOOL UiSelHeroMultDialog( BOOL (*fninfo)(BOOL (*fninfofunc)(_uiheroinfo *)), BOOL (*fncreate)(_uiheroinfo *), BOOL (*fnremove)(_uiheroinfo *), BOOL (*fnstats)(unsigned int, _uidefaultstats *), int *dlgresult, BOOL *hero_is_created, char *name) { selhero_isMultiPlayer = true; return UiSelHeroDialog(fninfo, fncreate, fnstats, fnremove, dlgresult, name); } }