4jcraft/Minecraft.Client/Platform/Common/XUI/XUI_Scene_AbstractContainer.cpp
2026-03-22 12:40:22 -05:00

492 lines
18 KiB
C++

#include "../../Minecraft.World/Platform/stdafx.h"
#include <xuiresource.h>
#include <xuiapp.h>
#include <assert.h>
#include "../../Minecraft.World/Player/Player.h"
#include "../../Minecraft.Client/Player/LocalPlayer.h"
#include "../../Minecraft.Client/Minecraft.h"
#include "../../Minecraft.Client/GameState/GameMode.h"
#include "../../Minecraft.World/Containers/AbstractContainerMenu.h"
#include "../../Minecraft.World/Headers/net.minecraft.world.inventory.h"
#include "../../Minecraft.World/Headers/net.minecraft.world.item.h"
#include "../../Minecraft.World/Blocks/Tile.h"
#include "../../Minecraft.World/Recipes/FurnaceRecipes.h"
#include "../../Minecraft.World/Recipes/Recipy.h"
#include "../../Minecraft.World/Recipes/Recipes.h"
#include "../../Minecraft.World/Recipes/ArmorRecipes.h"
#include "../../Minecraft.World/Util/StringHelpers.h"
#include "../Tutorial/Tutorial.h"
#include "../Tutorial/TutorialMode.h"
#include "XUI_Ctrl_SlotList.h"
#include "XUI_Ctrl_SlotItem.h"
#include "XUI_Ctrl_SlotItemListItem.h"
#include "XUI_Scene_AbstractContainer.h"
#ifdef _DEBUG_MENUS_ENABLED
#include "XUI_DebugItemEditor.h"
#endif
#define IGNORE_KEYPRESS_TIMERID 11
#define IGNORE_KEYPRESS_TIME 100
void CXuiSceneAbstractContainer::PlatformInitialize(int iPad, int startIndex) {
m_bIgnoreKeyPresses = true;
m_iPad = iPad;
XuiControlSetText(m_InventoryText, app.GetString(IDS_INVENTORY));
// Determine min and max extents for pointer, it needs to be able to move
// off the container to drop items.
float fPanelWidth, fPanelHeight;
float fPointerWidth, fPointerHeight;
// We may have varying depths of controls here, so base off the pointers
// parent
HXUIOBJ parent;
XuiElementGetBounds(m_pointerControl->m_hObj, &fPointerWidth,
&fPointerHeight);
XuiElementGetParent(m_pointerControl->m_hObj, &parent);
m_pointerControl->SetShow(TRUE);
XuiElementGetBounds(parent, &fPanelWidth, &fPanelHeight);
// Get size of pointer
m_fPointerImageOffsetX = floor(fPointerWidth / 2.0f);
m_fPointerImageOffsetY = floor(fPointerHeight / 2.0f);
m_fPanelMinX = 0.0f;
m_fPanelMaxX = fPanelWidth;
m_fPanelMinY = 0.0f;
m_fPanelMaxY = fPanelHeight;
// 4J-PB - need to limit this in splitscreen
if (app.GetLocalPlayerCount() > 1) {
// don't let the pointer go into someone's screen
m_fPointerMinY = floor(fPointerHeight / 2.0f);
} else {
m_fPointerMinY = -fPointerHeight;
}
m_fPointerMinX = -fPointerWidth;
m_fPointerMaxX = fPanelWidth + fPointerWidth;
m_fPointerMaxY = fPanelHeight + (fPointerHeight / 2);
// m_hPointerText=NULL;
// m_hPointerTextBkg=NULL;
UIVec2D itemPos;
UIVec2D itemSize;
GetItemScreenData(m_eCurrSection, 0, &(itemPos), &(itemSize));
UIVec2D sectionPos;
GetPositionOfSection(m_eCurrSection, &(sectionPos));
UIVec2D vPointerPos = sectionPos;
vPointerPos += itemPos;
vPointerPos.x += (itemSize.x / 2.0f);
vPointerPos.y += (itemSize.y / 2.0f);
vPointerPos.x -= m_fPointerImageOffsetX;
vPointerPos.y -= m_fPointerImageOffsetY;
D3DXVECTOR3 newPointerPos;
newPointerPos.x = vPointerPos.x;
newPointerPos.y = vPointerPos.y;
newPointerPos.z = 0.0f;
m_pointerControl->SetPosition(&newPointerPos);
m_pointerPos.x = newPointerPos.x;
m_pointerPos.y = newPointerPos.y;
#ifdef USE_POINTER_ACCEL
m_fPointerVelX = 0.0f;
m_fPointerVelY = 0.0f;
m_fPointerAccelX = 0.0f;
m_fPointerAccelY = 0.0f;
#endif
// Add timer to poll controller stick input at 60Hz
HRESULT timerResult = SetTimer(POINTER_INPUT_TIMER_ID, (1000 / 60));
assert(timerResult == S_OK);
XuiSetTimer(m_hObj, IGNORE_KEYPRESS_TIMERID, IGNORE_KEYPRESS_TIME);
// Disable the default navigation behaviour for all slot lsit items (prevent
// old style cursor navigation).
for (int iSection = m_eFirstSection; iSection < m_eMaxSection; ++iSection) {
ESceneSection eSection = (ESceneSection)(iSection);
if (!IsSectionSlotList(eSection)) continue;
// Get dimensions of this section.
int iNumRows;
int iNumColumns;
int iNumItems =
GetSectionDimensions(eSection, &(iNumColumns), &(iNumRows));
for (int iItem = 0; iItem < iNumItems; ++iItem) {
CXuiCtrlSlotItemListItem* pCXuiCtrlSlotItem;
GetSectionSlotList(eSection)->GetCXuiCtrlSlotItem(
iItem, &(pCXuiCtrlSlotItem));
pCXuiCtrlSlotItem->SetSkipsDefaultNavigation(TRUE);
}
}
}
// 4J Stu - Added to support auto-save. Need to re-associate on a navigate back
void CXuiSceneAbstractContainer::InitDataAssociations(
int iPad, AbstractContainerMenu* menu, int startIndex /*= 0*/) {
// TODO Inventory dimensions need defined as constants
m_inventoryControl->SetData(iPad, menu, 3, 9, startIndex,
startIndex + 3 * 9);
// TODO Inventory dimensions need defined as constants
m_useRowControl->SetData(iPad, menu, 1, 9, startIndex + 3 * 9,
startIndex + 4 * 9);
m_pointerControl->SetUserIndex(m_pointerControl->m_hObj, iPad);
}
int CXuiSceneAbstractContainer::getSectionColumns(ESceneSection eSection) {
return GetSectionSlotList(eSection)->GetColumns();
}
int CXuiSceneAbstractContainer::getSectionRows(ESceneSection eSection) {
return GetSectionSlotList(eSection)->GetRows();
}
// Adding this so we can turn off the pointer text background, since it flickers
// on then off at the start of a scene where a tutorial popup is
HRESULT CXuiSceneAbstractContainer::OnTransitionStart(
XUIMessageTransition* pTransition, BOOL& bHandled) {
if (pTransition->dwTransAction == XUI_TRANSITION_ACTION_DESTROY)
return S_OK;
if (pTransition->dwTransType == XUI_TRANSITION_TO ||
pTransition->dwTransType == XUI_TRANSITION_BACKTO) {
// 4J Stu - Added to support auto-save. Need to re-associate on a
// navigate back
if (pTransition->dwTransType == XUI_TRANSITION_BACKTO) {
// Add timer to poll controller stick input at 60Hz
HRESULT timerResult = SetTimer(POINTER_INPUT_TIMER_ID, (1000 / 60));
assert(timerResult == S_OK);
InitDataAssociations(m_iPad, m_menu);
}
HXUIOBJ hObj = NULL;
HRESULT hr = XuiControlGetVisual(m_pointerControl->m_hObj, &hObj);
hr = XuiElementGetChildById(hObj, L"text_measurer",
&m_hPointerTextMeasurer);
hr = XuiElementGetChildById(hObj, L"text_name", &m_hPointerText);
hr = XuiElementGetChildById(hObj, L"text_panel", &m_hPointerTextBkg);
hr = XuiElementGetChildById(hObj, L"pointer_image", &m_hPointerImg);
XuiElementSetShow(m_hPointerText, FALSE);
XuiElementSetShow(m_hPointerTextBkg, FALSE);
}
return S_OK;
}
D3DXVECTOR3 CXuiSceneAbstractContainer::GetCursorScreenPosition() {
return app.GetElementScreenPosition(m_pointerControl->m_hObj);
}
HRESULT CXuiSceneAbstractContainer::OnKeyDown(XUIMessageInput* pInputData,
BOOL& bHandled) {
if (m_bIgnoreKeyPresses) return S_OK;
bHandled = handleKeyDown(
pInputData->UserIndex, mapVKToAction(pInputData->dwKeyCode),
(pInputData->dwFlags & XUI_INPUT_FLAG_REPEAT) != 0);
return S_OK;
}
int CXuiSceneAbstractContainer::mapVKToAction(int vk) {
int action = MINECRAFT_ACTION_MAX;
switch (vk) {
case VK_PAD_A:
action = ACTION_MENU_A;
break;
case VK_PAD_B:
case VK_PAD_START:
action = ACTION_MENU_B;
break;
case VK_PAD_X:
action = ACTION_MENU_X;
break;
case VK_PAD_Y:
action = ACTION_MENU_Y;
break;
case VK_PAD_DPAD_LEFT:
action = ACTION_MENU_LEFT;
break;
case VK_PAD_DPAD_RIGHT:
action = ACTION_MENU_RIGHT;
break;
case VK_PAD_DPAD_UP:
action = ACTION_MENU_UP;
break;
case VK_PAD_DPAD_DOWN:
action = ACTION_MENU_DOWN;
break;
case VK_PAD_LTRIGGER:
action = ACTION_MENU_PAGEUP;
break;
case VK_PAD_RTRIGGER:
action = ACTION_MENU_PAGEDOWN;
break;
case VK_PAD_LSHOULDER:
action = ACTION_MENU_LEFT_SCROLL;
break;
case VK_PAD_RSHOULDER:
action = ACTION_MENU_RIGHT_SCROLL;
break;
case VK_PAD_RTHUMB_UP:
action = ACTION_MENU_OTHER_STICK_UP;
break;
case VK_PAD_RTHUMB_DOWN:
action = ACTION_MENU_OTHER_STICK_DOWN;
break;
case VK_PAD_RTHUMB_RIGHT:
action = ACTION_MENU_OTHER_STICK_RIGHT;
break;
case VK_PAD_RTHUMB_LEFT:
action = ACTION_MENU_OTHER_STICK_LEFT;
break;
};
return action;
}
void CXuiSceneAbstractContainer::handleSectionClick(ESceneSection eSection) {
CXuiCtrlSlotList* slotList = GetSectionSlotList(eSection);
slotList->Clicked();
}
HRESULT CXuiSceneAbstractContainer::handleCustomTimer(XUIMessageTimer* pTimer,
BOOL& bHandled) {
return S_OK;
}
// Returns XUI position of given scene section.
void CXuiSceneAbstractContainer::GetPositionOfSection(ESceneSection eSection,
UIVec2D* pPosition) {
D3DXVECTOR3 xuiPos;
GetSectionControl(eSection)->GetPosition(&xuiPos);
pPosition->x = xuiPos.x;
pPosition->y = xuiPos.y;
}
void CXuiSceneAbstractContainer::GetItemScreenData(ESceneSection eSection,
int iItemIndex,
UIVec2D* pPosition,
UIVec2D* pSize) {
D3DXVECTOR3 xuiPos;
if (IsSectionSlotList(eSection)) {
// Get slot item.
CXuiCtrlSlotItemListItem* pCXuiCtrlSlotItem;
GetSectionSlotList(eSection)->GetCXuiCtrlSlotItem(iItemIndex,
&(pCXuiCtrlSlotItem));
// Get size of slot.
pCXuiCtrlSlotItem->GetBounds(&(pSize->x), &(pSize->y));
// Get position of slot.
pCXuiCtrlSlotItem->GetPosition(&xuiPos);
pPosition->x = xuiPos.x;
pPosition->y = xuiPos.y;
} else {
// Get size of control
GetSectionControl(eSection)->GetBounds(&(pSize->x), &(pSize->y));
// Get position of control
GetSectionControl(eSection)->GetPosition(&xuiPos);
pPosition->x = xuiPos.x;
pPosition->y = xuiPos.y;
}
}
std::shared_ptr<ItemInstance> CXuiSceneAbstractContainer::getSlotItem(
ESceneSection eSection, int iSlot) {
CXuiCtrlSlotItemListItem* pCXuiCtrlSlotItem;
GetSectionSlotList(eSection)->GetCXuiCtrlSlotItem(iSlot,
&(pCXuiCtrlSlotItem));
return pCXuiCtrlSlotItem->getItemInstance(pCXuiCtrlSlotItem->m_hObj);
}
bool CXuiSceneAbstractContainer::isSlotEmpty(ESceneSection eSection,
int iSlot) {
CXuiCtrlSlotItemListItem* pCXuiCtrlSlotItem;
GetSectionSlotList(eSection)->GetCXuiCtrlSlotItem(iSlot,
&(pCXuiCtrlSlotItem));
return pCXuiCtrlSlotItem->isEmpty(pCXuiCtrlSlotItem->m_hObj);
}
HRESULT CXuiSceneAbstractContainer::OnTimer(XUIMessageTimer* pTimer,
BOOL& bHandled) {
HRESULT hr = S_OK;
// Update pointer from stick input on timer.
if (pTimer->nId == POINTER_INPUT_TIMER_ID) {
onMouseTick();
D3DXVECTOR3 pointerPos;
pointerPos.x = m_pointerPos.x;
pointerPos.y = m_pointerPos.y;
pointerPos.z = 0.0f;
m_pointerControl->SetPosition(&pointerPos);
// This message has been dealt with, don't pass it on further.
bHandled = TRUE;
} else if (pTimer->nId == IGNORE_KEYPRESS_TIMERID) {
KillTimer(IGNORE_KEYPRESS_TIMERID);
m_bIgnoreKeyPresses = false;
} else {
// Some scenes may have their own timers for other events, so handle
// them here
hr = handleCustomTimer(pTimer, bHandled);
}
return hr;
}
HRESULT CXuiSceneAbstractContainer::OnCustomMessage_Splitscreenplayer(
bool bJoining, BOOL& bHandled) {
bHandled = true;
return app.AdjustSplitscreenScene_PlayerChanged(m_hObj, &m_OriginalPosition,
m_iPad, bJoining);
}
bool CXuiSceneAbstractContainer::doesSectionTreeHaveFocus(
ESceneSection eSection) {
return GetSectionControl(eSection)->TreeHasFocus();
}
void CXuiSceneAbstractContainer::setSectionFocus(ESceneSection eSection,
int iPad) {
HRESULT focusResult = GetSectionControl(eSection)->SetFocus(iPad);
assert(focusResult == S_OK);
}
void CXuiSceneAbstractContainer::setSectionSelectedSlot(ESceneSection eSection,
int x, int y) {
GetSectionSlotList(eSection)->SetCurrentSlot(y, x);
}
void CXuiSceneAbstractContainer::setFocusToPointer(int iPad) {
m_pointerControl->SetFocus(iPad);
}
void CXuiSceneAbstractContainer::SetPointerText(
const std::wstring& description,
std::vector<std::wstring>& unformattedStrings, bool newSlot) {
if (description.empty()) {
m_pointerControl->SetText(L"");
XuiElementSetShow(m_hPointerText, FALSE);
XuiElementSetShow(m_hPointerTextBkg, FALSE);
return;
}
bool smallPointer = m_bSplitscreen || (!RenderManager.IsHiDef() &&
!RenderManager.IsWidescreen());
std::wstring desc = L"<font size=\"" +
_toString<int>(smallPointer ? 12 : 14) + L"\">" +
description + L"</font>";
XUIRect tempXuiRect, xuiRect;
HRESULT hr;
xuiRect.right = 0;
for (AUTO_VAR(it, unformattedStrings.begin());
it != unformattedStrings.end(); ++it) {
XuiTextPresenterMeasureText(m_hPointerTextMeasurer,
parseXMLSpecials((*it)).c_str(),
&tempXuiRect);
if (tempXuiRect.right > xuiRect.right) xuiRect = tempXuiRect;
}
// Set size with the new width so that the HTML height check is correct
XuiElementSetBounds(
m_hPointerTextBkg, xuiRect.right + 4.0f + 4.0f + 4.0f,
xuiRect.bottom); // edge graphics are 8 pixels, with 4 for the border,
// extra 4 for the background is fudge
XuiElementSetBounds(
m_hPointerText, xuiRect.right + 4.0f + 4.0f,
xuiRect.bottom); // edge graphics are 8 pixels, text is centred
XuiHtmlSetText(m_hPointerText, desc.c_str());
// Check if we need to resize the box
XUIContentDims contentDims;
XuiHtmlGetContentDims(m_hPointerText, &contentDims);
xuiRect.bottom = contentDims.nContentHeight;
// get the size of the button, and apply the change in size due to the text
// to the whole button
float fImgWidth, fImgHeight;
XuiElementGetBounds(m_hPointerImg, &fImgWidth, &fImgHeight);
// 4J-PB - changing to calculate values
D3DXVECTOR3 vPosText, vPosTextBkg, vPosImg;
XuiElementGetPosition(m_hPointerImg, &vPosImg);
XuiElementGetPosition(m_hPointerText, &vPosText);
XuiElementGetPosition(m_hPointerTextBkg, &vPosTextBkg);
// Set the new height
XuiElementSetBounds(
m_hPointerTextBkg, xuiRect.right + 4.0f + 4.0f + 4.0f,
xuiRect.bottom + 4.0f +
4.0f); // edge graphics are 8 pixels, with 4 for the border, extra
// 4 for the background is fudge
XuiElementSetBounds(
m_hPointerText, xuiRect.right + 4.0f + 4.0f,
xuiRect.bottom + 4.0f +
4.0f); // edge graphics are 8 pixels, text is centred
// position the text and panel relative to the pointer image
vPosTextBkg.x = vPosImg.x + fImgWidth * 0.6f;
vPosText.x = vPosTextBkg.x + 4;
vPosTextBkg.y =
vPosImg.y - (xuiRect.bottom + 4.0f + 4.0f) + fImgWidth * 0.4f;
vPosText.y = vPosTextBkg.y + 4;
XuiElementSetPosition(m_hPointerText, &vPosText);
XuiElementSetPosition(m_hPointerTextBkg, &vPosTextBkg);
XuiElementSetShow(m_hPointerTextBkg, TRUE);
XuiElementSetShow(m_hPointerText, TRUE);
}
void CXuiSceneAbstractContainer::adjustPointerForSafeZone() {
D3DXVECTOR2 baseSceneOrigin;
float baseWidth, baseHeight;
if (CXuiSceneBase::GetBaseSceneSafeZone(m_iPad, baseSceneOrigin, baseWidth,
baseHeight)) {
D3DXMATRIX pointerBackgroundMatrix;
XuiElementGetFullXForm(m_hPointerTextBkg, &pointerBackgroundMatrix);
float width, height;
XuiElementGetBounds(m_hPointerTextBkg, &width, &height);
if (((pointerBackgroundMatrix._41 / pointerBackgroundMatrix._11) +
width) > (baseSceneOrigin.x + baseWidth)) {
// app.DebugPrintf("Pointer is outside the safe zone for this base
// scene\n");
// get the size of the button, and apply the change in size due to
// the text to the whole button
float fImgWidth, fImgHeight;
XuiElementGetBounds(m_hPointerImg, &fImgWidth, &fImgHeight);
// 4J-PB - changing to calculate values
D3DXVECTOR3 vPosText, vPosTextBkg, vPosImg;
XuiElementGetPosition(m_hPointerImg, &vPosImg);
XuiElementGetPosition(m_hPointerText, &vPosText);
XuiElementGetPosition(m_hPointerTextBkg, &vPosTextBkg);
// position the text and panel relative to the pointer image
vPosTextBkg.x = (vPosImg.x + fImgWidth * 0.4f) - width;
vPosText.x = vPosTextBkg.x + 4;
XuiElementSetPosition(m_hPointerText, &vPosText);
XuiElementSetPosition(m_hPointerTextBkg, &vPosTextBkg);
}
}
}