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 000000000..f328206c0 Binary files /dev/null and b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_inventory.png differ 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 000000000..be178b743 Binary files /dev/null and b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_item_search.png differ 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 000000000..2b6fbb9c3 Binary files /dev/null and b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tab_items.png differ diff --git a/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tabs.png b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tabs.png new file mode 100644 index 000000000..3f5202355 Binary files /dev/null and b/Minecraft.Assets/Common/res/1_2_2/gui/creative_inventory/tabs.png differ 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); +};