#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; const int CreativeInventoryScreen::tabIconIds [IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT] = { // Building Blocks Tile::redBrick_Id, // Decorations Tile::rose_Id, // Redstone & Transportation Item::redStone_Id, // Materials Item::stick_Id, // Food Item::apple_Id, // Fix for it not compiling with shiggy #ifdef ENABLE_JAVA_GUIS // Search Items Item::compass_Id, #endif // Tools, Weapons & Armor Item::hatchet_iron_Id, // Brewing Item::potion_Id, // Materials Item::bucket_lava_Id}; std::shared_ptr CreativeInventoryScreen::basicInventory = std::make_shared(0, L"", false, ITEMS_PER_PAGE); ItemRenderer* CreativeInventoryScreen::itemRenderer = new ItemRenderer(); std::shared_ptr CreativeInventoryScreen::tabIcons [IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT]; // 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); for (int i = 0; i < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; i++) { tabIcons[i] = std::shared_ptr( new ItemInstance(tabIconIds[i], 1, 0)); } } 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, bool looped) { std::shared_ptr inventory = player->inventory; std::shared_ptr carried = inventory->getCarried(); // Handle clicks outside the GUI if (slotIndex == SLOT_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 mouseX = x - (width - imageWidth) / 2; int mouseY = 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, mouseX, mouseY)) { 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::SLOT_CLICKED_OUTSIDE; if (slotId == -1) return; bool quickKey = slotId != AbstractContainerMenu::SLOT_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 mouseX = x - (width - imageWidth) / 2; int mouseY = y - (height - imageHeight) / 2; // Check for tab clicks for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; tab++) { if (isMouseOverTab(tab, mouseX, mouseY)) { 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 left = (width - imageWidth) / 2; int top = (height - imageHeight) / 2; int x1 = left + 175; int y1 = top + 18; int x2 = x1 + 14; int y2 = y1 + 112; if (!wasClicking && mouseDown && xm >= x1 && ym >= y1 && xm < x2 && ym < y2) { isScrolling = needsScrollBars(); } if (!mouseDown) { isScrolling = false; } wasClicking = mouseDown; if (isScrolling) { currentScroll = ((float)(ym - y1) - 7.5f) / ((float)(y2 - y1) - 15.0f); currentScroll = std::max(0.0f, std::min(1.0f, currentScroll)); ((ContainerCreative*)menu)->scrollTo(currentScroll); } AbstractContainerScreen::render(xm, ym, a); for (int i = 0; i < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; i++) { if (renderIconTooltip(i, xm, ym)) { break; } } } 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 static int itemsTex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TAB_ITEMS); static int searchTex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TAB_ITEM_SEARCH); static int scrollTex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS); // Render all non-selected tabs first for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; tab++) { if (tab != selectedTabIndex) { renderTab(tab); } } // Load and render main creative inventory background glColor4f(1, 1, 1, 1); minecraft->textures->bind((selectedTabIndex == 5) ? searchTex : itemsTex); blit(x, y, 0, 0, imageWidth, imageHeight); // Render scrollbar minecraft->textures->bind(scrollTex); 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, 244, 0, 12, 15); } // Render selected tab last (on top) 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; 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) { return isMouseOverInternal(tab, mouseX, mouseY, 0, 0, 28, 32); } 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); int left = (width - imageWidth) / 2; int top = (height - imageHeight) / 2; int tabColumn = tab % 6; int sy = 0; int x = left + 28 * tabColumn; int y = top; static int tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS); if (isSelected) { sy += 32; } if (tabColumn == 5) { x = left + imageWidth - 28; } else if (tabColumn > 0) { x += tabColumn; } // Tabs are in the top row if (tabFirstRow) { y -= 28; } else { sy += 64; y += imageHeight - 4; } // Render tab background glDisable(GL_LIGHTING); minecraft->textures->bind(tex); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); blit(x, y, tabColumn * 28, sy, 28, 32); // Render tab icon x += 6; y += 8 + (tabFirstRow ? 1 : -1); glEnable(GL_LIGHTING); glEnable(GL_RESCALE_NORMAL); Lighting::turnOnGui(); itemRenderer->renderGuiItem(font, minecraft->textures, tabIcons[tab], x, y); itemRenderer->renderGuiItemDecorations(font, minecraft->textures, 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; }