From 0728ac8731c65c2cad845a4968cb709bec830bd3 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Wed, 25 Mar 2026 18:26:31 +0300 Subject: [PATCH 01/40] fix(jui): tick screen even if not ingame required for title screen panorama --- Minecraft.Client/Minecraft.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 65d5e87dd..bb2610813 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1965,13 +1965,12 @@ void Minecraft::run_middle() { // clear the stored button downs since the tick for this // player will now have actioned them player->ullButtonsPressed = 0LL; + } else if (screen != NULL) { + screen->updateEvents(); + screen->tick(); } } - if (screen != NULL) { - screen->updateEvents(); - } - ui.HandleGameTick(); setLocalPlayerIdx(ProfileManager.GetPrimaryPad()); From 3f2c6e0012f3f45e9fe654c256ecd1a1cb797a9f Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Wed, 25 Mar 2026 19:31:36 +0300 Subject: [PATCH 02/40] feat(jui): add panorama and splashes to title screen Also moved the version string to the same height as the copyright message, matching Beta 1.8+ --- .../Common/res/1_2_2/title/bg/panorama.png | Bin 0 -> 159778 bytes Minecraft.Client/Textures/Textures.cpp | 5 + Minecraft.Client/Textures/Textures.h | 5 + Minecraft.Client/UI/Screens/TitleScreen.cpp | 147 ++++++++++++++---- Minecraft.Client/UI/Screens/TitleScreen.h | 16 ++ 5 files changed, 147 insertions(+), 26 deletions(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/title/bg/panorama.png diff --git a/Minecraft.Assets/Common/res/1_2_2/title/bg/panorama.png b/Minecraft.Assets/Common/res/1_2_2/title/bg/panorama.png new file mode 100644 index 0000000000000000000000000000000000000000..65ae6070c6b5a6ba0c01c1bec95a4aa7a9b3c3e8 GIT binary patch literal 159778 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%0DM{^I}f7k}~p`sFYFpS!#7XlajOp1_J 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

92QGs?h>IgF^Pn2Ea@rw*kM5@R9zMzmpq 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 s1!4 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 splashes; - /* -BufferedReader *br = new BufferedReader(new -InputStreamReader(InputStream::getResourceAsStream(L"res\\title\\splashes.txt"))); -//, Charset.forName("UTF-8") + // 4jcraft: copied over from UIScene_MainMenu + int splashIndex; -std::wstring line = L""; -while ( !(line = br->readLine()).empty() ) - { - line = trimString( line ); - if (line.length() > 0) - { - splashes.push_back(line); + std::wstring filename = L"splashes.txt"; + if (app.hasArchiveFile(filename)) { + byteArray splashesArray = app.getArchiveFile(filename); + ByteArrayInputStream bais(splashesArray); + InputStreamReader isr(&bais); + BufferedReader br(&isr); + + std::wstring line = L""; + while (!(line = br.readLine()).empty()) { + line = trimString(line); + if (line.length() > 0) { + splashes.push_back(line); + } + } + + br.close(); } - br->close(); - delete br; - */ + splashIndex = + eSplashRandomStart + 1 + + random->nextInt((int)splashes.size() - (eSplashRandomStart + 1)); - // splash = L""; //splashes.at(random->nextInt(splashes.size())); + // Override splash text on certain dates + SYSTEMTIME LocalSysTime; + GetLocalTime(&LocalSysTime); + if (LocalSysTime.wMonth == 11 && LocalSysTime.wDay == 9) { + splashIndex = eSplashHappyBirthdayEx; + } else if (LocalSysTime.wMonth == 6 && LocalSysTime.wDay == 1) { + splashIndex = eSplashHappyBirthdayNotch; + } else if (LocalSysTime.wMonth == 12 && + LocalSysTime.wDay == 24) // the Java game shows this on + // Christmas Eve, so we will too + { + splashIndex = eSplashMerryXmas; + } else if (LocalSysTime.wMonth == 1 && LocalSysTime.wDay == 1) { + splashIndex = eSplashHappyNewYear; + } - // } catch (Exception e) { - // } + splash = splashes.at(splashIndex); } void TitleScreen::tick() { - // vo += 1.0f; + vo += 1.0f; // if( vo > 100.0f ) minecraft->setScreen(new SelectWorldScreen(this)); // // 4J - temp testing } @@ -104,32 +124,105 @@ if (c.get(Calendar.MONTH) + 1 == 11 && c.get(Calendar.DAY_OF_MONTH) == 9) { void TitleScreen::buttonClicked(Button* button) { if (button->id == 0) { - app.DebugPrintf("TitleScreen::buttonClicked() 'Options...' if (button->id == 0)\n"); + app.DebugPrintf( + "TitleScreen::buttonClicked() 'Options...' if (button->id == 0)\n"); minecraft->setScreen(new OptionsScreen(this, minecraft->options)); } if (button->id == 1) { - app.DebugPrintf("TitleScreen::buttonClicked() 'Singleplayer' if (button->id == 1)\n"); + app.DebugPrintf( + "TitleScreen::buttonClicked() 'Singleplayer' if (button->id == " + "1)\n"); minecraft->setScreen(new SelectWorldScreen(this)); } if (button->id == 2) { - app.DebugPrintf("TitleScreen::buttonClicked() 'Multiplayer' if (button->id == 2)\n"); + app.DebugPrintf( + "TitleScreen::buttonClicked() 'Multiplayer' if (button->id == " + "2)\n"); minecraft->setScreen(new JoinMultiplayerScreen(this)); } if (button->id == 3) { - app.DebugPrintf("TitleScreen::buttonClicked() 'Texture Pack' if (button->id == 3)\n"); + app.DebugPrintf( + "TitleScreen::buttonClicked() 'Texture Pack' if (button->id == " + "3)\n"); // minecraft->setScreen(new TexturePackSelectScreen(this)); // // 4J - TODO put back in } if (button->id == 4) { - app.DebugPrintf("TitleScreen::buttonClicked() Exit Game if (button->id == 4)\n"); - RenderManager.Close(); //minecraft->stop(); + app.DebugPrintf( + "TitleScreen::buttonClicked() Exit Game if (button->id == 4)\n"); + RenderManager.Close(); // minecraft->stop(); } } +// 4jcraft: render our panorama +// uses the TU panorama instead of JE panorama and as such a different rendering +// method +void TitleScreen::renderPanorama() { + Tesselator* t = Tesselator::getInstance(); + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, width, height, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glEnable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + + glBindTexture(GL_TEXTURE_2D, + minecraft->textures->loadTexture(TN_TITLE_BG_PANORAMA)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + float off = vo * 0.0001f; + + float screenAspect = (float)width / (float)height; + float texAspect = 1748.0f / 144.0f; + float scale; + if (screenAspect > texAspect) { + scale = (float)width / 1748.0f; + } else { + scale = (float)height / 144.0f; + } + + float texWidth = 1748.0f * scale; + float texHeight = 144.0f * scale; + float yOff = (height - texHeight) / 2.0f; + + float uMax = off + (texWidth / 1748.0f); + + t->begin(GL_QUADS); + t->color(0xffffff, 255); + t->vertexUV(0, yOff + texHeight, 0, off, 1.0f); + t->vertexUV(texWidth, yOff + texHeight, 0, uMax, 1.0f); + t->vertexUV(texWidth, yOff, 0, uMax, 0.0f); + t->vertexUV(0, yOff, 0, off, 0.0f); + t->end(); + + glDepthMask(true); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +} + void TitleScreen::render(int xm, int ym, float a) { // 4J Unused - Iggy Flash UI renders the title screen on consoles #ifdef ENABLE_JAVA_GUIS - renderBackground(); + renderPanorama(); Tesselator* t = Tesselator::getInstance(); int logoWidth = 155 + 119; @@ -155,7 +248,9 @@ void TitleScreen::render(int xm, int ym, float a) { drawCenteredString(font, splash, 0, -8, 0xffff00); glPopMatrix(); - drawString(font, ClientConstants::VERSION_STRING, 2, 2, 0x505050); + drawString( + font, ClientConstants::VERSION_STRING, 2, height - 10, + 0xffffff); // 4jcraft: use the same height as the copyright message wstring msg = L"Copyright Mojang AB. Do not distribute."; drawString(font, msg, width - font->width(msg) - 2, height - 10, 0xffffff); diff --git a/Minecraft.Client/UI/Screens/TitleScreen.h b/Minecraft.Client/UI/Screens/TitleScreen.h index 6a27ae427..f729cea2e 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.h +++ b/Minecraft.Client/UI/Screens/TitleScreen.h @@ -12,6 +12,22 @@ private: std::wstring splash; Button* multiplayerButton; + // 4jcraft: panorama + void renderPanorama(); + + // 4jcraft: taken from UIScene_MainMenu + // 4J Added + enum eSplashIndexes { + eSplashHappyBirthdayEx = 0, + eSplashHappyBirthdayNotch, + eSplashMerryXmas, + eSplashHappyNewYear, + + // The start index in the splashes vector from which we can select a + // random splash + eSplashRandomStart, + }; + public: TitleScreen(); virtual void tick(); From 65ff6a97f0427c0d5aba6aa471635fd51b0166b7 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Wed, 25 Mar 2026 20:01:59 +0300 Subject: [PATCH 03/40] feat(jui): add horse inventory screen --- .../Common/res/1_2_2/gui/horse.png | Bin 0 -> 5654 bytes Minecraft.Client/Player/LocalPlayer.cpp | 9 +- Minecraft.Client/Textures/Textures.cpp | 1 + Minecraft.Client/Textures/Textures.h | 1 + .../UI/Screens/HorseInventoryScreen.cpp | 100 ++++++++++++++++++ .../UI/Screens/HorseInventoryScreen.h | 22 ++++ 6 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/horse.png create mode 100644 Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/HorseInventoryScreen.h diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/horse.png b/Minecraft.Assets/Common/res/1_2_2/gui/horse.png new file mode 100644 index 0000000000000000000000000000000000000000..194cc6bf6fcc7baee87038d57cb51f2254c640a8 GIT binary patch literal 5654 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 container) { bool LocalPlayer::openHorseInventory(std::shared_ptr horse, std::shared_ptr container) { - // minecraft->setScreen(new HorseInventoryScreen(inventory, container, - // horse)); +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new HorseInventoryScreen(inventory, container, + horse)); + bool success = true; +#else bool success = app.LoadHorseMenu(GetXboxPad(), inventory, container, horse); if (success) ui.PlayUISFX(eSFX_Press); +#endif return success; } diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 6314e33ff..8d879aac8 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -177,6 +177,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { // 4jcraft: 1.6.4 java UI #ifdef ENABLE_JAVA_GUIS + L"gui/horse", L"title/bg/panorama", #endif // L"item/christmas", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 0ef8f6729..0bf5e42d5 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -159,6 +159,7 @@ typedef enum _TEXTURE_NAME { // 4jcraft: 1.6.4 java UI #ifdef ENABLE_JAVA_GUIS + TN_GUI_HORSE, TN_TITLE_BG_PANORAMA, #endif // TN_TILE_XMAS_CHEST, diff --git a/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp b/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp new file mode 100644 index 000000000..ffdfaaeb9 --- /dev/null +++ b/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp @@ -0,0 +1,100 @@ +#include "../../Platform/stdafx.h" +#include "HorseInventoryScreen.h" +#include "../../Player/MultiPlayerLocalPlayer.h" +#include "../../Rendering/EntityRenderers/EntityRenderDispatcher.h" +#include "../../Rendering/Lighting.h" +#include "../../Textures/Textures.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../../Minecraft.World/Containers/HorseInventoryMenu.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing InventoryScreen + +ResourceLocation GUI_HORSE_LOCATION = ResourceLocation(TN_GUI_HORSE); + +HorseInventoryScreen::HorseInventoryScreen( + std::shared_ptr inventory, + std::shared_ptr horseContainer, + std::shared_ptr horse) + : AbstractContainerScreen( + new HorseInventoryMenu(inventory, horseContainer, horse)) { + xMouse = yMouse = 0.0f; // 4J added + + this->inventory = inventory; + this->horseContainer = horseContainer; + this->horse = horse; + this->passEvents = false; +} + +void HorseInventoryScreen::init() { AbstractContainerScreen::init(); } + +void HorseInventoryScreen::renderLabels() { + font->draw(horseContainer->getName(), 8, 6, 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); +} + +void HorseInventoryScreen::render(int xm, int ym, float a) { + AbstractContainerScreen::render(xm, ym, a); + this->xMouse = (float)xm; + this->yMouse = (float)ym; +} + +void HorseInventoryScreen::renderBg(float a) { + // 4J Unused +#ifdef ENABLE_JAVA_GUIS + glColor4f(1, 1, 1, 1); + minecraft->textures->bindTexture(&GUI_HORSE_LOCATION); + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + blit(xo, yo, 0, 0, imageWidth, imageHeight); + + if (horse->isChestedHorse()) { + blit(xo + 79, yo + 17, 0, imageHeight, 90, 54); + } + + if (horse->canWearArmor()) { + blit(xo + 7, yo + 35, 0, imageHeight + 54, 18, 18); + } + + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_COLOR_MATERIAL); + + glPushMatrix(); + glTranslatef((float)xo + 51, (float)yo + 60, 50); + float ss = 30; + glScalef(-ss, ss, ss); + glRotatef(180, 0, 0, 1); + + float oybr = horse->yBodyRot; + float oyr = horse->yRot; + float oxr = horse->xRot; + float oyh = horse->yHeadRot; + float oyhp = horse->yHeadRotO; + + float xd = (xo + 51) - xMouse; + float yd = (yo + 75 - 50) - yMouse; + + glRotatef(45 + 90, 0, 1, 0); + Lighting::turnOn(); + glRotatef(-45 - 90, 0, 1, 0); + + glRotatef(-(float)atan(yd / 40.0f) * 20, 1, 0, 0); + + horse->yBodyRot = (float)atan(xd / 40.0f) * 20; + horse->yRot = (float)atan(xd / 40.0f) * 40; + horse->xRot = -(float)atan(yd / 40.0f) * 20; + horse->yHeadRot = (float)atan(xd / 40.0f) * 40; + horse->yHeadRotO = (float)atan(xd / 40.0f) * 40; + glTranslatef(0, horse->heightOffset, 0); + EntityRenderDispatcher::instance->playerRotY = 180; + EntityRenderDispatcher::instance->render(horse, 0, 0, 0, 0, 1); + horse->yBodyRot = oybr; + horse->yRot = oyr; + horse->xRot = oxr; + horse->yHeadRot = oyh; + horse->yHeadRotO = oyhp; + glPopMatrix(); + Lighting::turnOff(); + glDisable(GL_RESCALE_NORMAL); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/HorseInventoryScreen.h b/Minecraft.Client/UI/Screens/HorseInventoryScreen.h new file mode 100644 index 000000000..12b0e68a2 --- /dev/null +++ b/Minecraft.Client/UI/Screens/HorseInventoryScreen.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include "AbstractContainerScreen.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.entity.animal.h" + +class HorseInventoryScreen : public AbstractContainerScreen { +public: + HorseInventoryScreen(std::shared_ptr inventory, + std::shared_ptr horseContainer, + std::shared_ptr horse); + + virtual void init() override; + virtual void renderLabels() override; + virtual void renderBg(float a) override; + virtual void render(int xm, int ym, float a) override; + +private: + std::shared_ptr inventory; + std::shared_ptr horseContainer; + std::shared_ptr horse; + float xMouse, yMouse; +}; \ No newline at end of file From 08a87c432a4de1db62251f01c374f378a63ccde6 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Wed, 25 Mar 2026 23:38:09 +0300 Subject: [PATCH 04/40] fix: put jui-specific texture usage behind ifdefs --- Minecraft.Client/Textures/Textures.cpp | 26 +++++++++---------- Minecraft.Client/Textures/Textures.h | 26 +++++++++---------- .../UI/Screens/HorseInventoryScreen.cpp | 3 ++- Minecraft.Client/UI/Screens/TitleScreen.cpp | 2 ++ 4 files changed, 30 insertions(+), 27 deletions(-) diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 8d879aac8..f1e08232c 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -33,24 +33,12 @@ C4JRender::eTextureFormat Textures::TEXTURE_FORMAT = int Textures::preLoadedIdx[TN_COUNT]; const wchar_t* Textures::preLoaded[TN_COUNT] = { L"%blur%misc/pumpkinblur", - L"%blur%/misc/vignette", // Not currently used L"%clamp%misc/shadow", - L"/achievement/bg", // Not currently used L"art/kz", L"environment/clouds", L"environment/rain", L"environment/snow", L"gui/gui", - L"gui/background", - L"gui/inventory", - L"gui/container", - L"gui/crafting", - L"gui/furnace", - L"gui/creative_inventory/tabs", - L"gui/creative_inventory/tab_items", - L"gui/creative_inventory/tab_inventory", - L"gui/creative_inventory/tab_item_search", - L"title/mclogo", L"gui/icons", L"item/arrows", L"item/boat", @@ -175,8 +163,20 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"item/trapped", L"item/trapped_double", - // 4jcraft: 1.6.4 java UI + // 4jcraft: java UI specific #ifdef ENABLE_JAVA_GUIS + L"%blur%/misc/vignette", + L"/achievement/bg", + L"gui/background", + L"gui/inventory", + L"gui/container", + L"gui/crafting", + L"gui/furnace", + L"gui/creative_inventory/tabs", + L"gui/creative_inventory/tab_items", + L"gui/creative_inventory/tab_inventory", + L"gui/creative_inventory/tab_item_search", + L"title/mclogo", L"gui/horse", L"title/bg/panorama", #endif diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 0bf5e42d5..ddf2ecf47 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -16,24 +16,12 @@ class ResourceLocation; typedef enum _TEXTURE_NAME { TN__BLUR__MISC_PUMPKINBLUR, - TN__BLUR__MISC_VIGNETTE, // Not currently used TN__CLAMP__MISC_SHADOW, - TN_ACHIEVEMENT_BG, // Not currently used TN_ART_KZ, TN_ENVIRONMENT_CLOUDS, TN_ENVIRONMENT_RAIN, TN_ENVIRONMENT_SNOW, TN_GUI_GUI, - TN_GUI_BACKGROUND, - TN_GUI_INVENTORY, - TN_GUI_CONTAINER, - TN_GUI_CRAFTING, - TN_GUI_FURNACE, - TN_GUI_CREATIVE_TABS, - TN_GUI_CREATIVE_TAB_ITEMS, - TN_GUI_CREATIVE_TAB_INVENTORY, - TN_GUI_CREATIVE_TAB_ITEM_SEARCH, - TN_TITLE_MCLOGO, TN_GUI_ICONS, TN_ITEM_ARROWS, TN_ITEM_BOAT, @@ -157,8 +145,20 @@ typedef enum _TEXTURE_NAME { TN_TILE_TRAP_CHEST, TN_TILE_LARGE_TRAP_CHEST, - // 4jcraft: 1.6.4 java UI + // 4jcraft: java UI specific #ifdef ENABLE_JAVA_GUIS + TN__BLUR__MISC_VIGNETTE, + TN_ACHIEVEMENT_BG, + TN_GUI_BACKGROUND, + TN_GUI_INVENTORY, + TN_GUI_CONTAINER, + TN_GUI_CRAFTING, + TN_GUI_FURNACE, + TN_GUI_CREATIVE_TABS, + TN_GUI_CREATIVE_TAB_ITEMS, + TN_GUI_CREATIVE_TAB_INVENTORY, + TN_GUI_CREATIVE_TAB_ITEM_SEARCH, + TN_TITLE_MCLOGO, TN_GUI_HORSE, TN_TITLE_BG_PANORAMA, #endif diff --git a/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp b/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp index ffdfaaeb9..1ce2e2fe1 100644 --- a/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp +++ b/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp @@ -8,8 +8,9 @@ #include "../../../Minecraft.World/Containers/HorseInventoryMenu.h" // 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing InventoryScreen - +#ifdef ENABLE_JAVA_GUIS ResourceLocation GUI_HORSE_LOCATION = ResourceLocation(TN_GUI_HORSE); +#endif HorseInventoryScreen::HorseInventoryScreen( std::shared_ptr inventory, diff --git a/Minecraft.Client/UI/Screens/TitleScreen.cpp b/Minecraft.Client/UI/Screens/TitleScreen.cpp index 78e292a58..600ee8199 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.cpp +++ b/Minecraft.Client/UI/Screens/TitleScreen.cpp @@ -158,6 +158,7 @@ void TitleScreen::buttonClicked(Button* button) { // uses the TU panorama instead of JE panorama and as such a different rendering // method void TitleScreen::renderPanorama() { +#ifdef ENABLE_JAVA_GUIS Tesselator* t = Tesselator::getInstance(); glMatrixMode(GL_PROJECTION); @@ -217,6 +218,7 @@ void TitleScreen::renderPanorama() { glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); +#endif } void TitleScreen::render(int xm, int ym, float a) { From 7a0ebe7e36057adc74d531c527cf40421bfd07c7 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Wed, 25 Mar 2026 23:41:45 +0300 Subject: [PATCH 05/40] fix(jui): remove debug world autocreate from CreateWorldScreen --- Minecraft.Client/UI/Screens/CreateWorldScreen.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp b/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp index d38f3d067..8ca1690e9 100644 --- a/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp +++ b/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp @@ -30,8 +30,8 @@ void CreateWorldScreen::tick() { if (moreOptions) seedEdit->tick(); // 4J - debug code - to be removed - static int count = 0; - if (count++ == 100) buttonClicked(buttons[0]); + // static int count = 0; + // if (count++ == 100) buttonClicked(buttons[0]); } void CreateWorldScreen::init() { From 11e944f78b8d2317aba90e1055197246d83f31d8 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 00:15:27 +0300 Subject: [PATCH 06/40] fix(jui): remove debug autoconfirm from ConfirmScreen --- Minecraft.Client/UI/Screens/ConfirmScreen.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Minecraft.Client/UI/Screens/ConfirmScreen.cpp b/Minecraft.Client/UI/Screens/ConfirmScreen.cpp index 16f4ffe9d..17133b288 100644 --- a/Minecraft.Client/UI/Screens/ConfirmScreen.cpp +++ b/Minecraft.Client/UI/Screens/ConfirmScreen.cpp @@ -47,9 +47,9 @@ void ConfirmScreen::render(int xm, int ym, float a) { Screen::render(xm, ym, a); // 4J - debug code - remove - static int count = 0; - if (count++ == 100) { - count = 0; - buttonClicked(buttons[0]); - } + // static int count = 0; + // if (count++ == 100) { + // count = 0; + // buttonClicked(buttons[0]); + // } } \ No newline at end of file From 5f5e7e7f99fb87daf77524495825e5d988c62a3b Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 03:10:10 +0300 Subject: [PATCH 07/40] feat(jui): add item switch tooltips --- Minecraft.Client/UI/Gui.cpp | 50 +++++++++++++++++++++++++++++++++++++ Minecraft.Client/UI/Gui.h | 4 +++ 2 files changed, 54 insertions(+) diff --git a/Minecraft.Client/UI/Gui.cpp b/Minecraft.Client/UI/Gui.cpp index 8d9d12071..728277092 100644 --- a/Minecraft.Client/UI/Gui.cpp +++ b/Minecraft.Client/UI/Gui.cpp @@ -56,6 +56,10 @@ Gui::Gui(Minecraft* minecraft) { tbr = 1.0f; fAlphaIncrementPerCent = 255.0f / 100.0f; + // 4jcraft: backported item switch tooltip display from 1.6.4 + remainingHighlightTicks = 0; + highlightingItemStack = nullptr; + this->minecraft = minecraft; lastTickA = 0.0f; @@ -816,6 +820,31 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) { } #if RENDER_HUD + // 4jcraft: backported item switch tooltip display from 1.6.4 + if (remainingHighlightTicks > 0 && highlightingItemStack != nullptr) { + std::wstring displayName = highlightingItemStack->getHoverName(); + int x = (screenWidth - font->width(displayName)) / 2; + int y = screenHeight - 89; + + if (!minecraft->gameMode->canHurtPlayer()) { + y += 14; + } + + int alpha = (int)((float)remainingHighlightTicks * 256.0f / 10.0f); + if (alpha > 255) alpha = 255; + if (alpha > 0) { + glPushMatrix(); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + int color = 0xFFFFFF | (alpha << 24); + font->drawShadow(displayName, x, y, color); + + glDisable(GL_BLEND); + glPopMatrix(); + } + } + // Moved so the opacity blend is applied to it if (bDisplayGui && minecraft->gameMode->hasExperience() && minecraft->player->experienceLevel > 0) { @@ -1334,6 +1363,27 @@ void Gui::tick() { if (overlayMessageTime > 0) overlayMessageTime--; tickCount++; + // 4jcraft: backported item switch tooltip display from 1.6.4 + if (minecraft->player != nullptr) { + std::shared_ptr currentItem = + minecraft->player->inventory->getSelected(); + + if (currentItem == nullptr) { + remainingHighlightTicks = 0; + } else if (highlightingItemStack != nullptr && + currentItem->id == highlightingItemStack->id && + currentItem->sameItemWithTags(highlightingItemStack) && + (currentItem->isDamageableItem() || + currentItem->getDamageValue() == + highlightingItemStack->getDamageValue())) { + if (remainingHighlightTicks > 0) --remainingHighlightTicks; + } else { + remainingHighlightTicks = 40; + } + + highlightingItemStack = currentItem; + } + for (int iPad = 0; iPad < XUSER_MAX_COUNT; iPad++) { // 4J Stu - Fix for #10929 - MP LAB: Network Disconnects: Host does not // receive an error message stating the client left the game when diff --git a/Minecraft.Client/UI/Gui.h b/Minecraft.Client/UI/Gui.h index 81ee2b5f8..2ced840c3 100644 --- a/Minecraft.Client/UI/Gui.h +++ b/Minecraft.Client/UI/Gui.h @@ -34,6 +34,10 @@ private: float lastTickA; float fAlphaIncrementPerCent; + // 4jcraft: backported item switch tooltip display from 1.6.4 + int remainingHighlightTicks; + std::shared_ptr highlightingItemStack; + public: static float currentGuiBlendFactor; // 4J added static float currentGuiScaleFactor; // 4J added From 11f71c12b2368187b29b325372f6244ce873dc9f Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 03:20:32 +0300 Subject: [PATCH 08/40] chore: fmt --- Minecraft.Client/Player/LocalPlayer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Minecraft.Client/Player/LocalPlayer.cpp b/Minecraft.Client/Player/LocalPlayer.cpp index de9cfe1c7..1e029e8ca 100644 --- a/Minecraft.Client/Player/LocalPlayer.cpp +++ b/Minecraft.Client/Player/LocalPlayer.cpp @@ -601,8 +601,7 @@ bool LocalPlayer::openHopper(std::shared_ptr container) { bool LocalPlayer::openHorseInventory(std::shared_ptr horse, std::shared_ptr container) { #ifdef ENABLE_JAVA_GUIS - minecraft->setScreen(new HorseInventoryScreen(inventory, container, - horse)); + minecraft->setScreen(new HorseInventoryScreen(inventory, container, horse)); bool success = true; #else bool success = app.LoadHorseMenu(GetXboxPad(), inventory, container, horse); From 2131c12de7a9a7f00d754014e8466302cded69c1 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 05:27:50 +0300 Subject: [PATCH 09/40] feat(jui): make edit box background be able to get disabled --- Minecraft.Client/UI/EditBox.cpp | 33 +++++++++++++++++++++++++++------ Minecraft.Client/UI/EditBox.h | 5 +++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/Minecraft.Client/UI/EditBox.cpp b/Minecraft.Client/UI/EditBox.cpp index 2ce3d3224..24b564e41 100644 --- a/Minecraft.Client/UI/EditBox.cpp +++ b/Minecraft.Client/UI/EditBox.cpp @@ -7,6 +7,9 @@ EditBox::EditBox(Screen* screen, Font* font, int x, int y, int width, // 4J - added initialisers maxLength = 0; frame = 0; + enableBackgroundDrawing = + true; // 4jcraft: for toggling the background rendering (from 1.6.4, + // mainly for RepairScreen) this->screen = screen; this->font = font; @@ -68,18 +71,36 @@ void EditBox::focus(bool newFocus) { } void EditBox::render() { - fill(x - 1, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0); - fill(x, y, x + width, y + height, 0xff000000); + // 4jcraft: render the background conditionally + if (enableBackgroundDrawing) { + fill(x - 1, y - 1, x + width + 1, y + height + 1, 0xffa0a0a0); + fill(x, y, x + width, y + height, 0xff000000); + } + + // 4jcraft: offset conditionally + int textX = x; + int textY = y; + if (enableBackgroundDrawing) { + textX += 4; + textY += (height - 8) / 2; + } if (active) { bool renderUnderscore = inFocus && (frame / 6 % 2 == 0); - drawString(font, value + (renderUnderscore ? L"_" : L""), x + 4, - y + (height - 8) / 2, 0xe0e0e0); + drawString(font, value + (renderUnderscore ? L"_" : L""), textX, textY, + 0xe0e0e0); } else { - drawString(font, value, x + 4, y + (height - 8) / 2, 0x707070); + drawString(font, value, textX, textY, + (enableBackgroundDrawing ? 0xe0e0e0 : 0xffffff)); } } void EditBox::setMaxLength(int maxLength) { this->maxLength = maxLength; } -int EditBox::getMaxLength() { return maxLength; } \ No newline at end of file +int EditBox::getMaxLength() { return maxLength; } + +// 4jcraft: for toggling the background rendering (from 1.6.4, mainly for +// RepairScreen) +void EditBox::setEnableBackgroundDrawing(bool enable) { + enableBackgroundDrawing = enable; +} \ No newline at end of file diff --git a/Minecraft.Client/UI/EditBox.h b/Minecraft.Client/UI/EditBox.h index 1b78d424f..133bfc0f8 100644 --- a/Minecraft.Client/UI/EditBox.h +++ b/Minecraft.Client/UI/EditBox.h @@ -18,6 +18,8 @@ private: public: bool inFocus; bool active; + bool enableBackgroundDrawing; // 4jcraft: for toggling the background + // rendering (mainly for RepairScreen) private: Screen* screen; @@ -34,4 +36,7 @@ public: void render(); void setMaxLength(int maxLength); int getMaxLength(); + + // 4jcraft: for toggling the background rendering (mainly for RepairScreen) + void setEnableBackgroundDrawing(bool enable); }; \ No newline at end of file From a9a3cbb0f5bd89026a9a46fc95e8df89962d2d59 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 06:09:38 +0300 Subject: [PATCH 10/40] fix(jui): disable isConstantBlended for hotbar slot rendering Fixes the issue where in the java UI hotbar, enchant glints make the item appear as a black texture --- Minecraft.Client/UI/Gui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/UI/Gui.cpp b/Minecraft.Client/UI/Gui.cpp index 728277092..dea90b73c 100644 --- a/Minecraft.Client/UI/Gui.cpp +++ b/Minecraft.Client/UI/Gui.cpp @@ -1349,7 +1349,8 @@ void Gui::renderSlot(int slot, int x, int y, float a) { } itemRenderer->renderAndDecorateItem(minecraft->font, minecraft->textures, - item, x, y); + item, x, y, 1.0f, 1.0f, 1.0f, + item->isFoil(), false); if (pop > 0) { glPopMatrix(); From d18733b14c6fa4114756c1f3fd74aa9b29bc535d Mon Sep 17 00:00:00 2001 From: StevenSYS <139715581+StevenSYS@users.noreply.github.com> Date: Thu, 26 Mar 2026 06:28:39 +0000 Subject: [PATCH 11/40] Ported over the Java Edition's title screen panorama. --- Minecraft.Client/Minecraft.cpp | 6 +- Minecraft.Client/Textures/Texture.cpp | 12 +- Minecraft.Client/UI/Screens/TitleScreen.cpp | 180 +++++++++++++++----- Minecraft.Client/UI/Screens/TitleScreen.h | 7 +- 4 files changed, 154 insertions(+), 51 deletions(-) diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index bb2610813..ee7ac2623 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1967,7 +1967,11 @@ void Minecraft::run_middle() { player->ullButtonsPressed = 0LL; } else if (screen != NULL) { screen->updateEvents(); - screen->tick(); + // 4jcraft: this fixes the title screen panorama running + // faster than it should + if (!idx) { + screen->tick(); + } } } diff --git a/Minecraft.Client/Textures/Texture.cpp b/Minecraft.Client/Textures/Texture.cpp index 5cd1fbbdd..5d54d9a09 100644 --- a/Minecraft.Client/Textures/Texture.cpp +++ b/Minecraft.Client/Textures/Texture.cpp @@ -471,10 +471,10 @@ void Texture::blit(int x, int y, Texture* source, bool rotated) { } void Texture::transferFromBuffer(intArray buffer) { - //if (depth == 1) { - // return; - //} - // 4jcraft - move pos out of loops + // if (depth == 1) { + // return; + // } + // 4jcraft - move pos out of loops data[0]->clear(); // #ifdef __PS3__ // int byteRemapRGBA[] = { 3, 0, 1, 2 }; @@ -487,7 +487,7 @@ void Texture::transferFromBuffer(intArray buffer) { int totalPixels = width * height * depth; - for (int i = 0; i < totalPixels; i++){ + for (int i = 0; i < totalPixels; i++) { int pixel = buffer[i]; int offset = i * 4; @@ -501,7 +501,7 @@ void Texture::transferFromBuffer(intArray buffer) { updateOnGPU(); } - + /* for (int z = 0; z < depth; z++) { int plane = z * height * width * 4; for (int y = 0; y < height; y++) { diff --git a/Minecraft.Client/UI/Screens/TitleScreen.cpp b/Minecraft.Client/UI/Screens/TitleScreen.cpp index 600ee8199..dcf055456 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.cpp +++ b/Minecraft.Client/UI/Screens/TitleScreen.cpp @@ -17,7 +17,8 @@ Random* TitleScreen::random = new Random(); TitleScreen::TitleScreen() { // 4J - added initialisers - vo = 0; + // vo = 0; // 4jcraft removed + panoramaTimer = 0.0f; multiplayerButton = NULL; splash = L"missingno"; @@ -66,10 +67,18 @@ TitleScreen::TitleScreen() { } splash = splashes.at(splashIndex); + + titlePanoramaPaths[0] = L"title/bg/panorama0.png"; + titlePanoramaPaths[1] = L"title/bg/panorama1.png"; + titlePanoramaPaths[2] = L"title/bg/panorama2.png"; + titlePanoramaPaths[3] = L"title/bg/panorama3.png"; + titlePanoramaPaths[4] = L"title/bg/panorama4.png"; + titlePanoramaPaths[5] = L"title/bg/panorama5.png"; } void TitleScreen::tick() { - vo += 1.0f; + panoramaTimer += 1.0f; + // vo += 1.0f; // 4jcraft removed // if( vo > 100.0f ) minecraft->setScreen(new SelectWorldScreen(this)); // // 4J - temp testing } @@ -78,6 +87,10 @@ void TitleScreen::keyPressed(wchar_t eventCharacter, int eventKey) {} void TitleScreen::init() { app.DebugPrintf("TitleScreen::init() START\n"); + + // 4jcraft: this is for the blured panorama background + viewportTexture = + minecraft->textures->getTexture(new BufferedImage(256, 256, 2)); /* 4J - removed Calendar c = Calendar.getInstance(); c.setTime(new Date()); @@ -157,80 +170,161 @@ void TitleScreen::buttonClicked(Button* button) { // 4jcraft: render our panorama // uses the TU panorama instead of JE panorama and as such a different rendering // method -void TitleScreen::renderPanorama() { +void TitleScreen::renderPanorama(float a) { #ifdef ENABLE_JAVA_GUIS Tesselator* t = Tesselator::getInstance(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); - glOrtho(0, width, height, 0, 1000, 3000); + gluPerspective(120.0f, 1.0f, 0.05f, 10.0f); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); - glTranslatef(0, 0, -2000); - - glDisable(GL_LIGHTING); - glDisable(GL_FOG); - glEnable(GL_TEXTURE_2D); - glDisable(GL_ALPHA_TEST); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glRotatef(180.0f, 1.0f, 0.0f, 0.0f); glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_ALPHA_TEST); + glDisable(GL_CULL_FACE); glDepthMask(false); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + char offsetPasses = 8; - glBindTexture(GL_TEXTURE_2D, - minecraft->textures->loadTexture(TN_TITLE_BG_PANORAMA)); + for (int i = 0; i < (offsetPasses * offsetPasses); i++) { + glPushMatrix(); + float x = + ((float)(i % offsetPasses) / (float)offsetPasses - 0.5f) / 64.0f; + float y = + ((float)(i / offsetPasses) / (float)offsetPasses - 0.5f) / 64.0f; + float z = 0.0f; + glTranslatef(x, y, z); + glRotatef(sin((panoramaTimer + a) / 400.0f) * 25.0f + 20.0f, 1.0f, 0.0f, + 0.0f); + glRotatef(-(panoramaTimer + a) * 0.1f, 0.0f, 1.0f, 0.0f); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + for (int j = 0; j < 6; j++) { + glPushMatrix(); - float off = vo * 0.0001f; + switch (j) { + case 1: + glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + break; + case 2: + glRotatef(180.0f, 0.0f, 1.0f, 0.0f); + break; + case 3: + glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + break; + case 4: + glRotatef(90.0f, 1.0f, 0.0f, 0.0f); + break; + case 5: + glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + break; + default: + break; + } - float screenAspect = (float)width / (float)height; - float texAspect = 1748.0f / 144.0f; - float scale; - if (screenAspect > texAspect) { - scale = (float)width / 1748.0f; - } else { - scale = (float)height / 144.0f; + minecraft->textures->bindTexture(titlePanoramaPaths[j]); + t->begin(); + t->color(16777215, 255 / (i + 1)); + t->vertexUV(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f); + t->vertexUV(1.0f, -1.0f, 1.0f, 1.0f, 0.0f); + t->vertexUV(1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + t->vertexUV(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f); + t->end(); + glPopMatrix(); + } + glPopMatrix(); + glColorMask(true, true, true, false); } - float texWidth = 1748.0f * scale; - float texHeight = 144.0f * scale; - float yOff = (height - texHeight) / 2.0f; - - float uMax = off + (texWidth / 1748.0f); - - t->begin(GL_QUADS); - t->color(0xffffff, 255); - t->vertexUV(0, yOff + texHeight, 0, off, 1.0f); - t->vertexUV(texWidth, yOff + texHeight, 0, uMax, 1.0f); - t->vertexUV(texWidth, yOff, 0, uMax, 0.0f); - t->vertexUV(0, yOff, 0, off, 0.0f); - t->end(); - - glDepthMask(true); - glDisable(GL_BLEND); - glEnable(GL_ALPHA_TEST); + t->offset(0.0f, 0.0f, 0.0f); + glColorMask(true, true, true, true); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); + glDepthMask(true); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); +#endif +} + +// 4jcraft +void TitleScreen::renderSkybox(float a) { + glViewport(0, 0, 256, 256); + renderPanorama(a); + glDisable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_2D); + + for (int i = 0; i < 8; i++) { + rotateAndBlur(a); + } + + glViewport(0, 0, minecraft->width, minecraft->height); + + Tesselator* t = Tesselator::getInstance(); + t->begin(); + float aspect = + width > height ? 120.0f / (float)width : 120.0f / (float)height; + float sWidth = (float)height * aspect / 256.0f; + float sHeight = (float)width * aspect / 256.0f; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + t->color(1.0f, 1.0f, 1.0f, 1.0f); + t->vertexUV(0.0f, height, 0.0f, (0.5f - sWidth), (0.5f + sHeight)); + t->vertexUV(width, height, 0.0f, (0.5f - sWidth), (0.5f - sHeight)); + t->vertexUV(width, 0.0f, 0.0f, (0.5f + sWidth), (0.5f - sHeight)); + t->vertexUV(0.0f, 0.0f, 0.0f, (0.5f + sWidth), (0.5f + sHeight)); + t->end(); + return; +} + +// 4jcraft +void TitleScreen::rotateAndBlur(float a) { +#ifdef ENABLE_JAVA_GUIS + glBindTexture(GL_TEXTURE_2D, viewportTexture); + // this.mc.renderEngine.resetBoundTexture(); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 256, 256); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(true, true, true, false); + Tesselator* t = Tesselator::getInstance(); + t->begin(); + char blurPasses = 3; + + for (int i = 0; i < blurPasses; i++) { + t->color(1.0f, 1.0f, 1.0f, 1.0f / (float)(i + 1)); + float offset = (float)(i - blurPasses / 2) / 256.0f; + t->vertexUV(width, height, 0.0f, (0.0f + offset), 0.0f); + t->vertexUV(width, 0.0f, 0.0f, (1.0f + offset), 0.0f); + t->vertexUV(0.0f, 0.0f, 0.0f, (1.0f + offset), 1.0f); + t->vertexUV(0.0f, height, 0.0f, (0.0f + offset), 1.0f); + } + + t->end(); + glColorMask(true, true, true, true); + // this.mc.renderEngine.resetBoundTexture(); #endif } void TitleScreen::render(int xm, int ym, float a) { // 4J Unused - Iggy Flash UI renders the title screen on consoles #ifdef ENABLE_JAVA_GUIS - renderPanorama(); + // 4jcraft: panorama + renderSkybox(a); + Tesselator* t = Tesselator::getInstance(); int logoWidth = 155 + 119; int logoX = width / 2 - logoWidth / 2; int logoY = 30; + // 4jcraft: gradient + fillGradient(0, 0, width, height, -2130706433, 16777215); + fillGradient(0, 0, width, height, 0, INT_MIN); + glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture(TN_TITLE_MCLOGO)); glColor4f(1, 1, 1, 1); diff --git a/Minecraft.Client/UI/Screens/TitleScreen.h b/Minecraft.Client/UI/Screens/TitleScreen.h index f729cea2e..9bb7ffdca 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.h +++ b/Minecraft.Client/UI/Screens/TitleScreen.h @@ -13,7 +13,12 @@ private: Button* multiplayerButton; // 4jcraft: panorama - void renderPanorama(); + void renderPanorama(float a); + void renderSkybox(float a); + void rotateAndBlur(float a); + int viewportTexture; + float panoramaTimer; + std::wstring titlePanoramaPaths[6]; // 4jcraft: taken from UIScene_MainMenu // 4J Added From 9a2062c70a4086bf3f1dc30b782494d87427dbd6 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 15:48:52 +0300 Subject: [PATCH 12/40] fix(jui): always use white if background is disabled in EditBox --- Minecraft.Client/UI/EditBox.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/UI/EditBox.cpp b/Minecraft.Client/UI/EditBox.cpp index 24b564e41..da48457e7 100644 --- a/Minecraft.Client/UI/EditBox.cpp +++ b/Minecraft.Client/UI/EditBox.cpp @@ -88,7 +88,7 @@ void EditBox::render() { if (active) { bool renderUnderscore = inFocus && (frame / 6 % 2 == 0); drawString(font, value + (renderUnderscore ? L"_" : L""), textX, textY, - 0xe0e0e0); + (enableBackgroundDrawing ? 0xe0e0e0 : 0xffffff)); } else { drawString(font, value, textX, textY, (enableBackgroundDrawing ? 0xe0e0e0 : 0xffffff)); From fbbf086f71d4e0e81b3d91f4082e7b5b80b77172 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 16:51:20 +0300 Subject: [PATCH 13/40] feat(jui): add anvil screen --- .../Common/res/1_2_2/gui/anvil.png | Bin 0 -> 1900 bytes Minecraft.Assets/Common/res/lang/en_US.lang | 3 + Minecraft.Client/Player/LocalPlayer.cpp | 4 +- Minecraft.Client/Textures/Textures.cpp | 3 +- Minecraft.Client/Textures/Textures.h | 3 +- Minecraft.Client/UI/Screens/RepairScreen.cpp | 191 ++++++++++++++++++ Minecraft.Client/UI/Screens/RepairScreen.h | 39 ++++ 7 files changed, 239 insertions(+), 4 deletions(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/anvil.png create mode 100644 Minecraft.Client/UI/Screens/RepairScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/RepairScreen.h diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/anvil.png b/Minecraft.Assets/Common/res/1_2_2/gui/anvil.png new file mode 100644 index 0000000000000000000000000000000000000000..34e8f9f9f018814ce05dbe56d186f49e866f77df GIT binary patch literal 1900 zcma)52~d+q6y84u5~83~v4{vtG1vhGkI~3sLW{zWHJ}Ixh=~_sQ4%O4ks}G9)wYV7 z7->NTMT=r25mAvVKgEiO<2!B?_wC!ayWe}?d*1|7 zea#6r1OR|J?B}x$00dXuo`^R31$VHcde80GTVJ6mwYK>>!sRmOu)`dz>6 zy?skj&oSs}xV_#AH|@dfVwbmVG~>BechP$5&EZf5lgaG7A#UE3zcp4~CKzO;yb@`Z z@?AWoj32LN@#`EtJUk9ec(s>c+_Y`}p(|)fD{sD;vMAbF_2Ov+kh-^CKEFO?bW-3j zFwtH9FkZXOBG-%Y>#sBror>*sHg?^8qwO87`GPnX+98n3<^5{4dr;VBY?PC6cr^rZ zmuDg{Uvk*;cVXYtTt}Tw2lH~)F^I5QA<-P_>7ZUJd0_u~z&JMXmS`Mip=d&Y*u1Jt zKk&0Z{iTvo?@zz(%x!b#sG9SAVV*~#+7YhOHjWLiGxN#eOCPVxVi&> zNJq%)E}=(UTpL!9rALvw8nx$9t&$c7sAWT~MY}4K5`LB>y23-9)ftX;8n%|oQ&!Sa zI{RODL^*MI4=tl>lw-5FAyk8GxO|3)T-9DyCn>g~=o2Jt8BOuFHPj$Gy>MM-q}gQu zrF_L$&g{`k%`W?g0(|L7OFQ}8CSDyahn?6NEb*Y5h!Ouhwv5xVI*lKKVa`9GJ;bT} zD0-m=BNj9ipBHJg?e`~X5ujIqoDoURIcjU3y9Pv>J!4Z5C(~*|=d-fP%b^u%7EfEe zr9T4Cacuz4kvvhI$a7S1-nPR$Ns2+Vmy@cGP19Hm+`GYOBVM}oIueL6J5&PWFB-Np zJk)+h;x{y;*B{ND%k059gFF}wX0l-_p4S@9N{2G6K+`oVq$1hCf5uKk1olF}B?`f= zk;@#rP_`HYvc(vZ#*0ih0c+w+#KL4YLIBY%1jLJ6LIPX5l6#mCbYo1#X&l5hPSq6_ z%mI|Y1p4xjQ5dPFiwCUrY|d??X7up*g2{`*rCpvv3{~2)2vf@_3aOk)$MzgdQ=F6SPBTQBXC1H zFt&+T18DkeH_HZ{g4GiIQdvtt9l|0?9OS081Sb$>L8UwKSgsXR%G9&THC^In+$}S5 z%_^dCZAC~$wgVg-9L$p5+oi4A9Dxb@Avv!%p z4eRv%D%FBo(i^*4VnGanqQ|)GA=yST7>%UI>&Hl=g$XkO9xMi205BgAFr9PIljRA$ zY%mR!*+qUtdc1qJ6?9K!Q2k*9_H4*aI(EQ!C z3%gz1GZ&?K&Ul}XcUY$dh>52F?)>6U&c>}}+pa+EPu(5tmRd${apq2#>^QcFaq8~x zq$Aj&S^wnp{Wq`xi!4ED4}X$LdE7Q0S4t{S8=V%zY2Dvx= zV8zU)KzVGd|Gx|8&mf22=G&&uMh-l?avVZ)azl{C@?dEo^xw3;SAqX#>qCi4`9OmI c=RK}q!v1>Q*D;u|wqk%bcq7%P=relyA2O*3s{jB1 literal 0 HcmV?d00001 diff --git a/Minecraft.Assets/Common/res/lang/en_US.lang b/Minecraft.Assets/Common/res/lang/en_US.lang index 752a60f39..cc56540f8 100644 --- a/Minecraft.Assets/Common/res/lang/en_US.lang +++ b/Minecraft.Assets/Common/res/lang/en_US.lang @@ -595,6 +595,9 @@ container.crafting=Crafting container.dispenser=Dispenser container.furnace=Furnace container.enchant=Enchant +container.repair=Repair & Name +container.repair.cost=Enchantment Cost: %d +container.repair.expensive=Too Expensive! container.creative=Item Selection container.brewing=Brewing Stand container.chest=Chest diff --git a/Minecraft.Client/Player/LocalPlayer.cpp b/Minecraft.Client/Player/LocalPlayer.cpp index 1e029e8ca..d04c5f1f5 100644 --- a/Minecraft.Client/Player/LocalPlayer.cpp +++ b/Minecraft.Client/Player/LocalPlayer.cpp @@ -1,6 +1,7 @@ #include "../Platform/stdafx.h" #include "LocalPlayer.h" #include "UI/Screens/HorseInventoryScreen.h" +#include "UI/Screens/RepairScreen.h" #include "User.h" #include "../Input/Input.h" #include "../GameState/StatsCounter.h" @@ -644,8 +645,7 @@ bool LocalPlayer::startEnchanting(int x, int y, int z, bool LocalPlayer::startRepairing(int x, int y, int z) { #ifdef ENABLE_JAVA_GUIS - // minecraft.setScreen(new RepairScreen(inventory, level, x, y, z)); - // FUCK YOU 4J FIRST AND FOREMOST + minecraft->setScreen(new RepairScreen(inventory, level, x, y, z)); bool success = true; #else bool success = diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index f1e08232c..39e8cb4c7 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -163,7 +163,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"item/trapped", L"item/trapped_double", - // 4jcraft: java UI specific +// 4jcraft: java UI specific #ifdef ENABLE_JAVA_GUIS L"%blur%/misc/vignette", L"/achievement/bg", @@ -178,6 +178,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"gui/creative_inventory/tab_item_search", L"title/mclogo", L"gui/horse", + L"gui/anvil", L"title/bg/panorama", #endif // L"item/christmas", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index ddf2ecf47..48a392444 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -145,7 +145,7 @@ typedef enum _TEXTURE_NAME { TN_TILE_TRAP_CHEST, TN_TILE_LARGE_TRAP_CHEST, - // 4jcraft: java UI specific +// 4jcraft: java UI specific #ifdef ENABLE_JAVA_GUIS TN__BLUR__MISC_VIGNETTE, TN_ACHIEVEMENT_BG, @@ -160,6 +160,7 @@ typedef enum _TEXTURE_NAME { TN_GUI_CREATIVE_TAB_ITEM_SEARCH, TN_TITLE_MCLOGO, TN_GUI_HORSE, + TN_GUI_ANVIL, TN_TITLE_BG_PANORAMA, #endif // TN_TILE_XMAS_CHEST, diff --git a/Minecraft.Client/UI/Screens/RepairScreen.cpp b/Minecraft.Client/UI/Screens/RepairScreen.cpp new file mode 100644 index 000000000..6fc1e8697 --- /dev/null +++ b/Minecraft.Client/UI/Screens/RepairScreen.cpp @@ -0,0 +1,191 @@ +#include "../../Platform/stdafx.h" +#include "RepairScreen.h" +#include +#include +#include +#include "../EditBox.h" +#include "../../Player/MultiPlayerLocalPlayer.h" +#include "../../Rendering/Lighting.h" +#include "../../Textures/Textures.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../../Minecraft.World/Containers/AnvilMenu.h" +#include "../../../Minecraft.World/Containers/Slot.h" +#include "../../../Minecraft.Client/Network/ClientConnection.h" +#include "../../../Minecraft.Client/Minecraft.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing +// IUIScene_AnvilMenu (from iggy UI) +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_ANVIL_LOCATION = ResourceLocation(TN_GUI_ANVIL); +#endif + +RepairScreen::RepairScreen(std::shared_ptr inventory, Level* level, + int x, int y, int z) + : AbstractContainerScreen( + new AnvilMenu(inventory, level, x, y, z, + Minecraft::GetInstance()->localplayers[0])) { + this->inventory = inventory; + this->level = level; + this->repairMenu = static_cast(menu); + this->passEvents = false; +} + +RepairScreen::~RepairScreen() = default; + +void RepairScreen::init() { + AbstractContainerScreen::init(); + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + editName = new EditBox(this, font, xo + 62, yo + 24, 103, 12, L""); + editName->setMaxLength(40); + editName->setEnableBackgroundDrawing(false); + editName->inFocus = true; + + repairMenu->removeSlotListener(this); + repairMenu->addSlotListener(this); +} + +void RepairScreen::removed() { + AbstractContainerScreen::removed(); + repairMenu->removeSlotListener(this); +} + +void RepairScreen::render(int xm, int ym, float a) { + AbstractContainerScreen::render(xm, ym, a); + glDisable(GL_LIGHTING); + if (editName) { + editName->render(); + } +} + +void RepairScreen::renderLabels() { + std::wstring title = + Language::getInstance()->getElement(L"container.repair"); + font->draw(title, 60, 6, 0x404040); + + if (repairMenu->cost > 0) { + int textColor = 0x80ff20; + bool showCost = true; + std::wstring costString; + + if (repairMenu->cost >= 40 && + !Minecraft::GetInstance()->localplayers[0]->abilities.instabuild) { + costString = Language::getInstance()->getElement( + L"container.repair.expensive"); + textColor = 0xff6060; + } else if (!repairMenu->getSlot(AnvilMenu::RESULT_SLOT)->hasItem()) { + showCost = false; + } else if (!repairMenu->getSlot(AnvilMenu::RESULT_SLOT) + ->mayPickup( + Minecraft::GetInstance()->localplayers[0])) { + textColor = 0xff6060; + } + + if (showCost) { + if (costString.empty()) { + costString = Language::getInstance()->getElement( + L"container.repair.cost", repairMenu->cost); + } + + int shadowColor = -0x00ffffff | ((textColor & 0xfcfcfc) >> 2) | + (textColor & -0x00ffffff); + int costX = imageWidth - 8 - font->width(costString); + int costY = 67; + + // if (this.fontRenderer.getUnicodeFlag()) + // { + // drawRect(i1 - 3, b0 - 2, this.xSize - 7, b0 + 10, -16777216); + // drawRect(i1 - 2, b0 - 1, this.xSize - 8, b0 + 9, -12895429); + // } + // else + // { + font->draw(costString, costX, costY + 1, shadowColor); + font->draw(costString, costX + 1, costY, shadowColor); + font->draw(costString, costX + 1, costY + 1, shadowColor); + font->draw(costString, costX, costY, textColor); + // } + } + } +} + +void RepairScreen::renderBg(float a) { +#ifdef ENABLE_JAVA_GUIS + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + Minecraft::GetInstance()->textures->bindTexture(&GUI_ANVIL_LOCATION); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + blit(xo, yo, 0, 0, imageWidth, imageHeight); + + int texV = imageHeight + (repairMenu->getSlot(0)->hasItem() ? 0 : 16); + blit(xo + 59, yo + 20, 0, texV, 110, 16); + + if ((repairMenu->getSlot(AnvilMenu::INPUT_SLOT)->hasItem() || + repairMenu->getSlot(AnvilMenu::ADDITIONAL_SLOT)->hasItem()) && + !repairMenu->getSlot(AnvilMenu::RESULT_SLOT)->hasItem()) { + blit(xo + 99, yo + 45, imageWidth, 0, 28, 21); + } +#endif +} + +void RepairScreen::keyPressed(char ch, int eventKey) { + if (editName) { + editName->keyPressed(ch, eventKey); + updateItemName(); + } else { + AbstractContainerScreen::keyPressed(ch, eventKey); + } +} + +void RepairScreen::mouseClicked(int mouseX, int mouseY, int buttonNum) { + AbstractContainerScreen::mouseClicked(mouseX, mouseY, buttonNum); + if (editName) { + editName->mouseClicked(mouseX, mouseY, buttonNum); + } +} + +void RepairScreen::updateItemName() { + std::wstring itemName; + Slot* slot = repairMenu->getSlot(0); + if (slot != NULL && slot->hasItem()) { + if (!slot->getItem()->hasCustomHoverName() && + itemName == slot->getItem()->getHoverName()) { + itemName = L""; + } + } + + repairMenu->setItemName(itemName); + + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + dos.writeUTF(itemName); + Minecraft::GetInstance()->player->connection->send( + std::shared_ptr(new CustomPayloadPacket( + CustomPayloadPacket::SET_ITEM_NAME_PACKET, baos.toByteArray()))); +} + +// 4jcraft: these 3 are to implement Containerlistener (see IUIScene_AnvilMenu +// and net.minecraft.world.inventory.ContainerListener) +void RepairScreen::refreshContainer( + AbstractContainerMenu* container, + std::vector >* items) { + slotChanged(container, AnvilMenu::INPUT_SLOT, + container->getSlot(0)->getItem()); +} + +void RepairScreen::slotChanged(AbstractContainerMenu* container, int slotIndex, + std::shared_ptr item) { + if (slotIndex == AnvilMenu::INPUT_SLOT) { + std::wstring itemName = item == NULL ? L"" : item->getHoverName(); + editName->setValue(itemName); + if (item != NULL) { + editName->focus(true); + updateItemName(); + } else { + editName->focus(false); + } + } +} + +void RepairScreen::setContainerData(AbstractContainerMenu* container, int id, + int value) {} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/RepairScreen.h b/Minecraft.Client/UI/Screens/RepairScreen.h new file mode 100644 index 000000000..b5352a939 --- /dev/null +++ b/Minecraft.Client/UI/Screens/RepairScreen.h @@ -0,0 +1,39 @@ +#pragma once + +#include "../../Platform/stdafx.h" +#include "AbstractContainerScreen.h" +#include "../../../Minecraft.World/Containers/AnvilMenu.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.inventory.ContainerListener.h" + +class EditBox; + +class RepairScreen : public AbstractContainerScreen, public ContainerListener { +public: + RepairScreen(std::shared_ptr inventory, Level* level, int x, + int y, int z); + virtual ~RepairScreen(); + + void init(); + void removed(); + void render(int xm, int ym, float a); + void renderLabels(); + void renderBg(float a); + void keyPressed(char ch, int eventKey); + void mouseClicked(int mouseX, int mouseY, int buttonNum); + + // 4jcraft: these 3 are to implement Containerlistener (see + // IUIScene_AnvilMenu and net.minecraft.world.inventory.ContainerListener) + void refreshContainer(AbstractContainerMenu* container, + std::vector >* items); + void slotChanged(AbstractContainerMenu* container, int slotIndex, + std::shared_ptr item); + void setContainerData(AbstractContainerMenu* container, int id, int value); + +private: + void updateItemName(); + + std::shared_ptr inventory; + Level* level; + AnvilMenu* repairMenu; + EditBox* editName; +}; \ No newline at end of file From 975f716f9c8f9e04e0faab260d7a7c83797e749e Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 17:28:29 +0300 Subject: [PATCH 14/40] feat(jui): re-enable and update TrapScreen (Dispenser and Dropper GUI) --- Minecraft.Client/Player/LocalPlayer.cpp | 6 ++++- Minecraft.Client/Textures/Textures.cpp | 1 + Minecraft.Client/Textures/Textures.h | 1 + Minecraft.Client/UI/Screens/TrapScreen.cpp | 26 +++++++++++++--------- Minecraft.Client/UI/Screens/TrapScreen.h | 5 +++++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/Minecraft.Client/Player/LocalPlayer.cpp b/Minecraft.Client/Player/LocalPlayer.cpp index d04c5f1f5..18f22d8dc 100644 --- a/Minecraft.Client/Player/LocalPlayer.cpp +++ b/Minecraft.Client/Player/LocalPlayer.cpp @@ -683,9 +683,13 @@ bool LocalPlayer::openBeacon(std::shared_ptr beacon) { } bool LocalPlayer::openTrap(std::shared_ptr trap) { +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new TrapScreen(inventory, trap)); + bool success = true; +#else bool success = app.LoadTrapMenu(GetXboxPad(), inventory, trap); if (success) ui.PlayUISFX(eSFX_Press); - // minecraft->setScreen(new TrapScreen(inventory, trap)); +#endif return success; } diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 39e8cb4c7..25a502dcb 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -179,6 +179,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"title/mclogo", L"gui/horse", L"gui/anvil", + L"gui/trap", L"title/bg/panorama", #endif // L"item/christmas", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 48a392444..6c083cf70 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -161,6 +161,7 @@ typedef enum _TEXTURE_NAME { TN_TITLE_MCLOGO, TN_GUI_HORSE, TN_GUI_ANVIL, + TN_GUI_TRAP, TN_TITLE_BG_PANORAMA, #endif // TN_TILE_XMAS_CHEST, diff --git a/Minecraft.Client/UI/Screens/TrapScreen.cpp b/Minecraft.Client/UI/Screens/TrapScreen.cpp index 80de6d7e9..7a1afb6bd 100644 --- a/Minecraft.Client/UI/Screens/TrapScreen.cpp +++ b/Minecraft.Client/UI/Screens/TrapScreen.cpp @@ -6,23 +6,29 @@ #include "../../../Minecraft.World/Blocks/TileEntities/DispenserTileEntity.h" #include "../../../Minecraft.World/Headers/net.minecraft.world.h" +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_TRAP_LOCATION = ResourceLocation(TN_GUI_TRAP); +#endif + TrapScreen::TrapScreen(std::shared_ptr inventory, std::shared_ptr trap) - : AbstractContainerScreen(new TrapMenu(inventory, trap)) {} + : AbstractContainerScreen(new TrapMenu(inventory, trap)) { + this->trap = trap; + this->inventory = inventory; +} void TrapScreen::renderLabels() { - font->draw(L"Dispenser", 16 + 4 + 40, 2 + 2 + 2, 0x404040); - font->draw(L"Inventory", 8, imageHeight - 96 + 2, 0x404040); + font->draw(trap->getName(), 16 + 4 + 40, 2 + 2 + 2, 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); } void TrapScreen::renderBg(float a) { // 4J Unused -#if 0 - int tex = minecraft->textures->loadTexture(L"/gui/trap.png"); - glColor4f(1, 1, 1, 1); - minecraft->textures->bind(tex); - int xo = (width - imageWidth) / 2; - int yo = (height - imageHeight) / 2; - this->blit(xo, yo, 0, 0, imageWidth, imageHeight); +#ifdef ENABLE_JAVA_GUIS + glColor4f(1, 1, 1, 1); + minecraft->textures->bindTexture(&GUI_TRAP_LOCATION); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + this->blit(xo, yo, 0, 0, imageWidth, imageHeight); #endif } \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/TrapScreen.h b/Minecraft.Client/UI/Screens/TrapScreen.h index fcf8a33fa..ae2b02c89 100644 --- a/Minecraft.Client/UI/Screens/TrapScreen.h +++ b/Minecraft.Client/UI/Screens/TrapScreen.h @@ -1,4 +1,5 @@ #pragma once +#include #include "AbstractContainerScreen.h" class DispenserTileEntity; class Inventory; @@ -11,4 +12,8 @@ public: protected: virtual void renderLabels(); virtual void renderBg(float a); + +private: + std::shared_ptr inventory; + std::shared_ptr trap; }; \ No newline at end of file From b854af49c60fe84395705221a7b66c297ef07fd7 Mon Sep 17 00:00:00 2001 From: StevenSYS <139715581+StevenSYS@users.noreply.github.com> Date: Thu, 26 Mar 2026 15:49:52 +0000 Subject: [PATCH 15/40] Added option to use the TU panorama or the Java one. --- Minecraft.Assets/Common/res/lang/en_US.lang | 1 + Minecraft.Client/GameState/Options.cpp | 23 +- Minecraft.Client/GameState/Options.h | 4 +- .../Common/UI/UIScene_SettingsOptionsMenu.h | 8 +- Minecraft.Client/Textures/Textures.cpp | 8 +- Minecraft.Client/Textures/Textures.h | 8 +- Minecraft.Client/UI/Screens/TitleScreen.cpp | 305 +++++++++++------- Minecraft.Client/UI/Screens/TitleScreen.h | 2 - .../UI/Screens/VideoSettingsScreen.cpp | 27 +- 9 files changed, 240 insertions(+), 146 deletions(-) diff --git a/Minecraft.Assets/Common/res/lang/en_US.lang b/Minecraft.Assets/Common/res/lang/en_US.lang index cc56540f8..0b005e48c 100644 --- a/Minecraft.Assets/Common/res/lang/en_US.lang +++ b/Minecraft.Assets/Common/res/lang/en_US.lang @@ -189,6 +189,7 @@ options.renderDistance.short=Short options.renderDistance.normal=Normal options.renderDistance.far=Far options.viewBobbing=View Bobbing +options.classicPanorama=Classic Panorama options.ao=Smooth Lighting options.anaglyph=3D Anaglyph options.framerateLimit=Performance diff --git a/Minecraft.Client/GameState/Options.cpp b/Minecraft.Client/GameState/Options.cpp index 289fdecb0..3f6f4e4b1 100644 --- a/Minecraft.Client/GameState/Options.cpp +++ b/Minecraft.Client/GameState/Options.cpp @@ -16,7 +16,7 @@ // 4J - the Option sub-class used to be an java enumerated type, trying to // emulate that functionality here -const Options::Option Options::Option::options[17] = { +const Options::Option Options::Option::options[18] = { Options::Option(L"options.music", true, false), Options::Option(L"options.sound", true, false), Options::Option(L"options.invertMouse", false, true), @@ -34,6 +34,7 @@ const Options::Option Options::Option::options[17] = { Options::Option(L"options.gamma", true, false), Options::Option(L"options.renderClouds", false, true), Options::Option(L"options.particles", false, false), + Options::Option(L"options.classicPanorama", false, true), }; const Options::Option* Options::Option::MUSIC = &Options::Option::options[0]; @@ -65,6 +66,8 @@ const Options::Option* Options::Option::RENDER_CLOUDS = &Options::Option::options[15]; const Options::Option* Options::Option::PARTICLES = &Options::Option::options[16]; +const Options::Option* Options::Option::CLASSIC_PANORAMA = + &(Options::Option::options[17]); const Options::Option* Options::Option::getItem(int id) { return &options[id]; } @@ -111,6 +114,8 @@ void Options::init() { invertYMouse = false; viewDistance = 0; bobView = true; + // 4jcraft + classicPanorama = false; anaglyph3d = false; advancedOpengl = false; @@ -238,9 +243,12 @@ void Options::toggle(const Options::Option* option, int dir) { if (option == Option::PARTICLES) particles = (particles + dir) % 3; // 4J-PB - changing - // if (option == Option::VIEW_BOBBING) bobView = !bobView; - if (option == Option::VIEW_BOBBING) - ((dir == 0) ? bobView = false : bobView = true); + // 4jcraft: uncommented this so that the view bobbing option works + if (option == Option::VIEW_BOBBING) bobView = !bobView; + // 4jcraft + if (option == Option::CLASSIC_PANORAMA) { + classicPanorama = !classicPanorama; + } if (option == Option::RENDER_CLOUDS) renderClouds = !renderClouds; if (option == Option::ADVANCED_OPENGL) { advancedOpengl = !advancedOpengl; @@ -293,6 +301,8 @@ bool Options::getBooleanValue(const Options::Option* item) { // types if (item == Option::INVERT_MOUSE) return invertYMouse; if (item == Option::VIEW_BOBBING) return bobView; + // 4jcraft + if (item == Option::CLASSIC_PANORAMA) return classicPanorama; if (item == Option::ANAGLYPH) return anaglyph3d; if (item == Option::ADVANCED_OPENGL) return advancedOpengl; if (item == Option::AMBIENT_OCCLUSION) return ambientOcclusion; @@ -405,6 +415,8 @@ void Options::load() { if (cmds[0] == L"guiScale") guiScale = _fromString(cmds[1]); if (cmds[0] == L"particles") particles = _fromString(cmds[1]); if (cmds[0] == L"bobView") bobView = cmds[1] == L"true"; + // 4jcraft + if (cmds[0] == L"classicPanorama") classicPanorama = cmds[1] == L"true"; if (cmds[0] == L"anaglyph3d") anaglyph3d = cmds[1] == L"true"; if (cmds[0] == L"advancedOpengl") advancedOpengl = cmds[1] == L"true"; if (cmds[0] == L"fpsLimit") framerateLimit = _fromString(cmds[1]); @@ -459,6 +471,9 @@ void Options::save() { dos.writeChars(L"guiScale:" + _toString(guiScale)); dos.writeChars(L"particles:" + _toString(particles)); dos.writeChars(L"bobView:" + std::wstring(bobView ? L"true" : L"false")); + // 4jcraft + dos.writeChars(L"classicPanorama:" + + std::wstring(classicPanorama ? L"true" : L"false")); dos.writeChars(L"anaglyph3d:" + std::wstring(anaglyph3d ? L"true" : L"false")); dos.writeChars(L"advancedOpengl:" + diff --git a/Minecraft.Client/GameState/Options.h b/Minecraft.Client/GameState/Options.h index b727da4a0..bb84fc759 100644 --- a/Minecraft.Client/GameState/Options.h +++ b/Minecraft.Client/GameState/Options.h @@ -13,7 +13,7 @@ public: // 4J - this used to be an enum class Option { public: - static const Option options[17]; + static const Option options[18]; static const Option* MUSIC; static const Option* SOUND; static const Option* INVERT_MOUSE; @@ -31,6 +31,7 @@ public: static const Option* GAMMA; static const Option* RENDER_CLOUDS; static const Option* PARTICLES; + static const Option* CLASSIC_PANORAMA; private: const bool _isProgress; @@ -61,6 +62,7 @@ public: bool invertYMouse; int viewDistance; bool bobView; + bool classicPanorama; bool anaglyph3d; bool advancedOpengl; int framerateLimit; diff --git a/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h b/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h index 67c30660d..6bf721a2d 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h +++ b/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h @@ -6,6 +6,8 @@ class UIScene_SettingsOptionsMenu : public UIScene { private: enum EControls { eControl_ViewBob, + // 4jcraft + eControl_ClassicPanorama, eControl_ShowHints, eControl_ShowTooltips, eControl_InGameGamertags, @@ -20,8 +22,8 @@ protected: static int m_iDifficultyTitleSettingA[4]; private: - UIControl_CheckBox m_checkboxViewBob, m_checkboxShowHints, - m_checkboxShowTooltips, m_checkboxInGameGamertags, + UIControl_CheckBox m_checkboxViewBob, m_checkboxClassicPanorama, + m_checkboxShowHints, m_checkboxShowTooltips, m_checkboxInGameGamertags, m_checkboxMashupWorlds; // Checkboxes UIControl_Slider m_sliderAutosave, m_sliderDifficulty; // Sliders UIControl_Label m_labelDifficultyText; // Text @@ -29,6 +31,8 @@ private: UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) UI_MAP_ELEMENT(m_checkboxViewBob, "ViewBob") + // 4jcraft + UI_MAP_ELEMENT(m_checkboxClassicPanorama, "ClassicPanorama") UI_MAP_ELEMENT(m_checkboxShowHints, "ShowHints") UI_MAP_ELEMENT(m_checkboxShowTooltips, "ShowTooltips") UI_MAP_ELEMENT(m_checkboxInGameGamertags, "InGameGamertags") diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 25a502dcb..2afdd477a 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -181,6 +181,12 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"gui/anvil", L"gui/trap", L"title/bg/panorama", + L"title/bg/panorama0", + L"title/bg/panorama1", + L"title/bg/panorama2", + L"title/bg/panorama3", + L"title/bg/panorama4", + L"title/bg/panorama5", #endif // L"item/christmas", // L"item/christmas_double", @@ -1494,4 +1500,4 @@ bool Textures::IsOriginalImage(TEXTURE_NAME texId, const std::wstring& name) { i++; } return false; -} +} \ No newline at end of file diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 6c083cf70..c2ccaa936 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -163,6 +163,12 @@ typedef enum _TEXTURE_NAME { TN_GUI_ANVIL, TN_GUI_TRAP, TN_TITLE_BG_PANORAMA, + TN_TITLE_BG_PANORAMA0, + TN_TITLE_BG_PANORAMA1, + TN_TITLE_BG_PANORAMA2, + TN_TITLE_BG_PANORAMA3, + TN_TITLE_BG_PANORAMA4, + TN_TITLE_BG_PANORAMA5, #endif // TN_TILE_XMAS_CHEST, // TN_TILE_LARGE_XMAS_CHEST, @@ -360,4 +366,4 @@ public: // drive static bool IsTUImage(TEXTURE_NAME texId, const std::wstring& name); static bool IsOriginalImage(TEXTURE_NAME texId, const std::wstring& name); -}; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/TitleScreen.cpp b/Minecraft.Client/UI/Screens/TitleScreen.cpp index dcf055456..0f8212cb1 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.cpp +++ b/Minecraft.Client/UI/Screens/TitleScreen.cpp @@ -17,8 +17,7 @@ Random* TitleScreen::random = new Random(); TitleScreen::TitleScreen() { // 4J - added initialisers - // vo = 0; // 4jcraft removed - panoramaTimer = 0.0f; + vo = 0; multiplayerButton = NULL; splash = L"missingno"; @@ -67,18 +66,10 @@ TitleScreen::TitleScreen() { } splash = splashes.at(splashIndex); - - titlePanoramaPaths[0] = L"title/bg/panorama0.png"; - titlePanoramaPaths[1] = L"title/bg/panorama1.png"; - titlePanoramaPaths[2] = L"title/bg/panorama2.png"; - titlePanoramaPaths[3] = L"title/bg/panorama3.png"; - titlePanoramaPaths[4] = L"title/bg/panorama4.png"; - titlePanoramaPaths[5] = L"title/bg/panorama5.png"; } void TitleScreen::tick() { - panoramaTimer += 1.0f; - // vo += 1.0f; // 4jcraft removed + vo += 1.0f; // if( vo > 100.0f ) minecraft->setScreen(new SelectWorldScreen(this)); // // 4J - temp testing } @@ -173,139 +164,203 @@ void TitleScreen::buttonClicked(Button* button) { void TitleScreen::renderPanorama(float a) { #ifdef ENABLE_JAVA_GUIS Tesselator* t = Tesselator::getInstance(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - gluPerspective(120.0f, 1.0f, 0.05f, 10.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glRotatef(180.0f, 1.0f, 0.0f, 0.0f); - glEnable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); - glDepthMask(false); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - char offsetPasses = 8; - - for (int i = 0; i < (offsetPasses * offsetPasses); i++) { + if (minecraft->options->classicPanorama) { + glMatrixMode(GL_PROJECTION); glPushMatrix(); - float x = - ((float)(i % offsetPasses) / (float)offsetPasses - 0.5f) / 64.0f; - float y = - ((float)(i / offsetPasses) / (float)offsetPasses - 0.5f) / 64.0f; - float z = 0.0f; - glTranslatef(x, y, z); - glRotatef(sin((panoramaTimer + a) / 400.0f) * 25.0f + 20.0f, 1.0f, 0.0f, - 0.0f); - glRotatef(-(panoramaTimer + a) * 0.1f, 0.0f, 1.0f, 0.0f); + glLoadIdentity(); + gluPerspective(120.0f, 1.0f, 0.05f, 10.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glRotatef(180.0f, 1.0f, 0.0f, 0.0f); + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDisable(GL_CULL_FACE); + glDepthMask(false); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + char offsetPasses = 8; - for (int j = 0; j < 6; j++) { + for (int i = 0; i < (offsetPasses * offsetPasses); i++) { glPushMatrix(); + float x = ((float)(i % offsetPasses) / (float)offsetPasses - 0.5f) / + 64.0f; + float y = ((float)(i / offsetPasses) / (float)offsetPasses - 0.5f) / + 64.0f; + float z = 0.0f; + glTranslatef(x, y, z); + glRotatef(sin((vo + a) / 400.0f) * 25.0f + 20.0f, 1.0f, 0.0f, 0.0f); + glRotatef(-(vo + a) * 0.1f, 0.0f, 1.0f, 0.0f); - switch (j) { - case 1: - glRotatef(90.0f, 0.0f, 1.0f, 0.0f); - break; - case 2: - glRotatef(180.0f, 0.0f, 1.0f, 0.0f); - break; - case 3: - glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); - break; - case 4: - glRotatef(90.0f, 1.0f, 0.0f, 0.0f); - break; - case 5: - glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); - break; - default: - break; + for (int j = 0; j < 6; j++) { + glPushMatrix(); + + switch (j) { + case 1: + glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + break; + case 2: + glRotatef(180.0f, 0.0f, 1.0f, 0.0f); + break; + case 3: + glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + break; + case 4: + glRotatef(90.0f, 1.0f, 0.0f, 0.0f); + break; + case 5: + glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + break; + default: + break; + } + + glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture( + TN_TITLE_BG_PANORAMA0 + j)); + t->begin(); + t->color(16777215, 255 / (i + 1)); + t->vertexUV(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f); + t->vertexUV(1.0f, -1.0f, 1.0f, 1.0f, 0.0f); + t->vertexUV(1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + t->vertexUV(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f); + t->end(); + glPopMatrix(); } - - minecraft->textures->bindTexture(titlePanoramaPaths[j]); - t->begin(); - t->color(16777215, 255 / (i + 1)); - t->vertexUV(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f); - t->vertexUV(1.0f, -1.0f, 1.0f, 1.0f, 0.0f); - t->vertexUV(1.0f, 1.0f, 1.0f, 1.0f, 1.0f); - t->vertexUV(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f); - t->end(); glPopMatrix(); + glColorMask(true, true, true, false); } - glPopMatrix(); - glColorMask(true, true, true, false); - } - t->offset(0.0f, 0.0f, 0.0f); - glColorMask(true, true, true, true); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glDepthMask(true); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glEnable(GL_DEPTH_TEST); + t->offset(0.0f, 0.0f, 0.0f); + glColorMask(true, true, true, true); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glDepthMask(true); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); + } else { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, width, height, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glEnable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + + glBindTexture(GL_TEXTURE_2D, + minecraft->textures->loadTexture(TN_TITLE_BG_PANORAMA)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + float off = vo * 0.0004f; + + float screenAspect = (float)width / (float)height; + float texAspect = 1748.0f / 144.0f; + float scale; + if (screenAspect > texAspect) { + scale = (float)width / 1748.0f; + } else { + scale = (float)height / 144.0f; + } + + float texWidth = 1748.0f * scale; + float texHeight = 144.0f * scale; + float yOff = (height - texHeight) / 2.0f; + + float uMax = off + (texWidth / 1748.0f); + + t->begin(GL_QUADS); + t->color(0xffffff, 255); + t->vertexUV(0, yOff + texHeight, 0, off, 1.0f); + t->vertexUV(texWidth, yOff + texHeight, 0, uMax, 1.0f); + t->vertexUV(texWidth, yOff, 0, uMax, 0.0f); + t->vertexUV(0, yOff, 0, off, 0.0f); + t->end(); + + glDepthMask(true); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } #endif } // 4jcraft void TitleScreen::renderSkybox(float a) { - glViewport(0, 0, 256, 256); - renderPanorama(a); - glDisable(GL_TEXTURE_2D); - glEnable(GL_TEXTURE_2D); - - for (int i = 0; i < 8; i++) { - rotateAndBlur(a); +#ifdef ENABLE_JAVA_GUIS + if (minecraft->options->classicPanorama) { + glViewport(0, 0, 256, 256); } + renderPanorama(a); + if (minecraft->options->classicPanorama) { + glDisable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_2D); - glViewport(0, 0, minecraft->width, minecraft->height); + for (int i = 0; i < 8; i++) { + rotateAndBlur(a); + } - Tesselator* t = Tesselator::getInstance(); - t->begin(); - float aspect = - width > height ? 120.0f / (float)width : 120.0f / (float)height; - float sWidth = (float)height * aspect / 256.0f; - float sHeight = (float)width * aspect / 256.0f; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - t->color(1.0f, 1.0f, 1.0f, 1.0f); - t->vertexUV(0.0f, height, 0.0f, (0.5f - sWidth), (0.5f + sHeight)); - t->vertexUV(width, height, 0.0f, (0.5f - sWidth), (0.5f - sHeight)); - t->vertexUV(width, 0.0f, 0.0f, (0.5f + sWidth), (0.5f - sHeight)); - t->vertexUV(0.0f, 0.0f, 0.0f, (0.5f + sWidth), (0.5f + sHeight)); - t->end(); - return; + glViewport(0, 0, minecraft->width, minecraft->height); + + Tesselator* t = Tesselator::getInstance(); + t->begin(); + float aspect = + width > height ? 120.0f / (float)width : 120.0f / (float)height; + float sWidth = (float)height * aspect / 256.0f; + float sHeight = (float)width * aspect / 256.0f; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + t->color(1.0f, 1.0f, 1.0f, 1.0f); + t->vertexUV(0.0f, height, 0.0f, (0.5f - sWidth), (0.5f + sHeight)); + t->vertexUV(width, height, 0.0f, (0.5f - sWidth), (0.5f - sHeight)); + t->vertexUV(width, 0.0f, 0.0f, (0.5f + sWidth), (0.5f - sHeight)); + t->vertexUV(0.0f, 0.0f, 0.0f, (0.5f + sWidth), (0.5f + sHeight)); + t->end(); + } +#endif } // 4jcraft void TitleScreen::rotateAndBlur(float a) { #ifdef ENABLE_JAVA_GUIS - glBindTexture(GL_TEXTURE_2D, viewportTexture); - // this.mc.renderEngine.resetBoundTexture(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 256, 256); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColorMask(true, true, true, false); - Tesselator* t = Tesselator::getInstance(); - t->begin(); - char blurPasses = 3; + if (minecraft->options->classicPanorama) { + glBindTexture(GL_TEXTURE_2D, viewportTexture); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 256, 256); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(true, true, true, false); + Tesselator* t = Tesselator::getInstance(); + t->begin(); + char blurPasses = 3; - for (int i = 0; i < blurPasses; i++) { - t->color(1.0f, 1.0f, 1.0f, 1.0f / (float)(i + 1)); - float offset = (float)(i - blurPasses / 2) / 256.0f; - t->vertexUV(width, height, 0.0f, (0.0f + offset), 0.0f); - t->vertexUV(width, 0.0f, 0.0f, (1.0f + offset), 0.0f); - t->vertexUV(0.0f, 0.0f, 0.0f, (1.0f + offset), 1.0f); - t->vertexUV(0.0f, height, 0.0f, (0.0f + offset), 1.0f); + for (int i = 0; i < blurPasses; i++) { + t->color(1.0f, 1.0f, 1.0f, 1.0f / (float)(i + 1)); + float offset = (float)(i - blurPasses / 2) / 256.0f; + t->vertexUV(width, height, 0.0f, (0.0f + offset), 0.0f); + t->vertexUV(width, 0.0f, 0.0f, (1.0f + offset), 0.0f); + t->vertexUV(0.0f, 0.0f, 0.0f, (1.0f + offset), 1.0f); + t->vertexUV(0.0f, height, 0.0f, (0.0f + offset), 1.0f); + } + + t->end(); + glColorMask(true, true, true, true); } - - t->end(); - glColorMask(true, true, true, true); - // this.mc.renderEngine.resetBoundTexture(); #endif } @@ -321,9 +376,11 @@ void TitleScreen::render(int xm, int ym, float a) { int logoX = width / 2 - logoWidth / 2; int logoY = 30; - // 4jcraft: gradient - fillGradient(0, 0, width, height, -2130706433, 16777215); - fillGradient(0, 0, width, height, 0, INT_MIN); + // 4jcraft: gradient for classic panorama + if (minecraft->options->classicPanorama) { + fillGradient(0, 0, width, height, -2130706433, 16777215); + fillGradient(0, 0, width, height, 0, INT_MIN); + } glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture(TN_TITLE_MCLOGO)); diff --git a/Minecraft.Client/UI/Screens/TitleScreen.h b/Minecraft.Client/UI/Screens/TitleScreen.h index 9bb7ffdca..fa29afb52 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.h +++ b/Minecraft.Client/UI/Screens/TitleScreen.h @@ -17,8 +17,6 @@ private: void renderSkybox(float a); void rotateAndBlur(float a); int viewportTexture; - float panoramaTimer; - std::wstring titlePanoramaPaths[6]; // 4jcraft: taken from UIScene_MainMenu // 4J Added diff --git a/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp b/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp index 5daf8fdcc..4a2777494 100644 --- a/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp +++ b/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp @@ -6,6 +6,9 @@ #include "ControlsScreen.h" #include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +// 4jcraft +#define ITEM_COUNT 11 + VideoSettingsScreen::VideoSettingsScreen(Screen* lastScreen, Options* options) { this->title = L"Video Settings"; // 4J - added this->lastScreen = lastScreen; @@ -16,18 +19,20 @@ void VideoSettingsScreen::init() { Language* language = Language::getInstance(); this->title = language->getElement(L"options.videoTitle"); - const Options::Option* items[10] = {Options::Option::GRAPHICS, - Options::Option::RENDER_DISTANCE, - Options::Option::AMBIENT_OCCLUSION, - Options::Option::FRAMERATE_LIMIT, - Options::Option::ANAGLYPH, - Options::Option::VIEW_BOBBING, - Options::Option::GUI_SCALE, - Options::Option::ADVANCED_OPENGL, - Options::Option::GAMMA, - Options::Option::FOV}; + const Options::Option* items[ITEM_COUNT] = { + Options::Option::GRAPHICS, + Options::Option::RENDER_DISTANCE, + Options::Option::AMBIENT_OCCLUSION, + Options::Option::FRAMERATE_LIMIT, + Options::Option::ANAGLYPH, + Options::Option::VIEW_BOBBING, + Options::Option::GUI_SCALE, + Options::Option::ADVANCED_OPENGL, + Options::Option::GAMMA, + Options::Option::FOV, + Options::Option::CLASSIC_PANORAMA}; - for (int i = 0; i < 10; i++) { + for (int i = 0; i < ITEM_COUNT; i++) { const Options::Option* item = items[i]; int xPos = width / 2 - 155 + (i % 2 * 160); int yPos = height / 6 + 24 * (i / 2); From 5ac7f23577e43bdf652e303f3a324c72a0e0e2bd Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 19:37:28 +0300 Subject: [PATCH 16/40] feat(jui): add hopper screen --- .../Common/res/1_2_2/gui/hopper.png | Bin 0 -> 1202 bytes Minecraft.Client/Player/LocalPlayer.cpp | 7 +++- Minecraft.Client/Textures/Textures.cpp | 1 + Minecraft.Client/Textures/Textures.h | 1 + .../UI/Screens/CreateWorldScreen.cpp | 4 +- Minecraft.Client/UI/Screens/HopperScreen.cpp | 37 ++++++++++++++++++ Minecraft.Client/UI/Screens/HopperScreen.h | 21 ++++++++++ 7 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/hopper.png create mode 100644 Minecraft.Client/UI/Screens/HopperScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/HopperScreen.h diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/hopper.png b/Minecraft.Assets/Common/res/1_2_2/gui/hopper.png new file mode 100644 index 0000000000000000000000000000000000000000..3d0054790f574bb8ffcd90fe2069edc2375c0420 GIT binary patch literal 1202 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K58911MRQ8&P5C#Tjh5(-sS0EP%%+1Y1Lqof} zyN?|^_W%EXh~OwW8Un*U1d^tF&INjsu_VYZn8D%MjWiG^$=lt9p@UV{1IXbl@Q5sC zVBk9l!i+m6X1@h0u=8|r45_&F_O>BkvjLAo;BJ*W|4-zGuhN^U>rt71reTV{Vk5KQ z#Md2sJJ&1c@B3ey=HIZhEBACCLofp)i+}?P@#9&c$*Qn0P6NJgt3)wm1s<$oJa$}M zI#k;&iHSp@fdN50XL(`$Yn_eLd!7s5*2P;IqnQihYB7J2tqqr3IGbZ9E4sZvd0|Ci z#qG#OLJdz)`jX7>pO;Ia!QS1s)s`LAUXY@d8&+ container) { } bool LocalPlayer::openHopper(std::shared_ptr container) { - // minecraft->setScreen(new HopperScreen(inventory, container)); +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new HopperScreen(inventory, container)); + bool success = true; +#else bool success = app.LoadHopperMenu(GetXboxPad(), inventory, container); if (success) ui.PlayUISFX(eSFX_Press); +#endif return success; } diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 2afdd477a..50f92da4f 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -180,6 +180,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"gui/horse", L"gui/anvil", L"gui/trap", + L"gui/hopper", L"title/bg/panorama", L"title/bg/panorama0", L"title/bg/panorama1", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index c2ccaa936..8b80be1ba 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -162,6 +162,7 @@ typedef enum _TEXTURE_NAME { TN_GUI_HORSE, TN_GUI_ANVIL, TN_GUI_TRAP, + TN_GUI_HOPPER, TN_TITLE_BG_PANORAMA, TN_TITLE_BG_PANORAMA0, TN_TITLE_BG_PANORAMA1, diff --git a/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp b/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp index 8ca1690e9..593f8416a 100644 --- a/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp +++ b/Minecraft.Client/UI/Screens/CreateWorldScreen.cpp @@ -155,7 +155,9 @@ void CreateWorldScreen::buttonClicked(Button* button) { app.DebugPrintf("CreateWorldScreen::buttonClicked START\n"); if (!button->active) return; if (button->id == 1) { - app.DebugPrintf("CreateWorldScreen::buttonClicked 'Cancel' minecraft->setScreen(lastScreen)\n"); + app.DebugPrintf( + "CreateWorldScreen::buttonClicked 'Cancel' " + "minecraft->setScreen(lastScreen)\n"); minecraft->setScreen(lastScreen); } else if (button->id == 0) { minecraft->setScreen( diff --git a/Minecraft.Client/UI/Screens/HopperScreen.cpp b/Minecraft.Client/UI/Screens/HopperScreen.cpp new file mode 100644 index 000000000..2bab4945e --- /dev/null +++ b/Minecraft.Client/UI/Screens/HopperScreen.cpp @@ -0,0 +1,37 @@ +#include "../../Platform/stdafx.h" +#include "HopperScreen.h" +#include "../../Textures/Textures.h" +#include "../../Player/LocalPlayer.h" +#include "../Font.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.inventory.h" +#include "../../../Minecraft.World/Containers/HopperMenu.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing +// container classes +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_HOPPER_LOCATION = ResourceLocation(TN_GUI_HOPPER); +#endif + +HopperScreen::HopperScreen(std::shared_ptr inventory, + std::shared_ptr hopper) + : AbstractContainerScreen(new HopperMenu(inventory, hopper)) { + this->hopper = hopper; + this->inventory = inventory; + imageHeight = 133; +} + +void HopperScreen::renderLabels() { + font->draw(hopper->getName(), 8, 6, 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); +} + +void HopperScreen::renderBg(float a) { + // 4J Unused +#ifdef ENABLE_JAVA_GUIS + glColor4f(1, 1, 1, 1); + minecraft->textures->bindTexture(&GUI_HOPPER_LOCATION); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + this->blit(xo, yo, 0, 0, imageWidth, imageHeight); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/HopperScreen.h b/Minecraft.Client/UI/Screens/HopperScreen.h new file mode 100644 index 000000000..eb1bb752f --- /dev/null +++ b/Minecraft.Client/UI/Screens/HopperScreen.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include "AbstractContainerScreen.h" + +class HopperTileEntity; +class MinecartHopper; +class Inventory; + +class HopperScreen : public AbstractContainerScreen { +public: + HopperScreen(std::shared_ptr inventory, + std::shared_ptr hopper); + +protected: + virtual void renderLabels(); + virtual void renderBg(float a); + +private: + std::shared_ptr inventory; + std::shared_ptr hopper; +}; \ No newline at end of file From bdebc23e91542b768972a1b5a99a80ebfdb0f6fd Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Thu, 26 Mar 2026 19:43:56 +0300 Subject: [PATCH 17/40] fix(jui): open hopper screen for minecart hopper as well --- Minecraft.Client/Player/LocalPlayer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/Player/LocalPlayer.cpp b/Minecraft.Client/Player/LocalPlayer.cpp index c04a64230..c9cd930df 100644 --- a/Minecraft.Client/Player/LocalPlayer.cpp +++ b/Minecraft.Client/Player/LocalPlayer.cpp @@ -598,9 +598,13 @@ bool LocalPlayer::openHopper(std::shared_ptr container) { } bool LocalPlayer::openHopper(std::shared_ptr container) { - // minecraft->setScreen(new HopperScreen(inventory, container)); +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new HopperScreen(inventory, container)); + bool success = true; +#else bool success = app.LoadHopperMenu(GetXboxPad(), inventory, container); if (success) ui.PlayUISFX(eSFX_Press); +#endif return success; } From c512bcb19cb7edf7f548c3cc0aa115182131785c Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 19:32:10 +0300 Subject: [PATCH 18/40] feat(jui): add enchanting table screen --- Minecraft.Client/Textures/Textures.cpp | 1 + Minecraft.Client/Textures/Textures.h | 1 + .../UI/Screens/EnchantmentScreen.cpp | 322 ++++++++++++++++++ .../UI/Screens/EnchantmentScreen.h | 50 +++ 4 files changed, 374 insertions(+) create mode 100644 Minecraft.Client/UI/Screens/EnchantmentScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/EnchantmentScreen.h diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 50f92da4f..20b952c59 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -181,6 +181,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"gui/anvil", L"gui/trap", L"gui/hopper", + L"gui/enchant", L"title/bg/panorama", L"title/bg/panorama0", L"title/bg/panorama1", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 8b80be1ba..2a6ad074a 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -163,6 +163,7 @@ typedef enum _TEXTURE_NAME { TN_GUI_ANVIL, TN_GUI_TRAP, TN_GUI_HOPPER, + TN_GUI_ENCHANT, TN_TITLE_BG_PANORAMA, TN_TITLE_BG_PANORAMA0, TN_TITLE_BG_PANORAMA1, diff --git a/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp b/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp new file mode 100644 index 000000000..5b66569cc --- /dev/null +++ b/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp @@ -0,0 +1,322 @@ +#include "../../Platform/stdafx.h" +#include "EnchantmentScreen.h" +#include +#include +#include +#include +#include "../../Player/MultiPlayerLocalPlayer.h" +#include "../../Rendering/Lighting.h" +#include "../../Textures/Textures.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../../Minecraft.World/Containers/EnchantmentMenu.h" +#include "../../Rendering/Models/BookModel.h" +#include "../../../Minecraft.Client/Minecraft.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing +// container classes (and iggy too) +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_ENCHANT_LOCATION = ResourceLocation(TN_GUI_ENCHANT); +ResourceLocation ITEM_BOOK_LOCATION = ResourceLocation(TN_ITEM_BOOK); +#endif + +EnchantmentScreen::EnchantmentScreen(std::shared_ptr inventory, + Level* level, int x, int y, int z) + : AbstractContainerScreen(new EnchantmentMenu(inventory, level, x, y, z)) { + xMouse = yMouse = 0.0f; + + this->inventory = inventory; + this->enchantMenu = static_cast(menu); + bookTick = 0; + flip = oFlip = flipT = flipA = 0.0f; + open = oOpen = 0.0f; + last = nullptr; +} + +EnchantmentScreen::~EnchantmentScreen() = default; + +void EnchantmentScreen::init() { AbstractContainerScreen::init(); } + +void EnchantmentScreen::removed() { AbstractContainerScreen::removed(); } + +void EnchantmentScreen::renderLabels() { + font->draw(Language::getInstance()->getElement(L"container.enchant"), 12, 5, + 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + // 4jcraft: our own refactor, text rendering has been moved to the + // foreground here from renderBg() (which is where it was in the JE 1.6.4 + // code) + bool needsUpdate = false; + for (int i = 0; i < 3; ++i) { + if (enchantMenu->costs[i] != lastCosts[i]) { + needsUpdate = true; + lastCosts[i] = enchantMenu->costs[i]; + } + } + if (needsUpdate) { + for (int i = 0; i < 3; ++i) { + if (enchantMenu->costs[i] > 0) { + enchantNames[i] = EnchantmentNames::instance.getRandomName(); + } else { + enchantNames[i] = L""; + } + } + } + + for (int i = 0; i < 3; ++i) { + int cost = enchantMenu->costs[i]; + + int buttonX = 60; + int buttonY = 14 + 19 * i; + + bool isHovering = + (xMouse >= xo + buttonX && xMouse < xo + buttonX + 108 && + yMouse >= yo + buttonY && yMouse < yo + buttonY + 19); + + bool hasEnoughLevels = (minecraft->player->experienceLevel >= cost) || + minecraft->player->abilities.instabuild; + + if (cost > 0) { + std::wstring enchantName = enchantNames[i]; + + Font* weirdEnchantTableFont = minecraft->altFont; + if (weirdEnchantTableFont) { + int nameColor; + + if (!hasEnoughLevels) { + nameColor = 0x342F25; + } else if (isHovering) { + nameColor = 0xFFFF80; + } else { + nameColor = 0x685E4A; + } + + int textX = xo + buttonX + 2; + int textY = yo + buttonY + 2; + + weirdEnchantTableFont->drawWordWrap( + enchantName, buttonX + 2, buttonY + 2, 104, nameColor, 64); + } + + std::wstring costStr = std::to_wstring(cost); + int costX = buttonX + 108 - font->width(costStr) - 2; + int costY = buttonY + 8; + + int costColor; + if (!hasEnoughLevels) { + costColor = 0x407F10; + } else { + costColor = 0x80FF20; + } + + font->drawShadow(costStr, costX, costY, costColor); + } + } +} + +void EnchantmentScreen::render(int xm, int ym, float a) { + AbstractContainerScreen::render(xm, ym, a); + this->xMouse = (float)xm; + this->yMouse = (float)ym; +} + +void EnchantmentScreen::renderBg(float a) { + // 4J unused +#ifdef ENABLE_JAVA_GUIS + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + Minecraft::GetInstance()->textures->bindTexture(&GUI_ENCHANT_LOCATION); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + blit(xo, yo, 0, 0, imageWidth, imageHeight); + + glPushMatrix(); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + + ScreenSizeCalculator screenSize(minecraft->options, minecraft->width, + minecraft->height); + int scaledWidth = screenSize.getWidth(); + int scaledHeight = screenSize.getHeight(); + int scaleFactor = screenSize.scale; + + glViewport(((scaledWidth - 320) / 2) * scaleFactor, + ((scaledHeight - 240) / 2) * scaleFactor, 320 * scaleFactor, + 240 * scaleFactor); + + glTranslatef(-0.34f, 0.23f, 0.0f); + gluPerspective(90.0f, 1.3333334f, 9.0f, 80.0f); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + Lighting::turnOn(); + + glTranslatef(0.0f, 3.3f, -16.0f); + glScalef(5.0f, 5.0f, 5.0f); + glRotatef(180.0f, 0.0f, 0.0f, 1.0f); + + Minecraft::GetInstance()->textures->bindTexture(&ITEM_BOOK_LOCATION); + glRotatef(20.0f, 1.0f, 0.0f, 0.0f); + + // 4jcraft: brought over from UIControl_EnchantmentBook + float o = oOpen + (open - oOpen) * a; + glTranslatef((1 - o) * 0.2f, (1 - o) * 0.1f, (1 - o) * 0.25f); + glRotatef(-(1 - o) * 90 - 90, 0, 1, 0); + glRotatef(180, 1, 0, 0); + + float ff1 = oFlip + (flip - oFlip) * a + 0.25f; + float ff2 = oFlip + (flip - oFlip) * a + 0.75f; + ff1 = (ff1 - floor(ff1)) * 1.6f - 0.3f; + ff2 = (ff2 - floor(ff2)) * 1.6f - 0.3f; + + if (ff1 < 0.0f) ff1 = 0.0f; + if (ff2 < 0.0f) ff2 = 0.0f; + if (ff1 > 1.0f) ff1 = 1.0f; + if (ff2 > 1.0f) ff2 = 1.0f; + + glEnable(GL_RESCALE_NORMAL); + + static BookModel bookModel; + bookModel.render(nullptr, 0.0f, ff1, ff2, o, 0.0f, 0.0625f, true); + + glDisable(GL_RESCALE_NORMAL); + Lighting::turnOff(); + + glMatrixMode(GL_PROJECTION); + glViewport(0, 0, minecraft->width, minecraft->height); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + + Minecraft::GetInstance()->textures->bindTexture(&GUI_ENCHANT_LOCATION); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + + for (int i = 0; i < 3; ++i) { + int cost = enchantMenu->costs[i]; + int buttonX = 60; + int buttonY = 14 + 19 * i; + + bool isHovering = + (xMouse >= xo + buttonX && xMouse < xo + buttonX + 108 && + yMouse >= yo + buttonY && yMouse < yo + buttonY + 19); + + if (cost == 0) { + blit(xo + buttonX, yo + buttonY, 0, 185, 108, 19); + } else { + bool hasEnoughLevels = + (minecraft->player->experienceLevel >= cost) || + minecraft->player->abilities.instabuild; + + int texV; + if (!hasEnoughLevels) { + texV = 185; + } else if (isHovering) { + texV = 204; + } else { + texV = 166; + } + + blit(xo + buttonX, yo + buttonY, 0, texV, 108, 19); + } + } +#endif +} + +void EnchantmentScreen::mouseClicked(int x, int y, int buttonNum) { + AbstractContainerScreen::mouseClicked(x, y, buttonNum); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + for (int i = 0; i < 3; ++i) { + int buttonX = xo + 60; + int buttonY = yo + 14 + 19 * i; + + if (x >= buttonX && x < buttonX + 108 && y >= buttonY && + y < buttonY + 19) { + if (enchantMenu->clickMenuButton(minecraft->player, i)) { + minecraft->gameMode->handleInventoryButtonClick( + enchantMenu->containerId, i); + } + break; + } + } +} + +void EnchantmentScreen::tick() { + AbstractContainerScreen::tick(); + + // 4jcraft: brought over from UIControl_EnchantmentBook + oFlip = flip; + oOpen = open; + + std::shared_ptr current = + enchantMenu->getSlot(EnchantmentMenu::INGREDIENT_SLOT)->getItem(); + if (!ItemInstance::matches(current, last)) { + last = current; + + if (current) { + do { + flipT += random.nextInt(4) - random.nextInt(4); + } while (flip <= flipT + 1 && flip >= flipT - 1); + } else { + flipT = 0.0f; + } + } + + bool shouldBeOpen = false; + for (int i = 0; i < 3; ++i) { + if (enchantMenu->costs[i] != 0) { + shouldBeOpen = true; + break; + } + } + + if (shouldBeOpen) + open += 0.2f; + else + open -= 0.2f; + + if (open < 0.0f) open = 0.0f; + if (open > 1.0f) open = 1.0f; + + float diff = (flipT - flip) * 0.4f; + float max = 0.2f; + if (diff < -max) diff = -max; + if (diff > max) diff = max; + flipA += (diff - flipA) * 0.9f; + flip = flip + flipA; +} + +// 4jcraft: brought over from UIControl_EnchantmentButton +EnchantmentScreen::EnchantmentNames + EnchantmentScreen::EnchantmentNames::instance; + +EnchantmentScreen::EnchantmentNames::EnchantmentNames() { + std::wstring allWords = + L"the elder scrolls klaatu berata niktu xyzzy bless curse light " + L"darkness fire air earth water hot dry cold wet ignite snuff embiggen " + L"twist shorten stretch fiddle destroy imbue galvanize enchant free " + L"limited range of towards inside sphere cube self other ball mental " + L"physical grow shrink demon elemental spirit animal creature beast " + L"humanoid undead fresh stale "; + std::wistringstream iss(allWords); + std::copy(std::istream_iterator >(iss), + std::istream_iterator >(), + std::back_inserter(words)); +} + +std::wstring EnchantmentScreen::EnchantmentNames::getRandomName() { + int wordCount = random.nextInt(2) + 3; + std::wstring word = L""; + for (int i = 0; i < wordCount; i++) { + if (i > 0) word += L" "; + word += words[random.nextInt(words.size())]; + } + return word; +} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/EnchantmentScreen.h b/Minecraft.Client/UI/Screens/EnchantmentScreen.h new file mode 100644 index 000000000..79cf8786a --- /dev/null +++ b/Minecraft.Client/UI/Screens/EnchantmentScreen.h @@ -0,0 +1,50 @@ +#pragma once + +#include "AbstractContainerScreen.h" +#include "../../../Minecraft.World/Containers/EnchantmentMenu.h" + +class EnchantmentScreen : public AbstractContainerScreen { +public: + EnchantmentScreen(std::shared_ptr inventory, Level* level, int x, + int y, int z); + virtual ~EnchantmentScreen(); + + void init(); + void removed(); + void tick(); + void mouseClicked(int mouseX, int mouseY, int buttonNum); + void renderLabels(); + void renderBg(float a); + void render(int xm, int ym, float a); + +private: + std::shared_ptr inventory; + EnchantmentMenu* enchantMenu; + float xMouse, yMouse; + + Random random; + + // 4jcraft: brought over from UIControl_EnchantmentBook + int bookTick; + float flip, oFlip, flipT, flipA; + float open, oOpen; + std::shared_ptr last; + + // 4jcraft: brought over from UIControl_EnchantmentButton + class EnchantmentNames { + public: + static EnchantmentNames instance; + + private: + Random random; + std::vector words; + + EnchantmentNames(); + + public: + std::wstring getRandomName(); + }; + + std::wstring enchantNames[3]; + int lastCosts[3]; +}; \ No newline at end of file From 1a478c8a5b5feb85a97a45c4877a97b2e3221171 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 20:33:41 +0300 Subject: [PATCH 19/40] refactor(jui): specify overrides, resourcelocations, and localization --- Minecraft.Client/UI/Button.cpp | 9 ++++-- .../UI/Screens/AbstractContainerScreen.h | 18 ++++++------ .../UI/Screens/AchievementScreen.h | 10 +++---- Minecraft.Client/UI/Screens/ChatScreen.h | 12 ++++---- Minecraft.Client/UI/Screens/ConfirmScreen.h | 6 ++-- Minecraft.Client/UI/Screens/ConnectScreen.h | 8 +++--- Minecraft.Client/UI/Screens/ContainerScreen.h | 4 +-- Minecraft.Client/UI/Screens/ControlsScreen.h | 8 +++--- .../UI/Screens/CraftingScreen.cpp | 16 +++++++---- Minecraft.Client/UI/Screens/CraftingScreen.h | 9 ++++-- .../UI/Screens/CreateWorldScreen.h | 16 +++++------ .../UI/Screens/CreativeInventoryScreen.h | 26 ++++++++--------- Minecraft.Client/UI/Screens/DeathScreen.h | 8 +++--- .../UI/Screens/DisconnectedScreen.h | 8 +++--- .../UI/Screens/EnchantmentScreen.h | 14 +++++----- Minecraft.Client/UI/Screens/ErrorScreen.h | 6 ++-- Minecraft.Client/UI/Screens/FurnaceScreen.cpp | 12 +++++--- Minecraft.Client/UI/Screens/FurnaceScreen.h | 5 ++-- Minecraft.Client/UI/Screens/HopperScreen.h | 4 +-- Minecraft.Client/UI/Screens/InBedChatScreen.h | 10 +++---- Minecraft.Client/UI/Screens/InventoryScreen.h | 10 +++---- .../UI/Screens/JoinMultiplayerScreen.h | 14 +++++----- Minecraft.Client/UI/Screens/MessageScreen.h | 6 ++-- Minecraft.Client/UI/Screens/NameEntryScreen.h | 10 +++---- Minecraft.Client/UI/Screens/OptionsScreen.h | 6 ++-- Minecraft.Client/UI/Screens/PauseScreen.h | 8 +++--- .../UI/Screens/ReceivingLevelScreen.h | 8 +++--- .../UI/Screens/RenameWorldScreen.h | 14 +++++----- .../UI/Screens/SelectWorldScreen.h | 8 +++--- Minecraft.Client/UI/Screens/StatsScreen.h | 6 ++-- Minecraft.Client/UI/Screens/TextEditScreen.h | 12 ++++---- Minecraft.Client/UI/Screens/TitleScreen.h | 10 +++---- .../UI/Screens/VideoSettingsScreen.h | 6 ++-- Minecraft.Client/UI/SlideButton.h | 8 +++--- Minecraft.World/Containers/RepairContainer.h | 4 +-- Minecraft.World/Containers/RepairResultSlot.h | 8 +++--- Minecraft.World/Containers/ResultContainer.h | 28 +++++++++---------- Minecraft.World/Containers/ResultSlot.h | 12 ++++---- Minecraft.World/Containers/SimpleContainer.h | 28 +++++++++---------- Minecraft.World/Containers/TrapMenu.h | 4 +-- 40 files changed, 218 insertions(+), 201 deletions(-) diff --git a/Minecraft.Client/UI/Button.cpp b/Minecraft.Client/UI/Button.cpp index 09630ed6b..b5759fa1f 100644 --- a/Minecraft.Client/UI/Button.cpp +++ b/Minecraft.Client/UI/Button.cpp @@ -2,6 +2,10 @@ #include "Button.h" #include "../Textures/Textures.h" +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_GUI_LOCATION = ResourceLocation(TN_GUI_GUI); +#endif + Button::Button(int id, int x, int y, const std::wstring& msg) { init(id, x, y, 200, 20, msg); } @@ -38,8 +42,9 @@ void Button::render(Minecraft* minecraft, int xm, int ym) { Font* font = minecraft->font; - glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture( - TN_GUI_GUI)); // 4J was L"/gui/gui.png" + // glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture( + // TN_GUI_GUI)); // 4J was L"/gui/gui.png" + minecraft->textures->bindTexture(&GUI_GUI_LOCATION); glColor4f(1, 1, 1, 1); bool hovered = xm >= x && ym >= y && xm < x + w && ym < y + h; diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h index ace3ec4bf..eb7ddc0dd 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h @@ -17,8 +17,8 @@ public: AbstractContainerMenu* menu; AbstractContainerScreen(AbstractContainerMenu* menu); - virtual void init(); - virtual void render(int xm, int ym, float a); + virtual void init() override; + virtual void render(int xm, int ym, float a) override; protected: virtual void renderLabels(); @@ -33,13 +33,13 @@ private: virtual void renderSlot(Slot* slot); protected: - virtual void mouseClicked(int x, int y, int buttonNum); - virtual void mouseReleased(int x, int y, int buttonNum); - virtual void keyPressed(wchar_t eventCharacter, int eventKey); + virtual void mouseClicked(int x, int y, int buttonNum) override; + virtual void mouseReleased(int x, int y, int buttonNum) override; + virtual void keyPressed(wchar_t eventCharacter, int eventKey) override; public: - virtual void removed(); + virtual void removed() override; virtual void slotsChanged(std::shared_ptr container); - virtual bool isPauseScreen(); - virtual void tick(); -}; + virtual bool isPauseScreen() override; + virtual void tick() override; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/AchievementScreen.h b/Minecraft.Client/UI/Screens/AchievementScreen.h index 398ab950d..9c8a1223d 100644 --- a/Minecraft.Client/UI/Screens/AchievementScreen.h +++ b/Minecraft.Client/UI/Screens/AchievementScreen.h @@ -43,20 +43,20 @@ public: using Screen::keyPressed; AchievementScreen(StatsCounter* statsCounter); - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; virtual void keyPressed(char eventCharacter, int eventKey); public: - virtual void render(int mouseX, int mouseY, float a); - virtual void tick(); + virtual void render(int mouseX, int mouseY, float a) override; + virtual void tick() override; protected: virtual void renderLabels(); virtual void renderBg(int xm, int ym, float a); public: - virtual bool isPauseScreen(); + virtual bool isPauseScreen() override; }; diff --git a/Minecraft.Client/UI/Screens/ChatScreen.h b/Minecraft.Client/UI/Screens/ChatScreen.h index a1d508fab..ce20f5a62 100644 --- a/Minecraft.Client/UI/Screens/ChatScreen.h +++ b/Minecraft.Client/UI/Screens/ChatScreen.h @@ -10,19 +10,19 @@ private: public: ChatScreen(); // 4J added - virtual void init(); - virtual void removed(); - virtual void tick(); + virtual void init() override; + virtual void removed() override; + virtual void tick() override; private: static const std::wstring allowedChars; protected: - void keyPressed(wchar_t ch, int eventKey); + void keyPressed(wchar_t ch, int eventKey) override; public: - void render(int xm, int ym, float a); + void render(int xm, int ym, float a) override; protected: - void mouseClicked(int x, int y, int buttonNum); + void mouseClicked(int x, int y, int buttonNum) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/ConfirmScreen.h b/Minecraft.Client/UI/Screens/ConfirmScreen.h index d0e337129..d400c70cd 100644 --- a/Minecraft.Client/UI/Screens/ConfirmScreen.h +++ b/Minecraft.Client/UI/Screens/ConfirmScreen.h @@ -16,11 +16,11 @@ public: ConfirmScreen(Screen* parent, const std::wstring& title1, const std::wstring& title2, const std::wstring& yesButton, const std::wstring& noButton, int id); - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/ConnectScreen.h b/Minecraft.Client/UI/Screens/ConnectScreen.h index bb817198c..f99add276 100644 --- a/Minecraft.Client/UI/Screens/ConnectScreen.h +++ b/Minecraft.Client/UI/Screens/ConnectScreen.h @@ -10,17 +10,17 @@ private: public: ConnectScreen(Minecraft* minecraft, const std::wstring& ip, int port); - virtual void tick(); + virtual void tick() override; protected: virtual void keyPressed(char eventCharacter, int eventKey); public: - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/ContainerScreen.h b/Minecraft.Client/UI/Screens/ContainerScreen.h index 2e37eafa7..fca18c2f3 100644 --- a/Minecraft.Client/UI/Screens/ContainerScreen.h +++ b/Minecraft.Client/UI/Screens/ContainerScreen.h @@ -14,6 +14,6 @@ public: std::shared_ptr container); protected: - virtual void renderLabels(); - virtual void renderBg(float a); + virtual void renderLabels() override; + virtual void renderBg(float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/ControlsScreen.h b/Minecraft.Client/UI/Screens/ControlsScreen.h index c5079aba1..3132fb451 100644 --- a/Minecraft.Client/UI/Screens/ControlsScreen.h +++ b/Minecraft.Client/UI/Screens/ControlsScreen.h @@ -25,12 +25,12 @@ private: int getLeftScreenPosition(); public: - void init(); + void init() override; protected: - void buttonClicked(Button* button); - void keyPressed(wchar_t eventCharacter, int eventKey); + void buttonClicked(Button* button) override; + void keyPressed(wchar_t eventCharacter, int eventKey) override; public: - void render(int xm, int ym, float a); + void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/CraftingScreen.cpp b/Minecraft.Client/UI/Screens/CraftingScreen.cpp index d9c3f4759..00b90e8c4 100644 --- a/Minecraft.Client/UI/Screens/CraftingScreen.cpp +++ b/Minecraft.Client/UI/Screens/CraftingScreen.cpp @@ -2,11 +2,18 @@ #include "CraftingScreen.h" #include "../../Textures/Textures.h" #include "../../Player/MultiPlayerLocalPlayer.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" #include "../../../Minecraft.World/Headers/net.minecraft.world.inventory.h" +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_CRAFTING_LOCATION = ResourceLocation(TN_GUI_CRAFTING); +#endif + CraftingScreen::CraftingScreen(std::shared_ptr inventory, Level* level, int x, int y, int z) - : AbstractContainerScreen(new CraftingMenu(inventory, level, x, y, z)) {} + : AbstractContainerScreen(new CraftingMenu(inventory, level, x, y, z)) { + this->inventory = inventory; + } void CraftingScreen::removed() { AbstractContainerScreen::removed(); @@ -14,16 +21,15 @@ void CraftingScreen::removed() { } void CraftingScreen::renderLabels() { - font->draw(L"Crafting", 8 + 16 + 4, 2 + 2 + 2, 0x404040); - font->draw(L"Inventory", 8, imageHeight - 96 + 2, 0x404040); + font->draw(Language::getInstance()->getElement(L"container.crafting"), 8 + 16 + 4, 2 + 2 + 2, 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); } void CraftingScreen::renderBg(float a) { // 4J Unused #ifdef ENABLE_JAVA_GUIS - int tex = minecraft->textures->loadTexture(TN_GUI_CRAFTING); glColor4f(1, 1, 1, 1); - minecraft->textures->bind(tex); + minecraft->textures->bindTexture(&GUI_CRAFTING_LOCATION); int xo = (width - imageWidth) / 2; int yo = (height - imageHeight) / 2; this->blit(xo, yo, 0, 0, imageWidth, imageHeight); diff --git a/Minecraft.Client/UI/Screens/CraftingScreen.h b/Minecraft.Client/UI/Screens/CraftingScreen.h index ba59f871d..77eb664c7 100644 --- a/Minecraft.Client/UI/Screens/CraftingScreen.h +++ b/Minecraft.Client/UI/Screens/CraftingScreen.h @@ -4,12 +4,15 @@ class Inventory; class Level; class CraftingScreen : public AbstractContainerScreen { +private: + std::shared_ptr inventory; + public: CraftingScreen(std::shared_ptr inventory, Level* level, int x, int y, int z); - virtual void removed(); + virtual void removed() override; protected: - virtual void renderLabels(); - virtual void renderBg(float a); + virtual void renderLabels() override; + virtual void renderBg(float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/CreateWorldScreen.h b/Minecraft.Client/UI/Screens/CreateWorldScreen.h index 71a18e4d5..ba38b2ec3 100644 --- a/Minecraft.Client/UI/Screens/CreateWorldScreen.h +++ b/Minecraft.Client/UI/Screens/CreateWorldScreen.h @@ -31,8 +31,8 @@ private: public: CreateWorldScreen(Screen* lastScreen); - virtual void tick(); - virtual void init(); + virtual void tick() override; + virtual void init() override; private: void updateResultFolder(); @@ -41,16 +41,16 @@ private: public: static std::wstring findAvailableFolderName(LevelStorageSource* levelSource, const std::wstring& folder); - virtual void removed(); + virtual void removed() override; protected: - virtual void buttonClicked(Button* button); - virtual void keyPressed(wchar_t ch, int eventKey); - virtual void mouseClicked(int x, int y, int buttonNum); + virtual void buttonClicked(Button* button) override; + virtual void keyPressed(wchar_t ch, int eventKey) override; + virtual void mouseClicked(int x, int y, int buttonNum) override; public: - virtual void render(int xm, int ym, float a); - virtual void tabPressed(); + virtual void render(int xm, int ym, float a) override; + virtual void tabPressed() override; private: int m_iGameModeId; diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h index 987ec9a88..6a4ba7847 100644 --- a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h @@ -60,21 +60,19 @@ public: }; public: - CreativeInventoryScreen(std::shared_ptr player); - virtual void removed(); - virtual void init(); - virtual void containerTick(); - virtual void tick(); - virtual void updateEvents(); - virtual void keyPressed(wchar_t eventCharacter, int eventKey); - virtual void mouseClicked(int x, int y, int buttonNum); - virtual void mouseReleased(int x, int y, int buttonNum); - virtual void render(int xm, int ym, float a); - + CreativeInventoryScreen(std::shared_ptr player); + virtual void removed() override; + virtual void init() override; + virtual void containerTick(); + virtual void tick() override; + virtual void updateEvents() override; + virtual void keyPressed(wchar_t eventCharacter, int eventKey) override; + virtual void mouseClicked(int x, int y, int buttonNum) override; + virtual void mouseReleased(int x, int y, int buttonNum) override; + virtual void render(int xm, int ym, float a) override; protected: - virtual void renderLabels(); - virtual void renderBg(float a); - + virtual void renderLabels() override; + virtual void renderBg(float a) override; private: void setCurrentCreativeTab(int tab); void selectTab(int tab); diff --git a/Minecraft.Client/UI/Screens/DeathScreen.h b/Minecraft.Client/UI/Screens/DeathScreen.h index 976832afb..382f0e5b6 100644 --- a/Minecraft.Client/UI/Screens/DeathScreen.h +++ b/Minecraft.Client/UI/Screens/DeathScreen.h @@ -3,13 +3,13 @@ class DeathScreen : public Screen { public: - virtual void init(); + virtual void init() override; protected: virtual void keyPressed(char eventCharacter, int eventKey); - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); - virtual bool isPauseScreen(); + virtual void render(int xm, int ym, float a) override; + virtual bool isPauseScreen() override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/DisconnectedScreen.h b/Minecraft.Client/UI/Screens/DisconnectedScreen.h index 4e49685d7..1dcc1151d 100644 --- a/Minecraft.Client/UI/Screens/DisconnectedScreen.h +++ b/Minecraft.Client/UI/Screens/DisconnectedScreen.h @@ -8,7 +8,7 @@ private: public: DisconnectedScreen(const std::wstring& title, const std::wstring reason, void* reasonObjects, ...); - virtual void tick(); + virtual void tick() override; protected: using Screen::keyPressed; @@ -16,11 +16,11 @@ protected: virtual void keyPressed(char eventCharacter, int eventKey); public: - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; diff --git a/Minecraft.Client/UI/Screens/EnchantmentScreen.h b/Minecraft.Client/UI/Screens/EnchantmentScreen.h index 79cf8786a..96a7a6af6 100644 --- a/Minecraft.Client/UI/Screens/EnchantmentScreen.h +++ b/Minecraft.Client/UI/Screens/EnchantmentScreen.h @@ -9,13 +9,13 @@ public: int y, int z); virtual ~EnchantmentScreen(); - void init(); - void removed(); - void tick(); - void mouseClicked(int mouseX, int mouseY, int buttonNum); - void renderLabels(); - void renderBg(float a); - void render(int xm, int ym, float a); + void init() override; + void removed() override; + void tick() override; + void mouseClicked(int mouseX, int mouseY, int buttonNum) override; + void renderLabels() override; + void renderBg(float a) override; + void render(int xm, int ym, float a) override; private: std::shared_ptr inventory; diff --git a/Minecraft.Client/UI/Screens/ErrorScreen.h b/Minecraft.Client/UI/Screens/ErrorScreen.h index b89b64e5d..4c46d36ff 100644 --- a/Minecraft.Client/UI/Screens/ErrorScreen.h +++ b/Minecraft.Client/UI/Screens/ErrorScreen.h @@ -7,9 +7,9 @@ private: public: ErrorScreen(const std::wstring& title, const std::wstring& message); - virtual void init(); - virtual void render(int xm, int ym, float a); + virtual void init() override; + virtual void render(int xm, int ym, float a) override; protected: - virtual void keyPressed(wchar_t eventCharacter, int eventKey); + virtual void keyPressed(wchar_t eventCharacter, int eventKey) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/FurnaceScreen.cpp b/Minecraft.Client/UI/Screens/FurnaceScreen.cpp index c12ca82dc..04d0ca346 100644 --- a/Minecraft.Client/UI/Screens/FurnaceScreen.cpp +++ b/Minecraft.Client/UI/Screens/FurnaceScreen.cpp @@ -6,23 +6,27 @@ #include "../../../Minecraft.World/Headers/net.minecraft.world.inventory.h" #include "../../../Minecraft.World/Blocks/TileEntities/FurnaceTileEntity.h" +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_FURNACE_LOCATION = ResourceLocation(TN_GUI_FURNACE); +#endif + FurnaceScreen::FurnaceScreen(std::shared_ptr inventory, std::shared_ptr furnace) : AbstractContainerScreen(new FurnaceMenu(inventory, furnace)) { + this->inventory = inventory; this->furnace = furnace; } void FurnaceScreen::renderLabels() { - font->draw(L"Furnace", 16 + 4 + 40, 2 + 2 + 2, 0x404040); - font->draw(L"Inventory", 8, imageHeight - 96 + 2, 0x404040); + font->draw(furnace->getName(), 16 + 4 + 40, 2 + 2 + 2, 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); } void FurnaceScreen::renderBg(float a) { // 4J Unused #ifdef ENABLE_JAVA_GUIS - int tex = minecraft->textures->loadTexture(TN_GUI_FURNACE); glColor4f(1, 1, 1, 1); - minecraft->textures->bind(tex); + minecraft->textures->bindTexture(&GUI_FURNACE_LOCATION); int xo = (width - imageWidth) / 2; int yo = (height - imageHeight) / 2; this->blit(xo, yo, 0, 0, imageWidth, imageHeight); diff --git a/Minecraft.Client/UI/Screens/FurnaceScreen.h b/Minecraft.Client/UI/Screens/FurnaceScreen.h index 6908524ec..cdaab078e 100644 --- a/Minecraft.Client/UI/Screens/FurnaceScreen.h +++ b/Minecraft.Client/UI/Screens/FurnaceScreen.h @@ -6,6 +6,7 @@ class Inventory; class FurnaceScreen : public AbstractContainerScreen { private: + std::shared_ptr inventory; std::shared_ptr furnace; public: @@ -13,6 +14,6 @@ public: std::shared_ptr furnace); protected: - virtual void renderLabels(); - virtual void renderBg(float a); + virtual void renderLabels() override; + virtual void renderBg(float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/HopperScreen.h b/Minecraft.Client/UI/Screens/HopperScreen.h index eb1bb752f..e3e3d1eb4 100644 --- a/Minecraft.Client/UI/Screens/HopperScreen.h +++ b/Minecraft.Client/UI/Screens/HopperScreen.h @@ -12,8 +12,8 @@ public: std::shared_ptr hopper); protected: - virtual void renderLabels(); - virtual void renderBg(float a); + virtual void renderLabels() override; + virtual void renderBg(float a) override; private: std::shared_ptr inventory; diff --git a/Minecraft.Client/UI/Screens/InBedChatScreen.h b/Minecraft.Client/UI/Screens/InBedChatScreen.h index 3f8625eba..e068f5ce4 100644 --- a/Minecraft.Client/UI/Screens/InBedChatScreen.h +++ b/Minecraft.Client/UI/Screens/InBedChatScreen.h @@ -7,17 +7,17 @@ private: static const int WAKE_UP_BUTTON = 1; public: - virtual void init(); - virtual void removed(); + virtual void init() override; + virtual void removed() override; protected: - virtual void keyPressed(wchar_t ch, int eventKey); + virtual void keyPressed(wchar_t ch, int eventKey) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; private: void sendWakeUp(); diff --git a/Minecraft.Client/UI/Screens/InventoryScreen.h b/Minecraft.Client/UI/Screens/InventoryScreen.h index 0c7080014..d2a5307c7 100644 --- a/Minecraft.Client/UI/Screens/InventoryScreen.h +++ b/Minecraft.Client/UI/Screens/InventoryScreen.h @@ -6,18 +6,18 @@ class Button; class InventoryScreen : public AbstractContainerScreen { public: InventoryScreen(std::shared_ptr player); - virtual void init(); + virtual void init() override; protected: - virtual void renderLabels(); + virtual void renderLabels() override; private: float xMouse, yMouse; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; protected: - virtual void renderBg(float a); - virtual void buttonClicked(Button* button); + virtual void renderBg(float a) override; + virtual void buttonClicked(Button* button) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/JoinMultiplayerScreen.h b/Minecraft.Client/UI/Screens/JoinMultiplayerScreen.h index c04c0d41f..78bce6115 100644 --- a/Minecraft.Client/UI/Screens/JoinMultiplayerScreen.h +++ b/Minecraft.Client/UI/Screens/JoinMultiplayerScreen.h @@ -10,20 +10,20 @@ private: public: JoinMultiplayerScreen(Screen* lastScreen); - virtual void tick(); - virtual void init(); - virtual void removed(); + virtual void tick() override; + virtual void init() override; + virtual void removed() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; private: virtual int parseInt(const std::wstring& str, int def); protected: - virtual void keyPressed(wchar_t ch, int eventKey); - virtual void mouseClicked(int x, int y, int buttonNum); + virtual void keyPressed(wchar_t ch, int eventKey) override; + virtual void mouseClicked(int x, int y, int buttonNum) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/MessageScreen.h b/Minecraft.Client/UI/Screens/MessageScreen.h index 42a75aa78..62b4ee280 100644 --- a/Minecraft.Client/UI/Screens/MessageScreen.h +++ b/Minecraft.Client/UI/Screens/MessageScreen.h @@ -14,11 +14,11 @@ protected: virtual void keyPressed(char eventCharacter, int eventKey); public: - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/NameEntryScreen.h b/Minecraft.Client/UI/Screens/NameEntryScreen.h index e01c0b428..2f0babddd 100644 --- a/Minecraft.Client/UI/Screens/NameEntryScreen.h +++ b/Minecraft.Client/UI/Screens/NameEntryScreen.h @@ -15,9 +15,9 @@ private: public: NameEntryScreen(Screen* lastScreen, const std::wstring& oldName, int slot); - virtual void init(); - virtual void removed(); - virtual void tick(); + virtual void init() override; + virtual void removed() override; + virtual void tick() override; protected: virtual void buttonClicked(Button button); @@ -26,8 +26,8 @@ private: static const std::wstring allowedChars; protected: - virtual void keyPressed(wchar_t ch, int eventKey); + virtual void keyPressed(wchar_t ch, int eventKey) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/OptionsScreen.h b/Minecraft.Client/UI/Screens/OptionsScreen.h index 9e33e3dfe..509162eff 100644 --- a/Minecraft.Client/UI/Screens/OptionsScreen.h +++ b/Minecraft.Client/UI/Screens/OptionsScreen.h @@ -16,11 +16,11 @@ private: public: OptionsScreen(Screen* lastScreen, Options* options); - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/PauseScreen.h b/Minecraft.Client/UI/Screens/PauseScreen.h index 375d03eb6..ef67c9f14 100644 --- a/Minecraft.Client/UI/Screens/PauseScreen.h +++ b/Minecraft.Client/UI/Screens/PauseScreen.h @@ -8,15 +8,15 @@ private: public: PauseScreen(); // 4J added - virtual void init(); + virtual void init() override; static void exitWorld(Minecraft* minecraft, bool save); protected: using Screen::buttonClicked; - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void tick(); - virtual void render(int xm, int ym, float a); + virtual void tick() override; + virtual void render(int xm, int ym, float a) override; }; diff --git a/Minecraft.Client/UI/Screens/ReceivingLevelScreen.h b/Minecraft.Client/UI/Screens/ReceivingLevelScreen.h index ede53e3a2..fb54bec4d 100644 --- a/Minecraft.Client/UI/Screens/ReceivingLevelScreen.h +++ b/Minecraft.Client/UI/Screens/ReceivingLevelScreen.h @@ -16,12 +16,12 @@ protected: virtual void keyPressed(char eventCharacter, int eventKey); public: - virtual void init(); - virtual void tick(); + virtual void init() override; + virtual void tick() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; diff --git a/Minecraft.Client/UI/Screens/RenameWorldScreen.h b/Minecraft.Client/UI/Screens/RenameWorldScreen.h index 1bf433dd3..11f5ed41d 100644 --- a/Minecraft.Client/UI/Screens/RenameWorldScreen.h +++ b/Minecraft.Client/UI/Screens/RenameWorldScreen.h @@ -11,15 +11,15 @@ private: public: RenameWorldScreen(Screen* lastScreen, const std::wstring& levelId); - virtual void tick(); - virtual void init(); - virtual void removed(); + virtual void tick() override; + virtual void init() override; + virtual void removed() override; protected: - virtual void buttonClicked(Button* button); - virtual void keyPressed(wchar_t ch, int eventKey); - virtual void mouseClicked(int x, int y, int buttonNum); + virtual void buttonClicked(Button* button) override; + virtual void keyPressed(wchar_t ch, int eventKey) override; + virtual void mouseClicked(int x, int y, int buttonNum) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/SelectWorldScreen.h b/Minecraft.Client/UI/Screens/SelectWorldScreen.h index 5531d08e7..77f8dd120 100644 --- a/Minecraft.Client/UI/Screens/SelectWorldScreen.h +++ b/Minecraft.Client/UI/Screens/SelectWorldScreen.h @@ -41,7 +41,7 @@ private: public: SelectWorldScreen(Screen* lastScreen); - virtual void init(); + virtual void init() override; private: void loadLevelList(); @@ -54,12 +54,12 @@ public: virtual void postInit(); protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: void worldSelected(int id); - void confirmResult(bool result, int id); - virtual void render(int xm, int ym, float a); + void confirmResult(bool result, int id) override; + virtual void render(int xm, int ym, float a) override; class WorldSelectionList : public ScrolledSelectionList { public: diff --git a/Minecraft.Client/UI/Screens/StatsScreen.h b/Minecraft.Client/UI/Screens/StatsScreen.h index 91b0f5c40..dc567006d 100644 --- a/Minecraft.Client/UI/Screens/StatsScreen.h +++ b/Minecraft.Client/UI/Screens/StatsScreen.h @@ -31,14 +31,14 @@ private: public: StatsScreen(Screen* lastScreen, StatsCounter* stats); - virtual void init(); + virtual void init() override; virtual void postInit(); protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; class GeneralStatisticsList : public ScrolledSelectionList { protected: diff --git a/Minecraft.Client/UI/Screens/TextEditScreen.h b/Minecraft.Client/UI/Screens/TextEditScreen.h index 4373bd9c9..0c0af219b 100644 --- a/Minecraft.Client/UI/Screens/TextEditScreen.h +++ b/Minecraft.Client/UI/Screens/TextEditScreen.h @@ -13,19 +13,19 @@ private: public: TextEditScreen(std::shared_ptr sign); - virtual void init(); - virtual void removed(); - virtual void tick(); + virtual void init() override; + virtual void removed() override; + virtual void tick() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; private: static const std::wstring allowedChars; protected: - virtual void keyPressed(wchar_t ch, int eventKey); + virtual void keyPressed(wchar_t ch, int eventKey) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/TitleScreen.h b/Minecraft.Client/UI/Screens/TitleScreen.h index fa29afb52..5df5d4984 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.h +++ b/Minecraft.Client/UI/Screens/TitleScreen.h @@ -33,17 +33,17 @@ private: public: TitleScreen(); - virtual void tick(); + virtual void tick() override; protected: - virtual void keyPressed(wchar_t eventCharacter, int eventKey); + virtual void keyPressed(wchar_t eventCharacter, int eventKey) override; public: - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/VideoSettingsScreen.h b/Minecraft.Client/UI/Screens/VideoSettingsScreen.h index 7075523b2..bbeb74698 100644 --- a/Minecraft.Client/UI/Screens/VideoSettingsScreen.h +++ b/Minecraft.Client/UI/Screens/VideoSettingsScreen.h @@ -14,11 +14,11 @@ private: public: VideoSettingsScreen(Screen* lastScreen, Options* options); - virtual void init(); + virtual void init() override; protected: - virtual void buttonClicked(Button* button); + virtual void buttonClicked(Button* button) override; public: - virtual void render(int xm, int ym, float a); + virtual void render(int xm, int ym, float a) override; }; \ No newline at end of file diff --git a/Minecraft.Client/UI/SlideButton.h b/Minecraft.Client/UI/SlideButton.h index 28e7726ef..f2db25366 100644 --- a/Minecraft.Client/UI/SlideButton.h +++ b/Minecraft.Client/UI/SlideButton.h @@ -16,10 +16,10 @@ public: const std::wstring& msg, float value); protected: - virtual int getYImage(bool hovered); - virtual void renderBg(Minecraft* minecraft, int xm, int ym); + virtual int getYImage(bool hovered) override; + virtual void renderBg(Minecraft* minecraft, int xm, int ym) override; public: - virtual bool clicked(Minecraft* minecraft, int mx, int my); - virtual void released(int mx, int my); + virtual bool clicked(Minecraft* minecraft, int mx, int my) override; + virtual void released(int mx, int my) override; }; \ No newline at end of file diff --git a/Minecraft.World/Containers/RepairContainer.h b/Minecraft.World/Containers/RepairContainer.h index edb7a6756..1494a1099 100644 --- a/Minecraft.World/Containers/RepairContainer.h +++ b/Minecraft.World/Containers/RepairContainer.h @@ -11,6 +11,6 @@ private: public: RepairContainer(AnvilMenu* menu, int name, bool customName, int size); - void setChanged(); - bool canPlaceItem(int slot, std::shared_ptr item); + void setChanged() override; + bool canPlaceItem(int slot, std::shared_ptr item) override; }; \ No newline at end of file diff --git a/Minecraft.World/Containers/RepairResultSlot.h b/Minecraft.World/Containers/RepairResultSlot.h index 1f8f0f4ea..bc646aa9a 100644 --- a/Minecraft.World/Containers/RepairResultSlot.h +++ b/Minecraft.World/Containers/RepairResultSlot.h @@ -14,9 +14,9 @@ public: std::shared_ptr container, int slot, int x, int y); - bool mayPlace(std::shared_ptr item); - bool mayPickup(std::shared_ptr player); + bool mayPlace(std::shared_ptr item) override; + bool mayPickup(std::shared_ptr player) override; void onTake(std::shared_ptr player, - std::shared_ptr carried); - virtual bool mayCombine(std::shared_ptr item); // 4J Added + std::shared_ptr carried) override; + virtual bool mayCombine(std::shared_ptr item) override; // 4J Added }; \ No newline at end of file diff --git a/Minecraft.World/Containers/ResultContainer.h b/Minecraft.World/Containers/ResultContainer.h index 69ed8ed1e..ea2446558 100644 --- a/Minecraft.World/Containers/ResultContainer.h +++ b/Minecraft.World/Containers/ResultContainer.h @@ -10,19 +10,19 @@ public: // 4J Stu Added a ctor to init items ResultContainer(); - virtual unsigned int getContainerSize(); - virtual std::shared_ptr getItem(unsigned int slot); - virtual std::wstring getName(); - virtual std::wstring getCustomName(); - virtual bool hasCustomName(); + virtual unsigned int getContainerSize() override; + virtual std::shared_ptr getItem(unsigned int slot) override; + virtual std::wstring getName() override; + virtual std::wstring getCustomName() override; + virtual bool hasCustomName() override; virtual std::shared_ptr removeItem(unsigned int slot, - int count); - virtual std::shared_ptr removeItemNoUpdate(int slot); - virtual void setItem(unsigned int slot, std::shared_ptr item); - virtual int getMaxStackSize(); - virtual void setChanged(); - virtual bool stillValid(std::shared_ptr player); - virtual void startOpen() {} // TODO Auto-generated method stub - virtual void stopOpen() {} // TODO Auto-generated method stub - virtual bool canPlaceItem(int slot, std::shared_ptr item); + int count) override; + virtual std::shared_ptr removeItemNoUpdate(int slot) override; + virtual void setItem(unsigned int slot, std::shared_ptr item) override; + virtual int getMaxStackSize() override; + virtual void setChanged() override; + virtual bool stillValid(std::shared_ptr player) override; + virtual void startOpen() override {} // TODO Auto-generated method stub + virtual void stopOpen() override {} // TODO Auto-generated method stub + virtual bool canPlaceItem(int slot, std::shared_ptr item) override; }; \ No newline at end of file diff --git a/Minecraft.World/Containers/ResultSlot.h b/Minecraft.World/Containers/ResultSlot.h index b2a0347f4..473cba387 100644 --- a/Minecraft.World/Containers/ResultSlot.h +++ b/Minecraft.World/Containers/ResultSlot.h @@ -14,15 +14,15 @@ public: std::shared_ptr container, int id, int x, int y); virtual ~ResultSlot() {} - virtual bool mayPlace(std::shared_ptr item); - virtual std::shared_ptr remove(int c); + virtual bool mayPlace(std::shared_ptr item) override; + virtual std::shared_ptr remove(int c) override; protected: - virtual void onQuickCraft(std::shared_ptr picked, int count); - virtual void checkTakeAchievements(std::shared_ptr carried); + virtual void onQuickCraft(std::shared_ptr picked, int count) override; + virtual void checkTakeAchievements(std::shared_ptr carried) override; public: virtual void onTake(std::shared_ptr player, - std::shared_ptr carried); - virtual bool mayCombine(std::shared_ptr item); // 4J Added + std::shared_ptr carried) override; + virtual bool mayCombine(std::shared_ptr item) override; // 4J Added }; \ No newline at end of file diff --git a/Minecraft.World/Containers/SimpleContainer.h b/Minecraft.World/Containers/SimpleContainer.h index 74392b3f3..9ca5e15f9 100644 --- a/Minecraft.World/Containers/SimpleContainer.h +++ b/Minecraft.World/Containers/SimpleContainer.h @@ -19,20 +19,20 @@ public: virtual void addListener(net_minecraft_world::ContainerListener* listener); virtual void removeListener( net_minecraft_world::ContainerListener* listener); - virtual std::shared_ptr getItem(unsigned int slot); + virtual std::shared_ptr getItem(unsigned int slot) override; virtual std::shared_ptr removeItem(unsigned int slot, - int count); - virtual std::shared_ptr removeItemNoUpdate(int slot); - virtual void setItem(unsigned int slot, std::shared_ptr item); - virtual unsigned int getContainerSize(); - virtual std::wstring getName(); - virtual std::wstring getCustomName(); - virtual bool hasCustomName(); + int count) override; + virtual std::shared_ptr removeItemNoUpdate(int slot) override; + virtual void setItem(unsigned int slot, std::shared_ptr item) override; + virtual unsigned int getContainerSize() override; + virtual std::wstring getName() override; + virtual std::wstring getCustomName() override; + virtual bool hasCustomName() override; virtual void setCustomName(const std::wstring& name); - virtual int getMaxStackSize(); - virtual void setChanged(); - virtual bool stillValid(std::shared_ptr player); - virtual void startOpen() {} // TODO Auto-generated method stub - virtual void stopOpen() {} // TODO Auto-generated method stub - virtual bool canPlaceItem(int slot, std::shared_ptr item); + virtual int getMaxStackSize() override; + virtual void setChanged() override; + virtual bool stillValid(std::shared_ptr player) override; + virtual void startOpen() override {} // TODO Auto-generated method stub + virtual void stopOpen() override {} // TODO Auto-generated method stub + virtual bool canPlaceItem(int slot, std::shared_ptr item) override; }; \ No newline at end of file diff --git a/Minecraft.World/Containers/TrapMenu.h b/Minecraft.World/Containers/TrapMenu.h index 03b818380..c4f38b626 100644 --- a/Minecraft.World/Containers/TrapMenu.h +++ b/Minecraft.World/Containers/TrapMenu.h @@ -18,7 +18,7 @@ public: TrapMenu(std::shared_ptr inventory, std::shared_ptr trap); - virtual bool stillValid(std::shared_ptr player); + virtual bool stillValid(std::shared_ptr player) override; virtual std::shared_ptr quickMoveStack( - std::shared_ptr player, int slotIndex); + std::shared_ptr player, int slotIndex) override; }; \ No newline at end of file From 02845d9a5f8dbaee930b466edbf94bdd45968d4f Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 20:37:52 +0300 Subject: [PATCH 20/40] chore: fmt --- Minecraft.Client/UI/Button.cpp | 2 +- .../UI/Screens/AchievementPopup.cpp | 26 ++++++++----------- .../UI/Screens/CraftingScreen.cpp | 7 ++--- .../UI/Screens/SelectWorldScreen.cpp | 14 ++++++---- .../UI/Screens/SelectWorldScreen.h | 2 +- 5 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Minecraft.Client/UI/Button.cpp b/Minecraft.Client/UI/Button.cpp index b5759fa1f..4dd859f20 100644 --- a/Minecraft.Client/UI/Button.cpp +++ b/Minecraft.Client/UI/Button.cpp @@ -43,7 +43,7 @@ void Button::render(Minecraft* minecraft, int xm, int ym) { Font* font = minecraft->font; // glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture( - // TN_GUI_GUI)); // 4J was L"/gui/gui.png" + // TN_GUI_GUI)); // 4J was L"/gui/gui.png" minecraft->textures->bindTexture(&GUI_GUI_LOCATION); glColor4f(1, 1, 1, 1); diff --git a/Minecraft.Client/UI/Screens/AchievementPopup.cpp b/Minecraft.Client/UI/Screens/AchievementPopup.cpp index de59ec75b..f518da342 100644 --- a/Minecraft.Client/UI/Screens/AchievementPopup.cpp +++ b/Minecraft.Client/UI/Screens/AchievementPopup.cpp @@ -68,14 +68,14 @@ void AchievementPopup::prepareWindow() { void AchievementPopup::render() { // 4J Unused #if ENABLE_JAVA_GUIS - if (Minecraft::warezTime > 0) - { + if (Minecraft::warezTime > 0) { glDisable(GL_DEPTH_TEST); glDepthMask(false); Lighting::turnOff(); prepareWindow(); - std::wstring title = L"Minecraft " + SharedConstants::VERSION_STRING + L" Unlicensed Copy :("; + std::wstring title = L"Minecraft " + SharedConstants::VERSION_STRING + + L" Unlicensed Copy :("; std::wstring msg1 = L"(Or logged in from another location)"; std::wstring msg2 = L"Purchase at minecraft.net"; @@ -89,16 +89,12 @@ void AchievementPopup::render() { if (ach == NULL || startTime == 0) return; double time = (System::currentTimeMillis() - startTime) / 3000.0; - if (isHelper) - { - } - else if (!isHelper && (time < 0 || time > 1)) - { + if (isHelper) { + } else if (!isHelper && (time < 0 || time > 1)) { startTime = 0; return; } - prepareWindow(); glDisable(GL_DEPTH_TEST); glDepthMask(false); @@ -112,7 +108,7 @@ void AchievementPopup::render() { yo = yo * yo; int xx = width - 160; - int yy = 0 - (int) (yo * 36); + int yy = 0 - (int)(yo * 36); int tex = mc->textures->loadTexture(TN_ACHIEVEMENT_BG); glColor4f(1, 1, 1, 1); glEnable(GL_TEXTURE_2D); @@ -122,13 +118,13 @@ void AchievementPopup::render() { blit(xx, yy, 96, 202, 160, 32); // if (isHelper) - // { + // { // mc->font->drawWordWrap(desc, xx + 30, yy + 7, 120, 0xffffffff); // } - // else - // { - mc->font->draw(title, xx + 30, yy + 7, 0xffffff00); - mc->font->draw(desc, xx + 30, yy + 18, 0xffffffff); + // else + // { + mc->font->draw(title, xx + 30, yy + 7, 0xffffff00); + mc->font->draw(desc, xx + 30, yy + 18, 0xffffffff); // } glPushMatrix(); diff --git a/Minecraft.Client/UI/Screens/CraftingScreen.cpp b/Minecraft.Client/UI/Screens/CraftingScreen.cpp index 00b90e8c4..14b9aaae5 100644 --- a/Minecraft.Client/UI/Screens/CraftingScreen.cpp +++ b/Minecraft.Client/UI/Screens/CraftingScreen.cpp @@ -12,8 +12,8 @@ ResourceLocation GUI_CRAFTING_LOCATION = ResourceLocation(TN_GUI_CRAFTING); CraftingScreen::CraftingScreen(std::shared_ptr inventory, Level* level, int x, int y, int z) : AbstractContainerScreen(new CraftingMenu(inventory, level, x, y, z)) { - this->inventory = inventory; - } + this->inventory = inventory; +} void CraftingScreen::removed() { AbstractContainerScreen::removed(); @@ -21,7 +21,8 @@ void CraftingScreen::removed() { } void CraftingScreen::renderLabels() { - font->draw(Language::getInstance()->getElement(L"container.crafting"), 8 + 16 + 4, 2 + 2 + 2, 0x404040); + font->draw(Language::getInstance()->getElement(L"container.crafting"), + 8 + 16 + 4, 2 + 2 + 2, 0x404040); font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); } diff --git a/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp b/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp index aa410280b..8c181623d 100644 --- a/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp +++ b/Minecraft.Client/UI/Screens/SelectWorldScreen.cpp @@ -127,7 +127,9 @@ void SelectWorldScreen::buttonClicked(Button* button) { minecraft->setScreen( new RenameWorldScreen(this, getWorldId(selectedWorld))); } else if (button->id == BUTTON_CANCEL_ID) { - app.DebugPrintf("SelectWorldScreen::buttonClicked 'Cancel' minecraft->setScreen(lastScreen)\n"); + app.DebugPrintf( + "SelectWorldScreen::buttonClicked 'Cancel' " + "minecraft->setScreen(lastScreen)\n"); minecraft->setScreen(lastScreen); } else { worldSelectionList->buttonClicked(button); @@ -181,9 +183,9 @@ void SelectWorldScreen::render(int xm, int ym, float a) { static bool forceCreateLevel = false; if (count++ >= 100) { if (!forceCreateLevel && levelList->size() > 0) { - // 4J Stu - For some obscures reason the "delete" button is called - // "renameButton" and vice versa. - // if( levelList->size() > 2 && deleteButton->active ) + // 4J Stu - For some obscures reason the "delete" button is + // called "renameButton" and vice versa. if( levelList->size() > + // 2 && deleteButton->active ) //{ // this->selectedWorld = 2; // count = 0; @@ -205,7 +207,9 @@ void SelectWorldScreen::render(int xm, int ym, float a) { count = 0; } } else { - app.DebugPrintf("SelectWorldScreen::render minecraft->setScreen(new CreateWorldScreen(this))\n"); + app.DebugPrintf( + "SelectWorldScreen::render minecraft->setScreen(new " + "CreateWorldScreen(this))\n"); minecraft->setScreen(new CreateWorldScreen(this)); } } diff --git a/Minecraft.Client/UI/Screens/SelectWorldScreen.h b/Minecraft.Client/UI/Screens/SelectWorldScreen.h index 77f8dd120..39b56c32f 100644 --- a/Minecraft.Client/UI/Screens/SelectWorldScreen.h +++ b/Minecraft.Client/UI/Screens/SelectWorldScreen.h @@ -20,7 +20,7 @@ protected: private: // final DateFormat DATE_FORMAT = new SimpleDateFormat(); // 4J - - //removed + // removed protected: Screen* lastScreen; From 0ce9597e2e44bf4a322630167397ba61672821a4 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 20:40:28 +0300 Subject: [PATCH 21/40] chore: fmt (again) --- Minecraft.World/Containers/RepairResultSlot.h | 3 ++- Minecraft.World/Containers/ResultContainer.h | 6 ++++-- Minecraft.World/Containers/ResultSlot.h | 9 ++++++--- Minecraft.World/Containers/SimpleContainer.h | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Minecraft.World/Containers/RepairResultSlot.h b/Minecraft.World/Containers/RepairResultSlot.h index bc646aa9a..2dc6b5c17 100644 --- a/Minecraft.World/Containers/RepairResultSlot.h +++ b/Minecraft.World/Containers/RepairResultSlot.h @@ -18,5 +18,6 @@ public: bool mayPickup(std::shared_ptr player) override; void onTake(std::shared_ptr player, std::shared_ptr carried) override; - virtual bool mayCombine(std::shared_ptr item) override; // 4J Added + virtual bool mayCombine( + std::shared_ptr item) override; // 4J Added }; \ No newline at end of file diff --git a/Minecraft.World/Containers/ResultContainer.h b/Minecraft.World/Containers/ResultContainer.h index ea2446558..dc4ff7a9e 100644 --- a/Minecraft.World/Containers/ResultContainer.h +++ b/Minecraft.World/Containers/ResultContainer.h @@ -18,11 +18,13 @@ public: virtual std::shared_ptr removeItem(unsigned int slot, int count) override; virtual std::shared_ptr removeItemNoUpdate(int slot) override; - virtual void setItem(unsigned int slot, std::shared_ptr item) override; + virtual void setItem(unsigned int slot, + std::shared_ptr item) override; virtual int getMaxStackSize() override; virtual void setChanged() override; virtual bool stillValid(std::shared_ptr player) override; virtual void startOpen() override {} // TODO Auto-generated method stub virtual void stopOpen() override {} // TODO Auto-generated method stub - virtual bool canPlaceItem(int slot, std::shared_ptr item) override; + virtual bool canPlaceItem(int slot, + std::shared_ptr item) override; }; \ No newline at end of file diff --git a/Minecraft.World/Containers/ResultSlot.h b/Minecraft.World/Containers/ResultSlot.h index 473cba387..46fecd0b2 100644 --- a/Minecraft.World/Containers/ResultSlot.h +++ b/Minecraft.World/Containers/ResultSlot.h @@ -18,11 +18,14 @@ public: virtual std::shared_ptr remove(int c) override; protected: - virtual void onQuickCraft(std::shared_ptr picked, int count) override; - virtual void checkTakeAchievements(std::shared_ptr carried) override; + virtual void onQuickCraft(std::shared_ptr picked, + int count) override; + virtual void checkTakeAchievements( + std::shared_ptr carried) override; public: virtual void onTake(std::shared_ptr player, std::shared_ptr carried) override; - virtual bool mayCombine(std::shared_ptr item) override; // 4J Added + virtual bool mayCombine( + std::shared_ptr item) override; // 4J Added }; \ No newline at end of file diff --git a/Minecraft.World/Containers/SimpleContainer.h b/Minecraft.World/Containers/SimpleContainer.h index 9ca5e15f9..e9164c1eb 100644 --- a/Minecraft.World/Containers/SimpleContainer.h +++ b/Minecraft.World/Containers/SimpleContainer.h @@ -23,7 +23,8 @@ public: virtual std::shared_ptr removeItem(unsigned int slot, int count) override; virtual std::shared_ptr removeItemNoUpdate(int slot) override; - virtual void setItem(unsigned int slot, std::shared_ptr item) override; + virtual void setItem(unsigned int slot, + std::shared_ptr item) override; virtual unsigned int getContainerSize() override; virtual std::wstring getName() override; virtual std::wstring getCustomName() override; @@ -34,5 +35,6 @@ public: virtual bool stillValid(std::shared_ptr player) override; virtual void startOpen() override {} // TODO Auto-generated method stub virtual void stopOpen() override {} // TODO Auto-generated method stub - virtual bool canPlaceItem(int slot, std::shared_ptr item) override; + virtual bool canPlaceItem(int slot, + std::shared_ptr item) override; }; \ No newline at end of file From 678a186c46b5a3bb9e23806c79e8b5e595e44348 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 21:28:41 +0300 Subject: [PATCH 22/40] fix(jui): put button render method behind ifdef Fixes build error if using iggy instead of jui --- Minecraft.Client/UI/Button.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Minecraft.Client/UI/Button.cpp b/Minecraft.Client/UI/Button.cpp index 4dd859f20..ced455e3b 100644 --- a/Minecraft.Client/UI/Button.cpp +++ b/Minecraft.Client/UI/Button.cpp @@ -38,6 +38,7 @@ int Button::getYImage(bool hovered) { } void Button::render(Minecraft* minecraft, int xm, int ym) { +#ifdef ENABLE_JAVA_GUIS if (!visible) return; Font* font = minecraft->font; @@ -64,6 +65,7 @@ void Button::render(Minecraft* minecraft, int xm, int ym) { drawCenteredString(font, msg, x + w / 2, y + (h - 8) / 2, 0xe0e0e0); } } +#endif } void Button::renderBg(Minecraft* minecraft, int xm, int ym) {} From 141678ed9673187252e016d82df223dc1d9bf1a5 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 21:39:57 +0300 Subject: [PATCH 23/40] fix(jui): add Slot.h include Fixes clang builds --- Minecraft.Client/UI/Screens/EnchantmentScreen.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp b/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp index 5b66569cc..23685b1cc 100644 --- a/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp +++ b/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp @@ -9,6 +9,7 @@ #include "../../Textures/Textures.h" #include "../../../Minecraft.World/Headers/net.minecraft.locale.h" #include "../../../Minecraft.World/Containers/EnchantmentMenu.h" +#include "../../../Minecraft.World/Containers/Slot.h" #include "../../Rendering/Models/BookModel.h" #include "../../../Minecraft.Client/Minecraft.h" From ed83397c55db24bc58d6160b72537c2b50e5c0ed Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 22:57:39 +0300 Subject: [PATCH 24/40] chore(jui): remove inaccurate "unused" comments --- Minecraft.Client/UI/Screen.cpp | 1 - Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp | 1 - Minecraft.Client/UI/Screens/AchievementPopup.cpp | 1 - Minecraft.Client/UI/Screens/AchievementScreen.cpp | 1 - Minecraft.Client/UI/Screens/ContainerScreen.cpp | 1 - Minecraft.Client/UI/Screens/CraftingScreen.cpp | 1 - Minecraft.Client/UI/Screens/EnchantmentScreen.cpp | 1 - Minecraft.Client/UI/Screens/FurnaceScreen.cpp | 1 - Minecraft.Client/UI/Screens/HopperScreen.cpp | 1 - Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp | 1 - Minecraft.Client/UI/Screens/InventoryScreen.cpp | 1 - Minecraft.Client/UI/Screens/TitleScreen.cpp | 1 - Minecraft.Client/UI/Screens/TrapScreen.cpp | 1 - 13 files changed, 13 deletions(-) diff --git a/Minecraft.Client/UI/Screen.cpp b/Minecraft.Client/UI/Screen.cpp index c3260a893..d0d0a744c 100644 --- a/Minecraft.Client/UI/Screen.cpp +++ b/Minecraft.Client/UI/Screen.cpp @@ -174,7 +174,6 @@ void Screen::renderBackground(int vo) { } void Screen::renderDirtBackground(int vo) { - // 4J Unused - Iggy Flash UI renders the background on consoles #ifdef ENABLE_JAVA_GUIS glDisable(GL_LIGHTING); glDisable(GL_FOG); diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp index bf608e7ee..a1723776e 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp @@ -240,7 +240,6 @@ void AbstractContainerScreen::render(int xm, int ym, float a) { void AbstractContainerScreen::renderLabels() {} void AbstractContainerScreen::renderSlot(Slot* slot) { - // 4J Unused #if ENABLE_JAVA_GUIS int x = slot->x; int y = slot->y; diff --git a/Minecraft.Client/UI/Screens/AchievementPopup.cpp b/Minecraft.Client/UI/Screens/AchievementPopup.cpp index f518da342..512533c81 100644 --- a/Minecraft.Client/UI/Screens/AchievementPopup.cpp +++ b/Minecraft.Client/UI/Screens/AchievementPopup.cpp @@ -66,7 +66,6 @@ void AchievementPopup::prepareWindow() { } void AchievementPopup::render() { -// 4J Unused #if ENABLE_JAVA_GUIS if (Minecraft::warezTime > 0) { glDisable(GL_DEPTH_TEST); diff --git a/Minecraft.Client/UI/Screens/AchievementScreen.cpp b/Minecraft.Client/UI/Screens/AchievementScreen.cpp index 1ef4a92bc..48ea264b4 100644 --- a/Minecraft.Client/UI/Screens/AchievementScreen.cpp +++ b/Minecraft.Client/UI/Screens/AchievementScreen.cpp @@ -136,7 +136,6 @@ void AchievementScreen::renderLabels() { } void AchievementScreen::renderBg(int xm, int ym, float a) { - // 4J Unused #if 0 int xScroll = Mth::floor(xScrollO + (xScrollP - xScrollO) * a); int yScroll = Mth::floor(yScrollO + (yScrollP - yScrollO) * a); diff --git a/Minecraft.Client/UI/Screens/ContainerScreen.cpp b/Minecraft.Client/UI/Screens/ContainerScreen.cpp index 4f34f524e..ad2513207 100644 --- a/Minecraft.Client/UI/Screens/ContainerScreen.cpp +++ b/Minecraft.Client/UI/Screens/ContainerScreen.cpp @@ -25,7 +25,6 @@ void ContainerScreen::renderLabels() { } void ContainerScreen::renderBg(float a) { - // 4J Unused #ifdef ENABLE_JAVA_GUIS int tex = minecraft->textures->loadTexture(TN_GUI_CONTAINER); glColor4f(1, 1, 1, 1); diff --git a/Minecraft.Client/UI/Screens/CraftingScreen.cpp b/Minecraft.Client/UI/Screens/CraftingScreen.cpp index 14b9aaae5..666c66878 100644 --- a/Minecraft.Client/UI/Screens/CraftingScreen.cpp +++ b/Minecraft.Client/UI/Screens/CraftingScreen.cpp @@ -27,7 +27,6 @@ void CraftingScreen::renderLabels() { } void CraftingScreen::renderBg(float a) { - // 4J Unused #ifdef ENABLE_JAVA_GUIS glColor4f(1, 1, 1, 1); minecraft->textures->bindTexture(&GUI_CRAFTING_LOCATION); diff --git a/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp b/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp index 23685b1cc..24d239382 100644 --- a/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp +++ b/Minecraft.Client/UI/Screens/EnchantmentScreen.cpp @@ -125,7 +125,6 @@ void EnchantmentScreen::render(int xm, int ym, float a) { } void EnchantmentScreen::renderBg(float a) { - // 4J unused #ifdef ENABLE_JAVA_GUIS glColor4f(1.0f, 1.0f, 1.0f, 1.0f); Minecraft::GetInstance()->textures->bindTexture(&GUI_ENCHANT_LOCATION); diff --git a/Minecraft.Client/UI/Screens/FurnaceScreen.cpp b/Minecraft.Client/UI/Screens/FurnaceScreen.cpp index 04d0ca346..170aedb61 100644 --- a/Minecraft.Client/UI/Screens/FurnaceScreen.cpp +++ b/Minecraft.Client/UI/Screens/FurnaceScreen.cpp @@ -23,7 +23,6 @@ void FurnaceScreen::renderLabels() { } void FurnaceScreen::renderBg(float a) { - // 4J Unused #ifdef ENABLE_JAVA_GUIS glColor4f(1, 1, 1, 1); minecraft->textures->bindTexture(&GUI_FURNACE_LOCATION); diff --git a/Minecraft.Client/UI/Screens/HopperScreen.cpp b/Minecraft.Client/UI/Screens/HopperScreen.cpp index 2bab4945e..df3b3af9a 100644 --- a/Minecraft.Client/UI/Screens/HopperScreen.cpp +++ b/Minecraft.Client/UI/Screens/HopperScreen.cpp @@ -26,7 +26,6 @@ void HopperScreen::renderLabels() { } void HopperScreen::renderBg(float a) { - // 4J Unused #ifdef ENABLE_JAVA_GUIS glColor4f(1, 1, 1, 1); minecraft->textures->bindTexture(&GUI_HOPPER_LOCATION); diff --git a/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp b/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp index 1ce2e2fe1..f6de569c9 100644 --- a/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp +++ b/Minecraft.Client/UI/Screens/HorseInventoryScreen.cpp @@ -40,7 +40,6 @@ void HorseInventoryScreen::render(int xm, int ym, float a) { } void HorseInventoryScreen::renderBg(float a) { - // 4J Unused #ifdef ENABLE_JAVA_GUIS glColor4f(1, 1, 1, 1); minecraft->textures->bindTexture(&GUI_HORSE_LOCATION); diff --git a/Minecraft.Client/UI/Screens/InventoryScreen.cpp b/Minecraft.Client/UI/Screens/InventoryScreen.cpp index 67348db84..f4dccf4f6 100644 --- a/Minecraft.Client/UI/Screens/InventoryScreen.cpp +++ b/Minecraft.Client/UI/Screens/InventoryScreen.cpp @@ -32,7 +32,6 @@ void InventoryScreen::render(int xm, int ym, float a) { } void InventoryScreen::renderBg(float a) { - // 4J Unused #ifdef ENABLE_JAVA_GUIS int tex = minecraft->textures->loadTexture(TN_GUI_INVENTORY); glColor4f(1, 1, 1, 1); diff --git a/Minecraft.Client/UI/Screens/TitleScreen.cpp b/Minecraft.Client/UI/Screens/TitleScreen.cpp index 0f8212cb1..50b527600 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.cpp +++ b/Minecraft.Client/UI/Screens/TitleScreen.cpp @@ -365,7 +365,6 @@ void TitleScreen::rotateAndBlur(float a) { } void TitleScreen::render(int xm, int ym, float a) { - // 4J Unused - Iggy Flash UI renders the title screen on consoles #ifdef ENABLE_JAVA_GUIS // 4jcraft: panorama renderSkybox(a); diff --git a/Minecraft.Client/UI/Screens/TrapScreen.cpp b/Minecraft.Client/UI/Screens/TrapScreen.cpp index 7a1afb6bd..7936ec174 100644 --- a/Minecraft.Client/UI/Screens/TrapScreen.cpp +++ b/Minecraft.Client/UI/Screens/TrapScreen.cpp @@ -23,7 +23,6 @@ void TrapScreen::renderLabels() { } void TrapScreen::renderBg(float a) { - // 4J Unused #ifdef ENABLE_JAVA_GUIS glColor4f(1, 1, 1, 1); minecraft->textures->bindTexture(&GUI_TRAP_LOCATION); From 3dcc985cd4086c068639e2442bebc231f7336f59 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Fri, 27 Mar 2026 23:14:30 +0300 Subject: [PATCH 25/40] feat(jui): add brewing stand screen (+ fix enchantment screen not being used) --- .../Common/res/1_2_2/gui/brewing_stand.png | Bin 0 -> 1650 bytes Minecraft.Client/Player/LocalPlayer.cpp | 14 ++- Minecraft.Client/Textures/Textures.cpp | 1 + Minecraft.Client/Textures/Textures.h | 1 + .../UI/Screens/BrewingStandScreen.cpp | 99 ++++++++++++++++++ .../UI/Screens/BrewingStandScreen.h | 23 ++++ 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/brewing_stand.png create mode 100644 Minecraft.Client/UI/Screens/BrewingStandScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/BrewingStandScreen.h diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/brewing_stand.png b/Minecraft.Assets/Common/res/1_2_2/gui/brewing_stand.png new file mode 100644 index 0000000000000000000000000000000000000000..e7dc2031ac1614c34be70770119ce2ccb1eb0918 GIT binary patch literal 1650 zcmcIkX;70_6n*(fFvw~|4NGD`bcigXphbuh0w@#k{6#xW$MF6xs9DJfng5e+^E%lR!a!P|VJgd}(EoPUP*pxQ77CNU37CHCNn;eff8W?4O5@P2guNl7L zBHqrWV1*O(5S`k7#^6iKxLRyop(R42eRHlzVxev~-c93Tm}jM8C-NK$yn(yH$WXKFiU>!%jFfNTFC$Yg&|?sEQqBoWc;b~4vMHuGbJr&IDyZlojV zK8z$ci`FXfWK-;G{E$()V3JP;nI!FxO;_GuFxOVO%VJeeP~P$lje6yf#R?)+h0qh^0UYxqMLKHn=iZM5Yx#)<^EqT zdTyG$u(}ya-;S83uy;3-coJ=z=68_7b|eT;Ffnyoft>wG=>XfYqmlb6Y6rkn4TbjL z-#zhK(X>J{Zt`^Thwx~I0WPP#nxEHsUw8!kUgT#U_S+VIpH0HY1zed(E3EQhxEi3V zznr1TBs^mqvsVIn-2``!fhWgmCu^+%$^Nb5`Fzbp;{hGuSDTl8=XOovt@)5iU#V%0 z4@*l>t0T>ip5gO(-E`_O+;ba!_F7C=tJ#3C4MTXYapYPE{5w1P3_I>xS?707@Oam= zwGdojr>W?6O$A}{g&21-raS811`}Z^-D|J1M~RI;>tY+MRaWPB$2#n7oI5*8W9T*g;`ObaqZdUxNngB${XSmVI$P*$R@XGteBIs zaY>n`(x^}9xgrg6yxix&b}iwsC5+DfTjW1=BN=Y6diyJXrPB&BETJas(58;B`og2D z6wY`B2r~lqa%-k4Bjl8zI)M_;2(3@6x_WSuiu2b{%ATFc$R~`RC;A z!=*fvQ3^VvH7M$}c)`h7&qB_-+|%Inug`md_K{iS?E=g$DRmq8zgF;=RdAsQx%KHK Qv-K7Zc22gHlz`+v074gx(*OVf literal 0 HcmV?d00001 diff --git a/Minecraft.Client/Player/LocalPlayer.cpp b/Minecraft.Client/Player/LocalPlayer.cpp index c9cd930df..a74161798 100644 --- a/Minecraft.Client/Player/LocalPlayer.cpp +++ b/Minecraft.Client/Player/LocalPlayer.cpp @@ -1,5 +1,7 @@ #include "../Platform/stdafx.h" #include "LocalPlayer.h" +#include "UI/Screens/BrewingStandScreen.h" +#include "UI/Screens/EnchantmentScreen.h" #include "UI/Screens/HopperScreen.h" #include "UI/Screens/HorseInventoryScreen.h" #include "UI/Screens/RepairScreen.h" @@ -645,10 +647,14 @@ bool LocalPlayer::openFireworks(int x, int y, int z) { bool LocalPlayer::startEnchanting(int x, int y, int z, const std::wstring& name) { +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new EnchantmentScreen(inventory, level, x, y, z)); + bool success = true; +#else bool success = app.LoadEnchantingMenu(GetXboxPad(), inventory, x, y, z, level, name); if (success) ui.PlayUISFX(eSFX_Press); - // minecraft.setScreen(new EnchantmentScreen(inventory, level, x, y, z)); +#endif return success; } @@ -677,10 +683,14 @@ bool LocalPlayer::openFurnace(std::shared_ptr furnace) { bool LocalPlayer::openBrewingStand( std::shared_ptr brewingStand) { +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new BrewingStandScreen(inventory, brewingStand)); + bool success = true; +#else bool success = app.LoadBrewingStandMenu(GetXboxPad(), inventory, brewingStand); if (success) ui.PlayUISFX(eSFX_Press); - // minecraft.setScreen(new BrewingStandScreen(inventory, brewingStand)); +#endif return success; } diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 20b952c59..deef91211 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -182,6 +182,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"gui/trap", L"gui/hopper", L"gui/enchant", + L"gui/brewing_stand", L"title/bg/panorama", L"title/bg/panorama0", L"title/bg/panorama1", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 2a6ad074a..b073a77de 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -164,6 +164,7 @@ typedef enum _TEXTURE_NAME { TN_GUI_TRAP, TN_GUI_HOPPER, TN_GUI_ENCHANT, + TN_GUI_BREWING_STAND, TN_TITLE_BG_PANORAMA, TN_TITLE_BG_PANORAMA0, TN_TITLE_BG_PANORAMA1, diff --git a/Minecraft.Client/UI/Screens/BrewingStandScreen.cpp b/Minecraft.Client/UI/Screens/BrewingStandScreen.cpp new file mode 100644 index 000000000..1d1239c2a --- /dev/null +++ b/Minecraft.Client/UI/Screens/BrewingStandScreen.cpp @@ -0,0 +1,99 @@ +#include "../../Platform/stdafx.h" +#include "BrewingStandScreen.h" +#include +#include +#include +#include "../../Player/MultiPlayerLocalPlayer.h" +#include "../../Rendering/Lighting.h" +#include "../../Textures/Textures.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../../Minecraft.World/Containers/BrewingStandMenu.h" +#include "../../../Minecraft.World/Containers/Slot.h" +#include "../../../Minecraft.Client/Minecraft.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing +// container classes +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_BREWING_STAND_LOCATION = + ResourceLocation(TN_GUI_BREWING_STAND); +#endif + +BrewingStandScreen::BrewingStandScreen( + std::shared_ptr inventory, + std::shared_ptr brewingStand) + : AbstractContainerScreen(new BrewingStandMenu(inventory, brewingStand)) { + this->inventory = inventory; + this->brewingStand = brewingStand; + this->brewMenu = static_cast(menu); +} + +BrewingStandScreen::~BrewingStandScreen() = default; + +void BrewingStandScreen::init() { AbstractContainerScreen::init(); } + +void BrewingStandScreen::removed() { AbstractContainerScreen::removed(); } + +void BrewingStandScreen::renderLabels() { + font->draw(brewingStand->getName(), + (imageWidth / 2) - (font->width(brewingStand->getName()) / 2), 6, + 0x404040); + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); +} + +void BrewingStandScreen::renderBg(float a) { +#ifdef ENABLE_JAVA_GUIS + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + Minecraft::GetInstance()->textures->bindTexture( + &GUI_BREWING_STAND_LOCATION); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + blit(xo, yo, 0, 0, imageWidth, imageHeight); + + int brewTime = brewingStand->getBrewTime(); + + if (brewTime > 0) { + int arrowHeight = (int)(28.0f * (1.0f - (float)brewTime / 400.0f)); + + if (arrowHeight > 0) { + blit(xo + 97, yo + 16 + (28 - arrowHeight), 176, 28 - arrowHeight, + 9, arrowHeight); + } + + int bubbleStep = (brewTime / 2) % 7; + int bubbleHeight = 0; + + switch (bubbleStep) { + case 0: + bubbleHeight = 29; + break; + case 1: + bubbleHeight = 24; + break; + case 2: + bubbleHeight = 20; + break; + case 3: + bubbleHeight = 16; + break; + case 4: + bubbleHeight = 11; + break; + case 5: + bubbleHeight = 6; + break; + case 6: + bubbleHeight = 0; + break; + } + + if (bubbleHeight > 0) { + blit(xo + 65, yo + 14 + (29 - bubbleHeight), 185, 29 - bubbleHeight, + 12, bubbleHeight); + } + } +#endif +} + +void BrewingStandScreen::render(int xm, int ym, float a) { + AbstractContainerScreen::render(xm, ym, a); +} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/BrewingStandScreen.h b/Minecraft.Client/UI/Screens/BrewingStandScreen.h new file mode 100644 index 000000000..12f834ed7 --- /dev/null +++ b/Minecraft.Client/UI/Screens/BrewingStandScreen.h @@ -0,0 +1,23 @@ +#pragma once + +#include "AbstractContainerScreen.h" +#include "../../../Minecraft.World/Containers/BrewingStandMenu.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" + +class BrewingStandScreen : public AbstractContainerScreen { +public: + BrewingStandScreen(std::shared_ptr inventory, + std::shared_ptr brewingStand); + virtual ~BrewingStandScreen(); + + void init() override; + void removed() override; + void renderLabels() override; + void renderBg(float a) override; + void render(int xm, int ym, float a) override; + +private: + std::shared_ptr inventory; + std::shared_ptr brewingStand; + BrewingStandMenu* brewMenu; +}; \ No newline at end of file From affe60603c1169a8be8af867b0c467c48ae58e11 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sat, 28 Mar 2026 22:07:17 +0300 Subject: [PATCH 26/40] make AbstractContainerScreen's itemRenderer protected --- Minecraft.Client/UI/Screens/AbstractContainerScreen.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h index eb7ddc0dd..74f14cc5d 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h @@ -6,7 +6,8 @@ class Slot; class Container; class AbstractContainerScreen : public Screen { -private: +protected: + // 4jcraft: made protected to match JE 1.6.4 static ItemRenderer* itemRenderer; protected: From 80966a501c06575ead1324d4f3465531f74fbce8 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 04:05:31 +0300 Subject: [PATCH 27/40] refactor(jui): extract tooltip rendering into standalone method so we can use tooltips elsewhere (the upcoming merchantscreen more specifically) --- .../UI/Screens/AbstractContainerScreen.cpp | 268 +++++++++--------- .../UI/Screens/AbstractContainerScreen.h | 4 + 2 files changed, 141 insertions(+), 131 deletions(-) diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp index a1723776e..b437694a4 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp @@ -95,138 +95,11 @@ void AbstractContainerScreen::render(int xm, int ym, float a) { hoveredSlot->hasItem()) { std::shared_ptr item = hoveredSlot->getItem(); - // std::wstring elementName = - // trimString(Language::getInstance()->getElementName(hoveredSlot->getItem()->getDescriptionId())); - std::vector elementName; - std::vector* tooltipLines = - item->getHoverText(minecraft->player, false, elementName); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; - if (tooltipLines != NULL && tooltipLines->size() > 0) { - int tooltipWidth = 0; - std::vector cleanedLines; - std::vector lineColors; - - for (int lineIndex = 0; lineIndex < (int)tooltipLines->size(); - ++lineIndex) { - std::wstring rawLine = (*tooltipLines)[lineIndex]; - std::wstring clean = L""; - int lineColor = 0xffffffff; - - // 4jcraft: LCE is using HTML font elements for its tooltip - // colors, so make sure to parse them for parity w iggy UI - // - // examples would be enchantment books, potions and music - // discs - size_t fontPos = rawLine.find(L"') { - inTag = false; - } else if (!inTag) { - clean += currentChar; - } - } - - cleanedLines.push_back(clean); - lineColors.push_back(lineColor); - - int lineWidth = font->width(clean); - if (lineWidth > tooltipWidth) { - tooltipWidth = lineWidth; - } - } - - int tooltipX = xm - xo + 12; - int tooltipY = ym - yo - 12; - - int tooltipHeight = 8; - - if (tooltipLines->size() > 1) { - tooltipHeight += 2 + (tooltipLines->size() - 1) * 10; - } - - int bgColor = 0xf0100010; - fillGradient(tooltipX - 3, tooltipY - 4, - tooltipX + tooltipWidth + 3, tooltipY - 3, bgColor, - bgColor); - fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 3, - tooltipX + tooltipWidth + 3, - tooltipY + tooltipHeight + 4, bgColor, bgColor); - fillGradient(tooltipX - 3, tooltipY - 3, - tooltipX + tooltipWidth + 3, - tooltipY + tooltipHeight + 3, bgColor, bgColor); - fillGradient(tooltipX - 4, tooltipY - 3, tooltipX - 3, - tooltipY + tooltipHeight + 3, bgColor, bgColor); - fillGradient(tooltipX + tooltipWidth + 3, tooltipY - 3, - tooltipX + tooltipWidth + 4, - tooltipY + tooltipHeight + 3, bgColor, bgColor); - - int borderStart = 0x505000ff; - int borderFinish = - (borderStart & 0xfefefe) >> 1 | borderStart & 0xff000000; - fillGradient(tooltipX - 3, (tooltipY - 3) + 1, (tooltipX - 3) + 1, - (tooltipY + tooltipHeight + 3) - 1, borderStart, - borderFinish); - fillGradient(tooltipX + tooltipWidth + 2, (tooltipY - 3) + 1, - tooltipX + tooltipWidth + 3, - (tooltipY + tooltipHeight + 3) - 1, borderStart, - borderFinish); - fillGradient(tooltipX - 3, tooltipY - 3, - tooltipX + tooltipWidth + 3, (tooltipY - 3) + 1, - borderStart, borderStart); - fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 2, - tooltipX + tooltipWidth + 3, - tooltipY + tooltipHeight + 3, borderFinish, - borderFinish); - - int currentY = tooltipY; - for (int lineIndex = 0; lineIndex < (int)tooltipLines->size(); - ++lineIndex) { - std::wstring& currentLine = cleanedLines[lineIndex]; - int textColor; - - if (lineIndex == 0) { - textColor = app.GetHTMLColour(item->getRarity()->color); - } else { - textColor = (lineColors[lineIndex] != 0xffffffff) - ? lineColors[lineIndex] - : 0xffaaaaaa; - } - - font->drawShadow(currentLine, tooltipX, currentY, textColor); - - if (lineIndex == 0) { - currentY += 2; - } - - currentY += 10; - } - } + // 4jcraft: abstracted tooltip rendering into a new method + renderTooltip(item, xm - xo, ym - yo); } glPopMatrix(); @@ -237,6 +110,139 @@ void AbstractContainerScreen::render(int xm, int ym, float a) { #endif } +// 4jcraft: extracted from render() into a standalone method so this can be used +// in other derived classes +void AbstractContainerScreen::renderTooltip(std::shared_ptr item, + int x, int y) { + if (item == nullptr) return; + + std::vector elementName; + std::vector* tooltipLines = + item->getHoverText(minecraft->player, false, elementName); + + if (tooltipLines != NULL && tooltipLines->size() > 0) { + int tooltipWidth = 0; + std::vector cleanedLines; + std::vector lineColors; + + for (int lineIndex = 0; lineIndex < (int)tooltipLines->size(); + ++lineIndex) { + std::wstring rawLine = (*tooltipLines)[lineIndex]; + std::wstring clean = L""; + int lineColor = 0xffffffff; + + // 4jcraft: LCE is using HTML font elements for its tooltip + // colors, so make sure to parse them for parity w iggy UI + // + // examples would be enchantment books, potions and music + // discs + size_t fontPos = rawLine.find(L"') { + inTag = false; + } else if (!inTag) { + clean += currentChar; + } + } + + cleanedLines.push_back(clean); + lineColors.push_back(lineColor); + + int lineWidth = font->width(clean); + if (lineWidth > tooltipWidth) { + tooltipWidth = lineWidth; + } + } + + int tooltipX = x + 12; + int tooltipY = y - 12; + int tooltipHeight = 8; + + if (tooltipLines->size() > 1) { + tooltipHeight += 2 + (tooltipLines->size() - 1) * 10; + } + + int bgColor = 0xf0100010; + fillGradient(tooltipX - 3, tooltipY - 4, tooltipX + tooltipWidth + 3, + tooltipY - 3, bgColor, bgColor); + fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 3, + tooltipX + tooltipWidth + 3, tooltipY + tooltipHeight + 4, + bgColor, bgColor); + fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, + tooltipY + tooltipHeight + 3, bgColor, bgColor); + fillGradient(tooltipX - 4, tooltipY - 3, tooltipX - 3, + tooltipY + tooltipHeight + 3, bgColor, bgColor); + fillGradient(tooltipX + tooltipWidth + 3, tooltipY - 3, + tooltipX + tooltipWidth + 4, tooltipY + tooltipHeight + 3, + bgColor, bgColor); + + int borderStart = 0x505000ff; + int borderFinish = + (borderStart & 0xfefefe) >> 1 | borderStart & 0xff000000; + fillGradient(tooltipX - 3, (tooltipY - 3) + 1, (tooltipX - 3) + 1, + (tooltipY + tooltipHeight + 3) - 1, borderStart, + borderFinish); + fillGradient(tooltipX + tooltipWidth + 2, (tooltipY - 3) + 1, + tooltipX + tooltipWidth + 3, + (tooltipY + tooltipHeight + 3) - 1, borderStart, + borderFinish); + fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, + (tooltipY - 3) + 1, borderStart, borderStart); + fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 2, + tooltipX + tooltipWidth + 3, tooltipY + tooltipHeight + 3, + borderFinish, borderFinish); + + int currentY = tooltipY; + for (int lineIndex = 0; lineIndex < (int)tooltipLines->size(); + ++lineIndex) { + std::wstring& currentLine = cleanedLines[lineIndex]; + int textColor; + + if (lineIndex == 0) { + textColor = app.GetHTMLColour(item->getRarity()->color); + } else { + textColor = (lineColors[lineIndex] != 0xffffffff) + ? lineColors[lineIndex] + : 0xffaaaaaa; + } + + font->drawShadow(currentLine, tooltipX, currentY, textColor); + + if (lineIndex == 0) { + currentY += 2; + } + + currentY += 10; + } + } +} + void AbstractContainerScreen::renderLabels() {} void AbstractContainerScreen::renderSlot(Slot* slot) { diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h index 74f14cc5d..af1654fbf 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h @@ -29,6 +29,10 @@ protected: // handling. virtual Slot* findSlot(int x, int y); virtual bool isHovering(Slot* slot, int xm, int ym); + // 4jcraft: extracted from render() into a standalone method so this can be + // used in other classes + virtual void renderTooltip(std::shared_ptr item, int x, + int y); private: virtual void renderSlot(Slot* slot); From 32149b7ec3d7b94797867e69705278f63d025909 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 04:55:54 +0300 Subject: [PATCH 28/40] refactor(jui): backport hovering over helper from 1.6.x --- .../UI/Screens/AbstractContainerScreen.cpp | 12 +++++++++--- .../UI/Screens/AbstractContainerScreen.h | 3 +++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp index b437694a4..53a81d19d 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp @@ -283,14 +283,20 @@ Slot* AbstractContainerScreen::findSlot(int x, int y) { return NULL; } -bool AbstractContainerScreen::isHovering(Slot* slot, int xm, int ym) { +// 4jcraft: equivalent to MCP 8.11 (1.6.x)'s GuiContainer.isPointInRegion() for +// use in other derived classes +bool AbstractContainerScreen::isHoveringOver(int x, int y, int w, int h, int xm, + int ym) { int xo = (width - imageWidth) / 2; int yo = (height - imageHeight) / 2; xm -= xo; ym -= yo; - return xm >= slot->x - 1 && xm < slot->x + 16 + 1 && ym >= slot->y - 1 && - ym < slot->y + 16 + 1; + return xm >= x - 1 && xm < x + w + 1 && ym >= y - 1 && ym < y + h + 1; +} + +bool AbstractContainerScreen::isHovering(Slot* slot, int xm, int ym) { + return isHoveringOver(slot->x, slot->y, 16, 16, xm, ym); } void AbstractContainerScreen::mouseClicked(int x, int y, int buttonNum) { diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h index af1654fbf..dcd2f4214 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h @@ -28,6 +28,9 @@ protected: // can call findSlot() and isHovering() directly for its custom click // handling. virtual Slot* findSlot(int x, int y); + // 4jcraft: equivalent to MCP 8.11 (1.6.x)'s GuiContainer.isPointInRegion() + // for use in other derived classes + virtual bool isHoveringOver(int x, int y, int w, int h, int xm, int ym); virtual bool isHovering(Slot* slot, int xm, int ym); // 4jcraft: extracted from render() into a standalone method so this can be // used in other classes From 97c6704d82cdb61e512fbcbf1534ab3425cc1c2e Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 05:15:30 +0300 Subject: [PATCH 29/40] feat(jui): add merchant screen --- .../Common/res/1_2_2/gui/villager.png | Bin 0 -> 3130 bytes Minecraft.Client/Network/ClientConnection.cpp | 14 +- Minecraft.Client/Player/LocalPlayer.cpp | 7 +- Minecraft.Client/Textures/Textures.cpp | 1 + Minecraft.Client/Textures/Textures.h | 1 + .../UI/Screens/MerchantScreen.cpp | 205 ++++++++++++++++++ Minecraft.Client/UI/Screens/MerchantScreen.h | 32 +++ Minecraft.Client/UI/TradeSwitchButton.cpp | 46 ++++ Minecraft.Client/UI/TradeSwitchButton.h | 14 ++ 9 files changed, 318 insertions(+), 2 deletions(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/villager.png create mode 100644 Minecraft.Client/UI/Screens/MerchantScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/MerchantScreen.h create mode 100644 Minecraft.Client/UI/TradeSwitchButton.cpp create mode 100644 Minecraft.Client/UI/TradeSwitchButton.h diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/villager.png b/Minecraft.Assets/Common/res/1_2_2/gui/villager.png new file mode 100644 index 0000000000000000000000000000000000000000..3211a7a00b530648de14bd794a308a2a0f560156 GIT binary patch literal 3130 zcmd5;XH=8P8lHp*0YO-irXoRUHV8$@N()V-D@A${MPLEF^d=B+5d7YDFK2~r8jAY6mpY2yLJ?H+t-#2sSJI}n|Gw=JHXXeb^i&iE=0tW>E z079l0jBEgaa6Tab&dV8GgUhaS23{|76C-em``$YmIl)2r|8>DB1b}_7xg8>=WQGA? z?}(|9zFp+V@)x%hyIJXu?_)XYAA%J8AIs018{B1YE4Ksd3mJGb(E zB%?UL9W;hVstD+cK z-S><5q(r&S2GLBpo|E(Iifm^c2P*}ZUK+XBm6A1udVfuAU0bEU=f`x(VrWo|vW>pS zX#T(hZ1uor!fHD`TRw8c^Zv{moOkDj_GxA11?o+bebL2>HI9{0F}BRi%{6BeVM3Tj zuI0(F)ukiTQTc3dBA|pFlVI1acF!+RBA6oZjo!Ny)+Ban$r^YfqVk_be2p^6)KF6^ zKI1=F`J$=w+84&l9sDd4Nn2%)rd}+&PHhNZ8Y;3o@vVRrNX=kw81pBL=df2+40H4H zLiLDNxJ8nIev*M~Xx8WpmICaEdvoxNN2?}5IM~g>+}u2~G*2sn9*-S?81W}8 zObS~C!a*UwNyg#t&51@wFFm`eWFHEhS@Q4~7)NE0))Y-+wwHdJLIt}~Jm3L}um!RB zQQ7;`<%#-R3l)Tg&G~^7S!PAjn40BBSYP_>c#1TE*sFQ-Yh|5J&tOZeZ{Lb;X~mBj z`n^;=)K?k}B@KGtb;!h5s{4GrVDrVeZGoCBMw*AF<27&I5syC*5h1g#m2jRcXyVD+?WMzB{E-3e6w zP$F#}ATJ6eC0gZcmy^lHS!VLyYEn-I=866w-(fwCLx7vj+UzzcnmJk+Nacv3vK!pA zqyjx#L8Lu-pY+N(4}F+y&4)<{o_Z@3-&$byBy@4Gibz&5-PkCwe>wd~;AebH{%par zW!Fu2@qn2Yjr!EFG75jYJu?(YD%~|Q=)7jbX@ zPxNAC(cF-Z$2+&(^oMYspUEbe6Dkp|LoQHkJIdj+V62+|bBF>*t#R`>j#?xg$ClHta5&sFD#NDyq1^n^a9wR+^JMn&t^S6n z2Uc4J^*0QGpl{8fR+laFUYNf*QvC_iB{2u>>rgdGP^MjXVDN*FOv5v5O|oD0;9iDn z+d{EN_?KQ0q{FUQnz0kkK4goOH!0{|Hc%yk07|;-tMbE0ranlw-&ouXVP8_kca;O+7n0D+OJ0c!u;7U@G^a-A~k;1 z?n?NEBMkJyNC^4kP3J{P(N23Qx}RNcuB_6ei1?190K@AuEurAPxi@%1MncYXc@uCcTlqX01+Nr%TX>x83^t-A=piJlhS| z9UH#<65rt^fQ%eslj@WocI}eS;!l}Nh%3*J){L3kbo8`;(H4iG;f2GmUFkM>HLSxe zQUR8`=X@uYv@dl0A|V0Yii8&)FQO)d)ge?iI~%TX4gk!N;8k@{d0kJsY}i^*4KFMT zeg$`4(42VYN{YQL3+uN@UDTxr1W#pZLGxgb%vOvA1fEr-^MF^^lCn@js4n4|JI2{i zKI}H9-bqPVWPa#boU?%w>AXNs&HEnXfGi;O? zR`K-dQ{EZW0X;ZIp@8{i=vs+O2P7K`5CDh(;K+kM932%T3IkqmCl&sN;Q+t@fO7A0 zf-Sv^;M|t#h`PFZ`Ww_~>>DjPnTsIX1Fgkj)=*b}gGwJ*N4bu+AK->_?s91GC;2~E z{)VBLWbUkBQ7?8)?7+bkg5 zYT!W7je-4m!L0UF*NL9)?(gGJ!x4yMlWT?vPweeIZ6C|xK$o`M+`z0d_`i5~#@U%u VDd@O69>UywOpUFKstr67{{!+^XTJad literal 0 HcmV?d00001 diff --git a/Minecraft.Client/Network/ClientConnection.cpp b/Minecraft.Client/Network/ClientConnection.cpp index e41f43d77..78cc2c789 100644 --- a/Minecraft.Client/Network/ClientConnection.cpp +++ b/Minecraft.Client/Network/ClientConnection.cpp @@ -46,6 +46,7 @@ #include "../ClientConstants.h" #include "../../Minecraft.World/Util/SoundTypes.h" #include "../Textures/Packs/TexturePackRepository.h" +#include "UI/Screens/MerchantScreen.h" #ifdef _XBOX #include "../Platform/Common/XUI/XUI_Scene_Trading.h" #else @@ -3707,6 +3708,17 @@ void ClientConnection::handleCustomPayload( ByteArrayInputStream bais(customPayloadPacket->data); DataInputStream input(&bais); int containerId = input.readInt(); +#ifdef ENABLE_JAVA_GUIS + // 4jcraft: use the java gui getMerchant() to get trader as we don't + // have iggy's screen anymore + if (minecraft->screen && + dynamic_cast(minecraft->screen) && + containerId == minecraft->localplayers[m_userIndex] + ->containerMenu->containerId) { + std::shared_ptr trader = nullptr; + MerchantScreen* screen = (MerchantScreen*)minecraft->screen; + trader = screen->getMerchant(); +#else if (ui.IsSceneInStack(m_userIndex, eUIScene_TradingMenu) && containerId == minecraft->localplayers[m_userIndex] ->containerMenu->containerId) { @@ -3729,7 +3741,7 @@ void ClientConnection::handleCustomPayload( UIScene_TradingMenu* screen = (UIScene_TradingMenu*)scene; trader = screen->getMerchant(); #endif - +#endif MerchantRecipeList* recipeList = MerchantRecipeList::createFromStream(&input); trader->overrideOffers(recipeList); diff --git a/Minecraft.Client/Player/LocalPlayer.cpp b/Minecraft.Client/Player/LocalPlayer.cpp index a74161798..79003a286 100644 --- a/Minecraft.Client/Player/LocalPlayer.cpp +++ b/Minecraft.Client/Player/LocalPlayer.cpp @@ -4,6 +4,7 @@ #include "UI/Screens/EnchantmentScreen.h" #include "UI/Screens/HopperScreen.h" #include "UI/Screens/HorseInventoryScreen.h" +#include "UI/Screens/MerchantScreen.h" #include "UI/Screens/RepairScreen.h" #include "User.h" #include "../Input/Input.h" @@ -714,10 +715,14 @@ bool LocalPlayer::openTrap(std::shared_ptr trap) { bool LocalPlayer::openTrading(std::shared_ptr traderTarget, const std::wstring& name) { +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new MerchantScreen(inventory, traderTarget, level)); + bool success = true; +#else bool success = app.LoadTradingMenu(GetXboxPad(), inventory, traderTarget, level, name); if (success) ui.PlayUISFX(eSFX_Press); - // minecraft.setScreen(new MerchantScreen(inventory, traderTarget, level)); +#endif return success; } diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index deef91211..bbd96f14a 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -182,6 +182,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"gui/trap", L"gui/hopper", L"gui/enchant", + L"gui/villager", L"gui/brewing_stand", L"title/bg/panorama", L"title/bg/panorama0", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index b073a77de..3edec4b5d 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -164,6 +164,7 @@ typedef enum _TEXTURE_NAME { TN_GUI_TRAP, TN_GUI_HOPPER, TN_GUI_ENCHANT, + TN_GUI_VILLAGER, TN_GUI_BREWING_STAND, TN_TITLE_BG_PANORAMA, TN_TITLE_BG_PANORAMA0, diff --git a/Minecraft.Client/UI/Screens/MerchantScreen.cpp b/Minecraft.Client/UI/Screens/MerchantScreen.cpp new file mode 100644 index 000000000..b069e89e8 --- /dev/null +++ b/Minecraft.Client/UI/Screens/MerchantScreen.cpp @@ -0,0 +1,205 @@ +#include "../../Platform/stdafx.h" +#include "MerchantScreen.h" +#include +#include +#include "../TradeSwitchButton.h" +#include "../../Player/MultiPlayerLocalPlayer.h" +#include "../../Rendering/Lighting.h" +#include "../../Textures/Textures.h" +#include "../../Rendering/EntityRenderers/ItemRenderer.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../../Minecraft.World/Containers/MerchantMenu.h" +#include "../../../Minecraft.World/Containers/Slot.h" +#include "../../../Minecraft.World/Containers/MerchantContainer.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.item.trading.h" +#include "../../../Minecraft.Client/Minecraft.h" +#include "../../../Minecraft.Client/Network/ClientConnection.h" +#include "../../../Minecraft.World/IO/Streams/ByteArrayOutputStream.h" +#include "../../../Minecraft.World/IO/Streams/DataOutputStream.h" +#include "../../../Minecraft.World/Util/Rarity.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing +// container classes (and iggy too) +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_VILLAGER_LOCATION = ResourceLocation(TN_GUI_VILLAGER); +#endif + +MerchantScreen::MerchantScreen(std::shared_ptr inventory, + std::shared_ptr merchant, Level* level) + : AbstractContainerScreen(new MerchantMenu(inventory, merchant, level)) { + this->inventory = inventory; + this->merchantMenu = static_cast(menu); + this->merchant = merchant; + this->currentRecipeIndex = 0; + this->nextRecipeButton = nullptr; + this->prevRecipeButton = nullptr; +} + +MerchantScreen::~MerchantScreen() = default; + +void MerchantScreen::init() { + AbstractContainerScreen::init(); + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + nextRecipeButton = new TradeSwitchButton(1, xo + 120 + 27, yo + 24 - 1, true); + prevRecipeButton = new TradeSwitchButton(2, xo + 36 - 19, yo + 24 - 1, false); + + nextRecipeButton->active = false; + prevRecipeButton->active = false; + + buttons.push_back(nextRecipeButton); + buttons.push_back(prevRecipeButton); +} + +void MerchantScreen::removed() { AbstractContainerScreen::removed(); } + +void MerchantScreen::renderLabels() { + font->draw(merchant->getDisplayName(), + (imageWidth / 2) - (font->width(merchant->getDisplayName()) / 2), + 6, 0x404040); + + font->draw(inventory->getName(), 8, imageHeight - 96 + 2, 0x404040); +} + +void MerchantScreen::renderBg(float a) { +#ifdef ENABLE_JAVA_GUIS + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + minecraft->textures->bindTexture(&GUI_VILLAGER_LOCATION); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + blit(xo, yo, 0, 0, imageWidth, imageHeight); + + MerchantRecipe* activeRecipe = + merchantMenu->getTradeContainer()->getActiveRecipe(); + if (activeRecipe != nullptr && !activeRecipe->isDeprecated()) { + blit(xo + 83, yo + 21, 212, 0, 28, 21); + blit(xo + 83, yo + 51, 212, 0, 28, 21); + } +#endif +} + +void MerchantScreen::render(int xm, int ym, float a) { + AbstractContainerScreen::render(xm, ym, a); + +#ifdef ENABLE_JAVA_GUIS + std::shared_ptr player = std::dynamic_pointer_cast( + inventory->player->shared_from_this()); + MerchantRecipeList* offers = merchant->getOffers(player); + + if (offers != nullptr && !offers->empty()) { + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + MerchantRecipe* recipe = offers->at(currentRecipeIndex); + if (recipe != nullptr && !recipe->isDeprecated()) { + std::shared_ptr buyItem1 = recipe->getBuyAItem(); + std::shared_ptr buyItem2 = recipe->getBuyBItem(); + std::shared_ptr sellItem = recipe->getSellItem(); + + glPushMatrix(); + glTranslatef((float)xo, (float)yo, 0.0f); + + Lighting::turnOnGui(); + glEnable(GL_RESCALE_NORMAL); + glEnable(GL_LIGHTING); + + if (buyItem1 != nullptr) { + itemRenderer->renderGuiItem(font, minecraft->textures, buyItem1, + 36, 24); + itemRenderer->renderGuiItemDecorations( + font, minecraft->textures, buyItem1, 36, 24); + } + + if (buyItem2 != nullptr) { + itemRenderer->renderGuiItem(font, minecraft->textures, buyItem2, + 62, 24); + itemRenderer->renderGuiItemDecorations( + font, minecraft->textures, buyItem2, 62, 24); + } + + if (sellItem != nullptr) { + itemRenderer->renderGuiItem(font, minecraft->textures, sellItem, + 120, 24); + itemRenderer->renderGuiItemDecorations( + font, minecraft->textures, sellItem, 120, 24); + } + + glDisable(GL_LIGHTING); + glDisable(GL_RESCALE_NORMAL); + Lighting::turnOff(); + + glPopMatrix(); + + if (buyItem1 != nullptr && isHoveringOver(36, 24, 16, 16, xm, ym)) { + renderTooltip(buyItem1, xm, ym); + } else if (buyItem2 != nullptr && + isHoveringOver(62, 24, 16, 16, xm, ym)) { + renderTooltip(buyItem2, xm, ym); + } else if (sellItem != nullptr && + isHoveringOver(120, 24, 16, 16, xm, ym)) { + renderTooltip(sellItem, xm, ym); + } + } + } +#endif +} + +void MerchantScreen::tick() { + AbstractContainerScreen::tick(); + + std::shared_ptr player = std::dynamic_pointer_cast( + inventory->player->shared_from_this()); + + MerchantRecipeList* offers = merchant->getOffers(player); + + if (offers != nullptr) { + int offerCount = (int)offers->size(); + + nextRecipeButton->active = (currentRecipeIndex < offerCount - 1); + prevRecipeButton->active = (currentRecipeIndex > 0); + + if (currentRecipeIndex >= offerCount && offerCount > 0) { + currentRecipeIndex = offerCount - 1; + merchantMenu->setSelectionHint(currentRecipeIndex); + + // 4jcraft: taken from IUIScene_TradingMenu + ByteArrayOutputStream rawOutput; + DataOutputStream output(&rawOutput); + output.writeInt(currentRecipeIndex); + minecraft->player->connection->send( + std::shared_ptr(new CustomPayloadPacket( + CustomPayloadPacket::TRADER_SELECTION_PACKET, + rawOutput.toByteArray()))); + } + } else { + nextRecipeButton->active = false; + prevRecipeButton->active = false; + } +} + +void MerchantScreen::buttonClicked(Button* button) { + bool changed = false; + + if (button == nextRecipeButton) { + ++currentRecipeIndex; + changed = true; + } else if (button == prevRecipeButton) { + --currentRecipeIndex; + changed = true; + } + + if (changed) { + merchantMenu->setSelectionHint(currentRecipeIndex); + + // 4jcraft: taken from IUIScene_TradingMenu + ByteArrayOutputStream rawOutput; + DataOutputStream output(&rawOutput); + output.writeInt(currentRecipeIndex); + minecraft->player->connection->send( + std::shared_ptr(new CustomPayloadPacket( + CustomPayloadPacket::TRADER_SELECTION_PACKET, + rawOutput.toByteArray()))); + } +} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/MerchantScreen.h b/Minecraft.Client/UI/Screens/MerchantScreen.h new file mode 100644 index 000000000..4a40bbb72 --- /dev/null +++ b/Minecraft.Client/UI/Screens/MerchantScreen.h @@ -0,0 +1,32 @@ +#pragma once + +#include "AbstractContainerScreen.h" +#include "../../../Minecraft.World/Containers/MerchantMenu.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.item.trading.h" + +class TradeSwitchButton; + +class MerchantScreen : public AbstractContainerScreen { +public: + MerchantScreen(std::shared_ptr inventory, + std::shared_ptr merchant, Level* level); + virtual ~MerchantScreen(); + + void init() override; + void removed() override; + void renderLabels() override; + void renderBg(float a) override; + void render(int xm, int ym, float a) override; + void tick() override; + void buttonClicked(Button* button) override; + + std::shared_ptr getMerchant() { return merchant; } + +private: + std::shared_ptr inventory; + std::shared_ptr merchant; + MerchantMenu* merchantMenu; + TradeSwitchButton* nextRecipeButton; + TradeSwitchButton* prevRecipeButton; + int currentRecipeIndex; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/TradeSwitchButton.cpp b/Minecraft.Client/UI/TradeSwitchButton.cpp new file mode 100644 index 000000000..860a2c43f --- /dev/null +++ b/Minecraft.Client/UI/TradeSwitchButton.cpp @@ -0,0 +1,46 @@ +#include "../../Platform/stdafx.h" +#include "TradeSwitchButton.h" +#include "../Textures/Textures.h" +#include "../Rendering/Tesselator.h" +#include "../../../Minecraft.Client/Minecraft.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../../Minecraft.World/Containers/MerchantMenu.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) +#ifdef ENABLE_JAVA_GUIS +// ResourceLocation GUI_VILLAGER_LOCATION = ResourceLocation(TN_GUI_VILLAGER); +extern ResourceLocation GUI_VILLAGER_LOCATION; +#endif + +TradeSwitchButton::TradeSwitchButton(int id, int x, int y, bool mirrored) + : Button(id, x, y, 12, 19, L"") { + this->mirrored = mirrored; +} + +int TradeSwitchButton::getYImage(bool hovered) { return 0; } + +void TradeSwitchButton::renderBg(Minecraft* minecraft, int xm, int ym) { +#ifdef ENABLE_JAVA_GUIS + if (!visible) return; + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + minecraft->textures->bindTexture(&GUI_VILLAGER_LOCATION); + + bool hovered = (xm >= x && ym >= y && xm < x + w && ym < y + h); + + int textureX = 176; + int textureY = 0; + + if (!active) { + textureX += w * 2; + } else if (hovered) { + textureX += w; + } + + if (!mirrored) { + textureY += h; + } + + blit(x, y, textureX, textureY, w, h); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/UI/TradeSwitchButton.h b/Minecraft.Client/UI/TradeSwitchButton.h new file mode 100644 index 000000000..f5e8e0f0b --- /dev/null +++ b/Minecraft.Client/UI/TradeSwitchButton.h @@ -0,0 +1,14 @@ +#pragma once +#include "Button.h" + +class TradeSwitchButton : public Button { +private: + bool mirrored; + +public: + TradeSwitchButton(int id, int x, int y, bool mirrored); + +protected: + int getYImage(bool hovered) override; + void renderBg(Minecraft* minecraft, int xm, int ym) override; +}; \ No newline at end of file From cfb54e65bd65c15b175e60467bd9bfe3a8fd1a20 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 13:30:14 +0300 Subject: [PATCH 30/40] fix(jui): accidental ! in deprecated recipe check --- Minecraft.Client/UI/Screens/MerchantScreen.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Minecraft.Client/UI/Screens/MerchantScreen.cpp b/Minecraft.Client/UI/Screens/MerchantScreen.cpp index b069e89e8..f7e5a51e5 100644 --- a/Minecraft.Client/UI/Screens/MerchantScreen.cpp +++ b/Minecraft.Client/UI/Screens/MerchantScreen.cpp @@ -43,8 +43,10 @@ void MerchantScreen::init() { int xo = (width - imageWidth) / 2; int yo = (height - imageHeight) / 2; - nextRecipeButton = new TradeSwitchButton(1, xo + 120 + 27, yo + 24 - 1, true); - prevRecipeButton = new TradeSwitchButton(2, xo + 36 - 19, yo + 24 - 1, false); + nextRecipeButton = + new TradeSwitchButton(1, xo + 120 + 27, yo + 24 - 1, true); + prevRecipeButton = + new TradeSwitchButton(2, xo + 36 - 19, yo + 24 - 1, false); nextRecipeButton->active = false; prevRecipeButton->active = false; @@ -73,7 +75,7 @@ void MerchantScreen::renderBg(float a) { MerchantRecipe* activeRecipe = merchantMenu->getTradeContainer()->getActiveRecipe(); - if (activeRecipe != nullptr && !activeRecipe->isDeprecated()) { + if (activeRecipe != nullptr && activeRecipe->isDeprecated()) { blit(xo + 83, yo + 21, 212, 0, 28, 21); blit(xo + 83, yo + 51, 212, 0, 28, 21); } From 03fac5b8e19894f09a80a5abca7487a9f1a0ca66 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 16:34:23 +0300 Subject: [PATCH 31/40] refactor(jui): backport tooltip overloads from JE 1.6.4 --- .../UI/Screens/AbstractContainerScreen.cpp | 147 ++++++++++-------- .../UI/Screens/AbstractContainerScreen.h | 12 +- 2 files changed, 96 insertions(+), 63 deletions(-) diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp index 53a81d19d..80e1ff815 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp @@ -112,8 +112,70 @@ void AbstractContainerScreen::render(int xm, int ym, float a) { // 4jcraft: extracted from render() into a standalone method so this can be used // in other derived classes +// update: also added 1.6.x era overloads (for the creative inventory and other +// places) +void AbstractContainerScreen::renderTooltipInternal( + const std::vector& cleanedLines, + const std::vector& lineColors, int mouseX, int mouseY) { + if (cleanedLines.empty()) return; + + int tooltipWidth = 0; + for (const auto& line : cleanedLines) { + int lineWidth = font->width(line); + if (lineWidth > tooltipWidth) tooltipWidth = lineWidth; + } + + int tooltipX = mouseX + 12; + int tooltipY = mouseY - 12; + int tooltipHeight = 8; + + if (cleanedLines.size() > 1) { + tooltipHeight += 2 + (cleanedLines.size() - 1) * 10; + } + + int bgColor = 0xf0100010; + fillGradient(tooltipX - 3, tooltipY - 4, tooltipX + tooltipWidth + 3, + tooltipY - 3, bgColor, bgColor); + fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 3, + tooltipX + tooltipWidth + 3, tooltipY + tooltipHeight + 4, + bgColor, bgColor); + fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, + tooltipY + tooltipHeight + 3, bgColor, bgColor); + fillGradient(tooltipX - 4, tooltipY - 3, tooltipX - 3, + tooltipY + tooltipHeight + 3, bgColor, bgColor); + fillGradient(tooltipX + tooltipWidth + 3, tooltipY - 3, + tooltipX + tooltipWidth + 4, tooltipY + tooltipHeight + 3, + bgColor, bgColor); + + int borderStart = 0x505000ff; + int borderFinish = (borderStart & 0xfefefe) >> 1 | borderStart & 0xff000000; + fillGradient(tooltipX - 3, (tooltipY - 3) + 1, (tooltipX - 3) + 1, + (tooltipY + tooltipHeight + 3) - 1, borderStart, borderFinish); + fillGradient(tooltipX + tooltipWidth + 2, (tooltipY - 3) + 1, + tooltipX + tooltipWidth + 3, + (tooltipY + tooltipHeight + 3) - 1, borderStart, borderFinish); + fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, + (tooltipY - 3) + 1, borderStart, borderStart); + fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 2, + tooltipX + tooltipWidth + 3, tooltipY + tooltipHeight + 3, + borderFinish, borderFinish); + + int currentY = tooltipY; + for (size_t lineIndex = 0; lineIndex < cleanedLines.size(); ++lineIndex) { + const std::wstring& currentLine = cleanedLines[lineIndex]; + int textColor = lineColors[lineIndex]; + + font->drawShadow(currentLine, tooltipX, currentY, textColor); + + if (lineIndex == 0) { + currentY += 2; + } + currentY += 10; + } +} + void AbstractContainerScreen::renderTooltip(std::shared_ptr item, - int x, int y) { + int mouseX, int mouseY) { if (item == nullptr) return; std::vector elementName; @@ -121,7 +183,6 @@ void AbstractContainerScreen::renderTooltip(std::shared_ptr item, item->getHoverText(minecraft->player, false, elementName); if (tooltipLines != NULL && tooltipLines->size() > 0) { - int tooltipWidth = 0; std::vector cleanedLines; std::vector lineColors; @@ -173,74 +234,38 @@ void AbstractContainerScreen::renderTooltip(std::shared_ptr item, cleanedLines.push_back(clean); lineColors.push_back(lineColor); - - int lineWidth = font->width(clean); - if (lineWidth > tooltipWidth) { - tooltipWidth = lineWidth; - } } - int tooltipX = x + 12; - int tooltipY = y - 12; - int tooltipHeight = 8; - - if (tooltipLines->size() > 1) { - tooltipHeight += 2 + (tooltipLines->size() - 1) * 10; + if (!cleanedLines.empty()) { + lineColors[0] = app.GetHTMLColour(item->getRarity()->color); } - int bgColor = 0xf0100010; - fillGradient(tooltipX - 3, tooltipY - 4, tooltipX + tooltipWidth + 3, - tooltipY - 3, bgColor, bgColor); - fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 3, - tooltipX + tooltipWidth + 3, tooltipY + tooltipHeight + 4, - bgColor, bgColor); - fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, - tooltipY + tooltipHeight + 3, bgColor, bgColor); - fillGradient(tooltipX - 4, tooltipY - 3, tooltipX - 3, - tooltipY + tooltipHeight + 3, bgColor, bgColor); - fillGradient(tooltipX + tooltipWidth + 3, tooltipY - 3, - tooltipX + tooltipWidth + 4, tooltipY + tooltipHeight + 3, - bgColor, bgColor); + renderTooltipInternal(cleanedLines, lineColors, mouseX, mouseY); + } +} - int borderStart = 0x505000ff; - int borderFinish = - (borderStart & 0xfefefe) >> 1 | borderStart & 0xff000000; - fillGradient(tooltipX - 3, (tooltipY - 3) + 1, (tooltipX - 3) + 1, - (tooltipY + tooltipHeight + 3) - 1, borderStart, - borderFinish); - fillGradient(tooltipX + tooltipWidth + 2, (tooltipY - 3) + 1, - tooltipX + tooltipWidth + 3, - (tooltipY + tooltipHeight + 3) - 1, borderStart, - borderFinish); - fillGradient(tooltipX - 3, tooltipY - 3, tooltipX + tooltipWidth + 3, - (tooltipY - 3) + 1, borderStart, borderStart); - fillGradient(tooltipX - 3, tooltipY + tooltipHeight + 2, - tooltipX + tooltipWidth + 3, tooltipY + tooltipHeight + 3, - borderFinish, borderFinish); +void AbstractContainerScreen::renderTooltip( + const std::vector& lines, int mouseX, int mouseY) { + if (lines.empty()) return; - int currentY = tooltipY; - for (int lineIndex = 0; lineIndex < (int)tooltipLines->size(); - ++lineIndex) { - std::wstring& currentLine = cleanedLines[lineIndex]; - int textColor; + std::vector cleanedLines = lines; + std::vector lineColors; + lineColors.reserve(lines.size()); - if (lineIndex == 0) { - textColor = app.GetHTMLColour(item->getRarity()->color); - } else { - textColor = (lineColors[lineIndex] != 0xffffffff) - ? lineColors[lineIndex] - : 0xffaaaaaa; - } - - font->drawShadow(currentLine, tooltipX, currentY, textColor); - - if (lineIndex == 0) { - currentY += 2; - } - - currentY += 10; + for (size_t i = 0; i < lines.size(); ++i) { + if (i == 0) { + lineColors.push_back(0xffffffff); + } else { + lineColors.push_back(0xffaaaaaa); } } + + renderTooltipInternal(cleanedLines, lineColors, mouseX, mouseY); +} + +void AbstractContainerScreen::renderTooltip(const std::wstring& line, + int mouseX, int mouseY) { + renderTooltip(std::vector{line}, mouseX, mouseY); } void AbstractContainerScreen::renderLabels() {} diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h index dcd2f4214..72f774ef6 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h @@ -33,7 +33,10 @@ protected: virtual bool isHoveringOver(int x, int y, int w, int h, int xm, int ym); virtual bool isHovering(Slot* slot, int xm, int ym); // 4jcraft: extracted from render() into a standalone method so this can be - // used in other classes + // used in other places + virtual void renderTooltipInternal( + const std::vector& cleanedLines, + const std::vector& lineColors, int mouseX, int mouseY); virtual void renderTooltip(std::shared_ptr item, int x, int y); @@ -50,4 +53,9 @@ public: virtual void slotsChanged(std::shared_ptr container); virtual bool isPauseScreen() override; virtual void tick() override; -}; \ No newline at end of file + + // 4jcraft: 1.6.x era overloads + virtual void renderTooltip(const std::vector& lines, int x, + int y); + virtual void renderTooltip(const std::wstring& line, int x, int y); +}; From 18dd2ed2ca451d2f61166a201568e5c9eed16e6d Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 20:07:00 +0300 Subject: [PATCH 32/40] update inventory.png to 1.6.4 version --- .../Common/res/1_2_2/gui/inventory.png | Bin 7211 -> 10737 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/inventory.png b/Minecraft.Assets/Common/res/1_2_2/gui/inventory.png index 4d9911690cccd6a72aab7cc3db4192d7f554aa29..d553c4f736c56a81fffa205c3bf2099e66c88a3e 100644 GIT binary patch literal 10737 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 MMNNeYIji9R0S$lESO5S3 literal 7211 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

Ss4Q5pP d(|qnB1;~h~(@uBbN#s9yfR+YEy<80&^dHSX Date: Sun, 29 Mar 2026 20:16:56 +0300 Subject: [PATCH 33/40] feat(jui): add beacon screen --- .../Common/res/1_2_2/gui/beacon.png | Bin 0 -> 2094 bytes Minecraft.Assets/Common/res/lang/en_US.lang | 3 + Minecraft.Client/Player/LocalPlayer.cpp | 7 +- Minecraft.Client/Textures/Textures.cpp | 1 + Minecraft.Client/Textures/Textures.h | 1 + Minecraft.Client/UI/AbstractBeaconButton.cpp | 46 ++++ Minecraft.Client/UI/AbstractBeaconButton.h | 24 ++ Minecraft.Client/UI/BeaconCancelButton.cpp | 25 ++ Minecraft.Client/UI/BeaconCancelButton.h | 13 + Minecraft.Client/UI/BeaconConfirmButton.cpp | 25 ++ Minecraft.Client/UI/BeaconConfirmButton.h | 13 + Minecraft.Client/UI/BeaconPowerButton.cpp | 39 +++ Minecraft.Client/UI/BeaconPowerButton.h | 18 ++ Minecraft.Client/UI/Screens/BeaconScreen.cpp | 229 ++++++++++++++++++ Minecraft.Client/UI/Screens/BeaconScreen.h | 32 +++ Minecraft.Client/UI/Screens/TitleScreen.cpp | 1 + Minecraft.World/Entities/MobEffect.cpp | 49 ++++ Minecraft.World/Entities/MobEffect.h | 2 + 18 files changed, 527 insertions(+), 1 deletion(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/beacon.png create mode 100644 Minecraft.Client/UI/AbstractBeaconButton.cpp create mode 100644 Minecraft.Client/UI/AbstractBeaconButton.h create mode 100644 Minecraft.Client/UI/BeaconCancelButton.cpp create mode 100644 Minecraft.Client/UI/BeaconCancelButton.h create mode 100644 Minecraft.Client/UI/BeaconConfirmButton.cpp create mode 100644 Minecraft.Client/UI/BeaconConfirmButton.h create mode 100644 Minecraft.Client/UI/BeaconPowerButton.cpp create mode 100644 Minecraft.Client/UI/BeaconPowerButton.h create mode 100644 Minecraft.Client/UI/Screens/BeaconScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/BeaconScreen.h diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/beacon.png b/Minecraft.Assets/Common/res/1_2_2/gui/beacon.png new file mode 100644 index 0000000000000000000000000000000000000000..f51a2ef510b400fd868e2a53146fd032bbf89462 GIT binary patch literal 2094 zcmZux3pmv28vo|PxHIlyGRC%8om{gnF_X)>wS;KUSrd^6etPkJgh^a~=xV!;Pc3dFpCHt>~`pCNFQ=Sfsw|ySbJgTQJ_+!q*I_}Sa~LwB9Lw9*&{ zCe3il_%F)FB0q!)9!3Hhx$+N zp~FvL9c$~|ts4T?88+%)l0;c zecAGqckR59K71C@Mswj>bt8E`kb8^&*mA9P!KH9rUWJmp>UE^;kJt_}pO$fWk0Z35 znB<3a2wrOU_7_;ku{kI|xeV-!GSX;^=2olOEe$>@>RWR1&yjfrj^b zP|2eR<9niQmIWJ2t?e&!cth?XojI$Rlr|KDg5Nw`lJ(qZaybp}A5Reo9(3{T5H-;<^EnvAOT2c;f!zO^Heocsi06+)0Pl+6<8!v2W)`Ihmk5!4>}MH z!-!+5pyGsNSe7wyMvlS6=J|u?>PXWSa6$%NBu>rVk7a`f6N%kB$Hf2JEsW7|b!6P< z#(^G#sProV3-OdD=rWRVLIlx{gzg=igo%7h^gh7qp= z_2cfwfob;?Pm2%^AVe%;L@ZQuBN5QR|JA9}dD+>rrK$(8y&BdZ4G8`}c_}-MM()*_ zcGT^YE8E>4ZmwZxPZ6LUez|w@~#ue_{+FiA8Xvkx6aq$>_ zPYs8-$;1qf{d!n$XGK6Qlf${`rUW9aYV%`%>|im}Ms=2xL#Hr_BASj_$Z zQ$z^Z)KQoN)Co9aQQjI*B#`I)ROt8dZnS{x#LhZ5B(QohG0{|*zMTxDYP&Nk=@RC( zwYPpAycu)J?4zaCIlgtSqi;LD@En(PDC zIVve4f!&s>Z6QUH6PB&67vhJ6@Wq*pxF;p;R97OGX_=oB;)LC5%5565B{Wo|hzIi_ z(c2Kde~8nF%X0^(ppKd~V@kcc8F`gQIds$>&Y)VBb(@!8Td zEX+^LA}7W)%$MYn=9>ar6Up2-O?anS16|-&_4FV8vC%?F5* beacon) { - // minecraft->setScreen(new BeaconScreen(inventory, beacon)); +#ifdef ENABLE_JAVA_GUIS + minecraft->setScreen(new BeaconScreen(inventory, beacon)); + bool success = true; +#else bool success = app.LoadBeaconMenu(GetXboxPad(), inventory, beacon); if (success) ui.PlayUISFX(eSFX_Press); +#endif return success; } diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index bbd96f14a..907f51927 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -180,6 +180,7 @@ const wchar_t* Textures::preLoaded[TN_COUNT] = { L"gui/horse", L"gui/anvil", L"gui/trap", + L"gui/beacon", L"gui/hopper", L"gui/enchant", L"gui/villager", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 3edec4b5d..fd59fc66b 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -162,6 +162,7 @@ typedef enum _TEXTURE_NAME { TN_GUI_HORSE, TN_GUI_ANVIL, TN_GUI_TRAP, + TN_GUI_BEACON, TN_GUI_HOPPER, TN_GUI_ENCHANT, TN_GUI_VILLAGER, diff --git a/Minecraft.Client/UI/AbstractBeaconButton.cpp b/Minecraft.Client/UI/AbstractBeaconButton.cpp new file mode 100644 index 000000000..b2d874ddc --- /dev/null +++ b/Minecraft.Client/UI/AbstractBeaconButton.cpp @@ -0,0 +1,46 @@ +#include "../../Platform/stdafx.h" +#include "AbstractBeaconButton.h" +#include "../Textures/Textures.h" +#include "../../../Minecraft.Client/Minecraft.h" +#include + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) +#ifdef ENABLE_JAVA_GUIS +extern ResourceLocation GUI_BEACON_LOCATION; +#endif + +AbstractBeaconButton::AbstractBeaconButton(int id, int x, int y) + : Button(id, x, y, 22, 22, L"") { + hovered = false; + selected = false; + iconRes = nullptr; + iconU = iconV = 0; +} + +void AbstractBeaconButton::renderBg(Minecraft* minecraft, int xm, int ym) { +#ifdef ENABLE_JAVA_GUIS + if (!visible) return; + + hovered = (xm >= x && ym >= y && xm < x + w && ym < y + h); + + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + minecraft->textures->bindTexture(&GUI_BEACON_LOCATION); + + int texU = 0; + if (!active) { + texU += w * 2; + } else if (selected) { + texU += w * 1; + } else if (hovered) { + texU += w * 3; + } + int texV = 219; + + blit(x, y, texU, texV, w, h); + + if (iconRes != nullptr && iconRes != &GUI_BEACON_LOCATION) { + minecraft->textures->bindTexture(iconRes); + } + blit(x + 2, y + 2, iconU, iconV, 18, 18); +#endif +} \ No newline at end of file diff --git a/Minecraft.Client/UI/AbstractBeaconButton.h b/Minecraft.Client/UI/AbstractBeaconButton.h new file mode 100644 index 000000000..3254f4912 --- /dev/null +++ b/Minecraft.Client/UI/AbstractBeaconButton.h @@ -0,0 +1,24 @@ +#pragma once +#include "Button.h" + +class ResourceLocation; + +class AbstractBeaconButton : public Button { +protected: + bool hovered; + bool selected; + ResourceLocation* iconRes; + int iconU, iconV; + +public: + AbstractBeaconButton(int id, int x, int y); + + void setSelected(bool sel) { selected = sel; } + bool isSelected() const { return selected; } + bool isHovered() const { return hovered; } + + virtual void renderTooltip(int xm, int ym) = 0; + +protected: + virtual void renderBg(Minecraft* minecraft, int xm, int ym) override; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/BeaconCancelButton.cpp b/Minecraft.Client/UI/BeaconCancelButton.cpp new file mode 100644 index 000000000..081029e4c --- /dev/null +++ b/Minecraft.Client/UI/BeaconCancelButton.cpp @@ -0,0 +1,25 @@ +#include "../../Platform/stdafx.h" +#include "BeaconCancelButton.h" +#include "Screens/BeaconScreen.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) +#ifdef ENABLE_JAVA_GUIS +extern ResourceLocation GUI_BEACON_LOCATION; +#endif + +BeaconCancelButton::BeaconCancelButton(BeaconScreen* screen, int id, int x, + int y) + : AbstractBeaconButton(id, x, y) { + this->screen = screen; +#ifdef ENABLE_JAVA_GUIS + this->iconRes = &GUI_BEACON_LOCATION; +#endif + this->iconU = 112; + this->iconV = 220; +} + +void BeaconCancelButton::renderTooltip(int xm, int ym) { + screen->renderTooltip(Language::getInstance()->getElement(L"gui.cancel"), + xm, ym); +} \ No newline at end of file diff --git a/Minecraft.Client/UI/BeaconCancelButton.h b/Minecraft.Client/UI/BeaconCancelButton.h new file mode 100644 index 000000000..f9d62456e --- /dev/null +++ b/Minecraft.Client/UI/BeaconCancelButton.h @@ -0,0 +1,13 @@ +#pragma once +#include "AbstractBeaconButton.h" + +class BeaconScreen; + +class BeaconCancelButton : public AbstractBeaconButton { +public: + BeaconCancelButton(BeaconScreen* screen, int id, int x, int y); + void renderTooltip(int xm, int ym) override; + +private: + BeaconScreen* screen; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/BeaconConfirmButton.cpp b/Minecraft.Client/UI/BeaconConfirmButton.cpp new file mode 100644 index 000000000..00871f6e1 --- /dev/null +++ b/Minecraft.Client/UI/BeaconConfirmButton.cpp @@ -0,0 +1,25 @@ +#include "../../Platform/stdafx.h" +#include "BeaconConfirmButton.h" +#include "Screens/BeaconScreen.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) +#ifdef ENABLE_JAVA_GUIS +extern ResourceLocation GUI_BEACON_LOCATION; +#endif + +BeaconConfirmButton::BeaconConfirmButton(BeaconScreen* screen, int id, int x, + int y) + : AbstractBeaconButton(id, x, y) { + this->screen = screen; +#ifdef ENABLE_JAVA_GUIS + this->iconRes = &GUI_BEACON_LOCATION; +#endif + this->iconU = 90; + this->iconV = 220; +} + +void BeaconConfirmButton::renderTooltip(int xm, int ym) { + screen->renderTooltip(Language::getInstance()->getElement(L"gui.done"), xm, + ym); +} \ No newline at end of file diff --git a/Minecraft.Client/UI/BeaconConfirmButton.h b/Minecraft.Client/UI/BeaconConfirmButton.h new file mode 100644 index 000000000..ab1d28177 --- /dev/null +++ b/Minecraft.Client/UI/BeaconConfirmButton.h @@ -0,0 +1,13 @@ +#pragma once +#include "AbstractBeaconButton.h" + +class BeaconScreen; + +class BeaconConfirmButton : public AbstractBeaconButton { +public: + BeaconConfirmButton(BeaconScreen* screen, int id, int x, int y); + void renderTooltip(int xm, int ym) override; + +private: + BeaconScreen* screen; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/BeaconPowerButton.cpp b/Minecraft.Client/UI/BeaconPowerButton.cpp new file mode 100644 index 000000000..958badfe7 --- /dev/null +++ b/Minecraft.Client/UI/BeaconPowerButton.cpp @@ -0,0 +1,39 @@ +#include "../../Platform/stdafx.h" +#include "BeaconPowerButton.h" +#include "Screens/BeaconScreen.h" +#include "../../../Minecraft.World/Entities/MobEffect.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../Textures/Textures.h" +#include "Textures/ResourceLocation.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_INVENTORY_LOCATION = ResourceLocation(TN_GUI_INVENTORY); +#endif + +BeaconPowerButton::BeaconPowerButton(BeaconScreen* screen, int id, int x, int y, + int effectId, int tier) + : AbstractBeaconButton(id, x, y) { + this->screen = screen; + this->effectId = effectId; + this->tier = tier; + +#ifdef ENABLE_JAVA_GUIS + this->iconRes = &GUI_INVENTORY_LOCATION; +#endif + + int statusIconIndex = MobEffect::javaId(effectId); + this->iconU = (statusIconIndex % 8) * 18; + this->iconV = 198 + (statusIconIndex / 8) * 18; +} + +void BeaconPowerButton::renderTooltip(int xm, int ym) { + MobEffect* effect = MobEffect::effects[effectId]; + if (!effect) return; + + std::wstring name = app.GetString(effect->getDescriptionId()); + if (tier >= 3 && effect->id != MobEffect::regeneration->id) { + name += L" II"; + } + screen->renderTooltip(name, xm, ym); +} \ No newline at end of file diff --git a/Minecraft.Client/UI/BeaconPowerButton.h b/Minecraft.Client/UI/BeaconPowerButton.h new file mode 100644 index 000000000..fc6e40266 --- /dev/null +++ b/Minecraft.Client/UI/BeaconPowerButton.h @@ -0,0 +1,18 @@ +#pragma once +#include "AbstractBeaconButton.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.effect.h" + +class BeaconScreen; + +class BeaconPowerButton : public AbstractBeaconButton { +public: + BeaconPowerButton(BeaconScreen* screen, int id, int x, int y, int effectId, + int tier); + void renderTooltip(int xm, int ym) override; + bool isSelected() const { return selected; } + +private: + BeaconScreen* screen; + int effectId; + int tier; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/BeaconScreen.cpp b/Minecraft.Client/UI/Screens/BeaconScreen.cpp new file mode 100644 index 000000000..d44378d83 --- /dev/null +++ b/Minecraft.Client/UI/Screens/BeaconScreen.cpp @@ -0,0 +1,229 @@ +#include "../../Platform/stdafx.h" +#include "BeaconScreen.h" +#include "../BeaconConfirmButton.h" +#include "../BeaconCancelButton.h" +#include "../BeaconPowerButton.h" +#include +#include +#include "../../Player/MultiPlayerLocalPlayer.h" +#include "../../Rendering/Lighting.h" +#include "../../Textures/Textures.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../../Minecraft.World/Containers/Slot.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.item.h" +#include "../../../Minecraft.Client/Minecraft.h" +#include "../../../Minecraft.Client/Network/ClientConnection.h" +#include "../../../Minecraft.World/IO/Streams/ByteArrayOutputStream.h" +#include "../../../Minecraft.World/IO/Streams/DataOutputStream.h" + +// 4jcraft: referenced from MCP 8.11 (JE 1.6.4) and the existing +// container classes (and iggy too) +#ifdef ENABLE_JAVA_GUIS +ResourceLocation GUI_BEACON_LOCATION = ResourceLocation(TN_GUI_BEACON); +#endif + +BeaconScreen::BeaconScreen(std::shared_ptr inventory, + std::shared_ptr beacon) + : AbstractContainerScreen(new BeaconMenu(inventory, beacon)) { + this->inventory = inventory; + this->beacon = beacon; + this->beaconMenu = static_cast(menu); + this->imageWidth = 230; + this->imageHeight = 219; + this->buttonsNotDrawn = true; + this->beaconConfirmButton = nullptr; +} + +BeaconScreen::~BeaconScreen() = default; + +void BeaconScreen::init() { + AbstractContainerScreen::init(); + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + beaconConfirmButton = new BeaconConfirmButton(this, -1, xo + 164, yo + 107); + buttons.push_back(beaconConfirmButton); + buttons.push_back(new BeaconCancelButton(this, -2, xo + 190, yo + 107)); + + buttonsNotDrawn = true; + beaconConfirmButton->active = false; +} + +void BeaconScreen::tick() { + if (buttonsNotDrawn && beacon->getLevels() >= 0) { + buttonsNotDrawn = false; + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + + for (int tier = 0; tier <= 2; ++tier) { + int effectCount = BeaconTileEntity::BEACON_EFFECTS_EFFECTS; + int actualCount = 0; + for (int e = 0; e < effectCount; ++e) { + if (BeaconTileEntity::BEACON_EFFECTS[tier][e] != nullptr) { + actualCount++; + } else { + break; + } + } + + int totalWidth = actualCount * 22 + (actualCount - 1) * 2; + int startX = xo + 53 + (actualCount * 24 - totalWidth) / 2; + + for (int e = 0; e < actualCount; ++e) { + MobEffect* effect = BeaconTileEntity::BEACON_EFFECTS[tier][e]; + if (effect == nullptr) break; + + int buttonId = (tier << 8) | effect->id; + BeaconPowerButton* button = new BeaconPowerButton( + this, buttonId, startX + e * 24, yo + 22 + tier * 25, + effect->id, tier); + buttons.push_back(button); + + if (tier >= beacon->getLevels()) { + button->active = false; + } else if (effect->id == beacon->getPrimaryPower()) { + button->setSelected(true); + } + } + } + + int tier = 3; + int effectCount = BeaconTileEntity::BEACON_EFFECTS_EFFECTS; + int actualCount = 0; + for (int e = 0; e < effectCount; ++e) { + if (BeaconTileEntity::BEACON_EFFECTS[tier][e] != nullptr) { + actualCount++; + } else { + break; + } + } + + int totalWidth = (actualCount + 1) * 22 + actualCount * 2; + int startX = xo + 143 + ((actualCount + 1) * 24 - totalWidth) / 2; + + for (int e = 0; e < actualCount; ++e) { + MobEffect* effect = BeaconTileEntity::BEACON_EFFECTS[tier][e]; + if (effect == nullptr) break; + + int buttonId = (tier << 8) | effect->id; + BeaconPowerButton* button = new BeaconPowerButton( + this, buttonId, startX + e * 24, yo + 47, effect->id, tier); + buttons.push_back(button); + + if (tier >= beacon->getLevels()) { + button->active = false; + } else if (effect->id == beacon->getSecondaryPower()) { + button->setSelected(true); + } + } + + if (beacon->getPrimaryPower() > 0) { + int buttonId = (tier << 8) | beacon->getPrimaryPower(); + BeaconPowerButton* button = + new BeaconPowerButton(this, buttonId, startX + actualCount * 24, + yo + 47, beacon->getPrimaryPower(), tier); + buttons.push_back(button); + + if (tier >= beacon->getLevels()) { + button->active = false; + } else if (beacon->getPrimaryPower() == + beacon->getSecondaryPower()) { + button->setSelected(true); + } + } + } + + beaconConfirmButton->active = + (beacon->getItem(0) != nullptr && beacon->getPrimaryPower() > 0); +} + +void BeaconScreen::removed() { AbstractContainerScreen::removed(); } + +void BeaconScreen::renderLabels() { + std::wstring primaryLabel = + Language::getInstance()->getElement(L"tile.beacon.primary"); + font->drawShadow(primaryLabel, 25, 10, 0xE1E1E1); + + std::wstring secondaryLabel = + Language::getInstance()->getElement(L"tile.beacon.secondary"); + font->drawShadow(secondaryLabel, 125, 10, 0xE1E1E1); +} + +void BeaconScreen::renderBg(float a) { +#ifdef ENABLE_JAVA_GUIS + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + minecraft->textures->bindTexture(&GUI_BEACON_LOCATION); + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + blit(xo, yo, 0, 0, imageWidth, imageHeight); + + // Render payment item icons + itemRenderer->renderGuiItem( + font, minecraft->textures, + std::shared_ptr(new ItemInstance(Item::emerald_Id, 1, 0)), + xo + 42, yo + 109); + itemRenderer->renderGuiItem( + font, minecraft->textures, + std::shared_ptr(new ItemInstance(Item::diamond_Id, 1, 0)), + xo + 42 + 22, yo + 109); + itemRenderer->renderGuiItem(font, minecraft->textures, + std::shared_ptr( + new ItemInstance(Item::goldIngot_Id, 1, 0)), + xo + 42 + 44, yo + 109); + itemRenderer->renderGuiItem(font, minecraft->textures, + std::shared_ptr( + new ItemInstance(Item::ironIngot_Id, 1, 0)), + xo + 42 + 66, yo + 109); +#endif +} + +void BeaconScreen::render(int xm, int ym, float a) { + AbstractContainerScreen::render(xm, ym, a); + for (Button* button : buttons) { + AbstractBeaconButton* beaconButton = + dynamic_cast(button); + if (beaconButton && beaconButton->isHovered()) { + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + beaconButton->renderTooltip(xm, ym); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + break; + } + } +} + +void BeaconScreen::buttonClicked(Button* button) { + if (button->id == -2) { + minecraft->player->closeContainer(); + } else if (button->id == -1) { + // 4jcraft: copied from IUIScene_BeaconMenu + ByteArrayOutputStream baos; + DataOutputStream dos(&baos); + dos.writeInt(beacon->getPrimaryPower()); + dos.writeInt(beacon->getSecondaryPower()); + + minecraft->player->connection->send( + std::shared_ptr(new CustomPayloadPacket( + CustomPayloadPacket::SET_BEACON_PACKET, baos.toByteArray()))); + minecraft->player->closeContainer(); + } else if (dynamic_cast(button)) { + int effectId = button->id & 255; + int tier = button->id >> 8; + + if (tier < 3) { + beacon->setPrimaryPower(effectId); + } else { + beacon->setSecondaryPower(effectId); + } + + for (Button* btn : buttons) { + delete btn; + } + buttons.clear(); + init(); + tick(); + } +} \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/BeaconScreen.h b/Minecraft.Client/UI/Screens/BeaconScreen.h new file mode 100644 index 000000000..03a9db927 --- /dev/null +++ b/Minecraft.Client/UI/Screens/BeaconScreen.h @@ -0,0 +1,32 @@ +#pragma once + +#include "AbstractContainerScreen.h" +#include "../../../Minecraft.World/Containers/BeaconMenu.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" + +class BeaconConfirmButton; +class BeaconCancelButton; + +class BeaconScreen : public AbstractContainerScreen { +public: + BeaconScreen(std::shared_ptr inventory, + std::shared_ptr beacon); + virtual ~BeaconScreen(); + + void init() override; + void removed() override; + void tick() override; + void renderLabels() override; + void renderBg(float a) override; + void render(int xm, int ym, float a) override; + void buttonClicked(Button* button) override; + + std::shared_ptr getBeacon() { return beacon; } + +private: + std::shared_ptr inventory; + std::shared_ptr beacon; + BeaconMenu* beaconMenu; + BeaconConfirmButton* beaconConfirmButton; + bool buttonsNotDrawn; +}; \ No newline at end of file diff --git a/Minecraft.Client/UI/Screens/TitleScreen.cpp b/Minecraft.Client/UI/Screens/TitleScreen.cpp index 50b527600..650d49af8 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.cpp +++ b/Minecraft.Client/UI/Screens/TitleScreen.cpp @@ -5,6 +5,7 @@ #include "JoinMultiplayerScreen.h" #include "../../Rendering/Tesselator.h" #include "../../Textures/Textures.h" +#include "../../GameState/Options.h" #include "../../../Minecraft.World/Util/StringHelpers.h" #include "../../../Minecraft.World/IO/Streams/InputOutputStream.h" #include "../../../Minecraft.World/Headers/net.minecraft.locale.h" diff --git a/Minecraft.World/Entities/MobEffect.cpp b/Minecraft.World/Entities/MobEffect.cpp index a0822934f..828b5a4e3 100644 --- a/Minecraft.World/Entities/MobEffect.cpp +++ b/Minecraft.World/Entities/MobEffect.cpp @@ -443,4 +443,53 @@ void MobEffect::addAttributeModifiers(std::shared_ptr entity, double MobEffect::getAttributeModifierValue(int amplifier, AttributeModifier* original) { return original->getAmount() * (amplifier + 1); +} + +// 4jcraft: helper for inventoryscreen and beaconscreen +int MobEffect::javaId(int id) { + // mapped to java based on the inventory texture (see gui/inventory.png) + switch (id) { + case 1: + return 0; + case 2: + return 1; + case 3: + return 2; + case 4: + return 3; + case 5: + return 4; + case 18: + return 5; + case 19: + return 6; + case 10: + return 7; + case 14: + return 8; + case 17: + return 9; + case 8: + return 10; + case 9: + return 11; + case 16: + return 12; + case 15: + return 13; + case 11: + return 14; + case 12: + return 15; + case 13: + return 16; + case 20: + return 17; + case 21: + return 18; + case 22: + return 18; + default: + return 0; + } } \ No newline at end of file diff --git a/Minecraft.World/Entities/MobEffect.h b/Minecraft.World/Entities/MobEffect.h index 8e7ccce17..5576a2b4b 100644 --- a/Minecraft.World/Entities/MobEffect.h +++ b/Minecraft.World/Entities/MobEffect.h @@ -134,4 +134,6 @@ public: int amplifier); virtual double getAttributeModifierValue(int amplifier, AttributeModifier* original); + static int javaId( + int id); // 4jcraft: helper for inventoryscreen and beaconscreen }; \ No newline at end of file From 7889fbb4d27864279a49c05d3cce9785b1c802aa Mon Sep 17 00:00:00 2001 From: StevenSYS <139715581+StevenSYS@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:15:48 +0000 Subject: [PATCH 34/40] [Java Creative Inventory] Added tooltip when hovering over the tab icons. --- .../UI/Screens/CreativeInventoryScreen.cpp | 80 ++++++++++++++----- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp index 98e2a3aec..30f0d068c 100644 --- a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp @@ -17,7 +17,7 @@ // Static member initialization int CreativeInventoryScreen::selectedTabIndex = IUIScene_CreativeMenu::eCreativeInventoryTab_BuildingBlocks; -int CreativeInventoryScreen::tabIconIds +const int CreativeInventoryScreen::tabIconIds [IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT] = { // Building Blocks Tile::redBrick_Id, @@ -48,6 +48,7 @@ int CreativeInventoryScreen::tabIconIds // Materials Item::bucket_lava_Id}; + std::shared_ptr CreativeInventoryScreen::basicInventory = std::make_shared(0, L"", false, ITEMS_PER_PAGE); ItemRenderer* CreativeInventoryScreen::itemRenderer = new ItemRenderer(); @@ -378,6 +379,13 @@ void CreativeInventoryScreen::render(int xm, int ym, float a) { } AbstractContainerScreen::render(xm, ym, a); + + for (int i = 0; i < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; + i++) { + if (renderIconTooltip(i, xm, ym)) { + break; + } + } } void CreativeInventoryScreen::renderLabels() { @@ -409,7 +417,7 @@ void CreativeInventoryScreen::renderBg(float a) { for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; tab++) { if (tab != selectedTabIndex) { - drawTab(tab); + renderTab(tab); } } @@ -433,10 +441,33 @@ void CreativeInventoryScreen::renderBg(float a) { } // Render selected tab last (on top) - drawTab(selectedTabIndex); + renderTab(selectedTabIndex); #endif } +bool CreativeInventoryScreen::isMouseOverInternal(int tab, int mouseX, + int mouseY, int xo, int yo, + int w, int h) { + int tabColumn = tab % 6; + int x = (tabColumn * 28) + xo; + int y = yo; + + if (tabColumn == 5) { + x = imageWidth - 28 + 2; + } else if (tabColumn > 0) { + x += tabColumn; + } + + if (tab < 6) { + y -= 32; + } else { + y = imageHeight; + } + + return ((mouseX >= x && mouseX <= x + w) && + (mouseY >= y && mouseY <= y + h)); +} + void CreativeInventoryScreen::setCurrentCreativeTab(int tab) { if (tab < 0 || tab >= IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT) return; @@ -477,27 +508,14 @@ bool CreativeInventoryScreen::needsScrollBars() { } bool CreativeInventoryScreen::isMouseOverTab(int tab, int mouseX, int mouseY) { - int tabColumn = tab % 6; - int x = tabColumn * 28; - int y = 0; - - if (tabColumn == 5) { - x = imageWidth - 28 + 2; - } else if (tabColumn > 0) { - x += tabColumn; - } - - if (tab < 6) { - y -= 32; - } else { - y = imageHeight; - } - - return ((mouseX >= x && mouseX <= x + 28) && - (mouseY >= y && mouseY <= y + 32)); + return isMouseOverInternal(tab, mouseX, mouseY, 0, 0, 28, 32); } -void CreativeInventoryScreen::drawTab(int tab) { +bool CreativeInventoryScreen::isMouseOverIcon(int tab, int mouseX, int mouseY) { + return isMouseOverInternal(tab, mouseX, mouseY, 7, 12, 14, 16); +} + +void CreativeInventoryScreen::renderTab(int tab) { #ifdef ENABLE_JAVA_GUIS bool isSelected = (selectedTabIndex == tab); bool tabFirstRow = (tab < 6); @@ -544,4 +562,22 @@ void CreativeInventoryScreen::drawTab(int tab) { tabIcons[tab], x, y); glDisable(GL_LIGHTING); #endif +} + +bool CreativeInventoryScreen::renderIconTooltip(int tab, int mouseX, + int mouseY) { + int x = mouseX - (width - imageWidth) / 2; + int y = mouseY - (height - imageHeight) / 2; + + if (isMouseOverIcon(tab, x, y)) { + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + renderTooltip( + app.GetString(IUIScene_CreativeMenu::specs[tab]->m_descriptionId), + mouseX, mouseY); + glEnable(GL_LIGHTING); + glEnable(GL_DEPTH_TEST); + return true; + } + return false; } \ No newline at end of file From cd6959b9897ed1ab79209204c673ed283f7c8ea9 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 20:26:32 +0300 Subject: [PATCH 35/40] chore: fmt --- Minecraft.Client/UI/AbstractBeaconButton.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Minecraft.Client/UI/AbstractBeaconButton.cpp b/Minecraft.Client/UI/AbstractBeaconButton.cpp index b2d874ddc..92b49e881 100644 --- a/Minecraft.Client/UI/AbstractBeaconButton.cpp +++ b/Minecraft.Client/UI/AbstractBeaconButton.cpp @@ -28,13 +28,13 @@ void AbstractBeaconButton::renderBg(Minecraft* minecraft, int xm, int ym) { int texU = 0; if (!active) { - texU += w * 2; + texU += w * 2; } else if (selected) { - texU += w * 1; + texU += w * 1; } else if (hovered) { - texU += w * 3; + texU += w * 3; } - int texV = 219; + int texV = 219; blit(x, y, texU, texV, w, h); From ca276943f237f6bac148fe77ca07722436d70d14 Mon Sep 17 00:00:00 2001 From: Sally Knight Date: Sun, 29 Mar 2026 20:39:49 +0300 Subject: [PATCH 36/40] fix(jui): make creative screen correctly override menu clicked() (also made rendertooltip names in AbstractContainerScreen match so my editor stops complaining) --- .../UI/Screens/AbstractContainerScreen.cpp | 20 +++++++++---------- .../UI/Screens/AbstractContainerScreen.h | 12 +++++------ .../UI/Screens/CreativeInventoryScreen.cpp | 4 ++-- .../UI/Screens/CreativeInventoryScreen.h | 4 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp index 80e1ff815..00a7e81f1 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.cpp @@ -116,7 +116,7 @@ void AbstractContainerScreen::render(int xm, int ym, float a) { // places) void AbstractContainerScreen::renderTooltipInternal( const std::vector& cleanedLines, - const std::vector& lineColors, int mouseX, int mouseY) { + const std::vector& lineColors, int xm, int ym) { if (cleanedLines.empty()) return; int tooltipWidth = 0; @@ -125,8 +125,8 @@ void AbstractContainerScreen::renderTooltipInternal( if (lineWidth > tooltipWidth) tooltipWidth = lineWidth; } - int tooltipX = mouseX + 12; - int tooltipY = mouseY - 12; + int tooltipX = xm + 12; + int tooltipY = ym - 12; int tooltipHeight = 8; if (cleanedLines.size() > 1) { @@ -175,7 +175,7 @@ void AbstractContainerScreen::renderTooltipInternal( } void AbstractContainerScreen::renderTooltip(std::shared_ptr item, - int mouseX, int mouseY) { + int xm, int ym) { if (item == nullptr) return; std::vector elementName; @@ -240,12 +240,12 @@ void AbstractContainerScreen::renderTooltip(std::shared_ptr item, lineColors[0] = app.GetHTMLColour(item->getRarity()->color); } - renderTooltipInternal(cleanedLines, lineColors, mouseX, mouseY); + renderTooltipInternal(cleanedLines, lineColors, xm, ym); } } void AbstractContainerScreen::renderTooltip( - const std::vector& lines, int mouseX, int mouseY) { + const std::vector& lines, int xm, int ym) { if (lines.empty()) return; std::vector cleanedLines = lines; @@ -260,12 +260,12 @@ void AbstractContainerScreen::renderTooltip( } } - renderTooltipInternal(cleanedLines, lineColors, mouseX, mouseY); + renderTooltipInternal(cleanedLines, lineColors, xm, ym); } -void AbstractContainerScreen::renderTooltip(const std::wstring& line, - int mouseX, int mouseY) { - renderTooltip(std::vector{line}, mouseX, mouseY); +void AbstractContainerScreen::renderTooltip(const std::wstring& line, int xm, + int ym) { + renderTooltip(std::vector{line}, xm, ym); } void AbstractContainerScreen::renderLabels() {} diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h index 72f774ef6..462e178d5 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h @@ -36,9 +36,9 @@ protected: // used in other places virtual void renderTooltipInternal( const std::vector& cleanedLines, - const std::vector& lineColors, int mouseX, int mouseY); - virtual void renderTooltip(std::shared_ptr item, int x, - int y); + const std::vector& lineColors, int xm, int ym); + virtual void renderTooltip(std::shared_ptr item, int xm, + int ym); private: virtual void renderSlot(Slot* slot); @@ -55,7 +55,7 @@ public: virtual void tick() override; // 4jcraft: 1.6.x era overloads - virtual void renderTooltip(const std::vector& lines, int x, - int y); - virtual void renderTooltip(const std::wstring& line, int x, int y); + virtual void renderTooltip(const std::vector& lines, int xm, + int ym); + virtual void renderTooltip(const std::wstring& line, int xm, int ym); }; diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp index 30f0d068c..f2773df8f 100644 --- a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp @@ -90,8 +90,8 @@ bool CreativeInventoryScreen::ContainerCreative::stillValid( std::shared_ptr CreativeInventoryScreen::ContainerCreative::clicked( - int slotIndex, int buttonNum, int clickType, - std::shared_ptr player) { + int slotIndex, int buttonNum, int clickType, std::shared_ptr player, + bool looped) { std::shared_ptr inventory = player->inventory; std::shared_ptr carried = inventory->getCarried(); diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h index 6a4ba7847..f4f6004fa 100644 --- a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h @@ -51,10 +51,10 @@ public: std::vector> itemList; ContainerCreative(std::shared_ptr player); - virtual bool stillValid(std::shared_ptr player); + virtual bool stillValid(std::shared_ptr player) override; virtual std::shared_ptr clicked( int slotIndex, int buttonNum, int clickType, - std::shared_ptr player); + std::shared_ptr player, bool looped = false) override; void scrollTo(float pos); bool canScroll(); }; From ad67f4be71255a77df93afffe07a1ea5271224b2 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:41:46 -0500 Subject: [PATCH 37/40] refactor: configure classic panorama at compile-time --- Minecraft.Assets/Common/res/lang/en_US.lang | 1 - Minecraft.Client/GameState/Options.cpp | 18 +- Minecraft.Client/GameState/Options.h | 4 +- Minecraft.Client/UI/Screens/TitleScreen.cpp | 353 +++++++++--------- .../UI/Screens/VideoSettingsScreen.cpp | 3 +- Minecraft.Client/meson.build | 4 + meson.options | 5 + 7 files changed, 188 insertions(+), 200 deletions(-) diff --git a/Minecraft.Assets/Common/res/lang/en_US.lang b/Minecraft.Assets/Common/res/lang/en_US.lang index 890a2f576..44040b2b7 100644 --- a/Minecraft.Assets/Common/res/lang/en_US.lang +++ b/Minecraft.Assets/Common/res/lang/en_US.lang @@ -189,7 +189,6 @@ options.renderDistance.short=Short options.renderDistance.normal=Normal options.renderDistance.far=Far options.viewBobbing=View Bobbing -options.classicPanorama=Classic Panorama options.ao=Smooth Lighting options.anaglyph=3D Anaglyph options.framerateLimit=Performance diff --git a/Minecraft.Client/GameState/Options.cpp b/Minecraft.Client/GameState/Options.cpp index 3f6f4e4b1..07549b5c4 100644 --- a/Minecraft.Client/GameState/Options.cpp +++ b/Minecraft.Client/GameState/Options.cpp @@ -16,7 +16,7 @@ // 4J - the Option sub-class used to be an java enumerated type, trying to // emulate that functionality here -const Options::Option Options::Option::options[18] = { +const Options::Option Options::Option::options[17] = { Options::Option(L"options.music", true, false), Options::Option(L"options.sound", true, false), Options::Option(L"options.invertMouse", false, true), @@ -34,7 +34,6 @@ const Options::Option Options::Option::options[18] = { Options::Option(L"options.gamma", true, false), Options::Option(L"options.renderClouds", false, true), Options::Option(L"options.particles", false, false), - Options::Option(L"options.classicPanorama", false, true), }; const Options::Option* Options::Option::MUSIC = &Options::Option::options[0]; @@ -66,8 +65,6 @@ const Options::Option* Options::Option::RENDER_CLOUDS = &Options::Option::options[15]; const Options::Option* Options::Option::PARTICLES = &Options::Option::options[16]; -const Options::Option* Options::Option::CLASSIC_PANORAMA = - &(Options::Option::options[17]); const Options::Option* Options::Option::getItem(int id) { return &options[id]; } @@ -114,8 +111,6 @@ void Options::init() { invertYMouse = false; viewDistance = 0; bobView = true; - // 4jcraft - classicPanorama = false; anaglyph3d = false; advancedOpengl = false; @@ -245,10 +240,6 @@ void Options::toggle(const Options::Option* option, int dir) { // 4J-PB - changing // 4jcraft: uncommented this so that the view bobbing option works if (option == Option::VIEW_BOBBING) bobView = !bobView; - // 4jcraft - if (option == Option::CLASSIC_PANORAMA) { - classicPanorama = !classicPanorama; - } if (option == Option::RENDER_CLOUDS) renderClouds = !renderClouds; if (option == Option::ADVANCED_OPENGL) { advancedOpengl = !advancedOpengl; @@ -301,8 +292,6 @@ bool Options::getBooleanValue(const Options::Option* item) { // types if (item == Option::INVERT_MOUSE) return invertYMouse; if (item == Option::VIEW_BOBBING) return bobView; - // 4jcraft - if (item == Option::CLASSIC_PANORAMA) return classicPanorama; if (item == Option::ANAGLYPH) return anaglyph3d; if (item == Option::ADVANCED_OPENGL) return advancedOpengl; if (item == Option::AMBIENT_OCCLUSION) return ambientOcclusion; @@ -415,8 +404,6 @@ void Options::load() { if (cmds[0] == L"guiScale") guiScale = _fromString(cmds[1]); if (cmds[0] == L"particles") particles = _fromString(cmds[1]); if (cmds[0] == L"bobView") bobView = cmds[1] == L"true"; - // 4jcraft - if (cmds[0] == L"classicPanorama") classicPanorama = cmds[1] == L"true"; if (cmds[0] == L"anaglyph3d") anaglyph3d = cmds[1] == L"true"; if (cmds[0] == L"advancedOpengl") advancedOpengl = cmds[1] == L"true"; if (cmds[0] == L"fpsLimit") framerateLimit = _fromString(cmds[1]); @@ -471,9 +458,6 @@ void Options::save() { dos.writeChars(L"guiScale:" + _toString(guiScale)); dos.writeChars(L"particles:" + _toString(particles)); dos.writeChars(L"bobView:" + std::wstring(bobView ? L"true" : L"false")); - // 4jcraft - dos.writeChars(L"classicPanorama:" + - std::wstring(classicPanorama ? L"true" : L"false")); dos.writeChars(L"anaglyph3d:" + std::wstring(anaglyph3d ? L"true" : L"false")); dos.writeChars(L"advancedOpengl:" + diff --git a/Minecraft.Client/GameState/Options.h b/Minecraft.Client/GameState/Options.h index bb84fc759..b727da4a0 100644 --- a/Minecraft.Client/GameState/Options.h +++ b/Minecraft.Client/GameState/Options.h @@ -13,7 +13,7 @@ public: // 4J - this used to be an enum class Option { public: - static const Option options[18]; + static const Option options[17]; static const Option* MUSIC; static const Option* SOUND; static const Option* INVERT_MOUSE; @@ -31,7 +31,6 @@ public: static const Option* GAMMA; static const Option* RENDER_CLOUDS; static const Option* PARTICLES; - static const Option* CLASSIC_PANORAMA; private: const bool _isProgress; @@ -62,7 +61,6 @@ public: bool invertYMouse; int viewDistance; bool bobView; - bool classicPanorama; bool anaglyph3d; bool advancedOpengl; int framerateLimit; diff --git a/Minecraft.Client/UI/Screens/TitleScreen.cpp b/Minecraft.Client/UI/Screens/TitleScreen.cpp index 650d49af8..8aac613d5 100644 --- a/Minecraft.Client/UI/Screens/TitleScreen.cpp +++ b/Minecraft.Client/UI/Screens/TitleScreen.cpp @@ -164,204 +164,203 @@ void TitleScreen::buttonClicked(Button* button) { // method void TitleScreen::renderPanorama(float a) { #ifdef ENABLE_JAVA_GUIS + Tesselator* t = Tesselator::getInstance(); - if (minecraft->options->classicPanorama) { - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - gluPerspective(120.0f, 1.0f, 0.05f, 10.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glRotatef(180.0f, 1.0f, 0.0f, 0.0f); - glEnable(GL_BLEND); - glDisable(GL_ALPHA_TEST); - glDisable(GL_CULL_FACE); - glDepthMask(false); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - char offsetPasses = 8; +#ifdef CLASSIC_PANORAMA + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + gluPerspective(120.0f, 1.0f, 0.05f, 10.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glRotatef(180.0f, 1.0f, 0.0f, 0.0f); + glEnable(GL_BLEND); + glDisable(GL_ALPHA_TEST); + glDisable(GL_CULL_FACE); + glDepthMask(false); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + char offsetPasses = 8; - for (int i = 0; i < (offsetPasses * offsetPasses); i++) { + for (int i = 0; i < (offsetPasses * offsetPasses); i++) { + glPushMatrix(); + float x = + ((float)(i % offsetPasses) / (float)offsetPasses - 0.5f) / 64.0f; + float y = + ((float)(i / offsetPasses) / (float)offsetPasses - 0.5f) / 64.0f; + float z = 0.0f; + glTranslatef(x, y, z); + glRotatef(sin((vo + a) / 400.0f) * 25.0f + 20.0f, 1.0f, 0.0f, 0.0f); + glRotatef(-(vo + a) * 0.1f, 0.0f, 1.0f, 0.0f); + + for (int j = 0; j < 6; j++) { glPushMatrix(); - float x = ((float)(i % offsetPasses) / (float)offsetPasses - 0.5f) / - 64.0f; - float y = ((float)(i / offsetPasses) / (float)offsetPasses - 0.5f) / - 64.0f; - float z = 0.0f; - glTranslatef(x, y, z); - glRotatef(sin((vo + a) / 400.0f) * 25.0f + 20.0f, 1.0f, 0.0f, 0.0f); - glRotatef(-(vo + a) * 0.1f, 0.0f, 1.0f, 0.0f); - for (int j = 0; j < 6; j++) { - glPushMatrix(); - - switch (j) { - case 1: - glRotatef(90.0f, 0.0f, 1.0f, 0.0f); - break; - case 2: - glRotatef(180.0f, 0.0f, 1.0f, 0.0f); - break; - case 3: - glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); - break; - case 4: - glRotatef(90.0f, 1.0f, 0.0f, 0.0f); - break; - case 5: - glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); - break; - default: - break; - } - - glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture( - TN_TITLE_BG_PANORAMA0 + j)); - t->begin(); - t->color(16777215, 255 / (i + 1)); - t->vertexUV(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f); - t->vertexUV(1.0f, -1.0f, 1.0f, 1.0f, 0.0f); - t->vertexUV(1.0f, 1.0f, 1.0f, 1.0f, 1.0f); - t->vertexUV(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f); - t->end(); - glPopMatrix(); + switch (j) { + case 1: + glRotatef(90.0f, 0.0f, 1.0f, 0.0f); + break; + case 2: + glRotatef(180.0f, 0.0f, 1.0f, 0.0f); + break; + case 3: + glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + break; + case 4: + glRotatef(90.0f, 1.0f, 0.0f, 0.0f); + break; + case 5: + glRotatef(-90.0f, 1.0f, 0.0f, 0.0f); + break; + default: + break; } + + glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture( + TN_TITLE_BG_PANORAMA0 + j)); + t->begin(); + t->color(16777215, 255 / (i + 1)); + t->vertexUV(-1.0f, -1.0f, 1.0f, 0.0f, 0.0f); + t->vertexUV(1.0f, -1.0f, 1.0f, 1.0f, 0.0f); + t->vertexUV(1.0f, 1.0f, 1.0f, 1.0f, 1.0f); + t->vertexUV(-1.0f, 1.0f, 1.0f, 0.0f, 1.0f); + t->end(); glPopMatrix(); - glColorMask(true, true, true, false); } - - t->offset(0.0f, 0.0f, 0.0f); - glColorMask(true, true, true, true); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glDepthMask(true); - glEnable(GL_CULL_FACE); - glEnable(GL_ALPHA_TEST); - glEnable(GL_DEPTH_TEST); - } else { - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(0, width, height, 0, 1000, 3000); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - glTranslatef(0, 0, -2000); - - glDisable(GL_LIGHTING); - glDisable(GL_FOG); - glEnable(GL_TEXTURE_2D); - glDisable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDepthMask(false); - - glBindTexture(GL_TEXTURE_2D, - minecraft->textures->loadTexture(TN_TITLE_BG_PANORAMA)); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - float off = vo * 0.0004f; - - float screenAspect = (float)width / (float)height; - float texAspect = 1748.0f / 144.0f; - float scale; - if (screenAspect > texAspect) { - scale = (float)width / 1748.0f; - } else { - scale = (float)height / 144.0f; - } - - float texWidth = 1748.0f * scale; - float texHeight = 144.0f * scale; - float yOff = (height - texHeight) / 2.0f; - - float uMax = off + (texWidth / 1748.0f); - - t->begin(GL_QUADS); - t->color(0xffffff, 255); - t->vertexUV(0, yOff + texHeight, 0, off, 1.0f); - t->vertexUV(texWidth, yOff + texHeight, 0, uMax, 1.0f); - t->vertexUV(texWidth, yOff, 0, uMax, 0.0f); - t->vertexUV(0, yOff, 0, off, 0.0f); - t->end(); - - glDepthMask(true); - glDisable(GL_BLEND); - glEnable(GL_ALPHA_TEST); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); glPopMatrix(); + glColorMask(true, true, true, false); } + + t->offset(0.0f, 0.0f, 0.0f); + glColorMask(true, true, true, true); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glDepthMask(true); + glEnable(GL_CULL_FACE); + glEnable(GL_ALPHA_TEST); + glEnable(GL_DEPTH_TEST); +#else + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0, width, height, 0, 1000, 3000); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glTranslatef(0, 0, -2000); + + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glEnable(GL_TEXTURE_2D); + glDisable(GL_ALPHA_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDepthMask(false); + + glBindTexture(GL_TEXTURE_2D, + minecraft->textures->loadTexture(TN_TITLE_BG_PANORAMA)); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + float off = vo * 0.0004f; + + float screenAspect = (float)width / (float)height; + float texAspect = 1748.0f / 144.0f; + float scale; + if (screenAspect > texAspect) { + scale = (float)width / 1748.0f; + } else { + scale = (float)height / 144.0f; + } + + float texWidth = 1748.0f * scale; + float texHeight = 144.0f * scale; + float yOff = (height - texHeight) / 2.0f; + + float uMax = off + (texWidth / 1748.0f); + + t->begin(GL_QUADS); + t->color(0xffffff, 255); + t->vertexUV(0, yOff + texHeight, 0, off, 1.0f); + t->vertexUV(texWidth, yOff + texHeight, 0, uMax, 1.0f); + t->vertexUV(texWidth, yOff, 0, uMax, 0.0f); + t->vertexUV(0, yOff, 0, off, 0.0f); + t->end(); + + glDepthMask(true); + glDisable(GL_BLEND); + glEnable(GL_ALPHA_TEST); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); +#endif #endif } // 4jcraft void TitleScreen::renderSkybox(float a) { #ifdef ENABLE_JAVA_GUIS - if (minecraft->options->classicPanorama) { - glViewport(0, 0, 256, 256); - } +#ifdef CLASSIC_PANORAMA + glViewport(0, 0, 256, 256); +#endif renderPanorama(a); - if (minecraft->options->classicPanorama) { - glDisable(GL_TEXTURE_2D); - glEnable(GL_TEXTURE_2D); +#ifdef CLASSIC_PANORAMA + glDisable(GL_TEXTURE_2D); + glEnable(GL_TEXTURE_2D); - for (int i = 0; i < 8; i++) { - rotateAndBlur(a); - } - - glViewport(0, 0, minecraft->width, minecraft->height); - - Tesselator* t = Tesselator::getInstance(); - t->begin(); - float aspect = - width > height ? 120.0f / (float)width : 120.0f / (float)height; - float sWidth = (float)height * aspect / 256.0f; - float sHeight = (float)width * aspect / 256.0f; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - t->color(1.0f, 1.0f, 1.0f, 1.0f); - t->vertexUV(0.0f, height, 0.0f, (0.5f - sWidth), (0.5f + sHeight)); - t->vertexUV(width, height, 0.0f, (0.5f - sWidth), (0.5f - sHeight)); - t->vertexUV(width, 0.0f, 0.0f, (0.5f + sWidth), (0.5f - sHeight)); - t->vertexUV(0.0f, 0.0f, 0.0f, (0.5f + sWidth), (0.5f + sHeight)); - t->end(); + for (int i = 0; i < 8; i++) { + rotateAndBlur(a); } + + glViewport(0, 0, minecraft->width, minecraft->height); + + Tesselator* t = Tesselator::getInstance(); + t->begin(); + float aspect = + width > height ? 120.0f / (float)width : 120.0f / (float)height; + float sWidth = (float)height * aspect / 256.0f; + float sHeight = (float)width * aspect / 256.0f; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + t->color(1.0f, 1.0f, 1.0f, 1.0f); + t->vertexUV(0.0f, height, 0.0f, (0.5f - sWidth), (0.5f + sHeight)); + t->vertexUV(width, height, 0.0f, (0.5f - sWidth), (0.5f - sHeight)); + t->vertexUV(width, 0.0f, 0.0f, (0.5f + sWidth), (0.5f - sHeight)); + t->vertexUV(0.0f, 0.0f, 0.0f, (0.5f + sWidth), (0.5f + sHeight)); + t->end(); +#endif #endif } // 4jcraft void TitleScreen::rotateAndBlur(float a) { -#ifdef ENABLE_JAVA_GUIS - if (minecraft->options->classicPanorama) { - glBindTexture(GL_TEXTURE_2D, viewportTexture); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 256, 256); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColorMask(true, true, true, false); - Tesselator* t = Tesselator::getInstance(); - t->begin(); - char blurPasses = 3; +#if defined(ENABLE_JAVA_GUIS) && defined(CLASSIC_PANORAMA) + glBindTexture(GL_TEXTURE_2D, viewportTexture); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 256, 256); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColorMask(true, true, true, false); + Tesselator* t = Tesselator::getInstance(); + t->begin(); + char blurPasses = 3; - for (int i = 0; i < blurPasses; i++) { - t->color(1.0f, 1.0f, 1.0f, 1.0f / (float)(i + 1)); - float offset = (float)(i - blurPasses / 2) / 256.0f; - t->vertexUV(width, height, 0.0f, (0.0f + offset), 0.0f); - t->vertexUV(width, 0.0f, 0.0f, (1.0f + offset), 0.0f); - t->vertexUV(0.0f, 0.0f, 0.0f, (1.0f + offset), 1.0f); - t->vertexUV(0.0f, height, 0.0f, (0.0f + offset), 1.0f); - } - - t->end(); - glColorMask(true, true, true, true); + for (int i = 0; i < blurPasses; i++) { + t->color(1.0f, 1.0f, 1.0f, 1.0f / (float)(i + 1)); + float offset = (float)(i - blurPasses / 2) / 256.0f; + t->vertexUV(width, height, 0.0f, (0.0f + offset), 0.0f); + t->vertexUV(width, 0.0f, 0.0f, (1.0f + offset), 0.0f); + t->vertexUV(0.0f, 0.0f, 0.0f, (1.0f + offset), 1.0f); + t->vertexUV(0.0f, height, 0.0f, (0.0f + offset), 1.0f); } + + t->end(); + glColorMask(true, true, true, true); #endif } @@ -377,10 +376,10 @@ void TitleScreen::render(int xm, int ym, float a) { int logoY = 30; // 4jcraft: gradient for classic panorama - if (minecraft->options->classicPanorama) { - fillGradient(0, 0, width, height, -2130706433, 16777215); - fillGradient(0, 0, width, height, 0, INT_MIN); - } +#ifdef CLASSIC_PANORAMA + fillGradient(0, 0, width, height, -2130706433, 16777215); + fillGradient(0, 0, width, height, 0, INT_MIN); +#endif glBindTexture(GL_TEXTURE_2D, minecraft->textures->loadTexture(TN_TITLE_MCLOGO)); diff --git a/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp b/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp index 4a2777494..d445f0052 100644 --- a/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp +++ b/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp @@ -29,8 +29,7 @@ void VideoSettingsScreen::init() { Options::Option::GUI_SCALE, Options::Option::ADVANCED_OPENGL, Options::Option::GAMMA, - Options::Option::FOV, - Options::Option::CLASSIC_PANORAMA}; + Options::Option::FOV}; for (int i = 0; i < ITEM_COUNT; i++) { const Options::Option* item = items[i]; diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index 8bcc874b1..9bb614fc4 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -75,6 +75,10 @@ if get_option('enable_vsync') global_cpp_defs += '-DENABLE_VSYNC' endif +if get_option('classic_panorama') + global_cpp_defs += '-DCLASSIC_PANORAMA' +endif + if get_option('ui_backend') == 'shiggy' shiggy_dep = dependency( 'shiggy', diff --git a/meson.options b/meson.options index 545d2a159..68eb92598 100644 --- a/meson.options +++ b/meson.options @@ -4,6 +4,11 @@ option('ui_backend', value : 'shiggy', description : 'Specifies a backend implementation for the game UI.') +option('classic_panorama', + type : 'boolean', + value : false, + description : 'Enable classic java edition panorama (ui_backend=java ONLY).') + option('enable_vsync', type : 'boolean', value : true, From 238b0019106ebebd74936d28a5e6fdb42ea5f298 Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:44:22 -0500 Subject: [PATCH 38/40] fix: remove remaining iggy checkmarks for pano --- .../Platform/Common/UI/UIScene_SettingsOptionsMenu.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h b/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h index 6bf721a2d..67c30660d 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h +++ b/Minecraft.Client/Platform/Common/UI/UIScene_SettingsOptionsMenu.h @@ -6,8 +6,6 @@ class UIScene_SettingsOptionsMenu : public UIScene { private: enum EControls { eControl_ViewBob, - // 4jcraft - eControl_ClassicPanorama, eControl_ShowHints, eControl_ShowTooltips, eControl_InGameGamertags, @@ -22,8 +20,8 @@ protected: static int m_iDifficultyTitleSettingA[4]; private: - UIControl_CheckBox m_checkboxViewBob, m_checkboxClassicPanorama, - m_checkboxShowHints, m_checkboxShowTooltips, m_checkboxInGameGamertags, + UIControl_CheckBox m_checkboxViewBob, m_checkboxShowHints, + m_checkboxShowTooltips, m_checkboxInGameGamertags, m_checkboxMashupWorlds; // Checkboxes UIControl_Slider m_sliderAutosave, m_sliderDifficulty; // Sliders UIControl_Label m_labelDifficultyText; // Text @@ -31,8 +29,6 @@ private: UI_BEGIN_MAP_ELEMENTS_AND_NAMES(UIScene) UI_MAP_ELEMENT(m_checkboxViewBob, "ViewBob") - // 4jcraft - UI_MAP_ELEMENT(m_checkboxClassicPanorama, "ClassicPanorama") UI_MAP_ELEMENT(m_checkboxShowHints, "ShowHints") UI_MAP_ELEMENT(m_checkboxShowTooltips, "ShowTooltips") UI_MAP_ELEMENT(m_checkboxInGameGamertags, "InGameGamertags") From 3653b7b465da2b3cff86f1c9ab764a9c6854886f Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 15:50:07 -0500 Subject: [PATCH 39/40] fix: readjust `VideoSettingsScreen` ITEM_COUNT --- Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp b/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp index d445f0052..e97aa1f54 100644 --- a/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp +++ b/Minecraft.Client/UI/Screens/VideoSettingsScreen.cpp @@ -7,7 +7,7 @@ #include "../../../Minecraft.World/Headers/net.minecraft.locale.h" // 4jcraft -#define ITEM_COUNT 11 +#define ITEM_COUNT 10 VideoSettingsScreen::VideoSettingsScreen(Screen* lastScreen, Options* options) { this->title = L"Video Settings"; // 4J - added From a856a7e54e65049217fd326cc65b7f0e289ebcef Mon Sep 17 00:00:00 2001 From: Tropical <42101043+tropicaaal@users.noreply.github.com> Date: Sun, 29 Mar 2026 16:12:30 -0500 Subject: [PATCH 40/40] fix: rebase issue with CreativeInventoryScreen --- Minecraft.Client/UI/Gui.h | 2 +- .../UI/Screens/CreativeInventoryScreen.cpp | 8 ++-- .../UI/Screens/CreativeInventoryScreen.h | 37 +++++++++++-------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Minecraft.Client/UI/Gui.h b/Minecraft.Client/UI/Gui.h index 2ced840c3..4077c9c40 100644 --- a/Minecraft.Client/UI/Gui.h +++ b/Minecraft.Client/UI/Gui.h @@ -2,9 +2,9 @@ #include #include "GuiComponent.h" #include "GuiMessage.h" +#include "../../../Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.h" class Random; class Minecraft; -class ItemRenderer; class Gui : public GuiComponent { private: diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp index f2773df8f..ac8481673 100644 --- a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp @@ -70,7 +70,7 @@ CreativeInventoryScreen::ContainerCreative::ContainerCreative( } // Add hotbar slots (9 slots at bottom) - for (int k = 0; k < 9; k++) { + for (int k = 0; k < 9; ++k) { addSlot(new Slot(inventoryplayer, k, 9 + k * 18, 112)); } @@ -181,8 +181,8 @@ void CreativeInventoryScreen::ContainerCreative::scrollTo(float pos) { j = 0; } - for (int k = 0; k < ROWS; k++) { - for (int l = 0; l < COLUMNS; l++) { + for (int k = 0; k < ROWS; ++k) { + for (int l = 0; l < COLUMNS; ++l) { int i1 = l + (k + j) * COLUMNS; if (i1 >= 0 && i1 < (int)itemList.size()) { @@ -484,7 +484,7 @@ void CreativeInventoryScreen::setCurrentCreativeTab(int tab) { IUIScene_CreativeMenu::specs[tab]; // Add items from static groups - for (int i = 0; i < spec->m_staticGroupsCount; i++) { + for (int i = 0; i < spec->m_staticGroupsCount; ++i) { int groupIdx = spec->m_staticGroupsA[i]; if (groupIdx >= 0 && groupIdx < diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h index f4f6004fa..e8fd36877 100644 --- a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h @@ -20,7 +20,8 @@ private: static int selectedTabIndex; // Array of item ids for the tab icons - static int tabIconIds[IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT]; + static const int + tabIconIds[IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT]; // Temporary inventory for creative mode items static std::shared_ptr basicInventory; @@ -60,23 +61,29 @@ public: }; public: - CreativeInventoryScreen(std::shared_ptr player); - virtual void removed() override; - virtual void init() override; - virtual void containerTick(); - virtual void tick() override; - virtual void updateEvents() override; - virtual void keyPressed(wchar_t eventCharacter, int eventKey) override; - virtual void mouseClicked(int x, int y, int buttonNum) override; - virtual void mouseReleased(int x, int y, int buttonNum) override; - virtual void render(int xm, int ym, float a) override; + CreativeInventoryScreen(std::shared_ptr player); + virtual void removed() override; + virtual void init() override; + virtual void containerTick(); + virtual void tick() override; + virtual void updateEvents() override; + virtual void keyPressed(wchar_t eventCharacter, int eventKey) override; + virtual void mouseClicked(int x, int y, int buttonNum) override; + virtual void mouseReleased(int x, int y, int buttonNum) override; + virtual void render(int xm, int ym, float a) override; + protected: - virtual void renderLabels() override; - virtual void renderBg(float a) override; + virtual void renderLabels() override; + virtual void renderBg(float a) override; + virtual bool isMouseOverInternal(int tab, int mouseX, int mouseY, int xo, + int yo, int w, int h); + private: void setCurrentCreativeTab(int tab); void selectTab(int tab); bool needsScrollBars(); bool isMouseOverTab(int tab, int mouseX, int mouseY); - void drawTab(int tab); -}; + bool isMouseOverIcon(int tab, int mouseX, int mouseY); + void renderTab(int tab); + bool renderIconTooltip(int tab, int mouseX, int mouseY); +}; \ No newline at end of file