From 66fb0f1a962de310f568504924be937afb6bfd7f Mon Sep 17 00:00:00 2001 From: lotaviods Date: Fri, 13 Mar 2026 01:00:52 -0300 Subject: [PATCH] feat: Implement creative inventory --- .../gui/creative_inventory/tab_inventory.png | Bin 0 -> 1125 bytes .../creative_inventory/tab_item_search.png | Bin 0 -> 1005 bytes .../gui/creative_inventory/tab_items.png | Bin 0 -> 965 bytes .../res/1_2_2/gui/creative_inventory/tabs.png | Bin 0 -> 1723 bytes Minecraft.Client/Minecraft.cpp | 8 +- .../Common/UI/IUIScene_CreativeMenu.h | 3 + Minecraft.Client/Textures/Textures.cpp | 4 + Minecraft.Client/Textures/Textures.h | 4 + .../UI/Screens/AbstractContainerScreen.h | 6 +- .../UI/Screens/CreativeInventoryScreen.cpp | 527 ++++++++++++++++++ .../UI/Screens/CreativeInventoryScreen.h | 71 +++ 11 files changed, 620 insertions(+), 3 deletions(-) create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_inventory.png create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_item_search.png create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_items.png create mode 100644 Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tabs.png create mode 100644 Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp create mode 100644 Minecraft.Client/UI/Screens/CreativeInventoryScreen.h diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_inventory.png b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_inventory.png new file mode 100644 index 0000000000000000000000000000000000000000..f328206c0245b74d3669ac70fcdcec12b4e4079c GIT binary patch literal 1125 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD19Zwg>kcwMx?*@9kb`WWJ zsIpq?EL)7j?{{KMu?^fBDct+E-}Y!;eae(ES2O5}Wd7wj@0&jtO*|;}e7~n^a_Ai1 zV=Qw%vq`iS1ka8C9Uvi`yG~ia_v^-=ii;4v5XK zQ}`fyK*+>*vQ6yu)aY!#`=^gbZMebEV>a9O(wnl~=eORwcki9m^!wiDuCq?v&cMPT zz~BIKH3s7cum3K`584d=yBebznu<*3GNkODB(a_?=-BID|4(h&qRgrg8+$$ST=(Z) zSd3_}j(XMXDmTN4VPED-ezD2~E|3)w9t_j|Es^Fpz&o#mc{@V`0|OHS2ZI6{;|QjTaWFJEIGJoicbI^Bm=?zp7 z1%HMjS(i-N*A^$fFftd|$aP=Mnria(z)o>A@A_n0hJ9<%ae`6P+qD0F9j=4zOn zwdS$Qf&+{NLA|AOI7I%49q=^0R;V=LJl60;cF2ryhB+b0vg%B%d=9bKUGINCnJoKy z2~Qp)I3j-hW`M+Bsw{Qdm-IotlNKlS&EF)Dblg*A z)#q72Vs(N73{CkUQtaKQI{V{5Q$g|KGbb#2uH{@Vo4JA?K20+DqX2VA*{KcD;FQx4 zpLIKo;okBZ^Sk1``5lIVZJ$LQcE+vWE8S9JA+yX@;IG&Mnfn~ci+rz~wP#I`XnU08 zmCSVR%jfsb4%ky3a!6rGS~$`x1LFrV90>|N?yw~~>}o(kfHjR`S5Ay#97giLmKWXM VQ^B~kY9a#=c)I$ztaD0e0sx9rIaL4v literal 0 HcmV?d00001 diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_item_search.png b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_item_search.png new file mode 100644 index 0000000000000000000000000000000000000000..be178b743a2c901d9882b068275747d6e0853bbe GIT binary patch literal 1005 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJFdEuJopAr-gY-U;m5l_PQ> zu{2O{XKz%;1tTW$pxXx1pDt10yZhAo+U+^d8;d{3%KUKud*a8lkfTWkY7cbe8xlFp zuYSsx=Sp65wwPtn%4hucE+zZ#JG(Ea`o~{;Us^cr);3+g<-s>NLe9(A#ToBwJhSa` z>0I+$yZ14E*mXBA-E7Y1pKpBC7#bKDm>4)16p$G?DgW%Ro+)c)NLjruyFTP|+e2r@ zj9F@x$LybNI6iNRZT-i6U))`__cO}$A73(a|FN6N{-@?$`M&fxXFtRk3=~2Q3{u39}J`0A@Sn=BX>ixFgmhi|i zg4NuxWAMGK;(xvL+l|Rv+WG643)Wtf3vaY#!r}(BtoP< gJhovs@<)_?$p3rO>Yd!y0<$rLr>mdKI;Vst0BXzrt^fc4 literal 0 HcmV?d00001 diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_items.png b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_items.png new file mode 100644 index 0000000000000000000000000000000000000000..2b6fbb9c3fe931652d714e1c0af02261647e4d8e GIT binary patch literal 965 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJFd9#0p?kcwMx?*wMO3J_>` zsIpqLj*|ZW2In^$W~NEBJu9$S((Ca3$3u3ZdS(U|1_1^K zkQ2}t4d?kB?tgzMdHkYWwNrK6$9;b?`x*WmtlQ6j_G87CAjgOqAE+WLv>Dc2y%qd8^g;T!Pc95GtG6ycw)(*N zcSn`Mu`%JA0S{Bb+;3TxcDwc}97~bBme26EZ1>l!373A>#Mv?5lxMtkn>*2G+sR)K z>mOE3NcXt_X%=rPrO0{;FGiff8VXl0p*}?kTiU;c$be&@dY}` zsn~fU1-GPHSsvk(komG@9U73>yeg$LAh7%&52*Q>ry%kj}w9jk{A0-=sn_p)%8cl-T^N;o4mo<_LcnH1zPf99sfV9~+d((mQG0 zhbgIFB!Jjn~= zphy@ikhs8mC{?x`2j=IH;B&nCAl*9qFF|yzCeEw3uQq7V1@HhcK_Vmh+Wd&*YK0w7 zT^W9j@=Ik#MnQ&VATkY*=q~@0g4dDsnbPHLlj?- z&%{*RKxxqR$}^?iM@&ex2t#cshcVv6cnj)1y0?ZH)a-38pgSBe6Vq-4=*1mHQ$!Cl zacAe2@ijvop2EI|BN3acX?UJQBKe-es*)tGu*qtz7m0|jP{-bgFOSN#SLR;Vn{q=5NF+UI|b;^Q=kWzWQwAHqBAu z>Lw=;ZFeO6X6e-4)MqP=K1fZBRmsm6P;u)u?S2SnKH41413dZnba3h?<`OGQCWi#=qt@2ec52C1D6c(6t-SuYBjm-PcruS#hK zo3fOUcDz2EKO0?0mz`o|&)5vf16DMj@zJ_d7@rpGlR^AE$=YEF(ljKvP!&aK)ScTi z!RlR{6E~2PgQ24y;HUcele=;e6_rT*$>^5D$CxG2of=nhdq%lb`Lx=b6G^4wDY`?R zvjfz6uin~}U}5UFou;}fqTQ(oX)eUy|XWutc9vsPEQflOdR66p6s z4KGKprGZtGxUF>b7&b||%;z%Nl-KdT6#qzOq^qp-l}E@jL``Uu>&@oStX=lc{nRs6 z8oPK^FWGZ3*Ysm+yPPlD{(YaYE(3B=X;Q-zes9?etDVANTTcUjJ`Mj5z1R ff$<;T8S;d&oOt-+lRoF?09gUw1o;d6cBcIWZ6&c5 literal 0 HcmV?d00001 diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index 531168cc0..7f70940b4 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -29,6 +29,9 @@ #include "UI/Screens/ErrorScreen.h" #include "UI/Screens/TitleScreen.h" #include "UI/Screens/InventoryScreen.h" +#ifdef ENABLE_JAVA_GUIS +#include "UI/Screens/CreativeInventoryScreen.h" +#endif #include "UI/Screens/InBedChatScreen.h" #include "UI/Screens/AchievementPopup.h" #include "Input/Input.h" @@ -3385,7 +3388,10 @@ void Minecraft::tick(bool bFirst, bool bUpdateTextures) std::shared_ptr player = std::dynamic_pointer_cast( Minecraft::GetInstance()->player ); ui.PlayUISFX(eSFX_Press); #ifdef ENABLE_JAVA_GUIS - setScreen(new InventoryScreen(player)); + if(gameMode->hasInfiniteItems()) + setScreen(new CreativeInventoryScreen(player)); + else + setScreen(new InventoryScreen(player)); #else app.LoadInventoryMenu(iPad,player); #endif diff --git a/Minecraft.Client/Platform/Common/UI/IUIScene_CreativeMenu.h b/Minecraft.Client/Platform/Common/UI/IUIScene_CreativeMenu.h index 4320e3a17..1a0ab14d1 100644 --- a/Minecraft.Client/Platform/Common/UI/IUIScene_CreativeMenu.h +++ b/Minecraft.Client/Platform/Common/UI/IUIScene_CreativeMenu.h @@ -4,9 +4,12 @@ // 4J Stu - This class is for code that is common between XUI and Iggy class SimpleContainer; +class CreativeInventoryScreen; class IUIScene_CreativeMenu : public virtual IUIScene_AbstractContainerMenu { + friend class CreativeInventoryScreen; + public: // 4J Stu - These map directly to the tabs seenon the screen enum ECreativeInventoryTabs diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index d7dbef35f..cb633ddba 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -47,6 +47,10 @@ const wchar_t *Textures::preLoaded[TN_COUNT] = 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", #endif L"gui/icons", diff --git a/Minecraft.Client/Textures/Textures.h b/Minecraft.Client/Textures/Textures.h index 0c8994169..a31976376 100644 --- a/Minecraft.Client/Textures/Textures.h +++ b/Minecraft.Client/Textures/Textures.h @@ -35,6 +35,10 @@ typedef enum _TEXTURE_NAME 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, #endif TN_GUI_ICONS, diff --git a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h index 8086837ef..fae8d427c 100644 --- a/Minecraft.Client/UI/Screens/AbstractContainerScreen.h +++ b/Minecraft.Client/UI/Screens/AbstractContainerScreen.h @@ -22,10 +22,12 @@ public: protected: virtual void renderLabels(); virtual void renderBg(float a) = 0; + // 4jcraft: promoted from private to protected so CreativeInventoryScreen can + // call findSlot() and isHovering() directly for its custom click handling. + virtual Slot *findSlot(int x, int y); + virtual bool isHovering(Slot *slot, int xm, int ym); private: virtual void renderSlot(Slot *slot); - virtual Slot *findSlot(int x, int y); - virtual bool isHovering(Slot *slot, int xm, int ym); protected: virtual void mouseClicked(int x, int y, int buttonNum); virtual void mouseReleased(int x, int y, int buttonNum); diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp new file mode 100644 index 000000000..f3da3066c --- /dev/null +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.cpp @@ -0,0 +1,527 @@ +#include "../../Platform/stdafx.h" +#include "CreativeInventoryScreen.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.entity.player.h" +#include "../../../Minecraft.World/Containers/Inventory.h" +#include "../../../Minecraft.World/Containers/SimpleContainer.h" +#include "../../../Minecraft.World/Containers/Slot.h" +#include "../../../Minecraft.World/Headers/net.minecraft.world.item.h" +#include "../../../Minecraft.World/Headers/net.minecraft.locale.h" +#include "../../Minecraft.h" +#include "../../Textures/Textures.h" +#include "../../Rendering/EntityRenderers/ItemRenderer.h" +#include "../../Rendering/Lighting.h" +#include "../../../Minecraft.World/Containers/InventoryMenu.h" +#include +#include + +// Static member initialization +int CreativeInventoryScreen::selectedTabIndex = IUIScene_CreativeMenu::eCreativeInventoryTab_BuildingBlocks; +std::shared_ptr CreativeInventoryScreen::basicInventory = std::make_shared(0, ITEMS_PER_PAGE); + +// ContainerCreative implementation +CreativeInventoryScreen::ContainerCreative::ContainerCreative(std::shared_ptr player) : AbstractContainerMenu() +{ + std::shared_ptr inventoryplayer = player->inventory; + + // Add creative inventory slots (5 rows x 9 columns = 45 slots) + for (int i = 0; i < ROWS; ++i) + { + for (int j = 0; j < COLUMNS; ++j) + { + addSlot(new Slot(basicInventory, i * COLUMNS + j, 9 + j * 18, 18 + i * 18)); + } + } + + // Add hotbar slots (9 slots at bottom) + for (int k = 0; k < 9; ++k) + { + addSlot(new Slot(inventoryplayer, k, 9 + k * 18, 112)); + } + + scrollTo(0.0f); +} + +bool CreativeInventoryScreen::ContainerCreative::stillValid(std::shared_ptr player) +{ + return true; +} + +std::shared_ptr CreativeInventoryScreen::ContainerCreative::clicked(int slotIndex, int buttonNum, int clickType, std::shared_ptr player) +{ + std::shared_ptr inventory = player->inventory; + std::shared_ptr carried = inventory->getCarried(); + + // Handle clicks outside the GUI + if (slotIndex == CLICKED_OUTSIDE) + { + // Drop the carried item + if (carried != NULL) + { + if (buttonNum == 0) + { + player->drop(carried, true); + inventory->setCarried(std::shared_ptr()); + } + else if (buttonNum == 1) + { + std::shared_ptr single = carried->copy(); + single->count = 1; + player->drop(single, true); + carried->count--; + if (carried->count <= 0) + { + inventory->setCarried(std::shared_ptr()); + } + } + } + return std::shared_ptr(); + } + + // Validate slot index + if (slotIndex < 0 || slotIndex >= (int)slots->size()) + { + return std::shared_ptr(); + } + + Slot* slot = slots->at(slotIndex); + + // Handle creative inventory slots (0-44) + if (slotIndex >= 0 && slotIndex < ITEMS_PER_PAGE) + { + std::shared_ptr slotItem = slot->getItem(); + + // Handle SWAP (number key) - copy item to hotbar + if (clickType == CLICK_SWAP) + { + if (slotItem != NULL && buttonNum >= 0 && buttonNum < 9) + { + std::shared_ptr copy = slotItem->copy(); + copy->count = copy->getMaxStackSize(); + inventory->setItem(buttonNum, copy); + } + return std::shared_ptr(); + } + + // Handle CLONE (middle click) + if (clickType == CLICK_CLONE) + { + if (slotItem != NULL) + { + std::shared_ptr copy = slotItem->copy(); + copy->count = copy->getMaxStackSize(); + inventory->setCarried(copy); + } + return std::shared_ptr(); + } + + // Handle normal clicks + if (slotItem != NULL) + { + if (buttonNum == 0) // Left click + { + std::shared_ptr copy = slotItem->copy(); + copy->count = copy->getMaxStackSize(); + inventory->setCarried(copy); + } + else if (buttonNum == 1) // Right click + { + std::shared_ptr copy = slotItem->copy(); + copy->count = 1; + inventory->setCarried(copy); + } + } + else if (carried != NULL) + { + // Clicking on empty creative slot with item - clear the carried item + inventory->setCarried(std::shared_ptr()); + } + + return std::shared_ptr(); + } + + // For hotbar slots (45-53), use normal container behavior + return AbstractContainerMenu::clicked(slotIndex, buttonNum, clickType, player); +} + +void CreativeInventoryScreen::ContainerCreative::scrollTo(float pos) +{ + int i = (itemList.size() + COLUMNS - 1) / COLUMNS - ROWS; + int j = (int)((double)(pos * (float)i) + 0.5); + + if (j < 0) + { + j = 0; + } + + 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()) + { + basicInventory->setItem(l + k * COLUMNS, itemList[i1]); + } + else + { + basicInventory->setItem(l + k * COLUMNS, std::shared_ptr()); + } + } + } +} + +bool CreativeInventoryScreen::ContainerCreative::canScroll() +{ + return itemList.size() > ITEMS_PER_PAGE; +} + +CreativeInventoryScreen::CreativeInventoryScreen(std::shared_ptr player) + : AbstractContainerScreen(new ContainerCreative(player)) +{ + this->player = player; + player->containerMenu = menu; + + currentScroll = 0.0f; + isScrolling = false; + wasClicking = false; + isLeftMouseDown = false; + + imageHeight = 136; + imageWidth = 195; +} + +void CreativeInventoryScreen::removed() +{ + AbstractContainerScreen::removed(); +} + +void CreativeInventoryScreen::init() +{ + buttons.clear(); + + int i = selectedTabIndex; + selectedTabIndex = -1; + setCurrentCreativeTab(i); +} + +void CreativeInventoryScreen::updateEvents() +{ +#ifdef ENABLE_JAVA_GUIS + // Handle mouse wheel scrolling. + // We use ButtonDown with the scroll actions rather than GetScrollDelta() because + // both share s_scrollTicksForButtonPressed; whichever is called first in a tick + // zeroes it, so GetScrollDelta() would return 0 if hotbar scroll ran first. + // ButtonDown/ScrollSnap() snapshots once per tick so all callers see the same value. + if (needsScrollBars()) + { + ContainerCreative* container = (ContainerCreative*)menu; + int totalRows = ((int)container->itemList.size() + COLUMNS - 1) / COLUMNS; + int scrollableRows = totalRows - ROWS; + if (scrollableRows > 0) + { + float step = 1.0f / (float)scrollableRows; + if (InputManager.ButtonDown(0, MINECRAFT_ACTION_LEFT_SCROLL)) + { + currentScroll -= step; + currentScroll = std::max(0.0f, std::min(1.0f, currentScroll)); + container->scrollTo(currentScroll); + } + else if (InputManager.ButtonDown(0, MINECRAFT_ACTION_RIGHT_SCROLL)) + { + currentScroll += step; + currentScroll = std::max(0.0f, std::min(1.0f, currentScroll)); + container->scrollTo(currentScroll); + } + } + } +#endif + Screen::updateEvents(); +} + +void CreativeInventoryScreen::containerTick() +{ +} + +void CreativeInventoryScreen::tick() +{ + Screen::tick(); +} + +void CreativeInventoryScreen::keyPressed(wchar_t eventCharacter, int eventKey) +{ + AbstractContainerScreen::keyPressed(eventCharacter, eventKey); +} + +void CreativeInventoryScreen::mouseClicked(int x, int y, int buttonNum) +{ + if (buttonNum == 0) isLeftMouseDown = true; + + Screen::mouseClicked(x, y, buttonNum); + + if (buttonNum == 0 || buttonNum == 1) + { + int i = x - (width - imageWidth) / 2; + int j = y - (height - imageHeight) / 2; + + // Check for tab clicks first; let mouseReleased handle the actual tab switch + for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; ++tab) + { + if (isMouseOverTab(tab, i, j)) + { + return; + } + } + + // Determine which slot (if any) was clicked + Slot *slot = findSlot(x, y); + + int xo = (width - imageWidth) / 2; + int yo = (height - imageHeight) / 2; + bool clickedOutside = (x < xo || y < yo || x >= xo + imageWidth || y >= yo + imageHeight); + + int slotId = -1; + if (slot != NULL) slotId = slot->index; + if (clickedOutside) slotId = AbstractContainerMenu::CLICKED_OUTSIDE; + + if (slotId == -1) return; + + bool quickKey = slotId != AbstractContainerMenu::CLICKED_OUTSIDE && + (Keyboard::isKeyDown(Keyboard::KEY_LSHIFT) || Keyboard::isKeyDown(Keyboard::KEY_RSHIFT)); + int clickType = quickKey ? AbstractContainerMenu::CLICK_QUICK_MOVE : AbstractContainerMenu::CLICK_PICKUP; + + // 4jcraft: bypass AbstractContainerScreen::mouseClicked / handleInventoryMouseClick + // here intentionally. The normal path sends a ContainerClickPacket to the server, + // where player->containerMenu is still the InventoryMenu (45 slots). Creative slot + // indices 0-44 are valid in ContainerCreative but not in InventoryMenu, and hotbar + // indices 45-53 exceed InventoryMenu's slot count entirely, causing an out-of-range + // crash in AbstractContainerMenu::clicked on the server side. + // Instead we apply the click locally and sync hotbar changes via SetCreativeModeSlotPacket. + menu->clicked(slotId, buttonNum, clickType, minecraft->player); + + // 4jcraft: sync hotbar slot changes to the server using SetCreativeModeSlotPacket. + // The packet handler (PlayerConnection::handleSetCreativeModeSlot) validates slots + // against InventoryMenu coordinates where the hotbar starts at USE_ROW_SLOT_START (36), + // so we must offset the local hotbar index (0-8) accordingly. + if (slotId >= ITEMS_PER_PAGE && slotId < ITEMS_PER_PAGE + 9) + { + int hotbarSlot = slotId - ITEMS_PER_PAGE; + std::shared_ptr hotbarItem = minecraft->player->inventory->getItem(hotbarSlot); + minecraft->gameMode->handleCreativeModeItemAdd(hotbarItem, hotbarSlot + InventoryMenu::USE_ROW_SLOT_START); + } + } +} + +void CreativeInventoryScreen::mouseReleased(int x, int y, int buttonNum) +{ + if (buttonNum == 0) isLeftMouseDown = false; + + if (buttonNum == 0) + { + int i = x - (width - imageWidth) / 2; + int j = y - (height - imageHeight) / 2; + + // Check for tab clicks + for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; ++tab) + { + if (isMouseOverTab(tab, i, j)) + { + setCurrentCreativeTab(tab); + return; + } + } + } + + AbstractContainerScreen::mouseReleased(x, y, buttonNum); +} + +void CreativeInventoryScreen::render(int xm, int ym, float a) +{ + // Java: drawDefaultBackground() + renderBackground(); + + // Handle scrollbar dragging + bool mouseDown = isLeftMouseDown; + int i = (width - imageWidth) / 2; + int j = (height - imageHeight) / 2; + int k = i + 175; + int l = j + 18; + int i1 = k + 14; + int j1 = l + 112; + + if (!wasClicking && mouseDown && xm >= k && ym >= l && xm < i1 && ym < j1) + { + isScrolling = needsScrollBars(); + } + + if (!mouseDown) + { + isScrolling = false; + } + + wasClicking = mouseDown; + + if (isScrolling) + { + currentScroll = ((float)(ym - l) - 7.5f) / ((float)(j1 - l) - 15.0f); + currentScroll = std::max(0.0f, std::min(1.0f, currentScroll)); + ((ContainerCreative*)menu)->scrollTo(currentScroll); + } + + AbstractContainerScreen::render(xm, ym, a); +} + +void CreativeInventoryScreen::renderLabels() +{ +#ifdef ENABLE_JAVA_GUIS + if (IUIScene_CreativeMenu::specs && selectedTabIndex >= 0 && selectedTabIndex < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT) + { + IUIScene_CreativeMenu::TabSpec* spec = IUIScene_CreativeMenu::specs[selectedTabIndex]; + if (spec) + { + std::wstring tabName = app.GetString(spec->m_descriptionId); + font->draw(tabName, 8, 6, 0x404040); + } + } +#endif +} + +void CreativeInventoryScreen::renderBg(float a) +{ + int x = (width - imageWidth) / 2; + int y = (height - imageHeight) / 2; + +#ifdef ENABLE_JAVA_GUIS + // Render all non-selected tabs first + for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; ++tab) + { + if (tab != selectedTabIndex) + { + drawTab(tab); + } + } + + // Load and render main creative inventory background + int tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TAB_ITEMS); + glColor4f(1, 1, 1, 1); + minecraft->textures->bind(tex); + blit(x, y, 0, 0, imageWidth, imageHeight); + + // Render scrollbar + tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS); + minecraft->textures->bind(tex); + + int scrollX = x + 175; + int scrollY = y + 18; + int scrollHeight = 112; + + if (needsScrollBars()) + { + int scrollPos = (int)((float)(scrollHeight - 17) * currentScroll); + blit(scrollX, scrollY + scrollPos, 232, 0, 12, 15); + } + else + { + blit(scrollX, scrollY + (scrollHeight - 17) / 2, 244, 0, 12, 15); + } + + // Render selected tab last (on top) + drawTab(selectedTabIndex); +#endif +} + +void CreativeInventoryScreen::setCurrentCreativeTab(int tab) +{ + if (tab < 0 || tab >= IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT) + return; + + int oldTab = selectedTabIndex; + selectedTabIndex = tab; + + ContainerCreative* container = (ContainerCreative*)menu; + container->itemList.clear(); + + // Populate itemList from the tab's category groups + if (IUIScene_CreativeMenu::specs && IUIScene_CreativeMenu::specs[tab]) + { + IUIScene_CreativeMenu::TabSpec* spec = IUIScene_CreativeMenu::specs[tab]; + + // Add items from static groups + for (int i = 0; i < spec->m_staticGroupsCount; ++i) + { + int groupIdx = spec->m_staticGroupsA[i]; + if (groupIdx >= 0 && groupIdx < IUIScene_CreativeMenu::eCreativeInventoryGroupsCount) + { + auto& group = IUIScene_CreativeMenu::categoryGroups[groupIdx]; + for (auto& item : group) + { + container->itemList.push_back(item); + } + } + } + } + + currentScroll = 0.0f; + container->scrollTo(0.0f); +} + +void CreativeInventoryScreen::selectTab(int tab) +{ + setCurrentCreativeTab(tab); +} + +bool CreativeInventoryScreen::needsScrollBars() +{ + return ((ContainerCreative*)menu)->canScroll(); +} + +bool CreativeInventoryScreen::isMouseOverTab(int tab, int mouseX, int mouseY) +{ + int i = tab; + int j = 28 * i; + int k = 0; + + if (i > 0) + { + j += i; + } + + // Tabs are in the top row + k = k - 32; + + return mouseX >= j && mouseX <= j + 28 && mouseY >= k && mouseY <= k + 32; +} + +void CreativeInventoryScreen::drawTab(int tab) +{ +#ifdef ENABLE_JAVA_GUIS + bool isSelected = (tab == selectedTabIndex); + int i = tab; + int j = i * 28; + int k = 0; + int l = (width - imageWidth) / 2 + 28 * i; + int i1 = (height - imageHeight) / 2; + int j1 = 32; + + if (isSelected) + { + k += 32; + } + + if (i > 0) + { + l += i; + } + + // Tabs are in the top row + i1 = i1 - 28; + + // Render tab background + int tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS); + minecraft->textures->bind(tex); + glColor4f(1, 1, 1, 1); + blit(l, i1, j, k, 28, 32); + + // TODO: Render tab icon (would need item icons) +#endif +} diff --git a/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h new file mode 100644 index 000000000..4ce1659f4 --- /dev/null +++ b/Minecraft.Client/UI/Screens/CreativeInventoryScreen.h @@ -0,0 +1,71 @@ +#pragma once +#include "AbstractContainerScreen.h" +#include "../../../Minecraft.World/Containers/AbstractContainerMenu.h" +#include "../../Platform/Common/UI/IUIScene_CreativeMenu.h" +#include "../../Player/MultiPlayerLocalPlayer.h" + +class Player; +class SimpleContainer; +class Inventory; +class Slot; + +class CreativeInventoryScreen : public AbstractContainerScreen +{ +private: + static constexpr int ROWS = 5; + static constexpr int COLUMNS = 9; + static constexpr int ITEMS_PER_PAGE = ROWS * COLUMNS; // 45 items (9x5 grid) + + // Currently selected creative tab index + static int selectedTabIndex; + + // Temporary inventory for creative mode items + static std::shared_ptr basicInventory; + + // Amount scrolled in Creative mode inventory (0 = top, 1 = bottom) + float currentScroll; + + bool isScrolling; + + // True if the left mouse button is currently being held + bool isLeftMouseDown; + + // True if the left mouse button was held down last time render was called + bool wasClicking; + + std::shared_ptr player; + +public: + class ContainerCreative : public AbstractContainerMenu + { + public: + std::vector> itemList; + + ContainerCreative(std::shared_ptr player); + virtual bool stillValid(std::shared_ptr player); + virtual std::shared_ptr clicked(int slotIndex, int buttonNum, int clickType, std::shared_ptr player); + void scrollTo(float pos); + bool canScroll(); + }; + +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); +protected: + virtual void renderLabels(); + virtual void renderBg(float a); +private: + void setCurrentCreativeTab(int tab); + void selectTab(int tab); + bool needsScrollBars(); + bool isMouseOverTab(int tab, int mouseX, int mouseY); + void drawTab(int tab); +};