Merge pull request #318 from StevenSYS/dev

Make the Java creative inventory more complete.
This commit is contained in:
ffqq 2026-03-27 20:42:06 +03:00 committed by GitHub
commit 7db2fe0318
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 585 additions and 524 deletions

View file

@ -4760,6 +4760,9 @@ Would you like to unlock the full game?</value>
<data name="IDS_GROUPNAME_FOOD"> <data name="IDS_GROUPNAME_FOOD">
<value>Food</value> <value>Food</value>
</data> </data>
<data name="IDS_GROUPNAME_SEARCH">
<value>Search Items</value>
</data>
<data name="IDS_GROUPNAME_STRUCTURES"> <data name="IDS_GROUPNAME_STRUCTURES">
<value>Structures</value> <value>Structures</value>
</data> </data>

View file

@ -1,4 +1,4 @@
<root> <root>
<data name="IDS_TIPS_GAMETIP_NEWDLC"> <data name="IDS_TIPS_GAMETIP_NEWDLC">
<value>New Downloadable Content is available! Access it from the Minecraft Store button on the Main Menu.</value> <value>New Downloadable Content is available! Access it from the Minecraft Store button on the Main Menu.</value>
</data> </data>
@ -6044,6 +6044,10 @@ Would you like to unlock the full game now?</value>
<value>Food</value> <value>Food</value>
</data> </data>
<data name="IDS_GROUPNAME_SEARCH">
<value>Search Items</value>
</data>
<data name="IDS_GROUPNAME_STRUCTURES"> <data name="IDS_GROUPNAME_STRUCTURES">
<value>Structures</value> <value>Structures</value>
</data> </data>
@ -8811,4 +8815,4 @@ All Ender Chests in a world are linked. Items placed into an Ender Chest are acc
<data name="IDS_TOOLTIPS_CURE"> <data name="IDS_TOOLTIPS_CURE">
<value>Cure</value> <value>Cure</value>
</data> </data>
</root> </root>

View file

