diff --git a/CMakeLists.txt b/CMakeLists.txt index 994206a5b..8129e74d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,6 +206,7 @@ add_executable(devilutionx SourceX/DiabloUI/selconn.cpp SourceX/DiabloUI/selgame.cpp SourceX/DiabloUI/selhero.cpp + SourceX/DiabloUI/selyesno.cpp SourceX/DiabloUI/title.cpp SourceX/main.cpp) diff --git a/SourceX/DiabloUI/diabloui.cpp b/SourceX/DiabloUI/diabloui.cpp index f392903e2..e31e40807 100644 --- a/SourceX/DiabloUI/diabloui.cpp +++ b/SourceX/DiabloUI/diabloui.cpp @@ -23,6 +23,7 @@ void (*gfnSoundFunction)(char *file); void (*gfnListFocus)(int value); void (*gfnListSelect)(int value); void (*gfnListEsc)(); +void (*gfnListConfirm)(int value); UI_Item *gUiItems; int gUiItemCnt; bool UiItemsWraps; @@ -138,7 +139,7 @@ void UiDestroy() font = NULL; } -void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UI_Item *items, int itemCnt, bool itemsWraps) +void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UI_Item *items, int itemCnt, bool itemsWraps, void (*fnConfirm)(int value)) { SelectedItem = min; SelectedItemMin = min; @@ -146,6 +147,7 @@ void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(i gfnListFocus = fnFocus; gfnListSelect = fnSelect; gfnListEsc = fnEsc; + gfnListConfirm = fnConfirm; gUiItems = items; gUiItemCnt = itemCnt; UiItemsWraps = itemsWraps; @@ -307,6 +309,12 @@ void UiFocusNavigationEsc() gfnListEsc(); } +void UiFocusNavigationConfirm() +{ + if (gfnListConfirm) + gfnListConfirm(SelectedItem); +} + bool IsInsideRect(const SDL_Event *event, const SDL_Rect *rect) { const SDL_Point point = { event->button.x, event->button.y }; diff --git a/SourceX/DiabloUI/diabloui.h b/SourceX/DiabloUI/diabloui.h index e30e8f643..fff93c697 100644 --- a/SourceX/DiabloUI/diabloui.h +++ b/SourceX/DiabloUI/diabloui.h @@ -110,7 +110,8 @@ void LoadMaskedArtFont(char *pszFile, Art *art, int frames, int mask = 250); void SetMenu(int MenuId); void UiFocusNavigationSelect(); void UiFocusNavigationEsc(); -void UiInitList(int min, int max, void(*fnFocus)(int value), void(*fnSelect)(int value), void(*fnEsc)(), UI_Item *items, int size, bool wraps = false); +void UiFocusNavigationConfirm(); +void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UI_Item *items, int size, bool wraps = false, void (*fcConfirm)(int value) = NULL); void UiRender(); void UiRenderItems(UI_Item *items, int size); void WordWrap(UI_Item *item); diff --git a/SourceX/DiabloUI/selhero.cpp b/SourceX/DiabloUI/selhero.cpp index c09d53dbb..f5d543e2c 100644 --- a/SourceX/DiabloUI/selhero.cpp +++ b/SourceX/DiabloUI/selhero.cpp @@ -1,13 +1,14 @@ #include "selhero.h" +#include "selyesno.h" -#include "devilution.h" #include "DiabloUI/diabloui.h" +#include "devilution.h" namespace dvl { int selhero_SaveCount = 0; -_uiheroinfo heros[MAX_CHARACTERS]; -_uiheroinfo heroInfo; +_uiheroinfo selhero_heros[MAX_CHARACTERS]; +_uiheroinfo selhero_heroInfo; char listItems[6][16]; char textStats[5][4]; char title[32]; @@ -16,11 +17,14 @@ char selhero_Description[256]; int selhero_result; bool selhero_endMenu; bool isMultiPlayer; +bool navigateConfirm; BOOL(*gfnHeroStats) (unsigned int, _uidefaultstats *); BOOL(*gfnHeroCreate) (_uiheroinfo *); +BOOL(*gfnHeroDelete) +(_uiheroinfo *); UI_Item SELHERO_DIALOG[] = { { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, @@ -47,7 +51,7 @@ UI_Item SELLIST_DIALOG[] = { { { 265, 360, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 4, listItems[4] }, { { 265, 386, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 5, listItems[5] }, { { 239, 429, 120, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, - { { 364, 429, 120, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_DISABLED, 0, "Delete" }, + { { 364, 429, 120, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_DISABLED, 0, "Delete", (void *)UiFocusNavigationConfirm }, { { 489, 429, 120, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, }; @@ -62,7 +66,7 @@ UI_Item SELCLASS_DIALOG[] = { UI_Item ENTERNAME_DIALOG[] = { { { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Enter Name" }, - { { 265, 317, 320, 33 }, UI_EDIT, UIS_LIST | UIS_MED | UIS_GOLD, 15, heroInfo.name }, + { { 265, 317, 320, 33 }, UI_EDIT, UIS_LIST | UIS_MED | UIS_GOLD, 15, selhero_heroInfo.name }, { { 279, 429, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, { { 429, 429, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, }; @@ -84,20 +88,20 @@ void selhero_Free() void selhero_SetStats() { - SELHERO_DIALOG[2].value = heroInfo.heroclass; - sprintf(textStats[0], "%d", heroInfo.level); - sprintf(textStats[1], "%d", heroInfo.strength); - sprintf(textStats[2], "%d", heroInfo.magic); - sprintf(textStats[3], "%d", heroInfo.dexterity); - sprintf(textStats[4], "%d", heroInfo.vitality); + SELHERO_DIALOG[2].value = 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); } void selhero_List_Init() { - UiInitList(0, selhero_SaveCount, selhero_List_Focus, selhero_List_Select, selhero_List_Esc, SELLIST_DIALOG, size(SELLIST_DIALOG)); + UiInitList(0, selhero_SaveCount, selhero_List_Focus, selhero_List_Select, selhero_List_Esc, SELLIST_DIALOG, size(SELLIST_DIALOG), false, selhero_List_DeleteConfirm); int i; for (i = 0; i < selhero_SaveCount && i < 6; i++) { - sprintf(listItems[i], heros[i].name); + sprintf(listItems[i], selhero_heros[i].name); } if (i < 6) sprintf(listItems[i], "New Hero"); @@ -110,9 +114,11 @@ void selhero_List_Init() void selhero_List_Focus(int value) { + int baseFlags = UIS_CENTER | UIS_BIG; if (selhero_SaveCount && value < selhero_SaveCount) { - memcpy(&heroInfo, &heros[value], sizeof(heroInfo)); + memcpy(&selhero_heroInfo, &selhero_heros[value], sizeof(selhero_heroInfo)); selhero_SetStats(); + SELLIST_DIALOG[8].flags = baseFlags | UIS_GOLD; return; } @@ -122,19 +128,25 @@ void selhero_List_Focus(int value) sprintf(textStats[2], "--"); sprintf(textStats[3], "--"); sprintf(textStats[4], "--"); + SELLIST_DIALOG[8].flags = baseFlags | UIS_DISABLED; +} + +void selhero_List_DeleteConfirm(int value) +{ + navigateConfirm = true; } void selhero_List_Select(int value) { if (value == selhero_SaveCount) { UiInitList(0, 2, selhero_ClassSelector_Focus, selhero_ClassSelector_Select, selhero_ClassSelector_Esc, SELCLASS_DIALOG, size(SELCLASS_DIALOG)); - memset(&heroInfo.name, 0, sizeof(heroInfo.name)); + memset(&selhero_heroInfo.name, 0, sizeof(selhero_heroInfo.name)); sprintf(title, "New Single Player Hero"); if (isMultiPlayer) { sprintf(title, "New Multi Player Hero"); } return; - } else if (heroInfo.hassaved) { + } else if (selhero_heroInfo.hassaved) { UiInitList(0, 1, selhero_Load_Focus, selhero_Load_Select, selhero_List_Init, SELLOAD_DIALOG, size(SELLOAD_DIALOG), true); sprintf(title, "Single Player Characters"); return; @@ -156,12 +168,12 @@ void selhero_ClassSelector_Focus(int value) _uidefaultstats defaults; gfnHeroStats(value, &defaults); - heroInfo.level = 1; - heroInfo.heroclass = value; - heroInfo.strength = defaults.strength; - heroInfo.magic = defaults.magic; - heroInfo.dexterity = defaults.dexterity; - heroInfo.vitality = defaults.vitality; + 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(); } @@ -172,7 +184,7 @@ void selhero_ClassSelector_Select(int value) if (isMultiPlayer) { sprintf(title, "New Multi Player Hero"); } - memset(heroInfo.name, '\0', sizeof(heroInfo.name)); + memset(selhero_heroInfo.name, '\0', sizeof(selhero_heroInfo.name)); UiInitList(0, 0, NULL, selhero_Name_Select, selhero_Name_Esc, ENTERNAME_DIALOG, size(ENTERNAME_DIALOG)); } @@ -189,7 +201,7 @@ void selhero_ClassSelector_Esc() void selhero_Name_Select(int value) { UiInitList(0, 0, NULL, NULL, NULL, NULL, 0); - gfnHeroCreate(&heroInfo); + gfnHeroCreate(&selhero_heroInfo); selhero_endMenu = true; } @@ -216,71 +228,81 @@ void selhero_Load_Select(int value) BOOL SelHero_GetHeroInfo(_uiheroinfo *pInfo) { - heros[selhero_SaveCount] = *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 (*fninfo)(BOOL (*fninfofunc)(_uiheroinfo *)), + BOOL (*fncreate)(_uiheroinfo *), + BOOL (*fnstats)(unsigned int, _uidefaultstats *), + BOOL (*fnremove)(_uiheroinfo *), int *dlgresult, char *name) { - selhero_result = *dlgresult; - gfnHeroStats = fnstats; - gfnHeroCreate = fncreate; - LoadBackgroundArt("ui_art\\selhero.pcx"); - - selhero_SaveCount = 0; - fninfo(SelHero_GetHeroInfo); - - if (selhero_SaveCount) { - selhero_List_Init(); - } else { - selhero_List_Select(selhero_SaveCount); - } + do { + selhero_result = *dlgresult; + gfnHeroStats = fnstats; + gfnHeroCreate = fncreate; + gfnHeroDelete = fnremove; + LoadBackgroundArt("ui_art\\selhero.pcx"); + + navigateConfirm = false; + + selhero_SaveCount = 0; + fninfo(SelHero_GetHeroInfo); + + if (selhero_SaveCount) { + selhero_List_Init(); + } else { + selhero_List_Select(selhero_SaveCount); + } - selhero_endMenu = false; - while (!selhero_endMenu) { - UiRenderItems(SELHERO_DIALOG, size(SELHERO_DIALOG)); - UiRender(); - } - BlackPalette(); - selhero_Free(); + selhero_endMenu = false; + while (!selhero_endMenu && !navigateConfirm) { + UiRenderItems(SELHERO_DIALOG, size(SELHERO_DIALOG)); + UiRender(); + } + BlackPalette(); + selhero_Free(); - strcpy(name, heroInfo.name); + if (navigateConfirm) { + if (!UiSelHeroDelYesNoDialog(gfnHeroDelete, &selhero_heroInfo, isMultiPlayer)) + app_fatal("Unable to load Yes/No dialog"); + } + } while (navigateConfirm); *dlgresult = selhero_result; + strcpy(name, selhero_heroInfo.name); + return true; } BOOL UiSelHeroSingDialog( - BOOL(*fninfo)(BOOL(*fninfofunc)(_uiheroinfo *)), - BOOL(*fncreate)(_uiheroinfo *), - BOOL(*fnremove)(_uiheroinfo *), - BOOL(*fnstats)(unsigned int, _uidefaultstats *), + BOOL (*fninfo)(BOOL (*fninfofunc)(_uiheroinfo *)), + BOOL (*fncreate)(_uiheroinfo *), + BOOL (*fnremove)(_uiheroinfo *), + BOOL (*fnstats)(unsigned int, _uidefaultstats *), int *dlgresult, char *name, int *difficulty) { isMultiPlayer = false; - return UiSelHeroDialog(fninfo, fncreate, fnstats, dlgresult, name); + 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 *), + BOOL (*fninfo)(BOOL (*fninfofunc)(_uiheroinfo *)), + BOOL (*fncreate)(_uiheroinfo *), + BOOL (*fnremove)(_uiheroinfo *), + BOOL (*fnstats)(unsigned int, _uidefaultstats *), int *dlgresult, BOOL *hero_is_created, char *name) { isMultiPlayer = true; - return UiSelHeroDialog(fninfo, fncreate, fnstats, dlgresult, name); + return UiSelHeroDialog(fninfo, fncreate, fnstats, fnremove, dlgresult, name); } - } diff --git a/SourceX/DiabloUI/selhero.h b/SourceX/DiabloUI/selhero.h index 54be00cb5..77e0eb269 100644 --- a/SourceX/DiabloUI/selhero.h +++ b/SourceX/DiabloUI/selhero.h @@ -5,6 +5,7 @@ namespace dvl { void selhero_List_Init(); void selhero_List_Focus(int value); void selhero_List_Select(int value); +void selhero_List_DeleteConfirm(int value); void selhero_List_Esc(); void selhero_ClassSelector_Focus(int value); void selhero_ClassSelector_Select(int value); diff --git a/SourceX/DiabloUI/selyesno.cpp b/SourceX/DiabloUI/selyesno.cpp new file mode 100644 index 000000000..ab1c2df79 --- /dev/null +++ b/SourceX/DiabloUI/selyesno.cpp @@ -0,0 +1,74 @@ +#include "selyesno.h" + +#include "diablo.h" +#include "diabloui.h" + +namespace dvl { + +_uiheroinfo selyesno_heroInfo; +int selyesno_endMenu; +BOOL(*selyesno_gfnRemove) +(_uiheroinfo *); +char confirmationMessage[256]; +char selyesno_title[32]; + +UI_Item DEL_SELYESNO_DIALOG[] = { + { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, + { { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, selyesno_title }, + { { 100, 230, 280, 270 }, UI_TEXT, UIS_BIG, 0, confirmationMessage }, + { { 230, 390, 180, 35 }, UI_LIST, UIS_CENTER | UIS_BIG | UIS_GOLD, 1, "Yes" }, + { { 230, 426, 180, 35 }, UI_LIST, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "No" }, +}; + +void selyesno_Free() +{ + mem_free_dbg(ArtBackground.data); + ArtBackground.data = NULL; +} + +void selyesno_Select(int value) +{ + if (value == 1) + selyesno_gfnRemove(&selyesno_heroInfo); + + selyesno_endMenu = true; +} + +void selyesno_Esc() +{ + selyesno_endMenu = true; +} + +BOOL UiSelHeroDelYesNoDialog( + BOOL (*fnremove)(_uiheroinfo *), + _uiheroinfo *selectHero, + bool isMultiplayer) +{ + selyesno_heroInfo = *selectHero; + selyesno_gfnRemove = fnremove; + LoadBackgroundArt("ui_art\\black.pcx"); + + if (isMultiplayer) { + sprintf(selyesno_title, "Delete Multiplayer Hero"); + } else { + sprintf(selyesno_title, "Delete Single Player Hero"); + } + + sprintf(confirmationMessage, "Are you sure you want to delete the character \"%s\"?", selyesno_heroInfo.name); + WordWrap(&DEL_SELYESNO_DIALOG[2]); + + UiInitList(0, 1, NULL, selyesno_Select, selyesno_Esc, DEL_SELYESNO_DIALOG, size(DEL_SELYESNO_DIALOG), false, NULL); + + selyesno_endMenu = false; + while (!selyesno_endMenu) { + UiRenderItems(DEL_SELYESNO_DIALOG, size(DEL_SELYESNO_DIALOG)); + UiRender(); + } + + BlackPalette(); + + selyesno_Free(); + return true; +} + +} diff --git a/SourceX/DiabloUI/selyesno.h b/SourceX/DiabloUI/selyesno.h new file mode 100644 index 000000000..d40a763a4 --- /dev/null +++ b/SourceX/DiabloUI/selyesno.h @@ -0,0 +1,12 @@ +#pragma once + +#include "diablo.h" +#include "diabloui.h" + +namespace dvl { +BOOL UiSelHeroDelYesNoDialog(BOOL (*fnremove)(_uiheroinfo *), _uiheroinfo *selectHero, bool isMultiplayer); +void selyesno_Free(); +void selyesno_Select(int value); +void selyesno_Esc(); + +} diff --git a/SourceX/miniwin/misc.cpp b/SourceX/miniwin/misc.cpp index e5ea29fc5..2523ae44c 100644 --- a/SourceX/miniwin/misc.cpp +++ b/SourceX/miniwin/misc.cpp @@ -227,7 +227,14 @@ UINT GetDriveTypeA(LPCSTR lpRootPathName) WINBOOL DeleteFileA(LPCSTR lpFileName) { - UNIMPLEMENTED(); + FILE *f = fopen(lpFileName, "r+"); + + if (f) { + fclose(f); + remove(lpFileName); + f = NULL; + } + return true; } @@ -798,5 +805,4 @@ void __debugbreak() { DUMMY(); } - }