Browse Source

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).
pull/300/head
Gleb Mazovetskiy 7 years ago committed by Anders Jenbo
parent
commit
37936c6d15
  1. 3
      .editorconfig
  2. 1
      CMakeLists.txt
  3. 1
      SourceX/.clang-format
  4. 43
      SourceX/DiabloUI/art.cpp
  5. 32
      SourceX/DiabloUI/art.h
  6. 315
      SourceX/DiabloUI/diabloui.cpp
  7. 72
      SourceX/DiabloUI/diabloui.h
  8. 56
      SourceX/DiabloUI/dialogs.cpp
  9. 21
      SourceX/DiabloUI/mainmenu.cpp
  10. 43
      SourceX/DiabloUI/selconn.cpp
  11. 109
      SourceX/DiabloUI/selgame.cpp
  12. 107
      SourceX/DiabloUI/selhero.cpp
  13. 19
      SourceX/DiabloUI/selyesno.cpp
  14. 6
      SourceX/DiabloUI/title.cpp
  15. 202
      SourceX/DiabloUI/ui_item.h

3
.editorconfig

@ -13,3 +13,6 @@ end_of_line = lf
[*.sh] [*.sh]
end_of_line = lf end_of_line = lf
[.clang-format]
end_of_line = lf

1
CMakeLists.txt

@ -197,6 +197,7 @@ add_executable(devilutionx MACOSX_BUNDLE
SourceX/dvlnet/tcp_client.cpp SourceX/dvlnet/tcp_client.cpp
SourceX/dvlnet/tcp_server.cpp SourceX/dvlnet/tcp_server.cpp
SourceX/dvlnet/udp_p2p.cpp SourceX/dvlnet/udp_p2p.cpp
SourceX/DiabloUI/art.cpp
SourceX/DiabloUI/credits.cpp SourceX/DiabloUI/credits.cpp
SourceX/DiabloUI/diabloui.cpp SourceX/DiabloUI/diabloui.cpp
SourceX/DiabloUI/dialogs.cpp SourceX/DiabloUI/dialogs.cpp

1
SourceX/.clang-format

@ -7,4 +7,5 @@
TabWidth: 4, TabWidth: 4,
UseTab: ForIndentation, UseTab: ForIndentation,
SortIncludes: false, SortIncludes: false,
NamespaceIndentation: None,
} }

43
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<BYTE *>(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

32
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);
}

315
SourceX/DiabloUI/diabloui.cpp