@ -469,6 +469,11 @@ void IUIScene_CreativeMenu::staticCtor() {
ITEM(Item::carrotGolden_Id) ITEM(Item::carrotGolden_Id)
ITEM(Item::pumpkinPie_Id) ITEM(Item::pumpkinPie_Id)
// 4jcraft: Search
#ifdef ENABLE_JAVA_GUIS
DEF(eCreativeInventory_Search)
#endif
// Tools, Armour and Weapons (Complete) // Tools, Armour and Weapons (Complete)
DEF(eCreativeInventory_ToolsArmourWeapons) DEF(eCreativeInventory_ToolsArmourWeapons)
ITEM(Item::compass_Id) ITEM(Item::compass_Id)
@ -856,6 +861,13 @@ void IUIScene_CreativeMenu::staticCtor() {
specs[eCreativeInventoryTab_Food] = specs[eCreativeInventoryTab_Food] =
new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup); new TabSpec(L"Food", IDS_GROUPNAME_FOOD, 1, foodGroup);
// 4jcraft
#ifdef ENABLE_JAVA_GUIS
ECreative_Inventory_Groups searchGroup[] = {eCreativeInventory_Search};
specs[eCreativeInventoryTab_Search] =
new TabSpec(L"Search Items", IDS_GROUPNAME_SEARCH, 1, searchGroup);
#endif
ECreative_Inventory_Groups toolsGroup[] = { ECreative_Inventory_Groups toolsGroup[] = {
eCreativeInventory_ToolsArmourWeapons}; eCreativeInventory_ToolsArmourWeapons};
specs[eCreativeInventoryTab_ToolsWeaponsArmor] = specs[eCreativeInventoryTab_ToolsWeaponsArmor] =
@ -898,7 +910,7 @@ IUIScene_CreativeMenu::IUIScene_CreativeMenu() {
m_bCarryingCreativeItem = false; m_bCarryingCreativeItem = false;
m_creativeSlotX = m_creativeSlotY = m_inventorySlotX = m_inventorySlotY = 0; m_creativeSlotX = m_creativeSlotY = m_inventorySlotX = m_inventorySlotY = 0;
// 4J JEV - Settup Tabs // 4J JEV - Setup Tabs
for (int i = 0; i < eCreativeInventoryTab_COUNT; i++) { for (int i = 0; i < eCreativeInventoryTab_COUNT; i++) {
m_tabDynamicPos[i] = 0; m_tabDynamicPos[i] = 0;
m_tabPage[i] = 0; m_tabPage[i] = 0;
@ -1523,4 +1535,4 @@ void IUIScene_CreativeMenu::BuildFirework(
} }
list->push_back(firework); list->push_back(firework);
} }

View file

@ -7,20 +7,24 @@ class SimpleContainer;
class IUIScene_CreativeMenu : public virtual IUIScene_AbstractContainerMenu { class IUIScene_CreativeMenu : public virtual IUIScene_AbstractContainerMenu {
public: public:
// 4J Stu - These map directly to the tabs seenon the screen // 4J Stu - These map directly to the tabs seen on the screen
enum ECreativeInventoryTabs { enum ECreativeInventoryTabs {
eCreativeInventoryTab_BuildingBlocks = 0, eCreativeInventoryTab_BuildingBlocks = 0,
eCreativeInventoryTab_Decorations, eCreativeInventoryTab_Decorations,
eCreativeInventoryTab_RedstoneAndTransport, eCreativeInventoryTab_RedstoneAndTransport,
eCreativeInventoryTab_Materials, eCreativeInventoryTab_Materials,
eCreativeInventoryTab_Food, eCreativeInventoryTab_Food,
// 4jcraft: java search tab
#ifdef ENABLE_JAVA_GUIS
eCreativeInventoryTab_Search,
#endif
eCreativeInventoryTab_ToolsWeaponsArmor, eCreativeInventoryTab_ToolsWeaponsArmor,
eCreativeInventoryTab_Brewing, eCreativeInventoryTab_Brewing,
eCreativeInventoryTab_Misc, eCreativeInventoryTab_Misc,
eCreativeInventoryTab_COUNT, eCreativeInventoryTab_COUNT,
}; };
// 4J Stu - These are logical groupings of items, and be be combined for // 4J Stu - These are logical groupings of items, and are combined for
// tabs on-screen // tabs on-screen
enum ECreative_Inventory_Groups { enum ECreative_Inventory_Groups {
eCreativeInventory_BuildingBlocks, eCreativeInventory_BuildingBlocks,
@ -29,6 +33,10 @@ public:
eCreativeInventory_Transport, eCreativeInventory_Transport,
eCreativeInventory_Materials, eCreativeInventory_Materials,
eCreativeInventory_Food, eCreativeInventory_Food,
// 4jcraft
#ifdef ENABLE_JAVA_GUIS
eCreativeInventory_Search,
#endif
eCreativeInventory_ToolsArmourWeapons, eCreativeInventory_ToolsArmourWeapons,
eCreativeInventory_Brewing, eCreativeInventory_Brewing,
eCreativeInventory_Potions_Basic, eCreativeInventory_Potions_Basic,
@ -97,7 +105,7 @@ public:
virtual void loopClick(int slotIndex, int buttonNum, bool quickKeyHeld, virtual void loopClick(int slotIndex, int buttonNum, bool quickKeyHeld,
std::shared_ptr<Player> player) {} // do nothing std::shared_ptr<Player> player) {} // do nothing
}* itemPickerMenu; }* itemPickerMenu;
// 4jcraft: changed these two from public to protected for the java UI // 4jcraft: changed these two from public to protected for the java UI
static std::vector<std::shared_ptr<ItemInstance> > static std::vector<std::shared_ptr<ItemInstance> >
categoryGroups[eCreativeInventoryGroupsCount]; categoryGroups[eCreativeInventoryGroupsCount];
@ -148,4 +156,4 @@ protected:
static void BuildFirework(std::vector<std::shared_ptr<ItemInstance> >* list, static void BuildFirework(std::vector<std::shared_ptr<ItemInstance> >* list,
uint8_t type, int color, int sulphur, uint8_t type, int color, int sulphur,
bool flicker, bool trail, int fadeColor = -1); bool flicker, bool trail, int fadeColor = -1);
}; };

View file

@ -306,9 +306,10 @@ void AbstractContainerScreen::mouseClicked(int x, int y, int buttonNum) {
} }
if (slotId != -1) { if (slotId != -1) {
bool quickKey = slotId != AbstractContainerMenu::SLOT_CLICKED_OUTSIDE && bool quickKey =
(Keyboard::isKeyDown(Keyboard::KEY_LSHIFT) || slotId != AbstractContainerMenu::SLOT_CLICKED_OUTSIDE &&
Keyboard::isKeyDown(Keyboard::KEY_RSHIFT)); (Keyboard::isKeyDown(Keyboard::KEY_LSHIFT) ||
Keyboard::isKeyDown(Keyboard::KEY_RSHIFT));
minecraft->gameMode->handleInventoryMouseClick( minecraft->gameMode->handleInventoryMouseClick(
menu->containerId, slotId, buttonNum, quickKey, menu->containerId, slotId, buttonNum, quickKey,
minecraft->player); minecraft->player);
@ -341,4 +342,4 @@ void AbstractContainerScreen::tick() {
Screen::tick(); Screen::tick();
if (!minecraft->player->isAlive() || minecraft->player->removed) if (!minecraft->player->isAlive() || minecraft->player->removed)
minecraft->player->closeContainer(); minecraft->player->closeContainer();
} }

View file

@ -42,4 +42,4 @@ public:
virtual void slotsChanged(std::shared_ptr<Container> container); virtual void slotsChanged(std::shared_ptr<Container> container);
virtual bool isPauseScreen(); virtual bool isPauseScreen();
virtual void tick(); virtual void tick();
}; };

