From 37936c6d15377aba83ea4ebdbb82ca4ce990d3db Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 29 Sep 2019 13:20:04 +0100 Subject: [PATCH] DiabloUI: Refactor UI_Item to tagged unions Makes the code easier to understand. Also makes building lists easier. Also fixes focus indicator in SELLOAD_DIALOG (this type of bug is impossible with the new structs). --- .editorconfig | 3 + CMakeLists.txt | 1 + SourceX/.clang-format | 1 + SourceX/DiabloUI/art.cpp | 43 +++++ SourceX/DiabloUI/art.h | 32 ++++ SourceX/DiabloUI/diabloui.cpp | 315 +++++++++++++++++----------------- SourceX/DiabloUI/diabloui.h | 72 +------- SourceX/DiabloUI/dialogs.cpp | 56 +++--- SourceX/DiabloUI/mainmenu.cpp | 21 ++- SourceX/DiabloUI/selconn.cpp | 43 +++-- SourceX/DiabloUI/selgame.cpp | 109 ++++++------ SourceX/DiabloUI/selhero.cpp | 107 ++++++------ SourceX/DiabloUI/selyesno.cpp | 19 +- SourceX/DiabloUI/title.cpp | 6 +- SourceX/DiabloUI/ui_item.h | 202 ++++++++++++++++++++++ 15 files changed, 642 insertions(+), 388 deletions(-) create mode 100644 SourceX/DiabloUI/art.cpp create mode 100644 SourceX/DiabloUI/art.h create mode 100644 SourceX/DiabloUI/ui_item.h diff --git a/.editorconfig b/.editorconfig index aea18afba..469d5eefb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,3 +13,6 @@ end_of_line = lf [*.sh] end_of_line = lf + +[.clang-format] +end_of_line = lf diff --git a/CMakeLists.txt b/CMakeLists.txt index b400e17b3..8105e4002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,7 @@ add_executable(devilutionx MACOSX_BUNDLE SourceX/dvlnet/tcp_client.cpp SourceX/dvlnet/tcp_server.cpp SourceX/dvlnet/udp_p2p.cpp + SourceX/DiabloUI/art.cpp SourceX/DiabloUI/credits.cpp SourceX/DiabloUI/diabloui.cpp SourceX/DiabloUI/dialogs.cpp diff --git a/SourceX/.clang-format b/SourceX/.clang-format index 7074a4775..e199beec1 100644 --- a/SourceX/.clang-format +++ b/SourceX/.clang-format @@ -7,4 +7,5 @@ TabWidth: 4, UseTab: ForIndentation, SortIncludes: false, + NamespaceIndentation: None, } diff --git a/SourceX/DiabloUI/art.cpp b/SourceX/DiabloUI/art.cpp new file mode 100644 index 000000000..af043f1b0 --- /dev/null +++ b/SourceX/DiabloUI/art.cpp @@ -0,0 +1,43 @@ +#include "DiabloUI/art.h" + +namespace dvl { + +void LoadArt(char *pszFile, Art *art, int frames, PALETTEENTRY *pPalette) +{ + if (art == NULL || art->surface != NULL) + return; + + DWORD width, height, bpp; + if (!SBmpLoadImage(pszFile, 0, 0, 0, &width, &height, &bpp)) + return; + + Uint32 format; + switch (bpp) { + case 8: + format = SDL_PIXELFORMAT_INDEX8; + break; + case 24: + format = SDL_PIXELFORMAT_RGB888; + break; + case 32: + format = SDL_PIXELFORMAT_RGBA8888; + break; + default: + format = 0; + break; + } + SDL_Surface *art_surface = SDL_CreateRGBSurfaceWithFormat(SDL_SWSURFACE, width, height, bpp, format); + + if (!SBmpLoadImage(pszFile, pPalette, static_cast(art_surface->pixels), + art_surface->pitch * art_surface->format->BytesPerPixel * height, 0, 0, 0)) { + SDL_Log("Failed to load image"); + SDL_FreeSurface(art_surface); + return; + } + + art->surface = art_surface; + art->frames = frames; + art->frame_height = height / frames; +} + +} // namespace dvl diff --git a/SourceX/DiabloUI/art.h b/SourceX/DiabloUI/art.h new file mode 100644 index 000000000..b7a090dce --- /dev/null +++ b/SourceX/DiabloUI/art.h @@ -0,0 +1,32 @@ +#pragma once + +#include "devilution.h" + +namespace dvl { + +struct Art { + SDL_Surface *surface = NULL; + int frames = 1; + int frame_height = 0; + unsigned int palette_version = 0; + + int w() const + { + return surface->w; + } + + int h() const + { + return frame_height; + } + + void Unload() + { + SDL_FreeSurface(surface); + surface = NULL; + } +}; + +void LoadArt(char *pszFile, Art *art, int frames = 1, PALETTEENTRY *pPalette = NULL); + +} diff --git a/SourceX/DiabloUI/diabloui.cpp b/SourceX/DiabloUI/diabloui.cpp index ae6f8bb43..5910e772d 100644 --- a/SourceX/DiabloUI/diabloui.cpp +++ b/SourceX/DiabloUI/diabloui.cpp @@ -9,7 +9,7 @@ namespace dvl { -TTF_Font *font; +TTF_Font *font = nullptr; int SelectedItemMin = 1; int SelectedItemMax = 1; BYTE *FontTables[4]; @@ -25,7 +25,7 @@ void (*gfnListFocus)(int value); void (*gfnListSelect)(int value); void (*gfnListEsc)(); bool (*gfnListYesNo)(); -UI_Item *gUiItems; +UiItem *gUiItems; int gUiItemCnt; bool UiItemsWraps; char *UiTextInput; @@ -140,7 +140,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, bool (*fnYesNo)()) +void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UiItem *items, int itemCnt, bool itemsWraps, bool (*fnYesNo)()) { SelectedItem = min; SelectedItemMin = min; @@ -159,8 +159,8 @@ void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(i for (int i = 0; i < itemCnt; i++) { if (items[i].type == UI_EDIT) { SDL_StartTextInput(); - UiTextInput = items[i].caption; - UiTextInputLen = items[i].value; + UiTextInput = items[i].edit.value; + UiTextInputLen = items[i].edit.max_length; } } } @@ -367,48 +367,10 @@ void UiFocusNavigationYesNo() UiPlaySelectSound(); } -bool IsInsideRect(const SDL_Event *event, const SDL_Rect *rect) +bool IsInsideRect(const SDL_Event &event, const SDL_Rect &rect) { - const SDL_Point point = { event->button.x, event->button.y }; - return SDL_PointInRect(&point, rect); -} - -void LoadArt(char *pszFile, Art *art, int frames, PALETTEENTRY *pPalette) -{ - if (art == NULL || art->surface != NULL) - return; - - DWORD width, height, bpp; - if (!SBmpLoadImage(pszFile, 0, 0, 0, &width, &height, &bpp)) - return; - - Uint32 format; - switch (bpp) { - case 8: - format = SDL_PIXELFORMAT_INDEX8; - break; - case 24: - format = SDL_PIXELFORMAT_RGB888; - break; - case 32: - format = SDL_PIXELFORMAT_RGBA8888; - break; - default: - format = 0; - break; - } - SDL_Surface *art_surface = SDL_CreateRGBSurfaceWithFormat(SDL_SWSURFACE, width, height, bpp, format); - - if (!SBmpLoadImage(pszFile, pPalette, static_cast(art_surface->pixels), - art_surface->pitch * art_surface->format->BytesPerPixel * height, 0, 0, 0)) { - SDL_Log("Failed to load image"); - SDL_FreeSurface(art_surface); - return; - } - - art->surface = art_surface; - art->frames = frames; - art->frame_height = height / frames; + const SDL_Point point = { event.button.x, event.button.y }; + return SDL_PointInRect(&point, &rect); } void LoadMaskedArtFont(char *pszFile, Art *art, int frames, int mask) @@ -604,10 +566,19 @@ void DrawArt(int screenX, int screenY, Art *art, int nFrame, DWORD drawW) if (screenY >= SCREEN_Y + SCREEN_HEIGHT || screenX >= SCREEN_X + SCREEN_WIDTH) return; - SDL_Rect src_rect = { 0, nFrame * art->h(), art->w(), art->h() }; + SDL_Rect src_rect = { + 0, + static_cast(nFrame * art->h()), + static_cast(art->w()), + static_cast(art->h()) + }; if (drawW && static_cast(drawW) < src_rect.w) src_rect.w = drawW; - SDL_Rect dst_rect = { screenX + SCREEN_X, screenY + SCREEN_Y, src_rect.w, src_rect.h }; + SDL_Rect dst_rect = { + static_cast(screenX + SCREEN_X), + static_cast(screenY + SCREEN_Y), + src_rect.w, src_rect.h + }; if (art->surface->format->BitsPerPixel == 8 && art->palette_version != pal_surface_palette_version) { #ifdef USE_SDL1 @@ -634,11 +605,11 @@ int GetCenterOffset(int w, int bw) return (bw - w) / 2; } -int GetStrWidth(char *str, int size) +int GetStrWidth(const char *str, int size) { int strWidth = 0; - for (size_t i = 0; i < strlen((char *)str); i++) { + for (size_t i = 0, n = strlen(str); i < n; i++) { BYTE w = FontTables[size][*(BYTE *)&str[i] + 2]; if (w) strWidth += w; @@ -649,43 +620,43 @@ int GetStrWidth(char *str, int size) return strWidth; } -int TextAlignment(UI_Item *item, TXT_JUST align, _artFontTables size) +int TextAlignment(const char *text, int rect_w, TXT_JUST align, _artFontTables size) { if (align != JustLeft) { - int w = GetStrWidth(item->caption, size); + int w = GetStrWidth(text, size); if (align == JustCentre) { - return GetCenterOffset(w, item->rect.w); + return GetCenterOffset(w, rect_w); } else if (align == JustRight) { - return item->rect.w - w; + return rect_w - w; } } return 0; } -void WordWrap(UI_Item *item) +void WordWrap(UiText *item) { int lineStart = 0; - int len = strlen((char *)item->caption); + int len = strlen((char *)item->text); for (int i = 0; i <= len; i++) { - if (item->caption[i] == '\n') { + if (item->text[i] == '\n') { lineStart = i + 1; continue; - } else if (item->caption[i] != ' ' && i != len) { + } else if (item->text[i] != ' ' && i != len) { continue; } if (i != len) - item->caption[i] = '\0'; - if (GetStrWidth(&item->caption[lineStart], AFT_SMALL) <= item->rect.w) { + item->text[i] = '\0'; + if (GetStrWidth(&item->text[lineStart], AFT_SMALL) <= item->rect.w) { if (i != len) - item->caption[i] = ' '; + item->text[i] = ' '; continue; } int j; for (j = i; j >= lineStart; j--) { - if (item->caption[j] == ' ') { + if (item->text[j] == ' ') { break; // Scan for previous space } } @@ -697,49 +668,48 @@ void WordWrap(UI_Item *item) } if (i != len) - item->caption[i] = ' '; - item->caption[j] = '\n'; + item->text[i] = ' '; + item->text[j] = '\n'; lineStart = j + 1; } }; -void DrawArtStr(UI_Item *item) +void DrawArtStr(const char *text, const SDL_Rect &rect, int flags, bool drawTextCursor = false) { _artFontTables size = AFT_SMALL; - _artFontColors color = item->flags & UIS_GOLD ? AFC_GOLD : AFC_SILVER; + _artFontColors color = flags & UIS_GOLD ? AFC_GOLD : AFC_SILVER; TXT_JUST align = JustLeft; - if (item->flags & UIS_MED) + if (flags & UIS_MED) size = AFT_MED; - else if (item->flags & UIS_BIG) + else if (flags & UIS_BIG) size = AFT_BIG; - else if (item->flags & UIS_HUGE) + else if (flags & UIS_HUGE) size = AFT_HUGE; - if (item->flags & UIS_CENTER) + if (flags & UIS_CENTER) align = JustCentre; - else if (item->flags & UIS_RIGHT) + else if (flags & UIS_RIGHT) align = JustRight; - int x = item->rect.x + TextAlignment(item, align, size); + int x = rect.x + TextAlignment(text, rect.w, align, size); int sx = x; - int sy = item->rect.y; - if (item->flags & UIS_VCENTER) - sy += (item->rect.h - ArtFonts[size][color].h()) / 2; + int sy = rect.y; + if (flags & UIS_VCENTER) + sy += (rect.h - ArtFonts[size][color].h()) / 2; - for (size_t i = 0; i < strlen((char *)item->caption); i++) { - if (item->caption[i] == '\n') { + for (size_t i = 0, n = strlen(text); i < n; i++) { + if (text[i] == '\n') { sx = x; sy += ArtFonts[size][color].h(); continue; } - BYTE w = FontTables[size][*(BYTE *)&item->caption[i] + 2] ? FontTables[size][*(BYTE *)&item->caption[i] + 2] : FontTables[size][0]; - DrawArt(sx, sy, &ArtFonts[size][color], *(BYTE *)&item->caption[i], w); + BYTE w = FontTables[size][*(BYTE *)&text[i] + 2] ? FontTables[size][*(BYTE *)&text[i] + 2] : FontTables[size][0]; + DrawArt(sx, sy, &ArtFonts[size][color], *(BYTE *)&text[i], w); sx += w; } - - if (item->type == UI_EDIT && GetAnimationFrame(2, 500)) { + if (drawTextCursor && GetAnimationFrame(2, 500)) { DrawArt(sx, sy, &ArtFonts[size][color], '|'); } } @@ -783,38 +753,20 @@ void UiFadeIn(int steps) SetFadeLevel(fadeValue); } -void UiRenderItemDebug(UI_Item item) -{ - item.rect.x += 64; // pal_surface is shifted? - item.rect.y += SCREEN_Y; - if (SDL_FillRect(pal_surface, &item.rect, random(0, 255)) <= -1) { - SDL_Log(SDL_GetError()); - } -} - -void DrawSelector(UI_Item *item = 0) +void DrawSelector(const SDL_Rect &rect) { int size = FOCUS_SMALL; - if (item->rect.h >= 42) + if (rect.h >= 42) size = FOCUS_BIG; - else if (item->rect.h >= 30) + else if (rect.h >= 30) size = FOCUS_MED; Art *art = &ArtFocus[size]; int frame = GetAnimationFrame(art->frames); - int y = item->rect.y + (item->rect.h - art->h()) / 2; // TODO FOCUS_MED appares higher then the box + int y = rect.y + (rect.h - art->h()) / 2; // TODO FOCUS_MED appares higher then the box - DrawArt(item->rect.x, y, art, frame); - DrawArt(item->rect.x + item->rect.w - art->w(), y, art, frame); -} - -void DrawEditBox(UI_Item item) -{ - DrawSelector(&item); - item.rect.x += 43; - item.rect.y += 1; - item.rect.w -= 86; - DrawArtStr(&item); + DrawArt(rect.x, y, art, frame); + DrawArt(rect.x + rect.w - art->w(), y, art, frame); } void UiRender() @@ -829,73 +781,120 @@ void UiRender() UiFadeIn(); } -void UiRenderItems(UI_Item *items, int size) +namespace { + +void Render(const UiText &ui_text) { - for (int i = 0; i < size; i++) { - if (items[i].flags & UIS_HIDDEN) - continue; + DrawArtStr(ui_text.text, ui_text.rect, ui_text.flags); +} - switch (items[i].type) { - case UI_EDIT: - DrawEditBox(items[i]); - break; - case UI_LIST: - if (items[i].caption == NULL) - continue; - if (SelectedItem == items[i].value) - DrawSelector(&items[i]); - DrawArtStr(&items[i]); - break; - case UI_BUTTON: - case UI_TEXT: - DrawArtStr(&items[i]); - break; - case UI_IMAGE: - DrawArt(items[i].rect.x, items[i].rect.y, (Art *)items[i].context, items[i].value, items[i].rect.w); - break; - default: - UiRenderItemDebug(items[i]); - break; - } - } +void Render(const UiImage &ui_image) +{ + DrawArt(ui_image.rect.x, ui_image.rect.y, ui_image.art, ui_image.frame, ui_image.rect.w); } -bool UiItemMouseEvents(SDL_Event *event, UI_Item *items, int size) +void Render(const UiButton &ui_button) { - if (event->type != SDL_MOUSEBUTTONDOWN || event->button.button != SDL_BUTTON_LEFT) { - return false; + DrawArtStr(ui_button.text, ui_button.rect, ui_button.flags); +} + +void Render(const UiList &ui_list) +{ + for (std::size_t i = 0; i < ui_list.length; ++i) { + SDL_Rect rect = ui_list.itemRect(i); + const auto &item = ui_list.items[i]; + if (item.value == SelectedItem) + DrawSelector(rect); + DrawArtStr(item.text, rect, ui_list.flags); } +} - for (int i = 0; i < size; i++) { - if (!IsInsideRect(event, &items[i].rect)) { - continue; - } +void Render(const UiEdit &ui_edit) +{ + DrawSelector(ui_edit.rect); + SDL_Rect rect = ui_edit.rect; + rect.x += 43; + rect.y += 1; + rect.w -= 86; + DrawArtStr(ui_edit.value, rect, ui_edit.flags, /*drawTextCursor=*/true); +} - if (items[i].type != UI_BUTTON && items[i].type != UI_LIST) { - continue; - } +void RenderItem(const UiItem &item) +{ + if (item.flags() & UIS_HIDDEN) + return; + switch (item.type) { + case UI_TEXT: + Render(item.text); + break; + case UI_IMAGE: + Render(item.image); + break; + case UI_BUTTON: + Render(item.button); + break; + case UI_LIST: + Render(item.list); + break; + case UI_EDIT: + Render(item.edit); + break; + } +} - if (items[i].type == UI_LIST) { - if (items[i].caption != NULL && *items[i].caption != '\0') { - if (gfnListFocus != NULL && SelectedItem != items[i].value) { - UiFocus(items[i].value); +bool HandleMouseEventButton(const SDL_Event &event, const UiButton &ui_button) +{ + ui_button.action(); + return true; +} + +bool HandleMouseEventList(const SDL_Event &event, const UiList &ui_list) +{ + const UiListItem *list_item = ui_list.itemAt(event.button.y); + if (gfnListFocus != NULL && SelectedItem != list_item->value) { + UiFocus(list_item->value); #ifdef USE_SDL1 - } else if (gfnListFocus == NULL) { + } else if (gfnListFocus == NULL) { #else - } else if (gfnListFocus == NULL || event->button.clicks >= 2) { + } else if (gfnListFocus == NULL || event.button.clicks >= 2) { #endif - SelectedItem = items[i].value; - UiFocusNavigationSelect(); - } - } + SelectedItem = list_item->value; + UiFocusNavigationSelect(); + } + return true; +} - return true; - } +bool HandleMouseEvent(const SDL_Event &event, const UiItem &item) +{ + if (!IsInsideRect(event, item.rect())) + return false; + switch (item.type) { + case UI_BUTTON: + return HandleMouseEventButton(event, item.button); + case UI_LIST: + return HandleMouseEventList(event, item.list); + default: + return false; + } +} - if (items[i].context) { - ((void (*)(int value))items[i].context)(items[i].value); - } - return true; +} // namespace + +void UiRenderItems(UiItem *items, int size) +{ + for (int i = 0; i < size; i++) + RenderItem(items[i]); +} + +bool UiItemMouseEvents(SDL_Event *event, UiItem *items, int size) +{ + if (event->type != SDL_MOUSEBUTTONDOWN || event->button.button != SDL_BUTTON_LEFT) { + return false; + } + + for (int i = 0; i < size; i++) { + if (HandleMouseEvent(*event, items[i])) + return true; } return false; diff --git a/SourceX/DiabloUI/diabloui.h b/SourceX/DiabloUI/diabloui.h index 9600f7886..976f3b522 100644 --- a/SourceX/DiabloUI/diabloui.h +++ b/SourceX/DiabloUI/diabloui.h @@ -1,8 +1,12 @@ #pragma once +#include #include #include +#include "art.h" +#include "ui_item.h" + namespace dvl { typedef enum _artFocus { @@ -29,63 +33,6 @@ typedef enum _artFontColors { AFC_GOLD, } _artFontColors; -typedef enum UiTypes { - UI_TEXT, - UI_IMAGE, - UI_BUTTON, - UI_LIST, - UI_EDIT, -} UiTypes; - -typedef enum UiFlags { - UIS_SMALL = 1 << 0, - UIS_MED = 1 << 1, - UIS_BIG = 1 << 2, - UIS_HUGE = 1 << 3, - UIS_CENTER = 1 << 4, - UIS_RIGHT = 1 << 5, - UIS_VCENTER = 1 << 6, - UIS_SILVER = 1 << 7, - UIS_GOLD = 1 << 8, - UIS_SML1 = 1 << 9, - UIS_SML2 = 1 << 10, - UIS_LIST = 1 << 11, - UIS_DISABLED = 1 << 12, - UIS_HIDDEN = 1 << 13, -} UiFlags; - -struct Art { - SDL_Surface *surface = NULL; - int frames = 1; - int frame_height = 0; - unsigned int palette_version = 0; - - int w() const - { - return surface->w; - } - - int h() const - { - return frame_height; - } - - void Unload() - { - SDL_FreeSurface(surface); - surface = NULL; - } -}; - -typedef struct UI_Item { - SDL_Rect rect; - UiTypes type; - int flags; - int value; - char *caption; - const void *context; -} UI_Item; - extern TTF_Font *font; extern BYTE *FontTables[4]; @@ -110,26 +57,25 @@ constexpr size_t size(T (&)[N]) extern void (*gfnSoundFunction)(char *file); -bool IsInsideRect(const SDL_Event *event, const SDL_Rect *rect); +bool IsInsideRect(const SDL_Event &event, const SDL_Rect &rect); void UiFadeIn(int steps = 16); bool UiFocusNavigation(SDL_Event *event); -bool UiItemMouseEvents(SDL_Event *event, UI_Item *items, int size); +bool UiItemMouseEvents(SDL_Event *event, UiItem *items, int size); int GetAnimationFrame(int frames, int fps = 60); int GetCenterOffset(int w, int bw = 0); void DrawArt(int screenX, int screenY, Art *art, int nFrame = 0, DWORD drawW = 0); void DrawLogo(int t = 0, int size = LOGO_MED); void DrawMouse(); -void LoadArt(char *pszFile, Art *art, int frames = 1, PALETTEENTRY *pPalette = NULL); void LoadBackgroundArt(char *pszFile); void LoadMaskedArtFont(char *pszFile, Art *art, int frames, int mask = 250); void SetMenu(int MenuId); void UiFocusNavigationSelect(); void UiFocusNavigationEsc(); void UiFocusNavigationYesNo(); -void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UI_Item *items, int size, bool wraps = false, bool (*fnYesNo)() = NULL); +void UiInitList(int min, int max, void (*fnFocus)(int value), void (*fnSelect)(int value), void (*fnEsc)(), UiItem *items, int size, bool wraps = false, bool (*fnYesNo)() = NULL); void UiRender(); -void UiRenderItems(UI_Item *items, int size); -void WordWrap(UI_Item *item); +void UiRenderItems(UiItem *items, int size); +void WordWrap(UiText *item); void DvlIntSetting(const char *valuename, int *value); void DvlStringSetting(const char *valuename, char *string, int len); diff --git a/SourceX/DiabloUI/dialogs.cpp b/SourceX/DiabloUI/dialogs.cpp index a4b79d313..5eb754c44 100644 --- a/SourceX/DiabloUI/dialogs.cpp +++ b/SourceX/DiabloUI/dialogs.cpp @@ -3,34 +3,50 @@ namespace dvl { -UI_Item OKCANCEL_DIALOG[] = { - { { 180, 168, 280, 144 }, UI_IMAGE, 0, 0, "ui_art\\spopup.pcx" }, - { { 200, 180, 240, 80 }, UI_TEXT, UIS_CENTER }, // message - { { 200, 265, 110, 28 }, UI_BUTTON, UIS_SML1, 0, "OK" }, - { { 330, 265, 110, 28 }, UI_BUTTON, UIS_SML2, 0, "Cancel" }, +Art dialogArt; // "ui_art\\spopup.pcx" +char dialogMessage[256]; + +Art progressArt; // "ui_art\\prog_bg.pcx" + +void DialogActionOK() +{ +} + +void DialogActionCancel() +{ +} + +UiItem OKCANCEL_DIALOG[] = { + UiImage(&dialogArt, { 180, 168, 280, 144 }), + UiText(dialogMessage, { 200, 180, 240, 80 }, UIS_CENTER), + UiButton("OK", &DialogActionOK, { 200, 265, 110, 28 }, UIS_SML1), + UiButton("Cancel", &DialogActionCancel, { 330, 265, 110, 28 }, UIS_SML2), }; -UI_Item OK_DIALOG[] = { - { { 180, 168, 280, 144 }, UI_IMAGE, 0, 0, "ui_art\\spopup.pcx" }, - { { 200, 180, 240, 80 }, UI_TEXT, UIS_CENTER }, // message - { { 265, 265, 110, 28 }, UI_BUTTON, UIS_SML1, 0, "OK" }, +UiItem OK_DIALOG[] = { + UiImage(&dialogArt, { 180, 168, 280, 144 }), + UiText(dialogMessage, { 200, 180, 240, 80 }, UIS_CENTER), + UiButton("OK", &DialogActionOK, { 200, 265, 110, 28 }, UIS_SML1), }; -UI_Item PROGRESS_DIALOG[] = { - { { 180, 168, 280, 144 }, UI_IMAGE, 0, 0, "ui_art\\spopup.pcx" }, - { { 180, 177, 280, 43 }, UI_TEXT, UIS_CENTER }, // Message - { { 205, 220, 228, 38 }, UI_IMAGE, 0, 0, "ui_art\\prog_bg.pcx" }, - { { 265, 267, 110, 28 }, UI_BUTTON, UIS_SML1, 0, "Cancel" }, +UiItem PROGRESS_DIALOG[] = { + UiImage(&dialogArt, { 180, 168, 280, 144 }), + UiText(dialogMessage, { 180, 177, 280, 43 }, UIS_CENTER), + UiImage(&progressArt, { 205, 220, 228, 38 }), + UiButton("Cancel", &DialogActionCancel, { 265, 267, 110, 28 }, UIS_SML1), }; -UI_Item SELOK_DIALOG[] = { - { { 140, 210, 400, 168 } }, // Message - { { 230, 390, 180, 35 }, UI_LIST, UIS_CENTER, 0, "OK" }, +UiListItem SELOK_DIALOG_ITEMS[] = { + { "OK", 0 } +}; +UiItem SELOK_DIALOG[] = { + UiText(dialogMessage, { 140, 210, 400, 168 }, UIS_CENTER), + UiList(SELOK_DIALOG_ITEMS, 230, 390, 180, 35, UIS_CENTER), }; -UI_Item SPAWNERR_DIALOG[] = { - { { 140, 199, 400, 177 }, UI_TEXT, 0, 0, "The Rogue and Sorcerer are only available in the full retail version of Diablo. For ordering information call (800) 953-SNOW." }, - { { 230, 407, 180, 43 }, UI_BUTTON, 0, 0, "OK" }, +UiItem SPAWNERR_DIALOG[] = { + UiText("The Rogue and Sorcerer are only available in the full retail version of Diablo. For ordering information call (800) 953-SNOW.", { 140, 199, 400, 177 }), + UiButton("OK", &DialogActionOK, { 230, 407, 180, 43 }), }; } diff --git a/SourceX/DiabloUI/mainmenu.cpp b/SourceX/DiabloUI/mainmenu.cpp index 4311a8107..16d8228d7 100644 --- a/SourceX/DiabloUI/mainmenu.cpp +++ b/SourceX/DiabloUI/mainmenu.cpp @@ -7,14 +7,17 @@ int mainmenu_attract_time_out; //seconds DWORD dwAttractTicks; int MainMenuResult; -UI_Item MAINMENU_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 64, 192, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_SINGLE_PLAYER, "Single Player" }, - { { 64, 235, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_MULTIPLAYER, "Multi Player" }, - { { 64, 277, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_REPLAY_INTRO, "Replay Intro" }, - { { 64, 320, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_SHOW_CREDITS, "Show Credits" }, - { { 64, 363, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_EXIT_DIABLO, "Exit Diablo" }, - { { 17, 444, 605, 21 }, UI_TEXT, UIS_SMALL }, +UiListItem MAINMENU_DIALOG_ITEMS[] = { + {"Single Player", MAINMENU_SINGLE_PLAYER}, + {"Multi Player", MAINMENU_MULTIPLAYER}, + {"Replay Intro", MAINMENU_REPLAY_INTRO}, + {"Show Credits", MAINMENU_SHOW_CREDITS}, + {"Exit Diablo", MAINMENU_EXIT_DIABLO} +}; +UiItem MAINMENU_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiList(MAINMENU_DIALOG_ITEMS, 64, 192, 510, 43, UIS_HUGE | UIS_GOLD | UIS_CENTER), + UiText(nullptr, { 17, 444, 605, 21 }, UIS_SMALL) }; void UiMainMenuSelect(int value) @@ -35,7 +38,7 @@ void mainmenu_restart_repintro() void mainmenu_Load(char *name, void (*fnSound)(char *file)) { gfnSoundFunction = fnSound; - MAINMENU_DIALOG[6].caption = name; + MAINMENU_DIALOG[size(MAINMENU_DIALOG) - 1].text.text = name; MainMenuResult = 0; diff --git a/SourceX/DiabloUI/selconn.cpp b/SourceX/DiabloUI/selconn.cpp index 42d97f0bc..167385705 100644 --- a/SourceX/DiabloUI/selconn.cpp +++ b/SourceX/DiabloUI/selconn.cpp @@ -17,24 +17,25 @@ _SNETVERSIONDATA *selconn_FileInfo; DWORD provider; -UI_Item SELCONNECT_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Multi Player Game" }, - { { 35, 218, 205, 21 }, UI_TEXT, 0, 0, selconn_MaxPlayers }, // Max players - { { 35, 256, 205, 21 }, UI_TEXT, 0, 0, "Requirements:" }, - { { 35, 275, 205, 66 }, UI_TEXT, 0, 0, selconn_Description }, //Description - { { 30, 356, 220, 31 }, UI_TEXT, UIS_CENTER | UIS_MED, 0, "no gateway needed" }, - { { 35, 393, 205, 21 }, UI_TEXT, UIS_CENTER, 0, selconn_Gateway }, // Gateway - { { 16, 427, 250, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD | UIS_HIDDEN, 0, "Change Gateway" }, - { { 300, 211, 295, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Connection" }, - { { 305, 256, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD, 0, "Client-Server (TCP)" }, - { { 305, 282, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD, 1, "Peer-to-Peer (UDP)" }, - { { 305, 308, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD, 2, "Loopback" }, - { { 305, 334, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD }, - { { 305, 360, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD }, - { { 305, 386, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD }, - { { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, - { { 454, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, +UiText SELCONNECT_DIALOG_DESCRIPTION(selconn_Description, { 35, 275, 205, 66 }); +UiListItem SELCONN_DIALOG_ITEMS[] = { + { "Client-Server (TCP)", 0 }, + { "Peer-to-Peer (UDP)", 1 }, + { "Loopback", 2 }, +}; +UiItem SELCONNECT_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText("Multi Player Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiText(selconn_MaxPlayers, { 35, 218, 205, 21 }), + UiText("Requirements:", { 35, 256, 205, 21 }), + SELCONNECT_DIALOG_DESCRIPTION, + UiText("no gateway needed", { 30, 356, 220, 31 }, UIS_CENTER | UIS_MED), + UiText(selconn_Gateway, { 35, 393, 205, 21 }, UIS_CENTER), + UiText("Select Connection", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG), + UiButton("Change Gateway", nullptr, { 16, 427, 250, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD | UIS_HIDDEN), + UiList(SELCONN_DIALOG_ITEMS, 305, 256, 285, 26, UIS_CENTER | UIS_VCENTER | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD), + UiButton("Cancel", &UiFocusNavigationEsc, { 454, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD) }; void selconn_Load() @@ -73,11 +74,7 @@ void selconn_Focus(int value) } sprintf(selconn_MaxPlayers, "Players Supported: %d", players); - - for (auto &item : SELCONNECT_DIALOG) { - if (item.caption != NULL && !(item.flags & (UIS_VCENTER | UIS_CENTER))) - WordWrap(&item); - } + WordWrap(&SELCONNECT_DIALOG_DESCRIPTION); } void selconn_Select(int value) diff --git a/SourceX/DiabloUI/selgame.cpp b/SourceX/DiabloUI/selgame.cpp index 552c7c68c..b4169ed4b 100644 --- a/SourceX/DiabloUI/selgame.cpp +++ b/SourceX/DiabloUI/selgame.cpp @@ -5,7 +5,7 @@ namespace dvl { -char selgame_Lable[32]; +char selgame_Label[32]; char selgame_Ip[129] = ""; char selgame_Password[16] = ""; char selgame_Description[256]; @@ -18,55 +18,58 @@ int gbDifficulty; static _SNETPROGRAMDATA *m_client_info; extern DWORD provider; -UI_Item SELUDPGAME_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Join TCP/UDP Games" }, - { { 35, 211, 205, 33 }, UI_TEXT, UIS_MED, 0, "Description:" }, - { { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description - { { 300, 211, 295, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Action" }, - { { 305, 255, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 0, "Create Game" }, - { { 305, 281, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 1, "Enter IP" }, - { { 305, 307, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, - { { 305, 333, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, - { { 305, 359, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, - { { 305, 385, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, - { { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, - { { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, +UiText SELGAME_DESCRIPTION(selgame_Description, { 35, 256, 205, 192 }); +UiListItem SELUDPGAME_DIALOG_ITEMS[] = { + { "Create Game", 0 }, + { "Enter IP", 1 }, +}; +UiItem SELUDPGAME_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiText("Description:", { 35, 256, 205, 192 }, UIS_MED), + SELGAME_DESCRIPTION, + UiText("Select Action", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG), + UiList(SELUDPGAME_DIALOG_ITEMS, 305, 255, 285, 26, UIS_CENTER | UIS_MED | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD), + UiButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD) }; -UI_Item SELDIFF_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Create Game" }, - { { 34, 211, 205, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, selgame_Lable }, // DIFF - { { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description - { { 299, 211, 295, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Difficulty" }, - { { 300, 282, 295, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, DIFF_NORMAL, "Normal" }, - { { 300, 308, 295, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, DIFF_NIGHTMARE, "Nightmare" }, - { { 300, 334, 295, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, DIFF_HELL, "Hell" }, - { { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, - { { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, +UiListItem SELDIFF_DIALOG_ITEMS[] = { + { "Normal", DIFF_NORMAL }, + { "Nightmare", DIFF_NIGHTMARE }, + { "Hell", DIFF_HELL } +}; +UiItem SELDIFF_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText("Create Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiText(selgame_Label, { 34, 211, 205, 33 }, UIS_CENTER | UIS_BIG), // DIFF + SELGAME_DESCRIPTION, + UiText("Select Difficulty", { 299, 211, 295, 35 }, UIS_CENTER | UIS_BIG), + UiList(SELDIFF_DIALOG_ITEMS, 300, 282, 295, 26, UIS_CENTER | UIS_MED | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD), + UiButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD) }; -UI_Item ENTERIP_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Join TCP/UDP Games" }, - { { 35, 211, 205, 33 }, UI_TEXT, UIS_MED, 0, "Description:" }, - { { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description - { { 305, 211, 285, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Enter IP" }, - { { 305, 314, 285, 33 }, UI_EDIT, UIS_LIST | UIS_MED | UIS_GOLD, 128, selgame_Ip }, - { { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, - { { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, +UiItem ENTERIP_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiText("Description:", { 35, 211, 205, 33 }, UIS_MED), + SELGAME_DESCRIPTION, + UiText("Enter IP", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG), + UiEdit(selgame_Ip, 128, { 305, 314, 285, 33 }, UIS_LIST | UIS_MED | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD), + UiButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD) }; -UI_Item ENTERPASSWORD_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Join TCP/UDP Games" }, - { { 35, 211, 205, 33 }, UI_TEXT, UIS_MED, 0, "Description:" }, - { { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description - { { 305, 211, 285, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Enter Password" }, - { { 305, 314, 285, 33 }, UI_EDIT, UIS_LIST | UIS_MED | UIS_GOLD, 15, selgame_Password }, - { { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, - { { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, +UiItem ENTERPASSWORD_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiText("Description:", { 35, 211, 205, 33 }, UIS_MED), + SELGAME_DESCRIPTION, + UiText("Enter Password", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG), + UiEdit(selgame_Password, 15, { 305, 314, 285, 33 }, UIS_LIST | UIS_MED | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD), + UiButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD) }; void selgame_Free() @@ -99,11 +102,7 @@ void selgame_GameSelection_Focus(int value) sprintf(selgame_Description, "Enter an IP and join a game already in progress at that address."); break; } - - for (auto &item : SELUDPGAME_DIALOG) { - if (item.caption != NULL && !(item.flags & (UIS_VCENTER | UIS_CENTER))) - WordWrap(&item); - } + WordWrap(&SELGAME_DESCRIPTION); } void selgame_GameSelection_Select(int value) @@ -132,23 +131,19 @@ void selgame_Diff_Focus(int value) { switch (value) { case DIFF_NORMAL: - sprintf(selgame_Lable, "Normal"); + sprintf(selgame_Label, "Normal"); sprintf(selgame_Description, "Normal Difficulty\nThis is where a starting character should begin the quest to defeat Diablo."); break; case DIFF_NIGHTMARE: - sprintf(selgame_Lable, "Nightmare"); + sprintf(selgame_Label, "Nightmare"); sprintf(selgame_Description, "Nightmare Difficulty\nThe denizens of the Labyrinth have been bolstered and will prove to be a greater challenge. This is recommended for experienced characters only."); break; case DIFF_HELL: - sprintf(selgame_Lable, "Hell"); + sprintf(selgame_Label, "Hell"); sprintf(selgame_Description, "Hell Difficulty\nThe most powerful of the underworld's creatures lurk at the gateway into Hell. Only the most experienced characters should venture in this realm."); break; } - - for (auto &item : SELDIFF_DIALOG) { - if (item.caption != NULL && !(item.flags & (UIS_VCENTER | UIS_CENTER))) - WordWrap(&item); - } + WordWrap(&SELGAME_DESCRIPTION); } void selgame_Diff_Select(int value) diff --git a/SourceX/DiabloUI/selhero.cpp b/SourceX/DiabloUI/selhero.cpp index 1f2978d67..2eac1e560 100644 --- a/SourceX/DiabloUI/selhero.cpp +++ b/SourceX/DiabloUI/selhero.cpp @@ -27,57 +27,68 @@ BOOL(*gfnHeroCreate) BOOL(*gfnHeroDelete) (_uiheroinfo *); -UI_Item SELHERO_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, title }, - { { 30, 211, 180, 76 }, UI_IMAGE, 0, UI_NUM_CLASSES, NULL, &ArtHero }, - { { 39, 323, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Level:" }, - { { 159, 323, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[0] }, - { { 39, 358, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Strength:" }, - { { 159, 358, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[1] }, - { { 39, 380, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Magic:" }, - { { 159, 380, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[2] }, - { { 39, 401, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Dexterity:" }, - { { 159, 401, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[3] }, - { { 39, 422, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Vitality:" }, - { { 159, 422, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[4] }, +UiItem SELHERO_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText(title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiImage(&ArtHero, UI_NUM_CLASSES, { 30, 211, 180, 76 }), + UiText("Level:", { 39, 323, 110, 21 }, UIS_RIGHT), + UiText(textStats[0], { 159, 323, 40, 21 }, UIS_CENTER), + UiText("Strength:", { 39, 358, 110, 21 }, UIS_RIGHT), + UiText(textStats[1], { 159, 358, 40, 21 }, UIS_CENTER), + UiText("Magic:", { 39, 380, 110, 21 }, UIS_RIGHT), + UiText(textStats[2], { 159, 380, 40, 21 }, UIS_CENTER), + UiText("Dexterity:", { 39, 401, 110, 21 }, UIS_RIGHT), + UiText(textStats[3], { 159, 401, 40, 21 }, UIS_CENTER), + UiText("Vitality:", { 39, 422, 110, 21 }, UIS_RIGHT), + UiText(textStats[4], { 159, 422, 40, 21 }, UIS_CENTER), }; - -UI_Item SELLIST_DIALOG[] = { - { { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Hero" }, - { { 265, 256, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 0, listItems[0] }, - { { 265, 282, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 1, listItems[1] }, - { { 265, 308, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 2, listItems[2] }, - { { 265, 334, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 3, listItems[3] }, - { { 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", (void *)selhero_UiFocusNavigationYesNo }, - { { 489, 429, 120, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, +UiImage *SELHERO_DIALOG_HERO_IMG = &SELHERO_DIALOG[2].image; + +UiListItem SELLIST_DIALOG_ITEMS[] = { + { listItems[0], 0 }, + { listItems[1], 1 }, + { listItems[2], 2 }, + { listItems[3], 3 }, + { listItems[4], 4 }, + { listItems[5], 5 }, +}; +UiItem SELLIST_DIALOG[] = { + UiText("Select Hero", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), + UiList(SELLIST_DIALOG_ITEMS, 265, 256, 320, 26, UIS_CENTER | UIS_MED | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 239, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD), + UiButton("Delete", &selhero_UiFocusNavigationYesNo, { 364, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_DISABLED), + UiButton("Cancel", &UiFocusNavigationEsc, { 489, 429, 120, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD) }; +UiButton *SELLIST_DIALOG_DELETE_BUTTON = &SELLIST_DIALOG[3].button; -UI_Item SELCLASS_DIALOG[] = { - { { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Choose Class" }, - { { 264, 285, 320, 33 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, UI_WARRIOR, "Warrior" }, - { { 264, 318, 320, 33 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, UI_ROGUE, "Rogue" }, - { { 264, 352, 320, 33 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, UI_SORCERER, "Sorcerer" }, - { { 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 }, +UiListItem SELCLAS_DIALOG_ITEMS[] = { + {"Warrior", UI_WARRIOR}, + {"Rogue", UI_ROGUE}, + {"Sorcerer", UI_SORCERER} +}; +UiItem SELCLASS_DIALOG[] = { + UiText("Choose Class", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), + UiList(SELCLAS_DIALOG_ITEMS, 264, 285, 320, 33, UIS_CENTER | UIS_MED | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 279, 429, 140, 35 },UIS_CENTER | UIS_BIG | UIS_GOLD), + UiButton("Cancel", &UiFocusNavigationEsc, { 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD) }; -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, 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 }, +UiItem ENTERNAME_DIALOG[] = { + UiText("Enter Name", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG), + UiEdit(selhero_heroInfo.name, 15, { 265, 317, 320, 33 }, UIS_LIST | UIS_MED | UIS_GOLD), + UiButton("OK", &UiFocusNavigationSelect, { 279, 429, 140, 35 },UIS_CENTER | UIS_BIG | UIS_GOLD), + UiButton("Cancel", &UiFocusNavigationEsc, { 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD) }; -UI_Item SELLOAD_DIALOG[] = { - { { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Save File Exists" }, - { { 265, 285, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 0, "Load Game" }, - { { 265, 318, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 1, "New Game" }, - { { 279, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, - { { 429, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, +UiListItem SELLOAD_DIALOG_ITEMS[] = { + {"Load Game", 0}, + {"New Game", 1} +}; +UiItem SELLOAD_DIALOG[] = { + UiText("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), + UiButton("OK", &UiFocusNavigationSelect, { 279, 427, 140, 35 },UIS_CENTER | UIS_VCENTER |UIS_BIG | UIS_GOLD), + UiButton("Cancel", &UiFocusNavigationEsc, { 429, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER |UIS_BIG | UIS_GOLD) }; void selhero_UiFocusNavigationYesNo() @@ -94,7 +105,7 @@ void selhero_Free() void selhero_SetStats() { - SELHERO_DIALOG[2].value = selhero_heroInfo.heroclass; + 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); @@ -124,18 +135,18 @@ void selhero_List_Focus(int value) if (selhero_SaveCount && value < selhero_SaveCount) { memcpy(&selhero_heroInfo, &selhero_heros[value], sizeof(selhero_heroInfo)); selhero_SetStats(); - SELLIST_DIALOG[8].flags = baseFlags | UIS_GOLD; + SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_GOLD; selhero_deleteEnabled = true; return; } - SELHERO_DIALOG[2].value = UI_NUM_CLASSES; + SELHERO_DIALOG_HERO_IMG->frame = UI_NUM_CLASSES; sprintf(textStats[0], "--"); sprintf(textStats[1], "--"); sprintf(textStats[2], "--"); sprintf(textStats[3], "--"); sprintf(textStats[4], "--"); - SELLIST_DIALOG[8].flags = baseFlags | UIS_DISABLED; + SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_DISABLED; selhero_deleteEnabled = false; } diff --git a/SourceX/DiabloUI/selyesno.cpp b/SourceX/DiabloUI/selyesno.cpp index ba1f5459d..ac0844d46 100644 --- a/SourceX/DiabloUI/selyesno.cpp +++ b/SourceX/DiabloUI/selyesno.cpp @@ -12,14 +12,19 @@ BOOL(*selyesno_gfnRemove) char selyesno_confirmationMessage[256]; char selyesno_title[32]; -UI_Item 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 }, - { { 120, 236, 280, 168 }, UI_TEXT, UIS_MED, 0, selyesno_confirmationMessage }, - { { 230, 390, 180, 35 }, UI_LIST, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "Yes" }, - { { 230, 426, 180, 35 }, UI_LIST, UIS_CENTER | UIS_BIG | UIS_GOLD, 1, "No" }, +UiListItem SELYESNO_DIALOG_ITEMS[] = { + { "Yes", 0 }, + { "No", 1 } }; +UiItem SELYESNO_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText(selyesno_title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG), + UiText(selyesno_confirmationMessage, { 120, 236, 280, 168 }, UIS_MED), + UiList(SELYESNO_DIALOG_ITEMS, 230, 390, 180, 35, UIS_CENTER | UIS_BIG | UIS_GOLD) +}; +UiText *SELYESNO_DIALOG_CONFIRMATION_MESSAGE = &SELYESNO_DIALOG[2].text; + void selyesno_Free() { ArtBackground.Unload(); @@ -54,7 +59,7 @@ BOOL UiSelHeroDelYesNoDialog( } sprintf(selyesno_confirmationMessage, "Are you sure you want to delete the character \"%s\"?", selyesno_heroInfo.name); - WordWrap(&SELYESNO_DIALOG[2]); + WordWrap(SELYESNO_DIALOG_CONFIRMATION_MESSAGE); UiInitList(0, 1, NULL, selyesno_Select, selyesno_Esc, SELYESNO_DIALOG, size(SELYESNO_DIALOG), false, NULL); diff --git a/SourceX/DiabloUI/title.cpp b/SourceX/DiabloUI/title.cpp index 0c121410b..30be3a1b0 100644 --- a/SourceX/DiabloUI/title.cpp +++ b/SourceX/DiabloUI/title.cpp @@ -17,9 +17,9 @@ void title_Free() BOOL UiTitleDialog(int a1) { - UI_Item TITLESCREEN_DIALOG[] = { - { { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, - { { 49, 410, 550, 26 }, UI_TEXT, UIS_MED | UIS_CENTER, 0, "Copyright \xA9 1996-2001 Blizzard Entertainment" }, + UiItem TITLESCREEN_DIALOG[] = { + UiImage(&ArtBackground, { 0, 0, 640, 480 }), + UiText("Copyright \xA9 1996-2001 Blizzard Entertainment", { 49, 410, 550, 26 }, UIS_MED | UIS_CENTER) }; title_Load(); diff --git a/SourceX/DiabloUI/ui_item.h b/SourceX/DiabloUI/ui_item.h new file mode 100644 index 000000000..0ef54c253 --- /dev/null +++ b/SourceX/DiabloUI/ui_item.h @@ -0,0 +1,202 @@ +#pragma once + +#include +#include +#include + +#include "devilution.h" +#include "art.h" +#include "stubs.h" + +namespace dvl { + +enum UiType { + UI_TEXT, + UI_IMAGE, + UI_BUTTON, + UI_LIST, + UI_EDIT, +}; + +enum UiFlags { + UIS_SMALL = 1 << 0, + UIS_MED = 1 << 1, + UIS_BIG = 1 << 2, + UIS_HUGE = 1 << 3, + UIS_CENTER = 1 << 4, + UIS_RIGHT = 1 << 5, + UIS_VCENTER = 1 << 6, + UIS_SILVER = 1 << 7, + UIS_GOLD = 1 << 8, + UIS_SML1 = 1 << 9, + UIS_SML2 = 1 << 10, + UIS_LIST = 1 << 11, + UIS_DISABLED = 1 << 12, + UIS_HIDDEN = 1 << 13, +}; + +struct UiItemBase { + constexpr UiItemBase(SDL_Rect rect, int flags) + : rect(rect) + , flags(flags) + { + } + SDL_Rect rect; + int flags; +}; + +struct UiImage : public UiItemBase { + constexpr UiImage(Art *art, SDL_Rect rect, int flags = 0) + : UiImage(art, /*frame=*/0, rect, flags) + { + } + + constexpr UiImage(Art *art, int frame, SDL_Rect rect, int flags = 0) + : UiItemBase(rect, flags) + , art(art) + , frame(frame) + { + } + + Art *art; + int frame; +}; + +struct UiText : public UiItemBase { + constexpr UiText(char *text, SDL_Rect rect, int flags = 0) + : UiItemBase(rect, flags) + , text(text) + { + } + + char *text; +}; + +struct UiButton : public UiItemBase { + constexpr UiButton(char *text, void (*action)(), SDL_Rect rect, int flags = 0) + : UiItemBase(rect, flags) + , text(text) + , action(action) + { + } + + char *text; + void (*action)(); +}; + +struct UiListItem { + constexpr UiListItem(char *text = "", int value = 0) + : text(text) + , value(value) + { + } + char *text; + int value; +}; + +struct UiList : public UiItemBase { + template + constexpr UiList( + UiListItem (&items)[N], + decltype(SDL_Rect().x) x, + decltype(SDL_Rect().y) y, + decltype(SDL_Rect().w) item_width, + decltype(SDL_Rect().h) item_height, + int flags) + : UiItemBase({ x, y, item_width, static_cast(item_height * N) }, flags) + , x(x) + , y(y) + , item_width(item_width) + , item_height(item_height) + , items(items) + , length(N) + { + } + + decltype(SDL_Rect().x) x; + decltype(SDL_Rect().y) y; + decltype(SDL_Rect().w) item_width; + decltype(SDL_Rect().h) item_height; + UiListItem *items; + std::size_t length; + + SDL_Rect itemRect(std::size_t i) const + { + return { x, static_cast(y + item_height * i), item_width, item_height }; + } + + UiListItem *itemAt(decltype(SDL_Rect().y) y) const + { + ASSERT(y >= rect.y); + const std::size_t index = (y - rect.y) / item_height; + ASSERT(index < length); + return &items[index]; + } +}; + +struct UiEdit : public UiItemBase { + constexpr UiEdit(char *value, std::size_t max_length, SDL_Rect rect, int flags) + : UiItemBase(rect, flags) + , value(value) + , max_length(max_length) + + { + } + + char *value; + std::size_t max_length; +}; + +struct UiItem { + constexpr UiItem(UiText text) + : type(UI_TEXT) + , text(text) + { + } + + constexpr UiItem(UiImage image) + : type(UI_IMAGE) + , image(image) + { + } + + constexpr UiItem(UiButton button) + : type(UI_BUTTON) + , button(button) + { + } + + constexpr UiItem(UiList list) + : type(UI_LIST) + , list(list) + { + } + + constexpr UiItem(UiEdit edit) + : type(UI_EDIT) + , edit(edit) + { + } + + UiType type; + union { + UiText text; + UiImage image; + UiButton button; + UiList list; + UiEdit edit; + UiItemBase common; + }; + + int flags() const + { + return common.flags; + } + + const SDL_Rect &rect() const + { + return common.rect; + } +}; + +} // namespace dvl