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

553 lines
20 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 "NkMiscMenus.h"
#include "NkCommon.h"
#include "MenuScreens.h"
#include "NuklearBridge.h"
#include <cstring>
namespace lve { namespace ui {
static const float BASE_W = 1280.0f;
static const float BASE_H = 720.0f;
static const float MSGBOX_W = 500.0f;
static const float MSGBOX_H = 280.0f;
static const float MSGBOX_BUTTON_W = 200.0f;
static const float MSGBOX_BUTTON_H = 40.0f;
static const float MSGBOX_BUTTON_GAP = 10.0f;
static const float MSGBOX_TITLE_H = 40.0f;
static const float MSGBOX_CONTENT_Y = 60.0f;
static const float MSGBOX_CONTENT_H = 130.0f;
static const float MSGBOX_BUTTON_Y = 210.0f;
static const float SAVE_MSG_BOX_W = 500.0f;
static const float SAVE_MSG_BOX_H = 200.0f;
static const float SAVE_MSG_BUTTON_W = 200.0f;
static const float SAVE_MSG_BUTTON_H = 40.0f;
static const float EULA_BOX_W = 700.0f;
static const float EULA_BOX_H = 400.0f;
static const float EULA_TEXT_PADDING = 20.0f;
static const float EULA_BUTTON_W = 200.0f;
static const float EULA_BUTTON_H = 40.0f;
static const float PROGRESS_BAR_W = 500.0f;
static const float PROGRESS_BAR_H = 20.0f;
static const float PROGRESS_TITLE_H = 40.0f;
static const float PROGRESS_STATUS_H = 30.0f;
static const float PROGRESS_TIP_H = 40.0f;
static const float PROGRESS_BUTTON_W = 200.0f;
static const float PROGRESS_BUTTON_H = 40.0f;
static const float EULA_LOGO_Y = 56.0f;
static const float EULA_LOGO_H = 138.0f;
static const float INTRO_FADE_IN = 1.5f;
static const float INTRO_HOLD = 2.0f;
static const float INTRO_FADE_OUT = 1.0f;
static const float INTRO_TOTAL = INTRO_FADE_IN + INTRO_HOLD + INTRO_FADE_OUT;
static const float SHADOW_OFFSET = 2.0f;
static void drawShadowedCenteredText(nk_context *ctx, const char *text,
float x, float y, float w, float h,
struct nk_color color)
{
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;
nk_draw_text(canvas, nk_rect(textX + SHADOW_OFFSET, textY + SHADOW_OFFSET, textW, font->height),
text, len, font,
nk_rgba(0, 0, 0, 0), nk_rgba(0x0F, 0x0F, 0x0F, color.a));
nk_draw_text(canvas, nk_rect(textX, textY, textW, font->height),
text, len, font,
nk_rgba(0, 0, 0, 0), color);
}
static void drawProgressBar(nk_context *ctx, float x, float y, float w, float h,
int percent, MenuTextures &tex)
{
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas == nullptr) return;
nk_fill_rect(canvas, nk_rect(x, y, w, h), 0, nk_rgb(40, 40, 40));
nk_stroke_rect(canvas, nk_rect(x, y, w, h), 0, 1.0f, nk_rgb(100, 100, 100));
int clamped = percent < 0 ? 0 : (percent > 100 ? 100 : percent);
float fillW = w * (float)clamped / 100.0f;
if (fillW > 0.0f) {
nk_fill_rect(canvas, nk_rect(x, y, fillW, h), 0, nk_rgb(76, 175, 80));
}
}
IntroState createIntroState()
{
IntroState state;
state.animationDone = false;
state.timer = 0.0f;
state.fadeDuration = INTRO_FADE_IN;
state.holdDuration = INTRO_HOLD;
state.fadeAlpha = 0.0f;
return state;
}
MiscMenuAction renderIntro(nk_context *ctx, int winW, int winH, MenuTextures &tex, IntroState &state, float dt)
{
MiscMenuAction result = kMiscAction_None;
state.timer += dt;
if (state.timer < INTRO_FADE_IN) {
state.fadeAlpha = state.timer / INTRO_FADE_IN;
} else if (state.timer < INTRO_FADE_IN + INTRO_HOLD) {
state.fadeAlpha = 1.0f;
} else if (state.timer < INTRO_TOTAL) {
state.fadeAlpha = 1.0f - (state.timer - INTRO_FADE_IN - INTRO_HOLD) / INTRO_FADE_OUT;
} else {
state.fadeAlpha = 0.0f;
state.animationDone = true;
result = kMiscAction_Continue;
}
if (state.fadeAlpha < 0.0f) state.fadeAlpha = 0.0f;
if (state.fadeAlpha > 1.0f) state.fadeAlpha = 1.0f;
pushCleanStyle(ctx);
if (nk_begin(ctx, "MiscIntro", nk_rect(0, 0, (float)winW, (float)winH),
NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT | NK_WINDOW_BACKGROUND)) {
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas != nullptr) {
nk_fill_rect(canvas, nk_rect(0, 0, (float)winW, (float)winH), 0, nk_rgb(0, 0, 0));
}
if (tex.logo.w > 0 && canvas != nullptr) {
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
float logoNativeW = (float)tex.logo.w;
float logoNativeH = (float)tex.logo.h;
float logoAR = logoNativeW / logoNativeH;
float drawH = 138.0f * scaleY;
float drawW = drawH * logoAR;
float maxW = 600.0f * scaleX;
if (drawW > maxW) {
drawW = maxW;
drawH = drawW / logoAR;
}
float logoX = ((float)winW - drawW) * 0.5f;
float logoY = ((float)winH - drawH) * 0.5f;
unsigned char alpha = (unsigned char)(state.fadeAlpha * 255.0f);
struct nk_image logoImg = slotToNkImage(tex.logo);
nk_draw_image(canvas, nk_rect(logoX, logoY, drawW, drawH),
&logoImg, nk_rgba(255, 255, 255, alpha));
}
const struct nk_input *input = &ctx->input;
if (nk_input_is_mouse_released(input, NK_BUTTON_LEFT) != 0) {
state.animationDone = true;
result = kMiscAction_Continue;
}
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
MiscMenuAction renderMessageBox(nk_context *ctx, int winW, int winH, MenuTextures &tex, const MessageBoxConfig &config)
{
MiscMenuAction result = kMiscAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "MiscMessageBox", nk_rect(0, 0, (float)winW, (float)winH),
NK_WINDOW_NO_SCROLLBAR | NK_WINDOW_NO_INPUT | NK_WINDOW_BACKGROUND)) {
drawDirtBg(ctx, winW, winH, tex);
float boxW = MSGBOX_W * scaleX;
float boxH = MSGBOX_H * scaleY;
float boxX = ((float)winW - boxW) * 0.5f;
float boxY = ((float)winH - boxH) * 0.5f;
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas != nullptr) {
nk_fill_rect(canvas, nk_rect(boxX, boxY, boxW, boxH), 0, nk_rgba(0, 0, 0, 180));
nk_stroke_rect(canvas, nk_rect(boxX, boxY, boxW, boxH), 0, 2.0f, nk_rgb(100, 100, 100));
}
if (config.title != nullptr) {
drawShadowedCenteredText(ctx, config.title,
boxX, boxY, boxW, MSGBOX_TITLE_H * scaleY,
nk_rgb(255, 255, 255));
}
if (config.content != nullptr) {
drawShadowedCenteredText(ctx, config.content,
boxX + 20.0f * scaleX,
boxY + MSGBOX_CONTENT_Y * scaleY,
boxW - 40.0f * scaleX,
MSGBOX_CONTENT_H * scaleY,
nk_rgb(200, 200, 200));
}
int btnCount = config.buttonCount;
if (btnCount < 1) btnCount = 1;
if (btnCount > 4) btnCount = 4;
float btnW = MSGBOX_BUTTON_W * scaleX;
float btnH = MSGBOX_BUTTON_H * scaleY;
float gap = MSGBOX_BUTTON_GAP * scaleX;
float totalBtnW = (float)btnCount * btnW + (float)(btnCount - 1) * gap;
float btnStartX = boxX + (boxW - totalBtnW) * 0.5f;
float btnY = boxY + MSGBOX_BUTTON_Y * scaleY;
const struct nk_input *input = &ctx->input;
static const MiscMenuAction btnActions[4] = {
kMiscAction_Accept,
kMiscAction_Decline,
kMiscAction_ThirdOption,
kMiscAction_FourthOption
};
for (int i = 0; i < btnCount; i++) {
float bx = btnStartX + (float)i * (btnW + gap);
const char *label = (config.buttons[i] != nullptr) ? config.buttons[i] : "OK";
struct nk_rect btnRect = nk_rect(bx, btnY, btnW, btnH);
bool hovering = nk_input_is_mouse_hovering_rect(input, btnRect) != 0;
if (drawBtn(ctx, label, bx, btnY, btnW, btnH, hovering, winH, tex)) {
result = btnActions[i];
}
}
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
static const char *EULA_TEXT =
"END USER LICENSE AGREEMENT\n"
"\n"
"This is a legal agreement between you and Mojang AB regarding the use "
"of Minecraft: Console Edition. By using the software you are agreeing "
"to be bound by the terms of this agreement.\n"
"\n"
"You must not distribute anything we've made unless we specifically "
"agree to it. You must not make commercial use of anything we've made. "
"You must not try to make money from anything we've made.\n"
"\n"
"If you have bought the game, you may play around with it and modify "
"it. We'd appreciate it if you didn't use this for griefing. We won't "
"tolerate any form of abuse towards other players.\n"
"\n"
"Any tools you write for the game from scratch belong to you. Basically, "
"Mods (or Plugins or Tools) are OK to distribute. However anything else "
"is not OK.\n"
"\n"
"By pressing Accept you agree to the terms of this agreement.";
EulaState createEulaState()
{
EulaState state;
state.scrollOffset = 0.0f;
state.accepted = false;
return state;
}
MiscMenuAction renderEula(nk_context *ctx, int winW, int winH, MenuTextures &tex, EulaState &state)
{
MiscMenuAction result = kMiscAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "MiscEula", 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);
float boxW = EULA_BOX_W * scaleX;
float boxH = EULA_BOX_H * scaleY;
float boxX = ((float)winW - boxW) * 0.5f;
float boxY = (EULA_LOGO_Y + EULA_LOGO_H + 20.0f) * scaleY;
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas != nullptr) {
nk_fill_rect(canvas, nk_rect(boxX, boxY, boxW, boxH), 0, nk_rgba(0, 0, 0, 160));
nk_stroke_rect(canvas, nk_rect(boxX, boxY, boxW, boxH), 0, 1.0f, nk_rgb(100, 100, 100));
}
float textX = boxX + EULA_TEXT_PADDING * scaleX;
float textY = boxY + EULA_TEXT_PADDING * scaleY;
float textW = boxW - EULA_TEXT_PADDING * 2.0f * scaleX;
float textH = boxH - EULA_TEXT_PADDING * 2.0f * scaleY;
const struct nk_user_font *font = ctx->style.font;
if (font != nullptr && canvas != nullptr) {
float lineH = font->height + 4.0f;
float yOff = textY - state.scrollOffset;
const char *p = EULA_TEXT;
while (*p != '\0') {
const char *lineEnd = p;
while (*lineEnd != '\0' && *lineEnd != '\n') lineEnd++;
int lineLen = (int)(lineEnd - p);
if (lineLen > 0 && yOff + lineH > textY && yOff < textY + textH) {
nk_draw_text(canvas, nk_rect(textX + SHADOW_OFFSET, yOff + SHADOW_OFFSET, textW, lineH),
p, lineLen, font,
nk_rgba(0, 0, 0, 0), nk_rgb(0x0F, 0x0F, 0x0F));
nk_draw_text(canvas, nk_rect(textX, yOff, textW, lineH),
p, lineLen, font,
nk_rgba(0, 0, 0, 0), nk_rgb(200, 200, 200));
}
yOff += lineH;
p = lineEnd;
if (*p == '\n') p++;
}
}
float btnW = EULA_BUTTON_W * scaleX;
float btnH = EULA_BUTTON_H * scaleY;
float btnX = ((float)winW - btnW) * 0.5f;
float btnY = boxY + boxH + 10.0f * scaleY;
const struct nk_input *input = &ctx->input;
struct nk_rect btnRect = nk_rect(btnX, btnY, btnW, btnH);
bool hovering = nk_input_is_mouse_hovering_rect(input, btnRect) != 0;
if (drawBtn(ctx, "Accept", btnX, btnY, btnW, btnH, hovering, winH, tex)) {
state.accepted = true;
result = kMiscAction_Accept;
}
if (input->mouse.scroll_delta.y != 0.0f) {
state.scrollOffset -= input->mouse.scroll_delta.y * 20.0f;
if (state.scrollOffset < 0.0f) state.scrollOffset = 0.0f;
}
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
MiscMenuAction renderSaveMessage(nk_context *ctx, int winW, int winH, MenuTextures &tex)
{
MiscMenuAction result = kMiscAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "MiscSaveMessage", 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);
float boxW = SAVE_MSG_BOX_W * scaleX;
float boxH = SAVE_MSG_BOX_H * scaleY;
float boxX = ((float)winW - boxW) * 0.5f;
float boxY = ((float)winH - boxH) * 0.5f + 40.0f * scaleY;
struct nk_command_buffer *canvas = nk_window_get_canvas(ctx);
if (canvas != nullptr) {
nk_fill_rect(canvas, nk_rect(boxX, boxY, boxW, boxH), 0, nk_rgba(0, 0, 0, 160));
nk_stroke_rect(canvas, nk_rect(boxX, boxY, boxW, boxH), 0, 1.0f, nk_rgb(100, 100, 100));
}
drawShadowedCenteredText(ctx,
"This game saves data automatically at certain points.",
boxX, boxY + 20.0f * scaleY,
boxW, 60.0f * scaleY,
nk_rgb(200, 200, 200));
float btnW = SAVE_MSG_BUTTON_W * scaleX;
float btnH = SAVE_MSG_BUTTON_H * scaleY;
float btnX = ((float)winW - btnW) * 0.5f;
float btnY = boxY + boxH - btnH - 20.0f * scaleY;
const struct nk_input *input = &ctx->input;
struct nk_rect btnRect = nk_rect(btnX, btnY, btnW, btnH);
bool hovering = nk_input_is_mouse_hovering_rect(input, btnRect) != 0;
if (drawBtn(ctx, "OK", btnX, btnY, btnW, btnH, hovering, winH, tex)) {
result = kMiscAction_Confirm;
}
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
MiscMenuAction renderConnectingProgress(nk_context *ctx, int winW, int winH, MenuTextures &tex, ConnectingProgressState &state)
{
MiscMenuAction result = kMiscAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "MiscConnecting", 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);
float centerY = (float)winH * 0.5f + 40.0f * scaleY;
if (state.title != nullptr) {
drawShadowedCenteredText(ctx, state.title,
0.0f, centerY - PROGRESS_TITLE_H * scaleY,
(float)winW, PROGRESS_TITLE_H * scaleY,
nk_rgb(255, 255, 255));
}
if (!state.showButton) {
float barW = PROGRESS_BAR_W * scaleX;
float barH = PROGRESS_BAR_H * scaleY;
float barX = ((float)winW - barW) * 0.5f;
float barY = centerY + 10.0f * scaleY;
drawProgressBar(ctx, barX, barY, barW, barH, state.progressPercent, tex);
}
if (state.connectionFailed && state.failReason != nullptr) {
drawShadowedCenteredText(ctx, state.failReason,
0.0f, centerY + 40.0f * scaleY,
(float)winW, PROGRESS_STATUS_H * scaleY,
nk_rgb(255, 85, 85));
}
if (state.showButton) {
float btnW = PROGRESS_BUTTON_W * scaleX;
float btnH = PROGRESS_BUTTON_H * scaleY;
float btnX = ((float)winW - btnW) * 0.5f;
float btnY = centerY + 80.0f * scaleY;
const struct nk_input *input = &ctx->input;
struct nk_rect btnRect = nk_rect(btnX, btnY, btnW, btnH);
bool hovering = nk_input_is_mouse_hovering_rect(input, btnRect) != 0;
if (drawBtn(ctx, "OK", btnX, btnY, btnW, btnH, hovering, winH, tex)) {
result = kMiscAction_Confirm;
}
}
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
MiscMenuAction renderFullscreenProgress(nk_context *ctx, int winW, int winH, MenuTextures &tex, FullscreenProgressState &state)
{
MiscMenuAction result = kMiscAction_None;
float scaleX = (float)winW / BASE_W;
float scaleY = (float)winH / BASE_H;
pushCleanStyle(ctx);
if (nk_begin(ctx, "MiscFullscreenProgress", 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);
float centerY = (float)winH * 0.5f + 20.0f * scaleY;
if (state.title != nullptr) {
drawShadowedCenteredText(ctx, state.title,
0.0f, centerY - PROGRESS_TITLE_H * scaleY - 10.0f * scaleY,
(float)winW, PROGRESS_TITLE_H * scaleY,
nk_rgb(255, 255, 255));
}
float barW = PROGRESS_BAR_W * scaleX;
float barH = PROGRESS_BAR_H * scaleY;
float barX = ((float)winW - barW) * 0.5f;
float barY = centerY;
drawProgressBar(ctx, barX, barY, barW, barH, state.progressPercent, tex);
if (state.status != nullptr) {
drawShadowedCenteredText(ctx, state.status,
0.0f, barY + barH + 5.0f * scaleY,
(float)winW, PROGRESS_STATUS_H * scaleY,
nk_rgb(200, 200, 200));
}
if (state.showTip && state.tip != nullptr) {
float tipY = (float)winH - PROGRESS_TIP_H * scaleY - 20.0f * scaleY;
drawShadowedCenteredText(ctx, state.tip,
0.0f, tipY,
(float)winW, PROGRESS_TIP_H * scaleY,
nk_rgb(255, 255, 100));
}
if (state.showButton) {
float btnW = PROGRESS_BUTTON_W * scaleX;
float btnH = PROGRESS_BUTTON_H * scaleY;
float btnX = ((float)winW - btnW) * 0.5f;
float btnY = barY + barH + PROGRESS_STATUS_H * scaleY + 20.0f * scaleY;
const struct nk_input *input = &ctx->input;
struct nk_rect btnRect = nk_rect(btnX, btnY, btnW, btnH);
bool hovering = nk_input_is_mouse_hovering_rect(input, btnRect) != 0;
if (drawBtn(ctx, "OK", btnX, btnY, btnW, btnH, hovering, winH, tex)) {
result = kMiscAction_Confirm;
}
}
}
nk_end(ctx);
popCleanStyle(ctx);
return result;
}
}}