View file

@ -15,513 +15,533 @@
#include <cmath> #include <cmath>
// Static member initialization // Static member initialization
int CreativeInventoryScreen::selectedTabIndex = IUIScene_CreativeMenu::eCreativeInventoryTab_BuildingBlocks; int CreativeInventoryScreen::selectedTabIndex =
std::shared_ptr<SimpleContainer> CreativeInventoryScreen::basicInventory = std::make_shared<SimpleContainer>(0, L"", false, ITEMS_PER_PAGE); IUIScene_CreativeMenu::eCreativeInventoryTab_BuildingBlocks;
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<SimpleContainer> CreativeInventoryScreen::basicInventory =
std::make_shared<SimpleContainer>(0, L"", false, ITEMS_PER_PAGE);
ItemRenderer* CreativeInventoryScreen::itemRenderer = new ItemRenderer();
std::shared_ptr<ItemInstance> CreativeInventoryScreen::tabIcons
[IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT];
// ContainerCreative implementation // ContainerCreative implementation
CreativeInventoryScreen::ContainerCreative::ContainerCreative(std::shared_ptr<Player> player) : AbstractContainerMenu() CreativeInventoryScreen::ContainerCreative::ContainerCreative(
{ std::shared_ptr<Player> player)
std::shared_ptr<Inventory> inventoryplayer = player->inventory; : AbstractContainerMenu() {
std::shared_ptr<Inventory> inventoryplayer = player->inventory;
// Add creative inventory slots (5 rows x 9 columns = 45 slots)
for (int i = 0; i < ROWS; ++i) // 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) for (int j = 0; j < COLUMNS; j++) {
{ addSlot(new Slot(basicInventory, i * COLUMNS + j, 9 + j * 18,
addSlot(new Slot(basicInventory, i * COLUMNS + j, 9 + j * 18, 18 + i * 18)); 18 + i * 18));
} }
} }
// Add hotbar slots (9 slots at bottom) // 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));
addSlot(new Slot(inventoryplayer, k, 9 + k * 18, 112)); }
}
scrollTo(0.0f);
scrollTo(0.0f);
for (int i = 0; i < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT;
i++) {
tabIcons[i] = std::shared_ptr<ItemInstance>(
new ItemInstance(tabIconIds[i], 1, 0));
}
} }
bool CreativeInventoryScreen::ContainerCreative::stillValid(std::shared_ptr<Player> player) bool CreativeInventoryScreen::ContainerCreative::stillValid(
{ std::shared_ptr<Player> player) {
return true; return true;
} }
std::shared_ptr<ItemInstance> CreativeInventoryScreen::ContainerCreative::clicked(int slotIndex, int buttonNum, int clickType, std::shared_ptr<Player> player) std::shared_ptr<ItemInstance>
{ CreativeInventoryScreen::ContainerCreative::clicked(
std::shared_ptr<Inventory> inventory = player->inventory; int slotIndex, int buttonNum, int clickType,
std::shared_ptr<ItemInstance> carried = inventory->getCarried(); std::shared_ptr<Player> player) {
std::shared_ptr<Inventory> inventory = player->inventory;
// Handle clicks outside the GUI std::shared_ptr<ItemInstance> carried = inventory->getCarried();
if (slotIndex == SLOT_CLICKED_OUTSIDE)
{ // Handle clicks outside the GUI
// Drop the carried item if (slotIndex == SLOT_CLICKED_OUTSIDE) {
if (carried != NULL) // Drop the carried item
{ if (carried != NULL) {
if (buttonNum == 0) if (buttonNum == 0) {
{ player->drop(carried, true);
player->drop(carried, true); inventory->setCarried(std::shared_ptr<ItemInstance>());
inventory->setCarried(std::shared_ptr<ItemInstance>()); } else if (buttonNum == 1) {
} std::shared_ptr<ItemInstance> single = carried->copy();
else if (buttonNum == 1) single->count = 1;
{ player->drop(single, true);
std::shared_ptr<ItemInstance> single = carried->copy(); carried->count--;
single->count = 1; if (carried->count <= 0) {
player->drop(single, true); inventory->setCarried(std::shared_ptr<ItemInstance>());
carried->count--; }
if (carried->count <= 0) }
{ }
inventory->setCarried(std::shared_ptr<ItemInstance>()); return std::shared_ptr<ItemInstance>();
} }
}
} // Validate slot index
return std::shared_ptr<ItemInstance>(); if (slotIndex < 0 || slotIndex >= (int)slots.size()) {
} return std::shared_ptr<ItemInstance>();
}
// Validate slot index
if (slotIndex < 0 || slotIndex >= (int)slots.size()) Slot* slot = slots.at(slotIndex);
{
return std::shared_ptr<ItemInstance>(); // Handle creative inventory slots (0-44)
} if (slotIndex >= 0 && slotIndex < ITEMS_PER_PAGE) {
std::shared_ptr<ItemInstance> slotItem = slot->getItem();
Slot* slot = slots.at(slotIndex);
// Handle SWAP (number key) - copy item to hotbar
// Handle creative inventory slots (0-44) if (clickType == CLICK_SWAP) {
if (slotIndex >= 0 && slotIndex < ITEMS_PER_PAGE) if (slotItem != NULL && buttonNum >= 0 && buttonNum < 9) {
{ std::shared_ptr<ItemInstance> copy = slotItem->copy();
std::shared_ptr<ItemInstance> slotItem = slot->getItem(); copy->count = copy->getMaxStackSize();
inventory->setItem(buttonNum, copy);
// Handle SWAP (number key) - copy item to hotbar }
if (clickType == CLICK_SWAP) return std::shared_ptr<ItemInstance>();
{ }
if (slotItem != NULL && buttonNum >= 0 && buttonNum < 9)
{ // Handle CLONE (middle click)
std::shared_ptr<ItemInstance> copy = slotItem->copy(); if (clickType == CLICK_CLONE) {
copy->count = copy->getMaxStackSize(); if (slotItem != NULL) {
inventory->setItem(buttonNum, copy); std::shared_ptr<ItemInstance> copy = slotItem->copy();
} copy->count = copy->getMaxStackSize();
return std::shared_ptr<ItemInstance>(); inventory->setCarried(copy);
} }
return std::shared_ptr<ItemInstance>();
// Handle CLONE (middle click) }
if (clickType == CLICK_CLONE)
{ // Handle normal clicks
if (slotItem != NULL) if (slotItem != NULL) {
{ if (buttonNum == 0) // Left click
std::shared_ptr<ItemInstance> copy = slotItem->copy(); {
copy->count = copy->getMaxStackSize(); std::shared_ptr<ItemInstance> copy = slotItem->copy();
inventory->setCarried(copy); copy->count = copy->getMaxStackSize();
} inventory->setCarried(copy);
return std::shared_ptr<ItemInstance>(); } else if (buttonNum == 1) // Right click
} {
std::shared_ptr<ItemInstance> copy = slotItem->copy();
// Handle normal clicks copy->count = 1;
if (slotItem != NULL) inventory->setCarried(copy);
{ }
if (buttonNum == 0) // Left click } else if (carried != NULL) {
{ // Clicking on empty creative slot with item - clear the carried
std::shared_ptr<ItemInstance> copy = slotItem->copy(); // item
copy->count = copy->getMaxStackSize(); inventory->setCarried(std::shared_ptr<ItemInstance>());
inventory->setCarried(copy); }
}
else if (buttonNum == 1) // Right click return std::shared_ptr<ItemInstance>();
{ }
std::shared_ptr<ItemInstance> copy = slotItem->copy();
copy->count = 1; // For hotbar slots (45-53), use normal container behavior
inventory->setCarried(copy); return AbstractContainerMenu::clicked(slotIndex, buttonNum, clickType,
} player);
}
else if (carried != NULL)
{
// Clicking on empty creative slot with item - clear the carried item
inventory->setCarried(std::shared_ptr<ItemInstance>());
}
return std::shared_ptr<ItemInstance>();
}
// For hotbar slots (45-53), use normal container behavior
return AbstractContainerMenu::clicked(slotIndex, buttonNum, clickType, player);
} }
void CreativeInventoryScreen::ContainerCreative::scrollTo(float pos) void CreativeInventoryScreen::ContainerCreative::scrollTo(float pos) {
{ int i = (itemList.size() + COLUMNS - 1) / COLUMNS - ROWS;
int i = (itemList.size() + COLUMNS - 1) / COLUMNS - ROWS; int j = (int)((double)(pos * (float)i) + 0.5);
int j = (int)((double)(pos * (float)i) + 0.5);
if (j < 0) {
if (j < 0) j = 0;
{ }
j = 0;
} for (int k = 0; k < ROWS; k++) {
for (int l = 0; l < COLUMNS; l++) {
for (int k = 0; k < ROWS; ++k) int i1 = l + (k + j) * COLUMNS;
{
for (int l = 0; l < COLUMNS; ++l) if (i1 >= 0 && i1 < (int)itemList.size()) {
{ basicInventory->setItem(l + k * COLUMNS, itemList[i1]);
int i1 = l + (k + j) * COLUMNS; } else {
basicInventory->setItem(l + k * COLUMNS,
if (i1 >= 0 && i1 < (int)itemList.size()) std::shared_ptr<ItemInstance>());
{ }
basicInventory->setItem(l + k * COLUMNS, itemList[i1]); }
} }
else
{
basicInventory->setItem(l + k * COLUMNS, std::shared_ptr<ItemInstance>());
}
}
}
} }
bool CreativeInventoryScreen::ContainerCreative::canScroll() bool CreativeInventoryScreen::ContainerCreative::canScroll() {
{ return itemList.size() > ITEMS_PER_PAGE;
return itemList.size() > ITEMS_PER_PAGE;
} }
CreativeInventoryScreen::CreativeInventoryScreen(std::shared_ptr<Player> player) CreativeInventoryScreen::CreativeInventoryScreen(std::shared_ptr<Player> player)
: AbstractContainerScreen(new ContainerCreative(player)) : AbstractContainerScreen(new ContainerCreative(player)) {
{ this->player = player;
this->player = player; player->containerMenu = menu;
player->containerMenu = menu;
currentScroll = 0.0f;
currentScroll = 0.0f; isScrolling = false;
isScrolling = false; wasClicking = false;
wasClicking = false; isLeftMouseDown = false;
isLeftMouseDown = false;
imageHeight = 136;
imageHeight = 136; imageWidth = 195;
imageWidth = 195;
} }
void CreativeInventoryScreen::removed() void CreativeInventoryScreen::removed() { AbstractContainerScreen::removed(); }
{
AbstractContainerScreen::removed(); void CreativeInventoryScreen::init() {
buttons.clear();
int i = selectedTabIndex;
selectedTabIndex = -1;
setCurrentCreativeTab(i);
} }
void CreativeInventoryScreen::init() void CreativeInventoryScreen::updateEvents() {
{
buttons.clear();
int i = selectedTabIndex;
selectedTabIndex = -1;
setCurrentCreativeTab(i);
}
void CreativeInventoryScreen::updateEvents()
{
#ifdef ENABLE_JAVA_GUIS #ifdef ENABLE_JAVA_GUIS
// Handle mouse wheel scrolling. // Handle mouse wheel scrolling.
// We use ButtonDown with the scroll actions rather than GetScrollDelta() because // We use ButtonDown with the scroll actions rather than GetScrollDelta()
// both share s_scrollTicksForButtonPressed; whichever is called first in a tick // because both share s_scrollTicksForButtonPressed; whichever is called
// zeroes it, so GetScrollDelta() would return 0 if hotbar scroll ran first. // first in a tick zeroes it, so GetScrollDelta() would return 0 if hotbar
// ButtonDown/ScrollSnap() snapshots once per tick so all callers see the same value. // scroll ran first. ButtonDown/ScrollSnap() snapshots once per tick so all
if (needsScrollBars()) // callers see the same value.
{ if (needsScrollBars()) {
ContainerCreative* container = (ContainerCreative*)menu; ContainerCreative* container = (ContainerCreative*)menu;
int totalRows = ((int)container->itemList.size() + COLUMNS - 1) / COLUMNS; int totalRows =
int scrollableRows = totalRows - ROWS; ((int)container->itemList.size() + COLUMNS - 1) / COLUMNS;
if (scrollableRows > 0) int scrollableRows = totalRows - ROWS;
{ if (scrollableRows > 0) {
float step = 1.0f / (float)scrollableRows; float step = 1.0f / (float)scrollableRows;
if (InputManager.ButtonDown(0, MINECRAFT_ACTION_LEFT_SCROLL)) if (InputManager.ButtonDown(0, MINECRAFT_ACTION_LEFT_SCROLL)) {
{ currentScroll -= step;
currentScroll -= step; currentScroll = std::max(0.0f, std::min(1.0f, currentScroll));
currentScroll = std::max(0.0f, std::min(1.0f, currentScroll)); container->scrollTo(currentScroll);
container->scrollTo(currentScroll); } else if (InputManager.ButtonDown(0,
} MINECRAFT_ACTION_RIGHT_SCROLL)) {
else if (InputManager.ButtonDown(0, MINECRAFT_ACTION_RIGHT_SCROLL)) currentScroll += step;
{ currentScroll = std::max(0.0f, std::min(1.0f, currentScroll));
currentScroll += step; container->scrollTo(currentScroll);
currentScroll = std::max(0.0f, std::min(1.0f, currentScroll)); }
container->scrollTo(currentScroll); }
} }
}
}
#endif #endif
Screen::updateEvents(); Screen::updateEvents();
} }
void CreativeInventoryScreen::containerTick() void CreativeInventoryScreen::containerTick() {}
{
void CreativeInventoryScreen::tick() { Screen::tick(); }
void CreativeInventoryScreen::keyPressed(wchar_t eventCharacter, int eventKey) {
AbstractContainerScreen::keyPressed(eventCharacter, eventKey);
} }
void CreativeInventoryScreen::tick() void CreativeInventoryScreen::mouseClicked(int x, int y, int buttonNum) {
{ if (buttonNum == 0) isLeftMouseDown = true;
Screen::tick();
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<ItemInstance> hotbarItem =
minecraft->player->inventory->getItem(hotbarSlot);
minecraft->gameMode->handleCreativeModeItemAdd(
hotbarItem, hotbarSlot + InventoryMenu::USE_ROW_SLOT_START);
}
}
} }
void CreativeInventoryScreen::keyPressed(wchar_t eventCharacter, int eventKey) void CreativeInventoryScreen::mouseReleased(int x, int y, int buttonNum) {
{ if (buttonNum == 0) isLeftMouseDown = false;
AbstractContainerScreen::keyPressed(eventCharacter, eventKey);
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::mouseClicked(int x, int y, int buttonNum) void CreativeInventoryScreen::render(int xm, int ym, float a) {
{ // Java: drawDefaultBackground()
if (buttonNum == 0) isLeftMouseDown = true; renderBackground();
Screen::mouseClicked(x, y, buttonNum); // 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 (buttonNum == 0 || buttonNum == 1) if (!wasClicking && mouseDown && xm >= x1 && ym >= y1 && xm < x2 &&
{ ym < y2) {
int i = x - (width - imageWidth) / 2; isScrolling = needsScrollBars();
int j = y - (height - imageHeight) / 2; }
// Check for tab clicks first; let mouseReleased handle the actual tab switch if (!mouseDown) {
for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; ++tab) isScrolling = false;
{ }
if (isMouseOverTab(tab, i, j))
{
return;
}
}
// Determine which slot (if any) was clicked wasClicking = mouseDown;
Slot *slot = findSlot(x, y);
int xo = (width - imageWidth) / 2; if (isScrolling) {
int yo = (height - imageHeight) / 2; currentScroll = ((float)(ym - y1) - 7.5f) / ((float)(y2 - y1) - 15.0f);
bool clickedOutside = (x < xo || y < yo || x >= xo + imageWidth || y >= yo + imageHeight); currentScroll = std::max(0.0f, std::min(1.0f, currentScroll));
((ContainerCreative*)menu)->scrollTo(currentScroll);
}
int slotId = -1; AbstractContainerScreen::render(xm, ym, a);
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<ItemInstance> 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) void CreativeInventoryScreen::renderLabels() {
{
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 #ifdef ENABLE_JAVA_GUIS
if (IUIScene_CreativeMenu::specs && selectedTabIndex >= 0 && selectedTabIndex < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT) if (IUIScene_CreativeMenu::specs && selectedTabIndex >= 0 &&
{ selectedTabIndex < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT) {
IUIScene_CreativeMenu::TabSpec* spec = IUIScene_CreativeMenu::specs[selectedTabIndex]; IUIScene_CreativeMenu::TabSpec* spec =
if (spec) IUIScene_CreativeMenu::specs[selectedTabIndex];
{ if (spec) {
std::wstring tabName = app.GetString(spec->m_descriptionId); std::wstring tabName = app.GetString(spec->m_descriptionId);
font->draw(tabName, 8, 6, 0x404040); font->draw(tabName, 8, 6, 0x404040);
} }
} }
#endif #endif
} }
void CreativeInventoryScreen::renderBg(float a) void CreativeInventoryScreen::renderBg(float a) {
{ int x = (width - imageWidth) / 2;
int x = (width - imageWidth) / 2; int y = (height - imageHeight) / 2;
int y = (height - imageHeight) / 2;
#ifdef ENABLE_JAVA_GUIS #ifdef ENABLE_JAVA_GUIS
// Render all non-selected tabs first static int itemsTex =
for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT; ++tab) minecraft->textures->loadTexture(TN_GUI_CREATIVE_TAB_ITEMS);
{ static int searchTex =
if (tab != selectedTabIndex) minecraft->textures->loadTexture(TN_GUI_CREATIVE_TAB_ITEM_SEARCH);
{ static int scrollTex =
drawTab(tab); minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS);
} // Render all non-selected tabs first
} for (int tab = 0; tab < IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT;
tab++) {
// Load and render main creative inventory background if (tab != selectedTabIndex) {
int tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TAB_ITEMS); drawTab(tab);
glColor4f(1, 1, 1, 1); }
minecraft->textures->bind(tex); }
blit(x, y, 0, 0, imageWidth, imageHeight);
// Load and render main creative inventory background
// Render scrollbar glColor4f(1, 1, 1, 1);
tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS); minecraft->textures->bind((selectedTabIndex == 5) ? searchTex : itemsTex);
minecraft->textures->bind(tex); blit(x, y, 0, 0, imageWidth, imageHeight);
int scrollX = x + 175; // Render scrollbar
int scrollY = y + 18; minecraft->textures->bind(scrollTex);
int scrollHeight = 112;
int scrollX = x + 175;
if (needsScrollBars()) int scrollY = y + 18;
{ int scrollHeight = 112;
int scrollPos = (int)((float)(scrollHeight - 17) * currentScroll);
blit(scrollX, scrollY + scrollPos, 232, 0, 12, 15); if (needsScrollBars()) {
} int scrollPos = (int)((float)(scrollHeight - 17) * currentScroll);
else blit(scrollX, scrollY + scrollPos, 232, 0, 12, 15);
{ } else {
blit(scrollX, scrollY + (scrollHeight - 17) / 2, 244, 0, 12, 15); blit(scrollX, scrollY, 244, 0, 12, 15);
} }
// Render selected tab last (on top) // Render selected tab last (on top)
drawTab(selectedTabIndex); drawTab(selectedTabIndex);
#endif #endif
} }
void CreativeInventoryScreen::setCurrentCreativeTab(int tab) void CreativeInventoryScreen::setCurrentCreativeTab(int tab) {
{ if (tab < 0 || tab >= IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT)
if (tab < 0 || tab >= IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT) return;
return;
int oldTab = selectedTabIndex;
int oldTab = selectedTabIndex; selectedTabIndex = tab;
selectedTabIndex = tab;
ContainerCreative* container = (ContainerCreative*)menu;
ContainerCreative* container = (ContainerCreative*)menu; container->itemList.clear();
container->itemList.clear();
// Populate itemList from the tab's category groups
// Populate itemList from the tab's category groups if (IUIScene_CreativeMenu::specs && IUIScene_CreativeMenu::specs[tab]) {
if (IUIScene_CreativeMenu::specs && IUIScene_CreativeMenu::specs[tab]) IUIScene_CreativeMenu::TabSpec* spec =
{ IUIScene_CreativeMenu::specs[tab];
IUIScene_CreativeMenu::TabSpec* spec = IUIScene_CreativeMenu::specs[tab];
// Add items from static groups
// 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 &&
int groupIdx = spec->m_staticGroupsA[i]; groupIdx <
if (groupIdx >= 0 && groupIdx < IUIScene_CreativeMenu::eCreativeInventoryGroupsCount) IUIScene_CreativeMenu::eCreativeInventoryGroupsCount) {
{ auto& group = IUIScene_CreativeMenu::categoryGroups[groupIdx];
auto& group = IUIScene_CreativeMenu::categoryGroups[groupIdx]; for (auto& item : group) {
for (auto& item : group) container->itemList.push_back(item);
{ }
container->itemList.push_back(item); }
} }
} }
}
} currentScroll = 0.0f;
container->scrollTo(0.0f);
currentScroll = 0.0f;
container->scrollTo(0.0f);
} }
void CreativeInventoryScreen::selectTab(int tab) void CreativeInventoryScreen::selectTab(int tab) { setCurrentCreativeTab(tab); }
{
setCurrentCreativeTab(tab); bool CreativeInventoryScreen::needsScrollBars() {
return ((ContainerCreative*)menu)->canScroll();
} }
bool CreativeInventoryScreen::needsScrollBars() bool CreativeInventoryScreen::isMouseOverTab(int tab, int mouseX, int mouseY) {
{ int tabColumn = tab % 6;
return ((ContainerCreative*)menu)->canScroll(); 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));
} }
bool CreativeInventoryScreen::isMouseOverTab(int tab, int mouseX, int mouseY) void CreativeInventoryScreen::drawTab(int tab) {
{
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 #ifdef ENABLE_JAVA_GUIS
bool isSelected = (tab == selectedTabIndex); bool isSelected = (selectedTabIndex == tab);
int i = tab; bool tabFirstRow = (tab < 6);
int j = i * 28; int left = (width - imageWidth) / 2;
int k = 0; int top = (height - imageHeight) / 2;
int l = (width - imageWidth) / 2 + 28 * i; int tabColumn = tab % 6;
int i1 = (height - imageHeight) / 2; int sy = 0;
int j1 = 32; int x = left + 28 * tabColumn;
int y = top;
if (isSelected) static int tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS);
{
k += 32; if (isSelected) {
} sy += 32;
}
if (i > 0)
{ if (tabColumn == 5) {
l += i; x = left + imageWidth - 28;
} } else if (tabColumn > 0) {
x += tabColumn;
// Tabs are in the top row }
i1 = i1 - 28;
// Tabs are in the top row
// Render tab background if (tabFirstRow) {
int tex = minecraft->textures->loadTexture(TN_GUI_CREATIVE_TABS); y -= 28;
minecraft->textures->bind(tex); } else {
glColor4f(1, 1, 1, 1); sy += 64;
blit(l, i1, j, k, 28, 32); y += imageHeight - 4;
}
// TODO: Render tab icon (would need item icons)
// 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 #endif
} }

