Files
coah80-LegacyVulkEdition/Minecraft.Client/CrossPlatform/NkWorldMenus.cpp

666 lines
25 KiB
C++

#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#include "nuklear.h"
#include "NkWorldMenus.h"
#include "NkCommon.h"
#include "MenuScreens.h"
#include "NuklearBridge.h"
#include <cstring>
#include <cstdio>
namespace lve { namespace ui {
static const float BASE_W = 1280.0f;
static const float BASE_H = 720.0f;
static const float PANEL_W = 700.0f;
static const float FIELD_H = 36.0f;
static const float FIELD_SPACING = 8.0f;
static const float LABEL_H = 20.0f;
static const float BUTTON_W = 450.0f;
static const float BUTTON_H = 40.0f;
static const float HALF_BUTTON_W = 218.0f;
static const float LIST_ITEM_H = 40.0f;
static const float LIST_PANEL_X = 40.0f;
static const float LIST_PANEL_W = 560.0f;
static const float LIST_Y_START = 90.0f;
static const float JOIN_LIST_X = 660.0f;
static const float JOIN_LIST_W = 560.0f;
static const float BOTTOM_BTN_Y_OFFSET = 40.0f;
static const float SHADOW_OFF = 2.0f;
static const char *DIFFICULTY_NAMES[4] = {
"Peaceful",
"Easy",
"Normal",
"Hard"
};
static void drawShadowLabel(nk_context *ctx, const char *text,
float x, float y, float w,
struct nk_color fg)
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr) return;
const struct nk_user_font *font = ctx->style.font;
if (font == nullptr) return;
int len = (int)strlen(text);
struct nk_color shadow = nk_rgb(0x3F, 0x3F, 0x3F);
nk_draw_text(canvas, nk_rect(x + SHADOW_OFF, y + SHADOW_OFF, w, font->height),
text, len, font, nk_rgba(0, 0, 0, 0), shadow);
nk_draw_text(canvas, nk_rect(x, y, w, font->height),
text, len, font, nk_rgba(0, 0, 0, 0), fg);
}
static void drawShadowLabelCentered(nk_context *ctx, const char *text,
float x, float y, float w, float h,
struct nk_color fg)
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr) return;
const struct nk_user_font *font = ctx->style.font;
if (font == nullptr) return;
int len = (int)strlen(text);
float textW = font->width(font->userdata, font->height, text, len);
float textX = x + (w - textW) * 0.5f;
float textY = y + (h - font->height) * 0.5f;
struct nk_color shadow = nk_rgb(0x3F, 0x3F, 0x3F);
nk_draw_text(canvas, nk_rect(textX + SHADOW_OFF, textY + SHADOW_OFF, textW, font->height),
text, len, font, nk_rgba(0, 0, 0, 0), shadow);
nk_draw_text(canvas, nk_rect(textX, textY, textW, font->height),
text, len, font, nk_rgba(0, 0, 0, 0), fg);
}
static void drawShadowTitle(nk_context *ctx, const char *title,
float scaleY, int winW)
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr) return;
nk_font *titleNkFont = getTitleFont();
const struct nk_user_font *font = titleNkFont ? &titleNkFont->handle : ctx->style.font;
if (font == nullptr) return;
int len = (int)strlen(title);
float textW = font->width(font->userdata, font->height, title, len);
float textX = ((float)winW - textW) * 0.5f;
float textY = 30.0f * scaleY;
struct nk_color shadow = nk_rgb(0x3F, 0x3F, 0x3F);
nk_draw_text(canvas, nk_rect(textX + SHADOW_OFF, textY + SHADOW_OFF, textW, font->height),
title, len, font, nk_rgba(0, 0, 0, 0), shadow);
nk_draw_text(canvas, nk_rect(textX, textY, textW, font->height),
title, len, font, nk_rgba(0, 0, 0, 0), nk_rgb(255, 255, 255));
}
static bool drawStyledTextInput(nk_context *ctx, const char *content,
float x, float y, float w, float h,
bool selected, MenuTextures &tex)
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr) return false;
struct nk_rect fieldRect = nk_rect(x, y, w, h);
if (tex.textInputBg.w > 0) {
struct nk_image bgImg = slotToNkImage(tex.textInputBg);
nk_draw_image(canvas, fieldRect, &bgImg, nk_rgb(255, 255, 255));
} else {
nk_fill_rect(canvas, fieldRect, 0, nk_rgb(0, 0, 0));
}
if (selected && tex.textInputSelectedBorder.w > 0) {
struct nk_image selImg = slotToNkImage(tex.textInputSelectedBorder);
nk_draw_image(canvas, fieldRect, &selImg, nk_rgb(255, 255, 255));
} else if (tex.textInputBorder.w > 0) {
struct nk_image borderImg = slotToNkImage(tex.textInputBorder);
nk_draw_image(canvas, fieldRect, &borderImg, nk_rgb(255, 255, 255));
} else {
struct nk_color borderCol = selected ? nk_rgb(160, 160, 255) : nk_rgb(160, 160, 160);
float thick = selected ? 2.0f : 1.0f;
nk_stroke_rect(canvas, fieldRect, 0, thick, borderCol);
}
const struct nk_user_font *font = ctx->style.font;
if (font != nullptr) {
int len = (int)strlen(content);
float pad = 6.0f;
float textX = x + pad;
float textY = y + (h - font->height) * 0.5f;
float maxW = w - pad * 2.0f;
nk_draw_text(canvas, nk_rect(textX + SHADOW_OFF, textY + SHADOW_OFF, maxW, font->height),
content, len, font, nk_rgba(0, 0, 0, 0), nk_rgb(0x3F, 0x3F, 0x3F));
nk_draw_text(canvas, nk_rect(textX, textY, maxW, font->height),
content, len, font, nk_rgba(0, 0, 0, 0), nk_rgb(224, 224, 224));
if (selected) {
float caretX = textX + font->width(font->userdata, font->height, content, len);
if (caretX > x + w - 8.0f) caretX = x + w - 8.0f;
if (tex.textInputCaret.w > 0) {
struct nk_image caretImg = slotToNkImage(tex.textInputCaret);
float caretW = 2.0f;
float caretH = h - 8.0f;
nk_draw_image(canvas, nk_rect(caretX, y + 4.0f, caretW, caretH),
&caretImg, nk_rgb(255, 255, 255));
} else {
nk_stroke_line(canvas, caretX, y + 4.0f, caretX, y + h - 4.0f,
1.0f, nk_rgb(255, 255, 255));
}
}
}
const struct nk_input *input = &ctx->input;
bool hovering = nk_input_is_mouse_hovering_rect(input, fieldRect) != 0;
bool clicked = hovering && nk_input_is_mouse_released(input, NK_BUTTON_LEFT) != 0;
return clicked;
}
static void drawSliderTrack(nk_context *ctx, float x, float y, float w, float h,
int value, int maxVal, MenuTextures &tex)
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr) return;
struct nk_rect trackRect = nk_rect(x, y, w, h);
if (tex.sliderBackground.w > 0) {
struct nk_image bgImg = slotToNkImage(tex.sliderBackground);
nk_draw_image(canvas, trackRect, &bgImg, nk_rgb(255, 255, 255));
} else {
nk_fill_rect(canvas, trackRect, 0, nk_rgb(0, 0, 0));
nk_stroke_rect(canvas, trackRect, 0, 1.0f, nk_rgb(160, 160, 160));
}
if (maxVal > 0) {
float inset = 2.0f;
float fillW = (w - inset * 2.0f) * ((float)value / (float)maxVal);
struct nk_rect fillRect = nk_rect(x + inset, y + inset, fillW, h - inset * 2.0f);
if (tex.sliderTrackActive.w > 0) {
struct nk_image activeImg = slotToNkImage(tex.sliderTrackActive);
nk_draw_image(canvas, fillRect, &activeImg, nk_rgb(255, 255, 255));
} else {
nk_fill_rect(canvas, fillRect, 0, nk_rgb(60, 120, 60));
}
float thumbW = 8.0f;
float thumbX = x + inset + fillW - thumbW * 0.5f;
if (thumbX < x) thumbX = x;
if (thumbX + thumbW > x + w) thumbX = x + w - thumbW;
if (tex.sliderThumb.w > 0) {
struct nk_image thumbImg = slotToNkImage(tex.sliderThumb);
nk_draw_image(canvas, nk_rect(thumbX, y, thumbW, h),
&thumbImg, nk_rgb(255, 255, 255));
}
}
}
static bool drawShadowListItem(nk_context *ctx, const char *label,
float x, float y, float w, float h,
bool selected, bool isHovered)
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr) return false;
struct nk_rect itemRect = nk_rect(x, y, w, h);
if (selected) {
nk_fill_rect(canvas, itemRect, 0, nk_rgb(60, 60, 100));
} else if (isHovered) {
nk_fill_rect(canvas, itemRect, 0, nk_rgb(50, 50, 70));
} else {
nk_fill_rect(canvas, itemRect, 0, nk_rgba(0, 0, 0, 120));
}
nk_stroke_rect(canvas, itemRect, 0, 1.0f, nk_rgb(80, 80, 80));
const struct nk_user_font *font = ctx->style.font;
if (font != nullptr) {
int len = (int)strlen(label);
float textX = x + 8.0f;
float textY = y + (h - font->height) * 0.5f;
float maxW = w - 16.0f;
nk_draw_text(canvas, nk_rect(textX + SHADOW_OFF, textY + SHADOW_OFF, maxW, font->height),
label, len, font, nk_rgba(0, 0, 0, 0), nk_rgb(0x3F, 0x3F, 0x3F));
nk_draw_text(canvas, nk_rect(textX, textY, maxW, font->height),
label, len, font, nk_rgba(0, 0, 0, 0), nk_rgb(255, 255, 255));
}
const struct nk_input *input = &ctx->input;
bool hovering = nk_input_is_mouse_hovering_rect(input, itemRect) != 0;
bool clicked = hovering && nk_input_is_mouse_released(input, NK_BUTTON_LEFT) != 0;
return clicked;
}
WorldMenuAction renderCreateWorld(nk_context *ctx, int winW, int winH,
MenuTextures &tex, CreateWorldState &state)
{
WorldMenuAction result = kWorldAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "CreateWorldMenu", nk_rect(0, 0, (float)winW, (float)winH),
NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT | NK_WINDOW_BACKGROUND)) {
drawPanoramaBg(ctx, winW, winH, tex);
const struct nk_input *input = &ctx->input;
float panelW = PANEL_W * scaleX;
float panelX = ((float)winW - panelW) * 0.5f;
float curY = 80.0f * scaleY;
float fieldH = FIELD_H * scaleY;
float labelH = LABEL_H * scaleY;
float spacing = FIELD_SPACING * scaleY;
drawShadowTitle(ctx, "Create New World", scaleY, winW);
drawShadowLabel(ctx, "World Name", panelX, curY, panelW, nk_rgb(160, 160, 160));
curY += labelH;
if (drawStyledTextInput(ctx, state.worldName, panelX, curY, panelW, fieldH,
state.activeField == kField_WorldName, tex)) {
state.activeField = kField_WorldName;
}
curY += fieldH + spacing;
drawShadowLabel(ctx, "Seed for the World Generator (Leave blank for random)",
panelX, curY, panelW, nk_rgb(160, 160, 160));
curY += labelH;
if (drawStyledTextInput(ctx, state.seed, panelX, curY, panelW, fieldH,
state.activeField == kField_Seed, tex)) {
state.activeField = kField_Seed;
}
curY += fieldH + spacing * 2.0f;
float btnW = BUTTON_W * scaleX;
float btnH = BUTTON_H * scaleY;
float btnX = ((float)winW - btnW) * 0.5f;
const char *gameModeLabel = state.survivalMode
? "Game Mode: Survival"
: "Game Mode: Creative";
struct nk_rect gmRect = nk_rect(btnX, curY, btnW, btnH);
bool gmHover = nk_input_is_mouse_hovering_rect(input, gmRect) != 0;
if (drawBtn(ctx, gameModeLabel, btnX, curY, btnW, btnH, gmHover, winH, tex)) {
result = kWorldAction_ToggleGameMode;
}
curY += btnH + spacing;
char diffLabel[64];
snprintf(diffLabel, sizeof(diffLabel), "Difficulty: %s", DIFFICULTY_NAMES[state.difficulty]);
drawShadowLabelCentered(ctx, diffLabel, btnX, curY, btnW, labelH, nk_rgb(255, 255, 255));
curY += labelH + 2.0f * scaleY;
float sliderH = 16.0f * scaleY;
drawSliderTrack(ctx, btnX, curY, btnW, sliderH, state.difficulty, 3, tex);
struct nk_rect sliderRect = nk_rect(btnX, curY, btnW, sliderH);
bool sliderHover = nk_input_is_mouse_hovering_rect(input, sliderRect) != 0;
if (sliderHover && nk_input_is_mouse_released(input, NK_BUTTON_LEFT)) {
float mouseX = input->mouse.pos.x;
float rel = (mouseX - btnX) / btnW;
if (rel < 0.0f) rel = 0.0f;
if (rel > 1.0f) rel = 1.0f;
state.difficulty = (int)(rel * 3.0f + 0.5f);
if (state.difficulty > 3) state.difficulty = 3;
}
curY += sliderH + spacing * 2.0f;
struct nk_rect moRect = nk_rect(btnX, curY, btnW, btnH);
bool moHover = nk_input_is_mouse_hovering_rect(input, moRect) != 0;
if (drawBtn(ctx, "More Options...", btnX, curY, btnW, btnH, moHover, winH, tex)) {
result = kWorldAction_MoreOptions;
}
curY += btnH + spacing;
struct nk_rect cwRect = nk_rect(btnX, curY, btnW, btnH);
bool cwHover = nk_input_is_mouse_hovering_rect(input, cwRect) != 0;
if (drawBtn(ctx, "Create New World", btnX, curY, btnW, btnH, cwHover, winH, tex)) {
result = kWorldAction_CreateWorld;
}
curY += btnH + spacing;
float backY = (float)winH - BOTTOM_BTN_Y_OFFSET * scaleY - btnH;
struct nk_rect backRect = nk_rect(btnX, backY, btnW, btnH);
bool backHover = nk_input_is_mouse_hovering_rect(input, backRect) != 0;
if (drawBtn(ctx, "Cancel", btnX, backY, btnW, btnH, backHover, winH, tex)) {
result = kWorldAction_Back;
}
drawControllerPrompts(ctx, winW, winH, tex);
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
WorldMenuAction renderLoadMenu(nk_context *ctx, int winW, int winH,
MenuTextures &tex, LoadMenuState &state)
{
WorldMenuAction result = kWorldAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "LoadMenuScreen", nk_rect(0, 0, (float)winW, (float)winH),
NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT | NK_WINDOW_BACKGROUND)) {
drawPanoramaBg(ctx, winW, winH, tex);
drawLogo(ctx, winW, winH, tex);
const struct nk_input *input = &ctx->input;
float panelW = PANEL_W * scaleX;
float panelX = ((float)winW - panelW) * 0.5f;
float curY = 80.0f * scaleY;
float itemH = LIST_ITEM_H * scaleY;
float spacing = FIELD_SPACING * scaleY;
float btnH = BUTTON_H * scaleY;
drawShadowTitle(ctx, "Load Saved World", scaleY, winW);
int visibleItems = 8;
float listH = itemH * (float)visibleItems;
if (state.itemCount == 0) {
drawShadowLabelCentered(ctx, "No saved worlds", panelX, curY, panelW, listH,
nk_rgb(160, 160, 160));
} else {
for (int i = 0; i < state.itemCount && i < visibleItems; i++) {
int idx = i + state.scrollOffset;
if (idx >= state.itemCount) break;
float itemY = curY + (float)i * itemH;
bool isSelected = (idx == state.selectedIndex);
char itemLabel[64];
snprintf(itemLabel, sizeof(itemLabel), "World %d", idx + 1);
struct nk_rect itemRect = nk_rect(panelX, itemY, panelW, itemH);
bool itemHover = nk_input_is_mouse_hovering_rect(input, itemRect) != 0;
if (drawShadowListItem(ctx, itemLabel, panelX, itemY, panelW, itemH,
isSelected, itemHover)) {
state.selectedIndex = idx;
result = kWorldAction_SelectSave;
}
}
}
curY += listH + spacing * 2.0f;
float halfBtnW = HALF_BUTTON_W * scaleX;
float btnGap = 14.0f * scaleX;
float threeW = halfBtnW * 3.0f + btnGap * 2.0f;
float threeX = ((float)winW - threeW) * 0.5f;
struct nk_rect loadRect = nk_rect(threeX, curY, halfBtnW, btnH);
bool loadHover = nk_input_is_mouse_hovering_rect(input, loadRect) != 0;
if (drawBtn(ctx, "Load", threeX, curY, halfBtnW, btnH, loadHover, winH, tex)) {
result = kWorldAction_LoadWorld;
}
float delX = threeX + halfBtnW + btnGap;
struct nk_rect delRect = nk_rect(delX, curY, halfBtnW, btnH);
bool delHover = nk_input_is_mouse_hovering_rect(input, delRect) != 0;
if (drawBtn(ctx, "Delete", delX, curY, halfBtnW, btnH, delHover, winH, tex)) {
result = kWorldAction_DeleteSave;
}
float renX = delX + halfBtnW + btnGap;
struct nk_rect renRect = nk_rect(renX, curY, halfBtnW, btnH);
bool renHover = nk_input_is_mouse_hovering_rect(input, renRect) != 0;
if (drawBtn(ctx, "Rename", renX, curY, halfBtnW, btnH, renHover, winH, tex)) {
result = kWorldAction_RenameSave;
}
curY += btnH + spacing;
float backBtnW = BUTTON_W * scaleX;
float backX = ((float)winW - backBtnW) * 0.5f;
float backY = (float)winH - BOTTOM_BTN_Y_OFFSET * scaleY - btnH;
struct nk_rect backRect = nk_rect(backX, backY, backBtnW, btnH);
bool backHover = nk_input_is_mouse_hovering_rect(input, backRect) != 0;
if (drawBtn(ctx, "Cancel", backX, backY, backBtnW, btnH, backHover, winH, tex)) {
result = kWorldAction_Back;
}
drawControllerPrompts(ctx, winW, winH, tex);
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
WorldMenuAction renderLoadOrJoin(nk_context *ctx, int winW, int winH,
MenuTextures &tex, LoadMenuState &state)
{
WorldMenuAction result = kWorldAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "LoadOrJoinMenu", nk_rect(0, 0, (float)winW, (float)winH),
NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT | NK_WINDOW_BACKGROUND)) {
drawPanoramaBg(ctx, winW, winH, tex);
drawLogo(ctx, winW, winH, tex);
const struct nk_input *input = &ctx->input;
float listX = LIST_PANEL_X * scaleX;
float listW = LIST_PANEL_W * scaleX;
float listStartY = LIST_Y_START * scaleY;
float itemH = LIST_ITEM_H * scaleY;
float btnH = BUTTON_H * scaleY;
drawShadowLabel(ctx, "Start Game", listX, 50.0f * scaleY, listW,
nk_rgb(255, 255, 255));
float curY = listStartY;
struct nk_rect createRect = nk_rect(listX, curY, listW, itemH);
bool createHover = nk_input_is_mouse_hovering_rect(input, createRect) != 0;
if (drawShadowListItem(ctx, "Create New World", listX, curY, listW, itemH,
false, createHover)) {
result = kWorldAction_CreateNewWorld;
}
curY += itemH;
int visibleSaves = 10;
for (int i = 0; i < state.itemCount && i < visibleSaves; i++) {
int idx = i + state.scrollOffset;
if (idx >= state.itemCount) break;
float itemY = curY + (float)i * itemH;
bool isSelected = (idx == state.selectedIndex);
char itemLabel[64];
snprintf(itemLabel, sizeof(itemLabel), "World %d", idx + 1);
struct nk_rect itemRect = nk_rect(listX, itemY, listW, itemH);
bool itemHover = nk_input_is_mouse_hovering_rect(input, itemRect) != 0;
if (drawShadowListItem(ctx, itemLabel, listX, itemY, listW, itemH,
isSelected, itemHover)) {
state.selectedIndex = idx;
result = kWorldAction_PlayGame;
}
}
float joinX = JOIN_LIST_X * scaleX;
float joinW = JOIN_LIST_W * scaleX;
drawShadowLabel(ctx, "Join Game", joinX, 50.0f * scaleY, joinW,
nk_rgb(255, 255, 255));
drawShadowLabelCentered(ctx, "No games found", joinX, listStartY, joinW, itemH * 3.0f,
nk_rgb(160, 160, 160));
float backBtnW = BUTTON_W * scaleX;
float backX = ((float)winW - backBtnW) * 0.5f;
float backY = (float)winH - BOTTOM_BTN_Y_OFFSET * scaleY - btnH;
struct nk_rect backRect = nk_rect(backX, backY, backBtnW, btnH);
bool backHover = nk_input_is_mouse_hovering_rect(input, backRect) != 0;
if (drawBtn(ctx, "Cancel", backX, backY, backBtnW, btnH, backHover, winH, tex)) {
result = kWorldAction_Back;
}
drawControllerPrompts(ctx, winW, winH, tex);
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
WorldMenuAction renderJoinMenu(nk_context *ctx, int winW, int winH,
MenuTextures &tex, JoinMenuState &state)
{
WorldMenuAction result = kWorldAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "JoinMenuScreen", nk_rect(0, 0, (float)winW, (float)winH),
NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT | NK_WINDOW_BACKGROUND)) {
drawPanoramaBg(ctx, winW, winH, tex);
drawLogo(ctx, winW, winH, tex);
const struct nk_input *input = &ctx->input;
float panelW = PANEL_W * scaleX;
float panelX = ((float)winW - panelW) * 0.5f;
float curY = 80.0f * scaleY;
float itemH = LIST_ITEM_H * scaleY;
float spacing = FIELD_SPACING * scaleY;
float labelH = LABEL_H * scaleY;
float btnH = BUTTON_H * scaleY;
drawShadowTitle(ctx, "Join Game", scaleY, winW);
drawShadowLabel(ctx, "Players", panelX, curY, panelW, nk_rgb(160, 160, 160));
curY += labelH;
int visiblePlayers = 4;
float playerListH = itemH * (float)visiblePlayers;
if (state.itemCount == 0) {
drawShadowLabelCentered(ctx, "No players", panelX, curY, panelW, playerListH,
nk_rgb(160, 160, 160));
} else {
for (int i = 0; i < state.itemCount && i < visiblePlayers; i++) {
int idx = i + state.scrollOffset;
if (idx >= state.itemCount) break;
float itemY = curY + (float)i * itemH;
bool isSelected = (idx == state.selectedIndex);
char playerLabel[64];
snprintf(playerLabel, sizeof(playerLabel), "Player %d", idx + 1);
struct nk_rect itemRect = nk_rect(panelX, itemY, panelW, itemH);
bool itemHover = nk_input_is_mouse_hovering_rect(input, itemRect) != 0;
drawShadowListItem(ctx, playerLabel, panelX, itemY, panelW, itemH,
isSelected, itemHover);
}
}
curY += playerListH + spacing;
float labelColW = panelW * 0.5f;
struct { const char *label; const char *value; } infoRows[] = {
{"Difficulty:", "Normal"},
{"Game Type:", "Survival"},
{"Gamertags:", "On"},
{"Generate Structures:", "On"},
{"Level Type:", "Normal"},
{"PvP:", "On"},
{"Trust Players:", "On"},
{"TNT Explodes:", "On"},
{"Fire Spreads:", "On"}
};
int rowCount = 9;
float rowH = labelH + 4.0f * scaleY;
for (int i = 0; i < rowCount; i++) {
float rowY = curY + (float)i * rowH;
drawShadowLabel(ctx, infoRows[i].label, panelX, rowY, labelColW,
nk_rgb(160, 160, 160));
drawShadowLabel(ctx, infoRows[i].value, panelX + labelColW, rowY, labelColW,
nk_rgb(255, 255, 255));
}
curY += (float)rowCount * rowH + spacing * 2.0f;
float btnW = BUTTON_W * scaleX;
float btnX = ((float)winW - btnW) * 0.5f;
struct nk_rect joinRect = nk_rect(btnX, curY, btnW, btnH);
bool joinHover = nk_input_is_mouse_hovering_rect(input, joinRect) != 0;
if (drawBtn(ctx, "Join Game", btnX, curY, btnW, btnH, joinHover, winH, tex)) {
result = kWorldAction_JoinGame;
}
float backY = (float)winH - BOTTOM_BTN_Y_OFFSET * scaleY - btnH;
struct nk_rect backRect = nk_rect(btnX, backY, btnW, btnH);
bool backHover = nk_input_is_mouse_hovering_rect(input, backRect) != 0;
if (drawBtn(ctx, "Cancel", btnX, backY, btnW, btnH, backHover, winH, tex)) {
result = kWorldAction_Back;
}
drawControllerPrompts(ctx, winW, winH, tex);
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
}}