@ -9,7 +9,7 @@
namespace dvl { namespace dvl {
TTF_Font *font; TTF_Font *font = nullptr;
int SelectedItemMin = 1; int SelectedItemMin = 1;
int SelectedItemMax = 1; int SelectedItemMax = 1;
BYTE *FontTables[4]; BYTE *FontTables[4];
@ -25,7 +25,7 @@ void (*gfnListFocus)(int value);
void (*gfnListSelect)(int value); void (*gfnListSelect)(int value);
void (*gfnListEsc)(); void (*gfnListEsc)();
bool (*gfnListYesNo)(); bool (*gfnListYesNo)();
UI_Item *gUiItems; UiItem *gUiItems;
int gUiItemCnt; int gUiItemCnt;
bool UiItemsWraps; bool UiItemsWraps;
char *UiTextInput; char *UiTextInput;
@ -140,7 +140,7 @@ void UiDestroy()
font = NULL; 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; SelectedItem = min;
SelectedItemMin = 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++) { for (int i = 0; i < itemCnt; i++) {
if (items[i].type == UI_EDIT) { if (items[i].type == UI_EDIT) {
SDL_StartTextInput(); SDL_StartTextInput();
UiTextInput = items[i].caption; UiTextInput = items[i].edit.value;
UiTextInputLen = items[i].value; UiTextInputLen = items[i].edit.max_length;
} }
} }
} }
@ -367,48 +367,10 @@ void UiFocusNavigationYesNo()
UiPlaySelectSound(); 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 }; const SDL_Point point = { event.button.x, event.button.y };
return SDL_PointInRect(&point, rect); 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<BYTE *>(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;
} }
void LoadMaskedArtFont(char *pszFile, Art *art, int frames, int mask) 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) if (screenY >= SCREEN_Y + SCREEN_HEIGHT || screenX >= SCREEN_X + SCREEN_WIDTH)
return; return;
SDL_Rect src_rect = { 0, nFrame * art->h(), art->w(), art->h() }; SDL_Rect src_rect = {
0,
static_cast<decltype(SDL_Rect().y)>(nFrame * art->h()),
static_cast<decltype(SDL_Rect().w)>(art->w()),
static_cast<decltype(SDL_Rect().h)>(art->h())
};
if (drawW && static_cast<int>(drawW) < src_rect.w) if (drawW && static_cast<int>(drawW) < src_rect.w)
src_rect.w = drawW; 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<decltype(SDL_Rect().x)>(screenX + SCREEN_X),
static_cast<decltype(SDL_Rect().y)>(screenY + SCREEN_Y),
src_rect.w, src_rect.h
};
if (art->surface->format->BitsPerPixel == 8 && art->palette_version != pal_surface_palette_version) { if (art->surface->format->BitsPerPixel == 8 && art->palette_version != pal_surface_palette_version) {
#ifdef USE_SDL1 #ifdef USE_SDL1
@ -634,11 +605,11 @@ int GetCenterOffset(int w, int bw)
return (bw - w) / 2; return (bw - w) / 2;
} }
int GetStrWidth(char *str, int size) int GetStrWidth(const char *str, int size)
{ {
int strWidth = 0; 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]; BYTE w = FontTables[size][*(BYTE *)&str[i] + 2];
if (w) if (w)
strWidth += w; strWidth += w;
@ -649,43 +620,43 @@ int GetStrWidth(char *str, int size)
return strWidth; 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) { if (align != JustLeft) {
int w = GetStrWidth(item->caption, size); int w = GetStrWidth(text, size);
if (align == JustCentre) { if (align == JustCentre) {
return GetCenterOffset(w, item->rect.w); return GetCenterOffset(w, rect_w);
} else if (align == JustRight) { } else if (align == JustRight) {
return item->rect.w - w; return rect_w - w;
} }
} }
return 0; return 0;
} }
void WordWrap(UI_Item *item) void WordWrap(UiText *item)
{ {
int lineStart = 0; int lineStart = 0;
int len = strlen((char *)item->caption); int len = strlen((char *)item->text);
for (int i = 0; i <= len; i++) { for (int i = 0; i <= len; i++) {
if (item->caption[i] == '\n') { if (item->text[i] == '\n') {
lineStart = i + 1; lineStart = i + 1;
continue; continue;
} else if (item->caption[i] != ' ' && i != len) { } else if (item->text[i] != ' ' && i != len) {
continue; continue;
} }
if (i != len) if (i != len)
item->caption[i] = '\0'; item->text[i] = '\0';
if (GetStrWidth(&item->caption[lineStart], AFT_SMALL) <= item->rect.w) { if (GetStrWidth(&item->text[lineStart], AFT_SMALL) <= item->rect.w) {
if (i != len) if (i != len)
item->caption[i] = ' '; item->text[i] = ' ';
continue; continue;
} }
int j; int j;
for (j = i; j >= lineStart; j--) { for (j = i; j >= lineStart; j--) {
if (item->caption[j] == ' ') { if (item->text[j] == ' ') {
break; // Scan for previous space break; // Scan for previous space
} }
} }
@ -697,49 +668,48 @@ void WordWrap(UI_Item *item)
} }
if (i != len) if (i != len)
item->caption[i] = ' '; item->text[i] = ' ';
item->caption[j] = '\n'; item->text[j] = '\n';
lineStart = j + 1; 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; _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; TXT_JUST align = JustLeft;
if (item->flags & UIS_MED) if (flags & UIS_MED)
size = AFT_MED; size = AFT_MED;
else if (item->flags & UIS_BIG) else if (flags & UIS_BIG)
size = AFT_BIG; size = AFT_BIG;
else if (item->flags & UIS_HUGE) else if (flags & UIS_HUGE)
size = AFT_HUGE; size = AFT_HUGE;
if (item->flags & UIS_CENTER) if (flags & UIS_CENTER)
align = JustCentre; align = JustCentre;
else if (item->flags & UIS_RIGHT) else if (flags & UIS_RIGHT)
align = JustRight; 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 sx = x;
int sy = item->rect.y; int sy = rect.y;
if (item->flags & UIS_VCENTER) if (flags & UIS_VCENTER)
sy += (item->rect.h - ArtFonts[size][color].h()) / 2; sy += (rect.h - ArtFonts[size][color].h()) / 2;
for (size_t i = 0; i < strlen((char *)item->caption); i++) { for (size_t i = 0, n = strlen(text); i < n; i++) {
if (item->caption[i] == '\n') { if (text[i] == '\n') {
sx = x; sx = x;
sy += ArtFonts[size][color].h(); sy += ArtFonts[size][color].h();
continue; continue;
} }
BYTE w = FontTables[size][*(BYTE *)&item->caption[i] + 2] ? FontTables[size][*(BYTE *)&item->caption[i] + 2] : FontTables[size][0]; BYTE w = FontTables[size][*(BYTE *)&text[i] + 2] ? FontTables[size][*(BYTE *)&text[i] + 2] : FontTables[size][0];
DrawArt(sx, sy, &ArtFonts[size][color], *(BYTE *)&item->caption[i], w); DrawArt(sx, sy, &ArtFonts[size][color], *(BYTE *)&text[i], w);
sx += w; sx += w;
} }
if (drawTextCursor && GetAnimationFrame(2, 500)) {
if (item->type == UI_EDIT && GetAnimationFrame(2, 500)) {
DrawArt(sx, sy, &ArtFonts[size][color], '|'); DrawArt(sx, sy, &ArtFonts[size][color], '|');
} }
} }
@ -783,38 +753,20 @@ void UiFadeIn(int steps)
SetFadeLevel(fadeValue); SetFadeLevel(fadeValue);
} }
void UiRenderItemDebug(UI_Item item) void DrawSelector(const SDL_Rect &rect)
{
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)
{ {
int size = FOCUS_SMALL; int size = FOCUS_SMALL;
if (item->rect.h >= 42) if (rect.h >= 42)
size = FOCUS_BIG; size = FOCUS_BIG;
else if (item->rect.h >= 30) else if (rect.h >= 30)
size = FOCUS_MED; size = FOCUS_MED;
Art *art = &ArtFocus[size]; Art *art = &ArtFocus[size];
int frame = GetAnimationFrame(art->frames); 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(rect.x, y, art, frame);
DrawArt(item->rect.x + item->rect.w - art->w(), y, art, frame); DrawArt(rect.x + 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);
} }
void UiRender() void UiRender()
@ -829,73 +781,120 @@ void UiRender()
UiFadeIn(); UiFadeIn();
} }
void UiRenderItems(UI_Item *items, int size) namespace {
void Render(const UiText &ui_text)
{ {
for (int i = 0; i < size; i++) { DrawArtStr(ui_text.text, ui_text.rect, ui_text.flags);
if (items[i].flags & UIS_HIDDEN) }
continue;
switch (items[i].type) { void Render(const UiImage &ui_image)
case UI_EDIT: {
DrawEditBox(items[i]); DrawArt(ui_image.rect.x, ui_image.rect.y, ui_image.art, ui_image.frame, ui_image.rect.w);
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;
}
}
} }
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) { DrawArtStr(ui_button.text, ui_button.rect, ui_button.flags);
return false; }
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++) { void Render(const UiEdit &ui_edit)
if (!IsInsideRect(event, &items[i].rect)) { {
continue; 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) { void RenderItem(const UiItem &item)
continue; {
} 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) { bool HandleMouseEventButton(const SDL_Event &event, const UiButton &ui_button)
if (items[i].caption != NULL && *items[i].caption != '\0') { {
if (gfnListFocus != NULL && SelectedItem != items[i].value) { ui_button.action();
UiFocus(items[i].value); 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 #ifdef USE_SDL1
} else if (gfnListFocus == NULL) { } else if (gfnListFocus == NULL) {
#else #else
} else if (gfnListFocus == NULL || event->button.clicks >= 2) { } else if (gfnListFocus == NULL || event.button.clicks >= 2) {
#endif #endif
SelectedItem = items[i].value; SelectedItem = list_item->value;
UiFocusNavigationSelect(); 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) { } // namespace
((void (*)(int value))items[i].context)(items[i].value);
} void UiRenderItems(UiItem *items, int size)
return true; {
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; return false;

72
SourceX/DiabloUI/diabloui.h

@ -1,8 +1,12 @@
#pragma once #pragma once
#include <cstddef>
#include <SDL.h> #include <SDL.h>
#include <SDL_ttf.h> #include <SDL_ttf.h>
#include "art.h"
#include "ui_item.h"
namespace dvl { namespace dvl {
typedef enum _artFocus { typedef enum _artFocus {
@ -29,63 +33,6 @@ typedef enum _artFontColors {
AFC_GOLD, AFC_GOLD,
} _artFontColors; } _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 TTF_Font *font;
extern BYTE *FontTables[4]; extern BYTE *FontTables[4];
@ -110,26 +57,25 @@ constexpr size_t size(T (&)[N])
extern void (*gfnSoundFunction)(char *file); 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); void UiFadeIn(int steps = 16);
bool UiFocusNavigation(SDL_Event *event); 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 GetAnimationFrame(int frames, int fps = 60);
int GetCenterOffset(int w, int bw = 0); int GetCenterOffset(int w, int bw = 0);
void DrawArt(int screenX, int screenY, Art *art, int nFrame = 0, DWORD drawW = 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 DrawLogo(int t = 0, int size = LOGO_MED);
void DrawMouse(); void DrawMouse();
void LoadArt(char *pszFile, Art *art, int frames = 1, PALETTEENTRY *pPalette = NULL);
void LoadBackgroundArt(char *pszFile); void LoadBackgroundArt(char *pszFile);
void LoadMaskedArtFont(char *pszFile, Art *art, int frames, int mask = 250); void LoadMaskedArtFont(char *pszFile, Art *art, int frames, int mask = 250);
void SetMenu(int MenuId); void SetMenu(int MenuId);
void UiFocusNavigationSelect(); void UiFocusNavigationSelect();
void UiFocusNavigationEsc(); void UiFocusNavigationEsc();
void UiFocusNavigationYesNo(); 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 UiRender();
void UiRenderItems(UI_Item *items, int size); void UiRenderItems(UiItem *items, int size);
void WordWrap(UI_Item *item); void WordWrap(UiText *item);
void DvlIntSetting(const char *valuename, int *value); void DvlIntSetting(const char *valuename, int *value);
void DvlStringSetting(const char *valuename, char *string, int len); void DvlStringSetting(const char *valuename, char *string, int len);

56
SourceX/DiabloUI/dialogs.cpp

@ -3,34 +3,50 @@
namespace dvl { namespace dvl {
UI_Item OKCANCEL_DIALOG[] = { Art dialogArt; // "ui_art\\spopup.pcx"
{ { 180, 168, 280, 144 }, UI_IMAGE, 0, 0, "ui_art\\spopup.pcx" }, char dialogMessage[256];
{ { 200, 180, 240, 80 }, UI_TEXT, UIS_CENTER }, // message
{ { 200, 265, 110, 28 }, UI_BUTTON, UIS_SML1, 0, "OK" }, Art progressArt; // "ui_art\\prog_bg.pcx"
{ { 330, 265, 110, 28 }, UI_BUTTON, UIS_SML2, 0, "Cancel" },
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[] = { UiItem OK_DIALOG[] = {
{ { 180, 168, 280, 144 }, UI_IMAGE, 0, 0, "ui_art\\spopup.pcx" }, UiImage(&dialogArt, { 180, 168, 280, 144 }),
{ { 200, 180, 240, 80 }, UI_TEXT, UIS_CENTER }, // message UiText(dialogMessage, { 200, 180, 240, 80 }, UIS_CENTER),
{ { 265, 265, 110, 28 }, UI_BUTTON, UIS_SML1, 0, "OK" }, UiButton("OK", &DialogActionOK, { 200, 265, 110, 28 }, UIS_SML1),
}; };
UI_Item PROGRESS_DIALOG[] = { UiItem PROGRESS_DIALOG[] = {
{ { 180, 168, 280, 144 }, UI_IMAGE, 0, 0, "ui_art\\spopup.pcx" }, UiImage(&dialogArt, { 180, 168, 280, 144 }),
{ { 180, 177, 280, 43 }, UI_TEXT, UIS_CENTER }, // Message UiText(dialogMessage, { 180, 177, 280, 43 }, UIS_CENTER),
{ { 205, 220, 228, 38 }, UI_IMAGE, 0, 0, "ui_art\\prog_bg.pcx" }, UiImage(&progressArt, { 205, 220, 228, 38 }),
{ { 265, 267, 110, 28 }, UI_BUTTON, UIS_SML1, 0, "Cancel" }, UiButton("Cancel", &DialogActionCancel, { 265, 267, 110, 28 }, UIS_SML1),
}; };
UI_Item SELOK_DIALOG[] = { UiListItem SELOK_DIALOG_ITEMS[] = {
{ { 140, 210, 400, 168 } }, // Message { "OK", 0 }
{ { 230, 390, 180, 35 }, UI_LIST, UIS_CENTER, 0, "OK" }, };
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[] = { UiItem 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." }, 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 }),
{ { 230, 407, 180, 43 }, UI_BUTTON, 0, 0, "OK" }, UiButton("OK", &DialogActionOK, { 230, 407, 180, 43 }),
}; };
} }

21
SourceX/DiabloUI/mainmenu.cpp

@ -7,14 +7,17 @@ int mainmenu_attract_time_out; //seconds
DWORD dwAttractTicks; DWORD dwAttractTicks;
int MainMenuResult; int MainMenuResult;
UI_Item MAINMENU_DIALOG[] = { UiListItem MAINMENU_DIALOG_ITEMS[] = {
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, {"Single Player", MAINMENU_SINGLE_PLAYER},
{ { 64, 192, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_SINGLE_PLAYER, "Single Player" }, {"Multi Player", MAINMENU_MULTIPLAYER},
{ { 64, 235, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_MULTIPLAYER, "Multi Player" }, {"Replay Intro", MAINMENU_REPLAY_INTRO},
{ { 64, 277, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_REPLAY_INTRO, "Replay Intro" }, {"Show Credits", MAINMENU_SHOW_CREDITS},
{ { 64, 320, 510, 43 }, UI_LIST, UIS_HUGE | UIS_GOLD | UIS_CENTER, MAINMENU_SHOW_CREDITS, "Show Credits" }, {"Exit Diablo", MAINMENU_EXIT_DIABLO}
{ { 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 }, 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) void UiMainMenuSelect(int value)
@ -35,7 +38,7 @@ void mainmenu_restart_repintro()
void mainmenu_Load(char *name, void (*fnSound)(char *file)) void mainmenu_Load(char *name, void (*fnSound)(char *file))
{ {
gfnSoundFunction = fnSound; gfnSoundFunction = fnSound;
MAINMENU_DIALOG[6].caption = name; MAINMENU_DIALOG[size(MAINMENU_DIALOG) - 1].text.text = name;
MainMenuResult = 0; MainMenuResult = 0;

43
SourceX/DiabloUI/selconn.cpp

@ -17,24 +17,25 @@ _SNETVERSIONDATA *selconn_FileInfo;
DWORD provider; DWORD provider;
UI_Item SELCONNECT_DIALOG[] = { UiText SELCONNECT_DIALOG_DESCRIPTION(selconn_Description, { 35, 275, 205, 66 });
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, UiListItem SELCONN_DIALOG_ITEMS[] = {
{ { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Multi Player Game" }, { "Client-Server (TCP)", 0 },
{ { 35, 218, 205, 21 }, UI_TEXT, 0, 0, selconn_MaxPlayers }, // Max players { "Peer-to-Peer (UDP)", 1 },
{ { 35, 256, 205, 21 }, UI_TEXT, 0, 0, "Requirements:" }, { "Loopback", 2 },
{ { 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" }, UiItem SELCONNECT_DIALOG[] = {
{ { 35, 393, 205, 21 }, UI_TEXT, UIS_CENTER, 0, selconn_Gateway }, // Gateway UiImage(&ArtBackground, { 0, 0, 640, 480 }),
{ { 16, 427, 250, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD | UIS_HIDDEN, 0, "Change Gateway" }, UiText("Multi Player Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
{ { 300, 211, 295, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Connection" }, UiText(selconn_MaxPlayers, { 35, 218, 205, 21 }),
{ { 305, 256, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD, 0, "Client-Server (TCP)" }, UiText("Requirements:", { 35, 256, 205, 21 }),
{ { 305, 282, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD, 1, "Peer-to-Peer (UDP)" }, SELCONNECT_DIALOG_DESCRIPTION,
{ { 305, 308, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD, 2, "Loopback" }, UiText("no gateway needed", { 30, 356, 220, 31 }, UIS_CENTER | UIS_MED),
{ { 305, 334, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD }, UiText(selconn_Gateway, { 35, 393, 205, 21 }, UIS_CENTER),
{ { 305, 360, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD }, UiText("Select Connection", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
{ { 305, 386, 285, 26 }, UI_LIST, UIS_CENTER | UIS_VCENTER | UIS_GOLD }, UiButton("Change Gateway", nullptr, { 16, 427, 250, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD | UIS_HIDDEN),
{ { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, UiList(SELCONN_DIALOG_ITEMS, 305, 256, 285, 26, UIS_CENTER | UIS_VCENTER | UIS_GOLD),
{ { 454, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, 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() void selconn_Load()
@ -73,11 +74,7 @@ void selconn_Focus(int value)
} }
sprintf(selconn_MaxPlayers, "Players Supported: %d", players); sprintf(selconn_MaxPlayers, "Players Supported: %d", players);
WordWrap(&SELCONNECT_DIALOG_DESCRIPTION);
for (auto &item : SELCONNECT_DIALOG) {
if (item.caption != NULL && !(item.flags & (UIS_VCENTER | UIS_CENTER)))
WordWrap(&item);
}
} }
void selconn_Select(int value) void selconn_Select(int value)

109
SourceX/DiabloUI/selgame.cpp

@ -5,7 +5,7 @@
namespace dvl { namespace dvl {
char selgame_Lable[32]; char selgame_Label[32];
char selgame_Ip[129] = ""; char selgame_Ip[129] = "";
char selgame_Password[16] = ""; char selgame_Password[16] = "";
char selgame_Description[256]; char selgame_Description[256];
@ -18,55 +18,58 @@ int gbDifficulty;
static _SNETPROGRAMDATA *m_client_info; static _SNETPROGRAMDATA *m_client_info;
extern DWORD provider; extern DWORD provider;
UI_Item SELUDPGAME_DIALOG[] = { UiText SELGAME_DESCRIPTION(selgame_Description, { 35, 256, 205, 192 });
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, UiListItem SELUDPGAME_DIALOG_ITEMS[] = {
{ { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Join TCP/UDP Games" }, { "Create Game", 0 },
{ { 35, 211, 205, 33 }, UI_TEXT, UIS_MED, 0, "Description:" }, { "Enter IP", 1 },
{ { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description };
{ { 300, 211, 295, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Action" }, UiItem SELUDPGAME_DIALOG[] = {
{ { 305, 255, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 0, "Create Game" }, UiImage(&ArtBackground, { 0, 0, 640, 480 }),
{ { 305, 281, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 1, "Enter IP" }, UiText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
{ { 305, 307, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, UiText("Description:", { 35, 256, 205, 192 }, UIS_MED),
{ { 305, 333, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, SELGAME_DESCRIPTION,
{ { 305, 359, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, UiText("Select Action", { 300, 211, 295, 33 }, UIS_CENTER | UIS_BIG),
{ { 305, 385, 285, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD }, UiList(SELUDPGAME_DIALOG_ITEMS, 305, 255, 285, 26, UIS_CENTER | UIS_MED | UIS_GOLD),
{ { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD),
{ { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, UiButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD)
}; };
UI_Item SELDIFF_DIALOG[] = { UiListItem SELDIFF_DIALOG_ITEMS[] = {
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, { "Normal", DIFF_NORMAL },
{ { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Create Game" }, { "Nightmare", DIFF_NIGHTMARE },
{ { 34, 211, 205, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, selgame_Lable }, // DIFF { "Hell", DIFF_HELL }
{ { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description };
{ { 299, 211, 295, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Difficulty" }, UiItem SELDIFF_DIALOG[] = {
{ { 300, 282, 295, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, DIFF_NORMAL, "Normal" }, UiImage(&ArtBackground, { 0, 0, 640, 480 }),
{ { 300, 308, 295, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, DIFF_NIGHTMARE, "Nightmare" }, UiText("Create Game", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
{ { 300, 334, 295, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, DIFF_HELL, "Hell" }, UiText(selgame_Label, { 34, 211, 205, 33 }, UIS_CENTER | UIS_BIG), // DIFF
{ { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, SELGAME_DESCRIPTION,
{ { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, 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[] = { UiItem ENTERIP_DIALOG[] = {
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, UiImage(&ArtBackground, { 0, 0, 640, 480 }),
{ { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Join TCP/UDP Games" }, UiText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
{ { 35, 211, 205, 33 }, UI_TEXT, UIS_MED, 0, "Description:" }, UiText("Description:", { 35, 211, 205, 33 }, UIS_MED),
{ { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description SELGAME_DESCRIPTION,
{ { 305, 211, 285, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Enter IP" }, UiText("Enter IP", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
{ { 305, 314, 285, 33 }, UI_EDIT, UIS_LIST | UIS_MED | UIS_GOLD, 128, selgame_Ip }, UiEdit(selgame_Ip, 128, { 305, 314, 285, 33 }, UIS_LIST | UIS_MED | UIS_GOLD),
{ { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD),
{ { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, UiButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD)
}; };
UI_Item ENTERPASSWORD_DIALOG[] = { UiItem ENTERPASSWORD_DIALOG[] = {
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, UiImage(&ArtBackground, { 0, 0, 640, 480 }),
{ { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Join TCP/UDP Games" }, UiText("Join TCP/UDP Games", { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
{ { 35, 211, 205, 33 }, UI_TEXT, UIS_MED, 0, "Description:" }, UiText("Description:", { 35, 211, 205, 33 }, UIS_MED),
{ { 35, 256, 205, 192 }, UI_TEXT, 0, 0, selgame_Description }, // Description SELGAME_DESCRIPTION,
{ { 305, 211, 285, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Enter Password" }, UiText("Enter Password", { 305, 211, 285, 33 }, UIS_CENTER | UIS_BIG),
{ { 305, 314, 285, 33 }, UI_EDIT, UIS_LIST | UIS_MED | UIS_GOLD, 15, selgame_Password }, UiEdit(selgame_Password, 15, { 305, 314, 285, 33 }, UIS_LIST | UIS_MED | UIS_GOLD),
{ { 299, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, UiButton("OK", &UiFocusNavigationSelect, { 299, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD),
{ { 449, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, UiButton("CANCEL", &UiFocusNavigationEsc, { 449, 427, 140, 35 }, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD)
}; };
void selgame_Free() 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."); sprintf(selgame_Description, "Enter an IP and join a game already in progress at that address.");
break; break;
} }
WordWrap(&SELGAME_DESCRIPTION);
for (auto &item : SELUDPGAME_DIALOG) {
if (item.caption != NULL && !(item.flags & (UIS_VCENTER | UIS_CENTER)))
WordWrap(&item);
}
} }
void selgame_GameSelection_Select(int value) void selgame_GameSelection_Select(int value)
@ -132,23 +131,19 @@ void selgame_Diff_Focus(int value)
{ {
switch (value) { switch (value) {
case DIFF_NORMAL: 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."); sprintf(selgame_Description, "Normal Difficulty\nThis is where a starting character should begin the quest to defeat Diablo.");
break; break;
case DIFF_NIGHTMARE: 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."); 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; break;
case DIFF_HELL: 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."); 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; break;
} }
WordWrap(&SELGAME_DESCRIPTION);
for (auto &item : SELDIFF_DIALOG) {
if (item.caption != NULL && !(item.flags & (UIS_VCENTER | UIS_CENTER)))
WordWrap(&item);
}
} }
void selgame_Diff_Select(int value) void selgame_Diff_Select(int value)

107
SourceX/DiabloUI/selhero.cpp

@ -27,57 +27,68 @@ BOOL(*gfnHeroCreate)
BOOL(*gfnHeroDelete) BOOL(*gfnHeroDelete)
(_uiheroinfo *); (_uiheroinfo *);
UI_Item SELHERO_DIALOG[] = { UiItem SELHERO_DIALOG[] = {
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, UiImage(&ArtBackground, { 0, 0, 640, 480 }),
{ { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, title }, UiText(title, { 24, 161, 590, 35 }, UIS_CENTER | UIS_BIG),
{ { 30, 211, 180, 76 }, UI_IMAGE, 0, UI_NUM_CLASSES, NULL, &ArtHero }, UiImage(&ArtHero, UI_NUM_CLASSES, { 30, 211, 180, 76 }),
{ { 39, 323, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Level:" }, UiText("Level:", { 39, 323, 110, 21 }, UIS_RIGHT),
{ { 159, 323, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[0] }, UiText(textStats[0], { 159, 323, 40, 21 }, UIS_CENTER),
{ { 39, 358, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Strength:" }, UiText("Strength:", { 39, 358, 110, 21 }, UIS_RIGHT),
{ { 159, 358, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[1] }, UiText(textStats[1], { 159, 358, 40, 21 }, UIS_CENTER),
{ { 39, 380, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Magic:" }, UiText("Magic:", { 39, 380, 110, 21 }, UIS_RIGHT),
{ { 159, 380, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[2] }, UiText(textStats[2], { 159, 380, 40, 21 }, UIS_CENTER),
{ { 39, 401, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Dexterity:" }, UiText("Dexterity:", { 39, 401, 110, 21 }, UIS_RIGHT),
{ { 159, 401, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[3] }, UiText(textStats[3], { 159, 401, 40, 21 }, UIS_CENTER),
{ { 39, 422, 110, 21 }, UI_TEXT, UIS_RIGHT, 0, "Vitality:" }, UiText("Vitality:", { 39, 422, 110, 21 }, UIS_RIGHT),
{ { 159, 422, 40, 21 }, UI_TEXT, UIS_CENTER, 0, textStats[4] }, UiText(textStats[4], { 159, 422, 40, 21 }, UIS_CENTER),
}; };
UiImage *SELHERO_DIALOG_HERO_IMG = &SELHERO_DIALOG[2].image;
UI_Item SELLIST_DIALOG[] = {
{ { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Select Hero" }, UiListItem SELLIST_DIALOG_ITEMS[] = {
{ { 265, 256, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 0, listItems[0] }, { listItems[0], 0 },
{ { 265, 282, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 1, listItems[1] }, { listItems[1], 1 },
{ { 265, 308, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 2, listItems[2] }, { listItems[2], 2 },
{ { 265, 334, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 3, listItems[3] }, { listItems[3], 3 },
{ { 265, 360, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 4, listItems[4] }, { listItems[4], 4 },
{ { 265, 386, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 5, listItems[5] }, { listItems[5], 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 }, UiItem SELLIST_DIALOG[] = {
{ { 489, 429, 120, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, 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[] = { UiListItem SELCLAS_DIALOG_ITEMS[] = {
{ { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Choose Class" }, {"Warrior", UI_WARRIOR},
{ { 264, 285, 320, 33 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, UI_WARRIOR, "Warrior" }, {"Rogue", UI_ROGUE},
{ { 264, 318, 320, 33 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, UI_ROGUE, "Rogue" }, {"Sorcerer", UI_SORCERER}
{ { 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 }, UiItem SELCLASS_DIALOG[] = {
{ { 429, 429, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, 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[] = { UiItem ENTERNAME_DIALOG[] = {
{ { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Enter Name" }, UiText("Enter Name", { 264, 211, 320, 33 }, UIS_CENTER | UIS_BIG),
{ { 265, 317, 320, 33 }, UI_EDIT, UIS_LIST | UIS_MED | UIS_GOLD, 15, selhero_heroInfo.name }, UiEdit(selhero_heroInfo.name, 15, { 265, 317, 320, 33 }, UIS_LIST | UIS_MED | UIS_GOLD),
{ { 279, 429, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "OK", (void *)UiFocusNavigationSelect }, UiButton("OK", &UiFocusNavigationSelect, { 279, 429, 140, 35 },UIS_CENTER | UIS_BIG | UIS_GOLD),
{ { 429, 429, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, UiButton("Cancel", &UiFocusNavigationEsc, { 429, 429, 140, 35 }, UIS_CENTER | UIS_BIG | UIS_GOLD)
}; };
UI_Item SELLOAD_DIALOG[] = { UiListItem SELLOAD_DIALOG_ITEMS[] = {
{ { 264, 211, 320, 33 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, "Save File Exists" }, {"Load Game", 0},
{ { 265, 285, 320, 26 }, UI_LIST, UIS_CENTER | UIS_MED | UIS_GOLD, 0, "Load Game" }, {"New Game", 1}
{ { 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 }, UiItem SELLOAD_DIALOG[] = {
{ { 429, 427, 140, 35 }, UI_BUTTON, UIS_CENTER | UIS_VCENTER | UIS_BIG | UIS_GOLD, 0, "Cancel", (void *)UiFocusNavigationEsc }, 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() void selhero_UiFocusNavigationYesNo()
@ -94,7 +105,7 @@ void selhero_Free()
void selhero_SetStats() 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[0], "%d", selhero_heroInfo.level);
sprintf(textStats[1], "%d", selhero_heroInfo.strength); sprintf(textStats[1], "%d", selhero_heroInfo.strength);
sprintf(textStats[2], "%d", selhero_heroInfo.magic); sprintf(textStats[2], "%d", selhero_heroInfo.magic);
@ -124,18 +135,18 @@ void selhero_List_Focus(int value)
if (selhero_SaveCount && value < selhero_SaveCount) { if (selhero_SaveCount && value < selhero_SaveCount) {
memcpy(&selhero_heroInfo, &selhero_heros[value], sizeof(selhero_heroInfo)); memcpy(&selhero_heroInfo, &selhero_heros[value], sizeof(selhero_heroInfo));
selhero_SetStats(); selhero_SetStats();
SELLIST_DIALOG[8].flags = baseFlags | UIS_GOLD; SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_GOLD;
selhero_deleteEnabled = true; selhero_deleteEnabled = true;
return; return;
} }
SELHERO_DIALOG[2].value = UI_NUM_CLASSES; SELHERO_DIALOG_HERO_IMG->frame = UI_NUM_CLASSES;
sprintf(textStats[0], "--"); sprintf(textStats[0], "--");
sprintf(textStats[1], "--"); sprintf(textStats[1], "--");
sprintf(textStats[2], "--"); sprintf(textStats[2], "--");
sprintf(textStats[3], "--"); sprintf(textStats[3], "--");
sprintf(textStats[4], "--"); sprintf(textStats[4], "--");
SELLIST_DIALOG[8].flags = baseFlags | UIS_DISABLED; SELLIST_DIALOG_DELETE_BUTTON->flags = baseFlags | UIS_DISABLED;
selhero_deleteEnabled = false; selhero_deleteEnabled = false;
} }

19
SourceX/DiabloUI/selyesno.cpp

@ -12,14 +12,19 @@ BOOL(*selyesno_gfnRemove)
char selyesno_confirmationMessage[256]; char selyesno_confirmationMessage[256];
char selyesno_title[32]; char selyesno_title[32];
UI_Item SELYESNO_DIALOG[] = { UiListItem SELYESNO_DIALOG_ITEMS[] = {
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, { "Yes", 0 },
{ { 24, 161, 590, 35 }, UI_TEXT, UIS_CENTER | UIS_BIG, 0, selyesno_title }, { "No", 1 }
{ { 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" },
}; };
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() void selyesno_Free()
{ {
ArtBackground.Unload(); ArtBackground.Unload();
@ -54,7 +59,7 @@ BOOL UiSelHeroDelYesNoDialog(
} }
sprintf(selyesno_confirmationMessage, "Are you sure you want to delete the character \"%s\"?", selyesno_heroInfo.name); 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); UiInitList(0, 1, NULL, selyesno_Select, selyesno_Esc, SELYESNO_DIALOG, size(SELYESNO_DIALOG), false, NULL);

6
SourceX/DiabloUI/title.cpp

@ -17,9 +17,9 @@ void title_Free()
BOOL UiTitleDialog(int a1) BOOL UiTitleDialog(int a1)
{ {
UI_Item TITLESCREEN_DIALOG[] = { UiItem TITLESCREEN_DIALOG[] = {
{ { 0, 0, 640, 480 }, UI_IMAGE, 0, 0, NULL, &ArtBackground }, UiImage(&ArtBackground, { 0, 0, 640, 480 }),
{ { 49, 410, 550, 26 }, UI_TEXT, UIS_MED | UIS_CENTER, 0, "Copyright \xA9 1996-2001 Blizzard Entertainment" }, UiText("Copyright \xA9 1996-2001 Blizzard Entertainment", { 49, 410, 550, 26 }, UIS_MED | UIS_CENTER)
}; };
title_Load(); title_Load();

202
SourceX/DiabloUI/ui_item.h

@ -0,0 +1,202 @@
#pragma once
#include <cstdint>
#include <string>
#include <vector>
#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 <std::size_t N>
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<decltype(SDL_Rect().h)>(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<decltype(SDL_Rect().y)>(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
Loading…
Cancel
Save