View file

@ -9,63 +9,76 @@ class SimpleContainer;
class Inventory; class Inventory;
class Slot; class Slot;
class CreativeInventoryScreen : public AbstractContainerScreen class CreativeInventoryScreen : public AbstractContainerScreen {
{
private: private:
static constexpr int ROWS = 5; static constexpr int ROWS = 5;
static constexpr int COLUMNS = 9; static constexpr int COLUMNS = 9;
static constexpr int ITEMS_PER_PAGE = ROWS * COLUMNS; // 45 items (9x5 grid) 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<SimpleContainer> 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 // Currently selected creative tab index
bool wasClicking; static int selectedTabIndex;
// Array of item ids for the tab icons
static int tabIconIds[IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT];
// Temporary inventory for creative mode items
static std::shared_ptr<SimpleContainer> basicInventory;
// Item renderer for the tab icons
static ItemRenderer* itemRenderer;
// Array of tab icons
static std::shared_ptr<ItemInstance>
tabIcons[IUIScene_CreativeMenu::eCreativeInventoryTab_COUNT];
// 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> player;
std::shared_ptr<Player> player;
public: public:
class ContainerCreative : public AbstractContainerMenu class ContainerCreative : public AbstractContainerMenu {
{ public:
public: std::vector<std::shared_ptr<ItemInstance>> itemList;
std::vector<std::shared_ptr<ItemInstance>> itemList;
ContainerCreative(std::shared_ptr<Player> player);
ContainerCreative(std::shared_ptr<Player> player); virtual bool stillValid(std::shared_ptr<Player> player);
virtual bool stillValid(std::shared_ptr<Player> player); virtual std::shared_ptr<ItemInstance> clicked(
virtual std::shared_ptr<ItemInstance> clicked(int slotIndex, int buttonNum, int clickType, std::shared_ptr<Player> player); int slotIndex, int buttonNum, int clickType,
void scrollTo(float pos); std::shared_ptr<Player> player);
bool canScroll(); void scrollTo(float pos);
}; bool canScroll();
};
public: public:
CreativeInventoryScreen(std::shared_ptr<Player> player); CreativeInventoryScreen(std::shared_ptr<Player> player);
virtual void removed(); virtual void removed();
virtual void init(); virtual void init();
virtual void containerTick(); virtual void containerTick();
virtual void tick(); virtual void tick();
virtual void updateEvents(); virtual void updateEvents();
virtual void keyPressed(wchar_t eventCharacter, int eventKey); virtual void keyPressed(wchar_t eventCharacter, int eventKey);
virtual void mouseClicked(int x, int y, int buttonNum); virtual void mouseClicked(int x, int y, int buttonNum);
virtual void mouseReleased(int x, int y, int buttonNum); virtual void mouseReleased(int x, int y, int buttonNum);
virtual void render(int xm, int ym, float a); virtual void render(int xm, int ym, float a);
protected: protected:
virtual void renderLabels(); virtual void renderLabels();
virtual void renderBg(float a); virtual void renderBg(float a);
private: private:
void setCurrentCreativeTab(int tab); void setCurrentCreativeTab(int tab);
void selectTab(int tab); void selectTab(int tab);
bool needsScrollBars(); bool needsScrollBars();
bool isMouseOverTab(int tab, int mouseX, int mouseY); bool isMouseOverTab(int tab, int mouseX, int mouseY);
void drawTab(int tab); void drawTab(int tab);
}; };